From 09f858af20f83e9780bce2353974f76e69ee273f Mon Sep 17 00:00:00 2001 From: Electroblob <35599699+Electroblob77@users.noreply.github.com> Date: Thu, 7 Jun 2018 11:58:24 +0100 Subject: [PATCH] Replace branch with old 1.7.10 code and assets --- build.gradle | 56 +- gradle.properties | 3 - gradle/wrapper/gradle-wrapper.jar | Bin 52271 -> 51017 bytes gradle/wrapper/gradle-wrapper.properties | 4 +- libs/antiqueatlas-1.7.10-4.2.10-deobf.jar | Bin 0 -> 1099560 bytes libs/antiqueatlas-1.7.10-4.2.10-sources.jar | Bin 0 -> 1002711 bytes .../electroblob/wizardry/CommonProxy.java | 173 ++- .../electroblob/wizardry/EnumElement.java | 39 + .../wizardry/EnumParticleType.java | 16 + .../electroblob/wizardry/EnumSpellType.java | 21 + .../java/electroblob/wizardry/EnumTier.java | 38 + .../electroblob/wizardry/ExtendedPlayer.java | 485 ++++++ .../wizardry/IElementalDamage.java | 16 + .../{util => }/IndirectMagicDamage.java | 13 +- .../wizardry/{util => }/MagicDamage.java | 100 +- .../java/electroblob/wizardry/Settings.java | 507 ------- .../electroblob/wizardry/SpellGlyphData.java | 21 +- .../wizardry/{util => }/WandHelper.java | 97 +- .../wizardry/WeightedRandomSpellBook.java | 64 + .../java/electroblob/wizardry/WizardData.java | 660 -------- .../java/electroblob/wizardry/Wizardry.java | 1169 +++++++++++--- .../wizardry/WizardryEventHandler.java | 1063 +++++++++---- .../wizardry/WizardryGuiFactory.java | 5 +- .../wizardry/WizardryGuiHandler.java | 31 +- .../wizardry/WizardryKeyHandler.java | 16 +- .../wizardry/WizardryRegistry.java | 631 ++++++++ .../wizardry/WizardryUtilities.java | 881 +++++++++++ .../wizardry/WizardryWorldGenerator.java | 401 +++-- .../wizardry/block/BlockArcaneWorkbench.java | 88 +- .../wizardry/block/BlockCrystal.java | 16 + .../wizardry/block/BlockCrystalFlower.java | 151 +- .../wizardry/block/BlockCrystalOre.java | 13 +- .../wizardry/block/BlockMagicLight.java | 44 +- .../wizardry/block/BlockSnare.java | 91 +- .../wizardry/block/BlockSpectral.java | 107 +- .../wizardry/block/BlockStatue.java | 174 ++- .../block/BlockTransportationStone.java | 200 ++- .../wizardry/block/BlockVanishingCobweb.java | 57 +- .../wizardry/client/ClientProxy.java | 543 ++++--- .../wizardry/client/EntityNameEntry.java | 39 +- .../wizardry/client/GuiArcaneWorkbench.java | 314 ++-- .../wizardry/client/GuiButtonApply.java | 62 +- .../wizardry/client/GuiButtonInvisible.java | 10 +- .../wizardry/client/GuiButtonNextPage.java | 78 + .../wizardry/client/GuiButtonTurnPage.java | 52 - .../wizardry/client/GuiConfigWizardry.java | 62 +- .../wizardry/client/GuiPortableCrafting.java | 11 +- .../wizardry/client/GuiSpellBook.java | 50 +- .../wizardry/client/GuiSpellDisplay.java | 216 +-- .../wizardry/client/GuiWizardHandbook.java | 722 +++++---- .../wizardry/client/MixedFontRenderer.java | 21 +- .../wizardry/client/MovingSoundEntity.java | 24 +- .../client/WizardryClientEventHandler.java | 789 +++++----- .../wizardry/client/model/ModelIceGiant.java | 6 +- .../wizardry/client/model/ModelPhoenix.java | 4 +- .../client/model/ModelSkeletonMinion.java | 58 + .../client/model/ModelSpiritHorse.java | 559 +++++++ .../client/model/ModelSpiritWolf.java | 190 +++ .../wizardry/client/model/ModelWizard.java | 1 + .../client/model/ModelWizardArmour.java | 204 +-- .../client/model/WizardryItemModels.java | 192 --- .../client/particle/EntityBlizzardFX.java | 139 ++ ...eDarkMagic.java => EntityDarkMagicFX.java} | 31 +- .../{ParticleDust.java => EntityDustFX.java} | 26 +- .../wizardry/client/particle/EntityIceFX.java | 199 +++ .../client/particle/EntityLeafFX.java | 180 +++ ...ntBubble.java => EntityMagicBubbleFX.java} | 14 +- .../client/particle/EntityMagicFireFX.java | 108 ++ .../client/particle/EntityPathFX.java | 181 +++ .../client/particle/EntitySnowFX.java | 131 ++ .../client/particle/EntitySparkFX.java | 117 ++ .../client/particle/EntitySparkleFX.java | 203 +++ .../client/particle/EntityTornadoFX.java | 135 ++ .../client/particle/ParticleBlizzard.java | 72 - .../particle/ParticleCustomTexture.java | 195 --- .../wizardry/client/particle/ParticleIce.java | 34 - .../client/particle/ParticleLeaf.java | 33 - .../client/particle/ParticleMagicFlame.java | 47 - .../client/particle/ParticlePath.java | 93 -- .../particle/ParticleRotatingSparkle.java | 59 - .../client/particle/ParticleSnow.java | 33 - .../client/particle/ParticleSpark.java | 58 - .../client/particle/ParticleSparkle.java | 96 -- .../client/particle/ParticleTornado.java | 83 - .../wizardry/client/renderer/LayerStone.java | 191 --- .../wizardry/client/renderer/RenderArc.java | 217 +-- .../renderer/RenderArcaneWorkbench.java | 114 +- .../client/renderer/RenderBlackHole.java | 278 ++-- .../wizardry/client/renderer/RenderBlank.java | 9 +- .../client/renderer/RenderBubble.java | 162 +- .../wizardry/client/renderer/RenderDecay.java | 60 +- .../wizardry/client/renderer/RenderDecoy.java | 582 +++++-- .../client/renderer/RenderEvilWizard.java | 117 +- .../client/renderer/RenderFallingGrass.java | 74 + .../client/renderer/RenderFireRing.java | 158 +- .../client/renderer/RenderFirestorm.java | 205 +++ .../client/renderer/RenderForceArrow.java | 145 +- .../client/renderer/RenderHammer.java | 27 +- .../client/renderer/RenderIceGiant.java | 111 +- .../client/renderer/RenderIceSpike.java | 73 +- .../client/renderer/RenderLightningDisc.java | 73 +- .../client/renderer/RenderLightningPulse.java | 65 +- .../client/renderer/RenderMagicArrow.java | 128 +- .../client/renderer/RenderMagicLight.java | 199 ++- .../client/renderer/RenderMagicSlime.java | 95 ++ .../client/renderer/RenderMeteor.java | 75 + .../client/renderer/RenderPhoenix.java | 129 +- .../client/renderer/RenderProjectile.java | 78 +- .../client/renderer/RenderShield.java | 163 ++ .../wizardry/client/renderer/RenderSigil.java | 68 +- .../renderer/RenderSilverfishMinion.java | 109 ++ .../client/renderer/RenderSkeletonMinion.java | 66 + .../client/renderer/RenderSpiderMinion.java | 55 + .../client/renderer/RenderSpiritHorse.java | 325 +++- .../client/renderer/RenderSpiritWolf.java | 329 +++- .../client/renderer/RenderStatue.java | 224 ++- .../renderer/RenderTransientPlayer.java | 90 ++ .../renderer/RenderTranslucentItem.java | 97 ++ .../wizardry/client/renderer/RenderWand.java | 134 ++ .../client/renderer/RenderWizard.java | 217 ++- .../client/renderer/RenderWraithMinion.java | 66 +- .../client/renderer/RenderZombieMinion.java | 157 ++ .../wizardry/command/CommandCastSpell.java | 143 +- .../command/CommandDiscoverSpell.java | 186 +-- .../wizardry/command/CommandSetAlly.java | 149 +- .../wizardry/command/CommandViewAllies.java | 60 +- .../wizardry/constants/Constants.java | 44 - .../wizardry/constants/Element.java | 67 - .../wizardry/constants/SpellType.java | 23 - .../electroblob/wizardry/constants/Tier.java | 81 - .../enchantment/EnchantmentMagicSword.java | 19 +- .../enchantment/EnchantmentTimed.java | 16 +- .../wizardry/enchantment/Imbuement.java | 131 -- .../wizardry/entity/EntityArc.java | 47 +- .../wizardry/entity/EntityFallingGrass.java | 93 ++ .../wizardry/entity/EntityMagicSlime.java | 331 ++++ .../wizardry/entity/EntityMeteor.java | 179 ++- .../wizardry/entity/EntityShield.java | 19 +- .../entity/construct/EntityArrowRain.java | 8 +- .../entity/construct/EntityBlackHole.java | 50 +- .../entity/construct/EntityBlizzard.java | 38 +- .../entity/construct/EntityBubble.java | 55 +- .../entity/construct/EntityDecay.java | 27 +- .../entity/construct/EntityEarthquake.java | 52 +- .../entity/construct/EntityFireRing.java | 12 +- .../entity/construct/EntityFireSigil.java | 17 +- .../entity/construct/EntityForcefield.java | 34 +- .../entity/construct/EntityFrostSigil.java | 19 +- .../entity/construct/EntityHammer.java | 145 +- .../entity/construct/EntityHealAura.java | 16 +- .../entity/construct/EntityIceSpike.java | 13 +- .../construct/EntityLightningPulse.java | 9 + .../construct/EntityLightningSigil.java | 27 +- .../construct/EntityMagicConstruct.java | 18 +- .../entity/construct/EntityTornado.java | 84 +- .../entity/living/EntityAIAttackSpell.java | 238 --- .../entity/living/EntityAISelectSpell.java | 14 - .../entity/living/EntityBlazeMinion.java | 410 +++-- .../wizardry/entity/living/EntityDecoy.java | 65 +- .../entity/living/EntityEvilWizard.java | 638 ++++---- .../entity/living/EntityIceGiant.java | 398 +++-- .../entity/living/EntityIceWraith.java | 475 +++--- .../entity/living/EntityLightningWraith.java | 396 +++-- .../entity/living/EntityMagicSlime.java | 177 --- .../wizardry/entity/living/EntityPhoenix.java | 420 ++++-- .../entity/living/EntityShadowWraith.java | 294 ++-- .../entity/living/EntitySilverfishMinion.java | 381 +++-- .../entity/living/EntitySkeletonMinion.java | 548 +++++-- .../entity/living/EntitySpiderMinion.java | 435 +++--- .../entity/living/EntitySpiritHorse.java | 110 +- .../entity/living/EntitySpiritWolf.java | 108 +- .../entity/living/EntityStormElemental.java | 313 ++-- .../entity/living/EntitySummonedCreature.java | 482 ++++-- .../wizardry/entity/living/EntityWizard.java | 1342 +++++++++-------- .../entity/living/EntityZombieMinion.java | 398 +++-- .../living/IIntelligentSpellCaster.java | 20 - .../wizardry/entity/living/ISpellCaster.java | 61 - .../entity/living/ISummonedCreature.java | 333 ---- .../entity/projectile/EntityBomb.java | 59 - .../entity/projectile/EntityDarknessOrb.java | 36 +- .../entity/projectile/EntityDart.java | 15 +- .../entity/projectile/EntityFirebolt.java | 33 +- .../entity/projectile/EntityFirebomb.java | 67 +- .../entity/projectile/EntityForceArrow.java | 7 +- .../entity/projectile/EntityForceOrb.java | 35 +- .../entity/projectile/EntityIceCharge.java | 87 +- .../entity/projectile/EntityIceLance.java | 16 +- .../entity/projectile/EntityIceShard.java | 16 +- .../projectile/EntityLightningArrow.java | 19 +- .../projectile/EntityLightningDisc.java | 53 +- .../entity/projectile/EntityMagicArrow.java | 204 +-- .../entity/projectile/EntityMagicMissile.java | 9 +- .../projectile/EntityMagicProjectile.java | 30 +- .../entity/projectile/EntityPoisonBomb.java | 75 +- .../entity/projectile/EntitySmokeBomb.java | 81 +- .../entity/projectile/EntitySpark.java | 30 +- .../entity/projectile/EntitySparkBomb.java | 72 +- .../entity/projectile/EntityThunderbolt.java | 30 +- .../wizardry/event/DiscoverSpellEvent.java | 49 - .../wizardry/event/SpellBindEvent.java | 31 - .../wizardry/event/SpellCastEvent.java | 139 -- .../wizardry/item/IConjuredItem.java | 57 - .../wizardry/item/ItemArcaneTome.java | 46 +- .../wizardry/item/ItemArmourUpgrade.java | 37 +- .../wizardry/item/ItemFirebomb.java | 45 +- .../wizardry/item/ItemFlamingAxe.java | 85 +- .../wizardry/item/ItemFrostAxe.java | 89 +- .../item/ItemIdentificationScroll.java | 91 +- .../wizardry/item/ItemPoisonBomb.java | 46 +- .../electroblob/wizardry/item/ItemScroll.java | 107 +- .../wizardry/item/ItemSmokeBomb.java | 45 +- .../wizardry/item/ItemSpawnWizard.java | 194 +++ .../wizardry/item/ItemSpectralArmour.java | 119 +- .../wizardry/item/ItemSpectralBow.java | 361 +++-- .../wizardry/item/ItemSpectralPickaxe.java | 74 +- .../wizardry/item/ItemSpectralSword.java | 69 +- .../wizardry/item/ItemSpellBook.java | 53 +- .../electroblob/wizardry/item/ItemWand.java | 441 +++--- .../wizardry/item/ItemWizardArmour.java | 210 ++- .../wizardry/item/ItemWizardHandbook.java | 44 +- .../wizardry/loot/RandomSpell.java | 267 ---- .../wizardry/loot/WizardSpell.java | 65 - .../packet/PacketCastContinuousSpell.java | 72 +- .../wizardry/packet/PacketCastSpell.java | 94 +- .../wizardry/packet/PacketClairvoyance.java | 27 +- .../wizardry/packet/PacketControlInput.java | 316 +++- .../wizardry/packet/PacketGlyphData.java | 25 +- .../wizardry/packet/PacketNPCCastSpell.java | 80 - .../wizardry/packet/PacketParticleSpawn.java | 157 ++ .../wizardry/packet/PacketPlayerSync.java | 36 +- .../wizardry/packet/PacketSyncSettings.java | 90 -- .../wizardry/packet/PacketTransportation.java | 28 +- .../packet/WizardryPacketHandler.java | 13 +- .../potion/ICustomPotionParticles.java | 45 - .../wizardry/potion/PotionDecay.java | 74 +- .../wizardry/potion/PotionFrost.java | 73 +- .../wizardry/potion/PotionMagicEffect.java | 56 +- .../potion/PotionMagicEffectParticles.java | 11 - .../electroblob/wizardry/registry/Spells.java | 352 ----- .../registry/WizardryAchievements.java | 47 - .../wizardry/registry/WizardryBlocks.java | 92 -- .../registry/WizardryEnchantments.java | 41 - .../wizardry/registry/WizardryItems.java | 419 ----- .../wizardry/registry/WizardryPotions.java | 113 -- .../wizardry/registry/WizardryRegistry.java | 320 ---- .../wizardry/registry/WizardrySounds.java | 68 - .../wizardry/registry/WizardryTabs.java | 175 --- .../electroblob/wizardry/spell/Agility.java | 29 +- .../java/electroblob/wizardry/spell/Arc.java | 58 +- .../wizardry/spell/ArcaneJammer.java | 65 +- .../electroblob/wizardry/spell/ArrowRain.java | 35 +- .../electroblob/wizardry/spell/Banish.java | 82 +- .../electroblob/wizardry/spell/BlackHole.java | 101 +- .../electroblob/wizardry/spell/Blink.java | 134 +- .../electroblob/wizardry/spell/Blizzard.java | 43 +- .../electroblob/wizardry/spell/Bubble.java | 77 +- .../wizardry/spell/ChainLightning.java | 64 +- .../wizardry/spell/Clairvoyance.java | 149 +- .../electroblob/wizardry/spell/Cobwebs.java | 199 ++- .../wizardry/spell/ConjureArmour.java | 62 +- .../wizardry/spell/ConjureBow.java | 44 +- .../wizardry/spell/ConjurePickaxe.java | 38 +- .../wizardry/spell/ConjureSword.java | 35 +- .../wizardry/spell/CureEffects.java | 50 +- .../wizardry/spell/CurseOfSoulbinding.java | 66 +- .../wizardry/spell/DarknessOrb.java | 30 +- .../wizardry/spell/Darkvision.java | 28 +- .../java/electroblob/wizardry/spell/Dart.java | 29 +- .../electroblob/wizardry/spell/Decay.java | 73 +- .../electroblob/wizardry/spell/Decoy.java | 35 +- .../electroblob/wizardry/spell/Detonate.java | 94 +- .../wizardry/spell/Diamondflesh.java | 28 +- .../wizardry/spell/Earthquake.java | 39 +- .../wizardry/spell/Entrapment.java | 68 +- .../wizardry/spell/FireResistance.java | 36 +- .../electroblob/wizardry/spell/FireSigil.java | 36 +- .../electroblob/wizardry/spell/Fireball.java | 33 +- .../electroblob/wizardry/spell/Firebolt.java | 33 +- .../electroblob/wizardry/spell/Firebomb.java | 29 +- .../electroblob/wizardry/spell/Fireskin.java | 32 +- .../electroblob/wizardry/spell/Firestorm.java | 120 +- .../electroblob/wizardry/spell/FlameRay.java | 90 +- .../wizardry/spell/FlamingAxe.java | 34 +- .../wizardry/spell/FlamingWeapon.java | 70 +- .../electroblob/wizardry/spell/Flight.java | 36 +- .../wizardry/spell/FontOfMana.java | 33 +- .../wizardry/spell/FontOfVitality.java | 30 +- .../wizardry/spell/ForceArrow.java | 31 +- .../electroblob/wizardry/spell/ForceOrb.java | 29 +- .../wizardry/spell/Forcefield.java | 28 +- .../wizardry/spell/ForestsCurse.java | 46 +- .../electroblob/wizardry/spell/Freeze.java | 115 +- .../wizardry/spell/FreezingWeapon.java | 72 +- .../electroblob/wizardry/spell/FrostAxe.java | 34 +- .../electroblob/wizardry/spell/FrostRay.java | 146 +- .../wizardry/spell/FrostSigil.java | 36 +- .../electroblob/wizardry/spell/Glide.java | 27 +- .../wizardry/spell/GreaterFireball.java | 30 +- .../wizardry/spell/GreaterHeal.java | 31 +- .../electroblob/wizardry/spell/GroupHeal.java | 41 +- .../wizardry/spell/GrowthAura.java | 55 +- .../electroblob/wizardry/spell/Hailstorm.java | 35 +- .../java/electroblob/wizardry/spell/Heal.java | 31 +- .../electroblob/wizardry/spell/HealAlly.java | 34 +- .../wizardry/spell/HealingAura.java | 22 +- .../wizardry/spell/HomingSpark.java | 28 +- .../electroblob/wizardry/spell/IceAge.java | 121 +- .../electroblob/wizardry/spell/IceCharge.java | 29 +- .../electroblob/wizardry/spell/IceLance.java | 29 +- .../electroblob/wizardry/spell/IceShard.java | 29 +- .../electroblob/wizardry/spell/IceShroud.java | 32 +- .../electroblob/wizardry/spell/IceSpikes.java | 59 +- .../electroblob/wizardry/spell/IceStatue.java | 104 +- .../electroblob/wizardry/spell/Ignite.java | 188 ++- .../wizardry/spell/ImbueWeapon.java | 90 +- .../wizardry/spell/Intimidate.java | 80 +- .../wizardry/spell/InvigoratingPresence.java | 33 +- .../wizardry/spell/Invisibility.java | 36 +- .../wizardry/spell/InvokeWeather.java | 41 +- .../electroblob/wizardry/spell/Ironflesh.java | 36 +- .../java/electroblob/wizardry/spell/Leap.java | 28 +- .../wizardry/spell/Levitation.java | 23 +- .../electroblob/wizardry/spell/LifeDrain.java | 92 +- .../electroblob/wizardry/spell/Light.java | 67 +- .../wizardry/spell/LightningArrow.java | 29 +- .../wizardry/spell/LightningBolt.java | 85 +- .../wizardry/spell/LightningDisc.java | 32 +- .../wizardry/spell/LightningHammer.java | 36 +- .../wizardry/spell/LightningPulse.java | 38 +- .../wizardry/spell/LightningRay.java | 111 +- .../wizardry/spell/LightningSigil.java | 38 +- .../wizardry/spell/LightningWeb.java | 84 +- .../wizardry/spell/MagicMissile.java | 29 +- .../wizardry/spell/Metamorphosis.java | 46 +- .../electroblob/wizardry/spell/Meteor.java | 37 +- .../wizardry/spell/MindControl.java | 137 +- .../electroblob/wizardry/spell/MindTrick.java | 97 +- .../java/electroblob/wizardry/spell/None.java | 15 +- .../electroblob/wizardry/spell/Oakflesh.java | 36 +- .../electroblob/wizardry/spell/Petrify.java | 111 +- .../electroblob/wizardry/spell/PhaseStep.java | 126 +- .../wizardry/spell/PlagueOfDarkness.java | 50 +- .../wizardry/spell/PocketFurnace.java | 26 +- .../wizardry/spell/PocketWorkbench.java | 18 +- .../electroblob/wizardry/spell/Poison.java | 70 +- .../wizardry/spell/PoisonBomb.java | 29 +- .../wizardry/spell/ReplenishHunger.java | 23 +- .../wizardry/spell/RingOfFire.java | 29 +- .../wizardry/spell/ShadowWard.java | 54 +- .../electroblob/wizardry/spell/Shield.java | 28 +- .../electroblob/wizardry/spell/Shockwave.java | 57 +- .../wizardry/spell/SilverfishSwarm.java | 44 +- .../wizardry/spell/SixthSense.java | 27 +- .../electroblob/wizardry/spell/Slime.java | 71 +- .../electroblob/wizardry/spell/SmokeBomb.java | 29 +- .../electroblob/wizardry/spell/Snare.java | 55 +- .../electroblob/wizardry/spell/Snowball.java | 30 +- .../electroblob/wizardry/spell/SparkBomb.java | 29 +- .../wizardry/spell/SpectralPathway.java | 146 +- .../electroblob/wizardry/spell/Spell.java | 347 +++-- .../wizardry/spell/SpiderSwarm.java | 74 +- .../wizardry/spell/StaticAura.java | 32 +- .../wizardry/spell/SummonBlaze.java | 51 +- .../wizardry/spell/SummonIceGiant.java | 51 +- .../wizardry/spell/SummonIceWraith.java | 52 +- .../wizardry/spell/SummonIronGolem.java | 45 +- .../wizardry/spell/SummonLightningWraith.java | 51 +- .../wizardry/spell/SummonPhoenix.java | 35 +- .../wizardry/spell/SummonShadowWraith.java | 37 +- .../wizardry/spell/SummonSkeleton.java | 60 +- .../wizardry/spell/SummonSkeletonLegion.java | 93 +- .../wizardry/spell/SummonSnowGolem.java | 42 +- .../wizardry/spell/SummonSpiritHorse.java | 37 +- .../wizardry/spell/SummonSpiritWolf.java | 41 +- .../wizardry/spell/SummonStormElemental.java | 34 +- .../wizardry/spell/SummonWitherSkeleton.java | 65 +- .../wizardry/spell/SummonZombie.java | 52 +- .../wizardry/spell/Telekinesis.java | 69 +- .../wizardry/spell/Thunderbolt.java | 32 +- .../wizardry/spell/Thunderstorm.java | 51 +- .../electroblob/wizardry/spell/Tornado.java | 30 +- .../wizardry/spell/Transience.java | 70 +- .../wizardry/spell/Transportation.java | 46 +- .../wizardry/spell/VanishingBox.java | 16 +- .../wizardry/spell/WallOfFrost.java | 105 +- .../wizardry/spell/WaterBreathing.java | 26 +- .../electroblob/wizardry/spell/Whirlwind.java | 41 +- .../electroblob/wizardry/spell/Wither.java | 73 +- .../wizardry/spell/WitherSkull.java | 31 +- .../tileentity/ContainerArcaneWorkbench.java | 641 +++----- .../ContainerPortableWorkbench.java | 9 +- .../wizardry/tileentity/SlotItemList.java | 36 - .../wizardry/tileentity/SlotWandArmour.java | 20 +- .../wizardry/tileentity/SlotWizardry.java | 39 + .../tileentity/TileEntityArcaneWorkbench.java | 201 +-- .../tileentity/TileEntityMagicLight.java | 20 +- .../tileentity/TileEntityPlayerSave.java | 25 +- .../wizardry/tileentity/TileEntityStatue.java | 100 +- .../wizardry/tileentity/TileEntityTimer.java | 45 +- .../wizardry/util/IElementalDamage.java | 47 - .../wizardry/util/IndirectMinionDamage.java | 35 - .../wizardry/util/MinionDamage.java | 32 - .../wizardry/util/SpellModifiers.java | 143 -- .../wizardry/util/WizardryParticleType.java | 19 - .../wizardry/util/WizardryPathFinder.java | 138 -- .../wizardry/util/WizardryUtilities.java | 976 ------------ .../blockstates/arcane_workbench.json | 6 - .../wizardry/blockstates/crystal_block.json | 6 - .../wizardry/blockstates/crystal_flower.json | 5 - .../wizardry/blockstates/crystal_ore.json | 6 - .../wizardry/blockstates/ice_statue.json | 6 - .../wizardry/blockstates/magic_light.json | 6 - .../assets/wizardry/blockstates/meteor.json | 6 - .../wizardry/blockstates/petrified_stone.json | 6 - .../assets/wizardry/blockstates/snare.json | 6 - .../wizardry/blockstates/spectral_block.json | 6 - .../blockstates/transportation_stone.json | 6 - .../blockstates/vanishing_cobweb.json | 6 - .../resources/assets/wizardry/lang/en_GB.lang | 1326 ++++++++-------- .../resources/assets/wizardry/lang/en_US.lang | 1324 ++++++++-------- .../resources/assets/wizardry/lang/es_ES.lang | 1323 ++++++++-------- .../resources/assets/wizardry/lang/es_MX.lang | 1323 ++++++++-------- .../resources/assets/wizardry/lang/ru_RU.lang | 991 ++++++------ .../loot_tables/chests/dungeon_additions.json | 92 -- .../loot_tables/chests/wizard_tower.json | 77 - .../loot_tables/entities/evil_wizard.json | 55 - .../loot_tables/entities/mob_additions.json | 29 - .../loot_tables/subsets/arcane_tomes.json | 46 - .../loot_tables/subsets/novice_wands.json | 50 - .../loot_tables/subsets/wand_upgrades.json | 50 - .../loot_tables/subsets/wizard_armour.json | 170 --- .../models/block/arcane_workbench.json | 21 - .../wizardry/models/block/crystal_block.json | 6 - .../wizardry/models/block/crystal_flower.json | 6 - .../wizardry/models/block/crystal_ore.json | 6 - .../wizardry/models/block/ice_statue.json | 3 - .../wizardry/models/block/magic_light.json | 16 - .../assets/wizardry/models/block/meteor.json | 6 - .../models/block/petrified_stone.json | 20 - .../assets/wizardry/models/block/snare.json | 16 - .../wizardry/models/block/spectral_block.json | 6 - .../models/block/transportation_stone.json | 20 - .../models/block/vanishing_cobweb.json | 6 - .../models/item/advanced_earth_wand.json | 6 - .../models/item/advanced_fire_wand.json | 6 - .../models/item/advanced_healing_wand.json | 6 - .../models/item/advanced_ice_wand.json | 6 - .../models/item/advanced_lightning_wand.json | 6 - .../models/item/advanced_necromancy_wand.json | 6 - .../models/item/advanced_sorcery_wand.json | 6 - .../wizardry/models/item/advanced_wand.json | 6 - .../models/item/apprentice_earth_wand.json | 6 - .../models/item/apprentice_fire_wand.json | 6 - .../models/item/apprentice_healing_wand.json | 6 - .../models/item/apprentice_ice_wand.json | 6 - .../item/apprentice_lightning_wand.json | 6 - .../item/apprentice_necromancy_wand.json | 6 - .../models/item/apprentice_sorcery_wand.json | 6 - .../wizardry/models/item/apprentice_wand.json | 6 - .../wizardry/models/item/arcane_tome.json | 6 - .../models/item/arcane_workbench.json | 3 - .../wizardry/models/item/armour_upgrade.json | 6 - .../models/item/attunement_upgrade.json | 6 - .../models/item/basic_earth_wand.json | 6 - .../wizardry/models/item/basic_fire_wand.json | 6 - .../models/item/basic_healing_wand.json | 6 - .../wizardry/models/item/basic_ice_wand.json | 6 - .../models/item/basic_lightning_wand.json | 6 - .../models/item/basic_necromancy_wand.json | 6 - .../models/item/basic_sorcery_wand.json | 6 - .../wizardry/models/item/blank_scroll.json | 6 - .../wizardry/models/item/blast_upgrade.json | 6 - .../models/item/condenser_upgrade.json | 6 - .../models/item/cooldown_upgrade.json | 6 - .../wizardry/models/item/crystal_block.json | 3 - .../wizardry/models/item/crystal_flower.json | 6 - .../wizardry/models/item/crystal_ore.json | 3 - .../models/item/duration_upgrade.json | 6 - .../assets/wizardry/models/item/firebomb.json | 6 - .../wizardry/models/item/flaming_axe.json | 6 - .../wizardry/models/item/frost_axe.json | 6 - .../wizardry/models/item/ice_statue.json | 3 - .../models/item/identification_scroll.json | 6 - .../wizardry/models/item/magic_crystal.json | 6 - .../wizardry/models/item/magic_light.json | 3 - .../wizardry/models/item/magic_silk.json | 6 - .../wizardry/models/item/magic_wand.json | 6 - .../wizardry/models/item/mana_flask.json | 6 - .../models/item/master_earth_wand.json | 6 - .../models/item/master_fire_wand.json | 6 - .../models/item/master_healing_wand.json | 6 - .../wizardry/models/item/master_ice_wand.json | 6 - .../models/item/master_lightning_wand.json | 6 - .../models/item/master_necromancy_wand.json | 6 - .../models/item/master_sorcery_wand.json | 6 - .../wizardry/models/item/master_wand.json | 6 - .../assets/wizardry/models/item/meteor.json | 3 - .../wizardry/models/item/petrified_stone.json | 3 - .../wizardry/models/item/poison_bomb.json | 6 - .../wizardry/models/item/range_upgrade.json | 6 - .../assets/wizardry/models/item/scroll.json | 6 - .../wizardry/models/item/siphon_upgrade.json | 6 - .../wizardry/models/item/smoke_bomb.json | 6 - .../assets/wizardry/models/item/snare.json | 6 - .../wizardry/models/item/spawn_wizard.json | 7 - .../wizardry/models/item/spectral_block.json | 3 - .../wizardry/models/item/spectral_boots.json | 6 - .../wizardry/models/item/spectral_bow.json | 50 - .../models/item/spectral_bow_pulling_0.json | 6 - .../models/item/spectral_bow_pulling_1.json | 6 - .../models/item/spectral_bow_pulling_2.json | 6 - .../models/item/spectral_chestplate.json | 6 - .../wizardry/models/item/spectral_helmet.json | 6 - .../models/item/spectral_leggings.json | 6 - .../models/item/spectral_pickaxe.json | 6 - .../wizardry/models/item/spectral_sword.json | 6 - .../wizardry/models/item/spell_book.json | 6 - .../wizardry/models/item/storage_upgrade.json | 6 - .../models/item/transportation_stone.json | 3 - .../models/item/vanishing_cobweb.json | 6 - .../wizardry/models/item/wizard_boots.json | 6 - .../models/item/wizard_boots_earth.json | 6 - .../models/item/wizard_boots_fire.json | 6 - .../models/item/wizard_boots_healing.json | 6 - .../models/item/wizard_boots_ice.json | 6 - .../models/item/wizard_boots_lightning.json | 6 - .../models/item/wizard_boots_necromancy.json | 6 - .../models/item/wizard_boots_sorcery.json | 6 - .../wizardry/models/item/wizard_handbook.json | 6 - .../wizardry/models/item/wizard_hat.json | 6 - .../models/item/wizard_hat_earth.json | 6 - .../wizardry/models/item/wizard_hat_fire.json | 6 - .../models/item/wizard_hat_healing.json | 6 - .../wizardry/models/item/wizard_hat_ice.json | 6 - .../models/item/wizard_hat_lightning.json | 6 - .../models/item/wizard_hat_necromancy.json | 6 - .../models/item/wizard_hat_sorcery.json | 6 - .../wizardry/models/item/wizard_leggings.json | 6 - .../models/item/wizard_leggings_earth.json | 6 - .../models/item/wizard_leggings_fire.json | 6 - .../models/item/wizard_leggings_healing.json | 6 - .../models/item/wizard_leggings_ice.json | 6 - .../item/wizard_leggings_lightning.json | 6 - .../item/wizard_leggings_necromancy.json | 6 - .../models/item/wizard_leggings_sorcery.json | 6 - .../wizardry/models/item/wizard_robe.json | 6 - .../models/item/wizard_robe_earth.json | 6 - .../models/item/wizard_robe_fire.json | 6 - .../models/item/wizard_robe_healing.json | 6 - .../wizardry/models/item/wizard_robe_ice.json | 6 - .../models/item/wizard_robe_lightning.json | 6 - .../models/item/wizard_robe_necromancy.json | 6 - .../models/item/wizard_robe_sorcery.json | 6 - .../resources/assets/wizardry/sounds.json | 45 +- .../assets/wizardry/sounds/force.ogg | Bin 32548 -> 0 bytes .../assets/wizardry/texts/handbook_en_GB.txt | 3 +- .../assets/wizardry/texts/handbook_en_US.txt | 3 +- .../assets/wizardry/texts/handbook_es_ES.txt | 3 +- .../assets/wizardry/texts/handbook_es_MX.txt | 3 +- .../assets/wizardry/texts/handbook_ru_RU.txt | 3 +- .../textures/gui/arcane_workbench.png | Bin 4698 -> 5761 bytes .../textures/gui/element_icon_earth.png | Bin 368 -> 0 bytes .../textures/gui/element_icon_fire.png | Bin 280 -> 0 bytes .../textures/gui/element_icon_healing.png | Bin 312 -> 0 bytes .../textures/gui/element_icon_ice.png | Bin 330 -> 0 bytes .../textures/gui/element_icon_lightning.png | Bin 290 -> 0 bytes .../textures/gui/element_icon_necromancy.png | Bin 338 -> 0 bytes .../textures/gui/element_icon_simple.png | Bin 248 -> 0 bytes .../textures/gui/element_icon_sorcery.png | Bin 328 -> 0 bytes .../textures/gui/empty_slot_crystal.png | Bin 240 -> 0 bytes .../textures/gui/empty_slot_upgrade.png | Bin 211 -> 0 bytes .../textures/gui/sixth_sense_overlay.png | Bin 56730 -> 45905 bytes src/main/resources/mcmod.info | 2 +- 573 files changed, 28574 insertions(+), 26017 deletions(-) delete mode 100644 gradle.properties create mode 100644 libs/antiqueatlas-1.7.10-4.2.10-deobf.jar create mode 100644 libs/antiqueatlas-1.7.10-4.2.10-sources.jar create mode 100644 src/main/java/electroblob/wizardry/EnumElement.java create mode 100644 src/main/java/electroblob/wizardry/EnumParticleType.java create mode 100644 src/main/java/electroblob/wizardry/EnumSpellType.java create mode 100644 src/main/java/electroblob/wizardry/EnumTier.java create mode 100644 src/main/java/electroblob/wizardry/ExtendedPlayer.java create mode 100644 src/main/java/electroblob/wizardry/IElementalDamage.java rename src/main/java/electroblob/wizardry/{util => }/IndirectMagicDamage.java (58%) rename src/main/java/electroblob/wizardry/{util => }/MagicDamage.java (69%) delete mode 100644 src/main/java/electroblob/wizardry/Settings.java rename src/main/java/electroblob/wizardry/{util => }/WandHelper.java (71%) create mode 100644 src/main/java/electroblob/wizardry/WeightedRandomSpellBook.java delete mode 100644 src/main/java/electroblob/wizardry/WizardData.java create mode 100644 src/main/java/electroblob/wizardry/WizardryRegistry.java create mode 100644 src/main/java/electroblob/wizardry/WizardryUtilities.java create mode 100644 src/main/java/electroblob/wizardry/block/BlockCrystal.java create mode 100644 src/main/java/electroblob/wizardry/client/GuiButtonNextPage.java delete mode 100644 src/main/java/electroblob/wizardry/client/GuiButtonTurnPage.java create mode 100644 src/main/java/electroblob/wizardry/client/model/ModelSkeletonMinion.java create mode 100644 src/main/java/electroblob/wizardry/client/model/ModelSpiritHorse.java create mode 100644 src/main/java/electroblob/wizardry/client/model/ModelSpiritWolf.java delete mode 100644 src/main/java/electroblob/wizardry/client/model/WizardryItemModels.java create mode 100644 src/main/java/electroblob/wizardry/client/particle/EntityBlizzardFX.java rename src/main/java/electroblob/wizardry/client/particle/{ParticleDarkMagic.java => EntityDarkMagicFX.java} (66%) rename src/main/java/electroblob/wizardry/client/particle/{ParticleDust.java => EntityDustFX.java} (64%) create mode 100644 src/main/java/electroblob/wizardry/client/particle/EntityIceFX.java create mode 100644 src/main/java/electroblob/wizardry/client/particle/EntityLeafFX.java rename src/main/java/electroblob/wizardry/client/particle/{ParticleGiantBubble.java => EntityMagicBubbleFX.java} (82%) create mode 100644 src/main/java/electroblob/wizardry/client/particle/EntityMagicFireFX.java create mode 100644 src/main/java/electroblob/wizardry/client/particle/EntityPathFX.java create mode 100644 src/main/java/electroblob/wizardry/client/particle/EntitySnowFX.java create mode 100644 src/main/java/electroblob/wizardry/client/particle/EntitySparkFX.java create mode 100644 src/main/java/electroblob/wizardry/client/particle/EntitySparkleFX.java create mode 100644 src/main/java/electroblob/wizardry/client/particle/EntityTornadoFX.java delete mode 100644 src/main/java/electroblob/wizardry/client/particle/ParticleBlizzard.java delete mode 100644 src/main/java/electroblob/wizardry/client/particle/ParticleCustomTexture.java delete mode 100644 src/main/java/electroblob/wizardry/client/particle/ParticleIce.java delete mode 100644 src/main/java/electroblob/wizardry/client/particle/ParticleLeaf.java delete mode 100644 src/main/java/electroblob/wizardry/client/particle/ParticleMagicFlame.java delete mode 100644 src/main/java/electroblob/wizardry/client/particle/ParticlePath.java delete mode 100644 src/main/java/electroblob/wizardry/client/particle/ParticleRotatingSparkle.java delete mode 100644 src/main/java/electroblob/wizardry/client/particle/ParticleSnow.java delete mode 100644 src/main/java/electroblob/wizardry/client/particle/ParticleSpark.java delete mode 100644 src/main/java/electroblob/wizardry/client/particle/ParticleSparkle.java delete mode 100644 src/main/java/electroblob/wizardry/client/particle/ParticleTornado.java delete mode 100644 src/main/java/electroblob/wizardry/client/renderer/LayerStone.java create mode 100644 src/main/java/electroblob/wizardry/client/renderer/RenderFallingGrass.java create mode 100644 src/main/java/electroblob/wizardry/client/renderer/RenderFirestorm.java create mode 100644 src/main/java/electroblob/wizardry/client/renderer/RenderMagicSlime.java create mode 100644 src/main/java/electroblob/wizardry/client/renderer/RenderMeteor.java create mode 100644 src/main/java/electroblob/wizardry/client/renderer/RenderShield.java create mode 100644 src/main/java/electroblob/wizardry/client/renderer/RenderSilverfishMinion.java create mode 100644 src/main/java/electroblob/wizardry/client/renderer/RenderSkeletonMinion.java create mode 100644 src/main/java/electroblob/wizardry/client/renderer/RenderSpiderMinion.java create mode 100644 src/main/java/electroblob/wizardry/client/renderer/RenderTransientPlayer.java create mode 100644 src/main/java/electroblob/wizardry/client/renderer/RenderTranslucentItem.java create mode 100644 src/main/java/electroblob/wizardry/client/renderer/RenderWand.java create mode 100644 src/main/java/electroblob/wizardry/client/renderer/RenderZombieMinion.java delete mode 100644 src/main/java/electroblob/wizardry/constants/Constants.java delete mode 100644 src/main/java/electroblob/wizardry/constants/Element.java delete mode 100644 src/main/java/electroblob/wizardry/constants/SpellType.java delete mode 100644 src/main/java/electroblob/wizardry/constants/Tier.java delete mode 100644 src/main/java/electroblob/wizardry/enchantment/Imbuement.java create mode 100644 src/main/java/electroblob/wizardry/entity/EntityFallingGrass.java create mode 100644 src/main/java/electroblob/wizardry/entity/EntityMagicSlime.java delete mode 100644 src/main/java/electroblob/wizardry/entity/living/EntityAIAttackSpell.java delete mode 100644 src/main/java/electroblob/wizardry/entity/living/EntityAISelectSpell.java delete mode 100644 src/main/java/electroblob/wizardry/entity/living/EntityMagicSlime.java delete mode 100644 src/main/java/electroblob/wizardry/entity/living/IIntelligentSpellCaster.java delete mode 100644 src/main/java/electroblob/wizardry/entity/living/ISpellCaster.java delete mode 100644 src/main/java/electroblob/wizardry/entity/living/ISummonedCreature.java delete mode 100644 src/main/java/electroblob/wizardry/entity/projectile/EntityBomb.java delete mode 100644 src/main/java/electroblob/wizardry/event/DiscoverSpellEvent.java delete mode 100644 src/main/java/electroblob/wizardry/event/SpellBindEvent.java delete mode 100644 src/main/java/electroblob/wizardry/event/SpellCastEvent.java delete mode 100644 src/main/java/electroblob/wizardry/item/IConjuredItem.java create mode 100644 src/main/java/electroblob/wizardry/item/ItemSpawnWizard.java delete mode 100644 src/main/java/electroblob/wizardry/loot/RandomSpell.java delete mode 100644 src/main/java/electroblob/wizardry/loot/WizardSpell.java delete mode 100644 src/main/java/electroblob/wizardry/packet/PacketNPCCastSpell.java create mode 100644 src/main/java/electroblob/wizardry/packet/PacketParticleSpawn.java delete mode 100644 src/main/java/electroblob/wizardry/packet/PacketSyncSettings.java delete mode 100644 src/main/java/electroblob/wizardry/potion/ICustomPotionParticles.java delete mode 100644 src/main/java/electroblob/wizardry/potion/PotionMagicEffectParticles.java delete mode 100644 src/main/java/electroblob/wizardry/registry/Spells.java delete mode 100644 src/main/java/electroblob/wizardry/registry/WizardryAchievements.java delete mode 100644 src/main/java/electroblob/wizardry/registry/WizardryBlocks.java delete mode 100644 src/main/java/electroblob/wizardry/registry/WizardryEnchantments.java delete mode 100644 src/main/java/electroblob/wizardry/registry/WizardryItems.java delete mode 100644 src/main/java/electroblob/wizardry/registry/WizardryPotions.java delete mode 100644 src/main/java/electroblob/wizardry/registry/WizardryRegistry.java delete mode 100644 src/main/java/electroblob/wizardry/registry/WizardrySounds.java delete mode 100644 src/main/java/electroblob/wizardry/registry/WizardryTabs.java delete mode 100644 src/main/java/electroblob/wizardry/tileentity/SlotItemList.java create mode 100644 src/main/java/electroblob/wizardry/tileentity/SlotWizardry.java delete mode 100644 src/main/java/electroblob/wizardry/util/IElementalDamage.java delete mode 100644 src/main/java/electroblob/wizardry/util/IndirectMinionDamage.java delete mode 100644 src/main/java/electroblob/wizardry/util/MinionDamage.java delete mode 100644 src/main/java/electroblob/wizardry/util/SpellModifiers.java delete mode 100644 src/main/java/electroblob/wizardry/util/WizardryParticleType.java delete mode 100644 src/main/java/electroblob/wizardry/util/WizardryPathFinder.java delete mode 100644 src/main/java/electroblob/wizardry/util/WizardryUtilities.java delete mode 100644 src/main/resources/assets/wizardry/blockstates/arcane_workbench.json delete mode 100644 src/main/resources/assets/wizardry/blockstates/crystal_block.json delete mode 100644 src/main/resources/assets/wizardry/blockstates/crystal_flower.json delete mode 100644 src/main/resources/assets/wizardry/blockstates/crystal_ore.json delete mode 100644 src/main/resources/assets/wizardry/blockstates/ice_statue.json delete mode 100644 src/main/resources/assets/wizardry/blockstates/magic_light.json delete mode 100644 src/main/resources/assets/wizardry/blockstates/meteor.json delete mode 100644 src/main/resources/assets/wizardry/blockstates/petrified_stone.json delete mode 100644 src/main/resources/assets/wizardry/blockstates/snare.json delete mode 100644 src/main/resources/assets/wizardry/blockstates/spectral_block.json delete mode 100644 src/main/resources/assets/wizardry/blockstates/transportation_stone.json delete mode 100644 src/main/resources/assets/wizardry/blockstates/vanishing_cobweb.json delete mode 100644 src/main/resources/assets/wizardry/loot_tables/chests/dungeon_additions.json delete mode 100644 src/main/resources/assets/wizardry/loot_tables/chests/wizard_tower.json delete mode 100644 src/main/resources/assets/wizardry/loot_tables/entities/evil_wizard.json delete mode 100644 src/main/resources/assets/wizardry/loot_tables/entities/mob_additions.json delete mode 100644 src/main/resources/assets/wizardry/loot_tables/subsets/arcane_tomes.json delete mode 100644 src/main/resources/assets/wizardry/loot_tables/subsets/novice_wands.json delete mode 100644 src/main/resources/assets/wizardry/loot_tables/subsets/wand_upgrades.json delete mode 100644 src/main/resources/assets/wizardry/loot_tables/subsets/wizard_armour.json delete mode 100644 src/main/resources/assets/wizardry/models/block/arcane_workbench.json delete mode 100644 src/main/resources/assets/wizardry/models/block/crystal_block.json delete mode 100644 src/main/resources/assets/wizardry/models/block/crystal_flower.json delete mode 100644 src/main/resources/assets/wizardry/models/block/crystal_ore.json delete mode 100644 src/main/resources/assets/wizardry/models/block/ice_statue.json delete mode 100644 src/main/resources/assets/wizardry/models/block/magic_light.json delete mode 100644 src/main/resources/assets/wizardry/models/block/meteor.json delete mode 100644 src/main/resources/assets/wizardry/models/block/petrified_stone.json delete mode 100644 src/main/resources/assets/wizardry/models/block/snare.json delete mode 100644 src/main/resources/assets/wizardry/models/block/spectral_block.json delete mode 100644 src/main/resources/assets/wizardry/models/block/transportation_stone.json delete mode 100644 src/main/resources/assets/wizardry/models/block/vanishing_cobweb.json delete mode 100644 src/main/resources/assets/wizardry/models/item/advanced_earth_wand.json delete mode 100644 src/main/resources/assets/wizardry/models/item/advanced_fire_wand.json delete mode 100644 src/main/resources/assets/wizardry/models/item/advanced_healing_wand.json delete mode 100644 src/main/resources/assets/wizardry/models/item/advanced_ice_wand.json delete mode 100644 src/main/resources/assets/wizardry/models/item/advanced_lightning_wand.json delete mode 100644 src/main/resources/assets/wizardry/models/item/advanced_necromancy_wand.json delete mode 100644 src/main/resources/assets/wizardry/models/item/advanced_sorcery_wand.json delete mode 100644 src/main/resources/assets/wizardry/models/item/advanced_wand.json delete mode 100644 src/main/resources/assets/wizardry/models/item/apprentice_earth_wand.json delete mode 100644 src/main/resources/assets/wizardry/models/item/apprentice_fire_wand.json delete mode 100644 src/main/resources/assets/wizardry/models/item/apprentice_healing_wand.json delete mode 100644 src/main/resources/assets/wizardry/models/item/apprentice_ice_wand.json delete mode 100644 src/main/resources/assets/wizardry/models/item/apprentice_lightning_wand.json delete mode 100644 src/main/resources/assets/wizardry/models/item/apprentice_necromancy_wand.json delete mode 100644 src/main/resources/assets/wizardry/models/item/apprentice_sorcery_wand.json delete mode 100644 src/main/resources/assets/wizardry/models/item/apprentice_wand.json delete mode 100644 src/main/resources/assets/wizardry/models/item/arcane_tome.json delete mode 100644 src/main/resources/assets/wizardry/models/item/arcane_workbench.json delete mode 100644 src/main/resources/assets/wizardry/models/item/armour_upgrade.json delete mode 100644 src/main/resources/assets/wizardry/models/item/attunement_upgrade.json delete mode 100644 src/main/resources/assets/wizardry/models/item/basic_earth_wand.json delete mode 100644 src/main/resources/assets/wizardry/models/item/basic_fire_wand.json delete mode 100644 src/main/resources/assets/wizardry/models/item/basic_healing_wand.json delete mode 100644 src/main/resources/assets/wizardry/models/item/basic_ice_wand.json delete mode 100644 src/main/resources/assets/wizardry/models/item/basic_lightning_wand.json delete mode 100644 src/main/resources/assets/wizardry/models/item/basic_necromancy_wand.json delete mode 100644 src/main/resources/assets/wizardry/models/item/basic_sorcery_wand.json delete mode 100644 src/main/resources/assets/wizardry/models/item/blank_scroll.json delete mode 100644 src/main/resources/assets/wizardry/models/item/blast_upgrade.json delete mode 100644 src/main/resources/assets/wizardry/models/item/condenser_upgrade.json delete mode 100644 src/main/resources/assets/wizardry/models/item/cooldown_upgrade.json delete mode 100644 src/main/resources/assets/wizardry/models/item/crystal_block.json delete mode 100644 src/main/resources/assets/wizardry/models/item/crystal_flower.json delete mode 100644 src/main/resources/assets/wizardry/models/item/crystal_ore.json delete mode 100644 src/main/resources/assets/wizardry/models/item/duration_upgrade.json delete mode 100644 src/main/resources/assets/wizardry/models/item/firebomb.json delete mode 100644 src/main/resources/assets/wizardry/models/item/flaming_axe.json delete mode 100644 src/main/resources/assets/wizardry/models/item/frost_axe.json delete mode 100644 src/main/resources/assets/wizardry/models/item/ice_statue.json delete mode 100644 src/main/resources/assets/wizardry/models/item/identification_scroll.json delete mode 100644 src/main/resources/assets/wizardry/models/item/magic_crystal.json delete mode 100644 src/main/resources/assets/wizardry/models/item/magic_light.json delete mode 100644 src/main/resources/assets/wizardry/models/item/magic_silk.json delete mode 100644 src/main/resources/assets/wizardry/models/item/magic_wand.json delete mode 100644 src/main/resources/assets/wizardry/models/item/mana_flask.json delete mode 100644 src/main/resources/assets/wizardry/models/item/master_earth_wand.json delete mode 100644 src/main/resources/assets/wizardry/models/item/master_fire_wand.json delete mode 100644 src/main/resources/assets/wizardry/models/item/master_healing_wand.json delete mode 100644 src/main/resources/assets/wizardry/models/item/master_ice_wand.json delete mode 100644 src/main/resources/assets/wizardry/models/item/master_lightning_wand.json delete mode 100644 src/main/resources/assets/wizardry/models/item/master_necromancy_wand.json delete mode 100644 src/main/resources/assets/wizardry/models/item/master_sorcery_wand.json delete mode 100644 src/main/resources/assets/wizardry/models/item/master_wand.json delete mode 100644 src/main/resources/assets/wizardry/models/item/meteor.json delete mode 100644 src/main/resources/assets/wizardry/models/item/petrified_stone.json delete mode 100644 src/main/resources/assets/wizardry/models/item/poison_bomb.json delete mode 100644 src/main/resources/assets/wizardry/models/item/range_upgrade.json delete mode 100644 src/main/resources/assets/wizardry/models/item/scroll.json delete mode 100644 src/main/resources/assets/wizardry/models/item/siphon_upgrade.json delete mode 100644 src/main/resources/assets/wizardry/models/item/smoke_bomb.json delete mode 100644 src/main/resources/assets/wizardry/models/item/snare.json delete mode 100644 src/main/resources/assets/wizardry/models/item/spawn_wizard.json delete mode 100644 src/main/resources/assets/wizardry/models/item/spectral_block.json delete mode 100644 src/main/resources/assets/wizardry/models/item/spectral_boots.json delete mode 100644 src/main/resources/assets/wizardry/models/item/spectral_bow.json delete mode 100644 src/main/resources/assets/wizardry/models/item/spectral_bow_pulling_0.json delete mode 100644 src/main/resources/assets/wizardry/models/item/spectral_bow_pulling_1.json delete mode 100644 src/main/resources/assets/wizardry/models/item/spectral_bow_pulling_2.json delete mode 100644 src/main/resources/assets/wizardry/models/item/spectral_chestplate.json delete mode 100644 src/main/resources/assets/wizardry/models/item/spectral_helmet.json delete mode 100644 src/main/resources/assets/wizardry/models/item/spectral_leggings.json delete mode 100644 src/main/resources/assets/wizardry/models/item/spectral_pickaxe.json delete mode 100644 src/main/resources/assets/wizardry/models/item/spectral_sword.json delete mode 100644 src/main/resources/assets/wizardry/models/item/spell_book.json delete mode 100644 src/main/resources/assets/wizardry/models/item/storage_upgrade.json delete mode 100644 src/main/resources/assets/wizardry/models/item/transportation_stone.json delete mode 100644 src/main/resources/assets/wizardry/models/item/vanishing_cobweb.json delete mode 100644 src/main/resources/assets/wizardry/models/item/wizard_boots.json delete mode 100644 src/main/resources/assets/wizardry/models/item/wizard_boots_earth.json delete mode 100644 src/main/resources/assets/wizardry/models/item/wizard_boots_fire.json delete mode 100644 src/main/resources/assets/wizardry/models/item/wizard_boots_healing.json delete mode 100644 src/main/resources/assets/wizardry/models/item/wizard_boots_ice.json delete mode 100644 src/main/resources/assets/wizardry/models/item/wizard_boots_lightning.json delete mode 100644 src/main/resources/assets/wizardry/models/item/wizard_boots_necromancy.json delete mode 100644 src/main/resources/assets/wizardry/models/item/wizard_boots_sorcery.json delete mode 100644 src/main/resources/assets/wizardry/models/item/wizard_handbook.json delete mode 100644 src/main/resources/assets/wizardry/models/item/wizard_hat.json delete mode 100644 src/main/resources/assets/wizardry/models/item/wizard_hat_earth.json delete mode 100644 src/main/resources/assets/wizardry/models/item/wizard_hat_fire.json delete mode 100644 src/main/resources/assets/wizardry/models/item/wizard_hat_healing.json delete mode 100644 src/main/resources/assets/wizardry/models/item/wizard_hat_ice.json delete mode 100644 src/main/resources/assets/wizardry/models/item/wizard_hat_lightning.json delete mode 100644 src/main/resources/assets/wizardry/models/item/wizard_hat_necromancy.json delete mode 100644 src/main/resources/assets/wizardry/models/item/wizard_hat_sorcery.json delete mode 100644 src/main/resources/assets/wizardry/models/item/wizard_leggings.json delete mode 100644 src/main/resources/assets/wizardry/models/item/wizard_leggings_earth.json delete mode 100644 src/main/resources/assets/wizardry/models/item/wizard_leggings_fire.json delete mode 100644 src/main/resources/assets/wizardry/models/item/wizard_leggings_healing.json delete mode 100644 src/main/resources/assets/wizardry/models/item/wizard_leggings_ice.json delete mode 100644 src/main/resources/assets/wizardry/models/item/wizard_leggings_lightning.json delete mode 100644 src/main/resources/assets/wizardry/models/item/wizard_leggings_necromancy.json delete mode 100644 src/main/resources/assets/wizardry/models/item/wizard_leggings_sorcery.json delete mode 100644 src/main/resources/assets/wizardry/models/item/wizard_robe.json delete mode 100644 src/main/resources/assets/wizardry/models/item/wizard_robe_earth.json delete mode 100644 src/main/resources/assets/wizardry/models/item/wizard_robe_fire.json delete mode 100644 src/main/resources/assets/wizardry/models/item/wizard_robe_healing.json delete mode 100644 src/main/resources/assets/wizardry/models/item/wizard_robe_ice.json delete mode 100644 src/main/resources/assets/wizardry/models/item/wizard_robe_lightning.json delete mode 100644 src/main/resources/assets/wizardry/models/item/wizard_robe_necromancy.json delete mode 100644 src/main/resources/assets/wizardry/models/item/wizard_robe_sorcery.json delete mode 100644 src/main/resources/assets/wizardry/sounds/force.ogg delete mode 100644 src/main/resources/assets/wizardry/textures/gui/element_icon_earth.png delete mode 100644 src/main/resources/assets/wizardry/textures/gui/element_icon_fire.png delete mode 100644 src/main/resources/assets/wizardry/textures/gui/element_icon_healing.png delete mode 100644 src/main/resources/assets/wizardry/textures/gui/element_icon_ice.png delete mode 100644 src/main/resources/assets/wizardry/textures/gui/element_icon_lightning.png delete mode 100644 src/main/resources/assets/wizardry/textures/gui/element_icon_necromancy.png delete mode 100644 src/main/resources/assets/wizardry/textures/gui/element_icon_simple.png delete mode 100644 src/main/resources/assets/wizardry/textures/gui/element_icon_sorcery.png delete mode 100644 src/main/resources/assets/wizardry/textures/gui/empty_slot_crystal.png delete mode 100644 src/main/resources/assets/wizardry/textures/gui/empty_slot_upgrade.png diff --git a/build.gradle b/build.gradle index 5d35e7aa..92e3f5b5 100644 --- a/build.gradle +++ b/build.gradle @@ -1,34 +1,44 @@ buildscript { repositories { - jcenter() - maven { url = "http://files.minecraftforge.net/maven" } + mavenCentral() + maven { + name = "forge" + url = "http://files.minecraftforge.net/maven" + } + maven { + name = "sonatype" + url = "https://oss.sonatype.org/content/repositories/snapshots/" + } } dependencies { - classpath 'net.minecraftforge.gradle:ForgeGradle:2.2-SNAPSHOT' + classpath 'net.minecraftforge.gradle:ForgeGradle:1.2-SNAPSHOT' } } -apply plugin: 'net.minecraftforge.gradle.forge' -//Only edit below this line, the above code adds and enables the nessasary things for Forge to be setup. +apply plugin: 'forge' -version = " 2.1.2 - MC 1.10.2" +version = " 1.1.4 - MC 1.7.10" group= "electroblob.wizardry"// http://maven.apache.org/guides/mini/guide-naming-conventions.html archivesBaseName = "Electroblob's Wizardry " -srcCompat = JavaVersion.VERSION_1_8 -targetCompat = JavaVersion.VERSION_1_8 - minecraft { - version = "1.10.2-12.18.3.2185" - runDir = "run" - - // the mappings can be changed at any time, and must be in the following format. - // snapshot_YYYYMMDD snapshot are built nightly. - // stable_# stables are built at the discretion of the MCP team. - // Use non-default mappings at your own risk. they may not allways work. - // simply re-run your setup task after changing the mappings to update your workspace. - mappings = "snapshot_20161111" - // makeObfSourceJar = false // an Srg named sources jar is made by default. uncomment this to disable. + version = "1.7.10-10.13.4.1558-1.7.10" + runDir = "eclipse" +} + +task deobfJar(type: Jar) { // Generate deobfuscated + from sourceSets.main.output + classifier = 'deobf' +} +task sourceJar(type: Jar) { // Generate sources + from sourceSets.main.allSource + classifier = 'sources' +} +tasks.build.dependsOn('sourceJar', 'deobfJar') +artifacts { + archives deobfJar + archives sourceJar + archives jar } dependencies { @@ -41,14 +51,6 @@ dependencies { //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 diff --git a/gradle.properties b/gradle.properties deleted file mode 100644 index e9b9fd5a..00000000 --- a/gradle.properties +++ /dev/null @@ -1,3 +0,0 @@ -# Sets default memory used for gradle commands. Can be overridden by user or command line properties. -# This is required to provide enough memory for the Minecraft decompilation process. -org.gradle.jvmargs=-Xmx3G diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index 30d399d8d2bf522ff5de94bf434a7cc43a9a74b5..b7612167031001b7b84baf2a959e8ea8ad03c011 100644 GIT binary patch delta 21708 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 delta 22968 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 diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index e18cba72..678d9d8d 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ -#Mon Sep 14 12:28:28 PDT 2015 +#Wed Jul 02 15:54:47 CDT 2014 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-2.14-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-2.0-bin.zip diff --git a/libs/antiqueatlas-1.7.10-4.2.10-deobf.jar b/libs/antiqueatlas-1.7.10-4.2.10-deobf.jar new file mode 100644 index 0000000000000000000000000000000000000000..f7cf509b43aaa22de92fe076793cfb290e4092a8 GIT binary patch literal 1099560 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

d{BPK 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

u)z(Sm4!dq<>Zf6@&@0mE5u>!UecOt++zr9Y zkFkB`+MIu-ej3xtti^ExJiq@Me?H$Mt$BW8-IIOx;mapZi#=(zi05p4PFQRpIy2sOLXcl)lTm?7jM;?c^xal!(RW z{~U|i%y5?VXw$O?T&D!~YbmB~{u$NwUgScw&1~jwO;hD!Q!Txs_caMQQBnY4eUL3o8tvw!`j zLZEtMBsX=~P6x6VC%!RN^2yp`IsE3F*ef3CZ~ z>SFkAYYDcj`5O(z+H$hW*E}ft5boHyfio<`qAytYPH5Cz*Yh!JD*}yoi)wII{5@No zXs5Jc!5^0oyFSW2{_)K_RgHa`*NxdtiD@x&?YNr4jh;-bX>!ZazqQQ%=0me-xkjG_ zW20|RtJ%T&^ch>@%$XL4-rc@6@0iS1y&T~k^9-x^+3&ZV`avLV%M(3~RQ}?KpFwda znCn_k<*zER=CTVgW0|`6P_x^2C-Y4=WQ*2GKF)eH_h>^s(yS4vn5nIHvADy?z_5x? zF;kTh@9YOogteCs=3RCWV0%!#)Q*Kc?*xCt6K8z|6^F`J-@PhrTj$=&bIx?CxFUyksHi zIfMN&+x?bKeL6ta{ly_rJ>Q9c%tSkKeyH^rrar*ub={u`UIYD{Ef#zC0<^ zX8X+l#dd{uCC7@!E9MbTA0B6wiM(R8=yfEYh(YAx{;)gSxgTn`3M`FNS~2m)i#(C} zGbb9S%~vn5J^%1Wu5aMibfHx$juv(jd7DJ{m=|Y+ihRgo>+L_k&&76g0ms@^SFTT) z$9AUdx(U0t)LfJ<1xSDYAvEVIk&=elk)A_zihY_%51oHwywsb-LwDwxXhIlQ8eRkn$Z(6pBDQ~ zOirhbgoSio7xx}ui`lkzYhvzV<|vf}oh7e$d&Jjq&fR{;YUNbkn2C?p%bcw!3e;aK z?OC?$xAFQ5>pndTl#w?T6`iF0w%~H!%9Mn=>&%p5j(V9W7nweqalq=co9$E$!E^gP zw=*}(8v6!+E$sf&d(S?nX#L5L^`d`wCmMZk-27$zByRtIuV(m6e^tuM^=-bi&mS8G zL~9)sDixOeKTb0+FuY)7U@#*hR8n&bN-7=mz3yN>ippT%poH(uZ89GuP}uTjN5OD-`cS4 zBB!p9L0XV4Z%?@%$K359bFZ9Y^ICFK!`(P8re*q^)49v@rDMy^{Suv(IsJEwquPAa zRLe=bZ@u^3y;Pg|b=g+ohgHj5jFm!}`#9Ga+rC;E(OF=qc9)T-?2L)$mE*dX}peR2rGbfdSq2kuu;PjB-mtwZx?|pId?r50eogXC1p%CQM-5@eiO-qqe zz^g-X;Z4!NgMzNEEYc?ybTIezHS~xE>hN@N-C%#BxTvpraYxsw-w&%7XJ6a%{n^=n zTd&(aU-NnH=JV_aIC!Rc7zdg*@T_!_6MWEo{K&C~PxKrbxl{@m1UNRQC>H-!W@PwL zXJezSbbx&y17pQyZVrY6zOxS)$h80Gf8{hYhOwcA@j%H$3vZ4F5eA2A zfAIMX1H*!eqJj^aKmV;b$G7g>IkUcPeDS)k_Otz0N=kbC@9Fu~1r7`h4@>GU{?pOl zEY)Dm){y&ek?K2^j5zKErQf^LHt{=bWmxd?R`}-vP}sOGoUrre&8JVE-0(W$6rFe~ zy!K!Eq4)#ge{+7;+5Gwb^})VJDvd_5jqWTz9vnLJ#b>F^`AM8DNq3(0yZ_nF{r`{b zWu+sYNjoF7L>>nU752Hz>iKN6RH@TxlIBs>OaD_I-PifeYWWbA;}DSvI+hg2zO|(A2VAS-dZI8R*l*UbgwpVzz zI`3RzF4f|;i2FiHiQ%k%u?4d)6m5~R?J-;U{KD!C{#*Ta&c_W7ypUiwK9XagckHm! zMhhLz$;qcTDy`vJeN1XY*c#^DonMpBZ!BjKYIHroX~t6Sq>w1*=n>%);djGhg@TNV ziQp|xb57=#z(eXvlUI0eQQIZ>^N2)}kzsp;hmP_!p?+a~f$9$DBjzeXoz6z;J0~!C zZd?*_N$8T>rMw{JmpUtFn|MemPd<4oC3_RQe9v6}qGXrg-KlbZ(=UWyI{ni9#q<}ccg1b&X!=8 zNS7>^SUSjg=7z6lJfF!wGe0y!YnxV$=)_Z8Q@y6DO_kQt4_Uct*{XZ1 zxI;gOo(}0>85dl;&Mnk9`0+~4E7Mkou9_S0I#@b5eWmq^_EqUC@|h`$D9&B&gUA-r+=>Nx+n5p^O2_XG83PjhN&^xtENq#7O~o6b>M3E z*L%&@yD!O4JN$0)-pRGQYJ30Mvh%jzc z?WKNt#Cd`9H;f-|6F#kZBy(=??91-y;^{k!n?HAScX~IS)|+mpH(UDH-4lDCc$IuD zjD1*j>FeCtn`a+-yW;lxcV_Q0-?iVFf5v3K=DbMb;tPjQI6nw{deNSH{#1kP`)lg_ zo&6irw?0?aPu8CpzV`Zt?Mus7eNTKZZuiZuX#Qin$2R8ojP6CopD{nM{lxau{)hAD z#kbyfu2=q_^1tf;MTX@Ks*LH(osEu-(M-R8RMjZ%@3XhMo%(Il&9{y{3wAn+I`%iM zZF+ietzyQBHxt$>et$mW+{AO4e60a>FRoo+PkffxeYlQCy|ukHyv@F^pd&)+qTzJY z@{4zzi|@Pqa+&6GZ+5z&zvX&eFHEYQ)?K&m32Jl)x%zH-7IA6}ok zv#sWd%_>vKHL|tZYuG$#;)Ft{Rf@iox23;If0kBudQEEUrhiBCy0`1gO=Ulsy?MeK z)<|8hZBENxghXjv*J9t4o*JIE|7_^9RleR5w z^A6XQjd-?qO_t>*o5IT5T#rRrwcEnj*4XUm+NPCz)qnNd@agO7_NrC+W;eziJk@k? zD`WO+?oh6$t+lW3Tz?zuAE6(4e{15ewz<-FqMcpqL~e^u7p=dcai`*`bbJ1({WI@O z*|Ku=k)0{$(=MgGe0EQFUNqmdRnwnM=gZ^0d20*r{je=>zkYMQtv~&&#_iqB+njeU z-fy#GPwMiM=gwN&hb#&C67nm=?A@iiTNAJU$h-JXx_ZwylQ&myNALOm*Y4!L+o6o1 zzE{((rvCQ)z4^QSy3SQEe@y$G8@tvoxA5(8=1a_f*_HXWUEg`-s_Z4(kf8o++OOUD zj?3@+H}`$HF0UDHXz!e3kCY2sDxbXk&Uyd49Iw@_vYzTS57{rvm&vBf>SsC4`ZUXJ zcJQp<(Mi#}{Lk23y!Pr|)gSlU^89m5Y$7YYK8Kvly!Eo_^5pbS=X>T(om*R{_w!D| z?M2(FpC_NYpT(d5ewRSW!%MFJyPsR{u|5~`X-CPYO}C#4hwliVwcaW|_f^R&n}3rp z`ybyuXZNgiJ7cnUzN^~1wL0zlOX+#$=k9*lTlrD!|Fx_0EN$=Z{als*{pk0v_r>nk z6{-I{cY62Da`ks>_xSIL|Kjl0@uu_L_N(^M|L)v)f2Tg{K>32t7w@=#XKQUf|8L^g zetu`x{+a!!+j!ev&$F2=~_`gK39zP^6$;*5(6 zr+=D0d4F!r(!Z|`yp~*l``Dpla{EqJ7#qfY3H$x_VELVL+4qa@^ZP%kQK;AWpYe0! z*We4we@~X2Jo!Hh^Ni1bl8tq5GcYhHBzpw;GB8xBGB7kWGcf%8&%n^|l7XSrfPvvv z0t1893_6N?gUeUtL*gv$N*s z-?FMtHbK1`^IlKO`*W>U?5q8}T@I@w^jR44?>-N`n}65tPj6EGw3V*EKYjaFb>iCE zVn^H3#vf~c`}oHy?6Z=4Jbm4}8~o3M7#Z&E`_I8pP*(h?_UE6Jod0$C=<|INPk`>cI0j?ZybPxg%AZkKI}!u=oDlP*JnFyt1;}FLG?I z{JbXhVo$Q>m;4Z~t-th@t}`;s)O_CX-{+XJ#?mG`FM)>2P)3F&Ng{{hjoXVm3l$GA z%&XqQE`H@%(8Gfz&tm^SY2Ezoy|MJKo9fHf2WuK?a>wj1VU&)Zy<^AD#`*y~ zu@zmCO(*nvLbSV?S|fQDZCx<6{C{QW!WHV1I1Khb%u7&o$#(W%e)&!7BCERnpDj-X z*{{FZGC8oiaL3~?=7~wEa_SaSW-uyoe6CYfjnK4R_wHEmTVEr|UIxW4H&{B<=d!M! zv2cc=`RvkS8@4&`@9ZiTkFS5uSNHN2-??0$5C85pvgbT!7FgeSmUa8d3>G0KP=I~C z_j||J2ZHnV6o^?**K=aacAjBrJHvFpp;Yf12@a**VRznZq#fuvY{F!-R3PA4lu>cy z`ioU_{w_>^{!i)84W_l*k3B1%vPg2b7K50|wQalieR_DN=VQvHTkQ{Lg?{~Pc>AN= zOv4bbt=G13UGvKN-&S1{7b?fs@;FqU$5qi__F1d>*;#h`KWD_RKipnjk(z&A*!#4^ zinnv_zG5{Nzw(NY@j#kk<0>s5HDL}#2d5XU2j1`s88^=~)H;2wnM-k2_Tj~*Of$Zg zPF}>IEzdFIX4Bl=3a>riL?^8DEEV_4VQg7DZw>F}8%s(YZp?fjkaL1z^X>1aZqE7Y zmNNb5&K})s(^*!}a6h(4glX|C%`j1|Faec-X{paY&+9k1eE0B7_Po6RuYJS5e}C}c zz`C`+woeU;t3TtA^laVymsj%6#I*JpJyt*a_tn>f2Y2brJ-lLGej009L;){<*RQyx z3Nlvg8P}^i8)xnQSHzfSHFuGYcm7;X#XZiOwwyosZw}kn|Dq6%Y%Zh-iBF|1m;e$ z@ose9vMF0rt$D`(X^Srg{mtQ(DSlr1?ofE>)7bMr`R}ChZ~T6!dEsYA3qFp6-`x$` zTVB4sn((J(Gh^_ZTp_{!S9U(9m^OPoI+XFm);&;UZS#R|nyf-I&!5u_6WcSrnj`4Z z_t*u?dUn3uljks*tz=11+1|o;JJx*I_2MztMJoqB*}`udoM+v-cc-K5`P$z;U!GL{ z?ODhX_4UNhsQW>Z0g48O9Sljn*`kaA*@v&SFPykp@YVj#FZJ=eKQB0Yt!H7vvIh3{ z;2sW(^n1cA0?aBL;^yiK*S;q*-Meqga$v`vy*uT;lpa2|;!XHR5hb-rS%D@jTTfQ> zoZ7H;{=PEi|9}7g$*y?aYrW&w5!N?$a~`dHKF{ogzeVvoogI@BJPa5oa49ebPSfDJ zq%FB5d|fe1U~Qg&>6K8EN6OLxYy6qH{EYu6EdIqb|7hU-?_q^{lfIW0{agQ2mx=Rl zev!oDWv{rE&T?g_?XKUGaj*>zL1!y)*q~IHUDyB`1T4 z))ons6Mc!i>)sVtzG+O(X$pUI;>byJi%P-t`!lWHZ~k@cU;Ug`!PMk4ovkN+e7nA{ z(f(Jt{eju{_cz!7S^wW$#;)S(r|K}Cwz&A5FWUohN@wO=yTrfm@A3qO=lZ#ie_cNR z;H`Pw@t@q{jv)_jJ+I^a|GNIOeO1!SbCGNFR~x>Wx_7VJ@v_$s=lxwUEqeaX?D#|L z?aCXr->Z?=-}}dK&);LZHD84F6E+pDTlgxAug80W#_Y^M9x;}zw?8Jdy>iW(`po6* zA?3V_zeJ9fJD!dGqWI#-fsO>jUGS0qAW6)Y~N5fIa(0WdymF+&WeM_?e>J zcW(a&A75MFuv&fn=Fij96}cFfo_YUDZpOa?=09KW%s#9Y6XrSLqeEXfFS8w=Zd=lE zyJbCxm0)E}cj=Wh6Hilo8 zufKOB*#F&wgMW9swL7gytJXL!yg(yL*wO9Y0zIFC8B+wF%)h1^8xh@q?T_!mpv7DD zG&u9#&VSVR)z$XUeC+J;kIW~CKOjpvbFKdykN}7 zFys8Y$D8Ya=0CQs|8TqFqw)L$z2^50EU*7q{NtW{J^TD0XO{0VUNwnpsbP#=VczYg z+3H8)Zrs|ipzQNOy?KluCicf3{*uu4J#TOS@p(5IlUkctj#MyDDBjFzG5>`{kn+Jq zqduicEvh;5Hr-FnIr{ACgNB7H9f2(d*7f0+*LbQhBo$q(m-{{YNy+3jGdde`H_dX3 z*qfXZD9a{3G5?9etdy9ge{3_?Tv>JNGWXezMIlnEcG_lCcDQPXAGo&oil8)>{WVr@fMx;V=JSbNnysALr!%nc01u>HpxZ zdHnZMb+*Op=Gr%VX~o>Qb>Q#c!)D2=MGJKoH}1E04AAgmDrD61Uhz|rVJ2^$E`vZz zQqeL=>14a_4IB1d)9W+dcW(`wS^K4BCE;hH&TPCsRam=k)0|%c&pK?6{*ZBK2@+7~ zVl&AW;6A8l)^;>(v&GVREYmnY6gm3!IEX~ZFP2uBE;z%G_o~I22Sv;Yxqp+F&XnxQ zbMfrV+Q&Z0E8d^wpmK*NBZq@W~o*S?vZ=fXc3Cy^aLZlrQ(ZKzC> zRyn5dn0c83qv~Gg#+{aoo+iRpxAt7A%R256`ug{w`u1ttzh%j0Zs@lN)_B=GPi*Nk zUWp49M*KXB*gCpj)CAhi+UT^`^ymwjD}NHd6lonh>@ay&qtg4IOsapM2|vg(@Af@) zQ~CY3q?&7omd&xtijY2GB(YbY(RBCfyYt+{V{2F$YJQ2^cUK%6Qi1j{))3XjT0C7F)!Wxi6wpvhmsVKdcJpe-ay@0E3CZ${bms|e zO*`{Dx;d))ncHIBggsZUO~3lxb?pwlzxNk3_2zV9k5C}>!u5!k^L zB_;l>pq3tIdn`^zM$GbS8CPZktSZIXF$HB0RldzMGvR%`V$m)4wR>fz-- z@@n?A;+OI^JevH0BGTOQE((oKSI!46Fu2tG+vIC}(Z2ml^7>_K&9{pC^Y0LSyjgC? z$)CzCBGV(9XRQuiyYij8u^6LY;B*R-- zvSADXp}+5*KilZY%fR`(vc;iqYuM@p^?>&BNnNt5XD_YVH_QLY8@UM0j*myp8dqKy zX<%m$o^yXrg0;}2r;N8_R%OWCx~y;0{J#GAZ?~J_^-C|wU6)!IQ1VtysUwM5!O66G zL21h5(|udD`LxdN&t`W1JMU$3rk{hQ@9ioHjZ5NTUXs&c(bJEV+!}?pNbAG7grqc z@e*lu*t+wT-e2%o=QhI~hHKLz_%-E3|LxY14((tsnsRHKu>W`4E623V%5-*2o&5RN z&!>&oR!h!Z_pLrvBdI}xYnF!R;?Gky|Nil2`aiRMzqj5$_S5_J$%REl=FI+u6OU047qq| zP;=dGxrW)oRm|Q376sE+@-{g2EKK{Za7;Y#tm%&0&#U*hYKP06ueS3)FOrx&|Hqfj zaXl)WncvDD<`lhSFumU$(;T%>-EBwGvdRL(ZC;hflNjq;yB(G#I)7c+$-VX{+oVQ4 zzQB(yPOmx&f&*RZrOF6J7hymA)Dq$=NF-UcQfRtbe<*;vjdhLqGVeN}dy zt_D5FR%Pn|yO9kQRtqFDCX`?Ot|CSI=!Zx#iGv9tb_rczdkhi}!mU8{s|9kd-nfG`1FfW{Q z+D*0VqNK>yeVW^51uZbM%4RfV6I!=aWKr0)!@EKv7AR&lPx!fgis+IxYdClo-gNo9 z>4D>meew%yWA8t-Khyi_h!iWEQ2Zvf-)U>TmCjpSj%bWLyOr^zfq_#-;^o(i6)wK; ziDXvlyc88?d&+NXVk6HE*{Z)=!_F%lo2ESZfTu(B(UyWoai<$ECvBdY-Jo$xpL>hZ zz8p@*pAkM>3qSR2U!L_f;H~om%VgmLSLG`9wHMVK>{#;s&7b|{uWSFj%l}vWUR^II zw_@K8&#r>H;wEUG%dhckON=dJCY6eeTjwB~=T z_)Tf2L)LePg(7Jt^NLf9dG{MH*O2u zbCV_|`|jwNeuwMX7SW)DO{J2o;%$MMOZ7xmJJi}1271eE-KDkYTHm5yEp3Gdw$vqP z2CRIqXziz;HFxFQ=O(OzBI_bvMSQM&aB$_4oENr-f7#9Dx~67z=a;e$eN#H44*G09ii_2p3lSvscQdY~6p8qeY3M{Nr846%s$!358wB@vAi# z+i-FBkM+l1g$Ac&#@&7HKW$cz%abLac{#3>$~<3@7qe|+&#Lye`R;oB=R`xl))?O6 za#v5gT-viuPqFb}o3t#~#QVapZKPH73y$Afb!7+V=S2IC%m%-lgCE~X6~B`?lpLgc zLC8ntI6G`uT@8_P47Mz_I3_8!~9=& zyeq!XEoWe0<(jg%*NrCuIU*IkdUU@cjA;yHV7x68La2c8E8Vx^I24 z@VTAF=1D8cP0u&IzqY=J;lf1bKxb3)y0CjM_C~8a3hZ_4RbF?@P@UI0=5fRFw_9GZ z_IcSpiFC3(EL8eBQ1?RNgKNS&r}&sXKk8S%YJLSn-Za@qFV?urJY_tv_)E8oi=?rD z%3l4`%FdaA&C-=d>(731ZVKozQ@rv1@bdbPn=9Ub+x=tb^thfR(F+l7i;ZKY`R;r> z^I5*xsLt=?VeLcL?qo0*-IRRrLu=ykTffpzd`(`x)pAw%ThkYNmfdQ~s%uWpwy1O0 zT*_+vO!B~HzPa%YS#wq%kUn+2XY%WyYr5A1LbeqD6=w7bJsT;0~y8ouUd87Ss z&!(4=kCMB*gwEc|DB0n1NXE_dbh<3-K|4loNr6pg3%eLwxiq{pxIM481g9h)jK8vO zzN7Xz3nW>R&gsP4Jyyoa@xpy}T*q(QAHt}Ho7TU)`AfG%CAhfcfY@LsM`E}LfHq7=X&#xX1Hu!@a8gy*PIlQw8^R!&o`^z z3T~TJB5`q+-|5#8#YKv|jhk>iH&YYX1Vb6Lufs zD+~?m*|2e`O3lZn*ihM@UqVicTTKmks~6OLI&l%>=gmQ(dl>`f&YIXKYN+xq?8&le z@=hsdVzQ5ay3xL{E-lpWSZ(#9shiI<@*3{opE98@D_q|Gv3GKcNYfgwZ|AJb3!i`6 z{qukr+eEk2aMgpG90GIPVr(zBH5%k@fBn{f%HnrRP6!k|sn-0zo%KtY?Sp5lkNkR5 zk|S~_NA&KU8NZr->$)#Wo_*!AC2z={9ZD-`6vpThPS(nkiooi>iv+BDU z&is|Zku7{r%%ds#L-pK$` zY@pqgZ%PLqhtL0zK1JfllNHB>b9$S+0@t=qf0s1F@w$~j$t#^qh6TJcOx`(4tTR?R zbNT!QA{?-z401(@6Mou~%-H*r}yA z`&M^xuln{$t$3aKzRhPkmE27@S&Hv{Z(9G_>DE(*4?^dRb<``b?O&a8mFK*m$DX=% zE`8@^4;!i+@G7_Vk2&P$*4DrEW5V5Qxp(bKbh&aKFWOL-v%6pJ#%Bkuxd$(CSMVJT zP_MfXE@5TLZL{&p*U6f1x)r~4D^F1@>Uy$qYmAaNvqa;3TeCAuE}y@kWx%~b{K|Wm z$OfC_hVNc2##@!sG&TDKcR4+NthB99PeHwTt@g5OH}9?Z=-%WhmdV4?>&>0>x5F-b z-_*LZ?>{tDdhhdI_4e5V&#A6OZ+_pI{mAbz=X$T}{lyB`Gma%m_C5L@FS2z0t;LsD zgsa5;l@gFQJJET%OedcwWb^NNS=ssF%Vp~e436&jP`}#vlR}PF>BPXvvqjv_+_~nl zy(QV4WjU+s2FtrMoZ8nNsl7A(!!pynz^=~ITXqPUYDqEn7AkCgW0751q+D&@z2lfwjCwz9Q33+bvf2nKz%T=F{QZc&@~%x*pA2T?62-Uj{sZ}tE2 z^L>BHKYX~CyXXJ8yK4+j$#ovSsmNt8LwoYo=E+CdZ1!%PWopWzo3G8Zp?Gbx%-Nly z@kjn896y+Q^jRU_uWqa9p?VFawLTjcS3FpFT2gw7vV5UTLz~Jh<7;tT*H%wC)fD!Z zcj*-2g%4P|3Xjj&ZWk;YqVUJ%U{>2=*Zk0bj)pVd2Jfpo=P7Sj5vh0R>m1urvHGYi(3#EBJWo>>~C`TWRY#ZR}Cx_Vg-ODxFnW~mZy%?NUTeot%4 zqm|yr7xH+oj#qT{-fYU1lrFt7(mFWn*jwvuoWlBt6YHuEoE8!~l95^d+@@b(UyyJ!J}yNSw%ZJ&zjH=TKNRokGaJoxYe=I_%(dnca1mbUl%)l&TvC%-=5 zSHtVZwc0EEVc7Nv0UHL6Yn9I>Sv0bYxTZvZ_!HFery<=)GCc5BFv+ z!zsd)F;X->;xdz8Yw1$s&tlSviMBdo^Jh%{IdvM7d3?rtNe~VOS{0U z%lq!>B-7}}PTCE&0iSgi@veBuyr-b(&TA({nP=sXBzPa4Y4sMod+=iOtSLryDrw8x z^r}1)r@vbBbc^1K=#MWOyNcPrtI4V=m`G|i?R({X&Oh%q%bIAh@cx@Mw^+0_R<6FM zooXB^(Cb)Q>sQ?0bj)$%KGz1D4OgnoRHM|M`zOU2AK_j1=d1JCT}7`2cEp|z-G41m z=Zzxp&xMiOG3a_hAqPcGt&*t1Pf5Dj_%4~1ir7s@bRBoEzwLL&#(S+Y`oW!5T zvFc{D{^mL@Wm&4IyW8eS)9a2+(*?g~6k1O1eD>X!mF8~kn>SPORIWzl zitCaL&ouXa+$#Ur{?Ehyx7X8yZdILFmm+ZXouIMIi_#M-JRDjb&%N8_rX&z~&L%&t zOVTd6b+hHS$Qf^&7vEa>F67?n-`g@6}|;LjaDtygXO zi;jKi6NEh0s_s}Iq^HYfy>-P1n4P2DJsBd?T zZqdoemd+nmQqwmDmegp7CCKf0UaNKIY)d{(jbq~FT4%r{F67HKR_yW}P- z#2T?`*`d%U?5>7J*RDn}?o(OOxhVO+)b00N93F}sL4{8QCVRGjn`5pSERmsa+GTK= zW#5wkXSoAi#SUi=8mGnI5A!a~``IwP{$nIVkNy9Lf2X;Y)W_)_oq9d@?isfSpbpE* z9NmQ3o}Jq1+irD7Fsd@EPTjZl?83g-Ly4dHw^s=tUwWuVs5Hm;J^xa_)ytBOJN~#; zd18e#qtMr=jH9Vyjvnh?OwSS)X?pN$`N5V0+vgTsjcH_Y6iHrjE4_EhTDAB9Mn$f< z7aHc;>3Z*UmW`Vx<9_&9^3BvEe;?0TT+y-C&u!gYTjvY0DKbW}^Jm?W@XIrs?BN&? zeCT8TpIhf|zuyt@^+Um}vo~V$@9o`NYxDc!az@5Edn?#ht%zd#oH8f>OtkaaM-R+G zUO&Au{mtQ=yqCA9#kQZaSgF)_tajJdLpw_j$_k$?cI&)R*2S*RRLr|y&wl5Gp-!b<$7aRkYjPwe*lzs++DXrE!!Y?@r2cBr$Q{UlzeN9FUSO)T)~*7+Axig zJNM8u?!2?bqJip}8=pu;=X?#iW)-#NmdM(cm0$n1iE=4Few8#;+VQGXZd%^a_`UyL z%?_(Hul{0|vh{lGN}1I&8oLDx72YtFsBJK5W4bz(`L($8PT}m0x>Y@EKwXMm zwF*((H{4t=3Pzk-aOlZHj-!)X;#rr=T*#6jd$yq}7-nqi3uBY29PTC}n*br#Jv zP%Jn$?JCa_t;x$39@(U>S+eP##1zdOU8$uPQhg$JTFhGgKR9BQ!)c~deTy^#L|nPM zq~9JBc-$DX%DpQ+*O{q7`+Aw!=>^gC$E}`Qeabm#l@QT$-eF-DkG-XRh+n`I;f|ui zJL49WiJU!mcT&fvji&sYIwg`NvV$33bJ_Vu+8+OS(SOzMmOk~`+7NfQgNrsxbv~;< zYg2Y5wl}p)E%Dj%bD3eg3t#vipKz;l+SKV!x4q-DzS6VjxAx%=A+ui2R-B%-?6uQ^ zqHEmIY0LSKClv-rmmHH>%*5i+6|=3}ZpMVo)Y_k5+#wO1yhZE9Qf04Dk@uEOA#-%x^~CR+PN~y> z+U?fqbYM!fLdAq_N=37-1ZrsbL}`_^P3&b{?yz)K*i#p!h5J`e>0=CA;wZ$lU}vBH z*47$^DYMlKmvJn6ydv~So!PF(nWAE*MmIShINoO8Xu+n~wLdB&|vw@33Hz#m0OeSA4E~dZ8n)@8FrYR~~bFH=Mfge&3(GotC>#KQYc+ z`u2cdl@>#m@cwf$DY5%q>lWFF#qq??)m_9Hn<}8K|7yX>C2y==nmtyzDH5h~?WAh= zDHWF#UB(S*O*$@zr*Sw|9#R%E-}#(*y+g_O?_Et7UJAOn_Ak^tyZ({f$2&thAe=Af;ayT32jWZ#qPac?rkx0&4v3p6*icrYXI z>|eWIm(DY?GOkg`l06~dJzt`7@_fjt0ek>2HOtDjO=7g%pR}t$JI^{Nj?;tJJMZeOH3QCM@$<9dz}Ok8ZfgR>v(@ z^t+TwyL5|R6)Th}iOuOVQ!UKUIbmN zSigQ)vg&(D(mt`2-2A=&E{i3)JTy!mXU{SCtyCwOHA-gZFP<#}cMKUnO={ z|IL(ZORY|>cweBrqVwt3Nsr4^0t5=~Huc54@~QJ+er8%NTY9Zw>7UT;PbBZmO`rAp z%%X!|Op+gkUHuf4!p~y9?%Qtbn}%w-<^5KsKkl7zzxA`E?fAO*gWLbVkgj-j^ZCc; z^?%egUiN%ld*#>~&4_o~twmC^?i7An@$`W1$`8xl`0gpauK<*FN65 zPuV7(K9*6@^G%E4s$LkM!<4yo##*M=pE1vt32(TudNbQn29YPy8BTNbj-A^(JG4mW z!8GGETLy_&wD((P7Iw3X3RDDAXzNZARW&BZ0mi) zu%FpGkKTA?Rl7Cm-7)F+F6_OEv$GzZODI^%*!Q$fDVOtW-{(UQ-2Q&=ic|>cduTNtGG7;Sd*k+4 zY5r@ucdxzG4>`@W?ep{V>PAnC?B6~)wI}5nP1Knu~92!d>ZTt`;E%0hm*DYm_oexv>K0l7KS9b_FHete_ z($DiFCq+kG3tPYOv6#DYNy6P|UDc$}ZjLGLAJ$w^{#Ud(T={9iO_uEBn|s)TZn2lk zJ$!3k*LHlJUE|-q6^-TFFU3s@HHrPRcAHWPU*%l28Lu1C#Lt8{=}cWc|KO5Kb8VgW zzIt`z?UY%Pk{#?SQW2F(TtT-Kr^L24Z>!nX9x~J3+x?d4y%`e}=GM-faOJ3p+ja%L zZA&++Fy{%m-?B2Q4+#2M;>0Ah{?|^+i87A+mxT1jwa)FB{EJEaMAg9__VsTLhFuIO z>;1LwdK;q<^KZ{w7Wb-!>CMsS-n;Jk(D}Qh=--U`Gp8ZkTFNe*x**-OM&fexno4;;4{==M1Z z4}MHov!ST+z^s#9lSO{079Rf~c5VGyh6N$PE86!QI=?lqk9lsaMULy@V;O%JSlqJ7 z$PSEYWvbNcU$MQ0*9oShGHY<_uV-NNsA`{eg(Non8W z)!H@r{=Zx1A6n!8@m{W7Se&tPVO(SEbuR{PKdJO5O5G(>_xf<;G@kT|iz~XH>h-ks z81uV(7Hdx{u||jKv7P)>eVcZ>}JPoy|gFNe#;&kIVnxMPbd4o)~TPaxbWhp zkJr7|;WhjvJG*sOzi|5Et!LsGxNeHp$0v)#4>NE1;Sikm{_(SjSF22~KK=BztwA&82$1{nbtOos-Tjy5ziQdaQ!zln@oA$J>lQZQ3-U!FTTc zH(P%e9kx-~d@Abfjy(0U3zw^YuDyM6i%nJURTOUPY+vcWX2XpWfEgCvIC4>X=$kSyIf zyC=}BK5{1C>g&SFI~+9c-#F~zyVPojr(jasf{B{DJs5se6M6lFS;0o1I{S)k!ar*L z7X6|6X;O#Tha^==Erzshm5ml*-v(=o25{=^F&8es~NXW=qxBL__|c*)tlTLrA;p19Yhu{J^A*O zW3SPdVCQSUn4U8Ct8+52Hi&88ymtI=)B6>gEAF_=J7yKyncL8(E8?83QuFyN`;Tka z=4^P#6Zhav zO?=LB<$M2hd)INe%swQx{M0e_H0UZZ^}?W2ZibYPM-;G~dd;X%kL=DaU5Z{{!Ax_j1YE9K=Xd$O|yRTNI~*0Nro zy7%mks-2%Ijuvl^nj2Mh&FItv-805VHuknonG@sIo6hZe%{yY9K}xpYvu#DIt)tgv zp0D|mS@XYtf4l$RL(2<)Ka-8AOgjF%MorbNRiaH}|0ntQ!}Y)K|5g2?zW?L(-j4?q z9vw@*%PeCQ5tq9@GwEuL6+Q-wv)|)jhY!W#Yuh6uKt#j{t z_S9QD_$00;sCPN#cX1SVrxm>Y@I>G5Q0fxp{S3N19Tti0(aXQB4&r8Aw|-W|umm3rZ2hWwQZNsp4EQYN4whIUjP4kf1~#G^$*@&zklfO?`rq;2Yv6^Gv5*U{(>dU zZ}O+a-bN>vPs; zH0th*9z_|4qSL#6wmMw8$h4g8!Y?rf-GIsOS!S*ZvHZAp;)*FA-(J43t$6wN3b*ju zH*(wM-c{z8*`98lYrOiv57kiiEY;KfVOu-IEwt<%DxTS{@0i!hv%o|_Pl$CvV?$WEw_&z^LzcTKX!l9 z&o%R7QpJ7d$;v!rE%t ze)Qfb)q7lV$NJl#dDyvqVN7@5$*l{1`S9Haq4bZhs@fekGN!2Ky%BPE-Mx5T(d=l~ zDV7&%*Z2GlFkigjm(0gIsuJ@$=Nx)HcYFWpjE=`8np2sKn$hLpa9s1>sri$FC#mRv|I^~K z=0}rUJxh=JoSXEnb8WRB_vxLf+?%pG z^zJWtG|gz|RZ+z=H!mih{W59cg!ZQ=AAWRldfv$^D{d#MJ!jQ4W#7r4Eta1<-8yam zp(7o~^`Ep>Jt~^Y&VKntY?bT9`Tsts?~v83Hk}po>W$jzz3*(-Evt*#(i%DY&;w~* zDZz=Qhm#Dwj(e}Nj#zhOm0IZ3>zr+!CE<>ew(nG$>yntK2mU_EbAoxht3dL_l`#ry zEmF^Emu@hB6=kj7nwa%5z1&gP)aI(=ehe%p3PV&ndq5 zUjEC~*wEP3zv62D`L#Ng*)8ZxnK|K0^_|e*89o}#pDsn`@B8%ZSb&7Gm%%P8PvMG% zO&gc8rSb2V7CCY2(4nO@lRp;LNQQeqIQ=aprpIJT!F9#PjYnh;CS3oW_v)E@-A>`v zhf`Cx9L-#Diuqslj;~Ldgf1(`@8`-m+Qu3BbMv-g^Ovn<2^Lw$P$m^SpyO#gV@!A|)79QMOs zJj$L-Moznvw?|!+s{Z#P=jMrv(OF6fNZ{mLEmiDru!mTTx9kOcnUbOzvrkmG(_r0tYTAKfF+m5Eg z2RXi1PXGLHnVXEDZbSI@jAohB@NsX0JZ$FteG`mWCzuk>T>s{J6z} z1)KN&`?i2np1n5ooZPcnD-s;Uwr{Xk&zF0+XwJ;-2PRH*?Rws-BC8c?zB^*e&D{Tc ze-=G0eeh|I$4kKjO0x>)N|{BK_MY7lR4HHgf%U|UmV*Dz=RN*LtoNPu==W~P%wxOj zZOd5MZhv#U@L-nKw*09nH`&s%ul$wWe$Hp9WCr`;Fc&_z6|PyPoo(|*xQxXvh~-dWxVJAXfg{= zJ~elb+53CbFRq>Yyus(r&oj0j_xhWfg;(00u2-ISV8xHa(sAeJTUMy)i@!YiMQ7y| zp66A3>Ha4)Ql*1VMyKDIqi39&U!d`AqSJ&0%{~#L54H;ZQS9N~_fhts%s)TgqUvu) z?kX2Y+=~#mGT!5=GWn_L+Gly~O3kts_4m?+6{Gf7XYE_N+HPrG|BtQymu5ehTT{cX z*%NKPc&&wW@Q0L*n*;Y9v_BEB;hDwC+*<7qrtxL}Q=fP97lm*8J#R_4ZKGP+^>CAx z+qZ79tu+(By6@%{m#(9`Wp$ngn~2QY6R@25!Dqo#9jl)ShLtwEuOwe@XI{QsSR?4; zs)K8pj;6MiPbqNOxk)vwcwbt^1)+#71|Lt|W1rsVl&>o;YFGcmSK2XijX;E+rw@Pq z&Tnzfc@-ube%)0US-4%RcL#I!Tm#oDjT6t_GQKBTbZ5oR&}1$p`-i(9RfTvTQFgX} zrE^>P$%C`GY$AUSzp?Q4&sy;MKttBlbu*1a4rfG8*{2zA#=5#e;(-0GCZCnn9iL~n zJ`7ZzvBe_o;R2aELWen*CU6S@JGV<5)D=$B=_Pg}o&F`P@e%5iAHL3FO-hcH$ zn~POX&$)VoL9u?dEJydFDNZLQiq=Rxah$d=oXT;-yZoLD-TPvySsFiHn{ceD~+dUpK|2{#9Q3 zxAOS9tRp}54Zdzay5`-6w5#IprQS`Mu2+fql(j`6~7C!20PeB zujRQ^ws$#S@g-G$`}j~!@v@J>w-33qAD;KCl9fZODQJsKgSvCV*9k8^_`fWDxmG2% zWlHuOA>(Hg*0#8X9e+}OE>$GHbY9be{=;GGyC)w$;kkO|1!JZY_rg4`?CfWYj$Rj8 z_wM=wmD&~idy6@4*E-!5$&^w*d7?r{zqZl5I*fNwuIgD{N0$9RdroT2vN*Z#SbVRY z49h$duPsgXh5OIkD-PY^oMh`hE$v=_^sJ*&`VE?YotdsoYT2&;(n4y%m&k{l-fJs* zy-hNvXfmo_ovLZ*-Y7Vwy>L3?57n(3Mec=*p86`$;uR?Pb5;>g?l(u{Q?nU-^1pts z3FmNAT2#j0=RVa^{`$=1Nm6; z`3l>g8ol<5w_iuF+9(TC^EOCl44ip)Z1DT%)A-2cb0 z?QEV)>%PSBEH>}ee~$WaZr*hJ%lh8;Vfwo+1XU&eTxD>ARr-Z)*A_R^<9DkTh4n8| zwEMI1+I73?`Mi!wdFM8t-y}K5#7{a#Iy~m(Pb1q&4__o6xPIJt!c(_18ZTA}U%2^V zhsf25|86aDoG#y{c67$tL$_yqz3efe&VSd--p-^MPQl;JLs_4)@ZOu;C4RbIaEZmP zfN9Nv+bpbhI7u#Dcq(LOkzZBj?pbI1Eo;`U6+NpP7HJse;IRM9OV?8c$&Axy|7tvb zmpS3+o&lK+aBoKS(lvnv#^-prTua-!a<@V}E?B%QZu5AC)T5RL#?yjxZc=K^gV{R^fSC-S|$en#egV0vDe=1hmQuc2a- zV>1rB`ARyTsBx{@z1!|?rST4xZm$i)opOQv0)a!=g37CU>XSm_>F z>l5};;X*2z6&!a3WG*}m(Dw1|>|B=j|HxT;rGy8EX4I}U`V#o^RfGh8Cy#E<^>@N@ z9Mk+O!W8DGW>kA$WqWb*ir&ma+$F0+)BfyhpBxI%e(CsSOr!+x{2q zSD4JFP_Zk0bkiz_cl#;IUF*H*H<+_G|;U@w#E8cX>3`#nZMTA z7493Ry?iBj?a>_Ts!*NHI~TZ~dCQ%VJJn;IhUC-Wi)|%*1`>BQ)-Igt^!o7{TYLM} zg6ZLs?Na@D z2So0FeIzK*yd}JFRrve`8#2wRxmL}UJKpfJy)&}BY0fm$?&Q2Z+iM^6KeZ9CU^*7e zuj<_XV*fn;e+RBE3~sA@D53E?+%5cQ^TZ9?^h_?ElvmnW6wSd@;au+Ac{PcZSF>l% zw{K>rAABu%-@Hlp!|!{RhkOnzvh7+IzwBJV|A}!oh2)RREH{~bK0m!^Pw3KLZ|rBT zKCy7dpZf|C>lU9@4>@F_`;tvAH{j_{3A<4L(;o#@WIleFZ>qRCvv>EOwnjzP-}{zb z6~8p>amXf1g4p|J27x6JH=v20p;o-VAL4D#X)&KHDFaTk}( zeaO&wDqrbR)ANirw<_;ZfuqMR`71gmsxildldfc^RKYp_rEjz1z$+U z_j`JCzP0Jxuv4s^3kJh=TyPUN%ueQ7v zy!`x8sP?BXypAPOB0Z)n7c68sZ1gPEZGvGpKjU$JzSb4~Rwv5NNbF0VEuG30WM|ZU zPQGcwl!ijCSeACSu5Id4d8v>eh`u*N{%-^YM&lJ@2lQ&}8>A1u#xmg`)FXoi7AHMv;B&?0&s{Q|Z)wLJq zY+GqqEak*}^YzK1-P!2@vof~VJ1)C&XIY+5;l8&a*(vReeU4^{|Qfj|5THRf^q@&4Qx86Zfl&9%Fz0*73mwC71l%P0Fj5w`qj!%$q4+KaKrM z;@ac?j(HqVUjHI~fyvvN=-1u){KrmK?#w(nPcWu=?Od6q;Yt>DeGea(-S)3ZTmH#k zTrO*xs`kN+i3-@ecw1!JFAuL@5H>HK1b%! zqL-~vTLqpNF)UDcqC9EM#P6z8Iy^gP*<|0XY&X~4{pFu#xcRETl{11&KRnFcFguT@ zfInlx|E8`JF^xOthzQ&ePOxfwsm5iKa*+G^+|w)!+f+3ImfX2$;l$%*D|StgMP$2B zw(&(F{?#su9ZMPRe_Xhk{dwTETmKGzboKdv!1~GS2sz$ae=4J{Go(cKPd{0Za%RGo ziA6Gh4=!FC_>W60n{Vrk18j5uS8x5bnaef#!EgPJ9|ezAo@%h07o#SbB39D0ZmNme z^povpel$gSMy*@*Z9<1*kaqu`;JUaTy|tPr`R@MzvZOIIY4;}nTDxt|i3ZopX1tuk zvr#*00pm%VbxgH3dlKEhGoO>Vdii6MKb;ds5PC$>%KI`aHanxUfE5uZ!{TvywC zkm!qwir~Gz`LV<<`)`kaPM`Pws|$DPGW~iskD40Q>SdEz&WkKN_Qf>zPhQ$dwr{Fs zJEjGxy2f$67oNtxLtJXolA_JuvvNEa_7~du9`ZZ3U`z9l`-{%i%sYEO3$lh0SS6I)U_N5!@+iP9@*@aHrSnj;7A)m1`Z%T&xce{r-uegf1B?Z6IPY$%rD#Mfh`Di6Ec@k=9e;VYGJ0K- ze&rO9@i(Vzs?_B-Q=U%yzjJm;^xxAfKibGk9Sm%Wyd`gH=+k3(hI!MOP9^2#tCv1< zSJGN}_PfUUOCgzhE5hcedAeU??E5q~{PH(xs|AZ}7T!D(a6qmo(qZ<|zLkMbQZ(~s zYIr6s6F;``76%*SuL+*P>s!lJB%PM$Fzw!5ZgfX{@4Lm^)G0F*Rc6t;_SJX&v{<)UvvUd68r&D(9oME#5kVuC5;R6a! zKK1i-hxGJmf46)2lJ&tmE|;BqP8Z~`bvYEtS0#pVR&PXWTx&ghr20?i(K-u28w;vVUFMe1V1A z8rJ1h2=#wbIJ4SEyIR{c@#nV3^;3U8)4H1a?nJ&t)P&Gd%{70N`>!W0@LpN>eQK~i zLtg6Yz!U3K!}xfgw4IhOXurGqk6g7~@XCx0nf{tR57N`k4N8A5%3Av2O=9eUEe6vz zy|$?P8!Ms^BY*k(?u}DkMj!mj@FVj_sKeIFYj@py%CXn^Dp#$50B4zV_SwIxA-XL> zi$Cr0x$r6e&f<`zb}_!82a@et6Rfs8;t8t}%)Zf+@j*9%OTT@4*Vn9F849ka+0*7< z{N$qjc+Hh1OD8TkrTJfK%8`&7`>PACbw7KuT7;FM{wS;L?4swo1*fNE%e>w5b9ol8 z%wNW*3+JzU`{BeN(VRcuU(LBYZDRMU5Bp|he{#+KQlG9lcNwEu*&b)>a}g8r{g!Xe zO>*7yB=L%8)2FSUo_4#N2`kF-F16L(wp@U7O{ws_m>Cy!?kreSdUyNr zoiFS$eo|n$@wkTXvC^GgPal7|leTKZwI;LPxnYgQtArjMy=+n+?RC)2bFODX?t#^_ z&(2iPV)VFRAsD*;^_2~)mu9UE+t;%~aC4);lH};gd}~cDn;*C-y>xjRuMy=m>1d$f zSME=&>*_arV7$aw(6;4tX+g9yV}s_->Pga1C$WbZNJr07?VmnDikEo>$C0Xg2}!vk zW(BU7)6Y0AJFout;(xzMKVoYayxn!*IsRO8Q%02g$;1=4wpM+s%qh6kxx4j{rGVU% zK82>a^(SRMeUb0lwfc!sOO@2lv|LTiBrTh$lXvCC-ak6}Wr4ZEHT5q_cS;w?_fOEw z4{eOrPf3zy2>kH(hSqlT1htrdaAW_d=KhsNn|wFPpE~Vw^Q~FnAwHM3 zvSXkAz4Ft_*pbL|@PYWZ1qY@SE7dWo+-%CY!ePCkN9Bn9>!3Qp4;TDuQ&o94OtF)5 zj%su{=zH0{=j*nO$JCNJ9!RfYyWP_4d}YQXrTflL(v5v4^eKF@+NV9^+r&!aZ-4nS z92Ld?Em?AP*&UxNrPCgZo-+x)BUiL}UHu7rhuYV3{$J;2{>txrHlw>(QT?q@$coL* zSC&uC{N}ys$Pcf_`i|i{Sb1#!{|_m1wOhONnAXG4rx$mhV0&|XrBwT)zxx%r#TTqB z_%cIF4{HX9$n90erso?pZJW;jGw10R{A=0pPhNFigBy<^?3(=+-R89cY9NgthVXZ zEn(r>x;aG`j@{N;B6O|TUEY0(;hiTraVtLPp6xD4Ie24%L(aRMj`=s!?qyD4+86&M zwa(`Cv!{-F=ht!WNi00xG|{kSyKU;uxCP7ej7>eVwf{eUEih$G)Y#fXm^-sqWpAlCnA*ofR|R9k1M}B(!PHb%$n!DeAeo(aYFA zvQJ;9V!c{o*{!IH&8tsLOLcb7yegTrDoxCcSHkAkD~UL>U!K?E_Yfb@B4=I36`eo*skSY-+O%f-IKzzU)MH&m!Dg{y-3qUWYXcV zxL-^Y(sj<3zySZeRPqCEFY zi{LSjOKRrfzh6r4IPjVM_Rb{32Ht`!<`2{Vub8&4WaZmL!L66l-V4YiF&cC{iDZ6lhuXBB`&{$w#Xt3r$~z`ps5x^@>hVY0XXbV#K}{wH{x4Fu{j0KPR$_eN^5+f` z{nure^s)=@jJ)MBU;g)%JC{!;U*FPF@AP(3h_J8LA;n$tO&Ut9JiBc4Obcj}M=g&HFM%=}YF#A3L3$c4Y4QS7n>Ltmo1HhO;kD zop_km{-CMwSK4W|rrq!6v7T7-be`g-bm#X)@)l>-MJp{lVpN^{yinCnE|N#rBI*1C zrTRmLdM;;cM1FJK$`moZ{Gk2o`ntM=D-S(SEex0DzWM903ZL6q2S$r1vpusjKCSOr zYrFDBc3^?=lvyS}nk!yCt}Av>yq>$u;?4j2>yB4M#B1j*n4Eai_&}Sjvxr@4ie2eO zt7pvFMM7-nhanXkJ?4_e%@Ud3;Ow!V$VjW&0eio!tUrf4!_aP1#K% z8p^)q?&RpQH~;s(XavYJ?+8Q=jYT3h{?= z?^@r(eQ4#=!o2O*8Gn7aC$YS47XKOkpHH9uTQ_~jN|ivK*}v~GT$|_jNA}YH^EQ${ zE;UK5whG>;-X1!^!EeW;l5eLj?)UN$?lknSV)$CrT+@2_)Y3yspRO!jyx2?Fx6W*? z#QE6Te-k-QzHC?f^1-t7ll;7U%=Zo|bscc(a#8)U(~sqZe!W*=uJ1*sty;VIHmA9L zS}Llok~rINNx|WhwedDJA14?uvok*+Y$aU5_=zrg=w!TWwdugof+APOieA}lbZPwkl zO;X}j@WZW@KDxew8J@e!4t>79Z>fOCZa=N%AM788&J!@LcVJzz>Z1In_2EWaRHFiq zI=%jrZ8Y2I)x7cz2g9dVBrfKC!Qi7@X7e^QDf61g5=)y&Mh%R2t-7tdIJX9rEW^YOU%CQVB|k^P>ow5Tdx$p>(Wzb@0{+q!dL6_3LfWG3yvgQ z@I4pxwg(m+!jsuli+}VUMqe;0=58sS}>}9-OK^=Xh+0m$vS!U;NuRZna(JgGk%cqv10!`{7r z?N={aV9fj{YVEmqwv(NXC9(b5l^u3J{NK&5r=~9{X1<$v$D@52lMP48rc>3OQTy4# z_Eu<`_ekvjZ1GF+TgCgRne3%Axc?j1KD_HbPbWPo5hBJ$?69aNl;v>yvFK ze=B@cb~SA3rJ}-cNzqBUM&ZppkJz8Y7DyfFS4dm>gX@sgS(nPq3pwxp&@6Wj=QCQX z>A!y>2b+iZt`#W`FBAfuSTqiQ2{^kf)F)G9W2LRfMn;#ZF|$p73V!R1o#Y>(uDa!( zm5clB2wB70zH2XeS!;LPy<02nbwG|&ci|U#rXmeDkBE+hMe79a&196?;5Rv8UD|ms zQTJZ$qt|?ORpYaT1B!!fPySH|mHqU~s6gvSSjIxDbwvRjT73~;!~PeE`(Nveuidca zT<+E%Hh=DavlmLcx^`}(+{8mXnX0Sgj5HnNj~~hq>Uzw{{dVQsl(uuh--}NMpD(v= z`Exh#LLn317ToQ8fbGV>orG7-BoxHztR`C_BNr|Cv)gNd4kiH`*?_eWdcIWKZ zhsD0rL=L<>|NiR41^*A8ejZhm-1+Ccb5Tx_pH0k8v85KZ28R5GtjeyJ^%tMwH@(gJ z+;^q&(giQLny+t*6_V`qI+_tN$Fnq4?@j{mECm&Jd8LMgtxQ!D>eGCVuU?b=Dabfd~ejhyNu@q{;=Or=}kMZ^ylK<#aA}{=!@>% z)BE9&m$nFVIXBn)X^YhtIxB8{_T;9F2G`!+YjcIoF8?d$ng9Fj$_GyBe>YB^T9qnx zPb}$f_1W_ePhWjkFFLR3{oRE-SpOe8lfS*WgmLecmnp~t)c;cOQUm-{a_bST_5<8+zX|8dWI)8*~gH_hiS z%{?FX>~Zh^S5G$`Vq5y(??|wPLwE0yH8&RfK7V5J`tr%uE3-}>|2ki#YSp8!yUk^a zG+kuVFItPtnx-S2$ixpJ9%3N)rA^(4ERQ#g@TnA^pw_JZs zccJd&9hDt&2h&d8<}1uRnScJ=-?Lv+x5b&9ww9Q*QH3E<+J^P<*0pZ44m^4E(*LW; zO~;kn<6o}sW1qbtpu*#H*{UUr*Kx`*nPvF2znY+W{nINibFFU;_A3@>25IGQ6?=Yi zy(P1%$n4tMALXCF&wDMTYjSnLeqnFPMNdV}=A60F=XhSjY{D^-g;{%#N966ZOR;S# zEm~K3f70Iax4~{(#UsuytyPjStNU|u=CQp!pLT`)%J-B!BJ;>;cciPNzs<^E_1*Wb z%0FZj)3Td=Bvr&-Q#vO`%TZ#&l8(FiIVLAp$==Yq>-KzWjg{kj6+huy|7{<**h{q6 znRqYIo7a5NPRC1i)uhE0Iu)lLN$vN9a7KK$fX|Tx@~yt&1>bJIklExeb4@Qz+vJ|b zN-b9YnOCPT4!Y;DFrzr+uNmS5r%w(`E1ux#gxx&ObO z3OIk}(!8p-TQffLbQZI_DrP@y`@yodOV}~x#k_Sj6Sw(n|MTOb*s3q8c6uUu8P+v* zGN(k($l6|<$aeksL4RwdxXqhQ0$%USoH5-qAjD#;^6c4LUdHcHtXEro_1*8*U$ImE zl|R1jUAOthgo=QR{hXggxR)Ls2;-oe2lM^l^Hm}YQfhZ@gT-=a80^>yTO6{Lab_0lkyalwPb&*;Ij}AD@QaKEv|gGq-ZjjDL@XKiPf#`Ck3ZtMljI*O@H4bbVr* zTD^Vv^$qenA_H}N|Lg7X@?W&RTPRm4&AK&!d2Uns4wj6kvNp+y7OsMciNDQGMqik| zLn1(QL+! za`?De?kvn_PB?w8@xhfj3)-qI-2^9zH8Cw{Oz=rs-4}k+{ehpK1&>A7Z-1_9jS~$G zq*ezRXf2o2b3fI5udzDIGdJb`x=pXW^zJxM-d^q3 z+WN`A_MNM#_^|EU-n~EgSVFDmT5Z0z$M%|$>c&MEPb}Uc?d5X#{T6>`&rg1rPdxkJ ze@;%-!9+Y`!4Hl8KZ~AdJ3Q&$#r{D~VA-WV(P}A=MA*dixdN`1$ge!MGU}eh```s1 zM4uLW+nwFMU}Im2A@}O9rg08ovJ-_r$JpMQRsE+ZsV8QI^m(tH>gLM1f0C;|wHGgo zf0QSA?S0YK^MBIY>n?r!|98J|SkW81vzI57ry5p$dC@#k3AN4HNBO`R#A zU72;Kh3)-S$6a}cS`PRW_^gYtVAnU}Udb0cBl58Fr+>dS&a9RD{bP-^tgXH@XTUsP zwY^fBCudKH_Iegz6M2-$NwHmG$;_*VoPsXtJTs^>%XP%w=`T34<#^&sWzW`v<7R)p@;uC# zFTgziyrWffiQ-%Sl9{;|_udeBG2@!P$HanZOZ^Wov+TV5x?cAm!{)6)7e#-cI4OOb z&Ce(x@TIy>LiuyiZOf({SKZQ@VYb-JPDP@K@AZc6v@MIWggxK9DiwasKeMQIg3js_ zg)(Ik@6&Z}ak2OBO)fn;O{Z2vcabg&+i#w2cdO*~*-hOpR5E47t}o3eg+nI^T79`YU-9}5BlGS6>nUgcWvrnS2LCFpOKmKc`v@uExr8c3msmj{kEx)aMqqA~aZa9Vcz5TP5Atoy5q`X+<@$%U(PS2QH@BQt5ie_4R z@8ZyN_9~q>J|;gEyt;GWo5US|KHYm==O=V)X5rT-i5s9rF!D#HSaXiQ`c*rx%hd)Q^vDlF}_7N_2Pfzdaph+ zS1eenbGL=ak?u&P2}-VuLIPMXu>>jqnPD-%W<#j~bKRX)clL`)U-+>kJjf&M?vj6x zb{ZYiXIi>DXqQ$$$L>ETe#FnPpT%{bp(TInM|)q3eT|t@V!SW-{*-ah?N$Gi)Elo8 z-52_4S5bB9h98~pxRlraJhY+f?FW_;3AJOY8T{hrZKcnxOT8bxS*UVL(cnhi+e=$B zg3}HepK^%4^8Nb8@2q!UT}yRYB)$H0qojqFr>>GZs41|kO29KKZKYHy z+m4OJQ`cn7eHzax*%z~ok*Q_6c;XZ1`eorN-WRk3f9Vu|n3?ou)$FauHeCCDmF2Ts z`t0X#!VcWfUvZi5LPw0_36I>o`~>7wEq}YU_3FqvPuNzjsf(x_~$4&w4p`i3L7iEw;Pa&X~5R z%U<_~NxF<*=!e&}b>gfup7*uQJTpJ{xW@O;OHp@||2SEkJh0^$SCZST1qa!#9?E=v zR<$(f*`nRqau;Rh2mP0n`e^A~_vhaKM>_n4QrkKgyw^0G8gJa4 zz;|nd(5E|ldb)zXMjhEIdgIc*nCP8#De9M<vsuDug*A`erVB#i8fIOj;>ennYti2tgq|f0miq%8xH;sIK%fa!tJYsl1ywTF$=i*9iXm>>hKFb8<^y<81b))+^Y;Ha!*jwAyJa-;1{MbtWqVO1J)! zEwFnKqF?{$IuJ2yW(J$Pzg@rtF|MoxBqaU#6BtM@eIuHAH*eZq0Q z){7ZaD%3g_>ls7_l`+1^H}E;;di>_5zHY9jF2(o1b{p-!VEyOq6vkT{XYQ?uDG@NK zYxJ~Eahf}6vhNWFkF#o9)-*4FRlFl*u1l-DmFKIQ6IkV&TKnA``I4@k52)%`z381` zj^PYJ_btygU7vo_dA=+_=g^0igq@e(zTCZY!ZyR%o9-WIoG#4LvSfjSlM$;&w%z<1 z>%-wMB|;K!g>CsYqiOxKpGh~r?oB=RBy7sQkjHBnMdv%dvU#5!<06ooY{;$})3N>1 z#g}u0SnBi}3tz;Gawp5PKKl5aQO}I!#fh(r!&p_AKdPt}Oy{dQBNtonLNBs;NoMG* ze=BTCE-dg-R#VhJD0l5Z*t*ksQp*<4U8L~tU#V%(TzlVwGjA76*WY_9e2SLKR$o2; z!~H({=RJD=@SH}tG^fJc>yH9s`;Kc>W(Gc5F?|)gpW0;Nn z8O>#_jeh^vaeYZupWDn`Ju0uK`|j#?4c{Yqk^A=ABA&MnCh6-|+mu~j$?P?YH+GQ{ z*N0s@0-vnlJ=0^lRsIg&!Elx0X$zuyrY(-EGZB+f6@1hGK2@|!@xY-P@dcczKf;!r zH`adeXR4Uoi5H6wB)PSCWq<#xdx>qq54*Vk zebUDl%)7hM(uzAe*Tcmiaizi=9O zlQoU^t8q2=#HNPe_%HJBj^{Gu=6Qrh%jdE`tk$t^P1W%coia5$_~vKP0M*`4o7aCb zuD%?0s;yOWm)Gi-Jkc&$SA1RxUrqBi<*K;7_|})dAG(($gx;*^_n*6I=`~r=l!$2a zO>t~&28RyaD6z52EU)3WOtFmK?Y$yb&b4NXih)%KpRwG=#uCSn8qNufns172b(2oT zcj>%-9$oFPeqh(3oYS9O^DDIY?_IJC%e;L-cgpeJ-eSYa9A`SRKIW-jZSVe;qdMvJ zfw*f|Ds3jbp1#q-TC3U5zIgW|rA|ZRJsQu%CfN%~yq&dY^R06eB?E;fK3dc7lKA%V zF3YUPiPOCNWB$FmnAebDzNJP-xx2W}JNKdYvFm>>Y5rLGX|ZnFx|dT=EztY^c+K6$ z$o<=IJKXt_q1S$R#?`j=tzKAVVFb;TIP`_U9p7s38zc23I{Fk9L*xK4WXXm43 z2??zclT)K}lxvwGSyTLpEsD2(&G@ zdWKmjMETU5Q=L~WzARkZA2zWlXvc4%NYlGNd9VKKU9)Gue!GqI<@gn;!NUKg{sQ+&`x5~a_we&{qfUP}ItKTYe@AX=I z`{lv*|1VE*9@+mcds)80+3yjtEC;Rn+5f&!FchA}7G}89t#W0T%e^Bv>UgB>Wd7H1 zF7Us7#@4k{|J0>K9;xKcSyo%66Q;7>Y-)YoP^l9ixoEq>GKm|ju1;*(8;~Tlq_|K@ z@6&5T>8sj{MI4!Cu}xx{UVQm-I*;k;4=ypGN3IFD{ViGOmGCBvLrUs8kLT?-88cg3 zTU`WL=Y;)vyR-Jln!5QYY36-yx0fP~*L4;jd7)iV!g)OY(|{q{@dk6to5_%U!fVx53^Q(->i|(zw^Cgz=y=liuv2Wb{uEOp5^u8XyWPD zOW*9TxLp+Y_i5Kbuen0AR_y;^6Z83$f7=mH;p+dBKJM%N&*BvHEwxbLDfhjUww0$o z+)@&*c2GHfQ^(ibcRm69hMgjcy{O|12wWPAO9_S)F*lGW1L@v#*-2V}0sJzsK8p)-5YnSk?b zI!ndaZE8-6|7|oBvtzb@yD2Gi(yIBF1up*obA|uaafv$C)iLV4^H$H;{I{;BxMIc0 zTZzAne&3vJ@BaM9zHcQ>4>KmX*1?y30R(~BF0+!nu6(L6D& zrtp{YiM`)fNgS6u^J0aQa_Jr9(NS?rGs@D=mgOA60PH}#qQ z$^Emi)SM#ih*y)NmTU=Gy~TA=1EZIINKTtJr@5XDk7G&80T?k9R8nrW;8u`L$`VB+sJcObN6#S zE>r)mc~EqPQ^rNb?iqQ4H*Pw!x=wJI;4`CYg_Y}_-OdX{``TY8B=;ImQEMtFnAF2E zWy#bEjxvSfAB_b+jvAZThp=6kJxlK5KIdCz8%*SjUOnoK`ERGh@O10zMDH{0R&TCt zJ*!rir*0#!u`+Es=PsH1lMmz-B)qP=m9g1=&N-uoOP}qg-+HzB)W*`)S9dS4N>n`4 zHRUne_fNknYInTVTc`3)q4w;?&%5%hckVd))x6DDW4l)U`YCU$ zma_47-`@BwHn%)x_Jfj%K3-XqxThax7e4&q%)^|6r&>7mYAwu`d-w4An{KV(cy(yw z#mA~b>zurr)=xe(VT$nLX(`W=j%+=*&H5ipK;#q8T1M}%=7uY?+Ip66e7pN1rwZeB z-{pU=USof9c;+gd4ZEZkJ=e)%+}xr$`}y03Z567&=Dl-G;YvCBWX-JG(~bv8uK8rO zNWceZd{&rF4zg`~FUiTRHx)$nIzY1(Ch-*IoOXKnP z$!xc3A8)SD-&$2~-249D&c>3<*V)hKJo)j)(DjRIcYl!GXP>zt)5;S(g_Ty-#B2VG zwGujif3HVAkC>$M55?{W{emJzI}8+N+I&(7itSwT^<3`FQU+h4gxvG$0-JxjvZjT+ zaWZC{EE4&k&&27%jGxu3en!k>o0k01D_G0q=E^_33nz)Ege@v&Gip<5IudeIUhIZU zVPb!c&83Ckayar=YV0nvwtunZ$>}p&-`~#``aj{%N3o#kdv8x|y{T&Y=9cT6wF1w7 ztUvK^ono>3tF5yfee1%a?4}o9J5r(~;SkLK%&R6=V)u=|*HYTq4PKS)7Ww19;)22E zuXaUW+aBNkI1heck=?Cg%;sTSC9*X4j=% zt$nsP?Q5;FF?ZdE*~eZsy6djdy5hfeuhI?X`FeY$6nEZP$innh z?s(^W&fO}`Cx3_*U)l2dvOz|q*y8>AK6?F@`iE92cE&P>&3b;)_1C&jR@%l)hu<(7ns-TH!YPo{9@;hI2&1J+v4|s zzvjw6I}vx{2zy@L^y%;NKWt{)@ou{|(|q^E+u0QhnYQ}pSKgZvYor*uuGgEVyxMl% zeU5$nN8(>t$+537-z`^tyZUxy%(eruFo$3eIDxJ+TKf#@{WYf~+a=LfUC;Tt7oAGz6-EQ3%&g&akO5{JedQXXt z<#+K|ccg9JHwBHawQ>I(Z$F5(jaP`1Pt4wZJMqATd2j9K=jZ)8d_8xYTV39b3$bxK z*W8r6u$6J|u?MWKZTp2ZqNhz%i>~kM+%Nh>UQu>`_+EQ?fU!DU`vR>ljF8AXMPLreX{HBSB`Tl zy>_4eWqJDc?TX~rS7*y5eEW0!e^pKH<=FQuT2&%Wf8(dW`a5m%x7F9rOJA<*-gS4L z)BE4ugCje76rXn)?oz3Q)T^Hs0)>Xo;hGoAD;dbZy@94{1iWy!a9XS%n^ za(rXfP%$m|z0@H1(lOSXg>pd~pB>6q{q@epe*KZUmOB4Tmm~k9dRFkgJhNf*B?q^ZVWg z1b^#LlWEPHcXr-^wNGPR$`!*8)_O=sDL3{$KG-AnEj`)GzQ$kDKBzsuveCepEjiLb zY(rGUJP8;7i4oe3&n|D-@hM))%;3xdC*74oPZr2aWGVLT;}tLXzBl0Fos%N0Q}yp2 z-ENbjnkZTN^=E6g^8P!2(^tCvdHUk4Z1uY9^Cgx3uG{C=Xv!!1Ep7U`W#RHA*NvAw z__U|#b2sO+>b0#e_A~w5Ykx^$k>%0be`mCtdcJ;bV4?pbHs$W#y$7QI+5E{Ya%DOx z+4Azc!q#+_m1_cfeysoc?JUFZZ(SD>CMrbL95t6rRPd2gtqiPwcoSRwC+TwXzShii*McDzMyg9 zlS}W8{#~SfRP){GTGNEu!vW#N_Rnr>sT9w879qaX_O!_Us|h@JuCIHw+~Q^2 zomZZri$3vAK9`YZGv~yF+fngpDPitD{4+~8mNGHlWxg%ob7kJkIIh({Zg?i1?z(k6 zsO0ct{XfrkzSwZLoaPgO%*l&v`?aPt~-m?s&szuYyJ1uZH#U^>$hg^ zA#wH;j_uVZGgMqdvinYSADW&0$dondkYshZ@`G3XnMd8*18zyxikRkaxO(N=FHLdL zQjfnn_XFDnw|u*5W%e*7|AKSz*TdJ=e*TtOsMq}U*}}DaZ>-|Vwx4)>gfB3@{QOou zuH^T}-6I6%cP|XRIQf8ge)A2j@an^BjSu_1e>XGCzQ2I&kN&)yZa4QB2%Ps0Ja=x| z=REg0RR`IZTt4<$hjE42vb{-DzEw?YEUIGfH4jpMtF(#l)S=TSE?CUIdp>K6>eA`4 zo}MjytG8(>-t`tTZqn6Wdi8SpEb)Fv?Zy1|uD?2F>FiNsd3&^5dzF55TKLA+sPjsX zo`|kv*x$mtVB!fWzO+NCc`=%^CVbcKX^QzA(a!Mh;eYWp48i{|=CrAw%WG69sLQ@n zeQ6iBFhgBQK|_CjUfSBdBGS?8oA*m3KU^m*ekJKZ8f(*(bGdh~`M!2LP`_z^xkL09 zi}xQF-%@>ZnS0IbD19b|{hy8`f3e=4_qpot+S>=VK3?rHzlZ(9XV49=n+=yK8OZ}U-N=koXa_iXH5u6Ln|cU^Q|c4p!K|BMX#?!?@Gf8}r# z0|Nttr>mdKI;VuD0B>d%E(Qh;4u-&a6WxUcDz$?A85j=xGcf2eFfb$*7pImK>nG-w zWEPgDCYIzR7VDR!R+N+$r55X_muBiG<>zO+6(#1T>J{Xr%S0weq~?m>ySXiQx7u}+ zo7)T{C+C{qoRxEX+uM_um#nmZ)ay<@Qix3 znv0>Ku(|GjLo3@}qf@u#o9;c3S-K^bfnn$W&+Kb-4?bgJcp$pJyIWf`(sGB@jtEPO zNnYLEldY_*BWkvrvNQDe%7l8GUJcGU*gp6FkrZX7NvX0S8xlN2k1S#~5MJ@}xF+0ZB|f8Gri48yn^v=gBv4FrUf8tz*xqaMYmx2Uo;L=FrVjfkF*p z+6|eC)<@Xa7&5N8G)pYVAy)f>d2`nVh674WYfcF+EMZt;o?znU_>i$Bg<;11JQLP+ zl34-%FC6Ed$`Hs{{$w)8mDbdvri)in)RxVicr5Y26EimRb0UdyAI=;|=6~Yyv(}oG z;U{Org5`T_KhJPnH*@C0zHCRclwY^G|2pO+6@92PiGHcU#8ADi?(_Q|o!%dee=abV zz2Cn=PjM}ez^8f}muQm(9VW~_mrKvu-Zy{mM2#Oe_wB1Iso0@rtg`7AUGkAOARM{aOzB^)B5{oG+=p zjruZ!{nQJ;!g%B4yqn!8m5&O&Y+EU2cjjBUWV}L4+0F*5Y(<0jEX#~)gFC7tr9yPC z@B7Qnu;XRPi}*#YtsEP;?#(X!zeA{Q@|opKOgc-1=Q134psl+3mdoM~8afO$XZe?` z7gqhTUVi#U{#hI3vsTMLyxL*y(o>Y-z!S=IPp9F+&5m_i9zA9`yZSorb)bZqv& zsikvLaAu{$Uf#$Hf=_Qsm~EfGOdIdxu8HRovYj=l z#D*z&W{g>*cbBiKS8qsc*9*4`l5_N9-bcGL{g*f(_eJWO>d9#`Hx}sZXt|+tFYZ{g z{HM3#hvv-Gj!v^FtGhO*Sa@FoPx--(3SYN(bRqpY{q0`PH&!gNhC^;!mQ`b_r)pVs%kdv3+7G4WE ziw=ROj8dPL7~NER#kXa-VvKOvwgO#Ew=U5y6+B`g?>Rmy%6Wb9C@h=xZT^ExUtP1f zUvS?SdlRbRCwY3a#nKO&la866$&pI@|Y8UF&{g>B_Ai>$-I(>#Xc@`y5vH? zXNIb+N&V0l&~MSd;Oym_u2;E@L${sgx^iL1qtYX*xT8X{rXD_RU{Rm)$(KFZ`?Og2 zf3L_R+YfE5`Vsg0_*Ujz>(&UxT)*PPPa&WDZ^?K3zau#D(1s>=Wu45An+g&ObU)rH z%)b28>x0Q^6~<_)RM-nX)Nh1`_Gx=X`Wg+I}lyHaRH)U?mjTf-x!S+3v||Czbp)hV0*6t7m7 zxaRZg(sCue6O?8=GOaS0bWypR*J z(|4Odo25s2)tR!;2J3~ozPrD6-#WNw(e35R8V{#rryF12ywbzh`|7GGt3uXIv3dP$ z-A;|OnlGolxKhXIA|^X^;l;%hxAe?!JY^Xe8-7=>RsZNT@jpf3hhq|heF850yt?=L zzAr|$+m9Z>fQjayLgGy0ABlo{`ptJtTomnoMiw_dq$CFRPdPxVjrjYFT@ z_I}~@Qjp5IY3-)@DRWcSroG)J^|IoH!L}(|x6Jf5mw7cMBYs=S z&GuIxGR3o3zJ6g|xBA@5Wl=BJywh>j&JFhnT5%?Jqs*G!d6M~>`A1hXGDlesbb zqvX*Gca2dFA)%?T6l4;TA`(c=vTGmlQu0XBHj%v*=^0da>H?pc~~s_FoAW z^IjWy<)&$0vsjPku7&R{S4DPDnr1QWt=_@yf)^)la-4i7)9Fkki}LN{yX{8n%Kd&- z$8nciM)pQBi>J=sI{nV{`}&8se_Ceo`9;xI%a~_u$B)!&%b(>|ezJ7Ktv6vmHM_Lg zqRi$8Y?!f0B_nM8wXI^i!)+wuA9CE~wtOzjAGx(M_T-uz8QT}_9>P*`t0dKAo=YrjwLd@Y*i*ax zs_!=a{`xcf)%CeL-e#`*qSPeysupskWjFH9<2%RqP%w@A?aI`(GP;@3kxq7Lzohc2 zZ_m4Xz5DiuzjJC^nDgh9Of#%?Ki@R7@xPPz#?vz|O736Mb;4ESgW7*#*QNT0`xfpx zSg^dS`MZpQ)CQIGna}6-M+jeEtRA*w;-cx_y=?qnJo>am=9xyCO&0$`&Hp!5+f}yD z{i-T%eYy8aU+f2O^XP)4L;mU;xr}};5j(wq+HtYG$)V2{^1eU(?c!RUBbo+}&3f%u zFW55kLG%%?e=A;ISa<2LNyMz|s4sr*ejk>GJu~u&_c8U$`S;}HzEi(5|pEb(@}L3lx01(_(+1DT(89!#bgi?yQB|qr=whx!|+<C*zz8QPcgJpeU z>ux69{Nws-=|{&MYMUN?G5mVeZ-3Omb1#!bk{BQHwlVEY-CCPDGs7{!SGBJ7p^s4I zlqJt?pP!n`*(vR5YgU~y|Io+P&mX>IzVnXR@X~bN?Go=-+0JkkGFS3e63gCrX7-u< z$W=4-cd6}??Fx4KtX*HeCr;JeGhmY?>lWM8ysO9d=g(o&Rg1iL=Gc=ZPk#I|w9l2y z{g?I8&T#I{t!{-oUOANBjh^tM!|vhxNAoZ4&Me-WdBf(=g`CT0*8EM%_(U+Vj6^{+fda(>_+m*hSBs=|5lJ zW4EXGtF50k>we!ZZwudTX=LDZ$N1gBgJ~b;A3N>3|MxWOi=hwox*jd$Uuplg{c7;v zxvyegdJDw`U1C>p`So_SCi`Wz6Zw;7d96Ib+#oK>z{0|O;4=G*zak8E8<-BnD>M9< z#K&O4*V%UXzQeZ%LBCe7n05SE<@R^}s{;LB{oc;=b@Cgz?>GN%i@0>qMqx>yr)|{I zDI5DYtXMT$>+4~*#s6}TtUbu_S!mv0={J?{_Vb_HEq=F|(|oBy3AbWSX8x;N({E;} zb*mhA7IkfH307?7ZkVCuxogwDi<@l@xPIbi(Wa7LvRjKEC@cQhDW&dw=ZLFZ`bLUUsoP%kT2PQ?~s+d*L*z^lXi* zD=#ljIp0=s$7sfC!%uH!{oiVyW*ZqaqlwYxoyvm-wr_49Lwz^DyFAsJ>(d^Sw*J$d z0xtXBNIq?SB%{+^y?ox1zZT26>a*QFR=6#zzkBOtqW;Oh-K!4I>UsTg>c*oojxn9` zzWr*~lKj)1qa7+L%J;5p`8F-H+qc*GP5IsvO~*txrM^!7UhsCq1*4BwMeq6u$Nh>~ zSIO!4sx!CCZTqV0UCE3MUy>L7o&Mj=^x3I49yy^L`L`+zJsZp~Zcp3O|5QBr;Wyp( z*ZiV|o0F^ArRVQ+n{Q|n>nmhC=eFcJmQ9ftA3W9w5jES!dZbNhzkOcDH$A z=Qazm8Q(a%B;oL;9Si4PS@iI!T1JAz9(L`2g6to^M}Ev(`=QP-#5r9kaiZy^quBy$ zpDw(7_)&G`tFW1#S9sgk=;Xh?&tWb8x$?Who;kmsF31)Oj;jg$s(;Yj<+4?z4b$Wf zp_4X-mx6o~zi(W$F(z!6?E5W~e>Pv7@?d?_?-QXXf20>5{QJ}I(brYe50%$+IyY?% zR}^0t>*rj4Lok`Q{Nt*MlP?pVZ289|b79-nsjG9@Utj0q=31pxGRr1HS?1KiwZWyq zJN%7hi?1boey96xI=lbuKmF0KKZWK==dKpl6fC=%^t&-z?VQ=tlAw}xH!Mo`@gLFU zKl1zc`44xxw$)}yCz^-*+Vo84RK3reuqZ=TV%=Ry%ewiJ+MV1ktFG*QeK%&K`C09= za-nnnRIaNi3(C-X_UxE?@U$Cd$Bka{XDVi`wJo!?%f78<#~uG{o8+EU>z$`7|6ST5 zbUkPJ@41REZ!Wa7+qW=_Bk8B4oKgjAUBUT}>$xwz=CtSPvipDS>c5G#t0T3(_Ai?D zjqTnNO<_?MjSmbf-fqbJw#?W1R+8Y%ZyW6Pa(QuQ@NxY-QYBF;xkkA6gXkZ|Ba@${ zz3?qF2@_{EU!LG1rq0{3=6?v^p^&gfu83);n45IWOe{R7nMuAo>|vLd|3==pIHN|& z@2lEWp2-tl<%mAL82_@%<)zMt8_`p`>Vo?8=e`Knntou9qgUvDdtN_yJ@I}-@thyeUPnx5UMwoW zxtz0q&u8}U%%Ylxe!_FkaYV1HPUA6oD|%~Yk)C(nJFX0&S3SwO|Av86X;XNsvwic~QYK&N{qb&Ljd$NO z$VNP^wyI>%TX|&Gw9pT;>MUYr|CaHQ¿T<7zbO2a5u>vMJo>5UE7n& z`n&t8oM^VA?CTkaqFFZz`23B2?0bLv-U)3hoZiO%vslg|dH9n3p~H7KxJK`uQF1?H z+T-IE?w*!E?iEk?rI5SG`q0@(!HL#~d%1p}>5*VweW2<8-Qz|NuiZQ_zc|%dv7F-) z)5H9XygZHT=gS}Y7KHKa7yrEPi21w^4VJrOvnIKKY&pYO}oOpR7Q+7RT*7blqe zc7ey)t=^}Ej$~QR30TEZy3U=!_gz5F+BsQkPrrUY`-jovrCi&REta&rHfOJ&b9ede zJ@?P^{ONE0mw%{rFTa&(`0F=2O!s}|-_3k(`sB3TE9=bjRSqVET1GK1ocyu+_%1%R zvV8*aAN@bw`x5`y{@UNGH&ZK3HdoJ-xvsYT?ROvZ@^yWl3fAvm@!GTeiCYs`kyXYX znqJE=?{4oz@rU6jdgr8W?DE}WDJfWG)tvF!>XYT#AGbJ{hH6d^vU~coUT|?*{*;L- zo>tNITHPV4`&(1Qqdq;Uf53z|2s3VU-($aPei(CeyZ6-# zFHUu{ZwW};-aIqCrEjUyMkax!`wqA&wCN=&E02cKQHLykprRVzgs-JWH^ zYi@3)eo@4P)pY;&D-Pc{UM&1oZS&)5XhFDJ(5gwFId49`eDB=Z?77SD{$!b+c;Ud`Ey1I&-eI`n{cWXaC*#}_1N%+j7HzNsM~M$ zeOk)9F?MpqhPDfx@7wn7Y@cS&F>%te$riJ|&pPyQ-;|UO+G_($*It_c`<2gy?xjEO zPWYT~cn+8J*=HM9xg@H$MmyY*7VQx*Q?4jnb!XP{hd-kui&oyqvYRWsM^``3Ve*Wn zhEda&HU(}h=-B#uzv<@-Dol}UXD<@lUu(SZ&qK~Pn#|U7pz>Tp53@xwYu= z-|u(+>q`6*(&XAJnO`~A@aLS>=iaryuR1*Ohpo8S=8MG%?$5SzZYfhyf7$2$LVBLO z*jAI>>^)4e;R5P8^%LgYiSH2oWafNE@X15%X$O}byVLx|%6y-J;LZiYQu8_7-KI!4 zMn?Q$S+4q0qF%wIF@CAi`?4ox4V(BCes5jBJ4Wu7TKJqsJLXX zI(=8-LjSsL(OLhpJN{SgT{v?}>4wCJG$UqK>GRj*CzaK6ZQQdlHz%M(?46ory_SM+ z@YBx<%B5Pz1 zbxq6vv(f((R9#lR&zShxO-IsYvGzBcAO2B`Gb$gj#jc86q9MDjPxNBjGznu_$7n%0 zkA_q697iuK-jtnr-|kN0fs=A~3J=YA$F%?*bGYM+lSKC3;`_e+0J%!69#i76^KBkrn+ENGK+R=IZ}n@^-TaQ>|3oLxVj zZ(q6DX}|2p9edJeeLwS?H`zfYFzuXW(cAT{5$WkWt~dqGG%S^0JGFRimGSbmN4Kvu z?Xcod780D(kZpTLF8jv^{kIddj-LFsb%HSOPK_D0&o6gJzhB}Qsmbu@;ywFSw^M`* zmg&nyzn0vv?e?Fvh`W0p;_;v{xxX`PYs|s8 zSD!bnHud~3d&E&B|JqT_r!3R*;(}!PL|5-L+Ictio0rA?zqPi9lP(wb{%W26!(r{! zrWHmhOHTettP5K*{l||D+h=;E8J<$U8+gQe-?Ga|8^0<0vfCQOT9gR<;nvI&L+Y9Y4t*>(`r1fHMO>FGvo>;DT2fdLHL9ZX}

2Z}ui`z5>L-}K72vY)m2FEkq%slQSFy!DCvMv0c09YO8-Rhy)h#Jv9(pLDr6 zWA{|9m52XLo?WuZbk(<6h88amZWevA;o~tQw-1skt4rMPPZ3$LRN~Ng*??P}nMEl+ zXQOnzL@r97^y>dL*Zs5jwFPmFqSX6`%Zceq9bvj{3OzLx9BM zZ|OT-Kbd!Cy?MFfc1z4fIeC|+mEZ3RJrbO|v}(qR#z~A^nrf=rojI+xVq&{*olH3C zcyG6~V)8cicWDfFf@}XW&2?bBvWw%-!%4~Q$ro;HQmxwa(ub#ap=#pxg3gnsA}uFA zx_A2g*y!`}Fn?J0tqJWlHn(j*@qC$YUdCYe{Vi*wNoGB}?!3&eO+A;duXHfpdST~& zmtC&gypL>%@tp0jiK(yKTGnsfQ=jIF)=$1|2394z1MT&nIZmJQ(9&ic6brPWl+xs89d z&hB*;&?v3a_sze^xnIxvj?3G*6@U5qKbKjUnf;nPsiRfwm;0lrB-JyAcHCI{gjeWo zsdo91FK6!D>b;R{88829WkaM`?ro#kEh>N6Q%|lJjS%d9RjF5es=rbG>EBaYM*=RT zTni4~CKxnHEURvBkJgi{9HC^1HFMpXJDFEmZ>oJR*m5*n=4Dky?f%c3T~meD)b}Xv z@2Rj$dlBQI;PuOCvFP;aA{)-WRLQtBn(_XH%Xeyos0= z{!?1>5F1;EpXO=19}`*kuxeB)COdg}OrQRH4Nu1XRTq=)ExFadC0=ks&}!u_1*a1X z#=jdMHYJ?gozVOveS)ae*OwXxCt{dUO{vnTf*^m@qlXKSfiuI?*2!&S+`Q`}|dNX_c% zy3$kAGWjmk?qg08E$%WQ7f#(s7OSei_167mqn*uEF2n+n!_jup~-zA@A8_6G^K zwfjEEsZBDulVxT3ImYC&e#*=*^ZF(U9?O()kLcHuVPv;an%?)~ZUR9ywmw46BUp|s|7X@@qyt=2YCw;||P8Z(L zKwrDx6UsuC$+s1xMqEsl;GBCfU$iwN=8#??gHYIPhE?Y{f14WI<#_$NBX?QB%R_#- zlVAVYZ?B~OE$wdYo%Dc4`$%6yrwvR294{|4J6b1;^i5jtZ17Dt@AQGkm)gD`kGJd+ ztTMSPGk1FZV$+j}Hd@MhhJ~tsUZ0Oc@a&957W?Dva8ZC;*ns$cExNVL3Y4;~V2i~e%KC;kRxBKC~56!ym7DhZw)IIiYcJ;VarD~o$aTTzH5Z!XJEvYMQt^4H-XwpnFrYBu2&N0mNrxzrii zo4hRCw6Z42QmwQ_|Hrwm^KsU5yf&TazM|sGTbgBMyX2G0zh)M{o3=4imz>$vxWxKE zpqh%L{vU0ve}c)c~8z+Y*WS&bs+B4@%M2;fk7b=ukK$yB+vI!>Cpp?MJF@fa$XcX)_W%V z4fo;gJ3NlmcBr&3dwpB{Uca*ZdyCXW(ZlU`II|VpqO6^Z)U2d$pNm+${qbvgEzj+{ zBn@nMm##SI(B8Udk(M#@Y$ucJ=T>O3`uOQ3T;vg1rC)aIFh|vGr{j-|W2d#{n0(9% zpU1}~Ice(Jh$3ely+`v5GP?}|W^Y|7@awgtR{6pMzjOT$m9AZTMf}zN7wj4jei#Hj z(VsD!_tkBwJykarTl2RG{a0PIcSZe|HkS9_`EIJYXs$o;boJDR2~(a^8I*D<}xU3%8`OHWQTF!=5k;^p7PzxZgb%0k1nJ|PE{M07W+sB1V+ z=*jzREz_aRmF2&xdqbtx$y-wIC-==b_M2IIbB1?toJ3WcoEh_Zn?|K2sVOJUO|B5! zG2_9(Ur`w)?l08(OGUKAJJUY!XcTS>eDF=vSLlMbx+lMx%;T+pJ)2Sj9xPgR`u@ME z7beMeUA*_;nBl`)OywtLbO)+QY?z?8BtG=ozZg$GpTL#YZ{GFfgbSyBu2c@cl<`l; z^2(j%*}B)xr9NMGXfa1sOf!pkY2iVe<1g!u6j{cE+8@gn+&=%r%^HOj)7M6>?wGb| zE9U~~P3-(QPVcrF$FJKgNLLC9~SPB!xzMolgCWwJ{+jzzRDP|~{kkv}qJ z!r7be`QEi`4Ci@!k8#GT)XST!@;)uLD*ZXNN73iJ)Xy7_{g+7Y_-m-^u=K=obG8fdFy*(>&D(){NJ}G`941pt(PoBhZomCA`_yMoE>=Blvu9U2 z=TV}wX~~MBS+C;0W^+2$_}zQ|M$Y^4!Q`NjZ|c)udZz}zS{1SD=AyOnVu7FETwnJ@ z`}*55Aqn|UvV~m73N#Mf=D7iz6SG z7Htm5)T)o_zWi$C>3I^?%(*+ejeX{5Md<`v+QpSH-s3d2-*++8UQ(_3lD@;S>zrX7 z{U4tGzIA=Bh+vSm z;GoW>TQlHlFycHmcR@O00qZqZzS z8<_aKvh^P?Q|@>yHK*;afU_@S*T$9p657|a_$Pkd7Se1E&WUf1VDPX036tCmZoVu9ekw-ZyJlup<;+ znG6M5JCmk7eE(rzKl|#r7Zp!D5`8*B?$6zZXAftz>|Mrsup+N}!V89vvzM#>U7u|k z{dLxvl8e{oNQhNA#wYiz?@JTQ(2f;gI>)jq=hW9#nlD)&H11z|LQ+m@YiIpZVWX30 zSI_%!WO>)89~Ty!IWjNX-}#c0?&}s$ne|-Hi@PpG*W201FlE;825oxD`r$9@rss*P z%@x-Fvd_H!Ni+3lM+BR*F7w>RIa!O={chf~SNC$oKjrR^i#Ig8#POvHNU}Y5sQ#9e@IKAbL_{6I- z-Pt*_d2bwjePM6QI(_NL_bZh@-CfCH!F^+*)%*tkGpBT-nCtpeEEB@?=6sC0&7b_b zf1#B4!@A}bOs^K?CDt3C%gh#>EZf9&*5&HSzWHjioW9jOTd2licvo5N@u!04OV2+% z>RwVMx8U>72lr+5(mL!l?$$awUvIo-=F%SL{Kk4-aoJ+gw?E!wPjJ?`ANgOpzAW_Y z#>K@tvEFkREqr`s+x3~!ldYtaBv+}beJ!{9w{&+vK&GSMqZY}GX-!>yRVVaQPnj(> z<~yGC-**|e#Kz9%6lvdB^Vk$VU4I5y`P|~k{3{b>(wozWf8u z6|*+{ukoDsYni|fBi4-g+}0KAoI+PdN%b{~U3WgRd1`uAMpMKorqbfG)57j)tZn&zX`5)F&3s1DZ!_FKJ=hp|>FgY@ zCHF3xUY@nkCH=RqkXvleVx2G%-Y@y5eNVlZeo2xq+Nz34++QR)(!lkVpk=@GjXD$k zlNTJm=X|gD)^_rtrJ2|Xmj(s@8~u?oPkA=lJ=Nc~?W}wEw!pgCU#GY|-lB88;;+$d z>1|mrythQ%?q#XCW6Rl|<+1!rm+B{{lm&^6CbRoB&u`1Rt95)=x#E$Is78rNyH1}M zZ*_gaJpX2ZLB)65d*`hUxju#ztq^ZH`q;{FSF%XK>#Uy)H?~MEJaF#XwP+EcWtE$w zzwBRTtE#l5fA+^xwUQHpO*>8JFgsdAsW5#vd~_mtPjyp5w?O<~Hr5 z#1#vUU#EZHG%1sB{QcdtUpfDwqUS^PZEL^w{wtck=8^G{h>KmR^H+3V`@a45@r(V- zZ`}`<|C_nKy>Xhe@$&2$9>Vr#0(T*`l#_zZzhW+x?&%TtKhAruOJ8NvhL8U5 zQd*}>?Ce~9P|@z!@kV8@y(}k|u8ebwsO#o`dfeXV?H9XbwmScBbs>7UZcj+Q7*_L% z*J0u0B=>hJP96Sv>y&3W+s*Bbj0_jBuGg)FQ7&TF2b=N7Q{ zq1wg6pPge`dskj8e8!(`9lr8J;VCJ_^2_e;q(c|Xj4|v@whvSJ{c2Zn|EGVy^+QC( zIo&2XKYDYq^zE4+i|>E`Gf9A9AM5@ZPO`J^G#=rdDty|^;Eq|?%dn#Q+C1Cs&m5ek zqgwu5PWSnGd)Qbr7yer_W+Jtm@f6L3w(rbSG ztJ!r#%6mKaFQ1q|iz31H*>0h?r=FB=S}Dk8V*GCQqGdvc(YBGVSxRffbtWbH+;4sw zx;&`+>?yVT@o~Goj4pYcQP`pXYE8(r4}vF((o70B|GQhbB&U*5C;iLsO)Y!1V~bsM z`!b_`^mrX#YUg_M#EwOKo|@k}U+{kA$7k{ejwN4?sVs<={2_Z_=_P^uTqovjzvkZJ zvAo}>yh!hqC~H!*tPEFKq?`4t)RYB1*Sj744MO*dZG7%l-jjd7etXp78{B7R&rG?R z^I+G0vyhtY8@e8-yk55EuStxFYg)$19tPvp%aqt-?)~2R^z6Dtiu%{zvdK^W`blK^ z?aBGKIGiGH)#WVO(t0G_Vddh?X0C0UJn}@!@4xzSeZhn+pOO4XsSifVPHKI6T=)@sGtofi{>#0`BW6p1utNa*T| zc+L;I_~G@AlbND^_qc2IR&-4K`(9wuSzqO;Yr~J-+j}u{?yq+f`?%F+2A@wdpNztD+ID}zO*Ve>!`5ft z|L)$l$a%vRx81$leBTudI;`%~iaz1Jsgn0^x(buC-PAluttm$CUr+KJVwHA(BqzMZ zYI|Ue`{T<2y|1?!M#pzVsmK4x=ee&K=gAYFVR1`a$Y)RdyN&5Qx0yM@$}{}kH!c6_ zvUHk}<5S63mS06&bLU5geJ=a+o#S!J%i3@1U70GZt_)HaF5UNCKCkCN^9Ak`mL^Ak z>_5hw_F8PQvP;2!&bOhJF~(~;75}mRn{jG^7th~2qKZYyb)wo=YTi#4O?kAoH2!*G z-i*aK4Ab5~Awxc=mGZG3mBf>B7hE~Z7`YZKGnH7gRM5aEZN)r#WE^p#)XFlD_iVlK3uS2 zT1}*!g!C_#o~B-=SAjx4>sFQ4x_2Kt@?-CmUYBe3Oq**j{9!d(`*WxM)L(Kl|Ld;* z8*+E$%WoE4Q`RLonKrjoGbolxy=4>PJadt!r&{45!;^W-uI1mJ*T*?ePvY^{Df=sb z9aww*yTP>PL)}X@@s>F_Kb+kmx^VKEmjd%vpVf*D7XElJNnA-KH*Ef5{vF#E*(x}# z?|;g4N^p+Wwme^(S2M2ioypWv{nRcxFJg8}v3*RYYKk1n2zvh^L z@Z@Cir2^Smw|)t9Y-ha~KK*SaqwicJ!CXp?`(ZBgw~oC!@pu=Ew9*V!pA-_f(U|`doWQ zEpe%MHTQDwiRued?`Ew%@q4kmcir_}qCDc}+zI;JYTiF@ixr0-zi+?!Bq>&F`PKHl-2Wf0S@e&usKD{Dt5kpYoWjx>deT)&K3`w8 z&f7kh^8srBm++Z6`^Dy!@EAG#maH>SSYx!Vy#7#96^q)86=yoQb_Ykc+;v-h;%7)( zuMIwVqS$z54M>mNv2a>W;iqr=Bae zI9zZ2dgh5b2 zaaoWQ^{`xtmDTt3(!e82Cr(?FwzT(bqxar`h&57Gcb+jiui{wA?p4_xR-O>>HYKLy zcG2hJHF3hVlbG)Ak(_QRwc)GP84-aE6^Dv#KRysQ-1YeVN~3+Dti0bPpWg0WxxPH3 z>i^+aRd+729A$VVyT1EXYtZ{Vg9)Dxx=ddD#_rj~1BdtSo|5mu8QaG4{(V={B;k(T zVR1LlR@fz}3VKeOz1%(GptM9@te1J;r^-Z^|NnPhcKLX1m7CtIUxF9oR_+rl-MiqV z+)|Dc$NhMP9?1XgDPJy@d33qN1?k?<%{^Sq(w_KJf3Hz|eQsf- zL**GIw`A2#jrvb6nT0YQQ7i~8Gd-g6HKxbPUf5{Y3kTtrLjQh&KIyPEtab7?ALs0Q zQC?YkNN$Pw%!xNGRorKK+Wn0(>gxJ+Cj9c{t|Z4NOP6KKHBI7{PR;-A`|MT0fo%I@ zf&bLrzxY$|^yHt`*_RJ)SvYY$_q|{F8;(@JXtS-}Z?UM`FK9~qj3pt#^PlVd&DimW z=VSV^W1iXz`kTey{C_y%%cfnS>Q}^YrhL%(HLpeIpSq56 zq58iyq4DTbpx?7u2Xp?F zuiWG>T{%ZTtkz|P(WyXBhsl5Wj zU%40C`!q#vH2CTkQxuf+F8kD*I^%?yNBntCc~Eo8B2_QyD$o%^meTrf{+YSI08YQfyn6d$K&<%L$S*>&HP*B)t$ zS(x>_X(9JUCBrKpPcm%HjgMMkbgOywjJ^72g_2Xdc56Obdg`oFrFk^_8;5eIIQ_ua z4d;Ie?l!pfBA1hIPR5d>hQWE<5l=s7&k0MYSDLgqV)38%apV3KjgzrF|Jj9gy+h{Qt+SPi@pGTMZNhqoT9*F}_bN^=HxGLI_;lAs?V!xq zx9mG7zVxtufA6f7OV1BWkKob?Lem%c`sg})cdgoCz3JrXEpt*$=EdBP4|#vTJtEM- zY5C`WSuTIJaz^cRN@dQx#GQEjq<&ZBpO4d&ujr^4pHBTDjuTeexbY8Q5JL|$cvwaPX2V*}Ump%PRNsFd&ey!>KXOUH3`y*HKZ<=xM z%GXXa4Vn`FRx<5AmvVc3WWS@){YjUX9E?oQVPETi=jQAwCZ@Bhd6cGH&&o)a|7arX z@vLhmhxv?y*?+nIYG_+S5cdpU- zDQx??d+PT+{l&9Olop?!8Fv2pvkSXAPQ*JIw!QhhGyHJg^UpID-`%9ym{zQ?ajx02 zh_cPTraL^=Bxp;#oPBcR<TpdT9Ns1he@bLdyV~)#_V?`PVJr>7vGtMrZdA?*RFdvG{6k)#;cumy?~VQa35Py>Ec$$? zC_(d0vfsJOe_lOXJwsc<(|ESEi_(J@_AP6lABs5l?)(~s@Tt>l6XzYf%2aZP#qmeE z=NXZ;;i2k$PP;#uOj2i@c>4YSTlw9Q0Z)28^*i6M{~uQ|d-bkG`_I4GH=|MS_3z)) zDBsjEn%4SGR_FIjRwt>H898;0p911nt&c4`_4{?nTK@0t`jJ1seS2@Z$o%n? zc@zE@FFD?L?LmNA@P*Ek9{a?EYpV7uG-NQ_PP}Q%`}W+`<;6cT&#@b-hE*x%%)4!^ z{=aznKGPPCw6h&yOEp%xbukv`3g2{Hbw%LLtWQk)uQg8lzh&JA-DvBdmjci2T5Q;? z+OhQ3?Uc@&W|QmZ1qJGw9b79gY0IwJ4vuC^*g_t+G}{V?J$7_DG4uI~MO|!5^iHSD zalKN#{{L#v{aT9-TvO|omE9w{I%Rb?x6DuVe}>n(FL7(kI9Y#etw_P0-S!sMSN(0* zNmWV9FF&$dhPBRe8>8+k=91?I%wBUW9sMm+i!{IF`M>$1%Pa9=uTg}luSUVY?Jq7{ zD>=o!*!eqsQ}Wq8zKfap6hB&8G+h_X6x8RR)+fR}U7z_tPI0U9PxI*C%YIZiHRhVu zRkipYQZ2b#Xd>g*EEWAQarxza#ix>-3#P3-uU{guga3*6msvlX_-bD1{@a=Px%$x8 zl_fw7zwZ^4e9W|6!F44vIwBxk zVM_Zvktyfw*BN{)trQo(DOt+j?{C;?{C`$jpnoeX_fmEZ`OMtWDf;XIG)8kKgGYHIC1* z3gO@XxGkG-SbXl&1^V}Hu9!OW*w-)Oi_G(kKC92s;&8n-Vb`K-+W8`DL}Ww`v0VGB z{UrVN;>kL(id}Zs`D;6TcX8yFYe!}??Yod~-PaQ%C$b>r+i~91><5Xn7Js?5QT%yR z;v;@TMS~Jgq033L*VVpnV&kq1Pr534WqaYeljlCl*nO3f`qyNnz2)hAy$xHfPi=V3 zZ~D6Y?}Li6_Rr@xn%k@lQ55Kz@FC{o3Z(^qUVip|qW)w0oUHd|*OjXtd|&pz_T}T% zho#G15l&ad+FBmAc?L_hB578^ny%Y#s7rBdT*Mhk>Eq)r;Xn3ADKRM$1{^tJZ zN6U>%7&pwEv*mf$Bf;sP+?SOeeRJwM>meoocTbN;u@&YPr8*^*vwnvER&6rd7IiCgW=if-z3Ih^73@;yvGHG6rZe|+%N5J(j`6Gu zIx8#puFTsXA==t+!*D!mv10r;F^iC^Chh_TX4R$Dw>qay?q2!&qnobIe`UFh1*)0b zcl_9#^A?%1>So^b!=;@?Z3=Q#!(_`ZAPR-31~QKpAC zro5BZEW9qg<=Cnu2YF5YD77lP8MPl|O7>6lwOVsYMlJN-L$Na%iO&vaUI|cmeNETO ztsvli4iCe-TZI}&6#abVQsO6^nQNSO;-~RFf#55b(|%Piy|aB`b71z3fLGZuKli&V z&Uhsr*|d6{z=yrF{@q!WUDSL&(0uE}geMnz|2-56w<%t;?bx1!YF*n_CQRNYx&22l zkKp6ADPpSU-tGwg5Lg^L_amotzdvij$@GKflUlzTUpm7n;P-JkN;gywezUDN*EE}*NlcSWw9 zQtBR+ZFa_L?0vWAF4^4)xb&j0R#TGBmDt3x%ZtR$6O%leKBd2M&Oi68M{}Q5 zu-;C3rpVc|lUqf1&QYNqIUm$s#7i_VxtzS(({!i*r#Te$RqW2B?jbsoiuW$Fj3A~yP0Rwo}~4fj0dzU+CBchR&D`8f}#u8LYOck{#J z*`FpJPxifX-)HHbE0>Oc{Q0LbDc>|WWUJ~sHL1fheNPmm-ms1L5DiW#POe{M@HjLj zPE!6&XH#11t3rL{zNMO7OG2mp;I34)>e12ObZ&9udm-DsQOjfk)`UJ2JO1~$q^Ep$ zm#u#Ep?$|^)fog&I6B)+*DcR!?QQ*s?rL|Oi`4X=TUfXK-l^eazo124UzBmd_q*11 zf5drL^4(b6z`?VC@sIoK_lb#z1843zbTv`IkZZlsv_tbky!y7-U-k9QTU6M$ck&WL z$IG`^BHJVqoa7etntj{xYxDbO>SD4Ln-*q2ou({O;Z!2e$m(=-hRoZ?J%0ky4j$Or z-ssXLyKYC3fqHnKN27q3*U_M;XEBwxo$4RoNlq*^z4lWkA!M21hEEmV&nMLF=oh=& zaBN)%M*`m~i*}yd>Ce7Ryl4Jz+Uz9LS$&g&g)H=yp0zC45WtXPc-*LAioGKHf9_wr zte;kvb4{FNX<7W`4rj>euvas@Q=A!mmem|z6yvnw$lts+o~6Rmw{8vjZx*%H;|W`k z%My3N#G}nqR!Oc%D)pP#r1ffAFKec$-=>E$Y5)0V#vb-#=wZlM-|;4*`f*HWM=<=19y3e@@PmD+P#G3y>{`|m=#&Xc!p=_I!mUDMDuSRE{` z_ven${h#lO<8A-0)URdDaZg~|w!T(A+I-uGSK8(uZpQyjSQ%XND}3I8;`%>P#Yu}j zxn?v5KlgjGzy8DG@PhK+cK`mw|MRZ-Xl~EXw3NY&p^C{t`am*68&AZe9*0h*gtewi z=HxeizPL0%VVBXVK$Qz?ja^TMuG{WkntgRQL*zj|oe=hQybT;(Gwz&L|F(Fm^y4YT z8auK#PA=e4dMn+e%5`Oy^vuAS9geDBmuM|6P|*L@66Wq;CG|4NHSL%cbCZ;oX0+_Y z>1+2`%}P?Ud?xkI{fJjzihL*Hn$KKAF9aNpp7p$$%v%7+9VzKGL7&8xJ@_o;Qox=D{4-HJcf?O3#6#k0A=t!EBpE%b~&_0uLn(t$HH#^g-sjdFH4 z?cXKSSY1T9FpSbtq3jqBx#8EaJy4)^@ZIe3bX_x3`?)7K)lNloiD zm2^vc60@Gc{ak3!l8VgKsWWvo^QEHBiL6OfV{KE5P!dR(J6lnm*J~O>wt!&wS>3r0 zY*Hi6@rAA_bBg}-bi=hTQ~ktteA>HcU-z_U<_z+|*DwFcwcPZtdCr1M-`t*TSTk#p zq{FPehn${>OTX{idGpki-YSiizHtBr1#tvzjKA31m8wEdqu@9QNG%wDQL$FiO=KmYEbnV)Ch*jvtEuz2sM z3agqQ-J)!z9Y0iN=zGqPwyBOo>GJ)= z^rT6wjxW!tO`ff_m8U^RSet9^yuJT69aWgOol|yq;pR2ZZ7xok|9Ev*4g115Q!HNl zdEGrC`ZAC4!iw_w$ED6zxG&<=;t}1Nk^RT6bTVh@vpL4{oAWHE@(3CR8Axwnjh@hy znq9d>LOFXA<4VQI4Xs<%HXd$FWs5wVbB3eLBxUy34tLF)ZzjwPGIR0bWf${Va*6fa zuh_XIVZGlu1Fq5ACsOWq+TV-G%fz^%>M=#Ucg_+pD)^44)*3jUv;8(U#f4j2?h_APq~w_Q`yR(c`E<&3-&c1b>A1U zyvuw4=pW;UAmNTsHl<-%AwaV>@qNY+ZP8;wwJGw5i_%Og`NC>~JrvFKE8@ zs@vjIogM^g-f@;Sta3KDcy3{S^kVP(!x!)8ID21JT*i?z)!WPe!dj8jnM~O_Iud)E znQblJi0*aNEcssbLXNNC^)bF(clFH9=PErENol;e;r)@wMYa41_Uc*vGONuW|Log* zPd#q+-d~P9-|E>6q5?}IWB*@S>R*}V)1>!Trhn(<&5!Rrdee3F(|JM3Bb)P%Og*tH zA#$Ue$ojGm8@k{9v)lJ^>+_Fi($_Uf?%4g8Vg3IhKk>MfxTm)1KcUH9 zEbo7AP5pDx{`Yf>(swFl<=?!-E2>{lyDg-BpPO+<^=H-eX?pSJmZ$yybpD@^zg;Qo z{U5LP&$JK9KBv#}HTI3|iJf-m#V>|2v3@?+<<+OY?#$`d9d|4Wdi&Ns-n;t2t(OtA z|39Ab_{KB$B6((Exi@!Q-#<`f+bidA;GrY`-e$RX_is9X-_U=!**Rj{3%M^JFU#*~ zvo?SC;KKukz0G-n@2~b2NN;7F_0Ca_+hj@A`WIUiJ}Ez_|DpcB)Z=*YtzDbW~{<5jNzd%SDa(_KF&K7KiSX>Op?g4}!W8@9*oZnOV=>vP;v+XM0S zpO?q2jCERl_t0^>zYjN7zk6qM<8)7rhPc_%&KF> z7MF)tZ+|cN`bz2Fz4{-PG1YIy>b^9;HcnSoYGr2nFfm;(`SBB@eSh!V{}WWvptWDn zH}kpZrbR2>JY+Ymx=|kP;alHI?seiNJNr14G;WEklDJ&)$-0BD=E+R&9iR3U|M(Ri zf8gwETbs&nDgSPs*uk?q|IM36PxH1bY%5;v9TER#%DSCf%O~iY&1LVL-V$|jzi90C zi;^b4ty>D0hN%l>2%WvO=2KwuM~CAV4tO8@zBAcqmfDgVE1%j)M?NSpVBxrN-bf~@ zdzw6llZuCTX3q_!kH_1uy|nS2BG23~E$rNq(3UgHS4uWc{N)v)@Vt&IdhOq$TZb?1 zvbe6XAak1P_1W#EtM?q6F*`A?&-Vl z9y%^2wB_iNvbx+#y`<>qjO2||Wp-|0JiYc)(9GtC1$`6y?q9t)G2>jG>-$9lPk!#M zcXGJpx$(Aif~%%XRCnL>ZAV&+z31MM-~0ZyAOC?9;s0g@MXVLLnlvk+c4PC}z07ys zecSMW{aEnnx#j(5`xa+a)@fCi-_|($b?%P&*S=n?bh+@*V0L%5+Qg{3ScZz7w~rco zf0ZnmZ&NK4UCo}%64o9#MSA7ZFB7%vW=sti-8yC6#1%&;mPPrz`{w@i=;gUv_Uu?> zE;Du3^`wYByblU2DkoOd#NGY+$lU+(ES`%Swb$>ktC&}ud8y<4#v-oxeizzYu+Tb_`49>IvJB(i zl@%wQmenm?m3yuAl*j$j%bR}{y|SDC#VEb@yTdF`L zy1VK@x;^KC!~X=d=a$|3CoU9z?(oEePK&Z{F1&T@(Y(FeF1RfU`M2k@P5IpeVc*}l zuRR^f{CvOe+dVJKm&HE6TeVGVTGZvrrK_$9Jmp<9eRphKTIR|Ur>!5~%6)sTcjZ@7 z`kU&HrLU##t~s^#_qi>{tY6DrmMRcll;fr1JMU@WDI5NU{kjo-fc-uc2wx0bvvSBv2Bz5Q;> zC!cI~-&cxu>6d=`p1$&>a@F%|U;i&I@{HZhIp^usS;gt`G1}YT-cXsl?cnMC+B3c~ z-S@VLiWRV0xz2L_$AW|pul_m5WLZxxUU~P>;fDgx!^9i|BBFE7%49xn<7BXZ{q@7a z)>gON{7F)i%Rby&(Y@pDktJd7IXfKqC zy@I@S#^u5hZ#o0~+^X_+~x3=9>w<_4#S1iuus{eJI@ zlXpkM6z}{XQ4WP5r|t%kiE3JkoC01QiVJUw1|Af2b!Cx0v7m#wudks;G*E}9i|Ypa z6U9Y+&5Jv_PW^sZy*T^Yp6}1j{@Z%p?)jR}b2p!7Kfu8=&BHj*w1H=(lbqm#=Ho|> zJ$$0)(8#4yz#zb}IYqJfuQDUUk2)J0ZKVV3`xqE2E^~7*9Ppiez(A(`KmRMInK6tF zEsO_BCR%uNG>9-bRP{M6V{mX|Y?wPkEtp{e3q!)`h;eqGc?ToF|(SH zA$p3UDC2^O3<|=j9WD$R)(mUThjqC!L>MrLq6;9S1`29WDs%t6mIiTd!4{@Ee3{)nVW2Os#v-UC>XZi8q z(3vkjOJ&Ya;%rH}^Q_eiX1^mAfIp!VI*we_# z;&_5XI8pId2V+uKgmNE;_{`QHiU&Pdgt}D}PIia{DtS*d^)L+T3Q-94ko{y^#AMkX zbG+~ZgAj*1iSH(90R>$hn+TB=x|O>KD|+C4bSRhQX9h7Fz@dCntXm^Ig3!E z>j6$PmU1VBL^(%~2%iYQ8y+hZWK>K9Z*iJ)GPeXCQdgS1!h4I_F2SEiB$A8_+ao-5 zl&=Z(3+oG1cQ_w0R}t!THd5a?fyr~@l8{S6m)tJp1u4JOSvlLpLrQt_$y+Jeo7gR< z=qPBPXnivG$>Ar)Po$qHev0Oro8(ZqdmntE9(LpUy9Dn}mGhf^A^g(m zm+mj7zhM4S&coX#+I+ktT|;uV1iM7KWVyup89vJ-FLz5G4>2$|d_Cj&O#Yeqp%Gf! zv}!~rp5mJ7HC1h@w3dFz%2mr&-CM;S`Z@G;NdL;X;M#R=p~k_FS8`sNwmNjx+<@1? z(!uE~tyi?KN?(y5V!xy=@^2?w3LV94-qmfTjiyJ` zHlDp%Q>L|h#$Vap-L|V%F0^zu_A(CN9Q1ignmzmD8HGRQl=yKz*H}LNb6wXxk@uR9 zG^LlB`0O-Hjmcg$ZThr`)gG$@SG&L7Yqsv~^0~|Qy6%qr-N@&7+)F)1-95ek9P8ZY z!8e2NF6UY<=4)&9b)DX-?4{BRce|AD&s+X(#=L!T4fA~Rm*1=0`}~(ot^VJTzq!Ab ze|6`HVB5^2!gibIoP^C$)53fkS^eX;KL$F-9(HhEnm9Fa`oorqJ1^QSzUtQN#@~~9 zEatJw1lh+z$8M!nfG%9wp+uFHnRj=({yL-9d@|}K@ z{Z`MrmVS7{$89IKt=ulT;pq*w+v~RFZmUg9PZCexesta8ca!%{uH9AJ``4D8xBVvH zYS}2+y5cQGB35Bm_hy}&^Ul8Z=$C0P_0uEH3!J}U{CJ!2Y0V>UVP=g!_d`_S7Jx7WWjdzbmH{m%R|Ci6At zMH&}hIDEqSLEzJi_T2NQ8f4#JQ|IsO-1RL*EeimTE6Og;(KwsZ+1oV zAKN{)F~4VYFEaj&`GM^xwx9MtoIfwV^}chx^8b|oRsSzCEN@U{OlR(FbZm@f`u(G- zMsa_iz18j1Z<}tub?jNN(^1s1ziDmL(}QajGfupjuvYQ=^BLzRp3CHG4XAr@?E-t^ zv&8Phbv){=?XBT$_I(8%5mFZor<;~vyyIMa-{qIfG?#m`(+&MC*Xw$jYT2#Q%d)v; zzRP5n{V~0?$TL?X=0PB!KcH|V#)_c0j(8cJ*uCC%T@chDi6sXN>|dqXm+vVhI5RToZlx8E#In1 zZzk?BtyDQ`yj17u&KC8R6ZZJ<`rMsuHBW3-nL@6St<_$`=1CJL6gsU^^qssd{Z;z2 zw6fD{Qd>9uJDS(MU0-f0`^oIh6V|Xs>T+#!TJ|C&O5?f~`=<2N@U;DBJ5O0oTRl(v z?AtS&Pu-qiKIi@X|8Ip32|b*&ZE2f#xUOu(v%PDwEH~K{R^H}%EXu0g7RI*5W=GdH zt=y~rtKWuCUstzRt;#pMG49~0rh{7{=&sTYS1`{SA#f6;Gwx^H1%cd1uO&m8*~JOgW!+DedL6d%E+Y`KGO!{%ks59`DUt zTX^q>ZF&3ko9k`;>1Q==?{41aymRq>n;m;nm!CX$*4jQ~NywLwUm<4iF5TUlc>PD- z#dp%xd%l^xxq3T#&-cG}C->bBWeoMbnsznyx99K8-|g3Ru6p@n+V9-hwSKvUZ;vxy zV*bmn%(w0O&MQ}CFWH6!^_6Sc+xB{%g`K74Bm1JiFaIoeKA&=4 zZJzkQMgLB(6BpC3`*HR4^>Y_zTwFN))AY&vb8D9VeRbfql*!YMw;*iuf7XAa$AJ{ge9-JjBxJHK2@Nq%K zG2?Bf%8}diLa(dl>#W-SOKauRm7ItAECsvt{pN*~U5$^Nd9%LT{(hCjw#AI;&!)W1 zX!zLX;U@moxc1RI6;p4V8Ucx;~i* z+t+{CZqOloYK`+9xyhE^(J!+%pRw|B`m;FOdHZdpkAkfxQLEOc;lUGQKKR;dae2ZV%25ZZ= zDGje@eRwEmQ)lsH@^jXHxhXckZJx1zH=b7~_W6b5wh8ui7SAR>_dMG#-*d9}@yCAU zeLv1~ZoDSL!&!g-zuZyTqc`h3UvE?UZzYhY`Ds1V!);M#-fMjNE4g#=fyyg;#a=r0 zsVZ52c9fgizNAk6!P?3oEe5x``I8qZ*jgpl7_3Qq7rp7j^T~gveEiWf_paggvLd?| z?C;*By~zuWnePAFx3b!N`Q<)M*)2jR7fdgl7UjcqH?}=jbCWgJLDpqxSYu^Ln-k3bIt{M zTUWn+?a8q1UHQ+8?(<4CGQTcZI)g)1>_*U)ua!<$)8g8#8X3ZcuV%-) z=2+y{oC*KE=%eB1$3j2Xac-!+Y^KG~kZoQ*S4iRWgD2ib_S!nnKfj&b@|pFUZ|CQu zJ};Km?EAaw#@nhM&a2ruPP#ey#}jiRUf#c57~XgDo=}AGPBvrVGM&e}YVD30?TflF zZJTzJZ)?u!$bC9bYO0?tT)J91BePpAPB2Gn$t2IY`rMZH-_r{`~5~8j8F1pR`t2NNqzuT8nua7|9uQ8IhYN(=eV zZ?8EBt~zS+?)7Wg*~^^P&)&K6e*EoyCw=BUKlEI-FQdELe9jkFF3)V$`CPd^4?b*J z5?;Y)?79Af%3rZ#8fBBd8t)J?i#)m9DO$NOvnwY%CTn7Dug9vY>h5ReOY^Py?Bk>4 zk}PV=&V5;GQ4>39pGWq(KmV8fJ$X6u_KSjz{Xt^(UV*D-y?vbAedh8kZSR9i{(hKQ z`JH8LtEI;EPp4kQ#Avl2jfxZX4ARN-ntrq-_s){}yan&p#krbXbW6@ssws%rbav{y zoDFAK4&>TRf5y67V~@DYsaV|=(`M`T>W0VW#5|=(w0koz3lf%U6v(z^~SiMD$kc9OQ-j&P0A5n zd6E0;9nDRicH5-v-SwH~Z?j`tStwHRkTYUV0gKMGMZbiPI^9hwSagKDf3bo`7Mpy< zjq1K0ruloj91l%DF1T$?hDgoNuRWPs6?5I5SnjD3O?19`UbEt_)u9T2@0ev0(l{Nbh@|JND6+_s!{Xl=VwrO$AynQ`O2tTSuHoj&}If1}vC zK>p~B2NyT06i;(pYUQ?NOXQF8_?}!ab%LdLcgvbJcYbX#DEVvFAhBAQNkAZ{ zRJhOh_`7Y7EKDb4%rXCBZMb+#!s^)uUfkP$>-Gg7&JzlpHqCF5Cf8xV<;)6wn+2<0 zxbcLn_uL*gCDUuq-m5dB`8)2g)qN{`aPO|7e*DopyQBRq<{8W`*l}QXwKCW0ia)|f z>;J!fvAvkDt)4 zd2Y_+FKnAIZ&Tp*eY4{B1#Xu+usUnfmlspPH|J?HW$XnWm9 z20rzpId^iFT;sYPG=2Bi)5(4ANuQhlmgoI_mbUfD?u>8RE0$Mow_~}#B09eIX!Gvx zmrrdyf7HC}wfb$FyR)Ze+v|Zi;b|Gdd4H`A?pqnX|4nM%eY4khf6bn~`|IhrW$#vQ z&HMZ5z3=p$U$0j^{V$*Qch|Sd#PB=6C$VmyS8>TM@zQ#^BUan%=2q%_7J2?-%H`V#=Xx+_!L8B=6K)Xpg@sn>o@JJyH)fh%J7`bZCQzV2i?t8pKW-e;j+Ty z@rzXMiB)d*lKj(byjZo)ThvTk@M+b2FXKz*Le*N5k*@pKCp;0{|MEVE@2;;5_v^o` zzOW?P-f+g@(!0M>+Z^7mVLoI1ZT7COr(@(k*E2IX?mEu9E~@ekXy^_5tsM0g5d`^JAESeQql12< zgMOofexrkaql12Uz34I!^8bYoxgI}vir0&UgCc7 zfN|Fh31Qj2oz3TT`igkOV`^M9M0b_^{I}1fa@D$QF~9$NLvL?;p&Da0Z_`KT71KA! zbbgWIDcEiA;p_2e=Ysjy_E$)KQEXA!H?NOD%lKH&hON{7=ADjC7o5g<@pS5A*4__q zmd{_$*Kqi!&_fLYo&eFx7WacvI*mo=)*fPzj$E_qM7!$m?J@Hi8D7=7M6O+``3QGI ze(^hjf>c=sh6Gs#27RI$@)ZU7MJ1^zPVV5we1h!!M_3y3P7DkTPT61QJ6q%3 z+3T-ctl3b=pq#k&poFmCq!S(bfs0x?>Lhb}T!& zI{aAT{XMVmzO2ieTeD)9zJK}oTSD8WyFI@7^5x5Y+w-oj3|{_9ss8Kq{r(1uPg{)B z`xdMc5S?RJE5(p#!qK?L%fiCuK-N@6PX-PLM=#DMuUC8e`p2v5Kbf8>zxU}nlfujO>c98*8VGfi?<)Mux9D+49&ZgV{MJ}};`6Z&o$mGQ?e6UXszv7%7)4uGJ$jJAdbQ`OmUHl- zhYt^)d-kfy^@_P>(=PMb+$KNyoBr82YOsiyIA7sC@#CJ7fR@JrjU8oA+2a1aT>tTc zy4=xc&+gpoR&hAsapBDU(|`W{u`Yb~$8yO_owy(0_a9g8-z6wl_a*qp#bSMh>vMe~ z3o2fT6rJ+0+|GYOGkMmsXPTd59E2{LI4+sVFiB*UqzMy~imyYELFA0Cx*Lz|!!9fA zan{(E>6mjQL1l}SmuRHO{JkF}`tI-TJ9{?#Q1vY?hYj*93|a~sm`+ z-$TwxLLo=|Ti84k_vjpMyDJpZxioyWin-6%MM(y?9Wpp>QIwtW?t`J(q3_39 zq(gwoX}R%Ni-e~V3JVl8I1d=E*}=}u5FOtrx$yGN1C!;h&149ftZ%k$#`2!#!^#3| zjSiEQmWnEL^%Zkvd@5dhT+^k#(IG^^igiL(MN#>~Hpxv#SovjLgZMcGS!X!tG8kt_ z2Z$&nZp*9>YrN&`xge%7#`(;w_e-u?PB^k>Uz>jITj`3AyU#!B)VFQCljOsqGT~E8 z^I>ND#UI3cgN7`EDeeNZETM2~mUQ zXPjYa8)TZzm{VG2GJNpu-fh()sl?jg=rHAL=mnl#R@bldSwuXW!^pAhYRi->v2F~$ z!a=hZ%-U|o_g~=bzl&zSlRk5Ks@WgiWqk1!Gvkc9sC|}O4wPO|U(hJ`aN%E5F4YJB z6+?`BSe=@mIUi>`=P;Ae&0>X;qwMs61dRZJ*5eEfoA$LWpZ}9B?$5o=CtjBxeE5*j z#6ghd+H2lrS6k*7xdhukO=B-?Xj#1N70bzX2Bx3x1tH~$6CN_YN?6?`wPMNE>n`u! z<@r@fxLnI@Q(SmC^Em&>f7R<8j2o6#?xHsy6#-!y&Z;|Ry3Hb;0ij$ z+v(W2?2dn4Pwh!&KMOt!HV@@6)8LG+avOBBW@mE*DLXCrDaG_4>zwRdL8S#V*DKFw z$`MX{E!k^!{B#zFR!CzKWAbH5h530YIdSpr%a%mQuGwH^(KJ(W=^KWb!R7f0r#arO zdUrgyro6n&Zeq>hRdQ;p=J^GGd+t)^5y@-2@_`1|o65-AAXnv{U zyd-h*CBM7oTU+&i%T4_lvY^{{cB12*UE42uMtn7iW!!UjN9U_61?^!ndt4I|dw$Kk zURNr(e*Y&mlk8&`XZAauNw)A84l1(JU9s}?qi)_EQTJ9WP7+$6*<*0zSuC$WDPyj} ztHp*}mOrz-_cC%*TjY1I72ggsC|EguR=iYlWslQx;~Celf4HDNf88HVMx7IvTfSDk z{b~L5)W7zmCSBimE!|<4T7K+hF_@Rrvaj;ysrku&l@7o6H+tG2uhg(4VtJ!Z@85mb ze?Muy+Hy9nS*7LGgim5Gb^T&Z0-_W&U2`9_W!y}1l<+fRdM&msrY_!6z53MdcY;n^ zyskP{mld=gc*HQ3^FkAo>%2aL{ClMnmYbB#3=J+|bUxKJCD@$9&3pCdHU{zwUTWf5>`HVC&JO(?Yk|cO925;}!jApT^U3G*BYLM&MoHn++MozgD=hGIcqH zIxw3Erm-$KsHkH4Eo!r`8=tta{u(K{Me|*hLrq0e4s?Bcc$cFnTscQHTRA^IWm%q4 znMuS1t1U^nA9PqxJo(x>V}0=4yNkp&JDv-vUORc|yj{B%XGtt=cjo%YGS^U#VRN^Z zZv6iH1&Y>GtBv)6YwEHvBEU^m^N->z(s9*}v`)4EZ_3 zW$)z{HO2(iS)0S>D{)@d+WoslV6#nduad{~jrx0+NrX>6)<0U1}ay5iht}98M_cDQ-A*&-mwAIcK+( zS2freVk^R~8fd!*Y@5)o*}>Z&)TR(3Wa2L%G38m-`_6ZgURNp($=FOv*db?Mq1;pR zXooqMCE9e8So@2hUn6Z2rP!@FA%?gnwmF**9JJ1+u$- z=*J#QHD}t+$>y&j%WE^+M&r?|*$kEQyz5s!iwoE~sbaUx^uS{4oeIAWtmO?ROR<^mPuxKR4-=O^CNz>xYPS5VJX|^iapt@rhf3)n=RE> zPf|uQIu4ewQYkL0eWz&RrJEm}~xx;ow%jLf4S#eDtyQpNHqqE

gpblov`-Q3~3kbYGuy6Jigp-mCH@oDhoDlS!9+;?lTv+kZQ+15*r@B0nzb6X<=p_Q76_6_Z+4 z9l92tl$I5@WPw`GDbHJrAH=C#n0)hTL!wd7sXM8?s}C!$Pzlev^eX0jTiBkh>P$+{ zCZ1T^o1OowPyUaf9mh@G#%&qm?g4iV7p$+XeQp*Kk`VSS-QnWgWqFgIJ=$9;XtlqV z|K`gl--{NQE@ja0ahUeE%IQSKqTny4hcjn2SeI;lDkqr~eM;KpUhYc4OP5!69eA_j zt?6YRu{};~Z5el3Tm+3|gEfLa$=zMiucddaIhZG^Hu`eVUAfSL`TM6Vv|g@qP(t#~ z)U$itJ!hTQjbvODXteJ6ydQ_owP&4{>b-vAhVe_Mb1P14ye>7jT0ipU3V)%rb7!kV zuRmA!{AU)C#GgE6DSPLS4=bn6xb*UT_VW98eg*5bYJ9oKa5JpCuDhUUyQcuVhhCkV z&x>od&y04L{yLJ~xWwj^amT|fiLPt`E_t@K4VC=cyoELxy_lZ5!sFs9@1ni{jb5o8 zj4C#FdzUa6$jW@vn=*CdPs;?M-KL7xLT7DH^*-FVEk(Ta;+hJF3ro(Y>a*SCOkSa9 z>94?>T6|5ziRa8 zHI6Xj4lI!ge55dG8*77l!uQ{qKklU-j#=gTwd8j~w0uGGQ}gOuPYb82U5osla^j~5 z!@-Xqb6?DTZ2IhLsDMM%-S^G+Qkk`1JKUI6%$s&CR3I^?i24iSyB)q>l0`T+CD0;odHNOmXdGC*$U+ zydnG>?e?)r96V$2R9le8P7Pop|Wiw!%^E;pR(^Qy{h$rK~;t?Yrk~t-hQK1r_~d^Qk%Cdo9yvtw^mST z-R6zQcL+T_Hsf!#;S8}e+t)wn4ZnY&WJ2uGb-(qGhl?LbOTGMj%A#4yylJdy(*Ms% zZjcP?>OLTL!lo%j_s*k~sJVyM7VKCpxazp>>$!EWme$X-PfvSinl|HkY4qZm(hv8~ zuV>u<{cC;`%O`8|yz87i0!Mb6=Rf>XyIgv`!R;TcB3B|i4BT%1V4bjIh7xmh@w{WK zZXP@Z276AV@7WUGc1>FE&NY?f%WlnX;_Lcc^=%k(ikgm|wd;0XbaqGOW42Ro+IIZ- z-2LOz_5GdZ@pW?V@9cPM^Qij&5B`d0=l{t#J@_)yxOrCo-YK%|eUYq90sS z3+C!d;|SuZ_LAn!OSqMM#_{^A2CkYn8tW7?rEB9B_&nzfbLgCTSE1r;HdBV~tEE9) zX0uK0KDVxSs9?{u>OT81SKdOk@TIu@fu+~=zq0w;HkB0QT`50w)k`OZ`PQ!Or;e$4 zDs#6PGV1xw_)@nja`*MlpaVe(sc$Pn%zC$*JUXT}-}Oi2VXbZ6de?Tin>Mks@B05H z^UoXh|7}y=wM#B|$Jo#!^w*5uIDh(&UV z=~=eTg&yhK_AJ^G>lR)3$ZY-XaLvukc?=3|U)Yl?4p|5VOv~m?N@aYpMUubn&*O>@ z3)w&1+5hLQQ|9iPkNXQ=e)axw^Z%Z{&CmUIyW9M0^?!V@|89T%-)q~q6>aC<-J5Ep zQ4zADN9UoN=DvvJ+dMCCwm#g)lWf_Ln_$-77ht0j`6ns+=8xwGzOvkS<5N_@uQzL5 zkIyr=6|r?cU!YBhKq_eOPb~X zS$*a~jm)vg#Z!9IryH?XSDUY{v+-J89%1&+E=+07 zjo{*?55*E4vd?VKyR}AWfdGz-5^@4{d-5rI_|M@8YL+hOV9}b5{ zAJy%9>;KK(&*rh_UA2x{dXPua$^AFpyPNoMe0aQvQ&+)nzF&H;ao5>K?(73y)vagi zCQmQE@JL9{PJeGkob`fp*&EK>%aY(a#I>*I(1lmU8wBPy%K!bab(-S6eKoxL|NpFA zyRm^|^JjkhrqA;$Qcg?E|F}b*YvKPN-@aRGo8)a>{QCvNcCCVyJL5J?{l4?}qsF}3 z8&00{Q=Y=yo>dwdoWuKe_WL`B)=FRh=wJVR{*O!H_RK;RzdwcU`SW@Ehnwf;vUAw{ z`|0o6%zj|K-rml|m(vUWGpFB{)BpEnx5e)Z>5M0D+^LjSI?x~ggHzAur^LM9-#-7i zBW~YbcGR&=`{LG_i_}+mtzEIj%e?(Rck4Nq>i3m5H${t-9dhMT@BBJ9c}>50gna&o z?&)=%-T!~PRU8c6^XF2uLIbzI9T&roN8x%8&aI7RX#6kFuwM7|fr2;EjksrQB^e(y zC~`9}$Z#_-m=ZH%3)*g-S`?pFl$e_upP!Znp0}0ZevEAqrV;}KgA#b&*2}d3`qtU~TMh`n0J?v=ou%oq2qlX=h z9(FW(*wN5A?1&*4smX1?!R#n(C>4D;Z^=o%1uyRIu)Uag`pmVaAKB7nUoKACq2hW( zW6`QD&y*Y*7#NvYI0O_Rj1%t#>hJ&f5mC$tX@1MJa;-gf(XIk_^V?MfFi3z0ydRX?{umwb9v$!=9q=9< z@E#rT9v$!=9q=9<@E#rT9v$!=L<8QRA+7H<(@ygqR^VydcvM1GdHE)m;3px=JpE@X zc`sSsbJ6zSJe`GHJ&Hd>9;%!D6JdHFrnI9ihT-J)qj5EVoLU~=`8MsNQwz7k>b*Y- zJO!PjV%v{QxHgObJjc49pVmCsYP?SK$*rwRq6~MQ+wFLgU6;}O@b{h0^KSkMiaNA) zRT^h@`1{Q8v*)+}ioOuYb8^qg6xXALd0#5_?TU6`I8$@7GDTGX;98qkxf8EXTRO$0 ztwg?T?~4~rcVmC7JRkD-+zTPT%`04kG&{Vdf5zvo{B_Le-80Du=Bwu_Oe#*g9sMf% zB6Azdfm-|g^qYQ;uIKYl=QGHD+0ypjBl0fp-htt#+Xczo3=DC^o<&xWlUNBoi!6?N zA+}xtI|Bm)2e?;Y%JLL+55eg11*69oj2>SwdVE3i=vic=#}|wqUod)n!RYY?qsJEv zj^hg$Iw8G+kZwgjCPNXI=fz?|hhn=l7g?yV3Gpv>)Z?s4BX(>`pPxsYNKQLqht7^ zWB8+E_@iU^qht7^WB8+E_@iU^qht7kXbk@tq_v*(=l_5EW=`eD^=wQJ&iw!1qodZ- z^WS{_oNFCA8_PcCZG8}NJ^DlvNO0x_-xq6Uz21;;>eq>Lt9J6eDDrv1b^7)seggvo zgDKlp?xy}^j{*l-ZUzPkZUzP;Vw(EJ z$wm1&IZ25{@frD$n?fYGpJQw9b1*P4u!GzCrja~xqwW3C_Wo#lf3&?n+TI^+?~k_k zN89_O?fudA{vc}aJ44$0E$0t%9&q4rxtPDf$$DF>?!nz1qBEE_9jUM5={dtwuj!Ch zH)VHn+9HNf*YbFAxoKew7@o^0J>p8K{>aC$;-L7+v;%*i<8EwE=!-6p<7QxxB)zd+ z23-mx$^8slV|#QdOv35pq{j>ax(o^G_0KMkHnv9_+oO%`(Z=>@V|%o*J=)kFZETM= zwg*vT+Xd3tp3{Gj^MC;lv%c!@{zr*6P5+NoFg=aVnC#9{C98g6!Vx`whqPC*#=-|! zR%q?|ec$F>+(FI+?SOd`-9a);AcBE`L7kYP?abu- zykdQ3h5&D77B0}za)!Wp6W!CNa<09>!@zKvhscv0A;u)8q{KtcbOdi~z09NY8OsfP z0t^fc0^mV%`Op6oM+eDA+wG(6_R)6xXuEy1-9FlGA8ogfw%bSB?SrS?&iD_~Za*^F zHaj>`c&j85AgR=dLJAd{e}1 zvn?V`&VhkPLh|Eg&wg_6&Tk7B;pSv^ew)jc|Mchlw>%VeI?$`=$R!5;_iG|FSzq?B zJ6KI-J8)*k%Ja?t8Xg!dxX#;gNOAkMQ%?Ug_2$Q@Sx;A=d~(57mke=D)oCmYOA{n` zI}csFUANn^@PUO?e%Z7`e{A|bPPnz9^yRyVpMRcN72a@N(V+5j(t?xXT$3d&)*U?k ztl*A_(F(dBaf=J1=#?{oiW z>khQ||68h_mx1g5>17P_lQ=pLrM<-jUW-N=eN@?kc_G z5ghF*jh?_cdIIO@37n%RaE_k9IeG%;=n0$}*GEs_96f<^=$ydGBnj&(8TtqZN*u3$ ze|NX_E$drB7ZOAl`IZzaEMXKk%{%8~CB-f&YhYkyDZnl+*5f9goW!GJm65b4!)FT9 z#0diIi`<+YmN;-<|MsrxJY(+OnFsYNFMt1g?|1$C-}O$}XC0l24g@fKU&o)N$?AEX zQQ@T;h&dC4t)Ua%r{$)}7f+A(#d#rJ&#TlTr%U0v}1T`zsy0saOycW+dE%z_K zd}vv!&=1Djvs0&ug{=tvTc60N@b9OtC?Lm$9phkO8qdln69@J#l@n9yS=GbE~$8%S)FKf_Qt8%IypKzM-JsDE@od; zP^HnacEybqu0`9Yze!WKHWpJ{b5Vj@eoxxH-~aE;HqQ^?X#8);{Ncs5IDtTu%VG>? zw{SXany8~X{qm%mIl(?fY1YgSGbJ9zLXTToIBK?L_E zg+<~GA0JMce*L|`ot~E^XI?iyulZ}V#+pH*_P0lY>bo+vW*whMc+qV4` z>#eoT{qOA)qaxP5d8>1T%VBNw)4S=-8)h6_e%t=asyR~|T0fLWvrgJC62AJ#%dNfk z=TgtQ2mcph7I2DqZT&m_@U9={85v5CzkHxS$tMJN?}x9gcJ?}M28NZ~3=HPP_kJ=m zQ&ORmNRXQ(R&s}7+bA=-;BIuI%=fPk_B~Q*G>UC>XZi79biv)|g1gZLccTmLMi<>xQa_6npA1U`j|S+$|F(aGQg+lB<;n%Ydxbq$GU`pzdtl>TFO zVz3p6kB|CfZ*9lE`rS(IO|9Xv#O>&jUsQ9KL`p*#!>2Ba92l$ruwZxzb33fp=s1qKEN1@Pwn z;;O1IqnrCjH}{XuHH^+RjLtQT&NYnAHH^+RjLtQT&NU38&HW9Kj>em5_TGmbB--Z- z?|0BFn;0b1wZpxgXQ|6ASAj*%DI0c7DZ99I_BmIXUE9|w-92+pvE-DBbk2_@eS`}_Cr#ryji z*B35~>32&AeA%YPxXn2F%`-0s3w~Zp&5c5gtlPBI7|!l?Uv(gFT}xpcx2j^(9oAPI zB0Dmk{VhKk!qiZ9`q-)&GBV+zLL#S11tXbuE%apdTPD=gmdxOG*zjZs)9R#4c{dsM zxukH$$i6$hXy*6w)ekOTea~A_TX%fbjF@?MA9!jiy>F~6U=4d}75AlhL2`NegTMD0 z`tJTUxA777x_cpj*JAHu&kcDQhe~ZYS*)5In0jAz=4{WIaztrE)wGk#7+09i@9=6^ ze?C9v_~*$p*yr`#=l<~afbWmp+7nMM3zaf{aJhT&)jO;=_sZ7aSWz*ZwOy#9`sWA7 ztQ$M}wu@MV@E@^PfrQJZIyQy}tSf!%k4|I6-r)hAME0+vLr9&6fkBCffx(27eo9Ja zVs3sOq_3jHb9c0_GCC+bDKa`KGCC+bDKa`KGCCi_>~Ej}j@d0^v&$60A&&;F~| z|92ELPEY-L{>-7CM;x9m0v1$p9AIdlAa(d-Z?R;F?^lB-YTI>}#ac2j^PkU4 zm@r8&ZN@b-cGo-CuJ$xGHa0%|@Gf{uXVd=re@3%5Z#qz$^*XDq>AK#nV+Rf#IItn} zrXL#{8=Kp#rH$X;?|U>w`RaES>j&@W@9(jEc$Ftg zaD`doE91se#>W|}E3Pnt1FWN*A%WqjY>k6Pm*p+o9gn@!UQ9mE&A@Pqn}NZKl#WMw zetvomY+nX=s^k>6DYi9Pqf;fL+cHMCWqh(NVzO+HaoaV~`UHc~=v2w*RLSU6$>>zc z=v2w*RLSU6$>5nPVfYH^e!Q7x&DU(e)An{%QnJDwC1d634+U&l+aDOo&NTkUcJ|P* zM|zxGnpp?CxR+uf^*Hiv5uz_vjuWfbF`|r-z>G0-y&cKj2 zSIR|_HR<`WbEmh49rfy*m|=cYR3hySPxH)azx}3bb8Hmxxbc!q*8nTkVW^ z(i{5sT~=jc=;(XoFXr6IctmT5vG%XS4Ark5s89Y77`l+_&5OwY%=O=OPcTR-FsC%| zOknh5ui#~v?U-Tclh53PyPIIvd{A{CHv_{?Qo9LxsU;byMe(5Xs}gg--GrUoZlm3V z(OHVoS&GqFiqTn$(OHVoS&GqFiqTn$(OHV2G)uwo1kz2wGD{KRarMKqQ$qZ}(%5KHYD~%(kW# zo`#d32viiO^EY>Nar@1B)+vy7v+wlj1xlXa4IPG!0>=;jF!5&2uuE-GwJ36K=)5DK z5H{!6Q-)Rk{~rr1ua(+SsXOK2v8(=I^-{NLJQN+4n=ovi1DbU>cGdq2f8*8D8`3>@ zwK8ly#po6!wKRX7j=N{Yl@&b_?$JlVGY2b}-S#juFf%Z|IkLK_nGLi}n*sZ!APOuT zegC-`7=Ce+xN4~&GdVl4A{E>p_{GgS+8-G04~+H)M*9P!{ejW`z-WJ9v_CM~9~kWq z45j`6qbj67@MfB#KeM4k+x-4B>v#&E>8b0zQCB#~!y~DvsA2HpXREy9hYX(J(;|wF z6CQl$XKP~Ma=dZ(Wm4Jcy9sL!OcmbZ>3e(2`QKmv?hD&;E#&636+0{$*dH)xv9Fk^ zm$P=o&4uT+W~dZ;N&IB^;@wb=PlO%XB7mUg~j`)T3+%*q_EP$#^LkvE$n3)I~AjyiqTHR zXs2SdQ!(1980}Pyb}B|Y6{DSsq135hxC!f2IP$eB@VHnXV?M-bBdvZwy03eEn?l>6 z{71`Tou6B_I!>6PpFOevG^5M`MmzQh-Pvys%*|?S;cVEJx$|5J%TC2j%c?)}kUkOpN65F;+gf4aW%*mjGpt)5b$feSetsEUz@@O=z2#$JeV{m9r)sb%>ElZ~hu;+WRP?WBRx7(-~|ZrWJKgaqzNw zdrtVJ#Ty4f3;r3GTG#B6eBH4^gGpWc`roO{|C}E&@I7FdR?5R5xiqU&aK@on+#P{{ zpTVqixfvK{a5FHF(Ge)lP0WF;Hk!emKH3o&?Ffu^1V%doqaA_Kj=*R~V6-DJ+7TG- z2n?x?z+qTN;3Dr~1s<38tN148IjfzJnY*Akg=v@Xrn6rcPq?HpHB0G8|Ie<@2lL)A zJA1yc)O%qga{$yQ*u@-^EoOW2t+j_`SXbuF?*HGK*jGkxT=zqjp(C@-?3W|OlB={80ben^gBtg+e96@GWJNIX63)C9)XoOTL=cc#YjtNIN(_t$E=> zPVLrC&->lYTH1=-Ny?bF#XX^JRY!djSJ`LBl&7|{giI&>(boP%{0T+AmL#`i!Z0LuPjI{&wcGxCk-PkBn(Noy`nfHvzgD?I0 z_a*H1{63+eaK!(%%Z|E}dOOzl>Xb@8nX39SSfIAz`^1OGx4-9^=eP~BW5Mdvg@B)Z z3;QAtc=$|Sqju!y{w;G@;!OSJ_a=PcD^5GXyFLwcY2A`6sfvQ%g3e{3GLEH@idR+K z83Pp)&L2OkpriHU|J&E!U1t9L`q_L@F~*Tdj{;hK$36ET^m;;(G7kfT91jD7 z5lJ19>{4hyM2_d-Xg_4MA2Qkx8SRIR_CrSdA*20}(SFEiKV-BYGNk$;o{)aXn`s;U zngc}I{$DmZ$g0|8bc?Sv<55T$`=z7i7k=>7GNr4zo25HHVf2aMne3ON!ejK{mKaaw z^fD_yfms<}W(7>Eo;3S_<@ZVN@BN-{FJQd*^oxa$Col*(FqQCK5RXj|{QE3MgmZ>; zr^Q0oBaUz0J~_4LK+(ok_tUSJI|&>LU$8>0@0{Mk6>1C)#w9(+oafAuyH@Ptw?Kc_ z@8-v!^Iy+e$jlIPztQ!Gpz+p4jc1=%Z&$jr?z#On6X*LEowXUl#kYSgRy~n+?(5vY zzZ_>Z%5X3$2SvSaDMrByDf9l!-xL_Gbbt5)UAEJ_~V96kN2}0bN6#L za2`1O@tJjK+>2GG1}!A?Q)EEIYzr2qg{^CF2`t>W30g z9hxIAIjZE;H9Pv4P2O2;UTnU4hBW8w8CowNh~J3t(AsErDc->@n04jjS6iBYAIn~M ze)8{oyK8znCZs-{P^ihMx`4&x-f^|$jjyX#9=Kgr9kE9tC38MQrDMm-tSO${C%B#F zY%>;lz4r>M+=I27_!x9_8;=x7GQ}TsWqDzDchdtW^$Tyezh6H?WJd?XY->H8+f%sw z=P*VVFeL2h)TrV#)#Ycfn6IC5Q?0G}W3K)C=dbyf=?XF$Wr@Bqv(veKB(eU;ox+_$ zMyx9?XD1p)x;b<;ot!*D(Sac#SK>zQ9}9txQ6D=G+!l!1_LEC)?O&6YFD$~V)<-XJ zGg4i^^3Umk6$9(i?*DyzE-%L2cW`;jZMcP-fnhy21A`$c(-kH8xk=y?nAUR#jCLJH zyAGpWhtaOXXxCx1>oD4N80|WYb{$5$4nwKyz;FxFb-=M|>Pgm_Vl!9$gA(d*ZGAUc z_AHs!V$fY}zQ$cppO2$uc4f|e$u?t`S>9nBU-BZ(e|UUXS7hV0%DFs!T<3Vg{M5Ge zH_5-xGYnCmYP=##+wji5D*ks2yOuG=i22>Bo^`Ng;>2%!EBv(56A}^{+7B(@WZX54 z`KssB4-*ztTQ+Emcy!j!n$nW)p%dPr*Rf$rjl_c+`;1TUB`&EF5t#g8-5i^P6QtvO zXPZ1&@xbTnsgA-ep*F7?0@-`45{_6i7%&((dCh;5`Yi%?7vO8iU;Al13=9)VU9nV> zU!Gb7?gLEZ$s1j`(qkVwUKEP-nV6+b~+6Nd)eE`NikUqehY5TpE z9Yq@U3tA_C;1{sr6ZK=Q7YK?`NpNUg(ZS{D>XRen(tJ;m(RJMh3mv|r*N(YuyY3{$ z>d2kjbhfFrDd|zBUV+D-)|+W-)sEzeZ(edUoOko#+xhz|^UGQiC8ii_X%t&9@HH?8 zlr?y5=C*aJvDi4NP4P+0#~nW^Px7vqb?DQr8b*(+A|bw-2UgBH^ybnOTeg&vvg$z7 zr-$!cyJvOYe|^ln_{T-zk*Czp{NsKS&ez_&rE+%R&ec1-|1v*#^@y#($LjRS#7o-_f zJ4=n8+%I{TznMWP(`bDrv(du`75Wn^ILdpwUmRjd-l)is)amJV=dBwAG z_5K%?dpEqFZy1nyw85Q&sn+hCpXsapwFhH^&pw-O@<8lmD5cAU($b`0Oj z`(8?ZVcuZ0-A%`k^FY#Lx8mqr$7nxiw4XED&l&CKjP`Rz`#GcioY8*HXg_C2 z^>a8O{TwWF9U4UqoXU^uh27k`xP-)tbU}NiZxmKa9`0m*{@{=C|Nq99t}vbuyT7kY zUQceO+=&J27W+(dj?maBAv@u*fLM}_dWxZ4yFk&A3^SS10}a*#v*V!ciN=HKccvLn{=Kuea ziK@{lRSJg>-|m-OGsj2b-rl&Ql|}l*f)3`szJ?yrKpmbgt{d!6 z6c_b1FYf3%_4{G<;_PdCzCSzrZ|ilt=W9OC-F%+?00+-B592`72A-8pa)J+TGPZl@74)V_>Yf%+0}Yz<2fm1DW>!{I8s5#xORt zFditGXyMJ#Fxm+j?SzbWLPk3wqn(h^PRM8{WV919+6fs-oe+kdkWR>zu#19+6*y|Y zMJNPYG-#)=wV&aMR0+0l`sLbR-8;QttL{VhFD!EWfx(PrEMklerO{4Jvx|>1A5^t* zc-X3WeLquH$(<*+8EuMO&i(cJv$Drd$!L>{{mVz1;pW`e+KwA9vy?mE+B?N@^?cS( zoAMZMRM_Y*p7o@I)qi>Rq}=GZeI@-pd!lwQg-?EcQ=DNX!|vMdnOXiPrGCEr`JyqL zEp&cyExW|q%@Y>YZ=c7&!0`V+GeeSM=<1MZNAH1p1=#Pk_L_EDt&W?4p^}?{!IGp- z1EQ&K25#wBa<`4P^haCzqb>c>mi}l5sPbM_c+ss-?dK*3v)7+n~T> z;~}2F#a6(Yp5T#IAh;}nr6$qma(l>?TjGl4FTdVWE9JhBV8Ow#ZIW+SbaGSsM+5x{ zKioTB20KpemA0LmprTUz@A{+NhSe?^Y29%*9|=sp#mf7=XZnvL2k##I{5xw=pq{vT z;h!^a-}1%i_B>W$|9j@?3RQWht*%9Oo_ZP6To1qf5jW>ZIrjw}dG2$+c5Y>cH0qZG z`7HVE!`Y0xQLj`q;lo6328JH;$M20yA+7oz?&8r_{b;Lxv{gUasvm9DkGASZTlJ%@ z`q5VXXsdomwd%J+TlLd!gIe{j`pdX2SeZ9485=Ay-q8_k(NuTUM6^dE>#cbIwiy4+ zBdiIHiOLKT%MUKu@j}>2!u&Inr~VhaTCI$*>%H^r791-o_!s_Y_qAVL0jGubUOKwh zKUCyK_;J~%6RU0Xo+`?oV0rq($1-)!yw^6S`(jV`J!O8DdBbChUC?2TiOZ**ov1uf zSo3;c1{;GOL-!x??Bm+9r&+u!&mD7}|BY#_t=OEe&#!So8u~WN(ko)!4&Yew16rt) zB-?Ylo|}Q8iu}=gVXghzjoj=;nA8qH4w)02Z`J?Up(RTi5JAbsDKcw3E zOQG%jF!a&;^a8ap2G6W?;zVW?--&Z%vK~xKW?UJ#Dm6Kia4tZPbr8>PH*(qmBB}M*V1` zezZ|P+Nd8=jru8&Mtzb=TEdU>1|5vt%*@P(=gnTtw)An#mBSJ}lS+lNH*A zXKu8ZIJM%&65MV0z(lp(t=tR@4dgGuF*Sq?%{Oq@kGA4RTk)f<_|aDUXe)lS6+haF zA8o~tw&F)y@k6Q=zZ%ktzXBba_Yp7PVoP96FYriD5L{lsQajURx{%_cYt@dKrvuk{ zIy047v@tluiBEsB%p$p=c!#=1j>O|XnhSfMsQJfVU|MbQ|M#QaFLVW8{&Zb$=E)oQ zcD45M?@Mi_?H5&-DUo~eHPpG_(yR#WdATdyCx2MH<*!%&Qmxl>E_KIbn|eRr!_Rrc z*m6tOggEcv}JHN?(d}^ucdI=jTvNwestN* z=__Bne z#tJz$e#aCu!{b-|pv{66Ws2^?A52;4|RLFombsF_4txP`rI~6-l z$ElULzRzEm1X_6$TWoAs9m(eW%ehRVN+@|z+DE6nU#eCtAAh!7YcjeZiWF{Lypo85-vP zm|4xp5IsdvlySjC1_j~N4i|a?m?eIaAW8(S-lO=Q9ip3nq#RK4|{@x8fY%x^w5u`nK`K z>%Q90_FpL}>G8j(=T{dvFfcqUsk``3M}M`(qkVwUKEP-nV6+b~l==Y79*{o3 znHl@@qz!qF)!S6)Uofm*Ub4G+_fKBF+-(f+UWmVVaB*@wPh|s>YdG`0u1E6@+)J9X zU8J^oPn*>YugXm&R>$?E7}@_%^q6Q<_e8z3xUbkvHK~4Gqv(Y#=K?#Y{Q7Tas{dR{ zKq8pA_<{6?+vm2Pdz<{(ley%h#0|#B*LEH~;8C$&{ebBQ?h0<5rWFhUUtWBfU8P** z{(4URx3$I9cT1Mdx4wJloP1mHy3bPrrf>Li|7-E;|M|hMy|1R$zy97=vm;NWFzuFA zd35KMwZ*=wn^@m9T0d~t3A*N-z;%bo^a6wCPs0rzdz+@_S*>%CYwQ)Q6OY`_@NAZG z^x02q^8_ZZ*Pgia)YZKjA0lsCnZJw_uF9BvHGS{Y_}?7Nz7tQbU8(hb_S8hNbfqsk z_vK`C8`roDpHdGx7SPkaX_ABgr~FKAXy3wN;9q+{Lh_oAJlH2iuw- z9-a}dH2>_f^D8)>^uJ=-Fp2w1lm4rlI!t?a)>S?3d*&{l!6tUSp%@8Gj+nNJzC-Qfhc;002H?#}r zd~=(D_2L?{zkj%Xu*8HadGkKz+Ob*1*ePi5AJY$}HtHf%J>~{ZFbSCa!I0T{ef=`O zk}uws76#u^zv?mX{>_uKQODwn{?q#|o>~^dWqW_QuiA3)sCDcgyMPV656n2qxNfT_ zUzqss;!dud!j&#zR{xg?ZQP(ewXMB$VKh72haY!XTb4eQjf|bYPyWQ*_qEMZ1pyn^ z>#fmtf8Mi?yX!sI+@)HXMRIJPyiIp0%dhh;J*@JkCizmSeB=Da_ENTOlVgl$skJ26 zB$VmNYxq6=$*lGK)V#Xl@7#t#vsQEOJasce_;u^slQjqCy^m*=|M2{`-7#a9ZM6n( zbaj~X-8OHKEpogVC=%y4Ct!xvFRiC)}O$~z|hUdz+gyXe-pAxq?@l~w8J?%=`=d&G&<=t+Tk4SaE^93 zM?0LO9nR4X=aA}fZi93<*F@~~lXevNcdV#$%JPnk7kN)79op;qVuHs^@dX7hPqDJH zrY_M~t6HwH$o-IP%KFVBcbR2eci9|Vl5kCGeR@5g#mT1IcW3YaoA&IsYTB`CIrk;; z*BQbX-q{+<1S&ty&6;;F+acu)&jFbO{0#M9=A`GX{kep3Qw7rl$pc4>^|h2QN?cwm zv2ZaHgG6cy+tPy_ZywJ)y#DK%wclh8gzSG^Y4G{pdd4)~4Ltu(-|y|oFM1ukpUomq zDfXeYHq-QZl@B}8=KZ`Un9Fd7VF%lX4A)a#6O?DI%6%zxUss1YY*ERhj%|y+NF9)E zIPdmCoZ(qthT8qs<^GAM1$7y97|sN6%$59>;d^7=+IwAY7c$nwYu}6%U^=k0#XjQx z5e+@p6BYBES{QRSmoAuceN)=wnf}H`%NXQpmbiZ~lok(qeNS+&#N)M9do{20-kT-P z6Vo@v^>|>U>1VSbKE?;veeS+6DothOHsLkc$}olDVJPz-pR2t=v7V9zHU}C?oqlX% zoVK)5xFu1$#&vO!LoG{z-pzt_o06u^^{;5X#lf>uy2|dwj|*in(ys1Xq~&uQwASDM z)srRS_VOlwg!P$*mWGJL=@YN>m#`&!JBU2XV@Q6*6@7L3wguigbB>)^-emaIbWegv z-m>%b*Uvo3t4i6Hmd#SAN!MXR%$;PG? zf7UUsvH850>F!DnnI{e_`W|1hR8U%^&AQ9svUw2WZJ#K)6a0}zeV2{~F}Pphu-3cy zlBW{OjH|9SRCaao)-&;$8S?vE8R{JO}h+EqVQKn+F>;A1n!c z`e)ih=3LL8jPffMBr{BS!hG?sgJ`^r_>|JZ@)DQnt%oKrdR&{yy=%(Wi@$^4@_HRL z^%ZKVy>i+#a_`qO+dt)r&6}V*Gkey`^tv0y4u`BuUHIF!YSvAE#9Z4o<4Dml=Zd|F zGuGv}Gt`~_5x90vAj>AdNb9T5tyll>2;Mbm)%WYWBtvgUoM}?Z+PWb?F!Z5gTi@H0 zmp?x~bN2fM~Qfx(Q#?rnTV zW=>8qcvAHTANyz@ceIZ?+Q%L3 z+y#!+SI5kC*tMhZsaED8JHD77T;?m~j;!RHa;eu#JpIw5r6-uV*Muc-cxeVaf3&LP zSnEud()X+2Ol-&gS{g4@ho! z<+5VV=U@N3@9a1&8DJUCY@EPs!~T!uh0ClaL(>f`b|o5_ zvT8A_+1wMWiug)vxodd$_(>M<=?HbpGv>cpv?@cGC+z7_UWQ`h_ditv?YTVO>#P^$}H>jI)s!J8S08+xw$V)o+y?wiW z&g~h-ag6fH4psa$OW9-;ly1LUxb^HK zS=Nus(;Uj3ZJXI#>2)%?&fvhHvS0yvq2^?d;Ol?xqm?XK_0#=akZ%ww8t@klnva{H#l z*|jG<@6MYmOJc6r%N~539ebti z_KFRcx&K_0*s{3ZD(K)wshLIs#`YWL>uW#P;r$*hcBfwbOJTy1-~ZenJP$}Xu|Y}x zrrGrk%AxW%q!On55a~BJ|GPwZr+w3+qX$IZom$!USV3rB)(e(zyN%f7R!Prqa~1Bt z7S{Xj?hUa*}^$Vs4zvg3Lc*e)TU`|$t8ZvAAj8Ag3M?Ko3 z9_>+&_NYgD)T2G>(H`|^k9xF6J=&ulQax%xNRRr;jJ;jT?gGc_Q>RGt@V&W{Q`Gs= z@u6H!(ZvUMa~}R-;xnIM>?*8thn0{2%u=RQagCy+J(DBPJW4tEhWUrgZ_ZN(L%o-V zm7jl+CtKmLB7N8DUAyf5UtP4S=G5hmAJ-T&S-DPzC$ZqWikOJ=$D zztzukjxPA6!m6u!|NzAWg3~IdN zd5!fM|F&gT@!D?eh4Za4moeOB3>V`K*V78S`m<}Tr=QPB6+2$`7t7DM*BOcOFOjNT zm2Q^!Siy;kQU%NPJ)Xk(xl^C1H~;{|Zn&Cm?tAv!Trd z?o53TKK>t7^Tg&gSJ?^jFDcph!El9M`nf0y>3*mP@U!$87 z&uh(=Wt6p_*Lx*9Xu0w2ytlVccd+-kZ{U3wT>bn9|0;H=6S*6dbu8{0CtU2P;rk}tcwI)MiedD!+%9a~~8F}hcqSq^)PiEXB zAF|%Kc>4is&gwVzudZ(VRxNO=V@y-2hTh|IIMNwVDazq{$tEb+f35V<=cz$ zi+r3NKDqr#>lv}x6Y6^GvQpYPmp?LBKVY@_=YMhjCoCO@&)t1HF*)Y+oOAqLUsN3? z9W9#H7VNh3$YQ7Hz1_l>i z29oyo7F8yv>J{XrGrI8Z{xrJ3m%*W`&uJNhgBxSR+!<=Y3=3Eo5>6*4J!S~dWk^u3 ze|C9vfA66)UwoFzoS($ml62=;zx$u<-2eZ`URFBdnY1%POXP8&P+_0Tte($COO-mE zCTSj3z4Slj(S4oY%y#!p88&3uF;-91T@jni{LNckAfn{Jqwfq1pLXXTyrRLu+>p`y z@O=6I_x69?7pO8cqmYrR2&53iw)rr>!Ysf4>5d@61v4i44&PB3I2~yx+*qD|}S-L9nUUn9Ug#BUWY9y4HFopow-;Yz$tw6;8NukjJEE7jQ~W0TJ4WdPd>^ELq)j((n7hUC%#x#z zk{`6|zH2ytpgKWZg0Y=7TP*h?m&lhtrUhB~-vS?0^*5BeSuHpFuM;YJ{Ufts@T}X} z4R#;6D)L^5Nu95m6V7hDF!jgrqbk>=UR8#12&fsyb;-|I{@nXJ^XD&nJ~PjikC-LD zV)gUxfASt$94q$C)9;G^^84TXfASfhYw!P+kJ$28l7S(deZQ*QDly#Mve+qJxl%j~ z45B=wZG#1M%ODe)qCBrYkM_$(`(;O*q7zSr*ZxaC6n`N6Z_dv;n?K*bKG^q2rO_z1 z(VgYTgVBCj)1S6I?uJtuHwD^W;o0iEbA`E7i`ydZ3n?Xrv--sr%)U^xMas6vY~k|@ zt26j-_1ifgH#qP@g5CH?j)C5>!%iD5bT}s`pWdjnhG+FLsSROkn0I%6O+LS|oJFY7 z^#G?COSzLmqMV~gginOu4UZKHGAbs5w>Zr?nOg!6sVhxh;k`v|m*CGM5=lme?GYY2 z%GZSYh4lrhJDiW0s|a;E8>#P{z~s4cNysIkOKz9)f|OtCtekD)A*DR|!t)@+0_>M$oCze%bWTqy4fCqy4hce%WZhY_wlC zl=@|i4$yvC#O}3Bh5~c8dI)lI@&+)JNxt_iad2V|XJ);?pgGBzYv=Tf3{&5D%gpin zcJ~&ymEzi^lkGo6WLKw`pF46UVv~hVj2bV8i=n{LYe!ux($3Y%?O5{bWn;vw}|}k9|?JLRE-N1IK$V_v?%|`rB+9ycMdr`r>-ZB|of9-}fxfyW)Llg(kOI^S50G z<-04UZ~QlBSL`Z(hPtX}&ad{TmBqLF&-?YdG4IggzZ>2*TFhC0@OOZL$joY+fJ+G5##io=Lwi$8;wh2`Sz8naD-MA$n#)W@MMciuku*cOp6&pI9Gic$5V<>erm~|l?jWaW>yF&~` zj@PHp@N?nT-4dP1uA`%}acApOu|s0pHa)FtWW3Fo=*69#c)%%^MYp9jHgcOl_sVx- zb`BDb(g*Vs)HfPP8qHKOpLsKtH)hi8<3H8apGz7ZD-d(-HaT$R?{>yMS@vX~Pr)BN z4$f=JydZz>cG!dKzU+68T|C*u&LA(Eqj{s}jO)Q2Y@!#1zixf7*3f;e;r)i_1CH5eI6uhkn6xWje#6=+E8{rc z_5NjgmpM`PfnmWEnHh*S? z=#33;c^`b%%zAm^q@TX=%gLe+4ds2zQ7PWtIoIagljnb`dc5)U0pEURaiyZ5!@qXy ziKyMP?!mU@^Zl9c)-FzPKN#nCJiB*NlUinjhz9qoSK{0DUfJFYRbi*9_lz%*@|)cfl? zA8LMi9dzF^Wy4CByGf5NtKNNPo)p;^x>{s@T#!Zf+UuG9;oJ**pMGNu4?oTv$v5Rk zF>7+Pymp1?I{p_aJ2&^)*QdSKpXl4;{nzD`klZd6<->dI<9;s{o0&0jdtU7fX|bD( z{z1zvZLcdiE10b2x^Y$Mespr)loJ8jXX@^=>j|pq8fEt0ysGrwxp33h_cs>W+|tvR zIBU6Kn#h0c>3hB%ZR)5DNoBWrIO~X$iF(VnzYDz?YTc(X&z_yOI_PlBABiKAj>~?Y zy+mr-jROC(A)fW;w3hYwpVQ@*%~gm*`z={(`MGeD zpa$bU*^p}O-JNP1rQ{gxi=&>L+m_@wWz7T53+pzrwKr71U-Vl(mZ|*!V*$?}#=4e= z%nx)8CYAXA{$a7``I8^V822;Ov3?Ny!BfL>uEGC+_5;1`U*~;WSr`~IXgx zb~~02=?{N${otzM{HxD)m3#Nvt+$SM%ZnR)+iLjn% z9i?)cZ6|a8uRRswzBwn$A$?B1&<*DDwF#~t4o)&W+Idm2RO$8N#FJce9$Y)#J*oW^ zd)DnX_q^F>uF19D53<`)d~2WD=ZATm@_RbO8Q<$@&R*K{y&-FAr|;&IoE7Ufi|x2J z(R5|*s=N2@>{#COG{omnERWrt{>pbYMSjo1LPMsSxy&toF2f*^y>7bMKbgGfiHf zs{1n5l^bi$p1bF~X6i{hYnkvRZ;!`)D%5|tLn*hOc@O{8lBu`X@g1LgB9p^dfZGO*r}O4+TOMQ)J>$lkuP+!MHAOGUnRBI5=wOcFMiXhT zt<~Jey_1i{S@dn?%UQSA`4&^H%dJHh#oi>}oPA67#qYh^hBEo#d*V zEVOFv`|0=ieZ&G*RBZlQ`_6BJiv7l>9sByTs`qp+w|I5)X!Q}DS&2;+c}ufTzYX~* zE52x_a%NW14#}^M+kE0hc;8;_-dcY7m(Y%>)n7jSxzFGx#M^IK{@w?7zi>YzQ`%xa z28MZj3=9?|oYqoWoRL}_pPZAKmIgThbsk^h=%nLl_i(g(INCiN?H-PH4@bL)qus;N z?%`=o?mZauI!XyUQ8-8vlGA_erE>Kayy zHY$hSLW8|I=Sz-MeN^G$T7hT*`!7D~~cWzg_+>S@7qh@Ixw9`!2m+)#Cf7R(2oD znfG4nR|}avto^a?#V#d%qfbiBr*`_bEe${5vGybDz7Doo6%#kl;(I2mZpBzuk^HUl z?DqNF7ql!Enx?u&>dY>~@5eueJ$c|gV`@Q2$T?X?=Y)%!cBEhYygf~I=MPf@oraBe zJ+m*wJy}2PT=7Tegjj}X*84&3H^t)$Paj?1SjgzpIQhiHS+^X|?p|qH&s4{g(|sa@ z!M-%2^p2j<2kwNZiir$0H+P2ZJv2&u(A(K!=$P;rgevBS?5m-wWYe~mS} zcl2e1X3@%rSFii&)g0dv+x}fid}{IT^0*Z7QeWYW;%#ffuCEv0xsH90N3PxZtIXA3 zkJK+@5Bd=A%D?4dwfRNnYa6GWoBY)!F{xbg@``s4`48lqZ4En@tMy#ZmoI{0y5^6& zEO`^o$Il4)qyE9aZ{ChhxfiQ{`ZwoyT0>aG1x|fID!) zD)u~P|0!Mmr9vyK9!yr+#B<$n{{J_w`@)-~81AoReV^U-dKG)wECX+T3E9TH`?jfj zn^|lPa<4G+zTGMQQ+0dZizVzmGa|IidwlHjdy12L(B0JGSGiAqpo^3e%K>KU^a;bTTJ!E;6FWnZ)`m~i#=ET#v$9|ZzO%Zw?eoB~M zn(cm@%C*f)7S!+CB)YDQtss8x>$CGt>|Fg@=5hYZmVXA1zj9CZ^Vgm4URam$=+AMV zc^uD~{e4nPE?vIF_4V8L1ChP|HW&UYk+94F;jL=&#!w6AH@rQoPYXrLRsmg zbwB@vtmij6&%W*YriI(q{rnSSZQ1{4xptk@^ye$T{=HD}*X(&L&d4tSueR0VxjMS7Z?xk(+VLIj_>OjbM?1cw9pBN8 z?`X$&wBtLJI=+mdkdE)0X@*`$9RwWiw`^%j$bZNxGAZn*_=Q_;7lk?G90GN0TFp0n zWQkwf^0#&B+&}Mmk31_+epusqNKI|_B}tBGpL;2r-thT{Oii1j6r8{w!9Vd&ykX0E zWd+C5psqK}r-M>%Zl5S(*Wci(x#`q>CSwP0!KPJTetb}{Gp}I!)8shSORzyZA*@S7 z;p%?L)6aSMb4BW94yWrz-wJ(j=-T4G%@<~$7b;5-wqP*x=$4v)=>Yo%-aYDO0gr>F zBAB0rwQQ{MVvau`_F>iyhhG1ktk-+GrS5NI4t}suwLJN?=W+L{&yg%OjPu?uiF4z6 zd+V-!@0o^!dqnKnlla~+mLFiU*fPC8m-lM+J&g+cw&{nK2^a9$l;&h_c=$2x^Io&N zK|il5#xSIR+j2DBU2mrNcSbz0vHm^teP#xIrj~o2%ZizBcS0H;6t2C-%fN7vw6h+Q z6O&6a!JUtbyn3UHGDkZfqn(e@&c|ryW3=-z+W8pme2jKJMmrxvsq?|~2h#aCGQ&1c z+EAc%|IHg74C)>am6lpf&2xFew2t??R*!_E6yv_;hB?X`v_`-)@`W`fRhpUc zjZvzBrW2Qluinl{*P|Q+>c4&DZQne{y=#@xkz^M)hu5#o9u@w5^LD}>)(kG@v%Mz{ zb^Q2sqeEueiM3g;J)R%nns1b+|Kh_@!w(su^09w!Opd;~Tk6w_pF2()Z)CSQ>6&nX zKmE#<<3eYYZQN4i_C21ywDV-|3C@Cdx9nK|K3})qW!~X>#=bSLqI^smUGMGnesF2( z`iAlYbF-tvcGw^L{ah-d?c;>sTz(CbAH*tnYf9c4KiGDQro zw-=0$*)g-f`G2O8$Mq)t=Ks7k49o8Qbz@n5E0f`X{)6S({>=(!-Rjut8Rjhe)}qGd z=2#>;X}6@HYq7L@c}x7n<&V^M3rAfoFmpe^QIYH-7xTY1T*2Z`^@IMy)tA`A?`&W7 z_Lh*vAyac{LH{~wp1SyrqRFAr?d%mCA6G984h@;gDdU&$=5=EAooT;)6HFQd&sM*a z&tk9VdobX;!ge>xdA#bp61J!1|NIrVPaj|R=kBVy)dlt0Ee6(IRUOE0?Od6lz6+IE6~ zivDu;N4p#6zF@4E7nsn$>(ft#Dg_gTU2`OS_@A(FuJk@S>2))+_wCmAr6%3$vcj&f zow*>_`-x%R^W&fEnHer#`#gy?-{TzaJ|f>F5!bi;3=A*$85ltGS&7BPsU^kwiFqZN zg{7&9B{_-3`X#9qC8b5F#ro-`nffJ}IjP0^$%$pD@#TrI`{`fsi+>nBd~)>g$s?nO zPb!@35D8TBo@nY}7}OP_5b7cO$+n2evOUIa*F@_R3`V1ePtLMD+brhky>#uOsh6cP z#52BUu76RoOYrVgIlt)_!Y`eE>HcE+3+6B7JiKk9&Br^^H6&+CuuG&%mP@Rk;j>Kg za<}C15Ce0=*E62aZF^skHy zu3hIAY8?D{CFhlCt3y}K4R{?a9h|<>dPV!H^cDFb_Dkv_|8}xP9`-rx@4O^%!N&N< zdyXk^CrfUw&{53hUENmNXnI6#yu{FU9^ZM$maLQ7|3FXQmdL7%6j*|R^M zQTSs{i67^4jpd_+512GGHPG~(4xoV(dO6{``a0D)>CQ{!d7#8KD~Tf zG{#gt^MP=}`rzvk2GP+Aiy92q30$3%d`Iw%86$Ut;L%m~Kdlnv3Y0e(&M0MEe4zc{ z8S`yr?prq;?z$S?*x!7!`rsRl526K3FW8@XSR4?0;5S`6YEi;A)fu^`OD0(f zG#Ve6e(Ol~fz<~Xd+U|&>1Af@{l##6w&kYtrk@s99+-G$?vcxy4-6AzW-ydl9o(LA z&OpQ$WcplPi=Ggp1s_-A%E^G_sNQ>5x~|^TVcKzvDJ{+2oUMEPD&kN6+qQCTb%F31wb<`s zr!RIdw@>-6l*^jO_AYP1lEj}U98ONXQ|rmhdT?!0)6*`6RRs=m$@#67%l&t8&wAL% zW?8o)be@*YMIpHvT9r16U+2G-;4jgNnRRcmiT!>>xfLBP+OEfK=6Oh2upCJ!6WFnw z>9p_5FN-n{#K%3Bn>PP??fq?Hw+wE2J@`6hL6fBE(tC#wm>yW1de2()Q>1=>q0S6j z&KgD;mRF`vzsWste6UbE_rFM8Xm*;=y{AFh?#pwIJbz%b>(~8IrNzq{*R5P-yI!v- zo#FAX{qBeDE7fKE4OYt@{P!vAkU8HQ&a}Y4{!8Cpw&TCUU~{$a*K85}qYLM9y06+B zHvQ%+AMU?AHh=3=?7y-%u5Y~GqNv_t@4h)<+Phz~_c)tga%&GX@?TgdcZ1;$`<3nu zd-fm6zU^molsRK>dt+(dE&Y@1LQ-~|I!wQ~EjBJb5`6GC{|1-8cMrTeb8zkXCo;vY z5id=2f5$ien^o!-UE}pSDR(;#Y z) z?T%-6gAP`QF$y zA&Q~qg3773t!}r@EMWO2a$mfwr|kOE?ytKitzLd0{VTG{P z>7)y$D{zk=bh8vaZsuWNsN*5&(xT)H@Fa5`Pvhtc*U?Gl(MjgfN#@Z>=Fv&! z(Mjgfu>+0kTI`$BQ^V8tpY1$lIc@bk?Xz#sY(8~+g87{H^Z&mUIwbUP(zc~--r>5k z5zqFn$+FyJQ&@SM>#-=Sc3T+R8k-$m+q818`mcT)K7C!?UbQOU?8dl*ru+QIBlIKhZ%zEwHdoqCw6klS$ZhfIqV+d4?o>RLZqGlpf99PjTUM?< zvNPp;+NHFY&+h5Yi{_iQYWlP3e0jV#Z*AedAGYP~*Ke-3^{1cJxV^i1oAb`a`)zjY zNnL*O+*xb;kR>5sLVksqy}NXGYvT1Ec^BVFSMT{|^5*L8=sn;6+MV2YJCrfh_iEbJ z)Zd=JH-EQZ*SYHDk7>ViW7qoSj!rUP97pb@d?ejjSmy`{_i}0dhxpK+@hl^oNoWi{W-7A#N*{Zg{8b}7WQr0wzAW|G*+Ia ze?!Cb8OOzA4zsmAX1=kl<}y>=gQIf3;e}g{GD-ccJIrZ!fHUV2pIoBs`sjNO%J;vp zwN;3J(2{fe`*s?4#*^Tq>@`O;bJDBi(ih*?(t9ZU?$}1YBGCAT!M|1p{OPN%=<@7ibE1s$#>H+jxFSh%O>xcR$7{*Mj> z+00ma<6zhe+cghL*z_O7@GQ`tf4nhz;reAI!n=1g-v6=nd>{W&f4_vCsR=iap0|C) zfA;$XsZSGsyw?6$aW7fm`+DQ}KMo(6kMfJy2jF7-`Cj+nK0|}k%Wq4qxi;bM(U>1R z9xBVrz#u`|S*A#b)kyF@A6+&pGCCVNIvc7oIve`bi%Xxbm; zw07aPp!I?6D}+r-M`uHq?RDK9`MZ(N@wk_IjJkVz|2fvV&x3CU-(AkNT+G+j>gzha zRoP3W7w&c`-=DYq-Hdtr;u_}p$)yfkrY;`E0t6L(&;S$x&4*Nwj?^H|JdmB+cqa*r)mV^dR9 z^Y(q^`)*c{@6KhK%dCB@eU6_!b9T|$Ycow}+ZtXsdOrL4?CZ1dZ%>G-h|<}%V%v^X z&rMIFmPNkXwkNMO$})0kL}*m(X1BF-*Q#FIy>|C9GbI+S`eOwQ0A%?!i5I}SXq_c_+_JYnHp;wiYBTVdr*<0S$MZn^4hMb*2=X%aJ_k`hwgWii4vOidpf9Rjk1YWg=_r%}0$Ti$gdub`uy4N;_zjAs0 z;t5?%>G}Uu)of()lnhqQPjOH$n$^j)=X!yZv(q#6D5gD0=hI%ND|ecdy%l0A_+V@L zIo3<{&hjeTsU4cXt~KnRc;PB1yZ)u~POR#adQ?BK{E=*Y6e2hGE4xOTFe}I151J2- zMz3fPJ@->%#g_{k_N{#AyJVlad>ju7&AD9@psbZF;bS_T!SG3Z4u9+_-Rl^`8GP z!ma1>TkU-*9%xr!9@MdB`CheKi`A~*W+~cNsx;Mb1G`*LusGY&eND@RB@6x~C(iYF z%)GH~-RJs^R*4Vybnzc4$otvQ{`vKXMp29X9|{cRIlg}0*lZ;@MSq^#zMe}z4@k|4 z@8v)9_WAAqjjR93&-h&{?{VhqfA+Nc`|GdFf5N=6&Q9feP;JA1W`?y37e+ILE_{u< zuazEpNV}egfuV}DV^Sb}t@z}e{L&Q2`Jh!it)r7jqg}4iNu<$9q|r&F(MhDyNu<$9 zq|q+dXqRg!b-5UqLc3hk6#Wl7h#arKu2a*%$)e!Ekd^2;+mngY!)B2@L$|}CRX_Mn zaGepF6gf#yC3BOmFbB`11q{5E9C6dPx9M&XH7!56wEX?M?^WO5{rXj|WS`KDe$tjX7!F=J}C>{ zu9Mt#HeAMXd&&3IpF5Q=Y<`_xKBrT_vYcTKO_M}{ef zA$;Zuk6iZyx0(W`KZ{N);;*>=PVNF*+W~C@Bfj?y`gd>c<;+#RcQSuQ)q%y0>bKr8 z1aEeJEunDE<&srR!UJE^ZM+7oVOtmVwI4`SESmb0;l{;p-RlbSm_97qnw|IFN`N^t z_Fk(Lm#hc(bS<-Oeftu)Ev`LDdLH_S`K*`bheVmP=fZP09@5;vH0O3pP&@yZ=(Crf zuVYlJ$`0OYFDi7QSj{O`PSDYXL-FGCHM8o3oK{Q~I5cPV>D|lfk2CU$uV>#Q_o3>* zYerm*1%kP;EFsP4 zvbJdr4o&-)Sl_GPV;vMS)i6pmh&lLy?-Yx*`dTsqOAT*tD56Egp9?G-d1qowfYK6|*@vkNahCPHCL9bB_B*S=Okl zZlslXpH8w2*dgTo?QJ%swO4SvS_rGRQ05opyDVp7z9Bzb>e66FC*}^2Xxi zYpddBeozxq*K1sV;JhQ(l1<5Z-*|3Ms8X0JTfF)CYBR|VpEB&$yS)4L!Jvmf?10Sb z{Q1j2{MGpIs;Tz#-$VcX#btMjUMyX`tM~2NS3e#0TvktdY4PNa`Rd!ps~@j@<$8*p z?`h2hf05*3yDREl%7ydZ3J2x+l$x9i-Z7;hz|LMoe~;D=jXB>RNI&%o&&->*t3^F@ zY0K2JYrR(F=84>yUirx)q4shlzf9EQ)CcQdHBLUx82rdtM|_SJ-yY_D+gD{L{I%!% zGrRD>v3zm%sH@A?D^$+55ZI|`$@jeAaZ7n*!t*uPSH)F`hijjH-fKN$*$0ozeYTn9 zS_!O&o7ZMcx$!=>XwBwxZSU5Yt3AKxd&cT{#5v%6EGBkJtMY)5DOt7I|ME zUX-4u7;dtG^Y(W`zvK!(=@R<~m8DZIOT63hE~}oMp|bzm+*=O?FXHavowWLM=r%6{ z1L$;3b5gr_Y57H|#U$yrI;` zV`hZ(@vh9!&5m&tX`8?K$dM^sPO17KqCqQmtP^=BzhXtsl`etpT3UIjzAHLbtc(yB zoVdS0F`#s@P{hpDUXPv+qXo#}S>KA*FGE@ODi;HrkJS+C*l zr6+g`7;6~%8kHBYJ#)A%5ga*RQkXyXk3_{yiOuYDnACUu;L^DBK2?jwYhsjoJcGP+ z@;C2^)(;#64@B-^U3F~654Jg++hv6AhaCv=-^Fl_k@-$!{Z@Tb8HTGm#tXS4W4l=P zU!8I9#S`5RqCZm1-`|`rHHm4(e}@BpD>mK&GI${D$}gYh(A^{C0BU zv?;bFyxI?T)@2;;K5+7*8vi7-^!b&OTd)0?`Ag0H)*RdAEnAzt0yw=VewVzY=4ty& zw;=DJ<%h$HpYQyX-s-=xQ6zKi5_eDC;AhOv$Idl8H~V4rt1Ns?Q1Z3pk1t+JYD_6?R?mxu z$D?1j%w}AoQzY|Z^5dNBBMi*4IqWzs_R*E~k~d>Qdls zC;lXV-2=HEqW`UBBmCDg$9iqf@Gv?1NpbzJDP{HE?DH7s|6j^ya^RtcEW@!^2iPiP z)-a#rO#Xi7YUA&Yzq^|Q0@(i?O0nE2uaP#-P0+sy6d!GfmlfSL|H#g65jbot78~@*C7Qf6iinhjW6Z_)*XRb%{ zp}t$00WHTOtQSj*tgG$#|A6gi#P26(UoCw6ogwMn+l5Ro(>~ztFaBE2%EHLU!0?Bc zr0d`yQ-YBGq7k^m_=lHyw8J>sVI1u+j&>MFJB*_p#?cPrXoqpM!#LVu98w)dbx4Qt z%?$f&5l4w*^_$m(ws1Jz+O4m^az|@Hc|v{dC&^1USrs`~%oW(*)EdsA#2FDUn&%WZ{&4o;QVm)W({8t!>2~G|9pQobiZdbKM?((_wViL40@G^ z9$h~mAu+vf*SfWqny%k;MA;8yC){kCJ%P<;&buFcffCJ!+`oO?`8Q^Yl6$M6qs{9B zFEtW0dgE`U-@aP2yHeo1b&*y>;j$fb^uN|*e^Z#pZgDqwcJ{fN#lp|^-dP{mX0+Nu z+Slg)_49sP(zudi8rMFsfw_C*zE zgySNZ)`HRNwU2{@0E#vH#%%-3v*FugJEWYI9Bh0Y*(2S_yxf&VVep33b$27CkWX<9u z;+7u}Ey(_v|6u6}ksVq+J7wQ6-u9JC)v#!?l(SAYBl^W8Ygl2z}v0X z{ZEA>n@-B#)HY!+DY(hr=RHxXyy?7Mfy^QuEvMhl_EpF9RzH}V{7LkV@%zRuM3IkUZD-_u9)vR};pFKn@bDR|z5>*~j=ze}l|nZ737CjOx9tc2%n=KQtOJMM>Vbo|X2ulqxwE@7*^0Oxl8hwQWdHvX?$uwMQ}MYmY$ARoMj54oQotQ1WF7mbN^`@1XKPSxk zkT)l2#*%AQbF&VHaqsxcxVkgqyvD6yE!T5Ne_O9TGFiYmeU(Dowl(u_$!ENlEPbiD zHe6QyOHWX>SMSGkhFXrt-7onh1#ovOPd>@svVo6*VKpBEgDrW}kntIruuB|P^M!mI zy~JVk*pks>OGb|^5g0wTMC85ZBTeaLCO$h2Q)9AMO`AR~VztNWz}4=r_nNJHyL|5G zu_b?P*?HS<@~xJQlC3M=QY2y(W_54YxjFCbYma`J_EJAR;=I858^({f37^(Hk~uec z_GR~U@${X=&7V8EJH4Au>rJ=Qn=O6p?uormyh^?n#y+gN^mXp+&9e`^U2%K;JF|D0 z@7nLoKVvdqb6%uz@rA=DoF4=}y=c!pf2u+D{WW#|&i;+*Tc0cIC+klPUweJS_NC>k zz9+sHxBF&SH2<;PV;l2(M)xA)&zK+Beq#G+|HJw7;#==K*DL=|`Cs+_BE#|qRmODY z&PK<^Xr|vks%jMX_t{(BPW`s&=3B>}1v?!@M~^Le5fU}pw;b(T4ynH7HAvs`%8ZTO z(%}Nf>iu`@RFkm~@F<<2mC%zoJ^N7OzeZgrDQkg(13QXKr*bzt^h!@|7Re0aU^?f> zCI0fTlR|4`xM0joU-42dmM1L-cFbH}J*)b2*eA|*n~1R8pXa5OJ)1rA{ayv7H`!Md z*H}$#wra8b!1P0;f@cnsZh`y@>xqRi#mU|Fxebqh-08HDoM9in^t;u>=u!^b4;(-E zEP|D_b{!RLK?W-9Ina#9Xi28-9mw|~&zaOwGqnrR0e_~-ELuo0iMPk3g`O6Fg#><7#b zNbF$#v+{l2E0=e-{^&QFX_Y@UTYIKB`rxHK*5{WzjtMTxOSVxdpQ_IGW#;$iZk3Z8 z&NXf0copEtEOYzQs+VHHc{$1JO&>3{o8}PBQo1A0+-K7R_6PkeXTDF~t$n|pQTIb3 z+m`(=!~1M2)s6UmKVi-{E_=wVp7q!0y6~j3Js0!Xf4Z!bRaf1%@vYf)S=-8}XM1ys zr>gI-FU|UGWO`)v-*f-h|8!X=WTwfz%DIB?PT8%uRw{A3eZBJa_FN8B+ur|G?znnu zS+&`XJnqH9l|n8mr~jMm=Wo>aQQRr9;_vw`nF~FEB|8J}y_!B(_3lU05BCHT3wPZY zReGFuP)sJjW5XL3FK>@qU%0tj9xV|5xxi(&ZNRO92&bYgs>gQ+OkY>_tYNxa+}#-# zQ{Fs^YI!U)C6zzp)mfewdRz;-_vkU5VSc)DX-O%^^VjRc8hm&;BUN_Kk$G|F(wyXr zqJQ4KSvTv_y$iQrzunGPaY}hb8HeXez76ZGF8q&wb>R1b@c*f@FB;nYWOsbldwrn& zK>9W2C{_L43lA5S-+cWvX5L-yGNsAAwasS@Ow{@@WmwCkenwiUX$&J}HA2$4o?KNj+PBh@BM>bIZcX<-DOTJ_KinX%Y+oa zsx`H)DVU&NkeRc$p;w?nY@g-xXKCeS-_M0aMwFHR`<#|`zxs21{yafJvBIUtQXJT1 z7?=~JcdXCNZ{$B<&3>OD%r}Q|rJ-%Y-MZ&W)s1$^4>ZIrJ$fY>jvrWg`6*k)+s8XQ z99^2qnf9_&HGbRo-PSK*!C&@UWiuV@887Y@(4TWh(#AXCiT)wIBahBKnCUld=O@=6 zJ)A+wRXb0lX9v6IO7A?e>iYKi-ESggA_8B;%n$ygXtN?N_Gjb1=&zoQ+tezU#DDRe zYA;Y$x_0q`y*zXJD-G+NCpKT1;r&4PgO<1EFKg)ZCA4S|%6L+1@be zdu^M4bbaH(RkIuZ@Z9^R#vZXbTGjMe=>(n^EY}`v+ZkPWcryPZN4Wx){S34D*k?5D zyUuEp@Ra*wSi%9jf)$h3S_s$s^jdOe9Juvmx2g08Hj5H9{bSu8ZnoRj%hf#GGQYxT z=GSj*HXqKjl=+80`8zr0$x7$L>2iO=9yapaFz5Wz@O9PX>Se!{MPL6ubB4c9%_$u% zsaK2EH7w=e_pvO_Zk!ySaYA<0;Vdg3JhVR?YGV7|# zS41kVpMU$=#o~i9>3hA>9qpA@MagH>{Oi9ezUm75Gwc7yUvZ1RVorTtzp`QaHStAX z8-?bdXsC5()_vf4=2>0s!l2p(E7F#*X3lAK&3$)We3gvf-SgTH1OrSy@XT{Rwmw*X z!}I^Swd=dt*V{8QMD0lH$y~-i7k6hg_uglP$Gi*-_jnl?Oo{J|rX?2T<|O8&#QT8P z`rhMJ99`=>+6Nu&gO2t=NBf|oebCW9=x85wv=2Jk2OUyoYD?=Ko``>9UdObq_~g=w{&yQWC8h}L9oiYR zD`a*WTieBT#k-{g*m{oj&%Jqbd-3mSzCDH=8v`fJVg4Z^V8#By@?_)Z13Q;*HM|@+ z>B*P>?hKL*0Op>sIH7!VLRU8254B$O~Pl z{fS@HF5m~-0cL6DTHy`0A+32Qu5(9d{yA{e;XcbcIg4e6RgBlU*2nOC@qO^BQTn(T zpRVnOjfNM@8KW~J9UezGES9h+Fub777_G>a%Xnwv{=gprKh7M8GJY`G;kyI3!qT+Z zpvjGYLN-k0V(Uv`toJqu;JnfEqIKFVQ}Na`hPvQC+>QN=I^SIv1wV}E`oEU>dSP~8 zhBnt<&wpG8#5bn;M{?i05WnIq#&wJgorY{q7oFaOw#b}MdSY>H z*;d2L5|({>2~$)mdt|P=B+Dv4H!O+?3~s&0zKx6H;^*~(-x<}JYoGqGZg^f2zmRL0b{j$)*2{#u{I@cw zTiq~)EyMOaYn%p~*dv(_z1xifBEIYqHJQ#OFD$J1F^sL|i_OxL>Wv>67^XdrUGY%n zKn}mPS>yrMlTW=>H}~+jo;Uh^&}d!$pS|&o_g+upn-n5rqbqwfH(}3L6`dD~3kC0< z`Z4o^<;O!t>+<(}ReAH`(S()aeSLaTLeI}{;OV)qbzoV7kbR(qaRAG)e9;5%&6!W@ zy1m-j&3L~;oB5O5WX+n(&5ZLaw3%f~SHG1jU-My-L5&A83enDS-g8}tACU)bi! z_vgk!iFyCJzc*_9HfBf1Wwsa7mVBv6d29HOq!_*g76nQ#?XB_REbmE-%+mKfi>Q{Z; zuWtW;{m;KbsXYs|GnUFVgex$b9Z>(kvxh;lfY*kJTfw&{@j&4Q#$yNA3o^~+cQKup zQJ-Mo+cT5PGCBGPN5$sZ>8f>XXSM`gWzvaT%CseDa*UJzp1cVCJzP^Jd^cd;*I>PS zo^?{b?4Bv{AD9=m?+-h4W?gK-$Dq1{zqw9S|6q=>tcqysp8knDE{Ng5?RD+*BEiGZX~4AYP<_Jf3y&ur@#cE-meZ_>X`kGC)qBsG zzdoWPu61HGKW1W zaHhJ`v&I)7KOUK3k|x*jl)L2|^Ts_94zGXPOvqpB@TFG&tnk9m4m$Uv7(d$aKZ({> zxc9tqkHxgt%Ig>3=dO}}QM=%Kgc-He zyyR4Hx5$>~&}g@4v|BXVEgJ0>jdqJhyG5hjqS0>AXt!vzTQsD)MH$d;QTXZJBMLmV zYc4ftKQv+usN#As$xGv4b;OAcENs^EXLhzM_1M@v_jq@*^x-oV62c5R6mXr-?D< zAj2c`C0)#^_c#3X(Rlr9{#-{6^^+k1I=p+-7OK3K*2$On(ZR#;-T%;)T1lOfIc!IdKB7?P` zE|$GtEVshv=&hL>tVG#e8=M(7RX+GUKVj9y*Z+SVPP=}xY)a#FhMq{a`&?r_IPcCay2J+97FdB|We$>6SxrL4T@>zSTaR{I|1SV%B1{NZER za`n&Y!x>j@%^hwlaF!%IIk;qo=Lh z8$E61D!O4JN$0)-pRGQYI{ddTTyRqZw+s=?2hUf0W1%Wjokmd!2mT_(HikLjgFp1B$^H^MW= zTgFmPYn@qM&c6G9-hOU>H1`qr<7)94`X^!wDi@wR^rdyV*h)Pi9jj>$J{^7*Q$9Ef zXsrhMtc1VU&hlHgO(jhrAV}EvZxX5w)>lqo#)q=NqOi*QI z=c$@O)#ZnL^2qCWpP#PAy)j z%f&KD?9|%}?|I$BzuVfLY>N@tR&wv>&vUl--|y9HIZzq)@Xr1N@&|Gsr2nm9nQ|cf z0QUoiiYX_o9d5@uFfd#^5u9+hWA49mCu%v?735u5Wf-I7)nItx^UnnK18N(%YZ7Fp zU+R|p-Vprw4@*Ss%EEih-+Tm?Zt}g(@#6K(>jyXuPQLID>GZYy&uI~P((=3Fmz0bJ z=L6b`9N&1=ao=G4_eC*=$*+bZtbS2f4Ra3j^%{=Lq8;aQTRhujr(RoU?N)dF)$9CK z@(r`?TGk%gxp@ndtY$9r^@gh|&e1{9jX|G`SHE{Y@iLxWPW0Sfwz#KN3DP?_a~RV- z+qU~8A1&qlap%vzT;nq@_OsM6>|;Ha$xz+(W1`fn&`A&H&C1UDDRAr6Z1Z1+hePhS z7u;+waQ%DGE6uwXWP7vEQMG6P)k}X}Tp7F5GQqfq_xie-uGP1;)z-dWzO9DiZP(hk zZIgl~-Apq(yl!UQ4*|DRzdE0^A1F4>QopJ{XLiI+{>D>*GRh`1zZk!-n^IZF@P>Il z(|y&@t)KcE&c*0x%y}JlGKt|-&T)zU1CK+t9@eVNmb;-6S*ATlKI6fg?BD;m?p(i6 z8K|DG+f-vFv3cI<43jvf?`O1EPu1r=&b=V@ZIosE=Vcj@yPv&nn#m)4ASyJu*W}}} zdD%e=U4J?#Z;4srWGp&k@c#UkjE8-fbU8_{K+sGro zBd&Q`z5XfnX%Ct8i!+`(zsPu}e+0yvewAA+>h|eRojJlx@0stverVcz`M#@FQS{cu zXO0~>ejwiW%X2>`%Lw7qz7I=sq+YF?mFGS6<;t1MzdZLlbZDpN!V$b(r&`7c+X9D18>fo$@4vB%@;);uIEZ~j(vaEn#1qn@_p-9 zz6SoK_s`zcpYz+k;f2D%*W32hOFrCEkaxd*1FH?gJMnWzzi;Qeb6MtQ*BXf*uA)|J zHu$e*oG0(881z;oPx!tc1PMTW! zExOBSTI#Vzp4=CqxhwA2p7iH_e(bIhL z)Gky0^>~U;=uLmg=b8mqr>tAdRT9#;zR|xy+UoB*v2#l^#H*8Md}5sJpt3r+_<=;l z3bv}|=XNc9`(rX*lt6upPj*$?b@e5 zG|KBY@b`R~S;u-SsC*JnzIOAAC$^bw?TZ`jE?($!`tN&!1q9?<*HTQkbca<(rH}2W=>%fac@7Rspe?GMN zdy8S#9sVP0P13KvGUq(7?|a^>JO25}&tiU_c`^U@wm*Ab?`LEPT&BY{efPE;+&%T} zGc~s+^D{8S@iQ=hCKwZoi&IOA^%L_-G7C#n6H9Uui}g!VD@sa>Qj7J|OEdLLGILUk z^^q^#h~r;4+EpK&I~<)m9GyEHojV+zJN&q9w5$H~;9A9u6K^K0Rs8;Z#<_{-GWl8q z>Rw#Cz@GRlvHNfx&uCZu;iPR#+q}bdWh0*LU6WRKHrHcOR_(Shwly|8y0&TM zUiDx7HhlWJy1i;uzS)g&2TwH}+{&2!nmd&1X>0B4JJ;XF`bX$T-rt(|t8K2dooHv* zI+5Gr(?#oVXxyoID&3xcYX8hTQ?{&JePn0K`Ls)EFQ47hofpkFZPoN=)A{mvZ{FI% zdp~T;+pphTZ|hG#t8sgG^ET(5i}%~?*ps^aYcEz`rd3AcZp;5#Vb677b%5uy;cZ4z;;kilc$U2C6Ae(X6QNj2}&IzT!uRZ1TS$& zUs)!gch2>Iq2?vYYeE_a-!yt$NuR3pz;8kWYvUfxKRp$uv+nMl{I=r3g{-vie=DB- ztw?K{`B7;x&%p-e1&q^7YndZh*D>ZZ#xrW4{8fJ7cJZDc91c%g-ZR=WT}eAA>A6X+ z;bKgG`SAmc1}tr*a%%-QEG-xNqW!gYqT!Rjw{)f`$}ml5mp!HZATM_t(;MzbcV;lW zKG3)AP=j?;HA~J^MUzyH-3JnLzqL*I&mhBRWwSnObD-oQi5>dqi;|-`?o1NAk|v*V z&bolJBKVYfL-}S7kDvYrmhdFT%wEfQpJDdNxMLqXogau@_+qhPAww+lyk`Oy6E0o7 zmE#-z(Q?C;Mu(>Y5jn*UZaJqH2tSBC*Kjmrt4-Xr_Rwy|&j*foO}r?=XX14*zftYU ztA^&P^VM>TEjOor&eO_!_;*I%fn>`MOE2m7X_bgBGHOwNFzM2=f;OK!*1bUj3j2b$ zTL!cl+l9nt?%=+|ocH49deL|HWh0rQ?5h& zq1|=GQNIx9y$OrI~L(6e&gRjTSiBQE_Kql+J+* z-#_1aQ`E-KbK9f$RM~dMjnh|eetP0W+v^hl;+h1pAma>W<)C-fJ3UWsi~7p>LFXy+ ztwk$6eY*}cPH{U{v~2qIw8dNISKBT4I@81VRF}Byn|qgnZ_QfIlS+h2uJfL|${N8=m ze{0X?G1o6UrGH>izIL#j#n0%AojX{$KP9ZOU2{sHZ|`!c_bJboC0>mD$*{0Ou4MKB z-v?c{4~IU8$~Bw+=I`|boBZte>fHL=I_J-fHt(jjLO#ji?B_0iHJNnx;pY>Y-ws~W zsz3Mi`K|}XbMGJLno;tm^h4wP#{EVv+d}JVjBWSpKP`&q_`~>T-6ZRvwf(G_s>c`I zwR|||kK?4@h6zqZO&zt9dT)+x6tP(-!TFcQ1cNT)ef!N8=OY->=`9 z4@;PDjWkgyyFS_F-_M{od{WV#k$bfs@_r~UI+Ax~()MI;zl>S=C&Pn-CTSn-U-pps zW6l3tKOAiCr@v#5la&|HT3h4rdi^y16%&3`8eP!aXJj{F-h1Wjvf{nvEVXP$3YRFC zb~sHoi?L)06Y1Fbalz|Ji_bRPmHgpw@#3oYUrxTFMsltbjmuhWUJ7>`^g8g<^JHkIt@-cG;UoyX;!*o6=Lm)Apb3JY_j;^*rsf zZ_jK#b$f#OocHtpzZE(pG}>i1;|=YdbL^3FflK9+m)|+>f0yI6x>eRwz2+hNW%)AM zbXol@r&*t7xy=rq^*cH#dYAthyNlOe-K+ZJep{Y@j)_fVrPt?>lbN?(HeH^a{^@+r z+^KVG>-2u!Nw~deTlMqgbN93O^WX0hD0z6v^?&zs>pj-zVm|FC`LyZwQ{nI(;j`9T z#pk{%d1do&@@40-a6iNzT1A)KKkFC8}IMbXB{YCFtobt ztSOK#`;r;wyQh@O9FL!W(*D3|*^&huEpG$PH&0mbdZmJd;I197W!4;-&Az$Y%Ob$7 zU5uqOp)=x$>~&`r(L-zpSE!iQupj+q63{2hw6NK6!SuJ66YcMR+HxDfb%}b2G4(Kfq zRO5)~VmLBAkD2|?wpi!1_=YfD#cdp08Yds9eNdfjkhitr@B!z9`N=Xfg+o+VaBWck z$9~O>GhtTyf?XUjEMbkYiJQ8s4oD_&R%Eplo~d5yaXIo;f#xQ*8tyfntK6R1OJCmJ z^y+8OlU+YQ{#_8&S9p-Yf;GlWv5);~)u(fI3%Ge2>Kiv7kTH^tom3-#0lo7j}D`T9ds~*$pr5+4%?6V9j&NxkPdZqZlPw7bN z<|~B>C+{6(vW)fq_RqBV`s5c(@+-^tK09#w*q+d?Ey-4q3V~f~81fnTWj=dMo^XEC z-UHJe_(PX6gqYs@AK}?-UU|tXIQURPW~4&>GknFWlK*i5P8WGdZ?|medB}gZzrdo3_TRIWW(+GTB{=1 zcL;eX$h??~t!J@&Y$I(zl1pq*V;eY8@KugfmWja({oWbfKP zK@OMBFifahRr%(5+1H8%cISjFe{Gf7Y`Atyp3iwEeb%dzuk*M!N*~)XV~NjHhPz$j zvor0t-}|tFi{EGAnKVYPiD_CL)z|!gS89F|sW4*W_gQ>Kjr)u7(dF+uS+7pZ{2}^b zX6AqH59^jDWpkfm*r0r!`_N{#t7kd1N0vFW}ET8r}_xApYJCyxA-Hn(+FRi!atT%b{efQ7zQx5-kZzvAyYF7~5@qES+ z$FgfCOGP>vXDsFCS#slp>)p09><6mVp2}>vUsWkxAv(h_t-X=IkzXjGh}%NoV-g#G z&xQMpZ?<&aP>$caVy-dU=X0{#?^fTpP-|Dp^SS0{e__cNO5-83 zD#MVy)Z?&XTz}npmuI0B3=suP%XKRgp0n%oRX6Hyb}YSSd`_pI{n;5N<>$5JoUFA*P**k0E4|rYu>Cm-e^*Qci=i1gy|E_&z#!=>Xd+VlJy9rFR_SrFu zbDmaBInNK%$(FUj6)dSsOR|D}j#@evR6Lk=dd7}x%a;awbClVNe0GdrJsopvUD`LB zeP_}R_8A6!D`uI?zh2F$RGDL~z_$6t?z-iA0j4fKMORyjpX%6F-h2G(dF+3Fh8kso z`!cobZs8t{Xz`fO^Mi+h;WG~dgC(g;{o~Ve^2<|;iot^tpLuvj_aKk1BN$yrFuIOl zbREIyI)c%41fzS9NB1C)F8Loy>j;?SAcGQbW^D93Y#`8P@BU^FSCG--eGV!rY;0Br zADBAU2C1LV>5+AO#c}G&*3Fd+M-Iy{mpx^>z40;=b7#s=so(qW&3*sqewflB!})G2 zcM4W^^=x%`zJImR)OR8lJ1=OcpJV9ZIA$5gtmMSIcJlAF|7I=!YiF?h>yy(Ab+U)j zwYNWxzJ34ev90%`f1a5vZun=xCkGZY(<`;-vv1tm$5Ed&*@^l20k((=&f=`=?>Hlv zv>ymo6nAI2>~ho0bvY-!wCm$JtE`C1iy75&XL5_PZ?f%)sl0gN*l)$6X8wgPTRz9J zP1-84f4av)6`o4rz;%CB&K#XA)synw=3%0t9cy1&`H7B=72lJ@xL3dZ?zOz%lEq(V z&cZixm)kz<^txuKx@}M1u?N{1#s6ZSyCf{I`ZcxS=`&-qEvjxyo~=m!+<57E_)iCp z_@`R=Q!eE_<~qB1w!h`SK-u*%m3J4bHMUfdQHOlhED#Tr_5%H-9I+wTLv6r z|MRzoM_$8dLCX2_51AKFQwY0!++dNG?S1*#{8R4mui@V#f1&*jzYgCX`58YB9ARJd z<5cYIo2fq--X-T;?}%You2wK@!)>_JN>A7(H5cyaSW@lB#1xJs5ikeS)NUMTz3k?03f8Q%N; z@BE?V+{rfY_x3xiZYm$^>umnZFx2=~_`gK39zP^6$;*5(6r+=D0d4F!r z(!Z|`yp~*l``Dpla{EqJ7#qfY3H$x_VELVL+4qa@^ZP%kQK;AWpYe0!*We4we@~X2 zJo!Hh^Ni1bl8tpiX9g)Gdj$D1FjT2BFf=qXF#P<_z|ioLfuYoZf#FpG1B2BJ1_tr` zN%2SB7#NtnVErjue`!a7w)tECFt5vw&d!v^QbAuS zsX7NPndyRk9WP{DnKXBKcuZXJQ2xRa4^vH}*K4kqKeu(xc%bsS?EAO7KX2Tedp+5u zWp|ZOa$bY<+jCD3-s&{GePE&ewH?k6nIACBU_RHd`pAreTRIMUO~LCVcUEwn$^Krg zcS(Q4ABG#uyAHS~=pNY5R;0lC%Wq-?|C#4XxetUl1SlpVp&OflOZ@I&L{&Q(FKg2w_@VdM~^+D~yFOsESr9SXhaK zZSi)Vl2z{-!y>m-bLsu$Vw-+;a?BG~**`O0>weMs;*R|Knq6AkYS8TQV++mrrs`#)jcc_J>!N4G}VXukW$ z#v?QG^o)+EAM-hqoVZ}(&8`1`t=zZtjYZXC=GyYu{e1Us>{r=jH2?lI{*Z^WFF*OL zAbf7cYWEL2Po#;=?>PHBwMOAb-P_%^ZT==npDRV>Kg=tBu!(Pe#vQBq{dWB|4;nu* za^K$mkU4mHvi*Gjlk8>7!V(VsZ}W?_3+(1^`9EXTE9U>*{Ab**u-jdE$js~i+3^7T zykFuTNoT};?`L*CZH`IduZU|p-=()9!fwOV?lm@_8r|(a82n%;SYNY1^37$ZNYm5S ze%zggVi`ND<{y5&eN&dytXbkE<RBGW&35_MM=$w^^CEx@V>SU}By*rR(^o%E(#;vG{KO9jr;O znJ+*1&%+=kKP%7dchD@{ea{Mwb0T_t3=Eo-^gSU9j5YbLjrKi9`<|nH&(Xf;Xy0?R z?>XA{9PN9K&gG5HBFF77f8kdUy_z9%GN;K&vCm|sO95AX zlIW~C#|oI7n-1ip3LlwRs;a`VDfAEX+9M`kJK7)c)|$_qJ9DqK+08elWpU;A&iwv2 z!_no&^S6mJGq}GW`g6CFgFR*4v7PG*`z~FzxE{Jq{`F?|XT8O-dXxR!Z;2bd`*L&k zw(DkgDeo2c0CPjo+AJaV3W;&+D`6R+<7chRaeuq;>a z@{akv*Z*IiwPy0iGPZZup5|}kez2l2&vM$zlNDDju4m7m@RZ4dY12nPmqTB+9pE%z z7PCrycsHp29*@%MiA~v!lO0p8S%k~apRwtwNXJocLsPqV8%~5)WeO~<;NLE{?u*({ z=HrE1&Civl{q?$D*i$cD&%B4(j`4ou>SLDgeEqrpcir4}pzJh{{-19jR29x1p2`04 zj$?}4Ri{4(MPzc%*hu;0FQ|O|@Y3Oxp6`~o7xRCgd0s?#UbX3^SYJS)jogt zzS8dLg*A~0`(_%2zKYVmmo9ijj#o+l&~A>b{;7=T84TZwAGrEZ;_Wg$)j2!f_VPVp zvhZ)Qy}-YpWgnw|V|)X1oNDIhdXJ6&_VFDRe-~?a?d@UvoUapO4EX$M+iG<_+*bQF1Kv*Q+4fPGo$dVILmRW_%znb|wC5}H){pJeQg{S=U;eB* z!yhr})Q`rmE95<1RyKA0@0T}lF^oK4{#BTLm)-5xm3!qqes{)x-D6aD#Xse1jQxzO zpC`#{{OnvLukrGy$Rw}89l{fN?H3qqT3xbQz?RuA`*!%%&+P|HKcom$%2l_{DebRR zc+S45%I?lv?PGr&uKA1nb#OXwA7MNHknrTA8fwa}P5y6{x68VGtT|A?a-iSwNl^CD zPjwgipZs*Fm~NB3;%f2fqHFv<7V-*PkAzmq8O+{zua?J_|GZb-0revp+V&Hya#Z|pgP8wRc>|Yi@%9PxPBZy@f6tzDMx1R&y?w&9 zE%Fb$qU<+LmY2BtxuWvhJN7bL`HlpuyZmpe>?TOuaJ&CxV~*R~TY^ffZnfQ1zI7q& z3$vpG+r%yN!gHd|Z1oaL?3LG;D|_XQXz=&>j10#O4$kZTI~{Z=G=rzBpUXO@gr)#* zW)?061`ZB}zI@$#t?ShVWK}WlwL#YeODhla>9+}~v5fUzPtUmq3qIIhRUagaSajdDyV0}=O z%4TnK!CO<=ShyEHPzoqe^jgX+m?_Sv*zNQ(X@fxQ%2J1dz=}Yv*gK2|b)v0qN=}%x zZj0{IlFxS^K6`dnB|quoA%pjOf1kTw{_f|#>a%xmp4T|@k98h1_W@hmIA*U0+7J96 z*cBMh&S$#3Z8mf3O_>6vd+gt}Z`wG7eB!W}=`;IZ`5E?d?K4)v$vpO~_t@eXm>0<2 z(L6KFNZsmyW`b(rl_`ymLWR;dJbKnMyGtwgOg~`wAbKg+0-ZA)@4~q@FFlmwdH(kS z)&<;qSgtkdO1@G16n*N5rsRI+HSd40wBEe@piZZ11;_8Mf*G89ceBi46*IkmwPv?L zH2*ty?#(g(bRP&6sO>m!ZuGeM$PZc9$!<47vMyIe_&n2hdwf9l@AFS`z2a%Jdm}Qm z%v4G^E5aos7^gKBM|wSum>B%L;cSTAj_{N6jCo>d`i~lHO365PbTz zQ&Nh+o!g$*wr#mmH@&z!vqkh%K2!bbeYbiuVis%n=dW52mm*je8gtvNVE(jV=bsXZ zdh&PpS-&4XxR-gt`lddvr_~R#W93=wE|)G(x|pMSeQ)8Xiy2m{rae@Cu=~snlMg&Q zLO<4RH_+zZJndcmROP)U&IdLhdK0$o%IQ6Vx{Tp&E)`N4jQkBZbzWt>Nq-=I^^lX6 z{2hPsv}mI{EVB;IO_V=j9rGn%d#-4Z%=M<YiXE6Ic zv9$}lBXx1@d1iaI@AJYAeRZj1kqf?7CuN#ndcmMnZO8Y?sS+3U^zJV|5c}XX4{!My z|ErDnGEAiXmz0G5S1qvonUefGH^^>+h_I#G6-nWbjFS1UXO~?{;z7QNNM=iI||%B~2@YiNHlX8pr(XWI4EoN@7?mm|9))^QZ?==FKdWT3p< zPgD4K>Yslae}i{F+}f4NTQS2$ruSUN;+K*u_fF4oI$ii{`IA(i(|pW9CX20R2JX5& z>wdu7H75hvm!I!~1sLs_4Q^NB`OXaM)FKF8pKTH@%t%m+XEXC`u|y)KctAsd><&bV7L# zZ;jTL>SfRSwSOGY_*q=^>g&fwkxB8~@*MAu#IOC_xax21+|P}xKDS0aXKuZ@_QlRc zTB4!*AJruIKj+?ho}Ftm`@veXP12I@RIQ)2?wzMElD7U!&4O(;iO2SxXD_K;q*Pqk zGxh%IXH!ztCu_at*8JT3y2N_E{cXkk-?r=SzyFcXP*JmjtyPjs68B8D+bfcFgl1ZI*2ejh%h>cFr)?%mcs@S zd^2Q2MUL02r#y69qrjmzt3i8V>w%tFk;`nmG(_fIixm#oRdCr=eCdu256J%e)8-}{q=4I2{XRGJN|Rm z^T+k;c3N9kdKt5uHJB9y9A3}-_<-O8%Lh6J)6Uj2?J+oUPM&#{Lk(*L+lsro9G+(R z2d+FYyJgFg$9(vJtKi%-(|j2>M;R~Ke&ydH#|gDtcTY`wePCXr+!^f$HE-?M?l9e3 zbywYc^UIu#e_ylj>L_9MAzE^S`)mFt6{ zK^p6ptnaINH_mlRGs(z_{gWCl@oHtmA${AaU%gzG-elVlofypGcYfMFSzK3R($!fe(_X>9~y>Xx-YId7Z z+Vd~A!nzDGtaU8=I0Oqk3hNmouD-uJYk%M@*`SK1-R1X~mOcKTv+ly~D+br~5(4)x z=UTC;Hum}Nqs@KKSLP#(#L$y3b(_-ajipX|7K2ays4haq-sQ_a;;%nZB{R zzy6x-nun|*mo{9AxU}U~-o1aiasf@A53j$oxO(~8r?)Ht)9#30&d;2->F$(IwM%zy ztoxQ9b>Z-qwM5 zE&C$p==;`g&eK^ZmlfUNVp-ncny{ZGlX-t<=BjDAQ*TtJ9u?mDyZfNO(z+Y-TRfUf zratzT{LCkDQFx}kz~`2ZK4$wI_N+zrIa(*|@P5{ch|da3uQ1i=Cny&mSXQ&4)31u%SjRxJyV4|vv$&w3EAuoKV zrtxQgkCpsW#Tl{sZa=G{+pmHhbL=|ziTcmUW3%9T!<%lwC)4@t(lJrSs|)J_wmjQ) z$=}aHYjfx%1J#AM4ZXW=WKXP2y29XeI$`g+FYHY_gC0MaR1nDLnR$&XwM5mman`vq zt>XvG4}8D5@#~WLVbA{l&fqv0wjyWUpMveuav2GA3_n&#|E(!aGB_f<#>4sg70r`d zf6u#=9Jy|D9WprD3Ka9Sq^mw$@?~voQ4kG&X`$2J&`O5%;Ob(XbZ5#kF>6&wUCGiUoV zxi_XaxXIkP{LP7FqSyv58Fo1)xq_a_7glfk&h^0~WU9g?)@^+$vyQKrHDf-zTT z6;|Rdw;Y$`2CqJlm>{=_ z7B6%P7@;vHA~ATaqJmlC(;oJFf<6r4U_@nJLP40n&) z?){pdEee<-Ttinfg!;aZ4-t6#w~Ccp)I>XC!_L#9TeJgy-}kxyZ~k&7tq2+2AJ@Jg zxbOHx`N8fLyYxGBZsfC9oIPjL`#0_2lDjS;a|;4D9n~?IdhM*FdgJBA+g`Bl;Qf=T z_DgG~ZO0E^*@WqP=kqB|ecCs3Mb*igCzaxDEEDgaJ*m$+F)pTm-`|~)w^)N$+Ma$F zd$%goad}hjOAjrH)eFKMQ;SPq_oNukm02q96c~Eu;jzp054uVE(OiONE4?UoD z`lD^}9_|C1eqPinxbrOgy8FAOEbdpYGJO{Npt#|3$NRmrOLvCbJaFP#FkOA&ZrO4X zg`XZ_yw-uGMjcypoXc`@KjrO{zcHcp&X(%@WjfPSPtE&V_I7(}b+u@T*L{BOuQeM3 zz1Dj+99ht`Zt3H7hOcHQ*jDFXTXH|s``}v@kIZ1>YR4Ue8{bkw?mp3Fh$(eCd+WVV+F3@M z3+ERdezn!#PVY@rdv#eM;-)ix#s@z-U!V+7RuCTAv`!emU z__2oVlah~FUy%3Y3k+Y++Z4;z$GH29QhLvgM0US2sn4Z@V=a}T}CC} z>e$b}2kU1ZKJ)bV_hnmCPxIB8%AR}ec6IH|zYgcGPBvV;`-#l0%klk9ccL5D&3wPg zvPZsMxcI@EzrTJraz2nv&{?Gy*{gXmp5>mt(XUy>%_W`1!rvJW*Bmp@bEL#B>DR7*SN~t&(82nX0ytfn1%cGA2u?V z*-N$-;F}szMNli{hH_Y zXA{m$Q`jVVBi0~UkU!$j@An@olsbIeuLd(WEGhX`D4?s$G~0Hb@w59@3QPvlE_G+; z-f+_A+!Xevr>1)z=TrY&!&QDyd$W?{Letw0S_-GEvY%eAvr}H=0IR6ZwNjg%@1OX3 zeww?F|HtpfGmY1|VhgSIhvbCGJeVWRmSDTL@x8Yc-{+|7`>sZ&-&lL2t#ar6*S~(t z{^w&*@oBs~?Mq1w?xB=yp1rfS@h~uK`F|6?_VYWl_Rs7;-NxJYdY*-yrR5|0 zqQ5WyEFV0l>@nVf45hr8Ve5CqK%jMhXHoy%X?&WQf^!<$xTZ`F2rOVY*Ki@|VvxH+ zpz0+rk=sQqc?nYKQ@^U6v#39OT`P&1xn%2EFWzTCge#%zz=y4yTU-s>= z{#`N_;W`V1_fKbCdBSXJ&VrEoigKOl>wGReFpX5$rk}MxIHA2Pc;XuVxYI592b2pk z->;r_<(+x$hdA*c5^Em)lWW=1Jl%dNyG@}%wCSA|rdde}FCX7Fj1BO!TqxY~H|E0e z>!xQnBq!Wa(GvOk=hW6yvK3jjAA-^yk3RWd^)Mw*4}XQ?`DgzY?s~t-aLtDElkIj3 zRWj*&l$2a$?v)EzIdh-zR@O&m+_z3-yb@zyQ+4YzxX^OAFg6QnN$SuXF*yXDhpXK1uDG};*&?F@}}hDJL>qn)AA&d}(Q;60>ph(KAPC8Rk3kOuJj- zyjbjVo=*#-;~dt=51OZ*Y`Jo+7+y1sbTzx`2bw*FWwxby41-aqPQ zQWM2iG{}B1Ny`s9=v>_SVEHVEshi9@9o8<&FmC=X^@sa|j78w@5~jun+b5_rG2ba| z`hMW_l)0Q&c7B~`q7yX*cy=m+1G1FJV0nJAU6VL9qi z(yH}}%jI{o^QH!CS*D*V2<>!9GW{VB#N!xEES$8YiE- zrFE&rS-F-?ZfBuW`a!LxCqg?eS+4Hg=@*jZQ22K5{mU%*e_YOZn9FA_&~|0|z*6zr z;>=Cn_^qAtOvjy-4hpeM`zp2Lz0ySmb5`}m(k1zHb?E^ z4v%9^=1Q~A-THiqC2VK?8HYW#FSul~);#<4Q}K&~;QZgU2I2f$me#!My}Yw(!KIx+ zVlh7aZ?1TnaZh{I=(eQHJ89X6O65&67qb5f;Pm=YW8JhWg#S(M;T*9yYu^6-?xu3u z?gVS_&zelzfHTkAFEG2-?CX%raLL-gZYPA*^}gPlck`0XCw>a}A8@xndNIT|VVa2e zLOU%nmG(=_yEZ*vx8L=vL)+i7ZE_r6+dny8saxZ6tawhb_B}Vf*+;i$J+}1wRJr0} z(N~A%?Dqr29$TK<~2{sZ0R?(E%yJ-Wc_82GK4MsJ99-g?-%bAJMJ?xq<#OkcI{sCm$uYAtWb+#^9X)0w|3eLo;B zox*Z&_f;O#$GSh_RGySQxN^Y!fc6944+p~vrsia>GkLuH@hQuPyajwQ4EGz$^H{^5 z1uWgfG5_6xxo3YEf8hR+dA>RN;If1o!?+p+BX-G#>j&Il*RcIz*ysCT=j$D8^R1ce z51)J(r`Ms|{UNrfzqnRz+4pO0aVk&NC5PCq5Vm_+)*wFj{<(u|6O#L;@Y>WjPZu+q z^)BaRg?^D_sQuPQjfShXO$-c?-Ft9_Ra##{xJ1&HSMyK1e>i{PW1#wl?ijmSat3Xy zr``>!>FaBL{&_{!E4G|#%xN3?!ml<8b>B;KJpJL>uR?)QgN`q za3f()*L$srx!g5#7hcyA-}1m(e`y-qtf!xZ#7!QjAIey_&jL z)q!6lZ_TTT4Tx=@t zysiFyf9CC(bN~PEU7>aTXXY=piiv_7q>itBRo2@Ww`WyZ9G@GXo9y@F#RlhEO}#(H zb|*CQos*TZv@q`Oua=Q~I&bNZoevT!)GfBA&pq+hZT`nAf*UTifmV86k9 zkKsLIrg3NX$J(8bTw_*S<@zj&Y1nhZ@&n@xw#WPp>kk+^FuwHIBDwhD%#^eG2e>v| zTIHwMS-$CO;qAv2BKu6AdAaU6U$ihM>S}YJ?5^Bxci(*%EI96W=D|zOr+p-<(iYWAa-HN0dM_J?}CJ?1e4|@Yj90I7pbmm*YZA? z|CJAwigwy>t6n>N*yA

Vw%%cKVajoIjjhRI^XS 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ν!*!u!P4|>lZcCUnf>xE>Id*ns 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

w>7nwKnmu%fV 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

-hOt5XEK{r&crO-um7--xy=5;seSR1X}xxGC${c={cm3bJC_Exw~zGNKxRAP z3Yi0KtxlUOmj7-P+VA_rJSb*`1#?0o;k*Ynfdef&RuH=b#{&J|l|wLc^$Oy?|!G*%$qN`DgjyIc1OW4rD0h%?w+=BL)Jk`#X#J z?@r^>%oLo{(8e`oazJ1K!?}hFK^KGE6#`W+d5PREV#!O8N}u{w?VLsZX(gS=lWMVWwMwrK}8 zah#cFR&g>mQ*#!C)K`@2Okd}7 z;elzS!Z!V^{lN+CWx*5I@W-8Q$v>c6kokV~v@7q-Yd^$^|BzVo=$~B6mged9OWAD- z4Wdo&v@p#|Qh53Jwqb05pXEZ~mcKCUNe4gRuVlq=b&lTfpG3GRSQiAfg;ffoC%Ka6wY_L8>X6G&+jjdq4cj~qWf+8G);ogt<&SZ8Q=mx!ZC+y3A)%sNs! z5*hoOO5C~Ql#JyQ5|o&Yn~$D3Qp+&kk!RZ78t27gm-BpD7#-)ZMt;yd^<>MHYbAGg zRbM#Jl=dR&+|%{NtN!heTC?@XV!@qX@AdvsHD&2yt4D_L=&Yzm8|{D^D;k_ z|C&9$!F*MaO5a`g2SPvit{hms(a1!pbPdZ(4msv31mTi;c z_}c!-@k-qqmt)0qinZ^#>CHa6J?pWh->1qI7mL0+EN8zTAokeu+`jwg|JYqPf0fz0 zMt;UR_O@l8PX#Vg|M$AF-iQCp!_Pab7_#5aU*5Ls+cU3uQf5oPsco_UZzk(6dz2w; z;oq4nvU$IFpV)DqnIY}_x3z2cn!m)}O*7~8bIV$D?aDM>28M|gbkiWqe<$+hjds&U zm;a6~{~cZaJG%ULbouY-^54XIzHe5g8{Tw_k# z&=-ESSt?<}H#-K#ZH)KWZusB#y83vn?E!XIwULU0{e>F|d%E6hP0Z!4nY-}1miU$j z*7{4+*k(QbBqVO~IQ>xOiq<>Hq4Q2n(0yZ6`~6B&N_eXC`K)6Jzi(+39e?&wRqQNZ zP6AVo?a^(&+7~}rE1kc>SHY%L&O`iRZ9>kU{p;1#y{ZoU>RJ8OL4Q;JS^h0A8uf&m zf^7@tKK?v&|J*rpJL*Ji4L-;Ji05B(nfV^SR#if;n#QBuKFf-WPQ3j8@7%2kAF4Di zZamE|@^STN=AE)1R^D3tj6KTKQ8Bsb&YyZ?wlZ+T$DT@s0NQ zMtgjtJ-#8;<9iM3@fl`^I0_uEXMWEeack*T;WAHgowe&Wbxyi;P02&bP{6|8f$QKg zFO_AJE;k6|B)vIgEuf>4AZXwClP&pS-i+IO_HO_EF3H8F;?CRZ-}h(Uo;mmb|K1f^ z*MDaIQmdFKxIyap+E-=0jd6QcmBsP7@wv%+1Z{6m9ydt>K^M&g%HLL!H*aY?)%=Z}HGiDlhW`C^R`N%b9 zwN<2vk1djmFV0LktABuN!=+Vzik;<~z82noTp_a0 z^qH6Ip7TWubE2*`_sQte(Hz6SLJkI#9%a6QH-kuS@dW0S+g6nqADjH|u(Bat! zzI)j^gz;bbP^oCA{kH10!-qXyv#vgv?PRAvDb4xA*+n(`L~I3KeHK~q{4?vt$yZK~}?`SK+${8O&|NvfOS9>HgJsZzByW9Hw+*=t`NcKv!_N&i!}myGPv zs$Xqiv_5^^c;oJlEZcI^Vw~as{Vf@c+5a zT;DDi@#1ftWYvKRyNxWttJxcZy^n`hDZK0v`CR#ipJh+<3i}D)n7w}3EPdPho4NJ< z_w5b&u~iGU*?-7p;A?o_I`8~TS?nFeS6qH>&LXQDt@s!iO!*iX>?!IXLY8it@|_yJ zv3InGINC!T?IDi#5J!86qdmmY9^z;ZakPgxlzNEF3Gg0b26*XaxlBi^fQRC;3)wnv zgwi|}HNwigqgtI^oDL{?Y_Q{r@v`QY7Cg8hUAdFdadmf7>zhPDN0B4m$~Ts7p0x4A z?bN(Y)!R2$Pde6^n*aUT{O|Yn%>4e}I%)0E{D~({6i>RG9@Fje%Yj>hDg4;OqvZ#T z@3HGK>~CED%4d1YN7DzBtu}D!SRL$nw07&w)B}6J@!Yw1-+8(J^`jL(8XH-U9$|aN z!_%95{CK+Y1J`}*cP?loB{0-@tjW#1zfQjQQM=Vw%{0~>oLlag?D*m4H!u0Yl=PaT z(ZX|&-14)U&@9gsE?OM_-hEbge#QUNxiwRzPE?!n?_r(CeBX5T>Q^)NhxsJsy06*l zqO@_}{fpIBQ+FS*e$a3AQNg#_Jc7rD$&dN<0saTmtmx-p$liGUh_9OeSU&jtwhO!y`#`!N?~z%F*h9t-QOiG0-FN${Zl(UlFfJKJ5AjRY z4<=i`N83y-YxcXOwtD`AE*>a|9JU$wk31vWew$tf3Iw~x4m&GXREz*B4CrW?zQCE~BW77CoHWwEg_#QN>EHK(8dcj9mP_)}vWug>+0 z%zp#y)~{xlic`&I>{XKTQ|@rtr6+3r<7T1By!QMjAAaU=%x{0y=y8NWvMz~Kk9k7;Y$U(t45X=1#h$-K8r%%IRw37XoL{jk=GZ`s3LRR^xO zEDB$hVAddhpiIT+dHsebtRh|c*PeaOe9<_?W7UVBAzvE)1$LhA59a@}=TXVp6?QDI z8JXV}UXC%iYRjDO^wlA=Ffe7ufr)Xtj*mWDMy+A`dSFjoa*F&iuj{k(d}pkZ(`dA3 z*69~n%2k!HS|c~~i_76)kNkWA*QgGAF66^wE-EVx*C9!?ktw>kwIsxk51(55imn zL=PW4)KvJ`t5tD_fQkycW>Ue@JjK?7^H!^Hv^lKf3Y%`S@!-+oiRRzl+y8s z)Nki&K0n)AX?@>ZdDW@ZX@%F@gO)I=9b7HXFy~c`F!RzAJU70b&#nlK-OZK3wV>hD zE47O7}ZRtKP@~&8up+45q{wM0nGyS7Z0}iI%{40 z^Vjpj@o8J{8@t!Y-#8#KRmphyuOln%HnOoFIDa7f!BkswN0wz9KQj0qYTRqb9_1O) zm2hkwBmdbOr4QsE_~+j#T_d~HBh~F&Lwv*ZZ!ZlB7I1xB>|!7Mfa^o!*_-YME-O9` z)B5~Du;BhXd&Yeme`~RAVM~~Mz3eRSm+8TGS;fkDr`~UPubdpNE7g5(>IRXOhtF)g zdg1!-vic_>DQt84w@m4ZhavT6N*ErfeegMVs%@!~mg)X;t z|I4G}jZVHFD}^^o9Jm`1^5sRR;~#5VhBMZCyb6CBO$+xc-QLW->qF^sISy;Df}dym zd`~j{Fj+X|+7<`zJA4zp-u;r_5-qYpBI~-Ro9x_kMb>Ym3mCUQDr@e_5VoDr`2Wnx zdp9HaUEa$-_qlznN`c4Ui{GW{;s5K*_nNz}F*ge5+9vEjtuWvEs^|Hd^w*5~uaD1u z_kQohIiDN3uQDH;GS{2`NQupk{rr*dbe=OC+I??$bolfY=9nFF9GayL{0t@Y_)}gq zn*Px+`gddHBYo)u3$IR_en;1}FX&jKIH&D}?(P8gWE z$<#U9mU1&N6mT;z7!W^Wn_iSyTns*Hw1B(+%joGkqo?Z}af(hn6<+%<{ZRaY@V_}f z>ummf|N3CxBb7#@*hY7j9}fZSiFkM8UIX12R;%CI5JjoJgCUr$9_i>2NZ2h5l(1S&2v@bl`7ar{k zkM@N}`@*Ar;nBYEQ0faa%!c%Z-(2zLYf#{Ey=eEii%0L8>B)Hoi(EKWcve~Lm}{z7 z*&aVNXwyW~n-6Ba_ni>+I?VX?dNr$gw?a5qU40~bhv~@zrJUCeyBOX%ntHhH`^j{oZ@C+FeI@F4_Rh;iraLw4L>@cH9DU) z+J+x(!;iM%N89kDZTQhP{Ae3~NVVa=Lfh~&4hkMo;Bam9bdQ>p8aT;z8}6@S6%KUi*E zndbk0$)l?U3tJcqWF8nZIL_r4*uH6JE$-fdmzs)7I}Zax6A$UV1ITK(CZ5{S%fv=| z2cx}%(cZym?_jidFxop9?H!Ev4n}(iL#cPbxE9(wn7%Rhuz`Tjp5F3t=Ht=l`7WrCo}&cjCnZP%5bv$avr@SFJk;GZWkCzYf1Z!qLATw_~s zw7Gz)t!JiCyX?@r zEqDCmHC~4LMTdTAt+X_FJSpk{?~1>Mdzt1RSUaWhj*(3C+N0OJ7xUzE9$InMD;B1)F*gQ?(>P9;O&E-RHTn%Z1&_Rd&PvbE4whfp2W;f5io?KXstY z^fp6A?|MdtbBwBT>WxBgarbI$TyyqJe?bVF- zYDRlCqrIBZUd?E)X0%r`q%0~NkvZ)byfmGrtI0GaomJe?-hZ_BjavHx zkKIqr^m`qTWp6l}ZXcJJyTxHGsjRK#%Yte9E%@#EUjo2qmA~z;4nUX5Ul0D!5iRR)p zIssSS?LH9RbIb9!*oITQysE4X{0#e--TKKGAi1OcQuar)o(%Egn@ht=DyRESjpL5m zy`bXbGl!=SL%9|mI==pNVaMkLK8{5fu30nwxO66VKJ%=fMgICbqE6j-exNMw!J%yv z1$3uBTD~>eF@$T;CEsfyTdrw5G;cUIYloShWx#W`hry>bxMbXS2X2ozZt~ZyZ-(kR zioYJ%`^K94&kc9&bC>;4<8BfybFgJk zeLl;o?~@)WH`EIsxX+;B$FO#(!JR7XeVnw}&TbLc9adlFW?(o++SUxj`ReDm4M&%C zjdpQHyEvm=oY5}MXcuR+i!<8A8SUbXc5#MM7l+{|w2L$2An#!X9@qV=doD5-wF^Hs z@ZghO^7zXX#$CPDo=Y^E5~R0&Nl@W)sXTV**>mk9XCy3_UE`jp!8qgl*HeG4-)rDm^PYh(ENo<_vX_~!|e9Af|I zzmA>tWxG|uis!Y~r`k8l>-}8GHZNPeQ_NE-D^A;pF~a2frlYGx*G+4gx|~6UVYB07 z0vrS1)zP{RgqhL~+2~V5) zyl=mC1O%UaIq;zI@2b6%XN&DoRyuLflAWQP!JeUt;r_#$ZYGNje106^d(b#ogwb7!!*?zx7LAUtLQ(qTz{a@K~RKEH7 z+lhBX^ruLg?RljCs@A^TkIY4TK_upL)oA2G^-qb z&ljI;;mXevpX+k+nv{s8(6`mUL4*IcZ4<1 zU+V4iWBUunnTve+w|s6av%7GP{npQh)Ian=8~Ukr-D zmo}E1yD@#?B*FBTDzS4|4sr5%zkl~#W$wIr|Lg)oA0@e-YZP9-E5(7+W_P?fdsldsb+Lr7*@Y?Qf_* zkiNVs!}T9Sd~ov%mV12sGEA;^JPGe^rytnc|H%Kq#9-UC9e;HA=X`t1W@x~kr+nKv z!}{t{(a5i^``_D~WcFS#eYypIgkZzLN`+^&&lAtSjBWB!+WC2z@`IBr>#}`ZLTj`H z|9)%y!u&(pGVb@y7?EQaj!mD`)0kY}`ps%;hgZ#&xc4fHoZ4>3S*n}X9N+fzu-=TX zE|x13KJ8XsQ#`SJ_r`5Z@5-7!tUcXQt~dFL*(Rmy&a&G+$$Rs=)ZKIE|4}8pU)=UV zh_VCMX-uJ&nR4He(bs7-i^Uwv)&!P*6%T4j<@}LhKxeJFH?MNm)=^TBD?VE z3X_8;vwAuMCLHsgIKyITcd^`oIsg7|w3_}jnD4_U!(+?&8Tajq=ZuI>?ms2$VCc4` zCEW4jDuZ>CmYe*X_uQr7sDpRia^=&{(@TZU-FlL=?&hR9yJa%Y_Am&PoK`vM9e9R+ z-7OaFDKXLe*M2%%>JwMJ`jVBniPeFYUWubuxNI5k&)TT#zA*Ot!R!5A?HoIvew=gQ z;)U7YIlsTJc;Ig_;dFLj+R}|y>_RU~GMBF@i|#p?wWI9UMCPRj$_(_qJ_wxp@~&=D zTE^4HQycWN4rf;^xbgeyr^7Y@?<>M8%bsO^|D^umK%aJ2WheU~PaB6ta(QVny7s4- zkKC}D%g)8~S$0KYWy6w5H7#AL=UlX3MsZ1qeURZdXsdneQoeV_n|-E5$1a(_*|^6{ z@8f}t`vy-M+CJ(`ta!uR_Q?48qRmGoK7AJu_+q7)IbC?sho!v@+XQ!J8Skn1wz5*x z|0yH0xy_q7XPL`>Ui?=#Mg3lx-GZl#1%LlW+7!I}z#uvEfsNdTwx28Zgq)8rviopF zy6+wPO7kBK1?heNSJ-Tr!{gMmcY`k4v^JwGow@f3CrQ@4b8T{_`-jxDfkFx*7g63&F3=9)_NV*;+Bfqpb6+8(wktc7o6E)h28tp`lcA`c*QKOxx z(N5H8Cu+14HQI?9N}VXiJ&;b+n;Cok4jTv@uV42`Xsv*>bo8aINvR8x*g6;X2OK$Z zZE6?eN?{vjEoJ7WJPl=GWoP%egF50n+G=+E%y9hV+j;)mfAQ^h^Up_QpWJ+6#*xfN zbLv+4t8$-eX;ePocI-)`OE2S+7U!q47}l#QTxH~Hx%`cLiJpV|k+ZYBEg9P!-lo3T zkouWHPC?^i^w;(0H}BwOn0@Q}XQl~>uWDIjmMdi%{gbPhY-!B6<%8q})^&~13Cs3{ zHP2zPO_{K1rA%7-~^Ez|s1u=jD!`uV#3?BAIFm79U#12<{Q?Ld8;_~P8eoE&gZ z=L0wY=xGC^J)O~>&S+0(w5K!L(;4mQjP`U!dpe^%ogvlJk%snkrup(U83?$=Pvp*c z!?!BN$A^?>R7mC@(7nL@K+d*VyJPx>JcZ0zpDrgIN}FXenXkqt*QrhDpyP}dv){U+Ifn}6Rurt z;ZDf@IqT~l^?BQ`-8*nH>U;xl0sE?X$zn@Fga7v_tH~W=#s?>VK9JAQEzEUovK#kN z+#QJuLGKVB9tH+?9tH+I687zsmVi4D?mRn2r%FaU5ThN4(GJ9D2V%4XG1`F`?Ldrn zAVxb7L#hK&1nWTT^Mmx zn5X!OVGqMvr)lF4Kl+)tCI&Cy{v!I$dLrwB zg-*H;PG=PToVBt>EVq!xj@Cv$m|K6M88x$|;KaWOD3@OpZ<1Tio$C@?TE7;vyLFfbIXsmNzwVBjq9 zh%9Dc;1&j9Muu5)Bp4XD{4<@M0|GMgOY(~|@(UC^ot+g5it^Jkb5a=?DsIgUP7euw zDQ5fq-WMnDj)p1T`9Y!_3PDcY4I&fOv=li7ygC#Y-V_ZyDCp|SB7I^(2XkLvLyu^n z4o?@?4fZFBi~5=ucXXZl{jhp*_O(6VpPl`;^}5~jHJ|5hKF@xDgJ+tDaiD1f&q^mb z!3WL9j~sjWM9-m-OQnE8fMautV)0*PMus1CHa6Ny2iW&9FjidV=3qGBJNtlvO#6TS zS57ly7#ms`50p%_@aAY3?VgNwPe!{ZqurCy?#XEPWVCxS+C3TVo(!e#3DXHk_hd=L z?TN1qI9PkFyKZ}UT2Hsu%)DLT|HfT~UA}Qb(%Fi#4ty{tEfq#0@c zx&A@rnz=CRK_QKV``N)sDrf5M&wP7w@7jtGHwBAL$Ljb+H%z-0ylL9xrJD1mwrAb` zlEdXpLKQ8{omm0yn3Hp&2eudh^zhP(b zUw!B3=iOhTTb@Vk4BL>?8XP zu1@iLu(N!Lz3`Xx(mOM^rmPJWxaiMq_2PcZze!!u3N=&g=6+E4wfxd{1&f4|{+8r< z>0k1y_Zh_{YtGX>yZGZC9kaP5+x<$izcxK|{9>d~%qY$6HuLLSmKc*WEzzttPrL(y z^fEf88hGiRDPDj6{><|Vyf5T$@aM!kWJRePRn4w^di&=+&*M)o%}r`-f4~e$#_@HUD{QuL%BKuTi>O-s3&{rM`^PeepZw zGj!+g_|9x0S=sDiCO9)$Jm28ss{>D0l^J~ZR4~}3J?Y|~8)w_J53e|$QWo(wLyzl> zfzi54EZdgnUHs3+@Mem?ZtDqt1Kgdyq|bhz7V$7J%;h2JaBNVgFE1at+sBhMIx98W z?Hld(jduG+yM3eGzR_;qXt!^)+c(!-So3~if>tWqhPo##Km4|DtW#mB zS$=2ct*3VjlEgC~|J}1`iToseFUk8oD~~ZQ@GudXl*+attbNnlEw`-f&R+<)+iI?L z@!b}y^sa65Id({<$JSsUq`j zBkL?7x6G+W-^+A_t@q*m@ZnD5mU;1S*{?|C?Qh@l$&^R@*!J`beTJ)^JK1ljsMsvR zEn@t2PNF!2KDT_H7ju6!Hm_sShEf4*0tYV^B~_x#*zyOO-m|L@&y$?v&L{=%ey z$M5o<2OZCOs68>ZcZ_4%%%iwtCo~ISnd0AYTbdqb_MD76Moq#a9%97td(JC*w4t|np`@qa`C<0*t2{>dw95>oW?m%acvQ^M5e@94tj?wXG$71n(#mPe1?Hx!9-EP z2hE@VR-EHockY~7-!{H@-B&4ux4w>{kKT< z9ZNh!QcZv@&i<9v2ZsK{}9^PVG9vW_UECW$f|d0uGxASZP~O^elhbJ4}K zI=f#Ua-FWn#J{QF{Vu(hpZsi&8Cl${Cm;3wSgd+hfn|-9U{{A`U_k%u;^pai@2fxG zYpHtpvF>j1d&}qZnh_;EZ}(m?bL$R;qLdVC394tiAqk0a{iy6T5z#$hQhY*Ty9pX|GRh& z)fzUJCGklbOt-#%cJAuM+jPp;iQn6M!l7Sv-gb^N%()MAKI|5^k$cHP;z zpm%DU^w$O^jgYsVzZNvM-q{)P_jgr(G?RXjsukPmFYdcCHEj!AXU^Wzc;msI69Mu0 zum4`~i99*?wMx$i{k`c9UHfFSH&0Dc$eCxo{jFErk5eJWi0kF`Ro!td?lIfipYAFOFhntk_!(srqwI?f+= zIyuzr${WSM{_LL67J6yjU6ZLv(gqurI^8Qa{cynOL)*UltnXfYR(t|Fib} zXZVxzu|-_cH{eVI`?jE;@&DJRZU}2-*O_-hiB0NZ<0|b#%u_2CEzOemvE0m?z+16Y zCG`3sVMW!|)_e_lx;777zcuVPz4UciPRNWF7fCq|v(G9Y541I1RjC#D;rXaByz%sZ zb2*K)qs;1p*XDPzFY}0RV^8{(?%&PsB*Ht(dVTu-@Fw=8Uxz2~o}4Rvwt?MX$NO30 zJJ)=P^tIVwB7U4f-{{Kfe7pTqthX;LyJYNh>*L~!_X5miF0W3TJ?EN?CzQ` zD7I*|GyTWI)8pl|Dt1Z!x`h`{Y%-e6IP;{uVeP4a=gtaJGxx-c#$8wbzwOM;2O;eI zHuvN&?D-!TX?OiWif&zWZLN}=$pPl8g$jP_S1t6NnzY>Il95a5M~D5Jtn^mC|8MBI zEU@0;Wsqcvkl@)#=hK(+t2%Trb5DHr>e=VZ4;q;hwqD(^@4c#+HSgcNxBna>Yvq5I z1^+tdQm;_{?ud`nHOB)75A8h>cSU}MbA4h~eL__2`i+Z4{HI)eelC_Z&*uw!!|Zz@ zr|UKqKthtKWx{JkmHx4zl8+9ZEg(Z9W#ol zSP$^oe;n=Bk9O;iI7KI(3a|Z_eklGx_}`qLbvA##e|@m;kxHXcY@<8Nj|Yd&eDPT- zbAA$MOVXWZ{qBFZbN~M%ds*p-XVT6HEs@88LWO-UvwA)oEmi7tnxuJD_0s>8NB4Dp zGuz!aW!R8q$5=g4cSUS6^EYpGfryd=kG?Z7eA=CV@QMZpb3;b+!}I0;-`oFnU!cm+ zkmfn5kbxo2MMW><)MoqR91ILD3mUaHI7)vxD4}zJP39o$orBze63kkhRE`{S5^&;5 zX^<&O&|K5xq|tVEgREA9{uKvVlLLVnZIcW5^bUpY;Il2@|CPuw@1Vw>Mot#T6CA>c zinlr#le!|5`#8jBw*F8&=)od1+N~e$){l1UN4xc--TKjP{b;v-D0SK{wD$zL@*$UkeCwkd! zqkRvxO&S>ue2#zDVPbErJz)D+pI<0Btg-z7`I#}m8PZ(iG;@}!8; z|ITuj#inhS6}flY33g3zW7u?5;+5f#=R(X|kIX1b>JjGcG@N^6#txsJm0N8CyY=t6 zR=(;qEPbH7;pJP|v)Nh74(vO){>*CJJX_-O*L}Q39KTmg$P+Vg$a)*j#NubbTySEO z!l_405rUm1PYWk;x=C>PF@0`&()4}5-s8pDI>40CNW-<-pcz_^3$&5HW;l$bD=iBY$w7HR}Z)O9yn zF>Py5?PuT(-@02%`L>?Gcl~p+cYo(U|YF$cBqR!+s+lq?@}M|&)m;k$51Eo`^I{kWY!zH3nS9a zgR>4~ed+JtunhkI?+Ub*$Tcg_9wr5rC9dl|aR+O9Ir zTYBx6=%v@2{zkrw30}BShwn@4?MwRsE#WlW#VRr5hqqD{>zgIPYbI=+QuNQfA?-+y!i1TD8gW1P zQf~@ah*a4NglPLLIJ9`3A!7vlHYa}0l~rD|=6WA^yYV;UgLP$^=l3c-h)?iS*&1!W zNpB0&?v&o<`Ucx~%Utg3Gwhx4yK(UW<)3!yudKLb*yBzdy*%s2#pDM{ALJ~QI_7!I zyTK4O=}^3f=7|?|4yHZ7>K=#`$nIeIv({p_X72ako1bTAJ^wF~#9+tyJ$lo-`tRKT zc^TSL44>Q8S|wob7=yNUhOO-uyur)BaG95Z!I1c^omr)M={b-C`Y-e9jBe{3UEe!8 z)jirL9_Tn4PHn9~d-`scf!Xj72Ed`ekh3=3PU|Hy^%M|X}?XYAn$JX;^Mm+1UDhO?!$aZ8& zP=R7+!L1r`z5c>kGnXqL-2I{A`~C9sJD=YxK6kt{%`L*~+||&(Q)l?H|J}VxPCf1M zoZwwYcLy*wnm0&&5RqVJGp%7-la*s>s69`4k9Lj9gR}*eyffIP9C+3+Y<3XUU>06E zH|@~w>Hzj_ji#Bk+!9ldG3-9D?^ryOXhI}+U#8#rA0AOv1u^HbNY63fn*!|pBee_WdKVCRDM>3hNxoc{M-=r3(PeLy)gp1p3l!ORbFjZr6e zEit4b={|K!f3V<)=4b4{3`%ks5wW--%^;Bwg?4_KEr2dw|m z@ogy^PxeLiOh?%%^^cTG>v9;sDSq+ZusQ6r(Zg6P$)6Wi8Q)=i$7a5J@g-h6FGlgH zTg_PaY0l4OySZlWgLNMRPfh9Wx_jAc>Bc{%Y}>YMF80>1nEttO4*!!Hh3Cw%&pr!3 zC_B0HZb(eLSl$bxp9eHQNL1)<{(m9-`p&S1?Tta=r`X#XgSVa*vREy9%;@u;_>e1fX|8(-%pDlWIwm%@TwY%lHGw{*T32(&OASUa_Oy4 zjg4kMW!`G|{rs|I-$pSzd&w)c4`yb&CtW(3Ay~n-hke=9^N$(AJK zkLCx8ukwHCZ%kImn|i?NDf9KlwNs8dtu_(f-1LEcSHo2Ar_4pC^S$;5*X7;+(eP^@ z^ZJJJ#*@DfvpSxR!Lq9Rrv-cf7ag*Q9V8M(6i60g5_u2PpZCs<0Ccib}pWTH0{7>F9|9}7c zz@vaZ@N8Dp~iQ8^e=mc zS$>`0>gNbVoIdlc;rg!vS&ObU6SpgK#_1Xy=u}#|*m6I^_S8?h3!)`hccwG%w&Q>C zM!eDbRxN*p@Y>4jMw`ph!pe8oh(^B7o5_?ZbjU5O%-%TL>%tzJfBX+3YWJRbGtEh7 zt-v#R=F;T}i+vc4HvfLNbLHa<#^bNvG4>a`YCoG9U(C(p>*5^66*Do&d*hz(~?QQAH9n!X)+rM(>+JIT1d(QV+onCq1qw5w)y&YG(KFCyXzIm=Vo!Mv3@zbvw z3$I=G`mjge#(iZ)_{~k$KAoP|rne@h+Q>7WRKDImY0(y*^Q&bJM!fFsy0r1bpW4@r zQJnMaMgQ0d&bvOfW1oCt!^Z2^lWh8r#WT)-60qvkw>+M;UIEj&g39*PF|6U|J#1ui z{ZXjn7cZeh?`M6Ey81=!f|t_^OZC-p{5*$?!&SG6gtb57y8Y?hz8x-79~(}-W^8|6 z`u0L`Q~RnV`LAkSV-_vmnSCJr!1SrF8U-u#bSeOhH??X+g=Xy?umbD^D2l5lU*&de`?DIkEXt(l0gsV z_5U}laNb(waL@iiyZq|+ZBsISelB~>_-$Dz{}Xd%S%tG_rakEhpCT-Fey?1?#0gCI zPkubNUammoU*=rQ;IaYr&TKeI- z4O2zA9iQZs91|7$qQ4W6D4Hpi%r8tUcTMKRr7KnDVC@9>x!SaGrM1lS^_As%o z)+Uam|2j36#XHaB8Lj5ynVIrQRsUqQ{{3nSN6(GAzDX_6BPHEq{T=X>-65-&b*f;wK`U6v+S1M zy}5B6?0p&c6SWn7M8N)tN4Lc+p`}&S@}cr!fMe2T|FfqpNp>cKzY=pU)AI(IKWQcGS9o5}J3MjQy4(MJ{xHngmgS>0rRrjE<@5Pn z@0k{d6-+Cgw|3TIZSRN9_8a{9n@*l%kUk)LUu3R;Wktg6u0LW51rCZU_;XysKJGl$ z`1GMGi{QeB&fk3ZnbcTASVN-LC)MtGU&wga>3ILcyW3ap?kk@6z=-Sg#B!I!{ipu$ z+671nXWwftzc%R*`#$}{>ZYuXU~!IdiQae;-4 zs&==(&RO<7**|u0mHe3hk@3M?(Va3|_A<$_J&w`st(IWNvB zthK_jpMOi~gx~*OF{?jrP+xoE?yR|$a#z9|g*HUJlWP@yx!S=0+mFUsCzrpQzwP?Z zp0yFi0Z|iIPpo%ujLST;Bi49ju~B$~W9FIVY&NN)7VEb^*PGAbaq@;p^<%5>vXTiO zykr^9C;eK)9Dn`B`y(z2vxcz7i+ysXWp!~n!a4}hkcDg&vWBP)vq}x_TE3tY3nreFuT|JYrn%% z?r7}4Fopk2ch5g|d56h9yH74#_Omth6Z5`W{@KsOr#)m|x#Y|JC+rR051;sc;y(S1 zJ&8|8+x`OAPlrsNcf88+vP<6c{t=4^d^r2T<_lQn!=3`)}<6~g3B(tXrnH{dmF7({D8tEJ!M3QR%2)RJ0eH{bHGD*fi_z z1djB?>IEIW0udZHmh5oautCVlq*s7LBc`}|*~HXcrcV-PJ1EFBr5|uwGp{xOQ1Oqq zyMF(E8#^_#?(^)K-_!5y`MxjTEo5rpwwNW^cfUJseRu5pn%{;i&K)V#3{nt}WL(DB z^r}fZpRsfS?-nPg>_g^_d>c4oSp05&QBOGgOpf)cX2Tid^RBmco-j_}`ym~1!Zpr% zY2J(vRu_u4RXx?JIapWdap1zcMi;~T_}?RodiEcXA}`+MIO-EH~OaQeXfYxCKj zHRjG{uSmJ}f^qs@)~I7^p0=`I)lLg~b0)m8_eieTT!)zb>HmF$b1retXPe<6vG%9@ z^OV&mcWo}zTj6)p@XYQ1FJqouxOUb$Tj%C2=is&lR#QbelvfCa^veim%vikjm&^U; z1yxcN>0i_v{~nRr_%@p5&osA#UIDRwPOIO^tmD1&YO#avudegVVvN2I($iPg+xmu0 zV3z7+it9A56?w6P_son1+|NGBH!w@TX1lst?9yB();oDgxBX-{uZWy+>|c+pJFmrt zhk76Gw3;x?w0>UvO}FUK%}FzNGS4xMGY@uM%v!^Ai*4HPHwMd|suh@v-z+GOW>qm{ z{coBP8=|1Lpl(_2Go_WHRm=>UQJ>~j{yxdHFFLTXZSKv^CJhhC^H#!nYJAd%%g*cU z;{L-R!}ObvcYo$5`2!zyZ$G>o^Mvn%(2lgqM57gUvMUa(sIa?MwSZ?{|5CXXP0vGL zrhdzAx=}cx>e!|m>d8COEB5o*nB3`~P-U1@9r%H`UiI(lY7%61){|@_ke&u<)KkK;84?X(1@GA4tk9)LgZJO8ux-+lM{Iad}O8)`( z!=GpGJYlxrfMYD9GD+kzN=nd{7t zE95n5_sie@?QrvV?bGj#tG@p(+`}KT>wfss_us?rY%{*Ys%Lp#?pvFUN$tw2goX#N z-ZL+LRC%@P!D$BH&8t3KYF+M(y=OaB%gOD`{#R1DybKH(yd)hM1=$xHpPG^m*&Lg} zJAHI>>}a=kv|BsctsU*wj&^HDyS1a;+R<+9Xt#DKb!(X>!@9K_vqc<5j@1{hXiZqN zs6|99s`}`=FtcCmHzH;n`{XfM&3DZjmW><40?(a$cF9e^XJNu~UlwMrCXS?q6J7}@ zF8OeCkL|N#cjwOB%dK9)eEQv)v)`Yc-DCc~_Ipo6=iav1PmumB^Mh~CW~Kg; z9EP4XC#pOWczA50UCJm0Ii=ER1ikFg0H z5)YD?iVjTfBED7gX~w7e;?5Lu!C1VKvKV9`K)vMBPRY<+#@*Ah)qOleRItMErH01iFs!P zZulNNKZoDsL3o3?on0$`PSiwcZo3T+m<8=7EPY_j$QW32;B;5D<`2KQ3}>ImH`e~0 z=doN+U9dIZsB!lBwX+Y;{r>aLjHMiVPOI;_==b9JhgEfK-j@rSlRb}DotfV-ThymB zL1SLh!_Wr@x@26N(+*}WtF+pBPB7uXzjLMEPp;LHbiK-c;rd&wYJ>^=GmVgFsnEAAMk$A_wL1=DPJ4C>;w<9*D<$$e!`qCUXyU`Yh#q~ zlVfu})vZ7Kd}5~MO#Tw*pEhPi+a58;ecZUsE+Cly0Qd3DPnjqBh3ovAw|tvDGlTTR z&2JvJDsIHyfosrma@)2|uzevf1H&9%1_pDIrzSxiILO509Ny&79^7aTZnOtC+JhVI z!FAmed9V3MQ+k<+&rZYCnCw;4rcaAl?Xfy=wfpP6X6xQApSx_Y>+Zi-S-oBM0|S9hKWw#_^$Y`1yNN!T1UEzGx()jxjwW1w^FVF%}>iBl7&KWv$}^P4!&e@@X3F!HxFdMtg8Wst0!*(t~?4V`HDNp+MXI$fdiRjXYdL zOguyij#~K(IChtqyePT*RCV1#-=oqhvQ~kY?+ecfUh3fFaedM1PK|aZX-m8J>-Ktb z2u%yycKvttvB#3<_lt!-ov`%Lfu{;w5)*HAFc@U~%y>4-`2oj|oY3`mZroUPZ;2Y) zGzVUt+|S?iqIDBo#15P)UEdg;kmMrf)-vTo!XqmYfip9=v-d`yRz;o38nHtiz}AXB+FDG|t}FQm^>Be}taR`XLgv2-iS=4M?3OB99tdb1>;9K!!9D%Bvz$eV+U^rinC1R6Tbw%@&MEY3RZEN7hvTY0 zv>cuGZ=ZOfV~xn<8xrxQ6EAe=`Y!$@_+c&Ye1+xeHm*EHJ9J7b-1AoP;EY6*` z>ZIkap!_THU;UomUXn1sE_=qetq<%jy{|~;x>B&u`RJsYgbR~?&yT*^pI9B9S*a{! zWMY3`)cDi+d;hqLI6P~_{+QZbDA~A?-!7n~|L1|}cf40BIOmnF6#d+|oppMU`tJwK z`9U=c9*ftC*8I0)uAlTb|Aw-%-Md4_%eh16d#_yPt@~h$Nc`3hjjQJ=#<%=8WtgVf zyDq%8QwMvmuUpH>jpxXY+qJw53>9Se`i#LlFDrQ4M|*vvy}r?2-)OIIwAVM<>l>Z7 z8}0Rt_WDM9eM73(w;0mvJ2GQ$-faVciIg^m?o*|Z|9yqw{y>; zKX>+TSLByzbU&Y}F@LrCq!YT)nnmkRR~dCqVC>a<-6&U_9CmJl=cAmw-5Go*(^u-f zo^6!>S#+mipLnU>YD?2rbGL~pjU5U~5eE(hSzh}s_BkSA*`N3GUJ0#HUVPhVL&?TX zM^8#jjZ`_m=j`L^)9-bhPU%K)e>>CQ8l8M5q9De7GE2y6*5wC3H>%lO*SM)Gf1)=s zizi|WtGaCRL}}y8-;~_!KKI2l+H7G|Kg)mZfUo8!Z`;oTs`7;?_d6)L^!=9aN*ws}0qDEo$U752-U!Bnp6Kxac{N#T$PwZ6IUxS$D=h6>a z_c7mL5a;~PeEw6T+N#5!_pgi7alMxJlc{i#q5O$i4jSgqYO^*<-EjB&SAFGg!=%us z(hq{}vulQ_H~JMNm&JMf3+&3-GuOs9zJcf$QFwEjvnEWRWA;`jod zZPQ0_tJKd~ZV47cNZJqu8 zZ#V1WZ(BTnVHC^FU3E<6Ymem%$<3=zEwfEN_UZmLR*^r2T=Hs<=UjG_XPV!rf57&E z#18H`4DyYRPw)8oKF(*Y=DNdv@5zUG3=C6i`sNu1KaknMyN9XmfbRpI8!P5i3z+0+ zKYzCCIO9LV0_F<&N7FepKDfN%Y5W`WUb$ENVeh;arhC)h%=I6?`@m zyJ{au7r3|O@UJjp4(R#*Ij^x{L-q2AI`+DgyX`Kl&)%!}@BEy?bFnsJZH{$04^=dz zU)DtLnY!(;e}VRi{~v5Me2!M^e>BNf_?xxZyhw@XZVPqqiRZX8<#|q+*}C?`@3%Mp zFZ*$$T;|1nGylmFD;c(JxpU~mq0g5j9xhp$t6yBKpt6`Vvf|0>c&SF2&JfwlTjh7i zT`qp0{b6CzyRBj~oD8#C_>aEWaD>0&XYYv*iNW@Tjt9D%S=N1JKH#0OxhQYa#3yU! zUtrnwl{v|4*JoxWzUQ zPFdDSjI@z_G>au*4lS6azCf*`8HF>=LZx-Eq45!m40tI|MHCwiejW!+I8H>*O|zujj;7UIvB>ybKJs6!h&ND>X0h>W_BrMmu+-ox9P_ z-Du}-v~xGwxf|`=jdt!vJ9k5>bN3tCxr9HzWV&-6}wgH3s6R+bdcqbA35Nvi|`nzOPt*3K|Ld;8^|8+&b|W*MAJEz7$< zyYlz5+23n-A33w*blBI@4KBxorskyGTb5$|Y4WKZg-?4-c0PDg#CjlN(K3^p8m22% zqfVsqCa@n^nQV7T=MKwflZglNY!ar*LkCxbqD9^K}7vuDc0 zxEl5Y!kkXuAIDfVSAJu>($rX)$z8I0!j|g{YDV);?EQS{P+j5iKY_jcNAuQgKWb>6 z@a)=o2K7t+OXGiSWE5Lid*uIy)3(eFGjB^w*fKXIsOG_ZHMS`4FOmn6y-RXBmkIu_ zd?DYo;hRVK8TTTgGOH_W{I~KIGrTKSezALL8}xR&YSfAYQT`Jd-@5AjxG;Z)cpSMcKmidD+gCRqV#k1x)^*kEJcgq>>ny{>8Ok=tg%)jl*`mQAzs_rRMHMj2C zyUj)ZkR?Np?B~-?V&5~Pb2NVcauBOqy^Vt*YUvFjS%!)iGoEkpwc6$q&sf9%-7evX z*o~KFUmExn*32q-&(g`3%rs>-tAf|@FC0nFX3Sp5c;}~BPf_}n@GUN#dZ{aB*Du=j zw%ugb;+u;9q(ATcb)M(r{$mXjP6adUFH$l&I&1c?@Y$b!2G|zJ9tg0Le-+>PaGlV? z3H#1}|qap!v7* zK!#f3HD}uo9y#;d{~6^gug^8tUZ}A1=<`)?)=jx^M_PnTYGsp&ljrRJXS00&tS{G?TQ6U@{r^0}&2>sQ{+Tr1 zz}{aJ(sObvix*XV!^^<%oWlMhWR2%@UWw8E;%I+yw7)poUmWc(j`kNv`-`Lf#nJxa zXn%1i^%t3iApONN5sLl7jw1h-MTR#jO0zsuVd2^AsOTZ}MN&v;)4EMkfu$=KGW9C1 z+#Sir>y(7@7&LuAui7u?804w z87!w~Gn{y`!u5Hw=&ITiLd%kmDc3SRIQ964P4S=mEgI|w4}Z1n&rP;o-(lDF%gN{X zjQ{_vC6ufxbPLWtjn)hIz02f%_g-T>%f8yp2fS`cS{^EUd%H;V;vPxggeT%c`ne%4 z`V4nMTs}Xu-^=kiZE5k(s1}`PC%-;=6S07!?HrT0>?ViXuGhcpxaD-N{FJt#^M`f( zOIZ^djjpcPqImkkYo^0Y*OG7SI+WS?-7r@+H_eXySC7?}ziUcPFnwOOm;ZwKm+mWO zYGM3KCIoZYonm~}zV%V-obSI%IO;?=uRZxP+451M|DU#xkIzgj+p1AG@p3W4cZQn& zj(x}aU5?#3#c}1&t{*jA3A#W2$Q)3gZcyl9)_Z^JmjeQJ-#I6z*e-DK_PGUMLw696_3hXVOI3pak*-N;ygVqzL$*B zi=K$s3OK*qmAqc!V@BglfBp)#u&n5{mgQ&nmU(=toNs!etKpr^5_YR?iJOGi9$u+! z+@Mbm+auyvqtP>y*3%mlZ~{tdimzXoj%>-(Tn$7gZkushQ|-m*IR9ZWo`f20Z|Uogui z7%t8f+-J`D{+0oQS>x+lU$qZJAD9!2zxHEDd? zW415X`@^c1>xbM|-ptLPd0_2@{-=zx39h^48TMz^1s=+JsQlilv-s|n99vWC#jV^$ zD~})OJI1#1=Izen*t3B`^)uSPODmX0U!Pbe>QlD$w5+Zq!}@B+n+hQf^;6pWjV7MS z-YCa1HC**U?Ssu{YJ;Tn(`HEKzWB9op8U4NoxFF=dW?$AZ*7<(xw@r1H-b&540Z$KOppB64MDgfqrj?nbZAx`;FR4LN``>t~8mp z|JVWB%xSk*J-GY7k^c-M?*os{C0z0uCq%b5`Y(UTys>59avlp^d5({@3pi_H+W4oO zd|sRBAzKroJinUv^@Hb&gnr7%{*B~(-G8zm(k@VJ|MQ;*JiZqPOyuurm|Xhfz$&HC zIJfBHO#l$~s zP{$H??N;nxd2V?N&O5qEHO^^Pk&~GWe=5P5c zW~twi)BdIwJemAq>W>3Ael^_5c0aG*Ca)>{;jGU5jw_W5tZz?wI)75Wa{Tu^R{`!k zEw_?}e60uO!q3@Ce;x>owYwm3n0;38751d1*H(RP-0S(Z@poDq_`Lu7Py(FfecyctjR6FmMZlFeAgPIT8#ET>hEP&H(`#`6cCNL2B(JvzZA3me(#HucSpk%@BAQ94uv46?go*GYFdh% z0$v@83vY@B9u#zSWsyFypo6)uuc1dYP=}|B>jwK1#YKJ1i#xhb{eD=zIQ!b3@6XQu z+j`yZ`I^sjH=k!ez`--k!#L2ifoG+YoZy4z<42A?e4^*j$fZ)iAi%LXMX~s=G9$x} zIvX2pr338y7#J%qb8|2p@ST0YK&Jga|0}1NF^ml@j0Z|4T6l9bjCPqvyUe3q=Fu+m zXqS1k%RJg;9_=!ZcA1A%mw7X!%Y0;pe@0BG$g%oz4v~o`8WL1@&G4PLfH6_dSz}6H zw4ysD+z-?fq;@=H z$*M^FY%;NB1N$4s{)VqtaztZh7#-2^eclPZtj{jRwt z67-X+fFoqf_pTS)8dt8^snYUn#*1e%&UZdDb6&W>meO*1)&7-R+YhW-7QW=}(JOC+ z_uH99^QDQ|?%}cVKC7puD3f{C_*c`5ZIZhHB-A?H5Bx|d&K zAFSMWUFFTR;7nUn?cKru=3TtD{J)?=RT)qJl^m}gwWlBJnfgtx`<`8Q>Y8=!qy3zF zSf@-DxOFe3l4*_#hhW|*8PlW24&oKo(#I=%?#{ArSbQbNjB}wZYh~UH$o1>q z6tkVnIP}L(uDv_fapn3yu9y7fC%P^?aWR*y`TcJuJC9V^3mi@pIgmd|RH41j&FM?g zwRs$Zs|=X0?1`E>OTF=a!}r*flw6s=3&K*SG0ERNDYp3arz^J|V!tNJSg;*q*L67D z6QpJw`~3cum~f3=?R&=?b2FdCxSx2`_`kZRH|o6mvh$ovZCm4Rx2Bu71zo%CzVy7+ zL%j{F|NIyHFgK{s#j{&5`s*{yC=`yg5He@a!Hyu!BjMg0{I z&V1d&z`rGg^Y`P%y00;2XVyMv{gQeyUVG8mE zug}nV*`lH-^Wu5nSIz~JBK%KW4|;Z}DCRIsPu3Oq-1gx2E}=7fo~>t!XWYK!YHecY zX{X@8j+=|lt$5ks>2CI0=0}|GsixvBmJfVer4DGXn%A#-NB2O3d_(7p8c&W~eHYFC zoyS&bJzDLhQy!b*B4NMs=d=sOuAhq!TtAR~>A@=RqnqUZ+>SZ3rhdIwH1~2VN7N(%zAfKQD#Vs#9$2(Zq3w5`-aM;s zT!MzN6D+5-v8k{s&w82NY5by9=6g>q zuDv=-{88f`n+ezNe`mS+ZpE59o%elw*9*>kezAM`J${}W_qU69g_m^Qc)*Zmw{+?LS--A*NAbLh2Un`rU6$WH?_oe@HRlhpA89Lo zH`;yuV`;NMYUA5#w%jQ8f0uqWemt_FVnO(pe>z{9A2fC@sExux3<0_f3F`IFE;BUD{V}teks*4DqA25ni3|$DsU0p18P*JI&WClmGDH|Kh@^QR zv1GWR%V6Lcp60=@VLQWta{}C(7&v$s3<5h^H5pi{84^w?D_1bI%w!O8`xI{TQG1=h zb1epjikX{icB)vqHb!&k7P5zj>&a=H^Ay(>F-v4hoaLZ*sB)&HQKJd}gU@Fe7#2(v z6@1YA`ESKJzIEr$ne}bsi`RX%pY6X=QqtppPtUI|aA06~SW!-AK$!ao;)20mODPS|<#=F=xnMo(7~8tvSVcJ406sOqKv zDUa^!{ARYhZ_2PC%Z{;nqV9^=Wae+)>H-lZ2OfQAVED8<|KJr34(5i8=7;CY|G&5Y z>%Kshp&`w4QXvCFoQsNH$f?ct$2k}nToyEHZE%$Sa!^9&0GrG~);kBe|0I~TIH?>t z;_q_1pO-xvL*)tGukE>@aY{2-N9#D!2c_eW8OiHJ&l|! zjwd*T6BTcDFeY_HDED!Q&usmnc+i7ov`->A+9w(9lZ^IBM*AeAeUi~W$x!N(u(v|` zBx`2m_QZyYw#~0iKhPh-5t=Pyz;~1@%khDn`bM_29yQk|Sv{h+6`S0I>mQ~szGm_5 zh@szxFa3)YT};dhZ*@gYUVQZYmolEKG23;^)FuWwr+1l^EXl}O7{ETw{@LBhrRC>k z|5r?!Tzzh5_1mrAZoQ6u9cw*9@_+woj)#E{I3LtK&|J_pYd6P2)*9&w-!orKt9-7# zpLg4pW3wyA%e_A5^Uml!h(6HYSk5S}deoIe_<-txk~NVz8BWTx_HH zBJ<$oc+~%8 zCQbh(dUENaUxg2XcK+r)!1Uo*#y3^TT?{#ld4(Pl?0ii6Z0oLgP5zVlp!uSX>)#cp zxw|)=b=62pGfCXWsr&t&*M47%n=P5P+wabZ_1$sn%!&6Z>z|+f7kmHM^dyIBd;UK@ z7Jk3)G3{fF(JS9xt5;Bck7*rC9G{NMX2b7)qGTlF{0m8w*6r2obJ}b@;}%~R!|{gcOzT&j3|{)K z&{2T(MpQU+oaxuR2cJicq>zUKCH{5$Mky(#9?A2wj2X8$q`J2s`*<4`YdC{H5eRIJ?InxHE zmVFuD_7uNgd|>N=@4`=FCfGd)pWhYvBfdsq=bm*_yfU9|TQ;>xci#NOY~!@WX_wsI z-QvkhU4L?l;pd;1eoWYQ<9uLv*Z!CmgAF{4l8l%BhYB955Iodyp`iKLnu48A?Ig1b zFWy!CaCOPf5PqSD|_UG&sPimnEjYpoXLLr#?Wcg)R!(%RsDNF{>UOBtF$hG zT?#X89%LVs3tYW;k*{mg#&=#7^4IJ3OZ{No;HbHM>Cuvg>#mz~_56K0QhU|HYUHk| z?r?rmwD{j_pVi%IQ>E+0c38|hyJX7h|JzsSj8QYZCKaUK%qvF5jr1O?NB>-Hfyof zfvhyCzbuRHmcm2AGplh zq;7uQe!ZV|TML4J{%rWV;BIQ%-buTkwpP#G-nQje<%35#*>(CeZ#SH`nCY4}>1vbN zhq);Y@85R|sC5b4n=W-W!;JM$^!wzjwCg&>b_Hw0`MRGwPjh{}AmiEuuTN>_Cr>v# zHafCADn@WsjDcV3r~QF}x9(5vI@+~wQBsN2j}EU-Yv+VhPm%* z9;|6Hn9F~MX-;Qa_Fv6N`44%nT1k%TA{p~P{oVV0%BG}8%>9<1c3wWXo~bKi+eVH= zzq7AbtZzHX!2D_PF0&1Hca~gQdoH<-w; zR?pkp;@ffxeJZY;Ez}a6l+v=kP2b{abLCl~>C&e=UTm3hnX|I+hNklyt=MeWBDJN< zyM?1ns-rI2L|iVjUwUS`e88Ndlr|qp)7G_<+_z!*GVkz^{61&?^Zm>W-ZMpF z55D5Lgng_kP0`bB{!hc_(LxLiVL}WH2BeKuK~~3y39TF*tQuV%?=?DDrJ``MLnKhi zd!ng_VNh3yLa2xAC)*+>%k~(zT@$TOFc>xcY1`v&IHhq@pzRf&tM3E*L%&@ybpi*MDmQm-TOAsc!MP&hWKQ(P zjr$a)b92oNygfbt9w)~>gEMDp-yh%k+~&E> z`Mn3v{HV8cBEZ-Pqm0ocHj(y0epP*2NspY0M>!=?&?P(Ti{HKDY69%^Sg9Yc3fkt_R@&8T0rCGh!`Z7#$OnxVe^_lCL>SnYaO=elZJZ0g(UAOmh#xUwI-Va>v z&;0pfPQIWM^9;7OOD(hB=snAoZ;1`%DhK#>Fu9}2`n>$ ze_zZf3*9!`arwnFXrSLEyyaIa=V^&U0Uh3p7g(Rm%j&?NEgN3oMm*N@^SHj_Ml}~ z?&O`*l5(*BP!Oj3`KWwA$2Df>mB+*teOBAWgtS};nsHx$!LsyC({yDvo#baIPg6BN zKTr4OZpXJJ3_lbKDownl|M>4a$#Q5`(jD=$=R&5O)p{S$a)Eu1SU9s~WvY3=u2nzP z9>l+lgiyK>G`L}cnuuVO`^l^*K0^RMOy&J8+?vGE5U(32EQv30LSKb#|)6*N} zIX<>GobAe7bp4~8P;NlY!@YOHTLi)kCNyT)74HA^@BE_3|2``|$zJ{col!h3WUsr; z#?#U-ezp8lY7saew(Pg6);;bomVNg*c^G}&Z}ZmjGxUoq+OX>|ycc zX=@X%X3S@uH(k-6A^%#_{Lt+Wk30{}TxC_z&GE|OgIwzyVZTVz#W$1cU0>;}IQ;oQ ze8cC)>@7Fg9*EC7zxSNBlf{Y&Qy#TdKj~ET+_HXE?ZxT0wp6f7u)Y&Ns`NC7A$rU1 zv|9^bIY?J12PYdq5m{w%R?#C?dvRQf3 z)74)ZgtyN%sSnlswy;L*hv5fT_D!=J_g;MSZ_asR+Yg1U87h~VYY)g?%xS)~XZDYU zuY3xGexw&I7ORxFG35gHpO~Pt`uh|P>$5+*GVR#?HwKqkw}~pAS>JNt>es&RaLzMg ziaws)?=CLsXIgLhan`BNRu4jt*N7;d@oF;PdaUq)(ACyZg&a-?hWzUbGN1lEs+s=v zA8W1s+e`VI7BQ{sSubGIZOQSOub#P%bI$XhF}LcpH)zf>y_+O^^xk&w=6fe_Whc}TT5@;S}Z>+e;=Rj|8>(0CR}{d$#v9P?MraW zg$wUP^y8*h*iOx6p6;1^pueHezd}26#?*_u8`f(ptA&(3Z^&NtXs+aKwt1IQU7R29 zU+()oo86TrpjiVYA9Wm)vpz+*6?=aDpehgXWT|-PQMScQgz*InR>8n>KFf& zOjW1yepneOi~nWwm@f3Y;q{-Naosv6lKTRr%2FfoxHEH2Y2UP|o7HH&~CI7uxXkX|~jS#?6|{uP%IAA$H3= zo#Xp^>6`y+{!FfHtI(Tqm9zX<==S6T>W!LL8!|mN&2IQ>EAal{4u30y-g?d*NBg}c zIIJxCxGFr}pJlvX=2hX)J^w_FL(HN>!&XL;eEifd_&Ad z=9#nI8-6c}+^)pKD;Ze2Aa9*L}wqoTy zjQpoQ&Gad_x9+voAH64?(xscfP44l%y{l!1)&37BdOacwR_@q6(>wP;)Pw9@_fOB? zwP)IV#rL9hpZ*>GS!pa;AoHWPe~O*|(^A9B|2#I;?`sMa|C^eUc7kvEs`b|y@A8K5 z{#YBl`{&Av`9143d;V!w_x#*m|NTBALx>o6f5`p%!`KI^)WtpBB>Xa-9u;I@I3UQt zU`pCRReW(qei3B5#{oh6ccV+IMweENj#iD1RtY>cdCHbL!^kmo@vNY`D;9)b+(gCDQtyfSTd=&HE^uY;w7(^p!r zXkV4SB0t1_NnPaMPPWLyK8O9Cmjo`@82@1^y}9KJc|^OQ7u_Qx{{f6OWI<9x2MeER3n(W=qWs;1G=s?pJ^ z(b1}*G+M>>8a7%LnG+f+(zd_0npH$2VB$g^rG>68A}=NNw@@b zUS;>#dP#wAf!!6wK*82)hDI?g>LD^+2UdDXtZmY5>fhJ(Teoez_DFIv`x2)7pVy8t*d%ZIQ7*%DiLoZT*f!bd>%2$N))S2-jz#^R z&p*%5xZ#{bodLTA_YOe`J{@K@M(;ltZdJ6U`?xPqi;(`<<5^yJhWTD8-+h)G)>jRp z`?l_M=xnKqotE)(MPa-C z>z}>M5$d1wnDv-0HN17;6S!O*o}hZcab0GJHRsel!^t(-nSLui%<14e!K|jq z@}04~?aWpdiNzi_*v~NiI{rBS?L`eUr2{FA`nD#TXN`}ECQa3uxZu0u0p7KabrB89 z4%Us_3H^`%2Om1YbX!!=)It1TyCCDK#;XpZ1$GZKx$bO~*b%P~vnoG!>$;l)JilX8 zO0>Ax7~LCh9$0-gs*Z#9i-12wodH6-%ZoDXlZ+FbHRax=pVew{CCdn=Er zFpD;>obuI3M6Cw z_XBaE9y<>(C72sTy}#Tj=OFzsW%9LiUwb0?bT0i0YOIzE5L@%zKWYz$#`hl0K9AEW zw%LvLDercBW*$pzgh{Jmz{!CTHS@G*%qZ|``<`0hgSg#6rjixM<9+*mUA zijA6^%!8JPGQL&}GS|Ley;QWLd8Xc~30+_7=L(4yUA`Z*&f(pP;=+VWDl;d)e{OLz zf3ZN_x=B}dSS)_+9=Y64Cb{8u%j)%0?sPRyQsCikFg;MQQ`wy%n!P6@x5GNplz9dN z59=$}^Pg>#+gxpv)tJ+n?N^^W&G<{pus5U2ZFYjqlt+Rb-@P|7)y`gKdDGFhfNulq z4;P1MwwU{uinOO>^}l%Uzc*jBnCXvH%2AFDN4J*=8Hnw;sTmS&o#oWTKc%Q$q>w?m zapeJT!37~9(R%a02i6wMdbW1uZ&RV8hId_^R=cFV4D>Yeoa7&NcDwq$TY7UoA1z7T z^JWr*zott*)30CGp0Or&)WoyaaK>B}cp-J;$CmuwGnvXu%RFvoZEetQZdwwiw?b^` zr5MGFhb)iF#^eSX8k;FBGBUjFcr;J?fk(nAF@^ohR_>fmPwDEc{|i|2ubt`LdBj-F@k#Ld!|ja68&(}K*?Hi$(!%3Y zglkMS(?h;z=Y(HQb}jqP@x@yCREdW8o@I-aS2iANepdBg&5eEOtC|P;U#D~)_@(hd zw_y7kgRq_Z?Q|TnU)QOhS^edI+;R`kUaic7^Eivn zGYQ7&yb@U)c$Zry*!ffU;=tNDv#yy4{XdwY?&`ndl~>oQvei>xzHa&DS9DtH{EWO^ zCsuVm|904NN5qd!T2+&$REk#}^SQbuGO#taeCxfu$ZsnQCNkEn^(}v$wQPON<&)dL zO(=fBc2#Ue;7=b`?XFD^^j@WISL2bI?BiE-$mxMqWYRLTH*0&QY1B;VG|usOZDgv= z`DzifV;fie=g=21idKEBJ~P$^)im7+P`dmzV5VHgR{jdthA)kEn_QYseM&q~D`equ z*kAkG@$PT#O!BYg9yKzwFgI|ER_R+j*%%gde9pGNo~ClSvfrn#epKsnXupQ{+N*Zf zM;OCjfB5F0uVAUgTV%P{UFXWvqX~CKS*BlS+~KCVteUZO-_~~9WG|V-yI&W(7BI_P zpTaEa*dfAreU+eR*{N4vt$x#L#D(pT9GRou#COVg*>bH{3(gl@_b->4=kp-);o5`0 z8?&DCE!dqr(fi5sSf-cKT6$Vj&vmyfJTI%QV*R^^=^l6aqyIb%EKgP_8U%CZU>_F} zl<;)>GH*Svu^nRA0@Uv^IyPv2SG{JEpM)4S=k-gG;?+0w`E zp4j`utK@58?8B-{U+2!=Jp0hw6}Q*FGkcf$uKmvZGbZyj=S3P9UpRci`9a{*i}u{} zry6A6UsLDr?BAHa^|`Wsvi`*IwbwUnUs}HEd*XX>yKiziwlTkFbT2agjQN4> zC$^vVKb$`=zV*Iyz4HH*|5g7lGAwUUWlU%8Y;PUV`I1=nJ;KquL)aKLH89$-MwPw~UBG*T;YwNBf$I5A3{DAq7($r! z8DA?WziQ~LdRDE#e1|h)0c!-m4-;R|6Asr}RRjJEhJ8~U>#s0A{dJmY>j8xXnGd?V$@=%)Pn44{om-KApqTRj%K|f_ zG|!@g^L8ZI6!3r8e45?MOuTAkeZrv!B$?%U+sj=d-iT?AHJuwIB z^&hNiy7D{FAfmhXd&G^$f${7i4E3iCX3b}Rr*Fm;qZP+~X8OZ9pAI$8Gpr9`Vs3aJ zqfvI8bvZ+TVG;N5&p&@<9e0}h{kO*hMpx&*nL&k8C(2pRg=}fRZ2 zdwzc4jy$G&qRi_oo>~dMs(YZvcv5Rgc0=!hy}u^(*&PbmqI~ml=+Vs$p;d` zvcc8OojJ8i|LIFL)sM$siOWsui@UaY_S1V=J8OLxFg>{tr(?(`IAiUB*9RmX6xaUL z(A^j9_B%^&;ub~MH}8eQklVOlr&#jUO{wTk7e zr&j!KoO__dVZZ6hfhsPU|!knez~-^z_!&+a`^bI(|gsZsq%0K+va zhNT;G+n?K(oO-u{BYcU?FGfEm;pFUS=BH2DbmN{iaGYVaEp%0{Zkg<`|Hdt`yZ(&6 z6AhO(7CG#+n#(-V(Cwj9&f9}=MedzucIRXlzMCst^|PuX{O0=Qim$x327OA{c!P_j zah7*O?QVM*l@OWYqi3|tU#Yn?`+Ru96wODM z4oo<(xO6AOnJH?2defsP&NL8}yu4yt9dp4{27Y7F4?FH`kcwFI@4e~VuCr`XU79|x zL?=J@i!@bV>ENOCl}U2J{48fT**ASm-TW4JKh7z+xI$$9#b@7ZckVv4^KPi~_OnvD zYR?$VtJB@Xna*z9s{S|S>GK{IhQ+$~9Jc=W+?aUa?!2|V2c8{R{KQ#OnpNl0_1&@O z#TNAYnV(9^UOU~=TFK#n(*yTV>jrnxD!n_auXdhi`lM&-7tCFH=eENYt_xpZWz62< zG28f=;RgR>!KbQ{uZBKvP~W4m`cl~1(A*gJygIGIxH(rVADAET{gmjZJNx6SR^KzX z8)I^EGgHna?X~xafBUVSx9Ie~8@Kw3Vsz)P|8l2tUYQC1>mA!-+&8PcJk*nYY1Xwf zwrf_;^%d(TcxT&$Z9Xvhz~z76{)j$E4t#!&eNxYblLw!&ysi6{9c(SO!}YIg@8Zbi zuOoL}_m2qu&&$9WoF~o4))0by7zT6{>zQerpFiYgV7S9g(ow8AnMp;7MU~)VkM3|Q ze;HkyGrBfsbZw5x=uWcHwK=0Z$wr4@o*rDQm~rCGgtdy_pU*fq@mwZfYe3zLYZurP zpCxu5uH#W}ZEp>4v+pbDh>*HyINh}T;vMJW`!2s+rn%gkoo?uFxn9@HRLgFaUY5-* z^Iayp?2qZCMV`4DF*m|9$6LlyPivi7Ue3Pzf8Ks>e>C?I_v32u8Tuz;3n~|$JM^V> zx!6iQAswq}4n7@z7E?Yr3TUkm>rwqAT&~*3Re4DEP`Z--MYD??H=JX%qY_0YhHcy&3q0niSqVMEw>95kC zrInptliIrJ-_gA8?fP<4*-vI~p0I{BQkQF+)3O(%Lom68qeC!v+`qH6HlP1D@oPW7 zGi(3M{?l!|ZLjB9*jZXWvM>7k^3QVT^C{=m=86AX^zZaKaWVb6A6H*rKX-A)#f8&9 zO`p6!w`S?zR|j58uD^Zk&@s7vCo7B%OHp31o43 z?CS6^(a?3xnj-Dfb;e7K>zkfl&=dcsvA@sOMc%r|opx-gRO<%DV-7b`&t3gs$js!^ zpqa2(;Dyu@etF{uv71cU?|C_{dzj2>W7ODg=S=LS^F^m$KxOdRt?r046D2T zx!R>2h)!U%VfH`y{_@wubAMDY-m$jHZkPGMU?bM_<=JBXiDEI#>kn{NFz#zuJpV7t zyV|>P*~~iGKW6j9XVZ+hVPxi@>;_&+iJY^ZTy z6mhUqke|SRg8d2eCq~u)hMx^J5AOdy@SP#)s-uJI%|AD=cNo~#*tosF`s>>Seg=ka zev;;Gb5qk3J_=(X4{iE}?qn(DK z)M;SZ2I(~Hncy-GWgnvzuI|=!uj(+}#x+{MboAWx>H?5{5qlZ)Yv& z>I!3erIzKgq)FbzTZ1jBbM~|aY^8$D7aO+UxWeCc>qf$g89Iv;4k~=y@nYul+Rtgv z&hBwwZ!bYCnV9M8egS=%-?%Eq-8bfMoVpugM7FkX#OgPf1 zGr7=mqU3|(1aCC!yq%}V>D`GBps<1^>!gb9L9Y3~qnvh!6l>^M* zveZ8;6aJ}u^usROX`df3KQ2DtGM72-t8Rjf1als99mBjc9R~!}HwCi1cM)p{T(CTi z^UUw$1EB}p4}?2de-J8Q+HrdIx1-MTwYC=o_+5TjChVK?;VVCb|LuhI2aFFK?(B$d zZwy_at-kMckGfj|!wiNv_A*h0`vz+L<{+8H}g3j={XS&(N_GZl%wq!nYJ=a-%69><2seSBw zl3rhX8@gTXO_N{(f55j@J3mZo(Pus!%09&MTtCjN?o(4q z%-hrY|G!F!(p@*H^^E&(d=~n^S|C=T!?6Fc3tO`15AlL{zdySVtiP(V=0MzoxZ9hz zGsiQPGtFn%&ahl}iayidv=knTccH&~5~el0=6Z1a?9bK$&U3epyq%_Ff6L{5^w}3i z1-rxF_B?gqzrZAOsQzN)@9_??`JupAA^g!pDTi0$s&)#05bgwf(zKZ<= zZ{?}nbCYUJbdFXY3r$#hWx*ldiT_`n?R?KWV|!_E)q&2Q2#?vFl~SxHkO4@N-ug+WEu)h0Mr|-+g0~On!x+mHSzG|G`*gyLMb9G~QBmbNK+t_}Xo}T+S zZmWJw6R-4@74z($gqEvJxODq~=YyyRe|i2mK3LST#pS%ZpNrP_>Z`iEuYRryzdO(2 z^ydTf9=r%`@4sEBwB<;~SC5hkpTGmf2U>(9w{z}laD3d7_+W$RihmY%C;0;O7^gGy zx6PRsa{ln=mYniZ< z?F6s;L7vO5RwsS^Q~2Pv!WCh&!2N%ZdA@wKL(br4ocsNt##WDoO!K2A$rxYYTzqN% zna-^RO&V7eHCaVXkHlw+7TIjLEc}%{?Jf7ThIvyy|201F-g6<}*I2jugynlz-|Bo3 zt@Yf|Vxs7SpFLG|!Uc=>c01K>pUWDz!Sd6K&gjN#Ym!Vdw)|=nJf+C}+;jac$(Aaq z8EjUK=Nx!bADORZ-;?Ejrs8TR_agQ+cedS(s^hT9?ztXPzi{P0hiMvn6{-|$?z6AC z7E-@Z(l(%YU;ZNd39Hz@9zf>dVGGe;a3gu04=d`J37FZ|kAIjc$v! zWYjBOZR`;)blEFdy^XEJ=i=-Aj10WlDu;PDE}MwGkH3(^(rwZIbGiQf3=CfU3=HPP zugym4<3ko_d+~1_?c|Sk@<%)Qqn-TGPX1^of3%Z7+Q}d7 z6c$NOIIxaqn>=^6ww6nYKW8VSy~Ey)?avQhclPxStv}Pn>-_!OzE$5}UETlh{r{+t zsaKx5oD*Mle8tW=CuSd5e?Zw`y~i2JTZ|>E<}gp6b3&hYPCf4();;_+k{e&Dy}~agM@`u>AniLTVDk==lH&vb7DvE!#|PLUj;r)6#l7f_;8x-w8`IAGkuuT z-_YxK0_OQ#WJ#j!)ebd|C__f@JI@d72cF51-Zn|pEoWuCLq5A;e26L9B zmiCNKTv->`{{B?R@}@*+b^W0^*2Q%K5q@(z_!}-eY(KPe{={{b@$6M|4}4H65c?sq zga5~dBMcu_3ZGQ|T;3S$5clv;m$2lm)iM?A6^uV}4;t5q?zlan{$S9*sdpKtHO}^X z61A{u{qx6tPNv_3q~pRHm=BbmHgb69lE+|eeEdRZ471&Kg*di*Z1ap1?ny;HWUxqD zzJULYW(a$t7}wRS3@yqJj(RfqJ(=KJw4d>V{)6b-UA70lA1FUy+xqG;BggbpS1l*% z6)5l=+APo1tSMkMF`Vb;(?ToOI}CM9>wcYUJU-`yIwQw%RSxc}dk$5~@33KYD&Ci* z8NccG;RCO?{h!`=`^`MpL1VjJ& z*GcjiXQWO)xGbZ2#q1bW{wt}9TV9*Y*)zMseuctroPEKX;+Rx_kT2kh0!kmK&&AIXorH}v4UvY7Hec0_Q#jBZnzdDp~znT+% z^|R#{2UE6N*Ff}chC@|(I%P~h4$Uyy&ch?s=g z4G(|33ZCF--kc~pwd(xrd!H44hx?hztzWg}>F>Iu|Bsb!RWO@Zz*;P}XkzdEtOV<) z$B#R#=j@OE+ixbBIC;_v=Dm^vZCj)m1B;rRz8VzVI3~~e`>;dLnG5C1^H>f4>Hl_o zUA5ffWM@zERsE;#niiAC6TqNK9K4(f1AvHZQBOf)?SN><$jl&+=wii|ze*ExZPwK)YPv;%y zp8EK(&z5&RJUPaH_G?Gp+q6Dg)ob;yLw8dY+`JA~8X4p>1cuF4Z>VQuxNYe7_POep z&Di_st5u!d*r)TvNAoi{=&sTYS1`{SA#f6;Gwx^H1%cd1uO& zm8*~JOgW!+DedL6d%E+Y`KGO!{%ks59`DUtTX^q>ZF&3ko9k`;>1Q==?{41aymRq> zn;m;nm!CX$*4jQ~NywLwUm<4iF5TUlc>PD-#dp%xd%l^xxq3T#&-cG}C->bBWeoMb znsznyx99K8-|g3Ru6p@n+V9-hwSJ>Zct2kpQXO^ZWe{&>_(D4BrhG0J4##~{n!(wj z)D*W$(Dj9TKoz(|ys= z>kosgl3L^h6_tdrfJo6=xldVZ-o2eSZ`Q}hNsh+BcmKZs{_1M=`~Pu@N+Pjh&#G_g?&_7Cojvm2%hCtqt2XP9#$W(nWq12Y{u+k_9E z@9juweb0Q4Ev>=7VeSF*13ki$o;{i0R=Xtmu>X+RalSJrXkl+aCoh*qfn)_o1&al* z%{v!8-g)NLk;;b4_+%L7G0kJ+W9Da=UZMTt{uHKzuhuj7F~~C}ADEogE9;jMzQQAn zQ}o`YmD?ROY-Dva5(9JjcR&Wrrm`Q`e` z;@=molgbS%mSxCe>|-=%h-bLZ6g~IET2{5=om)!y0@%eGKOf*Q;N22)>yZR^srn_a zo}1ivxOEuwnZ7eP4%v%m7@cjE<_5))9SH%r(c0RRZXT&5H zJY)SGYBe#?zT8qVU*`>D+3K6`80=WyvGp-+XN-RxX2a;Sa#DLkxsl?`sK}dUKg$p6 zS}c{9yZod$M)YTpZfN+!`2a|F5>yjnlVOzD%*T zW%tih+H_%?YNuvJ`OQOH#rUE!A|m=d{30@z3r(zBBLBgTJ3L~?-9zie?rs0jKJA^b z*fgan866zQ{-v&}JDDnWgKH1R9!8t>w?EDlzA5Gy^fR=OGb3%nzJ=FUxN+QHz4uW5 zu19GG`rMBTSmwP`O(vH9T=~QCN zpdpC{Ze2b!5$@mGK8v3`Dm)czrgP*1 zJ8lm?eee4ZB_pq^%z|_FEi97|xxbb@>GmrDW1fHizx_GS$Z&Cn$68OlTWhd)+Rv*x zyR}4dM2Ye*FS@!s z(o{6E>P#{Tyr>|3#JPpHqeFP;OCKh2qHvvwz8o3(NYhOaXmV9UF zyvszZv(4XV>rW{6aTs<$E9W&L6Ee-oSKciJa|Q7qjI~8~AUq>{*pp z?Rog)PIl29dtcAro9MVFyiqKn-S{cq6+r+ z(@LD?oTxeVB5lG_e&*W^-n$;<9>{!e^uY5$Oxl@Q!kJ5B*1eZ0(B9xTdF^x~6|pAM z2bO0WA0JRmVBhe-<=WH}|27|Zd_d=ehQZUjb0sgImNqnSxNE_k!?2C1o*|yWoZ&k2 z{R|De0;PNvwfG_>{-@dZc-4PR5ag}hxnTA=!3XZO|2ThmOnz=?Z&~0YY+AN;%QJpv zYvbf|{huS0jh{KreZWw_Um&uB`3BP(#@~&$QEs~%emCs33d~7WZ}{D4-gx&X=iC!} z&Ue;ugo&E4&tcvdth_JGf9qA};sfXXQmP|f`zd9-$UuppEkzJ>^tQo4o(j+dfTA*+jL5R zqTNhp*P3@O%hWG2E7~pX_%!9#wOjkdZ>+RV3z7{zXztx`_!e8m`Rir8wgye-xhDzQ z@gECooF;4-`sIMdp9yj+4nLp#s!{OoGRfCYvKb507mB}@Ry)|uv2l~->O&i6>(Bn+ zACht|Vn+bGyy<~Mmv+p$epM%W$GJmIRSV-;UkW;_+J&ZcrE%C4X!7R_O2Y5c%6_|fm zEA^S@14F@)&OO4BXHu^+?`L@3yzgP~16TE;Ql)=izdCrmW>&5}eB?QE_#C+pH=~X` zpZw@AJBvL3$DO?b*Jf;5+rrPjxMrUHO&y~}eETF-cxM`}^*HMve&d$+V|I4&$6p#+ zH?CU7cW-|_%N?8brn}wGv-xG7U8lk)X=RzWz9syvVNpQj&i(y`fBwI!dGMZ*;X%rL z!HYw+8+{YtpHzF2^PAy0s$%jdug*-H{L(-Xn7<9PxT-;W+}HG06+=mA%w z2V9NLj*rfckIs%ajn0mb&W=x1>-lC{|xL6lMnDL@XlSlN?eSg zjJfte%>uTZ4{5#@;cN*)f zXGLjl&+hCs3ct&@_h#3bor$&bOnR4Zo@Jk7{`%_A=U;2ShrgQho%!y8!<&wkJ-Aw+ z7tZ`AQ&p;L#kV``Z7I(tANXlhp0~Af&dlZVJJ>3iD&%ggdi+BqLT6^ux;sqPT4fvt zJbO6LT-)K@pxvN)AR*vB!yXPBHXW9G?CY59*!N9NT7GCJm;8ip$qmd_P1`=?1pi%p zZ;f_wo@_yP-U06ez6b0qAKE=&e2|{dX#V=doZo^AZ0CDC-C<)jf6tCZzt+7D|LJ@G zcH`Tb#gAu|>wnD!t?1}s4*VcB;pX;NXLxU@Y0I3O zb9=7kcC|T+b!QcO1;{hzU7a~ycVFOF(ND8=4}4!G&wB6j#=ENa73>v8#qUHP*d%zZ zJJ3*e>*-mmtJ~bW^K}dz?%DZr+i=7zx8CQ_m>cu>U0_ZG^Bo?YsZ(`$Tx@TBT*YN0 zHIcu>EmvRrMa@Eq06sFaA3nTy<=O$-<0`3d0VkLa^vQ#@0FHHpXEDj?D|cA{?$LW^|z4Sf>txe zzk$({V_7D?q#k;dz@n`r&;$}Z&EI|Gs)`f3Z(|mtc%s-_u?L$oX9o~kL&ly_a}Jk$Eu|IUj&(PN#)EPtXa_0!}-FJ5L% zIK??_`h%Yu2HZ2$)Oq?7e7Z_cNABl+vOD#g`J+3^+a0u4U46LoO!$(#dpUnuW|k=O z`{i(^C&Eit|DiZriN0zu)P-K|$il*^XC#J8blm>EettKlR~{ z=7VzA-6HBUbXk9$`q5CZu;5e#m&weUsWtaKV%@d#RXJj}NSUl5v>B*zmM=^RiEZrvY zPv73h&%n@0#*usAQ4Gj(flmIi(d7c8%LPW43ydxo7+o$fx?Es%xxnahfzjmxqss;E z454$nS++oiF}BP&pB)n_a@@Y0hvVo3*WLid6#?Ok0-krOwkpQ9h-I4VJ#s6U+W1ra zQPs0}4xbo~cb8HZZHvf|C=jU5T%oGX&F1#1e?}`u;q}K#&;8Eb+4+61xto5=leyLB zt)JJOU;n;aSdfA1Im5OFW&KSDdsY|ZvCm)++q2KOIB!M$JI3zbLzZ06e>sRpFu1iA z-}UG`v@`70&VqHTf2F!bKP%*`VCZRJy>4^+26G*|9>eyKYfNmv{fxlQJ8)ZJ65*q(1Ooz#qeu_xfRfqjc8=W!Kko8#r}dRxa-=37HqYpJ_kC z?*paX2h0yBe%NKV-mOcsr75mv+hbQVV-6PC24{68k#o6>i{f-8-Sw&`n)ZH_@4CSJ zfNR5hSF_Uhlf-w%DhFk8@a`yqff<@*vY+D+qiAzeXKh9A(F?8yCHkYD}`g2~d z*z9@)w!GnI2augdzcao$-ec0R7>RmZOFyR}o^ zN#I;PyAAua<^$^A_MX@@@onYAsa-;QID2y9M9$UnU+7p+%_kGBA-b>gug7!!>T7pQ zViE+Tua`2-F%emG=apv7T$kioR%_hesm_%Cq%Pm*^Xv*jnUT z?5zEq=|hmmp_s=vLe$xQeM-$Wv(2?QIb->S&IuXJ^BI#)3%qSRzKr?Vp`z;{C-^>0 zUG!zzw~4O$o8)9J*=#UYoMWBqvdo#cQGCyd-)?E?PFZgm{I_`iUHP_3_p0y~!Nl`N zIhG`J-+s|@=gIj=ZLeeB_H0*rYL?2oXMugZM?a(2aR0Q*;0{Ba-t6b0H44|iay{S2 zw(r7`w_@jXqx>u`Uai?HwwF03WZsAMyV4h=tXyaIH8$Mhy7rvgtBhADHC;M?las@z zbgM*T#gB@K{^>KN?UIk1`b+k`%?f7_m+tm5LSfSkJzyQ?rkJC=_M+=F36Q(Pki{}{J`|V*%_iofBk2b|8Zx_tA)=lhDN{sZ!Z5r zeNs`Hok3h@W$a1zLps}Q4xGsP%qiG-=U`8fdeyw51hHDq(17x*Pk(KGaXs{kGkh~* zL8}#nnY6@KD%2(ZYKpqVRcO3@Y0_Vg9UfwWLQw^RF`O66BFlvYy<%Bnb{Kro+_6HH z`yvbHtHolA76o1EOy~}K%&s50>g=kfE{%$qFK0gQsr-JgJih7Cfv5L&8vnOUKR44d z-p_^M#2n@b7CrWf>V~?-?^qwOKaft))KFXh(uzgU=y@OWb_U@>o1<3sU-vTnVR^%L zkCmRs%h6^S@f9mqt!|r zn@$8Nb#6YOe<1gOp7KmH&N;06*v%Rgryj6-z?Y!UD#Rq#e4?6lcYNT~xq+NDtao^L zSSH$PGH;w4E_;9N--ju8`8Y*aI&#jro)=SW@tx;H=tLc+MKbp}Yk1$Vv^AaZcy`CB zujL%e#G5jYPVaiW<5;=Xf477sF9f1$bAzY)w0)Dlq_$AOXpVp!zmm7x9_|ATmdxS} zoW&fB)0CJ`TRn_E{59gq4iBf7rKV@}Rxf#UXLp00@!X6v z49n@&Y$a=}1ZJL_lg;FJJ#$8B)LQ$UQj2!2W4`Rh$~#y7xaLOYUIih}+!rtVXJ;@h z7P~!tyOY37zh8@|{XVmE+wPoy*Le%O`~6cE&0)+9u5@XT_A)R{P3h`dyFSDGz{xpt zd6>@bSe)`*_aghm$*wHTCsqV%{-~eV*1oJ=^!`72!GnjV{LJg%`1tX|oVVeXO3SVW zCi(Jk5}Url?gN zJp0I6LgVvu#}pOO%082-L(@GM9G=fUu~UvOQMKlxq{i3hq8;JK#Us}3+BaM7+UGOt z|5_5%YoZp5w<$(6g;_#}2(`P=4 zYW=*`xl{hyKfed6sf)f>9QgHl&kWsk@$U?u4`f^Y=YHTJa5GL-j^WnQgu8{T+pA6- zek&*U@k4t%zs;Yg4`;M{@_!b+TkLKVaC3fj#j(Sh{QXrwLObI>eyHnbV2e?(+A| z2bP}MS-K!BKtAH^S?)6rR-I&hcBPM>v3S;f`!y#$?LY7pFwJ0{$MB7HV)o<;g`?uf znYV2UoOEA4baNHM58fX>n^!RI<$d08+40HMO}`vg>IdAn3x05({pzv(D|gp(-{9ZF zc!zxsTO6C6?rMG!&jq1RHBP?gXPCdj&!15_%G`p{hTlfc<=gZJ(UFByTzvd;HJ9n^ z|Ibmuu!Cidrtvk$tMwsz((2ljo+w*8!| zimn;F{MmMmC8utFdG@7W4vPDn?{?;{Q~$P0cY#KCPL9eI-uzns_#mgX8@GnNKDfT* z7<1}HpU9BQ+WUN`MxTAZz3YGd^5_3ue^)b@Z#d)q-Eq-M>|Or^HD|Y-m*giL<7Z$v zNX9vSNL_!(6#qefyV1V?Xy1Rd@83PTQ(<(c!st$g(VYsTI~A^v?o=4vsW5b|;%0dR z>HDt%_5DKyj@M6KwyitNWusPv@QQ-kJJ(yxHPFjo$>X$hub(`*H+}Z^Duq3> zi{G7@Iny}(-~XM9Rvnqx$-u+F&-9)B>h(fp;hzFK*d=)U&aFMqG`;zifoi)8^POjx z^ZxG5a8dijXu!XR;SbLpZXVW9qlv}`<{wympfG{Gz+{ErbfW{g|B}93*!#Hmm)sBW z9sD;q1@f#uh!=3)kP4}e31MqcpSy3P4fD4f%U0WL`Tor3XZf~&+#7gr$c2t>hUYOU+f;uR3hdyP*qSWOuxv47 zKEv){{^nN!YDKsH{TF?~T(`Ykbp_w9C&8b0o#$D}cJ=PIe_{oa6;c-bcX)f2dG0uM zc!QDSeuiz)E!!A&*RZ_V`@ccjv9$P+?Sb?IrMGMHV_uhCf35s{;jKu%SsyYVXeS(0 z+IaZcuh>P5cNo^)ijl35vtY4do3n-E4C8zDs~1Hr6P_FKa|mqSk+@&HLD2isvnrd@ zGcsnLSR`3sw4%>q?{VMrt`8XF-BmPQil^Oup3}UFJ5jdc&b4Z8u8T?=6j$W=Ow!M* zIl0KO$6@>J1N~XM8Y@k;J$tHr$WJTHYHBV2KTR-C5Ao5XWMY__?ig}$TA8j5od#C9`uLawi&6lis&ahn#ep}EqgE`%3 zqUHm?*EP#xN`21f3sp$YU{q^l*fXnBr&@OAi7BfmY;WV2_~3K%XFU5kroGpel|L

Oh{$G2*@l#O1-CgV6Gr#@R$Q1Rh-+J1#Pb(R}zNoyFwESVCJ#*zJhpWkszyH?G{^XD= zDz%Przk0*%hUZSw0&yz$KYnlYJN}=UVV8hkH(SA)bnN2;izhp~<+LQteZs@QaG!^P zL7$YR0mYER3-0qMd>%c#VD#{U(ZdTyhXh841V)DhMu!AOhXh841dfjm2@IVf0VXcU zkieS=L%+iYBDMBOu4mqKY-Ijs=n%VmV?iTNkB=pT_=&D58*SMp?RMGAp#D?G@71pR zeRG#FYUxDY-}oo#fByeH;=;zseTzE97@`=s8P;z6EH-2P7KUlpzO^O^UU1=Mka%|0 zW4-k4n3)%B&gg`(hFp6ma-rqeO+)VMUKOG^`7mrdh#YT`t&aP?8Dr)cB0FJ=ss==YNy!l(a8s+uEHEaI8{_4Q> zFQ@82*4}&P*0HM?8ts<*aOA{(xgXb=#n-Vf*)u`pauJESe<%YFEHXZd5#FAjY3DJVgpNOdwE3h) z$xEB(=T}y|e4`g2{PkN*{qflwxxbwL&QPZQ*JXL1<2vjeCWS@LZhQRtcgOKDFhue( zFc^^5VS-GeM)EEDINDq(0Z1ZsEeVCZCCq|0t`%d_Bx_KPhs2NhLP9v|2$cu+;gQMzYV)~t7KZ+1*uwQ?q- z=ie*)zpEUP_;_x<7>DkI+SNb53*Y;3r|Z#&IhP{Wc-F@;F6iI5Ta@*e#NMw`40Av4 zWiDVhxY(_v#jW<(|DzSd3`QFj4z{wHmtViWWX0UDvcHhmA=;yosiBYOPM6Y&*y^|> zg*1kEZiZW18~-pLIMMsfuq^6MxziTO`LkOV|ES+5>Z;58Uh+D_ye)U-PNrqvm^FXT zj#-zF?^xAT-1T=yyOcxef#`R?gP$$Bcka;sEB}}A1PjG!?ws>vp^=mP<0G4-_E>Fs za`iwlzYSx_$~9}ApX=>rGiI1@PeS+b_C-6GCA?Jcu_UntOmzPxQK;SJW$1UF<-w$j z>%{qI%{Aa%Vw8RJ^+Bcs&QDrC@;x}pa69HekI$ZGy>}tFsPnoo9d|Hq%sb1!scMIYMi=_GZgHz?EUjJ$V zr$kgN*xazevc7`f^XVOi9YWQsWGBDey6o?|oy%CITu-z9tmnQXo4X@Y`ET6IW4#}F z4occAzQwjA;c0@`vfuM0Qx7|T)cmyMq|dckTmSgIYAo*4^Ke)fG$m!4#``s;l~b;F zK3wzU?3`frb&+2V%vsFIvf*b#nZ(p-z6r#mOwUNwQaYGjcKnPP*@wlh=7x+>y0Yap6`!`%Jy4p1I3PpT;;? z7H`s1;Vav3T>SNgfJEz0zwG&Qj=C)sk657fex66pvS-QtaY4Q8EgI%lTvl~Ulkb%) zd)`KmIJt^SsH?M2P9~rn>(E?v>{h&BQe%Zy5<}HMq0E;DWVOv7kcM zV+nb)6qQrz7SH$i+K2C}etz(3O>dv&udp2g*2lKa-Oi@!e=k(m=+27HsWJ{X_Ga}>$vybCkM>nZ7yOPc_#IvF zJG$9-bhGd1X5Z1xzN4FcM>qQprOm#~#gM-0k%--Ww+#ejxb)Ts7BjYJH)%i7Vq{wR zMgK+iS8lyjmVk1-CJ}bV>2C86h#WUP_WG&wb8e>t8+0e`K3vPk`QFy<^JPY7_9gdB z|Li-Y-gj^3+3YgT#M~b{*&9y&-dN5z`MYMNZiC^+I+g;44JVkN^ENvEjI@^%o-;X=$vXa%J#fZL{k{9? z_mbjw&*<|Wiu@a2{ak9T#qv8$5s|y^3Z0EA%&Fb*=<2MKzgO#2@a&skyn^wH^qQ$P zaiUzlZ*zWl{JXy-k>k)_mT;vCm0p9&{Q)7(n-6ovaoa>c;7Q=g6qRNVTgP>VvtipY znczFt#yCXg5UZ7*+P)+j%QMFPZT}Ou#yQv7WF)27#B>EIz(mf}t$%P%k^f zevX1Y;u9vd7TayA-ZTzrlx^W;f4Cx=Yt}KdSsnf(k!7rbLTXY$=lwnC?<@PK)>a-6~Jh{12Aeb5~v{eOOSG;P=Br&m!y5 zjNN}HoYpSwG<{v#R;pLC;9kVVxX_Kigj(!d-%meL>c35z>sH|WlEa;0EPEJ#NS@i* zazFp!-IK4>vpS^jy>762+?=;pZ+S{<&BD7SSMK(`(l~#2z1gXchZroJteGxq9E}Nh zGV@Gjn8K_RhIKob5?D?w&=LInSg4Z!@T_N5wSg;N7BBbd=qbv6KKId_@Yl)VTQv+G z`(~+ig-p(M`=9+smSI8bC5=~8rdwj~ni?%~cDo}Q!M~7?fng5mT~iZq*K`hF@@Us| zv}-!rH687mj&@B)yQZUE)6uT!XxDVKYdWO5rpF;&(>)Qk`NEC@waGhOU(E8DqY=<$ z?4&4kXWF{pjkX~sKbimW{$%{goH;Q_DRw%a=F^>)7gbtW&xR_Pe{tTNeeChguXB?N zCq4ar<8Gz3@$Y+Ynr0TBiRb+xb)r`L>37b)3d=SP%RL8KB;1Tu=bDKZ-$8N zXTHDso3_$#>&J|U$_1W`g0*!Vi!)}ma^H$cN`(j)&z^r;iIhEs>hBMzulhS<|yd;g$DSYMC?4 zv|Lp5q^aZS4Yo5SYt|iaQl7ep`S%pZ9y6aEVIe!WKj4V7D0UHN_?OXqs(S9iuGW~g z)(3_6jm$R{t~!%7^SxVDzBlJmb}0tA1GN$B?MxqBcw`*7VV!I1-YZF07(OV@6YFs1 zS$4&M>7}}snQz3TlZ^Z){=6}r>X!1zML*+T{>BpBq|SDGDMK0o#w&+~QGFqBry-nwR$fY{(myJfZG%e#8XXH;ZcoXr?_5a#K7d@5< z?>&6-Yv8?3?~ftxc)JDTZk$SS_GWnCIQ>3TN*M1`$4TKIzqQS&a`v7!ZJxw)Uv0^z zvk9*RH1ig7r|uBnypr|)X@hf9e~Gfmeiv#ub0MlhV``$O#>d#4;#Ce)I&=5jXG+kK z&zC(YS@=fy{zR)|m;Y|xmd53xsdZy(LHfBJrk|}8Pu6~FIK+^&v(WvJ@tMeFzy4o; zyR-DKEJIn6;pWTc%qOvTcTX6YxqZ<4T0ez{fuWy=fx(1?Rm1tEc_oRNdBxx^Z$D4L z=gt5>cU~?jE(Qh$UQZ8~AO;2o1qKEN0}eI@28Mz)75NMd44efXk;M!Q+`=Ht$S`Y; z1Oo$?f2Ol@KtM)*Nq%ugeu09gv$H}$QGQxxPAUUK#jUx)=^?={#caRd`{Lx?(J;k3 zKS-2AA;_t_L1dzumLjKsSBK)lo1%dS1zlZPq)#m9VD9T{=n)On;pyVK!Tv;XQD5`o zj;>R`A675UzP9K4v$OxUUblO`=JVXm=h+W%@J#bC4m54xS?MGv_@Mdtkz)^^=s7fU zsT42>aBNOdEdHy^$nc}i#ztG|0Q)`$#)`|_91I71XCE+-Y5&jv%4uc{V?zt$fs%<9 z-W(01UEa|y?`W6z>FCPg(UrraD~Cr{4v(%J9$h&+gjNnS?t*lA-^}pMYc>#Q-S4LdW69SV zqFk~k9(AA6wEo$F#j6<7)t88!44T)pUhTlY_b1=k8&6Sxyqa?ZlhHqx7lIY10_OFs zPnyR5n1Sbji#oORR8|RO(*5yYKa?t?;f1BR@A?@%k!yaMPO*w zM79@aiW=Bn%;n!3Qp9A%(ETa&$6VhpiWWQ?3s|j!wHl?x9_z;>h<*RI{p*1U1=+Pb zT+Y{X);(uD{88@q)TL8y6@JV9IO%fFnNP=Lf6wSx>l(#?^D!2HlEV#+j8yuiFIu2=ifi|SwjB9$Ft=; zIuDp*AN+3)wm2@BRFl-nD1I@|-IgIgEGOm6gq=&I*R2&^%@Yx=zRqnD_qA)CKXq67 z#Z_Oh%`muL@qNeh>I~)V^&cKiJ*M?-w>@Egm3msOrr}fVf*ScBlkc;?JI4O+>Ftl#8S?krW$B4U;_2nIubEVPg@=LR zJP!kdDH**S$Og&tJO-oPoY8L1Xg6oHn={(Y8J*c1o!J|m*&ChN8=cu3N;7+mzYyJ= z+n{caVo~?KMPi%0s~b<gaLPhgufEq}0*p>V zC(Y;B^U7rvwM!_1uPYmEUq@Sdvf4F^cx<5*q^03BE=sb&bTeKFz|%>ZHC(|!5^)HKmT*y9-Q*Sy5>NY%BT-7l)J&gE#tUILKiOsLn|+N-6Y7~yjI?-PouLzqvyzso+C4Qj?Cz6 z(CBQ?=xosFY|!Xz(CBQ?5Sk5Q+5qb&ZS20{Aabl;SVt%K$i1Z5+U9Jz>qEqj9LjBK zRnJMz-+EPX&C;ix9WTtvTYUw-@C!_v@NJg%8WzFq?$ys`nV){6GV@H1nT`yf4(H99 z&-*HkKkv(Lx;9I(F4;oT`FS| z1#-?xp|8|GI2wHrtY8uO6d%L3q*m~Ql7hv7*atgP9rAmC8Ov}O&@ z9`=3A?{9rjl$2l;ZwwA(SpW2UG>0woKZfTHxwa~~i~XmbV$fPU-RY1|YyXk-2f814 zcWAcQF>*JTd#cR4(toD7`$!>sXY>L72ZkTKmp$aM*zz$gkV#{$snM;Sob$A%hDJ>{ zGu&w^d#3fquiDDA6X^%SA8Zre&^LWm>&mF)6LY%4PMDg1mlZYO$bWQ|BSL>igU%Ee z@q2yGUoR+de_ArBRG^^Q{L$2FR@=_Zx*O=0P?V6lmcL=st+oT}q7TiR?cd{9qa`-s zn?lW{@bl~+nGLJ<727|!{Q2PD17=nGvh4-dKCk^KUFzUwFp=+H*RHcJ=jSu9_XTF2 zY7po5(b7&6TJhZ7_V0nf3GyFa{9LnKeg^B$#SSw z_J>^hf1f?;Z5G3x!rry#f;>TeI|ffzKbLh*2~7ds%q(0C3>+K`4>nl1Jr6n5A;8DL zz|BWq=MJ*FjhpYw$I;H+Xy$^U5&%KERz2)A6VG;byhROS9x9)GQVr z@O~g_P~i86$&Mj@_J_zL7P@mH*vc9<7AGCH-T%8{duk%P)?>};_gf7=NJJD0S~C1& z@cwroXzQujoB1LhPdy$dvMqa&!hzy+nFrDZ9Cw(+W_^g%c+=>zX3mbt-j#(;K2GZx z<~JBuEZAho^rB)lk85H^&mDc2YfgQ;%oyvK;v4!8?CNK~cIS^?ZRm2__KAPzI-j?7 zxBPNuw{Exh(dP}Xqjzq;Tf+C|<{Q?wH0v_%iE%2L=O3=#z`WZbU#jtx_x<0yuC|2T zV~cln3Y{n+e&b2WYPnl;JS|W8KAL+tUGtdlj&|L?wAVGZc2j)J7VZhS$DMmnr(&m$ z)xM0^6^hwspKpF-%wH3-YTKjFksoKCf8KhcZsn{qAkptdr=Bl;wLoBFO`XJSl_vjd z%%>0dDH`d8vL9%x{+G9yo#{u4t-;gc?TeVVPg=UIN}+9gRl+YxD}lE2)9)r2YTsvz zYxg^}tztoZl#y2GQmLGuHlh4W9%X-Rm|enT!>~(l<-BbdndSaTy=Xk4IC%-XQFYYM z-_fhSd0*|EdEwpP3%&Mv&E|eS=W19xs~$|eeSKx0j?1nC#t(Lc)Eed6Zg@B8bpHFs z-QmmF_oUoDvvBrdX?xD#FASQG79HD|cc3$HRl>?$OOv@KPIgL(>6mlu6XTBLqK9*T z@8`I);&A({7ypZQu6obDZcm(UX!zUov`HJISN;1Sl)I5LzPuuJ;;(;Ku66Jp`)yeB zrf$KyegEIQZah_?xG8g+ilqEi=Eh(9{P=CY9GcbB|B5mAKKsu-%O?39i?+RBxOD6J z=j~Um*+2Zeqknet$8RNPZe9Ccyj$LY<>HCwoZp#0UuIUc+aF{ru;zXG5%X7!>N3(l zYr<+K$z?qHyz@T4#fR4S?2CS6*V}){XPB`qTB=2M`eINY6SIp6+DvxOetxzyF9U-e zF9U-y3Ck=?i!+My^K-#_$?SL!j?NyAF0ve5WI4LXa&-1^bdlxgBFoW5mZOU-M;BQR zrA3xZnUD_Vn;DK>R~-b}_E+s$x_Lt3!p)~xkGM8h-CS-hfpruB|FXV+|+ z{=!RTcVw3U>)r--4fDARbFA;aRB;xmy`WP0@>R&Hf2;n#?^w0zY5wtJ`)gmE_gZ2) z{rO?f<-aV_3wQf1k78F}`AUCr%Qad4!y8*BOPl}ub3tRF>@~yeEsDPvT>Maw-x#f) z)c1p*Z_UNqKh0QOmq&#twfk*d>9VdN{oTFr2WzeOv-Kq_z7Kir_2j{!do0WLu5|h3 zx0OL4nsus2zS=*Ayaw(Ej0NYPZ0>k^_t|aB;8~jP+4{+;FBnQ*_Ph=h=)FCUIsKpQ z{hxC`PCD=;%`ek3{d~+TJBIiMJCU8SPaNMK5_;cw{eb!d>3}uYI{tL1D*X`rG3RQB zTFVa4rKdsH`5=!^=*H0ZLj0)mGj=U@GRSMZO+5;i&rMp z&+Gf|V>{vK^X%7+F?R9`@735EOh1493iIY4HKDc!?dRXS@IU$RGsspzSY_|y&+iW? zI~cp3RQVm-64YE1*SY1e$Ayn7wj2BdvVuE#ri+{HpL!yZ>jYW{?{+sER6SQ>sA0F3aI9ry`d+AKsfpFcGmx>x@AC-)(QV5B%vug-9OmI`Z zd4#Q)(}p$f4CAvT+Xr%AiWt%~Vz)il_j2qw@0)4+GUWa721{1yphFT5ChhEH*gU_d zcWF(WQ~aUdLhHOLwztZ?;5CqccI!R!?UiqqC0GAbt2*%hXZ>=y8Hb-I-~M!UAODhX z-&g8u1n=WddEI!X*Yj50+Sqq@KGs+7`}yw2bA}uGS&wdOh26m2Cu4aMvZa=XfuVxL z13sXAGRV%i3ZC}Soo}O^ve8c2Xs2wnQ#RTu8|{>hcFIOOWuu+4q0}j3Tny`!ZS*~2 zAkwzK{Zc?o7M$#{`cSaKg(T0EmN!?9mrKHC}28zVToYoogJZTzquV?v<^@Xx}Cm!?JL(X zwpF_>dS~n@eB19GG4(?0`y*xl>yNHyc3YAes;wM!TejlEv#bSM?5D4NRryUI;={52 zat59aEbE#lT(!G7L#=k|g`?6DEyX*-{^|1G-B$f5nj!yy@&~Sxod?Z1zT_mb-(fm` zz%#Z61*HHbg3nCdX!FXe@aiIVn$Td9hZep>u1X8AS6CObjx!+(w?DNaA~ zO2nRg>vYvAD4TC_>G0b#>SD=Z|0er?xpk|<$4F2H?$pM$xCXHjApNyzhZ6H)y4hR5AIDm8{eUGVk)y7$K5KPPR-U6 zKaZRa`>GN;!?RA|IIG&t312vk)r5=Yw0Vlp&^caM(0Fsdy3eiyPkvo6*m{0l#dDTz zx3kF@XLVh=g_U>OkxK!=6OVO0+;w8-`Nv=X?tH8wC-JV* z`1_vU@8`V#{Y)xbx6{x!LB2p~>$c|fPD9=V28FFPj3qo z#BQ*Cky|Q_4jUe%sU|T!FGe*^CI;P zI{U?cmNUmQ-A-LP{poM(l<)xN-~V_nJpVOYLdt>Ng1IKbAp7n9DN)ulX5?&^68-D_ zSBFzL`5Z&>fvXM5H&nIXGPu7ym=|>OHq)=GJo?jq{0_>tetGPP^DpsVYHiL%`*WR3zkZ5XyX)D{bIUey zo?sAfxO}ML+`1EbDqYNJ4dG9=C;qIH{NZ^tcHIGn^u>DYc?{>Ps}{UF_?BUV{ER8j z6B*ZS-qhf(x-W8}`m0YiJa67a@tlyo{$QOUmUrD|f8@T)O#V#yE=J1jtiCHtS$Jb# zp2=HeulHD%rK2e1Lm!vQZ4rCJWfP-rcZPhb)!o0ARUuN}ZWFU?gNEU=2@_7UK8U;7 zxNmLDULC74hb?MMk8dBDp?-x=;Lvkcv*3emuO(%dUz^9|S1E8ueX>T>!JL_X0!Nm# z@NQ>pcCZc#*!kk`$rX=28)k-2?aT1oEpnjkccbN6cDbM`g_iUC8PsG}R#ZLE3%{rO z{^n#(q1N+f^M1yhxz6k@`oCw>5096Pm*e?aGavUq0=FJGmF=$Tm=MM=zn3mbJQ^|J2RI3Hk*j@I?{Ro_-bI-|h-@pFfjIJl6 zEm!1UFX79uH4rPX|DgV1^P6d#_r~7SpQCe$d9B_%_APQh94!)ecK@h<$#|((d-kH= zjc*U!KD0=ib^lUwAV|>mB<$M&<*bF07f&a=NR0r=kC~ zz`1wXi>@S`Js_1}wt=NUvvApKsSSI>*yEUj8xj+m=15-q?)CKN0oDYi0`ZEq-#2fX z7Rz_5vF?G`1$LfSkDIujGB3RO>IR$ls`(LmxrYwyj6RV2Ahv7fY_2=}XMZt2|8{i~ zi|?yF3?hxK^V*v^pSs5FSh_NP<^pW@(#@7`l_}% zu0*{+kN*y%S;)or879Baooq*``|$A$Chl2ex(x)10*u)46UBA?lh*}Adc z!LUG&{Yhi$p?&8Y>fc*mSheHds^@zqU79d2{nfo?JM5Wr!hV!7$(~ZYKSle}qusM? zn-u;?PP(||9J3sQ@+o261CeX*Pnh22A3Eb<(GR?jd!0kOB~8`VV8O)om;8D^9c1VKu<{t++*>o&J#RmdI=$Iq_Sv=5ZW~WyjN2h| zhovw4^z*IL<}&?UW*4(?>@b2c7|e6V&!>8?Gi%PZL@^nYUbvrAkmQC*2$Dfar! z1%2*QH?S$QpJVoJ^zNOdUb$52t`Tfl68`9&VbtS(ZiE2<=|2cV=_|XW@ntjnP zw*_BUC~%ftdT@R3jA9w_BWX(fHG&l^GnNF3ElrDiS!%Vu%c^uk*H8hf6XSlcxQz-us?mUYd=(MU<7iK%q>}%f^uZ-#6Vl&%Wvc^WM-` zjjDG4{9iQM$^TDgaQ*2Xc}I8pc?JdsEQjYb6nVSl%rW?)$iyiI7j`m_ld$FUv*wJ3>XfJlO7dzUE9ZJ1e7Jo=D_Q(v!j1YH;MiA%8>3hS)vfn|2J)B?aa#jdu*i%H@}zNe}A^d_`do5dOKyMC&#ul z)E)@czI3klVAq4FyfZ2rbpbxg5`-pVG(Nie$geks4gWXC+M z@p^;jgW?DL4?-VQ=L^&}fB7MNfHi^h#^U=G*O!+vF=?9Yq z!<~!2Ip1bv=bbr!;63X<*R6c(A7A+D`JjwV=F761TX!FDeZZcuyzh_Th8MQQ!W9A! z!uO_6+avj5ePDB#@!Y&SQrXfU-F7c8czdh*z%&m3majjZ9h8~>mA!uT_vYG72fBP1 zxdr!~s%j`cu=Y&X7G`Dnm75OuR<^9)B$ptw;9=!^eMWAMicPZ{%kp>x_eBe_uG8J2 zQ6XQ!bBFz&$A`H6bIflhWQ)1D zWsj@%{ngE-e-?>W-t3xl?Ob2xt=LPlo7b}GFuiBGJ(qtASLNN*ZP)kSe`e}3SM>5c zIf)K-gX9aBnP0iMUpdWacdgV~wBzwwhxu|6E&VSUw@;lH6e<#Rc!u1HqwQY~oIBDZ zcA0tWj|p-W6J8t)4pUjeel57DQ*`DgC2mDqgDEr4pZ?;obWxGF*d^v{P3xJjBMuuZ zVb{3HT3zG8&$IN&=Q}eD7jVY(zi7;hv@59b;9qm~5 zu634)V$D?zQme|gx$y6h)R=vAf!vDKS+fQ0Oq*u)Hp%S>SLT{^Afv_q<3crqTg%yP ze8Ve^E(xd{(pS$obb8}2hgGcVjkl#PyV|L8E&AFh+ZkBkp3SIeJ3)wb_Bwu7c`-AzaiK$P!i~>=7RgC;yk9L^oxY>ecK2H5{Vz2ur9W(wi#Y$n z?bB4fg9rY!{bDdk{J(y|@mI`?()UT;Z7@5Uzk;73^lM`~*Lnuu+OmXy`xzNh*U!2% zr~BIr?459-5^pyR&XrN~_!$^x@-r~lk~@{0o>&4|06vpHZL|+Q+6N!)gOB#XNBiKT zeelsf_-G$|v=2Vo2Om;>@FS2u_?{WD8Ph{$j@v)Kzu!Y+>N4HA3y;Jff5z0d0TqVm(W zMa;?DmoCG1{&wNT#`e$E=Pd6R&-+|mr+>e=XX~_GhJO#RJ+M7B-7KLt>Fv3r4|=zj zEnJ%C!~Q|BfOQ9#4&yYY;sf^&_#RmAke~Fnz+&--_gOx1FIn4!vsYg0&19AfIWF-* zyg=jz^PV|D<=GqSdh@@k$pzQ4Z{W9Ju;JUY_!sj7@7|Z|rJFNb_a4}Lfa^hPhwPWk ziHGBUcOTl%>XVvaZTgPGK{SKmKI8w41CbBh5^R#2qC@6i+M$_n?6p024zt_WdvCs) zGf!8o=Z#s*wBI{)zIQ9{1O9|4=5W?c+WITwmhD=0>)~9s8ebpAXw~yf%e6YU9_~!< zi2E6NzCvO5rH8sP-Ut0FzPem^=5@9r?+33z{I&Ku;wL_3YfheCczC^!c8TX!*#=$C zJg+r(W6aYWW_{#cBNBJdAdYd}$qj3+SN7{{ofl+1-~O%ct(Us@SaK##%oBQ-cXf8n zSLV6@^cga)v#1yEyVtwZbYt1v_OHLy8uW|q6eRYp5_jlhXg=_l%WU$i=-Ep8jBKxu zZOOW*`|7{oj@WNo)DH7rKk2%gaSj{X7sKr*+xLqm6!-lR{xIcUb%XSmLhA;9E~Y@| z?6>PrE#kk@%lKk?;MR)WzLTFfdIvq#DL7MnHd5D4u%cZ*zQAy)R^1(#RfY-H?e+J#H6`cSmpZlfu-%=z8-Cl^T~Ie=o}=BE z&lnaK!{f2$t~ig*qO0cBpA4UJ?uiI!*+1nNdrV}o()|3dPn@e_s^Z@=#qiZ>^zsX= zSZgpp{R5-dqjLQ={t~8grt=K`9jjz}k1qe%xasDaiI?_SiOoO89`Np+=)xDG<`u5* z?JJ%r*Cc)LXS%)l$ANU##v^x`TT;VW>XZ#;A7%fheOdCy|9lL6OeU)j z1tuTC-tnJNSqF{dfgm`{?@d(O&;(uYa`HKicab?e&lL z`bT^HqrLvoUjI<)^)oMq^!ne-@Xrtp7dckH-EO6xNmB<$D~riC>!XsFF1eo3TVPYN zP#}rZDe#1ss;)@VgM}q`9mJ>V9dQlWB%qwXc->Jv7R>1QKPhNd)T-mI z`+x7z-(zq5{wZshoW!#;XZ}8$xAXhF&$Y#KX9`+}Gi+lP7v8Moty8AQU&5%8rhKq8 zJ?YwYo_jNIUOKkCvAXkSrRg8G87#{hVmohgnCfWXbN%nSsr^`vR7b|4q62ve+#e(# z1U>ROZu+2LGouaLHRjC+v{;Q-Kfn5C?E%*W(*pL2MZafn{-xJs6Tp(kBz%C`_DZtS+psdCywMwvX)}i(B$@r3LIaG~d*){i%^~ znEvV4)OFP%Q_@QIdOnYP`Zsbx{7Y*Vo>gss7Dr7Kv!BDw(exzN>sMkf`#UE8P<5+@ z;#vD=E)e-2lG>vEP`#yIY}rHB1c!8?%}xT9i_Yz1@MD_K?A~~qk-vuj%~i=>AJad} z5A{!F3TXa);Oc@+3s2l`w0O{)g(D`)$&;Pfi?2EnU6dQ0==0}ipr z%ll=9)}+}k5WlkN@&Vfid#qNiU{88}?`7kWoUD_NqB(y{%=l83pf%(7`-c5b^;sJB z&w6v=rEEQexrx&GJ#rtmJ}~_twBrKD_T^vfC)NvCOb&M0Cni!;w`x}9y?q;d{!RGx zn*Bs*#Mf+QhP9uPpMUC%G!)m_7yl(kutH^Dp2yQGA8WhB7>hO*x4ZCtZB1=UOxTStlgD)qpjAdeGrht3$Uml_oImn96N6Kak;g@Q0uK56p2p z9qsOBYZ%AnVEVxOW#dJinsvXM59CXJIWQspoRQv#uaQqKE$-Ukd1k9yyrq_@@$%nO zb>f-rd2HDCG1Y0jY`n3PG4w!H)q&5I8IfNY4jfxm<#2KNv(Ic_4iw9tUd*obmjB1f zoK889-Sz?g{8QfY|B3(7u(al5(&nlJz8zPXWFP!pF;DKpw~U#k4c`}6RW*beC)(OR z`2TTQH2ajyrl_g|!d|OIFS6TysC&)WzkG{*fFu8sU;C$z<=eQd<3Iy zKwG^v!*-$2^Im6rZ({G%W)^w7J;?f`8p+SV5W-JErxvoDJcNJs=v4M-r*^bcJKCuo z?bMETYDYV@qn+B(PVH!?b|`gfS?VF3+9Qy&%-Z)`_f6?qswc8g<%+BBE4Ko=jzt`LT9F-kIWG@&om1Oo*mucwNAQLSj&0oRP72GC zf|@oaS?bx8SABZ6@B2HI5PhEacF*eG|FeER@4l^{%ZWMJjK3Q{$1Ry_*)Fa)<;>2) z2Mi73GmYN&S4~WA`!8w1H-}xUbNcfJ%LEpQ(oH)Ti+xbrz+%JL!>ryI-*~)H|BQke z@1+mN8_OGCAF!)UdX@i@;TFT!Gdr2%Qts8Hi~fj{W}L&s#}xjx*2ntRM9Xr{h;tvm zzkFjLd;FDpgSP1d$<1dL>M*=#jA!D1b>e!%x=dsKI~^ZZFFRYhnQ2>}U&hvDr+sfE z>nf*h`CariAdcak?gPyP-FlW4*BfRh#-E+v9cuhRN!Vd|!}?ja2W}sDI!ip$-OVn@ zw0Xl5)|#tt82Om5^nU#(F_Tfyn@{_~xjBm);&nbY1y~*Aj$^sjn0;U}=e6w9CC~0S zwa9z^@!QOn!?1hr3IB`Kw^hSu^jzdd9i4 z8mn(TSa4{v)uZo?=4W{LUO&8YLNX=aL%(88e#@)F?G4cftZzMxKaltLzxf002dbv? z`Wrbv8=hW%>%zT%3{Cv(tFIZco?)2YDEh$qLGT071#DjqeUjd1+9LjCjr2U`w8r$W z4VkIudKq^!tUqx2S9R3dzdbjb4zzEwPgG62Ftg=i40D?C^J8bd*1QNx{iP=*f39NU z%`-b+%E+IR?<~7eVrVIu(ehQ?OW@~*tqGr3GcX=bL{->E{Hk>h+ zUVNXzm*Bv?y2s0&zTR0;C;da#g574#+=;8rd*ps-99q0QrAPKba@B)2JN*|P;>BMB)b@Bx zlzSnzp*YOHVxpf9{~Mp4=c!c-f&yE0yI(f;PMNGzvgVr1W#%b!nriKqv6n47^7+h8 zwhJu#bT2S>r+!rUU}(F+(8osq0&{4NLD;LfCrSl|>Zi5S7bO1Fx$sq~^4~wMZ}SdZ z-zI&Y`^Qoi*U%O0ZC%e<|IB+ZPxb!I+x=~EDW|&U$OQ?1>v4Vlb7@Ox&nw2ZfUU-= zRSIhyZfd!GJ+NWbw#Wm^s{)K~9MFAGy7&I_Do0@n-3P%rD>!@Dd~bBMT)s4W;w}Z& zKS51uHqle$J?0+de9-l)WS;-WrbB-YtaH)dv1(^SM4jSM?rn4BEf&RzJpO*?>mP?5 z_r*WFF1NK8c=tVw_5bppjRpJv@Bhr4TBV?AAF%zSP4TjjOExMdzQIEAwCRg9Fq4b-7lCwjUbP zJm+xkHGYX(dhXz zrJs%A#XlQG^)KD!lRwKpW`=LJh@(K;{F`?tD|AV>tYH^Fqa{^5p;M0O<70M~V%Z~;AIRUZ zSKw$`psueZXESrL8jE6kgL$f|+@G#T9D)-WFE+5autf4#x_70OO@4Rtv58LT-K+0& zN^?uE?iN1!K_G(R9K&9wlLFC7)dqH5iF`Yla~{~|d9yv1{*=ZX@r7l+*fgUbZBB)e z-78bXe=ycuuQ~JL^fSiFj^>YMObo)4o;OT)kp974bJ01EA^ogo4olMq)47Xu-v=kl zuzzFNpHly1j!>fa!(0}%IrlSUelWjbUB`5MZtyNajzhZz3vBQ3+vs^LC~;uf^D&U; zdgILlI{#E`CFb96iq!qlH?L5EBR>D2>4SMfX-ggUNhWzWtTg(e(f0d*=+w(D4U^OM zDdbOXvOV8WUZrqA@xki$dwC2q4H4mSj~b*%%buuemERuFR~N3bN=+F zMjQ67UqAisu3?_T@NU|{>m{+XxI&+$d{(dx&i@}F_Wl#&`?_}~YWX$3+}Q^zozGv2 zopVGs_W8W`T=By4Gm@>vtGImQo(8#}*S`P1Zk@k(O3Fp?_>YZU{2@@gG`*)cC{Ox) z&c<-p?&Yy!_N%@#AFMdtvFyuzIsV$y28$H_thmo#@!0)u9Yg$|{frFXJ#QA5bFY7e zy~lQ<(A#bDJEJBOUIqqzUIqp;lGhDqB2kqg}PpuG(lB)xHi(bhmFO&0Ce1gYMOO0GH~AHDz!F96mtHpmj6ZYpA*j?s4_j2=x+;@vOk1e;NMn0bg=ZEYL@dIB z?=4Ep+u7y+s~}$WCFA<77p~lA-q!M-p*~BfLf?8ROH2&^4ewK%Qq(+uwMFnT-tWo3 zE%;#bpV^!8c@1ru*D=0t=)QI5?xffUmJ_t|qz|Y*FnjU2UsnHxJ0Ej=sz1Z}hO4KJ zZ9B{KypcE8D0g4w^Yhtn4IRuTtAyH4VA^h4mz1}Mebeq^*IqW3w8%wFI=fh|qe|gb z;|lggas}oKlMg$EXC-D#W;EH&Ifo@q!i?vTwXMLJ=eb`UW*rtynC};)Wqtg6-}&;! z=BV3Dbqt-8{@-PrzgOq}K!B4?X@3J0Vgyc4inf0_C7 zCFZyvj#Uc`=eyeqe08|O9LVotdm)U!q#^!5mqsb;ju=VtW9yHV+8|{gW_QXbeVxv8=(Vp07Pi(X&Hk5i|EX9zX7-*rZEa=kS??3-+ z4p`+Rr7X6)Vq#vGQ(K0%39BGWrDyr9E>Tyle8nj&tScLrU)goxSS$C&h`=dn>d#!4 zNEFBmsgtJ9blhWb$J+S&?CN)x-{*WjTX?Tp z==rPzxd%dC{pE3ZG>2X0L1W?q);G**4ORvmUzXpq&-kF;c=v#a!Ln?lSMCjw54aZG zuY7;8sZ?*f*xlP3e$Cv%V)wOf*(3||jJTfF_3OOlpIsGxKI@W0Ud^MJ%e)7ISAN&` zd|G)s?15`2VS}|?^x+Yg```OQyn`h5jr}SfAmIY6c@&R49OJT3}PA)qZcEdNCDS%;r zgzubcjvt|QwW|Z;r6t5}yxX>xxjbY>>R#=p_p6!28>Xk8?qfPS;flzwuo8RrnpLju zUp_Jm+Fks7BunMc`}NE2?N>Ku*t1yNefJl|G9L!t%?<~cZJ2djwe0mBng10p4~Sup zW374`WzS&F7|$@f@$mt%2SNp+AJ{)w7i_nyVE<6uQXkb_lgVNs_V9d2@84w&-MjAd zeaU=y_Q2JL7Z~m{9BMBZ?$1~w)^vGN%xd@Val2<<+bw(Gx6#?mv#b`` zF%vz6x0yCEh&QS;%TI9UN&V31+s6Nf;n&v7dkJl>Qm$TGZn#tc7fCQbzbw-L`?CY@|1aU(aNYVIcIAY>Bzq@UAaSGzQ$?xr26w` zLCWLKu4gnCJJ8Q!x4|#dB;QWn)LQQlyW1lNlNI8R5A5uzvE#DIdG_|A-vrLjkHmH4 zUofm^c-<)9Xm0fBr@Ply?_Z+MHVhA{I482#_djKhPM!a?>e~v&c_H@vS)l~gH}`*dongQ7%(%(jT6wuSPpI|WkNY=Bi7g21{Lygrz>+0-lH#je zyYnyn5c=To<>NHYi(7k4?lPE7dCJT^@mBF;@9XSmLRt2&)seTzKlYmIfxPx~N+D;42?J=#Mb?V*qM&_{ddqdoM|9{OkxeYA%@+Cv}h zp%1AZx;>~UNFgW=Hz(%)c&-^nSH;{ z?R#|Sw6(GI|MYn?EA9ThPhPXtq{ouohOx%&B*#^)||OosIy+dqbV zOlgg*56nJv|GM57miD$vBk;%LSN~RhFIjSz>6&r2VX4KO$lIJOIXxHmX&=4ia98@i z$ARmNs}5{-aPR&t%<#-@3!~M6d*?-p^4zy)>+S=F4}>m!@0-E$ z=lX#%)*r$Oo4@5eVcDg8E9Kfgt~(5Rul;^&Ry-(wP-L3qb9vsf#buA)q}s~pca<6r8ZZ&VTEn4epoBMz-U@}v8*8iuR(Vyhh|4guXq`7zg$6x=Z>hLm8?Ayd= zv%Iyc>dv{p8zUdsKB#>#M}3PX>#hU-jfuaw{29aNNbfpY{cH0wkBVsF+f2JQGH&p{ z9&mk8yUFwP2ZlF=5B&2`u~C?L*x~_C66225)_1QQnI|@TV!+}`e$8*YW9Ls_Bl|#A z_eI(J`A+d(jDHyBtlS|nA+kEk?7_P|b1&ZutoJ%}j%m5=|98)iFg3`}b=KdxP+& zmjBcFHF$Qfu;AM-_L$j0(=MRDXZeRl)&;R`{4(+j9fam<9A-CDx}x36FT<=CT5I=U zu9La@Z_E7c|01+44#w>H!D6VaQ`LUfW6gWD2PfCW&Y!_wv3phvH_uO(6YO>#C!Swd zE{~qUZ_*>lpxwv+Mo8Y{2MhO~pPNz4`M)1ehZ(zTCU|Um4>cyM*!+?0-5df66=% zTwg0#apN^VSIS{)L%W9ZlE6PCXE+}LKg+H3Rrj78i&{@w?83WC2jrUj@Ml*fzoZ*E+D zAXG9T{wt%>{g%9(V0oFy7XAqh^@2Mhmf1N3DCe9s=zjFP@owrXX719TzH4V!KooNGk+bheekq_U*++C zNrnKQ@JAjCJQcC`w%bd+-A2%jYM@Y5d82O=Msf3N2H!g$~5`~Bz}d>Iclmp1KI zJzvdo=EVKDgtd+57={1cKgci5pO+@Qa<3B4PcL0*<_%vHq$8%VAKPbifVJ}H2A|b( zccX-3S*JD575f|YN@s&=eOtqxhk^MSS-x}G*D>)&f5~2Hd_b>Z{gsxAH0xil57Y>~ zZ``^SYQlmnksie@1Fv(o>Q~-v8-cEKCr9A;-bTR zwmydP#!VNuy)tNScF<*h!}z^+X;_Lv_ooXn!m;LZVG|icWRh2~#hm-) zi>E*6OgX4lu(RG?cPnaLaG}kHhW5D`Kg!XI3Ouk0soGg^S7TqjV-!Am0j!L!z&LOHyxIW6N+Wu zm1-HY;>S!o=~*@pgpQVa+BeKM5?&W+Aa|76@A>bJ)7SPfYb-g*%$@n!LH&cGuiOuo zH_J{kt2c^gemY>Apz14kgJIgGCyh%r%3xKw(YI>I(;36$BA+q*7CpDoMagy zf8t?8{DrTLna6)M*zwO={Q zTAm|YCim8t9+#zw?MuZUMiy-3Oj9_+Z0lD0pgzr!Z-?wr=HpQzZi`A(+j`gO_xzV+ zxKkC@eTL=ER_tBV69#5(D|Rx)9phzSILOPuU_ksta6x8XD&%0JgS>X5yJbf^rK6qF z(N5`Tr*yPaI@&26?UasoN=G}TL#b2B^aj=`HO!WF6lmQqsui;<_+`jS=JF+FvH|kq z?=G$4*&(uolf${rq08aGs>@5nRt3fUYxbD#w=~GN>&>avSKqw5^Rr2uuVmKl+o|`? z%r=M`rnoFcBJvgNnB>;KXB`Q-8+{wRcnLSE;Fv&xn)(JF<0AG_)Yc$ zwjW%3y6=DQpLfGrFzWfVVCH4o3HPnO^KbsVm5ERA8N>ak=SpAY`9X08@^JM1Y zy)Bh`YMTrv9QLWqTX%D)+#Ms2eVJ z5^(*?w}VN>fsN^<%bfpv8RJTorvIyBkI5|CfBfCe%7jxD+6n*IcJwaT!>ngwZE%ip z%Z!tW^NwA~5Qs4JPtLl+{H3#V!@t{%YqBqizYZ$&>d3gmtZ`H`JAdx&+5QG01sO** zb?v5|Uc+7!aF==e+T#7e@BH6}*#@p^+juOx;!{WGl9!p`UtJ8jo#dG3D=p$wDt;}$ z`W7>P2K$V#Pe1IQy>n2R+j04mjoP9c?-XZ>Zgx2M+UGE1e1rYYs{5ULICN%I{?CzD zx0@lf$7K4kU2&KHM%UM~*PLc2ceBy!TblcxyWL6@S{d&HuxJ$hCYHF6&ko zRcm?ZSQSq?d^&&6mREJk{Bvp#n{F_9koxj#md%1?tFq;6OV(VSc>8MAyld6k{25lo z1@RZ!*}ZCyE(yrHYP0SOZ{)m=Z1))HJ~@Lg-?!SV|L9U{x1hjry-oe4ngdOdn&*$d z;Pxx2{_b(n@w$BX}QvznbnS4^*xG9Jm&S^6Hj^N9LUcz zQ-8*D=8bX^M#86ZljiYXF<5(+z34OR<(dU;nYnfWv-D>~Z&uuT@Y7wpw>xJYw|#S% zKVY_GYEs(m&Y0`_a+?lV8GBV8*feji?SE^Avs&Ug3xAin;_ef^D!S`*o{xdy6dwbF zA&GrLBk;1|Q+%eQGnJ#A!qHCQXs2+rQ#jfw9PJd2b_z#3g`=Iqq0}j4{tD?7Zkb`2 zA>%I6wtwP!fpt#~t=Qr*@m#`W+7EY|hwP?EE17fzk)j4eW22roF z$~;JX8*`w>SzNG}UuZIO%@Y0Iui|sP)|8&@KJ(i8_W?`&MVRc%e74Ze<_CF8-t%|8-1o}iz#s1i=ObdypQ-=IGJj=4$idTG zYN9erz9=01EF`-k!G0BUW8SW{`VE^Er(J5!ELEstd!rKfBxj+Ncw*jThGxlUjZdfW z9Vua&!EoBx+f8%cYCfHaxePHkUe458aDFC7?lW!;-?)WmzaG7%GJoZy>sPo9+LDyXo0NuG=|co7w;5O%CVl+IdMgUv2t=zgoLjKG}MeHS#&1iOs!P zUG1Wiw1n=n#4&K6`uTPm`x|D*8$}QL1ZO1}so#3rq_*0>;rOqg-S3WQKPVA4y3Hmj zcYm7=pVMNeOINN^tqSrR8`UOp_ZfpCBcU^4soA!BiuatD^onKTcmE!FF=LMBFGI-a@y{R6Y^T#uIr zVjS0&_skTJ46;Z{PYXSN!d1V>O}+nB{WU?=b1sJGlgpjL{%@XVFj4%?($F2|ztlsy zZ$)if`G4>0rp$_Yt5}z>?eWrEyUp7`zwBtO-rAt_W8FIEmzb=cE3H~#_f6?~@ONh8 z)x2MMdZcXRnXmu-uk-#sFGIrAD-zG|NEYGl9S79^PvPQcU|{7Zy?1O3?j5u8{~Fyi zJK8%Q?HzZkD4gsN2~_f)XzF1Y)D@x->LL5dwus5HJ;rUKE1*sP1q+Vy+_8>1?FF za{`m+#w8(_gf6*V$_r9{sk3sniHDT({B7s?#?<@ozVDq`dH?_azx(s$&ROpIam8TW!z$k6 zzjntTUeLB!>X)RvmX(Ct6T<@G9ZYW|CZ-=yKcM_TuHbmbAJLz47&?mc?j8O6RHA}o z4fFYz_3Q_<8=lmxYCE}}o%@Mfy!to)a%KybU)OeaU$fNwDWaU>X?agQN5A*!~ zi9|4&ttnPk-o#pyH#gcq>F*8p6{`;&NlxI9abMVXT=_xu1NH<)3C8U`9jtN;x80t| zKe&A&onuevS&vndnOO9hnIFVHaNV1pG1u7Zi0|t=pAY;!@my@fh0_yWg&xh3n5boX ztwH+QPS@1>)VYT{cp9>;o(sRevnkF#_#JOUcBphvs#$?_MEHq)pC>TxKCo&#`-ZCd zu?btJ&nQ@O-@MVj^dW!K|Hp;=J66YVlqSgE2%c~4_C(@`#1E+-q3?IzI&E_?RrO4& zzgBCq^27bBD($YZUT^;5_p+fqw4qt6d8PUp`FSBTk22ny>CA3cZJDGwaUPSnREq{@ zij z&d^t{xfOa?AlUl(vA-*#^!_p2VCxIodaxrWvv5}Bmfiz$AC{Hoo$L^BQ@rs&^=R$N z%e*VL`6tf0ouDr8V?x%B^O?JavV=Y@__0QEqI&1=+pY>+FPAr7*~hkr{|{fzuL;>! z>zMLim&!UZ=`8KIeMNBY;XB#8PrW|ESHrx=TF23IIbICmIXX3$SZ`r5y zo=9m{O#kz@SH?i8oiQ%>tBsWAZ@+c&9G1dGNlcAq-29FFjrzulcY_swX(jSo8gi%m zSuRwca@@2gkI@cXe1e|B?TlFZizxzD)?r`FAhn)quC z`x$A~*fh9#8c*k-OTXikGzn?$uA4bXg`cU;)!2Jvh1U$zrytk+b$HCpb+kS%H~$s;H6eWsw>svQ^qtFW@^165xS_gr z*%iU9&s^5YU-(_`lX2E<{RN?SZi;h{?pvHWV^e?HyACBkpDoeO^G<(WNaYW%2Z4j%)(SozXzzNocdc<&l+efI5{&8Kcp zFrV{&{{Oc^hlCzZ+P1XKJ6u;b;@RFcS(ck@3M+4OJr-rvZVO{uW3!`cn^x{s|J859 zr?0Eqt5)Tk-57W9RMWw&jM=ZbL%E)|*1o=T{cWs&gns1xt%<+d=1SX%c6O~3xh+0j zwEl+1orFOT=;tu4Iw!?wKr z`pxyW{`9jNw|6&hbKbdlzs-(4smo8EJ8NwpvLxh7$gdEycbD#NO}zdi@8Ub@>OJ2~ z-dw#Mz32O1yOaBFhcbrxUQN52`rGsO=I{3FI#<2?G3|G5>{`Fv!qMLKQ0iSXAAd2*5op;g{P35|!vjjWTB4U&nA9~oBsK_lJ1AIk zbMtnjPV!25tJ8D!=&2Gxmn|1I&MbeE`|ZB0pm^V&>6M@Fy{j|6U$eP`L;dUOgsn~U ztM0B$*xa<3%WB=;ritfyW(G=MW_&iQT6>dFWuE8{2AfsAKRNd>mtFa@%Iq2U1{NEp zz6Rq5XH+)K@-r@e|Nn2xc|TV7bALB5b>4JkQC2z_8a?stk#%4G-d)jqlV#hNZ#zXT zU;k`*yKri|LGPL!@lBPd?6xtVJyr52%;$N7s&WuI^6t= zA@>_6!)J=t3mt+VFZ0U@IQZRXgPhu*$=|}1ZftAND+vC7>=@gI8%q`~Tkv}RmhD28 ze?=_VYnbD%AA94X_QCCQ&5Mr*WFHh?lk0r5uYabKA%~6rbcLz@0`Vy+zBzj(<5| z^+7xA{n`_*+DS~`*3H&^S1!fzMw`UU>N{iKPj76OONmVge}DY3(gY_* z+X;`$dVlo&l5hRu@zL3#*>%rCp)5gm1$_ETPq)vRE!}cbt z{-Sl5w9wOS#-8)s|L}L8XSeY&ciK7U)Mw7~?AsdD!%~)tp1gQZq%!Ubx7k+5%`4tt zYbs;-y!4RG{AV=@=exGJS2;-YY@hS3@wIB@y*D$=wk($E|JZTbbXn5^xetcg7nv>p zFOl<@!=E$dRinr=Lst=N)#y;$4RW2GLTC0@{o5V3WNwO6e4c;tv&HHQk3Q#~EaNoo z%m3yXw%2ZLUnJ{orS{GF;}X`4!&gQAZBY8W)v$S^&WXAFDZlC)b~mv6{(t7DQ($?_ z?7&-QPp`em`t!W+X=UD@@8ABsXXsPSGxswu?!(>RO)o#lS;o)6P)OPt%t-xR$YSe4 z{=U)ahQA6fpmD+%y7(*4HaqIf19o6 zsB6s5Y%lS5Er)pW7itu0+Xx)Iq%gH%VQgx!Hj9dY1k2KnuGLPumkxNJaM}_XQSwZrjz>Y4^p^Bg^#bo(NtvFR3m#6kE@%I-vgVtTH|PG;2XY0v<$3%!*nVa0n``}Dy+CjW zTTg=~M_AGx-|+Q`U#bI*m(4$@Az^>QL8rRXQgGwFWv^Y$ik@$4oZo1v|H&mm|IytO zb6+#A{#{|b&cC%jOcakax*xN@{`7+&w!=K@(l_M)5X()9OTIs+BP{a8+ct5_ z)z%yM&QwpF{L!y+;?g^oLWcDWIx0nvR^Qp#e!xlf-oe>ljc)XnB}n{Y*_U{x{M3xs z??RH~Zk?WUV3x^@0`um0;~#sEtv`{F@aG=4(9gnCb%(lhBs_O8)iB**RBPmY5ccj) zDRU$Dff`QNyYGxHrZtNGHG8YZ)#SQ|>kj)o=4*RTq#j<+9>XB_D#_}Awd0xjS6OWg z-gfH7^35x6{%v}=p69u0xQpe+w5Ke&p3CwC-OjM*zHR^Yxoxfm4}XrVK_Xl1g2|75 z%#sRZDF6CVOTqEX?4n2YzYW6=&$W8j|Mp~5fz14l-}x02({=VI$2+{cbD`Vj?zR1e zj5~{CdzBB^ZFu3TCcH2CsSz83UKaOa9KCKw-&yx9vFrEiEaw^i4^1at z_-4-(&+wjADnIz>$*OmE?jQI*@!0K4&s`t0SM`Qhh@70>&Vr^?(s zUZJ$YPuRYuyW8u)4_Nk6#Yh>^3>A3}QhlRr%pPSBi;;3i8BdhHuc;%O=g+2ct z);U>^IQ5N0&)r@nbu9W;!{?&Yy7N|CWzPNmdr>c2&Lhrl?n5dud^_%5`Z}9)PJ6W7 zN{veG2c6R!r#F16Sr8?C>)UtTg1ncF$38b++x2fnmBKsG(;pQ@*Si^L9o{7I;E(g) z+Jpx-PdcS{79XCVzUXKASB51Tr$25w$e5^K9%7sDl{GppW!WP327l)@a!&kr*p~&{ z1ggzB%6fbu`>hWyLHskGGq3!2uY1l#tZ8!U+rN8sd*|Xuz-I>PK=jMK%`@W`F zP;lGhN-nhMC8MW6j`~6d&&9)QZExC7yTP*6; z0#Ua48+RryoL9Q;``WHO=DKTEG^=vokKQ3=<8b(pm%tG=Y3|;rDW{j6FHbyUV-PE` zdDh|fM-PQQ2!60EFfWj^2$y)=!200rp&9!d%de(QzvaXHySJla!9$mG>JFy7JeCt> zB`4^;mbYV#31`22K>k3o!*=1D!4k&vHdIa!xlq)g$@toIZ#@4o^^&|(=iANd_C1?# zrDJ{f-{u2}4}21RPQMR&B*orVa`vS{>SPD|4=e^EORnDbVfyRlQna;REQ0A9!{NdO zjc;45cCp-98U5SlgSG*i4fh@HHEcEok{b?ejC#bs-6{zu%4C-7JZP*s!YqAhS19}2 zIeFLBPclo|ez5q^cdy@KqAllbHiK6#Ww~MgDa;+eoBPgIz^{PEXH z`S80WC;F{VKUJnSB|$WI&&(qZOV}S-^_kwwF}TmTOY2NTMxICGwQc*aHR;+iomqXX zn&Cz8I{D6Rd*m&anEZ2|F)8HMEr}CPc1wOMmW^ilrrE#Ukg%OKu1A z0EuGdiI@3&rkwddL+dcFRDkJir)B5ZMby{7{PX%ixabUzPaPZ0m8}FiTMy^nt=oLg z-)+I?1u7T*1|Rq*;{T*V)IFVsxiIVC#YTr#{-J7FkVy*xBs$vRLc0 zK2x*blWl!vGS^qOW~Y|Qd~{KM!g*h``KQYDC%^uF`oEC9>GQK)@$sh%eml6zez%^u zxYcm8=DZ7sJ*#V*vi)8^Og$CusC?q)G2#;5dE6%AuuABF$QE1)_7Q3FE$2xi5pOM|&JV$=j1?G9PZ2fzdU3>oL z$vu9PZI1kBj$e7}Qe{0SbWhW7{i+1j&2?IKq2(t&-SIwQ~Ecew<$|(|2}z!3f2ibcqUu+-_s9~^J5P97G~-3_4lgm&5S>8yC%pd zZ24NW`no+c1Ec*_Glm_Fpn(JiPgg&ebxsLQ0p83kTnr2x91OMGE^Zb-KI%&gFffP- zFfdq_iqWrYX zoKyyeid%Do(?fz^irId@_r=M(qhX47evl}KLXcB;gUCcREk#ZNuMWk9H$?*v3c9+o zNS|2H!Q9u^&?6eC!_&oegZ+u(qQ2(E9bKn>KdfGyeQnS8XJ`Lyy>9n>&F8tB&$A!k z;F;!O9BA6Wv(iaU@ImwOBgY;-(Q|0zQYl~%;Mkm^So~L+k>N+3jg7X_0rq_ij1`x; zIT#N3&OTru)Bd0TmD9`^#)cNg10@qJyg3?1hZ07I5=Ms-Mu!qchZ07I5=Ms-Mu!qc zhZ2U=P=X_DD8auYG*sre{qs+^t}LqeaZ;G9+!(ys z)6>U3mrb1U-8ZyNY{K(<{psg?buUL;u2_GKfBW8^J-1fh`uJU?BmTU<&h}{w&g?Y3 zcu&MUQRU*Vc}5?f-dA9!d12+mbxgYGW}@IuhB<8OSRyLF|0xps!5iV> zb7#JBV#S_Ep9&t_Tk+uJo!^GiRmRVcas8=VTe+Wi!7}d46aB1Rp59|;wpE#UzO!TJ zo!?>4?r<1LNXNbVdynZ+`dNXbH#Kg~ z_e|V2|4OV$HtUr049`OxYyytVRoS3gyzZW>_5Jj7Z_n(lU#rf!#31;PXX?a@{UXT{ z-vahBIi!E+Y7qe zyng*X@c4k#1C9b4!=3-?^4>}?<+1BA?rwB%3={G)2#nnCsJD97B<=%co9(1?6j

{VeGYGQ?flPC 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

ED2z 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

a=2=lg5uFHaEq7@>UdNiq+5lHHDuwru1~e;slWt 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 extendedEntityData = new HashMap(); - // SECTION Registry - // =============================================================================================================== - public void registerRenderers(){} - - public void initialiseLayers(){} public void registerKeyBindings(){} public void registerSpellHUD(){} - public net.minecraft.client.model.ModelBiped getWizardArmourModel(){ return null; } - - public void initMixedFontRenderer(){} + public ModelBiped getWizardArmourModel(){ + return null; + } - // SECTION Particles - // =============================================================================================================== + /** + * Adds an entity's custom data to the map for temporary storage + * @param compound An NBT Tag Compound that stores the IExtendedEntityProperties data only + */ + public static void storeEntityData(String name, NBTTagCompound compound){ + extendedEntityData.put(name, compound); + } + + /** + * Removes the compound from the map and returns the NBT tag stored for name or null if none exists + */ + public static NBTTagCompound getEntityData(String name){ + return extendedEntityData.remove(name); + } + + public double getPlayerEyesPos(EntityPlayer player){ + return player.posY + player.eyeHeight; + } + + /** Gets the absolute coordinates of the tip of the wand belonging to the given entity. If this is a client in + * first person view, the coordinates will instead be the entity's position + 1.2. */ + public Vec3 getWandTipPosition(EntityLivingBase entity){ + + // 0.7 forwards, 0.4 to the right, and 1.35 upwards. + + double x = entity.posX - 0.7*Math.sin(Math.toRadians(entity.renderYawOffset)) - 0.4*Math.cos(Math.toRadians(entity.renderYawOffset)); + double y = entity.boundingBox.minY + 1.35; + double z = entity.posZ + 0.7*Math.cos(Math.toRadians(entity.renderYawOffset)) - 0.4*Math.sin(Math.toRadians(entity.renderYawOffset)); + + return Vec3.createVectorHelper(x, y, z); + } /** * Spawns a custom particle of the specified type. @@ -62,13 +89,13 @@ public class CommonProxy { * @param velY Particle y velocity * @param velZ Particle z velocity * @param maxAge Lifetime of the particle in ticks - * @param r Red component of particle colour; will be clamped to between 0 and 1 - * @param g Red component of particle colour; will be clamped to between 0 and 1 - * @param b Red component of particle colour; will be clamped to between 0 and 1 + * @param r Red component of particle colour + * @param g Red component of particle colour + * @param b Red component of particle colour * @param doGravity Whether the particle is affected by gravity (only affects SPARKLE at the moment) * @param radius The radius of the particle's motion, for cirular motion particles */ - public void spawnParticle(WizardryParticleType type, World world, double x, double y, double z, double velX, double velY, double velZ, int maxAge, float r, float g, float b, boolean doGravity, double radius){ + public void spawnParticle(EnumParticleType type, World world, double x, double y, double z, double velX, double velY, double velZ, int maxAge, float r, float g, float b, boolean doGravity, double radius){ // Does nothing since particles are client-side only } @@ -85,11 +112,11 @@ public class CommonProxy { * @param velY Particle y velocity * @param velZ Particle z velocity * @param maxAge Lifetime of the particle in ticks - * @param r Red component of particle colour; will be clamped to between 0 and 1 (unless this is a MAGIC_FIRE particle, in which case this is the scale) - * @param g Red component of particle colour; will be clamped to between 0 and 1 - * @param b Red component of particle colour; will be clamped to between 0 and 1 + * @param r Red component of particle colour (unless this is a MAGIC_FIRE particle, in which case this is the scale) + * @param g Red component of particle colour + * @param b Red component of particle colour */ - public void spawnParticle(WizardryParticleType type, World world, double x, double y, double z, double velX, double velY, double velZ, int maxAge, float r, float g, float b){ + public void spawnParticle(EnumParticleType type, World world, double x, double y, double z, double velX, double velY, double velZ, int maxAge, float r, float g, float b){ this.spawnParticle(type, world, x, y, z, velX, velY, velZ, maxAge, r, g, b, false, 0); } @@ -107,16 +134,41 @@ public class CommonProxy { * @param velZ Particle z velocity * @param maxAge Lifetime of the particle in ticks */ - public void spawnParticle(WizardryParticleType type, World world, double x, double y, double z, double velX, double velY, double velZ, int maxAge){ + public void spawnParticle(EnumParticleType type, World world, double x, double y, double z, double velX, double velY, double velZ, int maxAge){ this.spawnParticle(type, world, x, y, z, velX, velY, velZ, maxAge, 1, 1, 1, false, 0); } - public void spawnTornadoParticle(World world, double x, double y, double z, double velX, double velZ, double radius, int maxAge, IBlockState block, BlockPos pos){} + public void spawnDigParticle(World world, double x, double y, double z, double velX, double velY, double velZ, Block block){} - // SECTION Items - // =============================================================================================================== - - public net.minecraft.client.gui.FontRenderer getFontRenderer(ItemStack stack) { + public void spawnTornadoParticle(World world, double x, double y, double z, double velX, double velZ, double radius, int maxAge, Block block, int metadata){} + + /** Plays a sound which moves with the given entity. + * + * @param entity The source of the sound + * @param soundName String resource path of the sound + * @param volume Volume relative to 1 + * @param pitch Pitch relative to 1 + * @param repeat Whether to repeat the sound for as long as the entity is alive (or until stopped manually) + */ + public void playMovingSound(Entity entity, String soundName, float volume, float pitch, boolean repeat){} + + public void handleCastSpellPacket(PacketCastSpell.Message message){} + + public double getWandDisplayDamage(ItemStack stack){ + return 1.0d; + } + + public int getConjuredItemDisplayDamage(ItemStack stack){ + return 0; + } + + public void registerEventHandlers(){ + MinecraftForge.EVENT_BUS.register(new WizardryEventHandler()); + } + + public void handleTransportationPacket(PacketTransportation.Message message){} + + public FontRenderer getFontRenderer(ItemStack stack) { return null; } @@ -128,49 +180,24 @@ public class CommonProxy { // I have now learnt that the server side I18n always translates to the default en_US, so I could just return // a hardcoded name in English instead. - Wizardry.logger.info("A mod has called ItemScroll#getItemStackDisplayName from the server side. Using the" - + "deprecated server-side translation methods as a fallback."); + //Wizardry.logger.info("A mod has called ItemScroll#getItemStackDisplayName from the server side. Using the" + // + "deprecated server-side translation methods as a fallback."); // Displays [Empty slot] if spell is continuous. Spell spell = Spell.get(scroll.getItemDamage()); - if(spell.isContinuous) spell = Spells.none; + if(spell.isContinuous) spell = WizardryRegistry.none; - return I18n.translateToLocalFormatted("item.wizardry:wizardry:scroll.name", I18n.translateToLocal("spell." + spell.getUnlocalisedName())).trim(); + return StatCollector.translateToLocalFormatted("item.scroll.name", StatCollector.translateToLocal("spell." + spell.getUnlocalisedName())).trim(); } - public double getConjuredBowDurability(ItemStack stack){ - return ((ItemSpectralBow)WizardryItems.spectral_bow).getDefaultDurabilityForDisplay(stack); - } - - // SECTION Packet Handlers - // =============================================================================================================== - public void handlePlayerSyncPacket(PacketPlayerSync.Message message){} + public void handleGlyphDataPacket(PacketGlyphData.Message message){} - public void handleCastSpellPacket(PacketCastSpell.Message message){} + public void handleCastContinuousSpellPacket(PacketCastContinuousSpell.Message message){} - public void handleNPCCastSpellPacket(Message message){} - public void handleTransportationPacket(PacketTransportation.Message message){} - public void handleClairvoyancePacket(PacketClairvoyance.Message message){} - - // SECTION Misc - // =============================================================================================================== public void setToNumberSliderEntry(Property property){} - //public void setToEntityNameEntry(Property property){} - - /** Plays a sound which moves with the given entity. - * - * @param entity The source of the sound - * @param sound The SoundEvent to play - * @param volume Volume relative to 1 - * @param pitch Pitch relative to 1 - * @param repeat Whether to repeat the sound for as long as the entity is alive (or until stopped manually) - */ - public void playMovingSound(Entity entity, SoundEvent sound, float volume, float pitch, boolean repeat){} - - /** Gets the client side world using Minecraft.getMinecraft().theWorld. Only to be called client side! - * Returns null on the server side. */ - public World getTheWorld(){ return null; } + + public void handleClairvoyancePacket(PacketClairvoyance.Message message){} } \ No newline at end of file diff --git a/src/main/java/electroblob/wizardry/EnumElement.java b/src/main/java/electroblob/wizardry/EnumElement.java new file mode 100644 index 00000000..d440fd96 --- /dev/null +++ b/src/main/java/electroblob/wizardry/EnumElement.java @@ -0,0 +1,39 @@ +package electroblob.wizardry; + +import net.minecraft.util.StatCollector; + +public enum EnumElement { + + MAGIC("\u00A77", "simple"), + FIRE("\u00A74", "fire"), + ICE("\u00A7b", "ice"), + LIGHTNING("\u00A73", "lightning"), + NECROMANCY("\u00A75", "necromancy"), + EARTH("\u00A72", "earth"), + SORCERY("\u00A7a", "sorcery"), + HEALING("\u00A7e", "healing"); + + /** Display colour for this element */ + public final String colour; + /** Unlocalised name for this element */ + public final String unlocalisedName; + + private EnumElement(String colour, String name){ + this.colour = colour; + this.unlocalisedName = name; + } + + public EnumElement get(int id){ + return this.values()[id]; + } + + /** Returns the translated display name of this element, without formatting. */ + public String getDisplayName() { + return StatCollector.translateToLocal("element." + unlocalisedName); + } + + /** Returns the translated display name for wizards of this element, shown in the trading GUI */ + public String getWizardName() { + return StatCollector.translateToLocal("element." + unlocalisedName + ".wizard"); + } +} diff --git a/src/main/java/electroblob/wizardry/EnumParticleType.java b/src/main/java/electroblob/wizardry/EnumParticleType.java new file mode 100644 index 00000000..52d990f6 --- /dev/null +++ b/src/main/java/electroblob/wizardry/EnumParticleType.java @@ -0,0 +1,16 @@ +package electroblob.wizardry; + +public enum EnumParticleType { + BLIZZARD, + BRIGHT_DUST, + DARK_MAGIC, + DUST, + ICE, + LEAF, + MAGIC_BUBBLE, + MAGIC_FIRE, + PATH, + SNOW, + SPARK, + SPARKLE +} diff --git a/src/main/java/electroblob/wizardry/EnumSpellType.java b/src/main/java/electroblob/wizardry/EnumSpellType.java new file mode 100644 index 00000000..30d294c0 --- /dev/null +++ b/src/main/java/electroblob/wizardry/EnumSpellType.java @@ -0,0 +1,21 @@ +package electroblob.wizardry; + +import net.minecraft.util.StatCollector; + +public enum EnumSpellType { + + ATTACK("attack"), + DEFENCE("defence"), + UTILITY("utility"), + MINION("minion"); + + private final String unlocalisedName; + + EnumSpellType(String name){ + this.unlocalisedName = name; + } + + public String getDisplayName() { + return StatCollector.translateToLocal("spelltype." + unlocalisedName); + } +} \ No newline at end of file diff --git a/src/main/java/electroblob/wizardry/EnumTier.java b/src/main/java/electroblob/wizardry/EnumTier.java new file mode 100644 index 00000000..98c72e81 --- /dev/null +++ b/src/main/java/electroblob/wizardry/EnumTier.java @@ -0,0 +1,38 @@ +package electroblob.wizardry; + +import net.minecraft.util.StatCollector; + +public enum EnumTier { + + BASIC(700, 3, "\u00A7f", "basic"), + APPRENTICE(1000, 4, "\u00A7b", "apprentice"), + ADVANCED(1500, 5, "\u00A71", "advanced"), + MASTER(2500, 6, "\u00A75", "master"); + + /** Maximum mana a wand of this tier can store. */ + public final int maxCharge; + /** Just an ordinal. Shouldn't really be needed but no point changing it now. */ + public final int level; + /** The maximum number of upgrades that can be applied to a wand of this tier. */ + public final int upgradeLimit; + /** The colour of text associated with this tier */ + public final String colour; + + private final String unlocalisedName; + + private EnumTier(int maxCharge, int upgradeLimit, String colour, String name){ + this.maxCharge = maxCharge; + this.level = ordinal(); + this.upgradeLimit = upgradeLimit; + this.colour = colour; + this.unlocalisedName = name; + } + + public String getDisplayName() { + return StatCollector.translateToLocal("tier." + unlocalisedName); + } + + public String getDisplayNameWithFormatting() { + return this.colour + StatCollector.translateToLocal("tier." + unlocalisedName); + } +} diff --git a/src/main/java/electroblob/wizardry/ExtendedPlayer.java b/src/main/java/electroblob/wizardry/ExtendedPlayer.java new file mode 100644 index 00000000..9942fece --- /dev/null +++ b/src/main/java/electroblob/wizardry/ExtendedPlayer.java @@ -0,0 +1,485 @@ +package electroblob.wizardry; + +import java.lang.ref.WeakReference; +import java.util.HashSet; +import java.util.Iterator; +import java.util.Random; +import java.util.UUID; + +import cpw.mods.fml.common.network.simpleimpl.IMessage; +import electroblob.wizardry.MagicDamage.DamageType; +import electroblob.wizardry.entity.EntityShield; +import electroblob.wizardry.entity.living.EntitySummonedCreature; +import electroblob.wizardry.packet.PacketCastContinuousSpell; +import electroblob.wizardry.packet.PacketPlayerSync; +import electroblob.wizardry.packet.PacketTransportation; +import electroblob.wizardry.packet.WizardryPacketHandler; +import electroblob.wizardry.spell.None; +import electroblob.wizardry.spell.Spell; +import net.minecraft.entity.Entity; +import net.minecraft.entity.EntityLivingBase; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.entity.player.EntityPlayerMP; +import net.minecraft.nbt.NBTTagCompound; +import net.minecraft.nbt.NBTTagList; +import net.minecraft.nbt.NBTTagString; +import net.minecraft.potion.Potion; +import net.minecraft.potion.PotionEffect; +import net.minecraft.world.World; +import net.minecraftforge.common.IExtendedEntityProperties; +import net.minecraftforge.common.util.Constants.NBT; + +/** IExtendedEntityProperties for players. Everything player-data-related that is needed by wizardry is handled here, + * including the ADS, soulbinding, conjured items, summoned wolves and horses, transportation and clairvoyance. */ +public class ExtendedPlayer implements IExtendedEntityProperties { + + public final static String name = "WizardryExtendedPlayer"; + + private final EntityPlayer player; + + public int damageToApply; + + // These were originally done using the item damage directly, but this caused the annoying re-equip animation + // to keep happening so they were moved to here instead. + public int conjuredSwordDuration; + public int conjuredPickaxeDuration; + public int conjuredBowDuration; + public int conjuredArmourDuration; + public int flamingAxeDuration; + public int frostAxeDuration; + public int magicWeaponDuration; + public int flamingWeaponDuration; + public int freezingWeaponDuration; + public boolean hasSpiritWolf; + public boolean hasSpiritHorse; + + /** Whether this player is currently casting a continuous spell via commands. Not saved over world reload + * and reset on player death. */ + private Spell currentlyCasting; + /** The time for which this player has been casting a continuous spell via commands. Increments by 1 each tick. + * Not saved over world reload and reset on player death. */ + private int castingTick; + /** The damage multiplier for the current continuous spell cast via commands. Not saved over world reload and + * reset on player death. */ + private float spellDamageMultiplier; + /** The range multiplier for the current continuous spell cast via commands. Not saved over world reload and + * reset on player death. */ + private float spellRangeMultiplier; + /** The duration multiplier for the current continuous spell cast via commands. Not saved over world reload and + * reset on player death. */ + private float spellDurationMultiplier; + /** The blast multiplier for the current continuous spell cast via commands. Not saved over world reload and + * reset on player death. */ + private float spellBlastMultiplier; + + /** Coordinate for the saved transportation stone location. Will be -1 if no location is saved. */ + public int transportX, transportY, transportZ; + /** Dimension id which the saved stone circle is in. */ + public int transportDimension; + /** Time left until the player teleports under the effect of transportation */ + public int tpTimer; + + /** Coordinate for the saved clairvoyance location. Will be -1 if no location is saved. */ + public int clairvoyanceX, clairvoyanceY, clairvoyanceZ; + /** Dimension id which the saved clairvoyance point is in. */ + public int clairvoyanceDimension; + + public EntityShield shield; + + public WeakReference selectedMinion; + + public HashSet spellsDiscovered; + + private HashSet allies; + /** List of usernames of this player's allies. May not be accurate 100% of the time. This is here so that a player + * can view the usernames of their allies even when those allies are not online. + * Do not use this for any other purpose than displaying the names! */ + public HashSet allyNames; + + private HashSet soulboundCreatures; + + public ExtendedPlayer(EntityPlayer player){ + this.player = player; + this.damageToApply = 0; + this.conjuredSwordDuration = 0; + this.conjuredPickaxeDuration = 0; + this.conjuredBowDuration = 0; + this.conjuredArmourDuration = 0; + this.flamingAxeDuration = 0; + this.frostAxeDuration = 0; + this.magicWeaponDuration = 0; + this.flamingWeaponDuration = 0; + this.freezingWeaponDuration = 0; + this.spellsDiscovered = new HashSet(); + // All players can recognise magic missile. This is not done using discoverSpell because that seems to cause + // a crash on load occasionally (probably something to do with achievements being initalised) + this.spellsDiscovered.add(WizardryRegistry.magicMissile); + this.hasSpiritWolf = false; + this.hasSpiritHorse = false; + this.currentlyCasting = WizardryRegistry.none; + this.spellDamageMultiplier = 1; + this.spellRangeMultiplier = 1; + this.spellDurationMultiplier = 1; + this.spellBlastMultiplier = 1; + this.castingTick = 0; + this.transportX = -1; + this.transportY = -1; + this.transportZ = -1; + this.transportDimension = 0; + this.clairvoyanceX = -1; + this.clairvoyanceY = -1; + this.clairvoyanceZ = -1; + this.clairvoyanceDimension = 0; + this.tpTimer = 0; + this.allies = new HashSet(); + this.allyNames = new HashSet(); + this.soulboundCreatures = new HashSet(); + } + + public boolean hasSpellBeenDiscovered(Spell spell){ + return spellsDiscovered.contains(spell) || spell instanceof None; + } + + /** + * Adds the spell to the list of discovered spells for this player. Automatically takes into account + * whether the spell has been discovered. Use this method rather than adding directly to the list + * because it handles the mage of all trades achievement. + * @param spell + */ + public void discoverSpell(Spell spell){ + + if(spellsDiscovered == null){ + spellsDiscovered = new HashSet(); + } + + if(!(spell instanceof None)){ + spellsDiscovered.add(spell); + } + + if(spellsDiscovered.containsAll(Spell.getSpells(Spell.enabledSpells))){ + this.player.triggerAchievement(Wizardry.allSpells); + } + + for(EnumElement element : EnumElement.values()){ + if(element != EnumElement.MAGIC && spellsDiscovered.containsAll(Spell.getSpells(new Spell.TierElementFilter(null, element)))){ + this.player.triggerAchievement(Wizardry.elementMaster); + } + } + } + + /** Sets the player's saved transportation stone location. */ + public void setStoneLocation(int x, int y, int z, int dimensionID){ + this.transportX = x; + this.transportY = y; + this.transportZ = z; + this.transportDimension = dimensionID; + } + + /** Sets the player's saved clairvoyance location. */ + public void setClairvoyancePoint(int x, int y, int z, int dimensionID){ + this.clairvoyanceX = x; + this.clairvoyanceY = y; + this.clairvoyanceZ = z; + this.clairvoyanceDimension = dimensionID; + } + + /** Adds the given player to the list of allies belonging to the associated player, or removes the player if + * they are already in the list of allies. Returns true if the player was added, false if they were removed. */ + public boolean toggleAlly(EntityPlayer player){ + if(this.isPlayerAlly(player)){ + this.allies.remove(player.getUniqueID()); + // The remove method uses .equals() rather than == so this will work fine. + this.allyNames.remove(player.getCommandSenderName()); + return false; + }else{ + this.allies.add(player.getUniqueID()); + this.allyNames.add(player.getCommandSenderName()); + return true; + } + } + + /** Returns whether the given player is in this player's list of allies, or is on the same team as this player. */ + public boolean isPlayerAlly(EntityPlayer player){ + return this.allies.contains(player.getUniqueID()) || this.player.isOnSameTeam(player); + } + + /** Adds the given entity to this player's list of soulbound creatures, and returns whether it succeeded. */ + public boolean soulbind(EntityLivingBase target){ + return this.soulboundCreatures.add(target.getUniqueID()); + } + + /** Returns whether the given entity has been soulbound to this player. */ + public boolean isCreatureSoulbound(EntityPlayer target){ + return this.soulboundCreatures.contains(target.getUniqueID()); + } + + /** Damages all creatures soulbound to this player by the given amount, and removes from the list any that no + * longer exist. */ + public void damageAllSoulboundCreatures(float damage){ + + for(Iterator iterator = this.soulboundCreatures.iterator(); iterator.hasNext();){ + + Entity entity = WizardryUtilities.getEntityByUUID(this.player.worldObj, iterator.next()); + + if(entity == null) iterator.remove(); + + if(entity instanceof EntityLivingBase){ + if(entity.attackEntityFrom(MagicDamage.causeDirectMagicDamage(this.player, DamageType.MAGIC), damage)){ + // Sound only plays if the damage succeeds + player.worldObj.playSoundAtEntity(entity, "mob.wither.hurt", 1.0F, player.worldObj.rand.nextFloat() * 0.2F + 1.0F); + } + } + } + } + + /** Starts casting the given spell with the given modifiers. */ + public void startCastingContinuousSpell(Spell spell, float damageMultiplier, float rangeMultiplier, float durationMultiplier, float blastMultiplier){ + this.currentlyCasting = spell; + this.spellDamageMultiplier = damageMultiplier; + this.spellRangeMultiplier = rangeMultiplier; + this.spellDurationMultiplier = durationMultiplier; + this.spellBlastMultiplier = blastMultiplier; + + if(!this.player.worldObj.isRemote){ + PacketCastContinuousSpell.Message message = new PacketCastContinuousSpell.Message(this.player.getEntityId(), spell.id(), damageMultiplier, rangeMultiplier, blastMultiplier); + WizardryPacketHandler.net.sendToDimension(message, this.player.worldObj.provider.dimensionId); + } + } + + /** Stops casting the current spell. */ + public void stopCastingContinuousSpell(){ + this.currentlyCasting = WizardryRegistry.none; + this.castingTick = 0; + this.spellDamageMultiplier = 1; + this.spellRangeMultiplier = 1; + this.spellDurationMultiplier = 1; + this.spellBlastMultiplier = 1; + + if(!this.player.worldObj.isRemote){ + PacketCastContinuousSpell.Message message = new PacketCastContinuousSpell.Message(this.player.getEntityId(), WizardryRegistry.none.id(), 1, 1, 1); + WizardryPacketHandler.net.sendToDimension(message, this.player.worldObj.provider.dimensionId); + } + } + + /** Returns whether this player is currently casting a continuous spell via commands. */ + public boolean isCasting(){ + return this.currentlyCasting != null && this.currentlyCasting != WizardryRegistry.none; + } + + /** Returns the continuous spell this player is currently casting via commands, or the 'none' spell if they aren't + * casting anything. */ + public Spell currentlyCasting(){ + return currentlyCasting; + } + + /** Called from the event handler each time the associated player is updated. */ + public void update(EntityPlayer entityplayer){ + + if(this.selectedMinion != null && this.selectedMinion.get() == null) this.selectedMinion = null; + + Random random = entityplayer.worldObj.rand; + + // Annoyingly, it seems that the armour inventory is dealt with completely separately. + if(entityplayer.inventory.hasItem(Wizardry.spectralHelmet) + || entityplayer.inventory.hasItem(Wizardry.spectralChestplate) + || entityplayer.inventory.hasItem(Wizardry.spectralLeggings) + || entityplayer.inventory.hasItem(Wizardry.spectralBoots) + || (entityplayer.inventory.armorInventory[3] != null && entityplayer.inventory.armorInventory[3].getItem() == Wizardry.spectralHelmet) + || (entityplayer.inventory.armorInventory[2] != null && entityplayer.inventory.armorInventory[2].getItem() == Wizardry.spectralChestplate) + || (entityplayer.inventory.armorInventory[1] != null && entityplayer.inventory.armorInventory[1].getItem() == Wizardry.spectralLeggings) + || (entityplayer.inventory.armorInventory[0] != null && entityplayer.inventory.armorInventory[0].getItem() == Wizardry.spectralBoots)){ + + this.conjuredArmourDuration++; + + }else{ + this.conjuredArmourDuration = 0; + } + + if(!entityplayer.worldObj.isRemote){ + if(tpTimer == 1){ + entityplayer.setPositionAndUpdate(this.transportX + 0.5, this.transportY, this.transportZ + 0.5); + entityplayer.worldObj.playSoundAtEntity(entityplayer, "portal.travel", 1.0f, 1.0f); + entityplayer.addPotionEffect(new PotionEffect(Potion.blindness.id, 50, 0)); + IMessage msg = new PacketTransportation.Message(entityplayer.getEntityId()); + WizardryPacketHandler.net.sendToDimension(msg, entityplayer.worldObj.provider.dimensionId); + } + + if(tpTimer > 0){ + tpTimer--; + } + } + + if(this.currentlyCasting != null && this.currentlyCasting.isContinuous){ + this.currentlyCasting.cast(player.worldObj, player, castingTick++, spellDamageMultiplier, spellRangeMultiplier, spellDurationMultiplier, spellBlastMultiplier); + }else{ + this.castingTick = 0; + } + } + + /** Called from the event handler each time the associated player dies. Used to reset any variables that shouldn't + * persist over player death. */ + public void onPlayerDeath(){ + this.conjuredSwordDuration = 0; + this.conjuredPickaxeDuration = 0; + this.conjuredBowDuration = 0; + this.conjuredArmourDuration = 0; + this.flamingAxeDuration = 0; + this.frostAxeDuration = 0; + this.magicWeaponDuration = 0; + this.flamingWeaponDuration = 0; + this.freezingWeaponDuration = 0; + // Death cancels transportation spell. + this.tpTimer = 0; + this.soulboundCreatures = new HashSet(); + this.currentlyCasting = WizardryRegistry.none; + this.castingTick = 0; + this.spellDamageMultiplier = 1; + this.spellRangeMultiplier = 1; + this.spellDurationMultiplier = 1; + this.spellBlastMultiplier = 1; + } + + /** + * Returns the string key to be used when saving and loading the extended player data on respawn. + */ + public static String getSaveKey(EntityPlayer player) { + return player.getUniqueID().toString() + ":" + name; + } + + /** + * Used to register these extended properties for the player during EntityConstructing event + * This method is for convenience only; it will make your code look nicer + */ + public static final void register(EntityPlayer player){ + player.registerExtendedProperties(ExtendedPlayer.name, new ExtendedPlayer(player)); + } + + /** + * Returns the ExtendedPlayer properties for the specified player. + */ + public static final ExtendedPlayer get(EntityPlayer player){ + return (ExtendedPlayer) player.getExtendedProperties(name); + } + + /** Sends a packet to this player's client to synchronise necessary information. Only called server side. */ + public void sync(){ + if(this.player instanceof EntityPlayerMP){ + int id = -1; + if(this.selectedMinion != null && this.selectedMinion.get() != null) id = this.selectedMinion.get().getEntityId(); + IMessage msg = new PacketPlayerSync.Message(this.spellsDiscovered, id); + WizardryPacketHandler.net.sendTo(msg, (EntityPlayerMP)this.player); + } + } + + @Override + public void saveNBTData(NBTTagCompound compound) { + + // NBTTagCompound is for Maps, NBTTagList is for Lists/Sets + + NBTTagCompound properties = new NBTTagCompound(); + properties.setInteger("conjuredSwordDuration", this.conjuredSwordDuration); + properties.setInteger("conjuredPickaxeDuration", this.conjuredPickaxeDuration); + properties.setInteger("conjuredBowDuration", this.conjuredBowDuration); + properties.setInteger("conjuredArmourDuration", this.conjuredArmourDuration); + properties.setInteger("flamingAxeDuration", this.flamingAxeDuration); + properties.setInteger("frostAxeDuration", this.frostAxeDuration); + properties.setInteger("magicWeaponDuration", this.magicWeaponDuration); + properties.setInteger("flamingWeaponDuration", this.flamingWeaponDuration); + properties.setInteger("freezingWeaponDuration", this.freezingWeaponDuration); + properties.setBoolean("hasSpiritWolf", this.hasSpiritWolf); + properties.setBoolean("hasSpiritHorse", this.hasSpiritHorse); + properties.setInteger("x", this.transportX); + properties.setInteger("y", this.transportY); + properties.setInteger("z", this.transportZ); + properties.setInteger("dimension", this.transportDimension); + properties.setInteger("x2", this.clairvoyanceX); + properties.setInteger("y2", this.clairvoyanceY); + properties.setInteger("z2", this.clairvoyanceZ); + properties.setInteger("dimension2", this.clairvoyanceDimension); + properties.setInteger("tpTimer", this.tpTimer); + + NBTTagList tagList = new NBTTagList(); + for(UUID id : this.allies){ + tagList.appendTag(new NBTTagString(id.toString())); + } + properties.setTag("allies", tagList); + + NBTTagList tagList2 = new NBTTagList(); + for(String name : this.allyNames){ + tagList2.appendTag(new NBTTagString(name)); + } + properties.setTag("allyNames", tagList2); + + NBTTagList tagList3 = new NBTTagList(); + for(UUID id : this.soulboundCreatures){ + tagList3.appendTag(new NBTTagString(id.toString())); + } + properties.setTag("soulboundCreatures", tagList3); + + int[] spells = new int[this.spellsDiscovered.size()]; + int i=0; + for(Spell spell : this.spellsDiscovered){ + spells[i] = spell.id(); + i++; + } + properties.setIntArray("discoveredSpells", spells); + + compound.setTag(name, properties); + } + + @Override + public void loadNBTData(NBTTagCompound compound) { + NBTTagCompound properties = (NBTTagCompound) compound.getTag(name); + if(properties != null){ + this.conjuredSwordDuration = properties.getInteger("conjuredSwordDuration"); + this.conjuredPickaxeDuration = properties.getInteger("conjuredPickaxeDuration"); + this.conjuredBowDuration = properties.getInteger("conjuredBowDuration"); + this.conjuredArmourDuration = properties.getInteger("conjuredArmourDuration"); + this.flamingAxeDuration = properties.getInteger("flamingAxeDuration"); + this.frostAxeDuration = properties.getInteger("frostAxeDuration"); + this.magicWeaponDuration = properties.getInteger("magicWeaponDuration"); + this.flamingWeaponDuration = properties.getInteger("flamingWeaponDuration"); + this.freezingWeaponDuration = properties.getInteger("freezingWeaponDuration"); + this.hasSpiritWolf = properties.getBoolean("hasSpiritWolf"); + this.hasSpiritHorse = properties.getBoolean("hasSpiritHorse"); + this.transportX = properties.getInteger("x"); + this.transportY = properties.getInteger("y"); + this.transportZ = properties.getInteger("z"); + this.transportDimension = properties.getInteger("dimension"); + this.clairvoyanceX = properties.getInteger("x2"); + this.clairvoyanceY = properties.getInteger("y2"); + this.clairvoyanceZ = properties.getInteger("z2"); + this.clairvoyanceDimension = properties.getInteger("dimension2"); + this.tpTimer = properties.getInteger("tpTimer"); + + this.allies = new HashSet(); + NBTTagList tagList = properties.getTagList("allies", NBT.TAG_STRING); + for(int i=0; i(); + NBTTagList tagList2 = properties.getTagList("allyNames", NBT.TAG_STRING); + for(int i=0; i(); + NBTTagList tagList3 = properties.getTagList("soulboundCreatures", NBT.TAG_STRING); + for(int i=0; i(); + for(int id : properties.getIntArray("discoveredSpells")){ + spellsDiscovered.add(Spell.get(id)); + } + } + } + + @Override + public void init(Entity entity, World world) { + // For reference, apparently there is a strange error that causes 'this' to be null here. + } + +} diff --git a/src/main/java/electroblob/wizardry/IElementalDamage.java b/src/main/java/electroblob/wizardry/IElementalDamage.java new file mode 100644 index 00000000..62d45c43 --- /dev/null +++ b/src/main/java/electroblob/wizardry/IElementalDamage.java @@ -0,0 +1,16 @@ +package electroblob.wizardry; + +import electroblob.wizardry.MagicDamage.DamageType; +import net.minecraft.entity.Entity; +import scala.actors.threadpool.Arrays; + +/** This interface allows {@link MagicDamage} and {@link IndirectMagicDamage} to both be treated as instances of a + * single type so that the damage type field can be accessed, rather than having to deal with each of them separately, + * which would be inefficient and cumbersome (the latter of those classes cannot extend the former because they both + * need to extend different subclasses of {@link net.minecraft.util.DamageSource DamageSource}). + * @since Wizardry 1.1 */ +public interface IElementalDamage { + + DamageType getType(); + +} \ No newline at end of file diff --git a/src/main/java/electroblob/wizardry/util/IndirectMagicDamage.java b/src/main/java/electroblob/wizardry/IndirectMagicDamage.java similarity index 58% rename from src/main/java/electroblob/wizardry/util/IndirectMagicDamage.java rename to src/main/java/electroblob/wizardry/IndirectMagicDamage.java index f116f3e1..227c948b 100644 --- a/src/main/java/electroblob/wizardry/util/IndirectMagicDamage.java +++ b/src/main/java/electroblob/wizardry/IndirectMagicDamage.java @@ -1,18 +1,16 @@ -package electroblob.wizardry.util; +package electroblob.wizardry; -import electroblob.wizardry.util.MagicDamage.DamageType; +import electroblob.wizardry.MagicDamage.DamageType; import net.minecraft.entity.Entity; import net.minecraft.util.EntityDamageSourceIndirect; public class IndirectMagicDamage extends EntityDamageSourceIndirect implements IElementalDamage { private final DamageType type; - private final boolean isRetaliatory; - public IndirectMagicDamage(String name, Entity magic, Entity caster, DamageType type, boolean isRetaliatory){ + public IndirectMagicDamage(String name, Entity magic, Entity caster, DamageType type) { super(name, magic, caster); this.type = type; - this.isRetaliatory = isRetaliatory; } @Override @@ -20,9 +18,4 @@ public class IndirectMagicDamage extends EntityDamageSourceIndirect implements I return type; } - @Override - public boolean isRetaliatory(){ - return isRetaliatory; - } - } diff --git a/src/main/java/electroblob/wizardry/util/MagicDamage.java b/src/main/java/electroblob/wizardry/MagicDamage.java similarity index 69% rename from src/main/java/electroblob/wizardry/util/MagicDamage.java rename to src/main/java/electroblob/wizardry/MagicDamage.java index ce2a5c2f..6f253ad5 100644 --- a/src/main/java/electroblob/wizardry/util/MagicDamage.java +++ b/src/main/java/electroblob/wizardry/MagicDamage.java @@ -1,4 +1,4 @@ -package electroblob.wizardry.util; +package electroblob.wizardry; import java.util.Arrays; import java.util.HashMap; @@ -16,6 +16,7 @@ import electroblob.wizardry.entity.living.EntitySpiderMinion; import electroblob.wizardry.entity.living.EntityStormElemental; import electroblob.wizardry.entity.living.EntityZombieMinion; import net.minecraft.entity.Entity; +import net.minecraft.entity.EntityLivingBase; import net.minecraft.entity.boss.EntityDragon; import net.minecraft.entity.boss.EntityWither; import net.minecraft.entity.monster.EntityBlaze; @@ -27,7 +28,6 @@ import net.minecraft.entity.monster.EntitySkeleton; import net.minecraft.entity.monster.EntitySnowman; import net.minecraft.entity.monster.EntitySpider; import net.minecraft.entity.monster.EntityZombie; -import net.minecraft.entity.monster.SkeletonType; import net.minecraft.util.DamageSource; import net.minecraft.util.EntityDamageSource; @@ -38,11 +38,11 @@ import net.minecraft.util.EntityDamageSource; // The vanilla approach to damage types is inconsistent, to say the least. Poison is simply 'magic', and relies on // EntityLivingBase.isPotionApplicable to determine whether an entity is affected or not. Wither, on the other hand, is -// its own damage type, but again relies on the potion to determine what is immune (which in vanilla is nothing). Fire +// its own damage type, but again relies on the potion to determine what is immune (which in this case is nothing). Fire // has a proper implementation of course, with both block-based and projectile-based types, and any other damagesource // can also be designated as fire damage with setFireDamage(). Likewise, projectile and explosion damage can also be -// applied to any damagesource, but these aren't considered types in their own right; rather, they seem to be damage -// type 'attributes'. This is my attempt to collect all of these into a reasonably coherent system. +// applied to any damagesource, but these aren't condsidered types in their own right; rather, they seem to be damage +// type 'attributes'. /** As of wizardry 1.1, this class has replaced the damagesource-related methods in WizardryUtilities, allowing a * {@link DamageType} to be specified with the damage. The main reason for this is so that damage sources can fit with the @@ -57,19 +57,19 @@ import net.minecraft.util.EntityDamageSource; * * @see IndirectMagicDamage * @see IElementalDamage - * @since Wizardry 1.1 - * @author Electroblob */ + * @since Wizardry 1.1 */ public class MagicDamage extends EntityDamageSource implements IElementalDamage { - /** The name of the damagesource for direct magic damage from the wizardry mod. */ + /** The name of the damagesource for direct magic damage from the wizardry mod. Defined here so that any + * references get changed automatically. */ public static final String DIRECT_MAGIC_DAMAGE = "wizardryMagic"; - /** The name of the damagesource for indirect magic damage from the wizardry mod. */ + /** The name of the damagesource for indirect magic damage from the wizardry mod. Defined here so that any + * references get changed automatically. */ public static final String INDIRECT_MAGIC_DAMAGE = "indirectWizardryMagic"; // Technically, I don't need to specify that the classes in this map must extend entity, but it's good practice to. private static final Map, DamageType[]> immunityMapping = new HashMap, DamageType[]>(); private final DamageType type; - private final boolean isRetaliatory; /** A simple set of constants for the types of damage. The names are deliberately different to those in EnumElement * to avoid confusion. All types are classified as magic damage in the vanilla system, so witches are resistant to them. */ @@ -87,11 +87,11 @@ public class MagicDamage extends EntityDamageSource implements IElementalDamage WITHER, /** Poison damage from the wizardry mod. Spiders, cave spiders and undead mobs are immune. */ POISON, - /** [NYI] Force damage from the wizardry mod. Insubstantial creatures (ghast, shadow wraith, etc.) are immune. */ + /** Force damage from the wizardry mod. Insubstantial creatures (ghast, shadow wraith, etc.) are immune. */ FORCE, /** Blast damage from the wizardry mod. Affected by the blast protection enchantment. */ BLAST, - /** [NYI] Radiant damage from the wizardry mod. */ + /** Radiant damage from the wizardry mod. */ RADIANT; } @@ -122,10 +122,9 @@ public class MagicDamage extends EntityDamageSource implements IElementalDamage setEntityImmunities(EntitySkeletonMinion.class, DamageType.POISON); } - public MagicDamage(String name, Entity caster, DamageType type, boolean isRetaliatory){ + public MagicDamage(String name, Entity caster, DamageType type) { super(name, caster); this.type = type; - this.isRetaliatory = isRetaliatory; this.setMagicDamage(); if(type == DamageType.FIRE) this.setFireDamage(); if(type == DamageType.BLAST) this.setExplosion(); @@ -141,9 +140,9 @@ public class MagicDamage extends EntityDamageSource implements IElementalDamage // Because Mojang, in their infinite wisdom, did not make wither skeletons their own separate class (despite the // fact that cave spiders are), I have to test for this manually. Realistically, no mod author would be stupid // enough to put two entities in one class, so this should be the only time I ever have to do this. - if(type == DamageType.WITHER && entity instanceof EntitySkeleton - // Don't need to check EntitySkeletonMinion separately any more because it now extends EntitySkeleton. - && ((EntitySkeleton)entity).getSkeletonType() == SkeletonType.WITHER){ + if(type == DamageType.WITHER && ( + (entity instanceof EntitySkeleton && ((EntitySkeleton)entity).getSkeletonType() == 1) + || (entity instanceof EntitySkeletonMinion && ((EntitySkeletonMinion)entity).getSkeletonType() == 1))){ return true; } @@ -154,12 +153,13 @@ public class MagicDamage extends EntityDamageSource implements IElementalDamage return immunities != null && Arrays.asList(immunities).contains(type); } + // I found a use for varargs! /** Registers the given type of entity as immune to all of the passed in damage types. */ public static void setEntityImmunities(Class entityType, DamageType... immunities){ immunityMapping.put(entityType, immunities); } - /** Adds the passed in damage type to the list of damage types to which the given entity is immune. */ + /** Registers the given type of entity as immune to all of the passed in damage types. */ public static void addEntityImmunity(Class entityType, DamageType immunity){ List immunities = Arrays.asList(immunityMapping.get(entityType)); immunities.add(immunity); @@ -172,61 +172,19 @@ public class MagicDamage extends EntityDamageSource implements IElementalDamage * types to allow things to distinguish between magic and regular melee/swords. Unlike DamageSource.magic, it does * not bypass armour and has a player as the source (rather than nothing). It is still classed as magic damage (for * the record, all this does in vanilla is make witches 85% resistant to it - but that seems kinda right anyway). - * isRetaliatory defaults to false. *

* Now that this is its own class, this static method is largely redundant, but it's not worth refactoring the entire * mod just to get rid of this method and use the constructor instead. * @param caster The player or other living entity causing the damage * @param type The type that this damage belongs to; used for resistances and wand perks. Use - * {@link DamageType#MAGIC} for regular, non-elemental magic damage (sometimes you might not want an element + * {@link EnumElement#MAGIC} for regular, non-elemental magic damage (sometimes you might not want an element * even though the spell has one - for example, not all necromancy spells are 'withery', so some of them might * reasonably affect creatures that are ususally immune to wither effects). * @return A damagesource object of type EntityDamageSource */ - public static DamageSource causeDirectMagicDamage(Entity caster, DamageType type){ - return causeDirectMagicDamage(caster, type, false); - } - - /** - * Returns a DamageSource called "wizardryMagic" with the given entity as the caster. Use in preference to vanilla - * types to allow things to distinguish between magic and regular melee/swords. Unlike DamageSource.magic, it does - * not bypass armour and has a player as the source (rather than nothing). It is still classed as magic damage (for - * the record, all this does in vanilla is make witches 85% resistant to it - but that seems kinda right anyway). - *

- * Now that this is its own class, this static method is largely redundant, but it's not worth refactoring the entire - * mod just to get rid of this method and use the constructor instead. - * @param caster The player or other living entity causing the damage - * @param type The type that this damage belongs to; used for resistances and wand perks. Use - * {@link DamageType#MAGIC} for regular, non-elemental magic damage (sometimes you might not want an element - * even though the spell has one - for example, not all necromancy spells are 'withery', so some of them might - * reasonably affect creatures that are ususally immune to wither effects). - * @param isRetaliatory whether this damage source came from a retaliatory attack; prevents infinite loops - * occurring when two entities damage each other with retaliatory effects. - * @return A damagesource object of type EntityDamageSource - */ - public static DamageSource causeDirectMagicDamage(Entity caster, DamageType type, boolean isRetaliatory){ - return new MagicDamage(DIRECT_MAGIC_DAMAGE, caster, type, isRetaliatory); - } - - /** - * Returns a DamageSource called "indirectWizardryMagic" with the player as the caster. Use in preference to vanilla - * types to allow things to distinguish between magic and regular arrows/throwables. Unlike - * DamageSource.causeIndirectMagicDamage, it does not bypass armour. It is still classed as magic damage (for the - * record, all this does in vanilla is make witches 85% resistant to it - but that seems kinda right anyway). - * isRetaliatory defaults to false. - *

- * Now that this is its own class, this static method is largely redundant, but it's not worth refactoring the entire - * mod just to get rid of this method and use the constructor instead. - * @param magic The entity that actually caused the damage - * @param caster The player or other living entity that cast the spell originally - * @param type The type that this damage belongs to; used for resistances and wand perks. Use - * {@link DamageType#MAGIC} for regular, non-elemental magic damage (sometimes you might not want an element - * even though the spell has one - for example, not all necromancy spells are 'withery', so some of them might - * reasonably affect creatures that are ususally immune to wither effects). - * @return A damagesource object of type EntityDamageSourceIndirect - */ - public static DamageSource causeIndirectMagicDamage(Entity magic, Entity caster, DamageType type){ - return causeIndirectMagicDamage(magic, caster, type, false); + public static DamageSource causeDirectMagicDamage(EntityLivingBase caster, DamageType type) + { + return new MagicDamage(DIRECT_MAGIC_DAMAGE, caster, type); } /** @@ -240,15 +198,14 @@ public class MagicDamage extends EntityDamageSource implements IElementalDamage * @param magic The entity that actually caused the damage * @param caster The player or other living entity that cast the spell originally * @param type The type that this damage belongs to; used for resistances and wand perks. Use - * {@link DamageType#MAGIC} for regular, non-elemental magic damage (sometimes you might not want an element + * {@link EnumElement#MAGIC} for regular, non-elemental magic damage (sometimes you might not want an element * even though the spell has one - for example, not all necromancy spells are 'withery', so some of them might * reasonably affect creatures that are ususally immune to wither effects). - * @param isRetaliatory whether this damage source came from a retaliatory attack; prevents infinite loops - * occurring when two entities damage each other with retaliatory effects. * @return A damagesource object of type EntityDamageSourceIndirect */ - public static DamageSource causeIndirectMagicDamage(Entity magic, Entity caster, DamageType type, boolean isRetaliatory){ - return new IndirectMagicDamage(INDIRECT_MAGIC_DAMAGE, magic, caster, type, isRetaliatory); + public static DamageSource causeIndirectEntityMagicDamage(Entity magic, EntityLivingBase caster, DamageType type) + { + return new IndirectMagicDamage(INDIRECT_MAGIC_DAMAGE, magic, caster, type); } @Override @@ -256,9 +213,4 @@ public class MagicDamage extends EntityDamageSource implements IElementalDamage return type; } - @Override - public boolean isRetaliatory(){ - return isRetaliatory; - } - } diff --git a/src/main/java/electroblob/wizardry/Settings.java b/src/main/java/electroblob/wizardry/Settings.java deleted file mode 100644 index 8d56dce6..00000000 --- a/src/main/java/electroblob/wizardry/Settings.java +++ /dev/null @@ -1,507 +0,0 @@ -package electroblob.wizardry; - -import java.util.ArrayList; -import java.util.List; -import java.util.Locale; - -import electroblob.wizardry.packet.PacketSyncSettings; -import electroblob.wizardry.packet.WizardryPacketHandler; -import electroblob.wizardry.spell.Spell; -import electroblob.wizardry.util.MagicDamage; -import electroblob.wizardry.util.MagicDamage.DamageType; -import net.minecraft.entity.EntityList; -import net.minecraft.entity.player.EntityPlayerMP; -import net.minecraft.util.text.translation.I18n; -import net.minecraftforge.common.config.ConfigCategory; -import net.minecraftforge.common.config.Configuration; -import net.minecraftforge.common.config.Property; -import net.minecraftforge.fml.common.event.FMLPreInitializationEvent; -import net.minecraftforge.fml.common.network.simpleimpl.IMessage; - -/** Singleton class which deals with everything related to wizardry's config file. To access individual settings, use - * {@link Wizardry#settings}. Also stores a few string constants for easy access. - *

- * As part of the 1.2 update and code overhaul, the way the config settings work in multiplayer has been tightened up. - * Importantly, there are three different types of config options: - *

- *

  • Server-only settings. These only affect server-side code and hence are not synced. Changing these locally only - * has an effect if the local game is the host, i.e. a dedicated server, a LAN host or a singleplayer world. Examples - * include worldgen, mob drops and commands. - *
  • Synchronised settings. These settings affect both client-side AND server-side code, and are synchronised with - * each client on login via {@link Settings#sync(EntityPlayerMP)}. Changing these locally - * only has an effect if the local game is the host, i.e. a dedicated server, a LAN host or a singleplayer world. - * Examples include discovery mode and crafting recipes. Note that as far as users are concerned, - * there is no difference in behaviour between server-only and synchronised settings. - *
  • Client-only settings. These settings only affect client-side code and hence are not synced. Each client obeys - * its own values for these, and changing them on a dedicated server will have no effect. These are usually only display - * and controls settings.
  • - *

    - * Each of the settings fields in this class is marked with one of the above categories to indicate which it belongs - * to. This in turn dictates which logical side it should be called from: client, server or both. Do not access a - * config setting from the wrong side, because it may cause unexpected or strange behaviour. - * @since Wizardry 1.2 - * @author Electroblob */ -// NOTE: We could convert over to the @Config system IF it is sufficiently complete in this Forge version for my purposes. -// (For one, I know that the LangKey annotation is not applicable to type declarations in 1.10.2, unlike in 1.11.2) -//@Config(modid = Wizardry.MODID) -@SuppressWarnings("deprecation") // Used server I18n deliberately; we want to write the comments in english. -public final class Settings { - - // Category names - /** The unlocalised name of the spells config category. */ - public static final String SPELLS_CATEGORY = "spells"; - /** The unlocalised name of the resistances config category. */ - public static final String RESISTANCES_CATEGORY = "resistances"; - /** The unlocalised name of the client config category. */ - public static final String CLIENT_CATEGORY = "client"; - /** The unlocalised name of the commands config category. */ - public static final String COMMANDS_CATEGORY = "commands"; - /** The unlocalised name of the worldgen config category. */ - public static final String WORLDGEN_CATEGORY = "worldgen"; - /** The unlocalised name of the global config category. */ - public static final String GLOBAL_CATEGORY = "global"; - - /** The wizardry config file. */ - private Configuration config; - - // Server-only settings. These only affect server-side code and hence are not synced. Changing these locally only - // has an effect if the local game is the host, i.e. a dedicated server, a LAN host or a singleplayer world. - - // Worldgen - /** [Server-only] The rarity of wizard towers, used by the world generator. Larger numbers are rarer. */ - public int towerRarity = 8; - /** [Server-only] List of dimension ids in which to generate crystal ore. */ - public int[] oreDimensions = {0}; - /** [Server-only] List of dimension ids in which to generate crystal ore. */ - public int[] flowerDimensions = {0}; - /** [Server-only] List of dimension ids in which to generate crystal ore. */ - public int[] towerDimensions = {0}; - /** [Server-only] Whether or not wizardry loot should generate in dungeon chests. Note that this does not - * affect the generation of loot in wizard towers. */ - public boolean generateLoot = true; - - // Entities' drops, targeting, damage, etc. - /** [Server-only] Chance (out of 200) for mobs to drop spell books. */ - public int spellBookDropChance = 3; - /** [Server-only] Whether or not players can teleport through unbreakable blocks (e.g. bedrock) using the phase step spell. */ - public boolean teleportThroughUnbreakableBlocks = false; - /** [Server-only] Whether to allow players to damage their designated allies using magic. */ - public boolean friendlyFire = true; - /** [Server-only] Whether to allow players to disarm other players using the telekinesis spell. */ - public boolean telekineticDisarmament = true; - /** [Server-only] Whether summoned creatures can revenge attack their caster if their caster attacks them. */ - public boolean minionRevengeTargeting = true; - /** [Server-only] List of names of entities which summoned creatures are allowed to attack, in addition to the defaults. */ - public String[] summonedCreatureTargetsWhitelist = {}; - /** [Server-only] List of names of entities which summoned creatures are specifically not allowed to attack, overriding the defaults and the whitelist. */ - public String[] summonedCreatureTargetsBlacklist = {"creeper"}; - /** [Server-only] Global damage scaling factor for all player magic damage. */ - public double playerDamageScale = 1.0f; - /** [Server-only] Global damage scaling factor for all npc magic damage. */ - public double npcDamageScale = 1.0f; - - // Commands (these don't need synchronising since typing a command always queries the server). - /** [Server-only] The maximum allowed multiplier for the /cast command. This limit is here to stop people from accidentally - * breaking their worlds! */ - public double maxSpellCommandMultiplier = 20d; - /** [Server-only] The name of the /cast command. */ - public String castCommandName = "cast"; - /** [Server-only] The name of the /discoverspell command. */ - public String discoverspellCommandName = "discoverspell"; - /** [Server-only] The name of the /ally command. */ - public String allyCommandName = "ally"; - /** [Server-only] The name of the /allies command. */ - public String alliesCommandName = "allies"; - - // Synchronised settings. These settings affect both client-side AND server-side code. Changing these locally - // only has an effect if the local game is the host, i.e. a dedicated server, a LAN host or a singleplayer world. - - // Recipes (only need syncing for display in the wizard's handbook) - /** [Synchronised] Whether or not firebombs should be craftable. */ - public boolean firebombIsCraftable = true; - /** [Synchronised] Whether or not poison bombs should be craftable. */ - public boolean poisonBombIsCraftable = true; - /** [Synchronised] Whether or not poison bombs should be craftable. */ - public boolean smokeBombIsCraftable = true; - /** [Synchronised] Whether to require a magic crystal in the blank scroll crafting recipe (in case it conflicts with another mod). */ - public boolean useAlternateScrollRecipe = false; - - // Gamemodes - /** [Synchronised] When set to true, spells a player hasn't cast yet will be unreadable until they are cast (on a per-world basis). Has no effect when in creative mode. Spells of - * identification will be unobtainable in survival mode if this is false. */ - public boolean discoveryMode = true; - - // Client-only settings. These settings only affect client-side code and hence are not synced. Each client obeys - // its own values for these, and changing them on a dedicated server will have no effect. - - // Controls - /** [Client-only] Whether the player can switch between spells on a wand by scrolling with the mouse wheel while - * sneaking. */ - public boolean enableShiftScrolling = true; - - // Display - /** [Client-only] Whether to show summoned creatures' names and owners above their heads. */ - public boolean showSummonedCreatureNames = true; - /** [Client-only] The position of the spell HUD. */ - public GuiPosition spellHUDPosition = GuiPosition.BOTTOM_LEFT; - - /** Set of constants for each of the four positions that the spell HUD can be in. */ - public enum GuiPosition { - - BOTTOM_LEFT("Bottom left"), - TOP_LEFT("Top left"), - TOP_RIGHT("Top right"), - BOTTOM_RIGHT("Bottom right"); - - /** Constant array storing the names of each of the constants, in the order they are declared. */ - public static final String[] names; - - static { - names = new String[values().length]; - for(GuiPosition position : values()){ - names[position.ordinal()] = position.name; - } - } - - /** The readable name for this GUI position that will be displayed on the button in the config GUI. */ - public final String name; - - GuiPosition(String name){ - this.name = name; - } - - /** Gets a GUI position from its string name (ignoring case), or BOTTOM_LEFT if the given name is not a valid position. */ - public static GuiPosition fromName(String name){ - - for(GuiPosition position : values()){ - if(position.name.equalsIgnoreCase(name)) return position; - } - - Wizardry.logger.info("Invalid string for the spell HUD position. Using default (bottom left) instead."); - return BOTTOM_LEFT; - } - - } - - // As of Wizardry 1.1, all keys used in the config file itself are now hardcoded, not localised. The localisations - // are done in the config GUI. The config file has to be written in one language or it will end up with multiple - // options for different languages. - - // These methods are package-protected to stop anyone else from calling them. - - /** Called from preInit to initialise the config file. The first part of the config file has to be done here (as is - * conventional) so that the various registries can change what they do accordingly. The spell part of the config - * has to be done in the init method.*/ - void initConfig(FMLPreInitializationEvent event){ - - config = new Configuration(event.getSuggestedConfigurationFile()); - config.load(); - - Wizardry.logger.info("Setting up main config"); - - setupGeneralConfig(); - - config.save(); - } - - /** Called from init to initialise the parts of the config file that depend on other mods. For example, the spell - * config is done here and not in preInit because all spells must be registered before it is added, including - * those in other mods. */ - void initConfigExtras(){ - - Wizardry.logger.info("Setting up spells config for " + Spell.getTotalSpellCount() + " spells"); - - setupSpellsConfig(); - - config.save(); - } - - /** Called to save changes to the config file after it has been edited in game from the menus. */ - void saveConfigChanges(){ - - Wizardry.logger.info("Saving in-game config changes"); - - setupGeneralConfig(); - setupSpellsConfig(); - - config.save(); - } - - /** Sends a packet to the specified player's client containing all the synchronised settings. */ - public void sync(EntityPlayerMP player){ - Wizardry.logger.info("Synchronising config settings for " + player.getName()); - IMessage message = new PacketSyncSettings.Message(this); - WizardryPacketHandler.net.sendTo(message, player); - } - - public ConfigCategory getConfigCategory(String name){ - return config.getCategory(name); - } - - private void setupSpellsConfig(){ - - config.addCustomCategoryComment(SPELLS_CATEGORY, "Set a spell to false to disable it. Disabled spells will still have their associated spell book (mainly so the spell books don't all change) and can still be bound to wands, but cannot be cast in game, will not appear in any subsequently generated chests or wizard trades and will not drop from mobs. Disable a spell if it is causing problems, conflicts with another mod or creates an unintended exploit."); - - Property property; - - for(Spell spell : Spell.getSpells(Spell.allSpells)){ - property = config.get(SPELLS_CATEGORY, spell.getRegistryName().toString(), true, - I18n.translateToLocal("spell." + spell.getUnlocalisedName() + ".desc")); - // Uses the same config key as the spell name, because - well, that's what it's called! - property.setLanguageKey("spell." + spell.getUnlocalisedName()); - spell.setEnabled(property.getBoolean()); - } - - } - - private void setupGeneralConfig(){ - - // This trick is borrowed from forge; it sorts the config options into the order you want them. - List propOrder = new ArrayList(); - - Property property; - - config.addCustomCategoryComment(Configuration.CATEGORY_GENERAL, "Please note that changing some of these settings may make the mod very difficult to play."); - - property = config.get(Configuration.CATEGORY_GENERAL, "towerRarity", 8, "Rarity of wizard towers. Higher numbers are rarer. Set to 0 to disable wizard towers completely.", 0, 50); - property.setLanguageKey("config.wizardry.tower_rarity"); - property.setRequiresWorldRestart(true); - Wizardry.proxy.setToNumberSliderEntry(property); - towerRarity = property.getInt(); - propOrder.add(property.getName()); - - property = config.get(Configuration.CATEGORY_GENERAL, "spellBookDropChance", 3, "The chance for mobs to drop a spell book when killed. The greater this number, the more often they will drop. Set to 0 to disable spell book drops. Set to 200 for guaranteed drops.", 0, 200); - property.setLanguageKey("config.wizardry.spell_book_drop_chance"); - Wizardry.proxy.setToNumberSliderEntry(property); - spellBookDropChance = property.getInt(); - propOrder.add(property.getName()); - - property = config.get(Configuration.CATEGORY_GENERAL, "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.setLanguageKey("config.wizardry.ore_dimensions"); - property.setRequiresWorldRestart(true); - oreDimensions = property.getIntList(); - propOrder.add(property.getName()); - - property = config.get(Configuration.CATEGORY_GENERAL, "flowerDimensions", new int[]{0}, "List of dimension ids in which crystal flowers will generate."); - property.setLanguageKey("config.wizardry.flower_dimensions"); - property.setRequiresWorldRestart(true); - flowerDimensions = property.getIntList(); - propOrder.add(property.getName()); - - property = config.get(Configuration.CATEGORY_GENERAL, "towerDimensions", new int[]{0}, "List of dimension ids in which wizard towers will generate."); - property.setLanguageKey("config.wizardry.tower_dimensions"); - property.setRequiresWorldRestart(true); - towerDimensions = property.getIntList(); - propOrder.add(property.getName()); - - property = config.get(Configuration.CATEGORY_GENERAL, "generateLoot", true, "Whether to generate wizardry loot in dungeon chests."); - property.setLanguageKey("config.wizardry.generate_loot"); - property.setRequiresWorldRestart(true); - generateLoot = property.getBoolean(); - propOrder.add(property.getName()); - - property = config.get(Configuration.CATEGORY_GENERAL, "firebombIsCraftable", true, "Whether firebombs can be crafted or not."); - property.setLanguageKey("config.wizardry.firebomb_is_craftable"); - property.setRequiresMcRestart(true); - firebombIsCraftable = property.getBoolean(); - propOrder.add(property.getName()); - - property = config.get(Configuration.CATEGORY_GENERAL, "poisonBombIsCraftable", true, "Whether poison bombs can be crafted or not."); - property.setLanguageKey("config.wizardry.poison_bomb_is_craftable"); - property.setRequiresMcRestart(true); - poisonBombIsCraftable = property.getBoolean(); - propOrder.add(property.getName()); - - property = config.get(Configuration.CATEGORY_GENERAL, "smokeBombIsCraftable", true, "Whether smoke bombs can be crafted or not."); - property.setLanguageKey("config.wizardry.smoke_bomb_is_craftable"); - property.setRequiresMcRestart(true); - smokeBombIsCraftable = property.getBoolean(); - propOrder.add(property.getName()); - - property = config.get(Configuration.CATEGORY_GENERAL, "useAlternateScrollRecipe", false, "Whether to require a magic crystal in the shapeless crafting recipe for blank scrolls. Set to true if another mod adds a conflicting recipe."); - property.setLanguageKey("config.wizardry.use_alternate_scroll_recipe"); - property.setRequiresMcRestart(true); - useAlternateScrollRecipe = property.getBoolean(); - propOrder.add(property.getName()); - - property = config.get(Configuration.CATEGORY_GENERAL, "teleportThroughUnbreakableBlocks", false, "Whether players are allowed to teleport through unbreakable blocks (e.g. bedrock) using the phase step spell."); - property.setLanguageKey("config.wizardry.teleport_through_unbreakable_blocks"); - teleportThroughUnbreakableBlocks = property.getBoolean(); - propOrder.add(property.getName()); - - property = config.get(Configuration.CATEGORY_GENERAL, "showSummonedCreatureNames", true, "Whether to show summoned creatures' names and owners above their heads."); - property.setLanguageKey("config.wizardry.show_summoned_creature_names"); - showSummonedCreatureNames = property.getBoolean(); - propOrder.add(property.getName()); - - property = config.get(Configuration.CATEGORY_GENERAL, "friendlyFire", true, "Whether to allow players to damage their designated allies using magic."); - property.setLanguageKey("config.wizardry.friendly_fire"); - friendlyFire = property.getBoolean(); - propOrder.add(property.getName()); - - property = config.get(Configuration.CATEGORY_GENERAL, "telekineticDisarmament", true, "Whether to allow players to disarm other players using the telekinesis spell. Set to false to prevent stealing of items."); - property.setLanguageKey("config.wizardry.telekinetic_disarmament"); - telekineticDisarmament = property.getBoolean(); - propOrder.add(property.getName()); - - property = config.get(Configuration.CATEGORY_GENERAL, "discoveryMode", true, "For those who like a sense of mystery! When set to true, spells you haven't cast yet will be unreadable until you cast them (on a per-world basis). Has no effect when in creative mode. Spells of identification will be unobtainable in survival mode if this is false."); - property.setLanguageKey("config.wizardry.discovery_mode"); - property.setRequiresWorldRestart(true); - discoveryMode = property.getBoolean(); - propOrder.add(property.getName()); - - property = config.get(Configuration.CATEGORY_GENERAL, "enableShiftScrolling", true, "Whether you can switch between spells on a wand by scrolling with the mouse wheel while sneaking. Note that this will only affect you; other players connected to the same server obey their own settings."); - property.setLanguageKey("config.wizardry.enable_shift_scrolling"); - property.setRequiresWorldRestart(false); - enableShiftScrolling = property.getBoolean(); - propOrder.add(property.getName()); - - property = config.get(Configuration.CATEGORY_GENERAL, "minionRevengeTargeting", true, "Whether summoned creatures can revenge attack their owner if their owner attacks them."); - property.setLanguageKey("config.wizardry.minion_revenge_targeting"); - property.setRequiresWorldRestart(false); - minionRevengeTargeting = property.getBoolean(); - propOrder.add(property.getName()); - - // These two aren't sliders because using a slider makes it difficult to fine-tune the numbers; the nature of a - // scaling factor means that 0.5 is as big a change as 2.0, so whilst a slider is fine for increasing the damage, - // it doesn't give fine enough control for values less than 1. - property = config.get(Configuration.CATEGORY_GENERAL, "playerDamageScaling", 1.0, "Global damage scaling factor for the damage dealt by players casting spells, relative to 1.", 0, 20); - property.setLanguageKey("config.wizardry.player_damage_scaling"); - playerDamageScale = property.getDouble(); - propOrder.add(property.getName()); - - property = config.get(Configuration.CATEGORY_GENERAL, "npcDamageScaling", 1.0, "Global damage scaling factor for the damage dealt by NPCs casting spells, relative to 1.", 0, 20); - property.setLanguageKey("config.wizardry.npc_damage_scaling"); - npcDamageScale = property.getDouble(); - propOrder.add(property.getName()); - - // This one isn't a slider either because people are likely to want exact values ("it must be at most 50.3" is a bit strange!). - property = config.get(Configuration.CATEGORY_GENERAL, "castCommandMultiplierLimit", 20.0, "Upper limit for the multipliers passed into the /cast command. This is here to stop players from accidentally breaking a world/server. Large blast mutipliers can cause extreme lag - you have been warned!", 1, 255); - property.setLanguageKey("config.wizardry.cast_command_multiplier_limit"); - maxSpellCommandMultiplier = property.getDouble(); - propOrder.add(property.getName()); - - property = config.get(Configuration.CATEGORY_GENERAL, "summonedCreatureTargetsWhitelist", new String[0], "List of names of entities which summoned creatures and wizards are allowed to attack, in addition to the defaults. Add mod creatures to this list if you want summoned creatures to attack them and they aren't already doing so. Entity names are not case sensitive. For mod entities, prefix with the mod ID (e.g. wizardry:Wizard)."); - property.setLanguageKey("config.wizardry.summoned_creature_targets_whitelist"); - property.setRequiresWorldRestart(true); - //Wizardry.proxy.setToEntityNameEntry(property); - summonedCreatureTargetsWhitelist = property.getStringList(); - // Converts all strings in the list to lower case, to ignore case sensitivity, and trims them. - for(int i=0; i propOrder1 = new ArrayList(); - - config.addCustomCategoryComment(RESISTANCES_CATEGORY, "Settings which allow entities to be made immune to certain types of magic. In multiplayer, the server/LAN host settings will apply."); - - property = config.get(RESISTANCES_CATEGORY, "mobsImmuneToFire", new String[]{}, "List of names of entities that are immune to fire, in addition to the defaults. Add mod creatures to this list if you want them to be immune to fire magic and they aren't already. Entity names are not case sensitive. For mod entities, prefix with the mod ID (e.g. wizardry:Wizard)."); - property.setLanguageKey("config.wizardry.mobs_immune_to_fire"); - property.setRequiresMcRestart(true); - //Wizardry.proxy.setToEntityNameEntry(property); - // Converts all strings in the list to lower case, to ignore case sensitivity, and trims them. - for(int i=0; i randomNames = new HashMap(Spell.getTotalSpellCount()); public Map randomDescriptions = new HashMap(Spell.getTotalSpellCount()); @@ -113,8 +109,6 @@ public class SpellGlyphData extends WorldSavedData { PacketGlyphData.Message msg = new PacketGlyphData.Message(names, descriptions); WizardryPacketHandler.net.sendTo(msg, player); - - Wizardry.logger.info("Synchronising spell glyph data for " + player.getName()); } @@ -146,7 +140,7 @@ public class SpellGlyphData extends WorldSavedData { } @Override - public NBTTagCompound writeToNBT(NBTTagCompound nbt){ + public void writeToNBT(NBTTagCompound nbt){ NBTTagList tagList = new NBTTagList(); @@ -161,16 +155,5 @@ public class SpellGlyphData extends WorldSavedData { } nbt.setTag("spellGlyphData", tagList); - - return nbt; - } - - @SubscribeEvent - public static void onWorldLoadEvent(WorldEvent.Load event){ - if(!event.getWorld().isRemote && event.getWorld().provider.getDimension() == 0){ - // Called to initialise the spell glyph data when a world loads, if it isn't already. - // NOTE: Do we actually need this, or can we just let it initialise the first time it is needed? (see below) - SpellGlyphData.get(event.getWorld()); - } } } \ No newline at end of file diff --git a/src/main/java/electroblob/wizardry/util/WandHelper.java b/src/main/java/electroblob/wizardry/WandHelper.java similarity index 71% rename from src/main/java/electroblob/wizardry/util/WandHelper.java rename to src/main/java/electroblob/wizardry/WandHelper.java index 15c0466b..a36202cc 100644 --- a/src/main/java/electroblob/wizardry/util/WandHelper.java +++ b/src/main/java/electroblob/wizardry/WandHelper.java @@ -1,11 +1,8 @@ -package electroblob.wizardry.util; +package electroblob.wizardry; -import java.util.Collections; import java.util.HashMap; import java.util.Set; -import electroblob.wizardry.registry.Spells; -import electroblob.wizardry.registry.WizardryItems; import electroblob.wizardry.spell.Spell; import net.minecraft.item.Item; import net.minecraft.item.ItemStack; @@ -23,18 +20,15 @@ import net.minecraft.nbt.NBTTagCompound; * unlikely case that this is necessary. *

    * Also note that none of the methods in this class actually check that the given ItemStack contains an ItemWand; you - * can, for example, pass in a stack of snowballs without causing problems, but that is of course pointless! However, - * if you have your own spell casting item (which doesn't extend ItemWand), this setup means you can still use this class - * to manage its NBT structure. + * can, for example, pass in a stack of snowballs without causing problems, but that is of course pointless! *

    - * All get methods in this class return some kind of default if the passed-in wand stack has no nbt data. - * See individual method descriptions for more details.
    + * All get methods in this class return some kind of default if the passed-in wand stack has no nbt data.
    * All set methods in this class create a new nbt data for the passed-in wand if it has none, before doing * whatever else they do. * @see electroblob.wizardry.item.ItemWand ItemWand * @see electroblob.wizardry.packet.PacketControlInput PacketControlInput * @since Wizardry 1.1 */ -public final class WandHelper { +public class WandHelper { // NBT tag keys public static final String SPELL_ARRAY_KEY = "spells"; @@ -45,14 +39,14 @@ public final class WandHelper { private static final HashMap upgradeMap = new HashMap(); static { - upgradeMap.put(WizardryItems.condenser_upgrade, "condenser"); - upgradeMap.put(WizardryItems.storage_upgrade, "storage"); - upgradeMap.put(WizardryItems.siphon_upgrade, "siphon"); - upgradeMap.put(WizardryItems.range_upgrade, "range"); - upgradeMap.put(WizardryItems.duration_upgrade, "duration"); - upgradeMap.put(WizardryItems.cooldown_upgrade, "cooldown"); - upgradeMap.put(WizardryItems.blast_upgrade, "blast"); - upgradeMap.put(WizardryItems.attunement_upgrade, "attunement"); + upgradeMap.put(Wizardry.condenserUpgrade, "condenser"); + upgradeMap.put(Wizardry.storageUpgrade, "storage"); + upgradeMap.put(Wizardry.siphonUpgrade, "siphon"); + upgradeMap.put(Wizardry.rangeUpgrade, "range"); + upgradeMap.put(Wizardry.durationUpgrade, "duration"); + upgradeMap.put(Wizardry.cooldownUpgrade, "cooldown"); + upgradeMap.put(Wizardry.blastUpgrade, "blast"); + upgradeMap.put(Wizardry.attunementUpgrade, "attunement"); } /** Returns an array containing the spells currently bound to the given wand. As of Wizardry 1.1, this array is not @@ -62,9 +56,9 @@ public final class WandHelper { Spell[] spells = new Spell[0]; - if(wand.getTagCompound() != null){ + if(wand.stackTagCompound != null){ - int[] spellIDs = wand.getTagCompound().getIntArray(SPELL_ARRAY_KEY); + int[] spellIDs = wand.stackTagCompound.getIntArray(SPELL_ARRAY_KEY); spells = new Spell[spellIDs.length]; @@ -79,15 +73,15 @@ public final class WandHelper { /** Binds the given array of spells to the given wand. The array can be anywhere between 5 and 8 (inclusive) in length. */ public static void setSpells(ItemStack wand, Spell[] spells){ - if(wand.getTagCompound() == null) wand.setTagCompound((new NBTTagCompound())); + if(wand.stackTagCompound == null) wand.stackTagCompound = new NBTTagCompound(); int[] spellIDs = new int[spells.length]; for(int i=0; inot
    account for the individual or total upgrade stack limits. */ public static void applyUpgrade(ItemStack wand, Item upgrade){ - if(wand.getTagCompound() == null) wand.setTagCompound((new NBTTagCompound())); + if(wand.stackTagCompound == null) wand.stackTagCompound = new NBTTagCompound(); - if(!wand.getTagCompound().hasKey(UPGRADES_KEY)) wand.getTagCompound().setTag(UPGRADES_KEY, new NBTTagCompound()); + if(!wand.stackTagCompound.hasKey(UPGRADES_KEY)) wand.stackTagCompound.setTag(UPGRADES_KEY, new NBTTagCompound()); - NBTTagCompound upgrades = wand.getTagCompound().getCompoundTag(UPGRADES_KEY); + NBTTagCompound upgrades = wand.stackTagCompound.getCompoundTag(UPGRADES_KEY); String key = upgradeMap.get(upgrade); if(key != null) upgrades.setInteger(key, upgrades.getInteger(key) + 1); - wand.getTagCompound().setTag(UPGRADES_KEY, upgrades); + wand.stackTagCompound.setTag(UPGRADES_KEY, upgrades); } /** Returns true if the given item is a valid special wand upgrade. */ @@ -264,18 +258,9 @@ public final class WandHelper { return upgradeMap.containsKey(upgrade); } - /** Returns an unmodifiable set of all the items which are valid special wand upgrades. */ + /** Returns a set of all the items which are valid special wand upgrades. */ public static Set getSpecialUpgrades(){ - return Collections.unmodifiableSet(WandHelper.upgradeMap.keySet()); - } - - /** Package-protected getter for the identifier that corresponds to the given item, used only in the - * {@link SpellModifiers} class. Internal to Wizardry. - * @throws IllegalArgumentException if the given item is not a registered special wand upgrade.*/ - static String getIdentifier(Item upgrade){ - if(!isWandUpgrade(upgrade)) throw new IllegalArgumentException("Tried to get a wand upgrade key for an item" - + "that is not a registered special wand upgrade."); - return upgradeMap.get(upgrade); + return WandHelper.upgradeMap.keySet(); } /** Registers a special upgrade with wizardry. Not used in the base mod, but I've put it here to make it easy diff --git a/src/main/java/electroblob/wizardry/WeightedRandomSpellBook.java b/src/main/java/electroblob/wizardry/WeightedRandomSpellBook.java new file mode 100644 index 00000000..3dacabd8 --- /dev/null +++ b/src/main/java/electroblob/wizardry/WeightedRandomSpellBook.java @@ -0,0 +1,64 @@ +package electroblob.wizardry; + +import java.util.Random; + +import electroblob.wizardry.item.ItemScroll; +import electroblob.wizardry.item.ItemSpellBook; +import electroblob.wizardry.item.ItemWand; +import electroblob.wizardry.item.ItemWizardArmour; +import electroblob.wizardry.spell.Spell; +import net.minecraft.inventory.IInventory; +import net.minecraft.item.ItemStack; +import net.minecraft.util.WeightedRandomChestContent; +import net.minecraftforge.common.ChestGenHooks; + +public class WeightedRandomSpellBook extends WeightedRandomChestContent{ + + public WeightedRandomSpellBook(ItemStack par1ItemStack, int par2, int par3, + int par4) { + super(par1ItemStack, par2, par3, par4); + } + + /* A brief comment about dungeon loot: + * Tiers are weighted as per usual. Elements all have equal chance, INCLUDING simple stuff. The main reason for + * this is that dungeon loot is not that common anyway, so making elemental gear even rarer would be too much. + */ + + protected ItemStack[] generateChestContent(Random random, IInventory newInventory){ + + if(theItemId.getItem() instanceof ItemSpellBook){ + + theItemId = new ItemStack(theItemId.getItem(), 1, WizardryUtilities.getStandardWeightedRandomSpellId(random)); + + }else if(theItemId.getItem() instanceof ItemWand){ + + theItemId = new ItemStack(WizardryUtilities.getWand(EnumTier.BASIC, EnumElement.values()[random.nextInt(EnumElement.values().length)])); + + }else if(theItemId.getItem() instanceof ItemWizardArmour){ + + theItemId = new ItemStack(WizardryUtilities.getArmour(EnumElement.values()[random.nextInt(EnumElement.values().length)], random.nextInt(3))); + + }else if(theItemId.getItem() instanceof ItemScroll){ + + int spellId = WizardryUtilities.getStandardWeightedRandomSpellId(random, true); + theItemId = new ItemStack(theItemId.getItem(), 1, spellId); + + }else if(theItemId.getItem() == Wizardry.condenserUpgrade){ + + switch(random.nextInt(8)){ + case 0: theItemId = new ItemStack(Wizardry.condenserUpgrade); break; + case 1: theItemId = new ItemStack(Wizardry.siphonUpgrade); break; + case 2: theItemId = new ItemStack(Wizardry.rangeUpgrade); break; + case 3: theItemId = new ItemStack(Wizardry.cooldownUpgrade); break; + case 4: theItemId = new ItemStack(Wizardry.durationUpgrade); break; + case 5: theItemId = new ItemStack(Wizardry.storageUpgrade); break; + case 6: theItemId = new ItemStack(Wizardry.blastUpgrade); break; + case 7: theItemId = new ItemStack(Wizardry.attunementUpgrade); break; + } + + } + + return ChestGenHooks.generateStacks(random, theItemId, theMinimumChanceToGenerateItem, theMaximumChanceToGenerateItem); + } + +} diff --git a/src/main/java/electroblob/wizardry/WizardData.java b/src/main/java/electroblob/wizardry/WizardData.java deleted file mode 100644 index c8cc4aca..00000000 --- a/src/main/java/electroblob/wizardry/WizardData.java +++ /dev/null @@ -1,660 +0,0 @@ -package electroblob.wizardry; - -import java.lang.ref.WeakReference; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Iterator; -import java.util.Map; -import java.util.Map.Entry; -import java.util.Set; -import java.util.UUID; - -import electroblob.wizardry.constants.Element; -import electroblob.wizardry.enchantment.Imbuement; -import electroblob.wizardry.entity.EntityShield; -import electroblob.wizardry.entity.living.ISummonedCreature; -import electroblob.wizardry.event.SpellCastEvent; -import electroblob.wizardry.event.SpellCastEvent.Source; -import electroblob.wizardry.packet.PacketCastContinuousSpell; -import electroblob.wizardry.packet.PacketPlayerSync; -import electroblob.wizardry.packet.PacketTransportation; -import electroblob.wizardry.packet.WizardryPacketHandler; -import electroblob.wizardry.registry.Spells; -import electroblob.wizardry.registry.WizardryAchievements; -import electroblob.wizardry.spell.None; -import electroblob.wizardry.spell.Spell; -import electroblob.wizardry.util.MagicDamage; -import electroblob.wizardry.util.MagicDamage.DamageType; -import electroblob.wizardry.util.SpellModifiers; -import electroblob.wizardry.util.WizardryUtilities; -import net.minecraft.enchantment.Enchantment; -import net.minecraft.enchantment.EnchantmentHelper; -import net.minecraft.entity.Entity; -import net.minecraft.entity.EntityLivingBase; -import net.minecraft.entity.player.EntityPlayer; -import net.minecraft.entity.player.EntityPlayerMP; -import net.minecraft.init.MobEffects; -import net.minecraft.init.SoundEvents; -import net.minecraft.item.ItemStack; -import net.minecraft.nbt.NBTTagCompound; -import net.minecraft.nbt.NBTTagInt; -import net.minecraft.nbt.NBTTagString; -import net.minecraft.potion.PotionEffect; -import net.minecraft.util.EnumFacing; -import net.minecraft.util.EnumHand; -import net.minecraft.util.ResourceLocation; -import net.minecraft.util.math.BlockPos; -import net.minecraftforge.common.MinecraftForge; -import net.minecraftforge.common.capabilities.Capability; -import net.minecraftforge.common.capabilities.CapabilityInject; -import net.minecraftforge.common.capabilities.ICapabilitySerializable; -import net.minecraftforge.common.util.Constants.NBT; -import net.minecraftforge.event.AttachCapabilitiesEvent; -import net.minecraftforge.event.entity.EntityJoinWorldEvent; -import net.minecraftforge.event.entity.living.LivingEvent.LivingUpdateEvent; -import net.minecraftforge.event.entity.player.PlayerEvent; -import net.minecraftforge.common.util.INBTSerializable; -import net.minecraftforge.fml.common.Mod; -import net.minecraftforge.fml.common.eventhandler.SubscribeEvent; -import net.minecraftforge.fml.common.network.simpleimpl.IMessage; - -/** Capability-based replacement for the old ExtendedPlayer class from 1.7.10. This has been reworked to leave - * minimum external changes (for my own sanity, mainly!). Turns out the only major difference between an internal - * capability and an IEEP is a couple of redundant classes and a different way of registering it. - *

    - * Forge seems to have separate classes to hold the Capability<...> instance ('key') and - * methods for getting the capability, but in my opinion there are already too many classes to deal with, so I'm not - * adding any more than are necessary, meaning those constants and values are kept here instead. - * @since Wizardry 1.2 - * @author Electroblob */ -// On the plus side, having to rethink this class allowed me to clean it up a lot. -@Mod.EventBusSubscriber -public class WizardData implements INBTSerializable { - - /** Static instance of what I like to refer to as the capability key. Private because, well, it's internal! */ - // This annotation does some crazy Forge magic behind the scenes and assigns this field a value. - @CapabilityInject(WizardData.class) - private static final Capability WIZARD_DATA_CAPABILITY = null; - - /** The player this WizardData instance belongs to. */ - private final EntityPlayer player; - - // This one is still necessary, because I can't override the equip animation for items that aren't from Wizardry. - private Map imbuementDurations; - - public boolean hasSpiritWolf; - public boolean hasSpiritHorse; - - /** Whether this player is currently casting a continuous spell via commands. Not saved over world reload - * and reset on player death. */ - private Spell currentlyCasting; - /** The time for which this player has been casting a continuous spell via commands. Increments by 1 each tick. - * Not saved over world reload and reset on player death. */ - private int castingTick; - /** SpellModifiers object for the current continuous spell cast via commands. Not saved over world reload and - * reset on player death. */ - private SpellModifiers spellModifiers; - /** Coordinates for the saved transportation stone circle location. Will be null if no location is saved. */ - private BlockPos stoneCircleLocation; - /** Dimension id which the saved stone circle is in. */ - private int stoneCircleDimension; - /** Time left until the player teleports under the effect of transportation */ - private int tpCountdown; - - /** Coordinates for the saved clairvoyance location. Will be null if no location is saved. */ - private BlockPos clairvoyanceLocation; - /** Dimension id which the saved clairvoyance point is in. */ - private int clairvoyanceDimension; - - public EntityShield shield; - - public WeakReference selectedMinion; - - /** Set of this player's discovered spells. Do not write to this list directly, use - * {@link WizardData#discoverSpell(Spell)} instead. */ - public Set spellsDiscovered; - - private Set allies; - /** List of usernames of this player's allies. May not be accurate 100% of the time. This is here so that a player - * can view the usernames of their allies even when those allies are not online. - * Do not use this for any other purpose than displaying the names! */ - public Set allyNames; - - private Set soulboundCreatures; - - public WizardData(EntityPlayer player){ - this.player = player; - this.imbuementDurations = new HashMap(); - this.spellsDiscovered = new HashSet(); - // All players can recognise magic missile. This is not done using discoverSpell because that seems to cause - // a crash on load occasionally (probably something to do with achievements being initalised) - this.spellsDiscovered.add(Spells.magic_missile); - this.hasSpiritWolf = false; - this.hasSpiritHorse = false; - this.currentlyCasting = Spells.none; - this.spellModifiers = new SpellModifiers(); - this.castingTick = 0; - this.stoneCircleDimension = 0; - this.clairvoyanceDimension = 0; - this.setTpCountdown(0); - this.allies = new HashSet(); - this.allyNames = new HashSet(); - this.soulboundCreatures = new HashSet(); - } - - public boolean hasSpellBeenDiscovered(Spell spell){ - return spellsDiscovered.contains(spell) || spell instanceof None; - } - - /** - * Adds the given spell to the list of discovered spells for this player. Automatically takes into account - * whether the spell has been discovered. Use this method rather than adding directly to the list because it - * handles achievements. - * @param spell The spell to be discovered - * @return True if the spell had not already been discovered; false otherwise. - */ - public boolean discoverSpell(Spell spell){ - - if(spellsDiscovered == null){ - spellsDiscovered = new HashSet(); - } - // The 'none' spell cannot be discovered - if(spell instanceof None) return false; - // Tries to add the spell to the list of discovered spells, and returns false if it was already present - if(!spellsDiscovered.add(spell)) return false; - // If the spell had not already been discovered, achievements can be triggered and the method returns true - if(spellsDiscovered.containsAll(Spell.getSpells(Spell::isEnabled))){ - this.player.addStat(WizardryAchievements.all_spells); - } - - for(Element element : Element.values()){ - if(element != Element.MAGIC && spellsDiscovered.containsAll(Spell.getSpells(new Spell.TierElementFilter(null, element)))){ - this.player.addStat(WizardryAchievements.element_master); - } - } - - return true; - } - - /** Sets the player's saved transportation stone location and dimension. */ - public void setStoneCircleLocation(BlockPos pos, int dimensionID){ - this.stoneCircleLocation = pos; - this.stoneCircleDimension = dimensionID; - } - - /** Returns the coordinates of the associated player's saved transportation stone circle. */ - public BlockPos getStoneCircleLocation(){ return stoneCircleLocation; } - /** Returns the dimension ID of the associated player's saved transportation stone circle. */ - public int getStoneCircleDimension(){ return stoneCircleDimension; } - - public int getTpCountdown() { return tpCountdown; } - - public void setTpCountdown(int tpCountdown) { this.tpCountdown = tpCountdown; } - - /** Sets the player's saved clairvoyance location. */ - public void setClairvoyancePoint(BlockPos pos, int dimensionID){ - this.clairvoyanceLocation = pos; - this.clairvoyanceDimension = dimensionID; - } - - /** Returns the coordinates for the saved clairvoyance location. Will be null if no location is saved. */ - public BlockPos getClairvoyanceLocation(){ return clairvoyanceLocation; } - - /** Returns the dimension ID for the saved clairvoyance location. Will be null if no location is saved. */ - public int getClairvoyanceDimension(){ return clairvoyanceDimension; } - - /** Overwrites the imbuement duration associated with the given imubement for this player, or creates it if there - * was none previously. - * @throws IllegalArgumentException if the given {@link Enchantment} is not an {@link Imbuement}. */ - public void setImbuementDuration(Enchantment enchantment, int duration){ - // It is best to throw an exception here, because otherwise the error would either go unnoticed (if non-imbuements - // were ignored) or cause a ClassCastException later (if non-imbuements were allowed to be added). - if(enchantment instanceof Imbuement){ - this.imbuementDurations.put((Imbuement) enchantment, duration); - }else{ - throw new IllegalArgumentException("Attempted to set an imbuement duration for something that isn't an Imbuement! (This exception has been thrown now to prevent a ClassCastException from occurring later.)"); - } - } - - /** Returns the imbuement duration associated with the given imbuement for this player, or 0 if it does not exist. */ - @SuppressWarnings("unlikely-arg-type") - public int getImbuementDuration(Enchantment enchantment){ - // Need to check that i is not null, otherwise it throws an NPE when Java auto-unboxes it. - // What's nice here is that the map simply accepts objects as keys, so there's no need to cast or throw exceptions. - Integer i = this.imbuementDurations.get(enchantment); - // If i is null, returns 0; otherwise returns i, auto-unboxed to an int. - return i == null ? 0 : i; - } - - /** Decrements the duration for each conjured item by 1, and removes from the map any that are 0 or less or that - * the player no longer has. Also deletes the item from the player's inventory if it runs out of time. */ - private void updateImbuedItems(){ - - Set activeImbuements = new HashSet(); - - // For each item in the player's inventory - for(ItemStack stack : player.inventory.mainInventory){ - if(stack != null && stack.isItemEnchanted()){ - - Map enchantments = EnchantmentHelper.getEnchantments(stack); - - Iterator> iterator = enchantments.entrySet().iterator(); - // For each of the item's enchantments - while(iterator.hasNext()){ - Enchantment enchantment = iterator.next().getKey(); - // Ignores the enchantment unless it is an imbuement - if(enchantment instanceof Imbuement){ - int duration = this.getImbuementDuration(enchantment); - // If the imbuement is still active: - if(duration > 0){ - // Decrements the timer - this.imbuementDurations.put((Imbuement)enchantment, duration-1); - // Adds this imbuement to the set of imbuements that need to be kept - activeImbuements.add((Imbuement)enchantment); - // Otherwise: - }else{ - // Removes the enchantment from the enchantment map - iterator.remove(); - // Applies the new enchantment map to the item - EnchantmentHelper.setEnchantments(enchantments, stack); - } - } - } - } - } - // Removes all imbuements from the map that are no longer active - this.imbuementDurations.keySet().retainAll(activeImbuements); - } - - /** Adds the given player to the list of allies belonging to the associated player, or removes the player if - * they are already in the list of allies. Returns true if the player was added, false if they were removed. */ - public boolean toggleAlly(EntityPlayer player){ - if(this.isPlayerAlly(player)){ - this.allies.remove(player.getUniqueID()); - // The remove method uses .equals() rather than == so this will work fine. - this.allyNames.remove(player.getName()); - return false; - }else{ - this.allies.add(player.getUniqueID()); - this.allyNames.add(player.getName()); - return true; - } - } - - /** Returns whether the given player is in this player's list of allies, or is on the same team as this player. */ - public boolean isPlayerAlly(EntityPlayer player){ - return this.allies.contains(player.getUniqueID()) || this.player.isOnSameTeam(player); - } - - /** Adds the given entity to this player's list of soulbound creatures, and returns whether it succeeded. */ - public boolean soulbind(EntityLivingBase target){ - return this.soulboundCreatures.add(target.getUniqueID()); - } - - /** Returns whether the given entity has been soulbound to this player. */ - public boolean isCreatureSoulbound(EntityPlayer target){ - return this.soulboundCreatures.contains(target.getUniqueID()); - } - - /** Damages all creatures soulbound to this player by the given amount, and removes from the list any that no - * longer exist. */ - public void damageAllSoulboundCreatures(float damage){ - - for(Iterator iterator = this.soulboundCreatures.iterator(); iterator.hasNext();){ - - Entity entity = WizardryUtilities.getEntityByUUID(this.player.worldObj, iterator.next()); - - if(entity == null) iterator.remove(); - - if(entity instanceof EntityLivingBase){ - // Retaliatory effect - if(entity.attackEntityFrom(MagicDamage.causeDirectMagicDamage(this.player, DamageType.MAGIC, true), damage)){ - // Sound only plays if the damage succeeds - player.playSound(SoundEvents.ENTITY_WITHER_HURT, 1.0F, player.worldObj.rand.nextFloat() * 0.2F + 1.0F); - } - } - } - } - - /** Starts casting the given spell with the given modifiers. */ - public void startCastingContinuousSpell(Spell spell, SpellModifiers modifiers){ - - this.currentlyCasting = spell; - this.spellModifiers = modifiers; - - if(!this.player.worldObj.isRemote){ - PacketCastContinuousSpell.Message message = new PacketCastContinuousSpell.Message(this.player.getEntityId(), - spell.id(), this.spellModifiers); - WizardryPacketHandler.net.sendToDimension(message, this.player.worldObj.provider.getDimension()); - } - } - - /** Stops casting the current spell. */ - public void stopCastingContinuousSpell(){ - - this.currentlyCasting = Spells.none; - this.castingTick = 0; - this.spellModifiers.reset(); - - if(!this.player.worldObj.isRemote){ - PacketCastContinuousSpell.Message message = new PacketCastContinuousSpell.Message(this.player.getEntityId(), - Spells.none.id(), this.spellModifiers); - WizardryPacketHandler.net.sendToDimension(message, this.player.worldObj.provider.getDimension()); - } - } - - /** Casts the current continuous spell, fires relevant events and updates the castingTick field. */ - public void updateContinuousSpellCasting(){ - - if(this.currentlyCasting != null && this.currentlyCasting.isContinuous){ - - if(MinecraftForge.EVENT_BUS.post(new SpellCastEvent.Tick(player, currentlyCasting, spellModifiers, Source.COMMAND, castingTick))){ - this.stopCastingContinuousSpell(); - return; - } - - if(this.currentlyCasting.cast(player.worldObj, player, EnumHand.MAIN_HAND, castingTick, this.spellModifiers) - && this.castingTick == 0){ - // On the first tick casting a continuous spell via commands, SpellCastEvent.Post is fired. - MinecraftForge.EVENT_BUS.post(new SpellCastEvent.Post(player, currentlyCasting, spellModifiers, Source.COMMAND)); - } - - castingTick++; - - }else{ - // Why is this here? Surely castingTick will always be 0 if currentlyCasting is null? - this.castingTick = 0; - } - } - - /** Returns whether this player is currently casting a continuous spell via commands. */ - public boolean isCasting(){ - return this.currentlyCasting != null && this.currentlyCasting != Spells.none; - } - - /** Returns the continuous spell this player is currently casting via commands, or the 'none' spell if they aren't - * casting anything. */ - public Spell currentlyCasting(){ - return currentlyCasting; - } - - /** Called each time the associated player is updated. */ - private void update(){ - - if(this.selectedMinion != null && this.selectedMinion.get() == null) this.selectedMinion = null; - - // This new system removes a lot of repetitive event handler code and inflexible variables which had duplicate - // functions, just for different enchantments. - updateImbuedItems(); - - if(!player.worldObj.isRemote){ - if(getTpCountdown() == 1){ - player.setPositionAndUpdate(this.stoneCircleLocation.getX() + 0.5, this.stoneCircleLocation.getY(), - this.stoneCircleLocation.getZ() + 0.5); - player.addPotionEffect(new PotionEffect(MobEffects.BLINDNESS, 50, 0)); - IMessage msg = new PacketTransportation.Message(player.getEntityId()); - WizardryPacketHandler.net.sendToDimension(msg, player.worldObj.provider.getDimension()); - } - - if(getTpCountdown() > 0){ - setTpCountdown(getTpCountdown() - 1); - } - } - - updateContinuousSpellCasting(); - } - - /** - * Returns the WizardData instance for the specified player. - */ - public static final WizardData get(EntityPlayer player){ - return player.getCapability(WIZARD_DATA_CAPABILITY, null); - } - - /** Called from the event handler each time the associated player entity is cloned, i.e. on respawn or when - * travelling to a different dimension. Used to copy over any variables that should persist over player death. - * This is the inverse of the old onPlayerDeath method, which reset the variables that shouldn't persist. - * @param data The old WizardData whose variables are to be copied over. - * @param respawn True if the player died and is respawning, false if they are just travelling between dimensions. */ - public void copyFrom(WizardData data, boolean respawn){ - // TODO: What happens with spirit wolf and spirit horse? - this.hasSpiritHorse = data.hasSpiritHorse; - this.hasSpiritWolf = data.hasSpiritWolf; - this.allies = data.allies; - this.allyNames = data.allyNames; - this.clairvoyanceDimension = data.clairvoyanceDimension; - this.clairvoyanceLocation = data.clairvoyanceLocation; - this.selectedMinion = data.selectedMinion; - // Curse of soulbinding is lifted when the caster dies, but not when they switch dimensions. - if(!respawn) this.soulboundCreatures = data.soulboundCreatures; - this.spellsDiscovered = data.spellsDiscovered; - this.stoneCircleDimension = data.stoneCircleDimension; - this.stoneCircleLocation = data.stoneCircleLocation; - - // Imbuements are lost on death so their durations do not persist. - // Command spell casting is reset on death so the associated variables do not persist. - // tpCountdown is reset both when the player dies and when they switch dimensions. - - } - - /** Sends a packet to this player's client to synchronise necessary information. Only called server side. */ - public void sync(){ - if(this.player instanceof EntityPlayerMP){ - int id = -1; - if(this.selectedMinion != null && this.selectedMinion.get() instanceof Entity) id = ((Entity)this.selectedMinion.get()).getEntityId(); - IMessage msg = new PacketPlayerSync.Message(this.spellsDiscovered, id); - WizardryPacketHandler.net.sendTo(msg, (EntityPlayerMP)this.player); - } - } - - @Override - public NBTTagCompound serializeNBT() { - - NBTTagCompound properties = new NBTTagCompound(); - - // ...so Java 8 allows you to do stuff like this: - properties.setTag("imbuements", WizardryUtilities.mapToNBT(this.imbuementDurations, - imbuement -> new NBTTagInt(Enchantment.getEnchantmentID((Enchantment)imbuement)), NBTTagInt::new)); - - properties.setBoolean("hasSpiritWolf", this.hasSpiritWolf); - properties.setBoolean("hasSpiritHorse", this.hasSpiritHorse); - - if(this.stoneCircleLocation != null) properties.setLong("stoneCircleLocation", this.stoneCircleLocation.toLong()); - properties.setInteger("stoneCircleDimension", this.stoneCircleDimension); - properties.setInteger("tpCountdown", this.tpCountdown); - - if(this.clairvoyanceLocation != null) properties.setLong("clairvoyanceLocation", this.clairvoyanceLocation.toLong()); - properties.setInteger("clairvoyanceDimension", this.getClairvoyanceDimension()); - - // THIS is why I wrote the list/map <-> NBT methods. Look how neat this is! - properties.setTag("allies", WizardryUtilities.listToNBT(this.allies, WizardryUtilities::UUIDtoTagCompound)); - properties.setTag("allyNames", WizardryUtilities.listToNBT(this.allyNames, NBTTagString::new)); - properties.setTag("soulboundCreatures", WizardryUtilities.listToNBT(this.soulboundCreatures, WizardryUtilities::UUIDtoTagCompound)); - - // Might be worth converting this over to WizardryUtilities.listToNBT. - int[] spells = new int[this.spellsDiscovered.size()]; - int i=0; - for(Spell spell : this.spellsDiscovered){ - spells[i] = spell.id(); - i++; - } - properties.setIntArray("discoveredSpells", spells); - - return properties; - } - - @Override - public void deserializeNBT(NBTTagCompound nbt) { - - if(nbt != null){ - - this.imbuementDurations = WizardryUtilities.NBTToMap(nbt.getTagList("imbuements", NBT.TAG_COMPOUND), - (NBTTagInt tag) -> (Imbuement)Enchantment.getEnchantmentByID(tag.getInt()), NBTTagInt::getInt); - - this.hasSpiritWolf = nbt.getBoolean("hasSpiritWolf"); - this.hasSpiritHorse = nbt.getBoolean("hasSpiritHorse"); - - this.stoneCircleLocation = BlockPos.fromLong(nbt.getLong("stoneCircleLocation")); - this.stoneCircleDimension = nbt.getInteger("stoneCircleDimension"); - this.tpCountdown = nbt.getInteger("tpCountdown"); - - this.clairvoyanceLocation = BlockPos.fromLong(nbt.getLong("clairvoyanceLocation")); - this.clairvoyanceDimension = nbt.getInteger("clairvoyanceDimension"); - - this.allies = new HashSet(WizardryUtilities.NBTToList(nbt.getTagList("allies", NBT.TAG_COMPOUND), - WizardryUtilities::tagCompoundToUUID)); - - this.allyNames = new HashSet(WizardryUtilities.NBTToList(nbt.getTagList("allyNames", NBT.TAG_STRING), - NBTTagString::getString)); - - this.soulboundCreatures = new HashSet(WizardryUtilities.NBTToList(nbt.getTagList("soulboundCreatures", NBT.TAG_COMPOUND), - WizardryUtilities::tagCompoundToUUID)); - - this.spellsDiscovered = new HashSet(); - for(int id : nbt.getIntArray("discoveredSpells")){ - spellsDiscovered.add(Spell.get(id)); - } - } - } - - // Event handlers - - @SubscribeEvent - // The type parameter here has to be Entity, not EntityPlayer, or the event won't get fired. - public static void onCapabilityLoad(AttachCapabilitiesEvent event){ - - if(event.getObject() instanceof EntityPlayer) - event.addCapability(new ResourceLocation(Wizardry.MODID, "WizardData"), new WizardData.Provider((EntityPlayer)event.getObject())); - - // This demonstrates why capabilities are badly structured: The following code compiles, but what it does is put - // a player into a CapabilityDispatcher, which is in turn stored in that very same player, which makes no sense - // at all! - //event.addCapability(new ResourceLocation(Wizardry.MODID, "WizardData"), event.getObject()); - } - - @SubscribeEvent - public static void onPlayerCloneEvent(PlayerEvent.Clone event){ - - WizardData newData = WizardData.get(event.getEntityPlayer()); - WizardData oldData = WizardData.get(event.getOriginal()); - - newData.copyFrom(oldData, event.isWasDeath()); - } - - @SubscribeEvent - public static void onEntityJoinWorld(EntityJoinWorldEvent event){ - if(!event.getEntity().worldObj.isRemote && event.getEntity() instanceof EntityPlayerMP){ - // Synchronises wizard data after loading. - WizardData data = WizardData.get((EntityPlayer)event.getEntity()); - if(data != null) data.sync(); - } - } - - @SubscribeEvent - public static void onLivingUpdateEvent(LivingUpdateEvent event){ - - if(event.getEntityLiving() instanceof EntityPlayer){ - - EntityPlayer player = (EntityPlayer)event.getEntityLiving(); - - if(WizardData.get(player) != null){ - WizardData.get(player).update(); - } - } - } - - /** This is a nested class for a few reasons: firstly, it makes sense because instances of this and WizardData go - * hand-in-hand; secondly, it's too short to be worth a separate file; and thirdly (and most importantly) it allows - * me to access WIZARD_DATA_CAPABILITY while keeping it private. */ - public static class Provider implements ICapabilitySerializable { - - private final WizardData data; - - public Provider(EntityPlayer player){ - data = new WizardData(player); - } - - @Override - public boolean hasCapability(Capability capability, EnumFacing facing) { - return capability == WIZARD_DATA_CAPABILITY; - } - - @Override - public T getCapability(Capability capability, EnumFacing facing) { - - if(capability == WIZARD_DATA_CAPABILITY){ - return WIZARD_DATA_CAPABILITY.cast(data); - } - - return null; - } - - @Override - public NBTTagCompound serializeNBT() { - return data.serializeNBT(); - } - - @Override - public void deserializeNBT(NBTTagCompound nbt) { - data.deserializeNBT(nbt); - } - - } - - // Ended up deleting IWizardData because it was unnecessary. This is the comment that was at the start of it: - - /* I'm not going to lie, I will never find the capabilities system even remotely intuitive so this is a bare-minimum - * approach just to get things working (four classes where one would have done?!) At one point I considered simply - * wrapping my old IEEP inside a single-field capability, but I eventually decided I would at least *try* to do it - * properly. - * - * "...without having to directly implement many interfaces." - Forge Docs. I still can't see what's wrong with - * implementing many interfaces; surely that's what Java interfaces are designed for? - * - * Other things I find annoying: - * - IStorage. It's completely redundant in the majority of cases, and I don't understand why we need yet another - * separate class. - * - Making an interface, only to implement it once and once only. This completely defeats the point of interfaces. - * - The EnumFacing parameter, which is again redundant for everything that isn't a tile entity. So much for a clean, - * neat system. - * - * What Forge has effectively done is conflated two different functions: attaching data to stuff and cross-mod - * integration/soft dependencies. I think this is bad design; it would have been better to keep the two features separate. - * - * Here's my current understanding of how the capability system works: - * - You make an interface which defines the things your capability can do (this class). I will call this the TEMPLATE. - * - You implement that interface with your default implementation (WizardData). This is the closest analog to your old - * IEEP implementation class. THIS CLASS STORES ALL THE VARIABLES, and hence has one instance for each instance of whatever - * it is attached to. I will call this the DATA. - * - The DATA class implements INBTSerializable (assuming you want it to be saved, which is nearly always the case) - * - Despite its name, Capability does NOT represent a capability itself. Instead, it acts as a sort of identifier/key, - * the idea being that you can access a particular instance of your DATA given the key (which tells forge that you want a - * capability of type TEMPLATE) and the object you want the DATA for. This is what Entity.getCapability(...) does. - * - * To really understand what's going on though, you need to sift through Forge's verbose data structures and find where - * capabilities are actually hooked into vanilla: - * - Anything that implements ICapabilityProvider will have a private CapabilityDispatcher field. This holds other - * ICapabilityProviders. (I know. This inheritance pattern DOES NOT MAKE SENSE, because these could, in theory, be OTHER - * ENTITIES!) - * - This field is assigned a value through Forge's event factory, which, as we are all familiar with, calls all the - * methods marked with @SubscribeEvent. These methods add individual ICapabilityProviders to a Map stored in the event, - * which the event factory then wraps in a CapabilityDispatcher (which is itself an ICapabilityProvider) for the object - * that called it. - * - In your event handler, you return a custom ICapabilityProvider which is effectively bolted on to the player, and - * duplicates the ICapabilityProvider methods so you can hook into them and return an instance of your DATA class. - * - Where before there was a simple collection of IEEPs stored in the player, there is now a tree of ICapabilityProviders: - * - * - Entity/TileEntity/ItemStack - * - Vanilla ICapabilityProviders, mostly IItemHandlers, stored as fields. - * - CapabilityDispatcher, stored as a field. - * - Custom ICapabilityProviders - * - Custom CapabilityDispatchers - * - ... - * - * Most importantly, EACH PLAYER HOLDS THEIR OWN INSTANCE OF THIS TREE. - * - * When a capability is retrieved, the following process happens: - * 1. For the Entity/TileEntity/ItemStack instance, ICapabilityProvider.getCapability(...) is called. - * 2. The request propogates through the tree and finds the requested capability. */ - -} diff --git a/src/main/java/electroblob/wizardry/Wizardry.java b/src/main/java/electroblob/wizardry/Wizardry.java index 69f955fe..98e9bd8b 100644 --- a/src/main/java/electroblob/wizardry/Wizardry.java +++ b/src/main/java/electroblob/wizardry/Wizardry.java @@ -1,173 +1,665 @@ package electroblob.wizardry; -import org.apache.logging.log4j.Logger; +import java.lang.reflect.Field; +import java.lang.reflect.Modifier; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.Comparator; +import java.util.List; +import java.util.Locale; +import com.google.common.base.Function; +import com.google.common.collect.Ordering; + +import cpw.mods.fml.client.event.ConfigChangedEvent; +import cpw.mods.fml.common.FMLCommonHandler; +import cpw.mods.fml.common.Mod; +import cpw.mods.fml.common.Mod.EventHandler; +import cpw.mods.fml.common.Mod.Instance; +import cpw.mods.fml.common.SidedProxy; +import cpw.mods.fml.common.event.FMLInitializationEvent; +import cpw.mods.fml.common.event.FMLPostInitializationEvent; +import cpw.mods.fml.common.event.FMLPreInitializationEvent; +import cpw.mods.fml.common.event.FMLServerStartingEvent; +import cpw.mods.fml.common.eventhandler.SubscribeEvent; +import cpw.mods.fml.common.network.NetworkRegistry; +import cpw.mods.fml.common.registry.GameRegistry; +import cpw.mods.fml.relauncher.Side; +import cpw.mods.fml.relauncher.SideOnly; +import electroblob.wizardry.MagicDamage.DamageType; +import electroblob.wizardry.block.BlockArcaneWorkbench; +import electroblob.wizardry.block.BlockCrystal; +import electroblob.wizardry.block.BlockCrystalFlower; +import electroblob.wizardry.block.BlockCrystalOre; +import electroblob.wizardry.block.BlockMagicLight; +import electroblob.wizardry.block.BlockSnare; +import electroblob.wizardry.block.BlockSpectral; +import electroblob.wizardry.block.BlockStatue; +import electroblob.wizardry.block.BlockTransportationStone; +import electroblob.wizardry.block.BlockVanishingCobweb; import electroblob.wizardry.command.CommandCastSpell; import electroblob.wizardry.command.CommandDiscoverSpell; import electroblob.wizardry.command.CommandSetAlly; import electroblob.wizardry.command.CommandViewAllies; +import electroblob.wizardry.enchantment.EnchantmentMagicSword; +import electroblob.wizardry.enchantment.EnchantmentTimed; +import electroblob.wizardry.item.ItemArcaneTome; +import electroblob.wizardry.item.ItemArmourUpgrade; +import electroblob.wizardry.item.ItemFirebomb; +import electroblob.wizardry.item.ItemFlamingAxe; +import electroblob.wizardry.item.ItemFrostAxe; +import electroblob.wizardry.item.ItemIdentificationScroll; +import electroblob.wizardry.item.ItemPoisonBomb; +import electroblob.wizardry.item.ItemScroll; +import electroblob.wizardry.item.ItemSmokeBomb; +import electroblob.wizardry.item.ItemSpawnWizard; +import electroblob.wizardry.item.ItemSpectralArmour; +import electroblob.wizardry.item.ItemSpectralBow; +import electroblob.wizardry.item.ItemSpectralPickaxe; +import electroblob.wizardry.item.ItemSpectralSword; +import electroblob.wizardry.item.ItemSpellBook; +import electroblob.wizardry.item.ItemWand; +import electroblob.wizardry.item.ItemWizardArmour; +import electroblob.wizardry.item.ItemWizardHandbook; import electroblob.wizardry.packet.WizardryPacketHandler; -import electroblob.wizardry.registry.WizardryAchievements; -import electroblob.wizardry.registry.WizardryItems; -import electroblob.wizardry.registry.WizardryRegistry; -import electroblob.wizardry.registry.WizardryTabs; +import electroblob.wizardry.potion.PotionDecay; +import electroblob.wizardry.potion.PotionFrost; +import electroblob.wizardry.potion.PotionMagicEffect; +import electroblob.wizardry.spell.Spell; +import net.minecraft.block.Block; +import net.minecraft.block.material.Material; +import net.minecraft.creativetab.CreativeTabs; +import net.minecraft.enchantment.Enchantment; +import net.minecraft.entity.Entity; +import net.minecraft.entity.EntityList; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.init.Blocks; +import net.minecraft.init.Items; import net.minecraft.item.Item; -import net.minecraft.nbt.NBTBase; -import net.minecraft.util.EnumFacing; +import net.minecraft.item.Item.ToolMaterial; +import net.minecraft.item.ItemArmor; +import net.minecraft.item.ItemStack; +import net.minecraft.potion.Potion; +import net.minecraft.stats.Achievement; +import net.minecraft.util.StatCollector; +import net.minecraft.world.World; import net.minecraftforge.common.AchievementPage; -import net.minecraftforge.common.MinecraftForge; -import net.minecraftforge.common.capabilities.Capability; -import net.minecraftforge.common.capabilities.Capability.IStorage; -import net.minecraftforge.common.capabilities.CapabilityManager; -import net.minecraftforge.fml.common.Mod; -import net.minecraftforge.fml.common.Mod.EventHandler; -import net.minecraftforge.fml.common.Mod.Instance; -import net.minecraftforge.fml.common.SidedProxy; -import net.minecraftforge.fml.common.event.FMLInitializationEvent; -import net.minecraftforge.fml.common.event.FMLMissingMappingsEvent; -import net.minecraftforge.fml.common.event.FMLPostInitializationEvent; -import net.minecraftforge.fml.common.event.FMLPreInitializationEvent; -import net.minecraftforge.fml.common.event.FMLServerStartingEvent; -import net.minecraftforge.fml.common.eventhandler.SubscribeEvent; -import net.minecraftforge.fml.common.network.NetworkRegistry; -import net.minecraftforge.fml.common.registry.GameRegistry; -import net.minecraftforge.fml.common.registry.GameRegistry.Type; +import net.minecraftforge.common.config.Configuration; +import net.minecraftforge.common.config.Property; +import net.minecraftforge.common.util.EnumHelper; @Mod(modid = Wizardry.MODID, name = Wizardry.NAME, version = Wizardry.VERSION, guiFactory = "electroblob." + Wizardry.MODID + ".WizardryGuiFactory") public class Wizardry { - /** Wizardry's mod ID. */ public static final String MODID = "wizardry"; - /** Wizardry's mod name, in readable form. */ public static final String NAME = "Electroblob's Wizardry"; - /** The version number for this version of wizardry. The following system is used for version numbers: - *

    [major Minecraft version].[major mod version].[minor mod version/patch]

    - * The major mod version is consistent across Minecraft versions, i.e. Wizardry 1.1 has the same features as - * Wizardry 2.1, but they are for different versions of Minecraft and have separate minor versioning. 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. */ - public static final String VERSION = "2.1.2"; - - // IDEA: Improve the algorithm that finds a place to summon creatures to take walls into account. - // IDEA: Replace all uses of Math.cos and Math.sin with MathHelper versions - // IDEA: API feature that automatically injects custom wand upgrades into wizardry's loot tables - // IDEA: Support for the @e selector in the /cast command (the methods already exist, just plug them in) - // IDEA: Dispensers shooting spells from scrolls! Imagine the possibilities! - // == Rendering overhaul! == - // IDEA: Make spirit animals fade away when dispelled - // IDEA: Use your newfound knowledge of OpenGL to render animated boxes around entities being buffed, similar to creeper charge - // IDEA: Redo RenderArc so it looks like lightning (it's literally a load of translucent boxes inside each other) - // IDEA: The arcane workbench could use a few particles - // IDEA: Add a box to the wizard armour model so that the robes bend when the wearer is sneaking - // IDEA: Flash particle (I think it exists in vanilla somewhere) on spell impact - // IDEA: Dust particles spray out from projectiles as they move - // IDEA: Colour fading for particles - // IDEA: Make lightning and other effects particles. There's no reason why you can't pass in a player argument and have the particles move with them! - - /* Minor bugs that need fixing at some point: - * - Player skin hat layer shows through wizard hats - * - Wizard armour breaks rather than just running out of mana (I can't seem to replicate this bug, but I have had - * it happen to me before...) - * - Shift-clicking a stack of special upgrades when in the arcane workbench causes the whole stack to be - * transferred when it should be just one (this is a bug with vanilla as well - try putting a stack of bottles into - * a brewing stand). I have at least made it so only one gets used now, so it has no impact on the game. - * - When a spell is on cooldown, you can't break blocks when holding a wand. */ - - // TODO: So somehow I have managed to overlook the fact that health is actually a float. What this means is that - // I can make the healing spells use damage multipliers - hurrah! + public static final String VERSION = "1.1.5"; - // TODO: Switch from IInventory to IItemHandler (Or don't. It's only useful for automation really.) - // TODO: Triggering of inbuilt Forge events in relevant places? - // TODO: Implement custom events - // TODO: Rearrange the config gui and file + // Constants + /** The amount of mana each magic crystal is worth */ + public static final int MANA_PER_CRYSTAL = 100; + /** The amount of mana each mana flask can hold */ + public static final int MANA_PER_FLASK = 700; + /** The maximum number of one type of wand upgrade which can be applied to a wand. */ + public static final int UPGRADE_STACK_LIMIT = 3; + /** The fraction by which cooldowns are reduced for each level of cooldown upgrade. */ + public static final float COOLDOWN_REDUCTION_PER_LEVEL = 0.15f; + /** The fraction by which maximum charge is increased for each level of storage upgrade. */ + public static final float STORAGE_INCREASE_PER_LEVEL = 0.15f; + /** The fraction by which damage is increased for each tier of matching wand. */ + public static final float DAMAGE_INCREASE_PER_TIER = 0.15f; + /** The fraction by which costs are reduced for each piece of matching armour. Note that changing this value will not + * affect continuous spells, since they are handled differently. */ + public static final float COST_REDUCTION_PER_ARMOUR = 0.2f; + /** The fraction by which spell duration is increased for each level of duration upgrade. */ + public static final float DURATION_INCREASE_PER_LEVEL = 0.25f; + /** The fraction by which spell range is increased for each level of range upgrade. */ + public static final float RANGE_INCREASE_PER_LEVEL = 0.25f; + /** The fraction by which spell blast radius is increased for each level of range upgrade. */ + public static final float BLAST_RADIUS_INCREASE_PER_LEVEL = 0.25f; + /** The fraction by which movement speed is reduced per level of frost effect. */ + public static final double FROST_SLOWNESS_PER_LEVEL = 0.5; + /** The fraction by which movement speed is reduced per level of decay effect. */ + public static final double DECAY_SLOWNESS_PER_LEVEL = 0.2; + /** The fraction by which dig speed is reduced per level of frost effect. */ + public static final float FROST_FATIGUE_PER_LEVEL = 0.45f; + /** The number of ticks between each mana increase for wands with the condenser upgrade. */ + public static final int CONDENSER_TICK_INTERVAL = 50; + /** The amount of mana given for a kill for each level of siphon upgrade. A random amount from 0 to this number - 1 + * is also added. See {@link WizardryEventHandler#onLivingDeathEvent} for more details. */ + public static final int SIPHON_MANA_PER_LEVEL = 3; + /** The number of ticks between the spawning of patches of decay when an entity has the decay effect. + * Note that decay won't spawn again if something is already standing in it. */ + public static final int DECAY_SPREAD_INTERVAL = 8; - // TODO: Have particles obey Minecraft's particle setting where appropriate - // (see https://github.com/RootsTeam/Embers/blob/master/src/main/java/teamroots/embers/particle/ParticleUtil.java) + // Category names + /** The unlocalised name of the spells config category. */ + public static final String SPELLS_CATEGORY = "spells"; + /** The unlocalised name of the ids config category. */ + public static final String IDS_CATEGORY = "ids"; + /** The unlocalised name of the ids config category. */ + public static final String RESISTANCES_CATEGORY = "resistances"; + + // Chest loot + /** The frequency with which spell books will be generated in dungeon chests. Higher numbers are more common. */ + public static final int SPELL_BOOK_FREQUENCY = 20; + /** The frequency with which (basic) wands will be generated in dungeon chests. Higher numbers are more common. */ + public static final int WAND_FREQUENCY = 3; + /** The frequency with which wizard armour will be generated in dungeon chests. Higher numbers are more common. */ + public static final int ARMOUR_FREQUENCY = 6; + /** The frequency with which spell books will be generated in dungeon chests. Higher numbers are more common. */ + public static final int SCROLL_FREQUENCY = 10; + /** The frequency with which armour upgrades will be generated in dungeon chests. Higher numbers are more common. */ + public static final int WAND_UPGRADE_FREQUENCY = 2; + /** The frequency with which spell books will be generated in dungeon chests. Higher numbers are more common. */ + public static final int CRYSTAL_FREQUENCY = 5; + /** The frequency with which apprentice tomes of arcana will be generated in dungeon chests. Higher numbers are more common. */ + public static final int APPRENTICE_TOME_FREQUENCY = 3; + /** The frequency with which advanced tomes of arcana will be generated in dungeon chests. Higher numbers are more common. */ + public static final int ADVANCED_TOME_FREQUENCY = 2; + /** The frequency with which master tomes of arcana will be generated in dungeon chests. Higher numbers are more common. */ + public static final int MASTER_TOME_FREQUENCY = 1; + /** The frequency with which armour upgrades will be generated in dungeon chests. Higher numbers are more common. */ + public static final int ARMOUR_UPGRADE_FREQUENCY = 1; + /** The frequency with which firebombs will be generated in dungeon chests. Higher numbers are more common. */ + public static final int FIREBOMB_FREQUENCY = 4; + /** The frequency with which poison bombs will be generated in dungeon chests. Higher numbers are more common. */ + public static final int POISON_BOMB_FREQUENCY = 4; + /** The frequency with which smoke bombs will be generated in dungeon chests. Higher numbers are more common. */ + public static final int SMOKE_BOMB_FREQUENCY = 4; + /** The frequency with which scrolls of identification will be generated in dungeon chests. Higher numbers are more common. */ + public static final int IDENTIFICATION_SCROLL_FREQUENCY = 3; - // NOTE: Add melee upgrades to loot tables when they are added. + // Global options + /** The rarity of wizard towers, used by the world generator. Larger numbers are rarer. */ + public static int towerRarity = 8; + /** List of dimension ids in which to generate crystal ore. */ + public static int[] oreDimensions = {0}; + /** List of dimension ids in which to generate crystal ore. */ + public static int[] flowerDimensions = {0}; + /** List of dimension ids in which to generate crystal ore. */ + public static int[] towerDimensions = {0}; + /** Chance (out of 200) for mobs to drop spell books. */ + public static int spellBookDropChance = 3; + /** Whether or not wizardry loot should generate in dungeon chests. */ + public static boolean generateLoot = true; + /** Whether or not firebombs should be craftable. */ + public static boolean firebombIsCraftable = true; + /** Whether or not poison bombs should be craftable. */ + public static boolean poisonBombIsCraftable = true; + /** Whether or not poison bombs should be craftable. */ + public static boolean smokeBombIsCraftable = true; + /** Whether to require a magic crystal in the blank scroll crafting recipe (in case it conflicts with another mod). */ + public static boolean useAlternateScrollRecipe = false; + /** Whether or not players can teleport through unbreakable blocks (e.g. bedrock) using the phase step spell. */ + public static boolean teleportThroughUnbreakableBlocks = false; + /** Whether to show summoned creatures' names and owners above their heads. */ + public static boolean showSummonedCreatureNames = true; + /** Whether to allow players to damage their designated allies using magic. */ + public static boolean friendlyFire = true; + /** Whether to allow players to disarm other players using the telekinesis spell. */ + public static boolean telekineticDisarmament = true; + /** When set to true, spells a player hasn't cast yet will be unreadable until they are cast (on a per-world basis). Has no effect when in creative mode. Spells of + * identification will be unobtainable in survival mode if this is false. */ + public static boolean discoveryMode = true; + /** Whether the player can switch between spells on a wand by scrolling with the mouse wheel while sneaking. Note that different players connected to the same server each have their own setting for this. */ + public static boolean enableShiftScrolling = true; + /** Whether summoned creatures can revenge attack their caster if their caster attacks them. */ + public static boolean minionRevengeTargeting = true; + /** Global damage scaling factor for all player magic damage. */ + public static double playerDamageScale = 1.0f; + /** Global damage scaling factor for all npc magic damage. */ + public static double npcDamageScale = 1.0f; + /** The maximum allowed multiplier for the /cast command. This limit is here to stop people from accidentally + * breaking their worlds! */ + public static double maxSpellCommandMultiplier = 20d; + /** List of names of entities which summoned creatures are allowed to attack, in addition to the defaults. */ + public static String[] summonedCreatureTargetsWhitelist = {}; + /** List of names of entities which summoned creatures are specifically not allowed to attack, overriding the defaults and the whitelist. */ + public static String[] summonedCreatureTargetsBlacklist = {"creeper"}; + /** The position of the spell HUD. */ + public static String spellHUDPosition = "Bottom left"; + /** The name of the /cast command. */ + public static String castCommandName = "cast"; + /** The name of the /discoverspell command. */ + public static String discoverspellCommandName = "discoverspell"; + /** The name of the /ally command. */ + public static String allyCommandName = "ally"; + /** The name of the /allies command. */ + public static String alliesCommandName = "allies"; - /** Static instance of the {@link Settings} object for Wizardry. */ - public static final Settings settings = new Settings(); - - /** Static instance of the {@link Logger} object for Wizardry. */ - public static Logger logger; - + // Config ids + /** The id of the frost potion effect. */ + private static int frostPotionID = 31; + /** The id of the fireskin potion effect. */ + private static int transiencePotionID = 32; + /** The id of the fireskin potion effect. */ + private static int fireskinPotionID = 33; + /** The id of the ice shroud potion effect. */ + private static int iceShroudPotionID = 34; + /** The id of the static aura potion effect. */ + private static int staticAuraPotionID = 35; + /** The id of the decay potion effect. */ + private static int decayPotionID = 36; + /** The id of the sixth sense potion effect. */ + private static int sixthSensePotionID = 37; + /** The id of the arcane jammer potion effect. */ + private static int arcaneJammerPotionID = 38; + /** The id of the mind trick potion effect. */ + private static int mindTrickPotionID = 39; + /** The id of the mind control potion effect. */ + private static int mindControlPotionID = 40; + /** The id of the font of mana potion effect. */ + private static int fontOfManaPotionID = 41; + /** The id of the fear potion effect. */ + private static int fearPotionID = 42; + /** The id of the magic sword enchantment. */ + private static int magicSwordEnchantmentID = 100; + /** The id of the magic bow enchantment. */ + private static int magicBowEnchantmentID = 101; + /** The id of the flaming weapon enchantment. */ + private static int flamingWeaponEnchantmentID = 102; + /** The id of the flaming weapon enchantment. */ + private static int freezingWeaponEnchantmentID = 103; + + private static Comparator itemSorter; + private static Comparator spellItemSorter; //private static Pattern entityNamePattern; - - // EventManager - WizardryWorldGenerator generator = new WizardryWorldGenerator(); - // The instance of wizardry that Forge uses. - @Instance(Wizardry.MODID) + // Creative Tabs + public static CreativeTabs tabWizardry = new CreativeTabs("wizardry"){ + @Override + @SideOnly(Side.CLIENT) + public Item getTabIconItem() { + return wizardHandbook; + } + + @Override + @SideOnly(Side.CLIENT) + public void displayAllReleventItems(List items) { + // items is a list of itemstacks, not items! + super.displayAllReleventItems(items); + Collections.sort(items, itemSorter); + } + }; + + public static CreativeTabs tabSpells = new CreativeTabs("wizardryspells"){ + @Override + @SideOnly(Side.CLIENT) + public Item getTabIconItem() { + return spellBook; + } + + @Override + @SideOnly(Side.CLIENT) + public void displayAllReleventItems(List items) { + // items is a list of itemstacks, not items! + super.displayAllReleventItems(items); + Collections.sort(items, spellItemSorter); + } + }; + + /** The wizardry config file */ + public static Configuration config; + + // Anything set to use the material 'air' will not render, even with a TESR! + + // Blocks + public final static Block arcaneWorkbench = new BlockArcaneWorkbench().setHardness(1.0F).setBlockName("arcaneWorkbench").setCreativeTab(tabWizardry); + public final static Block crystalOre = new BlockCrystalOre(Material.rock).setHardness(3.0F).setBlockName("crystalOre").setCreativeTab(tabWizardry).setBlockTextureName("wizardry:crystal_ore"); + public final static Block petrifiedStone = new BlockStatue(Material.rock).setHardness(1.5F).setResistance(10.0F).setBlockName("petrifiedStone").setBlockTextureName("minecraft:stone"); + public final static Block iceStatue = new BlockStatue(Material.ice).setHardness(0.5F).setLightOpacity(3).setStepSound(Block.soundTypeGlass).setBlockName("iceStatue").setBlockTextureName("minecraft:ice"); + public final static Block magicLight = new BlockMagicLight(Material.circuits).setBlockName("magicLight").setBlockTextureName("minecraft:beacon"); + public final static Block crystalFlower = new BlockCrystalFlower(Material.plants).setHardness(0.0F).setBlockName("crystalFlower").setStepSound(Block.soundTypeGrass).setBlockTextureName("wizardry:crystal_flower").setCreativeTab(tabWizardry); + public final static Block snare = new BlockSnare(Material.plants).setHardness(0.0F).setBlockName("snare").setStepSound(Block.soundTypeGrass).setBlockTextureName("wizardry:snare"); + public final static Block transportationStone = new BlockTransportationStone(Material.rock).setHardness(0.0F).setLightLevel(0.5f).setLightOpacity(0).setBlockName("transportationStone").setCreativeTab(tabWizardry).setBlockTextureName("wizardry:transportation_stone"); + public final static Block spectralBlock = new BlockSpectral(Material.glass).setBlockName("spectralBlock").setStepSound(Block.soundTypeGlass).setBlockTextureName("wizardry:spectral_block").setLightLevel(0.7f).setLightOpacity(0).setBlockUnbreakable().setResistance(6000000.0F); + public final static Block crystalBlock = new BlockCrystal(Material.iron).setHardness(5.0F).setResistance(10.0F).setBlockName("crystalBlock").setBlockTextureName("wizardry:crystal_block").setCreativeTab(tabWizardry); + public final static Block meteor = new Block(Material.rock){}.setBlockTextureName("wizardry:meteor").setLightLevel(1); + public final static Block vanishingCobweb = new BlockVanishingCobweb(Material.web).setBlockName("vanishingCobweb").setBlockTextureName("minecraft:web").setLightOpacity(1).setHardness(4.0F); + + // Items + public final static Item magicCrystal = new Item().setTextureName("wizardry:magic_crystal").setUnlocalizedName("magicCrystal").setCreativeTab(tabWizardry); + + public final static Item magicWand = new ItemWand(EnumTier.BASIC, null).setTextureName("wizardry:wand_basic").setUnlocalizedName("magicWand"); + public final static Item apprenticeWand = new ItemWand(EnumTier.APPRENTICE, null).setTextureName("wizardry:wand_apprentice").setUnlocalizedName("apprenticeWand"); + public final static Item advancedWand = new ItemWand(EnumTier.ADVANCED, null).setTextureName("wizardry:wand_advanced").setUnlocalizedName("advancedWand"); + public final static Item masterWand = new ItemWand(EnumTier.MASTER, null).setTextureName("wizardry:wand_master").setUnlocalizedName("masterWand"); + + public final static Item arcaneTome = new ItemArcaneTome(); + public final static Item wizardHandbook = new ItemWizardHandbook(); + public final static Item spellBook = new ItemSpellBook(); + + public final static Item basicFireWand = new ItemWand(EnumTier.BASIC, EnumElement.FIRE).setTextureName("wizardry:wand_basic_fire").setUnlocalizedName("basicFireWand"); + public final static Item basicIceWand = new ItemWand(EnumTier.BASIC, EnumElement.ICE).setTextureName("wizardry:wand_basic_ice").setUnlocalizedName("basicIceWand"); + public final static Item basicLightningWand = new ItemWand(EnumTier.BASIC, EnumElement.LIGHTNING).setTextureName("wizardry:wand_basic_lightning").setUnlocalizedName("basicLightningWand"); + public final static Item basicNecromancyWand = new ItemWand(EnumTier.BASIC, EnumElement.NECROMANCY).setTextureName("wizardry:wand_basic_necromancy").setUnlocalizedName("basicNecromancyWand"); + public final static Item basicEarthWand = new ItemWand(EnumTier.BASIC, EnumElement.EARTH).setTextureName("wizardry:wand_basic_earth").setUnlocalizedName("basicEarthWand"); + public final static Item basicSorceryWand = new ItemWand(EnumTier.BASIC, EnumElement.SORCERY).setTextureName("wizardry:wand_basic_sorcery").setUnlocalizedName("basicSorceryWand"); + public final static Item basicHealingWand = new ItemWand(EnumTier.BASIC, EnumElement.HEALING).setTextureName("wizardry:wand_basic_healing").setUnlocalizedName("basicHealingWand"); + + public final static Item apprenticeFireWand = new ItemWand(EnumTier.APPRENTICE, EnumElement.FIRE).setTextureName("wizardry:wand_apprentice_fire").setUnlocalizedName("apprenticeFireWand"); + public final static Item apprenticeIceWand = new ItemWand(EnumTier.APPRENTICE, EnumElement.ICE).setTextureName("wizardry:wand_apprentice_ice").setUnlocalizedName("apprenticeIceWand"); + public final static Item apprenticeLightningWand = new ItemWand(EnumTier.APPRENTICE, EnumElement.LIGHTNING).setTextureName("wizardry:wand_apprentice_lightning").setUnlocalizedName("apprenticeLightningWand"); + public final static Item apprenticeNecromancyWand = new ItemWand(EnumTier.APPRENTICE, EnumElement.NECROMANCY).setTextureName("wizardry:wand_apprentice_necromancy").setUnlocalizedName("apprenticeNecromancyWand"); + public final static Item apprenticeEarthWand = new ItemWand(EnumTier.APPRENTICE, EnumElement.EARTH).setTextureName("wizardry:wand_apprentice_earth").setUnlocalizedName("apprenticeEarthWand"); + public final static Item apprenticeSorceryWand = new ItemWand(EnumTier.APPRENTICE, EnumElement.SORCERY).setTextureName("wizardry:wand_apprentice_sorcery").setUnlocalizedName("apprenticeSorceryWand"); + public final static Item apprenticeHealingWand = new ItemWand(EnumTier.APPRENTICE, EnumElement.HEALING).setTextureName("wizardry:wand_apprentice_healing").setUnlocalizedName("apprenticeHealingWand"); + + public final static Item advancedFireWand = new ItemWand(EnumTier.ADVANCED, EnumElement.FIRE).setTextureName("wizardry:wand_advanced_fire").setUnlocalizedName("advancedFireWand"); + public final static Item advancedIceWand = new ItemWand(EnumTier.ADVANCED, EnumElement.ICE).setTextureName("wizardry:wand_advanced_ice").setUnlocalizedName("advancedIceWand"); + public final static Item advancedLightningWand = new ItemWand(EnumTier.ADVANCED, EnumElement.LIGHTNING).setTextureName("wizardry:wand_advanced_lightning").setUnlocalizedName("advancedLightningWand"); + public final static Item advancedNecromancyWand = new ItemWand(EnumTier.ADVANCED, EnumElement.NECROMANCY).setTextureName("wizardry:wand_advanced_necromancy").setUnlocalizedName("advancedNecromancyWand"); + public final static Item advancedEarthWand = new ItemWand(EnumTier.ADVANCED, EnumElement.EARTH).setTextureName("wizardry:wand_advanced_earth").setUnlocalizedName("advancedEarthWand"); + public final static Item advancedSorceryWand = new ItemWand(EnumTier.ADVANCED, EnumElement.SORCERY).setTextureName("wizardry:wand_advanced_sorcery").setUnlocalizedName("advancedSorceryWand"); + public final static Item advancedHealingWand = new ItemWand(EnumTier.ADVANCED, EnumElement.HEALING).setTextureName("wizardry:wand_advanced_healing").setUnlocalizedName("advancedHealingWand"); + + public final static Item masterFireWand = new ItemWand(EnumTier.MASTER, EnumElement.FIRE).setTextureName("wizardry:wand_master_fire").setUnlocalizedName("masterFireWand"); + public final static Item masterIceWand = new ItemWand(EnumTier.MASTER, EnumElement.ICE).setTextureName("wizardry:wand_master_ice").setUnlocalizedName("masterIceWand"); + public final static Item masterLightningWand = new ItemWand(EnumTier.MASTER, EnumElement.LIGHTNING).setTextureName("wizardry:wand_master_lightning").setUnlocalizedName("masterLightningWand"); + public final static Item masterNecromancyWand = new ItemWand(EnumTier.MASTER, EnumElement.NECROMANCY).setTextureName("wizardry:wand_master_necromancy").setUnlocalizedName("masterNecromancyWand"); + public final static Item masterEarthWand = new ItemWand(EnumTier.MASTER, EnumElement.EARTH).setTextureName("wizardry:wand_master_earth").setUnlocalizedName("masterEarthWand"); + public final static Item masterSorceryWand = new ItemWand(EnumTier.MASTER, EnumElement.SORCERY).setTextureName("wizardry:wand_master_sorcery").setUnlocalizedName("masterSorceryWand"); + public final static Item masterHealingWand = new ItemWand(EnumTier.MASTER, EnumElement.HEALING).setTextureName("wizardry:wand_master_healing").setUnlocalizedName("masterHealingWand"); + + public final static Item spectralSword = new ItemSpectralSword(ToolMaterial.IRON).setTextureName("wizardry:spectral_sword").setUnlocalizedName("spectralSword"); + public final static Item spectralPickaxe = new ItemSpectralPickaxe(ToolMaterial.IRON).setTextureName("wizardry:spectral_pickaxe").setUnlocalizedName("spectralPickaxe"); + public final static Item spectralBow = new ItemSpectralBow().setUnlocalizedName("spectralBow"); + + public final static Item manaFlask = new Item(){ + @Override + public void onCreated(ItemStack par1ItemStack, World par2World, EntityPlayer entityplayer){ + entityplayer.triggerAchievement(Wizardry.craftFlask); + } + }.setTextureName("wizardry:mana_flask").setUnlocalizedName("manaFlask").setCreativeTab(tabWizardry); + + public final static Item storageUpgrade = new Item().setTextureName("wizardry:upgrade_storage").setUnlocalizedName("storageUpgrade").setCreativeTab(tabWizardry); + public final static Item siphonUpgrade = new Item().setTextureName("wizardry:upgrade_siphon").setUnlocalizedName("siphonUpgrade").setCreativeTab(tabWizardry); + public final static Item condenserUpgrade = new Item().setTextureName("wizardry:upgrade_condenser").setUnlocalizedName("condenserUpgrade").setCreativeTab(tabWizardry); + public final static Item rangeUpgrade = new Item().setTextureName("wizardry:upgrade_range").setUnlocalizedName("rangeUpgrade").setCreativeTab(tabWizardry); + public final static Item durationUpgrade = new Item().setTextureName("wizardry:upgrade_duration").setUnlocalizedName("durationUpgrade").setCreativeTab(tabWizardry); + public final static Item cooldownUpgrade = new Item().setTextureName("wizardry:upgrade_cooldown").setUnlocalizedName("cooldownUpgrade").setCreativeTab(tabWizardry); + public final static Item blastUpgrade = new Item().setTextureName("wizardry:upgrade_blast").setUnlocalizedName("blastUpgrade").setCreativeTab(tabWizardry); + public final static Item attunementUpgrade = new Item().setTextureName("wizardry:upgrade_attunement").setUnlocalizedName("attunementUpgrade").setCreativeTab(tabWizardry); + + public final static Item magicSilk = new Item().setTextureName("wizardry:magic_silk").setUnlocalizedName("magicSilk").setCreativeTab(tabWizardry); + + public final static Item.ToolMaterial MAGICAL = EnumHelper.addToolMaterial("MAGICAL", 3, 1000, 8.0f, 4.0f, 0); + + public final static Item flamingAxe = new ItemFlamingAxe(Wizardry.MAGICAL).setTextureName("wizardry:flaming_axe").setUnlocalizedName("flamingAxe"); + public final static Item frostAxe = new ItemFrostAxe(Wizardry.MAGICAL).setTextureName("wizardry:frost_axe").setUnlocalizedName("frostAxe"); + + public final static Item firebomb = new ItemFirebomb().setTextureName("wizardry:firebomb").setUnlocalizedName("firebomb"); + public final static Item poisonBomb = new ItemPoisonBomb().setTextureName("wizardry:poison_bomb").setUnlocalizedName("poisonBomb"); + + public final static Item blankScroll = new Item().setTextureName("wizardry:scroll").setUnlocalizedName("blankScroll").setCreativeTab(tabWizardry); + public final static Item scroll = new ItemScroll().setTextureName("wizardry:scroll").setUnlocalizedName("scroll").setCreativeTab(tabSpells); + + // The only way to get these is in dungeon chests (They are legendary, after all. Wizards don't just have them. + // Also if they were sold you could buy four from the same wizard - and that's no fun at all!) + public final static Item armourUpgrade = new ItemArmourUpgrade().setTextureName("wizardry:armour_upgrade").setUnlocalizedName("armourUpgrade"); + + public final static ItemArmor.ArmorMaterial SILK = EnumHelper.addArmorMaterial("SILK", 15, new int[]{2, 5, 4, 2}, 0); + + // Saw a post somewhere that said you have to put these in the init methods rather than defining them as constants. + // I *think* that's because the post was for a newer Minecraft version, where custom armour has its own renderer or + // something, meaning it would be done a lot like the entity rendering registry in ClientProxy. This is all working + // fine it seems, so I'm not going to fiddle with it, but it might be useful to know if I update versions again. + public final static Item wizardHat = new ItemWizardArmour(Wizardry.SILK, 1, 0, null); + public final static Item wizardRobe = new ItemWizardArmour(Wizardry.SILK, 1, 1, null); + public final static Item wizardLeggings = new ItemWizardArmour(Wizardry.SILK, 1, 2, null); + public final static Item wizardBoots = new ItemWizardArmour(Wizardry.SILK, 1, 3, null); + + public final static Item wizardHatFire = new ItemWizardArmour(Wizardry.SILK, 1, 0, EnumElement.FIRE); + public final static Item wizardRobeFire = new ItemWizardArmour(Wizardry.SILK, 1, 1, EnumElement.FIRE); + public final static Item wizardLeggingsFire = new ItemWizardArmour(Wizardry.SILK, 1, 2, EnumElement.FIRE); + public final static Item wizardBootsFire = new ItemWizardArmour(Wizardry.SILK, 1, 3, EnumElement.FIRE); + + public final static Item wizardHatIce = new ItemWizardArmour(Wizardry.SILK, 1, 0, EnumElement.ICE); + public final static Item wizardRobeIce = new ItemWizardArmour(Wizardry.SILK, 1, 1, EnumElement.ICE); + public final static Item wizardLeggingsIce = new ItemWizardArmour(Wizardry.SILK, 1, 2, EnumElement.ICE); + public final static Item wizardBootsIce = new ItemWizardArmour(Wizardry.SILK, 1, 3, EnumElement.ICE); + + public final static Item wizardHatLightning = new ItemWizardArmour(Wizardry.SILK, 1, 0, EnumElement.LIGHTNING); + public final static Item wizardRobeLightning = new ItemWizardArmour(Wizardry.SILK, 1, 1, EnumElement.LIGHTNING); + public final static Item wizardLeggingsLightning = new ItemWizardArmour(Wizardry.SILK, 1, 2, EnumElement.LIGHTNING); + public final static Item wizardBootsLightning = new ItemWizardArmour(Wizardry.SILK, 1, 3, EnumElement.LIGHTNING); + + public final static Item wizardHatNecromancy = new ItemWizardArmour(Wizardry.SILK, 1, 0, EnumElement.NECROMANCY); + public final static Item wizardRobeNecromancy = new ItemWizardArmour(Wizardry.SILK, 1, 1, EnumElement.NECROMANCY); + public final static Item wizardLeggingsNecromancy = new ItemWizardArmour(Wizardry.SILK, 1, 2, EnumElement.NECROMANCY); + public final static Item wizardBootsNecromancy = new ItemWizardArmour(Wizardry.SILK, 1, 3, EnumElement.NECROMANCY); + + public final static Item wizardHatEarth = new ItemWizardArmour(Wizardry.SILK, 1, 0, EnumElement.EARTH); + public final static Item wizardRobeEarth = new ItemWizardArmour(Wizardry.SILK, 1, 1, EnumElement.EARTH); + public final static Item wizardLeggingsEarth = new ItemWizardArmour(Wizardry.SILK, 1, 2, EnumElement.EARTH); + public final static Item wizardBootsEarth = new ItemWizardArmour(Wizardry.SILK, 1, 3, EnumElement.EARTH); + + public final static Item wizardHatSorcery = new ItemWizardArmour(Wizardry.SILK, 1, 0, EnumElement.SORCERY); + public final static Item wizardRobeSorcery = new ItemWizardArmour(Wizardry.SILK, 1, 1, EnumElement.SORCERY); + public final static Item wizardLeggingsSorcery = new ItemWizardArmour(Wizardry.SILK, 1, 2, EnumElement.SORCERY); + public final static Item wizardBootsSorcery = new ItemWizardArmour(Wizardry.SILK, 1, 3, EnumElement.SORCERY); + + public final static Item wizardHatHealing = new ItemWizardArmour(Wizardry.SILK, 1, 0, EnumElement.HEALING); + public final static Item wizardRobeHealing = new ItemWizardArmour(Wizardry.SILK, 1, 1, EnumElement.HEALING); + public final static Item wizardLeggingsHealing = new ItemWizardArmour(Wizardry.SILK, 1, 2, EnumElement.HEALING); + public final static Item wizardBootsHealing = new ItemWizardArmour(Wizardry.SILK, 1, 3, EnumElement.HEALING); + + public final static Item spawnWizard = new ItemSpawnWizard().setUnlocalizedName("spawn_wizard"); + + public final static Item spectralHelmet = new ItemSpectralArmour(ItemArmor.ArmorMaterial.IRON, 1, 0).setTextureName("wizardry:spectral_helmet").setUnlocalizedName("spectral_helmet"); + public final static Item spectralChestplate = new ItemSpectralArmour(ItemArmor.ArmorMaterial.IRON, 1, 1).setTextureName("wizardry:spectral_chestplate").setUnlocalizedName("spectral_chestplate"); + public final static Item spectralLeggings = new ItemSpectralArmour(ItemArmor.ArmorMaterial.IRON, 1, 2).setTextureName("wizardry:spectral_leggings").setUnlocalizedName("spectral_leggings"); + public final static Item spectralBoots = new ItemSpectralArmour(ItemArmor.ArmorMaterial.IRON, 1, 3).setTextureName("wizardry:spectral_boots").setUnlocalizedName("spectral_boots"); + + public final static Item smokeBomb = new ItemSmokeBomb().setTextureName("wizardry:smoke_bomb").setUnlocalizedName("smoke_bomb"); + + public final static Item identificationScroll = new ItemIdentificationScroll(); + + // Potion Effects + public static Potion frost; + public static Potion transience; + public static Potion fireskin; + public static Potion iceShroud; + public static Potion staticAura; + public static Potion decay; + public static Potion sixthSense; + public static Potion arcaneJammer; + public static Potion mindTrick; + public static Potion mindControl; + public static Potion fontOfMana; + public static Potion fear; + + // Enchantments + public static Enchantment magicSword; + public static Enchantment magicBow; + public static Enchantment flamingWeapon; + public static Enchantment freezingWeapon; + + // Achievements + public static final Achievement crystal = new Achievement("crystal", "Crystal", -1, -1, magicCrystal, null).registerStat(); + public static final Achievement arcaneInitiate = new Achievement("arcaneInitiate", "ArcaneInitiate", 2, -1, magicWand, crystal).registerStat(); + public static final Achievement apprentice = new Achievement("apprentice", "Apprentice", 2, 3, apprenticeWand, arcaneInitiate).registerStat(); + public static final Achievement master = new Achievement("master", "Master", 3, 6, masterWand, apprentice).registerStat(); + public static final Achievement allSpells = new Achievement("allSpells", "AllSpells", 5, 7, wizardHandbook, master).registerStat().setSpecial(); + public static final Achievement wizardTrade = new Achievement("wizardTrade", "WizardTrade", 2, -5, Items.emerald, arcaneInitiate).registerStat(); + public static final Achievement buyMasterSpell = new Achievement("buyMasterSpell", "BuyMasterSpell", 5, -5, spellBook, wizardTrade).registerStat().setSpecial(); + public static final Achievement freezeBlaze = new Achievement("freezeBlaze", "FreezeBlaze", -1, 3, Blocks.ice, apprentice).registerStat(); + public static final Achievement chargeCreeper = new Achievement("chargeCreeper", "ChargeCreeper", -1, 1, Items.gunpowder, arcaneInitiate).registerStat(); + public static final Achievement frankenstein = new Achievement("frankenstein", "Frankenstein", -3, 1, advancedLightningWand, chargeCreeper).registerStat().setSpecial(); + public static final Achievement specialUpgrade = new Achievement("specialUpgrade", "SpecialUpgrade", 4, 1, condenserUpgrade, arcaneInitiate).registerStat(); + public static final Achievement craftFlask = new Achievement("craftFlask", "CraftFlask", 4, -2, manaFlask, arcaneInitiate).registerStat(); + public static final Achievement elemental = new Achievement("elemental", "Elemental", 6, -1, basicFireWand, arcaneInitiate).registerStat(); + public static final Achievement armourSet = new Achievement("armourSet", "ArmourSet", 0, -4, wizardHat, arcaneInitiate).registerStat(); + public static final Achievement legendary = new Achievement("legendary", "Legendary", -2, -5, armourUpgrade, armourSet).registerStat().setSpecial(); + public static final Achievement selfDestruct = new Achievement("selfDestruct", "SelfDestruct", 1, 0, Blocks.pumpkin, arcaneInitiate).registerStat(); + public static final Achievement pigTornado = new Achievement("pigTornado", "PigTornado", 6, 3, Items.saddle, apprentice).registerStat().setSpecial(); + public static final Achievement jamWizard = new Achievement("jamWizard", "JamWizard", 4, 4, Blocks.web, apprentice).registerStat(); + public static final Achievement slimeSkeleton = new Achievement("slimeSkeleton", "SlimeSkeleton", 1, 5, Items.slime_ball, apprentice).registerStat(); + public static final Achievement angerWizard = new Achievement("angerWizard", "AngerWizard", 1, -7, Items.iron_sword, wizardTrade).registerStat(); + public static final Achievement defeatEvilWizard = new Achievement("defeatEvilWizard", "DefeatEvilWizard", 4, -7, wizardBootsNecromancy, wizardTrade).registerStat(); + public static final Achievement maxOutWand = new Achievement("maxOutWand", "MaxOutWand", 7, 1, arcaneTome, specialUpgrade).registerStat().setSpecial(); + public static final Achievement elementMaster = new Achievement("elementMaster", "ElementMaster", 7, -3, masterIceWand, elemental).registerStat().setSpecial(); + public static final Achievement identifySpell = new Achievement("identifySpell", "IdentifySpell", -2, -3, identificationScroll, arcaneInitiate).registerStat(); + + private static final Achievement[] achievementsList = {crystal, arcaneInitiate, apprentice, master, allSpells, wizardTrade, buyMasterSpell, freezeBlaze, chargeCreeper, + frankenstein, specialUpgrade, craftFlask, elemental, armourSet, legendary, selfDestruct, pigTornado, jamWizard, slimeSkeleton, angerWizard, defeatEvilWizard, + maxOutWand, elementMaster, identifySpell}; + public static final AchievementPage wizardryAchievementTab = new AchievementPage("Wizardry", achievementsList); + + // EventManager + WizardryWorldGenerator oreManager = new WizardryWorldGenerator(); + + // The instance of your mod that Forge uses. + @Instance(value = Wizardry.MODID) public static Wizardry instance; - // Location of the proxy code, used by Forge. + // Says where the client and server 'proxy' code is loaded. @SidedProxy(clientSide="electroblob.wizardry.client.ClientProxy", serverSide="electroblob.wizardry.CommonProxy") public static CommonProxy proxy; @EventHandler public void preInit(FMLPreInitializationEvent event){ - logger = event.getModLog(); - - // The array in question no longer exists, so I'm pretty sure this isn't necessary any more. - // expandPotionTypesArray(); + expandPotionTypesArray(); - settings.initConfig(event); - - // Yes - by the looks of it, having an interface is completely unnecessary in this case. - CapabilityManager.INSTANCE.register(WizardData.class, new IStorage(){ - // These methods are only called by Capability.writeNBT() or Capability.readNBT(), which in turn are - // NEVER CALLED. Unless I'm missing some reflective invocation, that means this entire class serves only - // to allow capabilities to be saved and loaded manually. What that would be useful for I don't know. - // (If an API forces most users to write redundant code for no reason, it's not user friendly, is it?) - // ... well, that's my rant for today! - @Override public NBTBase writeNBT(Capability capability, WizardData instance, EnumFacing side){ return null; } - @Override public void readNBT(Capability capability, WizardData instance, EnumFacing side, NBTBase nbt){} - }, WizardData.class); + // Config file + + /* The first part of the config file has to be done here (as is conventional) so that the various registries + * can change what they do accordingly. The spell part of the config has to be done in the init method. */ + + config = new Configuration(event.getSuggestedConfigurationFile()); + + config.load(); + + setupGeneralConfig(); + + config.save(); + + WizardryRegistry.registerBlocks(); + + WizardryRegistry.registerItems(); WizardryRegistry.registerTileEntities(); WizardryRegistry.registerEntities(this); + + WizardryRegistry.registerSpells(); // The check for the generateLoot setting is now done within this method. WizardryRegistry.registerLoot(); + + // Potion effects + frost = new PotionFrost(frostPotionID, true, 0x38ddec); + transience = new PotionMagicEffect(transiencePotionID, false, 0xffe89b, 0).setPotionName("potion.transience"); + fireskin = new PotionMagicEffect(fireskinPotionID, false, 0xff2f02, 1).setPotionName("potion.fireskin"); + iceShroud = new PotionMagicEffect(iceShroudPotionID, false, 0x52f1ff, 2).setPotionName("potion.ice_shroud"); + staticAura = new PotionMagicEffect(staticAuraPotionID, false, 0x0070ff, 3).setPotionName("potion.static_aura"); + decay = new PotionDecay(decayPotionID, true, 0x3c006c); + sixthSense = new PotionMagicEffect(sixthSensePotionID, false, 0xc6ff01, 4).setPotionName("potion.sixth_sense"); + arcaneJammer = new PotionMagicEffect(arcaneJammerPotionID, false, 0xcf4aa2, 5).setPotionName("potion.arcane_jammer"); + mindTrick = new PotionMagicEffect(mindTrickPotionID, true, 0x601683, 6).setPotionName("potion.mind_trick"); + mindControl = new PotionMagicEffect(mindControlPotionID, true, 0x320b44, 7).setPotionName("potion.mind_control"); + fontOfMana = new PotionMagicEffect(fontOfManaPotionID, false, 0xffe5bb, 8).setPotionName("potion.font_of_mana"); + fear = new PotionMagicEffect(fearPotionID, true, 0xbd0100, 9).setPotionName("potion.fear"); + + // Enchantments + magicSword = new EnchantmentMagicSword(magicSwordEnchantmentID); + magicBow = new EnchantmentTimed(magicBowEnchantmentID).setName("magic_bow"); + flamingWeapon = new EnchantmentTimed(flamingWeaponEnchantmentID).setName("flaming_weapon"); + freezingWeapon = new EnchantmentTimed(freezingWeaponEnchantmentID).setName("freezing_weapon"); - // NOTE: Will need to be moved to init for 1.12, as will anything that needs to be after the registry events. - WizardryTabs.sort(); + // Creative tab sorter + + List orderedItemList = Arrays.asList( + + Item.getItemFromBlock(arcaneWorkbench), + Item.getItemFromBlock(crystalOre), + Item.getItemFromBlock(crystalBlock), + Item.getItemFromBlock(crystalFlower), + Item.getItemFromBlock(transportationStone), + magicCrystal, magicWand, apprenticeWand, advancedWand, masterWand, arcaneTome, wizardHandbook, spawnWizard, + basicFireWand, basicIceWand, basicLightningWand, basicNecromancyWand, basicEarthWand, basicSorceryWand, basicHealingWand, + smokeBomb, firebomb, poisonBomb, blankScroll, identificationScroll, manaFlask, storageUpgrade, siphonUpgrade, + condenserUpgrade, rangeUpgrade, durationUpgrade, cooldownUpgrade, blastUpgrade, attunementUpgrade, magicSilk, armourUpgrade, + wizardHat, wizardRobe, wizardLeggings, wizardBoots, + wizardHatFire, wizardRobeFire, wizardLeggingsFire, wizardBootsFire, + wizardHatIce, wizardRobeIce, wizardLeggingsIce, wizardBootsIce, + wizardHatLightning, wizardRobeLightning, wizardLeggingsLightning, wizardBootsLightning, + wizardHatNecromancy, wizardRobeNecromancy, wizardLeggingsNecromancy, wizardBootsNecromancy, + wizardHatEarth, wizardRobeEarth, wizardLeggingsEarth, wizardBootsEarth, + wizardHatSorcery, wizardRobeSorcery, wizardLeggingsSorcery, wizardBootsSorcery, + wizardHatHealing, wizardRobeHealing, wizardLeggingsHealing, wizardBootsHealing + + ); + + itemSorter = Ordering.explicit(orderedItemList).onResultOf(new Function() { + @Override + public Item apply(ItemStack input) { + return input.getItem(); + } + }); - // Moved to preInit, because apparently it has to be here now. - proxy.registerRenderers(); - // It seems this also has to be here - proxy.registerKeyBindings(); + spellItemSorter = new Comparator(){ + @Override + public int compare(ItemStack stack1, ItemStack stack2) { + + if((stack1.getItem() instanceof ItemSpellBook && stack2.getItem() instanceof ItemSpellBook) + || (stack1.getItem() instanceof ItemScroll && stack2.getItem() instanceof ItemScroll)){ + + Spell spell1 = Spell.get(stack1.getItemDamage()); + Spell spell2 = Spell.get(stack2.getItemDamage()); + + return spell1.compareTo(spell2); + + }else if(stack1.getItem() instanceof ItemScroll){ + return 1; + }else if(stack2.getItem() instanceof ItemScroll){ + return -1; + } + return 0; + } + }; } @EventHandler public void init(FMLInitializationEvent event){ - settings.initConfigExtras(); + // Spell config (this has to be here and not in preInit because all spells must be registered before it is + // added, including those in other mods.) + + //config.load(); + + setupSpellsConfig(); + + config.save(); + + proxy.registerKeyBindings(); // Event Handlers - GameRegistry.registerWorldGenerator(generator, 0); - MinecraftForge.EVENT_BUS.register(new WizardryKeyHandler()); - MinecraftForge.EVENT_BUS.register(instance); - proxy.registerSpellHUD(); // This can't easily be converted to use the new @Mod.EventBusSubscriber system + GameRegistry.registerWorldGenerator(oreManager, 0); + FMLCommonHandler.instance().bus().register(new WizardryKeyHandler()); + FMLCommonHandler.instance().bus().register(instance); + proxy.registerEventHandlers(); + proxy.registerSpellHUD(); NetworkRegistry.INSTANCE.registerGuiHandler(this, new WizardryGuiHandler()); WizardryPacketHandler.initPackets(); // Achievements - AchievementPage.registerAchievementPage(WizardryAchievements.WIZARDRY_ACHIEVEMENT_PAGE); + AchievementPage.registerAchievementPage(wizardryAchievementTab); - // Recipes WizardryRegistry.registerRecipes(); - - proxy.initMixedFontRenderer(); + + proxy.registerRenderers(); } @EventHandler public void postInit(FMLPostInitializationEvent event){ + // To ensure backwards compatibility, the spell books and scrolls themselves are now sorted in the creative tab, + // and the spells are left in whatever order they were registered. + //Spell.sortSpells(); // This needs to be here or it won't necessarily include all the mods' entities. - // TODO: Re-implement this when the Forge bug is fixed. /* Doesn't seem to be doing anything... String entityNames = ""; for(Object name : EntityList.classToStringMapping.values()){ @@ -179,7 +671,6 @@ public class Wizardry { entityNames = entityNames.substring(0, entityNames.length()-1); entityNamePattern = Pattern.compile(entityNames); */ - proxy.initialiseLayers(); } @EventHandler @@ -191,71 +682,399 @@ public class Wizardry { } @SubscribeEvent - public void onConfigChanged(net.minecraftforge.fml.client.event.ConfigChangedEvent.OnConfigChangedEvent event){ - if(event.getModID().equals(Wizardry.MODID)){ - settings.saveConfigChanges(); - // All of the synchronised settings require a world restart anyway so this doesn't need syncing. - } + public void onConfigChanged(ConfigChangedEvent.OnConfigChangedEvent eventArgs){ + if(eventArgs.modID.equals(Wizardry.MODID)) Wizardry.syncConfig(); } - // 2.1 changed some item ids, so this fixes them for existing worlds - // NOTE: Needs changing to RegistryEvent.MissingMapping in 1.12, or just removing since nobody updates minecraft - // versions when using mods. - @EventHandler - public static void onMissingMappingEvent(FMLMissingMappingsEvent event){ - // Just get, not getAll, since the mod id didn't change! - for(FMLMissingMappingsEvent.MissingMapping mapping : event.get()){ - if(mapping.type == Type.ITEM && mapping.resourceLocation.getResourceDomain().equals(Wizardry.MODID)){ - - Item replacement = null; - - switch(mapping.resourceLocation.getResourcePath()){ - - case "wand_basic": replacement = WizardryItems.magic_wand; break; - case "wand_basic_fire": replacement = WizardryItems.basic_fire_wand; break; - case "wand_basic_ice": replacement = WizardryItems.basic_ice_wand; break; - case "wand_basic_lightning": replacement = WizardryItems.basic_lightning_wand; break; - case "wand_basic_necromancy": replacement = WizardryItems.basic_necromancy_wand; break; - case "wand_basic_earth": replacement = WizardryItems.basic_earth_wand; break; - case "wand_basic_sorcery": replacement = WizardryItems.basic_sorcery_wand; break; - case "wand_basic_healing": replacement = WizardryItems.basic_healing_wand; break; - case "wand_apprentice": replacement = WizardryItems.apprentice_wand; break; - case "wand_apprentice_fire": replacement = WizardryItems.apprentice_fire_wand; break; - case "wand_apprentice_ice": replacement = WizardryItems.apprentice_ice_wand; break; - case "wand_apprentice_lightning": replacement = WizardryItems.apprentice_lightning_wand; break; - case "wand_apprentice_necromancy": replacement = WizardryItems.apprentice_necromancy_wand; break; - case "wand_apprentice_earth": replacement = WizardryItems.apprentice_earth_wand; break; - case "wand_apprentice_sorcery": replacement = WizardryItems.apprentice_sorcery_wand; break; - case "wand_apprentice_healing": replacement = WizardryItems.apprentice_healing_wand; break; - case "wand_advanced": replacement = WizardryItems.advanced_wand; break; - case "wand_advanced_fire": replacement = WizardryItems.advanced_fire_wand; break; - case "wand_advanced_ice": replacement = WizardryItems.advanced_ice_wand; break; - case "wand_advanced_lightning": replacement = WizardryItems.advanced_lightning_wand; break; - case "wand_advanced_necromancy": replacement = WizardryItems.advanced_necromancy_wand; break; - case "wand_advanced_earth": replacement = WizardryItems.advanced_earth_wand; break; - case "wand_advanced_sorcery": replacement = WizardryItems.advanced_sorcery_wand; break; - case "wand_advanced_healing": replacement = WizardryItems.advanced_healing_wand; break; - case "wand_master": replacement = WizardryItems.master_wand; break; - case "wand_master_fire": replacement = WizardryItems.master_fire_wand; break; - case "wand_master_ice": replacement = WizardryItems.master_ice_wand; break; - case "wand_master_lightning": replacement = WizardryItems.master_lightning_wand; break; - case "wand_master_necromancy": replacement = WizardryItems.master_necromancy_wand; break; - case "wand_master_earth": replacement = WizardryItems.master_earth_wand; break; - case "wand_master_sorcery": replacement = WizardryItems.master_sorcery_wand; break; - case "wand_master_healing": replacement = WizardryItems.master_healing_wand; break; - case "upgrade_storage": replacement = WizardryItems.storage_upgrade; break; - case "upgrade_siphon": replacement = WizardryItems.siphon_upgrade; break; - case "upgrade_condenser": replacement = WizardryItems.condenser_upgrade; break; - case "upgrade_range": replacement = WizardryItems.range_upgrade; break; - case "upgrade_duration": replacement = WizardryItems.duration_upgrade; break; - case "upgrade_cooldown": replacement = WizardryItems.cooldown_upgrade; break; - case "upgrade_blast": replacement = WizardryItems.blast_upgrade; break; - case "upgrade_attunement": replacement = WizardryItems.attunement_upgrade; break; - // If it didn't match any of the ones that changed, do nothing. - default: return; + /** Call to sync the config file after it has been edited in game from the menus. */ + public static void syncConfig(){ + + setupGeneralConfig(); + + setupSpellsConfig(); + + config.save(); + + } + + // As of wizardry 1.1, all keys used in the config file itself are now hardcoded, not localised. The localisations + // are done in the config GUI. The config file has to be written in one language or it will end up with multiple + // options for different languages. + + private static void setupSpellsConfig(){ + + config.addCustomCategoryComment(Wizardry.SPELLS_CATEGORY, "Set a spell to false to disable it. Disabled spells will still have their associated spell book (mainly so the spell books don't all change) and can still be bound to wands, but cannot be cast in game, will not appear in any subsequently generated chests or wizard trades and will not drop from mobs. Disable a spell if it is causing problems, conflicts with another mod or creates an unintended exploit."); + + Property property; + + for(Spell spell : Spell.getSpells(Spell.allSpells)){ + property = config.get(Wizardry.SPELLS_CATEGORY, spell.getUnlocalisedName(), true, spell.getDescription()); + property.setLanguageKey("spell." + spell.getUnlocalisedName()); + spell.setEnabled(property.getBoolean()); + } + + } + + private static void setupGeneralConfig(){ + + // This trick is borrowed from forge; it sorts the config options into the order you want them. + List propOrder = new ArrayList(); + + Property property; + + config.addCustomCategoryComment(config.CATEGORY_GENERAL, "Please note that changing some of these settings may make the mod very difficult to play."); + + property = config.get(config.CATEGORY_GENERAL, "towerRarity", 8, "Rarity of wizard towers. Higher numbers are rarer. Set to 0 to disable wizard towers completely.", 0, 50); + property.setLanguageKey("config.tower_rarity"); + property.setRequiresWorldRestart(true); + Wizardry.proxy.setToNumberSliderEntry(property); + towerRarity = property.getInt(); + propOrder.add(property.getName()); + + property = config.get(config.CATEGORY_GENERAL, "spellBookDropChance", 3, "The chance for mobs to drop a spell book when killed. The greater this number, the more often they will drop. Set to 0 to disable spell book drops. Set to 200 for guaranteed drops.", 0, 200); + property.setLanguageKey("config.spell_book_drop_chance"); + Wizardry.proxy.setToNumberSliderEntry(property); + spellBookDropChance = property.getInt(); + propOrder.add(property.getName()); + + property = config.get(config.CATEGORY_GENERAL, "oreDimensions", oreDimensions, "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.setLanguageKey("config.ore_dimensions"); + property.setRequiresWorldRestart(true); + oreDimensions = property.getIntList(); + propOrder.add(property.getName()); + + property = config.get(config.CATEGORY_GENERAL, "flowerDimensions", flowerDimensions, "List of dimension ids in which crystal flowers will generate."); + property.setLanguageKey("config.flower_dimensions"); + property.setRequiresWorldRestart(true); + flowerDimensions = property.getIntList(); + propOrder.add(property.getName()); + + property = config.get(config.CATEGORY_GENERAL, "towerDimensions", towerDimensions, "List of dimension ids in which wizard towers will generate."); + property.setLanguageKey("config.tower_dimensions"); + property.setRequiresWorldRestart(true); + towerDimensions = property.getIntList(); + propOrder.add(property.getName()); + + property = config.get(config.CATEGORY_GENERAL, "generateLoot", true, "Whether to generate wizardry loot in dungeon chests."); + property.setLanguageKey("config.generate_loot"); + property.setRequiresWorldRestart(true); + generateLoot = property.getBoolean(); + propOrder.add(property.getName()); + + property = config.get(config.CATEGORY_GENERAL, "firebombIsCraftable", true, "Whether firebombs can be crafted or not."); + property.setLanguageKey("config.firebomb_is_craftable"); + property.setRequiresMcRestart(true); + firebombIsCraftable = property.getBoolean(); + propOrder.add(property.getName()); + + property = config.get(config.CATEGORY_GENERAL, "poisonBombIsCraftable", true, "Whether poison bombs can be crafted or not."); + property.setLanguageKey("config.poison_bomb_is_craftable"); + property.setRequiresMcRestart(true); + poisonBombIsCraftable = property.getBoolean(); + propOrder.add(property.getName()); + + property = config.get(config.CATEGORY_GENERAL, "smokeBombIsCraftable", true, "Whether smoke bombs can be crafted or not."); + property.setLanguageKey("config.smoke_bomb_is_craftable"); + property.setRequiresMcRestart(true); + smokeBombIsCraftable = property.getBoolean(); + propOrder.add(property.getName()); + + property = config.get(config.CATEGORY_GENERAL, "useAlternateScrollRecipe", false, "Whether to require a magic crystal in the shapeless crafting recipe for blank scrolls. Set to true if another mod adds a conflicting recipe."); + property.setLanguageKey("config.use_alternate_scroll_recipe"); + property.setRequiresMcRestart(true); + useAlternateScrollRecipe = property.getBoolean(); + propOrder.add(property.getName()); + + property = config.get(config.CATEGORY_GENERAL, "teleportThroughUnbreakableBlocks", false, "Whether players are allowed to teleport through unbreakable blocks (e.g. bedrock) using the phase step spell."); + property.setLanguageKey("config.teleport_through_unbreakable_blocks"); + teleportThroughUnbreakableBlocks = property.getBoolean(); + propOrder.add(property.getName()); + + property = config.get(config.CATEGORY_GENERAL, "showSummonedCreatureNames", true, "Whether to show summoned creatures' names and owners above their heads."); + property.setLanguageKey("config.show_summoned_creature_names"); + showSummonedCreatureNames = property.getBoolean(); + propOrder.add(property.getName()); + + property = config.get(config.CATEGORY_GENERAL, "friendlyFire", true, "Whether to allow players to damage their designated allies using magic."); + property.setLanguageKey("config.friendly_fire"); + friendlyFire = property.getBoolean(); + propOrder.add(property.getName()); + + property = config.get(config.CATEGORY_GENERAL, "telekineticDisarmament", true, "Whether to allow players to disarm other players using the telekinesis spell. Set to false to prevent stealing of items."); + property.setLanguageKey("config.telekinetic_disarmament"); + telekineticDisarmament = property.getBoolean(); + propOrder.add(property.getName()); + + property = config.get(config.CATEGORY_GENERAL, "discoveryMode", true, "For those who like a sense of mystery! When set to true, spells you haven't cast yet will be unreadable until you cast them (on a per-world basis). Has no effect when in creative mode. Spells of identification will be unobtainable in survival mode if this is false."); + property.setLanguageKey("config.discovery_mode"); + property.setRequiresWorldRestart(true); + discoveryMode = property.getBoolean(); + propOrder.add(property.getName()); + + property = config.get(config.CATEGORY_GENERAL, "enableShiftScrolling", true, "Whether you can switch between spells on a wand by scrolling with the mouse wheel while sneaking. Note that this will only affect you; other players connected to the same server obey their own settings."); + property.setLanguageKey("config.enable_shift_scrolling"); + property.setRequiresWorldRestart(false); + enableShiftScrolling = property.getBoolean(); + propOrder.add(property.getName()); + + property = config.get(config.CATEGORY_GENERAL, "minionRevengeTargeting", true, "Whether summoned creatures can revenge attack their owner if their owner attacks them."); + property.setLanguageKey("config.minion_revenge_targeting"); + property.setRequiresWorldRestart(false); + minionRevengeTargeting = property.getBoolean(); + propOrder.add(property.getName()); + + // These two aren't sliders because using a slider makes it difficult to fine-tune the numbers; the nature of a + // scaling factor means that 0.5 is as big a change as 2.0, so whilst a slider is fine for increasing the damage, + // it doesn't give fine enough control for values less than 1. + property = config.get(config.CATEGORY_GENERAL, "playerDamageScaling", 1.0, "Global damage scaling factor for the damage dealt by players casting spells, relative to 1.", 0, 20); + property.setLanguageKey("config.player_damage_scaling"); + playerDamageScale = property.getDouble(); + propOrder.add(property.getName()); + + property = config.get(config.CATEGORY_GENERAL, "npcDamageScaling", 1.0, "Global damage scaling factor for the damage dealt by NPCs casting spells, relative to 1.", 0, 20); + property.setLanguageKey("config.npc_damage_scaling"); + npcDamageScale = property.getDouble(); + propOrder.add(property.getName()); + + // This one isn't a slider either because people are likely to want exact values ("it must be at most 50.3" is a bit strange!). + property = config.get(config.CATEGORY_GENERAL, "castCommandMultiplierLimit", 20.0, "Upper limit for the multipliers passed into the /cast command. This is here to stop players from accidentally breaking a world/server. Large blast mutipliers can cause extreme lag - you have been warned!", 1, 255); + property.setLanguageKey("config.cast_command_multiplier_limit"); + maxSpellCommandMultiplier = property.getDouble(); + propOrder.add(property.getName()); + + property = config.get(config.CATEGORY_GENERAL, "summonedCreatureTargetsWhitelist", summonedCreatureTargetsWhitelist, "List of names of entities which summoned creatures and wizards are allowed to attack, in addition to the defaults. Add mod creatures to this list if you want summoned creatures to attack them and they aren't already doing so. Entity names are not case sensitive. For mod entities, prefix with the mod ID (e.g. wizardry:Wizard)."); + property.setLanguageKey("config.summoned_creature_targets_whitelist"); + property.setRequiresWorldRestart(true); + summonedCreatureTargetsWhitelist = property.getStringList(); + // Converts all strings in the list to lower case, to ignore case sensitivity, and trims them. + for(int i=0; i propOrder1 = new ArrayList(); + + config.addCustomCategoryComment(Wizardry.RESISTANCES_CATEGORY, "These options allow entities to be made immune to certain types of magic."); + + property = config.get(Wizardry.RESISTANCES_CATEGORY, "mobsImmuneToFire", new String[]{}, "List of names of entities that are immune to fire, in addition to the defaults. Add mod creatures to this list if you want them to be immune to fire magic and they aren't already. Entity names are not case sensitive. For mod entities, prefix with the mod ID (e.g. wizardry:Wizard)."); + property.setLanguageKey("config.mobs_immune_to_fire"); + property.setRequiresMcRestart(true); + // Converts all strings in the list to lower case, to ignore case sensitivity, and trims them. + for(int i=0; i)EntityList.stringToClassMapping.get(property.getStringList()[i]), DamageType.FIRE); + } + propOrder1.add(property.getName()); + + property = config.get(Wizardry.RESISTANCES_CATEGORY, "mobsImmuneToIce", new String[]{}, "List of names of entities that are immune to ice, in addition to the defaults. Add mod creatures to this list if you want them to be immune to ice magic and they aren't already. Entity names are not case sensitive. For mod entities, prefix with the mod ID (e.g. wizardry:Wizard)."); + property.setLanguageKey("config.mobs_immune_to_ice"); + property.setRequiresMcRestart(true); + // Converts all strings in the list to lower case, to ignore case sensitivity, and trims them. + for(int i=0; i)EntityList.stringToClassMapping.get(property.getStringList()[i]), DamageType.FROST); + } + propOrder1.add(property.getName()); + + property = config.get(Wizardry.RESISTANCES_CATEGORY, "mobsImmuneToLightning", new String[]{}, "List of names of entities that are immune to lightning, in addition to the defaults. Add mod creatures to this list if you want them to be immune to lightning magic and they aren't already. Entity names are not case sensitive. For mod entities, prefix with the mod ID (e.g. wizardry:Wizard)."); + property.setLanguageKey("config.mobs_immune_to_lightning"); + property.setRequiresMcRestart(true); + // Converts all strings in the list to lower case, to ignore case sensitivity, and trims them. + for(int i=0; i)EntityList.stringToClassMapping.get(property.getStringList()[i]), DamageType.SHOCK); + } + propOrder1.add(property.getName()); + + property = config.get(Wizardry.RESISTANCES_CATEGORY, "mobsImmuneToWither", new String[]{}, "List of names of entities that are immune to wither effects, in addition to the defaults. Add mod creatures to this list if you want them to be immune to withering magic and they aren't already. Entity names are not case sensitive. For mod entities, prefix with the mod ID (e.g. wizardry:Wizard)."); + property.setLanguageKey("config.mobs_immune_to_wither"); + property.setRequiresMcRestart(true); + // Converts all strings in the list to lower case, to ignore case sensitivity, and trims them. + for(int i=0; i)EntityList.stringToClassMapping.get(property.getStringList()[i]), DamageType.WITHER); + } + propOrder1.add(property.getName()); + + property = config.get(Wizardry.RESISTANCES_CATEGORY, "mobsImmuneToPoison", new String[]{}, "List of names of entities that are immune to poison, in addition to the defaults. Add mod creatures to this list if you want them to be immune to poison magic and they aren't already. Entity names are not case sensitive. For mod entities, prefix with the mod ID (e.g. wizardry:Wizard)."); + property.setLanguageKey("config.mobs_immune_to_poison"); + property.setRequiresMcRestart(true); + // Converts all strings in the list to lower case, to ignore case sensitivity, and trims them. + for(int i=0; i)EntityList.stringToClassMapping.get(property.getStringList()[i]), DamageType.POISON); + } + propOrder1.add(property.getName()); + + config.setCategoryPropertyOrder(Wizardry.RESISTANCES_CATEGORY, propOrder1); + + // IDs + + List propOrder2 = new ArrayList(); + + config.addCustomCategoryComment(Wizardry.IDS_CATEGORY, "Change these IDs if they conflict with another mod."); + + property = config.get(Wizardry.IDS_CATEGORY, "frostPotionID", 31, "The ID of the frost potion effect. Change if this conflicts with another mod.", 24, 255); + property.setLanguageKey("config.frost_potion_id"); + property.setRequiresMcRestart(true); + frostPotionID = property.getInt(); + propOrder2.add(property.getName()); + + property = config.get(Wizardry.IDS_CATEGORY, "transiencePotionID", 32, "The ID of the transience potion effect. Change if this conflicts with another mod.", 24, 255); + property.setLanguageKey("config.transience_potion_id"); + property.setRequiresMcRestart(true); + transiencePotionID = property.getInt(); + propOrder2.add(property.getName()); + + property = config.get(Wizardry.IDS_CATEGORY, "fireskinPotionID", 33, "The ID of the fireskin potion effect. Change if this conflicts with another mod.", 24, 255); + property.setLanguageKey("config.fireskin_potion_id"); + property.setRequiresMcRestart(true); + fireskinPotionID = property.getInt(); + propOrder2.add(property.getName()); + + property = config.get(Wizardry.IDS_CATEGORY, "iceShroudPotionID", 34, "The ID of the ice shroud potion effect. Change if this conflicts with another mod.", 24, 255); + property.setLanguageKey("config.ice_shroud_potion_id"); + property.setRequiresMcRestart(true); + iceShroudPotionID = property.getInt(); + propOrder2.add(property.getName()); + + property = config.get(Wizardry.IDS_CATEGORY, "staticAuraPotionID", 35, "The ID of the static aura potion effect. Change if this conflicts with another mod.", 24, 255); + property.setLanguageKey("config.static_aura_potion_id"); + property.setRequiresMcRestart(true); + staticAuraPotionID = property.getInt(); + propOrder2.add(property.getName()); + + property = config.get(Wizardry.IDS_CATEGORY, "decayPotionID", 36, "The ID of the decay potion effect. Change if this conflicts with another mod.", 24, 255); + property.setLanguageKey("config.decay_potion_id"); + property.setRequiresMcRestart(true); + decayPotionID = property.getInt(); + propOrder2.add(property.getName()); + + property = config.get(Wizardry.IDS_CATEGORY, "sixthSensePotionID", 37, "The ID of the sixth sense potion effect. Change if this conflicts with another mod.", 24, 255); + property.setLanguageKey("config.sixth_sense_potion_id"); + property.setRequiresMcRestart(true); + sixthSensePotionID = property.getInt(); + propOrder2.add(property.getName()); + + property = config.get(Wizardry.IDS_CATEGORY, "arcaneJammerPotionID", 38, "The ID of the arcane jammer potion effect. Change if this conflicts with another mod.", 24, 255); + property.setLanguageKey("config.arcane_jammer_potion_id"); + property.setRequiresMcRestart(true); + arcaneJammerPotionID = property.getInt(); + propOrder2.add(property.getName()); + + property = config.get(Wizardry.IDS_CATEGORY, "mindTrickPotionID", 39, "The ID of the mind trick potion effect. Change if this conflicts with another mod.", 24, 255); + property.setLanguageKey("config.mind_trick_potion_id"); + property.setRequiresMcRestart(true); + mindTrickPotionID = property.getInt(); + propOrder2.add(property.getName()); + + property = config.get(Wizardry.IDS_CATEGORY, "mindControlPotionID", 40, "The ID of the mind control potion effect. Change if this conflicts with another mod.", 24, 255); + property.setLanguageKey("config.mind_control_potion_id"); + property.setRequiresMcRestart(true); + mindControlPotionID = property.getInt(); + propOrder2.add(property.getName()); + + property = config.get(Wizardry.IDS_CATEGORY, "fontOfManaPotionID", 41, "The ID of the font of mana potion effect. Change if this conflicts with another mod.", 24, 255); + property.setLanguageKey("config.font_of_mana_potion_id"); + property.setRequiresMcRestart(true); + fontOfManaPotionID = property.getInt(); + propOrder2.add(property.getName()); + + property = config.get(Wizardry.IDS_CATEGORY, "fearPotionID", 42, "The ID of the fear potion effect. Change if this conflicts with another mod.", 24, 255); + property.setLanguageKey("config.fear_potion_id"); + property.setRequiresMcRestart(true); + fearPotionID = property.getInt(); + propOrder2.add(property.getName()); + + property = config.get(Wizardry.IDS_CATEGORY, "magicSwordEnchantmentID", 100, "The ID of the magic sword enchantment. Change if this conflicts with another mod.", 63, 255); + property.setLanguageKey("config.magic_sword_enchantment_id"); + property.setRequiresMcRestart(true); + magicSwordEnchantmentID = property.getInt(); + propOrder2.add(property.getName()); + + property = config.get(Wizardry.IDS_CATEGORY, "magicBowEnchantmentID", 101, "The ID of the magic bow enchantment. Change if this conflicts with another mod.", 63, 255); + property.setLanguageKey("config.magic_bow_enchantment_id"); + property.setRequiresMcRestart(true); + magicBowEnchantmentID = property.getInt(); + propOrder2.add(property.getName()); + + property = config.get(Wizardry.IDS_CATEGORY, "flamingWeaponEnchantmentID", 102, "The ID of the flaming weapon enchantment. Change if this conflicts with another mod.", 63, 255); + property.setLanguageKey("config.flaming_weapon_enchantment_id"); + property.setRequiresMcRestart(true); + flamingWeaponEnchantmentID = property.getInt(); + propOrder2.add(property.getName()); + + property = config.get(Wizardry.IDS_CATEGORY, "freezingWeaponEnchantmentID", 103, "The ID of the freezing weapon enchantment. Change if this conflicts with another mod.", 63, 255); + property.setLanguageKey("config.freezing_weapon_enchantment_id"); + property.setRequiresMcRestart(true); + freezingWeaponEnchantmentID = property.getInt(); + propOrder2.add(property.getName()); + + config.setCategoryPropertyOrder(Wizardry.IDS_CATEGORY, propOrder2); + + } + + private static void expandPotionTypesArray(){ + + Potion[] potionTypes = null; + + for(Field f : Potion.class.getDeclaredFields()){ + f.setAccessible(true); + // Notice that this already takes the obfuscation into account. + try{ + if(f.getName().equals("potionTypes") || f.getName().equals("field_76425_a")) { + Field modfield = Field.class.getDeclaredField("modifiers"); + modfield.setAccessible(true); + modfield.setInt(f, f.getModifiers() & ~Modifier.FINAL); + + potionTypes = (Potion[])f.get(null); + final Potion[] newPotionTypes = new Potion[256]; + System.arraycopy(potionTypes, 0, newPotionTypes, 0, potionTypes.length); + f.set(null, newPotionTypes); } - // No need to log, Forge does that. - mapping.remap(replacement); + } + catch (Exception e) { + System.err.println("Something went very wrong! Error while expanding potion types array:"); + e.printStackTrace(); } } } diff --git a/src/main/java/electroblob/wizardry/WizardryEventHandler.java b/src/main/java/electroblob/wizardry/WizardryEventHandler.java index a91acd2b..f5e530cc 100644 --- a/src/main/java/electroblob/wizardry/WizardryEventHandler.java +++ b/src/main/java/electroblob/wizardry/WizardryEventHandler.java @@ -1,171 +1,86 @@ package electroblob.wizardry; -import electroblob.wizardry.constants.Constants; +import java.util.List; +import java.util.Map; +import java.util.UUID; + +import cpw.mods.fml.common.eventhandler.SubscribeEvent; +import electroblob.wizardry.MagicDamage.DamageType; import electroblob.wizardry.entity.EntityArc; +import electroblob.wizardry.entity.construct.EntityBubble; +import electroblob.wizardry.entity.construct.EntityDecay; import electroblob.wizardry.entity.living.EntityEvilWizard; -import electroblob.wizardry.entity.living.ISpellCaster; -import electroblob.wizardry.entity.living.ISummonedCreature; -import electroblob.wizardry.event.DiscoverSpellEvent; -import electroblob.wizardry.event.SpellCastEvent; +import electroblob.wizardry.entity.living.EntitySilverfishMinion; +import electroblob.wizardry.entity.living.EntityWizard; +import electroblob.wizardry.item.ItemFlamingAxe; +import electroblob.wizardry.item.ItemFrostAxe; +import electroblob.wizardry.item.ItemSpectralArmour; +import electroblob.wizardry.item.ItemSpectralBow; +import electroblob.wizardry.item.ItemSpectralPickaxe; +import electroblob.wizardry.item.ItemSpectralSword; import electroblob.wizardry.item.ItemWand; import electroblob.wizardry.item.ItemWizardArmour; -import electroblob.wizardry.registry.Spells; -import electroblob.wizardry.registry.WizardryAchievements; -import electroblob.wizardry.registry.WizardryEnchantments; -import electroblob.wizardry.registry.WizardryItems; -import electroblob.wizardry.registry.WizardryPotions; -import electroblob.wizardry.registry.WizardrySounds; +import electroblob.wizardry.spell.Clairvoyance; import electroblob.wizardry.spell.FreezingWeapon; +import electroblob.wizardry.spell.MindControl; +import electroblob.wizardry.spell.ShadowWard; import electroblob.wizardry.spell.Spell; -import electroblob.wizardry.util.IElementalDamage; -import electroblob.wizardry.util.MagicDamage; -import electroblob.wizardry.util.MagicDamage.DamageType; -import electroblob.wizardry.util.SpellModifiers; -import electroblob.wizardry.util.WandHelper; -import electroblob.wizardry.util.WizardryParticleType; -import electroblob.wizardry.util.WizardryUtilities; +import electroblob.wizardry.spell.Intimidate; import net.minecraft.enchantment.EnchantmentHelper; +import net.minecraft.entity.Entity; +import net.minecraft.entity.EntityCreature; import net.minecraft.entity.EntityLiving; import net.minecraft.entity.EntityLivingBase; import net.minecraft.entity.item.EntityItem; -import net.minecraft.entity.monster.IMob; +import net.minecraft.entity.monster.EntityCreeper; +import net.minecraft.entity.monster.EntityMob; +import net.minecraft.entity.passive.EntityPig; import net.minecraft.entity.player.EntityPlayer; import net.minecraft.entity.player.EntityPlayerMP; import net.minecraft.entity.projectile.EntityArrow; -import net.minecraft.init.SoundEvents; +import net.minecraft.init.Blocks; import net.minecraft.inventory.ContainerPlayer; import net.minecraft.inventory.ContainerWorkbench; import net.minecraft.inventory.IInventory; +import net.minecraft.item.ItemBow; import net.minecraft.item.ItemStack; import net.minecraft.item.ItemSword; +import net.minecraft.nbt.NBTTagCompound; import net.minecraft.potion.PotionEffect; -import net.minecraft.util.EnumHand; -import net.minecraft.util.EnumParticleTypes; -import net.minecraft.util.ResourceLocation; -import net.minecraft.util.text.TextComponentTranslation; +import net.minecraft.util.ChatComponentTranslation; +import net.minecraft.util.DamageSource; import net.minecraft.world.World; -import net.minecraft.world.storage.loot.LootEntry; -import net.minecraft.world.storage.loot.LootEntryTable; -import net.minecraft.world.storage.loot.LootPool; -import net.minecraft.world.storage.loot.RandomValueRange; -import net.minecraft.world.storage.loot.conditions.LootCondition; -import net.minecraftforge.common.MinecraftForge; -import net.minecraftforge.event.LootTableLoadEvent; +import net.minecraftforge.common.util.FakePlayer; +import net.minecraftforge.event.entity.EntityEvent.EntityConstructing; +import net.minecraftforge.event.entity.EntityJoinWorldEvent; +import net.minecraftforge.event.entity.EntityStruckByLightningEvent; +import net.minecraftforge.event.entity.item.ItemTossEvent; import net.minecraftforge.event.entity.living.LivingAttackEvent; import net.minecraftforge.event.entity.living.LivingDeathEvent; import net.minecraftforge.event.entity.living.LivingDropsEvent; import net.minecraftforge.event.entity.living.LivingEvent.LivingUpdateEvent; import net.minecraftforge.event.entity.living.LivingHurtEvent; +import net.minecraftforge.event.entity.living.LivingSetAttackTargetEvent; +import net.minecraftforge.event.entity.player.BonemealEvent; import net.minecraftforge.event.entity.player.EntityItemPickupEvent; -import net.minecraftforge.fml.common.Mod; -import net.minecraftforge.fml.common.eventhandler.SubscribeEvent; -import net.minecraftforge.fml.common.gameevent.PlayerEvent.PlayerLoggedInEvent; - -// Better ways of organising event handling: -// - Split it into several separate handlers, each with a logical area to deal with (see Astral Sorcery) -// - Remove most of the stuff, and have individual spell, item, block, entity, etc. classes handle their own events -// (see Botania) -// - Keep all the methods, but delegate near-repeated behaviours to their individual spell/potion/whatever classes -// (TGG does this, apparently) -// Incidentally, Twilight Forest still has one big event handler class - and a comment about how it's so long...! - -// In the end, I decided the most pragmatic solution was to either use the second option or keep things how they were, -// whichever will result in more readable/easily maintainable code in each case. To keep in line with the modularity -// of the spell system, the goal is to have nothing in here which relates to a specific spell (mostly done, but a few -// things remain). - -/** - * As of Wizardry 2.1, most of the code in this class has been relocated somewhere sensible, leaving only a few - * miscellaneous things that don't make much sense anywhere else, or that are better kept together. Previously, this was - * a gigantic class with about half of the entire mod's logic in it! - * @author Electroblob - * @since Wizardry 1.0 - */ -@Mod.EventBusSubscriber -public final class WizardryEventHandler { - - // IDEA: Config option allowing users to specify loot locations - private static final String[] LOOT_INJECTION_LOCATIONS = { - "minecraft:chests/simple_dungeon", - "minecraft:chests/abandoned_mineshaft", - "minecraft:chests/desert_pyramid", - "minecraft:chests/jungle_temple", - "minecraft:chests/stronghold_corridor", - "minecraft:chests/stronghold_crossing", - "minecraft:chests/stronghold_library", - "minecraft:chests/igloo_chest" - }; +import net.minecraftforge.event.entity.player.PlayerEvent.BreakSpeed; +import net.minecraftforge.event.entity.player.PlayerInteractEvent; +import net.minecraftforge.event.world.BlockEvent; +import net.minecraftforge.event.world.WorldEvent; +public class WizardryEventHandler { + @SubscribeEvent - public static void onLootTableLoadEvent(LootTableLoadEvent event){ - if(Wizardry.settings.generateLoot){ - for(String location : LOOT_INJECTION_LOCATIONS){ - if(event.getName().toString().matches(location)){ - event.getTable().addPool(getAdditive("wizardry:chests/dungeon_additions")); - } - } - } - } - - private static LootPool getAdditive(String entryName){ - return new LootPool(new LootEntry[] { getAdditiveEntry(entryName, 1) }, new LootCondition[0], new RandomValueRange(1), new RandomValueRange(0, 1), Wizardry.MODID + "_additive_pool"); - } - - private static LootEntryTable getAdditiveEntry(String name, int weight){ - return new LootEntryTable(new ResourceLocation(name), weight, 0, new LootCondition[0], Wizardry.MODID + "_additive_entry"); - } - - @SubscribeEvent - public static void onPlayerLoggedInEvent(PlayerLoggedInEvent event){ - // When a player logs in, they are sent the glyph data and the server's settings. - if(event.player instanceof EntityPlayerMP){ - SpellGlyphData.get(event.player.worldObj).sync((EntityPlayerMP)event.player); - Wizardry.settings.sync((EntityPlayerMP)event.player); + public void onEntityConstructingEvent(EntityConstructing event){ + if(event.entity instanceof EntityPlayer && ExtendedPlayer.get((EntityPlayer)event.entity) == null){ + ExtendedPlayer.register((EntityPlayer) event.entity); } } @SubscribeEvent - public static void onSpellCastPreEvent(SpellCastEvent.Pre event){ - // If a spell is disabled in the config, it will not work. - if(!event.getSpell().isEnabled()){ - if(!event.getEntityLiving().worldObj.isRemote) - event.getEntity().addChatMessage(new TextComponentTranslation("spell.disabled", event.getSpell().getNameForTranslationFormatted())); - event.setCanceled(true); - } - } - - @SubscribeEvent - public static void onSpellCastPostEvent(SpellCastEvent.Post event){ - - // Spell discovery (only players can discover spells, obviously) - if(event.getEntity() instanceof EntityPlayer){ - - EntityPlayer player = (EntityPlayer)event.getEntity(); - - WizardData data = WizardData.get(player); - - if(data != null){ - // Data is updated on both sides (This line was added client-side to fix a bug back in 1.1.3, so now - // it's in common code, which is nice!) - // Short-circuiting AND means that discoverSpell is only called if the event isn't cancelled. - if(!MinecraftForge.EVENT_BUS.post(new DiscoverSpellEvent(player, event.getSpell(), - DiscoverSpellEvent.Source.CASTING)) && data.discoverSpell(event.getSpell())){ - - // If the spell wasn't already discovered, other stuff happens: - if(event.getSource() == SpellCastEvent.Source.COMMAND){ - // If the spell didn't send a packet itself, the extended player needs to be synced so the - // spell discovery updates on the client. - if(!event.getSpell().doesSpellRequirePacket()) data.sync(); - - }else if(!event.getEntity().worldObj.isRemote && !player.capabilities.isCreativeMode - && Wizardry.settings.discoveryMode){ - // Sound and text only happen server-side, in survival, with discovery mode on, and only when - // the spell wasn't cast using commands. - WizardryUtilities.playSoundAtPlayer(player, SoundEvents.ENTITY_PLAYER_LEVELUP, 1.25f, 1); - player.addChatMessage(new TextComponentTranslation("spell.discover", event.getSpell().getNameForTranslationFormatted())); - } - } - } + public void onWorldLoadEvent(WorldEvent.Load event){ + if(!event.world.isRemote && event.world.provider.dimensionId == 0){ + SpellGlyphData.get(event.world); } } @@ -192,229 +107,496 @@ public final class WizardryEventHandler { * conventions for what sort of things take what priority...? */ @SubscribeEvent - public static void onLivingAttackEvent(LivingAttackEvent event){ + public void onLivingAttackEvent(LivingAttackEvent event){ // Prevents any damage to allies from magic if friendly fire is enabled - if(!Wizardry.settings.friendlyFire && event.getSource() != null && event.getSource().getEntity() instanceof EntityPlayer - && event.getEntity() instanceof EntityPlayer && event.getSource() instanceof IElementalDamage){ - if(WizardryUtilities.isPlayerAlly((EntityPlayer)event.getSource().getEntity(), (EntityPlayer)event.getEntity())){ + if(!Wizardry.friendlyFire && event.source != null && event.source.getEntity() instanceof EntityPlayer + && event.entity instanceof EntityPlayer && event.source instanceof IElementalDamage){ + if(WizardryUtilities.isPlayerAlly((EntityPlayer)event.source.getEntity(), (EntityPlayer)event.entity)){ event.setCanceled(true); - // This needs to be here, since if the event is cancelled nothing else needs to happen. + // I think this ought to be here, since if the event is cancelled nothing else needs to happen. return; } } + + if(event.source instanceof IElementalDamage){ + if(MagicDamage.isEntityImmune(((IElementalDamage)event.source).getType(), event.entity)){ + event.setCanceled(true); + // I would have liked to have done the 'resist' chat message here, but I overlooked the fact that I + // would need an instance of the spell to get its display name! + return; + } + // One convenient side effect of the new damage type system is that I can get rid of all the places where + // creepers are charged and just put them here under shock damage - this is precisely the sort of + // repetitive code I was trying to get rid of, since errors can (and did!) occur. + if(event.entityLiving instanceof EntityCreeper && !((EntityCreeper)event.entityLiving).getPowered() + && ((IElementalDamage)event.source).getType() == DamageType.SHOCK){ + // Charges creepers when they are hit by shock damage + event.entityLiving.getDataWatcher().updateObject(17, Byte.valueOf((byte)1)); + // Gives the player that caused the shock damage the 'It's Gonna Blow' achievement + if(event.source.getEntity() instanceof EntityPlayer){ + ((EntityPlayer)event.source.getEntity()).triggerAchievement(Wizardry.chargeCreeper); + } + } + } - // Retaliatory effects - // These are better off here because the revenge effects are pretty similar, and I'd rather keep the (lengthy) - // if statement in one place. - if(event.getSource() != null && event.getSource().getEntity() instanceof EntityLivingBase - && !event.getSource().isProjectile() && !(event.getSource() instanceof IElementalDamage && - ((IElementalDamage)event.getSource()).isRetaliatory())){ + // Bursts bubble when the creature inside takes damage + if(event.entityLiving.ridingEntity instanceof EntityBubble && + !((EntityBubble)event.entityLiving.ridingEntity).isDarkOrb){ + event.entityLiving.ridingEntity.worldObj.playSoundAtEntity(event.entityLiving.ridingEntity, "random.pop", 1.5f, 1.0f); + event.entityLiving.ridingEntity.setDead(); + } - EntityLivingBase attacker = (EntityLivingBase)event.getSource().getEntity(); - World world = event.getEntityLiving().worldObj; + // Prevents all unblockable damage while transience is active + if(event.entityLiving.isPotionActive(Wizardry.transience) && event.source != null && !event.source.isUnblockable()){ + event.setCanceled(true); + // Again, I think this ought to be here, since if the event is cancelled nothing else needs to happen. + return; + } - // Fireskin - if(event.getEntityLiving().isPotionActive(WizardryPotions.fireskin) - && !MagicDamage.isEntityImmune(DamageType.FIRE, event.getEntityLiving())) - attacker.setFire(5); + if(event.source != null && event.source.getEntity() instanceof EntityLivingBase){ - // Ice Shroud - if(event.getEntityLiving().isPotionActive(WizardryPotions.ice_shroud) && - !MagicDamage.isEntityImmune(DamageType.FROST, event.getEntityLiving())) - attacker.addPotionEffect(new PotionEffect(WizardryPotions.frost, 100, 0)); + // Cancels the mind trick effect if the creature takes damage + // This has been moved to within the (event.source.getEntity() instanceof EntityLivingBase) check so it doesn't + // crash the game with a ConcurrentModificationException. If you think about it, mind trick only ought to be + // cancelled if something attacks the entity since potions, drowning, cacti etc. don't affect the targeting. + if(event.entityLiving.isPotionActive(Wizardry.mindTrick)){ + event.entityLiving.removePotionEffect(Wizardry.mindTrick.id); + } - // Static Aura - if(event.getEntityLiving().isPotionActive(WizardryPotions.static_aura)){ + // 'Revenge' effects + EntityLivingBase attacker = (EntityLivingBase)event.source.getEntity(); + World world = event.entityLiving.worldObj; + + ItemStack wand = event.entityLiving.getHeldItem(); + + if(event.entityLiving.isPotionActive(Wizardry.fireskin) && !event.source.isProjectile()){ + if(!MagicDamage.isEntityImmune(DamageType.FIRE, event.entityLiving)) attacker.setFire(5); + } + + if(event.entityLiving.isPotionActive(Wizardry.iceShroud) && !event.source.isProjectile()){ + if(!MagicDamage.isEntityImmune(DamageType.FROST, event.entityLiving)) + attacker.addPotionEffect(new PotionEffect(Wizardry.frost.id, 100, 0, true)); + } + + if(event.entityLiving.isPotionActive(Wizardry.staticAura) && !event.source.isProjectile()){ if(!world.isRemote){ EntityArc arc = new EntityArc(world); - arc.setEndpointCoords(event.getEntityLiving().posX, event.getEntityLiving().posY + 1, event.getEntityLiving().posZ, + arc.setEndpointCoords(event.entityLiving.posX, event.entityLiving.posY + 1, event.entityLiving.posZ, attacker.posX, attacker.posY + attacker.height/2, attacker.posZ); world.spawnEntityInWorld(arc); }else{ for(int i=0;i<8;i++){ - Wizardry.proxy.spawnParticle(WizardryParticleType.SPARK, world, attacker.posX + world.rand.nextFloat() - 0.5, attacker.getEntityBoundingBox().minY + attacker.height/2 + world.rand.nextFloat()*2 - 1, attacker.posZ + world.rand.nextFloat() - 0.5, 0, 0, 0, 3); - world.spawnParticle(EnumParticleTypes.SMOKE_LARGE, attacker.posX + world.rand.nextFloat() - 0.5, attacker.getEntityBoundingBox().minY + attacker.height/2 + world.rand.nextFloat()*2 - 1, attacker.posZ + world.rand.nextFloat() - 0.5, 0, 0, 0); + Wizardry.proxy.spawnParticle(EnumParticleType.SPARK, world, attacker.posX + world.rand.nextFloat() - 0.5, WizardryUtilities.getEntityFeetPos(attacker) + attacker.height/2 + world.rand.nextFloat()*2 - 1, attacker.posZ + world.rand.nextFloat() - 0.5, 0, 0, 0, 3); + world.spawnParticle("largesmoke", attacker.posX + world.rand.nextFloat() - 0.5, WizardryUtilities.getEntityFeetPos(attacker) + attacker.height/2 + world.rand.nextFloat()*2 - 1, attacker.posZ + world.rand.nextFloat() - 0.5, 0, 0, 0); } } - attacker.attackEntityFrom(MagicDamage.causeDirectMagicDamage(event.getEntityLiving(), DamageType.SHOCK, true), 4.0f); - attacker.playSound(WizardrySounds.SPELL_SPARK, 1.0F, world.rand.nextFloat() * 0.4F + 1.5F); + attacker.attackEntityFrom(MagicDamage.causeDirectMagicDamage(event.entityLiving, DamageType.SHOCK), 4.0f); + world.playSoundAtEntity(attacker, "wizardry:arc", 1.0F, world.rand.nextFloat() * 0.4F + 1.5F); } - } + // Shadow ward + if(event.entityLiving instanceof EntityPlayer){ + if(((EntityPlayer)event.entityLiving).isUsingItem() && wand != null && wand.getItemDamage() < wand.getMaxDamage() + && wand.getItem() instanceof ItemWand && WandHelper.getCurrentSpell(wand) instanceof ShadowWard + && !event.source.isUnblockable()){ + + event.setCanceled(true); + event.entityLiving.attackEntityFrom(DamageSource.magic, event.ammount/2); + attacker.attackEntityFrom(MagicDamage.causeDirectMagicDamage(event.entityLiving, DamageType.MAGIC), event.ammount/2); + } + } + + if(attacker.isPotionActive(Wizardry.transience)){ + event.setCanceled(true); + } + + // This behaviour has been removed, but is left here in case it is needed in future. + /* + if(attacker.isPotionActive(Wizardry.fireskin) && !event.source.isProjectile()){ + event.entityLiving.setFire(5); + } + + if(attacker.isPotionActive(Wizardry.iceShroud) && !event.source.isProjectile()){ + event.entityLiving.addPotionEffect(new PotionEffect(Wizardry.frost.id, 100, 0, true)); + } + */ + } } @SubscribeEvent - public static void onLivingHurtEvent(LivingHurtEvent event){ - + public void onLivingHurtEvent(LivingHurtEvent event){ + + // Curse of soulbinding + if(!event.entity.worldObj.isRemote && event.entityLiving instanceof EntityPlayer && !event.source.isUnblockable()){ + ExtendedPlayer properties = ExtendedPlayer.get((EntityPlayer)event.entityLiving); + if(properties != null){ + properties.damageAllSoulboundCreatures(event.ammount); + } + } + // Flaming and freezing swords - if(event.getSource().getEntity() instanceof EntityLivingBase){ - - EntityLivingBase attacker = (EntityLivingBase)event.getSource().getEntity(); - - // Players can only ever attack with their main hand, so this is the right method to use here. - if(attacker.getHeldItemMainhand() != null && attacker.getHeldItemMainhand().getItem() instanceof ItemSword){ - - int level = EnchantmentHelper.getEnchantmentLevel(WizardryEnchantments.flaming_weapon, attacker.getHeldItemMainhand()); - - if(level > 0 && !MagicDamage.isEntityImmune(DamageType.FIRE, event.getEntityLiving())) - event.getEntityLiving().setFire(level*4); - - level = EnchantmentHelper.getEnchantmentLevel(WizardryEnchantments.freezing_weapon, attacker.getHeldItemMainhand()); + if(event.source.getEntity() instanceof EntityLivingBase){ + EntityLivingBase attacker = (EntityLivingBase)event.source.getEntity(); + if(attacker.getHeldItem() != null && attacker.getHeldItem().getItem() instanceof ItemSword){ + + int level = EnchantmentHelper.getEnchantmentLevel(Wizardry.flamingWeapon.effectId, attacker.getHeldItem()); + + if(level > 0 && !MagicDamage.isEntityImmune(DamageType.FIRE, event.entityLiving)) + event.entityLiving.setFire(level*4); + + level = EnchantmentHelper.getEnchantmentLevel(Wizardry.freezingWeapon.effectId, attacker.getHeldItem()); // Frost lasts for longer because it doesn't do any actual damage - if(level > 0 && !MagicDamage.isEntityImmune(DamageType.FROST, event.getEntityLiving())) - event.getEntityLiving().addPotionEffect(new PotionEffect(WizardryPotions.frost, level*200, 0)); + if(level > 0 && !MagicDamage.isEntityImmune(DamageType.FROST, event.entityLiving)) + event.entityLiving.addPotionEffect(new PotionEffect(Wizardry.frost.id, level*200, 0, true)); } } - + // Freezing bow - if(event.getSource().getSourceOfDamage() instanceof EntityArrow && event.getSource().getSourceOfDamage().getEntityData() != null){ - - int level = event.getSource().getSourceOfDamage().getEntityData().getInteger(FreezingWeapon.FREEZING_ARROW_NBT_KEY); - - if(level > 0 && !MagicDamage.isEntityImmune(DamageType.FROST, event.getEntityLiving())) - event.getEntityLiving().addPotionEffect(new PotionEffect(WizardryPotions.frost, level*150, 0)); + if(event.source.getSourceOfDamage() instanceof EntityArrow && event.source.getSourceOfDamage().getEntityData() != null){ + + int level = event.source.getSourceOfDamage().getEntityData().getInteger(FreezingWeapon.FREEZING_ARROW_NBT_KEY); + + if(level > 0 && !MagicDamage.isEntityImmune(DamageType.FROST, event.entityLiving)) + event.entityLiving.addPotionEffect(new PotionEffect(Wizardry.frost.id, level*150, 0, true)); } - + // Damage scaling - if(event.getSource() != null && event.getSource() instanceof IElementalDamage){ - - if(event.getSource().getEntity() instanceof EntityPlayer){ - event.setAmount((float)(event.getAmount() * Wizardry.settings.playerDamageScale)); + if(event.source != null && event.source instanceof IElementalDamage){ + + if(event.source.getEntity() instanceof EntityPlayer){ + event.ammount *= Wizardry.playerDamageScale; }else{ - event.setAmount((float)(event.getAmount() * Wizardry.settings.npcDamageScale)); + event.ammount *= Wizardry.npcDamageScale; } } } @SubscribeEvent - public static void onLivingUpdateEvent(LivingUpdateEvent event){ + public void onBlockPlaceEvent(BlockEvent.PlaceEvent event){ - if(event.getEntityLiving() instanceof EntityPlayer){ + if(event.player.isPotionActive(Wizardry.transience)){ + event.setCanceled(true); + return; + } - EntityPlayer player = (EntityPlayer)event.getEntityLiving(); + // Spectral blocks cannot be built on + if(event.placedAgainst == Wizardry.spectralBlock){ + event.setCanceled(true); + return; + } + } - if(player.worldObj.isRemote) hackilyFixContinuousSpellCasting(player); + @SubscribeEvent + public void onBlockBreakEvent(BlockEvent.BreakEvent event){ + + if(event.getPlayer().isPotionActive(Wizardry.transience)){ + event.setCanceled(true); + return; + } - if(player.openContainer instanceof ContainerWorkbench){ - craftingTableTick(player); + // Makes wizards angry if a player breaks a block in their tower + if(!(event.getPlayer() instanceof FakePlayer)){ + + List wizards = WizardryUtilities.getEntitiesWithinRadius(64, event.x, event.y, event.z, event.world, EntityWizard.class); + + if(!wizards.isEmpty()){ + for(EntityWizard wizard : wizards){ + if(wizard.isBlockPartOfTower(event.x, event.y, event.z)){ + wizard.setRevengeTarget(event.getPlayer()); + event.getPlayer().triggerAchievement(Wizardry.angerWizard); + } + } + } + } + } + + @SubscribeEvent + public void onEntityStruckByLightningEvent(EntityStruckByLightningEvent event){ + + if(event.lightning.getEntityData() != null && event.lightning.getEntityData().hasKey("summoningPlayer")){ + + EntityPlayer player = (EntityPlayer)WizardryUtilities.getEntityByUUID(event.lightning.worldObj, UUID.fromString(event.lightning.getEntityData().getString("summoningPlayer"))); + + if(event.entity instanceof EntityCreeper){ + player.triggerAchievement(Wizardry.chargeCreeper); } - if(player.openContainer instanceof ContainerPlayer){ + if(event.entity instanceof EntityPig){ + player.triggerAchievement(Wizardry.frankenstein); + } + } + + } + + @SubscribeEvent + public void onLivingUpdateEvent(LivingUpdateEvent event){ + + if(event.entityLiving instanceof EntityPlayer){ + + EntityPlayer entityplayer = (EntityPlayer)event.entityLiving; + + if(ExtendedPlayer.get(entityplayer) != null){ + ExtendedPlayer.get(entityplayer).update(entityplayer); + } + + if(entityplayer.openContainer instanceof ContainerWorkbench){ + this.craftingTableTick(entityplayer); + } + + if(entityplayer.openContainer instanceof ContainerPlayer){ // Unfortunately I have no choice but to call this method every tick when the player isn't using another // inventory, since the only thing tracking whether the player is looking at their inventory is the GUI // itself, which is client-side only. - playerInventoryTick(player); + this.playerInventoryTick(entityplayer); } - testForArmourSet:{ - for(ItemStack stack : player.getArmorInventoryList()){ - if(stack == null || !(stack.getItem() instanceof ItemWizardArmour)){ - break testForArmourSet; + if(entityplayer.getCurrentArmor(0) != null && entityplayer.getCurrentArmor(0).getItem() instanceof ItemWizardArmour + && entityplayer.getCurrentArmor(1) != null && entityplayer.getCurrentArmor(1).getItem() instanceof ItemWizardArmour + && entityplayer.getCurrentArmor(2) != null && entityplayer.getCurrentArmor(2).getItem() instanceof ItemWizardArmour + && entityplayer.getCurrentArmor(3) != null && entityplayer.getCurrentArmor(3).getItem() instanceof ItemWizardArmour){ + + entityplayer.triggerAchievement(Wizardry.armourSet); + } + + // Tests for magic weapons, decrements the time until the magic wears off, and removes the enchantment if + // it has run out. + + magicWeapons: + if(ExtendedPlayer.get(entityplayer) != null){ + + for(ItemStack stack : entityplayer.inventory.mainInventory){ + + if(stack != null && stack.isItemEnchanted()){ + + Map enchantments = EnchantmentHelper.getEnchantments(stack); + + if(enchantments.containsKey(Wizardry.magicSword.effectId) || enchantments.containsKey(Wizardry.magicBow.effectId)){ + + if(ExtendedPlayer.get(entityplayer).magicWeaponDuration > 0){ + // Decrements the time until the magic wears off + ExtendedPlayer.get(entityplayer).magicWeaponDuration--; + }else{ + // Removes the magic weapon enchantment from the enchantment map + enchantments.remove(Wizardry.magicSword.effectId); + enchantments.remove(Wizardry.magicBow.effectId); + // Applies the new enchantment map to the item + EnchantmentHelper.setEnchantments(enchantments, stack); + } + + // If it found an imbued weapon, it can stop since there should only be one. + // More importantly, doing this skips the reset thing below. Just a bit tidier than a flag. + break magicWeapons; + } } } - player.addStat(WizardryAchievements.armour_set); + + // Resets the magic weapon timer if the player no longer has one. + if(ExtendedPlayer.get(entityplayer).magicWeaponDuration > 0){ + ExtendedPlayer.get(entityplayer).magicWeaponDuration = 0; + } } - } + // Tests for flaming weapons, decrements the time until the magic wears off, and removes the enchantment if + // it has run out. - if(event.getEntityLiving().worldObj.isRemote){ + flamingWeapons: + if(ExtendedPlayer.get(entityplayer) != null){ - // Client-side continuous spell casting for NPCs + for(ItemStack stack : entityplayer.inventory.mainInventory){ - if(event.getEntity() instanceof ISpellCaster && event.getEntity() instanceof EntityLiving){ + if(stack != null && stack.isItemEnchanted()){ - Spell spell = ((ISpellCaster)event.getEntity()).getContinuousSpell(); - SpellModifiers modifiers = ((ISpellCaster)event.getEntity()).getModifiers(); + Map enchantments = EnchantmentHelper.getEnchantments(stack); - if(spell != null && spell != Spells.none){ - - if(!MinecraftForge.EVENT_BUS.post(new SpellCastEvent.Tick(event.getEntityLiving(), spell, modifiers, - SpellCastEvent.Source.NPC, 0))){ - - spell.cast(event.getEntity().worldObj, (EntityLiving)event.getEntity(), EnumHand.MAIN_HAND, 0, - // TODO: This implementation of modifiers relies on them being accessible client-side. - ((EntityLiving)event.getEntity()).getAttackTarget(), modifiers); + if(enchantments.containsKey(Wizardry.flamingWeapon.effectId)){ + + if(ExtendedPlayer.get(entityplayer).flamingWeaponDuration > 0){ + // Decrements the time until the flaming wears off + ExtendedPlayer.get(entityplayer).flamingWeaponDuration--; + }else{ + // Removes the flaming weapon enchantment from the enchantment map + enchantments.remove(Wizardry.flamingWeapon.effectId); + // Applies the new enchantment map to the item + EnchantmentHelper.setEnchantments(enchantments, stack); + } + + // If it found an imbued weapon, it can stop since there should only be one. + // More importantly, doing this skips the reset thing below. Just a bit tidier than a flag. + break flamingWeapons; + } } } - } - } - } - @SubscribeEvent - public static void onLivingDeathEvent(LivingDeathEvent event){ - - if(event.getSource().getEntity() instanceof EntityPlayer){ - - EntityPlayer player = (EntityPlayer)event.getSource().getEntity(); - - for(ItemStack stack : WizardryUtilities.getPrioritisedHotbarAndOffhand(player)){ - - if(stack != null && stack.getItem() instanceof ItemWand && stack.isItemDamaged() && WandHelper.getUpgradeLevel(stack, WizardryItems.siphon_upgrade) > 0){ - int damage = stack.getItemDamage() - Constants.SIPHON_MANA_PER_LEVEL*WandHelper.getUpgradeLevel(stack, WizardryItems.siphon_upgrade) - player.worldObj.rand.nextInt(Constants.SIPHON_MANA_PER_LEVEL); - if(damage < 0) damage = 0; - stack.setItemDamage(damage); - break; + // Resets the flaming weapon timer if the player no longer has one. + if(ExtendedPlayer.get(entityplayer).flamingWeaponDuration > 0){ + ExtendedPlayer.get(entityplayer).flamingWeaponDuration = 0; } } + + // Tests for freezing weapons, decrements the time until the magic wears off, and removes the enchantment if + // it has run out. - if(event.getEntityLiving() == player && event.getSource() instanceof IElementalDamage){ - player.addStat(WizardryAchievements.self_destruct); + freezingWeapons: + if(ExtendedPlayer.get(entityplayer) != null){ + + for(ItemStack stack : entityplayer.inventory.mainInventory){ + + if(stack != null && stack.isItemEnchanted()){ + + Map enchantments = EnchantmentHelper.getEnchantments(stack); + + if(enchantments.containsKey(Wizardry.freezingWeapon.effectId)){ + + if(ExtendedPlayer.get(entityplayer).freezingWeaponDuration > 0){ + // Decrements the time until the freezing wears off + ExtendedPlayer.get(entityplayer).freezingWeaponDuration--; + }else{ + // Removes the freezing weapon enchantment from the enchantment map + enchantments.remove(Wizardry.freezingWeapon.effectId); + // Applies the new enchantment map to the item + EnchantmentHelper.setEnchantments(enchantments, stack); + } + + // If it found an imbued weapon, it can stop since there should only be one. + // More importantly, doing this skips the reset thing below. Just a bit tidier than a flag. + break freezingWeapons; + } + } + } + + // Resets the freezing weapon timer if the player no longer has one. + if(ExtendedPlayer.get(entityplayer).freezingWeaponDuration > 0){ + ExtendedPlayer.get(entityplayer).freezingWeaponDuration = 0; + } + } + + } + + if(event.entityLiving.worldObj.isRemote){ + + if(event.entityLiving.isPotionActive(Wizardry.frost)){ + + double x = event.entityLiving.posX + (event.entityLiving.worldObj.rand.nextDouble() - 0.5)*event.entityLiving.width; + double y = WizardryUtilities.getEntityFeetPos(event.entityLiving) + event.entityLiving.worldObj.rand.nextDouble()*event.entityLiving.height; + double z = event.entityLiving.posZ + (event.entityLiving.worldObj.rand.nextDouble() - 0.5)*event.entityLiving.width; + + Wizardry.proxy.spawnParticle(EnumParticleType.SNOW, event.entityLiving.worldObj, x, y, z, 0, -0.02, 0, 15 + event.entityLiving.worldObj.rand.nextInt(5)); + } + + if(event.entityLiving.isPotionActive(Wizardry.fireskin)){ + + double x = event.entityLiving.posX + (event.entityLiving.worldObj.rand.nextDouble() - 0.5)*event.entityLiving.width; + double y = WizardryUtilities.getEntityFeetPos(event.entityLiving) + event.entityLiving.worldObj.rand.nextDouble()*event.entityLiving.height; + double z = event.entityLiving.posZ + (event.entityLiving.worldObj.rand.nextDouble() - 0.5)*event.entityLiving.width; + + event.entityLiving.worldObj.spawnParticle("flame", x, y, z, 0, 0, 0); + } + + if(event.entityLiving.isPotionActive(Wizardry.iceShroud)){ + + double x = event.entityLiving.posX + (event.entityLiving.worldObj.rand.nextDouble() - 0.5)*event.entityLiving.width; + double y = WizardryUtilities.getEntityFeetPos(event.entityLiving) + event.entityLiving.worldObj.rand.nextDouble()*event.entityLiving.height; + double z = event.entityLiving.posZ + (event.entityLiving.worldObj.rand.nextDouble() - 0.5)*event.entityLiving.width; + + float brightness = 0.5f + (event.entityLiving.worldObj.rand.nextFloat()/2); + Wizardry.proxy.spawnParticle(EnumParticleType.SPARKLE, event.entityLiving.worldObj, x, y, z, 0, 0, 0, 48 + event.entityLiving.worldObj.rand.nextInt(12), brightness, brightness + 0.1f, 1.0f, true, 0); + Wizardry.proxy.spawnParticle(EnumParticleType.SNOW, event.entityLiving.worldObj, x, y, z, 0, -0.02, 0, 40 + event.entityLiving.worldObj.rand.nextInt(10)); + } + + if(event.entityLiving.isPotionActive(Wizardry.staticAura)){ + + double x = event.entityLiving.posX + (event.entityLiving.worldObj.rand.nextDouble() - 0.5)*event.entityLiving.width; + double y = WizardryUtilities.getEntityFeetPos(event.entityLiving) + event.entityLiving.worldObj.rand.nextDouble()*event.entityLiving.height; + double z = event.entityLiving.posZ + (event.entityLiving.worldObj.rand.nextDouble() - 0.5)*event.entityLiving.width; + + Wizardry.proxy.spawnParticle(EnumParticleType.SPARK, event.entityLiving.worldObj, x, y, z, 0, 0, 0, 3); + } + + if(event.entityLiving.isPotionActive(Wizardry.transience)){ + + double x = event.entityLiving.posX + (event.entityLiving.worldObj.rand.nextDouble() - 0.5)*event.entityLiving.width; + double y = WizardryUtilities.getEntityFeetPos(event.entityLiving) + event.entityLiving.worldObj.rand.nextDouble()*event.entityLiving.height; + double z = event.entityLiving.posZ + (event.entityLiving.worldObj.rand.nextDouble() - 0.5)*event.entityLiving.width; + + Wizardry.proxy.spawnParticle(EnumParticleType.DUST, event.entityLiving.worldObj, x, y, z, 0, 0, 0, (int)(16.0D / (Math.random() * 0.8D + 0.2D)), 0.8f, 0.8f, 1.0f); + } + + }else{ + + if(event.entityLiving.isPotionActive(Wizardry.decay) && event.entityLiving.onGround && event.entityLiving.ticksExisted % Wizardry.DECAY_SPREAD_INTERVAL == 0){ + + List list = event.entityLiving.worldObj.getEntitiesWithinAABBExcludingEntity(event.entityLiving, event.entityLiving.boundingBox); + + boolean flag = true; + + for(Object object : list){ + if(object instanceof EntityDecay) flag = false; + } + + if(flag){ + // The victim spreading the decay is the 'caster' here, so that it can actually wear off, otherwise it just gets infected with its own decay and the effect lasts forever. + event.entityLiving.worldObj.spawnEntityInWorld(new EntityDecay(event.entityLiving.worldObj, event.entityLiving.posX, event.entityLiving.posY, event.entityLiving.posZ, event.entityLiving)); + } + } + } + + // Mind trick + if(event.entityLiving.isPotionActive(Wizardry.mindTrick) && event.entityLiving instanceof EntityLiving){ + // Old AI (this can't be done in onLivingSetAttackTargetEvent because that only fires for the new AI). + if(event.entityLiving instanceof EntityCreature) ((EntityCreature)event.entityLiving).setTarget(null); + } + + // Mind control - old AI (this can't be done in onLivingSetAttackTargetEvent because that only fires for the new AI). + mindcontrol: + if(event.entityLiving.isPotionActive(Wizardry.mindControl) && event.entityLiving instanceof EntityLiving){ + + NBTTagCompound entityNBT = event.entityLiving.getEntityData(); + + if(entityNBT != null && entityNBT.hasKey(MindControl.NBT_KEY)){ + + Entity caster = WizardryUtilities.getEntityByUUID(event.entity.worldObj, UUID.fromString(entityNBT.getString(MindControl.NBT_KEY))); + + if(caster instanceof EntityLivingBase){ + + if(MindControl.findMindControlTarget((EntityLiving)event.entityLiving, (EntityLivingBase)caster, event.entity.worldObj)){ + // If it worked, skip setting the target to null. + break mindcontrol; + } + } + } + // If the caster couldn't be found or no valid target was found, this just acts like mind trick. + ((EntityLiving)event.entityLiving).setAttackTarget(null); + } + + // Terror + if(event.entityLiving.isPotionActive(Wizardry.fear) && event.entityLiving instanceof EntityCreature){ + + NBTTagCompound entityNBT = event.entityLiving.getEntityData(); + EntityCreature creature = (EntityCreature)event.entityLiving; + + if(entityNBT != null && entityNBT.hasKey(Intimidate.NBT_KEY)){ + + Entity caster = WizardryUtilities.getEntityByUUID(creature.worldObj, UUID.fromString(entityNBT.getString(Intimidate.NBT_KEY))); + + if(caster instanceof EntityLivingBase){ + Intimidate.runAway(creature, (EntityLivingBase)caster); + } } } } @SubscribeEvent - public static void onLivingDropsEvent(LivingDropsEvent event){ - // TODO: Really, this should be in a loot table (mob_additions), however I can't seem to find a way of - // automatically adding it to all subclasses of IMob. - // Evil wizards drop spell books themselves - if(event.getEntityLiving() instanceof IMob && !(event.getEntityLiving() instanceof EntityEvilWizard) - // TODO: Backport when you backport the new summoned creature system. - && !(event.getEntityLiving() instanceof ISummonedCreature) && event.getSource().getEntity() instanceof EntityPlayer - && Wizardry.settings.spellBookDropChance > 0){ - - // This does exactly what the entity drop method does, but with a different random number so that the - // spell book doesn't always drop with other rare drops. - int rareDropNumber = event.getEntity().worldObj.rand.nextInt(200) - event.getLootingLevel(); - if(rareDropNumber < Wizardry.settings.spellBookDropChance){ - // Drops a spell book - int id = WizardryUtilities.getStandardWeightedRandomSpellId(event.getEntity().worldObj.rand); - - event.getDrops().add(new EntityItem(event.getEntityLiving().worldObj, event.getEntityLiving().posX, event.getEntityLiving().posY, event.getEntityLiving().posZ, - new ItemStack(WizardryItems.spell_book, 1, id))); - } + public void onBreakSpeedEvent(BreakSpeed event){ + if(event.entityPlayer.isPotionActive(Wizardry.frost)){ + // Amplifier + 1 because it starts at 0 + event.newSpeed = event.originalSpeed * (1 - Wizardry.FROST_FATIGUE_PER_LEVEL*(event.entityPlayer.getActivePotionEffect(Wizardry.frost).getAmplifier() + 1)); } } - @SubscribeEvent - public static void onItemPickupEvent(EntityItemPickupEvent event){ - if(event.getItem().getEntityItem().getItem() == WizardryItems.magic_crystal){ - event.getEntityPlayer().addStat(WizardryAchievements.crystal, 1); - } - } - - // Private helper methods - // ================================================================================================================ - - /** - * Detects inconsistencies between player.getActiveItemStack and the actual itemstack and forces them to be equal. - * Fixes issue #25. - * @param player - */ - private static void hackilyFixContinuousSpellCasting(EntityPlayer player){ - if(player.isHandActive() && player.getHeldItem(player.getActiveHand()) != null - && player.getHeldItem(player.getActiveHand()).getItem() instanceof ItemWand - && WandHelper.getCurrentSpell(player.getHeldItem(player.getActiveHand())).isContinuous){ - if(player.getActiveItemStack() != player.getHeldItem(player.getActiveHand())){ - player.setHeldItem(player.getActiveHand(), player.getActiveItemStack()); - } - } - } - - private static void playerInventoryTick(EntityPlayer player){ + private void playerInventoryTick(EntityPlayer player) { // Charges wand using mana flask. It is here rather than in the crafting handler so the result displays // the proper damage before it is actually crafted. @@ -425,11 +607,11 @@ public final class WizardryEventHandler { IInventory craftMatrix = ((ContainerPlayer)player.openContainer).craftMatrix; ItemStack outputItem = ((ContainerPlayer)player.openContainer).craftResult.getStackInSlot(0); - for(int i = 0; i < craftMatrix.getSizeInventory(); i++){ + for (int i = 0; i < craftMatrix.getSizeInventory(); i++){ if(craftMatrix.getStackInSlot(i) != null){ ItemStack itemstack = craftMatrix.getStackInSlot(i); - if(itemstack.getItem() == WizardryItems.mana_flask){ + if(itemstack.getItem() == Wizardry.manaFlask){ flag = true; } @@ -444,27 +626,28 @@ public final class WizardryEventHandler { } if(outputItem != null && outputItem.getItem() instanceof ItemWand && flag && wand != null){ - outputItem.setTagCompound((wand.getTagCompound())); - if(wand.getItemDamage()-Constants.MANA_PER_FLASK < 0){ + outputItem.setTagCompound(wand.getTagCompound()); + if(wand.getItemDamage()-Wizardry.MANA_PER_FLASK < 0){ outputItem.setItemDamage(0); }else{ - outputItem.setItemDamage(wand.getItemDamage()-Constants.MANA_PER_FLASK); + outputItem.setItemDamage(wand.getItemDamage()-Wizardry.MANA_PER_FLASK); } } if(outputItem != null && outputItem.getItem() instanceof ItemWizardArmour && flag && armour != null){ - outputItem.setTagCompound((armour.getTagCompound())); - if(armour.getItemDamage()-Constants.MANA_PER_FLASK < 0){ + outputItem.setTagCompound(armour.getTagCompound()); + if(armour.getItemDamage()-Wizardry.MANA_PER_FLASK < 0){ outputItem.setItemDamage(0); }else{ - outputItem.setItemDamage(wand.getItemDamage()-Constants.MANA_PER_FLASK); + outputItem.setItemDamage(wand.getItemDamage()-Wizardry.MANA_PER_FLASK); } } } - private static void craftingTableTick(EntityPlayer player) { + private void craftingTableTick(EntityPlayer player) { - // Charges wand using mana flask + // Charges wand using mana flask. It is here rather than in the crafting handler so the result displays + // the proper damage before it is actually crafted. boolean flag = false; ItemStack wand = null; @@ -476,7 +659,7 @@ public final class WizardryEventHandler { if(craftMatrix.getStackInSlot(i) != null){ ItemStack itemstack = craftMatrix.getStackInSlot(i); - if(itemstack.getItem() == WizardryItems.mana_flask){ + if(itemstack.getItem() == Wizardry.manaFlask){ flag = true; } @@ -491,22 +674,300 @@ public final class WizardryEventHandler { } if(outputItem != null && outputItem.getItem() instanceof ItemWand && flag && wand != null){ - outputItem.setTagCompound((wand.getTagCompound())); - if(wand.getItemDamage()-Constants.MANA_PER_FLASK < 0){ + outputItem.setTagCompound(wand.getTagCompound()); + if(wand.getItemDamage()-Wizardry.MANA_PER_FLASK < 0){ outputItem.setItemDamage(0); }else{ - outputItem.setItemDamage(wand.getItemDamage()-Constants.MANA_PER_FLASK); + outputItem.setItemDamage(wand.getItemDamage()-Wizardry.MANA_PER_FLASK); } } if(outputItem != null && outputItem.getItem() instanceof ItemWizardArmour && flag && armour != null){ - outputItem.setTagCompound((armour.getTagCompound())); - if(armour.getItemDamage()-Constants.MANA_PER_FLASK < 0){ + outputItem.setTagCompound(armour.getTagCompound()); + if(armour.getItemDamage()-Wizardry.MANA_PER_FLASK < 0){ outputItem.setItemDamage(0); }else{ - outputItem.setItemDamage(wand.getItemDamage()-Constants.MANA_PER_FLASK); + outputItem.setItemDamage(wand.getItemDamage()-Wizardry.MANA_PER_FLASK); } } } + @SubscribeEvent + public void onLivingDeathEvent(LivingDeathEvent event){ + + // Extended player data saving + if (!event.entity.worldObj.isRemote && event.entity instanceof EntityPlayer){ + // This should never be null, but no harm in checking. + if(ExtendedPlayer.get((EntityPlayer)event.entity) != null){ + + ExtendedPlayer.get((EntityPlayer)event.entity).onPlayerDeath(); + + NBTTagCompound playerData = new NBTTagCompound(); + ExtendedPlayer.get((EntityPlayer)event.entity).saveNBTData(playerData); + CommonProxy.storeEntityData(ExtendedPlayer.getSaveKey((EntityPlayer)event.entity), playerData); + } + } + + if(event.source.getEntity() instanceof EntityPlayer){ + + EntityPlayer player = (EntityPlayer)event.source.getEntity(); + + // Mana siphoning. Works for the first wand with a siphon upgrade on the hotbar. + for(int i=0; i<9; i++){ + ItemStack itemstack = player.inventory.getStackInSlot(i); + if(itemstack != null && itemstack.getItem() instanceof ItemWand && itemstack.isItemDamaged() && WandHelper.getUpgradeLevel(itemstack, Wizardry.siphonUpgrade) > 0){ + int damage = itemstack.getItemDamage() - Wizardry.SIPHON_MANA_PER_LEVEL*WandHelper.getUpgradeLevel(itemstack, Wizardry.siphonUpgrade) - player.worldObj.rand.nextInt(Wizardry.SIPHON_MANA_PER_LEVEL); + if(damage < 0) damage = 0; + itemstack.setItemDamage(damage); + break; + } + } + + if(event.entityLiving == player && event.source instanceof IElementalDamage){ + player.triggerAchievement(Wizardry.selfDestruct); + } + } + + if(event.source.getEntity() instanceof EntitySilverfishMinion){ + int lifetime = ((EntitySilverfishMinion)event.source.getEntity()).lifetime; + // Summons 1-4 more silverfish + int alliesToSummon = event.entity.worldObj.rand.nextInt(4) + 1; + + for(int i=0; i 0){ + arrow.setDamage(arrow.getDamage() + (double)level * 0.5D + 0.5D); + } + + if(EnchantmentHelper.getEnchantmentLevel(Wizardry.flamingWeapon.effectId, archer.getHeldItem()) > 0){ + // Again, this is exactly what happens in ItemBow (flame is flame; level does nothing). + arrow.setFire(100); + } + + level = EnchantmentHelper.getEnchantmentLevel(Wizardry.freezingWeapon.effectId, archer.getHeldItem()); + + if(level > 0){ + if(arrow.getEntityData() != null){ + arrow.getEntityData().setInteger(FreezingWeapon.FREEZING_ARROW_NBT_KEY, level); + } + } + } + } + } + + } + + @SubscribeEvent + public void onLivingDropsEvent(LivingDropsEvent event){ + // Evil wizards drop spell books themselves + if(event.entityLiving instanceof EntityMob && !(event.entityLiving instanceof EntityEvilWizard) && event.source.getEntity() instanceof EntityPlayer && Wizardry.spellBookDropChance > 0){ + + // This does exactly what the entity drop method does, but with a different random number so that the + // spell book doesn't always drop with other rare drops. + int rareDropNumber = event.entity.worldObj.rand.nextInt(200) - event.lootingLevel; + if(rareDropNumber < Wizardry.spellBookDropChance){ + // Drops a spell book + int id = WizardryUtilities.getStandardWeightedRandomSpellId(event.entity.worldObj.rand); + + event.drops.add(new EntityItem(event.entityLiving.worldObj, event.entityLiving.posX, event.entityLiving.posY, event.entityLiving.posZ, + new ItemStack(Wizardry.spellBook, 1, id))); + } + } + + for(EntityItem item : event.drops){ + + // Destroys conjured items if their caster dies. + if(item.getEntityItem().getItem() instanceof ItemSpectralSword + || item.getEntityItem().getItem() instanceof ItemSpectralPickaxe + || item.getEntityItem().getItem() instanceof ItemSpectralBow + || item.getEntityItem().getItem() instanceof ItemSpectralArmour + || item.getEntityItem().getItem() instanceof ItemFlamingAxe + || item.getEntityItem().getItem() instanceof ItemFrostAxe){ + + item.setDead(); + } + + // Instantly disenchants an imbued weapon if it is dropped when the player dies. + if(item.getEntityItem().isItemEnchanted()){ + + // No need to check what enchantments the item has, since remove() does nothing if the element does not exist. + Map enchantments = EnchantmentHelper.getEnchantments(item.getEntityItem()); + + // Removes the magic weapon enchantments from the enchantment map + enchantments.remove(Wizardry.magicSword.effectId); + enchantments.remove(Wizardry.magicBow.effectId); + enchantments.remove(Wizardry.flamingWeapon.effectId); + enchantments.remove(Wizardry.freezingWeapon.effectId); + + // Applies the new enchantment map to the item + EnchantmentHelper.setEnchantments(enchantments, item.getEntityItem()); + } + } + } + + @SubscribeEvent + public void onLivingSetAttackTargetEvent(LivingSetAttackTargetEvent event){ + // Mind trick + // If the target is null already, no need to set it to null, or infinite loops will occur. + if((event.entityLiving.isPotionActive(Wizardry.mindTrick) || event.entityLiving.isPotionActive(Wizardry.fear)) && event.entityLiving instanceof EntityLiving && event.target != null){ + // New AI + ((EntityLiving)event.entityLiving).setAttackTarget(null); + } + + // Mind control + mindcontrol: + if(event.entityLiving.isPotionActive(Wizardry.mindControl) && event.entityLiving instanceof EntityLiving){ + + NBTTagCompound entityNBT = event.entityLiving.getEntityData(); + + if(entityNBT != null && entityNBT.hasKey(MindControl.NBT_KEY)){ + + Entity caster = WizardryUtilities.getEntityByUUID(event.entity.worldObj, UUID.fromString(entityNBT.getString(MindControl.NBT_KEY))); + + // If the target that the event tried to set is already a valid mind control target, nothing happens. + if(WizardryUtilities.isValidTarget(caster, event.target)) break mindcontrol; + + if(caster instanceof EntityLivingBase){ + + if(MindControl.findMindControlTarget((EntityLiving)event.entityLiving, (EntityLivingBase)caster, event.entity.worldObj)){ + // If it worked, skip setting the target to null. + break mindcontrol; + } + } + } + // If the caster couldn't be found or no valid target was found, this just acts like mind trick. + // If the target is null already, no need to set it to null, or infinite loops will occur. + if(event.target != null) ((EntityLiving)event.entityLiving).setAttackTarget(null); + } + } + + @SubscribeEvent + public void onItemPickupEvent(EntityItemPickupEvent event){ + if(event.item.getEntityItem().getItem() == Wizardry.magicCrystal){ + event.entityPlayer.addStat(Wizardry.crystal, 1); + } + } + + @SubscribeEvent + public void onItemTossEvent(ItemTossEvent event){ + + // Prevents conjured items being thrown by dragging and dropping outside the inventory. + if(event.entityItem.getEntityItem().getItem() instanceof ItemSpectralSword + || event.entityItem.getEntityItem().getItem() instanceof ItemSpectralPickaxe + || event.entityItem.getEntityItem().getItem() instanceof ItemSpectralBow + || event.entityItem.getEntityItem().getItem() instanceof ItemSpectralArmour + || event.entityItem.getEntityItem().getItem() instanceof ItemFlamingAxe + || event.entityItem.getEntityItem().getItem() instanceof ItemFrostAxe){ + + event.setCanceled(true); + event.player.inventory.addItemStackToInventory(event.entityItem.getEntityItem()); + } + + // Instantly disenchants an imbued weapon if it is thrown on the ground. + if(event.entityItem.getEntityItem().isItemEnchanted()){ + + // No need to check what enchantments the item has, since remove() does nothing if the element does not exist. + Map enchantments = EnchantmentHelper.getEnchantments(event.entityItem.getEntityItem()); + + // Removes the magic weapon enchantments from the enchantment map + enchantments.remove(Wizardry.magicSword.effectId); + enchantments.remove(Wizardry.magicBow.effectId); + enchantments.remove(Wizardry.flamingWeapon.effectId); + enchantments.remove(Wizardry.freezingWeapon.effectId); + + // Applies the new enchantment map to the item + EnchantmentHelper.setEnchantments(enchantments, event.entityItem.getEntityItem()); + } + } + + @SubscribeEvent + public void onPlayerInteractEvent(PlayerInteractEvent event){ + + if(event.action == PlayerInteractEvent.Action.RIGHT_CLICK_BLOCK && event.entityPlayer.isSneaking()){ + + ItemStack wand = event.entityPlayer.getHeldItem(); + + if(wand != null && wand.getItem() instanceof ItemWand && WandHelper.getCurrentSpell(wand) instanceof Clairvoyance){ + + ExtendedPlayer properties = ExtendedPlayer.get(event.entityPlayer); + + if(properties != null){ + + int x = event.x; + int y = event.y; + int z = event.z; + + if(event.face == 0) y--; + if(event.face == 1) y++; + if(event.face == 2) z--; + if(event.face == 3) z++; + if(event.face == 4) x--; + if(event.face == 5) x++; + + properties.setClairvoyancePoint(x, y, z, event.world.provider.dimensionId); + if(!event.world.isRemote){ + event.entityPlayer.addChatMessage(new ChatComponentTranslation("spell.clairvoyance.confirm", WizardryRegistry.clairvoyance.getDisplayNameWithFormatting())); + event.setCanceled(true); + } + } + } + } + } + + @SubscribeEvent + public void onBonemealEvent(BonemealEvent event){ + // Grows crystal flowers when bonemeal is used on grass + if(event.block == Blocks.grass){ + + int x = event.x + event.world.rand.nextInt(8) - event.world.rand.nextInt(8); + int y = event.y + event.world.rand.nextInt(4) - event.world.rand.nextInt(4); + int z = event.z + event.world.rand.nextInt(8) - event.world.rand.nextInt(8); + + if (event.world.isAirBlock(x, y, z) && (!event.world.provider.hasNoSky || y < 127) && Wizardry.crystalFlower.canBlockStay(event.world, x, y, z)) + { + event.world.setBlock(x, y, z, Wizardry.crystalFlower, 0, 2); + } + } + } } \ No newline at end of file diff --git a/src/main/java/electroblob/wizardry/WizardryGuiFactory.java b/src/main/java/electroblob/wizardry/WizardryGuiFactory.java index 0f7c0161..1d330a0e 100644 --- a/src/main/java/electroblob/wizardry/WizardryGuiFactory.java +++ b/src/main/java/electroblob/wizardry/WizardryGuiFactory.java @@ -2,10 +2,10 @@ package electroblob.wizardry; import java.util.Set; +import cpw.mods.fml.client.IModGuiFactory; import electroblob.wizardry.client.GuiConfigWizardry; import net.minecraft.client.Minecraft; import net.minecraft.client.gui.GuiScreen; -import net.minecraftforge.fml.client.IModGuiFactory; public class WizardryGuiFactory implements IModGuiFactory { @@ -27,8 +27,7 @@ public class WizardryGuiFactory implements IModGuiFactory { return null; } - @SuppressWarnings("deprecation") // It's a bit of Forge that hasn't been tidied up, but we have to implement it. - @Override + @Override public RuntimeOptionGuiHandler getHandlerFor(RuntimeOptionCategoryElement element) { return null; diff --git a/src/main/java/electroblob/wizardry/WizardryGuiHandler.java b/src/main/java/electroblob/wizardry/WizardryGuiHandler.java index ad17f45d..9a0569bd 100644 --- a/src/main/java/electroblob/wizardry/WizardryGuiHandler.java +++ b/src/main/java/electroblob/wizardry/WizardryGuiHandler.java @@ -1,5 +1,10 @@ package electroblob.wizardry; +import cpw.mods.fml.common.network.IGuiHandler; +import electroblob.wizardry.client.GuiArcaneWorkbench; +import electroblob.wizardry.client.GuiPortableCrafting; +import electroblob.wizardry.client.GuiSpellBook; +import electroblob.wizardry.client.GuiWizardHandbook; import electroblob.wizardry.item.ItemSpellBook; import electroblob.wizardry.item.ItemWizardHandbook; import electroblob.wizardry.spell.Spell; @@ -8,9 +13,7 @@ import electroblob.wizardry.tileentity.ContainerPortableWorkbench; import electroblob.wizardry.tileentity.TileEntityArcaneWorkbench; import net.minecraft.entity.player.EntityPlayer; import net.minecraft.tileentity.TileEntity; -import net.minecraft.util.math.BlockPos; import net.minecraft.world.World; -import net.minecraftforge.fml.common.network.IGuiHandler; public class WizardryGuiHandler implements IGuiHandler { @@ -26,13 +29,13 @@ public class WizardryGuiHandler implements IGuiHandler { public Object getServerGuiElement(int id, EntityPlayer player, World world, int x, int y, int z) { if(id == ARCANE_WORKBENCH){ - TileEntity tileEntity = world.getTileEntity(new BlockPos(x, y, z)); + TileEntity tileEntity = world.getTileEntity(x, y, z); if(tileEntity instanceof TileEntityArcaneWorkbench){ return new ContainerArcaneWorkbench(player.inventory, (TileEntityArcaneWorkbench) tileEntity); } } else if(id == PORTABLE_CRAFTING){ - return new ContainerPortableWorkbench(player.inventory, world, new BlockPos(x, y, z)); + return new ContainerPortableWorkbench(player.inventory, world, x, y, z); } return null; } @@ -41,25 +44,19 @@ public class WizardryGuiHandler implements IGuiHandler { public Object getClientGuiElement(int id, EntityPlayer player, World world, int x, int y, int z) { if(id == ARCANE_WORKBENCH){ - TileEntity tileEntity = world.getTileEntity(new BlockPos(x, y, z)); + TileEntity tileEntity = world.getTileEntity(x, y, z); if(tileEntity instanceof TileEntityArcaneWorkbench){ - return new electroblob.wizardry.client.GuiArcaneWorkbench(player.inventory, (TileEntityArcaneWorkbench) tileEntity); + return new GuiArcaneWorkbench(player.inventory, (TileEntityArcaneWorkbench) tileEntity); } } - else if(id == WIZARD_HANDBOOK - && ((player.getHeldItemMainhand() != null && player.getHeldItemMainhand().getItem() instanceof ItemWizardHandbook) - || (player.getHeldItemOffhand() != null && player.getHeldItemOffhand().getItem() instanceof ItemWizardHandbook))){ - return new electroblob.wizardry.client.GuiWizardHandbook(); + else if(id == WIZARD_HANDBOOK && player.getHeldItem().getItem() instanceof ItemWizardHandbook){ + return new GuiWizardHandbook(); } - else if(id == SPELL_BOOK){ - if(player.getHeldItemMainhand() != null && player.getHeldItemMainhand().getItem() instanceof ItemSpellBook){ - return new electroblob.wizardry.client.GuiSpellBook(Spell.get(player.getHeldItemMainhand().getItemDamage())); - }else if(player.getHeldItemOffhand() != null && player.getHeldItemOffhand().getItem() instanceof ItemSpellBook){ - return new electroblob.wizardry.client.GuiSpellBook(Spell.get(player.getHeldItemOffhand().getItemDamage())); - } + else if(id == SPELL_BOOK && player.getHeldItem().getItem() instanceof ItemSpellBook){ + return new GuiSpellBook(Spell.get(player.getHeldItem().getItemDamage())); } else if(id == PORTABLE_CRAFTING){ - return new electroblob.wizardry.client.GuiPortableCrafting(player.inventory, world, new BlockPos(x, y, z)); + return new GuiPortableCrafting(player.inventory, world, x, y, z); } return null; } diff --git a/src/main/java/electroblob/wizardry/WizardryKeyHandler.java b/src/main/java/electroblob/wizardry/WizardryKeyHandler.java index 9cfc7ab9..ec8bc139 100644 --- a/src/main/java/electroblob/wizardry/WizardryKeyHandler.java +++ b/src/main/java/electroblob/wizardry/WizardryKeyHandler.java @@ -2,13 +2,13 @@ package electroblob.wizardry; import org.lwjgl.input.Keyboard; +import cpw.mods.fml.common.eventhandler.SubscribeEvent; +import cpw.mods.fml.common.gameevent.InputEvent; +import cpw.mods.fml.common.network.simpleimpl.IMessage; import electroblob.wizardry.client.ClientProxy; import electroblob.wizardry.packet.PacketControlInput; import electroblob.wizardry.packet.WizardryPacketHandler; import net.minecraft.client.Minecraft; -import net.minecraftforge.fml.common.eventhandler.SubscribeEvent; -import net.minecraftforge.fml.common.gameevent.InputEvent; -import net.minecraftforge.fml.common.network.simpleimpl.IMessage; public class WizardryKeyHandler { @@ -25,7 +25,9 @@ public class WizardryKeyHandler { if(Wizardry.proxy instanceof ClientProxy){ - if(ClientProxy.NEXT_SPELL.isPressed() && Minecraft.getMinecraft().inGameHasFocus){ + ClientProxy proxy = (ClientProxy)Wizardry.proxy; + + if(proxy.nextSpell.isPressed() && Minecraft.getMinecraft().inGameHasFocus){ if(!NkeyPressed){ NkeyPressed = true; }else{ @@ -33,12 +35,12 @@ public class WizardryKeyHandler { } if(!NkeyAlreadyPressed){ // Packet building - IMessage msg = new PacketControlInput.Message(PacketControlInput.ControlType.NEXT_SPELL_KEY); + IMessage msg = new PacketControlInput.Message(1); WizardryPacketHandler.net.sendToServer(msg); } } - if(ClientProxy.PREVIOUS_SPELL.isPressed() && Minecraft.getMinecraft().inGameHasFocus){ + if(proxy.previousSpell.isPressed() && Minecraft.getMinecraft().inGameHasFocus){ if(!BkeyPressed){ BkeyPressed = true; }else{ @@ -46,7 +48,7 @@ public class WizardryKeyHandler { } if(!BkeyAlreadyPressed){ // Packet building - IMessage msg = new PacketControlInput.Message(PacketControlInput.ControlType.PREVIOUS_SPELL_KEY); + IMessage msg = new PacketControlInput.Message(2); WizardryPacketHandler.net.sendToServer(msg); } } diff --git a/src/main/java/electroblob/wizardry/WizardryRegistry.java b/src/main/java/electroblob/wizardry/WizardryRegistry.java new file mode 100644 index 00000000..3df9ae9b --- /dev/null +++ b/src/main/java/electroblob/wizardry/WizardryRegistry.java @@ -0,0 +1,631 @@ +package electroblob.wizardry; + +import java.util.ArrayList; +import java.util.List; + +import cpw.mods.fml.common.registry.EntityRegistry; +import cpw.mods.fml.common.registry.GameRegistry; +import electroblob.wizardry.entity.EntityArc; +import electroblob.wizardry.entity.EntityFallingGrass; +import electroblob.wizardry.entity.EntityMagicSlime; +import electroblob.wizardry.entity.EntityMeteor; +import electroblob.wizardry.entity.EntityShield; +import electroblob.wizardry.entity.construct.EntityArrowRain; +import electroblob.wizardry.entity.construct.EntityBlackHole; +import electroblob.wizardry.entity.construct.EntityBlizzard; +import electroblob.wizardry.entity.construct.EntityBubble; +import electroblob.wizardry.entity.construct.EntityDecay; +import electroblob.wizardry.entity.construct.EntityEarthquake; +import electroblob.wizardry.entity.construct.EntityFireRing; +import electroblob.wizardry.entity.construct.EntityFireSigil; +import electroblob.wizardry.entity.construct.EntityForcefield; +import electroblob.wizardry.entity.construct.EntityFrostSigil; +import electroblob.wizardry.entity.construct.EntityHailstorm; +import electroblob.wizardry.entity.construct.EntityHammer; +import electroblob.wizardry.entity.construct.EntityHealAura; +import electroblob.wizardry.entity.construct.EntityIceSpike; +import electroblob.wizardry.entity.construct.EntityLightningPulse; +import electroblob.wizardry.entity.construct.EntityLightningSigil; +import electroblob.wizardry.entity.construct.EntityTornado; +import electroblob.wizardry.entity.living.EntityBlazeMinion; +import electroblob.wizardry.entity.living.EntityDecoy; +import electroblob.wizardry.entity.living.EntityEvilWizard; +import electroblob.wizardry.entity.living.EntityIceGiant; +import electroblob.wizardry.entity.living.EntityIceWraith; +import electroblob.wizardry.entity.living.EntityLightningWraith; +import electroblob.wizardry.entity.living.EntityPhoenix; +import electroblob.wizardry.entity.living.EntityShadowWraith; +import electroblob.wizardry.entity.living.EntitySilverfishMinion; +import electroblob.wizardry.entity.living.EntitySkeletonMinion; +import electroblob.wizardry.entity.living.EntitySpiderMinion; +import electroblob.wizardry.entity.living.EntitySpiritHorse; +import electroblob.wizardry.entity.living.EntitySpiritWolf; +import electroblob.wizardry.entity.living.EntityStormElemental; +import electroblob.wizardry.entity.living.EntityWizard; +import electroblob.wizardry.entity.living.EntityZombieMinion; +import electroblob.wizardry.entity.projectile.EntityDarknessOrb; +import electroblob.wizardry.entity.projectile.EntityDart; +import electroblob.wizardry.entity.projectile.EntityFirebolt; +import electroblob.wizardry.entity.projectile.EntityFirebomb; +import electroblob.wizardry.entity.projectile.EntityForceArrow; +import electroblob.wizardry.entity.projectile.EntityForceOrb; +import electroblob.wizardry.entity.projectile.EntityIceCharge; +import electroblob.wizardry.entity.projectile.EntityIceLance; +import electroblob.wizardry.entity.projectile.EntityIceShard; +import electroblob.wizardry.entity.projectile.EntityLightningArrow; +import electroblob.wizardry.entity.projectile.EntityLightningDisc; +import electroblob.wizardry.entity.projectile.EntityMagicMissile; +import electroblob.wizardry.entity.projectile.EntityPoisonBomb; +import electroblob.wizardry.entity.projectile.EntitySmokeBomb; +import electroblob.wizardry.entity.projectile.EntitySpark; +import electroblob.wizardry.entity.projectile.EntitySparkBomb; +import electroblob.wizardry.entity.projectile.EntityThunderbolt; +import electroblob.wizardry.spell.*; +import electroblob.wizardry.tileentity.TileEntityArcaneWorkbench; +import electroblob.wizardry.tileentity.TileEntityMagicLight; +import electroblob.wizardry.tileentity.TileEntityPlayerSave; +import electroblob.wizardry.tileentity.TileEntityStatue; +import electroblob.wizardry.tileentity.TileEntityTimer; +import net.minecraft.entity.EnumCreatureType; +import net.minecraft.init.Blocks; +import net.minecraft.init.Items; +import net.minecraft.item.ItemStack; +import net.minecraft.util.WeightedRandomChestContent; +import net.minecraft.world.biome.BiomeGenBase; +import net.minecraftforge.common.ChestGenHooks; +import net.minecraftforge.oredict.OreDictionary; + +public final class WizardryRegistry { + + // Usage note on ChestGenHooks: First number is minimum, second is maximum. Third is rarity, lower numbers are more rare [bread = 100]. + // i starts at 1 here so that 'none' does not spawn. + // Earlier problem solved using clever forge hook - extend WeightedRandomChestContent and override generateChestContent. + // Apparently this seems to be incompatible with roguelike dungeons (any others?), in that it generates the same spell book in + // all of the chests that have a spell book in the same dungeon (usually, though it has been known to do different ones on + // different floors). It also appears to be able to generate the 'none' book, which should be impossible... never mind. + // I don't think that's my fault, or I would have seen a 'none' book in a vanilla dungeon by now. + // Another curious observation: Recurrent complex's generators also seem to always generate the same book/wand/armour + // until you restart the game and can generate the 'none' book as well. + // My best guess as to what's happening is that some mods hook into the dungeon loot differently to others, in that + // they take all the WeightedRandomChestContent objects once and pick from them each time rather than making new ones + // for each chest - which is, in theory, a more efficient way of doing things and will speed up world gen a bit, but + // does of course result in custom chest content implementations behaving strangely. The 'none' book thing remains a + // mystery, however. + + /** Called from the preInit method in the main mod class to register the custom dungeon loot. */ + public static void registerLoot(){ + + // The generate loot option only applies to vanilla chests, not wizard tower chests. + if(Wizardry.generateLoot){ + + String[] chestTypes = { + ChestGenHooks.DUNGEON_CHEST, + ChestGenHooks.PYRAMID_JUNGLE_CHEST, + ChestGenHooks.PYRAMID_DESERT_CHEST, + ChestGenHooks.MINESHAFT_CORRIDOR, + ChestGenHooks.STRONGHOLD_CORRIDOR, + ChestGenHooks.STRONGHOLD_CROSSING, + ChestGenHooks.STRONGHOLD_LIBRARY + }; + + for(String chest : chestTypes){ + + ChestGenHooks.addItem(chest, new WeightedRandomSpellBook(new ItemStack(Wizardry.spellBook), 1, 1, Wizardry.SPELL_BOOK_FREQUENCY)); + ChestGenHooks.addItem(chest, new WeightedRandomSpellBook(new ItemStack(Wizardry.magicWand), 1, 1, Wizardry.WAND_FREQUENCY)); + ChestGenHooks.addItem(chest, new WeightedRandomSpellBook(new ItemStack(Wizardry.wizardHat), 1, 1, Wizardry.ARMOUR_FREQUENCY)); + ChestGenHooks.addItem(chest, new WeightedRandomSpellBook(new ItemStack(Wizardry.scroll), 1, 1, Wizardry.SCROLL_FREQUENCY)); + ChestGenHooks.addItem(chest, new WeightedRandomSpellBook(new ItemStack(Wizardry.condenserUpgrade), 1, 1, Wizardry.WAND_UPGRADE_FREQUENCY)); + ChestGenHooks.addItem(chest, new WeightedRandomChestContent(new ItemStack(Wizardry.magicCrystal), 1, 4, Wizardry.CRYSTAL_FREQUENCY)); + ChestGenHooks.addItem(chest, new WeightedRandomChestContent(new ItemStack(Wizardry.arcaneTome, 1, 1), 1, 1, Wizardry.APPRENTICE_TOME_FREQUENCY)); + ChestGenHooks.addItem(chest, new WeightedRandomChestContent(new ItemStack(Wizardry.arcaneTome, 1, 2), 1, 1, Wizardry.ADVANCED_TOME_FREQUENCY)); + ChestGenHooks.addItem(chest, new WeightedRandomChestContent(new ItemStack(Wizardry.arcaneTome, 1, 3), 1, 1, Wizardry.MASTER_TOME_FREQUENCY)); + ChestGenHooks.addItem(chest, new WeightedRandomChestContent(new ItemStack(Wizardry.armourUpgrade), 1, 1, Wizardry.ARMOUR_UPGRADE_FREQUENCY)); + ChestGenHooks.addItem(chest, new WeightedRandomChestContent(new ItemStack(Wizardry.firebomb), 1, 5, Wizardry.FIREBOMB_FREQUENCY)); + ChestGenHooks.addItem(chest, new WeightedRandomChestContent(new ItemStack(Wizardry.poisonBomb), 1, 5, Wizardry.POISON_BOMB_FREQUENCY)); + ChestGenHooks.addItem(chest, new WeightedRandomChestContent(new ItemStack(Wizardry.smokeBomb), 1, 5, Wizardry.SMOKE_BOMB_FREQUENCY)); + if(Wizardry.discoveryMode) ChestGenHooks.addItem(chest, new WeightedRandomChestContent(new ItemStack(Wizardry.identificationScroll), 1, 1, Wizardry.IDENTIFICATION_SCROLL_FREQUENCY)); + + } + } + + // Creates a new ChestGenHooks for wizard towers. + ChestGenHooks chestGen = ChestGenHooks.getInfo(WizardryWorldGenerator.WIZARD_TOWER); + chestGen.setMax(7); + chestGen.setMin(3); + + // Wizard tower chests use exactly the same things, minus the firebombs, poison bombs and smoke bombs + ChestGenHooks.addItem(WizardryWorldGenerator.WIZARD_TOWER, new WeightedRandomSpellBook(new ItemStack(Wizardry.spellBook), 1, 1, Wizardry.SPELL_BOOK_FREQUENCY)); + ChestGenHooks.addItem(WizardryWorldGenerator.WIZARD_TOWER, new WeightedRandomSpellBook(new ItemStack(Wizardry.magicWand), 1, 1, Wizardry.WAND_FREQUENCY)); + ChestGenHooks.addItem(WizardryWorldGenerator.WIZARD_TOWER, new WeightedRandomSpellBook(new ItemStack(Wizardry.wizardHat), 1, 1, Wizardry.ARMOUR_FREQUENCY)); + ChestGenHooks.addItem(WizardryWorldGenerator.WIZARD_TOWER, new WeightedRandomSpellBook(new ItemStack(Wizardry.scroll), 1, 1, Wizardry.SCROLL_FREQUENCY)); + ChestGenHooks.addItem(WizardryWorldGenerator.WIZARD_TOWER, new WeightedRandomSpellBook(new ItemStack(Wizardry.condenserUpgrade), 1, 1, Wizardry.WAND_UPGRADE_FREQUENCY)); + ChestGenHooks.addItem(WizardryWorldGenerator.WIZARD_TOWER, new WeightedRandomChestContent(new ItemStack(Wizardry.magicCrystal), 1, 4, Wizardry.CRYSTAL_FREQUENCY)); + ChestGenHooks.addItem(WizardryWorldGenerator.WIZARD_TOWER, new WeightedRandomChestContent(new ItemStack(Wizardry.arcaneTome, 1, 1), 1, 1, Wizardry.APPRENTICE_TOME_FREQUENCY)); + ChestGenHooks.addItem(WizardryWorldGenerator.WIZARD_TOWER, new WeightedRandomChestContent(new ItemStack(Wizardry.arcaneTome, 1, 2), 1, 1, Wizardry.ADVANCED_TOME_FREQUENCY)); + ChestGenHooks.addItem(WizardryWorldGenerator.WIZARD_TOWER, new WeightedRandomChestContent(new ItemStack(Wizardry.arcaneTome, 1, 3), 1, 1, Wizardry.MASTER_TOME_FREQUENCY)); + ChestGenHooks.addItem(WizardryWorldGenerator.WIZARD_TOWER, new WeightedRandomChestContent(new ItemStack(Wizardry.armourUpgrade), 1, 1, Wizardry.ARMOUR_UPGRADE_FREQUENCY)); + if(Wizardry.discoveryMode) ChestGenHooks.addItem(WizardryWorldGenerator.WIZARD_TOWER, new WeightedRandomChestContent(new ItemStack(Wizardry.identificationScroll), 1, 1, Wizardry.IDENTIFICATION_SCROLL_FREQUENCY)); + + } + + /* The name defined here in the regitry is the one Minecraft uses in game for commands like /give, as + * opposed to the one defined earlier with setBlockName() or setUnlocalizedName() which is the one used by + * the lang files. Ideally these should both be the same, but I've written the lang file now, so... */ + + /** Called from the preInit method in the main mod class to register all the blocks. */ + public static void registerBlocks(){ + + GameRegistry.registerBlock(Wizardry.arcaneWorkbench, "arcane_workbench"); + GameRegistry.registerBlock(Wizardry.crystalOre, "crystal_ore"); + GameRegistry.registerBlock(Wizardry.petrifiedStone, "petrified_stone"); + GameRegistry.registerBlock(Wizardry.iceStatue, "ice_statue"); + GameRegistry.registerBlock(Wizardry.magicLight, "magic_light"); + GameRegistry.registerBlock(Wizardry.crystalFlower, "crystal_flower"); + GameRegistry.registerBlock(Wizardry.snare, "snare"); + GameRegistry.registerBlock(Wizardry.transportationStone, "transportation_stone"); + GameRegistry.registerBlock(Wizardry.spectralBlock, "spectral_block"); + GameRegistry.registerBlock(Wizardry.crystalBlock, "crystal_block"); + GameRegistry.registerBlock(Wizardry.meteor, "meteor"); + GameRegistry.registerBlock(Wizardry.vanishingCobweb, "vanishing_cobweb"); + } + + /** Called from the preInit method in the main mod class to register all the items. */ + public static void registerItems(){ + + GameRegistry.registerItem(Wizardry.magicCrystal, "magic_crystal"); + + GameRegistry.registerItem(Wizardry.magicWand, "magic_wand"); + GameRegistry.registerItem(Wizardry.apprenticeWand, "apprentice_wand"); + GameRegistry.registerItem(Wizardry.advancedWand, "advanced_wand"); + GameRegistry.registerItem(Wizardry.masterWand, "master_wand"); + + GameRegistry.registerItem(Wizardry.spellBook, "spell_book"); + GameRegistry.registerItem(Wizardry.arcaneTome, "arcane_tome"); + GameRegistry.registerItem(Wizardry.wizardHandbook, "wizard_handbook"); + + GameRegistry.registerItem(Wizardry.basicFireWand, "basic_fire_wand"); + GameRegistry.registerItem(Wizardry.basicIceWand, "basic_ice_wand"); + GameRegistry.registerItem(Wizardry.basicLightningWand, "basic_lightning_wand"); + GameRegistry.registerItem(Wizardry.basicNecromancyWand, "basic_necromancy_wand"); + GameRegistry.registerItem(Wizardry.basicEarthWand, "basic_earth_wand"); + GameRegistry.registerItem(Wizardry.basicSorceryWand, "basic_sorcery_wand"); + GameRegistry.registerItem(Wizardry.basicHealingWand, "basic_healing_wand"); + + GameRegistry.registerItem(Wizardry.apprenticeFireWand, "apprentice_fire_wand"); + GameRegistry.registerItem(Wizardry.apprenticeIceWand, "apprentice_ice_wand"); + GameRegistry.registerItem(Wizardry.apprenticeLightningWand, "apprentice_lightning_wand"); + GameRegistry.registerItem(Wizardry.apprenticeNecromancyWand, "apprentice_necromancy_wand"); + GameRegistry.registerItem(Wizardry.apprenticeEarthWand, "apprentice_earth_wand"); + GameRegistry.registerItem(Wizardry.apprenticeSorceryWand, "apprentice_sorcery_wand"); + GameRegistry.registerItem(Wizardry.apprenticeHealingWand, "apprentice_healing_wand"); + + GameRegistry.registerItem(Wizardry.advancedFireWand, "advanced_fire_wand"); + GameRegistry.registerItem(Wizardry.advancedIceWand, "advanced_ice_wand"); + GameRegistry.registerItem(Wizardry.advancedLightningWand, "advanced_lightning_wand"); + GameRegistry.registerItem(Wizardry.advancedNecromancyWand, "advanced_necromancy_wand"); + GameRegistry.registerItem(Wizardry.advancedEarthWand, "advanced_earth_wand"); + GameRegistry.registerItem(Wizardry.advancedSorceryWand, "advanced_sorcery_wand"); + GameRegistry.registerItem(Wizardry.advancedHealingWand, "advanced_healing_wand"); + + GameRegistry.registerItem(Wizardry.masterFireWand, "master_fire_wand"); + GameRegistry.registerItem(Wizardry.masterIceWand, "master_ice_wand"); + GameRegistry.registerItem(Wizardry.masterLightningWand, "master_lightning_wand"); + GameRegistry.registerItem(Wizardry.masterNecromancyWand, "master_necromancy_wand"); + GameRegistry.registerItem(Wizardry.masterEarthWand, "master_earth_wand"); + GameRegistry.registerItem(Wizardry.masterSorceryWand, "master_sorcery_wand"); + GameRegistry.registerItem(Wizardry.masterHealingWand, "master_healing_wand"); + + GameRegistry.registerItem(Wizardry.spectralSword, "spectral_sword"); + GameRegistry.registerItem(Wizardry.spectralPickaxe, "spectral_pickaxe"); + GameRegistry.registerItem(Wizardry.spectralBow, "spectral_bow"); + + GameRegistry.registerItem(Wizardry.manaFlask, "mana_flask"); + + GameRegistry.registerItem(Wizardry.storageUpgrade, "storage_upgrade"); + GameRegistry.registerItem(Wizardry.siphonUpgrade, "siphon_upgrade"); + GameRegistry.registerItem(Wizardry.condenserUpgrade, "condenser_upgrade"); + GameRegistry.registerItem(Wizardry.rangeUpgrade, "range_upgrade"); + GameRegistry.registerItem(Wizardry.durationUpgrade, "duration_upgrade"); + GameRegistry.registerItem(Wizardry.cooldownUpgrade, "cooldown_upgrade"); + GameRegistry.registerItem(Wizardry.blastUpgrade, "blast_upgrade"); + GameRegistry.registerItem(Wizardry.attunementUpgrade, "attunement_upgrade"); + + GameRegistry.registerItem(Wizardry.flamingAxe, "flaming_axe"); + GameRegistry.registerItem(Wizardry.frostAxe, "frost_axe"); + + GameRegistry.registerItem(Wizardry.firebomb, "firebomb"); + GameRegistry.registerItem(Wizardry.poisonBomb, "poison_bomb"); + + GameRegistry.registerItem(Wizardry.blankScroll, "blank_scroll"); + GameRegistry.registerItem(Wizardry.scroll, "scroll"); + + GameRegistry.registerItem(Wizardry.armourUpgrade, "armour_upgrade"); + + GameRegistry.registerItem(Wizardry.magicSilk, "magic_silk"); + + GameRegistry.registerItem(Wizardry.wizardHat, "wizard_hat"); + GameRegistry.registerItem(Wizardry.wizardRobe, "wizard_robe"); + GameRegistry.registerItem(Wizardry.wizardLeggings, "wizard_leggings"); + GameRegistry.registerItem(Wizardry.wizardBoots, "wizard_boots"); + + GameRegistry.registerItem(Wizardry.wizardHatFire, "wizard_hat_fire"); + GameRegistry.registerItem(Wizardry.wizardRobeFire, "wizard_robe_fire"); + GameRegistry.registerItem(Wizardry.wizardLeggingsFire, "wizard_leggings_fire"); + GameRegistry.registerItem(Wizardry.wizardBootsFire, "wizard_boots_fire"); + + GameRegistry.registerItem(Wizardry.wizardHatIce, "wizard_hat_ice"); + GameRegistry.registerItem(Wizardry.wizardRobeIce, "wizard_robe_ice"); + GameRegistry.registerItem(Wizardry.wizardLeggingsIce, "wizard_leggings_ice"); + GameRegistry.registerItem(Wizardry.wizardBootsIce, "wizard_boots_ice"); + + GameRegistry.registerItem(Wizardry.wizardHatLightning, "wizard_hat_lightning"); + GameRegistry.registerItem(Wizardry.wizardRobeLightning, "wizard_robe_lightning"); + GameRegistry.registerItem(Wizardry.wizardLeggingsLightning, "wizard_leggings_lightning"); + GameRegistry.registerItem(Wizardry.wizardBootsLightning, "wizard_boots_lightning"); + + GameRegistry.registerItem(Wizardry.wizardHatNecromancy, "wizard_hat_necromancy"); + GameRegistry.registerItem(Wizardry.wizardRobeNecromancy, "wizard_robe_necromancy"); + GameRegistry.registerItem(Wizardry.wizardLeggingsNecromancy, "wizard_leggings_necromancy"); + GameRegistry.registerItem(Wizardry.wizardBootsNecromancy, "wizard_boots_necromancy"); + + GameRegistry.registerItem(Wizardry.wizardHatEarth, "wizard_hat_earth"); + GameRegistry.registerItem(Wizardry.wizardRobeEarth, "wizard_robe_earth"); + GameRegistry.registerItem(Wizardry.wizardLeggingsEarth, "wizard_leggings_earth"); + GameRegistry.registerItem(Wizardry.wizardBootsEarth, "wizard_boots_earth"); + + GameRegistry.registerItem(Wizardry.wizardHatSorcery, "wizard_hat_sorcery"); + GameRegistry.registerItem(Wizardry.wizardRobeSorcery, "wizard_robe_sorcery"); + GameRegistry.registerItem(Wizardry.wizardLeggingsSorcery, "wizard_leggings_sorcery"); + GameRegistry.registerItem(Wizardry.wizardBootsSorcery, "wizard_boots_sorcery"); + + GameRegistry.registerItem(Wizardry.wizardHatHealing, "wizard_hat_healing"); + GameRegistry.registerItem(Wizardry.wizardRobeHealing, "wizard_robe_healing"); + GameRegistry.registerItem(Wizardry.wizardLeggingsHealing, "wizard_leggings_healing"); + GameRegistry.registerItem(Wizardry.wizardBootsHealing, "wizard_boots_healing"); + + GameRegistry.registerItem(Wizardry.spawnWizard, "spawn_wizard"); + + GameRegistry.registerItem(Wizardry.spectralHelmet, "spectral_helmet"); + GameRegistry.registerItem(Wizardry.spectralChestplate, "spectral_chestplate"); + GameRegistry.registerItem(Wizardry.spectralLeggings, "spectral_leggings"); + GameRegistry.registerItem(Wizardry.spectralBoots, "spectral_boots"); + + GameRegistry.registerItem(Wizardry.smokeBomb, "smoke_bomb"); + + GameRegistry.registerItem(Wizardry.identificationScroll, "identification_scroll"); + } + + /** Called from the preInit method in the main mod class to register all the tileentities. */ + public static void registerTileEntities(){ + + GameRegistry.registerTileEntity(TileEntityArcaneWorkbench.class, Wizardry.MODID + "ArcaneWorkbenchTileEntity"); + GameRegistry.registerTileEntity(TileEntityStatue.class, Wizardry.MODID + "PetrifiedStoneTileEntity"); + GameRegistry.registerTileEntity(TileEntityMagicLight.class, Wizardry.MODID + "MagicLightTileEntity"); + GameRegistry.registerTileEntity(TileEntityTimer.class, Wizardry.MODID + "TimerTileEntity"); + GameRegistry.registerTileEntity(TileEntityPlayerSave.class, Wizardry.MODID + "TileEntityPlayerSave"); + } + + /** Called from the preInit method in the main mod class to register all the entities. */ + public static void registerEntities(Wizardry wizardry){ + + int id = 0; // Incrementable index for the mod specific entity id. + + EntityRegistry.registerModEntity(EntityZombieMinion.class, "Zombie Minion", id++, wizardry, 128, 3, true); + EntityRegistry.registerModEntity(EntityMagicMissile.class, "Magic Missile", id++, wizardry, 128, 10, true); + EntityRegistry.registerModEntity(EntityArc.class, "Arc", id++, wizardry, 128, 10, false); + EntityRegistry.registerModEntity(EntitySkeletonMinion.class, "Skeleton Minion", id++, wizardry, 128, 3, true); + EntityRegistry.registerModEntity(EntitySparkBomb.class, "Spark Bomb", id++, wizardry, 128, 10, true); + EntityRegistry.registerModEntity(EntitySpiritWolf.class, "Spirit Wolf", id++, wizardry, 128, 3, true); + EntityRegistry.registerModEntity(EntityIceShard.class, "Ice Shard", id++, wizardry, 128, 10, true); + EntityRegistry.registerModEntity(EntityBlazeMinion.class, "Blaze Minion", id++, wizardry, 128, 10, true); + EntityRegistry.registerModEntity(EntityIceWraith.class, "Ice Wraith", id++, wizardry, 128, 10, true); + EntityRegistry.registerModEntity(EntityLightningWraith.class, "Lightning Wraith", id++, wizardry, 128, 10, true); + EntityRegistry.registerModEntity(EntityBlackHole.class, "Black Hole", id++, wizardry, 128, 10, false); + EntityRegistry.registerModEntity(EntityShield.class, "Shield", id++, wizardry, 128, 1, true); + EntityRegistry.registerModEntity(EntityMeteor.class, "Meteor", id++, wizardry, 128, 5, true); + EntityRegistry.registerModEntity(EntityBlizzard.class, "Blizzard", id++, wizardry, 128, 10, false); + EntityRegistry.registerModEntity(EntityWizard.class, "Wizard", id++, wizardry, 128, 3, true); + EntityRegistry.registerModEntity(EntityBubble.class, "Bubble", id++, wizardry, 128, 3, false); + EntityRegistry.registerModEntity(EntityTornado.class, "Tornado", id++, wizardry, 128, 1, false); + EntityRegistry.registerModEntity(EntityHammer.class, "Lightning Hammer", id++, wizardry, 128, 1, true); + EntityRegistry.registerModEntity(EntityFirebomb.class, "Firebomb", id++, wizardry, 128, 10, true); + EntityRegistry.registerModEntity(EntityForceOrb.class, "Force Orb", id++, wizardry, 128, 10, true); + EntityRegistry.registerModEntity(EntityArrowRain.class, "Arrow Rain", id++, wizardry, 128, 10, false); + EntityRegistry.registerModEntity(EntitySpark.class, "Spark", id++, wizardry, 128, 10, true); + EntityRegistry.registerModEntity(EntityShadowWraith.class, "Shadow Wraith", id++, wizardry, 128, 10, true); + EntityRegistry.registerModEntity(EntityDarknessOrb.class, "Darkness Orb", id++, wizardry, 128, 10, true); + EntityRegistry.registerModEntity(EntitySpiderMinion.class, "Spider Minion", id++, wizardry, 128, 3, true); + EntityRegistry.registerModEntity(EntityHealAura.class, "Healing Aura", id++, wizardry, 128, 10, false); + EntityRegistry.registerModEntity(EntityFireSigil.class, "Fire Sigil", id++, wizardry, 128, 10, false); + EntityRegistry.registerModEntity(EntityFrostSigil.class, "Frost Sigil", id++, wizardry, 128, 10, false); + EntityRegistry.registerModEntity(EntityLightningSigil.class, "Lightning Sigil", id++, wizardry, 128, 10, false); + EntityRegistry.registerModEntity(EntityLightningArrow.class, "Lightning Arrow", id++, wizardry, 128, 10, true); + EntityRegistry.registerModEntity(EntityFirebolt.class, "Firebolt", id++, wizardry, 128, 10, true); + EntityRegistry.registerModEntity(EntityPoisonBomb.class, "Poison Bomb", id++, wizardry, 128, 10, true); + EntityRegistry.registerModEntity(EntityIceCharge.class, "Ice Charge", id++, wizardry, 128, 10, true); + EntityRegistry.registerModEntity(EntityForceArrow.class, "Force Arrow", id++, wizardry, 128, 10, true); + EntityRegistry.registerModEntity(EntityDart.class, "Dart", id++, wizardry, 128, 10, true); + EntityRegistry.registerModEntity(EntityMagicSlime.class, "Magic Slime", id++, wizardry, 128, 3, true); + EntityRegistry.registerModEntity(EntityForcefield.class, "Forcefield", id++, wizardry, 128, 10, false); + EntityRegistry.registerModEntity(EntityFireRing.class, "Ring of Fire", id++, wizardry, 128, 10, false); + EntityRegistry.registerModEntity(EntityLightningDisc.class, "Lightning Disc", id++, wizardry, 128, 10, true); + EntityRegistry.registerModEntity(EntityThunderbolt.class, "Thunderbolt", id++, wizardry, 128, 10, true); + EntityRegistry.registerModEntity(EntityIceGiant.class, "Ice Giant", id++, wizardry, 128, 3, true); + EntityRegistry.registerModEntity(EntitySpiritHorse.class, "Spirit Horse", id++, wizardry, 128, 3, true); + EntityRegistry.registerModEntity(EntityPhoenix.class, "Phoenix", id++, wizardry, 128, 3, true); + EntityRegistry.registerModEntity(EntitySilverfishMinion.class, "Silverfish Minion", id++, wizardry, 128, 3, true); + EntityRegistry.registerModEntity(EntityDecay.class, "Decay", id++, wizardry, 128, 10, false); + EntityRegistry.registerModEntity(EntityStormElemental.class, "Storm Elemental", id++, wizardry, 128, 10, true); + EntityRegistry.registerModEntity(EntityEarthquake.class, "Earthquake", id++, wizardry, 128, 10, false); + EntityRegistry.registerModEntity(EntityFallingGrass.class, "Falling Grass", id++, wizardry, 128, 5, true); + EntityRegistry.registerModEntity(EntityIceLance.class, "Ice Lance", id++, wizardry, 128, 10, true); + EntityRegistry.registerModEntity(EntityHailstorm.class, "Hailstorm", id++, wizardry, 128, 10, false); + EntityRegistry.registerModEntity(EntitySmokeBomb.class, "Smoke Bomb", id++, wizardry, 128, 10, true); + EntityRegistry.registerModEntity(EntityEvilWizard.class, "Evil Wizard", id++, wizardry, 128, 3, true); + EntityRegistry.registerModEntity(EntityDecoy.class, "Decoy", id++, wizardry, 128, 3, true); + EntityRegistry.registerModEntity(EntityIceSpike.class, "Ice Spike", id++, wizardry, 128, 1, true); + EntityRegistry.registerModEntity(EntityLightningPulse.class, "Lightning Pulse", id++, wizardry, 128, 10, false); + + List biomes = new ArrayList(); + for(BiomeGenBase biome : BiomeGenBase.getBiomeGenArray()){ + if(biome != null){ + biomes.add(biome); + } + } + // For reference: 5, 1, 1 are the parameters for the witch in vanilla. + EntityRegistry.addSpawn(EntityEvilWizard.class, 3, 1, 1, EnumCreatureType.monster, biomes.toArray(new BiomeGenBase[biomes.size()])); + + } + + /** Static instance of the none spell, used for various purposes. */ + public static Spell none; + /** Static instance of the magic missile spell, used for crafting spell books and by wizards. */ + public static Spell magicMissile; + /** Static instance of the transportation spell, used for chat messages. */ + public static Spell transportation; + /** Static instance of the clairvoyance spell, used for chat messages. */ + public static Spell clairvoyance; + + /** Called from the preInit method in the main mod class to register all the spells in wizardry. */ + public static void registerSpells(){ + + // Wizardry 1.0 spells + + Spell.register(none = new None()); + Spell.register(magicMissile = new MagicMissile()); + Spell.register(new Ignite()); + Spell.register(new Freeze()); + Spell.register(new Snowball()); + Spell.register(new Arc()); + Spell.register(new Thunderbolt()); + Spell.register(new SummonZombie()); + Spell.register(new Snare()); + Spell.register(new Dart()); + Spell.register(new Light()); + Spell.register(new Telekinesis()); + Spell.register(new Heal()); + + Spell.register(new Fireball()); + Spell.register(new FlameRay()); + Spell.register(new Firebomb()); + Spell.register(new FireSigil()); + Spell.register(new Firebolt()); + Spell.register(new FrostRay()); + Spell.register(new SummonSnowGolem()); + Spell.register(new IceShard()); + Spell.register(new IceStatue()); + Spell.register(new FrostSigil()); + Spell.register(new LightningRay()); + Spell.register(new SparkBomb()); + Spell.register(new HomingSpark()); + Spell.register(new LightningSigil()); + Spell.register(new LightningArrow()); + Spell.register(new LifeDrain()); + Spell.register(new SummonSkeleton()); + Spell.register(new Metamorphosis()); + Spell.register(new Wither()); + Spell.register(new Poison()); + Spell.register(new GrowthAura()); + Spell.register(new Bubble()); + Spell.register(new Whirlwind()); + Spell.register(new PoisonBomb()); + Spell.register(new SummonSpiritWolf()); + Spell.register(new Blink()); + Spell.register(new Agility()); + Spell.register(new ConjureSword()); + Spell.register(new ConjurePickaxe()); + Spell.register(new ConjureBow()); + Spell.register(new ForceArrow()); + Spell.register(new Shield()); + Spell.register(new ReplenishHunger()); + Spell.register(new CureEffects()); + Spell.register(new HealAlly()); + + Spell.register(new SummonBlaze()); + Spell.register(new RingOfFire()); + Spell.register(new Detonate()); + Spell.register(new FireResistance()); + Spell.register(new Fireskin()); + Spell.register(new FlamingAxe()); + Spell.register(new Blizzard()); + Spell.register(new SummonIceWraith()); + Spell.register(new IceShroud()); + Spell.register(new IceCharge()); + Spell.register(new FrostAxe()); + Spell.register(new InvokeWeather()); + Spell.register(new ChainLightning()); + Spell.register(new LightningBolt()); + Spell.register(new SummonLightningWraith()); + Spell.register(new StaticAura()); + Spell.register(new LightningDisc()); + Spell.register(new MindControl()); + Spell.register(new SummonWitherSkeleton()); + Spell.register(new Entrapment()); + Spell.register(new WitherSkull()); + Spell.register(new DarknessOrb()); + Spell.register(new ShadowWard()); + Spell.register(new Decay()); + Spell.register(new WaterBreathing()); + Spell.register(new Tornado()); + Spell.register(new Glide()); + Spell.register(new SummonSpiritHorse()); + Spell.register(new SpiderSwarm()); + Spell.register(new Slime()); + Spell.register(new Petrify()); + Spell.register(new Invisibility()); + Spell.register(new Levitation()); + Spell.register(new ForceOrb()); + Spell.register(transportation = new Transportation()); + Spell.register(new SpectralPathway()); + Spell.register(new PhaseStep()); + Spell.register(new VanishingBox()); + Spell.register(new GreaterHeal()); + Spell.register(new HealingAura()); + Spell.register(new Forcefield()); + Spell.register(new Ironflesh()); + Spell.register(new Transience()); + + Spell.register(new Meteor()); + Spell.register(new Firestorm()); + Spell.register(new SummonPhoenix()); + Spell.register(new IceAge()); + Spell.register(new WallOfFrost()); + Spell.register(new SummonIceGiant()); + Spell.register(new Thunderstorm()); + Spell.register(new LightningHammer()); + Spell.register(new PlagueOfDarkness()); + Spell.register(new SummonSkeletonLegion()); + Spell.register(new SummonShadowWraith()); + Spell.register(new ForestsCurse()); + Spell.register(new Flight()); + Spell.register(new SilverfishSwarm()); + Spell.register(new BlackHole()); + Spell.register(new Shockwave()); + Spell.register(new SummonIronGolem()); + Spell.register(new ArrowRain()); + Spell.register(new Diamondflesh()); + Spell.register(new FontOfVitality()); + + // Wizardry 1.1 spells + + Spell.register(new SmokeBomb()); + Spell.register(new MindTrick()); + Spell.register(new Leap()); + + Spell.register(new PocketFurnace()); + Spell.register(new Intimidate()); + Spell.register(new Banish()); + Spell.register(new SixthSense()); + Spell.register(new Darkvision()); + Spell.register(clairvoyance = new Clairvoyance()); + Spell.register(new PocketWorkbench()); + Spell.register(new ImbueWeapon()); + Spell.register(new InvigoratingPresence()); + Spell.register(new Oakflesh()); + + Spell.register(new GreaterFireball()); + Spell.register(new FlamingWeapon()); + Spell.register(new IceLance()); + Spell.register(new FreezingWeapon()); + Spell.register(new IceSpikes()); + Spell.register(new LightningPulse()); + Spell.register(new CurseOfSoulbinding()); + Spell.register(new Cobwebs()); + Spell.register(new Decoy()); + Spell.register(new ArcaneJammer()); + Spell.register(new ConjureArmour()); + Spell.register(new GroupHeal()); + + Spell.register(new Hailstorm()); + Spell.register(new LightningWeb()); + Spell.register(new SummonStormElemental()); + Spell.register(new Earthquake()); + Spell.register(new FontOfMana()); + } + + /** Called from the init method in the main mod class to register all the recipes. */ + public static void registerRecipes(){ + + ItemStack ironIngotStack = new ItemStack(Items.iron_ingot); + ItemStack magicCrystalStack = new ItemStack(Wizardry.magicCrystal); + ItemStack magicWandStack = new ItemStack(Wizardry.magicWand, 1, EnumTier.BASIC.maxCharge); + ItemStack goldNuggetStack = new ItemStack(Items.gold_nugget); + ItemStack stickStack = new ItemStack(Items.stick); + ItemStack bookStack = new ItemStack(Items.book); + ItemStack stringStack = new ItemStack(Items.string); + ItemStack spellBookStack = new ItemStack(Wizardry.spellBook, 1, WizardryRegistry.magicMissile.id()); + ItemStack arcaneWorkbenchStack = new ItemStack(Wizardry.arcaneWorkbench); + ItemStack stoneStack = new ItemStack(Blocks.stone); + ItemStack lapisBlockStack = new ItemStack(Blocks.lapis_block); + ItemStack purpleCarpetStack = new ItemStack(Blocks.carpet, 1, 10); + ItemStack wizardHandbookStack = new ItemStack(Wizardry.wizardHandbook); + ItemStack crystalFlowerStack = new ItemStack(Wizardry.crystalFlower); + ItemStack magicCrystalStack1 = new ItemStack(Wizardry.magicCrystal, 2); + ItemStack magicCrystalStack2 = new ItemStack(Wizardry.magicCrystal, 9); + ItemStack crystalBlockStack = new ItemStack(Wizardry.crystalBlock); + ItemStack manaFlaskStack = new ItemStack(Wizardry.manaFlask); + ItemStack bottleStack = new ItemStack(Items.glass_bottle); + ItemStack gunpowderStack = new ItemStack(Items.gunpowder); + ItemStack blazePowderStack = new ItemStack(Items.blaze_powder); + ItemStack spiderEyeStack = new ItemStack(Items.spider_eye); + // Coal or charcoal is equally fine, hence the wildcard value + ItemStack coalStack = new ItemStack(Items.coal, 1, OreDictionary.WILDCARD_VALUE); + ItemStack firebombStack = new ItemStack(Wizardry.firebomb, 3); + ItemStack poisonBombStack = new ItemStack(Wizardry.poisonBomb, 3); + ItemStack smokeBombStack = new ItemStack(Wizardry.smokeBomb, 3); + ItemStack transportationStoneStack = new ItemStack(Wizardry.transportationStone, 2); + ItemStack silkStack = new ItemStack(Wizardry.magicSilk); + ItemStack silkStack1 = new ItemStack(Wizardry.magicSilk, 2); + ItemStack hatStack = new ItemStack(Wizardry.wizardHat); + ItemStack robeStack = new ItemStack(Wizardry.wizardRobe); + ItemStack leggingsStack = new ItemStack(Wizardry.wizardLeggings); + ItemStack bootsStack = new ItemStack(Wizardry.wizardBoots); + ItemStack scrollStack = new ItemStack(Wizardry.blankScroll); + ItemStack paperStack = new ItemStack(Items.paper); + + GameRegistry.addRecipe(magicWandStack, " x", " y ", "z ", 'x', magicCrystalStack, 'y', stickStack, 'z', goldNuggetStack); + GameRegistry.addRecipe(spellBookStack, " x ", "xyx", " x ", 'x', magicCrystalStack, 'y', bookStack); + GameRegistry.addRecipe(arcaneWorkbenchStack, "vwv", "xyx", "zzz", 'v', goldNuggetStack, 'w', purpleCarpetStack, 'x', magicCrystalStack, 'y', lapisBlockStack, 'z', stoneStack); + GameRegistry.addRecipe(manaFlaskStack, "yyy", "yxy", "yyy", 'x', bottleStack, 'y', magicCrystalStack); + GameRegistry.addRecipe(transportationStoneStack, " x ", "xyx", " x ", 'x', stoneStack, 'y', magicCrystalStack); + GameRegistry.addRecipe(hatStack, "yyy", "y y", 'y', silkStack); + GameRegistry.addRecipe(robeStack, "y y", "yyy", "yyy", 'y', silkStack); + GameRegistry.addRecipe(leggingsStack, "yyy", "y y", "y y", 'y', silkStack); + GameRegistry.addRecipe(bootsStack, "y y", "y y", 'y', silkStack); + GameRegistry.addRecipe(silkStack1, " x ", "xyx", " x ", 'x', stringStack, 'y', magicCrystalStack); + GameRegistry.addRecipe(crystalBlockStack, "zzz", "zzz", "zzz", 'z', magicCrystalStack); + + GameRegistry.addShapelessRecipe(wizardHandbookStack, bookStack, magicCrystalStack); + GameRegistry.addShapelessRecipe(magicCrystalStack1, crystalFlowerStack); + GameRegistry.addShapelessRecipe(magicCrystalStack2, crystalBlockStack); + if(Wizardry.firebombIsCraftable) GameRegistry.addShapelessRecipe(firebombStack, bottleStack, gunpowderStack, blazePowderStack, blazePowderStack); + if(Wizardry.poisonBombIsCraftable) GameRegistry.addShapelessRecipe(poisonBombStack, bottleStack, gunpowderStack, spiderEyeStack, spiderEyeStack); + if(Wizardry.smokeBombIsCraftable) GameRegistry.addShapelessRecipe(smokeBombStack, bottleStack, gunpowderStack, coalStack, coalStack); + if(Wizardry.useAlternateScrollRecipe){ + GameRegistry.addShapelessRecipe(scrollStack, paperStack, stringStack, magicCrystalStack); + }else{ + GameRegistry.addShapelessRecipe(scrollStack, paperStack, stringStack); + } + + + // Mana flask recipes + ItemStack miscWandStack; + + for(int i=0; i getEntitiesWithinRadius(double radius, double x, double y, double z, World world){ + return getEntitiesWithinRadius(radius, x, y, z, world, EntityLivingBase.class); + } + + /** + * Returns all entities of the specified type within the specified radius of the given coordinates. This is different to using + * a raw AABB because a raw AABB will search in a cube volume rather than a sphere. + * Note that this does not exclude any entities; if any specific entities are to be excluded this must be + * checked when iterating through the list. + * @see {@link WizardryUtilities#getEntitiesWithinRadius(double, double, double, double, World)} + * @param radius The search radius + * @param x The x coordinate to search around + * @param y The y coordinate to search around + * @param z The z coordinate to search around + * @param world The world to search in + * @param entityType The class of entity to search for; pass in Entity.class for all entities + */ + public static List getEntitiesWithinRadius(double radius, double x, double y, double z, World world, Class entityType){ + AxisAlignedBB aabb = AxisAlignedBB.getBoundingBox(x - radius, y - radius, z - radius, x + radius, y + radius, z + radius); + List entityList = world.getEntitiesWithinAABB(entityType, aabb); + for(int i=0;i radius){ + entityList.remove(i); + } + } + return entityList; + } + + // ^ Handy syntax using type parameters here! + // I *think* that eclipse, for whatever reason, was not showing an 'unchecked' warning here. If you think about it, + // if this method returns a generic list type I could, in theory, put strings or something in it and then when + // invoking the method store its result as a List, which would cause a ClassCastException. Using + // this handy syntax prevents that from happening. TL;DR: Always use type parameters when available, they're there + // for a reason. Minecraft is bad in this respect because it doesn't use them in places like World. + + /** + * Gets an entity from its UUID. Note that you should check this isn't null. + * @param world The world the entity is in + * @param id The entity's UUID + * @return The Entity that has the given UUID, or null if no such entity exists in the specified world. + */ + public static Entity getEntityByUUID(World world, UUID id){ + for(Object object : world.loadedEntityList){ + if(object instanceof Entity){ + Entity entity = (Entity)object; + // This is a perfect example of where you need to use .equals() and not ==. For most applications, + // this was unnoticeable until world reload because the UUID instance or entity instance is stored. + // Fixed now though. Of course, you could alternatively compare the string representations. + if(entity.getUniqueID().equals(id)){ + return entity; + } + } + } + return null; + } + + /** + * Does a block ray trace (NOT entities) from an entity's eyes (i.e. properly...) + */ + public static MovingObjectPosition rayTrace(double range, World world, EntityLivingBase entity, boolean hitLiquids) + { + Vec3 vec3 = Vec3.createVectorHelper(entity.posX, entity.posY + entity.getEyeHeight(), entity.posZ); + Vec3 vec31 = entity.getLookVec(); + Vec3 vec32 = vec3.addVector(vec31.xCoord * range, vec31.yCoord * range, vec31.zCoord * range); + return world.rayTraceBlocks(vec3, vec32, hitLiquids); + } + + /** + * Helper method which does a rayTrace for entities from an entity's eye level in the direction they are looking + * with a specified range, using the tracePath method. Tidies up the code a bit. Border size defaults to 1. + * + * @param world + * @param entity + * @param range + * @return + */ + public static MovingObjectPosition standardEntityRayTrace(World world, EntityLivingBase entity, double range){ + double dx = entity.getLookVec().xCoord * range; + double dy = entity.getLookVec().yCoord * range; + double dz = entity.getLookVec().zCoord * range; + HashSet hashset = new HashSet(1); + hashset.add(entity); + return WizardryUtilities.tracePath(world, (float)entity.posX, (float)(entity.posY + entity.getEyeHeight()), (float)entity.posZ, (float)(entity.posX + dx), (float)(entity.posY + entity.getEyeHeight() + dy), (float)(entity.posZ + dz), 1.0f, hashset, false); + } + + /** + * Helper method which does a rayTrace for entities from a entity's eye level in the direction they are looking + * with a specified range and radius, using the tracePath method. Tidies up the code a bit. + * + * @param world + * @param entity + * @param range + * @param borderSize + * @return + */ + public static MovingObjectPosition standardEntityRayTrace(World world, EntityLivingBase entity, double range, float borderSize){ + double dx = entity.getLookVec().xCoord * range; + double dy = entity.getLookVec().yCoord * range; + double dz = entity.getLookVec().zCoord * range; + HashSet hashset = new HashSet(1); + hashset.add(entity); + return WizardryUtilities.tracePath(world, (float)entity.posX, (float)(entity.posY + entity.getEyeHeight()), (float)entity.posZ, (float)(entity.posX + dx), (float)(entity.posY + entity.getEyeHeight() + dy), (float)(entity.posZ + dz), borderSize, hashset, false); + } + + /** + * Method for ray tracing entities (the useless default method doesn't work, despite EnumHitType having an ENTITY field...) + * You can also use this for seeking. + * + * @param world + * @param x startX + * @param y startY + * @param z startZ + * @param tx endX + * @param ty endY + * @param tz endZ + * @param borderSize extra area to examine around line for entities + * @param excluded any excluded entities (the player, etc) + * @return a MovingObjectPosition of either the block hit (no entity hit), the entity hit (hit an entity), or null for nothing hit + */ + public static MovingObjectPosition tracePath(World world, float x, float y, float z, float tx, float ty, float tz, float borderSize, HashSet excluded, boolean collideablesOnly){ + + Vec3 startVec = Vec3.createVectorHelper(x, y, z); + Vec3 lookVec = Vec3.createVectorHelper(tx-x, ty-y, tz-z); + Vec3 endVec = Vec3.createVectorHelper(tx, ty, tz); + float minX = x < tx ? x : tx; + float minY = y < ty ? y : ty; + float minZ = z < tz ? z : tz; + float maxX = x > tx ? x : tx; + float maxY = y > ty ? y : ty; + float maxZ = z > tz ? z : tz; + AxisAlignedBB bb = AxisAlignedBB.getBoundingBox(minX, minY, minZ, maxX, maxY, maxZ).expand(borderSize, borderSize, borderSize); + List allEntities = world.getEntitiesWithinAABBExcludingEntity(null, bb); + MovingObjectPosition blockHit = world.rayTraceBlocks(startVec, endVec); + startVec = Vec3.createVectorHelper(x, y, z); + endVec = Vec3.createVectorHelper(tx, ty, tz); + float maxDistance = (float) endVec.distanceTo(startVec); + if(blockHit!=null) + { + maxDistance = (float) blockHit.hitVec.distanceTo(startVec); + } + Entity closestHitEntity = null; + float closestHit = maxDistance; + float currentHit = 0.f; + AxisAlignedBB entityBb;// = ent.getBoundingBox(); + MovingObjectPosition intercept; + for(Entity ent : allEntities) + { + if((ent.canBeCollidedWith() || !collideablesOnly) && ((excluded != null && !excluded.contains(ent)) || excluded == null)) + { + float entBorder = ent.getCollisionBorderSize(); + entityBb = ent.boundingBox; + if(entityBb!=null) + { + entityBb = entityBb.expand(entBorder, entBorder, entBorder); + intercept = entityBb.calculateIntercept(startVec, endVec); + if(intercept!=null) + { + currentHit = (float) intercept.hitVec.distanceTo(startVec); + if(currentHit < closestHit || currentHit==0) + { + closestHit = currentHit; + closestHitEntity = ent; + } + } + } + } + } + if(closestHitEntity!=null) + { + blockHit = new MovingObjectPosition(closestHitEntity); + } + return blockHit; + } + + // Just what benefit does having posY be the eye position on the first person client actually give? + + /** + * Gets the y coordinate of the given player's eyes. This is to cover an inconsistency between the value of + * EntityPlayer.posY on the first person client and everywhere else; in first person (i.e. when + * Minecraft.getMinecraft().thePlayer == player) player.posY is the eye position, but everywhere else it is the + * feet position. This is intended for use when spawning particles, since this is the only situation where the + * discrepancy is likely to matter. + */ + public static double getPlayerEyesPos(EntityPlayer player){ + return Wizardry.proxy.getPlayerEyesPos(player); + } + + /** + * Gets the y coordinate of the given entity's feet. This is to cover an inconsistency between the value of + * EntityPlayer.posY on the first person client and everywhere else; in first person (i.e. when + * Minecraft.getMinecraft().thePlayer == player) player.posY is the eye position, but everywhere else it is the + * feet position. This is intended for use when spawning particles, since this is the only situation where the + * discrepancy is likely to matter. + */ + public static double getEntityFeetPos(Entity entity){ + if(entity instanceof EntityPlayer){ + // For some reason, EntityPlayer.getEyeHeight() always returns 0.12, so I have replicated the behaviour + // of the same method from EntityLivingBase and subtracted that instead. + return getPlayerEyesPos((EntityPlayer)entity) - entity.height*0.85f; + }else{ + return entity.posY; + } + } + + /** + * Verifies that the given string is a valid string representation of a UUID. More specifically, returns true if and + * only if the given string is not null and matches the regular expression:

    + *

    /^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$/

    + * which is the regex equivlent of the standard string representation of a UUID as described in {@link UUID#toString()}. + * This method is intended to be used as a check to prevent an {@link IllegalArgumentException} from occurring when + * calling {@link UUID#fromString(String)}. + * @param string The string to be checked + * @return Whether the given string is a valid string representation of a UUID + */ + public static boolean verifyUUIDString(String string){ + return string != null && string.matches("/^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$/"); + } + + /** + * Returns whether the given target can be attacked by the given attacker. It is up to the caller of this method + * to work out what this means; it doesn't necessarily mean the target is completely immune (for example, revenge + * targeting might reasonably bypass this). This method is intended for use where the damage is indirect and/or + * unavoidable; direct attacks should not check this method. Currently this means the following situations check + * this method: + *

    + * - AI targeting for summoned creatures
    + * - AI targeting for mind-controlled creatures
    + * - Constructs with an area of effect
    + * - Instantaneous spells with an area of effect around the caster (e.g. forest's curse, thunderstorm)
    + * - Any lightning chaining effects
    + * - Any projectiles which seek targets + *

    + * Also note that the friendly fire option is dealt with + * in the event handler. This method acts as a sort of wrapper for all the ADS stuff in {@link ExtendedPlayer}; more + * details about the ally designation system can be found there. + * + * @param attacker The entity that cast the spell originally + * @param target The entity being attacked + * + * @return False under any of the following circumstances, true otherwise: + *

    + * - The target is the attacker (this isn't as stupid as it sounds - anything with an AoE might cause this to be + * true, as can summoned creatures) + *

    + * - The target and the attacker are both players and the target is an ally of the attacker (but the + * attacker need not be an ally of the target) + *

    + * - The target is a creature that was summoned/controlled by the attacker or by an ally of the attacker. + */ + public static boolean isValidTarget(Entity attacker, Entity target){ + + // Don't need to check if either entity is null since instanceof does this anyway. + + // Tests whether the target is the attacker + if(target == attacker) return false; + + // Tests whether the target is a creature that was summoned by the attacker + if(target instanceof EntitySummonedCreature && ((EntitySummonedCreature)target).getCaster() == attacker){ + return false; + } + + // Tests whether the target is a creature that was mind controlled by the attacker + if(target instanceof EntityLiving && ((EntityLivingBase)target).isPotionActive(Wizardry.mindControl)){ + + NBTTagCompound entityNBT = target.getEntityData(); + + if(entityNBT != null && entityNBT.hasKey(MindControl.NBT_KEY)){ + if(attacker == WizardryUtilities.getEntityByUUID(target.worldObj, UUID.fromString(entityNBT.getString(MindControl.NBT_KEY)))){ + return false; + } + } + } + + // Ally section + if(attacker instanceof EntityPlayer && ExtendedPlayer.get((EntityPlayer)attacker) != null){ + + if(target instanceof EntityPlayer){ + // Tests whether the target is an ally of the attacker + if(ExtendedPlayer.get((EntityPlayer)attacker).isPlayerAlly((EntityPlayer)target)){ + return false; + } + + }else if(target instanceof EntitySummonedCreature){ + // Tests whether the target is a creature that was summoned by an ally of the attacker + if(((EntitySummonedCreature)target).getCaster() instanceof EntityPlayer + && ExtendedPlayer.get((EntityPlayer)attacker).isPlayerAlly((EntityPlayer)((EntitySummonedCreature)target).getCaster())){ + return false; + } + + }else if(target instanceof EntityLiving && ((EntityLivingBase)target).isPotionActive(Wizardry.mindControl)){ + // Tests whether the target is a creature that was mind controlled by an ally of the attacker + NBTTagCompound entityNBT = target.getEntityData(); + + if(entityNBT != null && entityNBT.hasKey(MindControl.NBT_KEY)){ + + Entity controller = WizardryUtilities.getEntityByUUID(target.worldObj, UUID.fromString(entityNBT.getString(MindControl.NBT_KEY))); + + if(controller instanceof EntityPlayer && ExtendedPlayer.get((EntityPlayer)attacker).isPlayerAlly((EntityPlayer)controller)){ + return false; + } + } + } + } + + return true; + } + + /** Helper method for testing if the second player is an ally of the first player. Makes the code neater. */ + public static boolean isPlayerAlly(EntityPlayer allyOf, EntityPlayer possibleAlly) { + + ExtendedPlayer properties = ExtendedPlayer.get(allyOf); + + if(properties != null && properties.isPlayerAlly(possibleAlly)) return true; + + return false; + } + + /** + * See {@link WizardryUtilities#getStandardWeightedRandomSpellId(Random, boolean)}. isContinuous defaults to false. + */ + public static int getStandardWeightedRandomSpellId(Random random){ + return getStandardWeightedRandomSpellId(random, false); + } + + /** + * Helper method which gets a spell id according to the standard weighting. The tier is a weighted random value; + * the actual spell within that tier is completely random. Will not return the id of a spell which has been + * disabled in the config. This is for simple stuff like chests and drops; more complex generators like wizard + * trades don't use this method. + *

    + * For reference, the standard weighting is as follows: + * Basic: 60%, Apprentice: 25%, Advanced: 10%, Master: 5% + * + * @param random An instance of the Random class + * @param nonContinuous Whether the spells must be non-continuous (used for scrolls) + * @return A random spell id number + */ + public static int getStandardWeightedRandomSpellId(Random random, boolean nonContinuous){ + + int randomiser = random.nextInt(20); + EnumTier tier; + + if(randomiser < 12){ + tier = EnumTier.BASIC; + }else if(randomiser < 17){ + tier = EnumTier.APPRENTICE; + }else if(randomiser < 19){ + tier = EnumTier.ADVANCED; + }else{ + tier = EnumTier.MASTER; + } + + List spells = Spell.getSpells(new Spell.TierElementFilter(tier, null)); + if(nonContinuous) spells.retainAll(Spell.getSpells(Spell.nonContinuousSpells)); + + // Ensures the tier chosen actually has spells in it, and if not uses BASIC instead. BASIC always has at least + // the NONE spell since this spell cannot be disabled. + if(spells.isEmpty()){ + spells = Spell.getSpells(new Spell.TierElementFilter(EnumTier.BASIC, null)); + if(nonContinuous) spells.retainAll(Spell.getSpells(Spell.nonContinuousSpells)); + } + + // Finds a random spell in the list and returns its id. + return spells.get(random.nextInt(spells.size())).id(); + } + + /** + * Helper method to return the appropriate armour item based on element and slot. + * Currently used to iterate through armour for registering charging recipes and for chest generation. + * @param slot 0 = head, 1 = body, 2 = legs, 3 = feet + */ + public static Item getArmour(EnumElement element, int slot){ + Item armour = null; + if(element == null){ + switch(slot){ + case 0: + armour = Wizardry.wizardHat; + break; + case 1: + armour = Wizardry.wizardRobe; + break; + case 2: + armour = Wizardry.wizardLeggings; + break; + case 3: + armour = Wizardry.wizardBoots; + break; + default: + break; + } + }else{ + switch(element){ + case FIRE: + switch(slot){ + case 0: + armour = Wizardry.wizardHatFire; + break; + case 1: + armour = Wizardry.wizardRobeFire; + break; + case 2: + armour = Wizardry.wizardLeggingsFire; + break; + case 3: + armour = Wizardry.wizardBootsFire; + break; + default: + break; + } + break; + case HEALING: + switch(slot){ + case 0: + armour = Wizardry.wizardHatHealing; + break; + case 1: + armour = Wizardry.wizardRobeHealing; + break; + case 2: + armour = Wizardry.wizardLeggingsHealing; + break; + case 3: + armour = Wizardry.wizardBootsHealing; + break; + default: + break; + } + break; + case ICE: + switch(slot){ + case 0: + armour = Wizardry.wizardHatIce; + break; + case 1: + armour = Wizardry.wizardRobeIce; + break; + case 2: + armour = Wizardry.wizardLeggingsIce; + break; + case 3: + armour = Wizardry.wizardBootsIce; + break; + default: + break; + } + break; + case LIGHTNING: + switch(slot){ + case 0: + armour = Wizardry.wizardHatLightning; + break; + case 1: + armour = Wizardry.wizardRobeLightning; + break; + case 2: + armour = Wizardry.wizardLeggingsLightning; + break; + case 3: + armour = Wizardry.wizardBootsLightning; + break; + default: + break; + } + break; + case NECROMANCY: + switch(slot){ + case 0: + armour = Wizardry.wizardHatNecromancy; + break; + case 1: + armour = Wizardry.wizardRobeNecromancy; + break; + case 2: + armour = Wizardry.wizardLeggingsNecromancy; + break; + case 3: + armour = Wizardry.wizardBootsNecromancy; + break; + default: + break; + } + break; + case SORCERY: + switch(slot){ + case 0: + armour = Wizardry.wizardHatSorcery; + break; + case 1: + armour = Wizardry.wizardRobeSorcery; + break; + case 2: + armour = Wizardry.wizardLeggingsSorcery; + break; + case 3: + armour = Wizardry.wizardBootsSorcery; + break; + default: + break; + } + break; + case EARTH: + switch(slot){ + case 0: + armour = Wizardry.wizardHatEarth; + break; + case 1: + armour = Wizardry.wizardRobeEarth; + break; + case 2: + armour = Wizardry.wizardLeggingsEarth; + break; + case 3: + armour = Wizardry.wizardBootsEarth; + break; + default: + break; + } + break; + default: + switch(slot){ + case 0: + armour = Wizardry.wizardHat; + break; + case 1: + armour = Wizardry.wizardRobe; + break; + case 2: + armour = Wizardry.wizardLeggings; + break; + case 3: + armour = Wizardry.wizardBoots; + break; + default: + break; + } + break; + } + } + return armour; + } + + /** + * Helper method to return the appropriate wand based on tier and element. + * Currently used in the packet handler for upgrading wands, for chest generation and to iterate through + * wands for charging recipes. + */ + public static Item getWand(EnumTier tier, EnumElement element){ + Item wand = null; + if(element == null){ + switch(tier){ + case BASIC: + wand = Wizardry.magicWand; + break; + case APPRENTICE: + wand = Wizardry.apprenticeWand; + break; + case ADVANCED: + wand = Wizardry.advancedWand; + break; + case MASTER: + wand = Wizardry.masterWand; + break; + default: + break; + } + }else{ + switch(element){ + case FIRE: + switch(tier){ + case BASIC: + wand = Wizardry.basicFireWand; + break; + case APPRENTICE: + wand = Wizardry.apprenticeFireWand; + break; + case ADVANCED: + wand = Wizardry.advancedFireWand; + break; + case MASTER: + wand = Wizardry.masterFireWand; + break; + default: + break; + } + break; + case HEALING: + switch(tier){ + case BASIC: + wand = Wizardry.basicHealingWand; + break; + case APPRENTICE: + wand = Wizardry.apprenticeHealingWand; + break; + case ADVANCED: + wand = Wizardry.advancedHealingWand; + break; + case MASTER: + wand = Wizardry.masterHealingWand; + break; + default: + break; + } + break; + case ICE: + switch(tier){ + case BASIC: + wand = Wizardry.basicIceWand; + break; + case APPRENTICE: + wand = Wizardry.apprenticeIceWand; + break; + case ADVANCED: + wand = Wizardry.advancedIceWand; + break; + case MASTER: + wand = Wizardry.masterIceWand; + break; + default: + break; + } + break; + case LIGHTNING: + switch(tier){ + case BASIC: + wand = Wizardry.basicLightningWand; + break; + case APPRENTICE: + wand = Wizardry.apprenticeLightningWand; + break; + case ADVANCED: + wand = Wizardry.advancedLightningWand; + break; + case MASTER: + wand = Wizardry.masterLightningWand; + break; + default: + break; + } + break; + case NECROMANCY: + switch(tier){ + case BASIC: + wand = Wizardry.basicNecromancyWand; + break; + case APPRENTICE: + wand = Wizardry.apprenticeNecromancyWand; + break; + case ADVANCED: + wand = Wizardry.advancedNecromancyWand; + break; + case MASTER: + wand = Wizardry.masterNecromancyWand; + break; + default: + break; + } + break; + case SORCERY: + switch(tier){ + case BASIC: + wand = Wizardry.basicSorceryWand; + break; + case APPRENTICE: + wand = Wizardry.apprenticeSorceryWand; + break; + case ADVANCED: + wand = Wizardry.advancedSorceryWand; + break; + case MASTER: + wand = Wizardry.masterSorceryWand; + break; + default: + break; + } + break; + case EARTH: + switch(tier){ + case BASIC: + wand = Wizardry.basicEarthWand; + break; + case APPRENTICE: + wand = Wizardry.apprenticeEarthWand; + break; + case ADVANCED: + wand = Wizardry.advancedEarthWand; + break; + case MASTER: + wand = Wizardry.masterEarthWand; + break; + default: + break; + } + break; + default: + switch(tier){ + case BASIC: + wand = Wizardry.magicWand; + break; + case APPRENTICE: + wand = Wizardry.apprenticeWand; + break; + case ADVANCED: + wand = Wizardry.advancedWand; + break; + case MASTER: + wand = Wizardry.masterWand; + break; + default: + break; + } + break; + } + } + return wand; + } + +} diff --git a/src/main/java/electroblob/wizardry/WizardryWorldGenerator.java b/src/main/java/electroblob/wizardry/WizardryWorldGenerator.java index 74fdab00..7528aa4a 100644 --- a/src/main/java/electroblob/wizardry/WizardryWorldGenerator.java +++ b/src/main/java/electroblob/wizardry/WizardryWorldGenerator.java @@ -1,43 +1,26 @@ package electroblob.wizardry; -import java.util.HashSet; +import java.util.ArrayList; +import java.util.List; import java.util.Random; -import java.util.Set; - -import org.apache.commons.lang3.ArrayUtils; +import cpw.mods.fml.common.IWorldGenerator; import electroblob.wizardry.entity.living.EntityEvilWizard; import electroblob.wizardry.entity.living.EntityWizard; -import electroblob.wizardry.registry.WizardryBlocks; -import electroblob.wizardry.util.WizardryUtilities; -import net.minecraft.block.BlockChest; -import net.minecraft.block.BlockColored; -import net.minecraft.block.BlockLeaves; +import net.minecraft.block.Block; +import net.minecraft.block.BlockLeavesBase; import net.minecraft.block.BlockLiquid; -import net.minecraft.block.BlockPlanks; -import net.minecraft.block.BlockSlab; -import net.minecraft.block.BlockTorch; -import net.minecraft.block.BlockSlab.EnumBlockHalf; -import net.minecraft.block.state.IBlockState; -import net.minecraft.init.Biomes; import net.minecraft.init.Blocks; import net.minecraft.inventory.IInventory; -import net.minecraft.item.EnumDyeColor; import net.minecraft.item.ItemDoor; -import net.minecraft.util.EnumFacing; -import net.minecraft.util.ResourceLocation; -import net.minecraft.util.math.BlockPos; +import net.minecraft.util.WeightedRandomChestContent; import net.minecraft.world.World; -import net.minecraft.world.WorldServer; -import net.minecraft.world.biome.Biome; -import net.minecraft.world.chunk.IChunkGenerator; +import net.minecraft.world.biome.BiomeGenBase; import net.minecraft.world.chunk.IChunkProvider; import net.minecraft.world.gen.feature.WorldGenMinable; -import net.minecraft.world.storage.loot.LootContext; -import net.minecraft.world.storage.loot.LootTable; import net.minecraftforge.common.BiomeDictionary; +import net.minecraftforge.common.ChestGenHooks; import net.minecraftforge.common.IPlantable; -import net.minecraftforge.fml.common.IWorldGenerator; public class WizardryWorldGenerator implements IWorldGenerator { @@ -45,19 +28,20 @@ public class WizardryWorldGenerator implements IWorldGenerator { public static final String WIZARD_TOWER = Wizardry.MODID + "wizardTower"; @Override - public void generate(Random random, int chunkX, int chunkZ, World world, IChunkGenerator chunkGenerator, IChunkProvider chunkProvider){ + public void generate(Random random, int chunkX, int chunkZ, World world, + IChunkProvider chunkGenerator, IChunkProvider chunkProvider) { - for(int id : Wizardry.settings.oreDimensions){ - if(id == world.provider.getDimension()) this.addOreSpawn(WizardryBlocks.crystal_ore.getDefaultState(), world, random, chunkX * 16, chunkZ * 16, 16, 16, 5, 7, 5, 30); + for(int id : Wizardry.oreDimensions){ + if(id == world.provider.dimensionId) this.addOreSpawn(Wizardry.crystalOre, world, random, chunkX * 16, chunkZ * 16, 16, 16, 5, 7, 5, 30); } - for(int id : Wizardry.settings.flowerDimensions){ - if(id == world.provider.getDimension()) this.generatePlant(WizardryBlocks.crystal_flower.getDefaultState(), world, random, chunkX * 16, chunkZ * 16, 2, 20); + for(int id : Wizardry.flowerDimensions){ + if(id == world.provider.dimensionId) this.generatePlant(Wizardry.crystalFlower, world, random, chunkX * 16, chunkZ * 16, 2, 20); } if(world.getWorldInfo().isMapFeaturesEnabled()){ - for(int id : Wizardry.settings.towerDimensions){ - if(id == world.provider.getDimension()) this.generateWizardTower(world, random, chunkX * 16, chunkZ * 16); + for(int id : Wizardry.towerDimensions){ + if(id == world.provider.dimensionId) this.generateWizardTower(world, random, chunkX * 16, chunkZ * 16); } } } @@ -77,9 +61,9 @@ public class WizardryWorldGenerator implements IWorldGenerator { * @param An int for the minimum Y-Coordinate height at which this block may spawn * @param An int for the maximum Y-Coordinate height at which this block may spawn **/ - public void addOreSpawn(IBlockState state, World world, Random random, int blockXPos, int blockZPos, int maxX, int maxZ, int maxVeinSize, int chancesToSpawn, int minY, int maxY) + public void addOreSpawn(Block block, World world, Random random, int blockXPos, int blockZPos, int maxX, int maxZ, int maxVeinSize, int chancesToSpawn, int minY, int maxY) { - //int maxPossY = minY + (maxY - 1); + int maxPossY = minY + (maxY - 1); assert maxY > minY: "The maximum Y must be greater than the Minimum Y"; assert maxX > 0 && maxX <= 16: "addOreSpawn: The Maximum X must be greater than 0 and less than 16"; assert minY > 0: "addOreSpawn: The Minimum Y must be greater than 0"; @@ -92,7 +76,7 @@ public class WizardryWorldGenerator implements IWorldGenerator { int posX = blockXPos + random.nextInt(maxX); int posY = minY + random.nextInt(diffBtwnMinMaxY); int posZ = blockZPos + random.nextInt(maxZ); - (new WorldGenMinable(state, maxVeinSize)).generate(world, random, new BlockPos(posX, posY, posZ)); + (new WorldGenMinable(block, maxVeinSize)).generate(world, random, posX, posY, posZ); } } @@ -106,8 +90,7 @@ public class WizardryWorldGenerator implements IWorldGenerator { * @param chancesToSpawn Number of chances to spawn a flower patch * @param groupSize The number of times to try generating a flower per flower patch spawn */ - public void generatePlant(IBlockState state, World world, Random random, int x, int z, int chancesToSpawn, int groupSize){ - + public void generatePlant(Block block, World world, Random random, int x, int z, int chancesToSpawn, int groupSize){ for(int i = 0; i < chancesToSpawn; i++){ int randPosX = x + random.nextInt(16); int randPosY = random.nextInt(256); @@ -117,12 +100,10 @@ public class WizardryWorldGenerator implements IWorldGenerator { int i1 = randPosX + random.nextInt(8) - random.nextInt(8); int j1 = randPosY + random.nextInt(4) - random.nextInt(4); int k1 = randPosZ + random.nextInt(8) - random.nextInt(8); - - BlockPos pos = new BlockPos(i1, j1, k1); - if(world.isBlockLoaded(pos) && world.isAirBlock(pos) && (!world.provider.getHasNoSky() || j1 < 127) && state.getBlock().canPlaceBlockOnSide(world, pos, EnumFacing.UP)){ + if(world.blockExists(i1, j1, k1) && world.isAirBlock(i1, j1, k1) && (!world.provider.hasNoSky || j1 < 127) && block.canBlockStay(world, i1, j1, k1)){ - world.setBlockState(pos, state, 2); + world.setBlock(i1, j1, k1, block, 0, 2); } } } @@ -134,20 +115,21 @@ public class WizardryWorldGenerator implements IWorldGenerator { public void generateWizardTower(World world, Random random, int chunkX, int chunkZ){ // Allows the config file to set the rarity value to 0 to disable tower generation completely. - if(Wizardry.settings.towerRarity == 0) return; + if(Wizardry.towerRarity == 0) return; // Compensates for the lack of space in forests. Math.max is required since treeless biomes have treesPerChunk = -999 //double treeFactor = 70 - Math.max((double)world.getBiomeGenForCoords(chunkX, chunkZ).theBiomeDecorator.treesPerChunk, 0) * 1.5d; // Multiplied by 70 to (roughly) retain the old rarity scale - if(random.nextInt((int)(Wizardry.settings.towerRarity * 70)) == 0){ - - BlockPos origin = new BlockPos(chunkX + random.nextInt(16), 0, chunkZ + random.nextInt(16)); + if(random.nextInt((int)(Wizardry.towerRarity * 70)) == 0){ + + int posX = chunkX + random.nextInt(16); + int posZ = chunkZ + random.nextInt(16); // Despite what its name suggests, this method does not return the position of a liquid. It is in fact // exactly what is needed here since it is used for placing villages and stuff, and doesn't include leaves // or other foliage. - origin = origin.up(world.getTopSolidOrLiquidBlock(origin).getY() - 1); + int posY = world.getTopSolidOrLiquidBlock(posX, posZ) - 1; int[][][] towerBlueprint = towerBlueprintSmall; @@ -159,82 +141,140 @@ public class WizardryWorldGenerator implements IWorldGenerator { } // 0 = West, 1 = North, 2 = East, 3 = South (The way you would face when walking out of the door) - EnumFacing orientation = EnumFacing.getHorizontal(random.nextInt(4)); + int orientation = random.nextInt(4); boolean flip = random.nextBoolean(); - - if(checkSpaceForTower(world, origin, towerBlueprint, orientation, flip)){ - // == Setup == - + /* It seems there is something specific about sand which makes it very unlikely (but NOT impossible) for + * the towers to generate on it... + * Edit: The culprit was canBlockSeeTheSky, so I've taken that out and replaced it with getTopSolidOrLiquidBlock + * (which also greatly increases the efficiency of the generator since it doesn't even try underground blocks). + * In addition, I have added a check for solid blocks in the way of the tower; see checkForSpace. */ + + // Debugging + //System.out.println("Tried to generate wizard tower at (" + posX + ", " + posY + ", " + posZ + "); Biome: " + // + world.getBiomeGenForCoords(posX, posZ).biomeName + ", Can block see sky: " + // + world.canBlockSeeTheSky(posX, posY, posZ) + ", Is block normal cube: " + // + world.isBlockNormalCubeDefault(posX, posY, posZ, false) + ", Block: " + // + world.getBlock(posX, posY, posZ).getLocalizedName()); + + // These conditions are no longer necessary thanks to world.getTopSolidOrLiquidBlock + //if((world.canBlockSeeTheSky(posX, posY, posZ) || world.getBlock(posX, posY, posZ) == Blocks.grass) && + //if(!world.isAirBlock(posX, posY, posZ) && world.isBlockNormalCubeDefault(posX, posY, posZ, false) && + + if(checkSpaceForTower(world, posX, posY, posZ, towerBlueprint, orientation, flip)){ + boolean evilWizard = random.nextInt(5) == 0; - IBlockState wallMaterial = Blocks.COBBLESTONE.getDefaultState(); - BlockPlanks.EnumType woodType = BlockPlanks.EnumType.OAK; - - Biome biome = world.getBiome(origin); + Block wallMaterial = Blocks.cobblestone; + int woodType = 0; + BiomeGenBase biome = world.getBiomeGenForCoords(posX, posZ); // The order of these is somewhat important in that biomes can be many types, so the last of these checks - // that is true gets priority. Generally speaking, the later the check, the more specific it is. - if(BiomeDictionary.isBiomeOfType(biome, BiomeDictionary.Type.DENSE)) wallMaterial = Blocks.MOSSY_COBBLESTONE.getDefaultState(); - if(BiomeDictionary.isBiomeOfType(biome, BiomeDictionary.Type.SWAMP)) wallMaterial = Blocks.MOSSY_COBBLESTONE.getDefaultState(); - if(BiomeDictionary.isBiomeOfType(biome, BiomeDictionary.Type.SANDY)) wallMaterial = Blocks.SANDSTONE.getDefaultState(); - if(BiomeDictionary.isBiomeOfType(biome, BiomeDictionary.Type.NETHER)) wallMaterial = Blocks.NETHER_BRICK.getDefaultState(); - if(BiomeDictionary.isBiomeOfType(biome, BiomeDictionary.Type.MOUNTAIN)) wallMaterial = Blocks.STONEBRICK.getDefaultState(); - if(BiomeDictionary.isBiomeOfType(biome, BiomeDictionary.Type.MESA)) wallMaterial = Blocks.HARDENED_CLAY.getDefaultState(); + // that is true gets priority + if(BiomeDictionary.isBiomeOfType(biome, BiomeDictionary.Type.DENSE)) wallMaterial = Blocks.mossy_cobblestone; + if(BiomeDictionary.isBiomeOfType(biome, BiomeDictionary.Type.SWAMP)) wallMaterial = Blocks.mossy_cobblestone; + if(BiomeDictionary.isBiomeOfType(biome, BiomeDictionary.Type.SANDY)) wallMaterial = Blocks.sandstone; + if(BiomeDictionary.isBiomeOfType(biome, BiomeDictionary.Type.NETHER)) wallMaterial = Blocks.nether_brick; + if(BiomeDictionary.isBiomeOfType(biome, BiomeDictionary.Type.MOUNTAIN)) wallMaterial = Blocks.stonebrick; + if(BiomeDictionary.isBiomeOfType(biome, BiomeDictionary.Type.MESA)) wallMaterial = Blocks.hardened_clay; // Unfortunately, I can't check all the wood types with the biome dictionary - if(BiomeDictionary.isBiomeOfType(biome, BiomeDictionary.Type.CONIFEROUS)) woodType = BlockPlanks.EnumType.SPRUCE; - if(biome == Biomes.BIRCH_FOREST || biome == Biomes.BIRCH_FOREST_HILLS) woodType = BlockPlanks.EnumType.BIRCH; - if(BiomeDictionary.isBiomeOfType(biome, BiomeDictionary.Type.JUNGLE)) woodType = BlockPlanks.EnumType.JUNGLE; - if(BiomeDictionary.isBiomeOfType(biome, BiomeDictionary.Type.SAVANNA)) woodType = BlockPlanks.EnumType.ACACIA; + if(BiomeDictionary.isBiomeOfType(biome, BiomeDictionary.Type.CONIFEROUS)) woodType = 1; + if(biome == BiomeGenBase.birchForest || biome == BiomeGenBase.birchForestHills) woodType = 2; + if(BiomeDictionary.isBiomeOfType(biome, BiomeDictionary.Type.JUNGLE)) woodType = 3; + if(BiomeDictionary.isBiomeOfType(biome, BiomeDictionary.Type.SAVANNA)) woodType = 4; // Not technically a tree type, but I think it fits quite well anyway - if(BiomeDictionary.isBiomeOfType(biome, BiomeDictionary.Type.SPOOKY)) woodType = BlockPlanks.EnumType.DARK_OAK; - - IBlockState[] blockStateList = new IBlockState[]{ + if(BiomeDictionary.isBiomeOfType(biome, BiomeDictionary.Type.SPOOKY)) woodType = 5; + + Block[] blockList = new Block[]{ null, - Blocks.AIR.getDefaultState(), - Blocks.PLANKS.getDefaultState().withProperty(BlockPlanks.VARIANT, woodType), - Blocks.BOOKSHELF.getDefaultState(), - Blocks.STAINED_HARDENED_CLAY.getDefaultState().withProperty(BlockColored.COLOR, - EnumDyeColor.values()[random.nextInt(EnumDyeColor.values().length)]), - Blocks.WOODEN_SLAB.getDefaultState().withProperty(BlockSlab.HALF, EnumBlockHalf.BOTTOM), - Blocks.WOODEN_SLAB.getDefaultState().withProperty(BlockSlab.HALF, EnumBlockHalf.TOP), + Blocks.air, + Blocks.planks, + Blocks.bookshelf, + Blocks.stained_hardened_clay, + Blocks.wooden_slab, + Blocks.wooden_slab, wallMaterial, - Blocks.GLASS_PANE.getDefaultState(), - Blocks.OAK_DOOR.getDefaultState(), - WizardryBlocks.arcane_workbench.getDefaultState(), - Blocks.TORCH.getDefaultState(), - evilWizard ? Blocks.CHEST.getDefaultState() : Blocks.BOOKSHELF.getDefaultState() + Blocks.glass_pane, + Blocks.wooden_door, + Wizardry.arcaneWorkbench, + Blocks.torch, + evilWizard ? Blocks.chest : Blocks.bookshelf }; - Set blocksPlaced = new HashSet(); - - // == Foundations == + int[] metadataList = new int[]{0, 0, woodType, 0, random.nextInt(15), woodType, woodType + 8, 0, 0, 0, 0, 0, 0}; + + List blocksPlaced = new ArrayList(); // Fills in foundations. This is done first so the door always has something to be placed on. boolean flag = true; - - // BlockPos is immutable, so I'm not sure if simply saying pos1 = pos will be sufficient. - BlockPos layerCentre = new BlockPos(origin); + int y1 = 0; while(flag){ - flag = false; - - for(BlockPos offset : foundationLayer){ - if(!world.isBlockNormalCube(layerCentre.add(offset), false)){ - world.setBlockState(layerCentre.add(offset), wallMaterial); - blocksPlaced.add(layerCentre.add(offset)); - // Keeps going as long as something was filled in. - flag = true; - } + if(!world.isBlockNormalCubeDefault(posX - 2, posY + y1, posZ - 1, false)){ + world.setBlock(posX - 2, posY + y1, posZ - 1, wallMaterial); + blocksPlaced.add(new int[]{posX - 2, posY + y1, posZ - 1}); + flag = true; } - - layerCentre = layerCentre.down(); + if(!world.isBlockNormalCubeDefault(posX - 2, posY + y1, posZ, false)){ + world.setBlock(posX - 2, posY + y1, posZ, wallMaterial); + blocksPlaced.add(new int[]{posX - 2, posY + y1, posZ}); + flag = true; + } + if(!world.isBlockNormalCubeDefault(posX - 2, posY + y1, posZ + 1, false)){ + world.setBlock(posX - 2, posY + y1, posZ + 1, wallMaterial); + blocksPlaced.add(new int[]{posX - 2, posY + y1, posZ + 1}); + flag = true; + } + if(!world.isBlockNormalCubeDefault(posX + 2, posY + y1, posZ - 1, false)){ + world.setBlock(posX + 2, posY + y1, posZ - 1, wallMaterial); + blocksPlaced.add(new int[]{posX + 2, posY + y1, posZ - 1}); + flag = true; + } + if(!world.isBlockNormalCubeDefault(posX + 2, posY + y1, posZ, false)){ + world.setBlock(posX + 2, posY + y1, posZ, wallMaterial); + blocksPlaced.add(new int[]{posX + 2, posY + y1, posZ}); + flag = true; + } + if(!world.isBlockNormalCubeDefault(posX + 2, posY + y1, posZ + 1, false)){ + world.setBlock(posX + 2, posY + y1, posZ + 1, wallMaterial); + blocksPlaced.add(new int[]{posX + 2, posY + y1, posZ + 1}); + flag = true; + } + if(!world.isBlockNormalCubeDefault(posX - 1, posY + y1, posZ - 2, false)){ + world.setBlock(posX - 1, posY + y1, posZ - 2, wallMaterial); + blocksPlaced.add(new int[]{posX - 1, posY + y1, posZ - 2}); + flag = true; + } + if(!world.isBlockNormalCubeDefault(posX, posY + y1, posZ - 2, false)){ + world.setBlock(posX, posY + y1, posZ - 2, wallMaterial); + blocksPlaced.add(new int[]{posX, posY + y1, posZ - 2}); + flag = true; + } + if(!world.isBlockNormalCubeDefault(posX + 1, posY + y1, posZ - 2, false)){ + world.setBlock(posX + 1, posY + y1, posZ - 2, wallMaterial); + blocksPlaced.add(new int[]{posX + 1, posY + y1, posZ - 2}); + flag = true; + } + if(!world.isBlockNormalCubeDefault(posX - 1, posY + y1, posZ + 2, false)){ + world.setBlock(posX - 1, posY + y1, posZ + 2, wallMaterial); + blocksPlaced.add(new int[]{posX - 1, posY + y1, posZ + 2}); + flag = true; + } + if(!world.isBlockNormalCubeDefault(posX, posY + y1, posZ + 2, false)){ + world.setBlock(posX, posY + y1, posZ + 2, wallMaterial); + blocksPlaced.add(new int[]{posX, posY + y1, posZ + 2}); + flag = true; + } + if(!world.isBlockNormalCubeDefault(posX + 1, posY + y1, posZ + 2, false)){ + world.setBlock(posX + 1, posY + y1, posZ + 2, wallMaterial); + blocksPlaced.add(new int[]{posX + 1, posY + y1, posZ + 2}); + flag = true; + } + y1--; } - // == Main Structure == - // It is assumed that the width of the blueprint is the same all the way up, and that the layers are square. int width = towerBlueprint[0].length-1; @@ -243,52 +283,46 @@ public class WizardryWorldGenerator implements IWorldGenerator { int x1 = 0, z1 = 0; + // Main structure for(int y=0; y 3 || world.getBlockState(pos1).getBlock() instanceof BlockLiquid)){ + if(towerBlueprint[y][z1][x1] != 0 && !WizardryUtilities.canBlockBeReplacedB(world, posX + x - width/2, posY + y, posZ + z - width/2) + && !(world.getBlock(posX + x - width/2, posY + y, posZ + z - width/2) instanceof IPlantable) + && !(world.getBlock(posX + x - width/2, posY + y, posZ + z - width/2) instanceof BlockLeavesBase) + && (y > 3 || world.getBlock(posX + x - width/2, posY + y, posZ + z - width/2) instanceof BlockLiquid)){ return false; } } @@ -465,22 +440,6 @@ public class WizardryWorldGenerator implements IWorldGenerator { return true; } - - /** Array of relative positions of each block in a layer of foundations. */ - private static final BlockPos[] foundationLayer = new BlockPos[]{ - new BlockPos(-2, 0, -1), - new BlockPos(-2, 0, 0), - new BlockPos(-2, 0, 1), - new BlockPos(2, 0, -1), - new BlockPos(2, 0, 0), - new BlockPos(2, 0, 1), - new BlockPos(-1, 0, -2), - new BlockPos(0, 0, -2), - new BlockPos(1, 0, -2), - new BlockPos(-1, 0, 2), - new BlockPos(0, 0, 2), - new BlockPos(1, 0, 2), - }; /** * 3D matrix of integers representing the different blocks which make up the wizard tower. The blocks corresponding diff --git a/src/main/java/electroblob/wizardry/block/BlockArcaneWorkbench.java b/src/main/java/electroblob/wizardry/block/BlockArcaneWorkbench.java index 4a0c3f8f..06b0ba97 100644 --- a/src/main/java/electroblob/wizardry/block/BlockArcaneWorkbench.java +++ b/src/main/java/electroblob/wizardry/block/BlockArcaneWorkbench.java @@ -4,36 +4,49 @@ import java.util.Random; import electroblob.wizardry.Wizardry; import electroblob.wizardry.WizardryGuiHandler; +import electroblob.wizardry.client.GuiArcaneWorkbench; import electroblob.wizardry.tileentity.TileEntityArcaneWorkbench; +import net.minecraft.block.Block; import net.minecraft.block.BlockContainer; import net.minecraft.block.material.Material; -import net.minecraft.block.state.IBlockState; +import net.minecraft.client.renderer.texture.IIconRegister; +import net.minecraft.creativetab.CreativeTabs; import net.minecraft.entity.item.EntityItem; import net.minecraft.entity.player.EntityPlayer; import net.minecraft.item.ItemStack; import net.minecraft.nbt.NBTTagCompound; import net.minecraft.tileentity.TileEntity; -import net.minecraft.util.EnumBlockRenderType; -import net.minecraft.util.EnumFacing; -import net.minecraft.util.EnumHand; -import net.minecraft.util.math.AxisAlignedBB; -import net.minecraft.util.math.BlockPos; -import net.minecraft.world.IBlockAccess; +import net.minecraft.util.IIcon; import net.minecraft.world.World; -public class BlockArcaneWorkbench extends BlockContainer { +public class BlockArcaneWorkbench extends BlockContainer{ - private static final AxisAlignedBB AABB = new AxisAlignedBB(0.0D, 0.0D, 0.0D, 1.0D, 0.75D, 1.0D); + public IIcon[] icons = new IIcon[6]; public BlockArcaneWorkbench(){ - super(Material.ROCK); + super(Material.rock); + this.setBlockBounds(0.0F, 0.0F, 0.0F, 1.0F, 0.75F, 1.0F); this.setLightLevel(0.8f); + this.setCreativeTab(CreativeTabs.tabDecorations); + this.setBlockTextureName("wizardry:arcane_workbench"); + } + + @Override + public void registerBlockIcons(IIconRegister par1IconRegister) { + this.icons[0] = par1IconRegister.registerIcon(this.textureName + "_bottom"); + this.icons[1] = par1IconRegister.registerIcon(this.textureName + "_top"); + this.icons[2] = par1IconRegister.registerIcon(this.textureName + "_side"); + this.icons[3] = par1IconRegister.registerIcon(this.textureName + "_side"); + this.icons[4] = par1IconRegister.registerIcon(this.textureName + "_side"); + this.icons[5] = par1IconRegister.registerIcon(this.textureName + "_side"); } + + @Override - public AxisAlignedBB getBoundingBox(IBlockState state, IBlockAccess source, BlockPos pos){ - return AABB; - } + public IIcon getIcon(int side, int meta) { + return this.icons[side]; + } @Override public TileEntity createNewTileEntity(World world, int metadata) { @@ -41,39 +54,32 @@ public class BlockArcaneWorkbench extends BlockContainer { } @Override - public boolean isNormalCube(IBlockState state, IBlockAccess world, BlockPos pos) { - return false; - } - + public boolean renderAsNormalBlock() + { + return false; + } + @Override - public EnumBlockRenderType getRenderType(IBlockState state) { - return EnumBlockRenderType.MODEL; - } - + public boolean isOpaqueCube() + { + return false; + } + @Override - public boolean isOpaqueCube(IBlockState state) { - return false; - } - - @Override - public boolean onBlockActivated(World world, BlockPos pos, IBlockState block, EntityPlayer player, - EnumHand hand, ItemStack heldItem, EnumFacing side, float hitX, float hitY, float hitZ){ - - TileEntity tileEntity = world.getTileEntity(pos); - - if(tileEntity == null || player.isSneaking()){ + public boolean onBlockActivated(World world, int x, int y, int z, EntityPlayer player, int metadata, float what, float these, float are) { + TileEntity tileEntity = world.getTileEntity(x, y, z); + if (tileEntity == null || player.isSneaking()) { return false; } - - player.openGui(Wizardry.instance, WizardryGuiHandler.ARCANE_WORKBENCH, world, pos.getX(), pos.getY(), pos.getZ()); + player.openGui(Wizardry.instance, WizardryGuiHandler.ARCANE_WORKBENCH, world, x, y, z); return true; } @Override - public void breakBlock(World world, BlockPos pos, IBlockState block) + public void breakBlock(World par1World, int par2, int par3, int par4, Block block, int par6) { Random random = new Random(); - TileEntityArcaneWorkbench tileentityarcaneworkbench = (TileEntityArcaneWorkbench)world.getTileEntity(pos); + TileEntityArcaneWorkbench tileentityarcaneworkbench = (TileEntityArcaneWorkbench)par1World.getTileEntity(par2, par3, par4); if (tileentityarcaneworkbench != null) { @@ -87,7 +93,7 @@ public class BlockArcaneWorkbench extends BlockContainer { float f1 = random.nextFloat() * 0.8F + 0.1F; EntityItem entityitem; - for (float f2 = random.nextFloat() * 0.8F + 0.1F; itemstack.stackSize > 0; world.spawnEntityInWorld(entityitem)) + for (float f2 = random.nextFloat() * 0.8F + 0.1F; itemstack.stackSize > 0; par1World.spawnEntityInWorld(entityitem)) { int k1 = random.nextInt(21) + 10; @@ -97,7 +103,7 @@ public class BlockArcaneWorkbench extends BlockContainer { } itemstack.stackSize -= k1; - entityitem = new EntityItem(world, (double)((float)pos.getX() + f), (double)((float)pos.getY() + f1), (double)((float)pos.getZ() + f2), new ItemStack(itemstack.getItem(), k1, itemstack.getItemDamage())); + entityitem = new EntityItem(par1World, (double)((float)par2 + f), (double)((float)par3 + f1), (double)((float)par4 + f2), new ItemStack(itemstack.getItem(), k1, itemstack.getItemDamage())); float f3 = 0.05F; entityitem.motionX = (double)((float)random.nextGaussian() * f3); entityitem.motionY = (double)((float)random.nextGaussian() * f3 + 0.2F); @@ -105,7 +111,7 @@ public class BlockArcaneWorkbench extends BlockContainer { if (itemstack.hasTagCompound()) { - entityitem.getEntityItem().setTagCompound(((NBTTagCompound)itemstack.getTagCompound().copy())); + entityitem.getEntityItem().setTagCompound((NBTTagCompound)itemstack.getTagCompound().copy()); } } } @@ -113,8 +119,8 @@ public class BlockArcaneWorkbench extends BlockContainer { //par1World.func_96440_m(par2, par3, par4, block); } - - super.breakBlock(world, pos, block); + + super.breakBlock(par1World, par2, par3, par4, block, par6); } } diff --git a/src/main/java/electroblob/wizardry/block/BlockCrystal.java b/src/main/java/electroblob/wizardry/block/BlockCrystal.java new file mode 100644 index 00000000..7488b2c2 --- /dev/null +++ b/src/main/java/electroblob/wizardry/block/BlockCrystal.java @@ -0,0 +1,16 @@ +package electroblob.wizardry.block; + +import java.util.Random; + +import electroblob.wizardry.Wizardry; +import net.minecraft.block.Block; +import net.minecraft.block.material.Material; +import net.minecraft.item.Item; + +public class BlockCrystal extends Block { + + public BlockCrystal(Material material) { + super(material); + setHarvestLevel("pickaxe", 2); + } +} diff --git a/src/main/java/electroblob/wizardry/block/BlockCrystalFlower.java b/src/main/java/electroblob/wizardry/block/BlockCrystalFlower.java index 9aded57a..7d01265c 100644 --- a/src/main/java/electroblob/wizardry/block/BlockCrystalFlower.java +++ b/src/main/java/electroblob/wizardry/block/BlockCrystalFlower.java @@ -2,67 +2,142 @@ package electroblob.wizardry.block; import java.util.Random; +import electroblob.wizardry.EnumParticleType; import electroblob.wizardry.Wizardry; -import electroblob.wizardry.registry.WizardryBlocks; -import electroblob.wizardry.util.WizardryParticleType; -import net.minecraft.block.BlockBush; -import net.minecraft.block.SoundType; +import net.minecraft.block.Block; import net.minecraft.block.material.Material; -import net.minecraft.block.state.IBlockState; import net.minecraft.init.Blocks; -import net.minecraft.util.math.AxisAlignedBB; -import net.minecraft.util.math.BlockPos; +import net.minecraft.util.AxisAlignedBB; import net.minecraft.world.IBlockAccess; import net.minecraft.world.World; import net.minecraftforge.common.EnumPlantType; -import net.minecraftforge.event.entity.player.BonemealEvent; -import net.minecraftforge.fml.common.Mod; -import net.minecraftforge.fml.common.eventhandler.SubscribeEvent; +import net.minecraftforge.common.IPlantable; +import net.minecraftforge.common.util.ForgeDirection; -// Extending BlockBush allows me to remove nearly everything from this class. -@Mod.EventBusSubscriber -public class BlockCrystalFlower extends BlockBush { - - private static final AxisAlignedBB AABB = new AxisAlignedBB(0.5F - 0.2f, 0.0F, 0.5F - 0.2f, 0.5F + 0.2f, 0.2f * 3.0F, 0.5F + 0.2f); +public class BlockCrystalFlower extends Block implements IPlantable { public BlockCrystalFlower(Material par2Material) { super(par2Material); this.setLightLevel(0.5f); + this.setBlockBounds(0.5F - 0.2f, 0.0F, 0.5F - 0.2f, 0.5F + 0.2f, 0.2f * 3.0F, 0.5F + 0.2f); this.setTickRandomly(true); - this.setSoundType(SoundType.PLANT); } @Override - public AxisAlignedBB getBoundingBox(IBlockState state, IBlockAccess source, BlockPos pos) - { - return AABB; - } - - @Override - public void randomDisplayTick(IBlockState state, World world, BlockPos pos, Random random){ + public void randomDisplayTick(World world, int x, int y, int z, Random random){ if(world.isRemote && random.nextBoolean()){ - Wizardry.proxy.spawnParticle(WizardryParticleType.SPARKLE, world, pos.getX()+random.nextDouble(), pos.getY()+random.nextDouble()/2+0.5, pos.getZ()+random.nextDouble(), 0d, 0.01, 0d, 20 + random.nextInt(10), 0.5f + (random.nextFloat()/2), 0.5f + (random.nextFloat()/2), 0.5f + (random.nextFloat()/2)); + Wizardry.proxy.spawnParticle(EnumParticleType.SPARKLE, world, x+random.nextDouble(), y+random.nextDouble()/2+0.5, z+random.nextDouble(), 0d, 0.01, 0d, 20 + random.nextInt(10), 0.5f + (random.nextFloat()/2), 0.5f + (random.nextFloat()/2), 0.5f + (random.nextFloat()/2)); } } + + /** + * Checks to see if its valid to put this block at the specified coordinates. Args: world, x, y, z + */ + @Override + public boolean canPlaceBlockAt(World par1World, int par2, int par3, int par4) + { + return super.canPlaceBlockAt(par1World, par2, par3, par4) && canBlockStay(par1World, par2, par3, par4); + } + + /** + * Returns a bounding box from the pool of bounding boxes (this means this box can change after the pool has been + * cleared to be reused) + */ + @Override + public AxisAlignedBB getCollisionBoundingBoxFromPool(World par1World, int par2, int par3, int par4) + { + return null; + } + + /** + * Gets passed in the blockID of the block below and supposed to return true if its allowed to grow on the type of + * blockID passed in. Args: blockID + */ + protected boolean canThisPlantGrowOnThisBlockID(Block par1) + { + return par1 == Blocks.grass || par1 == Blocks.dirt || par1 == Blocks.farmland; + } + + /** + * Lets the block know when one of its neighbor changes. Doesn't know which neighbor changed (coordinates passed are + * their own) Args: x, y, z, neighbor blockID + */ + @Override + public void onNeighborBlockChange(World par1World, int par2, int par3, int par4, Block par5) + { + super.onNeighborBlockChange(par1World, par2, par3, par4, par5); + this.checkFlowerChange(par1World, par2, par3, par4); + } + + /** + * Ticks the block if it's been scheduled + */ + @Override + public void updateTick(World par1World, int par2, int par3, int par4, Random par5Random) + { + this.checkFlowerChange(par1World, par2, par3, par4); + } + + protected final void checkFlowerChange(World par1World, int par2, int par3, int par4) + { + if (!this.canBlockStay(par1World, par2, par3, par4)) + { + this.dropBlockAsItem(par1World, par2, par3, par4, par1World.getBlockMetadata(par2, par3, par4), 0); + par1World.setBlock(par2, par3, par4, Blocks.air, 0, 2); + } + } + + /** + * Can this block stay at this position. Similar to canPlaceBlockAt except gets checked often with plants. + */ + @Override + public boolean canBlockStay(World par1World, int par2, int par3, int par4) + { + Block soil = par1World.getBlock(par2, par3 - 1, par4); + return (par1World.getFullBlockLightValue(par2, par3, par4) >= 8 || par1World.canBlockSeeTheSky(par2, par3, par4)) && + (soil != null && soil.canSustainPlant(par1World, par2, par3 - 1, par4, ForgeDirection.UP, this)); + } + + /** + * Is this block (a) opaque and (b) a full 1m cube? This determines whether or not to render the shared face of two + * adjacent blocks and also whether the player can attach torches, redstone wire, etc to this block. + */ + @Override + public boolean isOpaqueCube() + { + return false; + } + + /** + * If this block doesn't render as an ordinary block it will return False (examples: signs, buttons, stairs, etc) + */ + @Override + public boolean renderAsNormalBlock() + { + return false; + } + + /** + * The type of render function that is called for this block + */ + @Override + public int getRenderType() + { + return 1; + } @Override - public EnumPlantType getPlantType(IBlockAccess world, BlockPos pos) { + public EnumPlantType getPlantType(IBlockAccess world, int x, int y, int z) { return EnumPlantType.Plains; } - @SubscribeEvent - public static void onBonemealEvent(BonemealEvent event){ - // Grows crystal flowers when bonemeal is used on grass - if(event.getBlock().getBlock() == Blocks.GRASS){ + @Override + public Block getPlant(IBlockAccess world, int x, int y, int z) { + return this; + } - BlockPos pos = event.getPos().add(event.getWorld().rand.nextInt(8) - event.getWorld().rand.nextInt(8), - event.getWorld().rand.nextInt(4) - event.getWorld().rand.nextInt(4), - event.getWorld().rand.nextInt(8) - event.getWorld().rand.nextInt(8)); - - if (event.getWorld().isAirBlock(new BlockPos(pos)) && (!event.getWorld().provider.getHasNoSky() || pos.getY() < 127) && WizardryBlocks.crystal_flower.canPlaceBlockAt(event.getWorld(), pos)) - { - event.getWorld().setBlockState(pos, WizardryBlocks.crystal_flower.getDefaultState(), 2); - } - } + @Override + public int getPlantMetadata(IBlockAccess world, int x, int y, int z) { + return 0; } } diff --git a/src/main/java/electroblob/wizardry/block/BlockCrystalOre.java b/src/main/java/electroblob/wizardry/block/BlockCrystalOre.java index dc594dc0..0b572cc4 100644 --- a/src/main/java/electroblob/wizardry/block/BlockCrystalOre.java +++ b/src/main/java/electroblob/wizardry/block/BlockCrystalOre.java @@ -2,24 +2,23 @@ package electroblob.wizardry.block; import java.util.Random; -import electroblob.wizardry.registry.WizardryItems; +import electroblob.wizardry.Wizardry; import net.minecraft.block.Block; -import net.minecraft.block.SoundType; import net.minecraft.block.material.Material; -import net.minecraft.block.state.IBlockState; import net.minecraft.item.Item; public class BlockCrystalOre extends Block { public BlockCrystalOre(Material material) { super(material); - this.setSoundType(SoundType.STONE); + setStepSound(Block.soundTypeStone); + setBlockName("crystalOre"); setResistance(5.0F); setHarvestLevel("pickaxe", 2); } @Override - public int quantityDropped(IBlockState state, int fortune, Random random) + public int quantityDropped(int meta, int fortune, Random random) { if(fortune > 0){ return random.nextInt(2) + 1 + random.nextInt(fortune); @@ -29,7 +28,7 @@ public class BlockCrystalOre extends Block { } @Override - public Item getItemDropped(IBlockState state, Random random, int fortune){ - return WizardryItems.magic_crystal; + public Item getItemDropped(int Metadata, Random random, int fortune){ + return Wizardry.magicCrystal; } } diff --git a/src/main/java/electroblob/wizardry/block/BlockMagicLight.java b/src/main/java/electroblob/wizardry/block/BlockMagicLight.java index c3ef8499..7287e618 100644 --- a/src/main/java/electroblob/wizardry/block/BlockMagicLight.java +++ b/src/main/java/electroblob/wizardry/block/BlockMagicLight.java @@ -1,40 +1,26 @@ package electroblob.wizardry.block; +import java.util.Random; + import electroblob.wizardry.tileentity.TileEntityMagicLight; import net.minecraft.block.BlockContainer; import net.minecraft.block.material.Material; -import net.minecraft.block.state.IBlockState; import net.minecraft.tileentity.TileEntity; -import net.minecraft.util.EnumBlockRenderType; -import net.minecraft.util.math.AxisAlignedBB; -import net.minecraft.util.math.BlockPos; -import net.minecraft.world.IBlockAccess; +import net.minecraft.util.AxisAlignedBB; import net.minecraft.world.World; public class BlockMagicLight extends BlockContainer { - - private static final AxisAlignedBB AABB = new AxisAlignedBB(0, 0, 0, 0, 0, 0); public BlockMagicLight(Material par2Material) { super(par2Material); this.setLightLevel(1.0f); + this.setBlockBounds(0, 0, 0, 0, 0, 0); } - - @Override - public AxisAlignedBB getCollisionBoundingBox(IBlockState blockState, World worldIn, BlockPos pos){ - // The other two bounding box methods in Block aren't nullable, so this is the only one that can return NULL_AABB. - return NULL_AABB; - } - - @Override - public AxisAlignedBB getBoundingBox(IBlockState state, IBlockAccess source, BlockPos pos){ - return AABB; - } - @Override - public boolean isCollidable(){ - return false; - } + public AxisAlignedBB getCollisionBoundingBoxFromPool(World par1World, int par2, int par3, int par4) + { + return null; + } @Override public TileEntity createNewTileEntity(World world, int metadata) { @@ -42,17 +28,13 @@ public class BlockMagicLight extends BlockContainer { } @Override - public boolean isOpaqueCube(IBlockState state) { - return false; - } + public boolean renderAsNormalBlock() + { + return false; + } @Override - public boolean isNormalCube(IBlockState state, IBlockAccess world, BlockPos pos) { + public boolean isOpaqueCube(){ return false; } - - @Override - public EnumBlockRenderType getRenderType(IBlockState state) { - return EnumBlockRenderType.ENTITYBLOCK_ANIMATED; - } } diff --git a/src/main/java/electroblob/wizardry/block/BlockSnare.java b/src/main/java/electroblob/wizardry/block/BlockSnare.java index d020a580..5a554c4c 100644 --- a/src/main/java/electroblob/wizardry/block/BlockSnare.java +++ b/src/main/java/electroblob/wizardry/block/BlockSnare.java @@ -2,103 +2,84 @@ package electroblob.wizardry.block; import java.util.Random; +import electroblob.wizardry.EnumElement; +import electroblob.wizardry.MagicDamage; +import electroblob.wizardry.MagicDamage.DamageType; +import electroblob.wizardry.WizardryUtilities; import electroblob.wizardry.tileentity.TileEntityPlayerSave; -import electroblob.wizardry.util.MagicDamage; -import electroblob.wizardry.util.MagicDamage.DamageType; -import electroblob.wizardry.util.WizardryUtilities; import net.minecraft.block.Block; import net.minecraft.block.BlockContainer; -import net.minecraft.block.SoundType; import net.minecraft.block.material.Material; -import net.minecraft.block.state.IBlockState; import net.minecraft.entity.Entity; import net.minecraft.entity.EntityLivingBase; -import net.minecraft.init.MobEffects; import net.minecraft.item.Item; +import net.minecraft.potion.Potion; import net.minecraft.potion.PotionEffect; import net.minecraft.tileentity.TileEntity; -import net.minecraft.util.BlockRenderLayer; -import net.minecraft.util.EnumBlockRenderType; -import net.minecraft.util.EnumFacing; -import net.minecraft.util.math.AxisAlignedBB; -import net.minecraft.util.math.BlockPos; -import net.minecraft.world.IBlockAccess; +import net.minecraft.util.AxisAlignedBB; +import net.minecraft.util.DamageSource; import net.minecraft.world.World; -// TODO: Apparently you shouldn't extend BlockContainer. I feel like BlockArcaneWorkbench should, but what about the rest? -public class BlockSnare extends BlockContainer { +import net.minecraftforge.common.util.ForgeDirection; - private static final AxisAlignedBB AABB = new AxisAlignedBB(0.0f, 0.0f, 0.0f, 1.0f, 0.0625f, 1.0f); +public class BlockSnare extends BlockContainer { public BlockSnare(Material par2Material){ super(par2Material); - this.setSoundType(SoundType.PLANT); + this.setBlockBounds(0.0f, 0.0f, 0.0f, 1.0f, 0.0625f, 1.0f); } @Override - public AxisAlignedBB getBoundingBox(IBlockState state, IBlockAccess source, BlockPos pos){ - return AABB; - } + public AxisAlignedBB getCollisionBoundingBoxFromPool(World par1World, int par2, int par3, int par4){ + return null; + } @Override - public AxisAlignedBB getCollisionBoundingBox(IBlockState blockState, World worldIn, BlockPos pos){ - return NULL_AABB; - } - - @Override - public boolean hasTileEntity(IBlockState state){ + public boolean hasTileEntity(int metadata){ return true; } @Override - public void onEntityCollidedWithBlock(World world, BlockPos pos, IBlockState state, Entity entity) { - + public void onEntityCollidedWithBlock(World world, int x, int y, int z, Entity entity){ if(!world.isRemote && entity instanceof EntityLivingBase){ - if(world.getTileEntity(pos) instanceof TileEntityPlayerSave){ - - TileEntityPlayerSave tileentity = (TileEntityPlayerSave)world.getTileEntity(pos); - + if(world.getTileEntity(x, y, z) instanceof TileEntityPlayerSave){ + TileEntityPlayerSave tileentity = (TileEntityPlayerSave)world.getTileEntity(x, y, z); if(WizardryUtilities.isValidTarget(tileentity.getCaster(), entity)){ ((EntityLivingBase)entity).attackEntityFrom(MagicDamage.causeDirectMagicDamage(tileentity.getCaster(), DamageType.MAGIC), 6); - ((EntityLivingBase)entity).addPotionEffect(new PotionEffect(MobEffects.SLOWNESS, 100, 2)); - - world.destroyBlock(pos, false); + ((EntityLivingBase)entity).addPotionEffect(new PotionEffect(Potion.moveSlowdown.id, 100, 2)); + world.func_147480_a(x, y, z, false); } } } } - // The similarly named onNeighborChange method does NOT do the same thing. - @SuppressWarnings("deprecation") @Override - public void neighborChanged(IBlockState state, World world, BlockPos pos, Block blockIn){ - super.neighborChanged(state, world, pos, blockIn); - if(!world.isSideSolid(pos.down(), EnumFacing.UP, false)){ - world.setBlockToAir(pos); + public void onNeighborBlockChange(World world, int x, int y, int z, Block block) + { + super.onNeighborBlockChange(world, x, y, z, block); + if(!world.isSideSolid(x, y-1, z, ForgeDirection.UP)){ + world.setBlockToAir(x, y, z); } } @Override - public BlockRenderLayer getBlockLayer(){ - return BlockRenderLayer.CUTOUT; - } - - @Override - public EnumBlockRenderType getRenderType(IBlockState state){ - return EnumBlockRenderType.MODEL; - } - - @Override - public boolean isOpaqueCube(IBlockState state){ + public boolean renderAsNormalBlock() + { return false; } - + @Override - public boolean isFullCube(IBlockState state){ - return false; + public boolean isOpaqueCube() + { + return false; } @Override - public Item getItemDropped(IBlockState state, Random rand, int fortune){ + public int getRenderType(){ + return 23; + } + + @Override + public Item getItemDropped(int a, Random random, int b){ return null; } diff --git a/src/main/java/electroblob/wizardry/block/BlockSpectral.java b/src/main/java/electroblob/wizardry/block/BlockSpectral.java index 23837eea..3bf42f7c 100644 --- a/src/main/java/electroblob/wizardry/block/BlockSpectral.java +++ b/src/main/java/electroblob/wizardry/block/BlockSpectral.java @@ -2,106 +2,73 @@ package electroblob.wizardry.block; import java.util.Random; +import electroblob.wizardry.EnumParticleType; import electroblob.wizardry.Wizardry; -import electroblob.wizardry.registry.WizardryBlocks; import electroblob.wizardry.tileentity.TileEntityTimer; -import electroblob.wizardry.util.WizardryParticleType; import net.minecraft.block.Block; import net.minecraft.block.BlockContainer; -import net.minecraft.block.SoundType; import net.minecraft.block.material.Material; -import net.minecraft.block.state.IBlockState; import net.minecraft.tileentity.TileEntity; -import net.minecraft.util.BlockRenderLayer; -import net.minecraft.util.EnumBlockRenderType; -import net.minecraft.util.EnumFacing; -import net.minecraft.util.math.BlockPos; import net.minecraft.world.IBlockAccess; import net.minecraft.world.World; -import net.minecraftforge.event.world.BlockEvent; -import net.minecraftforge.fml.common.Mod; -import net.minecraftforge.fml.common.eventhandler.SubscribeEvent; -import net.minecraftforge.fml.relauncher.Side; -import net.minecraftforge.fml.relauncher.SideOnly; -// For future reference - extend BlockContainer whenever possible because it has methods for removing tile entities on -// block break. -@Mod.EventBusSubscriber +// For future reference - extend BlockContainer whenever possible because it has methods for removing tile entities on block break. public class BlockSpectral extends BlockContainer { public BlockSpectral(Material material) { super(material); - this.setSoundType(SoundType.GLASS); - } - - // Replaces getRenderBlockPass - @Override - public BlockRenderLayer getBlockLayer(){ - return BlockRenderLayer.TRANSLUCENT; - } - - @Override - public EnumBlockRenderType getRenderType(IBlockState state) { - return EnumBlockRenderType.MODEL; } - // Apparently it's OK to override this, despite it being deprecated. More importantly, it being deprecated is not - // Forge's doing, rather it is Mojang themselves misusing the @Deprecated annotation to mean 'internal, don't call'. @Override - public boolean isOpaqueCube(IBlockState state){ + public int getRenderBlockPass() + { + return 1; + } + + @Override + public boolean isOpaqueCube() + { return false; } - + @Override - public boolean isNormalCube(IBlockState state, IBlockAccess world, BlockPos pos) { - return false; - } - + public boolean shouldSideBeRendered(IBlockAccess par1IBlockAccess, int par2, int par3, int par4, int par5) + { + Block block = par1IBlockAccess.getBlock(par2, par3, par4); + return block == this ? false : super.shouldSideBeRendered(par1IBlockAccess, par2, par3, par4, par5); + } + @Override - public void randomDisplayTick(IBlockState state, World world, BlockPos pos, Random random) { - // Middle of block - Wizardry.proxy.spawnParticle(WizardryParticleType.DUST, world, pos.getX()+random.nextDouble(), pos.getY()+random.nextDouble(), pos.getZ()+random.nextDouble(), 0, 0, 0, (int)(16.0D / (Math.random() * 0.8D + 0.2D)), - 0.4f + random.nextFloat()*0.2f, 0.6f + random.nextFloat()*0.4f, 0.6f + random.nextFloat()*0.4f); - // Top surface - Wizardry.proxy.spawnParticle(WizardryParticleType.DUST, world, pos.getX()+random.nextDouble(), pos.getY()+1, pos.getZ()+random.nextDouble(), 0, 0, 0, (int)(16.0D / (Math.random() * 0.8D + 0.2D)), - 0.4f + random.nextFloat()*0.2f, 0.6f + random.nextFloat()*0.4f, 0.6f + random.nextFloat()*0.4f); - Wizardry.proxy.spawnParticle(WizardryParticleType.DUST, world, pos.getX()+random.nextDouble(), pos.getY()+1, pos.getZ()+random.nextDouble(), 0, 0, 0, (int)(16.0D / (Math.random() * 0.8D + 0.2D)), - 0.4f + random.nextFloat()*0.2f, 0.6f + random.nextFloat()*0.4f, 0.6f + random.nextFloat()*0.4f); - } - + public void randomDisplayTick(World world, int x, int y, int z, Random random){ + // Middle of block + Wizardry.proxy.spawnParticle(EnumParticleType.DUST, world, x+random.nextDouble(), y+random.nextDouble(), z+random.nextDouble(), 0, 0, 0, (int)(16.0D / (Math.random() * 0.8D + 0.2D)), + 0.4f + random.nextFloat()*0.2f, 0.6f + random.nextFloat()*0.4f, 0.6f + random.nextFloat()*0.4f); + // Top surface + Wizardry.proxy.spawnParticle(EnumParticleType.DUST, world, x+random.nextDouble(), y+1, z+random.nextDouble(), 0, 0, 0, (int)(16.0D / (Math.random() * 0.8D + 0.2D)), + 0.4f + random.nextFloat()*0.2f, 0.6f + random.nextFloat()*0.4f, 0.6f + random.nextFloat()*0.4f); + Wizardry.proxy.spawnParticle(EnumParticleType.DUST, world, x+random.nextDouble(), y+1, z+random.nextDouble(), 0, 0, 0, (int)(16.0D / (Math.random() * 0.8D + 0.2D)), + 0.4f + random.nextFloat()*0.2f, 0.6f + random.nextFloat()*0.4f, 0.6f + random.nextFloat()*0.4f); + } + // Overriden to make the block always look full brightness despite not emitting full light. @Override - public int getPackedLightmapCoords(IBlockState state, IBlockAccess source, BlockPos pos) { - return 15; - } + public int getMixedBrightnessForBlock(IBlockAccess par1IBlockAccess, int par2, int par3, int par4) + { + return 15; + } @Override public TileEntity createNewTileEntity(World world, int metadata) { return new TileEntityTimer(1200); } + /** + * Returns the quantity of items to drop on block destruction. + */ @Override - public int quantityDropped(Random par1Random){ + public int quantityDropped(Random par1Random) + { return 0; } - - @SuppressWarnings("deprecation") - @SideOnly(Side.CLIENT) - @Override - public boolean shouldSideBeRendered(IBlockState blockState, IBlockAccess blockAccess, BlockPos pos, EnumFacing side){ - - IBlockState iblockstate = blockAccess.getBlockState(pos.offset(side)); - Block block = iblockstate.getBlock(); - - return block == this ? false : super.shouldSideBeRendered(blockState, blockAccess, pos, side); - } - - @SubscribeEvent - public static void onBlockPlaceEvent(BlockEvent.PlaceEvent event){ - // Spectral blocks cannot be built on - if(event.getPlacedAgainst() == WizardryBlocks.spectral_block){ - event.setCanceled(true); - } - } } diff --git a/src/main/java/electroblob/wizardry/block/BlockStatue.java b/src/main/java/electroblob/wizardry/block/BlockStatue.java index 3b3e9b5e..364483c9 100644 --- a/src/main/java/electroblob/wizardry/block/BlockStatue.java +++ b/src/main/java/electroblob/wizardry/block/BlockStatue.java @@ -2,50 +2,43 @@ package electroblob.wizardry.block; import java.util.Random; -import electroblob.wizardry.spell.Petrify; +import cpw.mods.fml.relauncher.Side; +import cpw.mods.fml.relauncher.SideOnly; import electroblob.wizardry.tileentity.TileEntityStatue; import net.minecraft.block.Block; import net.minecraft.block.BlockContainer; -import net.minecraft.block.SoundType; import net.minecraft.block.material.Material; -import net.minecraft.block.state.IBlockState; +import net.minecraft.init.Blocks; import net.minecraft.tileentity.TileEntity; -import net.minecraft.util.BlockRenderLayer; -import net.minecraft.util.EnumBlockRenderType; -import net.minecraft.util.EnumFacing; -import net.minecraft.util.math.AxisAlignedBB; -import net.minecraft.util.math.BlockPos; +import net.minecraft.util.AxisAlignedBB; +import net.minecraft.util.Facing; import net.minecraft.world.IBlockAccess; import net.minecraft.world.World; -import net.minecraftforge.fml.relauncher.Side; -import net.minecraftforge.fml.relauncher.SideOnly; public class BlockStatue extends BlockContainer { private boolean isIce; - public BlockStatue(Material material) { - super(material); - this.isIce = material == Material.ICE; + public BlockStatue(Material par2Material) { + super(par2Material); + this.isIce = par2Material == Material.ice; if(this.isIce){ this.slipperiness = 0.98F; - this.setSoundType(SoundType.GLASS); } } - + @Override - public AxisAlignedBB getBoundingBox(IBlockState state, IBlockAccess world, BlockPos pos){ + public void setBlockBoundsBasedOnState(IBlockAccess world, int x, int y, int z) { // Not a good idea to call getBlockBoundsMinX() or whatever from in here, since this method changes those! if(!this.isIce){ - if(world.getTileEntity(pos) instanceof TileEntityStatue){ + if(world.getTileEntity(x, y, z) instanceof TileEntityStatue){ - TileEntityStatue statue = (TileEntityStatue)world.getTileEntity(pos); + TileEntityStatue statue = (TileEntityStatue)world.getTileEntity(x, y, z); if(statue.creature != null){ - // Block bounds are set to match the width and height of the entity, clamped to within 1 block. - return new AxisAlignedBB((float)Math.max(0.5 - statue.creature.width/2, 0), 0, + this.setBlockBounds((float)Math.max(0.5 - statue.creature.width/2, 0), 0, (float)Math.max(0.5 - statue.creature.width/2, 0), (float)Math.min(0.5 + statue.creature.width/2, 1), // This checks if the block is the top one and if so reduces its height so the top lines up with @@ -55,92 +48,125 @@ public class BlockStatue extends BlockContainer { } } } - - return FULL_BLOCK_AABB; } - - // getCollisionBoundingBox eventually calls getBoundingBox anyway, and since I want the collision box and the block - // outline to be the same here, I've removed that getCollisionBoundingBox entirely. - // The number of these methods is quite simply ridiculous. This one seems to be for placement logic and block - // connections (fences, glass panes, etc.)... - @Override public boolean isFullCube(IBlockState state) { return false; } - // ...this one isn't used much but has something to do with redstone... - @Override public boolean isBlockNormalCube(IBlockState state) { return false; } - // ... this one is for most other game logic... - @Override public boolean isNormalCube(IBlockState state) { return false; } - // Forge version of the above method. I still need to override both though because vanilla uses the other one. - @Override public boolean isNormalCube(IBlockState state, IBlockAccess world, BlockPos pos) { return false; } - // ... and this one is for rendering. - @Override public boolean isOpaqueCube(IBlockState state){ return false; } - @Override - public BlockRenderLayer getBlockLayer(){ - return this.isIce ? BlockRenderLayer.TRANSLUCENT : BlockRenderLayer.SOLID; + public AxisAlignedBB getCollisionBoundingBoxFromPool(World world, int x, int y, int z) { + + if(!this.isIce){ + + if(world.getTileEntity(x, y, z) instanceof TileEntityStatue){ + + TileEntityStatue statue = (TileEntityStatue)world.getTileEntity(x, y, z); + + if(statue.creature != null){ + // Block bounds are set to match the width and height of the entity, clamped to within 1 block. + return AxisAlignedBB.getBoundingBox(x + Math.max(0.5 - statue.creature.width/2, 0), + y, z + Math.max(0.5 - statue.creature.width/2, 0), + x + Math.min(0.5 + statue.creature.width/2, 1), + // This checks if the block is the top one and if so reduces its height so the top lines up with + // the top of the entity model + statue.position == statue.parts ? y + (float)Math.min(statue.creature.height - statue.parts + 1, 1) : y + 1, + z + Math.min(0.5 + statue.creature.width/2, 1)); + } + } + } + + return super.getCollisionBoundingBoxFromPool(world, x, y, z); } - + @Override - public EnumBlockRenderType getRenderType(IBlockState state){ - return this.isIce ? EnumBlockRenderType.MODEL : EnumBlockRenderType.ENTITYBLOCK_ANIMATED; + public boolean renderAsNormalBlock(){ + return false; + } + + @Override + public boolean canRenderInPass(int pass){ + return this.isIce ? super.canRenderInPass(pass) : false; + } + + @Override + public boolean isOpaqueCube(){ + return false;// !this.isIce; + } + + @Override + public int getRenderBlockPass() + { + return this.isIce? 1 : 0; } @Override public TileEntity createNewTileEntity(World world, int metadata) { return new TileEntityStatue(this.isIce); } - + /* @Override - public int quantityDropped(Random random){ + public void randomDisplayTick(World world, int x, int y, int z, Random random){ + if(this.isIce){ + float brightness = 0.5f + (random.nextFloat()/2); + Minecraft.getMinecraft().effectRenderer.addEffect(new EntitySparkleFX(world, x + random.nextDouble(), y + random.nextDouble(), z + random.nextDouble(), 0, 0, 0, null, brightness, brightness + 0.1f, 1.0f, true)); + } + } + */ + + + /** + * Returns the quantity of items to drop on block destruction. + */ + @Override + public int quantityDropped(Random par1Random) + { return 0; } - + + /** + * Returns true if the given side of this block type should be rendered, if the adjacent block is at the given + * coordinates. Args: blockAccess, x, y, z, side + */ @Override - public void breakBlock(World world, BlockPos pos, IBlockState state){ - - if(!world.isRemote){ - - TileEntityStatue tileentity = (TileEntityStatue)world.getTileEntity(pos); - + @SideOnly(Side.CLIENT) + public boolean shouldSideBeRendered(IBlockAccess p_149646_1_, int p_149646_2_, int p_149646_3_, int p_149646_4_, int p_149646_5_) + { + Block block = p_149646_1_.getBlock(p_149646_2_, p_149646_3_, p_149646_4_); + + return block == this ? false : super.shouldSideBeRendered(p_149646_1_, p_149646_2_, p_149646_3_, p_149646_4_, p_149646_5_); + } + + @Override + public void breakBlock(World par1World, int x, int y, int z, Block block, int par6) + { + if(!par1World.isRemote){ + TileEntityStatue tileentity = (TileEntityStatue)par1World.getTileEntity(x, y, z); if(tileentity != null){ if(tileentity.parts == 2){ if(tileentity.position == 2){ - world.destroyBlock(pos.down(), false); + // func_147480_a is the method that causes the block to smash as if broken by a player. + // Used to be called destroyBlock. The boolean is whether the block should drop items. + par1World.func_147480_a(x, y-1, z, false); }else{ - world.destroyBlock(pos.up(), false); + par1World.func_147480_a(x, y+1, z, false); } }else if(tileentity.parts == 3){ if(tileentity.position == 3){ - world.destroyBlock(pos.down(), false); - world.destroyBlock(pos.down(2), false); + par1World.func_147480_a(x, y-1, z, false); + par1World.func_147480_a(x, y-2, z, false); }else if(tileentity.position == 2){ - world.destroyBlock(pos.down(), false); - world.destroyBlock(pos.up(), false); + par1World.func_147480_a(x, y-1, z, false); + par1World.func_147480_a(x, y+1, z, false); }else{ - world.destroyBlock(pos.up(), false); - world.destroyBlock(pos.up(2), false); + par1World.func_147480_a(x, y+1, z, false); + par1World.func_147480_a(x, y+2, z, false); } } } // This is only when position == 1 because world.destroyBlock calls this function for the other blocks. if(tileentity != null && tileentity.position == 1 && tileentity.creature != null){ - tileentity.creature.getEntityData().removeTag(Petrify.NBT_KEY); tileentity.creature.isDead = false; - world.spawnEntityInWorld(tileentity.creature); + par1World.spawnEntityInWorld(tileentity.creature); } } - - super.breakBlock(world, pos, state); + super.breakBlock(par1World, x, y, z, block, par6); } - - @SuppressWarnings("deprecation") - @SideOnly(Side.CLIENT) - @Override - public boolean shouldSideBeRendered(IBlockState blockState, IBlockAccess blockAccess, BlockPos pos, EnumFacing side){ - - IBlockState iblockstate = blockAccess.getBlockState(pos.offset(side)); - Block block = iblockstate.getBlock(); - - return this.isIce && block == this ? false : super.shouldSideBeRendered(blockState, blockAccess, pos, side); - } } diff --git a/src/main/java/electroblob/wizardry/block/BlockTransportationStone.java b/src/main/java/electroblob/wizardry/block/BlockTransportationStone.java index 681d5266..f555b9e1 100644 --- a/src/main/java/electroblob/wizardry/block/BlockTransportationStone.java +++ b/src/main/java/electroblob/wizardry/block/BlockTransportationStone.java @@ -2,114 +2,148 @@ package electroblob.wizardry.block; import java.util.Random; -import electroblob.wizardry.WizardData; +import electroblob.wizardry.ExtendedPlayer; +import electroblob.wizardry.Wizardry; +import electroblob.wizardry.WizardryRegistry; import electroblob.wizardry.item.ItemWand; -import electroblob.wizardry.registry.Spells; -import electroblob.wizardry.registry.WizardryBlocks; +import electroblob.wizardry.spell.Spell; import net.minecraft.block.Block; import net.minecraft.block.material.Material; -import net.minecraft.block.state.IBlockState; import net.minecraft.entity.player.EntityPlayer; -import net.minecraft.item.ItemStack; -import net.minecraft.util.EnumFacing; -import net.minecraft.util.EnumHand; -import net.minecraft.util.math.AxisAlignedBB; -import net.minecraft.util.math.BlockPos; -import net.minecraft.util.text.TextComponentTranslation; -import net.minecraft.world.IBlockAccess; +import net.minecraft.util.ChatComponentTranslation; import net.minecraft.world.World; +import net.minecraftforge.common.util.ForgeDirection; -public class BlockTransportationStone extends Block { - - private static final AxisAlignedBB AABB = new AxisAlignedBB(0.0625f*5, 0, 0.0625f*5, 0.0625f*11, 0.0625f*6, 0.0625f*11); +public class BlockTransportationStone extends Block{ public BlockTransportationStone(Material material){ super(material); + this.setBlockBounds(0.0625f*5, 0, 0.0625f*5, 0.0625f*11, 0.0625f*6, 0.0625f*11); this.setTickRandomly(true); } - - @Override - public AxisAlignedBB getBoundingBox(IBlockState state, IBlockAccess source, BlockPos pos){ - return AABB; - } - - // The number of these methods is quite simply ridiculous. This one seems to be for placement logic and block - // connections (fences, glass panes, etc.)... - @Override public boolean isFullCube(IBlockState state) { return false; } - // ...this one isn't used much but has something to do with redstone... - @Override public boolean isBlockNormalCube(IBlockState state) { return false; } - // ... this one is for most other game logic... - @Override public boolean isNormalCube(IBlockState state) { return false; } - // Forge version of the above method. I still need to override both though because vanilla uses the other one. - @Override public boolean isNormalCube(IBlockState state, IBlockAccess world, BlockPos pos) { return false; } - // ... and this one is for rendering. - @Override public boolean isOpaqueCube(IBlockState state){ return false; } - - @Override - public void onNeighborChange(IBlockAccess world, BlockPos pos, BlockPos neighbor){ - - super.onNeighborChange(world, pos, neighbor); - - if(!world.isSideSolid(pos.down(), EnumFacing.UP, false) && world instanceof World){ - this.dropBlockAsItem((World) world, pos, world.getBlockState(pos), 0); - ((World)world).setBlockToAir(pos); - } - } - - @Override - public void updateTick(World world, BlockPos pos, IBlockState state, Random random) { - if(!world.isSideSolid(pos.down(), EnumFacing.UP)){ - this.dropBlockAsItem(world, pos, world.getBlockState(pos), 0); - world.setBlockToAir(pos); + @Override + public boolean isOpaqueCube(){ + return false; + } + + @Override + public boolean renderAsNormalBlock(){ + return false; + } + + @Override + public void onNeighborBlockChange(World world, int x, int y, int z, Block block) + { + super.onNeighborBlockChange(world, x, y, z, block); + if(!world.isSideSolid(x, y-1, z, ForgeDirection.UP)){ + this.dropBlockAsItem(world, x, y, z, world.getBlockMetadata(x, y, z), 0); + world.setBlockToAir(x, y, z); } - } + } @Override - public boolean canPlaceBlockAt(World world, BlockPos pos){ - return super.canPlaceBlockAt(world, pos) && world.isSideSolid(pos.down(), EnumFacing.UP); - } + public void updateTick(World world, int x, int y, int z, Random random) + { + if(!world.isSideSolid(x, y-1, z, ForgeDirection.UP)){ + this.dropBlockAsItem(world, x, y, z, world.getBlockMetadata(x, y, z), 0); + world.setBlockToAir(x, y, z); + } + } @Override - public boolean onBlockActivated(World world, BlockPos pos, IBlockState state, EntityPlayer player, - EnumHand hand, ItemStack stack, EnumFacing side, float hitX, float hitY, float hitZ) { - - if(stack != null && stack.getItem() instanceof ItemWand){ - if(WizardData.get(player) != null){ + public boolean canPlaceBlockAt(World par1World, int x, int y, int z) + { + return super.canPlaceBlockAt(par1World, x, y, z) && par1World.isSideSolid(x, y-1, z, ForgeDirection.UP); + } + + @Override + public boolean onBlockActivated(World world, int x, int y, int z, EntityPlayer entityplayer, int par6, float par7, float par8, float par9) + { + if(entityplayer.getHeldItem() != null && entityplayer.getHeldItem().getItem() instanceof ItemWand){ + if(ExtendedPlayer.get(entityplayer) != null){ + ExtendedPlayer extendedplayer = ExtendedPlayer.get(entityplayer); - WizardData data = WizardData.get(player); - - for(int x=-1; x<=1; x++){ - for(int z=-1; z<=1; z++){ - BlockPos pos1 = pos.add(x, 0, z); - if(testForCircle(world, pos1)){ - data.setStoneCircleLocation(pos1, world.provider.getDimension()); - if(!world.isRemote) player.addChatMessage(new TextComponentTranslation("tile.wizardry:transportation_stone.confirm", Spells.transportation.getNameForTranslationFormatted())); - return true; - } - } + int x1=x-1, z1=z-1; + if(testForCircle(world, x1, y, z1)){ + extendedplayer.setStoneLocation(x1, y, z1, world.provider.dimensionId); + if(!world.isRemote) entityplayer.addChatMessage(new ChatComponentTranslation("tile.transportationStone.confirm", WizardryRegistry.transportation.getDisplayNameWithFormatting()));//"You will now be returned here upon casting " + EnumSpell.TRANSPORTATION.name); + return true; } - if(!world.isRemote) player.addChatMessage(new TextComponentTranslation("tile.wizardry:transportation_stone.invalid")); + x1=x; + z1=z-1; + if(testForCircle(world, x1, y, z1)){ + extendedplayer.setStoneLocation(x1, y, z1, world.provider.dimensionId); + if(!world.isRemote) entityplayer.addChatMessage(new ChatComponentTranslation("tile.transportationStone.confirm", WizardryRegistry.transportation.getDisplayNameWithFormatting()));//"You will now be returned here upon casting " + EnumSpell.TRANSPORTATION.name); + return true; + } + + x1=x+1; + z1=z-1; + if(testForCircle(world, x1, y, z1)){ + extendedplayer.setStoneLocation(x1, y, z1, world.provider.dimensionId); + if(!world.isRemote) entityplayer.addChatMessage(new ChatComponentTranslation("tile.transportationStone.confirm", WizardryRegistry.transportation.getDisplayNameWithFormatting()));//"You will now be returned here upon casting " + EnumSpell.TRANSPORTATION.name); + return true; + } + + x1=x-1; + z1=z; + if(testForCircle(world, x1, y, z1)){ + extendedplayer.setStoneLocation(x1, y, z1, world.provider.dimensionId); + if(!world.isRemote) entityplayer.addChatMessage(new ChatComponentTranslation("tile.transportationStone.confirm", WizardryRegistry.transportation.getDisplayNameWithFormatting()));//"You will now be returned here upon casting " + EnumSpell.TRANSPORTATION.name); + return true; + } + + x1=x+1; + z1=z; + if(testForCircle(world, x1, y, z1)){ + extendedplayer.setStoneLocation(x1, y, z1, world.provider.dimensionId); + if(!world.isRemote) entityplayer.addChatMessage(new ChatComponentTranslation("tile.transportationStone.confirm", WizardryRegistry.transportation.getDisplayNameWithFormatting()));//"You will now be returned here upon casting " + EnumSpell.TRANSPORTATION.name); + return true; + } + + x1=x-1; + z1=z+1; + if(testForCircle(world, x1, y, z1)){ + extendedplayer.setStoneLocation(x1, y, z1, world.provider.dimensionId); + if(!world.isRemote) entityplayer.addChatMessage(new ChatComponentTranslation("tile.transportationStone.confirm", WizardryRegistry.transportation.getDisplayNameWithFormatting()));//"You will now be returned here upon casting " + EnumSpell.TRANSPORTATION.name); + return true; + } + + x1=x; + z1=z+1; + if(testForCircle(world, x1, y, z1)){ + extendedplayer.setStoneLocation(x1, y, z1, world.provider.dimensionId); + if(!world.isRemote) entityplayer.addChatMessage(new ChatComponentTranslation("tile.transportationStone.confirm", WizardryRegistry.transportation.getDisplayNameWithFormatting()));//"You will now be returned here upon casting " + EnumSpell.TRANSPORTATION.name); + return true; + } + + x1=x+1; + z1=z+1; + if(testForCircle(world, x1, y, z1)){ + extendedplayer.setStoneLocation(x1, y, z1, world.provider.dimensionId); + if(!world.isRemote) entityplayer.addChatMessage(new ChatComponentTranslation("tile.transportationStone.confirm", WizardryRegistry.transportation.getDisplayNameWithFormatting()));//"You will now be returned here upon casting " + EnumSpell.TRANSPORTATION.name); + return true; + } + + if(!world.isRemote) entityplayer.addChatMessage(new ChatComponentTranslation("tile.transportationStone.invalid"));//"You must make a circle with 8 stones of transportation first!"); return true; } } return false; - } + } /** Returns whether the specified location is surrounded by a complete cicle of 8 transportation stones. */ - public static boolean testForCircle(World world, BlockPos pos){ - - if(world.getBlockState(pos).getMaterial().blocksMovement()) return false; - - for(int x=-1; x<=1; x++){ - for(int z=-1; z<=1; z++){ - if(world.getBlockState(pos.add(x, 0, z)).getBlock() != WizardryBlocks.transportation_stone){ - if(x != 0 || z != 0) return false; - } - } - } - - return true; + public static boolean testForCircle(World world, int x, int y, int z){ + return world.getBlock(x-1, y, z-1) == Wizardry.transportationStone + && world.getBlock(x, y, z-1) == Wizardry.transportationStone + && world.getBlock(x+1, y, z-1) == Wizardry.transportationStone + && world.getBlock(x-1, y, z) == Wizardry.transportationStone + && !world.getBlock(x, y, z).getMaterial().blocksMovement() + && world.getBlock(x+1, y, z) == Wizardry.transportationStone + && world.getBlock(x-1, y, z+1) == Wizardry.transportationStone + && world.getBlock(x, y, z+1) == Wizardry.transportationStone + && world.getBlock(x+1, y, z+1) == Wizardry.transportationStone; } } diff --git a/src/main/java/electroblob/wizardry/block/BlockVanishingCobweb.java b/src/main/java/electroblob/wizardry/block/BlockVanishingCobweb.java index 3dcc77cd..b96fa137 100644 --- a/src/main/java/electroblob/wizardry/block/BlockVanishingCobweb.java +++ b/src/main/java/electroblob/wizardry/block/BlockVanishingCobweb.java @@ -2,19 +2,17 @@ package electroblob.wizardry.block; import java.util.Random; +import electroblob.wizardry.EnumParticleType; +import electroblob.wizardry.Wizardry; import electroblob.wizardry.tileentity.TileEntityTimer; +import net.minecraft.block.Block; import net.minecraft.block.BlockContainer; import net.minecraft.block.material.Material; -import net.minecraft.block.state.IBlockState; import net.minecraft.entity.Entity; import net.minecraft.tileentity.TileEntity; -import net.minecraft.util.BlockRenderLayer; -import net.minecraft.util.EnumBlockRenderType; -import net.minecraft.util.math.AxisAlignedBB; -import net.minecraft.util.math.BlockPos; +import net.minecraft.util.AxisAlignedBB; +import net.minecraft.world.IBlockAccess; import net.minecraft.world.World; -import net.minecraftforge.fml.relauncher.Side; -import net.minecraftforge.fml.relauncher.SideOnly; // For future reference - extend BlockContainer whenever possible because it has methods for removing tile entities on block break. public class BlockVanishingCobweb extends BlockContainer { @@ -22,32 +20,15 @@ public class BlockVanishingCobweb extends BlockContainer { public BlockVanishingCobweb(Material material) { super(material); } - - @SideOnly(Side.CLIENT) - public BlockRenderLayer getBlockLayer() + + @Override + public int getRenderType() { - return BlockRenderLayer.CUTOUT; + return 1; } - + @Override - public EnumBlockRenderType getRenderType(IBlockState state){ - return EnumBlockRenderType.MODEL; - } - - @Override - public boolean isOpaqueCube(IBlockState state) - { - return false; - } - - @Override - public AxisAlignedBB getCollisionBoundingBox(IBlockState blockState, World worldIn, BlockPos pos) - { - return NULL_AABB; - } - - @Override - public boolean isFullCube(IBlockState state) + public boolean isOpaqueCube() { return false; } @@ -58,12 +39,22 @@ public class BlockVanishingCobweb extends BlockContainer { } @Override - public void onEntityCollidedWithBlock(World world, BlockPos pos, IBlockState state, Entity entity){ + public void onEntityCollidedWithBlock(World world, int x, int y, int z, Entity entity) + { entity.setInWeb(); } - + @Override - public int quantityDropped(Random par1Random){ + public AxisAlignedBB getCollisionBoundingBoxFromPool(World p_149668_1_, int p_149668_2_, int p_149668_3_, int p_149668_4_) { + return null; + } + + /** + * Returns the quantity of items to drop on block destruction. + */ + @Override + public int quantityDropped(Random par1Random) + { return 0; } diff --git a/src/main/java/electroblob/wizardry/client/ClientProxy.java b/src/main/java/electroblob/wizardry/client/ClientProxy.java index 5863acec..a92d164f 100644 --- a/src/main/java/electroblob/wizardry/client/ClientProxy.java +++ b/src/main/java/electroblob/wizardry/client/ClientProxy.java @@ -5,25 +5,32 @@ import java.util.HashMap; import org.lwjgl.input.Keyboard; +import cpw.mods.fml.client.config.GuiConfigEntries.NumberSliderEntry; +import cpw.mods.fml.client.registry.ClientRegistry; +import cpw.mods.fml.client.registry.RenderingRegistry; import electroblob.wizardry.CommonProxy; +import electroblob.wizardry.EnumParticleType; +import electroblob.wizardry.ExtendedPlayer; import electroblob.wizardry.SpellGlyphData; -import electroblob.wizardry.WizardData; +import electroblob.wizardry.WandHelper; import electroblob.wizardry.Wizardry; +import electroblob.wizardry.WizardryRegistry; +import electroblob.wizardry.WizardryUtilities; +import electroblob.wizardry.client.model.ModelSpiritHorse; +import electroblob.wizardry.client.model.ModelSpiritWolf; import electroblob.wizardry.client.model.ModelWizardArmour; -import electroblob.wizardry.client.particle.ParticleBlizzard; -import electroblob.wizardry.client.particle.ParticleDarkMagic; -import electroblob.wizardry.client.particle.ParticleDust; -import electroblob.wizardry.client.particle.ParticleGiantBubble; -import electroblob.wizardry.client.particle.ParticleIce; -import electroblob.wizardry.client.particle.ParticleLeaf; -import electroblob.wizardry.client.particle.ParticleMagicFlame; -import electroblob.wizardry.client.particle.ParticlePath; -import electroblob.wizardry.client.particle.ParticleRotatingSparkle; -import electroblob.wizardry.client.particle.ParticleSnow; -import electroblob.wizardry.client.particle.ParticleSpark; -import electroblob.wizardry.client.particle.ParticleSparkle; -import electroblob.wizardry.client.particle.ParticleTornado; -import electroblob.wizardry.client.renderer.LayerStone; +import electroblob.wizardry.client.particle.EntityBlizzardFX; +import electroblob.wizardry.client.particle.EntityDarkMagicFX; +import electroblob.wizardry.client.particle.EntityDustFX; +import electroblob.wizardry.client.particle.EntityIceFX; +import electroblob.wizardry.client.particle.EntityLeafFX; +import electroblob.wizardry.client.particle.EntityMagicBubbleFX; +import electroblob.wizardry.client.particle.EntityMagicFireFX; +import electroblob.wizardry.client.particle.EntityPathFX; +import electroblob.wizardry.client.particle.EntitySnowFX; +import electroblob.wizardry.client.particle.EntitySparkFX; +import electroblob.wizardry.client.particle.EntitySparkleFX; +import electroblob.wizardry.client.particle.EntityTornadoFX; import electroblob.wizardry.client.renderer.RenderArc; import electroblob.wizardry.client.renderer.RenderArcaneWorkbench; import electroblob.wizardry.client.renderer.RenderBlackHole; @@ -32,6 +39,7 @@ import electroblob.wizardry.client.renderer.RenderBubble; import electroblob.wizardry.client.renderer.RenderDecay; import electroblob.wizardry.client.renderer.RenderDecoy; import electroblob.wizardry.client.renderer.RenderEvilWizard; +import electroblob.wizardry.client.renderer.RenderFallingGrass; import electroblob.wizardry.client.renderer.RenderFireRing; import electroblob.wizardry.client.renderer.RenderForceArrow; import electroblob.wizardry.client.renderer.RenderHammer; @@ -41,14 +49,25 @@ import electroblob.wizardry.client.renderer.RenderLightningDisc; import electroblob.wizardry.client.renderer.RenderLightningPulse; import electroblob.wizardry.client.renderer.RenderMagicArrow; import electroblob.wizardry.client.renderer.RenderMagicLight; +import electroblob.wizardry.client.renderer.RenderMagicSlime; +import electroblob.wizardry.client.renderer.RenderMeteor; import electroblob.wizardry.client.renderer.RenderPhoenix; import electroblob.wizardry.client.renderer.RenderProjectile; import electroblob.wizardry.client.renderer.RenderSigil; +import electroblob.wizardry.client.renderer.RenderSilverfishMinion; +import electroblob.wizardry.client.renderer.RenderSkeletonMinion; +import electroblob.wizardry.client.renderer.RenderSpiderMinion; import electroblob.wizardry.client.renderer.RenderSpiritHorse; import electroblob.wizardry.client.renderer.RenderSpiritWolf; import electroblob.wizardry.client.renderer.RenderStatue; +import electroblob.wizardry.client.renderer.RenderTranslucentItem; import electroblob.wizardry.client.renderer.RenderWizard; +import electroblob.wizardry.client.renderer.RenderWraithMinion; +import electroblob.wizardry.client.renderer.RenderZombieMinion; import electroblob.wizardry.entity.EntityArc; +import electroblob.wizardry.entity.EntityFallingGrass; +import electroblob.wizardry.entity.EntityMagicSlime; +import electroblob.wizardry.entity.EntityMeteor; import electroblob.wizardry.entity.EntityShield; import electroblob.wizardry.entity.construct.EntityArrowRain; import electroblob.wizardry.entity.construct.EntityBlackHole; @@ -67,6 +86,7 @@ import electroblob.wizardry.entity.construct.EntityIceSpike; import electroblob.wizardry.entity.construct.EntityLightningPulse; import electroblob.wizardry.entity.construct.EntityLightningSigil; import electroblob.wizardry.entity.construct.EntityTornado; +import electroblob.wizardry.entity.living.EntityBlazeMinion; import electroblob.wizardry.entity.living.EntityDecoy; import electroblob.wizardry.entity.living.EntityEvilWizard; import electroblob.wizardry.entity.living.EntityIceGiant; @@ -74,12 +94,15 @@ import electroblob.wizardry.entity.living.EntityIceWraith; import electroblob.wizardry.entity.living.EntityLightningWraith; import electroblob.wizardry.entity.living.EntityPhoenix; import electroblob.wizardry.entity.living.EntityShadowWraith; +import electroblob.wizardry.entity.living.EntitySilverfishMinion; +import electroblob.wizardry.entity.living.EntitySkeletonMinion; +import electroblob.wizardry.entity.living.EntitySpiderMinion; import electroblob.wizardry.entity.living.EntitySpiritHorse; import electroblob.wizardry.entity.living.EntitySpiritWolf; import electroblob.wizardry.entity.living.EntityStormElemental; +import electroblob.wizardry.entity.living.EntitySummonedCreature; import electroblob.wizardry.entity.living.EntityWizard; -import electroblob.wizardry.entity.living.ISpellCaster; -import electroblob.wizardry.entity.living.ISummonedCreature; +import electroblob.wizardry.entity.living.EntityZombieMinion; import electroblob.wizardry.entity.projectile.EntityDarknessOrb; import electroblob.wizardry.entity.projectile.EntityDart; import electroblob.wizardry.entity.projectile.EntityFirebolt; @@ -97,81 +120,86 @@ import electroblob.wizardry.entity.projectile.EntitySmokeBomb; import electroblob.wizardry.entity.projectile.EntitySpark; import electroblob.wizardry.entity.projectile.EntitySparkBomb; import electroblob.wizardry.entity.projectile.EntityThunderbolt; -import electroblob.wizardry.event.SpellCastEvent; -import electroblob.wizardry.event.SpellCastEvent.Source; +import electroblob.wizardry.item.ItemFlamingAxe; +import electroblob.wizardry.item.ItemFrostAxe; import electroblob.wizardry.item.ItemScroll; +import electroblob.wizardry.item.ItemSpectralArmour; +import electroblob.wizardry.item.ItemSpectralBow; +import electroblob.wizardry.item.ItemSpectralPickaxe; +import electroblob.wizardry.item.ItemSpectralSword; import electroblob.wizardry.item.ItemSpellBook; import electroblob.wizardry.item.ItemWand; import electroblob.wizardry.packet.PacketCastContinuousSpell; import electroblob.wizardry.packet.PacketCastSpell; -import electroblob.wizardry.packet.PacketNPCCastSpell; import electroblob.wizardry.packet.PacketPlayerSync.Message; import electroblob.wizardry.packet.PacketTransportation; -import electroblob.wizardry.registry.Spells; import electroblob.wizardry.spell.Clairvoyance; -import electroblob.wizardry.spell.None; import electroblob.wizardry.spell.Spell; import electroblob.wizardry.tileentity.TileEntityArcaneWorkbench; import electroblob.wizardry.tileentity.TileEntityMagicLight; import electroblob.wizardry.tileentity.TileEntityStatue; -import electroblob.wizardry.util.WandHelper; -import electroblob.wizardry.util.WizardryParticleType; -import net.minecraft.block.state.IBlockState; +import net.minecraft.block.Block; import net.minecraft.client.Minecraft; import net.minecraft.client.gui.FontRenderer; import net.minecraft.client.model.ModelBiped; -import net.minecraft.client.renderer.entity.RenderBlaze; +import net.minecraft.client.model.ModelSlime; +import net.minecraft.client.particle.EntityDiggingFX; import net.minecraft.client.resources.I18n; import net.minecraft.client.settings.KeyBinding; import net.minecraft.entity.Entity; import net.minecraft.entity.EntityLiving; import net.minecraft.entity.EntityLivingBase; -import net.minecraft.entity.monster.EntityBlaze; import net.minecraft.entity.player.EntityPlayer; -import net.minecraft.init.SoundEvents; -import net.minecraft.item.Item; import net.minecraft.item.ItemStack; -import net.minecraft.util.EnumParticleTypes; import net.minecraft.util.ResourceLocation; -import net.minecraft.util.SoundEvent; -import net.minecraft.util.math.BlockPos; -import net.minecraft.util.math.MathHelper; +import net.minecraft.util.Vec3; import net.minecraft.world.World; +import net.minecraftforge.client.MinecraftForgeClient; import net.minecraftforge.common.MinecraftForge; import net.minecraftforge.common.config.Property; -import net.minecraftforge.fml.client.config.GuiConfigEntries.NumberSliderEntry; -import net.minecraftforge.fml.client.registry.ClientRegistry; -import net.minecraftforge.fml.client.registry.RenderingRegistry; -/** - * The client proxy for wizardry. - * @author Electroblob - * @since Wizardry 1.0 - */ public class ClientProxy extends CommonProxy { /** Static instance of the mixed font renderer */ public static MixedFontRenderer mixedFontRenderer; // Key Bindings - public static final KeyBinding NEXT_SPELL = new KeyBinding("key.wizardry.next_spell", Keyboard.KEY_N, "key.categories.wizardry"); - public static final KeyBinding PREVIOUS_SPELL = new KeyBinding("key.wizardry.previous_spell", Keyboard.KEY_B, "key.categories.wizardry"); + public static final KeyBinding nextSpell = new KeyBinding("key.next_spell", Keyboard.KEY_N, "key.categories.wizardry"); + public static final KeyBinding previousSpell = new KeyBinding("key.previous_spell", Keyboard.KEY_B, "key.categories.wizardry"); // Armour Model - public static final ModelBiped WIZARD_ARMOUR_MODEL = new ModelWizardArmour(0.75f); - - // SECTION Registry - // =============================================================================================================== + public static ModelBiped wizardArmourModel = new ModelWizardArmour(0.75f); @Override public ModelBiped getWizardArmourModel(){ - return WIZARD_ARMOUR_MODEL; + return wizardArmourModel; + } + + @Override + public double getPlayerEyesPos(EntityPlayer player){ + return Minecraft.getMinecraft().thePlayer == player ? player.posY : player.posY + player.eyeHeight; + } + + @Override + public Vec3 getWandTipPosition(EntityLivingBase entity){ + // Behaves differently in first person + if(Minecraft.getMinecraft().thePlayer == entity && Minecraft.getMinecraft().gameSettings.thirdPersonView == 0){ + return Vec3.createVectorHelper(entity.posX, entity.boundingBox.minY + 1.2, entity.posZ); + }else{ + return super.getWandTipPosition(entity); + } } @Override public void registerKeyBindings(){ - ClientRegistry.registerKeyBinding(NEXT_SPELL); - ClientRegistry.registerKeyBinding(PREVIOUS_SPELL); + ClientRegistry.registerKeyBinding(nextSpell); + ClientRegistry.registerKeyBinding(previousSpell); + } + + @Override + public void registerEventHandlers(){ + super.registerEventHandlers(); + MinecraftForge.EVENT_BUS.register(new WizardryClientEventHandler()); } @Override @@ -179,37 +207,15 @@ public class ClientProxy extends CommonProxy { MinecraftForge.EVENT_BUS.register(new GuiSpellDisplay(Minecraft.getMinecraft())); } - @Override - public void initMixedFontRenderer(){ - mixedFontRenderer = new MixedFontRenderer(Minecraft.getMinecraft().gameSettings, - new ResourceLocation("textures/font/ascii.png"), Minecraft.getMinecraft().renderEngine, false); - } - - // SECTION Misc - // =============================================================================================================== - @Override public void setToNumberSliderEntry(Property property) { property.setConfigEntryClass(NumberSliderEntry.class); } - @Override - public World getTheWorld() { - return Minecraft.getMinecraft().theWorld; - } - - @Override - public void playMovingSound(Entity entity, SoundEvent sound, float volume, float pitch, boolean repeat){ - Minecraft.getMinecraft().getSoundHandler().playSound(new MovingSoundEntity(entity, sound, volume, pitch, repeat)); - } - - // SECTION Items - // =============================================================================================================== - @Override public FontRenderer getFontRenderer(ItemStack stack){ - Spell spell = Spells.none; + Spell spell = WizardryRegistry.none; if(stack.getItem() instanceof ItemWand){ spell = WandHelper.getCurrentSpell(stack); @@ -217,10 +223,8 @@ public class ClientProxy extends CommonProxy { spell = Spell.get(stack.getItemDamage()); } - if(Minecraft.getMinecraft().thePlayer != null && Wizardry.settings.discoveryMode - && WizardData.get(Minecraft.getMinecraft().thePlayer) != null - && !Minecraft.getMinecraft().thePlayer.capabilities.isCreativeMode - && !WizardData.get(Minecraft.getMinecraft().thePlayer).hasSpellBeenDiscovered(spell)){ + if(Wizardry.discoveryMode && ExtendedPlayer.get(Minecraft.getMinecraft().thePlayer) != null && !Minecraft.getMinecraft().thePlayer.capabilities.isCreativeMode + && !ExtendedPlayer.get(Minecraft.getMinecraft().thePlayer).hasSpellBeenDiscovered(spell)){ return mixedFontRenderer; } @@ -232,184 +236,111 @@ public class ClientProxy extends CommonProxy { // Displays [Empty slot] if spell is continuous. Spell spell = Spell.get(scroll.getItemDamage()); - if(spell.isContinuous) spell = Spells.none; + if(spell.isContinuous) spell = WizardryRegistry.none; EntityPlayer player = Minecraft.getMinecraft().thePlayer; boolean discovered = true; // It seems that this method is called when the world is loading, before thePlayer has been initialised. // If the player is null, the spell is assumed to be discovered. - if(player != null && Wizardry.settings.discoveryMode && !player.capabilities.isCreativeMode - && WizardData.get(player) != null && !WizardData.get(player).hasSpellBeenDiscovered(spell)){ + if(player != null && Wizardry.discoveryMode && !player.capabilities.isCreativeMode + && ExtendedPlayer.get(player) != null && !ExtendedPlayer.get(player).hasSpellBeenDiscovered(spell)){ discovered = false; } if(discovered){ - return I18n.format("item.wizardry:scroll.name", spell.getDisplayName()).trim(); + return I18n.format("item.scroll.name", spell.getDisplayName()).trim(); }else{ - return I18n.format("item.wizardry:scroll.undiscovered.name", "#" + SpellGlyphData.getGlyphName(spell, player.worldObj) + "#").trim(); + return I18n.format("item.scroll.undiscovered.name", "#" + SpellGlyphData.getGlyphName(spell, player.worldObj) + "#").trim(); } } @Override - public double getConjuredBowDurability(ItemStack stack){ - EntityPlayer player = Minecraft.getMinecraft().thePlayer; - if(player.getActiveItemStack() == stack){ - return (double)(stack.getItemDamage() + (player.getItemInUseMaxCount()) ) / (double)stack.getMaxDamage(); - } - return super.getConjuredBowDurability(stack); - } - - // SECTION Particles - // =============================================================================================================== - - @Override - public void spawnParticle(WizardryParticleType type, World world, double x, double y, double z, double velX, double velY, double velZ, int maxAge, float r, float g, float b, boolean doGravity, double radius){ + public void spawnParticle(EnumParticleType type, World world, double x, double y, double z, double velX, double velY, double velZ, int maxAge, float r, float g, float b, boolean doGravity, double radius){ - // Colour values are now automatically clamped to between 0 and 1, as values outside this range seem to - // cause strange effects in 1.10 (or more specifically, particles that are bright pink!) - // TODO: This is a terrible dirty fix, but it'll do for now. Find a nicer way in future. - if(type != WizardryParticleType.MAGIC_FIRE) r = MathHelper.clamp_float(r, 0, 1); - g = MathHelper.clamp_float(g, 0, 1); - b = MathHelper.clamp_float(b, 0, 1); - switch(type){ case BLIZZARD: - Minecraft.getMinecraft().effectRenderer.addEffect(new ParticleBlizzard(world, maxAge, x, z, radius, y)); + Minecraft.getMinecraft().effectRenderer.addEffect(new EntityBlizzardFX(world, r, g, b, maxAge, x, z, radius, y)); break; case BRIGHT_DUST: - Minecraft.getMinecraft().effectRenderer.addEffect(new ParticleDust(world, x, y, z, velX, velY, velZ, r, g, b, false)); + Minecraft.getMinecraft().effectRenderer.addEffect(new EntityDustFX(world, x, y, z, velX, velY, velZ, r, g, b, false)); break; case DARK_MAGIC: - Minecraft.getMinecraft().effectRenderer.addEffect(new ParticleDarkMagic(world, x, y, z, velX, velY, velZ, r, g, b)); + Minecraft.getMinecraft().effectRenderer.addEffect(new EntityDarkMagicFX(world, x, y, z, velX, velY, velZ, r, g, b)); break; case DUST: - Minecraft.getMinecraft().effectRenderer.addEffect(new ParticleDust(world, x, y, z, velX, velY, velZ, r, g, b, true)); + Minecraft.getMinecraft().effectRenderer.addEffect(new EntityDustFX(world, x, y, z, velX, velY, velZ, r, g, b, true)); break; case ICE: - Minecraft.getMinecraft().effectRenderer.addEffect(new ParticleIce(world, x, y, z, velX, velY, velZ, maxAge)); + Minecraft.getMinecraft().effectRenderer.addEffect(new EntityIceFX(world, x, y, z, velX, velY, velZ, maxAge)); break; case LEAF: - Minecraft.getMinecraft().effectRenderer.addEffect(new ParticleLeaf(world, x, y, z, velX, velY, velZ, maxAge)); + Minecraft.getMinecraft().effectRenderer.addEffect(new EntityLeafFX(world, x, y, z, velX, velY, velZ, maxAge)); break; case MAGIC_BUBBLE: - Minecraft.getMinecraft().effectRenderer.addEffect(new ParticleGiantBubble(world, x, y, z, velX, velY, velZ)); + Minecraft.getMinecraft().effectRenderer.addEffect(new EntityMagicBubbleFX(world, x, y, z, velX, velY, velZ)); break; case MAGIC_FIRE: - Minecraft.getMinecraft().effectRenderer.addEffect(new ParticleMagicFlame(world, x, y, z, velX, velY, velZ, maxAge, r == 0 ? 1 + world.rand.nextFloat() : r)); + Minecraft.getMinecraft().effectRenderer.addEffect(new EntityMagicFireFX(world, x, y, z, velX, velY, velZ, maxAge, r == 0 ? 1 + world.rand.nextFloat() : r)); break; case PATH: - Minecraft.getMinecraft().effectRenderer.addEffect(new ParticlePath(world, x, y, z, velX, velY, velZ, r, g, b, maxAge)); + Minecraft.getMinecraft().effectRenderer.addEffect(new EntityPathFX(world, x, y, z, velX, velY, velZ, r, g, b, maxAge)); break; case SNOW: - Minecraft.getMinecraft().effectRenderer.addEffect(new ParticleSnow(world, x, y, z, velX, velY, velZ)); + Minecraft.getMinecraft().effectRenderer.addEffect(new EntitySnowFX(world, x, y, z, velX, velY, velZ)); break; case SPARK: - Minecraft.getMinecraft().effectRenderer.addEffect(new ParticleSpark(world, x, y, z, velX, velY, velZ)); + Minecraft.getMinecraft().effectRenderer.addEffect(new EntitySparkFX(world, x, y, z, velX, velY, velZ)); break; case SPARKLE: - Minecraft.getMinecraft().effectRenderer.addEffect(new ParticleSparkle(world, x, y, z, velX, velY, velZ, r, g, b, maxAge, doGravity)); - break; - case SPARKLE_ROTATING: - Minecraft.getMinecraft().effectRenderer.addEffect(new ParticleRotatingSparkle(world, maxAge, x, z, radius, y, r, g, b)); - break; - default: + Minecraft.getMinecraft().effectRenderer.addEffect(new EntitySparkleFX(world, x, y, z, velX, velY, velZ, r, g, b, maxAge, doGravity)); break; } } @Override - public void spawnTornadoParticle(World world, double x, double y, double z, double velX, double velZ, double radius, int maxAge, IBlockState block, BlockPos pos){ - Minecraft.getMinecraft().effectRenderer.addEffect(new ParticleTornado(world, maxAge, x, z, radius, y, velX, velZ, block).setBlockPos(pos));//, world.rand.nextInt(6))); + public void spawnDigParticle(World world, double x, double y, double z, double velX, double velY, double velZ, Block block){ + Minecraft.getMinecraft().effectRenderer.addEffect(new EntityDiggingFX(world, x, y, z, velX, velY, velZ, block, 0)); } - // SECTION Packet Handlers - // =============================================================================================================== + @Override + public void spawnTornadoParticle(World world, double x, double y, double z, double velX, double velZ, double radius, int maxAge, Block block, int metadata){ + Minecraft.getMinecraft().effectRenderer.addEffect(new EntityTornadoFX(world, maxAge, x, z, radius, y, velX, velZ, block, metadata, world.rand.nextInt(6))); + } + + @Override + public void playMovingSound(Entity entity, String soundName, float volume, float pitch, boolean repeat){ + Minecraft.getMinecraft().getSoundHandler().playSound(new MovingSoundEntity(entity, soundName, volume, pitch, repeat)); + } @Override public void handleCastSpellPacket(PacketCastSpell.Message message){ World world = Minecraft.getMinecraft().theWorld; Entity caster = world.getEntityByID(message.casterID); + Entity target = world.getEntityByID(message.targetID); Spell spell = Spell.get(message.spellID); - // Should always be true + if(caster instanceof EntityPlayer){ - ((EntityPlayer)caster).setActiveHand(message.hand); + ItemStack stack = ((EntityPlayer)caster).getHeldItem(); + + if(stack != null) ((EntityPlayer)caster).setItemInUse(stack, stack.getMaxItemUseDuration()); // Duration isn't needed because it only ever affects things server-side, and anything that is // seen client-side gets synced elsewhere. - spell.cast(world, (EntityPlayer)caster, message.hand, 0, message.modifiers); - - Source source = Source.OTHER; - - if(((EntityPlayer)caster).getHeldItem(message.hand) != null){ - Item item = ((EntityPlayer)caster).getHeldItem(message.hand).getItem(); - if(item instanceof ItemWand){ - source = Source.WAND; - }else if(item instanceof ItemScroll){ - source = Source.SCROLL; - } - } - - // No need to check if the spell succeeded, because the packet is only ever sent when it succeeds. - // The handler for this event now deals with discovery. - MinecraftForge.EVENT_BUS.post(new SpellCastEvent.Post((EntityPlayer)caster, spell, message.modifiers, source)); - - }else{ - Wizardry.logger.warn("Recieved a PacketCastSpell, but the caster ID was not the ID of a player"); - } - } - - @Override - public void handleCastContinuousSpellPacket(PacketCastContinuousSpell.Message message){ + spell.cast(world, (EntityPlayer)caster, 0, message.damageMultiplier, message.rangeMultiplier, 1, message.blastMultiplier); - World world = Minecraft.getMinecraft().theWorld; - Entity caster = world.getEntityByID(message.casterID); - Spell spell = Spell.get(message.spellID); - // Should always be true - if(caster instanceof EntityPlayer){ + // Updates the spell discovery data client-side. Could be done by calling sync(), but that means sending + // another packet unnecessarily. + if(ExtendedPlayer.get((EntityPlayer)caster) != null){ + ExtendedPlayer.get((EntityPlayer)caster).discoverSpell(spell); + } - WizardData data = WizardData.get((EntityPlayer)caster); - - if(data != null){ - if(data.isCasting()){ - WizardData.get((EntityPlayer)caster).stopCastingContinuousSpell(); - }else{ - WizardData.get((EntityPlayer)caster).startCastingContinuousSpell(spell, message.modifiers); - } - } - }else{ - Wizardry.logger.warn("Recieved a PacketCastContinuousSpell, but the caster ID was not the ID of a player"); } - } - - @Override - public void handleNPCCastSpellPacket(PacketNPCCastSpell.Message message){ - - World world = Minecraft.getMinecraft().theWorld; - Entity caster = world.getEntityByID(message.casterID); - Entity target = message.targetID == -1 ? null : world.getEntityByID(message.targetID); - Spell spell = Spell.get(message.spellID); - // Should always be true - if(caster instanceof EntityLiving){ - - if(target instanceof EntityLivingBase){ - - spell.cast(world, (EntityLiving)caster, message.hand, 0, (EntityLivingBase)target, message.modifiers); - // Again, no need to check if the spell succeeded, because the packet is only ever sent when it succeeds. - MinecraftForge.EVENT_BUS.post(new SpellCastEvent.Post((EntityLiving)caster, spell, message.modifiers, Source.NPC)); - } - - if(caster instanceof ISpellCaster){ - if(spell.isContinuous || spell instanceof None){ - ((ISpellCaster)caster).setContinuousSpell(spell); - ((EntityLiving)caster).setAttackTarget((EntityLivingBase)target); - } - } - }else{ - Wizardry.logger.warn("Recieved a PacketNPCCastSpell, but the caster ID was not the ID of an EntityLiving"); + else if(caster instanceof EntityLiving && target instanceof EntityLivingBase){ + spell.cast(world, (EntityLiving)caster, (EntityLivingBase)target, message.damageMultiplier, message.rangeMultiplier, 1, message.blastMultiplier); } } @@ -418,39 +349,37 @@ public class ClientProxy extends CommonProxy { World world = Minecraft.getMinecraft().theWorld; Entity caster = world.getEntityByID(message.casterID); - // Moved from when the packet is sent to when it is received; fixes the sound not playing in first person. - caster.playSound(SoundEvents.BLOCK_PORTAL_TRAVEL, 1, 1); for(int i=0; i<20; i++){ double radius = 1; double angle = world.rand.nextDouble()*Math.PI*2; double x = caster.posX + radius*Math.cos(angle); - double y = caster.getEntityBoundingBox().minY + world.rand.nextDouble()*2; + double y = WizardryUtilities.getEntityFeetPos(caster) + world.rand.nextDouble()*2; double z = caster.posZ + radius*Math.sin(angle); - Minecraft.getMinecraft().effectRenderer.addEffect(new ParticleSparkle(world, x, y, z, 0, 0.02, 0, 0.6f, 1.0f, 0.6f, 80 + world.rand.nextInt(10))); + Minecraft.getMinecraft().effectRenderer.addEffect(new EntitySparkleFX(world, x, y, z, 0, 0.02, 0, 0.6f, 1.0f, 0.6f, 80 + world.rand.nextInt(10))); } for(int i=0; i<20; i++){ double radius = 1; double angle = world.rand.nextDouble()*Math.PI*2; double x = caster.posX + radius*Math.cos(angle); - double y = caster.getEntityBoundingBox().minY + world.rand.nextDouble()*2; + double y = WizardryUtilities.getEntityFeetPos(caster) + world.rand.nextDouble()*2; double z = caster.posZ + radius*Math.sin(angle); - world.spawnParticle(EnumParticleTypes.VILLAGER_HAPPY, x, y, z, 0, 0.02, 0); + world.spawnParticle("happyVillager", x, y, z, 0, 0.02, 0); } for(int i=0; i<20; i++){ double radius = 1; double angle = world.rand.nextDouble()*Math.PI*2; double x = caster.posX + radius*Math.cos(angle); - double y = caster.getEntityBoundingBox().minY + world.rand.nextDouble()*2; + double y = WizardryUtilities.getEntityFeetPos(caster) + world.rand.nextDouble()*2; double z = caster.posZ + radius*Math.sin(angle); - world.spawnParticle(EnumParticleTypes.ENCHANTMENT_TABLE, x, y, z, 0, 0.02, 0); + world.spawnParticle("enchantmenttable", x, y, z, 0, 0.02, 0); } } @Override public void handlePlayerSyncPacket(Message message){ - WizardData properties = WizardData.get(Minecraft.getMinecraft().thePlayer); + ExtendedPlayer properties = ExtendedPlayer.get(Minecraft.getMinecraft().thePlayer); if(properties != null){ @@ -461,8 +390,8 @@ public class ClientProxy extends CommonProxy { }else{ Entity entity = Minecraft.getMinecraft().theWorld.getEntityByID(message.selectedMinionID); - if(entity instanceof ISummonedCreature){ - properties.selectedMinion = new WeakReference((ISummonedCreature)entity); + if(entity instanceof EntitySummonedCreature){ + properties.selectedMinion = new WeakReference(entity); }else{ properties.selectedMinion = null; } @@ -472,7 +401,7 @@ public class ClientProxy extends CommonProxy { @Override public void handleGlyphDataPacket(electroblob.wizardry.packet.PacketGlyphData.Message message){ - + SpellGlyphData data = SpellGlyphData.get(Minecraft.getMinecraft().theWorld); data.randomNames = new HashMap(); @@ -485,109 +414,165 @@ public class ClientProxy extends CommonProxy { } } + @Override + public void handleCastContinuousSpellPacket(PacketCastContinuousSpell.Message message){ + + World world = Minecraft.getMinecraft().theWorld; + Entity caster = world.getEntityByID(message.casterID); + Spell spell = Spell.get(message.spellID); + // This should always be true + if(caster instanceof EntityPlayer){ + + ExtendedPlayer properties = ExtendedPlayer.get((EntityPlayer)caster); + + if(properties != null){ + if(properties.isCasting()){ + ExtendedPlayer.get((EntityPlayer)caster).stopCastingContinuousSpell(); + }else{ + ExtendedPlayer.get((EntityPlayer)caster).startCastingContinuousSpell(spell, message.damageMultiplier, message.rangeMultiplier, 1, message.blastMultiplier); + } + } + } + } + @Override public void handleClairvoyancePacket(electroblob.wizardry.packet.PacketClairvoyance.Message message) { Clairvoyance.spawnPathPaticles(Minecraft.getMinecraft().theWorld, message.path, message.durationMultiplier); } - // SECTION Rendering - // =============================================================================================================== - - private static final ResourceLocation ICE_WRAITH_TEXTURE = new ResourceLocation(Wizardry.MODID, "textures/entity/ice_wraith.png"); - private static final ResourceLocation LIGHTNING_WRAITH_TEXTURE = new ResourceLocation(Wizardry.MODID, "textures/entity/lightning_wraith.png"); - /** Static instance of the statue renderer, used to access the block breaking texture. */ - public static RenderStatue renderStatue; - @Override - public void initialiseLayers(){ - LayerStone.initialiseLayers(); + public double getWandDisplayDamage(ItemStack stack){ + ExtendedPlayer properties = ExtendedPlayer.get(net.minecraft.client.Minecraft.getMinecraft().thePlayer); + // If the player is creative or this wand is not the wand being used, the arbitrary placeholder damage is not displayed. + if(net.minecraft.client.Minecraft.getMinecraft().thePlayer.capabilities.isCreativeMode || net.minecraft.client.Minecraft.getMinecraft().thePlayer.getHeldItem() != stack){ + return (double)stack.getItemDamageForDisplay() / (double)stack.getMaxDamage(); + }else{ + return (double)(stack.getItemDamageForDisplay() + properties.damageToApply) / (double)stack.getMaxDamage(); + } } @Override - public void registerRenderers(){ + public int getConjuredItemDisplayDamage(ItemStack stack){ + if(stack.getItem() instanceof ItemSpectralSword){ + ExtendedPlayer properties = ExtendedPlayer.get(Minecraft.getMinecraft().thePlayer); + return stack.getItemDamage() + properties.conjuredSwordDuration; + + }else if(stack.getItem() instanceof ItemSpectralPickaxe){ + ExtendedPlayer properties = ExtendedPlayer.get(Minecraft.getMinecraft().thePlayer); + return stack.getItemDamage() + properties.conjuredPickaxeDuration; + + }else if(stack.getItem() instanceof ItemSpectralBow){ + ExtendedPlayer properties = ExtendedPlayer.get(Minecraft.getMinecraft().thePlayer); + return stack.getItemDamage() + properties.conjuredBowDuration; + + }else if(stack.getItem() instanceof ItemSpectralArmour){ + ExtendedPlayer properties = ExtendedPlayer.get(Minecraft.getMinecraft().thePlayer); + return stack.getItemDamage() + properties.conjuredArmourDuration; + + }else if(stack.getItem() instanceof ItemFlamingAxe){ + ExtendedPlayer properties = ExtendedPlayer.get(Minecraft.getMinecraft().thePlayer); + return stack.getItemDamage() + properties.flamingAxeDuration; + + }else if(stack.getItem() instanceof ItemFrostAxe){ + ExtendedPlayer properties = ExtendedPlayer.get(Minecraft.getMinecraft().thePlayer); + return stack.getItemDamage() + properties.frostAxeDuration; + } + + return 0; + } + + @Override + public void registerRenderers() { + + mixedFontRenderer = new MixedFontRenderer(Minecraft.getMinecraft().gameSettings, + new ResourceLocation("textures/font/ascii.png"), Minecraft.getMinecraft().renderEngine, false); + // Minions - // Yet another advantage to the new system: turns out you don't even need to register the renderer if you - // just want the vanilla one for the mob you're extending. - - // An anonymous class in a lambda expression! No point writing a separate class really, is there? - RenderingRegistry.registerEntityRenderingHandler(EntityLightningWraith.class, manager -> new RenderBlaze(manager){ - @Override - protected ResourceLocation getEntityTexture(EntityBlaze entity){ - return LIGHTNING_WRAITH_TEXTURE; - } - }); - - RenderingRegistry.registerEntityRenderingHandler(EntityIceWraith.class, manager -> new RenderBlaze(manager){ - @Override - protected ResourceLocation getEntityTexture(EntityBlaze entity){ - return ICE_WRAITH_TEXTURE; - } - }); - - RenderingRegistry.registerEntityRenderingHandler(EntityIceGiant.class, RenderIceGiant::new); - RenderingRegistry.registerEntityRenderingHandler(EntityPhoenix.class, RenderPhoenix::new); + RenderingRegistry.registerEntityRenderingHandler(EntityZombieMinion.class, new RenderZombieMinion()); + RenderingRegistry.registerEntityRenderingHandler(EntitySkeletonMinion.class, new RenderSkeletonMinion()); + RenderingRegistry.registerEntityRenderingHandler(EntitySpiderMinion.class, new RenderSpiderMinion()); + RenderingRegistry.registerEntityRenderingHandler(EntityBlazeMinion.class, new RenderWraithMinion(new ResourceLocation("textures/entity/blaze.png"))); + RenderingRegistry.registerEntityRenderingHandler(EntityIceWraith.class, new RenderWraithMinion(new ResourceLocation("wizardry:textures/entity/ice_wraith.png"))); + RenderingRegistry.registerEntityRenderingHandler(EntityLightningWraith.class, new RenderWraithMinion(new ResourceLocation("wizardry:textures/entity/lightning_wraith.png"))); + RenderingRegistry.registerEntityRenderingHandler(EntityMagicSlime.class, new RenderMagicSlime(new ModelSlime(16), new ModelSlime(0), 0.25F)); + RenderingRegistry.registerEntityRenderingHandler(EntityIceGiant.class, new RenderIceGiant()); + RenderingRegistry.registerEntityRenderingHandler(EntityPhoenix.class, new RenderPhoenix()); + RenderingRegistry.registerEntityRenderingHandler(EntitySilverfishMinion.class, new RenderSilverfishMinion()); // Projectiles - RenderingRegistry.registerEntityRenderingHandler(EntityMagicMissile.class, manager -> new RenderMagicArrow(manager, new ResourceLocation(Wizardry.MODID, "textures/entity/magic_missile.png"), false, 8.0, 4.0, 16, 9, false)); - RenderingRegistry.registerEntityRenderingHandler(EntityIceShard.class, manager -> new RenderMagicArrow(manager, new ResourceLocation(Wizardry.MODID, "textures/entity/ice_shard.png"), false, 8.0, 2.0, 16, 5, false)); - RenderingRegistry.registerEntityRenderingHandler(EntityLightningArrow.class, manager -> new RenderMagicArrow(manager, new ResourceLocation(Wizardry.MODID, "textures/entity/lightning_arrow.png"), true, 8.0, 2.0, 16, 5, false)); - RenderingRegistry.registerEntityRenderingHandler(EntityDart.class, manager -> new RenderMagicArrow(manager, new ResourceLocation(Wizardry.MODID, "textures/entity/dart.png"), false, 8.0, 2.0, 16, 5, true)); - RenderingRegistry.registerEntityRenderingHandler(EntityIceLance.class, manager -> new RenderMagicArrow(manager, new ResourceLocation(Wizardry.MODID, "textures/entity/ice_lance.png"), false, 16.0, 3.0, 22, 5, true)); + RenderingRegistry.registerEntityRenderingHandler(EntityMagicMissile.class, new RenderMagicArrow(new ResourceLocation("wizardry:textures/entity/magic_missile.png"), false, 8.0, 4.0, 16, 9, false)); + RenderingRegistry.registerEntityRenderingHandler(EntityIceShard.class, new RenderMagicArrow(new ResourceLocation("wizardry:textures/entity/ice_shard.png"), false, 8.0, 2.0, 16, 5, false)); + RenderingRegistry.registerEntityRenderingHandler(EntityLightningArrow.class, new RenderMagicArrow(new ResourceLocation("wizardry:textures/entity/lightning_arrow.png"), true, 8.0, 2.0, 16, 5, false)); + RenderingRegistry.registerEntityRenderingHandler(EntityDart.class, new RenderMagicArrow(new ResourceLocation("wizardry:textures/entity/dart.png"), false, 8.0, 2.0, 16, 5, true)); + RenderingRegistry.registerEntityRenderingHandler(EntityIceLance.class, new RenderMagicArrow(new ResourceLocation("wizardry:textures/entity/ice_lance.png"), false, 16.0, 3.0, 22, 5, true)); - RenderingRegistry.registerEntityRenderingHandler(EntityForceArrow.class, RenderForceArrow::new); + RenderingRegistry.registerEntityRenderingHandler(EntityForceArrow.class, new RenderForceArrow()); // Creatures - RenderingRegistry.registerEntityRenderingHandler(EntitySpiritWolf.class, manager -> new RenderSpiritWolf(manager, 0.5f)); - RenderingRegistry.registerEntityRenderingHandler(EntitySpiritHorse.class, manager -> new RenderSpiritHorse(manager, 0.5f)); - RenderingRegistry.registerEntityRenderingHandler(EntityWizard.class, RenderWizard::new); - RenderingRegistry.registerEntityRenderingHandler(EntityEvilWizard.class, RenderEvilWizard::new); - RenderingRegistry.registerEntityRenderingHandler(EntityDecoy.class, RenderDecoy::new); + RenderingRegistry.registerEntityRenderingHandler(EntitySpiritWolf.class, new RenderSpiritWolf(new ModelSpiritWolf(), new ModelSpiritWolf(), 0.5f)); + RenderingRegistry.registerEntityRenderingHandler(EntitySpiritHorse.class, new RenderSpiritHorse(new ModelSpiritHorse(), 0.5f)); + RenderingRegistry.registerEntityRenderingHandler(EntityWizard.class, new RenderWizard()); + RenderingRegistry.registerEntityRenderingHandler(EntityEvilWizard.class, new RenderEvilWizard()); + RenderingRegistry.registerEntityRenderingHandler(EntityDecoy.class, new RenderDecoy()); // Throwables - RenderingRegistry.registerEntityRenderingHandler(EntitySparkBomb.class, manager -> new RenderProjectile(manager, 0.6f, new ResourceLocation(Wizardry.MODID, "textures/entity/spark_bomb.png"), false)); - RenderingRegistry.registerEntityRenderingHandler(EntityFirebomb.class, manager -> new RenderProjectile(manager, 0.6f, new ResourceLocation(Wizardry.MODID, "textures/items/firebomb.png"), false)); - RenderingRegistry.registerEntityRenderingHandler(EntityPoisonBomb.class, manager -> new RenderProjectile(manager, 0.6f, new ResourceLocation(Wizardry.MODID, "textures/items/poison_bomb.png"), false)); - RenderingRegistry.registerEntityRenderingHandler(EntityIceCharge.class, manager -> new RenderProjectile(manager, 0.6f, new ResourceLocation(Wizardry.MODID, "textures/entity/ice_charge.png"), false)); - RenderingRegistry.registerEntityRenderingHandler(EntityForceOrb.class, manager -> new RenderProjectile(manager, 0.7f, new ResourceLocation(Wizardry.MODID, "textures/entity/force_orb.png"), true)); - RenderingRegistry.registerEntityRenderingHandler(EntitySpark.class, manager -> new RenderProjectile(manager, 0.4f, new ResourceLocation(Wizardry.MODID, "textures/entity/spark.png"), true)); - RenderingRegistry.registerEntityRenderingHandler(EntityDarknessOrb.class, manager -> new RenderProjectile(manager, 0.6f, new ResourceLocation(Wizardry.MODID, "textures/entity/darkness_orb.png"), true)); - RenderingRegistry.registerEntityRenderingHandler(EntityFirebolt.class, manager -> new RenderProjectile(manager, 0.2f, new ResourceLocation(Wizardry.MODID, "textures/entity/firebolt.png"), false)); - RenderingRegistry.registerEntityRenderingHandler(EntityLightningDisc.class, manager -> new RenderLightningDisc(manager, new ResourceLocation(Wizardry.MODID, "textures/entity/lightning_sigil.png"), 2.0f)); - RenderingRegistry.registerEntityRenderingHandler(EntitySmokeBomb.class, manager -> new RenderProjectile(manager, 0.6f, new ResourceLocation(Wizardry.MODID, "textures/items/smoke_bomb.png"), false)); + RenderingRegistry.registerEntityRenderingHandler(EntitySparkBomb.class, new RenderProjectile(0.6f, new ResourceLocation("wizardry:textures/entity/spark_bomb.png"), false)); + RenderingRegistry.registerEntityRenderingHandler(EntityFirebomb.class, new RenderProjectile(0.6f, new ResourceLocation("wizardry:textures/items/firebomb.png"), false)); + RenderingRegistry.registerEntityRenderingHandler(EntityPoisonBomb.class, new RenderProjectile(0.6f, new ResourceLocation("wizardry:textures/items/poison_bomb.png"), false)); + RenderingRegistry.registerEntityRenderingHandler(EntityIceCharge.class, new RenderProjectile(0.6f, new ResourceLocation("wizardry:textures/entity/ice_charge.png"), false)); + RenderingRegistry.registerEntityRenderingHandler(EntityForceOrb.class, new RenderProjectile(0.7f, new ResourceLocation("wizardry:textures/entity/force_orb.png"), true)); + RenderingRegistry.registerEntityRenderingHandler(EntitySpark.class, new RenderProjectile(0.4f, new ResourceLocation("wizardry:textures/entity/spark.png"), true)); + RenderingRegistry.registerEntityRenderingHandler(EntityDarknessOrb.class, new RenderProjectile(0.6f, new ResourceLocation("wizardry:textures/entity/darkness_orb.png"), true)); + RenderingRegistry.registerEntityRenderingHandler(EntityFirebolt.class, new RenderProjectile(0.2f, new ResourceLocation("wizardry:textures/entity/firebolt.png"), false)); + RenderingRegistry.registerEntityRenderingHandler(EntityLightningDisc.class, new RenderLightningDisc(new ResourceLocation("wizardry:textures/entity/lightning_sigil.png"), 2.0f)); + RenderingRegistry.registerEntityRenderingHandler(EntitySmokeBomb.class, new RenderProjectile(0.6f, new ResourceLocation("wizardry:textures/items/smoke_bomb.png"), false)); // Effects and constructs - RenderingRegistry.registerEntityRenderingHandler(EntityArc.class, RenderArc::new); - RenderingRegistry.registerEntityRenderingHandler(EntityBlackHole.class, RenderBlackHole::new); - RenderingRegistry.registerEntityRenderingHandler(EntityShield.class, RenderBlank::new); - RenderingRegistry.registerEntityRenderingHandler(EntityBubble.class, RenderBubble::new); - RenderingRegistry.registerEntityRenderingHandler(EntityHammer.class, RenderHammer::new); - RenderingRegistry.registerEntityRenderingHandler(EntityIceSpike.class, RenderIceSpike::new); + RenderingRegistry.registerEntityRenderingHandler(EntityArc.class, new RenderArc()); + RenderingRegistry.registerEntityRenderingHandler(EntityBlackHole.class, new RenderBlackHole()); + RenderingRegistry.registerEntityRenderingHandler(EntityShield.class, new RenderBlank()); + RenderingRegistry.registerEntityRenderingHandler(EntityMeteor.class, new RenderMeteor()); + RenderingRegistry.registerEntityRenderingHandler(EntityBubble.class, new RenderBubble()); + RenderingRegistry.registerEntityRenderingHandler(EntityHammer.class, new RenderHammer()); + RenderingRegistry.registerEntityRenderingHandler(EntityFallingGrass.class, new RenderFallingGrass()); + RenderingRegistry.registerEntityRenderingHandler(EntityIceSpike.class, new RenderIceSpike()); // Stuff that doesn't render - RenderingRegistry.registerEntityRenderingHandler(EntityBlizzard.class, RenderBlank::new); - RenderingRegistry.registerEntityRenderingHandler(EntityTornado.class, RenderBlank::new); - RenderingRegistry.registerEntityRenderingHandler(EntityArrowRain.class, RenderBlank::new); - RenderingRegistry.registerEntityRenderingHandler(EntityShadowWraith.class, RenderBlank::new); - RenderingRegistry.registerEntityRenderingHandler(EntityForcefield.class, RenderBlank::new); - RenderingRegistry.registerEntityRenderingHandler(EntityThunderbolt.class, RenderBlank::new); - RenderingRegistry.registerEntityRenderingHandler(EntityStormElemental.class, RenderBlank::new); - RenderingRegistry.registerEntityRenderingHandler(EntityEarthquake.class, RenderBlank::new); - RenderingRegistry.registerEntityRenderingHandler(EntityHailstorm.class, RenderBlank::new); + RenderingRegistry.registerEntityRenderingHandler(EntityBlizzard.class, new RenderBlank()); + RenderingRegistry.registerEntityRenderingHandler(EntityTornado.class, new RenderBlank()); + RenderingRegistry.registerEntityRenderingHandler(EntityArrowRain.class, new RenderBlank()); + RenderingRegistry.registerEntityRenderingHandler(EntityShadowWraith.class, new RenderBlank()); + RenderingRegistry.registerEntityRenderingHandler(EntityForcefield.class, new RenderBlank()); + RenderingRegistry.registerEntityRenderingHandler(EntityThunderbolt.class, new RenderBlank()); + RenderingRegistry.registerEntityRenderingHandler(EntityStormElemental.class, new RenderBlank()); + RenderingRegistry.registerEntityRenderingHandler(EntityEarthquake.class, new RenderBlank()); + RenderingRegistry.registerEntityRenderingHandler(EntityHailstorm.class, new RenderBlank()); // Runes on ground - RenderingRegistry.registerEntityRenderingHandler(EntityHealAura.class, manager -> new RenderSigil(manager, new ResourceLocation(Wizardry.MODID, "textures/entity/healing_aura.png"), 5.0f, false)); - RenderingRegistry.registerEntityRenderingHandler(EntityFireSigil.class, manager -> new RenderSigil(manager, new ResourceLocation(Wizardry.MODID, "textures/entity/fire_sigil.png"), 2.0f, true)); - RenderingRegistry.registerEntityRenderingHandler(EntityFrostSigil.class, manager -> new RenderSigil(manager, new ResourceLocation(Wizardry.MODID, "textures/entity/frost_sigil.png"), 2.0f, true)); - RenderingRegistry.registerEntityRenderingHandler(EntityLightningSigil.class, manager -> new RenderSigil(manager, new ResourceLocation(Wizardry.MODID, "textures/entity/lightning_sigil.png"), 2.0f, true)); - RenderingRegistry.registerEntityRenderingHandler(EntityFireRing.class, manager -> new RenderFireRing(manager, new ResourceLocation(Wizardry.MODID, "textures/entity/ring_of_fire.png"), 5.0f)); - RenderingRegistry.registerEntityRenderingHandler(EntityDecay.class, RenderDecay::new); - RenderingRegistry.registerEntityRenderingHandler(EntityLightningPulse.class, manager -> new RenderLightningPulse(manager, 8.0f)); + RenderingRegistry.registerEntityRenderingHandler(EntityHealAura.class, new RenderSigil(new ResourceLocation("wizardry:textures/entity/healing_aura.png"), 5.0f, false)); + RenderingRegistry.registerEntityRenderingHandler(EntityFireSigil.class, new RenderSigil(new ResourceLocation("wizardry:textures/entity/fire_sigil.png"), 2.0f, true)); + RenderingRegistry.registerEntityRenderingHandler(EntityFrostSigil.class, new RenderSigil(new ResourceLocation("wizardry:textures/entity/frost_sigil.png"), 2.0f, true)); + RenderingRegistry.registerEntityRenderingHandler(EntityLightningSigil.class, new RenderSigil(new ResourceLocation("wizardry:textures/entity/lightning_sigil.png"), 2.0f, true)); + RenderingRegistry.registerEntityRenderingHandler(EntityFireRing.class, new RenderFireRing(new ResourceLocation("wizardry:textures/entity/ring_of_fire.png"), 5.0f)); + RenderingRegistry.registerEntityRenderingHandler(EntityDecay.class, new RenderDecay()); + RenderingRegistry.registerEntityRenderingHandler(EntityLightningPulse.class, new RenderLightningPulse(8.0f)); + + //RenderingRegistry.registerBlockHandler(new RenderSigilBlock()); // TESRs ClientRegistry.bindTileEntitySpecialRenderer(TileEntityArcaneWorkbench.class, new RenderArcaneWorkbench()); - ClientRegistry.bindTileEntitySpecialRenderer(TileEntityStatue.class, renderStatue = new RenderStatue()); + ClientRegistry.bindTileEntitySpecialRenderer(TileEntityStatue.class, new RenderStatue()); ClientRegistry.bindTileEntitySpecialRenderer(TileEntityMagicLight.class, new RenderMagicLight()); + // Items + MinecraftForgeClient.registerItemRenderer(Wizardry.spectralSword, new RenderTranslucentItem()); + MinecraftForgeClient.registerItemRenderer(Wizardry.spectralPickaxe, new RenderTranslucentItem()); + MinecraftForgeClient.registerItemRenderer(Wizardry.spectralBow, new RenderTranslucentItem()); + MinecraftForgeClient.registerItemRenderer(Wizardry.spectralHelmet, new RenderTranslucentItem()); + MinecraftForgeClient.registerItemRenderer(Wizardry.spectralChestplate, new RenderTranslucentItem()); + MinecraftForgeClient.registerItemRenderer(Wizardry.spectralLeggings, new RenderTranslucentItem()); + MinecraftForgeClient.registerItemRenderer(Wizardry.spectralBoots, new RenderTranslucentItem()); + } } \ No newline at end of file diff --git a/src/main/java/electroblob/wizardry/client/EntityNameEntry.java b/src/main/java/electroblob/wizardry/client/EntityNameEntry.java index 3221e189..e3c2b27c 100644 --- a/src/main/java/electroblob/wizardry/client/EntityNameEntry.java +++ b/src/main/java/electroblob/wizardry/client/EntityNameEntry.java @@ -1,20 +1,17 @@ package electroblob.wizardry.client; -import java.util.HashMap; -import java.util.Map; - +import cpw.mods.fml.client.config.GuiButtonExt; +import cpw.mods.fml.client.config.GuiEditArray; +import cpw.mods.fml.client.config.GuiEditArrayEntries; +import cpw.mods.fml.client.config.GuiSelectString; +import cpw.mods.fml.client.config.GuiUtils; +import cpw.mods.fml.client.config.GuiEditArrayEntries.StringEntry; import net.minecraft.client.Minecraft; +import net.minecraft.client.renderer.Tessellator; import net.minecraft.client.resources.I18n; import net.minecraft.entity.EntityList; -import net.minecraftforge.fml.client.config.GuiButtonExt; -import net.minecraftforge.fml.client.config.GuiEditArray; -import net.minecraftforge.fml.client.config.GuiEditArrayEntries; -import net.minecraftforge.fml.client.config.GuiEditArrayEntries.StringEntry; -import net.minecraftforge.fml.client.config.GuiSelectString; -import net.minecraftforge.fml.client.config.IConfigElement; +import cpw.mods.fml.client.config.IConfigElement; -/** [NYI] Intended as a way of choosing entities by name from all those currently registered, within the config file, so - * that users don't have to look up the entity IDs. I can't get this to work correctly at the moment. */ public class EntityNameEntry extends StringEntry { protected final GuiButtonExt btnValue; @@ -26,9 +23,9 @@ public class EntityNameEntry extends StringEntry { this.btnValue = new GuiButtonExt(0, 0, 0, owningEntryList.controlWidth, 18, I18n.format(this.textFieldValue.getText())); //this.btnValue.enabled = owningScreen.enabled; } - + @Override - public void drawEntry(int slotIndex, int x, int y, int listWidth, int slotHeight, int mouseX, int mouseY, boolean isSelected) + public void drawEntry(int slotIndex, int x, int y, int listWidth, int slotHeight, Tessellator tessellator, int mouseX, int mouseY, boolean isSelected) { //super.drawEntry(slotIndex, x, y, listWidth, slotHeight, tessellator, mouseX, mouseY, isSelected); this.btnValue.xPosition = listWidth / 4; @@ -41,18 +38,19 @@ public class EntityNameEntry extends StringEntry { this.btnValue.displayString = this.textFieldValue.getText(); //btnValue.packedFGColour = value ? GuiUtils.getColorCode('2', true) : GuiUtils.getColorCode('4', true); - this.btnValue.drawButton(owningEntryList.getMC(), mouseX, mouseY); + this.btnValue.drawButton(owningEntryList.mc, mouseX, mouseY); } + /** + * Returns true if the mouse has been pressed on this control. + */ @Override public boolean mousePressed(int index, int x, int y, int mouseEvent, int relativeX, int relativeY) { - if (this.btnValue.mousePressed(owningEntryList.getMC(), x, y)) + if (this.btnValue.mousePressed(owningEntryList.mc, x, y)) { - btnValue.playPressSound(owningEntryList.getMC().getSoundHandler()); - // Some sort of type incompatiblity meant that I had to do this first. - Map map = new HashMap(EntityList.CLASS_TO_NAME); - Minecraft.getMinecraft().displayGuiScreen(new GuiSelectString(this.owningScreen, configElement, index, map, this.getValue(), true)); + btnValue.func_146113_a(owningEntryList.mc.getSoundHandler()); + Minecraft.getMinecraft().displayGuiScreen(new GuiSelectString(this.owningScreen, configElement, index, EntityList.classToStringMapping, this.getValue(), true)); owningEntryList.recalculateState(); return true; } @@ -60,6 +58,9 @@ public class EntityNameEntry extends StringEntry { return super.mousePressed(index, x, y, mouseEvent, relativeX, relativeY); } + /** + * Fired when the mouse button is released. Arguments: index, x, y, mouseEvent, relativeX, relativeY + */ @Override public void mouseReleased(int index, int x, int y, int mouseEvent, int relativeX, int relativeY) { diff --git a/src/main/java/electroblob/wizardry/client/GuiArcaneWorkbench.java b/src/main/java/electroblob/wizardry/client/GuiArcaneWorkbench.java index 43f0470a..3c2ba523 100644 --- a/src/main/java/electroblob/wizardry/client/GuiArcaneWorkbench.java +++ b/src/main/java/electroblob/wizardry/client/GuiArcaneWorkbench.java @@ -1,31 +1,44 @@ package electroblob.wizardry.client; -import org.lwjgl.input.Keyboard; +import java.io.ByteArrayOutputStream; +import java.io.DataOutputStream; +import java.util.Iterator; +import java.util.List; +import java.util.Map.Entry; +import org.lwjgl.input.Keyboard; +import org.lwjgl.opengl.GL11; +import org.lwjgl.opengl.GL12; + +import cpw.mods.fml.common.network.simpleimpl.IMessage; +import electroblob.wizardry.ExtendedPlayer; import electroblob.wizardry.SpellGlyphData; -import electroblob.wizardry.WizardData; +import electroblob.wizardry.WandHelper; import electroblob.wizardry.Wizardry; -import electroblob.wizardry.constants.Element; +import electroblob.wizardry.WizardryGuiHandler; import electroblob.wizardry.item.ItemWand; import electroblob.wizardry.packet.PacketControlInput; import electroblob.wizardry.packet.WizardryPacketHandler; import electroblob.wizardry.spell.Spell; import electroblob.wizardry.tileentity.ContainerArcaneWorkbench; +import electroblob.wizardry.tileentity.SlotWizardry; import electroblob.wizardry.tileentity.TileEntityArcaneWorkbench; -import electroblob.wizardry.util.WandHelper; -import electroblob.wizardry.util.WizardryUtilities; import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.FontRenderer; import net.minecraft.client.gui.GuiButton; import net.minecraft.client.gui.inventory.GuiContainer; -import net.minecraft.client.renderer.GlStateManager; +import net.minecraft.client.renderer.RenderHelper; +import net.minecraft.client.renderer.texture.TextureMap; import net.minecraft.client.resources.I18n; import net.minecraft.entity.player.InventoryPlayer; import net.minecraft.inventory.IInventory; import net.minecraft.inventory.Slot; import net.minecraft.item.Item; import net.minecraft.item.ItemStack; +import net.minecraft.nbt.NBTTagCompound; +import net.minecraft.util.EnumChatFormatting; import net.minecraft.util.ResourceLocation; -import net.minecraftforge.fml.common.network.simpleimpl.IMessage; +import net.minecraft.util.StatCollector; public class GuiArcaneWorkbench extends GuiContainer { @@ -66,12 +79,69 @@ public class GuiArcaneWorkbench extends GuiContainer { super.drawScreen(p_73863_1_, p_73863_2_, p_73863_3_); } + /** + * Draw the foreground layer for the GuiContainer (everything in front of the items) + */ + protected void drawGuiContainerForegroundLayer(int mouseX, int mouseY){ + + this.fontRendererObj.drawString(this.arcaneWorkbenchInventory.hasCustomInventoryName() ? this.arcaneWorkbenchInventory.getInventoryName() : I18n.format(this.arcaneWorkbenchInventory.getInventoryName()), 8, 6, 4210752); + this.fontRendererObj.drawString(this.playerInventory.hasCustomInventoryName() ? this.playerInventory.getInventoryName() : I18n.format(this.playerInventory.getInventoryName()), 8, this.ySize - 96 + 2, 4210752); + + if(this.inventorySlots.getSlot(ContainerArcaneWorkbench.WAND_SLOT).getHasStack() && this.inventorySlots.getSlot(ContainerArcaneWorkbench.WAND_SLOT).getStack().getItem() instanceof ItemWand){ + + ItemStack wand = this.inventorySlots.getSlot(ContainerArcaneWorkbench.WAND_SLOT).getStack(); + + this.fontRendererObj.drawStringWithShadow("\u00A7f" + wand.getDisplayName(), xSize + 6, 6, 0); + this.fontRendererObj.drawStringWithShadow("\u00A77" + StatCollector.translateToLocal("container.arcaneWorkbench.mana") + " " + (wand.getMaxDamage() - wand.getItemDamage()) + "/" + wand.getMaxDamage(), xSize + 6, 20, 0); + + Spell[] spells = WandHelper.getSpells(wand); + + int y = 34; + + for(Spell spell : spells){ + + boolean discovered = true; + + if(!this.mc.thePlayer.capabilities.isCreativeMode && ExtendedPlayer.get(this.mc.thePlayer) != null){ + discovered = ExtendedPlayer.get(this.mc.thePlayer).hasSpellBeenDiscovered(spell); + } + + if(discovered){ + this.fontRendererObj.drawStringWithShadow(spell.getDisplayNameWithFormatting(), xSize + 16, y, 0); + }else{ + this.mc.standardGalacticFontRenderer.drawStringWithShadow("\u00A79" + SpellGlyphData.getGlyphName(spell, this.mc.theWorld), xSize + 16, y, 0); + } + y += 10; + } + + if(WandHelper.getTotalUpgrades(wand) > 0){ + + this.fontRendererObj.drawStringWithShadow("\u00A7f" + StatCollector.translateToLocal("container.arcaneWorkbench.upgrades"), xSize + 6, y + 6, 0); + + int x = 0; + y = guiTop + 50 + spells.length*10; + // Wand upgrade tooltips + for(Item item : WandHelper.getSpecialUpgrades()){ + + int level = WandHelper.getUpgradeLevel(wand, item); + + if(level > 0){ + // No idea why this is -16 instead of +6, but it works! + if(isPointInRegion(xSize -16 + x, y, 16, 16, mouseX + guiLeft, mouseY + guiTop)){ + ItemStack stack = new ItemStack(item, level); + this.drawItemStackTooltip(stack, mouseX - guiLeft, mouseY - guiTop); + } + x += 18; + } + } + } + } + } + @Override public void drawGuiContainerBackgroundLayer(float f, int mouseX, int mouseY) { - GlStateManager.pushAttrib(); - - GlStateManager.color(1F, 1F, 1F, 1F); + GL11.glColor4f(1F, 1F, 1F, 1F); Minecraft.getMinecraft().renderEngine.bindTexture(texture); // Main inventory @@ -82,7 +152,7 @@ public class GuiArcaneWorkbench extends GuiContainer { Slot slot = this.inventorySlots.getSlot(i); if(slot.xDisplayPosition >=0 && slot.yDisplayPosition >= 0) this.drawTexturedModalRect(guiLeft + slot.xDisplayPosition - 10, guiTop + slot.yDisplayPosition - 10, - 0, 220, 36, 36); + 64, 220, 36, 36); } // Tooltip only drawn if there is a wand @@ -103,15 +173,12 @@ public class GuiArcaneWorkbench extends GuiContainer { boolean discovered = true; - if(!this.mc.thePlayer.capabilities.isCreativeMode && WizardData.get(this.mc.thePlayer) != null){ - discovered = WizardData.get(this.mc.thePlayer).hasSpellBeenDiscovered(spell); + if(!this.mc.thePlayer.capabilities.isCreativeMode && ExtendedPlayer.get(this.mc.thePlayer) != null){ + discovered = ExtendedPlayer.get(this.mc.thePlayer).hasSpellBeenDiscovered(spell); } - // As of Wizardry 1.2, the icons have been split off into their own texture files to allow for add-on - // mods to add their own. - Minecraft.getMinecraft().renderEngine.bindTexture(discovered ? spell.element.getIcon() : Element.MAGIC.getIcon()); - // Renders the little element icon - WizardryUtilities.drawTexturedRect(guiLeft + xSize + 5, guiTop + 34 + 10*i++, 8, 8); + // Renders the little element icon if there is an element + this.drawTexturedModalRect(guiLeft + xSize + 5, guiTop + 34 + 10*i++, discovered ? spell.element.ordinal()*8 : 0, 220, 8, 8); } int x = 0; @@ -124,84 +191,39 @@ public class GuiArcaneWorkbench extends GuiContainer { if(level > 0){ ItemStack stack = new ItemStack(item, level); - GlStateManager.enableDepth(); - this.itemRender.renderItemAndEffectIntoGUI(stack, guiLeft + xSize + 6 + x, y); - this.itemRender.renderItemOverlayIntoGUI(this.fontRendererObj, stack, guiLeft + xSize + 6 + x, y, null); + this.itemRender.renderItemAndEffectIntoGUI(this.fontRendererObj, this.mc.getTextureManager(), stack, guiLeft + xSize + 6 + x, y); + this.itemRender.renderItemOverlayIntoGUI(this.fontRendererObj, this.mc.getTextureManager(), stack, guiLeft + xSize + 6 + x, y); x += 18; - GlStateManager.disableDepth(); } } } Minecraft.getMinecraft().renderEngine.bindTexture(texture); - - // Fixes the bug that caused the slot hightlight to render opaque. I don't know why it works, it just works! - GlStateManager.disableBlend(); - GlStateManager.enableAlpha(); - - GlStateManager.popAttrib(); - } - @Override - protected void drawGuiContainerForegroundLayer(int mouseX, int mouseY){ + for(Object object : this.inventorySlots.inventorySlots){ + if(object instanceof Slot){ - this.fontRendererObj.drawString(this.arcaneWorkbenchInventory.hasCustomName() ? this.arcaneWorkbenchInventory.getName() : I18n.format(this.arcaneWorkbenchInventory.getName()), 8, 6, 4210752); - this.fontRendererObj.drawString(this.playerInventory.hasCustomName() ? this.playerInventory.getName() : I18n.format(this.playerInventory.getName()), 8, this.ySize - 96 + 2, 4210752); + Slot slot = (Slot)object; - if(this.inventorySlots.getSlot(ContainerArcaneWorkbench.WAND_SLOT).getHasStack() && this.inventorySlots.getSlot(ContainerArcaneWorkbench.WAND_SLOT).getStack().getItem() instanceof ItemWand){ - - ItemStack wand = this.inventorySlots.getSlot(ContainerArcaneWorkbench.WAND_SLOT).getStack(); - - this.fontRendererObj.drawStringWithShadow("\u00A7f" + wand.getDisplayName(), xSize + 6, 6, 0); - this.fontRendererObj.drawStringWithShadow("\u00A77" + I18n.format("container.wizardry:arcane_workbench.mana") + " " + (wand.getMaxDamage() - wand.getItemDamage()) + "/" + wand.getMaxDamage(), xSize + 6, 20, 0); - - Spell[] spells = WandHelper.getSpells(wand); - - int y = 34; - - for(Spell spell : spells){ - - boolean discovered = true; - - if(!this.mc.thePlayer.capabilities.isCreativeMode && WizardData.get(this.mc.thePlayer) != null){ - discovered = WizardData.get(this.mc.thePlayer).hasSpellBeenDiscovered(spell); - } - - if(discovered){ - this.fontRendererObj.drawStringWithShadow(spell.getDisplayNameWithFormatting(), xSize + 16, y, 0); - }else{ - this.mc.standardGalacticFontRenderer.drawStringWithShadow("\u00A79" + SpellGlyphData.getGlyphName(spell, this.mc.theWorld), xSize + 16, y, 0); - } - y += 10; - } - - if(WandHelper.getTotalUpgrades(wand) > 0){ - - this.fontRendererObj.drawStringWithShadow("\u00A7f" + I18n.format("container.wizardry:arcane_workbench.upgrades"), xSize + 6, y + 6, 0); - - int x = 0; - y = 50 + spells.length*10; - // Wand upgrade tooltips - for(Item item : WandHelper.getSpecialUpgrades()){ - - int level = WandHelper.getUpgradeLevel(wand, item); - - if(level > 0){ - // The javadoc for isPointInRegion is ambiguous; what it means is that the REGION is - // relative to the GUI but the POINT isn't. - if(isPointInRegion(xSize + 6 + x, y, 16, 16, mouseX, mouseY)){ - ItemStack stack = new ItemStack(item, level); - this.renderToolTip(stack, mouseX - guiLeft, mouseY - guiTop); - } - x += 18; - } + // Tests whether I have added a background texture and if so actually renders it + if(!slot.getHasStack() && slot instanceof SlotWizardry && ((SlotWizardry)slot).backgroundIndex > -1){ + // Taken directly from GuiContainer#func_146977_a, with a few changes. + this.zLevel = 100.0F; + itemRender.zLevel = 100.0F; + GL11.glDisable(GL11.GL_LIGHTING); + GL11.glEnable(GL11.GL_BLEND); + this.drawTexturedModalRect(guiLeft + slot.xDisplayPosition, guiTop + slot.yDisplayPosition, ((SlotWizardry)slot).backgroundIndex*16, 240, 16, 16); + GL11.glDisable(GL11.GL_BLEND); + GL11.glEnable(GL11.GL_LIGHTING); + itemRender.zLevel = 0.0F; + this.zLevel = 0.0F; } } } } - @Override - public void initGui(){ + public void initGui() + { this.mc.thePlayer.openContainer = this.inventorySlots; this.guiLeft = (this.width - this.xSize) / 2; this.guiTop = (this.height - this.ySize) / 2; @@ -210,21 +232,133 @@ public class GuiArcaneWorkbench extends GuiContainer { this.buttonList.add(this.applyBtn = new GuiButtonApply(0, this.width/2 + 48, this.height/2 + 3)); } - @Override - public void onGuiClosed(){ + public void onGuiClosed() + { super.onGuiClosed(); Keyboard.enableRepeatEvents(false); } - @Override - protected void actionPerformed(GuiButton button){ - if(button.enabled){ - if(button.id == 0){ + /** + * Fired when a control is clicked. This is the equivalent of ActionListener.actionPerformed(ActionEvent e). + */ + protected void actionPerformed(GuiButton par1GuiButton) + { + if (par1GuiButton.enabled) + { + if (par1GuiButton.id == 0) + { // Packet building - IMessage msg = new PacketControlInput.Message(PacketControlInput.ControlType.APPLY_BUTTON); + IMessage msg = new PacketControlInput.Message(0); WizardryPacketHandler.net.sendToServer(msg); } } } + + protected boolean isPointInRegion(int par1, int par2, int par3, int par4, int par5, int par6) + { + int k1 = this.width/2 - xSize/2; + int l1 = this.height/2 - this.ySize/2; + par5 -= k1; + par6 -= l1; + return par5 >= par1 - 1 && par5 < par1 + par3 + 1 && par6 >= par2 - 1 && par6 < par2 + par4 + 1; + } + + protected void drawItemStackTooltip(ItemStack par1ItemStack, int par2, int par3) + { + List list = par1ItemStack.getTooltip(this.mc.thePlayer, this.mc.gameSettings.advancedItemTooltips); + + for (int k = 0; k < list.size(); ++k) + { + if (k == 0) + { + list.set(k, par1ItemStack.getRarity().rarityColor + (String)list.get(k)); + } + else + { + list.set(k, EnumChatFormatting.GRAY + (String)list.get(k)); + } + } + + FontRenderer font = par1ItemStack.getItem().getFontRenderer(par1ItemStack); + drawHoveringText(list, par2, par3, (font == null ? fontRendererObj : font)); + } + + protected void drawHoveringText(List par1List, int par2, int par3, FontRenderer font) + { + if (!par1List.isEmpty()) + { + GL11.glDisable(GL12.GL_RESCALE_NORMAL); + RenderHelper.disableStandardItemLighting(); + GL11.glDisable(GL11.GL_LIGHTING); + GL11.glDisable(GL11.GL_DEPTH_TEST); + int k = 0; + Iterator iterator = par1List.iterator(); + + while (iterator.hasNext()) + { + String s = (String)iterator.next(); + int l = font.getStringWidth(s); + + if (l > k) + { + k = l; + } + } + + int i1 = par2 + 12; + int j1 = par3 - 12; + int k1 = 8; + + if (par1List.size() > 1) + { + k1 += 2 + (par1List.size() - 1) * 10; + } + + if (i1 + k > this.width) + { + i1 -= 28 + k; + } + + if (j1 + k1 + 6 > this.height) + { + j1 = this.height - k1 - 6; + } + + this.zLevel = 300.0F; + itemRender.zLevel = 300.0F; + int l1 = -267386864; + this.drawGradientRect(i1 - 3, j1 - 4, i1 + k + 3, j1 - 3, l1, l1); + this.drawGradientRect(i1 - 3, j1 + k1 + 3, i1 + k + 3, j1 + k1 + 4, l1, l1); + this.drawGradientRect(i1 - 3, j1 - 3, i1 + k + 3, j1 + k1 + 3, l1, l1); + this.drawGradientRect(i1 - 4, j1 - 3, i1 - 3, j1 + k1 + 3, l1, l1); + this.drawGradientRect(i1 + k + 3, j1 - 3, i1 + k + 4, j1 + k1 + 3, l1, l1); + int i2 = 1347420415; + int j2 = (i2 & 16711422) >> 1 | i2 & -16777216; + this.drawGradientRect(i1 - 3, j1 - 3 + 1, i1 - 3 + 1, j1 + k1 + 3 - 1, i2, j2); + this.drawGradientRect(i1 + k + 2, j1 - 3 + 1, i1 + k + 3, j1 + k1 + 3 - 1, i2, j2); + this.drawGradientRect(i1 - 3, j1 - 3, i1 + k + 3, j1 - 3 + 1, i2, i2); + this.drawGradientRect(i1 - 3, j1 + k1 + 2, i1 + k + 3, j1 + k1 + 3, j2, j2); + + for(int k2 = 0; k2 < par1List.size(); ++k2){ + + String s1 = (String)par1List.get(k2); + font.drawStringWithShadow(s1, i1, j1, -1); + + if(k2 == 0){ + j1 += 2; + } + + j1 += 10; + } + + this.zLevel = 0.0F; + itemRender.zLevel = 0.0F; + GL11.glEnable(GL11.GL_LIGHTING); + GL11.glEnable(GL11.GL_DEPTH_TEST); + RenderHelper.enableStandardItemLighting(); + GL11.glEnable(GL12.GL_RESCALE_NORMAL); + } + } + +} -} \ No newline at end of file diff --git a/src/main/java/electroblob/wizardry/client/GuiButtonApply.java b/src/main/java/electroblob/wizardry/client/GuiButtonApply.java index fd54d344..8b285336 100644 --- a/src/main/java/electroblob/wizardry/client/GuiButtonApply.java +++ b/src/main/java/electroblob/wizardry/client/GuiButtonApply.java @@ -1,32 +1,39 @@ package electroblob.wizardry.client; -import electroblob.wizardry.util.WizardryUtilities; +import cpw.mods.fml.relauncher.Side; +import cpw.mods.fml.relauncher.SideOnly; import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.FontRenderer; import net.minecraft.client.gui.GuiButton; -import net.minecraft.client.resources.I18n; -import net.minecraftforge.fml.relauncher.Side; -import net.minecraftforge.fml.relauncher.SideOnly; +import net.minecraft.client.renderer.Tessellator; +import net.minecraft.util.ResourceLocation; +import net.minecraft.util.StatCollector; + +import org.lwjgl.opengl.GL11; @SideOnly(Side.CLIENT) class GuiButtonApply extends GuiButton { + + private static final ResourceLocation texture = new ResourceLocation("wizardry:textures/gui/handbook.png"); public GuiButtonApply(int id, int x, int y){ - super(id, x, y, 32, 16, I18n.format("container.wizardry:arcane_workbench.apply")); + super(id, x, y, 32, 16, StatCollector.translateToLocal("container.arcaneWorkbench.apply")); } - @Override - public void drawButton(Minecraft minecraft, int mouseX, int mouseY){ - + /** + * Draws this button to the screen. + */ + public void drawButton(Minecraft minecraft, int par2, int par3){ // Whether the button is highlighted - this.hovered = mouseX >= this.xPosition && mouseY >= this.yPosition && mouseX < this.xPosition + this.width && mouseY < this.yPosition + this.height; - - int k = 36; + this.field_146123_n = par2 >= this.xPosition && par3 >= this.yPosition && par2 < this.xPosition + this.width && par3 < this.yPosition + this.height; + + int k = 100; int l = 220; int colour = 14737632; if(this.enabled){ - if(this.hovered){ + if(this.field_146123_n){ k += this.width*2; colour = 16777120; } @@ -35,7 +42,32 @@ class GuiButtonApply extends GuiButton { colour = 10526880; } - WizardryUtilities.drawTexturedRect(this.xPosition, this.yPosition, k, l, this.width, this.height, 256, 256); - this.drawCenteredString(minecraft.fontRendererObj, this.displayString, this.xPosition + this.width / 2, this.yPosition + (this.height - 8) / 2, colour); + this.drawTexturedRect(this.xPosition, this.yPosition, k, l, this.width, this.height, 256, 256); + this.drawCenteredString(minecraft.fontRenderer, this.displayString, this.xPosition + this.width / 2, this.yPosition + (this.height - 8) / 2, colour); + } -} \ No newline at end of file + + /** + * Draws a textured rectangle, taking the size of the image and the bit needed into account. + * @param x The x position of the rectangle + * @param y The y position of the rectangle + * @param u The x position of the top left corner of the section of the image wanted + * @param v The y position of the top left corner of the section of the image wanted + * @param width The width of the section + * @param height The height of the section + * @param textureWidth The width of the actual image. + * @param textureHeight The height of the actual image. + */ + public void drawTexturedRect(int x, int y, int u, int v, int width, int height, int textureWidth, int textureHeight) + { + float f = 1F / (float)textureWidth; + float f1 = 1F / (float)textureHeight; + Tessellator tessellator = Tessellator.instance; + tessellator.startDrawingQuads(); + tessellator.addVertexWithUV((double)(x), (double)(y + height), 0, (double)((float)(u) * f), (double)((float)(v + height) * f1)); + tessellator.addVertexWithUV((double)(x + width), (double)(y + height), 0, (double)((float)(u + width) * f), (double)((float)(v + height) * f1)); + tessellator.addVertexWithUV((double)(x + width), (double)(y), 0, (double)((float)(u + width) * f), (double)((float)(v) * f1)); + tessellator.addVertexWithUV((double)(x), (double)(y), 0, (double)((float)(u) * f), (double)((float)(v) * f1)); + tessellator.draw(); + } +} diff --git a/src/main/java/electroblob/wizardry/client/GuiButtonInvisible.java b/src/main/java/electroblob/wizardry/client/GuiButtonInvisible.java index 3cc2f12e..c2b32055 100644 --- a/src/main/java/electroblob/wizardry/client/GuiButtonInvisible.java +++ b/src/main/java/electroblob/wizardry/client/GuiButtonInvisible.java @@ -1,9 +1,13 @@ package electroblob.wizardry.client; +import cpw.mods.fml.relauncher.Side; +import cpw.mods.fml.relauncher.SideOnly; import net.minecraft.client.Minecraft; import net.minecraft.client.gui.GuiButton; -import net.minecraftforge.fml.relauncher.Side; -import net.minecraftforge.fml.relauncher.SideOnly; +import net.minecraft.client.renderer.Tessellator; +import net.minecraft.util.ResourceLocation; + +import org.lwjgl.opengl.GL11; @SideOnly(Side.CLIENT) class GuiButtonInvisible extends GuiButton { @@ -17,7 +21,7 @@ class GuiButtonInvisible extends GuiButton { * Draws this button to the screen. */ public void drawButton(Minecraft par1Minecraft, int par2, int par3){ - this.hovered = par2 >= this.xPosition && par3 >= this.yPosition && par2 < this.xPosition + this.width && par3 < this.yPosition + this.height; + this.field_146123_n = par2 >= this.xPosition && par3 >= this.yPosition && par2 < this.xPosition + this.width && par3 < this.yPosition + this.height; } } diff --git a/src/main/java/electroblob/wizardry/client/GuiButtonNextPage.java b/src/main/java/electroblob/wizardry/client/GuiButtonNextPage.java new file mode 100644 index 00000000..8960e641 --- /dev/null +++ b/src/main/java/electroblob/wizardry/client/GuiButtonNextPage.java @@ -0,0 +1,78 @@ +package electroblob.wizardry.client; + +import cpw.mods.fml.relauncher.Side; +import cpw.mods.fml.relauncher.SideOnly; +import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.GuiButton; +import net.minecraft.client.renderer.Tessellator; +import net.minecraft.util.ResourceLocation; + +import org.lwjgl.opengl.GL11; + +@SideOnly(Side.CLIENT) +class GuiButtonNextPage extends GuiButton +{ + /** + * True for pointing right (next page), false for pointing left (previous page). + */ + private final boolean nextPage; + + private static final ResourceLocation texture = new ResourceLocation("wizardry:textures/gui/handbook.png"); + + public GuiButtonNextPage(int id, int x, int y, boolean par4) + { + super(id, x, y, 23, 13, ""); + this.nextPage = par4; + } + + /** + * Draws this button to the screen. + */ + public void drawButton(Minecraft par1Minecraft, int par2, int par3) + { + if (this.visible) + { + boolean flag = par2 >= this.xPosition && par3 >= this.yPosition && par2 < this.xPosition + this.width && par3 < this.yPosition + this.height; + GL11.glColor4f(1.0F, 1.0F, 1.0F, 1.0F); + par1Minecraft.getTextureManager().bindTexture(texture); + int k = 0; + int l = 192; + + if (flag) + { + k += 23; + } + + if (!this.nextPage) + { + l += 13; + } + + this.drawTexturedRect(this.xPosition, this.yPosition, k, l, 23, 13, 288, 256); + } + } + + /** + * Draws a textured rectangle, taking the size of the image and the bit needed into account. + * @param x The x position of the rectangle + * @param y The y position of the rectangle + * @param u The x position of the top left corner of the section of the image wanted + * @param v The y position of the top left corner of the section of the image wanted + * @param width The width of the section + * @param height The height of the section + * @param textureWidth The width of the actual image. + * @param textureHeight The height of the actual image. + */ + public void drawTexturedRect(int x, int y, int u, int v, int width, int height, int textureWidth, int textureHeight) + { + float f = 1F / (float)textureWidth; + float f1 = 1F / (float)textureHeight; + Tessellator tessellator = Tessellator.instance; + tessellator.startDrawingQuads(); + tessellator.addVertexWithUV((double)(x), (double)(y + height), 0, (double)((float)(u) * f), (double)((float)(v + height) * f1)); + tessellator.addVertexWithUV((double)(x + width), (double)(y + height), 0, (double)((float)(u + width) * f), (double)((float)(v + height) * f1)); + tessellator.addVertexWithUV((double)(x + width), (double)(y), 0, (double)((float)(u + width) * f), (double)((float)(v) * f1)); + tessellator.addVertexWithUV((double)(x), (double)(y), 0, (double)((float)(u) * f), (double)((float)(v) * f1)); + tessellator.draw(); + } +} diff --git a/src/main/java/electroblob/wizardry/client/GuiButtonTurnPage.java b/src/main/java/electroblob/wizardry/client/GuiButtonTurnPage.java deleted file mode 100644 index b4b7e68b..00000000 --- a/src/main/java/electroblob/wizardry/client/GuiButtonTurnPage.java +++ /dev/null @@ -1,52 +0,0 @@ -package electroblob.wizardry.client; - -import electroblob.wizardry.Wizardry; -import electroblob.wizardry.util.WizardryUtilities; -import net.minecraft.client.Minecraft; -import net.minecraft.client.gui.GuiButton; -import net.minecraft.client.renderer.GlStateManager; -import net.minecraft.util.ResourceLocation; -import net.minecraftforge.fml.relauncher.Side; -import net.minecraftforge.fml.relauncher.SideOnly; - -@SideOnly(Side.CLIENT) -class GuiButtonTurnPage extends GuiButton { - - /** True for pointing right (next page), false for pointing left (previous page). */ - private final boolean nextPage; - - private static final ResourceLocation texture = new ResourceLocation(Wizardry.MODID, "textures/gui/handbook.png"); - - public GuiButtonTurnPage(int id, int x, int y, boolean isNextPage) - { - super(id, x, y, 23, 13, ""); - this.nextPage = isNextPage; - } - - /** - * Draws this button to the screen. - */ - public void drawButton(Minecraft par1Minecraft, int par2, int par3) - { - if (this.visible) - { - boolean flag = par2 >= this.xPosition && par3 >= this.yPosition && par2 < this.xPosition + this.width && par3 < this.yPosition + this.height; - GlStateManager.color(1.0F, 1.0F, 1.0F, 1.0F); - par1Minecraft.getTextureManager().bindTexture(texture); - int k = 0; - int l = 192; - - if (flag) - { - k += 23; - } - - if (!this.nextPage) - { - l += 13; - } - - WizardryUtilities.drawTexturedRect(this.xPosition, this.yPosition, k, l, 23, 13, 288, 256); - } - } -} diff --git a/src/main/java/electroblob/wizardry/client/GuiConfigWizardry.java b/src/main/java/electroblob/wizardry/client/GuiConfigWizardry.java index 587a8603..dacdadf7 100644 --- a/src/main/java/electroblob/wizardry/client/GuiConfigWizardry.java +++ b/src/main/java/electroblob/wizardry/client/GuiConfigWizardry.java @@ -3,30 +3,30 @@ package electroblob.wizardry.client; import java.util.ArrayList; import java.util.List; -import electroblob.wizardry.Settings; +import cpw.mods.fml.client.config.DummyConfigElement.DummyCategoryElement; +import cpw.mods.fml.client.config.GuiConfig; +import cpw.mods.fml.client.config.GuiConfigEntries; +import cpw.mods.fml.client.config.GuiConfigEntries.CategoryEntry; +import cpw.mods.fml.client.config.IConfigElement; import electroblob.wizardry.Wizardry; import net.minecraft.client.gui.GuiScreen; -import net.minecraft.client.resources.I18n; +import net.minecraft.util.StatCollector; import net.minecraftforge.common.config.ConfigElement; import net.minecraftforge.common.config.Configuration; -import net.minecraftforge.fml.client.config.DummyConfigElement.DummyCategoryElement; -import net.minecraftforge.fml.client.config.GuiConfig; -import net.minecraftforge.fml.client.config.GuiConfigEntries; -import net.minecraftforge.fml.client.config.GuiConfigEntries.CategoryEntry; -import net.minecraftforge.fml.client.config.IConfigElement; public class GuiConfigWizardry extends GuiConfig { public GuiConfigWizardry(GuiScreen parent){ - super(parent, getConfigEntries(), Wizardry.MODID, false, false, Wizardry.NAME + " - " + I18n.format("config.wizardry.title.general")); + super(parent, getConfigEntries(), Wizardry.MODID, false, false, Wizardry.NAME + " - " + StatCollector.translateToLocal("config.title.general")); //this.titleLine2 = "File location: " + Wizardry.config.getConfigFile().getAbsolutePath(); } private static List getConfigEntries(){ List configList = new ArrayList(1); - configList.add(new DummyCategoryElement("spellsConfig", "config.wizardry.category." + Settings.SPELLS_CATEGORY, SpellsCategory.class)); - configList.add(new DummyCategoryElement("resistancesConfig", "config.wizardry.category." + Settings.RESISTANCES_CATEGORY, ResistancesCategory.class)); - configList.addAll(new ConfigElement(Wizardry.settings.getConfigCategory(Configuration.CATEGORY_GENERAL)).getChildElements()); + configList.add(new DummyCategoryElement("spellsConfig", "config.category.spells", SpellsCategory.class)); + configList.add(new DummyCategoryElement("resistancesConfig", "config.category.resistances", ResistancesCategory.class)); + configList.add(new DummyCategoryElement("idsConfig", "config.category.ids", IDsCategory.class)); + configList.addAll(new ConfigElement(Wizardry.config.getCategory(Configuration.CATEGORY_GENERAL)).getChildElements()); return configList; } @@ -44,11 +44,11 @@ public class GuiConfigWizardry extends GuiConfig { // This GuiConfig object specifies the configID of the object and as such will force-save when it is closed. // The parent GuiConfig object's entryList will also be refreshed to reflect the changes. GuiConfig spellsMenu = new GuiConfig(this.owningScreen, - (new ConfigElement(Wizardry.settings.getConfigCategory(Settings.SPELLS_CATEGORY))).getChildElements(), - this.owningScreen.modID, Settings.SPELLS_CATEGORY, false, false, - Wizardry.NAME + " - " + I18n.format("config.wizardry.title." + Settings.SPELLS_CATEGORY)); + (new ConfigElement(Wizardry.config.getCategory(Wizardry.SPELLS_CATEGORY))).getChildElements(), + this.owningScreen.modID, Wizardry.SPELLS_CATEGORY, false, false, + Wizardry.NAME + " - " + StatCollector.translateToLocal("config.title.spells")); - spellsMenu.titleLine2 = I18n.format("config.wizardry.subtitle." + Settings.SPELLS_CATEGORY); + spellsMenu.titleLine2 = StatCollector.translateToLocal("config.subtitle.spells"); return spellsMenu; } @@ -68,11 +68,35 @@ public class GuiConfigWizardry extends GuiConfig { // This GuiConfig object specifies the configID of the object and as such will force-save when it is closed. // The parent GuiConfig object's entryList will also be refreshed to reflect the changes. GuiConfig idsMenu = new GuiConfig(this.owningScreen, - (new ConfigElement(Wizardry.settings.getConfigCategory(Settings.RESISTANCES_CATEGORY))).getChildElements(), - this.owningScreen.modID, Settings.RESISTANCES_CATEGORY, false, false, - Wizardry.NAME + " - " + I18n.format("config.wizardry.title." + Settings.RESISTANCES_CATEGORY)); + (new ConfigElement(Wizardry.config.getCategory(Wizardry.RESISTANCES_CATEGORY))).getChildElements(), + this.owningScreen.modID, Wizardry.RESISTANCES_CATEGORY, false, false, + Wizardry.NAME + " - " + StatCollector.translateToLocal("config.title.resistances")); - idsMenu.titleLine2 = I18n.format("config.wizardry.subtitle." + Settings.RESISTANCES_CATEGORY); + idsMenu.titleLine2 = StatCollector.translateToLocal("config.subtitle.resistances"); + + return idsMenu; + } + } + + /** IDs category of the config gui. */ + public static class IDsCategory extends CategoryEntry + { + public IDsCategory(GuiConfig owningScreen, GuiConfigEntries owningEntryList, IConfigElement prop) + { + super(owningScreen, owningEntryList, prop); + } + + @Override + protected GuiScreen buildChildScreen() + { + // This GuiConfig object specifies the configID of the object and as such will force-save when it is closed. + // The parent GuiConfig object's entryList will also be refreshed to reflect the changes. + GuiConfig idsMenu = new GuiConfig(this.owningScreen, + (new ConfigElement(Wizardry.config.getCategory(Wizardry.IDS_CATEGORY))).getChildElements(), + this.owningScreen.modID, Wizardry.IDS_CATEGORY, false, false, + Wizardry.NAME + " - " + StatCollector.translateToLocal("config.title.ids")); + + idsMenu.titleLine2 = StatCollector.translateToLocal("config.subtitle.ids"); return idsMenu; } diff --git a/src/main/java/electroblob/wizardry/client/GuiPortableCrafting.java b/src/main/java/electroblob/wizardry/client/GuiPortableCrafting.java index d4f8e225..0ff67b7d 100644 --- a/src/main/java/electroblob/wizardry/client/GuiPortableCrafting.java +++ b/src/main/java/electroblob/wizardry/client/GuiPortableCrafting.java @@ -1,12 +1,13 @@ package electroblob.wizardry.client; +import org.lwjgl.opengl.GL11; + +import electroblob.wizardry.WizardryGuiHandler; import net.minecraft.client.gui.inventory.GuiContainer; -import net.minecraft.client.renderer.GlStateManager; import net.minecraft.client.resources.I18n; import net.minecraft.entity.player.InventoryPlayer; import net.minecraft.inventory.ContainerWorkbench; import net.minecraft.util.ResourceLocation; -import net.minecraft.util.math.BlockPos; import net.minecraft.world.World; /** Crafting table GUI that doesn't require a crafting table container object. */ @@ -14,9 +15,9 @@ public class GuiPortableCrafting extends GuiContainer { private static final ResourceLocation craftingTableGuiTextures = new ResourceLocation("textures/gui/container/crafting_table.png"); - public GuiPortableCrafting(InventoryPlayer p_i1084_1_, World p_i1084_2_, BlockPos pos) + public GuiPortableCrafting(InventoryPlayer p_i1084_1_, World p_i1084_2_, int p_i1084_3_, int p_i1084_4_, int p_i1084_5_) { - super(new ContainerWorkbench(p_i1084_1_, p_i1084_2_, pos)); + super(new ContainerWorkbench(p_i1084_1_, p_i1084_2_, p_i1084_3_, p_i1084_4_, p_i1084_5_)); } /** @@ -30,7 +31,7 @@ public class GuiPortableCrafting extends GuiContainer { protected void drawGuiContainerBackgroundLayer(float p_146976_1_, int p_146976_2_, int p_146976_3_) { - GlStateManager.color(1.0F, 1.0F, 1.0F, 1.0F); + GL11.glColor4f(1.0F, 1.0F, 1.0F, 1.0F); this.mc.getTextureManager().bindTexture(craftingTableGuiTextures); int k = (this.width - this.xSize) / 2; int l = (this.height - this.ySize) / 2; diff --git a/src/main/java/electroblob/wizardry/client/GuiSpellBook.java b/src/main/java/electroblob/wizardry/client/GuiSpellBook.java index c7a90cb2..ea5370f7 100644 --- a/src/main/java/electroblob/wizardry/client/GuiSpellBook.java +++ b/src/main/java/electroblob/wizardry/client/GuiSpellBook.java @@ -2,16 +2,18 @@ package electroblob.wizardry.client; import org.lwjgl.input.Keyboard; +import electroblob.wizardry.EnumTier; +import electroblob.wizardry.ExtendedPlayer; import electroblob.wizardry.SpellGlyphData; -import electroblob.wizardry.WizardData; import electroblob.wizardry.Wizardry; -import electroblob.wizardry.constants.Tier; -import electroblob.wizardry.registry.Spells; +import electroblob.wizardry.WizardryGuiHandler; +import electroblob.wizardry.WizardryRegistry; import electroblob.wizardry.spell.Spell; -import electroblob.wizardry.util.WizardryUtilities; import net.minecraft.client.Minecraft; import net.minecraft.client.gui.GuiScreen; +import net.minecraft.client.renderer.Tessellator; import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.util.EnumChatFormatting; import net.minecraft.util.ResourceLocation; public class GuiSpellBook extends GuiScreen { @@ -39,17 +41,17 @@ public class GuiSpellBook extends GuiScreen { EntityPlayer player = Minecraft.getMinecraft().thePlayer; boolean discovered = true; - if(Wizardry.settings.discoveryMode && !player.capabilities.isCreativeMode && WizardData.get(player) != null - && !WizardData.get(player).hasSpellBeenDiscovered(spell)){ + if(Wizardry.discoveryMode && !player.capabilities.isCreativeMode && ExtendedPlayer.get(player) != null + && !ExtendedPlayer.get(player).hasSpellBeenDiscovered(spell)){ discovered = false; } // Draws spell illustration on opposite page, underneath the book so it shows through the hole. - Minecraft.getMinecraft().renderEngine.bindTexture(discovered ? spell.getIcon() : Spells.none.getIcon()); - WizardryUtilities.drawTexturedRect(xPos + 145, yPos + 20, 0, 0, 128, 128, 128, 128); + Minecraft.getMinecraft().renderEngine.bindTexture(discovered ? spell.icon : WizardryRegistry.none.icon); + this.drawTexturedRect(xPos + 145, yPos + 20, 0, 0, 128, 128, 128, 128); Minecraft.getMinecraft().renderEngine.bindTexture(texture); - WizardryUtilities.drawTexturedRect(xPos, yPos, 0, 0, xSize, ySize, xSize, 256); + this.drawTexturedRect(xPos, yPos, 0, 0, xSize, ySize, xSize, 256); super.drawScreen(par1, par2, par3); @@ -63,14 +65,14 @@ public class GuiSpellBook extends GuiScreen { this.fontRendererObj.drawString("-------------------", xPos+17, yPos+34, 0); - if(spell.tier == Tier.BASIC){ + if(spell.tier == EnumTier.BASIC){ // Basic is usually white but this doesn't show up. - this.fontRendererObj.drawString("Tier: \u00A77" + Tier.BASIC.getDisplayName(), xPos+17, yPos+44, 0); + this.fontRendererObj.drawString("Tier: \u00A77" + EnumTier.BASIC.getDisplayName(), xPos+17, yPos+44, 0); }else{ this.fontRendererObj.drawString("Tier: " + spell.tier.getDisplayNameWithFormatting(), xPos+17, yPos+44, 0); } - String element = "Element: " + spell.element.getFormattingCode() + spell.element.getDisplayName(); + String element = "Element: " + spell.element.colour + spell.element.getDisplayName(); if(!discovered) element = "Element: ?"; this.fontRendererObj.drawString(element, xPos+17, yPos+56, 0); @@ -123,5 +125,29 @@ public class GuiSpellBook extends GuiScreen { super.onGuiClosed(); Keyboard.enableRepeatEvents(false); } + + /** + * Draws a textured rectangle, taking the size of the image and the bit needed into account. Client side only, of course. + * @param x The x position of the rectangle + * @param y The y position of the rectangle + * @param u The x position of the top left corner of the section of the image wanted + * @param v The y position of the top left corner of the section of the image wanted + * @param width The width of the section + * @param height The height of the section + * @param textureWidth The width of the actual image. + * @param textureHeight The height of the actual image. + */ + public static void drawTexturedRect(int x, int y, int u, int v, int width, int height, int textureWidth, int textureHeight) + { + float f = 1F / (float)textureWidth; + float f1 = 1F / (float)textureHeight; + Tessellator tessellator = Tessellator.instance; + tessellator.startDrawingQuads(); + tessellator.addVertexWithUV((double)(x), (double)(y + height), 0, (double)((float)(u) * f), (double)((float)(v + height) * f1)); + tessellator.addVertexWithUV((double)(x + width), (double)(y + height), 0, (double)((float)(u + width) * f), (double)((float)(v + height) * f1)); + tessellator.addVertexWithUV((double)(x + width), (double)(y), 0, (double)((float)(u + width) * f), (double)((float)(v) * f1)); + tessellator.addVertexWithUV((double)(x), (double)(y), 0, (double)((float)(u) * f), (double)((float)(v) * f1)); + tessellator.draw(); + } } diff --git a/src/main/java/electroblob/wizardry/client/GuiSpellDisplay.java b/src/main/java/electroblob/wizardry/client/GuiSpellDisplay.java index dc27a3eb..366e1712 100644 --- a/src/main/java/electroblob/wizardry/client/GuiSpellDisplay.java +++ b/src/main/java/electroblob/wizardry/client/GuiSpellDisplay.java @@ -2,29 +2,26 @@ package electroblob.wizardry.client; import java.util.List; -import electroblob.wizardry.Settings.GuiPosition; +import org.lwjgl.opengl.GL11; + +import cpw.mods.fml.common.eventhandler.SubscribeEvent; +import electroblob.wizardry.ExtendedPlayer; import electroblob.wizardry.SpellGlyphData; -import electroblob.wizardry.WizardData; +import electroblob.wizardry.WandHelper; import electroblob.wizardry.Wizardry; -import electroblob.wizardry.constants.Constants; +import electroblob.wizardry.WizardryRegistry; import electroblob.wizardry.item.ItemWand; -import electroblob.wizardry.registry.Spells; -import electroblob.wizardry.registry.WizardryItems; -import electroblob.wizardry.registry.WizardryPotions; +import electroblob.wizardry.spell.None; import electroblob.wizardry.spell.Spell; -import electroblob.wizardry.util.WandHelper; -import electroblob.wizardry.util.WizardryUtilities; import net.minecraft.client.Minecraft; import net.minecraft.client.gui.FontRenderer; import net.minecraft.client.gui.Gui; -import net.minecraft.client.renderer.GlStateManager; -import net.minecraft.client.renderer.GlStateManager.DestFactor; -import net.minecraft.client.renderer.GlStateManager.SourceFactor; +import net.minecraft.client.renderer.Tessellator; import net.minecraft.entity.player.EntityPlayer; -import net.minecraft.item.ItemStack; +import net.minecraft.nbt.NBTTagCompound; +import net.minecraft.nbt.NBTTagIntArray; import net.minecraft.util.ResourceLocation; import net.minecraftforge.client.event.RenderGameOverlayEvent; -import net.minecraftforge.fml.common.eventhandler.SubscribeEvent; public class GuiSpellDisplay extends Gui { @@ -42,114 +39,131 @@ public class GuiSpellDisplay extends Gui { EntityPlayer player = this.mc.thePlayer; - // If the player has a wand in each hand, only displays for the one in the main hand. + if(player.getHeldItem() != null && player.getHeldItem().getItem() instanceof ItemWand){ - ItemStack wand = player.getHeldItemMainhand(); + int width = event.resolution.getScaledWidth(); + int height = event.resolution.getScaledHeight(); - if(wand == null || !(wand.getItem() instanceof ItemWand)){ - wand = player.getHeldItemOffhand(); - // If the player isn't holding a wand, then nothing else needs to be done. - if(wand == null || !(wand.getItem() instanceof ItemWand)) return; - } + Spell spell = WandHelper.getCurrentSpell(player.getHeldItem()); + int cooldown = WandHelper.getCurrentCooldown(player.getHeldItem()); - int width = event.getResolution().getScaledWidth(); - int height = event.getResolution().getScaledHeight(); + float cooldownMultiplier = 1.0f - WandHelper.getUpgradeLevel(player.getHeldItem(), Wizardry.cooldownUpgrade)*Wizardry.COOLDOWN_REDUCTION_PER_LEVEL; - Spell spell = WandHelper.getCurrentSpell(wand); - int cooldown = WandHelper.getCurrentCooldown(wand); + if(player.isPotionActive(Wizardry.fontOfMana)){ + // Dividing by this rather than setting it takes upgrades and font of mana into account simultaneously + cooldownMultiplier /= 2 + player.getActivePotionEffect(Wizardry.fontOfMana).getAmplifier(); + } - float cooldownMultiplier = 1.0f - WandHelper.getUpgradeLevel(wand, WizardryItems.cooldown_upgrade)*Constants.COOLDOWN_REDUCTION_PER_LEVEL; + // Coordinates of the top left corner of the HUD. + int left = 0; + int top = 0; + boolean mirror = false; - if(player.isPotionActive(WizardryPotions.font_of_mana)){ - // Dividing by this rather than setting it takes upgrades and font of mana into account simultaneously - cooldownMultiplier /= 2 + player.getActivePotionEffect(WizardryPotions.font_of_mana).getAmplifier(); - } + if(Wizardry.spellHUDPosition.equals("Bottom left")){ + left = 0; + top = height-36; + }else if(Wizardry.spellHUDPosition.equals("Top left")){ + left = 0; + top = 0; + }else if(Wizardry.spellHUDPosition.equals("Top right")){ + left = width-128; + top = 0; + mirror = true; + }else if(Wizardry.spellHUDPosition.equals("Bottom right")){ + left = width-128; + top = height-36; + mirror = true; + } - // Coordinates of the top left corner of the HUD. - int left = 0; - int top = 0; - boolean mirror = false; + boolean discovered = true; - if(Wizardry.settings.spellHUDPosition == GuiPosition.BOTTOM_LEFT){ - left = 0; - top = height-36; - }else if(Wizardry.settings.spellHUDPosition == GuiPosition.TOP_LEFT){ - left = 0; - top = 0; - }else if(Wizardry.settings.spellHUDPosition == GuiPosition.TOP_RIGHT){ - left = width-128; - top = 0; - mirror = true; - }else if(Wizardry.settings.spellHUDPosition == GuiPosition.BOTTOM_RIGHT){ - left = width-128; - top = height-36; - mirror = true; - } + if(!player.capabilities.isCreativeMode && ExtendedPlayer.get(player) != null){ + discovered = ExtendedPlayer.get(player).hasSpellBeenDiscovered(spell); + } - boolean discovered = true; + if(event.type == RenderGameOverlayEvent.ElementType.TEXT){ - if(!player.capabilities.isCreativeMode && WizardData.get(player) != null){ - discovered = WizardData.get(player).hasSpellBeenDiscovered(spell); - } + // Makes spells greyed out if they are in cooldown or if the player has the arcane jammer effect + String colour = cooldown > 0 || player.isPotionActive(Wizardry.arcaneJammer) ? "\u00A78" : spell.element.colour; + if(!discovered) colour = "\u00A79"; + String spellName = discovered ? spell.getDisplayName() : SpellGlyphData.getGlyphName(spell, player.worldObj); + FontRenderer font = discovered ? this.mc.fontRenderer : this.mc.standardGalacticFontRenderer; - if(event.getType() == RenderGameOverlayEvent.ElementType.TEXT){ + int maxWidth = 90; - // Makes spells greyed out if they are in cooldown or if the player has the arcane jammer effect - String colour = cooldown > 0 || player.isPotionActive(WizardryPotions.arcane_jammer) ? "\u00A78" : spell.element.getFormattingCode(); - if(!discovered) colour = "\u00A79"; - String spellName = discovered ? spell.getDisplayName() : SpellGlyphData.getGlyphName(spell, player.worldObj); - FontRenderer font = discovered ? this.mc.fontRendererObj : this.mc.standardGalacticFontRenderer; + if(font.getStringWidth(spellName) <= maxWidth){ + // Single line is rendered more centrally + font.drawStringWithShadow(colour + spellName, mirror ? left+5 : left+41, top+13, 0xffffffff); - int maxWidth = 90; + }else{ - if(font.getStringWidth(spellName) <= maxWidth){ - // Single line is rendered more centrally - font.drawStringWithShadow(colour + spellName, mirror ? left+5 : left+41, top+13, 0xffffffff); + int lineNumber = 0; - }else{ + List lines = font.listFormattedStringToWidth(spellName, maxWidth); - int lineNumber = 0; - - List lines = font.listFormattedStringToWidth(spellName, maxWidth); - - for(Object line : lines){ - if(line instanceof String){ - font.drawStringWithShadow(colour + (String)line, mirror ? left+5 : left+41, top+6 + 11*lineNumber, 0xffffffff); + for(Object line : lines){ + if(line instanceof String){ + font.drawStringWithShadow(colour + (String)line, mirror ? left+5 : left+41, top+6 + 11*lineNumber, 0xffffffff); + } + lineNumber++; } - lineNumber++; } + + }else if(event.type == RenderGameOverlayEvent.ElementType.HOTBAR){ + + GL11.glPushMatrix(); + GL11.glEnable(GL11.GL_BLEND); + GL11.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA); + + GL11.glColor3f(1.0f, 1.0f, 1.0f); + this.mc.renderEngine.bindTexture(hudTexture); + + // Background of spell hud + this.drawTexturedModalRect(left, top, 0, mirror ? 36 : 0, 128, 36); + + // Cooldown bar + if(cooldown > 0){ + this.drawTexturedModalRect(mirror ? left+5 : left+41, height-8, 128, 6, 82, 6); + + int l = (int)(((double)(spell.cooldown * cooldownMultiplier - cooldown) + / (double)(spell.cooldown * cooldownMultiplier)) * 82); + + this.drawTexturedModalRect(mirror ? left+5 : left+41, height-8, 128, 0, l, 6); + } + + // Spell illustration + this.mc.renderEngine.bindTexture(discovered ? spell.icon : WizardryRegistry.none.icon); + + this.drawTexturedRect(mirror ? left+94 : left+2, top+2, 0, 0, 32, 32, 32, 32); + + GL11.glDisable(GL11.GL_BLEND); + GL11.glPopMatrix(); } - - }else if(event.getType() == RenderGameOverlayEvent.ElementType.HOTBAR){ - - GlStateManager.pushAttrib(); - - GlStateManager.enableBlend(); - GlStateManager.blendFunc(SourceFactor.SRC_ALPHA, DestFactor.ONE_MINUS_SRC_ALPHA); - GlStateManager.color(1, 1, 1); - - this.mc.renderEngine.bindTexture(hudTexture); - - // Background of spell hud - this.drawTexturedModalRect(left, top, 0, mirror ? 36 : 0, 128, 36); - - // Cooldown bar - if(cooldown > 0){ - this.drawTexturedModalRect(mirror ? left+5 : left+41, height-8, 128, 6, 82, 6); - - int l = (int)(((double)(spell.cooldown * cooldownMultiplier - cooldown) - / (double)(spell.cooldown * cooldownMultiplier)) * 82); - - this.drawTexturedModalRect(mirror ? left+5 : left+41, height-8, 128, 0, l, 6); - } - - // Spell illustration - this.mc.renderEngine.bindTexture(discovered ? spell.getIcon() : Spells.none.getIcon()); - - WizardryUtilities.drawTexturedRect(mirror ? left+94 : left+2, top+2, 0, 0, 32, 32, 32, 32); - - GlStateManager.popAttrib(); } } + /** + * Draws a textured rectangle, taking the size of the image and the bit needed into account. Client side only, of course. + * @param x The x position of the rectangle + * @param y The y position of the rectangle + * @param u The x position of the top left corner of the section of the image wanted + * @param v The y position of the top left corner of the section of the image wanted + * @param width The width of the section + * @param height The height of the section + * @param textureWidth The width of the actual image. + * @param textureHeight The height of the actual image. + */ + public static void drawTexturedRect(int x, int y, int u, int v, int width, int height, int textureWidth, int textureHeight) + { + float f = 1F / (float)textureWidth; + float f1 = 1F / (float)textureHeight; + Tessellator tessellator = Tessellator.instance; + tessellator.startDrawingQuads(); + tessellator.addVertexWithUV((double)(x), (double)(y + height), 0, (double)((float)(u) * f), (double)((float)(v + height) * f1)); + tessellator.addVertexWithUV((double)(x + width), (double)(y + height), 0, (double)((float)(u + width) * f), (double)((float)(v + height) * f1)); + tessellator.addVertexWithUV((double)(x + width), (double)(y), 0, (double)((float)(u + width) * f), (double)((float)(v) * f1)); + tessellator.addVertexWithUV((double)(x), (double)(y), 0, (double)((float)(u) * f), (double)((float)(v) * f1)); + tessellator.draw(); + } + } diff --git a/src/main/java/electroblob/wizardry/client/GuiWizardHandbook.java b/src/main/java/electroblob/wizardry/client/GuiWizardHandbook.java index 5cb8ee9e..522134f2 100644 --- a/src/main/java/electroblob/wizardry/client/GuiWizardHandbook.java +++ b/src/main/java/electroblob/wizardry/client/GuiWizardHandbook.java @@ -4,42 +4,40 @@ import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.util.ArrayList; +import java.util.Iterator; import java.util.List; import org.apache.commons.io.Charsets; import org.lwjgl.input.Keyboard; import org.lwjgl.opengl.GL11; +import org.lwjgl.opengl.GL12; +import electroblob.wizardry.EnumElement; +import electroblob.wizardry.EnumTier; import electroblob.wizardry.Wizardry; -import electroblob.wizardry.constants.Constants; -import electroblob.wizardry.constants.Element; -import electroblob.wizardry.constants.Tier; -import electroblob.wizardry.registry.WizardryBlocks; -import electroblob.wizardry.registry.WizardryItems; -import electroblob.wizardry.util.WizardryUtilities; +import electroblob.wizardry.WizardryGuiHandler; import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.FontRenderer; import net.minecraft.client.gui.GuiButton; import net.minecraft.client.gui.GuiScreen; -import net.minecraft.client.renderer.GlStateManager; import net.minecraft.client.renderer.RenderHelper; import net.minecraft.client.renderer.Tessellator; -import net.minecraft.client.renderer.VertexBuffer; -import net.minecraft.client.renderer.vertex.DefaultVertexFormats; +import net.minecraft.client.renderer.entity.RenderItem; +import net.minecraft.client.resources.LanguageManager; import net.minecraft.init.Blocks; import net.minecraft.init.Items; import net.minecraft.item.ItemStack; +import net.minecraft.util.EnumChatFormatting; import net.minecraft.util.ResourceLocation; +import net.minecraft.util.StatCollector; public class GuiWizardHandbook extends GuiScreen { + private GuiButtonNextPage nextPageBtn, prevPageBtn; private int xSize, ySize; private int pageNumber = 0; - private static final int PAGE_WIDTH = 120; - /** The integer colour for black passed into the font renderer methods. This used to be 0 but that's now white - * for some reason, so I've made a it a constant in case it changes again. */ - // I think this is actually ever-so-slightly lighter than pure black, but the difference is unnoticeable. - private static final int BLACK = 1; + private static final int pageWidth = 120; public static final ResourceLocation regularHandbook = new ResourceLocation(Wizardry.MODID, "textures/gui/handbook.png"); public static final ResourceLocation ore = new ResourceLocation(Wizardry.MODID, "textures/gui/ore_picture.png"); @@ -58,7 +56,9 @@ public class GuiWizardHandbook extends GuiScreen { ySize = 180; } - @Override + /** + * Draws the screen and all the components in it. + */ public void drawScreen(int mouseX, int mouseY, float par3){ int xPos = this.width/2 - xSize/2; @@ -71,7 +71,7 @@ public class GuiWizardHandbook extends GuiScreen { Minecraft.getMinecraft().renderEngine.bindTexture(regularHandbook); } - WizardryUtilities.drawTexturedRect(xPos, yPos, 0, 0, xSize, ySize, xSize, 256); + drawTexturedRect(xPos, yPos, 0, 0, xSize, ySize, xSize, 256); // Arcane workbench gui picture if(pageNumber == (this.guiPage-1)/2){ @@ -83,13 +83,13 @@ public class GuiWizardHandbook extends GuiScreen { if(pageNumber == (this.imagePage-1)/2){ Minecraft.getMinecraft().renderEngine.bindTexture(ore); - WizardryUtilities.drawTexturedRect(this.imagePage % 2 == 1 ? xPos + 17 : this.width/2 + 7, yPos + 80, 0, 0, 64, 64, 64, 64); + drawTexturedRect(this.imagePage % 2 == 1 ? xPos + 17 : this.width/2 + 7, yPos + 80, 0, 0, 64, 64, 64, 64); Minecraft.getMinecraft().renderEngine.bindTexture(crystal); drawTexturedStretchedRect(this.imagePage % 2 == 1 ? xPos + 17 + 64 : this.width/2 + 7 + 62, yPos + 80, 0, 0, 64, 64, 1, 1); } - + this.fontRendererObj.drawString("" + (pageNumber*2 + 1), xPos + xSize/4 - 3, yPos + ySize - 20, 0); this.fontRendererObj.drawString("" + (pageNumber*2 + 2), xPos + 3*xSize/4 - 5, yPos + ySize - 20, 0); @@ -98,8 +98,10 @@ public class GuiWizardHandbook extends GuiScreen { int lineNumber = 0; if(pageNumber == 1){ + int row = 2; for(Section s : sections){ s.drawContents(); + row++; } }else{ for(Section s : sections){ @@ -109,9 +111,9 @@ public class GuiWizardHandbook extends GuiScreen { for(String paragraph : text.get(pageNumber*2)){ - this.fontRendererObj.drawSplitString(paragraph, xPos + 17, yPos + 14 + lineNumber*this.fontRendererObj.FONT_HEIGHT, PAGE_WIDTH, BLACK); + this.fontRendererObj.drawSplitString(paragraph, xPos + 17, yPos + 14 + lineNumber*this.fontRendererObj.FONT_HEIGHT, pageWidth, 0); - List list = new ArrayList(this.fontRendererObj.listFormattedStringToWidth(paragraph, GuiWizardHandbook.PAGE_WIDTH)); + ArrayList list = new ArrayList(this.fontRendererObj.listFormattedStringToWidth(paragraph, this.pageWidth)); lineNumber += list.size(); } @@ -124,18 +126,26 @@ public class GuiWizardHandbook extends GuiScreen { // First page is centred if(pageNumber == 0){ - int startX = this.width/2 + 7 + PAGE_WIDTH/2 - this.fontRendererObj.getStringWidth(paragraph)/2; - this.fontRendererObj.drawSplitString(paragraph, startX, yPos + 14 + lineNumber*this.fontRendererObj.FONT_HEIGHT, PAGE_WIDTH, BLACK); + int startX = this.width/2 + 7 + pageWidth/2 - this.fontRendererObj.getStringWidth(paragraph)/2; + this.fontRendererObj.drawSplitString(paragraph, startX, yPos + 14 + lineNumber*this.fontRendererObj.FONT_HEIGHT, pageWidth, 0); }else{ - this.fontRendererObj.drawSplitString(paragraph, this.width/2 + 7, yPos + 14 + lineNumber*this.fontRendererObj.FONT_HEIGHT, PAGE_WIDTH, BLACK); + this.fontRendererObj.drawSplitString(paragraph, this.width/2 + 7, yPos + 14 + lineNumber*this.fontRendererObj.FONT_HEIGHT, pageWidth, 0); } - List list = new ArrayList(this.fontRendererObj.listFormattedStringToWidth(paragraph, GuiWizardHandbook.PAGE_WIDTH)); + ArrayList list = new ArrayList(this.fontRendererObj.listFormattedStringToWidth(paragraph, this.pageWidth)); lineNumber += list.size(); } } + /* + for(int i=0;i<(Math.min(text[pageNumber].length, 14));i++){ + this.fontRendererObj.drawString(text[pageNumber][i], xPos + 16, yPos + 14 + i*10, 0); + } + for(int i=0;i<(Math.min(text[pageNumber].length - 14, 14));i++){ + this.fontRendererObj.drawString(text[pageNumber][i+14], this.width/2 + 5, yPos + 14 + i*10, 0); + } + */ ItemStack[][] craftingGrid; ItemStack craftingResult; @@ -143,164 +153,164 @@ public class GuiWizardHandbook extends GuiScreen { if(pageNumber == (sections.get(sections.size()-1).pageNumber-1)/2){ craftingGrid = new ItemStack[3][3]; - craftingGrid[0][0] = new ItemStack(Items.GOLD_NUGGET); - craftingGrid[1][0] = new ItemStack(Blocks.CARPET, 1, 10); - craftingGrid[2][0] = new ItemStack(Items.GOLD_NUGGET); - craftingGrid[0][1] = new ItemStack(WizardryItems.magic_crystal); - craftingGrid[1][1] = new ItemStack(Blocks.LAPIS_BLOCK); - craftingGrid[2][1] = new ItemStack(WizardryItems.magic_crystal); - craftingGrid[0][2] = new ItemStack(Blocks.STONE); - craftingGrid[1][2] = new ItemStack(Blocks.STONE); - craftingGrid[2][2] = new ItemStack(Blocks.STONE); - craftingResult = new ItemStack(WizardryBlocks.arcane_workbench); + craftingGrid[0][0] = new ItemStack(Items.gold_nugget); + craftingGrid[1][0] = new ItemStack(Blocks.carpet, 1, 10); + craftingGrid[2][0] = new ItemStack(Items.gold_nugget); + craftingGrid[0][1] = new ItemStack(Wizardry.magicCrystal); + craftingGrid[1][1] = new ItemStack(Blocks.lapis_block); + craftingGrid[2][1] = new ItemStack(Wizardry.magicCrystal); + craftingGrid[0][2] = new ItemStack(Blocks.stone); + craftingGrid[1][2] = new ItemStack(Blocks.stone); + craftingGrid[2][2] = new ItemStack(Blocks.stone); + craftingResult = new ItemStack(Wizardry.arcaneWorkbench); this.renderCraftingRecipe(xPos + 23, yPos + 39, mouseX, mouseY, craftingGrid, craftingResult); craftingGrid = new ItemStack[3][3]; - craftingGrid[2][0] = new ItemStack(WizardryItems.magic_crystal); - craftingGrid[1][1] = new ItemStack(Items.STICK); - craftingGrid[0][2] = new ItemStack(Items.GOLD_NUGGET); - craftingResult = new ItemStack(WizardryItems.magic_wand); + craftingGrid[2][0] = new ItemStack(Wizardry.magicCrystal); + craftingGrid[1][1] = new ItemStack(Items.stick); + craftingGrid[0][2] = new ItemStack(Items.gold_nugget); + craftingResult = new ItemStack(Wizardry.magicWand); this.renderCraftingRecipe(xPos + 23, yPos + 98, mouseX, mouseY, craftingGrid, craftingResult); craftingGrid = new ItemStack[3][3]; - craftingGrid[1][0] = new ItemStack(WizardryItems.magic_crystal); - craftingGrid[0][1] = new ItemStack(WizardryItems.magic_crystal); - craftingGrid[1][1] = new ItemStack(Items.BOOK); - craftingGrid[1][2] = new ItemStack(WizardryItems.magic_crystal); - craftingGrid[2][1] = new ItemStack(WizardryItems.magic_crystal); - craftingResult = new ItemStack(WizardryItems.spell_book, 1, 1); + craftingGrid[1][0] = new ItemStack(Wizardry.magicCrystal); + craftingGrid[0][1] = new ItemStack(Wizardry.magicCrystal); + craftingGrid[1][1] = new ItemStack(Items.book); + craftingGrid[1][2] = new ItemStack(Wizardry.magicCrystal); + craftingGrid[2][1] = new ItemStack(Wizardry.magicCrystal); + craftingResult = new ItemStack(Wizardry.spellBook, 1, 1); this.renderCraftingRecipe(xPos + 156, yPos + 39, mouseX, mouseY, craftingGrid, craftingResult); craftingGrid = new ItemStack[3][3]; - craftingGrid[0][0] = new ItemStack(Items.BOOK); - craftingGrid[1][0] = new ItemStack(WizardryItems.magic_crystal); - craftingResult = new ItemStack(WizardryItems.wizard_handbook); + craftingGrid[0][0] = new ItemStack(Items.book); + craftingGrid[1][0] = new ItemStack(Wizardry.magicCrystal); + craftingResult = new ItemStack(Wizardry.wizardHandbook); this.renderCraftingRecipe(xPos + 156, yPos + 98, mouseX, mouseY, craftingGrid, craftingResult); }else if(pageNumber == (sections.get(sections.size()-1).pageNumber-1)/2 + 1){ craftingGrid = new ItemStack[3][3]; - craftingGrid[0][0] = new ItemStack(WizardryBlocks.crystal_flower); - craftingResult = new ItemStack(WizardryItems.magic_crystal, 2); + craftingGrid[0][0] = new ItemStack(Wizardry.crystalFlower); + craftingResult = new ItemStack(Wizardry.magicCrystal, 2); this.renderCraftingRecipe(xPos + 23, yPos + 39, mouseX, mouseY, craftingGrid, craftingResult); craftingGrid = new ItemStack[3][3]; - craftingGrid[0][0] = new ItemStack(WizardryItems.magic_crystal); - craftingGrid[1][0] = new ItemStack(WizardryItems.magic_crystal); - craftingGrid[2][0] = new ItemStack(WizardryItems.magic_crystal); - craftingGrid[0][1] = new ItemStack(WizardryItems.magic_crystal); - craftingGrid[1][1] = new ItemStack(Items.GLASS_BOTTLE); - craftingGrid[2][1] = new ItemStack(WizardryItems.magic_crystal); - craftingGrid[0][2] = new ItemStack(WizardryItems.magic_crystal); - craftingGrid[1][2] = new ItemStack(WizardryItems.magic_crystal); - craftingGrid[2][2] = new ItemStack(WizardryItems.magic_crystal); - craftingResult = new ItemStack(WizardryItems.mana_flask); + craftingGrid[0][0] = new ItemStack(Wizardry.magicCrystal); + craftingGrid[1][0] = new ItemStack(Wizardry.magicCrystal); + craftingGrid[2][0] = new ItemStack(Wizardry.magicCrystal); + craftingGrid[0][1] = new ItemStack(Wizardry.magicCrystal); + craftingGrid[1][1] = new ItemStack(Items.glass_bottle); + craftingGrid[2][1] = new ItemStack(Wizardry.magicCrystal); + craftingGrid[0][2] = new ItemStack(Wizardry.magicCrystal); + craftingGrid[1][2] = new ItemStack(Wizardry.magicCrystal); + craftingGrid[2][2] = new ItemStack(Wizardry.magicCrystal); + craftingResult = new ItemStack(Wizardry.manaFlask); this.renderCraftingRecipe(xPos + 23, yPos + 98, mouseX, mouseY, craftingGrid, craftingResult); craftingGrid = new ItemStack[3][3]; - craftingGrid[1][0] = new ItemStack(Blocks.STONE); - craftingGrid[0][1] = new ItemStack(Blocks.STONE); - craftingGrid[1][1] = new ItemStack(WizardryItems.magic_crystal); - craftingGrid[1][2] = new ItemStack(Blocks.STONE); - craftingGrid[2][1] = new ItemStack(Blocks.STONE); - craftingResult = new ItemStack(WizardryBlocks.transportation_stone, 2); + craftingGrid[1][0] = new ItemStack(Blocks.stone); + craftingGrid[0][1] = new ItemStack(Blocks.stone); + craftingGrid[1][1] = new ItemStack(Wizardry.magicCrystal); + craftingGrid[1][2] = new ItemStack(Blocks.stone); + craftingGrid[2][1] = new ItemStack(Blocks.stone); + craftingResult = new ItemStack(Wizardry.transportationStone, 2); this.renderCraftingRecipe(xPos + 156, yPos + 39, mouseX, mouseY, craftingGrid, craftingResult); craftingGrid = new ItemStack[3][3]; - craftingGrid[1][0] = new ItemStack(Items.STRING); - craftingGrid[0][1] = new ItemStack(Items.STRING); - craftingGrid[1][1] = new ItemStack(WizardryItems.magic_crystal); - craftingGrid[1][2] = new ItemStack(Items.STRING); - craftingGrid[2][1] = new ItemStack(Items.STRING); - craftingResult = new ItemStack(WizardryItems.magic_silk, 2); + craftingGrid[1][0] = new ItemStack(Items.string); + craftingGrid[0][1] = new ItemStack(Items.string); + craftingGrid[1][1] = new ItemStack(Wizardry.magicCrystal); + craftingGrid[1][2] = new ItemStack(Items.string); + craftingGrid[2][1] = new ItemStack(Items.string); + craftingResult = new ItemStack(Wizardry.magicSilk, 2); this.renderCraftingRecipe(xPos + 156, yPos + 98, mouseX, mouseY, craftingGrid, craftingResult); }else if(pageNumber == (sections.get(sections.size()-1).pageNumber-1)/2 + 2){ craftingGrid = new ItemStack[3][3]; - craftingGrid[0][0] = new ItemStack(WizardryItems.magic_silk); - craftingGrid[1][0] = new ItemStack(WizardryItems.magic_silk); - craftingGrid[2][0] = new ItemStack(WizardryItems.magic_silk); - craftingGrid[0][1] = new ItemStack(WizardryItems.magic_silk); - craftingGrid[2][1] = new ItemStack(WizardryItems.magic_silk); - craftingResult = new ItemStack(WizardryItems.wizard_hat); + craftingGrid[0][0] = new ItemStack(Wizardry.magicSilk); + craftingGrid[1][0] = new ItemStack(Wizardry.magicSilk); + craftingGrid[2][0] = new ItemStack(Wizardry.magicSilk); + craftingGrid[0][1] = new ItemStack(Wizardry.magicSilk); + craftingGrid[2][1] = new ItemStack(Wizardry.magicSilk); + craftingResult = new ItemStack(Wizardry.wizardHat); this.renderCraftingRecipe(xPos + 23, yPos + 39, mouseX, mouseY, craftingGrid, craftingResult); craftingGrid = new ItemStack[3][3]; - craftingGrid[0][0] = new ItemStack(WizardryItems.magic_silk); - craftingGrid[2][0] = new ItemStack(WizardryItems.magic_silk); - craftingGrid[0][1] = new ItemStack(WizardryItems.magic_silk); - craftingGrid[1][1] = new ItemStack(WizardryItems.magic_silk); - craftingGrid[2][1] = new ItemStack(WizardryItems.magic_silk); - craftingGrid[0][2] = new ItemStack(WizardryItems.magic_silk); - craftingGrid[1][2] = new ItemStack(WizardryItems.magic_silk); - craftingGrid[2][2] = new ItemStack(WizardryItems.magic_silk); - craftingResult = new ItemStack(WizardryItems.wizard_robe); + craftingGrid[0][0] = new ItemStack(Wizardry.magicSilk); + craftingGrid[2][0] = new ItemStack(Wizardry.magicSilk); + craftingGrid[0][1] = new ItemStack(Wizardry.magicSilk); + craftingGrid[1][1] = new ItemStack(Wizardry.magicSilk); + craftingGrid[2][1] = new ItemStack(Wizardry.magicSilk); + craftingGrid[0][2] = new ItemStack(Wizardry.magicSilk); + craftingGrid[1][2] = new ItemStack(Wizardry.magicSilk); + craftingGrid[2][2] = new ItemStack(Wizardry.magicSilk); + craftingResult = new ItemStack(Wizardry.wizardRobe); this.renderCraftingRecipe(xPos + 23, yPos + 98, mouseX, mouseY, craftingGrid, craftingResult); craftingGrid = new ItemStack[3][3]; - craftingGrid[0][0] = new ItemStack(WizardryItems.magic_silk); - craftingGrid[1][0] = new ItemStack(WizardryItems.magic_silk); - craftingGrid[2][0] = new ItemStack(WizardryItems.magic_silk); - craftingGrid[0][1] = new ItemStack(WizardryItems.magic_silk); - craftingGrid[2][1] = new ItemStack(WizardryItems.magic_silk); - craftingGrid[0][2] = new ItemStack(WizardryItems.magic_silk); - craftingGrid[2][2] = new ItemStack(WizardryItems.magic_silk); - craftingResult = new ItemStack(WizardryItems.wizard_leggings); + craftingGrid[0][0] = new ItemStack(Wizardry.magicSilk); + craftingGrid[1][0] = new ItemStack(Wizardry.magicSilk); + craftingGrid[2][0] = new ItemStack(Wizardry.magicSilk); + craftingGrid[0][1] = new ItemStack(Wizardry.magicSilk); + craftingGrid[2][1] = new ItemStack(Wizardry.magicSilk); + craftingGrid[0][2] = new ItemStack(Wizardry.magicSilk); + craftingGrid[2][2] = new ItemStack(Wizardry.magicSilk); + craftingResult = new ItemStack(Wizardry.wizardLeggings); this.renderCraftingRecipe(xPos + 156, yPos + 39, mouseX, mouseY, craftingGrid, craftingResult); craftingGrid = new ItemStack[3][3]; - craftingGrid[0][0] = new ItemStack(WizardryItems.magic_silk); - craftingGrid[2][0] = new ItemStack(WizardryItems.magic_silk); - craftingGrid[0][1] = new ItemStack(WizardryItems.magic_silk); - craftingGrid[2][1] = new ItemStack(WizardryItems.magic_silk); - craftingResult = new ItemStack(WizardryItems.wizard_boots); + craftingGrid[0][0] = new ItemStack(Wizardry.magicSilk); + craftingGrid[2][0] = new ItemStack(Wizardry.magicSilk); + craftingGrid[0][1] = new ItemStack(Wizardry.magicSilk); + craftingGrid[2][1] = new ItemStack(Wizardry.magicSilk); + craftingResult = new ItemStack(Wizardry.wizardBoots); this.renderCraftingRecipe(xPos + 156, yPos + 98, mouseX, mouseY, craftingGrid, craftingResult); }else if(pageNumber == (sections.get(sections.size()-1).pageNumber-1)/2 + 3){ - if(Wizardry.settings.useAlternateScrollRecipe){ + if(Wizardry.useAlternateScrollRecipe){ craftingGrid = new ItemStack[3][3]; - craftingGrid[0][0] = new ItemStack(Items.PAPER); - craftingGrid[1][0] = new ItemStack(Items.STRING); - craftingGrid[2][0] = new ItemStack(WizardryItems.magic_crystal); - craftingResult = new ItemStack(WizardryItems.blank_scroll); + craftingGrid[0][0] = new ItemStack(Items.paper); + craftingGrid[1][0] = new ItemStack(Items.string); + craftingGrid[2][0] = new ItemStack(Wizardry.magicCrystal); + craftingResult = new ItemStack(Wizardry.blankScroll); this.renderCraftingRecipe(xPos + 23, yPos + 39, mouseX, mouseY, craftingGrid, craftingResult); }else{ craftingGrid = new ItemStack[3][3]; - craftingGrid[0][0] = new ItemStack(Items.PAPER); - craftingGrid[1][0] = new ItemStack(Items.STRING); - craftingResult = new ItemStack(WizardryItems.blank_scroll); + craftingGrid[0][0] = new ItemStack(Items.paper); + craftingGrid[1][0] = new ItemStack(Items.string); + craftingResult = new ItemStack(Wizardry.blankScroll); this.renderCraftingRecipe(xPos + 23, yPos + 39, mouseX, mouseY, craftingGrid, craftingResult); } - if(Wizardry.settings.firebombIsCraftable){ + if(Wizardry.firebombIsCraftable){ craftingGrid = new ItemStack[3][3]; - craftingGrid[0][0] = new ItemStack(Items.BLAZE_POWDER); - craftingGrid[1][0] = new ItemStack(Items.BLAZE_POWDER); - craftingGrid[0][1] = new ItemStack(Items.GLASS_BOTTLE); - craftingGrid[1][1] = new ItemStack(Items.GUNPOWDER); - craftingResult = new ItemStack(WizardryItems.firebomb, 3); + craftingGrid[0][0] = new ItemStack(Items.blaze_powder); + craftingGrid[1][0] = new ItemStack(Items.blaze_powder); + craftingGrid[0][1] = new ItemStack(Items.glass_bottle); + craftingGrid[1][1] = new ItemStack(Items.gunpowder); + craftingResult = new ItemStack(Wizardry.firebomb, 3); this.renderCraftingRecipe(xPos + 23, yPos + 98, mouseX, mouseY, craftingGrid, craftingResult); } - if(Wizardry.settings.poisonBombIsCraftable){ + if(Wizardry.poisonBombIsCraftable){ craftingGrid = new ItemStack[3][3]; - craftingGrid[0][0] = new ItemStack(Items.SPIDER_EYE); - craftingGrid[1][0] = new ItemStack(Items.SPIDER_EYE); - craftingGrid[0][1] = new ItemStack(Items.GLASS_BOTTLE); - craftingGrid[1][1] = new ItemStack(Items.GUNPOWDER); - craftingResult = new ItemStack(WizardryItems.poison_bomb, 3); + craftingGrid[0][0] = new ItemStack(Items.spider_eye); + craftingGrid[1][0] = new ItemStack(Items.spider_eye); + craftingGrid[0][1] = new ItemStack(Items.glass_bottle); + craftingGrid[1][1] = new ItemStack(Items.gunpowder); + craftingResult = new ItemStack(Wizardry.poisonBomb, 3); this.renderCraftingRecipe(xPos + 156, yPos + 39, mouseX, mouseY, craftingGrid, craftingResult); } - if(Wizardry.settings.smokeBombIsCraftable){ + if(Wizardry.smokeBombIsCraftable){ craftingGrid = new ItemStack[3][3]; - craftingGrid[0][0] = new ItemStack(Items.COAL); - craftingGrid[1][0] = new ItemStack(Items.COAL); - craftingGrid[0][1] = new ItemStack(Items.GLASS_BOTTLE); - craftingGrid[1][1] = new ItemStack(Items.GUNPOWDER); - craftingResult = new ItemStack(WizardryItems.smoke_bomb, 3); + craftingGrid[0][0] = new ItemStack(Items.coal); + craftingGrid[1][0] = new ItemStack(Items.coal); + craftingGrid[0][1] = new ItemStack(Items.glass_bottle); + craftingGrid[1][1] = new ItemStack(Items.gunpowder); + craftingResult = new ItemStack(Wizardry.smokeBomb, 3); this.renderCraftingRecipe(xPos + 156, yPos + 98, mouseX, mouseY, craftingGrid, craftingResult); } @@ -309,164 +319,164 @@ public class GuiWizardHandbook extends GuiScreen { if(pageNumber == (sections.get(sections.size()-1).pageNumber-1)/2){ craftingGrid = new ItemStack[3][3]; - craftingGrid[0][0] = new ItemStack(Items.GOLD_NUGGET); - craftingGrid[1][0] = new ItemStack(Blocks.CARPET, 1, 10); - craftingGrid[2][0] = new ItemStack(Items.GOLD_NUGGET); - craftingGrid[0][1] = new ItemStack(WizardryItems.magic_crystal); - craftingGrid[1][1] = new ItemStack(Blocks.LAPIS_BLOCK); - craftingGrid[2][1] = new ItemStack(WizardryItems.magic_crystal); - craftingGrid[0][2] = new ItemStack(Blocks.STONE); - craftingGrid[1][2] = new ItemStack(Blocks.STONE); - craftingGrid[2][2] = new ItemStack(Blocks.STONE); - craftingResult = new ItemStack(WizardryBlocks.arcane_workbench); + craftingGrid[0][0] = new ItemStack(Items.gold_nugget); + craftingGrid[1][0] = new ItemStack(Blocks.carpet, 1, 10); + craftingGrid[2][0] = new ItemStack(Items.gold_nugget); + craftingGrid[0][1] = new ItemStack(Wizardry.magicCrystal); + craftingGrid[1][1] = new ItemStack(Blocks.lapis_block); + craftingGrid[2][1] = new ItemStack(Wizardry.magicCrystal); + craftingGrid[0][2] = new ItemStack(Blocks.stone); + craftingGrid[1][2] = new ItemStack(Blocks.stone); + craftingGrid[2][2] = new ItemStack(Blocks.stone); + craftingResult = new ItemStack(Wizardry.arcaneWorkbench); this.renderCraftingTooltips(xPos + 23, yPos + 39, mouseX, mouseY, craftingGrid, craftingResult); craftingGrid = new ItemStack[3][3]; - craftingGrid[2][0] = new ItemStack(WizardryItems.magic_crystal); - craftingGrid[1][1] = new ItemStack(Items.STICK); - craftingGrid[0][2] = new ItemStack(Items.GOLD_NUGGET); - craftingResult = new ItemStack(WizardryItems.magic_wand); + craftingGrid[2][0] = new ItemStack(Wizardry.magicCrystal); + craftingGrid[1][1] = new ItemStack(Items.stick); + craftingGrid[0][2] = new ItemStack(Items.gold_nugget); + craftingResult = new ItemStack(Wizardry.magicWand); this.renderCraftingTooltips(xPos + 23, yPos + 98, mouseX, mouseY, craftingGrid, craftingResult); craftingGrid = new ItemStack[3][3]; - craftingGrid[1][0] = new ItemStack(WizardryItems.magic_crystal); - craftingGrid[0][1] = new ItemStack(WizardryItems.magic_crystal); - craftingGrid[1][1] = new ItemStack(Items.BOOK); - craftingGrid[1][2] = new ItemStack(WizardryItems.magic_crystal); - craftingGrid[2][1] = new ItemStack(WizardryItems.magic_crystal); - craftingResult = new ItemStack(WizardryItems.spell_book, 1, 1); + craftingGrid[1][0] = new ItemStack(Wizardry.magicCrystal); + craftingGrid[0][1] = new ItemStack(Wizardry.magicCrystal); + craftingGrid[1][1] = new ItemStack(Items.book); + craftingGrid[1][2] = new ItemStack(Wizardry.magicCrystal); + craftingGrid[2][1] = new ItemStack(Wizardry.magicCrystal); + craftingResult = new ItemStack(Wizardry.spellBook, 1, 1); this.renderCraftingTooltips(xPos + 156, yPos + 39, mouseX, mouseY, craftingGrid, craftingResult); craftingGrid = new ItemStack[3][3]; - craftingGrid[0][0] = new ItemStack(Items.BOOK); - craftingGrid[1][0] = new ItemStack(WizardryItems.magic_crystal); - craftingResult = new ItemStack(WizardryItems.wizard_handbook); + craftingGrid[0][0] = new ItemStack(Items.book); + craftingGrid[1][0] = new ItemStack(Wizardry.magicCrystal); + craftingResult = new ItemStack(Wizardry.wizardHandbook); this.renderCraftingTooltips(xPos + 156, yPos + 98, mouseX, mouseY, craftingGrid, craftingResult); }else if(pageNumber == (sections.get(sections.size()-1).pageNumber-1)/2 + 1){ craftingGrid = new ItemStack[3][3]; - craftingGrid[0][0] = new ItemStack(WizardryBlocks.crystal_flower); - craftingResult = new ItemStack(WizardryItems.magic_crystal, 2); + craftingGrid[0][0] = new ItemStack(Wizardry.crystalFlower); + craftingResult = new ItemStack(Wizardry.magicCrystal, 2); this.renderCraftingTooltips(xPos + 23, yPos + 39, mouseX, mouseY, craftingGrid, craftingResult); craftingGrid = new ItemStack[3][3]; - craftingGrid[0][0] = new ItemStack(WizardryItems.magic_crystal); - craftingGrid[1][0] = new ItemStack(WizardryItems.magic_crystal); - craftingGrid[2][0] = new ItemStack(WizardryItems.magic_crystal); - craftingGrid[0][1] = new ItemStack(WizardryItems.magic_crystal); - craftingGrid[1][1] = new ItemStack(Items.GLASS_BOTTLE); - craftingGrid[2][1] = new ItemStack(WizardryItems.magic_crystal); - craftingGrid[0][2] = new ItemStack(WizardryItems.magic_crystal); - craftingGrid[1][2] = new ItemStack(WizardryItems.magic_crystal); - craftingGrid[2][2] = new ItemStack(WizardryItems.magic_crystal); - craftingResult = new ItemStack(WizardryItems.mana_flask); + craftingGrid[0][0] = new ItemStack(Wizardry.magicCrystal); + craftingGrid[1][0] = new ItemStack(Wizardry.magicCrystal); + craftingGrid[2][0] = new ItemStack(Wizardry.magicCrystal); + craftingGrid[0][1] = new ItemStack(Wizardry.magicCrystal); + craftingGrid[1][1] = new ItemStack(Items.glass_bottle); + craftingGrid[2][1] = new ItemStack(Wizardry.magicCrystal); + craftingGrid[0][2] = new ItemStack(Wizardry.magicCrystal); + craftingGrid[1][2] = new ItemStack(Wizardry.magicCrystal); + craftingGrid[2][2] = new ItemStack(Wizardry.magicCrystal); + craftingResult = new ItemStack(Wizardry.manaFlask); this.renderCraftingTooltips(xPos + 23, yPos + 98, mouseX, mouseY, craftingGrid, craftingResult); craftingGrid = new ItemStack[3][3]; - craftingGrid[1][0] = new ItemStack(Blocks.STONE); - craftingGrid[0][1] = new ItemStack(Blocks.STONE); - craftingGrid[1][1] = new ItemStack(WizardryItems.magic_crystal); - craftingGrid[1][2] = new ItemStack(Blocks.STONE); - craftingGrid[2][1] = new ItemStack(Blocks.STONE); - craftingResult = new ItemStack(WizardryBlocks.transportation_stone, 2); + craftingGrid[1][0] = new ItemStack(Blocks.stone); + craftingGrid[0][1] = new ItemStack(Blocks.stone); + craftingGrid[1][1] = new ItemStack(Wizardry.magicCrystal); + craftingGrid[1][2] = new ItemStack(Blocks.stone); + craftingGrid[2][1] = new ItemStack(Blocks.stone); + craftingResult = new ItemStack(Wizardry.transportationStone, 2); this.renderCraftingTooltips(xPos + 156, yPos + 39, mouseX, mouseY, craftingGrid, craftingResult); craftingGrid = new ItemStack[3][3]; - craftingGrid[1][0] = new ItemStack(Items.STRING); - craftingGrid[0][1] = new ItemStack(Items.STRING); - craftingGrid[1][1] = new ItemStack(WizardryItems.magic_crystal); - craftingGrid[1][2] = new ItemStack(Items.STRING); - craftingGrid[2][1] = new ItemStack(Items.STRING); - craftingResult = new ItemStack(WizardryItems.magic_silk, 2); + craftingGrid[1][0] = new ItemStack(Items.string); + craftingGrid[0][1] = new ItemStack(Items.string); + craftingGrid[1][1] = new ItemStack(Wizardry.magicCrystal); + craftingGrid[1][2] = new ItemStack(Items.string); + craftingGrid[2][1] = new ItemStack(Items.string); + craftingResult = new ItemStack(Wizardry.magicSilk, 2); this.renderCraftingTooltips(xPos + 156, yPos + 98, mouseX, mouseY, craftingGrid, craftingResult); }else if(pageNumber == (sections.get(sections.size()-1).pageNumber-1)/2 + 2){ craftingGrid = new ItemStack[3][3]; - craftingGrid[0][0] = new ItemStack(WizardryItems.magic_silk); - craftingGrid[1][0] = new ItemStack(WizardryItems.magic_silk); - craftingGrid[2][0] = new ItemStack(WizardryItems.magic_silk); - craftingGrid[0][1] = new ItemStack(WizardryItems.magic_silk); - craftingGrid[2][1] = new ItemStack(WizardryItems.magic_silk); - craftingResult = new ItemStack(WizardryItems.wizard_hat); + craftingGrid[0][0] = new ItemStack(Wizardry.magicSilk); + craftingGrid[1][0] = new ItemStack(Wizardry.magicSilk); + craftingGrid[2][0] = new ItemStack(Wizardry.magicSilk); + craftingGrid[0][1] = new ItemStack(Wizardry.magicSilk); + craftingGrid[2][1] = new ItemStack(Wizardry.magicSilk); + craftingResult = new ItemStack(Wizardry.wizardHat); this.renderCraftingTooltips(xPos + 23, yPos + 39, mouseX, mouseY, craftingGrid, craftingResult); craftingGrid = new ItemStack[3][3]; - craftingGrid[0][0] = new ItemStack(WizardryItems.magic_silk); - craftingGrid[2][0] = new ItemStack(WizardryItems.magic_silk); - craftingGrid[0][1] = new ItemStack(WizardryItems.magic_silk); - craftingGrid[1][1] = new ItemStack(WizardryItems.magic_silk); - craftingGrid[2][1] = new ItemStack(WizardryItems.magic_silk); - craftingGrid[0][2] = new ItemStack(WizardryItems.magic_silk); - craftingGrid[1][2] = new ItemStack(WizardryItems.magic_silk); - craftingGrid[2][2] = new ItemStack(WizardryItems.magic_silk); - craftingResult = new ItemStack(WizardryItems.wizard_robe); + craftingGrid[0][0] = new ItemStack(Wizardry.magicSilk); + craftingGrid[2][0] = new ItemStack(Wizardry.magicSilk); + craftingGrid[0][1] = new ItemStack(Wizardry.magicSilk); + craftingGrid[1][1] = new ItemStack(Wizardry.magicSilk); + craftingGrid[2][1] = new ItemStack(Wizardry.magicSilk); + craftingGrid[0][2] = new ItemStack(Wizardry.magicSilk); + craftingGrid[1][2] = new ItemStack(Wizardry.magicSilk); + craftingGrid[2][2] = new ItemStack(Wizardry.magicSilk); + craftingResult = new ItemStack(Wizardry.wizardRobe); this.renderCraftingTooltips(xPos + 23, yPos + 98, mouseX, mouseY, craftingGrid, craftingResult); craftingGrid = new ItemStack[3][3]; - craftingGrid[0][0] = new ItemStack(WizardryItems.magic_silk); - craftingGrid[1][0] = new ItemStack(WizardryItems.magic_silk); - craftingGrid[2][0] = new ItemStack(WizardryItems.magic_silk); - craftingGrid[0][1] = new ItemStack(WizardryItems.magic_silk); - craftingGrid[2][1] = new ItemStack(WizardryItems.magic_silk); - craftingGrid[0][2] = new ItemStack(WizardryItems.magic_silk); - craftingGrid[2][2] = new ItemStack(WizardryItems.magic_silk); - craftingResult = new ItemStack(WizardryItems.wizard_leggings); + craftingGrid[0][0] = new ItemStack(Wizardry.magicSilk); + craftingGrid[1][0] = new ItemStack(Wizardry.magicSilk); + craftingGrid[2][0] = new ItemStack(Wizardry.magicSilk); + craftingGrid[0][1] = new ItemStack(Wizardry.magicSilk); + craftingGrid[2][1] = new ItemStack(Wizardry.magicSilk); + craftingGrid[0][2] = new ItemStack(Wizardry.magicSilk); + craftingGrid[2][2] = new ItemStack(Wizardry.magicSilk); + craftingResult = new ItemStack(Wizardry.wizardLeggings); this.renderCraftingTooltips(xPos + 156, yPos + 39, mouseX, mouseY, craftingGrid, craftingResult); craftingGrid = new ItemStack[3][3]; - craftingGrid[0][0] = new ItemStack(WizardryItems.magic_silk); - craftingGrid[2][0] = new ItemStack(WizardryItems.magic_silk); - craftingGrid[0][1] = new ItemStack(WizardryItems.magic_silk); - craftingGrid[2][1] = new ItemStack(WizardryItems.magic_silk); - craftingResult = new ItemStack(WizardryItems.wizard_boots); + craftingGrid[0][0] = new ItemStack(Wizardry.magicSilk); + craftingGrid[2][0] = new ItemStack(Wizardry.magicSilk); + craftingGrid[0][1] = new ItemStack(Wizardry.magicSilk); + craftingGrid[2][1] = new ItemStack(Wizardry.magicSilk); + craftingResult = new ItemStack(Wizardry.wizardBoots); this.renderCraftingTooltips(xPos + 156, yPos + 98, mouseX, mouseY, craftingGrid, craftingResult); }else if(pageNumber == (sections.get(sections.size()-1).pageNumber-1)/2 + 3){ - if(Wizardry.settings.useAlternateScrollRecipe){ + if(Wizardry.useAlternateScrollRecipe){ craftingGrid = new ItemStack[3][3]; - craftingGrid[0][0] = new ItemStack(Items.PAPER); - craftingGrid[1][0] = new ItemStack(Items.STRING); - craftingGrid[2][0] = new ItemStack(WizardryItems.magic_crystal); - craftingResult = new ItemStack(WizardryItems.blank_scroll); + craftingGrid[0][0] = new ItemStack(Items.paper); + craftingGrid[1][0] = new ItemStack(Items.string); + craftingGrid[2][0] = new ItemStack(Wizardry.magicCrystal); + craftingResult = new ItemStack(Wizardry.blankScroll); this.renderCraftingTooltips(xPos + 23, yPos + 39, mouseX, mouseY, craftingGrid, craftingResult); }else{ craftingGrid = new ItemStack[3][3]; - craftingGrid[0][0] = new ItemStack(Items.PAPER); - craftingGrid[1][0] = new ItemStack(Items.STRING); - craftingResult = new ItemStack(WizardryItems.blank_scroll); + craftingGrid[0][0] = new ItemStack(Items.paper); + craftingGrid[1][0] = new ItemStack(Items.string); + craftingResult = new ItemStack(Wizardry.blankScroll); this.renderCraftingTooltips(xPos + 23, yPos + 39, mouseX, mouseY, craftingGrid, craftingResult); } - if(Wizardry.settings.firebombIsCraftable){ + if(Wizardry.firebombIsCraftable){ craftingGrid = new ItemStack[3][3]; - craftingGrid[0][0] = new ItemStack(Items.BLAZE_POWDER); - craftingGrid[1][0] = new ItemStack(Items.BLAZE_POWDER); - craftingGrid[0][1] = new ItemStack(Items.GLASS_BOTTLE); - craftingGrid[1][1] = new ItemStack(Items.GUNPOWDER); - craftingResult = new ItemStack(WizardryItems.firebomb, 3); + craftingGrid[0][0] = new ItemStack(Items.blaze_powder); + craftingGrid[1][0] = new ItemStack(Items.blaze_powder); + craftingGrid[0][1] = new ItemStack(Items.glass_bottle); + craftingGrid[1][1] = new ItemStack(Items.gunpowder); + craftingResult = new ItemStack(Wizardry.firebomb, 3); this.renderCraftingTooltips(xPos + 23, yPos + 98, mouseX, mouseY, craftingGrid, craftingResult); } - if(Wizardry.settings.poisonBombIsCraftable){ + if(Wizardry.poisonBombIsCraftable){ craftingGrid = new ItemStack[3][3]; - craftingGrid[0][0] = new ItemStack(Items.SPIDER_EYE); - craftingGrid[1][0] = new ItemStack(Items.SPIDER_EYE); - craftingGrid[0][1] = new ItemStack(Items.GLASS_BOTTLE); - craftingGrid[1][1] = new ItemStack(Items.GUNPOWDER); - craftingResult = new ItemStack(WizardryItems.poison_bomb, 3); + craftingGrid[0][0] = new ItemStack(Items.spider_eye); + craftingGrid[1][0] = new ItemStack(Items.spider_eye); + craftingGrid[0][1] = new ItemStack(Items.glass_bottle); + craftingGrid[1][1] = new ItemStack(Items.gunpowder); + craftingResult = new ItemStack(Wizardry.poisonBomb, 3); this.renderCraftingTooltips(xPos + 156, yPos + 39, mouseX, mouseY, craftingGrid, craftingResult); } - if(Wizardry.settings.smokeBombIsCraftable){ + if(Wizardry.smokeBombIsCraftable){ craftingGrid = new ItemStack[3][3]; - craftingGrid[0][0] = new ItemStack(Items.COAL); - craftingGrid[1][0] = new ItemStack(Items.COAL); - craftingGrid[0][1] = new ItemStack(Items.GLASS_BOTTLE); - craftingGrid[1][1] = new ItemStack(Items.GUNPOWDER); - craftingResult = new ItemStack(WizardryItems.smoke_bomb, 3); + craftingGrid[0][0] = new ItemStack(Items.coal); + craftingGrid[1][0] = new ItemStack(Items.coal); + craftingGrid[0][1] = new ItemStack(Items.glass_bottle); + craftingGrid[1][1] = new ItemStack(Items.gunpowder); + craftingResult = new ItemStack(Wizardry.smokeBomb, 3); this.renderCraftingTooltips(xPos + 156, yPos + 98, mouseX, mouseY, craftingGrid, craftingResult); } @@ -475,30 +485,33 @@ public class GuiWizardHandbook extends GuiScreen { private void renderCraftingRecipe(int xPos, int yPos, int mouseX, int mouseY, ItemStack[][] craftingGrid, ItemStack craftingResult) { - GlStateManager.pushMatrix(); + int guiLeft = this.width/2 - xSize/2; + int guiTop = this.height/2 - this.ySize/2; + + GL11.glPushMatrix(); RenderHelper.enableGUIStandardItemLighting(); - GlStateManager.disableLighting(); - GlStateManager.enableRescaleNormal(); + GL11.glDisable(GL11.GL_LIGHTING); + GL11.glEnable(GL12.GL_RESCALE_NORMAL); GL11.glEnable(GL11.GL_COLOR_MATERIAL); - GlStateManager.enableLighting(); + GL11.glEnable(GL11.GL_LIGHTING); itemRender.zLevel = 100.0F; for(int i=0; i>(1); sections = new ArrayList

    (1); @@ -561,7 +574,7 @@ public class GuiWizardHandbook extends GuiScreen { } catch (IOException e){ - Wizardry.logger.info("Wizard handbook text file missing for the current language. Using default (English - US) instead."); + System.out.println("Warning: wizard handbook text file missing for the current language. Using default (English - US) instead."); textFilepath = "wizardry:texts/handbook_en_US.txt"; @@ -570,8 +583,8 @@ public class GuiWizardHandbook extends GuiScreen { bufferedreader = new BufferedReader(new InputStreamReader(this.mc.getResourceManager().getResource(new ResourceLocation(textFilepath)).getInputStream(), Charsets.UTF_8)); } catch (IOException x){ - Wizardry.logger.error("Couldn't find file: wizardry:assets/texts/handbook_en_US.txt. The file may be" - + "missing; please try re-downloading and reinstalling Wizardry.", x); + System.err.println("Couldn't find file: wizardry:assets/texts/handbook_en_US.txt. The file may be missing; please try re-downloading and reinstalling wizardry."); + x.printStackTrace(); } } @@ -626,29 +639,29 @@ public class GuiWizardHandbook extends GuiScreen { }else{ - paragraph = paragraph.replaceAll("NEXT_SPELL_KEY", Keyboard.getKeyName(ClientProxy.NEXT_SPELL.getKeyCode())); - paragraph = paragraph.replaceAll("PREVIOUS_SPELL_KEY", Keyboard.getKeyName(ClientProxy.PREVIOUS_SPELL.getKeyCode())); - paragraph = paragraph.replaceAll("MANA_PER_CRYSTAL_MINUS_30", "" + (Constants.MANA_PER_CRYSTAL - 30)); - paragraph = paragraph.replaceAll("MANA_PER_CRYSTAL", "" + Constants.MANA_PER_CRYSTAL); - paragraph = paragraph.replaceAll("BASIC_MAX_CHARGE", "" + Tier.BASIC.maxCharge); - paragraph = paragraph.replaceAll("APPRENTICE_MAX_CHARGE", "" + Tier.APPRENTICE.maxCharge); - paragraph = paragraph.replaceAll("ADVANCED_MAX_CHARGE", "" + Tier.ADVANCED.maxCharge); - paragraph = paragraph.replaceAll("MASTER_MAX_CHARGE", "" + Tier.MASTER.maxCharge); + paragraph = paragraph.replaceAll("NEXT_SPELL_KEY", Keyboard.getKeyName(ClientProxy.nextSpell.getKeyCode())); + paragraph = paragraph.replaceAll("PREVIOUS_SPELL_KEY", Keyboard.getKeyName(ClientProxy.previousSpell.getKeyCode())); + paragraph = paragraph.replaceAll("MANA_PER_CRYSTAL_MINUS_30", "" + (Wizardry.MANA_PER_CRYSTAL - 30)); + paragraph = paragraph.replaceAll("MANA_PER_CRYSTAL", "" + Wizardry.MANA_PER_CRYSTAL); + paragraph = paragraph.replaceAll("BASIC_MAX_CHARGE", "" + EnumTier.BASIC.maxCharge); + paragraph = paragraph.replaceAll("APPRENTICE_MAX_CHARGE", "" + EnumTier.APPRENTICE.maxCharge); + paragraph = paragraph.replaceAll("ADVANCED_MAX_CHARGE", "" + EnumTier.ADVANCED.maxCharge); + paragraph = paragraph.replaceAll("MASTER_MAX_CHARGE", "" + EnumTier.MASTER.maxCharge); paragraph = paragraph.replaceAll("BASIC_COLOUR", "\u00A77"); - paragraph = paragraph.replaceAll("APPRENTICE_COLOUR", Tier.APPRENTICE.getFormattingCode()); - paragraph = paragraph.replaceAll("ADVANCED_COLOUR", Tier.ADVANCED.getFormattingCode()); - paragraph = paragraph.replaceAll("MASTER_COLOUR", Tier.MASTER.getFormattingCode()); - paragraph = paragraph.replaceAll("FIRE_COLOUR", Element.FIRE.getFormattingCode()); - paragraph = paragraph.replaceAll("ICE_COLOUR", Element.ICE.getFormattingCode()); - paragraph = paragraph.replaceAll("LIGHTNING_COLOUR", Element.LIGHTNING.getFormattingCode()); - paragraph = paragraph.replaceAll("NECROMANCY_COLOUR", Element.NECROMANCY.getFormattingCode()); - paragraph = paragraph.replaceAll("EARTH_COLOUR", Element.EARTH.getFormattingCode()); - paragraph = paragraph.replaceAll("SORCERY_COLOUR", Element.SORCERY.getFormattingCode()); - paragraph = paragraph.replaceAll("HEALING_COLOUR", Element.HEALING.getFormattingCode()); + paragraph = paragraph.replaceAll("APPRENTICE_COLOUR", EnumTier.APPRENTICE.colour); + paragraph = paragraph.replaceAll("ADVANCED_COLOUR", EnumTier.ADVANCED.colour); + paragraph = paragraph.replaceAll("MASTER_COLOUR", EnumTier.MASTER.colour); + paragraph = paragraph.replaceAll("FIRE_COLOUR", EnumElement.FIRE.colour); + paragraph = paragraph.replaceAll("ICE_COLOUR", EnumElement.ICE.colour); + paragraph = paragraph.replaceAll("LIGHTNING_COLOUR", EnumElement.LIGHTNING.colour); + paragraph = paragraph.replaceAll("NECROMANCY_COLOUR", EnumElement.NECROMANCY.colour); + paragraph = paragraph.replaceAll("EARTH_COLOUR", EnumElement.EARTH.colour); + paragraph = paragraph.replaceAll("SORCERY_COLOUR", EnumElement.SORCERY.colour); + paragraph = paragraph.replaceAll("HEALING_COLOUR", EnumElement.HEALING.colour); paragraph = paragraph.replaceAll("RESET_COLOUR", "\u00A70"); paragraph = paragraph.replaceAll("VERSION", Wizardry.VERSION); - int linesInParagraph = this.fontRendererObj.listFormattedStringToWidth(paragraph, GuiWizardHandbook.PAGE_WIDTH).size(); + int linesInParagraph = this.fontRendererObj.listFormattedStringToWidth(paragraph, this.pageWidth).size(); // Ignores empty lines at the top of a page. if(paragraph.isEmpty() && lineNumber == 0){ @@ -674,7 +687,7 @@ public class GuiWizardHandbook extends GuiScreen { int i = 0; - List strings = this.fontRendererObj.listFormattedStringToWidth(paragraph, GuiWizardHandbook.PAGE_WIDTH); + List strings = this.fontRendererObj.listFormattedStringToWidth(paragraph, this.pageWidth); for(Object s : strings){ if(i < linesInFirstPart){ @@ -699,17 +712,21 @@ public class GuiWizardHandbook extends GuiScreen { text.add(page); } catch (IOException e){ - Wizardry.logger.error("Something went wrong reading file: " + textFilepath + ". The file may be damaged;" - + "please try re-downloading and reinstalling wizardry.", e); + System.err.println("Something went wrong reading file: " + textFilepath + ". The file may be damaged; please try re-downloading and reinstalling wizardry."); + e.printStackTrace(); } } + /* + for(ArrayList a : text){ + for(String s : a){ + System.out.println(s); + } + } + */ } private class Section { - /** The integer text colour used for the section when it is moused over. Currently orange. */ - private static final int HIGHLIGHT_COLOUR = 0xdd4c1d; - String name; int pageNumber; int x, y; @@ -721,32 +738,31 @@ public class GuiWizardHandbook extends GuiScreen { this.x = x; this.y = y; this.buttonId = id; - GuiWizardHandbook.this.buttonList.add(new GuiButtonInvisible(id, x, y, GuiWizardHandbook.PAGE_WIDTH, GuiWizardHandbook.this.fontRendererObj.FONT_HEIGHT)); + GuiWizardHandbook.this.buttonList.add(new GuiButtonInvisible(id, x, y, GuiWizardHandbook.this.pageWidth, GuiWizardHandbook.this.fontRendererObj.FONT_HEIGHT)); } void hideButton(){ - GuiWizardHandbook.this.buttonList.get(buttonId).visible = false; + ((GuiButton)GuiWizardHandbook.this.buttonList.get(buttonId)).visible = false; } void drawContents(){ - GuiWizardHandbook.this.buttonList.get(buttonId).visible = true; + ((GuiButton)GuiWizardHandbook.this.buttonList.get(buttonId)).visible = true; - GuiWizardHandbook.this.fontRendererObj.drawString(name, x, y, GuiWizardHandbook.this.buttonList.get(buttonId).isMouseOver() ? HIGHLIGHT_COLOUR : BLACK); + GuiWizardHandbook.this.fontRendererObj.drawString(name, x, y, ((GuiButton)GuiWizardHandbook.this.buttonList.get(buttonId)).func_146115_a() ? 0xdd4c1d : 0); int nameWidth = GuiWizardHandbook.this.fontRendererObj.getStringWidth(name); String dotsAndNumber = " " + this.pageNumber; - while(GuiWizardHandbook.this.fontRendererObj.getStringWidth(dotsAndNumber) < GuiWizardHandbook.PAGE_WIDTH - nameWidth - 2){ + while(GuiWizardHandbook.this.fontRendererObj.getStringWidth(dotsAndNumber) < GuiWizardHandbook.pageWidth - nameWidth - 2){ dotsAndNumber = "." + dotsAndNumber; } - GuiWizardHandbook.this.fontRendererObj.drawString(dotsAndNumber, x + GuiWizardHandbook.PAGE_WIDTH - GuiWizardHandbook.this.fontRendererObj.getStringWidth(dotsAndNumber), y, BLACK); + GuiWizardHandbook.this.fontRendererObj.drawString(dotsAndNumber, x + GuiWizardHandbook.pageWidth - GuiWizardHandbook.this.fontRendererObj.getStringWidth(dotsAndNumber), y, 0); } } - @Override public void onGuiClosed() { super.onGuiClosed(); @@ -756,7 +772,6 @@ public class GuiWizardHandbook extends GuiScreen { /** * Fired when a control is clicked. This is the equivalent of ActionListener.actionPerformed(ActionEvent e). */ - @Override protected void actionPerformed(GuiButton par1GuiButton){ if(par1GuiButton.enabled){ @@ -782,6 +797,127 @@ public class GuiWizardHandbook extends GuiScreen { par6 -= l1; return par5 >= par1 - 1 && par5 < par1 + par3 + 1 && par6 >= par2 - 1 && par6 < par2 + par4 + 1; } + + protected void drawItemStackTooltip(ItemStack par1ItemStack, int par2, int par3) + { + List list = par1ItemStack.getTooltip(this.mc.thePlayer, this.mc.gameSettings.advancedItemTooltips); + + for (int k = 0; k < list.size(); ++k) + { + if (k == 0) + { + list.set(k, par1ItemStack.getRarity().rarityColor + (String)list.get(k)); + } + else + { + list.set(k, EnumChatFormatting.GRAY + (String)list.get(k)); + } + } + + FontRenderer font = par1ItemStack.getItem().getFontRenderer(par1ItemStack); + drawHoveringText(list, par2, par3, (font == null ? fontRendererObj : font)); + } + + protected void drawHoveringText(List par1List, int par2, int par3, FontRenderer font) + { + if (!par1List.isEmpty()) + { + GL11.glDisable(GL12.GL_RESCALE_NORMAL); + RenderHelper.disableStandardItemLighting(); + GL11.glDisable(GL11.GL_LIGHTING); + GL11.glDisable(GL11.GL_DEPTH_TEST); + int k = 0; + Iterator iterator = par1List.iterator(); + + while (iterator.hasNext()) + { + String s = (String)iterator.next(); + int l = font.getStringWidth(s); + + if (l > k) + { + k = l; + } + } + + int i1 = par2 + 12; + int j1 = par3 - 12; + int k1 = 8; + + if (par1List.size() > 1) + { + k1 += 2 + (par1List.size() - 1) * 10; + } + + if (i1 + k > this.width) + { + i1 -= 28 + k; + } + + if (j1 + k1 + 6 > this.height) + { + j1 = this.height - k1 - 6; + } + + this.zLevel = 300.0F; + itemRender.zLevel = 300.0F; + int l1 = -267386864; + this.drawGradientRect(i1 - 3, j1 - 4, i1 + k + 3, j1 - 3, l1, l1); + this.drawGradientRect(i1 - 3, j1 + k1 + 3, i1 + k + 3, j1 + k1 + 4, l1, l1); + this.drawGradientRect(i1 - 3, j1 - 3, i1 + k + 3, j1 + k1 + 3, l1, l1); + this.drawGradientRect(i1 - 4, j1 - 3, i1 - 3, j1 + k1 + 3, l1, l1); + this.drawGradientRect(i1 + k + 3, j1 - 3, i1 + k + 4, j1 + k1 + 3, l1, l1); + int i2 = 1347420415; + int j2 = (i2 & 16711422) >> 1 | i2 & -16777216; + this.drawGradientRect(i1 - 3, j1 - 3 + 1, i1 - 3 + 1, j1 + k1 + 3 - 1, i2, j2); + this.drawGradientRect(i1 + k + 2, j1 - 3 + 1, i1 + k + 3, j1 + k1 + 3 - 1, i2, j2); + this.drawGradientRect(i1 - 3, j1 - 3, i1 + k + 3, j1 - 3 + 1, i2, i2); + this.drawGradientRect(i1 - 3, j1 + k1 + 2, i1 + k + 3, j1 + k1 + 3, j2, j2); + + for(int k2 = 0; k2 < par1List.size(); ++k2){ + + String s1 = (String)par1List.get(k2); + font.drawStringWithShadow(s1, i1, j1, -1); + + if(k2 == 0){ + j1 += 2; + } + + j1 += 10; + } + + this.zLevel = 0.0F; + itemRender.zLevel = 0.0F; + GL11.glEnable(GL11.GL_LIGHTING); + GL11.glEnable(GL11.GL_DEPTH_TEST); + RenderHelper.enableStandardItemLighting(); + GL11.glEnable(GL12.GL_RESCALE_NORMAL); + } + } + + /** + * Draws a textured rectangle, taking the size of the image and the bit needed into account. + * @param x The x position of the rectangle + * @param y The y position of the rectangle + * @param u The x position of the top left corner of the section of the image wanted + * @param v The y position of the top left corner of the section of the image wanted + * @param width The width of the section + * @param height The height of the section + * @param textureWidth The width of the actual image. + * @param textureHeight The height of the actual image. + */ + public void drawTexturedRect(int x, int y, int u, int v, int width, int height, int textureWidth, int textureHeight) + { + float f = 1F / (float)textureWidth; + float f1 = 1F / (float)textureHeight; + Tessellator tessellator = Tessellator.instance; + tessellator.startDrawingQuads(); + tessellator.addVertexWithUV((double)(x), (double)(y + height), 0, (double)((float)(u) * f), (double)((float)(v + height) * f1)); + tessellator.addVertexWithUV((double)(x + width), (double)(y + height), 0, (double)((float)(u + width) * f), (double)((float)(v + height) * f1)); + tessellator.addVertexWithUV((double)(x + width), (double)(y), 0, (double)((float)(u + width) * f), (double)((float)(v) * f1)); + tessellator.addVertexWithUV((double)(x), (double)(y), 0, (double)((float)(u) * f), (double)((float)(v) * f1)); + tessellator.draw(); + } /** * Draws a textured rectangle, stretching the section of the image to fit the size given. @@ -794,16 +930,16 @@ public class GuiWizardHandbook extends GuiScreen { * @param width The width of the section, expressed as a fraction of the image width * @param height The height of the section, expressed as a fraction of the image width */ - public static void drawTexturedStretchedRect(int x, int y, int u, int v, int finalWidth, int finalHeight, int width, int height){ - - Tessellator tessellator = Tessellator.getInstance(); - VertexBuffer buffer = tessellator.getBuffer(); - buffer.begin(GL11.GL_QUADS, DefaultVertexFormats.POSITION_TEX); - buffer.pos((x), y + finalHeight, 0).tex(u, v + height).endVertex(); - buffer.pos(x + finalWidth, y + finalHeight, 0).tex(u + width, v + height).endVertex(); - buffer.pos(x + finalWidth, (y), 0).tex(u + width, v).endVertex(); - buffer.pos((x), (y), 0).tex(u, v).endVertex(); - tessellator.draw(); + public void drawTexturedStretchedRect(int x, int y, int u, int v, int finalWidth, int finalHeight, int width, int height) + { + Tessellator tessellator = Tessellator.instance; + tessellator.startDrawingQuads(); + tessellator.addVertexWithUV((double)(x), (double)(y + finalHeight), 0, u, v + height); + tessellator.addVertexWithUV((double)(x + finalWidth), (double)(y + finalHeight), 0, u + width, v + height); + tessellator.addVertexWithUV((double)(x + finalWidth), (double)(y), 0, u + width, v); + tessellator.addVertexWithUV((double)(x), (double)(y), 0, u, v); + tessellator.draw(); } -} \ No newline at end of file +} + diff --git a/src/main/java/electroblob/wizardry/client/MixedFontRenderer.java b/src/main/java/electroblob/wizardry/client/MixedFontRenderer.java index 43b51f8b..4124cc49 100644 --- a/src/main/java/electroblob/wizardry/client/MixedFontRenderer.java +++ b/src/main/java/electroblob/wizardry/client/MixedFontRenderer.java @@ -1,12 +1,15 @@ package electroblob.wizardry.client; +import java.util.ArrayList; +import java.util.List; + +import cpw.mods.fml.relauncher.Side; +import cpw.mods.fml.relauncher.SideOnly; import net.minecraft.client.Minecraft; import net.minecraft.client.gui.FontRenderer; import net.minecraft.client.renderer.texture.TextureManager; import net.minecraft.client.settings.GameSettings; import net.minecraft.util.ResourceLocation; -import net.minecraftforge.fml.relauncher.Side; -import net.minecraftforge.fml.relauncher.SideOnly; /** Font renderer that renders parts of strings surrounded by '#' (without quotes) in the SGA instead of normal text. * @since Wizardry 1.1 */ @@ -19,7 +22,7 @@ public class MixedFontRenderer extends FontRenderer { } @Override - public int drawString(String string, float x, float y, int colour, boolean shadow){ + public int drawString(String string, int x, int y, int colour, boolean shadow){ int l = 0; @@ -33,8 +36,8 @@ public class MixedFontRenderer extends FontRenderer { l += Minecraft.getMinecraft().standardGalacticFontRenderer.drawString(section, x, y, colour, shadow); x += Minecraft.getMinecraft().standardGalacticFontRenderer.getStringWidth(section); }else{ - l += Minecraft.getMinecraft().fontRendererObj.drawString(section, x, y, colour, shadow); - x += Minecraft.getMinecraft().fontRendererObj.getStringWidth(section); + l += Minecraft.getMinecraft().fontRenderer.drawString(section, x, y, colour, shadow); + x += Minecraft.getMinecraft().fontRenderer.getStringWidth(section); } string = string.substring(string.indexOf('#') + 1); @@ -44,7 +47,7 @@ public class MixedFontRenderer extends FontRenderer { if(sga){ l += Minecraft.getMinecraft().standardGalacticFontRenderer.drawString(string, x, y, colour, shadow); }else{ - l += Minecraft.getMinecraft().fontRendererObj.drawString(string, x, y, colour, shadow); + l += Minecraft.getMinecraft().fontRenderer.drawString(string, x, y, colour, shadow); } return l; @@ -64,7 +67,7 @@ public class MixedFontRenderer extends FontRenderer { if(sga){ l += Minecraft.getMinecraft().standardGalacticFontRenderer.getStringWidth(section); }else{ - l += Minecraft.getMinecraft().fontRendererObj.getStringWidth(section); + l += Minecraft.getMinecraft().fontRenderer.getStringWidth(section); } string = string.substring(string.indexOf('#') + 1); @@ -74,7 +77,7 @@ public class MixedFontRenderer extends FontRenderer { if(sga){ l += Minecraft.getMinecraft().standardGalacticFontRenderer.getStringWidth(string); }else{ - l += Minecraft.getMinecraft().fontRendererObj.getStringWidth(string); + l += Minecraft.getMinecraft().fontRenderer.getStringWidth(string); } return l; @@ -87,7 +90,7 @@ public class MixedFontRenderer extends FontRenderer { if(string.contains("#")){ Minecraft.getMinecraft().standardGalacticFontRenderer.drawSplitString(string.substring(1), x, y, width, colour); }else{ - Minecraft.getMinecraft().fontRendererObj.drawSplitString(string, x, y, width, colour); + Minecraft.getMinecraft().fontRenderer.drawSplitString(string, x, y, width, colour); } } diff --git a/src/main/java/electroblob/wizardry/client/MovingSoundEntity.java b/src/main/java/electroblob/wizardry/client/MovingSoundEntity.java index 3696e866..d10388e9 100644 --- a/src/main/java/electroblob/wizardry/client/MovingSoundEntity.java +++ b/src/main/java/electroblob/wizardry/client/MovingSoundEntity.java @@ -1,35 +1,31 @@ package electroblob.wizardry.client; +import cpw.mods.fml.relauncher.Side; +import cpw.mods.fml.relauncher.SideOnly; import net.minecraft.client.audio.MovingSound; import net.minecraft.entity.Entity; -import net.minecraft.util.SoundCategory; -import net.minecraft.util.SoundEvent; -import net.minecraft.util.math.MathHelper; -import net.minecraftforge.fml.relauncher.Side; -import net.minecraftforge.fml.relauncher.SideOnly; +import net.minecraft.util.MathHelper; +import net.minecraft.util.ResourceLocation; -// Copied from MovingSoundMinecart; if it ever breaks between updates take a look at that. @SideOnly(Side.CLIENT) public class MovingSoundEntity extends MovingSound { private final Entity source; - private float distance = 0.0F; + private float pitch = 0.0F; - public MovingSoundEntity(Entity entity, SoundEvent sound, float volume, float pitch, boolean repeat) + public MovingSoundEntity(Entity entity, String soundName, float volume, float pitch, boolean repeat) { - // Uses BLOCKS because that's the closest thing to inanimate entities. Could use NEUTRAL like MovingSoundMinecart. - super(sound, SoundCategory.BLOCKS); + super(new ResourceLocation(soundName)); this.source = entity; this.repeat = repeat; this.volume = volume; - this.pitch = pitch; - this.repeatDelay = 0; + this.field_147663_c = pitch; + this.field_147665_h = 0; } /** * Updates the JList with a new model. */ - @Override public void update() { if (this.source.isDead && repeat) @@ -46,7 +42,7 @@ public class MovingSoundEntity extends MovingSound // Is this something to do with the Doppler effect? if ((double)f >= 0.01D) { - this.distance = MathHelper.clamp_float(this.distance + 0.0025F, 0.0F, 1.0F); + this.pitch = MathHelper.clamp_float(this.pitch + 0.0025F, 0.0F, 1.0F); this.volume = 0.0F + MathHelper.clamp_float(f, 0.0F, 0.5F) * 0.7F; } else diff --git a/src/main/java/electroblob/wizardry/client/WizardryClientEventHandler.java b/src/main/java/electroblob/wizardry/client/WizardryClientEventHandler.java index 84b0d552..5a117bd6 100644 --- a/src/main/java/electroblob/wizardry/client/WizardryClientEventHandler.java +++ b/src/main/java/electroblob/wizardry/client/WizardryClientEventHandler.java @@ -2,130 +2,116 @@ package electroblob.wizardry.client; import org.lwjgl.opengl.GL11; -import electroblob.wizardry.WizardData; +import cpw.mods.fml.common.eventhandler.SubscribeEvent; +import cpw.mods.fml.common.network.simpleimpl.IMessage; +import electroblob.wizardry.ExtendedPlayer; +import electroblob.wizardry.WandHelper; import electroblob.wizardry.Wizardry; -import electroblob.wizardry.constants.Constants; +import electroblob.wizardry.WizardryUtilities; +import electroblob.wizardry.entity.EntityShield; +import electroblob.wizardry.item.ItemSpectralArmour; import electroblob.wizardry.item.ItemSpectralBow; import electroblob.wizardry.item.ItemWand; import electroblob.wizardry.packet.PacketControlInput; import electroblob.wizardry.packet.WizardryPacketHandler; -import electroblob.wizardry.registry.WizardryPotions; import electroblob.wizardry.spell.Flight; import electroblob.wizardry.spell.ShadowWard; import electroblob.wizardry.spell.Shield; -import electroblob.wizardry.tileentity.ContainerArcaneWorkbench; -import electroblob.wizardry.util.WandHelper; -import electroblob.wizardry.util.WizardryUtilities; +import electroblob.wizardry.spell.Spell; import net.minecraft.client.Minecraft; -import net.minecraft.client.renderer.GlStateManager; import net.minecraft.client.renderer.OpenGlHelper; import net.minecraft.client.renderer.Tessellator; -import net.minecraft.client.renderer.VertexBuffer; import net.minecraft.client.renderer.entity.RenderManager; -import net.minecraft.client.renderer.vertex.DefaultVertexFormats; import net.minecraft.entity.EntityLivingBase; import net.minecraft.entity.player.EntityPlayer; import net.minecraft.item.ItemStack; +import net.minecraft.util.MovingObjectPosition; import net.minecraft.util.ResourceLocation; -import net.minecraft.util.math.RayTraceResult; import net.minecraftforge.client.event.FOVUpdateEvent; import net.minecraftforge.client.event.MouseEvent; import net.minecraftforge.client.event.RenderGameOverlayEvent; import net.minecraftforge.client.event.RenderLivingEvent; import net.minecraftforge.client.event.RenderPlayerEvent; import net.minecraftforge.client.event.RenderWorldLastEvent; -import net.minecraftforge.client.event.TextureStitchEvent; -import net.minecraftforge.fml.common.Mod; -import net.minecraftforge.fml.common.eventhandler.SubscribeEvent; -import net.minecraftforge.fml.common.network.simpleimpl.IMessage; -import net.minecraftforge.fml.relauncher.Side; -/** - * Event handler responsible for all client-side only events, mostly rendering. - * @author Electroblob - * @since Wizardry 1.0 - */ -@Mod.EventBusSubscriber(Side.CLIENT) -public final class WizardryClientEventHandler { +/** Event handler responsible for all client-side only events, mostly rendering. + * @since Wizardry 1.0 */ +public class WizardryClientEventHandler { - private static final ResourceLocation shieldTexture = new ResourceLocation(Wizardry.MODID, "textures/entity/shield.png"); - private static final ResourceLocation wingTexture = new ResourceLocation(Wizardry.MODID, "textures/entity/wing.png"); - private static final ResourceLocation shadowWardTexture = new ResourceLocation(Wizardry.MODID, "textures/entity/shadow_ward.png"); - private static final ResourceLocation sixthSenseTexture = new ResourceLocation(Wizardry.MODID, "textures/entity/sixth_sense.png"); - private static final ResourceLocation sixthSenseOverlayTexture = new ResourceLocation(Wizardry.MODID, "textures/gui/sixth_sense_overlay.png"); - private static final ResourceLocation frostOverlayTexture = new ResourceLocation(Wizardry.MODID, "textures/gui/frost_overlay.png"); - private static final ResourceLocation pointerTexture = new ResourceLocation(Wizardry.MODID, "textures/entity/pointer.png"); - private static final ResourceLocation targetPointerTexture = new ResourceLocation(Wizardry.MODID, "textures/entity/target_pointer.png"); + private static final ResourceLocation shieldTexture = new ResourceLocation("wizardry:textures/entity/shield.png"); + private static final ResourceLocation wingTexture = new ResourceLocation("wizardry:textures/entity/wing.png"); + private static final ResourceLocation shadowWardTexture = new ResourceLocation("wizardry:textures/entity/shadow_ward.png"); + private static final ResourceLocation sixthSenseTexture = new ResourceLocation("wizardry:textures/entity/sixth_sense.png"); + private static final ResourceLocation sixthSenseOverlayTexture = new ResourceLocation("wizardry:textures/gui/sixth_sense_overlay.png"); + private static final ResourceLocation frostOverlayTexture = new ResourceLocation("wizardry:textures/gui/frost_overlay.png"); + private static final ResourceLocation pointerTexture = new ResourceLocation("wizardry:textures/entity/pointer.png"); + private static final ResourceLocation targetPointerTexture = new ResourceLocation("wizardry:textures/entity/target_pointer.png"); @SubscribeEvent - public static void onTextureStitchEvent(TextureStitchEvent.Pre event){ - event.getMap().registerSprite(ContainerArcaneWorkbench.EMPTY_SLOT_CRYSTAL); - event.getMap().registerSprite(ContainerArcaneWorkbench.EMPTY_SLOT_UPGRADE); + public void onRenderPlayerArmourEvent(RenderPlayerEvent.SetArmorModel event){ + + ItemStack stack = event.entityPlayer.inventory.armorInventory[event.slot]; + + if(stack != null && stack.getItem() instanceof ItemSpectralArmour){ + GL11.glEnable(GL11.GL_BLEND); + } } // Shift-scrolling to change spells @SubscribeEvent - public static void onMouseEvent(MouseEvent event){ - - EntityPlayer player = Minecraft.getMinecraft().thePlayer; - ItemStack wand = player.getHeldItemMainhand(); - - if(wand == null || !(wand.getItem() instanceof ItemWand)){ - wand = player.getHeldItemOffhand(); - // If the player isn't holding a wand, then nothing else needs to be done. - if(wand == null || !(wand.getItem() instanceof ItemWand)) return; - } - - if(Minecraft.getMinecraft().inGameHasFocus && wand != null && event.getDwheel() != 0 && player.isSneaking() - && Wizardry.settings.enableShiftScrolling){ + public void onMouseEvent(MouseEvent event){ + if(Minecraft.getMinecraft().inGameHasFocus && Minecraft.getMinecraft().thePlayer.getHeldItem() != null + && Minecraft.getMinecraft().thePlayer.getHeldItem().getItem() instanceof ItemWand && event.dwheel != 0 + && Minecraft.getMinecraft().thePlayer.isSneaking() && Wizardry.enableShiftScrolling){ event.setCanceled(true); - if(event.getDwheel() > 0){ + if(event.dwheel > 0){ // Packet building - IMessage msg = new PacketControlInput.Message(PacketControlInput.ControlType.PREVIOUS_SPELL_KEY); + IMessage msg = new PacketControlInput.Message(2); WizardryPacketHandler.net.sendToServer(msg); - }else if(event.getDwheel() < 0){ + }else if(event.dwheel < 0){ // Packet building - IMessage msg = new PacketControlInput.Message(PacketControlInput.ControlType.NEXT_SPELL_KEY); + IMessage msg = new PacketControlInput.Message(1); WizardryPacketHandler.net.sendToServer(msg); } } } @SubscribeEvent - public static void onFOVUpdateEvent(FOVUpdateEvent event){ + public void onFOVUpdateEvent(FOVUpdateEvent event){ - // Bow zoom. Taken directly from AbstractClientPlayer so it works exactly like vanilla. - if(event.getEntity().isHandActive() && event.getEntity().getActiveItemStack() != null - && event.getEntity().getActiveItemStack().getItem() instanceof ItemSpectralBow){ - - int maxUseTicks = event.getEntity().getItemInUseMaxCount(); - - float maxUseSeconds = (float)maxUseTicks / 20.0F; + // Bow zoom. Taken directly from EntityPlayerSP so it works exactly like vanilla. + if (event.entity.isUsingItem() && event.entity.getItemInUse().getItem() instanceof ItemSpectralBow) + { + int i = event.entity.getItemInUseDuration(); + float f1 = (float)i / 20.0F; - if(maxUseSeconds > 1.0F){ - maxUseSeconds = 1.0F; - }else{ - maxUseSeconds = maxUseSeconds * maxUseSeconds; - } + if (f1 > 1.0F) + { + f1 = 1.0F; + } + else + { + f1 *= f1; + } - event.setNewfov(event.getFov() * 1.0F - maxUseSeconds * 0.15F); - } + event.newfov *= 1.0F - f1 * 0.15F; + } } // Third person @SubscribeEvent - public static void onRenderPlayerEvent(RenderPlayerEvent.Post event){ - renderShieldIfActive(event.getEntityPlayer()); - renderWingsIfActive(event.getEntityPlayer(), event.getPartialRenderTick()); - renderShadowWardIfActive(event.getEntityPlayer()); + public void onRenderPlayerEvent(RenderPlayerEvent.Specials.Post event){ + renderShieldIfActive(event.entityPlayer); + renderWingsIfActive(event.entityPlayer, event.partialRenderTick); + renderShadowWardIfActive(event.entityPlayer); } // First person @SubscribeEvent - public static void onRenderWorldLastEvent(RenderWorldLastEvent event){ + public void onRenderWorldLastEvent(RenderWorldLastEvent event){ // Now only fires in first person. if(Minecraft.getMinecraft().gameSettings.thirdPersonView == 0){ renderShieldFirstPerson(Minecraft.getMinecraft().thePlayer); @@ -134,15 +120,15 @@ public final class WizardryClientEventHandler { } @SubscribeEvent - public static void onRenderLivingEvent(RenderLivingEvent.Post event){ + public void onRenderLivingEvent(RenderLivingEvent.Post event){ /* // Frost effect if(event.entity.isPotionActive(Wizardry.frost)){ - GlStateManager.pushMatrix(); + GL11.glPushMatrix(); GL11.glDisable(GL11.GL_TEXTURE_2D); - GlStateManager.enableBlend(); - GlStateManager.blendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA); + GL11.glEnable(GL11.GL_BLEND); + GL11.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA); float someScalingFactor = 0.0625f; @@ -153,16 +139,16 @@ public final class WizardryClientEventHandler { //int j = brightness % 65536; //int k = brightness / 65536; //OpenGlHelper.setLightmapTextureCoords(OpenGlHelper.lightmapTexUnit, (float)j / 1.0F, (float)k / 1.0F); - //GlStateManager.color(1.0F, 1.0F, 1.0F, 1.0F); + //GL11.glColor4f(1.0F, 1.0F, 1.0F, 1.0F); - GlStateManager.translate(event.x, event.y, event.z); + GL11.glTranslated(event.x, event.y, event.z); - GlStateManager.rotate(-yaw + 180, 0F, 1F, 0F); + GL11.glRotatef(-yaw + 180, 0F, 1F, 0F); - Render render = renderManager.getEntityRenderObject(event.entity); + Render render = RenderManager.instance.getEntityRenderObject(event.entity); - RenderLiving renderliving = event.renderer; + RendererLivingEntity renderliving = event.renderer; // Reflection @@ -180,20 +166,20 @@ public final class WizardryClientEventHandler { // Turns out that java automatically infers the type parameter T in this method from the type // I am assigning the returned value to. Neat! - ModelBase mainModel = ReflectionHelper.getPrivateValue(RenderLiving.class, renderliving, "mainModel"); + ModelBase mainModel = ReflectionHelper.getPrivateValue(RendererLivingEntity.class, renderliving, "mainModel"); mainModel.isRiding = event.entity.isRiding(); mainModel.isChild = event.entity.isChild(); - GlStateManager.enableRescaleNormal(); - GlStateManager.scale(-1.0F, -1.0F, 1.0F); + GL11.glEnable(GL12.GL_RESCALE_NORMAL); + GL11.glScalef(-1.0F, -1.0F, 1.0F); // The second argument is never used... - ReflectionHelper.findMethod(RenderLiving.class, renderliving, new String[]{"preRenderCallback"}, EntityLivingBase.class, float.class) + ReflectionHelper.findMethod(RendererLivingEntity.class, renderliving, new String[]{"preRenderCallback"}, EntityLivingBase.class, float.class) .invoke(renderliving, event.entity, someScalingFactor); // Why is this -1.5f? No idea! - GlStateManager.translate(0, -1.5f, 0); + GL11.glTranslatef(0, -1.5f, 0); float f6 = event.entity.prevLimbSwingAmount + (event.entity.limbSwingAmount - event.entity.prevLimbSwingAmount) * timer.renderPartialTicks; float f7 = event.entity.limbSwing - event.entity.limbSwingAmount * (1.0F - timer.renderPartialTicks); @@ -210,8 +196,8 @@ public final class WizardryClientEventHandler { mainModel.setLivingAnimations(event.entity, f7, f6, timer.renderPartialTicks); - GlStateManager.enableAlpha(); - GlStateManager.color(0.5f, 0.7f, 1, 0.5f); + GL11.glEnable(GL11.GL_ALPHA_TEST); + GL11.glColor4f(0.5f, 0.7f, 1, 0.5f); mainModel.render(event.entity, f7, f6, 0, 0, 0, someScalingFactor); @@ -223,488 +209,510 @@ public final class WizardryClientEventHandler { e.printStackTrace(); } - GlStateManager.disableAlpha(); - GlStateManager.disableBlend(); - GlStateManager.blendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA); - GlStateManager.disableRescaleNormal(); + GL11.glDisable(GL11.GL_ALPHA_TEST); + GL11.glDisable(GL11.GL_BLEND); + GL11.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA); + GL11.glDisable(GL12.GL_RESCALE_NORMAL); GL11.glEnable(GL11.GL_TEXTURE_2D); - GlStateManager.popMatrix(); + GL11.glPopMatrix(); } */ Minecraft mc = Minecraft.getMinecraft(); - WizardData properties = WizardData.get(mc.thePlayer); - RayTraceResult rayTrace = WizardryUtilities.standardEntityRayTrace(mc.theWorld, mc.thePlayer, 16); - RenderManager renderManager = event.getRenderer().getRenderManager(); - - ItemStack wand = mc.thePlayer.getHeldItemMainhand(); - - if(wand == null || !(wand.getItem() instanceof ItemWand)){ - wand = mc.thePlayer.getHeldItemOffhand(); - } + ExtendedPlayer properties = ExtendedPlayer.get(mc.thePlayer); + MovingObjectPosition rayTrace = WizardryUtilities.standardEntityRayTrace(mc.theWorld, mc.thePlayer, 16); // Target selection pointer - if(mc.thePlayer.isSneaking() && wand != null && wand.getItem() instanceof ItemWand && rayTrace != null - && rayTrace.entityHit instanceof EntityLivingBase && rayTrace.entityHit == event.getEntity() + if(mc.thePlayer.isSneaking() && mc.thePlayer.getHeldItem() != null + && mc.thePlayer.getHeldItem().getItem() instanceof ItemWand && rayTrace != null + && rayTrace.entityHit instanceof EntityLivingBase && rayTrace.entityHit == event.entity && properties != null && properties.selectedMinion != null){ - Tessellator tessellator = Tessellator.getInstance(); - VertexBuffer buffer = tessellator.getBuffer(); + Tessellator tessellator = Tessellator.instance; - GlStateManager.pushMatrix(); + GL11.glPushMatrix(); - GlStateManager.disableCull(); - GlStateManager.disableLighting(); + GL11.glDisable(GL11.GL_CULL_FACE); + GL11.glDisable(GL11.GL_LIGHTING); OpenGlHelper.setLightmapTextureCoords(OpenGlHelper.lightmapTexUnit, 240f, 240f); // Disabling depth test allows it to be seen through everything. GL11.glDisable(GL11.GL_DEPTH_TEST); - GlStateManager.color(1, 1, 1, 1); + GL11.glColor4f(1, 1, 1, 1); - GlStateManager.translate(event.getX(), event.getY() + event.getEntity().height + 0.5, event.getZ()); + GL11.glTranslated(event.x, event.y + event.entity.height + 0.5, event.z); // This counteracts the reverse rotation behaviour when in front f5 view. // Fun fact: this is a bug with vanilla too! Look at a snowball in front f5 view, for example. - float yaw = mc.gameSettings.thirdPersonView == 2 ? renderManager.playerViewX : -renderManager.playerViewX; - GlStateManager.rotate(180 - renderManager.playerViewY, 0.0F, 1.0F, 0.0F); - GlStateManager.rotate(yaw, 1.0F, 0.0F, 0.0F); + float yaw = mc.gameSettings.thirdPersonView == 2 ? RenderManager.instance.playerViewX : -RenderManager.instance.playerViewX; + GL11.glRotatef(180 - RenderManager.instance.playerViewY, 0.0F, 1.0F, 0.0F); + GL11.glRotatef(yaw, 1.0F, 0.0F, 0.0F); - buffer.begin(GL11.GL_QUADS, DefaultVertexFormats.POSITION_TEX); + tessellator.startDrawingQuads(); mc.renderEngine.bindTexture(targetPointerTexture); - buffer.pos(-0.2, 0.24, 0).tex(0, 0).endVertex(); - buffer.pos(0.2, 0.24, 0).tex(9f/16f, 0).endVertex(); - buffer.pos(0.2, -0.24, 0).tex(9f/16f, 11f/16f).endVertex(); - buffer.pos(-0.2, -0.24, 0).tex(0, 11f/16f).endVertex(); + tessellator.addVertexWithUV(-0.2, 0.24, 0, 0, 0); + tessellator.addVertexWithUV(0.2, 0.24, 0, 9f/16f, 0); + tessellator.addVertexWithUV(0.2, -0.24, 0, 9f/16f, 11f/16f); + tessellator.addVertexWithUV(-0.2, -0.24, 0, 0, 11f/16f); tessellator.draw(); - GlStateManager.enableCull(); - GlStateManager.enableLighting(); + GL11.glEnable(GL11.GL_CULL_FACE); + GL11.glEnable(GL11.GL_LIGHTING); GL11.glEnable(GL11.GL_DEPTH_TEST); - GlStateManager.popMatrix(); + GL11.glPopMatrix(); } // Summoned creature selection pointer - if(properties != null && properties.selectedMinion != null && properties.selectedMinion.get() == event.getEntity()){ + if(properties != null && properties.selectedMinion != null && properties.selectedMinion.get() == event.entity){ - Tessellator tessellator = Tessellator.getInstance(); - VertexBuffer buffer = tessellator.getBuffer(); + Tessellator tessellator = Tessellator.instance; - GlStateManager.pushMatrix(); + GL11.glPushMatrix(); - GlStateManager.disableCull(); - GlStateManager.disableLighting(); + GL11.glDisable(GL11.GL_CULL_FACE); + GL11.glDisable(GL11.GL_LIGHTING); OpenGlHelper.setLightmapTextureCoords(OpenGlHelper.lightmapTexUnit, 240f, 240f); // Disabling depth test allows it to be seen through everything. GL11.glDisable(GL11.GL_DEPTH_TEST); - GlStateManager.color(1, 1, 1, 1); + GL11.glColor4f(1, 1, 1, 1); - GlStateManager.translate(event.getX(), event.getY() + event.getEntity().height + 0.5, event.getZ()); + GL11.glTranslated(event.x, event.y + event.entity.height + 0.5, event.z); // This counteracts the reverse rotation behaviour when in front f5 view. // Fun fact: this is a bug with vanilla too! Look at a snowball in front f5 view, for example. - float yaw = mc.gameSettings.thirdPersonView == 2 ? renderManager.playerViewX : -renderManager.playerViewX; - GlStateManager.rotate(180 - renderManager.playerViewY, 0.0F, 1.0F, 0.0F); - GlStateManager.rotate(yaw, 1.0F, 0.0F, 0.0F); + float yaw = mc.gameSettings.thirdPersonView == 2 ? RenderManager.instance.playerViewX : -RenderManager.instance.playerViewX; + GL11.glRotatef(180 - RenderManager.instance.playerViewY, 0.0F, 1.0F, 0.0F); + GL11.glRotatef(yaw, 1.0F, 0.0F, 0.0F); - buffer.begin(GL11.GL_QUADS, DefaultVertexFormats.POSITION_TEX); + tessellator.startDrawingQuads(); mc.renderEngine.bindTexture(pointerTexture); - buffer.pos(-0.2, 0.24, 0).tex(0, 0).endVertex(); - buffer.pos(0.2, 0.24, 0).tex(9f/16f, 0).endVertex(); - buffer.pos(0.2, -0.24, 0).tex(9f/16f, 11f/16f).endVertex(); - buffer.pos(-0.2, -0.24, 0).tex(0, 11f/16f).endVertex(); + tessellator.addVertexWithUV(-0.2, 0.24, 0, 0, 0); + tessellator.addVertexWithUV(0.2, 0.24, 0, 9f/16f, 0); + tessellator.addVertexWithUV(0.2, -0.24, 0, 9f/16f, 11f/16f); + tessellator.addVertexWithUV(-0.2, -0.24, 0, 0, 11f/16f); tessellator.draw(); - GlStateManager.enableCull(); - GlStateManager.enableLighting(); + GL11.glEnable(GL11.GL_CULL_FACE); + GL11.glEnable(GL11.GL_LIGHTING); GL11.glEnable(GL11.GL_DEPTH_TEST); - GlStateManager.popMatrix(); + GL11.glPopMatrix(); } // Sixth sense - if(mc.thePlayer.isPotionActive(WizardryPotions.sixth_sense) && event.getEntity() != mc.thePlayer - && mc.thePlayer.getActivePotionEffect(WizardryPotions.sixth_sense) != null - && event.getEntity().getDistanceToEntity(mc.thePlayer) < 20*(1+mc.thePlayer.getActivePotionEffect(WizardryPotions.sixth_sense).getAmplifier()*Constants.RANGE_INCREASE_PER_LEVEL)){ + if(mc.thePlayer.isPotionActive(Wizardry.sixthSense) && event.entity != mc.thePlayer + && mc.thePlayer.getActivePotionEffect(Wizardry.sixthSense) != null + && event.entity.getDistanceToEntity(mc.thePlayer) < 20*(1+mc.thePlayer.getActivePotionEffect(Wizardry.sixthSense).getAmplifier()*Wizardry.RANGE_INCREASE_PER_LEVEL)){ - Tessellator tessellator = Tessellator.getInstance(); - VertexBuffer buffer = tessellator.getBuffer(); + Tessellator tessellator = Tessellator.instance; - GlStateManager.pushMatrix(); + GL11.glPushMatrix(); - GlStateManager.disableCull(); - GlStateManager.enableBlend(); - GlStateManager.disableLighting(); + GL11.glDisable(GL11.GL_CULL_FACE); + GL11.glEnable(GL11.GL_BLEND); + GL11.glDisable(GL11.GL_LIGHTING); OpenGlHelper.setLightmapTextureCoords(OpenGlHelper.lightmapTexUnit, 240f, 240f); - GlStateManager.blendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA); + GL11.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA); // Disabling depth test allows it to be seen through everything. GL11.glDisable(GL11.GL_DEPTH_TEST); - GlStateManager.translate(event.getX(), event.getY() + event.getEntity().height * 0.6, event.getZ()); + GL11.glTranslated(event.x, event.y + event.entity.height * 0.6, event.z); // This counteracts the reverse rotation behaviour when in front f5 view. // Fun fact: this is a bug with vanilla too! Look at a snowball in front f5 view, for example. - float yaw = mc.gameSettings.thirdPersonView == 2 ? renderManager.playerViewX : -renderManager.playerViewX; - GlStateManager.rotate(180 - renderManager.playerViewY, 0.0F, 1.0F, 0.0F); - GlStateManager.rotate(yaw, 1.0F, 0.0F, 0.0F); + float yaw = mc.gameSettings.thirdPersonView == 2 ? RenderManager.instance.playerViewX : -RenderManager.instance.playerViewX; + GL11.glRotatef(180 - RenderManager.instance.playerViewY, 0.0F, 1.0F, 0.0F); + GL11.glRotatef(yaw, 1.0F, 0.0F, 0.0F); - buffer.begin(GL11.GL_QUADS, DefaultVertexFormats.POSITION_TEX); + tessellator.startDrawingQuads(); mc.renderEngine.bindTexture(sixthSenseTexture); - buffer.pos(-0.6, 0.6, 0).tex(0, 0).endVertex(); - buffer.pos(0.6, 0.6, 0).tex(1, 0).endVertex(); - buffer.pos(0.6, -0.6, 0).tex(1, 1).endVertex(); - buffer.pos(-0.6, -0.6, 0).tex(0, 1).endVertex(); + tessellator.addVertexWithUV(-0.6, 0.6, 0, 0, 0); + tessellator.addVertexWithUV(0.6, 0.6, 0, 1, 0); + tessellator.addVertexWithUV(0.6, -0.6, 0, 1, 1); + tessellator.addVertexWithUV(-0.6, -0.6, 0, 0, 1); tessellator.draw(); - GlStateManager.enableCull(); - GlStateManager.disableBlend(); - GlStateManager.enableLighting(); + GL11.glEnable(GL11.GL_CULL_FACE); + GL11.glDisable(GL11.GL_BLEND); + GL11.glEnable(GL11.GL_LIGHTING); GL11.glEnable(GL11.GL_DEPTH_TEST); - GlStateManager.popMatrix(); + GL11.glPopMatrix(); } } @SubscribeEvent - public static void onRenderGameOverlayEvent(RenderGameOverlayEvent.Post event){ - - if(event.getType() == RenderGameOverlayEvent.ElementType.HELMET - && Minecraft.getMinecraft().thePlayer.isPotionActive(WizardryPotions.sixth_sense)){ + public void onRenderGameOverlayEvent(RenderGameOverlayEvent.Post event){ + if(event.type == RenderGameOverlayEvent.ElementType.HELMET + && Minecraft.getMinecraft().thePlayer.isPotionActive(Wizardry.sixthSense)){ - GlStateManager.pushMatrix(); + GL11.glPushMatrix(); GL11.glDisable(GL11.GL_DEPTH_TEST); GL11.glDepthMask(false); OpenGlHelper.glBlendFunc(770, 771, 1, 0); - GlStateManager.color(1.0F, 1.0F, 1.0F, 1.0F); - GlStateManager.disableAlpha(); + GL11.glColor4f(1.0F, 1.0F, 1.0F, 1.0F); + GL11.glDisable(GL11.GL_ALPHA_TEST); Minecraft.getMinecraft().renderEngine.bindTexture(sixthSenseOverlayTexture); - - Tessellator tessellator = Tessellator.getInstance(); - VertexBuffer buffer = tessellator.getBuffer(); - - buffer.begin(GL11.GL_QUADS, DefaultVertexFormats.POSITION_TEX); - buffer.pos(0.0D, (double)event.getResolution().getScaledHeight(), -90.0D).tex(0.0D, 1.0D).endVertex(); - buffer.pos((double)event.getResolution().getScaledWidth(), (double)event.getResolution().getScaledHeight(), -90.0D).tex(1.0D, 1.0D).endVertex(); - buffer.pos((double)event.getResolution().getScaledWidth(), 0.0D, -90.0D).tex(1.0D, 0.0D).endVertex(); - buffer.pos(0.0D, 0.0D, -90.0D).tex(0.0D, 0.0D).endVertex(); + Tessellator tessellator = Tessellator.instance; + tessellator.startDrawingQuads(); + tessellator.addVertexWithUV(0.0D, (double)event.resolution.getScaledHeight(), -90.0D, 0.0D, 1.0D); + tessellator.addVertexWithUV((double)event.resolution.getScaledWidth(), (double)event.resolution.getScaledHeight(), -90.0D, 1.0D, 1.0D); + tessellator.addVertexWithUV((double)event.resolution.getScaledWidth(), 0.0D, -90.0D, 1.0D, 0.0D); + tessellator.addVertexWithUV(0.0D, 0.0D, -90.0D, 0.0D, 0.0D); tessellator.draw(); - GL11.glDepthMask(true); GL11.glEnable(GL11.GL_DEPTH_TEST); - GlStateManager.enableAlpha(); - GlStateManager.color(1.0F, 1.0F, 1.0F, 1.0F); + GL11.glEnable(GL11.GL_ALPHA_TEST); + GL11.glColor4f(1.0F, 1.0F, 1.0F, 1.0F); - GlStateManager.popMatrix(); + GL11.glPopMatrix(); } - if(event.getType() == RenderGameOverlayEvent.ElementType.HELMET - && Minecraft.getMinecraft().thePlayer.isPotionActive(WizardryPotions.frost)){ + if(event.type == RenderGameOverlayEvent.ElementType.HELMET + && Minecraft.getMinecraft().thePlayer.isPotionActive(Wizardry.frost)){ - GlStateManager.pushMatrix(); + GL11.glPushMatrix(); GL11.glDisable(GL11.GL_DEPTH_TEST); GL11.glDepthMask(false); OpenGlHelper.glBlendFunc(770, 771, 1, 0); - GlStateManager.color(1.0F, 1.0F, 1.0F, 1.0F); - GlStateManager.disableAlpha(); + GL11.glColor4f(1.0F, 1.0F, 1.0F, 1.0F); + GL11.glDisable(GL11.GL_ALPHA_TEST); Minecraft.getMinecraft().renderEngine.bindTexture(frostOverlayTexture); - - Tessellator tessellator = Tessellator.getInstance(); - VertexBuffer buffer = tessellator.getBuffer(); - - buffer.begin(GL11.GL_QUADS, DefaultVertexFormats.POSITION_TEX); - buffer.pos(0.0D, (double)event.getResolution().getScaledHeight(), -90.0D).tex(0.0D, 1.0D).endVertex(); - buffer.pos((double)event.getResolution().getScaledWidth(), (double)event.getResolution().getScaledHeight(), -90.0D).tex(1.0D, 1.0D).endVertex(); - buffer.pos((double)event.getResolution().getScaledWidth(), 0.0D, -90.0D).tex(1.0D, 0.0D).endVertex(); - buffer.pos(0.0D, 0.0D, -90.0D).tex(0.0D, 0.0D).endVertex(); - + Tessellator tessellator = Tessellator.instance; + tessellator.startDrawingQuads(); + tessellator.addVertexWithUV(0.0D, (double)event.resolution.getScaledHeight(), -90.0D, 0.0D, 1.0D); + tessellator.addVertexWithUV((double)event.resolution.getScaledWidth(), (double)event.resolution.getScaledHeight(), -90.0D, 1.0D, 1.0D); + tessellator.addVertexWithUV((double)event.resolution.getScaledWidth(), 0.0D, -90.0D, 1.0D, 0.0D); + tessellator.addVertexWithUV(0.0D, 0.0D, -90.0D, 0.0D, 0.0D); tessellator.draw(); GL11.glDepthMask(true); GL11.glEnable(GL11.GL_DEPTH_TEST); - GlStateManager.enableAlpha(); - GlStateManager.color(1.0F, 1.0F, 1.0F, 1.0F); + GL11.glEnable(GL11.GL_ALPHA_TEST); + GL11.glColor4f(1.0F, 1.0F, 1.0F, 1.0F); - GlStateManager.popMatrix(); + GL11.glPopMatrix(); } } - // FIXME: Something in here is making the first person shadow ward rather translucent. private static void renderShadowWardFirstPerson(EntityPlayer entityplayer){ - ItemStack wand = entityplayer.getActiveItemStack(); - if(WizardData.get(entityplayer) != null && WizardData.get(entityplayer).currentlyCasting() instanceof ShadowWard || (entityplayer.isHandActive() && wand != null && wand.getItemDamage() < wand.getMaxDamage() + ItemStack wand = entityplayer.getHeldItem(); + if(ExtendedPlayer.get(entityplayer).currentlyCasting() instanceof ShadowWard || (entityplayer.isUsingItem() && wand != null && wand.getItemDamage() < wand.getMaxDamage() && wand.getItem() instanceof ItemWand && WandHelper.getCurrentSpell(wand) instanceof ShadowWard)){ - GlStateManager.pushMatrix(); + GL11.glPushMatrix(); - GlStateManager.enableBlend(); - GlStateManager.blendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA); - GlStateManager.shadeModel(GL11.GL_SMOOTH); - GlStateManager.disableLighting(); - GlStateManager.disableAlpha(); + GL11.glEnable(GL11.GL_BLEND); + GL11.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA); + GL11.glDisable(GL11.GL_LIGHTING); OpenGlHelper.setLightmapTextureCoords(OpenGlHelper.lightmapTexUnit, 240f, 240f); - GlStateManager.translate(0, 1.2, 0); - GlStateManager.rotate(-entityplayer.rotationYaw, 0, 1, 0); - GlStateManager.rotate(entityplayer.rotationPitch, 1, 0, 0); - + GL11.glRotatef(-1*entityplayer.rotationYaw, 0, 1, 0); + GL11.glRotatef(entityplayer.rotationPitch, 1, 0, 0); Minecraft.getMinecraft().renderEngine.bindTexture(shadowWardTexture); + Tessellator tessellator = Tessellator.instance; - GlStateManager.pushMatrix(); + GL11.glPushMatrix(); - GlStateManager.translate(0, 0, 1.2); - GlStateManager.rotate(entityplayer.worldObj.getWorldTime()*-2, 0, 0, 1); - GlStateManager.scale(1.1, 1.1, 1.1); + GL11.glTranslated(0, -0.3, 1.2); + GL11.glRotated(entityplayer.worldObj.getWorldTime()*-2, 0, 0, 1); + GL11.glScaled(1.1, 1.1, 1.1); - Tessellator tessellator = Tessellator.getInstance(); - VertexBuffer buffer = tessellator.getBuffer(); - - buffer.begin(GL11.GL_QUADS, DefaultVertexFormats.POSITION_TEX); + tessellator.startDrawingQuads(); - buffer.pos(-0.5, 0.5, -0.5).tex(0, 0).endVertex(); - buffer.pos(0.5, 0.5, -0.5).tex(1, 0).endVertex(); - buffer.pos(0.5, -0.5, -0.5).tex(1, 1).endVertex(); - buffer.pos(-0.5, -0.5, -0.5).tex(0, 1).endVertex(); + tessellator.addVertexWithUV(-0.5, 0.5, -0.5, 0, 0); + tessellator.addVertexWithUV(0.5, 0.5, -0.5, 1, 0); + tessellator.addVertexWithUV(0.5, -0.5, -0.5, 1, 1); + tessellator.addVertexWithUV(-0.5, -0.5, -0.5, 0, 1); tessellator.draw(); - buffer.begin(GL11.GL_QUADS, DefaultVertexFormats.POSITION_TEX); + tessellator.startDrawingQuads(); - buffer.pos(-0.5, 0.5, -0.5).tex(0, 0).endVertex(); - buffer.pos(-0.5, -0.5, -0.5).tex(0, 1).endVertex(); - buffer.pos(0.5, -0.5, -0.5).tex(1, 1).endVertex(); - buffer.pos(0.5, 0.5, -0.5).tex(1, 0).endVertex(); + tessellator.addVertexWithUV(-0.5, 0.5, -0.5, 0, 0); + tessellator.addVertexWithUV(-0.5, -0.5, -0.5, 0, 1); + tessellator.addVertexWithUV(0.5, -0.5, -0.5, 1, 1); + tessellator.addVertexWithUV(0.5, 0.5, -0.5, 1, 0); tessellator.draw(); - GlStateManager.popMatrix(); + GL11.glPopMatrix(); - GlStateManager.shadeModel(GL11.GL_FLAT); - GlStateManager.enableLighting(); - GlStateManager.disableBlend(); + GL11.glEnable(GL11.GL_LIGHTING); + GL11.glDisable(GL11.GL_BLEND); - GlStateManager.popMatrix(); + GL11.glPopMatrix(); } } private static void renderShadowWardIfActive(EntityPlayer entityplayer){ - ItemStack wand = entityplayer.getActiveItemStack(); - if(WizardData.get(entityplayer).currentlyCasting() instanceof ShadowWard || (entityplayer.isHandActive() && wand != null + ItemStack wand = entityplayer.getHeldItem(); + if(ExtendedPlayer.get(entityplayer).currentlyCasting() instanceof ShadowWard || (entityplayer.isUsingItem() && wand != null && wand.getItemDamage() < wand.getMaxDamage() && wand.getItem() instanceof ItemWand && WandHelper.getCurrentSpell(wand) instanceof ShadowWard)){ - GlStateManager.pushMatrix(); + GL11.glPushMatrix(); - GlStateManager.enableBlend(); - GlStateManager.blendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA); - GlStateManager.disableLighting(); + GL11.glEnable(GL11.GL_BLEND); + GL11.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA); + GL11.glDisable(GL11.GL_LIGHTING); OpenGlHelper.setLightmapTextureCoords(OpenGlHelper.lightmapTexUnit, 240f, 240f); - GlStateManager.rotate(180, 0, 1, 0); - GlStateManager.rotate(-entityplayer.renderYawOffset, 0, 1, 0); - + GL11.glRotatef(180, 0, 1, 0); + //GL11.glRotatef(entityplayer.rotationYaw, 0, 1, 0); + //GL11.glRotatef(-entityplayer.rotationPitch, 1, 0, 0); Minecraft.getMinecraft().renderEngine.bindTexture(shadowWardTexture); - - Tessellator tessellator = Tessellator.getInstance(); - VertexBuffer buffer = tessellator.getBuffer(); + Tessellator tessellator = Tessellator.instance; - GlStateManager.translate(0, 1.2, 0); - GlStateManager.rotate(entityplayer.worldObj.getWorldTime()*-2, 0, 0, 1); - GlStateManager.scale(1.1, 1.1, 1.1); + GL11.glPushMatrix(); - buffer.begin(GL11.GL_QUADS, DefaultVertexFormats.POSITION_TEX); + // was 0, -0.3, 1.2 + GL11.glTranslated(0, 0, 1.2); + GL11.glRotated(entityplayer.worldObj.getWorldTime()*-2, 0, 0, 1); + GL11.glScaled(1.1, 1.1, 1.1); - buffer.pos(-0.5, 0.5, -0.5).tex(0, 0).endVertex(); - buffer.pos(0.5, 0.5, -0.5).tex(1, 0).endVertex(); - buffer.pos(0.5, -0.5, -0.5).tex(1, 1).endVertex(); - buffer.pos(-0.5, -0.5, -0.5).tex(0, 1).endVertex(); + tessellator.startDrawingQuads(); + + tessellator.addVertexWithUV(-0.5, 0.5, -0.5, 0, 0); + tessellator.addVertexWithUV(0.5, 0.5, -0.5, 1, 0); + tessellator.addVertexWithUV(0.5, -0.5, -0.5, 1, 1); + tessellator.addVertexWithUV(-0.5, -0.5, -0.5, 0, 1); tessellator.draw(); - buffer.begin(GL11.GL_QUADS, DefaultVertexFormats.POSITION_TEX); + tessellator.startDrawingQuads(); - buffer.pos(-0.5, 0.5, -0.5).tex(0, 0).endVertex(); - buffer.pos(-0.5, -0.5, -0.5).tex(0, 1).endVertex(); - buffer.pos(0.5, -0.5, -0.5).tex(1, 1).endVertex(); - buffer.pos(0.5, 0.5, -0.5).tex(1, 0).endVertex(); + tessellator.addVertexWithUV(-0.5, 0.5, -0.5, 0, 0); + tessellator.addVertexWithUV(-0.5, -0.5, -0.5, 0, 1); + tessellator.addVertexWithUV(0.5, -0.5, -0.5, 1, 1); + tessellator.addVertexWithUV(0.5, 0.5, -0.5, 1, 0); tessellator.draw(); - GlStateManager.enableLighting(); - GlStateManager.disableBlend(); + GL11.glPopMatrix(); - GlStateManager.popMatrix(); + GL11.glEnable(GL11.GL_LIGHTING); + GL11.glDisable(GL11.GL_BLEND); + + GL11.glPopMatrix(); } } private static void renderWingsIfActive(EntityPlayer entityplayer, float partialTickTime){ - ItemStack wand = entityplayer.getActiveItemStack(); - if(WizardData.get(entityplayer).currentlyCasting() instanceof Flight || (entityplayer.isHandActive() && wand != null + ItemStack wand = entityplayer.getHeldItem(); + if(ExtendedPlayer.get(entityplayer).currentlyCasting() instanceof Flight || (entityplayer.isUsingItem() && wand != null && wand.getItemDamage() < wand.getMaxDamage() && wand.getItem() instanceof ItemWand && WandHelper.getCurrentSpell(wand) instanceof Flight)){ - GlStateManager.pushMatrix(); + GL11.glPushMatrix(); - GlStateManager.enableBlend(); - GlStateManager.blendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA); - GlStateManager.disableLighting(); + GL11.glEnable(GL11.GL_BLEND); + GL11.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA); + GL11.glDisable(GL11.GL_LIGHTING); OpenGlHelper.setLightmapTextureCoords(OpenGlHelper.lightmapTexUnit, 240f, 240f); - //GlStateManager.rotate(-entityplayer.rotationYawHead, 0, 1, 0); - GlStateManager.rotate(-entityplayer.renderYawOffset, 0, 1, 0); - //GlStateManager.rotate(180, 1, 0, 0); + GL11.glRotatef(-entityplayer.rotationYawHead, 0, 1, 0); + GL11.glRotatef(entityplayer.rotationYaw, 0, 1, 0); + GL11.glRotatef(180, 1, 0, 0); Minecraft.getMinecraft().renderEngine.bindTexture(wingTexture); - Tessellator tessellator = Tessellator.getInstance(); - VertexBuffer buffer = tessellator.getBuffer(); + Tessellator tessellator = Tessellator.instance; - GlStateManager.pushMatrix(); + GL11.glPushMatrix(); - GlStateManager.translate(0.1, 0.4, -0.15); - GlStateManager.rotate(20 + 20*(float)Math.sin(entityplayer.worldObj.getWorldTime()*0.3), 0, 1, 0); + GL11.glTranslated(0.1, -1.0, -0.15); + GL11.glRotatef(20 + 20*(float)Math.sin(entityplayer.worldObj.getWorldTime()*0.3), 0, 1, 0); - buffer.begin(GL11.GL_QUADS, DefaultVertexFormats.POSITION_TEX); + tessellator.startDrawingQuads(); - buffer.pos(0, 2, 0).tex(0, 0).endVertex(); - buffer.pos(2, 2, 0).tex(1, 0).endVertex(); - buffer.pos(2, 0, 0).tex(1, 1).endVertex(); - buffer.pos(0, 0, 0).tex(0, 1).endVertex(); + tessellator.addVertexWithUV(0, 2, 0, 0, 0); + tessellator.addVertexWithUV(2, 2, 0, 1, 0); + tessellator.addVertexWithUV(2, 0, 0, 1, 1); + tessellator.addVertexWithUV(0, 0, 0, 0, 1); tessellator.draw(); - buffer.begin(GL11.GL_QUADS, DefaultVertexFormats.POSITION_TEX); + tessellator.startDrawingQuads(); - buffer.pos(0, 2, 0).tex(0, 0).endVertex(); - buffer.pos(0, 0, 0).tex(0, 1).endVertex(); - buffer.pos(2, 0, 0).tex(1, 1).endVertex(); - buffer.pos(2, 2, 0).tex(1, 0).endVertex(); + tessellator.addVertexWithUV(0, 2, 0, 0, 0); + tessellator.addVertexWithUV(0, 0, 0, 0, 1); + tessellator.addVertexWithUV(2, 0, 0, 1, 1); + tessellator.addVertexWithUV(2, 2, 0, 1, 0); tessellator.draw(); - GlStateManager.popMatrix(); + GL11.glPopMatrix(); - GlStateManager.pushMatrix(); + GL11.glPushMatrix(); - GlStateManager.translate(-0.1, 0.4, -0.15); - GlStateManager.rotate(-200 - 20*(float)Math.sin(entityplayer.worldObj.getWorldTime()*0.3), 0, 1, 0); + GL11.glTranslated(-0.1, -1.0, -0.15); + GL11.glRotatef(-200 - 20*(float)Math.sin(entityplayer.worldObj.getWorldTime()*0.3), 0, 1, 0); - buffer.begin(GL11.GL_QUADS, DefaultVertexFormats.POSITION_TEX); + tessellator.startDrawingQuads(); - buffer.pos(0, 2, 0).tex(0, 0).endVertex(); - buffer.pos(2, 2, 0).tex(1, 0).endVertex(); - buffer.pos(2, 0, 0).tex(1, 1).endVertex(); - buffer.pos(0, 0, 0).tex(0, 1).endVertex(); + tessellator.addVertexWithUV(0, 2, 0, 0, 0); + tessellator.addVertexWithUV(2, 2, 0, 1, 0); + tessellator.addVertexWithUV(2, 0, 0, 1, 1); + tessellator.addVertexWithUV(0, 0, 0, 0, 1); tessellator.draw(); - buffer.begin(GL11.GL_QUADS, DefaultVertexFormats.POSITION_TEX); + tessellator.startDrawingQuads(); - buffer.pos(0, 2, 0).tex(0, 0).endVertex(); - buffer.pos(0, 0, 0).tex(0, 1).endVertex(); - buffer.pos(2, 0, 0).tex(1, 1).endVertex(); - buffer.pos(2, 2, 0).tex(1, 0).endVertex(); + tessellator.addVertexWithUV(0, 2, 0, 0, 0); + tessellator.addVertexWithUV(0, 0, 0, 0, 1); + tessellator.addVertexWithUV(2, 0, 0, 1, 1); + tessellator.addVertexWithUV(2, 2, 0, 1, 0); tessellator.draw(); - GlStateManager.popMatrix(); + GL11.glPopMatrix(); - GlStateManager.enableLighting(); - GlStateManager.disableBlend(); + GL11.glEnable(GL11.GL_LIGHTING); + GL11.glDisable(GL11.GL_BLEND); - GlStateManager.popMatrix(); + GL11.glPopMatrix(); } } private static void renderShieldFirstPerson(EntityPlayer entityplayer){ - ItemStack wand = entityplayer.getActiveItemStack(); - if(WizardData.get(entityplayer) != null && WizardData.get(entityplayer).shield != null && (WizardData.get(entityplayer).currentlyCasting() instanceof Shield || (entityplayer.isHandActive() && wand != null + ItemStack wand = entityplayer.getHeldItem(); + if(ExtendedPlayer.get(entityplayer).shield != null && (ExtendedPlayer.get(entityplayer).currentlyCasting() instanceof Shield || (entityplayer.isUsingItem() && wand != null && wand.getItemDamage() < wand.getMaxDamage() && wand.getItem() instanceof ItemWand && WandHelper.getCurrentSpell(wand) instanceof Shield))){ - GlStateManager.pushMatrix(); + double x = entityplayer.posX; + double y = entityplayer.posY; + double z = entityplayer.posZ; - GlStateManager.disableCull(); - GlStateManager.enableBlend(); - GlStateManager.blendFunc(GL11.GL_ONE, GL11.GL_SRC_ALPHA); - GlStateManager.shadeModel(GL11.GL_SMOOTH); - GlStateManager.disableLighting(); + EntityShield shield = (EntityShield)ExtendedPlayer.get(entityplayer).shield; + + GL11.glPushMatrix(); + + GL11.glDisable(GL11.GL_CULL_FACE); + GL11.glEnable(GL11.GL_BLEND); + GL11.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_SRC_ALPHA); + GL11.glShadeModel(GL11.GL_SMOOTH); + GL11.glDisable(GL11.GL_LIGHTING); OpenGlHelper.setLightmapTextureCoords(OpenGlHelper.lightmapTexUnit, 240f, 240f); - GlStateManager.translate(0, 1.4, 0); + GL11.glTranslated(0, -0.3, 0); - GlStateManager.rotate(-entityplayer.rotationYaw, 0, 1, 0); - GlStateManager.rotate(entityplayer.rotationPitch, 1, 0, 0); + GL11.glRotatef(-1*entityplayer.rotationYaw, 0, 1, 0); + GL11.glRotatef(entityplayer.rotationPitch, 1, 0, 0); - GlStateManager.translate(0, 0, 0.8); + GL11.glTranslated(0, 0, 0.8); - Tessellator tessellator = Tessellator.getInstance(); + Tessellator tessellator = Tessellator.instance; Minecraft.getMinecraft().renderEngine.bindTexture(shieldTexture); - renderShield(tessellator); - - GlStateManager.enableLighting(); + renderShield(tessellator, -1); - GlStateManager.shadeModel(GL11.GL_FLAT); - GlStateManager.enableCull(); - GlStateManager.disableBlend(); + // Enchantment effect + /* + GL11.glPushMatrix(); + + GL11.glDisable(GL11.GL_LIGHTING); + //this.bindTexture(RES_ITEM_GLINT); + GL11.glBlendFunc(GL11.GL_SRC_COLOR, GL11.GL_ONE); + float f7 = 0.76F; + GL11.glColor4f(0.5F * f7, 0.25F * f7, 0.8F * f7, 1.0F); + float f8 = 0.125F; + //GL11.glScalef(f8, f8, f8); + float f9 = (float)(Minecraft.getSystemTime() % 3000L) / 3000.0F * 0.8f; + //GL11.glTranslatef(f9, 0.0F, 0.0F); + //GL11.glRotatef(-50.0F, 0.0F, 0.0F, 1.0F); + + //this.renderShield(tessellator, f9); + + GL11.glPopMatrix(); + */ + GL11.glEnable(GL11.GL_LIGHTING); + + GL11.glShadeModel(GL11.GL_FLAT); + GL11.glEnable(GL11.GL_CULL_FACE); + GL11.glDisable(GL11.GL_BLEND); //RenderHelper.enableStandardItemLighting(); - GlStateManager.popMatrix(); + GL11.glPopMatrix(); } } private static void renderShieldIfActive(EntityPlayer entityplayer){ - ItemStack wand = entityplayer.getActiveItemStack(); - if(WizardData.get(entityplayer).shield != null && (WizardData.get(entityplayer).currentlyCasting() instanceof Shield || (entityplayer.isHandActive() && wand != null + ItemStack wand = entityplayer.getHeldItem(); + if(ExtendedPlayer.get(entityplayer).shield != null && (ExtendedPlayer.get(entityplayer).currentlyCasting() instanceof Shield || (entityplayer.isUsingItem() && wand != null && wand.getItemDamage() < wand.getMaxDamage() && wand.getItem() instanceof ItemWand && WandHelper.getCurrentSpell(wand) instanceof Shield))){ - GlStateManager.pushMatrix(); + double x = entityplayer.posX; + double y = entityplayer.posY; + double z = entityplayer.posZ; - GlStateManager.disableCull(); - GlStateManager.enableBlend(); - // For some reason, the old blend function (GL11.GL_SRC_ALPHA, GL11.GL_SRC_ALPHA) caused the inner - // edges to appear black, so I have changed it to this, which looks very slightly different. - GlStateManager.blendFunc(GL11.GL_ONE, GL11.GL_SRC_ALPHA); - GlStateManager.shadeModel(GL11.GL_SMOOTH); - GlStateManager.disableLighting(); + EntityShield shield = (EntityShield)ExtendedPlayer.get(entityplayer).shield; + + GL11.glPushMatrix(); + + GL11.glDisable(GL11.GL_CULL_FACE); + GL11.glEnable(GL11.GL_BLEND); + GL11.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_SRC_ALPHA); + GL11.glShadeModel(GL11.GL_SMOOTH); + GL11.glDisable(GL11.GL_LIGHTING); OpenGlHelper.setLightmapTextureCoords(OpenGlHelper.lightmapTexUnit, 240f, 240f); - GlStateManager.translate(0, 1.3, 0); + GL11.glTranslated(0, 0, 0); - //GlStateManager.rotate(180, 0, 1, 0); - GlStateManager.rotate(-entityplayer.renderYawOffset, 0, 1, 0); - //GlStateManager.rotate(-entityplayer.rotationPitch, 1, 0, 0); + GL11.glRotatef(180, 0, 1, 0); + //GL11.glRotatef(entityplayer.rotationYaw, 0, 1, 0); + //GL11.glRotatef(-entityplayer.rotationPitch, 1, 0, 0); - GlStateManager.translate(0, 0, 0.8); + GL11.glTranslated(0, 0, 0.8); - Tessellator tessellator = Tessellator.getInstance(); + Tessellator tessellator = Tessellator.instance; Minecraft.getMinecraft().renderEngine.bindTexture(shieldTexture); - renderShield(tessellator); + renderShield(tessellator, -1); - GlStateManager.enableLighting(); + // Enchantment effect + /* + GL11.glPushMatrix(); - GlStateManager.shadeModel(GL11.GL_FLAT); - GlStateManager.enableCull(); - GlStateManager.disableBlend(); + GL11.glDisable(GL11.GL_LIGHTING); + //this.bindTexture(RES_ITEM_GLINT); + GL11.glBlendFunc(GL11.GL_SRC_COLOR, GL11.GL_ONE); + float f7 = 0.76F; + GL11.glColor4f(0.5F * f7, 0.25F * f7, 0.8F * f7, 1.0F); + float f8 = 0.125F; + //GL11.glScalef(f8, f8, f8); + float f9 = (float)(Minecraft.getSystemTime() % 3000L) / 3000.0F * 0.8f; + //GL11.glTranslatef(f9, 0.0F, 0.0F); + //GL11.glRotatef(-50.0F, 0.0F, 0.0F, 1.0F); + + //this.renderShield(tessellator, f9); + + GL11.glPopMatrix(); + */ + GL11.glEnable(GL11.GL_LIGHTING); + + GL11.glShadeModel(GL11.GL_FLAT); + GL11.glEnable(GL11.GL_CULL_FACE); + GL11.glDisable(GL11.GL_BLEND); //RenderHelper.enableStandardItemLighting(); - GlStateManager.popMatrix(); + GL11.glPopMatrix(); } } - private static void renderShield(Tessellator tessellator){ - - VertexBuffer buffer = tessellator.getBuffer(); + private static void renderShield(Tessellator tessellator, float textureOffset){ double widthOuter = 0.6d; double heightOuter = 0.7d; @@ -712,39 +720,72 @@ public final class WizardryClientEventHandler { double heightInner = 0.4d; double depth = 0.2d; - buffer.begin(GL11.GL_TRIANGLE_STRIP, DefaultVertexFormats.POSITION_TEX_COLOR); + double textureSection = 1.0d; + double textureU = 0.0d; - buffer.pos(-widthOuter, heightInner, -depth).tex(0, 0.2).color(0, 0, 0, 255).endVertex(); - buffer.pos(-widthInner, heightInner, 0).tex(0.2, 0.2).color(200, 200, 255, 255).endVertex(); - buffer.pos(-widthInner, heightOuter, -depth).tex(0.2, 0).color(0, 0, 0, 255).endVertex(); - buffer.pos(-widthInner, heightInner, 0).tex(0.2, 0.2).color(200, 200, 255, 255).endVertex(); + if(textureOffset != -1){ + textureSection = 0.2d; + textureU = textureOffset; + } - buffer.pos(widthInner, heightOuter, -depth).tex(0.8, 0).color(0, 0, 0, 255).endVertex(); - buffer.pos(widthInner, heightInner, 0).tex(0.8, 0.2).color(200, 200, 255, 255).endVertex(); - buffer.pos(widthOuter, heightInner, -depth).tex(1, 0.2).color(0, 0, 0, 255).endVertex(); - buffer.pos(widthInner, heightInner, 0).tex(0.8, 0.2).color(200, 200, 255, 255).endVertex(); + tessellator.startDrawing(5); - buffer.pos(widthOuter, -heightInner, -depth).tex(1, 0.8).color(0, 0, 0, 255).endVertex(); - buffer.pos(widthInner, -heightInner, 0).tex(0.8, 0.8).color(200, 200, 255, 255).endVertex(); - buffer.pos(widthInner, -heightOuter, -depth).tex(0.8, 1).color(0, 0, 0, 255).endVertex(); - buffer.pos(widthInner, -heightInner, 0).tex(0.8, 0.8).color(200, 200, 255, 255).endVertex(); + tessellator.setColorRGBA(0, 0, 0, 255); + tessellator.addVertexWithUV(-widthOuter, heightOuter - 0.3, -depth, 0, 0); + tessellator.setColorOpaque(200, 200, 255); + tessellator.addVertexWithUV(-widthInner, heightInner, 0, textureU, 0.2); + tessellator.setColorRGBA(0, 0, 0, 255); + tessellator.addVertexWithUV(-widthOuter + 0.3, heightOuter, -depth, 0, 0); + tessellator.setColorOpaque(200, 200, 255); + tessellator.addVertexWithUV(-widthInner, heightInner, 0, textureU, 0.2); - buffer.pos(-widthInner, -heightOuter, -depth).tex(0.2, 1).color(0, 0, 0, 255).endVertex(); - buffer.pos(-widthInner, -heightInner, 0).tex(0.2, 0.8).color(200, 200, 255, 255).endVertex(); - buffer.pos(-widthOuter, -heightInner, -depth).tex(0, 0.8).color(0, 0, 0, 255).endVertex(); - buffer.pos(-widthInner, -heightInner, 0).tex(0.2, 0.8).color(200, 200, 255, 255).endVertex(); + tessellator.setColorRGBA(0, 0, 0, 255); + tessellator.addVertexWithUV(widthOuter - 0.3, heightOuter, -depth, 1, 0); + tessellator.setColorOpaque(200, 200, 255); + tessellator.addVertexWithUV(widthInner, heightInner, 0, textureU + textureSection, 0.2); + tessellator.setColorRGBA(0, 0, 0, 255); + tessellator.addVertexWithUV(widthOuter, heightOuter - 0.3, -depth, 1, 0); + tessellator.setColorOpaque(200, 200, 255); + tessellator.addVertexWithUV(widthInner, heightInner, 0, textureU + textureSection, 0.2); - buffer.pos(-widthOuter, heightInner, -depth).tex(0, 0.2).color(0, 0, 0, 255).endVertex(); - buffer.pos(-widthInner, heightInner, 0).tex(0.2, 0.2).color(200, 200, 255, 255).endVertex(); + tessellator.setColorRGBA(0, 0, 0, 255); + tessellator.addVertexWithUV(widthOuter, -heightOuter + 0.3, -depth, 1, 1); + tessellator.setColorOpaque(200, 200, 255); + tessellator.addVertexWithUV(widthInner, -heightInner, 0, textureU + textureSection, 0.8); + tessellator.setColorRGBA(0, 0, 0, 255); + tessellator.addVertexWithUV(widthOuter - 0.3, -heightOuter, -depth, 1, 1); + tessellator.setColorOpaque(200, 200, 255); + tessellator.addVertexWithUV(widthInner, -heightInner, 0, textureU + textureSection, 0.8); + + tessellator.setColorRGBA(0, 0, 0, 255); + tessellator.addVertexWithUV(-widthOuter + 0.3, -heightOuter, -depth, 0, 1); + tessellator.setColorOpaque(200, 200, 255); + tessellator.addVertexWithUV(-widthInner, -heightInner, 0, textureU, 0.8); + tessellator.setColorRGBA(0, 0, 0, 255); + tessellator.addVertexWithUV(-widthOuter, -heightOuter + 0.3, -depth, 0, 1); + tessellator.setColorOpaque(200, 200, 255); + tessellator.addVertexWithUV(-widthInner, -heightInner, 0, textureU, 0.8); + + tessellator.setColorRGBA(0, 0, 0, 255); + tessellator.addVertexWithUV(-widthOuter, heightOuter - 0.3, -depth, 0, 0); + tessellator.setColorOpaque(200, 200, 255); + tessellator.addVertexWithUV(-widthInner, heightInner, 0, textureU, 0.2); tessellator.draw(); - buffer.begin(GL11.GL_TRIANGLE_STRIP, DefaultVertexFormats.POSITION_TEX_COLOR); + tessellator.startDrawing(5); - buffer.pos(-widthInner, heightInner, 0).tex(0.2, 0.2).color(200, 200, 255, 255).endVertex(); - buffer.pos(widthInner, heightInner, 0).tex(0.8, 0.2).color(200, 200, 255, 255).endVertex(); - buffer.pos(-widthInner, -heightInner, 0).tex(0.2, 0.8).color(200, 200, 255, 255).endVertex(); - buffer.pos(widthInner, -heightInner, 0).tex(0.8, 0.8).color(200, 200, 255, 255).endVertex(); + tessellator.setColorOpaque(200, 200, 255); + tessellator.addVertexWithUV(-widthInner, heightInner, 0, textureU, 0.2); + + tessellator.setColorOpaque(200, 200, 255); + tessellator.addVertexWithUV(widthInner, heightInner, 0, textureU + textureSection, 0.2); + + tessellator.setColorOpaque(200, 200, 255); + tessellator.addVertexWithUV(-widthInner, -heightInner, 0, textureU, 0.8); + + tessellator.setColorOpaque(200, 200, 255); + tessellator.addVertexWithUV(widthInner, -heightInner, 0, textureU + textureSection, 0.8); tessellator.draw(); } diff --git a/src/main/java/electroblob/wizardry/client/model/ModelIceGiant.java b/src/main/java/electroblob/wizardry/client/model/ModelIceGiant.java index 4a8591d7..d77809c5 100644 --- a/src/main/java/electroblob/wizardry/client/model/ModelIceGiant.java +++ b/src/main/java/electroblob/wizardry/client/model/ModelIceGiant.java @@ -3,14 +3,14 @@ package electroblob.wizardry.client.model; import javax.vecmath.Matrix4f; import javax.vecmath.Vector3f; +import cpw.mods.fml.relauncher.Side; +import cpw.mods.fml.relauncher.SideOnly; import electroblob.wizardry.entity.living.EntityIceGiant; import net.minecraft.client.model.ModelBase; import net.minecraft.client.model.ModelRenderer; import net.minecraft.entity.Entity; import net.minecraft.entity.EntityLivingBase; -import net.minecraft.util.math.MathHelper; -import net.minecraftforge.fml.relauncher.Side; -import net.minecraftforge.fml.relauncher.SideOnly; +import net.minecraft.util.MathHelper; @SideOnly(Side.CLIENT) public class ModelIceGiant extends ModelBase diff --git a/src/main/java/electroblob/wizardry/client/model/ModelPhoenix.java b/src/main/java/electroblob/wizardry/client/model/ModelPhoenix.java index 568b580c..ac472271 100644 --- a/src/main/java/electroblob/wizardry/client/model/ModelPhoenix.java +++ b/src/main/java/electroblob/wizardry/client/model/ModelPhoenix.java @@ -3,7 +3,7 @@ package electroblob.wizardry.client.model; import net.minecraft.client.model.ModelBase; import net.minecraft.client.model.ModelRenderer; import net.minecraft.entity.Entity; -import net.minecraft.util.math.MathHelper; +import net.minecraft.util.MathHelper; public class ModelPhoenix extends ModelBase { @@ -109,7 +109,7 @@ public class ModelPhoenix extends ModelBase { super.render(entity, f, f1, f2, f3, f4, f5); setRotationAngles(f, f1, f2, f3, f4, f5, entity); - //float f6 = (180F / (float)Math.PI); + float f6 = (180F / (float)Math.PI); this.neck.rotateAngleX = f4 / (180F / (float)Math.PI); this.neck.rotateAngleY = f3 / (180F / (float)Math.PI); this.neck.rotateAngleZ = 0.0F; diff --git a/src/main/java/electroblob/wizardry/client/model/ModelSkeletonMinion.java b/src/main/java/electroblob/wizardry/client/model/ModelSkeletonMinion.java new file mode 100644 index 00000000..65ee1b01 --- /dev/null +++ b/src/main/java/electroblob/wizardry/client/model/ModelSkeletonMinion.java @@ -0,0 +1,58 @@ +package electroblob.wizardry.client.model; + +import cpw.mods.fml.relauncher.Side; +import cpw.mods.fml.relauncher.SideOnly; +import electroblob.wizardry.entity.living.EntitySkeletonMinion; +import net.minecraft.client.model.ModelRenderer; +import net.minecraft.client.model.ModelZombie; +import net.minecraft.entity.Entity; +import net.minecraft.entity.EntityLivingBase; +import net.minecraft.entity.monster.EntitySkeleton; + +@SideOnly(Side.CLIENT) +public class ModelSkeletonMinion extends ModelZombie +{ + public ModelSkeletonMinion() + { + this(0.0F); + } + + public ModelSkeletonMinion(float par1) + { + super(par1, 0.0F, 64, 32); + this.bipedRightArm = new ModelRenderer(this, 40, 16); + this.bipedRightArm.addBox(-1.0F, -2.0F, -1.0F, 2, 12, 2, par1); + this.bipedRightArm.setRotationPoint(-5.0F, 2.0F, 0.0F); + this.bipedLeftArm = new ModelRenderer(this, 40, 16); + this.bipedLeftArm.mirror = true; + this.bipedLeftArm.addBox(-1.0F, -2.0F, -1.0F, 2, 12, 2, par1); + this.bipedLeftArm.setRotationPoint(5.0F, 2.0F, 0.0F); + this.bipedRightLeg = new ModelRenderer(this, 0, 16); + this.bipedRightLeg.addBox(-1.0F, 0.0F, -1.0F, 2, 12, 2, par1); + this.bipedRightLeg.setRotationPoint(-2.0F, 12.0F, 0.0F); + this.bipedLeftLeg = new ModelRenderer(this, 0, 16); + this.bipedLeftLeg.mirror = true; + this.bipedLeftLeg.addBox(-1.0F, 0.0F, -1.0F, 2, 12, 2, par1); + this.bipedLeftLeg.setRotationPoint(2.0F, 12.0F, 0.0F); + } + + /** + * Used for easily adding entity-dependent animations. The second and third float params here are the same second + * and third as in the setRotationAngles method. + */ + public void setLivingAnimations(EntityLivingBase par1EntityLivingBase, float par2, float par3, float par4) + { + this.aimedBow = ((EntitySkeletonMinion)par1EntityLivingBase).getSkeletonType() == 1; + super.setLivingAnimations(par1EntityLivingBase, par2, par3, par4); + } + + /** + * Sets the model's various rotation angles. For bipeds, par1 and par2 are used for animating the movement of arms + * and legs, where par1 represents the time(so that arms and legs swing back and forth) and par2 represents how + * "far" arms and legs can swing at most. + */ + public void setRotationAngles(float par1, float par2, float par3, float par4, float par5, float par6, Entity par7Entity) + { + super.setRotationAngles(par1, par2, par3, par4, par5, par6, par7Entity); + } +} diff --git a/src/main/java/electroblob/wizardry/client/model/ModelSpiritHorse.java b/src/main/java/electroblob/wizardry/client/model/ModelSpiritHorse.java new file mode 100644 index 00000000..07c08c7d --- /dev/null +++ b/src/main/java/electroblob/wizardry/client/model/ModelSpiritHorse.java @@ -0,0 +1,559 @@ +package electroblob.wizardry.client.model; + +import org.lwjgl.opengl.GL11; + +import cpw.mods.fml.relauncher.Side; +import cpw.mods.fml.relauncher.SideOnly; +import electroblob.wizardry.entity.living.EntitySpiritHorse; +import net.minecraft.client.model.ModelBase; +import net.minecraft.client.model.ModelRenderer; +import net.minecraft.entity.Entity; +import net.minecraft.entity.EntityLivingBase; +import net.minecraft.util.MathHelper; + +@SideOnly(Side.CLIENT) +public class ModelSpiritHorse extends ModelBase +{ + private ModelRenderer head; + private ModelRenderer mouthTop; + private ModelRenderer mouthBottom; + private ModelRenderer horseLeftEar; + private ModelRenderer horseRightEar; + private ModelRenderer field_110703_f; + private ModelRenderer field_110704_g; + private ModelRenderer neck; + private ModelRenderer field_110717_i; + private ModelRenderer mane; + private ModelRenderer body; + private ModelRenderer tailBase; + private ModelRenderer tailMiddle; + private ModelRenderer tailTip; + private ModelRenderer backLeftLeg; + private ModelRenderer backLeftShin; + private ModelRenderer backLeftHoof; + private ModelRenderer backRightLeg; + private ModelRenderer backRightShin; + private ModelRenderer backRightHoof; + private ModelRenderer frontRightLeg; + private ModelRenderer frontLeftShin; + private ModelRenderer frontLeftHoof; + private ModelRenderer field_110684_D; + private ModelRenderer frontRightShin; + private ModelRenderer frontRightHoof; + private ModelRenderer field_110687_G; + private ModelRenderer field_110695_H; + private ModelRenderer field_110696_I; + private ModelRenderer field_110697_J; + private ModelRenderer field_110698_K; + private ModelRenderer field_110691_L; + private ModelRenderer field_110692_M; + private ModelRenderer field_110693_N; + private ModelRenderer field_110694_O; + private ModelRenderer field_110700_P; + private ModelRenderer field_110699_Q; + private ModelRenderer field_110702_R; + private ModelRenderer field_110701_S; + + public ModelSpiritHorse() + { + this.textureWidth = 128; + this.textureHeight = 128; + this.body = new ModelRenderer(this, 0, 34); + this.body.addBox(-5.0F, -8.0F, -19.0F, 10, 10, 24); + this.body.setRotationPoint(0.0F, 11.0F, 9.0F); + this.tailBase = new ModelRenderer(this, 44, 0); + this.tailBase.addBox(-1.0F, -1.0F, 0.0F, 2, 2, 3); + this.tailBase.setRotationPoint(0.0F, 3.0F, 14.0F); + this.func_110682_a(this.tailBase, -1.134464F, 0.0F, 0.0F); + this.tailMiddle = new ModelRenderer(this, 38, 7); + this.tailMiddle.addBox(-1.5F, -2.0F, 3.0F, 3, 4, 7); + this.tailMiddle.setRotationPoint(0.0F, 3.0F, 14.0F); + this.func_110682_a(this.tailMiddle, -1.134464F, 0.0F, 0.0F); + this.tailTip = new ModelRenderer(this, 24, 3); + this.tailTip.addBox(-1.5F, -4.5F, 9.0F, 3, 4, 7); + this.tailTip.setRotationPoint(0.0F, 3.0F, 14.0F); + this.func_110682_a(this.tailTip, -1.40215F, 0.0F, 0.0F); + this.backLeftLeg = new ModelRenderer(this, 78, 29); + this.backLeftLeg.addBox(-2.5F, -2.0F, -2.5F, 4, 9, 5); + this.backLeftLeg.setRotationPoint(4.0F, 9.0F, 11.0F); + this.backLeftShin = new ModelRenderer(this, 78, 43); + this.backLeftShin.addBox(-2.0F, 0.0F, -1.5F, 3, 5, 3); + this.backLeftShin.setRotationPoint(4.0F, 16.0F, 11.0F); + this.backLeftHoof = new ModelRenderer(this, 78, 51); + this.backLeftHoof.addBox(-2.5F, 5.1F, -2.0F, 4, 3, 4); + this.backLeftHoof.setRotationPoint(4.0F, 16.0F, 11.0F); + this.backRightLeg = new ModelRenderer(this, 96, 29); + this.backRightLeg.addBox(-1.5F, -2.0F, -2.5F, 4, 9, 5); + this.backRightLeg.setRotationPoint(-4.0F, 9.0F, 11.0F); + this.backRightShin = new ModelRenderer(this, 96, 43); + this.backRightShin.addBox(-1.0F, 0.0F, -1.5F, 3, 5, 3); + this.backRightShin.setRotationPoint(-4.0F, 16.0F, 11.0F); + this.backRightHoof = new ModelRenderer(this, 96, 51); + this.backRightHoof.addBox(-1.5F, 5.1F, -2.0F, 4, 3, 4); + this.backRightHoof.setRotationPoint(-4.0F, 16.0F, 11.0F); + this.frontRightLeg = new ModelRenderer(this, 44, 29); + this.frontRightLeg.addBox(-1.9F, -1.0F, -2.1F, 3, 8, 4); + this.frontRightLeg.setRotationPoint(4.0F, 9.0F, -8.0F); + this.frontLeftShin = new ModelRenderer(this, 44, 41); + this.frontLeftShin.addBox(-1.9F, 0.0F, -1.6F, 3, 5, 3); + this.frontLeftShin.setRotationPoint(4.0F, 16.0F, -8.0F); + this.frontLeftHoof = new ModelRenderer(this, 44, 51); + this.frontLeftHoof.addBox(-2.4F, 5.1F, -2.1F, 4, 3, 4); + this.frontLeftHoof.setRotationPoint(4.0F, 16.0F, -8.0F); + this.field_110684_D = new ModelRenderer(this, 60, 29); + this.field_110684_D.addBox(-1.1F, -1.0F, -2.1F, 3, 8, 4); + this.field_110684_D.setRotationPoint(-4.0F, 9.0F, -8.0F); + this.frontRightShin = new ModelRenderer(this, 60, 41); + this.frontRightShin.addBox(-1.1F, 0.0F, -1.6F, 3, 5, 3); + this.frontRightShin.setRotationPoint(-4.0F, 16.0F, -8.0F); + this.frontRightHoof = new ModelRenderer(this, 60, 51); + this.frontRightHoof.addBox(-1.6F, 5.1F, -2.1F, 4, 3, 4); + this.frontRightHoof.setRotationPoint(-4.0F, 16.0F, -8.0F); + this.head = new ModelRenderer(this, 0, 0); + this.head.addBox(-2.5F, -10.0F, -1.5F, 5, 5, 7); + this.head.setRotationPoint(0.0F, 4.0F, -10.0F); + this.func_110682_a(this.head, 0.5235988F, 0.0F, 0.0F); + this.mouthTop = new ModelRenderer(this, 24, 18); + this.mouthTop.addBox(-2.0F, -10.0F, -7.0F, 4, 3, 6); + this.mouthTop.setRotationPoint(0.0F, 3.95F, -10.0F); + this.func_110682_a(this.mouthTop, 0.5235988F, 0.0F, 0.0F); + this.mouthBottom = new ModelRenderer(this, 24, 27); + this.mouthBottom.addBox(-2.0F, -7.0F, -6.5F, 4, 2, 5); + this.mouthBottom.setRotationPoint(0.0F, 4.0F, -10.0F); + this.func_110682_a(this.mouthBottom, 0.5235988F, 0.0F, 0.0F); + this.head.addChild(this.mouthTop); + this.head.addChild(this.mouthBottom); + this.horseLeftEar = new ModelRenderer(this, 0, 0); + this.horseLeftEar.addBox(0.45F, -12.0F, 4.0F, 2, 3, 1); + this.horseLeftEar.setRotationPoint(0.0F, 4.0F, -10.0F); + this.func_110682_a(this.horseLeftEar, 0.5235988F, 0.0F, 0.0F); + this.horseRightEar = new ModelRenderer(this, 0, 0); + this.horseRightEar.addBox(-2.45F, -12.0F, 4.0F, 2, 3, 1); + this.horseRightEar.setRotationPoint(0.0F, 4.0F, -10.0F); + this.func_110682_a(this.horseRightEar, 0.5235988F, 0.0F, 0.0F); + this.field_110703_f = new ModelRenderer(this, 0, 12); + this.field_110703_f.addBox(-2.0F, -16.0F, 4.0F, 2, 7, 1); + this.field_110703_f.setRotationPoint(0.0F, 4.0F, -10.0F); + this.func_110682_a(this.field_110703_f, 0.5235988F, 0.0F, 0.2617994F); + this.field_110704_g = new ModelRenderer(this, 0, 12); + this.field_110704_g.addBox(0.0F, -16.0F, 4.0F, 2, 7, 1); + this.field_110704_g.setRotationPoint(0.0F, 4.0F, -10.0F); + this.func_110682_a(this.field_110704_g, 0.5235988F, 0.0F, -0.2617994F); + this.neck = new ModelRenderer(this, 0, 12); + this.neck.addBox(-2.05F, -9.8F, -2.0F, 4, 14, 8); + this.neck.setRotationPoint(0.0F, 4.0F, -10.0F); + this.func_110682_a(this.neck, 0.5235988F, 0.0F, 0.0F); + this.field_110687_G = new ModelRenderer(this, 0, 34); + this.field_110687_G.addBox(-3.0F, 0.0F, 0.0F, 8, 8, 3); + this.field_110687_G.setRotationPoint(-7.5F, 3.0F, 10.0F); + this.func_110682_a(this.field_110687_G, 0.0F, ((float)Math.PI / 2F), 0.0F); + this.field_110695_H = new ModelRenderer(this, 0, 47); + this.field_110695_H.addBox(-3.0F, 0.0F, 0.0F, 8, 8, 3); + this.field_110695_H.setRotationPoint(4.5F, 3.0F, 10.0F); + this.func_110682_a(this.field_110695_H, 0.0F, ((float)Math.PI / 2F), 0.0F); + this.field_110696_I = new ModelRenderer(this, 80, 0); + this.field_110696_I.addBox(-5.0F, 0.0F, -3.0F, 10, 1, 8); + this.field_110696_I.setRotationPoint(0.0F, 2.0F, 2.0F); + this.field_110697_J = new ModelRenderer(this, 106, 9); + this.field_110697_J.addBox(-1.5F, -1.0F, -3.0F, 3, 1, 2); + this.field_110697_J.setRotationPoint(0.0F, 2.0F, 2.0F); + this.field_110698_K = new ModelRenderer(this, 80, 9); + this.field_110698_K.addBox(-4.0F, -1.0F, 3.0F, 8, 1, 2); + this.field_110698_K.setRotationPoint(0.0F, 2.0F, 2.0F); + this.field_110692_M = new ModelRenderer(this, 74, 0); + this.field_110692_M.addBox(-0.5F, 6.0F, -1.0F, 1, 2, 2); + this.field_110692_M.setRotationPoint(5.0F, 3.0F, 2.0F); + this.field_110691_L = new ModelRenderer(this, 70, 0); + this.field_110691_L.addBox(-0.5F, 0.0F, -0.5F, 1, 6, 1); + this.field_110691_L.setRotationPoint(5.0F, 3.0F, 2.0F); + this.field_110694_O = new ModelRenderer(this, 74, 4); + this.field_110694_O.addBox(-0.5F, 6.0F, -1.0F, 1, 2, 2); + this.field_110694_O.setRotationPoint(-5.0F, 3.0F, 2.0F); + this.field_110693_N = new ModelRenderer(this, 80, 0); + this.field_110693_N.addBox(-0.5F, 0.0F, -0.5F, 1, 6, 1); + this.field_110693_N.setRotationPoint(-5.0F, 3.0F, 2.0F); + this.field_110700_P = new ModelRenderer(this, 74, 13); + this.field_110700_P.addBox(1.5F, -8.0F, -4.0F, 1, 2, 2); + this.field_110700_P.setRotationPoint(0.0F, 4.0F, -10.0F); + this.func_110682_a(this.field_110700_P, 0.5235988F, 0.0F, 0.0F); + this.field_110699_Q = new ModelRenderer(this, 74, 13); + this.field_110699_Q.addBox(-2.5F, -8.0F, -4.0F, 1, 2, 2); + this.field_110699_Q.setRotationPoint(0.0F, 4.0F, -10.0F); + this.func_110682_a(this.field_110699_Q, 0.5235988F, 0.0F, 0.0F); + this.field_110702_R = new ModelRenderer(this, 44, 10); + this.field_110702_R.addBox(2.6F, -6.0F, -6.0F, 0, 3, 16); + this.field_110702_R.setRotationPoint(0.0F, 4.0F, -10.0F); + this.field_110701_S = new ModelRenderer(this, 44, 5); + this.field_110701_S.addBox(-2.6F, -6.0F, -6.0F, 0, 3, 16); + this.field_110701_S.setRotationPoint(0.0F, 4.0F, -10.0F); + this.mane = new ModelRenderer(this, 58, 0); + this.mane.addBox(-1.0F, -11.5F, 5.0F, 2, 16, 4); + this.mane.setRotationPoint(0.0F, 4.0F, -10.0F); + this.func_110682_a(this.mane, 0.5235988F, 0.0F, 0.0F); + this.field_110717_i = new ModelRenderer(this, 80, 12); + this.field_110717_i.addBox(-2.5F, -10.1F, -7.0F, 5, 5, 12, 0.2F); + this.field_110717_i.setRotationPoint(0.0F, 4.0F, -10.0F); + this.func_110682_a(this.field_110717_i, 0.5235988F, 0.0F, 0.0F); + } + + /** + * Sets the models various rotation angles then renders the model. + */ + public void render(Entity par1Entity, float par2, float par3, float par4, float par5, float par6, float par7) + { + EntitySpiritHorse entityhorse = (EntitySpiritHorse)par1Entity; + int i = entityhorse.getHorseType(); + float f6 = entityhorse.getGrassEatingAmount(0.0F); + boolean flag = entityhorse.isAdultHorse(); + boolean flag1 = flag && entityhorse.isHorseSaddled(); + boolean flag2 = flag && entityhorse.isChested(); + boolean flag3 = i == 1 || i == 2; + float f7 = entityhorse.getHorseSize(); + boolean flag4 = entityhorse.riddenByEntity != null; + + if (flag1) + { + this.field_110717_i.render(par7); + this.field_110696_I.render(par7); + this.field_110697_J.render(par7); + this.field_110698_K.render(par7); + this.field_110691_L.render(par7); + this.field_110692_M.render(par7); + this.field_110693_N.render(par7); + this.field_110694_O.render(par7); + this.field_110700_P.render(par7); + this.field_110699_Q.render(par7); + + if (flag4) + { + this.field_110702_R.render(par7); + this.field_110701_S.render(par7); + } + } + + if (!flag) + { + GL11.glPushMatrix(); + GL11.glScalef(f7, 0.5F + f7 * 0.5F, f7); + GL11.glTranslatef(0.0F, 0.95F * (1.0F - f7), 0.0F); + } + + this.backLeftLeg.render(par7); + this.backLeftShin.render(par7); + this.backLeftHoof.render(par7); + this.backRightLeg.render(par7); + this.backRightShin.render(par7); + this.backRightHoof.render(par7); + this.frontRightLeg.render(par7); + this.frontLeftShin.render(par7); + this.frontLeftHoof.render(par7); + this.field_110684_D.render(par7); + this.frontRightShin.render(par7); + this.frontRightHoof.render(par7); + + if (!flag) + { + GL11.glPopMatrix(); + GL11.glPushMatrix(); + GL11.glScalef(f7, f7, f7); + GL11.glTranslatef(0.0F, 1.35F * (1.0F - f7), 0.0F); + } + + this.body.render(par7); + this.tailBase.render(par7); + this.tailMiddle.render(par7); + this.tailTip.render(par7); + this.neck.render(par7); + this.mane.render(par7); + + if (!flag) + { + GL11.glPopMatrix(); + GL11.glPushMatrix(); + float f8 = 0.5F + f7 * f7 * 0.5F; + GL11.glScalef(f8, f8, f8); + + if (f6 <= 0.0F) + { + GL11.glTranslatef(0.0F, 1.35F * (1.0F - f7), 0.0F); + } + else + { + GL11.glTranslatef(0.0F, 0.9F * (1.0F - f7) * f6 + 1.35F * (1.0F - f7) * (1.0F - f6), 0.15F * (1.0F - f7) * f6); + } + } + + if (flag3) + { + this.field_110703_f.render(par7); + this.field_110704_g.render(par7); + } + else + { + this.horseLeftEar.render(par7); + this.horseRightEar.render(par7); + } + + this.head.render(par7); + + if (!flag) + { + GL11.glPopMatrix(); + } + + if (flag2) + { + this.field_110687_G.render(par7); + this.field_110695_H.render(par7); + } + } + + private void func_110682_a(ModelRenderer par1ModelRenderer, float par2, float par3, float par4) + { + par1ModelRenderer.rotateAngleX = par2; + par1ModelRenderer.rotateAngleY = par3; + par1ModelRenderer.rotateAngleZ = par4; + } + + private float func_110683_a(float par1, float par2, float par3) + { + float f3; + + for (f3 = par2 - par1; f3 < -180.0F; f3 += 360.0F) + { + ; + } + + while (f3 >= 180.0F) + { + f3 -= 360.0F; + } + + return par1 + par3 * f3; + } + + /** + * Used for easily adding entity-dependent animations. The second and third float params here are the same second + * and third as in the setRotationAngles method. + */ + public void setLivingAnimations(EntityLivingBase par1EntityLivingBase, float par2, float par3, float par4) + { + super.setLivingAnimations(par1EntityLivingBase, par2, par3, par4); + float f3 = this.func_110683_a(par1EntityLivingBase.prevRenderYawOffset, par1EntityLivingBase.renderYawOffset, par4); + float f4 = this.func_110683_a(par1EntityLivingBase.prevRotationYawHead, par1EntityLivingBase.rotationYawHead, par4); + float f5 = par1EntityLivingBase.prevRotationPitch + (par1EntityLivingBase.rotationPitch - par1EntityLivingBase.prevRotationPitch) * par4; + float f6 = f4 - f3; + float f7 = f5 / (180F / (float)Math.PI); + + if (f6 > 20.0F) + { + f6 = 20.0F; + } + + if (f6 < -20.0F) + { + f6 = -20.0F; + } + + if (par3 > 0.2F) + { + f7 += MathHelper.cos(par2 * 0.4F) * 0.15F * par3; + } + + EntitySpiritHorse entityhorse = (EntitySpiritHorse)par1EntityLivingBase; + float f8 = entityhorse.getGrassEatingAmount(par4); + float f9 = entityhorse.getRearingAmount(par4); + float f10 = 1.0F - f9; + float f11 = entityhorse.func_110201_q(par4); + boolean flag = entityhorse.field_110278_bp != 0; + boolean flag1 = entityhorse.isHorseSaddled(); + boolean flag2 = entityhorse.riddenByEntity != null; + float f12 = (float)par1EntityLivingBase.ticksExisted + par4; + float f13 = MathHelper.cos(par2 * 0.6662F + (float)Math.PI); + float f14 = f13 * 0.8F * par3; + this.head.rotationPointY = 4.0F; + this.head.rotationPointZ = -10.0F; + this.tailBase.rotationPointY = 3.0F; + this.tailMiddle.rotationPointZ = 14.0F; + this.field_110695_H.rotationPointY = 3.0F; + this.field_110695_H.rotationPointZ = 10.0F; + this.body.rotateAngleX = 0.0F; + this.head.rotateAngleX = 0.5235988F + f7; + this.head.rotateAngleY = f6 / (180F / (float)Math.PI); + this.head.rotateAngleX = f9 * (0.2617994F + f7) + f8 * 2.18166F + (1.0F - Math.max(f9, f8)) * this.head.rotateAngleX; + this.head.rotateAngleY = f9 * (f6 / (180F / (float)Math.PI)) + (1.0F - Math.max(f9, f8)) * this.head.rotateAngleY; + this.head.rotationPointY = f9 * -6.0F + f8 * 11.0F + (1.0F - Math.max(f9, f8)) * this.head.rotationPointY; + this.head.rotationPointZ = f9 * -1.0F + f8 * -10.0F + (1.0F - Math.max(f9, f8)) * this.head.rotationPointZ; + this.tailBase.rotationPointY = f9 * 9.0F + f10 * this.tailBase.rotationPointY; + this.tailMiddle.rotationPointZ = f9 * 18.0F + f10 * this.tailMiddle.rotationPointZ; + this.field_110695_H.rotationPointY = f9 * 5.5F + f10 * this.field_110695_H.rotationPointY; + this.field_110695_H.rotationPointZ = f9 * 15.0F + f10 * this.field_110695_H.rotationPointZ; + this.body.rotateAngleX = f9 * -((float)Math.PI / 4F) + f10 * this.body.rotateAngleX; + this.horseLeftEar.rotationPointY = this.head.rotationPointY; + this.horseRightEar.rotationPointY = this.head.rotationPointY; + this.field_110703_f.rotationPointY = this.head.rotationPointY; + this.field_110704_g.rotationPointY = this.head.rotationPointY; + this.neck.rotationPointY = this.head.rotationPointY; + this.mouthTop.rotationPointY = 0.02F; + this.mouthBottom.rotationPointY = 0.0F; + this.mane.rotationPointY = this.head.rotationPointY; + this.horseLeftEar.rotationPointZ = this.head.rotationPointZ; + this.horseRightEar.rotationPointZ = this.head.rotationPointZ; + this.field_110703_f.rotationPointZ = this.head.rotationPointZ; + this.field_110704_g.rotationPointZ = this.head.rotationPointZ; + this.neck.rotationPointZ = this.head.rotationPointZ; + this.mouthTop.rotationPointZ = 0.02F - f11 * 1.0F; + this.mouthBottom.rotationPointZ = 0.0F + f11 * 1.0F; + this.mane.rotationPointZ = this.head.rotationPointZ; + this.horseLeftEar.rotateAngleX = this.head.rotateAngleX; + this.horseRightEar.rotateAngleX = this.head.rotateAngleX; + this.field_110703_f.rotateAngleX = this.head.rotateAngleX; + this.field_110704_g.rotateAngleX = this.head.rotateAngleX; + this.neck.rotateAngleX = this.head.rotateAngleX; + this.mouthTop.rotateAngleX = 0.0F - 0.09424778F * f11; + this.mouthBottom.rotateAngleX = 0.0F + 0.15707964F * f11; + this.mane.rotateAngleX = this.head.rotateAngleX; + this.horseLeftEar.rotateAngleY = this.head.rotateAngleY; + this.horseRightEar.rotateAngleY = this.head.rotateAngleY; + this.field_110703_f.rotateAngleY = this.head.rotateAngleY; + this.field_110704_g.rotateAngleY = this.head.rotateAngleY; + this.neck.rotateAngleY = this.head.rotateAngleY; + this.mouthTop.rotateAngleY = 0.0F; + this.mouthBottom.rotateAngleY = 0.0F; + this.mane.rotateAngleY = this.head.rotateAngleY; + this.field_110687_G.rotateAngleX = f14 / 5.0F; + this.field_110695_H.rotateAngleX = -f14 / 5.0F; + float f15 = ((float)Math.PI / 2F); + float f16 = ((float)Math.PI * 3F / 2F); + float f17 = -1.0471976F; + float f18 = 0.2617994F * f9; + float f19 = MathHelper.cos(f12 * 0.6F + (float)Math.PI); + this.frontRightLeg.rotationPointY = -2.0F * f9 + 9.0F * f10; + this.frontRightLeg.rotationPointZ = -2.0F * f9 + -8.0F * f10; + this.field_110684_D.rotationPointY = this.frontRightLeg.rotationPointY; + this.field_110684_D.rotationPointZ = this.frontRightLeg.rotationPointZ; + this.backLeftShin.rotationPointY = this.backLeftLeg.rotationPointY + MathHelper.sin(((float)Math.PI / 2F) + f18 + f10 * -f13 * 0.5F * par3) * 7.0F; + this.backLeftShin.rotationPointZ = this.backLeftLeg.rotationPointZ + MathHelper.cos(((float)Math.PI * 3F / 2F) + f18 + f10 * -f13 * 0.5F * par3) * 7.0F; + this.backRightShin.rotationPointY = this.backRightLeg.rotationPointY + MathHelper.sin(((float)Math.PI / 2F) + f18 + f10 * f13 * 0.5F * par3) * 7.0F; + this.backRightShin.rotationPointZ = this.backRightLeg.rotationPointZ + MathHelper.cos(((float)Math.PI * 3F / 2F) + f18 + f10 * f13 * 0.5F * par3) * 7.0F; + float f20 = (-1.0471976F + f19) * f9 + f14 * f10; + float f21 = (-1.0471976F + -f19) * f9 + -f14 * f10; + this.frontLeftShin.rotationPointY = this.frontRightLeg.rotationPointY + MathHelper.sin(((float)Math.PI / 2F) + f20) * 7.0F; + this.frontLeftShin.rotationPointZ = this.frontRightLeg.rotationPointZ + MathHelper.cos(((float)Math.PI * 3F / 2F) + f20) * 7.0F; + this.frontRightShin.rotationPointY = this.field_110684_D.rotationPointY + MathHelper.sin(((float)Math.PI / 2F) + f21) * 7.0F; + this.frontRightShin.rotationPointZ = this.field_110684_D.rotationPointZ + MathHelper.cos(((float)Math.PI * 3F / 2F) + f21) * 7.0F; + this.backLeftLeg.rotateAngleX = f18 + -f13 * 0.5F * par3 * f10; + this.backLeftShin.rotateAngleX = -0.08726646F * f9 + (-f13 * 0.5F * par3 - Math.max(0.0F, f13 * 0.5F * par3)) * f10; + this.backLeftHoof.rotateAngleX = this.backLeftShin.rotateAngleX; + this.backRightLeg.rotateAngleX = f18 + f13 * 0.5F * par3 * f10; + this.backRightShin.rotateAngleX = -0.08726646F * f9 + (f13 * 0.5F * par3 - Math.max(0.0F, -f13 * 0.5F * par3)) * f10; + this.backRightHoof.rotateAngleX = this.backRightShin.rotateAngleX; + this.frontRightLeg.rotateAngleX = f20; + this.frontLeftShin.rotateAngleX = (this.frontRightLeg.rotateAngleX + (float)Math.PI * Math.max(0.0F, 0.2F + f19 * 0.2F)) * f9 + (f14 + Math.max(0.0F, f13 * 0.5F * par3)) * f10; + this.frontLeftHoof.rotateAngleX = this.frontLeftShin.rotateAngleX; + this.field_110684_D.rotateAngleX = f21; + this.frontRightShin.rotateAngleX = (this.field_110684_D.rotateAngleX + (float)Math.PI * Math.max(0.0F, 0.2F - f19 * 0.2F)) * f9 + (-f14 + Math.max(0.0F, -f13 * 0.5F * par3)) * f10; + this.frontRightHoof.rotateAngleX = this.frontRightShin.rotateAngleX; + this.backLeftHoof.rotationPointY = this.backLeftShin.rotationPointY; + this.backLeftHoof.rotationPointZ = this.backLeftShin.rotationPointZ; + this.backRightHoof.rotationPointY = this.backRightShin.rotationPointY; + this.backRightHoof.rotationPointZ = this.backRightShin.rotationPointZ; + this.frontLeftHoof.rotationPointY = this.frontLeftShin.rotationPointY; + this.frontLeftHoof.rotationPointZ = this.frontLeftShin.rotationPointZ; + this.frontRightHoof.rotationPointY = this.frontRightShin.rotationPointY; + this.frontRightHoof.rotationPointZ = this.frontRightShin.rotationPointZ; + + if (flag1) + { + this.field_110696_I.rotationPointY = f9 * 0.5F + f10 * 2.0F; + this.field_110696_I.rotationPointZ = f9 * 11.0F + f10 * 2.0F; + this.field_110697_J.rotationPointY = this.field_110696_I.rotationPointY; + this.field_110698_K.rotationPointY = this.field_110696_I.rotationPointY; + this.field_110691_L.rotationPointY = this.field_110696_I.rotationPointY; + this.field_110693_N.rotationPointY = this.field_110696_I.rotationPointY; + this.field_110692_M.rotationPointY = this.field_110696_I.rotationPointY; + this.field_110694_O.rotationPointY = this.field_110696_I.rotationPointY; + this.field_110687_G.rotationPointY = this.field_110695_H.rotationPointY; + this.field_110697_J.rotationPointZ = this.field_110696_I.rotationPointZ; + this.field_110698_K.rotationPointZ = this.field_110696_I.rotationPointZ; + this.field_110691_L.rotationPointZ = this.field_110696_I.rotationPointZ; + this.field_110693_N.rotationPointZ = this.field_110696_I.rotationPointZ; + this.field_110692_M.rotationPointZ = this.field_110696_I.rotationPointZ; + this.field_110694_O.rotationPointZ = this.field_110696_I.rotationPointZ; + this.field_110687_G.rotationPointZ = this.field_110695_H.rotationPointZ; + this.field_110696_I.rotateAngleX = this.body.rotateAngleX; + this.field_110697_J.rotateAngleX = this.body.rotateAngleX; + this.field_110698_K.rotateAngleX = this.body.rotateAngleX; + this.field_110702_R.rotationPointY = this.head.rotationPointY; + this.field_110701_S.rotationPointY = this.head.rotationPointY; + this.field_110717_i.rotationPointY = this.head.rotationPointY; + this.field_110700_P.rotationPointY = this.head.rotationPointY; + this.field_110699_Q.rotationPointY = this.head.rotationPointY; + this.field_110702_R.rotationPointZ = this.head.rotationPointZ; + this.field_110701_S.rotationPointZ = this.head.rotationPointZ; + this.field_110717_i.rotationPointZ = this.head.rotationPointZ; + this.field_110700_P.rotationPointZ = this.head.rotationPointZ; + this.field_110699_Q.rotationPointZ = this.head.rotationPointZ; + this.field_110702_R.rotateAngleX = f7; + this.field_110701_S.rotateAngleX = f7; + this.field_110717_i.rotateAngleX = this.head.rotateAngleX; + this.field_110700_P.rotateAngleX = this.head.rotateAngleX; + this.field_110699_Q.rotateAngleX = this.head.rotateAngleX; + this.field_110717_i.rotateAngleY = this.head.rotateAngleY; + this.field_110700_P.rotateAngleY = this.head.rotateAngleY; + this.field_110702_R.rotateAngleY = this.head.rotateAngleY; + this.field_110699_Q.rotateAngleY = this.head.rotateAngleY; + this.field_110701_S.rotateAngleY = this.head.rotateAngleY; + + if (flag2) + { + this.field_110691_L.rotateAngleX = -1.0471976F; + this.field_110692_M.rotateAngleX = -1.0471976F; + this.field_110693_N.rotateAngleX = -1.0471976F; + this.field_110694_O.rotateAngleX = -1.0471976F; + this.field_110691_L.rotateAngleZ = 0.0F; + this.field_110692_M.rotateAngleZ = 0.0F; + this.field_110693_N.rotateAngleZ = 0.0F; + this.field_110694_O.rotateAngleZ = 0.0F; + } + else + { + this.field_110691_L.rotateAngleX = f14 / 3.0F; + this.field_110692_M.rotateAngleX = f14 / 3.0F; + this.field_110693_N.rotateAngleX = f14 / 3.0F; + this.field_110694_O.rotateAngleX = f14 / 3.0F; + this.field_110691_L.rotateAngleZ = f14 / 5.0F; + this.field_110692_M.rotateAngleZ = f14 / 5.0F; + this.field_110693_N.rotateAngleZ = -f14 / 5.0F; + this.field_110694_O.rotateAngleZ = -f14 / 5.0F; + } + } + + f15 = -1.3089F + par3 * 1.5F; + + if (f15 > 0.0F) + { + f15 = 0.0F; + } + + if (flag) + { + this.tailBase.rotateAngleY = MathHelper.cos(f12 * 0.7F); + f15 = 0.0F; + } + else + { + this.tailBase.rotateAngleY = 0.0F; + } + + this.tailMiddle.rotateAngleY = this.tailBase.rotateAngleY; + this.tailTip.rotateAngleY = this.tailBase.rotateAngleY; + this.tailMiddle.rotationPointY = this.tailBase.rotationPointY; + this.tailTip.rotationPointY = this.tailBase.rotationPointY; + this.tailMiddle.rotationPointZ = this.tailBase.rotationPointZ; + this.tailTip.rotationPointZ = this.tailBase.rotationPointZ; + this.tailBase.rotateAngleX = f15; + this.tailMiddle.rotateAngleX = f15; + this.tailTip.rotateAngleX = -0.2618F + f15; + } +} diff --git a/src/main/java/electroblob/wizardry/client/model/ModelSpiritWolf.java b/src/main/java/electroblob/wizardry/client/model/ModelSpiritWolf.java new file mode 100644 index 00000000..8032e03e --- /dev/null +++ b/src/main/java/electroblob/wizardry/client/model/ModelSpiritWolf.java @@ -0,0 +1,190 @@ +package electroblob.wizardry.client.model; + +import net.minecraft.client.model.ModelBase; +import net.minecraft.client.model.ModelRenderer; +import net.minecraft.entity.Entity; +import net.minecraft.entity.EntityLivingBase; +import net.minecraft.util.MathHelper; + +import org.lwjgl.opengl.GL11; + +import cpw.mods.fml.relauncher.Side; +import cpw.mods.fml.relauncher.SideOnly; +import electroblob.wizardry.entity.living.EntitySpiritWolf; + +@SideOnly(Side.CLIENT) +public class ModelSpiritWolf extends ModelBase +{ + /** main box for the wolf head */ + public ModelRenderer wolfHeadMain; + + /** The wolf's body */ + public ModelRenderer wolfBody; + + /** Wolf'se first leg */ + public ModelRenderer wolfLeg1; + + /** Wolf's second leg */ + public ModelRenderer wolfLeg2; + + /** Wolf's third leg */ + public ModelRenderer wolfLeg3; + + /** Wolf's fourth leg */ + public ModelRenderer wolfLeg4; + + /** The wolf's tail */ + ModelRenderer wolfTail; + + /** The wolf's mane */ + ModelRenderer wolfMane; + + public ModelSpiritWolf() + { + float f = 0.0F; + float f1 = 13.5F; + this.wolfHeadMain = new ModelRenderer(this, 0, 0); + this.wolfHeadMain.addBox(-3.0F, -3.0F, -2.0F, 6, 6, 4, f); + this.wolfHeadMain.setRotationPoint(-1.0F, f1, -7.0F); + this.wolfBody = new ModelRenderer(this, 18, 14); + this.wolfBody.addBox(-4.0F, -2.0F, -3.0F, 6, 9, 6, f); + this.wolfBody.setRotationPoint(0.0F, 14.0F, 2.0F); + this.wolfMane = new ModelRenderer(this, 21, 0); + this.wolfMane.addBox(-4.0F, -3.0F, -3.0F, 8, 6, 7, f); + this.wolfMane.setRotationPoint(-1.0F, 14.0F, 2.0F); + this.wolfLeg1 = new ModelRenderer(this, 0, 18); + this.wolfLeg1.addBox(-1.0F, 0.0F, -1.0F, 2, 8, 2, f); + this.wolfLeg1.setRotationPoint(-2.5F, 16.0F, 7.0F); + this.wolfLeg2 = new ModelRenderer(this, 0, 18); + this.wolfLeg2.addBox(-1.0F, 0.0F, -1.0F, 2, 8, 2, f); + this.wolfLeg2.setRotationPoint(0.5F, 16.0F, 7.0F); + this.wolfLeg3 = new ModelRenderer(this, 0, 18); + this.wolfLeg3.addBox(-1.0F, 0.0F, -1.0F, 2, 8, 2, f); + this.wolfLeg3.setRotationPoint(-2.5F, 16.0F, -4.0F); + this.wolfLeg4 = new ModelRenderer(this, 0, 18); + this.wolfLeg4.addBox(-1.0F, 0.0F, -1.0F, 2, 8, 2, f); + this.wolfLeg4.setRotationPoint(0.5F, 16.0F, -4.0F); + this.wolfTail = new ModelRenderer(this, 9, 18); + this.wolfTail.addBox(-1.0F, 0.0F, -1.0F, 2, 8, 2, f); + this.wolfTail.setRotationPoint(-1.0F, 12.0F, 8.0F); + this.wolfHeadMain.setTextureOffset(16, 14).addBox(-3.0F, -5.0F, 0.0F, 2, 2, 1, f); + this.wolfHeadMain.setTextureOffset(16, 14).addBox(1.0F, -5.0F, 0.0F, 2, 2, 1, f); + this.wolfHeadMain.setTextureOffset(0, 10).addBox(-1.5F, 0.0F, -5.0F, 3, 3, 4, f); + } + + /** + * Sets the models various rotation angles then renders the model. + */ + public void render(Entity par1Entity, float par2, float par3, float par4, float par5, float par6, float par7) + { + super.render(par1Entity, par2, par3, par4, par5, par6, par7); + this.setRotationAngles(par2, par3, par4, par5, par6, par7, par1Entity); + + if (this.isChild) + { + float f6 = 2.0F; + GL11.glPushMatrix(); + GL11.glEnable(GL11.GL_BLEND); + GL11.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA); + GL11.glTranslatef(0.0F, 5.0F * par7, 2.0F * par7); + this.wolfHeadMain.renderWithRotation(par7); + GL11.glDisable(GL11.GL_BLEND); + GL11.glPopMatrix(); + GL11.glPushMatrix(); + GL11.glEnable(GL11.GL_BLEND); + GL11.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA); + GL11.glScalef(1.0F / f6, 1.0F / f6, 1.0F / f6); + GL11.glTranslatef(0.0F, 24.0F * par7, 0.0F); + this.wolfBody.render(par7); + this.wolfLeg1.render(par7); + this.wolfLeg2.render(par7); + this.wolfLeg3.render(par7); + this.wolfLeg4.render(par7); + this.wolfTail.renderWithRotation(par7); + this.wolfMane.render(par7); + GL11.glDisable(GL11.GL_BLEND); + GL11.glPopMatrix(); + } + else + { + this.wolfHeadMain.renderWithRotation(par7); + this.wolfBody.render(par7); + this.wolfLeg1.render(par7); + this.wolfLeg2.render(par7); + this.wolfLeg3.render(par7); + this.wolfLeg4.render(par7); + this.wolfTail.renderWithRotation(par7); + this.wolfMane.render(par7); + } + } + + /** + * Used for easily adding entity-dependent animations. The second and third float params here are the same second + * and third as in the setRotationAngles method. + */ + public void setLivingAnimations(EntityLivingBase par1EntityLivingBase, float par2, float par3, float par4) + { + EntitySpiritWolf EntitySpiritWolf = (EntitySpiritWolf)par1EntityLivingBase; + + if (EntitySpiritWolf.isAngry()) + { + this.wolfTail.rotateAngleY = 0.0F; + } + else + { + this.wolfTail.rotateAngleY = MathHelper.cos(par2 * 0.6662F) * 1.4F * par3; + } + + if (EntitySpiritWolf.isSitting()) + { + this.wolfMane.setRotationPoint(-1.0F, 16.0F, -3.0F); + this.wolfMane.rotateAngleX = ((float)Math.PI * 2F / 5F); + this.wolfMane.rotateAngleY = 0.0F; + this.wolfBody.setRotationPoint(0.0F, 18.0F, 0.0F); + this.wolfBody.rotateAngleX = ((float)Math.PI / 4F); + this.wolfTail.setRotationPoint(-1.0F, 21.0F, 6.0F); + this.wolfLeg1.setRotationPoint(-2.5F, 22.0F, 2.0F); + this.wolfLeg1.rotateAngleX = ((float)Math.PI * 3F / 2F); + this.wolfLeg2.setRotationPoint(0.5F, 22.0F, 2.0F); + this.wolfLeg2.rotateAngleX = ((float)Math.PI * 3F / 2F); + this.wolfLeg3.rotateAngleX = 5.811947F; + this.wolfLeg3.setRotationPoint(-2.49F, 17.0F, -4.0F); + this.wolfLeg4.rotateAngleX = 5.811947F; + this.wolfLeg4.setRotationPoint(0.51F, 17.0F, -4.0F); + } + else + { + this.wolfBody.setRotationPoint(0.0F, 14.0F, 2.0F); + this.wolfBody.rotateAngleX = ((float)Math.PI / 2F); + this.wolfMane.setRotationPoint(-1.0F, 14.0F, -3.0F); + this.wolfMane.rotateAngleX = this.wolfBody.rotateAngleX; + this.wolfTail.setRotationPoint(-1.0F, 12.0F, 8.0F); + this.wolfLeg1.setRotationPoint(-2.5F, 16.0F, 7.0F); + this.wolfLeg2.setRotationPoint(0.5F, 16.0F, 7.0F); + this.wolfLeg3.setRotationPoint(-2.5F, 16.0F, -4.0F); + this.wolfLeg4.setRotationPoint(0.5F, 16.0F, -4.0F); + this.wolfLeg1.rotateAngleX = MathHelper.cos(par2 * 0.6662F) * 1.4F * par3; + this.wolfLeg2.rotateAngleX = MathHelper.cos(par2 * 0.6662F + (float)Math.PI) * 1.4F * par3; + this.wolfLeg3.rotateAngleX = MathHelper.cos(par2 * 0.6662F + (float)Math.PI) * 1.4F * par3; + this.wolfLeg4.rotateAngleX = MathHelper.cos(par2 * 0.6662F) * 1.4F * par3; + } + + this.wolfHeadMain.rotateAngleZ = EntitySpiritWolf.getInterestedAngle(par4) + EntitySpiritWolf.getShakeAngle(par4, 0.0F); + this.wolfMane.rotateAngleZ = EntitySpiritWolf.getShakeAngle(par4, -0.08F); + this.wolfBody.rotateAngleZ = EntitySpiritWolf.getShakeAngle(par4, -0.16F); + this.wolfTail.rotateAngleZ = EntitySpiritWolf.getShakeAngle(par4, -0.2F); + } + + /** + * Sets the model's various rotation angles. For bipeds, par1 and par2 are used for animating the movement of arms + * and legs, where par1 represents the time(so that arms and legs swing back and forth) and par2 represents how + * "far" arms and legs can swing at most. + */ + public void setRotationAngles(float par1, float par2, float par3, float par4, float par5, float par6, Entity par7Entity) + { + super.setRotationAngles(par1, par2, par3, par4, par5, par6, par7Entity); + this.wolfHeadMain.rotateAngleX = par5 / (180F / (float)Math.PI); + this.wolfHeadMain.rotateAngleY = par4 / (180F / (float)Math.PI); + this.wolfTail.rotateAngleX = par3; + } +} diff --git a/src/main/java/electroblob/wizardry/client/model/ModelWizard.java b/src/main/java/electroblob/wizardry/client/model/ModelWizard.java index e10cf45e..cc6c2c90 100644 --- a/src/main/java/electroblob/wizardry/client/model/ModelWizard.java +++ b/src/main/java/electroblob/wizardry/client/model/ModelWizard.java @@ -2,6 +2,7 @@ package electroblob.wizardry.client.model; import net.minecraft.client.model.ModelBiped; import net.minecraft.client.model.ModelRenderer; +import net.minecraft.entity.Entity; public class ModelWizard extends ModelBiped { diff --git a/src/main/java/electroblob/wizardry/client/model/ModelWizardArmour.java b/src/main/java/electroblob/wizardry/client/model/ModelWizardArmour.java index 3829f103..cde60300 100644 --- a/src/main/java/electroblob/wizardry/client/model/ModelWizardArmour.java +++ b/src/main/java/electroblob/wizardry/client/model/ModelWizardArmour.java @@ -6,106 +6,108 @@ import net.minecraft.entity.Entity; public class ModelWizardArmour extends ModelBiped { - ModelRenderer Shape1; - ModelRenderer Shape2; - ModelRenderer Shape3; - ModelRenderer Shape4; - ModelRenderer Shape5; - ModelRenderer Shape6; - ModelRenderer Shape7; - ModelRenderer robe; - - public ModelWizardArmour(float scale){ - - super(scale, 0, 64, 64); - - // This is necessary to stop the head from scaling. - this.bipedHead = new ModelRenderer(this, 0, 0); - this.bipedHead.addBox(-4.0F, -8.0F, -4.0F, 8, 8, 8, 0.1f); - this.bipedHead.setRotationPoint(0.0F, 0.0F + 0, 0.0F); - - Shape1 = new ModelRenderer(this, -16, 32); - Shape1.addBox(-8F, -7F, -8F, 16, 0, 16); - Shape1.setRotationPoint(0F, 0F, 0F); - Shape1.setTextureSize(64, 64); - Shape1.mirror = true; - setRotation(Shape1, 0F, 0F, 0F); - - Shape2 = new ModelRenderer(this, 0, 48); - Shape2.addBox(0F, 0F, 0F, 6, 2, 6); - Shape2.setRotationPoint(-3F, -10F, -3F); - Shape2.setTextureSize(64, 64); - Shape2.mirror = true; - setRotation(Shape2, -0.1396263F, 0F, 0F); - - Shape3 = new ModelRenderer(this, 0, 56); - Shape3.addBox(0F, 0F, 0F, 5, 2, 5); - Shape3.setRotationPoint(-2.5F, -11.53333F, -2F); - Shape3.setTextureSize(64, 64); - Shape3.mirror = true; - setRotation(Shape3, -0.2443461F, 0F, 0F); - - Shape4 = new ModelRenderer(this, 24, 48); - Shape4.addBox(0F, 0F, 0F, 4, 2, 4); - Shape4.setRotationPoint(-2F, -13F, -1F); - Shape4.setTextureSize(64, 64); - Shape4.mirror = true; - setRotation(Shape4, -0.4014257F, 0F, 0F); - - Shape5 = new ModelRenderer(this, 24, 54); - Shape5.addBox(0F, 0F, 0F, 3, 2, 3); - Shape5.setRotationPoint(-1.5F, -14F, 0F); - Shape5.setTextureSize(64, 64); - Shape5.mirror = true; - setRotation(Shape5, -0.5759587F, 0F, 0F); - - Shape6 = new ModelRenderer(this, 20, 59); - Shape6.addBox(0F, 0F, 0F, 2, 2, 2); - Shape6.setRotationPoint(-1F, -14F, 0F); - Shape6.setTextureSize(64, 64); - Shape6.mirror = true; - setRotation(Shape6, 0.3316126F, 0F, 0F); - - Shape7 = new ModelRenderer(this, 28, 59); - Shape7.addBox(0F, 0F, 0F, 1, 1, 3); - Shape7.setRotationPoint(-0.5F, -14.5F, 2F); - Shape7.setTextureSize(64, 64); - Shape7.mirror = true; - setRotation(Shape7, -0.5585054F, 0F, 0F); - - // The robe is now the body - bipedBody = new ModelRenderer(this, 40, 42); - bipedBody.addBox(-4F, 0F, -2F, 8, 18, 4, scale); - bipedBody.setRotationPoint(0F, 0F, 0F); - bipedBody.setTextureSize(64, 64); - bipedBody.mirror = true; - setRotation(bipedBody, 0F, 0F, 0F); - - // Makes the hat rotate with the head. - bipedHead.addChild(Shape1); - bipedHead.addChild(Shape2); - bipedHead.addChild(Shape3); - bipedHead.addChild(Shape4); - bipedHead.addChild(Shape5); - bipedHead.addChild(Shape6); - bipedHead.addChild(Shape7); - // Makes the robe move with the body - //bipedBody.addChild(robe); - } - - public void render(Entity entity, float f, float f1, float f2, float f3, float f4, float f5){ - super.render(entity, f, f1, f2, f3, f4, f5); - setRotationAngles(f, f1, f2, f3, f4, f5, entity); - } - - private void setRotation(ModelRenderer model, float x, float y, float z){ - model.rotateAngleX = x; - model.rotateAngleY = y; - model.rotateAngleZ = z; - } - - public void setRotationAngles(float f, float f1, float f2, float f3, float f4, float f5, Entity entity){ - super.setRotationAngles(f, f1, f2, f3, f4, f5, entity); - } + ModelRenderer Shape1; + ModelRenderer Shape2; + ModelRenderer Shape3; + ModelRenderer Shape4; + ModelRenderer Shape5; + ModelRenderer Shape6; + ModelRenderer Shape7; + ModelRenderer robe; + + public ModelWizardArmour(float scale){ + + super(scale, 0, 64, 64); + + // This is necessary to stop the head from scaling. + this.bipedHead = new ModelRenderer(this, 0, 0); + this.bipedHead.addBox(-4.0F, -8.0F, -4.0F, 8, 8, 8, 0.1f); + this.bipedHead.setRotationPoint(0.0F, 0.0F + 0, 0.0F); + + Shape1 = new ModelRenderer(this, -16, 32); + Shape1.addBox(-8F, -7F, -8F, 16, 0, 16); + Shape1.setRotationPoint(0F, 0F, 0F); + Shape1.setTextureSize(64, 64); + Shape1.mirror = true; + setRotation(Shape1, 0F, 0F, 0F); + + Shape2 = new ModelRenderer(this, 0, 48); + Shape2.addBox(0F, 0F, 0F, 6, 2, 6); + Shape2.setRotationPoint(-3F, -10F, -3F); + Shape2.setTextureSize(64, 64); + Shape2.mirror = true; + setRotation(Shape2, -0.1396263F, 0F, 0F); + + Shape3 = new ModelRenderer(this, 0, 56); + Shape3.addBox(0F, 0F, 0F, 5, 2, 5); + Shape3.setRotationPoint(-2.5F, -11.53333F, -2F); + Shape3.setTextureSize(64, 64); + Shape3.mirror = true; + setRotation(Shape3, -0.2443461F, 0F, 0F); + + Shape4 = new ModelRenderer(this, 24, 48); + Shape4.addBox(0F, 0F, 0F, 4, 2, 4); + Shape4.setRotationPoint(-2F, -13F, -1F); + Shape4.setTextureSize(64, 64); + Shape4.mirror = true; + setRotation(Shape4, -0.4014257F, 0F, 0F); + + Shape5 = new ModelRenderer(this, 24, 54); + Shape5.addBox(0F, 0F, 0F, 3, 2, 3); + Shape5.setRotationPoint(-1.5F, -14F, 0F); + Shape5.setTextureSize(64, 64); + Shape5.mirror = true; + setRotation(Shape5, -0.5759587F, 0F, 0F); + + Shape6 = new ModelRenderer(this, 20, 59); + Shape6.addBox(0F, 0F, 0F, 2, 2, 2); + Shape6.setRotationPoint(-1F, -14F, 0F); + Shape6.setTextureSize(64, 64); + Shape6.mirror = true; + setRotation(Shape6, 0.3316126F, 0F, 0F); + + Shape7 = new ModelRenderer(this, 28, 59); + Shape7.addBox(0F, 0F, 0F, 1, 1, 3); + Shape7.setRotationPoint(-0.5F, -14.5F, 2F); + Shape7.setTextureSize(64, 64); + Shape7.mirror = true; + setRotation(Shape7, -0.5585054F, 0F, 0F); + + robe = new ModelRenderer(this, 40, 42); + robe.addBox(-4F, 0F, -2F, 8, 18, 4, scale); + robe.setRotationPoint(0F, 0F, 0F); + robe.setTextureSize(64, 64); + robe.mirror = true; + setRotation(robe, 0F, 0F, 0F); + + // Makes the hat rotate with the head. + bipedHead.addChild(Shape1); + bipedHead.addChild(Shape2); + bipedHead.addChild(Shape3); + bipedHead.addChild(Shape4); + bipedHead.addChild(Shape5); + bipedHead.addChild(Shape6); + bipedHead.addChild(Shape7); + // Makes the robe move with the body + bipedBody.addChild(robe); + } + + public void render(Entity entity, float f, float f1, float f2, float f3, float f4, float f5) + { + super.render(entity, f, f1, f2, f3, f4, f5); + setRotationAngles(f, f1, f2, f3, f4, f5, entity); + } + + private void setRotation(ModelRenderer model, float x, float y, float z) + { + model.rotateAngleX = x; + model.rotateAngleY = y; + model.rotateAngleZ = z; + } + + public void setRotationAngles(float f, float f1, float f2, float f3, float f4, float f5, Entity entity) + { + super.setRotationAngles(f, f1, f2, f3, f4, f5, entity); + } } diff --git a/src/main/java/electroblob/wizardry/client/model/WizardryItemModels.java b/src/main/java/electroblob/wizardry/client/model/WizardryItemModels.java deleted file mode 100644 index a1d678ab..00000000 --- a/src/main/java/electroblob/wizardry/client/model/WizardryItemModels.java +++ /dev/null @@ -1,192 +0,0 @@ -package electroblob.wizardry.client.model; - -import java.util.ArrayList; -import java.util.List; - -import electroblob.wizardry.registry.WizardryBlocks; -import electroblob.wizardry.registry.WizardryItems; -import net.minecraft.client.renderer.block.model.ModelResourceLocation; -import net.minecraft.item.Item; -import net.minecraft.item.ItemStack; -import net.minecraftforge.client.event.ModelRegistryEvent; -import net.minecraftforge.client.model.ModelLoader; -import net.minecraftforge.fml.common.Mod; -import net.minecraftforge.fml.common.eventhandler.SubscribeEvent; -import net.minecraftforge.fml.relauncher.Side; -import net.minecraftforge.fml.relauncher.SideOnly; -import net.minecraftforge.oredict.OreDictionary; - -/** - * Class responsible for registering all of wizardry's item (and itemblock) models. - * @author Electroblob - * @since Wizardry 2.1 - */ -@SideOnly(Side.CLIENT) -@Mod.EventBusSubscriber(Side.CLIENT) -public final class WizardryItemModels { - - @SubscribeEvent - public static void register(ModelRegistryEvent event){ - - // ItemBlocks - - registerItemModel(Item.getItemFromBlock(WizardryBlocks.arcane_workbench)); - registerItemModel(Item.getItemFromBlock(WizardryBlocks.crystal_ore)); - registerItemModel(Item.getItemFromBlock(WizardryBlocks.crystal_flower)); - registerItemModel(Item.getItemFromBlock(WizardryBlocks.transportation_stone)); - registerItemModel(Item.getItemFromBlock(WizardryBlocks.crystal_block)); - - // Items - - registerItemModel(WizardryItems.magic_crystal); - - registerItemModel(WizardryItems.magic_wand); - registerItemModel(WizardryItems.apprentice_wand); - registerItemModel(WizardryItems.advanced_wand); - registerItemModel(WizardryItems.master_wand); - - registerItemModel(WizardryItems.spell_book); - // Wildcard registered for wizard trades. - registerItemModel(WizardryItems.spell_book, OreDictionary.WILDCARD_VALUE, "normal"); - registerItemModel(WizardryItems.arcane_tome); - registerItemModel(WizardryItems.wizard_handbook); - - registerItemModel(WizardryItems.basic_fire_wand); - registerItemModel(WizardryItems.basic_ice_wand); - registerItemModel(WizardryItems.basic_lightning_wand); - registerItemModel(WizardryItems.basic_necromancy_wand); - registerItemModel(WizardryItems.basic_earth_wand); - registerItemModel(WizardryItems.basic_sorcery_wand); - registerItemModel(WizardryItems.basic_healing_wand); - - registerItemModel(WizardryItems.apprentice_fire_wand); - registerItemModel(WizardryItems.apprentice_ice_wand); - registerItemModel(WizardryItems.apprentice_lightning_wand); - registerItemModel(WizardryItems.apprentice_necromancy_wand); - registerItemModel(WizardryItems.apprentice_earth_wand); - registerItemModel(WizardryItems.apprentice_sorcery_wand); - registerItemModel(WizardryItems.apprentice_healing_wand); - - registerItemModel(WizardryItems.advanced_fire_wand); - registerItemModel(WizardryItems.advanced_ice_wand); - registerItemModel(WizardryItems.advanced_lightning_wand); - registerItemModel(WizardryItems.advanced_necromancy_wand); - registerItemModel(WizardryItems.advanced_earth_wand); - registerItemModel(WizardryItems.advanced_sorcery_wand); - registerItemModel(WizardryItems.advanced_healing_wand); - - registerItemModel(WizardryItems.master_fire_wand); - registerItemModel(WizardryItems.master_ice_wand); - registerItemModel(WizardryItems.master_lightning_wand); - registerItemModel(WizardryItems.master_necromancy_wand); - registerItemModel(WizardryItems.master_earth_wand); - registerItemModel(WizardryItems.master_sorcery_wand); - registerItemModel(WizardryItems.master_healing_wand); - - registerItemModel(WizardryItems.spectral_sword); - registerItemModel(WizardryItems.spectral_pickaxe); - registerItemModel(WizardryItems.spectral_bow); - - registerItemModel(WizardryItems.mana_flask); - - registerItemModel(WizardryItems.storage_upgrade); - registerItemModel(WizardryItems.siphon_upgrade); - registerItemModel(WizardryItems.condenser_upgrade); - registerItemModel(WizardryItems.range_upgrade); - registerItemModel(WizardryItems.duration_upgrade); - registerItemModel(WizardryItems.cooldown_upgrade); - registerItemModel(WizardryItems.blast_upgrade); - registerItemModel(WizardryItems.attunement_upgrade); - - registerItemModel(WizardryItems.flaming_axe); - registerItemModel(WizardryItems.frost_axe); - - registerItemModel(WizardryItems.firebomb); - registerItemModel(WizardryItems.poison_bomb); - - registerItemModel(WizardryItems.blank_scroll); - registerItemModel(WizardryItems.scroll); - - registerItemModel(WizardryItems.armour_upgrade); - - registerItemModel(WizardryItems.magic_silk); - - registerItemModel(WizardryItems.wizard_hat); - registerItemModel(WizardryItems.wizard_robe); - registerItemModel(WizardryItems.wizard_leggings); - registerItemModel(WizardryItems.wizard_boots); - - registerItemModel(WizardryItems.wizard_hat_fire); - registerItemModel(WizardryItems.wizard_robe_fire); - registerItemModel(WizardryItems.wizard_leggings_fire); - registerItemModel(WizardryItems.wizard_boots_fire); - - registerItemModel(WizardryItems.wizard_hat_ice); - registerItemModel(WizardryItems.wizard_robe_ice); - registerItemModel(WizardryItems.wizard_leggings_ice); - registerItemModel(WizardryItems.wizard_boots_ice); - - registerItemModel(WizardryItems.wizard_hat_lightning); - registerItemModel(WizardryItems.wizard_robe_lightning); - registerItemModel(WizardryItems.wizard_leggings_lightning); - registerItemModel(WizardryItems.wizard_boots_lightning); - - registerItemModel(WizardryItems.wizard_hat_necromancy); - registerItemModel(WizardryItems.wizard_robe_necromancy); - registerItemModel(WizardryItems.wizard_leggings_necromancy); - registerItemModel(WizardryItems.wizard_boots_necromancy); - - registerItemModel(WizardryItems.wizard_hat_earth); - registerItemModel(WizardryItems.wizard_robe_earth); - registerItemModel(WizardryItems.wizard_leggings_earth); - registerItemModel(WizardryItems.wizard_boots_earth); - - registerItemModel(WizardryItems.wizard_hat_sorcery); - registerItemModel(WizardryItems.wizard_robe_sorcery); - registerItemModel(WizardryItems.wizard_leggings_sorcery); - registerItemModel(WizardryItems.wizard_boots_sorcery); - - registerItemModel(WizardryItems.wizard_hat_healing); - registerItemModel(WizardryItems.wizard_robe_healing); - registerItemModel(WizardryItems.wizard_leggings_healing); - registerItemModel(WizardryItems.wizard_boots_healing); - - registerItemModel(WizardryItems.spectral_helmet); - registerItemModel(WizardryItems.spectral_chestplate); - registerItemModel(WizardryItems.spectral_leggings); - registerItemModel(WizardryItems.spectral_boots); - - registerItemModel(WizardryItems.smoke_bomb); - - registerItemModel(WizardryItems.identification_scroll); - } - - // Moved from the proxies - - /** Registers an item model, using the item's registry name as the model name (this - * convention makes it easier to keep track of everything). Variant defaults to "normal". Registers the model - * for metadata 0 automatically, plus all the other metadata values that the item can take, as defined in - * {@link Item#getSubItems(Item, net.minecraft.creativetab.CreativeTabs, java.util.List)}. The passed in item - * must allow null to be passed in for the creative tab parameter in the aforementioned method, or a - * {@link NullPointerException} will result. */ - private static void registerItemModel(Item item){ - - if(item.getHasSubtypes()){ - List items = new ArrayList(); - item.getSubItems(item, null, items); // Client-only method, but we're client-side so this is OK. - for(ItemStack stack : items){ - ModelLoader.setCustomModelResourceLocation(item, stack.getMetadata(), new ModelResourceLocation(item.getRegistryName(), "inventory")); - } - } - // Changing the last parameter from null to "inventory" fixed the item/block model weirdness. No idea why! - ModelLoader.setCustomModelResourceLocation(item, 0, new ModelResourceLocation(item.getRegistryName(), "inventory")); - } - - /** Registers an item model for the given metadata, using the item's registry name as the model name (this - * convention makes it easier to keep track of everything). This is intended for registering additional metadata - * values which aren't displayed in the creative menu, for example the wildcard spell book used in wizard trades. */ - private static void registerItemModel(Item item, int metadata, String variant) { - ModelLoader.setCustomModelResourceLocation(item, metadata, new ModelResourceLocation(item.getRegistryName(), variant)); - } - -} diff --git a/src/main/java/electroblob/wizardry/client/particle/EntityBlizzardFX.java b/src/main/java/electroblob/wizardry/client/particle/EntityBlizzardFX.java new file mode 100644 index 00000000..6e4b204f --- /dev/null +++ b/src/main/java/electroblob/wizardry/client/particle/EntityBlizzardFX.java @@ -0,0 +1,139 @@ +package electroblob.wizardry.client.particle; + +import cpw.mods.fml.relauncher.Side; +import cpw.mods.fml.relauncher.SideOnly; +import electroblob.wizardry.Wizardry; +import net.minecraft.client.particle.EffectRenderer; +import net.minecraft.client.particle.EntityFX; +import net.minecraft.client.renderer.Tessellator; +import net.minecraft.util.AxisAlignedBB; +import net.minecraft.world.World; + +@SideOnly(Side.CLIENT) +public class EntityBlizzardFX extends EntitySnowFX +{ + /** The name used to identify this particle. Uses the mod id to avoid any possible conflicts (Not that there would + * be any, but I may as well.) */ + public static final String NAME = Wizardry.MODID + "blizzard"; + + private boolean gravity = false; + private double angle; + private double radius; + private double speed; + + public EntityBlizzardFX(World world, float red, float green, float blue, int maxAge, double originX, double originZ, double radius, double yPos) + { + super(world, 0, 0, 0, 0, 0, 0, maxAge); + this.angle = this.rand.nextDouble() * Math.PI * 2; + double x = originX - Math.cos(angle)*radius; + double z = originZ + radius*Math.sin(angle); + this.radius = radius; + this.setPosition(x, yPos, z); + //this.particleScale *= 0.75F; + this.noClip = true; + particleRed = red; + particleGreen = green; + particleBlue = blue; + if(rand.nextBoolean()){ + speed = rand.nextDouble()*2 + 1; + }else{ + speed = rand.nextDouble()*-2 - 1; + } + } + + public EntityBlizzardFX(World world, double x, double y, double z, double vx, double vy, double vz, float red, float green, float blue) + { + super(world, x, y, z, vx, vy, vz); + //this.particleScale *= 0.75F; + this.particleMaxAge = 48 + this.rand.nextInt(12); + this.noClip = true; + particleRed = red; + particleGreen = green; + particleBlue = blue; + } + + public void setColour(int par1) + { + float f = (float)((par1 & 16711680) >> 16) / 255.0F; + float f1 = (float)((par1 & 65280) >> 8) / 255.0F; + float f2 = (float)((par1 & 255) >> 0) / 255.0F; + float f3 = 1.0F; + this.setRBGColorF(f * f3, f1 * f3, f2 * f3); + } + + /** + * returns the bounding box for this entity + */ + public AxisAlignedBB getBoundingBox() + { + return null; + } + + /** + * Returns true if this entity should push and be pushed by other entities when colliding. + */ + public boolean canBePushed() + { + return false; + } + + public void renderParticle(Tessellator par1Tessellator, float par2, float par3, float par4, float par5, float par6, float par7) + { + if (this.particleAge < this.particleMaxAge / 3 || (this.particleAge + this.particleMaxAge) / 3 % 2 == 0) + { + super.renderParticle(par1Tessellator, par2, par3, par4, par5, par6, par7); + } + } + + /** + * Called to update the entity's position/logic. + */ + public void onUpdate() + { + this.prevPosX = this.posX; + this.prevPosY = this.posY; + this.prevPosZ = this.posZ; + + if (this.particleAge++ >= this.particleMaxAge) + { + this.setDead(); + } + + // This is in radians per tick... + double omega = (Math.PI*2)/20 - speed/(20*radius); + + if(speed < 0){ + omega = -(Math.PI*2)/20 + speed/(20*radius); + } + + // v = r times omega; therefore the normalised velocity vector needs to be r times the angle increment / 2 pi. + this.angle += omega; + + this.motionZ = radius * omega * Math.cos(angle); + this.motionX = radius * omega * Math.sin(angle); + this.moveEntity(motionX, motionY, motionZ); + + if(this.gravity){ + this.motionY -= 0.05d; + } + + if (this.particleAge > this.particleMaxAge / 2) + { + this.setAlphaF(1.0F - ((float)this.particleAge - (float)(this.particleMaxAge / 2)) / (float)this.particleMaxAge); + } + + } +/* + @Override + public int getBrightnessForRender(float par1) + { + return 15728880; + } + + @Override + public float getBrightness(float par1) + { + return 1.0F; + } + */ +} diff --git a/src/main/java/electroblob/wizardry/client/particle/ParticleDarkMagic.java b/src/main/java/electroblob/wizardry/client/particle/EntityDarkMagicFX.java similarity index 66% rename from src/main/java/electroblob/wizardry/client/particle/ParticleDarkMagic.java rename to src/main/java/electroblob/wizardry/client/particle/EntityDarkMagicFX.java index 986e3c7c..2052bf72 100644 --- a/src/main/java/electroblob/wizardry/client/particle/ParticleDarkMagic.java +++ b/src/main/java/electroblob/wizardry/client/particle/EntityDarkMagicFX.java @@ -1,19 +1,23 @@ package electroblob.wizardry.client.particle; -import net.minecraft.client.particle.Particle; -import net.minecraft.client.renderer.VertexBuffer; -import net.minecraft.entity.Entity; +import cpw.mods.fml.relauncher.Side; +import cpw.mods.fml.relauncher.SideOnly; +import electroblob.wizardry.Wizardry; +import net.minecraft.client.particle.EntityFX; +import net.minecraft.client.renderer.Tessellator; import net.minecraft.world.World; -import net.minecraftforge.fml.relauncher.Side; -import net.minecraftforge.fml.relauncher.SideOnly; @SideOnly(Side.CLIENT) -public class ParticleDarkMagic extends Particle { +public class EntityDarkMagicFX extends EntityFX +{ + /** The name used to identify this particle. Uses the mod id to avoid any possible conflicts (Not that there would + * be any, but I may as well.) */ + public static final String NAME = Wizardry.MODID + "darkmagic"; /** Base spell texture index */ private int baseSpellTextureIndex = 128; - public ParticleDarkMagic(World par1World, double par2, double par4, double par6, double par8, double par10, double par12, float r, float g, float b) + public EntityDarkMagicFX(World par1World, double par2, double par4, double par6, double par8, double par10, double par12, float r, float g, float b) { super(par1World, par2, par4, par6, par8, par10, par12); this.motionY *= 0.20000000298023224D; @@ -24,13 +28,12 @@ public class ParticleDarkMagic extends Particle { this.particleScale *= 0.75F; this.particleMaxAge = (int)(8.0D / (Math.random() * 0.8D + 0.2D)); - this.canCollide = true; + this.noClip = true; } - @Override - public void renderParticle(VertexBuffer buffer, Entity entity, float partialTicks, float rotationX, float rotationZ, float rotationYZ, float rotationXY, float rotationXZ) + public void renderParticle(Tessellator par1Tessellator, float par2, float par3, float par4, float par5, float par6, float par7) { - float f6 = ((float)this.particleAge + partialTicks) / (float)this.particleMaxAge * 32.0F; + float f6 = ((float)this.particleAge + par2) / (float)this.particleMaxAge * 32.0F; if (f6 < 0.0F) { @@ -42,7 +45,7 @@ public class ParticleDarkMagic extends Particle { f6 = 1.0F; } - super.renderParticle(buffer, entity, partialTicks, rotationX, rotationZ, rotationYZ, rotationXY, rotationXZ); + super.renderParticle(par1Tessellator, par2, par3, par4, par5, par6, par7); } /** @@ -56,7 +59,7 @@ public class ParticleDarkMagic extends Particle { if (this.particleAge++ >= this.particleMaxAge) { - this.setExpired(); + this.setDead(); } this.setParticleTextureIndex(this.baseSpellTextureIndex + (7 - this.particleAge * 8 / this.particleMaxAge)); @@ -73,7 +76,7 @@ public class ParticleDarkMagic extends Particle { this.motionY *= 0.9599999785423279D; this.motionZ *= 0.9599999785423279D; - if (this.isCollided) + if (this.onGround) { this.motionX *= 0.699999988079071D; this.motionZ *= 0.699999988079071D; diff --git a/src/main/java/electroblob/wizardry/client/particle/ParticleDust.java b/src/main/java/electroblob/wizardry/client/particle/EntityDustFX.java similarity index 64% rename from src/main/java/electroblob/wizardry/client/particle/ParticleDust.java rename to src/main/java/electroblob/wizardry/client/particle/EntityDustFX.java index 6a121fa3..77e35ebc 100644 --- a/src/main/java/electroblob/wizardry/client/particle/ParticleDust.java +++ b/src/main/java/electroblob/wizardry/client/particle/EntityDustFX.java @@ -1,16 +1,27 @@ package electroblob.wizardry.client.particle; -import net.minecraft.client.particle.Particle; +import org.lwjgl.opengl.GL11; + +import cpw.mods.fml.relauncher.Side; +import cpw.mods.fml.relauncher.SideOnly; +import electroblob.wizardry.Wizardry; +import net.minecraft.block.material.Material; +import net.minecraft.client.particle.EntityFX; +import net.minecraft.client.renderer.OpenGlHelper; +import net.minecraft.client.renderer.Tessellator; +import net.minecraft.util.MathHelper; import net.minecraft.world.World; -import net.minecraftforge.fml.relauncher.Side; -import net.minecraftforge.fml.relauncher.SideOnly; @SideOnly(Side.CLIENT) -public class ParticleDust extends Particle { +public class EntityDustFX extends EntityFX +{ + /** The name used to identify this particle. Uses the mod id to avoid any possible conflicts (Not that there would + * be any, but I may as well.) */ + public static final String NAME = Wizardry.MODID + "dust"; private final boolean shaded; - public ParticleDust(World par1World, double x, double y, double z, double par8, double par10, double par12, float r, float g, float b, boolean shaded) + public EntityDustFX(World par1World, double x, double y, double z, double par8, double par10, double par12, float r, float g, float b, boolean shaded) { super(par1World, x, y, z, par8, par10, par12); this.particleRed = r; @@ -38,7 +49,7 @@ public class ParticleDust extends Particle { if (this.particleMaxAge-- <= 0) { - this.setExpired(); + this.setDead(); } } @@ -47,11 +58,10 @@ public class ParticleDust extends Particle { { return shaded ? super.getBrightnessForRender(par1) : 15728880; } - /* + @Override public float getBrightness(float par1) { return shaded ? super.getBrightness(par1) : 1.0F; } - */ } diff --git a/src/main/java/electroblob/wizardry/client/particle/EntityIceFX.java b/src/main/java/electroblob/wizardry/client/particle/EntityIceFX.java new file mode 100644 index 00000000..88039816 --- /dev/null +++ b/src/main/java/electroblob/wizardry/client/particle/EntityIceFX.java @@ -0,0 +1,199 @@ +package electroblob.wizardry.client.particle; + +import java.lang.reflect.Field; + +import org.lwjgl.opengl.GL11; + +import cpw.mods.fml.relauncher.Side; +import cpw.mods.fml.relauncher.SideOnly; +import electroblob.wizardry.Wizardry; +import net.minecraft.client.Minecraft; +import net.minecraft.client.particle.EntityFX; +import net.minecraft.client.renderer.OpenGlHelper; +import net.minecraft.client.renderer.RenderHelper; +import net.minecraft.client.renderer.Tessellator; +import net.minecraft.entity.Entity; +import net.minecraft.util.ResourceLocation; +import net.minecraft.world.World; + +@SideOnly(Side.CLIENT) +public class EntityIceFX extends EntityFX +{ + /** The name used to identify this particle. Uses the mod id to avoid any possible conflicts (Not that there would + * be any, but I may as well.) */ + public static final String NAME = Wizardry.MODID + "ice"; + + private static final ResourceLocation iceTextures = new ResourceLocation("wizardry:textures/particle/ice_particles.png"); + + public EntityIceFX(World world, double x, double y, double z, double vx, double vy, double vz) + { + super(world, x, y, z, vx, vy, vz); + this.particleRed = 1.0F; + this.particleGreen = 1.0F; + this.particleBlue = 1.0F; + this.setParticleTextureIndex(rand.nextInt(8)); + this.setSize(0.02F, 0.02F); + this.particleScale *= 0.75f; + this.motionX = vx; + this.motionY = vy; + this.motionZ = vz; + this.particleMaxAge = 40 + rand.nextInt(10); + this.noClip = true; + } + + public EntityIceFX(World par1World, double par2, double par4, double par6, double par8, double par10, double par12, int maxAge){ + this(par1World, par2, par4, par6, par8, par10, par12); + this.particleMaxAge = maxAge; + this.noClip = true; + } + + /** + * Called to update the entity's position/logic. + */ + public void onUpdate(){ + + this.prevPosX = this.posX; + this.prevPosY = this.posY; + this.prevPosZ = this.posZ; + + if (this.particleAge++ >= this.particleMaxAge) + { + this.setDead(); + } + + this.moveEntity(this.motionX, this.motionY, this.motionZ); + this.motionX *= 0.9800000190734863D; + this.motionY *= 0.9800000190734863D; + this.motionZ *= 0.9800000190734863D; + // Gravity + this.motionY -= 0.05d; + } + + /* There are 4 layers of particles, specified as 0-3 by the method below. + * - Layer 0 causes the normal particles.png to be bound to the render engine for normal particles. + * - Layer 1 causes the block textures to be bound to the render engine for digging fx and falling fx. + * - Layer 2 causes the item textures to be bound to the render engine for tool breaking fx, snowballpoofs, slime particles, etc. + * - Layer 3 is not used in vanilla minecraft and was presumably added by forge for exactly this reason. + * This means no texture is bound by vanilla minecraft, meaning you are free to do as you wish without possibly + * overwriting vanilla particles. Mod particles won't be overwritten anyway since they bind their own textures. + * It is of course important to bind the texture every time you render a custom particle, but I don't see how + * you could do it any other way, since you don't have access to EffectRenderer. + */ + @Override + public int getFXLayer() { + // This can only be 0-3 or it will cause an ArrayIndexOutOfBoundsException in EffectRenderer. + return 3; + } + + @Override + public void setParticleTextureIndex(int par1) + { + this.particleTextureIndexX = par1 % 4; + this.particleTextureIndexY = par1 / 4; + } + + // Overwitten from entityfx to bind the new texture. + @Override + public void renderParticle(Tessellator tessellator, float partialTicks, float par3, float par4, float par5, float par6, float par7) + { + // Resets the tessellator and binds the new texture. Used to check if tessellator is drawing, but that field + // is no longer visible. If this results in an IllegalStateException (tessellator not drawing), look into this. + // Perhaps I could use reflection (ooooooh) to access it? Like this: + + /* + Field f = null; + boolean isDrawing = false; + + try { + f = tessellator.getClass().getDeclaredField("isDrawing"); + } catch (NoSuchFieldException e) { + e.printStackTrace(); + } catch (SecurityException e) { + e.printStackTrace(); + } + + if(f != null){ + + f.setAccessible(true); + + try { + isDrawing = (Boolean)f.get(tessellator); + } catch (IllegalArgumentException e) { + e.printStackTrace(); + } catch (IllegalAccessException e) { + e.printStackTrace(); + } + } + + if(isDrawing)*/ + //tessellator.draw(); + + GL11.glPushMatrix(); + + // This stuff does the shading. The parameter does nothing apparently. + int j = this.getBrightnessForRender(0); + int k = j % 65536; + int l = j / 65536; + OpenGlHelper.setLightmapTextureCoords(OpenGlHelper.lightmapTexUnit, (float)k / 1.0F, (float)l / 1.0F); + + RenderHelper.disableStandardItemLighting(); + + Minecraft.getMinecraft().renderEngine.bindTexture(iceTextures); + + tessellator.startDrawingQuads(); + + float f6 = (float)this.particleTextureIndexX / 4.0F; + float f7 = f6 + 0.0624375F*4; + float f8 = (float)this.particleTextureIndexY / 4.0F; + float f9 = f8 + 0.0624375F*4; + float f10 = 0.1F * this.particleScale; + + if (this.particleIcon != null) + { + f6 = this.particleIcon.getMinU(); + f7 = this.particleIcon.getMaxU(); + f8 = this.particleIcon.getMinV(); + f9 = this.particleIcon.getMaxV(); + } + + // Fix which gets proper values for interpPos so the particles don't appear to move. + Entity player = Minecraft.getMinecraft().thePlayer; + this.interpPosX = player.lastTickPosX + (player.posX - player.lastTickPosX) * (double)partialTicks; + this.interpPosY = player.lastTickPosY + (player.posY - player.lastTickPosY) * (double)partialTicks; + this.interpPosZ = player.lastTickPosZ + (player.posZ - player.lastTickPosZ) * (double)partialTicks; + + float f11 = (float)(this.prevPosX + (this.posX - this.prevPosX) * (double)partialTicks - interpPosX); + float f12 = (float)(this.prevPosY + (this.posY - this.prevPosY) * (double)partialTicks - interpPosY); + float f13 = (float)(this.prevPosZ + (this.posZ - this.prevPosZ) * (double)partialTicks - interpPosZ); + float f14 = 1.0F; + + tessellator.setColorRGBA_F(this.particleRed * f14, this.particleGreen * f14, this.particleBlue * f14, this.particleAlpha); + tessellator.addVertexWithUV((double)(f11 - par3 * f10 - par6 * f10), (double)(f12 - par4 * f10), (double)(f13 - par5 * f10 - par7 * f10), (double)f7, (double)f9); + tessellator.addVertexWithUV((double)(f11 - par3 * f10 + par6 * f10), (double)(f12 + par4 * f10), (double)(f13 - par5 * f10 + par7 * f10), (double)f7, (double)f8); + tessellator.addVertexWithUV((double)(f11 + par3 * f10 + par6 * f10), (double)(f12 + par4 * f10), (double)(f13 + par5 * f10 + par7 * f10), (double)f6, (double)f8); + tessellator.addVertexWithUV((double)(f11 + par3 * f10 - par6 * f10), (double)(f12 - par4 * f10), (double)(f13 + par5 * f10 - par7 * f10), (double)f6, (double)f9); + + tessellator.draw(); + + GL11.glPopMatrix(); + + // Re-binds the normal particles so that the mod texture isn't on all of them + //Minecraft.getMinecraft().renderEngine.bindTexture(new ResourceLocation("textures/particle/particles.png")); + + //tessellator.startDrawingQuads(); + + } + + public int getBrightnessForRender(float par1) + { + return 15728880; + } + + /** + * Gets how bright this entity is. + */ + public float getBrightness(float par1) + { + return 1.0F; + } +} diff --git a/src/main/java/electroblob/wizardry/client/particle/EntityLeafFX.java b/src/main/java/electroblob/wizardry/client/particle/EntityLeafFX.java new file mode 100644 index 00000000..d565c241 --- /dev/null +++ b/src/main/java/electroblob/wizardry/client/particle/EntityLeafFX.java @@ -0,0 +1,180 @@ +package electroblob.wizardry.client.particle; + +import java.lang.reflect.Field; + +import org.lwjgl.opengl.GL11; + +import cpw.mods.fml.relauncher.Side; +import cpw.mods.fml.relauncher.SideOnly; +import electroblob.wizardry.Wizardry; +import net.minecraft.client.Minecraft; +import net.minecraft.client.particle.EntityFX; +import net.minecraft.client.renderer.OpenGlHelper; +import net.minecraft.client.renderer.RenderHelper; +import net.minecraft.client.renderer.Tessellator; +import net.minecraft.entity.Entity; +import net.minecraft.util.ResourceLocation; +import net.minecraft.world.World; + +@SideOnly(Side.CLIENT) +public class EntityLeafFX extends EntityFX +{ + /** The name used to identify this particle. Uses the mod id to avoid any possible conflicts (Not that there would + * be any, but I may as well.) */ + public static final String NAME = Wizardry.MODID + "leaf"; + + private static final ResourceLocation leafTextures = new ResourceLocation("wizardry:textures/particle/leaf_particles.png"); + + public EntityLeafFX(World par1World, double par2, double par4, double par6, double par8, double par10, double par12) + { + super(par1World, par2, par4, par6, par8, par10, par12); + this.particleRed = 1.0F; + this.particleGreen = 1.0F; + this.particleBlue = 1.0F; + this.setParticleTextureIndex(rand.nextInt(16)); + this.setSize(0.02F, 0.02F); + this.particleScale *= 1.4f; + this.motionX = par8; + this.motionY = par10; + this.motionZ = par12; + this.particleMaxAge = 40 + rand.nextInt(10); + } + + public EntityLeafFX(World par1World, double par2, double par4, double par6, double par8, double par10, double par12, int maxAge){ + this(par1World, par2, par4, par6, par8, par10, par12); + this.particleMaxAge = maxAge; + } + + /** + * Called to update the entity's position/logic. + */ + public void onUpdate() + { + this.prevPosX = this.posX; + this.prevPosY = this.posY; + this.prevPosZ = this.posZ; + this.moveEntity(this.motionX, this.motionY, this.motionZ); + + if (this.particleMaxAge-- <= 0) + { + this.setDead(); + } + } + + /* There are 4 layers of particles, specified as 0-3 by the method below. + * - Layer 0 causes the normal particles.png to be bound to the render engine for normal particles. + * - Layer 1 causes the block textures to be bound to the render engine for digging fx and falling fx. + * - Layer 2 causes the item textures to be bound to the render engine for tool breaking fx, snowballpoofs, slime particles, etc. + * - Layer 3 is not used in vanilla minecraft and was presumably added by forge for exactly this reason. + * This means no texture is bound by vanilla minecraft, meaning you are free to do as you wish without possibly + * overwriting vanilla particles. Mod particles won't be overwritten anyway since they bind their own textures. + * It is of course important to bind the texture every time you render a custom particle, but I don't see how + * you could do it any other way, since you don't have access to EffectRenderer. + */ + @Override + public int getFXLayer() { + // This can only be 0-3 or it will cause an ArrayIndexOutOfBoundsException in EffectRenderer. + return 3; + } + + /** + * Public method to set private field particleTextureIndex. + */ + public void setParticleTextureIndex(int par1) + { + this.particleTextureIndexX = par1 % 4; + this.particleTextureIndexY = par1 / 4; + } + + // Overwitten from entityfx to bind the new texture. + @Override + public void renderParticle(Tessellator tessellator, float partialTicks, float par3, float par4, float par5, float par6, float par7) + { + // Resets the tessellator and binds the new texture. Used to check if tessellator is drawing, but that field + // is no longer visible. If this results in an IllegalStateException (tessellator not drawing), look into this. + // Perhaps I could use reflection (ooooooh) to access it? Like this: + + /* + Field f = null; + boolean isDrawing = false; + + try { + f = tessellator.getClass().getDeclaredField("isDrawing"); + } catch (NoSuchFieldException e) { + e.printStackTrace(); + } catch (SecurityException e) { + e.printStackTrace(); + } + + if(f != null){ + + f.setAccessible(true); + + try { + isDrawing = (Boolean)f.get(tessellator); + } catch (IllegalArgumentException e) { + e.printStackTrace(); + } catch (IllegalAccessException e) { + e.printStackTrace(); + } + } + + if(isDrawing)*/ + //tessellator.draw(); + + GL11.glPushMatrix(); + + // This stuff does the shading. The parameter does nothing apparently. + int j = this.getBrightnessForRender(0); + int k = j % 65536; + int l = j / 65536; + OpenGlHelper.setLightmapTextureCoords(OpenGlHelper.lightmapTexUnit, (float)k / 1.0F, (float)l / 1.0F); + + RenderHelper.disableStandardItemLighting(); + + Minecraft.getMinecraft().renderEngine.bindTexture(leafTextures); + + tessellator.startDrawingQuads(); + + float f6 = (float)this.particleTextureIndexX / 4.0F; + float f7 = f6 + 0.0624375F*4; + float f8 = (float)this.particleTextureIndexY / 4.0F; + float f9 = f8 + 0.0624375F*4; + float f10 = 0.1F * this.particleScale; + + if (this.particleIcon != null) + { + f6 = this.particleIcon.getMinU(); + f7 = this.particleIcon.getMaxU(); + f8 = this.particleIcon.getMinV(); + f9 = this.particleIcon.getMaxV(); + } + + // Fix which gets proper values for interpPos so the particles don't appear to move. + Entity player = Minecraft.getMinecraft().thePlayer; + this.interpPosX = player.lastTickPosX + (player.posX - player.lastTickPosX) * (double)partialTicks; + this.interpPosY = player.lastTickPosY + (player.posY - player.lastTickPosY) * (double)partialTicks; + this.interpPosZ = player.lastTickPosZ + (player.posZ - player.lastTickPosZ) * (double)partialTicks; + + float f11 = (float)(this.prevPosX + (this.posX - this.prevPosX) * (double)partialTicks - interpPosX); + float f12 = (float)(this.prevPosY + (this.posY - this.prevPosY) * (double)partialTicks - interpPosY); + float f13 = (float)(this.prevPosZ + (this.posZ - this.prevPosZ) * (double)partialTicks - interpPosZ); + float f14 = 1.0F; + + tessellator.setColorRGBA_F(this.particleRed * f14, this.particleGreen * f14, this.particleBlue * f14, this.particleAlpha); + tessellator.addVertexWithUV((double)(f11 - par3 * f10 - par6 * f10), (double)(f12 - par4 * f10), (double)(f13 - par5 * f10 - par7 * f10), (double)f7, (double)f9); + tessellator.addVertexWithUV((double)(f11 - par3 * f10 + par6 * f10), (double)(f12 + par4 * f10), (double)(f13 - par5 * f10 + par7 * f10), (double)f7, (double)f8); + tessellator.addVertexWithUV((double)(f11 + par3 * f10 + par6 * f10), (double)(f12 + par4 * f10), (double)(f13 + par5 * f10 + par7 * f10), (double)f6, (double)f8); + tessellator.addVertexWithUV((double)(f11 + par3 * f10 - par6 * f10), (double)(f12 - par4 * f10), (double)(f13 + par5 * f10 - par7 * f10), (double)f6, (double)f9); + + tessellator.draw(); + + GL11.glPopMatrix(); + + // Re-binds the normal particles so that the mod texture isn't on all of them + //Minecraft.getMinecraft().renderEngine.bindTexture(new ResourceLocation("textures/particle/particles.png")); + + //tessellator.startDrawingQuads(); + + } +} diff --git a/src/main/java/electroblob/wizardry/client/particle/ParticleGiantBubble.java b/src/main/java/electroblob/wizardry/client/particle/EntityMagicBubbleFX.java similarity index 82% rename from src/main/java/electroblob/wizardry/client/particle/ParticleGiantBubble.java rename to src/main/java/electroblob/wizardry/client/particle/EntityMagicBubbleFX.java index b469bcb2..48780022 100644 --- a/src/main/java/electroblob/wizardry/client/particle/ParticleGiantBubble.java +++ b/src/main/java/electroblob/wizardry/client/particle/EntityMagicBubbleFX.java @@ -1,19 +1,21 @@ package electroblob.wizardry.client.particle; +import cpw.mods.fml.relauncher.Side; +import cpw.mods.fml.relauncher.SideOnly; import electroblob.wizardry.Wizardry; -import net.minecraft.client.particle.Particle; +import net.minecraft.block.material.Material; +import net.minecraft.client.particle.EntityFX; +import net.minecraft.util.MathHelper; import net.minecraft.world.World; -import net.minecraftforge.fml.relauncher.Side; -import net.minecraftforge.fml.relauncher.SideOnly; @SideOnly(Side.CLIENT) -public class ParticleGiantBubble extends Particle +public class EntityMagicBubbleFX extends EntityFX { /** The name used to identify this particle. Uses the mod id to avoid any possible conflicts (Not that there would * be any, but I may as well.) */ public static final String NAME = Wizardry.MODID + "magicbubble"; - public ParticleGiantBubble(World par1World, double par2, double par4, double par6, double par8, double par10, double par12) + public EntityMagicBubbleFX(World par1World, double par2, double par4, double par6, double par8, double par10, double par12) { super(par1World, par2, par4, par6, par8, par10, par12); this.particleRed = 1.0F; @@ -44,7 +46,7 @@ public class ParticleGiantBubble extends Particle if (this.particleMaxAge-- <= 0) { - this.setExpired(); + this.setDead(); } } } diff --git a/src/main/java/electroblob/wizardry/client/particle/EntityMagicFireFX.java b/src/main/java/electroblob/wizardry/client/particle/EntityMagicFireFX.java new file mode 100644 index 00000000..d15ed7ec --- /dev/null +++ b/src/main/java/electroblob/wizardry/client/particle/EntityMagicFireFX.java @@ -0,0 +1,108 @@ +package electroblob.wizardry.client.particle; + +import cpw.mods.fml.relauncher.Side; +import cpw.mods.fml.relauncher.SideOnly; +import electroblob.wizardry.Wizardry; +import net.minecraft.client.particle.EntityFX; +import net.minecraft.client.renderer.Tessellator; +import net.minecraft.world.World; + +@SideOnly(Side.CLIENT) +public class EntityMagicFireFX extends EntityFX +{ + /** The name used to identify this particle. Uses the mod id to avoid any possible conflicts (Not that there would + * be any, but I may as well.) */ + public static final String NAME = Wizardry.MODID + "magicfire"; + + /** the scale of the flame FX */ + private float flameScale; + + public EntityMagicFireFX(World par1World, double par2, double par4, double par6, double par8, double par10, double par12, int maxAge, float scale) + { + super(par1World, par2, par4, par6, par8, par10, par12); + this.motionX = this.motionX * 0.009999999776482582D + par8; + this.motionY = this.motionY * 0.009999999776482582D + par10; + this.motionZ = this.motionZ * 0.009999999776482582D + par12; + double d6 = par2 + (double)((this.rand.nextFloat() - this.rand.nextFloat()) * 0.05F); + d6 = par4 + (double)((this.rand.nextFloat() - this.rand.nextFloat()) * 0.05F); + d6 = par6 + (double)((this.rand.nextFloat() - this.rand.nextFloat()) * 0.05F); + this.flameScale = scale; + this.particleRed = this.particleGreen = this.particleBlue = 1.0F; + if(maxAge == 0){ + this.particleMaxAge = (int)(2.0D / (Math.random() * 0.8D + 0.2D)); + }else{ + this.particleMaxAge = maxAge; + } + this.noClip = true; + this.setParticleTextureIndex(48); + } + + public void renderParticle(Tessellator par1Tessellator, float par2, float par3, float par4, float par5, float par6, float par7) + { + float f6 = ((float)this.particleAge + par2) / (float)this.particleMaxAge; + this.particleScale = this.flameScale * (1.0F - f6 * f6 * 0.5F); + super.renderParticle(par1Tessellator, par2, par3, par4, par5, par6, par7); + } + + public int getBrightnessForRender(float par1) + { + float f1 = ((float)this.particleAge + par1) / (float)this.particleMaxAge; + + if (f1 < 0.0F) + { + f1 = 0.0F; + } + + if (f1 > 1.0F) + { + f1 = 1.0F; + } + + int i = super.getBrightnessForRender(par1); + int j = i & 255; + int k = i >> 16 & 255; + j += (int)(f1 * 15.0F * 16.0F); + + if (j > 240) + { + j = 240; + } + + //return j | k << 16; + return 256; + } + + /** + * Gets how bright this entity is. + */ + public float getBrightness(float par1) + { + return 1.0f; + } + + /** + * Called to update the entity's position/logic. + */ + public void onUpdate() + { + this.prevPosX = this.posX; + this.prevPosY = this.posY; + this.prevPosZ = this.posZ; + + if (this.particleAge++ >= this.particleMaxAge) + { + this.setDead(); + } + + this.moveEntity(this.motionX, this.motionY, this.motionZ); + this.motionX *= 0.9599999785423279D; + this.motionY *= 0.9599999785423279D; + this.motionZ *= 0.9599999785423279D; + + if (this.onGround) + { + this.motionX *= 0.699999988079071D; + this.motionZ *= 0.699999988079071D; + } + } +} diff --git a/src/main/java/electroblob/wizardry/client/particle/EntityPathFX.java b/src/main/java/electroblob/wizardry/client/particle/EntityPathFX.java new file mode 100644 index 00000000..8111c43a --- /dev/null +++ b/src/main/java/electroblob/wizardry/client/particle/EntityPathFX.java @@ -0,0 +1,181 @@ +package electroblob.wizardry.client.particle; + +import org.lwjgl.opengl.GL11; + +import cpw.mods.fml.relauncher.Side; +import cpw.mods.fml.relauncher.SideOnly; +import electroblob.wizardry.Wizardry; +import electroblob.wizardry.spell.Clairvoyance; +import net.minecraft.client.Minecraft; +import net.minecraft.client.particle.EntityFX; +import net.minecraft.client.renderer.OpenGlHelper; +import net.minecraft.client.renderer.RenderHelper; +import net.minecraft.client.renderer.Tessellator; +import net.minecraft.entity.Entity; +import net.minecraft.util.AxisAlignedBB; +import net.minecraft.util.ResourceLocation; +import net.minecraft.world.World; + +@SideOnly(Side.CLIENT) +public class EntityPathFX extends EntityFX +{ + /** The name used to identify this particle, without gravity. Uses the mod id to avoid any possible conflicts (Not that there would + * be any, but I may as well.) */ + public static final String NAME = Wizardry.MODID + "path"; + + private static final ResourceLocation pathTextures = new ResourceLocation("wizardry:textures/particle/path_particles.png"); + + private final double originX, originY, originZ; + + public EntityPathFX(World par1World, double par2, double par4, double par6, double par8, double par10, double par12, float red, float green, float blue, int maxAge) + { + super(par1World, par2, par4, par6); + this.motionX = par8; + this.motionY = par10; + this.motionZ = par12; + // Set to a constant to remove the randomness from EntityFX. + this.particleScale = 1.25F; + this.particleMaxAge = maxAge; + this.noClip = true; + particleRed = red; + particleGreen = green; + particleBlue = blue; + this.originX = par2; + this.originY = par4; + this.originZ = par6; + } + + public EntityPathFX(World par1World, double par2, double par4, double par6, double par8, double par10, double par12, float red, float green, float blue) + { + super(par1World, par2, par4, par6); + this.motionX = par8; + this.motionY = par10; + this.motionZ = par12; + // Set to a constant to remove the randomness from EntityFX. + this.particleScale = 1.25F; + this.particleMaxAge = 48 + this.rand.nextInt(12); + this.noClip = true; + particleRed = red; + particleGreen = green; + particleBlue = blue; + this.originX = par2; + this.originY = par4; + this.originZ = par6; + } + + /** + * returns the bounding box for this entity + */ + public AxisAlignedBB getBoundingBox() + { + return null; + } + + /** + * Returns true if this entity should push and be pushed by other entities when colliding. + */ + public boolean canBePushed() + { + return false; + } + + /** + * Called to update the entity's position/logic. + */ + public void onUpdate() + { + this.prevPosX = this.posX; + this.prevPosY = this.posY; + this.prevPosZ = this.posZ; + + if (this.particleAge++ >= this.particleMaxAge) + { + this.setDead(); + } + + this.moveEntity(this.motionX, this.motionY, this.motionZ); + + if (this.particleAge > this.particleMaxAge / 2) + { + this.setAlphaF(1.0F - 2 * (((float)this.particleAge - (float)(this.particleMaxAge / 2)) / (float)this.particleMaxAge)); + } + + if(this.particleAge % Clairvoyance.PARTICLE_MOVEMENT_INTERVAL == 0){ + this.setPosition(this.originX, this.originY, this.originZ); + this.prevPosX = this.posX; + this.prevPosY = this.posY; + this.prevPosZ = this.posZ; + } + + } + + /* There are 4 layers of particles, specified as 0-3 by the method below. + * - Layer 0 causes the normal particles.png to be bound to the render engine for normal particles. + * - Layer 1 causes the block textures to be bound to the render engine for digging fx and falling fx. + * - Layer 2 causes the item textures to be bound to the render engine for tool breaking fx, snowballpoofs, slime particles, etc. + * - Layer 3 is not used in vanilla minecraft and was presumably added by forge for exactly this reason. + * This means no texture is bound by vanilla minecraft, meaning you are free to do as you wish without possibly + * overwriting vanilla particles. Mod particles won't be overwritten anyway since they bind their own textures. + * It is of course important to bind the texture every time you render a custom particle, but I don't see how + * you could do it any other way, since you don't have access to EffectRenderer. + */ + @Override + public int getFXLayer() { + // This can only be 0-3 or it will cause an ArrayIndexOutOfBoundsException in EffectRenderer. + return 3; + } + + @Override + public void renderParticle(Tessellator tessellator, float partialTicks, float par3, float par4, float par5, float par6, float par7){ + + GL11.glPushMatrix(); + GL11.glDisable(GL11.GL_LIGHTING); + GL11.glEnable(GL11.GL_BLEND); + GL11.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA); + OpenGlHelper.setLightmapTextureCoords(OpenGlHelper.lightmapTexUnit, 240, 240); + + RenderHelper.disableStandardItemLighting(); + + Minecraft.getMinecraft().renderEngine.bindTexture(pathTextures); + + tessellator.startDrawingQuads(); + + float f10 = 0.1F * this.particleScale; + + // Fix which gets proper values for interpPos so the particles don't appear to move. + Entity player = Minecraft.getMinecraft().thePlayer; + this.interpPosX = player.lastTickPosX + (player.posX - player.lastTickPosX) * (double)partialTicks; + this.interpPosY = player.lastTickPosY + (player.posY - player.lastTickPosY) * (double)partialTicks; + this.interpPosZ = player.lastTickPosZ + (player.posZ - player.lastTickPosZ) * (double)partialTicks; + + float f11 = (float)(this.prevPosX + (this.posX - this.prevPosX) * (double)partialTicks - interpPosX); + float f12 = (float)(this.prevPosY + (this.posY - this.prevPosY) * (double)partialTicks - interpPosY); + float f13 = (float)(this.prevPosZ + (this.posZ - this.prevPosZ) * (double)partialTicks - interpPosZ); + float f14 = 1.0F; + + tessellator.setColorRGBA_F(this.particleRed * f14, this.particleGreen * f14, this.particleBlue * f14, this.particleAlpha); + tessellator.addVertexWithUV((double)(f11 - par3 * f10 - par6 * f10), (double)(f12 - par4 * f10), (double)(f13 - par5 * f10 - par7 * f10), 1, 1); + tessellator.addVertexWithUV((double)(f11 - par3 * f10 + par6 * f10), (double)(f12 + par4 * f10), (double)(f13 - par5 * f10 + par7 * f10), 1, 0); + tessellator.addVertexWithUV((double)(f11 + par3 * f10 + par6 * f10), (double)(f12 + par4 * f10), (double)(f13 + par5 * f10 + par7 * f10), 0, 0); + tessellator.addVertexWithUV((double)(f11 + par3 * f10 - par6 * f10), (double)(f12 - par4 * f10), (double)(f13 + par5 * f10 - par7 * f10), 0, 1); + + tessellator.draw(); + + GL11.glEnable(GL11.GL_LIGHTING); + GL11.glDisable(GL11.GL_BLEND); + GL11.glPopMatrix(); + + } + + @Override + public int getBrightnessForRender(float par1) + { + return 15728880; + } + + @Override + public float getBrightness(float par1) + { + return 1.0F; + } +} diff --git a/src/main/java/electroblob/wizardry/client/particle/EntitySnowFX.java b/src/main/java/electroblob/wizardry/client/particle/EntitySnowFX.java new file mode 100644 index 00000000..e8ad6365 --- /dev/null +++ b/src/main/java/electroblob/wizardry/client/particle/EntitySnowFX.java @@ -0,0 +1,131 @@ +package electroblob.wizardry.client.particle; + +import org.lwjgl.opengl.GL11; + +import cpw.mods.fml.relauncher.Side; +import cpw.mods.fml.relauncher.SideOnly; +import electroblob.wizardry.Wizardry; +import net.minecraft.client.Minecraft; +import net.minecraft.client.particle.EntityFX; +import net.minecraft.client.renderer.OpenGlHelper; +import net.minecraft.client.renderer.RenderHelper; +import net.minecraft.client.renderer.Tessellator; +import net.minecraft.entity.Entity; +import net.minecraft.util.ResourceLocation; +import net.minecraft.world.World; + +@SideOnly(Side.CLIENT) +public class EntitySnowFX extends EntityFX +{ + /** The name used to identify this particle. Uses the mod id to avoid any possible conflicts (Not that there would + * be any, but I may as well.) */ + public static final String NAME = Wizardry.MODID + "snow"; + + private static final ResourceLocation snowTextures = new ResourceLocation("wizardry:textures/particle/snow_particles.png"); + + public EntitySnowFX(World world, double x, double y, double z, double velX, double velY, double velZ) + { + super(world, x, y, z, velX, velY, velZ); + this.particleRed = 1.0F; + this.particleGreen = 1.0F; + this.particleBlue = 1.0F; + this.setParticleTextureIndex(rand.nextInt(8)); + this.setSize(0.02F, 0.02F); + this.particleScale *= 0.6f; + this.motionX = velX; + this.motionY = velY; + this.motionZ = velZ; + this.particleMaxAge = 40 + rand.nextInt(10); + } + + public EntitySnowFX(World par1World, double par2, double par4, double par6, double par8, double par10, double par12, int maxAge){ + this(par1World, par2, par4, par6, par8, par10, par12); + this.particleMaxAge = maxAge; + } + + /** + * Called to update the entity's position/logic. + */ + public void onUpdate() + { + this.prevPosX = this.posX; + this.prevPosY = this.posY; + this.prevPosZ = this.posZ; + this.moveEntity(this.motionX, this.motionY, this.motionZ); + + if (this.particleMaxAge-- <= 0) + { + this.setDead(); + } + } + + /** + * Public method to set private field particleTextureIndex. + */ + public void setParticleTextureIndex(int par1) + { + this.particleTextureIndexX = par1 % 4; + this.particleTextureIndexY = par1 / 4; + } + + @Override + public int getFXLayer() { + // This can only be 0-3 or it will cause an ArrayIndexOutOfBoundsException in EffectRenderer. + return 3; + } + + // Overwitten from entityfx to bind the new texture. + @Override + public void renderParticle(Tessellator tessellator, float partialTicks, float par3, float par4, float par5, float par6, float par7) + { + GL11.glPushMatrix(); + + // This stuff does the shading. The parameter does nothing apparently. + int j = this.getBrightnessForRender(0); + int k = j % 65536; + int l = j / 65536; + OpenGlHelper.setLightmapTextureCoords(OpenGlHelper.lightmapTexUnit, (float)k / 1.0F, (float)l / 1.0F); + + RenderHelper.disableStandardItemLighting(); + + Minecraft.getMinecraft().renderEngine.bindTexture(snowTextures); + + tessellator.startDrawingQuads(); + + float f6 = (float)this.particleTextureIndexX / 4.0F; + float f7 = f6 + 0.0624375F*4; + float f8 = (float)this.particleTextureIndexY / 4.0F; + float f9 = f8 + 0.0624375F*4; + float f10 = 0.1F * this.particleScale; + + if (this.particleIcon != null) + { + f6 = this.particleIcon.getMinU(); + f7 = this.particleIcon.getMaxU(); + f8 = this.particleIcon.getMinV(); + f9 = this.particleIcon.getMaxV(); + } + + // Fix which gets proper values for interpPos so the particles don't appear to move. + Entity player = Minecraft.getMinecraft().thePlayer; + this.interpPosX = player.lastTickPosX + (player.posX - player.lastTickPosX) * (double)partialTicks; + this.interpPosY = player.lastTickPosY + (player.posY - player.lastTickPosY) * (double)partialTicks; + this.interpPosZ = player.lastTickPosZ + (player.posZ - player.lastTickPosZ) * (double)partialTicks; + + float f11 = (float)(this.prevPosX + (this.posX - this.prevPosX) * (double)partialTicks - interpPosX); + float f12 = (float)(this.prevPosY + (this.posY - this.prevPosY) * (double)partialTicks - interpPosY); + float f13 = (float)(this.prevPosZ + (this.posZ - this.prevPosZ) * (double)partialTicks - interpPosZ); + float f14 = 1.0F; + + tessellator.setColorRGBA_F(this.particleRed * f14, this.particleGreen * f14, this.particleBlue * f14, this.particleAlpha); + tessellator.addVertexWithUV((double)(f11 - par3 * f10 - par6 * f10), (double)(f12 - par4 * f10), (double)(f13 - par5 * f10 - par7 * f10), (double)f7, (double)f9); + tessellator.addVertexWithUV((double)(f11 - par3 * f10 + par6 * f10), (double)(f12 + par4 * f10), (double)(f13 - par5 * f10 + par7 * f10), (double)f7, (double)f8); + tessellator.addVertexWithUV((double)(f11 + par3 * f10 + par6 * f10), (double)(f12 + par4 * f10), (double)(f13 + par5 * f10 + par7 * f10), (double)f6, (double)f8); + tessellator.addVertexWithUV((double)(f11 + par3 * f10 - par6 * f10), (double)(f12 - par4 * f10), (double)(f13 + par5 * f10 - par7 * f10), (double)f6, (double)f9); + + tessellator.draw(); + + GL11.glPopMatrix(); + + } +} diff --git a/src/main/java/electroblob/wizardry/client/particle/EntitySparkFX.java b/src/main/java/electroblob/wizardry/client/particle/EntitySparkFX.java new file mode 100644 index 00000000..4e5b7fa6 --- /dev/null +++ b/src/main/java/electroblob/wizardry/client/particle/EntitySparkFX.java @@ -0,0 +1,117 @@ +package electroblob.wizardry.client.particle; + +import org.lwjgl.opengl.GL11; + +import cpw.mods.fml.relauncher.Side; +import cpw.mods.fml.relauncher.SideOnly; +import electroblob.wizardry.Wizardry; +import net.minecraft.client.Minecraft; +import net.minecraft.client.particle.EntityFX; +import net.minecraft.client.renderer.OpenGlHelper; +import net.minecraft.client.renderer.RenderHelper; +import net.minecraft.client.renderer.Tessellator; +import net.minecraft.entity.Entity; +import net.minecraft.util.ResourceLocation; +import net.minecraft.world.World; + +@SideOnly(Side.CLIENT) +public class EntitySparkFX extends EntityFX +{ + /** The name used to identify this particle. Uses the mod id to avoid any possible conflicts (Not that there would + * be any, but I may as well.) */ + public static final String NAME = Wizardry.MODID + "spark"; + + private static final ResourceLocation sparkTextures = new ResourceLocation("wizardry:textures/particle/lightning_particles.png"); + + public EntitySparkFX(World par1World, double par2, double par4, double par6, double par8, double par10, double par12) + { + super(par1World, par2, par4, par6, par8, par10, par12); + this.particleRed = 1.0F; + this.particleGreen = 1.0F; + this.particleBlue = 1.0F; + this.setParticleTextureIndex(rand.nextInt(8)); + this.setSize(0.02F, 0.02F); + this.particleScale *= 1.4f; + this.motionX = par8 * 0.20000000298023224D + (double)((float)(Math.random() * 2.0D - 1.0D) * 0.02F); + this.motionY = par10 * 0.20000000298023224D + (double)((float)(Math.random() * 2.0D - 1.0D) * 0.02F); + this.motionZ = par12 * 0.20000000298023224D + (double)((float)(Math.random() * 2.0D - 1.0D) * 0.02F); + this.particleMaxAge = 3; + } + + /** + * Called to update the entity's position/logic. + */ + public void onUpdate() + { + this.prevPosX = this.posX; + this.prevPosY = this.posY; + this.prevPosZ = this.posZ; + + if(this.particleAge++ >= this.particleMaxAge) + { + this.setDead(); + } + } + + /** + * Public method to set private field particleTextureIndex. + */ + public void setParticleTextureIndex(int par1) + { + this.particleTextureIndexY = par1; + } + + @Override + public int getFXLayer() { + // This can only be 0-3 or it will cause an ArrayIndexOutOfBoundsException in EffectRenderer. + return 3; + } + + // Overwitten from entityfx to bind the new texture. + @Override + public void renderParticle(Tessellator par1Tessellator, float partialTicks, float par3, float par4, float par5, float par6, float par7) + { + GL11.glPushMatrix(); + GL11.glDisable(GL11.GL_LIGHTING); + GL11.glEnable(GL11.GL_BLEND); + GL11.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA); + OpenGlHelper.setLightmapTextureCoords(OpenGlHelper.lightmapTexUnit, 240f, 240f); + + RenderHelper.disableStandardItemLighting(); + + Minecraft.getMinecraft().renderEngine.bindTexture(sparkTextures); + + par1Tessellator.startDrawingQuads(); + + // Particles are now animated using a strip of 4 from left to right. + float f6 = (float)this.particleAge / 4.0F; + float f7 = f6 + 0.0624375F*4; + float f8 = (float)this.particleTextureIndexY / 8.0f; + float f9 = f8 + 0.0624375F*2; + float f10 = 0.1F * this.particleScale; + + // Fix which gets proper values for interpPos so the particles don't appear to move. + Entity player = Minecraft.getMinecraft().thePlayer; + this.interpPosX = player.lastTickPosX + (player.posX - player.lastTickPosX) * (double)partialTicks; + this.interpPosY = player.lastTickPosY + (player.posY - player.lastTickPosY) * (double)partialTicks; + this.interpPosZ = player.lastTickPosZ + (player.posZ - player.lastTickPosZ) * (double)partialTicks; + + float f11 = (float)(this.prevPosX + (this.posX - this.prevPosX) * (double)partialTicks - interpPosX); + float f12 = (float)(this.prevPosY + (this.posY - this.prevPosY) * (double)partialTicks - interpPosY); + float f13 = (float)(this.prevPosZ + (this.posZ - this.prevPosZ) * (double)partialTicks - interpPosZ); + float f14 = 1.0F; + + par1Tessellator.setColorRGBA_F(this.particleRed * f14, this.particleGreen * f14, this.particleBlue * f14, this.particleAlpha); + par1Tessellator.addVertexWithUV((double)(f11 - par3 * f10 - par6 * f10), (double)(f12 - par4 * f10), (double)(f13 - par5 * f10 - par7 * f10), (double)f7, (double)f9); + par1Tessellator.addVertexWithUV((double)(f11 - par3 * f10 + par6 * f10), (double)(f12 + par4 * f10), (double)(f13 - par5 * f10 + par7 * f10), (double)f7, (double)f8); + par1Tessellator.addVertexWithUV((double)(f11 + par3 * f10 + par6 * f10), (double)(f12 + par4 * f10), (double)(f13 + par5 * f10 + par7 * f10), (double)f6, (double)f8); + par1Tessellator.addVertexWithUV((double)(f11 + par3 * f10 - par6 * f10), (double)(f12 - par4 * f10), (double)(f13 + par5 * f10 - par7 * f10), (double)f6, (double)f9); + + par1Tessellator.draw(); + + GL11.glEnable(GL11.GL_LIGHTING); + GL11.glDisable(GL11.GL_BLEND); + GL11.glPopMatrix(); + + } +} diff --git a/src/main/java/electroblob/wizardry/client/particle/EntitySparkleFX.java b/src/main/java/electroblob/wizardry/client/particle/EntitySparkleFX.java new file mode 100644 index 00000000..9dc164fc --- /dev/null +++ b/src/main/java/electroblob/wizardry/client/particle/EntitySparkleFX.java @@ -0,0 +1,203 @@ +package electroblob.wizardry.client.particle; + +import org.lwjgl.opengl.GL11; + +import cpw.mods.fml.relauncher.Side; +import cpw.mods.fml.relauncher.SideOnly; +import electroblob.wizardry.Wizardry; +import net.minecraft.client.Minecraft; +import net.minecraft.client.particle.EffectRenderer; +import net.minecraft.client.particle.EntityFX; +import net.minecraft.client.renderer.OpenGlHelper; +import net.minecraft.client.renderer.RenderHelper; +import net.minecraft.client.renderer.Tessellator; +import net.minecraft.entity.Entity; +import net.minecraft.util.AxisAlignedBB; +import net.minecraft.util.ResourceLocation; +import net.minecraft.world.World; + +@SideOnly(Side.CLIENT) +public class EntitySparkleFX extends EntityFX +{ + /** The name used to identify this particle, without gravity. Uses the mod id to avoid any possible conflicts (Not that there would + * be any, but I may as well.) */ + public static final String NAME = Wizardry.MODID + "sparkle"; + + /** The name used to identify this particle, with gravity. Uses the mod id to avoid any possible conflicts (Not that there would + * be any, but I may as well.) */ + public static final String NAME_GRAVITY = Wizardry.MODID + "sparkleGravity"; + + private static final ResourceLocation sparkleTextures = new ResourceLocation("wizardry:textures/particle/sparkle_particles.png"); + + private boolean gravity = false; + + public EntitySparkleFX(World par1World, double par2, double par4, double par6, double par8, double par10, double par12, float red, float green, float blue, int maxAge) + { + super(par1World, par2, par4, par6); + this.motionX = par8; + this.motionY = par10; + this.motionZ = par12; + this.particleScale *= 0.75F; + this.particleMaxAge = maxAge; + this.noClip = true; + particleRed = red; + particleGreen = green; + particleBlue = blue; + } + + public EntitySparkleFX(World par1World, double par2, double par4, double par6, double par8, double par10, double par12, float red, float green, float blue) + { + super(par1World, par2, par4, par6); + this.motionX = par8; + this.motionY = par10; + this.motionZ = par12; + this.particleScale *= 0.75F; + this.particleMaxAge = 48 + this.rand.nextInt(12); + this.noClip = true; + particleRed = red; + particleGreen = green; + particleBlue = blue; + } + + public EntitySparkleFX(World par1World, double par2, double par4, double par6, double par8, double par10, double par12, float red, float green, float blue, boolean doGravity){ + this(par1World, par2, par4, par6, par8, par10, par12, red, green, blue); + this.gravity = doGravity; + } + + public EntitySparkleFX(World par1World, double par2, double par4, double par6, double par8, double par10, double par12, float red, float green, float blue, int maxAge, boolean doGravity){ + this(par1World, par2, par4, par6, par8, par10, par12, red, green, blue, maxAge); + this.gravity = doGravity; + } + + /** + * returns the bounding box for this entity + */ + public AxisAlignedBB getBoundingBox() + { + return null; + } + + /** + * Returns true if this entity should push and be pushed by other entities when colliding. + */ + public boolean canBePushed() + { + return false; + } + + @Override + public void setParticleTextureIndex(int par1) + { + this.particleTextureIndexX = par1 % 4; + this.particleTextureIndexY = par1 / 4; + } + + /** + * Called to update the entity's position/logic. + */ + public void onUpdate() + { + this.prevPosX = this.posX; + this.prevPosY = this.posY; + this.prevPosZ = this.posZ; + + if (this.particleAge++ >= this.particleMaxAge) + { + this.setDead(); + } + + this.moveEntity(this.motionX, this.motionY, this.motionZ); + this.motionX *= 0.9800000190734863D; + this.motionY *= 0.9800000190734863D; + this.motionZ *= 0.9800000190734863D; + if(this.gravity){ + this.motionY -= 0.05d; + } + + if (this.particleAge > this.particleMaxAge / 2) + { + this.setAlphaF(1.0F - ((float)this.particleAge - (float)(this.particleMaxAge / 2)) / (float)this.particleMaxAge); + } + + this.setParticleTextureIndex((this.particleAge * 11)/this.particleMaxAge); + + } + + /* There are 4 layers of particles, specified as 0-3 by the method below. + * - Layer 0 causes the normal particles.png to be bound to the render engine for normal particles. + * - Layer 1 causes the block textures to be bound to the render engine for digging fx and falling fx. + * - Layer 2 causes the item textures to be bound to the render engine for tool breaking fx, snowballpoofs, slime particles, etc. + * - Layer 3 is not used in vanilla minecraft and was presumably added by forge for exactly this reason. + * This means no texture is bound by vanilla minecraft, meaning you are free to do as you wish without possibly + * overwriting vanilla particles. Mod particles won't be overwritten anyway since they bind their own textures. + * It is of course important to bind the texture every time you render a custom particle, but I don't see how + * you could do it any other way, since you don't have access to EffectRenderer. + */ + @Override + public int getFXLayer() { + // This can only be 0-3 or it will cause an ArrayIndexOutOfBoundsException in EffectRenderer. + return 3; + } + + @Override + public void renderParticle(Tessellator tessellator, float partialTicks, float par3, float par4, float par5, float par6, float par7){ + + GL11.glPushMatrix(); + + OpenGlHelper.setLightmapTextureCoords(OpenGlHelper.lightmapTexUnit, 240, 240); + + RenderHelper.disableStandardItemLighting(); + + Minecraft.getMinecraft().renderEngine.bindTexture(sparkleTextures); + + tessellator.startDrawingQuads(); + + float f6 = (float)this.particleTextureIndexX / 4.0F; + float f7 = f6 + 0.0624375F*4; + float f8 = (float)this.particleTextureIndexY / 4.0F; + float f9 = f8 + 0.0624375F*4; + float f10 = 0.1F * this.particleScale; + + if (this.particleIcon != null) + { + f6 = this.particleIcon.getMinU(); + f7 = this.particleIcon.getMaxU(); + f8 = this.particleIcon.getMinV(); + f9 = this.particleIcon.getMaxV(); + } + + // Fix which gets proper values for interpPos so the particles don't appear to move. + Entity player = Minecraft.getMinecraft().thePlayer; + this.interpPosX = player.lastTickPosX + (player.posX - player.lastTickPosX) * (double)partialTicks; + this.interpPosY = player.lastTickPosY + (player.posY - player.lastTickPosY) * (double)partialTicks; + this.interpPosZ = player.lastTickPosZ + (player.posZ - player.lastTickPosZ) * (double)partialTicks; + + float f11 = (float)(this.prevPosX + (this.posX - this.prevPosX) * (double)partialTicks - interpPosX); + float f12 = (float)(this.prevPosY + (this.posY - this.prevPosY) * (double)partialTicks - interpPosY); + float f13 = (float)(this.prevPosZ + (this.posZ - this.prevPosZ) * (double)partialTicks - interpPosZ); + float f14 = 1.0F; + + tessellator.setColorRGBA_F(this.particleRed * f14, this.particleGreen * f14, this.particleBlue * f14, this.particleAlpha); + tessellator.addVertexWithUV((double)(f11 - par3 * f10 - par6 * f10), (double)(f12 - par4 * f10), (double)(f13 - par5 * f10 - par7 * f10), (double)f7, (double)f9); + tessellator.addVertexWithUV((double)(f11 - par3 * f10 + par6 * f10), (double)(f12 + par4 * f10), (double)(f13 - par5 * f10 + par7 * f10), (double)f7, (double)f8); + tessellator.addVertexWithUV((double)(f11 + par3 * f10 + par6 * f10), (double)(f12 + par4 * f10), (double)(f13 + par5 * f10 + par7 * f10), (double)f6, (double)f8); + tessellator.addVertexWithUV((double)(f11 + par3 * f10 - par6 * f10), (double)(f12 - par4 * f10), (double)(f13 + par5 * f10 - par7 * f10), (double)f6, (double)f9); + + tessellator.draw(); + + GL11.glPopMatrix(); + + } + + @Override + public int getBrightnessForRender(float par1) + { + return 15728880; + } + + @Override + public float getBrightness(float par1) + { + return 1.0F; + } +} diff --git a/src/main/java/electroblob/wizardry/client/particle/EntityTornadoFX.java b/src/main/java/electroblob/wizardry/client/particle/EntityTornadoFX.java new file mode 100644 index 00000000..dc75b635 --- /dev/null +++ b/src/main/java/electroblob/wizardry/client/particle/EntityTornadoFX.java @@ -0,0 +1,135 @@ +package electroblob.wizardry.client.particle; + +import cpw.mods.fml.relauncher.Side; +import cpw.mods.fml.relauncher.SideOnly; +import electroblob.wizardry.Wizardry; +import net.minecraft.block.Block; +import net.minecraft.client.particle.EntityDiggingFX; +import net.minecraft.client.renderer.Tessellator; +import net.minecraft.init.Blocks; +import net.minecraft.util.AxisAlignedBB; +import net.minecraft.world.World; + +@SideOnly(Side.CLIENT) +public class EntityTornadoFX extends EntityDiggingFX +{ + /** The name used to identify this particle. Uses the mod id to avoid any possible conflicts (Not that there would + * be any, but I may as well.) */ + public static final String NAME = Wizardry.MODID + "tornado"; + + private boolean gravity = false; + private double angle; + private double radius; + private double speed; + private double velX, velZ; + private boolean fullBrightness = false; + + public EntityTornadoFX(World world, int maxAge, double originX, double originZ, double radius, double yPos, double velX, double velZ, Block block, int metadata, int side) + { + super(world, 0, 0, 0, 0, 0, 0, block, metadata, side); + this.angle = this.rand.nextDouble() * Math.PI * 2; + double x = originX - Math.cos(angle)*radius; + double z = originZ + radius*Math.sin(angle); + this.radius = radius; + this.setPosition(x, yPos, z); + //this.particleScale *= 0.75F; + this.particleMaxAge = maxAge; + this.noClip = true; + // Grass has special treatment, since it has a colourised top but the rest is normal. + if(block != Blocks.grass || side == 1) this.setColour(block.getRenderColor(side)); + + if(block.getLightValue() == 0){ + this.particleRed *= 0.75; + this.particleGreen *= 0.75; + this.particleBlue *= 0.75; + }else{ + this.fullBrightness = true; + } + + speed = rand.nextDouble()*2 + 1; + this.velX = velX; + this.velZ = velZ; + } + + public EntityTornadoFX(World world, double x, double y, double z, double vx, double vy, double vz, float red, float green, float blue) + { + super(world, x, y, z, vx, vy, vz, Blocks.dirt, 0); + //this.particleScale *= 0.75F; + this.particleMaxAge = 48 + this.rand.nextInt(12); + this.noClip = true; + particleRed = red; + particleGreen = green; + particleBlue = blue; + } + + public void setColour(int par1) + { + float f = (float)((par1 & 16711680) >> 16) / 255.0F; + float f1 = (float)((par1 & 65280) >> 8) / 255.0F; + float f2 = (float)((par1 & 255) >> 0) / 255.0F; + float f3 = 1.0F; + this.setRBGColorF(f * f3, f1 * f3, f2 * f3); + } + + /** + * returns the bounding box for this entity + */ + public AxisAlignedBB getBoundingBox() + { + return null; + } + + /** + * Returns true if this entity should push and be pushed by other entities when colliding. + */ + public boolean canBePushed() + { + return false; + } + + @Override + public void onUpdate() + { + this.prevPosX = this.posX; + this.prevPosY = this.posY; + this.prevPosZ = this.posZ; + + if (this.particleAge++ >= this.particleMaxAge) + { + this.setDead(); + } + + // This is in radians per tick... + double omega = Math.signum(speed) * ((Math.PI*2)/20 - speed/(20*radius)); + + // v = r times omega; therefore the normalised velocity vector needs to be r times the angle increment / 2 pi. + this.angle += omega; + + this.motionZ = radius * omega * Math.cos(angle); + this.motionX = radius * omega * Math.sin(angle); + this.moveEntity(motionX + velX, 0, motionZ + velZ); + + if(this.gravity){ + this.motionY -= 0.05d; + } + + if (this.particleAge > this.particleMaxAge / 2) + { + this.setAlphaF(1.0F - ((float)this.particleAge - (float)(this.particleMaxAge / 2)) / (float)this.particleMaxAge); + } + + } + + @Override + public int getBrightnessForRender(float par1) + { + return fullBrightness ? 15728880 : super.getBrightnessForRender(par1); + } + + @Override + public float getBrightness(float par1) + { + return fullBrightness ? 1.0F : super.getBrightness(par1); + } + +} diff --git a/src/main/java/electroblob/wizardry/client/particle/ParticleBlizzard.java b/src/main/java/electroblob/wizardry/client/particle/ParticleBlizzard.java deleted file mode 100644 index 4ea3114b..00000000 --- a/src/main/java/electroblob/wizardry/client/particle/ParticleBlizzard.java +++ /dev/null @@ -1,72 +0,0 @@ -package electroblob.wizardry.client.particle; - -import net.minecraft.world.World; -import net.minecraftforge.fml.relauncher.Side; -import net.minecraftforge.fml.relauncher.SideOnly; - -@SideOnly(Side.CLIENT) -public class ParticleBlizzard extends ParticleSnow { - - private double angle; - private double radius; - private double speed; - - public ParticleBlizzard(World world, int maxAge, double originX, double originZ, double radius, double yPos){ - super(world, 0, 0, 0, 0, 0, 0, maxAge); - this.angle = this.rand.nextDouble() * Math.PI * 2; - double x = originX - Math.cos(angle)*radius; - double z = originZ + radius*Math.sin(angle); - this.radius = radius; - this.setPosition(x, yPos, z); - this.prevPosX = x; - this.prevPosY = yPos; - this.prevPosZ = z; - if(rand.nextBoolean()){ - speed = rand.nextDouble()*2 + 1; - }else{ - speed = rand.nextDouble()*-2 - 1; - } - this.multipleParticleScaleBy(1.5f); - } - - @Override - public void init(){ - super.init(); - this.fullBrightness = true; - } - -// @Override -// public void renderParticle(VertexBuffer buffer, Entity entity, float partialTicks, float rotationX, float rotationZ, float rotationYZ, float rotationXY, float rotationXZ){ -// if(this.particleAge < this.particleMaxAge / 3 || (this.particleAge + this.particleMaxAge) / 3 % 2 == 0){ -// super.renderParticle(buffer, entity, partialTicks, rotationX, rotationZ, rotationYZ, rotationXY, rotationXZ); -// } -// } - - @Override - public void onUpdate(){ - - this.prevPosX = this.posX; - this.prevPosY = this.posY; - this.prevPosZ = this.posZ; - - if(this.particleAge++ >= this.particleMaxAge){ - this.setExpired(); - } - - // This is in radians per tick... - double omega = Math.signum(speed) * ((Math.PI*2)/20 - speed/(20*radius)); - - // v = r times omega; therefore the normalised velocity vector needs to be r times the angle increment / 2 pi. - this.angle += omega; - - this.motionY -= 0.04D * (double)this.particleGravity; - this.motionZ = radius * omega * Math.cos(angle); - this.motionX = radius * omega * Math.sin(angle); - this.moveEntity(motionX, motionY, motionZ); - - if(this.particleAge > this.particleMaxAge / 2){ - this.setAlphaF(1.0F - ((float)this.particleAge - (float)(this.particleMaxAge / 2)) / (float)this.particleMaxAge); - } - - } -} diff --git a/src/main/java/electroblob/wizardry/client/particle/ParticleCustomTexture.java b/src/main/java/electroblob/wizardry/client/particle/ParticleCustomTexture.java deleted file mode 100644 index 3d5fa986..00000000 --- a/src/main/java/electroblob/wizardry/client/particle/ParticleCustomTexture.java +++ /dev/null @@ -1,195 +0,0 @@ -package electroblob.wizardry.client.particle; - -import java.util.List; - -import org.lwjgl.opengl.GL11; - -import net.minecraft.client.Minecraft; -import net.minecraft.client.particle.Particle; -import net.minecraft.client.renderer.GlStateManager; -import net.minecraft.client.renderer.OpenGlHelper; -import net.minecraft.client.renderer.RenderHelper; -import net.minecraft.client.renderer.Tessellator; -import net.minecraft.client.renderer.VertexBuffer; -import net.minecraft.client.renderer.vertex.DefaultVertexFormats; -import net.minecraft.entity.Entity; -import net.minecraft.util.ResourceLocation; -import net.minecraft.util.math.AxisAlignedBB; -import net.minecraft.world.World; -import net.minecraftforge.fml.relauncher.Side; -import net.minecraftforge.fml.relauncher.SideOnly; - -/** - * Abstract superclass for all particles that use custom textures. This is intended to centralise as much code as - * possible; all subclasses need to do is to define the texture to use, how the frames are arranged (and which to - * choose), and any properties like gravity and collisions. - * @author Electroblob - * @since Wizardry 1.2 - */ -@SideOnly(Side.CLIENT) -public abstract class ParticleCustomTexture extends Particle { - - /** True if the particle always renders at full brightness. Defaults to false. */ - protected boolean fullBrightness = false; - - public ParticleCustomTexture(World world, double x, double y, double z, double vx, double vy, double vz){ - super(world, x, y, z, vx, vy, vz); - this.motionX = vx; - this.motionY = vy; - this.motionZ = vz; - this.init(); - } - - public ParticleCustomTexture(World world, double x, double y, double z, double vx, double vy, double vz, int maxAge){ - this(world, x, y, z, vx, vy, vz); - this.particleMaxAge = maxAge; - } - - /** Called from both constructors to set constants, avoiding duplicate code. Common fields to set here - * include: particleScale, particleGravity, canCollide, fullBrightness and setting the texture index. */ - public abstract void init(); - /** Returns a ResourceLocation for the particle's texture sheet. Do not create a new ResourceLocation in this - * method, only return a constant. */ - public abstract ResourceLocation getTexture(); - /** Returns how many 'frames' there are in the x direction on the texture. */ - protected abstract int getXFrames(); - /** Returns how many 'frames' there are in the y direction on the texture. */ - protected abstract int getYFrames(); - - /* There are 4 layers of particles, specified as 0-3 by the method below. - * - Layer 0 causes the normal particles.png to be bound to the render engine for normal particles. - * - Layer 1 causes the block textures to be bound to the render engine for digging fx and falling fx. - * - Layer 2 causes the item textures to be bound to the render engine for tool breaking fx, snowballpoofs, slime particles, etc. - * - Layer 3 is not used in vanilla minecraft and was presumably added by forge for exactly this reason. - * This means no texture is bound by vanilla minecraft, meaning you are free to do as you wish without possibly - * overwriting vanilla particles. Mod particles won't be overwritten anyway since they bind their own textures. - * It is of course important to bind the texture every time you render a custom particle, but I don't see how - * you could do it any other way, since you don't have access to EffectRenderer. */ - @Override - public int getFXLayer() { - // This can only be 0-3 or it will cause an ArrayIndexOutOfBoundsException in EffectRenderer. - return 3; - } - - @Override - public void setParticleTextureIndex(int index){ - this.particleTextureIndexX = index % getXFrames(); - this.particleTextureIndexY = index / getYFrames(); - } - - // Overridden to fix the bug with vanilla that makes particles frictionless. (y != y... seriously, Mojang?) - @Override - public void moveEntity(double x, double y, double z){ - - double d0 = y; - - if (this.canCollide) - { - List list = this.worldObj.getCollisionBoxes((Entity)null, this.getEntityBoundingBox().addCoord(x, y, z)); - - for (AxisAlignedBB axisalignedbb : list) - { - y = axisalignedbb.calculateYOffset(this.getEntityBoundingBox(), y); - } - - this.setEntityBoundingBox(this.getEntityBoundingBox().offset(0.0D, y, 0.0D)); - - for (AxisAlignedBB axisalignedbb1 : list) - { - x = axisalignedbb1.calculateXOffset(this.getEntityBoundingBox(), x); - } - - this.setEntityBoundingBox(this.getEntityBoundingBox().offset(x, 0.0D, 0.0D)); - - for (AxisAlignedBB axisalignedbb2 : list) - { - z = axisalignedbb2.calculateZOffset(this.getEntityBoundingBox(), z); - } - - this.setEntityBoundingBox(this.getEntityBoundingBox().offset(0.0D, 0.0D, z)); - } - else - { - this.setEntityBoundingBox(this.getEntityBoundingBox().offset(x, y, z)); - } - - this.resetPositionToBB(); - this.isCollided = d0 != y && d0 < 0.0D; - - /* Can never be true! - But this doesn't seem to make any difference anyway. - if (x != x) - { - this.motionX = 0.0D; - } - - if (z != z) - { - this.motionZ = 0.0D; - } - */ - } - - // Overridden to bind the new texture. I think this can be done with TextureAtlasSprite, but this works as it is - // so I'm not changing it for the time being. - @Override - public void renderParticle(VertexBuffer buffer, Entity viewer, float partialTicks, float rotationX, float rotationZ, float rotationYZ, float rotationXY, float rotationXZ){ - - GlStateManager.pushMatrix(); - GlStateManager.pushAttrib(); - - this.applyGLStateChanges(); - - // This stuff does the shading. It vanilla does this later on for each point, but this also seems to work. - int brightness = this.getBrightnessForRender(partialTicks); - int lightmapX = brightness % 65536; - int lightmapY = brightness / 65536; - OpenGlHelper.setLightmapTextureCoords(OpenGlHelper.lightmapTexUnit, (float)lightmapX / 1.0F, (float)lightmapY / 1.0F); - - RenderHelper.disableStandardItemLighting(); - - Minecraft.getMinecraft().getTextureManager().bindTexture(getTexture()); - - buffer.begin(GL11.GL_QUADS, DefaultVertexFormats.POSITION_TEX_COLOR); - - float u1 = (float)this.particleTextureIndexX / (float)getXFrames(); - float u2 = u1 + 1.0f/getXFrames(); - float v1 = (float)this.particleTextureIndexY / (float)getYFrames(); - float v2 = v1 + 1.0f/getYFrames(); - float scale = 0.1F * this.particleScale; - - // I'm pretty sure these were always static. - Particle.interpPosX = viewer.lastTickPosX + (viewer.posX - viewer.lastTickPosX) * (double)partialTicks; - Particle.interpPosY = viewer.lastTickPosY + (viewer.posY - viewer.lastTickPosY) * (double)partialTicks; - Particle.interpPosZ = viewer.lastTickPosZ + (viewer.posZ - viewer.lastTickPosZ) * (double)partialTicks; - - float x = (float)(this.prevPosX + (this.posX - this.prevPosX) * (double)partialTicks - interpPosX); - float y = (float)(this.prevPosY + (this.posY - this.prevPosY) * (double)partialTicks - interpPosY); - float z = (float)(this.prevPosZ + (this.posZ - this.prevPosZ) * (double)partialTicks - interpPosZ); - - buffer.pos((double)(x - rotationX * scale - rotationXY * scale), (double)(y - rotationZ * scale), (double)(z - rotationYZ * scale - rotationXZ * scale)).tex(u2, v2).color(particleRed, particleGreen, particleBlue, particleAlpha).endVertex(); - buffer.pos((double)(x - rotationX * scale + rotationXY * scale), (double)(y + rotationZ * scale), (double)(z - rotationYZ * scale + rotationXZ * scale)).tex(u2, v1).color(particleRed, particleGreen, particleBlue, particleAlpha).endVertex(); - buffer.pos((double)(x + rotationX * scale + rotationXY * scale), (double)(y + rotationZ * scale), (double)(z + rotationYZ * scale + rotationXZ * scale)).tex(u1, v1).color(particleRed, particleGreen, particleBlue, particleAlpha).endVertex(); - buffer.pos((double)(x + rotationX * scale - rotationXY * scale), (double)(y - rotationZ * scale), (double)(z + rotationYZ * scale - rotationXZ * scale)).tex(u1, v2).color(particleRed, particleGreen, particleBlue, particleAlpha).endVertex();; - - Tessellator.getInstance().draw(); - - this.undoGLStateChanges(); - - GlStateManager.popAttrib(); - GlStateManager.popMatrix(); - - } - - /** Override to add any GL state changes, like blending. Does nothing by default. State changes should be - * done using GLStateManager, not using GL11 directly (as is the case with all rendering code now). */ - public void applyGLStateChanges(){} - - /** Override to undo any GL state changes, like blending. Does nothing by default. State changes should be - * done using GLStateManager, not using GL11 directly (as is the case with all rendering code now). */ - public void undoGLStateChanges(){} - - @Override - public int getBrightnessForRender(float partialTick){ - return fullBrightness ? 15728880 : super.getBrightnessForRender(partialTick); - } -} diff --git a/src/main/java/electroblob/wizardry/client/particle/ParticleIce.java b/src/main/java/electroblob/wizardry/client/particle/ParticleIce.java deleted file mode 100644 index 3332db15..00000000 --- a/src/main/java/electroblob/wizardry/client/particle/ParticleIce.java +++ /dev/null @@ -1,34 +0,0 @@ -package electroblob.wizardry.client.particle; - -import electroblob.wizardry.Wizardry; -import net.minecraft.util.ResourceLocation; -import net.minecraft.world.World; -import net.minecraftforge.fml.relauncher.Side; -import net.minecraftforge.fml.relauncher.SideOnly; - -@SideOnly(Side.CLIENT) -public class ParticleIce extends ParticleCustomTexture { - - private static final ResourceLocation TEXTURE = new ResourceLocation(Wizardry.MODID, "textures/particle/ice_particles.png"); - - public ParticleIce(World world, double x, double y, double z, double vx, double vy, double vz){ - super(world, x, y, z, vx, vy, vz); - } - - public ParticleIce(World world, double x, double y, double z, double vx, double vy, double vz, int maxAge){ - super(world, x, y, z, vx, vy, vz, maxAge); - } - - @Override - public void init(){ - this.setParticleTextureIndex(rand.nextInt(8)); - this.particleScale *= 0.75f; - this.particleGravity = 1; - this.canCollide = true; - this.fullBrightness = true; - } - - @Override public ResourceLocation getTexture(){ return TEXTURE; } - @Override protected int getXFrames(){ return 4; } - @Override protected int getYFrames(){ return 4; } -} diff --git a/src/main/java/electroblob/wizardry/client/particle/ParticleLeaf.java b/src/main/java/electroblob/wizardry/client/particle/ParticleLeaf.java deleted file mode 100644 index 0292dddf..00000000 --- a/src/main/java/electroblob/wizardry/client/particle/ParticleLeaf.java +++ /dev/null @@ -1,33 +0,0 @@ -package electroblob.wizardry.client.particle; - -import electroblob.wizardry.Wizardry; -import net.minecraft.util.ResourceLocation; -import net.minecraft.world.World; -import net.minecraftforge.fml.relauncher.Side; -import net.minecraftforge.fml.relauncher.SideOnly; - -@SideOnly(Side.CLIENT) -public class ParticleLeaf extends ParticleCustomTexture { - - private static final ResourceLocation TEXTURE = new ResourceLocation(Wizardry.MODID, "textures/particle/leaf_particles.png"); - - public ParticleLeaf(World world, double x, double y, double z, double vx, double vy, double vz){ - super(world, x, y, z, vx, vy, vz); - } - - public ParticleLeaf(World world, double x, double y, double z, double vx, double vy, double vz, int maxAge){ - super(world, x, y, z, vx, vy, vz, maxAge); - } - - @Override - public void init() { - this.setParticleTextureIndex(rand.nextInt(16)); - this.particleScale *= 1.4f; - this.particleGravity = 0; - this.canCollide = true; - } - - @Override public ResourceLocation getTexture(){ return TEXTURE; } - @Override protected int getXFrames(){ return 4; } - @Override protected int getYFrames(){ return 4; } -} diff --git a/src/main/java/electroblob/wizardry/client/particle/ParticleMagicFlame.java b/src/main/java/electroblob/wizardry/client/particle/ParticleMagicFlame.java deleted file mode 100644 index 755fa4db..00000000 --- a/src/main/java/electroblob/wizardry/client/particle/ParticleMagicFlame.java +++ /dev/null @@ -1,47 +0,0 @@ -package electroblob.wizardry.client.particle; - -import net.minecraft.client.particle.Particle; -import net.minecraft.client.renderer.VertexBuffer; -import net.minecraft.entity.Entity; -import net.minecraft.world.World; -import net.minecraftforge.fml.relauncher.Side; -import net.minecraftforge.fml.relauncher.SideOnly; - -@SideOnly(Side.CLIENT) -public class ParticleMagicFlame extends Particle { - - /** The scale of the flame particle */ - private float flameScale; - - public ParticleMagicFlame(World par1World, double par2, double par4, double par6, double par8, double par10, double par12, int maxAge, float scale) - { - super(par1World, par2, par4, par6, par8, par10, par12); - this.motionX = this.motionX * 0.009999999776482582D + par8; - this.motionY = this.motionY * 0.009999999776482582D + par10; - this.motionZ = this.motionZ * 0.009999999776482582D + par12; - this.flameScale = scale; - this.particleRed = this.particleGreen = this.particleBlue = 1.0F; - if(maxAge == 0){ - this.particleMaxAge = (int)(2.0D / (Math.random() * 0.8D + 0.2D)); - }else{ - this.particleMaxAge = maxAge; - } - // IDEA: Make the particles for ray spells collide properly and not spawn on the other side of stuff. - this.canCollide = false; - this.setParticleTextureIndex(48); - } - - @Override - public void renderParticle(VertexBuffer buffer, Entity entity, float partialTicks, float rotationX, float rotationZ, float rotationYZ, float rotationXY, float rotationXZ) - { - float f6 = ((float)this.particleAge + partialTicks) / (float)this.particleMaxAge; - this.particleScale = this.flameScale * (1.0F - f6 * f6 * 0.5F); - super.renderParticle(buffer, entity, partialTicks, rotationX, rotationZ, rotationYZ, rotationXY, rotationXZ); - } - - @Override - public int getBrightnessForRender(float par1) - { - return 256; - } -} diff --git a/src/main/java/electroblob/wizardry/client/particle/ParticlePath.java b/src/main/java/electroblob/wizardry/client/particle/ParticlePath.java deleted file mode 100644 index cf7b2c6e..00000000 --- a/src/main/java/electroblob/wizardry/client/particle/ParticlePath.java +++ /dev/null @@ -1,93 +0,0 @@ -package electroblob.wizardry.client.particle; - -import org.lwjgl.opengl.GL11; - -import electroblob.wizardry.Wizardry; -import electroblob.wizardry.spell.Clairvoyance; -import net.minecraft.client.renderer.GlStateManager; -import net.minecraft.client.renderer.OpenGlHelper; -import net.minecraft.util.ResourceLocation; -import net.minecraft.world.World; -import net.minecraftforge.fml.relauncher.Side; -import net.minecraftforge.fml.relauncher.SideOnly; - -@SideOnly(Side.CLIENT) -public class ParticlePath extends ParticleCustomTexture { - - private static final ResourceLocation TEXTURE = new ResourceLocation(Wizardry.MODID, "textures/particle/path_particles.png"); - - private final double originX, originY, originZ; - - public ParticlePath(World world, double x, double y, double z, double vx, double vy, double vz, float r, float g, float b){ - super(world, x, y, z, vx, vy, vz); - this.setRBGColorF(r, g, b); - this.originX = x; - this.originY = y; - this.originZ = z; - } - - public ParticlePath(World world, double x, double y, double z, double vx, double vy, double vz, float r, float g, float b, int maxAge){ - super(world, x, y, z, vx, vy, vz, maxAge); - this.setRBGColorF(r, g, b); - this.originX = x; - this.originY = y; - this.originZ = z; - } - - @Override - public void init(){ - this.setParticleTextureIndex(0); - // Set to a constant to remove the randomness from Particle. - this.particleScale = 1.25f; - this.particleGravity = 0; - this.fullBrightness = true; - this.canCollide = false; - } - - @Override public ResourceLocation getTexture(){ return TEXTURE; } - @Override protected int getXFrames(){ return 1; } - @Override protected int getYFrames(){ return 1; } - - @Override - public void onUpdate(){ - - this.prevPosX = this.posX; - this.prevPosY = this.posY; - this.prevPosZ = this.posZ; - - if (this.particleAge++ >= this.particleMaxAge) - { - this.setExpired(); - } - - this.moveEntity(this.motionX, this.motionY, this.motionZ); - - // Fading - if(this.particleAge > this.particleMaxAge / 2){ - this.setAlphaF(1.0F - 2 * (((float)this.particleAge - (float)(this.particleMaxAge / 2)) / (float)this.particleMaxAge)); - } - - if(this.particleAge % Clairvoyance.PARTICLE_MOVEMENT_INTERVAL == 0){ - this.setPosition(this.originX, this.originY, this.originZ); - this.prevPosX = this.posX; - this.prevPosY = this.posY; - this.prevPosZ = this.posZ; - } - - } - - @Override - public void applyGLStateChanges(){ - GlStateManager.enableBlend(); - GlStateManager.blendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA); - // TESTME: Are these two actually necessary? - GlStateManager.disableLighting(); - OpenGlHelper.setLightmapTextureCoords(OpenGlHelper.lightmapTexUnit, 240, 240); - } - - @Override - public void undoGLStateChanges(){ - GlStateManager.disableBlend(); - GlStateManager.enableLighting(); - } -} diff --git a/src/main/java/electroblob/wizardry/client/particle/ParticleRotatingSparkle.java b/src/main/java/electroblob/wizardry/client/particle/ParticleRotatingSparkle.java deleted file mode 100644 index eadc09bb..00000000 --- a/src/main/java/electroblob/wizardry/client/particle/ParticleRotatingSparkle.java +++ /dev/null @@ -1,59 +0,0 @@ -package electroblob.wizardry.client.particle; - -import net.minecraft.world.World; -import net.minecraftforge.fml.relauncher.Side; -import net.minecraftforge.fml.relauncher.SideOnly; - -@SideOnly(Side.CLIENT) -public class ParticleRotatingSparkle extends ParticleSparkle { - - private double angle; - private double radius; - private double speed; - - public ParticleRotatingSparkle(World world, int maxAge, double originX, double originZ, double radius, double yPos, float r, float g , float b){ - super(world, 0, 0, 0, 0, 0, 0, r, g, b, maxAge); - this.angle = this.rand.nextDouble() * Math.PI * 2; - double x = originX - Math.cos(angle)*radius; - double z = originZ + radius*Math.sin(angle); - this.radius = radius; - this.setPosition(x, yPos, z); - this.prevPosX = x; - this.prevPosY = yPos; - this.prevPosZ = z; - if(rand.nextBoolean()){ - speed = rand.nextDouble()*2 + 1; - }else{ - speed = rand.nextDouble()*-2 - 1; - } - this.multipleParticleScaleBy(1.5f); - } - - @Override - public void onUpdate(){ - - this.prevPosX = this.posX; - this.prevPosY = this.posY; - this.prevPosZ = this.posZ; - - if(this.particleAge++ >= this.particleMaxAge){ - this.setExpired(); - } - - // This is in radians per tick... - double omega = Math.signum(speed) * ((Math.PI*2)/20 - speed/(20*radius)); - - // v = r times omega; therefore the normalised velocity vector needs to be r times the angle increment / 2 pi. - this.angle += omega; - - this.motionY -= 0.04D * (double)this.particleGravity; - this.motionZ = radius * omega * Math.cos(angle); - this.motionX = radius * omega * Math.sin(angle); - this.moveEntity(motionX, motionY, motionZ); - - if(this.particleAge > this.particleMaxAge / 2){ - this.setAlphaF(1.0F - ((float)this.particleAge - (float)(this.particleMaxAge / 2)) / (float)this.particleMaxAge); - } - - } -} diff --git a/src/main/java/electroblob/wizardry/client/particle/ParticleSnow.java b/src/main/java/electroblob/wizardry/client/particle/ParticleSnow.java deleted file mode 100644 index a1455c22..00000000 --- a/src/main/java/electroblob/wizardry/client/particle/ParticleSnow.java +++ /dev/null @@ -1,33 +0,0 @@ -package electroblob.wizardry.client.particle; - -import electroblob.wizardry.Wizardry; -import net.minecraft.util.ResourceLocation; -import net.minecraft.world.World; -import net.minecraftforge.fml.relauncher.Side; -import net.minecraftforge.fml.relauncher.SideOnly; - -@SideOnly(Side.CLIENT) -public class ParticleSnow extends ParticleCustomTexture { - - private static final ResourceLocation TEXTURE = new ResourceLocation(Wizardry.MODID, "textures/particle/snow_particles.png"); - - public ParticleSnow(World world, double x, double y, double z, double vx, double vy, double vz){ - super(world, x, y, z, vx, vy, vz); - } - - public ParticleSnow(World world, double x, double y, double z, double vx, double vy, double vz, int maxAge){ - super(world, x, y, z, vx, vy, vz, maxAge); - } - - @Override - public void init(){ - this.setParticleTextureIndex(rand.nextInt(8)); - this.particleScale *= 0.6f; - this.particleGravity = 0; - this.canCollide = true; - } - - @Override public ResourceLocation getTexture(){ return TEXTURE; } - @Override protected int getXFrames(){ return 4; } - @Override protected int getYFrames(){ return 4; } -} diff --git a/src/main/java/electroblob/wizardry/client/particle/ParticleSpark.java b/src/main/java/electroblob/wizardry/client/particle/ParticleSpark.java deleted file mode 100644 index 57965ed2..00000000 --- a/src/main/java/electroblob/wizardry/client/particle/ParticleSpark.java +++ /dev/null @@ -1,58 +0,0 @@ -package electroblob.wizardry.client.particle; - -import org.lwjgl.opengl.GL11; - -import electroblob.wizardry.Wizardry; -import net.minecraft.client.renderer.GlStateManager; -import net.minecraft.client.renderer.OpenGlHelper; -import net.minecraft.util.ResourceLocation; -import net.minecraft.world.World; -import net.minecraftforge.fml.relauncher.Side; -import net.minecraftforge.fml.relauncher.SideOnly; - -@SideOnly(Side.CLIENT) -public class ParticleSpark extends ParticleCustomTexture { - - private static final ResourceLocation TEXTURE = new ResourceLocation(Wizardry.MODID, "textures/particle/lightning_particles.png"); - - public ParticleSpark(World world, double x, double y, double z, double vx, double vy, double vz){ - // Max age is always 3. - super(world, x, y, z, vx, vy, vz, 3); - } - - @Override - public void init(){ - // Multiplied by 4 because the index works slightly differently for spark particles. - this.setParticleTextureIndex(rand.nextInt(8)*4); - this.particleScale *= 1.4f; - this.fullBrightness = true; - this.canCollide = false; - } - - @Override - public void onUpdate(){ - super.onUpdate(); - // Well this is handy! Looks like vanilla uses the texture index like this too. - this.nextTextureIndexX(); - } - - @Override public ResourceLocation getTexture(){ return TEXTURE; } - @Override protected int getXFrames(){ return 4; } - @Override protected int getYFrames(){ return 8; } - - @Override - public void applyGLStateChanges(){ - GlStateManager.enableBlend(); - GlStateManager.blendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA); - // TESTME: Are these two actually necessary? - GlStateManager.disableLighting(); - OpenGlHelper.setLightmapTextureCoords(OpenGlHelper.lightmapTexUnit, 240, 240); - } - - @Override - public void undoGLStateChanges(){ - GlStateManager.disableBlend(); - GlStateManager.enableLighting(); - } - -} diff --git a/src/main/java/electroblob/wizardry/client/particle/ParticleSparkle.java b/src/main/java/electroblob/wizardry/client/particle/ParticleSparkle.java deleted file mode 100644 index da0e461d..00000000 --- a/src/main/java/electroblob/wizardry/client/particle/ParticleSparkle.java +++ /dev/null @@ -1,96 +0,0 @@ -package electroblob.wizardry.client.particle; - -import electroblob.wizardry.Wizardry; -import net.minecraft.util.ResourceLocation; -import net.minecraft.world.World; -import net.minecraftforge.fml.relauncher.Side; -import net.minecraftforge.fml.relauncher.SideOnly; - -@SideOnly(Side.CLIENT) -public class ParticleSparkle extends ParticleCustomTexture { - - /* I have now figured out what particle factories are for: they separate out the individual uses of the varargs - * parameter in spawnParticle so they are kept with the particle class. For my purposes, it would be easier to do - * that in the particle spawning method itself. */ - - private static final ResourceLocation TEXTURE = new ResourceLocation(Wizardry.MODID, "textures/particle/sparkle_particles.png"); - - // NOTE: Uncomment once 2.1.0 is released - //private final float initialRed; - //private final float initialGreen; - //private final float initialBlue; - - // TODO: Assign these via the constructors, as part of the refactoring for particle parameters. - // NOTE: Uncomment once 2.1.0 is released -// private final float fadeRed = 1; -// private final float fadeGreen = 1; -// private final float fadeBlue = 0; - - public ParticleSparkle(World world, double x, double y, double z, double vx, double vy, double vz, float r, float g, float b){ - super(world, x, y, z, vx, vy, vz); - this.setRBGColorF(r, g, b); - // NOTE: Uncomment once 2.1.0 is released - //initialRed = r; - //initialGreen = g; - //initialBlue = b; - this.particleMaxAge = 48 + this.rand.nextInt(12); - } - - public ParticleSparkle(World world, double x, double y, double z, double vx, double vy, double vz, float r, float g, float b, int maxAge){ - super(world, x, y, z, vx, vy, vz, maxAge); - this.setRBGColorF(r, g, b); - // NOTE: Uncomment once 2.1.0 is released - //initialRed = r; - //initialGreen = g; - //initialBlue = b; - } - - public ParticleSparkle(World world, double x, double y, double z, double vx, double vy, double vz, float r, float g, float b, boolean doGravity){ - this(world, x, y, z, vx, vy, vz, r, g, b); - this.particleGravity = doGravity ? 1 : 0; - } - - public ParticleSparkle(World world, double x, double y, double z, double vx, double vy, double vz, float r, float g, float b, int maxAge, boolean doGravity){ - this(world, x, y, z, vx, vy, vz, r, g, b, maxAge); - this.particleGravity = doGravity ? 1 : 0; - } - - @Override - public void init(){ - this.setParticleTextureIndex(rand.nextInt(16)); - this.particleScale *= 0.75f; - this.particleGravity = 0; - this.canCollide = false; - this.fullBrightness = true; - } - - @Override public ResourceLocation getTexture(){ return TEXTURE; } - @Override protected int getXFrames(){ return 4; } - @Override protected int getYFrames(){ return 4; } - - @Override - public void onUpdate(){ - - super.onUpdate(); - // Fading - if(this.particleAge > this.particleMaxAge / 2){ - this.setAlphaF(1.0F - ((float)this.particleAge - (float)(this.particleMaxAge / 2)) / (float)this.particleMaxAge); - } - // Colour fading TODO Uncomment once 2.1.0 is released -// float ageFraction = (float)this.particleAge / (float)this.particleMaxAge; -// this.setRBGColorF(this.initialRed + (this.fadeRed - this.initialRed)*ageFraction, -// this.initialGreen + (this.fadeGreen - this.initialGreen)*ageFraction, -// this.initialBlue + (this.fadeBlue - this.initialBlue)*ageFraction); - - this.setParticleTextureIndex((this.particleAge * 11)/this.particleMaxAge); - } - - /* - * As a side note, I see a lot of magic mods with fancy-looking particle effects that really seem to 'glow'. It's - * actually not that hard - you simply create a reasonably high-res texture with translucency and then set the - * OpenGL blend function to something like SRC_ALPHA, SRC_ALPHA or ONE, ONE. The thing is... they're not very - * Minecraft-y. I still maintain that part of wizardry's appeal is that it stays true to the game's pixelated charm, - * rather than trying to make it something it's not. Still, the newer textures are much better than the defaults I - * used to use. - */ -} diff --git a/src/main/java/electroblob/wizardry/client/particle/ParticleTornado.java b/src/main/java/electroblob/wizardry/client/particle/ParticleTornado.java deleted file mode 100644 index 1dd5635b..00000000 --- a/src/main/java/electroblob/wizardry/client/particle/ParticleTornado.java +++ /dev/null @@ -1,83 +0,0 @@ -package electroblob.wizardry.client.particle; - -import net.minecraft.block.state.IBlockState; -import net.minecraft.client.particle.ParticleDigging; -import net.minecraft.util.math.BlockPos; -import net.minecraft.world.World; -import net.minecraftforge.fml.relauncher.Side; -import net.minecraftforge.fml.relauncher.SideOnly; - -@SideOnly(Side.CLIENT) -public class ParticleTornado extends ParticleDigging { - - private double angle; - private double radius; - private double speed; - /** Velocity of the tornado itself; in other words the velocity of the point the particle circles around. */ - private double velX, velZ; - private boolean fullBrightness = false; - - public ParticleTornado(World world, int maxAge, double originX, double originZ, double radius, double yPos, double velX, double velZ, IBlockState block){ - super(world, 0, 0, 0, 0, 0, 0, block); - this.angle = this.rand.nextDouble() * Math.PI * 2; - double x = originX - Math.cos(angle)*radius; - double z = originZ + radius*Math.sin(angle); - this.radius = radius; - this.setPosition(x, yPos, z); - this.prevPosX = x; - this.prevPosY = yPos; - this.prevPosZ = z; - //this.particleScale *= 0.75F; - this.particleMaxAge = maxAge; - this.canCollide = false; - // Grass has special treatment, since it has a colourised top but the rest is normal. - // Commented out for now since vanilla does something about this now, but I'm not sure what exactly - //if(block.getBlock() != Blocks.GRASS || side == 1) this.setColour(block.getRenderColor(side)); - - // Blocks that emit light are rendered with full brightness. - if(block.getLightValue(world, new BlockPos(this.posX, this.posY, this.posZ)) == 0){ - this.particleRed *= 0.75; - this.particleGreen *= 0.75; - this.particleBlue *= 0.75; - }else{ - this.fullBrightness = true; - } - - speed = rand.nextDouble()*2 + 1; - this.velX = velX; - this.velZ = velZ; - } - - @Override - public void onUpdate(){ - - this.prevPosX = this.posX; - this.prevPosY = this.posY; - this.prevPosZ = this.posZ; - - if(this.particleAge++ >= this.particleMaxAge){ - this.setExpired(); - } - - // This is in radians per tick... - double omega = Math.signum(speed) * ((Math.PI*2)/20 - speed/(20*radius)); - - // v = r times omega; therefore the normalised velocity vector needs to be r times the angle increment / 2 pi. - this.angle += omega; - - this.motionZ = radius * omega * Math.cos(angle); - this.motionX = radius * omega * Math.sin(angle); - this.moveEntity(motionX + velX, 0, motionZ + velZ); - - if(this.particleAge > this.particleMaxAge / 2){ - this.setAlphaF(1.0F - ((float)this.particleAge - (float)(this.particleMaxAge / 2)) / (float)this.particleMaxAge); - } - - } - - @Override - public int getBrightnessForRender(float partialTicks){ - return fullBrightness ? 15728880 : super.getBrightnessForRender(partialTicks); - } - -} diff --git a/src/main/java/electroblob/wizardry/client/renderer/LayerStone.java b/src/main/java/electroblob/wizardry/client/renderer/LayerStone.java deleted file mode 100644 index 4365a842..00000000 --- a/src/main/java/electroblob/wizardry/client/renderer/LayerStone.java +++ /dev/null @@ -1,191 +0,0 @@ -package electroblob.wizardry.client.renderer; - -import java.lang.reflect.Field; -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; -import java.util.List; -import java.util.Map.Entry; - -import org.lwjgl.opengl.GL11; - -import electroblob.wizardry.Wizardry; -import electroblob.wizardry.client.ClientProxy; -import electroblob.wizardry.spell.Petrify; -import net.minecraft.client.Minecraft; -import net.minecraft.client.model.ModelBase; -import net.minecraft.client.model.ModelBiped; -import net.minecraft.client.renderer.GlStateManager; -import net.minecraft.client.renderer.GlStateManager.DestFactor; -import net.minecraft.client.renderer.GlStateManager.SourceFactor; -import net.minecraft.client.renderer.OpenGlHelper; -import net.minecraft.client.renderer.entity.Render; -import net.minecraft.client.renderer.entity.RenderLivingBase; -import net.minecraft.client.renderer.entity.RenderZombie; -import net.minecraft.client.renderer.entity.layers.LayerRenderer; -import net.minecraft.entity.Entity; -import net.minecraft.entity.EntityLivingBase; -import net.minecraft.entity.monster.EntityZombie; -import net.minecraft.util.ResourceLocation; -import net.minecraft.util.math.BlockPos; -import net.minecraft.util.math.MathHelper; -import net.minecraftforge.fml.relauncher.ReflectionHelper; - -/** - * Layer used to render the stone texture on a petrified creature. Handles dynamic tiling of the stone texture and - * reflective access for classes that don't play nicely (looking at you, {@link RenderZombie}). - * @author Electroblob - * @since Wizardry 1.2 - */ -public class LayerStone implements LayerRenderer { - - protected ModelBase model; - private final RenderLivingBase renderer; - - private static final ResourceLocation texture = new ResourceLocation("textures/blocks/stone.png"); - - private static final Field zombieLayers = ReflectionHelper.findField(RenderZombie.class, "defaultLayers", "field_177122_o"); - private static final Field zombieVillagerLayers = ReflectionHelper.findField(RenderZombie.class, "villagerLayers", "field_177121_n"); - private static final Method swapZombieModel = ReflectionHelper.findMethod(RenderZombie.class, null, - new String[]{"swapArmor", "func_82427_a"}, EntityZombie.class); // Second parameter (null) is unused - - @SuppressWarnings("unchecked") // The compiler is being annoying and I know that what I'm doing is type-safe. - public static void initialiseLayers(){ - for(Entry, Render> entry : Minecraft.getMinecraft().getRenderManager().entityRenderMap.entrySet()){ - // Zombies don't play nicely because they have their own, private lists of layers for the regular zombie - // and the zombie villager, which are assigned within the constructor for RenderZombie and swapped out - // as necessary. However, Mojang, in their infinite wisdom, haven't bothered to override addLayer to - // modify those internal lists, so that method is useless. - if(entry.getValue() instanceof RenderZombie){ - try { - Object layers = zombieLayers.get(entry.getValue()); - if(layers instanceof List){ - // Nice as the layer renderer system is, it doesn't lend itself to reflective access. - // I KNOW that 'layers' (which was obtained using reflection) is of the type - // List>, because that's what it's declared as. However, if I cast - // 'layers' to List>, I can't add a LayerStone because it's only a - // LayerRenderer, not a LayerRenderer. - ((List>)layers).add(new LayerStone((RenderLivingBase)entry.getValue())); - } - layers = zombieVillagerLayers.get(entry.getValue()); - if(layers instanceof List){ - ((List>)layers).add(new LayerStone((RenderLivingBase)entry.getValue())); - } - } catch (IllegalArgumentException | IllegalAccessException e){ - Wizardry.logger.error("Error while reflectively accessing zombie render layers"); - e.printStackTrace(); - } - }else if(entry.getValue() instanceof RenderLivingBase){ - // Adds a stone layer to all the living entity renderers in the game. Whether it is actually rendered - // is decided in doRenderLayer below on a per-entity basis. - ((RenderLivingBase)entry.getValue()).addLayer(new LayerStone((RenderLivingBase)entry.getValue())); - } - // NOTE: May have to do some special stuff for players if they are to be added; see Minecraft.getMinecraft().getRenderManager().getSkinMap() - } - } - - public LayerStone(RenderLivingBase renderer){ - this.renderer = renderer; - this.model = renderer.getMainModel(); - } - - @Override - public void doRenderLayer(EntityLivingBase entity, float limbSwing, float limbSwingAmount, float partialTicks, float ageInTicks, float netHeadYaw, float headPitch, float scale){ - - if(entity.getEntityData().getBoolean(Petrify.NBT_KEY)){ - - GlStateManager.enableLighting(); - int i = this.getBlockBrightnessForEntity(entity, partialTicks); - - int j = i % 65536; - int k = i / 65536; - OpenGlHelper.setLightmapTextureCoords(OpenGlHelper.lightmapTexUnit, (float)j / 1.0F, (float)k / 1.0F); - - ResourceLocation breakingTexture = ClientProxy.renderStatue.getBlockBreakingTexture(); - - if(breakingTexture != null){ - // Block breaking animation - // TODO: Spider eyes and enderman eyes (any others?) show through the stone when the block is being broken... - GlStateManager.enableBlend(); - GlStateManager.blendFunc(SourceFactor.SRC_ALPHA, DestFactor.ONE_MINUS_SRC_ALPHA); - this.renderer.bindTexture(breakingTexture); - this.renderEntityModel(entity, limbSwing, limbSwingAmount, partialTicks, ageInTicks, netHeadYaw, headPitch, scale); - GlStateManager.disableBlend(); - }else{ - // Stone texture - this.renderer.bindTexture(texture); - this.renderEntityModel(entity, limbSwing, limbSwingAmount, partialTicks, ageInTicks, netHeadYaw, headPitch, scale); - } - } - } - - private int getBlockBrightnessForEntity(Entity entity, float partialTicks){ - - BlockPos.MutableBlockPos pos = new BlockPos.MutableBlockPos(MathHelper.floor_double(entity.posX), 0, MathHelper.floor_double(entity.posZ)); - - if(entity.worldObj.isBlockLoaded(pos)){ - pos.setY(MathHelper.floor_double(entity.posY + (double)entity.getEyeHeight())); - return entity.worldObj.getCombinedLight(pos, 0); - }else{ - return 0; - } - } - - private void renderEntityModel(EntityLivingBase entity, float limbSwing, float limbSwingAmount, float partialTicks, float ageInTicks, float netHeadYaw, float headPitch, float scale){ - - GlStateManager.pushMatrix(); - // Enables tiling (Also used for guardian beam, beacon beam and ender crystal beam) - // TODO: Backport this improvement - GlStateManager.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_WRAP_S, GL11.GL_REPEAT); - GlStateManager.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_WRAP_T, GL11.GL_REPEAT); - - GlStateManager.depthMask(true); // Some entities set depth mask to false (i.e. no sorting of faces by depth) - // In particular, LayerSpiderEyes sets it to false when the spider is invisible, for some reason. - - // Changes the scale at which the texture is applied to the model. See LayerCreeper for a similar example, - // but with translation instead of scaling. - GlStateManager.matrixMode(GL11.GL_TEXTURE); - GlStateManager.loadIdentity(); - double scaleX = 1, scaleY = 1; - // It's more logical to use the model's texture size, but some classes don't bother setting it properly - // (e.g. ModelVillager), so to get the correct dimensions I'm getting them from the first box instead. - if(model.boxList != null && model.boxList.get(0) != null){ - scaleX = (double)model.boxList.get(0).textureWidth/16d; - scaleY = (double)model.boxList.get(0).textureHeight/16d; - }else{ // Fallback to model fields; should never be needed - scaleX = (double)model.textureWidth/16d; - scaleY = (double)model.textureHeight/16d; - } - GlStateManager.scale(scaleX, scaleY, 1); - GlStateManager.matrixMode(GL11.GL_MODELVIEW); - - // Lets RenderZombie do its (stupid and inflexible) model switching thing - if(this.renderer instanceof RenderZombie && entity instanceof EntityZombie){ - try { - swapZombieModel.invoke(this.renderer, entity); - this.model = this.renderer.getMainModel(); - } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) { - Wizardry.logger.error("Error while reflectively calling RenderZombie#swapArmor"); - e.printStackTrace(); - } - } - // Hides the hat layer for bipeds - if(this.model instanceof ModelBiped) ((ModelBiped) this.model).bipedHeadwear.isHidden = true; - - this.model.setLivingAnimations(entity, limbSwing, limbSwingAmount, partialTicks); - this.model.render(entity, limbSwing, limbSwingAmount, ageInTicks, netHeadYaw, headPitch, scale); - - if(this.model instanceof ModelBiped) ((ModelBiped) this.model).bipedHeadwear.isHidden = false; - - // Undoes the texture scaling - GlStateManager.matrixMode(GL11.GL_TEXTURE); - GlStateManager.loadIdentity(); - GlStateManager.matrixMode(GL11.GL_MODELVIEW); - - GlStateManager.popMatrix(); - } - - @Override - public boolean shouldCombineTextures(){ - return false; - } -} \ No newline at end of file diff --git a/src/main/java/electroblob/wizardry/client/renderer/RenderArc.java b/src/main/java/electroblob/wizardry/client/renderer/RenderArc.java index 6bf600d1..cb08aefd 100644 --- a/src/main/java/electroblob/wizardry/client/renderer/RenderArc.java +++ b/src/main/java/electroblob/wizardry/client/renderer/RenderArc.java @@ -1,129 +1,136 @@ package electroblob.wizardry.client.renderer; -import org.lwjgl.opengl.GL11; +import java.util.Random; + +import org.lwjgl.opengl.GL11; +import org.lwjgl.opengl.GL12; -import electroblob.wizardry.Wizardry; import electroblob.wizardry.entity.EntityArc; -import net.minecraft.client.renderer.GlStateManager; +import electroblob.wizardry.entity.projectile.EntityMagicMissile; +import net.minecraft.client.Minecraft; +import net.minecraft.client.entity.EntityClientPlayerMP; import net.minecraft.client.renderer.OpenGlHelper; import net.minecraft.client.renderer.Tessellator; -import net.minecraft.client.renderer.VertexBuffer; import net.minecraft.client.renderer.entity.Render; -import net.minecraft.client.renderer.entity.RenderManager; -import net.minecraft.client.renderer.vertex.DefaultVertexFormats; +import net.minecraft.entity.Entity; +import net.minecraft.util.MathHelper; import net.minecraft.util.ResourceLocation; -public class RenderArc extends Render { +public class RenderArc extends Render { private static final ResourceLocation[] textures = new ResourceLocation[16]; - - public RenderArc(RenderManager renderManager){ - super(renderManager); + + public RenderArc(){ for(int i=0;i<16;i++){ - textures[i] = new ResourceLocation(Wizardry.MODID, "textures/entity/arc_" + i + ".png"); + textures[i] = new ResourceLocation("wizardry:textures/entity/arc_" + i + ".png"); } } - + @Override - public void doRender(EntityArc arc, double d0, double d1, double d2, + public void doRender(Entity entity, double d0, double d1, double d2, float fa, float fb) { - GlStateManager.pushMatrix(); - GlStateManager.translate((float)d0, (float)d1, (float)d2); - GlStateManager.disableLighting(); - GlStateManager.enableBlend(); - GlStateManager.blendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA); //This line fixes the weird brightness bug. - OpenGlHelper.setLightmapTextureCoords(OpenGlHelper.lightmapTexUnit, 240f, 240f); - + GL11.glPushMatrix(); + GL11.glTranslatef((float)d0, (float)d1, (float)d2); + GL11.glDisable(GL11.GL_LIGHTING); + GL11.glEnable(GL11.GL_BLEND); + GL11.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA); //This line fixes the weird brightness bug. + OpenGlHelper.setLightmapTextureCoords(OpenGlHelper.lightmapTexUnit, 240f, 240f); + //System.out.println("Entity coords: " + entity.posX + ", " + entity.posY + ", " + entity.posZ); //System.out.println("doRender parameters: " + d0 + ", " + d1 + ", " + d2 + ", " + fa + ", " + fb); - - Tessellator tessellator = Tessellator.getInstance(); - VertexBuffer buffer = tessellator.getBuffer(); - - bindTexture(textures[arc.textureIndex]); // This MUST be after the tessellator declaration and the gl stuff - - /** - * Note: A lot of the maths here works on similar triangles and the ratios between them, avoiding too much - * pythagoras and eliminating the need for any trig. Ratios are used for the positioning of the arc endpoints. - * Ratios are usually used swapping x and z because the triangles are rotated through 90 degrees. - */ - - double dx = -d0; - double dy = -d1; - double dz = -d2; - - if(arc.x1 != 0){ - dx = arc.x1 - arc.posX;// - d2/lengthOffsetRatio; - dy = arc.y1 - arc.posY + 0.3; - dz = arc.z1 - arc.posZ;// + d0/lengthOffsetRatio; - - - //The distance from caster to target - double arcLength = Math.sqrt(dz*dz+dx*dx); - - //The ratio between the length of the arc and the offset of the start point from the player's centre (which is always 0.3). - //double lengthOffsetRatio = arcLength/0.3; - - //EntityClientPlayerMP player = Minecraft.getMinecraft().thePlayer; - - //double xViewDist = player.posX - d0; - //double yViewDist = player.posY + player.eyeHeight - d1; - //double zViewDist = player.posZ - d2; - - //double xzViewDist = Math.sqrt(xViewDist * xViewDist + zViewDist * zViewDist); - - //The angle above the horizontal that this particular player is viewing the arc from - //double viewAngle = Math.atan(yViewDist/xzViewDist); - - //Half the width of the arc - double arcWidth = 0.3d; - - //Right hand side of vertical plane - buffer.begin(GL11.GL_QUADS, DefaultVertexFormats.POSITION_TEX); - //Target end - buffer.pos(0, -0.5, 0).tex(1, 1).endVertex(); - buffer.pos(0, 0.5, 0).tex(1, 0).endVertex(); - //Caster end - buffer.pos(dx, dy, dz).tex(0, 0).endVertex(); - buffer.pos(dx, dy-1, dz).tex(0, 1).endVertex(); - tessellator.draw(); - - //Left - buffer.begin(GL11.GL_QUADS, net.minecraft.client.renderer.vertex.DefaultVertexFormats.POSITION_TEX); - //Target end - buffer.pos(0, -0.5, 0).tex(1, 1).endVertex(); - //Caster end - buffer.pos(dx, dy-1, dz).tex(0, 1).endVertex(); - buffer.pos(dx, dy, dz).tex(0, 0).endVertex(); - //Target end - buffer.pos(0, 0.5, 0).tex(1, 0).endVertex(); - tessellator.draw(); - - //Bottom of horizontal plane - buffer.begin(GL11.GL_QUADS, net.minecraft.client.renderer.vertex.DefaultVertexFormats.POSITION_TEX); - buffer.pos((arcWidth/arcLength)*dz, 0, (-arcWidth/arcLength)*dx).tex(1, 1).endVertex(); - buffer.pos(dx + (arcWidth/arcLength)*dz, dy-0.5, dz - (arcWidth/arcLength)*dx).tex(0, 1).endVertex(); - buffer.pos(dx - (arcWidth/arcLength)*dz, dy-0.5, dz + (arcWidth/arcLength)*dx).tex(0, 0).endVertex(); - buffer.pos((-arcWidth/arcLength)*dz, 0, (arcWidth/arcLength)*dx).tex(1, 0).endVertex(); - tessellator.draw(); - - //Top - buffer.begin(GL11.GL_QUADS, net.minecraft.client.renderer.vertex.DefaultVertexFormats.POSITION_TEX); - buffer.pos((arcWidth/arcLength)*dz, 0, (-arcWidth/arcLength)*dx).tex(1, 1).endVertex(); - buffer.pos((-arcWidth/arcLength)*dz, 0, (arcWidth/arcLength)*dx).tex(1, 0).endVertex(); - buffer.pos(dx - (arcWidth/arcLength)*dz, dy-0.5, dz + (arcWidth/arcLength)*dx).tex(0, 0).endVertex(); - buffer.pos(dx + (arcWidth/arcLength)*dz, dy-0.5, dz - (arcWidth/arcLength)*dx).tex(0, 1).endVertex(); - tessellator.draw(); - } - - GlStateManager.enableLighting(); - GlStateManager.disableBlend(); - GlStateManager.popMatrix(); + + Tessellator tessellator = Tessellator.instance; + + if(entity instanceof EntityArc){ + EntityArc arc = (EntityArc)entity; + bindTexture(textures[arc.textureIndex]); // This MUST be after the tessellator declaration and the gl stuff + + /** + * Note: A lot of the maths here works on similar triangles and the ratios between them, avoiding too much + * pythagoras and eliminating the need for any trig. Ratios are used for the positioning of the arc endpoints. + * Ratios are usually used swapping x and z because the triangles are rotated through 90 degrees. + */ + + double dx = -d0; + double dy = -d1; + double dz = -d2; + + if(arc.x1 != 0){ + dx = arc.x1 - arc.posX;// - d2/lengthOffsetRatio; + dy = arc.y1 - arc.posY + 0.3; + dz = arc.z1 - arc.posZ;// + d0/lengthOffsetRatio; + + + //The distance from caster to target + double arcLength = Math.sqrt(dz*dz+dx*dx); + + //The ratio between the length of the arc and the offset of the start point from the player's centre (which is always 0.3). + double lengthOffsetRatio = arcLength/0.3; + + //EntityClientPlayerMP player = Minecraft.getMinecraft().thePlayer; + + //double xViewDist = player.posX - d0; + //double yViewDist = player.posY + player.eyeHeight - d1; + //double zViewDist = player.posZ - d2; + + //double xzViewDist = Math.sqrt(xViewDist * xViewDist + zViewDist * zViewDist); + + //The angle above the horizontal that this particular player is viewing the arc from + //double viewAngle = Math.atan(yViewDist/xzViewDist); + + //Half the width of the arc + double arcWidth = 0.3d; + + //Right hand side of vertical plane + tessellator.startDrawingQuads(); + //Target end + tessellator.addVertexWithUV(0, -0.5, 0, 1, 1); + tessellator.addVertexWithUV(0, 0.5, 0, 1, 0); + //Caster end + tessellator.addVertexWithUV(dx, dy, dz, 0, 0); + tessellator.addVertexWithUV(dx, dy-1, dz, 0, 1); + tessellator.draw(); + + //Left + tessellator.startDrawingQuads(); + //Target end + tessellator.addVertexWithUV(0, -0.5, 0, 1, 1); + //Caster end + tessellator.addVertexWithUV(dx, dy-1, dz, 0, 1); + tessellator.addVertexWithUV(dx, dy, dz, 0, 0); + //Target end + tessellator.addVertexWithUV(0, 0.5, 0, 1, 0); + tessellator.draw(); + + //Bottom of horizontal plane + tessellator.startDrawingQuads(); + tessellator.addVertexWithUV((arcWidth/arcLength)*dz, 0, (-arcWidth/arcLength)*dx, 1, 1); + tessellator.addVertexWithUV(dx + (arcWidth/arcLength)*dz, dy-0.5, dz - (arcWidth/arcLength)*dx, 0, 1); + tessellator.addVertexWithUV(dx - (arcWidth/arcLength)*dz, dy-0.5, dz + (arcWidth/arcLength)*dx, 0, 0); + tessellator.addVertexWithUV((-arcWidth/arcLength)*dz, 0, (arcWidth/arcLength)*dx, 1, 0); + tessellator.draw(); + + //Top + tessellator.startDrawingQuads(); + tessellator.addVertexWithUV((arcWidth/arcLength)*dz, 0, (-arcWidth/arcLength)*dx, 1, 1); + tessellator.addVertexWithUV((-arcWidth/arcLength)*dz, 0, (arcWidth/arcLength)*dx, 1, 0); + tessellator.addVertexWithUV(dx - (arcWidth/arcLength)*dz, dy-0.5, dz + (arcWidth/arcLength)*dx, 0, 0); + tessellator.addVertexWithUV(dx + (arcWidth/arcLength)*dz, dy-0.5, dz - (arcWidth/arcLength)*dx, 0, 1); + tessellator.draw(); + } + } + + GL11.glEnable(GL11.GL_LIGHTING); + GL11.glDisable(GL11.GL_BLEND); + GL11.glPopMatrix(); } @Override - protected ResourceLocation getEntityTexture(EntityArc entity) { - return textures[entity.textureIndex]; + protected ResourceLocation getEntityTexture(Entity entity) { + if(entity instanceof EntityArc){ + return textures[((EntityArc)entity).textureIndex]; + } + return null; } } \ No newline at end of file diff --git a/src/main/java/electroblob/wizardry/client/renderer/RenderArcaneWorkbench.java b/src/main/java/electroblob/wizardry/client/renderer/RenderArcaneWorkbench.java index 12203694..600527cc 100644 --- a/src/main/java/electroblob/wizardry/client/renderer/RenderArcaneWorkbench.java +++ b/src/main/java/electroblob/wizardry/client/renderer/RenderArcaneWorkbench.java @@ -1,45 +1,48 @@ package electroblob.wizardry.client.renderer; +import net.minecraft.client.renderer.OpenGlHelper; +import net.minecraft.client.renderer.RenderBlocks; +import net.minecraft.client.renderer.Tessellator; +import net.minecraft.client.renderer.entity.RenderItem; +import net.minecraft.client.renderer.entity.RenderManager; +import net.minecraft.client.renderer.tileentity.TileEntitySpecialRenderer; +import net.minecraft.entity.item.EntityItem; +import net.minecraft.item.ItemStack; +import net.minecraft.tileentity.TileEntity; +import net.minecraft.util.ResourceLocation; + import org.lwjgl.opengl.GL11; -import electroblob.wizardry.Wizardry; import electroblob.wizardry.tileentity.ContainerArcaneWorkbench; import electroblob.wizardry.tileentity.TileEntityArcaneWorkbench; -import net.minecraft.client.Minecraft; -import net.minecraft.client.renderer.GlStateManager; -import net.minecraft.client.renderer.OpenGlHelper; -import net.minecraft.client.renderer.Tessellator; -import net.minecraft.client.renderer.VertexBuffer; -import net.minecraft.client.renderer.block.model.ItemCameraTransforms.TransformType; -import net.minecraft.client.renderer.tileentity.TileEntitySpecialRenderer; -import net.minecraft.client.renderer.vertex.DefaultVertexFormats; -import net.minecraft.item.ItemStack; -import net.minecraft.util.ResourceLocation; -public class RenderArcaneWorkbench extends TileEntitySpecialRenderer { - - private static final ResourceLocation runeTexture = new ResourceLocation(Wizardry.MODID, "textures/entity/rune.png"); +public class RenderArcaneWorkbench extends TileEntitySpecialRenderer +{ + private final RenderBlocks renderBlocksInstance = new RenderBlocks(); + private static final ResourceLocation runeTexture = new ResourceLocation("wizardry:textures/entity/rune.png"); - public RenderArcaneWorkbench(){} + public RenderArcaneWorkbench(){ + } @Override - public void renderTileEntityAt(TileEntityArcaneWorkbench tileentity, double x, double y, double z, float partialTicks, int destroyStage) { + public void renderTileEntityAt(TileEntity tileentity, double x, double y, double z, float f) { - GlStateManager.pushMatrix(); - // This line makes stuff render in the same place relative to the world wherever the player is. - GlStateManager.translate((float)x + 0.5F, (float)y + 1.5F, (float)z + 0.5F); - GlStateManager.rotate(180, 0F, 0F, 1F); - GlStateManager.pushMatrix(); + GL11.glPushMatrix(); + // It seems that these next two lines make stuff render in the same place relative to the world wherever + // the player is. + GL11.glTranslatef((float)x + 0.5F, (float)y +1.5F, (float)z + 0.5F); + GL11.glRotatef(180, 0F, 0F, 1F); + GL11.glPushMatrix(); double angle = 0.0d; if(x < -0.5){ angle = Math.toDegrees(Math.atan((z+0.5)/(x+0.5))) + 180; }else{ angle = Math.toDegrees(Math.atan((z+0.5)/(x+0.5))); } - this.renderEffect(tileentity); - this.renderWand(tileentity, angle); - GlStateManager.popMatrix(); - GlStateManager.popMatrix(); + this.renderEffect((TileEntityArcaneWorkbench)tileentity); + this.renderWand((TileEntityArcaneWorkbench)tileentity, angle); + GL11.glPopMatrix(); + GL11.glPopMatrix(); } private void renderEffect(TileEntityArcaneWorkbench tileentity) { @@ -47,27 +50,26 @@ public class RenderArcaneWorkbench extends TileEntitySpecialRenderer { - - private static final ResourceLocation texture = new ResourceLocation(Wizardry.MODID, "textures/entity/dark_ray.png"); - private static final ResourceLocation texture2 = new ResourceLocation(Wizardry.MODID, "textures/entity/black_hole.png"); - - public RenderBlackHole(RenderManager renderManager) { - super(renderManager); - } +public class RenderBlackHole extends Render{ + private static final ResourceLocation texture = new ResourceLocation("wizardry:textures/entity/dark_ray.png"); + private static final ResourceLocation texture2 = new ResourceLocation("wizardry:textures/entity/black_hole.png"); + @Override - public void doRender(EntityBlackHole blackhole, double x, double y, double z, float fa, float fb) { + public void doRender(Entity entity, double x, double y, double z, + float fa, float fb) { - GlStateManager.pushMatrix(); + GL11.glPushMatrix(); - GlStateManager.disableCull(); - GlStateManager.enableBlend(); - GlStateManager.blendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA); - GlStateManager.shadeModel(GL11.GL_SMOOTH); - GlStateManager.disableLighting(); - OpenGlHelper.setLightmapTextureCoords(OpenGlHelper.lightmapTexUnit, 240f, 240f); - RenderHelper.disableStandardItemLighting(); - - GlStateManager.translate(x, y, z); + GL11.glDisable(GL11.GL_CULL_FACE); + GL11.glEnable(GL11.GL_BLEND); + GL11.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA); + GL11.glShadeModel(GL11.GL_SMOOTH); + GL11.glDisable(GL11.GL_LIGHTING); + OpenGlHelper.setLightmapTextureCoords(OpenGlHelper.lightmapTexUnit, 240f, 240f); + RenderHelper.disableStandardItemLighting(); + GL11.glTranslated(x, y, z); + //float pitch = (float) Math.toDegrees(Math.atan(y/(x*x+z*z))); //float yaw = (float) Math.toDegrees(Math.atan(x/z)); + + Tessellator tessellator = Tessellator.instance; + + GL11.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA); + + // Renders the rays + if(entity instanceof EntityBlackHole){ + + EntityBlackHole blackhole = (EntityBlackHole)entity; + + if(blackhole.ticksExisted < 10){ + GL11.glScalef((float)blackhole.ticksExisted/10, (float)blackhole.ticksExisted/10, (float)blackhole.ticksExisted/10); + } + if(blackhole.ticksExisted > blackhole.lifetime - 10){ + GL11.glScalef((float)(blackhole.lifetime-blackhole.ticksExisted)/10, (float)(blackhole.lifetime-blackhole.ticksExisted)/10, (float)(blackhole.lifetime-blackhole.ticksExisted)/10); + } + + this.bindTexture(texture); + + // In theory this stuff should sort the rays into the correct render order based on the distance from + // the 'camera' (i.e. the player's viewpoint) + // In the end it was easier to do away with the openGL rotation because for some reason it produced + // coordinates which were inconsistent with my calculated ones. Since I know those will give the desired + // effect anyway, I just used them directly instead. + + ArrayList rays = new ArrayList(1); + + for(int j=0; j<30; j++){ - Tessellator tessellator = Tessellator.getInstance(); - VertexBuffer buffer = tessellator.getBuffer(); - - GlStateManager.blendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA); - - if(blackhole.ticksExisted < 10){ - GlStateManager.scale((float)blackhole.ticksExisted/10, (float)blackhole.ticksExisted/10, (float)blackhole.ticksExisted/10); - } - if(blackhole.ticksExisted > blackhole.lifetime - 10){ - GlStateManager.scale((float)(blackhole.lifetime-blackhole.ticksExisted)/10, (float)(blackhole.lifetime-blackhole.ticksExisted)/10, (float)(blackhole.lifetime-blackhole.ticksExisted)/10); - } - - this.bindTexture(texture); - - // In theory this stuff should sort the rays into the correct render order based on the distance from - // the 'camera' (i.e. the player's viewpoint) - // In the end it was easier to do away with the openGL rotation because for some reason it produced - // coordinates which were inconsistent with my calculated ones. Since I know those will give the desired - // effect anyway, I just used them directly instead. - - ArrayList rays = new ArrayList(1); - - for(int j=0; j<30; j++){ - - float scale = 3.0f; - - int a = blackhole.randomiser[j]; - int b = blackhole.randomiser2[j]; - - int sliceAngle = 20 + a; - - double x1 = scale*Math.sin((blackhole.ticksExisted + 40*j)*(Math.PI/180)); - //double y1 = 0.7*Math.cos((blackhole.timer - 40*j)*(Math.PI/180))*j/10; - double z1 = scale*Math.cos((blackhole.ticksExisted + 40*j)*(Math.PI/180)); - - double x2 = scale*Math.sin((blackhole.ticksExisted + 40*j - sliceAngle)*(Math.PI/180)); - //double y2 = 0.7*Math.sin((blackhole.timer - 40*j)*(Math.PI/180))*j/10; - double z2 = scale*Math.cos((blackhole.ticksExisted + 40*j - sliceAngle)*(Math.PI/180)); - - double absoluteX = x1*Math.cos(31*b); - double absoluteY = z1*Math.sin(31*a) + x1*Math.cos(31*a)*Math.sin(31*b); - double absoluteZ = z1*Math.cos(31*a); - - double absoluteX2 = x2*Math.cos(31*b); - double absoluteY2 = z2*Math.sin(31*a) + x2*Math.cos(31*a)*Math.sin(31*b); - double absoluteZ2 = z2*Math.cos(31*a); - /* - buffer.begin(0, DefaultVertexFormats.POSITION_TEX); + float scale = 3.0f; + + int a = blackhole.randomiser[j]; + int b = blackhole.randomiser2[j]; + + int sliceAngle = 20 + a; + + double x1 = scale*Math.sin((blackhole.ticksExisted + 40*j)*(Math.PI/180)); + //double y1 = 0.7*Math.cos((blackhole.timer - 40*j)*(Math.PI/180))*j/10; + double z1 = scale*Math.cos((blackhole.ticksExisted + 40*j)*(Math.PI/180)); + double x2 = scale*Math.sin((blackhole.ticksExisted + 40*j - sliceAngle)*(Math.PI/180)); + //double y2 = 0.7*Math.sin((blackhole.timer - 40*j)*(Math.PI/180))*j/10; + double z2 = scale*Math.cos((blackhole.ticksExisted + 40*j - sliceAngle)*(Math.PI/180)); + + double absoluteX = x1*Math.cos(31*b); + double absoluteY = z1*Math.sin(31*a) + x1*Math.cos(31*a)*Math.sin(31*b); + double absoluteZ = z1*Math.cos(31*a); + + double absoluteX2 = x2*Math.cos(31*b); + double absoluteY2 = z2*Math.sin(31*a) + x2*Math.cos(31*a)*Math.sin(31*b); + double absoluteZ2 = z2*Math.cos(31*a); + /* + tessellator.startDrawing(0); + tessellator.setColorOpaque(255, 255, 255); GL11.glPointSize(5); tessellator.addVertex(absoluteX-x, 0, 0); tessellator.addVertex(0, absoluteY-y, 0); tessellator.addVertex(0, 0, absoluteZ-z); - + tessellator.draw(); - */ - rays.add(new RayHelper(j, absoluteX, absoluteY, absoluteZ, absoluteX2, absoluteY2, absoluteZ2, x, y, z)); + */ + rays.add(new RayHelper(j, absoluteX, absoluteY, absoluteZ, absoluteX2, absoluteY2, absoluteZ2, x, y, z)); + + } + + Collections.sort(rays); + + for(RayHelper ray : rays){ + + GL11.glPushMatrix(); + //GL11.glRotatef(31*blackhole.randomiser[ray.ordinal], 1, 0, 0); + //GL11.glRotatef(31*blackhole.randomiser2[ray.ordinal], 0, 0, 1); + + tessellator.startDrawing(5); + //tessellator.setColorRGBA(255, 255, 255, 0); + tessellator.addVertexWithUV(0, 0, 0, 0, 0); + tessellator.addVertexWithUV(0, 0, 0, 0, 1); + + //tessellator.setColorRGBA(0, 0, 0, 255); + tessellator.addVertexWithUV(ray.x1, ray.y1, ray.z1, 1, 0); + tessellator.addVertexWithUV(ray.x2, ray.y2, ray.z2, 1, 1); + + tessellator.draw(); + + GL11.glPopMatrix(); + } } + - Collections.sort(rays); - - for(RayHelper ray : rays){ - - GlStateManager.pushMatrix(); - //GlStateManager.rotate(31*blackhole.randomiser[ray.ordinal], 1, 0, 0); - //GlStateManager.rotate(31*blackhole.randomiser2[ray.ordinal], 0, 0, 1); - - buffer.begin(5, DefaultVertexFormats.POSITION_TEX); - - //tessellator.setColorRGBA(255, 255, 255, 0); - buffer.pos(0, 0, 0).tex(0, 0).endVertex(); - buffer.pos(0, 0, 0).tex(0, 1).endVertex(); - - //tessellator.setColorRGBA(0, 0, 0, 255); - buffer.pos(ray.x1, ray.y1, ray.z1).tex(1, 0).endVertex(); - buffer.pos(ray.x2, ray.y2, ray.z2).tex(1, 1).endVertex(); - - tessellator.draw(); - - GlStateManager.popMatrix(); - } - - GlStateManager.pushMatrix(); - - /* Deprecated in favour of particle style method. - GlStateManager.rotate(yaw, 0, 1, 0); + GL11.glPushMatrix(); + + /* Deprecated in favour of particle style method. + GL11.glRotatef(yaw, 0, 1, 0); // GL transformations are relative, hence only x rotation if(z < 0){ - GlStateManager.rotate(pitch, 1, 0, 0); + GL11.glRotatef(pitch, 1, 0, 0); }else{ - GlStateManager.rotate(-1*pitch, 1, 0, 0); + GL11.glRotatef(-1*pitch, 1, 0, 0); } - */ + */ - // Renders the aura effect - - // This counteracts the reverse rotation behaviour when in front f5 view. Vanilla now has this fix too. - float yaw = Minecraft.getMinecraft().gameSettings.thirdPersonView == 2 ? this.renderManager.playerViewX : -this.renderManager.playerViewX; - GlStateManager.rotate(180.0F - this.renderManager.playerViewY, 0.0F, 1.0F, 0.0F); - GlStateManager.rotate(yaw, 1.0F, 0.0F, 0.0F); - - buffer.begin(GL11.GL_QUADS, DefaultVertexFormats.POSITION_TEX); + // Renders the aura effect + + // This counteracts the reverse rotation behaviour when in front f5 view. + // Fun fact: this is a bug with vanilla too! Look at a snowball in front f5 view, for example. + float yaw = Minecraft.getMinecraft().gameSettings.thirdPersonView == 2 ? this.renderManager.playerViewX : -this.renderManager.playerViewX; + GL11.glRotatef(180.0F - this.renderManager.playerViewY, 0.0F, 1.0F, 0.0F); + GL11.glRotatef(yaw, 1.0F, 0.0F, 0.0F); + + tessellator.startDrawingQuads(); this.bindTexture(texture2); - - buffer.pos(-0.4, 0.4, 0).tex(0, 0).endVertex(); - buffer.pos(0.4, 0.4, 0).tex(1, 0).endVertex(); - buffer.pos(0.4, -0.4, 0).tex(1, 1).endVertex(); - buffer.pos(-0.4, -0.4, 0).tex(0, 1).endVertex(); - - tessellator.draw(); - - GlStateManager.popMatrix(); - - - GlStateManager.shadeModel(GL11.GL_FLAT); - GlStateManager.enableCull(); - GlStateManager.disableBlend(); - GlStateManager.enableLighting(); - RenderHelper.enableStandardItemLighting(); - - GlStateManager.popMatrix(); + + tessellator.addVertexWithUV(-0.4, 0.4, 0, 0, 0); + tessellator.addVertexWithUV(0.4, 0.4, 0, 1, 0); + tessellator.addVertexWithUV(0.4, -0.4, 0, 1, 1); + tessellator.addVertexWithUV(-0.4, -0.4, 0, 0, 1); + + tessellator.draw(); + + GL11.glPopMatrix(); + + + GL11.glShadeModel(GL11.GL_FLAT); + GL11.glEnable(GL11.GL_CULL_FACE); + GL11.glDisable(GL11.GL_BLEND); + GL11.glEnable(GL11.GL_LIGHTING); + RenderHelper.enableStandardItemLighting(); + + GL11.glPopMatrix(); } @Override - protected ResourceLocation getEntityTexture(EntityBlackHole entity) { + protected ResourceLocation getEntityTexture(Entity entity) { return texture; } diff --git a/src/main/java/electroblob/wizardry/client/renderer/RenderBlank.java b/src/main/java/electroblob/wizardry/client/renderer/RenderBlank.java index 01926ccc..9dcf7d80 100644 --- a/src/main/java/electroblob/wizardry/client/renderer/RenderBlank.java +++ b/src/main/java/electroblob/wizardry/client/renderer/RenderBlank.java @@ -1,24 +1,21 @@ package electroblob.wizardry.client.renderer; import net.minecraft.client.renderer.entity.Render; -import net.minecraft.client.renderer.entity.RenderManager; import net.minecraft.entity.Entity; import net.minecraft.util.ResourceLocation; -public class RenderBlank extends Render { - - public RenderBlank(RenderManager renderManager) { - super(renderManager); - } +public class RenderBlank extends Render { @Override public void doRender(Entity entity, double d0, double d1, double d2, float f, float f1) { + // TODO Auto-generated method stub } @Override protected ResourceLocation getEntityTexture(Entity entity) { + // TODO Auto-generated method stub return null; } diff --git a/src/main/java/electroblob/wizardry/client/renderer/RenderBubble.java b/src/main/java/electroblob/wizardry/client/renderer/RenderBubble.java index d96a1020..14a23526 100644 --- a/src/main/java/electroblob/wizardry/client/renderer/RenderBubble.java +++ b/src/main/java/electroblob/wizardry/client/renderer/RenderBubble.java @@ -1,103 +1,101 @@ package electroblob.wizardry.client.renderer; import org.lwjgl.opengl.GL11; +import org.lwjgl.opengl.GL12; -import electroblob.wizardry.Wizardry; import electroblob.wizardry.entity.construct.EntityBubble; -import electroblob.wizardry.util.WizardryUtilities; import net.minecraft.client.Minecraft; -import net.minecraft.client.renderer.GlStateManager; import net.minecraft.client.renderer.OpenGlHelper; import net.minecraft.client.renderer.Tessellator; -import net.minecraft.client.renderer.VertexBuffer; import net.minecraft.client.renderer.entity.Render; -import net.minecraft.client.renderer.entity.RenderManager; -import net.minecraft.client.renderer.vertex.DefaultVertexFormats; +import net.minecraft.entity.Entity; +import net.minecraft.util.MathHelper; import net.minecraft.util.ResourceLocation; -public class RenderBubble extends Render { +public class RenderBubble extends Render { + + private static final ResourceLocation particleTextures = new ResourceLocation("textures/particle/particles.png"); + private static final ResourceLocation darkOrbTexture = new ResourceLocation("wizardry:textures/entity/dark_orb.png"); - private static final ResourceLocation particleTextures = new ResourceLocation("textures/particle/particles.png"); - private static final ResourceLocation darkOrbTexture = new ResourceLocation(Wizardry.MODID, "textures/entity/dark_orb.png"); - - public RenderBubble(RenderManager renderManager) { - super(renderManager); + public RenderBubble() { + } @Override - public void doRender(EntityBubble entity, double par2, double par4, double par6, float par8, float par9){ - - GlStateManager.pushMatrix(); - GlStateManager.enableBlend(); - GlStateManager.blendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA); - - float yOffset = 0; - - if(WizardryUtilities.getRider(entity) != null){ - yOffset = WizardryUtilities.getRider(entity).height/2; + public void doRender(Entity entity, double par2, double par4, double par6, float par8, float par9) + { + if(entity instanceof EntityBubble){ + + GL11.glPushMatrix(); + GL11.glEnable(GL11.GL_BLEND); + GL11.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA); + + float yOffset = 0; + + if(entity.riddenByEntity != null){ + yOffset = entity.riddenByEntity.height/2; + } + + GL11.glTranslatef((float)par2, (float)par4 + yOffset, (float)par6); + + this.bindTexture(((EntityBubble)entity).isDarkOrb ? darkOrbTexture : particleTextures); + float f6 = 1.0F; + float f7 = 0.5F; + float f8 = 0.5F; + + if(((EntityBubble)entity).isDarkOrb){ + GL11.glDisable(GL11.GL_LIGHTING); + OpenGlHelper.setLightmapTextureCoords(OpenGlHelper.lightmapTexUnit, 240, 240); + }else{ + int j = entity.getBrightnessForRender(par9); + int k = j % 65536; + int l = j / 65536; + OpenGlHelper.setLightmapTextureCoords(OpenGlHelper.lightmapTexUnit, (float)k / 1.0F, (float)l / 1.0F); + } + + GL11.glColor4f(1.0F, 1.0F, 1.0F, 1.0F); + + // This counteracts the reverse rotation behaviour when in front f5 view. + // Fun fact: this is a bug with vanilla too! Look at a snowball in front f5 view, for example. + float yaw = Minecraft.getMinecraft().gameSettings.thirdPersonView == 2 ? this.renderManager.playerViewX : -this.renderManager.playerViewX; + GL11.glRotatef(180.0F - this.renderManager.playerViewY, 0.0F, 1.0F, 0.0F); + GL11.glRotatef(yaw, 1.0F, 0.0F, 0.0F); + + float f11 = 3.0F; + GL11.glScalef(f11, f11, f11); + + double pixelwidth = (1.0d/128); + + Tessellator tessellator = Tessellator.instance; + tessellator.startDrawingQuads(); + //tessellator.setColorRGBA_I(k1, 128); + tessellator.setNormal(0.0F, 1.0F, 0.0F); + + if(((EntityBubble)entity).isDarkOrb){ + tessellator.addVertexWithUV((double)(0.0F - f7), (double)(0.0F - f8), 0.0D, 0, 1); + tessellator.addVertexWithUV((double)(f6 - f7), (double)(0.0F - f8), 0.0D, 1, 1); + tessellator.addVertexWithUV((double)(f6 - f7), (double)(1.0F - f8), 0.0D, 1, 0); + tessellator.addVertexWithUV((double)(0.0F - f7), (double)(1.0F - f8), 0.0D, 0, 0); + }else{ + tessellator.addVertexWithUV((double)(0.0F - f7), (double)(0.0F - f8), 0.0D, pixelwidth, pixelwidth * 24); + tessellator.addVertexWithUV((double)(f6 - f7), (double)(0.0F - f8), 0.0D, pixelwidth*8, pixelwidth * 24); + tessellator.addVertexWithUV((double)(f6 - f7), (double)(1.0F - f8), 0.0D, pixelwidth*8, pixelwidth * 17); + tessellator.addVertexWithUV((double)(0.0F - f7), (double)(1.0F - f8), 0.0D, pixelwidth, pixelwidth * 17); + } + + tessellator.draw(); + + GL11.glDisable(GL11.GL_BLEND); + if(((EntityBubble)entity).isDarkOrb){ + GL11.glEnable(GL11.GL_LIGHTING); + } + GL11.glDisable(GL12.GL_RESCALE_NORMAL); + GL11.glPopMatrix(); } - - GlStateManager.translate((float)par2, (float)par4 + yOffset, (float)par6); - - this.bindTexture(((EntityBubble)entity).isDarkOrb ? darkOrbTexture : particleTextures); - float f6 = 1.0F; - float f7 = 0.5F; - float f8 = 0.5F; - - if(((EntityBubble)entity).isDarkOrb){ - GlStateManager.disableLighting(); - OpenGlHelper.setLightmapTextureCoords(OpenGlHelper.lightmapTexUnit, 240, 240); - }else{ - int j = entity.getBrightnessForRender(par9); - int k = j % 65536; - int l = j / 65536; - OpenGlHelper.setLightmapTextureCoords(OpenGlHelper.lightmapTexUnit, (float)k / 1.0F, (float)l / 1.0F); - } - - GlStateManager.color(1.0F, 1.0F, 1.0F, 1.0F); - - // This counteracts the reverse rotation behaviour when in front f5 view. - // Fun fact: this is a bug with vanilla too! Look at a snowball in front f5 view, for example. - float yaw = Minecraft.getMinecraft().gameSettings.thirdPersonView == 2 ? this.renderManager.playerViewX : -this.renderManager.playerViewX; - GlStateManager.rotate(180.0F - this.renderManager.playerViewY, 0.0F, 1.0F, 0.0F); - GlStateManager.rotate(yaw, 1.0F, 0.0F, 0.0F); - - float f11 = 3.0F; - GlStateManager.scale(f11, f11, f11); - - double pixelwidth = (1.0d/128); - - Tessellator tessellator = Tessellator.getInstance(); - VertexBuffer buffer = tessellator.getBuffer(); - buffer.begin(GL11.GL_QUADS, DefaultVertexFormats.POSITION_TEX); - //tessellator.setColorRGBA_I(k1, 128); - //buffer.normal(0.0F, 1.0F, 0.0F); - - if(((EntityBubble)entity).isDarkOrb){ - buffer.pos((double)(0.0F - f7), (double)(0.0F - f8), 0.0D).tex(0, 1).endVertex(); - buffer.pos((double)(f6 - f7), (double)(0.0F - f8), 0.0D).tex(1, 1).endVertex(); - buffer.pos((double)(f6 - f7), (double)(1.0F - f8), 0.0D).tex(1, 0).endVertex(); - buffer.pos((double)(0.0F - f7), (double)(1.0F - f8), 0.0D).tex(0, 0).endVertex(); - }else{ - buffer.pos((double)(0.0F - f7), (double)(0.0F - f8), 0.0D).tex(pixelwidth, pixelwidth * 24).endVertex(); - buffer.pos((double)(f6 - f7), (double)(0.0F - f8), 0.0D).tex(pixelwidth*8, pixelwidth * 24).endVertex(); - buffer.pos((double)(f6 - f7), (double)(1.0F - f8), 0.0D).tex(pixelwidth*8, pixelwidth * 17).endVertex(); - buffer.pos((double)(0.0F - f7), (double)(1.0F - f8), 0.0D).tex(pixelwidth, pixelwidth * 17).endVertex(); - } - - tessellator.draw(); - - GlStateManager.disableBlend(); - if(((EntityBubble)entity).isDarkOrb){ - GlStateManager.enableLighting(); - } - GlStateManager.disableRescaleNormal(); - GlStateManager.popMatrix(); - - } + } @Override - protected ResourceLocation getEntityTexture(EntityBubble entity) { + protected ResourceLocation getEntityTexture(Entity entity) { return null; } diff --git a/src/main/java/electroblob/wizardry/client/renderer/RenderDecay.java b/src/main/java/electroblob/wizardry/client/renderer/RenderDecay.java index f849f967..3a333caa 100644 --- a/src/main/java/electroblob/wizardry/client/renderer/RenderDecay.java +++ b/src/main/java/electroblob/wizardry/client/renderer/RenderDecay.java @@ -1,73 +1,73 @@ package electroblob.wizardry.client.renderer; import org.lwjgl.opengl.GL11; +import org.lwjgl.opengl.GL12; -import electroblob.wizardry.Wizardry; import electroblob.wizardry.entity.construct.EntityDecay; -import net.minecraft.client.renderer.GlStateManager; +import electroblob.wizardry.entity.construct.EntityHealAura; import net.minecraft.client.renderer.OpenGlHelper; import net.minecraft.client.renderer.Tessellator; -import net.minecraft.client.renderer.VertexBuffer; import net.minecraft.client.renderer.entity.Render; -import net.minecraft.client.renderer.entity.RenderManager; -import net.minecraft.client.renderer.vertex.DefaultVertexFormats; +import net.minecraft.entity.Entity; import net.minecraft.util.ResourceLocation; -public class RenderDecay extends Render { +public class RenderDecay extends Render { private static final ResourceLocation[] textures = new ResourceLocation[10]; - public RenderDecay(RenderManager renderManager){ - super(renderManager); + public RenderDecay(){ for(int i=0;i<10;i++){ - textures[i] = new ResourceLocation(Wizardry.MODID, "textures/entity/decay_" + i + ".png"); + textures[i] = new ResourceLocation("wizardry:textures/entity/decay_" + i + ".png"); } } @Override - public void doRender(EntityDecay entity, double par2, double par4, double par6, float par8, float par9){ + public void doRender(Entity entity, double par2, double par4, double par6, float par8, float par9){ - GlStateManager.pushMatrix(); - GlStateManager.enableBlend(); - GlStateManager.disableLighting(); + GL11.glPushMatrix(); + GL11.glEnable(GL11.GL_BLEND); + GL11.glDisable(GL11.GL_LIGHTING); OpenGlHelper.setLightmapTextureCoords(OpenGlHelper.lightmapTexUnit, 240, 240); - GlStateManager.blendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA); + GL11.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA); float yOffset = 0; - GlStateManager.translate((float)par2, (float)par4 + yOffset, (float)par6); + if(entity.riddenByEntity != null){ + yOffset = entity.riddenByEntity.height/2; + } + + GL11.glTranslatef((float)par2, (float)par4 + yOffset, (float)par6); this.bindTexture(textures[((EntityDecay)entity).textureIndex]); float f6 = 1.0F; float f7 = 0.5F; float f8 = 0.5F; - GlStateManager.rotate(-90, 1, 0, 0); + GL11.glRotatef(-90, 1, 0, 0); float scale = 2*Math.min(1, (float)(EntityDecay.LIFETIME - entity.ticksExisted)/50f); - GlStateManager.scale(scale, scale, scale); + GL11.glScalef(scale, scale, scale); - Tessellator tessellator = Tessellator.getInstance(); - VertexBuffer buffer = tessellator.getBuffer(); - buffer.begin(GL11.GL_QUADS, DefaultVertexFormats.POSITION_TEX); + Tessellator tessellator = Tessellator.instance; + tessellator.startDrawingQuads(); //tessellator.setColorRGBA_I(k1, 128); - //buffer.normal(0.0F, 1.0F, 0.0F); - buffer.pos((double)(0.0F - f7), (double)(0.0F - f8), 0.01).tex(0, 1).endVertex(); - buffer.pos((double)(f6 - f7), (double)(0.0F - f8), 0.01).tex(1, 1).endVertex(); - buffer.pos((double)(f6 - f7), (double)(1.0F - f8), 0.01).tex(1, 0).endVertex(); - buffer.pos((double)(0.0F - f7), (double)(1.0F - f8), 0.01).tex(0, 0).endVertex(); + tessellator.setNormal(0.0F, 1.0F, 0.0F); + tessellator.addVertexWithUV((double)(0.0F - f7), (double)(0.0F - f8), 0.01, 0, 1); + tessellator.addVertexWithUV((double)(f6 - f7), (double)(0.0F - f8), 0.01, 1, 1); + tessellator.addVertexWithUV((double)(f6 - f7), (double)(1.0F - f8), 0.01, 1, 0); + tessellator.addVertexWithUV((double)(0.0F - f7), (double)(1.0F - f8), 0.01, 0, 0); tessellator.draw(); - GlStateManager.disableBlend(); - GlStateManager.enableLighting(); - GlStateManager.disableRescaleNormal(); - GlStateManager.popMatrix(); + GL11.glDisable(GL11.GL_BLEND); + GL11.glEnable(GL11.GL_LIGHTING); + GL11.glDisable(GL12.GL_RESCALE_NORMAL); + GL11.glPopMatrix(); } @Override - protected ResourceLocation getEntityTexture(EntityDecay entity) { + protected ResourceLocation getEntityTexture(Entity entity) { return null; } diff --git a/src/main/java/electroblob/wizardry/client/renderer/RenderDecoy.java b/src/main/java/electroblob/wizardry/client/renderer/RenderDecoy.java index 8e373b04..d45d3ee0 100644 --- a/src/main/java/electroblob/wizardry/client/renderer/RenderDecoy.java +++ b/src/main/java/electroblob/wizardry/client/renderer/RenderDecoy.java @@ -1,108 +1,498 @@ package electroblob.wizardry.client.renderer; -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; +import java.util.UUID; -import electroblob.wizardry.Wizardry; +import org.lwjgl.opengl.GL11; + +import com.mojang.authlib.GameProfile; + +import cpw.mods.fml.relauncher.Side; +import cpw.mods.fml.relauncher.SideOnly; +import electroblob.wizardry.client.model.ModelWizard; import electroblob.wizardry.entity.living.EntityDecoy; +import electroblob.wizardry.entity.living.EntityEvilWizard; +import electroblob.wizardry.entity.living.EntityWizard; +import net.minecraft.block.Block; +import net.minecraft.client.entity.AbstractClientPlayer; import net.minecraft.client.model.ModelBiped; -import net.minecraft.client.renderer.entity.Render; +import net.minecraft.client.renderer.RenderBlocks; import net.minecraft.client.renderer.entity.RenderBiped; -import net.minecraft.client.renderer.entity.RenderManager; +import net.minecraft.client.renderer.entity.RendererLivingEntity; +import net.minecraft.client.renderer.tileentity.TileEntitySkullRenderer; import net.minecraft.entity.Entity; +import net.minecraft.entity.EntityLivingBase; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.init.Items; +import net.minecraft.item.EnumAction; +import net.minecraft.item.Item; +import net.minecraft.item.ItemArmor; +import net.minecraft.item.ItemBlock; +import net.minecraft.item.ItemStack; +import net.minecraft.nbt.NBTTagCompound; +import net.minecraft.nbt.NBTUtil; +import net.minecraft.util.MathHelper; import net.minecraft.util.ResourceLocation; -import net.minecraftforge.fml.relauncher.ReflectionHelper; -import net.minecraftforge.fml.relauncher.Side; -import net.minecraftforge.fml.relauncher.SideOnly; +import net.minecraft.util.StringUtils; -// TODO: Backport the rewrite of this entire class. @SideOnly(Side.CLIENT) -public class RenderDecoy extends RenderBiped { - - private static final ResourceLocation steveTextures = new ResourceLocation("textures/entity/steve.png"); - - private static final Method getEntityTexture = ReflectionHelper.findMethod(Render.class, null, - new String[]{"getEntityTexture", "func_110775_a"}, Entity.class); // Generic parameter T is erased to Entity at runtime. - - public RenderDecoy(RenderManager manager){ - super(manager, new ModelBiped(0.0f), 0.5f); - } - - @Override - public void doRender(EntityDecoy entity, double x, double y, double z, float entityYaw, float partialTicks) { - - if(entity.getCaster() != null){ - - this.renderName(entity, x, y, z); - - // Save relevant animation fields from the caster to local variables - float pitch = entity.getCaster().rotationPitch; - float prevPitch = entity.getCaster().prevRotationPitch; - float swing = entity.getCaster().swingProgress; - float prevSwing = entity.getCaster().prevSwingProgress; - float yawOffset = entity.getCaster().renderYawOffset; - float prevYawOffset = entity.getCaster().prevRenderYawOffset; - float yaw = entity.getCaster().rotationYawHead; - float prevYaw = entity.getCaster().prevRotationYawHead; - float limbSwing = entity.getCaster().limbSwing; - float limbSwingAmount = entity.getCaster().limbSwingAmount; - float prevLimbSwingAmount = entity.getCaster().prevLimbSwingAmount; - int hurtTime = entity.getCaster().hurtTime; - boolean sneak = entity.getCaster().isSneaking(); - Entity mount = entity.getCaster().getRidingEntity(); - - // Assign decoy's animation fields to the caster - entity.getCaster().rotationPitch = entity.rotationPitch; - entity.getCaster().prevRotationPitch = entity.prevRotationPitch; - entity.getCaster().swingProgress = entity.swingProgress; - entity.getCaster().prevSwingProgress = entity.prevSwingProgress; - entity.getCaster().renderYawOffset = entity.renderYawOffset; - entity.getCaster().prevRenderYawOffset = entity.prevRenderYawOffset; - entity.getCaster().rotationYawHead = entity.rotationYawHead; - entity.getCaster().prevRotationYawHead = entity.prevRotationYawHead; - entity.getCaster().limbSwing = entity.limbSwing; - entity.getCaster().limbSwingAmount = entity.limbSwingAmount; - entity.getCaster().prevLimbSwingAmount = entity.prevLimbSwingAmount; - entity.getCaster().hurtTime = entity.hurtTime; - entity.getCaster().setSneaking(false); // Decoys can't sneak FIXME Not working! - entity.getCaster().dismountRidingEntity(); // Decoys can't ride anything - - // Do the rendering - renderManager.getEntityRenderObject(entity.getCaster()).doRender(entity.getCaster(), x, y, z, entityYaw, partialTicks); - - // Reset caster's animation fields to their original values - entity.getCaster().rotationPitch = pitch; - entity.getCaster().prevRotationPitch = prevPitch; - entity.getCaster().swingProgress = swing; - entity.getCaster().prevSwingProgress = prevSwing; - entity.getCaster().renderYawOffset = yawOffset; - entity.getCaster().prevRenderYawOffset = prevYawOffset; - entity.getCaster().rotationYawHead = yaw; - entity.getCaster().prevRotationYawHead = prevYaw; - entity.getCaster().limbSwing = limbSwing; - entity.getCaster().limbSwingAmount = limbSwingAmount; - entity.getCaster().prevLimbSwingAmount = prevLimbSwingAmount; - entity.getCaster().hurtTime = hurtTime; - entity.getCaster().setSneaking(sneak); - if(mount != null) entity.getCaster().startRiding(mount); - - }else{ - super.doRender(entity, x, y, z, entityYaw, partialTicks); - } - } - - @Override - protected ResourceLocation getEntityTexture(EntityDecoy entity){ - if(entity.getCaster() != null){ - try { - return (ResourceLocation)getEntityTexture.invoke(renderManager.getEntityRenderObject(entity.getCaster())); - } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException | ClassCastException e){ - Wizardry.logger.error("Error while reflectively calling Render#getEntityTexture as part of decoy rendering"); - e.printStackTrace(); - } - } - // Fallback to steve textures - return steveTextures; - } +public class RenderDecoy extends RenderBiped +{ + private static final ResourceLocation steveTextures = new ResourceLocation("textures/entity/steve.png"); + public ModelBiped modelBipedMain; + public ModelBiped modelArmorChestplate; + public ModelBiped modelArmor; + public ModelWizard modelWizard; + public RenderDecoy() + { + super(new ModelBiped(0.0F), 0.5F); + this.modelBipedMain = (ModelBiped)this.mainModel; + this.modelArmorChestplate = new ModelBiped(1.0F); + this.modelArmor = new ModelBiped(0.5F); + this.modelWizard = new ModelWizard(); + } + + /** + * Queries whether should render the specified pass or not. + */ + protected int shouldRenderPass(EntityDecoy decoy, int pass, float partialTickTime) + { + if(decoy.getCaster() instanceof EntityPlayer){ + + ItemStack itemstack = ((AbstractClientPlayer)decoy.getCaster()).inventory.armorItemInSlot(3 - pass); + + if (itemstack != null) + { + Item item = itemstack.getItem(); + + if (item instanceof ItemArmor) + { + ItemArmor itemarmor = (ItemArmor)item; + this.bindTexture(RenderBiped.getArmorResource(decoy, itemstack, pass, null)); + ModelBiped modelbiped = pass == 2 ? this.modelArmor : this.modelArmorChestplate; + modelbiped.bipedHead.showModel = pass == 0; + modelbiped.bipedHeadwear.showModel = pass == 0; + modelbiped.bipedBody.showModel = pass == 1 || pass == 2; + modelbiped.bipedRightArm.showModel = pass == 1; + modelbiped.bipedLeftArm.showModel = pass == 1; + modelbiped.bipedRightLeg.showModel = pass == 2 || pass == 3; + modelbiped.bipedLeftLeg.showModel = pass == 2 || pass == 3; + modelbiped = net.minecraftforge.client.ForgeHooksClient.getArmorModel(decoy, itemstack, pass, modelbiped); + this.setRenderPassModel(modelbiped); + modelbiped.onGround = this.mainModel.onGround; + modelbiped.isRiding = this.mainModel.isRiding; + modelbiped.isChild = this.mainModel.isChild; + + //Move outside if to allow for more then just CLOTH + int j = itemarmor.getColor(itemstack); + if (j != -1) + { + float f1 = (float)(j >> 16 & 255) / 255.0F; + float f2 = (float)(j >> 8 & 255) / 255.0F; + float f3 = (float)(j & 255) / 255.0F; + GL11.glColor3f(f1, f2, f3); + + if (itemstack.isItemEnchanted()) + { + return 31; + } + + return 16; + } + + GL11.glColor3f(1.0F, 1.0F, 1.0F); + + if (itemstack.isItemEnchanted()) + { + return 15; + } + + return 1; + } + } + }else{ + + return super.shouldRenderPass(decoy.getCaster(), pass, partialTickTime); + } + + return -1; + } + + protected void func_82408_c(EntityDecoy decoy, int p_82408_2_, float p_82408_3_) + { + ItemStack itemstack = ((AbstractClientPlayer)decoy.getCaster()).inventory.armorItemInSlot(3 - p_82408_2_); + + if (itemstack != null) + { + Item item = itemstack.getItem(); + + if (item instanceof ItemArmor) + { + this.bindTexture(RenderBiped.getArmorResource(decoy, itemstack, p_82408_2_, "overlay")); + GL11.glColor3f(1.0F, 1.0F, 1.0F); + } + } + } + + public void doRender(EntityDecoy decoy, double x, double y, double z, float yaw, float pitch) + { + if(decoy.getCaster() instanceof EntityPlayer){ + + this.mainModel = this.modelBipedMain; + + GL11.glColor3f(1.0F, 1.0F, 1.0F); + ItemStack itemstack = ((AbstractClientPlayer)decoy.getCaster()).inventory.getCurrentItem(); + this.modelArmorChestplate.heldItemRight = this.modelArmor.heldItemRight = this.modelBipedMain.heldItemRight = itemstack != null ? 1 : 0; + + if (itemstack != null && ((AbstractClientPlayer)decoy.getCaster()).getItemInUseCount() > 0) + { + EnumAction enumaction = itemstack.getItemUseAction(); + + if (enumaction == EnumAction.block) + { + this.modelArmorChestplate.heldItemRight = this.modelArmor.heldItemRight = this.modelBipedMain.heldItemRight = 3; + } + else if (enumaction == EnumAction.bow) + { + this.modelArmorChestplate.aimedBow = this.modelArmor.aimedBow = this.modelBipedMain.aimedBow = true; + } + } + + this.modelArmorChestplate.isSneak = this.modelArmor.isSneak = this.modelBipedMain.isSneak = decoy.isSneaking(); + double d3 = y - (double)decoy.yOffset; + + super.doRender((EntityLivingBase)decoy, x, d3, z, yaw, pitch); + this.modelArmorChestplate.aimedBow = this.modelArmor.aimedBow = this.modelBipedMain.aimedBow = false; + this.modelArmorChestplate.isSneak = this.modelArmor.isSneak = this.modelBipedMain.isSneak = false; + this.modelArmorChestplate.heldItemRight = this.modelArmor.heldItemRight = this.modelBipedMain.heldItemRight = 0; + + }else{ + + this.mainModel = this.modelWizard; + + // Since wizards always have a wand, I don't see any point in checking + this.modelArmorChestplate.heldItemRight = this.modelArmor.heldItemRight = this.modelBipedMain.heldItemRight = 1; + + super.doRender(decoy, x, y, z, yaw, pitch); + + } + } + + /** + * Returns the location of an entity's texture. Doesn't seem to be called unless you call Render.bindEntityTexture. + */ + protected ResourceLocation getEntityTexture(EntityDecoy decoy) + { + //System.out.println(decoy.getCaster()); + + if(decoy.getCaster() instanceof EntityWizard){ + return RenderWizard.textures[((EntityWizard)decoy.getCaster()).textureIndex]; + }else if(decoy.getCaster() instanceof EntityEvilWizard){ + return RenderEvilWizard.textures[((EntityEvilWizard)decoy.getCaster()).textureIndex]; + }else if(decoy.getCaster() instanceof EntityPlayer){ + return ((AbstractClientPlayer) decoy.getCaster()).getLocationSkin(); + }else{ + return steveTextures; + } + } + + protected void renderEquippedItems(EntityDecoy decoy, float p_77029_2_) + { + if(decoy.getCaster() instanceof EntityPlayer){ + + GL11.glColor3f(1.0F, 1.0F, 1.0F); + super.renderEquippedItems(decoy, p_77029_2_); + super.renderArrowsStuckInEntity(decoy, p_77029_2_); + ItemStack itemstack = ((AbstractClientPlayer)decoy.getCaster()).inventory.armorItemInSlot(3); + + if (itemstack != null) + { + GL11.glPushMatrix(); + this.modelBipedMain.bipedHead.postRender(0.0625F); + float f1; + + if (itemstack.getItem() instanceof ItemBlock) + { + net.minecraftforge.client.IItemRenderer customRenderer = net.minecraftforge.client.MinecraftForgeClient.getItemRenderer(itemstack, net.minecraftforge.client.IItemRenderer.ItemRenderType.EQUIPPED); + boolean is3D = (customRenderer != null && customRenderer.shouldUseRenderHelper(net.minecraftforge.client.IItemRenderer.ItemRenderType.EQUIPPED, itemstack, net.minecraftforge.client.IItemRenderer.ItemRendererHelper.BLOCK_3D)); + + if (is3D || RenderBlocks.renderItemIn3d(Block.getBlockFromItem(itemstack.getItem()).getRenderType())) + { + f1 = 0.625F; + GL11.glTranslatef(0.0F, -0.25F, 0.0F); + GL11.glRotatef(90.0F, 0.0F, 1.0F, 0.0F); + GL11.glScalef(f1, -f1, -f1); + } + + this.renderManager.itemRenderer.renderItem(decoy, itemstack, 0); + } + else if (itemstack.getItem() == Items.skull) + { + f1 = 1.0625F; + GL11.glScalef(f1, -f1, -f1); + GameProfile gameprofile = null; + + if (itemstack.hasTagCompound()) + { + NBTTagCompound nbttagcompound = itemstack.getTagCompound(); + + if (nbttagcompound.hasKey("SkullOwner", 10)) + { + gameprofile = NBTUtil.func_152459_a(nbttagcompound.getCompoundTag("SkullOwner")); + } + else if (nbttagcompound.hasKey("SkullOwner", 8) && !StringUtils.isNullOrEmpty(nbttagcompound.getString("SkullOwner"))) + { + gameprofile = new GameProfile((UUID)null, nbttagcompound.getString("SkullOwner")); + } + } + + TileEntitySkullRenderer.field_147536_b.func_152674_a(-0.5F, 0.0F, -0.5F, 1, 180.0F, itemstack.getItemDamage(), gameprofile); + } + + GL11.glPopMatrix(); + } + + float f2; + + if (decoy.getCommandSenderName().equals("deadmau5") && ((AbstractClientPlayer)decoy.getCaster()).func_152123_o()) + { + this.bindTexture(((AbstractClientPlayer)decoy.getCaster()).getLocationSkin()); + + for (int j = 0; j < 2; ++j) + { + float f9 = decoy.prevRotationYaw + (decoy.rotationYaw - decoy.prevRotationYaw) * p_77029_2_ - (decoy.prevRenderYawOffset + (decoy.renderYawOffset - decoy.prevRenderYawOffset) * p_77029_2_); + float f10 = decoy.prevRotationPitch + (decoy.rotationPitch - decoy.prevRotationPitch) * p_77029_2_; + GL11.glPushMatrix(); + GL11.glRotatef(f9, 0.0F, 1.0F, 0.0F); + GL11.glRotatef(f10, 1.0F, 0.0F, 0.0F); + GL11.glTranslatef(0.375F * (float)(j * 2 - 1), 0.0F, 0.0F); + GL11.glTranslatef(0.0F, -0.375F, 0.0F); + GL11.glRotatef(-f10, 1.0F, 0.0F, 0.0F); + GL11.glRotatef(-f9, 0.0F, 1.0F, 0.0F); + f2 = 1.3333334F; + GL11.glScalef(f2, f2, f2); + this.modelBipedMain.renderEars(0.0625F); + GL11.glPopMatrix(); + } + } + + boolean flag = ((AbstractClientPlayer)decoy.getCaster()).func_152122_n(); + float f4; + + if (flag && !decoy.isInvisible() && !((AbstractClientPlayer)decoy.getCaster()).getHideCape()) + { + this.bindTexture(((AbstractClientPlayer)decoy.getCaster()).getLocationCape()); + GL11.glPushMatrix(); + GL11.glTranslatef(0.0F, 0.0F, 0.125F); + double d3 = ((AbstractClientPlayer)decoy.getCaster()).field_71091_bM + (((AbstractClientPlayer)decoy.getCaster()).field_71094_bP - ((AbstractClientPlayer)decoy.getCaster()).field_71091_bM) * (double)p_77029_2_ - (decoy.prevPosX + (decoy.posX - decoy.prevPosX) * (double)p_77029_2_); + double d4 = ((AbstractClientPlayer)decoy.getCaster()).field_71096_bN + (((AbstractClientPlayer)decoy.getCaster()).field_71095_bQ - ((AbstractClientPlayer)decoy.getCaster()).field_71096_bN) * (double)p_77029_2_ - (decoy.prevPosY + (decoy.posY - decoy.prevPosY) * (double)p_77029_2_); + double d0 = ((AbstractClientPlayer)decoy.getCaster()).field_71097_bO + (((AbstractClientPlayer)decoy.getCaster()).field_71085_bR - ((AbstractClientPlayer)decoy.getCaster()).field_71097_bO) * (double)p_77029_2_ - (decoy.prevPosZ + (decoy.posZ - decoy.prevPosZ) * (double)p_77029_2_); + f4 = decoy.prevRenderYawOffset + (decoy.renderYawOffset - decoy.prevRenderYawOffset) * p_77029_2_; + double d1 = (double)MathHelper.sin(f4 * (float)Math.PI / 180.0F); + double d2 = (double)(-MathHelper.cos(f4 * (float)Math.PI / 180.0F)); + float f5 = (float)d4 * 10.0F; + + if (f5 < -6.0F) + { + f5 = -6.0F; + } + + if (f5 > 32.0F) + { + f5 = 32.0F; + } + + float f6 = (float)(d3 * d1 + d0 * d2) * 100.0F; + float f7 = (float)(d3 * d2 - d0 * d1) * 100.0F; + + if (f6 < 0.0F) + { + f6 = 0.0F; + } + + float f8 = ((AbstractClientPlayer)decoy.getCaster()).prevCameraYaw + (((AbstractClientPlayer)decoy.getCaster()).cameraYaw - ((AbstractClientPlayer)decoy.getCaster()).prevCameraYaw) * p_77029_2_; + f5 += MathHelper.sin((decoy.prevDistanceWalkedModified + (decoy.distanceWalkedModified - decoy.prevDistanceWalkedModified) * p_77029_2_) * 6.0F) * 32.0F * f8; + + if (decoy.isSneaking()) + { + f5 += 25.0F; + } + + GL11.glRotatef(6.0F + f6 / 2.0F + f5, 1.0F, 0.0F, 0.0F); + GL11.glRotatef(f7 / 2.0F, 0.0F, 0.0F, 1.0F); + GL11.glRotatef(-f7 / 2.0F, 0.0F, 1.0F, 0.0F); + GL11.glRotatef(180.0F, 0.0F, 1.0F, 0.0F); + this.modelBipedMain.renderCloak(0.0625F); + GL11.glPopMatrix(); + } + + ItemStack itemstack1 = ((AbstractClientPlayer)decoy.getCaster()).inventory.getCurrentItem(); + + if (itemstack1 != null) + { + GL11.glPushMatrix(); + this.modelBipedMain.bipedRightArm.postRender(0.0625F); + GL11.glTranslatef(-0.0625F, 0.4375F, 0.0625F); + + EnumAction enumaction = null; + + if (((AbstractClientPlayer)decoy.getCaster()).getItemInUseCount() > 0) + { + enumaction = itemstack1.getItemUseAction(); + } + + net.minecraftforge.client.IItemRenderer customRenderer = net.minecraftforge.client.MinecraftForgeClient.getItemRenderer(itemstack1, net.minecraftforge.client.IItemRenderer.ItemRenderType.EQUIPPED); + boolean is3D = (customRenderer != null && customRenderer.shouldUseRenderHelper(net.minecraftforge.client.IItemRenderer.ItemRenderType.EQUIPPED, itemstack1, net.minecraftforge.client.IItemRenderer.ItemRendererHelper.BLOCK_3D)); + + if (is3D || itemstack1.getItem() instanceof ItemBlock && RenderBlocks.renderItemIn3d(Block.getBlockFromItem(itemstack1.getItem()).getRenderType())) + { + f2 = 0.5F; + GL11.glTranslatef(0.0F, 0.1875F, -0.3125F); + f2 *= 0.75F; + GL11.glRotatef(20.0F, 1.0F, 0.0F, 0.0F); + GL11.glRotatef(45.0F, 0.0F, 1.0F, 0.0F); + GL11.glScalef(-f2, -f2, f2); + } + else if (itemstack1.getItem() == Items.bow) + { + f2 = 0.625F; + GL11.glTranslatef(0.0F, 0.125F, 0.3125F); + GL11.glRotatef(-20.0F, 0.0F, 1.0F, 0.0F); + GL11.glScalef(f2, -f2, f2); + GL11.glRotatef(-100.0F, 1.0F, 0.0F, 0.0F); + GL11.glRotatef(45.0F, 0.0F, 1.0F, 0.0F); + } + else if (itemstack1.getItem().isFull3D()) + { + f2 = 0.625F; + + if (itemstack1.getItem().shouldRotateAroundWhenRendering()) + { + GL11.glRotatef(180.0F, 0.0F, 0.0F, 1.0F); + GL11.glTranslatef(0.0F, -0.125F, 0.0F); + } + + if (((AbstractClientPlayer)decoy.getCaster()).getItemInUseCount() > 0 && enumaction == EnumAction.block) + { + GL11.glTranslatef(0.05F, 0.0F, -0.1F); + GL11.glRotatef(-50.0F, 0.0F, 1.0F, 0.0F); + GL11.glRotatef(-10.0F, 1.0F, 0.0F, 0.0F); + GL11.glRotatef(-60.0F, 0.0F, 0.0F, 1.0F); + } + + GL11.glTranslatef(0.0F, 0.1875F, 0.0F); + GL11.glScalef(f2, -f2, f2); + GL11.glRotatef(-100.0F, 1.0F, 0.0F, 0.0F); + GL11.glRotatef(45.0F, 0.0F, 1.0F, 0.0F); + } + else + { + f2 = 0.375F; + GL11.glTranslatef(0.25F, 0.1875F, -0.1875F); + GL11.glScalef(f2, f2, f2); + GL11.glRotatef(60.0F, 0.0F, 0.0F, 1.0F); + GL11.glRotatef(-90.0F, 1.0F, 0.0F, 0.0F); + GL11.glRotatef(20.0F, 0.0F, 0.0F, 1.0F); + } + + float f3; + int k; + float f12; + + if (itemstack1.getItem().requiresMultipleRenderPasses()) + { + for (k = 0; k < itemstack1.getItem().getRenderPasses(itemstack1.getItemDamage()); ++k) + { + int i = itemstack1.getItem().getColorFromItemStack(itemstack1, k); + f12 = (float)(i >> 16 & 255) / 255.0F; + f3 = (float)(i >> 8 & 255) / 255.0F; + f4 = (float)(i & 255) / 255.0F; + GL11.glColor4f(f12, f3, f4, 1.0F); + this.renderManager.itemRenderer.renderItem(decoy, itemstack1, k); + } + } + else + { + k = itemstack1.getItem().getColorFromItemStack(itemstack1, 0); + float f11 = (float)(k >> 16 & 255) / 255.0F; + f12 = (float)(k >> 8 & 255) / 255.0F; + f3 = (float)(k & 255) / 255.0F; + GL11.glColor4f(f11, f12, f3, 1.0F); + this.renderManager.itemRenderer.renderItem(decoy, itemstack1, 0); + } + + GL11.glPopMatrix(); + } + + }else{ + super.renderEquippedItems(decoy, p_77029_2_); + } + } + + /** + * Allows the render to do any OpenGL state modifications necessary before the model is rendered. Args: + * entityLiving, partialTickTime + */ + protected void preRenderCallback(EntityDecoy p_77041_1_, float p_77041_2_) + { + float f1 = 0.9375F; + GL11.glScalef(f1, f1, f1); + } + + /** + * Allows the render to do any OpenGL state modifications necessary before the model is rendered. Args: + * entityLiving, partialTickTime + */ + protected void preRenderCallback(EntityLivingBase p_77041_1_, float p_77041_2_) + { + this.preRenderCallback((EntityDecoy)p_77041_1_, p_77041_2_); + } + + protected void func_82408_c(EntityLivingBase p_82408_1_, int p_82408_2_, float p_82408_3_) + { + this.func_82408_c((EntityDecoy)p_82408_1_, p_82408_2_, p_82408_3_); + } + + /** + * Queries whether should render the specified pass or not. + */ + protected int shouldRenderPass(EntityLivingBase p_77032_1_, int p_77032_2_, float p_77032_3_) + { + return this.shouldRenderPass((EntityDecoy)p_77032_1_, p_77032_2_, p_77032_3_); + } + + protected void renderEquippedItems(EntityLivingBase p_77029_1_, float p_77029_2_) + { + this.renderEquippedItems((EntityDecoy)p_77029_1_, p_77029_2_); + } + + /** + * Actually renders the given argument. This is a synthetic bridge method, always casting down its argument and then + * handing it off to a worker function which does the actual work. In all probabilty, the class Render is generic + * (Render +public class RenderEvilWizard extends RenderBiped { static final ResourceLocation[] textures = new ResourceLocation[6]; - public RenderEvilWizard(RenderManager renderManager){ - - super(renderManager, new ModelWizard(), 0.5F); - + /** Model of the EvilWizard. */ + protected ModelWizard villagerModel; + + public RenderEvilWizard() + { + super(new ModelWizard(), 0.5F); + this.villagerModel = (ModelWizard)this.mainModel; for(int i=0;i<6;i++){ - textures[i] = new ResourceLocation(Wizardry.MODID, "textures/entity/evil_wizard_" + i + ".png"); + textures[i] = new ResourceLocation("wizardry:textures/entity/evil_wizard_" + i + ".png"); } - // Just using the default without overriding models, since the armour sets its own model anyway. - this.addLayer(new LayerBipedArmor(this)); - } - - @Override - protected ResourceLocation getEntityTexture(EntityEvilWizard wizard) { - return textures[wizard.textureIndex]; } + /** + * Determines wether Villager Render pass or not. + */ + protected int shouldVillagerRenderPass(EntityEvilWizard par1EntityEvilWizard, int par2, float par3) + { + return -1; + } + + public void renderVillager(EntityEvilWizard par1EntityEvilWizard, double par2, double par4, double par6, float par8, float par9) + { + super.doRender(par1EntityEvilWizard, par2, par4, par6, par8, par9); + } + + protected ResourceLocation func_110902_a(EntityEvilWizard par1EntityEvilWizard) + { + return textures[par1EntityEvilWizard.textureIndex]; + } + + protected void renderVillagerEquipedItems(EntityEvilWizard par1EntityEvilWizard, float par2) + { + super.renderEquippedItems(par1EntityEvilWizard, par2); + } + + protected void preRenderVillager(EntityEvilWizard par1EntityEvilWizard, float par2) + { + float f1 = 0.9375F; + this.shadowSize = 0.5F; + + GL11.glScalef(f1, f1, f1); + } + + public void doRenderLiving(EntityLiving par1EntityLiving, double par2, double par4, double par6, float par8, float par9) + { + this.renderVillager((EntityEvilWizard)par1EntityLiving, par2, par4, par6, par8, par9); + } + + /** + * Allows the render to do any OpenGL state modifications necessary before the model is rendered. Args: + * entityLiving, partialTickTime + */ + protected void preRenderCallback(EntityLivingBase par1EntityLivingBase, float par2) + { + this.preRenderVillager((EntityEvilWizard)par1EntityLivingBase, par2); + } + + /** + * Queries whether should render the specified pass or not. + */ + protected int shouldRenderPass(EntityLivingBase par1EntityLivingBase, int par2, float par3) + { + // TODO: Is this the right method name now? There are several with correct parameters. This one may end up being recursive! + return this.shouldRenderPass((EntityLiving)par1EntityLivingBase, par2, par3); + //return this.shouldVillagerRenderPass((EntityEvilWizard)par1EntityLivingBase, par2, par3); + } + + protected void renderEquippedItems(EntityLivingBase par1EntityLivingBase, float par2) + { + this.renderVillagerEquipedItems((EntityEvilWizard)par1EntityLivingBase, par2); + } + + public void renderPlayer(EntityLivingBase par1EntityLivingBase, double par2, double par4, double par6, float par8, float par9) + { + this.renderVillager((EntityEvilWizard)par1EntityLivingBase, par2, par4, par6, par8, par9); + } + + /** + * Returns the location of an entity's texture. Doesn't seem to be called unless you call Render.bindEntityTexture. + */ + protected ResourceLocation getEntityTexture(Entity par1Entity) + { + return this.func_110902_a((EntityEvilWizard)par1Entity); + } + + /** + * Actually renders the given argument. This is a synthetic bridge method, always casting down its argument and then + * handing it off to a worker function which does the actual work. In all probabilty, the class Render is generic + * (Render> 16 & 255) / 255.0F; + float g = (float)(colour >> 8 & 255) / 255.0F; + float b = (float)(colour & 255) / 255.0F; + + tessellator.setColorOpaque_F(f1*r, f1*g, f1*b); + this.renderBlocks.renderFaceYPos(block, -0.5D, -0.5D, -0.5D, this.renderBlocks.getBlockIconFromSideAndMetadata(block, 1, metadata)); + tessellator.setColorOpaque_F(f2, f2, f2); + this.renderBlocks.renderFaceZNeg(block, -0.5D, -0.5D, -0.5D, this.renderBlocks.getBlockIconFromSideAndMetadata(block, 2, metadata)); + tessellator.setColorOpaque_F(f2, f2, f2); + this.renderBlocks.renderFaceZPos(block, -0.5D, -0.5D, -0.5D, this.renderBlocks.getBlockIconFromSideAndMetadata(block, 3, metadata)); + tessellator.setColorOpaque_F(f3, f3, f3); + this.renderBlocks.renderFaceXNeg(block, -0.5D, -0.5D, -0.5D, this.renderBlocks.getBlockIconFromSideAndMetadata(block, 4, metadata)); + tessellator.setColorOpaque_F(f3, f3, f3); + this.renderBlocks.renderFaceXPos(block, -0.5D, -0.5D, -0.5D, this.renderBlocks.getBlockIconFromSideAndMetadata(block, 5, metadata)); + tessellator.draw(); + } +} diff --git a/src/main/java/electroblob/wizardry/client/renderer/RenderFireRing.java b/src/main/java/electroblob/wizardry/client/renderer/RenderFireRing.java index 1f70169f..c3eb7c69 100644 --- a/src/main/java/electroblob/wizardry/client/renderer/RenderFireRing.java +++ b/src/main/java/electroblob/wizardry/client/renderer/RenderFireRing.java @@ -1,102 +1,109 @@ package electroblob.wizardry.client.renderer; -import org.lwjgl.opengl.GL11; +import javax.swing.Icon; + +import org.lwjgl.opengl.GL11; +import org.lwjgl.opengl.GL12; -import electroblob.wizardry.entity.construct.EntityFireRing; -import net.minecraft.client.Minecraft; -import net.minecraft.client.renderer.GlStateManager; import net.minecraft.client.renderer.OpenGlHelper; import net.minecraft.client.renderer.Tessellator; -import net.minecraft.client.renderer.VertexBuffer; import net.minecraft.client.renderer.entity.Render; -import net.minecraft.client.renderer.entity.RenderManager; -import net.minecraft.client.renderer.texture.TextureAtlasSprite; import net.minecraft.client.renderer.texture.TextureMap; -import net.minecraft.client.renderer.vertex.DefaultVertexFormats; +import net.minecraft.entity.Entity; import net.minecraft.init.Blocks; +import net.minecraft.util.IIcon; import net.minecraft.util.ResourceLocation; -public class RenderFireRing extends Render { +public class RenderFireRing extends Render { private final ResourceLocation texture; private float scale = 1.0f; - public RenderFireRing(RenderManager renderManager, ResourceLocation texture, float scale) { - super(renderManager); + public RenderFireRing(ResourceLocation texture, float scale) { this.texture = texture; this.scale = scale; } @Override - public void doRender(EntityFireRing entity, double par2, double par4, double par6, float par8, float par9){ + public void doRender(Entity entity, double par2, double par4, double par6, float par8, float par9){ - GlStateManager.pushMatrix(); - GlStateManager.enableBlend(); - GlStateManager.disableLighting(); + GL11.glPushMatrix(); + GL11.glEnable(GL11.GL_BLEND); + GL11.glDisable(GL11.GL_LIGHTING); OpenGlHelper.setLightmapTextureCoords(OpenGlHelper.lightmapTexUnit, 240, 240); - GlStateManager.blendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA); + GL11.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA); float yOffset = 0; - GlStateManager.translate((float)par2, (float)par4 + yOffset, (float)par6); + if(entity.riddenByEntity != null){ + yOffset = entity.riddenByEntity.height/2; + } + + GL11.glTranslatef((float)par2, (float)par4 + yOffset, (float)par6); this.bindTexture(texture); float f6 = 1.0F; float f7 = 0.5F; float f8 = 0.5F; - GlStateManager.rotate(-90, 1, 0, 0); + GL11.glRotatef(-90, 1, 0, 0); - GlStateManager.scale(scale, scale, scale); + GL11.glScalef(scale, scale, scale); - Tessellator tessellator = Tessellator.getInstance(); - VertexBuffer buffer = tessellator.getBuffer(); - buffer.begin(GL11.GL_QUADS, DefaultVertexFormats.POSITION_TEX); - buffer.pos((double)(0.0F - f7), (double)(0.0F - f8), 0.01).tex(0, 1).endVertex(); - buffer.pos((double)(f6 - f7), (double)(0.0F - f8), 0.01).tex(1, 1).endVertex(); - buffer.pos((double)(f6 - f7), (double)(1.0F - f8), 0.01).tex(1, 0).endVertex(); - buffer.pos((double)(0.0F - f7), (double)(1.0F - f8), 0.01).tex(0, 0).endVertex(); + Tessellator tessellator = Tessellator.instance; + tessellator.startDrawingQuads(); + //tessellator.setColorRGBA_I(k1, 128); + tessellator.setNormal(0.0F, 1.0F, 0.0F); + tessellator.addVertexWithUV((double)(0.0F - f7), (double)(0.0F - f8), 0.01, 0, 1); + tessellator.addVertexWithUV((double)(f6 - f7), (double)(0.0F - f8), 0.01, 1, 1); + tessellator.addVertexWithUV((double)(f6 - f7), (double)(1.0F - f8), 0.01, 1, 0); + tessellator.addVertexWithUV((double)(0.0F - f7), (double)(1.0F - f8), 0.01, 0, 0); tessellator.draw(); - GlStateManager.disableBlend(); - GlStateManager.enableLighting(); - GlStateManager.disableRescaleNormal(); - GlStateManager.popMatrix(); + GL11.glDisable(GL11.GL_BLEND); + GL11.glEnable(GL11.GL_LIGHTING); + GL11.glDisable(GL12.GL_RESCALE_NORMAL); + GL11.glPopMatrix(); // Fire - GlStateManager.disableLighting(); - TextureAtlasSprite icon = Minecraft.getMinecraft().getBlockRendererDispatcher().getModelForState(Blocks.FIRE.getDefaultState()).getParticleTexture(); + GL11.glDisable(GL11.GL_LIGHTING); + IIcon icon = Blocks.fire.getFireIcon(0); + IIcon icon1 = Blocks.fire.getFireIcon(1); + int sides = 16; float height = 1.0f; for(int k=0; k 0.0F){ - - this.bindTexture(TextureMap.LOCATION_BLOCKS_TEXTURE); - float f71 = icon.getMinU(); - float f81 = icon.getMinV(); - float f9 = icon.getMaxU(); - float f10 = icon.getMaxV(); + while (f4 > 0.0F) + { + IIcon icon2 = i % 2 == 0 ? icon : icon1; + this.bindTexture(TextureMap.locationBlocksTexture); + float f71 = icon2.getMinU(); + float f81 = icon2.getMinV(); + float f9 = icon2.getMaxU(); + float f10 = icon2.getMaxV(); if (i / 2 % 2 == 0) { @@ -105,10 +112,10 @@ public class RenderFireRing extends Render { f71 = f11; } - buffer.pos((double)(f2 - f3), (double)(0.0F - f5), (double)f61).tex((double)f9, (double)f10).endVertex(); - buffer.pos((double)(-f2 - f3), (double)(0.0F - f5), (double)f61).tex((double)f71, (double)f10).endVertex(); - buffer.pos((double)(-f2 - f3), (double)(height - f5), (double)f61).tex((double)f71, (double)f81).endVertex(); - buffer.pos((double)(f2 - f3), (double)(height - f5), (double)f61).tex((double)f9, (double)f81).endVertex(); + tessellator.addVertexWithUV((double)(f2 - f3), (double)(0.0F - f5), (double)f61, (double)f9, (double)f10); + tessellator.addVertexWithUV((double)(-f2 - f3), (double)(0.0F - f5), (double)f61, (double)f71, (double)f10); + tessellator.addVertexWithUV((double)(-f2 - f3), (double)(height - f5), (double)f61, (double)f71, (double)f81); + tessellator.addVertexWithUV((double)(f2 - f3), (double)(height - f5), (double)f61, (double)f9, (double)f81); f4 -= 0.45F; f5 -= 0.45F; f2 *= 0.9F; @@ -117,35 +124,38 @@ public class RenderFireRing extends Render { } tessellator.draw(); - GlStateManager.popMatrix(); + GL11.glPopMatrix(); } for(int k=0; k 0.0F){ - - this.bindTexture(TextureMap.LOCATION_BLOCKS_TEXTURE); - float f71 = icon.getMinU(); - float f81 = icon.getMinV(); - float f9 = icon.getMaxU(); - float f10 = icon.getMaxV(); + while (f4 > 0.0F) + { + IIcon icon2 = i % 2 == 0 ? icon : icon1; + this.bindTexture(TextureMap.locationBlocksTexture); + float f71 = icon2.getMinU(); + float f81 = icon2.getMinV(); + float f9 = icon2.getMaxU(); + float f10 = icon2.getMaxV(); if (i / 2 % 2 == 0) { @@ -154,10 +164,10 @@ public class RenderFireRing extends Render { f71 = f11; } - buffer.pos((double)(f2 - f3), (double)(0.0F - f5), (double)f61).tex((double)f9, (double)f10).endVertex(); - buffer.pos((double)(-f2 - f3), (double)(0.0F - f5), (double)f61).tex((double)f71, (double)f10).endVertex(); - buffer.pos((double)(-f2 - f3), (double)(height - f5), (double)f61).tex((double)f71, (double)f81).endVertex(); - buffer.pos((double)(f2 - f3), (double)(height - f5), (double)f61).tex((double)f9, (double)f81).endVertex(); + tessellator.addVertexWithUV((double)(f2 - f3), (double)(0.0F - f5), (double)f61, (double)f9, (double)f10); + tessellator.addVertexWithUV((double)(-f2 - f3), (double)(0.0F - f5), (double)f61, (double)f71, (double)f10); + tessellator.addVertexWithUV((double)(-f2 - f3), (double)(height - f5), (double)f61, (double)f71, (double)f81); + tessellator.addVertexWithUV((double)(f2 - f3), (double)(height - f5), (double)f61, (double)f9, (double)f81); f4 -= 0.45F; f5 -= 0.45F; f2 *= 0.9F; @@ -166,14 +176,14 @@ public class RenderFireRing extends Render { } tessellator.draw(); - GlStateManager.popMatrix(); + GL11.glPopMatrix(); } - GlStateManager.enableLighting(); + GL11.glEnable(GL11.GL_LIGHTING); } @Override - protected ResourceLocation getEntityTexture(EntityFireRing entity) { + protected ResourceLocation getEntityTexture(Entity entity) { return null; } diff --git a/src/main/java/electroblob/wizardry/client/renderer/RenderFirestorm.java b/src/main/java/electroblob/wizardry/client/renderer/RenderFirestorm.java new file mode 100644 index 00000000..f2f99f07 --- /dev/null +++ b/src/main/java/electroblob/wizardry/client/renderer/RenderFirestorm.java @@ -0,0 +1,205 @@ +package electroblob.wizardry.client.renderer; + +import org.lwjgl.opengl.GL11; + +import net.minecraft.client.renderer.OpenGlHelper; +import net.minecraft.client.renderer.Tessellator; +import net.minecraft.client.renderer.entity.Render; +import net.minecraft.client.renderer.texture.TextureMap; +import net.minecraft.entity.Entity; +import net.minecraft.init.Blocks; +import net.minecraft.util.IIcon; +import net.minecraft.util.ResourceLocation; + +public class RenderFirestorm extends Render { + + private final ResourceLocation texture; + private float scale = 1.0f; + + public RenderFirestorm(ResourceLocation texture, float scale) { + this.texture = texture; + this.scale = scale; + } + + @Override + public void doRender(Entity entity, double par2, double par4, double par6, float par8, float par9){ + + GL11.glPushMatrix(); + + GL11.glTranslatef((float)par2, (float)par4, (float)par6); + + GL11.glRotatef(-entity.rotationYaw, 0, 1, 0); + GL11.glRotatef(entity.rotationPitch, 1, 0, 0); + + GL11.glPushMatrix(); + + GL11.glEnable(GL11.GL_BLEND); + GL11.glDisable(GL11.GL_LIGHTING); + OpenGlHelper.setLightmapTextureCoords(OpenGlHelper.lightmapTexUnit, 240, 240); + GL11.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA); + + this.bindTexture(texture); + float f6 = 1.0F; + float f7 = 0.5F; + float f8 = 0.5F; + + //GL11.glRotatef(-90, 1, 0, 0); + + GL11.glScalef(scale, scale, scale); + + Tessellator tessellator = Tessellator.instance; + + tessellator.startDrawingQuads(); + tessellator.setNormal(0.0F, 1.0F, 0.0F); + + tessellator.addVertexWithUV((double)(0.0F - f7), (double)(0.0F - f8), 0.01, 0, 1); + tessellator.addVertexWithUV((double)(f6 - f7), (double)(0.0F - f8), 0.01, 1, 1); + tessellator.addVertexWithUV((double)(f6 - f7), (double)(1.0F - f8), 0.01, 1, 0); + tessellator.addVertexWithUV((double)(0.0F - f7), (double)(1.0F - f8), 0.01, 0, 0); + + tessellator.draw(); + + tessellator.startDrawingQuads(); + tessellator.setNormal(0.0F, 1.0F, 0.0F); + + tessellator.addVertexWithUV((double)(0.0F - f7), (double)(0.0F - f8), 0.01, 0, 1); + tessellator.addVertexWithUV((double)(0.0F - f7), (double)(1.0F - f8), 0.01, 0, 0); + tessellator.addVertexWithUV((double)(f6 - f7), (double)(1.0F - f8), 0.01, 1, 0); + tessellator.addVertexWithUV((double)(f6 - f7), (double)(0.0F - f8), 0.01, 1, 1); + + tessellator.draw(); + + GL11.glDisable(GL11.GL_BLEND); + GL11.glEnable(GL11.GL_LIGHTING); + //GL11.glDisable(GL12.GL_RESCALE_NORMAL); + + GL11.glPopMatrix(); + + // Fire + + GL11.glDisable(GL11.GL_LIGHTING); + IIcon icon = Blocks.fire.getFireIcon(0); + IIcon icon1 = Blocks.fire.getFireIcon(1); + + int sides = 16; + float height = 2.0f; + + for(int k=0; k 0.0F) + { + IIcon icon2 = i % 2 == 0 ? icon : icon1; + this.bindTexture(TextureMap.locationBlocksTexture); + float f71 = icon2.getMinU(); + float f81 = icon2.getMinV(); + float f9 = icon2.getMaxU(); + float f10 = icon2.getMaxV(); + + if (i / 2 % 2 == 0) + { + float f11 = f9; + f9 = f71; + f71 = f11; + } + + tessellator.addVertexWithUV((double)(f2 - f3), (double)(0.0F - f5), (double)f61, (double)f9, (double)f10); + tessellator.addVertexWithUV((double)(-f2 - f3), (double)(0.0F - f5), (double)f61, (double)f71, (double)f10); + tessellator.addVertexWithUV((double)(-f2 - f3), (double)(height - f5), (double)f61, (double)f71, (double)f81); + tessellator.addVertexWithUV((double)(f2 - f3), (double)(height - f5), (double)f61, (double)f9, (double)f81); + f4 -= 0.45F; + f5 -= 0.45F; + f2 *= 0.9F; + f61 += 0.03F; + ++i; + } + + tessellator.draw(); + GL11.glPopMatrix(); + } + + for(int k=0; k 0.0F) + { + IIcon icon2 = i % 2 == 0 ? icon : icon1; + this.bindTexture(TextureMap.locationBlocksTexture); + float f71 = icon2.getMinU(); + float f81 = icon2.getMinV(); + float f9 = icon2.getMaxU(); + float f10 = icon2.getMaxV(); + + if (i / 2 % 2 == 0) + { + float f11 = f9; + f9 = f71; + f71 = f11; + } + + tessellator.addVertexWithUV((double)(f2 - f3), (double)(0.0F - f5), (double)f61, (double)f9, (double)f10); + tessellator.addVertexWithUV((double)(-f2 - f3), (double)(0.0F - f5), (double)f61, (double)f71, (double)f10); + tessellator.addVertexWithUV((double)(-f2 - f3), (double)(height - f5), (double)f61, (double)f71, (double)f81); + tessellator.addVertexWithUV((double)(f2 - f3), (double)(height - f5), (double)f61, (double)f9, (double)f81); + f4 -= 0.45F; + f5 -= 0.45F; + f2 *= 0.9F; + f61 += 0.03F; + ++i; + } + + tessellator.draw(); + GL11.glPopMatrix(); + } + + GL11.glEnable(GL11.GL_LIGHTING); + + GL11.glPopMatrix(); + } + + @Override + protected ResourceLocation getEntityTexture(Entity entity) { + return null; + } + +} diff --git a/src/main/java/electroblob/wizardry/client/renderer/RenderForceArrow.java b/src/main/java/electroblob/wizardry/client/renderer/RenderForceArrow.java index db37b23d..215bf087 100644 --- a/src/main/java/electroblob/wizardry/client/renderer/RenderForceArrow.java +++ b/src/main/java/electroblob/wizardry/client/renderer/RenderForceArrow.java @@ -1,47 +1,44 @@ package electroblob.wizardry.client.renderer; -import org.lwjgl.opengl.GL11; - -import electroblob.wizardry.Wizardry; -import electroblob.wizardry.entity.projectile.EntityForceArrow; -import net.minecraft.client.renderer.GlStateManager; import net.minecraft.client.renderer.OpenGlHelper; import net.minecraft.client.renderer.Tessellator; -import net.minecraft.client.renderer.VertexBuffer; import net.minecraft.client.renderer.entity.Render; -import net.minecraft.client.renderer.entity.RenderManager; -import net.minecraft.client.renderer.vertex.DefaultVertexFormats; +import net.minecraft.entity.Entity; +import net.minecraft.entity.projectile.EntityArrow; +import net.minecraft.util.MathHelper; import net.minecraft.util.ResourceLocation; -import net.minecraft.util.math.MathHelper; -import net.minecraftforge.fml.relauncher.Side; -import net.minecraftforge.fml.relauncher.SideOnly; + +import org.lwjgl.opengl.GL11; +import org.lwjgl.opengl.GL12; + +import cpw.mods.fml.relauncher.Side; +import cpw.mods.fml.relauncher.SideOnly; +import electroblob.wizardry.entity.projectile.EntityMagicMissile; @SideOnly(Side.CLIENT) -public class RenderForceArrow extends Render{ +public class RenderForceArrow extends Render +{ + private static final ResourceLocation arrowTextures = new ResourceLocation("wizardry:textures/entity/force_arrow.png"); - private static final ResourceLocation arrowTextures = new ResourceLocation(Wizardry.MODID, "textures/entity/force_arrow.png"); - - public RenderForceArrow(RenderManager renderManager){ - super(renderManager); + public RenderForceArrow(){ + } - @Override - public void doRender(EntityForceArrow arrow, double par2, double par4, double par6, float par8, float par9){ - - this.bindEntityTexture(arrow); - GlStateManager.pushMatrix(); - GlStateManager.disableLighting(); + public void renderArrow(Entity par1EntityArrow, double par2, double par4, double par6, float par8, float par9) + { + this.bindEntityTexture(par1EntityArrow); + GL11.glPushMatrix(); + GL11.glDisable(GL11.GL_LIGHTING); OpenGlHelper.setLightmapTextureCoords(OpenGlHelper.lightmapTexUnit, 240f, 240f); - GlStateManager.enableBlend(); - GlStateManager.blendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA); + GL11.glEnable(GL11.GL_BLEND); + GL11.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA); - GlStateManager.translate((float)par2, (float)par4, (float)par6); - GlStateManager.rotate(arrow.prevRotationYaw + (arrow.rotationYaw - arrow.prevRotationYaw) * par9 - 90.0F, 0.0F, 1.0F, 0.0F); - GlStateManager.rotate(arrow.prevRotationPitch + (arrow.rotationPitch - arrow.prevRotationPitch) * par9, 0.0F, 0.0F, 1.0F); - GlStateManager.rotate(180, 0, 1, 0); - Tessellator tessellator = Tessellator.getInstance(); - VertexBuffer buffer = tessellator.getBuffer(); + GL11.glTranslatef((float)par2, (float)par4, (float)par6); + GL11.glRotatef(par1EntityArrow.prevRotationYaw + (par1EntityArrow.rotationYaw - par1EntityArrow.prevRotationYaw) * par9 - 90.0F, 0.0F, 1.0F, 0.0F); + GL11.glRotatef(par1EntityArrow.prevRotationPitch + (par1EntityArrow.rotationPitch - par1EntityArrow.prevRotationPitch) * par9, 0.0F, 0.0F, 1.0F); + GL11.glRotatef(180, 0, 1, 0); + Tessellator tessellator = Tessellator.instance; float pixel = 1.0f/32.0f; float u1 = 0.0f; float u2 = pixel*14; @@ -57,71 +54,89 @@ public class RenderForceArrow extends Render{ float v6 = 1.0f; float scale = 0.05625F; float f11 = 0.0f; - GlStateManager.enableRescaleNormal(); + GL11.glEnable(GL12.GL_RESCALE_NORMAL); //f11 = (float)par1EntityArrow.arrowShake - par9; if (f11 > 0.0F) { float f12 = -MathHelper.sin(f11 * 3.0F) * f11; - GlStateManager.rotate(f12, 0.0F, 0.0F, 1.0F); + GL11.glRotatef(f12, 0.0F, 0.0F, 1.0F); } scale*=0.8f; - GlStateManager.rotate(45.0F, 1.0F, 0.0F, 0.0F); - GlStateManager.scale(scale, scale, scale); - GlStateManager.translate(-4.0F, 0.0F, 0.0F); + GL11.glRotatef(45.0F, 1.0F, 0.0F, 0.0F); + GL11.glScalef(scale, scale, scale); + GL11.glTranslatef(-4.0F, 0.0F, 0.0F); - buffer.begin(GL11.GL_QUADS, DefaultVertexFormats.POSITION_TEX); - buffer.pos(-5, 3.5, -3.5).tex((double)u5, (double)v5).endVertex(); - buffer.pos(-5, 3.5, 3.5).tex((double)u6, (double)v5).endVertex(); - buffer.pos(-5, -3.5, 3.5).tex((double)u6, (double)v6).endVertex(); - buffer.pos(-5, -3.5, -3.5).tex((double)u5, (double)v6); + tessellator.startDrawingQuads(); + tessellator.addVertexWithUV(-5, 3.5, -3.5, (double)u5, (double)v5); + tessellator.addVertexWithUV(-5, 3.5, 3.5, (double)u6, (double)v5); + tessellator.addVertexWithUV(-5, -3.5, 3.5, (double)u6, (double)v6); + tessellator.addVertexWithUV(-5, -3.5, -3.5, (double)u5, (double)v6); tessellator.draw(); for(int i=0; i<5; i++){ - GlStateManager.color(1, 1, 1, 1 - i*0.2f); - double j = i + ((double)arrow.ticksExisted%3)/3; + GL11.glColor4f(1, 1, 1, 1 - i*0.2f); + double j = i + ((double)par1EntityArrow.ticksExisted%3)/3; double width = 2.0d + (Math.sqrt(j*2)-0.6)*2; GL11.glNormal3f(scale, 0.0F, 0.0F); - buffer.begin(GL11.GL_QUADS, DefaultVertexFormats.POSITION_TEX); - buffer.pos(-10 + j*4, -width, -width).tex((double)u3, (double)v3).endVertex(); - buffer.pos(-10 + j*4, -width, width).tex((double)u4, (double)v3).endVertex(); - buffer.pos(-10 + j*4, width, width).tex((double)u4, (double)v4).endVertex(); - buffer.pos(-10 + j*4, width, -width).tex((double)u3, (double)v4).endVertex(); + tessellator.startDrawingQuads(); + tessellator.addVertexWithUV(-10 + j*4, -width, -width, (double)u3, (double)v3); + tessellator.addVertexWithUV(-10 + j*4, -width, width, (double)u4, (double)v3); + tessellator.addVertexWithUV(-10 + j*4, width, width, (double)u4, (double)v4); + tessellator.addVertexWithUV(-10 + j*4, width, -width, (double)u3, (double)v4); tessellator.draw(); GL11.glNormal3f(-scale, 0.0F, 0.0F); - buffer.begin(GL11.GL_QUADS, DefaultVertexFormats.POSITION_TEX); - buffer.pos(-10 + j*4, width, -width).tex((double)u3, (double)v3).endVertex(); - buffer.pos(-10 + j*4, width, width).tex((double)u4, (double)v3).endVertex(); - buffer.pos(-10 + j*4, -width, width).tex((double)u4, (double)v4).endVertex(); - buffer.pos(-10 + j*4, -width, -width).tex((double)u3, (double)v4).endVertex(); + tessellator.startDrawingQuads(); + tessellator.addVertexWithUV(-10 + j*4, width, -width, (double)u3, (double)v3); + tessellator.addVertexWithUV(-10 + j*4, width, width, (double)u4, (double)v3); + tessellator.addVertexWithUV(-10 + j*4, -width, width, (double)u4, (double)v4); + tessellator.addVertexWithUV(-10 + j*4, -width, -width, (double)u3, (double)v4); tessellator.draw(); } - GlStateManager.color(1, 1, 1, 1); + GL11.glColor4f(1, 1, 1, 1); for (int i = 0; i < 4; ++i) { - GlStateManager.rotate(90.0F, 1.0F, 0.0F, 0.0F); + GL11.glRotatef(90.0F, 1.0F, 0.0F, 0.0F); GL11.glNormal3f(0.0F, 0.0F, scale); - buffer.begin(GL11.GL_QUADS, DefaultVertexFormats.POSITION_TEX); - buffer.pos(-10.0D, -4.0D, 0.0D).tex((double)u1, (double)v1).endVertex(); - buffer.pos(10.0D, -4.0D, 0.0D).tex((double)u2, (double)v1).endVertex(); - buffer.pos(10.0D, 4.0D, 0.0D).tex((double)u2, (double)v2).endVertex(); - buffer.pos(-10.0D, 4.0D, 0.0D).tex((double)u1, (double)v2).endVertex(); + tessellator.startDrawingQuads(); + tessellator.addVertexWithUV(-10.0D, -4.0D, 0.0D, (double)u1, (double)v1); + tessellator.addVertexWithUV(10.0D, -4.0D, 0.0D, (double)u2, (double)v1); + tessellator.addVertexWithUV(10.0D, 4.0D, 0.0D, (double)u2, (double)v2); + tessellator.addVertexWithUV(-10.0D, 4.0D, 0.0D, (double)u1, (double)v2); tessellator.draw(); } - GlStateManager.disableBlend(); + GL11.glDisable(GL11.GL_BLEND); - GlStateManager.disableRescaleNormal(); - GlStateManager.enableLighting(); - GlStateManager.popMatrix(); + GL11.glDisable(GL12.GL_RESCALE_NORMAL); + GL11.glEnable(GL11.GL_LIGHTING); + GL11.glPopMatrix(); } - @Override - protected ResourceLocation getEntityTexture(EntityForceArrow par1Entity) + protected ResourceLocation getArrowTextures(Entity par1Entity) { return arrowTextures; } + + /** + * Returns the location of an entity's texture. Doesn't seem to be called unless you call Render.bindEntityTexture. + */ + protected ResourceLocation getEntityTexture(Entity par1Entity) + { + return this.getArrowTextures(par1Entity); + } + + /** + * Actually renders the given argument. This is a synthetic bridge method, always casting down its argument and then + * handing it off to a worker function which does the actual work. In all probabilty, the class Render is generic + * (Render { +public class RenderHammer extends Render { - private static final ResourceLocation texture = new ResourceLocation(Wizardry.MODID, "textures/entity/lightning_hammer.png"); + private static final ResourceLocation texture = new ResourceLocation("wizardry:textures/entity/lightning_hammer.png"); private ModelHammer model = new ModelHammer(); - public RenderHammer(RenderManager renderManager){ - super(renderManager); + public RenderHammer(){ + } @Override - public void doRender(EntityHammer entity, double x, double y, double z, float f, float f1) { + public void doRender(Entity entity, double x, double y, double z, float f, float f1) { - GlStateManager.pushMatrix(); - GlStateManager.translate(x, y+1.5, z); - GlStateManager.rotate(180, 0F, 0F, 1F); + GL11.glPushMatrix(); + GL11.glTranslated(x, y+1.5, z); + GL11.glRotatef(180, 0F, 0F, 1F); this.bindTexture(texture); model.render(entity, 0, 0, 0, 0, 0, 0.0625f); - GlStateManager.popMatrix(); + GL11.glPopMatrix(); } @Override - protected ResourceLocation getEntityTexture(EntityHammer entity) { + protected ResourceLocation getEntityTexture(Entity entity) { return texture; } diff --git a/src/main/java/electroblob/wizardry/client/renderer/RenderIceGiant.java b/src/main/java/electroblob/wizardry/client/renderer/RenderIceGiant.java index 4ee0812e..bdc4ea7b 100644 --- a/src/main/java/electroblob/wizardry/client/renderer/RenderIceGiant.java +++ b/src/main/java/electroblob/wizardry/client/renderer/RenderIceGiant.java @@ -1,39 +1,104 @@ package electroblob.wizardry.client.renderer; -import electroblob.wizardry.Wizardry; +import org.lwjgl.opengl.GL11; + +import cpw.mods.fml.relauncher.Side; +import cpw.mods.fml.relauncher.SideOnly; import electroblob.wizardry.client.model.ModelIceGiant; import electroblob.wizardry.entity.living.EntityIceGiant; -import net.minecraft.client.renderer.GlStateManager; import net.minecraft.client.renderer.entity.RenderLiving; -import net.minecraft.client.renderer.entity.RenderManager; +import net.minecraft.entity.Entity; +import net.minecraft.entity.EntityLiving; +import net.minecraft.entity.EntityLivingBase; import net.minecraft.util.ResourceLocation; -import net.minecraftforge.fml.relauncher.Side; -import net.minecraftforge.fml.relauncher.SideOnly; @SideOnly(Side.CLIENT) -public class RenderIceGiant extends RenderLiving { - - private static final ResourceLocation texture = new ResourceLocation(Wizardry.MODID, "textures/entity/ice_giant.png"); +public class RenderIceGiant extends RenderLiving +{ + private static final ResourceLocation iceGiantTextures = new ResourceLocation("wizardry:textures/entity/ice_giant.png"); - public RenderIceGiant(RenderManager renderManager){ - super(renderManager, new ModelIceGiant(), 0.5F); + /** Iron Golem's Model. */ + private final ModelIceGiant iceGiantModel; + + public RenderIceGiant() + { + super(new ModelIceGiant(), 0.5F); + this.iceGiantModel = (ModelIceGiant)this.mainModel; } - @Override - protected ResourceLocation getEntityTexture(EntityIceGiant entity){ - return texture; - } - - @Override - protected void rotateCorpse(EntityIceGiant entityLiving, float pitch, float yaw, float partialTicks){ - - super.rotateCorpse(entityLiving, pitch, yaw, partialTicks); - - if ((double)entityLiving.limbSwingAmount >= 0.01D){ + /** + * Renders the Iron Golem. + */ + public void doRenderIceGiant(EntityIceGiant par1EntityIceGiant, double par2, double par4, double par6, float par8, float par9) + { + super.doRender(par1EntityIceGiant, par2, par4, par6, par8, par9); + } + + protected ResourceLocation getIceGiantTextures(EntityIceGiant par1EntityIceGiant) + { + return iceGiantTextures; + } + + /** + * Rotates Iron Golem corpse. + */ + protected void rotateIceGiantCorpse(EntityIceGiant par1EntityIceGiant, float par2, float par3, float par4) + { + super.rotateCorpse(par1EntityIceGiant, par2, par3, par4); + + if ((double)par1EntityIceGiant.limbSwingAmount >= 0.01D) + { float f3 = 13.0F; - float f4 = entityLiving.limbSwing - entityLiving.limbSwingAmount * (1.0F - partialTicks) + 6.0F; + float f4 = par1EntityIceGiant.limbSwing - par1EntityIceGiant.limbSwingAmount * (1.0F - par4) + 6.0F; float f5 = (Math.abs(f4 % f3 - f3 * 0.5F) - f3 * 0.25F) / (f3 * 0.25F); - GlStateManager.rotate(6.5F * f5, 0.0F, 0.0F, 1.0F); + GL11.glRotatef(6.5F * f5, 0.0F, 0.0F, 1.0F); } } + + /** + * Renders Iron Golem Equipped items. + */ + protected void renderIceGiantEquippedItems(EntityIceGiant par1EntityIceGiant, float par2) + { + super.renderEquippedItems(par1EntityIceGiant, par2); + } + + public void doRenderLiving(EntityLiving par1EntityLiving, double par2, double par4, double par6, float par8, float par9) + { + this.doRenderIceGiant((EntityIceGiant)par1EntityLiving, par2, par4, par6, par8, par9); + } + + protected void renderEquippedItems(EntityLivingBase par1EntityLivingBase, float par2) + { + this.renderIceGiantEquippedItems((EntityIceGiant)par1EntityLivingBase, par2); + } + + protected void rotateCorpse(EntityLivingBase par1EntityLivingBase, float par2, float par3, float par4) + { + this.rotateIceGiantCorpse((EntityIceGiant)par1EntityLivingBase, par2, par3, par4); + } + + public void renderPlayer(EntityLivingBase par1EntityLivingBase, double par2, double par4, double par6, float par8, float par9) + { + this.doRenderIceGiant((EntityIceGiant)par1EntityLivingBase, par2, par4, par6, par8, par9); + } + + /** + * Returns the location of an entity's texture. Doesn't seem to be called unless you call Render.bindEntityTexture. + */ + protected ResourceLocation getEntityTexture(Entity par1Entity) + { + return this.getIceGiantTextures((EntityIceGiant)par1Entity); + } + + /** + * Actually renders the given argument. This is a synthetic bridge method, always casting down its argument and then + * handing it off to a worker function which does the actual work. In all probabilty, the class Render is generic + * (Render { +public class RenderIceSpike extends Render { - private static final ResourceLocation texture = new ResourceLocation(Wizardry.MODID, "textures/entity/ice_spike.png"); + private static final ResourceLocation texture = new ResourceLocation("wizardry:textures/entity/ice_spike.png"); + + public RenderIceSpike(){ - public RenderIceSpike(RenderManager renderManager){ - super(renderManager); } @Override - public void doRender(EntityIceSpike entity, double x, double y, double z, float fa, float partialTickTime) { + public void doRender(Entity entity, double x, double y, double z, float fa, float partialTickTime) { - GlStateManager.pushMatrix(); + GL11.glPushMatrix(); - GlStateManager.translate((float)x, (float)y, (float)z); + GL11.glTranslatef((float)x, (float)y, (float)z); // Apparently, disabling lighting... doesn't disable lighting. Or at least, you can still set the brightness // with setLightmapTextureCoords. - GlStateManager.disableLighting(); + GL11.glDisable(GL11.GL_LIGHTING); int j = entity.getBrightnessForRender(partialTickTime); int k = j % 65536; int l = j / 65536; OpenGlHelper.setLightmapTextureCoords(OpenGlHelper.lightmapTexUnit, (float)k / 1.0F, (float)l / 1.0F); - GlStateManager.color(1.0F, 1.0F, 1.0F, 1.0F); + GL11.glColor4f(1.0F, 1.0F, 1.0F, 1.0F); - Tessellator tessellator = Tessellator.getInstance(); - VertexBuffer buffer = tessellator.getBuffer(); + Tessellator tessellator = Tessellator.instance; bindTexture(texture); // West face - buffer.begin(GL11.GL_QUADS, DefaultVertexFormats.POSITION_TEX); - buffer.pos(0, 0, 0.5).tex(1, 1).endVertex(); - buffer.pos(0, 1, 0.5).tex(1, 0).endVertex(); - buffer.pos(0, 1, -0.5).tex(0, 0).endVertex(); - buffer.pos(0, 0, -0.5).tex(0, 1).endVertex(); + tessellator.startDrawingQuads(); + tessellator.addVertexWithUV(0, 0, 0.5, 1, 1); + tessellator.addVertexWithUV(0, 1, 0.5, 1, 0); + tessellator.addVertexWithUV(0, 1, -0.5, 0, 0); + tessellator.addVertexWithUV(0, 0, -0.5, 0, 1); tessellator.draw(); // South face - buffer.begin(GL11.GL_QUADS, net.minecraft.client.renderer.vertex.DefaultVertexFormats.POSITION_TEX); - buffer.pos( 0.5, 0, 0).tex(1, 1).endVertex(); - buffer.pos( 0.5, 1, 0).tex(1, 0).endVertex(); - buffer.pos(-0.5, 1, 0).tex(0, 0).endVertex(); - buffer.pos(-0.5, 0, 0).tex(0, 1).endVertex(); + tessellator.startDrawingQuads(); + tessellator.addVertexWithUV( 0.5, 0, 0, 1, 1); + tessellator.addVertexWithUV( 0.5, 1, 0, 1, 0); + tessellator.addVertexWithUV(-0.5, 1, 0, 0, 0); + tessellator.addVertexWithUV(-0.5, 0, 0, 0, 1); tessellator.draw(); // East face - buffer.begin(GL11.GL_QUADS, net.minecraft.client.renderer.vertex.DefaultVertexFormats.POSITION_TEX); - buffer.pos(0, 0, -0.5).tex(0, 1).endVertex(); - buffer.pos(0, 1, -0.5).tex(0, 0).endVertex(); - buffer.pos(0, 1, 0.5).tex(1, 0).endVertex(); - buffer.pos(0, 0, 0.5).tex(1, 1).endVertex(); + tessellator.startDrawingQuads(); + tessellator.addVertexWithUV(0, 0, -0.5, 0, 1); + tessellator.addVertexWithUV(0, 1, -0.5, 0, 0); + tessellator.addVertexWithUV(0, 1, 0.5, 1, 0); + tessellator.addVertexWithUV(0, 0, 0.5, 1, 1); tessellator.draw(); // North face - buffer.begin(GL11.GL_QUADS, net.minecraft.client.renderer.vertex.DefaultVertexFormats.POSITION_TEX); - buffer.pos(-0.5, 0, 0).tex(0, 1).endVertex(); - buffer.pos(-0.5, 1, 0).tex(0, 0).endVertex(); - buffer.pos( 0.5, 1, 0).tex(1, 0).endVertex(); - buffer.pos( 0.5, 0, 0).tex(1, 1).endVertex(); + tessellator.startDrawingQuads(); + tessellator.addVertexWithUV(-0.5, 0, 0, 0, 1); + tessellator.addVertexWithUV(-0.5, 1, 0, 0, 0); + tessellator.addVertexWithUV( 0.5, 1, 0, 1, 0); + tessellator.addVertexWithUV( 0.5, 0, 0, 1, 1); tessellator.draw(); - GlStateManager.enableLighting(); - GlStateManager.popMatrix(); + GL11.glEnable(GL11.GL_LIGHTING); + GL11.glPopMatrix(); } @Override - protected ResourceLocation getEntityTexture(EntityIceSpike entity) { + protected ResourceLocation getEntityTexture(Entity entity) { return texture; } diff --git a/src/main/java/electroblob/wizardry/client/renderer/RenderLightningDisc.java b/src/main/java/electroblob/wizardry/client/renderer/RenderLightningDisc.java index a9b620fa..a108a7cf 100644 --- a/src/main/java/electroblob/wizardry/client/renderer/RenderLightningDisc.java +++ b/src/main/java/electroblob/wizardry/client/renderer/RenderLightningDisc.java @@ -1,81 +1,82 @@ package electroblob.wizardry.client.renderer; import org.lwjgl.opengl.GL11; +import org.lwjgl.opengl.GL12; -import electroblob.wizardry.entity.projectile.EntityLightningDisc; -import net.minecraft.client.renderer.GlStateManager; +import electroblob.wizardry.entity.construct.EntityBubble; +import electroblob.wizardry.entity.construct.EntityHealAura; import net.minecraft.client.renderer.OpenGlHelper; import net.minecraft.client.renderer.Tessellator; -import net.minecraft.client.renderer.VertexBuffer; import net.minecraft.client.renderer.entity.Render; -import net.minecraft.client.renderer.entity.RenderManager; -import net.minecraft.client.renderer.vertex.DefaultVertexFormats; +import net.minecraft.entity.Entity; import net.minecraft.util.ResourceLocation; -public class RenderLightningDisc extends Render { +public class RenderLightningDisc extends Render { private final ResourceLocation texture; private float scale = 1.0f; - public RenderLightningDisc(RenderManager renderManager, ResourceLocation texture, float scale) { - super(renderManager); + public RenderLightningDisc(ResourceLocation texture, float scale) { this.texture = texture; this.scale = scale; } @Override - public void doRender(EntityLightningDisc entity, double par2, double par4, double par6, float par8, float par9){ + public void doRender(Entity entity, double par2, double par4, double par6, float par8, float par9){ - GlStateManager.pushMatrix(); - GlStateManager.enableBlend(); - GlStateManager.disableLighting(); + GL11.glPushMatrix(); + GL11.glEnable(GL11.GL_BLEND); + GL11.glDisable(GL11.GL_LIGHTING); OpenGlHelper.setLightmapTextureCoords(OpenGlHelper.lightmapTexUnit, 240, 240); - GlStateManager.blendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA); + GL11.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA); float yOffset = 0; - GlStateManager.translate((float)par2, (float)par4 + yOffset, (float)par6); + if(entity.riddenByEntity != null){ + yOffset = entity.riddenByEntity.height/2; + } + + GL11.glTranslatef((float)par2, (float)par4 + yOffset, (float)par6); this.bindTexture(texture); float f6 = 1.0F; float f7 = 0.5F; float f8 = 0.5F; - GlStateManager.rotate(-90, 1, 0, 0); + GL11.glRotatef(-90, 1, 0, 0); - GlStateManager.rotate(entity.ticksExisted*8, 0, 0, 1); + GL11.glRotatef(entity.ticksExisted*8, 0, 0, 1); - GlStateManager.scale(scale, scale, scale); + GL11.glScalef(scale, scale, scale); - Tessellator tessellator = Tessellator.getInstance(); - VertexBuffer buffer = tessellator.getBuffer(); - buffer.begin(GL11.GL_QUADS, DefaultVertexFormats.POSITION_TEX); + Tessellator tessellator = Tessellator.instance; + tessellator.startDrawingQuads(); //tessellator.setColorRGBA_I(k1, 128); - //buffer.normal(0.0F, 1.0F, 0.0F); - buffer.pos((double)(0.0F - f7), (double)(0.0F - f8), 0.01).tex(0, 1).endVertex(); - buffer.pos((double)(f6 - f7), (double)(0.0F - f8), 0.01).tex(1, 1).endVertex(); - buffer.pos((double)(f6 - f7), (double)(1.0F - f8), 0.01).tex(1, 0).endVertex(); - buffer.pos((double)(0.0F - f7), (double)(1.0F - f8), 0.01).tex(0, 0).endVertex(); + tessellator.setNormal(0.0F, 1.0F, 0.0F); + tessellator.addVertexWithUV((double)(0.0F - f7), (double)(0.0F - f8), 0.01, 0, 1); + tessellator.addVertexWithUV((double)(f6 - f7), (double)(0.0F - f8), 0.01, 1, 1); + tessellator.addVertexWithUV((double)(f6 - f7), (double)(1.0F - f8), 0.01, 1, 0); + tessellator.addVertexWithUV((double)(0.0F - f7), (double)(1.0F - f8), 0.01, 0, 0); tessellator.draw(); - buffer.begin(GL11.GL_QUADS, net.minecraft.client.renderer.vertex.DefaultVertexFormats.POSITION_TEX); - //buffer.normal(0.0F, 1.0F, 0.0F); - buffer.pos((double)(0.0F - f7), (double)(1.0F - f8), 0.01).tex(0, 0).endVertex(); - buffer.pos((double)(f6 - f7), (double)(1.0F - f8), 0.01).tex(1, 0).endVertex(); - buffer.pos((double)(f6 - f7), (double)(0.0F - f8), 0.01).tex(1, 1).endVertex(); - buffer.pos((double)(0.0F - f7), (double)(0.0F - f8), 0.01).tex(0, 1).endVertex(); + tessellator.startDrawingQuads(); + tessellator.setNormal(0.0F, 1.0F, 0.0F); + tessellator.addVertexWithUV((double)(0.0F - f7), (double)(1.0F - f8), 0.01, 0, 0); + tessellator.addVertexWithUV((double)(f6 - f7), (double)(1.0F - f8), 0.01, 1, 0); + tessellator.addVertexWithUV((double)(f6 - f7), (double)(0.0F - f8), 0.01, 1, 1); + tessellator.addVertexWithUV((double)(0.0F - f7), (double)(0.0F - f8), 0.01, 0, 1); tessellator.draw(); - GlStateManager.disableBlend(); - GlStateManager.enableLighting(); - GlStateManager.disableRescaleNormal(); - GlStateManager.popMatrix(); + GL11.glDisable(GL11.GL_BLEND); + GL11.glEnable(GL11.GL_LIGHTING); + GL11.glDisable(GL12.GL_RESCALE_NORMAL); + GL11.glPopMatrix(); } @Override - protected ResourceLocation getEntityTexture(EntityLightningDisc entity) { + protected ResourceLocation getEntityTexture(Entity entity) { return null; } diff --git a/src/main/java/electroblob/wizardry/client/renderer/RenderLightningPulse.java b/src/main/java/electroblob/wizardry/client/renderer/RenderLightningPulse.java index 2190011a..4f46d325 100644 --- a/src/main/java/electroblob/wizardry/client/renderer/RenderLightningPulse.java +++ b/src/main/java/electroblob/wizardry/client/renderer/RenderLightningPulse.java @@ -1,71 +1,76 @@ package electroblob.wizardry.client.renderer; import org.lwjgl.opengl.GL11; +import org.lwjgl.opengl.GL12; -import electroblob.wizardry.Wizardry; -import electroblob.wizardry.entity.construct.EntityLightningPulse; -import net.minecraft.client.renderer.GlStateManager; +import electroblob.wizardry.WizardryUtilities; +import electroblob.wizardry.entity.construct.EntityHealAura; +import electroblob.wizardry.entity.construct.EntityMagicConstruct; +import net.minecraft.client.Minecraft; import net.minecraft.client.renderer.OpenGlHelper; import net.minecraft.client.renderer.Tessellator; -import net.minecraft.client.renderer.VertexBuffer; import net.minecraft.client.renderer.entity.Render; -import net.minecraft.client.renderer.entity.RenderManager; -import net.minecraft.client.renderer.vertex.DefaultVertexFormats; +import net.minecraft.entity.Entity; +import net.minecraft.entity.player.EntityPlayer; import net.minecraft.util.ResourceLocation; -public class RenderLightningPulse extends Render { +public class RenderLightningPulse extends Render { private final ResourceLocation[] textures = new ResourceLocation[8]; private float scale = 1.0f; - public RenderLightningPulse(RenderManager renderManager, float scale) { - super(renderManager); + public RenderLightningPulse(float scale) { for(int i=0; i { - - private final ResourceLocation texture; +public class RenderMagicArrow extends Render +{ + // This class can now be used to render any projectile; hence this field is no longer static or final. + private ResourceLocation arrowTextures; private boolean blend; private boolean renderEnds; private double length = 8.0, width = 2.0; private int pixelsLong = 16, pixelsWide = 5; - public RenderMagicArrow(RenderManager renderManager, ResourceLocation texture, boolean blend, double length, double width, int pixelsLong, int pixelsWide, boolean renderEnds){ - super(renderManager); - this.texture = texture; + public RenderMagicArrow(ResourceLocation texture, boolean blend, double length, double width, int pixelsLong, int pixelsWide, boolean renderEnds){ + this.arrowTextures = texture; this.blend = blend; this.renderEnds = renderEnds; this.length = length; @@ -35,24 +35,22 @@ public class RenderMagicArrow extends Render { this.pixelsWide = pixelsWide; } - @Override - public void doRender(EntityMagicArrow entity, double par2, double par4, double par6, float par8, float par9){ - + public void renderArrow(Entity entity, double par2, double par4, double par6, float par8, float par9) + { this.bindEntityTexture(entity); - GlStateManager.pushMatrix(); - GlStateManager.disableLighting(); + GL11.glPushMatrix(); + GL11.glDisable(GL11.GL_LIGHTING); OpenGlHelper.setLightmapTextureCoords(OpenGlHelper.lightmapTexUnit, 240f, 240f); if(this.blend){ - GlStateManager.enableBlend(); - GlStateManager.blendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA); + GL11.glEnable(GL11.GL_BLEND); + GL11.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA); } - GlStateManager.translate((float)par2, (float)par4, (float)par6); - GlStateManager.rotate(entity.prevRotationYaw + (entity.rotationYaw - entity.prevRotationYaw) * par9 - 90.0F, 0.0F, 1.0F, 0.0F); - GlStateManager.rotate(entity.prevRotationPitch + (entity.rotationPitch - entity.prevRotationPitch) * par9, 0.0F, 0.0F, 1.0F); - Tessellator tessellator = Tessellator.getInstance(); - VertexBuffer buffer = tessellator.getBuffer(); + GL11.glTranslatef((float)par2, (float)par4, (float)par6); + GL11.glRotatef(entity.prevRotationYaw + (entity.rotationYaw - entity.prevRotationYaw) * par9 - 90.0F, 0.0F, 1.0F, 0.0F); + GL11.glRotatef(entity.prevRotationPitch + (entity.rotationPitch - entity.prevRotationPitch) * par9, 0.0F, 0.0F, 1.0F); + Tessellator tessellator = Tessellator.instance; float f2 = 0.0F; float f3 = pixelsLong / 32.0F; float f4 = 0.0F; @@ -63,60 +61,78 @@ public class RenderMagicArrow extends Render { float f9 = (float)10 / 32.0F; float f10 = 0.05625F; float f11 = 0.0f; - GlStateManager.enableRescaleNormal(); + GL11.glEnable(GL12.GL_RESCALE_NORMAL); //f11 = (float)par1EntityArrow.arrowShake - par9; if (f11 > 0.0F) { float f12 = -MathHelper.sin(f11 * 3.0F) * f11; - GlStateManager.rotate(f12, 0.0F, 0.0F, 1.0F); + GL11.glRotatef(f12, 0.0F, 0.0F, 1.0F); } - GlStateManager.rotate(45.0F, 1.0F, 0.0F, 0.0F); - GlStateManager.scale(f10, f10, f10); - GlStateManager.translate(-4.0F, 0.0F, 0.0F); + GL11.glRotatef(45.0F, 1.0F, 0.0F, 0.0F); + GL11.glScalef(f10, f10, f10); + GL11.glTranslatef(-4.0F, 0.0F, 0.0F); GL11.glNormal3f(f10, 0.0F, 0.0F); if(renderEnds){ // Ends - buffer.begin(GL11.GL_QUADS, DefaultVertexFormats.POSITION_TEX); - buffer.pos(-7.0D, -width, -width).tex((double)f6, (double)f8).endVertex(); - buffer.pos(-7.0D, -width, width).tex((double)f7, (double)f8).endVertex(); - buffer.pos(-7.0D, width, width).tex((double)f7, (double)f9).endVertex(); - buffer.pos(-7.0D, width, -width).tex((double)f6, (double)f9).endVertex(); + tessellator.startDrawingQuads(); + tessellator.addVertexWithUV(-7.0D, -width, -width, (double)f6, (double)f8); + tessellator.addVertexWithUV(-7.0D, -width, width, (double)f7, (double)f8); + tessellator.addVertexWithUV(-7.0D, width, width, (double)f7, (double)f9); + tessellator.addVertexWithUV(-7.0D, width, -width, (double)f6, (double)f9); tessellator.draw(); GL11.glNormal3f(-f10, 0.0F, 0.0F); - buffer.begin(GL11.GL_QUADS, DefaultVertexFormats.POSITION_TEX); - buffer.pos(-7.0D, width, -width).tex((double)f6, (double)f8).endVertex(); - buffer.pos(-7.0D, width, width).tex((double)f7, (double)f8).endVertex(); - buffer.pos(-7.0D, -width, width).tex((double)f7, (double)f9).endVertex(); - buffer.pos(-7.0D, -width, -width).tex((double)f6, (double)f9).endVertex(); + tessellator.startDrawingQuads(); + tessellator.addVertexWithUV(-7.0D, width, -width, (double)f6, (double)f8); + tessellator.addVertexWithUV(-7.0D, width, width, (double)f7, (double)f8); + tessellator.addVertexWithUV(-7.0D, -width, width, (double)f7, (double)f9); + tessellator.addVertexWithUV(-7.0D, -width, -width, (double)f6, (double)f9); tessellator.draw(); } for (int i = 0; i < 4; ++i){ // Sides - GlStateManager.rotate(90.0F, 1.0F, 0.0F, 0.0F); + GL11.glRotatef(90.0F, 1.0F, 0.0F, 0.0F); GL11.glNormal3f(0.0F, 0.0F, f10); - buffer.begin(GL11.GL_QUADS, DefaultVertexFormats.POSITION_TEX); - buffer.pos(-length, -width, 0.0D).tex((double)f2, (double)f4).endVertex(); - buffer.pos(length, -width, 0.0D).tex((double)f3, (double)f4).endVertex(); - buffer.pos(length, width, 0.0D).tex((double)f3, (double)f5).endVertex(); - buffer.pos(-length, width, 0.0D).tex((double)f2, (double)f5).endVertex(); + tessellator.startDrawingQuads(); + tessellator.addVertexWithUV(-length, -width, 0.0D, (double)f2, (double)f4); + tessellator.addVertexWithUV(length, -width, 0.0D, (double)f3, (double)f4); + tessellator.addVertexWithUV(length, width, 0.0D, (double)f3, (double)f5); + tessellator.addVertexWithUV(-length, width, 0.0D, (double)f2, (double)f5); tessellator.draw(); } if(this.blend){ - GlStateManager.disableBlend(); + GL11.glDisable(GL11.GL_BLEND); } - GlStateManager.disableRescaleNormal(); - GlStateManager.enableLighting(); - GlStateManager.popMatrix(); + GL11.glDisable(GL12.GL_RESCALE_NORMAL); + GL11.glEnable(GL11.GL_LIGHTING); + GL11.glPopMatrix(); } - @Override - protected ResourceLocation getEntityTexture(EntityMagicArrow arrow) + protected ResourceLocation getArrowTextures(Entity par1Entity) { - return texture; + return arrowTextures; + } + + /** + * Returns the location of an entity's texture. Doesn't seem to be called unless you call Render.bindEntityTexture. + */ + protected ResourceLocation getEntityTexture(Entity par1Entity) + { + return this.getArrowTextures(par1Entity); + } + + /** + * Actually renders the given argument. This is a synthetic bridge method, always casting down its argument and then + * handing it off to a worker function which does the actual work. In all probabilty, the class Render is generic + * (Render { - - private static final ResourceLocation texture = new ResourceLocation(Wizardry.MODID, "textures/entity/light_ray.png"); - private static final ResourceLocation texture2 = new ResourceLocation(Wizardry.MODID, "textures/entity/light_aura.png"); +public class RenderMagicLight extends TileEntitySpecialRenderer{ + private static final ResourceLocation texture = new ResourceLocation("wizardry:textures/entity/light_ray.png"); + private static final ResourceLocation texture2 = new ResourceLocation("wizardry:textures/entity/light_aura.png"); + @Override - public void renderTileEntityAt(TileEntityMagicLight tileentity, double x, double y, double z, float f, int destroyStage){ + public void renderTileEntityAt(TileEntity tileentity, double x, double y, + double z, float f) { - GlStateManager.pushMatrix(); + GL11.glPushMatrix(); - GlStateManager.disableCull(); - GlStateManager.enableBlend(); - GlStateManager.shadeModel(GL11.GL_SMOOTH); - GlStateManager.disableLighting(); - OpenGlHelper.setLightmapTextureCoords(OpenGlHelper.lightmapTexUnit, 240f, 240f); - RenderHelper.disableStandardItemLighting(); + GL11.glDisable(GL11.GL_CULL_FACE); + GL11.glEnable(GL11.GL_BLEND); + GL11.glShadeModel(GL11.GL_SMOOTH); + GL11.glDisable(GL11.GL_LIGHTING); + OpenGlHelper.setLightmapTextureCoords(OpenGlHelper.lightmapTexUnit, 240f, 240f); + RenderHelper.disableStandardItemLighting(); + + if(tileentity instanceof TileEntityMagicLight){ + + TileEntityMagicLight timerentity = (TileEntityMagicLight)tileentity; - GlStateManager.translate(x + 0.5, y + 0.5, z + 0.5); + GL11.glTranslated(x + 0.5, y + 0.5, z + 0.5); - if(tileentity.timer < 10){ - GlStateManager.scale((float)tileentity.timer/10, (float)tileentity.timer/10, (float)tileentity.timer/10); - } - if(tileentity.timer > tileentity.maxTimer-10){ - GlStateManager.scale((float)(tileentity.maxTimer-tileentity.timer)/10, (float)(tileentity.maxTimer-tileentity.timer)/10, (float)(tileentity.maxTimer-tileentity.timer)/10); - } + if(timerentity.timer < 10){ + GL11.glScalef((float)timerentity.timer/10, (float)timerentity.timer/10, (float)timerentity.timer/10); + } + if(timerentity.timer > timerentity.maxTimer-10){ + GL11.glScalef((float)(timerentity.maxTimer-timerentity.timer)/10, (float)(timerentity.maxTimer-timerentity.timer)/10, (float)(timerentity.maxTimer-timerentity.timer)/10); + } + + // Renders the aura effect + + Tessellator tessellator = Tessellator.instance; + + GL11.glPushMatrix(); + + GL11.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA); - // Renders the aura effect + // This counteracts the reverse rotation behaviour when in front f5 view. + // Fun fact: this is a bug with vanilla too! Look at a snowball in front f5 view, for example. + float yaw = Minecraft.getMinecraft().gameSettings.thirdPersonView == 2 ? RenderManager.instance.playerViewX : -RenderManager.instance.playerViewX; + GL11.glRotatef(180.0F - RenderManager.instance.playerViewY, 0.0F, 1.0F, 0.0F); + GL11.glRotatef(yaw, 1.0F, 0.0F, 0.0F); + + tessellator.startDrawingQuads(); - Tessellator tessellator = Tessellator.getInstance(); - VertexBuffer buffer = tessellator.getBuffer(); - - GlStateManager.pushMatrix(); - - GlStateManager.blendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA); - - // This counteracts the reverse rotation behaviour when in front f5 view. - // Fun fact: this is a bug with vanilla too! Look at a snowball in front f5 view, for example. - float yaw = Minecraft.getMinecraft().gameSettings.thirdPersonView == 2 ? Minecraft.getMinecraft().getRenderManager().playerViewX : -Minecraft.getMinecraft().getRenderManager().playerViewX; - GlStateManager.rotate(180.0F - Minecraft.getMinecraft().getRenderManager().playerViewY, 0.0F, 1.0F, 0.0F); - GlStateManager.rotate(yaw, 1.0F, 0.0F, 0.0F); - - buffer.begin(GL11.GL_QUADS, DefaultVertexFormats.POSITION_TEX); - - this.bindTexture(texture2); - - buffer.pos(-0.6, 0.6, 0).tex(0, 0).endVertex(); - buffer.pos(0.6, 0.6, 0).tex(1, 0).endVertex(); - buffer.pos(0.6, -0.6, 0).tex(1, 1).endVertex(); - buffer.pos(-0.6, -0.6, 0).tex(0, 1).endVertex(); - - tessellator.draw(); - - GlStateManager.popMatrix(); - - // Renders the rays - - // For some reason, the old blend function (GL11.GL_SRC_ALPHA, GL11.GL_SRC_ALPHA) caused the innermost - // ends of the rays to appear black, so I have changed it to this, which looks very slightly different. - GlStateManager.blendFunc(GL11.GL_ONE, GL11.GL_SRC_ALPHA); - - this.bindTexture(texture); - - if(tileentity.randomiser.length >= 30){ + this.bindTexture(texture2); + + tessellator.addVertexWithUV(-0.6, 0.6, 0, 0, 0); + tessellator.addVertexWithUV(0.6, 0.6, 0, 1, 0); + tessellator.addVertexWithUV(0.6, -0.6, 0, 1, 1); + tessellator.addVertexWithUV(-0.6, -0.6, 0, 0, 1); + + tessellator.draw(); + + GL11.glPopMatrix(); + + // Renders the rays + + GL11.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_SRC_ALPHA); + + this.bindTexture(texture); + for(int j=0; j<30; j++){ - - int sliceAngle = 20 + tileentity.randomiser[j]; - float scale = 0.5f; - - GlStateManager.pushMatrix(); - - GlStateManager.rotate(31*tileentity.randomiser[j], 1, 0, 0); - GlStateManager.rotate(31*tileentity.randomiser2[j], 0, 0, 1); - - /* - * OK, so here are the changes to rendering as far as I know: - * Vertex formats specify how the methods are arranged - * Color has to be called for every vertex, I think. - * The new methods thing is a bit weird, because other than the number of arguments there is - * essentially no difference between pos, tex, color, normal and lightmap. At least they make - * the code more readable. - */ - buffer.begin(5, DefaultVertexFormats.POSITION_TEX_COLOR); - - buffer.pos(0, 0, 0).tex(0, 0).color(255, 255, 255, 0).endVertex(); - buffer.pos(0, 0, 0).tex(0, 1).color(255, 255, 255, 0).endVertex(); - - double x1 = scale*Math.sin((tileentity.timer + 40*j)*(Math.PI/180)); - //double y1 = 0.7*Math.cos((timerentity.timer - 40*j)*(Math.PI/180))*j/10; - double z1 = scale*Math.cos((tileentity.timer + 40*j)*(Math.PI/180)); - - double x2 = scale*Math.sin((tileentity.timer + 40*j - sliceAngle)*(Math.PI/180)); - //double y2 = 0.7*Math.sin((timerentity.timer - 40*j)*(Math.PI/180))*j/10; - double z2 = scale*Math.cos((tileentity.timer + 40*j - sliceAngle)*(Math.PI/180)); - - buffer.pos(x1, 0, z1).tex(1, 0).color(0, 0, 0, 255).endVertex(); - buffer.pos(x2, 0, z2).tex(1, 1).color(0, 0, 0, 255).endVertex(); - - tessellator.draw(); - - GlStateManager.popMatrix(); + int sliceAngle = 20 + timerentity.randomiser[j]; + float scale = 0.5f; + + GL11.glPushMatrix(); + + GL11.glRotatef(31*timerentity.randomiser[j], 1, 0, 0); + GL11.glRotatef(31*timerentity.randomiser2[j], 0, 0, 1); + + tessellator.startDrawing(5); + + tessellator.setColorOpaque_I(16777215); + tessellator.addVertexWithUV(0, 0, 0, 0, 0); + tessellator.addVertexWithUV(0, 0, 0, 0, 1); + + double x1 = scale*Math.sin((timerentity.timer + 40*j)*(Math.PI/180)); + //double y1 = 0.7*Math.cos((timerentity.timer - 40*j)*(Math.PI/180))*j/10; + double z1 = scale*Math.cos((timerentity.timer + 40*j)*(Math.PI/180)); + + double x2 = scale*Math.sin((timerentity.timer + 40*j - sliceAngle)*(Math.PI/180)); + //double y2 = 0.7*Math.sin((timerentity.timer - 40*j)*(Math.PI/180))*j/10; + double z2 = scale*Math.cos((timerentity.timer + 40*j - sliceAngle)*(Math.PI/180)); + + tessellator.setColorRGBA(0, 0, 0, 255); + tessellator.addVertexWithUV(x1, 0, z1, 1, 0); + tessellator.addVertexWithUV(x2, 0, z2, 1, 1); + + tessellator.draw(); + + GL11.glPopMatrix(); } } - - GlStateManager.shadeModel(GL11.GL_FLAT); - GlStateManager.enableCull(); - GlStateManager.disableBlend(); - GlStateManager.enableLighting(); - RenderHelper.enableStandardItemLighting(); - - GlStateManager.popMatrix(); + + GL11.glShadeModel(GL11.GL_FLAT); + GL11.glEnable(GL11.GL_CULL_FACE); + GL11.glDisable(GL11.GL_BLEND); + GL11.glEnable(GL11.GL_LIGHTING); + RenderHelper.enableStandardItemLighting(); + + GL11.glPopMatrix(); } } diff --git a/src/main/java/electroblob/wizardry/client/renderer/RenderMagicSlime.java b/src/main/java/electroblob/wizardry/client/renderer/RenderMagicSlime.java new file mode 100644 index 00000000..a5eacc93 --- /dev/null +++ b/src/main/java/electroblob/wizardry/client/renderer/RenderMagicSlime.java @@ -0,0 +1,95 @@ +package electroblob.wizardry.client.renderer; + +import org.lwjgl.opengl.GL11; + +import cpw.mods.fml.relauncher.Side; +import cpw.mods.fml.relauncher.SideOnly; +import electroblob.wizardry.entity.EntityMagicSlime; +import net.minecraft.client.model.ModelBase; +import net.minecraft.client.renderer.entity.RenderLiving; +import net.minecraft.entity.Entity; +import net.minecraft.entity.EntityLivingBase; +import net.minecraft.util.ResourceLocation; + +@SideOnly(Side.CLIENT) +public class RenderMagicSlime extends RenderLiving +{ + private static final ResourceLocation slimeTextures = new ResourceLocation("textures/entity/slime/slime.png"); + private ModelBase scaleAmount; + + public RenderMagicSlime(ModelBase par1ModelBase, ModelBase par2ModelBase, float par3) + { + super(par1ModelBase, par3); + this.scaleAmount = par2ModelBase; + } + + /** + * Determines whether Slime Render should pass or not. + */ + protected int shouldSlimeRenderPass(EntityMagicSlime par1EntityMagicSlime, int par2, float par3) + { + if (par1EntityMagicSlime.isInvisible()) + { + return 0; + } + else if (par2 == 0) + { + this.setRenderPassModel(this.scaleAmount); + GL11.glEnable(GL11.GL_NORMALIZE); + GL11.glEnable(GL11.GL_BLEND); + GL11.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA); + return 1; + } + else + { + if (par2 == 1) + { + GL11.glDisable(GL11.GL_BLEND); + GL11.glColor4f(1.0F, 1.0F, 1.0F, 1.0F); + } + + return -1; + } + } + + /** + * sets the scale for the slime based on getSlimeSize in EntityMagicSlime + */ + protected void scaleSlime(EntityMagicSlime par1EntityMagicSlime, float par2) + { + float f1 = (float)par1EntityMagicSlime.getSlimeSize(); + float f2 = (par1EntityMagicSlime.prevSquishFactor + (par1EntityMagicSlime.squishFactor - par1EntityMagicSlime.prevSquishFactor) * par2) / (f1 * 0.5F + 1.0F); + float f3 = 1.0F / (f2 + 1.0F); + GL11.glScalef(f3 * f1, 1.0F / f3 * f1, f3 * f1); + } + + protected ResourceLocation getSlimeTextures(EntityMagicSlime par1EntityMagicSlime) + { + return slimeTextures; + } + + /** + * Allows the render to do any OpenGL state modifications necessary before the model is rendered. Args: + * entityLiving, partialTickTime + */ + protected void preRenderCallback(EntityLivingBase par1EntityLivingBase, float par2) + { + this.scaleSlime((EntityMagicSlime)par1EntityLivingBase, par2); + } + + /** + * Queries whether should render the specified pass or not. + */ + protected int shouldRenderPass(EntityLivingBase par1EntityLivingBase, int par2, float par3) + { + return this.shouldSlimeRenderPass((EntityMagicSlime)par1EntityLivingBase, par2, par3); + } + + /** + * Returns the location of an entity's texture. Doesn't seem to be called unless you call Render.bindEntityTexture. + */ + protected ResourceLocation getEntityTexture(Entity par1Entity) + { + return this.getSlimeTextures((EntityMagicSlime)par1Entity); + } +} diff --git a/src/main/java/electroblob/wizardry/client/renderer/RenderMeteor.java b/src/main/java/electroblob/wizardry/client/renderer/RenderMeteor.java new file mode 100644 index 00000000..92a578c9 --- /dev/null +++ b/src/main/java/electroblob/wizardry/client/renderer/RenderMeteor.java @@ -0,0 +1,75 @@ +package electroblob.wizardry.client.renderer; + +import org.lwjgl.opengl.GL11; + +import cpw.mods.fml.relauncher.Side; +import cpw.mods.fml.relauncher.SideOnly; +import electroblob.wizardry.Wizardry; +import electroblob.wizardry.entity.EntityMeteor; +import net.minecraft.block.Block; +import net.minecraft.client.renderer.OpenGlHelper; +import net.minecraft.client.renderer.RenderBlocks; +import net.minecraft.client.renderer.entity.Render; +import net.minecraft.client.renderer.texture.TextureMap; +import net.minecraft.entity.Entity; +import net.minecraft.util.MathHelper; +import net.minecraft.util.ResourceLocation; +import net.minecraft.world.World; + +@SideOnly(Side.CLIENT) +public class RenderMeteor extends Render +{ + private final RenderBlocks sandRenderBlocks = new RenderBlocks(); + + public RenderMeteor() + { + this.shadowSize = 0.5F; + } + + /** + * The actual render method that is used in doRender + */ + public void doRenderFallingSand(EntityMeteor par1EntityFallingSand, double par2, double par4, double par6, float par8, float par9) + { + World world = par1EntityFallingSand.getWorld(); + Block block = Wizardry.meteor; + GL11.glPushMatrix(); + GL11.glTranslatef((float)par2, (float)par4 + 0.5f, (float)par6); + this.bindEntityTexture(par1EntityFallingSand); + GL11.glDisable(GL11.GL_LIGHTING); + OpenGlHelper.setLightmapTextureCoords(OpenGlHelper.lightmapTexUnit, 240f, 240f); + + par1EntityFallingSand.width = 0.98f; + par1EntityFallingSand.height = 0.98f; + + this.sandRenderBlocks.setRenderBoundsFromBlock(block); + this.sandRenderBlocks.renderBlockSandFalling(block, world, MathHelper.floor_double(par1EntityFallingSand.posX), MathHelper.floor_double(par1EntityFallingSand.posY), MathHelper.floor_double(par1EntityFallingSand.posZ), 0); + + GL11.glEnable(GL11.GL_LIGHTING); + GL11.glPopMatrix(); + } + + protected ResourceLocation getFallingSandTextures(EntityMeteor par1Entity) + { + return TextureMap.locationBlocksTexture; + } + + /** + * Returns the location of an entity's texture. Doesn't seem to be called unless you call Render.bindEntityTexture. + */ + protected ResourceLocation getEntityTexture(Entity par1Entity) + { + return this.getFallingSandTextures((EntityMeteor)par1Entity); + } + + /** + * Actually renders the given argument. This is a synthetic bridge method, always casting down its argument and then + * handing it off to a worker function which does the actual work. In all probabilty, the class Render is generic + * (Render { +public class RenderPhoenix extends RenderLiving +{ + private static final ResourceLocation texture = new ResourceLocation("wizardry:textures/entity/phoenix.png"); - private static final ResourceLocation texture = new ResourceLocation(Wizardry.MODID, "textures/entity/phoenix.png"); + /** + * not actually sure this is size, is not used as of now, but the model would be recreated if the value changed and + * it seems a good match for a bat's size + */ + private int renderedPhoenixSize; - public RenderPhoenix(RenderManager renderManager){ - super(renderManager, new ModelPhoenix(), 1.0f); + public RenderPhoenix() + { + super(new ModelPhoenix(), 1.0f); + //this.renderedPhoenixSize = ((ModelPhoenix)this.mainModel).getBatSize(); } - @Override - protected ResourceLocation getEntityTexture(EntityPhoenix entity){ + public void func_82443_a(EntityPhoenix par1EntityPhoenix, double par2, double par4, double par6, float par8, float par9) + { + /* + int i = ((ModelPhoenix)this.mainModel).getBatSize(); + + if (i != this.renderedPhoenixSize) + { + this.renderedPhoenixSize = i; + this.mainModel = new ModelPhoenix(); + } + */ + super.doRender(par1EntityPhoenix, par2, par4, par6, par8, par9); + + } + + protected ResourceLocation getPhoenixTextures(EntityPhoenix par1EntityPhoenix) + { return texture; } - @Override - protected void rotateCorpse(EntityPhoenix par1EntityPhoenix, float par2, float par3, float par4){ - GlStateManager.translate(0.0F, -0.1F, 0.0F); + protected void func_82442_a(EntityPhoenix par1EntityPhoenix, float par2) + { + GL11.glScalef(1.0F, 1.0F, 1.0F); + } + + protected void func_82445_a(EntityPhoenix par1EntityPhoenix, double par2, double par4, double par6) + { + super.renderLivingAt(par1EntityPhoenix, par2, par4, par6); + } + + protected void func_82444_a(EntityPhoenix par1EntityPhoenix, float par2, float par3, float par4) + { + GL11.glTranslatef(0.0F, -0.1F, 0.0F); super.rotateCorpse(par1EntityPhoenix, par2, par3, par4); } - @Override - public void doRender(EntityPhoenix phoenix, double par2, double par4, double par6, float par8, float par9){ + public void doRenderLiving(EntityLiving par1EntityLiving, double par2, double par4, double par6, float par8, float par9) + { + this.func_82443_a((EntityPhoenix)par1EntityLiving, par2, par4, par6, par8, par9); + } + + /** + * Allows the render to do any OpenGL state modifications necessary before the model is rendered. Args: + * entityLiving, partialTickTime + */ + protected void preRenderCallback(EntityLivingBase par1EntityLivingBase, float par2) + { + this.func_82442_a((EntityPhoenix)par1EntityLivingBase, par2); + } + + protected void rotateCorpse(EntityLivingBase par1EntityLivingBase, float par2, float par3, float par4) + { + this.func_82444_a((EntityPhoenix)par1EntityLivingBase, par2, par3, par4); + } + + /** + * Sets a simple glTranslate on a LivingEntity. + */ + protected void renderLivingAt(EntityLivingBase par1EntityLivingBase, double par2, double par4, double par6) + { + this.func_82445_a((EntityPhoenix)par1EntityLivingBase, par2, par4, par6); + } + + public void doRenderLiving(EntityLivingBase par1EntityLivingBase, double par2, double par4, double par6, float par8, float par9) + { + this.func_82443_a((EntityPhoenix)par1EntityLivingBase, par2, par4, par6, par8, par9); + } + + /** + * Returns the location of an entity's texture. Doesn't seem to be called unless you call Render.bindEntityTexture. + */ + protected ResourceLocation getEntityTexture(Entity par1Entity) + { + return this.getPhoenixTextures((EntityPhoenix)par1Entity); + } + + /** + * Actually renders the given argument. This is a synthetic bridge method, always casting down its argument and then + * handing it off to a worker function which does the actual work. In all probabilty, the class Render is generic + * (Render { - +public class RenderProjectile extends Render +{ private float scale; private boolean blend = false; private final ResourceLocation texture; - public RenderProjectile(RenderManager renderManager, float scale, ResourceLocation texture, boolean doBlending) + public RenderProjectile(float scale, ResourceLocation texture, boolean doBlending) { - super(renderManager); this.scale = scale; this.texture = texture; this.blend = doBlending; } - @Override - public void doRender(EntityMagicProjectile entity, double par2, double par4, double par6, float par8, float par9){ - - GlStateManager.pushMatrix(); + public void doRender(Entity par1Entity, double par2, double par4, double par6, float par8, float par9) + { + GL11.glPushMatrix(); this.bindTexture(texture); - GlStateManager.translate((float)par2, (float)par4, (float)par6); - GlStateManager.enableRescaleNormal(); + GL11.glTranslatef((float)par2, (float)par4, (float)par6); + GL11.glEnable(GL12.GL_RESCALE_NORMAL); if(blend){ - GlStateManager.enableBlend(); - GlStateManager.blendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA); + GL11.glEnable(GL11.GL_BLEND); + GL11.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA); } - GlStateManager.disableLighting(); + GL11.glDisable(GL11.GL_LIGHTING); OpenGlHelper.setLightmapTextureCoords(OpenGlHelper.lightmapTexUnit, 240f, 240f); float f2 = this.scale; - GlStateManager.scale(f2 / 1.0F, f2 / 1.0F, f2 / 1.0F); + GL11.glScalef(f2 / 1.0F, f2 / 1.0F, f2 / 1.0F); - Tessellator tessellator = Tessellator.getInstance(); - VertexBuffer buffer = tessellator.getBuffer(); + Tessellator tessellator = Tessellator.instance; float f3 = 0.0f; float f4 = 1.0f; @@ -62,27 +56,35 @@ public class RenderProjectile extends Render { // This counteracts the reverse rotation behaviour when in front f5 view. // Fun fact: this is a bug with vanilla too! Look at a snowball in front f5 view, for example. float yaw = Minecraft.getMinecraft().gameSettings.thirdPersonView == 2 ? this.renderManager.playerViewX : -this.renderManager.playerViewX; - GlStateManager.rotate(180.0F - this.renderManager.playerViewY, 0.0F, 1.0F, 0.0F); - GlStateManager.rotate(yaw, 1.0F, 0.0F, 0.0F); + GL11.glRotatef(180.0F - this.renderManager.playerViewY, 0.0F, 1.0F, 0.0F); + GL11.glRotatef(yaw, 1.0F, 0.0F, 0.0F); - buffer.begin(GL11.GL_QUADS, DefaultVertexFormats.POSITION_TEX); - //buffer.normal(0.0F, 1.0F, 0.0F); - buffer.pos((double)(0.0F - f8), (double)(0.0F - f9), 0.0D).tex((double)f3, (double)f6).endVertex(); - buffer.pos((double)(f7 - f8), (double)(0.0F - f9), 0.0D).tex((double)f4, (double)f6).endVertex(); - buffer.pos((double)(f7 - f8), (double)(1.0F - f9), 0.0D).tex((double)f4, (double)f5).endVertex(); - buffer.pos((double)(0.0F - f8), (double)(1.0F - f9), 0.0D).tex((double)f3, (double)f5).endVertex(); + tessellator.startDrawingQuads(); + tessellator.setNormal(0.0F, 1.0F, 0.0F); + tessellator.addVertexWithUV((double)(0.0F - f8), (double)(0.0F - f9), 0.0D, (double)f3, (double)f6); + tessellator.addVertexWithUV((double)(f7 - f8), (double)(0.0F - f9), 0.0D, (double)f4, (double)f6); + tessellator.addVertexWithUV((double)(f7 - f8), (double)(1.0F - f9), 0.0D, (double)f4, (double)f5); + tessellator.addVertexWithUV((double)(0.0F - f8), (double)(1.0F - f9), 0.0D, (double)f3, (double)f5); tessellator.draw(); - GlStateManager.disableRescaleNormal(); + GL11.glDisable(GL12.GL_RESCALE_NORMAL); if(blend){ - GlStateManager.disableBlend(); + GL11.glDisable(GL11.GL_BLEND); } - GlStateManager.enableLighting(); - GlStateManager.popMatrix(); + GL11.glEnable(GL11.GL_LIGHTING); + GL11.glPopMatrix(); } - - @Override - protected ResourceLocation getEntityTexture(EntityMagicProjectile par1Entity){ + + protected ResourceLocation getTextures(Entity par1Entity) + { return texture; } + + /** + * Returns the location of an entity's texture. Doesn't seem to be called unless you call Render.bindEntityTexture. + */ + protected ResourceLocation getEntityTexture(Entity par1Entity) + { + return this.getTextures((Entity)par1Entity); + } } diff --git a/src/main/java/electroblob/wizardry/client/renderer/RenderShield.java b/src/main/java/electroblob/wizardry/client/renderer/RenderShield.java new file mode 100644 index 00000000..819319ae --- /dev/null +++ b/src/main/java/electroblob/wizardry/client/renderer/RenderShield.java @@ -0,0 +1,163 @@ +package electroblob.wizardry.client.renderer; + +import org.lwjgl.opengl.GL11; + +import electroblob.wizardry.entity.EntityShield; +import net.minecraft.client.Minecraft; +import net.minecraft.client.renderer.OpenGlHelper; +import net.minecraft.client.renderer.Tessellator; +import net.minecraft.client.renderer.entity.Render; +import net.minecraft.entity.DataWatcher; +import net.minecraft.entity.Entity; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.util.ResourceLocation; + +public class RenderShield extends Render{ + + private static final ResourceLocation texture = new ResourceLocation("wizardry:textures/entity/shield.png"); + private static final ResourceLocation RES_ITEM_GLINT = new ResourceLocation("textures/misc/enchanted_item_glint.png"); + + @Override + public void doRender(Entity entity, double x, double y, double z, + float fa, float fb) { + if(entity instanceof EntityShield){ + + EntityShield shield = (EntityShield)entity; + + GL11.glPushMatrix(); + + GL11.glDisable(GL11.GL_CULL_FACE); + GL11.glEnable(GL11.GL_BLEND); + GL11.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_SRC_ALPHA); + GL11.glShadeModel(GL11.GL_SMOOTH); + //GL11.glDisable(GL11.GL_LIGHTING); + if(Minecraft.getMinecraft().renderViewEntity != shield.player.get() || Minecraft.getMinecraft().gameSettings.thirdPersonView != 0){ + GL11.glDisable(GL11.GL_LIGHTING); + } + OpenGlHelper.setLightmapTextureCoords(OpenGlHelper.lightmapTexUnit, 240f, 240f); + //RenderHelper.disableStandardItemLighting(); + + GL11.glTranslated(x, y + 0.3, z); + GL11.glRotatef(-1*shield.rotationYaw, 0, 1, 0); + GL11.glRotatef(shield.rotationPitch, 1, 0, 0); + + Tessellator tessellator = Tessellator.instance; + + this.bindTexture(texture); + + this.renderShield(tessellator, -1); + + // Enchantment effect + + GL11.glPushMatrix(); + + GL11.glDisable(GL11.GL_LIGHTING); + this.bindTexture(RES_ITEM_GLINT); + GL11.glBlendFunc(GL11.GL_SRC_COLOR, GL11.GL_ONE); + float f7 = 0.76F; + GL11.glColor4f(0.5F * f7, 0.25F * f7, 0.8F * f7, 1.0F); + float f8 = 0.125F; + //GL11.glScalef(f8, f8, f8); + float f9 = (float)(Minecraft.getSystemTime() % 3000L) / 3000.0F * 0.8f; + //GL11.glTranslatef(f9, 0.0F, 0.0F); + //GL11.glRotatef(-50.0F, 0.0F, 0.0F, 1.0F); + + //this.renderShield(tessellator, f9); + + GL11.glPopMatrix(); + + GL11.glEnable(GL11.GL_LIGHTING); + + GL11.glShadeModel(GL11.GL_FLAT); + GL11.glEnable(GL11.GL_CULL_FACE); + GL11.glDisable(GL11.GL_BLEND); + //RenderHelper.enableStandardItemLighting(); + + GL11.glPopMatrix(); + } + } + + @Override + protected ResourceLocation getEntityTexture(Entity entity) { + return texture; + } + + private void renderShield(Tessellator tessellator, float textureOffset){ + + double widthOuter = 0.6d; + double heightOuter = 0.7d; + double widthInner = 0.3d; + double heightInner = 0.4d; + double depth = 0.2d; + + double textureSection = 1.0d; + double textureU = 0.0d; + + if(textureOffset != -1){ + textureSection = 0.2d; + textureU = textureOffset; + } + + tessellator.startDrawing(5); + + tessellator.setColorRGBA(0, 0, 0, 255); + tessellator.addVertexWithUV(-widthOuter, heightOuter - 0.3, -depth, 0, 0); + tessellator.setColorOpaque(200, 200, 255); + tessellator.addVertexWithUV(-widthInner, heightInner, 0, textureU, 0.2); + tessellator.setColorRGBA(0, 0, 0, 255); + tessellator.addVertexWithUV(-widthOuter + 0.3, heightOuter, -depth, 0, 0); + tessellator.setColorOpaque(200, 200, 255); + tessellator.addVertexWithUV(-widthInner, heightInner, 0, textureU, 0.2); + + tessellator.setColorRGBA(0, 0, 0, 255); + tessellator.addVertexWithUV(widthOuter - 0.3, heightOuter, -depth, 1, 0); + tessellator.setColorOpaque(200, 200, 255); + tessellator.addVertexWithUV(widthInner, heightInner, 0, textureU + textureSection, 0.2); + tessellator.setColorRGBA(0, 0, 0, 255); + tessellator.addVertexWithUV(widthOuter, heightOuter - 0.3, -depth, 1, 0); + tessellator.setColorOpaque(200, 200, 255); + tessellator.addVertexWithUV(widthInner, heightInner, 0, textureU + textureSection, 0.2); + + tessellator.setColorRGBA(0, 0, 0, 255); + tessellator.addVertexWithUV(widthOuter, -heightOuter + 0.3, -depth, 1, 1); + tessellator.setColorOpaque(200, 200, 255); + tessellator.addVertexWithUV(widthInner, -heightInner, 0, textureU + textureSection, 0.8); + tessellator.setColorRGBA(0, 0, 0, 255); + tessellator.addVertexWithUV(widthOuter - 0.3, -heightOuter, -depth, 1, 1); + tessellator.setColorOpaque(200, 200, 255); + tessellator.addVertexWithUV(widthInner, -heightInner, 0, textureU + textureSection, 0.8); + + tessellator.setColorRGBA(0, 0, 0, 255); + tessellator.addVertexWithUV(-widthOuter + 0.3, -heightOuter, -depth, 0, 1); + tessellator.setColorOpaque(200, 200, 255); + tessellator.addVertexWithUV(-widthInner, -heightInner, 0, textureU, 0.8); + tessellator.setColorRGBA(0, 0, 0, 255); + tessellator.addVertexWithUV(-widthOuter, -heightOuter + 0.3, -depth, 0, 1); + tessellator.setColorOpaque(200, 200, 255); + tessellator.addVertexWithUV(-widthInner, -heightInner, 0, textureU, 0.8); + + tessellator.setColorRGBA(0, 0, 0, 255); + tessellator.addVertexWithUV(-widthOuter, heightOuter - 0.3, -depth, 0, 0); + tessellator.setColorOpaque(200, 200, 255); + tessellator.addVertexWithUV(-widthInner, heightInner, 0, textureU, 0.2); + + tessellator.draw(); + + tessellator.startDrawing(5); + + tessellator.setColorOpaque(200, 200, 255); + tessellator.addVertexWithUV(-widthInner, heightInner, 0, textureU, 0.2); + + tessellator.setColorOpaque(200, 200, 255); + tessellator.addVertexWithUV(widthInner, heightInner, 0, textureU + textureSection, 0.2); + + tessellator.setColorOpaque(200, 200, 255); + tessellator.addVertexWithUV(-widthInner, -heightInner, 0, textureU, 0.8); + + tessellator.setColorOpaque(200, 200, 255); + tessellator.addVertexWithUV(widthInner, -heightInner, 0, textureU + textureSection, 0.8); + + tessellator.draw(); + } + +} diff --git a/src/main/java/electroblob/wizardry/client/renderer/RenderSigil.java b/src/main/java/electroblob/wizardry/client/renderer/RenderSigil.java index bb33b60d..01490d8a 100644 --- a/src/main/java/electroblob/wizardry/client/renderer/RenderSigil.java +++ b/src/main/java/electroblob/wizardry/client/renderer/RenderSigil.java @@ -1,88 +1,90 @@ package electroblob.wizardry.client.renderer; import org.lwjgl.opengl.GL11; +import org.lwjgl.opengl.GL12; +import electroblob.wizardry.WizardryUtilities; import electroblob.wizardry.entity.construct.EntityHealAura; import electroblob.wizardry.entity.construct.EntityMagicConstruct; -import electroblob.wizardry.util.WizardryUtilities; import net.minecraft.client.Minecraft; -import net.minecraft.client.renderer.GlStateManager; import net.minecraft.client.renderer.OpenGlHelper; import net.minecraft.client.renderer.Tessellator; -import net.minecraft.client.renderer.VertexBuffer; import net.minecraft.client.renderer.entity.Render; -import net.minecraft.client.renderer.entity.RenderManager; -import net.minecraft.client.renderer.vertex.DefaultVertexFormats; +import net.minecraft.entity.Entity; import net.minecraft.entity.player.EntityPlayer; import net.minecraft.util.ResourceLocation; -public class RenderSigil extends Render { +public class RenderSigil extends Render { private final ResourceLocation texture; private float scale = 1.0f; private boolean invisibleToEnemies; - public RenderSigil(RenderManager renderManager, ResourceLocation texture, float scale, boolean invisibleToEnemies) { - super(renderManager); + public RenderSigil(ResourceLocation texture, float scale, boolean invisibleToEnemies) { this.texture = texture; this.scale = scale; this.invisibleToEnemies = invisibleToEnemies; } @Override - public void doRender(EntityMagicConstruct entity, double par2, double par4, double par6, float par8, float par9){ + public void doRender(Entity entity, double par2, double par4, double par6, float par8, float par9){ // Makes the sigil invisible to enemies of the player that created it - if(this.invisibleToEnemies){ + if(this.invisibleToEnemies && entity instanceof EntityMagicConstruct){ - if(entity.getCaster() instanceof EntityPlayer - && !WizardryUtilities.isPlayerAlly((EntityPlayer)entity.getCaster(), Minecraft.getMinecraft().thePlayer)){ + EntityMagicConstruct construct = (EntityMagicConstruct)entity; + + if(construct.getCaster() instanceof EntityPlayer + && !WizardryUtilities.isPlayerAlly((EntityPlayer)construct.getCaster(), Minecraft.getMinecraft().thePlayer)){ return; } } - GlStateManager.pushMatrix(); - GlStateManager.enableBlend(); - GlStateManager.disableLighting(); + GL11.glPushMatrix(); + GL11.glEnable(GL11.GL_BLEND); + GL11.glDisable(GL11.GL_LIGHTING); OpenGlHelper.setLightmapTextureCoords(OpenGlHelper.lightmapTexUnit, 240, 240); - GlStateManager.blendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA); + GL11.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA); float yOffset = 0; - GlStateManager.translate((float)par2, (float)par4 + yOffset, (float)par6); + if(entity.riddenByEntity != null){ + yOffset = entity.riddenByEntity.height/2; + } + + GL11.glTranslatef((float)par2, (float)par4 + yOffset, (float)par6); this.bindTexture(texture); float f6 = 1.0F; float f7 = 0.5F; float f8 = 0.5F; - GlStateManager.rotate(-90, 1, 0, 0); + GL11.glRotatef(-90, 1, 0, 0); // Healing aura rotates slowly - if(entity instanceof EntityHealAura) GlStateManager.rotate(entity.ticksExisted/3.0f, 0, 0, 1); + if(entity instanceof EntityHealAura) GL11.glRotatef(entity.ticksExisted/3.0f, 0, 0, 1); - GlStateManager.scale(scale, scale, scale); + GL11.glScalef(scale, scale, scale); - Tessellator tessellator = Tessellator.getInstance(); - VertexBuffer buffer = tessellator.getBuffer(); - buffer.begin(GL11.GL_QUADS, DefaultVertexFormats.POSITION_TEX); + Tessellator tessellator = Tessellator.instance; + tessellator.startDrawingQuads(); //tessellator.setColorRGBA_I(k1, 128); - //buffer.normal(0.0F, 1.0F, 0.0F); - buffer.pos((double)(0.0F - f7), (double)(0.0F - f8), 0.01).tex(0, 1).endVertex(); - buffer.pos((double)(f6 - f7), (double)(0.0F - f8), 0.01).tex(1, 1).endVertex(); - buffer.pos((double)(f6 - f7), (double)(1.0F - f8), 0.01).tex(1, 0).endVertex(); - buffer.pos((double)(0.0F - f7), (double)(1.0F - f8), 0.01).tex(0, 0).endVertex(); + tessellator.setNormal(0.0F, 1.0F, 0.0F); + tessellator.addVertexWithUV((double)(0.0F - f7), (double)(0.0F - f8), 0.01, 0, 1); + tessellator.addVertexWithUV((double)(f6 - f7), (double)(0.0F - f8), 0.01, 1, 1); + tessellator.addVertexWithUV((double)(f6 - f7), (double)(1.0F - f8), 0.01, 1, 0); + tessellator.addVertexWithUV((double)(0.0F - f7), (double)(1.0F - f8), 0.01, 0, 0); tessellator.draw(); - GlStateManager.disableBlend(); - GlStateManager.enableLighting(); - GlStateManager.disableRescaleNormal(); - GlStateManager.popMatrix(); + GL11.glDisable(GL11.GL_BLEND); + GL11.glEnable(GL11.GL_LIGHTING); + GL11.glDisable(GL12.GL_RESCALE_NORMAL); + GL11.glPopMatrix(); } @Override - protected ResourceLocation getEntityTexture(EntityMagicConstruct entity) { + protected ResourceLocation getEntityTexture(Entity entity) { return null; } diff --git a/src/main/java/electroblob/wizardry/client/renderer/RenderSilverfishMinion.java b/src/main/java/electroblob/wizardry/client/renderer/RenderSilverfishMinion.java new file mode 100644 index 00000000..340e6fd0 --- /dev/null +++ b/src/main/java/electroblob/wizardry/client/renderer/RenderSilverfishMinion.java @@ -0,0 +1,109 @@ +package electroblob.wizardry.client.renderer; + +import cpw.mods.fml.relauncher.Side; +import cpw.mods.fml.relauncher.SideOnly; +import electroblob.wizardry.entity.living.EntitySilverfishMinion; +import net.minecraft.client.model.ModelSilverfish; +import net.minecraft.client.renderer.entity.RenderLiving; +import net.minecraft.entity.Entity; +import net.minecraft.entity.EntityLiving; +import net.minecraft.entity.EntityLivingBase; +import net.minecraft.util.ResourceLocation; + +@SideOnly(Side.CLIENT) +public class RenderSilverfishMinion extends RenderLiving +{ + private static final ResourceLocation silverfishTextures = new ResourceLocation("textures/entity/silverfish.png"); + private static final String __OBFID = "CL_00001022"; + + public RenderSilverfishMinion() + { + super(new ModelSilverfish(), 0.3F); + } + + protected float getDeathMaxRotation(EntitySilverfishMinion p_77037_1_) + { + return 180.0F; + } + + /** + * Actually renders the given argument. This is a synthetic bridge method, always casting down its argument and then + * handing it off to a worker function which does the actual work. In all probabilty, the class Render is generic + * (Render 1.0F) + { + f7 = 1.0F; + } + + GL11.glEnable(GL11.GL_ALPHA_TEST); + //GL11.glEnable(GL11.GL_BLEND); + //GL11.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA); + this.mainModel.setLivingAnimations(par1EntityLivingBase, f8, f7, par9); + this.renderModel(par1EntityLivingBase, f8, f7, f4, f3 - f2, f5, f6); + float f9; + int i; + float f10; + float f11; + + /* + for (int j = 0; j < 4; ++j) + { + i = this.shouldRenderPass(par1EntityLivingBase, j, par9); + + if (i > 0) + { + this.renderPassModel.setLivingAnimations(par1EntityLivingBase, f8, f7, par9); + this.renderPassModel.render(par1EntityLivingBase, f8, f7, f4, f3 - f2, f5, f6); + + if ((i & 240) == 16) + { + this.func_82408_c(par1EntityLivingBase, j, par9); + this.renderPassModel.render(par1EntityLivingBase, f8, f7, f4, f3 - f2, f5, f6); + } + + if ((i & 15) == 15) + { + f9 = (float)par1EntityLivingBase.ticksExisted + par9; + //this.bindTexture(RES_ITEM_GLINT); + GL11.glEnable(GL11.GL_BLEND); + f10 = 0.5F; + GL11.glColor4f(f10, f10, f10, 1.0F); + GL11.glDepthFunc(GL11.GL_EQUAL); + GL11.glDepthMask(false); + + for (int k = 0; k < 2; ++k) + { + GL11.glDisable(GL11.GL_LIGHTING); + f11 = 0.76F; + GL11.glColor4f(0.5F * f11, 0.25F * f11, 0.8F * f11, 1.0F); + GL11.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA); + //GL11.glBlendFunc(GL11.GL_SRC_COLOR, GL11.GL_ONE); + GL11.glMatrixMode(GL11.GL_TEXTURE); + GL11.glLoadIdentity(); + float f12 = f9 * (0.001F + (float)k * 0.003F) * 20.0F; + float f13 = 0.33333334F; + GL11.glScalef(f13, f13, f13); + GL11.glRotatef(30.0F - (float)k * 60.0F, 0.0F, 0.0F, 1.0F); + GL11.glTranslatef(0.0F, f12, 0.0F); + GL11.glMatrixMode(GL11.GL_MODELVIEW); + this.renderPassModel.render(par1EntityLivingBase, f8, f7, f4, f3 - f2, f5, f6); + } + + GL11.glColor4f(1.0F, 1.0F, 1.0F, 1.0F); + GL11.glMatrixMode(GL11.GL_TEXTURE); + GL11.glDepthMask(true); + GL11.glLoadIdentity(); + GL11.glMatrixMode(GL11.GL_MODELVIEW); + GL11.glEnable(GL11.GL_LIGHTING); + //GL11.glDisable(GL11.GL_BLEND); + GL11.glDepthFunc(GL11.GL_LEQUAL); + } + + GL11.glDisable(GL11.GL_BLEND); + GL11.glEnable(GL11.GL_ALPHA_TEST); + } + } +*/ + GL11.glDisable(GL11.GL_BLEND); + + GL11.glDepthMask(true); + //this.renderEquippedItems(par1EntityLivingBase, par9); + float f14 = par1EntityLivingBase.getBrightness(par9); + i = this.getColorMultiplier(par1EntityLivingBase, f14, par9); + OpenGlHelper.setActiveTexture(OpenGlHelper.lightmapTexUnit); + GL11.glDisable(GL11.GL_TEXTURE_2D); + OpenGlHelper.setActiveTexture(OpenGlHelper.defaultTexUnit); + + // The red hurt animation. + if ((i >> 24 & 255) > 0 || par1EntityLivingBase.hurtTime > 0 || par1EntityLivingBase.deathTime > 0) + { + GL11.glDisable(GL11.GL_TEXTURE_2D); + GL11.glDisable(GL11.GL_ALPHA_TEST); + GL11.glEnable(GL11.GL_BLEND); + GL11.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA); + GL11.glDepthFunc(GL11.GL_EQUAL); + + if (par1EntityLivingBase.hurtTime > 0 || par1EntityLivingBase.deathTime > 0) + { + GL11.glColor4f(f14, 0.0F, 0.0F, 0.4F); + this.mainModel.render(par1EntityLivingBase, f8, f7, f4, f3 - f2, f5, f6); + + for (int l = 0; l < 4; ++l) + { + if (this.inheritRenderPass(par1EntityLivingBase, l, par9) >= 0) + { + GL11.glColor4f(f14, 0.0F, 0.0F, 0.4F); + this.renderPassModel.render(par1EntityLivingBase, f8, f7, f4, f3 - f2, f5, f6); + } + } + } + + if ((i >> 24 & 255) > 0) + { + f9 = (float)(i >> 16 & 255) / 255.0F; + f10 = (float)(i >> 8 & 255) / 255.0F; + float f15 = (float)(i & 255) / 255.0F; + f11 = (float)(i >> 24 & 255) / 255.0F; + GL11.glColor4f(f9, f10, f15, f11); + this.mainModel.render(par1EntityLivingBase, f8, f7, f4, f3 - f2, f5, f6); + + for (int i1 = 0; i1 < 4; ++i1) + { + if (this.inheritRenderPass(par1EntityLivingBase, i1, par9) >= 0) + { + GL11.glColor4f(f9, f10, f15, f11); + this.renderPassModel.render(par1EntityLivingBase, f8, f7, f4, f3 - f2, f5, f6); + } + } + } + + GL11.glDepthFunc(GL11.GL_LEQUAL); + GL11.glDisable(GL11.GL_BLEND); + GL11.glEnable(GL11.GL_ALPHA_TEST); + GL11.glEnable(GL11.GL_TEXTURE_2D); + } + + GL11.glDisable(GL12.GL_RESCALE_NORMAL); + } + catch (Exception exception) + { + exception.printStackTrace(); + } + + OpenGlHelper.setActiveTexture(OpenGlHelper.lightmapTexUnit); + GL11.glEnable(GL11.GL_TEXTURE_2D); + OpenGlHelper.setActiveTexture(OpenGlHelper.defaultTexUnit); + GL11.glEnable(GL11.GL_CULL_FACE); + GL11.glPopMatrix(); + this.passSpecialRender(par1EntityLivingBase, par2, par4, par6); + MinecraftForge.EVENT_BUS.post(new RenderLivingEvent.Post(par1EntityLivingBase, this, par2, par4, par6)); + } + + /** + * Returns a rotation angle that is inbetween two other rotation angles. par1 and par2 are the angles between which + * to interpolate, par3 is probably a float between 0.0 and 1.0 that tells us where "between" the two angles we are. + * Example: par1 = 30, par2 = 50, par3 = 0.5, then return = 40 + */ + private float interpolateRotation(float par1, float par2, float par3) + { + float f3; + + for (f3 = par2 - par1; f3 < -180.0F; f3 += 360.0F) + { + ; + } + + while (f3 >= 180.0F) + { + f3 -= 360.0F; + } + + return par1 + par3 * f3; + } } diff --git a/src/main/java/electroblob/wizardry/client/renderer/RenderSpiritWolf.java b/src/main/java/electroblob/wizardry/client/renderer/RenderSpiritWolf.java index e1ffe797..3af0f3d6 100644 --- a/src/main/java/electroblob/wizardry/client/renderer/RenderSpiritWolf.java +++ b/src/main/java/electroblob/wizardry/client/renderer/RenderSpiritWolf.java @@ -1,35 +1,320 @@ package electroblob.wizardry.client.renderer; -import org.lwjgl.opengl.GL11; - -import electroblob.wizardry.Wizardry; -import net.minecraft.client.model.ModelWolf; -import net.minecraft.client.renderer.GlStateManager; -import net.minecraft.client.renderer.entity.RenderManager; -import net.minecraft.client.renderer.entity.RenderWolf; -import net.minecraft.entity.passive.EntityWolf; +import cpw.mods.fml.relauncher.Side; +import cpw.mods.fml.relauncher.SideOnly; +import electroblob.wizardry.entity.living.EntitySpiritWolf; +import net.minecraft.client.model.ModelBase; +import net.minecraft.client.renderer.OpenGlHelper; +import net.minecraft.client.renderer.entity.RenderLiving; +import net.minecraft.entity.Entity; +import net.minecraft.entity.EntityLivingBase; +import net.minecraft.entity.passive.EntitySheep; +import net.minecraft.util.MathHelper; import net.minecraft.util.ResourceLocation; -import net.minecraftforge.fml.relauncher.Side; -import net.minecraftforge.fml.relauncher.SideOnly; +import net.minecraftforge.client.event.RenderLivingEvent; +import net.minecraftforge.common.MinecraftForge; + +import org.lwjgl.opengl.GL11; +import org.lwjgl.opengl.GL12; @SideOnly(Side.CLIENT) -public class RenderSpiritWolf extends RenderWolf { - - private static final ResourceLocation texture = new ResourceLocation(Wizardry.MODID, "textures/entity/spirit_wolf.png"); +public class RenderSpiritWolf extends RenderLiving +{ + private static final ResourceLocation wolfTextures = new ResourceLocation("wizardry:textures/entity/spirit_wolf.png"); - public RenderSpiritWolf(RenderManager renderManager, float par3){ - super(renderManager, new ModelWolf(), par3); + public RenderSpiritWolf(ModelBase par1ModelBase, ModelBase par2ModelBase, float par3) + { + super(par1ModelBase, par3); + this.setRenderPassModel(par2ModelBase); } - @Override - protected ResourceLocation getEntityTexture(EntityWolf entity) { - return texture; + protected float getTailRotation(EntitySpiritWolf par1EntitySpiritWolf, float par2) + { + return par1EntitySpiritWolf.getTailRotation(); } + protected int func_82447_a(EntitySpiritWolf par1EntitySpiritWolf, int par2, float par3) + { + float f1; + + if (par2 == 0) + { + //f1 = par1EntitySpiritWolf.getBrightness(par3) * par1EntitySpiritWolf.getShadingWhileShaking(par3); + this.bindTexture(wolfTextures); + this.setRenderPassModel(this.mainModel); + //GL11.glColor3f(f1, f1, f1); + //GL11.glEnable(GL11.GL_NORMALIZE); + GL11.glEnable(GL11.GL_BLEND); + GL11.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA); + return 1; + } + /* + else if (par2 == 1 && par1EntitySpiritWolf.isTamed()) + { + f1 = par1EntitySpiritWolf.getBrightness(par3) * par1EntitySpiritWolf.getShadingWhileShaking(par3); + this.bindTexture(wolfTextures); + GL11.glColor3f(f1, f1, f1); + return 1; + } + */ + else + { + return -1; + } + } + + protected ResourceLocation func_110914_a(EntitySpiritWolf par1EntitySpiritWolf) + { + return wolfTextures; + } + + /** + * Queries whether should render the specified pass or not. + */ + protected int shouldRenderPass(EntityLivingBase par1EntityLivingBase, int par2, float par3) + { + return this.func_82447_a((EntitySpiritWolf)par1EntityLivingBase, par2, par3); + } + + /** + * Allows the render to do any OpenGL state modifications necessary before the model is rendered. Args: + * entityLiving, partialTickTime + */ + protected void preRenderCallback(EntityLivingBase par1EntityLivingBase, float par2){ + GL11.glEnable(GL11.GL_BLEND); + GL11.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA); + } + + // This is overridden to allow transparent stuff to happen. @Override - protected void preRenderCallback(EntityWolf entity, float partialTickTime){ - super.preRenderCallback(entity, partialTickTime); - GlStateManager.enableBlend(); - GlStateManager.blendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA); + public void doRender(EntityLivingBase par1EntityLivingBase, double par2, double par4, double par6, float par8, float par9) + { + if (MinecraftForge.EVENT_BUS.post(new RenderLivingEvent.Pre(par1EntityLivingBase, this, par2, par4, par6))) return; + GL11.glPushMatrix(); + GL11.glDisable(GL11.GL_CULL_FACE); + this.mainModel.onGround = this.renderSwingProgress(par1EntityLivingBase, par9); + + if (this.renderPassModel != null) + { + this.renderPassModel.onGround = this.mainModel.onGround; + } + + this.mainModel.isRiding = par1EntityLivingBase.isRiding(); + + if (this.renderPassModel != null) + { + this.renderPassModel.isRiding = this.mainModel.isRiding; + } + + this.mainModel.isChild = par1EntityLivingBase.isChild(); + + if (this.renderPassModel != null) + { + this.renderPassModel.isChild = this.mainModel.isChild; + } + + try + { + float f2 = this.interpolateRotation(par1EntityLivingBase.prevRenderYawOffset, par1EntityLivingBase.renderYawOffset, par9); + float f3 = this.interpolateRotation(par1EntityLivingBase.prevRotationYawHead, par1EntityLivingBase.rotationYawHead, par9); + float f4; + + float f5 = par1EntityLivingBase.prevRotationPitch + (par1EntityLivingBase.rotationPitch - par1EntityLivingBase.prevRotationPitch) * par9; + this.renderLivingAt(par1EntityLivingBase, par2, par4, par6); + f4 = this.handleRotationFloat(par1EntityLivingBase, par9); + this.rotateCorpse(par1EntityLivingBase, f4, f2, par9); + float f6 = 0.0625F; + GL11.glEnable(GL12.GL_RESCALE_NORMAL); + GL11.glScalef(-1.0F, -1.0F, 1.0F); + this.preRenderCallback(par1EntityLivingBase, par9); + GL11.glTranslatef(0.0F, -24.0F * f6 - 0.0078125F, 0.0F); + float f7 = par1EntityLivingBase.prevLimbSwingAmount + (par1EntityLivingBase.limbSwingAmount - par1EntityLivingBase.prevLimbSwingAmount) * par9; + float f8 = par1EntityLivingBase.limbSwing - par1EntityLivingBase.limbSwingAmount * (1.0F - par9); + + if (f7 > 1.0F) + { + f7 = 1.0F; + } + + GL11.glEnable(GL11.GL_ALPHA_TEST); + //GL11.glEnable(GL11.GL_BLEND); + //GL11.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA); + this.mainModel.setLivingAnimations(par1EntityLivingBase, f8, f7, par9); + this.renderModel(par1EntityLivingBase, f8, f7, f4, f3 - f2, f5, f6); + float f9; + int i; + float f10; + float f11; + + /* + for (int j = 0; j < 4; ++j) + { + i = this.shouldRenderPass(par1EntityLivingBase, j, par9); + + if (i > 0) + { + this.renderPassModel.setLivingAnimations(par1EntityLivingBase, f8, f7, par9); + this.renderPassModel.render(par1EntityLivingBase, f8, f7, f4, f3 - f2, f5, f6); + + if ((i & 240) == 16) + { + this.func_82408_c(par1EntityLivingBase, j, par9); + this.renderPassModel.render(par1EntityLivingBase, f8, f7, f4, f3 - f2, f5, f6); + } + + if ((i & 15) == 15) + { + f9 = (float)par1EntityLivingBase.ticksExisted + par9; + //this.bindTexture(RES_ITEM_GLINT); + GL11.glEnable(GL11.GL_BLEND); + f10 = 0.5F; + GL11.glColor4f(f10, f10, f10, 1.0F); + GL11.glDepthFunc(GL11.GL_EQUAL); + GL11.glDepthMask(false); + + for (int k = 0; k < 2; ++k) + { + GL11.glDisable(GL11.GL_LIGHTING); + f11 = 0.76F; + GL11.glColor4f(0.5F * f11, 0.25F * f11, 0.8F * f11, 1.0F); + GL11.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA); + //GL11.glBlendFunc(GL11.GL_SRC_COLOR, GL11.GL_ONE); + GL11.glMatrixMode(GL11.GL_TEXTURE); + GL11.glLoadIdentity(); + float f12 = f9 * (0.001F + (float)k * 0.003F) * 20.0F; + float f13 = 0.33333334F; + GL11.glScalef(f13, f13, f13); + GL11.glRotatef(30.0F - (float)k * 60.0F, 0.0F, 0.0F, 1.0F); + GL11.glTranslatef(0.0F, f12, 0.0F); + GL11.glMatrixMode(GL11.GL_MODELVIEW); + this.renderPassModel.render(par1EntityLivingBase, f8, f7, f4, f3 - f2, f5, f6); + } + + GL11.glColor4f(1.0F, 1.0F, 1.0F, 1.0F); + GL11.glMatrixMode(GL11.GL_TEXTURE); + GL11.glDepthMask(true); + GL11.glLoadIdentity(); + GL11.glMatrixMode(GL11.GL_MODELVIEW); + GL11.glEnable(GL11.GL_LIGHTING); + //GL11.glDisable(GL11.GL_BLEND); + GL11.glDepthFunc(GL11.GL_LEQUAL); + } + + GL11.glDisable(GL11.GL_BLEND); + GL11.glEnable(GL11.GL_ALPHA_TEST); + } + } +*/ + GL11.glDisable(GL11.GL_BLEND); + + GL11.glDepthMask(true); + //this.renderEquippedItems(par1EntityLivingBase, par9); + float f14 = par1EntityLivingBase.getBrightness(par9); + i = this.getColorMultiplier(par1EntityLivingBase, f14, par9); + OpenGlHelper.setActiveTexture(OpenGlHelper.lightmapTexUnit); + GL11.glDisable(GL11.GL_TEXTURE_2D); + OpenGlHelper.setActiveTexture(OpenGlHelper.defaultTexUnit); + + // The red hurt animation. + if ((i >> 24 & 255) > 0 || par1EntityLivingBase.hurtTime > 0 || par1EntityLivingBase.deathTime > 0) + { + GL11.glDisable(GL11.GL_TEXTURE_2D); + GL11.glDisable(GL11.GL_ALPHA_TEST); + GL11.glEnable(GL11.GL_BLEND); + GL11.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA); + GL11.glDepthFunc(GL11.GL_EQUAL); + + if (par1EntityLivingBase.hurtTime > 0 || par1EntityLivingBase.deathTime > 0) + { + GL11.glColor4f(f14, 0.0F, 0.0F, 0.4F); + this.mainModel.render(par1EntityLivingBase, f8, f7, f4, f3 - f2, f5, f6); + + for (int l = 0; l < 4; ++l) + { + if (this.inheritRenderPass(par1EntityLivingBase, l, par9) >= 0) + { + GL11.glColor4f(f14, 0.0F, 0.0F, 0.4F); + this.renderPassModel.render(par1EntityLivingBase, f8, f7, f4, f3 - f2, f5, f6); + } + } + } + + if ((i >> 24 & 255) > 0) + { + f9 = (float)(i >> 16 & 255) / 255.0F; + f10 = (float)(i >> 8 & 255) / 255.0F; + float f15 = (float)(i & 255) / 255.0F; + f11 = (float)(i >> 24 & 255) / 255.0F; + GL11.glColor4f(f9, f10, f15, f11); + this.mainModel.render(par1EntityLivingBase, f8, f7, f4, f3 - f2, f5, f6); + + for (int i1 = 0; i1 < 4; ++i1) + { + if (this.inheritRenderPass(par1EntityLivingBase, i1, par9) >= 0) + { + GL11.glColor4f(f9, f10, f15, f11); + this.renderPassModel.render(par1EntityLivingBase, f8, f7, f4, f3 - f2, f5, f6); + } + } + } + + GL11.glDepthFunc(GL11.GL_LEQUAL); + GL11.glDisable(GL11.GL_BLEND); + GL11.glEnable(GL11.GL_ALPHA_TEST); + GL11.glEnable(GL11.GL_TEXTURE_2D); + } + + GL11.glDisable(GL12.GL_RESCALE_NORMAL); + } + catch (Exception exception) + { + exception.printStackTrace(); + } + + OpenGlHelper.setActiveTexture(OpenGlHelper.lightmapTexUnit); + GL11.glEnable(GL11.GL_TEXTURE_2D); + OpenGlHelper.setActiveTexture(OpenGlHelper.defaultTexUnit); + GL11.glEnable(GL11.GL_CULL_FACE); + GL11.glPopMatrix(); + this.passSpecialRender(par1EntityLivingBase, par2, par4, par6); + MinecraftForge.EVENT_BUS.post(new RenderLivingEvent.Post(par1EntityLivingBase, this, par2, par4, par6)); + } + + /** + * Returns a rotation angle that is inbetween two other rotation angles. par1 and par2 are the angles between which + * to interpolate, par3 is probably a float between 0.0 and 1.0 that tells us where "between" the two angles we are. + * Example: par1 = 30, par2 = 50, par3 = 0.5, then return = 40 + */ + private float interpolateRotation(float par1, float par2, float par3) + { + float f3; + + for (f3 = par2 - par1; f3 < -180.0F; f3 += 360.0F) + { + ; + } + + while (f3 >= 180.0F) + { + f3 -= 360.0F; + } + + return par1 + par3 * f3; + } + + /** + * Defines what float the third param in setRotationAngles of ModelBase is + */ + protected float handleRotationFloat(EntityLivingBase par1EntityLivingBase, float par2) + { + return this.getTailRotation((EntitySpiritWolf)par1EntityLivingBase, par2); + } + + /** + * Returns the location of an entity's texture. Doesn't seem to be called unless you call Render.bindEntityTexture. + */ + protected ResourceLocation getEntityTexture(Entity par1Entity) + { + return this.func_110914_a((EntitySpiritWolf)par1Entity); } } diff --git a/src/main/java/electroblob/wizardry/client/renderer/RenderStatue.java b/src/main/java/electroblob/wizardry/client/renderer/RenderStatue.java index 33301d84..948e24f6 100644 --- a/src/main/java/electroblob/wizardry/client/renderer/RenderStatue.java +++ b/src/main/java/electroblob/wizardry/client/renderer/RenderStatue.java @@ -1,64 +1,196 @@ package electroblob.wizardry.client.renderer; +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.util.HashSet; + +import org.lwjgl.opengl.GL11; +import org.lwjgl.opengl.GL12; + +import cpw.mods.fml.relauncher.ReflectionHelper; import electroblob.wizardry.tileentity.TileEntityStatue; import net.minecraft.client.Minecraft; -import net.minecraft.client.renderer.GlStateManager; +import net.minecraft.client.model.ModelBase; import net.minecraft.client.renderer.OpenGlHelper; +import net.minecraft.client.renderer.entity.Render; +import net.minecraft.client.renderer.entity.RenderManager; +import net.minecraft.client.renderer.entity.RenderZombie; +import net.minecraft.client.renderer.entity.RendererLivingEntity; import net.minecraft.client.renderer.tileentity.TileEntitySpecialRenderer; +import net.minecraft.entity.EntityLiving; +import net.minecraft.entity.EntityLivingBase; +import net.minecraft.entity.boss.EntityWither; +import net.minecraft.entity.monster.EntityIronGolem; +import net.minecraft.entity.monster.EntityPigZombie; +import net.minecraft.entity.monster.EntityWitch; +import net.minecraft.entity.monster.EntityZombie; +import net.minecraft.entity.passive.EntityHorse; +import net.minecraft.entity.passive.EntityVillager; import net.minecraft.tileentity.TileEntity; import net.minecraft.util.ResourceLocation; -public class RenderStatue extends TileEntitySpecialRenderer { +public class RenderStatue extends TileEntitySpecialRenderer { + + private static final ResourceLocation stoneTexture32 = new ResourceLocation("wizardry:textures/entity/stone_statue_32.png"); + private static final ResourceLocation stoneTexture64 = new ResourceLocation("wizardry:textures/entity/stone_statue_64.png"); + + private static final HashSet> entitiesUsing64Texture = new HashSet>(); + + // Reflective method and field accesses; defined here since anything only ever needs to be reflectively accessed once. - private int destroyStage = 0; // Gets set each time a statue is rendered to allow access from the layer renderer + // Since the second argument is never used, it might as well be null. + private static final Method selectZombieModel = ReflectionHelper.findMethod(RenderZombie.class, null, new String[]{"func_82427_a"}, EntityZombie.class); + private static final Method preRenderCallback = ReflectionHelper.findMethod(RendererLivingEntity.class, null, new String[]{"func_77041_b", "preRenderCallback"}, EntityLivingBase.class, float.class); + private static final Method shouldRenderPass = ReflectionHelper.findMethod(RendererLivingEntity.class, null, new String[]{"func_77032_a", "shouldRenderPass"}, EntityLivingBase.class, int.class, float.class); + private static final Method renderEquippedItems = ReflectionHelper.findMethod(RendererLivingEntity.class, null, new String[]{"func_77029_c", "renderEquippedItems"}, EntityLivingBase.class, float.class); + + private static final Field mainModelField = ReflectionHelper.findField(RendererLivingEntity.class, "field_77045_g", "mainModel"); + private static final Field renderPassModelField = ReflectionHelper.findField(RendererLivingEntity.class, "field_77046_h", "renderPassModel"); + + static { + entitiesUsing64Texture.add(EntityZombie.class); + entitiesUsing64Texture.add(EntityPigZombie.class); + entitiesUsing64Texture.add(EntityVillager.class); + entitiesUsing64Texture.add(EntityWitch.class); + entitiesUsing64Texture.add(EntityHorse.class); + entitiesUsing64Texture.add(EntityIronGolem.class); + entitiesUsing64Texture.add(EntityWither.class); + } @Override - public void renderTileEntityAt(TileEntityStatue statue, double x, double y, double z, float partialTicks, int destroyStage){ + public void renderTileEntityAt(TileEntity tileentity, double x, double y, + double z, float partialTickTime) { + + GL11.glPushMatrix(); + // The next line makes stuff render in the same place relative to the world wherever the player is. + GL11.glTranslatef((float)x + 0.5F, (float)y, (float)z + 0.5F); + + if(tileentity instanceof TileEntityStatue){ + + TileEntityStatue statue = (TileEntityStatue)tileentity; + + if(statue.creature != null && statue.position == 1){ + + if(statue.isIce){ + + float yaw = statue.creature.prevRotationYaw; + int i = statue.creature.getBrightnessForRender(0); + + if (statue.creature.isBurning()) + { + i = 15728880; + } + + int j = i % 65536; + int k = i / 65536; + OpenGlHelper.setLightmapTextureCoords(OpenGlHelper.lightmapTexUnit, (float)j / 1.0F, (float)k / 1.0F); + GL11.glColor4f(1.0F, 1.0F, 1.0F, 1.0F); + + GL11.glRotatef(-yaw, 0F, 1F, 0F); + + RenderManager.instance.renderEntityWithPosYaw(statue.creature, 0, 0, 0, 0, 0); + + }else{ + + GL11.glPushMatrix(); + GL11.glDisable(GL11.GL_CULL_FACE); + GL11.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA); + + float someScalingFactor = 0.0625f; + + float yaw = statue.creature.prevRotationYaw; + int brightness = statue.creature.getBrightnessForRender(0); + + int j = brightness % 65536; + int k = brightness / 65536; + OpenGlHelper.setLightmapTextureCoords(OpenGlHelper.lightmapTexUnit, (float)j / 1.0F, (float)k / 1.0F); + GL11.glColor4f(1.0F, 1.0F, 1.0F, 1.0F); + + GL11.glRotatef(-yaw + 180, 0F, 1F, 0F); + + Render render = RenderManager.instance.getEntityRenderObject(statue.creature); + + if(render instanceof RendererLivingEntity){ + + RendererLivingEntity renderliving = (RendererLivingEntity)render; + + // Reflection. This is... complete madness really, but it works! + // What it effectively does is emulate the doRender method in RendererLivingEntity, but with + // my own control over the textures. + // Edit: Switched over to the forge-supplied ReflectionHelper methods, since these make it a + // lot more concise and easy to read - since they're there, I might as well use them! + // Edit 2: In order for this to work outside the development environment, you need to supply the + // obfuscated (srg) names as well as the mcp ones, unless of course they are the same. + + try { + + // Chooses the appropriate zombie model, normal or villager + // Fixed by moving before the model fields are accessed + if(render instanceof RenderZombie && statue.creature instanceof EntityZombie){ + selectZombieModel.invoke(renderliving, (EntityZombie)statue.creature); + } + + // Turns out that java automatically infers the type parameter T in this method from the type + // I am assigning the returned value to. Neat! + ModelBase mainModel = (ModelBase) mainModelField.get(renderliving); + ModelBase renderPassModel = (ModelBase) renderPassModelField.get(renderliving); + + mainModel.isRiding = statue.creature.isRiding(); + mainModel.isChild = statue.creature.isChild(); + + if(renderPassModel != null){ + renderPassModel.isRiding = statue.creature.isRiding(); + renderPassModel.isChild = statue.creature.isChild(); + } + + if(entitiesUsing64Texture.contains(statue.creature.getClass())){ + Minecraft.getMinecraft().renderEngine.bindTexture(stoneTexture64); + }else{ + Minecraft.getMinecraft().renderEngine.bindTexture(stoneTexture32); + } + + GL11.glEnable(GL12.GL_RESCALE_NORMAL); + GL11.glScalef(-1.0F, -1.0F, 1.0F); + + preRenderCallback.invoke(renderliving, statue.creature, someScalingFactor); + + // Why is this -1.5f? No idea! + GL11.glTranslatef(0, -1.5f, 0); + + GL11.glEnable(GL11.GL_ALPHA_TEST); + + mainModel.render(statue.creature, 0, 0, 0, 0, 0, someScalingFactor); + + for(int i=0; i<4; i++){ + if((Integer)shouldRenderPass.invoke(render, statue.creature, i, someScalingFactor) > 0){ + // Should never be null here since shouldRenderPass returns -1 when it is null, but there's no harm in checking. + if(renderPassModel != null) renderPassModel.render(statue.creature, 0, 0, 0, 0, 0, someScalingFactor); + } + } + + GL11.glDepthMask(true); + + renderEquippedItems.invoke(render, statue.creature, someScalingFactor); + + // 'Pokemon' exception handling... Because why not?! + } catch (Exception e) { + System.err.println("Something went very wrong! Error while rendering petrified creature:"); + e.printStackTrace(); + } + } + + GL11.glDisable(GL11.GL_ALPHA_TEST); + GL11.glDisable(GL11.GL_BLEND); + GL11.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA); + GL11.glDisable(GL12.GL_RESCALE_NORMAL); + GL11.glEnable(GL11.GL_CULL_FACE); + GL11.glPopMatrix(); + + } - // Multiblock support for the breaking animation. The chest has its own way of doing this in - // TileEntityRendererDispatcher, but I don't have access to that. - if(statue.position != 1 && destroyStage >= 0){ - TileEntity tileentity = statue.getWorld().getTileEntity(statue.getPos().down(statue.position-1)); - //System.out.println(tileentity); - if(tileentity instanceof TileEntityStatue){ - // If this is the block breaking animation pass and this isn't the bottom block, divert the call to - // the bottom block. - this.renderTileEntityAt((TileEntityStatue)tileentity, x, y - (statue.position-1), z, partialTicks, destroyStage); } } - - if(statue.creature != null && statue.position == 1){ - - this.destroyStage = destroyStage; - - GlStateManager.pushMatrix(); - // The next line makes stuff render in the same place relative to the world wherever the player is. - GlStateManager.translate((float)x + 0.5F, (float)y, (float)z + 0.5F); - GlStateManager.enableLighting(); - - float yaw = statue.creature.prevRotationYaw; - int i = statue.creature.getBrightnessForRender(0); - - int j = i % 65536; - int k = i / 65536; - OpenGlHelper.setLightmapTextureCoords(OpenGlHelper.lightmapTexUnit, (float)j / 1.0F, (float)k / 1.0F); - GlStateManager.color(1.0F, 1.0F, 1.0F, 1.0F); - - GlStateManager.rotate(-yaw, 0F, 1F, 0F); - // Stops the normal model from rendering. - if(!statue.isIce) statue.creature.setInvisible(true); - // Setting the last parameter to true prevents the debug bounding box from rendering. - // For some reason, passing in the partialTicks causes the entity to spin round really fast - Minecraft.getMinecraft().getRenderManager().doRenderEntity(statue.creature, 0, 0, 0, 0, 0, true); - if(!statue.isIce) statue.creature.setInvisible(false); - - GlStateManager.popMatrix(); - - } - } - - public ResourceLocation getBlockBreakingTexture(){ - return destroyStage < 0 ? null : DESTROY_STAGES[destroyStage]; + GL11.glPopMatrix(); } } diff --git a/src/main/java/electroblob/wizardry/client/renderer/RenderTransientPlayer.java b/src/main/java/electroblob/wizardry/client/renderer/RenderTransientPlayer.java new file mode 100644 index 00000000..3fa7aa7c --- /dev/null +++ b/src/main/java/electroblob/wizardry/client/renderer/RenderTransientPlayer.java @@ -0,0 +1,90 @@ +package electroblob.wizardry.client.renderer; + +import static net.minecraftforge.client.IItemRenderer.ItemRenderType.EQUIPPED; +import static net.minecraftforge.client.IItemRenderer.ItemRendererHelper.BLOCK_3D; + +import org.lwjgl.opengl.GL11; + +import cpw.mods.fml.relauncher.Side; +import cpw.mods.fml.relauncher.SideOnly; +import net.minecraft.block.Block; +import net.minecraft.client.entity.AbstractClientPlayer; +import net.minecraft.client.entity.EntityPlayerSP; +import net.minecraft.client.model.ModelBiped; +import net.minecraft.client.renderer.RenderBlocks; +import net.minecraft.client.renderer.entity.RenderBiped; +import net.minecraft.client.renderer.entity.RenderPlayer; +import net.minecraft.client.renderer.entity.RendererLivingEntity; +import net.minecraft.client.renderer.tileentity.TileEntitySkullRenderer; +import net.minecraft.entity.Entity; +import net.minecraft.entity.EntityLivingBase; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.item.EnumAction; +import net.minecraft.item.Item; +import net.minecraft.item.ItemArmor; +import net.minecraft.item.ItemBlock; +import net.minecraft.item.ItemStack; +import net.minecraft.scoreboard.Score; +import net.minecraft.scoreboard.ScoreObjective; +import net.minecraft.scoreboard.Scoreboard; +import net.minecraft.util.MathHelper; +import net.minecraft.util.ResourceLocation; +import net.minecraftforge.client.ForgeHooksClient; +import net.minecraftforge.client.IItemRenderer; +import net.minecraftforge.client.MinecraftForgeClient; +import net.minecraftforge.client.event.RenderPlayerEvent; +import net.minecraftforge.common.MinecraftForge; + +@Deprecated +public class RenderTransientPlayer extends RenderPlayer { + + private static final ResourceLocation steveTextures = new ResourceLocation("textures/entity/steve.png"); + private ModelBiped modelBipedMain; + private ModelBiped modelArmorChestplate; + private ModelBiped modelArmor; + + public RenderTransientPlayer() + { + super(); + this.modelBipedMain = (ModelBiped)this.mainModel; + this.modelArmorChestplate = new ModelBiped(1.0F); + this.modelArmor = new ModelBiped(0.5F); + } + + public void func_130009_a(AbstractClientPlayer par1AbstractClientPlayer, double par2, double par4, double par6, float par8, float par9) + { + float f2 = 1.0F; + GL11.glColor4f(f2, f2, f2, 0.5f); + ItemStack itemstack = par1AbstractClientPlayer.inventory.getCurrentItem(); + this.modelArmorChestplate.heldItemRight = this.modelArmor.heldItemRight = this.modelBipedMain.heldItemRight = itemstack != null ? 1 : 0; + + if (itemstack != null && par1AbstractClientPlayer.getItemInUseCount() > 0) + { + EnumAction enumaction = itemstack.getItemUseAction(); + + if (enumaction == EnumAction.block) + { + this.modelArmorChestplate.heldItemRight = this.modelArmor.heldItemRight = this.modelBipedMain.heldItemRight = 3; + } + else if (enumaction == EnumAction.bow) + { + this.modelArmorChestplate.aimedBow = this.modelArmor.aimedBow = this.modelBipedMain.aimedBow = true; + } + } + + this.modelArmorChestplate.isSneak = this.modelArmor.isSneak = this.modelBipedMain.isSneak = par1AbstractClientPlayer.isSneaking(); + double d3 = par4 - (double)par1AbstractClientPlayer.yOffset; + + if (par1AbstractClientPlayer.isSneaking() && !(par1AbstractClientPlayer instanceof EntityPlayerSP)) + { + d3 -= 0.125D; + } + + super.doRender(par1AbstractClientPlayer, par2, d3, par6, par8, par9); + this.modelArmorChestplate.aimedBow = this.modelArmor.aimedBow = this.modelBipedMain.aimedBow = false; + this.modelArmorChestplate.isSneak = this.modelArmor.isSneak = this.modelBipedMain.isSneak = false; + this.modelArmorChestplate.heldItemRight = this.modelArmor.heldItemRight = this.modelBipedMain.heldItemRight = 0; + MinecraftForge.EVENT_BUS.post(new RenderPlayerEvent.Post(par1AbstractClientPlayer, this, par9)); + } + +} diff --git a/src/main/java/electroblob/wizardry/client/renderer/RenderTranslucentItem.java b/src/main/java/electroblob/wizardry/client/renderer/RenderTranslucentItem.java new file mode 100644 index 00000000..7eacf26c --- /dev/null +++ b/src/main/java/electroblob/wizardry/client/renderer/RenderTranslucentItem.java @@ -0,0 +1,97 @@ +package electroblob.wizardry.client.renderer; + +import org.lwjgl.opengl.GL11; +import org.lwjgl.opengl.GL12; + +import net.minecraft.client.Minecraft; +import net.minecraft.client.renderer.ItemRenderer; +import net.minecraft.client.renderer.RenderBlocks; +import net.minecraft.client.renderer.Tessellator; +import net.minecraft.client.renderer.texture.TextureManager; +import net.minecraft.item.ItemStack; +import net.minecraft.util.IIcon; +import net.minecraft.util.ResourceLocation; +import net.minecraftforge.client.IItemRenderer; + +public class RenderTranslucentItem implements IItemRenderer { + + Minecraft mc = Minecraft.getMinecraft(); + + private static final ResourceLocation RES_ITEM_GLINT = new ResourceLocation("textures/misc/enchanted_item_glint.png"); + + @Override + public boolean handleRenderType(ItemStack item, ItemRenderType type) { + if(type == ItemRenderType.EQUIPPED_FIRST_PERSON || type == ItemRenderType.EQUIPPED){ + return true; + } + return false; + } + + @Override + public boolean shouldUseRenderHelper(ItemRenderType type, ItemStack item, + ItemRendererHelper helper) { + return false; + } + + @Override + public void renderItem(ItemRenderType type, ItemStack item, Object... data) { + if(type == ItemRenderType.EQUIPPED_FIRST_PERSON || type == ItemRenderType.EQUIPPED){ + + GL11.glPushMatrix(); + + TextureManager texturemanager = this.mc.getTextureManager(); + IIcon icon = mc.thePlayer.getItemIcon(item, 1); + + if (icon == null) + { + GL11.glPopMatrix(); + return; + } + + texturemanager.bindTexture(texturemanager.getResourceLocation(item.getItemSpriteNumber())); + Tessellator tessellator = Tessellator.instance; + float f = icon.getMinU(); + float f1 = icon.getMaxU(); + float f2 = icon.getMinV(); + float f3 = icon.getMaxV(); + GL11.glEnable(GL12.GL_RESCALE_NORMAL); + GL11.glEnable(GL11.GL_BLEND); + ItemRenderer.renderItemIn2D(tessellator, f1, f2, f, f3, icon.getIconWidth(), icon.getIconHeight(), 0.0625F); + + if (item.hasEffect(1)) + { + GL11.glDepthFunc(GL11.GL_EQUAL); + GL11.glDisable(GL11.GL_LIGHTING); + texturemanager.bindTexture(RES_ITEM_GLINT); + GL11.glBlendFunc(GL11.GL_SRC_COLOR, GL11.GL_ONE); + float f7 = 0.76F; + GL11.glColor4f(0.5F * f7, 0.25F * f7, 0.8F * f7, 1.0F); + GL11.glMatrixMode(GL11.GL_TEXTURE); + GL11.glPushMatrix(); + float f8 = 0.125F; + GL11.glScalef(f8, f8, f8); + float f9 = (float)(Minecraft.getSystemTime() % 3000L) / 3000.0F * 8.0F; + GL11.glTranslatef(f9, 0.0F, 0.0F); + GL11.glRotatef(-50.0F, 0.0F, 0.0F, 1.0F); + ItemRenderer.renderItemIn2D(tessellator, 0.0F, 0.0F, 1.0F, 1.0F, 256, 256, 0.0625F); + GL11.glPopMatrix(); + GL11.glPushMatrix(); + GL11.glScalef(f8, f8, f8); + f9 = (float)(Minecraft.getSystemTime() % 4873L) / 4873.0F * 8.0F; + GL11.glTranslatef(-f9, 0.0F, 0.0F); + GL11.glRotatef(10.0F, 0.0F, 0.0F, 1.0F); + ItemRenderer.renderItemIn2D(tessellator, 0.0F, 0.0F, 1.0F, 1.0F, 256, 256, 0.0625F); + GL11.glPopMatrix(); + GL11.glMatrixMode(GL11.GL_MODELVIEW); + GL11.glEnable(GL11.GL_LIGHTING); + GL11.glDepthFunc(GL11.GL_LEQUAL); + } + + GL11.glDisable(GL12.GL_RESCALE_NORMAL); + GL11.glDisable(GL11.GL_BLEND); + + GL11.glPopMatrix(); + } + } + +} diff --git a/src/main/java/electroblob/wizardry/client/renderer/RenderWand.java b/src/main/java/electroblob/wizardry/client/renderer/RenderWand.java new file mode 100644 index 00000000..d248668a --- /dev/null +++ b/src/main/java/electroblob/wizardry/client/renderer/RenderWand.java @@ -0,0 +1,134 @@ +package electroblob.wizardry.client.renderer; + +import org.lwjgl.opengl.GL11; +import org.lwjgl.opengl.GL12; + +import net.minecraft.client.Minecraft; +import net.minecraft.client.renderer.ItemRenderer; +import net.minecraft.client.renderer.Tessellator; +import net.minecraft.client.renderer.entity.RenderManager; +import net.minecraft.client.renderer.texture.TextureManager; +import net.minecraft.entity.Entity; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.item.ItemStack; +import net.minecraft.util.IIcon; +import net.minecraft.util.ResourceLocation; +import net.minecraftforge.client.IItemRenderer; + +@Deprecated +public class RenderWand implements IItemRenderer { + + Minecraft mc = Minecraft.getMinecraft(); + + private static final ResourceLocation RES_ITEM_GLINT = new ResourceLocation("textures/misc/enchanted_item_glint.png"); + private static final ResourceLocation magicEffect = new ResourceLocation("wizardry:textures/entity/aura.png"); + + @Override + public boolean handleRenderType(ItemStack item, ItemRenderType type) { + if(type == ItemRenderType.EQUIPPED_FIRST_PERSON || type == ItemRenderType.EQUIPPED){ + return true; + } + return false; + } + + @Override + public boolean shouldUseRenderHelper(ItemRenderType type, ItemStack item, + ItemRendererHelper helper) { + return false; + } + + @Override + public void renderItem(ItemRenderType type, ItemStack item, Object... data) { + if(type == ItemRenderType.EQUIPPED_FIRST_PERSON || type == ItemRenderType.EQUIPPED){ + + GL11.glPushMatrix(); + + TextureManager texturemanager = this.mc.getTextureManager(); + IIcon icon = mc.thePlayer.getItemIcon(item, 1); + + if (icon == null) + { + GL11.glPopMatrix(); + return; + } + + texturemanager.bindTexture(texturemanager.getResourceLocation(item.getItemSpriteNumber())); + Tessellator tessellator = Tessellator.instance; + float f = icon.getMinU(); + float f1 = icon.getMaxU(); + float f2 = icon.getMinV(); + float f3 = icon.getMaxV(); + GL11.glEnable(GL12.GL_RESCALE_NORMAL); + ItemRenderer.renderItemIn2D(tessellator, f1, f2, f, f3, icon.getIconWidth(), icon.getIconHeight(), 0.0625F); + + if (item.hasEffect(1)) + { + GL11.glDepthFunc(GL11.GL_EQUAL); + GL11.glDisable(GL11.GL_LIGHTING); + texturemanager.bindTexture(RES_ITEM_GLINT); + GL11.glBlendFunc(GL11.GL_SRC_COLOR, GL11.GL_ONE); + float f7 = 0.76F; + GL11.glColor4f(0.5F * f7, 0.25F * f7, 0.8F * f7, 1.0F); + GL11.glMatrixMode(GL11.GL_TEXTURE); + GL11.glPushMatrix(); + float f8 = 0.125F; + GL11.glScalef(f8, f8, f8); + float f9 = (float)(Minecraft.getSystemTime() % 3000L) / 3000.0F * 8.0F; + GL11.glTranslatef(f9, 0.0F, 0.0F); + GL11.glRotatef(-50.0F, 0.0F, 0.0F, 1.0F); + ItemRenderer.renderItemIn2D(tessellator, 0.0F, 0.0F, 1.0F, 1.0F, 256, 256, 0.0625F); + GL11.glPopMatrix(); + GL11.glPushMatrix(); + GL11.glScalef(f8, f8, f8); + f9 = (float)(Minecraft.getSystemTime() % 4873L) / 4873.0F * 8.0F; + GL11.glTranslatef(-f9, 0.0F, 0.0F); + GL11.glRotatef(10.0F, 0.0F, 0.0F, 1.0F); + ItemRenderer.renderItemIn2D(tessellator, 0.0F, 0.0F, 1.0F, 1.0F, 256, 256, 0.0625F); + GL11.glPopMatrix(); + GL11.glMatrixMode(GL11.GL_MODELVIEW); + GL11.glEnable(GL11.GL_LIGHTING); + GL11.glDepthFunc(GL11.GL_LEQUAL); + } + + GL11.glDisable(GL12.GL_RESCALE_NORMAL); + + + Entity entity = (Entity)data[1]; + + // Spell charge-up effect + if(entity instanceof EntityPlayer && ((EntityPlayer)entity).isUsingItem()){ + + //System.out.println(type.toString()); + + GL11.glPushMatrix(); + + //GL11.glEnable(GL11.GL_BLEND); + //GL11.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA); + + GL11.glColor3f(1, 0, 0); + GL11.glRotatef(180, 1, 0, 0); + + //GL11.glRotatef(180.0F - RenderManager.instance.playerViewY, 0.0F, 1.0F, 0.0F); + //GL11.glRotatef(-RenderManager.instance.playerViewX, 1.0F, 0.0F, 0.0F); + + texturemanager.bindTexture(magicEffect); + + tessellator.startDrawingQuads(); + + tessellator.addVertex(0, 0, 0); + tessellator.addVertex(0, 1, 0); + tessellator.addVertex(1, 1, 0); + tessellator.addVertex(1, 0, 0); + + tessellator.draw(); + + //GL11.glDisable(GL11.GL_BLEND); + GL11.glPopMatrix(); + + } + + GL11.glPopMatrix(); + } + } + +} diff --git a/src/main/java/electroblob/wizardry/client/renderer/RenderWizard.java b/src/main/java/electroblob/wizardry/client/renderer/RenderWizard.java index e2043d5b..020aa6e7 100644 --- a/src/main/java/electroblob/wizardry/client/renderer/RenderWizard.java +++ b/src/main/java/electroblob/wizardry/client/renderer/RenderWizard.java @@ -1,34 +1,213 @@ package electroblob.wizardry.client.renderer; -import electroblob.wizardry.Wizardry; +import static net.minecraftforge.client.IItemRenderer.ItemRenderType.EQUIPPED; +import static net.minecraftforge.client.IItemRenderer.ItemRendererHelper.BLOCK_3D; + +import org.lwjgl.opengl.GL11; + +import cpw.mods.fml.relauncher.Side; +import cpw.mods.fml.relauncher.SideOnly; import electroblob.wizardry.client.model.ModelWizard; import electroblob.wizardry.entity.living.EntityWizard; +import net.minecraft.block.Block; +import net.minecraft.client.model.ModelVillager; +import net.minecraft.client.renderer.RenderBlocks; import net.minecraft.client.renderer.entity.RenderBiped; -import net.minecraft.client.renderer.entity.RenderManager; -import net.minecraft.client.renderer.entity.layers.LayerBipedArmor; +import net.minecraft.client.renderer.entity.RenderLiving; +import net.minecraft.entity.Entity; +import net.minecraft.entity.EntityLiving; +import net.minecraft.entity.EntityLivingBase; +import net.minecraft.item.EnumAction; +import net.minecraft.item.Item; +import net.minecraft.item.ItemStack; import net.minecraft.util.ResourceLocation; -import net.minecraftforge.fml.relauncher.Side; -import net.minecraftforge.fml.relauncher.SideOnly; +import net.minecraftforge.client.IItemRenderer; +import net.minecraftforge.client.MinecraftForgeClient; @SideOnly(Side.CLIENT) -public class RenderWizard extends RenderBiped { - +public class RenderWizard extends RenderBiped +{ static final ResourceLocation[] textures = new ResourceLocation[6]; - public RenderWizard(RenderManager renderManager){ - - super(renderManager, new ModelWizard(), 0.5F); - + public RenderWizard() + { + super(new ModelWizard(), 0.5F); for(int i=0;i<6;i++){ - textures[i] = new ResourceLocation(Wizardry.MODID, "textures/entity/wizard_" + i + ".png"); + textures[i] = new ResourceLocation("wizardry:textures/entity/wizard_" + i + ".png"); } - // Just using the default without overriding models, since the armour sets its own model anyway. - this.addLayer(new LayerBipedArmor(this)); - } - - @Override - protected ResourceLocation getEntityTexture(EntityWizard wizard) { - return textures[wizard.textureIndex]; } + /** + * Determines wether Villager Render pass or not. + */ + protected int shouldVillagerRenderPass(EntityWizard par1EntityWizard, int par2, float par3) + { + return -1; + } + + public void renderVillager(EntityWizard par1EntityWizard, double par2, double par4, double par6, float par8, float par9) + { + super.doRender(par1EntityWizard, par2, par4, par6, par8, par9); + } + + protected ResourceLocation func_110902_a(EntityWizard par1EntityWizard) + { + return textures[par1EntityWizard.textureIndex]; + } + + protected void renderVillagerEquipedItems(EntityWizard par1EntityWizard, float par2) + { + super.renderEquippedItems(par1EntityWizard, par2); + /* + ItemStack itemstack1 = par1EntityWizard.getHeldItem(); + + if (itemstack1 != null) + { + GL11.glPushMatrix(); + this.villagerModel.bipedRightArm.postRender(0.0625F); + GL11.glTranslatef(-0.0625F, 0.5375F, 0.0625F); + + EnumAction enumaction = null; + + float f11; + float f6; + + IItemRenderer customRenderer = MinecraftForgeClient.getItemRenderer(itemstack1, EQUIPPED); + boolean is3D = (customRenderer != null && customRenderer.shouldUseRenderHelper(EQUIPPED, itemstack1, BLOCK_3D)); + boolean isBlock = itemstack1.itemID < Block.blocksList.length && itemstack1.getItemSpriteNumber() == 0; + + if (is3D || (isBlock && RenderBlocks.renderItemIn3d(Block.blocksList[itemstack1.itemID].getRenderType()))) + { + f11 = 0.5F; + GL11.glTranslatef(0.0F, 0.1875F, -0.3125F); + f11 *= 0.75F; + GL11.glRotatef(20.0F, 1.0F, 0.0F, 0.0F); + GL11.glRotatef(45.0F, 0.0F, 1.0F, 0.0F); + GL11.glScalef(-f11, -f11, f11); + } + else if (itemstack1.itemID == Item.bow.itemID) + { + f11 = 0.625F; + GL11.glTranslatef(0.0F, 0.125F, 0.3125F); + GL11.glRotatef(-20.0F, 0.0F, 1.0F, 0.0F); + GL11.glScalef(f11, -f11, f11); + GL11.glRotatef(-100.0F, 1.0F, 0.0F, 0.0F); + GL11.glRotatef(45.0F, 0.0F, 1.0F, 0.0F); + } + else if (Item.itemsList[itemstack1.itemID].isFull3D()) + { + f11 = 0.625F; + + if (Item.itemsList[itemstack1.itemID].shouldRotateAroundWhenRendering()) + { + GL11.glRotatef(180.0F, 0.0F, 0.0F, 1.0F); + GL11.glTranslatef(0.0F, -0.125F, 0.0F); + } + + GL11.glTranslatef(0.0F, 0.1875F, 0.0F); + GL11.glScalef(f11, -f11, f11); + GL11.glRotatef(-100.0F, 1.0F, 0.0F, 0.0F); + GL11.glRotatef(45.0F, 0.0F, 1.0F, 0.0F); + } + else + { + f11 = 0.375F; + GL11.glTranslatef(0.25F, 0.1875F, -0.1875F); + GL11.glScalef(f11, f11, f11); + GL11.glRotatef(60.0F, 0.0F, 0.0F, 1.0F); + GL11.glRotatef(-90.0F, 1.0F, 0.0F, 0.0F); + GL11.glRotatef(20.0F, 0.0F, 0.0F, 1.0F); + } + + float f12; + float f13; + int j; + + if (itemstack1.getItem().requiresMultipleRenderPasses()) + { + for (j = 0; j < itemstack1.getItem().getRenderPasses(itemstack1.getItemDamage()); ++j) + { + int k = itemstack1.getItem().getColorFromItemStack(itemstack1, j); + f13 = (float)(k >> 16 & 255) / 255.0F; + f12 = (float)(k >> 8 & 255) / 255.0F; + f6 = (float)(k & 255) / 255.0F; + GL11.glColor4f(f13, f12, f6, 1.0F); + this.renderManager.itemRenderer.renderItem(par1EntityWizard, itemstack1, j); + } + } + else + { + j = itemstack1.getItem().getColorFromItemStack(itemstack1, 0); + float f14 = (float)(j >> 16 & 255) / 255.0F; + f13 = (float)(j >> 8 & 255) / 255.0F; + f12 = (float)(j & 255) / 255.0F; + GL11.glColor4f(f14, f13, f12, 1.0F); + this.renderManager.itemRenderer.renderItem(par1EntityWizard, itemstack1, 0); + } + + GL11.glPopMatrix(); + } + */ + } + + protected void preRenderVillager(EntityWizard par1EntityWizard, float par2) + { + float f1 = 0.9375F; + this.shadowSize = 0.5F; + + GL11.glScalef(f1, f1, f1); + } + + public void doRenderLiving(EntityLiving par1EntityLiving, double par2, double par4, double par6, float par8, float par9) + { + this.renderVillager((EntityWizard)par1EntityLiving, par2, par4, par6, par8, par9); + } + + /** + * Allows the render to do any OpenGL state modifications necessary before the model is rendered. Args: + * entityLiving, partialTickTime + */ + protected void preRenderCallback(EntityLivingBase par1EntityLivingBase, float par2) + { + this.preRenderVillager((EntityWizard)par1EntityLivingBase, par2); + } + + /** + * Queries whether should render the specified pass or not. + */ + protected int shouldRenderPass(EntityLivingBase par1EntityLivingBase, int par2, float par3) + { + // TODO: Is this the right method name now? There are several with correct parameters. This one may end up being recursive! + return this.shouldRenderPass((EntityLiving)par1EntityLivingBase, par2, par3); + //return this.shouldVillagerRenderPass((EntityWizard)par1EntityLivingBase, par2, par3); + } + + protected void renderEquippedItems(EntityLivingBase par1EntityLivingBase, float par2) + { + this.renderVillagerEquipedItems((EntityWizard)par1EntityLivingBase, par2); + } + + public void renderPlayer(EntityLivingBase par1EntityLivingBase, double par2, double par4, double par6, float par8, float par9) + { + this.renderVillager((EntityWizard)par1EntityLivingBase, par2, par4, par6, par8, par9); + } + + /** + * Returns the location of an entity's texture. Doesn't seem to be called unless you call Render.bindEntityTexture. + */ + protected ResourceLocation getEntityTexture(Entity par1Entity) + { + return this.func_110902_a((EntityWizard)par1Entity); + } + + /** + * Actually renders the given argument. This is a synthetic bridge method, always casting down its argument and then + * handing it off to a worker function which does the actual work. In all probabilty, the class Render is generic + * (Render +public class RenderWraithMinion extends RenderLiving { - private ResourceLocation texture = new ResourceLocation("textures/entity/blaze.png"); + private ResourceLocation blazeTextures; + private int field_77068_a; - public RenderWraithMinion(RenderManager renderManagerIn) + public RenderWraithMinion(ResourceLocation texture) { - super(renderManagerIn, new ModelBlaze(), 0.5F); + super(new ModelBlaze(), 0.5F); + this.field_77068_a = ((ModelBlaze)this.mainModel).func_78104_a(); + this.blazeTextures = texture; } - @Override - protected ResourceLocation getEntityTexture(EntityBlazeMinion entity) + public void renderBlaze(EntityLiving par1EntityBlazeMinion, double par2, double par4, double par6, float par8, float par9) { - return texture; + int i = ((ModelBlaze)this.mainModel).func_78104_a(); + + if (i != this.field_77068_a) + { + this.field_77068_a = i; + this.mainModel = new ModelBlaze(); + } + + super.doRender(par1EntityBlazeMinion, par2, par4, par6, par8, par9); + } + + protected ResourceLocation getBlazeTextures(EntityLiving par1Entity) + { + return blazeTextures; + } + + public void doRenderLiving(EntityLiving par1EntityLiving, double par2, double par4, double par6, float par8, float par9) + { + this.renderBlaze(par1EntityLiving, par2, par4, par6, par8, par9); + } + + public void renderPlayer(EntityLivingBase par1EntityLivingBase, double par2, double par4, double par6, float par8, float par9) + { + this.renderBlaze((EntityLiving) par1EntityLivingBase, par2, par4, par6, par8, par9); + } + + /** + * Returns the location of an entity's texture. Doesn't seem to be called unless you call Render.bindEntityTexture. + */ + protected ResourceLocation getEntityTexture(Entity par1Entity) + { + return this.getBlazeTextures((EntityLiving)par1Entity); + } + + /** + * Actually renders the given argument. This is a synthetic bridge method, always casting down its argument and then + * handing it off to a worker function which does the actual work. In all probabilty, the class Render is generic + * (Render getTabCompletionOptions(MinecraftServer server, ICommandSender sender, String[] arguments, BlockPos pos) { + public List addTabCompletionOptions(ICommandSender sender, String[] arguments) { switch(arguments.length){ - case 1: return getListOfStringsMatchingLastWord(arguments, Spell.getSpellNames()); - case 2: return getListOfStringsMatchingLastWord(arguments, server.getAllUsernames()); + case 1: return getListOfStringsMatchingLastWord(arguments, Spell.getUnlocalisedNames()); + case 2: return getListOfStringsMatchingLastWord(arguments, MinecraftServer.getServer().getAllUsernames()); } - return super.getTabCompletionOptions(server, sender, arguments, pos); + return super.addTabCompletionOptions(sender, arguments); } @Override - public void execute(MinecraftServer server, ICommandSender sender, String[] arguments) throws CommandException { + public void processCommand(ICommandSender sender, String[] arguments){ if(arguments.length < 1){ - throw new WrongUsageException("commands.wizardry:cast.usage", Wizardry.settings.castCommandName); + throw new WrongUsageException("commands.cast.usage", Wizardry.castCommandName); }else{ - // ===== Parameter retrieval ===== - int i=0; - EntityPlayerMP caster = null; + EntityPlayerMP entityplayermp = null; try{ - caster = getCommandSenderAsPlayer(sender); + entityplayermp = getCommandSenderAsPlayer(sender); }catch(PlayerNotFoundException exception){ // Nothing here since the player specifying is done later, I just don't want it to throw an exception here. } @@ -81,7 +74,7 @@ public class CommandCastSpell extends CommandBase { Spell spell = Spell.get(arguments[i++]); if(spell == null){ - throw new NumberInvalidException("commands.wizardry:cast.not_found", new Object[]{arguments[i-1]}); + throw new NumberInvalidException("commands.cast.not_found", new Object[]{arguments[i-1]}); } boolean castAsOtherPlayer = false; @@ -90,10 +83,10 @@ public class CommandCastSpell extends CommandBase { try{ // If the second argument is a player and is not the player that gave the command, the spell is cast // as the given player rather than the command sender, and there is a different chat readout. - EntityPlayerMP entityplayermp = getPlayer(server, sender, arguments[i++]); - if(caster != entityplayermp){ + EntityPlayerMP entityplayermp1 = getPlayer(sender, arguments[i++]); + if(entityplayermp != entityplayermp1){ castAsOtherPlayer = true; - caster = entityplayermp; + entityplayermp = entityplayermp1; } }catch(PlayerNotFoundException exception){ // If no player was found, rather than give an error it simply assumes the player was unspecified. @@ -103,56 +96,27 @@ public class CommandCastSpell extends CommandBase { // If, after this point, the player is still null, the sender must be a command block or the console and the // player must not have been specified, meaning an exception should be thrown. - if(caster == null) throw new PlayerNotFoundException("You must specify which player you wish to perform this action on."); - - SpellModifiers modifiers = new SpellModifiers(); - - if(i < arguments.length){ - - // Copied from CommandGive. Why it doesn't just use arguments[i] itself I don't know. - String nbt = getChatComponentFromNthArg(sender, arguments, i++).getUnformattedText(); - - try{ - modifiers = SpellModifiers.fromNBT(JsonToNBT.getTagFromJson(nbt)); - }catch(NBTException nbtexception){ - throw new CommandException("commands.wizardry:cast.tag_error", nbtexception.getMessage()); - } - - for(float multiplier : modifiers.getModifiers().values()){ - if(multiplier < 0){ - throw new NumberInvalidException("commands.generic.double.tooSmall", multiplier, 0); - }else if(multiplier > Wizardry.settings.maxSpellCommandMultiplier){ - throw new NumberInvalidException("commands.generic.double.tooBig", multiplier, Wizardry.settings.maxSpellCommandMultiplier); - } - } - - } + if(entityplayermp == null) throw new PlayerNotFoundException("You must specify which player you wish to perform this action on."); - // ===== Spell casting ===== - - // If anything stops the spell working at this point, nothing else happens. - if(MinecraftForge.EVENT_BUS.post(new SpellCastEvent.Pre(caster, spell, modifiers, Source.COMMAND))){ - displayFailMessage(sender, spell); - return; - } + float damageMultiplier = i < arguments.length ? (float)this.parseDoubleBounded(sender, arguments[i++], 0d, Wizardry.maxSpellCommandMultiplier) : 1; + float rangeMultiplier = i < arguments.length ? (float)this.parseDoubleBounded(sender, arguments[i++], 0d, Wizardry.maxSpellCommandMultiplier) : 1; + float durationMultiplier = i < arguments.length ? (float)this.parseDoubleBounded(sender, arguments[i++], 0d, Wizardry.maxSpellCommandMultiplier) : 1; + float blastMultiplier = i < arguments.length ? (float)this.parseDoubleBounded(sender, arguments[i++], 0d, Wizardry.maxSpellCommandMultiplier) : 1; - WizardData data = WizardData.get((EntityPlayer)caster); - if(spell.isContinuous){ - // Events for continuous spell casting via commands are dealt with in WizardData. + ExtendedPlayer properties = ExtendedPlayer.get((EntityPlayer)entityplayermp); - if(data != null){ - if(data.isCasting()){ - data.stopCastingContinuousSpell(); + if(properties != null){ + if(properties.isCasting()){ + ExtendedPlayer.get((EntityPlayer)entityplayermp).stopCastingContinuousSpell(); }else{ - - data.startCastingContinuousSpell(spell, modifiers); + ExtendedPlayer.get((EntityPlayer)entityplayermp).startCastingContinuousSpell(spell, damageMultiplier, rangeMultiplier, durationMultiplier, blastMultiplier); if(castAsOtherPlayer){ - sender.addChatMessage(new TextComponentTranslation("commands.wizardry:cast.success_remote_continuous", spell.getNameForTranslationFormatted(), caster.getName())); + sender.addChatMessage(new ChatComponentTranslation("commands.cast.success_remote_continuous", spell.getDisplayNameWithFormatting(), entityplayermp.getCommandSenderName())); }else{ - sender.addChatMessage(new TextComponentTranslation("commands.wizardry:cast.success_continuous", spell.getNameForTranslationFormatted())); + sender.addChatMessage(new ChatComponentTranslation("commands.cast.success_continuous", spell.getDisplayNameWithFormatting())); } } @@ -161,36 +125,39 @@ public class CommandCastSpell extends CommandBase { }else{ - if(spell.cast(caster.worldObj, caster, EnumHand.MAIN_HAND, 0, modifiers)){ + if(spell.cast(entityplayermp.worldObj, entityplayermp, 0, damageMultiplier, rangeMultiplier, durationMultiplier, blastMultiplier)){ - MinecraftForge.EVENT_BUS.post(new SpellCastEvent.Post(caster, spell, modifiers, Source.COMMAND)); + // TODO: In this case, is it more efficient/simpler/better design to just do away with the packet + // optimisation entirely rather than calling sync() for spells which don't send packets? if(spell.doesSpellRequirePacket()){ // Sends a packet to all players in dimension to tell them to spawn particles. // Only sent if the spell succeeded, because if the spell failed, you wouldn't // need to spawn any particles! - IMessage msg = new PacketCastSpell.Message(caster.getEntityId(), null, spell.id(), modifiers); - WizardryPacketHandler.net.sendToDimension(msg, caster.worldObj.provider.getDimension()); + IMessage msg = new PacketCastSpell.Message(entityplayermp.getEntityId(), 0, spell.id(), damageMultiplier, rangeMultiplier, blastMultiplier); + WizardryPacketHandler.net.sendToDimension(msg, entityplayermp.worldObj.provider.dimensionId); } + if(ExtendedPlayer.get((EntityPlayer)entityplayermp) != null){ + ExtendedPlayer.get((EntityPlayer)entityplayermp).discoverSpell(spell); + // If the spell didn't send a packet itself, the extended player needs to be synced so the + // spell discovery updates on the client. + if(!spell.doesSpellRequirePacket()) ExtendedPlayer.get((EntityPlayer)entityplayermp).sync(); + } + if(castAsOtherPlayer){ - sender.addChatMessage(new TextComponentTranslation("commands.wizardry:cast.success_remote", spell.getNameForTranslationFormatted(), caster.getName())); + sender.addChatMessage(new ChatComponentTranslation("commands.cast.success_remote", spell.getDisplayNameWithFormatting(), entityplayermp.getCommandSenderName())); }else{ - sender.addChatMessage(new TextComponentTranslation("commands.wizardry:cast.success", spell.getNameForTranslationFormatted())); + sender.addChatMessage(new ChatComponentTranslation("commands.cast.success", spell.getDisplayNameWithFormatting())); } return; } } - displayFailMessage(sender, spell); + IChatComponent message = new ChatComponentTranslation("commands.cast.fail", spell.getDisplayNameWithFormatting()); + message.getChatStyle().setColor(EnumChatFormatting.RED); + sender.addChatMessage(message); } } - - /** Displays the "Unable to cast [spell]" message in the chat. */ - private void displayFailMessage(ICommandSender sender, Spell spell){ - ITextComponent message = new TextComponentTranslation("commands.wizardry:cast.fail", spell.getNameForTranslationFormatted()); - message.getStyle().setColor(TextFormatting.RED); - sender.addChatMessage(message); - } } diff --git a/src/main/java/electroblob/wizardry/command/CommandDiscoverSpell.java b/src/main/java/electroblob/wizardry/command/CommandDiscoverSpell.java index 85a292fa..f41da141 100644 --- a/src/main/java/electroblob/wizardry/command/CommandDiscoverSpell.java +++ b/src/main/java/electroblob/wizardry/command/CommandDiscoverSpell.java @@ -2,133 +2,133 @@ package electroblob.wizardry.command; import java.util.List; -import electroblob.wizardry.WizardData; +import cpw.mods.fml.common.network.simpleimpl.IMessage; +import electroblob.wizardry.ExtendedPlayer; import electroblob.wizardry.Wizardry; -import electroblob.wizardry.event.DiscoverSpellEvent; -import electroblob.wizardry.registry.Spells; +import electroblob.wizardry.WizardryRegistry; +import electroblob.wizardry.packet.PacketCastSpell; +import electroblob.wizardry.packet.WizardryPacketHandler; import electroblob.wizardry.spell.Spell; import net.minecraft.command.CommandBase; -import net.minecraft.command.CommandException; import net.minecraft.command.ICommandSender; import net.minecraft.command.NumberInvalidException; import net.minecraft.command.PlayerNotFoundException; import net.minecraft.command.WrongUsageException; +import net.minecraft.entity.player.EntityPlayer; import net.minecraft.entity.player.EntityPlayerMP; import net.minecraft.server.MinecraftServer; -import net.minecraft.util.math.BlockPos; -import net.minecraft.util.text.TextComponentTranslation; -import net.minecraftforge.common.MinecraftForge; +import net.minecraft.util.ChatComponentTranslation; +import net.minecraft.util.EnumChatFormatting; +import net.minecraft.util.IChatComponent; +import net.minecraft.util.StatCollector; public class CommandDiscoverSpell extends CommandBase { @Override public String getCommandName(){ - return Wizardry.settings.discoverspellCommandName; + return Wizardry.discoverspellCommandName; } - + @Override public int getRequiredPermissionLevel(){ // I *think* it's something like 0 = everyone, 1 = moderator, 2 = op/admin, 3 = op/console... return 2; } - - /* + @Override - public boolean checkPermission(MinecraftServer server, ICommandSender sender){ + public boolean canCommandSenderUseCommand(ICommandSender sender){ // Only ops (multiplayer) or players with cheats enabled (singleplayer/LAN) can use /discoverspell. - return !(sender instanceof EntityPlayer) || server.getServer().getConfigurationManager().func_152596_g(((EntityPlayer)sender).getGameProfile()); + return !(sender instanceof EntityPlayer) || MinecraftServer.getServer().getConfigurationManager().func_152596_g(((EntityPlayer)sender).getGameProfile()); } - */ + @Override public String getCommandUsage(ICommandSender p_71518_1_){ - // Not ideal, but the way this is implemented means I have no choice. Only used in the help command, so in there - // the custom command name will not display. - return "commands.wizardry:discoverspell.usage"; - //return I18n.format("commands.wizardry:discoverspell.usage", Wizardry.settings.discoverspellCommandName); + return StatCollector.translateToLocalFormatted("commands.discoverspell.usage", Wizardry.discoverspellCommandName); } - + @Override - public List getTabCompletionOptions(MinecraftServer server, ICommandSender sender, String[] arguments, BlockPos pos) { + public List addTabCompletionOptions(ICommandSender sender, String[] arguments) { switch(arguments.length){ - case 1: return getListOfStringsMatchingLastWord(arguments, Spell.getSpellNames()); - case 2: return getListOfStringsMatchingLastWord(arguments, server.getAllUsernames()); + case 1: return getListOfStringsMatchingLastWord(arguments, Spell.getUnlocalisedNames()); + case 2: return getListOfStringsMatchingLastWord(arguments, MinecraftServer.getServer().getAllUsernames()); } - return super.getTabCompletionOptions(server, sender, arguments, pos); + return super.addTabCompletionOptions(sender, arguments); } @Override - public void execute(MinecraftServer server, ICommandSender sender, String[] arguments) throws CommandException { - + public void processCommand(ICommandSender sender, String[] arguments){ + if(arguments.length < 1){ - throw new WrongUsageException("commands.wizardry:discoverspell.usage", Wizardry.settings.discoverspellCommandName); - }else{ - - int i=0; - boolean clear = false; - boolean all = false; - - EntityPlayerMP player = null; - - try{ - player = getCommandSenderAsPlayer(sender); - }catch(PlayerNotFoundException exception){ - // Nothing here since the player specifying is done later, I just don't want it to throw an exception here. - } - - Spell spell = Spells.none; - - if(arguments[i].equals("clear")){ - clear = true; - i++; - }else if(arguments[i].equals("all")){ - all = true; - i++; - }else{ - - spell = Spell.get(arguments[i++]); - - if(spell == null){ - throw new NumberInvalidException("commands.wizardry:discoverspell.not_found", new Object[]{arguments[i-1]}); - } - } - - if(i < arguments.length){ - // If the second argument is a player and is not the player that gave the command, the spell is - // discovered as the given player rather than the command sender. - EntityPlayerMP entityplayermp = getPlayer(server, sender, arguments[i++]); - if(player != entityplayermp){ - player = entityplayermp; - } - } - - // If, after this point, the player is still null, the sender must be a command block or the console and the - // player must not have been specified, meaning an exception should be thrown. - if(player == null) throw new PlayerNotFoundException("You must specify which player you wish to perform this action on."); - - WizardData properties = WizardData.get(player); - - if(properties != null){ - if(clear){ - properties.spellsDiscovered.clear(); - sender.addChatMessage(new TextComponentTranslation("commands.wizardry:discoverspell.clear", player.getName())); - }else if(all){ - properties.spellsDiscovered.addAll(Spell.getSpells(Spell.allSpells)); - sender.addChatMessage(new TextComponentTranslation("commands.wizardry:discoverspell.all", player.getName())); - }else{ - if(properties.hasSpellBeenDiscovered(spell)){ - properties.spellsDiscovered.remove(spell); - sender.addChatMessage(new TextComponentTranslation("commands.wizardry:discoverspell.removespell", spell.getNameForTranslationFormatted(), player.getName())); - }else{ - if(!MinecraftForge.EVENT_BUS.post(new DiscoverSpellEvent(player, spell, DiscoverSpellEvent.Source.COMMAND))){ - properties.discoverSpell(spell); - sender.addChatMessage(new TextComponentTranslation("commands.wizardry:discoverspell.addspell", spell.getNameForTranslationFormatted(), player.getName())); - } - } - } - properties.sync(); - } - } + throw new WrongUsageException("commands.discoverspell.usage", Wizardry.discoverspellCommandName); + }else{ + + int i=0; + boolean clear = false; + boolean all = false; + + EntityPlayerMP entityplayermp = null; + + try{ + entityplayermp = getCommandSenderAsPlayer(sender); + }catch(PlayerNotFoundException exception){ + // Nothing here since the player specifying is done later, I just don't want it to throw an exception here. + } + + Spell spell = WizardryRegistry.none; + + if(arguments[i].equals("clear")){ + clear = true; + i++; + }else if(arguments[i].equals("all")){ + all = true; + i++; + }else{ + + spell = Spell.get(arguments[i++]); + + if(spell == null){ + throw new NumberInvalidException("commands.discoverspell.not_found", new Object[]{arguments[i-1]}); + } + } + + boolean castAsOtherPlayer = false; + + if(i < arguments.length){ + // If the second argument is a player and is not the player that gave the command, the spell is cast + // as the given player rather than the command sender, and there is a different chat readout. + EntityPlayerMP entityplayermp1 = getPlayer(sender, arguments[i++]); + if(entityplayermp != entityplayermp1){ + castAsOtherPlayer = true; + entityplayermp = entityplayermp1; + } + } + + // If, after this point, the player is still null, the sender must be a command block or the console and the + // player must not have been specified, meaning an exception should be thrown. + if(entityplayermp == null) throw new PlayerNotFoundException("You must specify which player you wish to perform this action on."); + + ExtendedPlayer properties = ExtendedPlayer.get(entityplayermp); + + if(properties != null){ + if(clear){ + properties.spellsDiscovered.clear(); + sender.addChatMessage(new ChatComponentTranslation("commands.discoverspell.clear", entityplayermp.getCommandSenderName())); + }else if(all){ + properties.spellsDiscovered.addAll(Spell.getSpells(Spell.allSpells)); + sender.addChatMessage(new ChatComponentTranslation("commands.discoverspell.all", entityplayermp.getCommandSenderName())); + }else{ + if(properties.hasSpellBeenDiscovered(spell)){ + properties.spellsDiscovered.remove(spell); + sender.addChatMessage(new ChatComponentTranslation("commands.discoverspell.removespell", spell.getDisplayNameWithFormatting(), entityplayermp.getCommandSenderName())); + }else{ + properties.discoverSpell(spell); + sender.addChatMessage(new ChatComponentTranslation("commands.discoverspell.addspell", spell.getDisplayNameWithFormatting(), entityplayermp.getCommandSenderName())); + } + } + properties.sync(); + } + } } } diff --git a/src/main/java/electroblob/wizardry/command/CommandSetAlly.java b/src/main/java/electroblob/wizardry/command/CommandSetAlly.java index b566306c..4b20985b 100644 --- a/src/main/java/electroblob/wizardry/command/CommandSetAlly.java +++ b/src/main/java/electroblob/wizardry/command/CommandSetAlly.java @@ -2,11 +2,13 @@ package electroblob.wizardry.command; import java.util.List; -import electroblob.wizardry.WizardData; +import cpw.mods.fml.common.network.simpleimpl.IMessage; +import electroblob.wizardry.ExtendedPlayer; import electroblob.wizardry.Wizardry; -import electroblob.wizardry.util.WizardryUtilities; +import electroblob.wizardry.packet.PacketCastSpell; +import electroblob.wizardry.packet.WizardryPacketHandler; +import electroblob.wizardry.spell.Spell; import net.minecraft.command.CommandBase; -import net.minecraft.command.CommandException; import net.minecraft.command.ICommandSender; import net.minecraft.command.NumberInvalidException; import net.minecraft.command.PlayerNotFoundException; @@ -14,100 +16,99 @@ import net.minecraft.command.WrongUsageException; import net.minecraft.entity.player.EntityPlayer; import net.minecraft.entity.player.EntityPlayerMP; import net.minecraft.server.MinecraftServer; -import net.minecraft.util.math.BlockPos; -import net.minecraft.util.text.TextComponentTranslation; -import net.minecraft.util.text.TextFormatting; +import net.minecraft.util.ChatComponentTranslation; +import net.minecraft.util.EnumChatFormatting; +import net.minecraft.util.IChatComponent; +import net.minecraft.util.StatCollector; public class CommandSetAlly extends CommandBase { @Override public String getCommandName(){ - return Wizardry.settings.allyCommandName; + return Wizardry.allyCommandName; } - + @Override public int getRequiredPermissionLevel(){ // I *think* it's something like 0 = everyone, 1 = moderator, 2 = op/admin, 3 = op/console... return 0; } - + @Override - public boolean checkPermission(MinecraftServer server, ICommandSender p_71519_1_) - { - return true; - } - + public boolean canCommandSenderUseCommand(ICommandSender p_71519_1_) + { + return true; + } + @Override public String getCommandUsage(ICommandSender p_71518_1_){ - // Not ideal, but the way this is implemented means I have no choice. Only used in the help command, so in there - // the custom command name will not display. - return "commands.wizardry:ally.usage"; - //return I18n.format("commands.wizardry:ally.usage", Wizardry.settings.allyCommandName); + return StatCollector.translateToLocalFormatted("commands.ally.usage", Wizardry.allyCommandName); } - + @Override - public List getTabCompletionOptions(MinecraftServer server, ICommandSender sender, String[] arguments, BlockPos pos) { + public List addTabCompletionOptions(ICommandSender sender, String[] arguments) { switch(arguments.length){ - case 1: return getListOfStringsMatchingLastWord(arguments, server.getAllUsernames()); - case 2: return getListOfStringsMatchingLastWord(arguments, server.getAllUsernames()); + case 1: return getListOfStringsMatchingLastWord(arguments, MinecraftServer.getServer().getAllUsernames()); + case 2: return getListOfStringsMatchingLastWord(arguments, MinecraftServer.getServer().getAllUsernames()); } - return super.getTabCompletionOptions(server, sender, arguments, pos); + return super.addTabCompletionOptions(sender, arguments); } @Override - public void execute(MinecraftServer server, ICommandSender sender, String[] arguments) throws CommandException { - + public void processCommand(ICommandSender sender, String[] arguments){ + if(arguments.length < 1){ - throw new WrongUsageException("commands.wizardry:ally.usage", Wizardry.settings.allyCommandName); - }else{ + throw new WrongUsageException("commands.ally.usage", Wizardry.allyCommandName); + }else{ + + EntityPlayerMP allyOf = null; + + try{ + allyOf = getCommandSenderAsPlayer(sender); + }catch(PlayerNotFoundException exception){ + // Nothing here since the player specifying is done later, I just don't want it to throw an exception here. + } + + boolean executeAsOtherPlayer = false; + + EntityPlayerMP ally = getPlayer(sender, arguments[0]); + // Don't want to catch the exception here, because the first player argument is always required. - EntityPlayerMP allyOf = null; - - try{ - allyOf = getCommandSenderAsPlayer(sender); - }catch(PlayerNotFoundException exception){ - // Nothing here since the player specifying is done later, I just don't want it to throw an exception here. - } - - boolean executeAsOtherPlayer = false; - - EntityPlayerMP ally = getPlayer(server, sender, arguments[0]); - // Don't want to catch the exception here, because the first player argument is always required. - - if(arguments.length > 1){ - - allyOf = getPlayer(server, sender, arguments[1]); - // Don't want to catch the exception here either, because there can be no other second argument. - - if(allyOf != sender && sender instanceof EntityPlayer && !WizardryUtilities.isPlayerOp((EntityPlayer)sender, server)){ - // Displays a chat message if a non-op tries to modify another player's allies. - TextComponentTranslation TextComponentTranslation2 = new TextComponentTranslation("commands.wizardry:ally.permission"); - TextComponentTranslation2.getStyle().setColor(TextFormatting.RED); - allyOf.addChatMessage(TextComponentTranslation2); - return; - } - - if(allyOf != sender) executeAsOtherPlayer = true; - } - - // If, after this point, allyOf is still null, the sender must be a command block or the console and two - // players must not have been specified, meaning an exception should be thrown. - if(allyOf == null) throw new PlayerNotFoundException("You must specify which player you wish to perform this action on."); - - if(allyOf == ally) throw new NumberInvalidException("commands.wizardry:ally.self"); - - if(WizardData.get(allyOf) != null){ - String string = WizardData.get(allyOf).toggleAlly(ally) ? "add" : "remove"; - if(executeAsOtherPlayer){ - sender.addChatMessage(new TextComponentTranslation("commands.wizardry:ally." + string + "ally", ally.getName(), allyOf.getName())); - // In this case, the player whose allies have been modified is also notified. - allyOf.addChatMessage(new TextComponentTranslation("item.wand." + string + "ally", ally.getName())); - }else{ - sender.addChatMessage(new TextComponentTranslation("item.wand." + string + "ally", ally.getName())); - } - } - - } + if(arguments.length > 1){ + + allyOf = getPlayer(sender, arguments[1]); + // Don't want to catch the exception here either, because there can be no other second argument. + + // The long-winded statement after the '!' checks if the player is op (multiplayer) or if cheats are enabled for them (singleplayer). + if(allyOf != sender && allyOf instanceof EntityPlayer && !MinecraftServer.getServer().getConfigurationManager().func_152596_g(((EntityPlayer)allyOf).getGameProfile())){ + + ChatComponentTranslation chatcomponenttranslation2 = new ChatComponentTranslation("commands.ally.permission"); + chatcomponenttranslation2.getChatStyle().setColor(EnumChatFormatting.RED); + allyOf.addChatMessage(chatcomponenttranslation2); + return; + } + + if(allyOf != sender) executeAsOtherPlayer = true; + } + + // If, after this point, allyOf is still null, the sender must be a command block or the console and two + // players must not have been specified, meaning an exception should be thrown. + if(allyOf == null) throw new PlayerNotFoundException("You must specify which player you wish to perform this action on."); + + if(allyOf == ally) throw new NumberInvalidException("commands.ally.self"); + + if(ExtendedPlayer.get(allyOf) != null){ + String string = ExtendedPlayer.get(allyOf).toggleAlly(ally) ? "add" : "remove"; + if(executeAsOtherPlayer){ + sender.addChatMessage(new ChatComponentTranslation("commands.ally." + string + "ally", ally.getCommandSenderName(), allyOf.getCommandSenderName())); + // In this case, the player whose allies have been modified is also notified. + allyOf.addChatMessage(new ChatComponentTranslation("item.wand." + string + "ally", ally.getCommandSenderName())); + }else{ + sender.addChatMessage(new ChatComponentTranslation("item.wand." + string + "ally", ally.getCommandSenderName())); + } + } + + } } } diff --git a/src/main/java/electroblob/wizardry/command/CommandViewAllies.java b/src/main/java/electroblob/wizardry/command/CommandViewAllies.java index 257b4b85..ad99b846 100644 --- a/src/main/java/electroblob/wizardry/command/CommandViewAllies.java +++ b/src/main/java/electroblob/wizardry/command/CommandViewAllies.java @@ -1,28 +1,32 @@ package electroblob.wizardry.command; +import java.util.HashSet; import java.util.List; -import java.util.Set; -import electroblob.wizardry.WizardData; +import cpw.mods.fml.common.network.simpleimpl.IMessage; +import electroblob.wizardry.ExtendedPlayer; import electroblob.wizardry.Wizardry; -import electroblob.wizardry.util.WizardryUtilities; -import net.minecraft.client.resources.I18n; +import electroblob.wizardry.packet.PacketCastSpell; +import electroblob.wizardry.packet.WizardryPacketHandler; +import electroblob.wizardry.spell.Spell; import net.minecraft.command.CommandBase; -import net.minecraft.command.CommandException; import net.minecraft.command.ICommandSender; +import net.minecraft.command.NumberInvalidException; import net.minecraft.command.PlayerNotFoundException; +import net.minecraft.command.WrongUsageException; import net.minecraft.entity.player.EntityPlayer; import net.minecraft.entity.player.EntityPlayerMP; import net.minecraft.server.MinecraftServer; -import net.minecraft.util.math.BlockPos; -import net.minecraft.util.text.TextComponentTranslation; -import net.minecraft.util.text.TextFormatting; +import net.minecraft.util.ChatComponentTranslation; +import net.minecraft.util.EnumChatFormatting; +import net.minecraft.util.IChatComponent; +import net.minecraft.util.StatCollector; public class CommandViewAllies extends CommandBase { @Override public String getCommandName(){ - return Wizardry.settings.alliesCommandName; + return Wizardry.alliesCommandName; } @Override @@ -32,29 +36,26 @@ public class CommandViewAllies extends CommandBase { } @Override - public boolean checkPermission(MinecraftServer server, ICommandSender p_71519_1_) + public boolean canCommandSenderUseCommand(ICommandSender p_71519_1_) { return true; } @Override public String getCommandUsage(ICommandSender p_71518_1_){ - // Not ideal, but the way this is implemented means I have no choice. Only used in the help command, so in there - // the custom command name will not display. - return "commands.wizardry:allies.usage"; - //return I18n.format("commands.wizardry:allies.usage", Wizardry.settings.alliesCommandName); + return StatCollector.translateToLocalFormatted("commands.allies.usage", Wizardry.alliesCommandName); } @Override - public List getTabCompletionOptions(MinecraftServer server, ICommandSender sender, String[] arguments, BlockPos pos) { + public List addTabCompletionOptions(ICommandSender sender, String[] arguments) { switch(arguments.length){ - case 1: return getListOfStringsMatchingLastWord(arguments, server.getAllUsernames()); + case 1: return getListOfStringsMatchingLastWord(arguments, MinecraftServer.getServer().getAllUsernames()); } - return super.getTabCompletionOptions(server, sender, arguments, pos); + return super.addTabCompletionOptions(sender, arguments); } @Override - public void execute(MinecraftServer server, ICommandSender sender, String[] arguments) throws CommandException { + public void processCommand(ICommandSender sender, String[] arguments){ EntityPlayerMP player = null; @@ -68,14 +69,15 @@ public class CommandViewAllies extends CommandBase { if(arguments.length > 0){ - player = getPlayer(server, sender, arguments[0]); + player = getPlayer(sender, arguments[0]); // Don't want to catch the exception here either, because there can be no other first argument. - if(player != sender && sender instanceof EntityPlayer && !WizardryUtilities.isPlayerOp((EntityPlayer)sender, server)){ - // Displays a chat message if a non-op tries to view another player's allies. - TextComponentTranslation TextComponentTranslation2 = new TextComponentTranslation("commands.wizardry:allies.permission"); - TextComponentTranslation2.getStyle().setColor(TextFormatting.RED); - player.addChatMessage(TextComponentTranslation2); + // The long-winded statement after the '!' checks if the player is op (multiplayer) or if cheats are enabled for them (singleplayer). + if(player != sender && player instanceof EntityPlayer && !MinecraftServer.getServer().getConfigurationManager().func_152596_g(((EntityPlayer)player).getGameProfile())){ + + ChatComponentTranslation chatcomponenttranslation2 = new ChatComponentTranslation("commands.allies.permission"); + chatcomponenttranslation2.getChatStyle().setColor(EnumChatFormatting.RED); + player.addChatMessage(chatcomponenttranslation2); return; } @@ -86,10 +88,10 @@ public class CommandViewAllies extends CommandBase { // player must not have been specified, meaning an exception should be thrown. if(player == null) throw new PlayerNotFoundException("You must specify which player you wish to perform this action on."); - if(WizardData.get(player) != null){ + if(ExtendedPlayer.get(player) != null){ String string = ""; - Set names = WizardData.get(player).allyNames; + HashSet names = ExtendedPlayer.get(player).allyNames; if(!names.isEmpty()){ for(String name : names){ @@ -98,13 +100,13 @@ public class CommandViewAllies extends CommandBase { // Cuts the last " ," off of the string. string = string.substring(0, string.length() - 2); }else{ - string = I18n.format("commands.wizardry:allies.none"); + string = StatCollector.translateToLocal("commands.allies.none"); } if(executeAsOtherPlayer){ - sender.addChatMessage(new TextComponentTranslation("commands.wizardry:allies.list_other", player.getName(), string)); + sender.addChatMessage(new ChatComponentTranslation("commands.allies.list_other", player.getCommandSenderName(), string)); }else{ - sender.addChatMessage(new TextComponentTranslation("commands.wizardry:allies.list", string)); + sender.addChatMessage(new ChatComponentTranslation("commands.allies.list", string)); } } } diff --git a/src/main/java/electroblob/wizardry/constants/Constants.java b/src/main/java/electroblob/wizardry/constants/Constants.java deleted file mode 100644 index 95b79b9c..00000000 --- a/src/main/java/electroblob/wizardry/constants/Constants.java +++ /dev/null @@ -1,44 +0,0 @@ -package electroblob.wizardry.constants; - -import electroblob.wizardry.WizardryEventHandler; - -/** Stores various global constants used in Wizardry. */ -public final class Constants { - - /** The amount of mana each magic crystal is worth */ - public static final int MANA_PER_CRYSTAL = 100; - /** The amount of mana each mana flask can hold */ - public static final int MANA_PER_FLASK = 700; - /** The maximum number of one type of wand upgrade which can be applied to a wand. */ - public static final int UPGRADE_STACK_LIMIT = 3; - /** The fraction by which cooldowns are reduced for each level of cooldown upgrade. */ - public static final float COOLDOWN_REDUCTION_PER_LEVEL = 0.15f; - /** The fraction by which maximum charge is increased for each level of storage upgrade. */ - public static final float STORAGE_INCREASE_PER_LEVEL = 0.15f; - /** The fraction by which damage is increased for each tier of matching wand. */ - public static final float DAMAGE_INCREASE_PER_TIER = 0.15f; - /** The fraction by which costs are reduced for each piece of matching armour. Note that changing this value will not - * affect continuous spells, since they are handled differently. */ - public static final float COST_REDUCTION_PER_ARMOUR = 0.2f; - /** The fraction by which spell duration is increased for each level of duration upgrade. */ - public static final float DURATION_INCREASE_PER_LEVEL = 0.25f; - /** The fraction by which spell range is increased for each level of range upgrade. */ - public static final float RANGE_INCREASE_PER_LEVEL = 0.25f; - /** The fraction by which spell blast radius is increased for each level of range upgrade. */ - public static final float BLAST_RADIUS_INCREASE_PER_LEVEL = 0.25f; - /** The fraction by which movement speed is reduced per level of frost effect. */ - public static final double FROST_SLOWNESS_PER_LEVEL = 0.5; - /** The fraction by which movement speed is reduced per level of decay effect. */ - public static final double DECAY_SLOWNESS_PER_LEVEL = 0.2; - /** The fraction by which dig speed is reduced per level of frost effect. */ - public static final float FROST_FATIGUE_PER_LEVEL = 0.45f; - /** The number of ticks between each mana increase for wands with the condenser upgrade. */ - public static final int CONDENSER_TICK_INTERVAL = 50; - /** The amount of mana given for a kill for each level of siphon upgrade. A random amount from 0 to this number - 1 - * is also added. See {@link WizardryEventHandler#onLivingDeathEvent} for more details. */ - public static final int SIPHON_MANA_PER_LEVEL = 3; - /** The number of ticks between the spawning of patches of decay when an entity has the decay effect. - * Note that decay won't spawn again if something is already standing in it. */ - public static final int DECAY_SPREAD_INTERVAL = 8; - -} diff --git a/src/main/java/electroblob/wizardry/constants/Element.java b/src/main/java/electroblob/wizardry/constants/Element.java deleted file mode 100644 index 48dd01f4..00000000 --- a/src/main/java/electroblob/wizardry/constants/Element.java +++ /dev/null @@ -1,67 +0,0 @@ -package electroblob.wizardry.constants; - -import electroblob.wizardry.Wizardry; -import net.minecraft.util.ResourceLocation; -import net.minecraft.util.text.ITextComponent; -import net.minecraft.util.text.Style; -import net.minecraft.util.text.TextComponentTranslation; -import net.minecraft.util.text.TextFormatting; -import net.minecraftforge.fml.relauncher.Side; -import net.minecraftforge.fml.relauncher.SideOnly; - -public enum Element { - - /** The 'default' element, with {@link electroblob.wizardry.spell.MagicMissile MagicMissile} being its only spell. */ - MAGIC(new Style().setColor(TextFormatting.GRAY), "simple", Wizardry.MODID), - FIRE(new Style().setColor(TextFormatting.DARK_RED), "fire", Wizardry.MODID), - ICE(new Style().setColor(TextFormatting.AQUA), "ice", Wizardry.MODID), - LIGHTNING(new Style().setColor(TextFormatting.DARK_AQUA), "lightning", Wizardry.MODID), - NECROMANCY(new Style().setColor(TextFormatting.DARK_PURPLE), "necromancy", Wizardry.MODID), - EARTH(new Style().setColor(TextFormatting.DARK_GREEN), "earth", Wizardry.MODID), - SORCERY(new Style().setColor(TextFormatting.GREEN), "sorcery", Wizardry.MODID), - HEALING(new Style().setColor(TextFormatting.YELLOW), "healing", Wizardry.MODID); - - /** Display colour for this element */ - private final Style colour; - /** Unlocalised name for this element */ - private final String unlocalisedName; - /** The {@link ResourceLocation} for this element's 8x8 icon (displayed in the arcane workbench GUI) */ - private final ResourceLocation icon; - - private Element(Style colour, String name, String modid){ - this.colour = colour; - this.unlocalisedName = name; - this.icon = new ResourceLocation(modid, "textures/gui/element_icon_" + unlocalisedName + ".png"); - } - - /** Returns the translated display name of this element, without formatting. */ - @SideOnly(Side.CLIENT) - public String getDisplayName(){ - return net.minecraft.client.resources.I18n.format("element." + getUnlocalisedName()); - } - - /** Returns the {@link Style} object representing the colour of this element. */ - public Style getColour(){ - return colour; - } - - /** Returns the string formatting code which corresponds to the colour of this element. */ - public String getFormattingCode(){ - return colour.getFormattingCode(); - } - - /** Returns the translated display name for wizards of this element, shown in the trading GUI. */ - public ITextComponent getWizardName(){ - return new TextComponentTranslation("element." + getUnlocalisedName() + ".wizard"); - } - - /** Returns this element's unlocalised name. */ - public String getUnlocalisedName(){ - return unlocalisedName; - } - - /** Returns the {@link ResourceLocation} for this element's 8x8 icon (displayed in the arcane workbench GUI). */ - public ResourceLocation getIcon(){ - return icon; - } -} diff --git a/src/main/java/electroblob/wizardry/constants/SpellType.java b/src/main/java/electroblob/wizardry/constants/SpellType.java deleted file mode 100644 index 807d878c..00000000 --- a/src/main/java/electroblob/wizardry/constants/SpellType.java +++ /dev/null @@ -1,23 +0,0 @@ -package electroblob.wizardry.constants; - -import net.minecraftforge.fml.relauncher.Side; -import net.minecraftforge.fml.relauncher.SideOnly; - -public enum SpellType { - - ATTACK("attack"), - DEFENCE("defence"), - UTILITY("utility"), - MINION("minion"); - - private final String unlocalisedName; - - SpellType(String name){ - this.unlocalisedName = name; - } - - @SideOnly(Side.CLIENT) - public String getDisplayName(){ - return net.minecraft.client.resources.I18n.format("spelltype." + unlocalisedName); - } -} \ No newline at end of file diff --git a/src/main/java/electroblob/wizardry/constants/Tier.java b/src/main/java/electroblob/wizardry/constants/Tier.java deleted file mode 100644 index f989b82b..00000000 --- a/src/main/java/electroblob/wizardry/constants/Tier.java +++ /dev/null @@ -1,81 +0,0 @@ -package electroblob.wizardry.constants; - -import java.util.Random; - -import net.minecraft.util.text.Style; -import net.minecraft.util.text.TextFormatting; -import net.minecraftforge.fml.relauncher.Side; -import net.minecraftforge.fml.relauncher.SideOnly; - -public enum Tier { - - BASIC(700, 3, 12, new Style().setColor(TextFormatting.WHITE), "basic"), - APPRENTICE(1000, 4, 5, new Style().setColor(TextFormatting.AQUA), "apprentice"), - ADVANCED(1500, 5, 2, new Style().setColor(TextFormatting.DARK_BLUE), "advanced"), - MASTER(2500, 6, 1, new Style().setColor(TextFormatting.DARK_PURPLE), "master"); - - /** Maximum mana a wand of this tier can store. */ - public final int maxCharge; - /** Just an ordinal. Shouldn't really be needed but no point changing it now. */ - public final int level; - /** The maximum number of upgrades that can be applied to a wand of this tier. */ - public final int upgradeLimit; - /** The weight given to this tier in the standard weighting. */ - public final int weight; - /** The colour of text associated with this tier. */ - // Changed to a Style object for consistency. - private final Style colour; - - private final String unlocalisedName; - - private Tier(int maxCharge, int upgradeLimit, int weight, Style colour, String name){ - this.maxCharge = maxCharge; - this.level = ordinal(); - this.upgradeLimit = upgradeLimit; - this.weight = weight; - this.colour = colour; - this.unlocalisedName = name; - } - - @SideOnly(Side.CLIENT) - public String getDisplayName() { - return net.minecraft.client.resources.I18n.format("tier." + unlocalisedName); - } - - @SideOnly(Side.CLIENT) - public String getDisplayNameWithFormatting() { - return this.getFormattingCode() + net.minecraft.client.resources.I18n.format("tier." + unlocalisedName); - } - - public String getUnlocalisedName(){ - return unlocalisedName; - } - - public String getFormattingCode(){ - return colour.getFormattingCode(); - } - - /** Returns a random tier based on the standard weighting. Currently, the standard weighting is: Basic (Novice) 60%, - * Apprentice 25%, Advanced 10%, Master 5%. If an array of tiers is given, it picks a tier from the array, with the - * same relative weights for each. For example, if the array contains APPRENTICE and MASTER, then the weighting will - * become: Apprentice 83.3%, Master 16.7%. */ - public static Tier getWeightedRandomTier(Random random, Tier... tiers){ - - if(tiers.length == 0) tiers = values(); - - int totalWeight = 0; - - for(Tier tier : tiers) totalWeight += tier.weight; - - int randomiser = random.nextInt(totalWeight); - int cumulativeWeight = 0; - - for(Tier tier : tiers){ - cumulativeWeight += tier.weight; - if(randomiser < cumulativeWeight) return tier; - } - - // This will never happen, but it might as well be a sensible result. - return tiers[tiers.length-1]; - } -} diff --git a/src/main/java/electroblob/wizardry/enchantment/EnchantmentMagicSword.java b/src/main/java/electroblob/wizardry/enchantment/EnchantmentMagicSword.java index 0aca94ba..a0ece85a 100644 --- a/src/main/java/electroblob/wizardry/enchantment/EnchantmentMagicSword.java +++ b/src/main/java/electroblob/wizardry/enchantment/EnchantmentMagicSword.java @@ -1,17 +1,18 @@ package electroblob.wizardry.enchantment; -import net.minecraft.enchantment.Enchantment; import net.minecraft.enchantment.EnchantmentDamage; +import net.minecraft.entity.Entity; +import net.minecraft.entity.EntityLivingBase; import net.minecraft.entity.EnumCreatureAttribute; -import net.minecraft.inventory.EntityEquipmentSlot; +import net.minecraft.item.ItemAxe; import net.minecraft.item.ItemStack; // This one is for imbued swords. The only reason this is separate is that the way vanilla is written allows me to hook // into the damage increase for melee weapons, meaning I don't have to use events - always handy! -public class EnchantmentMagicSword extends EnchantmentDamage implements Imbuement { +public class EnchantmentMagicSword extends EnchantmentDamage { - public EnchantmentMagicSword() { - super(Enchantment.Rarity.COMMON, 0, EntityEquipmentSlot.MAINHAND); + public EnchantmentMagicSword(int id) { + super(id, 0, 0); // Setting this to null stops the book appearing in the creative inventory this.type = null; } @@ -35,15 +36,17 @@ public class EnchantmentMagicSword extends EnchantmentDamage implements Imbuemen // Returns the number by which the damage should be increased (or something) @Override - public float calcDamageByCreature(int p_152376_1_, EnumCreatureAttribute p_152376_2_) + public float func_152376_a(int p_152376_1_, EnumCreatureAttribute p_152376_2_) { return (float)p_152376_1_ * 1.25F; } - @Override + /** + * Return the name of key in translation table of this enchantment. + */ public String getName() { - return "enchantment." + this.getRegistryName(); + return "enchantment.magic_sword"; } @Override diff --git a/src/main/java/electroblob/wizardry/enchantment/EnchantmentTimed.java b/src/main/java/electroblob/wizardry/enchantment/EnchantmentTimed.java index d30252d1..3041d816 100644 --- a/src/main/java/electroblob/wizardry/enchantment/EnchantmentTimed.java +++ b/src/main/java/electroblob/wizardry/enchantment/EnchantmentTimed.java @@ -1,27 +1,21 @@ package electroblob.wizardry.enchantment; import net.minecraft.enchantment.Enchantment; -import net.minecraft.inventory.EntityEquipmentSlot; +import net.minecraft.enchantment.EnumEnchantmentType; import net.minecraft.item.ItemStack; // This one is for everything other than imbued swords. -public class EnchantmentTimed extends Enchantment implements Imbuement { - - public EnchantmentTimed() { +public class EnchantmentTimed extends Enchantment { + + public EnchantmentTimed(int id) { // Setting enchantment type to null stops the book appearing in the creative inventory - super(Enchantment.Rarity.COMMON, null, new EntityEquipmentSlot[]{EntityEquipmentSlot.MAINHAND}); + super(id, 0, null); } @Override public boolean canApply(ItemStack p_92089_1_){ return false; } - - @Override - public String getName() - { - return "enchantment." + this.getRegistryName(); - } /** * Returns the maximum level that the enchantment can have. diff --git a/src/main/java/electroblob/wizardry/enchantment/Imbuement.java b/src/main/java/electroblob/wizardry/enchantment/Imbuement.java deleted file mode 100644 index ac443d9a..00000000 --- a/src/main/java/electroblob/wizardry/enchantment/Imbuement.java +++ /dev/null @@ -1,131 +0,0 @@ -package electroblob.wizardry.enchantment; - -import java.util.Map; - -import electroblob.wizardry.Wizardry; -import electroblob.wizardry.registry.WizardryEnchantments; -import electroblob.wizardry.spell.FreezingWeapon; -import net.minecraft.enchantment.Enchantment; -import net.minecraft.enchantment.EnchantmentHelper; -import net.minecraft.entity.EntityLivingBase; -import net.minecraft.entity.item.EntityItem; -import net.minecraft.entity.projectile.EntityArrow; -import net.minecraft.inventory.ContainerChest; -import net.minecraft.inventory.Slot; -import net.minecraft.item.ItemBow; -import net.minecraft.item.ItemEnchantedBook; -import net.minecraft.item.ItemStack; -import net.minecraftforge.event.entity.EntityJoinWorldEvent; -import net.minecraftforge.event.entity.item.ItemTossEvent; -import net.minecraftforge.event.entity.living.LivingDropsEvent; -import net.minecraftforge.event.entity.player.PlayerContainerEvent; -import net.minecraftforge.fml.common.Mod; -import net.minecraftforge.fml.common.eventhandler.SubscribeEvent; - -/** Interface for temporary enchantments that last for a certain duration ('imbuements'). This interface allows - * {@link EnchantmentMagicSword} and {@link EnchantmentTimed} to both be treated as instances of a - * single type, rather than having to deal with each of them separately, - * which would be inefficient and cumbersome (the former of those classes cannot extend the latter because they both - * need to extend different subclasses of {@link net.minecraft.enchantment.Enchantment}). - * @since Wizardry 1.2 */ -@Mod.EventBusSubscriber -public interface Imbuement { - - // This interface has no abstract methods, these handlers are just here for the sake of keeping things organised. - - @SubscribeEvent - public static void onLivingDropsEvent(LivingDropsEvent event){ - // Instantly disenchants an imbued weapon if it is dropped when the player dies. - for(EntityItem item : event.getDrops()){ - removeImbuements(item.getEntityItem()); - } - } - - @SubscribeEvent - public static void onItemTossEvent(ItemTossEvent event){ - // Instantly disenchants an imbued weapon if it is thrown on the ground. - removeImbuements(event.getEntityItem().getEntityItem()); - } - - /** Removes all imbuements from the given itemstack. */ - static void removeImbuements(ItemStack stack){ - if(stack.isItemEnchanted()){ - // No need to check what enchantments the item has, since remove() does nothing if the element does not exist. - Map enchantments = EnchantmentHelper.getEnchantments(stack); - // Removes the magic weapon enchantments from the enchantment map - enchantments.entrySet().removeIf(entry -> entry.getKey() instanceof Imbuement); - // Applies the new enchantment map to the item - EnchantmentHelper.setEnchantments(enchantments, stack); - } - } - - @SubscribeEvent - public static void onPlayerOpenContainerEvent(PlayerContainerEvent event){ - // Brute-force fix to stop enchanted books in dungeon chests from having imbuements on them. - if(event.getContainer() instanceof ContainerChest){ - // Still not sure if it's better to set stacks in slots or modify the itemstack list directly, but I would - // imagine it's the former. - for(Slot slot : event.getContainer().inventorySlots){ - if(slot.getStack() != null && slot.getStack().getItem() instanceof ItemEnchantedBook){ - // We don't care about the level of the enchantments - Map enchantments = EnchantmentHelper.getEnchantments(slot.getStack()); - // Removes all imbuements - if(enchantments.keySet().removeIf(e -> e instanceof Imbuement)){ - // If any imbuements were removed, replaces the enchantments on the book with the new ones, or - // deletes the book entirely if there are none left. - if(enchantments.isEmpty()){ - slot.putStack(null); // NOTE: Will need changing in 1.11 - Wizardry.logger.info("Deleted enchanted book with illegal enchantments"); - }else{ - EnchantmentHelper.setEnchantments(enchantments, slot.getStack()); - Wizardry.logger.info("Removed illegal enchantments from enchanted book"); - } - } - } - } - } - } - - @SubscribeEvent - public static void onEntityJoinWorld(EntityJoinWorldEvent event){ - // Rather long-winded (but necessary) way of getting an arrow just after it has been fired, checking if the bow - // that fired it has the imbuement enchantment, and applying extra damage accordingly. - if(!event.getEntity().worldObj.isRemote && event.getEntity() instanceof EntityArrow){ - - EntityArrow arrow = (EntityArrow)event.getEntity(); - - if(arrow.shootingEntity instanceof EntityLivingBase){ - - EntityLivingBase archer = (EntityLivingBase)arrow.shootingEntity; - - ItemStack bow = archer.getHeldItemMainhand(); - - if(bow == null || !(bow.getItem() instanceof ItemBow)){ - bow = archer.getHeldItemOffhand(); - if(bow == null || !(bow.getItem() instanceof ItemBow)) return; - } - - // Taken directly from ItemBow, so it works exactly the same as the power enchantment. - int level = EnchantmentHelper.getEnchantmentLevel(WizardryEnchantments.magic_bow, bow); - - if(level > 0){ - arrow.setDamage(arrow.getDamage() + (double)level * 0.5D + 0.5D); - } - - if(EnchantmentHelper.getEnchantmentLevel(WizardryEnchantments.flaming_weapon, bow) > 0){ - // Again, this is exactly what happens in ItemBow (flame is flame; level does nothing). - arrow.setFire(100); - } - - level = EnchantmentHelper.getEnchantmentLevel(WizardryEnchantments.freezing_weapon, bow); - - if(level > 0){ - if(arrow.getEntityData() != null){ - arrow.getEntityData().setInteger(FreezingWeapon.FREEZING_ARROW_NBT_KEY, level); - } - } - } - } - } - -} diff --git a/src/main/java/electroblob/wizardry/entity/EntityArc.java b/src/main/java/electroblob/wizardry/entity/EntityArc.java index dc68f0eb..a1f2d0fa 100644 --- a/src/main/java/electroblob/wizardry/entity/EntityArc.java +++ b/src/main/java/electroblob/wizardry/entity/EntityArc.java @@ -1,10 +1,11 @@ package electroblob.wizardry.entity; +import cpw.mods.fml.common.registry.IEntityAdditionalSpawnData; import io.netty.buffer.ByteBuf; import net.minecraft.entity.Entity; import net.minecraft.nbt.NBTTagCompound; +import net.minecraft.util.Vec3; import net.minecraft.world.World; -import net.minecraftforge.fml.common.registry.IEntityAdditionalSpawnData; public class EntityArc extends Entity implements IEntityAdditionalSpawnData { @@ -29,12 +30,41 @@ public class EntityArc extends Entity implements IEntityAdditionalSpawnData { this.z2 = z2; this.setPosition(x2, y2, z2); } + + @Deprecated + public void setOffset(double x, double z){ + this.offsetX = x; + this.offsetZ = z; + } @Override public void onUpdate(){ if(this.ticksExisted >= lifetime){ this.setDead(); } + /* Deprecated in favour of IEntityAdditionalSpawnData + if(!this.worldObj.isRemote){ + // Packet building + ByteArrayOutputStream bos = new ByteArrayOutputStream(32); + DataOutputStream outputStream = new DataOutputStream(bos); + try { + outputStream.writeInt(3); //This is the event id. 3 stands for arc render. + outputStream.writeInt(this.entityId); + outputStream.writeDouble(this.x1); + outputStream.writeDouble(this.y1); + outputStream.writeDouble(this.z1); + } catch (Exception ex) { + ex.printStackTrace(); + } + + Packet250CustomPayload packet = new Packet250CustomPayload(); + packet.channel = "WizardryMod"; + packet.data = bos.toByteArray(); + packet.length = bos.size(); + + PacketDispatcher.sendPacketToAllPlayers(packet); + } + */ } protected void entityInit() @@ -48,14 +78,17 @@ public class EntityArc extends Entity implements IEntityAdditionalSpawnData { @Override protected void writeEntityToNBT(NBTTagCompound nbttagcompound) { - // Nothing needed here; arc is merely a graphic effect that only exists for a few ticks; as such there is no need to save it. - } - - @Override - public boolean isInRangeToRenderDist(double distance) { - return true; + //Nothing needed here; arc is merely a graphic effect that only exists for a few ticks; as such there is no need to save it. } + /** + * Checks using a Vec3d to determine if this entity is within range of that vector to be rendered. Args: vec3D + */ + public boolean isInRangeToRenderVec3D(Vec3 par1Vec3) + { + return true; + } + @Override public void writeSpawnData(ByteBuf data) { data.writeDouble(this.x1); diff --git a/src/main/java/electroblob/wizardry/entity/EntityFallingGrass.java b/src/main/java/electroblob/wizardry/entity/EntityFallingGrass.java new file mode 100644 index 00000000..443b11e8 --- /dev/null +++ b/src/main/java/electroblob/wizardry/entity/EntityFallingGrass.java @@ -0,0 +1,93 @@ +package electroblob.wizardry.entity; + +import java.util.Iterator; + +import net.minecraft.block.Block; +import net.minecraft.block.BlockFalling; +import net.minecraft.block.ITileEntityProvider; +import net.minecraft.entity.Entity; +import net.minecraft.entity.item.EntityFallingBlock; +import net.minecraft.init.Blocks; +import net.minecraft.item.ItemStack; +import net.minecraft.nbt.NBTBase; +import net.minecraft.nbt.NBTTagCompound; +import net.minecraft.tileentity.TileEntity; +import net.minecraft.util.MathHelper; +import net.minecraft.world.World; + +public class EntityFallingGrass extends EntityFallingBlock { + + public EntityFallingGrass(World world) { + super(world); + } + + public EntityFallingGrass(World world, double x, double y, double z, + Block block) { + super(world, x, y, z, block); + } + + public EntityFallingGrass(World world, double x, double y, double z, + Block block, int metadata) { + super(world, x, y, z, block, metadata); + } + + @Override + public void onUpdate(){ + + this.prevPosX = this.posX; + this.prevPosY = this.posY; + this.prevPosZ = this.posZ; + ++this.field_145812_b; + this.motionY -= 0.03999999910593033D; + this.moveEntity(this.motionX, this.motionY, this.motionZ); + this.motionX *= 0.9800000190734863D; + this.motionY *= 0.9800000190734863D; + this.motionZ *= 0.9800000190734863D; + + if (!this.worldObj.isRemote) + { + int i = MathHelper.floor_double(this.posX); + int j = MathHelper.floor_double(this.posY); + int k = MathHelper.floor_double(this.posZ); + + if (this.field_145812_b == 1) + { + if (this.worldObj.getBlock(i, j, k) != Blocks.grass) + { + this.setDead(); + return; + } + + this.worldObj.setBlockToAir(i, j, k); + } + + if (this.onGround) + { + this.motionX *= 0.699999988079071D; + this.motionZ *= 0.699999988079071D; + this.motionY *= -0.5D; + + if (this.worldObj.getBlock(i, j, k) != Blocks.piston_extension) + { + this.setDead(); + + if (this.worldObj.canPlaceEntityOnSide(Blocks.grass, i, j, k, true, 1, (Entity)null, (ItemStack)null) && !BlockFalling.func_149831_e(this.worldObj, i, j - 1, k) && this.worldObj.setBlock(i, j, k, Blocks.grass, this.field_145814_a, 3)) + { + // The stuff in here only related to tile entities, so was removed, but since the enclosing if statement + // involves setting the block it has to stay (and I'm too lazy to rearrange it). + } + } + } + else if (this.field_145812_b > 100 && !this.worldObj.isRemote && (j < 1 || j > 256) || this.field_145812_b > 600) + { + if (this.field_145813_c) + { + this.entityDropItem(new ItemStack(Blocks.grass, 1, Blocks.grass.damageDropped(this.field_145814_a)), 0.0F); + } + + this.setDead(); + } + } + + } +} diff --git a/src/main/java/electroblob/wizardry/entity/EntityMagicSlime.java b/src/main/java/electroblob/wizardry/entity/EntityMagicSlime.java new file mode 100644 index 00000000..d278e525 --- /dev/null +++ b/src/main/java/electroblob/wizardry/entity/EntityMagicSlime.java @@ -0,0 +1,331 @@ +package electroblob.wizardry.entity; + +import cpw.mods.fml.common.registry.IEntityAdditionalSpawnData; +import io.netty.buffer.ByteBuf; +import net.minecraft.entity.EntityLiving; +import net.minecraft.entity.EntityLivingBase; +import net.minecraft.entity.SharedMonsterAttributes; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.nbt.NBTTagCompound; +import net.minecraft.potion.Potion; +import net.minecraft.potion.PotionEffect; +import net.minecraft.util.DamageSource; +import net.minecraft.util.MathHelper; +import net.minecraft.world.World; + +public class EntityMagicSlime extends EntityLiving implements IEntityAdditionalSpawnData +{ + public float squishAmount; + public float squishFactor; + public float prevSquishFactor; + + /** the time between each jump of the slime */ + private int slimeJumpDelay; + + private int lifetime; + + public EntityMagicSlime(World par1World) + { + super(par1World); + this.yOffset = 0.0F; + this.slimeJumpDelay = this.rand.nextInt(20) + 10; + this.setSlimeSize(2); + } + + public EntityMagicSlime(World world, int lifetime){ + this(world); + this.lifetime = lifetime; + } + + protected void entityInit() + { + super.entityInit(); + this.dataWatcher.addObject(16, new Byte((byte)1)); + } + + protected void setSlimeSize(int par1) + { + this.dataWatcher.updateObject(16, new Byte((byte)par1)); + this.setSize(0.6F * (float)par1, 0.6F * (float)par1); + this.setPosition(this.posX, this.posY, this.posZ); + this.getEntityAttribute(SharedMonsterAttributes.maxHealth).setBaseValue((double)(par1 * par1)); + this.setHealth(this.getMaxHealth()); + this.experienceValue = par1; + } + + /** + * Returns the size of the slime. + */ + public int getSlimeSize() + { + return this.dataWatcher.getWatchableObjectByte(16); + } + + /** + * (abstract) Protected helper method to write subclass entity data to NBT. + */ + public void writeEntityToNBT(NBTTagCompound par1NBTTagCompound) + { + super.writeEntityToNBT(par1NBTTagCompound); + par1NBTTagCompound.setInteger("Size", this.getSlimeSize() - 1); + par1NBTTagCompound.setInteger("lifetime", lifetime); + } + + /** + * (abstract) Protected helper method to read subclass entity data from NBT. + */ + public void readEntityFromNBT(NBTTagCompound par1NBTTagCompound) + { + super.readEntityFromNBT(par1NBTTagCompound); + this.setSlimeSize(par1NBTTagCompound.getInteger("Size") + 1); + lifetime = par1NBTTagCompound.getInteger("lifetime"); + } + + /** + * Returns the name of a particle effect that may be randomly created by EntitySlime.onUpdate() + */ + protected String getSlimeParticle() + { + return "slime"; + } + + /** + * Returns the name of the sound played when the slime jumps. + */ + protected String getJumpSound() + { + return "mob.slime." + (this.getSlimeSize() > 1 ? "big" : "small"); + } + + /** + * Called to update the entity's position/logic. + */ + public void onUpdate() + { + if(this.ticksExisted > lifetime){ + this.setDead(); + for(int i=0; i<30; i++){ + double x = this.posX - 0.5 + rand.nextDouble(); + double y = this.posY - 0.5 + rand.nextDouble(); + double z = this.posZ - 0.5 + rand.nextDouble(); + this.worldObj.spawnParticle("slime", x, y, z, (x - this.posX)*2, (y - this.posY)*2, (z - this.posZ)*2); + } + this.playSound("mob.slime.attack", 2.5f, 0.6f); + this.playSound("fireworks.blast_far", 1.0f, 0.5f); + } + + // Damages and slows the slime's victim or makes the slime explode if the victim is dead. + if(this.ridingEntity != null && this.ridingEntity instanceof EntityLivingBase && ((EntityLivingBase)this.ridingEntity).getHealth() > 0){ + if(this.ticksExisted % 16 == 1){ + this.ridingEntity.attackEntityFrom(DamageSource.magic, 1); + ((EntityLivingBase)this.ridingEntity).addPotionEffect(new PotionEffect(Potion.moveSlowdown.id, 20, 2)); + this.playSound("mob.slime.attack", 1.0f, 1.0f); + this.squishAmount = 0.5F; + } + }else{ + this.setDead(); + for(int i=0; i<30; i++){ + double x = this.posX - 0.5 + rand.nextDouble(); + double y = this.posY - 0.5 + rand.nextDouble(); + double z = this.posZ - 0.5 + rand.nextDouble(); + this.worldObj.spawnParticle("slime", x, y, z, (x - this.posX)*2, (y - this.posY)*2, (z - this.posZ)*2); + } + this.playSound("mob.slime.attack", 2.5f, 0.6f); + this.playSound("fireworks.blast_far", 1.0f, 0.5f); + } + /* No despawn on peaceful. + if (!this.worldObj.isRemote && this.worldObj.difficultySetting == EnumDifficulty.PEACEFUL && this.getSlimeSize() > 0) + { + this.isDead = true; + } + */ + this.squishFactor += (this.squishAmount - this.squishFactor) * 0.5F; + this.prevSquishFactor = this.squishFactor; + boolean flag = this.onGround; + super.onUpdate(); + int i; + + if (this.onGround && !flag) + { + i = this.getSlimeSize(); + + for (int j = 0; j < i * 8; ++j) + { + float f = this.rand.nextFloat() * (float)Math.PI * 2.0F; + float f1 = this.rand.nextFloat() * 0.5F + 0.5F; + float f2 = MathHelper.sin(f) * (float)i * 0.5F * f1; + float f3 = MathHelper.cos(f) * (float)i * 0.5F * f1; + this.worldObj.spawnParticle(this.getSlimeParticle(), this.posX + (double)f2, this.boundingBox.minY, this.posZ + (double)f3, 0.0D, 0.0D, 0.0D); + } + + if (this.makesSoundOnLand()) + { + this.playSound(this.getJumpSound(), this.getSoundVolume(), ((this.rand.nextFloat() - this.rand.nextFloat()) * 0.2F + 1.0F) / 0.8F); + } + + this.squishAmount = -0.5F; + } + else if (!this.onGround && flag) + { + this.squishAmount = 1.0F; + } + + this.alterSquishAmount(); + + if (this.worldObj.isRemote) + { + i = this.getSlimeSize(); + this.setSize(0.6F * (float)i, 0.6F * (float)i); + } + } + + protected void updateEntityActionState() + { + this.despawnEntity(); + EntityPlayer entityplayer = this.worldObj.getClosestVulnerablePlayerToEntity(this, 16.0D); + + if (entityplayer != null) + { + this.faceEntity(entityplayer, 10.0F, 20.0F); + } + + if (this.onGround && this.slimeJumpDelay-- <= 0) + { + this.slimeJumpDelay = this.getJumpDelay(); + + if (entityplayer != null) + { + this.slimeJumpDelay /= 3; + } + + this.isJumping = true; + + if (this.makesSoundOnJump()) + { + this.playSound(this.getJumpSound(), this.getSoundVolume(), ((this.rand.nextFloat() - this.rand.nextFloat()) * 0.2F + 1.0F) * 0.8F); + } + + this.moveStrafing = 1.0F - this.rand.nextFloat() * 2.0F; + this.moveForward = (float)(1 * this.getSlimeSize()); + } + else + { + this.isJumping = false; + + if (this.onGround) + { + this.moveStrafing = this.moveForward = 0.0F; + } + } + } + + protected void alterSquishAmount() + { + this.squishAmount *= 0.6F; + } + + /** + * Gets the amount of time the slime needs to wait between jumps. + */ + protected int getJumpDelay() + { + return this.rand.nextInt(20) + 10; + } + + protected EntityMagicSlime createInstance() + { + return new EntityMagicSlime(this.worldObj); + } + + /** + * Will get destroyed next tick. + */ + public void setDead() + { + int i = this.getSlimeSize(); + + if (!this.worldObj.isRemote && i > 1 && this.getHealth() <= 0.0F) + { + int j = 2 + this.rand.nextInt(3); + + for (int k = 0; k < j; ++k) + { + float f = ((float)(k % 2) - 0.5F) * (float)i / 4.0F; + float f1 = ((float)(k / 2) - 0.5F) * (float)i / 4.0F; + EntityMagicSlime entityslime = this.createInstance(); + entityslime.setSlimeSize(i / 2); + entityslime.setLocationAndAngles(this.posX + (double)f, this.posY + 0.5D, this.posZ + (double)f1, this.rand.nextFloat() * 360.0F, 0.0F); + this.worldObj.spawnEntityInWorld(entityslime); + } + } + + super.setDead(); + } + + /** + * Returns the sound this mob makes when it is hurt. + */ + protected String getHurtSound() + { + return "mob.slime." + (this.getSlimeSize() > 1 ? "big" : "small"); + } + + /** + * Returns the sound this mob makes on death. + */ + protected String getDeathSound() + { + return "mob.slime." + (this.getSlimeSize() > 1 ? "big" : "small"); + } + + /** + * Returns the item ID for the item the mob drops on death. + */ + protected int getDropItemId() + { + return -1; + } + + /** + * Returns the volume for the sounds this mob makes. + */ + protected float getSoundVolume() + { + return 0.4F * (float)this.getSlimeSize(); + } + + /** + * The speed it takes to move the entityliving's rotationPitch through the faceEntity method. This is only currently + * use in wolves. + */ + public int getVerticalFaceSpeed() + { + return 0; + } + + /** + * Returns true if the slime makes a sound when it jumps (based upon the slime's size) + */ + protected boolean makesSoundOnJump() + { + return this.getSlimeSize() > 0; + } + + /** + * Returns true if the slime makes a sound when it lands after a jump (based upon the slime's size) + */ + protected boolean makesSoundOnLand() + { + return this.getSlimeSize() > 2; + } + + @Override + public void writeSpawnData(ByteBuf data) { + data.writeInt(lifetime); + } + + @Override + public void readSpawnData(ByteBuf data) { + lifetime = data.readInt(); + } +} diff --git a/src/main/java/electroblob/wizardry/entity/EntityMeteor.java b/src/main/java/electroblob/wizardry/entity/EntityMeteor.java index fce647ce..d48e1512 100644 --- a/src/main/java/electroblob/wizardry/entity/EntityMeteor.java +++ b/src/main/java/electroblob/wizardry/entity/EntityMeteor.java @@ -1,51 +1,80 @@ package electroblob.wizardry.entity; +import cpw.mods.fml.relauncher.Side; +import cpw.mods.fml.relauncher.SideOnly; import electroblob.wizardry.Wizardry; -import electroblob.wizardry.registry.WizardryBlocks; -import electroblob.wizardry.registry.WizardrySounds; -import electroblob.wizardry.util.WizardryUtilities; -import net.minecraft.block.state.IBlockState; +import electroblob.wizardry.WizardryUtilities; +import net.minecraft.block.Block; import net.minecraft.entity.item.EntityFallingBlock; import net.minecraft.init.Blocks; import net.minecraft.nbt.NBTTagCompound; -import net.minecraft.util.math.BlockPos; +import net.minecraft.util.MathHelper; +import net.minecraft.util.Vec3; import net.minecraft.world.World; -import net.minecraftforge.fml.relauncher.Side; -import net.minecraftforge.fml.relauncher.SideOnly; -public class EntityMeteor extends EntityFallingBlock { +public class EntityMeteor extends EntityFallingBlock +{ + /** How long the block has been falling for. */ + public int fallTime; /** The entity blast multiplier. Only some projectiles cause a blast, which is why this isn't in EntityMagicProjectile. */ public float blastMultiplier; - public EntityMeteor(World world){ - super(world); - // Superconstructor doesn't call this. - this.setSize(0.98F, 0.98F); + public EntityMeteor(World par1World) + { + super(par1World); } - public EntityMeteor(World world, double x, double y, double z, float blastMultiplier){ - super(world, x, y, z, WizardryBlocks.meteor.getDefaultState()); + public EntityMeteor(World par1World, double par2, double par4, double par6, Block block, float blastMultiplier) + { + super(par1World, par2, par4, par6, block); + this.setSize(0.98F, 0.98F); + this.yOffset = this.height / 2.0F; + this.setPosition(par2, par4, par6); + this.motionX = 0.0D; this.motionY = -1.0D; + this.motionZ = 0.0D; + this.prevPosX = par2; + this.prevPosY = par4; + this.prevPosZ = par6; this.setFire(200); this.blastMultiplier = blastMultiplier; } - - @Override - public double getYOffset() { - return this.height / 2.0F; + + /** + * returns if this entity triggers Block.onEntityWalking on the blocks they walk on. used for spiders and wolves to + * prevent them from trampling crops + */ + protected boolean canTriggerWalking() + { + return false; } - @Override + protected void entityInit() {} + + /** + * Returns true if other Entities should be prevented from moving through this Entity. + */ + public boolean canBeCollidedWith() + { + return !this.isDead; + } + + /** + * Called to update the entity's position/logic. + */ public void onUpdate(){ if(this.ticksExisted % 16 == 1 && worldObj.isRemote){ - Wizardry.proxy.playMovingSound(this, WizardrySounds.SPELL_LOOP_FIRE, 3.0f, 1.0f, false); + Wizardry.proxy.playMovingSound(this, "wizardry:flameray", 3.0f, 1.0f, false); } - - // You'd think the best way to do this would be to call super and do all the exploding stuff in fall() instead. - // However, for some reason, fallTile is null on the client side, causing an NPE in super.onUpdate() - + /* + if(worldObj.isRemote){ + System.out.println("Client: " + this.posX + ", " + this.posY + ", " + this.posZ); + }else{ + System.out.println("Server: " + this.posX + ", " + this.posY + ", " + this.posZ); + } + */ this.prevPosX = this.posX; this.prevPosY = this.posY; this.prevPosZ = this.posZ; @@ -56,22 +85,26 @@ public class EntityMeteor extends EntityFallingBlock { this.motionY *= 0.9800000190734863D; this.motionZ *= 0.9800000190734863D; - if(!this.worldObj.isRemote){ - - if(this.onGround){ + if (!this.worldObj.isRemote) + { + int i = MathHelper.floor_double(this.posX); + int j = MathHelper.floor_double(this.posY); + int k = MathHelper.floor_double(this.posZ); + if (this.onGround) + { this.motionX *= 0.699999988079071D; this.motionZ *= 0.699999988079071D; this.motionY *= -0.5D; this.worldObj.createExplosion(this, this.posX, this.posY, this.posZ, 2.0f*blastMultiplier, true); for(int i1=-3; i1<4; i1++){ for(int j1=-3; j1<4; j1++){ - int y = WizardryUtilities.getNearestFloorLevelB(this.worldObj, new BlockPos(this.posX + i1, this.posY, this.posZ + j1), 7); + int y = WizardryUtilities.getNearestFloorLevelB(this.worldObj, (int)this.posX + i1, (int)this.posY, (int)this.posZ + j1, 7); //System.out.println(y); double dist = this.getDistance((int)this.posX + i1, y, (int)this.posZ + j1); // Randomised with weighting so that the nearer the block the more likely it is to be set on fire. if(y != -1 && rand.nextInt((int)dist*2 + 1) < 3 && dist < 4){ - this.worldObj.setBlockState(new BlockPos(this.posX + i1, y, this.posZ + j1), Blocks.FIRE.getDefaultState()); + this.worldObj.setBlock((int)this.posX + i1, y, (int)this.posZ + j1, Blocks.fire); } } } @@ -80,37 +113,77 @@ public class EntityMeteor extends EntityFallingBlock { } } - @Override - public void fall(float distance, float damageMultiplier){ - // Don't need to do anything here, the meteor should have already exploded. + /** + * Called when the mob is falling. Calculates and applies fall damage. + */ + protected void fall(float par1) + { + } + /** + * (abstract) Protected helper method to write subclass entity data to NBT. + */ + /* + protected void writeEntityToNBT(NBTTagCompound par1NBTTagCompound) + { + par1NBTTagCompound.setByte("Tile", (byte)this.blockID); + par1NBTTagCompound.setInteger("TileID", this.blockID); + par1NBTTagCompound.setByte("Time", (byte)this.fallTime); + } + */ + /** + * (abstract) Protected helper method to read subclass entity data from NBT. + */ + /* + protected void readEntityFromNBT(NBTTagCompound par1NBTTagCompound) + { + if (par1NBTTagCompound.hasKey("TileID")) + { + this.blockID = par1NBTTagCompound.getInteger("TileID"); + } + else + { + this.blockID = par1NBTTagCompound.getByte("Tile") & 255; + } + + this.fallTime = par1NBTTagCompound.getByte("Time") & 255; + + if (this.blockID == 0) + { + this.blockID = Block.stone.blockID; + } + } +*/ @SideOnly(Side.CLIENT) - @Override - public boolean canRenderOnFire(){ + public float getShadowSize() + { + return 0.0F; + } + + @SideOnly(Side.CLIENT) + public World getWorld() + { + return this.worldObj; + } + + @SideOnly(Side.CLIENT) + + /** + * Return whether this entity should be rendered as on fire. + */ + public boolean canRenderOnFire() + { return true; } - @Override - public IBlockState getBlock(){ - return WizardryBlocks.meteor.getDefaultState(); // For some reason the superclass version returns null on the client + /** + * Checks using a Vec3d to determine if this entity is within range of that vector to be rendered. Args: vec3D + */ + public boolean isInRangeToRenderVec3D(Vec3 par1Vec3) + { + return true; } - - @SideOnly(Side.CLIENT) - @Override - public int getBrightnessForRender(float partialTicks){ - return 15728880; - } - - @Override - public float getBrightness(float partialTicks){ - return 1.0F; - } - - @Override - public boolean isInRangeToRenderDist(double distance){ - return true; - } @Override public void readEntityFromNBT(NBTTagCompound nbttagcompound){ diff --git a/src/main/java/electroblob/wizardry/entity/EntityShield.java b/src/main/java/electroblob/wizardry/entity/EntityShield.java index c884ea07..46f564a6 100644 --- a/src/main/java/electroblob/wizardry/entity/EntityShield.java +++ b/src/main/java/electroblob/wizardry/entity/EntityShield.java @@ -2,16 +2,17 @@ package electroblob.wizardry.entity; import java.lang.ref.WeakReference; -import electroblob.wizardry.WizardData; +import electroblob.wizardry.ExtendedPlayer; import electroblob.wizardry.item.ItemWand; -import electroblob.wizardry.registry.WizardrySounds; +import net.minecraft.entity.DataWatcher; import net.minecraft.entity.Entity; import net.minecraft.entity.IProjectile; import net.minecraft.entity.player.EntityPlayer; import net.minecraft.nbt.NBTTagCompound; +import net.minecraft.util.AxisAlignedBB; import net.minecraft.util.DamageSource; -import net.minecraft.util.math.AxisAlignedBB; import net.minecraft.world.World; +import net.minecraft.world.WorldServer; public class EntityShield extends Entity { @@ -28,10 +29,10 @@ public class EntityShield extends Entity { super(par1World); this.width = 1.2f; this.height = 1.4f; - this.player = new WeakReference(player); + this.player = new WeakReference(player); this.noClip = true; this.setPositionAndRotation(player.posX + player.getLookVec().xCoord, player.posY + 1 + player.getLookVec().yCoord, player.posZ + player.getLookVec().zCoord, player.rotationYawHead, player.rotationPitch); - this.setEntityBoundingBox(new AxisAlignedBB(this.posX - 0.6f, this.posY - 0.7f, this.posZ - 0.6f, this.posX + 0.6f, this.posY + 0.7f, this.posZ + 0.6f)); + this.boundingBox.setBounds(this.posX - 0.6f, this.posY - 0.7f, this.posZ - 0.6f, this.posX + 0.6f, this.posY + 0.7f, this.posZ + 0.6f); } @Override @@ -40,8 +41,8 @@ public class EntityShield extends Entity { EntityPlayer entityplayer = player != null ? player.get() : null; if(entityplayer != null){ this.setPositionAndRotation(entityplayer.posX + entityplayer.getLookVec().xCoord*0.3, entityplayer.posY + 1 + entityplayer.getLookVec().yCoord*0.3, entityplayer.posZ + entityplayer.getLookVec().zCoord*0.3, entityplayer.rotationYawHead, entityplayer.rotationPitch); - if(!entityplayer.isHandActive() || entityplayer.getHeldItem(entityplayer.getActiveHand()) == null || !(entityplayer.getHeldItem(entityplayer.getActiveHand()).getItem() instanceof ItemWand)){ - WizardData.get(entityplayer).shield = null; + if(!entityplayer.isUsingItem() || entityplayer.getHeldItem() == null || !(entityplayer.getHeldItem().getItem() instanceof ItemWand)){ + ExtendedPlayer.get(entityplayer).shield = null; this.setDead(); } }else if(!worldObj.isRemote){ @@ -60,7 +61,7 @@ public class EntityShield extends Entity { public boolean attackEntityFrom(DamageSource par1DamageSource, float par2) { if(par1DamageSource != null && par1DamageSource.getSourceOfDamage() instanceof IProjectile){ - par1DamageSource.getSourceOfDamage().playSound(WizardrySounds.SPELL_DEFLECTION, 0.3f, 1.3f); + worldObj.playSoundAtEntity(par1DamageSource.getSourceOfDamage(), "wizardry:effect", 0.3f, 1.3f); } super.attackEntityFrom(par1DamageSource, par2); return false; @@ -73,7 +74,7 @@ public class EntityShield extends Entity { public AxisAlignedBB getCollisionBox(Entity par1Entity) { - return par1Entity.getEntityBoundingBox(); + return par1Entity.boundingBox; } @Override diff --git a/src/main/java/electroblob/wizardry/entity/construct/EntityArrowRain.java b/src/main/java/electroblob/wizardry/entity/construct/EntityArrowRain.java index ddee05da..8191f94b 100644 --- a/src/main/java/electroblob/wizardry/entity/construct/EntityArrowRain.java +++ b/src/main/java/electroblob/wizardry/entity/construct/EntityArrowRain.java @@ -1,9 +1,7 @@ package electroblob.wizardry.entity.construct; import net.minecraft.entity.EntityLivingBase; -import net.minecraft.entity.projectile.EntityTippedArrow; -import net.minecraft.init.Items; -import net.minecraft.item.ItemStack; +import net.minecraft.entity.projectile.EntityArrow; import net.minecraft.world.World; public class EntityArrowRain extends EntityMagicConstruct { @@ -25,13 +23,13 @@ public class EntityArrowRain extends EntityMagicConstruct { super.onUpdate(); if(!this.worldObj.isRemote){ - EntityTippedArrow arrow = new EntityTippedArrow(worldObj, this.posX + rand.nextDouble()*6 - 3, this.posY + rand.nextDouble()*4 - 2, this.posZ + rand.nextDouble()*6 - 3); + //System.out.println(this.rotationYaw); + EntityArrow arrow = new EntityArrow(worldObj, this.posX + rand.nextDouble()*6 - 3, this.posY + rand.nextDouble()*4 - 2, this.posZ + rand.nextDouble()*6 - 3); arrow.motionX = Math.cos(Math.toRadians(this.rotationYaw + 90)); arrow.motionY = -0.6; arrow.motionZ = Math.sin(Math.toRadians(this.rotationYaw + 90)); arrow.shootingEntity = this.getCaster(); arrow.setDamage(7.0d*damageMultiplier); - arrow.setPotionEffect(new ItemStack(Items.ARROW)); this.worldObj.spawnEntityInWorld(arrow); } } diff --git a/src/main/java/electroblob/wizardry/entity/construct/EntityBlackHole.java b/src/main/java/electroblob/wizardry/entity/construct/EntityBlackHole.java index 56f85762..9709d411 100644 --- a/src/main/java/electroblob/wizardry/entity/construct/EntityBlackHole.java +++ b/src/main/java/electroblob/wizardry/entity/construct/EntityBlackHole.java @@ -2,17 +2,16 @@ package electroblob.wizardry.entity.construct; import java.util.List; -import electroblob.wizardry.util.MagicDamage; -import electroblob.wizardry.util.MagicDamage.DamageType; -import electroblob.wizardry.util.WizardryUtilities; +import electroblob.wizardry.EnumElement; +import electroblob.wizardry.MagicDamage; +import electroblob.wizardry.WizardryUtilities; +import electroblob.wizardry.MagicDamage.DamageType; import net.minecraft.entity.EntityLivingBase; import net.minecraft.entity.player.EntityPlayerMP; -import net.minecraft.init.SoundEvents; import net.minecraft.nbt.NBTTagCompound; -import net.minecraft.network.play.server.SPacketEntityVelocity; +import net.minecraft.network.play.server.S12PacketEntityVelocity; import net.minecraft.util.DamageSource; -import net.minecraft.util.EnumParticleTypes; -import net.minecraft.util.math.Vec3d; +import net.minecraft.util.Vec3; import net.minecraft.world.World; public class EntityBlackHole extends EntityMagicConstruct { @@ -71,15 +70,15 @@ public class EntityBlackHole extends EntityMagicConstruct { // Particle effect. Finishes 40 ticks before the end so the particles disappear at the same time. if(this.ticksExisted + 40 < this.lifetime){ for (int i=0; i<5; i++){ - //this.worldObj.spawnParticle(EnumParticleTypes.PORTAL, this.posX + (this.rand.nextDouble() - 0.5D) * (double)this.width, this.posY + this.rand.nextDouble() * (double)this.height - 0.75D, this.posZ + (this.rand.nextDouble() - 0.5D) * (double)this.width, (this.rand.nextDouble() - 0.5D) * 2.0D, -this.rand.nextDouble(), (this.rand.nextDouble() - 0.5D) * 2.0D); - this.worldObj.spawnParticle(EnumParticleTypes.PORTAL, this.posX, this.posY, this.posZ, (this.rand.nextDouble() - 0.5D) * 4.0D, (this.rand.nextDouble() - 0.5D) * 4.0D - 1, (this.rand.nextDouble() - 0.5D) * 4.0D); + //this.worldObj.spawnParticle("portal", this.posX + (this.rand.nextDouble() - 0.5D) * (double)this.width, this.posY + this.rand.nextDouble() * (double)this.height - 0.75D, this.posZ + (this.rand.nextDouble() - 0.5D) * (double)this.width, (this.rand.nextDouble() - 0.5D) * 2.0D, -this.rand.nextDouble(), (this.rand.nextDouble() - 0.5D) * 2.0D); + this.worldObj.spawnParticle("portal", this.posX, this.posY, this.posZ, (this.rand.nextDouble() - 0.5D) * 4.0D, (this.rand.nextDouble() - 0.5D) * 4.0D - 1, (this.rand.nextDouble() - 0.5D) * 4.0D); } } if(this.lifetime - this.ticksExisted == 75){ - this.playSound(SoundEvents.BLOCK_PORTAL_TRIGGER, 1.5f, 1.0f); + this.worldObj.playSoundAtEntity(this, "portal.trigger", 1.5f, 1.0f); }else if(this.ticksExisted % 80 == 1 && this.ticksExisted + 80 < this.lifetime){ - this.playSound(SoundEvents.BLOCK_PORTAL_AMBIENT, 1.5f, 1.0f); + this.worldObj.playSoundAtEntity(this, "portal.portal", 1.5f, 1.0f); } List targets = WizardryUtilities.getEntitiesWithinRadius(6.0d, this.posX, this.posY, this.posZ, this.worldObj); @@ -111,26 +110,47 @@ public class EntityBlackHole extends EntityMagicConstruct { // Player motion is handled on that player's client so needs packets if(target instanceof EntityPlayerMP){ - ((EntityPlayerMP)target).connection.sendPacket(new SPacketEntityVelocity(target)); + ((EntityPlayerMP)target).playerNetServerHandler.sendPacket(new S12PacketEntityVelocity(target)); } if(this.getDistanceToEntity(target) <= 2){ // Damages the target if it is close enough if(this.getCaster() != null){ - target.attackEntityFrom(MagicDamage.causeIndirectMagicDamage(this, getCaster(), DamageType.MAGIC), 2*damageMultiplier); + target.attackEntityFrom(MagicDamage.causeIndirectEntityMagicDamage(this, getCaster(), DamageType.MAGIC), 2*damageMultiplier); }else{ target.attackEntityFrom(DamageSource.magic, 2*damageMultiplier); } } } + /* + else if(targets.get(i) instanceof EntityItem && !this.worldObj.isRemote){ + EntityItem target = (EntityItem)targets.get(i); + // Sucks the item in + if(this.posX > target.posX && target.motionX < 1){ + target.motionX+=0.1; + }else if(this.posX < target.posX && target.motionX > -1){ + target.motionX-=0.1; + } + if(this.posY > target.posY && target.motionY < 1){ + target.motionY+=0.1; + }else if(this.posY < target.posY && target.motionY > -1){ + target.motionY-=0.1; + } + if(this.posZ > target.posZ && target.motionZ < 1){ + target.motionZ+=0.1; + }else if(this.posZ < target.posZ && target.motionZ > -1){ + target.motionZ-=0.1; + } + } + */ } } } /** - * Checks using a Vec3dd to determine if this entity is within range of that vector to be rendered. Args: Vec3dD + * Checks using a Vec3d to determine if this entity is within range of that vector to be rendered. Args: vec3D */ - public boolean isInRangeToRenderVec3dD(Vec3d par1Vec3d) + public boolean isInRangeToRenderVec3D(Vec3 par1Vec3) { return true; } diff --git a/src/main/java/electroblob/wizardry/entity/construct/EntityBlizzard.java b/src/main/java/electroblob/wizardry/entity/construct/EntityBlizzard.java index 342ad185..8012a239 100644 --- a/src/main/java/electroblob/wizardry/entity/construct/EntityBlizzard.java +++ b/src/main/java/electroblob/wizardry/entity/construct/EntityBlizzard.java @@ -2,14 +2,15 @@ package electroblob.wizardry.entity.construct; import java.util.List; +import electroblob.wizardry.EnumElement; +import electroblob.wizardry.EnumParticleType; +import electroblob.wizardry.MagicDamage; import electroblob.wizardry.Wizardry; -import electroblob.wizardry.registry.WizardryPotions; -import electroblob.wizardry.registry.WizardrySounds; -import electroblob.wizardry.util.MagicDamage; -import electroblob.wizardry.util.MagicDamage.DamageType; -import electroblob.wizardry.util.WizardryParticleType; -import electroblob.wizardry.util.WizardryUtilities; +import electroblob.wizardry.WizardryUtilities; +import electroblob.wizardry.MagicDamage.DamageType; +import net.minecraft.entity.EntityLiving; import net.minecraft.entity.EntityLivingBase; +import net.minecraft.entity.player.EntityPlayer; import net.minecraft.potion.PotionEffect; import net.minecraft.util.DamageSource; import net.minecraft.world.World; @@ -31,7 +32,7 @@ public class EntityBlizzard extends EntityMagicConstruct { public void onUpdate(){ if(this.ticksExisted % 120 == 1){ - this.playSound(WizardrySounds.SPELL_LOOP_WIND, 1.0f, 1.0f); + this.playSound("wizardry:wind", 1.0f, 1.0f); } super.onUpdate(); @@ -43,25 +44,32 @@ public class EntityBlizzard extends EntityMagicConstruct { for(EntityLivingBase target : targets){ if(this.isValidTarget(target)){ + + double velX = target.motionX; + double velY = target.motionY; + double velZ = target.motionZ; if(this.getCaster() != null){ - WizardryUtilities.attackEntityWithoutKnockback(target, MagicDamage.causeIndirectMagicDamage(this, getCaster(), DamageType.FROST), 1*damageMultiplier); + target.attackEntityFrom(MagicDamage.causeIndirectEntityMagicDamage(this, getCaster(), DamageType.FROST), 1*damageMultiplier); }else{ - WizardryUtilities.attackEntityWithoutKnockback(target, DamageSource.magic, 1*damageMultiplier); + target.attackEntityFrom(DamageSource.magic, 1*damageMultiplier); } + + // Removes knockback + target.motionX = velX; + target.motionY = velY; + target.motionZ = velZ; } // All entities are slowed, even the caster (except those immune to frost effects) if(!MagicDamage.isEntityImmune(DamageType.FROST, target)) - target.addPotionEffect(new PotionEffect(WizardryPotions.frost, 20, 0)); + target.addPotionEffect(new PotionEffect(Wizardry.frost.id, 20, 0, true)); } }else{ - // For some reason this number of particles now causes the game to lag significantly, despite it being fine - // in 1.7.10. I thought particles were supposed to be LESS laggy now... - for(int i=1; i<6; i++){ + for(int i=1; i<10; i++){ float brightness = 0.5f + (rand.nextFloat()/2); - Wizardry.proxy.spawnParticle(WizardryParticleType.BLIZZARD, worldObj, this.posX, this.posY + rand.nextDouble()*3, this.posZ, 0, 0, 0, 100, brightness, brightness + 0.1f, 1.0f, false, rand.nextDouble() * 2.5d + 0.5d); - Wizardry.proxy.spawnParticle(WizardryParticleType.BLIZZARD, worldObj, this.posX, this.posY + rand.nextDouble()*3, this.posZ, 0, 0, 0, 100, 1.0f, 1.0f, 1.0f, false, rand.nextDouble() * 2.5d + 0.5d); + Wizardry.proxy.spawnParticle(EnumParticleType.BLIZZARD, worldObj, this.posX, this.posY + rand.nextDouble()*3, this.posZ, 0, 0, 0, 100, brightness, brightness + 0.1f, 1.0f, false, rand.nextDouble() * 2.5d + 0.5d); + Wizardry.proxy.spawnParticle(EnumParticleType.BLIZZARD, worldObj, this.posX, this.posY + rand.nextDouble()*3, this.posZ, 0, 0, 0, 100, 1.0f, 1.0f, 1.0f, false, rand.nextDouble() * 2.5d + 0.5d); } } } diff --git a/src/main/java/electroblob/wizardry/entity/construct/EntityBubble.java b/src/main/java/electroblob/wizardry/entity/construct/EntityBubble.java index 620fb9ee..e4198c81 100644 --- a/src/main/java/electroblob/wizardry/entity/construct/EntityBubble.java +++ b/src/main/java/electroblob/wizardry/entity/construct/EntityBubble.java @@ -2,21 +2,15 @@ package electroblob.wizardry.entity.construct; import java.lang.ref.WeakReference; -import electroblob.wizardry.util.MagicDamage; -import electroblob.wizardry.util.MagicDamage.DamageType; -import electroblob.wizardry.util.WizardryUtilities; +import electroblob.wizardry.EnumElement; +import electroblob.wizardry.MagicDamage; +import electroblob.wizardry.MagicDamage.DamageType; import io.netty.buffer.ByteBuf; import net.minecraft.entity.EntityLivingBase; -import net.minecraft.init.SoundEvents; import net.minecraft.nbt.NBTTagCompound; import net.minecraft.util.DamageSource; -import net.minecraft.util.EnumParticleTypes; import net.minecraft.world.World; -import net.minecraftforge.event.entity.living.LivingAttackEvent; -import net.minecraftforge.fml.common.Mod; -import net.minecraftforge.fml.common.eventhandler.SubscribeEvent; -@Mod.EventBusSubscriber public class EntityBubble extends EntityMagicConstruct { public boolean isDarkOrb; @@ -49,54 +43,53 @@ public class EntityBubble extends EntityMagicConstruct { super.onUpdate(); // Synchronises the rider field - if((this.rider == null || this.rider.get() == null) && WizardryUtilities.getRider(this) instanceof EntityLivingBase - && !WizardryUtilities.getRider(this).isDead){ - this.rider = new WeakReference((EntityLivingBase) WizardryUtilities.getRider(this)); + if((this.rider == null || this.rider.get() == null) && this.riddenByEntity != null && !this.riddenByEntity.isDead){ + this.rider = new WeakReference(this.riddenByEntity); } // Prevents dismounting - if(WizardryUtilities.getRider(this) == null && this.rider != null && this.rider.get() != null && !this.rider.get().isDead){ - this.rider.get().startRiding(this); + if(this.riddenByEntity == null && this.rider != null && this.rider.get() != null && !this.rider.get().isDead){ + this.rider.get().mountEntity(this); } // Stops the bubble bursting instantly. - if(this.ticksExisted < 1 && !isDarkOrb) ((EntityLivingBase)WizardryUtilities.getRider(this)).hurtTime = 0; + if(this.ticksExisted < 1 && !isDarkOrb) ((EntityLivingBase)this.riddenByEntity).hurtTime = 0; this.moveEntity(0, 0.03, 0); if(isDarkOrb){ - if(WizardryUtilities.getRider(this) != null && this.ticksExisted % 30 == 0){ + if(this.riddenByEntity != null && this.ticksExisted % 30 == 0){ if(this.getCaster() != null){ - WizardryUtilities.getRider(this).attackEntityFrom(MagicDamage.causeIndirectMagicDamage(this, getCaster(), DamageType.MAGIC), 1*damageMultiplier); + this.riddenByEntity.attackEntityFrom(MagicDamage.causeIndirectEntityMagicDamage(this, getCaster(), DamageType.MAGIC), 1*damageMultiplier); }else{ - WizardryUtilities.getRider(this).attackEntityFrom(DamageSource.magic, 1*damageMultiplier); + this.riddenByEntity.attackEntityFrom(DamageSource.magic, 1*damageMultiplier); } } for(int i=0; i<5; i++){ - this.worldObj.spawnParticle(EnumParticleTypes.PORTAL, this.posX + (this.rand.nextDouble() - 0.5D) * (double)this.width, this.posY + this.rand.nextDouble() * (double)this.height + 0.5d, this.posZ + (this.rand.nextDouble() - 0.5D) * (double)this.width, (this.rand.nextDouble() - 0.5D) * 2.0D, -this.rand.nextDouble(), (this.rand.nextDouble() - 0.5D) * 2.0D); + this.worldObj.spawnParticle("portal", this.posX + (this.rand.nextDouble() - 0.5D) * (double)this.width, this.posY + this.rand.nextDouble() * (double)this.height + 0.5d, this.posZ + (this.rand.nextDouble() - 0.5D) * (double)this.width, (this.rand.nextDouble() - 0.5D) * 2.0D, -this.rand.nextDouble(), (this.rand.nextDouble() - 0.5D) * 2.0D); } if(lifetime - this.ticksExisted == 75){ - this.playSound(SoundEvents.BLOCK_PORTAL_TRIGGER, 1.5f, 1.0f); + this.worldObj.playSoundAtEntity(this, "portal.trigger", 1.5f, 1.0f); }else if(this.ticksExisted % 100 == 1 && this.ticksExisted < 150){ - this.playSound(SoundEvents.BLOCK_PORTAL_AMBIENT, 1.5f, 1.0f); + this.worldObj.playSoundAtEntity(this, "portal.portal", 1.5f, 1.0f); } } // Bubble bursts if the entity is hurt (see event handler) or killed, or if the bubble has existed for more than 10 seconds. - if(WizardryUtilities.getRider(this) == null && this.ticksExisted > 1){ - if(!this.isDarkOrb) this.playSound(SoundEvents.ENTITY_ITEM_PICKUP, 1.5f, 1.0f); + if(this.riddenByEntity == null && this.ticksExisted > 1){ + if(!this.isDarkOrb) this.worldObj.playSoundAtEntity(this, "random.pop", 1.5f, 1.0f); this.setDead(); } } @Override public void despawn(){ - if(WizardryUtilities.getRider(this) != null){ - ((EntityLivingBase)WizardryUtilities.getRider(this)).dismountEntity(this); + if(this.riddenByEntity != null){ + ((EntityLivingBase)this.riddenByEntity).dismountEntity(this); } - if(!this.isDarkOrb) this.playSound(SoundEvents.ENTITY_ITEM_PICKUP, 1.5f, 1.0f); + if(!this.isDarkOrb) this.worldObj.playSoundAtEntity(this, "random.pop", 1.5f, 1.0f); super.despawn(); } @@ -123,15 +116,5 @@ public class EntityBubble extends EntityMagicConstruct { super.readSpawnData(data); this.isDarkOrb = data.readBoolean(); } - - @SubscribeEvent - public static void onLivingAttackEvent(LivingAttackEvent event){ - // Bursts bubble when the creature inside takes damage - if(event.getEntityLiving().getRidingEntity() instanceof EntityBubble && - !((EntityBubble)event.getEntityLiving().getRidingEntity()).isDarkOrb){ - event.getEntityLiving().getRidingEntity().playSound(SoundEvents.ENTITY_ITEM_PICKUP, 1.5f, 1.0f); - event.getEntityLiving().getRidingEntity().setDead(); - } - } } diff --git a/src/main/java/electroblob/wizardry/entity/construct/EntityDecay.java b/src/main/java/electroblob/wizardry/entity/construct/EntityDecay.java index f8d33151..710e0b00 100644 --- a/src/main/java/electroblob/wizardry/entity/construct/EntityDecay.java +++ b/src/main/java/electroblob/wizardry/entity/construct/EntityDecay.java @@ -2,15 +2,13 @@ package electroblob.wizardry.entity.construct; import java.util.List; +import electroblob.wizardry.EnumParticleType; import electroblob.wizardry.Wizardry; -import electroblob.wizardry.registry.WizardryPotions; -import electroblob.wizardry.util.WizardryParticleType; -import electroblob.wizardry.util.WizardryUtilities; +import electroblob.wizardry.WizardryUtilities; import net.minecraft.entity.EntityLivingBase; -import net.minecraft.init.SoundEvents; import net.minecraft.nbt.NBTTagCompound; import net.minecraft.potion.PotionEffect; -import net.minecraft.util.math.Vec3d; +import net.minecraft.util.Vec3; import net.minecraft.world.World; public class EntityDecay extends EntityMagicConstruct { @@ -37,22 +35,23 @@ public class EntityDecay extends EntityMagicConstruct { super.onUpdate(); - if(this.rand.nextInt(700) == 0 && this.ticksExisted+100 < LIFETIME) this.playSound(SoundEvents.BLOCK_LAVA_AMBIENT, 0.2F + rand.nextFloat() * 0.2F, 0.6F + rand.nextFloat() * 0.15F); + if(this.rand.nextInt(700) == 0 && this.ticksExisted+100 < LIFETIME) this.playSound("liquid.lava", 0.2F + rand.nextFloat() * 0.2F, 0.6F + rand.nextFloat() * 0.15F); if(!this.worldObj.isRemote){ - List targets = WizardryUtilities.getEntitiesWithinRadius(1.0d, this.posX, this.posY, this.posZ, this.worldObj); - for(EntityLivingBase target : targets){ - if(target != this.getCaster()){ + List targets = WizardryUtilities.getEntitiesWithinRadius(1.0d, this.posX, this.posY, this.posZ, this.worldObj); + for(int i=0; i targets = WizardryUtilities.getEntitiesWithinRadius((this.ticksExisted*speed)+1.5, this.posX, this.posY, this.posZ, worldObj); - + // In this particular instance, the caster is completely unaffected because they will always be in the centre. targets.remove(this.getCaster()); @@ -74,33 +80,21 @@ public class EntityEarthquake extends EntityMagicConstruct { double motionZ = target.motionZ; if(this.isValidTarget(target)){ - target.attackEntityFrom(MagicDamage.causeIndirectMagicDamage(this, this.getCaster(), DamageType.BLAST), 10*this.damageMultiplier); - target.addPotionEffect(new PotionEffect(MobEffects.WEAKNESS, 400, 1)); + target.attackEntityFrom(MagicDamage.causeIndirectEntityMagicDamage(this, this.getCaster(), DamageType.BLAST), 10*this.damageMultiplier); + target.addPotionEffect(new PotionEffect(Potion.weakness.id, 400, 1)); } // All targets are thrown, even those immune to the damage, so they don't fall into the ground. target.motionX = motionX; target.motionY = 0.8; // Throws target into the air. target.motionZ = motionZ; - + // Player motion is handled on that player's client so needs packets if(target instanceof EntityPlayerMP){ - ((EntityPlayerMP)target).connection.sendPacket(new SPacketEntityVelocity(target)); + ((EntityPlayerMP)target).playerNetServerHandler.sendPacket(new S12PacketEntityVelocity(target)); } } } - // TODO: Uncomment once 2.1.0 is released -// }else{ -// -// // Constant 15 blocks for now -// List targets = WizardryUtilities.getEntitiesWithinRadius(15, this.posX, this.posY, this.posZ, worldObj, EntityPlayer.class); -// -// float magnitude = 6f * ((float)(this.lifetime - this.ticksExisted))/(float)this.lifetime; -// -// // Makes the screen shake -// for(EntityLivingBase target : targets){ -// target.setAngles(0, this.ticksExisted % 4 < 2 ? magnitude : -magnitude); -// } } } diff --git a/src/main/java/electroblob/wizardry/entity/construct/EntityFireRing.java b/src/main/java/electroblob/wizardry/entity/construct/EntityFireRing.java index e2055da5..807fc1be 100644 --- a/src/main/java/electroblob/wizardry/entity/construct/EntityFireRing.java +++ b/src/main/java/electroblob/wizardry/entity/construct/EntityFireRing.java @@ -2,11 +2,11 @@ package electroblob.wizardry.entity.construct; import java.util.List; -import electroblob.wizardry.util.MagicDamage; -import electroblob.wizardry.util.MagicDamage.DamageType; -import electroblob.wizardry.util.WizardryUtilities; +import electroblob.wizardry.EnumElement; +import electroblob.wizardry.MagicDamage; +import electroblob.wizardry.WizardryUtilities; +import electroblob.wizardry.MagicDamage.DamageType; import net.minecraft.entity.EntityLivingBase; -import net.minecraft.init.SoundEvents; import net.minecraft.util.DamageSource; import net.minecraft.world.World; @@ -27,7 +27,7 @@ public class EntityFireRing extends EntityMagicConstruct { public void onUpdate(){ if(this.ticksExisted % 40 == 1){ - this.playSound(SoundEvents.BLOCK_FIRE_AMBIENT, 4.0f, 0.7f); + this.playSound("fire.fire", 4.0f, 0.7f); } super.onUpdate(); @@ -49,7 +49,7 @@ public class EntityFireRing extends EntityMagicConstruct { target.setFire(10); if(this.getCaster() != null){ - target.attackEntityFrom(MagicDamage.causeIndirectMagicDamage(this, getCaster(), DamageType.FIRE), 1*damageMultiplier); + target.attackEntityFrom(MagicDamage.causeIndirectEntityMagicDamage(this, getCaster(), DamageType.FIRE), 1*damageMultiplier); }else{ target.attackEntityFrom(DamageSource.magic, 1*damageMultiplier); } diff --git a/src/main/java/electroblob/wizardry/entity/construct/EntityFireSigil.java b/src/main/java/electroblob/wizardry/entity/construct/EntityFireSigil.java index 9913006e..78b6a070 100644 --- a/src/main/java/electroblob/wizardry/entity/construct/EntityFireSigil.java +++ b/src/main/java/electroblob/wizardry/entity/construct/EntityFireSigil.java @@ -2,13 +2,12 @@ package electroblob.wizardry.entity.construct; import java.util.List; -import electroblob.wizardry.util.MagicDamage; -import electroblob.wizardry.util.MagicDamage.DamageType; -import electroblob.wizardry.util.WizardryUtilities; +import electroblob.wizardry.EnumElement; +import electroblob.wizardry.MagicDamage; +import electroblob.wizardry.WizardryUtilities; +import electroblob.wizardry.MagicDamage.DamageType; import net.minecraft.entity.EntityLivingBase; -import net.minecraft.init.SoundEvents; import net.minecraft.util.DamageSource; -import net.minecraft.util.EnumParticleTypes; import net.minecraft.world.World; public class EntityFireSigil extends EntityMagicConstruct { @@ -49,7 +48,7 @@ public class EntityFireSigil extends EntityMagicConstruct { double velY = target.motionY; double velZ = target.motionZ; - target.attackEntityFrom(this.getCaster() != null ? MagicDamage.causeIndirectMagicDamage(this, this.getCaster(), DamageType.FIRE) : DamageSource.magic, 6); + target.attackEntityFrom(this.getCaster() != null ? MagicDamage.causeIndirectEntityMagicDamage(this, this.getCaster(), DamageType.FIRE) : DamageSource.magic, 6); // Removes knockback target.motionX = velX; @@ -57,8 +56,8 @@ public class EntityFireSigil extends EntityMagicConstruct { target.motionZ = velZ; if(!MagicDamage.isEntityImmune(DamageType.FIRE, target)) target.setFire(10); - - this.playSound(SoundEvents.ENTITY_BLAZE_SHOOT, 1, 1); + + worldObj.playAuxSFX(1009, (int)this.posX, (int)this.posY, (int)this.posZ, 0); // The trap is destroyed once triggered. this.setDead(); @@ -67,7 +66,7 @@ public class EntityFireSigil extends EntityMagicConstruct { }else if(this.rand.nextInt(15) == 0){ double radius = 0.5 + rand.nextDouble()*0.3; double angle = rand.nextDouble()*Math.PI*2; - worldObj.spawnParticle(EnumParticleTypes.FLAME, this.posX + radius*Math.cos(angle), this.posY + 0.1, this.posZ + radius*Math.sin(angle), 0, 0, 0); + worldObj.spawnParticle("flame", this.posX + radius*Math.cos(angle), this.posY + 0.1, this.posZ + radius*Math.sin(angle), 0, 0, 0); } } diff --git a/src/main/java/electroblob/wizardry/entity/construct/EntityForcefield.java b/src/main/java/electroblob/wizardry/entity/construct/EntityForcefield.java index f0f817eb..d1a5eaae 100644 --- a/src/main/java/electroblob/wizardry/entity/construct/EntityForcefield.java +++ b/src/main/java/electroblob/wizardry/entity/construct/EntityForcefield.java @@ -2,16 +2,19 @@ package electroblob.wizardry.entity.construct; import java.util.List; +import electroblob.wizardry.EnumParticleType; +import electroblob.wizardry.ExtendedPlayer; import electroblob.wizardry.Wizardry; -import electroblob.wizardry.registry.WizardrySounds; -import electroblob.wizardry.util.WizardryParticleType; -import electroblob.wizardry.util.WizardryUtilities; +import electroblob.wizardry.WizardryUtilities; import net.minecraft.entity.Entity; import net.minecraft.entity.EntityLivingBase; +import net.minecraft.entity.IProjectile; +import net.minecraft.entity.player.EntityPlayer; import net.minecraft.entity.player.EntityPlayerMP; -import net.minecraft.network.play.server.SPacketEntityVelocity; +import net.minecraft.entity.projectile.EntityArrow; +import net.minecraft.network.play.server.S12PacketEntityVelocity; +import net.minecraft.util.AxisAlignedBB; import net.minecraft.util.DamageSource; -import net.minecraft.util.math.AxisAlignedBB; import net.minecraft.world.World; public class EntityForcefield extends EntityMagicConstruct { @@ -20,7 +23,7 @@ public class EntityForcefield extends EntityMagicConstruct { super(world); this.height = 6; this.width = 6; - this.setEntityBoundingBox(new AxisAlignedBB(this.posX - 3, this.posY - 3, this.posZ - 3, this.posX + 3, this.posY + 3, this.posZ + 3)); + this.boundingBox.setBounds(this.posX - 3, this.posY - 3, this.posZ - 3, this.posX + 3, this.posY + 3, this.posZ + 3); } public EntityForcefield(World world, double x, double y, double z, EntityLivingBase caster, int lifetime) { @@ -29,7 +32,7 @@ public class EntityForcefield extends EntityMagicConstruct { super(world, x, y-3, z, caster, lifetime, 1.0f); this.height = 6; this.width = 6; - this.setEntityBoundingBox(new AxisAlignedBB(this.posX - 3, this.posY - 3, this.posZ - 3, this.posX + 3, this.posY + 3, this.posZ + 3)); + this.boundingBox.setBounds(this.posX - 3, this.posY - 3, this.posZ - 3, this.posX + 3, this.posY + 3, this.posZ + 3); } public boolean canBeCollidedWith() @@ -39,7 +42,7 @@ public class EntityForcefield extends EntityMagicConstruct { public AxisAlignedBB getCollisionBox(Entity par1Entity) { - return par1Entity.getEntityBoundingBox(); + return par1Entity.boundingBox; } public void onUpdate(){ @@ -54,7 +57,7 @@ public class EntityForcefield extends EntityMagicConstruct { target.addVelocity((target.posX - this.posX)*multiplier, (target.posY - (this.posY + 3))*multiplier, (target.posZ - this.posZ)*multiplier); // Player motion is handled on that player's client so needs packets if(target instanceof EntityPlayerMP){ - ((EntityPlayerMP)target).connection.sendPacket(new SPacketEntityVelocity(target)); + ((EntityPlayerMP)target).playerNetServerHandler.sendPacket(new S12PacketEntityVelocity(target)); } } } @@ -65,18 +68,17 @@ public class EntityForcefield extends EntityMagicConstruct { double yaw = rand.nextDouble()*Math.PI*2; double pitch = (rand.nextDouble()-0.5)*Math.PI; // Generates a spherical pattern of particles - Wizardry.proxy.spawnParticle(WizardryParticleType.BRIGHT_DUST, worldObj, this.posX + radius*Math.cos(yaw)*Math.cos(pitch), this.posY + 3 + radius*Math.sin(pitch), this.posZ + radius*Math.sin(yaw)*Math.cos(pitch), 0, 0, 0, 48 + this.rand.nextInt(12), brightness, brightness, 1.0f); + Wizardry.proxy.spawnParticle(EnumParticleType.BRIGHT_DUST, worldObj, this.posX + radius*Math.cos(yaw)*Math.cos(pitch), this.posY + 3 + radius*Math.sin(pitch), this.posZ + radius*Math.sin(yaw)*Math.cos(pitch), 0, 0, 0, 48 + this.rand.nextInt(12), brightness, brightness, 1.0f); } } } - public boolean attackEntityFrom(DamageSource source, float par2){ - - if(source != null && source.getSourceOfDamage() != null){ - // Now works for any source of damage. - source.getSourceOfDamage().playSound(WizardrySounds.SPELL_DEFLECTION, 0.3f, 1.3f); + public boolean attackEntityFrom(DamageSource par1DamageSource, float par2) + { + if(par1DamageSource != null && par1DamageSource.getSourceOfDamage() instanceof IProjectile){ + worldObj.playSoundAtEntity(par1DamageSource.getSourceOfDamage(), "wizardry:effect", 0.3f, 1.3f); } - super.attackEntityFrom(source, par2); + super.attackEntityFrom(par1DamageSource, par2); return false; } diff --git a/src/main/java/electroblob/wizardry/entity/construct/EntityFrostSigil.java b/src/main/java/electroblob/wizardry/entity/construct/EntityFrostSigil.java index f2cb3ffe..d327561c 100644 --- a/src/main/java/electroblob/wizardry/entity/construct/EntityFrostSigil.java +++ b/src/main/java/electroblob/wizardry/entity/construct/EntityFrostSigil.java @@ -2,13 +2,12 @@ package electroblob.wizardry.entity.construct; import java.util.List; +import electroblob.wizardry.EnumElement; +import electroblob.wizardry.EnumParticleType; +import electroblob.wizardry.MagicDamage; import electroblob.wizardry.Wizardry; -import electroblob.wizardry.registry.WizardryPotions; -import electroblob.wizardry.registry.WizardrySounds; -import electroblob.wizardry.util.MagicDamage; -import electroblob.wizardry.util.MagicDamage.DamageType; -import electroblob.wizardry.util.WizardryParticleType; -import electroblob.wizardry.util.WizardryUtilities; +import electroblob.wizardry.WizardryUtilities; +import electroblob.wizardry.MagicDamage.DamageType; import net.minecraft.entity.EntityLivingBase; import net.minecraft.potion.PotionEffect; import net.minecraft.util.DamageSource; @@ -52,7 +51,7 @@ public class EntityFrostSigil extends EntityMagicConstruct { double velY = target.motionY; double velZ = target.motionZ; - target.attackEntityFrom(this.getCaster() != null ? MagicDamage.causeIndirectMagicDamage(this, this.getCaster(), DamageType.FROST) : DamageSource.magic, 8); + target.attackEntityFrom(this.getCaster() != null ? MagicDamage.causeIndirectEntityMagicDamage(this, this.getCaster(), DamageType.FROST) : DamageSource.magic, 8); // Removes knockback target.motionX = velX; @@ -60,9 +59,9 @@ public class EntityFrostSigil extends EntityMagicConstruct { target.motionZ = velZ; if(!MagicDamage.isEntityImmune(DamageType.FROST, target)) - target.addPotionEffect(new PotionEffect(WizardryPotions.frost, 200, 1)); + target.addPotionEffect(new PotionEffect(Wizardry.frost.id, 200, 1, true)); - this.playSound(WizardrySounds.SPELL_FREEZE, 1.0f, 1.0f); + this.playSound("wizardry:freeze", 1.0f, 1.0f); // The trap is destroyed once triggered. this.setDead(); @@ -71,7 +70,7 @@ public class EntityFrostSigil extends EntityMagicConstruct { }else if(this.rand.nextInt(15) == 0){ double radius = 0.5 + rand.nextDouble()*0.3; double angle = rand.nextDouble()*Math.PI*2; - Wizardry.proxy.spawnParticle(WizardryParticleType.SNOW, worldObj, this.posX + radius*Math.cos(angle), this.posY + 0.1, this.posZ + radius*Math.sin(angle), 0, 0, 0, 40 + rand.nextInt(10)); + Wizardry.proxy.spawnParticle(EnumParticleType.SNOW, worldObj, this.posX + radius*Math.cos(angle), this.posY + 0.1, this.posZ + radius*Math.sin(angle), 0, 0, 0, 40 + rand.nextInt(10)); } } diff --git a/src/main/java/electroblob/wizardry/entity/construct/EntityHammer.java b/src/main/java/electroblob/wizardry/entity/construct/EntityHammer.java index 67950d41..04892d8f 100644 --- a/src/main/java/electroblob/wizardry/entity/construct/EntityHammer.java +++ b/src/main/java/electroblob/wizardry/entity/construct/EntityHammer.java @@ -2,72 +2,80 @@ package electroblob.wizardry.entity.construct; import java.util.List; +import cpw.mods.fml.relauncher.Side; +import cpw.mods.fml.relauncher.SideOnly; +import electroblob.wizardry.EnumElement; +import electroblob.wizardry.EnumParticleType; +import electroblob.wizardry.MagicDamage; import electroblob.wizardry.Wizardry; +import electroblob.wizardry.WizardryUtilities; +import electroblob.wizardry.MagicDamage.DamageType; import electroblob.wizardry.entity.EntityArc; -import electroblob.wizardry.registry.WizardrySounds; -import electroblob.wizardry.util.MagicDamage; -import electroblob.wizardry.util.MagicDamage.DamageType; -import electroblob.wizardry.util.WizardryParticleType; -import electroblob.wizardry.util.WizardryUtilities; import net.minecraft.block.Block; -import net.minecraft.block.state.IBlockState; import net.minecraft.entity.EntityLivingBase; +import net.minecraft.entity.EntityTracker; import net.minecraft.entity.effect.EntityLightningBolt; -import net.minecraft.init.SoundEvents; +import net.minecraft.entity.monster.EntityCreeper; +import net.minecraft.entity.player.EntityPlayer; import net.minecraft.nbt.NBTTagCompound; +import net.minecraft.network.play.server.S14PacketEntity.S15PacketEntityRelMove; import net.minecraft.util.DamageSource; -import net.minecraft.util.EnumParticleTypes; -import net.minecraft.util.math.AxisAlignedBB; -import net.minecraft.util.math.BlockPos; +import net.minecraft.util.Vec3; import net.minecraft.world.World; +import net.minecraft.world.WorldServer; -public class EntityHammer extends EntityMagicConstruct { - +public class EntityHammer extends EntityMagicConstruct +{ /** How long the hammer has been falling for. */ public int fallTime; - public EntityHammer(World par1World){ + public EntityHammer(World par1World) + { super(par1World); this.setSize(1.0f, 1.9F); this.noClip = false; } - public EntityHammer(World world, double x, double y, double z, EntityLivingBase caster, int lifetime, float damageMultiplier){ + public EntityHammer(World world, double x, double y, double z, EntityLivingBase caster, int lifetime, float damageMultiplier) + { super(world, x, y, z, caster, lifetime, damageMultiplier); - this.setSize(1.0f, 1.9F); + this.setSize(1.1f, 1.9F); this.motionX = 0.0D; this.motionY = 0.0D; this.motionZ = 0.0D; this.noClip = false; } - @Override - public boolean isBurning(){ + /** + * Returns true if the entity is on fire. Used by render to add the fire effect on rendering. + */ + public boolean isBurning() + { return false; } - @Override - public boolean canBeCollidedWith(){ + /** + * Returns true if other Entities should be prevented from moving through this Entity. + */ + public boolean canBeCollidedWith() + { return true; } - - @Override - public AxisAlignedBB getCollisionBoundingBox(){ - return this.getEntityBoundingBox(); - } - @Override + /** + * Called to update the entity's position/logic. + */ public void onUpdate(){ super.onUpdate(); if(this.ticksExisted % 20 == 1 && !this.onGround && worldObj.isRemote){ // Though this sound does repeat, it stops when it hits the ground. - Wizardry.proxy.playMovingSound(this, WizardrySounds.SPELL_LOOP_LIGHTNING, 3.0f, 1.0f, false); + Wizardry.proxy.playMovingSound(this, "wizardry:electricityb", 3.0f, 1.0f, false); } if(this.worldObj.isRemote && this.ticksExisted % 3 == 0){ - Wizardry.proxy.spawnParticle(WizardryParticleType.SPARK, worldObj, this.posX - 0.5d + rand.nextDouble(), this.posY + 2*rand.nextDouble(), this.posZ - 0.5d + rand.nextDouble(), 0, 0, 0, 3); + Wizardry.proxy.spawnParticle(EnumParticleType.SPARK, worldObj, this.posX - 0.5d + rand.nextDouble(), this.posY + 2*rand.nextDouble(), this.posZ - 0.5d + rand.nextDouble(), 0, 0, 0, 3); } if(!this.worldObj.isRemote){ @@ -101,21 +109,20 @@ public class EntityHammer extends EntityMagicConstruct { if(!worldObj.isRemote){ EntityArc arc = new EntityArc(worldObj); - arc.setEndpointCoords(this.posX, this.posY + this.height - 0.1, this.posZ, + arc.setEndpointCoords(this.posX, this.posY+1, this.posZ, target.posX, target.posY + target.height/2, target.posZ); worldObj.spawnEntityInWorld(arc); }else{ for(int j=0;j<8;j++){ - Wizardry.proxy.spawnParticle(WizardryParticleType.SPARK, worldObj, target.posX + worldObj.rand.nextFloat() - 0.5, target.getEntityBoundingBox().minY + target.height/2 + worldObj.rand.nextFloat()*2 - 1, target.posZ + worldObj.rand.nextFloat() - 0.5, 0, 0, 0, 3); - worldObj.spawnParticle(EnumParticleTypes.SMOKE_LARGE, target.posX + rand.nextFloat(), target.getEntityBoundingBox().minY + target.height/2 + rand.nextFloat(), target.posZ + rand.nextFloat(), 0, 0, 0); + Wizardry.proxy.spawnParticle(EnumParticleType.SPARK, worldObj, target.posX + worldObj.rand.nextFloat() - 0.5, WizardryUtilities.getEntityFeetPos(target) + target.height/2 + worldObj.rand.nextFloat()*2 - 1, target.posZ + worldObj.rand.nextFloat() - 0.5, 0, 0, 0, 3); + worldObj.spawnParticle("largesmoke", target.posX + rand.nextFloat(), WizardryUtilities.getEntityFeetPos(target) + target.height/2 + rand.nextFloat(), target.posZ + rand.nextFloat(), 0, 0, 0); } } - target.playSound(WizardrySounds.SPELL_SPARK, 1.0F, rand.nextFloat() * 0.4F + 1.5F); + worldObj.playSoundAtEntity(target, "wizardry:arc", 1.0F, rand.nextFloat() * 0.4F + 1.5F); if(this.getCaster() != null){ - WizardryUtilities.attackEntityWithoutKnockback(target, MagicDamage.causeIndirectMagicDamage(this, getCaster(), DamageType.SHOCK), 6*damageMultiplier); - WizardryUtilities.applyStandardKnockback(this, target); + target.attackEntityFrom(MagicDamage.causeIndirectEntityMagicDamage(this, getCaster(), DamageType.SHOCK), 6*damageMultiplier); }else{ target.attackEntityFrom(DamageSource.magic, 6*damageMultiplier); } @@ -126,58 +133,96 @@ public class EntityHammer extends EntityMagicConstruct { } } - @Override public void despawn(){ - this.playSound(SoundEvents.ENTITY_GENERIC_EXPLODE, 1.0F, 1.0f); + this.playSound("random.explode", 1.0F, 1.0f); if(this.worldObj.isRemote){ - this.worldObj.spawnParticle(EnumParticleTypes.EXPLOSION_LARGE, this.posX, this.posY, this.posZ, 0, 0, 0); + this.worldObj.spawnParticle("largeexplode", this.posX, this.posY, this.posZ, 0, 0, 0); } super.despawn(); } - - @Override - public void fall(float distance, float damageMultiplier) { - + + /** + * Called when the mob is falling. Calculates and applies fall damage. + */ + protected void fall(float par1){ + if(worldObj.isRemote){ for(int i=0; i<40; i++){ double particleX = this.posX - 1.0d + 2*rand.nextDouble(); double particleZ = this.posZ - 1.0d + 2*rand.nextDouble(); // Roundabout way of getting a block instance for the block the hammer is standing on (if any). - IBlockState block = worldObj.getBlockState(new BlockPos(this.posX, this.posY-2, this.posZ)); + Block block = worldObj.getBlock((int)this.posX, (int)this.posY-2, (int)this.posZ); if(block != null){ - worldObj.spawnParticle(EnumParticleTypes.BLOCK_DUST, particleX, this.posY, particleZ, - particleX - this.posX, 0, particleZ - this.posZ, Block.getStateId(block)); + Wizardry.proxy.spawnDigParticle(worldObj, particleX, this.posY, particleZ, + particleX - this.posX, 0, particleZ - this.posZ, block); } } + }else{ - // Just to check the hammer has actually fallen from the sky, rather than the block under it being broken. + + // For some reason, the hammer falls again on world reload, but only by about 1 block. if(this.fallDistance > 10){ - EntityLightningBolt entitylightning = new EntityLightningBolt(worldObj, this.posX, this.posY, this.posZ, false); + + // For some reason, the hammer 'lands' one block above the ground first time around - on the SERVER as well. + // Edit: not any more, it seems... maybe because the yOffset thing got deleted. + //this.setPosition(this.posX, this.posY-0.8, this.posZ); + + EntityLightningBolt entitylightning = new EntityLightningBolt(worldObj, this.posX, this.posY, this.posZ); worldObj.addWeatherEffect(entitylightning); + + // The last three parameters are the difference in position between server and client. What this is doing + // is essentially hijacking the vanilla packet to force the hammer into the right position. (not sure why, + // but the packet divides these numbers by 32.) + S15PacketEntityRelMove packet = new S15PacketEntityRelMove(this.getEntityId(), (byte)0, (byte)-65, (byte)0); + + if(this.worldObj instanceof WorldServer){ + EntityTracker et = ((WorldServer)this.worldObj).getEntityTracker(); + // This sends a packet to all players tracking the entity. + et.func_151248_b(this, packet); + } + //packet.yPosition = -65; + + //PacketDispatcher.sendPacketToAllAround(this.posX, this.posY, this.posZ, 256, this.dimension, packet); + //PacketDispatcher.sendPacketToAllPlayers(packet); } } } - @Override + /** + * (abstract) Protected helper method to write subclass entity data to NBT. + */ public void writeEntityToNBT(NBTTagCompound nbttagcompound) { super.writeEntityToNBT(nbttagcompound); nbttagcompound.setByte("Time", (byte)this.fallTime); } - @Override + /** + * (abstract) Protected helper method to read subclass entity data from NBT. + */ public void readEntityFromNBT(NBTTagCompound nbttagcompound) { super.readEntityFromNBT(nbttagcompound); this.fallTime = nbttagcompound.getByte("Time") & 255; + // Undoes the manual position change on world reload to prevent the hammer being in the ground. + //this.setPosition(this.posX, this.posY+0.8, this.posZ); } - @Override - public boolean isInRangeToRenderDist(double distance) { - return true; + @SideOnly(Side.CLIENT) + public float getShadowSize() + { + return 1.0F; + } + + /** + * Checks using a Vec3d to determine if this entity is within range of that vector to be rendered. Args: vec3D + */ + public boolean isInRangeToRenderVec3D(Vec3 par1Vec3) + { + return true; } } diff --git a/src/main/java/electroblob/wizardry/entity/construct/EntityHealAura.java b/src/main/java/electroblob/wizardry/entity/construct/EntityHealAura.java index 34a96dd8..90b63272 100644 --- a/src/main/java/electroblob/wizardry/entity/construct/EntityHealAura.java +++ b/src/main/java/electroblob/wizardry/entity/construct/EntityHealAura.java @@ -2,12 +2,12 @@ package electroblob.wizardry.entity.construct; import java.util.List; +import electroblob.wizardry.EnumElement; +import electroblob.wizardry.EnumParticleType; +import electroblob.wizardry.MagicDamage; import electroblob.wizardry.Wizardry; -import electroblob.wizardry.registry.WizardrySounds; -import electroblob.wizardry.util.MagicDamage; -import electroblob.wizardry.util.MagicDamage.DamageType; -import electroblob.wizardry.util.WizardryParticleType; -import electroblob.wizardry.util.WizardryUtilities; +import electroblob.wizardry.WizardryUtilities; +import electroblob.wizardry.MagicDamage.DamageType; import net.minecraft.entity.EntityLivingBase; import net.minecraft.util.DamageSource; import net.minecraft.world.World; @@ -29,7 +29,7 @@ public class EntityHealAura extends EntityMagicConstruct { public void onUpdate(){ if(this.ticksExisted % 25 == 1){ - this.playSound(WizardrySounds.SPELL_LOOP_SPARKLE, 0.1f, 1.0f); + this.playSound("wizardry:sparkle", 0.1f, 1.0f); } super.onUpdate(); @@ -49,7 +49,7 @@ public class EntityHealAura extends EntityMagicConstruct { double velZ = target.motionZ; if(this.getCaster() != null){ - target.attackEntityFrom(MagicDamage.causeIndirectMagicDamage(this, getCaster(), DamageType.RADIANT), 1*damageMultiplier); + target.attackEntityFrom(MagicDamage.causeIndirectEntityMagicDamage(this, getCaster(), DamageType.RADIANT), 1*damageMultiplier); }else{ target.attackEntityFrom(DamageSource.magic, 1*damageMultiplier); } @@ -69,7 +69,7 @@ public class EntityHealAura extends EntityMagicConstruct { float brightness = 0.5f + (rand.nextFloat()*0.5f); double radius = rand.nextDouble()*2.0; double angle = rand.nextDouble()*Math.PI*2; - Wizardry.proxy.spawnParticle(WizardryParticleType.SPARKLE, worldObj, this.posX + radius*Math.cos(angle), this.posY, this.posZ + radius*Math.sin(angle), 0, 0.05f, 0, 48 + this.rand.nextInt(12), 1.0f, 1.0f, brightness); + Wizardry.proxy.spawnParticle(EnumParticleType.SPARKLE, worldObj, this.posX + radius*Math.cos(angle), this.posY, this.posZ + radius*Math.sin(angle), 0, 0.05f, 0, 48 + this.rand.nextInt(12), 1.0f, 1.0f, brightness); } } } diff --git a/src/main/java/electroblob/wizardry/entity/construct/EntityIceSpike.java b/src/main/java/electroblob/wizardry/entity/construct/EntityIceSpike.java index b314a2cf..f14367d6 100644 --- a/src/main/java/electroblob/wizardry/entity/construct/EntityIceSpike.java +++ b/src/main/java/electroblob/wizardry/entity/construct/EntityIceSpike.java @@ -1,9 +1,8 @@ package electroblob.wizardry.entity.construct; -import electroblob.wizardry.registry.WizardryPotions; -import electroblob.wizardry.registry.WizardrySounds; -import electroblob.wizardry.util.MagicDamage; -import electroblob.wizardry.util.MagicDamage.DamageType; +import electroblob.wizardry.MagicDamage; +import electroblob.wizardry.MagicDamage.DamageType; +import electroblob.wizardry.Wizardry; import net.minecraft.entity.EntityLivingBase; import net.minecraft.potion.PotionEffect; import net.minecraft.world.World; @@ -32,14 +31,14 @@ public class EntityIceSpike extends EntityMagicConstruct { this.moveEntity(this.motionX, this.motionY, this.motionZ); - if(lifetime - this.ticksExisted == 30) this.playSound(WizardrySounds.SPELL_ICE, 1, 2); + if(lifetime - this.ticksExisted == 30) this.playSound("wizardry:ice", 1, 2); if(!this.worldObj.isRemote){ - for(Object entity : this.worldObj.getEntitiesWithinAABBExcludingEntity(this, this.getEntityBoundingBox())){ + for(Object entity : this.worldObj.getEntitiesWithinAABBExcludingEntity(this, this.boundingBox)){ if(entity instanceof EntityLivingBase && this.isValidTarget((EntityLivingBase)entity)){ // Potion effect only gets added if the damage succeeded. if(((EntityLivingBase)entity).attackEntityFrom(MagicDamage.causeDirectMagicDamage(this.getCaster(), DamageType.FROST), 5*this.damageMultiplier)) - ((EntityLivingBase)entity).addPotionEffect(new PotionEffect(WizardryPotions.frost, 100, 0)); + ((EntityLivingBase)entity).addPotionEffect(new PotionEffect(Wizardry.frost.id, 100, 0)); } } } diff --git a/src/main/java/electroblob/wizardry/entity/construct/EntityLightningPulse.java b/src/main/java/electroblob/wizardry/entity/construct/EntityLightningPulse.java index 59653c0b..4b167b0a 100644 --- a/src/main/java/electroblob/wizardry/entity/construct/EntityLightningPulse.java +++ b/src/main/java/electroblob/wizardry/entity/construct/EntityLightningPulse.java @@ -1,6 +1,15 @@ package electroblob.wizardry.entity.construct; +import java.util.List; + +import electroblob.wizardry.EnumElement; +import electroblob.wizardry.EnumParticleType; +import electroblob.wizardry.MagicDamage; +import electroblob.wizardry.Wizardry; +import electroblob.wizardry.WizardryUtilities; +import electroblob.wizardry.MagicDamage.DamageType; import net.minecraft.entity.EntityLivingBase; +import net.minecraft.util.DamageSource; import net.minecraft.world.World; public class EntityLightningPulse extends EntityMagicConstruct { diff --git a/src/main/java/electroblob/wizardry/entity/construct/EntityLightningSigil.java b/src/main/java/electroblob/wizardry/entity/construct/EntityLightningSigil.java index 8ecb646d..6f490c1e 100644 --- a/src/main/java/electroblob/wizardry/entity/construct/EntityLightningSigil.java +++ b/src/main/java/electroblob/wizardry/entity/construct/EntityLightningSigil.java @@ -2,16 +2,17 @@ package electroblob.wizardry.entity.construct; import java.util.List; +import electroblob.wizardry.EnumElement; +import electroblob.wizardry.EnumParticleType; +import electroblob.wizardry.MagicDamage; import electroblob.wizardry.Wizardry; +import electroblob.wizardry.WizardryUtilities; +import electroblob.wizardry.MagicDamage.DamageType; import electroblob.wizardry.entity.EntityArc; -import electroblob.wizardry.registry.WizardrySounds; -import electroblob.wizardry.util.MagicDamage; -import electroblob.wizardry.util.MagicDamage.DamageType; -import electroblob.wizardry.util.WizardryParticleType; -import electroblob.wizardry.util.WizardryUtilities; import net.minecraft.entity.EntityLivingBase; +import net.minecraft.entity.monster.EntityCreeper; +import net.minecraft.entity.player.EntityPlayer; import net.minecraft.util.DamageSource; -import net.minecraft.util.EnumParticleTypes; import net.minecraft.world.World; public class EntityLightningSigil extends EntityMagicConstruct { @@ -57,14 +58,14 @@ public class EntityLightningSigil extends EntityMagicConstruct { double velZ = target.motionZ; // Only works if target is actually damaged to account for hurtResistantTime - if(target.attackEntityFrom(getCaster() != null ? MagicDamage.causeIndirectMagicDamage(this, getCaster(), DamageType.SHOCK) : DamageSource.magic, 6)){ + if(target.attackEntityFrom(getCaster() != null ? MagicDamage.causeIndirectEntityMagicDamage(this, getCaster(), DamageType.SHOCK) : DamageSource.magic, 6)){ // Removes knockback target.motionX = velX; target.motionY = velY; target.motionZ = velZ; - this.playSound(WizardrySounds.SPELL_SPARK, 1.0f, 1.0f); + this.playSound("wizardry:arc", 1.0f, 1.0f); // Secondary chaining effect double seekerRange = 5.0d; @@ -84,14 +85,14 @@ public class EntityLightningSigil extends EntityMagicConstruct { worldObj.spawnEntityInWorld(arc); }else{ for(int k=0;k<8;k++){ - Wizardry.proxy.spawnParticle(WizardryParticleType.SPARK, worldObj, secondaryTarget.posX + worldObj.rand.nextFloat() - 0.5, secondaryTarget.getEntityBoundingBox().minY + secondaryTarget.height/2 + worldObj.rand.nextFloat()*2 - 1, secondaryTarget.posZ + worldObj.rand.nextFloat() - 0.5, 0, 0, 0, 3); - worldObj.spawnParticle(EnumParticleTypes.SMOKE_LARGE, secondaryTarget.posX + worldObj.rand.nextFloat() - 0.5, secondaryTarget.getEntityBoundingBox().minY + secondaryTarget.height/2 + worldObj.rand.nextFloat()*2 - 1, secondaryTarget.posZ + worldObj.rand.nextFloat() - 0.5, 0, 0, 0); + Wizardry.proxy.spawnParticle(EnumParticleType.SPARK, worldObj, secondaryTarget.posX + worldObj.rand.nextFloat() - 0.5, WizardryUtilities.getEntityFeetPos(secondaryTarget) + secondaryTarget.height/2 + worldObj.rand.nextFloat()*2 - 1, secondaryTarget.posZ + worldObj.rand.nextFloat() - 0.5, 0, 0, 0, 3); + worldObj.spawnParticle("largesmoke", secondaryTarget.posX + worldObj.rand.nextFloat() - 0.5, WizardryUtilities.getEntityFeetPos(secondaryTarget) + secondaryTarget.height/2 + worldObj.rand.nextFloat()*2 - 1, secondaryTarget.posZ + worldObj.rand.nextFloat() - 0.5, 0, 0, 0); } } - secondaryTarget.playSound(WizardrySounds.SPELL_SPARK, 1.0F, worldObj.rand.nextFloat() * 0.4F + 1.5F); + worldObj.playSoundAtEntity(secondaryTarget, "wizardry:arc", 1.0F, worldObj.rand.nextFloat() * 0.4F + 1.5F); - secondaryTarget.attackEntityFrom(MagicDamage.causeIndirectMagicDamage(this, getCaster(), DamageType.SHOCK), 4); + secondaryTarget.attackEntityFrom(MagicDamage.causeIndirectEntityMagicDamage(this, getCaster(), DamageType.SHOCK), 4); } } @@ -105,7 +106,7 @@ public class EntityLightningSigil extends EntityMagicConstruct { if(this.worldObj.isRemote && this.rand.nextInt(15) == 0){ double radius = 0.5 + rand.nextDouble()*0.3; double angle = rand.nextDouble()*Math.PI*2; - Wizardry.proxy.spawnParticle(WizardryParticleType.SPARK, worldObj, this.posX + radius*Math.cos(angle), this.posY + 0.1, this.posZ + radius*Math.sin(angle), 0, 0, 0, 3); + Wizardry.proxy.spawnParticle(EnumParticleType.SPARK, worldObj, this.posX + radius*Math.cos(angle), this.posY + 0.1, this.posZ + radius*Math.sin(angle), 0, 0, 0, 3); } } diff --git a/src/main/java/electroblob/wizardry/entity/construct/EntityMagicConstruct.java b/src/main/java/electroblob/wizardry/entity/construct/EntityMagicConstruct.java index e477f9d5..c523fe55 100644 --- a/src/main/java/electroblob/wizardry/entity/construct/EntityMagicConstruct.java +++ b/src/main/java/electroblob/wizardry/entity/construct/EntityMagicConstruct.java @@ -3,13 +3,16 @@ package electroblob.wizardry.entity.construct; import java.lang.ref.WeakReference; import java.util.UUID; -import electroblob.wizardry.util.WizardryUtilities; +import cpw.mods.fml.common.registry.IEntityAdditionalSpawnData; +import electroblob.wizardry.ExtendedPlayer; +import electroblob.wizardry.WizardryUtilities; +import electroblob.wizardry.entity.living.EntitySummonedCreature; import io.netty.buffer.ByteBuf; import net.minecraft.entity.Entity; import net.minecraft.entity.EntityLivingBase; +import net.minecraft.entity.player.EntityPlayer; import net.minecraft.nbt.NBTTagCompound; import net.minecraft.world.World; -import net.minecraftforge.fml.common.registry.IEntityAdditionalSpawnData; /** * This class is for all inanimate magical constructs which are not projectiles. It was made from scratch @@ -59,10 +62,10 @@ public abstract class EntityMagicConstruct extends Entity implements IEntityAddi // Overrides the original to stop the entity moving when it intersects stuff. The default arrow does this to allow // it to stick in blocks. @Override - public void setPositionAndRotationDirect(double x, double y, double z, float yaw, float pitch, int posRotationIncrements, boolean teleport) + public void setPositionAndRotation2(double par1, double par3, double par5, float par7, float par8, int par9) { - this.setPosition(x, y, z); - this.setRotation(yaw, pitch); + this.setPosition(par1, par3, par5); + this.setRotation(par7, par8); } public void onUpdate(){ @@ -99,7 +102,8 @@ public abstract class EntityMagicConstruct extends Entity implements IEntityAddi @Override protected void readEntityFromNBT(NBTTagCompound nbttagcompound){ - casterUUID = nbttagcompound.getUniqueId("casterUUID"); + String string = nbttagcompound.getString("casterUUID"); + if(WizardryUtilities.verifyUUIDString(string)) casterUUID = UUID.fromString(string); lifetime = nbttagcompound.getInteger("lifetime"); damageMultiplier = nbttagcompound.getFloat("damageMultiplier"); } @@ -107,7 +111,7 @@ public abstract class EntityMagicConstruct extends Entity implements IEntityAddi @Override protected void writeEntityToNBT(NBTTagCompound nbttagcompound){ if(this.getCaster() != null){ - nbttagcompound.setUniqueId("casterUUID", this.getCaster().getUniqueID()); + nbttagcompound.setString("casterUUID", this.getCaster().getUniqueID().toString()); } nbttagcompound.setInteger("lifetime", lifetime); nbttagcompound.setFloat("damageMultiplier", damageMultiplier); diff --git a/src/main/java/electroblob/wizardry/entity/construct/EntityTornado.java b/src/main/java/electroblob/wizardry/entity/construct/EntityTornado.java index 2e1625a9..8530addf 100644 --- a/src/main/java/electroblob/wizardry/entity/construct/EntityTornado.java +++ b/src/main/java/electroblob/wizardry/entity/construct/EntityTornado.java @@ -2,25 +2,23 @@ package electroblob.wizardry.entity.construct; import java.util.List; +import electroblob.wizardry.EnumParticleType; +import electroblob.wizardry.MagicDamage; +import electroblob.wizardry.MagicDamage.DamageType; import electroblob.wizardry.Wizardry; -import electroblob.wizardry.registry.WizardryAchievements; -import electroblob.wizardry.registry.WizardrySounds; -import electroblob.wizardry.util.MagicDamage; -import electroblob.wizardry.util.MagicDamage.DamageType; -import electroblob.wizardry.util.WizardryParticleType; -import electroblob.wizardry.util.WizardryUtilities; +import electroblob.wizardry.WizardryUtilities; import io.netty.buffer.ByteBuf; +import net.minecraft.block.Block; import net.minecraft.block.material.Material; -import net.minecraft.block.state.IBlockState; import net.minecraft.entity.EntityLivingBase; import net.minecraft.entity.passive.EntityPig; import net.minecraft.entity.player.EntityPlayer; import net.minecraft.entity.player.EntityPlayerMP; import net.minecraft.nbt.NBTTagCompound; -import net.minecraft.network.play.server.SPacketEntityVelocity; +import net.minecraft.network.play.server.S12PacketEntityVelocity; import net.minecraft.util.DamageSource; -import net.minecraft.util.math.BlockPos; import net.minecraft.world.World; +import net.minecraftforge.common.BiomeDictionary; public class EntityTornado extends EntityMagicConstruct { @@ -48,16 +46,12 @@ public class EntityTornado extends EntityMagicConstruct { if(this.ticksExisted % 120 == 1 && worldObj.isRemote){ // Repeat is false so that the sound fades out when the tornado does rather than stopping suddenly - Wizardry.proxy.playMovingSound(this, WizardrySounds.SPELL_LOOP_WIND, 1.0f, 1.0f, false); + Wizardry.proxy.playMovingSound(this, "wizardry:wind", 1.0f, 1.0f, false); } this.moveEntity(velX, motionY, velZ); - BlockPos pos = new BlockPos(this); - int y = WizardryUtilities.getNearestFloorLevelC(worldObj, pos.up(3), 5); - pos = new BlockPos(pos.getX(), y, pos.getZ()); - - if(this.worldObj.getBlockState(pos).getMaterial() == Material.LAVA){ + if(this.worldObj.getBlock((int)this.posX, WizardryUtilities.getNearestFloorLevelC(worldObj, (int)this.posX, (int)this.posY + 3, (int)this.posZ, 5) - 1, (int)this.posZ).getMaterial() == Material.lava){ // Fire tornado! this.setFire(5); } @@ -70,7 +64,9 @@ public class EntityTornado extends EntityMagicConstruct { if(this.isValidTarget(target)){ + double velX = target.motionX; double velY = target.motionY; + double velZ = target.motionZ; double dx = this.posX-target.posX > 0? 0.5 - (this.posX-target.posX)/8 : -0.5 - (this.posX-target.posX)/8; double dz = this.posZ-target.posZ > 0? 0.5 - (this.posZ-target.posZ)/8 : -0.5 - (this.posZ-target.posZ)/8; @@ -80,7 +76,7 @@ public class EntityTornado extends EntityMagicConstruct { } if(this.getCaster() != null){ - target.attackEntityFrom(MagicDamage.causeIndirectMagicDamage(this, getCaster(), DamageType.MAGIC), 1*damageMultiplier); + target.attackEntityFrom(MagicDamage.causeIndirectEntityMagicDamage(this, getCaster(), DamageType.MAGIC), 1*damageMultiplier); }else{ target.attackEntityFrom(DamageSource.magic, 1*damageMultiplier); } @@ -91,66 +87,62 @@ public class EntityTornado extends EntityMagicConstruct { // Player motion is handled on that player's client so needs packets if(target instanceof EntityPlayerMP){ - ((EntityPlayerMP)target).connection.sendPacket(new SPacketEntityVelocity(target)); + ((EntityPlayerMP)target).playerNetServerHandler.sendPacket(new S12PacketEntityVelocity(target)); } - // The 'Not Again...' achievement - if(target instanceof EntityPig && WizardryUtilities.getRider(target) instanceof EntityPlayer){ - ((EntityPlayer)WizardryUtilities.getRider(target)).addStat(WizardryAchievements.pig_tornado); + if(target instanceof EntityPig && target.riddenByEntity instanceof EntityPlayer){ + ((EntityPlayer)target.riddenByEntity).triggerAchievement(Wizardry.pigTornado); } } } }else{ for(int i=1; i<10; i++){ - + float brightness = rand.nextFloat()*0.7f; double yPos = rand.nextDouble()*8; int blockX = (int)this.posX - 2 + this.rand.nextInt(4); int blockZ = (int)this.posZ - 2 + this.rand.nextInt(4); + int blockY = WizardryUtilities.getNearestFloorLevelC(worldObj, blockX, (int)this.posY + 3, blockZ, 5) - 1; - BlockPos pos1 = new BlockPos(blockX, this.posY+3, blockZ); - - int blockY = WizardryUtilities.getNearestFloorLevelC(worldObj, pos1, 5) - 1; - - pos1 = new BlockPos(pos1.getX(), blockY, pos1.getZ()); - - IBlockState block = this.worldObj.getBlockState(pos1); + Block block = this.worldObj.getBlock(blockX, blockY, blockZ); + int metadata = this.worldObj.getBlockMetadata(blockX, blockY, blockZ); // If the block it found was air or something it can't pick up, it makes a best guess based on the biome. if(!canTornadoPickUpBitsOf(block)){ - block = worldObj.getBiome(pos1).topBlock; + block = worldObj.getBiomeGenForCoords(blockX, blockZ).topBlock; + metadata = 0; } - Wizardry.proxy.spawnTornadoParticle(worldObj, this.posX, this.posY + yPos, this.posZ, this.velX, this.velZ, yPos/3 + 0.5d, 100, block, pos1); - Wizardry.proxy.spawnTornadoParticle(worldObj, this.posX, this.posY + yPos, this.posZ, this.velX, this.velZ, yPos/3 + 0.5d, 100, block, pos1); + Wizardry.proxy.spawnTornadoParticle(worldObj, this.posX, this.posY + yPos, this.posZ, this.velX, this.velZ, yPos/3 + 0.5d, 100, block, metadata); + Wizardry.proxy.spawnTornadoParticle(worldObj, this.posX, this.posY + yPos, this.posZ, this.velX, this.velZ, yPos/3 + 0.5d, 100, block, metadata); // Sometimes spawns leaf particles if the block is leaves - if(block.getMaterial() == Material.LEAVES && this.rand.nextInt(3) == 0){ + if(block.getMaterial() == Material.leaves && this.rand.nextInt(3) == 0){ double yPos1 = rand.nextDouble()*8; - Wizardry.proxy.spawnParticle(WizardryParticleType.LEAF, worldObj, this.posX + (rand.nextDouble()*2-1)*(yPos1/3 + 0.5d), this.posY + yPos1, this.posZ + (rand.nextDouble()*2-1)*(yPos1/3 + 0.5d), 0, -0.05, 0, 40 + rand.nextInt(10)); + Wizardry.proxy.spawnParticle(EnumParticleType.LEAF, worldObj, this.posX + (rand.nextDouble()*2-1)*(yPos1/3 + 0.5d), this.posY + yPos1, this.posZ + (rand.nextDouble()*2-1)*(yPos1/3 + 0.5d), 0, -0.05, 0, 40 + rand.nextInt(10)); } // Sometimes spawns snow particles if the block is snow - if(block.getMaterial() == Material.SNOW || block.getMaterial() == Material.CRAFTED_SNOW && this.rand.nextInt(3) == 0){ + if(block.getMaterial() == Material.snow || block.getMaterial() == Material.craftedSnow && this.rand.nextInt(3) == 0){ double yPos1 = rand.nextDouble()*8; - Wizardry.proxy.spawnParticle(WizardryParticleType.SNOW, worldObj, this.posX + (rand.nextDouble()*2-1)*(yPos1/3 + 0.5d), this.posY + yPos1, this.posZ + (rand.nextDouble()*2-1)*(yPos1/3 + 0.5d), 0, -0.02, 0, 40 + rand.nextInt(10)); + Wizardry.proxy.spawnParticle(EnumParticleType.SNOW, worldObj, this.posX + (rand.nextDouble()*2-1)*(yPos1/3 + 0.5d), this.posY + yPos1, this.posZ + (rand.nextDouble()*2-1)*(yPos1/3 + 0.5d), 0, -0.02, 0, 40 + rand.nextInt(10)); } } } } - private static boolean canTornadoPickUpBitsOf(IBlockState block){ + private static boolean canTornadoPickUpBitsOf(Block block){ Material material = block.getMaterial(); - return material == Material.CRAFTED_SNOW - || material == Material.GROUND - || material == Material.GRASS - || material == Material.LAVA - || material == Material.SAND - || material == Material.SNOW - || material == Material.WATER - || material == Material.PLANTS - || material == Material.LEAVES - || material == Material.VINE; + return material == Material.craftedSnow + || material == Material.ground + || material == Material.grass + || material == Material.lava + || material == Material.sand + || material == Material.snow + || material == Material.water + || material == Material.plants + || material == Material.leaves + || material == Material.vine; } @Override diff --git a/src/main/java/electroblob/wizardry/entity/living/EntityAIAttackSpell.java b/src/main/java/electroblob/wizardry/entity/living/EntityAIAttackSpell.java deleted file mode 100644 index d6843a66..00000000 --- a/src/main/java/electroblob/wizardry/entity/living/EntityAIAttackSpell.java +++ /dev/null @@ -1,238 +0,0 @@ -package electroblob.wizardry.entity.living; - -import java.util.ArrayList; -import java.util.List; - -import electroblob.wizardry.event.SpellCastEvent; -import electroblob.wizardry.event.SpellCastEvent.Source; -import electroblob.wizardry.packet.PacketNPCCastSpell; -import electroblob.wizardry.packet.WizardryPacketHandler; -import electroblob.wizardry.registry.Spells; -import electroblob.wizardry.spell.Spell; -import electroblob.wizardry.util.SpellModifiers; -import net.minecraft.entity.EntityLiving; -import net.minecraft.entity.EntityLivingBase; -import net.minecraft.entity.ai.EntityAIBase; -import net.minecraft.util.EnumHand; -import net.minecraftforge.common.MinecraftForge; -import net.minecraftforge.fml.common.network.NetworkRegistry.TargetPoint; -import net.minecraftforge.fml.common.network.simpleimpl.IMessage; - -/** Entity AI class for use by instances of {@link ISpellCaster}. This deals with pathing, the spell casting itself - * and the attack cooldown. Also provides an automatic implementation of continuous spell casting using the methods - * specified in {@code ISpellCaster}; all the entity class needs to do is implement those methods. */ -public class EntityAIAttackSpell extends EntityAIBase { - - /** The entity the AI instance has been applied to. */ - private final EntityLiving attacker; - /** The entity the AI instance has been applied to, but as an ISpellCaster. */ - private final ISpellCaster caster; - /** The tagret to be attacked. */ - private EntityLivingBase target; - /** Decremented each tick while greater than 0. When a spell is cast, this is set to that spell's cooldown plus - * the base cooldown. */ - private int cooldown; - /** The number of ticks between the entity finding a new target and when it first starts attacking, and also the - * amount that is added to the spell's cooldown between casting spells. */ - private final int baseCooldown; - /** Decremented each tick while greater than 0. When a continuous spell is first cast, this is set to - * the value of {@link EntityAIAttackSpell#continuousSpellDuration}. */ - // I think that in this case this is only necessary on the server side. If any inconsistent behaviour - // occurs, look into syncing this as well. - private int continuousSpellTimer; - /** The number of ticks that continuous spells will be cast for before cooling down. */ - private final int continuousSpellDuration; - /** The speed that the entity should move when attacking. Only used when passed into the navigator. */ - private final double speed; - private int seeTime; - private final float maxAttackDistance; - - /** - * Creates a new spell attack AI with the given parameters. - * @param attacker The entity that that uses this AI. - * @param speed The speed that the entity should move when attacking. Only used when passed into the navigator. - * @param maxDistance The maximum distance the entity should be from its target. - * @param baseCooldown The number of ticks between the entity finding a new target and when it first starts attacking, - * and also the amount that is added to the cooldown of the spell that has just been cast. - * @param continuousSpellDuration The number of ticks that continuous spells will be cast for before cooling down. - */ - public EntityAIAttackSpell(ISpellCaster attacker, double speed, float maxDistance, int baseCooldown, int continuousSpellDuration){ - - this.cooldown = -1; - - if(!(attacker instanceof EntityLiving)){ - throw new IllegalArgumentException("Tried to create an EntityAICastSpell for an entity that isn't an EntityLiving"); - }else{ - this.caster = attacker; - this.attacker = (EntityLiving)attacker; - this.baseCooldown = baseCooldown; - this.continuousSpellDuration = continuousSpellDuration; - this.speed = speed; - this.maxAttackDistance = maxDistance * maxDistance; - this.setMutexBits(3); - } - } - - @Override - public boolean shouldExecute(){ - - EntityLivingBase entitylivingbase = this.attacker.getAttackTarget(); - - if(entitylivingbase == null){ - return false; - }else{ - this.target = entitylivingbase; - return true; - } - } - - @Override - public boolean continueExecuting(){ - return this.shouldExecute() || !this.attacker.getNavigator().noPath(); - } - - @Override - public void resetTask(){ - this.target = null; - this.seeTime = 0; - this.cooldown = -1; - this.setContinuousSpellAndNotify(Spells.none, new SpellModifiers()); - this.continuousSpellTimer = 0; - } - - private void setContinuousSpellAndNotify(Spell spell, SpellModifiers modifiers){ - caster.setContinuousSpell(spell); - WizardryPacketHandler.net.sendToAllAround(new PacketNPCCastSpell.Message(attacker.getEntityId(), - target == null ? -1 : target.getEntityId(), EnumHand.MAIN_HAND, spell.id(), modifiers), - // Particles are usually only visible from 16 blocks away, so 128 is more than far enough. - new TargetPoint(attacker.dimension, attacker.posX, attacker.posY, attacker.posZ, 128)); - } - - @Override - public void updateTask(){ - - // Only executed server side. - - double distanceSq = this.attacker.getDistanceSq(this.target.posX, this.target.getEntityBoundingBox().minY, this.target.posZ); - boolean targetIsVisible = this.attacker.getEntitySenses().canSee(this.target); - - if(targetIsVisible){ - ++this.seeTime; - }else{ - this.seeTime = 0; - } - - if(distanceSq <= (double)this.maxAttackDistance && this.seeTime >= 20){ - this.attacker.getNavigator().clearPathEntity(); - }else{ - this.attacker.getNavigator().tryMoveToEntityLiving(this.target, this.speed); - } - - this.attacker.getLookHelper().setLookPositionWithEntity(this.target, 30.0F, 30.0F); - - if(this.continuousSpellTimer > 0){ - - this.continuousSpellTimer--; - - // If the target goes out of range or out of sight... - if(distanceSq > (double)this.maxAttackDistance || !targetIsVisible - // ...or the spell is cancelled via events... - || MinecraftForge.EVENT_BUS.post(new SpellCastEvent.Tick(attacker, caster.getContinuousSpell(), - caster.getModifiers(), Source.NPC, this.continuousSpellDuration - this.continuousSpellTimer)) - // ...or the spell no longer succeeds... - || !caster.getContinuousSpell().cast(attacker.worldObj, attacker, EnumHand.MAIN_HAND, - this.continuousSpellDuration - this.continuousSpellTimer, target, caster.getModifiers()) - // ...or the time has elapsed... - || this.continuousSpellTimer == 0){ - - // ...reset the continuous spell timer and start the cooldown. - this.continuousSpellTimer = 0; - setContinuousSpellAndNotify(Spells.none, new SpellModifiers()); - this.cooldown = this.baseCooldown; - return; - - }else if(this.continuousSpellDuration - this.continuousSpellTimer == 1){ - // On the first tick, if the spell did succeed, fire SpellCastEvent.Post. - MinecraftForge.EVENT_BUS.post(new SpellCastEvent.Post(attacker, caster.getContinuousSpell(), - caster.getModifiers(), Source.NPC)); - } - - }else if(--this.cooldown == 0){ - - if(distanceSq > (double)this.maxAttackDistance || !targetIsVisible){ - return; - } - - double dx = target.posX - attacker.posX; - double dz = target.posZ - attacker.posZ; - - List spells = new ArrayList(caster.getSpells()); - - if(spells.size() > 0){ - - if(!attacker.worldObj.isRemote){ - - // New way of choosing a spell; keeps trying until one works or all have been tried - - Spell spell; - - while(!spells.isEmpty()){ - - spell = spells.get(attacker.worldObj.rand.nextInt(spells.size())); - - SpellModifiers modifiers = caster.getModifiers(); - - if(spell != null && attemptCastSpell(spell, modifiers)){ - // The spell worked, so we're done! - attacker.rotationYaw = (float)(Math.atan2(dz, dx) * 180.0D / Math.PI) - 90.0F; - return; - }else{ - spells.remove(spell); - } - } - } - } - - }else if(this.cooldown < 0){ - // This should only be reached when the entity first starts attacking. Stops it attacking instantly. - this.cooldown = this.baseCooldown; - } - } - - /** Attempts to cast the given spell (including event firing) and returns true if it succeeded. */ - private boolean attemptCastSpell(Spell spell, SpellModifiers modifiers){ - - // If anything stops the spell working at this point, nothing else happens. - if(MinecraftForge.EVENT_BUS.post(new SpellCastEvent.Pre(attacker, spell, modifiers, Source.NPC))){ - return false; - } - - if(spell.cast(attacker.worldObj, attacker, EnumHand.MAIN_HAND, 0, target, modifiers)){ - - if(spell.isContinuous){ - // -1 because the spell has been cast once already! - this.continuousSpellTimer = this.continuousSpellDuration - 1; - setContinuousSpellAndNotify(spell, modifiers); - - }else{ - - MinecraftForge.EVENT_BUS.post(new SpellCastEvent.Post(attacker, spell, modifiers, Source.NPC)); - - // For now, the cooldown is just added to the constant base cooldown. I think this - // is a reasonable way of doing things; it's certainly better than before. - this.cooldown = this.baseCooldown + spell.cooldown; - - if(spell.doesSpellRequirePacket()){ - // Sends a packet to all players in dimension to tell them to spawn particles. - IMessage msg = new PacketNPCCastSpell.Message(attacker.getEntityId(), - target.getEntityId(), EnumHand.MAIN_HAND, spell.id(), modifiers); - WizardryPacketHandler.net.sendToDimension(msg, attacker.worldObj.provider.getDimension()); - } - } - - return true; - } - - return false; - } -} diff --git a/src/main/java/electroblob/wizardry/entity/living/EntityAISelectSpell.java b/src/main/java/electroblob/wizardry/entity/living/EntityAISelectSpell.java deleted file mode 100644 index 286dc93c..00000000 --- a/src/main/java/electroblob/wizardry/entity/living/EntityAISelectSpell.java +++ /dev/null @@ -1,14 +0,0 @@ -package electroblob.wizardry.entity.living; - -import net.minecraft.entity.ai.EntityAIBase; - -public class EntityAISelectSpell extends EntityAIBase { - - // TODO: Write this class - - @Override - public boolean shouldExecute(){ - return false; - } - -} diff --git a/src/main/java/electroblob/wizardry/entity/living/EntityBlazeMinion.java b/src/main/java/electroblob/wizardry/entity/living/EntityBlazeMinion.java index 473f30b7..019285d3 100644 --- a/src/main/java/electroblob/wizardry/entity/living/EntityBlazeMinion.java +++ b/src/main/java/electroblob/wizardry/entity/living/EntityBlazeMinion.java @@ -1,160 +1,294 @@ package electroblob.wizardry.entity.living; -import java.lang.ref.WeakReference; -import java.util.UUID; +import java.util.List; -import electroblob.wizardry.Wizardry; +import cpw.mods.fml.relauncher.Side; +import cpw.mods.fml.relauncher.SideOnly; +import electroblob.wizardry.WizardryUtilities; +import net.minecraft.entity.Entity; +import net.minecraft.entity.EntityLiving; import net.minecraft.entity.EntityLivingBase; +import net.minecraft.entity.SharedMonsterAttributes; import net.minecraft.entity.ai.EntityAIHurtByTarget; +import net.minecraft.entity.ai.EntityAILookIdle; import net.minecraft.entity.ai.EntityAINearestAttackableTarget; -import net.minecraft.entity.monster.EntityBlaze; +import net.minecraft.entity.ai.EntityAIWander; +import net.minecraft.entity.monster.EntityMob; +import net.minecraft.entity.monster.IMob; import net.minecraft.entity.player.EntityPlayer; -import net.minecraft.item.Item; -import net.minecraft.item.ItemStack; -import net.minecraft.nbt.NBTTagCompound; -import net.minecraft.util.EnumHand; -import net.minecraft.util.EnumParticleTypes; -import net.minecraft.util.ResourceLocation; -import net.minecraft.util.text.ITextComponent; -import net.minecraft.util.text.TextComponentTranslation; +import net.minecraft.entity.projectile.EntitySmallFireball; +import net.minecraft.util.DamageSource; +import net.minecraft.util.MathHelper; import net.minecraft.world.World; -public class EntityBlazeMinion extends EntityBlaze implements ISummonedCreature { +public class EntityBlazeMinion extends EntitySummonedCreature { + + /** Random offset used in floating behaviour */ + private float heightOffset = 0.5F; - // Field implementations - private int lifetime = 600; - private WeakReference casterReference; - private UUID casterUUID; + /** Ticks until heightOffset is randomized */ + private int heightOffsetUpdateTime; + private int someAttackCounter; - // Setter + getter implementations - @Override public int getLifetime(){ return lifetime; } - @Override public void setLifetime(int lifetime){ this.lifetime = lifetime; } - @Override public WeakReference getCasterReference(){ return casterReference; } - @Override public void setCasterReference(WeakReference reference){ casterReference = reference; } - @Override public UUID getCasterUUID() { return casterUUID; } - @Override public void setCasterUUID(UUID uuid) { this.casterUUID = uuid; } - - /** - * Default shell constructor, only used by client. Lifetime defaults arbitrarily to 600, but this doesn't - * matter because the client side entity immediately gets the lifetime value copied over to it by this class - * anyway. When extending this class, you must override this constructor or Minecraft won't like it, but there's - * no need to do anything inside it other than call super(). - */ public EntityBlazeMinion(World world){ - super(world); - this.experienceValue = 0; - } - - /** - * Set lifetime to -1 to allow this creature to last forever. This constructor should be overridden when - * extending this class (be sure to call super()) so that AI and other things can be added. - */ - public EntityBlazeMinion(World world, double x, double y, double z, EntityLivingBase caster, int lifetime){ - super(world); - this.setPosition(x, y, z); - this.casterReference = new WeakReference(caster); - this.experienceValue = 0; - this.lifetime = lifetime; - } - - // EntityBlaze overrides - - // This particular override is pretty standard: let the superclass handle basic AI like swimming, but replace its - // targeting system with one that targets hostile mobs and takes the ADS into account. - @Override - protected void initEntityAI() - { - super.initEntityAI(); - this.targetTasks.taskEntries.clear(); - this.targetTasks.addTask(1, new EntityAIHurtByTarget(this, false)); - this.targetTasks.addTask(2, new EntityAINearestAttackableTarget(this, EntityLivingBase.class, - 0, false, true, this.getTargetSelector())); + super(world); } - // Implementations + public EntityBlazeMinion(World world, double x, double y, double z, EntityLivingBase caster, int lifetime){ + + super(world, x, y, z, caster, lifetime); + this.isImmuneToFire = true; + // Don't know why these are here; the blaze minion uses the old AI system. + this.tasks.addTask(1, new EntityAIWander(this, 1.0D)); + this.tasks.addTask(2, new EntityAILookIdle(this)); + this.targetTasks.addTask(1, new EntityAIHurtByTarget(this, true)); + this.targetTasks.addTask(2, new EntityAINearestAttackableTarget(this, EntityLiving.class, 0, false, true, IMob.mobSelector)); + this.experienceValue = 0; + } + + /** + * Returns true if the newer Entity AI code should be run + */ + protected boolean isAIEnabled() + { + return false; + } @Override - public void setRevengeTarget(EntityLivingBase entity){ - if(this.shouldRevengeTarget(entity)) super.setRevengeTarget(entity); - } - - @Override - public void onUpdate(){ - super.onUpdate(); - this.updateDelegate(); - } - - @Override - public void onSpawn(){ - this.spawnParticleEffect(); - } - - @Override - public void onDespawn(){ - this.spawnParticleEffect(); - } - - /** Normally this would be private, but since this class has subclasses with different spawn/despawn particle - * effects, it makes sense to have them override this rather than both onSpawn() and onDespawn(). */ - protected void spawnParticleEffect(){ - if(this.worldObj.isRemote){ - for(int i=0;i<15;i++){ - this.worldObj.spawnParticle(EnumParticleTypes.FLAME, this.posX + this.rand.nextFloat(), this.posY + 1 + this.rand.nextFloat(), this.posZ + this.rand.nextFloat(), 0, 0, 0); - } - } - } - - @Override - public boolean hasParticleEffect() { - return false; - } - - @Override - protected boolean processInteract(EntityPlayer player, EnumHand hand, ItemStack stack) { - // In this case, the delegate method determines whether super is called. - // Rather handily, we can make use of Java's 'stop as soon as you find true' method of evaluating OR statements. - return this.interactDelegate(player, hand, stack) || super.processInteract(player, hand, stack); - } - - @Override - public void writeEntityToNBT(NBTTagCompound nbttagcompound){ - super.writeEntityToNBT(nbttagcompound); - this.writeNBTDelegate(nbttagcompound); - } - - @Override - public void readEntityFromNBT(NBTTagCompound nbttagcompound){ - super.readEntityFromNBT(nbttagcompound); - this.writeNBTDelegate(nbttagcompound); - } - - // Recommended overrides - - @Override protected int getExperiencePoints(EntityPlayer player){ return 0; } - @Override protected boolean canDropLoot(){ return false; } - @Override protected Item getDropItem(){ return null; } - @Override protected ResourceLocation getLootTable(){ return null; } - // This vanilla method has nothing to do with the custom despawn() method. - @Override protected boolean canDespawn(){ return false; } - - @Override - public boolean canAttackClass(Class entityType){ + public boolean hasRangedAttack() { return true; } - @Override - public ITextComponent getDisplayName(){ - if(getCaster() != null){ - return new TextComponentTranslation(NAMEPLATE_TRANSLATION_KEY, getCaster().getName(), - new TextComponentTranslation("entity." + this.getEntityString() + ".name")); - }else{ - return super.getDisplayName(); - } + protected void applyEntityAttributes() + { + super.applyEntityAttributes(); + this.getEntityAttribute(SharedMonsterAttributes.attackDamage).setBaseValue(6.0D); + this.getEntityAttribute(SharedMonsterAttributes.followRange).setBaseValue(16.0D); + } + + protected void entityInit() + { + super.entityInit(); + this.dataWatcher.addObject(16, new Byte((byte)0)); + } + + /** + * Returns the sound this mob makes while it's alive. + */ + protected String getLivingSound() + { + return "mob.blaze.breathe"; + } + + /** + * Returns the sound this mob makes when it is hurt. + */ + protected String getHurtSound() + { + return "mob.blaze.hit"; + } + + /** + * Returns the sound this mob makes on death. + */ + protected String getDeathSound() + { + return "mob.blaze.death"; + } + + @SideOnly(Side.CLIENT) + public int getBrightnessForRender(float par1) + { + return 15728880; + } + + /** + * Gets how bright this entity is. + */ + public float getBrightness(float par1) + { + return 1.0F; + } + + @Override + public void onSpawn(){ + if(this.worldObj.isRemote){ + for(int i=0;i<15;i++){ + this.worldObj.spawnParticle("flame", this.posX + this.rand.nextFloat(), this.posY + 1 + this.rand.nextFloat(), this.posZ + this.rand.nextFloat(), 0, 0, 0); + } + } + } + + @Override + public void despawn(){ + if(this.worldObj.isRemote){ + for(int i=0;i<15;i++){ + this.worldObj.spawnParticle("flame", this.posX + this.rand.nextFloat(), this.posY + 1 + this.rand.nextFloat(), this.posZ + this.rand.nextFloat(), 0, 0, 0); + } + } + super.despawn(); } - @Override - public boolean hasCustomName(){ - // If this returns true, the renderer will show the nameplate when looking directly at the entity - return Wizardry.settings.showSummonedCreatureNames && getCaster() != null; - } + /** + * Called frequently so the entity can update its state every tick as required. For example, zombies and skeletons + * use this to react to sunlight and start to burn. + */ + public void onLivingUpdate() + { + if (!this.worldObj.isRemote) + { + if (this.isWet()) + { + this.attackEntityFrom(DamageSource.drown, 1.0F); + } + + --this.heightOffsetUpdateTime; + + if (this.heightOffsetUpdateTime <= 0) + { + this.heightOffsetUpdateTime = 100; + this.heightOffset = 0.5F + (float)this.rand.nextGaussian() * 3.0F; + } + + if (this.getEntityToAttack() != null && this.getEntityToAttack().posY + (double)this.getEntityToAttack().getEyeHeight() > this.posY + (double)this.getEyeHeight() + (double)this.heightOffset) + { + this.motionY += (0.30000001192092896D - this.motionY) * 0.30000001192092896D; + } + } + + if (this.rand.nextInt(24) == 0) + { + this.worldObj.playSoundEffect(this.posX + 0.5D, this.posY + 0.5D, this.posZ + 0.5D, "fire.fire", 1.0F + this.rand.nextFloat(), this.rand.nextFloat() * 0.7F + 0.3F); + } + + if (!this.onGround && this.motionY < 0.0D) + { + this.motionY *= 0.6D; + } + + for (int i = 0; i < 2; ++i) + { + this.worldObj.spawnParticle("largesmoke", this.posX + (this.rand.nextDouble() - 0.5D) * (double)this.width, this.posY + this.rand.nextDouble() * (double)this.height, this.posZ + (this.rand.nextDouble() - 0.5D) * (double)this.width, 0.0D, 0.0D, 0.0D); + } + + super.onLivingUpdate(); + } + + /** + * Basic mob attack. Default to touch of death in EntityCreature. Overridden by each mob to define their attack. + */ + protected void attackEntity(Entity par1Entity, float par2) + { + if (this.attackTime <= 0 && par2 < 2.0F && par1Entity.boundingBox.maxY > this.boundingBox.minY && par1Entity.boundingBox.minY < this.boundingBox.maxY) + { + this.attackTime = 20; + this.attackEntityAsMob(par1Entity); + } + else if (par2 < 30.0F) + { + double d0 = par1Entity.posX - this.posX; + double d1 = par1Entity.boundingBox.minY + (double)(par1Entity.height / 2.0F) - (this.posY + (double)(this.height / 2.0F)); + double d2 = par1Entity.posZ - this.posZ; + + if (this.attackTime == 0) + { + ++this.someAttackCounter; + + if (this.someAttackCounter == 1) + { + this.attackTime = 60; + this.func_70844_e(true); + } + else if (this.someAttackCounter <= 4) + { + this.attackTime = 6; + } + else + { + this.attackTime = 100; + this.someAttackCounter = 0; + this.func_70844_e(false); + } + + if (this.someAttackCounter > 1) + { + float f1 = MathHelper.sqrt_float(par2) * 0.5F; + this.worldObj.playAuxSFXAtEntity((EntityPlayer)null, 1009, (int)this.posX, (int)this.posY, (int)this.posZ, 0); + + for (int i = 0; i < 1; ++i) + { + // Passing in the caster as the shooter of the fireball seems to cause the fireball to + // instantly disappear, so this has been left out for the time being. + EntitySmallFireball entitysmallfireball = new EntitySmallFireball(this.worldObj, this, d0 + this.rand.nextGaussian() * (double)f1, d1, d2 + this.rand.nextGaussian() * (double)f1); + entitysmallfireball.posX = this.posX; + entitysmallfireball.posZ = this.posZ; + entitysmallfireball.posY = this.posY + (double)(this.height / 2.0F) + 0.5D; + this.worldObj.spawnEntityInWorld(entitysmallfireball); + } + } + } + + this.rotationYaw = (float)(Math.atan2(d2, d0) * 180.0D / Math.PI) - 90.0F; + this.hasAttacked = true; + } + } + + /** + * Called when the mob is falling. Calculates and applies fall damage. + */ + protected void fall(float par1) {} + + /** + * Returns the item ID for the item the mob drops on death. + */ + protected int getDropItemId() + { + return -1; + } + + /** + * Returns true if the entity is on fire. Used by render to add the fire effect on rendering. + */ + public boolean isBurning() + { + return this.func_70845_n(); + } + + /** + * Drop 0-2 items of this living's type. @param par1 - Whether this entity has recently been hit by a player. @param + * par2 - Level of Looting used to kill this mob. + */ + protected void dropFewItems(boolean par1, int par2) + { + + } + + public boolean func_70845_n() + { + return (this.dataWatcher.getWatchableObjectByte(16) & 1) != 0; + } + + public void func_70844_e(boolean par1) + { + byte b0 = this.dataWatcher.getWatchableObjectByte(16); + + if (par1) + { + b0 = (byte)(b0 | 1); + } + else + { + b0 &= -2; + } + + this.dataWatcher.updateObject(16, Byte.valueOf(b0)); + } + + /** + * Checks to make sure the light is not too bright where the mob is spawning + */ + protected boolean isValidLightLevel() + { + return true; + } } diff --git a/src/main/java/electroblob/wizardry/entity/living/EntityDecoy.java b/src/main/java/electroblob/wizardry/entity/living/EntityDecoy.java index f4d9a423..0727ade8 100644 --- a/src/main/java/electroblob/wizardry/entity/living/EntityDecoy.java +++ b/src/main/java/electroblob/wizardry/entity/living/EntityDecoy.java @@ -1,19 +1,28 @@ package electroblob.wizardry.entity.living; -import java.lang.ref.WeakReference; +import java.util.UUID; +import cpw.mods.fml.common.network.ByteBufUtils; +import electroblob.wizardry.EnumParticleType; import electroblob.wizardry.Wizardry; -import electroblob.wizardry.util.WizardryParticleType; +import electroblob.wizardry.WizardryUtilities; import io.netty.buffer.ByteBuf; +import net.minecraft.client.entity.AbstractClientPlayer; import net.minecraft.entity.EntityLivingBase; import net.minecraft.entity.SharedMonsterAttributes; +import net.minecraft.entity.ai.EntityAIControlledByPlayer; +import net.minecraft.entity.ai.EntityAIFollowParent; import net.minecraft.entity.ai.EntityAILookIdle; +import net.minecraft.entity.ai.EntityAIMate; +import net.minecraft.entity.ai.EntityAIPanic; import net.minecraft.entity.ai.EntityAISwimming; +import net.minecraft.entity.ai.EntityAITempt; import net.minecraft.entity.ai.EntityAIWander; import net.minecraft.entity.ai.EntityAIWatchClosest; import net.minecraft.entity.player.EntityPlayer; -import net.minecraft.util.DamageSource; -import net.minecraft.util.text.ITextComponent; +import net.minecraft.init.Items; +import net.minecraft.item.ItemStack; +import net.minecraft.util.ResourceLocation; import net.minecraft.world.World; public class EntityDecoy extends EntitySummonedCreature { @@ -24,66 +33,54 @@ public class EntityDecoy extends EntitySummonedCreature { public EntityDecoy(World world, double x, double y, double z, EntityLivingBase caster, int lifetime) { super(world, x, y, z, caster, lifetime); - this.setAlwaysRenderNameTag(caster instanceof EntityPlayer); - } - - @Override - protected void initEntityAI() { // Decoys just wander around aimlessly, watching anything living. this.tasks.addTask(0, new EntityAISwimming(this)); this.tasks.addTask(1, new EntityAIWander(this, 1.0D)); this.tasks.addTask(2, new EntityAIWatchClosest(this, EntityLivingBase.class, 6.0F)); this.tasks.addTask(3, new EntityAILookIdle(this)); + this.setAlwaysRenderNameTag(caster instanceof EntityPlayer); } @Override - public void onDespawn(){ - super.onDespawn(); + public void despawn(){ + super.despawn(); for(int i=0; i<20; i++){ - Wizardry.proxy.spawnParticle(WizardryParticleType.DUST, worldObj, this.posX + (this.rand.nextDouble()-0.5)*this.width, - this.getEntityBoundingBox().minY + this.rand.nextDouble()*this.height, this.posZ + (this.rand.nextDouble()-0.5)*this.width, + Wizardry.proxy.spawnParticle(EnumParticleType.DUST, worldObj, this.posX + (this.rand.nextDouble()-0.5)*this.width, + this.boundingBox.minY + this.rand.nextDouble()*this.height, this.posZ + (this.rand.nextDouble()-0.5)*this.width, 0, 0, 0, 40, 0.2f, 1.0f, 0.8f); } } @Override - public boolean isEntityInvulnerable(DamageSource source){ + public boolean isEntityInvulnerable() { return true; } @Override - public boolean isSneaking(){ - return false; + public ItemStack getHeldItem() { + if(this.getCaster() instanceof EntityPlayer || this.getCaster() == null) return super.getHeldItem(); + // Tricks the renderer into rendering the decoy's arm in the right place. + return this.getCaster().getHeldItem(); } @Override public void onUpdate() { super.onUpdate(); if(this.getCaster() == null || this.getCaster().isDead){ - this.setDead(); - this.onDespawn(); + this.despawn(); } } + @Override + protected boolean isAIEnabled(){ + return true; + } + @Override protected void applyEntityAttributes() { super.applyEntityAttributes(); - this.getEntityAttribute(SharedMonsterAttributes.MOVEMENT_SPEED).setBaseValue(0.25D); - } - - @Override - public ITextComponent getDisplayName(){ - if(getCaster() instanceof EntityPlayer){ - return this.getCaster().getDisplayName(); - }else{ - return super.getDisplayName(); - } - } - - @Override - public boolean hasCustomName(){ - return getCaster() instanceof EntityPlayer; + this.getEntityAttribute(SharedMonsterAttributes.movementSpeed).setBaseValue(0.25D); } @Override @@ -101,7 +98,7 @@ public class EntityDecoy extends EntitySummonedCreature { public void readSpawnData(ByteBuf data){ super.readSpawnData(data); if(!data.isReadable()) return; - this.setCasterReference(new WeakReference((EntityLivingBase)this.worldObj.getEntityByID(data.readInt()))); + this.setCaster((EntityLivingBase)this.worldObj.getEntityByID(data.readInt())); } } diff --git a/src/main/java/electroblob/wizardry/entity/living/EntityEvilWizard.java b/src/main/java/electroblob/wizardry/entity/living/EntityEvilWizard.java index a6fe6eb5..bcf44643 100644 --- a/src/main/java/electroblob/wizardry/entity/living/EntityEvilWizard.java +++ b/src/main/java/electroblob/wizardry/entity/living/EntityEvilWizard.java @@ -1,122 +1,114 @@ package electroblob.wizardry.entity.living; +import java.lang.ref.WeakReference; import java.util.ArrayList; import java.util.Arrays; +import java.util.Collections; +import java.util.Iterator; import java.util.List; import java.util.Locale; -import com.google.common.base.Predicate; - +import cpw.mods.fml.common.network.simpleimpl.IMessage; +import cpw.mods.fml.common.registry.IEntityAdditionalSpawnData; +import cpw.mods.fml.relauncher.Side; +import cpw.mods.fml.relauncher.SideOnly; +import electroblob.wizardry.EnumElement; +import electroblob.wizardry.EnumParticleType; +import electroblob.wizardry.EnumTier; import electroblob.wizardry.Wizardry; -import electroblob.wizardry.constants.Element; -import electroblob.wizardry.constants.Tier; +import electroblob.wizardry.WizardryRegistry; +import electroblob.wizardry.WizardryUtilities; import electroblob.wizardry.item.ItemSpellBook; -import electroblob.wizardry.registry.Spells; -import electroblob.wizardry.registry.WizardryAchievements; -import electroblob.wizardry.registry.WizardryItems; -import electroblob.wizardry.registry.WizardryPotions; -import electroblob.wizardry.registry.WizardrySounds; +import electroblob.wizardry.packet.PacketCastSpell; +import electroblob.wizardry.packet.WizardryPacketHandler; import electroblob.wizardry.spell.Spell; -import electroblob.wizardry.util.SpellModifiers; -import electroblob.wizardry.util.WizardryParticleType; -import electroblob.wizardry.util.WizardryUtilities; import io.netty.buffer.ByteBuf; +import net.minecraft.command.IEntitySelector; import net.minecraft.entity.Entity; +import net.minecraft.entity.EntityAgeable; import net.minecraft.entity.EntityList; +import net.minecraft.entity.EntityLiving; import net.minecraft.entity.EntityLivingBase; import net.minecraft.entity.IEntityLivingData; +import net.minecraft.entity.IRangedAttackMob; import net.minecraft.entity.SharedMonsterAttributes; +import net.minecraft.entity.ai.EntityAIArrowAttack; import net.minecraft.entity.ai.EntityAIHurtByTarget; +import net.minecraft.entity.ai.EntityAILookAtTradePlayer; import net.minecraft.entity.ai.EntityAIMoveTowardsRestriction; import net.minecraft.entity.ai.EntityAINearestAttackableTarget; import net.minecraft.entity.ai.EntityAIOpenDoor; import net.minecraft.entity.ai.EntityAIRestrictOpenDoor; import net.minecraft.entity.ai.EntityAISwimming; +import net.minecraft.entity.ai.EntityAITradePlayer; import net.minecraft.entity.ai.EntityAIWander; +import net.minecraft.entity.ai.EntityAIWatchClosest; import net.minecraft.entity.ai.EntityAIWatchClosest2; import net.minecraft.entity.monster.EntityMob; +import net.minecraft.entity.monster.IMob; +import net.minecraft.entity.passive.EntityVillager; import net.minecraft.entity.player.EntityPlayer; -import net.minecraft.init.SoundEvents; -import net.minecraft.inventory.EntityEquipmentSlot; +import net.minecraft.init.Blocks; +import net.minecraft.init.Items; import net.minecraft.item.ItemStack; import net.minecraft.nbt.NBTTagCompound; -import net.minecraft.nbt.NBTTagInt; -import net.minecraft.network.datasync.DataParameter; -import net.minecraft.network.datasync.DataSerializers; -import net.minecraft.network.datasync.EntityDataManager; -import net.minecraft.pathfinding.PathNavigateGround; +import net.minecraft.nbt.NBTTagIntArray; +import net.minecraft.nbt.NBTTagList; +import net.minecraft.potion.Potion; +import net.minecraft.potion.PotionEffect; import net.minecraft.util.DamageSource; -import net.minecraft.util.EnumHand; -import net.minecraft.util.ResourceLocation; -import net.minecraft.util.SoundEvent; -import net.minecraft.world.DifficultyInstance; +import net.minecraft.village.MerchantRecipe; +import net.minecraft.village.MerchantRecipeList; import net.minecraft.world.World; import net.minecraftforge.common.util.Constants.NBT; -import net.minecraftforge.fml.common.registry.IEntityAdditionalSpawnData; +import net.minecraftforge.oredict.OreDictionary; -public class EntityEvilWizard extends EntityMob implements ISpellCaster, IEntityAdditionalSpawnData { - - private EntityAIAttackSpell spellCastingAI = new EntityAIAttackSpell(this, 0.5D, 14.0F, 30, 50); +public class EntityEvilWizard extends EntityMob implements IRangedAttackMob, IEntityAdditionalSpawnData { + + private EntityAIArrowAttack aiArrowAttack = new EntityAIArrowAttack(this, 0.5D, 20, 50, 14.0F); public int textureIndex = 0; - + public boolean hasTower = false; + + /** The entity selector passed into the new AI methods. */ + protected IEntitySelector targetSelector; + + /** Index for the heal cooldown field in the datawatcher */ + private static final int healCooldownIndex = 20; + /** Index for the element field in the datawatcher */ + private static final int elementIndex = 16; + + private int[] spells = new int[4]; - /** The entity selector passed into the new AI methods. */ - protected Predicate targetSelector; + public EntityEvilWizard(World par1World) + { + super(par1World); + this.setSize(0.6F, 1.8F); + this.dataWatcher.addObject(healCooldownIndex, 0); + this.getNavigator().setBreakDoors(true); + this.getNavigator().setAvoidsWater(true); + this.tasks.taskEntries.clear(); + this.tasks.addTask(0, new EntityAISwimming(this)); + this.tasks.addTask(4, new EntityAIRestrictOpenDoor(this)); + this.tasks.addTask(5, new EntityAIOpenDoor(this, true)); + this.tasks.addTask(6, new EntityAIMoveTowardsRestriction(this, 0.6D)); + this.tasks.addTask(7, new EntityAIWatchClosest2(this, EntityPlayer.class, 3.0F, 1.0F)); + this.tasks.addTask(7, new EntityAIWander(this, 0.6D)); + + this.targetSelector = new IEntitySelector(){ - /** Data parameter for the cooldown time for wizards healing themselves. */ - private static final DataParameter HEAL_COOLDOWN = EntityDataManager.createKey(EntityEvilWizard.class, DataSerializers.VARINT); - /** Data parameter for the wizard's element. */ - private static final DataParameter ELEMENT = EntityDataManager.createKey(EntityEvilWizard.class, DataSerializers.VARINT); - /** The resource location for the evil wizard's loot table. */ - private static final ResourceLocation LOOT_TABLE = new ResourceLocation(Wizardry.MODID, "entities/evil_wizard"); + public boolean isEntityApplicable(Entity entity){ - // Field implementations - private List spells = new ArrayList(4); - private Spell continuousSpell; - - public EntityEvilWizard(World world){ - - super(world); - this.setSize(0.6F, 1.8F); - ((PathNavigateGround)this.getNavigator()).setBreakDoors(true); - - // For some reason this can't be done in initEntityAI - this.tasks.addTask(3, this.spellCastingAI); - - this.detachHome(); - } - - @Override - protected void entityInit(){ - super.entityInit(); - this.dataManager.register(HEAL_COOLDOWN, -1); - this.dataManager.register(ELEMENT, 0); - } - - @Override - protected void initEntityAI(){ - this.tasks.addTask(0, new EntityAISwimming(this)); - this.tasks.addTask(4, new EntityAIRestrictOpenDoor(this)); - this.tasks.addTask(5, new EntityAIOpenDoor(this, true)); - this.tasks.addTask(6, new EntityAIMoveTowardsRestriction(this, 0.6D)); - this.tasks.addTask(7, new EntityAIWatchClosest2(this, EntityPlayer.class, 3.0F, 1.0F)); - this.tasks.addTask(7, new EntityAIWander(this, 0.6D)); - - this.targetSelector = new Predicate(){ - - public boolean apply(Entity entity){ - - // If the target is valid and not invisible... - if(entity != null && !entity.isInvisible() && WizardryUtilities.isValidTarget(EntityEvilWizard.this, entity)){ + // If the target is valid... + if(entity != null && WizardryUtilities.isValidTarget(EntityEvilWizard.this, entity)){ //... and is a player, a summoned creature, another (non-evil) wizard ... - if(entity instanceof EntityPlayer || (entity instanceof ISummonedCreature || entity instanceof EntityWizard + if(entity instanceof EntityPlayer || (entity instanceof EntitySummonedCreature || entity instanceof EntityWizard // ... or in the whitelist ... - || Arrays.asList(Wizardry.settings.summonedCreatureTargetsWhitelist).contains(EntityList.getEntityString(entity).toLowerCase(Locale.ROOT))) + || Arrays.asList(Wizardry.summonedCreatureTargetsWhitelist).contains(EntityList.getEntityString(entity).toLowerCase(Locale.ROOT))) // ... and isn't in the blacklist ... - && !Arrays.asList(Wizardry.settings.summonedCreatureTargetsBlacklist).contains(EntityList.getEntityString(entity).toLowerCase(Locale.ROOT))){ + && !Arrays.asList(Wizardry.summonedCreatureTargetsBlacklist).contains(EntityList.getEntityString(entity).toLowerCase(Locale.ROOT))){ // ... it can be attacked. return true; } @@ -125,231 +117,323 @@ public class EntityEvilWizard extends EntityMob implements ISpellCaster, IEntity return false; } }; - - this.targetTasks.addTask(1, new EntityAIHurtByTarget(this, true)); - this.targetTasks.addTask(0, new EntityAINearestAttackableTarget(this, EntityLivingBase.class, - 0, false, true, this.targetSelector)); - } + + this.targetTasks.addTask(1, new EntityAIHurtByTarget(this, true)); + this.targetTasks.addTask(0, new EntityAINearestAttackableTarget(this, EntityLivingBase.class, 0, false, true, this.targetSelector)); + + this.tasks.addTask(3, this.aiArrowAttack); + + this.detachHome(); + } + + protected void entityInit(){ + super.entityInit(); + this.dataWatcher.addObject(16, Integer.valueOf(0)); + } - @Override - protected void applyEntityAttributes() - { - super.applyEntityAttributes(); - this.getEntityAttribute(SharedMonsterAttributes.MOVEMENT_SPEED).setBaseValue(0.5D); - this.getEntityAttribute(SharedMonsterAttributes.MAX_HEALTH).setBaseValue(30); - } + protected void applyEntityAttributes() + { + super.applyEntityAttributes(); + this.getEntityAttribute(SharedMonsterAttributes.movementSpeed).setBaseValue(0.5D); + this.getEntityAttribute(SharedMonsterAttributes.maxHealth).setBaseValue(30); + } - private int getHealCooldown(){ - return this.dataManager.get(HEAL_COOLDOWN); - } - - private void setHealCooldown(int cooldown){ - this.dataManager.set(HEAL_COOLDOWN, cooldown); - } - - public Element getElement(){ - return Element.values()[this.dataManager.get(ELEMENT)]; - } - - public void setElement(Element element){ - this.dataManager.set(ELEMENT, element.ordinal()); - } - - @Override - public List getSpells(){ - return this.spells; - } - - @Override - public SpellModifiers getModifiers(){ - return new SpellModifiers(); - } - - @Override - public void setContinuousSpell(Spell spell){ - this.continuousSpell = spell; - } - - @Override - public Spell getContinuousSpell(){ - return this.continuousSpell; - } - - @Override - public void onLivingUpdate(){ - - super.onLivingUpdate(); - - int healCooldown = this.getHealCooldown(); - - // This is now done slightly differently because isPotionActive doesn't work on client here, meaning that when - // affected with arcane jammer and healCooldown == 0, whilst the wizard didn't actually heal or play the sound, - // the particles still spawned, and since healCooldown wasn't reset they spawned every tick until the arcane - // jammer wore off. - if(healCooldown == 0 && this.getHealth() < this.getMaxHealth() && this.getHealth() > 0 && !this.isPotionActive(WizardryPotions.arcane_jammer)){ - - // Healer wizards use greater heal. - this.heal(this.getElement() == Element.HEALING ? 8 : 4); - this.setHealCooldown(-1); - - // deathTime == 0 checks the wizard isn't currently dying - }else if(healCooldown == -1 && this.deathTime == 0){ - - // Heal particles + /** + * Returns true if the newer Entity AI code should be run + */ + public boolean isAIEnabled() + { + return true; + } + + @Override + public void onLivingUpdate(){ + + super.onLivingUpdate(); + + int healCooldown = this.dataWatcher.getWatchableObjectInt(healCooldownIndex); + + // This is now done slightly differently because isPotionActive doesn't work on client here, meaning that when + // affected with arcane jammer and healCooldown == 0, whilst the wizard didn't actually heal or play the sound, + // the particles still spawned, and since healCooldown wasn't reset they spawned every tick until the arcane + // jammer wore off. + if(healCooldown == 0 && this.getHealth() < this.getMaxHealth() && this.getHealth() > 0 && !this.isPotionActive(Wizardry.arcaneJammer)){ + + // Healer wizards use greater heal. + this.heal(this.getElement() == EnumElement.HEALING ? 8 : 4); + this.dataWatcher.updateObject(healCooldownIndex, -1); + + // deathTime == 0 checks the wizard isn't currently dying + }else if(healCooldown == -1 && this.deathTime == 0){ + + // Heal particles if(worldObj.isRemote){ for(int i=0; i<10; i++){ double d0 = (double)((float)this.posX + rand.nextFloat()*2 - 1.0F); // Apparently the client side spawns the particles 1 block higher than it should... hence the - 0.5F. double d1 = (double)((float)this.posY - 0.5F + rand.nextFloat()); double d2 = (double)((float)this.posZ + rand.nextFloat()*2 - 1.0F); - Wizardry.proxy.spawnParticle(WizardryParticleType.SPARKLE, worldObj, d0, d1, d2, 0, 0.1F, 0, 48 + rand.nextInt(12), 1.0f, 1.0f, 0.3f); + Wizardry.proxy.spawnParticle(EnumParticleType.SPARKLE, worldObj, d0, d1, d2, 0, 0.1F, 0, 48 + rand.nextInt(12), 1.0f, 1.0f, 0.3f); } }else{ - if(this.getHealth() < 10){ - // Wizard heals himself more often if he has low health - this.setHealCooldown(150); - }else{ - this.setHealCooldown(400); - } - - this.playSound(WizardrySounds.SPELL_HEAL, 0.7F, rand.nextFloat() * 0.4F + 1.0F); + if(this.getHealth() < 10){ + // Wizard heals himself more often if he has low health + this.dataWatcher.updateObject(healCooldownIndex, 150); + }else{ + this.dataWatcher.updateObject(healCooldownIndex, 400); + } + + worldObj.playSoundAtEntity(this, "wizardry:heal", 0.7F, rand.nextFloat() * 0.4F + 1.0F); } - } - if(healCooldown > 0){ - this.setHealCooldown(healCooldown - 1); - } + } + if(healCooldown > 0){// && !worldObj.isRemote){ + this.dataWatcher.updateObject(healCooldownIndex, healCooldown-1); + } + } + + @Override + public void attackEntityWithRangedAttack(EntityLivingBase target, float f){ + + if(f < 30.0F && !this.isPotionActive(Wizardry.arcaneJammer)){ + + double d0 = target.posX - this.posX; + double d1 = target.boundingBox.minY + (double)(target.height / 2.0F) - (this.posY + (double)(this.height / 2.0F)); + double d2 = target.posZ - this.posZ; + + if (this.attackTime == 0 && spells.length > 0){ + + if(!this.worldObj.isRemote){ + + // New way of choosing a spell; keeps trying until one works or all have been tried + + List spellsArray = new ArrayList(spells.length); + + for(int i=0; i= 4 && Spell.get(stack.getItemDamage()).canBeCastByNPCs()){ - this.spells.set(rand.nextInt(3)+1, Spell.get(stack.getItemDamage())); - return true; - } - } + /** + * (abstract) Protected helper method to read subclass entity data from NBT. + */ + public void readEntityFromNBT(NBTTagCompound tagcompound) + { + super.readEntityFromNBT(tagcompound); + this.setElement(EnumElement.values()[tagcompound.getInteger("element")]); + this.textureIndex = tagcompound.getInteger("skin"); + this.spells = tagcompound.getIntArray("spells"); + this.hasTower = tagcompound.getBoolean("hasTower"); + } - return false; - } + /** + * Determines if an entity can be despawned, used on idle far away entities + */ + @Override + protected boolean canDespawn(){ + // Evil wizards can only despawn if they don't have a tower (i.e. if they spawned naturally at night) + return !this.hasTower; + } - @Override - public void writeEntityToNBT(NBTTagCompound nbt){ - super.writeEntityToNBT(nbt); - nbt.setInteger("element", this.getElement().ordinal()); - nbt.setInteger("skin", this.textureIndex); - nbt.setTag("spells", WizardryUtilities.listToNBT(spells, spell -> new NBTTagInt(spell.id()))); - nbt.setBoolean("hasTower", this.hasTower); - } + /** + * Returns the sound this mob makes while it's alive. + */ + protected String getLivingSound() + { + return "mob.villager.no"; + } - @Override - public void readEntityFromNBT(NBTTagCompound nbt){ - super.readEntityFromNBT(nbt); - this.setElement(Element.values()[nbt.getInteger("element")]); - this.textureIndex = nbt.getInteger("skin"); - this.spells = (List) WizardryUtilities.NBTToList(nbt.getTagList("spells", NBT.TAG_INT), - (NBTTagInt tag) -> Spell.get(tag.getInt())); - this.hasTower = nbt.getBoolean("hasTower"); - } + /** + * Returns the sound this mob makes when it is hurt. + */ + protected String getHurtSound() + { + return "mob.villager.hit"; + } - @Override - protected boolean canDespawn(){ - // Evil wizards can only despawn if they don't have a tower (i.e. if they spawned naturally at night) - return !this.hasTower; - } - - @Override - protected float getSoundPitch(){ - return (this.rand.nextFloat() - this.rand.nextFloat()) * 0.2F + 0.6F; - } + /** + * Returns the sound this mob makes on death. + */ + protected String getDeathSound() + { + return "mob.villager.death"; + } + + public EnumElement getElement(){ + return EnumElement.values()[this.dataWatcher.getWatchableObjectInt(elementIndex)]; + } + + public void setElement(EnumElement element){ + this.dataWatcher.updateObject(elementIndex, Integer.valueOf(element.ordinal())); + } + + @Override + protected void dropFewItems(boolean hitByPlayer, int lootingLevel){ + // Drops 3-5 crystals without looting bonuses + int j = 3 + this.rand.nextInt(3) + this.rand.nextInt(1 + lootingLevel); - @Override - protected SoundEvent getAmbientSound() { - return SoundEvents.ENTITY_WITCH_AMBIENT; - } + for(int k=0; k 0 && rand.nextInt(100) - lootingLevel < 5) this.entityDropItem(new ItemStack(Wizardry.spellBook, 1, this.spells[1 + rand.nextInt(this.spells.length - 1)]), 0); + } - @Override - protected SoundEvent getHurtSound() - { - return SoundEvents.ENTITY_WITCH_HURT; - } + public void onDeath(DamageSource source){ + + super.onDeath(source); + if(source.getEntity() instanceof EntityPlayer){ + ((EntityPlayer)source.getEntity()).triggerAchievement(Wizardry.defeatEvilWizard); + } + } - @Override - protected SoundEvent getDeathSound() - { - return SoundEvents.ENTITY_WITCH_DEATH; - } - - // Although it *looks* like this is still called, in actual fact the only method that calls it is overridden in - // EntityLiving to use the loot table system instead. This has been kept as a fallback in case the loot table is - // not found. - @Override - protected void dropFewItems(boolean hitByPlayer, int lootingLevel){ - // Drops 3-5 crystals without looting bonuses - int j = 3 + this.rand.nextInt(3) + this.rand.nextInt(1 + lootingLevel); - - for(int k=0; k 0 && rand.nextInt(100) - lootingLevel < 5) this.entityDropItem(new ItemStack(WizardryItems.spell_book, 1, this.spells.get(1 + rand.nextInt(this.spells.size() - 1)).id()), 0); - } - - @Override - protected ResourceLocation getLootTable(){ - return LOOT_TABLE; - } - - @Override - public void onDeath(DamageSource source){ - - super.onDeath(source); - if(source.getEntity() instanceof EntityPlayer){ - ((EntityPlayer)source.getEntity()).addStat(WizardryAchievements.defeat_evil_wizard); - } - } - - @Override - public IEntityLivingData onInitialSpawn(DifficultyInstance difficulty, IEntityLivingData data){ - - data = super.onInitialSpawn(difficulty, data); + public IEntityLivingData onSpawnWithEgg(IEntityLivingData par1EntityLivingData) + { + par1EntityLivingData = super.onSpawnWithEgg(par1EntityLivingData); textureIndex = this.rand.nextInt(6); + + if(rand.nextBoolean()){ + this.setElement(EnumElement.values()[rand.nextInt(EnumElement.values().length - 1) + 1]); + }else{ + this.setElement(EnumElement.MAGIC); + } + + EnumElement e = this.getElement(); + + this.setCurrentItemOrArmor(1, new ItemStack(WizardryUtilities.getArmour(e, 3))); + this.setCurrentItemOrArmor(2, new ItemStack(WizardryUtilities.getArmour(e, 2))); + this.setCurrentItemOrArmor(3, new ItemStack(WizardryUtilities.getArmour(e, 1))); + this.setCurrentItemOrArmor(4, new ItemStack(WizardryUtilities.getArmour(e, 0))); + + // This is the tier of the highest tier spell the wizard has. + EnumTier maxTier = EnumTier.BASIC; + + // Default chance is 0.085f, for reference. + this.setEquipmentDropChance(0, 0.1f); + this.setEquipmentDropChance(1, 0.1f); + this.setEquipmentDropChance(2, 0.1f); + this.setEquipmentDropChance(3, 0.1f); + this.setEquipmentDropChance(4, 0.1f); + + // All wizards know magic missile, even if it is disabled. + spells[0] = WizardryRegistry.magicMissile.id(); + + List npcSpells = Spell.getSpells(Spell.npcSpells); + + for(int i=1; i maxTier.ordinal()) maxTier = tier; + + // Finds all the spells of the chosen tier and element + List list = Spell.getSpells(new Spell.TierElementFilter(tier, element)); + // Keeps only spells which can be cast by NPCs + list.retainAll(npcSpells); + // Removes spells that the wizard already has + for(int j=0; j casterReference; - private UUID casterUUID; - - // Setter + getter implementations - @Override public int getLifetime(){ return lifetime; } - @Override public void setLifetime(int lifetime){ this.lifetime = lifetime; } - @Override public WeakReference getCasterReference(){ return casterReference; } - @Override public void setCasterReference(WeakReference reference){ casterReference = reference; } - @Override public UUID getCasterUUID() { return casterUUID; } - @Override public void setCasterUUID(UUID uuid) { this.casterUUID = uuid; } - - /** - * Default shell constructor, only used by client. Lifetime defaults arbitrarily to 600, but this doesn't - * matter because the client side entity immediately gets the lifetime value copied over to it by this class - * anyway. When extending this class, you must override this constructor or Minecraft won't like it, but there's - * no need to do anything inside it other than call super(). - */ public EntityIceGiant(World world){ - super(world); + super(world); this.setSize(1.4F, 2.9F); - this.experienceValue = 0; - } - - /** - * Set lifetime to -1 to allow this creature to last forever. This constructor should be overridden when - * extending this class (be sure to call super()) so that AI and other things can be added. - */ - public EntityIceGiant(World world, double x, double y, double z, EntityLivingBase caster, int lifetime){ - super(world); - this.setSize(1.4F, 2.9F); - this.setPosition(x, y, z); - this.casterReference = new WeakReference(caster); - this.experienceValue = 0; - this.lifetime = lifetime; - } + } - @Override - protected void initEntityAI(){ - this.getNavigator().getNodeProcessor().setCanSwim(false); - this.tasks.addTask(1, new EntityAIAttackMelee(this, 1.0D, true)); + public EntityIceGiant(World world, double x, double y, double z, EntityLivingBase caster, int lifetime){ + + super(world, x, y, z, caster, lifetime); + this.setSize(1.4F, 2.9F); + this.getNavigator().setAvoidsWater(true); + this.tasks.addTask(1, new EntityAIAttackOnCollide(this, 1.0D, true)); this.tasks.addTask(2, new EntityAIMoveTowardsTarget(this, 0.9D, 32.0F)); - //this.tasks.addTask(4, new EntityAIMoveTowardsRestriction(this, 1.0D)); - //this.tasks.addTask(5, new EntityAIWander(this, 0.6D)); + this.tasks.addTask(3, new EntityAIMoveThroughVillage(this, 0.6D, true)); + this.tasks.addTask(4, new EntityAIMoveTowardsRestriction(this, 1.0D)); + this.tasks.addTask(5, new EntityAIWander(this, 0.6D)); this.tasks.addTask(6, new EntityAIWatchClosest(this, EntityPlayer.class, 6.0F)); this.tasks.addTask(7, new EntityAILookIdle(this)); this.targetTasks.addTask(1, new EntityAIHurtByTarget(this, false)); - this.targetTasks.addTask(2, new EntityAINearestAttackableTarget(this, EntityLivingBase.class, - 0, false, true, this.getTargetSelector())); - } - - // EntityIronGolem overrides - - @Override protected void updateAITasks(){} // Disables home-checking - @Override public Village getVillage(){ return null; } - @Override public int getHoldRoseTick(){ return 0; } - - // Implementations - - @Override - public void setRevengeTarget(EntityLivingBase entity){ - if(this.shouldRevengeTarget(entity)) super.setRevengeTarget(entity); - } - - @Override - public void onUpdate(){ - super.onUpdate(); - this.updateDelegate(); - } - - @Override - public void onSpawn(){} - - @Override - public void onDespawn(){ - this.playSound(WizardrySounds.SPELL_FREEZE, 1.0f, 1.0f); - if(this.worldObj.isRemote){ - for(int i=0; i<30; i++){ - float brightness = 0.5f + (rand.nextFloat()/2); - Wizardry.proxy.spawnParticle(WizardryParticleType.SPARKLE, this.worldObj, this.posX-1 + rand.nextDouble()*2, this.posY+ rand.nextDouble()*3, this.posZ-1 + rand.nextDouble()*2, 0, -0.02, 0, 12 + rand.nextInt(8), brightness, brightness + 0.1f, 1.0f); - } - } - } - - @Override - public void onLivingUpdate(){ - - super.onLivingUpdate(); - - if(this.worldObj.isRemote){ - Wizardry.proxy.spawnParticle(WizardryParticleType.SNOW, this.worldObj, this.posX-1 + rand.nextDouble()*2, this.posY+ rand.nextDouble()*3, this.posZ-1 + rand.nextDouble()*2, 0, -0.02, 0, 40 + rand.nextInt(10)); - } + this.targetTasks.addTask(2, new EntityAINearestAttackableTarget(this, EntityLivingBase.class, 0, false, true, this.targetSelector)); + this.lifetime = lifetime; } - - @Override - public void onSuccessfulAttack(EntityLivingBase target){ - - target.motionY += 0.2; - target.motionX += this.getLookVec().xCoord*0.2; - target.motionZ += this.getLookVec().xCoord*0.2; - - target.addPotionEffect(new PotionEffect(WizardryPotions.frost, 300, 0)); - - this.applyEnchantments(this, target); - this.playSound(SoundEvents.ENTITY_IRONGOLEM_ATTACK, 1.0F, 1.0F); - } + protected void entityInit() + { + super.entityInit(); + } - @Override - public boolean hasParticleEffect() { + /** + * Returns true if the newer Entity AI code should be run + */ + public boolean isAIEnabled() + { + return true; + } + + @Override + public boolean hasRangedAttack() { return false; } - @Override - protected boolean processInteract(EntityPlayer player, EnumHand hand, ItemStack stack) { - // In this case, the delegate method determines whether super is called. - // Rather handily, we can make use of Java's short-circuiting method of evaluating OR statements. - return this.interactDelegate(player, hand, stack) || super.processInteract(player, hand, stack); + /** + * main AI tick function, replaces updateEntityActionState + */ + protected void updateAITick() + { + if (--this.homeCheckTimer <= 0) + { + this.homeCheckTimer = 70 + this.rand.nextInt(50); + this.villageObj = this.worldObj.villageCollectionObj.findNearestVillage(MathHelper.floor_double(this.posX), MathHelper.floor_double(this.posY), MathHelper.floor_double(this.posZ), 32); + + if (this.villageObj == null) + { + this.detachHome(); + } + else + { + ChunkCoordinates chunkcoordinates = this.villageObj.getCenter(); + this.setHomeArea(chunkcoordinates.posX, chunkcoordinates.posY, chunkcoordinates.posZ, (int)((float)this.villageObj.getVillageRadius() * 0.6F)); + } + } + + super.updateAITick(); + } + + protected void applyEntityAttributes() + { + super.applyEntityAttributes(); + this.getEntityAttribute(SharedMonsterAttributes.maxHealth).setBaseValue(100.0D); + this.getEntityAttribute(SharedMonsterAttributes.movementSpeed).setBaseValue(0.25D); + } + + /** + * Decrements the entity's air supply when underwater + */ + protected int decreaseAirSupply(int par1) + { + return par1; + } + + protected void collideWithEntity(Entity par1Entity) + { + if (par1Entity instanceof IMob && this.getRNG().nextInt(20) == 0) + { + this.setAttackTarget((EntityLivingBase)par1Entity); + } + + super.collideWithEntity(par1Entity); + } + + @Override + public void despawn(){ + this.playSound("wizardry:freeze", 1.0f, 1.0f); + if(this.worldObj.isRemote){ + for(int i=0; i<30; i++){ + float brightness = 0.5f + (rand.nextFloat()/2); + Wizardry.proxy.spawnParticle(EnumParticleType.SPARKLE, this.worldObj, this.posX-1 + rand.nextDouble()*2, this.posY+ rand.nextDouble()*3, this.posZ-1 + rand.nextDouble()*2, 0, -0.02, 0, 12 + rand.nextInt(8), brightness, brightness + 0.1f, 1.0f); + } + } + super.despawn(); } - @Override - public void writeEntityToNBT(NBTTagCompound nbttagcompound){ - super.writeEntityToNBT(nbttagcompound); - this.writeNBTDelegate(nbttagcompound); - } + /** + * Called frequently so the entity can update its state every tick as required. For example, zombies and skeletons + * use this to react to sunlight and start to burn. + */ + public void onLivingUpdate() + { + super.onLivingUpdate(); + + if(this.worldObj.isRemote){ + Wizardry.proxy.spawnParticle(EnumParticleType.SNOW, this.worldObj, this.posX-1 + rand.nextDouble()*2, this.posY+ rand.nextDouble()*3, this.posZ-1 + rand.nextDouble()*2, 0, -0.02, 0, 40 + rand.nextInt(10)); + } + + if (this.attackTimer > 0) + { + --this.attackTimer; + } + + if (this.motionX * this.motionX + this.motionZ * this.motionZ > 2.500000277905201E-7D && this.rand.nextInt(5) == 0) + { + int i = MathHelper.floor_double(this.posX); + int j = MathHelper.floor_double(this.posY - 0.20000000298023224D - (double)this.yOffset); + int k = MathHelper.floor_double(this.posZ); + Block block = this.worldObj.getBlock(i, j, k); - @Override - public void readEntityFromNBT(NBTTagCompound nbttagcompound){ - super.readEntityFromNBT(nbttagcompound); - this.writeNBTDelegate(nbttagcompound); - } + if (block.getMaterial() != Material.air) + { + this.worldObj.spawnParticle("blockcrack_" + Block.getIdFromBlock(block) + "_" + this.worldObj.getBlockMetadata(i, j, k), this.posX + ((double)this.rand.nextFloat() - 0.5D) * (double)this.width, this.boundingBox.minY + 0.1D, this.posZ + ((double)this.rand.nextFloat() - 0.5D) * (double)this.width, 4.0D * ((double)this.rand.nextFloat() - 0.5D), 0.5D, ((double)this.rand.nextFloat() - 0.5D) * 4.0D); + } + } + } - // Recommended overrides + /** + * Returns true if this entity can attack entities of the specified class. + */ + public boolean canAttackClass(Class par1Class) + { + return this.isPlayerCreated() && EntityPlayer.class.isAssignableFrom(par1Class) ? false : super.canAttackClass(par1Class); + } - @Override protected int getExperiencePoints(EntityPlayer player){ return 0; } - @Override protected boolean canDropLoot(){ return false; } - @Override protected Item getDropItem(){ return null; } - @Override protected ResourceLocation getLootTable(){ return null; } - @Override public boolean canPickUpLoot(){ return false; } - // This vanilla method has nothing to do with the custom onDespawn() method. - @Override protected boolean canDespawn(){ return false; } + public boolean attackEntityAsMob(Entity par1Entity){ + + this.attackTimer = 10; + this.worldObj.setEntityState(this, (byte)4); + boolean flag = par1Entity.attackEntityFrom(MagicDamage.causeIndirectEntityMagicDamage(this, this.getCaster(), DamageType.FROST), (float)(7 + this.rand.nextInt(15))); - @Override - public boolean canAttackClass(Class entityType){ - // Returns true unless the given entity type is a flying entity. - return !EntityFlying.class.isAssignableFrom(entityType); - } + if(flag){ + + par1Entity.motionY += 0.2; + par1Entity.motionX += this.getLookVec().xCoord*0.2; + par1Entity.motionZ += this.getLookVec().xCoord*0.2; + + if(par1Entity instanceof EntityLivingBase){ + ((EntityLivingBase) par1Entity).addPotionEffect(new PotionEffect(Wizardry.frost.id, 300, 0)); + } + } - @Override - public ITextComponent getDisplayName(){ - if(getCaster() != null){ - return new TextComponentTranslation(NAMEPLATE_TRANSLATION_KEY, getCaster().getName(), - new TextComponentTranslation("entity." + this.getEntityString() + ".name")); - }else{ - return super.getDisplayName(); - } - } + this.playSound("step.stone", 1.0F, rand.nextFloat() * 0.2F + 0.9F); + return flag; + } - @Override - public boolean hasCustomName(){ - // If this returns true, the renderer will show the nameplate when looking directly at the entity - return Wizardry.settings.showSummonedCreatureNames && getCaster() != null; - } + @SideOnly(Side.CLIENT) + public void handleHealthUpdate(byte par1) + { + if (par1 == 4) + { + this.attackTimer = 10; + this.playSound("wizardry:ice", 1.0F, rand.nextFloat() * 0.1F + 0.2F); + } + else + { + super.handleHealthUpdate(par1); + } + } + + public Village getVillage() + { + return this.villageObj; + } + + @SideOnly(Side.CLIENT) + public int getAttackTimer() + { + return this.attackTimer; + } + + /** + * Returns the sound this mob makes while it's alive. + */ + protected String getLivingSound() + { + return "none"; + } + + /** + * Returns the sound this mob makes when it is hurt. + */ + protected String getHurtSound() + { + return "step.stone"; + } + + /** + * Returns the sound this mob makes on death. + */ + protected String getDeathSound() + { + return "wizardry:freeze"; + } + + /** + * Plays step sound at given x, y, z for the entity + */ + protected void playStepSound(int par1, int par2, int par3, int par4) + { + this.playSound(Blocks.ice.stepSound.soundName, 1.0F, 1.0F); + } + + /** + * Drop 0-2 items of this living's type. @param par1 - Whether this entity has recently been hit by a player. @param + * par2 - Level of Looting used to kill this mob. + */ + protected void dropFewItems(boolean par1, int par2) + { + + } + + public boolean isPlayerCreated() + { + return true; + } } diff --git a/src/main/java/electroblob/wizardry/entity/living/EntityIceWraith.java b/src/main/java/electroblob/wizardry/entity/living/EntityIceWraith.java index fa89a1d0..9b73d298 100644 --- a/src/main/java/electroblob/wizardry/entity/living/EntityIceWraith.java +++ b/src/main/java/electroblob/wizardry/entity/living/EntityIceWraith.java @@ -1,296 +1,279 @@ package electroblob.wizardry.entity.living; +import cpw.mods.fml.relauncher.Side; +import cpw.mods.fml.relauncher.SideOnly; +import electroblob.wizardry.EnumParticleType; import electroblob.wizardry.Wizardry; -import electroblob.wizardry.registry.Spells; -import electroblob.wizardry.registry.WizardrySounds; -import electroblob.wizardry.util.SpellModifiers; -import electroblob.wizardry.util.WizardryParticleType; +import electroblob.wizardry.entity.projectile.EntityIceShard; +import net.minecraft.entity.Entity; +import net.minecraft.entity.EntityLiving; import net.minecraft.entity.EntityLivingBase; -import net.minecraft.entity.ai.EntityAIBase; +import net.minecraft.entity.SharedMonsterAttributes; +import net.minecraft.entity.ai.EntityAIHurtByTarget; import net.minecraft.entity.ai.EntityAILookIdle; -import net.minecraft.entity.ai.EntityAIMoveTowardsRestriction; +import net.minecraft.entity.ai.EntityAINearestAttackableTarget; import net.minecraft.entity.ai.EntityAIWander; -import net.minecraft.entity.ai.EntityAIWatchClosest; -import net.minecraft.entity.monster.EntityBlaze; -import net.minecraft.entity.player.EntityPlayer; -import net.minecraft.init.MobEffects; -import net.minecraft.util.DamageSource; -import net.minecraft.util.EnumHand; -import net.minecraft.util.EnumParticleTypes; -import net.minecraft.util.math.MathHelper; +import net.minecraft.entity.monster.IMob; import net.minecraft.world.World; -public class EntityIceWraith extends EntityBlazeMinion { +public class EntityIceWraith extends EntitySummonedCreature +{ + /** Random offset used in floating behaviour */ + private float heightOffset = 0.5F; - /** The version from EntityLivingBase is only used in onLivingUpdate, so it can safely be copied. */ - private int jumpTicks; + /** ticks until heightOffset is randomized */ + private int heightOffsetUpdateTime; + private int field_70846_g; public EntityIceWraith(World world){ super(world); } public EntityIceWraith(World world, double x, double y, double z, EntityLivingBase caster, int lifetime){ + super(world, x, y, z, caster, lifetime); this.isImmuneToFire = false; + this.tasks.addTask(1, new EntityAIWander(this, 1.0D)); + this.tasks.addTask(2, new EntityAILookIdle(this)); + this.targetTasks.addTask(1, new EntityAIHurtByTarget(this, true)); + this.targetTasks.addTask(2, new EntityAINearestAttackableTarget(this, EntityLiving.class, 0, false, true, IMob.mobSelector)); + this.experienceValue = 0; } - - @Override - protected void initEntityAI(){ - super.initEntityAI(); - this.tasks.taskEntries.clear(); - this.tasks.addTask(4, new AIIceShardAttack(this)); - this.tasks.addTask(5, new EntityAIMoveTowardsRestriction(this, 1.0D)); - this.tasks.addTask(7, new EntityAIWander(this, 1.0D)); - this.tasks.addTask(8, new EntityAIWatchClosest(this, EntityPlayer.class, 8.0F)); - this.tasks.addTask(8, new EntityAILookIdle(this)); + + /** + * Returns true if the newer Entity AI code should be run + */ + protected boolean isAIEnabled() + { + return false; } @Override - protected void spawnParticleEffect() { + public boolean hasRangedAttack() { + return true; + } + + protected void applyEntityAttributes() + { + super.applyEntityAttributes(); + this.getEntityAttribute(SharedMonsterAttributes.attackDamage).setBaseValue(6.0D); + this.getEntityAttribute(SharedMonsterAttributes.followRange).setBaseValue(16.0D); + } + + protected void entityInit() + { + super.entityInit(); + this.dataWatcher.addObject(16, new Byte((byte)0)); + } + + /** + * Returns the sound this mob makes while it's alive. + */ + protected String getLivingSound() + { + return "mob.blaze.breathe"; + } + + /** + * Returns the sound this mob makes when it is hurt. + */ + protected String getHurtSound() + { + return "mob.blaze.hit"; + } + + /** + * Returns the sound this mob makes on death. + */ + protected String getDeathSound() + { + return "mob.blaze.death"; + } + + @SideOnly(Side.CLIENT) + public int getBrightnessForRender(float par1) + { + return 15728880; + } + + /** + * Gets how bright this entity is. + */ + public float getBrightness(float par1) + { + return 1.0F; + } + + @Override + public void onSpawn(){ if(this.worldObj.isRemote){ for(int i=0;i<15;i++){ float brightness = 0.5f + (rand.nextFloat()/2); - Wizardry.proxy.spawnParticle(WizardryParticleType.SPARKLE, worldObj, this.posX - 0.5d + rand.nextDouble(), this.posY + this.height/2 - 0.5d + rand.nextDouble(), this.posZ - 0.5d + rand.nextDouble(), 0, 0.05f, 0, 20 + rand.nextInt(10), brightness, brightness + 0.1f, 1.0f); + Wizardry.proxy.spawnParticle(EnumParticleType.SPARKLE, worldObj, this.posX - 0.5d + rand.nextDouble(), this.posY + this.height/2 - 0.5d + rand.nextDouble(), this.posZ - 0.5d + rand.nextDouble(), 0, 0.05f, 0, 20 + rand.nextInt(10), brightness, brightness + 0.1f, 1.0f); } } } @Override - public void onLivingUpdate(){ - - if(!this.onGround && this.motionY < 0.0D){ - this.motionY *= 0.6D; - } - - if(this.rand.nextInt(24) == 0){ - this.playSound(WizardrySounds.SPELL_LOOP_WIND, 0.3F + this.rand.nextFloat()/4, this.rand.nextFloat() * 0.7F + 1.4F); - } - + public void despawn(){ if(this.worldObj.isRemote){ - for(int i = 0; i < 2; ++i){ - this.worldObj.spawnParticle(EnumParticleTypes.CLOUD, this.posX + (this.rand.nextDouble() - 0.5D) * (double)this.width, this.posY + this.rand.nextDouble() * (double)this.height, this.posZ + (this.rand.nextDouble() - 0.5D) * (double)this.width, 0.0D, 0.0D, 0.0D); + for(int i=0;i<15;i++){ + float brightness = 0.5f + (rand.nextFloat()/2); + Wizardry.proxy.spawnParticle(EnumParticleType.SPARKLE, worldObj, this.posX - 0.5d + rand.nextDouble(), this.posY + this.height/2 - 0.5d + rand.nextDouble(), this.posZ - 0.5d + rand.nextDouble(), 0, 0.05f, 0, 20 + rand.nextInt(10), brightness, brightness + 0.1f, 1.0f); + } + } + super.despawn(); + } + + /** + * Called frequently so the entity can update its state every tick as required. For example, zombies and skeletons + * use this to react to sunlight and start to burn. + */ + public void onLivingUpdate() + { + + if (!this.worldObj.isRemote) + { + /* + if (this.isWet()) + { + this.attackEntityFrom(DamageSource.drown, 1.0F); + } + */ + + --this.heightOffsetUpdateTime; + + if (this.heightOffsetUpdateTime <= 0) + { + this.heightOffsetUpdateTime = 100; + this.heightOffset = 0.5F + (float)this.rand.nextGaussian() * 3.0F; + } + + if (this.getEntityToAttack() != null && this.getEntityToAttack().posY + (double)this.getEntityToAttack().getEyeHeight() > this.posY + (double)this.getEyeHeight() + (double)this.heightOffset) + { + this.motionY += (0.30000001192092896D - this.motionY) * 0.30000001192092896D; } } - // Replaces super call. - this.livingBaseUpdate(); - } - - /** Copied from {@link EntityLivingBase#onLivingUpdate()}. The only change is removal of the updateElytra() call - * since that's irrelevant here. In actual fact, neither EntityMob nor EntityLiving has any code in its version - * of this method that is of use. This isn't exactly ideal, but it's the lesser of two evils since the - * alternative is copying the entire EntityBlaze class and its renderer. All to remove one particle effect... */ - // ... demonstrating why critical methods should delegate any non-critical functionality (like particles) to - // separate protected methods. - private void livingBaseUpdate(){ - - if (this.jumpTicks > 0) - { - --this.jumpTicks; - } + if (this.rand.nextInt(24) == 0) + { + this.worldObj.playSoundEffect(this.posX + 0.5D, this.posY + 0.5D, this.posZ + 0.5D, "wizardry:wind", 0.3F + this.rand.nextFloat()/4, this.rand.nextFloat() * 0.7F + 1.4F); + } - if (this.newPosRotationIncrements > 0 && !this.canPassengerSteer()) - { - double d0 = this.posX + (this.interpTargetX - this.posX) / (double)this.newPosRotationIncrements; - double d1 = this.posY + (this.interpTargetY - this.posY) / (double)this.newPosRotationIncrements; - double d2 = this.posZ + (this.interpTargetZ - this.posZ) / (double)this.newPosRotationIncrements; - double d3 = MathHelper.wrapDegrees(this.interpTargetYaw - (double)this.rotationYaw); - this.rotationYaw = (float)((double)this.rotationYaw + d3 / (double)this.newPosRotationIncrements); - this.rotationPitch = (float)((double)this.rotationPitch + (this.interpTargetPitch - (double)this.rotationPitch) / (double)this.newPosRotationIncrements); - --this.newPosRotationIncrements; - this.setPosition(d0, d1, d2); - this.setRotation(this.rotationYaw, this.rotationPitch); - } - else if (!this.isServerWorld()) - { - this.motionX *= 0.98D; - this.motionY *= 0.98D; - this.motionZ *= 0.98D; - } + if (!this.onGround && this.motionY < 0.0D) + { + this.motionY *= 0.6D; + } - if (Math.abs(this.motionX) < 0.003D) - { - this.motionX = 0.0D; - } + for (int i = 0; i < 2; ++i) + { + this.worldObj.spawnParticle("cloud", this.posX + (this.rand.nextDouble() - 0.5D) * (double)this.width, this.posY + this.rand.nextDouble() * (double)this.height, this.posZ + (this.rand.nextDouble() - 0.5D) * (double)this.width, 0.0D, 0.0D, 0.0D); + } - if (Math.abs(this.motionY) < 0.003D) - { - this.motionY = 0.0D; - } - - if (Math.abs(this.motionZ) < 0.003D) - { - this.motionZ = 0.0D; - } - - this.worldObj.theProfiler.startSection("ai"); - - if (this.isMovementBlocked()) - { - this.isJumping = false; - this.moveStrafing = 0.0F; - this.moveForward = 0.0F; - this.randomYawVelocity = 0.0F; - } - else if (this.isServerWorld()) - { - this.worldObj.theProfiler.startSection("newAi"); - this.updateEntityActionState(); - this.worldObj.theProfiler.endSection(); - } - - this.worldObj.theProfiler.endSection(); - this.worldObj.theProfiler.startSection("jump"); - - if (this.isJumping) - { - if (this.isInWater()) - { - this.handleJumpWater(); - } - else if (this.isInLava()) - { - this.handleJumpLava(); - } - else if (this.onGround && this.jumpTicks == 0) - { - this.jump(); - this.jumpTicks = 10; - } - } - else - { - this.jumpTicks = 0; - } - - this.worldObj.theProfiler.endSection(); - this.worldObj.theProfiler.startSection("travel"); - this.moveStrafing *= 0.98F; - this.moveForward *= 0.98F; - this.randomYawVelocity *= 0.9F; - this.moveEntityWithHeading(this.moveStrafing, this.moveForward); - this.worldObj.theProfiler.endSection(); - this.worldObj.theProfiler.startSection("push"); - this.collideWithNearbyEntities(); - this.worldObj.theProfiler.endSection(); + super.onLivingUpdate(); } - @Override - public boolean attackEntityFrom(DamageSource source, float amount){ - // Removes the damage from being wet that applies to blazes by checking if the mob is actually drowning. - if(source == DamageSource.drown && (this.getAir() > 0 || this.isPotionActive(MobEffects.WATER_BREATHING))){ - // In this case, the ice wraith is not actually drowning, so cancel the damage. - return false; - }else{ - return super.attackEntityFrom(source, amount); + /** + * Basic mob attack. Default to touch of death in EntityCreature. Overridden by each mob to define their attack. + */ + protected void attackEntity(Entity target, float par2) + { + if (this.attackTime <= 0 && par2 < 2.0F && target.boundingBox.maxY > this.boundingBox.minY && target.boundingBox.minY < this.boundingBox.maxY) + { + this.attackTime = 20; + this.attackEntityAsMob(target); + } + else if (par2 < 30.0F) + { + double d0 = target.posX - this.posX; + double d1 = target.boundingBox.minY + (double)(target.height / 2.0F) - (this.posY + (double)(this.height / 2.0F)); + double d2 = target.posZ - this.posZ; + + if (this.attackTime == 0) + { + ++this.field_70846_g; + + if (this.field_70846_g == 1) + { + this.attackTime = 60; + //this.func_70844_e(true); + } + else if (this.field_70846_g <= 4) + { + this.attackTime = 6; + } + else + { + this.attackTime = 100; + this.field_70846_g = 0; + //this.func_70844_e(false); + } + + if (this.field_70846_g > 1) + { + this.playSound("wizardry:ice", 1.0F, rand.nextFloat() * 0.4F + 1.4F); + + EntityIceShard iceshard = new EntityIceShard(this.worldObj, this, target, 2, 4, 1); + iceshard.setShootingEntity(this.getCaster()); + //iceshard.posY = this.posY + (double)(this.height / 2.0F) + 0.5D; + //iceshard.setShootingEntity(this); + this.worldObj.spawnEntityInWorld(iceshard); + } + } + + this.rotationYaw = (float)(Math.atan2(d2, d0) * 180.0D / Math.PI) - 90.0F; + this.hasAttacked = true; } } - - @Override - public boolean isBurning(){ - // Uses the datawatcher on both sides because fire is private to Entity (and I'm not using reflection here). - // TESTME: This should work, but there may be some issues with updating, so if it doesn't work, copy the - // version from Entity and use reflection to access the fire field. - return this.getFlag(0); - } - /** Copied straight from EntityBlaze.AIFireballAttack, with the only changes being replacement of fireball - * spawning with a one-liner call to WizardryRegistry.iceShard.cast(...) and the removal of redundant local variables. */ - static class AIIceShardAttack extends EntityAIBase { - - private final EntityBlaze blaze; - private int attackStep; - private int attackTime; + /** + * Called when the mob is falling. Calculates and applies fall damage. + */ + protected void fall(float par1) {} - public AIIceShardAttack(EntityBlaze blazeIn) - { - this.blaze = blazeIn; - this.setMutexBits(3); - } + /** + * Returns the item ID for the item the mob drops on death. + */ + protected int getDropItemId() + { + return -1; + } - /** - * Returns whether the EntityAIBase should begin execution. - */ - public boolean shouldExecute() - { - EntityLivingBase entitylivingbase = this.blaze.getAttackTarget(); - return entitylivingbase != null && entitylivingbase.isEntityAlive(); - } + /** + * Drop 0-2 items of this living's type. @param par1 - Whether this entity has recently been hit by a player. @param + * par2 - Level of Looting used to kill this mob. + */ + protected void dropFewItems(boolean par1, int par2) + { - /** - * Execute a one shot task or start executing a continuous task - */ - public void startExecuting() - { - this.attackStep = 0; - } + } - /** - * Resets the task - */ - public void resetTask() - { - // This might be called setOnFire, but what it really controls is whether the wraith is in attack mode. - this.blaze.setOnFire(false); - } + public boolean func_70845_n() + { + return (this.dataWatcher.getWatchableObjectByte(16) & 1) != 0; + } - /** - * Updates the task - */ - public void updateTask() - { - --this.attackTime; - EntityLivingBase entitylivingbase = this.blaze.getAttackTarget(); - double d0 = this.blaze.getDistanceSqToEntity(entitylivingbase); + public void func_70844_e(boolean par1) + { + byte b0 = this.dataWatcher.getWatchableObjectByte(16); - if (d0 < 4.0D) - { - if (this.attackTime <= 0) - { - this.attackTime = 20; - this.blaze.attackEntityAsMob(entitylivingbase); - } + if (par1) + { + b0 = (byte)(b0 | 1); + } + else + { + b0 &= -2; + } - this.blaze.getMoveHelper().setMoveTo(entitylivingbase.posX, entitylivingbase.posY, entitylivingbase.posZ, 1.0D); - } - else if (d0 < 256.0D) - { - if (this.attackTime <= 0) - { - ++this.attackStep; + this.dataWatcher.updateObject(16, Byte.valueOf(b0)); + } - if (this.attackStep == 1) - { - this.attackTime = 60; - this.blaze.setOnFire(true); - } - else if (this.attackStep <= 4) - { - this.attackTime = 6; - } - else - { - this.attackTime = 100; - this.attackStep = 0; - this.blaze.setOnFire(false); - } - - if(this.attackStep > 1){ - // Proof, if it were at all needed, of the elegance and versatility of the spell system. - Spells.ice_shard.cast(this.blaze.worldObj, this.blaze, EnumHand.MAIN_HAND, 0, entitylivingbase, new SpellModifiers()); - // TODO: Decide if an event should be fired here. I'm guessing no. - } - } - - this.blaze.getLookHelper().setLookPositionWithEntity(entitylivingbase, 10.0F, 10.0F); - } - else - { - this.blaze.getNavigator().clearPathEntity(); - this.blaze.getMoveHelper().setMoveTo(entitylivingbase.posX, entitylivingbase.posY, entitylivingbase.posZ, 1.0D); - } - - super.updateTask(); - } - } + /** + * Checks to make sure the light is not too bright where the mob is spawning + */ + protected boolean isValidLightLevel() + { + return true; + } } diff --git a/src/main/java/electroblob/wizardry/entity/living/EntityLightningWraith.java b/src/main/java/electroblob/wizardry/entity/living/EntityLightningWraith.java index d2287d17..3c139bdd 100644 --- a/src/main/java/electroblob/wizardry/entity/living/EntityLightningWraith.java +++ b/src/main/java/electroblob/wizardry/entity/living/EntityLightningWraith.java @@ -1,180 +1,304 @@ package electroblob.wizardry.entity.living; +import java.util.List; + +import cpw.mods.fml.relauncher.Side; +import cpw.mods.fml.relauncher.SideOnly; +import electroblob.wizardry.EnumParticleType; +import electroblob.wizardry.MagicDamage; import electroblob.wizardry.Wizardry; -import electroblob.wizardry.registry.Spells; -import electroblob.wizardry.util.SpellModifiers; -import electroblob.wizardry.util.WizardryParticleType; +import electroblob.wizardry.WizardryUtilities; +import electroblob.wizardry.MagicDamage.DamageType; +import electroblob.wizardry.entity.EntityArc; +import net.minecraft.entity.Entity; +import net.minecraft.entity.EntityLiving; import net.minecraft.entity.EntityLivingBase; -import net.minecraft.entity.ai.EntityAIBase; +import net.minecraft.entity.SharedMonsterAttributes; +import net.minecraft.entity.ai.EntityAIHurtByTarget; import net.minecraft.entity.ai.EntityAILookIdle; -import net.minecraft.entity.ai.EntityAIMoveTowardsRestriction; +import net.minecraft.entity.ai.EntityAINearestAttackableTarget; import net.minecraft.entity.ai.EntityAIWander; -import net.minecraft.entity.ai.EntityAIWatchClosest; -import net.minecraft.entity.monster.EntityBlaze; -import net.minecraft.entity.player.EntityPlayer; -import net.minecraft.init.MobEffects; +import net.minecraft.entity.monster.EntityCreeper; +import net.minecraft.entity.monster.EntityMob; +import net.minecraft.entity.monster.IMob; import net.minecraft.util.DamageSource; -import net.minecraft.util.EnumHand; +import net.minecraft.util.MathHelper; import net.minecraft.world.World; -public class EntityLightningWraith extends EntityBlazeMinion { +public class EntityLightningWraith extends EntitySummonedCreature +{ + /** Random offset used in floating behaviour */ + private float heightOffset = 0.5F; + + /** ticks until heightOffset is randomized */ + private int heightOffsetUpdateTime; + private int field_70846_g; public EntityLightningWraith(World world){ super(world); } public EntityLightningWraith(World world, double x, double y, double z, EntityLivingBase caster, int lifetime){ + super(world, x, y, z, caster, lifetime); this.isImmuneToFire = false; + this.tasks.addTask(1, new EntityAIWander(this, 1.0D)); + this.tasks.addTask(2, new EntityAILookIdle(this)); + this.targetTasks.addTask(1, new EntityAIHurtByTarget(this, true)); + this.targetTasks.addTask(2, new EntityAINearestAttackableTarget(this, EntityLiving.class, 0, false, true, IMob.mobSelector)); + this.experienceValue = 0; } - - @Override - protected void initEntityAI(){ - super.initEntityAI(); - this.tasks.taskEntries.clear(); - this.tasks.addTask(4, new AILightningAttack(this)); - this.tasks.addTask(5, new EntityAIMoveTowardsRestriction(this, 1.0D)); - this.tasks.addTask(7, new EntityAIWander(this, 1.0D)); - this.tasks.addTask(8, new EntityAIWatchClosest(this, EntityPlayer.class, 8.0F)); - this.tasks.addTask(8, new EntityAILookIdle(this)); + + /** + * Returns true if the newer Entity AI code should be run + */ + protected boolean isAIEnabled() + { + return false; } @Override - protected void spawnParticleEffect(){ + public boolean hasRangedAttack() { + return true; + } + + protected void applyEntityAttributes() + { + super.applyEntityAttributes(); + this.getEntityAttribute(SharedMonsterAttributes.attackDamage).setBaseValue(6.0D); + this.getEntityAttribute(SharedMonsterAttributes.followRange).setBaseValue(16.0D); + } + + protected void entityInit() + { + super.entityInit(); + this.dataWatcher.addObject(16, new Byte((byte)0)); + } + + /** + * Returns the sound this mob makes while it's alive. + */ + protected String getLivingSound() + { + return "mob.blaze.breathe"; + } + + /** + * Returns the sound this mob makes when it is hurt. + */ + protected String getHurtSound() + { + return "mob.blaze.hit"; + } + + /** + * Returns the sound this mob makes on death. + */ + protected String getDeathSound() + { + return "mob.blaze.death"; + } + + @SideOnly(Side.CLIENT) + public int getBrightnessForRender(float par1) + { + return 15728880; + } + + /** + * Gets how bright this entity is. + */ + public float getBrightness(float par1) + { + return 1.0F; + } + + @Override + public void onSpawn(){ if(this.worldObj.isRemote){ for(int i=0;i<15;i++){ float brightness = 0.3f + (rand.nextFloat()/2); - Wizardry.proxy.spawnParticle(WizardryParticleType.SPARKLE, worldObj, this.posX - 0.5d + rand.nextDouble(), this.posY + this.height/2 - 0.5d + rand.nextDouble(), this.posZ - 0.5d + rand.nextDouble(), 0, 0.05f, 0, 20 + rand.nextInt(10), brightness, brightness + 0.2f, 1.0f); + Wizardry.proxy.spawnParticle(EnumParticleType.SPARKLE, worldObj, this.posX - 0.5d + rand.nextDouble(), this.posY + this.height/2 - 0.5d + rand.nextDouble(), this.posZ - 0.5d + rand.nextDouble(), 0, 0.05f, 0, 20 + rand.nextInt(10), brightness, brightness + 0.2f, 1.0f); } } } @Override - public void onLivingUpdate(){ - // Fortunately, lightning wraiths don't replace any of blazes' particle effects or the fire sound, they only - // add the sparks, so it's fine to call super here. - if(worldObj.isRemote){ - Wizardry.proxy.spawnParticle(WizardryParticleType.SPARK, worldObj, this.posX + (this.rand.nextDouble() - 0.5D) * (double)this.width, this.posY + this.rand.nextDouble() * (double)this.height, this.posZ + (this.rand.nextDouble() - 0.5D) * (double)this.width, 0, 0, 0, 3); + public void despawn(){ + if(this.worldObj.isRemote){ + for(int i=0;i<15;i++){ + float brightness = 0.3f + (rand.nextFloat()/2); + Wizardry.proxy.spawnParticle(EnumParticleType.SPARKLE, worldObj, this.posX - 0.5d + rand.nextDouble(), this.posY + this.height/2 - 0.5d + rand.nextDouble(), this.posZ - 0.5d + rand.nextDouble(), 0, 0.05f, 0, 20 + rand.nextInt(10), brightness, brightness + 0.2f, 1.0f); + } } + super.despawn(); + } + + /** + * Called frequently so the entity can update its state every tick as required. For example, zombies and skeletons + * use this to react to sunlight and start to burn. + */ + public void onLivingUpdate() + { + + if (!this.worldObj.isRemote) + { + /* + if (this.isWet()) + { + this.attackEntityFrom(DamageSource.drown, 1.0F); + } + */ + + --this.heightOffsetUpdateTime; + + if (this.heightOffsetUpdateTime <= 0) + { + this.heightOffsetUpdateTime = 100; + this.heightOffset = 0.5F + (float)this.rand.nextGaussian() * 3.0F; + } + + if (this.getEntityToAttack() != null && this.getEntityToAttack().posY + (double)this.getEntityToAttack().getEyeHeight() > this.posY + (double)this.getEyeHeight() + (double)this.heightOffset) + { + this.motionY += (0.30000001192092896D - this.motionY) * 0.30000001192092896D; + } + } + + if (this.rand.nextInt(24) == 0) + { + this.worldObj.playSoundEffect(this.posX + 0.5D, this.posY + 0.5D, this.posZ + 0.5D, "fire.fire", 1.0F + this.rand.nextFloat(), this.rand.nextFloat() * 0.7F + 0.3F); + } + + if (!this.onGround && this.motionY < 0.0D) + { + this.motionY *= 0.6D; + } + + if(worldObj.isRemote){ + for (int i = 0; i < 2; ++i) + { + this.worldObj.spawnParticle("largesmoke", this.posX + (this.rand.nextDouble() - 0.5D) * (double)this.width, this.posY + this.rand.nextDouble() * (double)this.height, this.posZ + (this.rand.nextDouble() - 0.5D) * (double)this.width, 0.0D, 0.0D, 0.0D); + } + Wizardry.proxy.spawnParticle(EnumParticleType.SPARK, worldObj, this.posX + (this.rand.nextDouble() - 0.5D) * (double)this.width, this.posY + this.rand.nextDouble() * (double)this.height, this.posZ + (this.rand.nextDouble() - 0.5D) * (double)this.width, 0, 0, 0, 3); + } + super.onLivingUpdate(); } - - @Override - public boolean attackEntityFrom(DamageSource source, float amount){ - // Removes the damage from being wet that applies to blazes by checking if the mob is actually drowning. - if(source == DamageSource.drown && (this.getAir() > 0 || this.isPotionActive(MobEffects.WATER_BREATHING))){ - // In this case, the lightning wraith is not actually drowning, so cancel the damage. - return false; - }else{ - return super.attackEntityFrom(source, amount); + + /** + * Basic mob attack. Default to touch of death in EntityCreature. Overridden by each mob to define their attack. + */ + protected void attackEntity(Entity target, float par2) + { + if (this.attackTime <= 0 && par2 < 2.0F && target.boundingBox.maxY > this.boundingBox.minY && target.boundingBox.minY < this.boundingBox.maxY) + { + this.attackTime = 20; + this.attackEntityAsMob(target); + } + + // Added a range limit. + else if (par2 < 30.0F && this.getDistanceToEntity(target) < 16.0d) + { + double d0 = target.posX - this.posX; + double d1 = target.boundingBox.minY + (double)(target.height / 2.0F) - (this.posY + (double)(this.height / 2.0F)); + double d2 = target.posZ - this.posZ; + + if (this.attackTime == 0) + { + ++this.field_70846_g; + + if (this.field_70846_g == 1) + { + this.attackTime = 60; + //this.func_70844_e(true); + } + else if (this.field_70846_g <= 4) + { + this.attackTime = 6; + } + else + { + this.attackTime = 100; + this.field_70846_g = 0; + //this.func_70844_e(false); + } + + if (this.field_70846_g > 1) + { + float f1 = MathHelper.sqrt_float(par2) * 0.5F; + worldObj.playSoundAtEntity(target, "wizardry:arc", 1.0F, rand.nextFloat() * 0.4F + 1.5F); + + if(!worldObj.isRemote){ + + EntityArc arc = new EntityArc(this.worldObj); + arc.setEndpointCoords(this.posX, this.posY + (double)(this.height / 2.0F) + 0.5D, this.posZ, target.posX, target.posY + target.height/2, target.posZ); + this.worldObj.spawnEntityInWorld(arc); + + target.attackEntityFrom(MagicDamage.causeIndirectEntityMagicDamage(this, this.getCaster(), DamageType.SHOCK), 5.0f); + + }else{ + // Particle effect + for(int j=0;j<8;j++){ + Wizardry.proxy.spawnParticle(EnumParticleType.SPARK, worldObj, target.posX + rand.nextFloat() - 0.5, WizardryUtilities.getEntityFeetPos(target) + target.height*rand.nextFloat(), target.posZ + rand.nextFloat() - 0.5, 0, 0, 0, 3); + worldObj.spawnParticle("largesmoke", target.posX + rand.nextFloat() - 0.5, WizardryUtilities.getEntityFeetPos(target) + target.height*rand.nextFloat(), target.posZ + rand.nextFloat() - 0.5, 0, 0, 0); + } + } + } + } + + this.rotationYaw = (float)(Math.atan2(d2, d0) * 180.0D / Math.PI) - 90.0F; + this.hasAttacked = true; } } - - @Override - public boolean isBurning(){ - // Uses the datawatcher on both sides because fire is private to Entity (and I'm not using reflection here). - // TESTME: This should work, but there may be some issues with updating, so if it doesn't work, copy the - // version from Entity and use reflection to access the fire field. - return this.getFlag(0); - } - - /** Copied straight from EntityBlaze.AIFireballAttack, with the only changes being replacement of fireball - * spawning with a one-liner call to WizardryRegistry.arc.cast(...) and the removal of redundant local variables. */ - static class AILightningAttack extends EntityAIBase { - - private final EntityBlaze blaze; - private int attackStep; - private int attackTime; - public AILightningAttack(EntityBlaze blazeIn) - { - this.blaze = blazeIn; - this.setMutexBits(3); - } + /** + * Called when the mob is falling. Calculates and applies fall damage. + */ + protected void fall(float par1) {} - /** - * Returns whether the EntityAIBase should begin execution. - */ - public boolean shouldExecute() - { - EntityLivingBase entitylivingbase = this.blaze.getAttackTarget(); - return entitylivingbase != null && entitylivingbase.isEntityAlive(); - } + /** + * Returns the item ID for the item the mob drops on death. + */ + protected int getDropItemId() + { + return -1; + } - /** - * Execute a one shot task or start executing a continuous task - */ - public void startExecuting() - { - this.attackStep = 0; - } + /** + * Drop 0-2 items of this living's type. @param par1 - Whether this entity has recently been hit by a player. @param + * par2 - Level of Looting used to kill this mob. + */ + protected void dropFewItems(boolean par1, int par2) + { - /** - * Resets the task - */ - public void resetTask() - { - // This might be called setOnFire, but what it really controls is whether the wraith is in attack mode. - this.blaze.setOnFire(false); - } + } - /** - * Updates the task - */ - public void updateTask() - { - --this.attackTime; - EntityLivingBase entitylivingbase = this.blaze.getAttackTarget(); - double d0 = this.blaze.getDistanceSqToEntity(entitylivingbase); + public boolean func_70845_n() + { + return (this.dataWatcher.getWatchableObjectByte(16) & 1) != 0; + } - if (d0 < 4.0D) - { - if (this.attackTime <= 0) - { - this.attackTime = 20; - this.blaze.attackEntityAsMob(entitylivingbase); - } + public void func_70844_e(boolean par1) + { + byte b0 = this.dataWatcher.getWatchableObjectByte(16); - this.blaze.getMoveHelper().setMoveTo(entitylivingbase.posX, entitylivingbase.posY, entitylivingbase.posZ, 1.0D); - } - else if (d0 < 256.0D) - { - if (this.attackTime <= 0) - { - ++this.attackStep; + if (par1) + { + b0 = (byte)(b0 | 1); + } + else + { + b0 &= -2; + } - if (this.attackStep == 1) - { - this.attackTime = 60; - this.blaze.setOnFire(true); - } - else if (this.attackStep <= 4) - { - this.attackTime = 6; - } - else - { - this.attackTime = 100; - this.attackStep = 0; - this.blaze.setOnFire(false); - } + this.dataWatcher.updateObject(16, Byte.valueOf(b0)); + } - if(this.attackStep > 1){ - // Proof, if it were at all needed, of the elegance and versatility of the spell system. - Spells.arc.cast(this.blaze.worldObj, this.blaze, EnumHand.MAIN_HAND, 0, entitylivingbase, new SpellModifiers()); - // TODO: Decide if an event should be fired here. I'm guessing no. - } - } - - this.blaze.getLookHelper().setLookPositionWithEntity(entitylivingbase, 10.0F, 10.0F); - } - else - { - this.blaze.getNavigator().clearPathEntity(); - this.blaze.getMoveHelper().setMoveTo(entitylivingbase.posX, entitylivingbase.posY, entitylivingbase.posZ, 1.0D); - } - - super.updateTask(); - } - } + /** + * Checks to make sure the light is not too bright where the mob is spawning + */ + protected boolean isValidLightLevel() + { + return true; + } } diff --git a/src/main/java/electroblob/wizardry/entity/living/EntityMagicSlime.java b/src/main/java/electroblob/wizardry/entity/living/EntityMagicSlime.java deleted file mode 100644 index c6799fcf..00000000 --- a/src/main/java/electroblob/wizardry/entity/living/EntityMagicSlime.java +++ /dev/null @@ -1,177 +0,0 @@ -package electroblob.wizardry.entity.living; - -import java.lang.ref.WeakReference; -import java.util.UUID; - -import javax.annotation.Nullable; - -import net.minecraft.entity.EntityLivingBase; -import net.minecraft.entity.IEntityLivingData; -import net.minecraft.entity.monster.EntitySlime; -import net.minecraft.entity.player.EntityPlayer; -import net.minecraft.init.MobEffects; -import net.minecraft.init.SoundEvents; -import net.minecraft.item.Item; -import net.minecraft.item.ItemStack; -import net.minecraft.nbt.NBTTagCompound; -import net.minecraft.potion.PotionEffect; -import net.minecraft.util.DamageSource; -import net.minecraft.util.EnumHand; -import net.minecraft.util.EnumParticleTypes; -import net.minecraft.util.ResourceLocation; -import net.minecraft.world.DifficultyInstance; -import net.minecraft.world.EnumDifficulty; -import net.minecraft.world.World; - -/** As of Wizardry 1.2, this is now an ISummonedCreature like the rest of them, and it extends EntitySlime. */ -public class EntityMagicSlime extends EntitySlime implements ISummonedCreature { - - // Field implementations - private int lifetime = 200; - private WeakReference casterReference; - private UUID casterUUID; - - // Setter + getter implementations - @Override public int getLifetime(){ return lifetime; } - @Override public void setLifetime(int lifetime){ this.lifetime = lifetime; } - @Override public WeakReference getCasterReference(){ return casterReference; } - @Override public void setCasterReference(WeakReference reference){ casterReference = reference; } - @Override public UUID getCasterUUID() { return casterUUID; } - @Override public void setCasterUUID(UUID uuid) { this.casterUUID = uuid; } - - public EntityMagicSlime(World world){ - super(world); - this.setSlimeSize(2); // Needs to be called before setting the experience value to 0 - this.experienceValue = 0; - } - - /** - * Creates a new magic slime with the given caster and lifetime, riding the given target. - * @param world The world that the slime is in. - * @param caster The entity that created the slime. - * @param target The slime's victim. The slime will automatically start riding this entity. - * @param lifetime The number of ticks before the slime bursts. - */ - public EntityMagicSlime(World world, EntityLivingBase caster, EntityLivingBase target, int lifetime){ - super(world); - this.setPosition(target.posX, target.posY, target.posZ); - this.startRiding(target); - this.casterReference = new WeakReference(caster); - this.setSlimeSize(2); // Needs to be called before setting the experience value to 0 - this.experienceValue = 0; - this.lifetime = lifetime; - } - - // EntitySlime overrides - - @Override - protected void initEntityAI(){} // Has no AI! - - @Override - protected void dealDamage(EntityLivingBase entity){} // Handles damage itself - - @Override - public void setDead(){ - // Restores behaviour from Entity, replacing slime splitting behaviour. - this.isDead = true; - // Makes sure that the undoing in onUpdate won't undo this. For some reason, EntitySlime sets isDead directly - // to do the peaceful despawning, which seems odd but is actually rather handy! - this.setHealth(0); - // Bursting effect - for(int i=0; i<30; i++){ - double x = this.posX - 0.5 + rand.nextDouble(); - double y = this.posY - 0.5 + rand.nextDouble(); - double z = this.posZ - 0.5 + rand.nextDouble(); - this.worldObj.spawnParticle(EnumParticleTypes.SLIME, x, y, z, (x - this.posX)*2, (y - this.posY)*2, (z - this.posZ)*2); - } - this.playSound(SoundEvents.ENTITY_SLIME_ATTACK, 2.5f, 0.6f); - this.playSound(SoundEvents.ENTITY_FIREWORK_BLAST_FAR, 1.0f, 0.5f); - } - - @Override - public IEntityLivingData onInitialSpawn(DifficultyInstance difficulty, @Nullable IEntityLivingData livingdata){ - // Removes size randomisation - IEntityLivingData data = super.onInitialSpawn(difficulty, livingdata); - this.setSlimeSize(2); - return data; - } - - @Override - public boolean attackEntityFrom(DamageSource source, float amount){ - // Immune to suffocation - return source == DamageSource.inWall ? false : super.attackEntityFrom(source, amount); - } - - // Implementations - - @Override - public void setRevengeTarget(EntityLivingBase entity){ - if(this.shouldRevengeTarget(entity)) super.setRevengeTarget(entity); - } - - @Override - public void onUpdate(){ - - super.onUpdate(); - // Undoes the despawning on peaceful behaviour. I don't think there's anything in super.onUpdate that sets - // isDead other than that, but it's better to do a quick sanity check just to be sure. - if(this.isDead && worldObj.getDifficulty() == EnumDifficulty.PEACEFUL && this.getHealth() > 0) this.isDead = false; - // Bursts instantly rather than doing the falling over animation. - if(this.getHealth() <= 0) this.setDead(); - - this.updateDelegate(); - - // Damages and slows the slime's victim or makes the slime explode if the victim is dead. - if(this.getRidingEntity() != null && this.getRidingEntity() instanceof EntityLivingBase && ((EntityLivingBase)this.getRidingEntity()).getHealth() > 0){ - if(this.ticksExisted % 16 == 1){ - this.getRidingEntity().attackEntityFrom(DamageSource.magic, 1); - ((EntityLivingBase)this.getRidingEntity()).addPotionEffect(new PotionEffect(MobEffects.SLOWNESS, 20, 2)); - this.playSound(SoundEvents.ENTITY_SLIME_ATTACK, 1.0f, 1.0f); - this.squishAmount = 0.5F; - } - }else{ - this.setDead(); - } - } - - @Override - public void onSpawn(){} - - @Override - public void onDespawn(){} - - @Override - public boolean hasParticleEffect() { - return false; - } - - @Override - protected boolean processInteract(EntityPlayer player, EnumHand hand, ItemStack stack) { - // In this case, the delegate method determines whether super is called. - // Rather handily, we can make use of Java's short-circuiting method of evaluating OR statements. - return this.interactDelegate(player, hand, stack) || super.processInteract(player, hand, stack); - } - - @Override - public void writeEntityToNBT(NBTTagCompound nbttagcompound){ - super.writeEntityToNBT(nbttagcompound); - this.writeNBTDelegate(nbttagcompound); - } - - @Override - public void readEntityFromNBT(NBTTagCompound nbttagcompound){ - super.readEntityFromNBT(nbttagcompound); - this.writeNBTDelegate(nbttagcompound); - } - - // Recommended overrides - - @Override protected int getExperiencePoints(EntityPlayer player){ return 0; } - @Override protected boolean canDropLoot(){ return false; } - @Override protected Item getDropItem(){ return null; } - @Override protected ResourceLocation getLootTable(){ return null; } - @Override public boolean canPickUpLoot(){ return false; } - // This vanilla method has nothing to do with the custom onDespawn() method. - @Override protected boolean canDespawn(){ return false; } - -} diff --git a/src/main/java/electroblob/wizardry/entity/living/EntityPhoenix.java b/src/main/java/electroblob/wizardry/entity/living/EntityPhoenix.java index 5d72f26e..8aeb1ff9 100644 --- a/src/main/java/electroblob/wizardry/entity/living/EntityPhoenix.java +++ b/src/main/java/electroblob/wizardry/entity/living/EntityPhoenix.java @@ -1,189 +1,417 @@ package electroblob.wizardry.entity.living; -import java.util.Collections; import java.util.List; -import electroblob.wizardry.registry.Spells; -import electroblob.wizardry.spell.Spell; -import electroblob.wizardry.util.SpellModifiers; -import electroblob.wizardry.util.WizardryUtilities; +import cpw.mods.fml.relauncher.Side; +import cpw.mods.fml.relauncher.SideOnly; +import electroblob.wizardry.EnumParticleType; +import electroblob.wizardry.MagicDamage; +import electroblob.wizardry.Wizardry; +import electroblob.wizardry.WizardryUtilities; +import electroblob.wizardry.MagicDamage.DamageType; +import net.minecraft.entity.Entity; +import net.minecraft.entity.EntityLiving; import net.minecraft.entity.EntityLivingBase; +import net.minecraft.entity.IRangedAttackMob; import net.minecraft.entity.SharedMonsterAttributes; +import net.minecraft.entity.ai.EntityAIArrowAttack; import net.minecraft.entity.ai.EntityAIHurtByTarget; import net.minecraft.entity.ai.EntityAILookIdle; import net.minecraft.entity.ai.EntityAINearestAttackableTarget; import net.minecraft.entity.ai.EntityAIWatchClosest; -import net.minecraft.init.SoundEvents; -import net.minecraft.util.EnumParticleTypes; -import net.minecraft.util.SoundEvent; -import net.minecraft.util.math.BlockPos; +import net.minecraft.entity.monster.EntityCreeper; +import net.minecraft.entity.monster.EntityMob; +import net.minecraft.entity.monster.IMob; +import net.minecraft.util.DamageSource; +import net.minecraft.util.MovingObjectPosition; +import net.minecraft.util.MovingObjectPosition.MovingObjectType; import net.minecraft.world.World; -import net.minecraftforge.fml.relauncher.Side; -import net.minecraftforge.fml.relauncher.SideOnly; -public class EntityPhoenix extends EntitySummonedCreature implements ISpellCaster { +public class EntityPhoenix extends EntitySummonedCreature implements IRangedAttackMob +{ + /** Random offset used in floating behaviour */ + private float heightOffset = 0.5F; private double AISpeed = 0.5; - // Can attack for 7 seconds, then must cool down for 3. - private EntityAIAttackSpell spellAttackAI = new EntityAIAttackSpell(this, AISpeed, 15f, 60, 140); - - private Spell continuousSpell; - - private static final List attack = Collections.singletonList(Spells.flame_ray); + /** ticks until heightOffset is randomized */ + private int heightOffsetUpdateTime; + private int field_70846_g; + + private EntityAIArrowAttack aiArrowAttack = new EntityAIArrowAttack(this, AISpeed, 1, 1, 15.0F); public EntityPhoenix(World world){ super(world); } public EntityPhoenix(World world, double x, double y, double z, EntityLivingBase caster, int lifetime){ + super(world, x, y, z, caster, lifetime); this.isImmuneToFire = true; this.height = 2.0f; - // For some reason this can't be in initEntityAI - this.tasks.addTask(1, this.spellAttackAI); - } - - @Override - protected void initEntityAI(){ - this.tasks.addTask(0, new EntityAIWatchClosest(this, EntityLivingBase.class, 0)); + this.tasks.addTask(1, this.aiArrowAttack); //this.tasks.addTask(2, new EntityAIWander(this, AISpeed)); this.tasks.addTask(3, new EntityAILookIdle(this)); //this.targetTasks.addTask(0, new EntityAIMoveTowardsTarget(this, 1, 10)); this.targetTasks.addTask(1, new EntityAIHurtByTarget(this, true)); - this.targetTasks.addTask(2, new EntityAINearestAttackableTarget(this, EntityLivingBase.class, - 0, false, true, this.getTargetSelector())); + this.targetTasks.addTask(2, new EntityAINearestAttackableTarget(this, EntityLivingBase.class, 0, false, true, this.targetSelector)); this.setAIMoveSpeed((float)AISpeed); + + this.experienceValue = 0; + + // Prevents the phoenix from attacking instantly on spawn. + this.attackTime = 60; } - - @Override - public List getSpells(){ - return attack; - } - @Override - public SpellModifiers getModifiers(){ - return new SpellModifiers(); - } - - @Override - public Spell getContinuousSpell(){ - return continuousSpell; - } - - @Override - public void setContinuousSpell(Spell spell){ - continuousSpell = spell; - } + /** + * Returns true if the newer Entity AI code should be run + */ + protected boolean isAIEnabled() + { + return true; + } @Override public boolean hasRangedAttack() { return true; } - - @Override - // Makes the flames come from the phoenix's head rather than its body - public float getEyeHeight(){ - return 2.1f; - } - @Override - protected void applyEntityAttributes(){ + protected void applyEntityAttributes() + { super.applyEntityAttributes(); - this.getAttributeMap().registerAttribute(SharedMonsterAttributes.ATTACK_DAMAGE).setBaseValue(6.0D); - this.getEntityAttribute(SharedMonsterAttributes.FOLLOW_RANGE).setBaseValue(30.0D); - this.getEntityAttribute(SharedMonsterAttributes.MAX_HEALTH).setBaseValue(30.0D); + this.getEntityAttribute(SharedMonsterAttributes.attackDamage).setBaseValue(6.0D); + this.getEntityAttribute(SharedMonsterAttributes.followRange).setBaseValue(30.0D); + this.getEntityAttribute(SharedMonsterAttributes.maxHealth).setBaseValue(30.0D); + //this.getEntityAttribute(SharedMonsterAttributes.movementSpeed).setBaseValue(3.0d); + } + + protected void entityInit() + { + super.entityInit(); + this.dataWatcher.addObject(16, new Byte((byte)0)); + this.dataWatcher.addObject(20, new Byte((byte)0)); + this.dataWatcher.addObject(21, new Integer(0)); } - @Override - protected SoundEvent getAmbientSound(){ - return SoundEvents.ENTITY_BLAZE_AMBIENT; + /** + * (Copied from EntityWither) Returns the target entity ID if present, or -1 if not + */ + public int getTargetId() + { + return this.dataWatcher.getWatchableObjectInt(21); } - @Override - protected SoundEvent getHurtSound(){ - return SoundEvents.ENTITY_BLAZE_HURT; + public void setTargetId(int id) + { + this.dataWatcher.updateObject(21, Integer.valueOf(id)); } - @Override - protected SoundEvent getDeathSound(){ - return SoundEvents.ENTITY_BLAZE_DEATH; + /** Retrieves the attacking boolean from the datawatcher. */ + public boolean getIsAttacking() + { + return (this.dataWatcher.getWatchableObjectByte(20) & 1) != 0; + } + + /** Sets the attacking boolean in the datawatcher. */ + public void setAttacking(boolean par1) + { + byte b0 = this.dataWatcher.getWatchableObjectByte(20); + + if (par1) + { + b0 = (byte)(b0 | 1); + } + else + { + b0 &= -2; + } + + this.dataWatcher.updateObject(20, Byte.valueOf(b0)); + } + + /** + * Returns the sound this mob makes while it's alive. + */ + protected String getLivingSound() + { + return "mob.blaze.breathe"; + } + + /** + * Returns the sound this mob makes when it is hurt. + */ + protected String getHurtSound() + { + return "mob.blaze.hit"; + } + + /** + * Returns the sound this mob makes on death. + */ + protected String getDeathSound() + { + return "mob.blaze.death"; } - @Override @SideOnly(Side.CLIENT) - public int getBrightnessForRender(float par1){ + public int getBrightnessForRender(float par1) + { return 15728880; } - @Override - public float getBrightness(float par1){ + /** + * Gets how bright this entity is. + */ + public float getBrightness(float par1) + { return 1.0F; } @Override public void onSpawn(){ - this.spawnParticleEffect(); - } - - @Override - public void onDespawn(){ - this.spawnParticleEffect(); - } - - private void spawnParticleEffect(){ if(this.worldObj.isRemote){ for(int i=0;i<15;i++){ - this.worldObj.spawnParticle(EnumParticleTypes.FLAME, this.posX + this.rand.nextFloat(), this.posY + 1 + this.rand.nextFloat(), this.posZ + this.rand.nextFloat(), 0, 0, 0); + this.worldObj.spawnParticle("flame", this.posX + this.rand.nextFloat(), this.posY + 1 + this.rand.nextFloat(), this.posZ + this.rand.nextFloat(), 0, 0, 0); } } } - @Override - public void onLivingUpdate(){ - + @Override + public void despawn(){ + if(this.worldObj.isRemote){ + for(int i=0;i<15;i++){ + this.worldObj.spawnParticle("flame", this.posX + this.rand.nextFloat(), this.posY + 1 + this.rand.nextFloat(), this.posZ + this.rand.nextFloat(), 0, 0, 0); + } + } + super.despawn(); + } + + /** + * Called frequently so the entity can update its state every tick as required. For example, zombies and skeletons + * use this to react to sunlight and start to burn. + */ + public void onLivingUpdate() + { // Makes the phoenix hover. - int floorLevel = WizardryUtilities.getNearestFloorLevel(worldObj, new BlockPos(this), 4); - - if(this.posY - floorLevel > 3){ + if(this.posY - WizardryUtilities.getNearestFloorLevel(worldObj, (int)this.posX, (int)this.posY, (int)this.posZ, 4) > 3){ this.motionY = -0.1; - }else if(this.posY - floorLevel < 2){ + }else if(this.posY - WizardryUtilities.getNearestFloorLevel(worldObj, (int)this.posX, (int)this.posY, (int)this.posZ, 4) < 2){ this.motionY = 0.1; }else{ this.motionY = 0.0; } // Living sound - if(this.rand.nextInt(24) == 0){ - this.playSound(SoundEvents.BLOCK_FIRE_AMBIENT, 1.0F + this.rand.nextFloat(), this.rand.nextFloat() * 0.7F + 0.3F); + if (this.rand.nextInt(24) == 0) + { + this.worldObj.playSoundEffect(this.posX + 0.5D, this.posY + 0.5D, this.posZ + 0.5D, "fire.fire", 1.0F + this.rand.nextFloat(), this.rand.nextFloat() * 0.7F + 0.3F); } // Flapping sound effect if(this.ticksExisted % 22 == 0){ - this.playSound(SoundEvents.ENTITY_ENDERDRAGON_FLAP, 1.0F, 1.0f); + this.worldObj.playSoundAtEntity(this, "mob.enderdragon.wings", 1.0F, 1.0f); } - for(int i=0; i<2; i++){ - this.worldObj.spawnParticle(EnumParticleTypes.FLAME, this.posX + (this.rand.nextDouble() - 0.5D) * (double)this.width, this.posY + this.height/2 + this.rand.nextDouble() * (double)this.height/2, this.posZ + (this.rand.nextDouble() - 0.5D) * (double)this.width, 0.0D, -0.1D, 0.0D); + for (int i = 0; i < 2; ++i) + { + this.worldObj.spawnParticle("flame", this.posX + (this.rand.nextDouble() - 0.5D) * (double)this.width, this.posY + this.height/2 + this.rand.nextDouble() * (double)this.height/2, this.posZ + (this.rand.nextDouble() - 0.5D) * (double)this.width, 0.0D, -0.1D, 0.0D); } + + // Attack particles (flame ray) + if(this.worldObj.isRemote && this.getIsAttacking()){ + + Entity target = this.worldObj.getEntityByID(this.getTargetId()); + + if(target != null){ + + //Vec3 look = this.getLookVec(); + + // This bit does a normalised vector towards the target. + double x = (target.posX - this.posX)/this.getDistanceToEntity(target); //-MathHelper.sin((float)Math.toRadians(this.rotationYawHead)); + double y = ((WizardryUtilities.getEntityFeetPos(target) + target.height/2) - (this.posY + this.getEyeHeight()))/this.getDistanceToEntity(target); //-MathHelper.sin((float)Math.toRadians(this.rotationPitch)); + double z = (target.posZ - this.posZ)/this.getDistanceToEntity(target); //MathHelper.cos((float)Math.toRadians(this.rotationYawHead)); + + for(int i=0; i<20; i++){ + // Starts a little bit forwards so it looks like it comes from the mouth + double x1 = this.posX + this.getLookVec().xCoord*0.7 + x*i/2 + rand.nextFloat()*0.2 - 0.1f; + double y1 = this.posY + 1.75 + y*i/2 + rand.nextFloat()/5 - 0.1f; + double z1 = this.posZ + this.getLookVec().zCoord*0.7 + z*i/2 + rand.nextFloat()*0.2 - 0.1f; + Wizardry.proxy.spawnParticle(EnumParticleType.MAGIC_FIRE, this.worldObj, x1, y1, z1, x, y, z, (int)(2.0D / (Math.random() * 0.8D + 0.2D)), 2, 0, 0); + Wizardry.proxy.spawnParticle(EnumParticleType.MAGIC_FIRE, this.worldObj, x1, y1, z1, x, y, z, (int)(2.0D / (Math.random() * 0.8D + 0.2D)), 2, 0, 0); + } + + } + } + + // 3 second cooldown after target is killed; otherwise it instantly shoots the next one which looks a bit odd. + if(this.getAttackTarget() != null && this.getAttackTarget().deathTime > 0){ + this.attackTime = 60; + } + + // This is set to false after each update so that it only becomes true if attackEntity is called. + if(this.getIsAttacking()) this.setAttacking(false); // Adding this allows the phoenix to attack despite being in the air. However, for some strange reason // it will only attack when within about 3 blocks of the ground. Any higher and it just sits there, not even - // attempting to find targets. + // attempting to find targets. Even weirder, it seems to be aiming a) in a different place to the particles + // and b) not always at the target. Edit: that last bit is fixed for now. this.onGround = true; super.onLivingUpdate(); } - - @Override - public void fall(float distance, float damageMultiplier){} // Immune to fall damage + /** + * Basic mob attack. Default to touch of death in EntityCreature. Overridden by each mob to define their attack. + */ + protected void attackEntity(Entity target, float par2) + { + this.setAttacking(true); + this.setTargetId(target.getEntityId()); + + MovingObjectPosition rayTrace = WizardryUtilities.standardEntityRayTrace(this.worldObj, this, 10); + + if(rayTrace != null && rayTrace.typeOfHit == MovingObjectType.ENTITY && rayTrace.entityHit instanceof EntityLivingBase){ + + EntityLivingBase entity = (EntityLivingBase) rayTrace.entityHit; + + if(!MagicDamage.isEntityImmune(DamageType.FIRE, entity)){ + entity.setFire(10); + // This motion stuff removes knockback, which is desirable for continuous spells. + double motionX = entity.motionX; + double motionY = entity.motionY; + double motionZ = entity.motionZ; + + entity.attackEntityFrom(MagicDamage.causeIndirectEntityMagicDamage(this, this.getCaster(), DamageType.FIRE), 3.0f); + + entity.motionX = motionX; + entity.motionY = motionY; + entity.motionZ = motionZ; + } + } + if(this.ticksExisted % 16 == 0){ + this.worldObj.playSoundAtEntity(this, "wizardry:flameray", 0.5F, 1.0f); + } + } + @Override + public void attackEntityWithRangedAttack(EntityLivingBase entitylivingbase, float f) { + + // Can attack for 7 seconds, then must cool down for 3. + if(this.attackTime > 0 && this.attackTime <= 60) return; + + // I can't get the raytracing method to work at the moment so it auto-aims instead. Not ideal since there is + // essentially no escape, but it is the best I can do. + + // Edit: Then again, it is quite cool! And hey, it's a master spell. + + this.setAttacking(true); + this.attackTime = 200; + this.setTargetId(entitylivingbase.getEntityId()); + + EntityLivingBase entity = entitylivingbase; + + if(!MagicDamage.isEntityImmune(DamageType.FIRE, entity)){ + + entity.setFire(10); + + // This motion stuff removes knockback, which is desirable for continuous spells. + double motionX = entity.motionX; + double motionY = entity.motionY; + double motionZ = entity.motionZ; + + entity.attackEntityFrom(MagicDamage.causeIndirectEntityMagicDamage(this, this.getCaster(), DamageType.FIRE), 3.0f); + + entity.motionX = motionX; + entity.motionY = motionY; + entity.motionZ = motionZ; + } + + if(this.ticksExisted % 16 == 0){ + this.worldObj.playSoundAtEntity(this, "wizardry:flameray", 0.5F, 1.0f); + } + } + + /** + * Finds the closest player within 16 blocks to attack, or null if this Entity isn't interested in attacking + * (Animals, Spiders at day, peaceful PigZombies). + */ + protected Entity findPlayerToAttack() + { + List entities = WizardryUtilities.getEntitiesWithinRadius(16, this.posX, this.posY, this.posZ, this.worldObj); + Entity entity = null; + + for(int i=0;i this.getDistanceToEntity((Entity)entities.get(i))){ + // Decides if new entity is a valid target. + if(entities.get(i) instanceof EntityMob && this.canEntityBeSeen((Entity)entities.get(i))){ + entity = (Entity)entities.get(i); + } + } + } + + return entity; + } + + /** + * Called when the mob is falling. Calculates and applies fall damage. + */ + protected void fall(float par1) {} + + /** + * Returns the item ID for the item the mob drops on death. + */ + protected int getDropItemId() + { + return -1; + } + + /** + * Returns true if the entity is on fire. Used by render to add the fire effect on rendering. + */ public boolean isBurning() { return false; } + + /** + * Drop 0-2 items of this living's type. @param par1 - Whether this entity has recently been hit by a player. @param + * par2 - Level of Looting used to kill this mob. + */ + protected void dropFewItems(boolean par1, int par2) + { + + } + + public boolean func_70845_n() + { + return (this.dataWatcher.getWatchableObjectByte(16) & 1) != 0; + } + + public void func_70844_e(boolean par1) + { + byte b0 = this.dataWatcher.getWatchableObjectByte(16); + + if (par1) + { + b0 = (byte)(b0 | 1); + } + else + { + b0 &= -2; + } + + this.dataWatcher.updateObject(16, Byte.valueOf(b0)); + } + + /** + * Checks to make sure the light is not too bright where the mob is spawning + */ + protected boolean isValidLightLevel() + { + return true; + } } diff --git a/src/main/java/electroblob/wizardry/entity/living/EntityShadowWraith.java b/src/main/java/electroblob/wizardry/entity/living/EntityShadowWraith.java index 000ba391..cf5d524a 100644 --- a/src/main/java/electroblob/wizardry/entity/living/EntityShadowWraith.java +++ b/src/main/java/electroblob/wizardry/entity/living/EntityShadowWraith.java @@ -1,60 +1,57 @@ package electroblob.wizardry.entity.living; -import java.util.Collections; import java.util.List; +import cpw.mods.fml.relauncher.Side; +import cpw.mods.fml.relauncher.SideOnly; +import electroblob.wizardry.EnumParticleType; import electroblob.wizardry.Wizardry; -import electroblob.wizardry.registry.Spells; -import electroblob.wizardry.spell.Spell; -import electroblob.wizardry.util.SpellModifiers; -import electroblob.wizardry.util.WizardryParticleType; +import electroblob.wizardry.WizardryUtilities; +import electroblob.wizardry.entity.projectile.EntityDarknessOrb; +import net.minecraft.entity.Entity; +import net.minecraft.entity.EntityLiving; import net.minecraft.entity.EntityLivingBase; import net.minecraft.entity.SharedMonsterAttributes; -import net.minecraft.entity.ai.EntityAIAttackMelee; import net.minecraft.entity.ai.EntityAIHurtByTarget; import net.minecraft.entity.ai.EntityAILookIdle; import net.minecraft.entity.ai.EntityAINearestAttackableTarget; import net.minecraft.entity.ai.EntityAIWander; -import net.minecraft.init.MobEffects; -import net.minecraft.init.SoundEvents; +import net.minecraft.entity.monster.EntityMob; +import net.minecraft.entity.monster.IMob; +import net.minecraft.potion.Potion; import net.minecraft.potion.PotionEffect; -import net.minecraft.util.EnumParticleTypes; -import net.minecraft.util.SoundEvent; import net.minecraft.world.World; -import net.minecraftforge.fml.relauncher.Side; -import net.minecraftforge.fml.relauncher.SideOnly; -public class EntityShadowWraith extends EntitySummonedCreature implements ISpellCaster { - - // TODO: This currently doesn't fly like it used to. Should it, or does it not matter? - - private double AISpeed = 1.0; - - private EntityAIAttackSpell spellAttackAI = new EntityAIAttackSpell(this, AISpeed, 15f, 30, 0); - - private static final List attack = Collections.singletonList(Spells.darkness_orb); +public class EntityShadowWraith extends EntitySummonedCreature +{ + /** Random offset used in floating behaviour */ + private float heightOffset = 0.5F; + + /** ticks until heightOffset is randomized */ + private int heightOffsetUpdateTime; + private int field_70846_g; public EntityShadowWraith(World world){ super(world); } public EntityShadowWraith(World world, double x, double y, double z, EntityLivingBase caster, int lifetime){ + super(world, x, y, z, caster, lifetime); - // For some reason this can't be in initEntityAI - this.tasks.addTask(0, this.spellAttackAI); - } - - @Override - protected void initEntityAI(){ - - this.tasks.addTask(1, new EntityAIAttackMelee(this, AISpeed, false)); - this.tasks.addTask(2, new EntityAIWander(this, AISpeed)); - this.tasks.addTask(3, new EntityAILookIdle(this)); + this.isImmuneToFire = false; + this.tasks.addTask(1, new EntityAIWander(this, 1.0D)); + this.tasks.addTask(2, new EntityAILookIdle(this)); this.targetTasks.addTask(1, new EntityAIHurtByTarget(this, true)); - this.targetTasks.addTask(2, new EntityAINearestAttackableTarget(this, EntityLivingBase.class, - 0, false, true, this.getTargetSelector())); + this.targetTasks.addTask(2, new EntityAINearestAttackableTarget(this, EntityLiving.class, 0, false, true, IMob.mobSelector)); + this.experienceValue = 0; + } - this.setAIMoveSpeed((float)AISpeed); + /** + * Returns true if the newer Entity AI code should be run + */ + protected boolean isAIEnabled() + { + return false; } @Override @@ -62,63 +59,61 @@ public class EntityShadowWraith extends EntitySummonedCreature implements ISpell return true; } - @Override - public List getSpells(){ - 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 - protected void applyEntityAttributes(){ + protected void applyEntityAttributes() + { super.applyEntityAttributes(); - this.getAttributeMap().registerAttribute(SharedMonsterAttributes.ATTACK_DAMAGE).setBaseValue(6.0D); - this.getEntityAttribute(SharedMonsterAttributes.MOVEMENT_SPEED).setBaseValue(AISpeed); - this.getEntityAttribute(SharedMonsterAttributes.MAX_HEALTH).setBaseValue(30.0D); - this.getEntityAttribute(SharedMonsterAttributes.FOLLOW_RANGE).setBaseValue(16.0D); + this.getEntityAttribute(SharedMonsterAttributes.attackDamage).setBaseValue(6.0D); + this.getEntityAttribute(SharedMonsterAttributes.movementSpeed).setBaseValue(3.0d); + this.getEntityAttribute(SharedMonsterAttributes.maxHealth).setBaseValue(30.0d); + this.getEntityAttribute(SharedMonsterAttributes.followRange).setBaseValue(16.0D); } @Override public boolean isPotionApplicable(PotionEffect potion){ - return potion.getPotion() == MobEffects.WITHER ? false : super.isPotionApplicable(potion); + return potion.getPotionID() == Potion.wither.id ? false : super.isPotionApplicable(potion); } - @Override - protected SoundEvent getAmbientSound(){ - return SoundEvents.ENTITY_BLAZE_AMBIENT; - } + protected void entityInit() + { + super.entityInit(); + this.dataWatcher.addObject(16, new Byte((byte)0)); + } - @Override - protected SoundEvent getHurtSound(){ - return SoundEvents.ENTITY_BLAZE_HURT; - } + /** + * Returns the sound this mob makes while it's alive. + */ + protected String getLivingSound() + { + return "mob.blaze.breathe"; + } + + /** + * Returns the sound this mob makes when it is hurt. + */ + protected String getHurtSound() + { + return "mob.blaze.hit"; + } + + /** + * Returns the sound this mob makes on death. + */ + protected String getDeathSound() + { + return "mob.blaze.death"; + } - @Override - protected SoundEvent getDeathSound(){ - return SoundEvents.ENTITY_BLAZE_DEATH; - } - - @Override @SideOnly(Side.CLIENT) - public int getBrightnessForRender(float partialTicks){ + public int getBrightnessForRender(float par1) + { return 15728880; } - @Override - public float getBrightness(float partialTicks){ + /** + * Gets how bright this entity is. + */ + public float getBrightness(float par1) + { return 1.0F; } @@ -127,39 +122,148 @@ public class EntityShadowWraith extends EntitySummonedCreature implements ISpell if(this.worldObj.isRemote){ for(int i=0;i<15;i++){ float brightness = rand.nextFloat()*0.4f; - Wizardry.proxy.spawnParticle(WizardryParticleType.SPARKLE, worldObj, this.posX - 0.5d + rand.nextDouble(), this.posY + this.height/2 - 0.5d + rand.nextDouble(), this.posZ - 0.5d + rand.nextDouble(), 0, 0.05f, 0, 20 + rand.nextInt(10), brightness, 0.0f, brightness); + Wizardry.proxy.spawnParticle(EnumParticleType.SPARKLE, worldObj, this.posX - 0.5d + rand.nextDouble(), this.posY + this.height/2 - 0.5d + rand.nextDouble(), this.posZ - 0.5d + rand.nextDouble(), 0, 0.05f, 0, 20 + rand.nextInt(10), brightness, 0.0f, brightness); } } } - @Override - public void onLivingUpdate(){ + /** + * Called frequently so the entity can update its state every tick as required. For example, zombies and skeletons + * use this to react to sunlight and start to burn. + */ + public void onLivingUpdate() + { + if (!this.worldObj.isRemote) + { + /* + if (this.isWet()) + { + this.attackEntityFrom(DamageSource.drown, 1.0F); + } + */ - if(this.rand.nextInt(24) == 0){ - this.playSound(SoundEvents.BLOCK_PORTAL_AMBIENT, 1.0F + this.rand.nextFloat(), this.rand.nextFloat() * 0.7F + 0.3F); + --this.heightOffsetUpdateTime; + + if (this.heightOffsetUpdateTime <= 0) + { + this.heightOffsetUpdateTime = 100; + this.heightOffset = 0.5F + (float)this.rand.nextGaussian() * 3.0F; + } + + if (this.getEntityToAttack() != null && this.getEntityToAttack().posY + (double)this.getEntityToAttack().getEyeHeight() > this.posY + (double)this.getEyeHeight() + (double)this.heightOffset) + { + this.motionY += (0.30000001192092896D - this.motionY) * 0.30000001192092896D; + } } - // Slow fall - if(!this.onGround && this.motionY < 0.0D){ + if (this.rand.nextInt(24) == 0) + { + this.worldObj.playSoundEffect(this.posX + 0.5D, this.posY + 0.5D, this.posZ + 0.5D, "portal.portal", 1.0F + this.rand.nextFloat(), this.rand.nextFloat() * 0.7F + 0.3F); + } + + if (!this.onGround && this.motionY < 0.0D) + { this.motionY *= 0.6D; } if(worldObj.isRemote){ - for(int i=0; i<2; i++){ - worldObj.spawnParticle(EnumParticleTypes.PORTAL, this.posX + (this.rand.nextDouble() - 0.5D) * (double)this.width, this.posY + this.rand.nextDouble() * (double)this.height, this.posZ + (this.rand.nextDouble() - 0.5D) * (double)this.width, 0, 0, 0); - worldObj.spawnParticle(EnumParticleTypes.SMOKE_LARGE, this.posX + (this.rand.nextDouble() - 0.5D) * (double)this.width, this.posY + this.rand.nextDouble() * (double)this.height, this.posZ + (this.rand.nextDouble() - 0.5D) * (double)this.width, 0, 0, 0); + for (int i = 0; i < 2; ++i) + { + worldObj.spawnParticle("portal", this.posX + (this.rand.nextDouble() - 0.5D) * (double)this.width, this.posY + this.rand.nextDouble() * (double)this.height, this.posZ + (this.rand.nextDouble() - 0.5D) * (double)this.width, 0, 0, 0); + worldObj.spawnParticle("largesmoke", this.posX + (this.rand.nextDouble() - 0.5D) * (double)this.width, this.posY + this.rand.nextDouble() * (double)this.height, this.posZ + (this.rand.nextDouble() - 0.5D) * (double)this.width, 0, 0, 0); float brightness = rand.nextFloat()*0.2f; - Wizardry.proxy.spawnParticle(WizardryParticleType.SPARKLE, worldObj, this.posX + (this.rand.nextDouble() - 0.5D) * (double)this.width, this.posY + this.rand.nextDouble() * (double)this.height, this.posZ + (this.rand.nextDouble() - 0.5D) * (double)this.width, 0, 0.05f, 0, 20 + rand.nextInt(10), brightness, 0.0f, brightness); - Wizardry.proxy.spawnParticle(WizardryParticleType.DARK_MAGIC, worldObj, this.posX + (this.rand.nextDouble() - 0.5D) * (double)this.width, this.posY + this.rand.nextDouble() * (double)this.height, this.posZ + (this.rand.nextDouble() - 0.5D) * (double)this.width, 0.0d, 0.0d, 0.0d, 0, 0.1f, 0.0f, 0.0f); + Wizardry.proxy.spawnParticle(EnumParticleType.SPARKLE, worldObj, this.posX + (this.rand.nextDouble() - 0.5D) * (double)this.width, this.posY + this.rand.nextDouble() * (double)this.height, this.posZ + (this.rand.nextDouble() - 0.5D) * (double)this.width, 0, 0.05f, 0, 20 + rand.nextInt(10), brightness, 0.0f, brightness); + Wizardry.proxy.spawnParticle(EnumParticleType.DARK_MAGIC, worldObj, this.posX + (this.rand.nextDouble() - 0.5D) * (double)this.width, this.posY + this.rand.nextDouble() * (double)this.height, this.posZ + (this.rand.nextDouble() - 0.5D) * (double)this.width, 0.0d, 0.0d, 0.0d, 0, 0.1f, 0.0f, 0.0f); } } super.onLivingUpdate(); } - - @Override - public void fall(float distance, float damageMultiplier){ - // Immune to fall damage. + + /** + * Basic mob attack. Default to touch of death in EntityCreature. Overridden by each mob to define their attack. + */ + protected void attackEntity(Entity par1Entity, float par2) + { + if (this.attackTime <= 0 && par2 < 2.0F && par1Entity.boundingBox.maxY > this.boundingBox.minY && par1Entity.boundingBox.minY < this.boundingBox.maxY) + { + this.attackTime = 20; + this.attackEntityAsMob(par1Entity); + } + else if (par2 < 30.0F) + { + double dx = par1Entity.posX - this.posX; + double dy = par1Entity.boundingBox.minY + (double)(par1Entity.height / 2.0F) - (this.posY + (double)(this.height / 2.0F)); + double dz = par1Entity.posZ - this.posZ; + + if (this.attackTime == 0) + { + this.playSound("mob.wither.shoot", 1.0F, 0.4F / (rand.nextFloat() * 0.4F + 0.8F)); + + EntityDarknessOrb darknessorb = new EntityDarknessOrb(this.worldObj, this.getCaster()); + darknessorb.setPosition(this.posX + this.getLookVec().xCoord, this.posY, this.posZ + this.getLookVec().zCoord); + + darknessorb.directTowards(par1Entity, 0.5f); + + darknessorb.posY = this.posY + (double)(this.height / 2.0F) + 0.5D; + this.worldObj.spawnEntityInWorld(darknessorb); + + this.attackTime = 50; + } + + this.rotationYaw = (float)(Math.atan2(dz, dx) * 180.0D / Math.PI) - 90.0F; + this.hasAttacked = true; + } + } + + /** + * Called when the mob is falling. Calculates and applies fall damage. + */ + protected void fall(float par1) {} + + /** + * Returns the item ID for the item the mob drops on death. + */ + protected int getDropItemId() + { + return -1; + } + + /** + * Drop 0-2 items of this living's type. @param par1 - Whether this entity has recently been hit by a player. @param + * par2 - Level of Looting used to kill this mob. + */ + protected void dropFewItems(boolean par1, int par2) + { + + } + + public boolean func_70845_n() + { + return (this.dataWatcher.getWatchableObjectByte(16) & 1) != 0; + } + + public void func_70844_e(boolean par1) + { + byte b0 = this.dataWatcher.getWatchableObjectByte(16); + + if (par1) + { + b0 = (byte)(b0 | 1); + } + else + { + b0 &= -2; + } + + this.dataWatcher.updateObject(16, Byte.valueOf(b0)); + } + + /** + * Checks to make sure the light is not too bright where the mob is spawning + */ + protected boolean isValidLightLevel() + { + return true; } - } diff --git a/src/main/java/electroblob/wizardry/entity/living/EntitySilverfishMinion.java b/src/main/java/electroblob/wizardry/entity/living/EntitySilverfishMinion.java index 2a8f237e..eda2251e 100644 --- a/src/main/java/electroblob/wizardry/entity/living/EntitySilverfishMinion.java +++ b/src/main/java/electroblob/wizardry/entity/living/EntitySilverfishMinion.java @@ -1,183 +1,268 @@ package electroblob.wizardry.entity.living; -import java.lang.ref.WeakReference; -import java.util.UUID; +import java.util.List; +import org.apache.commons.lang3.tuple.ImmutablePair; + +import electroblob.wizardry.EnumParticleType; import electroblob.wizardry.Wizardry; -import electroblob.wizardry.util.WizardryParticleType; -import net.minecraft.entity.EntityFlying; +import electroblob.wizardry.WizardryUtilities; +import net.minecraft.block.Block; +import net.minecraft.block.BlockSilverfish; +import net.minecraft.entity.Entity; import net.minecraft.entity.EntityLivingBase; -import net.minecraft.entity.ai.EntityAIAttackMelee; -import net.minecraft.entity.ai.EntityAIHurtByTarget; -import net.minecraft.entity.ai.EntityAINearestAttackableTarget; -import net.minecraft.entity.ai.EntityAISwimming; -import net.minecraft.entity.monster.EntitySilverfish; +import net.minecraft.entity.EnumCreatureAttribute; +import net.minecraft.entity.SharedMonsterAttributes; +import net.minecraft.entity.monster.EntityMob; import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.init.Blocks; import net.minecraft.item.Item; -import net.minecraft.item.ItemStack; -import net.minecraft.nbt.NBTTagCompound; -import net.minecraft.util.EnumHand; -import net.minecraft.util.ResourceLocation; -import net.minecraft.util.text.ITextComponent; -import net.minecraft.util.text.TextComponentTranslation; +import net.minecraft.util.DamageSource; +import net.minecraft.util.EntityDamageSource; +import net.minecraft.util.Facing; +import net.minecraft.util.MathHelper; import net.minecraft.world.World; -public class EntitySilverfishMinion extends EntitySilverfish implements ISummonedCreature { +public class EntitySilverfishMinion extends EntitySummonedCreature +{ + /** A cooldown before this entity will search for another Silverfish to join them in battle. */ + private int allySummonCooldown; + private static final String __OBFID = "CL_00001696"; - // Field implementations - private int lifetime = 600; - private WeakReference casterReference; - private UUID casterUUID; - - // Setter + getter implementations - @Override public int getLifetime(){ return lifetime; } - @Override public void setLifetime(int lifetime){ this.lifetime = lifetime; } - @Override public WeakReference getCasterReference(){ return casterReference; } - @Override public void setCasterReference(WeakReference reference){ casterReference = reference; } - @Override public UUID getCasterUUID() { return casterUUID; } - @Override public void setCasterUUID(UUID uuid) { this.casterUUID = uuid; } - - /** - * Default shell constructor, only used by client. Lifetime defaults arbitrarily to 600, but this doesn't - * matter because the client side entity immediately gets the lifetime value copied over to it by this class - * anyway. When extending this class, you must override this constructor or Minecraft won't like it, but there's - * no need to do anything inside it other than call super(). - */ public EntitySilverfishMinion(World world){ super(world); - this.experienceValue = 0; + } + + public EntitySilverfishMinion(World world, double x, double y, double z, EntityLivingBase caster, int lifetime){ + + super(world, x, y, z, caster, lifetime); + this.setSize(0.3F, 0.7F); + } + + protected void applyEntityAttributes() + { + super.applyEntityAttributes(); + this.getEntityAttribute(SharedMonsterAttributes.maxHealth).setBaseValue(8.0D); + this.getEntityAttribute(SharedMonsterAttributes.movementSpeed).setBaseValue(0.6000000238418579D); + this.getEntityAttribute(SharedMonsterAttributes.attackDamage).setBaseValue(1.0D); + this.getEntityAttribute(SharedMonsterAttributes.followRange).setBaseValue(8.0D); + } + + @Override + public boolean hasRangedAttack() { + return false; } /** - * Set lifetime to -1 to allow this creature to last forever. This constructor should be overridden when - * extending this class (be sure to call super()) so that AI and other things can be added. + * returns if this entity triggers Block.onEntityWalking on the blocks they walk on. used for spiders and wolves to + * prevent them from trampling crops */ - public EntitySilverfishMinion(World world, double x, double y, double z, EntityLivingBase caster, int lifetime){ - super(world); - this.setPosition(x, y, z); - this.casterReference = new WeakReference(caster); - this.experienceValue = 0; - this.lifetime = lifetime; - } - - // EntitySilverfish overrides - @Override - protected void initEntityAI() + protected boolean canTriggerWalking() { - // Super not called because we don't want AISummonSilverfish or AIHideInStone - this.tasks.addTask(1, new EntityAISwimming(this)); - this.tasks.addTask(4, new EntityAIAttackMelee(this, 1.0D, false)); - this.targetTasks.addTask(1, new EntityAIHurtByTarget(this, false)); - this.targetTasks.addTask(2, new EntityAINearestAttackableTarget(this, EntityLivingBase.class, - 0, false, true, this.getTargetSelector())); - } - - // Implementations - - @Override - public void setRevengeTarget(EntityLivingBase entity){ - if(this.shouldRevengeTarget(entity)) super.setRevengeTarget(entity); + return false; } - @Override - public void onUpdate(){ - super.onUpdate(); - this.updateDelegate(); + /** + * Returns the sound this mob makes while it's alive. + */ + protected String getLivingSound() + { + return "mob.silverfish.say"; + } + + /** + * Returns the sound this mob makes when it is hurt. + */ + protected String getHurtSound() + { + return "mob.silverfish.hit"; + } + + /** + * Returns the sound this mob makes on death. + */ + protected String getDeathSound() + { + return "mob.silverfish.kill"; + } + + /** + * Called when the entity is attacked. + */ + public boolean attackEntityFrom(DamageSource p_70097_1_, float p_70097_2_) + { + if (this.isEntityInvulnerable()) + { + return false; + } + else + { + if (this.allySummonCooldown <= 0 && (p_70097_1_ instanceof EntityDamageSource || p_70097_1_ == DamageSource.magic)) + { + this.allySummonCooldown = 20; + } + + return super.attackEntityFrom(p_70097_1_, p_70097_2_); + } + } + + /** + * Basic mob attack. Default to touch of death in EntityCreature. Overridden by each mob to define their attack. + */ + protected void attackEntity(Entity p_70785_1_, float p_70785_2_) + { + if (this.attackTime <= 0 && p_70785_2_ < 1.2F && p_70785_1_.boundingBox.maxY > this.boundingBox.minY && p_70785_1_.boundingBox.minY < this.boundingBox.maxY) + { + this.attackTime = 20; + this.attackEntityAsMob(p_70785_1_); + } + } + + protected void func_145780_a(int p_145780_1_, int p_145780_2_, int p_145780_3_, Block p_145780_4_) + { + this.playSound("mob.silverfish.step", 0.15F, 1.0F); + } + + protected Item getDropItem() + { + return Item.getItemById(0); } @Override public void onSpawn(){ - this.spawnParticleEffect(); - } - - @Override - public void onDespawn(){ - this.spawnParticleEffect(); - } - - private void spawnParticleEffect(){ if(this.worldObj.isRemote){ for(int i=0;i<15;i++){ - Wizardry.proxy.spawnParticle(WizardryParticleType.DARK_MAGIC, worldObj, this.posX + this.rand.nextFloat(), this.posY + this.rand.nextFloat(), this.posZ + this.rand.nextFloat(), 0.0d, 0.0d, 0.0d, 0, 0.3f, 0.3f, 0.3f); - } - } - } - - @Override - public void onSuccessfulAttack(EntityLivingBase target){ - if(!target.isEntityAlive()){ - this.onKillEntity(target); - } - } - - @Override - public void onKillEntity(EntityLivingBase victim) { - // If the silverfish has a summoner, this is actually called from Wizardry's event handler rather than by - // Minecraft itself, because the damagesource being changed causes it not to get called. - if(!this.worldObj.isRemote){ - // Summons 1-4 more silverfish - int alliesToSummon = rand.nextInt(4) + 1; - - for(int i=0; i 0) + { + --this.allySummonCooldown; + + if (this.allySummonCooldown == 0) + { + i = MathHelper.floor_double(this.posX); + j = MathHelper.floor_double(this.posY); + k = MathHelper.floor_double(this.posZ); + boolean flag = false; + + for (int l = 0; !flag && l <= 5 && l >= -5; l = l <= 0 ? 1 - l : 0 - l) + { + for (i1 = 0; !flag && i1 <= 10 && i1 >= -10; i1 = i1 <= 0 ? 1 - i1 : 0 - i1) + { + for (int j1 = 0; !flag && j1 <= 10 && j1 >= -10; j1 = j1 <= 0 ? 1 - j1 : 0 - j1) + { + if (this.worldObj.getBlock(i + i1, j + l, k + j1) == Blocks.monster_egg) + { + if (!this.worldObj.getGameRules().getGameRuleBooleanValue("mobGriefing")) + { + int k1 = this.worldObj.getBlockMetadata(i + i1, j + l, k + j1); + ImmutablePair immutablepair = BlockSilverfish.func_150197_b(k1); + this.worldObj.setBlock(i + i1, j + l, k + j1, (Block)immutablepair.getLeft(), ((Integer)immutablepair.getRight()).intValue(), 3); + } + else + { + this.worldObj.func_147480_a(i + i1, j + l, k + j1, false); + } + + Blocks.monster_egg.onBlockDestroyedByPlayer(this.worldObj, i + i1, j + l, k + j1, 0); + + if (this.rand.nextBoolean()) + { + flag = true; + break; + } + } + } + } + } + } + } + + if (this.entityToAttack == null && !this.hasPath()) + { + i = MathHelper.floor_double(this.posX); + j = MathHelper.floor_double(this.posY + 0.5D); + k = MathHelper.floor_double(this.posZ); + int l1 = this.rand.nextInt(6); + Block block = this.worldObj.getBlock(i + Facing.offsetsXForSide[l1], j + Facing.offsetsYForSide[l1], k + Facing.offsetsZForSide[l1]); + i1 = this.worldObj.getBlockMetadata(i + Facing.offsetsXForSide[l1], j + Facing.offsetsYForSide[l1], k + Facing.offsetsZForSide[l1]); + + if (BlockSilverfish.func_150196_a(block)) + { + this.worldObj.setBlock(i + Facing.offsetsXForSide[l1], j + Facing.offsetsYForSide[l1], k + Facing.offsetsZForSide[l1], Blocks.monster_egg, BlockSilverfish.func_150195_a(block, i1), 3); + this.spawnExplosionParticle(); + this.setDead(); + } + else + { + this.updateWanderPath(); + } + } + else if (this.entityToAttack != null && !this.hasPath()) + { + this.entityToAttack = null; + } + } + } + + /** + * Takes a coordinate in and returns a weight to determine how likely this creature will try to path to the block. + * Args: x, y, z + */ + public float getBlockPathWeight(int p_70783_1_, int p_70783_2_, int p_70783_3_) + { + return this.worldObj.getBlock(p_70783_1_, p_70783_2_ - 1, p_70783_3_) == Blocks.stone ? 10.0F : super.getBlockPathWeight(p_70783_1_, p_70783_2_, p_70783_3_); + } + + /** + * Checks to make sure the light is not too bright where the mob is spawning + */ + protected boolean isValidLightLevel() + { return true; } - @Override - protected boolean processInteract(EntityPlayer player, EnumHand hand, ItemStack stack) { - // In this case, the delegate method determines whether super is called. - // Rather handily, we can make use of Java's 'stop as soon as you find true' method of evaluating OR statements. - return this.interactDelegate(player, hand, stack) || super.processInteract(player, hand, stack); - } - - @Override - public void writeEntityToNBT(NBTTagCompound nbttagcompound){ - super.writeEntityToNBT(nbttagcompound); - this.writeNBTDelegate(nbttagcompound); - } - - @Override - public void readEntityFromNBT(NBTTagCompound nbttagcompound){ - super.readEntityFromNBT(nbttagcompound); - this.writeNBTDelegate(nbttagcompound); - } - - // Recommended overrides - - @Override protected int getExperiencePoints(EntityPlayer player){ return 0; } - @Override protected boolean canDropLoot(){ return false; } - @Override protected Item getDropItem(){ return null; } - @Override protected ResourceLocation getLootTable(){ return null; } - @Override public boolean canPickUpLoot(){ return false; } - // This vanilla method has nothing to do with the custom despawn() method. - @Override protected boolean canDespawn(){ return false; } - - @Override - public boolean canAttackClass(Class entityType){ - // Returns true unless the given entity type is a flying entity. - return !EntityFlying.class.isAssignableFrom(entityType); - } - - @Override - public ITextComponent getDisplayName(){ - if(getCaster() != null){ - return new TextComponentTranslation(NAMEPLATE_TRANSLATION_KEY, getCaster().getName(), - new TextComponentTranslation("entity." + this.getEntityString() + ".name")); - }else{ - return super.getDisplayName(); - } - } - - @Override - public boolean hasCustomName(){ - // If this returns true, the renderer will show the nameplate when looking directly at the entity - return Wizardry.settings.showSummonedCreatureNames && getCaster() != null; + /** + * Get this Entity's EnumCreatureAttribute + */ + public EnumCreatureAttribute getCreatureAttribute() + { + return EnumCreatureAttribute.ARTHROPOD; } } \ No newline at end of file diff --git a/src/main/java/electroblob/wizardry/entity/living/EntitySkeletonMinion.java b/src/main/java/electroblob/wizardry/entity/living/EntitySkeletonMinion.java index e62435f3..70895a64 100644 --- a/src/main/java/electroblob/wizardry/entity/living/EntitySkeletonMinion.java +++ b/src/main/java/electroblob/wizardry/entity/living/EntitySkeletonMinion.java @@ -1,216 +1,436 @@ package electroblob.wizardry.entity.living; -import java.lang.ref.WeakReference; import java.util.Calendar; -import java.util.UUID; - -import javax.annotation.Nullable; +import electroblob.wizardry.EnumParticleType; import electroblob.wizardry.Wizardry; +import net.minecraft.enchantment.Enchantment; +import net.minecraft.enchantment.EnchantmentHelper; +import net.minecraft.entity.Entity; +import net.minecraft.entity.EntityCreature; +import net.minecraft.entity.EntityLiving; import net.minecraft.entity.EntityLivingBase; +import net.minecraft.entity.EnumCreatureAttribute; import net.minecraft.entity.IEntityLivingData; +import net.minecraft.entity.IRangedAttackMob; import net.minecraft.entity.SharedMonsterAttributes; +import net.minecraft.entity.ai.EntityAIArrowAttack; +import net.minecraft.entity.ai.EntityAIAttackOnCollide; +import net.minecraft.entity.ai.EntityAIFleeSun; import net.minecraft.entity.ai.EntityAIHurtByTarget; +import net.minecraft.entity.ai.EntityAILookIdle; import net.minecraft.entity.ai.EntityAINearestAttackableTarget; -import net.minecraft.entity.ai.attributes.AttributeModifier; -import net.minecraft.entity.monster.EntitySkeleton; -import net.minecraft.entity.monster.SkeletonType; +import net.minecraft.entity.ai.EntityAIRestrictSun; +import net.minecraft.entity.ai.EntityAISwimming; +import net.minecraft.entity.ai.EntityAIWander; +import net.minecraft.entity.monster.EntityMob; +import net.minecraft.entity.monster.IMob; import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.entity.projectile.EntityArrow; import net.minecraft.init.Blocks; import net.minecraft.init.Items; -import net.minecraft.init.MobEffects; -import net.minecraft.inventory.EntityEquipmentSlot; -import net.minecraft.item.Item; import net.minecraft.item.ItemStack; import net.minecraft.nbt.NBTTagCompound; +import net.minecraft.potion.Potion; import net.minecraft.potion.PotionEffect; -import net.minecraft.util.EnumHand; -import net.minecraft.util.EnumParticleTypes; -import net.minecraft.util.ResourceLocation; -import net.minecraft.util.text.ITextComponent; -import net.minecraft.util.text.TextComponentTranslation; -import net.minecraft.world.DifficultyInstance; +import net.minecraft.stats.AchievementList; +import net.minecraft.util.DamageSource; +import net.minecraft.util.MathHelper; import net.minecraft.world.World; +import net.minecraft.world.WorldProviderHell; -public class EntitySkeletonMinion extends EntitySkeleton implements ISummonedCreature { +public class EntitySkeletonMinion extends EntitySummonedCreature implements IRangedAttackMob { - // Field implementations - private int lifetime = 600; - private WeakReference casterReference; - private UUID casterUUID; + private EntityAIArrowAttack aiArrowAttack = new EntityAIArrowAttack(this, 1.0D, 20, 60, 15.0F); + private EntityAIAttackOnCollide aiAttackOnCollide = new EntityAIAttackOnCollide(this, EntityLivingBase.class, 1.2D, false); - // Setter + getter implementations - @Override public int getLifetime(){ return lifetime; } - @Override public void setLifetime(int lifetime){ this.lifetime = lifetime; } - @Override public WeakReference getCasterReference(){ return casterReference; } - @Override public void setCasterReference(WeakReference reference){ casterReference = reference; } - @Override public UUID getCasterUUID() { return casterUUID; } - @Override public void setCasterUUID(UUID uuid) { this.casterUUID = uuid; } - - /** - * Default shell constructor, only used by client. Lifetime defaults arbitrarily to 600, but this doesn't - * matter because the client side entity immediately gets the lifetime value copied over to it by this class - * anyway. When extending this class, you must override this constructor or Minecraft won't like it, but there's - * no need to do anything inside it other than call super(). - */ public EntitySkeletonMinion(World world){ super(world); - this.experienceValue = 0; + } + + public EntitySkeletonMinion(World world, double x, double y, double z, EntityLivingBase caster, int lifetime){ + + super(world, x, y, z, caster, lifetime); + this.tasks.addTask(1, new EntityAISwimming(this)); + this.tasks.addTask(2, new EntityAIRestrictSun(this)); + this.tasks.addTask(3, new EntityAIFleeSun(this, 1.0D)); + this.tasks.addTask(4, new EntityAIWander(this, 1.0D)); + this.tasks.addTask(5, new EntityAILookIdle(this)); + this.targetTasks.addTask(1, new EntityAIHurtByTarget(this, true)); + this.targetTasks.addTask(2, new EntityAINearestAttackableTarget(this, EntityLivingBase.class, 0, false, true, this.targetSelector)); + + if (world != null && !world.isRemote) + { + this.setCombatTask(); + } + } + + protected void applyEntityAttributes() + { + super.applyEntityAttributes(); + this.getEntityAttribute(SharedMonsterAttributes.movementSpeed).setBaseValue(0.25D); + } + + @Override + public boolean hasRangedAttack() { + return this.getSkeletonType() == 0; + } + + protected void entityInit() + { + super.entityInit(); + this.dataWatcher.addObject(13, new Byte((byte)0)); } /** - * Set lifetime to -1 to allow this creature to last forever. This constructor should be overridden when - * extending this class (be sure to call super()) so that AI and other things can be added. + * Returns true if the newer Entity AI code should be run */ - public EntitySkeletonMinion(World world, double x, double y, double z, EntityLivingBase caster, int lifetime){ - super(world); - this.setPosition(x, y, z); - this.casterReference = new WeakReference(caster); - this.experienceValue = 0; - this.lifetime = lifetime; - } - - // EntitySkeleton overrides - - // This particular override is pretty standard: let the superclass handle basic AI like swimming, but replace its - // targeting system with one that targets hostile mobs and takes the ADS into account. - @Override - protected void initEntityAI() + public boolean isAIEnabled() { - super.initEntityAI(); - this.targetTasks.taskEntries.clear(); - this.targetTasks.addTask(1, new EntityAIHurtByTarget(this, false)); - this.targetTasks.addTask(2, new EntityAINearestAttackableTarget(this, EntityLivingBase.class, - 0, false, true, this.getTargetSelector())); - } - - // Shouldn't have randomised armour, but does still need a bow! - @Override - protected void setEquipmentBasedOnDifficulty(DifficultyInstance difficulty) { - this.setItemStackToSlot(EntityEquipmentSlot.MAINHAND, new ItemStack(Items.BOW)); - } - - // Where the skeleton minion is summoned does not affect its type. - @Override - public IEntityLivingData onInitialSpawn(DifficultyInstance difficulty, @Nullable IEntityLivingData livingdata) - { - // Can't call super, so the code from the next level up (EntityLiving) had to be copied as well. - this.getEntityAttribute(SharedMonsterAttributes.FOLLOW_RANGE).applyModifier(new AttributeModifier("Random spawn bonus", this.rand.nextGaussian() * 0.05D, 1)); - - if (this.rand.nextFloat() < 0.05F) - { - this.setLeftHanded(true); - } - else - { - this.setLeftHanded(false); - } - - // Halloween pumpkin heads! Why not? - if (this.getItemStackFromSlot(EntityEquipmentSlot.HEAD) == null) - { - Calendar calendar = this.worldObj.getCurrentDate(); - - if (calendar.get(2) + 1 == 10 && calendar.get(5) == 31 && this.rand.nextFloat() < 0.25F) - { - this.setItemStackToSlot(EntityEquipmentSlot.HEAD, new ItemStack(this.rand.nextFloat() < 0.1F ? Blocks.LIT_PUMPKIN : Blocks.PUMPKIN)); - this.inventoryArmorDropChances[EntityEquipmentSlot.HEAD.getIndex()] = 0.0F; - } - } - - return livingdata; - } - - // Implementations - - @Override - public void setRevengeTarget(EntityLivingBase entity){ - if(this.shouldRevengeTarget(entity)) super.setRevengeTarget(entity); + return true; } - @Override - public void onUpdate(){ - super.onUpdate(); - this.updateDelegate(); + /** + * Returns the sound this mob makes while it's alive. + */ + protected String getLivingSound() + { + return "mob.skeleton.say"; + } + + /** + * Returns the sound this mob makes when it is hurt. + */ + protected String getHurtSound() + { + return "mob.skeleton.hurt"; + } + + /** + * Returns the sound this mob makes on death. + */ + protected String getDeathSound() + { + return "mob.skeleton.death"; + } + + /** + * Plays step sound at given x, y, z for the entity + */ + protected void playStepSound(int par1, int par2, int par3, int par4) + { + this.playSound("mob.skeleton.step", 0.15F, 1.0F); + } + + public boolean attackEntityAsMob(Entity par1Entity) + { + if (super.attackEntityAsMob(par1Entity)) + { + if (this.getSkeletonType() == 1 && par1Entity instanceof EntityLivingBase) + { + ((EntityLivingBase)par1Entity).addPotionEffect(new PotionEffect(Potion.wither.id, 200)); + } + + return true; + } + else + { + return false; + } + } + + /** + * Get this Entity's EnumCreatureAttribute + */ + public EnumCreatureAttribute getCreatureAttribute() + { + return EnumCreatureAttribute.UNDEAD; } @Override public void onSpawn(){ - this.spawnParticleEffect(); - } - - @Override - public void onDespawn(){ - this.spawnParticleEffect(); - } - - private void spawnParticleEffect(){ if(this.worldObj.isRemote){ for(int i=0;i<15;i++){ - this.worldObj.spawnParticle(EnumParticleTypes.SMOKE_LARGE, this.posX + this.rand.nextFloat(), this.posY + 1 + this.rand.nextFloat(), this.posZ + this.rand.nextFloat(), 0, 0, 0); + this.worldObj.spawnParticle("largesmoke", this.posX + this.rand.nextFloat(), this.posY + 1 + this.rand.nextFloat(), this.posZ + this.rand.nextFloat(), 0, 0, 0); } } } @Override - public boolean hasParticleEffect() { - return true; - } - - @Override - public void onSuccessfulAttack(EntityLivingBase target) { - if(this.getSkeletonType() == SkeletonType.WITHER){ - target.addPotionEffect(new PotionEffect(MobEffects.WITHER, 200)); - } + public void despawn(){ + if(this.worldObj.isRemote){ + for(int i=0;i<15;i++){ + this.worldObj.spawnParticle("largesmoke", this.posX + this.rand.nextFloat(), this.posY + 1 + this.rand.nextFloat(), this.posZ + this.rand.nextFloat(), 0, 0, 0); + } + } + super.despawn(); } - @Override - protected boolean processInteract(EntityPlayer player, EnumHand hand, ItemStack stack) { - // In this case, the delegate method determines whether super is called. - // Rather handily, we can make use of Java's 'stop as soon as you find true' method of evaluating OR statements. - return this.interactDelegate(player, hand, stack) || super.processInteract(player, hand, stack); + public void onUpdate(){ + + super.onUpdate(); + + //Adds subtle particle effect while alive + if(this.worldObj.isRemote && rand.nextInt(8) == 0) Wizardry.proxy.spawnParticle(EnumParticleType.DARK_MAGIC, worldObj, this.posX, this.posY + rand.nextDouble()*1.5, this.posZ, 0.0d, 0.0d, 0.0d, 0, 0.1f, 0.0f, 0.0f); } - @Override - public void writeEntityToNBT(NBTTagCompound nbttagcompound){ - super.writeEntityToNBT(nbttagcompound); - this.writeNBTDelegate(nbttagcompound); + /** + * Called frequently so the entity can update its state every tick as required. For example, zombies and skeletons + * use this to react to sunlight and start to burn. + */ + public void onLivingUpdate() + { + + if (this.worldObj.isDaytime() && !this.worldObj.isRemote) + { + float f = this.getBrightness(1.0F); + + if (f > 0.5F && this.rand.nextFloat() * 30.0F < (f - 0.4F) * 2.0F && this.worldObj.canBlockSeeTheSky(MathHelper.floor_double(this.posX), MathHelper.floor_double(this.posY), MathHelper.floor_double(this.posZ))) + { + boolean flag = true; + ItemStack itemstack = this.getEquipmentInSlot(4); + + if (itemstack != null) + { + if (itemstack.isItemStackDamageable()) + { + itemstack.setItemDamage(itemstack.getItemDamageForDisplay() + this.rand.nextInt(2)); + + if (itemstack.getItemDamageForDisplay() >= itemstack.getMaxDamage()) + { + this.renderBrokenItemStack(itemstack); + this.setCurrentItemOrArmor(4, (ItemStack)null); + } + } + + flag = false; + } + + if (flag) + { + this.setFire(8); + } + } + } + + if (this.worldObj.isRemote && this.getSkeletonType() == 1) + { + this.setSize(0.72F, 2.34F); + } + + super.onLivingUpdate(); } - @Override - public void readEntityFromNBT(NBTTagCompound nbttagcompound){ - super.readEntityFromNBT(nbttagcompound); - this.writeNBTDelegate(nbttagcompound); - } + /** + * Handles updating while being ridden by an entity + */ + public void updateRidden() + { + super.updateRidden(); - // Recommended overrides - - @Override protected int getExperiencePoints(EntityPlayer player){ return 0; } - @Override protected boolean canDropLoot(){ return false; } - @Override protected Item getDropItem(){ return null; } - @Override protected ResourceLocation getLootTable(){ return null; } - @Override public boolean canPickUpLoot(){ return false; } - // This vanilla method has nothing to do with the custom despawn() method. - @Override protected boolean canDespawn(){ return false; } - - @Override - public boolean canAttackClass(Class entityType){ - return true; - } - - @Override - public ITextComponent getDisplayName(){ - if(getCaster() != null){ - return new TextComponentTranslation(NAMEPLATE_TRANSLATION_KEY, getCaster().getName(), - new TextComponentTranslation("entity." + this.getEntityString() + ".name")); - }else{ - return super.getDisplayName(); + if (this.ridingEntity instanceof EntityCreature) + { + EntityCreature entitycreature = (EntityCreature)this.ridingEntity; + this.renderYawOffset = entitycreature.renderYawOffset; } } - @Override - public boolean hasCustomName(){ - // If this returns true, the renderer will show the nameplate when looking directly at the entity - return Wizardry.settings.showSummonedCreatureNames && getCaster() != null; + /** + * Called when the mob's health reaches 0. + */ + public void onDeath(DamageSource par1DamageSource) + { + super.onDeath(par1DamageSource); + + if (par1DamageSource.getSourceOfDamage() instanceof EntityArrow && par1DamageSource.getEntity() instanceof EntityPlayer) + { + EntityPlayer entityplayer = (EntityPlayer)par1DamageSource.getEntity(); + double d0 = entityplayer.posX - this.posX; + double d1 = entityplayer.posZ - this.posZ; + + if (d0 * d0 + d1 * d1 >= 2500.0D) + { + entityplayer.triggerAchievement(AchievementList.snipeSkeleton); + } + } + } + + /** + * Returns the item ID for the item the mob drops on death. + */ + protected int getDropItemId() + { + return -1; + } + + /** + * Drop 0-2 items of this living's type. @param par1 - Whether this entity has recently been hit by a player. @param + * par2 - Level of Looting used to kill this mob. + */ + protected void dropFewItems(boolean par1, int par2) + { + + } + + /** + * Makes entity wear random armor based on difficulty + */ + protected void addRandomArmor() + { + this.setCurrentItemOrArmor(0, new ItemStack(Items.bow)); + } + + public IEntityLivingData onSpawnWithEgg(IEntityLivingData par1EntityLivingData) + { + par1EntityLivingData = super.onSpawnWithEgg(par1EntityLivingData); + + if (this.worldObj.provider instanceof WorldProviderHell && this.getRNG().nextInt(5) > 0) + { + this.tasks.addTask(4, this.aiAttackOnCollide); + this.setSkeletonType(1); + this.setCurrentItemOrArmor(0, new ItemStack(Items.stone_sword)); + this.getEntityAttribute(SharedMonsterAttributes.attackDamage).setBaseValue(4.0D); + } + else + { + this.tasks.addTask(4, this.aiArrowAttack); + this.addRandomArmor(); + this.enchantEquipment(); + } + + this.setCanPickUpLoot(this.rand.nextFloat() < 0.55F * this.worldObj.func_147462_b(this.posX, this.posY, this.posZ)); + + if (this.getEquipmentInSlot(4) == null) + { + Calendar calendar = this.worldObj.getCurrentDate(); + + if (calendar.get(2) + 1 == 10 && calendar.get(5) == 31 && this.rand.nextFloat() < 0.25F) + { + this.setCurrentItemOrArmor(4, new ItemStack(this.rand.nextFloat() < 0.1F ? Blocks.lit_pumpkin : Blocks.pumpkin)); + this.equipmentDropChances[4] = 0.0F; + } + } + + return par1EntityLivingData; + } + + /** + * sets this entity's combat AI. + */ + public void setCombatTask() + { + this.tasks.removeTask(this.aiAttackOnCollide); + this.tasks.removeTask(this.aiArrowAttack); + ItemStack itemstack = this.getHeldItem(); + + if (itemstack != null && itemstack.getItem() == Items.bow) + { + this.tasks.addTask(4, this.aiArrowAttack); + } + else + { + this.tasks.addTask(4, this.aiAttackOnCollide); + } + } + /** + * Attack the specified entity using a ranged attack. + */ + public void attackEntityWithRangedAttack(EntityLivingBase p_82196_1_, float p_82196_2_) + { + EntityArrow entityarrow = new EntityArrow(this.worldObj, this, p_82196_1_, 1.6F, (float)(14 - this.worldObj.difficultySetting.getDifficultyId() * 4)); + int i = EnchantmentHelper.getEnchantmentLevel(Enchantment.power.effectId, this.getHeldItem()); + int j = EnchantmentHelper.getEnchantmentLevel(Enchantment.punch.effectId, this.getHeldItem()); + entityarrow.setDamage((double)(p_82196_2_ * 2.0F) + this.rand.nextGaussian() * 0.25D + (double)((float)this.worldObj.difficultySetting.getDifficultyId() * 0.11F)); + entityarrow.shootingEntity = this.getCaster(); + + if (i > 0) + { + entityarrow.setDamage(entityarrow.getDamage() + (double)i * 0.5D + 0.5D); + } + + if (j > 0) + { + entityarrow.setKnockbackStrength(j); + } + + if (EnchantmentHelper.getEnchantmentLevel(Enchantment.flame.effectId, this.getHeldItem()) > 0 || this.getSkeletonType() == 1) + { + entityarrow.setFire(100); + } + + this.playSound("random.bow", 1.0F, 1.0F / (this.getRNG().nextFloat() * 0.4F + 0.8F)); + this.worldObj.spawnEntityInWorld(entityarrow); + } + + /** + * Return this skeleton's type. + */ + public int getSkeletonType() + { + return this.dataWatcher.getWatchableObjectByte(13); + } + + /** + * Set this skeleton's type. + */ + public void setSkeletonType(int par1) + { + this.dataWatcher.updateObject(13, Byte.valueOf((byte)par1)); + this.isImmuneToFire = par1 == 1; + + if (par1 == 1) + { + this.setSize(0.72F, 2.34F); + } + else + { + this.setSize(0.6F, 1.8F); + } + } + + /** + * (abstract) Protected helper method to read subclass entity data from NBT. + */ + public void readEntityFromNBT(NBTTagCompound par1NBTTagCompound) + { + super.readEntityFromNBT(par1NBTTagCompound); + + if (par1NBTTagCompound.hasKey("SkeletonType")) + { + byte b0 = par1NBTTagCompound.getByte("SkeletonType"); + this.setSkeletonType(b0); + } + + this.setCombatTask(); + } + + /** + * (abstract) Protected helper method to write subclass entity data to NBT. + */ + public void writeEntityToNBT(NBTTagCompound par1NBTTagCompound) + { + super.writeEntityToNBT(par1NBTTagCompound); + par1NBTTagCompound.setByte("SkeletonType", (byte)this.getSkeletonType()); + } + + /** + * Sets the held item, or an armor slot. Slot 0 is held item. Slot 1-4 is armor. Params: Item, slot + */ + public void setCurrentItemOrArmor(int par1, ItemStack par2ItemStack) + { + super.setCurrentItemOrArmor(par1, par2ItemStack); + + if (!this.worldObj.isRemote && par1 == 0) + { + this.setCombatTask(); + } + } + + /** + * Returns the Y Offset of this entity. + */ + public double getYOffset() + { + return super.getYOffset() - 0.5D; } } diff --git a/src/main/java/electroblob/wizardry/entity/living/EntitySpiderMinion.java b/src/main/java/electroblob/wizardry/entity/living/EntitySpiderMinion.java index f5714361..7ad638b6 100644 --- a/src/main/java/electroblob/wizardry/entity/living/EntitySpiderMinion.java +++ b/src/main/java/electroblob/wizardry/entity/living/EntitySpiderMinion.java @@ -1,207 +1,290 @@ package electroblob.wizardry.entity.living; -import java.lang.ref.WeakReference; -import java.util.UUID; - +import electroblob.wizardry.EnumParticleType; import electroblob.wizardry.Wizardry; -import electroblob.wizardry.util.WizardryParticleType; -import net.minecraft.entity.EntityFlying; +import net.minecraft.entity.Entity; +import net.minecraft.entity.EntityLiving; import net.minecraft.entity.EntityLivingBase; +import net.minecraft.entity.EnumCreatureAttribute; import net.minecraft.entity.IEntityLivingData; import net.minecraft.entity.SharedMonsterAttributes; +import net.minecraft.entity.ai.EntityAIAttackOnCollide; import net.minecraft.entity.ai.EntityAIHurtByTarget; +import net.minecraft.entity.ai.EntityAILookIdle; +import net.minecraft.entity.ai.EntityAIMoveTowardsRestriction; import net.minecraft.entity.ai.EntityAINearestAttackableTarget; -import net.minecraft.entity.ai.attributes.AttributeModifier; -import net.minecraft.entity.monster.EntityCaveSpider; -import net.minecraft.entity.player.EntityPlayer; -import net.minecraft.init.MobEffects; -import net.minecraft.item.Item; -import net.minecraft.item.ItemStack; -import net.minecraft.nbt.NBTTagCompound; +import net.minecraft.entity.ai.EntityAISwimming; +import net.minecraft.entity.ai.EntityAIWander; +import net.minecraft.entity.monster.EntityMob; +import net.minecraft.entity.monster.IMob; +import net.minecraft.potion.Potion; import net.minecraft.potion.PotionEffect; -import net.minecraft.util.EnumHand; -import net.minecraft.util.ResourceLocation; -import net.minecraft.util.text.ITextComponent; -import net.minecraft.util.text.TextComponentTranslation; -import net.minecraft.world.DifficultyInstance; -import net.minecraft.world.EnumDifficulty; +import net.minecraft.util.MathHelper; import net.minecraft.world.World; -public class EntitySpiderMinion extends EntityCaveSpider implements ISummonedCreature { +public class EntitySpiderMinion extends EntitySummonedCreature { - // Field implementations - private int lifetime = 600; - private WeakReference casterReference; - private UUID casterUUID; - - // Setter + getter implementations - @Override public int getLifetime(){ return lifetime; } - @Override public void setLifetime(int lifetime){ this.lifetime = lifetime; } - @Override public WeakReference getCasterReference(){ return casterReference; } - @Override public void setCasterReference(WeakReference reference){ casterReference = reference; } - @Override public UUID getCasterUUID() { return casterUUID; } - @Override public void setCasterUUID(UUID uuid) { this.casterUUID = uuid; } - - /** - * Default shell constructor, only used by client. Lifetime defaults arbitrarily to 600, but this doesn't - * matter because the client side entity immediately gets the lifetime value copied over to it by this class - * anyway. When extending this class, you must override this constructor or Minecraft won't like it, but there's - * no need to do anything inside it other than call super(). - */ public EntitySpiderMinion(World world){ - super(world); - this.experienceValue = 0; - } - - /** - * Set lifetime to -1 to allow this creature to last forever. This constructor should be overridden when - * extending this class (be sure to call super()) so that AI and other things can be added. - */ - public EntitySpiderMinion(World world, double x, double y, double z, EntityLivingBase caster, int lifetime){ - super(world); - this.setPosition(x, y, z); - this.casterReference = new WeakReference(caster); - this.experienceValue = 0; - this.lifetime = lifetime; - } - - // EntitySpider overrides + super(world); + } - // This particular override is pretty standard: let the superclass handle basic AI like swimming, but replace its - // targeting system with one that targets hostile mobs and takes the ADS into account. - @Override - protected void initEntityAI() - { - super.initEntityAI(); - this.targetTasks.taskEntries.clear(); - // Spiders use a custom AI type specific to spiders which I can't access, but it's just an extension of - // EntityAINearestAttackableTarget which takes daylight into account. Since I want spider minions to attack - // regardless of daylight, I can just use EntityAINearestAttackableTarget. - this.targetTasks.addTask(1, new EntityAIHurtByTarget(this, false)); - this.targetTasks.addTask(2, new EntityAINearestAttackableTarget(this, EntityLivingBase.class, - 0, false, true, this.getTargetSelector())); - } - - // No spider jockeys! - @Override - public IEntityLivingData onInitialSpawn(DifficultyInstance difficulty, IEntityLivingData livingdata) { - - // Can't call super, so the code from the next level up (EntityLiving) had to be copied as well. - this.getEntityAttribute(SharedMonsterAttributes.FOLLOW_RANGE).applyModifier(new AttributeModifier("Random spawn bonus", this.rand.nextGaussian() * 0.05D, 1)); + public EntitySpiderMinion(World world, double x, double y, double z, EntityLivingBase caster, int lifetime){ + + super(world, x, y, z, caster, lifetime); + this.setSize(0.7F, 0.5F); + this.tasks.addTask(0, new EntityAISwimming(this)); + this.tasks.addTask(1, new EntityAIAttackOnCollide(this, EntityLivingBase.class, 1.0D, true)); + this.tasks.addTask(2, new EntityAIMoveTowardsRestriction(this, 1.0D)); + this.tasks.addTask(3, new EntityAIWander(this, 1.0D)); + this.tasks.addTask(4, new EntityAILookIdle(this)); + this.targetTasks.addTask(1, new EntityAIHurtByTarget(this, true)); + this.targetTasks.addTask(2, new EntityAINearestAttackableTarget(this, EntityLivingBase.class, 0, false, true, this.targetSelector)); + this.experienceValue = 0; + } - if (this.rand.nextFloat() < 0.05F) + /** + * Returns true if the newer Entity AI code should be run + */ + protected boolean isAIEnabled() + { + return true; + } + + @Override + public boolean hasRangedAttack() { + return false; + } + + protected void entityInit() + { + super.entityInit(); + this.dataWatcher.addObject(16, new Byte((byte)0)); + } + + @Override + public void onSpawn(){ + if(this.worldObj.isRemote){ + for(int i=0;i<15;i++){ + Wizardry.proxy.spawnParticle(EnumParticleType.DARK_MAGIC, worldObj, this.posX + this.rand.nextFloat(), this.posY + this.rand.nextFloat(), this.posZ + this.rand.nextFloat(), 0.0d, 0.0d, 0.0d, 0, 0.1f, 0.2f, 0.0f); + } + } + } + + @Override + public void despawn(){ + if(this.worldObj.isRemote){ + for(int i=0;i<15;i++){ + Wizardry.proxy.spawnParticle(EnumParticleType.DARK_MAGIC, worldObj, this.posX + this.rand.nextFloat(), this.posY + this.rand.nextFloat(), this.posZ + this.rand.nextFloat(), 0.0d, 0.0d, 0.0d, 0, 0.1f, 0.2f, 0.0f); + } + } + super.despawn(); + } + + /** + * Called to update the entity's position/logic. + */ + public void onUpdate() + { + // Adds subtle particle effect while alive + if(this.worldObj.isRemote && rand.nextInt(8) == 0) Wizardry.proxy.spawnParticle(EnumParticleType.DARK_MAGIC, worldObj, this.posX, this.posY + rand.nextDouble()*1.5, this.posZ, 0.0d, 0.0d, 0.0d, 0, 0.1f, 0.0f, 0.0f); + + super.onUpdate(); + + if (!this.worldObj.isRemote) { - this.setLeftHanded(true); + this.setBesideClimbableBlock(this.isCollidedHorizontally); + } + } + + protected void applyEntityAttributes() + { + super.applyEntityAttributes(); + this.getEntityAttribute(SharedMonsterAttributes.maxHealth).setBaseValue(12.0D); + this.getEntityAttribute(SharedMonsterAttributes.movementSpeed).setBaseValue(0.3D); + } + + /** + * Finds the closest player within 16 blocks to attack, or null if this Entity isn't interested in attacking + * (Animals, Spiders at day, peaceful PigZombies). + */ + protected Entity findPlayerToAttack() + { + float f = this.getBrightness(1.0F); + + if (f < 0.5F) + { + double d0 = 16.0D; + return this.worldObj.getClosestVulnerablePlayerToEntity(this, d0); } else { - this.setLeftHanded(false); + return null; } - - // Don't need anything from EntitySpider, since neither spider jockeys nor group data is relevant. - return livingdata; - } + } - // Implementations + /** + * Returns the sound this mob makes while it's alive. + */ + protected String getLivingSound() + { + return "mob.spider.say"; + } - @Override - public void setRevengeTarget(EntityLivingBase entity){ - if(this.shouldRevengeTarget(entity)) super.setRevengeTarget(entity); - } + /** + * Returns the sound this mob makes when it is hurt. + */ + protected String getHurtSound() + { + return "mob.spider.say"; + } - @Override - public void onUpdate(){ - super.onUpdate(); - this.updateDelegate(); - } + /** + * Returns the sound this mob makes on death. + */ + protected String getDeathSound() + { + return "mob.spider.death"; + } - @Override - public void onSpawn(){ - this.spawnParticleEffect(); - } + /** + * Plays step sound at given x, y, z for the entity + */ + protected void playStepSound(int par1, int par2, int par3, int par4) + { + this.playSound("mob.spider.step", 0.15F, 1.0F); + } - @Override - public void onDespawn(){ - this.spawnParticleEffect(); - } - - private void spawnParticleEffect(){ - if(this.worldObj.isRemote){ - for(int i=0;i<15;i++){ - Wizardry.proxy.spawnParticle(WizardryParticleType.DARK_MAGIC, worldObj, this.posX + this.rand.nextFloat(), this.posY + this.rand.nextFloat(), this.posZ + this.rand.nextFloat(), 0.0d, 0.0d, 0.0d, 0, 0.1f, 0.2f, 0.0f); - } - } - } + /** + * Basic mob attack. Default to touch of death in EntityCreature. Overridden by each mob to define their attack. + */ + protected void attackEntity(Entity par1Entity, float par2) + { + float f1 = this.getBrightness(1.0F); - @Override - public boolean hasParticleEffect() { - return true; - } - - @Override - public void onSuccessfulAttack(EntityLivingBase target){ - - int seconds = 0; + if (f1 > 0.5F && this.rand.nextInt(100) == 0) + { + this.entityToAttack = null; + } + else + { + if (par2 > 2.0F && par2 < 6.0F && this.rand.nextInt(10) == 0) + { + if (this.onGround) + { + double d0 = par1Entity.posX - this.posX; + double d1 = par1Entity.posZ - this.posZ; + float f2 = MathHelper.sqrt_double(d0 * d0 + d1 * d1); + this.motionX = d0 / (double)f2 * 0.5D * 0.800000011920929D + this.motionX * 0.20000000298023224D; + this.motionZ = d1 / (double)f2 * 0.5D * 0.800000011920929D + this.motionZ * 0.20000000298023224D; + this.motionY = 0.4000000059604645D; + } + } + else + { + super.attackEntity(par1Entity, par2); + } + } + } - if(this.worldObj.getDifficulty() == EnumDifficulty.NORMAL){ - seconds = 7; - }else if(this.worldObj.getDifficulty() == EnumDifficulty.HARD){ - seconds = 15; + /** + * Returns the item ID for the item the mob drops on death. + */ + protected int getDropItemId() + { + return -1; + } + + /** + * returns true if this entity is by a ladder, false otherwise + */ + public boolean isOnLadder() + { + return this.isBesideClimbableBlock(); + } + + /** + * Sets the Entity inside a web block. + */ + public void setInWeb() {} + + /** + * Get this Entity's EnumCreatureAttribute + */ + public EnumCreatureAttribute getCreatureAttribute() + { + return EnumCreatureAttribute.ARTHROPOD; + } + + public boolean isPotionApplicable(PotionEffect par1PotionEffect) + { + return par1PotionEffect.getPotionID() == Potion.poison.id ? false : super.isPotionApplicable(par1PotionEffect); + } + + /** + * Returns true if the WatchableObject (Byte) is 0x01 otherwise returns false. The WatchableObject is updated using + * setBesideClimableBlock. + */ + public boolean isBesideClimbableBlock() + { + return (this.dataWatcher.getWatchableObjectByte(16) & 1) != 0; + } + + /** + * Updates the WatchableObject (Byte) created in entityInit(), setting it to 0x01 if par1 is true or 0x00 if it is + * false. + */ + public void setBesideClimbableBlock(boolean par1) + { + byte b0 = this.dataWatcher.getWatchableObjectByte(16); + + if (par1) + { + b0 = (byte)(b0 | 1); + } + else + { + b0 &= -2; } - if(seconds > 0){ - target.addPotionEffect(new PotionEffect(MobEffects.POISON, seconds * 20, 0)); + this.dataWatcher.updateObject(16, Byte.valueOf(b0)); + } +/* + public boolean attackEntityAsMob(Entity par1Entity) + { + if (super.attackEntityAsMob(par1Entity)) + { + if (par1Entity instanceof EntityLivingBase) + { + byte b0 = 0; + + if (this.worldObj.difficultySetting > 1) + { + if (this.worldObj.difficultySetting == 2) + { + b0 = 7; + } + else if (this.worldObj.difficultySetting == 3) + { + b0 = 15; + } + } + + if (b0 > 0) + { + ((EntityLivingBase)par1Entity).addPotionEffect(new PotionEffect(Potion.poison.id, b0 * 20, 0)); + } + } + + return true; } - } - - @Override - protected boolean processInteract(EntityPlayer player, EnumHand hand, ItemStack stack) { - // In this case, the delegate method determines whether super is called. - // Rather handily, we can make use of Java's 'stop as soon as you find true' method of evaluating OR statements. - return this.interactDelegate(player, hand, stack) || super.processInteract(player, hand, stack); - } - - @Override - public void writeEntityToNBT(NBTTagCompound nbttagcompound){ - super.writeEntityToNBT(nbttagcompound); - this.writeNBTDelegate(nbttagcompound); - } - - @Override - public void readEntityFromNBT(NBTTagCompound nbttagcompound){ - super.readEntityFromNBT(nbttagcompound); - this.writeNBTDelegate(nbttagcompound); - } - - // Recommended overrides - - @Override protected int getExperiencePoints(EntityPlayer player){ return 0; } - @Override protected boolean canDropLoot(){ return false; } - @Override protected Item getDropItem(){ return null; } - @Override protected ResourceLocation getLootTable(){ return null; } - @Override public boolean canPickUpLoot(){ return false; } - // This vanilla method has nothing to do with the custom despawn() method. - @Override protected boolean canDespawn(){ return false; } - - @Override - public boolean canAttackClass(Class entityType){ - // Returns true unless the given entity type is a flying entity. - return !EntityFlying.class.isAssignableFrom(entityType); - } - - @Override - public ITextComponent getDisplayName(){ - if(getCaster() != null){ - return new TextComponentTranslation(NAMEPLATE_TRANSLATION_KEY, getCaster().getName(), - new TextComponentTranslation("entity." + this.getEntityString() + ".name")); - }else{ - return super.getDisplayName(); - } - } - - @Override - public boolean hasCustomName(){ - // If this returns true, the renderer will show the nameplate when looking directly at the entity - return Wizardry.settings.showSummonedCreatureNames && getCaster() != null; - } + else + { + return false; + } + } +*/ + public IEntityLivingData onSpawnWithEgg(IEntityLivingData par1EntityLivingData) + { + return par1EntityLivingData; + } } diff --git a/src/main/java/electroblob/wizardry/entity/living/EntitySpiritHorse.java b/src/main/java/electroblob/wizardry/entity/living/EntitySpiritHorse.java index 47be717e..4dd0abae 100644 --- a/src/main/java/electroblob/wizardry/entity/living/EntitySpiritHorse.java +++ b/src/main/java/electroblob/wizardry/entity/living/EntitySpiritHorse.java @@ -1,31 +1,46 @@ package electroblob.wizardry.entity.living; -import electroblob.wizardry.WizardData; +import java.util.UUID; + +import cpw.mods.fml.relauncher.Side; +import cpw.mods.fml.relauncher.SideOnly; +import electroblob.wizardry.EnumParticleType; +import electroblob.wizardry.ExtendedPlayer; import electroblob.wizardry.Wizardry; +import electroblob.wizardry.WizardryUtilities; import electroblob.wizardry.item.ItemWand; -import electroblob.wizardry.registry.WizardrySounds; -import electroblob.wizardry.util.WizardryParticleType; -import electroblob.wizardry.util.WizardryUtilities; +import net.minecraft.block.Block; +import net.minecraft.block.Block.SoundType; import net.minecraft.entity.Entity; +import net.minecraft.entity.EntityAgeable; import net.minecraft.entity.EntityLivingBase; import net.minecraft.entity.IEntityLivingData; import net.minecraft.entity.SharedMonsterAttributes; +import net.minecraft.entity.ai.EntityAIFollowParent; +import net.minecraft.entity.ai.EntityAILookIdle; +import net.minecraft.entity.ai.EntityAIPanic; +import net.minecraft.entity.ai.EntityAISwimming; +import net.minecraft.entity.ai.EntityAIWander; +import net.minecraft.entity.ai.EntityAIWatchClosest; +import net.minecraft.entity.ai.attributes.IAttribute; +import net.minecraft.entity.ai.attributes.IAttributeInstance; +import net.minecraft.entity.ai.attributes.RangedAttribute; import net.minecraft.entity.passive.EntityAnimal; import net.minecraft.entity.passive.EntityHorse; import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.init.Blocks; +import net.minecraft.init.Items; +import net.minecraft.inventory.AnimalChest; import net.minecraft.item.Item; +import net.minecraft.item.ItemBlock; import net.minecraft.item.ItemStack; +import net.minecraft.nbt.NBTTagCompound; +import net.minecraft.potion.Potion; import net.minecraft.util.DamageSource; -import net.minecraft.util.EnumHand; -import net.minecraft.util.text.ITextComponent; -import net.minecraft.util.text.TextComponentTranslation; -import net.minecraft.util.text.translation.I18n; -import net.minecraft.world.DifficultyInstance; +import net.minecraft.util.MathHelper; +import net.minecraft.util.StatCollector; import net.minecraft.world.World; -/** Does not implement ISummonedCreature because it has different despawning rules and because EntityHorse already - * has an owner system. */ -@SuppressWarnings("deprecation") // It's what Entity does, so... public class EntitySpiritHorse extends EntityHorse { private int idleTimer = 0; @@ -35,16 +50,19 @@ public class EntitySpiritHorse extends EntityHorse { super(par1World); } + /** + * Gets the username of the entity. + */ @Override - public String getName() + public String getCommandSenderName() { - if (this.hasCustomName()) + if (this.hasCustomNameTag()) { return this.getCustomNameTag(); } else { - return I18n.translateToLocal("entity.wizardry.Spirit Horse.name"); + return StatCollector.translateToLocal("entity.wizardry.Spirit Horse.name"); } } @@ -78,36 +96,36 @@ public class EntitySpiritHorse extends EntityHorse { protected void applyEntityAttributes() { super.applyEntityAttributes(); - this.getEntityAttribute(SharedMonsterAttributes.MAX_HEALTH).setBaseValue(24.0D); + this.getEntityAttribute(SharedMonsterAttributes.maxHealth).setBaseValue(24.0D); } @Override public void openGUI(EntityPlayer p_110199_1_){} @Override - public boolean processInteract(EntityPlayer player, EnumHand hand, ItemStack stack){ + public boolean interact(EntityPlayer par1EntityPlayer){ - ItemStack itemstack = player.inventory.getCurrentItem(); + ItemStack itemstack = par1EntityPlayer.inventory.getCurrentItem(); // Allows the owner (but not other players) to dispel the spirit horse using a wand (shift-clicking, because clicking mounts the horse in this case). - if(itemstack != null && itemstack.getItem() instanceof ItemWand && this.getOwner() == player && player.isSneaking()){ + if(itemstack != null && itemstack.getItem() instanceof ItemWand && this.getOwner() == par1EntityPlayer && par1EntityPlayer.isSneaking()){ // Prevents accidental double clicking. if(this.ticksExisted > 20){ for(int i=0;i<15;i++){ - Wizardry.proxy.spawnParticle(WizardryParticleType.SPARKLE, worldObj, this.posX - this.width/2 + this.rand.nextFloat()*width, this.posY + this.height*this.rand.nextFloat() + 0.2f, this.posZ - this.width/2 + this.rand.nextFloat()*width, 0, 0, 0, 48 + this.rand.nextInt(12), 0.8f, 0.8f, 1.0f); + Wizardry.proxy.spawnParticle(EnumParticleType.SPARKLE, worldObj, this.posX - this.width/2 + this.rand.nextFloat()*width, this.posY + this.height*this.rand.nextFloat() + 0.2f, this.posZ - this.width/2 + this.rand.nextFloat()*width, 0, 0, 0, 48 + this.rand.nextInt(12), 0.8f, 0.8f, 1.0f); } this.setDead(); - if(WizardData.get(player) != null){ - WizardData.get(player).hasSpiritHorse = false; + if(ExtendedPlayer.get(par1EntityPlayer) != null){ + ExtendedPlayer.get(par1EntityPlayer).hasSpiritHorse = false; } - this.playSound(WizardrySounds.SPELL_HEAL, 0.7F, rand.nextFloat() * 0.4F + 1.0F); + this.playSound("wizardry:heal", 0.7F, rand.nextFloat() * 0.4F + 1.0F); // This is necessary to prevent the wand's spell being cast when performing this action. return true; } return false; } - return super.processInteract(player, hand, itemstack); + return super.interact(par1EntityPlayer); } @Override @@ -116,16 +134,18 @@ public class EntitySpiritHorse extends EntityHorse { super.onDeath(par1DamageSource); // Allows player to summon another spirit horse once this one has died. - if(this.getOwner() instanceof EntityPlayer && WizardData.get((EntityPlayer)this.getOwner()) != null){ - WizardData.get((EntityPlayer)this.getOwner()).hasSpiritHorse = false; + if(this.getOwner() instanceof EntityPlayer && ExtendedPlayer.get((EntityPlayer)this.getOwner()) != null){ + ExtendedPlayer.get((EntityPlayer)this.getOwner()).hasSpiritHorse = false; } } // I wrote this one! private EntityLivingBase getOwner(){ + + // func_152119_ch retrieves the owner UUID from the datawatcher + if(this.func_152119_ch() == null || WizardryUtilities.verifyUUIDString(this.func_152119_ch())) return null; - // I think the DataManager stores any objects, so it now stores the UUID instead of its string representation. - Entity owner = WizardryUtilities.getEntityByUUID(worldObj, this.getOwnerUniqueId()); + Entity owner = WizardryUtilities.getEntityByUUID(worldObj, UUID.fromString(this.func_152119_ch())); if(owner instanceof EntityLivingBase){ return (EntityLivingBase)owner; @@ -141,11 +161,11 @@ public class EntitySpiritHorse extends EntityHorse { // Adds a dust particle effect if(this.worldObj.isRemote){ - Wizardry.proxy.spawnParticle(WizardryParticleType.DUST, worldObj, this.posX - this.width/2 + this.rand.nextFloat()*width, this.posY + this.height*this.rand.nextFloat() + 0.2f, this.posZ - this.width/2 + this.rand.nextFloat()*width, 0, 0, 0, 0, 0.8f, 0.8f, 1.0f); + Wizardry.proxy.spawnParticle(EnumParticleType.DUST, worldObj, this.posX - this.width/2 + this.rand.nextFloat()*width, this.posY + this.height*this.rand.nextFloat() + 0.2f, this.posZ - this.width/2 + this.rand.nextFloat()*width, 0, 0, 0, 0, 0.8f, 0.8f, 1.0f); } // Spirit horse disappears a short time after being dismounted. - if(!this.isBeingRidden()){ + if(this.riddenByEntity == null){ this.idleTimer++; }else if(this.idleTimer > 0){ this.idleTimer = 0; @@ -154,13 +174,13 @@ public class EntitySpiritHorse extends EntityHorse { if(this.idleTimer > 200){ if(this.worldObj.isRemote){ for(int i=0;i<15;i++){ - Wizardry.proxy.spawnParticle(WizardryParticleType.SPARKLE, worldObj, this.posX - this.width/2 + this.rand.nextFloat()*width, this.posY + this.height*this.rand.nextFloat() + 0.2f, this.posZ - this.width/2 + this.rand.nextFloat()*width, 0, 0, 0, 48 + this.rand.nextInt(12), 0.8f, 0.8f, 1.0f); + Wizardry.proxy.spawnParticle(EnumParticleType.SPARKLE, worldObj, this.posX - this.width/2 + this.rand.nextFloat()*width, this.posY + this.height*this.rand.nextFloat() + 0.2f, this.posZ - this.width/2 + this.rand.nextFloat()*width, 0, 0, 0, 48 + this.rand.nextInt(12), 0.8f, 0.8f, 1.0f); } } - this.playSound(WizardrySounds.SPELL_HEAL, 0.7F, rand.nextFloat() * 0.4F + 1.0F); + this.playSound("wizardry:heal", 0.7F, rand.nextFloat() * 0.4F + 1.0F); // Allows player to summon another spirit horse once this one has disappeared. - if(this.getOwner() instanceof EntityPlayer && WizardData.get((EntityPlayer)this.getOwner()) != null){ - WizardData.get((EntityPlayer)this.getOwner()).hasSpiritHorse = false; + if(this.getOwner() instanceof EntityPlayer && ExtendedPlayer.get((EntityPlayer)this.getOwner()) != null){ + ExtendedPlayer.get((EntityPlayer)this.getOwner()).hasSpiritHorse = false; } this.setDead(); } @@ -172,32 +192,16 @@ public class EntitySpiritHorse extends EntityHorse { } @Override - public IEntityLivingData onInitialSpawn(DifficultyInstance difficulty, IEntityLivingData data){ + public IEntityLivingData onSpawnWithEgg(IEntityLivingData par1EntityLivingData){ // Adds Particles on spawn. Due to client/server differences this cannot be done in the item. if(this.worldObj.isRemote){ for(int i=0;i<15;i++){ - Wizardry.proxy.spawnParticle(WizardryParticleType.SPARKLE, worldObj, this.posX - this.width/2 + this.rand.nextFloat()*width, this.posY + this.height*this.rand.nextFloat() + 0.2f, this.posZ - this.width/2 + this.rand.nextFloat()*width, 0, 0, 0, 48 + this.rand.nextInt(12), 0.8f, 0.8f, 1.0f); + Wizardry.proxy.spawnParticle(EnumParticleType.SPARKLE, worldObj, this.posX - this.width/2 + this.rand.nextFloat()*width, this.posY + this.height*this.rand.nextFloat() + 0.2f, this.posZ - this.width/2 + this.rand.nextFloat()*width, 0, 0, 0, 48 + this.rand.nextInt(12), 0.8f, 0.8f, 1.0f); } } - return super.onInitialSpawn(difficulty, data); - } - - @Override - public ITextComponent getDisplayName(){ - if(getOwner() != null){ - return new TextComponentTranslation(ISummonedCreature.NAMEPLATE_TRANSLATION_KEY, getOwner().getName(), - new TextComponentTranslation("entity." + this.getEntityString() + ".name")); - }else{ - return super.getDisplayName(); - } - } - - @Override - public boolean hasCustomName(){ - // If this returns true, the renderer will show the nameplate when looking directly at the entity - return Wizardry.settings.showSummonedCreatureNames && getOwner() != null; + return super.onSpawnWithEgg(par1EntityLivingData); } } diff --git a/src/main/java/electroblob/wizardry/entity/living/EntitySpiritWolf.java b/src/main/java/electroblob/wizardry/entity/living/EntitySpiritWolf.java index 6de76c7b..061257ce 100644 --- a/src/main/java/electroblob/wizardry/entity/living/EntitySpiritWolf.java +++ b/src/main/java/electroblob/wizardry/entity/living/EntitySpiritWolf.java @@ -1,71 +1,76 @@ package electroblob.wizardry.entity.living; -import electroblob.wizardry.WizardData; +import cpw.mods.fml.relauncher.Side; +import cpw.mods.fml.relauncher.SideOnly; +import electroblob.wizardry.EnumParticleType; +import electroblob.wizardry.ExtendedPlayer; import electroblob.wizardry.Wizardry; import electroblob.wizardry.item.ItemWand; -import electroblob.wizardry.registry.WizardrySounds; -import electroblob.wizardry.util.WizardryParticleType; +import net.minecraft.block.Block; +import net.minecraft.entity.Entity; import net.minecraft.entity.EntityAgeable; +import net.minecraft.entity.EntityLivingBase; import net.minecraft.entity.IEntityLivingData; -import net.minecraft.entity.ai.EntityAIAttackMelee; +import net.minecraft.entity.SharedMonsterAttributes; +import net.minecraft.entity.ai.EntityAIAttackOnCollide; import net.minecraft.entity.ai.EntityAIFollowOwner; import net.minecraft.entity.ai.EntityAIHurtByTarget; import net.minecraft.entity.ai.EntityAILeapAtTarget; import net.minecraft.entity.ai.EntityAILookIdle; import net.minecraft.entity.ai.EntityAIOwnerHurtByTarget; import net.minecraft.entity.ai.EntityAIOwnerHurtTarget; -import net.minecraft.entity.ai.EntityAISit; import net.minecraft.entity.ai.EntityAISwimming; import net.minecraft.entity.ai.EntityAIWander; import net.minecraft.entity.ai.EntityAIWatchClosest; +import net.minecraft.entity.monster.EntityCreeper; +import net.minecraft.entity.monster.EntityGhast; +import net.minecraft.entity.passive.EntityAnimal; +import net.minecraft.entity.passive.EntityHorse; import net.minecraft.entity.passive.EntityWolf; import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.entity.projectile.EntityArrow; import net.minecraft.item.Item; +import net.minecraft.item.ItemFood; import net.minecraft.item.ItemStack; +import net.minecraft.nbt.NBTTagCompound; +import net.minecraft.pathfinding.PathEntity; import net.minecraft.util.DamageSource; -import net.minecraft.util.EnumHand; -import net.minecraft.util.text.ITextComponent; -import net.minecraft.util.text.TextComponentTranslation; -import net.minecraft.world.DifficultyInstance; +import net.minecraft.util.MathHelper; import net.minecraft.world.World; -/** Does not implement ISummonedCreature because it has different despawning rules and because EntityWolf already - * has an owner system. */ public class EntitySpiritWolf extends EntityWolf { public EntitySpiritWolf(World par1World){ super(par1World); - this.experienceValue = 0; - } - - @Override - protected void initEntityAI(){ - - this.aiSit = new EntityAISit(this); + // Removes all the AI in EntityWolf so I can add what I want instead. + this.tasks.taskEntries.clear(); + this.targetTasks.taskEntries.clear(); + this.getNavigator().setAvoidsWater(true); this.tasks.addTask(1, new EntityAISwimming(this)); this.tasks.addTask(2, this.aiSit); this.tasks.addTask(3, new EntityAILeapAtTarget(this, 0.4F)); - this.tasks.addTask(4, new EntityAIAttackMelee(this, 1.0D, true)); + this.tasks.addTask(4, new EntityAIAttackOnCollide(this, 1.0D, true)); this.tasks.addTask(5, new EntityAIFollowOwner(this, 1.0D, 10.0F, 2.0F)); this.tasks.addTask(7, new EntityAIWander(this, 1.0D)); this.tasks.addTask(9, new EntityAIWatchClosest(this, EntityPlayer.class, 8.0F)); this.tasks.addTask(9, new EntityAILookIdle(this)); this.targetTasks.addTask(1, new EntityAIOwnerHurtByTarget(this)); this.targetTasks.addTask(2, new EntityAIOwnerHurtTarget(this)); - this.targetTasks.addTask(3, new EntityAIHurtByTarget(this, true, new Class[0])); + this.targetTasks.addTask(3, new EntityAIHurtByTarget(this, true)); + this.experienceValue = 0; } @Override - public void onDeath(DamageSource source){ + public void onDeath(DamageSource par1DamageSource){ // Allows player to summon another spirit wolf once this one has died. - // NOTE: This has been known to work incorrectly. - if(this.getOwner() instanceof EntityPlayer && WizardData.get((EntityPlayer)this.getOwner()) != null){ - WizardData.get((EntityPlayer)this.getOwner()).hasSpiritWolf = false; + // TODO: Or at least, it should... + if(this.getOwner() instanceof EntityPlayer && ExtendedPlayer.get((EntityPlayer)this.getOwner()) != null){ + ExtendedPlayer.get((EntityPlayer)this.getOwner()).hasSpiritWolf = false; } - super.onDeath(source); + super.onDeath(par1DamageSource); } @Override @@ -74,16 +79,15 @@ public class EntitySpiritWolf extends EntityWolf { } @Override - public IEntityLivingData onInitialSpawn(DifficultyInstance difficulty, IEntityLivingData livingdata) { - + public IEntityLivingData onSpawnWithEgg(IEntityLivingData par1EntityLivingData){ + // Adds Particles on spawn. Due to client/server differences this cannot be done in the item. if(this.worldObj.isRemote){ for(int i=0;i<15;i++){ - Wizardry.proxy.spawnParticle(WizardryParticleType.SPARK, worldObj, this.posX - this.width/2 + this.rand.nextFloat()*width, this.posY + this.height*this.rand.nextFloat() + 0.2f, this.posZ - this.width/2 + this.rand.nextFloat()*width, 0, 0, 0, 48 + this.rand.nextInt(12), 0.8f, 0.8f, 1.0f); + Wizardry.proxy.spawnParticle(EnumParticleType.SPARK, worldObj, this.posX - this.width/2 + this.rand.nextFloat()*width, this.posY + this.height*this.rand.nextFloat() + 0.2f, this.posZ - this.width/2 + this.rand.nextFloat()*width, 0, 0, 0, 48 + this.rand.nextInt(12), 0.8f, 0.8f, 1.0f); } } - - return livingdata; + return par1EntityLivingData; } @Override @@ -93,29 +97,31 @@ public class EntitySpiritWolf extends EntityWolf { // Adds a dust particle effect if(this.worldObj.isRemote){ - Wizardry.proxy.spawnParticle(WizardryParticleType.DUST, worldObj, this.posX - this.width/2 + this.rand.nextFloat()*width, this.posY + this.height*this.rand.nextFloat() + 0.2f, this.posZ - this.width/2 + this.rand.nextFloat()*width, 0, 0, 0, 0, 0.8f, 0.8f, 1.0f); + Wizardry.proxy.spawnParticle(EnumParticleType.DUST, worldObj, this.posX - this.width/2 + this.rand.nextFloat()*width, this.posY + this.height*this.rand.nextFloat() + 0.2f, this.posZ - this.width/2 + this.rand.nextFloat()*width, 0, 0, 0, 0, 0.8f, 0.8f, 1.0f); } } - + @Override - public boolean processInteract(EntityPlayer player, EnumHand hand, ItemStack stack) { + public boolean interact(EntityPlayer player) + { + ItemStack itemstack = player.inventory.getCurrentItem(); if (this.isTamed()) { - if (stack != null){ + if (itemstack != null){ // Allows the owner (but not other players) to dispel the spirit wolf using a wand. - if(stack != null && stack.getItem() instanceof ItemWand && this.getOwner() == player && player.isSneaking()){ + if(itemstack != null && itemstack.getItem() instanceof ItemWand && this.getOwner() == player && player.isSneaking()){ // Prevents accidental double clicking. if(this.ticksExisted > 20){ for(int i=0;i<10;i++){ - Wizardry.proxy.spawnParticle(WizardryParticleType.SPARKLE, worldObj, this.posX - this.width/2 + this.rand.nextFloat()*width, this.posY + this.height*this.rand.nextFloat() + 0.2f, this.posZ - this.width/2 + this.rand.nextFloat()*width, 0, 0, 0, 48 + this.rand.nextInt(12), 0.8f, 0.8f, 1.0f); + Wizardry.proxy.spawnParticle(EnumParticleType.SPARKLE, worldObj, this.posX - this.width/2 + this.rand.nextFloat()*width, this.posY + this.height*this.rand.nextFloat() + 0.2f, this.posZ - this.width/2 + this.rand.nextFloat()*width, 0, 0, 0, 48 + this.rand.nextInt(12), 0.8f, 0.8f, 1.0f); } this.setDead(); - if(WizardData.get(player) != null){ - WizardData.get(player).hasSpiritWolf = false; + if(ExtendedPlayer.get(player) != null){ + ExtendedPlayer.get(player).hasSpiritWolf = false; } - this.playSound(WizardrySounds.SPELL_HEAL, 0.7F, rand.nextFloat() * 0.4F + 1.0F); + this.playSound("wizardry:heal", 0.7F, rand.nextFloat() * 0.4F + 1.0F); // This is necessary to prevent the wand's spell being cast when performing this action. return true; } @@ -123,7 +129,7 @@ public class EntitySpiritWolf extends EntityWolf { } } - return super.processInteract(player, hand, stack); + return super.interact(player); } @Override @@ -132,26 +138,16 @@ public class EntitySpiritWolf extends EntityWolf { return null; } + @Override + protected void func_145780_a(int p_145780_1_, int p_145780_2_, int p_145780_3_, Block p_145780_4_) + { + this.playSound("mob.wolf.step", 0.15F, 1.0F); + } + @Override protected Item getDropItem() { return null; } - - @Override - public ITextComponent getDisplayName(){ - if(getOwner() != null){ - return new TextComponentTranslation(ISummonedCreature.NAMEPLATE_TRANSLATION_KEY, getOwner().getName(), - new TextComponentTranslation("entity." + this.getEntityString() + ".name")); - }else{ - return super.getDisplayName(); - } - } - - @Override - public boolean hasCustomName(){ - // If this returns true, the renderer will show the nameplate when looking directly at the entity - return Wizardry.settings.showSummonedCreatureNames && getOwner() != null; - } } diff --git a/src/main/java/electroblob/wizardry/entity/living/EntityStormElemental.java b/src/main/java/electroblob/wizardry/entity/living/EntityStormElemental.java index 204d95a0..e4aaacbe 100644 --- a/src/main/java/electroblob/wizardry/entity/living/EntityStormElemental.java +++ b/src/main/java/electroblob/wizardry/entity/living/EntityStormElemental.java @@ -1,58 +1,57 @@ package electroblob.wizardry.entity.living; -import java.util.Collections; import java.util.List; +import cpw.mods.fml.relauncher.Side; +import cpw.mods.fml.relauncher.SideOnly; +import electroblob.wizardry.EnumParticleType; import electroblob.wizardry.Wizardry; -import electroblob.wizardry.registry.Spells; -import electroblob.wizardry.registry.WizardrySounds; -import electroblob.wizardry.spell.Spell; -import electroblob.wizardry.util.SpellModifiers; -import electroblob.wizardry.util.WizardryParticleType; +import electroblob.wizardry.WizardryUtilities; +import electroblob.wizardry.entity.projectile.EntityLightningDisc; +import net.minecraft.entity.Entity; +import net.minecraft.entity.EntityLiving; import net.minecraft.entity.EntityLivingBase; import net.minecraft.entity.SharedMonsterAttributes; -import net.minecraft.entity.ai.EntityAIAttackMelee; import net.minecraft.entity.ai.EntityAIHurtByTarget; import net.minecraft.entity.ai.EntityAILookIdle; import net.minecraft.entity.ai.EntityAINearestAttackableTarget; import net.minecraft.entity.ai.EntityAIWander; import net.minecraft.entity.effect.EntityLightningBolt; -import net.minecraft.init.SoundEvents; -import net.minecraft.util.EnumParticleTypes; -import net.minecraft.util.SoundEvent; +import net.minecraft.entity.monster.EntityMob; +import net.minecraft.entity.monster.IMob; +import net.minecraft.util.DamageSource; import net.minecraft.world.World; -import net.minecraftforge.fml.relauncher.Side; -import net.minecraftforge.fml.relauncher.SideOnly; -public class EntityStormElemental extends EntitySummonedCreature implements ISpellCaster { - - private double AISpeed = 1.0; - - private EntityAIAttackSpell spellAttackAI = new EntityAIAttackSpell(this, AISpeed, 15f, 30, 0); - - private static final List attack = Collections.singletonList(Spells.lightning_disc); +public class EntityStormElemental extends EntitySummonedCreature +{ + /** Random offset used in floating behaviour */ + private float heightOffset = 0.5F; + + /** ticks until heightOffset is randomized */ + private int heightOffsetUpdateTime; + private int field_70846_g; public EntityStormElemental(World world){ - super(world); - } - - public EntityStormElemental(World world, double x, double y, double z, EntityLivingBase caster, int lifetime){ - super(world, x, y, z, caster, lifetime); - // For some reason this can't be in initEntityAI - this.tasks.addTask(0, this.spellAttackAI); - } + super(world); + } - @Override - protected void initEntityAI(){ - - this.tasks.addTask(1, new EntityAIAttackMelee(this, AISpeed, false)); - this.tasks.addTask(2, new EntityAIWander(this, AISpeed)); - this.tasks.addTask(3, new EntityAILookIdle(this)); + public EntityStormElemental(World world, double x, double y, double z, EntityLivingBase caster, int lifetime){ + + super(world, x, y, z, caster, lifetime); + this.isImmuneToFire = true; + this.tasks.addTask(1, new EntityAIWander(this, 1.0D)); + this.tasks.addTask(2, new EntityAILookIdle(this)); this.targetTasks.addTask(1, new EntityAIHurtByTarget(this, true)); - this.targetTasks.addTask(2, new EntityAINearestAttackableTarget(this, EntityLivingBase.class, - 0, false, true, this.getTargetSelector())); + this.targetTasks.addTask(2, new EntityAINearestAttackableTarget(this, EntityLiving.class, 0, false, true, IMob.mobSelector)); + this.experienceValue = 0; + } - this.setAIMoveSpeed((float)AISpeed); + /** + * Returns true if the newer Entity AI code should be run + */ + protected boolean isAIEnabled() + { + return false; } @Override @@ -60,101 +59,219 @@ public class EntityStormElemental extends EntitySummonedCreature implements ISpe return true; } - @Override - public List getSpells(){ - 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 - protected void applyEntityAttributes(){ + protected void applyEntityAttributes() + { super.applyEntityAttributes(); - this.getAttributeMap().registerAttribute(SharedMonsterAttributes.ATTACK_DAMAGE).setBaseValue(6.0D); - this.getEntityAttribute(SharedMonsterAttributes.MOVEMENT_SPEED).setBaseValue(AISpeed); - this.getEntityAttribute(SharedMonsterAttributes.MAX_HEALTH).setBaseValue(30.0D); - this.getEntityAttribute(SharedMonsterAttributes.FOLLOW_RANGE).setBaseValue(16.0D); + this.getEntityAttribute(SharedMonsterAttributes.attackDamage).setBaseValue(6.0D); + this.getEntityAttribute(SharedMonsterAttributes.movementSpeed).setBaseValue(3.0d); + this.getEntityAttribute(SharedMonsterAttributes.maxHealth).setBaseValue(30.0d); + this.getEntityAttribute(SharedMonsterAttributes.followRange).setBaseValue(16.0D); } - @Override - protected SoundEvent getAmbientSound(){ - return SoundEvents.ENTITY_BLAZE_AMBIENT; - } + protected void entityInit() + { + super.entityInit(); + this.dataWatcher.addObject(16, new Byte((byte)0)); + } - @Override - protected SoundEvent getHurtSound(){ - return SoundEvents.ENTITY_BLAZE_HURT; - } + /** + * Returns the sound this mob makes while it's alive. + */ + protected String getLivingSound() + { + return "mob.blaze.breathe"; + } + + /** + * Returns the sound this mob makes when it is hurt. + */ + protected String getHurtSound() + { + return "mob.blaze.hit"; + } + + /** + * Returns the sound this mob makes on death. + */ + protected String getDeathSound() + { + return "mob.blaze.death"; + } - @Override - protected SoundEvent getDeathSound(){ - return SoundEvents.ENTITY_BLAZE_DEATH; - } - - @Override @SideOnly(Side.CLIENT) - public int getBrightnessForRender(float partialTicks){ + public int getBrightnessForRender(float par1) + { return 15728880; } - @Override - public float getBrightness(float partialTicks){ + /** + * Gets how bright this entity is. + */ + public float getBrightness(float par1) + { return 1.0F; } - + @Override - public void onLivingUpdate(){ + public void onSpawn(){ + for(int i=0;i<15;i++){ + float brightness = rand.nextFloat()*0.4f; + if(this.worldObj.isRemote){ + Wizardry.proxy.spawnParticle(EnumParticleType.SPARKLE, worldObj, this.posX - 0.5d + rand.nextDouble(), this.posY + this.height/2 - 0.5d + rand.nextDouble(), this.posZ - 0.5d + rand.nextDouble(), 0, 0.05f, 0, 20 + rand.nextInt(10), brightness, 0.0f, brightness); + } + } + } + + /** + * Called frequently so the entity can update its state every tick as required. For example, zombies and skeletons + * use this to react to sunlight and start to burn. + */ + public void onLivingUpdate() + { + if (!this.worldObj.isRemote) + { + /* + if (this.isWet()) + { + this.attackEntityFrom(DamageSource.drown, 1.0F); + } + */ + + --this.heightOffsetUpdateTime; + + if (this.heightOffsetUpdateTime <= 0) + { + this.heightOffsetUpdateTime = 100; + this.heightOffset = 0.5F + (float)this.rand.nextGaussian() * 3.0F; + } + + if (this.getEntityToAttack() != null && this.getEntityToAttack().posY + (double)this.getEntityToAttack().getEyeHeight() > this.posY + (double)this.getEyeHeight() + (double)this.heightOffset) + { + this.motionY += (0.30000001192092896D - this.motionY) * 0.30000001192092896D; + } + } if(this.ticksExisted % 120 == 1){ - this.playSound(WizardrySounds.SPELL_LOOP_WIND, 1.0f, 1.0f); + this.playSound("wizardry:wind", 1.0f, 1.0f); } - if (this.rand.nextInt(24) == 0){ - this.playSound(SoundEvents.ENTITY_BLAZE_BURN, 1.0F + this.rand.nextFloat(), this.rand.nextFloat() * 0.7F + 0.3F); + if (this.rand.nextInt(24) == 0) + { + this.worldObj.playSoundEffect(this.posX + 0.5D, this.posY + 0.5D, this.posZ + 0.5D, "fire.fire", 1.0F + this.rand.nextFloat(), this.rand.nextFloat() * 0.7F + 0.3F); } - // Slow fall - if(!this.onGround && this.motionY < 0.0D){ + if (!this.onGround && this.motionY < 0.0D) + { this.motionY *= 0.6D; } if(worldObj.isRemote){ - for(int i=0; i<2; ++i){ - worldObj.spawnParticle(EnumParticleTypes.SMOKE_LARGE, this.posX + (this.rand.nextDouble() - 0.5D) * (double)this.width, this.posY + this.rand.nextDouble() * (double)this.height, this.posZ + (this.rand.nextDouble() - 0.5D) * (double)this.width, 0, 0, 0); - Wizardry.proxy.spawnParticle(WizardryParticleType.SPARK, worldObj, this.posX + (this.rand.nextDouble() - 0.5D) * (double)this.width, this.posY + this.rand.nextDouble() * (double)this.height, this.posZ + (this.rand.nextDouble() - 0.5D) * (double)this.width, 0.0d, 0.0d, 0.0d, 0, 0, 0, 0); + worldObj.spawnParticle("largesmoke", this.posX + (this.rand.nextDouble() - 0.5D) * (double)this.width, this.posY + this.rand.nextDouble() * (double)this.height, this.posZ + (this.rand.nextDouble() - 0.5D) * (double)this.width, 0, 0, 0); + Wizardry.proxy.spawnParticle(EnumParticleType.SPARK, worldObj, this.posX + (this.rand.nextDouble() - 0.5D) * (double)this.width, this.posY + this.rand.nextDouble() * (double)this.height, this.posZ + (this.rand.nextDouble() - 0.5D) * (double)this.width, 0.0d, 0.0d, 0.0d, 0, 0, 0, 0); } for(int i=0; i<10; i++){ float brightness = rand.nextFloat()*0.2f; double dy = this.rand.nextDouble() * (double)this.height; - Wizardry.proxy.spawnParticle(WizardryParticleType.SPARKLE_ROTATING, worldObj, this.posX, this.posY + dy, this.posZ, 0, 0, 0, 20 + rand.nextInt(10), 0, brightness, brightness, false, 0.2f + 0.5f*dy); + Wizardry.proxy.spawnParticle(EnumParticleType.BLIZZARD, worldObj, this.posX, this.posY + dy, this.posZ, 0, 0, 0, 20 + rand.nextInt(10), 0, brightness, brightness, false, 0.2f + 0.5f*dy); } } super.onLivingUpdate(); } - - @Override - public void fall(float distance, float damageMultiplier){ - // Immune to fall damage. + + /** + * Basic mob attack. Default to touch of death in EntityCreature. Overridden by each mob to define their attack. + */ + protected void attackEntity(Entity target, float distance) + { + // This is normally the melee attack, but here it is changed to be lightning. + if (this.attackTime <= 0 && distance < 3.0F && target.boundingBox.maxY > this.boundingBox.minY && target.boundingBox.minY < this.boundingBox.maxY) + { + this.attackTime = 50; + + EntityLightningBolt lightning = new EntityLightningBolt(worldObj, this.posX, this.posY, this.posZ); + worldObj.addWeatherEffect(lightning); + } + else if (distance < 30.0F) + { + double dx = target.posX - this.posX; + double dy = target.boundingBox.minY + (double)(target.height / 2.0F) - (this.posY + (double)(this.height / 2.0F)); + double dz = target.posZ - this.posZ; + + if(this.attackTime == 0){ + + this.playSound("wizardry:electricitya", 1.0F, worldObj.rand.nextFloat() * 0.3F + 0.8F); + EntityLightningDisc lightningdisc = new EntityLightningDisc(this.worldObj, this.getCaster() == null ? this : this.getCaster()); + lightningdisc.setPosition(this.posX + this.getLookVec().xCoord, this.posY, this.posZ + this.getLookVec().zCoord); + + lightningdisc.directTowards(target, 0.5f); + + lightningdisc.posY = this.posY + (double)(this.height / 2.0F) + 0.5D; + this.worldObj.spawnEntityInWorld(lightningdisc); + + this.attackTime = 50; + } + + this.rotationYaw = (float)(Math.atan2(dz, dx) * 180.0D / Math.PI) - 90.0F; + this.hasAttacked = true; + } } - + @Override - public void onStruckByLightning(EntityLightningBolt lightning){ - // Immune to lightning. + public void onStruckByLightning(EntityLightningBolt p_70077_1_){ + // Left blank so the storm elemental is immune to lightning. } -} \ No newline at end of file + + /** + * Called when the mob is falling. Calculates and applies fall damage. + */ + protected void fall(float par1) {} + + /** + * Returns the item ID for the item the mob drops on death. + */ + protected int getDropItemId() + { + return -1; + } + + /** + * Drop 0-2 items of this living's type. @param par1 - Whether this entity has recently been hit by a player. @param + * par2 - Level of Looting used to kill this mob. + */ + protected void dropFewItems(boolean par1, int par2) + { + + } + + public boolean func_70845_n() + { + return (this.dataWatcher.getWatchableObjectByte(16) & 1) != 0; + } + + public void func_70844_e(boolean par1) + { + byte b0 = this.dataWatcher.getWatchableObjectByte(16); + + if (par1) + { + b0 = (byte)(b0 | 1); + } + else + { + b0 &= -2; + } + + this.dataWatcher.updateObject(16, Byte.valueOf(b0)); + } + + /** + * Checks to make sure the light is not too bright where the mob is spawning + */ + protected boolean isValidLightLevel() + { + return true; + } +} diff --git a/src/main/java/electroblob/wizardry/entity/living/EntitySummonedCreature.java b/src/main/java/electroblob/wizardry/entity/living/EntitySummonedCreature.java index 9ed6d48f..e2f8d625 100644 --- a/src/main/java/electroblob/wizardry/entity/living/EntitySummonedCreature.java +++ b/src/main/java/electroblob/wizardry/entity/living/EntitySummonedCreature.java @@ -1,42 +1,65 @@ package electroblob.wizardry.entity.living; import java.lang.ref.WeakReference; +import java.util.Arrays; +import java.util.List; +import java.util.Locale; import java.util.UUID; +import cpw.mods.fml.common.registry.IEntityAdditionalSpawnData; +import electroblob.wizardry.ExtendedPlayer; +import electroblob.wizardry.MagicDamage; +import electroblob.wizardry.MagicDamage.DamageType; import electroblob.wizardry.Wizardry; +import electroblob.wizardry.WizardryUtilities; +import electroblob.wizardry.item.ItemWand; +import io.netty.buffer.ByteBuf; +import net.minecraft.command.IEntitySelector; +import net.minecraft.enchantment.EnchantmentHelper; +import net.minecraft.entity.Entity; import net.minecraft.entity.EntityCreature; import net.minecraft.entity.EntityFlying; +import net.minecraft.entity.EntityList; import net.minecraft.entity.EntityLivingBase; +import net.minecraft.entity.SharedMonsterAttributes; +import net.minecraft.entity.monster.IMob; import net.minecraft.entity.player.EntityPlayer; -import net.minecraft.item.Item; -import net.minecraft.item.ItemStack; import net.minecraft.nbt.NBTTagCompound; -import net.minecraft.util.EnumHand; -import net.minecraft.util.ResourceLocation; -import net.minecraft.util.text.ITextComponent; -import net.minecraft.util.text.TextComponentTranslation; +import net.minecraft.util.DamageSource; +import net.minecraft.util.MathHelper; +import net.minecraft.world.EnumDifficulty; +import net.minecraft.world.EnumSkyBlock; import net.minecraft.world.World; -/** Abstract base implementation of {@link ISummonedCreature} which is the superclass to all custom summoned entities - * (i.e. entities that don't extend vanilla/mod creatures). Also serves as an example of how to correctly implement - * the above interface, and includes some non-critical method overrides which should be used for best results (xp, drops, - * and such like). Not to be confused with the old version of EntitySummonedCreature; that system has been replaced. - * @since Wizardry 1.2 - * @author Electroblob */ -public abstract class EntitySummonedCreature extends EntityCreature implements ISummonedCreature { +/** + * This class (formerly EntityMobMod) was originally copied from EntityMob to allow AI to distinguish between + * summoned creatures and mobs, whilst still letting summoned creatures have access to the EntityMob code. It + * was later renamed and generalised to better reflect its purpose, and now all 'sentient' magical entities + * extend this class. The lifetime variable was also factored in so it can be synced automatically, rather than + * bothering with this for every single subclass. In fact, this class now bears little resemblance to EntityMob at all. + *

    + * As of Wizardry 1.1, this class has a despawn method and an onSpawn method which can be overridden to add particles + * and such like. It also now stores the creature's owner. + * @since Wizardry 1.0 + */ +public abstract class EntitySummonedCreature extends EntityCreature implements IEntityAdditionalSpawnData { - // Field implementations - private int lifetime = 600; - private WeakReference casterReference; + /** The lifetime of the summoned creature in ticks. Allows primarily for duration multiplier support, but also + * for example the skeleton legion spell which lasts for 60 seconds instead of the usual 30. Syncing and saving + * is done automatically. As of Wizardry 1.3, despawning is handled in EntitySummonedCreature; see + * {@link EntitySummonedCreature#despawn()} for details. */ + public int lifetime = 600; + + /** The entity that summoned this creature */ + private WeakReference caster; + + /** The UUID of the caster. Note that this is only for loading purposes; during normal updates + * the actual entity instance is stored (so that getEntityByUUID is not called constantly), + * so this will not always be synced (this is why it is private). */ private UUID casterUUID; - // Setter + getter implementations - @Override public int getLifetime(){ return lifetime; } - @Override public void setLifetime(int lifetime){ this.lifetime = lifetime; } - @Override public WeakReference getCasterReference(){ return casterReference; } - @Override public void setCasterReference(WeakReference reference){ casterReference = reference; } - @Override public UUID getCasterUUID() { return casterUUID; } - @Override public void setCasterUUID(UUID uuid) { this.casterUUID = uuid; } + /** The entity selector passed into the new AI methods, if used. */ + protected IEntitySelector targetSelector; /** * Default shell constructor, only used by client. Lifetime defaults arbitrarily to 600, but this doesn't @@ -54,91 +77,360 @@ public abstract class EntitySummonedCreature extends EntityCreature implements I * extending this class (be sure to call super()) so that AI and other things can be added. */ public EntitySummonedCreature(World world, double x, double y, double z, EntityLivingBase caster, int lifetime){ + super(world); this.setPosition(x, y, z); - this.casterReference = new WeakReference(caster); + this.caster = new WeakReference(caster); this.experienceValue = 0; this.lifetime = lifetime; + + this.targetSelector = new IEntitySelector(){ + + public boolean isEntityApplicable(Entity entity){ + + // If the target is valid... + if(isValidTarget(entity)){ + + //... and is a player, they can be attacked, since players can't be in the whitelist or the blacklist. + if(entity instanceof EntityPlayer) return true; + + //... and is a mob, a summoned creature, a wizard ... + if((entity instanceof IMob || entity instanceof EntitySummonedCreature || (entity instanceof EntityWizard && !(getCaster() instanceof EntityWizard)) + // ... or in the whitelist ... + || Arrays.asList(Wizardry.summonedCreatureTargetsWhitelist).contains(EntityList.getEntityString(entity).toLowerCase(Locale.ROOT))) + // ... and isn't in the blacklist ... + && !Arrays.asList(Wizardry.summonedCreatureTargetsBlacklist).contains(EntityList.getEntityString(entity).toLowerCase(Locale.ROOT))){ + // ... it can be attacked. + return true; + } + } + + return false; + } + }; } - // Implementations + @Override + protected int getExperiencePoints(EntityPlayer player){ + return 0; + } @Override - public void setRevengeTarget(EntityLivingBase entity){ - if(this.shouldRevengeTarget(entity)) super.setRevengeTarget(entity); - } - - @Override - public void onUpdate(){ - super.onUpdate(); - this.updateDelegate(); - } - - @Override - public void onSpawn(){} - - @Override - public void onDespawn(){} - - @Override - public boolean hasParticleEffect() { - return false; - } - - @Override - protected boolean processInteract(EntityPlayer player, EnumHand hand, ItemStack stack) { - // In this case, the delegate method determines whether super is called. - // Rather handily, we can make use of Java's short-circuiting method of evaluating OR statements. - return this.interactDelegate(player, hand, stack) || super.processInteract(player, hand, stack); - } - - @Override - public void writeEntityToNBT(NBTTagCompound nbttagcompound){ - super.writeEntityToNBT(nbttagcompound); - this.writeNBTDelegate(nbttagcompound); - } - - @Override - public void readEntityFromNBT(NBTTagCompound nbttagcompound){ - super.readEntityFromNBT(nbttagcompound); - this.writeNBTDelegate(nbttagcompound); - } - - // Recommended overrides - - @Override protected int getExperiencePoints(EntityPlayer player){ return 0; } - @Override protected boolean canDropLoot(){ return false; } - @Override protected Item getDropItem(){ return null; } - @Override protected ResourceLocation getLootTable(){ return null; } - @Override public boolean canPickUpLoot(){ return false; } - // This vanilla method has nothing to do with the custom onDespawn() method. - @Override protected boolean canDespawn(){ return false; } - - @Override - public boolean canAttackClass(Class entityType){ + public boolean canAttackClass(Class entityType){ // Returns true unless the given entity type is a flying entity and this entity only has melee attacks. + // Overridden mainly to allow the zombie to attack creepers if the config settings allow it. return !EntityFlying.class.isAssignableFrom(entityType) || this.hasRangedAttack(); } - - // TODO: Backport the following two methods to 1.7.10. + @Override - public ITextComponent getDisplayName(){ - if(getCaster() != null){ - return new TextComponentTranslation(NAMEPLATE_TRANSLATION_KEY, getCaster().getName(), - new TextComponentTranslation("entity." + this.getEntityString() + ".name")); - }else{ - return super.getDisplayName(); - } + public void setRevengeTarget(EntityLivingBase entity){ + // Allows the config to prevent minions from revenge-targeting their owners. + if(entity != this.getCaster() || Wizardry.minionRevengeTargeting) super.setRevengeTarget(entity); } - @Override - public boolean hasCustomName(){ - // If this returns true, the renderer will show the nameplate when looking directly at the entity - return Wizardry.settings.showSummonedCreatureNames && getCaster() != null; - } - - // Specific to EntitySummonedCreature, remove if copying - /** Whether this summoned creature has a ranged attack. Used to test whether it should attack flying creatures. */ public abstract boolean hasRangedAttack(); -} \ No newline at end of file + + /** + * Called frequently so the entity can update its state every tick as required. For example, zombies and skeletons + * use this to react to sunlight and start to burn. - I still have no idea what the difference between this and + * the plain onUpdate() method is. From what I can gather, onUpdate() always calls this one anyway. + */ + public void onLivingUpdate(){ + + this.updateArmSwingProgress(); + float f = this.getBrightness(1.0F); + + if (f > 0.5F) + { + this.entityAge += 2; + } + + super.onLivingUpdate(); + } + + /** + * Called to update the entity's position/logic. + */ + public void onUpdate(){ + + super.onUpdate(); + + if(this.getCaster() == null && this.casterUUID != null){ + Entity entity = WizardryUtilities.getEntityByUUID(worldObj, casterUUID); + if(entity instanceof EntityLivingBase){ + this.caster = new WeakReference((EntityLivingBase)entity); + } + } + + if(this.ticksExisted == 1){ + this.onSpawn(); + } + + if(this.ticksExisted > this.lifetime && this.lifetime != -1){ + this.despawn(); + } + } + + /** + * Called when this creature has existed for 1 tick, effectively when it has just been spawned. Override + * to add particles, sounds, etc. + */ + public void onSpawn(){} + + /** + * Defaults to just setDead() in EntitySummonedCreature, but is provided to allow subclasses to override this, + * for example to spawn particles. You should always call super() when overriding this method, in case it changes. + * There is no need, therefore, to call setDead() when overriding. + */ + public void despawn(){ + this.setDead(); + } + + // This vanilla method has nothing to do with the custom despawn() method. + @Override + protected boolean canDespawn(){ + return false; + } + + /** + * Finds the closest player within 16 blocks to attack, or null if this Entity isn't interested in attacking + * (Animals, Spiders at day, peaceful PigZombies). + */ + // This was the same in all subclasses that use the old AI, so it might as well be here instead. + protected Entity findPlayerToAttack(){ + + // Thought I may as well use the new AI's attribute system to get the follow range, rather than make a new method. + List entities = WizardryUtilities.getEntitiesWithinRadius(this.getEntityAttribute(SharedMonsterAttributes.followRange).getAttributeValue(), this.posX, this.posY, this.posZ, this.worldObj); + Entity entity = null; + + for(Entity possibleTarget : entities){ + // Decides if current entity should be replaced. + if(entity == null || this.getDistanceToEntity(entity) > this.getDistanceToEntity(possibleTarget)){ + // Decides if new entity is a valid target. + if(this.isValidTarget(possibleTarget) && this.canEntityBeSeen(possibleTarget)){ + entity = possibleTarget; + } + } + } + + return entity; + } + + /** + * Called when the entity is attacked. + */ + public boolean attackEntityFrom(DamageSource source, float par2) + { + if (this.isEntityInvulnerable()) + { + return false; + } + else if (super.attackEntityFrom(source, par2)) + { + Entity entity = source.getEntity(); + + if (this.riddenByEntity != entity && this.ridingEntity != entity) + { + if (entity != this) + { + this.entityToAttack = entity; + } + + return true; + } + else + { + return true; + } + } + else + { + return false; + } + } + + public boolean attackEntityAsMob(Entity target) + { + float f = (float)this.getEntityAttribute(SharedMonsterAttributes.attackDamage).getAttributeValue(); + int i = 0; + + if (target instanceof EntityLivingBase) + { + f += EnchantmentHelper.getEnchantmentModifierLiving(this, (EntityLivingBase)target); + i += EnchantmentHelper.getKnockbackModifier(this, (EntityLivingBase)target); + } + + boolean flag = target.attackEntityFrom(MagicDamage.causeIndirectEntityMagicDamage(this, this.getCaster(), DamageType.MAGIC), f); + + if (flag) + { + if (i > 0) + { + target.addVelocity((double)(-MathHelper.sin(this.rotationYaw * (float)Math.PI / 180.0F) * (float)i * 0.5F), 0.1D, (double)(MathHelper.cos(this.rotationYaw * (float)Math.PI / 180.0F) * (float)i * 0.5F)); + this.motionX *= 0.6D; + this.motionZ *= 0.6D; + } + + int j = EnchantmentHelper.getFireAspectModifier(this); + + if (j > 0) + { + target.setFire(j * 4); + } + + if (target instanceof EntityLivingBase) + { + EnchantmentHelper.func_151384_a((EntityLivingBase)target, this); + } + + EnchantmentHelper.func_151385_b(this, target); + } + + return flag; + } + + /** + * Basic mob attack. Defaults to touch of death in EntityCreature. Overridden by each mob to define their attack. + */ + protected void attackEntity(Entity par1Entity, float par2) + { + if (this.attackTime <= 0 && par2 < 2.0F && par1Entity.boundingBox.maxY > this.boundingBox.minY && par1Entity.boundingBox.minY < this.boundingBox.maxY) + { + this.attackTime = 20; + this.attackEntityAsMob(par1Entity); + } + } + + /** + * Takes a coordinate in and returns a weight to determine how likely this creature will try to path to the block. + * Args: x, y, z + */ + public float getBlockPathWeight(int par1, int par2, int par3) + { + return 0.5F - this.worldObj.getLightBrightness(par1, par2, par3); + } + + /** + * Checks to make sure the light is not too bright where the mob is spawning + */ + protected boolean isValidLightLevel() + { + int i = MathHelper.floor_double(this.posX); + int j = MathHelper.floor_double(this.boundingBox.minY); + int k = MathHelper.floor_double(this.posZ); + + if (this.worldObj.getSavedLightValue(EnumSkyBlock.Sky, i, j, k) > this.rand.nextInt(32)) + { + return false; + } + else + { + int l = this.worldObj.getBlockLightValue(i, j, k); + + if (this.worldObj.isThundering()) + { + int i1 = this.worldObj.skylightSubtracted; + this.worldObj.skylightSubtracted = 10; + l = this.worldObj.getBlockLightValue(i, j, k); + this.worldObj.skylightSubtracted = i1; + } + + return l <= this.rand.nextInt(8); + } + } + + /** + * Checks if the entity's current position is a valid location to spawn this entity. + */ + public boolean getCanSpawnHere() + { + return this.worldObj.difficultySetting != EnumDifficulty.PEACEFUL && this.isValidLightLevel() && super.getCanSpawnHere(); + } + + @Override + protected boolean interact(EntityPlayer player) { + + ExtendedPlayer properties = ExtendedPlayer.get(player); + // Selects one of the player's minions. + if(player.isSneaking() && player.getHeldItem() != null && player.getHeldItem().getItem() instanceof ItemWand){ + + if(!player.worldObj.isRemote && properties != null && this.getCaster() == player){ + + if(properties.selectedMinion != null && properties.selectedMinion.get() == this){ + // Deselects the selected minion if right-clicked again + properties.selectedMinion = null; + }else{ + // Selects this minion + properties.selectedMinion = new WeakReference(this); + } + properties.sync(); + } + return true; + } + + return super.interact(player); + } + + protected void applyEntityAttributes() + { + super.applyEntityAttributes(); + this.getAttributeMap().registerAttribute(SharedMonsterAttributes.attackDamage); + } + + /** + * (abstract) Protected helper method to write subclass entity data to NBT. + */ + public void writeEntityToNBT(NBTTagCompound nbttagcompound){ + super.writeEntityToNBT(nbttagcompound); + if(this.getCaster() != null){ + nbttagcompound.setString("casterUUID", this.getCaster().getUniqueID().toString()); + } + nbttagcompound.setInteger("lifetime", lifetime); + } + + /** + * (abstract) Protected helper method to read subclass entity data from NBT. + */ + public void readEntityFromNBT(NBTTagCompound nbttagcompound){ + super.readEntityFromNBT(nbttagcompound); + String string = nbttagcompound.getString("casterUUID"); + if(WizardryUtilities.verifyUUIDString(string)) casterUUID = UUID.fromString(string); + this.lifetime = nbttagcompound.getInteger("lifetime"); + } + + @Override + public void writeSpawnData(ByteBuf data){ + data.writeInt(lifetime); + } + + @Override + public void readSpawnData(ByteBuf data){ + lifetime = data.readInt(); + } + + /** + * Returns the EntityLivingBase that summoned this creature, or null if it no longer exists. Cases where the + * entity may no longer exist are: entity died or was deleted, mob despawned, player logged out, entity teleported + * to another dimension, or this creature simply had no caster in the first place. + */ + public EntityLivingBase getCaster(){ + return caster == null ? null : caster.get(); + } + + /** + * Sets the EntityLivingBase that summoned this creature to the given caster. This should only be used by subclasses + * wishing to synchronise the caster for use on the client side; this is why it is protected. + */ + protected void setCaster(EntityLivingBase caster){ + this.caster = new WeakReference(caster); + } + + /** + * Shorthand for {@link WizardryUtilities#isValidTarget(Entity, Entity)}, with the owner of this creature as the + * attacker. Also allows subclasses to override it if they wish to do so. + */ + public boolean isValidTarget(Entity target){ + return WizardryUtilities.isValidTarget(this.getCaster(), target); + } +} diff --git a/src/main/java/electroblob/wizardry/entity/living/EntityWizard.java b/src/main/java/electroblob/wizardry/entity/living/EntityWizard.java index 7123ed28..4a3ca1d8 100644 --- a/src/main/java/electroblob/wizardry/entity/living/EntityWizard.java +++ b/src/main/java/electroblob/wizardry/entity/living/EntityWizard.java @@ -1,37 +1,38 @@ package electroblob.wizardry.entity.living; +import java.lang.ref.WeakReference; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; -import java.util.HashSet; +import java.util.Iterator; import java.util.List; import java.util.Locale; -import java.util.Random; -import java.util.Set; - -import com.google.common.base.Predicate; +import cpw.mods.fml.common.network.simpleimpl.IMessage; +import cpw.mods.fml.common.registry.IEntityAdditionalSpawnData; +import cpw.mods.fml.relauncher.Side; +import cpw.mods.fml.relauncher.SideOnly; +import electroblob.wizardry.EnumElement; +import electroblob.wizardry.EnumParticleType; +import electroblob.wizardry.EnumTier; import electroblob.wizardry.Wizardry; -import electroblob.wizardry.constants.Element; -import electroblob.wizardry.constants.Tier; +import electroblob.wizardry.WizardryRegistry; +import electroblob.wizardry.WizardryUtilities; import electroblob.wizardry.item.ItemSpellBook; -import electroblob.wizardry.registry.Spells; -import electroblob.wizardry.registry.WizardryAchievements; -import electroblob.wizardry.registry.WizardryItems; -import electroblob.wizardry.registry.WizardryPotions; -import electroblob.wizardry.registry.WizardrySounds; +import electroblob.wizardry.packet.PacketCastSpell; +import electroblob.wizardry.packet.WizardryPacketHandler; import electroblob.wizardry.spell.Spell; -import electroblob.wizardry.util.SpellModifiers; -import electroblob.wizardry.util.WandHelper; -import electroblob.wizardry.util.WizardryParticleType; -import electroblob.wizardry.util.WizardryUtilities; import io.netty.buffer.ByteBuf; +import net.minecraft.command.IEntitySelector; import net.minecraft.entity.Entity; import net.minecraft.entity.EntityAgeable; import net.minecraft.entity.EntityList; import net.minecraft.entity.EntityLiving; +import net.minecraft.entity.EntityLivingBase; import net.minecraft.entity.IEntityLivingData; +import net.minecraft.entity.IRangedAttackMob; import net.minecraft.entity.SharedMonsterAttributes; +import net.minecraft.entity.ai.EntityAIArrowAttack; import net.minecraft.entity.ai.EntityAIHurtByTarget; import net.minecraft.entity.ai.EntityAILookAtTradePlayer; import net.minecraft.entity.ai.EntityAIMoveTowardsRestriction; @@ -43,46 +44,27 @@ import net.minecraft.entity.ai.EntityAITradePlayer; import net.minecraft.entity.ai.EntityAIWander; import net.minecraft.entity.ai.EntityAIWatchClosest; import net.minecraft.entity.ai.EntityAIWatchClosest2; -import net.minecraft.entity.effect.EntityLightningBolt; import net.minecraft.entity.monster.IMob; import net.minecraft.entity.passive.EntityVillager; import net.minecraft.entity.player.EntityPlayer; import net.minecraft.init.Blocks; import net.minecraft.init.Items; -import net.minecraft.init.MobEffects; -import net.minecraft.init.SoundEvents; -import net.minecraft.inventory.EntityEquipmentSlot; -import net.minecraft.item.Item; import net.minecraft.item.ItemStack; import net.minecraft.nbt.NBTTagCompound; -import net.minecraft.nbt.NBTTagInt; -import net.minecraft.nbt.NBTTagLong; -import net.minecraft.network.datasync.DataParameter; -import net.minecraft.network.datasync.DataSerializers; -import net.minecraft.network.datasync.EntityDataManager; +import net.minecraft.nbt.NBTTagIntArray; +import net.minecraft.nbt.NBTTagList; +import net.minecraft.potion.Potion; import net.minecraft.potion.PotionEffect; +import net.minecraft.util.ChatComponentTranslation; import net.minecraft.util.DamageSource; -import net.minecraft.util.EnumHand; -import net.minecraft.util.math.BlockPos; -import net.minecraft.util.text.ITextComponent; import net.minecraft.village.MerchantRecipe; import net.minecraft.village.MerchantRecipeList; -import net.minecraft.world.DifficultyInstance; import net.minecraft.world.World; -import net.minecraftforge.common.util.FakePlayer; import net.minecraftforge.common.util.Constants.NBT; -import net.minecraftforge.event.world.BlockEvent; -import net.minecraftforge.fml.common.Mod; -import net.minecraftforge.fml.common.eventhandler.SubscribeEvent; -import net.minecraftforge.fml.common.registry.IEntityAdditionalSpawnData; -import net.minecraftforge.fml.common.registry.VillagerRegistry.VillagerProfession; -import net.minecraftforge.fml.relauncher.Side; -import net.minecraftforge.fml.relauncher.SideOnly; import net.minecraftforge.oredict.OreDictionary; -@Mod.EventBusSubscriber -public class EntityWizard extends EntityVillager implements ISpellCaster, IEntityAdditionalSpawnData { - +public class EntityWizard extends EntityVillager implements IRangedAttackMob, IEntityAdditionalSpawnData { + /* * After much debugging, the error in the compiled mod (outside of eclipse) was traced back to this class, * specifically the methods copied in from EntityVillager when I changed this class to extend it. This figures, @@ -91,76 +73,71 @@ public class EntityWizard extends EntityVillager implements ISpellCaster, IEntit * ones here was renamed and the other deleted since it was never called. Watch out for this in future (unless, * of course, they are overriding something, in which case it should be fine). */ - + // Extending EntityVillager turned out to be a pretty neat thing to do, since now zombies will attack wizards - - private EntityAIAttackSpell spellCastingAI = new EntityAIAttackSpell(this, 0.5D, 14.0F, 30, 50); + + private EntityAIArrowAttack aiArrowAttack = new EntityAIArrowAttack(this, 0.5D, 20, 50, 14.0F); + + // Note: profession is used as element for wizards. public int textureIndex = 0; - - /** The entity selector passed into the new AI methods. */ - protected Predicate targetSelector; - - /** Copy of EntityVillager's buyingList, renamed to avoid confusion. */ - private MerchantRecipeList trades; - private int timeUntilReset; - - /** addDefaultEquipmentAndRecipies is called if this is true */ - private boolean updateRecipes; - - /** Data parameter for the cooldown time for wizards healing themselves. */ - private static final DataParameter HEAL_COOLDOWN = EntityDataManager.createKey(EntityWizard.class, DataSerializers.VARINT); - /** Data parameter for the wizard's element. */ - private static final DataParameter ELEMENT = EntityDataManager.createKey(EntityWizard.class, DataSerializers.VARINT); - - // Field implementations - private List spells = new ArrayList(4); - private Spell continuousSpell; - - /** A set of the positions of the blocks that are part of this wizard's tower. */ - private Set towerBlocks; - - public EntityWizard(World world){ - super(world); - this.detachHome(); - // For some reason this can't be in initEntityAI - this.tasks.addTask(3, this.spellCastingAI); - } - @Override - protected void entityInit(){ - super.entityInit(); - this.dataManager.register(HEAL_COOLDOWN, -1); - this.dataManager.register(ELEMENT, 0); - } - - @Override - protected void initEntityAI(){ + /** This villager's current customer. */ + private WeakReference buyingPlayer; + + /** The entity selector passed into the new AI methods. */ + protected IEntitySelector targetSelector; - this.tasks.addTask(0, new EntityAISwimming(this)); - this.tasks.addTask(1, new EntityAITradePlayer(this)); - this.tasks.addTask(1, new EntityAILookAtTradePlayer(this)); - this.tasks.addTask(4, new EntityAIRestrictOpenDoor(this)); - this.tasks.addTask(5, new EntityAIOpenDoor(this, true)); - this.tasks.addTask(6, new EntityAIMoveTowardsRestriction(this, 0.6D)); - this.tasks.addTask(7, new EntityAIWatchClosest2(this, EntityPlayer.class, 3.0F, 1.0F)); - this.tasks.addTask(7, new EntityAIWatchClosest2(this, EntityWizard.class, 5.0F, 0.02F)); - this.tasks.addTask(7, new EntityAIWander(this, 0.6D)); - this.tasks.addTask(8, new EntityAIWatchClosest(this, EntityLiving.class, 8.0F)); + /** Initialises the MerchantRecipeList.java */ + private MerchantRecipeList buyingList; + private int timeUntilReset; - this.targetSelector = new Predicate(){ + /** addDefaultEquipmentAndRecipies is called if this is true */ + private boolean updateRecipes; + + /** Last player to trade with this villager, used for aggressivity. */ + private String lastBuyingPlayer; + + /** Index for the heal cooldown field in the datawatcher */ + private static final int healCooldownIndex = 20; + + private int[] spells = new int[4]; + + private int[][] towerBlocks; - public boolean apply(Entity entity){ + public EntityWizard(World par1World) + { + super(par1World); + this.setSize(0.6F, 1.8F); + this.dataWatcher.addObject(healCooldownIndex, 0); + this.getNavigator().setBreakDoors(true); + this.getNavigator().setAvoidsWater(true); + // EntityVillager adds unwanted AI, so this gets rid of it. + this.tasks.taskEntries.clear(); + this.tasks.addTask(0, new EntityAISwimming(this)); + this.tasks.addTask(1, new EntityAITradePlayer(this)); + this.tasks.addTask(1, new EntityAILookAtTradePlayer(this)); + this.tasks.addTask(4, new EntityAIRestrictOpenDoor(this)); + this.tasks.addTask(5, new EntityAIOpenDoor(this, true)); + this.tasks.addTask(6, new EntityAIMoveTowardsRestriction(this, 0.6D)); + this.tasks.addTask(7, new EntityAIWatchClosest2(this, EntityPlayer.class, 3.0F, 1.0F)); + this.tasks.addTask(7, new EntityAIWatchClosest2(this, EntityWizard.class, 5.0F, 0.02F)); + this.tasks.addTask(7, new EntityAIWander(this, 0.6D)); + this.tasks.addTask(8, new EntityAIWatchClosest(this, EntityLiving.class, 8.0F)); + + this.targetSelector = new IEntitySelector(){ - // If the target is valid and not invisible... - if(entity != null && !entity.isInvisible() && WizardryUtilities.isValidTarget(EntityWizard.this, entity)){ + public boolean isEntityApplicable(Entity entity){ + + // If the target is valid... + if(entity != null && WizardryUtilities.isValidTarget(EntityWizard.this, entity)){ //... and is a mob, a summoned creature ... - if((entity instanceof IMob || entity instanceof ISummonedCreature + if((entity instanceof IMob || entity instanceof EntitySummonedCreature // ... or in the whitelist ... - || Arrays.asList(Wizardry.settings.summonedCreatureTargetsWhitelist).contains(EntityList.getEntityString(entity).toLowerCase(Locale.ROOT))) + || Arrays.asList(Wizardry.summonedCreatureTargetsWhitelist).contains(EntityList.getEntityString(entity).toLowerCase(Locale.ROOT))) // ... and isn't in the blacklist ... - && !Arrays.asList(Wizardry.settings.summonedCreatureTargetsBlacklist).contains(EntityList.getEntityString(entity).toLowerCase(Locale.ROOT))){ + && !Arrays.asList(Wizardry.summonedCreatureTargetsBlacklist).contains(EntityList.getEntityString(entity).toLowerCase(Locale.ROOT))){ // ... it can be attacked. return true; } @@ -169,579 +146,714 @@ public class EntityWizard extends EntityVillager implements ISpellCaster, IEntit return false; } }; + + this.targetTasks.addTask(1, new EntityAIHurtByTarget(this, true)); + // By default, wizards don't attack players unless the player has attacked them. + this.targetTasks.addTask(0, new EntityAINearestAttackableTarget(this, EntityLiving.class, 0, false, true, this.targetSelector)); + + this.tasks.addTask(3, this.aiArrowAttack); + + this.detachHome(); + } - this.targetTasks.addTask(1, new EntityAIHurtByTarget(this, true)); - // By default, wizards don't attack players unless the player has attacked them. - this.targetTasks.addTask(0, new EntityAINearestAttackableTarget(this, EntityLiving.class, 0, false, true, this.targetSelector)); - } + protected void applyEntityAttributes() + { + super.applyEntityAttributes(); + this.getEntityAttribute(SharedMonsterAttributes.movementSpeed).setBaseValue(0.5D); + } - @Override - protected void applyEntityAttributes(){ - super.applyEntityAttributes(); - this.getEntityAttribute(SharedMonsterAttributes.MOVEMENT_SPEED).setBaseValue(0.5); - } - - private int getHealCooldown(){ - return this.dataManager.get(HEAL_COOLDOWN); - } - - private void setHealCooldown(int cooldown){ - this.dataManager.set(HEAL_COOLDOWN, cooldown); - } - - public Element getElement(){ - return Element.values()[this.dataManager.get(ELEMENT)]; - } - - public void setElement(Element element){ - this.dataManager.set(ELEMENT, element.ordinal()); - } - - @Override - public List getSpells(){ - return this.spells; - } - - @Override - public SpellModifiers getModifiers(){ - return new SpellModifiers(); - } - - @Override - public void setContinuousSpell(Spell spell){ - this.continuousSpell = spell; - } - - @Override - public Spell getContinuousSpell(){ - return this.continuousSpell; - } - - @Override - public void onLivingUpdate(){ - - super.onLivingUpdate(); - - // Still better to store this to a local variable as it's almost certainly more efficient. - int healCooldown = this.getHealCooldown(); - - // This is now done slightly differently because isPotionActive doesn't work on client here, meaning that when - // affected with arcane jammer and healCooldown == 0, whilst the wizard didn't actually heal or play the sound, - // the particles still spawned, and since healCooldown wasn't reset they spawned every tick until the arcane - // jammer wore off. - if(healCooldown == 0 && this.getHealth() < this.getMaxHealth() && this.getHealth() > 0 && !this.isPotionActive(WizardryPotions.arcane_jammer)){ - - // Healer wizards use greater heal. - this.heal(this.getElement() == Element.HEALING ? 8 : 4); - this.setHealCooldown(-1); - - // deathTime == 0 checks the wizard isn't currently dying - }else if(healCooldown == -1 && this.deathTime == 0){ - - // Heal particles + /** + * Returns true if the newer Entity AI code should be run + */ + public boolean isAIEnabled() + { + return true; + } + + @Override + public void onLivingUpdate(){ + + super.onLivingUpdate(); + + int healCooldown = this.dataWatcher.getWatchableObjectInt(healCooldownIndex); + + // This is now done slightly differently because isPotionActive doesn't work on client here, meaning that when + // affected with arcane jammer and healCooldown == 0, whilst the wizard didn't actually heal or play the sound, + // the particles still spawned, and since healCooldown wasn't reset they spawned every tick until the arcane + // jammer wore off. + if(healCooldown == 0 && this.getHealth() < this.getMaxHealth() && this.getHealth() > 0 && !this.isPotionActive(Wizardry.arcaneJammer)){ + + // Healer wizards use greater heal. + this.heal(this.getProfession() == 7? 8 : 4); + this.dataWatcher.updateObject(healCooldownIndex, -1); + + // deathTime == 0 checks the wizard isn't currently dying + }else if(healCooldown == -1 && this.deathTime == 0){ + + // Heal particles if(worldObj.isRemote){ for(int i=0; i<10; i++){ double d0 = (double)((float)this.posX + rand.nextFloat()*2 - 1.0F); // Apparently the client side spawns the particles 1 block higher than it should... hence the - 0.5F. double d1 = (double)((float)this.posY - 0.5F + rand.nextFloat()); double d2 = (double)((float)this.posZ + rand.nextFloat()*2 - 1.0F); - Wizardry.proxy.spawnParticle(WizardryParticleType.SPARKLE, worldObj, d0, d1, d2, 0, 0.1F, 0, 48 + rand.nextInt(12), 1.0f, 1.0f, 0.3f); + Wizardry.proxy.spawnParticle(EnumParticleType.SPARKLE, worldObj, d0, d1, d2, 0, 0.1F, 0, 48 + rand.nextInt(12), 1.0f, 1.0f, 0.3f); } }else{ - if(this.getHealth() < 10){ - // Wizard heals himself more often if he has low health - this.setHealCooldown(150); - }else{ - this.setHealCooldown(400); - } - - this.playSound(WizardrySounds.SPELL_HEAL, 0.7F, rand.nextFloat() * 0.4F + 1.0F); + if(this.getHealth() < 10){ + // Wizard heals himself more often if he has low health + this.dataWatcher.updateObject(healCooldownIndex, 150); + }else{ + this.dataWatcher.updateObject(healCooldownIndex, 400); + } + + worldObj.playSoundAtEntity(this, "wizardry:heal", 0.7F, rand.nextFloat() * 0.4F + 1.0F); } - } - - if(healCooldown > 0){ - this.setHealCooldown(healCooldown-1); - } + } + if(healCooldown > 0){// && !worldObj.isRemote){ + this.dataWatcher.updateObject(healCooldownIndex, healCooldown-1); + } + } + + @Override + public void attackEntityWithRangedAttack(EntityLivingBase target, float f){ + + if(f < 30.0F && !this.isPotionActive(Wizardry.arcaneJammer)){ + + double d0 = target.posX - this.posX; + double d1 = target.boundingBox.minY + (double)(target.height / 2.0F) - (this.posY + (double)(this.height / 2.0F)); + double d2 = target.posZ - this.posZ; + + if (this.attackTime == 0 && spells.length > 0){ + + if(!this.worldObj.isRemote){ + + // New way of choosing a spell; keeps trying until one works or all have been tried + + List spellsArray = new ArrayList(spells.length); + + for(int i=0; i 0) + { + --this.timeUntilReset; - if(!this.isTrading() && this.timeUntilReset > 0){ + if (this.timeUntilReset <= 0) + { + if (this.updateRecipes) + { + if (this.buyingList.size() > 1) + { + Iterator iterator = this.buyingList.iterator(); - --this.timeUntilReset; + while (iterator.hasNext()) + { + MerchantRecipe merchantrecipe = (MerchantRecipe)iterator.next(); - if(this.timeUntilReset <= 0){ + if (merchantrecipe.isRecipeDisabled()) + { + // Increases the number of allowed uses of a disabled recipe by a random number. + merchantrecipe.func_82783_a(this.rand.nextInt(6) + this.rand.nextInt(6) + 2); + } + } + } + + if(this.buyingList.size() < 12){ + this.addRandomRecipes(1); + } + this.updateRecipes = false; + } - if(this.updateRecipes){ + this.addPotionEffect(new PotionEffect(Potion.regeneration.id, 200, 0)); + } + } + + // Super call removed because EntityVillager's version does things I don't want and the next one up is + // in EntityLivingBase and does nothing. + } - for(MerchantRecipe merchantrecipe : this.trades){ - - if(merchantrecipe.isRecipeDisabled()){ - // Increases the number of allowed uses of a disabled recipe by a random number. - merchantrecipe.increaseMaxTradeUses(this.rand.nextInt(6) + this.rand.nextInt(6) + 2); - } - } - - if(this.trades.size() < 12){ - this.addRandomRecipes(1); - } - - this.updateRecipes = false; - } - - this.addPotionEffect(new PotionEffect(MobEffects.REGENERATION, 200, 0)); - } - } - - // Super call removed because EntityVillager's version does things I don't want and the next one up is - // in EntityLivingBase and does nothing. - } - - @Override - public boolean processInteract(EntityPlayer player, EnumHand hand, ItemStack stack) { - - // Debugging - //player.addChatComponentMessage(new TextComponentTranslation("wizard.debug", Spell.get(spells[1]).getDisplayName(), Spell.get(spells[2]).getDisplayName(), Spell.get(spells[3]).getDisplayName())); - - // When right-clicked with a spell book in creative, sets one of the spells to that spell - if(player.capabilities.isCreativeMode && stack != null && stack.getItem() instanceof ItemSpellBook){ - if(this.spells.size() >= 4 && Spell.get(stack.getItemDamage()).canBeCastByNPCs()){ - this.spells.set(rand.nextInt(3)+1, Spell.get(stack.getItemDamage())); - return true; - } - } - - // Won't trade with a player that has attacked them. - if (this.isEntityAlive() && !this.isTrading() && !this.isChild() && !player.isSneaking() && this.getAttackTarget() != player) - { - if (!this.worldObj.isRemote) - { - this.setCustomer(player); - player.displayVillagerTradeGui(this); - //player.displayGUIMerchant(this, this.getElement().getWizardName()); - } - - return true; - } - else - { - return false; - } - } - - @Override - public ITextComponent getDisplayName() { - return this.getElement().getWizardName(); - } - - @Override - public void writeEntityToNBT(NBTTagCompound nbt){ - - super.writeEntityToNBT(nbt); - - if (this.trades != null){ - nbt.setTag("trades", this.trades.getRecipiesAsTags()); + /** + * Called when a player interacts with a mob. e.g. gets milk from a cow, gets into the saddle on a pig. + */ + public boolean interact(EntityPlayer player){ + + // Debugging + //player.addChatComponentMessage(new ChatComponentTranslation("wizard.debug", Spell.get(spells[1]).getDisplayName(), Spell.get(spells[2]).getDisplayName(), Spell.get(spells[3]).getDisplayName())); + + ItemStack itemstack = player.inventory.getCurrentItem(); + + // When right-clicked with a spell book in creative, sets one of the spells to that spell + if(player.capabilities.isCreativeMode && itemstack != null && itemstack.getItem() instanceof ItemSpellBook){ + if(Spell.get(itemstack.getItemDamage()).canBeCastByNPCs()){ + this.spells[rand.nextInt(3)+1] = itemstack.getItemDamage(); + return true; + } } - nbt.setInteger("element", this.getElement().ordinal()); - nbt.setInteger("skin", this.textureIndex); - nbt.setTag("spells", WizardryUtilities.listToNBT(spells, spell -> new NBTTagInt(spell.id()))); + // Won't trade with a player that has attacked them. + if (this.isEntityAlive() && !this.isTrading() && !this.isChild() && !player.isSneaking() && this.getAttackTarget() != player) + { + if (!this.worldObj.isRemote) + { + this.setCustomer(player); + player.displayGUIMerchant(this, this.getElement().getWizardName()); + } - if(this.towerBlocks != null && this.towerBlocks.size() > 0){ - nbt.setTag("towerBlocks", WizardryUtilities.listToNBT(this.towerBlocks, pos -> new NBTTagLong(pos.toLong()))); - } - } - - @Override - public void readEntityFromNBT(NBTTagCompound nbt){ - - super.readEntityFromNBT(nbt); - - if(nbt.hasKey("trades")){ - NBTTagCompound nbttagcompound1 = nbt.getCompoundTag("trades"); - this.trades = new MerchantRecipeList(nbttagcompound1); + return true; } + else + { + return false; + } + } - this.setElement(Element.values()[nbt.getInteger("element")]); - this.textureIndex = nbt.getInteger("skin"); - this.spells = (List) WizardryUtilities.NBTToList(nbt.getTagList("spells", NBT.TAG_INT), - (NBTTagInt tag) -> Spell.get(tag.getInt())); + /** + * (abstract) Protected helper method to write subclass entity data to NBT. + */ + public void writeEntityToNBT(NBTTagCompound tagcompound) + { + super.writeEntityToNBT(tagcompound); + tagcompound.setInteger("Profession", this.getProfession()); + tagcompound.setInteger("skin", this.textureIndex); - this.towerBlocks = new HashSet(WizardryUtilities.NBTToList(nbt.getTagList("towerBlocks", - NBT.TAG_LONG), (NBTTagLong tag) -> BlockPos.fromLong(tag.getLong()))); - } + if (this.buyingList != null) + { + tagcompound.setTag("Offers", this.buyingList.getRecipiesAsTags()); + } + + tagcompound.setIntArray("spells", spells); + + if(this.towerBlocks != null && this.towerBlocks.length > 0){ + + NBTTagList blocks = new NBTTagList(); + + for(int[] block : this.towerBlocks){ + blocks.appendTag(new NBTTagIntArray(block)); + } + + tagcompound.setTag("towerBlocks", blocks); + } + } - @Override - protected boolean canDespawn(){ - return false; - } + /** + * (abstract) Protected helper method to read subclass entity data from NBT. + */ + public void readEntityFromNBT(NBTTagCompound tagcompound) + { + super.readEntityFromNBT(tagcompound); + this.setProfession(tagcompound.getInteger("Profession")); + this.textureIndex = tagcompound.getInteger("skin"); - @Override - public boolean isTrading() - { - return this.getCustomer() != null; - } + if (tagcompound.hasKey("Offers")) + { + NBTTagCompound nbttagcompound1 = tagcompound.getCompoundTag("Offers"); + this.buyingList = new MerchantRecipeList(nbttagcompound1); + } + + this.spells = tagcompound.getIntArray("spells"); + + NBTTagList blocks = tagcompound.getTagList("towerBlocks", NBT.TAG_INT_ARRAY); + + if(blocks != null){ + + if(this.towerBlocks == null) this.towerBlocks = new int[blocks.tagCount()][3]; + + for(int i=0; i 0){ + this.timeUntilReset = 40; + this.updateRecipes = true; - // Changed to a 4 in 5 chance of unlocking a new recipe. - if(this.rand.nextInt(5) > 0){ - this.timeUntilReset = 40; - this.updateRecipes = true; + if (this.getBuyingPlayer() != null) + { + this.lastBuyingPlayer = this.getBuyingPlayer().getCommandSenderName(); + } + else + { + this.lastBuyingPlayer = null; + } + } + } - if (this.getCustomer() != null) - { - this.getCustomer().getName(); - } - else - { - } - } + public void func_110297_a_(ItemStack par1ItemStack) + { + if (!this.worldObj.isRemote && this.livingSoundTime > -this.getTalkInterval() + 20) + { + this.livingSoundTime = -this.getTalkInterval(); + + if (par1ItemStack != null) + { + this.playSound("mob.villager.yes", this.getSoundVolume(), this.getSoundPitch()); + } + else + { + this.playSound("mob.villager.no", this.getSoundVolume(), this.getSoundPitch()); + } + } + } + + // This is called from the gui in order to display the recipes (no surprise there), and this is actually where + // the initialisation is done, i.e. the trades don't actually exist until some player goes to trade with the + // villager, at which point the first is added. + public MerchantRecipeList getRecipes(EntityPlayer par1EntityPlayer){ + + if(this.buyingList == null){ + + this.buyingList = new MerchantRecipeList(); + + // All wizards will buy spell books + ItemStack anySpellBook = new ItemStack(Wizardry.spellBook, 1, OreDictionary.WILDCARD_VALUE); + ItemStack crystalStack = new ItemStack(Wizardry.magicCrystal, 5); + + this.buyingList.add(new MerchantRecipe(anySpellBook, crystalStack)); + + this.addRandomRecipes(3); + } + + return this.buyingList; + } + + /** + * This is called once on initialisation and then once each time the wizard gains new trades (the particle thingy). + */ + private void addRandomRecipes(int numberOfItemsToAdd){ + + MerchantRecipeList merchantrecipelist; + merchantrecipelist = new MerchantRecipeList(); + + for(int i=0; i spells = Spell.getSpells(new Spell.TierElementFilter(tier, null)); // All enabled spells of the given tier that match this wizard's element List specialismSpells = Spell.getSpells(new Spell.TierElementFilter(tier, this.getElement())); - + // This code is sooooooo much neater with the new filter system! switch(tier){ - + case BASIC: randomiser = rand.nextInt(5); if(randomiser < 4 && !spells.isEmpty()){ - if(this.getElement() != Element.MAGIC && rand.nextInt(4) > 0 && !specialismSpells.isEmpty()){ + if(this.getProfession() > 0 && rand.nextInt(4) > 0 && !specialismSpells.isEmpty()){ // This means it is more likely for spell books sold to be of the same element as the wizard if the wizard has an element. - return new ItemStack(WizardryItems.spell_book, 1, specialismSpells.get(rand.nextInt(specialismSpells.size())).id()); + return new ItemStack(Wizardry.spellBook, 1, specialismSpells.get(rand.nextInt(specialismSpells.size())).id()); }else{ - return new ItemStack(WizardryItems.spell_book, 1, spells.get(rand.nextInt(spells.size())).id()); + return new ItemStack(Wizardry.spellBook, 1, spells.get(rand.nextInt(spells.size())).id()); } }else{ - if(this.getElement() != Element.MAGIC && rand.nextInt(4) > 0){ + if(this.getProfession() > 0 && rand.nextInt(4) > 0){ // This means it is more likely for wands sold to be of the same element as the wizard if the wizard has an element. return new ItemStack(WizardryUtilities.getWand(tier, this.getElement())); }else{ - return new ItemStack(WizardryUtilities.getWand(tier, Element.values()[rand.nextInt(Element.values().length)])); + return new ItemStack(WizardryUtilities.getWand(tier, EnumElement.values()[rand.nextInt(EnumElement.values().length)])); } } - + case APPRENTICE: - randomiser = rand.nextInt(Wizardry.settings.discoveryMode ? 12 : 10); + randomiser = rand.nextInt(Wizardry.discoveryMode ? 12 : 10); if(randomiser < 5 && !spells.isEmpty()){ - if(this.getElement() != Element.MAGIC && rand.nextInt(4) > 0 && !specialismSpells.isEmpty()){ + if(this.getProfession() > 0 && rand.nextInt(4) > 0 && !specialismSpells.isEmpty()){ // This means it is more likely for spell books sold to be of the same element as the wizard if the wizard has an element. - return new ItemStack(WizardryItems.spell_book, 1, specialismSpells.get(rand.nextInt(specialismSpells.size())).id()); + return new ItemStack(Wizardry.spellBook, 1, specialismSpells.get(rand.nextInt(specialismSpells.size())).id()); }else{ - return new ItemStack(WizardryItems.spell_book, 1, spells.get(rand.nextInt(spells.size())).id()); + return new ItemStack(Wizardry.spellBook, 1, spells.get(rand.nextInt(spells.size())).id()); } }else if(randomiser < 6){ - if(this.getElement() != Element.MAGIC && rand.nextInt(4) > 0){ + if(this.getProfession() > 0 && rand.nextInt(4) > 0){ // This means it is more likely for wands sold to be of the same element as the wizard if the wizard has an element. return new ItemStack(WizardryUtilities.getWand(tier, this.getElement())); }else{ - return new ItemStack(WizardryUtilities.getWand(tier, Element.values()[rand.nextInt(Element.values().length)])); + return new ItemStack(WizardryUtilities.getWand(tier, EnumElement.values()[rand.nextInt(EnumElement.values().length)])); } }else if(randomiser < 8){ - return new ItemStack(WizardryItems.arcane_tome, 1, 1); + return new ItemStack(Wizardry.arcaneTome, 1, 1); }else if(randomiser < 10){ - EntityEquipmentSlot slot = WizardryUtilities.ARMOUR_SLOTS[rand.nextInt(WizardryUtilities.ARMOUR_SLOTS.length)]; - if(this.getElement() != Element.MAGIC && rand.nextInt(4) > 0){ + int slot = rand.nextInt(4); + if(this.getProfession() > 0 && rand.nextInt(4) > 0){ // This means it is more likely for armour sold to be of the same element as the wizard if the wizard has an element. return new ItemStack(WizardryUtilities.getArmour(this.getElement(), slot)); }else{ - return new ItemStack(WizardryUtilities.getArmour(Element.values()[rand.nextInt(Element.values().length)], slot)); + return new ItemStack(WizardryUtilities.getArmour(EnumElement.values()[rand.nextInt(EnumElement.values().length)], slot)); } }else{ // Don't need to check for discovery mode here since it is done above - return new ItemStack(WizardryItems.identification_scroll); + return new ItemStack(Wizardry.identificationScroll); } - + case ADVANCED: randomiser = rand.nextInt(12); if(randomiser < 5 && !spells.isEmpty()){ - if(this.getElement() != Element.MAGIC && rand.nextInt(4) > 0 && !specialismSpells.isEmpty()){ + if(this.getProfession() > 0 && rand.nextInt(4) > 0 && !specialismSpells.isEmpty()){ // This means it is more likely for spell books sold to be of the same element as the wizard if the wizard has an element. - return new ItemStack(WizardryItems.spell_book, 1, specialismSpells.get(rand.nextInt(specialismSpells.size())).id()); + return new ItemStack(Wizardry.spellBook, 1, specialismSpells.get(rand.nextInt(specialismSpells.size())).id()); }else{ - return new ItemStack(WizardryItems.spell_book, 1, spells.get(rand.nextInt(spells.size())).id()); + return new ItemStack(Wizardry.spellBook, 1, spells.get(rand.nextInt(spells.size())).id()); } }else if(randomiser < 6){ - if(this.getElement() != Element.MAGIC && rand.nextInt(4) > 0){ + if(this.getProfession() > 0 && rand.nextInt(4) > 0){ // This means it is more likely for wands sold to be of the same element as the wizard if the wizard has an element. return new ItemStack(WizardryUtilities.getWand(tier, this.getElement())); }else{ - return new ItemStack(WizardryUtilities.getWand(tier, Element.values()[rand.nextInt(Element.values().length)])); + return new ItemStack(WizardryUtilities.getWand(tier, EnumElement.values()[rand.nextInt(EnumElement.values().length)])); } }else if(randomiser < 8){ - return new ItemStack(WizardryItems.arcane_tome, 1, 2); + return new ItemStack(Wizardry.arcaneTome, 1, 2); }else{ - List upgrades = new ArrayList(WandHelper.getSpecialUpgrades()); - randomiser = rand.nextInt(upgrades.size()); - return new ItemStack(upgrades.get(randomiser)); + randomiser = rand.nextInt(8); + switch(randomiser){ + case 0: return new ItemStack(Wizardry.condenserUpgrade); + case 1: return new ItemStack(Wizardry.siphonUpgrade); + case 2: return new ItemStack(Wizardry.storageUpgrade); + case 3: return new ItemStack(Wizardry.rangeUpgrade); + case 4: return new ItemStack(Wizardry.durationUpgrade); + case 5: return new ItemStack(Wizardry.cooldownUpgrade); + case 6: return new ItemStack(Wizardry.blastUpgrade); + case 7: return new ItemStack(Wizardry.attunementUpgrade); + } } - + case MASTER: // If a regular wizard rolls a master trade, it can only be a simple master wand or a tome of arcana - randomiser = this.getElement() != Element.MAGIC ? rand.nextInt(8) : 5 + rand.nextInt(3); - - if(randomiser < 5 && this.getElement() != Element.MAGIC && !specialismSpells.isEmpty()){ + randomiser = this.getProfession() > 0 ? rand.nextInt(8) : 5 + rand.nextInt(3); + + if(randomiser < 5 && this.getProfession() > 0 && !specialismSpells.isEmpty()){ // Master spells can only be sold by a specialist in that element. - return new ItemStack(WizardryItems.spell_book, 1, specialismSpells.get(rand.nextInt(specialismSpells.size())).id()); - + return new ItemStack(Wizardry.spellBook, 1, specialismSpells.get(rand.nextInt(specialismSpells.size())).id()); + }else if(randomiser < 6){ - if(this.getElement() != Element.MAGIC && rand.nextInt(4) > 0){ + if(this.getProfession() > 0 && rand.nextInt(4) > 0){ // Master elemental wands can only be sold by a specialist in that element. return new ItemStack(WizardryUtilities.getWand(tier, this.getElement())); }else{ - return new ItemStack(WizardryItems.master_wand); + return new ItemStack(Wizardry.masterWand); } }else{ - return new ItemStack(WizardryItems.arcane_tome, 1, 3); + return new ItemStack(Wizardry.arcaneTome, 1, 3); } } - return new ItemStack(Blocks.STONE); - } - - @Override - public void setProfession(VillagerProfession prof) { - // Disables Forge's stuff. + return new ItemStack(Blocks.stone); } - @Override - public IEntityLivingData onInitialSpawn(DifficultyInstance difficulty, IEntityLivingData livingdata){ + @SideOnly(Side.CLIENT) + public void setRecipes(MerchantRecipeList par1MerchantRecipeList) {} - livingdata = super.onInitialSpawn(difficulty, livingdata); + public IEntityLivingData onSpawnWithEgg(IEntityLivingData par1EntityLivingData) + { + par1EntityLivingData = super.onSpawnWithEgg(par1EntityLivingData); textureIndex = this.rand.nextInt(6); + + if(rand.nextBoolean()){ + this.setProfession(rand.nextInt(EnumElement.values().length - 1) + 1); + }else{ + this.setProfession(0); + } + + EnumElement e = this.getElement(); + + this.setCurrentItemOrArmor(1, new ItemStack(WizardryUtilities.getArmour(e, 3))); + this.setCurrentItemOrArmor(2, new ItemStack(WizardryUtilities.getArmour(e, 2))); + this.setCurrentItemOrArmor(3, new ItemStack(WizardryUtilities.getArmour(e, 1))); + this.setCurrentItemOrArmor(4, new ItemStack(WizardryUtilities.getArmour(e, 0))); + + // This is the tier of the highest tier spell the wizard has. + EnumTier maxTier = EnumTier.BASIC; + + // Default chance is 0.085f, for reference. + this.setEquipmentDropChance(0, 0.0f); + this.setEquipmentDropChance(1, 0.0f); + this.setEquipmentDropChance(2, 0.0f); + this.setEquipmentDropChance(3, 0.0f); + this.setEquipmentDropChance(4, 0.0f); + + // All wizards know magic missile, even if it is disabled. + spells[0] = WizardryRegistry.magicMissile.id(); + + List npcSpells = Spell.getSpells(Spell.npcSpells); + + for(int i=1; i maxTier.ordinal()) maxTier = tier; + + // Finds all the spells of the chosen tier and element + List list = Spell.getSpells(new Spell.TierElementFilter(tier, element)); + // Keeps only spells which can be cast by NPCs + list.retainAll(npcSpells); + // Removes spells that the wizard already has + for(int j=0; j spells, Element e, int n, Random random){ - - // This is the tier of the highest tier spell added. - Tier maxTier = Tier.BASIC; - - List npcSpells = Spell.getSpells(Spell.npcSpells); - - for(int i=0; i<3; i++){ - - Tier tier; - // If the wizard has no element, it picks a random one each time. - Element element = e == Element.MAGIC ? Element.values()[random.nextInt(Element.values().length)] : e; - - int randomiser = random.nextInt(20); - - // Uses its own special weighting - if(randomiser < 10){ - tier = Tier.BASIC; - }else if(randomiser < 16){ - tier = Tier.APPRENTICE; - }else{ - tier = Tier.ADVANCED; - } - - if(tier.ordinal() > maxTier.ordinal()) maxTier = tier; - - // Finds all the spells of the chosen tier and element - List list = Spell.getSpells(new Spell.TierElementFilter(tier, element)); - // Keeps only spells which can be cast by NPCs - list.retainAll(npcSpells); - // Removes spells that the wizard already has - list.removeAll(spells); - - // Ensures the tier chosen actually has spells in it. (isEmpty() is exactly the same as size() == 0) - if(list.isEmpty()){ - // If there are no spells applicable, tier and element restrictions are removed to give maximum - // possibility of there being an applicable spell. - list = npcSpells; - // Removes spells that the wizard already has - list.removeAll(spells); - } - - // If the list is still empty now, there must be less than 3 enabled spells that can be cast by wizards - // (excluding magic missile). In this case, having empty slots seems reasonable. - if(!list.isEmpty()) spells.add(list.get(random.nextInt(list.size()))); - - } - - return maxTier; - } + public EntityVillager createChild(EntityAgeable par1EntityAgeable) + { + return this.doSomething(par1EntityAgeable); + } @Override public void writeSpawnData(ByteBuf data){ @@ -753,25 +865,33 @@ public class EntityWizard extends EntityVillager implements ISpellCaster, IEntit textureIndex = data.readInt(); } - @Override - public boolean attackEntityFrom(DamageSource source, float damage){ - - if(source.getEntity() instanceof EntityPlayer){ - ((EntityPlayer)source.getEntity()).addStat(WizardryAchievements.anger_wizard); - } - - return super.attackEntityFrom(source, damage); + private EntityPlayer getBuyingPlayer() { + return buyingPlayer == null ? null : buyingPlayer.get(); } + private void setBuyingPlayer(EntityPlayer buyingPlayer) { + this.buyingPlayer = new WeakReference(buyingPlayer); + } + + @Override + public boolean attackEntityFrom(DamageSource source, float damage){ + + if(source.getEntity() instanceof EntityPlayer){ + ((EntityPlayer)source.getEntity()).triggerAchievement(Wizardry.angerWizard); + } + + return super.attackEntityFrom(source, damage); + } + /** * Sets the list of blocks that are part of this wizard's tower. If a player breaks any of these blocks, the wizard * will get angry and attack them. - * @param blocks A Set of BlockPos objects representing the blocks in the tower. + * @param blocks An array of coordinate arrays for the blocks in the tower. */ - public void setTowerBlocks(Set blocks){ + public void setTowerBlocks(int[][] blocks){ this.towerBlocks = blocks; } - + /** * Tests whether the block at the given coordinates is part of this wizard's tower. * @param x @@ -779,49 +899,39 @@ public class EntityWizard extends EntityVillager implements ISpellCaster, IEntit * @param z * @return */ - public boolean isBlockPartOfTower(BlockPos pos){ - if(this.towerBlocks == null) return false; - // Uses .equals() rather than == so this will work fine. - return this.towerBlocks.contains(pos); + public boolean isBlockPartOfTower(int x, int y, int z){ + + if(this.towerBlocks == null || this.towerBlocks.length <= 0) return false; + + for(int[] block : this.towerBlocks){ + if(block != null && block[0] == x && block[1] == y && block[2] == z) return true; + } + + return false; } - @SubscribeEvent - public static void onBlockBreakEvent(BlockEvent.BreakEvent event){ - // Makes wizards angry if a player breaks a block in their tower - if(!(event.getPlayer() instanceof FakePlayer)){ - - List wizards = WizardryUtilities.getEntitiesWithinRadius(64, event.getPos().getX(), - event.getPos().getY(), event.getPos().getZ(), event.getWorld(), EntityWizard.class); - - if(!wizards.isEmpty()){ - for(EntityWizard wizard : wizards){ - if(wizard.isBlockPartOfTower(event.getPos())){ - wizard.setRevengeTarget(event.getPlayer()); - event.getPlayer().addStat(WizardryAchievements.anger_wizard); - } - } - } - } - } - - // EntityVillager overrides (that don't add features) - - @Override public boolean isMating(){ return false; } - @Override public void setMating(boolean p_70947_1_){} - @Override public void setPlaying(boolean p_70939_1_){} - @Override public boolean isPlaying(){ return false; } - @Override public void setLookingForHome(){} - // Doesn't say it, but this is in fact nullable. - @Override public EntityVillager createChild(EntityAgeable par1EntityAgeable){ return null; } - @SideOnly(Side.CLIENT) - @Override public void setRecipes(MerchantRecipeList par1MerchantRecipeList){} + // Start of EntityVillager overrides + @Override - public void onStruckByLightning(EntityLightningBolt lightningBolt){ - // Restores the normal behaviour, replacing EntityVillager's witch conversion. - this.attackEntityFrom(DamageSource.lightningBolt, 5.0F); - // Entity's version does something strange with the private fire variable, but since I don't have access this - // will probably be fine. - this.setFire(8); - } - + public boolean isMating() + { + return false; + } + + @Override + public void setMating(boolean p_70947_1_){} + + @Override + public void setPlaying(boolean p_70939_1_){} + + @Override + public boolean isPlaying() + { + return false; + } + + @Override + public void setLookingForHome(){} + + } diff --git a/src/main/java/electroblob/wizardry/entity/living/EntityZombieMinion.java b/src/main/java/electroblob/wizardry/entity/living/EntityZombieMinion.java index c1ac3a7a..c343215e 100644 --- a/src/main/java/electroblob/wizardry/entity/living/EntityZombieMinion.java +++ b/src/main/java/electroblob/wizardry/entity/living/EntityZombieMinion.java @@ -1,175 +1,319 @@ package electroblob.wizardry.entity.living; -import java.lang.ref.WeakReference; import java.util.UUID; +import cpw.mods.fml.relauncher.Side; +import cpw.mods.fml.relauncher.SideOnly; +import electroblob.wizardry.EnumParticleType; import electroblob.wizardry.Wizardry; -import net.minecraft.entity.EntityFlying; +import net.minecraft.entity.Entity; +import net.minecraft.entity.EntityLiving; import net.minecraft.entity.EntityLivingBase; +import net.minecraft.entity.EnumCreatureAttribute; +import net.minecraft.entity.SharedMonsterAttributes; +import net.minecraft.entity.ai.EntityAIAttackOnCollide; import net.minecraft.entity.ai.EntityAIHurtByTarget; +import net.minecraft.entity.ai.EntityAILookIdle; import net.minecraft.entity.ai.EntityAIMoveThroughVillage; +import net.minecraft.entity.ai.EntityAIMoveTowardsRestriction; import net.minecraft.entity.ai.EntityAINearestAttackableTarget; -import net.minecraft.entity.monster.EntityZombie; -import net.minecraft.entity.monster.ZombieType; -import net.minecraft.entity.player.EntityPlayer; -import net.minecraft.item.Item; +import net.minecraft.entity.ai.EntityAISwimming; +import net.minecraft.entity.ai.EntityAIWander; +import net.minecraft.entity.ai.attributes.AttributeModifier; +import net.minecraft.entity.ai.attributes.IAttributeInstance; +import net.minecraft.entity.monster.EntityMob; +import net.minecraft.entity.monster.IMob; import net.minecraft.item.ItemStack; import net.minecraft.nbt.NBTTagCompound; -import net.minecraft.util.EnumHand; -import net.minecraft.util.EnumParticleTypes; -import net.minecraft.util.ResourceLocation; -import net.minecraft.util.text.ITextComponent; -import net.minecraft.util.text.TextComponentTranslation; -import net.minecraft.world.DifficultyInstance; +import net.minecraft.util.DamageSource; +import net.minecraft.util.MathHelper; import net.minecraft.world.World; -public class EntityZombieMinion extends EntityZombie implements ISummonedCreature { +public class EntityZombieMinion extends EntitySummonedCreature { - // Field implementations - private int lifetime = 600; - private WeakReference casterReference; - private UUID casterUUID; - - // Setter + getter implementations - @Override public int getLifetime(){ return lifetime; } - @Override public void setLifetime(int lifetime){ this.lifetime = lifetime; } - @Override public WeakReference getCasterReference(){ return casterReference; } - @Override public void setCasterReference(WeakReference reference){ casterReference = reference; } - @Override public UUID getCasterUUID() { return casterUUID; } - @Override public void setCasterUUID(UUID uuid) { this.casterUUID = uuid; } - - /** - * Default shell constructor, only used by client. Lifetime defaults arbitrarily to 600, but this doesn't - * matter because the client side entity immediately gets the lifetime value copied over to it by this class - * anyway. When extending this class, you must override this constructor or Minecraft won't like it, but there's - * no need to do anything inside it other than call super(). - */ public EntityZombieMinion(World world){ super(world); - this.experienceValue = 0; } - - /** - * Set lifetime to -1 to allow this creature to last forever. This constructor should be overridden when - * extending this class (be sure to call super()) so that AI and other things can be added. - */ + public EntityZombieMinion(World world, double x, double y, double z, EntityLivingBase caster, int lifetime){ - super(world); - this.setPosition(x, y, z); - this.casterReference = new WeakReference(caster); + + super(world, x, y, z, caster, lifetime); + + // The first number in the AI tasks is priority. + this.getNavigator().setBreakDoors(true); + this.tasks.addTask(0, new EntityAISwimming(this)); + // When it comes to ally system support, EntityMob.class needs to be changed to EntityLivingBase.class + // Parameters: Entity, Target type, move speed, long memory (will keep target even if it can't get to them) + this.tasks.addTask(1, new EntityAIAttackOnCollide(this, EntityLivingBase.class, 1.0D, true)); + this.tasks.addTask(2, new EntityAIMoveTowardsRestriction(this, 1.0D)); + this.tasks.addTask(3, new EntityAIMoveThroughVillage(this, 1.0D, false)); + this.tasks.addTask(4, new EntityAIWander(this, 1.0D)); + this.tasks.addTask(5, new EntityAILookIdle(this)); + this.targetTasks.addTask(1, new EntityAIHurtByTarget(this, true)); + // A good way of getting it to work with the new AI would be to make an anonymous class implementing + // IEntitySelector in EntitySummonedCreature and feed it in here, changing EntityLiving to EntityLivingBase. + // Also, the reason it uses an entity selector in the first place is because not all IMobs are subclasses of + // EntityMob, namely slimes, ghasts and the enderdragon. + this.targetTasks.addTask(2, new EntityAINearestAttackableTarget(this, EntityLivingBase.class, 0, false, true, this.targetSelector)); this.experienceValue = 0; - this.lifetime = lifetime; } - - // EntityZombie overrides (EntityZombie is a long class so there are lots of these) - - @Override - protected void applyEntityAI() - { - this.tasks.addTask(6, new EntityAIMoveThroughVillage(this, 1.0D, false)); - this.targetTasks.addTask(1, new EntityAIHurtByTarget(this, false)); - this.targetTasks.addTask(2, new EntityAINearestAttackableTarget(this, EntityLivingBase.class, - 0, false, true, this.getTargetSelector())); - } - - @Override public boolean isChild(){ return false; } - @Override public void setChild(boolean childZombie){} - @Override public ZombieType getZombieType(){ return ZombieType.NORMAL; } - @Override public boolean isVillager(){ return false; } - @Override public net.minecraftforge.fml.common.registry.VillagerRegistry.VillagerProfession getVillagerTypeForge(){ return null; } - @Override protected void setEquipmentBasedOnDifficulty(DifficultyInstance difficulty){} // They don't have equipment! - @Override public void onKillEntity(EntityLivingBase entityLivingIn){} // Turns villagers to zombies in EntityZombie - @Override protected void startConversion(int ticks){} - @Override public boolean isConverting(){ return false; } - @Override protected void convertToVillager(){} - @Override protected int getConversionTimeBoost(){ return 0; } - @Override public void setChildSize(boolean isChild){} - - // Implementations - @Override - public void setRevengeTarget(EntityLivingBase entity){ - if(this.shouldRevengeTarget(entity)) super.setRevengeTarget(entity); + protected void applyEntityAttributes() + { + super.applyEntityAttributes(); + this.getEntityAttribute(SharedMonsterAttributes.followRange).setBaseValue(40.0D); + this.getEntityAttribute(SharedMonsterAttributes.movementSpeed).setBaseValue(0.23000000417232513D); + this.getEntityAttribute(SharedMonsterAttributes.attackDamage).setBaseValue(3.0D); } @Override - public void onUpdate(){ - super.onUpdate(); - this.updateDelegate(); + public boolean hasRangedAttack() { + return false; + } + + protected void entityInit() + { + super.entityInit(); + this.getDataWatcher().addObject(12, Byte.valueOf((byte)0)); + this.getDataWatcher().addObject(13, Byte.valueOf((byte)0)); + this.getDataWatcher().addObject(14, Byte.valueOf((byte)0)); + } + + /** + * Returns the current armor value as determined by a call to InventoryPlayer.getTotalArmorValue + */ + public int getTotalArmorValue() + { + int i = super.getTotalArmorValue() + 2; + + if (i > 20) + { + i = 20; + } + + return i; + } + + /** + * Returns true if the newer Entity AI code should be run + */ + protected boolean isAIEnabled() + { + return true; + } + + /** + * If Animal, checks if the age timer is negative + */ + public boolean isChild() + { + return this.getDataWatcher().getWatchableObjectByte(12) == 1; + } + + /** + * Return whether this zombie is a villager. + */ + public boolean isVillager() + { + return this.getDataWatcher().getWatchableObjectByte(13) == 1; + } + + /** + * Set whether this zombie is a villager. + */ + public void setVillager(boolean par1) + { + this.getDataWatcher().updateObject(13, Byte.valueOf((byte)(par1 ? 1 : 0))); } @Override public void onSpawn(){ - this.spawnParticleEffect(); - } - - @Override - public void onDespawn(){ - this.spawnParticleEffect(); - } - - private void spawnParticleEffect(){ if(this.worldObj.isRemote){ for(int i=0;i<15;i++){ - this.worldObj.spawnParticle(EnumParticleTypes.SMOKE_LARGE, this.posX + this.rand.nextFloat(), this.posY + 1 + this.rand.nextFloat(), this.posZ + this.rand.nextFloat(), 0, 0, 0); + this.worldObj.spawnParticle("largesmoke", this.posX + this.rand.nextFloat(), this.posY + 1 + this.rand.nextFloat(), this.posZ + this.rand.nextFloat(), 0, 0, 0); } } } @Override - public boolean hasParticleEffect() { - return true; + public void despawn(){ + if(this.worldObj.isRemote){ + for(int i=0;i<15;i++){ + this.worldObj.spawnParticle("largesmoke", this.posX + this.rand.nextFloat(), this.posY + 1 + this.rand.nextFloat(), this.posZ + this.rand.nextFloat(), 0, 0, 0); + } + } + super.despawn(); } - @Override - public boolean processInteract(EntityPlayer player, EnumHand hand, ItemStack stack) { - // In this case, the delegate method determines whether super is called. - // Rather handily, we can make use of Java's 'stop as soon as you find true' method of evaluating OR statements. - return this.interactDelegate(player, hand, stack) || super.processInteract(player, hand, stack); + /** + * Called frequently so the entity can update its state every tick as required. For example, zombies and skeletons + * use this to react to sunlight and start to burn. + */ + public void onLivingUpdate() + { + if (this.worldObj.isDaytime() && !this.worldObj.isRemote && !this.isChild()) + { + float f = this.getBrightness(1.0F); + + if (f > 0.5F && this.rand.nextFloat() * 30.0F < (f - 0.4F) * 2.0F && this.worldObj.canBlockSeeTheSky(MathHelper.floor_double(this.posX), MathHelper.floor_double(this.posY), MathHelper.floor_double(this.posZ))) + { + boolean flag = true; + ItemStack itemstack = this.getEquipmentInSlot(4); + + if (itemstack != null) + { + if (itemstack.isItemStackDamageable()) + { + itemstack.setItemDamage(itemstack.getItemDamageForDisplay() + this.rand.nextInt(2)); + + if (itemstack.getItemDamageForDisplay() >= itemstack.getMaxDamage()) + { + this.renderBrokenItemStack(itemstack); + this.setCurrentItemOrArmor(4, (ItemStack)null); + } + } + + flag = false; + } + + if (flag) + { + this.setFire(8); + } + } + } + + super.onLivingUpdate(); } - @Override - public void writeEntityToNBT(NBTTagCompound nbttagcompound){ - super.writeEntityToNBT(nbttagcompound); - this.writeNBTDelegate(nbttagcompound); - } + /** + * Called when the entity is attacked. + */ + public boolean attackEntityFrom(DamageSource par1DamageSource, float par2) + { + if (!super.attackEntityFrom(par1DamageSource, par2)) + { + return false; + } + else + { + EntityLivingBase entitylivingbase = this.getAttackTarget(); - @Override - public void readEntityFromNBT(NBTTagCompound nbttagcompound){ - super.readEntityFromNBT(nbttagcompound); - this.writeNBTDelegate(nbttagcompound); - } + if (entitylivingbase == null && this.getEntityToAttack() instanceof EntityLivingBase) + { + entitylivingbase = (EntityLivingBase)this.getEntityToAttack(); + } - // Recommended overrides + if (entitylivingbase == null && par1DamageSource.getEntity() instanceof EntityLivingBase) + { + entitylivingbase = (EntityLivingBase)par1DamageSource.getEntity(); + } - @Override protected int getExperiencePoints(EntityPlayer player){ return 0; } - @Override protected boolean canDropLoot(){ return false; } - @Override protected Item getDropItem(){ return null; } - @Override protected ResourceLocation getLootTable(){ return null; } - @Override public boolean canPickUpLoot(){ return false; } - // This vanilla method has nothing to do with the custom despawn() method. - @Override protected boolean canDespawn(){ return false; } - - @Override - public boolean canAttackClass(Class entityType){ - // Returns true unless the given entity type is a flying entity. - return !EntityFlying.class.isAssignableFrom(entityType); - } - - @Override - public ITextComponent getDisplayName(){ - if(getCaster() != null){ - return new TextComponentTranslation(NAMEPLATE_TRANSLATION_KEY, getCaster().getName(), - new TextComponentTranslation("entity." + this.getEntityString() + ".name")); - }else{ - return super.getDisplayName(); + return true; } } @Override - public boolean hasCustomName(){ - // If this returns true, the renderer will show the nameplate when looking directly at the entity - return Wizardry.settings.showSummonedCreatureNames && getCaster() != null; + public boolean attackEntityAsMob(Entity entity) + { + boolean flag = super.attackEntityAsMob(entity); + + if (flag) + { + int i = this.worldObj.difficultySetting.getDifficultyId(); + + if (this.getHeldItem() == null && this.isBurning() && this.rand.nextFloat() < (float)i * 0.3F) + { + entity.setFire(2 * i); + } + } + + return flag; } -} \ No newline at end of file + /** + * Called to update the entity's position/logic. + */ + public void onUpdate() + { + // Adds subtle particle effect while alive + if(this.worldObj.isRemote && rand.nextInt(8) == 0) Wizardry.proxy.spawnParticle(EnumParticleType.DARK_MAGIC, worldObj, this.posX, this.posY + rand.nextDouble()*1.5, this.posZ, 0.0d, 0.0d, 0.0d, 0, 0.1f, 0.0f, 0.0f); + + super.onUpdate(); + } + + /** + * Returns the sound this mob makes while it's alive. + */ + protected String getLivingSound() + { + return "mob.zombie.say"; + } + + /** + * Returns the sound this mob makes when it is hurt. + */ + protected String getHurtSound() + { + return "mob.zombie.hurt"; + } + + /** + * Returns the sound this mob makes on death. + */ + protected String getDeathSound() + { + return "mob.zombie.death"; + } + + /** + * Plays step sound at given x, y, z for the entity + */ + protected void playStepSound(int par1, int par2, int par3, int par4) + { + this.playSound("mob.zombie.step", 0.15F, 1.0F); + } + + /** + * Returns the item ID for the item the mob drops on death. + */ + protected int getDropItemId() + { + return -1; + } + + /** + * Get this Entity's EnumCreatureAttribute + */ + public EnumCreatureAttribute getCreatureAttribute() + { + return EnumCreatureAttribute.UNDEAD; + } + + protected void dropRareDrop(int par1) + { + + } + + /** + * Makes entity wear random armor based on difficulty + */ + protected void addRandomArmor() + { + + } + + @SideOnly(Side.CLIENT) + public void handleHealthUpdate(byte par1) + { + if (par1 == 16) + { + this.worldObj.playSound(this.posX + 0.5D, this.posY + 0.5D, this.posZ + 0.5D, "mob.zombie.remedy", 1.0F + this.rand.nextFloat(), this.rand.nextFloat() * 0.7F + 0.3F, false); + } + else + { + super.handleHealthUpdate(par1); + } + } + +} diff --git a/src/main/java/electroblob/wizardry/entity/living/IIntelligentSpellCaster.java b/src/main/java/electroblob/wizardry/entity/living/IIntelligentSpellCaster.java deleted file mode 100644 index 1bcb7c86..00000000 --- a/src/main/java/electroblob/wizardry/entity/living/IIntelligentSpellCaster.java +++ /dev/null @@ -1,20 +0,0 @@ -package electroblob.wizardry.entity.living; - -import java.util.List; - -import electroblob.wizardry.spell.Spell; - -/** [NYI] Interface for entities that can select between spells based on their current circumstances, to be used in - * conjunction with {@link EntityAISelectSpell}. */ -public interface IIntelligentSpellCaster extends ISpellCaster { - - /** Called from {@link EntityAISelectSpell} to set the spells that the entity can use in its current situation. - * Implementors should assign the given list to some internal field and retrieve it when {@link #getSpells()} is - * called. */ - public void setCurrentSpells(List spells); - - /** Returns a list of spells that this entity 'knows'. The entity's current spell will be selected from this list - * based on the current situation: whether it is attacking, its health, etc. Most likely, you will want to return a - * constant list, but it may change for some reason - perhaps if the entity 'learns' a new spell. */ - public List getKnownSpells(); -} diff --git a/src/main/java/electroblob/wizardry/entity/living/ISpellCaster.java b/src/main/java/electroblob/wizardry/entity/living/ISpellCaster.java deleted file mode 100644 index ce2733df..00000000 --- a/src/main/java/electroblob/wizardry/entity/living/ISpellCaster.java +++ /dev/null @@ -1,61 +0,0 @@ -package electroblob.wizardry.entity.living; - -import java.util.List; - -import javax.annotation.Nonnull; - -import electroblob.wizardry.registry.Spells; -import electroblob.wizardry.spell.Spell; -import electroblob.wizardry.util.SpellModifiers; - -/** Interface for entities that can cast spells. Mainly intended for use by wizard-type entities, but can be - * implemented by any subclass of EntityLiving. Designed to be as flexible as possible - ranging from the simplest - * use of giving an entity a specific spell as an attack, to a complex AI which selects different spell types - * depending on the situation. The only restriction is that the spells must be castable by NPCs. - *

    - * This is intended for entities that use {@link EntityAIAttackSpell}. If so, all the spell casting code (including - * packets) is handled by that class, and all the implementor needs to do is decide which spell(s) to select. - *

    - * 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. - */ -/* 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 I may as well make it an API feature - but I'm not writing a capability unless someone sees a reason to attach - * it to a class they don't own, and I don't see that happening anytime soon. */ -// For even more fun, combine this with an ISummonedCreature and have skeletons that can cast spells! -public interface ISpellCaster { - - /** - * Called each time the entity attacks to get the spells that can be cast. For simple implementations, just - * return a list of size one containing the spell that the entity uses as an attack, perhaps performing some - * simple logic within this method. For more intelligent implementations based on spell types, consider using - * {@link IIntelligentSpellCaster} instead. - * @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 list - * must be castable by NPCs (i.e. {@link Spell#canBeCastByNPCs()} returns true). - */ - @Nonnull - public List getSpells(); - - /** - * Called each time the entity attacks to get the modifiers to apply to the spell. - * @return A {@link SpellModifiers} object representing the modifiers to apply to the spell. If no modifiers are required, - * pass in an empty {@code SpellModifiers} object. - */ - @Nonnull - public SpellModifiers getModifiers(); - - /** 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 saved to NBT - * is up to you. If the implementing class does not deal with continuous spells, just return {@link Spells#none}. If the - * implementing class only ever uses one continuous spell, do not just return that spell; the field must still - * be stored. */ - @Nonnull - public Spell getContinuousSpell(); - - /** 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 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); -} diff --git a/src/main/java/electroblob/wizardry/entity/living/ISummonedCreature.java b/src/main/java/electroblob/wizardry/entity/living/ISummonedCreature.java deleted file mode 100644 index 39c68a28..00000000 --- a/src/main/java/electroblob/wizardry/entity/living/ISummonedCreature.java +++ /dev/null @@ -1,333 +0,0 @@ -package electroblob.wizardry.entity.living; - -import java.lang.ref.WeakReference; -import java.util.Arrays; -import java.util.Locale; -import java.util.UUID; - -import javax.annotation.Nullable; - -import com.google.common.base.Predicate; - -import electroblob.wizardry.WizardData; -import electroblob.wizardry.Wizardry; -import electroblob.wizardry.WizardryEventHandler; -import electroblob.wizardry.item.ItemWand; -import electroblob.wizardry.util.IElementalDamage; -import electroblob.wizardry.util.IndirectMinionDamage; -import electroblob.wizardry.util.MagicDamage.DamageType; -import electroblob.wizardry.util.MinionDamage; -import electroblob.wizardry.util.WizardryParticleType; -import electroblob.wizardry.util.WizardryUtilities; -import io.netty.buffer.ByteBuf; -import net.minecraft.entity.Entity; -import net.minecraft.entity.EntityList; -import net.minecraft.entity.EntityLivingBase; -import net.minecraft.entity.monster.IMob; -import net.minecraft.entity.player.EntityPlayer; -import net.minecraft.item.ItemStack; -import net.minecraft.nbt.NBTTagCompound; -import net.minecraft.util.DamageSource; -import net.minecraft.util.EntityDamageSource; -import net.minecraft.util.EntityDamageSourceIndirect; -import net.minecraft.util.EnumHand; -import net.minecraftforge.event.entity.living.LivingAttackEvent; -import net.minecraftforge.fml.common.Mod; -import net.minecraftforge.fml.common.eventhandler.SubscribeEvent; -import net.minecraftforge.fml.common.registry.IEntityAdditionalSpawnData; - -/** Interface for all summoned creatures. The code for summoned creatures has been overhauled in Wizardry 2.1, and this - * interface allows summoned creatures to extend vanilla (or indeed modded) entity classes, so EntitySummonedZombie - * now extends EntityZombie, for example. This change has two major benefits: - *

    - * - There is no longer any need for separate render classes, because summoned creatures are now instances of vanilla - * types. You don't even need to assign a render class because the supertype should already be assigned the correct one.
    - * - Summoned creature classes are now much more robust when it comes to changes between Minecraft versions, since none - * of the vanilla code needs to be copied. - *

    - * Summoned creatures that do not emulate vanilla entities do not directly implement this interface. Instead, they - * should extend the abstract base implementation, {@link EntitySummonedCreature}. - *

    - * All damage dealt by ISummonedCreature instances is redirected via {@link WizardryEventHandler#onLivingAttackEvent( - * net.minecraftforge.event.entity.living.LivingAttackEvent) WizardryEventHandler.onLivingAttackEvent(LivingAttackEvent)} - * and replaced by an instance of {@link electroblob.wizardry.util.IElementalDamage IElementalDamage} with the summoner - * of that creature as the source rather than the creature itself. This means that kills by summoned creatures register - * as player kills, dropping xp and rare loot. - *

    - * Though this system is a lot better than the previous system, it is not a perfect solution. The old - * EntitySummonedCreature class overrode some methods from Entity in order to add shared functionality, but this cannot - * be done with an interface. To get around this problem, this interface contains 5 delegate methods that do the same - * things, with the aim of centralising as much code as possible, even though it is not automatically applied. - * Implementing classes must override the corresponding methods from Entity, and within them, call the appropriate - * delegate method in this interface. It is impossible to enforce this condition, but the summoned creature will not - * work properly unless it is adhered to. The position of the delegate method call is unimportant, but by convention - * it is usually at the start of the calling method, which avoids it being unintentionally skipped by a return statement. - *

    - * It is recommended that when implementing this interface, you begin by copying {@link EntitySummonedCreature} to ensure - * all the relevant methods are duplicated. You can then change the superclass, override any additional methods and add - * functionality to any that are already overridden. You will always want to override the AI methods at the very least. - *

    - * Due to the limitations of interfaces, some methods that really ought to be protected are public. These are clearly - * marked as 'Internal, DO NOT CALL'. Don't call them, only implement them. - * @since Wizardry 2.1 - * @author Electroblob */ -/* - * Quite honestly, this is not what default methods are really for. However, this is modding, and in modding some - * sacrifices have to be made when it comes to Java style - because adding on to a pre-existing program is not a - * good way of doing this sort of thing anyway, but we have no choice about that! - */ -@Mod.EventBusSubscriber -public interface ISummonedCreature extends IEntityAdditionalSpawnData { - - // Remember that ALL fields are static and final in interfaces, even if they don't explicitly state that. - String NAMEPLATE_TRANSLATION_KEY = "entity.wizardry.summonedcreature.nameplate"; - - // Setters and getters. The subclass fields that these access should be private. - - /** Sets the lifetime of the summoned creature in ticks. */ - void setLifetime(int ticks); - - /** Returns the lifetime of the summoned creature in ticks. Allows primarily for duration multiplier support, but - * also for example the skeleton legion spell which lasts for 60 seconds instead of the usual 30. Syncing and saving - * is done automatically. As of Wizardry 2.1, despawning is handled in ISummonedCreature; see - * {@link ISummonedCreature#onDespawn()} for details. */ - int getLifetime(); - - /** Sets the WeakReference object which refers to the owner of this summoned creature. Internal, don't call - * unless you know what you are doing. */ - void setCasterReference(WeakReference reference); - - /** Returns a WeakReference object which refers to the owner of this summoned creature. Subclasses should store this - * as a private field. This may be null; as such it is preferable to use {@link ISummonedCreature#getCaster()} to get - * the caster object itself. */ - @Nullable - WeakReference getCasterReference(); - - /** Internal, DO NOT CALL. */ - void setCasterUUID(UUID uuid); - /** Internal, DO NOT CALL. This is for loading purposes only and is not usually synchronised. */ - UUID getCasterUUID(); - - /** Returns the EntityLivingBase that summoned this creature, or null if it no longer exists. Cases where the - * entity may no longer exist are: entity died or was deleted, mob despawned, player logged out, entity teleported - * to another dimension, or this creature simply had no caster in the first place. This is the correct method - * to use to get the owner of this summoned creature. */ - @Nullable - default EntityLivingBase getCaster(){ - return getCasterReference() == null ? null : getCasterReference().get(); - } - - // Miscellaneous - - /** - * Called by the server when constructing the spawn packet. - * Data should be added to the provided stream. - * Implementors must call super when overriding. - * - * @param buffer The packet data stream - */ - @Override - default void writeSpawnData(ByteBuf buffer) { - buffer.writeInt(getCaster() != null ? getCaster().getEntityId() : -1); - buffer.writeInt(getLifetime()); - } - - /** - * Called by the client when it receives a Entity spawn packet. - * Data should be read out of the stream in the same way as it was written. - * Implementors must call super when overriding. - * - * @param additionalData The packet data stream - */ - @Override - default void readSpawnData(ByteBuf buffer) { - int id = buffer.readInt(); - // We're on the client side here, so we can safely use Minecraft.getMinecraft().theWorld via proxies. - if(id > -1) setCasterReference(new WeakReference((EntityLivingBase)Wizardry.proxy.getTheWorld().getEntityByID(id))); - setLifetime(buffer.readInt()); - } - - /** - * Shorthand for {@link WizardryUtilities#isValidTarget(Entity, Entity)}, with the owner of this creature as the - * attacker. Also allows implementors to override it if they wish to do so. - */ - default boolean isValidTarget(Entity target){ - return WizardryUtilities.isValidTarget(this.getCaster(), target); - } - - /** Returns a entity selector to be passed into AI methods. Normally, this should not be overridden, but it is - * possible for implementors to override this in order to do something special when selecting a target. */ - default Predicate getTargetSelector(){ - - return new Predicate(){ - - public boolean apply(Entity entity){ - // TODO: Backport invisibility check (also in wizards) - // If the target is valid and not invisible... - if(!entity.isInvisible() && isValidTarget(entity)){ - - //... and is a player, they can be attacked, since players can't be in the whitelist or the blacklist. - if(entity instanceof EntityPlayer) return true; - - //... and is a mob, a summoned creature, a wizard ... - if((entity instanceof IMob || entity instanceof ISummonedCreature || (entity instanceof EntityWizard && !(getCaster() instanceof EntityWizard)) - // ... or in the whitelist ... - || Arrays.asList(Wizardry.settings.summonedCreatureTargetsWhitelist).contains(EntityList.getEntityString(entity).toLowerCase(Locale.ROOT))) - // ... and isn't in the blacklist ... - && !Arrays.asList(Wizardry.settings.summonedCreatureTargetsBlacklist).contains(EntityList.getEntityString(entity).toLowerCase(Locale.ROOT))){ - // ... it can be attacked. - return true; - } - } - - return false; - } - }; - } - - /** - * Called when this creature has existed for 1 tick, effectively when it has just been spawned. Normally used - * to add particles, sounds, etc. - */ - void onSpawn(); - - /** - * Called when this summoned creature vanishes. Normally used to add particles, sounds, etc. - */ - void onDespawn(); - - /** Whether this creature should spawn a subtle black swirl particle effect while alive. */ - boolean hasParticleEffect(); - - /** Called from the event handler after the damage change is applied. Does nothing by default, but can be overridden - * to do something when a successful attack is made. This was added because the event-based damage source system - * can cause parts of attackEntityAsMob not to fire, since attackEntityFrom is intercepted and canceled. - *

    - * Usage examples: {@link EntitySliverfishMinion} uses this to summon more silverfish if the target is killed, - * {@link EntitySkeletonMinion} and {@link EntitySpiderMinion} use this to add potion effects to the target. */ - default void onSuccessfulAttack(EntityLivingBase target){}; - - // Delegates - - /** Implementors should call this from writeEntityToNBT. Can be overridden as long as super is called, but there's - * very little point in doing that since anything extra could just be added to writeEntityToNBT anyway. */ - default void writeNBTDelegate(NBTTagCompound tagcompound){ - if(this.getCaster() != null){ - tagcompound.setUniqueId("casterUUID", this.getCaster().getUniqueID()); - } - tagcompound.setInteger("lifetime", getLifetime()); - } - - /** Implementors should call this from readEntityFromNBT. Can be overridden as long as super is called, but there's - * very little point in doing that since anything extra could just be added to readEntityFromNBT anyway. */ - default void readNBTDelegate(NBTTagCompound tagcompound){ - this.setCasterUUID(tagcompound.getUniqueId("casterUUID")); - this.setLifetime(tagcompound.getInteger("lifetime")); - } - - /** Implementors should call this from setRevengeTarget, and call super.setRevengeTarget if and only if this method - * returns true. */ - default boolean shouldRevengeTarget(EntityLivingBase entity){ - // Allows the config to prevent minions from revenge-targeting their owners. - return entity != this.getCaster() || Wizardry.settings.minionRevengeTargeting; - } - - /** Implementors should call this from onUpdate. Can be overridden as long as super is called, but there's - * very little point in doing that since anything extra could just be added to onUpdate anyway. */ - default void updateDelegate(){ - - if(!(this instanceof Entity)) throw new ClassCastException("Implementations of ISummonedCreature must extend Entity!"); - - Entity thisEntity = ((Entity)this); - - if(this.getCaster() == null && this.getCasterUUID() != null){ - Entity entity = WizardryUtilities.getEntityByUUID(thisEntity.worldObj, getCasterUUID()); - if(entity instanceof EntityLivingBase){ - this.setCasterReference(new WeakReference((EntityLivingBase)entity)); - } - } - - if(thisEntity.ticksExisted == 1){ - this.onSpawn(); - } - - if(thisEntity.ticksExisted > this.getLifetime() && this.getLifetime() != -1){ - this.onDespawn(); - thisEntity.setDead(); - } - - if(this.hasParticleEffect() && thisEntity.worldObj.isRemote && thisEntity.worldObj.rand.nextInt(8) == 0) - Wizardry.proxy.spawnParticle(WizardryParticleType.DARK_MAGIC, thisEntity.worldObj, thisEntity.posX, thisEntity.posY + thisEntity.worldObj.rand.nextDouble()*1.5, thisEntity.posZ, 0.0d, 0.0d, 0.0d, 0, 0.1f, 0.0f, 0.0f); - - } - - /** Implementors should call this from processInteract, and call super.processInteract if and only if this method - * returns false. */ - default boolean interactDelegate(EntityPlayer player, EnumHand hand, ItemStack stack) { - - WizardData properties = WizardData.get(player); - // Selects one of the player's minions. - if(player.isSneaking() && stack != null && stack.getItem() instanceof ItemWand){ - - if(!player.worldObj.isRemote && properties != null && this.getCaster() == player){ - - if(properties.selectedMinion != null && properties.selectedMinion.get() == this){ - // Deselects the selected minion if right-clicked again - properties.selectedMinion = null; - }else{ - // Selects this minion - properties.selectedMinion = new WeakReference(this); - } - properties.sync(); - } - return true; - } - - return false; - } - - // Damage system - - @SubscribeEvent - public static void onLivingAttackEvent(LivingAttackEvent event){ - - // Rather than bother overriding entire attack methods in ISummonedCreature implementations, it's easier (and - // more robust) to use LivingAttackEvent to modify the damage source. - if(event.getSource().getEntity() instanceof ISummonedCreature){ - - EntityLivingBase summoner = ((ISummonedCreature)event.getSource().getEntity()).getCaster(); - - if(summoner != null){ - - event.setCanceled(true); - DamageSource newSource = event.getSource(); - // Copies over the original DamageType if appropriate. - DamageType type = event.getSource() instanceof IElementalDamage ? ((IElementalDamage)event.getSource()).getType() : DamageType.MAGIC; - // Copies over the original isRetaliatory flag if appropriate. - boolean isRetaliatory = event.getSource() instanceof IElementalDamage && ((IElementalDamage)event.getSource()).isRetaliatory(); - - // All summoned creatures are classified as magic, so it makes sense to do it this way. - if(event.getSource() instanceof EntityDamageSourceIndirect){ - newSource = new IndirectMinionDamage(event.getSource().damageType, event.getSource().getSourceOfDamage(), event.getSource().getEntity(), summoner, type, isRetaliatory); - }else if(event.getSource() instanceof EntityDamageSource){ - // Name is copied over so it uses the appropriate vanilla death message - newSource = new MinionDamage(event.getSource().damageType, event.getSource().getEntity(), summoner, type, isRetaliatory); - } - - // Copy over any relevant 'attributes' the original DamageSource might have had. - if(event.getSource().isExplosion()) newSource.setExplosion(); - if(event.getSource().isFireDamage()) newSource.setFireDamage(); - if(event.getSource().isProjectile()) newSource.setProjectile(); - - // For some reason Minecraft calculates knockback relative to DamageSource#getEntity. In vanilla this - // is unnoticeable, but it looks a bit weird with summoned creatures involved - so this fixes that. - if(WizardryUtilities.attackEntityWithoutKnockback(event.getEntity(), newSource, event.getAmount())){ - WizardryUtilities.applyStandardKnockback(event.getSource().getEntity(), event.getEntityLiving()); - ((ISummonedCreature)event.getSource().getEntity()).onSuccessfulAttack(event.getEntityLiving()); - } - - } - } - } - -} diff --git a/src/main/java/electroblob/wizardry/entity/projectile/EntityBomb.java b/src/main/java/electroblob/wizardry/entity/projectile/EntityBomb.java deleted file mode 100644 index f383ca21..00000000 --- a/src/main/java/electroblob/wizardry/entity/projectile/EntityBomb.java +++ /dev/null @@ -1,59 +0,0 @@ -package electroblob.wizardry.entity.projectile; - -import io.netty.buffer.ByteBuf; -import net.minecraft.entity.EntityLivingBase; -import net.minecraft.nbt.NBTTagCompound; -import net.minecraft.world.World; -import net.minecraftforge.fml.common.registry.IEntityAdditionalSpawnData; - -/** - * Same as {@link EntityMagicProjectile}, but with an additional blast multiplier field which is synced and saved to - * allow for the spread of particles to be changed depending on the blast area. - * @author Electroblob - * @since Wizardry 1.2 - */ -public abstract class EntityBomb extends EntityMagicProjectile implements IEntityAdditionalSpawnData { - - /** The entity blast multiplier. This is now synced and saved centrally from {@link EntityBomb}. */ - public float blastMultiplier = 1.0f; - - public EntityBomb(World world) { - super(world); - } - - public EntityBomb(World world, EntityLivingBase thrower) { - super(world, thrower); - } - - public EntityBomb(World world, EntityLivingBase thrower, float damageMultiplier, float blastMultiplier) { - super(world, thrower, damageMultiplier); - this.blastMultiplier = blastMultiplier; - } - - public EntityBomb(World par1World, double par2, double par4, double par6) { - super(par1World, par2, par4, par6); - } - - @Override - public void writeSpawnData(ByteBuf buffer) { - buffer.writeFloat(blastMultiplier); - } - - @Override - public void readSpawnData(ByteBuf buffer) { - blastMultiplier = buffer.readFloat(); - } - - @Override - public void readEntityFromNBT(NBTTagCompound nbttagcompound){ - super.readEntityFromNBT(nbttagcompound); - blastMultiplier = nbttagcompound.getFloat("blastMultiplier"); - } - - @Override - public void writeEntityToNBT(NBTTagCompound nbttagcompound){ - super.writeEntityToNBT(nbttagcompound); - nbttagcompound.setFloat("blastMultiplier", blastMultiplier); - } - -} diff --git a/src/main/java/electroblob/wizardry/entity/projectile/EntityDarknessOrb.java b/src/main/java/electroblob/wizardry/entity/projectile/EntityDarknessOrb.java index 1ba45a28..2c2947af 100644 --- a/src/main/java/electroblob/wizardry/entity/projectile/EntityDarknessOrb.java +++ b/src/main/java/electroblob/wizardry/entity/projectile/EntityDarknessOrb.java @@ -1,15 +1,16 @@ package electroblob.wizardry.entity.projectile; +import electroblob.wizardry.EnumParticleType; +import electroblob.wizardry.MagicDamage; import electroblob.wizardry.Wizardry; -import electroblob.wizardry.util.MagicDamage; -import electroblob.wizardry.util.MagicDamage.DamageType; -import electroblob.wizardry.util.WizardryParticleType; +import electroblob.wizardry.MagicDamage.DamageType; import net.minecraft.entity.Entity; import net.minecraft.entity.EntityLivingBase; -import net.minecraft.init.MobEffects; -import net.minecraft.init.SoundEvents; +import net.minecraft.entity.boss.EntityWither; +import net.minecraft.entity.monster.EntitySkeleton; +import net.minecraft.potion.Potion; import net.minecraft.potion.PotionEffect; -import net.minecraft.util.math.RayTraceResult; +import net.minecraft.util.MovingObjectPosition; import net.minecraft.world.World; public class EntityDarknessOrb extends EntityMagicProjectile @@ -34,25 +35,28 @@ public class EntityDarknessOrb extends EntityMagicProjectile super(par1World, par2, par4, par6); } - @Override - protected float getSpeed(){ + /** This is the speed */ + protected float func_70182_d() + { return 0.5F; } - @Override - protected void onImpact(RayTraceResult RayTraceResult) + /** + * Called when this EntityThrowable hits a block or entity. + */ + protected void onImpact(MovingObjectPosition movingobjectposition) { - Entity target = RayTraceResult.entityHit; + Entity target = movingobjectposition.entityHit; if (target != null && !MagicDamage.isEntityImmune(DamageType.WITHER, target)) { float damage = 8 * damageMultiplier; - target.attackEntityFrom(MagicDamage.causeIndirectMagicDamage(this, this.getThrower(), DamageType.WITHER).setProjectile(), damage); + target.attackEntityFrom(MagicDamage.causeIndirectEntityMagicDamage(this, this.getThrower(), DamageType.WITHER).setProjectile(), damage); - if(target instanceof EntityLivingBase && !MagicDamage.isEntityImmune(DamageType.WITHER, target)) ((EntityLivingBase)target).addPotionEffect(new PotionEffect(MobEffects.WITHER, 150, 1)); + if(target instanceof EntityLivingBase && !MagicDamage.isEntityImmune(DamageType.WITHER, target)) ((EntityLivingBase)target).addPotionEffect(new PotionEffect(Potion.wither.id, 150, 1)); - this.playSound(SoundEvents.ENTITY_WITHER_HURT, 1.0F, 1.2F / (this.rand.nextFloat() * 0.2F + 0.9F)); + this.playSound("mob.wither.hurt", 1.0F, 1.2F / (this.rand.nextFloat() * 0.2F + 0.9F)); } this.setDead(); @@ -64,8 +68,8 @@ public class EntityDarknessOrb extends EntityMagicProjectile if(worldObj.isRemote){ float brightness = rand.nextFloat()*0.2f; - Wizardry.proxy.spawnParticle(WizardryParticleType.SPARKLE, worldObj, this.posX + (this.rand.nextDouble() - 0.5D) * (double)this.width, this.posY + this.rand.nextDouble() * (double)this.height, this.posZ + (this.rand.nextDouble() - 0.5D) * (double)this.width, 0, 0, 0, 20 + rand.nextInt(10), brightness, 0.0f, brightness); - Wizardry.proxy.spawnParticle(WizardryParticleType.DARK_MAGIC, worldObj, this.posX + (this.rand.nextDouble() - 0.5D) * (double)this.width, this.posY + this.rand.nextDouble() * (double)this.height, this.posZ + (this.rand.nextDouble() - 0.5D) * (double)this.width, 0.0d, 0.0d, 0.0d, 0, 0.1f, 0.0f, 0.0f); + Wizardry.proxy.spawnParticle(EnumParticleType.SPARKLE, worldObj, this.posX + (this.rand.nextDouble() - 0.5D) * (double)this.width, this.posY + this.rand.nextDouble() * (double)this.height, this.posZ + (this.rand.nextDouble() - 0.5D) * (double)this.width, 0, 0, 0, 20 + rand.nextInt(10), brightness, 0.0f, brightness); + Wizardry.proxy.spawnParticle(EnumParticleType.DARK_MAGIC, worldObj, this.posX + (this.rand.nextDouble() - 0.5D) * (double)this.width, this.posY + this.rand.nextDouble() * (double)this.height, this.posZ + (this.rand.nextDouble() - 0.5D) * (double)this.width, 0.0d, 0.0d, 0.0d, 0, 0.1f, 0.0f, 0.0f); } if(this.ticksExisted > 150){ diff --git a/src/main/java/electroblob/wizardry/entity/projectile/EntityDart.java b/src/main/java/electroblob/wizardry/entity/projectile/EntityDart.java index 3625f995..5dfb24b4 100644 --- a/src/main/java/electroblob/wizardry/entity/projectile/EntityDart.java +++ b/src/main/java/electroblob/wizardry/entity/projectile/EntityDart.java @@ -1,12 +1,11 @@ package electroblob.wizardry.entity.projectile; +import electroblob.wizardry.EnumParticleType; import electroblob.wizardry.Wizardry; -import electroblob.wizardry.util.MagicDamage.DamageType; -import electroblob.wizardry.util.WizardryParticleType; +import electroblob.wizardry.MagicDamage.DamageType; import net.minecraft.entity.Entity; import net.minecraft.entity.EntityLivingBase; -import net.minecraft.init.MobEffects; -import net.minecraft.init.SoundEvents; +import net.minecraft.potion.Potion; import net.minecraft.potion.PotionEffect; import net.minecraft.world.World; @@ -42,20 +41,20 @@ public class EntityDart extends EntityMagicArrow @Override public void onEntityHit(EntityLivingBase entityHit){ //Adds a weakness effect to the target. - entityHit.addPotionEffect(new PotionEffect(MobEffects.WEAKNESS, 200, 1, false, false)); - this.playSound(SoundEvents.ENTITY_GENERIC_HURT, 1.0F, 1.2F / (this.rand.nextFloat() * 0.2F + 0.9F)); + entityHit.addPotionEffect(new PotionEffect(Potion.weakness.id, 200, 1, true)); + this.playSound("game.neutral.hurt", 1.0F, 1.2F / (this.rand.nextFloat() * 0.2F + 0.9F)); } @Override public void onBlockHit(){ - this.playSound(SoundEvents.ENTITY_ARROW_HIT, 1.0F, 1.2F / (this.rand.nextFloat() * 0.2F + 0.9F)); + this.playSound("random.bowhit", 1.0F, 1.2F / (this.rand.nextFloat() * 0.2F + 0.9F)); } @Override public void tickInAir(){ if(this.worldObj.isRemote){ - Wizardry.proxy.spawnParticle(WizardryParticleType.LEAF, worldObj, this.posX, this.posY, this.posZ, 0, -0.03, 0, 10 + rand.nextInt(5)); + Wizardry.proxy.spawnParticle(EnumParticleType.LEAF, worldObj, this.posX, this.posY, this.posZ, 0, -0.03, 0, 10 + rand.nextInt(5)); } } diff --git a/src/main/java/electroblob/wizardry/entity/projectile/EntityFirebolt.java b/src/main/java/electroblob/wizardry/entity/projectile/EntityFirebolt.java index ed8c5c0f..1f87ae6e 100644 --- a/src/main/java/electroblob/wizardry/entity/projectile/EntityFirebolt.java +++ b/src/main/java/electroblob/wizardry/entity/projectile/EntityFirebolt.java @@ -1,12 +1,10 @@ package electroblob.wizardry.entity.projectile; -import electroblob.wizardry.util.MagicDamage; -import electroblob.wizardry.util.MagicDamage.DamageType; +import electroblob.wizardry.MagicDamage; +import electroblob.wizardry.MagicDamage.DamageType; import net.minecraft.entity.Entity; import net.minecraft.entity.EntityLivingBase; -import net.minecraft.init.SoundEvents; -import net.minecraft.util.EnumParticleTypes; -import net.minecraft.util.math.RayTraceResult; +import net.minecraft.util.MovingObjectPosition; import net.minecraft.world.World; public class EntityFirebolt extends EntityMagicProjectile @@ -30,27 +28,38 @@ public class EntityFirebolt extends EntityMagicProjectile { super(par1World, par2, par4, par6); } - + + /** This is the speed */ @Override - protected void onImpact(RayTraceResult rayTrace) + protected float func_70182_d() { - Entity entityHit = rayTrace.entityHit; + return 2.5F; + } + + /** + * Called when this EntityThrowable hits a block or entity. + */ + @Override + protected void onImpact(MovingObjectPosition par1MovingObjectPosition) + { + Entity entityHit = par1MovingObjectPosition.entityHit; if (entityHit != null) { float damage = 5 * damageMultiplier; - entityHit.attackEntityFrom(MagicDamage.causeIndirectMagicDamage(this, this.getThrower(), DamageType.FIRE).setProjectile(), damage); + entityHit.attackEntityFrom(MagicDamage.causeIndirectEntityMagicDamage(this, this.getThrower(), DamageType.FIRE).setProjectile(), damage); if(!MagicDamage.isEntityImmune(DamageType.FIRE, entityHit)) entityHit.setFire(5); } - this.playSound(SoundEvents.BLOCK_LAVA_POP, 2, 0.8f + rand.nextFloat()*0.3f); + this.playSound("liquid.lavapop", 2, 0.8f + rand.nextFloat()*0.3f); // Particle effect if(worldObj.isRemote){ for(int i=0;i<8;i++){ - worldObj.spawnParticle(EnumParticleTypes.LAVA, this.posX + rand.nextFloat() - 0.5, this.posY + this.height/2 + rand.nextFloat() - 0.5, this.posZ + rand.nextFloat() - 0.5, 0, 0, 0); + //Minecraft.getMinecraft().effectRenderer.addEffect(new EntitySparkFX(worldObj, this.posX + rand.nextFloat() - 0.5, this.posY + this.height/2 + rand.nextFloat() - 0.5, this.posZ + rand.nextFloat() - 0.5, 0, 0, 0)); + worldObj.spawnParticle("lava", this.posX + rand.nextFloat() - 0.5, this.posY + this.height/2 + rand.nextFloat() - 0.5, this.posZ + rand.nextFloat() - 0.5, 0, 0, 0); } } @@ -64,7 +73,7 @@ public class EntityFirebolt extends EntityMagicProjectile if(worldObj.isRemote){ for(int i=0; i<4; i++){ - worldObj.spawnParticle(EnumParticleTypes.FLAME, this.posX + rand.nextFloat()*0.2 - 0.1, this.posY + this.height/2 + rand.nextFloat()*0.2 - 0.1, this.posZ + rand.nextFloat()*0.2 - 0.1, 0, 0, 0); + worldObj.spawnParticle("flame", this.posX + rand.nextFloat()*0.2 - 0.1, this.posY + this.height/2 + rand.nextFloat()*0.2 - 0.1, this.posZ + rand.nextFloat()*0.2 - 0.1, 0, 0, 0); } } diff --git a/src/main/java/electroblob/wizardry/entity/projectile/EntityFirebomb.java b/src/main/java/electroblob/wizardry/entity/projectile/EntityFirebomb.java index 637fe5e8..0270bd17 100644 --- a/src/main/java/electroblob/wizardry/entity/projectile/EntityFirebomb.java +++ b/src/main/java/electroblob/wizardry/entity/projectile/EntityFirebomb.java @@ -2,19 +2,24 @@ package electroblob.wizardry.entity.projectile; import java.util.List; +import cpw.mods.fml.common.registry.IEntityAdditionalSpawnData; +import electroblob.wizardry.EnumParticleType; +import electroblob.wizardry.MagicDamage; import electroblob.wizardry.Wizardry; -import electroblob.wizardry.util.MagicDamage; -import electroblob.wizardry.util.MagicDamage.DamageType; -import electroblob.wizardry.util.WizardryParticleType; -import electroblob.wizardry.util.WizardryUtilities; +import electroblob.wizardry.WizardryUtilities; +import electroblob.wizardry.MagicDamage.DamageType; +import io.netty.buffer.ByteBuf; import net.minecraft.entity.Entity; import net.minecraft.entity.EntityLivingBase; -import net.minecraft.init.SoundEvents; -import net.minecraft.util.EnumParticleTypes; -import net.minecraft.util.math.RayTraceResult; +import net.minecraft.nbt.NBTTagCompound; +import net.minecraft.util.DamageSource; +import net.minecraft.util.MovingObjectPosition; import net.minecraft.world.World; -public class EntityFirebomb extends EntityBomb { +public class EntityFirebomb extends EntityMagicProjectile implements IEntityAdditionalSpawnData +{ + /** The entity blast multiplier. Only some projectiles cause a blast, which is why this isn't in EntityMagicProjectile. */ + public float blastMultiplier = 1.0f; public EntityFirebomb(World par1World) { @@ -28,7 +33,8 @@ public class EntityFirebomb extends EntityBomb { public EntityFirebomb(World par1World, EntityLivingBase par2EntityLivingBase, float damageMultiplier, float blastMultiplier) { - super(par1World, par2EntityLivingBase, damageMultiplier, blastMultiplier); + super(par1World, par2EntityLivingBase, damageMultiplier); + this.blastMultiplier = blastMultiplier; } public EntityFirebomb(World par1World, double par2, double par4, double par6) @@ -39,34 +45,34 @@ public class EntityFirebomb extends EntityBomb { /** * Called when this EntityThrowable hits a block or entity. */ - protected void onImpact(RayTraceResult par1RayTraceResult) + protected void onImpact(MovingObjectPosition par1MovingObjectPosition) { - Entity entityHit = par1RayTraceResult.entityHit; + Entity entityHit = par1MovingObjectPosition.entityHit; if (entityHit != null) { // This is if the firebomb gets a direct hit float damage = 5 * damageMultiplier; - entityHit.attackEntityFrom(MagicDamage.causeIndirectMagicDamage(this, this.getThrower(), DamageType.FIRE).setProjectile(), damage); + entityHit.attackEntityFrom(MagicDamage.causeIndirectEntityMagicDamage(this, this.getThrower(), DamageType.FIRE).setProjectile(), damage); if(!MagicDamage.isEntityImmune(DamageType.FIRE, entityHit)) entityHit.setFire(10); } // Particle effect if(worldObj.isRemote){ - this.worldObj.spawnParticle(EnumParticleTypes.EXPLOSION_LARGE, this.posX, this.posY, this.posZ, 0, 0, 0); + this.worldObj.spawnParticle("largeexplode", this.posX, this.posY, this.posZ, 0, 0, 0); for(int i=0;i<60*blastMultiplier;i++){ - //this.worldObj.spawnParticle(EnumParticleTypes.FLAME, this.posX + (this.rand.nextDouble()*4 - 2)*blastMultiplier, this.posY + (this.rand.nextDouble()*4 - 2)*blastMultiplier, this.posZ + (this.rand.nextDouble()*4 - 2)*blastMultiplier, 0, 0, 0); - Wizardry.proxy.spawnParticle(WizardryParticleType.MAGIC_FIRE, worldObj, this.posX + (this.rand.nextDouble()*4 - 2)*blastMultiplier, this.posY + (this.rand.nextDouble()*4 - 2)*blastMultiplier, this.posZ + (this.rand.nextDouble()*4 - 2)*blastMultiplier, 0, 0, 0, 15 + rand.nextInt(5), 2 + rand.nextFloat(), 0, 0); - Wizardry.proxy.spawnParticle(WizardryParticleType.DARK_MAGIC, worldObj, this.posX + (this.rand.nextDouble()*4 - 2)*blastMultiplier, this.posY + (this.rand.nextDouble()*4 - 2)*blastMultiplier, this.posZ + (this.rand.nextDouble()*4 - 2)*blastMultiplier, 0.0d, 0.0d, 0.0d, 0, 1.0f, 0.2f + rand.nextFloat()*0.4f, 0.0f); + //this.worldObj.spawnParticle("flame", this.posX + (this.rand.nextDouble()*4 - 2)*blastMultiplier, this.posY + (this.rand.nextDouble()*4 - 2)*blastMultiplier, this.posZ + (this.rand.nextDouble()*4 - 2)*blastMultiplier, 0, 0, 0); + Wizardry.proxy.spawnParticle(EnumParticleType.MAGIC_FIRE, worldObj, this.posX + (this.rand.nextDouble()*4 - 2)*blastMultiplier, this.posY + (this.rand.nextDouble()*4 - 2)*blastMultiplier, this.posZ + (this.rand.nextDouble()*4 - 2)*blastMultiplier, 0, 0, 0, 15 + rand.nextInt(5), 2 + rand.nextFloat(), 0, 0); + Wizardry.proxy.spawnParticle(EnumParticleType.DARK_MAGIC, worldObj, this.posX + (this.rand.nextDouble()*4 - 2)*blastMultiplier, this.posY + (this.rand.nextDouble()*4 - 2)*blastMultiplier, this.posZ + (this.rand.nextDouble()*4 - 2)*blastMultiplier, 0.0d, 0.0d, 0.0d, 0, 1.0f, 0.2f + rand.nextFloat()*0.4f, 0.0f); } } if(!this.worldObj.isRemote){ - this.playSound(SoundEvents.ENTITY_SPLASH_POTION_BREAK, 1.5F, rand.nextFloat() * 0.4F + 0.6F); - this.playSound(SoundEvents.ENTITY_BLAZE_SHOOT, 1, 1); + this.worldObj.playSoundEffect(this.posX, this.posY, this.posZ, "game.potion.smash", 1.5F, rand.nextFloat() * 0.4F + 0.6F); + if(!worldObj.isRemote) worldObj.playAuxSFX(1009, (int)this.posX, (int)this.posY, (int)this.posZ, 0); double range = 3.0d*blastMultiplier; @@ -75,7 +81,7 @@ public class EntityFirebomb extends EntityBomb { for(EntityLivingBase target : targets){ if(target != entityHit && target != this.getThrower() && !MagicDamage.isEntityImmune(DamageType.FIRE, target)){ // Splash damage does not count as projectile damage - target.attackEntityFrom(MagicDamage.causeIndirectMagicDamage(this, this.getThrower(), DamageType.FIRE), 4.0f * damageMultiplier); + target.attackEntityFrom(MagicDamage.causeIndirectEntityMagicDamage(this, this.getThrower(), DamageType.FIRE), 4.0f * damageMultiplier); target.setFire(7); } } @@ -83,5 +89,26 @@ public class EntityFirebomb extends EntityBomb { this.setDead(); } } - + + @Override + public void writeSpawnData(ByteBuf buffer) { + buffer.writeFloat(blastMultiplier); + } + + @Override + public void readSpawnData(ByteBuf buffer) { + blastMultiplier = buffer.readFloat(); + } + + @Override + public void readEntityFromNBT(NBTTagCompound nbttagcompound){ + super.readEntityFromNBT(nbttagcompound); + blastMultiplier = nbttagcompound.getFloat("blastMultiplier"); + } + + @Override + public void writeEntityToNBT(NBTTagCompound nbttagcompound){ + super.writeEntityToNBT(nbttagcompound); + nbttagcompound.setFloat("blastMultiplier", blastMultiplier); + } } diff --git a/src/main/java/electroblob/wizardry/entity/projectile/EntityForceArrow.java b/src/main/java/electroblob/wizardry/entity/projectile/EntityForceArrow.java index 0cf2ec6d..aba9a5a5 100644 --- a/src/main/java/electroblob/wizardry/entity/projectile/EntityForceArrow.java +++ b/src/main/java/electroblob/wizardry/entity/projectile/EntityForceArrow.java @@ -1,9 +1,8 @@ package electroblob.wizardry.entity.projectile; -import electroblob.wizardry.util.MagicDamage.DamageType; +import electroblob.wizardry.MagicDamage.DamageType; import net.minecraft.entity.Entity; import net.minecraft.entity.EntityLivingBase; -import net.minecraft.init.SoundEvents; import net.minecraft.world.World; public class EntityForceArrow extends EntityMagicArrow { @@ -37,7 +36,7 @@ public class EntityForceArrow extends EntityMagicArrow { @Override public void onEntityHit(EntityLivingBase entityHit){ - this.playSound(SoundEvents.ENTITY_FIREWORK_BLAST, 1.0F, 1.0F); + this.playSound("fireworks.blast", 1.0F, 1.0F); } @Override @@ -47,7 +46,7 @@ public class EntityForceArrow extends EntityMagicArrow { @Override public void onBlockHit(){ - this.playSound(SoundEvents.ENTITY_FIREWORK_BLAST, 1.0F, 1.0F); + this.playSound("fireworks.blast", 1.0F, 1.0F); } @Override diff --git a/src/main/java/electroblob/wizardry/entity/projectile/EntityForceOrb.java b/src/main/java/electroblob/wizardry/entity/projectile/EntityForceOrb.java index 9a4eaa7e..f842abfc 100644 --- a/src/main/java/electroblob/wizardry/entity/projectile/EntityForceOrb.java +++ b/src/main/java/electroblob/wizardry/entity/projectile/EntityForceOrb.java @@ -2,22 +2,21 @@ package electroblob.wizardry.entity.projectile; import java.util.List; +import electroblob.wizardry.EnumParticleType; +import electroblob.wizardry.MagicDamage; import electroblob.wizardry.Wizardry; -import electroblob.wizardry.util.MagicDamage; -import electroblob.wizardry.util.MagicDamage.DamageType; -import electroblob.wizardry.util.WizardryParticleType; -import electroblob.wizardry.util.WizardryUtilities; +import electroblob.wizardry.WizardryUtilities; +import electroblob.wizardry.MagicDamage.DamageType; +import electroblob.wizardry.client.particle.EntitySparkleFX; +import net.minecraft.entity.Entity; import net.minecraft.entity.EntityLivingBase; -import net.minecraft.init.SoundEvents; import net.minecraft.nbt.NBTTagCompound; -import net.minecraft.util.EnumParticleTypes; -import net.minecraft.util.math.RayTraceResult; +import net.minecraft.util.MovingObjectPosition; import net.minecraft.world.World; public class EntityForceOrb extends EntityMagicProjectile { - /** The entity blast multiplier. In this particular case, it doesn't need syncing, so this class doesn't extend - * EntityBlastProjectile. */ + /** The entity blast multiplier. Only some projectiles cause a blast, which is why this isn't in EntityMagicProjectile. */ public float blastMultiplier; public EntityForceOrb(World par1World) @@ -44,32 +43,32 @@ public class EntityForceOrb extends EntityMagicProjectile { /** * Called when this EntityThrowable hits a block or entity. */ - protected void onImpact(RayTraceResult par1RayTraceResult){ + protected void onImpact(MovingObjectPosition par1MovingObjectPosition){ - if (par1RayTraceResult.entityHit != null) + if (par1MovingObjectPosition.entityHit != null) { // This is if the force orb gets a direct hit - this.playSound(SoundEvents.ENTITY_GENERIC_HURT, 1.0F, 1.2F / (this.rand.nextFloat() * 0.2F + 0.9F)); + this.playSound("game.neutral.hurt", 1.0F, 1.2F / (this.rand.nextFloat() * 0.2F + 0.9F)); } // Particle effect if(this.worldObj.isRemote){ + this.worldObj.spawnParticle("largeexplode", this.posX, this.posY, this.posZ, 0, 0, 0); for(int j=0; j<20; j++){ float brightness = 0.5f + (rand.nextFloat()/2); double x = this.posX - 0.25d + (rand.nextDouble()/2); double y = this.posY - 0.25d + (rand.nextDouble()/2); double z = this.posZ - 0.25d + (rand.nextDouble()/2); - Wizardry.proxy.spawnParticle(WizardryParticleType.SPARKLE, worldObj, x, y, z, (x - this.posX)*2, (y - this.posY)*2, (z - this.posZ)*2, 6, brightness, 1.0f, brightness + 0.2f); + Wizardry.proxy.spawnParticle(EnumParticleType.SPARKLE, worldObj, x, y, z, (x - this.posX)*2, (y - this.posY)*2, (z - this.posZ)*2, 6, brightness, 1.0f, brightness + 0.2f); } - this.worldObj.spawnParticle(EnumParticleTypes.EXPLOSION_LARGE, this.posX, this.posY, this.posZ, 0, 0, 0); } if(!this.worldObj.isRemote){ // 2 gives a cool flanging effect! float pitch = this.rand.nextFloat() * 0.2F + 0.3F; - this.playSound(SoundEvents.ENTITY_FIREWORK_BLAST, 1.5F, pitch); - this.playSound(SoundEvents.ENTITY_FIREWORK_BLAST, 1.5F, pitch - 0.01f); + this.playSound("fireworks.blast", 1.5F, pitch); + this.playSound("fireworks.blast", 1.5F, pitch - 0.01f); double blastRadius = 4.0d*blastMultiplier; @@ -78,14 +77,16 @@ public class EntityForceOrb extends EntityMagicProjectile { for(EntityLivingBase target : targets){ if(target != this.getThrower()){ + double velX = target.motionX; double velY = target.motionY; + double velZ = target.motionZ; double dx = this.posX-target.posX > 0? -0.5 - (this.posX-target.posX)/8 : 0.5 - (this.posX-target.posX)/8; double dz = this.posZ-target.posZ > 0? -0.5 - (this.posZ-target.posZ)/8 : 0.5 - (this.posZ-target.posZ)/8; float damage = 4*damageMultiplier; - target.attackEntityFrom(MagicDamage.causeIndirectMagicDamage(this, this.getThrower(), DamageType.BLAST), damage); + target.attackEntityFrom(MagicDamage.causeIndirectEntityMagicDamage(this, this.getThrower(), DamageType.BLAST), damage); target.motionX = dx; target.motionY = velY + 0.4; diff --git a/src/main/java/electroblob/wizardry/entity/projectile/EntityIceCharge.java b/src/main/java/electroblob/wizardry/entity/projectile/EntityIceCharge.java index ce5c9907..b9dc8c9e 100644 --- a/src/main/java/electroblob/wizardry/entity/projectile/EntityIceCharge.java +++ b/src/main/java/electroblob/wizardry/entity/projectile/EntityIceCharge.java @@ -2,24 +2,25 @@ package electroblob.wizardry.entity.projectile; import java.util.List; +import cpw.mods.fml.common.registry.IEntityAdditionalSpawnData; +import electroblob.wizardry.EnumParticleType; +import electroblob.wizardry.MagicDamage; import electroblob.wizardry.Wizardry; -import electroblob.wizardry.registry.WizardryPotions; -import electroblob.wizardry.registry.WizardrySounds; -import electroblob.wizardry.util.MagicDamage; -import electroblob.wizardry.util.MagicDamage.DamageType; -import electroblob.wizardry.util.WizardryParticleType; -import electroblob.wizardry.util.WizardryUtilities; +import electroblob.wizardry.WizardryUtilities; +import electroblob.wizardry.MagicDamage.DamageType; +import io.netty.buffer.ByteBuf; import net.minecraft.entity.Entity; import net.minecraft.entity.EntityLivingBase; import net.minecraft.init.Blocks; -import net.minecraft.init.SoundEvents; +import net.minecraft.nbt.NBTTagCompound; import net.minecraft.potion.PotionEffect; -import net.minecraft.util.EnumParticleTypes; -import net.minecraft.util.math.BlockPos; -import net.minecraft.util.math.RayTraceResult; +import net.minecraft.util.MovingObjectPosition; import net.minecraft.world.World; -public class EntityIceCharge extends EntityBomb { +public class EntityIceCharge extends EntityMagicProjectile implements IEntityAdditionalSpawnData +{ + /** The entity blast multiplier. Only some projectiles cause a blast, which is why this isn't in EntityMagicProjectile. */ + public float blastMultiplier; public EntityIceCharge(World par1World) { @@ -33,7 +34,8 @@ public class EntityIceCharge extends EntityBomb { public EntityIceCharge(World par1World, EntityLivingBase par2EntityLivingBase, float damageMultiplier, float blastMultiplier) { - super(par1World, par2EntityLivingBase, damageMultiplier, blastMultiplier); + super(par1World, par2EntityLivingBase, damageMultiplier); + this.blastMultiplier = blastMultiplier; } public EntityIceCharge(World par1World, double par2, double par4, double par6) @@ -44,35 +46,35 @@ public class EntityIceCharge extends EntityBomb { /** * Called when this EntityThrowable hits a block or entity. */ - protected void onImpact(RayTraceResult par1RayTraceResult) + protected void onImpact(MovingObjectPosition par1MovingObjectPosition) { - Entity entityHit = par1RayTraceResult.entityHit; + Entity entityHit = par1MovingObjectPosition.entityHit; if (entityHit != null) { // This is if the ice charge gets a direct hit float damage = 4 * damageMultiplier; - entityHit.attackEntityFrom(MagicDamage.causeIndirectMagicDamage(this, this.getThrower(), DamageType.FROST).setProjectile(), damage); + entityHit.attackEntityFrom(MagicDamage.causeIndirectEntityMagicDamage(this, this.getThrower(), DamageType.FROST).setProjectile(), damage); if(entityHit instanceof EntityLivingBase && !MagicDamage.isEntityImmune(DamageType.FROST, entityHit)) - ((EntityLivingBase)entityHit).addPotionEffect(new PotionEffect(WizardryPotions.frost, 120, 1)); + ((EntityLivingBase)entityHit).addPotionEffect(new PotionEffect(Wizardry.frost.id, 120, 1)); } // Particle effect if(worldObj.isRemote){ - this.worldObj.spawnParticle(EnumParticleTypes.EXPLOSION_LARGE, this.posX, this.posY, this.posZ, 0, 0, 0); + this.worldObj.spawnParticle("largeexplode", this.posX, this.posY, this.posZ, 0, 0, 0); for(int i=0;i<30*blastMultiplier;i++){ float brightness = 0.4f + rand.nextFloat()*0.5f; - Wizardry.proxy.spawnParticle(WizardryParticleType.ICE, worldObj, this.posX + (this.rand.nextDouble()*4 - 2)*blastMultiplier, this.posY + (this.rand.nextDouble()*4 - 2)*blastMultiplier, this.posZ + (this.rand.nextDouble()*4 - 2)*blastMultiplier, 0.0d, 0.0d, 0.0d, 35); - Wizardry.proxy.spawnParticle(WizardryParticleType.DARK_MAGIC, worldObj, this.posX + (this.rand.nextDouble()*4 - 2)*blastMultiplier, this.posY + (this.rand.nextDouble()*4 - 2)*blastMultiplier, this.posZ + (this.rand.nextDouble()*4 - 2)*blastMultiplier, 0.0d, 0.0d, 0.0d, 0, brightness, brightness+0.1f, 1.0f); + Wizardry.proxy.spawnParticle(EnumParticleType.ICE, worldObj, this.posX + (this.rand.nextDouble()*4 - 2)*blastMultiplier, this.posY + (this.rand.nextDouble()*4 - 2)*blastMultiplier, this.posZ + (this.rand.nextDouble()*4 - 2)*blastMultiplier, 0.0d, 0.0d, 0.0d, 35); + Wizardry.proxy.spawnParticle(EnumParticleType.DARK_MAGIC, worldObj, this.posX + (this.rand.nextDouble()*4 - 2)*blastMultiplier, this.posY + (this.rand.nextDouble()*4 - 2)*blastMultiplier, this.posZ + (this.rand.nextDouble()*4 - 2)*blastMultiplier, 0.0d, 0.0d, 0.0d, 0, brightness, brightness+0.1f, 1.0f); } } if(!this.worldObj.isRemote){ - this.playSound(SoundEvents.ENTITY_SPLASH_POTION_BREAK, 1.5f, rand.nextFloat() * 0.4f + 0.6f); - this.playSound(WizardrySounds.SPELL_ICE, 1.2f, rand.nextFloat() * 0.4f + 1.2f); + this.worldObj.playSoundEffect(this.posX, this.posY, this.posZ, "random.glass", 1.5f, rand.nextFloat() * 0.4f + 0.6f); + this.worldObj.playSoundEffect(this.posX, this.posY, this.posZ, "wizardry:ice", 1.2f, rand.nextFloat() * 0.4f + 1.2f); double radius = 3.0d*blastMultiplier; @@ -81,30 +83,21 @@ public class EntityIceCharge extends EntityBomb { // Slows targets for(EntityLivingBase target : targets){ if(target != entityHit && target != this.getThrower()){ - if(!MagicDamage.isEntityImmune(DamageType.FROST, target)) target.addPotionEffect(new PotionEffect(WizardryPotions.frost, 100, 0)); + if(!MagicDamage.isEntityImmune(DamageType.FROST, target)) target.addPotionEffect(new PotionEffect(Wizardry.frost.id, 100, 0, true)); } } // Places snow and ice on ground. for(int i=-1; i<2; i++){ for(int j=-1; j<2; j++){ - - BlockPos pos = new BlockPos(this.posX + i, this.posY, this.posZ + j); - - int y = WizardryUtilities.getNearestFloorLevelB(worldObj, pos, 7); - - pos = new BlockPos(pos.getX(), y, pos.getZ()); - - double dist = this.getDistance(pos.getX(), pos.getY(), pos.getZ()); - + int y = WizardryUtilities.getNearestFloorLevelB(worldObj, (int)this.posX + i, (int)this.posY, (int)this.posZ + j, 7); + double dist = this.getDistance((int)this.posX + i, y, (int)this.posZ + j); // Randomised with weighting so that the nearer the block the more likely it is to be snowed. if(y != -1 && rand.nextInt((int)dist*2 + 1) < 1 && dist < 2){ - if(worldObj.getBlockState(pos.down()).getBlock() == Blocks.WATER){ - worldObj.setBlockState(pos.down(), Blocks.ICE.getDefaultState()); + if(worldObj.getBlock((int)this.posX + i, y-1, (int)this.posZ + j) == Blocks.water){ + worldObj.setBlock((int)this.posX + i, y-1, (int)this.posZ + j, Blocks.ice); }else{ - // Don't need to check whether the block at pos can be replaced since getNearestFloorLevelB - // only ever returns floors with air above them. - worldObj.setBlockState(pos, Blocks.SNOW_LAYER.getDefaultState()); + worldObj.setBlock((int)this.posX + i, y, (int)this.posZ + j, Blocks.snow_layer); } } } @@ -132,4 +125,26 @@ public class EntityIceCharge extends EntityBomb { public boolean canRenderOnFire() { return false; } + + @Override + public void writeSpawnData(ByteBuf buffer) { + buffer.writeFloat(blastMultiplier); + } + + @Override + public void readSpawnData(ByteBuf buffer) { + blastMultiplier = buffer.readFloat(); + } + + @Override + public void readEntityFromNBT(NBTTagCompound nbttagcompound){ + super.readEntityFromNBT(nbttagcompound); + blastMultiplier = nbttagcompound.getFloat("blastMultiplier"); + } + + @Override + public void writeEntityToNBT(NBTTagCompound nbttagcompound){ + super.writeEntityToNBT(nbttagcompound); + nbttagcompound.setFloat("blastMultiplier", blastMultiplier); + } } diff --git a/src/main/java/electroblob/wizardry/entity/projectile/EntityIceLance.java b/src/main/java/electroblob/wizardry/entity/projectile/EntityIceLance.java index ca4dd674..5a61f4de 100644 --- a/src/main/java/electroblob/wizardry/entity/projectile/EntityIceLance.java +++ b/src/main/java/electroblob/wizardry/entity/projectile/EntityIceLance.java @@ -1,13 +1,11 @@ package electroblob.wizardry.entity.projectile; +import electroblob.wizardry.EnumParticleType; +import electroblob.wizardry.MagicDamage; import electroblob.wizardry.Wizardry; -import electroblob.wizardry.registry.WizardryPotions; -import electroblob.wizardry.util.MagicDamage; -import electroblob.wizardry.util.MagicDamage.DamageType; -import electroblob.wizardry.util.WizardryParticleType; +import electroblob.wizardry.MagicDamage.DamageType; import net.minecraft.entity.Entity; import net.minecraft.entity.EntityLivingBase; -import net.minecraft.init.SoundEvents; import net.minecraft.potion.PotionEffect; import net.minecraft.world.World; @@ -48,9 +46,9 @@ public class EntityIceLance extends EntityMagicArrow { public void onEntityHit(EntityLivingBase entityHit){ // Adds a freeze effect to the target. - if(!MagicDamage.isEntityImmune(DamageType.FROST, entityHit)) entityHit.addPotionEffect(new PotionEffect(WizardryPotions.frost, 300, 0)); + if(!MagicDamage.isEntityImmune(DamageType.FROST, entityHit)) entityHit.addPotionEffect(new PotionEffect(Wizardry.frost.id, 300, 0, true)); - this.playSound(SoundEvents.ENTITY_GENERIC_HURT, 1.0F, 1.2F / (this.rand.nextFloat() * 0.2F + 0.9F)); + this.playSound("game.neutral.hurt", 1.0F, 1.2F / (this.rand.nextFloat() * 0.2F + 0.9F)); } @Override @@ -66,11 +64,11 @@ public class EntityIceLance extends EntityMagicArrow { double x = this.posX - 0.25d + (rand.nextDouble()/2); double y = this.posY - 0.25d + (rand.nextDouble()/2); double z = this.posZ - 0.25d + (rand.nextDouble()/2); - Wizardry.proxy.spawnParticle(WizardryParticleType.ICE, worldObj, x, y, z, x - this.posX, y - this.posY, z - this.posZ, 20 + rand.nextInt(10)); + Wizardry.proxy.spawnParticle(EnumParticleType.ICE, worldObj, x, y, z, x - this.posX, y - this.posY, z - this.posZ, 20 + rand.nextInt(10)); } } // Parameters for sound: sound event name, volume, pitch. - this.playSound(SoundEvents.ENTITY_SPLASH_POTION_BREAK, 1.0F, rand.nextFloat() * 0.4F + 1.2F); + this.playSound("game.potion.smash", 1.0F, rand.nextFloat() * 0.4F + 1.2F); } diff --git a/src/main/java/electroblob/wizardry/entity/projectile/EntityIceShard.java b/src/main/java/electroblob/wizardry/entity/projectile/EntityIceShard.java index 0805c4e0..1d8d9b74 100644 --- a/src/main/java/electroblob/wizardry/entity/projectile/EntityIceShard.java +++ b/src/main/java/electroblob/wizardry/entity/projectile/EntityIceShard.java @@ -1,13 +1,11 @@ package electroblob.wizardry.entity.projectile; +import electroblob.wizardry.EnumParticleType; +import electroblob.wizardry.MagicDamage; import electroblob.wizardry.Wizardry; -import electroblob.wizardry.registry.WizardryPotions; -import electroblob.wizardry.util.MagicDamage; -import electroblob.wizardry.util.MagicDamage.DamageType; -import electroblob.wizardry.util.WizardryParticleType; +import electroblob.wizardry.MagicDamage.DamageType; import net.minecraft.entity.Entity; import net.minecraft.entity.EntityLivingBase; -import net.minecraft.init.SoundEvents; import net.minecraft.potion.PotionEffect; import net.minecraft.world.World; @@ -44,9 +42,9 @@ public class EntityIceShard extends EntityMagicArrow { public void onEntityHit(EntityLivingBase entityHit){ // Adds a freeze effect to the target. - if(!MagicDamage.isEntityImmune(DamageType.FROST, entityHit)) entityHit.addPotionEffect(new PotionEffect(WizardryPotions.frost, 200, 0)); + if(!MagicDamage.isEntityImmune(DamageType.FROST, entityHit)) entityHit.addPotionEffect(new PotionEffect(Wizardry.frost.id, 200, 0, true)); - this.playSound(SoundEvents.ENTITY_GENERIC_HURT, 1.0F, 1.2F / (this.rand.nextFloat() * 0.2F + 0.9F)); + this.playSound("game.neutral.hurt", 1.0F, 1.2F / (this.rand.nextFloat() * 0.2F + 0.9F)); } @Override @@ -62,11 +60,11 @@ public class EntityIceShard extends EntityMagicArrow { double x = this.posX - 0.25d + (rand.nextDouble()/2); double y = this.posY - 0.25d + (rand.nextDouble()/2); double z = this.posZ - 0.25d + (rand.nextDouble()/2); - Wizardry.proxy.spawnParticle(WizardryParticleType.ICE, worldObj, x, y, z, x - this.posX, y - this.posY, z - this.posZ, 20 + rand.nextInt(10)); + Wizardry.proxy.spawnParticle(EnumParticleType.ICE, worldObj, x, y, z, x - this.posX, y - this.posY, z - this.posZ, 20 + rand.nextInt(10)); } } // Parameters for sound: sound event name, volume, pitch. - this.playSound(SoundEvents.ENTITY_SPLASH_POTION_BREAK, 1.0F, rand.nextFloat() * 0.4F + 1.2F); + this.playSound("game.potion.smash", 1.0F, rand.nextFloat() * 0.4F + 1.2F); } diff --git a/src/main/java/electroblob/wizardry/entity/projectile/EntityLightningArrow.java b/src/main/java/electroblob/wizardry/entity/projectile/EntityLightningArrow.java index e7f250ba..a6dc7fe3 100644 --- a/src/main/java/electroblob/wizardry/entity/projectile/EntityLightningArrow.java +++ b/src/main/java/electroblob/wizardry/entity/projectile/EntityLightningArrow.java @@ -1,11 +1,12 @@ package electroblob.wizardry.entity.projectile; +import electroblob.wizardry.EnumParticleType; import electroblob.wizardry.Wizardry; -import electroblob.wizardry.registry.WizardrySounds; -import electroblob.wizardry.util.MagicDamage.DamageType; -import electroblob.wizardry.util.WizardryParticleType; +import electroblob.wizardry.MagicDamage.DamageType; import net.minecraft.entity.Entity; import net.minecraft.entity.EntityLivingBase; +import net.minecraft.entity.monster.EntityCreeper; +import net.minecraft.entity.player.EntityPlayer; import net.minecraft.world.World; public class EntityLightningArrow extends EntityMagicArrow { @@ -42,16 +43,16 @@ public class EntityLightningArrow extends EntityMagicArrow { if(worldObj.isRemote){ for(int j=0;j<8;j++){ - Wizardry.proxy.spawnParticle(WizardryParticleType.SPARK, worldObj, this.posX + rand.nextFloat() - 0.5, this.posY + this.height/2 + rand.nextFloat() - 0.5, this.posZ + rand.nextFloat() - 0.5, 0, 0, 0, 3); + Wizardry.proxy.spawnParticle(EnumParticleType.SPARK, worldObj, this.posX + rand.nextFloat() - 0.5, this.posY + this.height/2 + rand.nextFloat() - 0.5, this.posZ + rand.nextFloat() - 0.5, 0, 0, 0, 3); } } - /* Pretty sure this needn't be here, probably missed it when I implemented the damage type system. + if(entityHit instanceof EntityCreeper && !((EntityCreeper)entityHit).getPowered()){ entityHit.getDataWatcher().updateObject(17, Byte.valueOf((byte)1)); - if(this.getShootingEntity() instanceof EntityPlayer) ((EntityPlayer)this.getShootingEntity()).addStat(Wizardry.chargeCreeper); + if(this.getShootingEntity() instanceof EntityPlayer) ((EntityPlayer)this.getShootingEntity()).triggerAchievement(Wizardry.chargeCreeper); } - */ - this.playSound(WizardrySounds.SPELL_SPARK, 1.0F, 1.0F); + + this.playSound("wizardry:arc", 1.0F, 1.0F); } @Override @@ -62,7 +63,7 @@ public class EntityLightningArrow extends EntityMagicArrow { } if(worldObj.isRemote){ - Wizardry.proxy.spawnParticle(WizardryParticleType.SPARK, worldObj, this.posX, this.posY, this.posZ, 0, 0, 0, 3); + Wizardry.proxy.spawnParticle(EnumParticleType.SPARK, worldObj, this.posX, this.posY, this.posZ, 0, 0, 0, 3); } } diff --git a/src/main/java/electroblob/wizardry/entity/projectile/EntityLightningDisc.java b/src/main/java/electroblob/wizardry/entity/projectile/EntityLightningDisc.java index e27e34d1..68b65d11 100644 --- a/src/main/java/electroblob/wizardry/entity/projectile/EntityLightningDisc.java +++ b/src/main/java/electroblob/wizardry/entity/projectile/EntityLightningDisc.java @@ -2,15 +2,19 @@ package electroblob.wizardry.entity.projectile; import java.util.List; +import electroblob.wizardry.EnumParticleType; +import electroblob.wizardry.MagicDamage; import electroblob.wizardry.Wizardry; -import electroblob.wizardry.registry.WizardrySounds; -import electroblob.wizardry.util.MagicDamage; -import electroblob.wizardry.util.MagicDamage.DamageType; -import electroblob.wizardry.util.WizardryParticleType; -import electroblob.wizardry.util.WizardryUtilities; +import electroblob.wizardry.WizardryUtilities; +import electroblob.wizardry.MagicDamage.DamageType; import net.minecraft.entity.Entity; import net.minecraft.entity.EntityLivingBase; -import net.minecraft.util.math.RayTraceResult; +import net.minecraft.entity.SharedMonsterAttributes; +import net.minecraft.entity.monster.EntityCreeper; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.util.AxisAlignedBB; +import net.minecraft.util.MovingObjectPosition; +import net.minecraft.util.MovingObjectPosition.MovingObjectType; import net.minecraft.world.World; public class EntityLightningDisc extends EntityMagicProjectile @@ -37,14 +41,16 @@ public class EntityLightningDisc extends EntityMagicProjectile this.height = 0.5f; } - @Override - protected float getSpeed(){ - return 1.2f; + /** This is the speed */ + protected float func_70182_d() + { + return 1.2F; } - - @Override - protected void onImpact(RayTraceResult mop) + /** + * Called when this EntityThrowable hits a block or entity. + */ + protected void onImpact(MovingObjectPosition mop) { Entity entityHit = mop.entityHit; @@ -52,15 +58,14 @@ public class EntityLightningDisc extends EntityMagicProjectile { float damage = 12 * damageMultiplier; - entityHit.attackEntityFrom(MagicDamage.causeIndirectMagicDamage(this, this.getThrower(), DamageType.SHOCK), damage); + entityHit.attackEntityFrom(MagicDamage.causeIndirectEntityMagicDamage(this, this.getThrower(), DamageType.SHOCK), damage); } - this.playSound(WizardrySounds.SPELL_SPARK, 1.0F, 1.2F / (this.rand.nextFloat() * 0.2F + 0.9F)); + this.playSound("wizardry:arc", 1.0F, 1.2F / (this.rand.nextFloat() * 0.2F + 0.9F)); - if(mop.typeOfHit == RayTraceResult.Type.BLOCK) this.setDead(); + if(mop.typeOfHit == MovingObjectType.BLOCK) this.setDead(); } - - @Override + public void onUpdate(){ super.onUpdate(); @@ -68,8 +73,8 @@ public class EntityLightningDisc extends EntityMagicProjectile // Particle effect if(worldObj.isRemote){ for(int i=0;i<8;i++){ - Wizardry.proxy.spawnParticle(WizardryParticleType.SPARK, worldObj, this.posX + rand.nextFloat()*2 - 1, this.posY, this.posZ + rand.nextFloat() - 0.5, 0, 0, 0, 3); - //worldObj.spawnParticle(EnumParticleTypes.SMOKE_LARGE, this.posX + rand.nextFloat() - 0.5, this.posY + this.height/2 + rand.nextFloat() - 0.5, this.posZ + rand.nextFloat() - 0.5, 0, 0, 0); + Wizardry.proxy.spawnParticle(EnumParticleType.SPARK, worldObj, this.posX + rand.nextFloat()*2 - 1, this.posY, this.posZ + rand.nextFloat() - 0.5, 0, 0, 0, 3); + //worldObj.spawnParticle("largesmoke", this.posX + rand.nextFloat() - 0.5, this.posY + this.height/2 + rand.nextFloat() - 0.5, this.posZ + rand.nextFloat() - 0.5, 0, 0, 0); } } @@ -105,15 +110,17 @@ public class EntityLightningDisc extends EntityMagicProjectile this.motionZ /= 0.99; } - - @Override + /** + * Gets the amount of gravity to apply to the thrown entity with each tick. + */ protected float getGravityVelocity() { return 0.0F; } - - @Override + /** + * Return whether this entity should be rendered as on fire. + */ public boolean canRenderOnFire() { return false; diff --git a/src/main/java/electroblob/wizardry/entity/projectile/EntityMagicArrow.java b/src/main/java/electroblob/wizardry/entity/projectile/EntityMagicArrow.java index fefa58cd..b2990b9c 100644 --- a/src/main/java/electroblob/wizardry/entity/projectile/EntityMagicArrow.java +++ b/src/main/java/electroblob/wizardry/entity/projectile/EntityMagicArrow.java @@ -4,13 +4,17 @@ import java.lang.ref.WeakReference; import java.util.List; import java.util.UUID; -import electroblob.wizardry.util.MagicDamage; -import electroblob.wizardry.util.MagicDamage.DamageType; -import electroblob.wizardry.util.WizardryUtilities; +import org.apache.logging.log4j.core.helpers.UUIDUtil; + +import cpw.mods.fml.common.registry.IEntityAdditionalSpawnData; +import cpw.mods.fml.relauncher.Side; +import cpw.mods.fml.relauncher.SideOnly; +import electroblob.wizardry.MagicDamage; +import electroblob.wizardry.MagicDamage.DamageType; +import electroblob.wizardry.WizardryUtilities; import io.netty.buffer.ByteBuf; import net.minecraft.block.Block; import net.minecraft.block.material.Material; -import net.minecraft.block.state.IBlockState; import net.minecraft.enchantment.EnchantmentHelper; import net.minecraft.entity.Entity; import net.minecraft.entity.EntityLivingBase; @@ -19,38 +23,31 @@ import net.minecraft.entity.monster.EntityEnderman; import net.minecraft.entity.player.EntityPlayer; import net.minecraft.entity.player.EntityPlayerMP; import net.minecraft.nbt.NBTTagCompound; -import net.minecraft.network.play.server.SPacketChangeGameState; +import net.minecraft.network.play.server.S2BPacketChangeGameState; +import net.minecraft.util.AxisAlignedBB; import net.minecraft.util.DamageSource; -import net.minecraft.util.EnumParticleTypes; -import net.minecraft.util.ResourceLocation; -import net.minecraft.util.math.AxisAlignedBB; -import net.minecraft.util.math.BlockPos; -import net.minecraft.util.math.MathHelper; -import net.minecraft.util.math.RayTraceResult; -import net.minecraft.util.math.Vec3d; +import net.minecraft.util.MathHelper; +import net.minecraft.util.MovingObjectPosition; +import net.minecraft.util.Vec3; import net.minecraft.world.World; -import net.minecraftforge.fml.common.registry.IEntityAdditionalSpawnData; -import net.minecraftforge.fml.relauncher.Side; -import net.minecraftforge.fml.relauncher.SideOnly; /** This class was copied from EntityArrow in the 1.7.10 update as part of the overhaul and major cleanup * of the code for the projectiles. It provides a unifying superclass for all directed projectiles (i.e. not * spherical stuff like snowballs), namely magic missile, ice shard, force arrow, lightning arrow and dart. - * All spherical projectiles should extend {@link EntityMagicProjectile}. + * All other projectiles should extend {@link EntityMagicProjectile}. *

    * This class handles saving of the damage multiplier and all shared logic. Methods are provided which are * triggered at useful points during the entity update cycle as well as a few getters for various properties. * Override any of these to change the behaviour (no need to call super for any of them). * @since Wizardry 1.0 - * @author Electroblob */ -public abstract class EntityMagicArrow extends Entity implements IProjectile, IEntityAdditionalSpawnData { - +public abstract class EntityMagicArrow extends Entity implements IProjectile, IEntityAdditionalSpawnData +{ private int blockX = -1; private int blockY = -1; private int blockZ = -1; /** The block the arrow is stuck in */ - private IBlockState stuckInBlock; + private Block stuckInBlock; /** The metadata of the block the arrow is stuck in */ private int inData; private boolean inGround; @@ -74,6 +71,7 @@ public abstract class EntityMagicArrow extends Entity implements IProjectile, IE public EntityMagicArrow(World world) { super(world); + this.renderDistanceWeight = 10.0D; this.setSize(0.5F, 0.5F); } @@ -82,9 +80,10 @@ public abstract class EntityMagicArrow extends Entity implements IProjectile, IE public EntityMagicArrow(World world, double x, double y, double z) { super(world); + this.renderDistanceWeight = 10.0D; this.setSize(0.5F, 0.5F); this.setPosition(x, y, z); - // yOffset was set to 0 here, but that has been replaced by getYOffset(), which returns 0 in Entity anyway. + this.yOffset = 0.0F; } /** Creates a projectile at the position of the caster, pointing at the given target. The trajectory seems to be altered @@ -93,13 +92,14 @@ public abstract class EntityMagicArrow extends Entity implements IProjectile, IE public EntityMagicArrow(World world, EntityLivingBase caster, Entity target, float speed, float aimingError, float damageMultiplier) { super(world); - this.shootingEntity = new WeakReference(caster); + this.renderDistanceWeight = 10.0D; + this.shootingEntity = new WeakReference(caster); this.damageMultiplier = damageMultiplier; this.posY = caster.posY + (double)caster.getEyeHeight() - 0.10000000149011612D; double d0 = target.posX - caster.posX; - double d1 = this.doGravity() ? target.getEntityBoundingBox().minY + (double)(target.height / 3.0F) - this.posY - : target.getEntityBoundingBox().minY + (double)(target.height / 2.0F) - this.posY; + double d1 = this.doGravity() ? target.boundingBox.minY + (double)(target.height / 3.0F) - this.posY + : target.boundingBox.minY + (double)(target.height / 2.0F) - this.posY; double d2 = target.posZ - caster.posZ; double d3 = (double)MathHelper.sqrt_double(d0 * d0 + d2 * d2); @@ -110,21 +110,21 @@ public abstract class EntityMagicArrow extends Entity implements IProjectile, IE double d4 = d0 / d3; double d5 = d2 / d3; this.setLocationAndAngles(caster.posX + d4, this.posY, caster.posZ + d5, f2, f3); - // yOffset was set to 0 here, but that has been replaced by getYOffset(), which returns 0 in Entity anyway. - + this.yOffset = 0.0F; // f4 depends on the horizontal distance between the two entities and accounts for bullet drop, // but of course if gravity is ignored this should be 0. - float bulletDropCompensation = this.doGravity() ? (float)d3 * 0.2F : 0; - this.setThrowableHeading(d0, d1 + (double)bulletDropCompensation, d2, speed, aimingError); + float f4 = this.doGravity() ? (float)d3 * 0.2F : 0; + this.setThrowableHeading(d0, d1 + (double)f4, d2, speed, aimingError); } } /** Creates a projectile pointing in the direction the caster is looking, with the given speed. - * Use this constructor for normal, player-cast spells. */ + * USE THIS CONSTRUCTOR FOR NORMAL SPELLS. */ public EntityMagicArrow(World world, EntityLivingBase caster, float speed, float damageMultiplier) { super(world); - this.shootingEntity = new WeakReference(caster); + this.renderDistanceWeight = 10.0D; + this.shootingEntity = new WeakReference(caster); this.damageMultiplier = damageMultiplier; this.setSize(0.5F, 0.5F); @@ -133,7 +133,7 @@ public abstract class EntityMagicArrow extends Entity implements IProjectile, IE this.posY -= 0.10000000149011612D; this.posZ -= (double)(MathHelper.sin(this.rotationYaw / 180.0F * (float)Math.PI) * 0.16F); this.setPosition(this.posX, this.posY, this.posZ); - // yOffset was set to 0 here, but that has been replaced by getYOffset(), which returns 0 in Entity anyway. + this.yOffset = 0.0F; this.motionX = (double)(-MathHelper.sin(this.rotationYaw / 180.0F * (float)Math.PI) * MathHelper.cos(this.rotationPitch / 180.0F * (float)Math.PI)); this.motionZ = (double)(MathHelper.cos(this.rotationYaw / 180.0F * (float)Math.PI) * MathHelper.cos(this.rotationPitch / 180.0F * (float)Math.PI)); this.motionY = (double)(-MathHelper.sin(this.rotationPitch / 180.0F * (float)Math.PI)); @@ -189,13 +189,21 @@ public abstract class EntityMagicArrow extends Entity implements IProjectile, IE this.ticksInGround = 0; } - // There was an override for setPositionAndRotationDirect here, but it was exactly the same as the superclass - // method (in Entity), so it was removed since it was redundant. + /** + * Sets the position and rotation. Only difference from the other one is no bounding on the rotation. Args: posX, + * posY, posZ, yaw, pitch + * - Allows the arrow to stick in blocks, I think. + */ + @SideOnly(Side.CLIENT) + public void setPositionAndRotation2(double p_70056_1_, double p_70056_3_, double p_70056_5_, float p_70056_7_, float p_70056_8_, int p_70056_9_) + { + this.setPosition(p_70056_1_, p_70056_3_, p_70056_5_); + this.setRotation(p_70056_7_, p_70056_8_); + } /** * Sets the velocity to the args. Args: x, y, z. THIS IS CLIENT SIDE ONLY! DO NOT USE IN COMMON OR SERVER CODE! */ - @Override @SideOnly(Side.CLIENT) public void setVelocity(double p_70016_1_, double p_70016_3_, double p_70016_5_) { @@ -230,7 +238,9 @@ public abstract class EntityMagicArrow extends Entity implements IProjectile, IE /** Called when the projectile hits a block. Override to add sound effects and such like. */ public void onBlockHit(){} - @Override + /** + * Called to update the entity's position/logic. + */ public void onUpdate(){ super.onUpdate(); @@ -249,14 +259,14 @@ public abstract class EntityMagicArrow extends Entity implements IProjectile, IE this.prevRotationPitch = this.rotationPitch = (float)(Math.atan2(this.motionY, (double)f) * 180.0D / Math.PI); } - BlockPos blockpos = new BlockPos(this.blockX, this.blockY, this.blockZ); - IBlockState iblockstate = this.worldObj.getBlockState(blockpos); + Block block = this.worldObj.getBlock(this.blockX, this.blockY, this.blockZ); - if (iblockstate.getMaterial() != Material.AIR) + if (block.getMaterial() != Material.air) { - AxisAlignedBB axisalignedbb = iblockstate.getCollisionBoundingBox(this.worldObj, blockpos); + block.setBlockBoundsBasedOnState(this.worldObj, this.blockX, this.blockY, this.blockZ); + AxisAlignedBB axisalignedbb = block.getCollisionBoundingBoxFromPool(this.worldObj, this.blockX, this.blockY, this.blockZ); - if (axisalignedbb != Block.NULL_AABB && axisalignedbb.offset(blockpos).isVecInside(new Vec3d(this.posX, this.posY, this.posZ))) + if (axisalignedbb != null && axisalignedbb.isVecInside(Vec3.createVectorHelper(this.posX, this.posY, this.posZ))) { this.inGround = true; } @@ -274,26 +284,26 @@ public abstract class EntityMagicArrow extends Entity implements IProjectile, IE this.tickInGround(); } // When the arrow is in the air - else{ + else + { + ++this.ticksInAir; this.tickInAir(); - this.ticksInGround = 0; - ++this.ticksInAir; - Vec3d vec3d1 = new Vec3d(this.posX, this.posY, this.posZ); - Vec3d vec3d = new Vec3d(this.posX + this.motionX, this.posY + this.motionY, this.posZ + this.motionZ); - RayTraceResult raytraceresult = this.worldObj.rayTraceBlocks(vec3d1, vec3d, false, true, false); - vec3d1 = new Vec3d(this.posX, this.posY, this.posZ); - vec3d = new Vec3d(this.posX + this.motionX, this.posY + this.motionY, this.posZ + this.motionZ); + Vec3 vec31 = Vec3.createVectorHelper(this.posX, this.posY, this.posZ); + Vec3 vec3 = Vec3.createVectorHelper(this.posX + this.motionX, this.posY + this.motionY, this.posZ + this.motionZ); + MovingObjectPosition movingobjectposition = this.worldObj.func_147447_a(vec31, vec3, false, true, false); + vec31 = Vec3.createVectorHelper(this.posX, this.posY, this.posZ); + vec3 = Vec3.createVectorHelper(this.posX + this.motionX, this.posY + this.motionY, this.posZ + this.motionZ); - if (raytraceresult != null) + if (movingobjectposition != null) { - vec3d = new Vec3d(raytraceresult.hitVec.xCoord, raytraceresult.hitVec.yCoord, raytraceresult.hitVec.zCoord); + vec3 = Vec3.createVectorHelper(movingobjectposition.hitVec.xCoord, movingobjectposition.hitVec.yCoord, movingobjectposition.hitVec.zCoord); } Entity entity = null; - List list = this.worldObj.getEntitiesWithinAABBExcludingEntity(this, this.getEntityBoundingBox().addCoord(this.motionX, this.motionY, this.motionZ).expand(1.0D, 1.0D, 1.0D)); + List list = this.worldObj.getEntitiesWithinAABBExcludingEntity(this, this.boundingBox.addCoord(this.motionX, this.motionY, this.motionZ).expand(1.0D, 1.0D, 1.0D)); double d0 = 0.0D; int i; float f1; @@ -305,12 +315,12 @@ public abstract class EntityMagicArrow extends Entity implements IProjectile, IE if (entity1.canBeCollidedWith() && (entity1 != this.getShootingEntity() || this.ticksInAir >= 5)) { f1 = 0.3F; - AxisAlignedBB axisalignedbb1 = entity1.getEntityBoundingBox().expand((double)f1, (double)f1, (double)f1); - RayTraceResult RayTraceResult1 = axisalignedbb1.calculateIntercept(vec3d1, vec3d); + AxisAlignedBB axisalignedbb1 = entity1.boundingBox.expand((double)f1, (double)f1, (double)f1); + MovingObjectPosition movingobjectposition1 = axisalignedbb1.calculateIntercept(vec31, vec3); - if (RayTraceResult1 != null) + if (movingobjectposition1 != null) { - double d1 = vec3d1.distanceTo(RayTraceResult1.hitVec); + double d1 = vec31.distanceTo(movingobjectposition1.hitVec); if (d1 < d0 || d0 == 0.0D) { @@ -323,25 +333,25 @@ public abstract class EntityMagicArrow extends Entity implements IProjectile, IE if (entity != null) { - raytraceresult = new RayTraceResult(entity); + movingobjectposition = new MovingObjectPosition(entity); } // Players that are considered invulnerable to the caster allow the projectile to pass straight through them. - if (raytraceresult != null && raytraceresult.entityHit != null && raytraceresult.entityHit instanceof EntityPlayer) + if (movingobjectposition != null && movingobjectposition.entityHit != null && movingobjectposition.entityHit instanceof EntityPlayer) { - EntityPlayer entityplayer = (EntityPlayer)raytraceresult.entityHit; + EntityPlayer entityplayer = (EntityPlayer)movingobjectposition.entityHit; if (entityplayer.capabilities.disableDamage || this.getShootingEntity() instanceof EntityPlayer && !((EntityPlayer)this.getShootingEntity()).canAttackPlayer(entityplayer)) { - raytraceresult = null; + movingobjectposition = null; } } // If the arrow hits something - if (raytraceresult != null) + if (movingobjectposition != null) { // If the arrow hits an entity - if (raytraceresult.entityHit != null) + if (movingobjectposition.entityHit != null) { DamageSource damagesource = null; @@ -351,14 +361,14 @@ public abstract class EntityMagicArrow extends Entity implements IProjectile, IE } else { - damagesource = MagicDamage.causeIndirectMagicDamage(this, (EntityLivingBase)this.getShootingEntity(), this.getDamageType()).setProjectile(); + damagesource = MagicDamage.causeIndirectEntityMagicDamage(this, (EntityLivingBase)this.getShootingEntity(), this.getDamageType()).setProjectile(); } - if (raytraceresult.entityHit.attackEntityFrom(damagesource, (float)(this.getDamage()*this.damageMultiplier))) + if (movingobjectposition.entityHit.attackEntityFrom(damagesource, (float)(this.getDamage()*this.damageMultiplier))) { - if (raytraceresult.entityHit instanceof EntityLivingBase) + if (movingobjectposition.entityHit instanceof EntityLivingBase) { - EntityLivingBase entityHit = (EntityLivingBase)raytraceresult.entityHit; + EntityLivingBase entityHit = (EntityLivingBase)movingobjectposition.entityHit; this.onEntityHit(entityHit); @@ -368,24 +378,24 @@ public abstract class EntityMagicArrow extends Entity implements IProjectile, IE if (f4 > 0.0F) { - raytraceresult.entityHit.addVelocity(this.motionX * (double)this.knockbackStrength * 0.6000000238418579D / (double)f4, 0.1D, this.motionZ * (double)this.knockbackStrength * 0.6000000238418579D / (double)f4); + movingobjectposition.entityHit.addVelocity(this.motionX * (double)this.knockbackStrength * 0.6000000238418579D / (double)f4, 0.1D, this.motionZ * (double)this.knockbackStrength * 0.6000000238418579D / (double)f4); } } // Thorns enchantment if (this.getShootingEntity() != null && this.getShootingEntity() instanceof EntityLivingBase) { - EnchantmentHelper.applyThornEnchantments(entityHit, this.getShootingEntity()); - EnchantmentHelper.applyArthropodEnchantments((EntityLivingBase)this.getShootingEntity(), entityHit); + EnchantmentHelper.func_151384_a(entityHit, this.getShootingEntity()); + EnchantmentHelper.func_151385_b((EntityLivingBase)this.getShootingEntity(), entityHit); } - if (this.getShootingEntity() != null && raytraceresult.entityHit != this.getShootingEntity() && raytraceresult.entityHit instanceof EntityPlayer && this.getShootingEntity() instanceof EntityPlayerMP) + if (this.getShootingEntity() != null && movingobjectposition.entityHit != this.getShootingEntity() && movingobjectposition.entityHit instanceof EntityPlayer && this.getShootingEntity() instanceof EntityPlayerMP) { - ((EntityPlayerMP)this.getShootingEntity()).connection.sendPacket(new SPacketChangeGameState(6, 0.0F)); + ((EntityPlayerMP)this.getShootingEntity()).playerNetServerHandler.sendPacket(new S2BPacketChangeGameState(6, 0.0F)); } } - if (!(raytraceresult.entityHit instanceof EntityEnderman) && !this.doOverpenetration()) + if (!(movingobjectposition.entityHit instanceof EntityEnderman) && !this.doOverpenetration()) { this.setDead(); } @@ -408,13 +418,14 @@ public abstract class EntityMagicArrow extends Entity implements IProjectile, IE // If the arrow hits a block else { - this.blockX = raytraceresult.getBlockPos().getX(); - this.blockY = raytraceresult.getBlockPos().getY(); - this.blockZ = raytraceresult.getBlockPos().getZ(); - this.stuckInBlock = this.worldObj.getBlockState(raytraceresult.getBlockPos()); - this.motionX = (double)((float)(raytraceresult.hitVec.xCoord - this.posX)); - this.motionY = (double)((float)(raytraceresult.hitVec.yCoord - this.posY)); - this.motionZ = (double)((float)(raytraceresult.hitVec.zCoord - this.posZ)); + this.blockX = movingobjectposition.blockX; + this.blockY = movingobjectposition.blockY; + this.blockZ = movingobjectposition.blockZ; + this.stuckInBlock = this.worldObj.getBlock(this.blockX, this.blockY, this.blockZ); + this.inData = this.worldObj.getBlockMetadata(this.blockX, this.blockY, this.blockZ); + this.motionX = (double)((float)(movingobjectposition.hitVec.xCoord - this.posX)); + this.motionY = (double)((float)(movingobjectposition.hitVec.yCoord - this.posY)); + this.motionZ = (double)((float)(movingobjectposition.hitVec.zCoord - this.posZ)); //f2 = MathHelper.sqrt_double(this.motionX * this.motionX + this.motionY * this.motionY + this.motionZ * this.motionZ); //this.posX -= this.motionX / (double)f2 * 0.05000000074505806D; //this.posY -= this.motionY / (double)f2 * 0.05000000074505806D; @@ -425,9 +436,9 @@ public abstract class EntityMagicArrow extends Entity implements IProjectile, IE this.onBlockHit(); - if (this.stuckInBlock.getMaterial() != Material.AIR) + if (this.stuckInBlock.getMaterial() != Material.air) { - this.stuckInBlock.getBlock().onEntityCollidedWithBlock(this.worldObj, raytraceresult.getBlockPos(), this.stuckInBlock, this); + this.stuckInBlock.onEntityCollidedWithBlock(this.worldObj, this.blockX, this.blockY, this.blockZ, this); } } } @@ -468,7 +479,7 @@ public abstract class EntityMagicArrow extends Entity implements IProjectile, IE for (int l = 0; l < 4; ++l) { float f4 = 0.25F; - this.worldObj.spawnParticle(EnumParticleTypes.WATER_BUBBLE, this.posX - this.motionX * (double)f4, this.posY - this.motionY * (double)f4, this.posZ - this.motionZ * (double)f4, this.motionX, this.motionY, this.motionZ); + this.worldObj.spawnParticle("bubble", this.posX - this.motionX * (double)f4, this.posY - this.motionY * (double)f4, this.posZ - this.motionZ * (double)f4, this.motionX, this.motionY, this.motionZ); } f3 = 0.8F; @@ -488,44 +499,49 @@ public abstract class EntityMagicArrow extends Entity implements IProjectile, IE if(this.doGravity()) this.motionY -= 0.05; this.setPosition(this.posX, this.posY, this.posZ); - this.doBlockCollisions(); + this.func_145775_I(); } } - @Override + /** + * (abstract) Protected helper method to write subclass entity data to NBT. + */ public void writeEntityToNBT(NBTTagCompound tag) { tag.setShort("xTile", (short)this.blockX); tag.setShort("yTile", (short)this.blockY); tag.setShort("zTile", (short)this.blockZ); tag.setShort("life", (short)this.ticksInGround); - if(this.stuckInBlock != null){ - ResourceLocation resourcelocation = (ResourceLocation)Block.REGISTRY.getNameForObject(this.stuckInBlock.getBlock()); - tag.setString("inTile", resourcelocation == null ? "" : resourcelocation.toString()); - } + tag.setByte("inTile", (byte)Block.getIdFromBlock(this.stuckInBlock)); tag.setByte("inData", (byte)this.inData); tag.setByte("shake", (byte)this.arrowShake); tag.setByte("inGround", (byte)(this.inGround ? 1 : 0)); tag.setFloat("damageMultiplier", this.damageMultiplier); if(this.getShootingEntity() != null){ - tag.setUniqueId("casterUUID", this.getShootingEntity().getUniqueID()); + tag.setString("casterUUID", this.getShootingEntity().getUniqueID().toString()); } } - @Override + /** + * (abstract) Protected helper method to read subclass entity data from NBT. + */ public void readEntityFromNBT(NBTTagCompound tag) { this.blockX = tag.getShort("xTile"); this.blockY = tag.getShort("yTile"); this.blockZ = tag.getShort("zTile"); this.ticksInGround = tag.getShort("life"); - // Commented out for now because there's some funny stuff going on with blockstates and metadata. - //this.stuckInBlock = Block.getBlockById(tag.getByte("inTile") & 255); + this.stuckInBlock = Block.getBlockById(tag.getByte("inTile") & 255); this.inData = tag.getByte("inData") & 255; this.arrowShake = tag.getByte("shake") & 255; this.inGround = tag.getByte("inGround") == 1; this.damageMultiplier = tag.getFloat("damageMultiplier"); - casterUUID = tag.getUniqueId("casterUUID"); + String string = tag.getString("casterUUID"); + // Fixed issue #3: + // Uses regex to verify that the string is a valid UUID (I can't see why it wouldn't be, but it was reported + // on the issue tracker so I've fixed it). + if(WizardryUtilities.verifyUUIDString(string)) + casterUUID = UUID.fromString(string); } /** @@ -564,7 +580,7 @@ public abstract class EntityMagicArrow extends Entity implements IProjectile, IE } public void readSpawnData(ByteBuf buffer){ - if(buffer.isReadable()) this.shootingEntity = new WeakReference((EntityLivingBase)this.worldObj.getEntityByID(buffer.readInt())); + if(buffer.isReadable()) this.shootingEntity = new WeakReference((EntityLivingBase)this.worldObj.getEntityByID(buffer.readInt())); } /** @@ -577,6 +593,6 @@ public abstract class EntityMagicArrow extends Entity implements IProjectile, IE } public void setShootingEntity(EntityLivingBase entity) { - shootingEntity = new WeakReference(entity); + shootingEntity = new WeakReference(entity); } } \ No newline at end of file diff --git a/src/main/java/electroblob/wizardry/entity/projectile/EntityMagicMissile.java b/src/main/java/electroblob/wizardry/entity/projectile/EntityMagicMissile.java index 67c079d9..9b1ad13a 100644 --- a/src/main/java/electroblob/wizardry/entity/projectile/EntityMagicMissile.java +++ b/src/main/java/electroblob/wizardry/entity/projectile/EntityMagicMissile.java @@ -1,10 +1,9 @@ package electroblob.wizardry.entity.projectile; +import electroblob.wizardry.EnumParticleType; import electroblob.wizardry.Wizardry; -import electroblob.wizardry.util.WizardryParticleType; import net.minecraft.entity.Entity; import net.minecraft.entity.EntityLivingBase; -import net.minecraft.init.SoundEvents; import net.minecraft.world.World; public class EntityMagicMissile extends EntityMagicArrow { @@ -38,7 +37,7 @@ public class EntityMagicMissile extends EntityMagicArrow { @Override public void onEntityHit(EntityLivingBase entityHit){ - this.playSound(SoundEvents.ENTITY_GENERIC_HURT, 1.0F, 1.2F / (this.rand.nextFloat() * 0.2F + 0.9F)); + this.playSound("game.neutral.hurt", 1.0F, 1.2F / (this.rand.nextFloat() * 0.2F + 0.9F)); } @Override @@ -51,10 +50,10 @@ public class EntityMagicMissile extends EntityMagicArrow { if(this.worldObj.isRemote){ if(this.ticksExisted % 2 == 1){ - Wizardry.proxy.spawnParticle(WizardryParticleType.SPARKLE, worldObj, this.posX, this.posY, this.posZ, 0, 0, 0, 20 + rand.nextInt(10), 0.5f + (rand.nextFloat()/2), 0.5f + (rand.nextFloat()/2), 0.5f + (rand.nextFloat()/2)); + Wizardry.proxy.spawnParticle(EnumParticleType.SPARKLE, worldObj, this.posX, this.posY, this.posZ, 0, 0, 0, 20 + rand.nextInt(10), 0.5f + (rand.nextFloat()/2), 0.5f + (rand.nextFloat()/2), 0.5f + (rand.nextFloat()/2)); } else{ - Wizardry.proxy.spawnParticle(WizardryParticleType.SPARKLE, worldObj, this.posX, this.posY, this.posZ, 0, 0, 0, 20 + rand.nextInt(10), 0.5f + (rand.nextFloat()/2), 0.5f + (rand.nextFloat()/2), 0.5f + (rand.nextFloat()/2)); + Wizardry.proxy.spawnParticle(EnumParticleType.SPARKLE, worldObj, this.posX, this.posY, this.posZ, 0, 0, 0, 20 + rand.nextInt(10), 0.5f + (rand.nextFloat()/2), 0.5f + (rand.nextFloat()/2), 0.5f + (rand.nextFloat()/2)); } } } diff --git a/src/main/java/electroblob/wizardry/entity/projectile/EntityMagicProjectile.java b/src/main/java/electroblob/wizardry/entity/projectile/EntityMagicProjectile.java index e37c45fa..1c499771 100644 --- a/src/main/java/electroblob/wizardry/entity/projectile/EntityMagicProjectile.java +++ b/src/main/java/electroblob/wizardry/entity/projectile/EntityMagicProjectile.java @@ -5,7 +5,6 @@ import net.minecraft.entity.EntityLivingBase; import net.minecraft.entity.projectile.EntityThrowable; import net.minecraft.nbt.NBTTagCompound; import net.minecraft.world.World; -import net.minecraftforge.fml.common.registry.IEntityAdditionalSpawnData; /** This class is a generic superclass for all non-directed projectiles, namely: darkness orb, firebolt, firebomb, * force orb, ice charge, lightning disc, poison bomb, spark, spark bomb and thunderbolt. Directed (arrow-like) projectiles @@ -15,30 +14,26 @@ import net.minecraftforge.fml.common.registry.IEntityAdditionalSpawnData; * as it is. Range is done via the velocity when the constructor is called. Caster is already handled by EntityThrowable.getThrower(). * * Note that this class does not implement {@link IEntityAdditionalSpawnData}; subclasses that need to transfer extra data - * to the client should implement that interface themselves. See {@link EntityBomb} for an example. + * to the client should implement that interface themselves. See {@link EntitySparkBomb} for an example. * @since Wizardry 1.0 - * @author Electroblob - * @see EntityBomb */ -public abstract class EntityMagicProjectile extends EntityThrowable { - +public abstract class EntityMagicProjectile extends EntityThrowable +{ public float damageMultiplier = 1.0f; - public EntityMagicProjectile(World world) + public EntityMagicProjectile(World par1World) { - super(world); + super(par1World); } - public EntityMagicProjectile(World world, EntityLivingBase thrower) + public EntityMagicProjectile(World par1World, EntityLivingBase par2EntityLivingBase) { - super(world, thrower); + super(par1World, par2EntityLivingBase); } - public EntityMagicProjectile(World world, EntityLivingBase thrower, float damageMultiplier) + public EntityMagicProjectile(World par1World, EntityLivingBase par2EntityLivingBase, float damageMultiplier) { - super(world, thrower); - // This is the standard set of parameters for this method, used by snowballs and ender pearls amongst others. - this.setHeadingFromThrower(thrower, thrower.rotationPitch, thrower.rotationYaw, 0.0f, this.getSpeed(), 1.0f); + super(par1World, par2EntityLivingBase); this.damageMultiplier = damageMultiplier; } @@ -47,16 +42,11 @@ public abstract class EntityMagicProjectile extends EntityThrowable { super(par1World, par2, par4, par6); } - /** This got removed at some point since 1.7.10, but I liked it so I thought I'd add it back in again. */ - protected float getSpeed(){ - return 1.5f; - } - /** Sets this projectile's velocity as a normalised vector towards the target. */ public void directTowards(Entity target, float velocity){ double dx = target.posX - this.posX; - double dy = target.getEntityBoundingBox().minY + (double)(target.height / 2.0F) - (this.posY + (double)(this.height / 2.0F)); + double dy = target.boundingBox.minY + (double)(target.height / 2.0F) - (this.posY + (double)(this.height / 2.0F)); double dz = target.posZ - this.posZ; this.motionX = dx/this.getDistanceToEntity(target) * velocity; diff --git a/src/main/java/electroblob/wizardry/entity/projectile/EntityPoisonBomb.java b/src/main/java/electroblob/wizardry/entity/projectile/EntityPoisonBomb.java index 717aa618..ec3fefd4 100644 --- a/src/main/java/electroblob/wizardry/entity/projectile/EntityPoisonBomb.java +++ b/src/main/java/electroblob/wizardry/entity/projectile/EntityPoisonBomb.java @@ -2,21 +2,26 @@ package electroblob.wizardry.entity.projectile; import java.util.List; +import cpw.mods.fml.common.registry.IEntityAdditionalSpawnData; +import electroblob.wizardry.EnumParticleType; +import electroblob.wizardry.MagicDamage; +import electroblob.wizardry.MagicDamage.DamageType; import electroblob.wizardry.Wizardry; -import electroblob.wizardry.util.MagicDamage; -import electroblob.wizardry.util.MagicDamage.DamageType; -import electroblob.wizardry.util.WizardryParticleType; -import electroblob.wizardry.util.WizardryUtilities; +import electroblob.wizardry.WizardryUtilities; +import io.netty.buffer.ByteBuf; import net.minecraft.entity.Entity; import net.minecraft.entity.EntityLivingBase; -import net.minecraft.init.MobEffects; -import net.minecraft.init.SoundEvents; +import net.minecraft.nbt.NBTTagCompound; +import net.minecraft.potion.Potion; import net.minecraft.potion.PotionEffect; -import net.minecraft.util.EnumParticleTypes; -import net.minecraft.util.math.RayTraceResult; +import net.minecraft.util.DamageSource; +import net.minecraft.util.MovingObjectPosition; import net.minecraft.world.World; -public class EntityPoisonBomb extends EntityBomb { +public class EntityPoisonBomb extends EntityMagicProjectile implements IEntityAdditionalSpawnData +{ + /** The entity blast multiplier. Only some projectiles cause a blast, which is why this isn't in EntityMagicProjectile. */ + public float blastMultiplier = 1.0f; public EntityPoisonBomb(World par1World) { @@ -30,7 +35,8 @@ public class EntityPoisonBomb extends EntityBomb { public EntityPoisonBomb(World par1World, EntityLivingBase par2EntityLivingBase, float damageMultiplier, float blastMultiplier) { - super(par1World, par2EntityLivingBase, damageMultiplier, blastMultiplier); + super(par1World, par2EntityLivingBase, damageMultiplier); + this.blastMultiplier = blastMultiplier; } public EntityPoisonBomb(World par1World, double par2, double par4, double par6) @@ -38,35 +44,36 @@ public class EntityPoisonBomb extends EntityBomb { super(par1World, par2, par4, par6); } - @Override - protected void onImpact(RayTraceResult par1RayTraceResult) + /** + * Called when this EntityThrowable hits a block or entity. + */ + protected void onImpact(MovingObjectPosition par1MovingObjectPosition) { - Entity entityHit = par1RayTraceResult.entityHit; + Entity entityHit = par1MovingObjectPosition.entityHit; if (entityHit != null) { // This is if the poison bomb gets a direct hit float damage = 5 * damageMultiplier; - entityHit.attackEntityFrom(MagicDamage.causeIndirectMagicDamage(this, this.getThrower(), DamageType.POISON).setProjectile(), damage); + entityHit.attackEntityFrom(MagicDamage.causeIndirectEntityMagicDamage(this, this.getThrower(), DamageType.POISON).setProjectile(), damage); - if(entityHit instanceof EntityLivingBase && !MagicDamage.isEntityImmune(DamageType.POISON, entityHit)) ((EntityLivingBase)entityHit).addPotionEffect(new PotionEffect(MobEffects.POISON, 120, 1)); + if(entityHit instanceof EntityLivingBase && !MagicDamage.isEntityImmune(DamageType.POISON, entityHit)) ((EntityLivingBase)entityHit).addPotionEffect(new PotionEffect(Potion.poison.id, 120, 1)); } // Particle effect if(worldObj.isRemote){ + this.worldObj.spawnParticle("largeexplode", this.posX, this.posY, this.posZ, 0, 0, 0); for(int i=0;i<60*blastMultiplier;i++){ - Wizardry.proxy.spawnParticle(WizardryParticleType.SPARKLE, worldObj, this.posX + (this.rand.nextDouble()*4 - 2)*blastMultiplier, this.posY + (this.rand.nextDouble()*4 - 2)*blastMultiplier, this.posZ + (this.rand.nextDouble()*4 - 2)*blastMultiplier, 0.0d, 0.0d, 0.0d, 35, 0.2f + rand.nextFloat()*0.3f, 0.6f, 0.0f); - Wizardry.proxy.spawnParticle(WizardryParticleType.DARK_MAGIC, worldObj, this.posX + (this.rand.nextDouble()*4 - 2)*blastMultiplier, this.posY + (this.rand.nextDouble()*4 - 2)*blastMultiplier, this.posZ + (this.rand.nextDouble()*4 - 2)*blastMultiplier, 0.0d, 0.0d, 0.0d, 0, 0.2f + rand.nextFloat()*0.2f, 0.8f, 0.0f); + Wizardry.proxy.spawnParticle(EnumParticleType.SPARKLE, worldObj, this.posX + (this.rand.nextDouble()*4 - 2)*blastMultiplier, this.posY + (this.rand.nextDouble()*4 - 2)*blastMultiplier, this.posZ + (this.rand.nextDouble()*4 - 2)*blastMultiplier, 0.0d, 0.0d, 0.0d, 35, 0.2f + rand.nextFloat()*0.3f, 0.6f, 0.0f); + Wizardry.proxy.spawnParticle(EnumParticleType.DARK_MAGIC, worldObj, this.posX + (this.rand.nextDouble()*4 - 2)*blastMultiplier, this.posY + (this.rand.nextDouble()*4 - 2)*blastMultiplier, this.posZ + (this.rand.nextDouble()*4 - 2)*blastMultiplier, 0.0d, 0.0d, 0.0d, 0, 0.2f + rand.nextFloat()*0.2f, 0.8f, 0.0f); } - // Spawning this after the other particles fixes the rendering colour bug. It's a bit of a cheat, but it works pretty well. - this.worldObj.spawnParticle(EnumParticleTypes.EXPLOSION_LARGE, this.posX, this.posY, this.posZ, 0, 0, 0); } if(!this.worldObj.isRemote){ - this.playSound(SoundEvents.ENTITY_SPLASH_POTION_BREAK, 1.5F, rand.nextFloat() * 0.4F + 0.6F); - this.playSound(SoundEvents.BLOCK_FIRE_EXTINGUISH, 1.2F, 1.0f); + this.worldObj.playSoundEffect(this.posX, this.posY, this.posZ, "game.potion.smash", 1.5F, rand.nextFloat() * 0.4F + 0.6F); + this.worldObj.playSoundEffect(this.posX, this.posY, this.posZ, "random.fizz", 1.2F, 1.0f); double range = 3.0d*blastMultiplier; @@ -74,12 +81,34 @@ public class EntityPoisonBomb extends EntityBomb { for(EntityLivingBase target : targets){ if(target != entityHit && target != this.getThrower() && !MagicDamage.isEntityImmune(DamageType.POISON, target)){ - target.attackEntityFrom(MagicDamage.causeIndirectMagicDamage(this, this.getThrower(), DamageType.POISON), 4.0f * damageMultiplier); - target.addPotionEffect(new PotionEffect(MobEffects.POISON, 100, 1)); + target.attackEntityFrom(MagicDamage.causeIndirectEntityMagicDamage(this, this.getThrower(), DamageType.POISON), 4.0f * damageMultiplier); + target.addPotionEffect(new PotionEffect(19, 100, 1)); } } this.setDead(); } } + + @Override + public void writeSpawnData(ByteBuf buffer) { + buffer.writeFloat(blastMultiplier); + } + + @Override + public void readSpawnData(ByteBuf buffer) { + blastMultiplier = buffer.readFloat(); + } + + @Override + public void readEntityFromNBT(NBTTagCompound nbttagcompound){ + super.readEntityFromNBT(nbttagcompound); + blastMultiplier = nbttagcompound.getFloat("blastMultiplier"); + } + + @Override + public void writeEntityToNBT(NBTTagCompound nbttagcompound){ + super.writeEntityToNBT(nbttagcompound); + nbttagcompound.setFloat("blastMultiplier", blastMultiplier); + } } diff --git a/src/main/java/electroblob/wizardry/entity/projectile/EntitySmokeBomb.java b/src/main/java/electroblob/wizardry/entity/projectile/EntitySmokeBomb.java index 1ed5dea0..b10d0509 100644 --- a/src/main/java/electroblob/wizardry/entity/projectile/EntitySmokeBomb.java +++ b/src/main/java/electroblob/wizardry/entity/projectile/EntitySmokeBomb.java @@ -2,21 +2,25 @@ package electroblob.wizardry.entity.projectile; import java.util.List; +import cpw.mods.fml.common.registry.IEntityAdditionalSpawnData; +import electroblob.wizardry.EnumParticleType; import electroblob.wizardry.Wizardry; -import electroblob.wizardry.registry.WizardryPotions; -import electroblob.wizardry.util.WizardryParticleType; -import electroblob.wizardry.util.WizardryUtilities; +import electroblob.wizardry.WizardryUtilities; +import io.netty.buffer.ByteBuf; +import net.minecraft.entity.EntityCreature; import net.minecraft.entity.EntityLiving; import net.minecraft.entity.EntityLivingBase; import net.minecraft.entity.player.EntityPlayer; -import net.minecraft.init.MobEffects; -import net.minecraft.init.SoundEvents; +import net.minecraft.nbt.NBTTagCompound; +import net.minecraft.potion.Potion; import net.minecraft.potion.PotionEffect; -import net.minecraft.util.EnumParticleTypes; -import net.minecraft.util.math.RayTraceResult; +import net.minecraft.util.MovingObjectPosition; import net.minecraft.world.World; -public class EntitySmokeBomb extends EntityBomb { +public class EntitySmokeBomb extends EntityMagicProjectile implements IEntityAdditionalSpawnData +{ + /** The entity blast multiplier. Only some projectiles cause a blast, which is why this isn't in EntityMagicProjectile. */ + public float blastMultiplier = 1.0f; public EntitySmokeBomb(World par1World) { @@ -30,7 +34,8 @@ public class EntitySmokeBomb extends EntityBomb { public EntitySmokeBomb(World par1World, EntityLivingBase par2EntityLivingBase, float damageMultiplier, float blastMultiplier) { - super(par1World, par2EntityLivingBase, damageMultiplier, blastMultiplier); + super(par1World, par2EntityLivingBase, damageMultiplier); + this.blastMultiplier = blastMultiplier; } public EntitySmokeBomb(World par1World, double par2, double par4, double par6) @@ -38,23 +43,39 @@ public class EntitySmokeBomb extends EntityBomb { super(par1World, par2, par4, par6); } - @Override - protected void onImpact(RayTraceResult par1RayTraceResult){ + /** + * Called when this EntityThrowable hits a block or entity. + */ + protected void onImpact(MovingObjectPosition par1MovingObjectPosition) + { + /* + Entity entityHit = par1MovingObjectPosition.entityHit; + + if (entityHit != null) + { + // This is if the smoke bomb gets a direct hit + float damage = 2.0f * damageMultiplier; + + entityHit.attackEntityFrom(WizardryUtilities.causeIndirectEntityMagicDamage(this, this.getThrower()), damage); + + if(entityHit instanceof EntityLivingBase) ((EntityLivingBase)entityHit).addPotionEffect(new PotionEffect(Potion.blindness.id, 120, 0)); + } + */ // Particle effect if(worldObj.isRemote){ - this.worldObj.spawnParticle(EnumParticleTypes.EXPLOSION_LARGE, this.posX, this.posY, this.posZ, 0, 0, 0); + this.worldObj.spawnParticle("largeexplode", this.posX, this.posY, this.posZ, 0, 0, 0); for(int i=0;i<60*blastMultiplier;i++){ - this.worldObj.spawnParticle(EnumParticleTypes.SMOKE_LARGE, this.posX + (this.rand.nextDouble()*4 - 2)*blastMultiplier, this.posY + (this.rand.nextDouble()*4 - 2)*blastMultiplier, this.posZ + (this.rand.nextDouble()*4 - 2)*blastMultiplier, 0, 0, 0); + this.worldObj.spawnParticle("largesmoke", this.posX + (this.rand.nextDouble()*4 - 2)*blastMultiplier, this.posY + (this.rand.nextDouble()*4 - 2)*blastMultiplier, this.posZ + (this.rand.nextDouble()*4 - 2)*blastMultiplier, 0, 0, 0); float brightness = rand.nextFloat() * 0.3f; - Wizardry.proxy.spawnParticle(WizardryParticleType.DARK_MAGIC, worldObj, this.posX + (this.rand.nextDouble()*4 - 2)*blastMultiplier, this.posY + (this.rand.nextDouble()*4 - 2)*blastMultiplier, this.posZ + (this.rand.nextDouble()*4 - 2)*blastMultiplier, 0.0d, 0.0d, 0.0d, 0, brightness, brightness, brightness); + Wizardry.proxy.spawnParticle(EnumParticleType.DARK_MAGIC, worldObj, this.posX + (this.rand.nextDouble()*4 - 2)*blastMultiplier, this.posY + (this.rand.nextDouble()*4 - 2)*blastMultiplier, this.posZ + (this.rand.nextDouble()*4 - 2)*blastMultiplier, 0.0d, 0.0d, 0.0d, 0, brightness, brightness, brightness); } } if(!this.worldObj.isRemote){ - this.playSound(SoundEvents.ENTITY_SPLASH_POTION_BREAK, 1.5F, rand.nextFloat() * 0.4F + 0.6F); - this.playSound(SoundEvents.BLOCK_FIRE_EXTINGUISH, 1.2F, 1.0f); + this.worldObj.playSoundEffect(this.posX, this.posY, this.posZ, "game.potion.smash", 1.5F, rand.nextFloat() * 0.4F + 0.6F); + this.worldObj.playSoundEffect(this.posX, this.posY, this.posZ, "random.fizz", 1.2F, 1.0f); double range = 3.0d * blastMultiplier; @@ -65,12 +86,14 @@ public class EntitySmokeBomb extends EntityBomb { // Gives the target blindness if it is a player, mind trick otherwise (since this has the desired // effect of preventing targeting) if(target instanceof EntityPlayer){ - target.addPotionEffect(new PotionEffect(MobEffects.BLINDNESS, 120, 0)); + target.addPotionEffect(new PotionEffect(Potion.blindness.id, 120, 0)); }else if(target instanceof EntityLiving){ // New AI ((EntityLiving)target).setAttackTarget(null); + // Old AI + if(target instanceof EntityCreature) ((EntityCreature)target).setTarget(null); - target.addPotionEffect(new PotionEffect(WizardryPotions.mind_trick, 120, 0)); + target.addPotionEffect(new PotionEffect(Wizardry.mindTrick.id, 120, 0)); } } } @@ -78,4 +101,26 @@ public class EntitySmokeBomb extends EntityBomb { this.setDead(); } } + + @Override + public void writeSpawnData(ByteBuf buffer) { + buffer.writeFloat(blastMultiplier); + } + + @Override + public void readSpawnData(ByteBuf buffer) { + blastMultiplier = buffer.readFloat(); + } + + @Override + public void readEntityFromNBT(NBTTagCompound nbttagcompound){ + super.readEntityFromNBT(nbttagcompound); + blastMultiplier = nbttagcompound.getFloat("blastMultiplier"); + } + + @Override + public void writeEntityToNBT(NBTTagCompound nbttagcompound){ + super.writeEntityToNBT(nbttagcompound); + nbttagcompound.setFloat("blastMultiplier", blastMultiplier); + } } diff --git a/src/main/java/electroblob/wizardry/entity/projectile/EntitySpark.java b/src/main/java/electroblob/wizardry/entity/projectile/EntitySpark.java index 4061b954..f7ab6078 100644 --- a/src/main/java/electroblob/wizardry/entity/projectile/EntitySpark.java +++ b/src/main/java/electroblob/wizardry/entity/projectile/EntitySpark.java @@ -2,19 +2,21 @@ package electroblob.wizardry.entity.projectile; import java.util.List; +import electroblob.wizardry.EnumParticleType; +import electroblob.wizardry.MagicDamage; import electroblob.wizardry.Wizardry; -import electroblob.wizardry.registry.WizardrySounds; -import electroblob.wizardry.util.MagicDamage; -import electroblob.wizardry.util.MagicDamage.DamageType; -import electroblob.wizardry.util.WizardryParticleType; -import electroblob.wizardry.util.WizardryUtilities; +import electroblob.wizardry.WizardryUtilities; +import electroblob.wizardry.MagicDamage.DamageType; import net.minecraft.entity.Entity; import net.minecraft.entity.EntityLivingBase; -import net.minecraft.util.math.RayTraceResult; +import net.minecraft.entity.monster.EntityCreeper; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.util.AxisAlignedBB; +import net.minecraft.util.MovingObjectPosition; import net.minecraft.world.World; -public class EntitySpark extends EntityMagicProjectile { - +public class EntitySpark extends EntityMagicProjectile +{ public EntitySpark(World par1World) { super(par1World); @@ -36,7 +38,7 @@ public class EntitySpark extends EntityMagicProjectile { } /** This is the speed */ - protected float getSpeed() + protected float func_70182_d() { return 0.5F; } @@ -44,23 +46,23 @@ public class EntitySpark extends EntityMagicProjectile { /** * Called when this EntityThrowable hits a block or entity. */ - protected void onImpact(RayTraceResult par1RayTraceResult) + protected void onImpact(MovingObjectPosition par1MovingObjectPosition) { - Entity entityHit = par1RayTraceResult.entityHit; + Entity entityHit = par1MovingObjectPosition.entityHit; if (entityHit != null){ float damage = 6 * damageMultiplier; - entityHit.attackEntityFrom(MagicDamage.causeIndirectMagicDamage(this, this.getThrower(), DamageType.SHOCK), damage); + entityHit.attackEntityFrom(MagicDamage.causeIndirectEntityMagicDamage(this, this.getThrower(), DamageType.SHOCK), damage); } - this.playSound(WizardrySounds.SPELL_SPARK, 1.0F, 1.2F / (this.rand.nextFloat() * 0.2F + 0.9F)); + this.playSound("wizardry:arc", 1.0F, 1.2F / (this.rand.nextFloat() * 0.2F + 0.9F)); // Particle effect if(worldObj.isRemote){ for(int i=0;i<8;i++){ - Wizardry.proxy.spawnParticle(WizardryParticleType.SPARK, worldObj, this.posX + rand.nextFloat() - 0.5, this.posY + this.height/2 + rand.nextFloat() - 0.5, this.posZ + rand.nextFloat() - 0.5, 0, 0, 0, 3); + Wizardry.proxy.spawnParticle(EnumParticleType.SPARK, worldObj, this.posX + rand.nextFloat() - 0.5, this.posY + this.height/2 + rand.nextFloat() - 0.5, this.posZ + rand.nextFloat() - 0.5, 0, 0, 0, 3); } } diff --git a/src/main/java/electroblob/wizardry/entity/projectile/EntitySparkBomb.java b/src/main/java/electroblob/wizardry/entity/projectile/EntitySparkBomb.java index 9ffedcdf..f852d748 100644 --- a/src/main/java/electroblob/wizardry/entity/projectile/EntitySparkBomb.java +++ b/src/main/java/electroblob/wizardry/entity/projectile/EntitySparkBomb.java @@ -1,28 +1,35 @@ package electroblob.wizardry.entity.projectile; import java.util.List; +import java.util.UUID; +import cpw.mods.fml.common.network.ByteBufUtils; +import cpw.mods.fml.common.registry.IEntityAdditionalSpawnData; +import electroblob.wizardry.EnumParticleType; +import electroblob.wizardry.MagicDamage; import electroblob.wizardry.Wizardry; +import electroblob.wizardry.WizardryUtilities; +import electroblob.wizardry.MagicDamage.DamageType; import electroblob.wizardry.entity.EntityArc; -import electroblob.wizardry.registry.WizardrySounds; -import electroblob.wizardry.util.MagicDamage; -import electroblob.wizardry.util.MagicDamage.DamageType; -import electroblob.wizardry.util.WizardryParticleType; -import electroblob.wizardry.util.WizardryUtilities; import io.netty.buffer.ByteBuf; +import net.minecraft.enchantment.Enchantment; import net.minecraft.entity.Entity; import net.minecraft.entity.EntityLivingBase; +import net.minecraft.entity.monster.EntityCreeper; import net.minecraft.entity.player.EntityPlayer; -import net.minecraft.init.SoundEvents; -import net.minecraft.util.EnumParticleTypes; -import net.minecraft.util.math.RayTraceResult; +import net.minecraft.nbt.NBTTagCompound; +import net.minecraft.util.DamageSource; +import net.minecraft.util.MovingObjectPosition; import net.minecraft.world.World; -public class EntitySparkBomb extends EntityBomb { - +public class EntitySparkBomb extends EntityMagicProjectile implements IEntityAdditionalSpawnData +{ /** For client use, because thrower field is not visible. */ private int casterID; + /** The entity blast multiplier. Only some projectiles cause a blast, which is why this isn't in EntityMagicProjectile. */ + public float blastMultiplier; + public EntitySparkBomb(World par1World) { super(par1World); @@ -35,7 +42,8 @@ public class EntitySparkBomb extends EntityBomb { public EntitySparkBomb(World par1World, EntityLivingBase par2EntityLivingBase, float damageMultiplier, float blastMultiplier) { - super(par1World, par2EntityLivingBase, damageMultiplier, blastMultiplier); + super(par1World, par2EntityLivingBase, damageMultiplier); + this.blastMultiplier = blastMultiplier; } public EntitySparkBomb(World par1World, double par2, double par4, double par6) @@ -46,28 +54,28 @@ public class EntitySparkBomb extends EntityBomb { /** * Called when this EntityThrowable hits a block or entity. */ - protected void onImpact(RayTraceResult par1RayTraceResult) + protected void onImpact(MovingObjectPosition par1MovingObjectPosition) { - this.playSound(SoundEvents.ENTITY_FIREWORK_BLAST_FAR, 0.5f, 0.5f); + this.playSound("fireworks.blast_far", 0.5f, 0.5f); - Entity entityHit = par1RayTraceResult.entityHit; + Entity entityHit = par1MovingObjectPosition.entityHit; if (entityHit != null) { // This is if the spark bomb gets a direct hit float damage = 6 * damageMultiplier; - this.playSound(SoundEvents.ENTITY_GENERIC_HURT, 1.0F, 1.2F / (this.rand.nextFloat() * 0.2F + 0.9F)); + this.playSound("game.neutral.hurt", 1.0F, 1.2F / (this.rand.nextFloat() * 0.2F + 0.9F)); - entityHit.attackEntityFrom(MagicDamage.causeIndirectMagicDamage(this, this.getThrower(), DamageType.SHOCK).setProjectile(), damage); + entityHit.attackEntityFrom(MagicDamage.causeIndirectEntityMagicDamage(this, this.getThrower(), DamageType.SHOCK).setProjectile(), damage); } // Particle effect if(worldObj.isRemote){ for(int i=0;i<8;i++){ - Wizardry.proxy.spawnParticle(WizardryParticleType.SPARK, worldObj, this.posX + rand.nextFloat() - 0.5, this.posY + this.height/2 + rand.nextFloat() - 0.5, this.posZ + rand.nextFloat() - 0.5, 0, 0, 0, 3); - worldObj.spawnParticle(EnumParticleTypes.SMOKE_LARGE, this.posX + rand.nextFloat() - 0.5, this.posY + this.height/2 + rand.nextFloat() - 0.5, this.posZ + rand.nextFloat() - 0.5, 0, 0, 0); + Wizardry.proxy.spawnParticle(EnumParticleType.SPARK, worldObj, this.posX + rand.nextFloat() - 0.5, this.posY + this.height/2 + rand.nextFloat() - 0.5, this.posZ + rand.nextFloat() - 0.5, 0, 0, 0, 3); + worldObj.spawnParticle("largesmoke", this.posX + rand.nextFloat() - 0.5, this.posY + this.height/2 + rand.nextFloat() - 0.5, this.posZ + rand.nextFloat() - 0.5, 0, 0, 0); } } @@ -93,15 +101,15 @@ public class EntitySparkBomb extends EntityBomb { target.posX, target.posY + target.height/2, target.posZ); this.worldObj.spawnEntityInWorld(arc); - target.playSound(WizardrySounds.SPELL_SPARK, 1.0F, rand.nextFloat() * 0.4F + 1.5F); + worldObj.playSoundAtEntity(target, "wizardry:arc", 1.0F, rand.nextFloat() * 0.4F + 1.5F); - target.attackEntityFrom(MagicDamage.causeIndirectMagicDamage(this, this.getThrower(), DamageType.SHOCK), 5.0f * damageMultiplier); + target.attackEntityFrom(MagicDamage.causeIndirectEntityMagicDamage(this, this.getThrower(), DamageType.SHOCK), 5.0f * damageMultiplier); }else{ // Particle effect for(int j=0;j<8;j++){ - Wizardry.proxy.spawnParticle(WizardryParticleType.SPARK, worldObj, target.posX + rand.nextFloat() - 0.5, target.getEntityBoundingBox().minY + target.height*rand.nextFloat(), target.posZ + rand.nextFloat() - 0.5, 0, 0, 0, 3); - worldObj.spawnParticle(EnumParticleTypes.SMOKE_LARGE, target.posX + rand.nextFloat() - 0.5, target.getEntityBoundingBox().minY + target.height*rand.nextFloat(), target.posZ + rand.nextFloat() - 0.5, 0, 0, 0); + Wizardry.proxy.spawnParticle(EnumParticleType.SPARK, worldObj, target.posX + rand.nextFloat() - 0.5, WizardryUtilities.getEntityFeetPos(target) + target.height*rand.nextFloat(), target.posZ + rand.nextFloat() - 0.5, 0, 0, 0, 3); + worldObj.spawnParticle("largesmoke", target.posX + rand.nextFloat() - 0.5, WizardryUtilities.getEntityFeetPos(target) + target.height*rand.nextFloat(), target.posZ + rand.nextFloat() - 0.5, 0, 0, 0); } } } @@ -111,14 +119,26 @@ public class EntitySparkBomb extends EntityBomb { } @Override - public void writeSpawnData(ByteBuf data){ - super.writeSpawnData(data); + public void writeSpawnData(ByteBuf data) { data.writeInt(this.getThrower().getEntityId()); + data.writeFloat(blastMultiplier); } @Override - public void readSpawnData(ByteBuf data){ - super.readSpawnData(data); + public void readSpawnData(ByteBuf data) { this.casterID = data.readInt(); + this.blastMultiplier = data.readFloat(); + } + + @Override + public void readEntityFromNBT(NBTTagCompound nbttagcompound){ + super.readEntityFromNBT(nbttagcompound); + blastMultiplier = nbttagcompound.getFloat("blastMultiplier"); + } + + @Override + public void writeEntityToNBT(NBTTagCompound nbttagcompound){ + super.writeEntityToNBT(nbttagcompound); + nbttagcompound.setFloat("blastMultiplier", blastMultiplier); } } diff --git a/src/main/java/electroblob/wizardry/entity/projectile/EntityThunderbolt.java b/src/main/java/electroblob/wizardry/entity/projectile/EntityThunderbolt.java index 249dcae2..f211fa0d 100644 --- a/src/main/java/electroblob/wizardry/entity/projectile/EntityThunderbolt.java +++ b/src/main/java/electroblob/wizardry/entity/projectile/EntityThunderbolt.java @@ -1,18 +1,16 @@ package electroblob.wizardry.entity.projectile; +import electroblob.wizardry.EnumParticleType; +import electroblob.wizardry.MagicDamage; import electroblob.wizardry.Wizardry; -import electroblob.wizardry.util.MagicDamage; -import electroblob.wizardry.util.MagicDamage.DamageType; -import electroblob.wizardry.util.WizardryParticleType; +import electroblob.wizardry.MagicDamage.DamageType; import net.minecraft.entity.Entity; import net.minecraft.entity.EntityLivingBase; -import net.minecraft.init.SoundEvents; -import net.minecraft.util.EnumParticleTypes; -import net.minecraft.util.math.RayTraceResult; +import net.minecraft.util.MovingObjectPosition; import net.minecraft.world.World; -public class EntityThunderbolt extends EntityMagicProjectile { - +public class EntityThunderbolt extends EntityMagicProjectile +{ public EntityThunderbolt(World par1World) { super(par1World); @@ -34,7 +32,7 @@ public class EntityThunderbolt extends EntityMagicProjectile { } /** This is the speed */ - protected float getSpeed() + protected float func_70182_d() { return 2.5F; } @@ -42,25 +40,25 @@ public class EntityThunderbolt extends EntityMagicProjectile { /** * Called when this EntityThrowable hits a block or entity. */ - protected void onImpact(RayTraceResult par1RayTraceResult) + protected void onImpact(MovingObjectPosition par1MovingObjectPosition) { - Entity entityHit = par1RayTraceResult.entityHit; + Entity entityHit = par1MovingObjectPosition.entityHit; if(entityHit != null){ float damage = 3 * damageMultiplier; - entityHit.attackEntityFrom(MagicDamage.causeIndirectMagicDamage(this, this.getThrower(), DamageType.SHOCK).setProjectile(), damage); + entityHit.attackEntityFrom(MagicDamage.causeIndirectEntityMagicDamage(this, this.getThrower(), DamageType.SHOCK).setProjectile(), damage); // Knockback entityHit.addVelocity(this.motionX*0.2, this.motionY*0.2, this.motionZ*0.2); } - this.playSound(SoundEvents.ENTITY_FIREWORK_LARGE_BLAST, 1.4F, 0.5f + this.rand.nextFloat() * 0.1F); + this.playSound("fireworks.largeBlast", 1.4F, 0.5f + this.rand.nextFloat() * 0.1F); // Particle effect if(worldObj.isRemote){ - worldObj.spawnParticle(EnumParticleTypes.EXPLOSION_LARGE, this.posX, this.posY, this.posZ, 0, 0, 0); + worldObj.spawnParticle("largeexplode", this.posX, this.posY, this.posZ, 0, 0, 0); } this.setDead(); @@ -71,9 +69,9 @@ public class EntityThunderbolt extends EntityMagicProjectile { super.onUpdate(); if(worldObj.isRemote){ - Wizardry.proxy.spawnParticle(WizardryParticleType.SPARK, worldObj, this.posX + rand.nextFloat()*0.2 - 0.1, this.posY + this.height/2 + rand.nextFloat()*0.2 - 0.1, this.posZ + rand.nextFloat()*0.2 - 0.1, 0, 0, 0, 3); + Wizardry.proxy.spawnParticle(EnumParticleType.SPARK, worldObj, this.posX + rand.nextFloat()*0.2 - 0.1, this.posY + this.height/2 + rand.nextFloat()*0.2 - 0.1, this.posZ + rand.nextFloat()*0.2 - 0.1, 0, 0, 0, 3); for(int i=0; i<4; i++){ - worldObj.spawnParticle(EnumParticleTypes.SMOKE_NORMAL, this.posX + rand.nextFloat()*0.2 - 0.1, this.posY + this.height/2 + rand.nextFloat()*0.2 - 0.1, this.posZ + rand.nextFloat()*0.2 - 0.1, 0, 0, 0); + worldObj.spawnParticle("smoke", this.posX + rand.nextFloat()*0.2 - 0.1, this.posY + this.height/2 + rand.nextFloat()*0.2 - 0.1, this.posZ + rand.nextFloat()*0.2 - 0.1, 0, 0, 0); } } diff --git a/src/main/java/electroblob/wizardry/event/DiscoverSpellEvent.java b/src/main/java/electroblob/wizardry/event/DiscoverSpellEvent.java deleted file mode 100644 index 066ae73d..00000000 --- a/src/main/java/electroblob/wizardry/event/DiscoverSpellEvent.java +++ /dev/null @@ -1,49 +0,0 @@ -package electroblob.wizardry.event; - -import electroblob.wizardry.spell.Spell; -import net.minecraft.entity.player.EntityPlayer; -import net.minecraftforge.common.MinecraftForge; -import net.minecraftforge.event.entity.player.PlayerEvent; -import net.minecraftforge.fml.common.eventhandler.Cancelable; - -/** - * DiscoverSpellEvent is fired when a player discovers a spell by any method.
    - *
    - * This event is {@link Cancelable}. If this event is canceled, the spell is not discovered.
    - *
    - * This event does not have a result. {@link HasResult}
    - *
    - * This event is fired on the {@link MinecraftForge#EVENT_BUS}. - * - * @author Electroblob - * @since Wizardry 2.1 - */ -public class DiscoverSpellEvent extends PlayerEvent { - - private final Spell spell; - private final Source source; - - public DiscoverSpellEvent(EntityPlayer player, Spell spell, Source source) { - super(player); - this.spell = spell; - this.source = source; - } - - /** Returns the spell that is being discovered. */ - public Spell getSpell(){ - return spell; - } - - /** Returns the method used to discover the spell. */ - public Source getSource(){ - return source; - } - - public enum Source { - /** Signifies that the spell was discovered by trying to cast it. */ CASTING, - /** Signifies that the spell was discovered using a scroll of identification. */ IDENTIFICATION_SCROLL, - /** Signifies that the spell was discovered using commands. */ COMMAND, - /** Signifies that the spell was discovered by some other means. */ OTHER - } - -} \ No newline at end of file diff --git a/src/main/java/electroblob/wizardry/event/SpellBindEvent.java b/src/main/java/electroblob/wizardry/event/SpellBindEvent.java deleted file mode 100644 index dd6cf322..00000000 --- a/src/main/java/electroblob/wizardry/event/SpellBindEvent.java +++ /dev/null @@ -1,31 +0,0 @@ -package electroblob.wizardry.event; - -import electroblob.wizardry.tileentity.ContainerArcaneWorkbench; -import net.minecraft.entity.player.EntityPlayer; -import net.minecraftforge.common.MinecraftForge; -import net.minecraftforge.event.entity.player.PlayerContainerEvent; -import net.minecraftforge.fml.common.eventhandler.Cancelable; -import net.minecraftforge.fml.common.eventhandler.Event.HasResult; - -/** - * SpellBindEvent is fired when a player presses the apply button in the arcane workbench. Note that this - * event is only fired on the server side.
    - *
    - * This event is {@link Cancelable}. If this event is canceled, no further processing takes place: spells are not bound, - * upgrades are not applied and crystals are not consumed.
    - *
    - * This event does not have a result. {@link HasResult}
    - *
    - * This event is fired on the {@link MinecraftForge#EVENT_BUS}. - * - * @author Electroblob - * @since Wizardry 2.1 - */ -@Cancelable -public class SpellBindEvent extends PlayerContainerEvent { - - public SpellBindEvent(EntityPlayer player, ContainerArcaneWorkbench container) { - super(player, container); - } - -} \ No newline at end of file diff --git a/src/main/java/electroblob/wizardry/event/SpellCastEvent.java b/src/main/java/electroblob/wizardry/event/SpellCastEvent.java deleted file mode 100644 index c1072eda..00000000 --- a/src/main/java/electroblob/wizardry/event/SpellCastEvent.java +++ /dev/null @@ -1,139 +0,0 @@ -package electroblob.wizardry.event; - -import electroblob.wizardry.entity.living.ISpellCaster; -import electroblob.wizardry.spell.Spell; -import electroblob.wizardry.util.SpellModifiers; -import net.minecraft.entity.EntityLivingBase; -import net.minecraftforge.common.MinecraftForge; -import net.minecraftforge.event.entity.living.LivingEvent; -import net.minecraftforge.fml.common.eventhandler.Cancelable; - -/** - * SpellCastEvent is the parent event class for all spell casting events. Methods which subscribe to this event - * will receive all three child events (it is recommended that you use {@link SpellCastEvent.Pre}, - * {@link SpellCastEvent.Post} or {@link SpellCastEvent.Tick}, depending on the application).
    - *
    - * This event is fired on the {@link MinecraftForge#EVENT_BUS}. - * - * @author Electroblob - * @since Wizardry 2.1 - */ -public abstract class SpellCastEvent extends LivingEvent { - - private final Spell spell; - private final SpellModifiers modifiers; - private final Source source; - - public SpellCastEvent(EntityLivingBase caster, Spell spell, SpellModifiers modifiers, Source source) { - super(caster); - this.spell = spell; - this.modifiers = modifiers; - this.source = source; - } - - /** Returns the spell being cast. */ - public Spell getSpell(){ - return spell; - } - - /** Returns the modifiers for the spell being cast. */ - public SpellModifiers getModifiers(){ - return modifiers; - } - - /** Returns the source of the spell being cast. */ - public Source getSource(){ - return source; - } - - public enum Source { - /** Signifies that the spell was cast using a wand. */ WAND, - /** Signifies that the spell was cast using a scroll. */ SCROLL, - /** Signifies that the spell was cast using commands. */ COMMAND, - /** Signifies that the spell was cast by an {@link ISpellCaster}. */ NPC, - /** Signifies that the spell was cast by some other means. */ OTHER - } - - /** - * SpellCastEvent.Pre is fired just before a spell is cast. Use this event to change the spell modifiers and - * generally alter the behaviour of the spell, or stop it from being cast entirely. For example, wizardry uses this - * event to cancel spells cast by entities that have the arcane jammer effect. Note that for wands, this is called - * before mana, tier and cooldowns are checked. Also note that this event is only fired once for continuous - * spells, when they start casting.
    - *
    - * This event is {@link Cancelable}. If this event is canceled, the spell is not cast, mana is not consumed, and the - * right-click action that caused it (if any) returns a result of FAIL, meaning that the right-click is passed to - * the block/entity in front of the player, if any.
    - *
    - * This event does not have a result. {@link HasResult}
    - *
    - * This event is fired on the {@link MinecraftForge#EVENT_BUS}. - * - * @author Electroblob - * @since Wizardry 2.1 - */ - @Cancelable - public static class Pre extends SpellCastEvent { - - public Pre(EntityLivingBase caster, Spell spell, SpellModifiers modifiers, Source source) { - super(caster, spell, modifiers, source); - } - - } - - /** - * SpellCastEvent.Post is fired just after a spell is cast. Use this event for any processing which depends - * on whether the spell succeeds, and does not affect the spell itself. For example, wizardry uses this event - * to keep track of spellcasting stats. Note that although this event is fired from both sides, it is not fired from - * common code; rather, both sides fire it separately, so timing is not guaranteed. Also note that this event is - * only fired once for continuous spells, after the first casting tick.
    - *
    - * This event is not {@link Cancelable}.
    - *
    - * This event does not have a result. {@link HasResult}
    - *
    - * This event is fired on the {@link MinecraftForge#EVENT_BUS}. - * - * @author Electroblob - * @since Wizardry 2.1 - */ - public static class Post extends SpellCastEvent { - - public Post(EntityLivingBase caster, Spell spell, SpellModifiers modifiers, Source source) { - super(caster, spell, modifiers, source); - } - - } - - /** - * SpellCastEvent.Tick is fired each tick while a continuous spell is being cast.
    - *
    - * This event is {@link Cancelable}. If this event is canceled, the spell is not cast, mana is not consumed, and the - * spell casting is interrupted. Cancelling this event on the client side will stop particles being spawned, but - * will not interrupt spell casting.
    - *
    - * This event does not have a result. {@link HasResult}
    - *
    - * This event is fired on the {@link MinecraftForge#EVENT_BUS}. - * - * @author Electroblob - * @since Wizardry 2.1 - */ - @Cancelable - public static class Tick extends SpellCastEvent { - - private final int count; - - public Tick(EntityLivingBase caster, Spell spell, SpellModifiers modifiers, Source source, int count) { - super(caster, spell, modifiers, source); - this.count = count; - } - - /** Returns the number of ticks this (continuous) spell has already been cast for. */ - public int getCount(){ - return count; - } - - } - -} diff --git a/src/main/java/electroblob/wizardry/item/IConjuredItem.java b/src/main/java/electroblob/wizardry/item/IConjuredItem.java deleted file mode 100644 index ee8a0845..00000000 --- a/src/main/java/electroblob/wizardry/item/IConjuredItem.java +++ /dev/null @@ -1,57 +0,0 @@ -package electroblob.wizardry.item; - -import net.minecraft.entity.item.EntityItem; -import net.minecraft.item.Item; -import net.minecraft.item.ItemStack; -import net.minecraft.nbt.NBTTagCompound; -import net.minecraftforge.event.entity.item.ItemTossEvent; -import net.minecraftforge.event.entity.living.LivingDropsEvent; -import net.minecraftforge.fml.common.Mod; -import net.minecraftforge.fml.common.eventhandler.SubscribeEvent; - -/** Allows wizardry to identify items that are conjured (and therefore need destroying if they leave the inventory) - * without explicitly referencing each, thereby allowing for better expandibility. */ -@Mod.EventBusSubscriber -public interface IConjuredItem { - - /** The NBT tag key used to store the duration multiplier for conjured items. */ - public static final String DURATION_MULTIPLIER_KEY = "durationMultiplier"; - - /** Helper method for setting the duration multiplier (via NBT) for conjured items. */ - public static void setDurationMultiplier(ItemStack stack, float multiplier){ - if(!stack.hasTagCompound()) stack.setTagCompound(new NBTTagCompound()); - stack.getTagCompound().setFloat(DURATION_MULTIPLIER_KEY, multiplier); - } - - /** Helper method for returning the max damage of a conjured item based on its NBT data. Centralises the code. - * Implementors will almost certainly want to call this from {@link Item#getMaxDamage(ItemStack stack)}. */ - public default int getMaxDamageFromNBT(ItemStack stack){ - if(stack.hasTagCompound() && stack.getTagCompound().hasKey(DURATION_MULTIPLIER_KEY)){ - return (int)(this.getBaseDuration() * stack.getTagCompound().getFloat(DURATION_MULTIPLIER_KEY)); - } - return this.getBaseDuration(); - } - - /** Returns the base duration in ticks for this conjured item. Should be a constant (commonly 600). - * Implementors may want to call this when setting an item's max damage in its constructor. */ - public int getBaseDuration(); - - @SubscribeEvent - public static void onLivingDropsEvent(LivingDropsEvent event){ - // Destroys conjured items if their caster dies. - for(EntityItem item : event.getDrops()){ - if(item.getEntityItem().getItem() instanceof IConjuredItem){ - item.setDead(); - } - } - } - - @SubscribeEvent - public static void onItemTossEvent(ItemTossEvent event){ - // Prevents conjured items being thrown by dragging and dropping outside the inventory. - if(event.getEntityItem().getEntityItem().getItem() instanceof IConjuredItem){ - event.setCanceled(true); - event.getPlayer().inventory.addItemStackToInventory(event.getEntityItem().getEntityItem()); - } - } -} diff --git a/src/main/java/electroblob/wizardry/item/ItemArcaneTome.java b/src/main/java/electroblob/wizardry/item/ItemArcaneTome.java index dd7688d7..0c24bb37 100644 --- a/src/main/java/electroblob/wizardry/item/ItemArcaneTome.java +++ b/src/main/java/electroblob/wizardry/item/ItemArcaneTome.java @@ -2,15 +2,14 @@ package electroblob.wizardry.item; import java.util.List; -import electroblob.wizardry.constants.Tier; -import electroblob.wizardry.registry.WizardryTabs; +import electroblob.wizardry.EnumTier; +import electroblob.wizardry.Wizardry; import net.minecraft.creativetab.CreativeTabs; import net.minecraft.entity.player.EntityPlayer; import net.minecraft.item.EnumRarity; import net.minecraft.item.Item; import net.minecraft.item.ItemStack; -import net.minecraftforge.fml.relauncher.Side; -import net.minecraftforge.fml.relauncher.SideOnly; +import net.minecraft.util.StatCollector; public class ItemArcaneTome extends Item { @@ -18,42 +17,41 @@ public class ItemArcaneTome extends Item { super(); this.setHasSubtypes(true); this.setMaxStackSize(1); - this.setCreativeTab(WizardryTabs.WIZARDRY); + this.setCreativeTab(Wizardry.tabWizardry); + this.setTextureName("wizardry:arcane_tome"); + this.setUnlocalizedName("arcaneTome"); } @Override - @SideOnly(Side.CLIENT) - public void getSubItems(Item item, CreativeTabs tab, List list){ - for(int i=1; i tooltip, boolean showAdvanced){ - Tier tier = Tier.values()[stack.getItemDamage()]; - Tier tier2 = Tier.values()[stack.getItemDamage()-1]; - tooltip.add(tier.getDisplayNameWithFormatting()); - tooltip.add("\u00A77" + net.minecraft.client.resources.I18n.format("item.wizardry:arcane_tome.desc1", tier2.getDisplayNameWithFormatting())); - tooltip.add("\u00A77" + net.minecraft.client.resources.I18n.format("item.wizardry:arcane_tome.desc2", tier.getDisplayNameWithFormatting() + "\u00A77")); + public void addInformation(ItemStack par1ItemStack, EntityPlayer par2EntityPlayer, List par3List, boolean par4){ + EnumTier tier = EnumTier.values()[par1ItemStack.getItemDamage()]; + EnumTier tier2 = EnumTier.values()[par1ItemStack.getItemDamage()-1]; + par3List.add(tier.getDisplayNameWithFormatting()); + par3List.add("\u00A77" + StatCollector.translateToLocalFormatted("item.arcaneTome.desc1", tier2.getDisplayNameWithFormatting())); + par3List.add("\u00A77" + StatCollector.translateToLocalFormatted("item.arcaneTome.desc2", tier.getDisplayNameWithFormatting() + "\u00A77")); } } diff --git a/src/main/java/electroblob/wizardry/item/ItemArmourUpgrade.java b/src/main/java/electroblob/wizardry/item/ItemArmourUpgrade.java index b7b96f10..de8866ff 100644 --- a/src/main/java/electroblob/wizardry/item/ItemArmourUpgrade.java +++ b/src/main/java/electroblob/wizardry/item/ItemArmourUpgrade.java @@ -2,45 +2,44 @@ package electroblob.wizardry.item; import java.util.List; -import electroblob.wizardry.registry.WizardryTabs; +import electroblob.wizardry.EnumTier; +import electroblob.wizardry.Wizardry; import net.minecraft.creativetab.CreativeTabs; import net.minecraft.entity.player.EntityPlayer; import net.minecraft.item.EnumRarity; import net.minecraft.item.Item; import net.minecraft.item.ItemStack; -import net.minecraftforge.fml.relauncher.Side; -import net.minecraftforge.fml.relauncher.SideOnly; +import net.minecraft.util.StatCollector; -public class ItemArmourUpgrade extends Item { +public class ItemArmourUpgrade extends Item{ public ItemArmourUpgrade() { super(); this.setMaxStackSize(1); - this.setCreativeTab(WizardryTabs.WIZARDRY); + this.setCreativeTab(Wizardry.tabWizardry); + this.setTextureName("wizardry:armour_upgrade"); } + /** + * Return an item rarity from EnumRarity + */ @Override - public EnumRarity getRarity(ItemStack stack){ - return EnumRarity.EPIC; + public EnumRarity getRarity(ItemStack par1ItemStack) + { + return EnumRarity.epic; } @Override - @SideOnly(Side.CLIENT) - public boolean hasEffect(ItemStack stack){ + public boolean hasEffect(ItemStack stack, int par2){ return true; } @Override - @SideOnly(Side.CLIENT) - public void addInformation(ItemStack stack, EntityPlayer player, List tooltip, boolean advanced){ - tooltip.add(net.minecraft.client.resources.I18n.format("item.wizardry:armour_upgrade.desc1", "\u00A77")); - tooltip.add(net.minecraft.client.resources.I18n.format("item.wizardry:armour_upgrade.desc2", "\u00A77", "\u00A7d")); + public void addInformation(ItemStack par1ItemStack, EntityPlayer par2EntityPlayer, List par3List, boolean par4){ + par3List.add(StatCollector.translateToLocalFormatted("item.armourUpgrade.desc1", "\u00A77")); + par3List.add(StatCollector.translateToLocalFormatted("item.armourUpgrade.desc2", "\u00A77", "\u00A7d")); + //par3List.add("\u00A77Upgrades any wizard armour"); + //par3List.add("\u00A77to make it \u00A7dlegendary"); } - - @Override - @SideOnly(Side.CLIENT) - public void getSubItems(Item item, CreativeTabs tab, List subItems){ - subItems.add(new ItemStack(this, 1)); - } } diff --git a/src/main/java/electroblob/wizardry/item/ItemFirebomb.java b/src/main/java/electroblob/wizardry/item/ItemFirebomb.java index add289ef..b8611509 100644 --- a/src/main/java/electroblob/wizardry/item/ItemFirebomb.java +++ b/src/main/java/electroblob/wizardry/item/ItemFirebomb.java @@ -1,50 +1,37 @@ package electroblob.wizardry.item; -import java.util.List; - +import electroblob.wizardry.Wizardry; import electroblob.wizardry.entity.projectile.EntityFirebomb; -import electroblob.wizardry.registry.WizardryTabs; -import net.minecraft.creativetab.CreativeTabs; import net.minecraft.entity.player.EntityPlayer; -import net.minecraft.init.SoundEvents; import net.minecraft.item.Item; import net.minecraft.item.ItemStack; -import net.minecraft.util.ActionResult; -import net.minecraft.util.EnumActionResult; -import net.minecraft.util.EnumHand; import net.minecraft.world.World; -import net.minecraftforge.fml.relauncher.Side; -import net.minecraftforge.fml.relauncher.SideOnly; public class ItemFirebomb extends Item { - public ItemFirebomb(){ + public ItemFirebomb() + { this.maxStackSize = 16; - this.setCreativeTab(WizardryTabs.WIZARDRY); + this.setCreativeTab(Wizardry.tabWizardry); } - @Override - public ActionResult onItemRightClick(ItemStack stack, World world, EntityPlayer player, EnumHand hand){ - - if(!player.capabilities.isCreativeMode){ + /** + * Called whenever this item is equipped and the right mouse button is pressed. Args: itemStack, world, entityPlayer + */ + public ItemStack onItemRightClick(ItemStack stack, World world, EntityPlayer entityplayer) + { + if (!entityplayer.capabilities.isCreativeMode) + { --stack.stackSize; } - player.playSound(SoundEvents.ENTITY_SNOWBALL_THROW, 0.5F, 0.4F / (itemRand.nextFloat() * 0.4F + 0.8F)); + world.playSoundAtEntity(entityplayer, "random.bow", 0.5F, 0.4F / (itemRand.nextFloat() * 0.4F + 0.8F)); - if(!world.isRemote){ - EntityFirebomb firebomb = new EntityFirebomb(world, player); - // This is the standard set of parameters for this method, used by snowballs and ender pearls. - firebomb.setHeadingFromThrower(player, player.rotationPitch, player.rotationYaw, 0.0f, 1.5f, 1.0f); - world.spawnEntityInWorld(firebomb); + if (!world.isRemote) + { + world.spawnEntityInWorld(new EntityFirebomb(world, entityplayer)); } - return ActionResult.newResult(EnumActionResult.SUCCESS, stack); - } - - @Override - @SideOnly(Side.CLIENT) - public void getSubItems(Item parItem, CreativeTabs parTab, List parListSubItems){ - parListSubItems.add(new ItemStack(this, 1)); + return stack; } } \ No newline at end of file diff --git a/src/main/java/electroblob/wizardry/item/ItemFlamingAxe.java b/src/main/java/electroblob/wizardry/item/ItemFlamingAxe.java index 28e1400b..d0e113c2 100644 --- a/src/main/java/electroblob/wizardry/item/ItemFlamingAxe.java +++ b/src/main/java/electroblob/wizardry/item/ItemFlamingAxe.java @@ -1,10 +1,10 @@ package electroblob.wizardry.item; -import java.util.List; - -import electroblob.wizardry.util.MagicDamage; -import electroblob.wizardry.util.MagicDamage.DamageType; -import net.minecraft.creativetab.CreativeTabs; +import electroblob.wizardry.ExtendedPlayer; +import electroblob.wizardry.MagicDamage; +import electroblob.wizardry.Wizardry; +import electroblob.wizardry.MagicDamage.DamageType; +import net.minecraft.client.Minecraft; import net.minecraft.entity.Entity; import net.minecraft.entity.EntityLivingBase; import net.minecraft.entity.player.EntityPlayer; @@ -12,81 +12,72 @@ import net.minecraft.item.Item; import net.minecraft.item.ItemAxe; import net.minecraft.item.ItemStack; import net.minecraft.world.World; -import net.minecraftforge.fml.relauncher.Side; -import net.minecraftforge.fml.relauncher.SideOnly; -public class ItemFlamingAxe extends ItemAxe implements IConjuredItem { +public class ItemFlamingAxe extends ItemAxe { public ItemFlamingAxe(ToolMaterial material) { - super(material, 8, -3); - this.setMaxDamage(getBaseDuration()); + super(material); + this.setMaxDamage(600); this.setNoRepair(); this.setCreativeTab(null); } - - @Override - public int getBaseDuration(){ - return 600; - } @Override - public int getMaxDamage(ItemStack stack){ - return this.getMaxDamageFromNBT(stack); + public int getMaxDamage(ItemStack stack) + { + return stack.hasTagCompound()? (int)(getMaxDamage()*stack.stackTagCompound.getFloat("durationMultiplier")) : getMaxDamage(); } @Override - // This method allows the code for the item's timer to be greatly simplified by damaging it directly from - // onUpdate() and removing the workaround that involved WizardData and all sorts of crazy stuff. - public boolean shouldCauseReequipAnimation(ItemStack oldStack, ItemStack newStack, boolean slotChanged){ - - if(oldStack != null || newStack != null){ - // We only care about the situation where we specifically want the animation NOT to play. - if(oldStack.getItem() == newStack.getItem() && !slotChanged) return false; + public void onUpdate(ItemStack itemstack, World par2World, Entity entity, int par4, boolean par5) { + // Allows the 'cheaty' damage bar rendering code to start working. + if(itemstack.getItemDamage() == 0){ + itemstack.setItemDamage(1); + } + if(entity instanceof EntityPlayer){ + ExtendedPlayer properties = ExtendedPlayer.get((EntityPlayer)entity); + properties.flamingAxeDuration++; + if(properties.flamingAxeDuration > this.getMaxDamage(itemstack)){ + ((EntityPlayer)entity).inventory.consumeInventoryItem(itemstack.getItem()); + properties.flamingAxeDuration = 0; + } } - - return super.shouldCauseReequipAnimation(oldStack, newStack, slotChanged); } @Override - public void onUpdate(ItemStack stack, World world, Entity entity, int slot, boolean selected){ - int damage = stack.getItemDamage(); - if(damage > stack.getMaxDamage()) entity.replaceItemInInventory(slot, null); - stack.setItemDamage(damage + 1); - } - - @Override - public boolean hitEntity(ItemStack stack, EntityLivingBase target, EntityLivingBase wielder){ + public boolean hitEntity(ItemStack stack, EntityLivingBase target, EntityLivingBase wielder) + { if(!MagicDamage.isEntityImmune(DamageType.FIRE, target)) target.setFire(8); return false; } @Override - @SideOnly(Side.CLIENT) - public boolean hasEffect(ItemStack stack){ + public int getDisplayDamage(ItemStack stack){ + return Wizardry.proxy.getConjuredItemDisplayDamage(stack); + } + + @Override + public boolean hasEffect(ItemStack par1ItemStack, int pass){ return true; } @Override - public boolean getIsRepairable(ItemStack stack, ItemStack par2ItemStack){ + public boolean getIsRepairable(ItemStack par1ItemStack, ItemStack par2ItemStack) + { return false; } @Override - public int getItemEnchantability(){ + public int getItemEnchantability() + { return 0; } - // Cannot be dropped + //Cannot be dropped @Override - public boolean onDroppedByPlayer(ItemStack item, EntityPlayer player){ + public boolean onDroppedByPlayer(ItemStack item, EntityPlayer player) + { return false; } - - @Override - @SideOnly(Side.CLIENT) - // Why does this method pass in an item as an argument? It is always the same item the method is invoked on. - public void getSubItems(Item item, CreativeTabs tab, List items){ - items.add(new ItemStack(this, 1)); - } } diff --git a/src/main/java/electroblob/wizardry/item/ItemFrostAxe.java b/src/main/java/electroblob/wizardry/item/ItemFrostAxe.java index 0e6100e1..b380713c 100644 --- a/src/main/java/electroblob/wizardry/item/ItemFrostAxe.java +++ b/src/main/java/electroblob/wizardry/item/ItemFrostAxe.java @@ -1,94 +1,87 @@ package electroblob.wizardry.item; -import java.util.List; - -import electroblob.wizardry.registry.WizardryPotions; -import electroblob.wizardry.util.MagicDamage; -import electroblob.wizardry.util.MagicDamage.DamageType; -import net.minecraft.creativetab.CreativeTabs; +import electroblob.wizardry.ExtendedPlayer; +import electroblob.wizardry.MagicDamage; +import electroblob.wizardry.Wizardry; +import electroblob.wizardry.MagicDamage.DamageType; +import net.minecraft.client.Minecraft; import net.minecraft.entity.Entity; import net.minecraft.entity.EntityLivingBase; import net.minecraft.entity.player.EntityPlayer; -import net.minecraft.item.Item; import net.minecraft.item.ItemAxe; import net.minecraft.item.ItemStack; import net.minecraft.potion.PotionEffect; import net.minecraft.world.World; -import net.minecraftforge.fml.relauncher.Side; -import net.minecraftforge.fml.relauncher.SideOnly; -public class ItemFrostAxe extends ItemAxe implements IConjuredItem { +public class ItemFrostAxe extends ItemAxe { public ItemFrostAxe(ToolMaterial material) { - super(material, 8, -3); - this.setMaxDamage(getBaseDuration()); + super(material); + this.setMaxDamage(600); this.setNoRepair(); this.setCreativeTab(null); } @Override - public int getBaseDuration(){ - return 600; - } - - @Override - public int getMaxDamage(ItemStack stack){ - return this.getMaxDamageFromNBT(stack); + public int getMaxDamage(ItemStack stack) + { + return stack.hasTagCompound()? (int)(getMaxDamage()*stack.stackTagCompound.getFloat("durationMultiplier")) : getMaxDamage(); } @Override - // This method allows the code for the item's timer to be greatly simplified by damaging it directly from - // onUpdate() and removing the workaround that involved WizardData and all sorts of crazy stuff. - public boolean shouldCauseReequipAnimation(ItemStack oldStack, ItemStack newStack, boolean slotChanged){ - - if(oldStack != null || newStack != null){ - // We only care about the situation where we specifically want the animation NOT to play. - if(oldStack.getItem() == newStack.getItem() && !slotChanged) return false; + public void onUpdate(ItemStack itemstack, World par2World, Entity entity, int par4, boolean par5) { + // Allows the 'cheaty' damage bar rendering code to start working. + if(itemstack.getItemDamage() == 0){ + itemstack.setItemDamage(1); + } + if(entity instanceof EntityPlayer){ + ExtendedPlayer properties = ExtendedPlayer.get((EntityPlayer)entity); + properties.frostAxeDuration++; + if(properties.frostAxeDuration > this.getMaxDamage(itemstack)){ + ((EntityPlayer)entity).inventory.consumeInventoryItem(itemstack.getItem()); + properties.frostAxeDuration = 0; + } } - - return super.shouldCauseReequipAnimation(oldStack, newStack, slotChanged); - } - - @Override - public void onUpdate(ItemStack stack, World world, Entity entity, int slot, boolean selected){ - int damage = stack.getItemDamage(); - if(damage > stack.getMaxDamage()) entity.replaceItemInInventory(slot, null); - stack.setItemDamage(damage + 1); } + /** + * Current implementations of this method in child classes do not use the entry argument beside ev. They just raise + * the damage on the stack. + */ @Override public boolean hitEntity(ItemStack stack, EntityLivingBase target, EntityLivingBase wielder) { - if(!MagicDamage.isEntityImmune(DamageType.FROST, target)) target.addPotionEffect(new PotionEffect(WizardryPotions.frost, 160, 1)); + if(!MagicDamage.isEntityImmune(DamageType.FROST, target)) target.addPotionEffect(new PotionEffect(Wizardry.frost.id, 160, 1)); return false; } @Override - @SideOnly(Side.CLIENT) - public boolean hasEffect(ItemStack stack){ + public int getDisplayDamage(ItemStack stack){ + return Wizardry.proxy.getConjuredItemDisplayDamage(stack); + } + + @Override + public boolean hasEffect(ItemStack par1ItemStack, int pass){ return true; } @Override - public boolean getIsRepairable(ItemStack stack, ItemStack par2ItemStack){ + public boolean getIsRepairable(ItemStack par1ItemStack, ItemStack par2ItemStack) + { return false; } @Override - public int getItemEnchantability(){ + public int getItemEnchantability() + { return 0; } - // Cannot be dropped + //Cannot be dropped @Override - public boolean onDroppedByPlayer(ItemStack item, EntityPlayer player){ + public boolean onDroppedByPlayer(ItemStack item, EntityPlayer player) + { return false; } - - @Override - @SideOnly(Side.CLIENT) - public void getSubItems(Item parItem, CreativeTabs parTab, List parListSubItems){ - parListSubItems.add(new ItemStack(this, 1)); - } } diff --git a/src/main/java/electroblob/wizardry/item/ItemIdentificationScroll.java b/src/main/java/electroblob/wizardry/item/ItemIdentificationScroll.java index 265bf225..f4ef13cd 100644 --- a/src/main/java/electroblob/wizardry/item/ItemIdentificationScroll.java +++ b/src/main/java/electroblob/wizardry/item/ItemIdentificationScroll.java @@ -2,84 +2,81 @@ package electroblob.wizardry.item; import java.util.List; -import electroblob.wizardry.WizardData; -import electroblob.wizardry.event.DiscoverSpellEvent; -import electroblob.wizardry.registry.WizardryAchievements; -import electroblob.wizardry.registry.WizardryTabs; +import electroblob.wizardry.EnumParticleType; +import electroblob.wizardry.ExtendedPlayer; +import electroblob.wizardry.Wizardry; +import electroblob.wizardry.WizardryUtilities; import electroblob.wizardry.spell.Spell; -import electroblob.wizardry.util.WizardryUtilities; -import net.minecraft.creativetab.CreativeTabs; +import net.minecraft.enchantment.EnchantmentHelper; import net.minecraft.entity.player.EntityPlayer; -import net.minecraft.init.SoundEvents; +import net.minecraft.inventory.ContainerPlayer; +import net.minecraft.inventory.Slot; import net.minecraft.item.Item; +import net.minecraft.item.ItemBow; import net.minecraft.item.ItemStack; -import net.minecraft.util.ActionResult; -import net.minecraft.util.EnumActionResult; -import net.minecraft.util.EnumHand; -import net.minecraft.util.text.TextComponentTranslation; +import net.minecraft.item.ItemSword; +import net.minecraft.util.ChatComponentTranslation; +import net.minecraft.util.StatCollector; import net.minecraft.world.World; -import net.minecraftforge.common.MinecraftForge; -import net.minecraftforge.fml.relauncher.Side; -import net.minecraftforge.fml.relauncher.SideOnly; public class ItemIdentificationScroll extends Item { - + public ItemIdentificationScroll() { super(); - this.setCreativeTab(WizardryTabs.WIZARDRY); + this.setTextureName("wizardry:identification_scroll"); + this.setUnlocalizedName("identification_scroll"); + this.setCreativeTab(Wizardry.tabWizardry); } - + @Override - @SideOnly(Side.CLIENT) public boolean hasEffect(ItemStack stack){ return true; } - + @Override - @SideOnly(Side.CLIENT) - public void addInformation(ItemStack stack, EntityPlayer player, List tooltip, boolean par4){ - tooltip.add(net.minecraft.client.resources.I18n.format("item.wizardry:identification_scroll.desc1", "\u00A77")); - tooltip.add(net.minecraft.client.resources.I18n.format("item.wizardry:identification_scroll.desc2", "\u00A77")); + public void addInformation(ItemStack par1ItemStack, EntityPlayer par2EntityPlayer, List par3List, boolean par4){ + par3List.add(StatCollector.translateToLocalFormatted("item.identification_scroll.desc1", "\u00A77")); + par3List.add(StatCollector.translateToLocalFormatted("item.identification_scroll.desc2", "\u00A77")); } - + @Override - public ActionResult onItemRightClick(ItemStack stack, World world, EntityPlayer player, EnumHand hand){ + public ItemStack onItemRightClick(ItemStack stack, World world, EntityPlayer player){ + + if(ExtendedPlayer.get(player) != null){ + + ExtendedPlayer properties = ExtendedPlayer.get(player); - if(WizardData.get(player) != null){ + // Isolates just the hotbar + List hotbar = ((ContainerPlayer)player.openContainer).inventorySlots.subList(36, 45); - WizardData properties = WizardData.get(player); + for(Object slot : hotbar){ - for(ItemStack stack1 : WizardryUtilities.getPrioritisedHotbarAndOffhand(player)){ + if(slot instanceof Slot){ - if(stack1 != null){ - Spell spell = Spell.get(stack1.getItemDamage()); - if((stack1.getItem() instanceof ItemSpellBook || stack1.getItem() instanceof ItemScroll) - && !properties.hasSpellBeenDiscovered(spell)){ + ItemStack stack1 = ((Slot)slot).getStack(); - if(!MinecraftForge.EVENT_BUS.post(new DiscoverSpellEvent(player, spell, DiscoverSpellEvent.Source.IDENTIFICATION_SCROLL))){ + if(stack1 != null){ + Spell spell = Spell.get(stack1.getItemDamage()); + if((stack1.getItem() instanceof ItemSpellBook || stack1.getItem() instanceof ItemScroll) + && !properties.hasSpellBeenDiscovered(spell)){ + // Identification scrolls give the chat readout in creative mode, otherwise it looks like // nothing happens! properties.discoverSpell(spell); - player.addStat(WizardryAchievements.identify_spell); - player.playSound(SoundEvents.ENTITY_PLAYER_LEVELUP, 1.25f, 1); + player.triggerAchievement(Wizardry.identifySpell); + world.playSoundAtEntity(player, "random.levelup", 1.25f, 1); if(!player.capabilities.isCreativeMode) stack.stackSize--; - if(!world.isRemote) player.addChatMessage(new TextComponentTranslation("spell.discover", spell.getNameForTranslationFormatted())); - - return new ActionResult(EnumActionResult.SUCCESS, stack); + if(!world.isRemote) player.addChatMessage(new ChatComponentTranslation("spell.discover", spell.getDisplayNameWithFormatting())); + + return stack; } } } } // If it found nothing to identify, it says so! - if(!world.isRemote) player.addChatMessage(new TextComponentTranslation("item.wizardry:identification_scroll.nothing_to_identify")); + if(!world.isRemote) player.addChatMessage(new ChatComponentTranslation("item.identification_scroll.nothing_to_identify")); } - - return new ActionResult(EnumActionResult.FAIL, stack); - } - - @Override - @SideOnly(Side.CLIENT) - public void getSubItems(Item parItem, CreativeTabs parTab, List parListSubItems){ - parListSubItems.add(new ItemStack(this, 1)); + + return stack; } } diff --git a/src/main/java/electroblob/wizardry/item/ItemPoisonBomb.java b/src/main/java/electroblob/wizardry/item/ItemPoisonBomb.java index 94261841..96b4c9f0 100644 --- a/src/main/java/electroblob/wizardry/item/ItemPoisonBomb.java +++ b/src/main/java/electroblob/wizardry/item/ItemPoisonBomb.java @@ -1,51 +1,37 @@ package electroblob.wizardry.item; -import java.util.List; - +import electroblob.wizardry.Wizardry; import electroblob.wizardry.entity.projectile.EntityPoisonBomb; -import electroblob.wizardry.registry.WizardryTabs; -import net.minecraft.creativetab.CreativeTabs; import net.minecraft.entity.player.EntityPlayer; -import net.minecraft.init.SoundEvents; import net.minecraft.item.Item; import net.minecraft.item.ItemStack; -import net.minecraft.util.ActionResult; -import net.minecraft.util.EnumActionResult; -import net.minecraft.util.EnumHand; import net.minecraft.world.World; -import net.minecraftforge.fml.relauncher.Side; -import net.minecraftforge.fml.relauncher.SideOnly; public class ItemPoisonBomb extends Item { - public ItemPoisonBomb(){ + public ItemPoisonBomb() + { this.maxStackSize = 16; - this.setCreativeTab(WizardryTabs.WIZARDRY); + this.setCreativeTab(Wizardry.tabWizardry); } - @Override - public ActionResult onItemRightClick(ItemStack stack, World world, EntityPlayer player, EnumHand hand){ - - if(!player.capabilities.isCreativeMode){ + /** + * Called whenever this item is equipped and the right mouse button is pressed. Args: itemStack, world, entityPlayer + */ + public ItemStack onItemRightClick(ItemStack stack, World world, EntityPlayer entityplayer) + { + if (!entityplayer.capabilities.isCreativeMode) + { --stack.stackSize; } - player.playSound(SoundEvents.ENTITY_SNOWBALL_THROW, 0.5F, 0.4F / (itemRand.nextFloat() * 0.4F + 0.8F)); + world.playSoundAtEntity(entityplayer, "random.bow", 0.5F, 0.4F / (itemRand.nextFloat() * 0.4F + 0.8F)); - if(!world.isRemote){ - EntityPoisonBomb poisonbomb = new EntityPoisonBomb(world, player); - // This is the standard set of parameters for this method, used by snowballs and ender pearls. - poisonbomb.setHeadingFromThrower(player, player.rotationPitch, player.rotationYaw, 0.0f, 1.5f, 1.0f); - world.spawnEntityInWorld(poisonbomb); + if (!world.isRemote) + { + world.spawnEntityInWorld(new EntityPoisonBomb(world, entityplayer)); } - return ActionResult.newResult(EnumActionResult.SUCCESS, stack); + return stack; } - - @Override - @SideOnly(Side.CLIENT) - public void getSubItems(Item item, CreativeTabs tab, List subItems){ - subItems.add(new ItemStack(this, 1)); - } - } \ No newline at end of file diff --git a/src/main/java/electroblob/wizardry/item/ItemScroll.java b/src/main/java/electroblob/wizardry/item/ItemScroll.java index b77a95f6..79e57aed 100644 --- a/src/main/java/electroblob/wizardry/item/ItemScroll.java +++ b/src/main/java/electroblob/wizardry/item/ItemScroll.java @@ -2,26 +2,25 @@ package electroblob.wizardry.item; import java.util.List; +import cpw.mods.fml.common.network.simpleimpl.IMessage; +import cpw.mods.fml.relauncher.Side; +import cpw.mods.fml.relauncher.SideOnly; +import electroblob.wizardry.ExtendedPlayer; import electroblob.wizardry.Wizardry; -import electroblob.wizardry.event.SpellCastEvent; -import electroblob.wizardry.event.SpellCastEvent.Source; +import electroblob.wizardry.WizardryRegistry; import electroblob.wizardry.packet.PacketCastSpell; import electroblob.wizardry.packet.WizardryPacketHandler; -import electroblob.wizardry.registry.WizardryTabs; import electroblob.wizardry.spell.Spell; -import electroblob.wizardry.util.SpellModifiers; +import net.minecraft.client.gui.FontRenderer; import net.minecraft.creativetab.CreativeTabs; import net.minecraft.entity.player.EntityPlayer; import net.minecraft.item.Item; import net.minecraft.item.ItemStack; -import net.minecraft.util.ActionResult; -import net.minecraft.util.EnumActionResult; -import net.minecraft.util.EnumHand; +import net.minecraft.nbt.NBTTagCompound; +import net.minecraft.nbt.NBTTagIntArray; +import net.minecraft.util.ChatComponentTranslation; +import net.minecraft.util.StatCollector; import net.minecraft.world.World; -import net.minecraftforge.common.MinecraftForge; -import net.minecraftforge.fml.common.network.simpleimpl.IMessage; -import net.minecraftforge.fml.relauncher.Side; -import net.minecraftforge.fml.relauncher.SideOnly; public class ItemScroll extends Item { @@ -29,23 +28,24 @@ public class ItemScroll extends Item { super(); this.setHasSubtypes(true); this.setMaxStackSize(1); - this.setCreativeTab(WizardryTabs.SPELLS); + this.setCreativeTab(Wizardry.tabSpells); + this.setTextureName("wizardry:scroll"); + this.setUnlocalizedName("scroll"); } @Override - @SideOnly(Side.CLIENT) - public void getSubItems(Item item, CreativeTabs par2CreativeTabs, List list){ + public void getSubItems(Item item, CreativeTabs par2CreativeTabs, List list){ + // Isn't this sooooo much neater with the filter thing? for(Spell spell : Spell.getSpells(Spell.nonContinuousSpells)){ list.add(new ItemStack(item, 1, spell.id())); } } @Override - @SideOnly(Side.CLIENT) - public boolean hasEffect(ItemStack stack){ + public boolean hasEffect(ItemStack stack, int par2){ return true; } - + @Override // Item's version of this method is, quite frankly, an abomination. Why is a deprecated method being used as such // an integral part of the code? And what's the point in getUnlocalisedNameInefficiently? @@ -60,61 +60,70 @@ public class ItemScroll extends Item { * Simply put, I can't predict that, and it's not my job to cater for other people's incorrect usage of code, * especially when that might compromise some perfectly reasonable use (think Bibliocraft's 'best guess' book * detection). */ - // TODO: Backport this proxy-based fix. return Wizardry.proxy.getScrollDisplayName(stack); } @Override - public ActionResult onItemRightClick(ItemStack stack, World world, EntityPlayer player, EnumHand hand){ + public ItemStack onItemRightClick(ItemStack stack, World world, EntityPlayer player){ + if(player.isPotionActive(Wizardry.arcaneJammer)) return stack; + Spell spell = Spell.get(stack.getItemDamage()); - // By default, scrolls have no modifiers - but with the event system, they could be added. - SpellModifiers modifiers = new SpellModifiers(); - - // If anything stops the spell working at this point, nothing else happens. - if(MinecraftForge.EVENT_BUS.post(new SpellCastEvent.Pre(player, spell, modifiers, Source.SCROLL))){ - return new ActionResult(EnumActionResult.FAIL, stack); + + // If a spell is disabled in the config, it will not work. + if(!spell.isEnabled()){ + if(!world.isRemote) player.addChatMessage(new ChatComponentTranslation("spell.disabled", spell.getDisplayNameWithFormatting())); + return stack; } - + if(!spell.isContinuous){ - if(!world.isRemote){ - - if(spell.cast(world, player, hand, 0, new SpellModifiers())){ - - MinecraftForge.EVENT_BUS.post(new SpellCastEvent.Post(player, spell, modifiers, Source.SCROLL)); + /* + if(spell.chargeup > 0 && !entityplayer.isUsingItem()){ + // Spells with a chargeup time are now handled separately. + entityplayer.setItemInUse(stack, this.getMaxItemUseDuration(stack)); + return stack; + } + */ + if(!world.isRemote){ + + if(spell.cast(world, player, 0, 1, 1, 1, 1)){ + if(spell.doesSpellRequirePacket()){ // Sends a packet to all players in dimension to tell them to spawn particles. - IMessage msg = new PacketCastSpell.Message(player.getEntityId(), hand, spell.id(), modifiers); - WizardryPacketHandler.net.sendToDimension(msg, world.provider.getDimension()); + IMessage msg = new PacketCastSpell.Message(player.getEntityId(), 0, spell.id(), 1, 1, 1); + WizardryPacketHandler.net.sendToDimension(msg, world.provider.dimensionId); } - - // Scrolls are consumed upon successful use in survival mode + + if(!player.capabilities.isCreativeMode && !ExtendedPlayer.get(player).hasSpellBeenDiscovered(spell) && Wizardry.discoveryMode){ + player.worldObj.playSoundAtEntity(player, "random.levelup", 1.25f, 1); + if(!player.worldObj.isRemote) player.addChatMessage(new ChatComponentTranslation("spell.discover", spell.getDisplayNameWithFormatting())); + } + ExtendedPlayer.get(player).discoverSpell(spell); + if(!player.capabilities.isCreativeMode) stack.stackSize--; - - return new ActionResult(EnumActionResult.SUCCESS, stack); } - - // This else if check was bugging me for AGES! I can't believe I didn't compare to ItemWand before. - }else if(!spell.doesSpellRequirePacket()){ - // Client-inconsistent spell casting. This code only runs client-side. - if(spell.cast(world, player, hand, 0, modifiers)){ - // This is all that needs to happen, because everything above works fine on just the server side. - MinecraftForge.EVENT_BUS.post(new SpellCastEvent.Post(player, spell, modifiers, Source.SCROLL)); - return new ActionResult(EnumActionResult.SUCCESS, stack); + + }else{ + if(spell.cast(world, player, 0, 1, 1, 1, 1)){ + // Added in version 1.1.3 to fix the client-side spell discovery not updating for spells with the + // packet optimisation. + if(ExtendedPlayer.get(player) != null){ + ExtendedPlayer.get(player).discoverSpell(spell); + } } } } - - return new ActionResult(EnumActionResult.FAIL, stack); - + + return stack; + } @Override @SideOnly(Side.CLIENT) - public net.minecraft.client.gui.FontRenderer getFontRenderer(ItemStack stack){ + public FontRenderer getFontRenderer(ItemStack stack){ return Wizardry.proxy.getFontRenderer(stack); } } diff --git a/src/main/java/electroblob/wizardry/item/ItemSmokeBomb.java b/src/main/java/electroblob/wizardry/item/ItemSmokeBomb.java index a2180d97..71128b8a 100644 --- a/src/main/java/electroblob/wizardry/item/ItemSmokeBomb.java +++ b/src/main/java/electroblob/wizardry/item/ItemSmokeBomb.java @@ -1,50 +1,37 @@ package electroblob.wizardry.item; -import java.util.List; - +import electroblob.wizardry.Wizardry; import electroblob.wizardry.entity.projectile.EntitySmokeBomb; -import electroblob.wizardry.registry.WizardryTabs; -import net.minecraft.creativetab.CreativeTabs; import net.minecraft.entity.player.EntityPlayer; -import net.minecraft.init.SoundEvents; import net.minecraft.item.Item; import net.minecraft.item.ItemStack; -import net.minecraft.util.ActionResult; -import net.minecraft.util.EnumActionResult; -import net.minecraft.util.EnumHand; import net.minecraft.world.World; -import net.minecraftforge.fml.relauncher.Side; -import net.minecraftforge.fml.relauncher.SideOnly; public class ItemSmokeBomb extends Item { - public ItemSmokeBomb(){ + public ItemSmokeBomb() + { this.maxStackSize = 16; - this.setCreativeTab(WizardryTabs.WIZARDRY); + this.setCreativeTab(Wizardry.tabWizardry); } - @Override - public ActionResult onItemRightClick(ItemStack stack, World world, EntityPlayer player, EnumHand hand){ - - if(!player.capabilities.isCreativeMode){ + /** + * Called whenever this item is equipped and the right mouse button is pressed. Args: itemStack, world, entityPlayer + */ + public ItemStack onItemRightClick(ItemStack stack, World world, EntityPlayer entityplayer) + { + if (!entityplayer.capabilities.isCreativeMode) + { --stack.stackSize; } - player.playSound(SoundEvents.ENTITY_SNOWBALL_THROW, 0.5F, 0.4F / (itemRand.nextFloat() * 0.4F + 0.8F)); + world.playSoundAtEntity(entityplayer, "random.bow", 0.5F, 0.4F / (itemRand.nextFloat() * 0.4F + 0.8F)); - if(!world.isRemote){ - EntitySmokeBomb smokebomb = new EntitySmokeBomb(world, player); - // This is the standard set of parameters for this method, used by snowballs and ender pearls. - smokebomb.setHeadingFromThrower(player, player.rotationPitch, player.rotationYaw, 0.0f, 1.5f, 1.0f); - world.spawnEntityInWorld(smokebomb); + if (!world.isRemote) + { + world.spawnEntityInWorld(new EntitySmokeBomb(world, entityplayer)); } - return ActionResult.newResult(EnumActionResult.SUCCESS, stack); - } - - @Override - @SideOnly(Side.CLIENT) - public void getSubItems(Item item, CreativeTabs tab, List subItems){ - subItems.add(new ItemStack(this, 1)); + return stack; } } \ No newline at end of file diff --git a/src/main/java/electroblob/wizardry/item/ItemSpawnWizard.java b/src/main/java/electroblob/wizardry/item/ItemSpawnWizard.java new file mode 100644 index 00000000..25052f04 --- /dev/null +++ b/src/main/java/electroblob/wizardry/item/ItemSpawnWizard.java @@ -0,0 +1,194 @@ +package electroblob.wizardry.item; + +import java.util.List; + +import cpw.mods.fml.relauncher.Side; +import cpw.mods.fml.relauncher.SideOnly; +import electroblob.wizardry.Wizardry; +import electroblob.wizardry.entity.living.EntityEvilWizard; +import electroblob.wizardry.entity.living.EntityWizard; +import net.minecraft.block.Block; +import net.minecraft.block.BlockLiquid; +import net.minecraft.creativetab.CreativeTabs; +import net.minecraft.entity.Entity; +import net.minecraft.entity.EntityLiving; +import net.minecraft.entity.EntityLivingBase; +import net.minecraft.entity.IEntityLivingData; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.item.Item; +import net.minecraft.item.ItemMonsterPlacer; +import net.minecraft.item.ItemStack; +import net.minecraft.util.Facing; +import net.minecraft.util.MathHelper; +import net.minecraft.util.MovingObjectPosition; +import net.minecraft.util.StatCollector; +import net.minecraft.world.World; + +public class ItemSpawnWizard extends ItemMonsterPlacer { + + public ItemSpawnWizard(){ + this.setHasSubtypes(true); + this.setCreativeTab(Wizardry.tabWizardry); + this.setTextureName("minecraft:spawn_egg"); + } + + // Copied from Item to revert to normal behaviour. + @Override + public String getItemStackDisplayName(ItemStack stack) + { + return ("" + StatCollector.translateToLocal(this.getUnlocalizedName(stack) + ".name")).trim(); + } + + @Override + public String getUnlocalizedName(ItemStack stack){ + return stack.getItemDamage() == 1 ? "item.spawn_evil_wizard" : "item.spawn_wizard"; + } + + /** + * Callback for item usage. If the item does something special on right clicking, he will have one of those. Return + * True if something happen and false if it don't. This is for ITEMS, not BLOCKS + */ + @Override + public boolean onItemUse(ItemStack stack, EntityPlayer player, World world, int p_77648_4_, int p_77648_5_, int p_77648_6_, int p_77648_7_, float p_77648_8_, float p_77648_9_, float p_77648_10_) + { + if (world.isRemote) + { + return true; + } + else + { + Block block = world.getBlock(p_77648_4_, p_77648_5_, p_77648_6_); + p_77648_4_ += Facing.offsetsXForSide[p_77648_7_]; + p_77648_5_ += Facing.offsetsYForSide[p_77648_7_]; + p_77648_6_ += Facing.offsetsZForSide[p_77648_7_]; + double d0 = 0.0D; + + if (p_77648_7_ == 1 && block.getRenderType() == 11) + { + d0 = 0.5D; + } + + Entity entity = ItemSpawnWizard.spawnCreature(world, stack.getItemDamage(), (double)p_77648_4_ + 0.5D, (double)p_77648_5_ + d0, (double)p_77648_6_ + 0.5D); + + if (entity != null) + { + if (entity instanceof EntityLivingBase && stack.hasDisplayName()) + { + ((EntityLiving)entity).setCustomNameTag(stack.getDisplayName()); + } + + if (!player.capabilities.isCreativeMode) + { + --stack.stackSize; + } + } + + return true; + } + } + + /** + * Called whenever this item is equipped and the right mouse button is pressed. Args: itemStack, world, entityPlayer + */ + @Override + public ItemStack onItemRightClick(ItemStack stack, World world, EntityPlayer player) + { + if (world.isRemote) + { + return stack; + } + else + { + MovingObjectPosition movingobjectposition = this.getMovingObjectPositionFromPlayer(world, player, true); + + if (movingobjectposition == null) + { + return stack; + } + else + { + if (movingobjectposition.typeOfHit == MovingObjectPosition.MovingObjectType.BLOCK) + { + int i = movingobjectposition.blockX; + int j = movingobjectposition.blockY; + int k = movingobjectposition.blockZ; + + if (!world.canMineBlock(player, i, j, k)) + { + return stack; + } + + if (!player.canPlayerEdit(i, j, k, movingobjectposition.sideHit, stack)) + { + return stack; + } + + if (world.getBlock(i, j, k) instanceof BlockLiquid) + { + Entity entity = ItemSpawnWizard.spawnCreature(world, stack.getItemDamage(), (double)i, (double)j, (double)k); + + if (entity != null) + { + if (entity instanceof EntityLivingBase && stack.hasDisplayName()) + { + ((EntityLiving)entity).setCustomNameTag(stack.getDisplayName()); + } + + if (!player.capabilities.isCreativeMode) + { + --stack.stackSize; + } + } + } + } + + return stack; + } + } + } + + /** + * Spawns the creature specified by the egg's type in the location specified by the last three parameters. + * Parameters: world, metadata, x, y, z. + */ + public static Entity spawnCreature(World world, int metadata, double x, double y, double z){ + + Entity entity = null; + + for (int j = 0; j < 1; ++j) + { + entity = metadata == 1? new EntityEvilWizard(world) : new EntityWizard(world); + + if (entity != null && entity instanceof EntityLivingBase) + { + EntityLiving entityliving = (EntityLiving)entity; + entity.setLocationAndAngles(x, y, z, MathHelper.wrapAngleTo180_float(world.rand.nextFloat() * 360.0F), 0.0F); + entityliving.rotationYawHead = entityliving.rotationYaw; + entityliving.renderYawOffset = entityliving.rotationYaw; + entityliving.onSpawnWithEgg((IEntityLivingData)null); + world.spawnEntityInWorld(entity); + entityliving.playLivingSound(); + } + } + + return entity; + + } + + @Override + @SideOnly(Side.CLIENT) + public int getColorFromItemStack(ItemStack stack, int layer){ + if(stack.getItemDamage() == 1) return layer == 0 ? 0x290404 : 0xee9312; + return layer == 0 ? 0x19295e : 0xee9312; + } + + // Copied from Item to revert to normal behaviour. + @Override + @SideOnly(Side.CLIENT) + public void getSubItems(Item p_150895_1_, CreativeTabs p_150895_2_, List p_150895_3_) + { + p_150895_3_.add(new ItemStack(p_150895_1_, 1, 0)); + p_150895_3_.add(new ItemStack(p_150895_1_, 1, 1)); + } + +} diff --git a/src/main/java/electroblob/wizardry/item/ItemSpectralArmour.java b/src/main/java/electroblob/wizardry/item/ItemSpectralArmour.java index 951b69d9..2bea60ff 100644 --- a/src/main/java/electroblob/wizardry/item/ItemSpectralArmour.java +++ b/src/main/java/electroblob/wizardry/item/ItemSpectralArmour.java @@ -1,110 +1,117 @@ package electroblob.wizardry.item; import java.util.List; +import java.util.Locale; -import net.minecraft.creativetab.CreativeTabs; +import cpw.mods.fml.relauncher.Side; +import cpw.mods.fml.relauncher.SideOnly; +import electroblob.wizardry.EnumElement; +import electroblob.wizardry.ExtendedPlayer; +import electroblob.wizardry.Wizardry; +import net.minecraft.client.model.ModelBiped; import net.minecraft.entity.Entity; import net.minecraft.entity.EntityLivingBase; import net.minecraft.entity.player.EntityPlayer; -import net.minecraft.inventory.EntityEquipmentSlot; +import net.minecraft.item.EnumAction; import net.minecraft.item.EnumRarity; -import net.minecraft.item.Item; import net.minecraft.item.ItemArmor; import net.minecraft.item.ItemStack; +import net.minecraft.util.DamageSource; +import net.minecraft.util.StatCollector; import net.minecraft.world.World; -import net.minecraftforge.fml.relauncher.Side; -import net.minecraftforge.fml.relauncher.SideOnly; +import net.minecraftforge.common.ISpecialArmor.ArmorProperties; -public class ItemSpectralArmour extends ItemArmor implements IConjuredItem { +public class ItemSpectralArmour extends ItemArmor { - public ItemSpectralArmour(ArmorMaterial material, int renderIndex, EntityEquipmentSlot armourType) { + /** Armour types: 0 = helmet, 1 = chestplate, 2 = leggings, 3 = boots */ + public ItemSpectralArmour(ArmorMaterial material, int renderIndex, int armourType) { super(material, renderIndex, armourType); this.setCreativeTab(null); - this.setMaxDamage(getBaseDuration()); + this.setMaxDamage(1200); + + // Sets item icon texture and unlocalised name according to element and armour type. + switch(armourType){ + case 0: + this.setTextureName("wizardry:spectral_helmet"); + this.setUnlocalizedName("spectral_helmet"); + break; + case 1: + this.setTextureName("wizardry:spectral_chestplate"); + this.setUnlocalizedName("spectral_chestplate"); + break; + case 2: + this.setTextureName("wizardry:spectral_leggings"); + this.setUnlocalizedName("spectral_leggings"); + break; + case 3: + this.setTextureName("wizardry:spectral_boots"); + this.setUnlocalizedName("spectral_boots"); + break; + } } - + @Override - public int getBaseDuration(){ - return 1200; - } - - @Override - public int getMaxDamage(ItemStack stack){ - return this.getMaxDamageFromNBT(stack); + public int getMaxDamage(ItemStack stack) + { + return stack.hasTagCompound()? (int)(getMaxDamage()*stack.stackTagCompound.getFloat("durationMultiplier")) : getMaxDamage(); } // Overridden to stop the enchantment trick making the name turn blue. @Override - public EnumRarity getRarity(ItemStack stack){ - return EnumRarity.COMMON; + public EnumRarity getRarity(ItemStack p_77613_1_) + { + return EnumRarity.common; } @Override - // This method allows the code for the item's timer to be greatly simplified by damaging it directly from - // onUpdate() and removing the workaround that involved WizardData and all sorts of crazy stuff. - public boolean shouldCauseReequipAnimation(ItemStack oldStack, ItemStack newStack, boolean slotChanged){ - - if(oldStack != null || newStack != null){ - // We only care about the situation where we specifically want the animation NOT to play. - if(oldStack.getItem() == newStack.getItem() && !slotChanged) return false; + public void onArmorTick(World world, EntityPlayer player, ItemStack itemstack) { + // Allows the 'cheaty' damage bar rendering code to start working. + if(itemstack.getItemDamage() == 0){ + itemstack.setItemDamage(1); + } + ExtendedPlayer properties = ExtendedPlayer.get(player); + if(properties.conjuredArmourDuration > this.getMaxDamage(itemstack)){ + ((EntityPlayer)player).inventory.armorInventory[3-this.armorType] = null; } - - return super.shouldCauseReequipAnimation(oldStack, newStack, slotChanged); } @Override - public void onArmorTick(World world, EntityPlayer player, ItemStack stack){ - int damage = stack.getItemDamage(); - if(damage > stack.getMaxDamage()) player.inventory.clearMatchingItems(this, -1, 1, null); - stack.setItemDamage(damage + 1); - } + public int getDisplayDamage(ItemStack stack){ + return Wizardry.proxy.getConjuredItemDisplayDamage(stack); + } @Override - @SideOnly(Side.CLIENT) - public boolean hasEffect(ItemStack stack){ + public boolean hasEffect(ItemStack par1ItemStack, int pass){ return true; } @Override - public boolean getIsRepairable(ItemStack stack, ItemStack par2ItemStack){ + public boolean getIsRepairable(ItemStack par1ItemStack, ItemStack par2ItemStack) + { return false; } @Override - public int getItemEnchantability(){ + public int getItemEnchantability() + { return 0; } // Cannot be dropped @Override - public boolean onDroppedByPlayer(ItemStack item, EntityPlayer player){ + public boolean onDroppedByPlayer(ItemStack item, EntityPlayer player) + { return false; } @Override - public String getArmorTexture(ItemStack stack, Entity entity, EntityEquipmentSlot slot, String type) { - - if(slot == EntityEquipmentSlot.LEGS) return "wizardry:textures/armour/spectral_armour_legs.png"; + public String getArmorTexture(ItemStack stack, Entity entity, int slot, String type) + { + if(slot == 2) return "wizardry:textures/armour/spectral_armour_legs.png"; return "wizardry:textures/armour/spectral_armour.png"; - } - - @Override - @SideOnly(Side.CLIENT) - public net.minecraft.client.model.ModelBiped getArmorModel(EntityLivingBase entityLiving, ItemStack itemStack, EntityEquipmentSlot armorSlot, - net.minecraft.client.model.ModelBiped _default) { - net.minecraft.client.renderer.GlStateManager.enableBlend(); - net.minecraft.client.renderer.GlStateManager.tryBlendFuncSeparate(net.minecraft.client.renderer.GlStateManager.SourceFactor.SRC_ALPHA, net.minecraft.client.renderer.GlStateManager.DestFactor.ONE_MINUS_SRC_ALPHA, - net.minecraft.client.renderer.GlStateManager.SourceFactor.ONE, net.minecraft.client.renderer.GlStateManager.DestFactor.ZERO); - return super.getArmorModel(entityLiving, itemStack, armorSlot, _default); - } - - @Override - @SideOnly(Side.CLIENT) - public void getSubItems(Item parItem, CreativeTabs parTab, List parListSubItems){ - parListSubItems.add(new ItemStack(this, 1)); } } diff --git a/src/main/java/electroblob/wizardry/item/ItemSpectralBow.java b/src/main/java/electroblob/wizardry/item/ItemSpectralBow.java index b25efc8d..3f430376 100644 --- a/src/main/java/electroblob/wizardry/item/ItemSpectralBow.java +++ b/src/main/java/electroblob/wizardry/item/ItemSpectralBow.java @@ -1,229 +1,204 @@ package electroblob.wizardry.item; -import java.util.List; - -import javax.annotation.Nullable; +import javax.swing.Icon; +import cpw.mods.fml.relauncher.Side; +import cpw.mods.fml.relauncher.SideOnly; +import electroblob.wizardry.ExtendedPlayer; import electroblob.wizardry.Wizardry; import net.minecraft.client.Minecraft; -import net.minecraft.creativetab.CreativeTabs; +import net.minecraft.client.renderer.texture.IIconRegister; +import net.minecraft.enchantment.Enchantment; import net.minecraft.enchantment.EnchantmentHelper; import net.minecraft.entity.Entity; -import net.minecraft.entity.EntityLivingBase; import net.minecraft.entity.player.EntityPlayer; import net.minecraft.entity.projectile.EntityArrow; -import net.minecraft.init.Enchantments; import net.minecraft.init.Items; -import net.minecraft.init.SoundEvents; -import net.minecraft.item.IItemPropertyGetter; -import net.minecraft.item.Item; -import net.minecraft.item.ItemArrow; import net.minecraft.item.ItemBow; import net.minecraft.item.ItemStack; -import net.minecraft.stats.StatList; -import net.minecraft.util.ActionResult; -import net.minecraft.util.EnumActionResult; -import net.minecraft.util.EnumHand; -import net.minecraft.util.ResourceLocation; -import net.minecraft.util.SoundCategory; +import net.minecraft.util.IIcon; import net.minecraft.world.World; -import net.minecraftforge.fml.relauncher.Side; -import net.minecraftforge.fml.relauncher.SideOnly; +import net.minecraftforge.common.MinecraftForge; +import net.minecraftforge.event.entity.player.ArrowLooseEvent; +import net.minecraftforge.event.entity.player.ArrowNockEvent; -public class ItemSpectralBow extends ItemBow implements IConjuredItem { +public class ItemSpectralBow extends ItemBow { - public ItemSpectralBow(){ + @SideOnly(Side.CLIENT) + private IIcon[] iconArray; + + public ItemSpectralBow() { super(); - this.setMaxDamage(getBaseDuration()); + this.setMaxDamage(600); this.setNoRepair(); this.setCreativeTab(null); - this.addPropertyOverride(new ResourceLocation("pull"), new IItemPropertyGetter() - { - @SideOnly(Side.CLIENT) - public float apply(ItemStack stack, @Nullable World worldIn, @Nullable EntityLivingBase entityIn) - { - if (entityIn == null) - { - return 0.0F; - } - else - { - ItemStack itemstack = entityIn.getActiveItemStack(); - // Mojang, observe - hardcoding item references into their own classes is NOT good Java. - return itemstack != null && itemstack.getItem() == ItemSpectralBow.this ? (float)(stack.getMaxItemUseDuration() - entityIn.getItemInUseCount()) / 20.0F : 0.0F; - } - } - }); - this.addPropertyOverride(new ResourceLocation("pulling"), new IItemPropertyGetter() - { - @SideOnly(Side.CLIENT) - public float apply(ItemStack stack, @Nullable World worldIn, @Nullable EntityLivingBase entityIn) - { - return entityIn != null && entityIn.isHandActive() && entityIn.getActiveItemStack() == stack ? 1.0F : 0.0F; - } - }); } - + @Override - @SideOnly(Side.CLIENT) - // Why does this still exist? Item models deal with this now, right? public boolean isFull3D(){ return true; } @Override - public int getBaseDuration(){ - return 600; - } - - @Override - public int getMaxDamage(ItemStack stack){ - return this.getMaxDamageFromNBT(stack); + public int getMaxDamage(ItemStack stack) + { + return stack.hasTagCompound()? (int)(getMaxDamage()*stack.stackTagCompound.getFloat("durationMultiplier")) : getMaxDamage(); } @Override - // This method allows the code for the item's timer to be greatly simplified by damaging it directly from - // onUpdate() and removing the workaround that involved WizardData and all sorts of crazy stuff. - public boolean shouldCauseReequipAnimation(ItemStack oldStack, ItemStack newStack, boolean slotChanged){ - - if(oldStack != null && newStack != null){ - // We only care about the situation where we specifically want the animation NOT to play. - if(oldStack.getItem() == newStack.getItem() && !slotChanged - // This code should only run on the client side, so using Minecraft is ok. - && !Minecraft.getMinecraft().thePlayer.isHandActive()) return false; + public ItemStack onItemRightClick(ItemStack p_77659_1_, World p_77659_2_, EntityPlayer p_77659_3_) + { + ArrowNockEvent event = new ArrowNockEvent(p_77659_3_, p_77659_1_); + MinecraftForge.EVENT_BUS.post(event); + if (event.isCanceled()) + { + return event.result; + } + + // Spectral bow always fires, regardless of whether you have an arrow or not. + p_77659_3_.setItemInUse(p_77659_1_, this.getMaxItemUseDuration(p_77659_1_)); + + return p_77659_1_; + } + + @Override + public void onUpdate(ItemStack itemstack, World par2World, Entity entity, int par4, boolean par5) { + // Allows the 'cheaty' damage bar rendering code to start working. + if(itemstack.getItemDamage() == 0){ + itemstack.setItemDamage(1); } - - return super.shouldCauseReequipAnimation(oldStack, newStack, slotChanged); - } - - @Override - public void onUpdate(ItemStack stack, World world, Entity entity, int slot, boolean selected){ - int damage = stack.getItemDamage(); - if(damage > stack.getMaxDamage()) entity.replaceItemInInventory(slot, null); - // Can't damage it whilst in use because for some reason it causes the item use to constantly reset. - if(!(entity instanceof EntityLivingBase) || !((EntityLivingBase)entity).isHandActive()){ - stack.setItemDamage(damage + 1); - } - } - - // The following two methods re-route the displayed durability through the proxies in order to override the pausing - // of the item timer when the bow is being pulled. - - @Override - public double getDurabilityForDisplay(ItemStack stack){ - return Wizardry.proxy.getConjuredBowDurability(stack); - } - - public double getDefaultDurabilityForDisplay(ItemStack stack){ - return super.getDurabilityForDisplay(stack); - } - - @Override - public ActionResult onItemRightClick(ItemStack stack, World world, EntityPlayer player, EnumHand hand){ - - ActionResult ret = net.minecraftforge.event.ForgeEventFactory.onArrowNock(stack, world, player, hand, true); - - if(ret != null) return ret; - - player.setActiveHand(hand); - - return ActionResult.newResult(EnumActionResult.SUCCESS, stack); - - } - - @Override - @SideOnly(Side.CLIENT) - public boolean hasEffect(ItemStack stack){ - return true; - } - - @Override - public boolean getIsRepairable(ItemStack stack, ItemStack stack2){ - return false; - } - - @Override - public int getItemEnchantability(){ - return 0; - } - - // Cannot be dropped - @Override - public boolean onDroppedByPlayer(ItemStack item, EntityPlayer player){ - return false; - } - - @Override - public void onUsingTick(ItemStack stack, EntityLivingBase player, int count){ - // player.getItemInUseMaxCount() is named incorrectly; you only have to look at the method to see what it really - // does. - if(stack.getItemDamage() + player.getItemInUseMaxCount() > stack.getMaxDamage()) - player.replaceItemInInventory(player.getActiveHand() == EnumHand.MAIN_HAND ? 98 : 99, null); - } - - @Override - public void onPlayerStoppedUsing(ItemStack stack, World world, EntityLivingBase entity, int timeLeft){ - // Decreases the timer by the amount it should have been decreased while the bow was in use. - if(!world.isRemote) stack.setItemDamage(stack.getItemDamage() + (this.getMaxItemUseDuration(stack) - timeLeft)); - - if (entity instanceof EntityPlayer){ - - EntityPlayer entityplayer = (EntityPlayer)entity; - - int i = this.getMaxItemUseDuration(stack) - timeLeft; - i = net.minecraftforge.event.ForgeEventFactory.onArrowLoose(stack, world, (EntityPlayer)entity, i, true); - if (i < 0) return; - - float f = getArrowVelocity(i); - - if ((double)f >= 0.1D){ - - if (!world.isRemote){ - - ItemArrow itemarrow = (ItemArrow)Items.ARROW; - EntityArrow entityarrow = itemarrow.createArrow(world, new ItemStack(itemarrow), entityplayer); - entityarrow.setAim(entityplayer, entityplayer.rotationPitch, entityplayer.rotationYaw, 0.0F, f * 3.0F, 1.0F); - - if (f == 1.0F) - { - entityarrow.setIsCritical(true); - } - - int j = EnchantmentHelper.getEnchantmentLevel(Enchantments.POWER, stack); - - if (j > 0) - { - entityarrow.setDamage(entityarrow.getDamage() + (double)j * 0.5D + 0.5D); - } - - int k = EnchantmentHelper.getEnchantmentLevel(Enchantments.PUNCH, stack); - - if (k > 0) - { - entityarrow.setKnockbackStrength(k); - } - - if (EnchantmentHelper.getEnchantmentLevel(Enchantments.FLAME, stack) > 0) - { - entityarrow.setFire(100); - } - - entityarrow.pickupStatus = EntityArrow.PickupStatus.DISALLOWED; - - world.spawnEntityInWorld(entityarrow); - } - - world.playSound((EntityPlayer)null, entityplayer.posX, entityplayer.posY, entityplayer.posZ, SoundEvents.ENTITY_ARROW_SHOOT, SoundCategory.NEUTRAL, 1.0F, 1.0F / (itemRand.nextFloat() * 0.4F + 1.2F) + f * 0.5F); - - entityplayer.addStat(StatList.getObjectUseStats(this)); + if(entity instanceof EntityPlayer){ + ExtendedPlayer properties = ExtendedPlayer.get((EntityPlayer)entity); + properties.conjuredBowDuration++; + if(properties.conjuredBowDuration > this.getMaxDamage(itemstack)){ + ((EntityPlayer)entity).inventory.consumeInventoryItem(itemstack.getItem()); + properties.conjuredBowDuration = 0; } } } - + @Override - @SideOnly(Side.CLIENT) - public void getSubItems(Item item, CreativeTabs tab, List subItems){ - subItems.add(new ItemStack(this, 1)); + public int getDisplayDamage(ItemStack stack){ + return Wizardry.proxy.getConjuredItemDisplayDamage(stack); + } + + @Override + public boolean hasEffect(ItemStack par1ItemStack, int pass){ + return true; } + @Override + public boolean getIsRepairable(ItemStack par1ItemStack, ItemStack par2ItemStack) + { + return false; + } + + @Override + public int getItemEnchantability() + { + return 0; + } + + // Cannot be dropped + @Override + public boolean onDroppedByPlayer(ItemStack item, EntityPlayer player) + { + return false; + } + + @SideOnly(Side.CLIENT) + @Override + public void registerIcons(IIconRegister par1IconRegister) + { + this.itemIcon = par1IconRegister.registerIcon("wizardry:spectral_bow_standby"); + this.iconArray = new IIcon[bowPullIconNameArray.length]; + + for (int i = 0; i < this.iconArray.length; ++i) + { + this.iconArray[i] = par1IconRegister.registerIcon("wizardry:spectral_bow_" + bowPullIconNameArray[i]); + } + } + + @Override + public IIcon getIcon(ItemStack stack, int renderPass, EntityPlayer player, ItemStack usingItem, int useRemaining) + { + if(player.getItemInUse() == null) return this.itemIcon; + int time = stack.getMaxItemUseDuration() - useRemaining; + if (time >= 18) + { + return iconArray[2]; + } + else if (time > 9) + { + return iconArray[1]; + } + else if (time > 0) + { + return iconArray[0]; + } + return itemIcon; + } + + @Override + public void onPlayerStoppedUsing(ItemStack par1ItemStack, World par2World, EntityPlayer par3EntityPlayer, int par4) + { + int j = this.getMaxItemUseDuration(par1ItemStack) - par4; + + ArrowLooseEvent event = new ArrowLooseEvent(par3EntityPlayer, par1ItemStack, j); + MinecraftForge.EVENT_BUS.post(event); + if (event.isCanceled()) + { + return; + } + j = event.charge; + + float f = (float)j / 20.0F; + f = (f * f + f * 2.0F) / 3.0F; + + if ((double)f < 0.1D) + { + return; + } + + if (f > 1.0F) + { + f = 1.0F; + } + + EntityArrow entityarrow = new EntityArrow(par2World, par3EntityPlayer, f * 2.0F); + + if (f == 1.0F) + { + entityarrow.setIsCritical(true); + } + + int k = EnchantmentHelper.getEnchantmentLevel(Enchantment.power.effectId, par1ItemStack); + + if (k > 0) + { + entityarrow.setDamage(entityarrow.getDamage() + (double)k * 0.5D + 0.5D); + } + + int l = EnchantmentHelper.getEnchantmentLevel(Enchantment.punch.effectId, par1ItemStack); + + if (l > 0) + { + entityarrow.setKnockbackStrength(l); + } + + if (EnchantmentHelper.getEnchantmentLevel(Enchantment.flame.effectId, par1ItemStack) > 0) + { + entityarrow.setFire(100); + } + + par1ItemStack.damageItem(1, par3EntityPlayer); + par2World.playSoundAtEntity(par3EntityPlayer, "random.bow", 1.0F, 1.0F / (itemRand.nextFloat() * 0.4F + 1.2F) + f * 0.5F); + + entityarrow.canBePickedUp = 2; + + if (!par2World.isRemote) + { + par2World.spawnEntityInWorld(entityarrow); + } + } } diff --git a/src/main/java/electroblob/wizardry/item/ItemSpectralPickaxe.java b/src/main/java/electroblob/wizardry/item/ItemSpectralPickaxe.java index a91c9000..767beee0 100644 --- a/src/main/java/electroblob/wizardry/item/ItemSpectralPickaxe.java +++ b/src/main/java/electroblob/wizardry/item/ItemSpectralPickaxe.java @@ -1,82 +1,72 @@ package electroblob.wizardry.item; -import java.util.List; - -import net.minecraft.creativetab.CreativeTabs; +import electroblob.wizardry.ExtendedPlayer; +import electroblob.wizardry.Wizardry; +import net.minecraft.client.Minecraft; import net.minecraft.entity.Entity; import net.minecraft.entity.player.EntityPlayer; -import net.minecraft.item.Item; import net.minecraft.item.ItemPickaxe; import net.minecraft.item.ItemStack; import net.minecraft.world.World; -import net.minecraftforge.fml.relauncher.Side; -import net.minecraftforge.fml.relauncher.SideOnly; -public class ItemSpectralPickaxe extends ItemPickaxe implements IConjuredItem { +public class ItemSpectralPickaxe extends ItemPickaxe{ - public ItemSpectralPickaxe(ToolMaterial material){ + public ItemSpectralPickaxe(ToolMaterial material) { super(material); - this.setMaxDamage(getBaseDuration()); + this.setMaxDamage(600); this.setNoRepair(); this.setCreativeTab(null); } @Override - public int getBaseDuration(){ - return 600; - } - - @Override - public int getMaxDamage(ItemStack stack){ - return this.getMaxDamageFromNBT(stack); + public int getMaxDamage(ItemStack stack) + { + return stack.hasTagCompound()? (int)(getMaxDamage()*stack.stackTagCompound.getFloat("durationMultiplier")) : getMaxDamage(); } @Override - // This method allows the code for the item's timer to be greatly simplified by damaging it directly from - // onUpdate() and removing the workaround that involved WizardData and all sorts of crazy stuff. - public boolean shouldCauseReequipAnimation(ItemStack oldStack, ItemStack newStack, boolean slotChanged){ - - if(oldStack != null || newStack != null){ - // We only care about the situation where we specifically want the animation NOT to play. - if(oldStack.getItem() == newStack.getItem() && !slotChanged) return false; + public void onUpdate(ItemStack itemstack, World par2World, Entity entity, int par4, boolean par5) { + // Allows the 'cheaty' damage bar rendering code to start working. + if(itemstack.getItemDamage() == 0){ + itemstack.setItemDamage(1); + } + if(entity instanceof EntityPlayer){ + ExtendedPlayer properties = ExtendedPlayer.get((EntityPlayer)entity); + properties.conjuredPickaxeDuration++; + if(properties.conjuredPickaxeDuration > this.getMaxDamage(itemstack)){ + ((EntityPlayer)entity).inventory.consumeInventoryItem(itemstack.getItem()); + properties.conjuredPickaxeDuration = 0; + } } - - return super.shouldCauseReequipAnimation(oldStack, newStack, slotChanged); } @Override - public void onUpdate(ItemStack stack, World world, Entity entity, int slot, boolean selected){ - int damage = stack.getItemDamage(); - if(damage > stack.getMaxDamage()) entity.replaceItemInInventory(slot, null); - stack.setItemDamage(damage + 1); - } + public int getDisplayDamage(ItemStack stack){ + return Wizardry.proxy.getConjuredItemDisplayDamage(stack); + } @Override - @SideOnly(Side.CLIENT) - public boolean hasEffect(ItemStack stack){ + public boolean hasEffect(ItemStack par1ItemStack, int pass){ return true; } @Override - public boolean getIsRepairable(ItemStack stack, ItemStack par2ItemStack){ + public boolean getIsRepairable(ItemStack par1ItemStack, ItemStack par2ItemStack) + { return false; } @Override - public int getItemEnchantability(){ + public int getItemEnchantability() + { return 0; } - // Cannot be dropped + //Cannot be dropped @Override - public boolean onDroppedByPlayer(ItemStack item, EntityPlayer player){ + public boolean onDroppedByPlayer(ItemStack item, EntityPlayer player) + { return false; } - - @Override - @SideOnly(Side.CLIENT) - public void getSubItems(Item item, CreativeTabs tab, List subItems){ - subItems.add(new ItemStack(this, 1)); - } } diff --git a/src/main/java/electroblob/wizardry/item/ItemSpectralSword.java b/src/main/java/electroblob/wizardry/item/ItemSpectralSword.java index b1db6b6e..7c683a0b 100644 --- a/src/main/java/electroblob/wizardry/item/ItemSpectralSword.java +++ b/src/main/java/electroblob/wizardry/item/ItemSpectralSword.java @@ -1,82 +1,71 @@ package electroblob.wizardry.item; -import java.util.List; - -import net.minecraft.creativetab.CreativeTabs; +import electroblob.wizardry.ExtendedPlayer; +import electroblob.wizardry.Wizardry; import net.minecraft.entity.Entity; import net.minecraft.entity.player.EntityPlayer; -import net.minecraft.item.Item; import net.minecraft.item.ItemStack; import net.minecraft.item.ItemSword; import net.minecraft.world.World; -import net.minecraftforge.fml.relauncher.Side; -import net.minecraftforge.fml.relauncher.SideOnly; -public class ItemSpectralSword extends ItemSword implements IConjuredItem { +public class ItemSpectralSword extends ItemSword { public ItemSpectralSword(ToolMaterial material) { super(material); - this.setMaxDamage(getBaseDuration()); + this.setMaxDamage(600); this.setNoRepair(); this.setCreativeTab(null); } @Override - public int getBaseDuration(){ - return 600; - } - - @Override - public int getMaxDamage(ItemStack stack){ - return this.getMaxDamageFromNBT(stack); + public int getMaxDamage(ItemStack stack) + { + return stack.hasTagCompound()? (int)(getMaxDamage()*stack.stackTagCompound.getFloat("durationMultiplier")) : getMaxDamage(); } @Override - // This method allows the code for the item's timer to be greatly simplified by damaging it directly from - // onUpdate() and removing the workaround that involved WizardData and all sorts of crazy stuff. - public boolean shouldCauseReequipAnimation(ItemStack oldStack, ItemStack newStack, boolean slotChanged){ - - if(oldStack != null || newStack != null){ - // We only care about the situation where we specifically want the animation NOT to play. - if(oldStack.getItem() == newStack.getItem() && !slotChanged) return false; + public void onUpdate(ItemStack itemstack, World par2World, Entity entity, int par4, boolean par5) { + // Allows the 'cheaty' damage bar rendering code to start working. + if(itemstack.getItemDamage() == 0){ + itemstack.setItemDamage(1); + } + if(entity instanceof EntityPlayer){ + ExtendedPlayer properties = ExtendedPlayer.get((EntityPlayer)entity); + properties.conjuredSwordDuration++; + if(properties.conjuredSwordDuration > this.getMaxDamage(itemstack)){ + ((EntityPlayer)entity).inventory.consumeInventoryItem(itemstack.getItem()); + properties.conjuredSwordDuration = 0; + } } - - return super.shouldCauseReequipAnimation(oldStack, newStack, slotChanged); } @Override - public void onUpdate(ItemStack stack, World world, Entity entity, int slot, boolean selected){ - int damage = stack.getItemDamage(); - if(damage > stack.getMaxDamage()) entity.replaceItemInInventory(slot, null); - stack.setItemDamage(damage + 1); - } + public int getDisplayDamage(ItemStack stack){ + return Wizardry.proxy.getConjuredItemDisplayDamage(stack); + } @Override - @SideOnly(Side.CLIENT) - public boolean hasEffect(ItemStack stack){ + public boolean hasEffect(ItemStack par1ItemStack, int pass){ return true; } @Override - public boolean getIsRepairable(ItemStack stack, ItemStack par2ItemStack){ + public boolean getIsRepairable(ItemStack par1ItemStack, ItemStack par2ItemStack) + { return false; } @Override - public int getItemEnchantability(){ + public int getItemEnchantability() + { return 0; } // Cannot be dropped @Override - public boolean onDroppedByPlayer(ItemStack item, EntityPlayer player){ + public boolean onDroppedByPlayer(ItemStack item, EntityPlayer player) + { return false; } - - @Override - @SideOnly(Side.CLIENT) - public void getSubItems(Item item, CreativeTabs tab, List subItems){ - subItems.add(new ItemStack(this, 1)); - } } diff --git a/src/main/java/electroblob/wizardry/item/ItemSpellBook.java b/src/main/java/electroblob/wizardry/item/ItemSpellBook.java index 742c660a..b51ad7c8 100644 --- a/src/main/java/electroblob/wizardry/item/ItemSpellBook.java +++ b/src/main/java/electroblob/wizardry/item/ItemSpellBook.java @@ -2,35 +2,38 @@ package electroblob.wizardry.item; import java.util.List; +import cpw.mods.fml.relauncher.Side; +import cpw.mods.fml.relauncher.SideOnly; +import electroblob.wizardry.ExtendedPlayer; import electroblob.wizardry.SpellGlyphData; -import electroblob.wizardry.WizardData; import electroblob.wizardry.Wizardry; import electroblob.wizardry.WizardryGuiHandler; -import electroblob.wizardry.registry.WizardryTabs; +import electroblob.wizardry.WizardryRegistry; +import electroblob.wizardry.client.GuiSpellBook; import electroblob.wizardry.spell.Spell; +import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.FontRenderer; import net.minecraft.creativetab.CreativeTabs; import net.minecraft.entity.player.EntityPlayer; import net.minecraft.item.Item; import net.minecraft.item.ItemStack; -import net.minecraft.util.ActionResult; -import net.minecraft.util.EnumActionResult; -import net.minecraft.util.EnumHand; +import net.minecraft.util.EnumChatFormatting; import net.minecraft.world.World; -import net.minecraftforge.fml.relauncher.Side; -import net.minecraftforge.fml.relauncher.SideOnly; import net.minecraftforge.oredict.OreDictionary; -public class ItemSpellBook extends Item { +public class ItemSpellBook extends Item{ public ItemSpellBook() { super(); this.setHasSubtypes(true); this.setMaxStackSize(1); - this.setCreativeTab(WizardryTabs.SPELLS); + this.setCreativeTab(Wizardry.tabSpells); + this.setTextureName("wizardry:spell_book"); + this.setUnlocalizedName("spellBook"); } @Override - public void getSubItems(Item item, CreativeTabs tab, List list){ + public void getSubItems(Item item, CreativeTabs tab, List list){ // In this particular case, getTotalSpellCount() is a more efficient way of doing this since the spell instance // is not required, only the id. for(int i = 0; i < Spell.getTotalSpellCount(); i++){ @@ -40,42 +43,44 @@ public class ItemSpellBook extends Item { } } - @Override - public ActionResult onItemRightClick(ItemStack stack, World world, EntityPlayer player, EnumHand hand){ - player.openGui(Wizardry.instance, WizardryGuiHandler.SPELL_BOOK, world, 0, 0, 0); - return ActionResult.newResult(EnumActionResult.SUCCESS, stack); + public ItemStack onItemRightClick(ItemStack par1ItemStack, World par2World, EntityPlayer par3EntityPlayer){ + par3EntityPlayer.openGui(Wizardry.instance, WizardryGuiHandler.SPELL_BOOK, par2World, 0, 0, 0); + return par1ItemStack; } - + /* @Override - @SideOnly(Side.CLIENT) - public void addInformation(ItemStack itemstack, EntityPlayer player, List tooltip, boolean advanced){ + public String getUnlocalizedName(ItemStack stack) { + return EnumSpell.getSpellFromId(stack.getItemDamage()).name; + } + */ + public void addInformation(ItemStack itemstack, EntityPlayer player, List text, boolean par4){ // Tooltip is left blank for wizards buying generic spell books. if(itemstack.getItemDamage() != OreDictionary.WILDCARD_VALUE){ Spell spell = Spell.get(itemstack.getItemDamage()); boolean discovered = true; - if(Wizardry.settings.discoveryMode && !player.capabilities.isCreativeMode && WizardData.get(player) != null - && !WizardData.get(player).hasSpellBeenDiscovered(spell)){ + if(Wizardry.discoveryMode && !player.capabilities.isCreativeMode && ExtendedPlayer.get(player) != null + && !ExtendedPlayer.get(player).hasSpellBeenDiscovered(spell)){ discovered = false; } // Element colour is not given for undiscovered spells - tooltip.add(discovered ? "\u00A77" + spell.getDisplayNameWithFormatting() : "#\u00A79" + SpellGlyphData.getGlyphName(spell, player.worldObj)); - tooltip.add(spell.tier.getDisplayNameWithFormatting()); + text.add(discovered ? "\u00A77" + spell.getDisplayNameWithFormatting() : "#\u00A79" + SpellGlyphData.getGlyphName(spell, player.worldObj)); + text.add(spell.tier.getDisplayNameWithFormatting()); } /* Removed to streamline the tooltip a bit. Information is now within the book. if(spell.isContinuous){ - tooltip.add("\u00A79Mana Cost: " + spell.cost + " per second"); + par3List.add("\u00A79Mana Cost: " + spell.cost + " per second"); }else{ - tooltip.add("\u00A79Mana Cost: " + spell.cost); + par3List.add("\u00A79Mana Cost: " + spell.cost); } */ } @Override @SideOnly(Side.CLIENT) - public net.minecraft.client.gui.FontRenderer getFontRenderer(ItemStack stack){ + public FontRenderer getFontRenderer(ItemStack stack){ return Wizardry.proxy.getFontRenderer(stack); } diff --git a/src/main/java/electroblob/wizardry/item/ItemWand.java b/src/main/java/electroblob/wizardry/item/ItemWand.java index 6f1f99b5..2433051b 100644 --- a/src/main/java/electroblob/wizardry/item/ItemWand.java +++ b/src/main/java/electroblob/wizardry/item/ItemWand.java @@ -1,45 +1,33 @@ package electroblob.wizardry.item; +import java.lang.ref.WeakReference; import java.util.List; +import cpw.mods.fml.common.network.simpleimpl.IMessage; +import cpw.mods.fml.relauncher.Side; +import cpw.mods.fml.relauncher.SideOnly; +import electroblob.wizardry.EnumElement; +import electroblob.wizardry.EnumTier; +import electroblob.wizardry.ExtendedPlayer; import electroblob.wizardry.SpellGlyphData; -import electroblob.wizardry.WizardData; +import electroblob.wizardry.WandHelper; import electroblob.wizardry.Wizardry; -import electroblob.wizardry.constants.Constants; -import electroblob.wizardry.constants.Element; -import electroblob.wizardry.constants.Tier; -import electroblob.wizardry.entity.living.ISummonedCreature; -import electroblob.wizardry.event.SpellCastEvent; -import electroblob.wizardry.event.SpellCastEvent.Source; +import electroblob.wizardry.WizardryUtilities; +import electroblob.wizardry.entity.living.EntitySummonedCreature; import electroblob.wizardry.packet.PacketCastSpell; import electroblob.wizardry.packet.WizardryPacketHandler; -import electroblob.wizardry.registry.WizardryAchievements; -import electroblob.wizardry.registry.WizardryItems; -import electroblob.wizardry.registry.WizardryPotions; -import electroblob.wizardry.registry.WizardryTabs; import electroblob.wizardry.spell.Spell; -import electroblob.wizardry.util.SpellModifiers; -import electroblob.wizardry.util.WandHelper; -import electroblob.wizardry.util.WizardryUtilities; -import net.minecraft.creativetab.CreativeTabs; +import net.minecraft.client.gui.FontRenderer; import net.minecraft.entity.Entity; -import net.minecraft.entity.EntityLiving; import net.minecraft.entity.EntityLivingBase; import net.minecraft.entity.player.EntityPlayer; -import net.minecraft.inventory.EntityEquipmentSlot; import net.minecraft.item.EnumAction; import net.minecraft.item.Item; import net.minecraft.item.ItemStack; -import net.minecraft.util.ActionResult; -import net.minecraft.util.EnumActionResult; -import net.minecraft.util.EnumHand; -import net.minecraft.util.math.RayTraceResult; -import net.minecraft.util.text.TextComponentTranslation; +import net.minecraft.util.ChatComponentTranslation; +import net.minecraft.util.MovingObjectPosition; +import net.minecraft.util.StatCollector; import net.minecraft.world.World; -import net.minecraftforge.common.MinecraftForge; -import net.minecraftforge.fml.common.network.simpleimpl.IMessage; -import net.minecraftforge.fml.relauncher.Side; -import net.minecraftforge.fml.relauncher.SideOnly; /** This class is (literally) where the magic happens! All wand types are single instances of this class. There's a lot * of quite hard-to-read code in here, but unfortunately there's not much I can do about that. For this reason, I have @@ -55,48 +43,42 @@ import net.minecraftforge.fml.relauncher.SideOnly; * @since Wizardry 1.0 */ public class ItemWand extends Item { - public Tier tier; - public Element element; + public EnumTier tier; + public EnumElement element; - public ItemWand(Tier tier, Element element) { + public ItemWand(EnumTier enumtier, EnumElement enumelement) { super(); setMaxStackSize(1); - if(element == null || tier == Tier.BASIC){ - setCreativeTab(WizardryTabs.WIZARDRY); + if(enumelement == null || enumtier == EnumTier.BASIC){ + setCreativeTab(Wizardry.tabWizardry); } - this.tier = tier; - this.element = element; - this.setMaxDamage(this.tier.maxCharge); + this.tier = enumtier; + this.element = enumelement; + this.setMaxDamage(tier.maxCharge); } @Override - @SideOnly(Side.CLIENT) public boolean isFull3D(){ return true; } - @Override - @SideOnly(Side.CLIENT) - public net.minecraft.client.gui.FontRenderer getFontRenderer(ItemStack stack){ - return Wizardry.proxy.getFontRenderer(stack); - } - - @Override - @SideOnly(Side.CLIENT) - public void getSubItems(Item parItem, CreativeTabs parTab, List parListSubItems){ - parListSubItems.add(new ItemStack(this, 1)); - } - // Max damage is modifiable with upgrades. @Override public int getMaxDamage(ItemStack itemstack){ // + 0.5f corrects small float errors rounding down - return (int)(super.getMaxDamage(itemstack)*(1.0f + Constants.STORAGE_INCREASE_PER_LEVEL * WandHelper.getUpgradeLevel(itemstack, WizardryItems.storage_upgrade)) + 0.5f); + return (int)(getMaxDamage()*(1.0f + Wizardry.STORAGE_INCREASE_PER_LEVEL * WandHelper.getUpgradeLevel(itemstack, Wizardry.storageUpgrade)) + 0.5f); } @Override - public void onCreated(ItemStack stack, World par2World, EntityPlayer par3EntityPlayer){ - par3EntityPlayer.addStat(WizardryAchievements.arcane_initiate); + public void onCreated(ItemStack par1ItemStack, World par2World, EntityPlayer par3EntityPlayer){ + /* Removed because of mana flasks, which would cause a new book to be given each time a mana flask + * is crafted with the wand. Handbook is now given on acquiring a crystal. + ExtendedPlayer properties = ExtendedPlayer.get(par3EntityPlayer); + if(properties != null && !properties.handbookGiven){ + par3EntityPlayer.inventory.addItemStackToInventory(new ItemStack(Wizardry.wizardHandbook)); + properties.handbookGiven = true; + }*/ + par3EntityPlayer.triggerAchievement(Wizardry.arcaneInitiate); } @Override @@ -105,131 +87,136 @@ public class ItemWand extends Item { WandHelper.decrementCooldowns(itemstack); // Decrements wand damage (increases mana) every 1.5 seconds if it has a condenser upgrade - if(!world.isRemote && itemstack.isItemDamaged() && world.getWorldTime() % Constants.CONDENSER_TICK_INTERVAL == 0){ + if(!world.isRemote && itemstack.isItemDamaged() && world.getWorldTime() % Wizardry.CONDENSER_TICK_INTERVAL == 0){ // If the upgrade level is 0, this does nothing anyway. - itemstack.setItemDamage(itemstack.getItemDamage() - WandHelper.getUpgradeLevel(itemstack, WizardryItems.condenser_upgrade)); + itemstack.setItemDamage(itemstack.getItemDamage() - WandHelper.getUpgradeLevel(itemstack, Wizardry.condenserUpgrade)); } - if(entity instanceof EntityPlayer && this.element != null && this.element != Element.MAGIC){ + if(entity instanceof EntityPlayer && this.element != null && this.element != EnumElement.MAGIC){ // As it stands, this will trigger every tick. Not ideal, but I can't find a way to detect if a player // has a certain achievement. // EDIT: There is a way to check, using StatFileWriter#hasAchievementUnlocked, but this ends up calling the same - // thing as addStat anyway, meaning there's no point and it's probably not much of a problem anyway. - ((EntityPlayer)entity).addStat(WizardryAchievements.elemental); + // thing as triggerAchievement anyway, meaning there's no point and it's probably not much of a problem anyway. + ((EntityPlayer)entity).triggerAchievement(Wizardry.elemental); } } - @Override - public boolean shouldCauseReequipAnimation(ItemStack oldStack, ItemStack newStack, boolean slotChanged){ - - // This method does some VERY strange things! Despite its name, it also seems to affect the updating of NBT... - - if(oldStack != null || newStack != null){ - // We only care about the situation where we specifically want the animation NOT to play. - if(oldStack.getItem() == newStack.getItem() && !slotChanged - && oldStack.getItem() instanceof ItemWand && newStack.getItem() instanceof ItemWand - && WandHelper.getCurrentSpell(oldStack) == WandHelper.getCurrentSpell(newStack)) return false; - } - - return super.shouldCauseReequipAnimation(oldStack, newStack, slotChanged); - } - @Override public EnumAction getItemUseAction(ItemStack itemstack){ return WandHelper.getCurrentSpell(itemstack).action; } @Override - public int getMaxItemUseDuration(ItemStack stack){ + public int getMaxItemUseDuration(ItemStack par1ItemStack){ return 72000; } - @SideOnly(Side.CLIENT) @Override - public void addInformation(ItemStack itemstack, EntityPlayer player, List text, boolean advanced){ + public void addInformation(ItemStack itemstack, EntityPlayer player, List text, boolean par4){ // +0.5f is necessary due to the error in the way floats are calculated. - if(element != null) text.add("\u00A78" + net.minecraft.client.resources.I18n.format("item.wizardry:wand.buff", (int)((tier.level+1)*Constants.DAMAGE_INCREASE_PER_TIER*100 + 0.5f) + "%", element.getDisplayName())); + if(element != null) text.add("\u00A78" + StatCollector.translateToLocalFormatted("item.wand.buff", (int)((tier.level+1)*Wizardry.DAMAGE_INCREASE_PER_TIER*100 + 0.5f) + "%", element.getDisplayName())); Spell spell = WandHelper.getCurrentSpell(itemstack); boolean discovered = true; - if(Wizardry.settings.discoveryMode && !player.capabilities.isCreativeMode && WizardData.get(player) != null - && !WizardData.get(player).hasSpellBeenDiscovered(spell)){ + if(Wizardry.discoveryMode && !player.capabilities.isCreativeMode && ExtendedPlayer.get(player) != null + && !ExtendedPlayer.get(player).hasSpellBeenDiscovered(spell)){ discovered = false; } - text.add("\u00A77" + net.minecraft.client.resources.I18n.format("item.wizardry:wand.spell", discovered ? "\u00A77" + spell.getDisplayNameWithFormatting() : "#\u00A79" + SpellGlyphData.getGlyphName(spell, player.worldObj))); + text.add("\u00A77" + StatCollector.translateToLocalFormatted("item.wand.spell", discovered ? "\u00A77" + spell.getDisplayNameWithFormatting() : "#\u00A79" + SpellGlyphData.getGlyphName(spell, player.worldObj))); - text.add("\u00A79" + net.minecraft.client.resources.I18n.format("item.wizardry:wand.mana", (this.getMaxDamage(itemstack) - this.getDamage(itemstack)), this.getMaxDamage(itemstack))); + text.add("\u00A79" + StatCollector.translateToLocalFormatted("item.wand.mana", (this.getMaxDamage(itemstack) - this.getDamage(itemstack)), this.getMaxDamage(itemstack))); } @Override - public String getItemStackDisplayName(ItemStack stack){ - return (this.element == null ? "" : this.element.getFormattingCode()) + super.getItemStackDisplayName(stack); + public String getItemStackDisplayName(ItemStack p_77653_1_){ + + return ((this.element == null ? "" : this.element.colour) + StatCollector.translateToLocal(this.getUnlocalizedNameInefficiently(p_77653_1_) + ".name")).trim(); } - + // Continuous spells use the onUsingItemTick method instead of this one. /* An important thing to note about this method: it is only called on the server and the client of the player - * holding the item (I call this client-inconsistency). This means if you spawn particles here they will not show up - * on other players' screens. Instead, this must be done via packets. */ + * holding the item. This means if you spawn particles here they will not show up on other players' screens. + * Instead, this must be done via packets. */ @Override - public ActionResult onItemRightClick(ItemStack stack, World world, EntityPlayer player, EnumHand hand){ + public ItemStack onItemRightClick(ItemStack stack, World world, EntityPlayer player){ + + if(this.selectMinionTarget(player, world)) return stack; + + if(player.isPotionActive(Wizardry.arcaneJammer)) return stack; - // Alternate right-click function; overrides spell casting. - if(this.selectMinionTarget(player, world)) return new ActionResult(EnumActionResult.SUCCESS, stack); - Spell spell = WandHelper.getCurrentSpell(stack); - SpellModifiers modifiers = this.calculateModifiers(stack, spell); - - // If anything stops the spell working at this point, nothing else happens. - if(MinecraftForge.EVENT_BUS.post(new SpellCastEvent.Pre(player, spell, modifiers, Source.WAND))){ - return new ActionResult(EnumActionResult.FAIL, stack); + + // If a spell is disabled in the config, it will not work. + if(!spell.isEnabled()){ + if(!world.isRemote) player.addChatMessage(new ChatComponentTranslation("spell.disabled", spell.getDisplayNameWithFormatting())); + return stack; } - // This is here to start the inUse thing, otherwise the onUsingTick method will not fire. - if(spell.isContinuous && !player.isHandActive()){ - player.setActiveHand(hand); - // Probably ought to be here. (Does it succeed though?) - return new ActionResult(EnumActionResult.SUCCESS, stack); + // This is here to start the inUse thing, otherwise the onItemUsingTick method will not fire. + // If the castSpell method then returns false nothing will happen (continuous spells have no EnumAction + // either so setting the item in use has no direct visible effect). + // Edit: Strictly speaking this is not true but the spells that do have an action (shield, shadow ward and levitation) + // will never return false. + if(spell.isContinuous && !player.isUsingItem()){ + player.setItemInUse(stack, this.getMaxItemUseDuration(stack)); } // Conditions for the spell to be attempted. The tier check is a failsafe; it should never be false unless the // NBT is modified directly. if(!spell.isContinuous && spell.tier.level <= this.tier.level - // Checks that the wand has enough mana to cast the spell && spell.cost <= (stack.getMaxDamage() - stack.getItemDamage()) - // Checks that the spell is not in cooldown or that the player is in creative mode && (WandHelper.getCurrentCooldown(stack) == 0 || player.capabilities.isCreativeMode)){ + // = Spell modifiers = + float rangeMultiplier = 1.0f + WandHelper.getUpgradeLevel(stack, Wizardry.rangeUpgrade)*Wizardry.RANGE_INCREASE_PER_LEVEL; + float durationMultiplier = 1.0f + WandHelper.getUpgradeLevel(stack, Wizardry.durationUpgrade)*Wizardry.DURATION_INCREASE_PER_LEVEL; + float blastMultiplier = 1.0f + WandHelper.getUpgradeLevel(stack, Wizardry.blastUpgrade)*Wizardry.BLAST_RADIUS_INCREASE_PER_LEVEL; + + // I would have liked to have made potion effects increase in strength according to the damage multiplier, + // but the amplifier level is too discrete to make this work. For example, wither 3 for 10 seconds will kill a + // normal mob on full 20 health, but wither 2 for the same duration only deals about 6 hearts of damage in total. + float damageMultiplier = 1.0f; + + if(this.element == spell.element){ + damageMultiplier = 1.0f + (this.tier.level + 1) * Wizardry.DAMAGE_INCREASE_PER_TIER; + } + // If the spell does not require a packet, the code is run in the old client-inconsistent way, since this // means that swingItem() doesn't need packets in order to work, improving performance. if(!world.isRemote){ - if(spell.cast(world, player, hand, 0, modifiers)){ - - MinecraftForge.EVENT_BUS.post(new SpellCastEvent.Post(player, spell, modifiers, Source.WAND)); + if(spell.cast(world, player, 0, damageMultiplier, rangeMultiplier, durationMultiplier, blastMultiplier)){ // = Packets = if(spell.doesSpellRequirePacket()){ // Sends a packet to all players in dimension to tell them to spawn particles. // Only sent if the spell succeeded, because if the spell failed, you wouldn't // need to spawn any particles! - IMessage msg = new PacketCastSpell.Message(player.getEntityId(), hand, spell.id(), modifiers); - WizardryPacketHandler.net.sendToDimension(msg, world.provider.getDimension()); + IMessage msg = new PacketCastSpell.Message(player.getEntityId(), 0, spell.id(), damageMultiplier, rangeMultiplier, blastMultiplier); + WizardryPacketHandler.net.sendToDimension(msg, world.provider.dimensionId); } - player.setActiveHand(hand); + player.setItemInUse(stack, this.getMaxItemUseDuration(stack)); + + // = Discovery = + if(!player.capabilities.isCreativeMode && !ExtendedPlayer.get(player).hasSpellBeenDiscovered(spell) && Wizardry.discoveryMode){ + player.worldObj.playSoundAtEntity(player, "random.levelup", 1.25f, 1); + if(!player.worldObj.isRemote) player.addChatMessage(new ChatComponentTranslation("spell.discover", spell.getDisplayNameWithFormatting())); + } + ExtendedPlayer.get(player).discoverSpell(spell); // = Cooldown = // Spells only have a cooldown in survival if(!player.capabilities.isCreativeMode){ - float cooldownMultiplier = 1.0f - WandHelper.getUpgradeLevel(stack, WizardryItems.cooldown_upgrade)*Constants.COOLDOWN_REDUCTION_PER_LEVEL; + float cooldownMultiplier = 1.0f - WandHelper.getUpgradeLevel(stack, Wizardry.cooldownUpgrade)*Wizardry.COOLDOWN_REDUCTION_PER_LEVEL; - if(player.isPotionActive(WizardryPotions.font_of_mana)){ + if(player.isPotionActive(Wizardry.fontOfMana)){ // Dividing by this rather than setting it takes upgrades and font of mana into account simultaneously - cooldownMultiplier /= 2 + player.getActivePotionEffect(WizardryPotions.font_of_mana).getAmplifier(); + cooldownMultiplier /= 2 + player.getActivePotionEffect(Wizardry.fontOfMana).getAmplifier(); } WandHelper.setCurrentCooldown(stack, (int)(spell.cooldown * cooldownMultiplier)); @@ -237,162 +224,184 @@ public class ItemWand extends Item { // = Mana cost = // The spell costs 20% less for every armour piece of the matching element. - int armourPieces = getMatchingArmourCount(player, spell); + int armourPieces = 0; - stack.damageItem((int)(spell.cost * (1.0f - armourPieces*Constants.COST_REDUCTION_PER_ARMOUR)), player); + for(int i=0; i<4; i++){ + if(player.getCurrentArmor(i) != null && player.getCurrentArmor(i).getItem() instanceof ItemWizardArmour + && ((ItemWizardArmour)player.getCurrentArmor(i).getItem()).element == spell.element) armourPieces++; + } + + stack.damageItem((int)(spell.cost * (1.0f - armourPieces*Wizardry.COST_REDUCTION_PER_ARMOUR)), player); - return new ActionResult(EnumActionResult.SUCCESS, stack); } - + + // Client-inconsistent spells only. }else if(!spell.doesSpellRequirePacket()){ - // Client-inconsistent spell casting. This code only runs client-side. - if(spell.cast(world, player, hand, 0, modifiers)){ - // This is all that needs to happen, because everything above works fine on just the server side. - MinecraftForge.EVENT_BUS.post(new SpellCastEvent.Post(player, spell, modifiers, Source.WAND)); - return new ActionResult(EnumActionResult.SUCCESS, stack); + // This is all that needs to happen, because everything up there works fine on just the server side. + if(spell.cast(world, player, 0, damageMultiplier, rangeMultiplier, durationMultiplier, blastMultiplier)){ + // Added in version 1.1.3 to fix the client-side spell discovery not updating for spells with the + // packet optimisation. + if(ExtendedPlayer.get(player) != null){ + ExtendedPlayer.get(player).discoverSpell(spell); + } } } } - - return new ActionResult(EnumActionResult.FAIL, stack); + return stack; } - // For continuous spells. The count argument actually decrements by 1 each tick. + + // For continuous spells and (deprecated) spells with a charge up time. + // The count argument actually decrements by 1 each tick. @Override - public void onUsingTick(ItemStack stack, EntityLivingBase user, int count){ + public void onUsingTick(ItemStack stack, EntityPlayer player, int count){ - if(user instanceof EntityPlayer){ + Spell spell = WandHelper.getCurrentSpell(stack); - EntityPlayer player = (EntityPlayer)user; - - Spell spell = WandHelper.getCurrentSpell(stack); - SpellModifiers modifiers = this.calculateModifiers(stack, spell); - int castingTick = stack.getMaxItemUseDuration() - count; - - if(MinecraftForge.EVENT_BUS.post(new SpellCastEvent.Tick(player, spell, modifiers, Source.WAND, castingTick))) return; + ExtendedPlayer properties = ExtendedPlayer.get(player); - // Continuous spells (these must check if they can be cast each tick since the mana changes) - if(spell.isContinuous && spell.tier.level <= this.tier.level - && spell.cost/5 <= (stack.getMaxDamage() - stack.getItemDamage())){ + // Continuous spells (these must check if they can be cast each tick since the mana changes) + if(spell.isContinuous && spell.tier.level <= this.tier.level + && spell.cost/5 <= (stack.getMaxDamage() - stack.getItemDamage() - properties.damageToApply)){ - if(spell.cast(player.worldObj, player, player.getActiveHand(), castingTick, modifiers)){ + // = Spell modifiers = + float rangeMultiplier = 1.0f + WandHelper.getUpgradeLevel(stack, Wizardry.rangeUpgrade)*Wizardry.RANGE_INCREASE_PER_LEVEL; + float durationMultiplier = 1.0f + WandHelper.getUpgradeLevel(stack, Wizardry.durationUpgrade)*Wizardry.DURATION_INCREASE_PER_LEVEL; + float blastMultiplier = 1.0f + WandHelper.getUpgradeLevel(stack, Wizardry.blastUpgrade)*Wizardry.BLAST_RADIUS_INCREASE_PER_LEVEL; - if(castingTick == 0) MinecraftForge.EVENT_BUS.post(new SpellCastEvent.Post(player, spell, modifiers, Source.WAND)); + // I would have liked to have made potion effects increase in strength according to the damage multiplier, + // but the amplifier level is too discrete to make this work. For example, wither 3 for 10 seconds will kill a + // normal mob on full 20 health, but wither 2 for the same duration only deals about 6 hearts of damage in total. + float damageMultiplier = 1.0f; - // = Mana cost = - // Divides the mana cost over a second appropriately; since damage is an integer it cannot - // just be divided by 20. - // Now does five times per second regardless of the spell cost, but each time it does 1/5 of the - // cost per second. - int tickNumber = (count % 20) + 1; - // Tests if the tick counter is a multiple of 4 plus 1, i.e. is true when tickNumber = 1, 5, 9, 13 or 17. - // Made a slight adjustment since the counter starts on 1 and not 4. - if(tickNumber % 4 == 1){ + if(this.element == spell.element){ + damageMultiplier = 1.0f + (this.tier.level + 1) * Wizardry.DAMAGE_INCREASE_PER_TIER; + } - int armourPieces = getMatchingArmourCount(player, spell); + if(spell.cast(player.worldObj, player, stack.getMaxItemUseDuration() - count, damageMultiplier, rangeMultiplier, durationMultiplier, blastMultiplier)){ - switch(armourPieces){ + // = Discovery = + if(!player.capabilities.isCreativeMode && !properties.hasSpellBeenDiscovered(spell) && Wizardry.discoveryMode){ + player.worldObj.playSoundAtEntity(player, "random.levelup", 1.25f, 1); + if(!player.worldObj.isRemote) player.addChatMessage(new ChatComponentTranslation("spell.discover", spell.getDisplayNameWithFormatting())); + } + properties.discoverSpell(spell); - case 0: stack.damageItem(spell.cost/5, player); - break; + // = Mana cost = + // Divides the mana cost over a second appropriately; since damage is an integer it cannot + // just be divided by 20. + // Now does five times per second regardless of the spell cost, but each time it does 1/5 of the + // cost per second. + // Removed the tick counter because it is cumbersome and stupid, when I can just as easily use the + // count argument given right here. + //properties.incrementTickNumber(); + int tickNumber = (count % 20) + 1; + // Tests if the tick counter is a multiple of 4 plus 1, i.e. is true when tickNumber = 1, 5, 9, 13 or 17. + // Made a slight adjustment since the counter starts on 1 and not 4. + if(tickNumber % 4 == 1){ - case 1: if(tickNumber != 17) stack.damageItem(spell.cost/5, player); - break; + int armourPieces = 0; - case 2: if(tickNumber != 9 && tickNumber != 17) stack.damageItem(spell.cost/5, player); - break; + for(int i=0; i<4; i++){ + if(player.getCurrentArmor(i) != null && player.getCurrentArmor(i).getItem() instanceof ItemWizardArmour + && ((ItemWizardArmour)player.getCurrentArmor(i).getItem()).element == spell.element) armourPieces++; + } - case 3: if(tickNumber != 5 && tickNumber != 13 && tickNumber != 17) stack.damageItem(spell.cost/5, player); - break; + switch(armourPieces){ - case 4: if(tickNumber == 1) stack.damageItem(spell.cost/5, player); - break; + case 0: properties.damageToApply += spell.cost/5; + break; + + case 1: if(tickNumber != 17) properties.damageToApply += spell.cost/5; + break; + + case 2: if(tickNumber != 9 && tickNumber != 17) properties.damageToApply += spell.cost/5; + break; + + case 3: if(tickNumber != 5 && tickNumber != 13 && tickNumber != 17) properties.damageToApply += spell.cost/5; + break; + + case 4: if(tickNumber == 1) properties.damageToApply += spell.cost/5; + break; - } } } } } } + /** + * Called when the player releases the use item button. Args: itemstack, world, entityplayer, itemInUseCount. + * Here it is used to apply damage after using a continuous spell. The damage shows up on the damage bar in real- + * time thanks to the getItemDamageForDisplay function, but is not actually applied until the button is released. + * This is because applying damage causes the wand to 're-equip', interrupting the spell. + * (Note that if the wand has no damage then minecraft doesn't try to render the bar, so the getItemDamageForDisplay + * function is never called, meaning if you use a continuous spell on an undamaged wand the damage doesn't appear + * until you release the mouse. A minor bug, but there's nothing I can do about it.) + */ @Override - public boolean itemInteractionForEntity(ItemStack stack, EntityPlayer player, EntityLivingBase entity, EnumHand hand){ + public void onPlayerStoppedUsing(ItemStack itemstack, World world, EntityPlayer entityplayer, int par4) { + ExtendedPlayer properties = ExtendedPlayer.get(entityplayer); + // This if statement should always be true, but is here just in case to stop the wand breaking. + if(properties.damageToApply <= itemstack.getMaxDamage() - itemstack.getItemDamage()){ + itemstack.damageItem(properties.damageToApply, entityplayer); + }else{ + itemstack.setItemDamage(itemstack.getMaxDamage()); + } + properties.damageToApply = 0; + } - if(player.isSneaking() && entity instanceof EntityPlayer && WizardData.get(player) != null){ + @Override + public boolean itemInteractionForEntity(ItemStack stack, EntityPlayer player, + EntityLivingBase entity){ + + if(player.isSneaking() && entity instanceof EntityPlayer && ExtendedPlayer.get(player) != null){ // This is one of those "the method doing the work looks as if it's just returning a value" situations. // ... I know, right?! I feel very programmer-y. But it's not too confusing here, and it looks neat. - String string = WizardData.get(player).toggleAlly((EntityPlayer)entity) ? "item.wizardry:wand.addally" : "item.wizardry:wand.removeally"; - if(!player.worldObj.isRemote) player.addChatMessage(new TextComponentTranslation(string, entity.getName())); + String string = ExtendedPlayer.get(player).toggleAlly((EntityPlayer)entity) ? "item.wand.addally" : "item.wand.removeally"; + if(!player.worldObj.isRemote) player.addChatMessage(new ChatComponentTranslation(string, entity.getCommandSenderName())); return true; } return false; } - - /** Returns a SpellModifiers object with the appropriate modifiers applied for the given ItemStack and Spell. */ - protected SpellModifiers calculateModifiers(ItemStack stack, Spell spell){ - - SpellModifiers modifiers = new SpellModifiers(); - - // Now we only need to add multipliers if they are not 1. - int level = WandHelper.getUpgradeLevel(stack, WizardryItems.range_upgrade); - if(level > 0) modifiers.set(WizardryItems.range_upgrade, 1.0f + level * Constants.RANGE_INCREASE_PER_LEVEL, true); - - level = WandHelper.getUpgradeLevel(stack, WizardryItems.duration_upgrade); - if(level > 0) modifiers.set(WizardryItems.duration_upgrade, 1.0f + level * Constants.DURATION_INCREASE_PER_LEVEL, false); - - level = WandHelper.getUpgradeLevel(stack, WizardryItems.blast_upgrade); - if(level > 0) modifiers.set(WizardryItems.blast_upgrade, 1.0f + level * Constants.BLAST_RADIUS_INCREASE_PER_LEVEL, true); - - // I would have liked to have made potion effects increase in strength according to the damage multiplier, - // but the amplifier level is too discrete to make this work. For example, wither 3 for 10 seconds will kill a - // normal mob on full 20 health, but wither 2 for the same duration only deals about 6 hearts of damage in total. - if(this.element == spell.element){ - modifiers.set(SpellModifiers.DAMAGE, 1.0f + (this.tier.level + 1) * Constants.DAMAGE_INCREASE_PER_TIER, true); - } - - return modifiers; - } - - /** Counts the number of armour pieces the given player is wearing that match the given spell's element. */ - private int getMatchingArmourCount(EntityPlayer player, Spell spell){ - - int armourPieces = 0; - - for(EntityEquipmentSlot slot : WizardryUtilities.ARMOUR_SLOTS){ - - ItemStack armour = player.getItemStackFromSlot(slot); - - if(armour != null && armour.getItem() instanceof ItemWizardArmour - && ((ItemWizardArmour)armour.getItem()).element == spell.element) armourPieces++; - } - - return armourPieces; - } - + private boolean selectMinionTarget(EntityPlayer player, World world){ - - RayTraceResult rayTrace = WizardryUtilities.standardEntityRayTrace(world, player, 16); - + + MovingObjectPosition rayTrace = WizardryUtilities.standardEntityRayTrace(world, player, 16); + if(rayTrace != null && rayTrace.entityHit instanceof EntityLivingBase){ - + EntityLivingBase entity = (EntityLivingBase)rayTrace.entityHit; - + // Sets the selected minion's target to the right-clicked entity - if(player.isSneaking() && WizardData.get(player) != null && WizardData.get(player).selectedMinion != null){ - - ISummonedCreature minion = WizardData.get(player).selectedMinion.get(); - - if(minion instanceof EntityLiving && minion != entity){ - // There is now only the new AI! (which greatly improves things) - ((EntityLiving)minion).setAttackTarget(entity); + if(player.isSneaking() && ExtendedPlayer.get(player) != null && ExtendedPlayer.get(player).selectedMinion != null){ + + EntitySummonedCreature minion = ExtendedPlayer.get(player).selectedMinion.get(); + + if(minion != null && minion != entity){ + // New AI + minion.setAttackTarget(entity); + // Old AI + minion.setTarget(entity); // Deselects the selected minion - WizardData.get(player).selectedMinion = null; + ExtendedPlayer.get(player).selectedMinion = null; return true; } } } - + return false; } + + @Override + public double getDurabilityForDisplay(ItemStack stack){ + return Wizardry.proxy.getWandDisplayDamage(stack); + } + + @Override + @SideOnly(Side.CLIENT) + public FontRenderer getFontRenderer(ItemStack stack){ + return Wizardry.proxy.getFontRenderer(stack); + } } diff --git a/src/main/java/electroblob/wizardry/item/ItemWizardArmour.java b/src/main/java/electroblob/wizardry/item/ItemWizardArmour.java index 97a18fee..1cdec683 100644 --- a/src/main/java/electroblob/wizardry/item/ItemWizardArmour.java +++ b/src/main/java/electroblob/wizardry/item/ItemWizardArmour.java @@ -1,157 +1,152 @@ package electroblob.wizardry.item; import java.util.List; +import java.util.Locale; +import cpw.mods.fml.relauncher.Side; +import cpw.mods.fml.relauncher.SideOnly; +import electroblob.wizardry.EnumElement; import electroblob.wizardry.Wizardry; -import electroblob.wizardry.constants.Constants; -import electroblob.wizardry.constants.Element; -import electroblob.wizardry.registry.WizardryTabs; -import electroblob.wizardry.spell.Petrify; -import electroblob.wizardry.util.WizardryUtilities; -import net.minecraft.creativetab.CreativeTabs; +import net.minecraft.client.model.ModelBiped; import net.minecraft.entity.Entity; import net.minecraft.entity.EntityLivingBase; import net.minecraft.entity.player.EntityPlayer; -import net.minecraft.inventory.EntityEquipmentSlot; import net.minecraft.item.EnumAction; -import net.minecraft.item.Item; import net.minecraft.item.ItemArmor; import net.minecraft.item.ItemStack; +import net.minecraft.potion.Potion; import net.minecraft.util.DamageSource; -import net.minecraft.util.EnumHandSide; +import net.minecraft.util.StatCollector; import net.minecraftforge.common.ISpecialArmor; -import net.minecraftforge.fml.relauncher.Side; -import net.minecraftforge.fml.relauncher.SideOnly; public class ItemWizardArmour extends ItemArmor implements ISpecialArmor { + + /* + * In case I ever wonder what I was doing: what I think went wrong is that during development of wizardry 1.2, + * I did of course go through everything and make it dedicated server-compatible. I also fixed the lag spike bug + * caused by this very class. HOWEVER, I must have done that AFTER making everything dedicated server-compatible, + * and in my ignorance introduced a field which instantiated ModelBiped, which is of course client-only. This is + * now fixed and the fix is rolled back to 1.2.1 (but not 1.2), along with the arguably more important fix in + * EntityWizard. Now, for some reason importing ModelBiped in a common class is fine, as long as you don't + * instantiate it or reflect the class in question. In fact, the Item class does exactly this, and that's forge's + * doing, so it can't possibly break things. + */ - public Element element; + public EnumElement element; - public ItemWizardArmour(ArmorMaterial material, int renderIndex, EntityEquipmentSlot armourType, Element element) { + /** Armour types: 0 = helmet, 1 = chestplate, 2 = leggings, 3 = boots */ + public ItemWizardArmour(ArmorMaterial material, int renderIndex, int armourType, EnumElement element) { super(material, renderIndex, armourType); this.element = element; - this.setCreativeTab(WizardryTabs.WIZARDRY); - } - - @Override - @SideOnly(Side.CLIENT) - public void addInformation(ItemStack stack, EntityPlayer player, List tooltip, boolean advanced){ + this.setCreativeTab(Wizardry.tabWizardry); - if(stack.hasTagCompound() && stack.getTagCompound().getBoolean("legendary")) tooltip.add("\u00A7d" + net.minecraft.client.resources.I18n.format("item.wizardry:wizard_armour.legendary")); - if(element != null) tooltip.add("\u00A78" + net.minecraft.client.resources.I18n.format("item.wizardry:wizard_armour.buff", (int)(Constants.COST_REDUCTION_PER_ARMOUR*100) + "%", element.getDisplayName())); - tooltip.add("\u00A79" + net.minecraft.client.resources.I18n.format("item.wizardry:wizard_armour.mana", (this.getMaxDamage(stack) - this.getDamage(stack)), this.getMaxDamage(stack))); + // Sets item icon texture and unlocalised name according to element and armour type. + switch(armourType){ + case 0: + this.setTextureName(this.element == null ? "wizardry:wizard_hat" : + "wizardry:wizard_hat_" + this.element.unlocalisedName); + this.setUnlocalizedName(this.element == null ? "wizard_hat" : "wizard_hat_" + this.element.unlocalisedName); + break; + case 1: + this.setTextureName(this.element == null ? "wizardry:wizard_robe" : + "wizardry:wizard_robe_" + this.element.unlocalisedName); + this.setUnlocalizedName(this.element == null ? "wizard_robe" : "wizard_robe_" + this.element.unlocalisedName); + break; + case 2: + this.setTextureName(this.element == null ? "wizardry:wizard_leggings" : + "wizardry:wizard_leggings_" + this.element.unlocalisedName); + this.setUnlocalizedName(this.element == null ? "wizard_leggings" : "wizard_leggings_" + this.element.unlocalisedName); + break; + case 3: + this.setTextureName(this.element == null ? "wizardry:wizard_boots" : + "wizardry:wizard_boots_" + this.element.unlocalisedName); + this.setUnlocalizedName(this.element == null ? "wizard_boots" : "wizard_boots_" + this.element.unlocalisedName); + break; + } } @Override - public String getItemStackDisplayName(ItemStack stack){ - return ((this.element == null ? "" : this.element.getFormattingCode()) + super.getItemStackDisplayName(stack)); + public void addInformation(ItemStack par1ItemStack, EntityPlayer par2EntityPlayer, List par3List, boolean par4){ + + if(par1ItemStack.hasTagCompound() && par1ItemStack.stackTagCompound.getBoolean("legendary")) par3List.add("\u00A7d" + StatCollector.translateToLocal("item.wizardArmour.legendary")); + if(element != null) par3List.add("\u00A78" + StatCollector.translateToLocalFormatted("item.wizardArmour.buff", (int)(Wizardry.COST_REDUCTION_PER_ARMOUR*100) + "%", element.getDisplayName())); + par3List.add("\u00A79" + StatCollector.translateToLocalFormatted("item.wizardArmour.mana", (this.getMaxDamage(par1ItemStack) - this.getDamage(par1ItemStack)), this.getMaxDamage(par1ItemStack))); + } + + @Override + public String getItemStackDisplayName(ItemStack p_77653_1_){ + + return ((this.element == null ? "" : this.element.colour) + StatCollector.translateToLocal(this.getUnlocalizedNameInefficiently(p_77653_1_) + ".name")).trim(); } @Override - @SideOnly(Side.CLIENT) - public boolean hasEffect(ItemStack stack){ - return stack.hasTagCompound() && stack.getTagCompound().getBoolean("legendary"); + public boolean hasEffect(ItemStack stack, int pass){ + return stack.hasTagCompound() && stack.stackTagCompound.getBoolean("legendary"); } @Override @SideOnly(Side.CLIENT) - public net.minecraft.client.model.ModelBiped getArmorModel(EntityLivingBase entityLiving, ItemStack itemStack, EntityEquipmentSlot armourSlot, net.minecraft.client.model.ModelBiped _default){ + public ModelBiped getArmorModel(EntityLivingBase entityLiving, ItemStack itemStack, int armourSlot){ - net.minecraft.client.model.ModelBiped model = Wizardry.proxy.getWizardArmourModel(); + ModelBiped model = Wizardry.proxy.getWizardArmourModel(); // Legs use modelBiped - if(armourSlot == EntityEquipmentSlot.LEGS) return null; + if(armourSlot == 2) return null; if(model != null){ - - model.bipedHead.showModel = armourSlot == EntityEquipmentSlot.HEAD; + model.bipedHead.showModel = armourSlot == 0; model.bipedHeadwear.showModel = false; - model.bipedBody.showModel = armourSlot == EntityEquipmentSlot.CHEST || armourSlot == EntityEquipmentSlot.LEGS; - model.bipedRightArm.showModel = armourSlot == EntityEquipmentSlot.CHEST; - model.bipedLeftArm.showModel = armourSlot == EntityEquipmentSlot.CHEST; - model.bipedRightLeg.showModel = armourSlot == EntityEquipmentSlot.LEGS || armourSlot == EntityEquipmentSlot.FEET; - model.bipedLeftLeg.showModel = armourSlot == EntityEquipmentSlot.LEGS || armourSlot == EntityEquipmentSlot.FEET; + model.bipedBody.showModel = armourSlot == 1 || armourSlot == 2; + model.bipedRightArm.showModel = armourSlot == 1; + model.bipedLeftArm.showModel = armourSlot == 1; + model.bipedRightLeg.showModel = armourSlot == 2 || armourSlot == 3; + model.bipedLeftLeg.showModel = armourSlot == 2 || armourSlot == 3; model.isSneak = entityLiving.isSneaking(); model.isRiding = entityLiving.isRiding(); model.isChild = entityLiving.isChild(); + model.heldItemRight = entityLiving.getHeldItem() != null ? 1 : 0; - boolean leftHanded = entityLiving.getPrimaryHand() == EnumHandSide.LEFT; - - ItemStack itemstackR = leftHanded ? entityLiving.getHeldItemOffhand() : entityLiving.getHeldItemMainhand(); - ItemStack itemstackL = leftHanded ? entityLiving.getHeldItemMainhand() : entityLiving.getHeldItemOffhand(); - - if (itemstackR != null) - { - model.rightArmPose = net.minecraft.client.model.ModelBiped.ArmPose.ITEM; - - if (entityLiving.getItemInUseCount() > 0) - { - EnumAction enumaction = itemstackR.getItemUseAction(); - - if (enumaction == EnumAction.BLOCK) - { - model.rightArmPose = net.minecraft.client.model.ModelBiped.ArmPose.BLOCK; - } - else if (enumaction == EnumAction.BOW) - { - model.rightArmPose = net.minecraft.client.model.ModelBiped.ArmPose.BOW_AND_ARROW; - } - } - } - - if (itemstackL != null) - { - model.leftArmPose = net.minecraft.client.model.ModelBiped.ArmPose.ITEM; - - if (entityLiving.getItemInUseCount() > 0) - { - EnumAction enumaction1 = itemstackL.getItemUseAction(); - - if (enumaction1 == EnumAction.BLOCK) - { - model.leftArmPose = net.minecraft.client.model.ModelBiped.ArmPose.BLOCK; - } - else if (enumaction1 == EnumAction.BOW) - { - model.leftArmPose = net.minecraft.client.model.ModelBiped.ArmPose.BOW_AND_ARROW; - } - } - } + if(entityLiving instanceof EntityPlayer){ + if(entityLiving.getHeldItem() != null){ + if(entityLiving.getHeldItem().getItem().getItemUseAction(entityLiving.getHeldItem()) == EnumAction.bow){ + model.aimedBow = ((EntityPlayer)entityLiving).getItemInUseDuration() > 0; + } + if(entityLiving.getHeldItem().getItem().getItemUseAction(entityLiving.getHeldItem()) == EnumAction.block){ + if(((EntityPlayer)entityLiving).getItemInUseDuration() > 0) model.heldItemRight = 3; + } + } + } } return model; } @Override - public String getArmorTexture(ItemStack stack, Entity entity, EntityEquipmentSlot slot, String type) { - + public String getArmorTexture(ItemStack stack, Entity entity, int slot, String type){ + // Returns a completely transparent texture if the player is invisible. This is such an annoyingly easy // fix, considering how long I spent trying to do this before - a bit of lateral thinking was all it took. // Do note however that a texture pack could override this. - if(entity instanceof EntityLivingBase && ((EntityLivingBase)entity).isInvisible() - && !entity.getEntityData().getBoolean(Petrify.NBT_KEY)) return "wizardry:textures/armour/invisible_armour.png"; + if(entity instanceof EntityLivingBase && ((EntityLivingBase)entity).isInvisible()) return "wizardry:textures/armour/invisible_armour.png"; - if(slot == EntityEquipmentSlot.LEGS) return this.element == null ? "wizardry:textures/armour/wizard_armour_legs.png" : - "wizardry:textures/armour/wizard_armour_" + this.element.getUnlocalisedName() + "_legs.png"; + if(slot == 2) return this.element == null ? "wizardry:textures/armour/wizard_armour_legs.png" : + "wizardry:textures/armour/wizard_armour_" + this.element.unlocalisedName + "_legs.png"; return this.element == null ? "wizardry:textures/armour/wizard_armour.png" : - "wizardry:textures/armour/wizard_armour_" + this.element.getUnlocalisedName() + ".png"; - } + "wizardry:textures/armour/wizard_armour_" + this.element.unlocalisedName + ".png"; + } @Override - public boolean getIsRepairable(ItemStack stack, ItemStack par2ItemStack) + public boolean getIsRepairable(ItemStack par1ItemStack, ItemStack par2ItemStack) { return false; } @Override - public ArmorProperties getProperties(EntityLivingBase player, ItemStack armor, DamageSource source, double damage, int slotIndex){ - - EntityEquipmentSlot slot = getArmorSlotFromIndex(slotIndex); - + public ArmorProperties getProperties(EntityLivingBase player, ItemStack armor, DamageSource source, double damage, int slot){ if (!source.isUnblockable() && armor.getItemDamage() < armor.getMaxDamage()){ - if(armor.hasTagCompound() && armor.getTagCompound().getBoolean("legendary")){ + if(armor.hasTagCompound() && armor.stackTagCompound.getBoolean("legendary")){ // Legendary armour gives full 10 shields, like diamond. return new ArmorProperties(0, ArmorMaterial.DIAMOND.getDamageReductionAmount(slot) / 25D, armor.getMaxDamage() + 1 - armor.getItemDamage()); }else{ @@ -163,12 +158,9 @@ public class ItemWizardArmour extends ItemArmor implements ISpecialArmor { } @Override - public int getArmorDisplay(EntityPlayer player, ItemStack armor, int slotIndex) { - - EntityEquipmentSlot slot = getArmorSlotFromIndex(slotIndex); - + public int getArmorDisplay(EntityPlayer player, ItemStack armor, int slot) { if(armor.getItemDamage() < armor.getMaxDamage()){ - if(armor.hasTagCompound() && armor.getTagCompound().getBoolean("legendary")){ + if(armor.hasTagCompound() && armor.stackTagCompound.getBoolean("legendary")){ // Legendary armour gives full 10 shields, like diamond. return ArmorMaterial.DIAMOND.getDamageReductionAmount(slot); }else{ @@ -191,27 +183,5 @@ public class ItemWizardArmour extends ItemArmor implements ISpecialArmor { } } } - - @Override - @SideOnly(Side.CLIENT) - public void getSubItems(Item item, CreativeTabs tab, List subItems){ - subItems.add(new ItemStack(this, 1)); - } - - /** Returns the EntityEquipmentSlot for which index() returns an integer equal to the passed in slotIndex and - * which is an armour slot (not a hand slot). This only exists because the 1.10.2 version of Forge still uses - * an integer slot index in ISpecialArmor instead of an EntityEquipmentSlot. */ - // NOTE: Remove in 1.11.2 - private EntityEquipmentSlot getArmorSlotFromIndex(int slotIndex) { - // Had to pick something to begin with. - EntityEquipmentSlot slot = EntityEquipmentSlot.HEAD; - - for(EntityEquipmentSlot s : WizardryUtilities.ARMOUR_SLOTS){ - if(s.getIndex() == slotIndex){ - slot = s; - } - } - return slot; - } } diff --git a/src/main/java/electroblob/wizardry/item/ItemWizardHandbook.java b/src/main/java/electroblob/wizardry/item/ItemWizardHandbook.java index 3b7060db..01be5c44 100644 --- a/src/main/java/electroblob/wizardry/item/ItemWizardHandbook.java +++ b/src/main/java/electroblob/wizardry/item/ItemWizardHandbook.java @@ -4,43 +4,33 @@ import java.util.List; import electroblob.wizardry.Wizardry; import electroblob.wizardry.WizardryGuiHandler; -import electroblob.wizardry.registry.WizardryTabs; +import electroblob.wizardry.client.GuiWizardHandbook; +import electroblob.wizardry.spell.Spell; import net.minecraft.creativetab.CreativeTabs; import net.minecraft.entity.player.EntityPlayer; import net.minecraft.item.Item; import net.minecraft.item.ItemStack; -import net.minecraft.util.ActionResult; -import net.minecraft.util.EnumActionResult; -import net.minecraft.util.EnumHand; +import net.minecraft.util.StatCollector; import net.minecraft.world.World; -import net.minecraftforge.fml.relauncher.Side; -import net.minecraftforge.fml.relauncher.SideOnly; -public class ItemWizardHandbook extends Item { - - // Yep, I hardcoded my own name into the mod. Don't want people changing it now, do I? - private static final String AUTHOR = "Electroblob"; +public class ItemWizardHandbook extends Item{ public ItemWizardHandbook() { super(); this.setMaxStackSize(1); - this.setCreativeTab(WizardryTabs.WIZARDRY); - } - - @Override - public void addInformation(ItemStack stack, EntityPlayer player, List tooltip, boolean advanced){ - tooltip.add("\u00A77" + net.minecraft.client.resources.I18n.format("item.wizardry:wizard_handbook.desc", AUTHOR)); - } - - @Override - public ActionResult onItemRightClick(ItemStack stack, World world, EntityPlayer player, EnumHand hand){ - player.openGui(Wizardry.instance, WizardryGuiHandler.WIZARD_HANDBOOK, world, 0, 0, 0); - return ActionResult.newResult(EnumActionResult.SUCCESS, stack); + this.setCreativeTab(Wizardry.tabWizardry); + this.setTextureName("wizardry:wizard_handbook"); + this.setUnlocalizedName("wizardHandbook"); } - @Override - @SideOnly(Side.CLIENT) - public void getSubItems(Item item, CreativeTabs tab, List items){ - items.add(new ItemStack(this, 1)); - } + public void addInformation(ItemStack par1ItemStack, EntityPlayer par2EntityPlayer, List par3List, boolean par4){ + Spell spell = Spell.get(par1ItemStack.getItemDamage()); + // Yep, I hardcoded my own name into the mod. Don't want people changing it now, do I? + par3List.add("\u00A77" + StatCollector.translateToLocalFormatted("item.wizardHandbook.desc", "Electroblob")); + } + + public ItemStack onItemRightClick(ItemStack par1ItemStack, World par2World, EntityPlayer par3EntityPlayer){ + par3EntityPlayer.openGui(Wizardry.instance, WizardryGuiHandler.WIZARD_HANDBOOK, par2World, 0, 0, 0); + return par1ItemStack; + } } diff --git a/src/main/java/electroblob/wizardry/loot/RandomSpell.java b/src/main/java/electroblob/wizardry/loot/RandomSpell.java deleted file mode 100644 index c068ccb7..00000000 --- a/src/main/java/electroblob/wizardry/loot/RandomSpell.java +++ /dev/null @@ -1,267 +0,0 @@ -package electroblob.wizardry.loot; - -import java.util.ArrayList; -import java.util.List; -import java.util.Random; - -import org.apache.commons.lang3.ArrayUtils; - -import com.google.gson.JsonArray; -import com.google.gson.JsonDeserializationContext; -import com.google.gson.JsonElement; -import com.google.gson.JsonObject; -import com.google.gson.JsonPrimitive; -import com.google.gson.JsonSerializationContext; -import com.google.gson.JsonSyntaxException; - -import electroblob.wizardry.Wizardry; -import electroblob.wizardry.constants.Element; -import electroblob.wizardry.constants.Tier; -import electroblob.wizardry.item.ItemScroll; -import electroblob.wizardry.item.ItemSpellBook; -import electroblob.wizardry.spell.Spell; -import electroblob.wizardry.util.WizardryUtilities; -import net.minecraft.item.ItemStack; -import net.minecraft.util.JsonUtils; -import net.minecraft.util.ResourceLocation; -import net.minecraft.world.storage.loot.LootContext; -import net.minecraft.world.storage.loot.conditions.LootCondition; -import net.minecraft.world.storage.loot.functions.LootFunction; - -/** Loot function that allows spell books and scrolls to select a random spell based on the standard weighting. - * Automatically discounts continuous spells when the item in question is a scroll. Can be used as-is with no - * parameters, but several optional parameters are available for those wishing to customise further: - *

    - * - spells: A list of spells to choose from. Defaults to all enabled spells.
    - * - ignore_weighting: true to ignore the standard weighting and just pick a completely random spell. Defaults to - * false.
    - * - tiers: A list of tiers to choose from. Defaults to all tiers.
    - * - elements: A list of elements to choose from. Defaults to all elements. - *

    - * This class is effectively a loot table-friendly replacement for the standard weighting method in WizardryUtilities - * which was the basis of all the old loot systems (not counting wizard trades, which were - and still are - completely - * separate). - *

    - * Since spells are stored as metadata, this could be done by having an entry for each tier and letting it pick a - * random spell from that tier by setting the metadata to a random value in the range of values that correspond to that - * tier. However, this creates a two-fold problem: firstly, not all spells are in tier order, and secondly, if spells are - * added via addon mods the entries would have to be updated manually with the new numbers. - *

    - * (This reasoning is similar to that for the enchant_randomly function in vanilla Minecraft, since you can specify NBT - * data in loot tables, but using NBT in this way would be incredibly verbose and inflexible, hence the loot function.) - * @see WizardryUtilities#getStandardWeightedRandomSpellId(Random, boolean) - * @author Electroblob - * @since Wizardry 1.2 - */ -/* - * You could even use the yet more long-winded method of adding each spell as an individual entry, which would be - * stupidly long but would allow precise control over each individual spell... I'll leave that for pack makers to do - * if they have the patience! - * */ -public class RandomSpell extends LootFunction { - - private final List spells; - private final boolean ignoreWeighting; - private final List tiers; - private final List elements; - - protected RandomSpell(LootCondition[] conditions, List spells, boolean ignoreWeighting, List tiers, List elements){ - super(conditions); - this.spells = spells; - this.ignoreWeighting = ignoreWeighting; - this.tiers = tiers; - this.elements = elements; - } - - @Override - public ItemStack apply(ItemStack stack, Random random, LootContext context){ - - 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."); - - 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 BASIC - if(tier == Tier.BASIC){ - 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(tiers.size())); - //element = null; - } - - // 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 spellsList = Spell.getSpells(new Spell.TierElementFilter(tier, element)); - if(stack.getItem() instanceof ItemScroll) spellsList.retainAll(Spell.getSpells(Spell.nonContinuousSpells)); - - // Ensures the tier chosen actually has spells in it, and if not uses BASIC instead. BASIC always has at least - // the NONE spell since this spell cannot be disabled. - // NOTE: 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.BASIC, null)); - if(stack.getItem() instanceof ItemScroll) spellsList.retainAll(Spell.getSpells(Spell.nonContinuousSpells)); - } - */ - - if(spells != null && !spells.isEmpty()){ - spellsList.retainAll(spells); - } - - if(spellsList.isEmpty()){ - 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())).id()); - } - - return stack; - } - - public static class Serializer extends LootFunction.Serializer{ - - public Serializer(){ - super(new ResourceLocation(Wizardry.MODID, "random_spell"), RandomSpell.class); - } - - public void serialize(JsonObject object, RandomSpell function, JsonSerializationContext serializationContext){ - - if(function.spells != null && !function.spells.isEmpty()){ - - JsonArray jsonarray = new JsonArray(); - - for(Spell spell : function.spells){ - jsonarray.add(new JsonPrimitive(spell.getRegistryName().toString())); - } - - object.add("spells", jsonarray); - } - - object.addProperty("ignore_weighting", function.ignoreWeighting); - - if(function.tiers != null && !function.tiers.isEmpty()){ - - JsonArray jsonarray = new JsonArray(); - - for(Tier tier : function.tiers){ - jsonarray.add(new JsonPrimitive(tier.getUnlocalisedName())); - } - - object.add("tiers", jsonarray); - } - - if(function.elements != null && !function.elements.isEmpty()){ - - JsonArray jsonarray = new JsonArray(); - - for(Element element : function.elements){ - jsonarray.add(new JsonPrimitive(element.getUnlocalisedName())); - } - - object.add("elements", jsonarray); - } - } - - public RandomSpell deserialize(JsonObject object, JsonDeserializationContext deserializationContext, LootCondition[] conditions){ - - List spells = null; - List tiers = null; - List elements = null; - - if(object.has("spells")){ - - // Vanilla Minecraft calls Lists.newArrayList() here, which is completely pointless. - spells = new ArrayList(); - - // Importantly, it is necessary to specify a default (the new JsonArray) here because otherwise the - // parameter will be mandatory, and the game will crash if it isn't present. - for (JsonElement element : JsonUtils.getJsonArray(object, "spells", new JsonArray())){ - - String string = JsonUtils.getString(element, "spell"); - - Spell spell = Spell.get(string); - - if(spell == null){ - throw new JsonSyntaxException("Unknown spell \'" + string + "\'"); - } - - spells.add(spell); - } - } - - boolean ignoreWeighting = JsonUtils.getBoolean(object, "ignore_weighting", false); - - if(object.has("tiers")){ - - // Vanilla Minecraft calls Lists.newArrayList() here, which is completely pointless. - tiers = new ArrayList(); - - jsonarray: - for(JsonElement element : JsonUtils.getJsonArray(object, "tiers", new JsonArray())){ - - String string = JsonUtils.getString(element, "tier"); - - // IDEA: If it is necessary to get tiers by name elsewhere in the future, move this to EnumTier. - for(Tier tier : Tier.values()){ - if(tier.getUnlocalisedName().equals(string)){ - tiers.add(tier); - continue jsonarray; - } - } - // If the string does not match any of the tiers, throws an exception. - throw new JsonSyntaxException("Unknown tier \'" + string + "\'"); - } - } - - if(object.has("elements")){ - - // Vanilla Minecraft calls Lists.newArrayList() here, which is completely pointless. - elements = new ArrayList(); - - jsonarray: - for(JsonElement jelement : JsonUtils.getJsonArray(object, "elements", new JsonArray())){ - - String string = JsonUtils.getString(jelement, "element"); - - // IDEA: If it is necessary to get elements by name elsewhere in the future, move this to EnumElement. - for(Element element : Element.values()){ - if(element.getUnlocalisedName().equals(string)){ - elements.add(element); - continue jsonarray; - } - } - // If the string does not match any of the elements, throws an exception. - throw new JsonSyntaxException("Unknown element \'" + string + "\'"); - } - } - - return new RandomSpell(conditions, spells, ignoreWeighting, tiers, elements); - } - } - -} diff --git a/src/main/java/electroblob/wizardry/loot/WizardSpell.java b/src/main/java/electroblob/wizardry/loot/WizardSpell.java deleted file mode 100644 index c52898b5..00000000 --- a/src/main/java/electroblob/wizardry/loot/WizardSpell.java +++ /dev/null @@ -1,65 +0,0 @@ -package electroblob.wizardry.loot; - -import java.util.List; -import java.util.Random; - -import com.google.gson.JsonDeserializationContext; -import com.google.gson.JsonObject; -import com.google.gson.JsonSerializationContext; - -import electroblob.wizardry.Wizardry; -import electroblob.wizardry.entity.living.ISpellCaster; -import electroblob.wizardry.item.ItemSpellBook; -import electroblob.wizardry.registry.Spells; -import electroblob.wizardry.spell.Spell; -import net.minecraft.item.ItemStack; -import net.minecraft.util.ResourceLocation; -import net.minecraft.world.storage.loot.LootContext; -import net.minecraft.world.storage.loot.conditions.LootCondition; -import net.minecraft.world.storage.loot.functions.LootFunction; - -/** - * Loot function that allows spell books to select a random spell from the spells used by the ISpellCaster that dropped - * them. - * @author Electroblob - * @since Wizardry 1.2 - */ -public class WizardSpell extends LootFunction { - - protected WizardSpell(LootCondition[] conditions){ - super(conditions); - } - - @Override - public ItemStack apply(ItemStack stack, Random random, LootContext context){ - - if(!(stack.getItem() instanceof ItemSpellBook)) - Wizardry.logger.warn("Applying the wizard_spell loot function to an item that isn't a spell book or scroll."); - - if(context.getLootedEntity() instanceof ISpellCaster){ - List spells = ((ISpellCaster)context.getLootedEntity()).getSpells(); - spells.remove(Spells.magic_missile); // Can't drop magic missile - stack.setItemDamage(spells.get(random.nextInt(spells.size())).id()); - }else{ - Wizardry.logger.warn("Applying the wizard_spell loot function to an entity that isn't a spell caster."); - } - - return stack; - } - - public static class Serializer extends LootFunction.Serializer{ - - public Serializer(){ - super(new ResourceLocation(Wizardry.MODID, "wizard_spell"), WizardSpell.class); - } - - public void serialize(JsonObject object, WizardSpell function, JsonSerializationContext serializationContext){ - - } - - public WizardSpell deserialize(JsonObject object, JsonDeserializationContext deserializationContext, LootCondition[] conditions){ - return new WizardSpell(conditions); - } - } - -} diff --git a/src/main/java/electroblob/wizardry/packet/PacketCastContinuousSpell.java b/src/main/java/electroblob/wizardry/packet/PacketCastContinuousSpell.java index 9ef22bae..b467526b 100644 --- a/src/main/java/electroblob/wizardry/packet/PacketCastContinuousSpell.java +++ b/src/main/java/electroblob/wizardry/packet/PacketCastContinuousSpell.java @@ -1,70 +1,82 @@ package electroblob.wizardry.packet; +import cpw.mods.fml.common.network.simpleimpl.IMessage; +import cpw.mods.fml.common.network.simpleimpl.IMessageHandler; +import cpw.mods.fml.common.network.simpleimpl.MessageContext; import electroblob.wizardry.Wizardry; import electroblob.wizardry.packet.PacketCastContinuousSpell.Message; -import electroblob.wizardry.util.SpellModifiers; +import electroblob.wizardry.spell.Spell; import io.netty.buffer.ByteBuf; -import net.minecraftforge.fml.common.network.simpleimpl.IMessage; -import net.minecraftforge.fml.common.network.simpleimpl.IMessageHandler; -import net.minecraftforge.fml.common.network.simpleimpl.MessageContext; +import net.minecraft.entity.Entity; +import net.minecraft.entity.EntityLiving; +import net.minecraft.entity.EntityLivingBase; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.item.ItemStack; +import net.minecraft.item.Item; +import net.minecraft.world.World; -/** [Server -> Client] This packet is sent when the /cast command is used with a continuous spell, in order to - * sync the relevant variables in {@link electroblob.wizardry.WizardData WizardData}. */ +/** This packet is sent when the /cast command is used with a continuous spell, in order to sync the relevant variables + * in ExtendedPlayer. */ public class PacketCastContinuousSpell implements IMessageHandler { - + @Override - public IMessage onMessage(Message message, MessageContext ctx){ - + public IMessage onMessage(Message message, MessageContext ctx) + { // Just to make sure that the side is correct if(ctx.side.isClient()){ - // Using a fully qualified name is a good course of action here; we don't really want to clutter the proxy - // methods any more than necessary. - net.minecraft.client.Minecraft.getMinecraft().addScheduledTask(new Runnable(){ - @Override - public void run() { - Wizardry.proxy.handleCastContinuousSpellPacket(message); - } - }); + Wizardry.proxy.handleCastContinuousSpellPacket(message); } return null; } - public static class Message implements IMessage { - + public static class Message implements IMessage + { // Note that range and blast multipliers are the only two that affect particle spawning, so they are the // only two that need to be sent. - + /** EntityID of the caster */ public int casterID; /** ID of the spell being cast */ public int spellID; - /** SpellModifiers for the spell */ - public SpellModifiers modifiers; + /** Range multiplier for the spell */ + public float damageMultiplier; + /** Range multiplier for the spell */ + public float rangeMultiplier; + /** Blast multiplier for the spell */ + public float blastMultiplier; // This constructor is required otherwise you'll get errors (used somewhere in fml through reflection) - public Message(){} + public Message() {} - public Message(int casterID, int spellID, SpellModifiers modifiers){ + public Message(int casterID, int spellID, float damageMultiplier, float rangeMultiplier, float blastMultiplier) + { this.casterID = casterID; this.spellID = spellID; - this.modifiers = modifiers; + this.damageMultiplier = damageMultiplier; + this.rangeMultiplier = rangeMultiplier; + this.blastMultiplier = blastMultiplier; } @Override - public void fromBytes(ByteBuf buf){ + public void fromBytes(ByteBuf buf) + { // The order is important this.casterID = buf.readInt(); this.spellID = buf.readInt(); - this.modifiers = new SpellModifiers(); - modifiers.read(buf); + this.damageMultiplier = buf.readFloat(); + this.rangeMultiplier = buf.readFloat(); + this.blastMultiplier = buf.readFloat(); } @Override - public void toBytes(ByteBuf buf){ + public void toBytes(ByteBuf buf) + { buf.writeInt(casterID); buf.writeInt(spellID); - this.modifiers.write(buf); + buf.writeFloat(damageMultiplier); + buf.writeFloat(rangeMultiplier); + buf.writeFloat(blastMultiplier); } } } diff --git a/src/main/java/electroblob/wizardry/packet/PacketCastSpell.java b/src/main/java/electroblob/wizardry/packet/PacketCastSpell.java index cf57872c..e12badae 100644 --- a/src/main/java/electroblob/wizardry/packet/PacketCastSpell.java +++ b/src/main/java/electroblob/wizardry/packet/PacketCastSpell.java @@ -1,80 +1,92 @@ package electroblob.wizardry.packet; +import cpw.mods.fml.common.network.simpleimpl.IMessage; +import cpw.mods.fml.common.network.simpleimpl.IMessageHandler; +import cpw.mods.fml.common.network.simpleimpl.MessageContext; import electroblob.wizardry.Wizardry; import electroblob.wizardry.packet.PacketCastSpell.Message; -import electroblob.wizardry.util.SpellModifiers; +import electroblob.wizardry.spell.Spell; import io.netty.buffer.ByteBuf; +import net.minecraft.entity.Entity; +import net.minecraft.entity.EntityLiving; +import net.minecraft.entity.EntityLivingBase; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.item.ItemStack; import net.minecraft.item.Item; -import net.minecraft.util.EnumHand; -import net.minecraftforge.fml.common.network.simpleimpl.IMessage; -import net.minecraftforge.fml.common.network.simpleimpl.IMessageHandler; -import net.minecraftforge.fml.common.network.simpleimpl.MessageContext; +import net.minecraft.world.World; -/** [Server -> Client] This packet is sent when a spell is cast by a player and returns true, and is sent to other - * clients so they can spawn the particles themselves. What sending this packet effectively does is make the - * {@link Item#onItemRightClick} method client-consistent just for the item that sends it. Interestingly, - * {@link Item#onUsingTick} is client-consistent already, so continuous spells don't need to send packets from there - * (this is probably something to do with eating particles or usage actions). */ +/** This packet is sent when a spell is actually cast and returns true, and is sent to other clients so they can spawn + * the particles themselves. What sending this packet effectively does is make the {@link Item#onItemRightClick} method client-consistent + * just for the item that sends it. Interestingly, {@link Item#onUsingTick} is client-consistent already, so continuous + * spells don't ever need to send packets (this is probably something to do with eating particles or usage actions). */ public class PacketCastSpell implements IMessageHandler { - - @Override - public IMessage onMessage(Message message, MessageContext ctx){ + @Override + public IMessage onMessage(Message message, MessageContext ctx) + { // Just to make sure that the side is correct if(ctx.side.isClient()){ - // Using a fully qualified name is a good course of action here; we don't really want to clutter the proxy - // methods any more than necessary. - net.minecraft.client.Minecraft.getMinecraft().addScheduledTask(new Runnable(){ - @Override - public void run(){ - Wizardry.proxy.handleCastSpellPacket(message); - } - }); + Wizardry.proxy.handleCastSpellPacket(message); } return null; } - public static class Message implements IMessage { - + public static class Message implements IMessage + { + // Note that range and blast multipliers are the only two that affect particle spawning, so they are the + // only two that need to be sent. + /** EntityID of the caster */ public int casterID; + /** EntityID of the target */ + public int targetID; /** ID of the spell being cast */ public int spellID; - /** SpellModifiers for the spell */ - public SpellModifiers modifiers; - /** The hand that is holding the itemstack used to cast the spell. Defaults to MAIN_HAND. */ - public EnumHand hand; + // Do these really need to be here or can I just get them from the wand nbt at the other end? + // ... have I tried that before? + // You do now, because of the /cast command. + /** Range multiplier for the spell */ + public float damageMultiplier; + /** Range multiplier for the spell */ + public float rangeMultiplier; + /** Blast multiplier for the spell */ + public float blastMultiplier; // This constructor is required otherwise you'll get errors (used somewhere in fml through reflection) - public Message(){} + public Message() {} - public Message(int casterID, EnumHand hand, int spellID, SpellModifiers modifiers){ - + public Message(int casterID, int targetID, int spellID, float damageMultiplier, float rangeMultiplier, float blastMultiplier) + { this.casterID = casterID; + this.targetID = targetID; this.spellID = spellID; - this.modifiers = modifiers; - this.hand = hand == null ? EnumHand.MAIN_HAND : hand; + this.damageMultiplier = damageMultiplier; + this.rangeMultiplier = rangeMultiplier; + this.blastMultiplier = blastMultiplier; } @Override - public void fromBytes(ByteBuf buf){ - + public void fromBytes(ByteBuf buf) + { // The order is important this.casterID = buf.readInt(); + this.targetID = buf.readInt(); this.spellID = buf.readInt(); - this.modifiers = new SpellModifiers(); - this.modifiers.read(buf); - this.hand = buf.readBoolean() ? EnumHand.OFF_HAND : EnumHand.MAIN_HAND; + this.damageMultiplier = buf.readFloat(); + this.rangeMultiplier = buf.readFloat(); + this.blastMultiplier = buf.readFloat(); } @Override - public void toBytes(ByteBuf buf){ - + public void toBytes(ByteBuf buf) + { buf.writeInt(casterID); + buf.writeInt(targetID); buf.writeInt(spellID); - this.modifiers.write(buf); - buf.writeBoolean(this.hand == EnumHand.OFF_HAND); + buf.writeFloat(damageMultiplier); + buf.writeFloat(rangeMultiplier); + buf.writeFloat(blastMultiplier); } } } diff --git a/src/main/java/electroblob/wizardry/packet/PacketClairvoyance.java b/src/main/java/electroblob/wizardry/packet/PacketClairvoyance.java index f7f350bd..3daab7b3 100644 --- a/src/main/java/electroblob/wizardry/packet/PacketClairvoyance.java +++ b/src/main/java/electroblob/wizardry/packet/PacketClairvoyance.java @@ -3,17 +3,17 @@ package electroblob.wizardry.packet; import java.util.ArrayList; import java.util.List; +import cpw.mods.fml.common.network.simpleimpl.IMessage; +import cpw.mods.fml.common.network.simpleimpl.IMessageHandler; +import cpw.mods.fml.common.network.simpleimpl.MessageContext; import electroblob.wizardry.Wizardry; import electroblob.wizardry.packet.PacketClairvoyance.Message; import io.netty.buffer.ByteBuf; -import net.minecraft.pathfinding.Path; +import net.minecraft.pathfinding.PathEntity; import net.minecraft.pathfinding.PathPoint; -import net.minecraftforge.fml.common.network.simpleimpl.IMessage; -import net.minecraftforge.fml.common.network.simpleimpl.IMessageHandler; -import net.minecraftforge.fml.common.network.simpleimpl.MessageContext; -/** [Server -> Client] This packet is sent when a player casts the clairvoyance spell to allow pathing to chunks - * outside the render distance. */ +/** This packet is sent when a player casts the clairvoyance spell to allow pathing to chunks outside the render + * distance. */ public class PacketClairvoyance implements IMessageHandler { @Override @@ -21,14 +21,7 @@ public class PacketClairvoyance implements IMessageHandler { { // Just to make sure that the side is correct if(ctx.side.isClient()){ - // Using a fully qualified name is a good course of action here; we don't really want to clutter the proxy - // methods any more than necessary. - net.minecraft.client.Minecraft.getMinecraft().addScheduledTask(new Runnable(){ - @Override - public void run(){ - Wizardry.proxy.handleClairvoyancePacket(message); - } - }); + Wizardry.proxy.handleClairvoyancePacket(message); } return null; @@ -36,13 +29,13 @@ public class PacketClairvoyance implements IMessageHandler { public static class Message implements IMessage { - public Path path; + public PathEntity path; public float durationMultiplier; // This constructor is required otherwise you'll get errors (used somewhere in fml through reflection) public Message() {} - public Message(Path path, float durationMultiplier){ + public Message(PathEntity path, float durationMultiplier){ this.path = path; this.durationMultiplier = durationMultiplier; @@ -60,7 +53,7 @@ public class PacketClairvoyance implements IMessageHandler { points.add(new PathPoint(buf.readInt(), buf.readInt(), buf.readInt())); } - this.path = new Path(points.toArray(new PathPoint[0])); + this.path = new PathEntity(points.toArray(new PathPoint[points.size()])); } @Override diff --git a/src/main/java/electroblob/wizardry/packet/PacketControlInput.java b/src/main/java/electroblob/wizardry/packet/PacketControlInput.java index afa52a4f..056782c8 100644 --- a/src/main/java/electroblob/wizardry/packet/PacketControlInput.java +++ b/src/main/java/electroblob/wizardry/packet/PacketControlInput.java @@ -1,100 +1,282 @@ package electroblob.wizardry.packet; +import cpw.mods.fml.common.network.simpleimpl.IMessage; +import cpw.mods.fml.common.network.simpleimpl.IMessageHandler; +import cpw.mods.fml.common.network.simpleimpl.MessageContext; +import electroblob.wizardry.EnumTier; +import electroblob.wizardry.ExtendedPlayer; +import electroblob.wizardry.WandHelper; +import electroblob.wizardry.Wizardry; +import electroblob.wizardry.WizardryRegistry; +import electroblob.wizardry.WizardryUtilities; import electroblob.wizardry.item.ItemWand; +import electroblob.wizardry.item.ItemWizardArmour; import electroblob.wizardry.packet.PacketControlInput.Message; +import electroblob.wizardry.spell.Spell; import electroblob.wizardry.tileentity.ContainerArcaneWorkbench; -import electroblob.wizardry.util.WandHelper; +import electroblob.wizardry.tileentity.TileEntityArcaneWorkbench; import io.netty.buffer.ByteBuf; -import net.minecraft.entity.player.EntityPlayerMP; +import net.minecraft.entity.player.EntityPlayer; import net.minecraft.item.ItemStack; -import net.minecraftforge.fml.common.network.simpleimpl.IMessage; -import net.minecraftforge.fml.common.network.simpleimpl.IMessageHandler; -import net.minecraftforge.fml.common.network.simpleimpl.MessageContext; - -/** [Client -> Server] This packet is for control events such as buttons in GUIs and key presses. */ -public class PacketControlInput implements IMessageHandler { +import net.minecraft.nbt.NBTTagCompound; +/** This packet is for control events such as buttons in GUIs and key presses. */ +public class PacketControlInput implements IMessageHandler +{ @Override - public IMessage onMessage(Message message, MessageContext ctx){ - + public IMessage onMessage(Message message, MessageContext ctx) + { // Just to make sure that the side is correct - if(ctx.side.isServer()){ + if (ctx.side.isServer()) + { + int eventID = message.eventID; + EntityPlayer entityplayer = ctx.getServerHandler().playerEntity; + + if(eventID == 0){ + // Note: the following long line of code gets the itemstack in a roundabout way because it needs + // to access the tile entity rather than the container to actually get to the items. + TileEntityArcaneWorkbench tileentity = ((ContainerArcaneWorkbench)entityplayer.openContainer).tileEntityArcaneWorkbench; + + ItemStack wand = tileentity.getStackInSlot(ContainerArcaneWorkbench.WAND_SLOT); + ItemStack[] spellBooks = new ItemStack[ContainerArcaneWorkbench.CRYSTAL_SLOT]; + for(int i=0; i 0){ + for(int i=0; i ((ItemWand)wand.getItem()).tier.level)){ + spells[i] = Spell.get(spellBooks[i].getItemDamage()); + } + } + WandHelper.setSpells(wand, spells); + + // Charges wand by appropriate amount + if(crystals != null){ + int chargeDepleted = wand.getItemDamage(); + //System.out.println("Charge depleted: " + chargeDepleted); + //System.out.println("Crystals found: " + crystals.stackSize); + if(crystals.stackSize * Wizardry.MANA_PER_CRYSTAL < chargeDepleted){ + //System.out.println("charging"); + wand.setItemDamage(chargeDepleted - crystals.stackSize * Wizardry.MANA_PER_CRYSTAL); + tileentity.decrStackSize(ContainerArcaneWorkbench.CRYSTAL_SLOT, crystals.stackSize); + }else if(chargeDepleted != 0){ + //System.out.println((int)Math.ceil(((double)chargeDepleted)/50)); + tileentity.decrStackSize(ContainerArcaneWorkbench.CRYSTAL_SLOT, (int)Math.ceil(((double)chargeDepleted)/Wizardry.MANA_PER_CRYSTAL)); + wand.setItemDamage(0); + } + } + + // Armour + }else if(wand != null && wand.getItem() instanceof ItemWizardArmour){ + // Applies legendary upgrade + if(upgrade != null && upgrade.getItem() == Wizardry.armourUpgrade){ + if(!wand.hasTagCompound()){ + wand.stackTagCompound = new NBTTagCompound(); + } + if(!wand.stackTagCompound.hasKey("legendary")){ + wand.stackTagCompound.setBoolean("legendary", true); + tileentity.setInventorySlotContents(ContainerArcaneWorkbench.UPGRADE_SLOT, null); + entityplayer.triggerAchievement(Wizardry.legendary); + } + } + // Charges armour by appropriate amount + if(crystals != null){ + int chargeDepleted = wand.getItemDamage(); + if(crystals.stackSize * Wizardry.MANA_PER_CRYSTAL < chargeDepleted){ + wand.setItemDamage(chargeDepleted - crystals.stackSize * Wizardry.MANA_PER_CRYSTAL); + tileentity.decrStackSize(ContainerArcaneWorkbench.CRYSTAL_SLOT, crystals.stackSize); + }else if(chargeDepleted != 0){ + tileentity.decrStackSize(ContainerArcaneWorkbench.CRYSTAL_SLOT, (int)Math.ceil(((double)chargeDepleted)/Wizardry.MANA_PER_CRYSTAL)); + wand.setItemDamage(0); + } + } + + // Scrolls + }else if(wand != null && wand.getItem() == Wizardry.blankScroll){ + // Spells can only be bound to scrolls if the player has already cast them (prevents casting of master spells without getting a master wand) + // This restriction does not apply in creative mode + if(spellBooks[0] != null && (entityplayer.capabilities.isCreativeMode || (ExtendedPlayer.get(entityplayer) != null + && ExtendedPlayer.get(entityplayer).hasSpellBeenDiscovered(Spell.get(spellBooks[0].getItemDamage())))) + && crystals != null && crystals.stackSize * Wizardry.MANA_PER_CRYSTAL > Spell.get(spellBooks[0].getItemDamage()).cost){ - public void run() { + tileentity.decrStackSize(ContainerArcaneWorkbench.CRYSTAL_SLOT, (int)Math.ceil(((double)Spell.get(spellBooks[0].getItemDamage()).cost)/Wizardry.MANA_PER_CRYSTAL)); + tileentity.setInventorySlotContents(ContainerArcaneWorkbench.WAND_SLOT, new ItemStack(Wizardry.scroll, 1, spellBooks[0].getItemDamage())); + } + } + + ////////////////////////////////////////////////////////////////////////////////////////////// + + }else if(eventID == 1){ + if(entityplayer.inventory.getCurrentItem() != null && + entityplayer.inventory.getCurrentItem().getItem() instanceof ItemWand){ + + ItemStack wand = entityplayer.inventory.getCurrentItem(); + + if(wand != null) WandHelper.selectNextSpell(wand); + + // This line fixes the bug with continuous spells casting when they shouldn't be + entityplayer.clearItemInUse(); + } + + ////////////////////////////////////////////////////////////////////////////////////////////// + + }else if(eventID == 2){ + if(entityplayer.inventory.getCurrentItem() != null && + entityplayer.inventory.getCurrentItem().getItem() instanceof ItemWand){ + + ItemStack wand = entityplayer.inventory.getCurrentItem(); + + if(wand != null) WandHelper.selectPreviousSpell(wand); - ItemStack wand = player.getHeldItemMainhand(); - - if(wand == null || !(wand.getItem() instanceof ItemWand)){ - wand = player.getHeldItemOffhand(); - } - - switch(message.controlType){ - - case APPLY_BUTTON: - - ((ContainerArcaneWorkbench)player.openContainer).onApplyButtonPressed(player); - break; - - case NEXT_SPELL_KEY: - - if(wand != null && wand.getItem() instanceof ItemWand){ - - WandHelper.selectNextSpell(wand); - // This line fixes the bug with continuous spells casting when they shouldn't be - player.stopActiveHand(); - } - - break; - - case PREVIOUS_SPELL_KEY: - - if(wand != null && wand.getItem() instanceof ItemWand){ - - WandHelper.selectPreviousSpell(wand); - // This line fixes the bug with continuous spells casting when they shouldn't be - player.stopActiveHand(); - } - - break; - } - } - } - ); + // This line fixes the bug with continuous spells casting when they shouldn't be + entityplayer.clearItemInUse(); + } + } } return null; } - public static enum ControlType { - APPLY_BUTTON, - NEXT_SPELL_KEY, - PREVIOUS_SPELL_KEY; - } - - public static class Message implements IMessage { - - private ControlType controlType; + public static class Message implements IMessage + { + /** 0 = Apply button in arcane workbench, 1 = N key, 2 = B key */ + private int eventID; // This constructor is required otherwise you'll get errors (used somewhere in fml through reflection) public Message() {} - public Message(ControlType type){ - this.controlType = type; + public Message(int eventID) + { + this.eventID = eventID; } @Override - public void fromBytes(ByteBuf buf){ + public void fromBytes(ByteBuf buf) + { // The order is important - this.controlType = ControlType.values()[buf.readInt()]; + this.eventID = buf.readInt(); } @Override - public void toBytes(ByteBuf buf){ - buf.writeInt(controlType.ordinal()); + public void toBytes(ByteBuf buf) + { + buf.writeInt(eventID); } } } diff --git a/src/main/java/electroblob/wizardry/packet/PacketGlyphData.java b/src/main/java/electroblob/wizardry/packet/PacketGlyphData.java index 7f7191e5..cb3cc818 100644 --- a/src/main/java/electroblob/wizardry/packet/PacketGlyphData.java +++ b/src/main/java/electroblob/wizardry/packet/PacketGlyphData.java @@ -3,38 +3,31 @@ package electroblob.wizardry.packet; import java.util.ArrayList; import java.util.List; +import cpw.mods.fml.common.network.ByteBufUtils; +import cpw.mods.fml.common.network.simpleimpl.IMessage; +import cpw.mods.fml.common.network.simpleimpl.IMessageHandler; +import cpw.mods.fml.common.network.simpleimpl.MessageContext; import electroblob.wizardry.Wizardry; import electroblob.wizardry.packet.PacketGlyphData.Message; import io.netty.buffer.ByteBuf; -import net.minecraftforge.fml.common.network.ByteBufUtils; -import net.minecraftforge.fml.common.network.simpleimpl.IMessage; -import net.minecraftforge.fml.common.network.simpleimpl.IMessageHandler; -import net.minecraftforge.fml.common.network.simpleimpl.MessageContext; -/** [Server -> Client] This packet is sent each time a player joins a world to synchronise the client-side spell - * glyph names with the server-side ones. */ +/** This packet is sent each time a player joins a world to synchronise the client-side spell glyph names with + * the server-side ones. */ public class PacketGlyphData implements IMessageHandler { - + @Override public IMessage onMessage(Message message, MessageContext ctx) { // Just to make sure that the side is correct if(ctx.side.isClient()){ - // Using a fully qualified name is a good course of action here; we don't really want to clutter the proxy - // methods any more than necessary. - net.minecraft.client.Minecraft.getMinecraft().addScheduledTask(new Runnable(){ - @Override - public void run(){ - Wizardry.proxy.handleGlyphDataPacket(message); - } - }); + Wizardry.proxy.handleGlyphDataPacket(message); } return null; } public static class Message implements IMessage { - + /** The list of randomised spell names. */ public List names; /** The list of randomised spell descriptions. */ diff --git a/src/main/java/electroblob/wizardry/packet/PacketNPCCastSpell.java b/src/main/java/electroblob/wizardry/packet/PacketNPCCastSpell.java deleted file mode 100644 index ea2e049f..00000000 --- a/src/main/java/electroblob/wizardry/packet/PacketNPCCastSpell.java +++ /dev/null @@ -1,80 +0,0 @@ -package electroblob.wizardry.packet; - -import electroblob.wizardry.Wizardry; -import electroblob.wizardry.entity.living.ISpellCaster; -import electroblob.wizardry.packet.PacketNPCCastSpell.Message; -import electroblob.wizardry.util.SpellModifiers; -import io.netty.buffer.ByteBuf; -import net.minecraft.util.EnumHand; -import net.minecraftforge.fml.common.network.simpleimpl.IMessage; -import net.minecraftforge.fml.common.network.simpleimpl.IMessageHandler; -import net.minecraftforge.fml.common.network.simpleimpl.MessageContext; - -/** [Server -> Client] This packet is sent when an {@link ISpellCaster} casts a spell which returns true, and is - * sent to clients so they can spawn the particles. Unlike the player packets, this is for both continuous and - * non-continuous spells. */ -public class PacketNPCCastSpell implements IMessageHandler { - - @Override - public IMessage onMessage(Message message, MessageContext ctx){ - - // Just to make sure that the side is correct - if(ctx.side.isClient()){ - // Using a fully qualified name is a good course of action here; we don't really want to clutter the proxy - // methods any more than necessary. - net.minecraft.client.Minecraft.getMinecraft().addScheduledTask(new Runnable(){ - @Override - public void run(){ - Wizardry.proxy.handleNPCCastSpellPacket(message); - } - }); - } - - return null; - } - - public static class Message implements IMessage { - - /** EntityID of the caster */ - public int casterID; - /** EntityID of the target, or -1 if there is none */ - public int targetID; - /** ID of the spell being cast */ - public int spellID; - /** SpellModifiers for the spell */ - public SpellModifiers modifiers; - /** The hand that is holding the itemstack used to cast the spell. Defaults to MAIN_HAND. */ - public EnumHand hand; - - // This constructor is required otherwise you'll get errors (used somewhere in fml through reflection) - public Message(){} - - public Message(int casterID, int targetID, EnumHand hand, int spellID, SpellModifiers modifiers){ - this.casterID = casterID; - this.targetID = targetID; - this.spellID = spellID; - this.modifiers = modifiers; - this.hand = hand == null ? EnumHand.MAIN_HAND : hand; - } - - @Override - public void fromBytes(ByteBuf buf){ - // The order is important - this.casterID = buf.readInt(); - this.targetID = buf.readInt(); - this.spellID = buf.readInt(); - this.modifiers = new SpellModifiers(); - this.modifiers.read(buf); - this.hand = buf.readBoolean() ? EnumHand.OFF_HAND : EnumHand.MAIN_HAND; - } - - @Override - public void toBytes(ByteBuf buf){ - buf.writeInt(casterID); - buf.writeInt(targetID); - buf.writeInt(spellID); - this.modifiers.write(buf); - buf.writeBoolean(this.hand == EnumHand.OFF_HAND); - } - } -} diff --git a/src/main/java/electroblob/wizardry/packet/PacketParticleSpawn.java b/src/main/java/electroblob/wizardry/packet/PacketParticleSpawn.java new file mode 100644 index 00000000..8edb2e7a --- /dev/null +++ b/src/main/java/electroblob/wizardry/packet/PacketParticleSpawn.java @@ -0,0 +1,157 @@ +package electroblob.wizardry.packet; + +import cpw.mods.fml.common.network.ByteBufUtils; +import cpw.mods.fml.common.network.simpleimpl.IMessage; +import cpw.mods.fml.common.network.simpleimpl.IMessageHandler; +import cpw.mods.fml.common.network.simpleimpl.MessageContext; +import electroblob.wizardry.client.particle.EntityBlizzardFX; +import electroblob.wizardry.client.particle.EntityDarkMagicFX; +import electroblob.wizardry.client.particle.EntityDustFX; +import electroblob.wizardry.client.particle.EntityLeafFX; +import electroblob.wizardry.client.particle.EntityMagicBubbleFX; +import electroblob.wizardry.client.particle.EntityMagicFireFX; +import electroblob.wizardry.client.particle.EntitySnowFX; +import electroblob.wizardry.client.particle.EntitySparkFX; +import electroblob.wizardry.client.particle.EntitySparkleFX; +import electroblob.wizardry.client.particle.EntityTornadoFX; +import electroblob.wizardry.packet.PacketParticleSpawn.Message; +import io.netty.buffer.ByteBuf; +import net.minecraft.client.Minecraft; +import net.minecraft.world.World; + +/** This packet is for spawning particles from any method which is client-inconsistent, such as Item#onItemRightClick. + * It can also be used as a lazy fix when the particles depend on conditions which are not synchronised (though it is + * strictly better to synchronise the conditions themselves, in practice it isn't usually worth it unless there are + * loads of particles) + *

    + * DEPRECATED in favour of an 'event packet' based system. */ +@Deprecated +public class PacketParticleSpawn implements IMessageHandler { + + @Override + public IMessage onMessage(Message message, MessageContext ctx) + { + // Just to make sure that the side is correct + if(ctx.side.isClient()){ + + World world = Minecraft.getMinecraft().theWorld; + + if(message.name == EntityBlizzardFX.NAME){ + Minecraft.getMinecraft().effectRenderer.addEffect(new EntityBlizzardFX(world, message.r, message.g, message.b, message.maxAge, message.x, message.z, message.velY, message.y)); + } + else if(message.name == EntityDarkMagicFX.NAME){ + Minecraft.getMinecraft().effectRenderer.addEffect(new EntityDarkMagicFX(world, message.x, message.y, message.z, message.velX, message.velY, message.velZ, message.r, message.g, message.b)); + } + else if(message.name == EntityDustFX.NAME){ + Minecraft.getMinecraft().effectRenderer.addEffect(new EntityDustFX(world, message.x, message.y, message.z, message.velX, message.velY, message.velZ, message.r, message.g, message.b, false)); + } + else if(message.name == EntityLeafFX.NAME){ + Minecraft.getMinecraft().effectRenderer.addEffect(new EntityLeafFX(world, message.x, message.y, message.z, message.velX, message.velY, message.velZ, message.maxAge)); + } + else if(message.name == EntityMagicBubbleFX.NAME){ + Minecraft.getMinecraft().effectRenderer.addEffect(new EntityMagicBubbleFX(world, message.x, message.y, message.z, message.velX, message.velY, message.velZ)); + } + else if(message.name == EntityMagicFireFX.NAME){ + Minecraft.getMinecraft().effectRenderer.addEffect(new EntityMagicFireFX(world, message.x, message.y, message.z, message.velX, message.velY, message.velZ, message.maxAge, 1)); + } + else if(message.name == EntitySnowFX.NAME){ + Minecraft.getMinecraft().effectRenderer.addEffect(new EntitySnowFX(world, message.x, message.y, message.z, message.velX, message.velY, message.velZ, message.maxAge)); + } + else if(message.name == EntitySparkFX.NAME){ + Minecraft.getMinecraft().effectRenderer.addEffect(new EntitySparkFX(world, message.x, message.y, message.z, message.velX, message.velY, message.velZ)); + } + else if(message.name == EntitySparkleFX.NAME){ + Minecraft.getMinecraft().effectRenderer.addEffect(new EntitySparkleFX(world, message.x, message.y, message.z, message.velX, message.velY, message.velZ, message.r, message.g, message.b, message.maxAge, false)); + } + else if(message.name == EntitySparkleFX.NAME_GRAVITY){ + Minecraft.getMinecraft().effectRenderer.addEffect(new EntitySparkleFX(world, message.x, message.y, message.z, message.velX, message.velY, message.velZ, message.r, message.g, message.b, message.maxAge, true)); + } + else{ + world.spawnParticle(message.name, message.x, message.y, message.z, message.velX, message.velY, message.velZ); + } + } + + return null; + } + + public static class Message implements IMessage + { + // Some of these fields have multiple uses depending on the particle type. This is to save on packet + // size as much as possible. + + /** Particle name. Either a vanilla name (see RenderGlobal) or one of the custom particles' NAME field. */ + private String name; + + /** Usually the x position of the particle, but a few particles use it differently. */ + private double x; + /** Usually the y position of the particle, but a few particles use it differently. */ + private double y; + /** Usually the z position of the particle, but a few particles use it differently. */ + private double z; + + /** Usually the x velocity of the particle, but a few particles use it differently. */ + private double velX; + /** Usually the y velocity of the particle, but a few particles use it differently - + * for example, tornado and blizzard fx use this as the radius. */ + private double velY; + /** Usually the z velocity of the particle, but a few particles use it differently. */ + private double velZ; + + private int maxAge; + + private float r; + private float g; + private float b; + + // This constructor is required otherwise you'll get errors (used somewhere in fml through reflection) + public Message() {} + + public Message(String name, double x, double y, double z, double velX, double velY, double velZ, int maxAge, float r, float g, float b) + { + this.name = name; + this.x = x; + this.y = y; + this.z = z; + this.velX = velX; + this.velY = velY; + this.velZ = velZ; + this.maxAge = maxAge; + this.r = r; + this.g = g; + this.b = b; + } + + @Override + public void fromBytes(ByteBuf buf) + { + // The order is important + this.name = ByteBufUtils.readUTF8String(buf); + this.x = buf.readDouble(); + this.y = buf.readDouble(); + this.z = buf.readDouble(); + this.velX = buf.readDouble(); + this.velY = buf.readDouble(); + this.velZ = buf.readDouble(); + this.maxAge = buf.readInt(); + this.r = buf.readFloat(); + this.g = buf.readFloat(); + this.b = buf.readFloat(); + } + + @Override + public void toBytes(ByteBuf buf) + { + ByteBufUtils.writeUTF8String(buf, name); + buf.writeDouble(x); + buf.writeDouble(y); + buf.writeDouble(z); + buf.writeDouble(velX); + buf.writeDouble(velY); + buf.writeDouble(velZ); + buf.writeInt(maxAge); + buf.writeFloat(r); + buf.writeFloat(g); + buf.writeFloat(b); + } + } +} diff --git a/src/main/java/electroblob/wizardry/packet/PacketPlayerSync.java b/src/main/java/electroblob/wizardry/packet/PacketPlayerSync.java index d58dfbe4..154c88a0 100644 --- a/src/main/java/electroblob/wizardry/packet/PacketPlayerSync.java +++ b/src/main/java/electroblob/wizardry/packet/PacketPlayerSync.java @@ -1,49 +1,39 @@ package electroblob.wizardry.packet; import java.util.HashSet; -import java.util.Set; +import cpw.mods.fml.common.network.simpleimpl.IMessage; +import cpw.mods.fml.common.network.simpleimpl.IMessageHandler; +import cpw.mods.fml.common.network.simpleimpl.MessageContext; import electroblob.wizardry.Wizardry; import electroblob.wizardry.packet.PacketPlayerSync.Message; import electroblob.wizardry.spell.Spell; import io.netty.buffer.ByteBuf; -import net.minecraftforge.fml.common.network.simpleimpl.IMessage; -import net.minecraftforge.fml.common.network.simpleimpl.IMessageHandler; -import net.minecraftforge.fml.common.network.simpleimpl.MessageContext; -/** [Server -> Client] This packet is sent to synchronise any fields that need synchronising in - * {@link electroblob.wizardry.WizardData WizardData}. This packet is not sent often enough and is too small to warrant - * having separate packets for each field that needs synchronising. */ +/** This packet is sent to synchronise extended player data. */ public class PacketPlayerSync implements IMessageHandler { - + @Override public IMessage onMessage(Message message, MessageContext ctx) { // Just to make sure that the side is correct if(ctx.side.isClient()){ - // Using a fully qualified name is a good course of action here; we don't really want to clutter the proxy - // methods any more than necessary. - net.minecraft.client.Minecraft.getMinecraft().addScheduledTask(new Runnable(){ - @Override - public void run(){ - Wizardry.proxy.handlePlayerSyncPacket(message); - } - }); + Wizardry.proxy.handlePlayerSyncPacket(message); } return null; } public static class Message implements IMessage { - - public Set spellsDiscovered; + + public HashSet spellsDiscovered; public int selectedMinionID; // This constructor is required otherwise you'll get errors (used somewhere in fml through reflection) public Message() {} - public Message(Set spellsDiscovered2, int selectedMinionID){ - this.spellsDiscovered = spellsDiscovered2; + public Message(HashSet spellsDiscovered, int selectedMinionID){ + this.spellsDiscovered = spellsDiscovered; this.selectedMinionID = selectedMinionID; } @@ -58,11 +48,11 @@ public class PacketPlayerSync implements IMessageHandler { @Override public void toBytes(ByteBuf buf){ - + buf.writeInt(selectedMinionID); - + if(this.spellsDiscovered == null) return; - + for(Spell spell : this.spellsDiscovered){ buf.writeInt(spell.id()); } diff --git a/src/main/java/electroblob/wizardry/packet/PacketSyncSettings.java b/src/main/java/electroblob/wizardry/packet/PacketSyncSettings.java deleted file mode 100644 index bed891c6..00000000 --- a/src/main/java/electroblob/wizardry/packet/PacketSyncSettings.java +++ /dev/null @@ -1,90 +0,0 @@ -package electroblob.wizardry.packet; - -import electroblob.wizardry.Settings; -import electroblob.wizardry.Wizardry; -import electroblob.wizardry.packet.PacketSyncSettings.Message; -import io.netty.buffer.ByteBuf; -import net.minecraftforge.fml.common.network.simpleimpl.IMessage; -import net.minecraftforge.fml.common.network.simpleimpl.IMessageHandler; -import net.minecraftforge.fml.common.network.simpleimpl.MessageContext; - -/** [Server -> Client] This packet is sent to synchronise the config settings with clients on player login. - * @see Settings */ -public class PacketSyncSettings implements IMessageHandler { - - @Override - public IMessage onMessage(Message message, MessageContext ctx){ - - if(ctx.side.isClient()){ - // Using a fully qualified name is a good course of action here; we don't really want to clutter the proxy - // any more than necessary. - net.minecraft.client.Minecraft.getMinecraft().addScheduledTask(new Runnable(){ - @Override - public void run(){ - copySettings(message); - } - }); - - } - - return null; - } - - private static void copySettings(Message message){ - Wizardry.settings.firebombIsCraftable = message.settings.firebombIsCraftable; - Wizardry.settings.poisonBombIsCraftable = message.settings.poisonBombIsCraftable; - Wizardry.settings.smokeBombIsCraftable = message.settings.smokeBombIsCraftable; - Wizardry.settings.useAlternateScrollRecipe = message.settings.useAlternateScrollRecipe; - Wizardry.settings.discoveryMode = message.settings.discoveryMode; -// Wizardry.settings.maxSpellCommandMultiplier = message.settings.maxSpellCommandMultiplier; -// Wizardry.settings.castCommandName = message.settings.castCommandName; -// Wizardry.settings.discoverspellCommandName = message.settings.discoverspellCommandName; -// Wizardry.settings.allyCommandName = message.settings.allyCommandName; -// Wizardry.settings.alliesCommandName = message.settings.alliesCommandName; - } - - public static class Message implements IMessage { - - /** EntityID of the caster */ - public Settings settings; - - // This constructor is required otherwise you'll get errors (used somewhere in fml through reflection) - public Message(){} - - public Message(Settings settings){ - this.settings = settings; - } - - @Override - public void fromBytes(ByteBuf buf){ - // I'm guessing the settings field will be null here, so it needs initialising. - // This is also a great reason to have the settings as an actual object. - settings = new Settings(); - // The order is important - settings.firebombIsCraftable = buf.readBoolean(); - settings.poisonBombIsCraftable = buf.readBoolean(); - settings.smokeBombIsCraftable = buf.readBoolean(); - settings.useAlternateScrollRecipe = buf.readBoolean(); - settings.discoveryMode = buf.readBoolean(); -// settings.maxSpellCommandMultiplier = buf.readDouble(); -// settings.castCommandName = ByteBufUtils.readUTF8String(buf); -// settings.discoverspellCommandName = ByteBufUtils.readUTF8String(buf); -// settings.allyCommandName = ByteBufUtils.readUTF8String(buf); -// settings.alliesCommandName = ByteBufUtils.readUTF8String(buf); - } - - @Override - public void toBytes(ByteBuf buf){ - buf.writeBoolean(settings.firebombIsCraftable); - buf.writeBoolean(settings.poisonBombIsCraftable); - buf.writeBoolean(settings.smokeBombIsCraftable); - buf.writeBoolean(settings.useAlternateScrollRecipe); - buf.writeBoolean(settings.discoveryMode); -// buf.writeDouble(settings.maxSpellCommandMultiplier); -// ByteBufUtils.writeUTF8String(buf, settings.castCommandName); -// ByteBufUtils.writeUTF8String(buf, settings.discoverspellCommandName); -// ByteBufUtils.writeUTF8String(buf, settings.allyCommandName); -// ByteBufUtils.writeUTF8String(buf, settings.alliesCommandName); - } - } -} diff --git a/src/main/java/electroblob/wizardry/packet/PacketTransportation.java b/src/main/java/electroblob/wizardry/packet/PacketTransportation.java index 7a7085c8..e440c524 100644 --- a/src/main/java/electroblob/wizardry/packet/PacketTransportation.java +++ b/src/main/java/electroblob/wizardry/packet/PacketTransportation.java @@ -1,29 +1,29 @@ package electroblob.wizardry.packet; +import cpw.mods.fml.common.network.simpleimpl.IMessage; +import cpw.mods.fml.common.network.simpleimpl.IMessageHandler; +import cpw.mods.fml.common.network.simpleimpl.MessageContext; import electroblob.wizardry.Wizardry; import electroblob.wizardry.packet.PacketTransportation.Message; +import electroblob.wizardry.spell.Spell; import io.netty.buffer.ByteBuf; -import net.minecraftforge.fml.common.network.simpleimpl.IMessage; -import net.minecraftforge.fml.common.network.simpleimpl.IMessageHandler; -import net.minecraftforge.fml.common.network.simpleimpl.MessageContext; +import net.minecraft.entity.Entity; +import net.minecraft.entity.EntityLiving; +import net.minecraft.entity.EntityLivingBase; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.item.ItemStack; +import net.minecraft.item.Item; +import net.minecraft.world.World; -/** [Server -> Client] This packet is sent when a player is teleported due to the transportation spell to spawn - * the particles. */ +/** This packet is sent when a player is teleported due to the transportation spell to spawn the particles. */ public class PacketTransportation implements IMessageHandler { - + @Override public IMessage onMessage(Message message, MessageContext ctx) { // Just to make sure that the side is correct if(ctx.side.isClient()){ - // Using a fully qualified name is a good course of action here; we don't really want to clutter the proxy - // methods any more than necessary. - net.minecraft.client.Minecraft.getMinecraft().addScheduledTask(new Runnable(){ - @Override - public void run(){ - Wizardry.proxy.handleTransportationPacket(message); - } - }); + Wizardry.proxy.handleTransportationPacket(message); } return null; diff --git a/src/main/java/electroblob/wizardry/packet/WizardryPacketHandler.java b/src/main/java/electroblob/wizardry/packet/WizardryPacketHandler.java index dedaead6..29bab1a5 100644 --- a/src/main/java/electroblob/wizardry/packet/WizardryPacketHandler.java +++ b/src/main/java/electroblob/wizardry/packet/WizardryPacketHandler.java @@ -1,11 +1,9 @@ package electroblob.wizardry.packet; +import cpw.mods.fml.common.network.NetworkRegistry; +import cpw.mods.fml.common.network.simpleimpl.SimpleNetworkWrapper; +import cpw.mods.fml.relauncher.Side; import electroblob.wizardry.Wizardry; -import net.minecraftforge.fml.common.network.NetworkRegistry; -import net.minecraftforge.fml.common.network.simpleimpl.IMessage; -import net.minecraftforge.fml.common.network.simpleimpl.IMessageHandler; -import net.minecraftforge.fml.common.network.simpleimpl.SimpleNetworkWrapper; -import net.minecraftforge.fml.relauncher.Side; public class WizardryPacketHandler { @@ -15,19 +13,18 @@ public class WizardryPacketHandler { { net = NetworkRegistry.INSTANCE.newSimpleChannel(Wizardry.MODID.toUpperCase()); registerMessage(PacketControlInput.class, PacketControlInput.Message.class); + //registerMessage(PacketParticleSpawn.class, PacketParticleSpawn.Message.class); registerMessage(PacketCastSpell.class, PacketCastSpell.Message.class); registerMessage(PacketTransportation.class, PacketTransportation.Message.class); registerMessage(PacketPlayerSync.class, PacketPlayerSync.Message.class); registerMessage(PacketGlyphData.class, PacketGlyphData.Message.class); registerMessage(PacketCastContinuousSpell.class, PacketCastContinuousSpell.Message.class); registerMessage(PacketClairvoyance.class, PacketClairvoyance.Message.class); - registerMessage(PacketSyncSettings.class, PacketSyncSettings.Message.class); - registerMessage(PacketNPCCastSpell.class, PacketNPCCastSpell.Message.class); } private static int nextPacketId = 0; - private static void registerMessage(Class> packet, Class message) + private static void registerMessage(Class packet, Class message) { net.registerMessage(packet, message, nextPacketId, Side.CLIENT); net.registerMessage(packet, message, nextPacketId, Side.SERVER); diff --git a/src/main/java/electroblob/wizardry/potion/ICustomPotionParticles.java b/src/main/java/electroblob/wizardry/potion/ICustomPotionParticles.java deleted file mode 100644 index dcb19997..00000000 --- a/src/main/java/electroblob/wizardry/potion/ICustomPotionParticles.java +++ /dev/null @@ -1,45 +0,0 @@ -package electroblob.wizardry.potion; - -import net.minecraft.potion.PotionEffect; -import net.minecraft.world.World; -import net.minecraftforge.event.entity.living.LivingEvent.LivingUpdateEvent; -import net.minecraftforge.fml.common.Mod; -import net.minecraftforge.fml.common.eventhandler.SubscribeEvent; - -/** - * Interface for potion effects that spawn custom particles instead of (or as well as) the vanilla 'swirly' particles. - * @author Electroblob - * @since Wizardry 1.2 - */ -// TODO: Backport. -@Mod.EventBusSubscriber -public interface ICustomPotionParticles { - - /** - * Called from the event handler to spawn a single custom potion particle. To get an instance of - * Random inside this method, use world.rand. - * @param world The world to spawn the particle in. - * @param x The x coordinate of the particle, already set to a random value within the entity's bounding box. - * @param y The y coordinate of the particle, already set to a random value within the entity's bounding box. - * @param z The z coordinate of the particle, already set to a random value within the entity's bounding box. - */ - void spawnCustomParticle(World world, double x, double y, double z); - - @SubscribeEvent - public static void onLivingUpdateEvent(LivingUpdateEvent event){ - if(event.getEntityLiving().worldObj.isRemote){ - // Behold the power of interfaces! - for(PotionEffect effect : event.getEntityLiving().getActivePotionEffects()){ - - if(effect.getPotion() instanceof ICustomPotionParticles && effect.doesShowParticles()){ - - double x = event.getEntityLiving().posX + (event.getEntityLiving().worldObj.rand.nextDouble() - 0.5)*event.getEntityLiving().width; - double y = event.getEntityLiving().getEntityBoundingBox().minY + event.getEntityLiving().worldObj.rand.nextDouble()*event.getEntityLiving().height; - double z = event.getEntityLiving().posZ + (event.getEntityLiving().worldObj.rand.nextDouble() - 0.5)*event.getEntityLiving().width; - - ((ICustomPotionParticles)effect.getPotion()).spawnCustomParticle(event.getEntityLiving().worldObj, x, y, z); - } - } - } - } -} diff --git a/src/main/java/electroblob/wizardry/potion/PotionDecay.java b/src/main/java/electroblob/wizardry/potion/PotionDecay.java index 6e767866..e8746fee 100644 --- a/src/main/java/electroblob/wizardry/potion/PotionDecay.java +++ b/src/main/java/electroblob/wizardry/potion/PotionDecay.java @@ -1,30 +1,21 @@ package electroblob.wizardry.potion; -import java.util.List; - import electroblob.wizardry.Wizardry; -import electroblob.wizardry.constants.Constants; -import electroblob.wizardry.entity.construct.EntityDecay; -import electroblob.wizardry.util.WizardryUtilities; -import net.minecraft.entity.Entity; import net.minecraft.entity.EntityLivingBase; import net.minecraft.entity.SharedMonsterAttributes; import net.minecraft.potion.Potion; import net.minecraft.potion.PotionEffect; import net.minecraft.util.DamageSource; import net.minecraft.util.ResourceLocation; -import net.minecraftforge.fml.relauncher.Side; -import net.minecraftforge.fml.relauncher.SideOnly; public class PotionDecay extends Potion { - private static final ResourceLocation ICON = new ResourceLocation(Wizardry.MODID, "textures/gui/decay_icon.png"); + private static final ResourceLocation potionIcon = new ResourceLocation(Wizardry.MODID, "textures/gui/decay_icon.png"); - public PotionDecay(boolean isBadEffect, int liquidColour) { - super(isBadEffect, liquidColour); - // This needs to be here because registerPotionAttributeModifier doesn't like it if the potion has no name yet. - this.setPotionName("potion.wizardry:decay"); - this.registerPotionAttributeModifier(SharedMonsterAttributes.MOVEMENT_SPEED, "85602e0b-4801-4a87-94f3-bf617c97014e", -Constants.DECAY_SLOWNESS_PER_LEVEL, 2); + public PotionDecay(int id, boolean isBadEffect, int liquidColour) { + super(id, isBadEffect, liquidColour); + this.setPotionName("potion.decay"); + this.func_111184_a(SharedMonsterAttributes.movementSpeed, "85602e0b-4801-4a87-94f3-bf617c97014e", -Wizardry.DECAY_SLOWNESS_PER_LEVEL, 2); } @Override @@ -36,39 +27,40 @@ public class PotionDecay extends Potion { } @Override - public void performEffect(EntityLivingBase target, int strength) { - - target.attackEntityFrom(DamageSource.wither, 1); - - if(target.onGround && target.ticksExisted % Constants.DECAY_SPREAD_INTERVAL == 0){ - - List entities = target.worldObj.getEntitiesWithinAABBExcludingEntity(target, target.getEntityBoundingBox()); - - boolean flag = true; - - for(Entity entity : entities){ - if(entity instanceof EntityDecay) flag = false; - } - - if(flag){ - // The victim spreading the decay is the 'caster' here, so that it can actually wear off, otherwise it just gets infected with its own decay and the effect lasts forever. - target.worldObj.spawnEntityInWorld(new EntityDecay(target.worldObj, target.posX, target.posY, target.posZ, target)); - } - } + public void performEffect(EntityLivingBase entitylivingbase, int strength) { + entitylivingbase.attackEntityFrom(DamageSource.wither, 1); } @Override - @SideOnly(Side.CLIENT) public void renderInventoryEffect(int x, int y, PotionEffect effect, net.minecraft.client.Minecraft mc) { - mc.renderEngine.bindTexture(ICON); - WizardryUtilities.drawTexturedRect(x + 6, y + 7, 0, 0, 18, 18, 18, 18); + + mc.renderEngine.bindTexture(potionIcon); + this.drawTexturedRect(x + 6, y + 7, 0, 0, 18, 18, 18, 18); + } - @Override - @SideOnly(Side.CLIENT) - public void renderHUDEffect(int x, int y, PotionEffect effect, net.minecraft.client.Minecraft mc, float alpha) { - mc.renderEngine.bindTexture(ICON); - WizardryUtilities.drawTexturedRect(x + 3, y + 3, 0, 0, 18, 18, 18, 18); + /** + * Draws a textured rectangle, taking the size of the image and the bit needed into account. Client side only, of course. + * @param x The x position of the rectangle + * @param y The y position of the rectangle + * @param u The x position of the top left corner of the section of the image wanted + * @param v The y position of the top left corner of the section of the image wanted + * @param width The width of the section + * @param height The height of the section + * @param textureWidth The width of the actual image. + * @param textureHeight The height of the actual image. + */ + public static void drawTexturedRect(int x, int y, int u, int v, int width, int height, int textureWidth, int textureHeight) + { + float f = 1F / (float)textureWidth; + float f1 = 1F / (float)textureHeight; + net.minecraft.client.renderer.Tessellator tessellator = net.minecraft.client.renderer.Tessellator.instance; + tessellator.startDrawingQuads(); + tessellator.addVertexWithUV((double)(x), (double)(y + height), 0, (double)((float)(u) * f), (double)((float)(v + height) * f1)); + tessellator.addVertexWithUV((double)(x + width), (double)(y + height), 0, (double)((float)(u + width) * f), (double)((float)(v + height) * f1)); + tessellator.addVertexWithUV((double)(x + width), (double)(y), 0, (double)((float)(u + width) * f), (double)((float)(v) * f1)); + tessellator.addVertexWithUV((double)(x), (double)(y), 0, (double)((float)(u) * f), (double)((float)(v) * f1)); + tessellator.draw(); } } diff --git a/src/main/java/electroblob/wizardry/potion/PotionFrost.java b/src/main/java/electroblob/wizardry/potion/PotionFrost.java index 152adfab..9953f4c6 100644 --- a/src/main/java/electroblob/wizardry/potion/PotionFrost.java +++ b/src/main/java/electroblob/wizardry/potion/PotionFrost.java @@ -1,66 +1,59 @@ package electroblob.wizardry.potion; import electroblob.wizardry.Wizardry; -import electroblob.wizardry.constants.Constants; -import electroblob.wizardry.registry.WizardryPotions; -import electroblob.wizardry.util.WizardryParticleType; -import electroblob.wizardry.util.WizardryUtilities; import net.minecraft.entity.EntityLivingBase; import net.minecraft.entity.SharedMonsterAttributes; import net.minecraft.potion.Potion; import net.minecraft.potion.PotionEffect; import net.minecraft.util.ResourceLocation; -import net.minecraft.world.World; -import net.minecraftforge.event.entity.player.PlayerEvent.BreakSpeed; -import net.minecraftforge.fml.common.Mod; -import net.minecraftforge.fml.common.eventhandler.SubscribeEvent; -import net.minecraftforge.fml.relauncher.Side; -import net.minecraftforge.fml.relauncher.SideOnly; -@Mod.EventBusSubscriber -public class PotionFrost extends Potion implements ICustomPotionParticles { +public class PotionFrost extends Potion { - private static final ResourceLocation ICON = new ResourceLocation(Wizardry.MODID, "textures/gui/frost_icon.png"); + private static final ResourceLocation potionIcon = new ResourceLocation(Wizardry.MODID, "textures/gui/frost_icon.png"); - public PotionFrost(boolean isBadEffect, int liquidColour) { - super(isBadEffect, liquidColour); - // This needs to be here because registerPotionAttributeModifier doesn't like it if the potion has no name yet. - this.setPotionName("potion.wizardry:frost"); + public PotionFrost(int id, boolean isBadEffect, int liquidColour) { + super(id, isBadEffect, liquidColour); + this.setPotionName("potion.frost"); // With -0.5 as the 'amount', frost 1 slows the entity down by a half and frost 2 roots it to the spot - this.registerPotionAttributeModifier(SharedMonsterAttributes.MOVEMENT_SPEED, "35dded48-2f19-4541-8510-b29e2dc2cd51", -Constants.FROST_SLOWNESS_PER_LEVEL, 2); - // More UUIDs: 85602e0b-4801-4a87-94f3-bf617c97014e + this.func_111184_a(SharedMonsterAttributes.movementSpeed, "35dded48-2f19-4541-8510-b29e2dc2cd51", -Wizardry.FROST_SLOWNESS_PER_LEVEL, 2); + //More UUIDs: 85602e0b-4801-4a87-94f3-bf617c97014e } @Override public void performEffect(EntityLivingBase entitylivingbase, int strength) { // Nothing here because this potion works on attribute modifiers. } - - @Override - public void spawnCustomParticle(World world, double x, double y, double z) { - Wizardry.proxy.spawnParticle(WizardryParticleType.SNOW, world, x, y, z, 0, -0.02, 0, 15 + world.rand.nextInt(5)); - } @Override - @SideOnly(Side.CLIENT) public void renderInventoryEffect(int x, int y, PotionEffect effect, net.minecraft.client.Minecraft mc) { - mc.renderEngine.bindTexture(ICON); - WizardryUtilities.drawTexturedRect(x + 6, y + 7, 0, 0, 18, 18, 18, 18); + + mc.renderEngine.bindTexture(potionIcon); + this.drawTexturedRect(x + 6, y + 7, 0, 0, 18, 18, 18, 18); + } - @Override - @SideOnly(Side.CLIENT) - public void renderHUDEffect(int x, int y, PotionEffect effect, net.minecraft.client.Minecraft mc, float alpha) { - mc.renderEngine.bindTexture(ICON); - WizardryUtilities.drawTexturedRect(x + 3, y + 3, 0, 0, 18, 18, 18, 18); - } - - @SubscribeEvent - public static void onBreakSpeedEvent(BreakSpeed event){ - if(event.getEntityPlayer().isPotionActive(WizardryPotions.frost)){ - // Amplifier + 1 because it starts at 0 - event.setNewSpeed(event.getOriginalSpeed() * (1 - Constants.FROST_FATIGUE_PER_LEVEL*(event.getEntityPlayer().getActivePotionEffect(WizardryPotions.frost).getAmplifier() + 1))); - } + /** + * Draws a textured rectangle, taking the size of the image and the bit needed into account. Client side only, of course. + * @param x The x position of the rectangle + * @param y The y position of the rectangle + * @param u The x position of the top left corner of the section of the image wanted + * @param v The y position of the top left corner of the section of the image wanted + * @param width The width of the section + * @param height The height of the section + * @param textureWidth The width of the actual image. + * @param textureHeight The height of the actual image. + */ + public static void drawTexturedRect(int x, int y, int u, int v, int width, int height, int textureWidth, int textureHeight) + { + float f = 1F / (float)textureWidth; + float f1 = 1F / (float)textureHeight; + net.minecraft.client.renderer.Tessellator tessellator = net.minecraft.client.renderer.Tessellator.instance; + tessellator.startDrawingQuads(); + tessellator.addVertexWithUV((double)(x), (double)(y + height), 0, (double)((float)(u) * f), (double)((float)(v + height) * f1)); + tessellator.addVertexWithUV((double)(x + width), (double)(y + height), 0, (double)((float)(u + width) * f), (double)((float)(v + height) * f1)); + tessellator.addVertexWithUV((double)(x + width), (double)(y), 0, (double)((float)(u + width) * f), (double)((float)(v) * f1)); + tessellator.addVertexWithUV((double)(x), (double)(y), 0, (double)((float)(u) * f), (double)((float)(v) * f1)); + tessellator.draw(); } } diff --git a/src/main/java/electroblob/wizardry/potion/PotionMagicEffect.java b/src/main/java/electroblob/wizardry/potion/PotionMagicEffect.java index bba9a646..f31df1b9 100644 --- a/src/main/java/electroblob/wizardry/potion/PotionMagicEffect.java +++ b/src/main/java/electroblob/wizardry/potion/PotionMagicEffect.java @@ -1,22 +1,21 @@ package electroblob.wizardry.potion; +import org.lwjgl.opengl.GL11; + import electroblob.wizardry.Wizardry; -import electroblob.wizardry.util.WizardryUtilities; import net.minecraft.entity.EntityLivingBase; +import net.minecraft.entity.SharedMonsterAttributes; import net.minecraft.potion.Potion; import net.minecraft.potion.PotionEffect; import net.minecraft.util.ResourceLocation; -import net.minecraftforge.fml.relauncher.Side; -import net.minecraftforge.fml.relauncher.SideOnly; -/** Class for all potions that work on events only. */ public class PotionMagicEffect extends Potion { - private static final ResourceLocation ICONS = new ResourceLocation(Wizardry.MODID, "textures/gui/potion_icons.png"); + private static final ResourceLocation potionIcons = new ResourceLocation(Wizardry.MODID, "textures/gui/potion_icons.png"); private final int textureIndex; - public PotionMagicEffect(boolean isBadEffect, int liquidColour, int textureIndex) { - super(isBadEffect, liquidColour); + public PotionMagicEffect(int id, boolean isBadEffect, int liquidColour, int textureIndex) { + super(id, isBadEffect, liquidColour); this.textureIndex = textureIndex; } @@ -26,17 +25,42 @@ public class PotionMagicEffect extends Potion { } @Override - @SideOnly(Side.CLIENT) - public void renderInventoryEffect(int x, int y, PotionEffect effect, net.minecraft.client.Minecraft mc){ - mc.renderEngine.bindTexture(ICONS); - WizardryUtilities.drawTexturedRect(x + 6, y + 7, 18*(textureIndex%4), 18*(textureIndex/4), 18, 18, 72, 72); + public void renderInventoryEffect(int x, int y, PotionEffect effect, net.minecraft.client.Minecraft mc) { + + GL11.glPushMatrix(); + GL11.glEnable(GL11.GL_BLEND); + GL11.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA); + + mc.renderEngine.bindTexture(potionIcons); + this.drawTexturedRect(x + 6, y + 7, 18*(textureIndex%4), 18*(textureIndex/4), 18, 18, 72, 72); + + GL11.glDisable(GL11.GL_BLEND); + GL11.glPopMatrix(); + } - @Override - @SideOnly(Side.CLIENT) - public void renderHUDEffect(int x, int y, PotionEffect effect, net.minecraft.client.Minecraft mc, float alpha) { - mc.renderEngine.bindTexture(ICONS); - WizardryUtilities.drawTexturedRect(x + 3, y + 3, 18*(textureIndex%4), 18*(textureIndex/4), 18, 18, 72, 72); + /** + * Draws a textured rectangle, taking the size of the image and the bit needed into account. Client side only, of course. + * @param x The x position of the rectangle + * @param y The y position of the rectangle + * @param u The x position of the top left corner of the section of the image wanted + * @param v The y position of the top left corner of the section of the image wanted + * @param width The width of the section + * @param height The height of the section + * @param textureWidth The width of the actual image. + * @param textureHeight The height of the actual image. + */ + public static void drawTexturedRect(int x, int y, int u, int v, int width, int height, int textureWidth, int textureHeight) + { + float f = 1F / (float)textureWidth; + float f1 = 1F / (float)textureHeight; + net.minecraft.client.renderer.Tessellator tessellator = net.minecraft.client.renderer.Tessellator.instance; + tessellator.startDrawingQuads(); + tessellator.addVertexWithUV((double)(x), (double)(y + height), 0, (double)((float)(u) * f), (double)((float)(v + height) * f1)); + tessellator.addVertexWithUV((double)(x + width), (double)(y + height), 0, (double)((float)(u + width) * f), (double)((float)(v + height) * f1)); + tessellator.addVertexWithUV((double)(x + width), (double)(y), 0, (double)((float)(u + width) * f), (double)((float)(v) * f1)); + tessellator.addVertexWithUV((double)(x), (double)(y), 0, (double)((float)(u) * f), (double)((float)(v) * f1)); + tessellator.draw(); } } diff --git a/src/main/java/electroblob/wizardry/potion/PotionMagicEffectParticles.java b/src/main/java/electroblob/wizardry/potion/PotionMagicEffectParticles.java deleted file mode 100644 index b9eac023..00000000 --- a/src/main/java/electroblob/wizardry/potion/PotionMagicEffectParticles.java +++ /dev/null @@ -1,11 +0,0 @@ -package electroblob.wizardry.potion; - -/** Same as {@link PotionMagicEffect}, but also implements {@link ICustomPotionParticles} to allow anonymous classes - * to extend it and add their own particles. */ -public abstract class PotionMagicEffectParticles extends PotionMagicEffect implements ICustomPotionParticles { - - public PotionMagicEffectParticles(boolean isBadEffect, int liquidColour, int textureIndex){ - super(isBadEffect, liquidColour, textureIndex); - } - -} diff --git a/src/main/java/electroblob/wizardry/registry/Spells.java b/src/main/java/electroblob/wizardry/registry/Spells.java deleted file mode 100644 index a59e7538..00000000 --- a/src/main/java/electroblob/wizardry/registry/Spells.java +++ /dev/null @@ -1,352 +0,0 @@ -package electroblob.wizardry.registry; - -import electroblob.wizardry.Wizardry; -import electroblob.wizardry.spell.*; -import net.minecraft.util.ResourceLocation; -import net.minecraftforge.event.RegistryEvent; -import net.minecraftforge.fml.common.Mod; -import net.minecraftforge.fml.common.eventhandler.SubscribeEvent; -import net.minecraftforge.fml.common.registry.GameRegistry.ObjectHolder; -import net.minecraftforge.fml.common.registry.IForgeRegistry; -import net.minecraftforge.fml.common.registry.RegistryBuilder; - -/** - * Class responsible for defining, storing and registering all of wizardry's spells. - * @author Electroblob - * @since Wizardry 2.1 - */ -// In case anyone was wondering, the reason @ObjectHolder is useful within one mod is that it allows you to initialise -// stuff during the registry events (or whenever), whilst still having a final field (which is important, not only -// because it makes the text go bold, but also because it stops anyone fiddling with your fields). "Why would I want to -// initialise things within the registry events?", I hear you ask - well, for one, custom registries don't like it if -// you haven't created the registry before you start calling constructors of classes extending IForgeRegistryEntry.Impl, -// and secondly, -@ObjectHolder(Wizardry.MODID) -@Mod.EventBusSubscriber -public final class Spells { - - // This is here because this class is already an event handler. - @SubscribeEvent - public static void createRegistry(RegistryEvent.NewRegistry event){ - - // Beats me why we need both of these. Surely the type parameter means it already knows? - RegistryBuilder builder = new RegistryBuilder(); - builder.setType(Spell.class); - builder.setName(new ResourceLocation(Wizardry.MODID, "spells")); - builder.setIDRange(0, 5000); // Is there any penalty for using a larger number? - - Spell.registry = builder.create(); - } - - // Wizardry 1.0 spells - - public static final Spell none = null; - public static final Spell magic_missile = null; - public static final Spell ignite = null; - public static final Spell freeze = null; - public static final Spell snowball = null; - public static final Spell arc = null; - public static final Spell thunderbolt = null; - public static final Spell summon_zombie = null; - public static final Spell snare = null; - public static final Spell dart = null; - public static final Spell light = null; - public static final Spell telekinesis = null; - public static final Spell heal = null; - - public static final Spell fireball = null; - public static final Spell flame_ray = null; - public static final Spell firebomb = null; - public static final Spell fire_sigil = null; - public static final Spell firebolt = null; - public static final Spell frost_ray = null; - public static final Spell summon_snow_golem = null; - public static final Spell ice_shard = null; - public static final Spell ice_statue = null; - public static final Spell frost_sigil = null; - public static final Spell lightning_ray = null; - public static final Spell spark_bomb = null; - public static final Spell homing_spark = null; - public static final Spell lightning_sigil = null; - public static final Spell lightning_arrow = null; - public static final Spell life_drain = null; - public static final Spell summon_skeleton = null; - public static final Spell metamorphosis = null; - public static final Spell wither = null; - public static final Spell poison = null; - public static final Spell growth_aura = null; - public static final Spell bubble = null; - public static final Spell whirlwind = null; - public static final Spell poison_bomb = null; - public static final Spell summon_spirit_wolf = null; - public static final Spell blink = null; - public static final Spell agility = null; - public static final Spell conjure_sword = null; - public static final Spell conjure_pickaxe = null; - public static final Spell conjure_bow = null; - public static final Spell force_arrow = null; - public static final Spell shield = null; - public static final Spell replenish_hunger = null; - public static final Spell cure_effects = null; - public static final Spell heal_ally = null; - - public static final Spell summon_blaze = null; - public static final Spell ring_of_fire = null; - public static final Spell detonate = null; - public static final Spell fire_resistance = null; - public static final Spell fireskin = null; - public static final Spell flaming_axe = null; - public static final Spell blizzard = null; - public static final Spell summon_ice_wraith = null; - public static final Spell ice_shroud = null; - public static final Spell ice_charge = null; - public static final Spell frost_axe = null; - public static final Spell invoke_weather = null; - public static final Spell chain_lightning = null; - public static final Spell lightning_bolt = null; - public static final Spell summon_lightning_wraith = null; - public static final Spell static_aura = null; - public static final Spell lightning_disc = null; - public static final Spell mind_control = null; - public static final Spell summon_wither_skeleton = null; - public static final Spell entrapment = null; - public static final Spell wither_skull = null; - public static final Spell darkness_orb = null; - public static final Spell shadow_ward = null; - public static final Spell decay = null; - public static final Spell water_breathing = null; - public static final Spell tornado = null; - public static final Spell glide = null; - public static final Spell summon_spirit_horse = null; - public static final Spell spider_swarm = null; - public static final Spell slime = null; - public static final Spell petrify = null; - public static final Spell invisibility = null; - public static final Spell levitation = null; - public static final Spell force_orb = null; - public static final Spell transportation = null; - public static final Spell spectral_pathway = null; - public static final Spell phase_step = null; - public static final Spell vanishing_box = null; - public static final Spell greater_heal = null; - public static final Spell healing_aura = null; - public static final Spell forcefield = null; - public static final Spell ironflesh = null; - public static final Spell transience = null; - - public static final Spell meteor = null; - public static final Spell firestorm = null; - public static final Spell summon_phoenix = null; - public static final Spell ice_age = null; - public static final Spell wall_of_frost = null; - public static final Spell summon_ice_giant = null; - public static final Spell thunderstorm = null; - public static final Spell lightning_hammer = null; - public static final Spell plague_of_darkness = null; - public static final Spell summon_skeleton_legion = null; - public static final Spell summon_shadow_wraith = null; - public static final Spell forests_curse = null; - public static final Spell flight = null; - public static final Spell silverfish_swarm = null; - public static final Spell black_hole = null; - public static final Spell shockwave = null; - public static final Spell summon_iron_golem = null; - public static final Spell arrow_rain = null; - public static final Spell diamondflesh = null; - public static final Spell font_of_vitality = null; - - // Wizardry 1.1 spells - - public static final Spell smoke_bomb = null; - public static final Spell mind_trick = null; - public static final Spell leap = null; - - public static final Spell pocket_furnace = null; - public static final Spell intimidate = null; - public static final Spell banish = null; - public static final Spell sixth_sense = null; - public static final Spell darkvision = null; - public static final Spell clairvoyance = null; - public static final Spell pocket_workbench = null; - public static final Spell imbue_weapon = null; - public static final Spell invigorating_presence = null; - public static final Spell oakflesh = null; - - public static final Spell greater_fireball = null; - public static final Spell flaming_weapon = null; - public static final Spell ice_lance = null; - public static final Spell freezing_weapon = null; - public static final Spell ice_spikes = null; - public static final Spell lightning_pulse = null; - public static final Spell curse_of_soulbinding = null; - public static final Spell cobwebs = null; - public static final Spell decoy = null; - public static final Spell arcane_jammer = null; - public static final Spell conjure_armour = null; - public static final Spell group_heal = null; - - public static final Spell hailstorm = null; - public static final Spell lightning_web = null; - public static final Spell summon_storm_elemental = null; - public static final Spell earthquake = null; - public static final Spell font_of_mana = null; - - @SubscribeEvent - public static void register(RegistryEvent.Register event){ - - IForgeRegistry registry = event.getRegistry(); - - // event.getRegistry should always equal Spell.registry. - registry.register(new None()); - registry.register(new MagicMissile()); - registry.register(new Ignite()); - registry.register(new Freeze()); - registry.register(new Snowball()); - registry.register(new Arc()); - registry.register(new Thunderbolt()); - registry.register(new SummonZombie()); - registry.register(new Snare()); - registry.register(new Dart()); - registry.register(new Light()); - registry.register(new Telekinesis()); - registry.register(new Heal()); - - registry.register(new Fireball()); - registry.register(new FlameRay()); - registry.register(new Firebomb()); - registry.register(new FireSigil()); - registry.register(new Firebolt()); - registry.register(new FrostRay()); - registry.register(new SummonSnowGolem()); - registry.register(new IceShard()); - registry.register(new IceStatue()); - registry.register(new FrostSigil()); - registry.register(new LightningRay()); - registry.register(new SparkBomb()); - registry.register(new HomingSpark()); - registry.register(new LightningSigil()); - registry.register(new LightningArrow()); - registry.register(new LifeDrain()); - registry.register(new SummonSkeleton()); - registry.register(new Metamorphosis()); - registry.register(new Wither()); - registry.register(new Poison()); - registry.register(new GrowthAura()); - registry.register(new Bubble()); - registry.register(new Whirlwind()); - registry.register(new PoisonBomb()); - registry.register(new SummonSpiritWolf()); - registry.register(new Blink()); - registry.register(new Agility()); - registry.register(new ConjureSword()); - registry.register(new ConjurePickaxe()); - registry.register(new ConjureBow()); - registry.register(new ForceArrow()); - registry.register(new Shield()); - registry.register(new ReplenishHunger()); - registry.register(new CureEffects()); - registry.register(new HealAlly()); - - registry.register(new SummonBlaze()); - registry.register(new RingOfFire()); - registry.register(new Detonate()); - registry.register(new FireResistance()); - registry.register(new Fireskin()); - registry.register(new FlamingAxe()); - registry.register(new Blizzard()); - registry.register(new SummonIceWraith()); - registry.register(new IceShroud()); - registry.register(new IceCharge()); - registry.register(new FrostAxe()); - registry.register(new InvokeWeather()); - registry.register(new ChainLightning()); - registry.register(new LightningBolt()); - registry.register(new SummonLightningWraith()); - registry.register(new StaticAura()); - registry.register(new LightningDisc()); - registry.register(new MindControl()); - registry.register(new SummonWitherSkeleton()); - registry.register(new Entrapment()); - registry.register(new WitherSkull()); - registry.register(new DarknessOrb()); - registry.register(new ShadowWard()); - registry.register(new Decay()); - registry.register(new WaterBreathing()); - registry.register(new Tornado()); - registry.register(new Glide()); - registry.register(new SummonSpiritHorse()); - registry.register(new SpiderSwarm()); - registry.register(new Slime()); - registry.register(new Petrify()); - registry.register(new Invisibility()); - registry.register(new Levitation()); - registry.register(new ForceOrb()); - registry.register(new Transportation()); - registry.register(new SpectralPathway()); - registry.register(new PhaseStep()); - registry.register(new VanishingBox()); - registry.register(new GreaterHeal()); - registry.register(new HealingAura()); - registry.register(new Forcefield()); - registry.register(new Ironflesh()); - registry.register(new Transience()); - - registry.register(new Meteor()); - registry.register(new Firestorm()); - registry.register(new SummonPhoenix()); - registry.register(new IceAge()); - registry.register(new WallOfFrost()); - registry.register(new SummonIceGiant()); - registry.register(new Thunderstorm()); - registry.register(new LightningHammer()); - registry.register(new PlagueOfDarkness()); - registry.register(new SummonSkeletonLegion()); - registry.register(new SummonShadowWraith()); - registry.register(new ForestsCurse()); - registry.register(new Flight()); - registry.register(new SilverfishSwarm()); - registry.register(new BlackHole()); - registry.register(new Shockwave()); - registry.register(new SummonIronGolem()); - registry.register(new ArrowRain()); - registry.register(new Diamondflesh()); - registry.register(new FontOfVitality()); - - // Wizardry 1.1 spells - - registry.register(new SmokeBomb()); - registry.register(new MindTrick()); - registry.register(new Leap()); - - registry.register(new PocketFurnace()); - registry.register(new Intimidate()); - registry.register(new Banish()); - registry.register(new SixthSense()); - registry.register(new Darkvision()); - registry.register(new Clairvoyance()); - registry.register(new PocketWorkbench()); - registry.register(new ImbueWeapon()); - registry.register(new InvigoratingPresence()); - registry.register(new Oakflesh()); - - registry.register(new GreaterFireball()); - registry.register(new FlamingWeapon()); - registry.register(new IceLance()); - registry.register(new FreezingWeapon()); - registry.register(new IceSpikes()); - registry.register(new LightningPulse()); - registry.register(new CurseOfSoulbinding()); - registry.register(new Cobwebs()); - registry.register(new Decoy()); - registry.register(new ArcaneJammer()); - registry.register(new ConjureArmour()); - registry.register(new GroupHeal()); - - registry.register(new Hailstorm()); - registry.register(new LightningWeb()); - registry.register(new SummonStormElemental()); - registry.register(new Earthquake()); - registry.register(new FontOfMana()); - } - -} \ No newline at end of file diff --git a/src/main/java/electroblob/wizardry/registry/WizardryAchievements.java b/src/main/java/electroblob/wizardry/registry/WizardryAchievements.java deleted file mode 100644 index 2e3ab165..00000000 --- a/src/main/java/electroblob/wizardry/registry/WizardryAchievements.java +++ /dev/null @@ -1,47 +0,0 @@ -package electroblob.wizardry.registry; - -import net.minecraft.init.Blocks; -import net.minecraft.init.Items; -import net.minecraft.stats.Achievement; -import net.minecraftforge.common.AchievementPage; - -/** - * Class responsible for defining, storing and registering all of wizardry's achievements (achievements don't have a - * registry, but it make sense to do them the same way as everything else). - * @author Electroblob - * @since Wizardry 2.1 - */ -public final class WizardryAchievements { - - public static final Achievement crystal = new Achievement("crystal", "crystal", -1, -1, WizardryItems.magic_crystal, null).registerStat(); - public static final Achievement arcane_initiate = new Achievement("arcane_initiate", "arcane_initiate", 2, -1, WizardryItems.magic_wand, crystal).registerStat(); - public static final Achievement apprentice = new Achievement("apprentice", "apprentice", 2, 3, WizardryItems.apprentice_wand, arcane_initiate).registerStat(); - public static final Achievement master = new Achievement("master", "master", 3, 6, WizardryItems.master_wand, apprentice).registerStat(); - public static final Achievement all_spells = new Achievement("all_spells", "all_spells", 5, 7, WizardryItems.wizard_handbook, master).registerStat().setSpecial(); - public static final Achievement wizard_trade = new Achievement("wizard_trade", "wizard_trade", 2, -5, Items.EMERALD, arcane_initiate).registerStat(); - public static final Achievement buy_master_spell = new Achievement("buy_master_spell", "buy_master_spell", 5, -5, WizardryItems.spell_book, wizard_trade).registerStat().setSpecial(); - public static final Achievement freeze_blaze = new Achievement("freeze_blaze", "freeze_blaze", -1, 3, Blocks.ICE, apprentice).registerStat(); - public static final Achievement charge_creeper = new Achievement("charge_creeper", "charge_creeper", -1, 1, Items.GUNPOWDER, arcane_initiate).registerStat(); - public static final Achievement frankenstein = new Achievement("frankenstein", "frankenstein", -3, 1, WizardryItems.advanced_lightning_wand, charge_creeper).registerStat().setSpecial(); - public static final Achievement special_upgrade = new Achievement("special_upgrade", "special_upgrade", 4, 1, WizardryItems.condenser_upgrade, arcane_initiate).registerStat(); - public static final Achievement craft_flask = new Achievement("craft_flask", "craft_flask", 4, -2, WizardryItems.mana_flask, arcane_initiate).registerStat(); - public static final Achievement elemental = new Achievement("elemental", "elemental", 6, -1, WizardryItems.basic_fire_wand, arcane_initiate).registerStat(); - public static final Achievement armour_set = new Achievement("armour_set", "armour_set", 0, -4, WizardryItems.wizard_hat, arcane_initiate).registerStat(); - public static final Achievement legendary = new Achievement("legendary", "legendary", -2, -5, WizardryItems.armour_upgrade, armour_set).registerStat().setSpecial(); - public static final Achievement self_destruct = new Achievement("self_destruct", "self_destruct", 1, 0, Blocks.PUMPKIN, arcane_initiate).registerStat(); - public static final Achievement pig_tornado = new Achievement("pig_tornado", "pig_tornado", 6, 3, Items.SADDLE, apprentice).registerStat().setSpecial(); - public static final Achievement jam_wizard = new Achievement("jam_wizard", "jam_wizard", 4, 4, Blocks.WEB, apprentice).registerStat(); - public static final Achievement slime_skeleton = new Achievement("slime_skeleton", "slime_skeleton", 1, 5, Items.SLIME_BALL, apprentice).registerStat(); - public static final Achievement anger_wizard = new Achievement("anger_wizard", "anger_wizard", 1, -7, Items.IRON_SWORD, wizard_trade).registerStat(); - public static final Achievement defeat_evil_wizard = new Achievement("defeat_evil_wizard", "defeat_evil_wizard", 4, -7, WizardryItems.wizard_boots_necromancy, wizard_trade).registerStat(); - public static final Achievement max_out_wand = new Achievement("max_out_wand", "max_out_wand", 7, 1, WizardryItems.arcane_tome, special_upgrade).registerStat().setSpecial(); - public static final Achievement element_master = new Achievement("element_master", "element_master", 7, -3, WizardryItems.master_ice_wand, elemental).registerStat().setSpecial(); - public static final Achievement identify_spell = new Achievement("identify_spell", "identify_spell", -2, -3, WizardryItems.identification_scroll, arcane_initiate).registerStat(); - - private static final Achievement[] ACHIEVEMENTS_LIST = {crystal, arcane_initiate, apprentice, master, all_spells, wizard_trade, buy_master_spell, freeze_blaze, charge_creeper, - frankenstein, special_upgrade, craft_flask, elemental, armour_set, legendary, self_destruct, pig_tornado, jam_wizard, slime_skeleton, anger_wizard, defeat_evil_wizard, - max_out_wand, element_master, identify_spell}; - - public static final AchievementPage WIZARDRY_ACHIEVEMENT_PAGE = new AchievementPage("Wizardry", ACHIEVEMENTS_LIST); - -} \ No newline at end of file diff --git a/src/main/java/electroblob/wizardry/registry/WizardryBlocks.java b/src/main/java/electroblob/wizardry/registry/WizardryBlocks.java deleted file mode 100644 index b1efa21a..00000000 --- a/src/main/java/electroblob/wizardry/registry/WizardryBlocks.java +++ /dev/null @@ -1,92 +0,0 @@ -package electroblob.wizardry.registry; - -import electroblob.wizardry.Wizardry; -import electroblob.wizardry.block.BlockArcaneWorkbench; -import electroblob.wizardry.block.BlockCrystalFlower; -import electroblob.wizardry.block.BlockCrystalOre; -import electroblob.wizardry.block.BlockMagicLight; -import electroblob.wizardry.block.BlockSnare; -import electroblob.wizardry.block.BlockSpectral; -import electroblob.wizardry.block.BlockStatue; -import electroblob.wizardry.block.BlockTransportationStone; -import electroblob.wizardry.block.BlockVanishingCobweb; -import net.minecraft.block.Block; -import net.minecraft.block.material.Material; -import net.minecraftforge.event.RegistryEvent; -import net.minecraftforge.fml.common.Mod; -import net.minecraftforge.fml.common.eventhandler.SubscribeEvent; -import net.minecraftforge.fml.common.registry.IForgeRegistry; - -/** - * Class responsible for defining, storing and registering all of wizardry's blocks. - * @author Electroblob - * @since Wizardry 2.1 - */ -@Mod.EventBusSubscriber -public final class WizardryBlocks { - - // I get registry events, they make sense - even I doubted myself with when to do various things in the load process, - // so I can see why the folks at Forge wanted to save us having to think about it. - // What I do not understand is why you would use @ObjectHolder for your own blocks and items. What's the point in - // making your code longer and more complicated, when you can just define the blocks as constants and register them - // later? - - // Found a very nice way of registering things using arrays, which might make @ObjectHolder actually useful. - // http://www.minecraftforge.net/forum/topic/49497-1112-is-using-registryevent-this-way-ok/ - - // Anything set to use the material 'air' will not render, even with a TESR! - - // setSoundType should be public, but in this particular version it isn't... which is a bit of a pain. - - public static final Block arcane_workbench = new BlockArcaneWorkbench().setHardness(1.0F).setCreativeTab(WizardryTabs.WIZARDRY); - public static final Block crystal_ore = new BlockCrystalOre(Material.ROCK).setHardness(3.0F).setCreativeTab(WizardryTabs.WIZARDRY); - public static final Block petrified_stone = new BlockStatue(Material.ROCK).setHardness(1.5F).setResistance(10.0F); - public static final Block ice_statue = new BlockStatue(Material.ICE).setHardness(0.5F).setLightOpacity(3); - public static final Block magic_light = new BlockMagicLight(Material.CIRCUITS); - public static final Block crystal_flower = new BlockCrystalFlower(Material.PLANTS).setHardness(0.0F).setCreativeTab(WizardryTabs.WIZARDRY); - public static final Block snare = new BlockSnare(Material.PLANTS).setHardness(0.0F); - public static final Block transportation_stone = new BlockTransportationStone(Material.ROCK).setHardness(0.0F).setLightLevel(0.5f).setLightOpacity(0).setCreativeTab(WizardryTabs.WIZARDRY); - public static final Block spectral_block = new BlockSpectral(Material.GLASS).setLightLevel(0.7f).setLightOpacity(0).setBlockUnbreakable().setResistance(6000000.0F); - public static final Block crystal_block = new Block(Material.IRON).setHardness(5.0F).setResistance(10.0F).setCreativeTab(WizardryTabs.WIZARDRY); - public static final Block meteor = new Block(Material.ROCK).setLightLevel(1); - public static final Block vanishing_cobweb = new BlockVanishingCobweb(Material.WEB).setLightOpacity(1).setHardness(4.0F); - - static { - // This is here because Block#setHarvestLevel isn't chainable. - crystal_block.setHarvestLevel("pickaxe", 2); - } - - /** - * Sets both the registry and unlocalised names of the given block, then registers it with the given registry. Use - * this instead of {@link Block#setRegistryName(String)} and {@link Block#setUnlocalizedName(String)} during - * construction, for convenience and consistency. - * @param registry The registry to register the given block to. - * @param item The block to register. - * @param name The name of the block, without the mod ID or the .name stuff. The registry name will be - * {@code wizardry:[name]}. The unlocalised name will be {@code tile.wizardry:[name].name}. - */ - public static void registerBlock(IForgeRegistry registry, Block block, String name){ - block.setRegistryName(Wizardry.MODID, name); - block.setUnlocalizedName(block.getRegistryName().toString()); - registry.register(block); - } - - @SubscribeEvent - public static void register(RegistryEvent.Register event){ - - IForgeRegistry registry = event.getRegistry(); - - registerBlock(registry, arcane_workbench, "arcane_workbench"); - registerBlock(registry, crystal_ore, "crystal_ore"); - registerBlock(registry, petrified_stone, "petrified_stone"); - registerBlock(registry, ice_statue, "ice_statue"); - registerBlock(registry, magic_light, "magic_light"); - registerBlock(registry, crystal_flower, "crystal_flower"); - registerBlock(registry, snare, "snare"); - registerBlock(registry, transportation_stone, "transportation_stone"); - registerBlock(registry, spectral_block, "spectral_block"); - registerBlock(registry, crystal_block, "crystal_block"); - registerBlock(registry, meteor, "meteor"); - registerBlock(registry, vanishing_cobweb, "vanishing_cobweb"); - } -} \ No newline at end of file diff --git a/src/main/java/electroblob/wizardry/registry/WizardryEnchantments.java b/src/main/java/electroblob/wizardry/registry/WizardryEnchantments.java deleted file mode 100644 index e34ca427..00000000 --- a/src/main/java/electroblob/wizardry/registry/WizardryEnchantments.java +++ /dev/null @@ -1,41 +0,0 @@ -package electroblob.wizardry.registry; - -import electroblob.wizardry.Wizardry; -import electroblob.wizardry.enchantment.EnchantmentMagicSword; -import electroblob.wizardry.enchantment.EnchantmentTimed; -import net.minecraft.enchantment.Enchantment; -import net.minecraftforge.event.RegistryEvent; -import net.minecraftforge.fml.common.Mod; -import net.minecraftforge.fml.common.eventhandler.SubscribeEvent; - -/** - * Class responsible for defining, storing and registering all of wizardry's enchantments. - * @author Electroblob - * @since Wizardry 2.1 - */ -@Mod.EventBusSubscriber -public final class WizardryEnchantments { - - // At the moment these enchantments generate on books in dungeon chests due to a bad bit of code (EnchantRandomly:50). - // No idea how to fix this because I have no way of hooking into that code... removing the enchantments from the - // registry works, but breaks everything else! - - // TODO: For the time being, a dynamic solution will have to do, i.e. intercept the book when it is generated and - // reassign its enchantment. - - // All of these have custom classes, so the unlocalised name (referred to simply as 'name' for enchantments) is - // dealt with inside those classes. - public static final Enchantment magic_sword = new EnchantmentMagicSword().setRegistryName(Wizardry.MODID, "magic_sword"); - public static final Enchantment magic_bow = new EnchantmentTimed().setRegistryName(Wizardry.MODID, "magic_bow"); - public static final Enchantment flaming_weapon = new EnchantmentTimed().setRegistryName(Wizardry.MODID, "flaming_weapon"); - public static final Enchantment freezing_weapon = new EnchantmentTimed().setRegistryName(Wizardry.MODID, "freezing_weapon"); - - @SubscribeEvent - public static void register(RegistryEvent.Register event){ - event.getRegistry().register(magic_sword); - event.getRegistry().register(magic_bow); - event.getRegistry().register(flaming_weapon); - event.getRegistry().register(freezing_weapon); - } - -} diff --git a/src/main/java/electroblob/wizardry/registry/WizardryItems.java b/src/main/java/electroblob/wizardry/registry/WizardryItems.java deleted file mode 100644 index 89b053dd..00000000 --- a/src/main/java/electroblob/wizardry/registry/WizardryItems.java +++ /dev/null @@ -1,419 +0,0 @@ -package electroblob.wizardry.registry; - -import java.util.HashMap; -import java.util.Map; - -import org.apache.commons.lang3.tuple.ImmutablePair; -import org.apache.commons.lang3.tuple.Pair; - -import electroblob.wizardry.Wizardry; -import electroblob.wizardry.constants.Element; -import electroblob.wizardry.constants.Tier; -import electroblob.wizardry.item.ItemArcaneTome; -import electroblob.wizardry.item.ItemArmourUpgrade; -import electroblob.wizardry.item.ItemFirebomb; -import electroblob.wizardry.item.ItemFlamingAxe; -import electroblob.wizardry.item.ItemFrostAxe; -import electroblob.wizardry.item.ItemIdentificationScroll; -import electroblob.wizardry.item.ItemPoisonBomb; -import electroblob.wizardry.item.ItemScroll; -import electroblob.wizardry.item.ItemSmokeBomb; -import electroblob.wizardry.item.ItemSpectralArmour; -import electroblob.wizardry.item.ItemSpectralBow; -import electroblob.wizardry.item.ItemSpectralPickaxe; -import electroblob.wizardry.item.ItemSpectralSword; -import electroblob.wizardry.item.ItemSpellBook; -import electroblob.wizardry.item.ItemWand; -import electroblob.wizardry.item.ItemWizardArmour; -import electroblob.wizardry.item.ItemWizardHandbook; -import net.minecraft.block.Block; -import net.minecraft.entity.player.EntityPlayer; -import net.minecraft.init.SoundEvents; -import net.minecraft.inventory.EntityEquipmentSlot; -import net.minecraft.item.Item; -import net.minecraft.item.Item.ToolMaterial; -import net.minecraft.item.ItemArmor.ArmorMaterial; -import net.minecraft.item.ItemBlock; -import net.minecraft.item.ItemStack; -import net.minecraft.world.World; -import net.minecraftforge.common.util.EnumHelper; -import net.minecraftforge.event.RegistryEvent; -import net.minecraftforge.fml.common.Mod; -import net.minecraftforge.fml.common.eventhandler.SubscribeEvent; -import net.minecraftforge.fml.common.registry.IForgeRegistry; - -/** - * Class responsible for defining, storing and registering all of wizardry's items. Also registers the ItemBlocks for - * wizardry's blocks. - * @author Electroblob - * @since Wizardry 2.1 - */ -@Mod.EventBusSubscriber -public final class WizardryItems { - - public static final Item magic_crystal = new Item().setCreativeTab(WizardryTabs.WIZARDRY); - - public static final Item magic_wand = new ItemWand(Tier.BASIC, null); - public static final Item apprentice_wand = new ItemWand(Tier.APPRENTICE, null); - public static final Item advanced_wand = new ItemWand(Tier.ADVANCED, null); - public static final Item master_wand = new ItemWand(Tier.MASTER, null); - - public static final Item arcane_tome = new ItemArcaneTome(); - public static final Item wizard_handbook = new ItemWizardHandbook(); - public static final Item spell_book = new ItemSpellBook(); - - public static final Item basic_fire_wand = new ItemWand(Tier.BASIC, Element.FIRE); - public static final Item basic_ice_wand = new ItemWand(Tier.BASIC, Element.ICE); - public static final Item basic_lightning_wand = new ItemWand(Tier.BASIC, Element.LIGHTNING); - public static final Item basic_necromancy_wand = new ItemWand(Tier.BASIC, Element.NECROMANCY); - public static final Item basic_earth_wand = new ItemWand(Tier.BASIC, Element.EARTH); - public static final Item basic_sorcery_wand = new ItemWand(Tier.BASIC, Element.SORCERY); - public static final Item basic_healing_wand = new ItemWand(Tier.BASIC, Element.HEALING); - - public static final Item apprentice_fire_wand = new ItemWand(Tier.APPRENTICE, Element.FIRE); - public static final Item apprentice_ice_wand = new ItemWand(Tier.APPRENTICE, Element.ICE); - public static final Item apprentice_lightning_wand = new ItemWand(Tier.APPRENTICE, Element.LIGHTNING); - public static final Item apprentice_necromancy_wand = new ItemWand(Tier.APPRENTICE, Element.NECROMANCY); - public static final Item apprentice_earth_wand = new ItemWand(Tier.APPRENTICE, Element.EARTH); - public static final Item apprentice_sorcery_wand = new ItemWand(Tier.APPRENTICE, Element.SORCERY); - public static final Item apprentice_healing_wand = new ItemWand(Tier.APPRENTICE, Element.HEALING); - - public static final Item advanced_fire_wand = new ItemWand(Tier.ADVANCED, Element.FIRE); - public static final Item advanced_ice_wand = new ItemWand(Tier.ADVANCED, Element.ICE); - public static final Item advanced_lightning_wand = new ItemWand(Tier.ADVANCED, Element.LIGHTNING); - public static final Item advanced_necromancy_wand = new ItemWand(Tier.ADVANCED, Element.NECROMANCY); - public static final Item advanced_earth_wand = new ItemWand(Tier.ADVANCED, Element.EARTH); - public static final Item advanced_sorcery_wand = new ItemWand(Tier.ADVANCED, Element.SORCERY); - public static final Item advanced_healing_wand = new ItemWand(Tier.ADVANCED, Element.HEALING); - - public static final Item master_fire_wand = new ItemWand(Tier.MASTER, Element.FIRE); - public static final Item master_ice_wand = new ItemWand(Tier.MASTER, Element.ICE); - public static final Item master_lightning_wand = new ItemWand(Tier.MASTER, Element.LIGHTNING); - public static final Item master_necromancy_wand = new ItemWand(Tier.MASTER, Element.NECROMANCY); - public static final Item master_earth_wand = new ItemWand(Tier.MASTER, Element.EARTH); - public static final Item master_sorcery_wand = new ItemWand(Tier.MASTER, Element.SORCERY); - public static final Item master_healing_wand = new ItemWand(Tier.MASTER, Element.HEALING); - - public static final Item spectral_sword = new ItemSpectralSword(ToolMaterial.IRON); - public static final Item spectral_pickaxe = new ItemSpectralPickaxe(ToolMaterial.IRON); - public static final Item spectral_bow = new ItemSpectralBow(); - - public static final Item mana_flask = new Item(){ - @Override - public void onCreated(ItemStack par1ItemStack, World par2World, EntityPlayer entityplayer){ - entityplayer.addStat(WizardryAchievements.craft_flask); - } - }.setCreativeTab(WizardryTabs.WIZARDRY); - - public static final Item storage_upgrade = new Item().setCreativeTab(WizardryTabs.WIZARDRY); - public static final Item siphon_upgrade = new Item().setCreativeTab(WizardryTabs.WIZARDRY); - public static final Item condenser_upgrade = new Item().setCreativeTab(WizardryTabs.WIZARDRY); - public static final Item range_upgrade = new Item().setCreativeTab(WizardryTabs.WIZARDRY); - public static final Item duration_upgrade = new Item().setCreativeTab(WizardryTabs.WIZARDRY); - public static final Item cooldown_upgrade = new Item().setCreativeTab(WizardryTabs.WIZARDRY); - public static final Item blast_upgrade = new Item().setCreativeTab(WizardryTabs.WIZARDRY); - public static final Item attunement_upgrade = new Item().setCreativeTab(WizardryTabs.WIZARDRY); - - public static final Item magic_silk = new Item().setCreativeTab(WizardryTabs.WIZARDRY); - - public static final Item.ToolMaterial MAGICAL = EnumHelper.addToolMaterial("MAGICAL", 3, 1000, 8.0f, 4.0f, 0); - - public static final Item flaming_axe = new ItemFlamingAxe(MAGICAL); - public static final Item frost_axe = new ItemFrostAxe(MAGICAL); - - public static final Item firebomb = new ItemFirebomb(); - public static final Item poison_bomb = new ItemPoisonBomb(); - - public static final Item blank_scroll = new Item().setCreativeTab(WizardryTabs.WIZARDRY); - public static final Item scroll = new ItemScroll().setCreativeTab(WizardryTabs.SPELLS); - - // The only way to get these is in dungeon chests (They are legendary, after all. Wizards don't just have them. - // Also if they were sold you could buy four from the same wizard - and that's no fun at all!) - public static final Item armour_upgrade = new ItemArmourUpgrade(); - - public static final ArmorMaterial SILK = EnumHelper.addArmorMaterial("SILK", "wizardry/textures/armour/wizard_armour", - 15, new int[]{2, 5, 4, 2}, 0, SoundEvents.ITEM_ARMOR_EQUIP_LEATHER, 0.0F); - // Saw a post somewhere that said you have to put these in the init methods rather than defining them as constants. - // I *think* that's because the post was for a newer Minecraft version, where custom armour has its own renderer or - // something, meaning it would be done a lot like the entity rendering registry in ClientProxy. This is all working - // fine it seems, so I'm not going to fiddle with it, but it might be useful to know if I update versions again. - public static final Item wizard_hat = new ItemWizardArmour(SILK, 1, EntityEquipmentSlot.HEAD, null); - public static final Item wizard_robe = new ItemWizardArmour(SILK, 1, EntityEquipmentSlot.CHEST, null); - public static final Item wizard_leggings = new ItemWizardArmour(SILK, 1, EntityEquipmentSlot.LEGS, null); - public static final Item wizard_boots = new ItemWizardArmour(SILK, 1, EntityEquipmentSlot.FEET, null); - - public static final Item wizard_hat_fire = new ItemWizardArmour(SILK, 1, EntityEquipmentSlot.HEAD, Element.FIRE); - public static final Item wizard_robe_fire = new ItemWizardArmour(SILK, 1, EntityEquipmentSlot.CHEST, Element.FIRE); - public static final Item wizard_leggings_fire = new ItemWizardArmour(SILK, 1, EntityEquipmentSlot.LEGS, Element.FIRE); - public static final Item wizard_boots_fire = new ItemWizardArmour(SILK, 1, EntityEquipmentSlot.FEET, Element.FIRE); - - public static final Item wizard_hat_ice = new ItemWizardArmour(SILK, 1, EntityEquipmentSlot.HEAD, Element.ICE); - public static final Item wizard_robe_ice = new ItemWizardArmour(SILK, 1, EntityEquipmentSlot.CHEST, Element.ICE); - public static final Item wizard_leggings_ice = new ItemWizardArmour(SILK, 1, EntityEquipmentSlot.LEGS, Element.ICE); - public static final Item wizard_boots_ice = new ItemWizardArmour(SILK, 1, EntityEquipmentSlot.FEET, Element.ICE); - - public static final Item wizard_hat_lightning = new ItemWizardArmour(SILK, 1, EntityEquipmentSlot.HEAD, Element.LIGHTNING); - public static final Item wizard_robe_lightning = new ItemWizardArmour(SILK, 1, EntityEquipmentSlot.CHEST, Element.LIGHTNING); - public static final Item wizard_leggings_lightning = new ItemWizardArmour(SILK, 1, EntityEquipmentSlot.LEGS, Element.LIGHTNING); - public static final Item wizard_boots_lightning = new ItemWizardArmour(SILK, 1, EntityEquipmentSlot.FEET, Element.LIGHTNING); - - public static final Item wizard_hat_necromancy = new ItemWizardArmour(SILK, 1, EntityEquipmentSlot.HEAD, Element.NECROMANCY); - public static final Item wizard_robe_necromancy = new ItemWizardArmour(SILK, 1, EntityEquipmentSlot.CHEST, Element.NECROMANCY); - public static final Item wizard_leggings_necromancy = new ItemWizardArmour(SILK, 1, EntityEquipmentSlot.LEGS, Element.NECROMANCY); - public static final Item wizard_boots_necromancy = new ItemWizardArmour(SILK, 1, EntityEquipmentSlot.FEET, Element.NECROMANCY); - - public static final Item wizard_hat_earth = new ItemWizardArmour(SILK, 1, EntityEquipmentSlot.HEAD, Element.EARTH); - public static final Item wizard_robe_earth = new ItemWizardArmour(SILK, 1, EntityEquipmentSlot.CHEST, Element.EARTH); - public static final Item wizard_leggings_earth = new ItemWizardArmour(SILK, 1, EntityEquipmentSlot.LEGS, Element.EARTH); - public static final Item wizard_boots_earth = new ItemWizardArmour(SILK, 1, EntityEquipmentSlot.FEET, Element.EARTH); - - public static final Item wizard_hat_sorcery = new ItemWizardArmour(SILK, 1, EntityEquipmentSlot.HEAD, Element.SORCERY); - public static final Item wizard_robe_sorcery = new ItemWizardArmour(SILK, 1, EntityEquipmentSlot.CHEST, Element.SORCERY); - public static final Item wizard_leggings_sorcery = new ItemWizardArmour(SILK, 1, EntityEquipmentSlot.LEGS, Element.SORCERY); - public static final Item wizard_boots_sorcery = new ItemWizardArmour(SILK, 1, EntityEquipmentSlot.FEET, Element.SORCERY); - - public static final Item wizard_hat_healing = new ItemWizardArmour(SILK, 1, EntityEquipmentSlot.HEAD, Element.HEALING); - public static final Item wizard_robe_healing = new ItemWizardArmour(SILK, 1, EntityEquipmentSlot.CHEST, Element.HEALING); - public static final Item wizard_leggings_healing = new ItemWizardArmour(SILK, 1, EntityEquipmentSlot.LEGS, Element.HEALING); - public static final Item wizard_boots_healing = new ItemWizardArmour(SILK, 1, EntityEquipmentSlot.FEET, Element.HEALING); - - public static final Item spectral_helmet = new ItemSpectralArmour(ArmorMaterial.IRON, 1, EntityEquipmentSlot.HEAD); - public static final Item spectral_chestplate = new ItemSpectralArmour(ArmorMaterial.IRON, 1, EntityEquipmentSlot.CHEST); - public static final Item spectral_leggings = new ItemSpectralArmour(ArmorMaterial.IRON, 1, EntityEquipmentSlot.LEGS); - public static final Item spectral_boots = new ItemSpectralArmour(ArmorMaterial.IRON, 1, EntityEquipmentSlot.FEET); - - public static final Item smoke_bomb = new ItemSmokeBomb(); - - public static final Item identification_scroll = new ItemIdentificationScroll(); - - public static final Map, Item> WAND_MAP = new HashMap<>(); - public static final Map, Item> ARMOUR_MAP = new HashMap<>(); - - static { - - WAND_MAP.put(ImmutablePair.of(Tier.BASIC, Element.MAGIC), magic_wand); - WAND_MAP.put(ImmutablePair.of(Tier.BASIC, Element.FIRE), basic_fire_wand); - WAND_MAP.put(ImmutablePair.of(Tier.BASIC, Element.ICE), basic_ice_wand); - WAND_MAP.put(ImmutablePair.of(Tier.BASIC, Element.LIGHTNING), basic_lightning_wand); - WAND_MAP.put(ImmutablePair.of(Tier.BASIC, Element.NECROMANCY), basic_necromancy_wand); - WAND_MAP.put(ImmutablePair.of(Tier.BASIC, Element.EARTH), basic_earth_wand); - WAND_MAP.put(ImmutablePair.of(Tier.BASIC, Element.SORCERY), basic_sorcery_wand); - WAND_MAP.put(ImmutablePair.of(Tier.BASIC, Element.HEALING), basic_healing_wand); - WAND_MAP.put(ImmutablePair.of(Tier.APPRENTICE, Element.MAGIC), apprentice_wand); - WAND_MAP.put(ImmutablePair.of(Tier.APPRENTICE, Element.FIRE), apprentice_fire_wand); - WAND_MAP.put(ImmutablePair.of(Tier.APPRENTICE, Element.ICE), apprentice_ice_wand); - WAND_MAP.put(ImmutablePair.of(Tier.APPRENTICE, Element.LIGHTNING), apprentice_lightning_wand); - WAND_MAP.put(ImmutablePair.of(Tier.APPRENTICE, Element.NECROMANCY), apprentice_necromancy_wand); - WAND_MAP.put(ImmutablePair.of(Tier.APPRENTICE, Element.EARTH), apprentice_earth_wand); - WAND_MAP.put(ImmutablePair.of(Tier.APPRENTICE, Element.SORCERY), apprentice_sorcery_wand); - WAND_MAP.put(ImmutablePair.of(Tier.APPRENTICE, Element.HEALING), apprentice_healing_wand); - WAND_MAP.put(ImmutablePair.of(Tier.ADVANCED, Element.MAGIC), advanced_wand); - WAND_MAP.put(ImmutablePair.of(Tier.ADVANCED, Element.FIRE), advanced_fire_wand); - WAND_MAP.put(ImmutablePair.of(Tier.ADVANCED, Element.ICE), advanced_ice_wand); - WAND_MAP.put(ImmutablePair.of(Tier.ADVANCED, Element.LIGHTNING), advanced_lightning_wand); - WAND_MAP.put(ImmutablePair.of(Tier.ADVANCED, Element.NECROMANCY), advanced_necromancy_wand); - WAND_MAP.put(ImmutablePair.of(Tier.ADVANCED, Element.EARTH), advanced_earth_wand); - WAND_MAP.put(ImmutablePair.of(Tier.ADVANCED, Element.SORCERY), advanced_sorcery_wand); - WAND_MAP.put(ImmutablePair.of(Tier.ADVANCED, Element.HEALING), advanced_healing_wand); - WAND_MAP.put(ImmutablePair.of(Tier.MASTER, Element.MAGIC), master_wand); - WAND_MAP.put(ImmutablePair.of(Tier.MASTER, Element.FIRE), master_fire_wand); - WAND_MAP.put(ImmutablePair.of(Tier.MASTER, Element.ICE), master_ice_wand); - WAND_MAP.put(ImmutablePair.of(Tier.MASTER, Element.LIGHTNING), master_lightning_wand); - WAND_MAP.put(ImmutablePair.of(Tier.MASTER, Element.NECROMANCY), master_necromancy_wand); - WAND_MAP.put(ImmutablePair.of(Tier.MASTER, Element.EARTH), master_earth_wand); - WAND_MAP.put(ImmutablePair.of(Tier.MASTER, Element.SORCERY), master_sorcery_wand); - WAND_MAP.put(ImmutablePair.of(Tier.MASTER, Element.HEALING), master_healing_wand); - - ARMOUR_MAP.put(ImmutablePair.of(EntityEquipmentSlot.HEAD, Element.MAGIC), wizard_hat); - ARMOUR_MAP.put(ImmutablePair.of(EntityEquipmentSlot.HEAD, Element.FIRE), wizard_hat_fire); - ARMOUR_MAP.put(ImmutablePair.of(EntityEquipmentSlot.HEAD, Element.ICE), wizard_hat_ice); - ARMOUR_MAP.put(ImmutablePair.of(EntityEquipmentSlot.HEAD, Element.LIGHTNING), wizard_hat_lightning); - ARMOUR_MAP.put(ImmutablePair.of(EntityEquipmentSlot.HEAD, Element.NECROMANCY), wizard_hat_necromancy); - ARMOUR_MAP.put(ImmutablePair.of(EntityEquipmentSlot.HEAD, Element.EARTH), wizard_hat_earth); - ARMOUR_MAP.put(ImmutablePair.of(EntityEquipmentSlot.HEAD, Element.SORCERY), wizard_hat_sorcery); - ARMOUR_MAP.put(ImmutablePair.of(EntityEquipmentSlot.HEAD, Element.HEALING), wizard_hat_healing); - ARMOUR_MAP.put(ImmutablePair.of(EntityEquipmentSlot.CHEST, Element.MAGIC), wizard_robe); - ARMOUR_MAP.put(ImmutablePair.of(EntityEquipmentSlot.CHEST, Element.FIRE), wizard_robe_fire); - ARMOUR_MAP.put(ImmutablePair.of(EntityEquipmentSlot.CHEST, Element.ICE), wizard_robe_ice); - ARMOUR_MAP.put(ImmutablePair.of(EntityEquipmentSlot.CHEST, Element.LIGHTNING), wizard_robe_lightning); - ARMOUR_MAP.put(ImmutablePair.of(EntityEquipmentSlot.CHEST, Element.NECROMANCY), wizard_robe_necromancy); - ARMOUR_MAP.put(ImmutablePair.of(EntityEquipmentSlot.CHEST, Element.EARTH), wizard_robe_earth); - ARMOUR_MAP.put(ImmutablePair.of(EntityEquipmentSlot.CHEST, Element.SORCERY), wizard_robe_sorcery); - ARMOUR_MAP.put(ImmutablePair.of(EntityEquipmentSlot.CHEST, Element.HEALING), wizard_robe_healing); - ARMOUR_MAP.put(ImmutablePair.of(EntityEquipmentSlot.LEGS, Element.MAGIC), wizard_leggings); - ARMOUR_MAP.put(ImmutablePair.of(EntityEquipmentSlot.LEGS, Element.FIRE), wizard_leggings_fire); - ARMOUR_MAP.put(ImmutablePair.of(EntityEquipmentSlot.LEGS, Element.ICE), wizard_leggings_ice); - ARMOUR_MAP.put(ImmutablePair.of(EntityEquipmentSlot.LEGS, Element.LIGHTNING), wizard_leggings_lightning); - ARMOUR_MAP.put(ImmutablePair.of(EntityEquipmentSlot.LEGS, Element.NECROMANCY), wizard_leggings_necromancy); - ARMOUR_MAP.put(ImmutablePair.of(EntityEquipmentSlot.LEGS, Element.EARTH), wizard_leggings_earth); - ARMOUR_MAP.put(ImmutablePair.of(EntityEquipmentSlot.LEGS, Element.SORCERY), wizard_leggings_sorcery); - ARMOUR_MAP.put(ImmutablePair.of(EntityEquipmentSlot.LEGS, Element.HEALING), wizard_leggings_healing); - ARMOUR_MAP.put(ImmutablePair.of(EntityEquipmentSlot.FEET, Element.MAGIC), wizard_boots); - ARMOUR_MAP.put(ImmutablePair.of(EntityEquipmentSlot.FEET, Element.FIRE), wizard_boots_fire); - ARMOUR_MAP.put(ImmutablePair.of(EntityEquipmentSlot.FEET, Element.ICE), wizard_boots_ice); - ARMOUR_MAP.put(ImmutablePair.of(EntityEquipmentSlot.FEET, Element.LIGHTNING), wizard_boots_lightning); - ARMOUR_MAP.put(ImmutablePair.of(EntityEquipmentSlot.FEET, Element.NECROMANCY), wizard_boots_necromancy); - ARMOUR_MAP.put(ImmutablePair.of(EntityEquipmentSlot.FEET, Element.EARTH), wizard_boots_earth); - ARMOUR_MAP.put(ImmutablePair.of(EntityEquipmentSlot.FEET, Element.SORCERY), wizard_boots_sorcery); - ARMOUR_MAP.put(ImmutablePair.of(EntityEquipmentSlot.FEET, Element.HEALING), wizard_boots_healing); - } - - /** - * Sets both the registry and unlocalised names of the given item, then registers it with the given registry. Use - * this instead of {@link Item#setRegistryName(String)} and {@link Item#setUnlocalizedName(String)} during - * construction, for convenience and consistency. - * @param registry The registry to register the given item to. - * @param item The item to register. - * @param name The name of the item, without the mod ID or the .name stuff. The registry name will be - * {@code wizardry:[name]}. The unlocalised name will be {@code item.wizardry:[name].name}. - */ - public static void registerItem(IForgeRegistry registry, Item item, String name){ - item.setRegistryName(Wizardry.MODID, name); - item.setUnlocalizedName(item.getRegistryName().toString()); - registry.register(item); - } - - /** Registers an ItemBlock afor the given block, with the same registry name as that block. */ - private static void registerItemBlock(IForgeRegistry registry, Block block){ - // We don't need to keep a reference to the ItemBlock, so this can all be done in one line. - registry.register(new ItemBlock(block).setRegistryName(block.getRegistryName())); - } - - @SubscribeEvent - public static void register(RegistryEvent.Register event){ - - IForgeRegistry registry = event.getRegistry(); - - // ItemBlocks - - // Not all blocks need an ItemBlock - registerItemBlock(registry, WizardryBlocks.arcane_workbench); - registerItemBlock(registry, WizardryBlocks.crystal_ore); - registerItemBlock(registry, WizardryBlocks.crystal_flower); - registerItemBlock(registry, WizardryBlocks.transportation_stone); - registerItemBlock(registry, WizardryBlocks.crystal_block); - - // Items - - registerItem(registry, magic_crystal, "magic_crystal"); - - registerItem(registry, magic_wand, "magic_wand"); - registerItem(registry, apprentice_wand, "apprentice_wand"); - registerItem(registry, advanced_wand, "advanced_wand"); - registerItem(registry, master_wand, "master_wand"); - - registerItem(registry, spell_book, "spell_book"); - registerItem(registry, arcane_tome, "arcane_tome"); - registerItem(registry, wizard_handbook, "wizard_handbook"); - - registerItem(registry, basic_fire_wand, "basic_fire_wand"); - registerItem(registry, basic_ice_wand, "basic_ice_wand"); - registerItem(registry, basic_lightning_wand, "basic_lightning_wand"); - registerItem(registry, basic_necromancy_wand, "basic_necromancy_wand"); - registerItem(registry, basic_earth_wand, "basic_earth_wand"); - registerItem(registry, basic_sorcery_wand, "basic_sorcery_wand"); - registerItem(registry, basic_healing_wand, "basic_healing_wand"); - - registerItem(registry, apprentice_fire_wand, "apprentice_fire_wand"); - registerItem(registry, apprentice_ice_wand, "apprentice_ice_wand"); - registerItem(registry, apprentice_lightning_wand, "apprentice_lightning_wand"); - registerItem(registry, apprentice_necromancy_wand, "apprentice_necromancy_wand"); - registerItem(registry, apprentice_earth_wand, "apprentice_earth_wand"); - registerItem(registry, apprentice_sorcery_wand, "apprentice_sorcery_wand"); - registerItem(registry, apprentice_healing_wand, "apprentice_healing_wand"); - - registerItem(registry, advanced_fire_wand, "advanced_fire_wand"); - registerItem(registry, advanced_ice_wand, "advanced_ice_wand"); - registerItem(registry, advanced_lightning_wand, "advanced_lightning_wand"); - registerItem(registry, advanced_necromancy_wand, "advanced_necromancy_wand"); - registerItem(registry, advanced_earth_wand, "advanced_earth_wand"); - registerItem(registry, advanced_sorcery_wand, "advanced_sorcery_wand"); - registerItem(registry, advanced_healing_wand, "advanced_healing_wand"); - - registerItem(registry, master_fire_wand, "master_fire_wand"); - registerItem(registry, master_ice_wand, "master_ice_wand"); - registerItem(registry, master_lightning_wand, "master_lightning_wand"); - registerItem(registry, master_necromancy_wand, "master_necromancy_wand"); - registerItem(registry, master_earth_wand, "master_earth_wand"); - registerItem(registry, master_sorcery_wand, "master_sorcery_wand"); - registerItem(registry, master_healing_wand, "master_healing_wand"); - - registerItem(registry, spectral_sword, "spectral_sword"); - registerItem(registry, spectral_pickaxe, "spectral_pickaxe"); - registerItem(registry, spectral_bow, "spectral_bow"); - - registerItem(registry, mana_flask, "mana_flask"); - - registerItem(registry, storage_upgrade, "storage_upgrade"); - registerItem(registry, siphon_upgrade, "siphon_upgrade"); - registerItem(registry, condenser_upgrade, "condenser_upgrade"); - registerItem(registry, range_upgrade, "range_upgrade"); - registerItem(registry, duration_upgrade, "duration_upgrade"); - registerItem(registry, cooldown_upgrade, "cooldown_upgrade"); - registerItem(registry, blast_upgrade, "blast_upgrade"); - registerItem(registry, attunement_upgrade, "attunement_upgrade"); - - registerItem(registry, flaming_axe, "flaming_axe"); - registerItem(registry, frost_axe, "frost_axe"); - - registerItem(registry, firebomb, "firebomb"); - registerItem(registry, poison_bomb, "poison_bomb"); - - registerItem(registry, blank_scroll, "blank_scroll"); - registerItem(registry, scroll, "scroll"); - - registerItem(registry, armour_upgrade, "armour_upgrade"); - - registerItem(registry, magic_silk, "magic_silk"); - - registerItem(registry, wizard_hat, "wizard_hat"); - registerItem(registry, wizard_robe, "wizard_robe"); - registerItem(registry, wizard_leggings, "wizard_leggings"); - registerItem(registry, wizard_boots, "wizard_boots"); - - registerItem(registry, wizard_hat_fire, "wizard_hat_fire"); - registerItem(registry, wizard_robe_fire, "wizard_robe_fire"); - registerItem(registry, wizard_leggings_fire, "wizard_leggings_fire"); - registerItem(registry, wizard_boots_fire, "wizard_boots_fire"); - - registerItem(registry, wizard_hat_ice, "wizard_hat_ice"); - registerItem(registry, wizard_robe_ice, "wizard_robe_ice"); - registerItem(registry, wizard_leggings_ice, "wizard_leggings_ice"); - registerItem(registry, wizard_boots_ice, "wizard_boots_ice"); - - registerItem(registry, wizard_hat_lightning, "wizard_hat_lightning"); - registerItem(registry, wizard_robe_lightning, "wizard_robe_lightning"); - registerItem(registry, wizard_leggings_lightning, "wizard_leggings_lightning"); - registerItem(registry, wizard_boots_lightning, "wizard_boots_lightning"); - - registerItem(registry, wizard_hat_necromancy, "wizard_hat_necromancy"); - registerItem(registry, wizard_robe_necromancy, "wizard_robe_necromancy"); - registerItem(registry, wizard_leggings_necromancy, "wizard_leggings_necromancy"); - registerItem(registry, wizard_boots_necromancy, "wizard_boots_necromancy"); - - registerItem(registry, wizard_hat_earth, "wizard_hat_earth"); - registerItem(registry, wizard_robe_earth, "wizard_robe_earth"); - registerItem(registry, wizard_leggings_earth, "wizard_leggings_earth"); - registerItem(registry, wizard_boots_earth, "wizard_boots_earth"); - - registerItem(registry, wizard_hat_sorcery, "wizard_hat_sorcery"); - registerItem(registry, wizard_robe_sorcery, "wizard_robe_sorcery"); - registerItem(registry, wizard_leggings_sorcery, "wizard_leggings_sorcery"); - registerItem(registry, wizard_boots_sorcery, "wizard_boots_sorcery"); - - registerItem(registry, wizard_hat_healing, "wizard_hat_healing"); - registerItem(registry, wizard_robe_healing, "wizard_robe_healing"); - registerItem(registry, wizard_leggings_healing, "wizard_leggings_healing"); - registerItem(registry, wizard_boots_healing, "wizard_boots_healing"); - - registerItem(registry, spectral_helmet, "spectral_helmet"); - registerItem(registry, spectral_chestplate, "spectral_chestplate"); - registerItem(registry, spectral_leggings, "spectral_leggings"); - registerItem(registry, spectral_boots, "spectral_boots"); - - registerItem(registry, smoke_bomb, "smoke_bomb"); - - registerItem(registry, identification_scroll, "identification_scroll"); - } - -} \ No newline at end of file diff --git a/src/main/java/electroblob/wizardry/registry/WizardryPotions.java b/src/main/java/electroblob/wizardry/registry/WizardryPotions.java deleted file mode 100644 index 4b51a675..00000000 --- a/src/main/java/electroblob/wizardry/registry/WizardryPotions.java +++ /dev/null @@ -1,113 +0,0 @@ -package electroblob.wizardry.registry; - -import electroblob.wizardry.Wizardry; -import electroblob.wizardry.potion.PotionDecay; -import electroblob.wizardry.potion.PotionFrost; -import electroblob.wizardry.potion.PotionMagicEffect; -import electroblob.wizardry.potion.PotionMagicEffectParticles; -import electroblob.wizardry.util.WizardryParticleType; -import net.minecraft.entity.EntityLivingBase; -import net.minecraft.potion.Potion; -import net.minecraft.util.EnumParticleTypes; -import net.minecraft.world.World; -import net.minecraftforge.event.RegistryEvent; -import net.minecraftforge.fml.common.Mod; -import net.minecraftforge.fml.common.eventhandler.SubscribeEvent; -import net.minecraftforge.fml.common.registry.IForgeRegistry; - -/** - * Class responsible for defining, storing and registering all of wizardry's potion effects. - * @author Electroblob - * @since Wizardry 2.1 - */ -@Mod.EventBusSubscriber -public final class WizardryPotions { - - /* Interestingly, setting the colour to black stops the particles from rendering. This is great, however, the black - * colour then 'mixes' with other potions that are applied. Whilst this is a bit annoying, for the amount it is - * likely to get noticed it is certainly preferable to always making showParticles false, because now I can give - * the user the option (via commands) of having one of my potion effects without particles, and more importantly - * the potion effect HUD still gets displayed. TODO: Backport to 1.7.10, assuming it also works in that version. - * This means also changing whenever the potion effect is added such that it is NOT ambient. */ - - public static final Potion frost = new PotionFrost(true, 0); // Colour was 0x38ddec (was arbitrary anyway) - - public static final Potion transience = new PotionMagicEffectParticles(false, 0, 0){ - @Override - public void spawnCustomParticle(World world, double x, double y, double z){ - Wizardry.proxy.spawnParticle(WizardryParticleType.DUST, world, x, y, z, 0, 0, 0, (int)(16.0D / (Math.random() * 0.8D + 0.2D)), 0.8f, 0.8f, 1.0f); - } - }.setBeneficial(); //0xffe89b - - public static final Potion fireskin = new PotionMagicEffectParticles(false, 0, 1){ - @Override - public void spawnCustomParticle(World world, double x, double y, double z){ - world.spawnParticle(EnumParticleTypes.FLAME, x, y, z, 0, 0, 0); - } - @Override - public void performEffect(EntityLivingBase entitylivingbase, int strength){ - entitylivingbase.extinguish(); // Stops melee mobs that are on fire from setting the player on fire, - // without allowing the player to actually stand in fire or swim in lava without taking damage. - }; - }.setBeneficial(); //0xff2f02 - - public static final Potion ice_shroud = new PotionMagicEffectParticles(false, 0, 2){ - @Override - public void spawnCustomParticle(World world, double x, double y, double z){ - float brightness = 0.5f + (world.rand.nextFloat()/2); - Wizardry.proxy.spawnParticle(WizardryParticleType.SPARKLE, world, x, y, z, 0, 0, 0, 48 + world.rand.nextInt(12), brightness, brightness + 0.1f, 1.0f, true, 0); - Wizardry.proxy.spawnParticle(WizardryParticleType.SNOW, world, x, y, z, 0, -0.02, 0, 40 + world.rand.nextInt(10)); - } - }.setBeneficial(); //0x52f1ff - - public static final Potion static_aura = new PotionMagicEffectParticles(false, 0, 3){ - @Override - public void spawnCustomParticle(World world, double x, double y, double z){ - Wizardry.proxy.spawnParticle(WizardryParticleType.SPARK, world, x, y, z, 0, 0, 0, 3); - } - }.setBeneficial(); //0x0070ff - - public static final Potion decay = new PotionDecay(true, 0x3c006c); - public static final Potion sixth_sense = new PotionMagicEffect(false, 0xc6ff01, 4).setBeneficial(); - public static final Potion arcane_jammer = new PotionMagicEffect(false, 0xcf4aa2, 5); - public static final Potion mind_trick = new PotionMagicEffect(true, 0x601683, 6); - public static final Potion mind_control = new PotionMagicEffect(true, 0x320b44, 7); - public static final Potion font_of_mana = new PotionMagicEffect(false, 0xffe5bb, 8).setBeneficial(); - public static final Potion fear = new PotionMagicEffect(true, 0xbd0100, 9); - - /** - * Sets both the registry and unlocalised names of the given potion, then registers it with the given registry. Use - * this instead of {@link potion#setRegistryName(String)} and {@link potion#setUnlocalizedName(String)} during - * construction, for convenience and consistency. - * @param registry The registry to register the given potion to. - * @param potion The potion to register. - * @param name The name of the potion, without the mod ID or the .name stuff. The registry name will be - * {@code wizardry:[name]}. The unlocalised name will be {@code potion.wizardry:[name].name}. - */ - public static void registerPotion(IForgeRegistry registry, Potion potion, String name){ - potion.setRegistryName(Wizardry.MODID, name); - // For some reason, Potion#getName() doesn't prepend "potion." itself, so it has to be done here. - potion.setPotionName("potion." + potion.getRegistryName().toString()); - registry.register(potion); - } - - @SubscribeEvent - public static void register(RegistryEvent.Register event){ - - IForgeRegistry registry = event.getRegistry(); - - registerPotion(registry, frost, "frost"); - registerPotion(registry, transience, "transience"); - registerPotion(registry, fireskin, "fireskin"); - registerPotion(registry, ice_shroud, "ice_shroud"); - registerPotion(registry, static_aura, "static_aura"); - registerPotion(registry, decay, "decay"); - registerPotion(registry, sixth_sense, "sixth_sense"); - registerPotion(registry, arcane_jammer, "arcane_jammer"); - registerPotion(registry, mind_trick, "mind_trick"); - registerPotion(registry, mind_control, "mind_control"); - registerPotion(registry, font_of_mana, "font_of_mana"); - registerPotion(registry, fear, "fear"); - } - -} \ No newline at end of file diff --git a/src/main/java/electroblob/wizardry/registry/WizardryRegistry.java b/src/main/java/electroblob/wizardry/registry/WizardryRegistry.java deleted file mode 100644 index f8103bf1..00000000 --- a/src/main/java/electroblob/wizardry/registry/WizardryRegistry.java +++ /dev/null @@ -1,320 +0,0 @@ -package electroblob.wizardry.registry; - -import java.util.ArrayList; -import java.util.List; - -import electroblob.wizardry.Wizardry; -import electroblob.wizardry.constants.Element; -import electroblob.wizardry.constants.Tier; -import electroblob.wizardry.entity.EntityArc; -import electroblob.wizardry.entity.EntityMeteor; -import electroblob.wizardry.entity.EntityShield; -import electroblob.wizardry.entity.construct.EntityArrowRain; -import electroblob.wizardry.entity.construct.EntityBlackHole; -import electroblob.wizardry.entity.construct.EntityBlizzard; -import electroblob.wizardry.entity.construct.EntityBubble; -import electroblob.wizardry.entity.construct.EntityDecay; -import electroblob.wizardry.entity.construct.EntityEarthquake; -import electroblob.wizardry.entity.construct.EntityFireRing; -import electroblob.wizardry.entity.construct.EntityFireSigil; -import electroblob.wizardry.entity.construct.EntityForcefield; -import electroblob.wizardry.entity.construct.EntityFrostSigil; -import electroblob.wizardry.entity.construct.EntityHailstorm; -import electroblob.wizardry.entity.construct.EntityHammer; -import electroblob.wizardry.entity.construct.EntityHealAura; -import electroblob.wizardry.entity.construct.EntityIceSpike; -import electroblob.wizardry.entity.construct.EntityLightningPulse; -import electroblob.wizardry.entity.construct.EntityLightningSigil; -import electroblob.wizardry.entity.construct.EntityTornado; -import electroblob.wizardry.entity.living.EntityBlazeMinion; -import electroblob.wizardry.entity.living.EntityDecoy; -import electroblob.wizardry.entity.living.EntityEvilWizard; -import electroblob.wizardry.entity.living.EntityIceGiant; -import electroblob.wizardry.entity.living.EntityIceWraith; -import electroblob.wizardry.entity.living.EntityLightningWraith; -import electroblob.wizardry.entity.living.EntityMagicSlime; -import electroblob.wizardry.entity.living.EntityPhoenix; -import electroblob.wizardry.entity.living.EntityShadowWraith; -import electroblob.wizardry.entity.living.EntitySilverfishMinion; -import electroblob.wizardry.entity.living.EntitySkeletonMinion; -import electroblob.wizardry.entity.living.EntitySpiderMinion; -import electroblob.wizardry.entity.living.EntitySpiritHorse; -import electroblob.wizardry.entity.living.EntitySpiritWolf; -import electroblob.wizardry.entity.living.EntityStormElemental; -import electroblob.wizardry.entity.living.EntityWizard; -import electroblob.wizardry.entity.living.EntityZombieMinion; -import electroblob.wizardry.entity.projectile.EntityDarknessOrb; -import electroblob.wizardry.entity.projectile.EntityDart; -import electroblob.wizardry.entity.projectile.EntityFirebolt; -import electroblob.wizardry.entity.projectile.EntityFirebomb; -import electroblob.wizardry.entity.projectile.EntityForceArrow; -import electroblob.wizardry.entity.projectile.EntityForceOrb; -import electroblob.wizardry.entity.projectile.EntityIceCharge; -import electroblob.wizardry.entity.projectile.EntityIceLance; -import electroblob.wizardry.entity.projectile.EntityIceShard; -import electroblob.wizardry.entity.projectile.EntityLightningArrow; -import electroblob.wizardry.entity.projectile.EntityLightningDisc; -import electroblob.wizardry.entity.projectile.EntityMagicMissile; -import electroblob.wizardry.entity.projectile.EntityPoisonBomb; -import electroblob.wizardry.entity.projectile.EntitySmokeBomb; -import electroblob.wizardry.entity.projectile.EntitySpark; -import electroblob.wizardry.entity.projectile.EntitySparkBomb; -import electroblob.wizardry.entity.projectile.EntityThunderbolt; -import electroblob.wizardry.loot.RandomSpell; -import electroblob.wizardry.loot.WizardSpell; -import electroblob.wizardry.tileentity.TileEntityArcaneWorkbench; -import electroblob.wizardry.tileentity.TileEntityMagicLight; -import electroblob.wizardry.tileentity.TileEntityPlayerSave; -import electroblob.wizardry.tileentity.TileEntityStatue; -import electroblob.wizardry.tileentity.TileEntityTimer; -import electroblob.wizardry.util.WizardryUtilities; -import net.minecraft.entity.EnumCreatureType; -import net.minecraft.init.Biomes; -import net.minecraft.init.Blocks; -import net.minecraft.init.Items; -import net.minecraft.inventory.EntityEquipmentSlot; -import net.minecraft.item.ItemStack; -import net.minecraft.util.ResourceLocation; -import net.minecraft.world.biome.Biome; -import net.minecraft.world.storage.loot.LootTableList; -import net.minecraft.world.storage.loot.functions.LootFunctionManager; -import net.minecraftforge.fml.common.registry.EntityRegistry; -import net.minecraftforge.fml.common.registry.GameRegistry; -import net.minecraftforge.oredict.OreDictionary; - -/** - * Class responsible for registering all the things that don't have (or need) instances: entities, loot tables, - * recipes, etc. - * @author Electroblob - * @since Wizardry 1.0 - */ -public final class WizardryRegistry { - - // NOTE: In 1.12, recipes have a registry (they can still stay here though since we don't keep references to them) - - /** Called from the preInit method in the main mod class to register the custom dungeon loot. */ - public static void registerLoot(){ - - /* Loot tables work as follows: - * Minecraft goes through each pool in turn. For each pool, it does a certain number or rolls, which can either - * be set to always be one number or a random number from a range. Each roll, it generates one stack of a single - * random entry in that pool, weighted according to the weights of the entries. Functions allow properties of - * that stack (stack size, damage, nbt) to be set, and even allow it to be replaced dynamically with a - * completely different item (though there's very little point in doing that as it could be achieved just as - * easily with more entries, which makes me think it would be bad practice). - * You can also use conditions to control whether an entry or pool is used at all, which is mostly for mob drops - * under specific conditions, but one of them is simply a random chance, meaning you could use it to make a pool - * that only gets rolled sometimes. - * All in all, this can get rather confusing, because stackable items can have 5 stages of randomness applied - * to them at once: a random chance for the pool, a random number of rolls for the pool, the weighted random - * chance of choosing that particular entry, a random chance for that entry, and a random stack size, and that's - * before you take functions into account. - * - * ...oh, and entries can be entire loot tables in themselves, allowing for potentially infinite levels of - * randomness. Yeah. - * - * Translating to the new system: - * ChestGenHooks.SOME_NAME -> loot table json file - * ??? -> loot pool (I don't think it was split up like this before) - * ChestGenHooks.addItem() -> entry in a loot pool - * Stack sizes in WeightedRandomChestContent -> set_count function for entries - * Weight in WeightedRandomChestContent -> weight of entries - * Custom WeightedRandomChestContent implementations -> custom loot functions, but only for complex/dynamic - * stuff - things that serve to allow the chance for a category of items (like armour) to be specified but still - * have a random chance for which exact item you get should be done with nested loot tables. - */ - - // Always registers the loot tables, but only injects the additions into vanilla if the appropriate option is - // enabled in the config (see WizardryEventHandler). - LootFunctionManager.registerFunction(new RandomSpell.Serializer()); - LootFunctionManager.registerFunction(new WizardSpell.Serializer()); - LootTableList.register(new ResourceLocation(Wizardry.MODID, "chests/wizard_tower")); - LootTableList.register(new ResourceLocation(Wizardry.MODID, "chests/dungeon_additions")); - LootTableList.register(new ResourceLocation(Wizardry.MODID, "subsets/novice_wands")); - LootTableList.register(new ResourceLocation(Wizardry.MODID, "subsets/wizard_armour")); - LootTableList.register(new ResourceLocation(Wizardry.MODID, "subsets/arcane_tomes")); - LootTableList.register(new ResourceLocation(Wizardry.MODID, "subsets/wand_upgrades")); - LootTableList.register(new ResourceLocation(Wizardry.MODID, "entities/evil_wizard")); - // TODO: At the moment this is not used anywhere because I can't find a way to add it to all mobs. - //LootTableList.register(new ResourceLocation(Wizardry.MODID, "entities/mob_additions")); - - } - - /** Called from the preInit method in the main mod class to register all the tile entities. */ - public static void registerTileEntities(){ - - GameRegistry.registerTileEntity(TileEntityArcaneWorkbench.class, Wizardry.MODID + "ArcaneWorkbenchTileEntity"); - GameRegistry.registerTileEntity(TileEntityStatue.class, Wizardry.MODID + "PetrifiedStoneTileEntity"); - GameRegistry.registerTileEntity(TileEntityMagicLight.class, Wizardry.MODID + "MagicLightTileEntity"); - GameRegistry.registerTileEntity(TileEntityTimer.class, Wizardry.MODID + "TimerTileEntity"); - GameRegistry.registerTileEntity(TileEntityPlayerSave.class, Wizardry.MODID + "TileEntityPlayerSave"); - } - - /** Not actually the frequency at all; smaller numbers are more frequent. Vanilla uses 3 I think. */ - private static final int LIVING_UPDATE_INTERVAL = 3; - /** Not actually the frequency at all; smaller numbers are more frequent. */ - private static final int PROJECTILE_UPDATE_INTERVAL = 10; - - /** Called from the preInit method in the main mod class to register all the entities. */ - public static void registerEntities(Wizardry wizardry){ - - int id = 0; // Incrementable index for the mod specific entity id. - - EntityRegistry.registerModEntity(EntityZombieMinion.class, "zombie_minion", id++, wizardry, 128, LIVING_UPDATE_INTERVAL, true); - EntityRegistry.registerModEntity(EntityMagicMissile.class, "magic_missile", id++, wizardry, 128, PROJECTILE_UPDATE_INTERVAL, true); - // TODO: This should be a particle - EntityRegistry.registerModEntity(EntityArc.class, "arc", id++, wizardry, 128, PROJECTILE_UPDATE_INTERVAL, false); - EntityRegistry.registerModEntity(EntitySkeletonMinion.class, "skeleton_minion", id++, wizardry, 128, LIVING_UPDATE_INTERVAL, true); - EntityRegistry.registerModEntity(EntitySparkBomb.class, "spark_bomb", id++, wizardry, 128, PROJECTILE_UPDATE_INTERVAL, true); - EntityRegistry.registerModEntity(EntitySpiritWolf.class, "spirit_wolf", id++, wizardry, 128, LIVING_UPDATE_INTERVAL, true); - EntityRegistry.registerModEntity(EntityIceShard.class, "ice_shard", id++, wizardry, 128, PROJECTILE_UPDATE_INTERVAL, true); - EntityRegistry.registerModEntity(EntityBlazeMinion.class, "blaze_minion", id++, wizardry, 128, LIVING_UPDATE_INTERVAL, true); - EntityRegistry.registerModEntity(EntityIceWraith.class, "ice_wraith", id++, wizardry, 128, LIVING_UPDATE_INTERVAL, true); - EntityRegistry.registerModEntity(EntityLightningWraith.class, "lightning_wraith", id++, wizardry, 128, LIVING_UPDATE_INTERVAL, true); - EntityRegistry.registerModEntity(EntityBlackHole.class, "black_hole", id++, wizardry, 128, PROJECTILE_UPDATE_INTERVAL, false); - EntityRegistry.registerModEntity(EntityShield.class, "shield", id++, wizardry, 128, 1, true); - EntityRegistry.registerModEntity(EntityMeteor.class, "meteor", id++, wizardry, 128, 5, true); - EntityRegistry.registerModEntity(EntityBlizzard.class, "blizzard", id++, wizardry, 128, PROJECTILE_UPDATE_INTERVAL, false); - EntityRegistry.registerModEntity(EntityWizard.class, "wizard", id++, wizardry, 128, LIVING_UPDATE_INTERVAL, true); - EntityRegistry.registerModEntity(EntityBubble.class, "bubble", id++, wizardry, 128, 3, false); - EntityRegistry.registerModEntity(EntityTornado.class, "tornado", id++, wizardry, 128, 1, false); - EntityRegistry.registerModEntity(EntityHammer.class, "lightning_hammer", id++, wizardry, 128, 1, true); - EntityRegistry.registerModEntity(EntityFirebomb.class, "firebomb", id++, wizardry, 128, PROJECTILE_UPDATE_INTERVAL, true); - EntityRegistry.registerModEntity(EntityForceOrb.class, "force_orb", id++, wizardry, 128, PROJECTILE_UPDATE_INTERVAL, true); - EntityRegistry.registerModEntity(EntityArrowRain.class, "arrow_rain", id++, wizardry, 128, PROJECTILE_UPDATE_INTERVAL, false); - EntityRegistry.registerModEntity(EntitySpark.class, "spark", id++, wizardry, 128, PROJECTILE_UPDATE_INTERVAL, true); - EntityRegistry.registerModEntity(EntityShadowWraith.class, "shadow_wraith", id++, wizardry, 128, PROJECTILE_UPDATE_INTERVAL, true); - EntityRegistry.registerModEntity(EntityDarknessOrb.class, "darkness_orb", id++, wizardry, 128, PROJECTILE_UPDATE_INTERVAL, true); - EntityRegistry.registerModEntity(EntitySpiderMinion.class, "spider_minion", id++, wizardry, 128, LIVING_UPDATE_INTERVAL, true); - EntityRegistry.registerModEntity(EntityHealAura.class, "healing_aura", id++, wizardry, 128, PROJECTILE_UPDATE_INTERVAL, false); - EntityRegistry.registerModEntity(EntityFireSigil.class, "fire_sigil", id++, wizardry, 128, PROJECTILE_UPDATE_INTERVAL, false); - EntityRegistry.registerModEntity(EntityFrostSigil.class, "frost_sigil", id++, wizardry, 128, PROJECTILE_UPDATE_INTERVAL, false); - EntityRegistry.registerModEntity(EntityLightningSigil.class, "lightning_sigil", id++, wizardry, 128, PROJECTILE_UPDATE_INTERVAL, false); - EntityRegistry.registerModEntity(EntityLightningArrow.class, "lightning_arrow", id++, wizardry, 128, PROJECTILE_UPDATE_INTERVAL, true); - EntityRegistry.registerModEntity(EntityFirebolt.class, "firebolt", id++, wizardry, 128, PROJECTILE_UPDATE_INTERVAL, true); - EntityRegistry.registerModEntity(EntityPoisonBomb.class, "poison_bomb", id++, wizardry, 128, PROJECTILE_UPDATE_INTERVAL, true); - EntityRegistry.registerModEntity(EntityIceCharge.class, "ice_charge", id++, wizardry, 128, PROJECTILE_UPDATE_INTERVAL, true); - EntityRegistry.registerModEntity(EntityForceArrow.class, "force_arrow", id++, wizardry, 128, PROJECTILE_UPDATE_INTERVAL, true); - EntityRegistry.registerModEntity(EntityDart.class, "dart", id++, wizardry, 128, PROJECTILE_UPDATE_INTERVAL, true); - EntityRegistry.registerModEntity(EntityMagicSlime.class, "magic_slime", id++, wizardry, 128, LIVING_UPDATE_INTERVAL, true); - EntityRegistry.registerModEntity(EntityForcefield.class, "forcefield", id++, wizardry, 128, PROJECTILE_UPDATE_INTERVAL, false); - EntityRegistry.registerModEntity(EntityFireRing.class, "ring_of_fire", id++, wizardry, 128, PROJECTILE_UPDATE_INTERVAL, false); - EntityRegistry.registerModEntity(EntityLightningDisc.class, "lightning_disc", id++, wizardry, 128, PROJECTILE_UPDATE_INTERVAL, true); - EntityRegistry.registerModEntity(EntityThunderbolt.class, "thunderbolt", id++, wizardry, 128, PROJECTILE_UPDATE_INTERVAL, true); - EntityRegistry.registerModEntity(EntityIceGiant.class, "ice_giant", id++, wizardry, 128, LIVING_UPDATE_INTERVAL, true); - EntityRegistry.registerModEntity(EntitySpiritHorse.class, "spirit_horse", id++, wizardry, 128, LIVING_UPDATE_INTERVAL, true); - EntityRegistry.registerModEntity(EntityPhoenix.class, "phoenix", id++, wizardry, 128, LIVING_UPDATE_INTERVAL, true); - EntityRegistry.registerModEntity(EntitySilverfishMinion.class, "silverfish_minion", id++, wizardry, 128, LIVING_UPDATE_INTERVAL, true); - EntityRegistry.registerModEntity(EntityDecay.class, "decay", id++, wizardry, 128, PROJECTILE_UPDATE_INTERVAL, false); - EntityRegistry.registerModEntity(EntityStormElemental.class, "storm_elemental", id++, wizardry, 128, PROJECTILE_UPDATE_INTERVAL, true); - EntityRegistry.registerModEntity(EntityEarthquake.class, "earthquake", id++, wizardry, 128, PROJECTILE_UPDATE_INTERVAL, false); - EntityRegistry.registerModEntity(EntityIceLance.class, "ice_lance", id++, wizardry, 128, PROJECTILE_UPDATE_INTERVAL, true); - EntityRegistry.registerModEntity(EntityHailstorm.class, "hailstorm", id++, wizardry, 128, PROJECTILE_UPDATE_INTERVAL, false); - EntityRegistry.registerModEntity(EntitySmokeBomb.class, "smoke_bomb", id++, wizardry, 128, PROJECTILE_UPDATE_INTERVAL, true); - EntityRegistry.registerModEntity(EntityEvilWizard.class, "evil_wizard", id++, wizardry, 128, LIVING_UPDATE_INTERVAL, true); - EntityRegistry.registerModEntity(EntityDecoy.class, "decoy", id++, wizardry, 128, LIVING_UPDATE_INTERVAL, true); - EntityRegistry.registerModEntity(EntityIceSpike.class, "ice_spike", id++, wizardry, 128, 1, true); - // TODO: This should be a particle. - EntityRegistry.registerModEntity(EntityLightningPulse.class, "lightning_pulse", id++, wizardry, 128, PROJECTILE_UPDATE_INTERVAL, false); - - EntityRegistry.registerEgg(EntityWizard.class, 0x19295e, 0xee9312); - EntityRegistry.registerEgg(EntityEvilWizard.class, 0x290404, 0xee9312); - - // TODO: May need fixing - List biomes = new ArrayList(); - for(Biome biome : Biome.EXPLORATION_BIOMES_LIST){ - if(biome != null){ - biomes.add(biome); - } - } - biomes.remove(Biomes.MUSHROOM_ISLAND); - biomes.remove(Biomes.MUSHROOM_ISLAND_SHORE); - // For reference: 5, 1, 1 are the parameters for the witch in vanilla. - EntityRegistry.addSpawn(EntityEvilWizard.class, 3, 1, 1, EnumCreatureType.MONSTER, biomes.toArray(new Biome[biomes.size()])); - - } - - /** Called from the init method in the main mod class to register all the recipes. */ - public static void registerRecipes(){ - - ItemStack magicCrystalStack = new ItemStack(WizardryItems.magic_crystal); - ItemStack magicWandStack = new ItemStack(WizardryItems.magic_wand, 1, Tier.BASIC.maxCharge); - ItemStack goldNuggetStack = new ItemStack(Items.GOLD_NUGGET); - ItemStack stickStack = new ItemStack(Items.STICK); - ItemStack bookStack = new ItemStack(Items.BOOK); - ItemStack stringStack = new ItemStack(Items.STRING); - ItemStack spellBookStack = new ItemStack(WizardryItems.spell_book, 1, Spells.magic_missile.id()); - ItemStack arcaneWorkbenchStack = new ItemStack(WizardryBlocks.arcane_workbench); - ItemStack stoneStack = new ItemStack(Blocks.STONE); - ItemStack lapisBlockStack = new ItemStack(Blocks.LAPIS_BLOCK); - ItemStack purpleCarpetStack = new ItemStack(Blocks.CARPET, 1, 10); - ItemStack wizardHandbookStack = new ItemStack(WizardryItems.wizard_handbook); - ItemStack crystalFlowerStack = new ItemStack(WizardryBlocks.crystal_flower); - ItemStack magicCrystalStack1 = new ItemStack(WizardryItems.magic_crystal, 2); - ItemStack magicCrystalStack2 = new ItemStack(WizardryItems.magic_crystal, 9); - ItemStack crystalBlockStack = new ItemStack(WizardryBlocks.crystal_block); - ItemStack manaFlaskStack = new ItemStack(WizardryItems.mana_flask); - ItemStack bottleStack = new ItemStack(Items.GLASS_BOTTLE); - ItemStack gunpowderStack = new ItemStack(Items.GUNPOWDER); - ItemStack blazePowderStack = new ItemStack(Items.BLAZE_POWDER); - ItemStack spiderEyeStack = new ItemStack(Items.SPIDER_EYE); - // Coal or charcoal is equally fine, hence the wildcard value - ItemStack coalStack = new ItemStack(Items.COAL, 1, OreDictionary.WILDCARD_VALUE); - ItemStack firebombStack = new ItemStack(WizardryItems.firebomb, 3); - ItemStack poisonBombStack = new ItemStack(WizardryItems.poison_bomb, 3); - ItemStack smokeBombStack = new ItemStack(WizardryItems.smoke_bomb, 3); - ItemStack transportationStoneStack = new ItemStack(WizardryBlocks.transportation_stone, 2); - ItemStack silkStack = new ItemStack(WizardryItems.magic_silk); - ItemStack silkStack1 = new ItemStack(WizardryItems.magic_silk, 2); - ItemStack hatStack = new ItemStack(WizardryItems.wizard_hat); - ItemStack robeStack = new ItemStack(WizardryItems.wizard_robe); - ItemStack leggingsStack = new ItemStack(WizardryItems.wizard_leggings); - ItemStack bootsStack = new ItemStack(WizardryItems.wizard_boots); - ItemStack scrollStack = new ItemStack(WizardryItems.blank_scroll); - ItemStack paperStack = new ItemStack(Items.PAPER); - - GameRegistry.addRecipe(magicWandStack, " x", " y ", "z ", 'x', magicCrystalStack, 'y', stickStack, 'z', goldNuggetStack); - GameRegistry.addRecipe(spellBookStack, " x ", "xyx", " x ", 'x', magicCrystalStack, 'y', bookStack); - GameRegistry.addRecipe(arcaneWorkbenchStack, "vwv", "xyx", "zzz", 'v', goldNuggetStack, 'w', purpleCarpetStack, 'x', magicCrystalStack, 'y', lapisBlockStack, 'z', stoneStack); - GameRegistry.addRecipe(manaFlaskStack, "yyy", "yxy", "yyy", 'x', bottleStack, 'y', magicCrystalStack); - GameRegistry.addRecipe(transportationStoneStack, " x ", "xyx", " x ", 'x', stoneStack, 'y', magicCrystalStack); - GameRegistry.addRecipe(hatStack, "yyy", "y y", 'y', silkStack); - GameRegistry.addRecipe(robeStack, "y y", "yyy", "yyy", 'y', silkStack); - GameRegistry.addRecipe(leggingsStack, "yyy", "y y", "y y", 'y', silkStack); - GameRegistry.addRecipe(bootsStack, "y y", "y y", 'y', silkStack); - GameRegistry.addRecipe(silkStack1, " x ", "xyx", " x ", 'x', stringStack, 'y', magicCrystalStack); - GameRegistry.addRecipe(crystalBlockStack, "zzz", "zzz", "zzz", 'z', magicCrystalStack); - - GameRegistry.addShapelessRecipe(wizardHandbookStack, bookStack, magicCrystalStack); - GameRegistry.addShapelessRecipe(magicCrystalStack1, crystalFlowerStack); - GameRegistry.addShapelessRecipe(magicCrystalStack2, crystalBlockStack); - if(Wizardry.settings.firebombIsCraftable) GameRegistry.addShapelessRecipe(firebombStack, bottleStack, gunpowderStack, blazePowderStack, blazePowderStack); - if(Wizardry.settings.poisonBombIsCraftable) GameRegistry.addShapelessRecipe(poisonBombStack, bottleStack, gunpowderStack, spiderEyeStack, spiderEyeStack); - if(Wizardry.settings.smokeBombIsCraftable) GameRegistry.addShapelessRecipe(smokeBombStack, bottleStack, gunpowderStack, coalStack, coalStack); - if(Wizardry.settings.useAlternateScrollRecipe){ - GameRegistry.addShapelessRecipe(scrollStack, paperStack, stringStack, magicCrystalStack); - }else{ - GameRegistry.addShapelessRecipe(scrollStack, paperStack, stringStack); - } - - // Mana flask recipes - ItemStack miscWandStack; - - for(Element element : Element.values()){ - for(Tier tier : Tier.values()){ - miscWandStack = new ItemStack(WizardryUtilities.getWand(tier, element), 1, OreDictionary.WILDCARD_VALUE); - GameRegistry.addShapelessRecipe(miscWandStack, miscWandStack, manaFlaskStack); - } - } - - ItemStack miscArmourStack; - - for(Element element : Element.values()){ - for(EntityEquipmentSlot slot : WizardryUtilities.ARMOUR_SLOTS){ - miscArmourStack = new ItemStack(WizardryUtilities.getArmour(element, slot), 1, OreDictionary.WILDCARD_VALUE); - GameRegistry.addShapelessRecipe(miscArmourStack, miscArmourStack, manaFlaskStack); - } - } - } - -} diff --git a/src/main/java/electroblob/wizardry/registry/WizardrySounds.java b/src/main/java/electroblob/wizardry/registry/WizardrySounds.java deleted file mode 100644 index d006add1..00000000 --- a/src/main/java/electroblob/wizardry/registry/WizardrySounds.java +++ /dev/null @@ -1,68 +0,0 @@ -package electroblob.wizardry.registry; - -import electroblob.wizardry.Wizardry; -import net.minecraft.util.ResourceLocation; -import net.minecraft.util.SoundEvent; -import net.minecraftforge.event.RegistryEvent; -import net.minecraftforge.fml.common.Mod; -import net.minecraftforge.fml.common.eventhandler.SubscribeEvent; - -/** - * Class responsible for defining, storing and registering all of wizardry's sound events. For some reason, these - * worked in the beta versions despite not being registered... - * @author Electroblob - * @since Wizardry 2.1 - */ -@Mod.EventBusSubscriber -public final class WizardrySounds { - - // Anything with LOOP in its name is intended to be played in a continuous loop. - public static final SoundEvent SPELL_SPARK = createSound("arc"); - public static final SoundEvent SPELL_CONJURATION = createSound("aura"); - public static final SoundEvent SPELL_SHOCKWAVE = createSound("boom"); - public static final SoundEvent SPELL_LOOP_CRACKLE = createSound("crackle"); - public static final SoundEvent SPELL_SUMMONING = createSound("darkaura"); - public static final SoundEvent SPELL_DEFLECTION = createSound("effect"); - public static final SoundEvent SPELL_LIGHTNING = createSound("electricitya"); - public static final SoundEvent SPELL_LOOP_LIGHTNING = createSound("electricityb"); - public static final SoundEvent SPELL_LOOP_FIRE = createSound("flameray"); - public static final SoundEvent SPELL_FREEZE = createSound("freeze"); - public static final SoundEvent SPELL_LOOP_ICE = createSound("frostray"); - public static final SoundEvent SPELL_HEAL = createSound("heal"); - public static final SoundEvent SPELL_ICE = createSound("ice"); - public static final SoundEvent SPELL_CONJURATION_LARGE = createSound("largeaura"); - public static final SoundEvent SPELL_MAGIC = createSound("magic"); - public static final SoundEvent SPELL_LOOP_SPARKLE = createSound("sparkle"); - public static final SoundEvent SPELL_LOOP_WIND = createSound("wind"); - public static final SoundEvent SPELL_EARTHQUAKE = createSound("rumble"); - public static final SoundEvent SPELL_FORCE = createSound("force"); - - /** Trick borrowed from the Twilight Forest, makes things neater. */ - public static SoundEvent createSound(String name){ - // 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); - } - - @SubscribeEvent - public static void register(RegistryEvent.Register event){ - event.getRegistry().register(SPELL_SPARK); - event.getRegistry().register(SPELL_CONJURATION); - event.getRegistry().register(SPELL_SHOCKWAVE); - event.getRegistry().register(SPELL_LOOP_CRACKLE); - event.getRegistry().register(SPELL_SUMMONING); - event.getRegistry().register(SPELL_DEFLECTION); - event.getRegistry().register(SPELL_LIGHTNING); - event.getRegistry().register(SPELL_LOOP_LIGHTNING); - event.getRegistry().register(SPELL_LOOP_FIRE); - event.getRegistry().register(SPELL_FREEZE); - event.getRegistry().register(SPELL_LOOP_ICE); - event.getRegistry().register(SPELL_HEAL); - event.getRegistry().register(SPELL_ICE); - event.getRegistry().register(SPELL_CONJURATION_LARGE); - event.getRegistry().register(SPELL_MAGIC); - event.getRegistry().register(SPELL_LOOP_SPARKLE); - event.getRegistry().register(SPELL_LOOP_WIND); - event.getRegistry().register(SPELL_EARTHQUAKE); - event.getRegistry().register(SPELL_FORCE); - } -} \ No newline at end of file diff --git a/src/main/java/electroblob/wizardry/registry/WizardryTabs.java b/src/main/java/electroblob/wizardry/registry/WizardryTabs.java deleted file mode 100644 index 6eeb9fc6..00000000 --- a/src/main/java/electroblob/wizardry/registry/WizardryTabs.java +++ /dev/null @@ -1,175 +0,0 @@ -package electroblob.wizardry.registry; - -import java.util.Arrays; -import java.util.Collections; -import java.util.Comparator; -import java.util.List; - -import com.google.common.base.Function; -import com.google.common.collect.Ordering; - -import electroblob.wizardry.item.ItemScroll; -import electroblob.wizardry.item.ItemSpellBook; -import electroblob.wizardry.spell.Spell; -import net.minecraft.creativetab.CreativeTabs; -import net.minecraft.item.Item; -import net.minecraft.item.ItemStack; -import net.minecraftforge.fml.relauncher.Side; -import net.minecraftforge.fml.relauncher.SideOnly; - -/** - * Class responsible for defining and storing all of wizardry's creative tabs. Also handles sorting of the items. - * @author Electroblob - * @since Wizardry 2.1 - */ -public final class WizardryTabs { - - private static Comparator itemSorter; - private static Comparator spellItemSorter; - - // Creative Tabs - public static final CreativeTabs WIZARDRY = new CreativeTabs("wizardry"){ - - @Override - @SideOnly(Side.CLIENT) - public Item getTabIconItem() { - return WizardryItems.wizard_handbook; - } - - @Override - @SideOnly(Side.CLIENT) - public void displayAllRelevantItems(List items) { - super.displayAllRelevantItems(items); - Collections.sort(items, itemSorter); - } - }; - public static final CreativeTabs SPELLS = new CreativeTabs("wizardryspells"){ - - @Override - @SideOnly(Side.CLIENT) - public Item getTabIconItem() { - return WizardryItems.spell_book; - } - - @Override - @SideOnly(Side.CLIENT) - public void displayAllRelevantItems(List items) { - super.displayAllRelevantItems(items); - Collections.sort(items, spellItemSorter); - } - - @Override - public boolean hasSearchBar() { - return true; - } - - @Override - @SideOnly(Side.CLIENT) - public String getBackgroundImageName() { - return "item_search.png"; - } - }; - - /** Initialises the item sorters for the creative tabs. */ - public static void sort(){ - - List orderedItemList = Arrays.asList( - - Item.getItemFromBlock(WizardryBlocks.arcane_workbench), - Item.getItemFromBlock(WizardryBlocks.crystal_ore), - Item.getItemFromBlock(WizardryBlocks.crystal_block), - Item.getItemFromBlock(WizardryBlocks.crystal_flower), - Item.getItemFromBlock(WizardryBlocks.transportation_stone), - WizardryItems.magic_crystal, - WizardryItems.magic_wand, - WizardryItems.apprentice_wand, - WizardryItems.advanced_wand, - WizardryItems.master_wand, - WizardryItems.arcane_tome, - WizardryItems.wizard_handbook, - WizardryItems.basic_fire_wand, - WizardryItems.basic_ice_wand, - WizardryItems.basic_lightning_wand, - WizardryItems.basic_necromancy_wand, - WizardryItems.basic_earth_wand, - WizardryItems.basic_sorcery_wand, - WizardryItems.basic_healing_wand, - WizardryItems.smoke_bomb, - WizardryItems.firebomb, - WizardryItems.poison_bomb, - WizardryItems.blank_scroll, - WizardryItems.identification_scroll, - WizardryItems.mana_flask, - WizardryItems.storage_upgrade, - WizardryItems.siphon_upgrade, - WizardryItems.condenser_upgrade, - WizardryItems.range_upgrade, - WizardryItems.duration_upgrade, - WizardryItems.cooldown_upgrade, - WizardryItems.blast_upgrade, - WizardryItems.attunement_upgrade, - WizardryItems.magic_silk, - WizardryItems.armour_upgrade, - WizardryItems.wizard_hat, - WizardryItems.wizard_robe, - WizardryItems.wizard_leggings, - WizardryItems.wizard_boots, - WizardryItems.wizard_hat_fire, - WizardryItems.wizard_robe_fire, - WizardryItems.wizard_leggings_fire, - WizardryItems.wizard_boots_fire, - WizardryItems.wizard_hat_ice, - WizardryItems.wizard_robe_ice, - WizardryItems.wizard_leggings_ice, - WizardryItems.wizard_boots_ice, - WizardryItems.wizard_hat_lightning, - WizardryItems.wizard_robe_lightning, - WizardryItems.wizard_leggings_lightning, - WizardryItems.wizard_boots_lightning, - WizardryItems.wizard_hat_necromancy, - WizardryItems.wizard_robe_necromancy, - WizardryItems.wizard_leggings_necromancy, - WizardryItems.wizard_boots_necromancy, - WizardryItems.wizard_hat_earth, - WizardryItems.wizard_robe_earth, - WizardryItems.wizard_leggings_earth, - WizardryItems.wizard_boots_earth, - WizardryItems.wizard_hat_sorcery, - WizardryItems.wizard_robe_sorcery, - WizardryItems.wizard_leggings_sorcery, - WizardryItems.wizard_boots_sorcery, - WizardryItems.wizard_hat_healing, - WizardryItems.wizard_robe_healing, - WizardryItems.wizard_leggings_healing, - WizardryItems.wizard_boots_healing); - - itemSorter = Ordering.explicit(orderedItemList).onResultOf(new Function() { - @Override - public Item apply(ItemStack input) { - return input.getItem(); - } - }); - - spellItemSorter = new Comparator(){ - @Override - public int compare(ItemStack stack1, ItemStack stack2) { - - if((stack1.getItem() instanceof ItemSpellBook && stack2.getItem() instanceof ItemSpellBook) - || (stack1.getItem() instanceof ItemScroll && stack2.getItem() instanceof ItemScroll)){ - - Spell spell1 = Spell.get(stack1.getItemDamage()); - Spell spell2 = Spell.get(stack2.getItemDamage()); - - return spell1.compareTo(spell2); - - }else if(stack1.getItem() instanceof ItemScroll){ - return 1; - }else if(stack2.getItem() instanceof ItemScroll){ - return -1; - } - return 0; - } - }; - } - -} diff --git a/src/main/java/electroblob/wizardry/spell/Agility.java b/src/main/java/electroblob/wizardry/spell/Agility.java index db01f4a4..b1d7de76 100644 --- a/src/main/java/electroblob/wizardry/spell/Agility.java +++ b/src/main/java/electroblob/wizardry/spell/Agility.java @@ -1,44 +1,39 @@ package electroblob.wizardry.spell; +import electroblob.wizardry.EnumElement; +import electroblob.wizardry.EnumParticleType; +import electroblob.wizardry.EnumSpellType; +import electroblob.wizardry.EnumTier; import electroblob.wizardry.Wizardry; -import electroblob.wizardry.constants.Element; -import electroblob.wizardry.constants.SpellType; -import electroblob.wizardry.constants.Tier; -import electroblob.wizardry.registry.WizardryItems; -import electroblob.wizardry.registry.WizardrySounds; -import electroblob.wizardry.util.SpellModifiers; -import electroblob.wizardry.util.WizardryParticleType; -import electroblob.wizardry.util.WizardryUtilities; +import electroblob.wizardry.WizardryUtilities; import net.minecraft.entity.player.EntityPlayer; -import net.minecraft.init.MobEffects; import net.minecraft.item.EnumAction; +import net.minecraft.potion.Potion; import net.minecraft.potion.PotionEffect; -import net.minecraft.util.EnumHand; import net.minecraft.world.World; public class Agility extends Spell { public Agility() { - super(Tier.APPRENTICE, 20, Element.SORCERY, "agility", SpellType.UTILITY, 40, EnumAction.BOW, false); + super(EnumTier.APPRENTICE, 20, EnumElement.SORCERY, "agility", EnumSpellType.UTILITY, 40, EnumAction.bow, false); } @Override - public boolean cast(World world, EntityPlayer caster, EnumHand hand, int ticksInUse, SpellModifiers modifiers) { + public boolean cast(World world, EntityPlayer caster, int ticksInUse, float damageMultiplier, float rangeMultiplier, float durationMultiplier, float blastMultiplier) { - // 1.10 allows the particles to be completely hidden. - caster.addPotionEffect(new PotionEffect(MobEffects.SPEED, (int)(600*modifiers.get(WizardryItems.duration_upgrade)), 1, false, false)); - caster.addPotionEffect(new PotionEffect(MobEffects.JUMP_BOOST, (int)(600*modifiers.get(WizardryItems.duration_upgrade)), 1, false, false)); + caster.addPotionEffect(new PotionEffect(Potion.moveSpeed.id, (int)(600*durationMultiplier), 1, true)); + caster.addPotionEffect(new PotionEffect(Potion.jump.id, (int)(600*durationMultiplier), 1, true)); for(int i=0; i<10; i++){ double x1 = (double)((float)caster.posX + world.rand.nextFloat()*2 - 1.0F); double y1 = (double)((float)WizardryUtilities.getPlayerEyesPos(caster) - 0.5F + world.rand.nextFloat()); double z1 = (double)((float)caster.posZ + world.rand.nextFloat()*2 - 1.0F); if(world.isRemote){ - Wizardry.proxy.spawnParticle(WizardryParticleType.SPARKLE, world, x1, y1, z1, 0, 0.1F, 0, 48 + world.rand.nextInt(12), 0.6f, 0.6f, 1.0f); + Wizardry.proxy.spawnParticle(EnumParticleType.SPARKLE, world, x1, y1, z1, 0, 0.1F, 0, 48 + world.rand.nextInt(12), 0.6f, 0.6f, 1.0f); } } - WizardryUtilities.playSoundAtPlayer(caster, WizardrySounds.SPELL_HEAL, 0.7F, world.rand.nextFloat() * 0.4F + 1.0F); + world.playSoundAtEntity(caster, "wizardry:heal", 0.7F, world.rand.nextFloat() * 0.4F + 1.0F); return true; } diff --git a/src/main/java/electroblob/wizardry/spell/Arc.java b/src/main/java/electroblob/wizardry/spell/Arc.java index 372911d4..9f9dceed 100644 --- a/src/main/java/electroblob/wizardry/spell/Arc.java +++ b/src/main/java/electroblob/wizardry/spell/Arc.java @@ -1,26 +1,24 @@ package electroblob.wizardry.spell; +import electroblob.wizardry.EnumElement; +import electroblob.wizardry.EnumParticleType; +import electroblob.wizardry.EnumSpellType; +import electroblob.wizardry.EnumTier; +import electroblob.wizardry.MagicDamage; import electroblob.wizardry.Wizardry; -import electroblob.wizardry.constants.Element; -import electroblob.wizardry.constants.SpellType; -import electroblob.wizardry.constants.Tier; +import electroblob.wizardry.WizardryUtilities; +import electroblob.wizardry.MagicDamage.DamageType; import electroblob.wizardry.entity.EntityArc; -import electroblob.wizardry.registry.WizardryItems; -import electroblob.wizardry.registry.WizardrySounds; -import electroblob.wizardry.util.MagicDamage; -import electroblob.wizardry.util.MagicDamage.DamageType; -import electroblob.wizardry.util.SpellModifiers; -import electroblob.wizardry.util.WizardryParticleType; -import electroblob.wizardry.util.WizardryUtilities; +import electroblob.wizardry.entity.living.EntityLightningWraith; +import electroblob.wizardry.entity.living.EntityStormElemental; import net.minecraft.entity.Entity; import net.minecraft.entity.EntityLiving; import net.minecraft.entity.EntityLivingBase; +import net.minecraft.entity.monster.EntityCreeper; import net.minecraft.entity.player.EntityPlayer; import net.minecraft.item.EnumAction; -import net.minecraft.util.EnumHand; -import net.minecraft.util.EnumParticleTypes; -import net.minecraft.util.math.RayTraceResult; -import net.minecraft.util.text.TextComponentTranslation; +import net.minecraft.util.ChatComponentTranslation; +import net.minecraft.util.MovingObjectPosition; import net.minecraft.world.World; // This spell was the 'guinea pig' for damage types, so to speak, so there's a bit of commentary on them here that may @@ -28,13 +26,13 @@ import net.minecraft.world.World; public class Arc extends Spell { public Arc() { - super(Tier.BASIC, 5, Element.LIGHTNING, "arc", SpellType.ATTACK, 15, EnumAction.NONE, false); + super(EnumTier.BASIC, 5, EnumElement.LIGHTNING, "arc", EnumSpellType.ATTACK, 15, EnumAction.none, false); } @Override - public boolean cast(World world, EntityPlayer caster, EnumHand hand, int ticksInUse, SpellModifiers modifiers) { + public boolean cast(World world, EntityPlayer caster, int ticksInUse, float damageMultiplier, float rangeMultiplier, float durationMultiplier, float blastMultiplier) { - RayTraceResult rayTrace = WizardryUtilities.standardEntityRayTrace(world, caster, 8*modifiers.get(WizardryItems.range_upgrade), 4.0f); + MovingObjectPosition rayTrace = WizardryUtilities.standardEntityRayTrace(world, caster, 8*rangeMultiplier, 4.0f); if(rayTrace != null && rayTrace.entityHit != null && rayTrace.entityHit instanceof EntityLivingBase){ @@ -47,20 +45,20 @@ public class Arc extends Spell { world.spawnEntityInWorld(arc); }else{ for(int i=0;i<8;i++){ - Wizardry.proxy.spawnParticle(WizardryParticleType.SPARK, world, target.posX + world.rand.nextFloat() - 0.5, target.getEntityBoundingBox().minY + target.height/2 + world.rand.nextFloat()*2 - 1, target.posZ + world.rand.nextFloat() - 0.5, 0, 0, 0, 3); - world.spawnParticle(EnumParticleTypes.SMOKE_LARGE, target.posX + world.rand.nextFloat() - 0.5, target.getEntityBoundingBox().minY + target.height/2 + world.rand.nextFloat()*2 - 1, target.posZ + world.rand.nextFloat() - 0.5, 0, 0, 0); + Wizardry.proxy.spawnParticle(EnumParticleType.SPARK, world, target.posX + world.rand.nextFloat() - 0.5, WizardryUtilities.getEntityFeetPos(target) + target.height/2 + world.rand.nextFloat()*2 - 1, target.posZ + world.rand.nextFloat() - 0.5, 0, 0, 0, 3); + world.spawnParticle("largesmoke", target.posX + world.rand.nextFloat() - 0.5, WizardryUtilities.getEntityFeetPos(target) + target.height/2 + world.rand.nextFloat()*2 - 1, target.posZ + world.rand.nextFloat() - 0.5, 0, 0, 0); } } // This is a lot neater than it was, thanks to the damage type system. if(MagicDamage.isEntityImmune(DamageType.SHOCK, target)){ - if(!world.isRemote) caster.addChatComponentMessage(new TextComponentTranslation("spell.resist", target.getName(), this.getNameForTranslationFormatted())); + if(!world.isRemote) caster.addChatComponentMessage(new ChatComponentTranslation("spell.resist", target.getCommandSenderName(), this.getDisplayNameWithFormatting())); }else{ - target.attackEntityFrom(MagicDamage.causeDirectMagicDamage(caster, DamageType.SHOCK), 3.0f * modifiers.get(SpellModifiers.DAMAGE)); + target.attackEntityFrom(MagicDamage.causeDirectMagicDamage(caster, DamageType.SHOCK), 3.0f * damageMultiplier); } - - caster.swingArm(hand); - target.playSound(WizardrySounds.SPELL_SPARK, 1.0F, world.rand.nextFloat() * 0.4F + 1.5F); + + caster.swingItem(); + world.playSoundAtEntity(target, "wizardry:arc", 1.0F, world.rand.nextFloat() * 0.4F + 1.5F); return true; } @@ -68,7 +66,7 @@ public class Arc extends Spell { } @Override - public boolean cast(World world, EntityLiving caster, EnumHand hand, int ticksInUse, EntityLivingBase target, SpellModifiers modifiers){ + public boolean cast(World world, EntityLiving caster, EntityLivingBase target, float damageMultiplier, float rangeMultiplier, float durationMultiplier, float blastMultiplier){ if(target != null){ @@ -79,17 +77,17 @@ public class Arc extends Spell { world.spawnEntityInWorld(arc); }else{ for(int i=0;i<8;i++){ - Wizardry.proxy.spawnParticle(WizardryParticleType.SPARK, world, target.posX + world.rand.nextFloat() - 0.5, target.getEntityBoundingBox().minY + target.height/2 + world.rand.nextFloat()*2 - 1, target.posZ + world.rand.nextFloat() - 0.5, 0, 0, 0, 3); - world.spawnParticle(EnumParticleTypes.SMOKE_LARGE, target.posX + world.rand.nextFloat() - 0.5, target.getEntityBoundingBox().minY + target.height/2 + world.rand.nextFloat()*2 - 1, target.posZ + world.rand.nextFloat() - 0.5, 0, 0, 0); + Wizardry.proxy.spawnParticle(EnumParticleType.SPARK, world, target.posX + world.rand.nextFloat() - 0.5, WizardryUtilities.getEntityFeetPos(target) + target.height/2 + world.rand.nextFloat()*2 - 1, target.posZ + world.rand.nextFloat() - 0.5, 0, 0, 0, 3); + world.spawnParticle("largesmoke", target.posX + world.rand.nextFloat() - 0.5, WizardryUtilities.getEntityFeetPos(target) + target.height/2 + world.rand.nextFloat()*2 - 1, target.posZ + world.rand.nextFloat() - 0.5, 0, 0, 0); } } // What's great about the damage type system is that, because I don't need to know if the creature resisted // the damage here, I can simply call this without having to check for immunities at all. - target.attackEntityFrom(MagicDamage.causeDirectMagicDamage(caster, DamageType.SHOCK), 3.0f * modifiers.get(SpellModifiers.DAMAGE)); + target.attackEntityFrom(MagicDamage.causeDirectMagicDamage(caster, DamageType.SHOCK), 3.0f * damageMultiplier); - caster.swingArm(hand); - target.playSound(WizardrySounds.SPELL_SPARK, 1.0F, world.rand.nextFloat() * 0.4F + 1.5F); + caster.swingItem(); + world.playSoundAtEntity(target, "wizardry:arc", 1.0F, world.rand.nextFloat() * 0.4F + 1.5F); return true; } diff --git a/src/main/java/electroblob/wizardry/spell/ArcaneJammer.java b/src/main/java/electroblob/wizardry/spell/ArcaneJammer.java index 163e2f42..0daa0d98 100644 --- a/src/main/java/electroblob/wizardry/spell/ArcaneJammer.java +++ b/src/main/java/electroblob/wizardry/spell/ArcaneJammer.java @@ -1,72 +1,63 @@ package electroblob.wizardry.spell; +import electroblob.wizardry.EnumElement; +import electroblob.wizardry.EnumParticleType; +import electroblob.wizardry.EnumSpellType; +import electroblob.wizardry.EnumTier; import electroblob.wizardry.Wizardry; -import electroblob.wizardry.constants.Element; -import electroblob.wizardry.constants.SpellType; -import electroblob.wizardry.constants.Tier; +import electroblob.wizardry.WizardryUtilities; import electroblob.wizardry.entity.living.EntityWizard; -import electroblob.wizardry.event.SpellCastEvent; -import electroblob.wizardry.registry.WizardryAchievements; -import electroblob.wizardry.registry.WizardryItems; -import electroblob.wizardry.registry.WizardryPotions; -import electroblob.wizardry.registry.WizardrySounds; -import electroblob.wizardry.util.SpellModifiers; -import electroblob.wizardry.util.WizardryParticleType; -import electroblob.wizardry.util.WizardryUtilities; import net.minecraft.entity.EntityLiving; import net.minecraft.entity.EntityLivingBase; import net.minecraft.entity.player.EntityPlayer; import net.minecraft.item.EnumAction; import net.minecraft.potion.PotionEffect; -import net.minecraft.util.EnumHand; -import net.minecraft.util.math.RayTraceResult; -import net.minecraft.util.math.Vec3d; +import net.minecraft.util.MovingObjectPosition; +import net.minecraft.util.MovingObjectPosition.MovingObjectType; +import net.minecraft.util.Vec3; import net.minecraft.world.World; -import net.minecraftforge.fml.common.Mod; -import net.minecraftforge.fml.common.eventhandler.SubscribeEvent; -@Mod.EventBusSubscriber public class ArcaneJammer extends Spell { public ArcaneJammer() { - super(Tier.ADVANCED, 30, Element.HEALING, "arcane_jammer", SpellType.ATTACK, 50, EnumAction.NONE, false); + super(EnumTier.ADVANCED, 30, EnumElement.HEALING, "arcane_jammer", EnumSpellType.ATTACK, 50, EnumAction.none, false); } @Override - public boolean cast(World world, EntityPlayer caster, EnumHand hand, int ticksInUse, SpellModifiers modifiers) { + public boolean cast(World world, EntityPlayer caster, int ticksInUse, float damageMultiplier, float rangeMultiplier, float durationMultiplier, float blastMultiplier) { - Vec3d look = caster.getLookVec(); + Vec3 look = caster.getLookVec(); - RayTraceResult rayTrace = WizardryUtilities.standardEntityRayTrace(world, caster, 10*modifiers.get(WizardryItems.range_upgrade)); + MovingObjectPosition rayTrace = WizardryUtilities.standardEntityRayTrace(world, caster, 10*rangeMultiplier); - if(rayTrace != null && rayTrace.typeOfHit == RayTraceResult.Type.ENTITY && rayTrace.entityHit instanceof EntityLivingBase){ + if(rayTrace != null && rayTrace.typeOfHit == MovingObjectType.ENTITY && rayTrace.entityHit instanceof EntityLivingBase){ EntityLivingBase entity = (EntityLivingBase) rayTrace.entityHit; - if(entity instanceof EntityWizard) caster.addStat(WizardryAchievements.jam_wizard); + if(entity instanceof EntityWizard) caster.triggerAchievement(Wizardry.jamWizard); if(!world.isRemote){ - entity.addPotionEffect(new PotionEffect(WizardryPotions.arcane_jammer, (int)(300*modifiers.get(WizardryItems.duration_upgrade)), 0)); + entity.addPotionEffect(new PotionEffect(Wizardry.arcaneJammer.id, (int)(300*durationMultiplier), 0)); } } if(world.isRemote){ - for(int i=1; i<(int)(25*modifiers.get(WizardryItems.range_upgrade)); i+=2){ + for(int i=1; i<(int)(25*rangeMultiplier); i+=2){ double x1 = caster.posX + look.xCoord*i/2 + world.rand.nextFloat()/5 - 0.1f; double y1 = WizardryUtilities.getPlayerEyesPos(caster) - 0.4f + look.yCoord*i/2 + world.rand.nextFloat()/5 - 0.1f; double z1 = caster.posZ + look.zCoord*i/2 + world.rand.nextFloat()/5 - 0.1f; - Wizardry.proxy.spawnParticle(WizardryParticleType.SPARKLE, world, x1, y1, z1, 0.0d, 0.0d, 0.0d, 12 + world.rand.nextInt(8), 0.9f, 0.3f, 0.7f); + Wizardry.proxy.spawnParticle(EnumParticleType.SPARKLE, world, x1, y1, z1, 0.0d, 0.0d, 0.0d, 12 + world.rand.nextInt(8), 0.9f, 0.3f, 0.7f); } } - caster.swingArm(hand); - WizardryUtilities.playSoundAtPlayer(caster, WizardrySounds.SPELL_DEFLECTION, 0.7F, world.rand.nextFloat() * 0.4F + 0.8F); + caster.swingItem(); + world.playSoundAtEntity(caster, "wizardry:effect", 0.7F, world.rand.nextFloat() * 0.4F + 0.8F); return true; } @Override - public boolean cast(World world, EntityLiving caster, EnumHand hand, int ticksInUse, EntityLivingBase target, SpellModifiers modifiers){ + public boolean cast(World world, EntityLiving caster, EntityLivingBase target, float damageMultiplier, float rangeMultiplier, float durationMultiplier, float blastMultiplier){ if(target != null){ if(!world.isRemote){ - target.addPotionEffect(new PotionEffect(WizardryPotions.arcane_jammer, (int)(300*modifiers.get(WizardryItems.duration_upgrade)), 0)); + target.addPotionEffect(new PotionEffect(Wizardry.arcaneJammer.id, (int)(300*durationMultiplier), 0)); } if(world.isRemote){ @@ -75,17 +66,17 @@ public class ArcaneJammer extends Spell { double dy = (target.posY - caster.posY)/caster.getDistanceToEntity(target); double dz = (target.posZ - caster.posZ)/caster.getDistanceToEntity(target); - for(int i=1; i<(int)(25*modifiers.get(WizardryItems.range_upgrade)); i+=2){ + for(int i=1; i<(int)(25*rangeMultiplier); i+=2){ double x1 = caster.posX + dx*i/2 + world.rand.nextFloat()/5 - 0.1f; double y1 = caster.posY + caster.getEyeHeight() - 0.4f + dy*i/2 + world.rand.nextFloat()/5 - 0.1f; double z1 = caster.posZ + dz*i/2 + world.rand.nextFloat()/5 - 0.1f; - Wizardry.proxy.spawnParticle(WizardryParticleType.SPARKLE, world, x1, y1, z1, 0.0d, 0.0d, 0.0d, 12 + world.rand.nextInt(8), 0.9f, 0.3f, 0.7f); + Wizardry.proxy.spawnParticle(EnumParticleType.SPARKLE, world, x1, y1, z1, 0.0d, 0.0d, 0.0d, 12 + world.rand.nextInt(8), 0.9f, 0.3f, 0.7f); } } - caster.swingArm(hand); - caster.playSound(WizardrySounds.SPELL_DEFLECTION, 0.7F, world.rand.nextFloat() * 0.4F + 0.8F); + caster.swingItem(); + world.playSoundAtEntity(caster, "wizardry:effect", 0.7F, world.rand.nextFloat() * 0.4F + 0.8F); } return false; @@ -95,11 +86,5 @@ public class ArcaneJammer extends Spell { public boolean canBeCastByNPCs(){ return true; } - - @SubscribeEvent - public static void onSpellCastPreEvent(SpellCastEvent.Pre event){ - // Arcane jammer prevents spell casting. - if(event.getEntityLiving().isPotionActive(WizardryPotions.arcane_jammer)) event.setCanceled(true); - } } diff --git a/src/main/java/electroblob/wizardry/spell/ArrowRain.java b/src/main/java/electroblob/wizardry/spell/ArrowRain.java index b8c8f210..5d040b82 100644 --- a/src/main/java/electroblob/wizardry/spell/ArrowRain.java +++ b/src/main/java/electroblob/wizardry/spell/ArrowRain.java @@ -1,23 +1,20 @@ package electroblob.wizardry.spell; -import electroblob.wizardry.constants.Element; -import electroblob.wizardry.constants.SpellType; -import electroblob.wizardry.constants.Tier; +import electroblob.wizardry.EnumElement; +import electroblob.wizardry.EnumSpellType; +import electroblob.wizardry.EnumTier; +import electroblob.wizardry.WizardryUtilities; import electroblob.wizardry.entity.construct.EntityArrowRain; -import electroblob.wizardry.registry.WizardryItems; -import electroblob.wizardry.registry.WizardrySounds; -import electroblob.wizardry.util.SpellModifiers; -import electroblob.wizardry.util.WizardryUtilities; import net.minecraft.entity.player.EntityPlayer; import net.minecraft.item.EnumAction; -import net.minecraft.util.EnumHand; -import net.minecraft.util.math.RayTraceResult; +import net.minecraft.util.MovingObjectPosition; +import net.minecraft.util.MovingObjectPosition.MovingObjectType; import net.minecraft.world.World; public class ArrowRain extends Spell { public ArrowRain() { - super(Tier.MASTER, 75, Element.SORCERY, "arrow_rain", SpellType.ATTACK, 300, EnumAction.NONE, false); + super(EnumTier.MASTER, 75, EnumElement.SORCERY, "arrow_rain", EnumSpellType.ATTACK, 300, EnumAction.none, false); } @Override @@ -26,15 +23,15 @@ public class ArrowRain extends Spell { } @Override - public boolean cast(World world, EntityPlayer caster, EnumHand hand, int ticksInUse, SpellModifiers modifiers) { + public boolean cast(World world, EntityPlayer caster, int ticksInUse, float damageMultiplier, float rangeMultiplier, float durationMultiplier, float blastMultiplier) { - RayTraceResult rayTrace = WizardryUtilities.rayTrace(20*modifiers.get(WizardryItems.range_upgrade), world, caster, false); + MovingObjectPosition rayTrace = WizardryUtilities.rayTrace(20*rangeMultiplier, world, caster, false); - if(rayTrace != null && rayTrace.typeOfHit == RayTraceResult.Type.BLOCK){ + if(rayTrace != null && rayTrace.typeOfHit == MovingObjectType.BLOCK){ if(!world.isRemote){ - double x = rayTrace.hitVec.xCoord; - double y = rayTrace.hitVec.yCoord; - double z = rayTrace.hitVec.zCoord; + double x = rayTrace.blockX; + double y = rayTrace.blockY; + double z = rayTrace.blockZ; // Moves the entity back towards the caster a bit, so the area of effect is better centred on the position. // 3.0d is the distance to move the entity back towards the caster. double dx = caster.posX - x; @@ -43,12 +40,12 @@ public class ArrowRain extends Spell { x += dx*distRatio; z += dz*distRatio; - EntityArrowRain arrowrain = new EntityArrowRain(world, x, y + 5, z, caster, (int)(120*modifiers.get(WizardryItems.duration_upgrade)), modifiers.get(SpellModifiers.DAMAGE)); + EntityArrowRain arrowrain = new EntityArrowRain(world, x, y + 5, z, caster, (int)(120*durationMultiplier), damageMultiplier); arrowrain.rotationYaw = caster.rotationYawHead; world.spawnEntityInWorld(arrowrain); } - caster.swingArm(hand); - WizardryUtilities.playSoundAtPlayer(caster, WizardrySounds.SPELL_SUMMONING, 1.0F, 1.0F); + caster.swingItem(); + world.playSoundAtEntity(caster, "wizardry:darkaura", 1.0F, 1.0F); return true; } return false; diff --git a/src/main/java/electroblob/wizardry/spell/Banish.java b/src/main/java/electroblob/wizardry/spell/Banish.java index 8bf92bec..1f298427 100644 --- a/src/main/java/electroblob/wizardry/spell/Banish.java +++ b/src/main/java/electroblob/wizardry/spell/Banish.java @@ -1,56 +1,52 @@ package electroblob.wizardry.spell; +import electroblob.wizardry.EnumElement; +import electroblob.wizardry.EnumParticleType; +import electroblob.wizardry.EnumSpellType; +import electroblob.wizardry.EnumTier; import electroblob.wizardry.Wizardry; -import electroblob.wizardry.constants.Element; -import electroblob.wizardry.constants.SpellType; -import electroblob.wizardry.constants.Tier; -import electroblob.wizardry.registry.WizardryItems; -import electroblob.wizardry.util.SpellModifiers; -import electroblob.wizardry.util.WizardryParticleType; -import electroblob.wizardry.util.WizardryUtilities; +import electroblob.wizardry.WizardryUtilities; +import electroblob.wizardry.entity.construct.EntityBubble; import net.minecraft.entity.EntityLiving; import net.minecraft.entity.EntityLivingBase; import net.minecraft.entity.player.EntityPlayer; -import net.minecraft.init.SoundEvents; import net.minecraft.item.EnumAction; -import net.minecraft.util.EnumHand; -import net.minecraft.util.EnumParticleTypes; -import net.minecraft.util.math.BlockPos; -import net.minecraft.util.math.MathHelper; -import net.minecraft.util.math.RayTraceResult; -import net.minecraft.util.math.Vec3d; +import net.minecraft.util.MathHelper; +import net.minecraft.util.MovingObjectPosition; +import net.minecraft.util.MovingObjectPosition.MovingObjectType; +import net.minecraft.util.Vec3; import net.minecraft.world.World; public class Banish extends Spell { public Banish() { - super(Tier.APPRENTICE, 15, Element.NECROMANCY, "banish", SpellType.ATTACK, 40, EnumAction.NONE, false); + super(EnumTier.APPRENTICE, 15, EnumElement.NECROMANCY, "banish", EnumSpellType.ATTACK, 40, EnumAction.none, false); } @Override - public boolean cast(World world, EntityPlayer caster, EnumHand hand, int ticksInUse, SpellModifiers modifiers) { + public boolean cast(World world, EntityPlayer caster, int ticksInUse, float damageMultiplier, float rangeMultiplier, float durationMultiplier, float blastMultiplier) { - Vec3d look = caster.getLookVec(); + Vec3 look = caster.getLookVec(); - RayTraceResult rayTrace = WizardryUtilities.standardEntityRayTrace(world, caster, 10*modifiers.get(WizardryItems.range_upgrade)); + MovingObjectPosition rayTrace = WizardryUtilities.standardEntityRayTrace(world, caster, 10*rangeMultiplier); - if(rayTrace != null && rayTrace.typeOfHit == RayTraceResult.Type.ENTITY && rayTrace.entityHit instanceof EntityLivingBase){ + if(rayTrace != null && rayTrace.typeOfHit == MovingObjectType.ENTITY && rayTrace.entityHit instanceof EntityLivingBase){ EntityLivingBase target = (EntityLivingBase) rayTrace.entityHit; - double radius = (8 + world.rand.nextDouble()*8) * modifiers.get(WizardryItems.range_upgrade); + double radius = (8 + world.rand.nextDouble()*8) * rangeMultiplier; double angle = world.rand.nextDouble()*Math.PI*2; int x = MathHelper.floor_double(target.posX + Math.sin(angle)*radius); int z = MathHelper.floor_double(target.posZ - Math.cos(angle)*radius); - int y = WizardryUtilities.getNearestFloorLevel(world, new BlockPos(x, (int)caster.getEntityBoundingBox().minY, z), (int)radius); + int y = WizardryUtilities.getNearestFloorLevel(world, x, (int)caster.boundingBox.minY, z, (int)radius); if(world.isRemote){ for(int i=0;i<10;i++){ double dx1 = target.posX; - double dy1 = target.getEntityBoundingBox().minY + target.height*world.rand.nextFloat(); + double dy1 = target.boundingBox.minY + target.height*world.rand.nextFloat(); double dz1 = target.posZ; - world.spawnParticle(EnumParticleTypes.PORTAL, dx1, dy1, dz1, world.rand.nextDouble() - 0.5, world.rand.nextDouble() - 0.5, world.rand.nextDouble() - 0.5); + world.spawnParticle("portal", dx1, dy1, dz1, world.rand.nextDouble() - 0.5, world.rand.nextDouble() - 0.5, world.rand.nextDouble() - 0.5); } } @@ -58,11 +54,11 @@ public class Banish extends Spell { // This means stuff like snow layers is ignored, meaning when on snow-covered ground the caster does // not teleport 1 block above the ground. - if(!world.getBlockState(new BlockPos(x, y, z)).getMaterial().blocksMovement()){ + if(!world.getBlock(x, y, z).getMaterial().blocksMovement()){ y--; } - if(world.getBlockState(new BlockPos(x, y + 1, z)).getMaterial().blocksMovement() || world.getBlockState(new BlockPos(x, y + 2, z)).getMaterial().blocksMovement()){ + if(world.getBlock(x, y + 1, z).getMaterial().blocksMovement() || world.getBlock(x, y + 2, z).getMaterial().blocksMovement()){ return false; } @@ -70,37 +66,37 @@ public class Banish extends Spell { target.setPositionAndUpdate(x + 0.5, y + 1, z + 0.5); } - target.playSound(SoundEvents.ENTITY_ENDERMEN_TELEPORT, 1.0F, 1.0f); + world.playSoundAtEntity(target, "mob.endermen.portal", 1.0F, 1.0f); } } if(world.isRemote){ - for(int i=1; i<(int)(25*modifiers.get(WizardryItems.range_upgrade)); i+=2){ + for(int i=1; i<(int)(25*rangeMultiplier); i+=2){ double x1 = caster.posX + look.xCoord*i/2 + world.rand.nextFloat()/5 - 0.1f; double y1 = WizardryUtilities.getPlayerEyesPos(caster) - 0.4f + look.yCoord*i/2 + world.rand.nextFloat()/5 - 0.1f; double z1 = caster.posZ + look.zCoord*i/2 + world.rand.nextFloat()/5 - 0.1f; - world.spawnParticle(EnumParticleTypes.PORTAL, x1, y1 - 0.5, z1, 0.0d, 0.0d, 0.0d); - Wizardry.proxy.spawnParticle(WizardryParticleType.DARK_MAGIC, world, x1, y1, z1, 0.0d, 0.0d, 0.0d, 0, 0.2f, 0.0f, 0.2f); + world.spawnParticle("portal", x1, y1 - 0.5, z1, 0.0d, 0.0d, 0.0d); + Wizardry.proxy.spawnParticle(EnumParticleType.DARK_MAGIC, world, x1, y1, z1, 0.0d, 0.0d, 0.0d, 0, 0.2f, 0.0f, 0.2f); } } - WizardryUtilities.playSoundAtPlayer(caster, SoundEvents.ENTITY_ENDERMEN_TELEPORT, 1.0F, 1.0f); - caster.swingArm(hand); + world.playSoundAtEntity(caster, "mob.endermen.portal", 1.0F, 1.0f); + caster.swingItem(); return true; } @Override - public boolean cast(World world, EntityLiving caster, EnumHand hand, int ticksInUse, EntityLivingBase target, SpellModifiers modifiers){ + public boolean cast(World world, EntityLiving caster, EntityLivingBase target, float damageMultiplier, float rangeMultiplier, float durationMultiplier, float blastMultiplier){ if(target != null){ - double radius = (8 + world.rand.nextDouble()*8) * modifiers.get(WizardryItems.range_upgrade); + double radius = (8 + world.rand.nextDouble()*8) * rangeMultiplier; double angle = world.rand.nextDouble()*Math.PI*2; int x = MathHelper.floor_double(target.posX + Math.sin(angle)*radius); int z = MathHelper.floor_double(target.posZ - Math.cos(angle)*radius); - int y = WizardryUtilities.getNearestFloorLevel(world, new BlockPos(x, (int)caster.getEntityBoundingBox().minY, z), (int)radius); + int y = WizardryUtilities.getNearestFloorLevel(world, x, (int)caster.boundingBox.minY, z, (int)radius); if(world.isRemote){ @@ -114,15 +110,15 @@ public class Banish extends Spell { double y1 = caster.posY + caster.getEyeHeight() - 0.4f + dy*i/2 + world.rand.nextFloat()/5 - 0.1f; double z1 = caster.posZ + dz*i/2 + world.rand.nextFloat()/5 - 0.1f; - world.spawnParticle(EnumParticleTypes.PORTAL, x1, y1 - 0.5, z1, 0.0d, 0.0d, 0.0d); - Wizardry.proxy.spawnParticle(WizardryParticleType.DARK_MAGIC, world, x1, y1, z1, 0.0d, 0.0d, 0.0d, 0, 0.2f, 0.0f, 0.2f); + world.spawnParticle("portal", x1, y1 - 0.5, z1, 0.0d, 0.0d, 0.0d); + Wizardry.proxy.spawnParticle(EnumParticleType.DARK_MAGIC, world, x1, y1, z1, 0.0d, 0.0d, 0.0d, 0, 0.2f, 0.0f, 0.2f); } for(int i=0;i<10;i++){ double dx1 = target.posX; - double dy1 = target.getEntityBoundingBox().minY + target.height*world.rand.nextFloat(); + double dy1 = target.boundingBox.minY + target.height*world.rand.nextFloat(); double dz1 = target.posZ; - world.spawnParticle(EnumParticleTypes.PORTAL, dx1, dy1, dz1, world.rand.nextDouble() - 0.5, world.rand.nextDouble() - 0.5, world.rand.nextDouble() - 0.5); + world.spawnParticle("portal", dx1, dy1, dz1, world.rand.nextDouble() - 0.5, world.rand.nextDouble() - 0.5, world.rand.nextDouble() - 0.5); } } @@ -130,11 +126,11 @@ public class Banish extends Spell { // This means stuff like snow layers is ignored, meaning when on snow-covered ground the caster does // not teleport 1 block above the ground. - if(!world.getBlockState(new BlockPos(x, y, z)).getMaterial().blocksMovement()){ + if(!world.getBlock(x, y, z).getMaterial().blocksMovement()){ y--; } - if(world.getBlockState(new BlockPos(x, y + 1, z)).getMaterial().blocksMovement() || world.getBlockState(new BlockPos(x, y + 2, z)).getMaterial().blocksMovement()){ + if(world.getBlock(x, y + 1, z).getMaterial().blocksMovement() || world.getBlock(x, y + 2, z).getMaterial().blocksMovement()){ return false; } @@ -142,12 +138,12 @@ public class Banish extends Spell { target.setPositionAndUpdate(x + 0.5, y + 1, z + 0.5); } - target.playSound(SoundEvents.ENTITY_ENDERMEN_TELEPORT, 1.0F, 1.0f); + world.playSoundAtEntity(target, "mob.endermen.portal", 1.0F, 1.0f); } } - caster.playSound(SoundEvents.ENTITY_ENDERMEN_TELEPORT, 1.0F, 1.0f); - caster.swingArm(hand); + world.playSoundAtEntity(caster, "mob.endermen.portal", 1.0F, 1.0f); + caster.swingItem(); return true; } diff --git a/src/main/java/electroblob/wizardry/spell/BlackHole.java b/src/main/java/electroblob/wizardry/spell/BlackHole.java index 3cfcd267..29b14cb8 100644 --- a/src/main/java/electroblob/wizardry/spell/BlackHole.java +++ b/src/main/java/electroblob/wizardry/spell/BlackHole.java @@ -1,24 +1,20 @@ package electroblob.wizardry.spell; -import electroblob.wizardry.constants.Element; -import electroblob.wizardry.constants.SpellType; -import electroblob.wizardry.constants.Tier; +import electroblob.wizardry.EnumElement; +import electroblob.wizardry.EnumSpellType; +import electroblob.wizardry.EnumTier; +import electroblob.wizardry.WizardryUtilities; import electroblob.wizardry.entity.construct.EntityBlackHole; -import electroblob.wizardry.registry.WizardryItems; -import electroblob.wizardry.util.SpellModifiers; -import electroblob.wizardry.util.WizardryUtilities; import net.minecraft.entity.player.EntityPlayer; -import net.minecraft.init.SoundEvents; import net.minecraft.item.EnumAction; -import net.minecraft.util.EnumHand; -import net.minecraft.util.math.BlockPos; -import net.minecraft.util.math.RayTraceResult; +import net.minecraft.util.MovingObjectPosition; +import net.minecraft.util.MovingObjectPosition.MovingObjectType; import net.minecraft.world.World; public class BlackHole extends Spell { public BlackHole() { - super(Tier.MASTER, 150, Element.SORCERY, "black_hole", SpellType.ATTACK, 400, EnumAction.NONE, false); + super(EnumTier.MASTER, 150, EnumElement.SORCERY, "black_hole", EnumSpellType.ATTACK, 400, EnumAction.none, false); } @Override @@ -27,36 +23,81 @@ public class BlackHole extends Spell { } @Override - public boolean cast(World world, EntityPlayer caster, EnumHand hand, int ticksInUse, SpellModifiers modifiers) { + public boolean cast(World world, EntityPlayer caster, int ticksInUse, float damageMultiplier, float rangeMultiplier, float durationMultiplier, float blastMultiplier) { - RayTraceResult rayTrace = WizardryUtilities.rayTrace(10*modifiers.get(WizardryItems.range_upgrade), world, caster, false); + MovingObjectPosition rayTrace = WizardryUtilities.rayTrace(10*rangeMultiplier, world, caster, false); - if(rayTrace != null && rayTrace.typeOfHit == RayTraceResult.Type.BLOCK){ - - // This demonstrates beautifully the elegance of BlockPos. In 1.7.10 this required a 50-line long switch - // statement and a flag variable; now it only needs a few lines. - BlockPos pos = new BlockPos(rayTrace.hitVec).offset(rayTrace.sideHit); - - if(world.isAirBlock(pos)){ - - if(!world.isRemote){ - world.spawnEntityInWorld(new EntityBlackHole(world, pos.getX()+0.5, pos.getY()-1+0.5, pos.getZ()+0.5, caster, (int)(600*modifiers.get(WizardryItems.duration_upgrade)), modifiers.get(SpellModifiers.DAMAGE))); + if(rayTrace != null && rayTrace.typeOfHit == MovingObjectType.BLOCK){ + int blockHitX = rayTrace.blockX; + int blockHitY = rayTrace.blockY; + int blockHitZ = rayTrace.blockZ; + //world.playSound(blockHitX, blockHitY, blockHitZ, "sound/ambient/cave/cave2", 1.0f, 1.5f, false); + int blockHitSide = rayTrace.sideHit; + boolean flag2 = false; + switch(blockHitSide){ + case 0: + if(world.isAirBlock(blockHitX, blockHitY-1, blockHitZ)){ + if(!world.isRemote){ + world.spawnEntityInWorld(new EntityBlackHole(world, (double)blockHitX+0.5, (double)blockHitY-1+0.5, (double)blockHitZ+0.5, caster, (int)(600*durationMultiplier), damageMultiplier)); + } + flag2 = true; } - - caster.swingArm(hand); - WizardryUtilities.playSoundAtPlayer(caster, SoundEvents.ENTITY_WITHER_SPAWN, 2.0f, 0.7f); + break; + case 1: + if(world.isAirBlock(blockHitX, blockHitY+1, blockHitZ)){ + if(!world.isRemote){ + world.spawnEntityInWorld(new EntityBlackHole(world, (double)blockHitX+0.5, (double)blockHitY+1+0.5, (double)blockHitZ+0.5, caster, (int)(600*durationMultiplier), damageMultiplier)); + } + flag2 = true; + } + break; + case 2: + if(world.isAirBlock(blockHitX, blockHitY, blockHitZ-1)){ + if(!world.isRemote){ + world.spawnEntityInWorld(new EntityBlackHole(world, (double)blockHitX+0.5, (double)blockHitY+0.5, (double)blockHitZ-1+0.5, caster, (int)(600*durationMultiplier), damageMultiplier)); + } + flag2 = true; + } + break; + case 3: + if(world.isAirBlock(blockHitX, blockHitY, blockHitZ+1)){ + if(!world.isRemote){ + world.spawnEntityInWorld(new EntityBlackHole(world, (double)blockHitX+0.5, (double)blockHitY+0.5, (double)blockHitZ+1+0.5, caster, (int)(600*durationMultiplier), damageMultiplier)); + } + flag2 = true; + } + break; + case 4: + if(world.isAirBlock(blockHitX-1, blockHitY, blockHitZ)){ + if(!world.isRemote){ + world.spawnEntityInWorld(new EntityBlackHole(world, (double)blockHitX-1+0.5, (double)blockHitY+0.5, (double)blockHitZ+0.5, caster, (int)(600*durationMultiplier), damageMultiplier)); + } + flag2 = true; + } + break; + case 5: + if(world.isAirBlock(blockHitX+1, blockHitY, blockHitZ)){ + if(!world.isRemote){ + world.spawnEntityInWorld(new EntityBlackHole(world, (double)blockHitX+1, (double)blockHitY, (double)blockHitZ, caster, (int)(600*durationMultiplier), damageMultiplier)); + } + flag2 = true; + } + break; + } + if(flag2){ + caster.swingItem(); + world.playSoundAtEntity(caster, "mob.wither.spawn", 2.0f, 0.7f); return true; } - }else{ int x = (int) (Math.floor(caster.posX) + caster.getLookVec().xCoord*8); int y = (int) (Math.floor(caster.posY) + caster.eyeHeight + caster.getLookVec().yCoord*8); int z = (int) (Math.floor(caster.posZ) + caster.getLookVec().zCoord*8); if(!world.isRemote){ - world.spawnEntityInWorld(new EntityBlackHole(world, x, y, z, caster, (int)(600*modifiers.get(WizardryItems.duration_upgrade)), modifiers.get(SpellModifiers.DAMAGE))); + world.spawnEntityInWorld(new EntityBlackHole(world, x, y, z, caster, (int)(600*durationMultiplier), damageMultiplier)); } - caster.swingArm(hand); - WizardryUtilities.playSoundAtPlayer(caster, SoundEvents.ENTITY_WITHER_SPAWN, 2.0f, 0.7f); + caster.swingItem(); + world.playSoundAtEntity(caster, "mob.wither.spawn", 2.0f, 0.7f); return true; } return false; diff --git a/src/main/java/electroblob/wizardry/spell/Blink.java b/src/main/java/electroblob/wizardry/spell/Blink.java index 3353d686..68f41a0c 100644 --- a/src/main/java/electroblob/wizardry/spell/Blink.java +++ b/src/main/java/electroblob/wizardry/spell/Blink.java @@ -1,75 +1,115 @@ package electroblob.wizardry.spell; -import electroblob.wizardry.constants.Element; -import electroblob.wizardry.constants.SpellType; -import electroblob.wizardry.constants.Tier; -import electroblob.wizardry.registry.WizardryItems; -import electroblob.wizardry.util.SpellModifiers; -import electroblob.wizardry.util.WizardryUtilities; +import electroblob.wizardry.EnumElement; +import electroblob.wizardry.EnumSpellType; +import electroblob.wizardry.EnumTier; +import electroblob.wizardry.WizardryUtilities; +import electroblob.wizardry.entity.living.EntityWizard; import net.minecraft.entity.EntityLiving; import net.minecraft.entity.EntityLivingBase; +import net.minecraft.entity.ai.EntityAIArrowAttack; +import net.minecraft.entity.ai.EntityAITasks.EntityAITaskEntry; import net.minecraft.entity.player.EntityPlayer; -import net.minecraft.init.SoundEvents; import net.minecraft.item.EnumAction; -import net.minecraft.util.EnumFacing; -import net.minecraft.util.EnumHand; -import net.minecraft.util.EnumParticleTypes; -import net.minecraft.util.math.BlockPos; -import net.minecraft.util.math.MathHelper; -import net.minecraft.util.math.RayTraceResult; +import net.minecraft.util.MathHelper; +import net.minecraft.util.MovingObjectPosition; +import net.minecraft.util.MovingObjectPosition.MovingObjectType; import net.minecraft.world.World; public class Blink extends Spell { public Blink() { - super(Tier.APPRENTICE, 15, Element.SORCERY, "blink", SpellType.UTILITY, 25, EnumAction.NONE, false); + super(EnumTier.APPRENTICE, 15, EnumElement.SORCERY, "blink", EnumSpellType.UTILITY, 25, EnumAction.none, false); } @Override - public boolean cast(World world, EntityPlayer caster, EnumHand hand, int ticksInUse, SpellModifiers modifiers) { + public boolean cast(World world, EntityPlayer caster, int ticksInUse, float damageMultiplier, float rangeMultiplier, float durationMultiplier, float blastMultiplier) { - RayTraceResult rayTrace = WizardryUtilities.rayTrace(25*modifiers.get(WizardryItems.range_upgrade), world, caster, false); + MovingObjectPosition rayTrace = WizardryUtilities.rayTrace(25*rangeMultiplier, world, caster, false); // It's worth noting that on the client side, the cast() method only gets called if the server side // cast method succeeded, so you need not check any conditions for spawning particles. if(world.isRemote){ for(int i=0;i<10;i++){ - double dx = caster.posX; - double dy = caster.getEntityBoundingBox().minY + 2*world.rand.nextFloat(); - double dz = caster.posZ; + double dx1 = caster.posX; + double dy1 = WizardryUtilities.getPlayerEyesPos(caster) - 1.5 + 2*world.rand.nextFloat(); + double dz1 = caster.posZ; // For portal particles, velocity is not velocity but the offset where they start, then drift to // the actual position given. - world.spawnParticle(EnumParticleTypes.PORTAL, dx, dy, dz, world.rand.nextDouble() - 0.5, world.rand.nextDouble() - 0.5, world.rand.nextDouble() - 0.5); + world.spawnParticle("portal", dx1, dy1, dz1, world.rand.nextDouble() - 0.5, world.rand.nextDouble() - 0.5, world.rand.nextDouble() - 0.5); } } - if(rayTrace != null && rayTrace.typeOfHit == RayTraceResult.Type.BLOCK){ + if(rayTrace != null && rayTrace.typeOfHit == MovingObjectType.BLOCK){ - BlockPos pos = rayTrace.getBlockPos(); - - // Can't teleport onto the ceiling. - if(rayTrace.sideHit == EnumFacing.DOWN) return false; + int blockHitX = rayTrace.blockX; + int blockHitY = rayTrace.blockY; + int blockHitZ = rayTrace.blockZ; + int blockHitSide = rayTrace.sideHit; // This means stuff like snow layers is ignored, meaning when on snow-covered ground the player does // not teleport 1 block above the ground. - if(rayTrace.sideHit == EnumFacing.UP && !world.getBlockState(pos).getMaterial().blocksMovement()){ - pos = pos.down(); + if(blockHitSide == 1 && !world.getBlock(blockHitX, blockHitY, blockHitZ).getMaterial().blocksMovement()){ + blockHitY--; } - pos = rayTrace.getBlockPos().offset(rayTrace.sideHit); - - // Prevents the player from teleporting into blocks and suffocating. - if(world.getBlockState(pos).getMaterial().blocksMovement() || world.getBlockState(pos.up()).getMaterial().blocksMovement()){ + // The following prevents the player from teleporting into blocks and suffocating + switch(blockHitSide){ + case -1: return false; + case 0: + return false; + case 1: + if(world.getBlock(blockHitX, blockHitY + 1, blockHitZ).getMaterial().blocksMovement() || world.getBlock(blockHitX, blockHitY + 2, blockHitZ).getMaterial().blocksMovement()){ + return false; + } + break; + case 2: + if(world.getBlock(blockHitX, blockHitY, blockHitZ - 1).getMaterial().blocksMovement() || world.getBlock(blockHitX, blockHitY + 1, blockHitZ - 1).getMaterial().blocksMovement()){ + return false; + } + break; + case 3: + if(world.getBlock(blockHitX, blockHitY, blockHitZ + 1).getMaterial().blocksMovement() || world.getBlock(blockHitX, blockHitY + 1, blockHitZ + 1).getMaterial().blocksMovement()){ + return false; + } + break; + case 4: + if(world.getBlock(blockHitX - 1, blockHitY, blockHitZ).getMaterial().blocksMovement() || world.getBlock(blockHitX - 1, blockHitY + 1, blockHitZ).getMaterial().blocksMovement()){ + return false; + } + break; + case 5: + if(world.getBlock(blockHitX + 1, blockHitY, blockHitZ).getMaterial().blocksMovement() || world.getBlock(blockHitX + 1, blockHitY + 1, blockHitZ).getMaterial().blocksMovement()){ + return false; + } + break; } - + // Plays before and after so it is heard from both positions - WizardryUtilities.playSoundAtPlayer(caster, SoundEvents.ENTITY_ENDERMEN_TELEPORT, 1.0F, 1.0f); + world.playSoundAtEntity(caster, "mob.endermen.portal", 1.0F, 1.0f); - if(!world.isRemote) caster.setPositionAndUpdate(pos.getX() + 0.5, pos.getY(), pos.getZ() + 0.5); - - WizardryUtilities.playSoundAtPlayer(caster, SoundEvents.ENTITY_ENDERMEN_TELEPORT, 1.0F, 1.0f); - caster.swingArm(hand); + if(!world.isRemote){ + switch(blockHitSide){ + case 1: + caster.setPositionAndUpdate(blockHitX + 0.5, blockHitY + 1, blockHitZ + 0.5); + break; + case 2: + caster.setPositionAndUpdate(blockHitX + 0.5, blockHitY, blockHitZ - 0.5); + break; + case 3: + caster.setPositionAndUpdate(blockHitX + 0.5, blockHitY, blockHitZ + 1.5); + break; + case 4: + caster.setPositionAndUpdate(blockHitX - 0.5, blockHitY, blockHitZ + 0.5); + break; + case 5: + caster.setPositionAndUpdate(blockHitX + 1.5, blockHitY, blockHitZ + 0.5); + break; + } + } + world.playSoundAtEntity(caster, "mob.endermen.portal", 1.0F, 1.0f); + caster.swingItem(); return true; } @@ -77,14 +117,14 @@ public class Blink extends Spell { } @Override - public boolean cast(World world, EntityLiving caster, EnumHand hand, int ticksInUse, EntityLivingBase target, SpellModifiers modifiers){ + public boolean cast(World world, EntityLiving caster, EntityLivingBase target, float damageMultiplier, float rangeMultiplier, float durationMultiplier, float blastMultiplier){ double angle = Math.atan2(target.posZ - caster.posZ, target.posX - caster.posX) + world.rand.nextDouble()*Math.PI; - double radius = caster.getDistance(target.posX, target.getEntityBoundingBox().minY, target.posZ) + world.rand.nextDouble()*3.0d; + double radius = caster.getDistance(target.posX, target.boundingBox.minY, target.posZ) + world.rand.nextDouble()*3.0d; int x = MathHelper.floor_double(target.posX + Math.sin(angle)*radius); int z = MathHelper.floor_double(target.posZ - Math.cos(angle)*radius); - int y = WizardryUtilities.getNearestFloorLevel(world, new BlockPos(caster), (int)radius); + int y = WizardryUtilities.getNearestFloorLevel(world, x, (int)caster.boundingBox.minY, z, (int)radius); // It's worth noting that on the client side, the cast() method only gets called if the server side // cast method succeeded, so you need not check any conditions for spawning particles. @@ -93,9 +133,9 @@ public class Blink extends Spell { if(world.isRemote){ for(int i=0;i<10;i++){ double dx1 = caster.posX; - double dy1 = caster.getEntityBoundingBox().minY + caster.height*world.rand.nextFloat(); + double dy1 = caster.boundingBox.minY + caster.height*world.rand.nextFloat(); double dz1 = caster.posZ; - world.spawnParticle(EnumParticleTypes.PORTAL, dx1, dy1, dz1, world.rand.nextDouble() - 0.5, world.rand.nextDouble() - 0.5, world.rand.nextDouble() - 0.5); + world.spawnParticle("portal", dx1, dy1, dz1, world.rand.nextDouble() - 0.5, world.rand.nextDouble() - 0.5, world.rand.nextDouble() - 0.5); } } @@ -103,23 +143,23 @@ public class Blink extends Spell { // This means stuff like snow layers is ignored, meaning when on snow-covered ground the caster does // not teleport 1 block above the ground. - if(!world.getBlockState(new BlockPos(x, y, z)).getMaterial().blocksMovement()){ + if(!world.getBlock(x, y, z).getMaterial().blocksMovement()){ y--; } - if(world.getBlockState(new BlockPos(x, y + 1, z)).getMaterial().blocksMovement() || world.getBlockState(new BlockPos(x, y + 2, z)).getMaterial().blocksMovement()){ + if(world.getBlock(x, y + 1, z).getMaterial().blocksMovement() || world.getBlock(x, y + 2, z).getMaterial().blocksMovement()){ return false; } // Plays before and after so it is heard from both positions - caster.playSound(SoundEvents.ENTITY_ENDERMEN_TELEPORT, 1.0F, 1.0f); + world.playSoundAtEntity(caster, "mob.endermen.portal", 1.0F, 1.0f); if(!world.isRemote){ caster.setPositionAndUpdate(x + 0.5, y + 1, z + 0.5); } - caster.playSound(SoundEvents.ENTITY_ENDERMEN_TELEPORT, 1.0F, 1.0f); - caster.swingArm(hand); + world.playSoundAtEntity(caster, "mob.endermen.portal", 1.0F, 1.0f); + caster.swingItem(); return true; } diff --git a/src/main/java/electroblob/wizardry/spell/Blizzard.java b/src/main/java/electroblob/wizardry/spell/Blizzard.java index 8f2066db..4fd66b1e 100644 --- a/src/main/java/electroblob/wizardry/spell/Blizzard.java +++ b/src/main/java/electroblob/wizardry/spell/Blizzard.java @@ -1,25 +1,22 @@ package electroblob.wizardry.spell; -import electroblob.wizardry.constants.Element; -import electroblob.wizardry.constants.SpellType; -import electroblob.wizardry.constants.Tier; +import electroblob.wizardry.EnumElement; +import electroblob.wizardry.EnumSpellType; +import electroblob.wizardry.EnumTier; +import electroblob.wizardry.WizardryUtilities; import electroblob.wizardry.entity.construct.EntityBlizzard; -import electroblob.wizardry.registry.WizardryItems; -import electroblob.wizardry.registry.WizardrySounds; -import electroblob.wizardry.util.SpellModifiers; -import electroblob.wizardry.util.WizardryUtilities; import net.minecraft.entity.EntityLiving; import net.minecraft.entity.EntityLivingBase; import net.minecraft.entity.player.EntityPlayer; import net.minecraft.item.EnumAction; -import net.minecraft.util.EnumHand; -import net.minecraft.util.math.RayTraceResult; +import net.minecraft.util.MovingObjectPosition; +import net.minecraft.util.MovingObjectPosition.MovingObjectType; import net.minecraft.world.World; public class Blizzard extends Spell { public Blizzard() { - super(Tier.ADVANCED, 40, Element.ICE, "blizzard", SpellType.ATTACK, 100, EnumAction.NONE, false); + super(EnumTier.ADVANCED, 40, EnumElement.ICE, "blizzard", EnumSpellType.ATTACK, 100, EnumAction.none, false); } @Override @@ -28,27 +25,27 @@ public class Blizzard extends Spell { } @Override - public boolean cast(World world, EntityPlayer caster, EnumHand hand, int ticksInUse, SpellModifiers modifiers) { + public boolean cast(World world, EntityPlayer caster, int ticksInUse, float damageMultiplier, float rangeMultiplier, float durationMultiplier, float blastMultiplier) { - RayTraceResult rayTrace = WizardryUtilities.rayTrace(20*modifiers.get(WizardryItems.range_upgrade), world, caster, false); + MovingObjectPosition rayTrace = WizardryUtilities.rayTrace(20*rangeMultiplier, world, caster, false); - if(rayTrace != null && rayTrace.typeOfHit == RayTraceResult.Type.BLOCK){ + if(rayTrace != null && rayTrace.typeOfHit == MovingObjectType.BLOCK){ if(!world.isRemote){ - double x = rayTrace.hitVec.xCoord; - double y = rayTrace.hitVec.yCoord; - double z = rayTrace.hitVec.zCoord; - EntityBlizzard blizzard = new EntityBlizzard(world, x, y+0.5, z, caster, (int)(600*modifiers.get(WizardryItems.duration_upgrade)), modifiers.get(SpellModifiers.DAMAGE)); + double x = rayTrace.blockX; + double y = rayTrace.blockY; + double z = rayTrace.blockZ; + EntityBlizzard blizzard = new EntityBlizzard(world, x, y+1.5, z, caster, (int)(600*durationMultiplier), damageMultiplier); world.spawnEntityInWorld(blizzard); } - caster.swingArm(hand); - WizardryUtilities.playSoundAtPlayer(caster, WizardrySounds.SPELL_ICE, 1.0F, 1.0F); + caster.swingItem(); + world.playSoundAtEntity(caster, "wizardry:ice", 1.0F, 1.0F); return true; } return false; } @Override - public boolean cast(World world, EntityLiving caster, EnumHand hand, int ticksInUse, EntityLivingBase target, SpellModifiers modifiers){ + public boolean cast(World world, EntityLiving caster, EntityLivingBase target, float damageMultiplier, float rangeMultiplier, float durationMultiplier, float blastMultiplier){ if(target != null){ @@ -56,11 +53,11 @@ public class Blizzard extends Spell { double x = target.posX; double y = target.posY; double z = target.posZ; - EntityBlizzard blizzard = new EntityBlizzard(world, x, y+0.5, z, caster, (int)(600*modifiers.get(WizardryItems.duration_upgrade)), modifiers.get(SpellModifiers.DAMAGE)); + EntityBlizzard blizzard = new EntityBlizzard(world, x, y+0.5, z, caster, (int)(600*durationMultiplier), damageMultiplier); world.spawnEntityInWorld(blizzard); } - caster.swingArm(hand); - caster.playSound(WizardrySounds.SPELL_ICE, 1.0F, 1.0F); + caster.swingItem(); + world.playSoundAtEntity(caster, "wizardry:ice", 1.0F, 1.0F); return true; } diff --git a/src/main/java/electroblob/wizardry/spell/Bubble.java b/src/main/java/electroblob/wizardry/spell/Bubble.java index c9c425d1..0ea7f8a7 100644 --- a/src/main/java/electroblob/wizardry/spell/Bubble.java +++ b/src/main/java/electroblob/wizardry/spell/Bubble.java @@ -1,80 +1,75 @@ package electroblob.wizardry.spell; +import electroblob.wizardry.EnumElement; +import electroblob.wizardry.EnumParticleType; +import electroblob.wizardry.EnumSpellType; +import electroblob.wizardry.EnumTier; +import electroblob.wizardry.MagicDamage; import electroblob.wizardry.Wizardry; -import electroblob.wizardry.constants.Element; -import electroblob.wizardry.constants.SpellType; -import electroblob.wizardry.constants.Tier; +import electroblob.wizardry.WizardryUtilities; +import electroblob.wizardry.MagicDamage.DamageType; import electroblob.wizardry.entity.construct.EntityBubble; -import electroblob.wizardry.registry.WizardryItems; -import electroblob.wizardry.registry.WizardrySounds; -import electroblob.wizardry.util.MagicDamage; -import electroblob.wizardry.util.MagicDamage.DamageType; -import electroblob.wizardry.util.SpellModifiers; -import electroblob.wizardry.util.WizardryParticleType; -import electroblob.wizardry.util.WizardryUtilities; import net.minecraft.entity.EntityLiving; import net.minecraft.entity.EntityLivingBase; import net.minecraft.entity.player.EntityPlayer; -import net.minecraft.init.SoundEvents; import net.minecraft.item.EnumAction; -import net.minecraft.util.EnumHand; -import net.minecraft.util.EnumParticleTypes; -import net.minecraft.util.math.RayTraceResult; -import net.minecraft.util.math.Vec3d; +import net.minecraft.util.MovingObjectPosition; +import net.minecraft.util.MovingObjectPosition.MovingObjectType; +import net.minecraft.util.Vec3; import net.minecraft.world.World; public class Bubble extends Spell { public Bubble() { - super(Tier.APPRENTICE, 15, Element.EARTH, "bubble", SpellType.ATTACK, 20, EnumAction.NONE, false); + super(EnumTier.APPRENTICE, 15, EnumElement.EARTH, "bubble", EnumSpellType.ATTACK, 20, EnumAction.none, false); } @Override - public boolean cast(World world, EntityPlayer caster, EnumHand hand, int ticksInUse, SpellModifiers modifiers) { + public boolean cast(World world, EntityPlayer caster, int ticksInUse, float damageMultiplier, float rangeMultiplier, float durationMultiplier, float blastMultiplier) { - Vec3d look = caster.getLookVec(); + Vec3 look = caster.getLookVec(); - RayTraceResult rayTrace = WizardryUtilities.standardEntityRayTrace(world, caster, 10*modifiers.get(WizardryItems.range_upgrade)); + MovingObjectPosition rayTrace = WizardryUtilities.standardEntityRayTrace(world, caster, 10*rangeMultiplier); - if(rayTrace != null && rayTrace.typeOfHit == RayTraceResult.Type.ENTITY && rayTrace.entityHit instanceof EntityLivingBase){ + if(rayTrace != null && rayTrace.typeOfHit == MovingObjectType.ENTITY && rayTrace.entityHit instanceof EntityLivingBase){ EntityLivingBase entity = (EntityLivingBase) rayTrace.entityHit; if(!world.isRemote){ - entity.attackEntityFrom(MagicDamage.causeDirectMagicDamage(caster, DamageType.MAGIC), 1.0f * modifiers.get(SpellModifiers.DAMAGE)); + entity.attackEntityFrom(MagicDamage.causeDirectMagicDamage(caster, DamageType.MAGIC), 1.0f * damageMultiplier); // Deprecated in favour of entity riding method - //entity.addPotionEffect(new PotionEffect(Wizardry.bubblePotion, 200, 0)); - EntityBubble entitybubble = new EntityBubble(world, entity.posX, entity.posY, entity.posZ, caster, (int)(200*modifiers.get(WizardryItems.duration_upgrade)), false, modifiers.get(SpellModifiers.DAMAGE)); + //entity.addPotionEffect(new PotionEffect(Wizardry.bubblePotion.id, 200, 0)); + EntityBubble entitybubble = new EntityBubble(world, entity.posX, entity.posY, entity.posZ, caster, (int)(200*durationMultiplier), false, damageMultiplier); world.spawnEntityInWorld(entitybubble); - entity.startRiding(entitybubble); + entity.mountEntity(entitybubble); } } if(world.isRemote){ - for(int i=1; i<(int)(25*modifiers.get(WizardryItems.range_upgrade)); i+=2){ + for(int i=1; i<(int)(25*rangeMultiplier); i+=2){ double x1 = caster.posX + look.xCoord*i/2 + world.rand.nextFloat()/5 - 0.1f; double y1 = WizardryUtilities.getPlayerEyesPos(caster) - 0.4f + look.yCoord*i/2 + world.rand.nextFloat()/5 - 0.1f; double z1 = caster.posZ + look.zCoord*i/2 + world.rand.nextFloat()/5 - 0.1f; - world.spawnParticle(EnumParticleTypes.WATER_SPLASH, x1, y1, z1, 0.0d, 0.0d, 0.0d); - Wizardry.proxy.spawnParticle(WizardryParticleType.MAGIC_BUBBLE, world, x1, y1, z1, 0.0d, 0.0d, 0.0d, 0); + world.spawnParticle("splash", x1, y1, z1, 0.0d, 0.0d, 0.0d); + Wizardry.proxy.spawnParticle(EnumParticleType.MAGIC_BUBBLE, world, x1, y1, z1, 0.0d, 0.0d, 0.0d, 0); } } - caster.swingArm(hand); - WizardryUtilities.playSoundAtPlayer(caster, SoundEvents.ENTITY_GENERIC_SWIM, 1.0F, world.rand.nextFloat() * 0.2F + 1.0F); - WizardryUtilities.playSoundAtPlayer(caster, WizardrySounds.SPELL_ICE, 0.5F, world.rand.nextFloat() * 0.2F + 1.0F); + caster.swingItem(); + world.playSoundAtEntity(caster, "game.neutral.swim", 1.0F, world.rand.nextFloat() * 0.2F + 1.0F); + world.playSoundAtEntity(caster, "wizardry:ice", 0.5F, world.rand.nextFloat() * 0.2F + 1.0F); return true; } @Override - public boolean cast(World world, EntityLiving caster, EnumHand hand, int ticksInUse, EntityLivingBase target, SpellModifiers modifiers){ + public boolean cast(World world, EntityLiving caster, EntityLivingBase target, float damageMultiplier, float rangeMultiplier, float durationMultiplier, float blastMultiplier){ if(target != null){ if(!world.isRemote){ - target.attackEntityFrom(MagicDamage.causeDirectMagicDamage(caster, DamageType.MAGIC), 1.0f * modifiers.get(SpellModifiers.DAMAGE)); + target.attackEntityFrom(MagicDamage.causeDirectMagicDamage(caster, DamageType.MAGIC), 1.0f * damageMultiplier); // Deprecated in favour of entity riding method - //entity.addPotionEffect(new PotionEffect(Wizardry.bubblePotion, 200, 0)); - EntityBubble entitybubble = new EntityBubble(world, target.posX, target.posY, target.posZ, caster, (int)(200*modifiers.get(WizardryItems.duration_upgrade)), false, modifiers.get(SpellModifiers.DAMAGE)); + //entity.addPotionEffect(new PotionEffect(Wizardry.bubblePotion.id, 200, 0)); + EntityBubble entitybubble = new EntityBubble(world, target.posX, target.posY, target.posZ, caster, (int)(200*durationMultiplier), false, damageMultiplier); world.spawnEntityInWorld(entitybubble); - target.startRiding(entitybubble); + target.mountEntity(entitybubble); } if(world.isRemote){ @@ -83,19 +78,19 @@ public class Bubble extends Spell { double dy = (target.posY - caster.posY)/caster.getDistanceToEntity(target); double dz = (target.posZ - caster.posZ)/caster.getDistanceToEntity(target); - for(int i=1; i<(int)(25*modifiers.get(WizardryItems.range_upgrade)); i+=2){ + for(int i=1; i<(int)(25*rangeMultiplier); i+=2){ double x1 = caster.posX + dx*i/2 + world.rand.nextFloat()/5 - 0.1f; double y1 = caster.posY + caster.getEyeHeight() - 0.4f + dy*i/2 + world.rand.nextFloat()/5 - 0.1f; double z1 = caster.posZ + dz*i/2 + world.rand.nextFloat()/5 - 0.1f; - world.spawnParticle(EnumParticleTypes.WATER_SPLASH, x1, y1, z1, 0.0d, 0.0d, 0.0d); - Wizardry.proxy.spawnParticle(WizardryParticleType.MAGIC_BUBBLE, world, x1, y1, z1, 0.0d, 0.0d, 0.0d, 0); + world.spawnParticle("splash", x1, y1, z1, 0.0d, 0.0d, 0.0d); + Wizardry.proxy.spawnParticle(EnumParticleType.MAGIC_BUBBLE, world, x1, y1, z1, 0.0d, 0.0d, 0.0d, 0); } } - caster.swingArm(hand); - caster.playSound(SoundEvents.ENTITY_GENERIC_SWIM, 1.0F, world.rand.nextFloat() * 0.2F + 1.0F); - caster.playSound(WizardrySounds.SPELL_ICE, 0.5F, world.rand.nextFloat() * 0.2F + 1.0F); + caster.swingItem(); + world.playSoundAtEntity(caster, "game.neutral.swim", 1.0F, world.rand.nextFloat() * 0.2F + 1.0F); + world.playSoundAtEntity(caster, "wizardry:ice", 0.5F, world.rand.nextFloat() * 0.2F + 1.0F); return true; } diff --git a/src/main/java/electroblob/wizardry/spell/ChainLightning.java b/src/main/java/electroblob/wizardry/spell/ChainLightning.java index 1b678ba5..491e653e 100644 --- a/src/main/java/electroblob/wizardry/spell/ChainLightning.java +++ b/src/main/java/electroblob/wizardry/spell/ChainLightning.java @@ -2,43 +2,41 @@ package electroblob.wizardry.spell; import java.util.List; +import electroblob.wizardry.EnumElement; +import electroblob.wizardry.EnumParticleType; +import electroblob.wizardry.EnumSpellType; +import electroblob.wizardry.EnumTier; +import electroblob.wizardry.MagicDamage; import electroblob.wizardry.Wizardry; -import electroblob.wizardry.constants.Element; -import electroblob.wizardry.constants.SpellType; -import electroblob.wizardry.constants.Tier; +import electroblob.wizardry.WizardryUtilities; +import electroblob.wizardry.MagicDamage.DamageType; import electroblob.wizardry.entity.EntityArc; -import electroblob.wizardry.registry.WizardryItems; -import electroblob.wizardry.registry.WizardrySounds; -import electroblob.wizardry.util.MagicDamage; -import electroblob.wizardry.util.MagicDamage.DamageType; -import electroblob.wizardry.util.SpellModifiers; -import electroblob.wizardry.util.WizardryParticleType; -import electroblob.wizardry.util.WizardryUtilities; +import electroblob.wizardry.entity.living.EntityLightningWraith; +import electroblob.wizardry.entity.living.EntityStormElemental; import net.minecraft.entity.Entity; import net.minecraft.entity.EntityLivingBase; +import net.minecraft.entity.monster.EntityCreeper; import net.minecraft.entity.player.EntityPlayer; import net.minecraft.item.EnumAction; -import net.minecraft.util.EnumHand; -import net.minecraft.util.EnumParticleTypes; -import net.minecraft.util.math.RayTraceResult; -import net.minecraft.util.text.TextComponentTranslation; +import net.minecraft.util.ChatComponentTranslation; +import net.minecraft.util.MovingObjectPosition; import net.minecraft.world.World; public class ChainLightning extends Spell { public ChainLightning() { - super(Tier.ADVANCED, 25, Element.LIGHTNING, "chain_lightning", SpellType.ATTACK, 50, EnumAction.NONE, false); + super(EnumTier.ADVANCED, 25, EnumElement.LIGHTNING, "chain_lightning", EnumSpellType.ATTACK, 50, EnumAction.none, false); } @Override - public boolean cast(World world, EntityPlayer caster, EnumHand hand, int ticksInUse, SpellModifiers modifiers) { + public boolean cast(World world, EntityPlayer caster, int ticksInUse, float damageMultiplier, float rangeMultiplier, float durationMultiplier, float blastMultiplier) { // First shot has range 10 (this is the only range affected by upgrades) and does 5 hearts of damage. // Chains to up to 5 secondary targets within a range of 5 of the primary target, and then to up to 2 // tertiary targets per secondary target within a range of 5 of that. Secondary targets are dealt 4 hearts // of damage; tertiary targets are dealt 3 hearts of damage. - RayTraceResult rayTrace = WizardryUtilities.standardEntityRayTrace(world, caster, 10*modifiers.get(WizardryItems.range_upgrade), 8.0f); + MovingObjectPosition rayTrace = WizardryUtilities.standardEntityRayTrace(world, caster, 10*rangeMultiplier, 8.0f); // Anything can be attacked with the initial arc, because the player has control over where it goes. If they // hit a minion or an ally, it's their problem! @@ -53,17 +51,17 @@ public class ChainLightning extends Spell { world.spawnEntityInWorld(arc); }else{ for(int i=0;i<8;i++){ - Wizardry.proxy.spawnParticle(WizardryParticleType.SPARK, world, target.posX + world.rand.nextFloat() - 0.5, target.getEntityBoundingBox().minY + target.height/2 + world.rand.nextFloat()*2 - 1, target.posZ + world.rand.nextFloat() - 0.5, 0, 0, 0, 3); - world.spawnParticle(EnumParticleTypes.SMOKE_LARGE, target.posX + world.rand.nextFloat() - 0.5, target.getEntityBoundingBox().minY + target.height/2 + world.rand.nextFloat()*2 - 1, target.posZ + world.rand.nextFloat() - 0.5, 0, 0, 0); + Wizardry.proxy.spawnParticle(EnumParticleType.SPARK, world, target.posX + world.rand.nextFloat() - 0.5, WizardryUtilities.getEntityFeetPos(target) + target.height/2 + world.rand.nextFloat()*2 - 1, target.posZ + world.rand.nextFloat() - 0.5, 0, 0, 0, 3); + world.spawnParticle("largesmoke", target.posX + world.rand.nextFloat() - 0.5, WizardryUtilities.getEntityFeetPos(target) + target.height/2 + world.rand.nextFloat()*2 - 1, target.posZ + world.rand.nextFloat() - 0.5, 0, 0, 0); } } - target.playSound(WizardrySounds.SPELL_SPARK, 1.0F, world.rand.nextFloat() * 0.4F + 1.5F); + world.playSoundAtEntity(target, "wizardry:arc", 1.0F, world.rand.nextFloat() * 0.4F + 1.5F); if(MagicDamage.isEntityImmune(DamageType.SHOCK, target)){ - if(!world.isRemote) caster.addChatComponentMessage(new TextComponentTranslation("spell.resist", target.getName(), this.getNameForTranslationFormatted())); + if(!world.isRemote) caster.addChatComponentMessage(new ChatComponentTranslation("spell.resist", target.getCommandSenderName(), this.getDisplayNameWithFormatting())); }else{ - target.attackEntityFrom(MagicDamage.causeDirectMagicDamage(caster, DamageType.SHOCK), 10.0f * modifiers.get(SpellModifiers.DAMAGE)); + target.attackEntityFrom(MagicDamage.causeDirectMagicDamage(caster, DamageType.SHOCK), 10.0f * damageMultiplier); } // Secondary chaining effect @@ -84,17 +82,17 @@ public class ChainLightning extends Spell { world.spawnEntityInWorld(arc); }else{ for(int j=0;j<8;j++){ - Wizardry.proxy.spawnParticle(WizardryParticleType.SPARK, world, secondaryTarget.posX + world.rand.nextFloat() - 0.5, secondaryTarget.getEntityBoundingBox().minY + secondaryTarget.height/2 + world.rand.nextFloat()*2 - 1, secondaryTarget.posZ + world.rand.nextFloat() - 0.5, 0, 0, 0, 3); - world.spawnParticle(EnumParticleTypes.SMOKE_LARGE, secondaryTarget.posX + world.rand.nextFloat() - 0.5, secondaryTarget.getEntityBoundingBox().minY + secondaryTarget.height/2 + world.rand.nextFloat()*2 - 1, secondaryTarget.posZ + world.rand.nextFloat() - 0.5, 0, 0, 0); + Wizardry.proxy.spawnParticle(EnumParticleType.SPARK, world, secondaryTarget.posX + world.rand.nextFloat() - 0.5, WizardryUtilities.getEntityFeetPos(secondaryTarget) + secondaryTarget.height/2 + world.rand.nextFloat()*2 - 1, secondaryTarget.posZ + world.rand.nextFloat() - 0.5, 0, 0, 0, 3); + world.spawnParticle("largesmoke", secondaryTarget.posX + world.rand.nextFloat() - 0.5, WizardryUtilities.getEntityFeetPos(secondaryTarget) + secondaryTarget.height/2 + world.rand.nextFloat()*2 - 1, secondaryTarget.posZ + world.rand.nextFloat() - 0.5, 0, 0, 0); } } - secondaryTarget.playSound(WizardrySounds.SPELL_SPARK, 1.0F, world.rand.nextFloat() * 0.4F + 1.5F); + world.playSoundAtEntity(secondaryTarget, "wizardry:arc", 1.0F, world.rand.nextFloat() * 0.4F + 1.5F); if(MagicDamage.isEntityImmune(DamageType.SHOCK, secondaryTarget)){ - if(!world.isRemote) caster.addChatComponentMessage(new TextComponentTranslation("spell.resist", secondaryTarget.getName(), this.getNameForTranslationFormatted())); + if(!world.isRemote) caster.addChatComponentMessage(new ChatComponentTranslation("spell.resist", secondaryTarget.getCommandSenderName(), this.getDisplayNameWithFormatting())); }else{ - secondaryTarget.attackEntityFrom(MagicDamage.causeDirectMagicDamage(caster, DamageType.SHOCK), 8.0f * modifiers.get(SpellModifiers.DAMAGE)); + secondaryTarget.attackEntityFrom(MagicDamage.causeDirectMagicDamage(caster, DamageType.SHOCK), 8.0f * damageMultiplier); } // Tertiary chaining effect @@ -114,24 +112,24 @@ public class ChainLightning extends Spell { world.spawnEntityInWorld(arc); }else{ for(int k=0;k<8;k++){ - Wizardry.proxy.spawnParticle(WizardryParticleType.SPARK, world, tertiaryTarget.posX + world.rand.nextFloat() - 0.5, tertiaryTarget.getEntityBoundingBox().minY + tertiaryTarget.height/2 + world.rand.nextFloat()*2 - 1, tertiaryTarget.posZ + world.rand.nextFloat() - 0.5, 0, 0, 0, 3); - world.spawnParticle(EnumParticleTypes.SMOKE_LARGE, tertiaryTarget.posX + world.rand.nextFloat() - 0.5, tertiaryTarget.getEntityBoundingBox().minY + tertiaryTarget.height/2 + world.rand.nextFloat()*2 - 1, tertiaryTarget.posZ + world.rand.nextFloat() - 0.5, 0, 0, 0); + Wizardry.proxy.spawnParticle(EnumParticleType.SPARK, world, tertiaryTarget.posX + world.rand.nextFloat() - 0.5, WizardryUtilities.getEntityFeetPos(tertiaryTarget) + tertiaryTarget.height/2 + world.rand.nextFloat()*2 - 1, tertiaryTarget.posZ + world.rand.nextFloat() - 0.5, 0, 0, 0, 3); + world.spawnParticle("largesmoke", tertiaryTarget.posX + world.rand.nextFloat() - 0.5, WizardryUtilities.getEntityFeetPos(tertiaryTarget) + tertiaryTarget.height/2 + world.rand.nextFloat()*2 - 1, tertiaryTarget.posZ + world.rand.nextFloat() - 0.5, 0, 0, 0); } } - tertiaryTarget.playSound(WizardrySounds.SPELL_SPARK, 1.0F, world.rand.nextFloat() * 0.4F + 1.5F); + world.playSoundAtEntity(tertiaryTarget, "wizardry:arc", 1.0F, world.rand.nextFloat() * 0.4F + 1.5F); if(MagicDamage.isEntityImmune(DamageType.SHOCK, tertiaryTarget)){ - if(!world.isRemote) caster.addChatComponentMessage(new TextComponentTranslation("spell.resist", tertiaryTarget.getName(), this.getNameForTranslationFormatted())); + if(!world.isRemote) caster.addChatComponentMessage(new ChatComponentTranslation("spell.resist", tertiaryTarget.getCommandSenderName(), this.getDisplayNameWithFormatting())); }else{ - tertiaryTarget.attackEntityFrom(MagicDamage.causeDirectMagicDamage(caster, DamageType.SHOCK), 6.0f * modifiers.get(SpellModifiers.DAMAGE)); + tertiaryTarget.attackEntityFrom(MagicDamage.causeDirectMagicDamage(caster, DamageType.SHOCK), 6.0f * damageMultiplier); } } } } } - caster.swingArm(hand); + caster.swingItem(); return true; } return false; diff --git a/src/main/java/electroblob/wizardry/spell/Clairvoyance.java b/src/main/java/electroblob/wizardry/spell/Clairvoyance.java index 816a02f0..11a65283 100644 --- a/src/main/java/electroblob/wizardry/spell/Clairvoyance.java +++ b/src/main/java/electroblob/wizardry/spell/Clairvoyance.java @@ -1,163 +1,104 @@ package electroblob.wizardry.spell; -import electroblob.wizardry.WizardData; +import electroblob.wizardry.EnumElement; +import electroblob.wizardry.EnumParticleType; +import electroblob.wizardry.EnumSpellType; +import electroblob.wizardry.EnumTier; +import electroblob.wizardry.ExtendedPlayer; import electroblob.wizardry.Wizardry; -import electroblob.wizardry.constants.Element; -import electroblob.wizardry.constants.SpellType; -import electroblob.wizardry.constants.Tier; -import electroblob.wizardry.item.ItemWand; import electroblob.wizardry.packet.PacketClairvoyance; import electroblob.wizardry.packet.WizardryPacketHandler; -import electroblob.wizardry.registry.Spells; -import electroblob.wizardry.registry.WizardryItems; -import electroblob.wizardry.registry.WizardrySounds; -import electroblob.wizardry.util.SpellModifiers; -import electroblob.wizardry.util.WandHelper; -import electroblob.wizardry.util.WizardryParticleType; -import electroblob.wizardry.util.WizardryPathFinder; -import electroblob.wizardry.util.WizardryUtilities; -import net.minecraft.entity.SharedMonsterAttributes; -import net.minecraft.entity.monster.EntityZombie; import net.minecraft.entity.player.EntityPlayer; import net.minecraft.entity.player.EntityPlayerMP; import net.minecraft.item.EnumAction; -import net.minecraft.item.ItemStack; -import net.minecraft.pathfinding.Path; -import net.minecraft.pathfinding.PathNodeType; +import net.minecraft.pathfinding.PathEntity; import net.minecraft.pathfinding.PathPoint; -import net.minecraft.util.EnumHand; -import net.minecraft.util.math.BlockPos; -import net.minecraft.util.text.TextComponentTranslation; +import net.minecraft.util.ChatComponentTranslation; import net.minecraft.world.World; -import net.minecraftforge.event.entity.player.PlayerInteractEvent; -import net.minecraftforge.fml.common.Mod; -import net.minecraftforge.fml.common.eventhandler.SubscribeEvent; -@Mod.EventBusSubscriber public class Clairvoyance extends Spell { - + /** The number of ticks it takes each path particle to move from one path point to the next. */ public static final int PARTICLE_MOVEMENT_INTERVAL = 45; - public Clairvoyance(){ - super(Tier.APPRENTICE, 20, Element.SORCERY, "clairvoyance", SpellType.UTILITY, 100, EnumAction.BOW, false); + public Clairvoyance() { + super(EnumTier.APPRENTICE, 20, EnumElement.SORCERY, "clairvoyance", EnumSpellType.UTILITY, 100, EnumAction.bow, false); } - + @Override public boolean doesSpellRequirePacket(){ return false; } @Override - public boolean cast(World world, EntityPlayer caster, EnumHand hand, int ticksInUse, SpellModifiers modifiers) { + public boolean cast(World world, EntityPlayer caster, int ticksInUse, float damageMultiplier, float rangeMultiplier, float durationMultiplier, float blastMultiplier) { - WizardData properties = WizardData.get(caster); + ExtendedPlayer properties = ExtendedPlayer.get(caster); if(properties != null && !caster.isSneaking()){ - if(caster.dimension == properties.getClairvoyanceDimension()){ - if(properties.getClairvoyanceLocation() != null){ - - if(!world.isRemote) caster.addChatComponentMessage(new TextComponentTranslation("spell.clairvoyance.searching")); - - EntityZombie arbitraryZombie = new EntityZombie(world){ - @Override public float getBlockPathWeight(BlockPos pos){ return 0; } - }; - arbitraryZombie.getEntityAttribute(SharedMonsterAttributes.FOLLOW_RANGE).setBaseValue(256*modifiers.get(WizardryItems.range_upgrade)); - arbitraryZombie.setPosition(caster.posX, caster.posY, caster.posZ); - arbitraryZombie.setPathPriority(PathNodeType.WATER, 0.0F); - arbitraryZombie.onGround = true; - - BlockPos destination = properties.getClairvoyanceLocation(); + if(caster.dimension == properties.clairvoyanceDimension){ + // Has to be y since x and z could reasonably be -1. + if(properties.clairvoyanceY > -1){ - WizardryPathFinder pathfinder = new WizardryPathFinder(arbitraryZombie.getNavigator().getNodeProcessor()); - - Path path = pathfinder.findPath(world, arbitraryZombie, destination, 256*modifiers.get(WizardryItems.range_upgrade)); - - if(path != null && path.getFinalPathPoint() != null){ - - int x = path.getFinalPathPoint().xCoord; - int y = path.getFinalPathPoint().yCoord; - int z = path.getFinalPathPoint().zCoord; - - if(x == destination.getX() && y == destination.getY() && z == destination.getZ()){ + if(!world.isRemote) caster.addChatComponentMessage(new ChatComponentTranslation("spell.clairvoyance.searching")); - WizardryUtilities.playSoundAtPlayer(caster, WizardrySounds.SPELL_CONJURATION, 1.0f, 1.0f); + // Parameters: entity, x, y, z, range, passOpenDoors, passClosedDoors, avoidWater, canSwim + PathEntity path = world.getEntityPathToXYZ(caster, properties.clairvoyanceX, properties.clairvoyanceY, properties.clairvoyanceZ, 256*rangeMultiplier, true, true, false, true); - if(!world.isRemote && caster instanceof EntityPlayerMP){ - WizardryPacketHandler.net.sendTo(new PacketClairvoyance.Message(path, modifiers.get(WizardryItems.duration_upgrade)), (EntityPlayerMP)caster); - } + // If the coordinates weren't checked, the path might not lead to the correct location. + if(path != null && path.getFinalPathPoint() != null + && path.getFinalPathPoint().xCoord == properties.clairvoyanceX + && path.getFinalPathPoint().yCoord == properties.clairvoyanceY + && path.getFinalPathPoint().zCoord == properties.clairvoyanceZ){ - return true; + world.playSoundAtEntity(caster, "wizardry:aura", 1.0f, 1.0f); + + // TODO: When the chunk the destination is in is not loaded, the particles don't spawn. + // I think I can fix this by sending the path positions in a packet to the caster, with the + // added benefit of only spawning them on that player's client. + if(!world.isRemote && caster instanceof EntityPlayerMP){ + WizardryPacketHandler.net.sendTo(new PacketClairvoyance.Message(path, durationMultiplier), (EntityPlayerMP)caster); } + + return true; + + }else{ + if(!world.isRemote) caster.addChatComponentMessage(new ChatComponentTranslation("spell.clairvoyance.outofrange")); } - - if(!world.isRemote) caster.addChatComponentMessage(new TextComponentTranslation("spell.clairvoyance.outofrange")); - }else{ - if(!world.isRemote) caster.addChatComponentMessage(new TextComponentTranslation("spell.clairvoyance.undefined")); + if(!world.isRemote) caster.addChatComponentMessage(new ChatComponentTranslation("spell.clairvoyance.undefined")); } }else{ - if(!world.isRemote) caster.addChatComponentMessage(new TextComponentTranslation("spell.clairvoyance.wrongdimension")); + if(!world.isRemote) caster.addChatComponentMessage(new ChatComponentTranslation("spell.clairvoyance.wrongdimension")); } } - // Fixes the problem with the sound not playing for the client of the caster. - if(world.isRemote) WizardryUtilities.playSoundAtPlayer(caster, WizardrySounds.SPELL_CONJURATION, 1.0f, 1.0f); - return false; } - - public static void spawnPathPaticles(World world, Path path, float durationMultiplier){ + + public static void spawnPathPaticles(World world, PathEntity path, float durationMultiplier) { PathPoint point, nextPoint; while(!path.isFinished()){ - + point = path.getPathPointFromIndex(path.getCurrentPathIndex()); if(point == path.getFinalPathPoint()) break; nextPoint = path.getCurrentPathLength() - path.getCurrentPathIndex() <= 2 ? path.getFinalPathPoint() : path.getPathPointFromIndex(path.getCurrentPathIndex() + 2); - - Wizardry.proxy.spawnParticle(WizardryParticleType.PATH, world, point.xCoord + 0.5, point.yCoord + 0.5, point.zCoord + 0.5, + + Wizardry.proxy.spawnParticle(EnumParticleType.PATH, world, point.xCoord + 0.5, point.yCoord + 0.5, point.zCoord + 0.5, (nextPoint.xCoord - point.xCoord)/(float)PARTICLE_MOVEMENT_INTERVAL, (nextPoint.yCoord - point.yCoord)/(float)PARTICLE_MOVEMENT_INTERVAL, (nextPoint.zCoord - point.zCoord)/(float)PARTICLE_MOVEMENT_INTERVAL, (int)(1800*durationMultiplier), 0, 1, 0.3f); - + path.incrementPathIndex(); path.incrementPathIndex(); } point = path.getFinalPathPoint(); - Wizardry.proxy.spawnParticle(WizardryParticleType.PATH, world, point.xCoord + 0.5, point.yCoord + 0.5, point.zCoord + 0.5, 0, 0, 0, (int)(1800*durationMultiplier), 1, 1, 1); - } - - @SubscribeEvent - public static void onRightClickBlockEvent(PlayerInteractEvent.RightClickBlock event){ - - if(event.getEntityPlayer().isSneaking()){ - - // The event now has an ItemStack, which greatly simplifies hand-related stuff. - ItemStack wand = event.getItemStack(); - - if(wand != null && wand.getItem() instanceof ItemWand && WandHelper.getCurrentSpell(wand) instanceof Clairvoyance){ - - WizardData properties = WizardData.get(event.getEntityPlayer()); - - if(properties != null){ - // THIS is why BlockPos is a thing - in 1.7.10 this requires a clumsy switch statement. - BlockPos pos = event.getPos().offset(event.getFace()); - - properties.setClairvoyancePoint(pos, event.getWorld().provider.getDimension()); - - if(!event.getWorld().isRemote){ - event.getEntityPlayer().addChatMessage(new TextComponentTranslation("spell.clairvoyance.confirm", Spells.clairvoyance.getNameForTranslationFormatted())); - } - - event.setCanceled(true); - } - } - } + Wizardry.proxy.spawnParticle(EnumParticleType.PATH, world, point.xCoord + 0.5, point.yCoord + 0.5, point.zCoord + 0.5, 0, 0, 0, (int)(1800*durationMultiplier), 1, 1, 1); } } \ No newline at end of file diff --git a/src/main/java/electroblob/wizardry/spell/Cobwebs.java b/src/main/java/electroblob/wizardry/spell/Cobwebs.java index b61e33ff..53b62618 100644 --- a/src/main/java/electroblob/wizardry/spell/Cobwebs.java +++ b/src/main/java/electroblob/wizardry/spell/Cobwebs.java @@ -1,23 +1,18 @@ package electroblob.wizardry.spell; -import electroblob.wizardry.constants.Element; -import electroblob.wizardry.constants.SpellType; -import electroblob.wizardry.constants.Tier; -import electroblob.wizardry.registry.WizardryBlocks; -import electroblob.wizardry.registry.WizardryItems; +import electroblob.wizardry.EnumElement; +import electroblob.wizardry.EnumSpellType; +import electroblob.wizardry.EnumTier; +import electroblob.wizardry.Wizardry; +import electroblob.wizardry.WizardryUtilities; import electroblob.wizardry.tileentity.TileEntityTimer; -import electroblob.wizardry.util.SpellModifiers; -import electroblob.wizardry.util.WizardryUtilities; import net.minecraft.entity.EntityLiving; import net.minecraft.entity.EntityLivingBase; import net.minecraft.entity.player.EntityPlayer; -import net.minecraft.init.SoundEvents; import net.minecraft.item.EnumAction; -import net.minecraft.util.EnumFacing; -import net.minecraft.util.EnumHand; -import net.minecraft.util.math.BlockPos; -import net.minecraft.util.math.MathHelper; -import net.minecraft.util.math.RayTraceResult; +import net.minecraft.util.MathHelper; +import net.minecraft.util.MovingObjectPosition; +import net.minecraft.util.MovingObjectPosition.MovingObjectType; import net.minecraft.world.World; public class Cobwebs extends Spell { @@ -25,7 +20,7 @@ public class Cobwebs extends Spell { private static final int baseDuration = 400; public Cobwebs() { - super(Tier.ADVANCED, 30, Element.EARTH, "cobwebs", SpellType.ATTACK, 70, EnumAction.NONE, false); + super(EnumTier.ADVANCED, 30, EnumElement.EARTH, "cobwebs", EnumSpellType.ATTACK, 70, EnumAction.none, false); } @Override @@ -34,44 +29,95 @@ public class Cobwebs extends Spell { } @Override - public boolean cast(World world, EntityPlayer caster, EnumHand hand, int ticksInUse, SpellModifiers modifiers) { + public boolean cast(World world, EntityPlayer caster, int ticksInUse, float damageMultiplier, float rangeMultiplier, float durationMultiplier, float blastMultiplier) { - RayTraceResult rayTrace = WizardryUtilities.rayTrace(12*modifiers.get(WizardryItems.range_upgrade), world, caster, true); + MovingObjectPosition rayTrace = WizardryUtilities.rayTrace(12*rangeMultiplier, world, caster, true); - if(rayTrace != null && rayTrace.typeOfHit == RayTraceResult.Type.BLOCK){ + if(rayTrace != null && rayTrace.typeOfHit == MovingObjectType.BLOCK){ + + int blockHitX = rayTrace.blockX; + int blockHitY = rayTrace.blockY; + int blockHitZ = rayTrace.blockZ; + int blockHitSide = rayTrace.sideHit; boolean flag = false; - BlockPos pos = rayTrace.getBlockPos().offset(rayTrace.sideHit); + switch(blockHitSide){ + case 0: blockHitY--; break; + case 1: blockHitY++; break; + case 2: blockHitZ--; break; + case 3: blockHitZ++; break; + case 4: blockHitX--; break; + case 5: blockHitX++; break; + } - if(world.isAirBlock(pos)){ + if(world.isAirBlock(blockHitX, blockHitY, blockHitZ)){ if(!world.isRemote){ - world.setBlockState(pos, WizardryBlocks.vanishing_cobweb.getDefaultState()); - if(world.getTileEntity(pos) instanceof TileEntityTimer){ - ((TileEntityTimer)world.getTileEntity(pos)).setLifetime((int)(baseDuration*modifiers.get(WizardryItems.duration_upgrade))); + world.setBlock(blockHitX, blockHitY, blockHitZ, Wizardry.vanishingCobweb); + if(world.getTileEntity(blockHitX, blockHitY, blockHitZ) instanceof TileEntityTimer){ + ((TileEntityTimer)world.getTileEntity(blockHitX, blockHitY, blockHitZ)).setLifetime((int)(baseDuration*durationMultiplier)); } } flag = true; } - - for(EnumFacing side : EnumFacing.values()){ - - BlockPos pos1 = pos.offset(side); - - if(world.isAirBlock(pos1)){ - if(!world.isRemote){ - world.setBlockState(pos1, WizardryBlocks.vanishing_cobweb.getDefaultState()); - if(world.getTileEntity(pos1) instanceof TileEntityTimer){ - ((TileEntityTimer)world.getTileEntity(pos1)).setLifetime((int)(baseDuration*modifiers.get(WizardryItems.duration_upgrade))); - } + if(world.isAirBlock(blockHitX+1, blockHitY, blockHitZ)){ + if(!world.isRemote){ + world.setBlock(blockHitX+1, blockHitY, blockHitZ, Wizardry.vanishingCobweb); + if(world.getTileEntity(blockHitX+1, blockHitY, blockHitZ) instanceof TileEntityTimer){ + ((TileEntityTimer)world.getTileEntity(blockHitX+1, blockHitY, blockHitZ)).setLifetime((int)(baseDuration*durationMultiplier)); } - flag = true; } + flag = true; + } + if(world.isAirBlock(blockHitX-1, blockHitY, blockHitZ)){ + if(!world.isRemote){ + world.setBlock(blockHitX-1, blockHitY, blockHitZ, Wizardry.vanishingCobweb); + if(world.getTileEntity(blockHitX-1, blockHitY, blockHitZ) instanceof TileEntityTimer){ + ((TileEntityTimer)world.getTileEntity(blockHitX-1, blockHitY, blockHitZ)).setLifetime((int)(baseDuration*durationMultiplier)); + } + } + flag = true; + } + if(world.isAirBlock(blockHitX, blockHitY+1, blockHitZ)){ + if(!world.isRemote){ + world.setBlock(blockHitX, blockHitY+1, blockHitZ, Wizardry.vanishingCobweb); + if(world.getTileEntity(blockHitX, blockHitY+1, blockHitZ) instanceof TileEntityTimer){ + ((TileEntityTimer)world.getTileEntity(blockHitX, blockHitY+1, blockHitZ)).setLifetime((int)(baseDuration*durationMultiplier)); + } + } + flag = true; + } + if(world.isAirBlock(blockHitX, blockHitY-1, blockHitZ)){ + if(!world.isRemote){ + world.setBlock(blockHitX, blockHitY-1, blockHitZ, Wizardry.vanishingCobweb); + if(world.getTileEntity(blockHitX, blockHitY-1, blockHitZ) instanceof TileEntityTimer){ + ((TileEntityTimer)world.getTileEntity(blockHitX, blockHitY-1, blockHitZ)).setLifetime((int)(baseDuration*durationMultiplier)); + } + } + flag = true; + } + if(world.isAirBlock(blockHitX, blockHitY, blockHitZ+1)){ + if(!world.isRemote){ + world.setBlock(blockHitX, blockHitY, blockHitZ+1, Wizardry.vanishingCobweb); + if(world.getTileEntity(blockHitX, blockHitY, blockHitZ+1) instanceof TileEntityTimer){ + ((TileEntityTimer)world.getTileEntity(blockHitX, blockHitY, blockHitZ+1)).setLifetime((int)(baseDuration*durationMultiplier)); + } + } + flag = true; + } + if(world.isAirBlock(blockHitX, blockHitY, blockHitZ-1)){ + if(!world.isRemote){ + world.setBlock(blockHitX, blockHitY, blockHitZ-1, Wizardry.vanishingCobweb); + if(world.getTileEntity(blockHitX, blockHitY, blockHitZ-1) instanceof TileEntityTimer){ + ((TileEntityTimer)world.getTileEntity(blockHitX, blockHitY, blockHitZ-1)).setLifetime((int)(baseDuration*durationMultiplier)); + } + } + flag = true; } if(flag){ - caster.swingArm(hand); - WizardryUtilities.playSoundAtPlayer(caster, SoundEvents.BLOCK_LAVA_EXTINGUISH, 1.0f, 1.0f); + caster.swingItem(); + world.playSoundAtEntity(caster, "random.fizz", 1.0f, 1.0f); return true; } } @@ -79,46 +125,83 @@ public class Cobwebs extends Spell { } @Override - public boolean cast(World world, EntityLiving caster, EnumHand hand, int ticksInUse, EntityLivingBase target, SpellModifiers modifiers) { + public boolean cast(World world, EntityLiving caster, EntityLivingBase target, float damageMultiplier, float rangeMultiplier, float durationMultiplier, float blastMultiplier) { if(target != null){ int x = MathHelper.floor_double(target.posX); - int y = (int)target.getEntityBoundingBox().minY; + int y = (int)target.boundingBox.minY; int z = MathHelper.floor_double(target.posZ); boolean flag = false; - BlockPos pos = new BlockPos(x, y, z); - - if(world.isAirBlock(pos)){ + if(world.isAirBlock(x, y, z)){ if(!world.isRemote){ - world.setBlockState(pos, WizardryBlocks.vanishing_cobweb.getDefaultState()); - if(world.getTileEntity(pos) instanceof TileEntityTimer){ - ((TileEntityTimer)world.getTileEntity(pos)).setLifetime((int)(baseDuration*modifiers.get(WizardryItems.duration_upgrade))); + world.setBlock(x, y, z, Wizardry.vanishingCobweb); + if(world.getTileEntity(x, y, z) instanceof TileEntityTimer){ + ((TileEntityTimer)world.getTileEntity(x, y, z)).setLifetime((int)(baseDuration*durationMultiplier)); } } flag = true; } - - for(EnumFacing side : EnumFacing.values()){ - - BlockPos pos1 = pos.offset(side); - - if(world.isAirBlock(pos1)){ - if(!world.isRemote){ - world.setBlockState(pos1, WizardryBlocks.vanishing_cobweb.getDefaultState()); - if(world.getTileEntity(pos1) instanceof TileEntityTimer){ - ((TileEntityTimer)world.getTileEntity(pos1)).setLifetime((int)(baseDuration*modifiers.get(WizardryItems.duration_upgrade))); - } + if(world.isAirBlock(x+1, y, z)){ + if(!world.isRemote){ + world.setBlock(x+1, y, z, Wizardry.vanishingCobweb); + if(world.getTileEntity(x+1, y, z) instanceof TileEntityTimer){ + ((TileEntityTimer)world.getTileEntity(x+1, y, z)).setLifetime((int)(baseDuration*durationMultiplier)); } - flag = true; } + flag = true; + } + if(world.isAirBlock(x-1, y, z)){ + if(!world.isRemote){ + world.setBlock(x-1, y, z, Wizardry.vanishingCobweb); + if(world.getTileEntity(x-1, y, z) instanceof TileEntityTimer){ + ((TileEntityTimer)world.getTileEntity(x-1, y, z)).setLifetime((int)(baseDuration*durationMultiplier)); + } + } + flag = true; + } + if(world.isAirBlock(x, y+1, z)){ + if(!world.isRemote){ + world.setBlock(x, y+1, z, Wizardry.vanishingCobweb); + if(world.getTileEntity(x, y+1, z) instanceof TileEntityTimer){ + ((TileEntityTimer)world.getTileEntity(x, y+1, z)).setLifetime((int)(baseDuration*durationMultiplier)); + } + } + flag = true; + } + if(world.isAirBlock(x, y-1, z)){ + if(!world.isRemote){ + world.setBlock(x, y-1, z, Wizardry.vanishingCobweb); + if(world.getTileEntity(x, y-1, z) instanceof TileEntityTimer){ + ((TileEntityTimer)world.getTileEntity(x, y-1, z)).setLifetime((int)(baseDuration*durationMultiplier)); + } + } + flag = true; + } + if(world.isAirBlock(x, y, z+1)){ + if(!world.isRemote){ + world.setBlock(x, y, z+1, Wizardry.vanishingCobweb); + if(world.getTileEntity(x, y, z+1) instanceof TileEntityTimer){ + ((TileEntityTimer)world.getTileEntity(x, y, z+1)).setLifetime((int)(baseDuration*durationMultiplier)); + } + } + flag = true; + } + if(world.isAirBlock(x, y, z-1)){ + if(!world.isRemote){ + world.setBlock(x, y, z-1, Wizardry.vanishingCobweb); + if(world.getTileEntity(x, y, z-1) instanceof TileEntityTimer){ + ((TileEntityTimer)world.getTileEntity(x, y, z-1)).setLifetime((int)(baseDuration*durationMultiplier)); + } + } + flag = true; } if(flag){ - caster.swingArm(hand); - caster.playSound(SoundEvents.BLOCK_LAVA_EXTINGUISH, 1.0f, 1.0f); + caster.swingItem(); + world.playSoundAtEntity(caster, "random.fizz", 1.0f, 1.0f); return true; } } diff --git a/src/main/java/electroblob/wizardry/spell/ConjureArmour.java b/src/main/java/electroblob/wizardry/spell/ConjureArmour.java index 0096ff24..505aaed7 100644 --- a/src/main/java/electroblob/wizardry/spell/ConjureArmour.java +++ b/src/main/java/electroblob/wizardry/spell/ConjureArmour.java @@ -1,30 +1,27 @@ package electroblob.wizardry.spell; +import electroblob.wizardry.EnumElement; +import electroblob.wizardry.EnumParticleType; +import electroblob.wizardry.EnumSpellType; +import electroblob.wizardry.EnumTier; +import electroblob.wizardry.ExtendedPlayer; import electroblob.wizardry.Wizardry; -import electroblob.wizardry.constants.Element; -import electroblob.wizardry.constants.SpellType; -import electroblob.wizardry.constants.Tier; -import electroblob.wizardry.item.IConjuredItem; -import electroblob.wizardry.registry.WizardryItems; -import electroblob.wizardry.registry.WizardrySounds; -import electroblob.wizardry.util.SpellModifiers; -import electroblob.wizardry.util.WizardryParticleType; -import electroblob.wizardry.util.WizardryUtilities; +import electroblob.wizardry.WizardryUtilities; import net.minecraft.entity.player.EntityPlayer; import net.minecraft.item.EnumAction; import net.minecraft.item.ItemStack; +import net.minecraft.nbt.NBTTagCompound; import net.minecraft.nbt.NBTTagList; -import net.minecraft.util.EnumHand; import net.minecraft.world.World; public class ConjureArmour extends Spell { public ConjureArmour() { - super(Tier.ADVANCED, 45, Element.HEALING, "conjure_armour", SpellType.DEFENCE, 50, EnumAction.BOW, false); + super(EnumTier.ADVANCED, 45, EnumElement.HEALING, "conjure_armour", EnumSpellType.DEFENCE, 50, EnumAction.bow, false); } @Override - public boolean cast(World world, EntityPlayer caster, EnumHand hand, int ticksInUse, SpellModifiers modifiers) { + public boolean cast(World world, EntityPlayer caster, int ticksInUse, float damageMultiplier, float rangeMultiplier, float durationMultiplier, float blastMultiplier) { ItemStack armour; boolean flag = false; @@ -32,34 +29,38 @@ public class ConjureArmour extends Spell { // Note that armourInventory is the other way round! Also note that a blank "ench" tag is set to trick // the renderer into showing the enchantment effect on the actual armour model. - if(caster.inventory.armorInventory[3] == null && !WizardryUtilities.doesPlayerHaveItem(caster, WizardryItems.spectral_helmet)){ - armour = new ItemStack(WizardryItems.spectral_helmet); - IConjuredItem.setDurationMultiplier(armour, modifiers.get(WizardryItems.duration_upgrade)); - armour.getTagCompound().setTag("ench", new NBTTagList()); + if(caster.inventory.armorInventory[3] == null && !caster.inventory.hasItem(Wizardry.spectralHelmet)){ + armour = new ItemStack(Wizardry.spectralHelmet); + armour.stackTagCompound = new NBTTagCompound(); + armour.stackTagCompound.setFloat("durationMultiplier", durationMultiplier); + armour.stackTagCompound.setTag("ench", new NBTTagList()); caster.inventory.armorInventory[3] = armour; flag = true; } - if(caster.inventory.armorInventory[2] == null && !WizardryUtilities.doesPlayerHaveItem(caster, WizardryItems.spectral_chestplate)){ - armour = new ItemStack(WizardryItems.spectral_chestplate); - IConjuredItem.setDurationMultiplier(armour, modifiers.get(WizardryItems.duration_upgrade)); - armour.getTagCompound().setTag("ench", new NBTTagList()); + if(caster.inventory.armorInventory[2] == null && !caster.inventory.hasItem(Wizardry.spectralChestplate)){ + armour = new ItemStack(Wizardry.spectralChestplate); + armour.stackTagCompound = new NBTTagCompound(); + armour.stackTagCompound.setFloat("durationMultiplier", durationMultiplier); + armour.stackTagCompound.setTag("ench", new NBTTagList()); caster.inventory.armorInventory[2] = armour; flag = true; } - if(caster.inventory.armorInventory[1] == null && !WizardryUtilities.doesPlayerHaveItem(caster, WizardryItems.spectral_leggings)){ - armour = new ItemStack(WizardryItems.spectral_leggings); - IConjuredItem.setDurationMultiplier(armour, modifiers.get(WizardryItems.duration_upgrade)); - armour.getTagCompound().setTag("ench", new NBTTagList()); + if(caster.inventory.armorInventory[1] == null && !caster.inventory.hasItem(Wizardry.spectralLeggings)){ + armour = new ItemStack(Wizardry.spectralLeggings); + armour.stackTagCompound = new NBTTagCompound(); + armour.stackTagCompound.setFloat("durationMultiplier", durationMultiplier); + armour.stackTagCompound.setTag("ench", new NBTTagList()); caster.inventory.armorInventory[1] = armour; flag = true; } - if(caster.inventory.armorInventory[0] == null && !WizardryUtilities.doesPlayerHaveItem(caster, WizardryItems.spectral_boots)){ - armour = new ItemStack(WizardryItems.spectral_boots); - IConjuredItem.setDurationMultiplier(armour, modifiers.get(WizardryItems.duration_upgrade)); - armour.getTagCompound().setTag("ench", new NBTTagList()); + if(caster.inventory.armorInventory[0] == null && !caster.inventory.hasItem(Wizardry.spectralBoots)){ + armour = new ItemStack(Wizardry.spectralBoots); + armour.stackTagCompound = new NBTTagCompound(); + armour.stackTagCompound.setFloat("durationMultiplier", durationMultiplier); + armour.stackTagCompound.setTag("ench", new NBTTagList()); caster.inventory.armorInventory[0] = armour; flag = true; } @@ -71,11 +72,12 @@ public class ConjureArmour extends Spell { double x1 = (double)((float)caster.posX + world.rand.nextFloat()*2 - 1.0F); double y1 = (double)((float)WizardryUtilities.getPlayerEyesPos(caster) - 0.5F + world.rand.nextFloat()); double z1 = (double)((float)caster.posZ + world.rand.nextFloat()*2 - 1.0F); - Wizardry.proxy.spawnParticle(WizardryParticleType.SPARKLE, world, x1, y1, z1, 0, 0.1F, 0, 48 + world.rand.nextInt(12), 0.7f, 0.9f, 1.0f); + Wizardry.proxy.spawnParticle(EnumParticleType.SPARKLE, world, x1, y1, z1, 0, 0.1F, 0, 48 + world.rand.nextInt(12), 0.7f, 0.9f, 1.0f); } } - WizardryUtilities.playSoundAtPlayer(caster, WizardrySounds.SPELL_CONJURATION, 1.0f, 1.0f); + ExtendedPlayer.get(caster).conjuredArmourDuration = 0; + world.playSoundAtEntity(caster, "wizardry:aura", 1.0f, 1.0f); } return flag; diff --git a/src/main/java/electroblob/wizardry/spell/ConjureBow.java b/src/main/java/electroblob/wizardry/spell/ConjureBow.java index 087e41c6..74559a38 100644 --- a/src/main/java/electroblob/wizardry/spell/ConjureBow.java +++ b/src/main/java/electroblob/wizardry/spell/ConjureBow.java @@ -1,58 +1,46 @@ package electroblob.wizardry.spell; +import electroblob.wizardry.EnumElement; +import electroblob.wizardry.EnumParticleType; +import electroblob.wizardry.EnumSpellType; +import electroblob.wizardry.EnumTier; +import electroblob.wizardry.ExtendedPlayer; import electroblob.wizardry.Wizardry; -import electroblob.wizardry.constants.Element; -import electroblob.wizardry.constants.SpellType; -import electroblob.wizardry.constants.Tier; -import electroblob.wizardry.item.IConjuredItem; -import electroblob.wizardry.registry.WizardryItems; -import electroblob.wizardry.registry.WizardrySounds; -import electroblob.wizardry.util.SpellModifiers; -import electroblob.wizardry.util.WizardryParticleType; -import electroblob.wizardry.util.WizardryUtilities; +import electroblob.wizardry.WizardryUtilities; import net.minecraft.entity.player.EntityPlayer; import net.minecraft.item.EnumAction; import net.minecraft.item.ItemStack; -import net.minecraft.util.EnumHand; +import net.minecraft.nbt.NBTTagCompound; import net.minecraft.world.World; public class ConjureBow extends Spell { public ConjureBow() { - super(Tier.APPRENTICE, 40, Element.SORCERY, "conjure_bow", SpellType.UTILITY, 50, EnumAction.BOW, false); + super(EnumTier.APPRENTICE, 40, EnumElement.SORCERY, "conjure_bow", EnumSpellType.UTILITY, 50, EnumAction.bow, false); } @Override - public boolean cast(World world, EntityPlayer caster, EnumHand hand, int ticksInUse, SpellModifiers modifiers) { + public boolean cast(World world, EntityPlayer caster, int ticksInUse, float damageMultiplier, float rangeMultiplier, float durationMultiplier, float blastMultiplier) { - ItemStack bow = new ItemStack(WizardryItems.spectral_bow); + ItemStack bow = new ItemStack(Wizardry.spectralBow); + bow.stackTagCompound = new NBTTagCompound(); + bow.stackTagCompound.setFloat("durationMultiplier", durationMultiplier); - IConjuredItem.setDurationMultiplier(bow, modifiers.get(WizardryItems.duration_upgrade)); - - if(!WizardryUtilities.doesPlayerHaveItem(caster, WizardryItems.spectral_bow) && conjureItemInInventory(caster, bow)){ + if(!caster.inventory.hasItem(Wizardry.spectralBow) && caster.inventory.addItemStackToInventory(bow)){ for(int i=0; i<10; i++){ double x1 = (double)((float)caster.posX + world.rand.nextFloat()*2 - 1.0F); double y1 = (double)((float)WizardryUtilities.getPlayerEyesPos(caster) - 0.5F + world.rand.nextFloat()); double z1 = (double)((float)caster.posZ + world.rand.nextFloat()*2 - 1.0F); if(world.isRemote){ - Wizardry.proxy.spawnParticle(WizardryParticleType.SPARKLE, world, x1, y1, z1, 0, 0.1F, 0, 48 + world.rand.nextInt(12), 0.7f, 0.9f, 1.0f); + Wizardry.proxy.spawnParticle(EnumParticleType.SPARKLE, world, x1, y1, z1, 0, 0.1F, 0, 48 + world.rand.nextInt(12), 0.7f, 0.9f, 1.0f); } } - WizardryUtilities.playSoundAtPlayer(caster, WizardrySounds.SPELL_CONJURATION, 1.0f, 1.0f); + ExtendedPlayer.get(caster).conjuredBowDuration = 0; + world.playSoundAtEntity(caster, "wizardry:aura", 1.0f, 1.0f); return true; } return false; } - // TODO: When spells get superclassed, this method needs to be in the conjuration superclass. - /** Adds the given item to the given player's inventory, placing it in the main hand if the main hand is empty. */ - public static boolean conjureItemInInventory(EntityPlayer caster, ItemStack item){ - if(caster.getHeldItemMainhand() == null){ - caster.setHeldItem(EnumHand.MAIN_HAND, item); - return true; - }else{ - return caster.inventory.addItemStackToInventory(item); - } - } } diff --git a/src/main/java/electroblob/wizardry/spell/ConjurePickaxe.java b/src/main/java/electroblob/wizardry/spell/ConjurePickaxe.java index f1fdb33e..73c5b74a 100644 --- a/src/main/java/electroblob/wizardry/spell/ConjurePickaxe.java +++ b/src/main/java/electroblob/wizardry/spell/ConjurePickaxe.java @@ -1,44 +1,42 @@ package electroblob.wizardry.spell; +import electroblob.wizardry.EnumElement; +import electroblob.wizardry.EnumParticleType; +import electroblob.wizardry.EnumSpellType; +import electroblob.wizardry.EnumTier; +import electroblob.wizardry.ExtendedPlayer; import electroblob.wizardry.Wizardry; -import electroblob.wizardry.constants.Element; -import electroblob.wizardry.constants.SpellType; -import electroblob.wizardry.constants.Tier; -import electroblob.wizardry.item.IConjuredItem; -import electroblob.wizardry.registry.WizardryItems; -import electroblob.wizardry.registry.WizardrySounds; -import electroblob.wizardry.util.SpellModifiers; -import electroblob.wizardry.util.WizardryParticleType; -import electroblob.wizardry.util.WizardryUtilities; +import electroblob.wizardry.WizardryUtilities; import net.minecraft.entity.player.EntityPlayer; import net.minecraft.item.EnumAction; import net.minecraft.item.ItemStack; -import net.minecraft.util.EnumHand; +import net.minecraft.nbt.NBTTagCompound; import net.minecraft.world.World; public class ConjurePickaxe extends Spell { public ConjurePickaxe() { - super(Tier.APPRENTICE, 25, Element.SORCERY, "conjure_pickaxe", SpellType.UTILITY, 50, EnumAction.BOW, false); + super(EnumTier.APPRENTICE, 25, EnumElement.SORCERY, "conjure_pickaxe", EnumSpellType.UTILITY, 50, EnumAction.bow, false); } @Override - public boolean cast(World world, EntityPlayer caster, EnumHand hand, int ticksInUse, SpellModifiers modifiers) { + public boolean cast(World world, EntityPlayer caster, int ticksInUse, float damageMultiplier, float rangeMultiplier, float durationMultiplier, float blastMultiplier) { - ItemStack pickaxe = new ItemStack(WizardryItems.spectral_pickaxe); + ItemStack pickaxe = new ItemStack(Wizardry.spectralPickaxe); + pickaxe.stackTagCompound = new NBTTagCompound(); + pickaxe.stackTagCompound.setFloat("durationMultiplier", durationMultiplier); - IConjuredItem.setDurationMultiplier(pickaxe, modifiers.get(WizardryItems.duration_upgrade)); - - if(!WizardryUtilities.doesPlayerHaveItem(caster, WizardryItems.spectral_pickaxe) && ConjureBow.conjureItemInInventory(caster, pickaxe)){ - if(world.isRemote){ - for(int i=0; i<10; i++){ + if(!caster.inventory.hasItem(Wizardry.spectralPickaxe) && caster.inventory.addItemStackToInventory(pickaxe)){ + for(int i=0; i<10; i++){ double x1 = (double)((float)caster.posX + world.rand.nextFloat()*2 - 1.0F); double y1 = (double)((float)WizardryUtilities.getPlayerEyesPos(caster) - 0.5F + world.rand.nextFloat()); double z1 = (double)((float)caster.posZ + world.rand.nextFloat()*2 - 1.0F); - Wizardry.proxy.spawnParticle(WizardryParticleType.SPARKLE, world, x1, y1, z1, 0, 0.1F, 0, 48 + world.rand.nextInt(12), 0.7f, 0.9f, 1.0f); + if(world.isRemote){ + Wizardry.proxy.spawnParticle(EnumParticleType.SPARKLE, world, x1, y1, z1, 0, 0.1F, 0, 48 + world.rand.nextInt(12), 0.7f, 0.9f, 1.0f); } } - WizardryUtilities.playSoundAtPlayer(caster, WizardrySounds.SPELL_CONJURATION, 1.0f, 1.0f); + ExtendedPlayer.get(caster).conjuredPickaxeDuration = 0; + world.playSoundAtEntity(caster, "wizardry:aura", 1.0f, 1.0f); return true; } return false; diff --git a/src/main/java/electroblob/wizardry/spell/ConjureSword.java b/src/main/java/electroblob/wizardry/spell/ConjureSword.java index adcce8bf..ecd21b34 100644 --- a/src/main/java/electroblob/wizardry/spell/ConjureSword.java +++ b/src/main/java/electroblob/wizardry/spell/ConjureSword.java @@ -1,44 +1,45 @@ package electroblob.wizardry.spell; +import electroblob.wizardry.EnumElement; +import electroblob.wizardry.EnumParticleType; +import electroblob.wizardry.EnumSpellType; +import electroblob.wizardry.EnumTier; +import electroblob.wizardry.ExtendedPlayer; import electroblob.wizardry.Wizardry; -import electroblob.wizardry.constants.Element; -import electroblob.wizardry.constants.SpellType; -import electroblob.wizardry.constants.Tier; -import electroblob.wizardry.item.IConjuredItem; -import electroblob.wizardry.registry.WizardryItems; -import electroblob.wizardry.registry.WizardrySounds; -import electroblob.wizardry.util.SpellModifiers; -import electroblob.wizardry.util.WizardryParticleType; -import electroblob.wizardry.util.WizardryUtilities; +import electroblob.wizardry.WizardryUtilities; +import electroblob.wizardry.client.particle.EntitySparkleFX; +import net.minecraft.client.Minecraft; import net.minecraft.entity.player.EntityPlayer; import net.minecraft.item.EnumAction; import net.minecraft.item.ItemStack; -import net.minecraft.util.EnumHand; +import net.minecraft.nbt.NBTTagCompound; import net.minecraft.world.World; public class ConjureSword extends Spell { public ConjureSword() { - super(Tier.APPRENTICE, 25, Element.SORCERY, "conjure_sword", SpellType.UTILITY, 50, EnumAction.BOW, false); + super(EnumTier.APPRENTICE, 25, EnumElement.SORCERY, "conjure_sword", EnumSpellType.UTILITY, 50, EnumAction.bow, false); } @Override - public boolean cast(World world, EntityPlayer caster, EnumHand hand, int ticksInUse, SpellModifiers modifiers) { + public boolean cast(World world, EntityPlayer caster, int ticksInUse, float damageMultiplier, float rangeMultiplier, float durationMultiplier, float blastMultiplier) { - ItemStack sword = new ItemStack(WizardryItems.spectral_sword); + ItemStack sword = new ItemStack(Wizardry.spectralSword); - IConjuredItem.setDurationMultiplier(sword, modifiers.get(WizardryItems.duration_upgrade)); + sword.stackTagCompound = new NBTTagCompound(); + sword.stackTagCompound.setFloat("durationMultiplier", durationMultiplier); - if(!WizardryUtilities.doesPlayerHaveItem(caster, WizardryItems.spectral_sword) && ConjureBow.conjureItemInInventory(caster, sword)){ + if(!caster.inventory.hasItem(Wizardry.spectralSword) && caster.inventory.addItemStackToInventory(sword)){ for(int i=0; i<10; i++){ double x1 = (double)((float)caster.posX + world.rand.nextFloat()*2 - 1.0F); double y1 = (double)((float)WizardryUtilities.getPlayerEyesPos(caster) - 0.5F + world.rand.nextFloat()); double z1 = (double)((float)caster.posZ + world.rand.nextFloat()*2 - 1.0F); if(world.isRemote){ - Wizardry.proxy.spawnParticle(WizardryParticleType.SPARKLE, world, x1, y1, z1, 0, 0.1F, 0, 48 + world.rand.nextInt(12), 0.7f, 0.9f, 1.0f); + Wizardry.proxy.spawnParticle(EnumParticleType.SPARKLE, world, x1, y1, z1, 0, 0.1F, 0, 48 + world.rand.nextInt(12), 0.7f, 0.9f, 1.0f); } } - WizardryUtilities.playSoundAtPlayer(caster, WizardrySounds.SPELL_CONJURATION, 1.0f, 1.0f); + ExtendedPlayer.get(caster).conjuredSwordDuration = 0; + world.playSoundAtEntity(caster, "wizardry:aura", 1.0f, 1.0f); return true; } return false; diff --git a/src/main/java/electroblob/wizardry/spell/CureEffects.java b/src/main/java/electroblob/wizardry/spell/CureEffects.java index 585a3946..28aacc89 100644 --- a/src/main/java/electroblob/wizardry/spell/CureEffects.java +++ b/src/main/java/electroblob/wizardry/spell/CureEffects.java @@ -1,63 +1,57 @@ package electroblob.wizardry.spell; +import electroblob.wizardry.EnumElement; +import electroblob.wizardry.EnumParticleType; +import electroblob.wizardry.EnumSpellType; +import electroblob.wizardry.EnumTier; import electroblob.wizardry.Wizardry; -import electroblob.wizardry.constants.Element; -import electroblob.wizardry.constants.SpellType; -import electroblob.wizardry.constants.Tier; -import electroblob.wizardry.registry.WizardrySounds; -import electroblob.wizardry.util.SpellModifiers; -import electroblob.wizardry.util.WizardryParticleType; -import electroblob.wizardry.util.WizardryUtilities; +import electroblob.wizardry.WizardryUtilities; import net.minecraft.entity.EntityLiving; import net.minecraft.entity.EntityLivingBase; import net.minecraft.entity.player.EntityPlayer; import net.minecraft.item.EnumAction; -import net.minecraft.util.EnumHand; import net.minecraft.world.World; public class CureEffects extends Spell { public CureEffects() { - super(Tier.APPRENTICE, 25, Element.HEALING, "cure_effects", SpellType.DEFENCE, 40, EnumAction.BOW, false); + super(EnumTier.APPRENTICE, 25, EnumElement.HEALING, "cure_effects", EnumSpellType.DEFENCE, 40, EnumAction.bow, false); } @Override - public boolean cast(World world, EntityPlayer caster, EnumHand hand, int ticksInUse, SpellModifiers modifiers) { - - if(world.isRemote){ + public boolean cast(World world, EntityPlayer caster, int ticksInUse, float damageMultiplier, float rangeMultiplier, float durationMultiplier, float blastMultiplier) { + + if(!caster.getActivePotionEffects().isEmpty()){ + caster.clearActivePotions(); for(int i=0; i<10; i++){ double x1 = (double)((float)caster.posX + world.rand.nextFloat()*2 - 1.0F); double y1 = (double)((float)WizardryUtilities.getPlayerEyesPos(caster) - 0.5F + world.rand.nextFloat()); double z1 = (double)((float)caster.posZ + world.rand.nextFloat()*2 - 1.0F); - Wizardry.proxy.spawnParticle(WizardryParticleType.SPARKLE, world, x1, y1, z1, 0, 0.1F, 0, 48 + world.rand.nextInt(12), 0.6f, 0.6f, 1.0f); + if(world.isRemote){ + Wizardry.proxy.spawnParticle(EnumParticleType.SPARKLE, world, x1, y1, z1, 0, 0.1F, 0, 48 + world.rand.nextInt(12), 0.6f, 0.6f, 1.0f); + } } - } - - if(!caster.getActivePotionEffects().isEmpty()){ - caster.clearActivePotions(); - WizardryUtilities.playSoundAtPlayer(caster, WizardrySounds.SPELL_HEAL, 0.7F, world.rand.nextFloat() * 0.4F + 1.0F); + world.playSoundAtEntity(caster, "wizardry:heal", 0.7F, world.rand.nextFloat() * 0.4F + 1.0F); return true; } - // Fixes the sound not playing in first person. - if(world.isRemote) WizardryUtilities.playSoundAtPlayer(caster, WizardrySounds.SPELL_HEAL, 0.7F, world.rand.nextFloat() * 0.4F + 1.0F); return false; } @Override - public boolean cast(World world, EntityLiving caster, EnumHand hand, int ticksInUse, EntityLivingBase target, SpellModifiers modifiers){ + public boolean cast(World world, EntityLiving caster, EntityLivingBase target, float damageMultiplier, float rangeMultiplier, float durationMultiplier, float blastMultiplier){ if(!caster.getActivePotionEffects().isEmpty()){ caster.clearActivePotions(); - if(world.isRemote){ - for(int i=0; i<10; i++){ - double x1 = (double)((float)caster.posX + world.rand.nextFloat()*2 - 1.0F); - double y1 = (double)((float)caster.posY + caster.getEyeHeight() - 0.5F + world.rand.nextFloat()); - double z1 = (double)((float)caster.posZ + world.rand.nextFloat()*2 - 1.0F); - Wizardry.proxy.spawnParticle(WizardryParticleType.SPARKLE, world, x1, y1, z1, 0, 0.1F, 0, 48 + world.rand.nextInt(12), 0.6f, 0.6f, 1.0f); + for(int i=0; i<10; i++){ + double x1 = (double)((float)caster.posX + world.rand.nextFloat()*2 - 1.0F); + double y1 = (double)((float)caster.posY + caster.getEyeHeight() - 0.5F + world.rand.nextFloat()); + double z1 = (double)((float)caster.posZ + world.rand.nextFloat()*2 - 1.0F); + if(world.isRemote){ + Wizardry.proxy.spawnParticle(EnumParticleType.SPARKLE, world, x1, y1, z1, 0, 0.1F, 0, 48 + world.rand.nextInt(12), 0.6f, 0.6f, 1.0f); } } - caster.playSound(WizardrySounds.SPELL_HEAL, 0.7F, world.rand.nextFloat() * 0.4F + 1.0F); + world.playSoundAtEntity(caster, "wizardry:heal", 0.7F, world.rand.nextFloat() * 0.4F + 1.0F); return true; } diff --git a/src/main/java/electroblob/wizardry/spell/CurseOfSoulbinding.java b/src/main/java/electroblob/wizardry/spell/CurseOfSoulbinding.java index 3a2bc261..c1aec3b8 100644 --- a/src/main/java/electroblob/wizardry/spell/CurseOfSoulbinding.java +++ b/src/main/java/electroblob/wizardry/spell/CurseOfSoulbinding.java @@ -1,74 +1,58 @@ package electroblob.wizardry.spell; -import electroblob.wizardry.WizardData; +import electroblob.wizardry.EnumElement; +import electroblob.wizardry.EnumParticleType; +import electroblob.wizardry.EnumSpellType; +import electroblob.wizardry.EnumTier; +import electroblob.wizardry.ExtendedPlayer; import electroblob.wizardry.Wizardry; -import electroblob.wizardry.constants.Element; -import electroblob.wizardry.constants.SpellType; -import electroblob.wizardry.constants.Tier; -import electroblob.wizardry.registry.WizardryItems; -import electroblob.wizardry.util.IElementalDamage; -import electroblob.wizardry.util.SpellModifiers; -import electroblob.wizardry.util.WizardryParticleType; -import electroblob.wizardry.util.WizardryUtilities; +import electroblob.wizardry.WizardryUtilities; +import net.minecraft.entity.EntityLiving; import net.minecraft.entity.EntityLivingBase; +import net.minecraft.entity.boss.EntityWither; +import net.minecraft.entity.monster.EntitySkeleton; import net.minecraft.entity.player.EntityPlayer; -import net.minecraft.init.SoundEvents; import net.minecraft.item.EnumAction; -import net.minecraft.util.EnumHand; -import net.minecraft.util.math.RayTraceResult; -import net.minecraft.util.math.Vec3d; +import net.minecraft.potion.PotionEffect; +import net.minecraft.util.MovingObjectPosition; +import net.minecraft.util.MovingObjectPosition.MovingObjectType; +import net.minecraft.util.Vec3; import net.minecraft.world.World; -import net.minecraftforge.event.entity.living.LivingHurtEvent; -import net.minecraftforge.fml.common.Mod; -import net.minecraftforge.fml.common.eventhandler.SubscribeEvent; -@Mod.EventBusSubscriber public class CurseOfSoulbinding extends Spell { public CurseOfSoulbinding() { - super(Tier.ADVANCED, 35, Element.NECROMANCY, "curse_of_soulbinding", SpellType.ATTACK, 100, EnumAction.NONE, false); + super(EnumTier.ADVANCED, 35, EnumElement.NECROMANCY, "curse_of_soulbinding", EnumSpellType.ATTACK, 100, EnumAction.none, false); } @Override - public boolean cast(World world, EntityPlayer caster, EnumHand hand, int ticksInUse, SpellModifiers modifiers) { + public boolean cast(World world, EntityPlayer caster, int ticksInUse, float damageMultiplier, float rangeMultiplier, float durationMultiplier, float blastMultiplier) { - Vec3d look = caster.getLookVec(); + Vec3 look = caster.getLookVec(); - RayTraceResult rayTrace = WizardryUtilities.standardEntityRayTrace(world, caster, 10*modifiers.get(WizardryItems.range_upgrade)); + MovingObjectPosition rayTrace = WizardryUtilities.standardEntityRayTrace(world, caster, 10*rangeMultiplier); - if(rayTrace != null && rayTrace.typeOfHit == RayTraceResult.Type.ENTITY && rayTrace.entityHit instanceof EntityLivingBase && WizardData.get(caster) != null){ + if(rayTrace != null && rayTrace.typeOfHit == MovingObjectType.ENTITY && rayTrace.entityHit instanceof EntityLivingBase && ExtendedPlayer.get(caster) != null){ EntityLivingBase target = (EntityLivingBase)rayTrace.entityHit; - if(!WizardData.get(caster).soulbind(target)) return false; + ExtendedPlayer.get(caster).soulbind(target); } if(world.isRemote){ - for(int i=1; i<(int)(25*modifiers.get(WizardryItems.range_upgrade)); i+=2){ + for(int i=1; i<(int)(25*rangeMultiplier); i+=2){ // I figured it out! when on client side, entityplayer.posY is at the eyes, not the feet! double x1 = caster.posX + look.xCoord*i/2 + world.rand.nextFloat()/5 - 0.1f; double y1 = WizardryUtilities.getPlayerEyesPos(caster) - 0.4f + look.yCoord*i/2 + world.rand.nextFloat()/5 - 0.1f; double z1 = caster.posZ + look.zCoord*i/2 + world.rand.nextFloat()/5 - 0.1f; //world.spawnParticle("mobSpell", x1, y1, z1, -1*look.xCoord, -1*look.yCoord, -1*look.zCoord); - Wizardry.proxy.spawnParticle(WizardryParticleType.DARK_MAGIC, world, x1, y1, z1, 0.0d, 0.0d, 0.0d, 0, 0.4f, 0.0f, 0.0f); - Wizardry.proxy.spawnParticle(WizardryParticleType.DARK_MAGIC, world, x1, y1, z1, 0.0d, 0.0d, 0.0d, 0, 0.1f, 0.0f, 0.0f); - Wizardry.proxy.spawnParticle(WizardryParticleType.SPARKLE, world, x1, y1, z1, 0.0d, 0.0d, 0.0d, 12 + world.rand.nextInt(8), 1.0f, 0.8f, 1.0f); + Wizardry.proxy.spawnParticle(EnumParticleType.DARK_MAGIC, world, x1, y1, z1, 0.0d, 0.0d, 0.0d, 0, 0.4f, 0.0f, 0.0f); + Wizardry.proxy.spawnParticle(EnumParticleType.DARK_MAGIC, world, x1, y1, z1, 0.0d, 0.0d, 0.0d, 0, 0.1f, 0.0f, 0.0f); + Wizardry.proxy.spawnParticle(EnumParticleType.SPARKLE, world, x1, y1, z1, 0.0d, 0.0d, 0.0d, 12 + world.rand.nextInt(8), 1.0f, 0.8f, 1.0f); } } - caster.swingArm(hand); - WizardryUtilities.playSoundAtPlayer(caster, SoundEvents.ENTITY_WITHER_SPAWN, 1.0F, world.rand.nextFloat() * 0.2F + 1.0F); + caster.swingItem(); + world.playSoundAtEntity(caster, "mob.wither.spawn", 1.0F, world.rand.nextFloat() * 0.2F + 1.0F); return true; } - - @SubscribeEvent - public static void onLivingHurtEvent(LivingHurtEvent event){ - - if(!event.getEntity().worldObj.isRemote && event.getEntityLiving() instanceof EntityPlayer && !event.getSource().isUnblockable() - && !(event.getSource() instanceof IElementalDamage && ((IElementalDamage)event.getSource()).isRetaliatory())){ - WizardData data = WizardData.get((EntityPlayer)event.getEntityLiving()); - if(data != null){ - data.damageAllSoulboundCreatures(event.getAmount()); - } - } - } } diff --git a/src/main/java/electroblob/wizardry/spell/DarknessOrb.java b/src/main/java/electroblob/wizardry/spell/DarknessOrb.java index d55ff4b0..19f6e3e3 100644 --- a/src/main/java/electroblob/wizardry/spell/DarknessOrb.java +++ b/src/main/java/electroblob/wizardry/spell/DarknessOrb.java @@ -1,23 +1,19 @@ package electroblob.wizardry.spell; -import electroblob.wizardry.constants.Element; -import electroblob.wizardry.constants.SpellType; -import electroblob.wizardry.constants.Tier; +import electroblob.wizardry.EnumElement; +import electroblob.wizardry.EnumSpellType; +import electroblob.wizardry.EnumTier; import electroblob.wizardry.entity.projectile.EntityDarknessOrb; -import electroblob.wizardry.util.SpellModifiers; -import electroblob.wizardry.util.WizardryUtilities; import net.minecraft.entity.EntityLiving; import net.minecraft.entity.EntityLivingBase; import net.minecraft.entity.player.EntityPlayer; -import net.minecraft.init.SoundEvents; import net.minecraft.item.EnumAction; -import net.minecraft.util.EnumHand; import net.minecraft.world.World; public class DarknessOrb extends Spell { public DarknessOrb() { - super(Tier.ADVANCED, 20, Element.NECROMANCY, "darkness_orb", SpellType.ATTACK, 20, EnumAction.NONE, false); + super(EnumTier.ADVANCED, 20, EnumElement.NECROMANCY, "darkness_orb", EnumSpellType.ATTACK, 20, EnumAction.none, false); } @Override @@ -26,31 +22,31 @@ public class DarknessOrb extends Spell { } @Override - public boolean cast(World world, EntityPlayer caster, EnumHand hand, int ticksInUse, SpellModifiers modifiers) { + public boolean cast(World world, EntityPlayer caster, int ticksInUse, float damageMultiplier, float rangeMultiplier, float durationMultiplier, float blastMultiplier) { if(!world.isRemote){ - EntityDarknessOrb darknessorb = new EntityDarknessOrb(world, caster, modifiers.get(SpellModifiers.DAMAGE)); + EntityDarknessOrb darknessorb = new EntityDarknessOrb(world, caster, damageMultiplier); world.spawnEntityInWorld(darknessorb); + world.playSoundAtEntity(caster, "mob.wither.shoot", 1.0F, 0.4F / (world.rand.nextFloat() * 0.4F + 0.8F)); } - WizardryUtilities.playSoundAtPlayer(caster, SoundEvents.ENTITY_WITHER_SHOOT, 1.0F, 0.4F / (world.rand.nextFloat() * 0.4F + 0.8F)); - caster.swingArm(hand); + caster.swingItem(); return true; } @Override - public boolean cast(World world, EntityLiving caster, EnumHand hand, int ticksInUse, EntityLivingBase target, SpellModifiers modifiers){ + public boolean cast(World world, EntityLiving caster, EntityLivingBase target, float damageMultiplier, float rangeMultiplier, float durationMultiplier, float blastMultiplier){ if(target != null){ if(!world.isRemote){ - EntityDarknessOrb darknessorb = new EntityDarknessOrb(world, caster, modifiers.get(SpellModifiers.DAMAGE)); + EntityDarknessOrb darknessorb = new EntityDarknessOrb(world, caster, damageMultiplier); darknessorb.directTowards(target, 0.5f); world.spawnEntityInWorld(darknessorb); + world.playSoundAtEntity(caster, "mob.wither.shoot", 1.0F, 0.4F / (world.rand.nextFloat() * 0.4F + 0.8F)); } - - caster.playSound(SoundEvents.ENTITY_WITHER_SHOOT, 1.0F, 0.4F / (world.rand.nextFloat() * 0.4F + 0.8F)); - caster.swingArm(hand); + + caster.swingItem(); return true; } diff --git a/src/main/java/electroblob/wizardry/spell/Darkvision.java b/src/main/java/electroblob/wizardry/spell/Darkvision.java index e426688f..11a2ee46 100644 --- a/src/main/java/electroblob/wizardry/spell/Darkvision.java +++ b/src/main/java/electroblob/wizardry/spell/Darkvision.java @@ -1,41 +1,39 @@ package electroblob.wizardry.spell; +import electroblob.wizardry.EnumElement; +import electroblob.wizardry.EnumParticleType; +import electroblob.wizardry.EnumSpellType; +import electroblob.wizardry.EnumTier; import electroblob.wizardry.Wizardry; -import electroblob.wizardry.constants.Element; -import electroblob.wizardry.constants.SpellType; -import electroblob.wizardry.constants.Tier; -import electroblob.wizardry.registry.WizardryItems; -import electroblob.wizardry.registry.WizardrySounds; -import electroblob.wizardry.util.SpellModifiers; -import electroblob.wizardry.util.WizardryParticleType; -import electroblob.wizardry.util.WizardryUtilities; +import electroblob.wizardry.WizardryUtilities; +import net.minecraft.entity.EntityLiving; +import net.minecraft.entity.EntityLivingBase; import net.minecraft.entity.player.EntityPlayer; -import net.minecraft.init.MobEffects; import net.minecraft.item.EnumAction; +import net.minecraft.potion.Potion; import net.minecraft.potion.PotionEffect; -import net.minecraft.util.EnumHand; import net.minecraft.world.World; public class Darkvision extends Spell { public Darkvision() { - super(Tier.APPRENTICE, 20, Element.EARTH, "darkvision", SpellType.UTILITY, 40, EnumAction.BOW, false); + super(EnumTier.APPRENTICE, 20, EnumElement.EARTH, "darkvision", EnumSpellType.UTILITY, 40, EnumAction.bow, false); } @Override - public boolean cast(World world, EntityPlayer caster, EnumHand hand, int ticksInUse, SpellModifiers modifiers) { + public boolean cast(World world, EntityPlayer caster, int ticksInUse, float damageMultiplier, float rangeMultiplier, float durationMultiplier, float blastMultiplier) { - caster.addPotionEffect(new PotionEffect(MobEffects.NIGHT_VISION, (int)(900*modifiers.get(WizardryItems.duration_upgrade)), 0, false, false)); + caster.addPotionEffect(new PotionEffect(Potion.nightVision.id, (int)(900*durationMultiplier), 0, true)); if(world.isRemote){ for(int i=0; i<10; i++){ double x1 = (double)((float)caster.posX + world.rand.nextFloat()*2 - 1.0F); double y1 = (double)((float)WizardryUtilities.getPlayerEyesPos(caster) - 0.5F + world.rand.nextFloat()); double z1 = (double)((float)caster.posZ + world.rand.nextFloat()*2 - 1.0F); - Wizardry.proxy.spawnParticle(WizardryParticleType.SPARKLE, world, x1, y1, z1, 0, 0.1F, 0, 48 + world.rand.nextInt(12), 0.0f, 0.4f, 0.7f); + Wizardry.proxy.spawnParticle(EnumParticleType.SPARKLE, world, x1, y1, z1, 0, 0.1F, 0, 48 + world.rand.nextInt(12), 0.0f, 0.4f, 0.7f); } } - WizardryUtilities.playSoundAtPlayer(caster, WizardrySounds.SPELL_HEAL, 0.7F, world.rand.nextFloat() * 0.4F + 1.0F); + world.playSoundAtEntity(caster, "wizardry:heal", 0.7F, world.rand.nextFloat() * 0.4F + 1.0F); return true; } diff --git a/src/main/java/electroblob/wizardry/spell/Dart.java b/src/main/java/electroblob/wizardry/spell/Dart.java index 3babbaa4..954924bc 100644 --- a/src/main/java/electroblob/wizardry/spell/Dart.java +++ b/src/main/java/electroblob/wizardry/spell/Dart.java @@ -1,24 +1,19 @@ package electroblob.wizardry.spell; -import electroblob.wizardry.constants.Element; -import electroblob.wizardry.constants.SpellType; -import electroblob.wizardry.constants.Tier; +import electroblob.wizardry.EnumElement; +import electroblob.wizardry.EnumSpellType; +import electroblob.wizardry.EnumTier; import electroblob.wizardry.entity.projectile.EntityDart; -import electroblob.wizardry.registry.WizardryItems; -import electroblob.wizardry.util.SpellModifiers; -import electroblob.wizardry.util.WizardryUtilities; import net.minecraft.entity.EntityLiving; import net.minecraft.entity.EntityLivingBase; import net.minecraft.entity.player.EntityPlayer; -import net.minecraft.init.SoundEvents; import net.minecraft.item.EnumAction; -import net.minecraft.util.EnumHand; import net.minecraft.world.World; public class Dart extends Spell { public Dart() { - super(Tier.BASIC, 5, Element.EARTH, "dart", SpellType.ATTACK, 10, EnumAction.NONE, false); + super(EnumTier.BASIC, 5, EnumElement.EARTH, "dart", EnumSpellType.ATTACK, 10, EnumAction.none, false); } @Override @@ -27,28 +22,28 @@ public class Dart extends Spell { } @Override - public boolean cast(World world, EntityPlayer caster, EnumHand hand, int ticksInUse, SpellModifiers modifiers) { + public boolean cast(World world, EntityPlayer caster, int ticksInUse, float damageMultiplier, float rangeMultiplier, float durationMultiplier, float blastMultiplier) { if(!world.isRemote){ - EntityDart dart = new EntityDart(world, caster, 2*modifiers.get(WizardryItems.range_upgrade), modifiers.get(SpellModifiers.DAMAGE)); + EntityDart dart = new EntityDart(world, caster, 2*rangeMultiplier, damageMultiplier); world.spawnEntityInWorld(dart); } - caster.swingArm(hand); - WizardryUtilities.playSoundAtPlayer(caster, SoundEvents.ENTITY_ARROW_SHOOT, 0.5F, 0.4F / (world.rand.nextFloat() * 0.4F + 0.8F)); + caster.swingItem(); + world.playSoundAtEntity(caster, "random.bow", 0.5F, 0.4F / (world.rand.nextFloat() * 0.4F + 0.8F)); return true; } @Override - public boolean cast(World world, EntityLiving caster, EnumHand hand, int ticksInUse, EntityLivingBase target, SpellModifiers modifiers){ + public boolean cast(World world, EntityLiving caster, EntityLivingBase target, float damageMultiplier, float rangeMultiplier, float durationMultiplier, float blastMultiplier){ if(target != null){ if(!world.isRemote){ - EntityDart dart = new EntityDart(world, caster, target, 2*modifiers.get(WizardryItems.range_upgrade), 2, modifiers.get(SpellModifiers.DAMAGE)); + EntityDart dart = new EntityDart(world, caster, target, 2*rangeMultiplier, 2, damageMultiplier); world.spawnEntityInWorld(dart); } - caster.swingArm(hand); - caster.playSound(SoundEvents.ENTITY_ARROW_SHOOT, 0.5F, 0.4F / (world.rand.nextFloat() * 0.4F + 0.8F)); + caster.swingItem(); + world.playSoundAtEntity(caster, "random.bow", 0.5F, 0.4F / (world.rand.nextFloat() * 0.4F + 0.8F)); return true; } diff --git a/src/main/java/electroblob/wizardry/spell/Decay.java b/src/main/java/electroblob/wizardry/spell/Decay.java index aa6b899f..a7ba1c6f 100644 --- a/src/main/java/electroblob/wizardry/spell/Decay.java +++ b/src/main/java/electroblob/wizardry/spell/Decay.java @@ -1,27 +1,28 @@ package electroblob.wizardry.spell; -import electroblob.wizardry.constants.Element; -import electroblob.wizardry.constants.SpellType; -import electroblob.wizardry.constants.Tier; +import electroblob.wizardry.EnumElement; +import electroblob.wizardry.EnumParticleType; +import electroblob.wizardry.EnumSpellType; +import electroblob.wizardry.EnumTier; +import electroblob.wizardry.Wizardry; +import electroblob.wizardry.WizardryUtilities; +import electroblob.wizardry.entity.EntityArc; import electroblob.wizardry.entity.construct.EntityDecay; -import electroblob.wizardry.registry.WizardryItems; -import electroblob.wizardry.util.SpellModifiers; -import electroblob.wizardry.util.WizardryUtilities; +import electroblob.wizardry.entity.living.EntitySkeletonMinion; import net.minecraft.entity.EntityLiving; import net.minecraft.entity.EntityLivingBase; +import net.minecraft.entity.monster.EntityCreeper; import net.minecraft.entity.player.EntityPlayer; -import net.minecraft.init.SoundEvents; import net.minecraft.item.EnumAction; -import net.minecraft.util.EnumHand; -import net.minecraft.util.math.BlockPos; -import net.minecraft.util.math.MathHelper; -import net.minecraft.util.math.RayTraceResult; +import net.minecraft.util.MathHelper; +import net.minecraft.util.MovingObjectPosition; +import net.minecraft.util.MovingObjectPosition.MovingObjectType; import net.minecraft.world.World; public class Decay extends Spell { public Decay(){ - super(Tier.ADVANCED, 50, Element.NECROMANCY, "decay", SpellType.ATTACK, 200, EnumAction.NONE, false); + super(EnumTier.ADVANCED, 50, EnumElement.NECROMANCY, "decay", EnumSpellType.ATTACK, 200, EnumAction.none, false); } @Override @@ -30,29 +31,35 @@ public class Decay extends Spell { } @Override - public boolean cast(World world, EntityPlayer caster, EnumHand hand, int ticksInUse, SpellModifiers modifiers) { + public boolean cast(World world, EntityPlayer caster, int ticksInUse, float damageMultiplier, float rangeMultiplier, float durationMultiplier, float blastMultiplier) { - RayTraceResult rayTrace = WizardryUtilities.rayTrace(12*modifiers.get(WizardryItems.range_upgrade), world, caster, false); + MovingObjectPosition rayTrace = WizardryUtilities.rayTrace(12*rangeMultiplier, world, caster, false); - if(rayTrace != null && rayTrace.typeOfHit == RayTraceResult.Type.BLOCK){ + if(rayTrace != null && rayTrace.typeOfHit == MovingObjectType.BLOCK){ - BlockPos pos = rayTrace.getBlockPos(); + int x = rayTrace.blockX; + int y = rayTrace.blockY; + int z = rayTrace.blockZ; - if(world.getBlockState(pos.up()).isNormalCube()) return false; + if(world.getBlock(x, y+1, z).isNormalCube()) return false; if(!world.isRemote){ - world.spawnEntityInWorld(new EntityDecay(world, pos.getX()+0.5, pos.getY()+1, pos.getZ()+0.5, caster)); + world.spawnEntityInWorld(new EntityDecay(world, x+0.5, y+1, z+0.5, caster)); for(int i=0;i<5;i++){ - BlockPos pos1 = WizardryUtilities.findNearbyFloorSpace(caster, 2, 6); - if(pos1 == null) break; - world.spawnEntityInWorld(new EntityDecay(world, pos1.getX()+0.5, pos1.getY(), pos1.getZ()+0.5, caster)); + double x1 = x + world.rand.nextDouble()*4 - 2; + double z1 = z + world.rand.nextDouble()*4 - 2; + // Allows for height variation. + if(world.getTopSolidOrLiquidBlock((int)x1, (int)z1) - y < 6){ + double y1 = Math.max(y, world.getTopSolidOrLiquidBlock((int)x1, (int)z1)); + world.spawnEntityInWorld(new EntityDecay(world, x1+0.5, y1, z1+0.5, caster)); + } } } - WizardryUtilities.playSoundAtPlayer(caster, SoundEvents.ENTITY_WITHER_SHOOT, 1.0F, world.rand.nextFloat() * 0.2F + 1.0F); - caster.swingArm(hand); + world.playSoundAtEntity(caster, "mob.wither.shoot", 1.0F, world.rand.nextFloat() * 0.2F + 1.0F); + caster.swingItem(); return true; } @@ -60,29 +67,33 @@ public class Decay extends Spell { } @Override - public boolean cast(World world, EntityLiving caster, EnumHand hand, int ticksInUse, EntityLivingBase target, SpellModifiers modifiers){ + public boolean cast(World world, EntityLiving caster, EntityLivingBase target, float damageMultiplier, float rangeMultiplier, float durationMultiplier, float blastMultiplier){ if(target != null){ int x = MathHelper.floor_double(target.posX); - int y = (int)(int)target.getEntityBoundingBox().minY; + int y = (int)(int)target.boundingBox.minY; int z = MathHelper.floor_double(target.posZ); - if(world.getBlockState(new BlockPos(x, y, z)).isNormalCube()) return false; + if(world.getBlock(x, y, z).isNormalCube()) return false; if(!world.isRemote){ world.spawnEntityInWorld(new EntityDecay(world, x+0.5, y+1, z+0.5, caster)); for(int i=0;i<5;i++){ - BlockPos pos = WizardryUtilities.findNearbyFloorSpace(caster, 2, 6); - if(pos == null) break; - world.spawnEntityInWorld(new EntityDecay(world, pos.getX()+0.5, pos.getY(), pos.getZ()+0.5, caster)); + double x1 = x + world.rand.nextDouble()*4 - 2; + double z1 = z + world.rand.nextDouble()*4 - 2; + // Allows for height variation. + if(world.getTopSolidOrLiquidBlock((int)x1, (int)z1) - y < 6){ + double y1 = Math.max(y, world.getTopSolidOrLiquidBlock((int)x1, (int)z1)); + world.spawnEntityInWorld(new EntityDecay(world, x1+0.5, y1, z1+0.5, caster)); + } } } - caster.playSound(SoundEvents.ENTITY_WITHER_SHOOT, 1.0F, world.rand.nextFloat() * 0.2F + 1.0F); - caster.swingArm(hand); + world.playSoundAtEntity(caster, "mob.wither.shoot", 1.0F, world.rand.nextFloat() * 0.2F + 1.0F); + caster.swingItem(); return true; } diff --git a/src/main/java/electroblob/wizardry/spell/Decoy.java b/src/main/java/electroblob/wizardry/spell/Decoy.java index dd4d6cbb..b6c00222 100644 --- a/src/main/java/electroblob/wizardry/spell/Decoy.java +++ b/src/main/java/electroblob/wizardry/spell/Decoy.java @@ -1,27 +1,26 @@ package electroblob.wizardry.spell; -import electroblob.wizardry.constants.Element; -import electroblob.wizardry.constants.SpellType; -import electroblob.wizardry.constants.Tier; +import electroblob.wizardry.EnumElement; +import electroblob.wizardry.EnumSpellType; +import electroblob.wizardry.EnumTier; +import electroblob.wizardry.Wizardry; +import electroblob.wizardry.WizardryUtilities; import electroblob.wizardry.entity.living.EntityDecoy; -import electroblob.wizardry.registry.WizardrySounds; -import electroblob.wizardry.util.SpellModifiers; -import electroblob.wizardry.util.WizardryUtilities; +import net.minecraft.entity.EntityCreature; import net.minecraft.entity.EntityLiving; import net.minecraft.entity.EntityLivingBase; import net.minecraft.entity.player.EntityPlayer; import net.minecraft.item.EnumAction; -import net.minecraft.util.EnumHand; import net.minecraft.world.World; public class Decoy extends Spell { public Decoy() { - super(Tier.ADVANCED, 40, Element.SORCERY, "decoy", SpellType.UTILITY, 200, EnumAction.BOW, false); + super(EnumTier.ADVANCED, 40, EnumElement.SORCERY, "decoy", EnumSpellType.UTILITY, 200, EnumAction.bow, false); } @Override - public boolean cast(World world, EntityPlayer caster, EnumHand hand, int ticksInUse, SpellModifiers modifiers) { + public boolean cast(World world, EntityPlayer caster, int ticksInUse, float damageMultiplier, float rangeMultiplier, float durationMultiplier, float blastMultiplier) { // Determines whether the caster moves left and the decoy moves right, or vice versa. // Uses the synchronised entity id to ensure it is consistent on client and server, but not always the same. double splitSpeed = caster.getEntityId() % 2 == 0 ? 0.3 : -0.3; @@ -31,26 +30,29 @@ public class Decoy extends Spell { decoy.setLocationAndAngles(caster.posX, caster.posY, caster.posZ, caster.rotationYaw, caster.rotationPitch); decoy.addVelocity(-caster.getLookVec().zCoord*splitSpeed, 0, caster.getLookVec().xCoord*splitSpeed); // Ignores the show names setting, since this would allow a player to easily detect a decoy - decoy.setCustomNameTag(caster.getName()); + decoy.setCustomNameTag(caster.getCommandSenderName()); world.spawnEntityInWorld(decoy); // Tricks any mobs that are targeting the caster into targeting the decoy instead. for(EntityLiving creature : WizardryUtilities.getEntitiesWithinRadius(16, caster.posX, caster.posY, caster.posZ, world, EntityLiving.class)){ // More likely to trick mobs the higher the damage multiplier. Starts off at 50%. - if(world.rand.nextInt((int)(6*modifiers.get(SpellModifiers.DAMAGE))) < 3){ + if(world.rand.nextInt((int)(6*damageMultiplier)) < 3){ + // New AI if(creature.getAttackTarget() == caster) creature.setAttackTarget(decoy); + // Old AI + if(creature instanceof EntityCreature && ((EntityCreature)creature).getEntityToAttack() == caster) ((EntityCreature)creature).setTarget(decoy); } } } caster.addVelocity(caster.getLookVec().zCoord*splitSpeed, 0, -caster.getLookVec().xCoord*splitSpeed); - WizardryUtilities.playSoundAtPlayer(caster, WizardrySounds.SPELL_CONJURATION, 1.0F, 0.4F / (world.rand.nextFloat() * 0.4F + 0.8F)); + world.playSoundAtEntity(caster, "wizardry:aura", 1.0F, 0.4F / (world.rand.nextFloat() * 0.4F + 0.8F)); return true; } @Override - public boolean cast(World world, EntityLiving caster, EnumHand hand, int ticksInUse, EntityLivingBase target, SpellModifiers modifiers) { + public boolean cast(World world, EntityLiving caster, EntityLivingBase target, float damageMultiplier, float rangeMultiplier, float durationMultiplier, float blastMultiplier) { // Determines whether the caster moves left and the decoy moves right, or vice versa. double splitSpeed = world.rand.nextBoolean() ? 0.3 : -0.3; @@ -64,14 +66,17 @@ public class Decoy extends Spell { // Tricks any mobs that are targeting the caster into targeting the decoy instead. for(EntityLiving creature : WizardryUtilities.getEntitiesWithinRadius(16, caster.posX, caster.posY, caster.posZ, world, EntityLiving.class)){ // More likely to trick mobs the higher the damage multiplier. Starts off at 50%. - if(world.rand.nextInt((int)(6*modifiers.get(SpellModifiers.DAMAGE))) < 3){ + if(world.rand.nextInt((int)(6*damageMultiplier)) < 3){ + // New AI if(creature.getAttackTarget() == caster) creature.setAttackTarget(decoy); + // Old AI + if(creature instanceof EntityCreature && ((EntityCreature)creature).getEntityToAttack() == caster) ((EntityCreature)creature).setTarget(decoy); } } } caster.addVelocity(caster.getLookVec().zCoord*splitSpeed, 0, -caster.getLookVec().xCoord*splitSpeed); - caster.playSound(WizardrySounds.SPELL_CONJURATION, 1.0F, 0.4F / (world.rand.nextFloat() * 0.4F + 0.8F)); + world.playSoundAtEntity(caster, "wizardry:aura", 1.0F, 0.4F / (world.rand.nextFloat() * 0.4F + 0.8F)); return true; } diff --git a/src/main/java/electroblob/wizardry/spell/Detonate.java b/src/main/java/electroblob/wizardry/spell/Detonate.java index 4a0c02c3..f2c66f9c 100644 --- a/src/main/java/electroblob/wizardry/spell/Detonate.java +++ b/src/main/java/electroblob/wizardry/spell/Detonate.java @@ -2,95 +2,91 @@ package electroblob.wizardry.spell; import java.util.List; -import electroblob.wizardry.constants.Element; -import electroblob.wizardry.constants.SpellType; -import electroblob.wizardry.constants.Tier; -import electroblob.wizardry.registry.WizardryItems; -import electroblob.wizardry.util.MagicDamage; -import electroblob.wizardry.util.MagicDamage.DamageType; -import electroblob.wizardry.util.SpellModifiers; -import electroblob.wizardry.util.WizardryUtilities; +import electroblob.wizardry.EnumElement; +import electroblob.wizardry.EnumSpellType; +import electroblob.wizardry.EnumTier; +import electroblob.wizardry.MagicDamage; +import electroblob.wizardry.WizardryUtilities; +import electroblob.wizardry.MagicDamage.DamageType; import net.minecraft.entity.EntityLiving; import net.minecraft.entity.EntityLivingBase; import net.minecraft.entity.player.EntityPlayer; -import net.minecraft.init.SoundEvents; import net.minecraft.item.EnumAction; -import net.minecraft.util.EnumHand; -import net.minecraft.util.EnumParticleTypes; -import net.minecraft.util.SoundCategory; -import net.minecraft.util.math.RayTraceResult; +import net.minecraft.util.MovingObjectPosition; +import net.minecraft.util.MovingObjectPosition.MovingObjectType; import net.minecraft.world.World; public class Detonate extends Spell { public Detonate() { - super(Tier.ADVANCED, 45, Element.FIRE, "detonate", SpellType.ATTACK, 50, EnumAction.NONE, false); + super(EnumTier.ADVANCED, 45, EnumElement.FIRE, "detonate", EnumSpellType.ATTACK, 50, EnumAction.none, false); } @Override - public boolean cast(World world, EntityPlayer caster, EnumHand hand, int ticksInUse, SpellModifiers modifiers) { - - RayTraceResult rayTrace = WizardryUtilities.rayTrace(16*modifiers.get(WizardryItems.range_upgrade), world, caster, false); - - if(rayTrace != null && rayTrace.typeOfHit == RayTraceResult.Type.BLOCK){ + public boolean cast(World world, EntityPlayer caster, int ticksInUse, float damageMultiplier, float rangeMultiplier, float durationMultiplier, float blastMultiplier) { + + MovingObjectPosition rayTrace = WizardryUtilities.rayTrace(16*rangeMultiplier, world, caster, false); + + if(rayTrace != null && rayTrace.typeOfHit == MovingObjectType.BLOCK){ if(!world.isRemote){ - List targets = WizardryUtilities.getEntitiesWithinRadius(3.0d*modifiers.get(WizardryItems.blast_upgrade), (rayTrace.hitVec.xCoord+0.5), (rayTrace.hitVec.yCoord+0.5), (rayTrace.hitVec.zCoord+0.5), world); + List targets = WizardryUtilities.getEntitiesWithinRadius(3.0d*blastMultiplier, (rayTrace.blockX+0.5), (rayTrace.blockY+0.5), (rayTrace.blockZ+0.5), world); for(int i=0;i targets = WizardryUtilities.getEntitiesWithinRadius(3.0d, target.posX, target.posY, target.posZ, world); + List targets = WizardryUtilities.getEntitiesWithinRadius(3.0d, target.posX, target.posY, target.posZ, world); for(int i=0;i targets = WizardryUtilities.getEntitiesWithinRadius(5*modifiers.get(WizardryItems.blast_upgrade), caster.posX, caster.posY, caster.posZ, world, EntityPlayer.class); + List targets = WizardryUtilities.getEntitiesWithinRadius(5*blastMultiplier, caster.posX, caster.posY, caster.posZ, world, EntityPlayer.class); for(EntityPlayer target : targets){ if(WizardryUtilities.isPlayerAlly(caster, target) || target == caster){ // Damage multiplier can only ever be 1 or 1.6 for master spells, so there's little point in actually calculating this. - target.addPotionEffect(new PotionEffect(WizardryPotions.font_of_mana, (int)(600*modifiers.get(WizardryItems.duration_upgrade)), modifiers.get(SpellModifiers.DAMAGE) > 1 ? 1 : 0)); + target.addPotionEffect(new PotionEffect(Wizardry.fontOfMana.id, (int)(600*durationMultiplier), damageMultiplier > 1 ? 1 : 0)); } } if(world.isRemote){ - for(int i=0;i<100*modifiers.get(WizardryItems.blast_upgrade);i++){ - double radius = (1 + world.rand.nextDouble()*4)*modifiers.get(WizardryItems.blast_upgrade); + for(int i=0;i<100*blastMultiplier;i++){ + double radius = (1 + world.rand.nextDouble()*4)*blastMultiplier; double angle = world.rand.nextDouble()*Math.PI*2; float hue = world.rand.nextFloat()*0.4f; - Wizardry.proxy.spawnParticle(WizardryParticleType.SPARKLE, world, caster.posX + radius*Math.cos(angle), caster.getEntityBoundingBox().minY, caster.posZ + radius*Math.sin(angle), + Wizardry.proxy.spawnParticle(EnumParticleType.SPARKLE, world, caster.posX + radius*Math.cos(angle), WizardryUtilities.getEntityFeetPos(caster), caster.posZ + radius*Math.sin(angle), 0, 0.03, 0, 50, 1, 1-hue, 0.6f+hue); } } - WizardryUtilities.playSoundAtPlayer(caster, WizardrySounds.SPELL_HEAL, 0.7F, world.rand.nextFloat() * 0.4F + 1.0F); + world.playSoundAtEntity(caster, "wizardry:heal", 0.7F, world.rand.nextFloat() * 0.4F + 1.0F); return true; } diff --git a/src/main/java/electroblob/wizardry/spell/FontOfVitality.java b/src/main/java/electroblob/wizardry/spell/FontOfVitality.java index 1056b526..040eb230 100644 --- a/src/main/java/electroblob/wizardry/spell/FontOfVitality.java +++ b/src/main/java/electroblob/wizardry/spell/FontOfVitality.java @@ -1,32 +1,28 @@ package electroblob.wizardry.spell; +import electroblob.wizardry.EnumElement; +import electroblob.wizardry.EnumParticleType; +import electroblob.wizardry.EnumSpellType; +import electroblob.wizardry.EnumTier; import electroblob.wizardry.Wizardry; -import electroblob.wizardry.constants.Element; -import electroblob.wizardry.constants.SpellType; -import electroblob.wizardry.constants.Tier; -import electroblob.wizardry.registry.WizardryItems; -import electroblob.wizardry.registry.WizardrySounds; -import electroblob.wizardry.util.SpellModifiers; -import electroblob.wizardry.util.WizardryParticleType; -import electroblob.wizardry.util.WizardryUtilities; +import electroblob.wizardry.WizardryUtilities; import net.minecraft.entity.player.EntityPlayer; -import net.minecraft.init.MobEffects; import net.minecraft.item.EnumAction; +import net.minecraft.potion.Potion; import net.minecraft.potion.PotionEffect; -import net.minecraft.util.EnumHand; import net.minecraft.world.World; public class FontOfVitality extends Spell { public FontOfVitality() { - super(Tier.MASTER, 75, Element.HEALING, "font_of_vitality", SpellType.DEFENCE, 300, EnumAction.BOW, false); + super(EnumTier.MASTER, 75, EnumElement.HEALING, "font_of_vitality", EnumSpellType.DEFENCE, 300, EnumAction.bow, false); } @Override - public boolean cast(World world, EntityPlayer caster, EnumHand hand, int ticksInUse, SpellModifiers modifiers) { + public boolean cast(World world, EntityPlayer caster, int ticksInUse, float damageMultiplier, float rangeMultiplier, float durationMultiplier, float blastMultiplier) { - caster.addPotionEffect(new PotionEffect(MobEffects.ABSORPTION, (int)(1200*modifiers.get(WizardryItems.duration_upgrade)), 1, false, false)); - caster.addPotionEffect(new PotionEffect(MobEffects.REGENERATION, (int)(300*modifiers.get(WizardryItems.duration_upgrade)), 1, false, false)); + caster.addPotionEffect(new PotionEffect(Potion.field_76444_x.id, (int)(1200*durationMultiplier), 1, true)); + caster.addPotionEffect(new PotionEffect(Potion.regeneration.id, (int)(300*durationMultiplier), 1, true)); if(world.isRemote){ for(int i=0; i<10; i++){ @@ -34,18 +30,18 @@ public class FontOfVitality extends Spell { double y1 = (double)((float)WizardryUtilities.getPlayerEyesPos(caster) - 0.5F + world.rand.nextFloat()); double z1 = (double)((float)caster.posZ + world.rand.nextFloat()*2 - 1.0F); - Wizardry.proxy.spawnParticle(WizardryParticleType.SPARKLE, world, x1, y1, z1, 0, 0.1F, 0, 48 + world.rand.nextInt(12), 1.0f, 0.6f, 0.7f); + Wizardry.proxy.spawnParticle(EnumParticleType.SPARKLE, world, x1, y1, z1, 0, 0.1F, 0, 48 + world.rand.nextInt(12), 1.0f, 0.6f, 0.7f); x1 = (double)((float)caster.posX + world.rand.nextFloat()*2 - 1.0F); y1 = (double)((float)WizardryUtilities.getPlayerEyesPos(caster) - 0.5F + world.rand.nextFloat()); z1 = (double)((float)caster.posZ + world.rand.nextFloat()*2 - 1.0F); - Wizardry.proxy.spawnParticle(WizardryParticleType.SPARKLE, world, x1, y1, z1, 0, 0.1F, 0, 48 + world.rand.nextInt(12), 1.0f, 0.8f, 0.3f); + Wizardry.proxy.spawnParticle(EnumParticleType.SPARKLE, world, x1, y1, z1, 0, 0.1F, 0, 48 + world.rand.nextInt(12), 1.0f, 0.8f, 0.3f); } } - WizardryUtilities.playSoundAtPlayer(caster, WizardrySounds.SPELL_HEAL, 0.7F, world.rand.nextFloat() * 0.4F + 1.0F); + world.playSoundAtEntity(caster, "wizardry:heal", 0.7F, world.rand.nextFloat() * 0.4F + 1.0F); return true; } diff --git a/src/main/java/electroblob/wizardry/spell/ForceArrow.java b/src/main/java/electroblob/wizardry/spell/ForceArrow.java index 05cd68eb..c1207c93 100644 --- a/src/main/java/electroblob/wizardry/spell/ForceArrow.java +++ b/src/main/java/electroblob/wizardry/spell/ForceArrow.java @@ -1,24 +1,19 @@ package electroblob.wizardry.spell; -import electroblob.wizardry.constants.Element; -import electroblob.wizardry.constants.SpellType; -import electroblob.wizardry.constants.Tier; +import electroblob.wizardry.EnumElement; +import electroblob.wizardry.EnumSpellType; +import electroblob.wizardry.EnumTier; import electroblob.wizardry.entity.projectile.EntityForceArrow; -import electroblob.wizardry.registry.WizardryItems; -import electroblob.wizardry.registry.WizardrySounds; -import electroblob.wizardry.util.SpellModifiers; -import electroblob.wizardry.util.WizardryUtilities; import net.minecraft.entity.EntityLiving; import net.minecraft.entity.EntityLivingBase; import net.minecraft.entity.player.EntityPlayer; import net.minecraft.item.EnumAction; -import net.minecraft.util.EnumHand; import net.minecraft.world.World; public class ForceArrow extends Spell { public ForceArrow() { - super(Tier.APPRENTICE, 15, Element.SORCERY, "force_arrow", SpellType.ATTACK, 20, EnumAction.NONE, false); + super(EnumTier.APPRENTICE, 15, EnumElement.SORCERY, "force_arrow", EnumSpellType.ATTACK, 20, EnumAction.none, false); } @Override @@ -27,30 +22,32 @@ public class ForceArrow extends Spell { } @Override - public boolean cast(World world, EntityPlayer caster, EnumHand hand, int ticksInUse, SpellModifiers modifiers) { + public boolean cast(World world, EntityPlayer caster, int ticksInUse, float damageMultiplier, float rangeMultiplier, float durationMultiplier, float blastMultiplier) { if(!world.isRemote){ - EntityForceArrow forceArrow = new EntityForceArrow(world, caster, 1*modifiers.get(WizardryItems.range_upgrade), modifiers.get(SpellModifiers.DAMAGE)); + EntityForceArrow forceArrow = new EntityForceArrow(world, caster, 1*rangeMultiplier, damageMultiplier); world.spawnEntityInWorld(forceArrow); } - caster.swingArm(hand); - WizardryUtilities.playSoundAtPlayer(caster, WizardrySounds.SPELL_FORCE, 1.0f, 1.2f + world.rand.nextFloat()*0.2f); + caster.swingItem(); + world.playSoundAtEntity(caster, "wizardry:aura", 1.0F, 1.4f); + world.playSoundAtEntity(caster, "wizardry:magic", 1.0F, 1.2F); return true; } @Override - public boolean cast(World world, EntityLiving caster, EnumHand hand, int ticksInUse, EntityLivingBase target, SpellModifiers modifiers){ + public boolean cast(World world, EntityLiving caster, EntityLivingBase target, float damageMultiplier, float rangeMultiplier, float durationMultiplier, float blastMultiplier){ if(target != null){ if(!world.isRemote){ - EntityForceArrow forceArrow = new EntityForceArrow(world, caster, target, 1*modifiers.get(WizardryItems.range_upgrade), 2, modifiers.get(SpellModifiers.DAMAGE)); + EntityForceArrow forceArrow = new EntityForceArrow(world, caster, target, 1*rangeMultiplier, 2, damageMultiplier); world.spawnEntityInWorld(forceArrow); } - caster.swingArm(hand); - caster.playSound(WizardrySounds.SPELL_FORCE, 1.0f, 1.2f + world.rand.nextFloat()*0.2f); + caster.swingItem(); + world.playSoundAtEntity(caster, "wizardry:aura", 1.0F, 1.4f); + world.playSoundAtEntity(caster, "wizardry:magic", 1.0F, 1.2F); return true; } diff --git a/src/main/java/electroblob/wizardry/spell/ForceOrb.java b/src/main/java/electroblob/wizardry/spell/ForceOrb.java index 30ee52a4..4e5e8821 100644 --- a/src/main/java/electroblob/wizardry/spell/ForceOrb.java +++ b/src/main/java/electroblob/wizardry/spell/ForceOrb.java @@ -1,24 +1,19 @@ package electroblob.wizardry.spell; -import electroblob.wizardry.constants.Element; -import electroblob.wizardry.constants.SpellType; -import electroblob.wizardry.constants.Tier; +import electroblob.wizardry.EnumElement; +import electroblob.wizardry.EnumSpellType; +import electroblob.wizardry.EnumTier; import electroblob.wizardry.entity.projectile.EntityForceOrb; -import electroblob.wizardry.registry.WizardryItems; -import electroblob.wizardry.util.SpellModifiers; -import electroblob.wizardry.util.WizardryUtilities; import net.minecraft.entity.EntityLiving; import net.minecraft.entity.EntityLivingBase; import net.minecraft.entity.player.EntityPlayer; -import net.minecraft.init.SoundEvents; import net.minecraft.item.EnumAction; -import net.minecraft.util.EnumHand; import net.minecraft.world.World; public class ForceOrb extends Spell { public ForceOrb() { - super(Tier.ADVANCED, 20, Element.SORCERY, "force_orb", SpellType.ATTACK, 20, EnumAction.NONE, false); + super(EnumTier.ADVANCED, 20, EnumElement.SORCERY, "force_orb", EnumSpellType.ATTACK, 20, EnumAction.none, false); } @Override @@ -27,31 +22,31 @@ public class ForceOrb extends Spell { } @Override - public boolean cast(World world, EntityPlayer caster, EnumHand hand, int ticksInUse, SpellModifiers modifiers) { + public boolean cast(World world, EntityPlayer caster, int ticksInUse, float damageMultiplier, float rangeMultiplier, float durationMultiplier, float blastMultiplier) { if(!world.isRemote){ - EntityForceOrb forceOrb = new EntityForceOrb(world, caster, modifiers.get(SpellModifiers.DAMAGE), modifiers.get(WizardryItems.blast_upgrade)); + EntityForceOrb forceOrb = new EntityForceOrb(world, caster, damageMultiplier, blastMultiplier); world.spawnEntityInWorld(forceOrb); } - caster.swingArm(hand); - WizardryUtilities.playSoundAtPlayer(caster, SoundEvents.ENTITY_SNOWBALL_THROW, 0.5F, 0.4F / (world.rand.nextFloat() * 0.4F + 0.8F)); + caster.swingItem(); + world.playSoundAtEntity(caster, "random.bow", 0.5F, 0.4F / (world.rand.nextFloat() * 0.4F + 0.8F)); return true; } @Override - public boolean cast(World world, EntityLiving caster, EnumHand hand, int ticksInUse, EntityLivingBase target, SpellModifiers modifiers){ + public boolean cast(World world, EntityLiving caster, EntityLivingBase target, float damageMultiplier, float rangeMultiplier, float durationMultiplier, float blastMultiplier){ if(target != null){ if(!world.isRemote){ - EntityForceOrb forceOrb = new EntityForceOrb(world, caster, modifiers.get(SpellModifiers.DAMAGE), modifiers.get(WizardryItems.blast_upgrade)); + EntityForceOrb forceOrb = new EntityForceOrb(world, caster, damageMultiplier, blastMultiplier); forceOrb.directTowards(target, 1.5f); world.spawnEntityInWorld(forceOrb); } - caster.swingArm(hand); - caster.playSound(SoundEvents.ENTITY_SNOWBALL_THROW, 0.5F, 0.4F / (world.rand.nextFloat() * 0.4F + 0.8F)); + caster.swingItem(); + world.playSoundAtEntity(caster, "random.bow", 0.5F, 0.4F / (world.rand.nextFloat() * 0.4F + 0.8F)); return true; } diff --git a/src/main/java/electroblob/wizardry/spell/Forcefield.java b/src/main/java/electroblob/wizardry/spell/Forcefield.java index 0b366bd1..0778db2f 100644 --- a/src/main/java/electroblob/wizardry/spell/Forcefield.java +++ b/src/main/java/electroblob/wizardry/spell/Forcefield.java @@ -1,24 +1,20 @@ package electroblob.wizardry.spell; -import electroblob.wizardry.constants.Element; -import electroblob.wizardry.constants.SpellType; -import electroblob.wizardry.constants.Tier; +import electroblob.wizardry.EnumElement; +import electroblob.wizardry.EnumSpellType; +import electroblob.wizardry.EnumTier; import electroblob.wizardry.entity.construct.EntityForcefield; -import electroblob.wizardry.registry.WizardryItems; -import electroblob.wizardry.registry.WizardrySounds; -import electroblob.wizardry.util.SpellModifiers; -import electroblob.wizardry.util.WizardryUtilities; +import electroblob.wizardry.entity.projectile.EntityMagicMissile; import net.minecraft.entity.EntityLiving; import net.minecraft.entity.EntityLivingBase; import net.minecraft.entity.player.EntityPlayer; import net.minecraft.item.EnumAction; -import net.minecraft.util.EnumHand; import net.minecraft.world.World; public class Forcefield extends Spell { public Forcefield() { - super(Tier.ADVANCED, 45, Element.HEALING, "forcefield", SpellType.DEFENCE, 200, EnumAction.BOW, false); + super(EnumTier.ADVANCED, 45, EnumElement.HEALING, "forcefield", EnumSpellType.DEFENCE, 200, EnumAction.bow, false); } @Override @@ -27,14 +23,14 @@ public class Forcefield extends Spell { } @Override - public boolean cast(World world, EntityPlayer caster, EnumHand hand, int ticksInUse, SpellModifiers modifiers) { + public boolean cast(World world, EntityPlayer caster, int ticksInUse, float damageMultiplier, float rangeMultiplier, float durationMultiplier, float blastMultiplier) { if(caster.onGround){ if(!world.isRemote){ - EntityForcefield forcefield = new EntityForcefield(world, caster.posX, caster.posY, caster.posZ, caster, (int)(600*modifiers.get(WizardryItems.duration_upgrade))); + EntityForcefield forcefield = new EntityForcefield(world, caster.posX, caster.posY, caster.posZ, caster, (int)(600*durationMultiplier)); world.spawnEntityInWorld(forcefield); } - WizardryUtilities.playSoundAtPlayer(caster, WizardrySounds.SPELL_CONJURATION_LARGE, 1.0f, 1.0f); + world.playSoundAtEntity(caster, "wizardry:largeaura", 1.0f, 1.0f); return true; } @@ -42,16 +38,16 @@ public class Forcefield extends Spell { } @Override - public boolean cast(World world, EntityLiving caster, EnumHand hand, int ticksInUse, EntityLivingBase target, SpellModifiers modifiers){ + public boolean cast(World world, EntityLiving caster, EntityLivingBase target, float damageMultiplier, float rangeMultiplier, float durationMultiplier, float blastMultiplier){ if(target != null){ // Wizards can no longer cast forcefield when they are inside one - if(caster.onGround && world.getEntitiesWithinAABB(EntityForcefield.class, caster.getEntityBoundingBox()).isEmpty()){ + if(caster.onGround && world.getEntitiesWithinAABB(EntityForcefield.class, caster.boundingBox).isEmpty()){ if(!world.isRemote){ - EntityForcefield forcefield = new EntityForcefield(world, caster.posX, caster.posY, caster.posZ, caster, (int)(600*modifiers.get(WizardryItems.duration_upgrade))); + EntityForcefield forcefield = new EntityForcefield(world, caster.posX, caster.posY, caster.posZ, caster, (int)(600*durationMultiplier)); world.spawnEntityInWorld(forcefield); } - caster.playSound(WizardrySounds.SPELL_CONJURATION_LARGE, 1.0f, 1.0f); + world.playSoundAtEntity(caster, "wizardry:largeaura", 1.0f, 1.0f); return true; } diff --git a/src/main/java/electroblob/wizardry/spell/ForestsCurse.java b/src/main/java/electroblob/wizardry/spell/ForestsCurse.java index 68612acd..1165666c 100644 --- a/src/main/java/electroblob/wizardry/spell/ForestsCurse.java +++ b/src/main/java/electroblob/wizardry/spell/ForestsCurse.java @@ -2,61 +2,57 @@ package electroblob.wizardry.spell; import java.util.List; +import electroblob.wizardry.EnumElement; +import electroblob.wizardry.EnumParticleType; +import electroblob.wizardry.EnumSpellType; +import electroblob.wizardry.EnumTier; +import electroblob.wizardry.MagicDamage; import electroblob.wizardry.Wizardry; -import electroblob.wizardry.constants.Element; -import electroblob.wizardry.constants.SpellType; -import electroblob.wizardry.constants.Tier; -import electroblob.wizardry.registry.WizardryItems; -import electroblob.wizardry.util.MagicDamage; -import electroblob.wizardry.util.MagicDamage.DamageType; -import electroblob.wizardry.util.SpellModifiers; -import electroblob.wizardry.util.WizardryParticleType; -import electroblob.wizardry.util.WizardryUtilities; +import electroblob.wizardry.WizardryUtilities; +import electroblob.wizardry.MagicDamage.DamageType; import net.minecraft.entity.EntityLivingBase; import net.minecraft.entity.player.EntityPlayer; -import net.minecraft.init.MobEffects; -import net.minecraft.init.SoundEvents; import net.minecraft.item.EnumAction; +import net.minecraft.potion.Potion; import net.minecraft.potion.PotionEffect; -import net.minecraft.util.EnumHand; import net.minecraft.world.World; public class ForestsCurse extends Spell { public ForestsCurse() { - super(Tier.MASTER, 75, Element.EARTH, "forests_curse", SpellType.ATTACK, 200, EnumAction.BOW, false); + super(EnumTier.MASTER, 75, EnumElement.EARTH, "forests_curse", EnumSpellType.ATTACK, 200, EnumAction.bow, false); } @Override - public boolean cast(World world, EntityPlayer caster, EnumHand hand, int ticksInUse, SpellModifiers modifiers) { + public boolean cast(World world, EntityPlayer caster, int ticksInUse, float damageMultiplier, float rangeMultiplier, float durationMultiplier, float blastMultiplier) { - List targets = WizardryUtilities.getEntitiesWithinRadius(5.0d*modifiers.get(WizardryItems.blast_upgrade), caster.posX, caster.posY, caster.posZ, world); + List targets = WizardryUtilities.getEntitiesWithinRadius(5.0d*blastMultiplier, caster.posX, caster.posY, caster.posZ, world); for(EntityLivingBase target : targets){ if(WizardryUtilities.isValidTarget(caster, target) && !MagicDamage.isEntityImmune(DamageType.POISON, target)){ - target.attackEntityFrom(MagicDamage.causeDirectMagicDamage(caster, DamageType.POISON), 4.0f * modifiers.get(SpellModifiers.DAMAGE)); - target.addPotionEffect(new PotionEffect(MobEffects.POISON, (int)(140*modifiers.get(WizardryItems.duration_upgrade)), 2)); - target.addPotionEffect(new PotionEffect(MobEffects.SLOWNESS, (int)(140*modifiers.get(WizardryItems.duration_upgrade)), 2)); - target.addPotionEffect(new PotionEffect(MobEffects.WEAKNESS, (int)(140*modifiers.get(WizardryItems.duration_upgrade)), 2)); + target.attackEntityFrom(MagicDamage.causeDirectMagicDamage(caster, DamageType.POISON), 4.0f * damageMultiplier); + target.addPotionEffect(new PotionEffect(Potion.poison.id, (int)(140*durationMultiplier), 2)); + target.addPotionEffect(new PotionEffect(Potion.moveSlowdown.id, (int)(140*durationMultiplier), 2)); + target.addPotionEffect(new PotionEffect(Potion.weakness.id, (int)(140*durationMultiplier), 2)); } } if(world.isRemote){ - for(int i=0;i<50*modifiers.get(WizardryItems.blast_upgrade);i++){ - double radius = (1 + world.rand.nextDouble()*4)*modifiers.get(WizardryItems.blast_upgrade); + for(int i=0;i<50*blastMultiplier;i++){ + double radius = (1 + world.rand.nextDouble()*4)*blastMultiplier; double angle = world.rand.nextDouble()*Math.PI*2; float brightness = world.rand.nextFloat()/4; - Wizardry.proxy.spawnParticle(WizardryParticleType.DARK_MAGIC, world, caster.posX + radius*Math.cos(angle), WizardryUtilities.getPlayerEyesPos(caster) + 0.5, caster.posZ + radius*Math.sin(angle), + Wizardry.proxy.spawnParticle(EnumParticleType.DARK_MAGIC, world, caster.posX + radius*Math.cos(angle), WizardryUtilities.getPlayerEyesPos(caster) + 0.5, caster.posZ + radius*Math.sin(angle), 0, -0.2, 0, 0, 0.05f+brightness, 0.2f+brightness, 0.0f); brightness = world.rand.nextFloat()/4; - Wizardry.proxy.spawnParticle(WizardryParticleType.SPARKLE, world, caster.posX + radius*Math.cos(angle), WizardryUtilities.getPlayerEyesPos(caster) + 0.5, caster.posZ + radius*Math.sin(angle), + Wizardry.proxy.spawnParticle(EnumParticleType.SPARKLE, world, caster.posX + radius*Math.cos(angle), WizardryUtilities.getPlayerEyesPos(caster) + 0.5, caster.posZ + radius*Math.sin(angle), 0, -0.05, 0, 50, 0.1f+brightness, 0.2f+brightness, 0.0f); - Wizardry.proxy.spawnParticle(WizardryParticleType.LEAF, world, caster.posX + radius*Math.cos(angle), WizardryUtilities.getPlayerEyesPos(caster) + 0.5, caster.posZ + radius*Math.sin(angle), 0, -0.01, 0, 40 + world.rand.nextInt(12)); + Wizardry.proxy.spawnParticle(EnumParticleType.LEAF, world, caster.posX + radius*Math.cos(angle), WizardryUtilities.getPlayerEyesPos(caster) + 0.5, caster.posZ + radius*Math.sin(angle), 0, -0.01, 0, 40 + world.rand.nextInt(12)); } } - WizardryUtilities.playSoundAtPlayer(caster, SoundEvents.ENTITY_WITHER_SPAWN, 1.0F, world.rand.nextFloat() * 0.2F + 1.0F); + world.playSoundAtEntity(caster, "mob.wither.spawn", 1.0F, world.rand.nextFloat() * 0.2F + 1.0F); return true; } diff --git a/src/main/java/electroblob/wizardry/spell/Freeze.java b/src/main/java/electroblob/wizardry/spell/Freeze.java index 01b289cc..bc159e03 100644 --- a/src/main/java/electroblob/wizardry/spell/Freeze.java +++ b/src/main/java/electroblob/wizardry/spell/Freeze.java @@ -1,58 +1,58 @@ package electroblob.wizardry.spell; +import electroblob.wizardry.EnumElement; +import electroblob.wizardry.EnumParticleType; +import electroblob.wizardry.EnumSpellType; +import electroblob.wizardry.EnumTier; +import electroblob.wizardry.MagicDamage; +import electroblob.wizardry.MagicDamage.DamageType; import electroblob.wizardry.Wizardry; -import electroblob.wizardry.constants.Element; -import electroblob.wizardry.constants.SpellType; -import electroblob.wizardry.constants.Tier; +import electroblob.wizardry.WizardryUtilities; import electroblob.wizardry.entity.living.EntityBlazeMinion; -import electroblob.wizardry.registry.WizardryItems; -import electroblob.wizardry.registry.WizardryPotions; -import electroblob.wizardry.registry.WizardrySounds; -import electroblob.wizardry.util.MagicDamage; -import electroblob.wizardry.util.MagicDamage.DamageType; -import electroblob.wizardry.util.SpellModifiers; -import electroblob.wizardry.util.WizardryParticleType; -import electroblob.wizardry.util.WizardryUtilities; +import electroblob.wizardry.entity.living.EntityIceGiant; +import electroblob.wizardry.entity.living.EntityIceWraith; import net.minecraft.entity.EntityLiving; import net.minecraft.entity.EntityLivingBase; import net.minecraft.entity.monster.EntityBlaze; import net.minecraft.entity.monster.EntityMagmaCube; +import net.minecraft.entity.monster.EntitySnowman; import net.minecraft.entity.player.EntityPlayer; import net.minecraft.init.Blocks; import net.minecraft.item.EnumAction; import net.minecraft.potion.PotionEffect; -import net.minecraft.util.EnumFacing; -import net.minecraft.util.EnumHand; -import net.minecraft.util.math.BlockPos; -import net.minecraft.util.math.RayTraceResult; -import net.minecraft.util.text.TextComponentTranslation; +import net.minecraft.util.ChatComponentTranslation; +import net.minecraft.util.MovingObjectPosition; +import net.minecraft.util.MovingObjectPosition.MovingObjectType; import net.minecraft.world.World; +import net.minecraftforge.common.util.ForgeDirection; public class Freeze extends Spell { public Freeze() { - super(Tier.BASIC, 5, Element.ICE, "freeze", SpellType.ATTACK, 10, EnumAction.NONE, false); + super(EnumTier.BASIC, 5, EnumElement.ICE, "freeze", EnumSpellType.ATTACK, 10, EnumAction.none, false); } @Override - public boolean cast(World world, EntityPlayer caster, EnumHand hand, int ticksInUse, SpellModifiers modifiers) { + public boolean cast(World world, EntityPlayer caster, int ticksInUse, float damageMultiplier, float rangeMultiplier, float durationMultiplier, float blastMultiplier) { + + //caster.addPotionEffect(new PotionEffect(Wizardry.frost.id, 200, 0)); // Entity ray trace is done first because block ray trace passes through entities; if it was the other // way round, entities would only be hit when there were no blocks in range behind them. - RayTraceResult rayTrace = WizardryUtilities.standardEntityRayTrace(world, caster, 10*modifiers.get(WizardryItems.range_upgrade)); + MovingObjectPosition rayTrace = WizardryUtilities.standardEntityRayTrace(world, caster, 10*rangeMultiplier); - if(rayTrace != null && rayTrace.typeOfHit == RayTraceResult.Type.ENTITY && rayTrace.entityHit instanceof EntityLivingBase){ + if(rayTrace != null && rayTrace.typeOfHit == MovingObjectType.ENTITY && rayTrace.entityHit instanceof EntityLivingBase){ EntityLivingBase target = (EntityLivingBase) rayTrace.entityHit; if(target instanceof EntityBlaze || target instanceof EntityMagmaCube || target instanceof EntityBlazeMinion){ - target.attackEntityFrom(MagicDamage.causeDirectMagicDamage(caster, DamageType.FROST), 3.0f * modifiers.get(SpellModifiers.DAMAGE)); + target.attackEntityFrom(MagicDamage.causeDirectMagicDamage(caster, DamageType.FROST), 3.0f * damageMultiplier); } if(MagicDamage.isEntityImmune(DamageType.FROST, target)){ - if(!world.isRemote) caster.addChatComponentMessage(new TextComponentTranslation("spell.resist", target.getName(), this.getNameForTranslationFormatted())); + if(!world.isRemote) caster.addChatComponentMessage(new ChatComponentTranslation("spell.resist", target.getCommandSenderName(), this.getDisplayNameWithFormatting())); }else{ - target.addPotionEffect(new PotionEffect(WizardryPotions.frost, (int)(200*modifiers.get(WizardryItems.duration_upgrade)), 1)); + target.addPotionEffect(new PotionEffect(Wizardry.frost.id, (int)(200*durationMultiplier), 1, true)); } if(target.isBurning()){ @@ -61,53 +61,52 @@ public class Freeze extends Spell { if(world.isRemote){ double dx = target.posX - caster.posX; - double dy = (target.getEntityBoundingBox().minY + target.height/2) - WizardryUtilities.getPlayerEyesPos(caster); + double dy = (WizardryUtilities.getEntityFeetPos(target) + target.height/2) - WizardryUtilities.getPlayerEyesPos(caster); double dz = target.posZ - caster.posZ; for(int i=1;i<5;i++){ float brightness = 0.5f + (world.rand.nextFloat()/2); - Wizardry.proxy.spawnParticle(WizardryParticleType.SPARKLE, world, caster.posX + (i*(dx/5)) + world.rand.nextFloat()/5, WizardryUtilities.getPlayerEyesPos(caster) + (i*(dy/5)) + world.rand.nextFloat()/5, caster.posZ + (i*(dz/5)) + world.rand.nextFloat()/5, 0.0d, 0.0d, 0.0d, 12 + world.rand.nextInt(8), brightness, brightness + 0.1f, 1.0f); - Wizardry.proxy.spawnParticle(WizardryParticleType.SNOW, world, caster.posX + (i*(dx/5)) + world.rand.nextFloat()/5, WizardryUtilities.getPlayerEyesPos(caster) + (i*(dy/5)) + world.rand.nextFloat()/5, caster.posZ + (i*(dz/5)) + world.rand.nextFloat()/5, 0, -0.02, 0, 40 + world.rand.nextInt(10)); + Wizardry.proxy.spawnParticle(EnumParticleType.SPARKLE, world, caster.posX + (i*(dx/5)) + world.rand.nextFloat()/5, WizardryUtilities.getPlayerEyesPos(caster) + (i*(dy/5)) + world.rand.nextFloat()/5, caster.posZ + (i*(dz/5)) + world.rand.nextFloat()/5, 0.0d, 0.0d, 0.0d, 12 + world.rand.nextInt(8), brightness, brightness + 0.1f, 1.0f); + Wizardry.proxy.spawnParticle(EnumParticleType.SNOW, world, caster.posX + (i*(dx/5)) + world.rand.nextFloat()/5, WizardryUtilities.getPlayerEyesPos(caster) + (i*(dy/5)) + world.rand.nextFloat()/5, caster.posZ + (i*(dz/5)) + world.rand.nextFloat()/5, 0, -0.02, 0, 40 + world.rand.nextInt(10)); } } - caster.swingArm(hand); - WizardryUtilities.playSoundAtPlayer(caster, WizardrySounds.SPELL_ICE, 1.0F, world.rand.nextFloat() * 0.4F + 1.2F); + caster.swingItem(); + world.playSoundAtEntity(caster, "wizardry:ice", 1.0F, world.rand.nextFloat() * 0.4F + 1.2F); return true; }else{ - rayTrace = WizardryUtilities.rayTrace(10*modifiers.get(WizardryItems.range_upgrade), world, caster, true); + rayTrace = WizardryUtilities.rayTrace(10*rangeMultiplier, world, caster, true); // Gets block the player is looking at and sets to ice or covers with snow as necessary // Note how the block is set on the server side only (kinda obvious really) but the particles are // spawned on the client side only. - if(rayTrace != null && rayTrace.typeOfHit == RayTraceResult.Type.BLOCK){ + if(rayTrace != null && rayTrace.typeOfHit == MovingObjectType.BLOCK){ - BlockPos pos = rayTrace.getBlockPos(); - - if(world.getBlockState(pos).getBlock() == Blocks.WATER && !world.isRemote){ - world.setBlockState(pos, Blocks.ICE.getDefaultState()); - }else if(world.getBlockState(pos).getBlock() == Blocks.LAVA && !world.isRemote){ - world.setBlockState(pos, Blocks.OBSIDIAN.getDefaultState()); - }else if(world.getBlockState(pos).getBlock() == Blocks.FLOWING_LAVA && !world.isRemote){ - world.setBlockState(pos, Blocks.COBBLESTONE.getDefaultState()); - }else if(rayTrace.sideHit == EnumFacing.UP && !world.isRemote && world.isSideSolid(pos, EnumFacing.UP) && WizardryUtilities.canBlockBeReplaced(world, pos.up())){ - world.setBlockState(pos.up(), Blocks.SNOW_LAYER.getDefaultState()); + int blockHitX = rayTrace.blockX; + int blockHitY = rayTrace.blockY; + int blockHitZ = rayTrace.blockZ; + int blockHitSide = rayTrace.sideHit; + if(world.getBlock(blockHitX, blockHitY, blockHitZ) == Blocks.water && !world.isRemote){ + world.setBlock(blockHitX, blockHitY, blockHitZ, Blocks.ice); + }else if(world.getBlock(blockHitX, blockHitY, blockHitZ) == Blocks.lava && !world.isRemote){ + world.setBlock(blockHitX, blockHitY, blockHitZ, Blocks.obsidian); + }else if(world.getBlock(blockHitX, blockHitY, blockHitZ) == Blocks.flowing_lava && !world.isRemote){ + world.setBlock(blockHitX, blockHitY, blockHitZ, Blocks.cobblestone); + }else if(blockHitSide == 1 && !world.isRemote && world.isSideSolid(blockHitX, blockHitY, blockHitZ, ForgeDirection.UP) && WizardryUtilities.canBlockBeReplaced(world, blockHitX, blockHitY+1, blockHitZ)){ + world.setBlock(blockHitX, blockHitY+1, blockHitZ, Blocks.snow_layer); } if(world.isRemote){ - - double dx = pos.getX() + 0.5 - caster.posX; - double dy = pos.getY() + 0.5 - WizardryUtilities.getPlayerEyesPos(caster); - double dz = pos.getZ() + 0.5 - caster.posZ; - + double dx = blockHitX + 0.5 - caster.posX; + double dy = blockHitY + 1.5 - (caster.posY + caster.height/2); + double dz = blockHitZ + 0.5 - caster.posZ; for(int i=1;i<5;i++){ float brightness = 0.5f + (world.rand.nextFloat()/2); - Wizardry.proxy.spawnParticle(WizardryParticleType.SPARKLE, world, caster.posX + (i*(dx/5)) + world.rand.nextFloat()/5, WizardryUtilities.getPlayerEyesPos(caster) + (i*(dy/5)) + world.rand.nextFloat()/5, caster.posZ + (i*(dz/5)) + world.rand.nextFloat()/5, 0.0d, 0.0d, 0.0d, 20 + world.rand.nextInt(8), brightness, brightness + 0.1f, 1.0f); - Wizardry.proxy.spawnParticle(WizardryParticleType.SPARKLE, world, caster.posX + (i*(dx/5)) + world.rand.nextFloat()/5, WizardryUtilities.getPlayerEyesPos(caster) + (i*(dy/5)) + world.rand.nextFloat()/5, caster.posZ + (i*(dz/5)) + world.rand.nextFloat()/5, 0, 0, 0, 20 + world.rand.nextInt(8), 1.0f, 1.0f, 1.0f); + Wizardry.proxy.spawnParticle(EnumParticleType.SPARKLE, world, caster.posX + (i*(dx/5)) + world.rand.nextFloat()/5, WizardryUtilities.getPlayerEyesPos(caster) + (i*(dy/5)) + world.rand.nextFloat()/5, caster.posZ + (i*(dz/5)) + world.rand.nextFloat()/5, 0.0d, 0.0d, 0.0d, 20 + world.rand.nextInt(8), brightness, brightness + 0.1f, 1.0f); + Wizardry.proxy.spawnParticle(EnumParticleType.SPARKLE, world, caster.posX + (i*(dx/5)) + world.rand.nextFloat()/5, WizardryUtilities.getPlayerEyesPos(caster) + (i*(dy/5)) + world.rand.nextFloat()/5, caster.posZ + (i*(dz/5)) + world.rand.nextFloat()/5, 0, 0, 0, 20 + world.rand.nextInt(8), 1.0f, 1.0f, 1.0f); } } - - caster.swingArm(hand); - WizardryUtilities.playSoundAtPlayer(caster, WizardrySounds.SPELL_ICE, 1.0F, world.rand.nextFloat() * 0.4F + 1.2F); + caster.swingItem(); + world.playSoundAtEntity(caster, "wizardry:ice", 1.0F, world.rand.nextFloat() * 0.4F + 1.2F); return true; } } @@ -115,16 +114,16 @@ public class Freeze extends Spell { } @Override - public boolean cast(World world, EntityLiving caster, EnumHand hand, int ticksInUse, EntityLivingBase target, SpellModifiers modifiers){ + public boolean cast(World world, EntityLiving caster, EntityLivingBase target, float damageMultiplier, float rangeMultiplier, float durationMultiplier, float blastMultiplier){ if(target != null){ if(target instanceof EntityBlaze || target instanceof EntityMagmaCube || target instanceof EntityBlazeMinion){ - target.attackEntityFrom(MagicDamage.causeDirectMagicDamage(caster, DamageType.FROST), 3.0f * modifiers.get(SpellModifiers.DAMAGE)); + target.attackEntityFrom(MagicDamage.causeDirectMagicDamage(caster, DamageType.FROST), 3.0f * damageMultiplier); } if(!world.isRemote && !MagicDamage.isEntityImmune(DamageType.FROST, target)){ - target.addPotionEffect(new PotionEffect(WizardryPotions.frost, (int)(200*modifiers.get(WizardryItems.duration_upgrade)), 1)); + target.addPotionEffect(new PotionEffect(Wizardry.frost.id, (int)(200*durationMultiplier), 1, true)); } if(target.isBurning()){ @@ -133,17 +132,17 @@ public class Freeze extends Spell { if(world.isRemote){ double dx = target.posX - caster.posX; - double dy = (target.getEntityBoundingBox().minY + target.height/2) - (caster.posY + caster.getEyeHeight()); + double dy = (WizardryUtilities.getEntityFeetPos(target) + target.height/2) - (caster.posY + caster.getEyeHeight()); double dz = target.posZ - caster.posZ; for(int i=1;i<5;i++){ float brightness = 0.5f + (world.rand.nextFloat()/2); - Wizardry.proxy.spawnParticle(WizardryParticleType.SPARKLE, world, caster.posX + (i*(dx/5)) + world.rand.nextFloat()/5, caster.posY + caster.getEyeHeight() + (i*(dy/5)) + world.rand.nextFloat()/5, caster.posZ + (i*(dz/5)) + world.rand.nextFloat()/5, 0.0d, 0.0d, 0.0d, 12 + world.rand.nextInt(8), brightness, brightness + 0.1f, 1.0f); - Wizardry.proxy.spawnParticle(WizardryParticleType.SNOW, world, caster.posX + (i*(dx/5)) + world.rand.nextFloat()/5, caster.posY + caster.getEyeHeight() + (i*(dy/5)) + world.rand.nextFloat()/5, caster.posZ + (i*(dz/5)) + world.rand.nextFloat()/5, 0, -0.02, 0, 40 + world.rand.nextInt(10)); + Wizardry.proxy.spawnParticle(EnumParticleType.SPARKLE, world, caster.posX + (i*(dx/5)) + world.rand.nextFloat()/5, caster.posY + caster.getEyeHeight() + (i*(dy/5)) + world.rand.nextFloat()/5, caster.posZ + (i*(dz/5)) + world.rand.nextFloat()/5, 0.0d, 0.0d, 0.0d, 12 + world.rand.nextInt(8), brightness, brightness + 0.1f, 1.0f); + Wizardry.proxy.spawnParticle(EnumParticleType.SNOW, world, caster.posX + (i*(dx/5)) + world.rand.nextFloat()/5, caster.posY + caster.getEyeHeight() + (i*(dy/5)) + world.rand.nextFloat()/5, caster.posZ + (i*(dz/5)) + world.rand.nextFloat()/5, 0, -0.02, 0, 40 + world.rand.nextInt(10)); } } - caster.swingArm(hand); - caster.playSound(WizardrySounds.SPELL_ICE, 1.0F, world.rand.nextFloat() * 0.4F + 1.2F); + caster.swingItem(); + world.playSoundAtEntity(caster, "wizardry:ice", 1.0F, world.rand.nextFloat() * 0.4F + 1.2F); return true; } diff --git a/src/main/java/electroblob/wizardry/spell/FreezingWeapon.java b/src/main/java/electroblob/wizardry/spell/FreezingWeapon.java index 911be3f4..1a7c5158 100644 --- a/src/main/java/electroblob/wizardry/spell/FreezingWeapon.java +++ b/src/main/java/electroblob/wizardry/spell/FreezingWeapon.java @@ -1,65 +1,71 @@ package electroblob.wizardry.spell; -import electroblob.wizardry.WizardData; +import java.util.List; + +import electroblob.wizardry.EnumElement; +import electroblob.wizardry.EnumParticleType; +import electroblob.wizardry.EnumSpellType; +import electroblob.wizardry.EnumTier; +import electroblob.wizardry.ExtendedPlayer; import electroblob.wizardry.Wizardry; -import electroblob.wizardry.constants.Constants; -import electroblob.wizardry.constants.Element; -import electroblob.wizardry.constants.SpellType; -import electroblob.wizardry.constants.Tier; -import electroblob.wizardry.registry.WizardryEnchantments; -import electroblob.wizardry.registry.WizardryItems; -import electroblob.wizardry.registry.WizardrySounds; -import electroblob.wizardry.util.SpellModifiers; -import electroblob.wizardry.util.WizardryParticleType; -import electroblob.wizardry.util.WizardryUtilities; +import electroblob.wizardry.WizardryUtilities; import net.minecraft.enchantment.EnchantmentHelper; import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.inventory.ContainerPlayer; +import net.minecraft.inventory.Slot; 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.world.World; public class FreezingWeapon extends Spell { - + /** The NBT tag name for storing the level of frost enchantment in the arrow's tag compound. (There's nothing * stopping you from using this elsewhere to shoot freezing arrows if you want to...) */ public static final String FREEZING_ARROW_NBT_KEY = "frostLevel"; public FreezingWeapon() { - super(Tier.ADVANCED, 35, Element.ICE, "freezing_weapon", SpellType.UTILITY, 70, EnumAction.BOW, false); + super(EnumTier.ADVANCED, 35, EnumElement.ICE, "freezing_weapon", EnumSpellType.UTILITY, 70, EnumAction.bow, false); } @Override - public boolean cast(World world, EntityPlayer caster, EnumHand hand, int ticksInUse, SpellModifiers modifiers) { + public boolean cast(World world, EntityPlayer caster, int ticksInUse, float damageMultiplier, float rangeMultiplier, float durationMultiplier, float blastMultiplier) { // Won't work if the weapon already has the enchantment - if(WizardData.get(caster) != null && WizardData.get(caster).getImbuementDuration(WizardryEnchantments.freezing_weapon) <= 0){ + if(ExtendedPlayer.get(caster) != null && ExtendedPlayer.get(caster).freezingWeaponDuration <= 0){ - for(ItemStack stack : WizardryUtilities.getPrioritisedHotbarAndOffhand(caster)){ + // Isolates just the hotbar + List hotbar = ((ContainerPlayer)caster.openContainer).inventorySlots.subList(36, 45); - if(stack != null){ + for(Object slot : hotbar){ - if((stack.getItem() instanceof ItemSword || stack.getItem() instanceof ItemBow) && !EnchantmentHelper.getEnchantments(stack).containsKey(WizardryEnchantments.freezing_weapon)){ - // The enchantment level as determined by the damage multiplier. The + 0.5f is so that - // weird float processing doesn't incorrectly round it down. - stack.addEnchantment(WizardryEnchantments.freezing_weapon, modifiers.get(SpellModifiers.DAMAGE) == 1.0f ? 1 : (int)((modifiers.get(SpellModifiers.DAMAGE) - 1.0f)/Constants.DAMAGE_INCREASE_PER_TIER + 0.5f)); + if(slot instanceof Slot){ - WizardData.get(caster).setImbuementDuration(WizardryEnchantments.freezing_weapon, (int)(900*modifiers.get(WizardryItems.duration_upgrade))); + ItemStack stack = ((Slot)slot).getStack(); - if(world.isRemote){ - for(int i=0; i<10; i++){ - double x1 = (double)((float)caster.posX + world.rand.nextFloat()*2 - 1.0F); - double y1 = (double)((float)WizardryUtilities.getPlayerEyesPos(caster) - 0.5F + world.rand.nextFloat()); - double z1 = (double)((float)caster.posZ + world.rand.nextFloat()*2 - 1.0F); - Wizardry.proxy.spawnParticle(WizardryParticleType.SPARKLE, world, x1, y1, z1, 0, 0.1F, 0, 48 + world.rand.nextInt(12), 0.9f, 0.7f, 1.0f); + if(stack != null){ + + if((stack.getItem() instanceof ItemSword || stack.getItem() instanceof ItemBow) && !EnchantmentHelper.getEnchantments(stack).containsKey(Wizardry.freezingWeapon.effectId)){ + // The enchantment level as determined by the damage multiplier. The + 0.5f is so that + // weird float processing doesn't incorrectly round it down. + stack.addEnchantment(Wizardry.freezingWeapon, damageMultiplier == 1.0f ? 1 : (int)((damageMultiplier - 1.0f)/Wizardry.DAMAGE_INCREASE_PER_TIER + 0.5f)); + + ExtendedPlayer.get(caster).freezingWeaponDuration = (int)(900*durationMultiplier); + + if(world.isRemote){ + for(int i=0; i<10; i++){ + double x1 = (double)((float)caster.posX + world.rand.nextFloat()*2 - 1.0F); + double y1 = (double)((float)WizardryUtilities.getPlayerEyesPos(caster) - 0.5F + world.rand.nextFloat()); + double z1 = (double)((float)caster.posZ + world.rand.nextFloat()*2 - 1.0F); + Wizardry.proxy.spawnParticle(EnumParticleType.SPARKLE, world, x1, y1, z1, 0, 0.1F, 0, 48 + world.rand.nextInt(12), 0.9f, 0.7f, 1.0f); + } } + + world.playSoundAtEntity(caster, "wizardry:aura", 1.0f, 1.0f); + return true; + } - - WizardryUtilities.playSoundAtPlayer(caster, WizardrySounds.SPELL_CONJURATION, 1.0f, 1.0f); - return true; - } } } diff --git a/src/main/java/electroblob/wizardry/spell/FrostAxe.java b/src/main/java/electroblob/wizardry/spell/FrostAxe.java index 1d48473e..9df23b16 100644 --- a/src/main/java/electroblob/wizardry/spell/FrostAxe.java +++ b/src/main/java/electroblob/wizardry/spell/FrostAxe.java @@ -1,46 +1,44 @@ package electroblob.wizardry.spell; +import electroblob.wizardry.EnumElement; +import electroblob.wizardry.EnumParticleType; +import electroblob.wizardry.EnumSpellType; +import electroblob.wizardry.EnumTier; +import electroblob.wizardry.ExtendedPlayer; import electroblob.wizardry.Wizardry; -import electroblob.wizardry.constants.Element; -import electroblob.wizardry.constants.SpellType; -import electroblob.wizardry.constants.Tier; -import electroblob.wizardry.item.IConjuredItem; -import electroblob.wizardry.registry.WizardryItems; -import electroblob.wizardry.registry.WizardrySounds; -import electroblob.wizardry.util.SpellModifiers; -import electroblob.wizardry.util.WizardryParticleType; -import electroblob.wizardry.util.WizardryUtilities; +import electroblob.wizardry.WizardryUtilities; import net.minecraft.entity.player.EntityPlayer; import net.minecraft.item.EnumAction; import net.minecraft.item.ItemStack; -import net.minecraft.util.EnumHand; +import net.minecraft.nbt.NBTTagCompound; import net.minecraft.world.World; public class FrostAxe extends Spell { public FrostAxe() { - super(Tier.ADVANCED, 45, Element.ICE, "frost_axe", SpellType.UTILITY, 50, EnumAction.BOW, false); + super(EnumTier.ADVANCED, 45, EnumElement.ICE, "frost_axe", EnumSpellType.UTILITY, 50, EnumAction.bow, false); } @Override - public boolean cast(World world, EntityPlayer caster, EnumHand hand, int ticksInUse, SpellModifiers modifiers) { + public boolean cast(World world, EntityPlayer caster, int ticksInUse, float damageMultiplier, float rangeMultiplier, float durationMultiplier, float blastMultiplier) { - ItemStack frostaxe = new ItemStack(WizardryItems.frost_axe); - - IConjuredItem.setDurationMultiplier(frostaxe, modifiers.get(WizardryItems.duration_upgrade)); + ItemStack frostaxe = new ItemStack(Wizardry.frostAxe); + frostaxe.stackTagCompound = new NBTTagCompound(); + frostaxe.stackTagCompound.setFloat("durationMultiplier", durationMultiplier); - if(!WizardryUtilities.doesPlayerHaveItem(caster, WizardryItems.frost_axe) && ConjureBow.conjureItemInInventory(caster, frostaxe)){ + if(!caster.inventory.hasItem(Wizardry.frostAxe) && caster.inventory.addItemStackToInventory(frostaxe)){ if(world.isRemote){ for(int i=0; i<10; i++){ double x1 = (double)((float)caster.posX + world.rand.nextFloat()*2 - 1.0F); double y1 = (double)((float)WizardryUtilities.getPlayerEyesPos(caster) - 0.5F + world.rand.nextFloat()); double z1 = (double)((float)caster.posZ + world.rand.nextFloat()*2 - 1.0F); - Wizardry.proxy.spawnParticle(WizardryParticleType.SNOW, world, x1, y1, z1, 0, -0.02d, 0, 40 + world.rand.nextInt(10)); + Wizardry.proxy.spawnParticle(EnumParticleType.SNOW, world, x1, y1, z1, 0, -0.02d, 0, 40 + world.rand.nextInt(10)); } } - WizardryUtilities.playSoundAtPlayer(caster, WizardrySounds.SPELL_ICE, 1.0f, 1.0f); + ExtendedPlayer.get(caster).frostAxeDuration = 0; + world.playSoundAtEntity(caster, "wizardry:ice", 1.0f, 1.0f); return true; } return false; diff --git a/src/main/java/electroblob/wizardry/spell/FrostRay.java b/src/main/java/electroblob/wizardry/spell/FrostRay.java index 10897960..98d4bd0f 100644 --- a/src/main/java/electroblob/wizardry/spell/FrostRay.java +++ b/src/main/java/electroblob/wizardry/spell/FrostRay.java @@ -1,132 +1,102 @@ package electroblob.wizardry.spell; +import electroblob.wizardry.EnumElement; +import electroblob.wizardry.EnumParticleType; +import electroblob.wizardry.EnumSpellType; +import electroblob.wizardry.EnumTier; +import electroblob.wizardry.MagicDamage; +import electroblob.wizardry.MagicDamage.DamageType; import electroblob.wizardry.Wizardry; -import electroblob.wizardry.constants.Element; -import electroblob.wizardry.constants.SpellType; -import electroblob.wizardry.constants.Tier; -import electroblob.wizardry.registry.WizardryItems; -import electroblob.wizardry.registry.WizardryPotions; -import electroblob.wizardry.registry.WizardrySounds; -import electroblob.wizardry.util.MagicDamage; -import electroblob.wizardry.util.MagicDamage.DamageType; -import electroblob.wizardry.util.SpellModifiers; -import electroblob.wizardry.util.WizardryParticleType; -import electroblob.wizardry.util.WizardryUtilities; -import net.minecraft.entity.EntityLiving; +import electroblob.wizardry.WizardryUtilities; +import electroblob.wizardry.entity.living.EntityBlazeMinion; +import electroblob.wizardry.entity.living.EntityIceGiant; +import electroblob.wizardry.entity.living.EntityIceWraith; import net.minecraft.entity.EntityLivingBase; import net.minecraft.entity.monster.EntityBlaze; import net.minecraft.entity.monster.EntityMagmaCube; +import net.minecraft.entity.monster.EntitySnowman; import net.minecraft.entity.player.EntityPlayer; import net.minecraft.item.EnumAction; import net.minecraft.potion.PotionEffect; -import net.minecraft.util.EnumHand; -import net.minecraft.util.math.RayTraceResult; -import net.minecraft.util.math.Vec3d; -import net.minecraft.util.text.TextComponentTranslation; +import net.minecraft.util.ChatComponentTranslation; +import net.minecraft.util.MovingObjectPosition; +import net.minecraft.util.MovingObjectPosition.MovingObjectType; +import net.minecraft.util.Vec3; import net.minecraft.world.World; public class FrostRay extends Spell { public FrostRay() { - super(Tier.APPRENTICE, 5, Element.ICE, "frost_ray", SpellType.ATTACK, 0, EnumAction.NONE, true); + super(EnumTier.APPRENTICE, 5, EnumElement.ICE, "frost_ray", EnumSpellType.ATTACK, 0, EnumAction.none, true); } @Override - public boolean cast(World world, EntityPlayer caster, EnumHand hand, int ticksInUse, SpellModifiers modifiers) { - - Vec3d look = caster.getLookVec(); - - RayTraceResult rayTrace = WizardryUtilities.standardEntityRayTrace(world, caster, 10*modifiers.get(WizardryItems.range_upgrade)); - - if(rayTrace != null && rayTrace.typeOfHit == RayTraceResult.Type.ENTITY && rayTrace.entityHit instanceof EntityLivingBase){ - + public boolean cast(World world, EntityPlayer caster, int ticksInUse, float damageMultiplier, float rangeMultiplier, float durationMultiplier, float blastMultiplier) { + + Vec3 look = caster.getLookVec(); + + MovingObjectPosition rayTrace = WizardryUtilities.standardEntityRayTrace(world, caster, 10*rangeMultiplier); + + if(rayTrace != null && rayTrace.typeOfHit == MovingObjectType.ENTITY && rayTrace.entityHit instanceof EntityLivingBase){ + EntityLivingBase target = (EntityLivingBase) rayTrace.entityHit; - + if(target.isBurning()){ target.extinguish(); } - + if(MagicDamage.isEntityImmune(DamageType.FROST, target)){ - if(!world.isRemote && ticksInUse == 1) caster.addChatComponentMessage(new TextComponentTranslation("spell.resist", target.getName(), this.getNameForTranslationFormatted())); + if(!world.isRemote && ticksInUse == 1) caster.addChatComponentMessage(new ChatComponentTranslation("spell.resist", target.getCommandSenderName(), this.getDisplayNameWithFormatting())); }else{ // For frost ray the entity can move slightly, unlike freeze. - target.addPotionEffect(new PotionEffect(WizardryPotions.frost, (int)(200*modifiers.get(WizardryItems.duration_upgrade)), 0)); + target.addPotionEffect(new PotionEffect(Wizardry.frost.id, (int)(200*durationMultiplier), 0, true)); + + if(target instanceof EntityBlaze || target instanceof EntityMagmaCube || target instanceof EntityBlazeMinion){ + // This motion stuff removes knockback, which is desirable for continuous spells. + double motionX = target.motionX; + double motionY = target.motionY; + double motionZ = target.motionZ; + + target.attackEntityFrom(MagicDamage.causeDirectMagicDamage(caster, DamageType.FROST), 6.0f * damageMultiplier); - float baseDamage = target instanceof EntityBlaze || target instanceof EntityMagmaCube ? 6.0f : 3.0f; - WizardryUtilities.attackEntityWithoutKnockback(target, MagicDamage.causeDirectMagicDamage(caster, DamageType.FROST), baseDamage * modifiers.get(SpellModifiers.DAMAGE)); + target.motionX = motionX; + target.motionY = motionY; + target.motionZ = motionZ; + }else{ + // This motion stuff removes knockback, which is desirable for continuous spells. + double motionX = target.motionX; + double motionY = target.motionY; + double motionZ = target.motionZ; + + target.attackEntityFrom(MagicDamage.causeDirectMagicDamage(caster, DamageType.FROST), 3.0f * damageMultiplier); + target.motionX = motionX; + target.motionY = motionY; + target.motionZ = motionZ; + } } } - + if(world.isRemote){ for(int i=0; i<20; i++){ double x1 = caster.posX + look.xCoord*i/2 + world.rand.nextFloat()/5 - 0.1f; double y1 = WizardryUtilities.getPlayerEyesPos(caster) - 0.4f + look.yCoord*i/2 + world.rand.nextFloat()/5 - 0.1f; double z1 = caster.posZ + look.zCoord*i/2 + world.rand.nextFloat()/5 - 0.1f; - Wizardry.proxy.spawnParticle(WizardryParticleType.SPARKLE, world, x1, y1, z1, look.xCoord*modifiers.get(WizardryItems.range_upgrade), look.yCoord*modifiers.get(WizardryItems.range_upgrade), look.zCoord*modifiers.get(WizardryItems.range_upgrade), 8 + world.rand.nextInt(12), 0.4f, 0.6f, 1.0f); + Wizardry.proxy.spawnParticle(EnumParticleType.SPARKLE, world, x1, y1, z1, look.xCoord*rangeMultiplier, look.yCoord*rangeMultiplier, look.zCoord*rangeMultiplier, 8 + world.rand.nextInt(12), 0.4f, 0.6f, 1.0f); x1 = caster.posX + look.xCoord*i/2 + world.rand.nextFloat()/5 - 0.1f; y1 = WizardryUtilities.getPlayerEyesPos(caster) - 0.4f + look.yCoord*i/2 + world.rand.nextFloat()/5 - 0.1f; z1 = caster.posZ + look.zCoord*i/2 + world.rand.nextFloat()/5 - 0.1f; - Wizardry.proxy.spawnParticle(WizardryParticleType.SPARKLE, world, x1, y1, z1, look.xCoord*modifiers.get(WizardryItems.range_upgrade), look.yCoord*modifiers.get(WizardryItems.range_upgrade), look.zCoord*modifiers.get(WizardryItems.range_upgrade), 8 + world.rand.nextInt(12), 1.0f, 1.0f, 1.0f); + Wizardry.proxy.spawnParticle(EnumParticleType.SPARKLE, world, x1, y1, z1, look.xCoord*rangeMultiplier, look.yCoord*rangeMultiplier, look.zCoord*rangeMultiplier, 8 + world.rand.nextInt(12), 1.0f, 1.0f, 1.0f); } } - - if(ticksInUse % 12 == 0){ - if(ticksInUse == 0) WizardryUtilities.playSoundAtPlayer(caster, WizardrySounds.SPELL_ICE, 0.5F, 1.0f); - WizardryUtilities.playSoundAtPlayer(caster, WizardrySounds.SPELL_LOOP_ICE, 0.5F, 1.0f); - } - return true; - } - - @Override - public boolean cast(World world, EntityLiving caster, EnumHand hand, int ticksInUse, EntityLivingBase target, SpellModifiers modifiers) { - - if(target != null){ - - Vec3d vec = new Vec3d(target.posX - caster.posX, target.posY - caster.posY, target.posZ - caster.posZ).normalize(); - - if(target.isBurning()){ - target.extinguish(); - } - - if(!MagicDamage.isEntityImmune(DamageType.FROST, target)){ - // For frost ray the entity can move slightly, unlike freeze. - target.addPotionEffect(new PotionEffect(WizardryPotions.frost, (int)(200*modifiers.get(WizardryItems.duration_upgrade)), 0)); - - float baseDamage = target instanceof EntityBlaze || target instanceof EntityMagmaCube ? 6.0f : 3.0f; - WizardryUtilities.attackEntityWithoutKnockback(target, MagicDamage.causeDirectMagicDamage(caster, DamageType.FROST), baseDamage * modifiers.get(SpellModifiers.DAMAGE)); - - } - - if(world.isRemote){ - for(int i=0; i<20; i++){ - double x1 = caster.posX + vec.xCoord*i/2 + world.rand.nextFloat()/5 - 0.1f; - double y1 = caster.posY + caster.getEyeHeight() - 0.4f + vec.yCoord*i/2 + world.rand.nextFloat()/5 - 0.1f; - double z1 = caster.posZ + vec.zCoord*i/2 + world.rand.nextFloat()/5 - 0.1f; - Wizardry.proxy.spawnParticle(WizardryParticleType.SPARKLE, world, x1, y1, z1, vec.xCoord*modifiers.get(WizardryItems.range_upgrade), vec.yCoord*modifiers.get(WizardryItems.range_upgrade), vec.zCoord*modifiers.get(WizardryItems.range_upgrade), 8 + world.rand.nextInt(12), 0.4f, 0.6f, 1.0f); - - x1 = caster.posX + vec.xCoord*i/2 + world.rand.nextFloat()/5 - 0.1f; - y1 = caster.posY + caster.getEyeHeight() - 0.4f + vec.yCoord*i/2 + world.rand.nextFloat()/5 - 0.1f; - z1 = caster.posZ + vec.zCoord*i/2 + world.rand.nextFloat()/5 - 0.1f; - Wizardry.proxy.spawnParticle(WizardryParticleType.SPARKLE, world, x1, y1, z1, vec.xCoord*modifiers.get(WizardryItems.range_upgrade), vec.yCoord*modifiers.get(WizardryItems.range_upgrade), vec.zCoord*modifiers.get(WizardryItems.range_upgrade), 8 + world.rand.nextInt(12), 1.0f, 1.0f, 1.0f); - } - } - - if(ticksInUse % 12 == 0){ - if(ticksInUse == 0) caster.playSound(WizardrySounds.SPELL_ICE, 0.5F, 1.0f); - caster.playSound(WizardrySounds.SPELL_LOOP_ICE, 0.5F, 1.0f); - } - - return true; - } - return false; - } - - @Override - public boolean canBeCastByNPCs() { + if(ticksInUse % 12 == 0){ + if(ticksInUse == 0)world.playSoundAtEntity(caster, "wizardry:ice", 0.5F, 1.0f); + world.playSoundAtEntity(caster, "wizardry:frostray", 0.5F, 1.0f); + } return true; } + } diff --git a/src/main/java/electroblob/wizardry/spell/FrostSigil.java b/src/main/java/electroblob/wizardry/spell/FrostSigil.java index 5423e9e8..f375f55e 100644 --- a/src/main/java/electroblob/wizardry/spell/FrostSigil.java +++ b/src/main/java/electroblob/wizardry/spell/FrostSigil.java @@ -1,24 +1,20 @@ package electroblob.wizardry.spell; -import electroblob.wizardry.constants.Element; -import electroblob.wizardry.constants.SpellType; -import electroblob.wizardry.constants.Tier; +import electroblob.wizardry.EnumElement; +import electroblob.wizardry.EnumSpellType; +import electroblob.wizardry.EnumTier; +import electroblob.wizardry.WizardryUtilities; import electroblob.wizardry.entity.construct.EntityFrostSigil; -import electroblob.wizardry.registry.WizardryItems; -import electroblob.wizardry.registry.WizardrySounds; -import electroblob.wizardry.util.SpellModifiers; -import electroblob.wizardry.util.WizardryUtilities; import net.minecraft.entity.player.EntityPlayer; import net.minecraft.item.EnumAction; -import net.minecraft.util.EnumFacing; -import net.minecraft.util.EnumHand; -import net.minecraft.util.math.RayTraceResult; +import net.minecraft.util.MovingObjectPosition; +import net.minecraft.util.MovingObjectPosition.MovingObjectType; import net.minecraft.world.World; public class FrostSigil extends Spell { public FrostSigil() { - super(Tier.APPRENTICE, 10, Element.ICE, "frost_sigil", SpellType.ATTACK, 20, EnumAction.NONE, false); + super(EnumTier.APPRENTICE, 10, EnumElement.ICE, "frost_sigil", EnumSpellType.ATTACK, 20, EnumAction.none, false); } @Override @@ -27,20 +23,20 @@ public class FrostSigil extends Spell { } @Override - public boolean cast(World world, EntityPlayer caster, EnumHand hand, int ticksInUse, SpellModifiers modifiers) { + public boolean cast(World world, EntityPlayer caster, int ticksInUse, float damageMultiplier, float rangeMultiplier, float durationMultiplier, float blastMultiplier) { - RayTraceResult rayTrace = WizardryUtilities.rayTrace(10*modifiers.get(WizardryItems.range_upgrade), world, caster, false); + MovingObjectPosition rayTrace = WizardryUtilities.rayTrace(10*rangeMultiplier, world, caster, false); - if(rayTrace != null && rayTrace.typeOfHit == RayTraceResult.Type.BLOCK && rayTrace.sideHit == EnumFacing.UP){ + if(rayTrace != null && rayTrace.typeOfHit == MovingObjectType.BLOCK && rayTrace.sideHit == 1){ if(!world.isRemote){ - double x = rayTrace.hitVec.xCoord; - double y = rayTrace.hitVec.yCoord; - double z = rayTrace.hitVec.zCoord; - EntityFrostSigil frostsigil = new EntityFrostSigil(world, x, y, z, caster, modifiers.get(SpellModifiers.DAMAGE)); + double x = rayTrace.blockX; + double y = rayTrace.blockY; + double z = rayTrace.blockZ; + EntityFrostSigil frostsigil = new EntityFrostSigil(world, x + 0.5, y+1, z + 0.5, caster, damageMultiplier); world.spawnEntityInWorld(frostsigil); } - caster.swingArm(hand); - WizardryUtilities.playSoundAtPlayer(caster, WizardrySounds.SPELL_ICE, 1.0F, 1.0F); + caster.swingItem(); + world.playSoundAtEntity(caster, "wizardry:ice", 1.0F, 1.0F); return true; } return false; diff --git a/src/main/java/electroblob/wizardry/spell/Glide.java b/src/main/java/electroblob/wizardry/spell/Glide.java index 45fa88d7..60fd80b9 100644 --- a/src/main/java/electroblob/wizardry/spell/Glide.java +++ b/src/main/java/electroblob/wizardry/spell/Glide.java @@ -1,26 +1,23 @@ package electroblob.wizardry.spell; +import electroblob.wizardry.EnumElement; +import electroblob.wizardry.EnumParticleType; +import electroblob.wizardry.EnumSpellType; +import electroblob.wizardry.EnumTier; import electroblob.wizardry.Wizardry; -import electroblob.wizardry.constants.Element; -import electroblob.wizardry.constants.SpellType; -import electroblob.wizardry.constants.Tier; -import electroblob.wizardry.util.SpellModifiers; -import electroblob.wizardry.util.WizardryParticleType; -import electroblob.wizardry.util.WizardryUtilities; +import electroblob.wizardry.WizardryUtilities; import net.minecraft.entity.player.EntityPlayer; -import net.minecraft.init.SoundEvents; import net.minecraft.item.EnumAction; -import net.minecraft.util.EnumHand; import net.minecraft.world.World; public class Glide extends Spell { public Glide() { - super(Tier.ADVANCED, 5, Element.EARTH, "glide", SpellType.UTILITY, 0, EnumAction.NONE, true); + super(EnumTier.ADVANCED, 5, EnumElement.EARTH, "glide", EnumSpellType.UTILITY, 0, EnumAction.none, true); } @Override - public boolean cast(World world, EntityPlayer caster, EnumHand hand, int ticksInUse, SpellModifiers modifiers) { + public boolean cast(World world, EntityPlayer caster, int ticksInUse, float damageMultiplier, float rangeMultiplier, float durationMultiplier, float blastMultiplier) { if(caster.motionY < -0.1 && !caster.isInWater()){ caster.motionY = -0.1; @@ -32,12 +29,16 @@ public class Glide extends Spell { } if(world.isRemote){ - Wizardry.proxy.spawnParticle(WizardryParticleType.SPARKLE, world, caster.posX - 0.25d + world.rand.nextDouble()/2, WizardryUtilities.getPlayerEyesPos(caster) - 1.5f + world.rand.nextDouble(), caster.posZ - 0.25d + world.rand.nextDouble()/2, 0, -0.1F, 0, 15, 1.0f, 1.0f, 1.0f); - Wizardry.proxy.spawnParticle(WizardryParticleType.LEAF, world, caster.posX - 0.25d + world.rand.nextDouble()/2, WizardryUtilities.getPlayerEyesPos(caster) - 1.5f + world.rand.nextDouble(), caster.posZ - 0.25d + world.rand.nextDouble()/2, 0, -0.03, 0, 20); + Wizardry.proxy.spawnParticle(EnumParticleType.SPARKLE, world, caster.posX - 0.25d + world.rand.nextDouble()/2, WizardryUtilities.getPlayerEyesPos(caster) - 1.5f + world.rand.nextDouble(), caster.posZ - 0.25d + world.rand.nextDouble()/2, 0, -0.1F, 0, 15, 1.0f, 1.0f, 1.0f); + Wizardry.proxy.spawnParticle(EnumParticleType.LEAF, world, caster.posX - 0.25d + world.rand.nextDouble()/2, WizardryUtilities.getPlayerEyesPos(caster) - 1.5f + world.rand.nextDouble(), caster.posZ - 0.25d + world.rand.nextDouble()/2, 0, -0.03, 0, 20); } + //if(caster.getItemInUseDuration() == 0){ + //world.playSoundAtEntity(entityplayer, "wizardry:sparkle", 0.5F, 1.0f); + //} + if(ticksInUse % 24 == 0){ - WizardryUtilities.playSoundAtPlayer(caster, SoundEvents.ITEM_ELYTRA_FLYING, 0.5F, 1.0f); + world.playSoundAtEntity(caster, "mob.enderdragon.wings", 0.5F, 1.0f); } return true; } diff --git a/src/main/java/electroblob/wizardry/spell/GreaterFireball.java b/src/main/java/electroblob/wizardry/spell/GreaterFireball.java index 93414391..c02157b9 100644 --- a/src/main/java/electroblob/wizardry/spell/GreaterFireball.java +++ b/src/main/java/electroblob/wizardry/spell/GreaterFireball.java @@ -1,24 +1,20 @@ package electroblob.wizardry.spell; -import electroblob.wizardry.constants.Element; -import electroblob.wizardry.constants.SpellType; -import electroblob.wizardry.constants.Tier; -import electroblob.wizardry.util.SpellModifiers; -import electroblob.wizardry.util.WizardryUtilities; +import electroblob.wizardry.EnumElement; +import electroblob.wizardry.EnumSpellType; +import electroblob.wizardry.EnumTier; import net.minecraft.entity.EntityLiving; import net.minecraft.entity.EntityLivingBase; import net.minecraft.entity.player.EntityPlayer; import net.minecraft.entity.projectile.EntityLargeFireball; -import net.minecraft.init.SoundEvents; import net.minecraft.item.EnumAction; -import net.minecraft.util.EnumHand; -import net.minecraft.util.math.Vec3d; +import net.minecraft.util.Vec3; import net.minecraft.world.World; public class GreaterFireball extends Spell { public GreaterFireball() { - super(Tier.ADVANCED, 20, Element.FIRE, "greater_fireball", SpellType.ATTACK, 30, EnumAction.NONE, false); + super(EnumTier.ADVANCED, 20, EnumElement.FIRE, "greater_fireball", EnumSpellType.ATTACK, 30, EnumAction.none, false); } @Override @@ -27,9 +23,9 @@ public class GreaterFireball extends Spell { } @Override - public boolean cast(World world, EntityPlayer caster, EnumHand hand, int ticksInUse, SpellModifiers modifiers) { + public boolean cast(World world, EntityPlayer caster, int ticksInUse, float damageMultiplier, float rangeMultiplier, float durationMultiplier, float blastMultiplier) { - Vec3d look = caster.getLookVec(); + Vec3 look = caster.getLookVec(); if(!world.isRemote){ EntityLargeFireball fireball = new EntityLargeFireball(world, caster, 1, 1, 1); @@ -41,15 +37,15 @@ public class GreaterFireball extends Spell { fireball.accelerationY = look.yCoord * 0.1; fireball.accelerationZ = look.zCoord * 0.1; world.spawnEntityInWorld(fireball); + world.playAuxSFX(1009, (int)caster.posX, (int)caster.posY, (int)caster.posZ, 0); } - WizardryUtilities.playSoundAtPlayer(caster, SoundEvents.ENTITY_BLAZE_SHOOT, 1, 1); - caster.swingArm(hand); + caster.swingItem(); return true; } @Override - public boolean cast(World world, EntityLiving caster, EnumHand hand, int ticksInUse, EntityLivingBase target, SpellModifiers modifiers){ + public boolean cast(World world, EntityLiving caster, EntityLivingBase target, float damageMultiplier, float rangeMultiplier, float durationMultiplier, float blastMultiplier){ if(target != null){ @@ -58,7 +54,7 @@ public class GreaterFireball extends Spell { EntityLargeFireball fireball = new EntityLargeFireball(world, caster, 1, 1, 1); double dx = target.posX - caster.posX; - double dy = target.getEntityBoundingBox().minY + (double)(target.height / 2.0F) - (caster.posY + (double)(caster.height / 2.0F)); + double dy = target.boundingBox.minY + (double)(target.height / 2.0F) - (caster.posY + (double)(caster.height / 2.0F)); double dz = target.posZ - caster.posZ; fireball.accelerationX = dx/caster.getDistanceToEntity(target) * 0.1; @@ -68,10 +64,10 @@ public class GreaterFireball extends Spell { fireball.setPosition(caster.posX, caster.posY + caster.getEyeHeight(), caster.posZ); world.spawnEntityInWorld(fireball); + world.playAuxSFX(1009, (int)caster.posX, (int)caster.posY, (int)caster.posZ, 0); } - caster.playSound(SoundEvents.ENTITY_BLAZE_SHOOT, 1, 1); - caster.swingArm(hand); + caster.swingItem(); return true; } diff --git a/src/main/java/electroblob/wizardry/spell/GreaterHeal.java b/src/main/java/electroblob/wizardry/spell/GreaterHeal.java index df20a7dc..d27e0338 100644 --- a/src/main/java/electroblob/wizardry/spell/GreaterHeal.java +++ b/src/main/java/electroblob/wizardry/spell/GreaterHeal.java @@ -1,59 +1,56 @@ package electroblob.wizardry.spell; +import electroblob.wizardry.EnumElement; +import electroblob.wizardry.EnumParticleType; +import electroblob.wizardry.EnumSpellType; +import electroblob.wizardry.EnumTier; import electroblob.wizardry.Wizardry; -import electroblob.wizardry.constants.Element; -import electroblob.wizardry.constants.SpellType; -import electroblob.wizardry.constants.Tier; -import electroblob.wizardry.registry.WizardrySounds; -import electroblob.wizardry.util.SpellModifiers; -import electroblob.wizardry.util.WizardryParticleType; -import electroblob.wizardry.util.WizardryUtilities; +import electroblob.wizardry.WizardryUtilities; import net.minecraft.entity.EntityLiving; import net.minecraft.entity.EntityLivingBase; import net.minecraft.entity.player.EntityPlayer; import net.minecraft.item.EnumAction; -import net.minecraft.util.EnumHand; import net.minecraft.world.World; public class GreaterHeal extends Spell { public GreaterHeal() { - super(Tier.ADVANCED, 15, Element.HEALING, "greater_heal", SpellType.DEFENCE, 40, EnumAction.BOW, false); + super(EnumTier.ADVANCED, 15, EnumElement.HEALING, "greater_heal", EnumSpellType.DEFENCE, 40, EnumAction.bow, false); } @Override - public boolean cast(World world, EntityPlayer caster, EnumHand hand, int ticksInUse, SpellModifiers modifiers) { + public boolean cast(World world, EntityPlayer caster, int ticksInUse, float damageMultiplier, float rangeMultiplier, float durationMultiplier, float blastMultiplier) { if(caster.shouldHeal()){ - caster.heal((int)(8*modifiers.get(SpellModifiers.DAMAGE))); + caster.heal((int)(8*damageMultiplier)); if(world.isRemote){ for(int i=0; i<10; i++){ double dx = (double)((float)caster.posX + world.rand.nextFloat()*2 - 1.0F); double dy = (double)((float)WizardryUtilities.getPlayerEyesPos(caster) - 0.5F + world.rand.nextFloat()); double dz = (double)((float)caster.posZ + world.rand.nextFloat()*2 - 1.0F); - Wizardry.proxy.spawnParticle(WizardryParticleType.SPARKLE, world, dx, dy, dz, 0, 0.1F, 0, 48 + world.rand.nextInt(12), 1.0f, 1.0f, 0.3f); + Wizardry.proxy.spawnParticle(EnumParticleType.SPARKLE, world, dx, dy, dz, 0, 0.1F, 0, 48 + world.rand.nextInt(12), 1.0f, 1.0f, 0.3f); } } - WizardryUtilities.playSoundAtPlayer(caster, WizardrySounds.SPELL_HEAL, 0.7F, world.rand.nextFloat() * 0.4F + 1.0F); + world.playSoundAtEntity(caster, "wizardry:heal", 0.7F, world.rand.nextFloat() * 0.4F + 1.0F); return true; } return false; } @Override - public boolean cast(World world, EntityLiving caster, EnumHand hand, int ticksInUse, EntityLivingBase target, SpellModifiers modifiers){ + public boolean cast(World world, EntityLiving caster, EntityLivingBase target, float damageMultiplier, float rangeMultiplier, float durationMultiplier, float blastMultiplier){ if(caster.getHealth() < caster.getMaxHealth()){ - caster.heal((int)(8*modifiers.get(SpellModifiers.DAMAGE))); + caster.heal((int)(8*damageMultiplier)); if(world.isRemote){ for(int i=0; i<10; i++){ double dx = (double)((float)caster.posX + world.rand.nextFloat()*2 - 1.0F); double dy = (double)((float)caster.posY + caster.getEyeHeight() - 0.5F + world.rand.nextFloat()); double dz = (double)((float)caster.posZ + world.rand.nextFloat()*2 - 1.0F); - Wizardry.proxy.spawnParticle(WizardryParticleType.SPARKLE, world, dx, dy, dz, 0, 0.1F, 0, 48 + world.rand.nextInt(12), 1.0f, 1.0f, 0.3f); + Wizardry.proxy.spawnParticle(EnumParticleType.SPARKLE, world, dx, dy, dz, 0, 0.1F, 0, 48 + world.rand.nextInt(12), 1.0f, 1.0f, 0.3f); } } - caster.playSound(WizardrySounds.SPELL_HEAL, 0.7F, world.rand.nextFloat() * 0.4F + 1.0F); + world.playSoundAtEntity(caster, "wizardry:heal", 0.7F, world.rand.nextFloat() * 0.4F + 1.0F); return true; } diff --git a/src/main/java/electroblob/wizardry/spell/GroupHeal.java b/src/main/java/electroblob/wizardry/spell/GroupHeal.java index 939ef998..b69714a5 100644 --- a/src/main/java/electroblob/wizardry/spell/GroupHeal.java +++ b/src/main/java/electroblob/wizardry/spell/GroupHeal.java @@ -2,34 +2,32 @@ package electroblob.wizardry.spell; import java.util.List; +import electroblob.wizardry.EnumElement; +import electroblob.wizardry.EnumParticleType; +import electroblob.wizardry.EnumSpellType; +import electroblob.wizardry.EnumTier; import electroblob.wizardry.Wizardry; -import electroblob.wizardry.constants.Element; -import electroblob.wizardry.constants.SpellType; -import electroblob.wizardry.constants.Tier; -import electroblob.wizardry.entity.living.ISummonedCreature; -import electroblob.wizardry.registry.WizardryItems; -import electroblob.wizardry.registry.WizardrySounds; -import electroblob.wizardry.util.SpellModifiers; -import electroblob.wizardry.util.WizardryParticleType; -import electroblob.wizardry.util.WizardryUtilities; +import electroblob.wizardry.WizardryUtilities; +import electroblob.wizardry.entity.living.EntitySummonedCreature; import net.minecraft.entity.EntityLivingBase; import net.minecraft.entity.player.EntityPlayer; import net.minecraft.item.EnumAction; -import net.minecraft.util.EnumHand; +import net.minecraft.potion.Potion; +import net.minecraft.potion.PotionEffect; import net.minecraft.world.World; public class GroupHeal extends Spell { public GroupHeal() { - super(Tier.ADVANCED, 35, Element.HEALING, "group_heal", SpellType.DEFENCE, 150, EnumAction.BOW, false); + super(EnumTier.ADVANCED, 35, EnumElement.HEALING, "group_heal", EnumSpellType.DEFENCE, 150, EnumAction.bow, false); } @Override - public boolean cast(World world, EntityPlayer caster, EnumHand hand, int ticksInUse, SpellModifiers modifiers) { + public boolean cast(World world, EntityPlayer caster, int ticksInUse, float damageMultiplier, float rangeMultiplier, float durationMultiplier, float blastMultiplier) { boolean flag = false; - List targets = WizardryUtilities.getEntitiesWithinRadius(5*modifiers.get(WizardryItems.blast_upgrade), caster.posX, caster.posY, caster.posZ, world); + List targets = WizardryUtilities.getEntitiesWithinRadius(5*blastMultiplier, caster.posX, caster.posY, caster.posZ, world); for(EntityLivingBase target : targets){ @@ -39,43 +37,42 @@ public class GroupHeal extends Spell { if(((EntityPlayer)target).shouldHeal()){ - target.heal((int)(6*modifiers.get(SpellModifiers.DAMAGE))); + target.heal((int)(6*damageMultiplier)); if(world.isRemote){ for(int i=0; i<10; i++){ double d0 = (double)((float)target.posX + world.rand.nextFloat()*2 - 1.0F); double d1 = (double)((float)WizardryUtilities.getPlayerEyesPos((EntityPlayer)target) - 0.5F + world.rand.nextFloat()); double d2 = (double)((float)target.posZ + world.rand.nextFloat()*2 - 1.0F); - Wizardry.proxy.spawnParticle(WizardryParticleType.SPARKLE, world, d0, d1, d2, 0, 0.1F, 0, 48 + world.rand.nextInt(12), 1.0f, 1.0f, 0.3f); + Wizardry.proxy.spawnParticle(EnumParticleType.SPARKLE, world, d0, d1, d2, 0, 0.1F, 0, 48 + world.rand.nextInt(12), 1.0f, 1.0f, 0.3f); } } - WizardryUtilities.playSoundAtPlayer(caster, WizardrySounds.SPELL_HEAL, 0.7F, world.rand.nextFloat() * 0.4F + 1.0F); + world.playSoundAtEntity(caster, "wizardry:heal", 0.7F, world.rand.nextFloat() * 0.4F + 1.0F); flag = true; } } - // Now also works on summoned creatures - }else if(target instanceof ISummonedCreature){ + }else if(target instanceof EntitySummonedCreature){ - EntityLivingBase summoner = ((ISummonedCreature)target).getCaster(); + EntityLivingBase summoner = ((EntitySummonedCreature)target).getCaster(); if(summoner == caster || (summoner instanceof EntityPlayer && WizardryUtilities.isPlayerAlly(caster, (EntityPlayer)summoner))){ if(target.getHealth() < target.getMaxHealth()){ - target.heal((int)(6*modifiers.get(SpellModifiers.DAMAGE))); + target.heal((int)(6*damageMultiplier)); if(world.isRemote){ for(int i=0; i<10; i++){ double d0 = (double)((float)target.posX + world.rand.nextFloat()*2 - 1.0F); double d1 = (double)((float)WizardryUtilities.getPlayerEyesPos((EntityPlayer)target) - 0.5F + world.rand.nextFloat()); double d2 = (double)((float)target.posZ + world.rand.nextFloat()*2 - 1.0F); - Wizardry.proxy.spawnParticle(WizardryParticleType.SPARKLE, world, d0, d1, d2, 0, 0.1F, 0, 48 + world.rand.nextInt(12), 1.0f, 1.0f, 0.3f); + Wizardry.proxy.spawnParticle(EnumParticleType.SPARKLE, world, d0, d1, d2, 0, 0.1F, 0, 48 + world.rand.nextInt(12), 1.0f, 1.0f, 0.3f); } } - WizardryUtilities.playSoundAtPlayer(caster, WizardrySounds.SPELL_HEAL, 0.7F, world.rand.nextFloat() * 0.4F + 1.0F); + world.playSoundAtEntity(caster, "wizardry:heal", 0.7F, world.rand.nextFloat() * 0.4F + 1.0F); flag = true; } } diff --git a/src/main/java/electroblob/wizardry/spell/GrowthAura.java b/src/main/java/electroblob/wizardry/spell/GrowthAura.java index 7e52855e..c13fe625 100644 --- a/src/main/java/electroblob/wizardry/spell/GrowthAura.java +++ b/src/main/java/electroblob/wizardry/spell/GrowthAura.java @@ -1,24 +1,22 @@ package electroblob.wizardry.spell; -import electroblob.wizardry.constants.Element; -import electroblob.wizardry.constants.SpellType; -import electroblob.wizardry.constants.Tier; -import electroblob.wizardry.registry.WizardrySounds; -import electroblob.wizardry.util.SpellModifiers; -import electroblob.wizardry.util.WizardryUtilities; +import electroblob.wizardry.EnumElement; +import electroblob.wizardry.EnumSpellType; +import electroblob.wizardry.EnumTier; +import electroblob.wizardry.WizardryUtilities; +import net.minecraft.block.Block; +import net.minecraft.block.BlockGrass; import net.minecraft.block.IGrowable; -import net.minecraft.block.state.IBlockState; import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.init.Blocks; import net.minecraft.item.EnumAction; import net.minecraft.item.ItemDye; -import net.minecraft.util.EnumHand; -import net.minecraft.util.math.BlockPos; import net.minecraft.world.World; public class GrowthAura extends Spell { public GrowthAura() { - super(Tier.APPRENTICE, 20, Element.EARTH, "growth_aura", SpellType.UTILITY, 50, EnumAction.NONE, false); + super(EnumTier.APPRENTICE, 20, EnumElement.EARTH, "growth_aura", EnumSpellType.UTILITY, 50, EnumAction.none, false); } @Override @@ -27,7 +25,7 @@ public class GrowthAura extends Spell { } @Override - public boolean cast(World world, EntityPlayer caster, EnumHand hand, int ticksInUse, SpellModifiers modifiers) { + public boolean cast(World world, EntityPlayer caster, int ticksInUse, float damageMultiplier, float rangeMultiplier, float durationMultiplier, float blastMultiplier) { boolean flag = false; @@ -35,39 +33,38 @@ public class GrowthAura extends Spell { for(int j=-1; j<2; j++){ - int x = (int)caster.posX + i; - int y = WizardryUtilities.getNearestFloorLevelC(world, new BlockPos(caster.posX + i, caster.posY, caster.posZ + j), 2) - 1; - int z = (int)caster.posZ + j; + if(!world.isRemote){ - BlockPos pos = new BlockPos(x, y, z); + int x = (int)caster.posX + i; + int y = WizardryUtilities.getNearestFloorLevelC(world, (int)caster.posX + i, (int)caster.posY, (int)caster.posZ + j, 2) - 1; + int z = (int)caster.posZ + j; - if(y > -1 && caster.getDistance(x, y, z) <= 2){ + if(y > -1 && caster.getDistance(x, y, z) <= 2){ - IBlockState state = world.getBlockState(pos); + Block block = world.getBlock(x, y, z); - if(state.getBlock() instanceof IGrowable){ + if(block instanceof IGrowable){ - IGrowable igrowable = (IGrowable)state.getBlock(); + IGrowable igrowable = (IGrowable)block; - if(igrowable.canGrow(world, pos, state, world.isRemote)){ - - if(!world.isRemote){ - if(igrowable.canUseBonemeal(world, world.rand, pos, state)){ - igrowable.grow(world, world.rand, pos, state); + if (igrowable.func_149851_a(world, x, y, z, world.isRemote)) + { + if (igrowable.func_149852_a(world, world.rand, x, y, z)) + { + igrowable.func_149853_b(world, world.rand, x, y, z); } - }else{ - // Yes, it's meant to be 0, and it automatically changes it to 15. - ItemDye.spawnBonemealParticles(world, pos, 0); + + world.playAuxSFX(2005, x, y, z, 0); + + flag = true; } - flag = true; } } } } } - if(flag) WizardryUtilities.playSoundAtPlayer(caster, WizardrySounds.SPELL_HEAL, 0.7F, world.rand.nextFloat() * 0.4F + 1.0F); return flag; } diff --git a/src/main/java/electroblob/wizardry/spell/Hailstorm.java b/src/main/java/electroblob/wizardry/spell/Hailstorm.java index 7dc8ee73..b6f02050 100644 --- a/src/main/java/electroblob/wizardry/spell/Hailstorm.java +++ b/src/main/java/electroblob/wizardry/spell/Hailstorm.java @@ -1,23 +1,20 @@ package electroblob.wizardry.spell; -import electroblob.wizardry.constants.Element; -import electroblob.wizardry.constants.SpellType; -import electroblob.wizardry.constants.Tier; +import electroblob.wizardry.EnumElement; +import electroblob.wizardry.EnumSpellType; +import electroblob.wizardry.EnumTier; +import electroblob.wizardry.WizardryUtilities; import electroblob.wizardry.entity.construct.EntityHailstorm; -import electroblob.wizardry.registry.WizardryItems; -import electroblob.wizardry.registry.WizardrySounds; -import electroblob.wizardry.util.SpellModifiers; -import electroblob.wizardry.util.WizardryUtilities; import net.minecraft.entity.player.EntityPlayer; import net.minecraft.item.EnumAction; -import net.minecraft.util.EnumHand; -import net.minecraft.util.math.RayTraceResult; +import net.minecraft.util.MovingObjectPosition; +import net.minecraft.util.MovingObjectPosition.MovingObjectType; import net.minecraft.world.World; public class Hailstorm extends Spell { public Hailstorm() { - super(Tier.MASTER, 75, Element.ICE, "hailstorm", SpellType.ATTACK, 300, EnumAction.NONE, false); + super(EnumTier.MASTER, 75, EnumElement.ICE, "hailstorm", EnumSpellType.ATTACK, 300, EnumAction.none, false); } @Override @@ -26,15 +23,15 @@ public class Hailstorm extends Spell { } @Override - public boolean cast(World world, EntityPlayer caster, EnumHand hand, int ticksInUse, SpellModifiers modifiers) { + public boolean cast(World world, EntityPlayer caster, int ticksInUse, float damageMultiplier, float rangeMultiplier, float durationMultiplier, float blastMultiplier) { - RayTraceResult rayTrace = WizardryUtilities.rayTrace(20*modifiers.get(WizardryItems.range_upgrade), world, caster, false); + MovingObjectPosition rayTrace = WizardryUtilities.rayTrace(20*rangeMultiplier, world, caster, false); - if(rayTrace != null && rayTrace.typeOfHit == RayTraceResult.Type.BLOCK){ + if(rayTrace != null && rayTrace.typeOfHit == MovingObjectType.BLOCK){ if(!world.isRemote){ - double x = rayTrace.hitVec.xCoord; - double y = rayTrace.hitVec.yCoord; - double z = rayTrace.hitVec.zCoord; + double x = rayTrace.blockX; + double y = rayTrace.blockY; + double z = rayTrace.blockZ; // Moves the entity back towards the caster a bit, so the area of effect is better centred on the position. // 3.0d is the distance to move the entity back towards the caster. double dx = caster.posX - x; @@ -43,12 +40,12 @@ public class Hailstorm extends Spell { x += dx*distRatio; z += dz*distRatio; - EntityHailstorm hailstorm = new EntityHailstorm(world, x, y + 5, z, caster, (int)(120*modifiers.get(WizardryItems.duration_upgrade)), modifiers.get(SpellModifiers.DAMAGE)); + EntityHailstorm hailstorm = new EntityHailstorm(world, x, y + 5, z, caster, (int)(120*durationMultiplier), damageMultiplier); hailstorm.rotationYaw = caster.rotationYawHead; world.spawnEntityInWorld(hailstorm); } - caster.swingArm(hand); - WizardryUtilities.playSoundAtPlayer(caster, WizardrySounds.SPELL_ICE, 1.0F, 1.0F); + caster.swingItem(); + world.playSoundAtEntity(caster, "wizardry:ice", 1.0F, 1.0F); return true; } return false; diff --git a/src/main/java/electroblob/wizardry/spell/Heal.java b/src/main/java/electroblob/wizardry/spell/Heal.java index 8a7044e3..a09297c9 100644 --- a/src/main/java/electroblob/wizardry/spell/Heal.java +++ b/src/main/java/electroblob/wizardry/spell/Heal.java @@ -1,32 +1,29 @@ package electroblob.wizardry.spell; +import electroblob.wizardry.EnumElement; +import electroblob.wizardry.EnumParticleType; +import electroblob.wizardry.EnumSpellType; +import electroblob.wizardry.EnumTier; import electroblob.wizardry.Wizardry; -import electroblob.wizardry.constants.Element; -import electroblob.wizardry.constants.SpellType; -import electroblob.wizardry.constants.Tier; -import electroblob.wizardry.registry.WizardrySounds; -import electroblob.wizardry.util.SpellModifiers; -import electroblob.wizardry.util.WizardryParticleType; -import electroblob.wizardry.util.WizardryUtilities; +import electroblob.wizardry.WizardryUtilities; import net.minecraft.entity.EntityLiving; import net.minecraft.entity.EntityLivingBase; import net.minecraft.entity.player.EntityPlayer; import net.minecraft.item.EnumAction; -import net.minecraft.util.EnumHand; import net.minecraft.world.World; public class Heal extends Spell { public Heal() { - super(Tier.BASIC, 5, Element.HEALING, "heal", SpellType.DEFENCE, 20, EnumAction.BOW, false); + super(EnumTier.BASIC, 5, EnumElement.HEALING, "heal", EnumSpellType.DEFENCE, 20, EnumAction.bow, false); } @Override - public boolean cast(World world, EntityPlayer caster, EnumHand hand, int ticksInUse, SpellModifiers modifiers) { + public boolean cast(World world, EntityPlayer caster, int ticksInUse, float damageMultiplier, float rangeMultiplier, float durationMultiplier, float blastMultiplier) { if(caster.shouldHeal()){ - caster.heal((int)(4*modifiers.get(SpellModifiers.DAMAGE))); + caster.heal((int)(4*damageMultiplier)); if(world.isRemote){ for(int i=0; i<10; i++){ @@ -34,30 +31,30 @@ public class Heal extends Spell { // Apparently the client side spawns the particles 1 block higher than it should... hence the - 0.5F. double d1 = (double)((float)WizardryUtilities.getPlayerEyesPos(caster) - 0.5F + world.rand.nextFloat()); double d2 = (double)((float)caster.posZ + world.rand.nextFloat()*2 - 1.0F); - Wizardry.proxy.spawnParticle(WizardryParticleType.SPARKLE, world, d0, d1, d2, 0, 0.1F, 0, 48 + world.rand.nextInt(12), 1.0f, 1.0f, 0.3f); + Wizardry.proxy.spawnParticle(EnumParticleType.SPARKLE, world, d0, d1, d2, 0, 0.1F, 0, 48 + world.rand.nextInt(12), 1.0f, 1.0f, 0.3f); } } - WizardryUtilities.playSoundAtPlayer(caster, WizardrySounds.SPELL_HEAL, 0.7F, world.rand.nextFloat() * 0.4F + 1.0F); + world.playSoundAtEntity(caster, "wizardry:heal", 0.7F, world.rand.nextFloat() * 0.4F + 1.0F); return true; } return false; } @Override - public boolean cast(World world, EntityLiving caster, EnumHand hand, int ticksInUse, EntityLivingBase target, SpellModifiers modifiers){ + public boolean cast(World world, EntityLiving caster, EntityLivingBase target, float damageMultiplier, float rangeMultiplier, float durationMultiplier, float blastMultiplier){ if(caster.getHealth() < caster.getMaxHealth()){ - caster.heal((int)(4*modifiers.get(SpellModifiers.DAMAGE))); + caster.heal((int)(4*damageMultiplier)); if(world.isRemote){ for(int i=0; i<10; i++){ double dx = (double)((float)caster.posX + world.rand.nextFloat()*2 - 1.0F); double dy = (double)((float)caster.posY + caster.getEyeHeight() - 0.5F + world.rand.nextFloat()); double dz = (double)((float)caster.posZ + world.rand.nextFloat()*2 - 1.0F); - Wizardry.proxy.spawnParticle(WizardryParticleType.SPARKLE, world, dx, dy, dz, 0, 0.1F, 0, 48 + world.rand.nextInt(12), 1.0f, 1.0f, 0.3f); + Wizardry.proxy.spawnParticle(EnumParticleType.SPARKLE, world, dx, dy, dz, 0, 0.1F, 0, 48 + world.rand.nextInt(12), 1.0f, 1.0f, 0.3f); } } - caster.playSound(WizardrySounds.SPELL_HEAL, 0.7F, world.rand.nextFloat() * 0.4F + 1.0F); + world.playSoundAtEntity(caster, "wizardry:heal", 0.7F, world.rand.nextFloat() * 0.4F + 1.0F); return true; } diff --git a/src/main/java/electroblob/wizardry/spell/HealAlly.java b/src/main/java/electroblob/wizardry/spell/HealAlly.java index 3ab49587..e8493459 100644 --- a/src/main/java/electroblob/wizardry/spell/HealAlly.java +++ b/src/main/java/electroblob/wizardry/spell/HealAlly.java @@ -1,49 +1,47 @@ package electroblob.wizardry.spell; +import electroblob.wizardry.EnumElement; +import electroblob.wizardry.EnumParticleType; +import electroblob.wizardry.EnumSpellType; +import electroblob.wizardry.EnumTier; import electroblob.wizardry.Wizardry; -import electroblob.wizardry.constants.Element; -import electroblob.wizardry.constants.SpellType; -import electroblob.wizardry.constants.Tier; -import electroblob.wizardry.registry.WizardryItems; -import electroblob.wizardry.registry.WizardrySounds; -import electroblob.wizardry.util.SpellModifiers; -import electroblob.wizardry.util.WizardryParticleType; -import electroblob.wizardry.util.WizardryUtilities; +import electroblob.wizardry.WizardryUtilities; +import electroblob.wizardry.client.particle.EntitySparkleFX; +import net.minecraft.client.Minecraft; import net.minecraft.entity.EntityLivingBase; import net.minecraft.entity.player.EntityPlayer; import net.minecraft.item.EnumAction; -import net.minecraft.util.EnumHand; -import net.minecraft.util.math.RayTraceResult; +import net.minecraft.util.MovingObjectPosition; import net.minecraft.world.World; public class HealAlly extends Spell { public HealAlly() { - super(Tier.APPRENTICE, 10, Element.HEALING, "heal_ally", SpellType.DEFENCE, 20, EnumAction.NONE, false); + super(EnumTier.APPRENTICE, 10, EnumElement.HEALING, "heal_ally", EnumSpellType.DEFENCE, 20, EnumAction.none, false); } @Override - public boolean cast(World world, EntityPlayer caster, EnumHand hand, int ticksInUse, SpellModifiers modifiers) { + public boolean cast(World world, EntityPlayer caster, int ticksInUse, float damageMultiplier, float rangeMultiplier, float durationMultiplier, float blastMultiplier) { - RayTraceResult rayTrace = WizardryUtilities.standardEntityRayTrace(world, caster, 10*modifiers.get(WizardryItems.range_upgrade), 8.0f); + MovingObjectPosition rayTrace = WizardryUtilities.standardEntityRayTrace(world, caster, 10*rangeMultiplier, 8.0f); if(rayTrace != null && rayTrace.entityHit != null && rayTrace.entityHit instanceof EntityLivingBase){ EntityLivingBase target = (EntityLivingBase)rayTrace.entityHit; if(target.getHealth() < target.getMaxHealth()){ - target.heal((int)(5*modifiers.get(SpellModifiers.DAMAGE))); + target.heal((int)(5*damageMultiplier)); if(world.isRemote){ for(int i=0; i<10; i++){ double d0 = (double)((float)target.posX + world.rand.nextFloat()*2 - 1.0F); // Apparently the client side spawns the particles 1 block higher than it should... hence the - 0.5F. - double d1 = (double)((float)target.getEntityBoundingBox().minY + target.height - 0.5f + world.rand.nextFloat()); + double d1 = (double)((float)WizardryUtilities.getEntityFeetPos(target) + target.height - 0.5f + world.rand.nextFloat()); double d2 = (double)((float)target.posZ + world.rand.nextFloat()*2 - 1.0F); - Wizardry.proxy.spawnParticle(WizardryParticleType.SPARKLE, world, d0, d1, d2, 0, 0.1F, 0, 48 + world.rand.nextInt(12), 1.0f, 1.0f, 0.3f); + Wizardry.proxy.spawnParticle(EnumParticleType.SPARKLE, world, d0, d1, d2, 0, 0.1F, 0, 48 + world.rand.nextInt(12), 1.0f, 1.0f, 0.3f); } } - caster.swingArm(hand); - target.playSound(WizardrySounds.SPELL_HEAL, 0.7F, world.rand.nextFloat() * 0.4F + 1.0F); + caster.swingItem(); + world.playSoundAtEntity(target, "wizardry:heal", 0.7F, world.rand.nextFloat() * 0.4F + 1.0F); return true; } } diff --git a/src/main/java/electroblob/wizardry/spell/HealingAura.java b/src/main/java/electroblob/wizardry/spell/HealingAura.java index bcd6ea61..67359c0e 100644 --- a/src/main/java/electroblob/wizardry/spell/HealingAura.java +++ b/src/main/java/electroblob/wizardry/spell/HealingAura.java @@ -1,22 +1,20 @@ package electroblob.wizardry.spell; -import electroblob.wizardry.constants.Element; -import electroblob.wizardry.constants.SpellType; -import electroblob.wizardry.constants.Tier; +import electroblob.wizardry.EnumElement; +import electroblob.wizardry.EnumSpellType; +import electroblob.wizardry.EnumTier; +import electroblob.wizardry.entity.construct.EntityForcefield; import electroblob.wizardry.entity.construct.EntityHealAura; -import electroblob.wizardry.registry.WizardryItems; -import electroblob.wizardry.util.SpellModifiers; import net.minecraft.entity.EntityLiving; import net.minecraft.entity.EntityLivingBase; import net.minecraft.entity.player.EntityPlayer; import net.minecraft.item.EnumAction; -import net.minecraft.util.EnumHand; import net.minecraft.world.World; public class HealingAura extends Spell { public HealingAura() { - super(Tier.ADVANCED, 35, Element.HEALING, "healing_aura", SpellType.DEFENCE, 150, EnumAction.BOW, false); + super(EnumTier.ADVANCED, 35, EnumElement.HEALING, "healing_aura", EnumSpellType.DEFENCE, 150, EnumAction.bow, false); } @Override @@ -25,11 +23,11 @@ public class HealingAura extends Spell { } @Override - public boolean cast(World world, EntityPlayer caster, EnumHand hand, int ticksInUse, SpellModifiers modifiers) { + public boolean cast(World world, EntityPlayer caster, int ticksInUse, float damageMultiplier, float rangeMultiplier, float durationMultiplier, float blastMultiplier) { if(caster.onGround){ if(!world.isRemote){ - EntityHealAura healaura = new EntityHealAura(world, caster.posX, caster.posY, caster.posZ, caster, (int)(600*modifiers.get(WizardryItems.duration_upgrade)), modifiers.get(SpellModifiers.DAMAGE)); + EntityHealAura healaura = new EntityHealAura(world, caster.posX, caster.posY, caster.posZ, caster, (int)(600*durationMultiplier), damageMultiplier); world.spawnEntityInWorld(healaura); } return true; @@ -38,12 +36,12 @@ public class HealingAura extends Spell { } @Override - public boolean cast(World world, EntityLiving caster, EnumHand hand, int ticksInUse, EntityLivingBase target, SpellModifiers modifiers) { + public boolean cast(World world, EntityLiving caster, EntityLivingBase target, float damageMultiplier, float rangeMultiplier, float durationMultiplier, float blastMultiplier) { if(target != null){ - if(caster.onGround && world.getEntitiesWithinAABB(EntityHealAura.class, caster.getEntityBoundingBox()).isEmpty()){ + if(caster.onGround && world.getEntitiesWithinAABB(EntityHealAura.class, caster.boundingBox).isEmpty()){ if(!world.isRemote){ - EntityHealAura healaura = new EntityHealAura(world, caster.posX, caster.posY, caster.posZ, caster, (int)(600*modifiers.get(WizardryItems.duration_upgrade)), modifiers.get(SpellModifiers.DAMAGE)); + EntityHealAura healaura = new EntityHealAura(world, caster.posX, caster.posY, caster.posZ, caster, (int)(600*durationMultiplier), damageMultiplier); world.spawnEntityInWorld(healaura); } return true; diff --git a/src/main/java/electroblob/wizardry/spell/HomingSpark.java b/src/main/java/electroblob/wizardry/spell/HomingSpark.java index 65ed1d64..d50f4708 100644 --- a/src/main/java/electroblob/wizardry/spell/HomingSpark.java +++ b/src/main/java/electroblob/wizardry/spell/HomingSpark.java @@ -1,23 +1,19 @@ package electroblob.wizardry.spell; -import electroblob.wizardry.constants.Element; -import electroblob.wizardry.constants.SpellType; -import electroblob.wizardry.constants.Tier; +import electroblob.wizardry.EnumElement; +import electroblob.wizardry.EnumSpellType; +import electroblob.wizardry.EnumTier; import electroblob.wizardry.entity.projectile.EntitySpark; -import electroblob.wizardry.registry.WizardrySounds; -import electroblob.wizardry.util.SpellModifiers; -import electroblob.wizardry.util.WizardryUtilities; import net.minecraft.entity.EntityLiving; import net.minecraft.entity.EntityLivingBase; import net.minecraft.entity.player.EntityPlayer; import net.minecraft.item.EnumAction; -import net.minecraft.util.EnumHand; import net.minecraft.world.World; public class HomingSpark extends Spell { public HomingSpark() { - super(Tier.APPRENTICE, 10, Element.LIGHTNING, "homing_spark", SpellType.ATTACK, 20, EnumAction.NONE, false); + super(EnumTier.APPRENTICE, 10, EnumElement.LIGHTNING, "homing_spark", EnumSpellType.ATTACK, 20, EnumAction.none, false); } @Override @@ -26,29 +22,29 @@ public class HomingSpark extends Spell { } @Override - public boolean cast(World world, EntityPlayer caster, EnumHand hand, int ticksInUse, SpellModifiers modifiers) { + public boolean cast(World world, EntityPlayer caster, int ticksInUse, float damageMultiplier, float rangeMultiplier, float durationMultiplier, float blastMultiplier) { if(!world.isRemote){ - EntitySpark spark = new EntitySpark(world, caster, modifiers.get(SpellModifiers.DAMAGE)); + EntitySpark spark = new EntitySpark(world, caster, damageMultiplier); world.spawnEntityInWorld(spark); - WizardryUtilities.playSoundAtPlayer(caster, WizardrySounds.SPELL_CONJURATION, 1.0F, 0.4F / (world.rand.nextFloat() * 0.4F + 0.8F)); + world.playSoundAtEntity(caster, "wizardry:aura", 1.0F, 0.4F / (world.rand.nextFloat() * 0.4F + 0.8F)); } - caster.swingArm(hand); + caster.swingItem(); return true; } @Override - public boolean cast(World world, EntityLiving caster, EnumHand hand, int ticksInUse, EntityLivingBase target, SpellModifiers modifiers){ + public boolean cast(World world, EntityLiving caster, EntityLivingBase target, float damageMultiplier, float rangeMultiplier, float durationMultiplier, float blastMultiplier){ if(target != null){ if(!world.isRemote){ - EntitySpark spark = new EntitySpark(world, caster, modifiers.get(SpellModifiers.DAMAGE)); + EntitySpark spark = new EntitySpark(world, caster, damageMultiplier); spark.directTowards(target, 0.5f); world.spawnEntityInWorld(spark); - caster.playSound(WizardrySounds.SPELL_CONJURATION, 1.0F, 0.4F / (world.rand.nextFloat() * 0.4F + 0.8F)); + world.playSoundAtEntity(caster, "wizardry:aura", 1.0F, 0.4F / (world.rand.nextFloat() * 0.4F + 0.8F)); } - caster.swingArm(hand); + caster.swingItem(); return true; } diff --git a/src/main/java/electroblob/wizardry/spell/IceAge.java b/src/main/java/electroblob/wizardry/spell/IceAge.java index 8f57ee59..b9278c7c 100644 --- a/src/main/java/electroblob/wizardry/spell/IceAge.java +++ b/src/main/java/electroblob/wizardry/spell/IceAge.java @@ -2,16 +2,12 @@ package electroblob.wizardry.spell; import java.util.List; -import electroblob.wizardry.constants.Element; -import electroblob.wizardry.constants.SpellType; -import electroblob.wizardry.constants.Tier; -import electroblob.wizardry.registry.WizardryAchievements; -import electroblob.wizardry.registry.WizardryBlocks; -import electroblob.wizardry.registry.WizardryItems; -import electroblob.wizardry.registry.WizardrySounds; +import electroblob.wizardry.EnumElement; +import electroblob.wizardry.EnumSpellType; +import electroblob.wizardry.EnumTier; +import electroblob.wizardry.Wizardry; +import electroblob.wizardry.WizardryUtilities; import electroblob.wizardry.tileentity.TileEntityStatue; -import electroblob.wizardry.util.SpellModifiers; -import electroblob.wizardry.util.WizardryUtilities; import net.minecraft.entity.EntityLiving; import net.minecraft.entity.EntityLivingBase; import net.minecraft.entity.monster.EntityBlaze; @@ -19,8 +15,6 @@ import net.minecraft.entity.monster.EntityMagmaCube; import net.minecraft.entity.player.EntityPlayer; import net.minecraft.init.Blocks; import net.minecraft.item.EnumAction; -import net.minecraft.util.EnumHand; -import net.minecraft.util.math.BlockPos; import net.minecraft.world.World; public class IceAge extends Spell { @@ -28,7 +22,7 @@ public class IceAge extends Spell { private static final int baseDuration = 1200; public IceAge() { - super(Tier.MASTER, 70, Element.ICE, "ice_age", SpellType.ATTACK, 250, EnumAction.BOW, false); + super(EnumTier.MASTER, 70, EnumElement.ICE, "ice_age", EnumSpellType.ATTACK, 250, EnumAction.bow, false); } @Override @@ -37,76 +31,78 @@ public class IceAge extends Spell { } @Override - public boolean cast(World world, EntityPlayer caster, EnumHand hand, int ticksInUse, SpellModifiers modifiers) { + public boolean cast(World world, EntityPlayer caster, int ticksInUse, float damageMultiplier, float rangeMultiplier, float durationMultiplier, float blastMultiplier) { - List targets = WizardryUtilities.getEntitiesWithinRadius(7*modifiers.get(WizardryItems.blast_upgrade), caster.posX, caster.posY, caster.posZ, world); + List targets = WizardryUtilities.getEntitiesWithinRadius(7*blastMultiplier, caster.posX, caster.posY, caster.posZ, world); for(EntityLivingBase target : targets){ if(WizardryUtilities.isValidTarget(caster, target)){ if(!world.isRemote){ if(target instanceof EntityBlaze || target instanceof EntityMagmaCube){ // These have been removed for the time being because they cause the entity to sink into the floor when it breaks out. - //target.attackEntityFrom(WizardryUtilities.causePlayerMagicDamage(entityplayer), 8.0f * modifiers.get(SpellModifiers.DAMAGE)); + //target.attackEntityFrom(WizardryUtilities.causePlayerMagicDamage(entityplayer), 8.0f * damageMultiplier); }else{ - //target.attackEntityFrom(WizardryUtilities.causePlayerMagicDamage(entityplayer), 4.0f * modifiers.get(SpellModifiers.DAMAGE)); + //target.attackEntityFrom(WizardryUtilities.causePlayerMagicDamage(entityplayer), 4.0f * damageMultiplier); } if(target.isBurning()){ target.extinguish(); } - if(target instanceof EntityBlaze) caster.addStat(WizardryAchievements.freeze_blaze, 1); + if(target instanceof EntityBlaze) caster.addStat(Wizardry.freezeBlaze, 1); if(target instanceof EntityLiving){ // Stops the entity looking red while frozen and the resulting z-fighting target.hurtTime = 0; - BlockPos pos = new BlockPos(target); + int x = (int)Math.floor(target.posX); + int y = (int)Math.floor(target.posY); + int z = (int)Math.floor(target.posZ); - // Short mobs such as spiders and pigs - if((target.height < 1.2 || target.isChild()) && WizardryUtilities.canBlockBeReplaced(world, pos)){ - world.setBlockState(pos, WizardryBlocks.ice_statue.getDefaultState()); - if(world.getTileEntity(pos) instanceof TileEntityStatue){ - ((TileEntityStatue)world.getTileEntity(pos)).setCreatureAndPart((EntityLiving) target, 1, 1); - ((TileEntityStatue)world.getTileEntity(pos)).setLifetime((int)(baseDuration*modifiers.get(WizardryItems.duration_upgrade))); + //Short mobs such as spiders and pigs + if((target.height < 1.2 || target.isChild()) && WizardryUtilities.canBlockBeReplaced(world, x, y, z)){ + world.setBlock(x, y, z, Wizardry.iceStatue); + if(world.getTileEntity(x, y, z) instanceof TileEntityStatue){ + ((TileEntityStatue)world.getTileEntity(x, y, z)).setCreatureAndPart((EntityLiving) target, 1, 1); + ((TileEntityStatue)world.getTileEntity(x, y, z)).setLifetime((int)(baseDuration*durationMultiplier)); } target.setDead(); - target.playSound(WizardrySounds.SPELL_FREEZE, 1.0F, world.rand.nextFloat() * 0.4F + 0.8F); + world.playSoundAtEntity(target, "wizardry:freeze", 1.0F, world.rand.nextFloat() * 0.4F + 0.8F); } - // Normal sized mobs like zombies and skeletons - else if(target.height < 2.5 && WizardryUtilities.canBlockBeReplaced(world, pos) && WizardryUtilities.canBlockBeReplaced(world, pos.up())){ - world.setBlockState(pos, WizardryBlocks.ice_statue.getDefaultState()); - if(world.getTileEntity(pos) instanceof TileEntityStatue){ - ((TileEntityStatue)world.getTileEntity(pos)).setCreatureAndPart((EntityLiving) target, 1, 2); - ((TileEntityStatue)world.getTileEntity(pos)).setLifetime((int)(baseDuration*modifiers.get(WizardryItems.duration_upgrade))); + //Normal sized mobs like zombies and skeletons + else if(target.height < 2.5 && WizardryUtilities.canBlockBeReplaced(world, x, y, z) && WizardryUtilities.canBlockBeReplaced(world, x, y+1, z)){ + world.setBlock(x, y, z, Wizardry.iceStatue); + if(world.getTileEntity(x, y, z) instanceof TileEntityStatue){ + ((TileEntityStatue)world.getTileEntity(x, y, z)).setCreatureAndPart((EntityLiving) target, 1, 2); + ((TileEntityStatue)world.getTileEntity(x, y, z)).setLifetime((int)(baseDuration*durationMultiplier)); } - world.setBlockState(pos.up(), WizardryBlocks.ice_statue.getDefaultState()); - if(world.getTileEntity(pos.up()) instanceof TileEntityStatue){ - ((TileEntityStatue)world.getTileEntity(pos.up())).setCreatureAndPart((EntityLiving) target, 2, 2); + world.setBlock(x, y+1, z, Wizardry.iceStatue); + if(world.getTileEntity(x, y+1, z) instanceof TileEntityStatue){ + ((TileEntityStatue)world.getTileEntity(x, y+1, z)).setCreatureAndPart((EntityLiving) target, 2, 2); } target.setDead(); - target.playSound(WizardrySounds.SPELL_FREEZE, 1.0F, world.rand.nextFloat() * 0.4F + 0.8F); + world.playSoundAtEntity(target, "wizardry:freeze", 1.0F, world.rand.nextFloat() * 0.4F + 0.8F); } - // Tall mobs like endermen - else if(WizardryUtilities.canBlockBeReplaced(world, pos) && WizardryUtilities.canBlockBeReplaced(world, pos.up()) && WizardryUtilities.canBlockBeReplaced(world, pos.up(2))){ - world.setBlockState(pos, WizardryBlocks.ice_statue.getDefaultState()); - if(world.getTileEntity(pos) instanceof TileEntityStatue){ - ((TileEntityStatue)world.getTileEntity(pos)).setCreatureAndPart((EntityLiving) target, 1, 3); - ((TileEntityStatue)world.getTileEntity(pos)).setLifetime((int)(baseDuration*modifiers.get(WizardryItems.duration_upgrade))); + //Tall mobs like endermen + else if(WizardryUtilities.canBlockBeReplaced(world, x, y, z) && WizardryUtilities.canBlockBeReplaced(world, x, y+1, z) && WizardryUtilities.canBlockBeReplaced(world, x, y+2, z)){ + world.setBlock(x, y, z, Wizardry.iceStatue); + if(world.getTileEntity(x, y, z) instanceof TileEntityStatue){ + ((TileEntityStatue)world.getTileEntity(x, y, z)).setCreatureAndPart((EntityLiving) target, 1, 3); + ((TileEntityStatue)world.getTileEntity(x, y, z)).setLifetime((int)(baseDuration*durationMultiplier)); } - world.setBlockState(pos.up(), WizardryBlocks.ice_statue.getDefaultState()); - if(world.getTileEntity(pos.up()) instanceof TileEntityStatue){ - ((TileEntityStatue)world.getTileEntity(pos.up())).setCreatureAndPart((EntityLiving) target, 2, 3); + world.setBlock(x, y+1, z, Wizardry.iceStatue); + if(world.getTileEntity(x, y+1, z) instanceof TileEntityStatue){ + ((TileEntityStatue)world.getTileEntity(x, y+1, z)).setCreatureAndPart((EntityLiving) target, 2, 3); } - world.setBlockState(pos.up(2), WizardryBlocks.ice_statue.getDefaultState()); - if(world.getTileEntity(pos.up(2)) instanceof TileEntityStatue){ - ((TileEntityStatue)world.getTileEntity(pos.up(2))).setCreatureAndPart((EntityLiving) target, 3, 3); + world.setBlock(x, y+2, z, Wizardry.iceStatue); + if(world.getTileEntity(x, y+2, z) instanceof TileEntityStatue){ + ((TileEntityStatue)world.getTileEntity(x, y+2, z)).setCreatureAndPart((EntityLiving) target, 3, 3); } target.setDead(); - target.playSound(WizardrySounds.SPELL_FREEZE, 1.0F, world.rand.nextFloat() * 0.4F + 0.8F); + world.playSoundAtEntity(target, "wizardry:freeze", 1.0F, world.rand.nextFloat() * 0.4F + 0.8F); } } } @@ -115,33 +111,28 @@ public class IceAge extends Spell { if(!world.isRemote){ for(int i=-7; i<8; i++){ for(int j=-7; j<8; j++){ - - BlockPos pos = new BlockPos(caster).add(i, 0, j); - - int y = WizardryUtilities.getNearestFloorLevelB(world, new BlockPos(pos), 7); - - pos = new BlockPos(pos.getX(), y, pos.getZ()); - + int y = WizardryUtilities.getNearestFloorLevelB(world, (int)caster.posX + i, (int)caster.posY, (int)caster.posZ + j, 7); + //System.out.println(y); double dist = caster.getDistance((int)caster.posX + i, y, (int)caster.posZ + j); - // Randomised with weighting so that the nearer the block the more likely it is to be snowed. if(y != -1 && world.rand.nextInt((int)dist*2 + 1) < 7 && dist < 8){ - if(world.getBlockState(pos.down()) == Blocks.WATER.getDefaultState()){ - world.setBlockState(pos.down(), Blocks.ICE.getDefaultState()); - }else if(world.getBlockState(pos.down()) == Blocks.LAVA.getDefaultState()){ - world.setBlockState(pos.down(), Blocks.OBSIDIAN.getDefaultState()); - }else if(world.getBlockState(pos.down()) == Blocks.FLOWING_LAVA.getDefaultState()){ - world.setBlockState(pos.down(), Blocks.COBBLESTONE.getDefaultState()); + if(world.getBlock((int)caster.posX + i, y-1, (int)caster.posZ + j) == Blocks.water){ + world.setBlock((int)caster.posX + i, y-1, (int)caster.posZ + j, Blocks.ice); + }else if(world.getBlock((int)caster.posX + i, y-1, (int)caster.posZ + j) == Blocks.lava){ + world.setBlock((int)caster.posX + i, y-1, (int)caster.posZ + j, Blocks.obsidian); + }else if(world.getBlock((int)caster.posX + i, y-1, (int)caster.posZ + j) == Blocks.flowing_lava){ + world.setBlock((int)caster.posX + i, y-1, (int)caster.posZ + j, Blocks.cobblestone); }else{ - world.setBlockState(pos, Blocks.SNOW_LAYER.getDefaultState()); + world.setBlock((int)caster.posX + i, y, (int)caster.posZ + j, Blocks.snow_layer); } } } } } - WizardryUtilities.playSoundAtPlayer(caster, WizardrySounds.SPELL_ICE, 0.7F, 1.0f); - WizardryUtilities.playSoundAtPlayer(caster, WizardrySounds.SPELL_LOOP_WIND, 1.0F, 1.0f); + world.playSoundAtEntity(caster, "wizardry:ice", 0.7F, 1.0f); + world.playSoundAtEntity(caster, "wizardry:wind", 1.0F, 1.0f); return true; } + } diff --git a/src/main/java/electroblob/wizardry/spell/IceCharge.java b/src/main/java/electroblob/wizardry/spell/IceCharge.java index c419b374..959a477c 100644 --- a/src/main/java/electroblob/wizardry/spell/IceCharge.java +++ b/src/main/java/electroblob/wizardry/spell/IceCharge.java @@ -1,24 +1,19 @@ package electroblob.wizardry.spell; -import electroblob.wizardry.constants.Element; -import electroblob.wizardry.constants.SpellType; -import electroblob.wizardry.constants.Tier; +import electroblob.wizardry.EnumElement; +import electroblob.wizardry.EnumSpellType; +import electroblob.wizardry.EnumTier; import electroblob.wizardry.entity.projectile.EntityIceCharge; -import electroblob.wizardry.registry.WizardryItems; -import electroblob.wizardry.registry.WizardrySounds; -import electroblob.wizardry.util.SpellModifiers; -import electroblob.wizardry.util.WizardryUtilities; import net.minecraft.entity.EntityLiving; import net.minecraft.entity.EntityLivingBase; import net.minecraft.entity.player.EntityPlayer; import net.minecraft.item.EnumAction; -import net.minecraft.util.EnumHand; import net.minecraft.world.World; public class IceCharge extends Spell { public IceCharge() { - super(Tier.ADVANCED, 20, Element.ICE, "ice_charge", SpellType.ATTACK, 30, EnumAction.NONE, false); + super(EnumTier.ADVANCED, 20, EnumElement.ICE, "ice_charge", EnumSpellType.ATTACK, 30, EnumAction.none, false); } @Override @@ -27,31 +22,31 @@ public class IceCharge extends Spell { } @Override - public boolean cast(World world, EntityPlayer caster, EnumHand hand, int ticksInUse, SpellModifiers modifiers) { + public boolean cast(World world, EntityPlayer caster, int ticksInUse, float damageMultiplier, float rangeMultiplier, float durationMultiplier, float blastMultiplier) { if(!world.isRemote){ - EntityIceCharge icecharge = new EntityIceCharge(world, caster, modifiers.get(SpellModifiers.DAMAGE), modifiers.get(WizardryItems.blast_upgrade)); + EntityIceCharge icecharge = new EntityIceCharge(world, caster, damageMultiplier, blastMultiplier); world.spawnEntityInWorld(icecharge); } - caster.swingArm(hand); - WizardryUtilities.playSoundAtPlayer(caster, WizardrySounds.SPELL_ICE, 1.0F, world.rand.nextFloat() * 0.4F + 1.4F); + caster.swingItem(); + world.playSoundAtEntity(caster, "wizardry:ice", 1.0F, world.rand.nextFloat() * 0.4F + 1.4F); return true; } @Override - public boolean cast(World world, EntityLiving caster, EnumHand hand, int ticksInUse, EntityLivingBase target, SpellModifiers modifiers){ + public boolean cast(World world, EntityLiving caster, EntityLivingBase target, float damageMultiplier, float rangeMultiplier, float durationMultiplier, float blastMultiplier){ if(target != null){ if(!world.isRemote){ - EntityIceCharge icecharge = new EntityIceCharge(world, caster, modifiers.get(SpellModifiers.DAMAGE), modifiers.get(WizardryItems.blast_upgrade)); + EntityIceCharge icecharge = new EntityIceCharge(world, caster, damageMultiplier, blastMultiplier); icecharge.directTowards(target, 1.5f); world.spawnEntityInWorld(icecharge); } - caster.swingArm(hand); - caster.playSound(WizardrySounds.SPELL_ICE, 1.0F, world.rand.nextFloat() * 0.4F + 1.4F); + caster.swingItem(); + world.playSoundAtEntity(caster, "wizardry:ice", 1.0F, world.rand.nextFloat() * 0.4F + 1.4F); return true; } diff --git a/src/main/java/electroblob/wizardry/spell/IceLance.java b/src/main/java/electroblob/wizardry/spell/IceLance.java index dcef7c07..6d571a35 100644 --- a/src/main/java/electroblob/wizardry/spell/IceLance.java +++ b/src/main/java/electroblob/wizardry/spell/IceLance.java @@ -1,24 +1,19 @@ package electroblob.wizardry.spell; -import electroblob.wizardry.constants.Element; -import electroblob.wizardry.constants.SpellType; -import electroblob.wizardry.constants.Tier; +import electroblob.wizardry.EnumElement; +import electroblob.wizardry.EnumSpellType; +import electroblob.wizardry.EnumTier; import electroblob.wizardry.entity.projectile.EntityIceLance; -import electroblob.wizardry.registry.WizardryItems; -import electroblob.wizardry.registry.WizardrySounds; -import electroblob.wizardry.util.SpellModifiers; -import electroblob.wizardry.util.WizardryUtilities; import net.minecraft.entity.EntityLiving; import net.minecraft.entity.EntityLivingBase; import net.minecraft.entity.player.EntityPlayer; import net.minecraft.item.EnumAction; -import net.minecraft.util.EnumHand; import net.minecraft.world.World; public class IceLance extends Spell { public IceLance() { - super(Tier.ADVANCED, 20, Element.ICE, "ice_lance", SpellType.ATTACK, 20, EnumAction.NONE, false); + super(EnumTier.ADVANCED, 20, EnumElement.ICE, "ice_lance", EnumSpellType.ATTACK, 20, EnumAction.none, false); } @Override @@ -27,28 +22,28 @@ public class IceLance extends Spell { } @Override - public boolean cast(World world, EntityPlayer caster, EnumHand hand, int ticksInUse, SpellModifiers modifiers) { + public boolean cast(World world, EntityPlayer caster, int ticksInUse, float damageMultiplier, float rangeMultiplier, float durationMultiplier, float blastMultiplier) { if(!world.isRemote){ - EntityIceLance iceLance = new EntityIceLance(world, caster, 2*modifiers.get(WizardryItems.range_upgrade), modifiers.get(SpellModifiers.DAMAGE)); + EntityIceLance iceLance = new EntityIceLance(world, caster, 2*rangeMultiplier, damageMultiplier); world.spawnEntityInWorld(iceLance); } - caster.swingArm(hand); - WizardryUtilities.playSoundAtPlayer(caster, WizardrySounds.SPELL_ICE, 1.0F, world.rand.nextFloat() * 0.4F + 0.8F); + caster.swingItem(); + world.playSoundAtEntity(caster, "wizardry:ice", 1.0F, world.rand.nextFloat() * 0.4F + 0.8F); return true; } @Override - public boolean cast(World world, EntityLiving caster, EnumHand hand, int ticksInUse, EntityLivingBase target, SpellModifiers modifiers){ + public boolean cast(World world, EntityLiving caster, EntityLivingBase target, float damageMultiplier, float rangeMultiplier, float durationMultiplier, float blastMultiplier){ if(target != null){ if(!world.isRemote){ - EntityIceLance iceLance = new EntityIceLance(world, caster, target, 2*modifiers.get(WizardryItems.range_upgrade), 4, modifiers.get(SpellModifiers.DAMAGE)); + EntityIceLance iceLance = new EntityIceLance(world, caster, target, 2*rangeMultiplier, 4, damageMultiplier); world.spawnEntityInWorld(iceLance); } - caster.swingArm(hand); - caster.playSound(WizardrySounds.SPELL_ICE, 1.0F, world.rand.nextFloat() * 0.4F + 0.8F); + caster.swingItem(); + world.playSoundAtEntity(caster, "wizardry:ice", 1.0F, world.rand.nextFloat() * 0.4F + 0.8F); return true; } diff --git a/src/main/java/electroblob/wizardry/spell/IceShard.java b/src/main/java/electroblob/wizardry/spell/IceShard.java index ddda2a1d..a361460c 100644 --- a/src/main/java/electroblob/wizardry/spell/IceShard.java +++ b/src/main/java/electroblob/wizardry/spell/IceShard.java @@ -1,24 +1,19 @@ package electroblob.wizardry.spell; -import electroblob.wizardry.constants.Element; -import electroblob.wizardry.constants.SpellType; -import electroblob.wizardry.constants.Tier; +import electroblob.wizardry.EnumElement; +import electroblob.wizardry.EnumSpellType; +import electroblob.wizardry.EnumTier; import electroblob.wizardry.entity.projectile.EntityIceShard; -import electroblob.wizardry.registry.WizardryItems; -import electroblob.wizardry.registry.WizardrySounds; -import electroblob.wizardry.util.SpellModifiers; -import electroblob.wizardry.util.WizardryUtilities; import net.minecraft.entity.EntityLiving; import net.minecraft.entity.EntityLivingBase; import net.minecraft.entity.player.EntityPlayer; import net.minecraft.item.EnumAction; -import net.minecraft.util.EnumHand; import net.minecraft.world.World; public class IceShard extends Spell { public IceShard() { - super(Tier.APPRENTICE, 10, Element.ICE, "ice_shard", SpellType.ATTACK, 10, EnumAction.NONE, false); + super(EnumTier.APPRENTICE, 10, EnumElement.ICE, "ice_shard", EnumSpellType.ATTACK, 10, EnumAction.none, false); } @Override @@ -27,28 +22,28 @@ public class IceShard extends Spell { } @Override - public boolean cast(World world, EntityPlayer caster, EnumHand hand, int ticksInUse, SpellModifiers modifiers) { + public boolean cast(World world, EntityPlayer caster, int ticksInUse, float damageMultiplier, float rangeMultiplier, float durationMultiplier, float blastMultiplier) { if(!world.isRemote){ - EntityIceShard iceShard = new EntityIceShard(world, caster, 2*modifiers.get(WizardryItems.range_upgrade), modifiers.get(SpellModifiers.DAMAGE)); + EntityIceShard iceShard = new EntityIceShard(world, caster, 2*rangeMultiplier, damageMultiplier); world.spawnEntityInWorld(iceShard); } - caster.swingArm(hand); - WizardryUtilities.playSoundAtPlayer(caster, WizardrySounds.SPELL_ICE, 1.0F, world.rand.nextFloat() * 0.4F + 1.4F); + caster.swingItem(); + world.playSoundAtEntity(caster, "wizardry:ice", 1.0F, world.rand.nextFloat() * 0.4F + 1.4F); return true; } @Override - public boolean cast(World world, EntityLiving caster, EnumHand hand, int ticksInUse, EntityLivingBase target, SpellModifiers modifiers){ + public boolean cast(World world, EntityLiving caster, EntityLivingBase target, float damageMultiplier, float rangeMultiplier, float durationMultiplier, float blastMultiplier){ if(target != null){ if(!world.isRemote){ - EntityIceShard iceShard = new EntityIceShard(world, caster, target, 2*modifiers.get(WizardryItems.range_upgrade), 4, modifiers.get(SpellModifiers.DAMAGE)); + EntityIceShard iceShard = new EntityIceShard(world, caster, target, 2*rangeMultiplier, 4, damageMultiplier); world.spawnEntityInWorld(iceShard); } - caster.swingArm(hand); - caster.playSound(WizardrySounds.SPELL_ICE, 1.0F, world.rand.nextFloat() * 0.4F + 1.4F); + caster.swingItem(); + world.playSoundAtEntity(caster, "wizardry:ice", 1.0F, world.rand.nextFloat() * 0.4F + 1.4F); return true; } diff --git a/src/main/java/electroblob/wizardry/spell/IceShroud.java b/src/main/java/electroblob/wizardry/spell/IceShroud.java index 85843dc1..82b2d9f0 100644 --- a/src/main/java/electroblob/wizardry/spell/IceShroud.java +++ b/src/main/java/electroblob/wizardry/spell/IceShroud.java @@ -1,25 +1,21 @@ package electroblob.wizardry.spell; -import electroblob.wizardry.constants.Element; -import electroblob.wizardry.constants.SpellType; -import electroblob.wizardry.constants.Tier; -import electroblob.wizardry.registry.WizardryItems; -import electroblob.wizardry.registry.WizardryPotions; -import electroblob.wizardry.registry.WizardrySounds; -import electroblob.wizardry.util.SpellModifiers; -import electroblob.wizardry.util.WizardryUtilities; +import electroblob.wizardry.EnumElement; +import electroblob.wizardry.EnumSpellType; +import electroblob.wizardry.EnumTier; +import electroblob.wizardry.ExtendedPlayer; +import electroblob.wizardry.Wizardry; import net.minecraft.entity.EntityLiving; import net.minecraft.entity.EntityLivingBase; import net.minecraft.entity.player.EntityPlayer; import net.minecraft.item.EnumAction; import net.minecraft.potion.PotionEffect; -import net.minecraft.util.EnumHand; import net.minecraft.world.World; public class IceShroud extends Spell { public IceShroud() { - super(Tier.ADVANCED, 40, Element.ICE, "ice_shroud", SpellType.DEFENCE, 250, EnumAction.BOW, false); + super(EnumTier.ADVANCED, 40, EnumElement.ICE, "ice_shroud", EnumSpellType.DEFENCE, 250, EnumAction.bow, false); } @Override @@ -28,29 +24,29 @@ public class IceShroud extends Spell { } @Override - public boolean cast(World world, EntityPlayer caster, EnumHand hand, int ticksInUse, SpellModifiers modifiers) { + public boolean cast(World world, EntityPlayer caster, int ticksInUse, float damageMultiplier, float rangeMultiplier, float durationMultiplier, float blastMultiplier) { // Cannot be cast when it has already been cast - if(!caster.isPotionActive(WizardryPotions.ice_shroud)){ + if(!caster.isPotionActive(Wizardry.iceShroud)){ if(!world.isRemote){ - caster.addPotionEffect(new PotionEffect(WizardryPotions.ice_shroud, (int)(600*modifiers.get(WizardryItems.duration_upgrade)), 0)); + caster.addPotionEffect(new PotionEffect(Wizardry.iceShroud.id, (int)(600*durationMultiplier), 0, true)); + world.playSoundAtEntity(caster, "wizardry:ice", 1.0F, world.rand.nextFloat() * 0.4F + 1.4F); } - WizardryUtilities.playSoundAtPlayer(caster, WizardrySounds.SPELL_ICE, 1.0F, world.rand.nextFloat() * 0.4F + 1.4F); return true; } return false; } @Override - public boolean cast(World world, EntityLiving caster, EnumHand hand, int ticksInUse, EntityLivingBase target, SpellModifiers modifiers) { + public boolean cast(World world, EntityLiving caster, EntityLivingBase target, float damageMultiplier, float rangeMultiplier, float durationMultiplier, float blastMultiplier) { if(target != null){ // Cannot be cast when it has already been cast - if(!caster.isPotionActive(WizardryPotions.ice_shroud)){ + if(!caster.isPotionActive(Wizardry.iceShroud)){ if(!world.isRemote){ - caster.addPotionEffect(new PotionEffect(WizardryPotions.ice_shroud, (int)(600*modifiers.get(WizardryItems.duration_upgrade)), 0)); + caster.addPotionEffect(new PotionEffect(Wizardry.iceShroud.id, (int)(600*durationMultiplier), 0, true)); + world.playSoundAtEntity(caster, "wizardry:ice", 1.0F, world.rand.nextFloat() * 0.4F + 1.4F); } - caster.playSound(WizardrySounds.SPELL_ICE, 1.0F, world.rand.nextFloat() * 0.4F + 1.4F); return true; } return false; diff --git a/src/main/java/electroblob/wizardry/spell/IceSpikes.java b/src/main/java/electroblob/wizardry/spell/IceSpikes.java index 95325e0f..917a21ab 100644 --- a/src/main/java/electroblob/wizardry/spell/IceSpikes.java +++ b/src/main/java/electroblob/wizardry/spell/IceSpikes.java @@ -1,28 +1,23 @@ package electroblob.wizardry.spell; -import electroblob.wizardry.constants.Element; -import electroblob.wizardry.constants.SpellType; -import electroblob.wizardry.constants.Tier; +import electroblob.wizardry.EnumElement; +import electroblob.wizardry.EnumSpellType; +import electroblob.wizardry.EnumTier; +import electroblob.wizardry.WizardryUtilities; import electroblob.wizardry.entity.construct.EntityIceSpike; -import electroblob.wizardry.registry.WizardryItems; -import electroblob.wizardry.registry.WizardrySounds; -import electroblob.wizardry.util.SpellModifiers; -import electroblob.wizardry.util.WizardryUtilities; import net.minecraft.entity.EntityLiving; import net.minecraft.entity.EntityLivingBase; import net.minecraft.entity.player.EntityPlayer; import net.minecraft.item.EnumAction; -import net.minecraft.util.EnumFacing; -import net.minecraft.util.EnumHand; -import net.minecraft.util.math.BlockPos; -import net.minecraft.util.math.MathHelper; -import net.minecraft.util.math.RayTraceResult; +import net.minecraft.util.MathHelper; +import net.minecraft.util.MovingObjectPosition; +import net.minecraft.util.MovingObjectPosition.MovingObjectType; import net.minecraft.world.World; public class IceSpikes extends Spell { public IceSpikes() { - super(Tier.ADVANCED, 30, Element.ICE, "ice_spikes", SpellType.ATTACK, 75, EnumAction.NONE, false); + super(EnumTier.ADVANCED, 30, EnumElement.ICE, "ice_spikes", EnumSpellType.ATTACK, 75, EnumAction.none, false); } @Override @@ -31,43 +26,43 @@ public class IceSpikes extends Spell { } @Override - public boolean cast(World world, EntityPlayer caster, EnumHand hand, int ticksInUse, SpellModifiers modifiers) { + public boolean cast(World world, EntityPlayer caster, int ticksInUse, float damageMultiplier, float rangeMultiplier, float durationMultiplier, float blastMultiplier) { - RayTraceResult rayTrace = WizardryUtilities.rayTrace(20*modifiers.get(WizardryItems.range_upgrade), world, caster, false); + MovingObjectPosition rayTrace = WizardryUtilities.rayTrace(20*rangeMultiplier, world, caster, false); - if(rayTrace != null && rayTrace.typeOfHit == RayTraceResult.Type.BLOCK && rayTrace.sideHit == EnumFacing.UP){ + if(rayTrace != null && rayTrace.typeOfHit == MovingObjectType.BLOCK && rayTrace.sideHit == 1){ if(!world.isRemote){ - double x = rayTrace.hitVec.xCoord; - double y = rayTrace.hitVec.yCoord; - double z = rayTrace.hitVec.zCoord; + double x = rayTrace.blockX; + double y = rayTrace.blockY; + double z = rayTrace.blockZ; - for(int i=0; i<(int)(18*modifiers.get(WizardryItems.blast_upgrade)); i++){ + for(int i=0; i<(int)(18*blastMultiplier); i++){ float angle = (float)(world.rand.nextFloat()*Math.PI*2); - double radius = 0.5 + world.rand.nextDouble()*2*modifiers.get(WizardryItems.blast_upgrade); + double radius = 0.5 + world.rand.nextDouble()*2*blastMultiplier; double x1 = x + radius*MathHelper.sin(angle); double z1 = z + radius*MathHelper.cos(angle); - double y1 = WizardryUtilities.getNearestFloorLevel(world, new BlockPos(MathHelper.floor_double(x1), (int)y, MathHelper.floor_double(z1)), 2) - 1; + double y1 = WizardryUtilities.getNearestFloorLevel(world, MathHelper.floor_double(x1), (int)y, MathHelper.floor_double(z1), 2) - 1; if(y1 > -1){ - EntityIceSpike icespike = new EntityIceSpike(world, x1, y1, z1, caster, 30 + world.rand.nextInt(15), modifiers.get(SpellModifiers.DAMAGE)); + EntityIceSpike icespike = new EntityIceSpike(world, x1, y1, z1, caster, 30 + world.rand.nextInt(15), damageMultiplier); world.spawnEntityInWorld(icespike); } } } - caster.swingArm(hand); - WizardryUtilities.playSoundAtPlayer(caster, WizardrySounds.SPELL_ICE, 1.0F, 1.0F); + caster.swingItem(); + world.playSoundAtEntity(caster, "wizardry:ice", 1.0F, 1.0F); return true; } return false; } @Override - public boolean cast(World world, EntityLiving caster, EnumHand hand, int ticksInUse, EntityLivingBase target, SpellModifiers modifiers){ + public boolean cast(World world, EntityLiving caster, EntityLivingBase target, float damageMultiplier, float rangeMultiplier, float durationMultiplier, float blastMultiplier){ if(target != null){ @@ -77,23 +72,23 @@ public class IceSpikes extends Spell { double y = target.posY; double z = target.posZ; - for(int i=0; i<(int)(18*modifiers.get(WizardryItems.blast_upgrade)); i++){ + for(int i=0; i<(int)(18*blastMultiplier); i++){ float angle = (float)(world.rand.nextFloat()*Math.PI*2); - double radius = 0.5 + world.rand.nextDouble()*2*modifiers.get(WizardryItems.blast_upgrade); + double radius = 0.5 + world.rand.nextDouble()*2*blastMultiplier; double x1 = x + radius*MathHelper.sin(angle); double z1 = z + radius*MathHelper.cos(angle); - double y1 = WizardryUtilities.getNearestFloorLevel(world, new BlockPos(MathHelper.floor_double(x1), (int)y, MathHelper.floor_double(z1)), 2) - 1; + double y1 = WizardryUtilities.getNearestFloorLevel(world, MathHelper.floor_double(x1), (int)y, MathHelper.floor_double(z1), 2) - 1; if(y1 > -1){ - EntityIceSpike icespike = new EntityIceSpike(world, x1, y1, z1, caster, 30 + world.rand.nextInt(15), modifiers.get(SpellModifiers.DAMAGE)); + EntityIceSpike icespike = new EntityIceSpike(world, x1, y1, z1, caster, 30 + world.rand.nextInt(15), damageMultiplier); world.spawnEntityInWorld(icespike); } } } - caster.swingArm(hand); - caster.playSound(WizardrySounds.SPELL_ICE, 1.0F, 1.0F); + caster.swingItem(); + world.playSoundAtEntity(caster, "wizardry:ice", 1.0F, 1.0F); return true; } diff --git a/src/main/java/electroblob/wizardry/spell/IceStatue.java b/src/main/java/electroblob/wizardry/spell/IceStatue.java index 1928421a..9635ce50 100644 --- a/src/main/java/electroblob/wizardry/spell/IceStatue.java +++ b/src/main/java/electroblob/wizardry/spell/IceStatue.java @@ -1,25 +1,19 @@ package electroblob.wizardry.spell; +import electroblob.wizardry.EnumElement; +import electroblob.wizardry.EnumParticleType; +import electroblob.wizardry.EnumSpellType; +import electroblob.wizardry.EnumTier; import electroblob.wizardry.Wizardry; -import electroblob.wizardry.constants.Element; -import electroblob.wizardry.constants.SpellType; -import electroblob.wizardry.constants.Tier; -import electroblob.wizardry.registry.WizardryAchievements; -import electroblob.wizardry.registry.WizardryBlocks; -import electroblob.wizardry.registry.WizardryItems; -import electroblob.wizardry.registry.WizardrySounds; +import electroblob.wizardry.WizardryUtilities; import electroblob.wizardry.tileentity.TileEntityStatue; -import electroblob.wizardry.util.SpellModifiers; -import electroblob.wizardry.util.WizardryParticleType; -import electroblob.wizardry.util.WizardryUtilities; import net.minecraft.entity.EntityLiving; import net.minecraft.entity.monster.EntityBlaze; import net.minecraft.entity.player.EntityPlayer; import net.minecraft.item.EnumAction; -import net.minecraft.util.EnumHand; -import net.minecraft.util.math.BlockPos; -import net.minecraft.util.math.RayTraceResult; -import net.minecraft.util.math.Vec3d; +import net.minecraft.util.MovingObjectPosition; +import net.minecraft.util.MovingObjectPosition.MovingObjectType; +import net.minecraft.util.Vec3; import net.minecraft.world.World; public class IceStatue extends Spell { @@ -27,21 +21,23 @@ public class IceStatue extends Spell { private static final int baseDuration = 400; public IceStatue() { - super(Tier.APPRENTICE, 15, Element.ICE, "ice_statue", SpellType.ATTACK, 40, EnumAction.NONE, false); + super(EnumTier.APPRENTICE, 15, EnumElement.ICE, "ice_statue", EnumSpellType.ATTACK, 40, EnumAction.none, false); } @Override - public boolean cast(World world, EntityPlayer caster, EnumHand hand, int ticksInUse, SpellModifiers modifiers) { + public boolean cast(World world, EntityPlayer caster, int ticksInUse, float damageMultiplier, float rangeMultiplier, float durationMultiplier, float blastMultiplier) { - Vec3d look = caster.getLookVec(); + Vec3 look = caster.getLookVec(); - RayTraceResult rayTrace = WizardryUtilities.standardEntityRayTrace(world, caster, 10*modifiers.get(WizardryItems.range_upgrade)); + MovingObjectPosition rayTrace = WizardryUtilities.standardEntityRayTrace(world, caster, 10*rangeMultiplier); - if(rayTrace != null && rayTrace.typeOfHit == RayTraceResult.Type.ENTITY && rayTrace.entityHit instanceof EntityLiving && !world.isRemote){ + if(rayTrace != null && rayTrace.typeOfHit == MovingObjectType.ENTITY && rayTrace.entityHit instanceof EntityLiving && !world.isRemote){ EntityLiving target = (EntityLiving) rayTrace.entityHit; - - BlockPos pos = new BlockPos(target); + + int x = (int)Math.floor(target.posX); + int y = (int)Math.floor(target.posY); + int z = (int)Math.floor(target.posZ); if(target.isBurning()){ target.extinguish(); @@ -50,69 +46,69 @@ public class IceStatue extends Spell { // Stops the entity looking red while frozen and the resulting z-fighting target.hurtTime = 0; - if(target instanceof EntityBlaze) caster.addStat(WizardryAchievements.freeze_blaze, 1); + if(target instanceof EntityBlaze) caster.addStat(Wizardry.freezeBlaze, 1); // Short mobs such as spiders and pigs - if((target.height < 1.2 || target.isChild()) && WizardryUtilities.canBlockBeReplaced(world, pos)){ - world.setBlockState(pos, WizardryBlocks.ice_statue.getDefaultState()); - if(world.getTileEntity(pos) instanceof TileEntityStatue){ - ((TileEntityStatue)world.getTileEntity(pos)).setCreatureAndPart(target, 1, 1); - ((TileEntityStatue)world.getTileEntity(pos)).setLifetime((int)(baseDuration*modifiers.get(WizardryItems.duration_upgrade))); + if((target.height < 1.2 || target.isChild()) && WizardryUtilities.canBlockBeReplaced(world, x, y, z)){ + world.setBlock(x, y, z, Wizardry.iceStatue); + if(world.getTileEntity(x, y, z) instanceof TileEntityStatue){ + ((TileEntityStatue)world.getTileEntity(x, y, z)).setCreatureAndPart(target, 1, 1); + ((TileEntityStatue)world.getTileEntity(x, y, z)).setLifetime((int)(baseDuration*durationMultiplier)); } target.setDead(); - target.playSound(WizardrySounds.SPELL_FREEZE, 1.0F, world.rand.nextFloat() * 0.4F + 0.8F); + world.playSoundAtEntity(target, "wizardry:freeze", 1.0F, world.rand.nextFloat() * 0.4F + 0.8F); } // Normal sized mobs like zombies and skeletons - else if(target.height < 2.5 && WizardryUtilities.canBlockBeReplaced(world, pos) && WizardryUtilities.canBlockBeReplaced(world, pos.up())){ - world.setBlockState(pos, WizardryBlocks.ice_statue.getDefaultState()); - if(world.getTileEntity(pos) instanceof TileEntityStatue){ - ((TileEntityStatue)world.getTileEntity(pos)).setCreatureAndPart(target, 1, 2); - ((TileEntityStatue)world.getTileEntity(pos)).setLifetime((int)(baseDuration*modifiers.get(WizardryItems.duration_upgrade))); + else if(target.height < 2.5 && WizardryUtilities.canBlockBeReplaced(world, x, y, z) && WizardryUtilities.canBlockBeReplaced(world, x, y+1, z)){ + world.setBlock(x, y, z, Wizardry.iceStatue); + if(world.getTileEntity(x, y, z) instanceof TileEntityStatue){ + ((TileEntityStatue)world.getTileEntity(x, y, z)).setCreatureAndPart(target, 1, 2); + ((TileEntityStatue)world.getTileEntity(x, y, z)).setLifetime((int)(baseDuration*durationMultiplier)); } - world.setBlockState(pos.up(), WizardryBlocks.ice_statue.getDefaultState()); - if(world.getTileEntity(pos.up()) instanceof TileEntityStatue){ - ((TileEntityStatue)world.getTileEntity(pos.up())).setCreatureAndPart(target, 2, 2); + world.setBlock(x, y+1, z, Wizardry.iceStatue); + if(world.getTileEntity(x, y+1, z) instanceof TileEntityStatue){ + ((TileEntityStatue)world.getTileEntity(x, y+1, z)).setCreatureAndPart(target, 2, 2); } target.setDead(); - target.playSound(WizardrySounds.SPELL_FREEZE, 1.0F, world.rand.nextFloat() * 0.4F + 0.8F); + world.playSoundAtEntity(target, "wizardry:freeze", 1.0F, world.rand.nextFloat() * 0.4F + 0.8F); } // Tall mobs like endermen and iron golems - else if(WizardryUtilities.canBlockBeReplaced(world, pos) && WizardryUtilities.canBlockBeReplaced(world, pos.up()) && WizardryUtilities.canBlockBeReplaced(world, pos.up(2))){ - world.setBlockState(pos, WizardryBlocks.ice_statue.getDefaultState()); - if(world.getTileEntity(pos) instanceof TileEntityStatue){ - ((TileEntityStatue)world.getTileEntity(pos)).setCreatureAndPart(target, 1, 3); - ((TileEntityStatue)world.getTileEntity(pos)).setLifetime((int)(baseDuration*modifiers.get(WizardryItems.duration_upgrade))); + else if(WizardryUtilities.canBlockBeReplaced(world, x, y, z) && WizardryUtilities.canBlockBeReplaced(world, x, y+1, z) && WizardryUtilities.canBlockBeReplaced(world, x, y+2, z)){ + world.setBlock(x, y, z, Wizardry.iceStatue); + if(world.getTileEntity(x, y, z) instanceof TileEntityStatue){ + ((TileEntityStatue)world.getTileEntity(x, y, z)).setCreatureAndPart(target, 1, 3); + ((TileEntityStatue)world.getTileEntity(x, y, z)).setLifetime((int)(baseDuration*durationMultiplier)); } - world.setBlockState(pos.up(), WizardryBlocks.ice_statue.getDefaultState()); - if(world.getTileEntity(pos.up()) instanceof TileEntityStatue){ - ((TileEntityStatue)world.getTileEntity(pos.up())).setCreatureAndPart(target, 2, 3); + world.setBlock(x, y+1, z, Wizardry.iceStatue); + if(world.getTileEntity(x, y+1, z) instanceof TileEntityStatue){ + ((TileEntityStatue)world.getTileEntity(x, y+1, z)).setCreatureAndPart(target, 2, 3); } - world.setBlockState(pos.up(2), WizardryBlocks.ice_statue.getDefaultState()); - if(world.getTileEntity(pos.up(2)) instanceof TileEntityStatue){ - ((TileEntityStatue)world.getTileEntity(pos.up(2))).setCreatureAndPart(target, 3, 3); + world.setBlock(x, y+2, z, Wizardry.iceStatue); + if(world.getTileEntity(x, y+2, z) instanceof TileEntityStatue){ + ((TileEntityStatue)world.getTileEntity(x, y+2, z)).setCreatureAndPart(target, 3, 3); } target.setDead(); - target.playSound(WizardrySounds.SPELL_FREEZE, 1.0F, world.rand.nextFloat() * 0.4F + 0.8F); + world.playSoundAtEntity(target, "wizardry:freeze", 1.0F, world.rand.nextFloat() * 0.4F + 0.8F); } } if(world.isRemote){ - for(int i=1; i<(int)(25*modifiers.get(WizardryItems.range_upgrade)); i+=2){ + for(int i=1; i<(int)(25*rangeMultiplier); i+=2){ float brightness = 0.5f + (world.rand.nextFloat()/2); double x1 = caster.posX + look.xCoord*i/2 + world.rand.nextFloat()/5 - 0.1f; double y1 = WizardryUtilities.getPlayerEyesPos(caster) - 0.4f + look.yCoord*i/2 + world.rand.nextFloat()/5 - 0.1f; double z1 = caster.posZ + look.zCoord*i/2 + world.rand.nextFloat()/5 - 0.1f; - Wizardry.proxy.spawnParticle(WizardryParticleType.SPARKLE, world, x1, y1, z1, 0.0d, 0.0d, 0.0d, 12 + world.rand.nextInt(8), brightness, brightness + 0.1f, 1.0f); - Wizardry.proxy.spawnParticle(WizardryParticleType.SNOW, world, x1, y1, z1, 0.0d, -0.02d, 0.0d, 20 + world.rand.nextInt(10)); + Wizardry.proxy.spawnParticle(EnumParticleType.SPARKLE, world, x1, y1, z1, 0.0d, 0.0d, 0.0d, 12 + world.rand.nextInt(8), brightness, brightness + 0.1f, 1.0f); + Wizardry.proxy.spawnParticle(EnumParticleType.SNOW, world, x1, y1, z1, 0.0d, -0.02d, 0.0d, 20 + world.rand.nextInt(10)); } } - caster.swingArm(hand); - WizardryUtilities.playSoundAtPlayer(caster, WizardrySounds.SPELL_ICE, 1.0F, world.rand.nextFloat() * 0.4F + 1.2F); + caster.swingItem(); + world.playSoundAtEntity(caster, "wizardry:ice", 1.0F, world.rand.nextFloat() * 0.4F + 1.2F); return true; } diff --git a/src/main/java/electroblob/wizardry/spell/Ignite.java b/src/main/java/electroblob/wizardry/spell/Ignite.java index 2807a570..a6eb665e 100644 --- a/src/main/java/electroblob/wizardry/spell/Ignite.java +++ b/src/main/java/electroblob/wizardry/spell/Ignite.java @@ -1,97 +1,141 @@ package electroblob.wizardry.spell; -import electroblob.wizardry.constants.Element; -import electroblob.wizardry.constants.SpellType; -import electroblob.wizardry.constants.Tier; -import electroblob.wizardry.registry.WizardryItems; -import electroblob.wizardry.util.MagicDamage; -import electroblob.wizardry.util.MagicDamage.DamageType; -import electroblob.wizardry.util.SpellModifiers; -import electroblob.wizardry.util.WizardryUtilities; +import electroblob.wizardry.EnumElement; +import electroblob.wizardry.EnumSpellType; +import electroblob.wizardry.EnumTier; +import electroblob.wizardry.MagicDamage; +import electroblob.wizardry.WizardryUtilities; +import electroblob.wizardry.MagicDamage.DamageType; +import electroblob.wizardry.entity.living.EntityIceGiant; +import electroblob.wizardry.entity.living.EntityIceWraith; import net.minecraft.entity.EntityLiving; import net.minecraft.entity.EntityLivingBase; +import net.minecraft.entity.monster.EntitySnowman; import net.minecraft.entity.player.EntityPlayer; import net.minecraft.init.Blocks; -import net.minecraft.init.SoundEvents; import net.minecraft.item.EnumAction; -import net.minecraft.util.EnumHand; -import net.minecraft.util.EnumParticleTypes; -import net.minecraft.util.math.BlockPos; -import net.minecraft.util.math.RayTraceResult; -import net.minecraft.util.text.TextComponentTranslation; +import net.minecraft.util.ChatComponentTranslation; +import net.minecraft.util.MovingObjectPosition; +import net.minecraft.util.MovingObjectPosition.MovingObjectType; import net.minecraft.world.World; public class Ignite extends Spell { public Ignite() { - super(Tier.BASIC, 5, Element.FIRE, "ignite", SpellType.ATTACK, 10, EnumAction.NONE, false); + super(EnumTier.BASIC, 5, EnumElement.FIRE, "ignite", EnumSpellType.ATTACK, 10, EnumAction.none, false); } @Override - public boolean cast(World world, EntityPlayer caster, EnumHand hand, int ticksInUse, SpellModifiers modifiers) { - + public boolean cast(World world, EntityPlayer caster, int ticksInUse, float damageMultiplier, float rangeMultiplier, float durationMultiplier, float blastMultiplier) { + // Entity ray trace is done first because block ray trace passes through entities; if it was the other // way round, entities would only be hit when there were no blocks in range behind them. - RayTraceResult rayTrace = WizardryUtilities.standardEntityRayTrace(world, caster, 10*modifiers.get(WizardryItems.range_upgrade)); - - if(rayTrace != null && rayTrace.typeOfHit == RayTraceResult.Type.ENTITY && rayTrace.entityHit instanceof EntityLivingBase){ - + MovingObjectPosition rayTrace = WizardryUtilities.standardEntityRayTrace(world, caster, 10*rangeMultiplier); + + if(rayTrace != null && rayTrace.typeOfHit == MovingObjectType.ENTITY && rayTrace.entityHit instanceof EntityLivingBase){ + EntityLivingBase target = (EntityLivingBase) rayTrace.entityHit; - + if(MagicDamage.isEntityImmune(DamageType.FIRE, target)){ - if(!world.isRemote) caster.addChatComponentMessage(new TextComponentTranslation("spell.resist", target.getName(), this.getNameForTranslationFormatted())); + if(!world.isRemote) caster.addChatComponentMessage(new ChatComponentTranslation("spell.resist", target.getCommandSenderName(), this.getDisplayNameWithFormatting())); }else{ - target.setFire((int)(10*modifiers.get(WizardryItems.duration_upgrade))); + target.setFire((int)(10*durationMultiplier)); } - + if(world.isRemote){ double dx = target.posX - caster.posX; - double dy = (target.getEntityBoundingBox().minY + target.height/2) - WizardryUtilities.getPlayerEyesPos(caster); + double dy = (WizardryUtilities.getEntityFeetPos(target) + target.height/2) - WizardryUtilities.getPlayerEyesPos(caster); double dz = target.posZ - caster.posZ; // i starts at 1 so that particles are not spawned in the player's head. for(int i=1;i<5;i++){ - //WizardryUtilities.spawnParticleAndNotify(world, EnumParticleTypes.FLAME, caster.posX + (i*(dx/5)) + world.rand.nextFloat()/5, caster.posY + (i*(dy/5)) + world.rand.nextFloat()/5, caster.posZ + (i*(dz/5)) + world.rand.nextFloat()/5, 0, 0, 0, 0, 0, 0, 0); - //WizardryUtilities.spawnParticleAndNotify(world, EnumParticleTypes.FLAME, caster.posX + (i*(dx/5)) + world.rand.nextFloat()/5, caster.posY + (i*(dy/5)) + world.rand.nextFloat()/5, caster.posZ + (i*(dz/5)) + world.rand.nextFloat()/5, 0, 0, 0, 0, 0, 0, 0); - world.spawnParticle(EnumParticleTypes.FLAME, caster.posX + (i*(dx/5)) + world.rand.nextFloat()/5, WizardryUtilities.getPlayerEyesPos(caster) + (i*(dy/5)) + world.rand.nextFloat()/5, caster.posZ + (i*(dz/5)) + world.rand.nextFloat()/5, 0, 0, 0); - world.spawnParticle(EnumParticleTypes.FLAME, caster.posX + (i*(dx/5)) + world.rand.nextFloat()/5, WizardryUtilities.getPlayerEyesPos(caster) + (i*(dy/5)) + world.rand.nextFloat()/5, caster.posZ + (i*(dz/5)) + world.rand.nextFloat()/5, 0, 0, 0); + //WizardryUtilities.spawnParticleAndNotify(world, "flame", caster.posX + (i*(dx/5)) + world.rand.nextFloat()/5, caster.posY + (i*(dy/5)) + world.rand.nextFloat()/5, caster.posZ + (i*(dz/5)) + world.rand.nextFloat()/5, 0, 0, 0, 0, 0, 0, 0); + //WizardryUtilities.spawnParticleAndNotify(world, "flame", caster.posX + (i*(dx/5)) + world.rand.nextFloat()/5, caster.posY + (i*(dy/5)) + world.rand.nextFloat()/5, caster.posZ + (i*(dz/5)) + world.rand.nextFloat()/5, 0, 0, 0, 0, 0, 0, 0); + world.spawnParticle("flame", caster.posX + (i*(dx/5)) + world.rand.nextFloat()/5, WizardryUtilities.getPlayerEyesPos(caster) + (i*(dy/5)) + world.rand.nextFloat()/5, caster.posZ + (i*(dz/5)) + world.rand.nextFloat()/5, 0, 0, 0); + world.spawnParticle("flame", caster.posX + (i*(dx/5)) + world.rand.nextFloat()/5, WizardryUtilities.getPlayerEyesPos(caster) + (i*(dy/5)) + world.rand.nextFloat()/5, caster.posZ + (i*(dz/5)) + world.rand.nextFloat()/5, 0, 0, 0); } } - - caster.swingArm(hand); - WizardryUtilities.playSoundAtPlayer(caster, SoundEvents.ITEM_FLINTANDSTEEL_USE, 1.0F, world.rand.nextFloat() * 0.4F + 0.8F); - + + caster.swingItem(); + world.playSoundEffect((double)caster.posX + 0.5D, (double)caster.posY + 1.5D, (double)caster.posZ + 0.5D, "fire.ignite", 1.0F, world.rand.nextFloat() * 0.4F + 0.8F); + return true; - + }else{ - - rayTrace = WizardryUtilities.rayTrace(10*modifiers.get(WizardryItems.range_upgrade), world, caster, false); - + + rayTrace = WizardryUtilities.rayTrace(10*rangeMultiplier, world, caster, false); + // Gets block the player is looking at and sets the appropriate surrounding air block to fire. // Note how the block is set on the server side only (kinda obvious really) but the particles are // spawned on the client side only. - if(rayTrace != null && rayTrace.typeOfHit == RayTraceResult.Type.BLOCK){ - - BlockPos pos = rayTrace.getBlockPos().offset(rayTrace.sideHit); - - if(world.isAirBlock(pos)){ - if(!world.isRemote){ - world.setBlockState(pos, Blocks.FIRE.getDefaultState()); + if(rayTrace != null && rayTrace.typeOfHit == MovingObjectType.BLOCK){ + + int blockHitX = rayTrace.blockX; + int blockHitY = rayTrace.blockY; + int blockHitZ = rayTrace.blockZ; + int blockHitSide = rayTrace.sideHit; + boolean flag = false; + switch(blockHitSide){ + case 0: + if(world.isAirBlock(blockHitX, blockHitY-1, blockHitZ)){ + if(!world.isRemote){ + world.setBlock(blockHitX, blockHitY-1, blockHitZ, Blocks.fire); + } + flag = true; } - + break; + case 1: + if(world.isAirBlock(blockHitX, blockHitY+1, blockHitZ)){ + if(!world.isRemote){ + world.setBlock(blockHitX, blockHitY+1, blockHitZ, Blocks.fire); + } + flag = true; + } + break; + case 2: + if(world.isAirBlock(blockHitX, blockHitY, blockHitZ-1)){ + if(!world.isRemote){ + world.setBlock(blockHitX, blockHitY, blockHitZ-1, Blocks.fire); + } + flag = true; + } + break; + case 3: + if(world.isAirBlock(blockHitX, blockHitY, blockHitZ+1)){ + if(!world.isRemote){ + world.setBlock(blockHitX, blockHitY, blockHitZ+1, Blocks.fire); + } + flag = true; + } + break; + case 4: + if(world.isAirBlock(blockHitX-1, blockHitY, blockHitZ)){ + if(!world.isRemote){ + world.setBlock(blockHitX-1, blockHitY, blockHitZ, Blocks.fire); + } + flag = true; + } + break; + case 5: + if(world.isAirBlock(blockHitX+1, blockHitY, blockHitZ)){ + if(!world.isRemote){ + world.setBlock(blockHitX+1, blockHitY, blockHitZ, Blocks.fire); + } + flag = true; + } + break; + } + if(flag){ + double dx = blockHitX + 0.5 - caster.posX; + double dy = blockHitY + 1.5 - (WizardryUtilities.getPlayerEyesPos(caster) + 1); + double dz = blockHitZ + 0.5 - caster.posZ; if(world.isRemote){ - - double dx = pos.getX() + 0.5 - caster.posX; - double dy = pos.getY() + 0.5 - WizardryUtilities.getPlayerEyesPos(caster); - double dz = pos.getZ() + 0.5 - caster.posZ; - for(int i=1;i<5;i++){ - world.spawnParticle(EnumParticleTypes.FLAME, caster.posX + (i*(dx/5)) + world.rand.nextFloat()/5, WizardryUtilities.getPlayerEyesPos(caster) + (i*(dy/5)) + world.rand.nextFloat()/5, caster.posZ + (i*(dz/5)) + world.rand.nextFloat()/5, 0, 0, 0); - world.spawnParticle(EnumParticleTypes.FLAME, caster.posX + (i*(dx/5)) + world.rand.nextFloat()/5, WizardryUtilities.getPlayerEyesPos(caster) + (i*(dy/5)) + world.rand.nextFloat()/5, caster.posZ + (i*(dz/5)) + world.rand.nextFloat()/5, 0, 0, 0); + world.spawnParticle("flame", caster.posX + (i*(dx/5)) + world.rand.nextFloat()/5, WizardryUtilities.getPlayerEyesPos(caster) + (i*(dy/5)) + world.rand.nextFloat()/5, caster.posZ + (i*(dz/5)) + world.rand.nextFloat()/5, 0, 0, 0); + world.spawnParticle("flame", caster.posX + (i*(dx/5)) + world.rand.nextFloat()/5, WizardryUtilities.getPlayerEyesPos(caster) + (i*(dy/5)) + world.rand.nextFloat()/5, caster.posZ + (i*(dz/5)) + world.rand.nextFloat()/5, 0, 0, 0); } } - - caster.swingArm(hand); - WizardryUtilities.playSoundAtPlayer(caster, SoundEvents.ITEM_FLINTANDSTEEL_USE, 1.0F, world.rand.nextFloat() * 0.4F + 0.8F); + caster.swingItem(); + world.playSoundEffect((double)caster.posX + 0.5D, (double)caster.posY + 1.5D, (double)caster.posZ + 0.5D, "fire.ignite", 1.0F, world.rand.nextFloat() * 0.4F + 0.8F); return true; } } @@ -100,34 +144,34 @@ public class Ignite extends Spell { } @Override - public boolean cast(World world, EntityLiving caster, EnumHand hand, int ticksInUse, EntityLivingBase target, SpellModifiers modifiers){ - + public boolean cast(World world, EntityLiving caster, EntityLivingBase target, float damageMultiplier, float rangeMultiplier, float durationMultiplier, float blastMultiplier){ + if(target != null){ - - if(!MagicDamage.isEntityImmune(DamageType.FIRE, target)) target.setFire((int)(10*modifiers.get(WizardryItems.duration_upgrade))); - + + if(!MagicDamage.isEntityImmune(DamageType.FIRE, target)) target.setFire((int)(10*durationMultiplier)); + if(world.isRemote){ double dx = target.posX - caster.posX; - double dy = (target.getEntityBoundingBox().minY + target.height/2) - (caster.posY + caster.getEyeHeight()); + double dy = (WizardryUtilities.getEntityFeetPos(target) + target.height/2) - (caster.posY + caster.getEyeHeight()); double dz = target.posZ - caster.posZ; // i starts at 1 so that particles are not spawned in the player's head. for(int i=1;i<5;i++){ - //WizardryUtilities.spawnParticleAndNotify(world, EnumParticleTypes.FLAME, caster.posX + (i*(dx/5)) + world.rand.nextFloat()/5, caster.posY + (i*(dy/5)) + world.rand.nextFloat()/5, caster.posZ + (i*(dz/5)) + world.rand.nextFloat()/5, 0, 0, 0, 0, 0, 0, 0); - //WizardryUtilities.spawnParticleAndNotify(world, EnumParticleTypes.FLAME, caster.posX + (i*(dx/5)) + world.rand.nextFloat()/5, caster.posY + (i*(dy/5)) + world.rand.nextFloat()/5, caster.posZ + (i*(dz/5)) + world.rand.nextFloat()/5, 0, 0, 0, 0, 0, 0, 0); - world.spawnParticle(EnumParticleTypes.FLAME, caster.posX + (i*(dx/5)) + world.rand.nextFloat()/5, caster.posY + caster.getEyeHeight() + (i*(dy/5)) + world.rand.nextFloat()/5, caster.posZ + (i*(dz/5)) + world.rand.nextFloat()/5, 0, 0, 0); - world.spawnParticle(EnumParticleTypes.FLAME, caster.posX + (i*(dx/5)) + world.rand.nextFloat()/5, caster.posY + caster.getEyeHeight() + (i*(dy/5)) + world.rand.nextFloat()/5, caster.posZ + (i*(dz/5)) + world.rand.nextFloat()/5, 0, 0, 0); + //WizardryUtilities.spawnParticleAndNotify(world, "flame", caster.posX + (i*(dx/5)) + world.rand.nextFloat()/5, caster.posY + (i*(dy/5)) + world.rand.nextFloat()/5, caster.posZ + (i*(dz/5)) + world.rand.nextFloat()/5, 0, 0, 0, 0, 0, 0, 0); + //WizardryUtilities.spawnParticleAndNotify(world, "flame", caster.posX + (i*(dx/5)) + world.rand.nextFloat()/5, caster.posY + (i*(dy/5)) + world.rand.nextFloat()/5, caster.posZ + (i*(dz/5)) + world.rand.nextFloat()/5, 0, 0, 0, 0, 0, 0, 0); + world.spawnParticle("flame", caster.posX + (i*(dx/5)) + world.rand.nextFloat()/5, caster.posY + caster.getEyeHeight() + (i*(dy/5)) + world.rand.nextFloat()/5, caster.posZ + (i*(dz/5)) + world.rand.nextFloat()/5, 0, 0, 0); + world.spawnParticle("flame", caster.posX + (i*(dx/5)) + world.rand.nextFloat()/5, caster.posY + caster.getEyeHeight() + (i*(dy/5)) + world.rand.nextFloat()/5, caster.posZ + (i*(dz/5)) + world.rand.nextFloat()/5, 0, 0, 0); } } - - caster.swingArm(hand); - caster.playSound(SoundEvents.ITEM_FLINTANDSTEEL_USE, 1.0F, world.rand.nextFloat() * 0.4F + 0.8F); - + + caster.swingItem(); + world.playSoundEffect((double)caster.posX + 0.5D, (double)caster.posY + 1.5D, (double)caster.posZ + 0.5D, "fire.ignite", 1.0F, world.rand.nextFloat() * 0.4F + 0.8F); + return true; } - + return false; } - + @Override public boolean canBeCastByNPCs(){ return true; diff --git a/src/main/java/electroblob/wizardry/spell/ImbueWeapon.java b/src/main/java/electroblob/wizardry/spell/ImbueWeapon.java index f2dbbcf6..85f29331 100644 --- a/src/main/java/electroblob/wizardry/spell/ImbueWeapon.java +++ b/src/main/java/electroblob/wizardry/spell/ImbueWeapon.java @@ -1,71 +1,79 @@ package electroblob.wizardry.spell; -import electroblob.wizardry.WizardData; +import java.util.List; + +import electroblob.wizardry.EnumElement; +import electroblob.wizardry.EnumParticleType; +import electroblob.wizardry.EnumSpellType; +import electroblob.wizardry.EnumTier; +import electroblob.wizardry.ExtendedPlayer; import electroblob.wizardry.Wizardry; -import electroblob.wizardry.constants.Constants; -import electroblob.wizardry.constants.Element; -import electroblob.wizardry.constants.SpellType; -import electroblob.wizardry.constants.Tier; -import electroblob.wizardry.registry.WizardryEnchantments; -import electroblob.wizardry.registry.WizardryItems; -import electroblob.wizardry.registry.WizardrySounds; -import electroblob.wizardry.util.SpellModifiers; -import electroblob.wizardry.util.WizardryParticleType; -import electroblob.wizardry.util.WizardryUtilities; +import electroblob.wizardry.WizardryUtilities; import net.minecraft.enchantment.EnchantmentHelper; import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.inventory.ContainerPlayer; +import net.minecraft.inventory.Slot; 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.world.World; public class ImbueWeapon extends Spell { public ImbueWeapon() { - super(Tier.APPRENTICE, 20, Element.SORCERY, "imbue_weapon", SpellType.UTILITY, 50, EnumAction.BOW, false); + super(EnumTier.APPRENTICE, 20, EnumElement.SORCERY, "imbue_weapon", EnumSpellType.UTILITY, 50, EnumAction.bow, false); } @Override - public boolean cast(World world, EntityPlayer caster, EnumHand hand, int ticksInUse, SpellModifiers modifiers) { + public boolean cast(World world, EntityPlayer caster, int ticksInUse, float damageMultiplier, float rangeMultiplier, float durationMultiplier, float blastMultiplier) { // Won't work if the weapon already has the enchantment - if(WizardData.get(caster) != null){ + if(ExtendedPlayer.get(caster) != null && ExtendedPlayer.get(caster).magicWeaponDuration <= 0){ - for(ItemStack stack : WizardryUtilities.getPrioritisedHotbarAndOffhand(caster)){ + // Isolates just the hotbar + List hotbar = ((ContainerPlayer)caster.openContainer).inventorySlots.subList(36, 45); - if(stack != null){ + for(Object slot : hotbar){ - if(stack.getItem() instanceof ItemSword && !EnchantmentHelper.getEnchantments(stack).containsKey(WizardryEnchantments.magic_sword) - && WizardData.get(caster).getImbuementDuration(WizardryEnchantments.magic_sword) <= 0){ - // The enchantment level as determined by the damage multiplier. The + 0.5f is so that - // weird float processing doesn't incorrectly round it down. - stack.addEnchantment(WizardryEnchantments.magic_sword, modifiers.get(SpellModifiers.DAMAGE) == 1.0f ? 1 : (int)((modifiers.get(SpellModifiers.DAMAGE) - 1.0f)/Constants.DAMAGE_INCREASE_PER_TIER + 0.5f)); - WizardData.get(caster).setImbuementDuration(WizardryEnchantments.magic_sword, (int)(900*modifiers.get(WizardryItems.duration_upgrade))); + if(slot instanceof Slot){ - }else if(stack.getItem() instanceof ItemBow && !EnchantmentHelper.getEnchantments(stack).containsKey(WizardryEnchantments.magic_bow) - && WizardData.get(caster).getImbuementDuration(WizardryEnchantments.magic_bow) <= 0){ - // The enchantment level as determined by the damage multiplier. The + 0.5f is so that - // weird float processing doesn't incorrectly round it down. - stack.addEnchantment(WizardryEnchantments.magic_bow, modifiers.get(SpellModifiers.DAMAGE) == 1.0f ? 1 : (int)((modifiers.get(SpellModifiers.DAMAGE) - 1.0f)/Constants.DAMAGE_INCREASE_PER_TIER + 0.5f)); - WizardData.get(caster).setImbuementDuration(WizardryEnchantments.magic_bow, (int)(900*modifiers.get(WizardryItems.duration_upgrade))); + ItemStack stack = ((Slot)slot).getStack(); - }else{ - continue; - } + if(stack != null){ - if(world.isRemote){ - for(int i=0; i<10; i++){ - double x1 = (double)((float)caster.posX + world.rand.nextFloat()*2 - 1.0F); - double y1 = (double)((float)WizardryUtilities.getPlayerEyesPos(caster) - 0.5F + world.rand.nextFloat()); - double z1 = (double)((float)caster.posZ + world.rand.nextFloat()*2 - 1.0F); - Wizardry.proxy.spawnParticle(WizardryParticleType.SPARKLE, world, x1, y1, z1, 0, 0.1F, 0, 48 + world.rand.nextInt(12), 0.9f, 0.7f, 1.0f); + boolean flag = false; + + if(stack.getItem() instanceof ItemSword && !EnchantmentHelper.getEnchantments(stack).containsKey(Wizardry.magicSword.effectId)){ + // The enchantment level as determined by the damage multiplier. The + 0.5f is so that + // weird float processing doesn't incorrectly round it down. + stack.addEnchantment(Wizardry.magicSword, damageMultiplier == 1.0f ? 1 : (int)((damageMultiplier - 1.0f)/Wizardry.DAMAGE_INCREASE_PER_TIER + 0.5f)); + flag = true; + + }else if(stack.getItem() instanceof ItemBow && !EnchantmentHelper.getEnchantments(stack).containsKey(Wizardry.magicBow.effectId)){ + // The enchantment level as determined by the damage multiplier. The + 0.5f is so that + // weird float processing doesn't incorrectly round it down. + stack.addEnchantment(Wizardry.magicBow, damageMultiplier == 1.0f ? 1 : (int)((damageMultiplier - 1.0f)/Wizardry.DAMAGE_INCREASE_PER_TIER + 0.5f)); + flag = true; + } + + if(flag){ + + ExtendedPlayer.get(caster).magicWeaponDuration = (int)(900*durationMultiplier); + + if(world.isRemote){ + for(int i=0; i<10; i++){ + double x1 = (double)((float)caster.posX + world.rand.nextFloat()*2 - 1.0F); + double y1 = (double)((float)WizardryUtilities.getPlayerEyesPos(caster) - 0.5F + world.rand.nextFloat()); + double z1 = (double)((float)caster.posZ + world.rand.nextFloat()*2 - 1.0F); + Wizardry.proxy.spawnParticle(EnumParticleType.SPARKLE, world, x1, y1, z1, 0, 0.1F, 0, 48 + world.rand.nextInt(12), 0.9f, 0.7f, 1.0f); + } + } + + world.playSoundAtEntity(caster, "wizardry:aura", 1.0f, 1.0f); + return true; } } - - WizardryUtilities.playSoundAtPlayer(caster, WizardrySounds.SPELL_CONJURATION, 1.0f, 1.0f); - return true; } } } diff --git a/src/main/java/electroblob/wizardry/spell/Intimidate.java b/src/main/java/electroblob/wizardry/spell/Intimidate.java index 941a9193..3dbdbd1e 100644 --- a/src/main/java/electroblob/wizardry/spell/Intimidate.java +++ b/src/main/java/electroblob/wizardry/spell/Intimidate.java @@ -2,76 +2,70 @@ package electroblob.wizardry.spell; import java.util.List; +import electroblob.wizardry.EnumElement; +import electroblob.wizardry.EnumParticleType; +import electroblob.wizardry.EnumSpellType; +import electroblob.wizardry.EnumTier; import electroblob.wizardry.Wizardry; -import electroblob.wizardry.constants.Element; -import electroblob.wizardry.constants.SpellType; -import electroblob.wizardry.constants.Tier; -import electroblob.wizardry.registry.WizardryItems; -import electroblob.wizardry.registry.WizardryPotions; -import electroblob.wizardry.util.SpellModifiers; -import electroblob.wizardry.util.WizardryParticleType; -import electroblob.wizardry.util.WizardryUtilities; -import net.minecraft.entity.Entity; +import electroblob.wizardry.WizardryUtilities; import net.minecraft.entity.EntityCreature; import net.minecraft.entity.EntityLiving; import net.minecraft.entity.EntityLivingBase; +import net.minecraft.entity.SharedMonsterAttributes; import net.minecraft.entity.ai.RandomPositionGenerator; import net.minecraft.entity.player.EntityPlayer; -import net.minecraft.init.SoundEvents; import net.minecraft.item.EnumAction; import net.minecraft.nbt.NBTTagCompound; +import net.minecraft.pathfinding.PathEntity; import net.minecraft.pathfinding.PathPoint; import net.minecraft.potion.PotionEffect; -import net.minecraft.util.EnumHand; -import net.minecraft.util.math.Vec3d; +import net.minecraft.util.MovingObjectPosition; +import net.minecraft.util.Vec3; import net.minecraft.world.World; -import net.minecraftforge.event.entity.living.LivingEvent.LivingUpdateEvent; -import net.minecraftforge.fml.common.Mod; -import net.minecraftforge.fml.common.eventhandler.SubscribeEvent; -@Mod.EventBusSubscriber public class Intimidate extends Spell { - /** The NBT tag name for storing the feared entity's UUID in the target's tag compound. */ + /** The NBT tag name for storing the feared entity's UUID in the target's tag compound. Defined here in case + * it changes. */ public static final String NBT_KEY = "fearedEntity"; public Intimidate() { - super(Tier.APPRENTICE, 20, Element.NECROMANCY, "intimidate", SpellType.ATTACK, 100, EnumAction.BOW, false); + super(EnumTier.APPRENTICE, 20, EnumElement.NECROMANCY, "intimidate", EnumSpellType.ATTACK, 100, EnumAction.bow, false); } @Override - public boolean cast(World world, EntityPlayer caster, EnumHand hand, int ticksInUse, SpellModifiers modifiers) { + public boolean cast(World world, EntityPlayer caster, int ticksInUse, float damageMultiplier, float rangeMultiplier, float durationMultiplier, float blastMultiplier) { if(!world.isRemote){ - List entities = WizardryUtilities.getEntitiesWithinRadius(8*modifiers.get(WizardryItems.range_upgrade), caster.posX, caster.posY, caster.posZ, world, EntityCreature.class); + List entities = WizardryUtilities.getEntitiesWithinRadius(8*rangeMultiplier, caster.posX, caster.posY, caster.posZ, world, EntityCreature.class); for(EntityCreature target : entities){ runAway(target, caster); NBTTagCompound entityNBT = target.getEntityData(); - if(entityNBT != null) entityNBT.setUniqueId(NBT_KEY, caster.getUniqueID()); + if(entityNBT != null) entityNBT.setString(NBT_KEY, caster.getUniqueID().toString()); - ((EntityLiving)target).addPotionEffect(new PotionEffect(WizardryPotions.fear, (int)(600*modifiers.get(WizardryItems.duration_upgrade)), 0)); + ((EntityLiving)target).addPotionEffect(new PotionEffect(Wizardry.fear.id, (int)(600*durationMultiplier), 0)); } }else{ for(int i=0; i<30; i++){ - Wizardry.proxy.spawnParticle(WizardryParticleType.DARK_MAGIC, world, caster.posX - 1 + world.rand.nextDouble()*2, - caster.getEntityBoundingBox().minY + 1.5 + world.rand.nextDouble()*0.5, + Wizardry.proxy.spawnParticle(EnumParticleType.DARK_MAGIC, world, caster.posX - 1 + world.rand.nextDouble()*2, + caster.boundingBox.minY + 1.5 + world.rand.nextDouble()*0.5, caster.posZ - 1 + world.rand.nextDouble()*2, 0, 0, 0, 0, 0.9f, 0.1f, 0.0f); } } - WizardryUtilities.playSoundAtPlayer(caster, SoundEvents.ENTITY_ENDERDRAGON_GROWL, 1.0f, 1.0f); + world.playSoundAtEntity(caster, "mob.enderdragon.growl", 1.0f, 1.0f); return true; } /** - * Finds a random position away from the caster and sets the given target's AI path to that location. Defined here - * so it can be used both in the spell itself and in the potion effect (event handler). + * Finds a random position away from the caster and sets the given target's AI path to that location. Handles both + * new AI and old AI. Defined here so it can be used both in the spell itself and in the potion effect (event handler). * @param target The entity running away * @param caster The entity that is being run away from * @return True if a new path was found and set, false if not. @@ -80,16 +74,21 @@ public class Intimidate extends Spell { if(target.getDistanceToEntity(caster) < 16){ - Vec3d Vec3d = RandomPositionGenerator.findRandomTargetBlockAwayFrom(target, 16, 7, new Vec3d(caster.posX, caster.posY, caster.posZ)); + Vec3 vec3 = RandomPositionGenerator.findRandomTargetBlockAwayFrom(target, 16, 7, Vec3.createVectorHelper(caster.posX, caster.posY, caster.posZ)); - if (Vec3d == null){ + if (vec3 == null){ return false; }else{ // In both cases it is necessary to check if the entity already has a path so it doesn't change direction // every tick, unless that path is towards the caster. - //Path path = target.getNavigator().getPathToXYZ(Vec3d.xCoord, Vec3d.yCoord, Vec3d.zCoord); + PathEntity path = target.getNavigator().getPathToXYZ(vec3.xCoord, vec3.yCoord, vec3.zCoord); + // Old AI + if(!target.hasPath() || target.getEntityToAttack() == caster) target.setPathToEntity(path); + target.setTarget(null); + + // New AI boolean flag = true; if(!target.getNavigator().noPath()){ @@ -99,30 +98,11 @@ public class Intimidate extends Spell { // Has a built in mind trick effect because for whatever reason this makes it work with skeletons. target.setAttackTarget(null); - if(flag) return target.getNavigator().tryMoveToXYZ(Vec3d.xCoord, Vec3d.yCoord, Vec3d.zCoord, 1.25);//target.getEntityAttribute(SharedMonsterAttributes.movementSpeed).getAttributeValue()); + if(flag) return target.getNavigator().tryMoveToXYZ(vec3.xCoord, vec3.yCoord, vec3.zCoord, 1.25);//target.getEntityAttribute(SharedMonsterAttributes.movementSpeed).getAttributeValue()); } } return false; } - - @SubscribeEvent - public static void onLivingUpdateEvent(LivingUpdateEvent event){ - - if(event.getEntityLiving().isPotionActive(WizardryPotions.fear) && event.getEntityLiving() instanceof EntityCreature){ - - NBTTagCompound entityNBT = event.getEntityLiving().getEntityData(); - EntityCreature creature = (EntityCreature)event.getEntityLiving(); - - if(entityNBT != null && entityNBT.hasKey(NBT_KEY)){ - - Entity caster = WizardryUtilities.getEntityByUUID(creature.worldObj, entityNBT.getUniqueId(NBT_KEY)); - - if(caster instanceof EntityLivingBase){ - Intimidate.runAway(creature, (EntityLivingBase)caster); - } - } - } - } } diff --git a/src/main/java/electroblob/wizardry/spell/InvigoratingPresence.java b/src/main/java/electroblob/wizardry/spell/InvigoratingPresence.java index b4cbc77d..920dc41b 100644 --- a/src/main/java/electroblob/wizardry/spell/InvigoratingPresence.java +++ b/src/main/java/electroblob/wizardry/spell/InvigoratingPresence.java @@ -2,51 +2,48 @@ package electroblob.wizardry.spell; import java.util.List; +import electroblob.wizardry.EnumElement; +import electroblob.wizardry.EnumParticleType; +import electroblob.wizardry.EnumSpellType; +import electroblob.wizardry.EnumTier; import electroblob.wizardry.Wizardry; -import electroblob.wizardry.constants.Element; -import electroblob.wizardry.constants.SpellType; -import electroblob.wizardry.constants.Tier; -import electroblob.wizardry.registry.WizardryItems; -import electroblob.wizardry.registry.WizardrySounds; -import electroblob.wizardry.util.SpellModifiers; -import electroblob.wizardry.util.WizardryParticleType; -import electroblob.wizardry.util.WizardryUtilities; +import electroblob.wizardry.WizardryUtilities; +import net.minecraft.entity.EntityLivingBase; import net.minecraft.entity.player.EntityPlayer; -import net.minecraft.init.MobEffects; import net.minecraft.item.EnumAction; +import net.minecraft.potion.Potion; import net.minecraft.potion.PotionEffect; -import net.minecraft.util.EnumHand; import net.minecraft.world.World; public class InvigoratingPresence extends Spell { public InvigoratingPresence() { - super(Tier.APPRENTICE, 30, Element.HEALING, "invigorating_presence", SpellType.UTILITY, 60, EnumAction.BOW, false); + super(EnumTier.APPRENTICE, 30, EnumElement.HEALING, "invigorating_presence", EnumSpellType.UTILITY, 60, EnumAction.bow, false); } @Override - public boolean cast(World world, EntityPlayer caster, EnumHand hand, int ticksInUse, SpellModifiers modifiers) { + public boolean cast(World world, EntityPlayer caster, int ticksInUse, float damageMultiplier, float rangeMultiplier, float durationMultiplier, float blastMultiplier) { - List targets = WizardryUtilities.getEntitiesWithinRadius(5*modifiers.get(WizardryItems.blast_upgrade), caster.posX, caster.posY, caster.posZ, world, EntityPlayer.class); + List targets = WizardryUtilities.getEntitiesWithinRadius(5*blastMultiplier, caster.posX, caster.posY, caster.posZ, world, EntityPlayer.class); for(EntityPlayer target : targets){ if(WizardryUtilities.isPlayerAlly(caster, target) || target == caster){ // Strength 2 for 45 seconds. - target.addPotionEffect(new PotionEffect(MobEffects.STRENGTH, (int)(900*modifiers.get(WizardryItems.duration_upgrade)), 1, false, false)); + target.addPotionEffect(new PotionEffect(Potion.damageBoost.id, (int)(900*durationMultiplier), 1, true)); } } if(world.isRemote){ - for(int i=0;i<50*modifiers.get(WizardryItems.blast_upgrade);i++){ - double radius = (1 + world.rand.nextDouble()*4)*modifiers.get(WizardryItems.blast_upgrade); + for(int i=0;i<50*blastMultiplier;i++){ + double radius = (1 + world.rand.nextDouble()*4)*blastMultiplier; double angle = world.rand.nextDouble()*Math.PI*2; - Wizardry.proxy.spawnParticle(WizardryParticleType.SPARKLE, world, caster.posX + radius*Math.cos(angle), caster.getEntityBoundingBox().minY, caster.posZ + radius*Math.sin(angle), + Wizardry.proxy.spawnParticle(EnumParticleType.SPARKLE, world, caster.posX + radius*Math.cos(angle), WizardryUtilities.getEntityFeetPos(caster), caster.posZ + radius*Math.sin(angle), 0, 0.03, 0, 50, 1, 0.2f, 0.2f); } } - WizardryUtilities.playSoundAtPlayer(caster, WizardrySounds.SPELL_CONJURATION, 1.0F, world.rand.nextFloat() * 0.2F + 1.0F); + world.playSoundAtEntity(caster, "wizardry:aura", 1.0F, world.rand.nextFloat() * 0.2F + 1.0F); return true; } diff --git a/src/main/java/electroblob/wizardry/spell/Invisibility.java b/src/main/java/electroblob/wizardry/spell/Invisibility.java index a26836d2..7d9cf512 100644 --- a/src/main/java/electroblob/wizardry/spell/Invisibility.java +++ b/src/main/java/electroblob/wizardry/spell/Invisibility.java @@ -1,62 +1,58 @@ package electroblob.wizardry.spell; +import electroblob.wizardry.EnumElement; +import electroblob.wizardry.EnumParticleType; +import electroblob.wizardry.EnumSpellType; +import electroblob.wizardry.EnumTier; import electroblob.wizardry.Wizardry; -import electroblob.wizardry.constants.Element; -import electroblob.wizardry.constants.SpellType; -import electroblob.wizardry.constants.Tier; -import electroblob.wizardry.registry.WizardryItems; -import electroblob.wizardry.registry.WizardrySounds; -import electroblob.wizardry.util.SpellModifiers; -import electroblob.wizardry.util.WizardryParticleType; -import electroblob.wizardry.util.WizardryUtilities; +import electroblob.wizardry.WizardryUtilities; import net.minecraft.entity.EntityLiving; import net.minecraft.entity.EntityLivingBase; import net.minecraft.entity.player.EntityPlayer; -import net.minecraft.init.MobEffects; import net.minecraft.item.EnumAction; +import net.minecraft.potion.Potion; import net.minecraft.potion.PotionEffect; -import net.minecraft.util.EnumHand; import net.minecraft.world.World; public class Invisibility extends Spell { public Invisibility() { - super(Tier.ADVANCED, 35, Element.SORCERY, "invisibility", SpellType.UTILITY, 200, EnumAction.BOW, false); + super(EnumTier.ADVANCED, 35, EnumElement.SORCERY, "invisibility", EnumSpellType.UTILITY, 200, EnumAction.bow, false); } @Override - public boolean cast(World world, EntityPlayer caster, EnumHand hand, int ticksInUse, SpellModifiers modifiers) { + public boolean cast(World world, EntityPlayer caster, int ticksInUse, float damageMultiplier, float rangeMultiplier, float durationMultiplier, float blastMultiplier) { - caster.addPotionEffect(new PotionEffect(MobEffects.INVISIBILITY, (int)(600*modifiers.get(WizardryItems.duration_upgrade)), 0, false, false)); + caster.addPotionEffect(new PotionEffect(Potion.invisibility.id, (int)(600*durationMultiplier), 0, true)); if(world.isRemote){ for(int i=0; i<10; i++){ double x1 = (double)((float)caster.posX + world.rand.nextFloat()*2 - 1.0F); double y1 = (double)((float)WizardryUtilities.getPlayerEyesPos(caster) - 0.5F + world.rand.nextFloat()); double z1 = (double)((float)caster.posZ + world.rand.nextFloat()*2 - 1.0F); - Wizardry.proxy.spawnParticle(WizardryParticleType.SPARKLE, world, x1, y1, z1, 0, 0.1F, 0, 48 + world.rand.nextInt(12), 0.7f, 1.0f, 1.0f); + Wizardry.proxy.spawnParticle(EnumParticleType.SPARKLE, world, x1, y1, z1, 0, 0.1F, 0, 48 + world.rand.nextInt(12), 0.7f, 1.0f, 1.0f); } } - WizardryUtilities.playSoundAtPlayer(caster, WizardrySounds.SPELL_HEAL, 0.7F, world.rand.nextFloat() * 0.4F + 1.0F); + world.playSoundAtEntity(caster, "wizardry:heal", 0.7F, world.rand.nextFloat() * 0.4F + 1.0F); return true; } @Override - public boolean cast(World world, EntityLiving caster, EnumHand hand, int ticksInUse, EntityLivingBase target, SpellModifiers modifiers){ + public boolean cast(World world, EntityLiving caster, EntityLivingBase target, float damageMultiplier, float rangeMultiplier, float durationMultiplier, float blastMultiplier){ - if(!caster.isPotionActive(MobEffects.INVISIBILITY)){ + if(!caster.isPotionActive(Potion.invisibility)){ - caster.addPotionEffect(new PotionEffect(MobEffects.INVISIBILITY, (int)(600*modifiers.get(WizardryItems.duration_upgrade)), 0, false, false)); + caster.addPotionEffect(new PotionEffect(Potion.invisibility.id, (int)(600*durationMultiplier), 0, true)); if(world.isRemote){ for(int i=0; i<10; i++){ double x1 = (double)((float)caster.posX + world.rand.nextFloat()*2 - 1.0F); double y1 = (double)((float)caster.posY + caster.getEyeHeight() - 0.5F + world.rand.nextFloat()); double z1 = (double)((float)caster.posZ + world.rand.nextFloat()*2 - 1.0F); - Wizardry.proxy.spawnParticle(WizardryParticleType.SPARKLE, world, x1, y1, z1, 0, 0.1F, 0, 48 + world.rand.nextInt(12), 0.7f, 1.0f, 1.0f); + Wizardry.proxy.spawnParticle(EnumParticleType.SPARKLE, world, x1, y1, z1, 0, 0.1F, 0, 48 + world.rand.nextInt(12), 0.7f, 1.0f, 1.0f); } } - caster.playSound(WizardrySounds.SPELL_HEAL, 0.7F, world.rand.nextFloat() * 0.4F + 1.0F); + world.playSoundAtEntity(caster, "wizardry:heal", 0.7F, world.rand.nextFloat() * 0.4F + 1.0F); return true; } diff --git a/src/main/java/electroblob/wizardry/spell/InvokeWeather.java b/src/main/java/electroblob/wizardry/spell/InvokeWeather.java index 98fd97c6..a03e9016 100644 --- a/src/main/java/electroblob/wizardry/spell/InvokeWeather.java +++ b/src/main/java/electroblob/wizardry/spell/InvokeWeather.java @@ -2,62 +2,47 @@ package electroblob.wizardry.spell; import java.util.Random; +import electroblob.wizardry.EnumElement; +import electroblob.wizardry.EnumParticleType; +import electroblob.wizardry.EnumSpellType; +import electroblob.wizardry.EnumTier; import electroblob.wizardry.Wizardry; -import electroblob.wizardry.constants.Element; -import electroblob.wizardry.constants.SpellType; -import electroblob.wizardry.constants.Tier; -import electroblob.wizardry.util.SpellModifiers; -import electroblob.wizardry.util.WizardryParticleType; -import electroblob.wizardry.util.WizardryUtilities; +import electroblob.wizardry.WizardryUtilities; import net.minecraft.entity.player.EntityPlayer; -import net.minecraft.init.SoundEvents; import net.minecraft.item.EnumAction; -import net.minecraft.util.EnumHand; -import net.minecraft.util.text.TextComponentTranslation; +import net.minecraft.util.ChatComponentTranslation; import net.minecraft.world.World; public class InvokeWeather extends Spell { public InvokeWeather() { - super(Tier.ADVANCED, 30, Element.LIGHTNING, "invoke_weather", SpellType.UTILITY, 100, EnumAction.BOW, false); + super(EnumTier.ADVANCED, 30, EnumElement.LIGHTNING, "invoke_weather", EnumSpellType.UTILITY, 100, EnumAction.bow, false); } @Override - public boolean cast(World world, EntityPlayer caster, EnumHand hand, int ticksInUse, SpellModifiers modifiers) { + public boolean cast(World world, EntityPlayer caster, int ticksInUse, float damageMultiplier, float rangeMultiplier, float durationMultiplier, float blastMultiplier) { if(caster.dimension == 0){ - if(!world.isRemote){ - // TODO: Backport these changes. - int standardWeatherTime = (300 + (new Random()).nextInt(600)) * 20; if(world.isRaining()){ - caster.addChatComponentMessage(new TextComponentTranslation("spell.invoke_weather.sun")); - world.getWorldInfo().setCleanWeatherTime(standardWeatherTime); + caster.addChatComponentMessage(new ChatComponentTranslation("spell.invoke_weather.sun")); world.getWorldInfo().setRainTime(0); - world.getWorldInfo().setThunderTime(0); world.getWorldInfo().setRaining(false); - world.getWorldInfo().setThundering(false); }else{ - caster.addChatComponentMessage(new TextComponentTranslation("spell.invoke_weather.rain")); - world.getWorldInfo().setCleanWeatherTime(0); - world.getWorldInfo().setRainTime(standardWeatherTime); - world.getWorldInfo().setThunderTime(standardWeatherTime); + caster.addChatComponentMessage(new ChatComponentTranslation("spell.invoke_weather.rain")); + world.getWorldInfo().setRainTime((300 + (new Random()).nextInt(600)) * 20); world.getWorldInfo().setRaining(true); - // 1/3 chance for a thunderstorm - world.getWorldInfo().setThundering(world.rand.nextInt(3) == 0); } } - if(world.isRemote){ for(int i=0; i<10; i++){ double x1 = (double)((float)caster.posX + world.rand.nextFloat()*2 - 1.0F); double y1 = (double)((float)WizardryUtilities.getPlayerEyesPos(caster) - 0.5F + world.rand.nextFloat()); double z1 = (double)((float)caster.posZ + world.rand.nextFloat()*2 - 1.0F); - Wizardry.proxy.spawnParticle(WizardryParticleType.SPARKLE, world, x1, y1, z1, 0, 0.1F, 0, 48 + world.rand.nextInt(12), 0.5f, 0.7f, 1.0f); + Wizardry.proxy.spawnParticle(EnumParticleType.SPARKLE, world, x1, y1, z1, 0, 0.1F, 0, 48 + world.rand.nextInt(12), 0.5f, 0.7f, 1.0f); } } - - WizardryUtilities.playSoundAtPlayer(caster, SoundEvents.ENTITY_LIGHTNING_THUNDER, 0.5F, 1.0f); + world.playSoundAtEntity(caster, "ambient.weather.thunder", 0.5F, 1.0f); return true; } return false; diff --git a/src/main/java/electroblob/wizardry/spell/Ironflesh.java b/src/main/java/electroblob/wizardry/spell/Ironflesh.java index 0997439b..e1e316c3 100644 --- a/src/main/java/electroblob/wizardry/spell/Ironflesh.java +++ b/src/main/java/electroblob/wizardry/spell/Ironflesh.java @@ -1,64 +1,60 @@ package electroblob.wizardry.spell; +import electroblob.wizardry.EnumElement; +import electroblob.wizardry.EnumParticleType; +import electroblob.wizardry.EnumSpellType; +import electroblob.wizardry.EnumTier; import electroblob.wizardry.Wizardry; -import electroblob.wizardry.constants.Element; -import electroblob.wizardry.constants.SpellType; -import electroblob.wizardry.constants.Tier; -import electroblob.wizardry.registry.WizardryItems; -import electroblob.wizardry.registry.WizardrySounds; -import electroblob.wizardry.util.SpellModifiers; -import electroblob.wizardry.util.WizardryParticleType; -import electroblob.wizardry.util.WizardryUtilities; +import electroblob.wizardry.WizardryUtilities; import net.minecraft.entity.EntityLiving; import net.minecraft.entity.EntityLivingBase; import net.minecraft.entity.player.EntityPlayer; -import net.minecraft.init.MobEffects; import net.minecraft.item.EnumAction; +import net.minecraft.potion.Potion; import net.minecraft.potion.PotionEffect; -import net.minecraft.util.EnumHand; import net.minecraft.world.World; public class Ironflesh extends Spell { public Ironflesh() { - super(Tier.ADVANCED, 30, Element.HEALING, "ironflesh", SpellType.DEFENCE, 100, EnumAction.BOW, false); + super(EnumTier.ADVANCED, 30, EnumElement.HEALING, "ironflesh", EnumSpellType.DEFENCE, 100, EnumAction.bow, false); } @Override - public boolean cast(World world, EntityPlayer caster, EnumHand hand, int ticksInUse, SpellModifiers modifiers) { + public boolean cast(World world, EntityPlayer caster, int ticksInUse, float damageMultiplier, float rangeMultiplier, float durationMultiplier, float blastMultiplier) { - caster.addPotionEffect(new PotionEffect(MobEffects.RESISTANCE, (int)(600*modifiers.get(WizardryItems.duration_upgrade)), 2, false, false)); + caster.addPotionEffect(new PotionEffect(Potion.resistance.id, (int)(600*durationMultiplier), 2, true)); if(world.isRemote){ for(int i=0; i<10; i++){ double x1 = (double)((float)caster.posX + world.rand.nextFloat()*2 - 1.0F); double y1 = (double)((float)WizardryUtilities.getPlayerEyesPos(caster) - 0.5F + world.rand.nextFloat()); double z1 = (double)((float)caster.posZ + world.rand.nextFloat()*2 - 1.0F); - Wizardry.proxy.spawnParticle(WizardryParticleType.SPARKLE, world, x1, y1, z1, 0, 0.1F, 0, 48 + world.rand.nextInt(12), 0.4f, 0.5f, 0.6f); + Wizardry.proxy.spawnParticle(EnumParticleType.SPARKLE, world, x1, y1, z1, 0, 0.1F, 0, 48 + world.rand.nextInt(12), 0.4f, 0.5f, 0.6f); } } - WizardryUtilities.playSoundAtPlayer(caster, WizardrySounds.SPELL_HEAL, 0.7F, world.rand.nextFloat() * 0.4F + 1.0F); + world.playSoundAtEntity(caster, "wizardry:heal", 0.7F, world.rand.nextFloat() * 0.4F + 1.0F); return true; } @Override - public boolean cast(World world, EntityLiving caster, EnumHand hand, int ticksInUse, EntityLivingBase target, SpellModifiers modifiers){ + public boolean cast(World world, EntityLiving caster, EntityLivingBase target, float damageMultiplier, float rangeMultiplier, float durationMultiplier, float blastMultiplier){ - if(!caster.isPotionActive(MobEffects.RESISTANCE)){ + if(!caster.isPotionActive(Potion.resistance)){ - caster.addPotionEffect(new PotionEffect(MobEffects.RESISTANCE, (int)(600*modifiers.get(WizardryItems.duration_upgrade)), 2, false, false)); + caster.addPotionEffect(new PotionEffect(Potion.resistance.id, (int)(600*durationMultiplier), 2, true)); if(world.isRemote){ for(int i=0; i<10; i++){ double x1 = (double)((float)caster.posX + world.rand.nextFloat()*2 - 1.0F); double y1 = (double)((float)caster.posY + caster.getEyeHeight() - 0.5F + world.rand.nextFloat()); double z1 = (double)((float)caster.posZ + world.rand.nextFloat()*2 - 1.0F); - Wizardry.proxy.spawnParticle(WizardryParticleType.SPARKLE, world, x1, y1, z1, 0, 0.1F, 0, 48 + world.rand.nextInt(12), 0.4f, 0.5f, 0.6f); + Wizardry.proxy.spawnParticle(EnumParticleType.SPARKLE, world, x1, y1, z1, 0, 0.1F, 0, 48 + world.rand.nextInt(12), 0.4f, 0.5f, 0.6f); } } - caster.playSound(WizardrySounds.SPELL_HEAL, 0.7F, world.rand.nextFloat() * 0.4F + 1.0F); + world.playSoundAtEntity(caster, "wizardry:heal", 0.7F, world.rand.nextFloat() * 0.4F + 1.0F); return true; } diff --git a/src/main/java/electroblob/wizardry/spell/Leap.java b/src/main/java/electroblob/wizardry/spell/Leap.java index 49c2265c..130a6278 100644 --- a/src/main/java/electroblob/wizardry/spell/Leap.java +++ b/src/main/java/electroblob/wizardry/spell/Leap.java @@ -1,42 +1,40 @@ package electroblob.wizardry.spell; -import electroblob.wizardry.constants.Element; -import electroblob.wizardry.constants.SpellType; -import electroblob.wizardry.constants.Tier; -import electroblob.wizardry.util.SpellModifiers; -import electroblob.wizardry.util.WizardryUtilities; +import electroblob.wizardry.EnumElement; +import electroblob.wizardry.EnumParticleType; +import electroblob.wizardry.EnumSpellType; +import electroblob.wizardry.EnumTier; +import electroblob.wizardry.Wizardry; +import electroblob.wizardry.WizardryUtilities; import net.minecraft.entity.player.EntityPlayer; -import net.minecraft.init.SoundEvents; import net.minecraft.item.EnumAction; -import net.minecraft.util.EnumHand; -import net.minecraft.util.EnumParticleTypes; import net.minecraft.world.World; public class Leap extends Spell { public Leap() { - super(Tier.BASIC, 10, Element.EARTH, "leap", SpellType.UTILITY, 20, EnumAction.NONE, false); + super(EnumTier.BASIC, 10, EnumElement.EARTH, "leap", EnumSpellType.UTILITY, 20, EnumAction.none, false); } @Override - public boolean cast(World world, EntityPlayer caster, EnumHand hand, int ticksInUse, SpellModifiers modifiers) { + public boolean cast(World world, EntityPlayer caster, int ticksInUse, float damageMultiplier, float rangeMultiplier, float durationMultiplier, float blastMultiplier) { if(caster.onGround){ - caster.motionY = 0.65 * modifiers.get(SpellModifiers.DAMAGE); + caster.motionY = 0.65 * damageMultiplier; caster.addVelocity(caster.getLookVec().xCoord*0.3, 0, caster.getLookVec().zCoord*0.3); if(world.isRemote){ for(int i=0; i<10; i++){ double x = (double)(caster.posX + world.rand.nextFloat() - 0.5F); - double y = (double)(caster.getEntityBoundingBox().minY); + double y = (double)(caster.boundingBox.minY); double z = (double)(caster.posZ + world.rand.nextFloat() - 0.5F); - world.spawnParticle(EnumParticleTypes.CLOUD, x, y, z, 0, 0, 0); + world.spawnParticle("cloud", x, y, z, 0, 0, 0); } } - WizardryUtilities.playSoundAtPlayer(caster, SoundEvents.ENTITY_ENDERDRAGON_FLAP, 0.5F, 1.0f); - caster.swingArm(hand); + world.playSoundAtEntity(caster, "mob.enderdragon.wings", 0.5F, 1.0f); + caster.swingItem(); return true; } diff --git a/src/main/java/electroblob/wizardry/spell/Levitation.java b/src/main/java/electroblob/wizardry/spell/Levitation.java index cc67b263..2e9d3b26 100644 --- a/src/main/java/electroblob/wizardry/spell/Levitation.java +++ b/src/main/java/electroblob/wizardry/spell/Levitation.java @@ -1,36 +1,33 @@ package electroblob.wizardry.spell; +import electroblob.wizardry.EnumElement; +import electroblob.wizardry.EnumParticleType; +import electroblob.wizardry.EnumSpellType; +import electroblob.wizardry.EnumTier; import electroblob.wizardry.Wizardry; -import electroblob.wizardry.constants.Element; -import electroblob.wizardry.constants.SpellType; -import electroblob.wizardry.constants.Tier; -import electroblob.wizardry.registry.WizardrySounds; -import electroblob.wizardry.util.SpellModifiers; -import electroblob.wizardry.util.WizardryParticleType; -import electroblob.wizardry.util.WizardryUtilities; +import electroblob.wizardry.WizardryUtilities; import net.minecraft.entity.player.EntityPlayer; import net.minecraft.item.EnumAction; -import net.minecraft.util.EnumHand; import net.minecraft.world.World; public class Levitation extends Spell { - public Levitation(){ - super(Tier.ADVANCED, 10, Element.SORCERY, "levitation", SpellType.UTILITY, 0, EnumAction.BOW, true); + public Levitation() { + super(EnumTier.ADVANCED, 10, EnumElement.SORCERY, "levitation", EnumSpellType.UTILITY, 0, EnumAction.bow, true); } @Override - public boolean cast(World world, EntityPlayer caster, EnumHand hand, int ticksInUse, SpellModifiers modifiers) { + public boolean cast(World world, EntityPlayer caster, int ticksInUse, float damageMultiplier, float rangeMultiplier, float durationMultiplier, float blastMultiplier) { caster.fallDistance = 0; caster.motionY = caster.motionY < 0.5d ? caster.motionY + 0.1d : caster.motionY; if(world.isRemote){ - Wizardry.proxy.spawnParticle(WizardryParticleType.SPARKLE, world, caster.posX - 0.25d + world.rand.nextDouble()/2, WizardryUtilities.getPlayerEyesPos(caster) - 1.5f, caster.posZ - 0.25d + world.rand.nextDouble()/2, 0, -0.1F, 0, 15, 0.5f, 1.0f, 0.7f); + Wizardry.proxy.spawnParticle(EnumParticleType.SPARKLE, world, caster.posX - 0.25d + world.rand.nextDouble()/2, WizardryUtilities.getPlayerEyesPos(caster) - 1.5f, caster.posZ - 0.25d + world.rand.nextDouble()/2, 0, -0.1F, 0, 15, 0.5f, 1.0f, 0.7f); } if(ticksInUse % 24 == 0 && world.isRemote){ - Wizardry.proxy.playMovingSound(caster, WizardrySounds.SPELL_LOOP_SPARKLE, 0.5F, 1.0f, false); + Wizardry.proxy.playMovingSound(caster, "wizardry:sparkle", 0.5F, 1.0f, false); } return true; } diff --git a/src/main/java/electroblob/wizardry/spell/LifeDrain.java b/src/main/java/electroblob/wizardry/spell/LifeDrain.java index cd2368b6..9327923a 100644 --- a/src/main/java/electroblob/wizardry/spell/LifeDrain.java +++ b/src/main/java/electroblob/wizardry/spell/LifeDrain.java @@ -1,102 +1,72 @@ package electroblob.wizardry.spell; +import electroblob.wizardry.EnumElement; +import electroblob.wizardry.EnumParticleType; +import electroblob.wizardry.EnumSpellType; +import electroblob.wizardry.EnumTier; +import electroblob.wizardry.MagicDamage; import electroblob.wizardry.Wizardry; -import electroblob.wizardry.constants.Element; -import electroblob.wizardry.constants.SpellType; -import electroblob.wizardry.constants.Tier; -import electroblob.wizardry.registry.WizardryItems; -import electroblob.wizardry.registry.WizardrySounds; -import electroblob.wizardry.util.MagicDamage; -import electroblob.wizardry.util.MagicDamage.DamageType; -import electroblob.wizardry.util.SpellModifiers; -import electroblob.wizardry.util.WizardryParticleType; -import electroblob.wizardry.util.WizardryUtilities; -import net.minecraft.entity.EntityLiving; +import electroblob.wizardry.WizardryUtilities; +import electroblob.wizardry.MagicDamage.DamageType; import net.minecraft.entity.EntityLivingBase; import net.minecraft.entity.player.EntityPlayer; import net.minecraft.item.EnumAction; -import net.minecraft.util.EnumHand; -import net.minecraft.util.math.RayTraceResult; -import net.minecraft.util.math.Vec3d; +import net.minecraft.util.MovingObjectPosition; +import net.minecraft.util.MovingObjectPosition.MovingObjectType; +import net.minecraft.util.Vec3; import net.minecraft.world.World; public class LifeDrain extends Spell { public LifeDrain() { - super(Tier.APPRENTICE, 10, Element.NECROMANCY, "life_drain", SpellType.ATTACK, 0, EnumAction.NONE, true); + super(EnumTier.APPRENTICE, 10, EnumElement.NECROMANCY, "life_drain", EnumSpellType.ATTACK, 0, EnumAction.none, true); } @Override - public boolean cast(World world, EntityPlayer caster, EnumHand hand, int ticksInUse, SpellModifiers modifiers) { + public boolean cast(World world, EntityPlayer caster, int ticksInUse, float damageMultiplier, float rangeMultiplier, float durationMultiplier, float blastMultiplier) { - Vec3d look = caster.getLookVec(); + Vec3 look = caster.getLookVec(); - RayTraceResult rayTrace = WizardryUtilities.standardEntityRayTrace(world, caster, 10*modifiers.get(WizardryItems.range_upgrade)); + MovingObjectPosition rayTrace = WizardryUtilities.standardEntityRayTrace(world, caster, 10*rangeMultiplier); - if(rayTrace != null && rayTrace.typeOfHit == RayTraceResult.Type.ENTITY && rayTrace.entityHit instanceof EntityLivingBase){ + if(rayTrace != null && rayTrace.typeOfHit == MovingObjectType.ENTITY && rayTrace.entityHit instanceof EntityLivingBase){ EntityLivingBase target = (EntityLivingBase) rayTrace.entityHit; if(ticksInUse % 12 == 0){ - WizardryUtilities.attackEntityWithoutKnockback(target, MagicDamage.causeDirectMagicDamage(caster, DamageType.MAGIC), 2.0f * modifiers.get(SpellModifiers.DAMAGE)); + // This motion stuff removes knockback, which is desirable for continuous spells. + double motionX = target.motionX; + double motionY = target.motionY; + double motionZ = target.motionZ; + + target.attackEntityFrom(MagicDamage.causeDirectMagicDamage(caster, DamageType.MAGIC), 2.0f * damageMultiplier); + + target.motionX = motionX; + target.motionY = motionY; + target.motionZ = motionZ; + caster.heal(1); } } if(world.isRemote){ - for(int i=5; i<(int)(25*modifiers.get(WizardryItems.range_upgrade)); i+=2){ + for(int i=5; i<(int)(25*rangeMultiplier); i+=2){ // I figured it out! when on client side, entityplayer.posY is at the eyes, not the feet! double x1 = caster.posX + look.xCoord*i/2 + world.rand.nextFloat()/5 - 0.1f; double y1 = WizardryUtilities.getPlayerEyesPos(caster) - 0.4f + look.yCoord*i/2 + world.rand.nextFloat()/5 - 0.1f; double z1 = caster.posZ + look.zCoord*i/2 + world.rand.nextFloat()/5 - 0.1f; //world.spawnParticle("mobSpell", x1, y1, z1, -1*look.xCoord, -1*look.yCoord, -1*look.zCoord); if(i % 5 == 0){ - Wizardry.proxy.spawnParticle(WizardryParticleType.DARK_MAGIC, world, x1, y1, z1, 0.0d, 0.0d, 0.0d, 0, 0.1f, 0.0f, 0.0f); + Wizardry.proxy.spawnParticle(EnumParticleType.DARK_MAGIC, world, x1, y1, z1, 0.0d, 0.0d, 0.0d, 0, 0.1f, 0.0f, 0.0f); } - Wizardry.proxy.spawnParticle(WizardryParticleType.SPARKLE, world, x1, y1, z1, -0.05*look.xCoord*i, -0.05*look.yCoord*i, -0.05*look.zCoord*i, 8 + world.rand.nextInt(6), 0.5f, 0.0f, 0.0f); + Wizardry.proxy.spawnParticle(EnumParticleType.SPARKLE, world, x1, y1, z1, -0.05*look.xCoord*i, -0.05*look.yCoord*i, -0.05*look.zCoord*i, 8 + world.rand.nextInt(6), 0.5f, 0.0f, 0.0f); } } if(ticksInUse % 18 == 0){ - if(ticksInUse == 0) WizardryUtilities.playSoundAtPlayer(caster, WizardrySounds.SPELL_SUMMONING, 1.0F, 0.6f); - WizardryUtilities.playSoundAtPlayer(caster, WizardrySounds.SPELL_LOOP_CRACKLE, 2.0F, 1.0f); + if(ticksInUse == 0) world.playSoundAtEntity(caster, "wizardry:darkaura", 1.0F, 0.6f); + world.playSoundAtEntity(caster, "wizardry:crackle", 2.0F, 1.0f); } return true; } - @Override - public boolean cast(World world, EntityLiving caster, EnumHand hand, int ticksInUse, EntityLivingBase target, SpellModifiers modifiers) { - - Vec3d vec = new Vec3d(target.posX - caster.posX, target.posY - caster.posY, target.posZ - caster.posZ).normalize(); - - if(target != null){ - if(ticksInUse % 12 == 0){ - WizardryUtilities.attackEntityWithoutKnockback(target, MagicDamage.causeDirectMagicDamage(caster, DamageType.MAGIC), 2.0f * modifiers.get(SpellModifiers.DAMAGE)); - caster.heal(1); - } - } - if(world.isRemote){ - for(int i=5; i<(int)(25*modifiers.get(WizardryItems.range_upgrade)); i+=2){ - // I figured it out! when on client side, entityplayer.posY is at the eyes, not the feet! - double x1 = caster.posX + vec.xCoord*i/2 + world.rand.nextFloat()/5 - 0.1f; - double y1 = caster.posY + caster.getEyeHeight() - 0.4f + vec.yCoord*i/2 + world.rand.nextFloat()/5 - 0.1f; - double z1 = caster.posZ + vec.zCoord*i/2 + world.rand.nextFloat()/5 - 0.1f; - //world.spawnParticle("mobSpell", x1, y1, z1, -1*look.xCoord, -1*look.yCoord, -1*look.zCoord); - if(i % 5 == 0){ - Wizardry.proxy.spawnParticle(WizardryParticleType.DARK_MAGIC, world, x1, y1, z1, 0.0d, 0.0d, 0.0d, 0, 0.1f, 0.0f, 0.0f); - } - Wizardry.proxy.spawnParticle(WizardryParticleType.SPARKLE, world, x1, y1, z1, -0.05*vec.xCoord*i, -0.05*vec.yCoord*i, -0.05*vec.zCoord*i, 8 + world.rand.nextInt(6), 0.5f, 0.0f, 0.0f); - } - } - if(ticksInUse % 18 == 0){ - if(ticksInUse == 0) caster.playSound(WizardrySounds.SPELL_SUMMONING, 1.0F, 0.6f); - caster.playSound(WizardrySounds.SPELL_LOOP_CRACKLE, 2.0F, 1.0f); - } - - return true; - } - - @Override - public boolean canBeCastByNPCs() { - return true; - } } diff --git a/src/main/java/electroblob/wizardry/spell/Light.java b/src/main/java/electroblob/wizardry/spell/Light.java index 96d056c8..5f0be405 100644 --- a/src/main/java/electroblob/wizardry/spell/Light.java +++ b/src/main/java/electroblob/wizardry/spell/Light.java @@ -1,25 +1,21 @@ package electroblob.wizardry.spell; -import electroblob.wizardry.constants.Element; -import electroblob.wizardry.constants.SpellType; -import electroblob.wizardry.constants.Tier; -import electroblob.wizardry.registry.WizardryBlocks; -import electroblob.wizardry.registry.WizardryItems; -import electroblob.wizardry.registry.WizardrySounds; +import electroblob.wizardry.EnumElement; +import electroblob.wizardry.EnumSpellType; +import electroblob.wizardry.EnumTier; +import electroblob.wizardry.Wizardry; +import electroblob.wizardry.WizardryUtilities; import electroblob.wizardry.tileentity.TileEntityTimer; -import electroblob.wizardry.util.SpellModifiers; -import electroblob.wizardry.util.WizardryUtilities; import net.minecraft.entity.player.EntityPlayer; import net.minecraft.item.EnumAction; -import net.minecraft.util.EnumHand; -import net.minecraft.util.math.BlockPos; -import net.minecraft.util.math.RayTraceResult; +import net.minecraft.util.MovingObjectPosition; +import net.minecraft.util.MovingObjectPosition.MovingObjectType; import net.minecraft.world.World; public class Light extends Spell { public Light() { - super(Tier.BASIC, 5, Element.SORCERY, "light", SpellType.UTILITY, 15, EnumAction.NONE, false); + super(EnumTier.BASIC, 5, EnumElement.SORCERY, "light", EnumSpellType.UTILITY, 15, EnumAction.none, false); } @Override @@ -28,45 +24,54 @@ public class Light extends Spell { } @Override - public boolean cast(World world, EntityPlayer caster, EnumHand hand, int ticksInUse, SpellModifiers modifiers) { + public boolean cast(World world, EntityPlayer caster, int ticksInUse, float damageMultiplier, float rangeMultiplier, float durationMultiplier, float blastMultiplier) { - RayTraceResult rayTrace = WizardryUtilities.rayTrace(4, world, caster, false); + MovingObjectPosition rayTrace = WizardryUtilities.rayTrace(4, world, caster, false); - if(rayTrace != null && rayTrace.typeOfHit == RayTraceResult.Type.BLOCK){ + if(rayTrace != null && rayTrace.typeOfHit == MovingObjectType.BLOCK){ - BlockPos pos = rayTrace.getBlockPos().offset(rayTrace.sideHit); + int blockHitX = rayTrace.blockX; + int blockHitY = rayTrace.blockY; + int blockHitZ = rayTrace.blockZ; + int blockHitSide = rayTrace.sideHit; - if(world.isAirBlock(pos)){ + switch(blockHitSide){ + case 0: blockHitY--; break; + case 1: blockHitY++; break; + case 2: blockHitZ--; break; + case 3: blockHitZ++; break; + case 4: blockHitX--; break; + case 5: blockHitX++; break; + } + + if(world.isAirBlock(blockHitX, blockHitY, blockHitZ)){ if(!world.isRemote){ - world.setBlockState(pos, WizardryBlocks.magic_light.getDefaultState()); - if(world.getTileEntity(pos) instanceof TileEntityTimer){ - ((TileEntityTimer)world.getTileEntity(pos)).setLifetime((int)(600*modifiers.get(WizardryItems.duration_upgrade))); + world.setBlock(blockHitX, blockHitY, blockHitZ, Wizardry.magicLight); + if(world.getTileEntity(blockHitX, blockHitY, blockHitZ) instanceof TileEntityTimer){ + ((TileEntityTimer)world.getTileEntity(blockHitX, blockHitY, blockHitZ)).setLifetime((int)(600*durationMultiplier)); } } - caster.swingArm(hand); - WizardryUtilities.playSoundAtPlayer(caster, WizardrySounds.SPELL_CONJURATION, 1.0f, 1.0f); + caster.swingItem(); + world.playSoundAtEntity(caster, "wizardry:aura", 1.0f, 1.0f); return true; } }else{ - int x = (int) (Math.floor(caster.posX) + caster.getLookVec().xCoord*4); int y = (int) (Math.floor(caster.posY) + caster.eyeHeight + caster.getLookVec().yCoord*4); int z = (int) (Math.floor(caster.posZ) + caster.getLookVec().zCoord*4); - BlockPos pos = new BlockPos(x, y, z); - - if(world.isAirBlock(pos)){ + if(world.isAirBlock(x, y, z)){ //world.playSound(x, y, z, "sound.ambient.cave.cave", 1.0f, 1.5f, false); if(!world.isRemote){ - world.setBlockState(pos, WizardryBlocks.magic_light.getDefaultState()); - if(world.getTileEntity(pos) instanceof TileEntityTimer){ - ((TileEntityTimer)world.getTileEntity(pos)).setLifetime((int)(600*modifiers.get(WizardryItems.duration_upgrade))); + world.setBlock(x, y, z, Wizardry.magicLight); + if(world.getTileEntity(x, y, z) instanceof TileEntityTimer){ + ((TileEntityTimer)world.getTileEntity(x, y, z)).setLifetime((int)(600*durationMultiplier)); } } - caster.swingArm(hand); - WizardryUtilities.playSoundAtPlayer(caster, WizardrySounds.SPELL_CONJURATION, 1.0f, 1.0f); + caster.swingItem(); + world.playSoundAtEntity(caster, "wizardry:aura", 1.0f, 1.0f); return true; } } diff --git a/src/main/java/electroblob/wizardry/spell/LightningArrow.java b/src/main/java/electroblob/wizardry/spell/LightningArrow.java index 423fb35e..64086ff9 100644 --- a/src/main/java/electroblob/wizardry/spell/LightningArrow.java +++ b/src/main/java/electroblob/wizardry/spell/LightningArrow.java @@ -1,24 +1,19 @@ package electroblob.wizardry.spell; -import electroblob.wizardry.constants.Element; -import electroblob.wizardry.constants.SpellType; -import electroblob.wizardry.constants.Tier; +import electroblob.wizardry.EnumElement; +import electroblob.wizardry.EnumSpellType; +import electroblob.wizardry.EnumTier; import electroblob.wizardry.entity.projectile.EntityLightningArrow; -import electroblob.wizardry.registry.WizardryItems; -import electroblob.wizardry.registry.WizardrySounds; -import electroblob.wizardry.util.SpellModifiers; -import electroblob.wizardry.util.WizardryUtilities; import net.minecraft.entity.EntityLiving; import net.minecraft.entity.EntityLivingBase; import net.minecraft.entity.player.EntityPlayer; import net.minecraft.item.EnumAction; -import net.minecraft.util.EnumHand; import net.minecraft.world.World; public class LightningArrow extends Spell { public LightningArrow() { - super(Tier.APPRENTICE, 15, Element.LIGHTNING, "lightning_arrow", SpellType.ATTACK, 20, EnumAction.NONE, false); + super(EnumTier.APPRENTICE, 15, EnumElement.LIGHTNING, "lightning_arrow", EnumSpellType.ATTACK, 20, EnumAction.none, false); } @Override @@ -27,30 +22,30 @@ public class LightningArrow extends Spell { } @Override - public boolean cast(World world, EntityPlayer caster, EnumHand hand, int ticksInUse, SpellModifiers modifiers) { + public boolean cast(World world, EntityPlayer caster, int ticksInUse, float damageMultiplier, float rangeMultiplier, float durationMultiplier, float blastMultiplier) { if(!world.isRemote){ - EntityLightningArrow lightningArrow = new EntityLightningArrow(world, caster, 2*modifiers.get(WizardryItems.range_upgrade), modifiers.get(SpellModifiers.DAMAGE)); + EntityLightningArrow lightningArrow = new EntityLightningArrow(world, caster, 2*rangeMultiplier, damageMultiplier); world.spawnEntityInWorld(lightningArrow); } - caster.swingArm(hand); - WizardryUtilities.playSoundAtPlayer(caster, WizardrySounds.SPELL_LIGHTNING, 1.0F, world.rand.nextFloat() * 0.3F + 1.3F); + caster.swingItem(); + world.playSoundAtEntity(caster, "wizardry:electricitya", 1.0F, world.rand.nextFloat() * 0.3F + 1.3F); return true; } @Override - public boolean cast(World world, EntityLiving caster, EnumHand hand, int ticksInUse, EntityLivingBase target, SpellModifiers modifiers){ + public boolean cast(World world, EntityLiving caster, EntityLivingBase target, float damageMultiplier, float rangeMultiplier, float durationMultiplier, float blastMultiplier){ if(target != null){ if(!world.isRemote){ - EntityLightningArrow lightningArrow = new EntityLightningArrow(world, caster, target, 2*modifiers.get(WizardryItems.range_upgrade), 4, modifiers.get(SpellModifiers.DAMAGE)); + EntityLightningArrow lightningArrow = new EntityLightningArrow(world, caster, target, 2*rangeMultiplier, 4, damageMultiplier); world.spawnEntityInWorld(lightningArrow); } - caster.swingArm(hand); - caster.playSound(WizardrySounds.SPELL_LIGHTNING, 1.0F, world.rand.nextFloat() * 0.3F + 1.3F); + caster.swingItem(); + world.playSoundAtEntity(caster, "wizardry:electricitya", 1.0F, world.rand.nextFloat() * 0.3F + 1.3F); return true; } diff --git a/src/main/java/electroblob/wizardry/spell/LightningBolt.java b/src/main/java/electroblob/wizardry/spell/LightningBolt.java index ae7e1d28..16ec26d2 100644 --- a/src/main/java/electroblob/wizardry/spell/LightningBolt.java +++ b/src/main/java/electroblob/wizardry/spell/LightningBolt.java @@ -1,35 +1,24 @@ package electroblob.wizardry.spell; -import electroblob.wizardry.constants.Element; -import electroblob.wizardry.constants.SpellType; -import electroblob.wizardry.constants.Tier; -import electroblob.wizardry.registry.WizardryAchievements; -import electroblob.wizardry.util.SpellModifiers; -import electroblob.wizardry.util.WizardryUtilities; +import electroblob.wizardry.EnumElement; +import electroblob.wizardry.EnumSpellType; +import electroblob.wizardry.EnumTier; +import electroblob.wizardry.WizardryUtilities; +import net.minecraft.block.Block; import net.minecraft.entity.EntityLiving; import net.minecraft.entity.EntityLivingBase; import net.minecraft.entity.effect.EntityLightningBolt; -import net.minecraft.entity.monster.EntityCreeper; -import net.minecraft.entity.passive.EntityPig; import net.minecraft.entity.player.EntityPlayer; import net.minecraft.item.EnumAction; import net.minecraft.nbt.NBTTagCompound; -import net.minecraft.util.EnumHand; -import net.minecraft.util.math.BlockPos; -import net.minecraft.util.math.RayTraceResult; +import net.minecraft.util.MovingObjectPosition; +import net.minecraft.util.MovingObjectPosition.MovingObjectType; import net.minecraft.world.World; -import net.minecraftforge.event.entity.EntityStruckByLightningEvent; -import net.minecraftforge.fml.common.Mod; -import net.minecraftforge.fml.common.eventhandler.SubscribeEvent; -@Mod.EventBusSubscriber public class LightningBolt extends Spell { - - /** The NBT key used to store the UUID of the player that summoned the lightning bolt. Used for achievements. */ - public static final String NBT_KEY = "summoningPlayer"; public LightningBolt() { - super(Tier.ADVANCED, 40, Element.LIGHTNING, "lightning_bolt", SpellType.ATTACK, 80, EnumAction.NONE, false); + super(EnumTier.ADVANCED, 40, EnumElement.LIGHTNING, "lightning_bolt", EnumSpellType.ATTACK, 80, EnumAction.none, false); } @Override @@ -38,28 +27,29 @@ public class LightningBolt extends Spell { } @Override - public boolean cast(World world, EntityPlayer caster, EnumHand hand, int ticksInUse, SpellModifiers modifiers) { + public boolean cast(World world, EntityPlayer caster, int ticksInUse, float damageMultiplier, float rangeMultiplier, float durationMultiplier, float blastMultiplier) { - RayTraceResult rayTrace = WizardryUtilities.rayTrace(200, world, caster, false); + MovingObjectPosition rayTrace = WizardryUtilities.rayTrace(200, world, caster, false); - if(rayTrace != null && rayTrace.typeOfHit == RayTraceResult.Type.BLOCK){ + if(rayTrace != null && rayTrace.typeOfHit == MovingObjectType.BLOCK){ - BlockPos pos = rayTrace.getBlockPos(); + int i = rayTrace.blockX; + int j = rayTrace.blockY; + int k = rayTrace.blockZ; - // Not sure why it is up 1 but it has to be for canBlockSeeSky to work properly. - // TODO: Remove this requirement? - if(world.canBlockSeeSky(pos.up())){ + // Not sure why it is +1 but it has to be to work properly. + if(world.canBlockSeeTheSky(i, j+1, k)){ if(!world.isRemote){ - EntityLightningBolt entitylightning = new EntityLightningBolt(world, pos.getX(), pos.getY(), pos.getZ(), false); + EntityLightningBolt entitylightning = new EntityLightningBolt(world, i, j, k); world.addWeatherEffect(entitylightning); // Code for eventhandler recognition; for achievements and such like. Left in for future use. NBTTagCompound entityNBT = entitylightning.getEntityData(); - entityNBT.setUniqueId(NBT_KEY, caster.getUniqueID()); + entityNBT.setString("summoningPlayer", caster.getUniqueID().toString()); } - caster.swingArm(hand); + caster.swingItem(); return true; } } @@ -68,24 +58,23 @@ public class LightningBolt extends Spell { } @Override - public boolean cast(World world, EntityLiving caster, EnumHand hand, int ticksInUse, EntityLivingBase target, SpellModifiers modifiers){ + public boolean cast(World world, EntityLiving caster, EntityLivingBase target, float damageMultiplier, float rangeMultiplier, float durationMultiplier, float blastMultiplier){ if(target != null){ - int x = (int)target.posX; - int y = (int)target.posY; - int z = (int)target.posZ; - - // Not sure why it is up 1 but it has to be for canBlockSeeSky to work properly. - // TODO: Remove this requirement? - if(world.canBlockSeeSky(new BlockPos(x, y, z))){ + int i = (int)target.posX; + int j = (int)target.posY; + int k = (int)target.posZ; + + // Not sure why it is +1 but it has to be to work properly. + if(world.canBlockSeeTheSky(i, j+1, k)){ if(!world.isRemote){ - EntityLightningBolt entitylightning = new EntityLightningBolt(world, x, y, z, false); + EntityLightningBolt entitylightning = new EntityLightningBolt(world, i, j, k); world.addWeatherEffect(entitylightning); } - caster.swingArm(hand); + caster.swingItem(); return true; } } @@ -97,22 +86,4 @@ public class LightningBolt extends Spell { public boolean canBeCastByNPCs(){ return true; } - - @SubscribeEvent - public static void onEntityStruckByLightningEvent(EntityStruckByLightningEvent event){ - - if(event.getLightning().getEntityData() != null && event.getLightning().getEntityData().hasKey(NBT_KEY)){ - - EntityPlayer player = (EntityPlayer)WizardryUtilities.getEntityByUUID(event.getLightning().worldObj, event.getLightning().getEntityData().getUniqueId("summoningPlayer")); - - if(event.getEntity() instanceof EntityCreeper){ - player.addStat(WizardryAchievements.charge_creeper); - } - - if(event.getEntity() instanceof EntityPig){ - player.addStat(WizardryAchievements.frankenstein); - } - } - - } } diff --git a/src/main/java/electroblob/wizardry/spell/LightningDisc.java b/src/main/java/electroblob/wizardry/spell/LightningDisc.java index fe0d5f18..99a9ed1e 100644 --- a/src/main/java/electroblob/wizardry/spell/LightningDisc.java +++ b/src/main/java/electroblob/wizardry/spell/LightningDisc.java @@ -1,23 +1,19 @@ package electroblob.wizardry.spell; -import electroblob.wizardry.constants.Element; -import electroblob.wizardry.constants.SpellType; -import electroblob.wizardry.constants.Tier; +import electroblob.wizardry.EnumElement; +import electroblob.wizardry.EnumSpellType; +import electroblob.wizardry.EnumTier; import electroblob.wizardry.entity.projectile.EntityLightningDisc; -import electroblob.wizardry.registry.WizardrySounds; -import electroblob.wizardry.util.SpellModifiers; -import electroblob.wizardry.util.WizardryUtilities; import net.minecraft.entity.EntityLiving; import net.minecraft.entity.EntityLivingBase; import net.minecraft.entity.player.EntityPlayer; import net.minecraft.item.EnumAction; -import net.minecraft.util.EnumHand; import net.minecraft.world.World; public class LightningDisc extends Spell { public LightningDisc() { - super(Tier.ADVANCED, 25, Element.LIGHTNING, "lightning_disc", SpellType.ATTACK, 60, EnumAction.NONE, false); + super(EnumTier.ADVANCED, 25, EnumElement.LIGHTNING, "lightning_disc", EnumSpellType.ATTACK, 60, EnumAction.none, false); } @Override @@ -26,31 +22,31 @@ public class LightningDisc extends Spell { } @Override - public boolean cast(World world, EntityPlayer caster, EnumHand hand, int ticksInUse, SpellModifiers modifiers) { + public boolean cast(World world, EntityPlayer caster, int ticksInUse, float damageMultiplier, float rangeMultiplier, float durationMultiplier, float blastMultiplier) { if(!world.isRemote){ - EntityLightningDisc lightningdisc = new EntityLightningDisc(world, caster, modifiers.get(SpellModifiers.DAMAGE)); + EntityLightningDisc lightningdisc = new EntityLightningDisc(world, caster, damageMultiplier); world.spawnEntityInWorld(lightningdisc); + world.playSoundAtEntity(caster, "wizardry:electricitya", 1.0F, world.rand.nextFloat() * 0.3F + 0.8F); } - - WizardryUtilities.playSoundAtPlayer(caster, WizardrySounds.SPELL_LIGHTNING, 1.0F, world.rand.nextFloat() * 0.3F + 0.8F); - caster.swingArm(hand); + + caster.swingItem(); return true; } @Override - public boolean cast(World world, EntityLiving caster, EnumHand hand, int ticksInUse, EntityLivingBase target, SpellModifiers modifiers){ + public boolean cast(World world, EntityLiving caster, EntityLivingBase target, float damageMultiplier, float rangeMultiplier, float durationMultiplier, float blastMultiplier){ if(target != null){ if(!world.isRemote){ - EntityLightningDisc lightningdisc = new EntityLightningDisc(world, caster, modifiers.get(SpellModifiers.DAMAGE)); + EntityLightningDisc lightningdisc = new EntityLightningDisc(world, caster, damageMultiplier); lightningdisc.directTowards(target, 1.2f); world.spawnEntityInWorld(lightningdisc); + world.playSoundAtEntity(caster, "wizardry:electricitya", 1.0F, world.rand.nextFloat() * 0.3F + 0.8F); } - - caster.playSound(WizardrySounds.SPELL_LIGHTNING, 1.0F, world.rand.nextFloat() * 0.3F + 0.8F); - caster.swingArm(hand); + + caster.swingItem(); return true; } diff --git a/src/main/java/electroblob/wizardry/spell/LightningHammer.java b/src/main/java/electroblob/wizardry/spell/LightningHammer.java index a7408c2e..dd165e75 100644 --- a/src/main/java/electroblob/wizardry/spell/LightningHammer.java +++ b/src/main/java/electroblob/wizardry/spell/LightningHammer.java @@ -1,24 +1,20 @@ package electroblob.wizardry.spell; -import electroblob.wizardry.constants.Element; -import electroblob.wizardry.constants.SpellType; -import electroblob.wizardry.constants.Tier; +import electroblob.wizardry.EnumElement; +import electroblob.wizardry.EnumSpellType; +import electroblob.wizardry.EnumTier; +import electroblob.wizardry.WizardryUtilities; import electroblob.wizardry.entity.construct.EntityHammer; -import electroblob.wizardry.registry.WizardryItems; -import electroblob.wizardry.registry.WizardrySounds; -import electroblob.wizardry.util.SpellModifiers; -import electroblob.wizardry.util.WizardryUtilities; import net.minecraft.entity.player.EntityPlayer; import net.minecraft.item.EnumAction; -import net.minecraft.util.EnumHand; -import net.minecraft.util.math.BlockPos; -import net.minecraft.util.math.RayTraceResult; +import net.minecraft.util.MovingObjectPosition; +import net.minecraft.util.MovingObjectPosition.MovingObjectType; import net.minecraft.world.World; public class LightningHammer extends Spell { public LightningHammer() { - super(Tier.MASTER, 100, Element.LIGHTNING, "lightning_hammer", SpellType.ATTACK, 300, EnumAction.BOW, false); + super(EnumTier.MASTER, 100, EnumElement.LIGHTNING, "lightning_hammer", EnumSpellType.ATTACK, 300, EnumAction.bow, false); } @Override @@ -27,20 +23,22 @@ public class LightningHammer extends Spell { } @Override - public boolean cast(World world, EntityPlayer caster, EnumHand hand, int ticksInUse, SpellModifiers modifiers) { + public boolean cast(World world, EntityPlayer caster, int ticksInUse, float damageMultiplier, float rangeMultiplier, float durationMultiplier, float blastMultiplier) { - RayTraceResult rayTrace = WizardryUtilities.rayTrace(40*modifiers.get(WizardryItems.range_upgrade), world, caster, false); + MovingObjectPosition rayTrace = WizardryUtilities.rayTrace(40*rangeMultiplier, world, caster, false); - if(rayTrace != null && rayTrace.typeOfHit == RayTraceResult.Type.BLOCK){ + if(rayTrace != null && rayTrace.typeOfHit == MovingObjectType.BLOCK){ - BlockPos pos = rayTrace.getBlockPos(); + int x = rayTrace.blockX; + int y = rayTrace.blockY; + int z = rayTrace.blockZ; // Not sure why it is +1 but it has to be to work properly. - if(world.canBlockSeeSky(pos.up())){ + if(world.canBlockSeeTheSky(x, y+1, z)){ if(!world.isRemote){ - EntityHammer hammer = new EntityHammer(world, pos.getX()+0.5, pos.getY()+50, pos.getZ()+0.5, caster, (int)(600*modifiers.get(WizardryItems.duration_upgrade)), modifiers.get(SpellModifiers.DAMAGE)); + EntityHammer hammer = new EntityHammer(world, x+0.5, y + 50, z+0.5, caster, (int)(600*durationMultiplier), damageMultiplier); hammer.motionX = 0; hammer.motionY = -2; @@ -49,8 +47,8 @@ public class LightningHammer extends Spell { world.spawnEntityInWorld(hammer); } - caster.swingArm(hand); - WizardryUtilities.playSoundAtPlayer(caster, WizardrySounds.SPELL_SUMMONING, 3.0f, 1.0f); + caster.swingItem(); + world.playSoundAtEntity(caster, "wizardry:darkaura", 3.0f, 1.0f); return true; } } diff --git a/src/main/java/electroblob/wizardry/spell/LightningPulse.java b/src/main/java/electroblob/wizardry/spell/LightningPulse.java index f180b10d..4226d008 100644 --- a/src/main/java/electroblob/wizardry/spell/LightningPulse.java +++ b/src/main/java/electroblob/wizardry/spell/LightningPulse.java @@ -2,29 +2,25 @@ package electroblob.wizardry.spell; import java.util.List; -import electroblob.wizardry.constants.Element; -import electroblob.wizardry.constants.SpellType; -import electroblob.wizardry.constants.Tier; +import electroblob.wizardry.EnumElement; +import electroblob.wizardry.EnumSpellType; +import electroblob.wizardry.EnumTier; +import electroblob.wizardry.MagicDamage; +import electroblob.wizardry.WizardryUtilities; +import electroblob.wizardry.MagicDamage.DamageType; import electroblob.wizardry.entity.construct.EntityLightningPulse; -import electroblob.wizardry.registry.WizardryItems; -import electroblob.wizardry.registry.WizardrySounds; -import electroblob.wizardry.util.MagicDamage; -import electroblob.wizardry.util.MagicDamage.DamageType; -import electroblob.wizardry.util.SpellModifiers; -import electroblob.wizardry.util.WizardryUtilities; import net.minecraft.entity.EntityLivingBase; import net.minecraft.entity.player.EntityPlayer; import net.minecraft.entity.player.EntityPlayerMP; import net.minecraft.item.EnumAction; -import net.minecraft.network.play.server.SPacketEntityVelocity; -import net.minecraft.util.EnumHand; -import net.minecraft.util.math.MathHelper; +import net.minecraft.network.play.server.S12PacketEntityVelocity; +import net.minecraft.util.MathHelper; import net.minecraft.world.World; public class LightningPulse extends Spell { public LightningPulse() { - super(Tier.ADVANCED, 25, Element.LIGHTNING, "lightning_pulse", SpellType.ATTACK, 75, EnumAction.NONE, false); + super(EnumTier.ADVANCED, 25, EnumElement.LIGHTNING, "lightning_pulse", EnumSpellType.ATTACK, 75, EnumAction.none, false); } @Override @@ -33,16 +29,16 @@ public class LightningPulse extends Spell { } @Override - public boolean cast(World world, EntityPlayer caster, EnumHand hand, int ticksInUse, SpellModifiers modifiers) { + public boolean cast(World world, EntityPlayer caster, int ticksInUse, float damageMultiplier, float rangeMultiplier, float durationMultiplier, float blastMultiplier) { if(caster.onGround){ - List targets = WizardryUtilities.getEntitiesWithinRadius(3.0d*modifiers.get(WizardryItems.blast_upgrade), caster.posX, caster.posY, caster.posZ, world); + List targets = WizardryUtilities.getEntitiesWithinRadius(3.0d*blastMultiplier, caster.posX, caster.posY, caster.posZ, world); for(EntityLivingBase target : targets){ if(WizardryUtilities.isValidTarget(caster, target)){ // Damage is 4 hearts no matter where the target is. - target.attackEntityFrom(MagicDamage.causeDirectMagicDamage(caster, DamageType.SHOCK), 8 * modifiers.get(SpellModifiers.DAMAGE)); + target.attackEntityFrom(MagicDamage.causeDirectMagicDamage(caster, DamageType.SHOCK), 8 * damageMultiplier); if(!world.isRemote){ @@ -59,18 +55,18 @@ public class LightningPulse extends Spell { // Player motion is handled on that player's client so needs packets if(target instanceof EntityPlayerMP){ - ((EntityPlayerMP)target).connection.sendPacket(new SPacketEntityVelocity(target)); + ((EntityPlayerMP)target).playerNetServerHandler.sendPacket(new S12PacketEntityVelocity(target)); } } } } if(!world.isRemote){ - EntityLightningPulse lightningpulse = new EntityLightningPulse(world, caster.posX, caster.getEntityBoundingBox().minY, caster.posZ, caster, 7, modifiers.get(SpellModifiers.DAMAGE)); + EntityLightningPulse lightningpulse = new EntityLightningPulse(world, caster.posX, caster.boundingBox.minY, caster.posZ, caster, 7, damageMultiplier); world.spawnEntityInWorld(lightningpulse); } - caster.swingArm(hand); - WizardryUtilities.playSoundAtPlayer(caster, WizardrySounds.SPELL_LIGHTNING, 1.0f, 1.0f); - WizardryUtilities.playSoundAtPlayer(caster, WizardrySounds.SPELL_SHOCKWAVE, 2.0f, 1.0f); + caster.swingItem(); + world.playSoundAtEntity(caster, "wizardry:electricitya", 1.0f, 1.0f); + world.playSoundAtEntity(caster, "wizardry:boom", 2.0f, 1.0f); return true; } return false; diff --git a/src/main/java/electroblob/wizardry/spell/LightningRay.java b/src/main/java/electroblob/wizardry/spell/LightningRay.java index b956bd49..e93e766c 100644 --- a/src/main/java/electroblob/wizardry/spell/LightningRay.java +++ b/src/main/java/electroblob/wizardry/spell/LightningRay.java @@ -1,39 +1,38 @@ package electroblob.wizardry.spell; +import electroblob.wizardry.EnumElement; +import electroblob.wizardry.EnumParticleType; +import electroblob.wizardry.EnumSpellType; +import electroblob.wizardry.EnumTier; +import electroblob.wizardry.MagicDamage; import electroblob.wizardry.Wizardry; -import electroblob.wizardry.constants.Element; -import electroblob.wizardry.constants.SpellType; -import electroblob.wizardry.constants.Tier; +import electroblob.wizardry.WizardryUtilities; +import electroblob.wizardry.MagicDamage.DamageType; import electroblob.wizardry.entity.EntityArc; -import electroblob.wizardry.registry.WizardryItems; -import electroblob.wizardry.registry.WizardrySounds; -import electroblob.wizardry.util.MagicDamage; -import electroblob.wizardry.util.MagicDamage.DamageType; -import electroblob.wizardry.util.SpellModifiers; -import electroblob.wizardry.util.WizardryParticleType; -import electroblob.wizardry.util.WizardryUtilities; +import electroblob.wizardry.entity.living.EntityLightningWraith; +import electroblob.wizardry.entity.living.EntityStormElemental; import net.minecraft.entity.Entity; -import net.minecraft.entity.EntityLiving; import net.minecraft.entity.EntityLivingBase; +import net.minecraft.entity.monster.EntityCreeper; import net.minecraft.entity.player.EntityPlayer; import net.minecraft.item.EnumAction; -import net.minecraft.util.EnumHand; -import net.minecraft.util.math.RayTraceResult; -import net.minecraft.util.text.TextComponentTranslation; +import net.minecraft.util.ChatComponentTranslation; +import net.minecraft.util.MovingObjectPosition; +import net.minecraft.util.MovingObjectPosition.MovingObjectType; import net.minecraft.world.World; public class LightningRay extends Spell { public LightningRay() { - super(Tier.APPRENTICE, 5, Element.LIGHTNING, "lightning_ray", SpellType.ATTACK, 0, EnumAction.NONE, true); + super(EnumTier.APPRENTICE, 5, EnumElement.LIGHTNING, "lightning_ray", EnumSpellType.ATTACK, 0, EnumAction.none, true); } @Override - public boolean cast(World world, EntityPlayer caster, EnumHand hand, int ticksInUse, SpellModifiers modifiers) { + public boolean cast(World world, EntityPlayer caster, int ticksInUse, float damageMultiplier, float rangeMultiplier, float durationMultiplier, float blastMultiplier) { - RayTraceResult rayTrace = WizardryUtilities.standardEntityRayTrace(world, caster, 10*modifiers.get(WizardryItems.range_upgrade), 2.0f); + MovingObjectPosition rayTrace = WizardryUtilities.standardEntityRayTrace(world, caster, 10*rangeMultiplier, 2.0f); - if(rayTrace != null && rayTrace.typeOfHit == RayTraceResult.Type.ENTITY && rayTrace.entityHit instanceof EntityLivingBase){ + if(rayTrace != null && rayTrace.typeOfHit == MovingObjectType.ENTITY && rayTrace.entityHit instanceof EntityLivingBase){ Entity target = rayTrace.entityHit; if(!world.isRemote){ // This statement means the arc only spawns every other tick. @@ -51,25 +50,32 @@ public class LightningRay extends Spell { } if(MagicDamage.isEntityImmune(DamageType.SHOCK, target)){ - if(!world.isRemote && ticksInUse == 1) caster.addChatComponentMessage(new TextComponentTranslation("spell.resist", target.getName(), this.getNameForTranslationFormatted())); + if(!world.isRemote && ticksInUse == 1) caster.addChatComponentMessage(new ChatComponentTranslation("spell.resist", target.getCommandSenderName(), this.getDisplayNameWithFormatting())); }else{ - WizardryUtilities.attackEntityWithoutKnockback(target, MagicDamage.causeDirectMagicDamage(caster, DamageType.SHOCK), 3.0f * modifiers.get(SpellModifiers.DAMAGE)); + // This motion stuff removes knockback, which is desirable for continuous spells. + double motionX = target.motionX; + double motionY = target.motionY; + double motionZ = target.motionZ; + + target.attackEntityFrom(MagicDamage.causeDirectMagicDamage(caster, DamageType.SHOCK), 3.0f * damageMultiplier); + + target.motionX = motionX; + target.motionY = motionY; + target.motionZ = motionZ; + } + + if(ticksInUse == 1){ + world.playSoundAtEntity(caster, "wizardry:electricitya", 1.0F, 1.0f); + }else if(ticksInUse > 0 && ticksInUse % 20 == 0){ + world.playSoundAtEntity(caster, "wizardry:electricityb", 1.0F, 1.0f); } }else{ for(int i=0;i<5;i++){ - Wizardry.proxy.spawnParticle(WizardryParticleType.SPARK, world, target.posX + world.rand.nextFloat() - 0.5, target.getEntityBoundingBox().minY + target.height/2 + world.rand.nextFloat()*2 - 1, target.posZ + world.rand.nextFloat() - 0.5, 0, 0, 0, 3); + Wizardry.proxy.spawnParticle(EnumParticleType.SPARK, world, target.posX + world.rand.nextFloat() - 0.5, WizardryUtilities.getEntityFeetPos(target) + target.height/2 + world.rand.nextFloat()*2 - 1, target.posZ + world.rand.nextFloat() - 0.5, 0, 0, 0, 3); } } - - if(ticksInUse == 1){ - WizardryUtilities.playSoundAtPlayer(caster, WizardrySounds.SPELL_LIGHTNING, 1.0F, 1.0f); - }else if(ticksInUse > 0 && ticksInUse % 20 == 0){ - WizardryUtilities.playSoundAtPlayer(caster, WizardrySounds.SPELL_LOOP_LIGHTNING, 1.0F, 1.0f); - } - return true; - }else{ if(!world.isRemote){ // This statement means the arc only spawns every other tick. @@ -87,57 +93,14 @@ public class LightningRay extends Spell { } if(ticksInUse == 1){ - WizardryUtilities.playSoundAtPlayer(caster, WizardrySounds.SPELL_LIGHTNING, 1.0F, 1.0f); + world.playSoundAtEntity(caster, "wizardry:electricitya", 1.0F, 1.0f); }else if(ticksInUse > 0 && ticksInUse % 20 == 0){ - WizardryUtilities.playSoundAtPlayer(caster, WizardrySounds.SPELL_LOOP_LIGHTNING, 1.0F, 1.0f); + world.playSoundAtEntity(caster, "wizardry:electricityb", 1.0F, 1.0f); } return true; } } - - @Override - public boolean cast(World world, EntityLiving caster, EnumHand hand, int ticksInUse, EntityLivingBase target, SpellModifiers modifiers){ - - if(target != null){ - if(!world.isRemote){ - // This statement means the arc only spawns every other tick. - if(ticksInUse % 2 == 0){ - - EntityArc arc = new EntityArc(world); - // The look vec stuff performs a translation on the start point to line it up with the wand. - // EDIT: removed due to 1st/3rd person render differences. - arc.setEndpointCoords(caster.posX, caster.posY + 1.2, caster.posZ, - target.posX, target.posY + target.height/2, target.posZ); - - arc.lifetime = 1; - - world.spawnEntityInWorld(arc); - } - - WizardryUtilities.attackEntityWithoutKnockback(target, MagicDamage.causeDirectMagicDamage(caster, DamageType.SHOCK), 3.0f * modifiers.get(SpellModifiers.DAMAGE)); - - }else{ - for(int i=0;i<5;i++){ - Wizardry.proxy.spawnParticle(WizardryParticleType.SPARK, world, target.posX + world.rand.nextFloat() - 0.5, target.getEntityBoundingBox().minY + target.height/2 + world.rand.nextFloat()*2 - 1, target.posZ + world.rand.nextFloat() - 0.5, 0, 0, 0, 3); - } - } - - if(ticksInUse == 1){ - caster.playSound(WizardrySounds.SPELL_LIGHTNING, 1.0F, 1.0f); - }else if(ticksInUse > 0 && ticksInUse % 20 == 0){ - caster.playSound(WizardrySounds.SPELL_LOOP_LIGHTNING, 1.0F, 1.0f); - } - - return true; - } - - return false; - } - @Override - public boolean canBeCastByNPCs(){ - return true; - } } diff --git a/src/main/java/electroblob/wizardry/spell/LightningSigil.java b/src/main/java/electroblob/wizardry/spell/LightningSigil.java index 831a1189..a580342c 100644 --- a/src/main/java/electroblob/wizardry/spell/LightningSigil.java +++ b/src/main/java/electroblob/wizardry/spell/LightningSigil.java @@ -1,24 +1,20 @@ package electroblob.wizardry.spell; -import electroblob.wizardry.constants.Element; -import electroblob.wizardry.constants.SpellType; -import electroblob.wizardry.constants.Tier; +import electroblob.wizardry.EnumElement; +import electroblob.wizardry.EnumSpellType; +import electroblob.wizardry.EnumTier; +import electroblob.wizardry.WizardryUtilities; import electroblob.wizardry.entity.construct.EntityLightningSigil; -import electroblob.wizardry.registry.WizardryItems; -import electroblob.wizardry.registry.WizardrySounds; -import electroblob.wizardry.util.SpellModifiers; -import electroblob.wizardry.util.WizardryUtilities; import net.minecraft.entity.player.EntityPlayer; import net.minecraft.item.EnumAction; -import net.minecraft.util.EnumFacing; -import net.minecraft.util.EnumHand; -import net.minecraft.util.math.RayTraceResult; +import net.minecraft.util.MovingObjectPosition; +import net.minecraft.util.MovingObjectPosition.MovingObjectType; import net.minecraft.world.World; public class LightningSigil extends Spell { public LightningSigil() { - super(Tier.APPRENTICE, 10, Element.LIGHTNING, "lightning_sigil", SpellType.ATTACK, 20, EnumAction.NONE, false); + super(EnumTier.APPRENTICE, 10, EnumElement.LIGHTNING, "lightning_sigil", EnumSpellType.ATTACK, 20, EnumAction.none, false); } @Override @@ -27,22 +23,20 @@ public class LightningSigil extends Spell { } @Override - public boolean cast(World world, EntityPlayer caster, EnumHand hand, int ticksInUse, SpellModifiers modifiers) { + public boolean cast(World world, EntityPlayer caster, int ticksInUse, float damageMultiplier, float rangeMultiplier, float durationMultiplier, float blastMultiplier) { - RayTraceResult rayTrace = WizardryUtilities.rayTrace(10*modifiers.get(WizardryItems.range_upgrade), world, caster, false); + MovingObjectPosition rayTrace = WizardryUtilities.rayTrace(10*rangeMultiplier, world, caster, false); - if(rayTrace != null && rayTrace.typeOfHit == RayTraceResult.Type.BLOCK && rayTrace.sideHit == EnumFacing.UP){ - + if(rayTrace != null && rayTrace.typeOfHit == MovingObjectType.BLOCK && rayTrace.sideHit == 1){ if(!world.isRemote){ - double x = rayTrace.hitVec.xCoord; - double y = rayTrace.hitVec.yCoord; - double z = rayTrace.hitVec.zCoord; - EntityLightningSigil lightningsigil = new EntityLightningSigil(world, x, y, z, caster, modifiers.get(SpellModifiers.DAMAGE)); + double x = rayTrace.blockX; + double y = rayTrace.blockY; + double z = rayTrace.blockZ; + EntityLightningSigil lightningsigil = new EntityLightningSigil(world, x + 0.5, y+1, z + 0.5, caster, damageMultiplier); world.spawnEntityInWorld(lightningsigil); } - - caster.swingArm(hand); - WizardryUtilities.playSoundAtPlayer(caster, WizardrySounds.SPELL_CONJURATION, 1.0F, 0.3F); + caster.swingItem(); + world.playSoundAtEntity(caster, "wizardry:aura", 1.0F, 0.3F); return true; } return false; diff --git a/src/main/java/electroblob/wizardry/spell/LightningWeb.java b/src/main/java/electroblob/wizardry/spell/LightningWeb.java index 3b654be1..2df7764c 100644 --- a/src/main/java/electroblob/wizardry/spell/LightningWeb.java +++ b/src/main/java/electroblob/wizardry/spell/LightningWeb.java @@ -2,39 +2,39 @@ package electroblob.wizardry.spell; import java.util.List; +import electroblob.wizardry.EnumElement; +import electroblob.wizardry.EnumParticleType; +import electroblob.wizardry.EnumSpellType; +import electroblob.wizardry.EnumTier; +import electroblob.wizardry.MagicDamage; import electroblob.wizardry.Wizardry; -import electroblob.wizardry.constants.Element; -import electroblob.wizardry.constants.SpellType; -import electroblob.wizardry.constants.Tier; +import electroblob.wizardry.WizardryUtilities; +import electroblob.wizardry.MagicDamage.DamageType; import electroblob.wizardry.entity.EntityArc; -import electroblob.wizardry.registry.WizardryItems; -import electroblob.wizardry.registry.WizardrySounds; -import electroblob.wizardry.util.MagicDamage; -import electroblob.wizardry.util.MagicDamage.DamageType; -import electroblob.wizardry.util.SpellModifiers; -import electroblob.wizardry.util.WizardryParticleType; -import electroblob.wizardry.util.WizardryUtilities; +import electroblob.wizardry.entity.living.EntityLightningWraith; +import electroblob.wizardry.entity.living.EntityStormElemental; import net.minecraft.entity.Entity; import net.minecraft.entity.EntityLivingBase; +import net.minecraft.entity.monster.EntityCreeper; import net.minecraft.entity.player.EntityPlayer; import net.minecraft.item.EnumAction; -import net.minecraft.util.EnumHand; -import net.minecraft.util.math.RayTraceResult; -import net.minecraft.util.text.TextComponentTranslation; +import net.minecraft.util.ChatComponentTranslation; +import net.minecraft.util.MovingObjectPosition; +import net.minecraft.util.MovingObjectPosition.MovingObjectType; import net.minecraft.world.World; public class LightningWeb extends Spell { public LightningWeb() { - super(Tier.MASTER, 15, Element.LIGHTNING, "lightning_web", SpellType.ATTACK, 0, EnumAction.NONE, true); + super(EnumTier.MASTER, 15, EnumElement.LIGHTNING, "lightning_web", EnumSpellType.ATTACK, 0, EnumAction.none, true); } @Override - public boolean cast(World world, EntityPlayer caster, EnumHand hand, int ticksInUse, SpellModifiers modifiers) { + public boolean cast(World world, EntityPlayer caster, int ticksInUse, float damageMultiplier, float rangeMultiplier, float durationMultiplier, float blastMultiplier) { - RayTraceResult rayTrace = WizardryUtilities.standardEntityRayTrace(world, caster, 10*modifiers.get(WizardryItems.range_upgrade), 2.0f); + MovingObjectPosition rayTrace = WizardryUtilities.standardEntityRayTrace(world, caster, 10*rangeMultiplier, 2.0f); - if(rayTrace != null && rayTrace.typeOfHit == RayTraceResult.Type.ENTITY && rayTrace.entityHit instanceof EntityLivingBase){ + if(rayTrace != null && rayTrace.typeOfHit == MovingObjectType.ENTITY && rayTrace.entityHit instanceof EntityLivingBase){ Entity target = rayTrace.entityHit; @@ -48,26 +48,34 @@ public class LightningWeb extends Spell { arc.setEndpointCoords(caster.posX, caster.posY + 1.2, caster.posZ, target.posX, target.posY + target.height/2, target.posZ); arc.lifetime = 1; + arc.setOffset(caster.getLookVec().zCoord * 0.5, caster.getLookVec().xCoord * 0.5); world.spawnEntityInWorld(arc); } if(MagicDamage.isEntityImmune(DamageType.SHOCK, target)){ - if(!world.isRemote && ticksInUse == 1) caster.addChatComponentMessage(new TextComponentTranslation("spell.resist", target.getName(), this.getNameForTranslationFormatted())); + if(!world.isRemote && ticksInUse == 1) caster.addChatComponentMessage(new ChatComponentTranslation("spell.resist", target.getCommandSenderName(), this.getDisplayNameWithFormatting())); }else{ // This motion stuff removes knockback, which is desirable for continuous spells. double motionX = target.motionX; double motionY = target.motionY; double motionZ = target.motionZ; - target.attackEntityFrom(MagicDamage.causeDirectMagicDamage(caster, DamageType.SHOCK), 5.0f * modifiers.get(SpellModifiers.DAMAGE)); + target.attackEntityFrom(MagicDamage.causeDirectMagicDamage(caster, DamageType.SHOCK), 5.0f * damageMultiplier); target.motionX = motionX; target.motionY = motionY; target.motionZ = motionZ; } + + if(ticksInUse == 1){ + world.playSoundAtEntity(caster, "wizardry:electricitya", 1.0F, 1.0f); + }else if(ticksInUse > 0 && ticksInUse % 20 == 0){ + world.playSoundAtEntity(caster, "wizardry:electricityb", 1.0F, 1.0f); + } + }else{ for(int i=0;i<5;i++){ - Wizardry.proxy.spawnParticle(WizardryParticleType.SPARK, world, target.posX + world.rand.nextFloat() - 0.5, target.getEntityBoundingBox().minY + target.height/2 + world.rand.nextFloat()*2 - 1, target.posZ + world.rand.nextFloat() - 0.5, 0, 0, 0, 3); + Wizardry.proxy.spawnParticle(EnumParticleType.SPARK, world, target.posX + world.rand.nextFloat() - 0.5, WizardryUtilities.getEntityFeetPos(target) + target.height/2 + world.rand.nextFloat()*2 - 1, target.posZ + world.rand.nextFloat() - 0.5, 0, 0, 0, 3); } } @@ -94,22 +102,29 @@ public class LightningWeb extends Spell { } if(MagicDamage.isEntityImmune(DamageType.SHOCK, secondaryTarget)){ - if(!world.isRemote && ticksInUse == 1) caster.addChatComponentMessage(new TextComponentTranslation("spell.resist", secondaryTarget.getName(), this.getNameForTranslationFormatted())); + if(!world.isRemote && ticksInUse == 1) caster.addChatComponentMessage(new ChatComponentTranslation("spell.resist", secondaryTarget.getCommandSenderName(), this.getDisplayNameWithFormatting())); }else{ // This motion stuff removes knockback, which is desirable for continuous spells. double motionX = secondaryTarget.motionX; double motionY = secondaryTarget.motionY; double motionZ = secondaryTarget.motionZ; - secondaryTarget.attackEntityFrom(MagicDamage.causeDirectMagicDamage(caster, DamageType.SHOCK), 4.0f * modifiers.get(SpellModifiers.DAMAGE)); + secondaryTarget.attackEntityFrom(MagicDamage.causeDirectMagicDamage(caster, DamageType.SHOCK), 4.0f * damageMultiplier); secondaryTarget.motionX = motionX; secondaryTarget.motionY = motionY; secondaryTarget.motionZ = motionZ; } + + if(ticksInUse == 1){ + world.playSoundAtEntity(caster, "wizardry:electricitya", 1.0F, 1.0f); + }else if(ticksInUse > 0 && ticksInUse % 20 == 0){ + world.playSoundAtEntity(caster, "wizardry:electricityb", 1.0F, 1.0f); + } + }else{ for(int i=0;i<5;i++){ - Wizardry.proxy.spawnParticle(WizardryParticleType.SPARK, world, secondaryTarget.posX + world.rand.nextFloat() - 0.5, secondaryTarget.getEntityBoundingBox().minY + secondaryTarget.height/2 + world.rand.nextFloat()*2 - 1, secondaryTarget.posZ + world.rand.nextFloat() - 0.5, 0, 0, 0, 3); + Wizardry.proxy.spawnParticle(EnumParticleType.SPARK, world, secondaryTarget.posX + world.rand.nextFloat() - 0.5, WizardryUtilities.getEntityFeetPos(secondaryTarget) + secondaryTarget.height/2 + world.rand.nextFloat()*2 - 1, secondaryTarget.posZ + world.rand.nextFloat() - 0.5, 0, 0, 0, 3); } } @@ -135,22 +150,29 @@ public class LightningWeb extends Spell { } if(MagicDamage.isEntityImmune(DamageType.SHOCK, tertiaryTarget)){ - if(!world.isRemote && ticksInUse == 1) caster.addChatComponentMessage(new TextComponentTranslation("spell.resist", tertiaryTarget.getName(), this.getNameForTranslationFormatted())); + if(!world.isRemote && ticksInUse == 1) caster.addChatComponentMessage(new ChatComponentTranslation("spell.resist", tertiaryTarget.getCommandSenderName(), this.getDisplayNameWithFormatting())); }else{ // This motion stuff removes knockback, which is desirable for continuous spells. double motionX = tertiaryTarget.motionX; double motionY = tertiaryTarget.motionY; double motionZ = tertiaryTarget.motionZ; - tertiaryTarget.attackEntityFrom(MagicDamage.causeDirectMagicDamage(caster, DamageType.SHOCK), 3.0f * modifiers.get(SpellModifiers.DAMAGE)); + tertiaryTarget.attackEntityFrom(MagicDamage.causeDirectMagicDamage(caster, DamageType.SHOCK), 3.0f * damageMultiplier); tertiaryTarget.motionX = motionX; tertiaryTarget.motionY = motionY; tertiaryTarget.motionZ = motionZ; } + + if(ticksInUse == 1){ + world.playSoundAtEntity(caster, "wizardry:electricitya", 1.0F, 1.0f); + }else if(ticksInUse > 0 && ticksInUse % 20 == 0){ + world.playSoundAtEntity(caster, "wizardry:electricityb", 1.0F, 1.0f); + } + }else{ for(int i=0;i<5;i++){ - Wizardry.proxy.spawnParticle(WizardryParticleType.SPARK, world, tertiaryTarget.posX + world.rand.nextFloat() - 0.5, tertiaryTarget.getEntityBoundingBox().minY + tertiaryTarget.height/2 + world.rand.nextFloat()*2 - 1, tertiaryTarget.posZ + world.rand.nextFloat() - 0.5, 0, 0, 0, 3); + Wizardry.proxy.spawnParticle(EnumParticleType.SPARK, world, tertiaryTarget.posX + world.rand.nextFloat() - 0.5, WizardryUtilities.getEntityFeetPos(tertiaryTarget) + tertiaryTarget.height/2 + world.rand.nextFloat()*2 - 1, tertiaryTarget.posZ + world.rand.nextFloat() - 0.5, 0, 0, 0, 3); } } } @@ -158,12 +180,6 @@ public class LightningWeb extends Spell { } } - if(ticksInUse == 1){ - WizardryUtilities.playSoundAtPlayer(caster, WizardrySounds.SPELL_LIGHTNING, 1.0F, 1.0f); - }else if(ticksInUse > 0 && ticksInUse % 20 == 0){ - WizardryUtilities.playSoundAtPlayer(caster, WizardrySounds.SPELL_LOOP_LIGHTNING, 1.0F, 1.0f); - } - return true; }else{ @@ -180,9 +196,9 @@ public class LightningWeb extends Spell { } if(ticksInUse == 1){ - WizardryUtilities.playSoundAtPlayer(caster, WizardrySounds.SPELL_LIGHTNING, 1.0F, 1.0f); + world.playSoundAtEntity(caster, "wizardry:electricitya", 1.0F, 1.0f); }else if(ticksInUse > 0 && ticksInUse % 20 == 0){ - WizardryUtilities.playSoundAtPlayer(caster, WizardrySounds.SPELL_LOOP_LIGHTNING, 1.0F, 1.0f); + world.playSoundAtEntity(caster, "wizardry:electricityb", 1.0F, 1.0f); } return true; diff --git a/src/main/java/electroblob/wizardry/spell/MagicMissile.java b/src/main/java/electroblob/wizardry/spell/MagicMissile.java index 9cb083e3..d029276b 100644 --- a/src/main/java/electroblob/wizardry/spell/MagicMissile.java +++ b/src/main/java/electroblob/wizardry/spell/MagicMissile.java @@ -1,24 +1,19 @@ package electroblob.wizardry.spell; -import electroblob.wizardry.constants.Element; -import electroblob.wizardry.constants.SpellType; -import electroblob.wizardry.constants.Tier; +import electroblob.wizardry.EnumElement; +import electroblob.wizardry.EnumSpellType; +import electroblob.wizardry.EnumTier; import electroblob.wizardry.entity.projectile.EntityMagicMissile; -import electroblob.wizardry.registry.WizardryItems; -import electroblob.wizardry.registry.WizardrySounds; -import electroblob.wizardry.util.SpellModifiers; -import electroblob.wizardry.util.WizardryUtilities; import net.minecraft.entity.EntityLiving; import net.minecraft.entity.EntityLivingBase; import net.minecraft.entity.player.EntityPlayer; import net.minecraft.item.EnumAction; -import net.minecraft.util.EnumHand; import net.minecraft.world.World; public class MagicMissile extends Spell { public MagicMissile() { - super(Tier.BASIC, 5, Element.MAGIC, "magic_missile", SpellType.ATTACK, 10, EnumAction.NONE, false); + super(EnumTier.BASIC, 5, EnumElement.MAGIC, "magic_missile", EnumSpellType.ATTACK, 10, EnumAction.none, false); } @Override @@ -27,31 +22,31 @@ public class MagicMissile extends Spell { } @Override - public boolean cast(World world, EntityPlayer caster, EnumHand hand, int ticksInUse, SpellModifiers modifiers) { + public boolean cast(World world, EntityPlayer caster, int ticksInUse, float damageMultiplier, float rangeMultiplier, float durationMultiplier, float blastMultiplier) { if(!world.isRemote){ - EntityMagicMissile magicMissile = new EntityMagicMissile(world, caster, 2*modifiers.get(WizardryItems.range_upgrade), modifiers.get(SpellModifiers.DAMAGE)); + EntityMagicMissile magicMissile = new EntityMagicMissile(world, caster, 2*rangeMultiplier, damageMultiplier); world.spawnEntityInWorld(magicMissile); } - caster.swingArm(hand); - WizardryUtilities.playSoundAtPlayer(caster, WizardrySounds.SPELL_MAGIC, 1.0F, world.rand.nextFloat() * 0.4F + 1.2F); + caster.swingItem(); + world.playSoundAtEntity(caster, "wizardry:magic", 1.0F, world.rand.nextFloat() * 0.4F + 1.2F); return true; } @Override - public boolean cast(World world, EntityLiving caster, EnumHand hand, int ticksInUse, EntityLivingBase target, SpellModifiers modifiers){ + public boolean cast(World world, EntityLiving caster, EntityLivingBase target, float damageMultiplier, float rangeMultiplier, float durationMultiplier, float blastMultiplier){ if(target != null){ if(!world.isRemote){ - EntityMagicMissile magicMissile = new EntityMagicMissile(world, caster, target, 2*modifiers.get(WizardryItems.range_upgrade), 4, modifiers.get(SpellModifiers.DAMAGE)); + EntityMagicMissile magicMissile = new EntityMagicMissile(world, caster, target, 2*rangeMultiplier, 4, damageMultiplier); world.spawnEntityInWorld(magicMissile); } - caster.swingArm(hand); - caster.playSound(WizardrySounds.SPELL_MAGIC, 1.0F, world.rand.nextFloat() * 0.4F + 1.2F); + caster.swingItem(); + world.playSoundAtEntity(caster, "wizardry:magic", 1.0F, world.rand.nextFloat() * 0.4F + 1.2F); return true; } diff --git a/src/main/java/electroblob/wizardry/spell/Metamorphosis.java b/src/main/java/electroblob/wizardry/spell/Metamorphosis.java index abfceedc..69cf50ff 100644 --- a/src/main/java/electroblob/wizardry/spell/Metamorphosis.java +++ b/src/main/java/electroblob/wizardry/spell/Metamorphosis.java @@ -1,14 +1,11 @@ package electroblob.wizardry.spell; +import electroblob.wizardry.EnumElement; +import electroblob.wizardry.EnumParticleType; +import electroblob.wizardry.EnumSpellType; +import electroblob.wizardry.EnumTier; import electroblob.wizardry.Wizardry; -import electroblob.wizardry.constants.Element; -import electroblob.wizardry.constants.SpellType; -import electroblob.wizardry.constants.Tier; -import electroblob.wizardry.registry.WizardryItems; -import electroblob.wizardry.registry.WizardrySounds; -import electroblob.wizardry.util.SpellModifiers; -import electroblob.wizardry.util.WizardryParticleType; -import electroblob.wizardry.util.WizardryUtilities; +import electroblob.wizardry.WizardryUtilities; import net.minecraft.entity.Entity; import net.minecraft.entity.EntityLiving; import net.minecraft.entity.EntityLivingBase; @@ -18,31 +15,31 @@ import net.minecraft.entity.monster.EntityPigZombie; import net.minecraft.entity.monster.EntitySkeleton; import net.minecraft.entity.monster.EntitySlime; import net.minecraft.entity.monster.EntitySpider; -import net.minecraft.entity.monster.SkeletonType; +import net.minecraft.entity.monster.EntityWitch; import net.minecraft.entity.passive.EntityBat; import net.minecraft.entity.passive.EntityChicken; import net.minecraft.entity.passive.EntityCow; import net.minecraft.entity.passive.EntityMooshroom; import net.minecraft.entity.passive.EntityPig; +import net.minecraft.entity.passive.EntityVillager; import net.minecraft.entity.player.EntityPlayer; import net.minecraft.item.EnumAction; -import net.minecraft.util.EnumHand; -import net.minecraft.util.math.RayTraceResult; -import net.minecraft.util.math.Vec3d; +import net.minecraft.util.MovingObjectPosition; +import net.minecraft.util.Vec3; import net.minecraft.world.World; public class Metamorphosis extends Spell { public Metamorphosis() { - super(Tier.APPRENTICE, 15, Element.NECROMANCY, "metamorphosis", SpellType.UTILITY, 30, EnumAction.NONE, false); + super(EnumTier.APPRENTICE, 15, EnumElement.NECROMANCY, "metamorphosis", EnumSpellType.UTILITY, 30, EnumAction.none, false); } @Override - public boolean cast(World world, EntityPlayer caster, EnumHand hand, int ticksInUse, SpellModifiers modifiers) { + public boolean cast(World world, EntityPlayer caster, int ticksInUse, float damageMultiplier, float rangeMultiplier, float durationMultiplier, float blastMultiplier) { - Vec3d look = caster.getLookVec(); + Vec3 look = caster.getLookVec(); - RayTraceResult rayTrace = WizardryUtilities.standardEntityRayTrace(world, caster, 10*modifiers.get(WizardryItems.range_upgrade)); + MovingObjectPosition rayTrace = WizardryUtilities.standardEntityRayTrace(world, caster, 10*rangeMultiplier); if(rayTrace != null && rayTrace.entityHit != null && rayTrace.entityHit instanceof EntityLivingBase){ @@ -61,11 +58,10 @@ public class Metamorphosis extends Spell { newEntity = new EntityPig(world); } else if(entityHit instanceof EntitySkeleton){ - // IDEA: Interaction with husks/strays? - if(((EntitySkeleton)entityHit).getSkeletonType() == SkeletonType.NORMAL){ - ((EntitySkeleton)entityHit).setSkeletonType(SkeletonType.WITHER); + if(((EntitySkeleton)entityHit).getSkeletonType() == 0){ + ((EntitySkeleton)entityHit).setSkeletonType(1); }else{ - ((EntitySkeleton)entityHit).setSkeletonType(SkeletonType.NORMAL); + ((EntitySkeleton)entityHit).setSkeletonType(0); } flag = true; } @@ -107,21 +103,21 @@ public class Metamorphosis extends Spell { } if(world.isRemote){ - for(int i=1; i<(int)(25*modifiers.get(WizardryItems.range_upgrade)); i+=2){ + for(int i=1; i<(int)(25*rangeMultiplier); i+=2){ // I figured it out! when on client side, entityplayer.posY is at the eyes, not the feet! double x1 = caster.posX + look.xCoord*i/2 + world.rand.nextFloat()/5 - 0.1f; double y1 = WizardryUtilities.getPlayerEyesPos(caster) - 0.4f + look.yCoord*i/2 + world.rand.nextFloat()/5 - 0.1f; double z1 = caster.posZ + look.zCoord*i/2 + world.rand.nextFloat()/5 - 0.1f; //world.spawnParticle("mobSpell", x1, y1, z1, -1*look.xCoord, -1*look.yCoord, -1*look.zCoord); - Wizardry.proxy.spawnParticle(WizardryParticleType.SPARKLE, world, x1, y1, z1, 0.0d, 0.0d, 0.0d, 12 + world.rand.nextInt(8), 0.2f, 0.0f, 0.1f); + Wizardry.proxy.spawnParticle(EnumParticleType.SPARKLE, world, x1, y1, z1, 0.0d, 0.0d, 0.0d, 12 + world.rand.nextInt(8), 0.2f, 0.0f, 0.1f); } for(int i=0;i<5;i++){ - Wizardry.proxy.spawnParticle(WizardryParticleType.DARK_MAGIC, world, xPos, yPos, zPos, 0.0d, 0.0d, 0.0d, 0, 0.1f, 0.0f, 0.0f); + Wizardry.proxy.spawnParticle(EnumParticleType.DARK_MAGIC, world, xPos, yPos, zPos, 0.0d, 0.0d, 0.0d, 0, 0.1f, 0.0f, 0.0f); } } - caster.swingArm(hand); - WizardryUtilities.playSoundAtPlayer(caster, WizardrySounds.SPELL_DEFLECTION, 0.5F, 0.8f); + caster.swingItem(); + world.playSoundAtEntity(caster, "wizardry:effect", 0.5F, 0.8f); return true; } } diff --git a/src/main/java/electroblob/wizardry/spell/Meteor.java b/src/main/java/electroblob/wizardry/spell/Meteor.java index af6c4aa3..2cf1cf8d 100644 --- a/src/main/java/electroblob/wizardry/spell/Meteor.java +++ b/src/main/java/electroblob/wizardry/spell/Meteor.java @@ -1,24 +1,21 @@ package electroblob.wizardry.spell; -import electroblob.wizardry.constants.Element; -import electroblob.wizardry.constants.SpellType; -import electroblob.wizardry.constants.Tier; +import electroblob.wizardry.EnumElement; +import electroblob.wizardry.EnumSpellType; +import electroblob.wizardry.EnumTier; +import electroblob.wizardry.WizardryUtilities; import electroblob.wizardry.entity.EntityMeteor; -import electroblob.wizardry.registry.WizardryItems; -import electroblob.wizardry.registry.WizardrySounds; -import electroblob.wizardry.util.SpellModifiers; -import electroblob.wizardry.util.WizardryUtilities; import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.init.Blocks; import net.minecraft.item.EnumAction; -import net.minecraft.util.EnumHand; -import net.minecraft.util.math.BlockPos; -import net.minecraft.util.math.RayTraceResult; +import net.minecraft.util.MovingObjectPosition; +import net.minecraft.util.MovingObjectPosition.MovingObjectType; import net.minecraft.world.World; public class Meteor extends Spell { public Meteor() { - super(Tier.MASTER, 100, Element.FIRE, "meteor", SpellType.ATTACK, 200, EnumAction.NONE, false); + super(EnumTier.MASTER, 100, EnumElement.FIRE, "meteor", EnumSpellType.ATTACK, 200, EnumAction.none, false); } @Override @@ -27,24 +24,26 @@ public class Meteor extends Spell { } @Override - public boolean cast(World world, EntityPlayer caster, EnumHand hand, int ticksInUse, SpellModifiers modifiers) { + public boolean cast(World world, EntityPlayer caster, int ticksInUse, float damageMultiplier, float rangeMultiplier, float durationMultiplier, float blastMultiplier) { - RayTraceResult rayTrace = WizardryUtilities.rayTrace(40*modifiers.get(WizardryItems.range_upgrade), world, caster, false); + MovingObjectPosition rayTrace = WizardryUtilities.rayTrace(40*rangeMultiplier, world, caster, false); - if(rayTrace != null && rayTrace.typeOfHit == RayTraceResult.Type.BLOCK){ + if(rayTrace != null && rayTrace.typeOfHit == MovingObjectType.BLOCK){ - BlockPos pos = rayTrace.getBlockPos(); + int x = rayTrace.blockX; + int y = rayTrace.blockY; + int z = rayTrace.blockZ; // Not sure why it is +1 but it has to be to work properly. - if(world.canBlockSeeSky(pos.up())){ + if(world.canBlockSeeTheSky(x, y+1, z)){ if(!world.isRemote){ - EntityMeteor meteor = new EntityMeteor(world, pos.getX(), pos.getY() + 50, pos.getZ(), modifiers.get(WizardryItems.blast_upgrade)); + EntityMeteor meteor = new EntityMeteor(world, x, y + 50, z, Blocks.stone, blastMultiplier); world.spawnEntityInWorld(meteor); } - caster.swingArm(hand); - WizardryUtilities.playSoundAtPlayer(caster, WizardrySounds.SPELL_SUMMONING, 3.0f, 1.0f); + caster.swingItem(); + world.playSoundAtEntity(caster, "wizardry:darkaura", 3.0f, 1.0f); return true; } } diff --git a/src/main/java/electroblob/wizardry/spell/MindControl.java b/src/main/java/electroblob/wizardry/spell/MindControl.java index 82e99605..dccb84a7 100644 --- a/src/main/java/electroblob/wizardry/spell/MindControl.java +++ b/src/main/java/electroblob/wizardry/spell/MindControl.java @@ -2,36 +2,28 @@ package electroblob.wizardry.spell; import java.util.List; +import electroblob.wizardry.EnumElement; +import electroblob.wizardry.EnumParticleType; +import electroblob.wizardry.EnumSpellType; +import electroblob.wizardry.EnumTier; import electroblob.wizardry.Wizardry; -import electroblob.wizardry.constants.Element; -import electroblob.wizardry.constants.SpellType; -import electroblob.wizardry.constants.Tier; -import electroblob.wizardry.entity.living.EntityEvilWizard; -import electroblob.wizardry.registry.WizardryItems; -import electroblob.wizardry.registry.WizardryPotions; -import electroblob.wizardry.registry.WizardrySounds; -import electroblob.wizardry.util.SpellModifiers; -import electroblob.wizardry.util.WizardryParticleType; -import electroblob.wizardry.util.WizardryUtilities; +import electroblob.wizardry.WizardryUtilities; +import electroblob.wizardry.entity.living.EntityWizard; import net.minecraft.entity.Entity; +import net.minecraft.entity.EntityCreature; import net.minecraft.entity.EntityLiving; import net.minecraft.entity.EntityLivingBase; import net.minecraft.entity.INpc; import net.minecraft.entity.SharedMonsterAttributes; +import net.minecraft.entity.boss.IBossDisplayData; import net.minecraft.entity.player.EntityPlayer; import net.minecraft.item.EnumAction; import net.minecraft.nbt.NBTTagCompound; import net.minecraft.potion.PotionEffect; -import net.minecraft.util.EnumHand; -import net.minecraft.util.math.RayTraceResult; -import net.minecraft.util.text.TextComponentTranslation; +import net.minecraft.util.ChatComponentTranslation; +import net.minecraft.util.MovingObjectPosition; import net.minecraft.world.World; -import net.minecraftforge.event.entity.living.LivingSetAttackTargetEvent; -import net.minecraftforge.event.entity.living.LivingEvent.LivingUpdateEvent; -import net.minecraftforge.fml.common.Mod; -import net.minecraftforge.fml.common.eventhandler.SubscribeEvent; -@Mod.EventBusSubscriber public class MindControl extends Spell { /** The NBT tag name for storing the controlling entity's UUID in the target's tag compound. Defined here in case @@ -39,101 +31,102 @@ public class MindControl extends Spell { public static final String NBT_KEY = "controllingEntity"; public MindControl() { - super(Tier.ADVANCED, 40, Element.NECROMANCY, "mind_control", SpellType.ATTACK, 150, EnumAction.NONE, false); + super(EnumTier.ADVANCED, 40, EnumElement.NECROMANCY, "mind_control", EnumSpellType.ATTACK, 150, EnumAction.none, false); } @Override - public boolean cast(World world, EntityPlayer caster, EnumHand hand, int ticksInUse, SpellModifiers modifiers) { + public boolean cast(World world, EntityPlayer caster, int ticksInUse, float damageMultiplier, float rangeMultiplier, float durationMultiplier, float blastMultiplier) { - RayTraceResult rayTrace = WizardryUtilities.standardEntityRayTrace(world, caster, 8*modifiers.get(WizardryItems.range_upgrade)); + MovingObjectPosition rayTrace = WizardryUtilities.standardEntityRayTrace(world, caster, 8*rangeMultiplier); if(rayTrace != null && rayTrace.entityHit != null && rayTrace.entityHit instanceof EntityLivingBase){ - EntityLivingBase target = (EntityLivingBase)rayTrace.entityHit; + Entity target = rayTrace.entityHit; if(!world.isRemote){ - if(!canControl(target)){ + if(target instanceof EntityPlayer || target instanceof IBossDisplayData || target instanceof INpc){ // Adds a message saying that the player/boss entity/wizard resisted mind control - if(!world.isRemote) caster.addChatComponentMessage(new TextComponentTranslation("spell.resist", target.getName(), this.getNameForTranslationFormatted())); + if(!world.isRemote) caster.addChatComponentMessage(new ChatComponentTranslation("spell.resist", target.getCommandSenderName(), this.getDisplayNameWithFormatting())); }else if(target instanceof EntityLiving){ if(!MindControl.findMindControlTarget((EntityLiving)target, caster, world)){ // If no valid target was found, this just acts like mind trick. + // New AI ((EntityLiving)target).setAttackTarget(null); + // Old AI + if(target instanceof EntityCreature) ((EntityCreature)target).setTarget(null); } NBTTagCompound entityNBT = target.getEntityData(); - if(entityNBT != null) entityNBT.setUniqueId(NBT_KEY, caster.getUniqueID()); + if(entityNBT != null) entityNBT.setString(NBT_KEY, caster.getUniqueID().toString()); - ((EntityLiving)target).addPotionEffect(new PotionEffect(WizardryPotions.mind_control, (int)(600*modifiers.get(WizardryItems.duration_upgrade)), 0)); + ((EntityLiving)target).addPotionEffect(new PotionEffect(Wizardry.mindControl.id, (int)(600*durationMultiplier), 0)); } }else{ for(int i=0; i<10; i++){ - Wizardry.proxy.spawnParticle(WizardryParticleType.DARK_MAGIC, world, target.posX - 0.25 + world.rand.nextDouble()*0.5, - target.getEntityBoundingBox().minY + target.getEyeHeight() - 0.25 + world.rand.nextDouble()*0.5, + Wizardry.proxy.spawnParticle(EnumParticleType.DARK_MAGIC, world, target.posX - 0.25 + world.rand.nextDouble()*0.5, + WizardryUtilities.getEntityFeetPos(target) + target.getEyeHeight() - 0.25 + world.rand.nextDouble()*0.5, target.posZ - 0.25 + world.rand.nextDouble()*0.5, 0, 0, 0, 0, 0.8f, 0.2f, 1.0f); - Wizardry.proxy.spawnParticle(WizardryParticleType.DARK_MAGIC, world, target.posX - 0.25 + world.rand.nextDouble()*0.5, - target.getEntityBoundingBox().minY + target.getEyeHeight() - 0.25 + world.rand.nextDouble()*0.5, + Wizardry.proxy.spawnParticle(EnumParticleType.DARK_MAGIC, world, target.posX - 0.25 + world.rand.nextDouble()*0.5, + WizardryUtilities.getEntityFeetPos(target) + target.getEyeHeight() - 0.25 + world.rand.nextDouble()*0.5, target.posZ - 0.25 + world.rand.nextDouble()*0.5, 0, 0, 0, 0, 0.2f, 0.04f, 0.25f); } } - target.playSound(WizardrySounds.SPELL_SUMMONING, 1.0f, 1.0f); - caster.swingArm(hand); + world.playSoundAtEntity(target, "wizardry:darkaura", 1.0f, 1.0f); + caster.swingItem(); return true; } return false; } @Override - public boolean cast(World world, EntityLiving caster, EnumHand hand, int ticksInUse, EntityLivingBase target, SpellModifiers modifiers) { + public boolean cast(World world, EntityLiving caster, EntityLivingBase target, float damageMultiplier, float rangeMultiplier, float durationMultiplier, float blastMultiplier) { if(target != null){ if(!world.isRemote){ - if(canControl(target)){ + if(target instanceof EntityLiving && !(target instanceof IBossDisplayData) + && !(target instanceof EntityWizard)){ if(!MindControl.findMindControlTarget((EntityLiving)target, caster, world)){ // If no valid target was found, this just acts like mind trick. + // New AI ((EntityLiving)target).setAttackTarget(null); + // Old AI + if(target instanceof EntityCreature) ((EntityCreature)target).setTarget(null); } NBTTagCompound entityNBT = target.getEntityData(); - if(entityNBT != null) entityNBT.setUniqueId(NBT_KEY, caster.getUniqueID()); + if(entityNBT != null) entityNBT.setString(NBT_KEY, caster.getUniqueID().toString()); - ((EntityLiving)target).addPotionEffect(new PotionEffect(WizardryPotions.mind_control, (int)(600*modifiers.get(WizardryItems.duration_upgrade)), 0)); + ((EntityLiving)target).addPotionEffect(new PotionEffect(Wizardry.mindControl.id, (int)(600*durationMultiplier), 0)); } }else{ for(int i=0; i<10; i++){ - Wizardry.proxy.spawnParticle(WizardryParticleType.DARK_MAGIC, world, target.posX - 0.25 + world.rand.nextDouble()*0.5, - target.getEntityBoundingBox().minY + target.getEyeHeight() - 0.25 + world.rand.nextDouble()*0.5, + Wizardry.proxy.spawnParticle(EnumParticleType.DARK_MAGIC, world, target.posX - 0.25 + world.rand.nextDouble()*0.5, + WizardryUtilities.getEntityFeetPos(target) + target.getEyeHeight() - 0.25 + world.rand.nextDouble()*0.5, target.posZ - 0.25 + world.rand.nextDouble()*0.5, 0, 0, 0, 0, 0.8f, 0.2f, 1.0f); - Wizardry.proxy.spawnParticle(WizardryParticleType.DARK_MAGIC, world, target.posX - 0.25 + world.rand.nextDouble()*0.5, - target.getEntityBoundingBox().minY + target.getEyeHeight() - 0.25 + world.rand.nextDouble()*0.5, + Wizardry.proxy.spawnParticle(EnumParticleType.DARK_MAGIC, world, target.posX - 0.25 + world.rand.nextDouble()*0.5, + WizardryUtilities.getEntityFeetPos(target) + target.getEyeHeight() - 0.25 + world.rand.nextDouble()*0.5, target.posZ - 0.25 + world.rand.nextDouble()*0.5, 0, 0, 0, 0, 0.2f, 0.04f, 0.25f); } } - target.playSound(WizardrySounds.SPELL_SUMMONING, 1.0f, 1.0f); - caster.swingArm(hand); + world.playSoundAtEntity(target, "wizardry:darkaura", 1.0f, 1.0f); + caster.swingItem(); return true; } return false; } - + @Override public boolean canBeCastByNPCs(){ return true; } - /** Returns true if the given entity can be mind controlled (i.e. is not a player, npc, evil wizard or boss). */ - public static boolean canControl(EntityLivingBase target){ - return target instanceof EntityLiving && target.isNonBoss() && !(target instanceof INpc) - && !(target instanceof EntityEvilWizard); - } - /** * Finds the nearest creature to the given target which it is allowed to attack according to the given caster * and sets it as the target's attack target. Handles both new and old AI and takes follow range into account. @@ -149,7 +142,7 @@ public class MindControl extends Spell { // no longer lasts until the creature dies; instead it is a potion effect which continues to // set the target until it wears off. List possibleTargets = WizardryUtilities.getEntitiesWithinRadius( - ((EntityLiving)target).getEntityAttribute(SharedMonsterAttributes.FOLLOW_RANGE).getAttributeValue(), + ((EntityLiving)target).getEntityAttribute(SharedMonsterAttributes.followRange).getAttributeValue(), target.posX, target.posY, target.posZ, world); possibleTargets.remove(target); @@ -164,7 +157,9 @@ public class MindControl extends Spell { } if(newAITarget != null){ - // From 1.7.10 - this seems not to work quite right; the entity appears to continue attacking this target + // Old AI + if(target instanceof EntityCreature) ((EntityCreature)target).setTarget(newAITarget); + // New AI - this seems not to work quite right; the entity appears to continue attacking this target // after it gets killed (not noticeable in survival since it will target the player again immediately.) ((EntityLiving)target).setAttackTarget(newAITarget); @@ -175,43 +170,5 @@ public class MindControl extends Spell { } - @SubscribeEvent - public static void onLivingUpdateEvent(LivingUpdateEvent event){ - // This was added because something got changed in the AI classes which means LivingSetAttackTargetEvent doesn't - // get fired when I want it to... so I'm firing it myself. - if(event.getEntityLiving().isPotionActive(WizardryPotions.mind_control) && event.getEntityLiving() instanceof EntityLiving - && ((EntityLiving)event.getEntityLiving()).getAttackTarget() != null - && !((EntityLiving)event.getEntityLiving()).getAttackTarget().isEntityAlive()) - ((EntityLiving)event.getEntityLiving()).setAttackTarget(null); // Causes the event to be fired - } - @SubscribeEvent - public static void onLivingSetAttackTargetEvent(LivingSetAttackTargetEvent event){ - - if(event.getTarget() == null) return; // Prevents infinite loops with mind trick - - if(event.getEntityLiving().isPotionActive(WizardryPotions.mind_control) && MindControl.canControl(event.getEntityLiving())){ - - NBTTagCompound entityNBT = event.getEntityLiving().getEntityData(); - - if(entityNBT != null && entityNBT.hasKey(MindControl.NBT_KEY + "Most")){ - - Entity caster = WizardryUtilities.getEntityByUUID(event.getEntity().worldObj, entityNBT.getUniqueId(MindControl.NBT_KEY)); - - // If the target that the event tried to set is already a valid mind control target, nothing happens. - if(WizardryUtilities.isValidTarget(caster, event.getTarget())) return; - - if(caster instanceof EntityLivingBase){ - - if(MindControl.findMindControlTarget((EntityLiving)event.getEntityLiving(), (EntityLivingBase)caster, event.getEntity().worldObj)){ - // If it worked, skip setting the target to null. - return; - } - } - } - // If the caster couldn't be found or no valid target was found, this just acts like mind trick. - ((EntityLiving)event.getEntityLiving()).setAttackTarget(null); - } - } - -} \ No newline at end of file +} diff --git a/src/main/java/electroblob/wizardry/spell/MindTrick.java b/src/main/java/electroblob/wizardry/spell/MindTrick.java index d9cab649..2edd394d 100644 --- a/src/main/java/electroblob/wizardry/spell/MindTrick.java +++ b/src/main/java/electroblob/wizardry/spell/MindTrick.java @@ -1,127 +1,94 @@ package electroblob.wizardry.spell; +import electroblob.wizardry.EnumElement; +import electroblob.wizardry.EnumParticleType; +import electroblob.wizardry.EnumSpellType; +import electroblob.wizardry.EnumTier; import electroblob.wizardry.Wizardry; -import electroblob.wizardry.constants.Element; -import electroblob.wizardry.constants.SpellType; -import electroblob.wizardry.constants.Tier; -import electroblob.wizardry.registry.WizardryItems; -import electroblob.wizardry.registry.WizardryPotions; -import electroblob.wizardry.registry.WizardrySounds; -import electroblob.wizardry.util.SpellModifiers; -import electroblob.wizardry.util.WizardryParticleType; -import electroblob.wizardry.util.WizardryUtilities; +import electroblob.wizardry.WizardryUtilities; +import net.minecraft.entity.EntityCreature; import net.minecraft.entity.EntityLiving; import net.minecraft.entity.EntityLivingBase; import net.minecraft.entity.player.EntityPlayer; -import net.minecraft.init.MobEffects; import net.minecraft.item.EnumAction; +import net.minecraft.potion.Potion; import net.minecraft.potion.PotionEffect; -import net.minecraft.util.EnumHand; -import net.minecraft.util.math.RayTraceResult; +import net.minecraft.util.MovingObjectPosition; import net.minecraft.world.World; -import net.minecraftforge.event.entity.living.LivingAttackEvent; -import net.minecraftforge.event.entity.living.LivingSetAttackTargetEvent; -import net.minecraftforge.fml.common.Mod; -import net.minecraftforge.fml.common.eventhandler.SubscribeEvent; -@Mod.EventBusSubscriber public class MindTrick extends Spell { public MindTrick() { - super(Tier.BASIC, 10, Element.NECROMANCY, "mind_trick", SpellType.ATTACK, 40, EnumAction.NONE, false); + super(EnumTier.BASIC, 10, EnumElement.NECROMANCY, "mind_trick", EnumSpellType.ATTACK, 40, EnumAction.none, false); } @Override - public boolean cast(World world, EntityPlayer caster, EnumHand hand, int ticksInUse, SpellModifiers modifiers) { + public boolean cast(World world, EntityPlayer caster, int ticksInUse, float damageMultiplier, float rangeMultiplier, float durationMultiplier, float blastMultiplier) { - RayTraceResult rayTrace = WizardryUtilities.standardEntityRayTrace(world, caster, 8*modifiers.get(WizardryItems.range_upgrade)); + MovingObjectPosition rayTrace = WizardryUtilities.standardEntityRayTrace(world, caster, 8*rangeMultiplier); if(rayTrace != null && rayTrace.entityHit != null && rayTrace.entityHit instanceof EntityLivingBase){ EntityLivingBase target = (EntityLivingBase)rayTrace.entityHit; if(!world.isRemote){ - if(target instanceof EntityPlayer){ - - target.addPotionEffect(new PotionEffect(MobEffects.NAUSEA, (int)(300*modifiers.get(WizardryItems.duration_upgrade)), 0)); - + target.addPotionEffect(new PotionEffect(Potion.confusion.id, (int)(300*durationMultiplier), 0)); }else if(target instanceof EntityLiving){ - + // New AI ((EntityLiving)target).setAttackTarget(null); - target.addPotionEffect(new PotionEffect(WizardryPotions.mind_trick, (int)(300*modifiers.get(WizardryItems.duration_upgrade)), 0)); + // Old AI + if(target instanceof EntityCreature) ((EntityCreature)target).setTarget(null); + + target.addPotionEffect(new PotionEffect(Wizardry.mindTrick.id, (int)(300*durationMultiplier), 0)); } }else{ for(int i=0; i<10; i++){ - Wizardry.proxy.spawnParticle(WizardryParticleType.DARK_MAGIC, world, target.posX - 0.25 + world.rand.nextDouble()*0.5, - target.getEntityBoundingBox().minY + target.getEyeHeight() - 0.25 + world.rand.nextDouble()*0.5, + Wizardry.proxy.spawnParticle(EnumParticleType.DARK_MAGIC, world, target.posX - 0.25 + world.rand.nextDouble()*0.5, + WizardryUtilities.getEntityFeetPos(target) + target.getEyeHeight() - 0.25 + world.rand.nextDouble()*0.5, target.posZ - 0.25 + world.rand.nextDouble()*0.5, 0, 0, 0, 0, 0.8f, 0.2f, 1.0f); } } - - target.playSound(WizardrySounds.SPELL_DEFLECTION, 0.7F, world.rand.nextFloat() * 0.4F + 0.8F); - caster.swingArm(hand); + world.playSoundAtEntity(target, "wizardry:effect", 0.7F, world.rand.nextFloat() * 0.4F + 0.8F); + caster.swingItem(); return true; } return false; } @Override - public boolean cast(World world, EntityLiving caster, EnumHand hand, int ticksInUse, EntityLivingBase target, SpellModifiers modifiers) { + public boolean cast(World world, EntityLiving caster, EntityLivingBase target, float damageMultiplier, float rangeMultiplier, float durationMultiplier, float blastMultiplier) { if(target != null){ if(!world.isRemote){ if(target instanceof EntityPlayer){ - - target.addPotionEffect(new PotionEffect(MobEffects.NAUSEA, (int)(300*modifiers.get(WizardryItems.duration_upgrade)), 0)); - + target.addPotionEffect(new PotionEffect(Potion.confusion.id, (int)(300*durationMultiplier), 0)); }else if(target instanceof EntityLiving){ - + // New AI ((EntityLiving)target).setAttackTarget(null); - target.addPotionEffect(new PotionEffect(WizardryPotions.mind_trick, (int)(300*modifiers.get(WizardryItems.duration_upgrade)), 0)); + // Old AI + if(target instanceof EntityCreature) ((EntityCreature)target).setTarget(null); + target.addPotionEffect(new PotionEffect(Wizardry.mindTrick.id, (int)(300*durationMultiplier), 0)); } }else{ for(int i=0; i<10; i++){ - Wizardry.proxy.spawnParticle(WizardryParticleType.DARK_MAGIC, world, target.posX - 0.25 + world.rand.nextDouble()*0.5, - target.getEntityBoundingBox().minY + target.getEyeHeight() - 0.25 + world.rand.nextDouble()*0.5, + Wizardry.proxy.spawnParticle(EnumParticleType.DARK_MAGIC, world, target.posX - 0.25 + world.rand.nextDouble()*0.5, + WizardryUtilities.getEntityFeetPos(target) + target.getEyeHeight() - 0.25 + world.rand.nextDouble()*0.5, target.posZ - 0.25 + world.rand.nextDouble()*0.5, 0, 0, 0, 0, 0.8f, 0.2f, 1.0f); } } - - target.playSound(WizardrySounds.SPELL_DEFLECTION, 0.7F, world.rand.nextFloat() * 0.4F + 0.8F); - caster.swingArm(hand); + world.playSoundAtEntity(target, "wizardry:effect", 0.7F, world.rand.nextFloat() * 0.4F + 0.8F); + caster.swingItem(); return true; } return false; } - + @Override public boolean canBeCastByNPCs() { return true; } - - @SubscribeEvent - public static void onLivingAttackEvent(LivingAttackEvent event){ - if(event.getSource() != null && event.getSource().getEntity() instanceof EntityLivingBase){ - // Cancels the mind trick effect if the creature takes damage - // This has been moved to within an (event.getSource().getEntity() instanceof EntityLivingBase) check so it doesn't - // crash the game with a ConcurrentModificationException. If you think about it, mind trick only ought to be - // cancelled if something attacks the entity since potions, drowning, cacti etc. don't affect the targeting. - if(event.getEntityLiving().isPotionActive(WizardryPotions.mind_trick)){ - event.getEntityLiving().removePotionEffect(WizardryPotions.mind_trick); - } - } - } - - @SubscribeEvent - public static void onLivingSetAttackTargetEvent(LivingSetAttackTargetEvent event){ - // Mind trick - // If the target is null already, no need to set it to null, or infinite loops will occur. - if((event.getEntityLiving().isPotionActive(WizardryPotions.mind_trick) || event.getEntityLiving().isPotionActive(WizardryPotions.fear)) && event.getEntityLiving() instanceof EntityLiving && event.getTarget() != null){ - ((EntityLiving)event.getEntityLiving()).setAttackTarget(null); - } - } } diff --git a/src/main/java/electroblob/wizardry/spell/None.java b/src/main/java/electroblob/wizardry/spell/None.java index 12566e04..e0978188 100644 --- a/src/main/java/electroblob/wizardry/spell/None.java +++ b/src/main/java/electroblob/wizardry/spell/None.java @@ -1,21 +1,18 @@ package electroblob.wizardry.spell; -import electroblob.wizardry.constants.Element; -import electroblob.wizardry.constants.SpellType; -import electroblob.wizardry.constants.Tier; -import electroblob.wizardry.util.SpellModifiers; +import electroblob.wizardry.EnumElement; +import electroblob.wizardry.EnumSpellType; +import electroblob.wizardry.EnumTier; import net.minecraft.entity.player.EntityPlayer; import net.minecraft.item.EnumAction; -import net.minecraft.util.EnumHand; import net.minecraft.world.World; /** This class represents a blank spell used to fill empty slots on wands. It is unobtainable in-game, except via - * commands, and does nothing when the player attempts to cast it. Its instance can be referenced directly using - * {@link electroblob.wizardry.registry.Spells#none WizardryRegistry.none}*/ + * commands, and does nothing when the player attempts to cast it. */ public class None extends Spell { public None() { - super(Tier.BASIC, 0, Element.MAGIC, "none", SpellType.UTILITY, 0, EnumAction.NONE, false); + super(EnumTier.BASIC, 0, EnumElement.MAGIC, "none", EnumSpellType.UTILITY, 0, EnumAction.none, false); } @Override @@ -24,7 +21,7 @@ public class None extends Spell { } @Override - public boolean cast(World world, EntityPlayer caster, EnumHand hand, int ticksInUse, SpellModifiers modifiers) { + public boolean cast(World world, EntityPlayer caster, int ticksInUse, float damageMultiplier, float rangeMultiplier, float durationMultiplier, float blastMultiplier) { return false; } diff --git a/src/main/java/electroblob/wizardry/spell/Oakflesh.java b/src/main/java/electroblob/wizardry/spell/Oakflesh.java index cb2f5fd6..53eec40b 100644 --- a/src/main/java/electroblob/wizardry/spell/Oakflesh.java +++ b/src/main/java/electroblob/wizardry/spell/Oakflesh.java @@ -1,64 +1,60 @@ package electroblob.wizardry.spell; +import electroblob.wizardry.EnumElement; +import electroblob.wizardry.EnumParticleType; +import electroblob.wizardry.EnumSpellType; +import electroblob.wizardry.EnumTier; import electroblob.wizardry.Wizardry; -import electroblob.wizardry.constants.Element; -import electroblob.wizardry.constants.SpellType; -import electroblob.wizardry.constants.Tier; -import electroblob.wizardry.registry.WizardryItems; -import electroblob.wizardry.registry.WizardrySounds; -import electroblob.wizardry.util.SpellModifiers; -import electroblob.wizardry.util.WizardryParticleType; -import electroblob.wizardry.util.WizardryUtilities; +import electroblob.wizardry.WizardryUtilities; import net.minecraft.entity.EntityLiving; import net.minecraft.entity.EntityLivingBase; import net.minecraft.entity.player.EntityPlayer; -import net.minecraft.init.MobEffects; import net.minecraft.item.EnumAction; +import net.minecraft.potion.Potion; import net.minecraft.potion.PotionEffect; -import net.minecraft.util.EnumHand; import net.minecraft.world.World; public class Oakflesh extends Spell { public Oakflesh() { - super(Tier.APPRENTICE, 20, Element.HEALING, "oakflesh", SpellType.DEFENCE, 50, EnumAction.BOW, false); + super(EnumTier.APPRENTICE, 20, EnumElement.HEALING, "oakflesh", EnumSpellType.DEFENCE, 50, EnumAction.bow, false); } @Override - public boolean cast(World world, EntityPlayer caster, EnumHand hand, int ticksInUse, SpellModifiers modifiers) { + public boolean cast(World world, EntityPlayer caster, int ticksInUse, float damageMultiplier, float rangeMultiplier, float durationMultiplier, float blastMultiplier) { - caster.addPotionEffect(new PotionEffect(MobEffects.RESISTANCE, (int)(600*modifiers.get(WizardryItems.duration_upgrade)), 1, false, false)); + caster.addPotionEffect(new PotionEffect(Potion.resistance.id, (int)(600*durationMultiplier), 1, true)); if(world.isRemote){ for(int i=0; i<10; i++){ double x1 = (double)((float)caster.posX + world.rand.nextFloat()*2 - 1.0F); double y1 = (double)((float)WizardryUtilities.getPlayerEyesPos(caster) - 0.5F + world.rand.nextFloat()); double z1 = (double)((float)caster.posZ + world.rand.nextFloat()*2 - 1.0F); - Wizardry.proxy.spawnParticle(WizardryParticleType.SPARKLE, world, x1, y1, z1, 0, 0.1F, 0, 48 + world.rand.nextInt(12), 0.6f, 0.5f, 0.4f); + Wizardry.proxy.spawnParticle(EnumParticleType.SPARKLE, world, x1, y1, z1, 0, 0.1F, 0, 48 + world.rand.nextInt(12), 0.6f, 0.5f, 0.4f); } } - WizardryUtilities.playSoundAtPlayer(caster, WizardrySounds.SPELL_HEAL, 0.7F, world.rand.nextFloat() * 0.4F + 1.0F); + world.playSoundAtEntity(caster, "wizardry:heal", 0.7F, world.rand.nextFloat() * 0.4F + 1.0F); return true; } @Override - public boolean cast(World world, EntityLiving caster, EnumHand hand, int ticksInUse, EntityLivingBase target, SpellModifiers modifiers){ + public boolean cast(World world, EntityLiving caster, EntityLivingBase target, float damageMultiplier, float rangeMultiplier, float durationMultiplier, float blastMultiplier){ - if(!caster.isPotionActive(MobEffects.RESISTANCE)){ + if(!caster.isPotionActive(Potion.resistance)){ - caster.addPotionEffect(new PotionEffect(MobEffects.RESISTANCE, (int)(600*modifiers.get(WizardryItems.duration_upgrade)), 1, false, false)); + caster.addPotionEffect(new PotionEffect(Potion.resistance.id, (int)(600*durationMultiplier), 1, true)); if(world.isRemote){ for(int i=0; i<10; i++){ double x1 = (double)((float)caster.posX + world.rand.nextFloat()*2 - 1.0F); double y1 = (double)((float)caster.posY + caster.getEyeHeight() - 0.5F + world.rand.nextFloat()); double z1 = (double)((float)caster.posZ + world.rand.nextFloat()*2 - 1.0F); - Wizardry.proxy.spawnParticle(WizardryParticleType.SPARKLE, world, x1, y1, z1, 0, 0.1F, 0, 48 + world.rand.nextInt(12), 0.6f, 0.5f, 0.4f); + Wizardry.proxy.spawnParticle(EnumParticleType.SPARKLE, world, x1, y1, z1, 0, 0.1F, 0, 48 + world.rand.nextInt(12), 0.6f, 0.5f, 0.4f); } } - caster.playSound(WizardrySounds.SPELL_HEAL, 0.7F, world.rand.nextFloat() * 0.4F + 1.0F); + world.playSoundAtEntity(caster, "wizardry:heal", 0.7F, world.rand.nextFloat() * 0.4F + 1.0F); return true; } diff --git a/src/main/java/electroblob/wizardry/spell/Petrify.java b/src/main/java/electroblob/wizardry/spell/Petrify.java index 4dd4cf24..694896f1 100644 --- a/src/main/java/electroblob/wizardry/spell/Petrify.java +++ b/src/main/java/electroblob/wizardry/spell/Petrify.java @@ -1,112 +1,101 @@ package electroblob.wizardry.spell; +import electroblob.wizardry.EnumElement; +import electroblob.wizardry.EnumParticleType; +import electroblob.wizardry.EnumSpellType; +import electroblob.wizardry.EnumTier; import electroblob.wizardry.Wizardry; -import electroblob.wizardry.constants.Element; -import electroblob.wizardry.constants.SpellType; -import electroblob.wizardry.constants.Tier; -import electroblob.wizardry.registry.WizardryBlocks; -import electroblob.wizardry.registry.WizardryItems; +import electroblob.wizardry.WizardryUtilities; import electroblob.wizardry.tileentity.TileEntityStatue; -import electroblob.wizardry.util.SpellModifiers; -import electroblob.wizardry.util.WizardryParticleType; -import electroblob.wizardry.util.WizardryUtilities; import net.minecraft.entity.EntityLiving; import net.minecraft.entity.player.EntityPlayer; -import net.minecraft.init.SoundEvents; import net.minecraft.item.EnumAction; -import net.minecraft.util.EnumHand; -import net.minecraft.util.math.BlockPos; -import net.minecraft.util.math.RayTraceResult; -import net.minecraft.util.math.Vec3d; +import net.minecraft.util.MovingObjectPosition; +import net.minecraft.util.MovingObjectPosition.MovingObjectType; +import net.minecraft.util.Vec3; import net.minecraft.world.World; public class Petrify extends Spell { private static final int baseDuration = 900; - - /** The NBT tag name for storing the petrified flag in the target's tag compound. Defined here in case it changes. */ - public static final String NBT_KEY = "petrified"; public Petrify() { - super(Tier.ADVANCED, 40, Element.SORCERY, "petrify", SpellType.ATTACK, 100, EnumAction.NONE, false); + super(EnumTier.ADVANCED, 40, EnumElement.SORCERY, "petrify", EnumSpellType.ATTACK, 100, EnumAction.none, false); } @Override - public boolean cast(World world, EntityPlayer caster, EnumHand hand, int ticksInUse, SpellModifiers modifiers) { + public boolean cast(World world, EntityPlayer caster, int ticksInUse, float damageMultiplier, float rangeMultiplier, float durationMultiplier, float blastMultiplier) { - Vec3d look = caster.getLookVec(); + Vec3 look = caster.getLookVec(); - RayTraceResult rayTrace = WizardryUtilities.standardEntityRayTrace(world, caster, 10*modifiers.get(WizardryItems.range_upgrade)); + MovingObjectPosition rayTrace = WizardryUtilities.standardEntityRayTrace(world, caster, 10*rangeMultiplier); - if(rayTrace != null && rayTrace.typeOfHit == RayTraceResult.Type.ENTITY && rayTrace.entityHit instanceof EntityLiving && !world.isRemote){ + if(rayTrace != null && rayTrace.typeOfHit == MovingObjectType.ENTITY && rayTrace.entityHit instanceof EntityLiving && !world.isRemote){ - EntityLiving target = (EntityLiving) rayTrace.entityHit; - - if(target.deathTime > 0) return false; + EntityLiving entity = (EntityLiving) rayTrace.entityHit; - BlockPos pos = new BlockPos(target); + int x = (int)Math.floor(entity.posX); + int y = (int)Math.floor(entity.posY); + int z = (int)Math.floor(entity.posZ); - target.extinguish(); + entity.extinguish(); //Short mobs such as spiders and pigs - if((target.height < 1.2 || target.isChild()) && WizardryUtilities.canBlockBeReplaced(world, pos)){ - world.setBlockState(pos, WizardryBlocks.petrified_stone.getDefaultState()); - if(world.getTileEntity(pos) instanceof TileEntityStatue){ - ((TileEntityStatue)world.getTileEntity(pos)).setCreatureAndPart(target, 1, 1); - ((TileEntityStatue)world.getTileEntity(pos)).setLifetime((int)(baseDuration*modifiers.get(WizardryItems.duration_upgrade))); + if((entity.height < 1.2 || entity.isChild()) && WizardryUtilities.canBlockBeReplaced(world, x, y, z)){ + world.setBlock(x, y, z, Wizardry.petrifiedStone); + if(world.getTileEntity(x, y, z) instanceof TileEntityStatue){ + ((TileEntityStatue)world.getTileEntity(x, y, z)).setCreatureAndPart(entity, 1, 1); + ((TileEntityStatue)world.getTileEntity(x, y, z)).setLifetime((int)(baseDuration*durationMultiplier)); } - target.getEntityData().setBoolean(NBT_KEY, true); - target.setDead(); + entity.setDead(); } //Normal sized mobs like zombies and skeletons - else if(target.height < 2.5 && WizardryUtilities.canBlockBeReplaced(world, pos) && WizardryUtilities.canBlockBeReplaced(world, pos.up())){ - world.setBlockState(pos, WizardryBlocks.petrified_stone.getDefaultState()); - if(world.getTileEntity(pos) instanceof TileEntityStatue){ - ((TileEntityStatue)world.getTileEntity(pos)).setCreatureAndPart(target, 1, 2); - ((TileEntityStatue)world.getTileEntity(pos)).setLifetime((int)(baseDuration*modifiers.get(WizardryItems.duration_upgrade))); + else if(entity.height < 2.5 && WizardryUtilities.canBlockBeReplaced(world, x, y, z) && WizardryUtilities.canBlockBeReplaced(world, x, y+1, z)){ + world.setBlock(x, y, z, Wizardry.petrifiedStone); + if(world.getTileEntity(x, y, z) instanceof TileEntityStatue){ + ((TileEntityStatue)world.getTileEntity(x, y, z)).setCreatureAndPart(entity, 1, 2); + ((TileEntityStatue)world.getTileEntity(x, y, z)).setLifetime((int)(baseDuration*durationMultiplier)); } - world.setBlockState(pos.up(), WizardryBlocks.petrified_stone.getDefaultState()); - if(world.getTileEntity(pos.up()) instanceof TileEntityStatue){ - ((TileEntityStatue)world.getTileEntity(pos.up())).setCreatureAndPart(target, 2, 2); + world.setBlock(x, y+1, z, Wizardry.petrifiedStone); + if(world.getTileEntity(x, y+1, z) instanceof TileEntityStatue){ + ((TileEntityStatue)world.getTileEntity(x, y+1, z)).setCreatureAndPart(entity, 2, 2); } - target.getEntityData().setBoolean(NBT_KEY, true); - target.setDead(); + entity.setDead(); } //Tall mobs like endermen - else if(WizardryUtilities.canBlockBeReplaced(world, pos) && WizardryUtilities.canBlockBeReplaced(world, pos.up()) && WizardryUtilities.canBlockBeReplaced(world, pos.up(2))){ - world.setBlockState(pos, WizardryBlocks.petrified_stone.getDefaultState()); - if(world.getTileEntity(pos) instanceof TileEntityStatue){ - ((TileEntityStatue)world.getTileEntity(pos)).setCreatureAndPart(target, 1, 3); - ((TileEntityStatue)world.getTileEntity(pos)).setLifetime((int)(baseDuration*modifiers.get(WizardryItems.duration_upgrade))); + else if(WizardryUtilities.canBlockBeReplaced(world, x, y, z) && WizardryUtilities.canBlockBeReplaced(world, x, y+1, z) && WizardryUtilities.canBlockBeReplaced(world, x, y+2, z)){ + world.setBlock(x, y, z, Wizardry.petrifiedStone); + if(world.getTileEntity(x, y, z) instanceof TileEntityStatue){ + ((TileEntityStatue)world.getTileEntity(x, y, z)).setCreatureAndPart(entity, 1, 3); + ((TileEntityStatue)world.getTileEntity(x, y, z)).setLifetime((int)(baseDuration*durationMultiplier)); } - world.setBlockState(pos.up(), WizardryBlocks.petrified_stone.getDefaultState()); - if(world.getTileEntity(pos.up()) instanceof TileEntityStatue){ - ((TileEntityStatue)world.getTileEntity(pos.up())).setCreatureAndPart(target, 2, 3); + world.setBlock(x, y+1, z, Wizardry.petrifiedStone); + if(world.getTileEntity(x, y+1, z) instanceof TileEntityStatue){ + ((TileEntityStatue)world.getTileEntity(x, y+1, z)).setCreatureAndPart(entity, 2, 3); } - world.setBlockState(pos.up(2), WizardryBlocks.petrified_stone.getDefaultState()); - if(world.getTileEntity(pos.up(2)) instanceof TileEntityStatue){ - ((TileEntityStatue)world.getTileEntity(pos.up(2))).setCreatureAndPart(target, 3, 3); + world.setBlock(x, y+2, z, Wizardry.petrifiedStone); + if(world.getTileEntity(x, y+2, z) instanceof TileEntityStatue){ + ((TileEntityStatue)world.getTileEntity(x, y+2, z)).setCreatureAndPart(entity, 3, 3); } - target.getEntityData().setBoolean(NBT_KEY, true); - target.setDead(); + entity.setDead(); } } if(world.isRemote){ - for(int i=1; i<(int)(25*modifiers.get(WizardryItems.range_upgrade)); i+=2){ + for(int i=1; i<(int)(25*rangeMultiplier); i+=2){ // I figured it out! when on client side, entityplayer.posY is at the eyes, not the feet! double x1 = caster.posX + look.xCoord*i/2 + world.rand.nextFloat()/5 - 0.1f; double y1 = WizardryUtilities.getPlayerEyesPos(caster) - 0.4f + look.yCoord*i/2 + world.rand.nextFloat()/5 - 0.1f; double z1 = caster.posZ + look.zCoord*i/2 + world.rand.nextFloat()/5 - 0.1f; //world.spawnParticle("mobSpell", x1, y1, z1, -1*look.xCoord, -1*look.yCoord, -1*look.zCoord); - Wizardry.proxy.spawnParticle(WizardryParticleType.DARK_MAGIC, world, x1, y1, z1, 0.0d, 0.0d, 0.0d, 0, 0.1f, 0.1f, 0.1f); - Wizardry.proxy.spawnParticle(WizardryParticleType.SPARKLE, world, x1, y1, z1, 0.0d, 0.0d, 0.0d, 12 + world.rand.nextInt(8), 0.2f, 0.2f, 0.2f); + Wizardry.proxy.spawnParticle(EnumParticleType.DARK_MAGIC, world, x1, y1, z1, 0.0d, 0.0d, 0.0d, 0, 0.1f, 0.1f, 0.1f); + Wizardry.proxy.spawnParticle(EnumParticleType.SPARKLE, world, x1, y1, z1, 0.0d, 0.0d, 0.0d, 12 + world.rand.nextInt(8), 0.2f, 0.2f, 0.2f); } } - caster.swingArm(hand); - WizardryUtilities.playSoundAtPlayer(caster, SoundEvents.ENTITY_WITHER_SPAWN, 1.0F, world.rand.nextFloat() * 0.2F + 1.0F); + caster.swingItem(); + world.playSoundAtEntity(caster, "mob.wither.spawn", 1.0F, world.rand.nextFloat() * 0.2F + 1.0F); return true; } diff --git a/src/main/java/electroblob/wizardry/spell/PhaseStep.java b/src/main/java/electroblob/wizardry/spell/PhaseStep.java index a99034ce..1c1e3c83 100644 --- a/src/main/java/electroblob/wizardry/spell/PhaseStep.java +++ b/src/main/java/electroblob/wizardry/spell/PhaseStep.java @@ -1,64 +1,119 @@ package electroblob.wizardry.spell; +import electroblob.wizardry.EnumElement; +import electroblob.wizardry.EnumSpellType; +import electroblob.wizardry.EnumTier; import electroblob.wizardry.Wizardry; -import electroblob.wizardry.constants.Constants; -import electroblob.wizardry.constants.Element; -import electroblob.wizardry.constants.SpellType; -import electroblob.wizardry.constants.Tier; -import electroblob.wizardry.registry.WizardryItems; -import electroblob.wizardry.util.SpellModifiers; -import electroblob.wizardry.util.WizardryUtilities; +import electroblob.wizardry.WizardryUtilities; import net.minecraft.entity.player.EntityPlayer; -import net.minecraft.init.SoundEvents; import net.minecraft.item.EnumAction; -import net.minecraft.util.EnumHand; -import net.minecraft.util.EnumParticleTypes; -import net.minecraft.util.math.BlockPos; -import net.minecraft.util.math.RayTraceResult; +import net.minecraft.util.MovingObjectPosition; +import net.minecraft.util.MovingObjectPosition.MovingObjectType; import net.minecraft.world.World; public class PhaseStep extends Spell { public PhaseStep() { - super(Tier.ADVANCED, 35, Element.SORCERY, "phase_step", SpellType.UTILITY, 40, EnumAction.NONE, false); + super(EnumTier.ADVANCED, 35, EnumElement.SORCERY, "phase_step", EnumSpellType.UTILITY, 40, EnumAction.none, false); } @Override - public boolean cast(World world, EntityPlayer caster, EnumHand hand, int ticksInUse, SpellModifiers modifiers) { + public boolean cast(World world, EntityPlayer caster, int ticksInUse, float damageMultiplier, float rangeMultiplier, float durationMultiplier, float blastMultiplier) { // Phase step does not gain range from range multiplier, instead it increases the thickness // of the wall you can teleport through. - RayTraceResult rayTrace = WizardryUtilities.rayTrace(5, world, caster, false); + MovingObjectPosition rayTrace = WizardryUtilities.rayTrace(5, world, caster, false); - if(rayTrace != null && rayTrace.typeOfHit == RayTraceResult.Type.BLOCK){ + if(rayTrace != null && rayTrace.typeOfHit == MovingObjectType.BLOCK){ - BlockPos pos = new BlockPos(rayTrace.getBlockPos().getX(), (int)caster.posY, rayTrace.getBlockPos().getZ()); + int blockHitX = rayTrace.blockX; + int blockHitY = rayTrace.blockY; + int blockHitZ = rayTrace.blockZ; + int blockHitSide = rayTrace.sideHit; + + int playerY = (int)caster.posY; // The maximum wall thickness as determined by the range multiplier. The + 0.5f is so that // weird float processing doesn't incorrectly round it down. - int maxThickness = 1 + (int)((modifiers.get(WizardryItems.range_upgrade) - 1)/Constants.RANGE_INCREASE_PER_LEVEL + 0.5f); - - if(rayTrace.sideHit.getAxis().isHorizontal()){ + int maxThickness = 1 + (int)((rangeMultiplier - 1)/Wizardry.RANGE_INCREASE_PER_LEVEL + 0.5f); - // i represents how far the player needs to teleport to get through the wall - for(int i = 0; i <= maxThickness; i++){ - - BlockPos pos1 = pos.offset(rayTrace.sideHit.getOpposite(), i); - - // Prevents the player from teleporting through unbreakable blocks, so they cannot cheat in other mods' mazes and dungeons. - if((WizardryUtilities.isBlockUnbreakable(world, pos1) || WizardryUtilities.isBlockUnbreakable(world, pos1.up())) && !Wizardry.settings.teleportThroughUnbreakableBlocks) return false; + // How far the player needs to teleport to get through the wall + int teleportDistance = 0; - if(!world.getBlockState(pos1).getMaterial().blocksMovement() && !world.getBlockState(pos1.up()).getMaterial().blocksMovement()){ - - if(!world.isRemote){ - caster.setPositionAndUpdate(pos1.getX() + 0.5, caster.posY, pos1.getZ() + 0.5); + // The following prevents the player from teleporting into blocks and suffocating + checkforspace: + switch(blockHitSide){ + case -1: + return false; + case 0: + return false; + case 1: + return false; + case 2: + for(int i = 0; i <= maxThickness; i++){ + // Prevents the player from teleporting through unbreakable blocks, so they cannot cheat in other mods' mazes and dungeons. + if((WizardryUtilities.isBlockUnbreakable(world, blockHitX, playerY, blockHitZ + i) || WizardryUtilities.isBlockUnbreakable(world, blockHitX, playerY + 1, blockHitZ + i)) && !Wizardry.teleportThroughUnbreakableBlocks) return false; + + if(!world.getBlock(blockHitX, playerY, blockHitZ + i).getMaterial().blocksMovement() && !world.getBlock(blockHitX, playerY + 1, blockHitZ + i).getMaterial().blocksMovement()){ + teleportDistance = i; + break checkforspace; } - - caster.swingArm(hand); - return true; } + return false; + case 3: + for(int i = 0; i <= maxThickness; i++){ + // Prevents the player from teleporting through unbreakable blocks, so they cannot cheat in other mods' mazes and dungeons. + if((WizardryUtilities.isBlockUnbreakable(world, blockHitX, playerY, blockHitZ - i) || WizardryUtilities.isBlockUnbreakable(world, blockHitX, playerY + 1, blockHitZ - i)) && !Wizardry.teleportThroughUnbreakableBlocks) return false; + + if(!world.getBlock(blockHitX, playerY, blockHitZ - i).getMaterial().blocksMovement() && !world.getBlock(blockHitX, playerY + 1, blockHitZ - i).getMaterial().blocksMovement()){ + teleportDistance = i; + break checkforspace; + } + } + return false; + case 4: + for(int i = 0; i <= maxThickness; i++){ + // Prevents the player from teleporting through unbreakable blocks, so they cannot cheat in other mods' mazes and dungeons. + if((WizardryUtilities.isBlockUnbreakable(world, blockHitX + i, playerY, blockHitZ) || WizardryUtilities.isBlockUnbreakable(world, blockHitX + i, playerY + 1, blockHitZ)) && !Wizardry.teleportThroughUnbreakableBlocks) return false; + + if(!world.getBlock(blockHitX + i, playerY, blockHitZ).getMaterial().blocksMovement() && !world.getBlock(blockHitX + i, playerY + 1, blockHitZ).getMaterial().blocksMovement()){ + teleportDistance = i; + break checkforspace; + } + } + return false; + case 5: + for(int i = 0; i <= maxThickness; i++){ + // Prevents the player from teleporting through unbreakable blocks, so they cannot cheat in other mods' mazes and dungeons. + if((WizardryUtilities.isBlockUnbreakable(world, blockHitX - i, playerY, blockHitZ) || WizardryUtilities.isBlockUnbreakable(world, blockHitX - i, playerY + 1, blockHitZ)) && !Wizardry.teleportThroughUnbreakableBlocks) return false; + + if(!world.getBlock(blockHitX - i, playerY, blockHitZ).getMaterial().blocksMovement() && !world.getBlock(blockHitX - i, playerY + 1, blockHitZ).getMaterial().blocksMovement()){ + teleportDistance = i; + break checkforspace; + } + } + return false; + } + + if(!world.isRemote){ + switch(blockHitSide){ + case 2: + caster.setPositionAndUpdate(blockHitX + 0.5, caster.posY, blockHitZ + 0.5 + teleportDistance); + break; + case 3: + caster.setPositionAndUpdate(blockHitX + 0.5, caster.posY, blockHitZ + 0.5 - teleportDistance); + break; + case 4: + caster.setPositionAndUpdate(blockHitX + 0.5 + teleportDistance, caster.posY, blockHitZ + 0.5); + break; + case 5: + caster.setPositionAndUpdate(blockHitX + 0.5 - teleportDistance, caster.posY, blockHitZ + 0.5); + break; } } + world.playSoundAtEntity(caster, "mob.endermen.portal", 1.0F, 1.0f); + caster.swingItem(); + return true; } // This is here because the conditions are false on the client for whatever reason. (see the Javadoc for cast() @@ -68,9 +123,8 @@ public class PhaseStep extends Spell { double dx1 = caster.posX; double dy1 = WizardryUtilities.getPlayerEyesPos(caster) - 1.5 + 2*world.rand.nextFloat(); double dz1 = caster.posZ; - world.spawnParticle(EnumParticleTypes.PORTAL, dx1, dy1, dz1, world.rand.nextDouble() - 0.5, world.rand.nextDouble() - 0.5, world.rand.nextDouble() - 0.5); + world.spawnParticle("portal", dx1, dy1, dz1, world.rand.nextDouble() - 0.5, world.rand.nextDouble() - 0.5, world.rand.nextDouble() - 0.5); } - WizardryUtilities.playSoundAtPlayer(caster, SoundEvents.ENTITY_ENDERMEN_TELEPORT, 1.0F, 1.0f); } return false; diff --git a/src/main/java/electroblob/wizardry/spell/PlagueOfDarkness.java b/src/main/java/electroblob/wizardry/spell/PlagueOfDarkness.java index 252868a7..695e86f6 100644 --- a/src/main/java/electroblob/wizardry/spell/PlagueOfDarkness.java +++ b/src/main/java/electroblob/wizardry/spell/PlagueOfDarkness.java @@ -2,69 +2,63 @@ package electroblob.wizardry.spell; import java.util.List; +import electroblob.wizardry.EnumElement; +import electroblob.wizardry.EnumParticleType; +import electroblob.wizardry.EnumSpellType; +import electroblob.wizardry.EnumTier; +import electroblob.wizardry.MagicDamage; import electroblob.wizardry.Wizardry; -import electroblob.wizardry.constants.Element; -import electroblob.wizardry.constants.SpellType; -import electroblob.wizardry.constants.Tier; -import electroblob.wizardry.registry.WizardryItems; -import electroblob.wizardry.util.MagicDamage; -import electroblob.wizardry.util.MagicDamage.DamageType; -import electroblob.wizardry.util.SpellModifiers; -import electroblob.wizardry.util.WizardryParticleType; -import electroblob.wizardry.util.WizardryUtilities; +import electroblob.wizardry.WizardryUtilities; +import electroblob.wizardry.MagicDamage.DamageType; import net.minecraft.block.Block; -import net.minecraft.block.state.IBlockState; import net.minecraft.entity.EntityLivingBase; import net.minecraft.entity.player.EntityPlayer; -import net.minecraft.init.MobEffects; -import net.minecraft.init.SoundEvents; import net.minecraft.item.EnumAction; +import net.minecraft.potion.Potion; import net.minecraft.potion.PotionEffect; -import net.minecraft.util.EnumHand; -import net.minecraft.util.EnumParticleTypes; import net.minecraft.world.World; public class PlagueOfDarkness extends Spell { public PlagueOfDarkness() { - super(Tier.MASTER, 75, Element.NECROMANCY, "plague_of_darkness", SpellType.ATTACK, 200, EnumAction.BOW, false); + super(EnumTier.MASTER, 75, EnumElement.NECROMANCY, "plague_of_darkness", EnumSpellType.ATTACK, 200, EnumAction.bow, false); } @Override - public boolean cast(World world, EntityPlayer caster, EnumHand hand, int ticksInUse, SpellModifiers modifiers) { + public boolean cast(World world, EntityPlayer caster, int ticksInUse, float damageMultiplier, float rangeMultiplier, float durationMultiplier, float blastMultiplier) { - List targets = WizardryUtilities.getEntitiesWithinRadius(5.0d*modifiers.get(WizardryItems.blast_upgrade), caster.posX, caster.posY, caster.posZ, world); + List targets = WizardryUtilities.getEntitiesWithinRadius(5.0d*blastMultiplier, caster.posX, caster.posY, caster.posZ, world); for(EntityLivingBase target : targets){ if(WizardryUtilities.isValidTarget(caster, target) && !MagicDamage.isEntityImmune(DamageType.WITHER, target)){ - target.attackEntityFrom(MagicDamage.causeDirectMagicDamage(caster, DamageType.WITHER), 8.0f * modifiers.get(SpellModifiers.DAMAGE)); - target.addPotionEffect(new PotionEffect(MobEffects.WITHER, (int)(140*modifiers.get(WizardryItems.duration_upgrade)), 2)); + target.attackEntityFrom(MagicDamage.causeDirectMagicDamage(caster, DamageType.WITHER), 8.0f * damageMultiplier); + target.addPotionEffect(new PotionEffect(Potion.wither.id, (int)(140*durationMultiplier), 2)); } } if(world.isRemote){ double particleX, particleZ; - for(int i=0;i<40*modifiers.get(WizardryItems.blast_upgrade);i++){ + for(int i=0;i<40*blastMultiplier;i++){ particleX = caster.posX - 1.0d + 2*world.rand.nextDouble(); particleZ = caster.posZ - 1.0d + 2*world.rand.nextDouble(); - Wizardry.proxy.spawnParticle(WizardryParticleType.DARK_MAGIC, world, particleX, WizardryUtilities.getPlayerEyesPos(caster) - 1.5, particleZ, + Wizardry.proxy.spawnParticle(EnumParticleType.DARK_MAGIC, world, particleX, WizardryUtilities.getPlayerEyesPos(caster) - 1.5, particleZ, particleX - caster.posX, 0, particleZ - caster.posZ, 0, 0.1f, 0.0f, 0.0f); particleX = caster.posX - 1.0d + 2*world.rand.nextDouble(); particleZ = caster.posZ - 1.0d + 2*world.rand.nextDouble(); - Wizardry.proxy.spawnParticle(WizardryParticleType.SPARKLE, world, particleX, WizardryUtilities.getPlayerEyesPos(caster) - 1.5, particleZ, + Wizardry.proxy.spawnParticle(EnumParticleType.SPARKLE, world, particleX, WizardryUtilities.getPlayerEyesPos(caster) - 1.5, particleZ, particleX - caster.posX, 0, particleZ - caster.posZ, 30, 0.1f, 0.0f, 0.05f); particleX = caster.posX - 1.0d + 2*world.rand.nextDouble(); particleZ = caster.posZ - 1.0d + 2*world.rand.nextDouble(); - IBlockState block = WizardryUtilities.getBlockEntityIsStandingOn(caster); - + Block block = WizardryUtilities.getBlockEntityIsStandingOn(caster); + // Player actual eye height is 1.62, client is 1.5 too high, hence the -1.5. if(block != null){ - world.spawnParticle(EnumParticleTypes.BLOCK_DUST, particleX, caster.getEntityBoundingBox().minY, particleZ, - particleX - caster.posX, 0, particleZ - caster.posZ, Block.getStateId(block)); + Wizardry.proxy.spawnDigParticle(world, particleX, caster.posY - 1.5, particleZ, particleX - caster.posX, 0, particleZ - caster.posZ, + block); } } } - caster.swingArm(hand); - WizardryUtilities.playSoundAtPlayer(caster, SoundEvents.ENTITY_WITHER_DEATH, 1.0F, world.rand.nextFloat() * 0.2F + 1.0F); + caster.swingItem(); + world.playSoundAtEntity(caster, "mob.wither.death", 1.0F, world.rand.nextFloat() * 0.2F + 1.0F); return true; } diff --git a/src/main/java/electroblob/wizardry/spell/PocketFurnace.java b/src/main/java/electroblob/wizardry/spell/PocketFurnace.java index 76f70f58..01bb1cd2 100644 --- a/src/main/java/electroblob/wizardry/spell/PocketFurnace.java +++ b/src/main/java/electroblob/wizardry/spell/PocketFurnace.java @@ -1,27 +1,25 @@ package electroblob.wizardry.spell; -import electroblob.wizardry.constants.Element; -import electroblob.wizardry.constants.SpellType; -import electroblob.wizardry.constants.Tier; -import electroblob.wizardry.util.SpellModifiers; -import electroblob.wizardry.util.WizardryUtilities; +import electroblob.wizardry.EnumElement; +import electroblob.wizardry.EnumParticleType; +import electroblob.wizardry.EnumSpellType; +import electroblob.wizardry.EnumTier; +import electroblob.wizardry.Wizardry; +import electroblob.wizardry.WizardryUtilities; import net.minecraft.entity.player.EntityPlayer; -import net.minecraft.init.SoundEvents; import net.minecraft.item.EnumAction; import net.minecraft.item.ItemStack; import net.minecraft.item.crafting.FurnaceRecipes; -import net.minecraft.util.EnumHand; -import net.minecraft.util.EnumParticleTypes; import net.minecraft.world.World; public class PocketFurnace extends Spell { public PocketFurnace() { - super(Tier.APPRENTICE, 30, Element.FIRE, "pocket_furnace", SpellType.UTILITY, 40, EnumAction.BOW, false); + super(EnumTier.APPRENTICE, 30, EnumElement.FIRE, "pocket_furnace", EnumSpellType.UTILITY, 40, EnumAction.bow, false); } @Override - public boolean cast(World world, EntityPlayer caster, EnumHand hand, int ticksInUse, SpellModifiers modifiers) { + public boolean cast(World world, EntityPlayer caster, int ticksInUse, float damageMultiplier, float rangeMultiplier, float durationMultiplier, float blastMultiplier) { int usesLeft = 5; @@ -33,12 +31,12 @@ public class PocketFurnace extends Spell { if(stack != null){ - result = FurnaceRecipes.instance().getSmeltingResult(stack); + result = FurnaceRecipes.smelting().getSmeltingResult(stack); if(result != null){ if(stack.stackSize <= usesLeft){ ItemStack stack2 = new ItemStack(result.getItem(), stack.stackSize, result.getItemDamage()); - if(WizardryUtilities.doesPlayerHaveItem(caster, result.getItem())){ + if(caster.inventory.hasItem(result.getItem())){ caster.inventory.addItemStackToInventory(stack2); caster.inventory.setInventorySlotContents(i, null); }else{ @@ -54,14 +52,14 @@ public class PocketFurnace extends Spell { } } - WizardryUtilities.playSoundAtPlayer(caster, SoundEvents.BLOCK_FURNACE_FIRE_CRACKLE, 1, 0.75f); + caster.playSound("fire.fire", 1, 0.75f); if(world.isRemote){ for(int i=0; i<10; i++){ double x1 = (double)((float)caster.posX + world.rand.nextFloat()*2 - 1.0F); double y1 = (double)((float)WizardryUtilities.getPlayerEyesPos(caster) - 0.5F + world.rand.nextFloat()); double z1 = (double)((float)caster.posZ + world.rand.nextFloat()*2 - 1.0F); - world.spawnParticle(EnumParticleTypes.FLAME, x1, y1, z1, 0, 0.01F, 0); + world.spawnParticle("flame", x1, y1, z1, 0, 0.01F, 0); } } diff --git a/src/main/java/electroblob/wizardry/spell/PocketWorkbench.java b/src/main/java/electroblob/wizardry/spell/PocketWorkbench.java index bcb5ce53..7e14d965 100644 --- a/src/main/java/electroblob/wizardry/spell/PocketWorkbench.java +++ b/src/main/java/electroblob/wizardry/spell/PocketWorkbench.java @@ -1,22 +1,20 @@ package electroblob.wizardry.spell; +import electroblob.wizardry.EnumElement; +import electroblob.wizardry.EnumSpellType; +import electroblob.wizardry.EnumTier; import electroblob.wizardry.Wizardry; import electroblob.wizardry.WizardryGuiHandler; -import electroblob.wizardry.constants.Element; -import electroblob.wizardry.constants.SpellType; -import electroblob.wizardry.constants.Tier; -import electroblob.wizardry.registry.WizardrySounds; -import electroblob.wizardry.util.SpellModifiers; -import electroblob.wizardry.util.WizardryUtilities; +import electroblob.wizardry.client.GuiPortableCrafting; import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.inventory.InventoryEnderChest; import net.minecraft.item.EnumAction; -import net.minecraft.util.EnumHand; import net.minecraft.world.World; public class PocketWorkbench extends Spell { public PocketWorkbench() { - super(Tier.APPRENTICE, 30, Element.SORCERY, "pocket_workbench", SpellType.UTILITY, 40, EnumAction.BOW, false); + super(EnumTier.APPRENTICE, 30, EnumElement.SORCERY, "pocket_workbench", EnumSpellType.UTILITY, 40, EnumAction.bow, false); } @Override @@ -25,14 +23,14 @@ public class PocketWorkbench extends Spell { } @Override - public boolean cast(World world, EntityPlayer caster, EnumHand hand, int ticksInUse, SpellModifiers modifiers) { + public boolean cast(World world, EntityPlayer caster, int ticksInUse, float damageMultiplier, float rangeMultiplier, float durationMultiplier, float blastMultiplier) { // TODO: Investigate possible item duplication bug with this spell. So far I have been unable to recreate it. if(!world.isRemote){ caster.openGui(Wizardry.instance, WizardryGuiHandler.PORTABLE_CRAFTING, world, (int)caster.posX, (int)caster.posY, (int)caster.posZ); } - WizardryUtilities.playSoundAtPlayer(caster, WizardrySounds.SPELL_CONJURATION, 1, 1); + world.playSoundAtEntity(caster, "wizardry:aura", 1, 1); return true; } diff --git a/src/main/java/electroblob/wizardry/spell/Poison.java b/src/main/java/electroblob/wizardry/spell/Poison.java index 9a69bc38..e98bb9d3 100644 --- a/src/main/java/electroblob/wizardry/spell/Poison.java +++ b/src/main/java/electroblob/wizardry/spell/Poison.java @@ -1,78 +1,76 @@ package electroblob.wizardry.spell; +import electroblob.wizardry.EnumElement; +import electroblob.wizardry.EnumParticleType; +import electroblob.wizardry.EnumSpellType; +import electroblob.wizardry.EnumTier; +import electroblob.wizardry.MagicDamage; import electroblob.wizardry.Wizardry; -import electroblob.wizardry.constants.Element; -import electroblob.wizardry.constants.SpellType; -import electroblob.wizardry.constants.Tier; -import electroblob.wizardry.registry.WizardryItems; -import electroblob.wizardry.registry.WizardrySounds; -import electroblob.wizardry.util.MagicDamage; -import electroblob.wizardry.util.MagicDamage.DamageType; -import electroblob.wizardry.util.SpellModifiers; -import electroblob.wizardry.util.WizardryParticleType; -import electroblob.wizardry.util.WizardryUtilities; +import electroblob.wizardry.WizardryUtilities; +import electroblob.wizardry.MagicDamage.DamageType; import net.minecraft.entity.EntityLiving; import net.minecraft.entity.EntityLivingBase; +import net.minecraft.entity.monster.EntitySpider; import net.minecraft.entity.player.EntityPlayer; -import net.minecraft.init.MobEffects; import net.minecraft.item.EnumAction; +import net.minecraft.potion.Potion; import net.minecraft.potion.PotionEffect; -import net.minecraft.util.EnumHand; -import net.minecraft.util.math.RayTraceResult; -import net.minecraft.util.math.Vec3d; -import net.minecraft.util.text.TextComponentTranslation; +import net.minecraft.util.ChatComponentTranslation; +import net.minecraft.util.MovingObjectPosition; +import net.minecraft.util.MovingObjectPosition.MovingObjectType; +import net.minecraft.util.Vec3; import net.minecraft.world.World; public class Poison extends Spell { public Poison() { - super(Tier.APPRENTICE, 10, Element.EARTH, "poison", SpellType.ATTACK, 20, EnumAction.NONE, false); + super(EnumTier.APPRENTICE, 10, EnumElement.EARTH, "poison", EnumSpellType.ATTACK, 20, EnumAction.none, false); } @Override - public boolean cast(World world, EntityPlayer caster, EnumHand hand, int ticksInUse, SpellModifiers modifiers) { + public boolean cast(World world, EntityPlayer caster, int ticksInUse, float damageMultiplier, float rangeMultiplier, float durationMultiplier, float blastMultiplier) { - Vec3d look = caster.getLookVec(); + Vec3 look = caster.getLookVec(); - RayTraceResult rayTrace = WizardryUtilities.standardEntityRayTrace(world, caster, 10*modifiers.get(WizardryItems.range_upgrade)); + MovingObjectPosition rayTrace = WizardryUtilities.standardEntityRayTrace(world, caster, 10*rangeMultiplier); - if(rayTrace != null && rayTrace.typeOfHit == RayTraceResult.Type.ENTITY && rayTrace.entityHit instanceof EntityLivingBase){ + if(rayTrace != null && rayTrace.typeOfHit == MovingObjectType.ENTITY && rayTrace.entityHit instanceof EntityLivingBase){ EntityLivingBase target = (EntityLivingBase) rayTrace.entityHit; // Has no effect on undead or spiders. if(MagicDamage.isEntityImmune(DamageType.POISON, target)){ - if(!world.isRemote) caster.addChatComponentMessage(new TextComponentTranslation("spell.resist", target.getName(), this.getNameForTranslationFormatted())); + if(!world.isRemote) caster.addChatComponentMessage(new ChatComponentTranslation("spell.resist", target.getCommandSenderName(), this.getDisplayNameWithFormatting())); }else{ - target.attackEntityFrom(MagicDamage.causeDirectMagicDamage(caster, DamageType.POISON), 1.0f * modifiers.get(SpellModifiers.DAMAGE)); - target.addPotionEffect(new PotionEffect(MobEffects.POISON, (int)(200*modifiers.get(WizardryItems.duration_upgrade)), 1)); + target.attackEntityFrom(MagicDamage.causeDirectMagicDamage(caster, DamageType.POISON), 1.0f * damageMultiplier); + target.addPotionEffect(new PotionEffect(Potion.poison.id, (int)(200*durationMultiplier), 1)); } } if(world.isRemote){ - for(int i=1; i<(int)(25*modifiers.get(WizardryItems.range_upgrade)); i+=2){ + for(int i=1; i<(int)(25*rangeMultiplier); i+=2){ // I figured it out! when on client side, entityplayer.posY is at the eyes, not the feet! double x1 = caster.posX + look.xCoord*i/2 + world.rand.nextFloat()/5 - 0.1f; double y1 = WizardryUtilities.getPlayerEyesPos(caster) - 0.4f + look.yCoord*i/2 + world.rand.nextFloat()/5 - 0.1f; double z1 = caster.posZ + look.zCoord*i/2 + world.rand.nextFloat()/5 - 0.1f; //world.spawnParticle("mobSpell", x1, y1, z1, -1*look.xCoord, -1*look.yCoord, -1*look.zCoord); - Wizardry.proxy.spawnParticle(WizardryParticleType.DARK_MAGIC, world, x1, y1, z1, 0.0d, 0.0d, 0.0d, 0, 0.3f, 0.7f, 0.0f); - Wizardry.proxy.spawnParticle(WizardryParticleType.SPARKLE, world, x1, y1, z1, 0.0d, 0.0d, 0.0d, 12 + world.rand.nextInt(8), 0.1f, 0.4f, 0.0f); + Wizardry.proxy.spawnParticle(EnumParticleType.DARK_MAGIC, world, x1, y1, z1, 0.0d, 0.0d, 0.0d, 0, 0.3f, 0.7f, 0.0f); + Wizardry.proxy.spawnParticle(EnumParticleType.SPARKLE, world, x1, y1, z1, 0.0d, 0.0d, 0.0d, 12 + world.rand.nextInt(8), 0.1f, 0.4f, 0.0f); } } - caster.swingArm(hand); - WizardryUtilities.playSoundAtPlayer(caster, WizardrySounds.SPELL_ICE, 1.0F, world.rand.nextFloat() * 0.2F + 1.0F); + caster.swingItem(); + world.playSoundAtEntity(caster, "wizardry:ice", 1.0F, world.rand.nextFloat() * 0.2F + 1.0F); return true; } @Override - public boolean cast(World world, EntityLiving caster, EnumHand hand, int ticksInUse, EntityLivingBase target, SpellModifiers modifiers){ + public boolean cast(World world, EntityLiving caster, EntityLivingBase target, float damageMultiplier, float rangeMultiplier, float durationMultiplier, float blastMultiplier){ if(target != null){ // Has no effect on undead or spiders. if(!MagicDamage.isEntityImmune(DamageType.POISON, target) && !world.isRemote){ - target.attackEntityFrom(MagicDamage.causeDirectMagicDamage(caster, DamageType.POISON), 1.0f * modifiers.get(SpellModifiers.DAMAGE)); - target.addPotionEffect(new PotionEffect(MobEffects.POISON, (int)(200*modifiers.get(WizardryItems.duration_upgrade)), 1)); + target.attackEntityFrom(MagicDamage.causeDirectMagicDamage(caster, DamageType.POISON), 1.0f * damageMultiplier); + target.addPotionEffect(new PotionEffect(Potion.poison.id, (int)(200*durationMultiplier), 1)); } if(world.isRemote){ @@ -81,19 +79,19 @@ public class Poison extends Spell { double dy = (target.posY - caster.posY)/caster.getDistanceToEntity(target); double dz = (target.posZ - caster.posZ)/caster.getDistanceToEntity(target); - for(int i=1; i<(int)(25*modifiers.get(WizardryItems.range_upgrade)); i+=2){ + for(int i=1; i<(int)(25*rangeMultiplier); i+=2){ double x1 = caster.posX + dx*i/2 + world.rand.nextFloat()/5 - 0.1f; double y1 = caster.posY + caster.getEyeHeight() - 0.4f + dy*i/2 + world.rand.nextFloat()/5 - 0.1f; double z1 = caster.posZ + dz*i/2 + world.rand.nextFloat()/5 - 0.1f; - Wizardry.proxy.spawnParticle(WizardryParticleType.DARK_MAGIC, world, x1, y1, z1, 0.0d, 0.0d, 0.0d, 0, 0.3f, 0.7f, 0.0f); - Wizardry.proxy.spawnParticle(WizardryParticleType.SPARKLE, world, x1, y1, z1, 0.0d, 0.0d, 0.0d, 12 + world.rand.nextInt(8), 0.1f, 0.4f, 0.0f); + Wizardry.proxy.spawnParticle(EnumParticleType.DARK_MAGIC, world, x1, y1, z1, 0.0d, 0.0d, 0.0d, 0, 0.3f, 0.7f, 0.0f); + Wizardry.proxy.spawnParticle(EnumParticleType.SPARKLE, world, x1, y1, z1, 0.0d, 0.0d, 0.0d, 12 + world.rand.nextInt(8), 0.1f, 0.4f, 0.0f); } } - caster.swingArm(hand); - caster.playSound(WizardrySounds.SPELL_ICE, 1.0F, world.rand.nextFloat() * 0.2F + 1.0F); + caster.swingItem(); + world.playSoundAtEntity(caster, "wizardry:ice", 1.0F, world.rand.nextFloat() * 0.2F + 1.0F); return true; } diff --git a/src/main/java/electroblob/wizardry/spell/PoisonBomb.java b/src/main/java/electroblob/wizardry/spell/PoisonBomb.java index 3ee00729..6340dbb6 100644 --- a/src/main/java/electroblob/wizardry/spell/PoisonBomb.java +++ b/src/main/java/electroblob/wizardry/spell/PoisonBomb.java @@ -1,24 +1,19 @@ package electroblob.wizardry.spell; -import electroblob.wizardry.constants.Element; -import electroblob.wizardry.constants.SpellType; -import electroblob.wizardry.constants.Tier; +import electroblob.wizardry.EnumElement; +import electroblob.wizardry.EnumSpellType; +import electroblob.wizardry.EnumTier; import electroblob.wizardry.entity.projectile.EntityPoisonBomb; -import electroblob.wizardry.registry.WizardryItems; -import electroblob.wizardry.util.SpellModifiers; -import electroblob.wizardry.util.WizardryUtilities; import net.minecraft.entity.EntityLiving; import net.minecraft.entity.EntityLivingBase; import net.minecraft.entity.player.EntityPlayer; -import net.minecraft.init.SoundEvents; import net.minecraft.item.EnumAction; -import net.minecraft.util.EnumHand; import net.minecraft.world.World; public class PoisonBomb extends Spell { public PoisonBomb() { - super(Tier.APPRENTICE, 15, Element.EARTH, "poison_bomb", SpellType.ATTACK, 25, EnumAction.NONE, false); + super(EnumTier.APPRENTICE, 15, EnumElement.EARTH, "poison_bomb", EnumSpellType.ATTACK, 25, EnumAction.none, false); } @Override @@ -27,31 +22,31 @@ public class PoisonBomb extends Spell { } @Override - public boolean cast(World world, EntityPlayer caster, EnumHand hand, int ticksInUse, SpellModifiers modifiers) { + public boolean cast(World world, EntityPlayer caster, int ticksInUse, float damageMultiplier, float rangeMultiplier, float durationMultiplier, float blastMultiplier) { if(!world.isRemote){ - EntityPoisonBomb poisonbomb = new EntityPoisonBomb(world, caster, modifiers.get(SpellModifiers.DAMAGE), modifiers.get(WizardryItems.blast_upgrade)); + EntityPoisonBomb poisonbomb = new EntityPoisonBomb(world, caster, damageMultiplier, blastMultiplier); world.spawnEntityInWorld(poisonbomb); } - caster.swingArm(hand); - WizardryUtilities.playSoundAtPlayer(caster, SoundEvents.ENTITY_SNOWBALL_THROW, 0.5F, 0.4F / (world.rand.nextFloat() * 0.4F + 0.8F)); + caster.swingItem(); + world.playSoundAtEntity(caster, "random.bow", 0.5F, 0.4F / (world.rand.nextFloat() * 0.4F + 0.8F)); return true; } @Override - public boolean cast(World world, EntityLiving caster, EnumHand hand, int ticksInUse, EntityLivingBase target, SpellModifiers modifiers){ + public boolean cast(World world, EntityLiving caster, EntityLivingBase target, float damageMultiplier, float rangeMultiplier, float durationMultiplier, float blastMultiplier){ if(target != null){ if(!world.isRemote){ - EntityPoisonBomb poisonbomb = new EntityPoisonBomb(world, caster, modifiers.get(SpellModifiers.DAMAGE), modifiers.get(WizardryItems.blast_upgrade)); + EntityPoisonBomb poisonbomb = new EntityPoisonBomb(world, caster, damageMultiplier, blastMultiplier); poisonbomb.directTowards(target, 1.5f); world.spawnEntityInWorld(poisonbomb); } - caster.swingArm(hand); - caster.playSound(SoundEvents.ENTITY_SNOWBALL_THROW, 0.5F, 0.4F / (world.rand.nextFloat() * 0.4F + 0.8F)); + caster.swingItem(); + world.playSoundAtEntity(caster, "random.bow", 0.5F, 0.4F / (world.rand.nextFloat() * 0.4F + 0.8F)); return true; } diff --git a/src/main/java/electroblob/wizardry/spell/ReplenishHunger.java b/src/main/java/electroblob/wizardry/spell/ReplenishHunger.java index bf9983a6..9569f0d7 100644 --- a/src/main/java/electroblob/wizardry/spell/ReplenishHunger.java +++ b/src/main/java/electroblob/wizardry/spell/ReplenishHunger.java @@ -1,29 +1,26 @@ package electroblob.wizardry.spell; +import electroblob.wizardry.EnumElement; +import electroblob.wizardry.EnumParticleType; +import electroblob.wizardry.EnumSpellType; +import electroblob.wizardry.EnumTier; import electroblob.wizardry.Wizardry; -import electroblob.wizardry.constants.Element; -import electroblob.wizardry.constants.SpellType; -import electroblob.wizardry.constants.Tier; -import electroblob.wizardry.registry.WizardrySounds; -import electroblob.wizardry.util.SpellModifiers; -import electroblob.wizardry.util.WizardryParticleType; -import electroblob.wizardry.util.WizardryUtilities; +import electroblob.wizardry.WizardryUtilities; import net.minecraft.entity.player.EntityPlayer; import net.minecraft.item.EnumAction; -import net.minecraft.util.EnumHand; import net.minecraft.world.World; public class ReplenishHunger extends Spell { public ReplenishHunger() { - super(Tier.APPRENTICE, 10, Element.HEALING, "replenish_hunger", SpellType.UTILITY, 30, EnumAction.BOW, false); + super(EnumTier.APPRENTICE, 10, EnumElement.HEALING, "replenish_hunger", EnumSpellType.UTILITY, 30, EnumAction.bow, false); } @Override - public boolean cast(World world, EntityPlayer caster, EnumHand hand, int ticksInUse, SpellModifiers modifiers) { + public boolean cast(World world, EntityPlayer caster, int ticksInUse, float damageMultiplier, float rangeMultiplier, float durationMultiplier, float blastMultiplier) { if(caster.getFoodStats().needFood()){ - int foodAmount = (int)(6*modifiers.get(SpellModifiers.DAMAGE)); + int foodAmount = (int)(6*damageMultiplier); // Fixed issue #6: Changed to addStats, since setFoodLevel is client-side only caster.getFoodStats().addStats(foodAmount, foodAmount*0.1f); if(world.isRemote){ @@ -31,10 +28,10 @@ public class ReplenishHunger extends Spell { double x1 = (double)((float)caster.posX + world.rand.nextFloat()*2 - 1.0F); double y1 = (double)((float)WizardryUtilities.getPlayerEyesPos(caster) - 0.5F + world.rand.nextFloat()); double z1 = (double)((float)caster.posZ + world.rand.nextFloat()*2 - 1.0F); - Wizardry.proxy.spawnParticle(WizardryParticleType.SPARKLE, world, x1, y1, z1, 0, 0.1F, 0, 48 + world.rand.nextInt(12), 1.0f, 0.7f, 0.3f); + Wizardry.proxy.spawnParticle(EnumParticleType.SPARKLE, world, x1, y1, z1, 0, 0.1F, 0, 48 + world.rand.nextInt(12), 1.0f, 0.7f, 0.3f); } } - WizardryUtilities.playSoundAtPlayer(caster, WizardrySounds.SPELL_HEAL, 0.7F, world.rand.nextFloat() * 0.4F + 1.0F); + world.playSoundAtEntity(caster, "wizardry:heal", 0.7F, world.rand.nextFloat() * 0.4F + 1.0F); return true; } return false; diff --git a/src/main/java/electroblob/wizardry/spell/RingOfFire.java b/src/main/java/electroblob/wizardry/spell/RingOfFire.java index e4f1bd84..cf865bde 100644 --- a/src/main/java/electroblob/wizardry/spell/RingOfFire.java +++ b/src/main/java/electroblob/wizardry/spell/RingOfFire.java @@ -1,24 +1,19 @@ package electroblob.wizardry.spell; -import electroblob.wizardry.constants.Element; -import electroblob.wizardry.constants.SpellType; -import electroblob.wizardry.constants.Tier; +import electroblob.wizardry.EnumElement; +import electroblob.wizardry.EnumSpellType; +import electroblob.wizardry.EnumTier; import electroblob.wizardry.entity.construct.EntityFireRing; -import electroblob.wizardry.registry.WizardryItems; -import electroblob.wizardry.util.SpellModifiers; -import electroblob.wizardry.util.WizardryUtilities; import net.minecraft.entity.EntityLiving; import net.minecraft.entity.EntityLivingBase; import net.minecraft.entity.player.EntityPlayer; -import net.minecraft.init.SoundEvents; import net.minecraft.item.EnumAction; -import net.minecraft.util.EnumHand; import net.minecraft.world.World; public class RingOfFire extends Spell { public RingOfFire() { - super(Tier.ADVANCED, 30, Element.FIRE, "ring_of_fire", SpellType.ATTACK, 100, EnumAction.BOW, false); + super(EnumTier.ADVANCED, 30, EnumElement.FIRE, "ring_of_fire", EnumSpellType.ATTACK, 100, EnumAction.bow, false); } @Override @@ -27,31 +22,29 @@ public class RingOfFire extends Spell { } @Override - public boolean cast(World world, EntityPlayer caster, EnumHand hand, int ticksInUse, SpellModifiers modifiers) { + public boolean cast(World world, EntityPlayer caster, int ticksInUse, float damageMultiplier, float rangeMultiplier, float durationMultiplier, float blastMultiplier) { if(caster.onGround){ if(!world.isRemote){ - EntityFireRing firering = new EntityFireRing(world, caster.posX, caster.posY, caster.posZ, caster, (int)(600*modifiers.get(WizardryItems.duration_upgrade)), modifiers.get(SpellModifiers.DAMAGE)); + EntityFireRing firering = new EntityFireRing(world, caster.posX, caster.posY, caster.posZ, caster, (int)(600*durationMultiplier), damageMultiplier); world.spawnEntityInWorld(firering); + world.playAuxSFX(1009, (int)caster.posX, (int)caster.posY, (int)caster.posZ, 0); } - - WizardryUtilities.playSoundAtPlayer(caster, SoundEvents.ENTITY_BLAZE_SHOOT, 1, 1); return true; } return false; } @Override - public boolean cast(World world, EntityLiving caster, EnumHand hand, int ticksInUse, EntityLivingBase target, SpellModifiers modifiers){ + public boolean cast(World world, EntityLiving caster, EntityLivingBase target, float damageMultiplier, float rangeMultiplier, float durationMultiplier, float blastMultiplier){ if(target != null){ - if(caster.onGround && world.getEntitiesWithinAABB(EntityFireRing.class, caster.getEntityBoundingBox()).isEmpty()){ + if(caster.onGround && world.getEntitiesWithinAABB(EntityFireRing.class, caster.boundingBox).isEmpty()){ if(!world.isRemote){ - EntityFireRing firering = new EntityFireRing(world, caster.posX, caster.posY, caster.posZ, caster, (int)(600*modifiers.get(WizardryItems.duration_upgrade)), modifiers.get(SpellModifiers.DAMAGE)); + EntityFireRing firering = new EntityFireRing(world, caster.posX, caster.posY, caster.posZ, caster, (int)(600*durationMultiplier), damageMultiplier); world.spawnEntityInWorld(firering); + world.playAuxSFX(1009, (int)caster.posX, (int)caster.posY, (int)caster.posZ, 0); } - - caster.playSound(SoundEvents.ENTITY_BLAZE_SHOOT, 1, 1); return true; } return false; diff --git a/src/main/java/electroblob/wizardry/spell/ShadowWard.java b/src/main/java/electroblob/wizardry/spell/ShadowWard.java index 01132024..7a5a4d57 100644 --- a/src/main/java/electroblob/wizardry/spell/ShadowWard.java +++ b/src/main/java/electroblob/wizardry/spell/ShadowWard.java @@ -1,67 +1,35 @@ package electroblob.wizardry.spell; -import electroblob.wizardry.constants.Element; -import electroblob.wizardry.constants.SpellType; -import electroblob.wizardry.constants.Tier; -import electroblob.wizardry.item.ItemWand; -import electroblob.wizardry.util.IElementalDamage; -import electroblob.wizardry.util.MagicDamage; -import electroblob.wizardry.util.SpellModifiers; -import electroblob.wizardry.util.WandHelper; -import electroblob.wizardry.util.WizardryUtilities; -import electroblob.wizardry.util.MagicDamage.DamageType; -import net.minecraft.entity.EntityLivingBase; +import electroblob.wizardry.EnumElement; +import electroblob.wizardry.EnumSpellType; +import electroblob.wizardry.EnumTier; +import electroblob.wizardry.WizardryUtilities; import net.minecraft.entity.player.EntityPlayer; -import net.minecraft.init.SoundEvents; import net.minecraft.item.EnumAction; -import net.minecraft.item.ItemStack; -import net.minecraft.util.EnumHand; -import net.minecraft.util.EnumParticleTypes; import net.minecraft.world.World; -import net.minecraftforge.event.entity.living.LivingAttackEvent; -import net.minecraftforge.fml.common.Mod; -import net.minecraftforge.fml.common.eventhandler.SubscribeEvent; -@Mod.EventBusSubscriber public class ShadowWard extends Spell { public ShadowWard() { - super(Tier.ADVANCED, 10, Element.NECROMANCY, "shadow_ward", SpellType.DEFENCE, 0, EnumAction.BLOCK, true); + super(EnumTier.ADVANCED, 10, EnumElement.NECROMANCY, "shadow_ward", EnumSpellType.DEFENCE, 0, EnumAction.block, true); } @Override - public boolean cast(World world, EntityPlayer caster, EnumHand hand, int ticksInUse, SpellModifiers modifiers) { - + public boolean cast(World world, EntityPlayer caster, int ticksInUse, float damageMultiplier, float rangeMultiplier, float durationMultiplier, float blastMultiplier) { + if(world.isRemote){ double dx = -1 + 2*world.rand.nextFloat(); double dy = -1 + world.rand.nextFloat(); double dz = -1 + 2*world.rand.nextFloat(); - world.spawnParticle(EnumParticleTypes.PORTAL, caster.posX, WizardryUtilities.getPlayerEyesPos(caster), caster.posZ, dx, dy, dz); + world.spawnParticle("portal", caster.posX, WizardryUtilities.getPlayerEyesPos(caster), caster.posZ, dx, dy, dz); } - + if(ticksInUse % 50 == 0){ - WizardryUtilities.playSoundAtPlayer(caster, SoundEvents.BLOCK_PORTAL_AMBIENT, 0.6f, 1.5f); + world.playSoundAtEntity(caster, "portal.portal", 0.6f, 1.5f); } - + return true; } - @SubscribeEvent - public static void onLivingAttackEvent(LivingAttackEvent event){ - if(event.getSource() != null && event.getSource().getEntity() instanceof EntityLivingBase){ - // There used to be a check that the target was a player here, but I don't see any reason for it. - ItemStack wand = event.getEntityLiving().getActiveItemStack(); - - if(wand != null && wand.getItemDamage() < wand.getMaxDamage() && wand.getItem() instanceof ItemWand - && WandHelper.getCurrentSpell(wand) instanceof ShadowWard && !event.getSource().isUnblockable() - && !(event.getSource() instanceof IElementalDamage && ((IElementalDamage)event.getSource()).isRetaliatory())){ - - event.setCanceled(true); - // Now we can preserve the original daage source (sort of) as long as we make it retaliatory. - event.getEntityLiving().attackEntityFrom(MagicDamage.causeDirectMagicDamage(event.getSource().getEntity(), DamageType.MAGIC, true), event.getAmount()/2); - ((EntityLivingBase)event.getSource().getEntity()).attackEntityFrom(MagicDamage.causeDirectMagicDamage(event.getEntityLiving(), DamageType.MAGIC, true), event.getAmount()/2); - } - } - } } diff --git a/src/main/java/electroblob/wizardry/spell/Shield.java b/src/main/java/electroblob/wizardry/spell/Shield.java index 518cbe22..1d7d80f1 100644 --- a/src/main/java/electroblob/wizardry/spell/Shield.java +++ b/src/main/java/electroblob/wizardry/spell/Shield.java @@ -1,39 +1,35 @@ package electroblob.wizardry.spell; -import electroblob.wizardry.WizardData; -import electroblob.wizardry.constants.Element; -import electroblob.wizardry.constants.SpellType; -import electroblob.wizardry.constants.Tier; +import electroblob.wizardry.EnumElement; +import electroblob.wizardry.EnumSpellType; +import electroblob.wizardry.EnumTier; +import electroblob.wizardry.ExtendedPlayer; import electroblob.wizardry.entity.EntityShield; -import electroblob.wizardry.registry.WizardrySounds; -import electroblob.wizardry.util.SpellModifiers; -import electroblob.wizardry.util.WizardryUtilities; import net.minecraft.entity.player.EntityPlayer; -import net.minecraft.init.MobEffects; import net.minecraft.item.EnumAction; +import net.minecraft.potion.Potion; import net.minecraft.potion.PotionEffect; -import net.minecraft.util.EnumHand; import net.minecraft.world.World; public class Shield extends Spell { public Shield() { - super(Tier.APPRENTICE, 5, Element.HEALING, "shield", SpellType.DEFENCE, 0, EnumAction.BLOCK, true); + super(EnumTier.APPRENTICE, 5, EnumElement.HEALING, "shield", EnumSpellType.DEFENCE, 0, EnumAction.block, true); } @Override - public boolean cast(World world, EntityPlayer caster, EnumHand hand, int ticksInUse, SpellModifiers modifiers) { + public boolean cast(World world, EntityPlayer caster, int ticksInUse, float damageMultiplier, float rangeMultiplier, float durationMultiplier, float blastMultiplier) { - caster.addPotionEffect(new PotionEffect(MobEffects.RESISTANCE, 10, 0, false, false)); + caster.addPotionEffect(new PotionEffect(Potion.resistance.id, 10, 0, true)); - if(WizardData.get(caster).shield == null){ - WizardData.get(caster).shield = new EntityShield(world, caster); + if(ExtendedPlayer.get(caster).shield == null){ + ExtendedPlayer.get(caster).shield = new EntityShield(world, caster); if(!world.isRemote){ - world.spawnEntityInWorld(WizardData.get(caster).shield); + world.spawnEntityInWorld(ExtendedPlayer.get(caster).shield); } } if(ticksInUse == 0){ - WizardryUtilities.playSoundAtPlayer(caster, WizardrySounds.SPELL_CONJURATION, 1.0f, 1.0f); + world.playSoundAtEntity(caster, "wizardry:aura", 1.0f, 1.0f); } return true; } diff --git a/src/main/java/electroblob/wizardry/spell/Shockwave.java b/src/main/java/electroblob/wizardry/spell/Shockwave.java index 1f48a728..35897664 100644 --- a/src/main/java/electroblob/wizardry/spell/Shockwave.java +++ b/src/main/java/electroblob/wizardry/spell/Shockwave.java @@ -2,44 +2,38 @@ package electroblob.wizardry.spell; import java.util.List; +import electroblob.wizardry.EnumElement; +import electroblob.wizardry.EnumParticleType; +import electroblob.wizardry.EnumSpellType; +import electroblob.wizardry.EnumTier; +import electroblob.wizardry.MagicDamage; import electroblob.wizardry.Wizardry; -import electroblob.wizardry.constants.Element; -import electroblob.wizardry.constants.SpellType; -import electroblob.wizardry.constants.Tier; -import electroblob.wizardry.registry.WizardryItems; -import electroblob.wizardry.registry.WizardrySounds; -import electroblob.wizardry.util.MagicDamage; -import electroblob.wizardry.util.MagicDamage.DamageType; -import electroblob.wizardry.util.SpellModifiers; -import electroblob.wizardry.util.WizardryParticleType; -import electroblob.wizardry.util.WizardryUtilities; +import electroblob.wizardry.WizardryUtilities; +import electroblob.wizardry.MagicDamage.DamageType; import net.minecraft.block.Block; -import net.minecraft.block.state.IBlockState; import net.minecraft.entity.EntityLivingBase; import net.minecraft.entity.player.EntityPlayer; import net.minecraft.entity.player.EntityPlayerMP; import net.minecraft.item.EnumAction; -import net.minecraft.network.play.server.SPacketEntityVelocity; -import net.minecraft.util.EnumHand; -import net.minecraft.util.EnumParticleTypes; +import net.minecraft.network.play.server.S12PacketEntityVelocity; import net.minecraft.world.World; public class Shockwave extends Spell { public Shockwave() { - super(Tier.MASTER, 65, Element.SORCERY, "shockwave", SpellType.ATTACK, 150, EnumAction.BOW, false); + super(EnumTier.MASTER, 65, EnumElement.SORCERY, "shockwave", EnumSpellType.ATTACK, 150, EnumAction.bow, false); } @Override - public boolean cast(World world, EntityPlayer caster, EnumHand hand, int ticksInUse, SpellModifiers modifiers) { + public boolean cast(World world, EntityPlayer caster, int ticksInUse, float damageMultiplier, float rangeMultiplier, float durationMultiplier, float blastMultiplier) { - List targets = WizardryUtilities.getEntitiesWithinRadius(5.0d*modifiers.get(WizardryItems.blast_upgrade), caster.posX, caster.posY, caster.posZ, world); + List targets = WizardryUtilities.getEntitiesWithinRadius(5.0d*blastMultiplier, caster.posX, caster.posY, caster.posZ, world); for(EntityLivingBase target : targets){ if(WizardryUtilities.isValidTarget(caster, target)){ // Damage increases closer to player up to a maximum of 4 hearts (at 1 block distance). float damage = Math.min(8.0f/target.getDistanceToEntity(caster), 8.0f); - target.attackEntityFrom(MagicDamage.causeDirectMagicDamage(caster, DamageType.BLAST), damage * modifiers.get(SpellModifiers.DAMAGE)); + target.attackEntityFrom(MagicDamage.causeDirectMagicDamage(caster, DamageType.BLAST), damage * damageMultiplier); if(!world.isRemote){ @@ -58,40 +52,39 @@ public class Shockwave extends Spell { // Player motion is handled on that player's client so needs packets if(target instanceof EntityPlayerMP){ - ((EntityPlayerMP)target).connection.sendPacket(new SPacketEntityVelocity(target)); + ((EntityPlayerMP)target).playerNetServerHandler.sendPacket(new S12PacketEntityVelocity(target)); } } } } if(world.isRemote){ - + + world.spawnParticle("largeexplode", caster.posX, caster.boundingBox.minY + 0.1, caster.posZ, 0, 0, 0); + double particleX, particleZ; for(int i=0;i<40;i++){ particleX = caster.posX - 1.0d + 2*world.rand.nextDouble(); particleZ = caster.posZ - 1.0d + 2*world.rand.nextDouble(); - Wizardry.proxy.spawnParticle(WizardryParticleType.SPARKLE, world, particleX, WizardryUtilities.getPlayerEyesPos(caster) - 1.5, particleZ, + Wizardry.proxy.spawnParticle(EnumParticleType.SPARKLE, world, particleX, WizardryUtilities.getPlayerEyesPos(caster) - 1.5, particleZ, particleX - caster.posX, 0, particleZ - caster.posZ, 30, 0.8f, 0.8f, 1.0f); particleX = caster.posX - 1.0d + 2*world.rand.nextDouble(); particleZ = caster.posZ - 1.0d + 2*world.rand.nextDouble(); - Wizardry.proxy.spawnParticle(WizardryParticleType.SPARKLE, world, particleX, WizardryUtilities.getPlayerEyesPos(caster) - 1.5, particleZ, + Wizardry.proxy.spawnParticle(EnumParticleType.SPARKLE, world, particleX, WizardryUtilities.getPlayerEyesPos(caster) - 1.5, particleZ, particleX - caster.posX, 0, particleZ - caster.posZ, 30, 0.9f, 0.9f, 0.9f); particleX = caster.posX - 1.0d + 2*world.rand.nextDouble(); particleZ = caster.posZ - 1.0d + 2*world.rand.nextDouble(); - IBlockState block = WizardryUtilities.getBlockEntityIsStandingOn(caster); - + Block block = WizardryUtilities.getBlockEntityIsStandingOn(caster); + // Player actual eye height is 1.62, client is 1.5 too high, hence the -1.5. if(block != null){ - world.spawnParticle(EnumParticleTypes.BLOCK_DUST, particleX, caster.getEntityBoundingBox().minY, particleZ, - particleX - caster.posX, 0, particleZ - caster.posZ, Block.getStateId(block)); + Wizardry.proxy.spawnDigParticle(world, particleX, WizardryUtilities.getPlayerEyesPos(caster) - 1.5, particleZ, particleX - caster.posX, 0, particleZ - caster.posZ, + block); } } - - world.spawnParticle(EnumParticleTypes.EXPLOSION_LARGE, caster.posX, caster.getEntityBoundingBox().minY + 0.1, caster.posZ, 0, 0, 0); - } - caster.swingArm(hand); - WizardryUtilities.playSoundAtPlayer(caster, WizardrySounds.SPELL_SHOCKWAVE, 1.0f, 0.7f); - WizardryUtilities.playSoundAtPlayer(caster, WizardrySounds.SPELL_SHOCKWAVE, 2.0f, 0.3f); + caster.swingItem(); + world.playSoundAtEntity(caster, "wizardry:boom", 1.0f, 0.7f); + world.playSoundAtEntity(caster, "wizardry:boom", 2.0f, 0.3f); return true; } diff --git a/src/main/java/electroblob/wizardry/spell/SilverfishSwarm.java b/src/main/java/electroblob/wizardry/spell/SilverfishSwarm.java index bd7ae7bc..424ac923 100644 --- a/src/main/java/electroblob/wizardry/spell/SilverfishSwarm.java +++ b/src/main/java/electroblob/wizardry/spell/SilverfishSwarm.java @@ -1,23 +1,17 @@ package electroblob.wizardry.spell; -import electroblob.wizardry.constants.Element; -import electroblob.wizardry.constants.SpellType; -import electroblob.wizardry.constants.Tier; +import electroblob.wizardry.EnumElement; +import electroblob.wizardry.EnumSpellType; +import electroblob.wizardry.EnumTier; import electroblob.wizardry.entity.living.EntitySilverfishMinion; -import electroblob.wizardry.registry.WizardryItems; -import electroblob.wizardry.util.SpellModifiers; -import electroblob.wizardry.util.WizardryUtilities; import net.minecraft.entity.player.EntityPlayer; -import net.minecraft.init.SoundEvents; import net.minecraft.item.EnumAction; -import net.minecraft.util.EnumHand; -import net.minecraft.util.math.BlockPos; import net.minecraft.world.World; public class SilverfishSwarm extends Spell { public SilverfishSwarm() { - super(Tier.MASTER, 80, Element.EARTH, "silverfish_swarm", SpellType.MINION, 300, EnumAction.BOW, false); + super(EnumTier.MASTER, 80, EnumElement.EARTH, "silverfish_swarm", EnumSpellType.MINION, 300, EnumAction.bow, false); } @Override @@ -26,22 +20,30 @@ public class SilverfishSwarm extends Spell { } @Override - public boolean cast(World world, EntityPlayer caster, EnumHand hand, int ticksInUse, SpellModifiers modifiers) { - + public boolean cast(World world, EntityPlayer caster, int ticksInUse, float damageMultiplier, float rangeMultiplier, float durationMultiplier, float blastMultiplier) { + + boolean flag = false; + if(!world.isRemote){ for(int i=0;i<20;i++){ - BlockPos pos = WizardryUtilities.findNearbyFloorSpace(caster, 3, 6); - // The spell instantly fails if no space was found (see javadoc for the above method). - if(pos == null) return false; - - EntitySilverfishMinion silverfish = new EntitySilverfishMinion(world, pos.getX() + 0.5, pos.getY(), pos.getZ() + 0.5, caster, (int)(600*modifiers.get(WizardryItems.duration_upgrade))); - world.spawnEntityInWorld(silverfish); + double x = caster.posX + world.rand.nextDouble()*6 - 3; + double z = caster.posZ + world.rand.nextDouble()*6 - 3; + // Allows for height variation. + if(world.getTopSolidOrLiquidBlock((int)x, (int)z) - caster.posY < 6){ + flag = true; + double y = Math.max(caster.posY, world.getTopSolidOrLiquidBlock((int)x, (int)z)); + EntitySilverfishMinion silverfish = new EntitySilverfishMinion(world, x, y, z, caster, (int)(600*durationMultiplier)); + world.spawnEntityInWorld(silverfish); + } } } - WizardryUtilities.playSoundAtPlayer(caster, SoundEvents.BLOCK_FIRE_EXTINGUISH, 1.0F, world.rand.nextFloat() * 0.2F + 1.0F); - // Can't possibly get this far if nothing was spawned. - return true; + if(flag){ + world.playSoundAtEntity(caster, "random.fizz", 1.0F, world.rand.nextFloat() * 0.2F + 1.0F); + } + // If no spiders were spawned (like in a 1x1 hole or something) then the spell does not use up mana. + return flag; } + } diff --git a/src/main/java/electroblob/wizardry/spell/SixthSense.java b/src/main/java/electroblob/wizardry/spell/SixthSense.java index f6505a75..9f221828 100644 --- a/src/main/java/electroblob/wizardry/spell/SixthSense.java +++ b/src/main/java/electroblob/wizardry/spell/SixthSense.java @@ -1,24 +1,19 @@ package electroblob.wizardry.spell; -import electroblob.wizardry.constants.Constants; -import electroblob.wizardry.constants.Element; -import electroblob.wizardry.constants.SpellType; -import electroblob.wizardry.constants.Tier; -import electroblob.wizardry.registry.WizardryItems; -import electroblob.wizardry.registry.WizardryPotions; -import electroblob.wizardry.util.SpellModifiers; -import electroblob.wizardry.util.WizardryUtilities; +import electroblob.wizardry.EnumElement; +import electroblob.wizardry.EnumSpellType; +import electroblob.wizardry.EnumTier; +import electroblob.wizardry.ExtendedPlayer; +import electroblob.wizardry.Wizardry; import net.minecraft.entity.player.EntityPlayer; -import net.minecraft.init.SoundEvents; import net.minecraft.item.EnumAction; import net.minecraft.potion.PotionEffect; -import net.minecraft.util.EnumHand; import net.minecraft.world.World; public class SixthSense extends Spell { public SixthSense() { - super(Tier.APPRENTICE, 20, Element.EARTH, "sixth_sense", SpellType.UTILITY, 100, EnumAction.BOW, false); + super(EnumTier.APPRENTICE, 20, EnumElement.EARTH, "sixth_sense", EnumSpellType.UTILITY, 100, EnumAction.bow, false); } @Override @@ -27,13 +22,15 @@ public class SixthSense extends Spell { } @Override - public boolean cast(World world, EntityPlayer caster, EnumHand hand, int ticksInUse, SpellModifiers modifiers) { - + public boolean cast(World world, EntityPlayer caster, int ticksInUse, float damageMultiplier, float rangeMultiplier, float durationMultiplier, float blastMultiplier) { + + ExtendedPlayer properties = ExtendedPlayer.get(caster); + // Cannot be cast when it has already been cast if(!world.isRemote){ - caster.addPotionEffect(new PotionEffect(WizardryPotions.sixth_sense, (int)(400*modifiers.get(WizardryItems.duration_upgrade)), (int)((modifiers.get(WizardryItems.range_upgrade)-1f)/Constants.RANGE_INCREASE_PER_LEVEL))); + caster.addPotionEffect(new PotionEffect(Wizardry.sixthSense.id, (int)(400*durationMultiplier), (int)((rangeMultiplier-1f)/Wizardry.RANGE_INCREASE_PER_LEVEL), true)); + world.playSoundAtEntity(caster, "mob.wither.shoot", 1.0F, world.rand.nextFloat() * 0.2F + 1.0F); } - WizardryUtilities.playSoundAtPlayer(caster, SoundEvents.ENTITY_WITHER_SHOOT, 1.0F, world.rand.nextFloat() * 0.2F + 1.0F); return true; } diff --git a/src/main/java/electroblob/wizardry/spell/Slime.java b/src/main/java/electroblob/wizardry/spell/Slime.java index 6d9d5346..5311b535 100644 --- a/src/main/java/electroblob/wizardry/spell/Slime.java +++ b/src/main/java/electroblob/wizardry/spell/Slime.java @@ -1,84 +1,81 @@ package electroblob.wizardry.spell; +import electroblob.wizardry.EnumElement; +import electroblob.wizardry.EnumParticleType; +import electroblob.wizardry.EnumSpellType; +import electroblob.wizardry.EnumTier; import electroblob.wizardry.Wizardry; -import electroblob.wizardry.constants.Element; -import electroblob.wizardry.constants.SpellType; -import electroblob.wizardry.constants.Tier; -import electroblob.wizardry.entity.living.EntityMagicSlime; -import electroblob.wizardry.registry.WizardryAchievements; -import electroblob.wizardry.registry.WizardryItems; -import electroblob.wizardry.registry.WizardrySounds; -import electroblob.wizardry.util.SpellModifiers; -import electroblob.wizardry.util.WizardryParticleType; -import electroblob.wizardry.util.WizardryUtilities; +import electroblob.wizardry.WizardryUtilities; +import electroblob.wizardry.entity.EntityMagicSlime; import net.minecraft.entity.EntityLiving; import net.minecraft.entity.EntityLivingBase; import net.minecraft.entity.monster.EntitySkeleton; import net.minecraft.entity.monster.EntitySlime; import net.minecraft.entity.player.EntityPlayer; -import net.minecraft.init.SoundEvents; import net.minecraft.item.EnumAction; -import net.minecraft.util.EnumHand; -import net.minecraft.util.EnumParticleTypes; -import net.minecraft.util.math.RayTraceResult; -import net.minecraft.util.math.Vec3d; -import net.minecraft.util.text.TextComponentTranslation; +import net.minecraft.util.ChatComponentTranslation; +import net.minecraft.util.MovingObjectPosition; +import net.minecraft.util.Vec3; import net.minecraft.world.World; public class Slime extends Spell { public Slime() { - super(Tier.ADVANCED, 20, Element.EARTH, "slime", SpellType.ATTACK, 50, EnumAction.NONE, false); + super(EnumTier.ADVANCED, 20, EnumElement.EARTH, "slime", EnumSpellType.ATTACK, 50, EnumAction.none, false); } @Override - public boolean cast(World world, EntityPlayer caster, EnumHand hand, int ticksInUse, SpellModifiers modifiers) { + public boolean cast(World world, EntityPlayer caster, int ticksInUse, float damageMultiplier, float rangeMultiplier, float durationMultiplier, float blastMultiplier) { - Vec3d look = caster.getLookVec(); + Vec3 look = caster.getLookVec(); - RayTraceResult rayTrace = WizardryUtilities.standardEntityRayTrace(world, caster, 8*modifiers.get(WizardryItems.range_upgrade)); + MovingObjectPosition rayTrace = WizardryUtilities.standardEntityRayTrace(world, caster, 8*rangeMultiplier); if(rayTrace != null && rayTrace.entityHit != null && rayTrace.entityHit instanceof EntityLivingBase){ EntityLivingBase target = (EntityLivingBase)rayTrace.entityHit; if(target instanceof EntitySlime){ - if(!world.isRemote) caster.addChatComponentMessage(new TextComponentTranslation("spell.resist", target.getName(), this.getNameForTranslationFormatted())); + if(!world.isRemote) caster.addChatComponentMessage(new ChatComponentTranslation("spell.resist", target.getCommandSenderName(), this.getDisplayNameWithFormatting())); }else if(!(target instanceof EntityMagicSlime)){ - if(target instanceof EntitySkeleton) caster.addStat(WizardryAchievements.slime_skeleton); + if(target instanceof EntitySkeleton) caster.triggerAchievement(Wizardry.slimeSkeleton); if(!world.isRemote){ - EntityMagicSlime slime = new EntityMagicSlime(world, caster, target, (int)(200*modifiers.get(WizardryItems.duration_upgrade))); + EntityMagicSlime slime = new EntityMagicSlime(world, (int)(200*durationMultiplier)); + slime.setPosition(target.posX, target.posY, target.posZ); + slime.mountEntity(target); world.spawnEntityInWorld(slime); } } } if(world.isRemote){ - for(int i=1; i<(int)(25*modifiers.get(WizardryItems.range_upgrade)); i+=2){ + for(int i=1; i<(int)(25*rangeMultiplier); i+=2){ double x1 = caster.posX + look.xCoord*i/2 + world.rand.nextFloat()/5 - 0.1f; double y1 = WizardryUtilities.getPlayerEyesPos(caster) - 0.4f + look.yCoord*i/2 + world.rand.nextFloat()/5 - 0.1f; double z1 = caster.posZ + look.zCoord*i/2 + world.rand.nextFloat()/5 - 0.1f; - world.spawnParticle(EnumParticleTypes.SLIME, x1, y1, z1, 0.0d, 0.0d, 0.0d); - Wizardry.proxy.spawnParticle(WizardryParticleType.DARK_MAGIC, world, x1, y1, z1, 0.0d, 0.0d, 0.0d, 0, 0.2f, 0.8f, 0.1f); + world.spawnParticle("slime", x1, y1, z1, 0.0d, 0.0d, 0.0d); + Wizardry.proxy.spawnParticle(EnumParticleType.DARK_MAGIC, world, x1, y1, z1, 0.0d, 0.0d, 0.0d, 0, 0.2f, 0.8f, 0.1f); } } - caster.swingArm(hand); - WizardryUtilities.playSoundAtPlayer(caster, SoundEvents.ENTITY_SLIME_ATTACK, 1.0F, 0.5F); - WizardryUtilities.playSoundAtPlayer(caster, WizardrySounds.SPELL_ICE, 1.0F, 1.0F); + caster.swingItem(); + world.playSoundAtEntity(caster, "mob.slime.attack", 1.0F, 0.5F); + world.playSoundAtEntity(caster, "wizardry:ice", 1.0F, 1.0F); return true; } @Override - public boolean cast(World world, EntityLiving caster, EnumHand hand, int ticksInUse, EntityLivingBase target, SpellModifiers modifiers){ + public boolean cast(World world, EntityLiving caster, EntityLivingBase target, float damageMultiplier, float rangeMultiplier, float durationMultiplier, float blastMultiplier){ if(target != null && !(target instanceof EntitySlime) && !(target instanceof EntityMagicSlime)){ if(!world.isRemote){ - EntityMagicSlime slime = new EntityMagicSlime(world, caster, target, (int)(200*modifiers.get(WizardryItems.duration_upgrade))); + EntityMagicSlime slime = new EntityMagicSlime(world, (int)(200*durationMultiplier)); + slime.setPosition(target.posX, target.posY, target.posZ); + slime.mountEntity(target); world.spawnEntityInWorld(slime); } @@ -88,20 +85,20 @@ public class Slime extends Spell { double dy = (target.posY - caster.posY)/caster.getDistanceToEntity(target); double dz = (target.posZ - caster.posZ)/caster.getDistanceToEntity(target); - for(int i=1; i<(int)(25*modifiers.get(WizardryItems.range_upgrade)); i+=2){ + for(int i=1; i<(int)(25*rangeMultiplier); i+=2){ double x1 = caster.posX + dx*i/2 + world.rand.nextFloat()/5 - 0.1f; double y1 = caster.posY + caster.getEyeHeight() - 0.4f + dy*i/2 + world.rand.nextFloat()/5 - 0.1f; double z1 = caster.posZ + dz*i/2 + world.rand.nextFloat()/5 - 0.1f; - world.spawnParticle(EnumParticleTypes.SLIME, x1, y1, z1, 0.0d, 0.0d, 0.0d); - Wizardry.proxy.spawnParticle(WizardryParticleType.DARK_MAGIC, world, x1, y1, z1, 0.0d, 0.0d, 0.0d, 0, 0.2f, 0.8f, 0.1f); + world.spawnParticle("slime", x1, y1, z1, 0.0d, 0.0d, 0.0d); + Wizardry.proxy.spawnParticle(EnumParticleType.DARK_MAGIC, world, x1, y1, z1, 0.0d, 0.0d, 0.0d, 0, 0.2f, 0.8f, 0.1f); } } - caster.swingArm(hand); - caster.playSound(SoundEvents.ENTITY_SLIME_ATTACK, 1.0F, 0.5F); - caster.playSound(WizardrySounds.SPELL_ICE, 1.0F, 1.0F); + caster.swingItem(); + world.playSoundAtEntity(caster, "mob.slime.attack", 1.0F, 0.5F); + world.playSoundAtEntity(caster, "wizardry:ice", 1.0F, 1.0F); return true; } diff --git a/src/main/java/electroblob/wizardry/spell/SmokeBomb.java b/src/main/java/electroblob/wizardry/spell/SmokeBomb.java index 6a71a532..2a49c833 100644 --- a/src/main/java/electroblob/wizardry/spell/SmokeBomb.java +++ b/src/main/java/electroblob/wizardry/spell/SmokeBomb.java @@ -1,24 +1,19 @@ package electroblob.wizardry.spell; -import electroblob.wizardry.constants.Element; -import electroblob.wizardry.constants.SpellType; -import electroblob.wizardry.constants.Tier; +import electroblob.wizardry.EnumElement; +import electroblob.wizardry.EnumSpellType; +import electroblob.wizardry.EnumTier; import electroblob.wizardry.entity.projectile.EntitySmokeBomb; -import electroblob.wizardry.registry.WizardryItems; -import electroblob.wizardry.util.SpellModifiers; -import electroblob.wizardry.util.WizardryUtilities; import net.minecraft.entity.EntityLiving; import net.minecraft.entity.EntityLivingBase; import net.minecraft.entity.player.EntityPlayer; -import net.minecraft.init.SoundEvents; import net.minecraft.item.EnumAction; -import net.minecraft.util.EnumHand; import net.minecraft.world.World; public class SmokeBomb extends Spell { public SmokeBomb() { - super(Tier.BASIC, 10, Element.FIRE, "smoke_bomb", SpellType.ATTACK, 20, EnumAction.NONE, false); + super(EnumTier.BASIC, 10, EnumElement.FIRE, "smoke_bomb", EnumSpellType.ATTACK, 20, EnumAction.none, false); } @Override @@ -27,31 +22,31 @@ public class SmokeBomb extends Spell { } @Override - public boolean cast(World world, EntityPlayer caster, EnumHand hand, int ticksInUse, SpellModifiers modifiers) { + public boolean cast(World world, EntityPlayer caster, int ticksInUse, float damageMultiplier, float rangeMultiplier, float durationMultiplier, float blastMultiplier) { if(!world.isRemote){ - EntitySmokeBomb smokebomb = new EntitySmokeBomb(world, caster, modifiers.get(SpellModifiers.DAMAGE), modifiers.get(WizardryItems.blast_upgrade)); + EntitySmokeBomb smokebomb = new EntitySmokeBomb(world, caster, damageMultiplier, blastMultiplier); world.spawnEntityInWorld(smokebomb); } - caster.swingArm(hand); - WizardryUtilities.playSoundAtPlayer(caster, SoundEvents.ENTITY_SNOWBALL_THROW, 0.5F, 0.4F / (world.rand.nextFloat() * 0.4F + 0.8F)); + caster.swingItem(); + world.playSoundAtEntity(caster, "random.bow", 0.5F, 0.4F / (world.rand.nextFloat() * 0.4F + 0.8F)); return true; } @Override - public boolean cast(World world, EntityLiving caster, EnumHand hand, int ticksInUse, EntityLivingBase target, SpellModifiers modifiers){ + public boolean cast(World world, EntityLiving caster, EntityLivingBase target, float damageMultiplier, float rangeMultiplier, float durationMultiplier, float blastMultiplier){ if(target != null){ if(!world.isRemote){ - EntitySmokeBomb smokebomb = new EntitySmokeBomb(world, caster, modifiers.get(SpellModifiers.DAMAGE), modifiers.get(WizardryItems.blast_upgrade)); + EntitySmokeBomb smokebomb = new EntitySmokeBomb(world, caster, damageMultiplier, blastMultiplier); smokebomb.directTowards(target, 1.5f); world.spawnEntityInWorld(smokebomb); } - caster.swingArm(hand); - caster.playSound(SoundEvents.ENTITY_SNOWBALL_THROW, 0.5F, 0.4F / (world.rand.nextFloat() * 0.4F + 0.8F)); + caster.swingItem(); + world.playSoundAtEntity(caster, "random.bow", 0.5F, 0.4F / (world.rand.nextFloat() * 0.4F + 0.8F)); return true; } diff --git a/src/main/java/electroblob/wizardry/spell/Snare.java b/src/main/java/electroblob/wizardry/spell/Snare.java index 6d1b4815..ca3389c0 100644 --- a/src/main/java/electroblob/wizardry/spell/Snare.java +++ b/src/main/java/electroblob/wizardry/spell/Snare.java @@ -1,61 +1,58 @@ package electroblob.wizardry.spell; +import electroblob.wizardry.EnumElement; +import electroblob.wizardry.EnumParticleType; +import electroblob.wizardry.EnumSpellType; +import electroblob.wizardry.EnumTier; import electroblob.wizardry.Wizardry; -import electroblob.wizardry.constants.Element; -import electroblob.wizardry.constants.SpellType; -import electroblob.wizardry.constants.Tier; -import electroblob.wizardry.registry.WizardryBlocks; -import electroblob.wizardry.registry.WizardryItems; +import electroblob.wizardry.WizardryUtilities; import electroblob.wizardry.tileentity.TileEntityPlayerSave; -import electroblob.wizardry.util.SpellModifiers; -import electroblob.wizardry.util.WizardryParticleType; -import electroblob.wizardry.util.WizardryUtilities; import net.minecraft.entity.player.EntityPlayer; -import net.minecraft.init.SoundEvents; import net.minecraft.item.EnumAction; -import net.minecraft.util.EnumFacing; -import net.minecraft.util.EnumHand; -import net.minecraft.util.math.BlockPos; -import net.minecraft.util.math.RayTraceResult; +import net.minecraft.util.MovingObjectPosition; +import net.minecraft.util.MovingObjectPosition.MovingObjectType; import net.minecraft.world.World; +import net.minecraftforge.common.util.ForgeDirection; public class Snare extends Spell { public Snare() { - super(Tier.BASIC, 10, Element.EARTH, "snare", SpellType.ATTACK, 10, EnumAction.NONE, false); + super(EnumTier.BASIC, 10, EnumElement.EARTH, "snare", EnumSpellType.ATTACK, 10, EnumAction.none, false); } @Override - public boolean cast(World world, EntityPlayer caster, EnumHand hand, int ticksInUse, SpellModifiers modifiers) { + public boolean cast(World world, EntityPlayer caster, int ticksInUse, float damageMultiplier, float rangeMultiplier, float durationMultiplier, float blastMultiplier) { - RayTraceResult rayTrace = WizardryUtilities.rayTrace(10*modifiers.get(WizardryItems.range_upgrade), world, caster, true); + MovingObjectPosition rayTrace = WizardryUtilities.rayTrace(10*rangeMultiplier, world, caster, true); // Gets block the player is looking at and places snare - if(rayTrace != null && rayTrace.typeOfHit == RayTraceResult.Type.BLOCK){ + if(rayTrace != null && rayTrace.typeOfHit == MovingObjectType.BLOCK){ - BlockPos pos = rayTrace.getBlockPos(); + int blockHitX = rayTrace.blockX; + int blockHitY = rayTrace.blockY; + int blockHitZ = rayTrace.blockZ; + int blockHitSide = rayTrace.sideHit; - if(rayTrace.sideHit == EnumFacing.UP && world.isSideSolid(pos, EnumFacing.UP) && WizardryUtilities.canBlockBeReplaced(world, pos.up())){ + if(blockHitSide == 1 && world.isSideSolid(blockHitX, blockHitY, blockHitZ, ForgeDirection.UP) && WizardryUtilities.canBlockBeReplaced(world, blockHitX, blockHitY+1, blockHitZ)){ if(!world.isRemote){ - world.setBlockState(pos.up(), WizardryBlocks.snare.getDefaultState()); - ((TileEntityPlayerSave)world.getTileEntity(pos.up())).setCaster(caster); + world.setBlock(blockHitX, blockHitY+1, blockHitZ, Wizardry.snare); + ((TileEntityPlayerSave)world.getTileEntity(blockHitX, blockHitY+1, blockHitZ)).setCaster(caster); } - double dx = pos.getX() + 0.5 - caster.posX; - double dy = pos.getY() + 1.5 - (caster.posY + caster.height/2); - double dz = pos.getZ() + 0.5 - caster.posZ; + double dx = blockHitX + 0.5 - caster.posX; + double dy = blockHitY + 1.5 - (caster.posY + caster.height/2); + double dz = blockHitZ + 0.5 - caster.posZ; if(world.isRemote){ for(int i=1;i<5;i++){ float brightness = world.rand.nextFloat()/4; - Wizardry.proxy.spawnParticle(WizardryParticleType.SPARKLE, world, caster.posX + (i*(dx/5)) + world.rand.nextFloat()/5, WizardryUtilities.getPlayerEyesPos(caster) + (i*(dy/5)) + world.rand.nextFloat()/5, caster.posZ + (i*(dz/5)) + world.rand.nextFloat()/5, 0.0d, 0.0d, 0.0d, 20 + world.rand.nextInt(8), brightness, brightness + 0.1f, 0.0f); - Wizardry.proxy.spawnParticle(WizardryParticleType.LEAF, world, caster.posX + (i*(dx/5)) + world.rand.nextFloat()/5, WizardryUtilities.getPlayerEyesPos(caster) + (i*(dy/5)) + world.rand.nextFloat()/5, caster.posZ + (i*(dz/5)) + world.rand.nextFloat()/5, 0, -0.01, 0, 40 + world.rand.nextInt(10)); + Wizardry.proxy.spawnParticle(EnumParticleType.SPARKLE, world, caster.posX + (i*(dx/5)) + world.rand.nextFloat()/5, WizardryUtilities.getPlayerEyesPos(caster) + (i*(dy/5)) + world.rand.nextFloat()/5, caster.posZ + (i*(dz/5)) + world.rand.nextFloat()/5, 0.0d, 0.0d, 0.0d, 20 + world.rand.nextInt(8), brightness, brightness + 0.1f, 0.0f); + Wizardry.proxy.spawnParticle(EnumParticleType.LEAF, world, caster.posX + (i*(dx/5)) + world.rand.nextFloat()/5, WizardryUtilities.getPlayerEyesPos(caster) + (i*(dy/5)) + world.rand.nextFloat()/5, caster.posZ + (i*(dz/5)) + world.rand.nextFloat()/5, 0, -0.01, 0, 40 + world.rand.nextInt(10)); } } - - caster.swingArm(hand); - WizardryUtilities.playSoundAtPlayer(caster, SoundEvents.BLOCK_GRASS_PLACE, 1.0F, world.rand.nextFloat() * 0.4F + 1.2F); + caster.swingItem(); + world.playSoundAtEntity(caster, "dig.grass", 1.0F, world.rand.nextFloat() * 0.4F + 1.2F); return true; } } diff --git a/src/main/java/electroblob/wizardry/spell/Snowball.java b/src/main/java/electroblob/wizardry/spell/Snowball.java index c5aed5ab..75c362a1 100644 --- a/src/main/java/electroblob/wizardry/spell/Snowball.java +++ b/src/main/java/electroblob/wizardry/spell/Snowball.java @@ -1,21 +1,18 @@ package electroblob.wizardry.spell; -import electroblob.wizardry.constants.Element; -import electroblob.wizardry.constants.SpellType; -import electroblob.wizardry.constants.Tier; -import electroblob.wizardry.util.SpellModifiers; -import electroblob.wizardry.util.WizardryUtilities; +import electroblob.wizardry.EnumElement; +import electroblob.wizardry.EnumSpellType; +import electroblob.wizardry.EnumTier; import net.minecraft.entity.player.EntityPlayer; import net.minecraft.entity.projectile.EntitySnowball; -import net.minecraft.init.SoundEvents; import net.minecraft.item.EnumAction; -import net.minecraft.util.EnumHand; +import net.minecraft.util.Vec3; import net.minecraft.world.World; public class Snowball extends Spell { public Snowball() { - super(Tier.BASIC, 1, Element.ICE, "snowball", SpellType.ATTACK, 1, EnumAction.NONE, false); + super(EnumTier.BASIC, 1, EnumElement.ICE, "snowball", EnumSpellType.ATTACK, 1, EnumAction.none, false); } @Override @@ -24,16 +21,23 @@ public class Snowball extends Spell { } @Override - public boolean cast(World world, EntityPlayer caster, EnumHand hand, int ticksInUse, SpellModifiers modifiers) { - + public boolean cast(World world, EntityPlayer caster, int ticksInUse, float damageMultiplier, float rangeMultiplier, float durationMultiplier, float blastMultiplier) { + + Vec3 look = caster.getLookVec(); + if(!world.isRemote){ + EntitySnowball snowball = new EntitySnowball(world, caster); - snowball.setHeadingFromThrower(caster, caster.rotationPitch, caster.rotationYaw, 0.0f, 1.5f, 1.0f); + + //snowball.motionX = look.xCoord * 1.5; + //snowball.motionY = look.yCoord * 1.5; + //snowball.motionZ = look.zCoord * 1.5; + world.spawnEntityInWorld(snowball); } - WizardryUtilities.playSoundAtPlayer(caster, SoundEvents.ENTITY_SNOWBALL_THROW, 0.5F, 0.4F / (world.rand.nextFloat() * 0.4F + 0.8F)); - caster.swingArm(hand); + world.playSoundAtEntity(caster, "random.bow", 0.5F, 0.4F / (world.rand.nextFloat() * 0.4F + 0.8F)); + caster.swingItem(); return true; } diff --git a/src/main/java/electroblob/wizardry/spell/SparkBomb.java b/src/main/java/electroblob/wizardry/spell/SparkBomb.java index e0c55295..721c5661 100644 --- a/src/main/java/electroblob/wizardry/spell/SparkBomb.java +++ b/src/main/java/electroblob/wizardry/spell/SparkBomb.java @@ -1,24 +1,19 @@ package electroblob.wizardry.spell; -import electroblob.wizardry.constants.Element; -import electroblob.wizardry.constants.SpellType; -import electroblob.wizardry.constants.Tier; +import electroblob.wizardry.EnumElement; +import electroblob.wizardry.EnumSpellType; +import electroblob.wizardry.EnumTier; import electroblob.wizardry.entity.projectile.EntitySparkBomb; -import electroblob.wizardry.registry.WizardryItems; -import electroblob.wizardry.util.SpellModifiers; -import electroblob.wizardry.util.WizardryUtilities; import net.minecraft.entity.EntityLiving; import net.minecraft.entity.EntityLivingBase; import net.minecraft.entity.player.EntityPlayer; -import net.minecraft.init.SoundEvents; import net.minecraft.item.EnumAction; -import net.minecraft.util.EnumHand; import net.minecraft.world.World; public class SparkBomb extends Spell { public SparkBomb() { - super(Tier.APPRENTICE, 15, Element.LIGHTNING, "spark_bomb", SpellType.ATTACK, 25, EnumAction.NONE, false); + super(EnumTier.APPRENTICE, 15, EnumElement.LIGHTNING, "spark_bomb", EnumSpellType.ATTACK, 25, EnumAction.none, false); } @Override @@ -27,31 +22,31 @@ public class SparkBomb extends Spell { } @Override - public boolean cast(World world, EntityPlayer caster, EnumHand hand, int ticksInUse, SpellModifiers modifiers) { + public boolean cast(World world, EntityPlayer caster, int ticksInUse, float damageMultiplier, float rangeMultiplier, float durationMultiplier, float blastMultiplier) { if(!world.isRemote){ - EntitySparkBomb sparkBomb = new EntitySparkBomb(world, caster, modifiers.get(SpellModifiers.DAMAGE), modifiers.get(WizardryItems.blast_upgrade)); + EntitySparkBomb sparkBomb = new EntitySparkBomb(world, caster, damageMultiplier, blastMultiplier); world.spawnEntityInWorld(sparkBomb); } - caster.swingArm(hand); - WizardryUtilities.playSoundAtPlayer(caster, SoundEvents.ENTITY_SNOWBALL_THROW, 0.5F, 0.4F / (world.rand.nextFloat() * 0.4F + 0.8F)); + caster.swingItem(); + world.playSoundAtEntity(caster, "random.bow", 0.5F, 0.4F / (world.rand.nextFloat() * 0.4F + 0.8F)); return true; } @Override - public boolean cast(World world, EntityLiving caster, EnumHand hand, int ticksInUse, EntityLivingBase target, SpellModifiers modifiers){ + public boolean cast(World world, EntityLiving caster, EntityLivingBase target, float damageMultiplier, float rangeMultiplier, float durationMultiplier, float blastMultiplier){ if(target != null){ if(!world.isRemote){ - EntitySparkBomb sparkBomb = new EntitySparkBomb(world, caster, modifiers.get(SpellModifiers.DAMAGE), modifiers.get(WizardryItems.blast_upgrade)); + EntitySparkBomb sparkBomb = new EntitySparkBomb(world, caster, damageMultiplier, blastMultiplier); sparkBomb.directTowards(target, 1.5f); world.spawnEntityInWorld(sparkBomb); } - caster.swingArm(hand); - caster.playSound(SoundEvents.ENTITY_SNOWBALL_THROW, 0.5F, 0.4F / (world.rand.nextFloat() * 0.4F + 0.8F)); + caster.swingItem(); + world.playSoundAtEntity(caster, "random.bow", 0.5F, 0.4F / (world.rand.nextFloat() * 0.4F + 0.8F)); return true; } diff --git a/src/main/java/electroblob/wizardry/spell/SpectralPathway.java b/src/main/java/electroblob/wizardry/spell/SpectralPathway.java index f544ffb3..1de47196 100644 --- a/src/main/java/electroblob/wizardry/spell/SpectralPathway.java +++ b/src/main/java/electroblob/wizardry/spell/SpectralPathway.java @@ -1,27 +1,21 @@ package electroblob.wizardry.spell; -import electroblob.wizardry.constants.Element; -import electroblob.wizardry.constants.SpellType; -import electroblob.wizardry.constants.Tier; -import electroblob.wizardry.registry.WizardryBlocks; -import electroblob.wizardry.registry.WizardryItems; -import electroblob.wizardry.registry.WizardrySounds; +import electroblob.wizardry.EnumElement; +import electroblob.wizardry.EnumSpellType; +import electroblob.wizardry.EnumTier; +import electroblob.wizardry.Wizardry; +import electroblob.wizardry.WizardryUtilities; import electroblob.wizardry.tileentity.TileEntityTimer; -import electroblob.wizardry.util.SpellModifiers; -import electroblob.wizardry.util.WizardryUtilities; import net.minecraft.entity.player.EntityPlayer; import net.minecraft.init.Blocks; import net.minecraft.item.EnumAction; -import net.minecraft.util.EnumFacing; -import net.minecraft.util.EnumFacing.AxisDirection; -import net.minecraft.util.EnumHand; -import net.minecraft.util.math.BlockPos; +import net.minecraft.util.MathHelper; import net.minecraft.world.World; public class SpectralPathway extends Spell { public SpectralPathway() { - super(Tier.ADVANCED, 40, Element.SORCERY, "spectral_pathway", SpellType.UTILITY, 300, EnumAction.BOW, false); + super(EnumTier.ADVANCED, 40, EnumElement.SORCERY, "spectral_pathway", EnumSpellType.UTILITY, 300, EnumAction.bow, false); } @Override @@ -30,15 +24,31 @@ public class SpectralPathway extends Spell { } @Override - public boolean cast(World world, EntityPlayer caster, EnumHand hand, int ticksInUse, SpellModifiers modifiers) { + public boolean cast(World world, EntityPlayer caster, int ticksInUse, float damageMultiplier, float rangeMultiplier, float durationMultiplier, float blastMultiplier) { // Won't work if caster is airborne or if they are already on a bridge (prevents infinite bridges) - if(WizardryUtilities.getBlockEntityIsStandingOn(caster).getBlock() == Blocks.AIR - || WizardryUtilities.getBlockEntityIsStandingOn(caster).getBlock() == WizardryBlocks.spectral_block){ + if(WizardryUtilities.getBlockEntityIsStandingOn(caster) == Blocks.air + || WizardryUtilities.getBlockEntityIsStandingOn(caster) == Wizardry.spectralBlock){ return false; } - EnumFacing direction = caster.getHorizontalFacing(); + // Changes the yaw to 0, 1, 2 or 3 (compass directions) + //int direction = 0; + /*= (int)((entityplayer.rotationYawHead-45)/90); + if(direction == 4) direction = 0;*/ + /* + if(caster.rotationYawHead <= 45 || caster.rotationYawHead > 315){ + direction = 3; + }else if(caster.rotationYawHead > 45 && caster.rotationYawHead <= 135){ + direction = 0; + }else if(caster.rotationYawHead > 135 && caster.rotationYawHead <= 225){ + direction = 1; + }else if(caster.rotationYawHead > 225 && caster.rotationYawHead <= 315){ + direction = 2; + } + */ + + int direction = MathHelper.floor_double((double)(caster.rotationYaw * 4.0F / 360.0F) + 0.5D) & 3; boolean flag = false; @@ -46,35 +56,87 @@ public class SpectralPathway extends Spell { int baseLength = 15; - // Gets the coordinates of the nearest block intersection to the player's feet. - // Remember that a block always takes the coordinates of its northwestern corner. - BlockPos origin = new BlockPos(Math.round(caster.posX), (int)caster.getEntityBoundingBox().minY-1, Math.round(caster.posZ)); - - int startPoint = direction.getAxisDirection() == AxisDirection.POSITIVE ? -1 : 0; - - for(int i=0; i<(int)(baseLength*modifiers.get(WizardryItems.range_upgrade)); i++){ - // If either a block gets placed or one has already been placed, flag is set to true. - flag = placePathwayBlockIfPossible(world, origin.offset(direction, startPoint + i), modifiers.get(WizardryItems.duration_upgrade)) || flag; - flag = placePathwayBlockIfPossible(world, origin.offset(direction, startPoint + i) - // Moves the BlockPos minus one block perpendicular to direction. - .offset(EnumFacing.getFacingFromAxis(AxisDirection.NEGATIVE, direction.rotateY().getAxis())), modifiers.get(WizardryItems.duration_upgrade)) || flag; + //entityplayer.addChatMessage("Direction: " + direction); + switch(direction){ + case 0: + for(int i=0; i<(int)(baseLength*rangeMultiplier); i++){ + if(WizardryUtilities.canBlockBeReplacedB(world, (int)(caster.posX+0.5) - 1, (int)caster.posY-1, (int)(caster.posZ+0.5) + i - 1)){ + world.setBlock((int)(caster.posX+0.5) - 1, (int)caster.posY-1, (int)(caster.posZ+0.5) + i - 1, Wizardry.spectralBlock); + if(world.getTileEntity((int)(caster.posX+0.5) - 1, (int)caster.posY-1, (int)(caster.posZ+0.5) + i - 1) instanceof TileEntityTimer){ + ((TileEntityTimer)world.getTileEntity((int)(caster.posX+0.5) - 1, (int)caster.posY-1, (int)(caster.posZ+0.5) + i - 1)).setLifetime((int)(1200*durationMultiplier)); + } + flag = true; + } + if(WizardryUtilities.canBlockBeReplacedB(world, (int)(caster.posX+0.5) - 2, (int)caster.posY-1, (int)(caster.posZ+0.5) + i - 1)){ + world.setBlock((int)(caster.posX+0.5) - 2, (int)caster.posY-1, (int)(caster.posZ+0.5) + i - 1, Wizardry.spectralBlock); + if(world.getTileEntity((int)(caster.posX+0.5) - 2, (int)caster.posY-1, (int)(caster.posZ+0.5) + i - 1) instanceof TileEntityTimer){ + ((TileEntityTimer)world.getTileEntity((int)(caster.posX+0.5) - 2, (int)caster.posY-1, (int)(caster.posZ+0.5) + i - 1)).setLifetime((int)(1200*durationMultiplier)); + } + flag = true; + } + } + break; + case 1: + for(int i=0; i<(int)(baseLength*rangeMultiplier); i++){ + if(WizardryUtilities.canBlockBeReplacedB(world, (int)(caster.posX+0.5) - i - 1, (int)caster.posY-1, (int)(caster.posZ+0.5) - 1)){ + world.setBlock((int)(caster.posX+0.5) - i - 1, (int)caster.posY-1, (int)(caster.posZ+0.5) - 1, Wizardry.spectralBlock); + if(world.getTileEntity((int)(caster.posX+0.5) - i - 1, (int)caster.posY-1, (int)(caster.posZ+0.5) - 1) instanceof TileEntityTimer){ + ((TileEntityTimer)world.getTileEntity((int)(caster.posX+0.5) - i - 1, (int)caster.posY-1, (int)(caster.posZ+0.5) - 1)).setLifetime((int)(1200*durationMultiplier)); + } + flag = true; + } + if(WizardryUtilities.canBlockBeReplacedB(world, (int)(caster.posX+0.5) - i - 1, (int)caster.posY-1, (int)(caster.posZ+0.5))){ + world.setBlock((int)(caster.posX+0.5) - i - 1, (int)caster.posY-1, (int)(caster.posZ+0.5), Wizardry.spectralBlock); + if(world.getTileEntity((int)(caster.posX+0.5) - i - 1, (int)caster.posY-1, (int)(caster.posZ+0.5)) instanceof TileEntityTimer){ + ((TileEntityTimer)world.getTileEntity((int)(caster.posX+0.5) - i - 1, (int)caster.posY-1, (int)(caster.posZ+0.5))).setLifetime((int)(1200*durationMultiplier)); + } + flag = true; + } + } + break; + case 2: + for(int i=0; i<(int)(baseLength*rangeMultiplier); i++){ + if(WizardryUtilities.canBlockBeReplacedB(world, (int)(caster.posX+0.5) - 1, (int)caster.posY-1, (int)(caster.posZ+0.5) - i)){ + world.setBlock((int)(caster.posX+0.5) - 1, (int)caster.posY-1, (int)(caster.posZ+0.5) - i, Wizardry.spectralBlock); + if(world.getTileEntity((int)(caster.posX+0.5) - 1, (int)caster.posY-1, (int)(caster.posZ+0.5) - i) instanceof TileEntityTimer){ + ((TileEntityTimer)world.getTileEntity((int)(caster.posX+0.5) - 1, (int)caster.posY-1, (int)(caster.posZ+0.5) - i)).setLifetime((int)(1200*durationMultiplier)); + } + flag = true; + } + if(WizardryUtilities.canBlockBeReplacedB(world, (int)(caster.posX+0.5) - 2, (int)caster.posY-1, (int)(caster.posZ+0.5) - i)){ + world.setBlock((int)(caster.posX+0.5) - 2, (int)caster.posY-1, (int)(caster.posZ+0.5) - i, Wizardry.spectralBlock); + if(world.getTileEntity((int)(caster.posX+0.5) - 2, (int)caster.posY-1, (int)(caster.posZ+0.5) - i) instanceof TileEntityTimer){ + ((TileEntityTimer)world.getTileEntity((int)(caster.posX+0.5) - 2, (int)caster.posY-1, (int)(caster.posZ+0.5) - i)).setLifetime((int)(1200*durationMultiplier)); + } + flag = true; + } + } + break; + case 3: + for(int i=0; i<(int)(baseLength*rangeMultiplier); i++){ + if(WizardryUtilities.canBlockBeReplacedB(world, (int)(caster.posX+0.5) + i - 2, (int)caster.posY-1, (int)(caster.posZ+0.5) - 1)){ + world.setBlock((int)(caster.posX+0.5) + i - 2, (int)caster.posY-1, (int)(caster.posZ+0.5) - 1, Wizardry.spectralBlock); + if(world.getTileEntity((int)(caster.posX+0.5) + i - 2, (int)caster.posY-1, (int)(caster.posZ+0.5) - 1) instanceof TileEntityTimer){ + ((TileEntityTimer)world.getTileEntity((int)(caster.posX+0.5) + i - 2, (int)caster.posY-1, (int)(caster.posZ+0.5) - 1)).setLifetime((int)(1200*durationMultiplier)); + } + flag = true; + } + if(WizardryUtilities.canBlockBeReplacedB(world, (int)(caster.posX+0.5) + i - 2, (int)caster.posY-1, (int)(caster.posZ+0.5))){ + world.setBlock((int)(caster.posX+0.5) + i - 2, (int)caster.posY-1, (int)(caster.posZ+0.5), Wizardry.spectralBlock); + if(world.getTileEntity((int)(caster.posX+0.5) + i - 2, (int)caster.posY-1, (int)(caster.posZ+0.5)) instanceof TileEntityTimer){ + ((TileEntityTimer)world.getTileEntity((int)(caster.posX+0.5) + i - 2, (int)caster.posY-1, (int)(caster.posZ+0.5))).setLifetime((int)(1200*durationMultiplier)); + } + flag = true; + } + } + break; } } - // TODO: There may be some client/server discrepancies here. - WizardryUtilities.playSoundAtPlayer(caster, WizardrySounds.SPELL_CONJURATION_LARGE, 1.0f, 1.0f); + + if(flag) world.playSoundAtEntity(caster, "wizardry:largeaura", 1.0f, 1.0f); return flag; } - private static boolean placePathwayBlockIfPossible(World world, BlockPos pos, float durationMultiplier){ - if(WizardryUtilities.canBlockBeReplacedB(world, pos)){ - world.setBlockState(pos, WizardryBlocks.spectral_block.getDefaultState()); - if(world.getTileEntity(pos) instanceof TileEntityTimer){ - ((TileEntityTimer)world.getTileEntity(pos)).setLifetime((int)(1200*durationMultiplier)); - } - return true; - } - return false; - } - + } diff --git a/src/main/java/electroblob/wizardry/spell/Spell.java b/src/main/java/electroblob/wizardry/spell/Spell.java index 594f1ef2..adfb9c67 100644 --- a/src/main/java/electroblob/wizardry/spell/Spell.java +++ b/src/main/java/electroblob/wizardry/spell/Spell.java @@ -1,50 +1,39 @@ package electroblob.wizardry.spell; import java.util.ArrayList; -import java.util.HashSet; +import java.util.Collections; import java.util.List; -import java.util.Set; -import java.util.function.Predicate; +import electroblob.wizardry.EnumElement; +import electroblob.wizardry.EnumSpellType; +import electroblob.wizardry.EnumTier; import electroblob.wizardry.Wizardry; -import electroblob.wizardry.constants.Element; -import electroblob.wizardry.constants.SpellType; -import electroblob.wizardry.constants.Tier; +import electroblob.wizardry.WizardryRegistry; import electroblob.wizardry.entity.living.EntityWizard; -import electroblob.wizardry.registry.Spells; -import electroblob.wizardry.util.SpellModifiers; import net.minecraft.entity.EntityLiving; import net.minecraft.entity.EntityLivingBase; import net.minecraft.entity.player.EntityPlayer; import net.minecraft.item.EnumAction; -import net.minecraft.util.EnumHand; import net.minecraft.util.ResourceLocation; -import net.minecraft.util.text.ITextComponent; -import net.minecraft.util.text.TextComponentTranslation; +import net.minecraft.util.StatCollector; import net.minecraft.world.World; -import net.minecraftforge.event.RegistryEvent; -import net.minecraftforge.fml.common.registry.IForgeRegistry; -import net.minecraftforge.fml.common.registry.IForgeRegistryEntry; -import net.minecraftforge.fml.relauncher.Side; -import net.minecraftforge.fml.relauncher.SideOnly; -/** - * Generic spell class which is the superclass to all spells in wizardry. When extending this class, you must do +/** Generic spell class which is the superclass to all spells in wizardry. When extending this class, you must do * the following: *

    * - Have a constructor which passes all necessary constants into the super constructor. I define the constants here * so that the constructor for an individual spell has no parameters, but you may prefer to pass in the parameters when * the spell is registered, so all the mana costs etc. are in one place like a sort of sandbox. *

    - * - Implement the {@link Spell#cast(World, EntityPlayer, EnumHand, int, SpellModifiers)} method, in which you should + * - Implement the {@link Spell#cast(World, EntityPlayer, int, float, float, float, float)} method, in which you should * execute the code that makes the spell work, and return true or false depending on whether the spell succeeded and * therefore whether mana should be used up. *

    - * - Register the spell using {@link RegistryEvent.Register}, with {@link Spell} as the type parameter. Each spell - * should have a single instance, like blocks and items. - * As of Wizardry 2.1, spells use the Forge registry system. Related methods such as {@link Spell#id()} and - * {@link Spell#get(int)} have been re-routed to use this system, leaving minimal external changes. Note also that - * the constructor automatically sets the registry name for you, though you may change it afterwards if necessary. + * - Register the spell using {@link Spell#register(Spell)} during the init() method of your main mod class. Each spell + * should have a single instance, like blocks and items. Unlike with blocks and items however, it is usually not + * necessary to keep a reference to this instance unless something specific requires it (for example, the + * {@link Transportation} spell instance is stored in {@link WizardryRegistry} so that its name can be used in chat + * messages). *

    * Also note that you can override some other methods from this class. For example, to add a * specific kind of formatting to a spell name or description, you can override {@link Spell#getDisplayName()}, @@ -55,33 +44,38 @@ import net.minecraftforge.fml.relauncher.SideOnly; * This class is also home to some useful static methods for interacting with the spell registry:

    * {@link Spell#get(int)} gets a spell instance from its integer id, which corresponds to the metadata of its spell book.
    * {@link Spell#get(String)} gets a spell instance from its unlocalised name.
    - * {@link Spell#getSpells(Predicate)} returns a list of spell instances the match the given {@link Predicate}.
    + * {@link Spell#getSpells(Filter)} returns a list of spell instances the match the given {@link Filter}. * {@link Spell#getTotalSpellCount()} returns the total number of registered spells. *


    * Spell implements the {@link Comparable} interface, and as such, any collection of spells can be sorted. Spells are - * sorted by increasing tier (see {@link Tier}), from novice to master. Within each tier, spells are sorted by element, - * the order of which is as defined in {@link Element} (i.e. magic, fire, ice, lightning, necromancy, earth, sorcery, healing). + * sorted by increasing tier (see {@link EnumTier}), from novice to master. Within each tier, spells are sorted by element, + * the order of which is as defined in {@link EnumElement} (i.e. magic, fire, ice, lightning, necromancy, earth, sorcery, healing). *
    * @since Wizardry 1.0 * @see electroblob.wizardry.item.ItemSpellBook ItemSpellBook * @see electroblob.wizardry.item.ItemScroll ItemScroll - * @see Spells + * @see WizardryRegistry */ -public abstract class Spell extends IForgeRegistryEntry.Impl implements Comparable { + +public abstract class Spell implements Comparable { - /** Forge registry-based replacement for the internal spells list. */ - public static IForgeRegistry registry; + /** The internal list of all registered spells. This is deliberately made private since it shouldn't be fiddled with. */ + private static ArrayList spellsList = new ArrayList(1); + + /** The internal list of all registered spells' unlocalised names, used in commands. This is deliberately made + * private since it shouldn't be fiddled with. */ + private static ArrayList unlocalisedNames = new ArrayList(1); /** The tier this spell belongs to. */ - public final Tier tier; + public final EnumTier tier; /** Mana cost of the spell. If it is a continuous spell the cost is per second. */ public final int cost; /** The element this spell belongs to. */ - public final Element element; + public final EnumElement element; /** The unlocalised name of the spell. */ private final String unlocalisedName; /** The type of spell this is classified as. */ - public final SpellType type; + public final EnumSpellType type; /** Cooldown for the spell in ticks */ public final int cooldown; /** The action the player does when this spell is cast. */ @@ -89,10 +83,11 @@ public abstract class Spell extends IForgeRegistryEntry.Impl implements C /** Whether or not the spell is continuous (keeps going as long as the mouse button is held) */ public final boolean isContinuous; - /** ResourceLocation of the spell icon. */ - private final ResourceLocation icon; - /** Mod ID of the mod that added this spell; defaults to {@link Wizardry#MODID} if not specified. */ - private final String modID; + /** ResourceLocation of the spell icon. This is set up within the constructor; don't change it otherwise. */ + public ResourceLocation icon; + /** Mod ID of the mod that added this spell; defaults to Wizardry.MODID if not specified. This is set up within the + * constructor; don't change it otherwise. */ + public String modID; /** False if the spell has been disabled in the config file, true otherwise. This is now encapsulated to stop it * being fiddled with. */ @@ -100,21 +95,29 @@ public abstract class Spell extends IForgeRegistryEntry.Impl implements C /** * This constructor should be called from any subclasses, either feeding in the constants directly or through - * their own constructor from wherever the spell is registered. This is the constructor for wizardry's own spells; - * spells added by other mods should use {@link Spell#Spell(Tier, int, Element, String, SpellType, int, EnumAction, boolean, String)}. + * their own constructor from wherever the spell is registered. This is the constructor for original spells; + * spells added by other mods should use {@link Spell#Spell(EnumTier, int, EnumElement, String, EnumSpellType, int, EnumAction, boolean, String)}. * @param tier The tier this spell belongs to. * @param cost The amount of mana used to cast the spell. If this is a continuous spell, it represents mana cost * per second and should be a multiple of 5. * @param element The element this spell belongs to. - * @param name The registry name of the spell. This will also be the name of the icon file. The spell's - * unlocalised name will be a resource location with the format [modid]:[name]. + * @param name The unlocalised name of the spell This will also be the name of the icon file. * @param cooldown The cooldown time for this spell in ticks. * @param action The vanilla usage action to be displayed when casting this spell. * @param isContinuous Whether this spell is continuous, meaning you cast it for a length of time by holding the * right mouse button. */ - public Spell(Tier tier, int cost, Element element, String name, SpellType type, int cooldown, EnumAction action, boolean isContinuous){ - this(tier, cost, element, name, type, cooldown, action, isContinuous, Wizardry.MODID); + public Spell(EnumTier tier, int cost, EnumElement element, String name, EnumSpellType type, int cooldown, EnumAction action, boolean isContinuous){ + this.tier = tier; + this.cost = cost; + this.element = element; + this.unlocalisedName = name; + this.type = type; + this.cooldown = cooldown; + this.action = action; + this.isContinuous = isContinuous; + this.modID = Wizardry.MODID; + this.icon = new ResourceLocation(this.modID, "textures/spells/" + this.unlocalisedName + ".png"); } /** @@ -124,8 +127,7 @@ public abstract class Spell extends IForgeRegistryEntry.Impl implements C * @param cost The amount of mana used to cast the spell. If this is a continuous spell, it represents mana cost * per second and should be a multiple of 5. * @param element The element this spell belongs to. - * @param name The registry name of the spell, excluding the mod id. This will also be the name of the icon - * file. The spell's unlocalised name will be a resource location with the format [modid]:[name]. + * @param name The unlocalised name of the spell. This will also be the name of the icon file. * @param cooldown The cooldown time for this spell in ticks. * @param action The vanilla usage action to be displayed when casting this spell (see {@link}EnumAction) * @param isContinuous Whether this spell is continuous, meaning you cast it for a length of time by holding the @@ -133,18 +135,10 @@ public abstract class Spell extends IForgeRegistryEntry.Impl implements C * @param modID The mod id of the mod that added this spell. This allows wizardry to use the correct file * path for the spell icon, and also more generally to distinguish between original and addon spells. */ - public Spell(Tier tier, int cost, Element element, String name, SpellType type, int cooldown, EnumAction action, boolean isContinuous, String modID){ - this.tier = tier; - this.cost = cost; - this.element = element; - this.type = type; - this.cooldown = cooldown; - this.action = action; - this.isContinuous = isContinuous; + public Spell(EnumTier tier, int cost, EnumElement element, String name, EnumSpellType type, int cooldown, EnumAction action, boolean isContinuous, String modID){ + this(tier, cost, element, name, type, cooldown, action, isContinuous); this.modID = modID; - this.setRegistryName(modID, name); - this.unlocalisedName = this.getRegistryName().toString(); - this.icon = new ResourceLocation(this.modID, "textures/spells/" + name + ".png"); + this.icon = new ResourceLocation(this.modID, "textures/spells/" + this.unlocalisedName + ".png"); } /** @@ -161,15 +155,17 @@ public abstract class Spell extends IForgeRegistryEntry.Impl implements C * are discrepancies between client and server. * @param world A reference to the world object. Again this is for convenience, you can also use caster.worldObj. * @param caster The EntityPlayer that cast the spell. - * @param hand The hand that is holding the item used to cast the spell. If no item was used, this will be the main hand. * @param ticksInUse The number of ticks the spell has already been cast for. For all non-continuous spells, this is 0 and * is not used. For continuous spells, it is passed in as the maximum use duration of the item minus the count parameter in * onUsingItemTick and therefore it increases by 1 each tick. - * @param modifiers A {@link SpellModifiers} object containing the modifiers that have been applied to the spell. See - * the javadoc for that class for more information. If no modifiers are required, pass in {@code new SpellModifiers()}. + * @param damageMultiplier The damage multiplier to apply; pass in 1 if no multiplier is needed. + * @param rangeMultiplier The range multiplier to apply; pass in 1 if no multiplier is needed. + * @param durationMultiplier The duration multiplier to apply; pass in 1 if no multiplier is needed. + * @param blastMultiplier The blast radius multiplier to apply; pass in 1 if no multiplier is needed. + * * @return True if the spell succeeded and mana should be used up, false if not. */ - public abstract boolean cast(World world, EntityPlayer caster, EnumHand hand, int ticksInUse, SpellModifiers modifiers); + public abstract boolean cast(World world, EntityPlayer caster, int ticksInUse, float damageMultiplier, float rangeMultiplier, float durationMultiplier, float blastMultiplier); /** Casts the spell, but with an EntityLiving as the caster. Each subclass can optionally override this method and * within it execute the code to make the spell work. Returns a boolean to allow whatever calls this method to check if the spell @@ -188,21 +184,21 @@ public abstract class Spell extends IForgeRegistryEntry.Impl implements C * It's worth noting that on the client side, this method only gets called if the server side * cast() method succeeded, so you can put any particle spawning code outside of any success conditions if there * are discrepancies between client and server. + * * @param world A reference to the world object. This is for convenience, you can also use caster.worldObj. * @param caster The EntityLiving that cast the spell. - * @param hand The hand that is holding the item used to cast the spell. This will almost certainly be the main hand. - * @param ticksInUse The number of ticks the spell has already been cast for. For all non-continuous spells, this is 0 and - * is not used. - * @param target The EntityLivingBase that is targeted by the spell. May be null in some cases. - * @param modifiers A {@link SpellModifiers} object containing the modifiers that have been applied to the spell. See - * the javadoc for that class for more information. If no modifiers are required, pass in {@code new SpellModifiers()}. + * @param target The EntityLivingBase that is targeted by the spell. You must check if this parameter is null when overriding. + * @param damageMultiplier The damage multiplier to apply; pass in 1 if no multiplier is needed. + * @param rangeMultiplier The range multiplier to apply; pass in 1 if no multiplier is needed. + * @param durationMultiplier The duration multiplier to apply; pass in 1 if no multiplier is needed. + * @param blastMultiplier The blast radius multiplier to apply; pass in 1 if no multiplier is needed. * @return True if the spell succeeded, false if not. Returns false by default. */ - public boolean cast(World world, EntityLiving caster, EnumHand hand, int ticksInUse, EntityLivingBase target, SpellModifiers modifiers){ + public boolean cast(World world, EntityLiving caster, EntityLivingBase target, float damageMultiplier, float rangeMultiplier, float durationMultiplier, float blastMultiplier){ return false; } - /** Whether NPCs such as wizards can cast this spell. If you have overridden {@link Spell#cast(World, EntityLiving, EnumHand, int, EntityLivingBase, SpellModifiers)}, + /** Whether NPCs such as wizards can cast this spell. If you have overridden {@link Spell#cast(World, EntityLiving, EntityLivingBase, float, float, float, float)}, * you should override this to return true. */ public boolean canBeCastByNPCs(){ return false; @@ -223,68 +219,31 @@ public abstract class Spell extends IForgeRegistryEntry.Impl implements C return true; } - /** Returns this spell's id number, which now corresponds to its position in the spell registry. + /** Returns this spell's id number, which now corresponds to its position in the spells ArrayList. * Returns -1 if the spell has not been registered. */ // This is final so nothing can override it, because that would cause all kinds of problems! public final int id(){ - return registry.getValues().indexOf(this); + return spellsList.indexOf(this); } - /** Returns the mod ID for this spell, which should be the ID of the mod that added it. The mod ID is - * used to tell wizardry which filepath to use for the spell's icon. As of Wizardry 1.2, the field itself is - * private, but this getter is provided for external use (though it is never called in the main Wizardry mod). */ - public final String getModID(){ - return modID; - } - - /** Returns the ResourceLocation for this spell's icon. */ - public final ResourceLocation getIcon(){ - return icon; - } - - /** Returns the unlocalised name of the spell, without any prefixes or suffixes, e.g. "flame_ray". This should - * only be used for translation purposes. */ - public final String getUnlocalisedName(){ + /** Returns the unlocalised name of the spell. */ + public String getUnlocalisedName(){ return unlocalisedName; } - - /* The general idea with translation is to use net.minecraft.client.resources.I18n directly on the client side (and - * just prepend formatting codes where necessary), and to use TextComponentTranslation on the server (setting the - * style as necessary). TextComponentTranslation effectively stores what needs to be translated, without actually - * translating it. If, for whatever reason, you need to supply an ITextComponent but don't want it translated - * (perhaps a name?), you can use TextComponentString, which will simply keep the raw string it is given. */ - /** Returns the translated display name of the spell, without formatting (i.e. not coloured). Client-side only! - * On the server side, use {@link TextComponentTranslation} (see {@link Spell#getNameForTranslation()}). */ - @SideOnly(Side.CLIENT) + /** Returns the translated display name of the spell, without formatting (i.e. not coloured). */ public String getDisplayName(){ - return net.minecraft.client.resources.I18n.format("spell." + unlocalisedName); - } - - /** Returns a {@code TextComponentTranslation} which will be translated to the display name of the spell, without - * formatting (i.e. not coloured). */ - public TextComponentTranslation getNameForTranslation(){ - return new TextComponentTranslation("spell." + unlocalisedName); + return StatCollector.translateToLocal("spell." + unlocalisedName); } - /** Returns the translated display name of the spell, with formatting (i.e. coloured). Client-side only! - * On the server side, use {@link TextComponentTranslation} (see {@link Spell#getNameForTranslationFormatted()}). */ - @SideOnly(Side.CLIENT) + /** Returns the translated display name of the spell, with formatting (i.e. coloured). */ public String getDisplayNameWithFormatting(){ - return this.element.getFormattingCode() + net.minecraft.client.resources.I18n.format("spell." + unlocalisedName); - } - - /** Returns a {@code TextComponentTranslation} which will be translated to the display name of the spell, with - * formatting (i.e. coloured). */ - public ITextComponent getNameForTranslationFormatted(){ - return new TextComponentTranslation("spell." + unlocalisedName).setStyle(this.element.getColour()); + return this.element.colour + StatCollector.translateToLocal("spell." + unlocalisedName); } - /** Returns the translated description of the spell, without formatting. Client-side only! You should not - * need to use this on the server side. */ - @SideOnly(Side.CLIENT) + /** Returns the translated description of the spell, without formatting. */ public String getDescription(){ - return net.minecraft.client.resources.I18n.format("spell." + unlocalisedName + ".desc"); + return StatCollector.translateToLocal("spell." + unlocalisedName + ".desc"); } /** Returns whether the spell is enabled in the config. */ @@ -319,49 +278,79 @@ public abstract class Spell extends IForgeRegistryEntry.Impl implements C // ================================================ Static methods ================================================== + /** + * Registers a spell with the wizardry mod. Each spell should have a single instance, like blocks and items (this + * instance does not usually need to be stored, but you will have to store it if you want to reference a specific + * spell directly, for example transportation requires an accessible instance for printing of chat messages). + * Spells must be registered in the preInit method in your main mod class for them to be added to the config + * file automatically. + * + * @param spell The spell instance to register + * @throws NullPointerException if the passed in spell is null (why would you ever do that?). + */ + public static void register(Spell spell){ + if(spell == null){ + // If the null value was added to the list, it would cause an NPE later when it tries to sort the list. + // If the null value was ignored and not added, there would be no record of what went wrong. + // Therefore the best way is to throw an NPE here so whoever caused it knows why it went wrong. + throw new NullPointerException("Tried to register a spell, but the passed in spell was null."); + }else{ + spellsList.add(spell); + if(!(spell instanceof None)) unlocalisedNames.add(spell.unlocalisedName); + } + } + + /** Called from the postInit method in the main wizardry class to sort all the spells. + *

    Deprecated in favour of sorting the books and scrolls themselves in the creative tab. */ + @Deprecated + public static void sortSpells(){ + Collections.sort(spellsList); + } + /** Returns the total number of registered spells, excluding the 'None' spell. Returns the same number that would * be returned by Spell.getSpells(Spell.allSpells).size(), but this method is more efficient. */ public static int getTotalSpellCount(){ - return registry.getValues().size() - 1; + return spellsList.size() - 1; } - /** Gets a spell instance from its integer id, which now corresponds to its id in the spell registry. + /** Gets a spell instance from its integer id, which now corresponds to its position in the spells ArrayList. * If the given id has no spell (i.e. is less than 0 or greater than the total number of spells - 1) * then it will return the {@link None} spell. *

    - * If you are calling this from inside a loop in which you are iterating through the spells, there is - * probably a better way; see {@link Spell#getSpells(Predicate)}. */ + * If you are calling this from inside a loop in which you are iterating through the spells, there is + * probably a better way; see {@link Spell#getSpells(Filter)}. */ public static Spell get(int id){ - if(id < 0 || id >= registry.getValues().size()){ - return Spells.none; + return id < 0 || id >= spellsList.size() ? WizardryRegistry.none : spellsList.get(id); + } + + /** Returns the spell with the given unlocalised name, or null if no such spell exists. */ + public static Spell get(String unlocalisedName){ + for(Spell spell : Spell.getSpells(allSpells)){ + if(spell.unlocalisedName.equals(unlocalisedName)){ + return spell; + } } - Spell spell = registry.getValues().get(id); - return spell == null ? Spells.none : spell; + return null; } - /** Returns the spell with the given registry name, or null if no such spell exists. - * @param name The registry name of the spell, in the form [mod id]:[spell name]. If no mod id is specified, it - * defaults to {@link Wizardry#MODID}. */ - public static Spell get(String name){ - ResourceLocation key = new ResourceLocation(name); - if(key.getResourceDomain().equals("minecraft")) key = new ResourceLocation(Wizardry.MODID, name); - return registry.getValue(key); + /** Returns a list of all registered spells' unlocalised names, excluding the 'none' spell. Used in commands. */ + public static String[] getUnlocalisedNames(){ + return unlocalisedNames.toArray(new String[unlocalisedNames.size()]); } - /** Returns a list of all registered spells' registry names, excluding the 'none' spell. Used in commands. */ - public static String[] getSpellNames(){ - // Maybe it would be better to store all of this statically? - Set keys = new HashSet(registry.getKeys()); - keys.remove(registry.getKey(Spells.none)); - // Streams! - return keys.stream().map(ResourceLocation::toString).toArray(String[]::new); - } + /* The following method is an example of where I would have liked to use the java 8 predicate and removeIf + * functionality. However, since people don't update their java, I am trying to stick to java 6, annoying as that + * is... so this is the most flexible I can make it within the constraints of java 6. The aim is to remove the need + * to EVER iterate through the spells by id number and use spell.get(id).[condition], because this is cumbersome and + * inefficient and often leads to timeouts. It's far better to restrict the RNG to only the spells you want it + * to select from. Even when you're not using RNG, the for each loop is nicer and more efficient than constantly + * calling Spell.get(i). */ /** - * 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 filter. 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 - * returned list is local to this method. Never includes the {@link None} spell. For convenience, there are some - * predefined predicates in the Spell class (some of these really aren't shortcuts any more): + * returned list is local to this method. Never includes the 'none' spell. For convenience, there are some + * predefined filters in the Spell class: *

    * {@link Spell#allSpells} will allow all spells to be returned
    * {@link Spell#enabledSpells} will filter out any spells that are disabled in the config
    @@ -369,40 +358,74 @@ public abstract class Spell extends IForgeRegistryEntry.Impl implements C * {@link Spell#nonContinuousSpells} will filter out continuous spells but not disabled spells
    * {@link Spell.TierElementFilter} will only allow enabled spells of the specified tier and element * - * @param filter A Predicate<Spell> that the returned spells must satisfy. + * @param filter see {@link Spell.Filter} * - * @return A local, modifiable list of spells matching the given predicate. Note that this list may be empty. + * @return A list of spells matching the given filter. Note that this list may be empty. * */ - public static List getSpells(Predicate filter){ + public static List getSpells(Filter filter){ ArrayList spells = new ArrayList(1); - for(Spell spell : registry.getValues()){ - if(filter.test(spell) && spell != Spells.none) spells.add(spell); + for(Spell spell : spellsList){ + if(filter.test(spell) && spell != WizardryRegistry.none) spells.add(spell); } return spells; } - /** Predicate which allows all spells. */ - public static Predicate allSpells = s -> true; + /** To allow Java 6 compatibility, this is defined here instead of using the jdk 8 predicate. Function is exactly + * the same, but specifically for spells. For convenience, there are some predefined filters in the {@link Spell} + * class: + *

    + * {@link Spell#allSpells} will allow all spells to be returned
    + * {@link Spell#enabledSpells} will filter out any spells that are disabled in the config
    + * {@link Spell#npcSpells} will only allow enabled spells that can be cast by NPCs (see {@link Spell#canBeCastByNPCs()})
    + * {@link Spell#nonContinuousSpells} will filter out continuous spells but not disabled spells
    + * {@link Spell.TierElementFilter} will only allow enabled spells of the specified tier and element + *

    + * Edit: Turns out the google stuff has a predicate class. Oh well. */ + public static interface Filter { + public boolean test(Spell spell); + } - /** Predicate which allows all enabled spells. */ - public static Predicate enabledSpells = Spell::isEnabled; + /** Filter which allows all spells. */ + public static Filter allSpells = new Filter(){ + @Override + public boolean test(Spell spell) { + return true; + } + }; - /** Predicate which allows all non-continuous spells, even those that have been disabled. */ - public static Predicate nonContinuousSpells = s -> !s.isContinuous; + /** Filter which allows all enabled spells. */ + public static Filter enabledSpells = new Filter(){ + @Override + public boolean test(Spell spell) { + return spell.isEnabled(); + } + }; - /** Predicate which allows all enabled spells for which {@link Spell#canBeCastByNPCs()} returns true. */ - public static Predicate npcSpells = s -> s.isEnabled() && s.canBeCastByNPCs(); + /** Filter which allows all non-continuous spells, even those that have been disabled. */ + public static Filter nonContinuousSpells = new Filter(){ + @Override + public boolean test(Spell spell) { + return !spell.isContinuous; + } + }; - /** Predicate which allows all enabled spells of the given tier and element (create an instance of this class - * each time you want to use it). This is somewhat useless now that Wizardry uses Java 8, but it is more readable - * than a lambda expression and you don't need to remember to check that the spell is enabled every time. */ - public static class TierElementFilter implements Predicate { + /** Filter which allows all enabled spells for which {@link Spell#canBeCastByNPCs()} returns true. */ + public static Filter npcSpells = new Filter(){ + @Override + public boolean test(Spell spell) { + return spell.isEnabled() && spell.canBeCastByNPCs(); + } + }; + + /** Filter type which allows all enabled spells of the given tier and element (create an instance of this class + * each time you want to use it). */ + public static class TierElementFilter implements Filter { - private Tier tier; - private Element element; + private EnumTier tier; + private EnumElement element; /** * Creates a new TierElementFilter that checks for the given tier and element. Does not allow spells that have @@ -410,7 +433,7 @@ public abstract class Spell extends IForgeRegistryEntry.Impl implements C * @param tier The EnumTier to check for. Pass in null to allow all tiers. * @param element The EnumElement to check for. Pass in null to allow all elements. */ - public TierElementFilter(Tier tier, Element element){ + public TierElementFilter(EnumTier tier, EnumElement element){ this.tier = tier; this.element = element; } diff --git a/src/main/java/electroblob/wizardry/spell/SpiderSwarm.java b/src/main/java/electroblob/wizardry/spell/SpiderSwarm.java index a7c6a90a..80356b1a 100644 --- a/src/main/java/electroblob/wizardry/spell/SpiderSwarm.java +++ b/src/main/java/electroblob/wizardry/spell/SpiderSwarm.java @@ -1,25 +1,19 @@ package electroblob.wizardry.spell; -import electroblob.wizardry.constants.Element; -import electroblob.wizardry.constants.SpellType; -import electroblob.wizardry.constants.Tier; +import electroblob.wizardry.EnumElement; +import electroblob.wizardry.EnumSpellType; +import electroblob.wizardry.EnumTier; import electroblob.wizardry.entity.living.EntitySpiderMinion; -import electroblob.wizardry.registry.WizardryItems; -import electroblob.wizardry.util.SpellModifiers; -import electroblob.wizardry.util.WizardryUtilities; import net.minecraft.entity.EntityLiving; import net.minecraft.entity.EntityLivingBase; import net.minecraft.entity.player.EntityPlayer; -import net.minecraft.init.SoundEvents; import net.minecraft.item.EnumAction; -import net.minecraft.util.EnumHand; -import net.minecraft.util.math.BlockPos; import net.minecraft.world.World; public class SpiderSwarm extends Spell { public SpiderSwarm() { - super(Tier.ADVANCED, 45, Element.EARTH, "spider_swarm", SpellType.MINION, 200, EnumAction.BOW, false); + super(EnumTier.ADVANCED, 45, EnumElement.EARTH, "spider_swarm", EnumSpellType.MINION, 200, EnumAction.bow, false); } @Override @@ -28,43 +22,57 @@ public class SpiderSwarm extends Spell { } @Override - public boolean cast(World world, EntityPlayer caster, EnumHand hand, int ticksInUse, SpellModifiers modifiers) { - + public boolean cast(World world, EntityPlayer caster, int ticksInUse, float damageMultiplier, float rangeMultiplier, float durationMultiplier, float blastMultiplier) { + + boolean flag = false; + if(!world.isRemote){ for(int i=0;i<5;i++){ - BlockPos pos = WizardryUtilities.findNearbyFloorSpace(caster, 3, 6); - // The spell instantly fails if no space was found (see javadoc for the above method). - if(pos == null) return false; - - EntitySpiderMinion spider = new EntitySpiderMinion(world, pos.getX() + 0.5, pos.getY(), pos.getZ() + 0.5, caster, (int)(600*modifiers.get(WizardryItems.duration_upgrade))); - world.spawnEntityInWorld(spider); + double x = caster.posX + world.rand.nextDouble()*6 - 3; + double z = caster.posZ + world.rand.nextDouble()*6 - 3; + // Allows for height variation. + if(world.getTopSolidOrLiquidBlock((int)x, (int)z) - caster.posY < 6){ + flag = true; + double y = Math.max(caster.posY, world.getTopSolidOrLiquidBlock((int)x, (int)z)); + EntitySpiderMinion spider = new EntitySpiderMinion(world, x, y, z, caster, (int)(600*durationMultiplier)); + world.spawnEntityInWorld(spider); + } } } - WizardryUtilities.playSoundAtPlayer(caster, SoundEvents.BLOCK_FIRE_EXTINGUISH, 1.0F, world.rand.nextFloat() * 0.2F + 1.0F); - // Can't possibly get this far if nothing was spawned. - return true; + if(flag){ + world.playSoundAtEntity(caster, "random.fizz", 1.0F, world.rand.nextFloat() * 0.2F + 1.0F); + } + // If no spiders were spawned (like in a 1x1 hole or something) then the spell does not use up mana. + return flag; } @Override - public boolean cast(World world, EntityLiving caster, EnumHand hand, int ticksInUse, EntityLivingBase target, SpellModifiers modifiers) { + public boolean cast(World world, EntityLiving caster, EntityLivingBase target, float damageMultiplier, float rangeMultiplier, float durationMultiplier, float blastMultiplier) { + + boolean flag = false; if(!world.isRemote){ for(int i=0;i<5;i++){ - BlockPos pos = WizardryUtilities.findNearbyFloorSpace(caster, 3, 6); - // The spell instantly fails if no space was found (see javadoc for the above method). - if(pos == null) return false; - - EntitySpiderMinion spider = new EntitySpiderMinion(world, pos.getX() + 0.5, pos.getY(), pos.getZ() + 0.5, caster, (int)(600*modifiers.get(WizardryItems.duration_upgrade))); - world.spawnEntityInWorld(spider); + double x = caster.posX + world.rand.nextDouble()*6 - 3; + double z = caster.posZ + world.rand.nextDouble()*6 - 3; + // Allows for height variation. + if(world.getTopSolidOrLiquidBlock((int)x, (int)z) - caster.posY < 6){ + flag = true; + double y = Math.max(caster.posY, world.getTopSolidOrLiquidBlock((int)x, (int)z)); + EntitySpiderMinion spider = new EntitySpiderMinion(world, x, y, z, caster, (int)(600*durationMultiplier)); + world.spawnEntityInWorld(spider); + } } } - - caster.playSound(SoundEvents.BLOCK_FIRE_EXTINGUISH, 1.0F, world.rand.nextFloat() * 0.2F + 1.0F); - // Can't possibly get this far if nothing was spawned. - return true; + + if(flag){ + world.playSoundAtEntity(caster, "random.fizz", 1.0F, world.rand.nextFloat() * 0.2F + 1.0F); + } + // If no spiders were spawned (like in a 1x1 hole or something) then the spell does not use up mana. + return flag; } - + @Override public boolean canBeCastByNPCs(){ return true; diff --git a/src/main/java/electroblob/wizardry/spell/StaticAura.java b/src/main/java/electroblob/wizardry/spell/StaticAura.java index 851c260f..2a1f94d1 100644 --- a/src/main/java/electroblob/wizardry/spell/StaticAura.java +++ b/src/main/java/electroblob/wizardry/spell/StaticAura.java @@ -1,25 +1,21 @@ package electroblob.wizardry.spell; -import electroblob.wizardry.constants.Element; -import electroblob.wizardry.constants.SpellType; -import electroblob.wizardry.constants.Tier; -import electroblob.wizardry.registry.WizardryItems; -import electroblob.wizardry.registry.WizardryPotions; -import electroblob.wizardry.registry.WizardrySounds; -import electroblob.wizardry.util.SpellModifiers; -import electroblob.wizardry.util.WizardryUtilities; +import electroblob.wizardry.EnumElement; +import electroblob.wizardry.EnumSpellType; +import electroblob.wizardry.EnumTier; +import electroblob.wizardry.ExtendedPlayer; +import electroblob.wizardry.Wizardry; import net.minecraft.entity.EntityLiving; import net.minecraft.entity.EntityLivingBase; import net.minecraft.entity.player.EntityPlayer; import net.minecraft.item.EnumAction; import net.minecraft.potion.PotionEffect; -import net.minecraft.util.EnumHand; import net.minecraft.world.World; public class StaticAura extends Spell { public StaticAura() { - super(Tier.ADVANCED, 40, Element.LIGHTNING, "static_aura", SpellType.DEFENCE, 250, EnumAction.BOW, false); + super(EnumTier.ADVANCED, 40, EnumElement.LIGHTNING, "static_aura", EnumSpellType.DEFENCE, 250, EnumAction.bow, false); } @Override @@ -28,29 +24,29 @@ public class StaticAura extends Spell { } @Override - public boolean cast(World world, EntityPlayer caster, EnumHand hand, int ticksInUse, SpellModifiers modifiers) { + public boolean cast(World world, EntityPlayer caster, int ticksInUse, float damageMultiplier, float rangeMultiplier, float durationMultiplier, float blastMultiplier) { // Cannot be cast when it has already been cast - if(!caster.isPotionActive(WizardryPotions.static_aura)){ + if(!caster.isPotionActive(Wizardry.staticAura)){ if(!world.isRemote){ - caster.addPotionEffect(new PotionEffect(WizardryPotions.static_aura, (int)(600*modifiers.get(WizardryItems.duration_upgrade)), 0)); + caster.addPotionEffect(new PotionEffect(Wizardry.staticAura.id, (int)(600*durationMultiplier), 0, true)); + world.playSoundAtEntity(caster, "wizardry:arc", 1.0F, world.rand.nextFloat() * 0.4F + 1.4F); } - WizardryUtilities.playSoundAtPlayer(caster, WizardrySounds.SPELL_SPARK, 1.0F, world.rand.nextFloat() * 0.4F + 1.4F); return true; } return false; } @Override - public boolean cast(World world, EntityLiving caster, EnumHand hand, int ticksInUse, EntityLivingBase target, SpellModifiers modifiers) { + public boolean cast(World world, EntityLiving caster, EntityLivingBase target, float damageMultiplier, float rangeMultiplier, float durationMultiplier, float blastMultiplier) { if(target != null){ // Cannot be cast when it has already been cast - if(!caster.isPotionActive(WizardryPotions.static_aura)){ + if(!caster.isPotionActive(Wizardry.staticAura)){ if(!world.isRemote){ - caster.addPotionEffect(new PotionEffect(WizardryPotions.static_aura, (int)(600*modifiers.get(WizardryItems.duration_upgrade)), 0)); + caster.addPotionEffect(new PotionEffect(Wizardry.staticAura.id, (int)(600*durationMultiplier), 0, true)); + world.playSoundAtEntity(caster, "wizardry:arc", 1.0F, world.rand.nextFloat() * 0.4F + 1.4F); } - caster.playSound(WizardrySounds.SPELL_SPARK, 1.0F, world.rand.nextFloat() * 0.4F + 1.4F); return true; } return false; diff --git a/src/main/java/electroblob/wizardry/spell/SummonBlaze.java b/src/main/java/electroblob/wizardry/spell/SummonBlaze.java index 3882eae8..e191fa32 100644 --- a/src/main/java/electroblob/wizardry/spell/SummonBlaze.java +++ b/src/main/java/electroblob/wizardry/spell/SummonBlaze.java @@ -1,25 +1,22 @@ package electroblob.wizardry.spell; -import electroblob.wizardry.constants.Element; -import electroblob.wizardry.constants.SpellType; -import electroblob.wizardry.constants.Tier; +import electroblob.wizardry.EnumElement; +import electroblob.wizardry.EnumSpellType; +import electroblob.wizardry.EnumTier; +import electroblob.wizardry.Wizardry; +import electroblob.wizardry.WizardryUtilities; import electroblob.wizardry.entity.living.EntityBlazeMinion; -import electroblob.wizardry.registry.WizardryItems; -import electroblob.wizardry.util.SpellModifiers; -import electroblob.wizardry.util.WizardryUtilities; import net.minecraft.entity.EntityLiving; import net.minecraft.entity.EntityLivingBase; import net.minecraft.entity.player.EntityPlayer; -import net.minecraft.init.SoundEvents; import net.minecraft.item.EnumAction; -import net.minecraft.util.EnumHand; -import net.minecraft.util.math.BlockPos; +import net.minecraft.util.StatCollector; import net.minecraft.world.World; public class SummonBlaze extends Spell { public SummonBlaze() { - super(Tier.ADVANCED, 40, Element.FIRE, "summon_blaze", SpellType.MINION, 200, EnumAction.BOW, false); + super(EnumTier.ADVANCED, 40, EnumElement.FIRE, "summon_blaze", EnumSpellType.MINION, 200, EnumAction.bow, false); } @Override @@ -28,34 +25,44 @@ public class SummonBlaze extends Spell { } @Override - public boolean cast(World world, EntityPlayer caster, EnumHand hand, int ticksInUse, SpellModifiers modifiers) { + public boolean cast(World world, EntityPlayer caster, int ticksInUse, float damageMultiplier, float rangeMultiplier, float durationMultiplier, float blastMultiplier) { if(!world.isRemote){ + double x = caster.posX + world.rand.nextDouble()*4 - 2; + double z = caster.posZ + world.rand.nextDouble()*4 - 2; + // Allows for height variation. + if(WizardryUtilities.getNearestFloorLevel(world, (int)x, (int)caster.posY, (int)z, 5) == -1){ + return false; + } + double y = Math.max(caster.posY, WizardryUtilities.getNearestFloorLevel(world, (int)x, (int)caster.posY, (int)z, 5)); - BlockPos pos = WizardryUtilities.findNearbyFloorSpace(caster, 2, 4); - if(pos == null) return false; - - EntityBlazeMinion blaze = new EntityBlazeMinion(world, pos.getX() + 0.5, pos.getY(), pos.getZ() + 0.5, caster, (int)(600*modifiers.get(WizardryItems.duration_upgrade))); + EntityBlazeMinion blaze = new EntityBlazeMinion(world, x, y, z, caster, (int)(600*durationMultiplier)); + if(Wizardry.showSummonedCreatureNames) blaze.setCustomNameTag(StatCollector.translateToLocalFormatted("entity.wizardry.summonedcreature.nameplate", caster.getCommandSenderName(), blaze.getCommandSenderName())); world.spawnEntityInWorld(blaze); } - WizardryUtilities.playSoundAtPlayer(caster, SoundEvents.ENTITY_WITHER_AMBIENT, 1.0F, world.rand.nextFloat() * 0.2F + 1.0F); + world.playSoundAtEntity(caster, "mob.wither.idle", 1.0F, world.rand.nextFloat() * 0.2F + 1.0F); return true; } @Override - public boolean cast(World world, EntityLiving caster, EnumHand hand, int ticksInUse, EntityLivingBase target, SpellModifiers modifiers){ + public boolean cast(World world, EntityLiving caster, EntityLivingBase target, float damageMultiplier, float rangeMultiplier, float durationMultiplier, float blastMultiplier){ if(!world.isRemote){ + double x = caster.posX + world.rand.nextDouble()*4 - 2; + double z = caster.posZ + world.rand.nextDouble()*4 - 2; + // Allows for height variation. + if(WizardryUtilities.getNearestFloorLevel(world, (int)x, (int)caster.posY, (int)z, 5) == -1){ + return false; + } + double y = Math.max(caster.posY, WizardryUtilities.getNearestFloorLevel(world, (int)x, (int)caster.posY, (int)z, 5)); - BlockPos pos = WizardryUtilities.findNearbyFloorSpace(caster, 2, 4); - if(pos == null) return false; - - EntityBlazeMinion blaze = new EntityBlazeMinion(world, pos.getX() + 0.5, pos.getY(), pos.getZ() + 0.5, caster, (int)(600*modifiers.get(WizardryItems.duration_upgrade))); + EntityBlazeMinion blaze = new EntityBlazeMinion(world, x, y, z, caster, (int)(600*durationMultiplier)); + if(Wizardry.showSummonedCreatureNames) blaze.setCustomNameTag(StatCollector.translateToLocalFormatted("entity.wizardry.summonedcreature.nameplate", caster.getCommandSenderName(), blaze.getCommandSenderName())); world.spawnEntityInWorld(blaze); } - caster.playSound(SoundEvents.ENTITY_WITHER_AMBIENT, 1.0F, world.rand.nextFloat() * 0.2F + 1.0F); + world.playSoundAtEntity(caster, "mob.wither.idle", 1.0F, world.rand.nextFloat() * 0.2F + 1.0F); return true; } diff --git a/src/main/java/electroblob/wizardry/spell/SummonIceGiant.java b/src/main/java/electroblob/wizardry/spell/SummonIceGiant.java index 26e986f8..843e81d7 100644 --- a/src/main/java/electroblob/wizardry/spell/SummonIceGiant.java +++ b/src/main/java/electroblob/wizardry/spell/SummonIceGiant.java @@ -1,49 +1,52 @@ package electroblob.wizardry.spell; +import electroblob.wizardry.EnumElement; +import electroblob.wizardry.EnumParticleType; +import electroblob.wizardry.EnumSpellType; +import electroblob.wizardry.EnumTier; import electroblob.wizardry.Wizardry; -import electroblob.wizardry.constants.Element; -import electroblob.wizardry.constants.SpellType; -import electroblob.wizardry.constants.Tier; +import electroblob.wizardry.WizardryUtilities; import electroblob.wizardry.entity.living.EntityIceGiant; -import electroblob.wizardry.registry.WizardryItems; -import electroblob.wizardry.registry.WizardrySounds; -import electroblob.wizardry.util.SpellModifiers; -import electroblob.wizardry.util.WizardryParticleType; -import electroblob.wizardry.util.WizardryUtilities; import net.minecraft.entity.player.EntityPlayer; import net.minecraft.item.EnumAction; -import net.minecraft.util.EnumHand; -import net.minecraft.util.math.BlockPos; +import net.minecraft.util.StatCollector; import net.minecraft.world.World; public class SummonIceGiant extends Spell { public SummonIceGiant() { - super(Tier.MASTER, 100, Element.ICE, "summon_ice_giant", SpellType.MINION, 400, EnumAction.BOW, false); + super(EnumTier.MASTER, 100, EnumElement.ICE, "summon_ice_giant", EnumSpellType.MINION, 400, EnumAction.bow, false); } @Override - public boolean cast(World world, EntityPlayer caster, EnumHand hand, int ticksInUse, SpellModifiers modifiers) { + public boolean cast(World world, EntityPlayer caster, int ticksInUse, float damageMultiplier, float rangeMultiplier, float durationMultiplier, float blastMultiplier) { + + double x = caster.posX + world.rand.nextDouble()*4 - 2; + double z = caster.posZ + world.rand.nextDouble()*4 - 2; + + // Allows for height variation. + if(WizardryUtilities.getNearestFloorLevel(world, (int)x, (int)caster.posY, (int)z, 5) == -1){ + return false; + } + + double y = Math.max(caster.posY, WizardryUtilities.getNearestFloorLevel(world, (int)x, (int)caster.posY, (int)z, 5)); - BlockPos pos = WizardryUtilities.findNearbyFloorSpace(caster, 2, 4); - if(pos == null) return false; - if(!world.isRemote){ - - EntityIceGiant icegiant = new EntityIceGiant(world, pos.getX() + 0.5, pos.getY(), pos.getZ() + 0.5, caster, (int)(600*modifiers.get(WizardryItems.duration_upgrade))); + EntityIceGiant icegiant = new EntityIceGiant(world, x, y, z, caster, (int)(600*durationMultiplier)); + if(Wizardry.showSummonedCreatureNames) icegiant.setCustomNameTag(StatCollector.translateToLocalFormatted("entity.wizardry.summonedcreature.nameplate", caster.getCommandSenderName(), icegiant.getCommandSenderName())); world.spawnEntityInWorld(icegiant); } - if(world.isRemote){ - for(int i=0; i<10; i++){ - double x1 = (double)((float)pos.getX() + world.rand.nextFloat()*2 - 1.0F); - double y1 = (double)((float)pos.getY() + 0.5F + world.rand.nextFloat()); - double z1 = (double)((float)pos.getZ() + world.rand.nextFloat()*2 - 1.0F); - Wizardry.proxy.spawnParticle(WizardryParticleType.SPARKLE, world, x1, y1, z1, 0, 0, 0, 48 + world.rand.nextInt(12), 0.6f, 0.6f, 1.0f); + for(int i=0; i<10; i++){ + double x1 = (double)((float)x + world.rand.nextFloat()*2 - 1.0F); + double y1 = (double)((float)y + 0.5F + world.rand.nextFloat()); + double z1 = (double)((float)z + world.rand.nextFloat()*2 - 1.0F); + if(world.isRemote){ + Wizardry.proxy.spawnParticle(EnumParticleType.SPARKLE, world, x1, y1, z1, 0, 0, 0, 48 + world.rand.nextInt(12), 0.6f, 0.6f, 1.0f); } } - WizardryUtilities.playSoundAtPlayer(caster, WizardrySounds.SPELL_ICE, 1.0F, world.rand.nextFloat() * 0.1F + 0.2F); + world.playSoundAtEntity(caster, "wizardry:ice", 1.0F, world.rand.nextFloat() * 0.1F + 0.2F); return true; } diff --git a/src/main/java/electroblob/wizardry/spell/SummonIceWraith.java b/src/main/java/electroblob/wizardry/spell/SummonIceWraith.java index 6f914344..6c0346ad 100644 --- a/src/main/java/electroblob/wizardry/spell/SummonIceWraith.java +++ b/src/main/java/electroblob/wizardry/spell/SummonIceWraith.java @@ -1,25 +1,22 @@ package electroblob.wizardry.spell; -import electroblob.wizardry.constants.Element; -import electroblob.wizardry.constants.SpellType; -import electroblob.wizardry.constants.Tier; +import electroblob.wizardry.EnumElement; +import electroblob.wizardry.EnumSpellType; +import electroblob.wizardry.EnumTier; +import electroblob.wizardry.Wizardry; +import electroblob.wizardry.WizardryUtilities; import electroblob.wizardry.entity.living.EntityIceWraith; -import electroblob.wizardry.registry.WizardryItems; -import electroblob.wizardry.util.SpellModifiers; -import electroblob.wizardry.util.WizardryUtilities; import net.minecraft.entity.EntityLiving; import net.minecraft.entity.EntityLivingBase; import net.minecraft.entity.player.EntityPlayer; -import net.minecraft.init.SoundEvents; import net.minecraft.item.EnumAction; -import net.minecraft.util.EnumHand; -import net.minecraft.util.math.BlockPos; +import net.minecraft.util.StatCollector; import net.minecraft.world.World; public class SummonIceWraith extends Spell { public SummonIceWraith() { - super(Tier.ADVANCED, 40, Element.ICE, "summon_ice_wraith", SpellType.MINION, 200, EnumAction.BOW, false); + super(EnumTier.ADVANCED, 40, EnumElement.ICE, "summon_ice_wraith", EnumSpellType.MINION, 200, EnumAction.bow, false); } @Override @@ -28,33 +25,42 @@ public class SummonIceWraith extends Spell { } @Override - public boolean cast(World world, EntityPlayer caster, EnumHand hand, int ticksInUse, SpellModifiers modifiers) { + public boolean cast(World world, EntityPlayer caster, int ticksInUse, float damageMultiplier, float rangeMultiplier, float durationMultiplier, float blastMultiplier) { if(!world.isRemote){ + double x = caster.posX + world.rand.nextDouble()*4 - 2; + double z = caster.posZ + world.rand.nextDouble()*4 - 2; + // Allows for height variation. + if(WizardryUtilities.getNearestFloorLevel(world, (int)x, (int)caster.posY, (int)z, 5) == -1){ + return false; + } + double y = Math.max(caster.posY, WizardryUtilities.getNearestFloorLevel(world, (int)x, (int)caster.posY, (int)z, 5)); - BlockPos pos = WizardryUtilities.findNearbyFloorSpace(caster, 2, 4); - if(pos == null) return false; - - EntityIceWraith iceWraith = new EntityIceWraith(world, pos.getX() + 0.5, pos.getY(), pos.getZ() + 0.5, caster, (int)(600*modifiers.get(WizardryItems.duration_upgrade))); + EntityIceWraith iceWraith = new EntityIceWraith(world, x, y, z, caster, (int)(600*durationMultiplier)); + if(Wizardry.showSummonedCreatureNames) iceWraith.setCustomNameTag(StatCollector.translateToLocalFormatted("entity.wizardry.summonedcreature.nameplate", caster.getCommandSenderName(), iceWraith.getCommandSenderName())); world.spawnEntityInWorld(iceWraith); } - - WizardryUtilities.playSoundAtPlayer(caster, SoundEvents.ENTITY_WITHER_AMBIENT, 1.0F, world.rand.nextFloat() * 0.2F + 1.0F); + world.playSoundAtEntity(caster, "mob.wither.idle", 1.0F, world.rand.nextFloat() * 0.2F + 1.0F); return true; } @Override - public boolean cast(World world, EntityLiving caster, EnumHand hand, int ticksInUse, EntityLivingBase target, SpellModifiers modifiers){ + public boolean cast(World world, EntityLiving caster, EntityLivingBase target, float damageMultiplier, float rangeMultiplier, float durationMultiplier, float blastMultiplier){ if(!world.isRemote){ + double x = caster.posX + world.rand.nextDouble()*4 - 2; + double z = caster.posZ + world.rand.nextDouble()*4 - 2; + // Allows for height variation. + if(WizardryUtilities.getNearestFloorLevel(world, (int)x, (int)caster.posY, (int)z, 5) == -1){ + return false; + } + double y = Math.max(caster.posY, WizardryUtilities.getNearestFloorLevel(world, (int)x, (int)caster.posY, (int)z, 5)); - BlockPos pos = WizardryUtilities.findNearbyFloorSpace(caster, 2, 4); - if(pos == null) return false; - - EntityIceWraith iceWraith = new EntityIceWraith(world, pos.getX() + 0.5, pos.getY(), pos.getZ() + 0.5, caster, (int)(600*modifiers.get(WizardryItems.duration_upgrade))); + EntityIceWraith iceWraith = new EntityIceWraith(world, x, y, z, caster, (int)(600*durationMultiplier)); + if(Wizardry.showSummonedCreatureNames) iceWraith.setCustomNameTag(StatCollector.translateToLocalFormatted("entity.wizardry.summonedcreature.nameplate", caster.getCommandSenderName(), iceWraith.getCommandSenderName())); world.spawnEntityInWorld(iceWraith); } - caster.playSound(SoundEvents.ENTITY_WITHER_AMBIENT, 1.0F, world.rand.nextFloat() * 0.2F + 1.0F); + world.playSoundAtEntity(caster, "mob.wither.idle", 1.0F, world.rand.nextFloat() * 0.2F + 1.0F); return true; } diff --git a/src/main/java/electroblob/wizardry/spell/SummonIronGolem.java b/src/main/java/electroblob/wizardry/spell/SummonIronGolem.java index 4f510f4d..6b665b3c 100644 --- a/src/main/java/electroblob/wizardry/spell/SummonIronGolem.java +++ b/src/main/java/electroblob/wizardry/spell/SummonIronGolem.java @@ -1,48 +1,51 @@ package electroblob.wizardry.spell; +import electroblob.wizardry.EnumElement; +import electroblob.wizardry.EnumParticleType; +import electroblob.wizardry.EnumSpellType; +import electroblob.wizardry.EnumTier; import electroblob.wizardry.Wizardry; -import electroblob.wizardry.constants.Element; -import electroblob.wizardry.constants.SpellType; -import electroblob.wizardry.constants.Tier; -import electroblob.wizardry.util.SpellModifiers; -import electroblob.wizardry.util.WizardryParticleType; -import electroblob.wizardry.util.WizardryUtilities; +import electroblob.wizardry.WizardryUtilities; import net.minecraft.entity.monster.EntityIronGolem; import net.minecraft.entity.player.EntityPlayer; -import net.minecraft.init.SoundEvents; import net.minecraft.item.EnumAction; -import net.minecraft.util.EnumHand; -import net.minecraft.util.math.BlockPos; +import net.minecraft.util.StatCollector; import net.minecraft.world.World; public class SummonIronGolem extends Spell { public SummonIronGolem() { - super(Tier.MASTER, 175, Element.SORCERY, "summon_iron_golem", SpellType.MINION, 400, EnumAction.BOW, false); + super(EnumTier.MASTER, 175, EnumElement.SORCERY, "summon_iron_golem", EnumSpellType.MINION, 400, EnumAction.bow, false); } @Override - public boolean cast(World world, EntityPlayer caster, EnumHand hand, int ticksInUse, SpellModifiers modifiers) { + public boolean cast(World world, EntityPlayer caster, int ticksInUse, float damageMultiplier, float rangeMultiplier, float durationMultiplier, float blastMultiplier) { - BlockPos pos = WizardryUtilities.findNearbyFloorSpace(caster, 2, 4); - if(pos == null) return false; + double x = caster.posX + world.rand.nextDouble()*4 - 2; + double z = caster.posZ + world.rand.nextDouble()*4 - 2; + // Allows for height variation. + if(WizardryUtilities.getNearestFloorLevel(world, (int)x, (int)caster.posY, (int)z, 5) == -1){ + return false; + } + + double y = Math.max(caster.posY, WizardryUtilities.getNearestFloorLevel(world, (int)x, (int)caster.posY, (int)z, 5)); + if(!world.isRemote){ EntityIronGolem golem = new EntityIronGolem(world); - golem.setPosition(pos.getX() + 0.5, pos.getY(), pos.getZ() + 0.5); + if(Wizardry.showSummonedCreatureNames) golem.setCustomNameTag(StatCollector.translateToLocalFormatted("entity.wizardry.summonedcreature.nameplate", caster.getCommandSenderName(), golem.getCommandSenderName())); + golem.setPosition(x, y, z); world.spawnEntityInWorld(golem); } - for(int i=0; i<10; i++){ - double x1 = (double)((float)pos.getX() + world.rand.nextFloat()*2 - 1.0F); - double y1 = (double)((float)pos.getY() + 0.5F + world.rand.nextFloat()); - double z1 = (double)((float)pos.getZ() + world.rand.nextFloat()*2 - 1.0F); + double x1 = (double)((float)x + world.rand.nextFloat()*2 - 1.0F); + double y1 = (double)((float)y + 0.5F + world.rand.nextFloat()); + double z1 = (double)((float)z + world.rand.nextFloat()*2 - 1.0F); if(world.isRemote){ - Wizardry.proxy.spawnParticle(WizardryParticleType.SPARKLE, world, x1, y1, z1, 0, 0, 0, 48 + world.rand.nextInt(12), 0.6f, 0.6f, 1.0f); + Wizardry.proxy.spawnParticle(EnumParticleType.SPARKLE, world, x1, y1, z1, 0, 0, 0, 48 + world.rand.nextInt(12), 0.6f, 0.6f, 1.0f); } } - - WizardryUtilities.playSoundAtPlayer(caster, SoundEvents.ENTITY_WITHER_SPAWN, 1.0F, world.rand.nextFloat() * 0.2F + 1.0F); + world.playSoundAtEntity(caster, "mob.wither.spawn", 1.0F, world.rand.nextFloat() * 0.2F + 1.0F); return true; } diff --git a/src/main/java/electroblob/wizardry/spell/SummonLightningWraith.java b/src/main/java/electroblob/wizardry/spell/SummonLightningWraith.java index 6c74adf1..654dd0fe 100644 --- a/src/main/java/electroblob/wizardry/spell/SummonLightningWraith.java +++ b/src/main/java/electroblob/wizardry/spell/SummonLightningWraith.java @@ -1,25 +1,22 @@ package electroblob.wizardry.spell; -import electroblob.wizardry.constants.Element; -import electroblob.wizardry.constants.SpellType; -import electroblob.wizardry.constants.Tier; +import electroblob.wizardry.EnumElement; +import electroblob.wizardry.EnumSpellType; +import electroblob.wizardry.EnumTier; +import electroblob.wizardry.Wizardry; +import electroblob.wizardry.WizardryUtilities; import electroblob.wizardry.entity.living.EntityLightningWraith; -import electroblob.wizardry.registry.WizardryItems; -import electroblob.wizardry.util.SpellModifiers; -import electroblob.wizardry.util.WizardryUtilities; import net.minecraft.entity.EntityLiving; import net.minecraft.entity.EntityLivingBase; import net.minecraft.entity.player.EntityPlayer; -import net.minecraft.init.SoundEvents; import net.minecraft.item.EnumAction; -import net.minecraft.util.EnumHand; -import net.minecraft.util.math.BlockPos; +import net.minecraft.util.StatCollector; import net.minecraft.world.World; public class SummonLightningWraith extends Spell { public SummonLightningWraith() { - super(Tier.ADVANCED, 40, Element.LIGHTNING, "summon_lightning_wraith", SpellType.MINION, 200, EnumAction.BOW, false); + super(EnumTier.ADVANCED, 40, EnumElement.LIGHTNING, "summon_lightning_wraith", EnumSpellType.MINION, 200, EnumAction.bow, false); } @Override @@ -28,32 +25,42 @@ public class SummonLightningWraith extends Spell { } @Override - public boolean cast(World world, EntityPlayer caster, EnumHand hand, int ticksInUse, SpellModifiers modifiers) { + public boolean cast(World world, EntityPlayer caster, int ticksInUse, float damageMultiplier, float rangeMultiplier, float durationMultiplier, float blastMultiplier) { if(!world.isRemote){ + double x = caster.posX + world.rand.nextDouble()*4 - 2; + double z = caster.posZ + world.rand.nextDouble()*4 - 2; + // Allows for height variation. + if(WizardryUtilities.getNearestFloorLevel(world, (int)x, (int)caster.posY, (int)z, 5) == -1){ + return false; + } + double y = Math.max(caster.posY, WizardryUtilities.getNearestFloorLevel(world, (int)x, (int)caster.posY, (int)z, 5)); - BlockPos pos = WizardryUtilities.findNearbyFloorSpace(caster, 2, 4); - if(pos == null) return false; - - EntityLightningWraith lightningWraith = new EntityLightningWraith(world, pos.getX() + 0.5, pos.getY(), pos.getZ() + 0.5, caster, (int)(600*modifiers.get(WizardryItems.duration_upgrade))); + EntityLightningWraith lightningWraith = new EntityLightningWraith(world, x, y, z, caster, (int)(600*durationMultiplier)); + if(Wizardry.showSummonedCreatureNames) lightningWraith.setCustomNameTag(StatCollector.translateToLocalFormatted("entity.wizardry.summonedcreature.nameplate", caster.getCommandSenderName(), lightningWraith.getCommandSenderName())); world.spawnEntityInWorld(lightningWraith); } - WizardryUtilities.playSoundAtPlayer(caster, SoundEvents.ENTITY_WITHER_AMBIENT, 1.0F, world.rand.nextFloat() * 0.2F + 1.0F); + world.playSoundAtEntity(caster, "mob.wither.idle", 1.0F, world.rand.nextFloat() * 0.2F + 1.0F); return true; } @Override - public boolean cast(World world, EntityLiving caster, EnumHand hand, int ticksInUse, EntityLivingBase target, SpellModifiers modifiers){ + public boolean cast(World world, EntityLiving caster, EntityLivingBase target, float damageMultiplier, float rangeMultiplier, float durationMultiplier, float blastMultiplier){ if(!world.isRemote){ - - BlockPos pos = WizardryUtilities.findNearbyFloorSpace(caster, 2, 4); - if(pos == null) return false; + double x = caster.posX + world.rand.nextDouble()*4 - 2; + double z = caster.posZ + world.rand.nextDouble()*4 - 2; + // Allows for height variation. + if(WizardryUtilities.getNearestFloorLevel(world, (int)x, (int)caster.posY, (int)z, 5) == -1){ + return false; + } + double y = Math.max(caster.posY, WizardryUtilities.getNearestFloorLevel(world, (int)x, (int)caster.posY, (int)z, 5)); - EntityLightningWraith lightningWraith = new EntityLightningWraith(world, pos.getX() + 0.5, pos.getY(), pos.getZ() + 0.5, caster, (int)(600*modifiers.get(WizardryItems.duration_upgrade))); + EntityLightningWraith lightningWraith = new EntityLightningWraith(world, x, y, z, caster, (int)(600*durationMultiplier)); + if(Wizardry.showSummonedCreatureNames) lightningWraith.setCustomNameTag(StatCollector.translateToLocalFormatted("entity.wizardry.summonedcreature.nameplate", caster.getCommandSenderName(), lightningWraith.getCommandSenderName())); world.spawnEntityInWorld(lightningWraith); } - caster.playSound(SoundEvents.ENTITY_WITHER_AMBIENT, 1.0F, world.rand.nextFloat() * 0.2F + 1.0F); + world.playSoundAtEntity(caster, "mob.wither.idle", 1.0F, world.rand.nextFloat() * 0.2F + 1.0F); return true; } diff --git a/src/main/java/electroblob/wizardry/spell/SummonPhoenix.java b/src/main/java/electroblob/wizardry/spell/SummonPhoenix.java index 5dc24a07..65d144ff 100644 --- a/src/main/java/electroblob/wizardry/spell/SummonPhoenix.java +++ b/src/main/java/electroblob/wizardry/spell/SummonPhoenix.java @@ -1,23 +1,20 @@ package electroblob.wizardry.spell; -import electroblob.wizardry.constants.Element; -import electroblob.wizardry.constants.SpellType; -import electroblob.wizardry.constants.Tier; +import electroblob.wizardry.EnumElement; +import electroblob.wizardry.EnumSpellType; +import electroblob.wizardry.EnumTier; +import electroblob.wizardry.Wizardry; +import electroblob.wizardry.WizardryUtilities; import electroblob.wizardry.entity.living.EntityPhoenix; -import electroblob.wizardry.registry.WizardryItems; -import electroblob.wizardry.util.SpellModifiers; -import electroblob.wizardry.util.WizardryUtilities; import net.minecraft.entity.player.EntityPlayer; -import net.minecraft.init.SoundEvents; import net.minecraft.item.EnumAction; -import net.minecraft.util.EnumHand; -import net.minecraft.util.math.BlockPos; +import net.minecraft.util.StatCollector; import net.minecraft.world.World; public class SummonPhoenix extends Spell { public SummonPhoenix() { - super(Tier.MASTER, 150, Element.FIRE, "summon_phoenix", SpellType.MINION, 400, EnumAction.BOW, false); + super(EnumTier.MASTER, 150, EnumElement.FIRE, "summon_phoenix", EnumSpellType.MINION, 400, EnumAction.bow, false); } @Override @@ -26,19 +23,23 @@ public class SummonPhoenix extends Spell { } @Override - public boolean cast(World world, EntityPlayer caster, EnumHand hand, int ticksInUse, SpellModifiers modifiers) { + public boolean cast(World world, EntityPlayer caster, int ticksInUse, float damageMultiplier, float rangeMultiplier, float durationMultiplier, float blastMultiplier) { if(!world.isRemote){ - - BlockPos pos = WizardryUtilities.findNearbyFloorSpace(caster, 2, 4); - if(pos == null) return false; + double x = caster.posX + world.rand.nextDouble()*4 - 2; + double z = caster.posZ + world.rand.nextDouble()*4 - 2; + // Allows for height variation. + if(WizardryUtilities.getNearestFloorLevel(world, (int)x, (int)caster.posY, (int)z, 5) == -1){ + return false; + } + double y = Math.max(caster.posY, WizardryUtilities.getNearestFloorLevel(world, (int)x, (int)caster.posY, (int)z, 5)); - EntityPhoenix phoenix = new EntityPhoenix(world, pos.getX() + 0.5, pos.getY(), pos.getZ() + 0.5, caster, - (int)(600*modifiers.get(WizardryItems.duration_upgrade))); + EntityPhoenix phoenix = new EntityPhoenix(world, x, y, z, caster, (int)(600*durationMultiplier)); + if(Wizardry.showSummonedCreatureNames) phoenix.setCustomNameTag(StatCollector.translateToLocalFormatted("entity.wizardry.summonedcreature.nameplate", caster.getCommandSenderName(), phoenix.getCommandSenderName())); world.spawnEntityInWorld(phoenix); } - WizardryUtilities.playSoundAtPlayer(caster, SoundEvents.ENTITY_WITHER_AMBIENT, 1.0F, world.rand.nextFloat() * 0.2F + 1.0F); + world.playSoundAtEntity(caster, "mob.wither.idle", 1.0F, world.rand.nextFloat() * 0.2F + 1.0F); return true; } diff --git a/src/main/java/electroblob/wizardry/spell/SummonShadowWraith.java b/src/main/java/electroblob/wizardry/spell/SummonShadowWraith.java index 621cfeab..cfdc539e 100644 --- a/src/main/java/electroblob/wizardry/spell/SummonShadowWraith.java +++ b/src/main/java/electroblob/wizardry/spell/SummonShadowWraith.java @@ -1,25 +1,20 @@ package electroblob.wizardry.spell; -import electroblob.wizardry.constants.Element; -import electroblob.wizardry.constants.SpellType; -import electroblob.wizardry.constants.Tier; +import electroblob.wizardry.EnumElement; +import electroblob.wizardry.EnumSpellType; +import electroblob.wizardry.EnumTier; +import electroblob.wizardry.Wizardry; +import electroblob.wizardry.WizardryUtilities; import electroblob.wizardry.entity.living.EntityShadowWraith; -import electroblob.wizardry.registry.WizardryItems; -import electroblob.wizardry.util.SpellModifiers; -import electroblob.wizardry.util.WizardryUtilities; import net.minecraft.entity.player.EntityPlayer; -import net.minecraft.init.SoundEvents; import net.minecraft.item.EnumAction; -import net.minecraft.util.EnumHand; -import net.minecraft.util.math.BlockPos; +import net.minecraft.util.StatCollector; import net.minecraft.world.World; -import net.minecraftforge.fml.relauncher.Side; -import net.minecraftforge.fml.relauncher.SideOnly; public class SummonShadowWraith extends Spell { public SummonShadowWraith() { - super(Tier.MASTER, 100, Element.NECROMANCY, "summon_shadow_wraith", SpellType.MINION, 400, EnumAction.BOW, false); + super(EnumTier.MASTER, 100, EnumElement.NECROMANCY, "summon_shadow_wraith", EnumSpellType.MINION, 400, EnumAction.bow, false); } @Override @@ -28,21 +23,25 @@ public class SummonShadowWraith extends Spell { } @Override - public boolean cast(World world, EntityPlayer caster, EnumHand hand, int ticksInUse, SpellModifiers modifiers) { + public boolean cast(World world, EntityPlayer caster, int ticksInUse, float damageMultiplier, float rangeMultiplier, float durationMultiplier, float blastMultiplier) { if(!world.isRemote){ - - BlockPos pos = WizardryUtilities.findNearbyFloorSpace(caster, 2, 4); - if(pos == null) return false; + double x = caster.posX + world.rand.nextDouble()*4 - 2; + double z = caster.posZ + world.rand.nextDouble()*4 - 2; + // Allows for height variation. + if(WizardryUtilities.getNearestFloorLevel(world, (int)x, (int)caster.posY, (int)z, 5) == -1){ + return false; + } + double y = Math.max(caster.posY, WizardryUtilities.getNearestFloorLevel(world, (int)x, (int)caster.posY, (int)z, 5)); - EntityShadowWraith shadowWraith = new EntityShadowWraith(world, pos.getX() + 0.5, pos.getY(), pos.getZ() + 0.5, caster, (int)(600*modifiers.get(WizardryItems.duration_upgrade))); + EntityShadowWraith shadowWraith = new EntityShadowWraith(world, x, y, z, caster, (int)(600*durationMultiplier)); + if(Wizardry.showSummonedCreatureNames) shadowWraith.setCustomNameTag(StatCollector.translateToLocalFormatted("entity.wizardry.summonedcreature.nameplate", caster.getCommandSenderName(), shadowWraith.getCommandSenderName())); world.spawnEntityInWorld(shadowWraith); } - WizardryUtilities.playSoundAtPlayer(caster, SoundEvents.ENTITY_WITHER_AMBIENT, 1.0F, world.rand.nextFloat() * 0.2F + 1.0F); + world.playSoundAtEntity(caster, "mob.wither.idle", 1.0F, world.rand.nextFloat() * 0.2F + 1.0F); return true; } - @SideOnly(Side.CLIENT) @Override public String getDescription(){ return "\u00A7k" + super.getDescription(); diff --git a/src/main/java/electroblob/wizardry/spell/SummonSkeleton.java b/src/main/java/electroblob/wizardry/spell/SummonSkeleton.java index 7c086e9f..4c901d57 100644 --- a/src/main/java/electroblob/wizardry/spell/SummonSkeleton.java +++ b/src/main/java/electroblob/wizardry/spell/SummonSkeleton.java @@ -1,28 +1,24 @@ package electroblob.wizardry.spell; -import electroblob.wizardry.constants.Element; -import electroblob.wizardry.constants.SpellType; -import electroblob.wizardry.constants.Tier; +import electroblob.wizardry.EnumElement; +import electroblob.wizardry.EnumSpellType; +import electroblob.wizardry.EnumTier; +import electroblob.wizardry.Wizardry; +import electroblob.wizardry.WizardryUtilities; import electroblob.wizardry.entity.living.EntitySkeletonMinion; -import electroblob.wizardry.registry.WizardryItems; -import electroblob.wizardry.registry.WizardrySounds; -import electroblob.wizardry.util.SpellModifiers; -import electroblob.wizardry.util.WizardryUtilities; import net.minecraft.entity.EntityLiving; import net.minecraft.entity.EntityLivingBase; import net.minecraft.entity.player.EntityPlayer; import net.minecraft.init.Items; -import net.minecraft.inventory.EntityEquipmentSlot; import net.minecraft.item.EnumAction; import net.minecraft.item.ItemStack; -import net.minecraft.util.EnumHand; -import net.minecraft.util.math.BlockPos; +import net.minecraft.util.StatCollector; import net.minecraft.world.World; public class SummonSkeleton extends Spell { public SummonSkeleton() { - super(Tier.APPRENTICE, 15, Element.NECROMANCY, "summon_skeleton", SpellType.MINION, 50, EnumAction.BOW, false); + super(EnumTier.APPRENTICE, 15, EnumElement.NECROMANCY, "summon_skeleton", EnumSpellType.MINION, 50, EnumAction.bow, false); } @Override @@ -31,36 +27,46 @@ public class SummonSkeleton extends Spell { } @Override - public boolean cast(World world, EntityPlayer caster, EnumHand hand, int ticksInUse, SpellModifiers modifiers) { + public boolean cast(World world, EntityPlayer caster, int ticksInUse, float damageMultiplier, float rangeMultiplier, float durationMultiplier, float blastMultiplier) { if(!world.isRemote){ - - BlockPos pos = WizardryUtilities.findNearbyFloorSpace(caster, 2, 4); - if(pos == null) return false; + double x = caster.posX + world.rand.nextDouble()*4 - 2; + double z = caster.posZ + world.rand.nextDouble()*4 - 2; + // Allows for height variation. + if(WizardryUtilities.getNearestFloorLevel(world, (int)x, (int)caster.posY, (int)z, 5) == -1){ + return false; + } + double y = Math.max(caster.posY, WizardryUtilities.getNearestFloorLevel(world, (int)x, (int)caster.posY, (int)z, 5)); - EntitySkeletonMinion skeleton = new EntitySkeletonMinion(world, pos.getX() + 0.5, pos.getY(), pos.getZ() + 0.5, caster, (int)(600*modifiers.get(WizardryItems.duration_upgrade))); - skeleton.setItemStackToSlot(EntityEquipmentSlot.MAINHAND, new ItemStack(Items.BOW)); - skeleton.setDropChance(EntityEquipmentSlot.MAINHAND, 0.0f); + EntitySkeletonMinion skeleton = new EntitySkeletonMinion(world, x, y, z, caster, (int)(600*durationMultiplier)); + if(Wizardry.showSummonedCreatureNames) skeleton.setCustomNameTag(StatCollector.translateToLocalFormatted("entity.wizardry.summonedcreature.nameplate", caster.getCommandSenderName(), skeleton.getCommandSenderName())); + skeleton.setCurrentItemOrArmor(0, new ItemStack(Items.bow)); + skeleton.setEquipmentDropChance(0, 0.0f); world.spawnEntityInWorld(skeleton); } - WizardryUtilities.playSoundAtPlayer(caster, WizardrySounds.SPELL_SUMMONING, 7.0f, 0.6f); + world.playSoundAtEntity(caster, "wizardry:darkaura", 7.0f, 0.6f); return true; } @Override - public boolean cast(World world, EntityLiving caster, EnumHand hand, int ticksInUse, EntityLivingBase target, SpellModifiers modifiers){ + public boolean cast(World world, EntityLiving caster, EntityLivingBase target, float damageMultiplier, float rangeMultiplier, float durationMultiplier, float blastMultiplier){ if(!world.isRemote){ - - BlockPos pos = WizardryUtilities.findNearbyFloorSpace(caster, 2, 4); - if(pos == null) return false; + double x = caster.posX + world.rand.nextDouble()*4 - 2; + double z = caster.posZ + world.rand.nextDouble()*4 - 2; + // Allows for height variation. + if(WizardryUtilities.getNearestFloorLevel(world, (int)x, (int)caster.posY, (int)z, 5) == -1){ + return false; + } + double y = Math.max(caster.posY, WizardryUtilities.getNearestFloorLevel(world, (int)x, (int)caster.posY, (int)z, 5)); - EntitySkeletonMinion skeleton = new EntitySkeletonMinion(world, pos.getX() + 0.5, pos.getY(), pos.getZ() + 0.5, caster, (int)(600*modifiers.get(WizardryItems.duration_upgrade))); - skeleton.setItemStackToSlot(EntityEquipmentSlot.MAINHAND, new ItemStack(Items.BOW)); - skeleton.setDropChance(EntityEquipmentSlot.MAINHAND, 0.0f); + EntitySkeletonMinion skeleton = new EntitySkeletonMinion(world, x, y, z, caster, (int)(600*durationMultiplier)); + if(Wizardry.showSummonedCreatureNames) skeleton.setCustomNameTag(StatCollector.translateToLocalFormatted("entity.wizardry.summonedcreature.nameplate", caster.getCommandSenderName(), skeleton.getCommandSenderName())); + skeleton.setCurrentItemOrArmor(0, new ItemStack(Items.bow)); + skeleton.setEquipmentDropChance(0, 0.0f); world.spawnEntityInWorld(skeleton); } - caster.playSound(WizardrySounds.SPELL_SUMMONING, 7.0f, 0.6f); + world.playSoundAtEntity(caster, "wizardry:darkaura", 7.0f, 0.6f); return true; } diff --git a/src/main/java/electroblob/wizardry/spell/SummonSkeletonLegion.java b/src/main/java/electroblob/wizardry/spell/SummonSkeletonLegion.java index 69a373ff..38ba20d9 100644 --- a/src/main/java/electroblob/wizardry/spell/SummonSkeletonLegion.java +++ b/src/main/java/electroblob/wizardry/spell/SummonSkeletonLegion.java @@ -1,26 +1,21 @@ package electroblob.wizardry.spell; -import electroblob.wizardry.constants.Element; -import electroblob.wizardry.constants.SpellType; -import electroblob.wizardry.constants.Tier; +import electroblob.wizardry.EnumElement; +import electroblob.wizardry.EnumSpellType; +import electroblob.wizardry.EnumTier; +import electroblob.wizardry.Wizardry; import electroblob.wizardry.entity.living.EntitySkeletonMinion; -import electroblob.wizardry.registry.WizardryItems; -import electroblob.wizardry.util.SpellModifiers; -import electroblob.wizardry.util.WizardryUtilities; import net.minecraft.entity.player.EntityPlayer; import net.minecraft.init.Items; -import net.minecraft.init.SoundEvents; -import net.minecraft.inventory.EntityEquipmentSlot; import net.minecraft.item.EnumAction; import net.minecraft.item.ItemStack; -import net.minecraft.util.EnumHand; -import net.minecraft.util.math.BlockPos; +import net.minecraft.util.StatCollector; import net.minecraft.world.World; public class SummonSkeletonLegion extends Spell { public SummonSkeletonLegion() { - super(Tier.MASTER, 100, Element.NECROMANCY, "summon_skeleton_legion", SpellType.MINION, 400, EnumAction.BOW, false); + super(EnumTier.MASTER, 100, EnumElement.NECROMANCY, "summon_skeleton_legion", EnumSpellType.MINION, 400, EnumAction.bow, false); } @Override @@ -29,46 +24,56 @@ public class SummonSkeletonLegion extends Spell { } @Override - public boolean cast(World world, EntityPlayer caster, EnumHand hand, int ticksInUse, SpellModifiers modifiers) { - + public boolean cast(World world, EntityPlayer caster, int ticksInUse, float damageMultiplier, float rangeMultiplier, float durationMultiplier, float blastMultiplier) { + + boolean flag = false; + if(!world.isRemote){ - // Archers for(int i=0;i<3;i++){ - // This is all so much neater now thanks to this method. - BlockPos pos = WizardryUtilities.findNearbyFloorSpace(caster, 3, 6); - // The spell instantly fails if no space was found (see javadoc for the above method). - if(pos == null) return false; - - EntitySkeletonMinion skeleton = new EntitySkeletonMinion(world, pos.getX() + 0.5, pos.getY(), pos.getZ() + 0.5, caster, (int)(1200*modifiers.get(WizardryItems.duration_upgrade))); - skeleton.setItemStackToSlot(EntityEquipmentSlot.MAINHAND, new ItemStack(Items.BOW)); - skeleton.setItemStackToSlot(EntityEquipmentSlot.HEAD, new ItemStack(Items.CHAINMAIL_HELMET)); - skeleton.setItemStackToSlot(EntityEquipmentSlot.CHEST, new ItemStack(Items.CHAINMAIL_CHESTPLATE)); - skeleton.setDropChance(EntityEquipmentSlot.MAINHAND, 0.0f); - skeleton.setDropChance(EntityEquipmentSlot.HEAD, 0.0f); - skeleton.setDropChance(EntityEquipmentSlot.CHEST, 0.0f); - world.spawnEntityInWorld(skeleton); + double x = caster.posX + world.rand.nextDouble()*6 - 3; + double z = caster.posZ + world.rand.nextDouble()*6 - 3; + // Allows for height variation. + if(world.getTopSolidOrLiquidBlock((int)x, (int)z) - caster.posY < 6){ + flag = true; + double y = Math.max(caster.posY, world.getTopSolidOrLiquidBlock((int)x, (int)z)); + EntitySkeletonMinion skeleton = new EntitySkeletonMinion(world, x, y, z, caster, (int)(1200*durationMultiplier)); + if(Wizardry.showSummonedCreatureNames) skeleton.setCustomNameTag(StatCollector.translateToLocalFormatted("entity.wizardry.summonedcreature.nameplate", caster.getCommandSenderName(), skeleton.getCommandSenderName())); + skeleton.setCurrentItemOrArmor(0, new ItemStack(Items.bow)); + skeleton.setCurrentItemOrArmor(4, new ItemStack(Items.chainmail_helmet)); + skeleton.setCurrentItemOrArmor(3, new ItemStack(Items.chainmail_chestplate)); + skeleton.setEquipmentDropChance(0, 0.0f); + skeleton.setEquipmentDropChance(3, 0.0f); + skeleton.setEquipmentDropChance(4, 0.0f); + world.spawnEntityInWorld(skeleton); + } } - // Swordsmen + for(int i=0;i<3;i++){ - - BlockPos pos = WizardryUtilities.findNearbyFloorSpace(caster, 3, 6); - // The spell instantly fails if no space was found (see javadoc for the above method). - if(pos == null) return false; - - EntitySkeletonMinion skeleton = new EntitySkeletonMinion(world, pos.getX() + 0.5, pos.getY(), pos.getZ() + 0.5, caster, (int)(1200*modifiers.get(WizardryItems.duration_upgrade))); - skeleton.setItemStackToSlot(EntityEquipmentSlot.MAINHAND, new ItemStack(Items.IRON_SWORD)); - skeleton.setItemStackToSlot(EntityEquipmentSlot.HEAD, new ItemStack(Items.CHAINMAIL_HELMET)); - skeleton.setItemStackToSlot(EntityEquipmentSlot.CHEST, new ItemStack(Items.CHAINMAIL_CHESTPLATE)); - skeleton.setDropChance(EntityEquipmentSlot.MAINHAND, 0.0f); - skeleton.setDropChance(EntityEquipmentSlot.HEAD, 0.0f); - skeleton.setDropChance(EntityEquipmentSlot.CHEST, 0.0f); - world.spawnEntityInWorld(skeleton); + double x = caster.posX + world.rand.nextDouble()*6 - 3; + double z = caster.posZ + world.rand.nextDouble()*6 - 3; + // Allows for height variation. + if(world.getTopSolidOrLiquidBlock((int)x, (int)z) - caster.posY < 6){ + flag = true; + double y = Math.max(caster.posY, world.getTopSolidOrLiquidBlock((int)x, (int)z)); + EntitySkeletonMinion skeleton = new EntitySkeletonMinion(world, x, y, z, caster, (int)(1200*durationMultiplier)); + if(Wizardry.showSummonedCreatureNames) skeleton.setCustomNameTag(StatCollector.translateToLocalFormatted("entity.wizardry.summonedcreature.nameplate", caster.getCommandSenderName(), skeleton.getCommandSenderName())); + skeleton.setCurrentItemOrArmor(0, new ItemStack(Items.iron_sword)); + skeleton.setCurrentItemOrArmor(4, new ItemStack(Items.chainmail_helmet)); + skeleton.setCurrentItemOrArmor(3, new ItemStack(Items.chainmail_chestplate)); + skeleton.setEquipmentDropChance(0, 0.0f); + skeleton.setEquipmentDropChance(3, 0.0f); + skeleton.setEquipmentDropChance(4, 0.0f); + world.spawnEntityInWorld(skeleton); + } } } - // Can't possibly get this far if nothing was spawned. - WizardryUtilities.playSoundAtPlayer(caster, SoundEvents.ENTITY_WITHER_SPAWN, 1.0F, world.rand.nextFloat() * 0.2F + 1.0F); - return true; + if(flag){ + world.playSoundAtEntity(caster, "mob.wither.spawn", 1.0F, world.rand.nextFloat() * 0.2F + 1.0F); + } + + // If no skeletons were spawned (like in a 1x1 hole or something) then the spell does not use up mana. + return flag; } diff --git a/src/main/java/electroblob/wizardry/spell/SummonSnowGolem.java b/src/main/java/electroblob/wizardry/spell/SummonSnowGolem.java index 7867c74a..37818847 100644 --- a/src/main/java/electroblob/wizardry/spell/SummonSnowGolem.java +++ b/src/main/java/electroblob/wizardry/spell/SummonSnowGolem.java @@ -1,46 +1,50 @@ package electroblob.wizardry.spell; +import electroblob.wizardry.EnumElement; +import electroblob.wizardry.EnumParticleType; +import electroblob.wizardry.EnumSpellType; +import electroblob.wizardry.EnumTier; import electroblob.wizardry.Wizardry; -import electroblob.wizardry.constants.Element; -import electroblob.wizardry.constants.SpellType; -import electroblob.wizardry.constants.Tier; -import electroblob.wizardry.registry.WizardrySounds; -import electroblob.wizardry.util.SpellModifiers; -import electroblob.wizardry.util.WizardryParticleType; -import electroblob.wizardry.util.WizardryUtilities; +import electroblob.wizardry.WizardryUtilities; import net.minecraft.entity.monster.EntitySnowman; import net.minecraft.entity.player.EntityPlayer; import net.minecraft.item.EnumAction; -import net.minecraft.util.EnumHand; -import net.minecraft.util.math.BlockPos; +import net.minecraft.util.StatCollector; import net.minecraft.world.World; public class SummonSnowGolem extends Spell { public SummonSnowGolem() { - super(Tier.APPRENTICE, 15, Element.ICE, "summon_snow_golem", SpellType.MINION, 20, EnumAction.BOW, false); + super(EnumTier.APPRENTICE, 15, EnumElement.ICE, "summon_snow_golem", EnumSpellType.MINION, 20, EnumAction.bow, false); } @Override - public boolean cast(World world, EntityPlayer caster, EnumHand hand, int ticksInUse, SpellModifiers modifiers) { + public boolean cast(World world, EntityPlayer caster, int ticksInUse, float damageMultiplier, float rangeMultiplier, float durationMultiplier, float blastMultiplier) { - BlockPos pos = WizardryUtilities.findNearbyFloorSpace(caster, 2, 4); - if(pos == null) return false; + double x = caster.posX + world.rand.nextDouble()*4 - 2; + double z = caster.posZ + world.rand.nextDouble()*4 - 2; + + // Allows for height variation. + if(WizardryUtilities.getNearestFloorLevel(world, (int)x, (int)caster.posY, (int)z, 5) == -1){ + return false; + } + double y2 = Math.max(caster.posY, WizardryUtilities.getNearestFloorLevel(world, (int)x, (int)caster.posY, (int)z, 5)); if(!world.isRemote){ EntitySnowman snowman = new EntitySnowman(world); - snowman.setPosition(pos.getX() + 0.5, pos.getY(), pos.getZ() + 0.5); + if(Wizardry.showSummonedCreatureNames) snowman.setCustomNameTag(StatCollector.translateToLocalFormatted("entity.wizardry.summonedcreature.nameplate", caster.getCommandSenderName(), snowman.getCommandSenderName())); + snowman.setPosition(x, y2, z); world.spawnEntityInWorld(snowman); } for(int i=0; i<10; i++){ - double x1 = (double)((float)pos.getX() + world.rand.nextFloat()*2 - 1.0F); - double y1 = (double)((float)pos.getY() + 0.5F + world.rand.nextFloat()); - double z1 = (double)((float)pos.getZ() + world.rand.nextFloat()*2 - 1.0F); + double x1 = (double)((float)x + world.rand.nextFloat()*2 - 1.0F); + double y1 = (double)((float)y2 + 0.5F + world.rand.nextFloat()); + double z1 = (double)((float)z + world.rand.nextFloat()*2 - 1.0F); if(world.isRemote){ - Wizardry.proxy.spawnParticle(WizardryParticleType.SPARKLE, world, x1, y1, z1, 0, 0.1F, 0, 48 + world.rand.nextInt(12), 0.6f, 0.6f, 1.0f); + Wizardry.proxy.spawnParticle(EnumParticleType.SPARKLE, world, x1, y1, z1, 0, 0.1F, 0, 48 + world.rand.nextInt(12), 0.6f, 0.6f, 1.0f); } } - WizardryUtilities.playSoundAtPlayer(caster, WizardrySounds.SPELL_HEAL, 0.7F, world.rand.nextFloat() * 0.4F + 1.0F); + world.playSoundAtEntity(caster, "wizardry:heal", 0.7F, world.rand.nextFloat() * 0.4F + 1.0F); return true; } diff --git a/src/main/java/electroblob/wizardry/spell/SummonSpiritHorse.java b/src/main/java/electroblob/wizardry/spell/SummonSpiritHorse.java index 277bc15c..6579ef94 100644 --- a/src/main/java/electroblob/wizardry/spell/SummonSpiritHorse.java +++ b/src/main/java/electroblob/wizardry/spell/SummonSpiritHorse.java @@ -1,23 +1,21 @@ package electroblob.wizardry.spell; -import electroblob.wizardry.WizardData; -import electroblob.wizardry.constants.Element; -import electroblob.wizardry.constants.SpellType; -import electroblob.wizardry.constants.Tier; +import electroblob.wizardry.EnumElement; +import electroblob.wizardry.EnumSpellType; +import electroblob.wizardry.EnumTier; +import electroblob.wizardry.ExtendedPlayer; +import electroblob.wizardry.Wizardry; +import electroblob.wizardry.WizardryUtilities; import electroblob.wizardry.entity.living.EntitySpiritHorse; -import electroblob.wizardry.registry.WizardrySounds; -import electroblob.wizardry.util.SpellModifiers; -import electroblob.wizardry.util.WizardryUtilities; import net.minecraft.entity.player.EntityPlayer; import net.minecraft.item.EnumAction; -import net.minecraft.util.EnumHand; -import net.minecraft.util.math.BlockPos; +import net.minecraft.util.StatCollector; import net.minecraft.world.World; public class SummonSpiritHorse extends Spell { public SummonSpiritHorse() { - super(Tier.ADVANCED, 50, Element.EARTH, "summon_spirit_horse", SpellType.MINION, 150, EnumAction.BOW, false); + super(EnumTier.ADVANCED, 50, EnumElement.EARTH, "summon_spirit_horse", EnumSpellType.MINION, 150, EnumAction.bow, false); } @Override @@ -26,24 +24,29 @@ public class SummonSpiritHorse extends Spell { } @Override - public boolean cast(World world, EntityPlayer caster, EnumHand hand, int ticksInUse, SpellModifiers modifiers) { + public boolean cast(World world, EntityPlayer caster, int ticksInUse, float damageMultiplier, float rangeMultiplier, float durationMultiplier, float blastMultiplier) { - WizardData properties = WizardData.get(caster); + ExtendedPlayer properties = ExtendedPlayer.get(caster); if(!properties.hasSpiritHorse){ if(!world.isRemote){ - - BlockPos pos = WizardryUtilities.findNearbyFloorSpace(caster, 2, 4); - if(pos == null) return false; + double x1 = caster.posX + world.rand.nextDouble()*4 - 2; + double z1 = caster.posZ + world.rand.nextDouble()*4 - 2; + // Allows for height variation. + if(WizardryUtilities.getNearestFloorLevel(world, (int)x1, (int)caster.posY, (int)z1, 5) == -1){ + return false; + } + double y1 = Math.max(caster.posY, WizardryUtilities.getNearestFloorLevel(world, (int)x1, (int)caster.posY, (int)z1, 5)); EntitySpiritHorse horse = new EntitySpiritHorse(world); - horse.setPosition(pos.getX() + 0.5, pos.getY(), pos.getZ() + 0.5); + if(Wizardry.showSummonedCreatureNames) horse.setCustomNameTag(StatCollector.translateToLocalFormatted("entity.wizardry.summonedcreature.nameplate", caster.getCommandSenderName(), horse.getCommandSenderName())); + horse.setPosition(x1, y1, z1); horse.setTamedBy(caster); horse.setHorseSaddled(true); world.spawnEntityInWorld(horse); } properties.hasSpiritHorse = true; - WizardryUtilities.playSoundAtPlayer(caster, WizardrySounds.SPELL_HEAL, 0.7F, world.rand.nextFloat() * 0.4F + 1.0F); + world.playSoundAtEntity(caster, "wizardry:heal", 0.7F, world.rand.nextFloat() * 0.4F + 1.0F); return true; } return false; diff --git a/src/main/java/electroblob/wizardry/spell/SummonSpiritWolf.java b/src/main/java/electroblob/wizardry/spell/SummonSpiritWolf.java index a7285484..1d0ce482 100644 --- a/src/main/java/electroblob/wizardry/spell/SummonSpiritWolf.java +++ b/src/main/java/electroblob/wizardry/spell/SummonSpiritWolf.java @@ -1,23 +1,23 @@ package electroblob.wizardry.spell; -import electroblob.wizardry.WizardData; -import electroblob.wizardry.constants.Element; -import electroblob.wizardry.constants.SpellType; -import electroblob.wizardry.constants.Tier; +import electroblob.wizardry.EnumElement; +import electroblob.wizardry.EnumSpellType; +import electroblob.wizardry.EnumTier; +import electroblob.wizardry.ExtendedPlayer; +import electroblob.wizardry.Wizardry; +import electroblob.wizardry.WizardryUtilities; import electroblob.wizardry.entity.living.EntitySpiritWolf; -import electroblob.wizardry.registry.WizardrySounds; -import electroblob.wizardry.util.SpellModifiers; -import electroblob.wizardry.util.WizardryUtilities; +import net.minecraft.entity.EntityLiving; +import net.minecraft.entity.EntityLivingBase; import net.minecraft.entity.player.EntityPlayer; import net.minecraft.item.EnumAction; -import net.minecraft.util.EnumHand; -import net.minecraft.util.math.BlockPos; +import net.minecraft.util.StatCollector; import net.minecraft.world.World; public class SummonSpiritWolf extends Spell { public SummonSpiritWolf() { - super(Tier.APPRENTICE, 25, Element.EARTH, "summon_spirit_wolf", SpellType.MINION, 100, EnumAction.BOW, false); + super(EnumTier.APPRENTICE, 25, EnumElement.EARTH, "summon_spirit_wolf", EnumSpellType.MINION, 100, EnumAction.bow, false); } @Override @@ -26,24 +26,29 @@ public class SummonSpiritWolf extends Spell { } @Override - public boolean cast(World world, EntityPlayer caster, EnumHand hand, int ticksInUse, SpellModifiers modifiers) { + public boolean cast(World world, EntityPlayer caster, int ticksInUse, float damageMultiplier, float rangeMultiplier, float durationMultiplier, float blastMultiplier) { - WizardData properties = WizardData.get(caster); + ExtendedPlayer properties = ExtendedPlayer.get(caster); if(!properties.hasSpiritWolf){ if(!world.isRemote){ - - BlockPos pos = WizardryUtilities.findNearbyFloorSpace(caster, 2, 4); - if(pos == null) return false; + double x1 = caster.posX + world.rand.nextDouble()*4 - 2; + double z1 = caster.posZ + world.rand.nextDouble()*4 - 2; + // Allows for height variation. + if(WizardryUtilities.getNearestFloorLevel(world, (int)x1, (int)caster.posY, (int)z1, 5) == -1){ + return false; + } + double y1 = Math.max(caster.posY, WizardryUtilities.getNearestFloorLevel(world, (int)x1, (int)caster.posY, (int)z1, 5)); EntitySpiritWolf wolf = new EntitySpiritWolf(world); - wolf.setPosition(pos.getX() + 0.5, pos.getY(), pos.getZ() + 0.5); + if(Wizardry.showSummonedCreatureNames) wolf.setCustomNameTag(StatCollector.translateToLocalFormatted("entity.wizardry.summonedcreature.nameplate", caster.getCommandSenderName(), wolf.getCommandSenderName())); + wolf.setPosition(x1, y1, z1); wolf.setTamed(true); - wolf.setOwnerId(caster.getUniqueID()); + wolf.func_152115_b(caster.getUniqueID().toString()); world.spawnEntityInWorld(wolf); } properties.hasSpiritWolf = true; - WizardryUtilities.playSoundAtPlayer(caster, WizardrySounds.SPELL_HEAL, 0.7F, world.rand.nextFloat() * 0.4F + 1.0F); + world.playSoundAtEntity(caster, "wizardry:heal", 0.7F, world.rand.nextFloat() * 0.4F + 1.0F); return true; } return false; diff --git a/src/main/java/electroblob/wizardry/spell/SummonStormElemental.java b/src/main/java/electroblob/wizardry/spell/SummonStormElemental.java index 2e61aa45..8796ff9c 100644 --- a/src/main/java/electroblob/wizardry/spell/SummonStormElemental.java +++ b/src/main/java/electroblob/wizardry/spell/SummonStormElemental.java @@ -1,23 +1,20 @@ package electroblob.wizardry.spell; -import electroblob.wizardry.constants.Element; -import electroblob.wizardry.constants.SpellType; -import electroblob.wizardry.constants.Tier; +import electroblob.wizardry.EnumElement; +import electroblob.wizardry.EnumSpellType; +import electroblob.wizardry.EnumTier; +import electroblob.wizardry.Wizardry; +import electroblob.wizardry.WizardryUtilities; import electroblob.wizardry.entity.living.EntityStormElemental; -import electroblob.wizardry.registry.WizardryItems; -import electroblob.wizardry.util.SpellModifiers; -import electroblob.wizardry.util.WizardryUtilities; import net.minecraft.entity.player.EntityPlayer; -import net.minecraft.init.SoundEvents; import net.minecraft.item.EnumAction; -import net.minecraft.util.EnumHand; -import net.minecraft.util.math.BlockPos; +import net.minecraft.util.StatCollector; import net.minecraft.world.World; public class SummonStormElemental extends Spell { public SummonStormElemental() { - super(Tier.MASTER, 100, Element.LIGHTNING, "summon_storm_elemental", SpellType.MINION, 400, EnumAction.BOW, false); + super(EnumTier.MASTER, 100, EnumElement.LIGHTNING, "summon_storm_elemental", EnumSpellType.MINION, 400, EnumAction.bow, false); } @Override @@ -26,17 +23,22 @@ public class SummonStormElemental extends Spell { } @Override - public boolean cast(World world, EntityPlayer caster, EnumHand hand, int ticksInUse, SpellModifiers modifiers) { + public boolean cast(World world, EntityPlayer caster, int ticksInUse, float damageMultiplier, float rangeMultiplier, float durationMultiplier, float blastMultiplier) { if(!world.isRemote){ - - BlockPos pos = WizardryUtilities.findNearbyFloorSpace(caster, 2, 4); - if(pos == null) return false; + double x = caster.posX + world.rand.nextDouble()*4 - 2; + double z = caster.posZ + world.rand.nextDouble()*4 - 2; + // Allows for height variation. + if(WizardryUtilities.getNearestFloorLevel(world, (int)x, (int)caster.posY, (int)z, 5) == -1){ + return false; + } + double y = Math.max(caster.posY, WizardryUtilities.getNearestFloorLevel(world, (int)x, (int)caster.posY, (int)z, 5)); - EntityStormElemental stormElemental = new EntityStormElemental(world, pos.getX() + 0.5, pos.getY(), pos.getZ() + 0.5, caster, (int)(600*modifiers.get(WizardryItems.duration_upgrade))); + EntityStormElemental stormElemental = new EntityStormElemental(world, x, y, z, caster, (int)(600*durationMultiplier)); + if(Wizardry.showSummonedCreatureNames) stormElemental.setCustomNameTag(StatCollector.translateToLocalFormatted("entity.wizardry.summonedcreature.nameplate", caster.getCommandSenderName(), stormElemental.getCommandSenderName())); world.spawnEntityInWorld(stormElemental); } - WizardryUtilities.playSoundAtPlayer(caster, SoundEvents.ENTITY_WITHER_AMBIENT, 1.0F, world.rand.nextFloat() * 0.2F + 1.0F); + world.playSoundAtEntity(caster, "mob.wither.idle", 1.0F, world.rand.nextFloat() * 0.2F + 1.0F); return true; } } diff --git a/src/main/java/electroblob/wizardry/spell/SummonWitherSkeleton.java b/src/main/java/electroblob/wizardry/spell/SummonWitherSkeleton.java index d8b6c22a..506328d8 100644 --- a/src/main/java/electroblob/wizardry/spell/SummonWitherSkeleton.java +++ b/src/main/java/electroblob/wizardry/spell/SummonWitherSkeleton.java @@ -1,29 +1,24 @@ package electroblob.wizardry.spell; -import electroblob.wizardry.constants.Element; -import electroblob.wizardry.constants.SpellType; -import electroblob.wizardry.constants.Tier; +import electroblob.wizardry.EnumElement; +import electroblob.wizardry.EnumSpellType; +import electroblob.wizardry.EnumTier; +import electroblob.wizardry.Wizardry; +import electroblob.wizardry.WizardryUtilities; import electroblob.wizardry.entity.living.EntitySkeletonMinion; -import electroblob.wizardry.registry.WizardryItems; -import electroblob.wizardry.registry.WizardrySounds; -import electroblob.wizardry.util.SpellModifiers; -import electroblob.wizardry.util.WizardryUtilities; import net.minecraft.entity.EntityLiving; import net.minecraft.entity.EntityLivingBase; -import net.minecraft.entity.monster.SkeletonType; import net.minecraft.entity.player.EntityPlayer; import net.minecraft.init.Items; -import net.minecraft.inventory.EntityEquipmentSlot; import net.minecraft.item.EnumAction; import net.minecraft.item.ItemStack; -import net.minecraft.util.EnumHand; -import net.minecraft.util.math.BlockPos; +import net.minecraft.util.StatCollector; import net.minecraft.world.World; public class SummonWitherSkeleton extends Spell { public SummonWitherSkeleton() { - super(Tier.ADVANCED, 35, Element.NECROMANCY, "summon_wither_skeleton", SpellType.MINION, 150, EnumAction.BOW, false); + super(EnumTier.ADVANCED, 35, EnumElement.NECROMANCY, "summon_wither_skeleton", EnumSpellType.MINION, 150, EnumAction.bow, false); } @Override @@ -32,38 +27,48 @@ public class SummonWitherSkeleton extends Spell { } @Override - public boolean cast(World world, EntityPlayer caster, EnumHand hand, int ticksInUse, SpellModifiers modifiers) { + public boolean cast(World world, EntityPlayer caster, int ticksInUse, float damageMultiplier, float rangeMultiplier, float durationMultiplier, float blastMultiplier) { if(!world.isRemote){ - - BlockPos pos = WizardryUtilities.findNearbyFloorSpace(caster, 2, 4); - if(pos == null) return false; + double x = caster.posX + world.rand.nextDouble()*4 - 2; + double z = caster.posZ + world.rand.nextDouble()*4 - 2; + // Allows for height variation. + if(WizardryUtilities.getNearestFloorLevel(world, (int)x, (int)caster.posY, (int)z, 5) == -1){ + return false; + } + double y = Math.max(caster.posY, WizardryUtilities.getNearestFloorLevel(world, (int)x, (int)caster.posY, (int)z, 5)); - EntitySkeletonMinion skeleton = new EntitySkeletonMinion(world, pos.getX() + 0.5, pos.getY(), pos.getZ() + 0.5, caster, (int)(600*modifiers.get(WizardryItems.duration_upgrade))); - skeleton.setSkeletonType(SkeletonType.WITHER); - skeleton.setItemStackToSlot(EntityEquipmentSlot.MAINHAND, new ItemStack(Items.STONE_SWORD)); - skeleton.setDropChance(EntityEquipmentSlot.MAINHAND, 0.0f); + EntitySkeletonMinion skeleton = new EntitySkeletonMinion(world, x, y, z, caster, (int)(600*durationMultiplier)); + if(Wizardry.showSummonedCreatureNames) skeleton.setCustomNameTag(StatCollector.translateToLocalFormatted("entity.wizardry.summonedcreature.nameplate", caster.getCommandSenderName(), skeleton.getCommandSenderName())); + skeleton.setSkeletonType(1); + skeleton.setCurrentItemOrArmor(0, new ItemStack(Items.stone_sword)); + skeleton.setEquipmentDropChance(0, 0.0f); world.spawnEntityInWorld(skeleton); } - WizardryUtilities.playSoundAtPlayer(caster, WizardrySounds.SPELL_SUMMONING, 7.0f, 0.6f); + world.playSoundAtEntity(caster, "wizardry:darkaura", 7.0f, 0.6f); return true; } @Override - public boolean cast(World world, EntityLiving caster, EnumHand hand, int ticksInUse, EntityLivingBase target, SpellModifiers modifiers){ + public boolean cast(World world, EntityLiving caster, EntityLivingBase target, float damageMultiplier, float rangeMultiplier, float durationMultiplier, float blastMultiplier){ if(!world.isRemote){ - - BlockPos pos = WizardryUtilities.findNearbyFloorSpace(caster, 2, 4); - if(pos == null) return false; + double x = caster.posX + world.rand.nextDouble()*4 - 2; + double z = caster.posZ + world.rand.nextDouble()*4 - 2; + // Allows for height variation. + if(WizardryUtilities.getNearestFloorLevel(world, (int)x, (int)caster.posY, (int)z, 5) == -1){ + return false; + } + double y = Math.max(caster.posY, WizardryUtilities.getNearestFloorLevel(world, (int)x, (int)caster.posY, (int)z, 5)); - EntitySkeletonMinion skeleton = new EntitySkeletonMinion(world, pos.getX() + 0.5, pos.getY(), pos.getZ() + 0.5, caster, (int)(600*modifiers.get(WizardryItems.duration_upgrade))); - skeleton.setSkeletonType(SkeletonType.WITHER); - skeleton.setItemStackToSlot(EntityEquipmentSlot.MAINHAND, new ItemStack(Items.STONE_SWORD)); - skeleton.setDropChance(EntityEquipmentSlot.MAINHAND, 0.0f); + EntitySkeletonMinion skeleton = new EntitySkeletonMinion(world, x, y, z, caster, (int)(600*durationMultiplier)); + if(Wizardry.showSummonedCreatureNames) skeleton.setCustomNameTag(StatCollector.translateToLocalFormatted("entity.wizardry.summonedcreature.nameplate", caster.getCommandSenderName(), skeleton.getCommandSenderName())); + skeleton.setSkeletonType(1); + skeleton.setCurrentItemOrArmor(0, new ItemStack(Items.stone_sword)); + skeleton.setEquipmentDropChance(0, 0.0f); world.spawnEntityInWorld(skeleton); } - caster.playSound(WizardrySounds.SPELL_SUMMONING, 7.0f, 0.6f); + world.playSoundAtEntity(caster, "wizardry:darkaura", 7.0f, 0.6f); return true; } diff --git a/src/main/java/electroblob/wizardry/spell/SummonZombie.java b/src/main/java/electroblob/wizardry/spell/SummonZombie.java index 21718a87..8d487ff3 100644 --- a/src/main/java/electroblob/wizardry/spell/SummonZombie.java +++ b/src/main/java/electroblob/wizardry/spell/SummonZombie.java @@ -1,25 +1,22 @@ package electroblob.wizardry.spell; -import electroblob.wizardry.constants.Element; -import electroblob.wizardry.constants.SpellType; -import electroblob.wizardry.constants.Tier; +import electroblob.wizardry.EnumElement; +import electroblob.wizardry.EnumSpellType; +import electroblob.wizardry.EnumTier; +import electroblob.wizardry.Wizardry; +import electroblob.wizardry.WizardryUtilities; import electroblob.wizardry.entity.living.EntityZombieMinion; -import electroblob.wizardry.registry.WizardryItems; -import electroblob.wizardry.registry.WizardrySounds; -import electroblob.wizardry.util.SpellModifiers; -import electroblob.wizardry.util.WizardryUtilities; import net.minecraft.entity.EntityLiving; import net.minecraft.entity.EntityLivingBase; import net.minecraft.entity.player.EntityPlayer; import net.minecraft.item.EnumAction; -import net.minecraft.util.EnumHand; -import net.minecraft.util.math.BlockPos; +import net.minecraft.util.StatCollector; import net.minecraft.world.World; public class SummonZombie extends Spell { public SummonZombie() { - super(Tier.BASIC, 10, Element.NECROMANCY, "summon_zombie", SpellType.MINION, 40, EnumAction.BOW, false); + super(EnumTier.BASIC, 10, EnumElement.NECROMANCY, "summon_zombie", EnumSpellType.MINION, 40, EnumAction.bow, false); } @Override @@ -28,31 +25,40 @@ public class SummonZombie extends Spell { } @Override - public boolean cast(World world, EntityPlayer caster, EnumHand hand, int ticksInUse, SpellModifiers modifiers) { + public boolean cast(World world, EntityPlayer caster, int ticksInUse, float damageMultiplier, float rangeMultiplier, float durationMultiplier, float blastMultiplier) { if(!world.isRemote){ - - BlockPos pos = WizardryUtilities.findNearbyFloorSpace(caster, 2, 4); - if(pos == null) return false; - EntityZombieMinion zombie = new EntityZombieMinion(world, pos.getX() + 0.5, pos.getY(), pos.getZ() + 0.5, caster, (int)(600*modifiers.get(WizardryItems.duration_upgrade))); + double x = caster.posX + world.rand.nextDouble()*4 - 2; + double z = caster.posZ + world.rand.nextDouble()*4 - 2; + // Allows for height variation. + if(WizardryUtilities.getNearestFloorLevel(world, (int)x, (int)caster.posY, (int)z, 5) == -1){ + return false; + } + double y = Math.max(caster.posY, WizardryUtilities.getNearestFloorLevel(world, (int)x, (int)caster.posY, (int)z, 5)); + EntityZombieMinion zombie = new EntityZombieMinion(world, x, y, z, caster, (int)(600*durationMultiplier)); + if(Wizardry.showSummonedCreatureNames) zombie.setCustomNameTag(StatCollector.translateToLocalFormatted("entity.wizardry.summonedcreature.nameplate", caster.getCommandSenderName(), zombie.getCommandSenderName())); world.spawnEntityInWorld(zombie); } - WizardryUtilities.playSoundAtPlayer(caster, WizardrySounds.SPELL_SUMMONING, 7.0f, 0.6f); + world.playSoundAtEntity(caster, "wizardry:darkaura", 7.0f, 0.6f); return true; } @Override - public boolean cast(World world, EntityLiving caster, EnumHand hand, int ticksInUse, EntityLivingBase target, SpellModifiers modifiers){ + public boolean cast(World world, EntityLiving caster, EntityLivingBase target, float damageMultiplier, float rangeMultiplier, float durationMultiplier, float blastMultiplier){ if(!world.isRemote){ - - BlockPos pos = WizardryUtilities.findNearbyFloorSpace(caster, 2, 4); - if(pos == null) return false; - - EntityZombieMinion zombie = new EntityZombieMinion(world, pos.getX() + 0.5, pos.getY(), pos.getZ() + 0.5, caster, (int)(600*modifiers.get(WizardryItems.duration_upgrade))); + double x = caster.posX + world.rand.nextDouble()*4 - 2; + double z = caster.posZ + world.rand.nextDouble()*4 - 2; + // Allows for height variation. + if(WizardryUtilities.getNearestFloorLevel(world, (int)x, (int)caster.posY, (int)z, 5) == -1){ + return false; + } + double y = Math.max(caster.posY, WizardryUtilities.getNearestFloorLevel(world, (int)x, (int)caster.posY, (int)z, 5)); + EntityZombieMinion zombie = new EntityZombieMinion(world, x, y, z, caster, (int)(600*durationMultiplier)); + if(Wizardry.showSummonedCreatureNames) zombie.setCustomNameTag(StatCollector.translateToLocalFormatted("entity.wizardry.summonedcreature.nameplate", caster.getCommandSenderName(), zombie.getCommandSenderName())); world.spawnEntityInWorld(zombie); } - caster.playSound(WizardrySounds.SPELL_SUMMONING, 7.0f, 0.6f); + world.playSoundAtEntity(caster, "wizardry:darkaura", 7.0f, 0.6f); return true; } diff --git a/src/main/java/electroblob/wizardry/spell/Telekinesis.java b/src/main/java/electroblob/wizardry/spell/Telekinesis.java index d46608ea..9b079a95 100644 --- a/src/main/java/electroblob/wizardry/spell/Telekinesis.java +++ b/src/main/java/electroblob/wizardry/spell/Telekinesis.java @@ -1,29 +1,25 @@ package electroblob.wizardry.spell; +import electroblob.wizardry.EnumElement; +import electroblob.wizardry.EnumSpellType; +import electroblob.wizardry.EnumTier; import electroblob.wizardry.Wizardry; -import electroblob.wizardry.constants.Element; -import electroblob.wizardry.constants.SpellType; -import electroblob.wizardry.constants.Tier; -import electroblob.wizardry.registry.WizardryItems; -import electroblob.wizardry.registry.WizardrySounds; -import electroblob.wizardry.util.SpellModifiers; -import electroblob.wizardry.util.WizardryUtilities; -import net.minecraft.block.state.IBlockState; +import electroblob.wizardry.WizardryUtilities; +import net.minecraft.block.Block; import net.minecraft.entity.Entity; import net.minecraft.entity.EntityLiving; import net.minecraft.entity.EntityLivingBase; import net.minecraft.entity.item.EntityItem; import net.minecraft.entity.player.EntityPlayer; import net.minecraft.item.EnumAction; -import net.minecraft.util.EnumHand; -import net.minecraft.util.math.BlockPos; -import net.minecraft.util.math.RayTraceResult; +import net.minecraft.util.MovingObjectPosition; +import net.minecraft.util.MovingObjectPosition.MovingObjectType; import net.minecraft.world.World; public class Telekinesis extends Spell { public Telekinesis() { - super(Tier.BASIC, 5, Element.SORCERY, "telekinesis", SpellType.UTILITY, 5, EnumAction.NONE, false); + super(EnumTier.BASIC, 5, EnumElement.SORCERY, "telekinesis", EnumSpellType.UTILITY, 5, EnumAction.none, false); } @Override @@ -32,9 +28,9 @@ public class Telekinesis extends Spell { } @Override - public boolean cast(World world, EntityPlayer caster, EnumHand hand, int ticksInUse, SpellModifiers modifiers) { + public boolean cast(World world, EntityPlayer caster, int ticksInUse, float damageMultiplier, float rangeMultiplier, float durationMultiplier, float blastMultiplier) { - RayTraceResult rayTrace = WizardryUtilities.standardEntityRayTrace(world, caster, 8*modifiers.get(WizardryItems.range_upgrade), 3.0f); + MovingObjectPosition rayTrace = WizardryUtilities.standardEntityRayTrace(world, caster, 8*rangeMultiplier, 3.0f); if(rayTrace != null && rayTrace.entityHit != null){ @@ -44,62 +40,55 @@ public class Telekinesis extends Spell { entityHit.motionX = (caster.posX - entityHit.posX)/6; entityHit.motionY = (caster.posY + caster.eyeHeight - entityHit.posY)/6; entityHit.motionZ = (caster.posZ - entityHit.posZ)/6; - entityHit.playSound(WizardrySounds.SPELL_CONJURATION, 1.0F, 1.0f); - caster.swingArm(hand); + world.playSoundAtEntity(entityHit, "wizardry:aura", 1.0F, 1.0f); + caster.swingItem(); return true; - }else if(rayTrace.entityHit instanceof EntityPlayer && Wizardry.settings.telekineticDisarmament){ + }else if(rayTrace.entityHit instanceof EntityPlayer && Wizardry.telekineticDisarmament){ EntityPlayer target = (EntityPlayer)rayTrace.entityHit; - if(target.getHeldItemMainhand() != null){ + if(target.getHeldItem() != null){ if(!world.isRemote){ - EntityItem item = target.entityDropItem(target.getHeldItemMainhand(), 0.0f); + EntityItem item = target.entityDropItem(target.getHeldItem(), 0.0f); // Makes the item move towards the caster item.motionX = (caster.posX - target.posX)/20; item.motionZ = (caster.posZ - target.posZ)/20; } - - target.setHeldItem(EnumHand.MAIN_HAND, null); + target.setCurrentItemOrArmor(0, null); - target.playSound(WizardrySounds.SPELL_CONJURATION, 1.0F, 1.0f); - caster.swingArm(hand); + world.playSoundAtEntity(target, "wizardry:aura", 1.0F, 1.0f); + caster.swingItem(); return true; } } } - - if(rayTrace != null && rayTrace.typeOfHit == RayTraceResult.Type.BLOCK){ - - IBlockState blockstate = world.getBlockState(new BlockPos(rayTrace.getBlockPos())); - - if(blockstate.getBlock().onBlockActivated(world, rayTrace.getBlockPos(), blockstate, caster, hand, null, rayTrace.sideHit, 0, 0, 0)){ - WizardryUtilities.playSoundAtPlayer(caster, WizardrySounds.SPELL_CONJURATION, 1.0F, 1.0f); - caster.swingArm(hand); + if(rayTrace != null && rayTrace.typeOfHit == MovingObjectType.BLOCK){ + Block block = world.getBlock(rayTrace.blockX, rayTrace.blockY, rayTrace.blockZ); + if(block.onBlockActivated(world, rayTrace.blockX, rayTrace.blockY, rayTrace.blockZ, caster, 0, 0, 0, 0)){ + world.playSound(rayTrace.blockX, rayTrace.blockY, rayTrace.blockZ, "wizardry:aura", 1.0F, 1.0f, false); + caster.swingItem(); return true; } } return false; } - public boolean cast(World world, EntityLiving caster, EnumHand hand, int ticksInUse, EntityLivingBase target, SpellModifiers modifiers){ + public boolean cast(World world, EntityLiving caster, EntityLivingBase target, float damageMultiplier, float rangeMultiplier, float durationMultiplier, float blastMultiplier){ - if(target instanceof EntityPlayer && target.getHeldItemMainhand() != null){ + if(target instanceof EntityPlayer && target.getHeldItem() != null){ - // IDEA: Disarm the offhand if the mainhand is empty or otherwise harmless? - if(!world.isRemote){ - EntityItem item = target.entityDropItem(target.getHeldItemMainhand(), 0.0f); + EntityItem item = target.entityDropItem(target.getHeldItem(), 0.0f); // Makes the item move towards the caster item.motionX = (caster.posX - target.posX)/20; item.motionZ = (caster.posZ - target.posZ)/20; } - - target.setHeldItem(EnumHand.MAIN_HAND, null); + target.setCurrentItemOrArmor(0, null); - target.playSound(WizardrySounds.SPELL_CONJURATION, 1.0F, 1.0f); - caster.swingArm(hand); + world.playSoundAtEntity(target, "wizardry:aura", 1.0F, 1.0f); + caster.swingItem(); return true; } diff --git a/src/main/java/electroblob/wizardry/spell/Thunderbolt.java b/src/main/java/electroblob/wizardry/spell/Thunderbolt.java index a146975f..7ea6aa5b 100644 --- a/src/main/java/electroblob/wizardry/spell/Thunderbolt.java +++ b/src/main/java/electroblob/wizardry/spell/Thunderbolt.java @@ -1,23 +1,19 @@ package electroblob.wizardry.spell; -import electroblob.wizardry.constants.Element; -import electroblob.wizardry.constants.SpellType; -import electroblob.wizardry.constants.Tier; +import electroblob.wizardry.EnumElement; +import electroblob.wizardry.EnumSpellType; +import electroblob.wizardry.EnumTier; import electroblob.wizardry.entity.projectile.EntityThunderbolt; -import electroblob.wizardry.registry.WizardrySounds; -import electroblob.wizardry.util.SpellModifiers; -import electroblob.wizardry.util.WizardryUtilities; import net.minecraft.entity.EntityLiving; import net.minecraft.entity.EntityLivingBase; import net.minecraft.entity.player.EntityPlayer; import net.minecraft.item.EnumAction; -import net.minecraft.util.EnumHand; import net.minecraft.world.World; public class Thunderbolt extends Spell { public Thunderbolt() { - super(Tier.BASIC, 10, Element.LIGHTNING, "thunderbolt", SpellType.ATTACK, 15, EnumAction.NONE, false); + super(EnumTier.BASIC, 10, EnumElement.LIGHTNING, "thunderbolt", EnumSpellType.ATTACK, 15, EnumAction.none, false); } @Override @@ -26,31 +22,31 @@ public class Thunderbolt extends Spell { } @Override - public boolean cast(World world, EntityPlayer caster, EnumHand hand, int ticksInUse, SpellModifiers modifiers) { + public boolean cast(World world, EntityPlayer caster, int ticksInUse, float damageMultiplier, float rangeMultiplier, float durationMultiplier, float blastMultiplier) { if(!world.isRemote){ - EntityThunderbolt thunderbolt = new EntityThunderbolt(world, caster, modifiers.get(SpellModifiers.DAMAGE)); + EntityThunderbolt thunderbolt = new EntityThunderbolt(world, caster, damageMultiplier); world.spawnEntityInWorld(thunderbolt); + world.playSoundAtEntity(caster, "wizardry:ice", 0.8F, world.rand.nextFloat() * 0.2F + 0.8F); } - - WizardryUtilities.playSoundAtPlayer(caster, WizardrySounds.SPELL_ICE, 0.8F, world.rand.nextFloat() * 0.2F + 0.8F); - caster.swingArm(hand); + + caster.swingItem(); return true; } @Override - public boolean cast(World world, EntityLiving caster, EnumHand hand, int ticksInUse, EntityLivingBase target, SpellModifiers modifiers){ + public boolean cast(World world, EntityLiving caster, EntityLivingBase target, float damageMultiplier, float rangeMultiplier, float durationMultiplier, float blastMultiplier){ if(target != null){ if(!world.isRemote){ - EntityThunderbolt thunderbolt = new EntityThunderbolt(world, caster, modifiers.get(SpellModifiers.DAMAGE)); + EntityThunderbolt thunderbolt = new EntityThunderbolt(world, caster, damageMultiplier); thunderbolt.directTowards(target, 2.5f); world.spawnEntityInWorld(thunderbolt); + world.playSoundAtEntity(caster, "wizardry:ice", 0.8F, world.rand.nextFloat() * 0.2F + 0.8F); } - - caster.playSound(WizardrySounds.SPELL_ICE, 0.8F, world.rand.nextFloat() * 0.2F + 0.8F); - caster.swingArm(hand); + + caster.swingItem(); return true; } diff --git a/src/main/java/electroblob/wizardry/spell/Thunderstorm.java b/src/main/java/electroblob/wizardry/spell/Thunderstorm.java index b1992941..83e70ab7 100644 --- a/src/main/java/electroblob/wizardry/spell/Thunderstorm.java +++ b/src/main/java/electroblob/wizardry/spell/Thunderstorm.java @@ -2,49 +2,44 @@ package electroblob.wizardry.spell; import java.util.List; +import electroblob.wizardry.EnumElement; +import electroblob.wizardry.EnumParticleType; +import electroblob.wizardry.EnumSpellType; +import electroblob.wizardry.EnumTier; +import electroblob.wizardry.MagicDamage; import electroblob.wizardry.Wizardry; -import electroblob.wizardry.constants.Element; -import electroblob.wizardry.constants.SpellType; -import electroblob.wizardry.constants.Tier; +import electroblob.wizardry.WizardryUtilities; +import electroblob.wizardry.MagicDamage.DamageType; import electroblob.wizardry.entity.EntityArc; -import electroblob.wizardry.registry.WizardryItems; -import electroblob.wizardry.registry.WizardrySounds; -import electroblob.wizardry.util.MagicDamage; -import electroblob.wizardry.util.MagicDamage.DamageType; -import electroblob.wizardry.util.SpellModifiers; -import electroblob.wizardry.util.WizardryParticleType; -import electroblob.wizardry.util.WizardryUtilities; import net.minecraft.entity.EntityLivingBase; import net.minecraft.entity.effect.EntityLightningBolt; +import net.minecraft.entity.monster.EntityCreeper; import net.minecraft.entity.player.EntityPlayer; import net.minecraft.item.EnumAction; -import net.minecraft.util.EnumHand; -import net.minecraft.util.EnumParticleTypes; -import net.minecraft.util.math.BlockPos; import net.minecraft.world.World; public class Thunderstorm extends Spell { public Thunderstorm() { - super(Tier.MASTER, 100, Element.LIGHTNING, "thunderstorm", SpellType.ATTACK, 250, EnumAction.BOW, false); + super(EnumTier.MASTER, 100, EnumElement.LIGHTNING, "thunderstorm", EnumSpellType.ATTACK, 250, EnumAction.bow, false); } @Override - public boolean cast(World world, EntityPlayer caster, EnumHand hand, int ticksInUse, SpellModifiers modifiers) { + public boolean cast(World world, EntityPlayer caster, int ticksInUse, float damageMultiplier, float rangeMultiplier, float durationMultiplier, float blastMultiplier) { - if(world.canBlockSeeSky(new BlockPos(caster))){ + if(world.canBlockSeeTheSky((int)caster.posX, (int)caster.posY, (int)caster.posZ)){ for(int r=0; r<10; r++){ - double radius = 4 + world.rand.nextDouble()*6*modifiers.get(WizardryItems.blast_upgrade); + double radius = 4 + world.rand.nextDouble()*6*blastMultiplier; double angle = world.rand.nextDouble()*Math.PI*2; double x = caster.posX + radius*Math.cos(angle); double z = caster.posZ + radius*Math.sin(angle); - double y = WizardryUtilities.getNearestFloorLevel(world, new BlockPos(x, caster.posY, z), 10); + double y = WizardryUtilities.getNearestFloorLevel(world, (int)x, (int)caster.posY, (int)z, 10); if(!world.isRemote){ - EntityLightningBolt entitylightning = new EntityLightningBolt(world, x, y, z, false); + EntityLightningBolt entitylightning = new EntityLightningBolt(world, x, y, z); world.addWeatherEffect(entitylightning); } @@ -52,7 +47,7 @@ public class Thunderstorm extends Spell { //NBTTagCompound entityNBT = entitylightning.getEntityData(); //entityNBT.setInteger("summoningPlayer", entityplayer.entityId); - // Secondary chaining effect + //Secondary chaining effect double seekerRange = 10.0d; List secondaryTargets = WizardryUtilities.getEntitiesWithinRadius(seekerRange, x, y+1, z, world); @@ -69,14 +64,14 @@ public class Thunderstorm extends Spell { world.spawnEntityInWorld(arc); }else{ for(int j=0;j<8;j++){ - Wizardry.proxy.spawnParticle(WizardryParticleType.SPARK, world, secondaryTarget.posX + world.rand.nextFloat() - 0.5, secondaryTarget.getEntityBoundingBox().minY + secondaryTarget.height/2 + world.rand.nextFloat()*2 - 1, secondaryTarget.posZ + world.rand.nextFloat() - 0.5, 0, 0, 0, 3); - world.spawnParticle(EnumParticleTypes.SMOKE_LARGE, secondaryTarget.posX + world.rand.nextFloat() - 0.5, secondaryTarget.getEntityBoundingBox().minY + secondaryTarget.height/2 + world.rand.nextFloat()*2 - 1, secondaryTarget.posZ + world.rand.nextFloat() - 0.5, 0, 0, 0); + Wizardry.proxy.spawnParticle(EnumParticleType.SPARK, world, secondaryTarget.posX + world.rand.nextFloat() - 0.5, WizardryUtilities.getEntityFeetPos(secondaryTarget) + secondaryTarget.height/2 + world.rand.nextFloat()*2 - 1, secondaryTarget.posZ + world.rand.nextFloat() - 0.5, 0, 0, 0, 3); + world.spawnParticle("largesmoke", secondaryTarget.posX + world.rand.nextFloat() - 0.5, WizardryUtilities.getEntityFeetPos(secondaryTarget) + secondaryTarget.height/2 + world.rand.nextFloat()*2 - 1, secondaryTarget.posZ + world.rand.nextFloat() - 0.5, 0, 0, 0); } } - secondaryTarget.playSound(WizardrySounds.SPELL_SPARK, 1.0F, world.rand.nextFloat() * 0.4F + 1.5F); + world.playSoundAtEntity(secondaryTarget, "wizardry:arc", 1.0F, world.rand.nextFloat() * 0.4F + 1.5F); - secondaryTarget.attackEntityFrom(MagicDamage.causeDirectMagicDamage(caster, DamageType.SHOCK), 10.0f * modifiers.get(SpellModifiers.DAMAGE)); + secondaryTarget.attackEntityFrom(MagicDamage.causeDirectMagicDamage(caster, DamageType.SHOCK), 10.0f * damageMultiplier); //Tertiary chaining effect @@ -95,14 +90,14 @@ public class Thunderstorm extends Spell { world.spawnEntityInWorld(arc); }else{ for(int k=0;k<8;k++){ - Wizardry.proxy.spawnParticle(WizardryParticleType.SPARK, world, tertiaryTarget.posX + world.rand.nextFloat() - 0.5, tertiaryTarget.getEntityBoundingBox().minY + tertiaryTarget.height/2 + world.rand.nextFloat()*2 - 1, tertiaryTarget.posZ + world.rand.nextFloat() - 0.5, 0, 0, 0, 3); - world.spawnParticle(EnumParticleTypes.SMOKE_LARGE, tertiaryTarget.posX + world.rand.nextFloat() - 0.5, tertiaryTarget.getEntityBoundingBox().minY + tertiaryTarget.height/2 + world.rand.nextFloat()*2 - 1, tertiaryTarget.posZ + world.rand.nextFloat() - 0.5, 0, 0, 0); + Wizardry.proxy.spawnParticle(EnumParticleType.SPARK, world, tertiaryTarget.posX + world.rand.nextFloat() - 0.5, WizardryUtilities.getEntityFeetPos(tertiaryTarget) + tertiaryTarget.height/2 + world.rand.nextFloat()*2 - 1, tertiaryTarget.posZ + world.rand.nextFloat() - 0.5, 0, 0, 0, 3); + world.spawnParticle("largesmoke", tertiaryTarget.posX + world.rand.nextFloat() - 0.5, WizardryUtilities.getEntityFeetPos(tertiaryTarget) + tertiaryTarget.height/2 + world.rand.nextFloat()*2 - 1, tertiaryTarget.posZ + world.rand.nextFloat() - 0.5, 0, 0, 0); } } - tertiaryTarget.playSound(WizardrySounds.SPELL_SPARK, 1.0F, world.rand.nextFloat() * 0.4F + 1.5F); + world.playSoundAtEntity(tertiaryTarget, "wizardry:arc", 1.0F, world.rand.nextFloat() * 0.4F + 1.5F); - tertiaryTarget.attackEntityFrom(MagicDamage.causeDirectMagicDamage(caster, DamageType.SHOCK), 8.0f * modifiers.get(SpellModifiers.DAMAGE)); + tertiaryTarget.attackEntityFrom(MagicDamage.causeDirectMagicDamage(caster, DamageType.SHOCK), 8.0f * damageMultiplier); } } } diff --git a/src/main/java/electroblob/wizardry/spell/Tornado.java b/src/main/java/electroblob/wizardry/spell/Tornado.java index c0f60744..77511207 100644 --- a/src/main/java/electroblob/wizardry/spell/Tornado.java +++ b/src/main/java/electroblob/wizardry/spell/Tornado.java @@ -1,24 +1,20 @@ package electroblob.wizardry.spell; -import electroblob.wizardry.constants.Element; -import electroblob.wizardry.constants.SpellType; -import electroblob.wizardry.constants.Tier; +import electroblob.wizardry.EnumElement; +import electroblob.wizardry.EnumSpellType; +import electroblob.wizardry.EnumTier; import electroblob.wizardry.entity.construct.EntityTornado; -import electroblob.wizardry.registry.WizardryItems; -import electroblob.wizardry.registry.WizardrySounds; -import electroblob.wizardry.util.SpellModifiers; -import electroblob.wizardry.util.WizardryUtilities; +import electroblob.wizardry.entity.projectile.EntityMagicMissile; import net.minecraft.entity.EntityLiving; import net.minecraft.entity.EntityLivingBase; import net.minecraft.entity.player.EntityPlayer; import net.minecraft.item.EnumAction; -import net.minecraft.util.EnumHand; import net.minecraft.world.World; public class Tornado extends Spell { public Tornado() { - super(Tier.ADVANCED, 35, Element.EARTH, "tornado", SpellType.ATTACK, 80, EnumAction.NONE, false); + super(EnumTier.ADVANCED, 35, EnumElement.EARTH, "tornado", EnumSpellType.ATTACK, 80, EnumAction.none, false); } @Override @@ -27,23 +23,23 @@ public class Tornado extends Spell { } @Override - public boolean cast(World world, EntityPlayer caster, EnumHand hand, int ticksInUse, SpellModifiers modifiers) { + public boolean cast(World world, EntityPlayer caster, int ticksInUse, float damageMultiplier, float rangeMultiplier, float durationMultiplier, float blastMultiplier) { if(!world.isRemote){ double x = caster.posX + caster.getLookVec().xCoord; double y = caster.posY; double z = caster.posZ + caster.getLookVec().zCoord; - EntityTornado tornado = new EntityTornado(world, x, y, z, caster, (int)(200*modifiers.get(WizardryItems.duration_upgrade)), caster.getLookVec().xCoord/3, caster.getLookVec().zCoord/3, modifiers.get(SpellModifiers.DAMAGE)); + EntityTornado tornado = new EntityTornado(world, x, y, z, caster, (int)(200*durationMultiplier), caster.getLookVec().xCoord/3, caster.getLookVec().zCoord/3, damageMultiplier); world.spawnEntityInWorld(tornado); } - caster.swingArm(hand); - WizardryUtilities.playSoundAtPlayer(caster, WizardrySounds.SPELL_ICE, 1.0F, 1.0F); + caster.swingItem(); + world.playSoundAtEntity(caster, "wizardry:ice", 1.0F, 1.0F); return true; } @Override - public boolean cast(World world, EntityLiving caster, EnumHand hand, int ticksInUse, EntityLivingBase target, SpellModifiers modifiers){ + public boolean cast(World world, EntityLiving caster, EntityLivingBase target, float damageMultiplier, float rangeMultiplier, float durationMultiplier, float blastMultiplier){ if(target != null){ @@ -52,11 +48,11 @@ public class Tornado extends Spell { double y = caster.posY; double z = caster.posZ + caster.getLookVec().zCoord; - EntityTornado tornado = new EntityTornado(world, x, y, z, caster, (int)(200*modifiers.get(WizardryItems.duration_upgrade)), caster.getLookVec().xCoord/3, caster.getLookVec().zCoord/3, modifiers.get(SpellModifiers.DAMAGE)); + EntityTornado tornado = new EntityTornado(world, x, y, z, caster, (int)(200*durationMultiplier), caster.getLookVec().xCoord/3, caster.getLookVec().zCoord/3, damageMultiplier); world.spawnEntityInWorld(tornado); } - caster.swingArm(hand); - caster.playSound(WizardrySounds.SPELL_ICE, 1.0F, 1.0F); + caster.swingItem(); + world.playSoundAtEntity(caster, "wizardry:ice", 1.0F, 1.0F); return true; } diff --git a/src/main/java/electroblob/wizardry/spell/Transience.java b/src/main/java/electroblob/wizardry/spell/Transience.java index 96d3f1c7..4232e188 100644 --- a/src/main/java/electroblob/wizardry/spell/Transience.java +++ b/src/main/java/electroblob/wizardry/spell/Transience.java @@ -1,30 +1,20 @@ package electroblob.wizardry.spell; -import electroblob.wizardry.constants.Element; -import electroblob.wizardry.constants.SpellType; -import electroblob.wizardry.constants.Tier; -import electroblob.wizardry.registry.WizardryItems; -import electroblob.wizardry.registry.WizardryPotions; -import electroblob.wizardry.registry.WizardrySounds; -import electroblob.wizardry.util.SpellModifiers; -import electroblob.wizardry.util.WizardryUtilities; -import net.minecraft.entity.EntityLivingBase; +import electroblob.wizardry.EnumElement; +import electroblob.wizardry.EnumSpellType; +import electroblob.wizardry.EnumTier; +import electroblob.wizardry.ExtendedPlayer; +import electroblob.wizardry.Wizardry; import net.minecraft.entity.player.EntityPlayer; -import net.minecraft.init.MobEffects; import net.minecraft.item.EnumAction; +import net.minecraft.potion.Potion; import net.minecraft.potion.PotionEffect; -import net.minecraft.util.EnumHand; import net.minecraft.world.World; -import net.minecraftforge.event.entity.living.LivingAttackEvent; -import net.minecraftforge.event.world.BlockEvent; -import net.minecraftforge.fml.common.Mod; -import net.minecraftforge.fml.common.eventhandler.SubscribeEvent; -@Mod.EventBusSubscriber public class Transience extends Spell { public Transience() { - super(Tier.ADVANCED, 50, Element.HEALING, "transience", SpellType.DEFENCE, 100, EnumAction.BOW, false); + super(EnumTier.ADVANCED, 50, EnumElement.HEALING, "transience", EnumSpellType.DEFENCE, 100, EnumAction.bow, false); } @Override @@ -33,50 +23,20 @@ public class Transience extends Spell { } @Override - public boolean cast(World world, EntityPlayer caster, EnumHand hand, int ticksInUse, SpellModifiers modifiers) { - - if(!caster.isPotionActive(WizardryPotions.transience)){ + public boolean cast(World world, EntityPlayer caster, int ticksInUse, float damageMultiplier, float rangeMultiplier, float durationMultiplier, float blastMultiplier) { + + ExtendedPlayer properties = ExtendedPlayer.get(caster); + + if(!caster.isPotionActive(Wizardry.transience)){ if(!world.isRemote){ - caster.addPotionEffect(new PotionEffect(WizardryPotions.transience, (int)(400*modifiers.get(WizardryItems.duration_upgrade)), 0)); - caster.addPotionEffect(new PotionEffect(MobEffects.INVISIBILITY, (int)(400*modifiers.get(WizardryItems.duration_upgrade)), 0, false, false)); - WizardryUtilities.playSoundAtPlayer(caster, WizardrySounds.SPELL_CONJURATION, 1.0f, 1.0f); + caster.addPotionEffect(new PotionEffect(Wizardry.transience.id, (int)(400*durationMultiplier), 0, true)); + caster.addPotionEffect(new PotionEffect(Potion.invisibility.id, (int)(400*durationMultiplier), 0, true)); + world.playSoundAtEntity(caster, "wizardry:aura", 1.0f, 1.0f); } return true; } return false; } - @SubscribeEvent - public static void onLivingAttackEvent(LivingAttackEvent event){ - if(event.getSource() != null){ - // Prevents all blockable damage while transience is active - if(event.getEntityLiving().isPotionActive(WizardryPotions.transience) && !event.getSource().isUnblockable()){ - event.setCanceled(true); - } - // Prevents transient entities from causing any damage - if(event.getSource().getEntity() instanceof EntityLivingBase - && ((EntityLivingBase)event.getSource().getEntity()).isPotionActive(WizardryPotions.transience)){ - event.setCanceled(true); - } - } - } - - @SubscribeEvent - public static void onBlockPlaceEvent(BlockEvent.PlaceEvent event){ - // Prevents transient players from placing blocks - if(event.getPlayer().isPotionActive(WizardryPotions.transience)){ - event.setCanceled(true); - return; - } - } - - @SubscribeEvent - public static void onBlockBreakEvent(BlockEvent.BreakEvent event){ - // Prevents transient players from breaking blocks - if(event.getPlayer().isPotionActive(WizardryPotions.transience)){ - event.setCanceled(true); - return; - } - } } diff --git a/src/main/java/electroblob/wizardry/spell/Transportation.java b/src/main/java/electroblob/wizardry/spell/Transportation.java index 23caeaf1..87d63985 100644 --- a/src/main/java/electroblob/wizardry/spell/Transportation.java +++ b/src/main/java/electroblob/wizardry/spell/Transportation.java @@ -1,25 +1,21 @@ package electroblob.wizardry.spell; -import electroblob.wizardry.WizardData; +import electroblob.wizardry.EnumElement; +import electroblob.wizardry.EnumSpellType; +import electroblob.wizardry.EnumTier; +import electroblob.wizardry.ExtendedPlayer; import electroblob.wizardry.block.BlockTransportationStone; -import electroblob.wizardry.constants.Element; -import electroblob.wizardry.constants.SpellType; -import electroblob.wizardry.constants.Tier; -import electroblob.wizardry.util.SpellModifiers; -import electroblob.wizardry.util.WizardryUtilities; import net.minecraft.entity.player.EntityPlayer; -import net.minecraft.init.MobEffects; -import net.minecraft.init.SoundEvents; import net.minecraft.item.EnumAction; +import net.minecraft.potion.Potion; import net.minecraft.potion.PotionEffect; -import net.minecraft.util.EnumHand; -import net.minecraft.util.text.TextComponentTranslation; +import net.minecraft.util.ChatComponentTranslation; import net.minecraft.world.World; public class Transportation extends Spell { - public Transportation(){ - super(Tier.ADVANCED, 100, Element.SORCERY, "transportation", SpellType.UTILITY, 100, EnumAction.BOW, false); + public Transportation() { + super(EnumTier.ADVANCED, 100, EnumElement.SORCERY, "transportation", EnumSpellType.UTILITY, 100, EnumAction.bow, false); } @Override @@ -28,30 +24,28 @@ public class Transportation extends Spell { } @Override - public boolean cast(World world, EntityPlayer caster, EnumHand hand, int ticksInUse, SpellModifiers modifiers) { + public boolean cast(World world, EntityPlayer caster, int ticksInUse, float damageMultiplier, float rangeMultiplier, float durationMultiplier, float blastMultiplier) { - WizardData properties = WizardData.get(caster); - // Fixes the sound not playing in first person. - if(world.isRemote) WizardryUtilities.playSoundAtPlayer(caster, SoundEvents.BLOCK_PORTAL_TRIGGER, 1.0f, 1.0f); + ExtendedPlayer properties = ExtendedPlayer.get(caster); // Only works when the caster is in the same dimension. - if(properties != null && properties.getTpCountdown() == 0){ - if(caster.dimension == properties.getStoneCircleDimension()){ + if(properties != null && properties.tpTimer == 0){ + if(caster.dimension == properties.transportDimension){ // Has to be y since x and z could reasonably be -1. - if(properties.getStoneCircleLocation() != null){ - if(BlockTransportationStone.testForCircle(world, properties.getStoneCircleLocation())){ - WizardryUtilities.playSoundAtPlayer(caster, SoundEvents.BLOCK_PORTAL_TRIGGER, 1.0f, 1.0f); - caster.addPotionEffect(new PotionEffect(MobEffects.NAUSEA, 150, 0)); - properties.setTpCountdown(75); + if(properties.transportY > -1){ + if(BlockTransportationStone.testForCircle(world, properties.transportX, properties.transportY, properties.transportZ)){ + world.playSoundAtEntity(caster, "portal.trigger", 1.0f, 1.0f); + caster.addPotionEffect(new PotionEffect(Potion.confusion.id, 150, 0)); + properties.tpTimer = 75; return true; }else{ - if(!world.isRemote) caster.addChatComponentMessage(new TextComponentTranslation("spell.transportation.missing")); + if(!world.isRemote) caster.addChatComponentMessage(new ChatComponentTranslation("spell.transportation.missing")); } }else{ - if(!world.isRemote) caster.addChatComponentMessage(new TextComponentTranslation("spell.transportation.undefined")); + if(!world.isRemote) caster.addChatComponentMessage(new ChatComponentTranslation("spell.transportation.undefined")); } }else{ - if(!world.isRemote) caster.addChatComponentMessage(new TextComponentTranslation("spell.transportation.wrongdimension")); + if(!world.isRemote) caster.addChatComponentMessage(new ChatComponentTranslation("spell.transportation.wrongdimension")); } } return false; diff --git a/src/main/java/electroblob/wizardry/spell/VanishingBox.java b/src/main/java/electroblob/wizardry/spell/VanishingBox.java index 85bc13b6..f91153ea 100644 --- a/src/main/java/electroblob/wizardry/spell/VanishingBox.java +++ b/src/main/java/electroblob/wizardry/spell/VanishingBox.java @@ -1,21 +1,17 @@ package electroblob.wizardry.spell; -import electroblob.wizardry.constants.Element; -import electroblob.wizardry.constants.SpellType; -import electroblob.wizardry.constants.Tier; -import electroblob.wizardry.util.SpellModifiers; -import electroblob.wizardry.util.WizardryUtilities; +import electroblob.wizardry.EnumElement; +import electroblob.wizardry.EnumSpellType; +import electroblob.wizardry.EnumTier; import net.minecraft.entity.player.EntityPlayer; -import net.minecraft.init.SoundEvents; import net.minecraft.inventory.InventoryEnderChest; import net.minecraft.item.EnumAction; -import net.minecraft.util.EnumHand; import net.minecraft.world.World; public class VanishingBox extends Spell { public VanishingBox() { - super(Tier.ADVANCED, 45, Element.SORCERY, "vanishing_box", SpellType.UTILITY, 70, EnumAction.BOW, false); + super(EnumTier.ADVANCED, 45, EnumElement.SORCERY, "vanishing_box", EnumSpellType.UTILITY, 70, EnumAction.bow, false); } @Override @@ -24,7 +20,7 @@ public class VanishingBox extends Spell { } @Override - public boolean cast(World world, EntityPlayer caster, EnumHand hand, int ticksInUse, SpellModifiers modifiers) { + public boolean cast(World world, EntityPlayer caster, int ticksInUse, float damageMultiplier, float rangeMultiplier, float durationMultiplier, float blastMultiplier) { if(!world.isRemote){ @@ -35,7 +31,7 @@ public class VanishingBox extends Spell { } } - WizardryUtilities.playSoundAtPlayer(caster, SoundEvents.BLOCK_ENDERCHEST_OPEN, 1, 1); + world.playSoundAtEntity(caster, "wizardry:aura", 1, 1); return true; } diff --git a/src/main/java/electroblob/wizardry/spell/WallOfFrost.java b/src/main/java/electroblob/wizardry/spell/WallOfFrost.java index 285719bf..fba7c809 100644 --- a/src/main/java/electroblob/wizardry/spell/WallOfFrost.java +++ b/src/main/java/electroblob/wizardry/spell/WallOfFrost.java @@ -1,89 +1,100 @@ package electroblob.wizardry.spell; +import electroblob.wizardry.EnumElement; +import electroblob.wizardry.EnumParticleType; +import electroblob.wizardry.EnumSpellType; +import electroblob.wizardry.EnumTier; import electroblob.wizardry.Wizardry; -import electroblob.wizardry.constants.Element; -import electroblob.wizardry.constants.SpellType; -import electroblob.wizardry.constants.Tier; -import electroblob.wizardry.registry.WizardryBlocks; -import electroblob.wizardry.registry.WizardryItems; -import electroblob.wizardry.registry.WizardrySounds; -import electroblob.wizardry.util.SpellModifiers; -import electroblob.wizardry.util.WizardryParticleType; -import electroblob.wizardry.util.WizardryUtilities; +import electroblob.wizardry.WizardryUtilities; import net.minecraft.entity.player.EntityPlayer; import net.minecraft.item.EnumAction; -import net.minecraft.util.EnumFacing; -import net.minecraft.util.EnumHand; -import net.minecraft.util.math.BlockPos; -import net.minecraft.util.math.RayTraceResult; -import net.minecraft.util.math.Vec3d; +import net.minecraft.util.MovingObjectPosition; +import net.minecraft.util.Vec3; import net.minecraft.world.World; public class WallOfFrost extends Spell { public WallOfFrost() { - super(Tier.MASTER, 15, Element.ICE, "wall_of_frost", SpellType.UTILITY, 0, EnumAction.NONE, true); + super(EnumTier.MASTER, 15, EnumElement.ICE, "wall_of_frost", EnumSpellType.UTILITY, 0, EnumAction.none, true); } @Override - public boolean cast(World world, EntityPlayer caster, EnumHand hand, int ticksInUse, SpellModifiers modifiers) { + public boolean cast(World world, EntityPlayer caster, int ticksInUse, float damageMultiplier, float rangeMultiplier, float durationMultiplier, float blastMultiplier) { - // IDEA: Use frosted ice instead of ice statue + Vec3 look = caster.getLookVec(); - Vec3d look = caster.getLookVec(); - - RayTraceResult rayTrace = WizardryUtilities.rayTrace(10*modifiers.get(WizardryItems.range_upgrade), world, caster, true); + MovingObjectPosition rayTrace = WizardryUtilities.rayTrace(10*rangeMultiplier, world, caster, true); if(rayTrace != null && !world.isRemote){ - - BlockPos pos = rayTrace.getBlockPos(); + int x = rayTrace.blockX; + int y = rayTrace.blockY; + int z = rayTrace.blockZ; // Stops the ice being placed floating above snow and grass. Directions other than up included for completeness. - if(WizardryUtilities.canBlockBeReplaced(world, pos)){ - // Moves the blockpos back into the block - pos = pos.offset(rayTrace.sideHit.getOpposite()); + if(WizardryUtilities.canBlockBeReplaced(world, x, y, z)){ + switch(rayTrace.sideHit){ + case 0: y++; break; + case 1: y--; break; + case 2: z++; break; + case 3: z--; break; + case 4: x++; break; + case 5: x--; break; + } } - if(caster.getDistance(pos.getX(), pos.getY(), pos.getZ()) > 2 && world.getBlockState(pos).getBlock() != WizardryBlocks.ice_statue){ + if(caster.getDistance(x, y, z) > 2 && world.getBlock(x, y, z) != Wizardry.iceStatue){ - pos = pos.offset(rayTrace.sideHit); - - if(WizardryUtilities.canBlockBeReplaced(world, pos)){ - world.setBlockState(pos, WizardryBlocks.ice_statue.getDefaultState()); - } - - // Builds a 2 block high wall if it hits the ground - if(rayTrace.sideHit == EnumFacing.UP){ - pos = pos.offset(rayTrace.sideHit); - - if(WizardryUtilities.canBlockBeReplaced(world, pos)){ - world.setBlockState(pos, WizardryBlocks.ice_statue.getDefaultState()); + switch(rayTrace.sideHit){ + case 0: + break; + case 1: + if(WizardryUtilities.canBlockBeReplaced(world, x, y+1, z)){ + world.setBlock(x, y+1, z, Wizardry.iceStatue); } + if(WizardryUtilities.canBlockBeReplaced(world, x, y+2, z)){ + world.setBlock(x, y+2, z, Wizardry.iceStatue); + } + break; + case 2: + if(WizardryUtilities.canBlockBeReplaced(world, x, y, z-1)){ + world.setBlock(x, y, z-1, Wizardry.iceStatue); + } + break; + case 3: + if(WizardryUtilities.canBlockBeReplaced(world, x, y, z+1)){ + world.setBlock(x, y, z+1, Wizardry.iceStatue); + } + break; + case 4: + if(WizardryUtilities.canBlockBeReplaced(world, x-1, y, z)){ + world.setBlock(x-1, y, z, Wizardry.iceStatue); + } + break; + case 5: + if(WizardryUtilities.canBlockBeReplaced(world, x+1, y, z)){ + world.setBlock(x+1, y, z, Wizardry.iceStatue); + } + break; } } } - for(int i=0; i<20; i++){ - if(world.isRemote){ - double x1 = caster.posX + look.xCoord*i/2 + world.rand.nextFloat()/5 - 0.1f; double y1 = WizardryUtilities.getPlayerEyesPos(caster) - 0.4f + look.yCoord*i/2 + world.rand.nextFloat()/5 - 0.1f; double z1 = caster.posZ + look.zCoord*i/2 + world.rand.nextFloat()/5 - 0.1f; - Wizardry.proxy.spawnParticle(WizardryParticleType.SPARKLE, world, x1, y1, z1, look.xCoord*modifiers.get(WizardryItems.range_upgrade), look.yCoord*modifiers.get(WizardryItems.range_upgrade), look.zCoord*modifiers.get(WizardryItems.range_upgrade), 8 + world.rand.nextInt(12), 0.4f, 0.6f, 1.0f); + Wizardry.proxy.spawnParticle(EnumParticleType.SPARKLE, world, x1, y1, z1, look.xCoord*rangeMultiplier, look.yCoord*rangeMultiplier, look.zCoord*rangeMultiplier, 8 + world.rand.nextInt(12), 0.4f, 0.6f, 1.0f); x1 = caster.posX + look.xCoord*i/2 + world.rand.nextFloat()/5 - 0.1f; y1 = WizardryUtilities.getPlayerEyesPos(caster) - 0.4f + look.yCoord*i/2 + world.rand.nextFloat()/5 - 0.1f; z1 = caster.posZ + look.zCoord*i/2 + world.rand.nextFloat()/5 - 0.1f; - Wizardry.proxy.spawnParticle(WizardryParticleType.SPARKLE, world, x1, y1, z1, look.xCoord*modifiers.get(WizardryItems.range_upgrade), look.yCoord*modifiers.get(WizardryItems.range_upgrade), look.zCoord*modifiers.get(WizardryItems.range_upgrade), 8 + world.rand.nextInt(12), 1.0f, 1.0f, 1.0f); + Wizardry.proxy.spawnParticle(EnumParticleType.SPARKLE, world, x1, y1, z1, look.xCoord*rangeMultiplier, look.yCoord*rangeMultiplier, look.zCoord*rangeMultiplier, 8 + world.rand.nextInt(12), 1.0f, 1.0f, 1.0f); } } - if(ticksInUse % 12 == 0){ - if(ticksInUse == 0) WizardryUtilities.playSoundAtPlayer(caster, WizardrySounds.SPELL_ICE, 0.5F, 1.0f); - WizardryUtilities.playSoundAtPlayer(caster, WizardrySounds.SPELL_LOOP_ICE, 0.5F, 1.0f); + if(ticksInUse == 0) world.playSoundAtEntity(caster, "wizardry:ice", 0.5F, 1.0f); + world.playSoundAtEntity(caster, "wizardry:frostray", 0.5F, 1.0f); } - return true; } diff --git a/src/main/java/electroblob/wizardry/spell/WaterBreathing.java b/src/main/java/electroblob/wizardry/spell/WaterBreathing.java index c132a85b..4ce6df42 100644 --- a/src/main/java/electroblob/wizardry/spell/WaterBreathing.java +++ b/src/main/java/electroblob/wizardry/spell/WaterBreathing.java @@ -1,40 +1,36 @@ package electroblob.wizardry.spell; +import electroblob.wizardry.EnumElement; +import electroblob.wizardry.EnumParticleType; +import electroblob.wizardry.EnumSpellType; +import electroblob.wizardry.EnumTier; import electroblob.wizardry.Wizardry; -import electroblob.wizardry.constants.Element; -import electroblob.wizardry.constants.SpellType; -import electroblob.wizardry.constants.Tier; -import electroblob.wizardry.registry.WizardryItems; -import electroblob.wizardry.registry.WizardrySounds; -import electroblob.wizardry.util.SpellModifiers; -import electroblob.wizardry.util.WizardryParticleType; -import electroblob.wizardry.util.WizardryUtilities; +import electroblob.wizardry.WizardryUtilities; import net.minecraft.entity.player.EntityPlayer; -import net.minecraft.init.MobEffects; import net.minecraft.item.EnumAction; +import net.minecraft.potion.Potion; import net.minecraft.potion.PotionEffect; -import net.minecraft.util.EnumHand; import net.minecraft.world.World; public class WaterBreathing extends Spell { public WaterBreathing() { - super(Tier.ADVANCED, 30, Element.EARTH, "water_breathing", SpellType.UTILITY, 250, EnumAction.BOW, false); + super(EnumTier.ADVANCED, 30, EnumElement.EARTH, "water_breathing", EnumSpellType.UTILITY, 250, EnumAction.bow, false); } @Override - public boolean cast(World world, EntityPlayer caster, EnumHand hand, int ticksInUse, SpellModifiers modifiers) { + public boolean cast(World world, EntityPlayer caster, int ticksInUse, float damageMultiplier, float rangeMultiplier, float durationMultiplier, float blastMultiplier) { - caster.addPotionEffect(new PotionEffect(MobEffects.WATER_BREATHING, (int)(1200*modifiers.get(WizardryItems.duration_upgrade)), 0, false, false)); + caster.addPotionEffect(new PotionEffect(Potion.waterBreathing.id, (int)(1200*durationMultiplier), 0, true)); if(world.isRemote){ for(int i=0; i<10; i++){ double x1 = (double)((float)caster.posX + world.rand.nextFloat()*2 - 1.0F); double y1 = (double)((float)WizardryUtilities.getPlayerEyesPos(caster) - 0.5F + world.rand.nextFloat()); double z1 = (double)((float)caster.posZ + world.rand.nextFloat()*2 - 1.0F); - Wizardry.proxy.spawnParticle(WizardryParticleType.SPARKLE, world, x1, y1, z1, 0, 0.1F, 0, 48 + world.rand.nextInt(12), 0.3f, 0.3f, 1.0f); + Wizardry.proxy.spawnParticle(EnumParticleType.SPARKLE, world, x1, y1, z1, 0, 0.1F, 0, 48 + world.rand.nextInt(12), 0.3f, 0.3f, 1.0f); } } - WizardryUtilities.playSoundAtPlayer(caster, WizardrySounds.SPELL_HEAL, 0.7F, world.rand.nextFloat() * 0.4F + 1.0F); + world.playSoundAtEntity(caster, "wizardry:heal", 0.7F, world.rand.nextFloat() * 0.4F + 1.0F); return true; } diff --git a/src/main/java/electroblob/wizardry/spell/Whirlwind.java b/src/main/java/electroblob/wizardry/spell/Whirlwind.java index 9a76561c..59be14b3 100644 --- a/src/main/java/electroblob/wizardry/spell/Whirlwind.java +++ b/src/main/java/electroblob/wizardry/spell/Whirlwind.java @@ -1,33 +1,28 @@ package electroblob.wizardry.spell; -import electroblob.wizardry.constants.Element; -import electroblob.wizardry.constants.SpellType; -import electroblob.wizardry.constants.Tier; -import electroblob.wizardry.registry.WizardryItems; -import electroblob.wizardry.registry.WizardrySounds; -import electroblob.wizardry.util.SpellModifiers; -import electroblob.wizardry.util.WizardryUtilities; +import electroblob.wizardry.EnumElement; +import electroblob.wizardry.EnumSpellType; +import electroblob.wizardry.EnumTier; +import electroblob.wizardry.WizardryUtilities; import net.minecraft.entity.EntityLiving; import net.minecraft.entity.EntityLivingBase; import net.minecraft.entity.player.EntityPlayer; import net.minecraft.entity.player.EntityPlayerMP; import net.minecraft.item.EnumAction; -import net.minecraft.network.play.server.SPacketEntityVelocity; -import net.minecraft.util.EnumHand; -import net.minecraft.util.EnumParticleTypes; -import net.minecraft.util.math.RayTraceResult; +import net.minecraft.network.play.server.S12PacketEntityVelocity; +import net.minecraft.util.MovingObjectPosition; import net.minecraft.world.World; public class Whirlwind extends Spell { public Whirlwind() { - super(Tier.APPRENTICE, 10, Element.EARTH, "whirlwind", SpellType.DEFENCE, 15, EnumAction.NONE, false); + super(EnumTier.APPRENTICE, 10, EnumElement.EARTH, "whirlwind", EnumSpellType.DEFENCE, 15, EnumAction.none, false); } @Override - public boolean cast(World world, EntityPlayer caster, EnumHand hand, int ticksInUse, SpellModifiers modifiers) { + public boolean cast(World world, EntityPlayer caster, int ticksInUse, float damageMultiplier, float rangeMultiplier, float durationMultiplier, float blastMultiplier) { - RayTraceResult rayTrace = WizardryUtilities.standardEntityRayTrace(world, caster, 10*modifiers.get(WizardryItems.range_upgrade)); + MovingObjectPosition rayTrace = WizardryUtilities.standardEntityRayTrace(world, caster, 10*rangeMultiplier); if(rayTrace != null && rayTrace.entityHit instanceof EntityLivingBase){ EntityLivingBase target = (EntityLivingBase) rayTrace.entityHit; @@ -40,7 +35,7 @@ public class Whirlwind extends Spell { // Player motion is handled on that player's client so needs packets if(target instanceof EntityPlayerMP){ - ((EntityPlayerMP)target).connection.sendPacket(new SPacketEntityVelocity(target)); + ((EntityPlayerMP)target).playerNetServerHandler.sendPacket(new S12PacketEntityVelocity(target)); } } @@ -49,19 +44,19 @@ public class Whirlwind extends Spell { double x2 = (double)(caster.posX + world.rand.nextFloat() - 0.5F + caster.getLookVec().xCoord*caster.getDistanceToEntity(target)*0.5); double y2 = (double)(WizardryUtilities.getPlayerEyesPos(caster) + world.rand.nextFloat() - 0.5F + caster.getLookVec().yCoord*caster.getDistanceToEntity(target)*0.5); double z2 = (double)(caster.posZ + world.rand.nextFloat() - 0.5F + caster.getLookVec().zCoord*caster.getDistanceToEntity(target)*0.5); - world.spawnParticle(EnumParticleTypes.CLOUD, x2, y2, z2, caster.getLookVec().xCoord, caster.getLookVec().yCoord, caster.getLookVec().zCoord); + world.spawnParticle("cloud", x2, y2, z2, caster.getLookVec().xCoord, caster.getLookVec().yCoord, caster.getLookVec().zCoord); //Minecraft.getMinecraft().effectRenderer.addEffect(new EntitySparkleFX(world, x2, y2, z2, entityplayer.getLookVec().xCoord, entityplayer.getLookVec().yCoord, entityplayer.getLookVec().zCoord, null, 1.0f, 1.0f, 0.8f, 10)); } } - caster.swingArm(hand); - WizardryUtilities.playSoundAtPlayer(caster, WizardrySounds.SPELL_ICE, 0.8F, world.rand.nextFloat() * 0.2F + 0.6F); + caster.swingItem(); + world.playSoundAtEntity(caster, "wizardry:ice", 0.8F, world.rand.nextFloat() * 0.2F + 0.6F); return true; } return false; } @Override - public boolean cast(World world, EntityLiving caster, EnumHand hand, int ticksInUse, EntityLivingBase target, SpellModifiers modifiers){ + public boolean cast(World world, EntityLiving caster, EntityLivingBase target, float damageMultiplier, float rangeMultiplier, float durationMultiplier, float blastMultiplier){ if(target != null){ @@ -72,7 +67,7 @@ public class Whirlwind extends Spell { // Player motion is handled on that player's client so needs packets if(target instanceof EntityPlayerMP){ - ((EntityPlayerMP)target).connection.sendPacket(new SPacketEntityVelocity(target)); + ((EntityPlayerMP)target).playerNetServerHandler.sendPacket(new S12PacketEntityVelocity(target)); } } if(world.isRemote){ @@ -80,11 +75,11 @@ public class Whirlwind extends Spell { double x2 = (double)(caster.posX + world.rand.nextFloat() - 0.5F + caster.getLookVec().xCoord*caster.getDistanceToEntity(target)*0.5); double y2 = (double)(caster.posY + caster.getEyeHeight() + world.rand.nextFloat() - 0.5F + caster.getLookVec().yCoord*caster.getDistanceToEntity(target)*0.5); double z2 = (double)(caster.posZ + world.rand.nextFloat() - 0.5F + caster.getLookVec().zCoord*caster.getDistanceToEntity(target)*0.5); - world.spawnParticle(EnumParticleTypes.CLOUD, x2, y2, z2, caster.getLookVec().xCoord, caster.getLookVec().yCoord, caster.getLookVec().zCoord); + world.spawnParticle("cloud", x2, y2, z2, caster.getLookVec().xCoord, caster.getLookVec().yCoord, caster.getLookVec().zCoord); } } - caster.swingArm(hand); - caster.playSound(WizardrySounds.SPELL_ICE, 0.8F, world.rand.nextFloat() * 0.2F + 0.6F); + caster.swingItem(); + world.playSoundAtEntity(caster, "wizardry:ice", 0.8F, world.rand.nextFloat() * 0.2F + 0.6F); return true; } diff --git a/src/main/java/electroblob/wizardry/spell/Wither.java b/src/main/java/electroblob/wizardry/spell/Wither.java index 878cf0bb..ba24106f 100644 --- a/src/main/java/electroblob/wizardry/spell/Wither.java +++ b/src/main/java/electroblob/wizardry/spell/Wither.java @@ -1,59 +1,58 @@ package electroblob.wizardry.spell; +import electroblob.wizardry.EnumElement; +import electroblob.wizardry.EnumParticleType; +import electroblob.wizardry.EnumSpellType; +import electroblob.wizardry.EnumTier; +import electroblob.wizardry.MagicDamage; import electroblob.wizardry.Wizardry; -import electroblob.wizardry.constants.Element; -import electroblob.wizardry.constants.SpellType; -import electroblob.wizardry.constants.Tier; -import electroblob.wizardry.registry.WizardryItems; -import electroblob.wizardry.util.MagicDamage; -import electroblob.wizardry.util.MagicDamage.DamageType; -import electroblob.wizardry.util.SpellModifiers; -import electroblob.wizardry.util.WizardryParticleType; -import electroblob.wizardry.util.WizardryUtilities; +import electroblob.wizardry.WizardryUtilities; +import electroblob.wizardry.MagicDamage.DamageType; import net.minecraft.entity.EntityLiving; import net.minecraft.entity.EntityLivingBase; +import net.minecraft.entity.boss.EntityWither; +import net.minecraft.entity.monster.EntitySkeleton; import net.minecraft.entity.player.EntityPlayer; -import net.minecraft.init.MobEffects; -import net.minecraft.init.SoundEvents; import net.minecraft.item.EnumAction; +import net.minecraft.potion.Potion; import net.minecraft.potion.PotionEffect; -import net.minecraft.util.EnumHand; -import net.minecraft.util.math.RayTraceResult; -import net.minecraft.util.math.Vec3d; -import net.minecraft.util.text.TextComponentTranslation; +import net.minecraft.util.ChatComponentTranslation; +import net.minecraft.util.MovingObjectPosition; +import net.minecraft.util.MovingObjectPosition.MovingObjectType; +import net.minecraft.util.Vec3; import net.minecraft.world.World; public class Wither extends Spell { public Wither() { - super(Tier.APPRENTICE, 10, Element.NECROMANCY, "wither", SpellType.ATTACK, 20, EnumAction.NONE, false); + super(EnumTier.APPRENTICE, 10, EnumElement.NECROMANCY, "wither", EnumSpellType.ATTACK, 20, EnumAction.none, false); } @Override - public boolean cast(World world, EntityPlayer caster, EnumHand hand, int ticksInUse, SpellModifiers modifiers) { + public boolean cast(World world, EntityPlayer caster, int ticksInUse, float damageMultiplier, float rangeMultiplier, float durationMultiplier, float blastMultiplier) { - Vec3d look = caster.getLookVec(); + Vec3 look = caster.getLookVec(); - RayTraceResult rayTrace = WizardryUtilities.standardEntityRayTrace(world, caster, 10*modifiers.get(WizardryItems.range_upgrade)); + MovingObjectPosition rayTrace = WizardryUtilities.standardEntityRayTrace(world, caster, 10*rangeMultiplier); - if(rayTrace != null && rayTrace.typeOfHit == RayTraceResult.Type.ENTITY && rayTrace.entityHit instanceof EntityLivingBase){ + if(rayTrace != null && rayTrace.typeOfHit == MovingObjectType.ENTITY && rayTrace.entityHit instanceof EntityLivingBase){ EntityLivingBase target = (EntityLivingBase) rayTrace.entityHit; // Has no effect on withers or wither skeletons. if(MagicDamage.isEntityImmune(DamageType.WITHER, target)){ - if(!world.isRemote) caster.addChatComponentMessage(new TextComponentTranslation("spell.resist", target.getName(), this.getNameForTranslationFormatted())); + if(!world.isRemote) caster.addChatComponentMessage(new ChatComponentTranslation("spell.resist", target.getCommandSenderName(), this.getDisplayNameWithFormatting())); }else{ - target.attackEntityFrom(MagicDamage.causeDirectMagicDamage(caster, DamageType.WITHER), 1.0f * modifiers.get(SpellModifiers.DAMAGE)); - target.addPotionEffect(new PotionEffect(MobEffects.WITHER, (int)(200*modifiers.get(WizardryItems.duration_upgrade)), 1)); + target.attackEntityFrom(MagicDamage.causeDirectMagicDamage(caster, DamageType.WITHER), 1.0f * damageMultiplier); + target.addPotionEffect(new PotionEffect(Potion.wither.id, (int)(200*durationMultiplier), 1)); } } if(world.isRemote){ - for(int i=1; i<(int)(25*modifiers.get(WizardryItems.range_upgrade)); i+=2){ + for(int i=1; i<(int)(25*rangeMultiplier); i+=2){ // I figured it out! when on client side, entityplayer.posY is at the eyes, not the feet! // This is a test for lining up the ray with the wand tip. Not sure if I like it or not. /* - Vec3d origin = Wizardry.proxy.getWandTipPosition(caster); + Vec3 origin = Wizardry.proxy.getWandTipPosition(caster); double x1 = origin.xCoord + look.xCoord*i/2 + world.rand.nextFloat()/5 - 0.1f; double y1 = origin.yCoord + look.yCoord*i/2 + world.rand.nextFloat()/5 - 0.1f; double z1 = origin.zCoord + look.zCoord*i/2 + world.rand.nextFloat()/5 - 0.1f; @@ -62,23 +61,23 @@ public class Wither extends Spell { double y1 = WizardryUtilities.getPlayerEyesPos(caster) - 0.4f + look.yCoord*i/2 + world.rand.nextFloat()/5 - 0.1f; double z1 = caster.posZ + look.zCoord*i/2 + world.rand.nextFloat()/5 - 0.1f; //world.spawnParticle("mobSpell", x1, y1, z1, -1*look.xCoord, -1*look.yCoord, -1*look.zCoord); - Wizardry.proxy.spawnParticle(WizardryParticleType.DARK_MAGIC, world, x1, y1, z1, 0.0d, 0.0d, 0.0d, 0, 0.1f, 0.0f, 0.0f); - Wizardry.proxy.spawnParticle(WizardryParticleType.SPARKLE, world, x1, y1, z1, 0.0d, 0.0d, 0.0d, 12 + world.rand.nextInt(8), 0.1f, 0.0f, 0.05f); + Wizardry.proxy.spawnParticle(EnumParticleType.DARK_MAGIC, world, x1, y1, z1, 0.0d, 0.0d, 0.0d, 0, 0.1f, 0.0f, 0.0f); + Wizardry.proxy.spawnParticle(EnumParticleType.SPARKLE, world, x1, y1, z1, 0.0d, 0.0d, 0.0d, 12 + world.rand.nextInt(8), 0.1f, 0.0f, 0.05f); } } - caster.swingArm(hand); - WizardryUtilities.playSoundAtPlayer(caster, SoundEvents.ENTITY_WITHER_HURT, 1.0F, world.rand.nextFloat() * 0.2F + 1.0F); + caster.swingItem(); + world.playSoundAtEntity(caster, "mob.wither.hurt", 1.0F, world.rand.nextFloat() * 0.2F + 1.0F); return true; } @Override - public boolean cast(World world, EntityLiving caster, EnumHand hand, int ticksInUse, EntityLivingBase target, SpellModifiers modifiers){ + public boolean cast(World world, EntityLiving caster, EntityLivingBase target, float damageMultiplier, float rangeMultiplier, float durationMultiplier, float blastMultiplier){ if(target != null){ // Has no effect on withers or wither skeletons. if(!MagicDamage.isEntityImmune(DamageType.WITHER, target) && !world.isRemote){ - target.attackEntityFrom(MagicDamage.causeDirectMagicDamage(caster, DamageType.WITHER), 1.0f * modifiers.get(SpellModifiers.DAMAGE)); - target.addPotionEffect(new PotionEffect(MobEffects.WITHER, (int)(200*modifiers.get(WizardryItems.duration_upgrade)), 1)); + target.attackEntityFrom(MagicDamage.causeDirectMagicDamage(caster, DamageType.WITHER), 1.0f * damageMultiplier); + target.addPotionEffect(new PotionEffect(Potion.wither.id, (int)(200*durationMultiplier), 1)); } if(world.isRemote){ @@ -87,18 +86,18 @@ public class Wither extends Spell { double dy = (target.posY - caster.posY)/caster.getDistanceToEntity(target); double dz = (target.posZ - caster.posZ)/caster.getDistanceToEntity(target); - for(int i=1; i<(int)(25*modifiers.get(WizardryItems.range_upgrade)); i+=2){ + for(int i=1; i<(int)(25*rangeMultiplier); i+=2){ double x1 = caster.posX + dx*i/2 + world.rand.nextFloat()/5 - 0.1f; double y1 = caster.posY + caster.getEyeHeight() - 0.4f + dy*i/2 + world.rand.nextFloat()/5 - 0.1f; double z1 = caster.posZ + dz*i/2 + world.rand.nextFloat()/5 - 0.1f; - Wizardry.proxy.spawnParticle(WizardryParticleType.DARK_MAGIC, world, x1, y1, z1, 0.0d, 0.0d, 0.0d, 0, 0.1f, 0.0f, 0.0f); - Wizardry.proxy.spawnParticle(WizardryParticleType.SPARKLE, world, x1, y1, z1, 0.0d, 0.0d, 0.0d, 12 + world.rand.nextInt(8), 0.1f, 0.0f, 0.05f); + Wizardry.proxy.spawnParticle(EnumParticleType.DARK_MAGIC, world, x1, y1, z1, 0.0d, 0.0d, 0.0d, 0, 0.1f, 0.0f, 0.0f); + Wizardry.proxy.spawnParticle(EnumParticleType.SPARKLE, world, x1, y1, z1, 0.0d, 0.0d, 0.0d, 12 + world.rand.nextInt(8), 0.1f, 0.0f, 0.05f); } } - caster.swingArm(hand); - caster.playSound(SoundEvents.ENTITY_WITHER_HURT, 1.0F, world.rand.nextFloat() * 0.2F + 1.0F); + caster.swingItem(); + world.playSoundAtEntity(caster, "mob.wither.hurt", 1.0F, world.rand.nextFloat() * 0.2F + 1.0F); return true; } diff --git a/src/main/java/electroblob/wizardry/spell/WitherSkull.java b/src/main/java/electroblob/wizardry/spell/WitherSkull.java index ce0f642e..27e25650 100644 --- a/src/main/java/electroblob/wizardry/spell/WitherSkull.java +++ b/src/main/java/electroblob/wizardry/spell/WitherSkull.java @@ -1,24 +1,21 @@ package electroblob.wizardry.spell; -import electroblob.wizardry.constants.Element; -import electroblob.wizardry.constants.SpellType; -import electroblob.wizardry.constants.Tier; -import electroblob.wizardry.util.SpellModifiers; -import electroblob.wizardry.util.WizardryUtilities; +import electroblob.wizardry.EnumElement; +import electroblob.wizardry.EnumSpellType; +import electroblob.wizardry.EnumTier; import net.minecraft.entity.EntityLiving; import net.minecraft.entity.EntityLivingBase; import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.entity.projectile.EntityLargeFireball; import net.minecraft.entity.projectile.EntityWitherSkull; -import net.minecraft.init.SoundEvents; import net.minecraft.item.EnumAction; -import net.minecraft.util.EnumHand; -import net.minecraft.util.math.Vec3d; +import net.minecraft.util.Vec3; import net.minecraft.world.World; public class WitherSkull extends Spell { public WitherSkull() { - super(Tier.ADVANCED, 20, Element.NECROMANCY, "wither_skull", SpellType.ATTACK, 30, EnumAction.NONE, false); + super(EnumTier.ADVANCED, 20, EnumElement.NECROMANCY, "wither_skull", EnumSpellType.ATTACK, 30, EnumAction.none, false); } @Override @@ -27,9 +24,9 @@ public class WitherSkull extends Spell { } @Override - public boolean cast(World world, EntityPlayer caster, EnumHand hand, int ticksInUse, SpellModifiers modifiers) { + public boolean cast(World world, EntityPlayer caster, int ticksInUse, float damageMultiplier, float rangeMultiplier, float durationMultiplier, float blastMultiplier) { - Vec3d look = caster.getLookVec(); + Vec3 look = caster.getLookVec(); if(!world.isRemote){ EntityWitherSkull witherskull = new EntityWitherSkull(world, caster, 1, 1, 1); @@ -41,14 +38,14 @@ public class WitherSkull extends Spell { witherskull.accelerationY = look.yCoord * 0.1; witherskull.accelerationZ = look.zCoord * 0.1; world.spawnEntityInWorld(witherskull); - WizardryUtilities.playSoundAtPlayer(caster, SoundEvents.ENTITY_WITHER_SHOOT, 1.0F, world.rand.nextFloat() * 0.2F + 1.0F); + world.playSoundAtEntity(caster, "mob.wither.shoot", 1.0F, world.rand.nextFloat() * 0.2F + 1.0F); } - caster.swingArm(hand); + caster.swingItem(); return true; } @Override - public boolean cast(World world, EntityLiving caster, EnumHand hand, int ticksInUse, EntityLivingBase target, SpellModifiers modifiers){ + public boolean cast(World world, EntityLiving caster, EntityLivingBase target, float damageMultiplier, float rangeMultiplier, float durationMultiplier, float blastMultiplier){ if(target != null){ @@ -57,7 +54,7 @@ public class WitherSkull extends Spell { EntityWitherSkull witherskull = new EntityWitherSkull(world, caster, 1, 1, 1); double dx = target.posX - caster.posX; - double dy = target.getEntityBoundingBox().minY + (double)(target.height / 2.0F) - (caster.posY + (double)(caster.height / 2.0F)); + double dy = target.boundingBox.minY + (double)(target.height / 2.0F) - (caster.posY + (double)(caster.height / 2.0F)); double dz = target.posZ - caster.posZ; witherskull.accelerationX = dx/caster.getDistanceToEntity(target) * 0.1; @@ -67,10 +64,10 @@ public class WitherSkull extends Spell { witherskull.setPosition(caster.posX, caster.posY + caster.getEyeHeight(), caster.posZ); world.spawnEntityInWorld(witherskull); - caster.playSound(SoundEvents.ENTITY_WITHER_SHOOT, 1.0F, world.rand.nextFloat() * 0.2F + 1.0F); + world.playSoundAtEntity(caster, "mob.wither.shoot", 1.0F, world.rand.nextFloat() * 0.2F + 1.0F); } - caster.swingArm(hand); + caster.swingItem(); return true; } diff --git a/src/main/java/electroblob/wizardry/tileentity/ContainerArcaneWorkbench.java b/src/main/java/electroblob/wizardry/tileentity/ContainerArcaneWorkbench.java index 6e0f4e55..e93e6e39 100644 --- a/src/main/java/electroblob/wizardry/tileentity/ContainerArcaneWorkbench.java +++ b/src/main/java/electroblob/wizardry/tileentity/ContainerArcaneWorkbench.java @@ -1,461 +1,252 @@ package electroblob.wizardry.tileentity; -import java.util.HashSet; -import java.util.Set; - -import electroblob.wizardry.WizardData; +import electroblob.wizardry.WandHelper; import electroblob.wizardry.Wizardry; -import electroblob.wizardry.constants.Constants; -import electroblob.wizardry.constants.Tier; -import electroblob.wizardry.event.SpellBindEvent; import electroblob.wizardry.item.ItemArcaneTome; import electroblob.wizardry.item.ItemArmourUpgrade; import electroblob.wizardry.item.ItemSpellBook; import electroblob.wizardry.item.ItemWand; import electroblob.wizardry.item.ItemWizardArmour; -import electroblob.wizardry.registry.Spells; -import electroblob.wizardry.registry.WizardryAchievements; -import electroblob.wizardry.registry.WizardryItems; -import electroblob.wizardry.spell.Spell; -import electroblob.wizardry.util.WandHelper; -import electroblob.wizardry.util.WizardryUtilities; +import net.minecraft.entity.item.EntityItem; import net.minecraft.entity.player.EntityPlayer; import net.minecraft.inventory.Container; import net.minecraft.inventory.IInventory; import net.minecraft.inventory.Slot; import net.minecraft.item.Item; import net.minecraft.item.ItemStack; -import net.minecraft.nbt.NBTTagCompound; import net.minecraft.util.ResourceLocation; -import net.minecraftforge.common.MinecraftForge; -public class ContainerArcaneWorkbench extends Container { +public class ContainerArcaneWorkbench extends Container +{ + public TileEntityArcaneWorkbench tileEntityArcaneWorkbench; + + public static final int CRYSTAL_SLOT = 8; + public static final int WAND_SLOT = 9; + public static final int UPGRADE_SLOT = 10; + + private static final int[][][] spellBookSlotCoords = { + {{80, 22}, {121, 51}, {106, 98}, {54, 98}, {39, 51}, {-999, -999}, {-999, -999}, {-999, -999}}, + {{80, 22}, {117, 43}, {117, 85}, {80, 106}, {43, 85}, {43, 43}, {-999, -999}, {-999, -999}}, + {{80, 22}, {113, 38}, {121, 74}, {98, 102}, {62, 102}, {39, 74}, {47, 38}, {-999, -999}}, + {{80, 22}, {111, 33}, {122, 64}, {111, 95}, {80, 106}, {49, 95}, {38, 64}, {49, 33}} + }; + + // Does slot.func_146977_a have something to do with whether the slot is enabled? - /** The arcane workbench tile entity associated with this container. */ - public TileEntityArcaneWorkbench tileentity; + public ContainerArcaneWorkbench(IInventory inventory, TileEntityArcaneWorkbench tileentity){ + + this.tileEntityArcaneWorkbench = tileentity; + + ItemStack wand = tileentity.getStackInSlot(WAND_SLOT); + + for(int i=0; i<8; i++){ + this.addSlotToContainer(new SlotWizardry(tileentity, i, -999, -999, 1, -1, Wizardry.spellBook)); + } + + this.addSlotToContainer(new SlotWizardry(tileentity, CRYSTAL_SLOT, 8, 88, 64, 1, Wizardry.magicCrystal)); + this.addSlotToContainer(new SlotWandArmour(tileentity, WAND_SLOT, 80, 64, this)); + this.addSlotToContainer(new SlotWizardry(tileentity, UPGRADE_SLOT, 8, 106, 1, 2, Wizardry.arcaneTome, + Wizardry.condenserUpgrade, Wizardry.siphonUpgrade, Wizardry.storageUpgrade, Wizardry.rangeUpgrade, + Wizardry.durationUpgrade, Wizardry.cooldownUpgrade, Wizardry.blastUpgrade, Wizardry.attunementUpgrade, + Wizardry.armourUpgrade)); - public static final ResourceLocation EMPTY_SLOT_CRYSTAL = new ResourceLocation(Wizardry.MODID, "gui/empty_slot_crystal"); - public static final ResourceLocation EMPTY_SLOT_UPGRADE = new ResourceLocation(Wizardry.MODID, "gui/empty_slot_upgrade"); - - public static final int CRYSTAL_SLOT = 8; - public static final int WAND_SLOT = 9; - public static final int UPGRADE_SLOT = 10; - - private static final int[][][] SPELL_BOOK_SLOT_COORDS = { - {{80, 22}, {121, 51}, {106, 98}, {54, 98}, {39, 51}, {-999, -999}, {-999, -999}, {-999, -999}}, - {{80, 22}, {117, 43}, {117, 85}, {80, 106}, {43, 85}, {43, 43}, {-999, -999}, {-999, -999}}, - {{80, 22}, {113, 38}, {121, 74}, {98, 102}, {62, 102}, {39, 74}, {47, 38}, {-999, -999}}, - {{80, 22}, {111, 33}, {122, 64}, {111, 95}, {80, 106}, {49, 95}, {38, 64}, {49, 33}} - }; - - public ContainerArcaneWorkbench(IInventory inventory, TileEntityArcaneWorkbench tileentity){ - - this.tileentity = tileentity; - - ItemStack wand = tileentity.getStackInSlot(WAND_SLOT); - - for(int i=0; i<8; i++){ - this.addSlotToContainer(new SlotItemList(tileentity, i, -999, -999, 1, WizardryItems.spell_book)); - } - - this.addSlotToContainer(new SlotItemList(tileentity, CRYSTAL_SLOT, 8, 88, 64, WizardryItems.magic_crystal)) - .setBackgroundName(EMPTY_SLOT_CRYSTAL.toString()); - - this.addSlotToContainer(new SlotWandArmour(tileentity, WAND_SLOT, 80, 64, this)); - - Set upgrades = new HashSet(WandHelper.getSpecialUpgrades()); // Can't be done statically. - upgrades.add(WizardryItems.arcane_tome); - upgrades.add(WizardryItems.armour_upgrade); - - this.addSlotToContainer(new SlotItemList(tileentity, UPGRADE_SLOT, 8, 106, 1, upgrades.toArray(new Item[0]))) - .setBackgroundName(EMPTY_SLOT_UPGRADE.toString()); - - for(int x = 0; x < 9; x++){ + for(int x = 0; x < 9; x++){ this.addSlotToContainer(new Slot(inventory, x, 8 + x * 18, 196)); } - + for(int y = 0; y < 3; y++){ for(int x = 0; x < 9; x++){ this.addSlotToContainer(new Slot(inventory, 9 + x + y * 9, 8 + x * 18, 138 + y * 18)); } } - + this.onSlotChanged(WAND_SLOT, wand, null); - } + } + + @Override + public boolean canInteractWith(EntityPlayer par1EntityPlayer) + { + return this.tileEntityArcaneWorkbench.isUseableByPlayer(par1EntityPlayer); + } + + public void onSlotChanged(int slotNumber, ItemStack stack, EntityPlayer player) { - @Override - public boolean canInteractWith(EntityPlayer player){ - return this.tileentity.isUseableByPlayer(player); - } + if(slotNumber == WAND_SLOT){ + + if(stack == null || (!(stack.getItem() instanceof ItemWand) && stack.getItem() != Wizardry.blankScroll)){ + // If the stack has been removed + for(int i=0; i

    - * Most external interaction with SpellModifiers objects will be in {@link SpellCastEvent.Pre}, where you can add additional - * modifiers to them if desired for use with your own spells, or modify the existing ones. If you have added a wand - * upgrade, this is not done automatically for you; you will have to do it yourself (for the simple reason that - * not all wand upgrades affect spells). SpellModifiers objects are mutable, so you can simply change the values - * they contain to modify the spell. - *

    - * To use a SpellModifiers object within the Spell.cast methods, simply retrieve the desired multiplier - * using {@link SpellModifiers#get(Item)} for wand upgrades, or {@link SpellModifiers#get(String)} if the multiplier - * is not from a wand upgrade. - * @author Electroblob - * @since Wizardry 1.2 - * @see WandHelper - */ -// I have made the decision that the USERS of this class must decide whether the modifiers need syncing or not, on a -// case-by-case basis. Why? Because assigning keys to either sync or not sync would be unnecessarily restrictive, and -// would mean they have to be registered, and part of the point of SpellModifiers is that they can be added to on the -// fly. -public final class SpellModifiers { - - /** Constant string identifier for the damage modifier. All the other modifiers in Wizardry have items. */ - public static final String DAMAGE = "damage"; - - private Map multiplierMap; - private Map syncedMultiplierMap; - - /** Creates an empty SpellModifiers object. All calls to get(...) on an empty SpellModifiers object - * will return a value of 1. */ - public SpellModifiers(){ - multiplierMap = new HashMap(); - syncedMultiplierMap = new HashMap(); - } - - /** - * Adds the given multiplier to this SpellModifiers object, using the string identifier that the given wand - * upgrade item was registered with. - * @throws IllegalArgumentException if the given item is not a registered special wand upgrade. - * @param upgrade The upgrade item the multiplier corresponds to. - * @param multiplier The multiplier value, with 1 being default. Usage of modifiers is up to individual spells to - * implement. - * @param needsSyncing Whether this multiplier should be synchronised with the client via packets. Only set this - * to true if particles will be spawned which need to know the value of the multiplier. - * @return The SpellModifiers object, allowing this method to be chained onto the constructor. - */ - public SpellModifiers set(Item upgrade, float multiplier, boolean needsSyncing){ - this.set(WandHelper.getIdentifier(upgrade), multiplier, needsSyncing); - return this; - } - - /** - * Adds the given multiplier to this SpellModifiers object, using the given string key. In most cases, the - * multiplier will correspond to a wand upgrade, in which case use {@link SpellModifiers#set(Item, float, boolean)} - * instead. - * @param key The key used to identify the multiplier. - * @param multiplier The multiplier value, with 1 being default. Usage of modifiers is up to individual spells to - * implement. - * @param needsSyncing Whether this multiplier should be synchronised with the client via packets. Only set this - * to true if particles will be spawned which depend on the multiplier. - * @return The SpellModifiers object, allowing this method to be chained onto the constructor. - */ - public SpellModifiers set(String key, float multiplier, boolean needsSyncing){ - multiplierMap.put(key, multiplier); - if(needsSyncing) syncedMultiplierMap.put(key, multiplier); - return this; - } - - /** Returns the multiplier corresponding to the given wand upgrade item, or 1 if no multiplier was stored. - * @throws IllegalArgumentException if the given item is not a registered special wand upgrade. */ - public float get(Item upgrade){ - return get(WandHelper.getIdentifier(upgrade)); - } - - /** Returns the multiplier corresponding to the given string key, or 1 if no multiplier was stored. In most cases, - * the multiplier will correspond to a wand upgrade, in which case use {@link SpellModifiers#get(Item)} - * instead. */ - public float get(String key){ - Float value = multiplierMap.get(key); - // Must check for null before unboxing, and if it is null, return the default 1. - return value == null ? 1 : value; - } - - /** Returns an unmodifiable map of the modifiers stored in this SpellModifiers object. Useful for iterating through - * the modifiers. */ - public Map getModifiers(){ - return Collections.unmodifiableMap(this.multiplierMap); - } - - /** Removes all modifiers from this SpellModifiers object, effectively resetting them all to 1. */ - public void reset(){ - this.multiplierMap.clear(); - this.syncedMultiplierMap.clear(); - } - - /** Reads this SpellModifiers object from the given ByteBuf. */ - public void read(ByteBuf buf){ - int entryCount = buf.readInt(); - for(int i=0; i entry : syncedMultiplierMap.entrySet()){ - ByteBufUtils.writeUTF8String(buf, entry.getKey()); - buf.writeFloat(entry.getValue()); - } - } - - /** Creates a new SpellModifiers object from the given NBTTagCompound. The NBTTagCompound should have 1 or more - * float tags, which will be stored as modifiers under the same name as the tag. For example, the following NBT - * tag (in command syntax) will create a SpellModifiers object with a damage modifier of 1.5 and a range modifier of - * 2:

    {damage:1.5, range:2}

    - * Note that needsSyncing is set to true for all returned modifiers. */ - public static SpellModifiers fromNBT(NBTTagCompound nbt){ - SpellModifiers modifiers = new SpellModifiers(); - for(String key : nbt.getKeySet()){ - modifiers.set(key, nbt.getFloat(key), true); - } - return modifiers; - } - -} diff --git a/src/main/java/electroblob/wizardry/util/WizardryParticleType.java b/src/main/java/electroblob/wizardry/util/WizardryParticleType.java deleted file mode 100644 index 53093f2c..00000000 --- a/src/main/java/electroblob/wizardry/util/WizardryParticleType.java +++ /dev/null @@ -1,19 +0,0 @@ -package electroblob.wizardry.util; - -/** Enum constants representing the different types of particle added by wizardry. This was renamed from the previous - * EnumParticleType in the 1.10.2 port to avoid confusion with the vanilla version, EnumParticleTypes. */ -public enum WizardryParticleType { - BLIZZARD, - BRIGHT_DUST, - DARK_MAGIC, - DUST, - ICE, - LEAF, - MAGIC_BUBBLE, - MAGIC_FIRE, - PATH, - SNOW, - SPARK, - SPARKLE, - SPARKLE_ROTATING -} diff --git a/src/main/java/electroblob/wizardry/util/WizardryPathFinder.java b/src/main/java/electroblob/wizardry/util/WizardryPathFinder.java deleted file mode 100644 index 2a431b38..00000000 --- a/src/main/java/electroblob/wizardry/util/WizardryPathFinder.java +++ /dev/null @@ -1,138 +0,0 @@ -package electroblob.wizardry.util; - -import java.util.Set; - -import javax.annotation.Nullable; - -import com.google.common.collect.Sets; - -import electroblob.wizardry.spell.Clairvoyance; -import net.minecraft.entity.EntityLiving; -import net.minecraft.pathfinding.NodeProcessor; -import net.minecraft.pathfinding.Path; -import net.minecraft.pathfinding.PathHeap; -import net.minecraft.pathfinding.PathPoint; -import net.minecraft.util.math.BlockPos; -import net.minecraft.world.IBlockAccess; - -/** Minecraft's pathfinder refused to play nicely, so I 'borrowed' its code and fiddled with it. Currently this is only - * used for the {@link Clairvoyance} spell. */ -public class WizardryPathFinder { - - /** The path being generated */ - private final PathHeap path = new PathHeap(); - private final Set closedSet = Sets.newHashSet(); - /** Selection of path points to add to the path */ - private final PathPoint[] pathOptions = new PathPoint[32]; - private final NodeProcessor nodeProcessor; - - public WizardryPathFinder(NodeProcessor processor){ - this.nodeProcessor = processor; - } - - @Nullable - public Path findPath(IBlockAccess world, EntityLiving entity, BlockPos destination, float range){ - return this.findPath(world, entity, (double)((float)destination.getX() + 0.5F), (double)((float)destination.getY() + 0.5F), (double)((float)destination.getZ() + 0.5F), range); - } - - @Nullable - private Path findPath(IBlockAccess world, EntityLiving entity, double x, double y, double z, float range){ - this.path.clearPath(); - this.nodeProcessor.initProcessor(world, entity); - PathPoint pathpoint = this.nodeProcessor.getStart(); - PathPoint pathpoint1 = this.nodeProcessor.getPathPointToCoords(x, y, z); - Path path = this.findPath(pathpoint, pathpoint1, range); - this.nodeProcessor.postProcess(); - return path; - } - - @Nullable - private Path findPath(PathPoint start, PathPoint end, float range){ - - start.totalPathDistance = 0.0F; - start.distanceToNext = start.distanceManhattan(end); - start.distanceToTarget = start.distanceToNext; - this.path.clearPath(); - this.closedSet.clear(); - this.path.addPoint(start); - PathPoint pathpoint = start; - int i = 0; - - while(!this.path.isPathEmpty()){ - - ++i; - - // This is the offending line - timeout limit changed from 200 to 5000. - if(i >= 5000){ - break; - } - - PathPoint pathpoint1 = this.path.dequeue(); - - if(pathpoint1.equals(end)){ - pathpoint = end; - break; - } - - if(pathpoint1.distanceManhattan(end) < pathpoint.distanceManhattan(end)){ - pathpoint = pathpoint1; - } - - pathpoint1.visited = true; - int j = this.nodeProcessor.findPathOptions(this.pathOptions, pathpoint1, end, range); - - for(int k = 0; k < j; ++k){ - - PathPoint pathpoint2 = this.pathOptions[k]; - float f = pathpoint1.distanceManhattan(pathpoint2); - pathpoint2.distanceFromOrigin = pathpoint1.distanceFromOrigin + f; - pathpoint2.cost = f + pathpoint2.costMalus; - float f1 = pathpoint1.totalPathDistance + pathpoint2.cost; - - if(pathpoint2.distanceFromOrigin < range && (!pathpoint2.isAssigned() || f1 < pathpoint2.totalPathDistance)){ - - pathpoint2.previous = pathpoint1; - pathpoint2.totalPathDistance = f1; - pathpoint2.distanceToNext = pathpoint2.distanceManhattan(end) + pathpoint2.costMalus; - - if(pathpoint2.isAssigned()){ - this.path.changeDistance(pathpoint2, pathpoint2.totalPathDistance + pathpoint2.distanceToNext); - }else{ - pathpoint2.distanceToTarget = pathpoint2.totalPathDistance + pathpoint2.distanceToNext; - this.path.addPoint(pathpoint2); - } - } - } - } - - if(pathpoint == start){ - return null; - }else{ - Path path = this.createEntityPath(start, pathpoint); - return path; - } - } - - /** - * Returns a new PathEntity for a given start and end point - */ - private Path createEntityPath(PathPoint start, PathPoint end){ - - int i = 1; - - for(PathPoint pathpoint = end; pathpoint.previous != null; pathpoint = pathpoint.previous){ - ++i; - } - - PathPoint[] points = new PathPoint[i]; - PathPoint pathpoint1 = end; - --i; - - for(points[i] = end; pathpoint1.previous != null; points[i] = pathpoint1){ - pathpoint1 = pathpoint1.previous; - --i; - } - - return new Path(points); - } -} \ No newline at end of file diff --git a/src/main/java/electroblob/wizardry/util/WizardryUtilities.java b/src/main/java/electroblob/wizardry/util/WizardryUtilities.java deleted file mode 100644 index 1d84a83d..00000000 --- a/src/main/java/electroblob/wizardry/util/WizardryUtilities.java +++ /dev/null @@ -1,976 +0,0 @@ -package electroblob.wizardry.util; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Map.Entry; -import java.util.Random; -import java.util.UUID; -import java.util.function.Function; - -import javax.annotation.Nullable; - -import org.apache.commons.lang3.tuple.ImmutablePair; - -import electroblob.wizardry.CommonProxy; -import electroblob.wizardry.WizardData; -import electroblob.wizardry.Wizardry; -import electroblob.wizardry.constants.Element; -import electroblob.wizardry.constants.Tier; -import electroblob.wizardry.entity.living.ISummonedCreature; -import electroblob.wizardry.registry.WizardryItems; -import electroblob.wizardry.registry.WizardryPotions; -import electroblob.wizardry.spell.MindControl; -import electroblob.wizardry.spell.Spell; -import net.minecraft.block.state.IBlockState; -import net.minecraft.entity.Entity; -import net.minecraft.entity.EntityLiving; -import net.minecraft.entity.EntityLivingBase; -import net.minecraft.entity.monster.EntityCreeper; -import net.minecraft.entity.player.EntityPlayer; -import net.minecraft.inventory.EntityEquipmentSlot; -import net.minecraft.inventory.EntityEquipmentSlot.Type; -import net.minecraft.item.Item; -import net.minecraft.item.ItemStack; -import net.minecraft.nbt.NBTBase; -import net.minecraft.nbt.NBTTagCompound; -import net.minecraft.nbt.NBTTagList; -import net.minecraft.network.datasync.DataParameter; -import net.minecraft.server.MinecraftServer; -import net.minecraft.util.DamageSource; -import net.minecraft.util.EnumFacing; -import net.minecraft.util.SoundCategory; -import net.minecraft.util.SoundEvent; -import net.minecraft.util.math.AxisAlignedBB; -import net.minecraft.util.math.BlockPos; -import net.minecraft.util.math.MathHelper; -import net.minecraft.util.math.RayTraceResult; -import net.minecraft.util.math.Vec3d; -import net.minecraft.world.World; -import net.minecraftforge.fml.relauncher.ReflectionHelper; -import net.minecraftforge.fml.relauncher.Side; -import net.minecraftforge.fml.relauncher.SideOnly; - -/** This class contains some useful static methods for use anywhere - items, entities, spells, events, blocks, etc. - * Broadly speaking, these fall into the following categories: - *

    - * - In-world utilities (position calculating, retrieving entities, etc.)
    - * - Raytracing
    - * - Drawing utilities (client-only)
    - * - NBT and data storage utilities
    - * - Interaction with the ally designation system
    - * - Loot and weighting utilities - * @see CommonProxy - * @see WandHelper - * @since Wizardry 1.0 - * @author Electroblob */ -public final class WizardryUtilities { - - /** Constant which is simply an array of the four armour slots. (Could've sworn this exists somewhere in - * vanilla, but I can't find it anywhere...) */ - public static final EntityEquipmentSlot[] ARMOUR_SLOTS; - /** Changed to a constant in wizardry 2.1, since this is a lot more efficient. */ - private static final DataParameter POWERED; - - static { - // The list of slots needs to be mutable. - List slots = new ArrayList(Arrays.asList(EntityEquipmentSlot.values())); - slots.removeIf(slot -> slot.getSlotType() != Type.ARMOR); - ARMOUR_SLOTS = slots.toArray(new EntityEquipmentSlot[0]); - - // Null is passed in deliberately since POWERED is a static field. - POWERED = ReflectionHelper.getPrivateValue(EntityCreeper.class, null, "POWERED", "field_184714_b"); - } - - // SECTION Block/Entity/World Utilities - // =============================================================================================================== - - /** - * Returns whether the block at the given coordinates can be replaced by another one (works as if a block is being placed by a player). - * True for air, liquids, vines, tall grass and snow layers but not for flowers, signs etc. - * This is a shortcut for world.getBlockState(pos).getMaterial().isReplaceable(). - * @see WizardryUtilities#canBlockBeReplacedB(World, BlockPos) - */ - public static boolean canBlockBeReplaced(World world, BlockPos pos){ - return world.isAirBlock(new BlockPos(pos)) || world.getBlockState(pos).getMaterial().isReplaceable(); - } - - /** - * Returns whether the block at the given coordinates can be replaced by another one (works as if a block is being placed by a player) - * and is not a liquid. - * True for air, vines, tall grass and snow layers but not for flowers, signs etc. or any liquids. - * @see WizardryUtilities#canBlockBeReplaced(World, BlockPos) - */ - public static boolean canBlockBeReplacedB(World world, BlockPos pos){ - return canBlockBeReplaced(world, pos) && !world.getBlockState(pos).getMaterial().isLiquid(); - } - - /** - * Returns whether the block at the given coordinates is unbreakable in survival mode. In vanilla this is true for - * bedrock and end portal frame, for example. - * This is a shortcut for world.getBlockState(pos).getBlockHardness(world, pos) == -1.0f. - * Not much of a shortcut any more, since block ids have been phased out. - */ - public static boolean isBlockUnbreakable(World world, BlockPos pos){ - return world.isAirBlock(new BlockPos(pos)) ? false : world.getBlockState(pos).getBlockHardness(world, pos) == -1.0f; - } - - /** - * Finds the nearest floor level to the given y coord within the range specified at the given x and z coords. - * Liquids and other blocks that cannot be built on top of do not count, but stuff like signs does. - * (Technically any block is allowed to be the floor according to the code, but seeing as it searches upwards and - * non-solid blocks usually need a supporting block, the floor is likely to always be solid). - * @param world - * @param x The x coordinate to search in - * @param y The y coordinate to search from - * @param z The z coordinate to search in - * @param range The maximum distance from the given y coordinate to search. - * @return The y coordinate of the closest floor level, or -1 if there is none. Returns the actual level of the floor - * as would be seen in the debug screen when the player is standing on it. - * @see WizardryUtilities#getNearestFloorLevelB(World, BlockPos, int) - */ - public static int getNearestFloorLevel(World world, BlockPos pos, int range){ - - int yCoord = -2; - for(int i = -range; i <= range; i++){ - // The last bit determines whether the block found to be a suitable floor is closer than the previous one found. - if(world.isSideSolid(pos.up(i), EnumFacing.UP) && (world.isAirBlock(pos.up(i+1)) || !world.isSideSolid(pos.up(i+1), EnumFacing.UP)) - && (i < yCoord-pos.getY() || yCoord == -2)){ - yCoord = pos.getY() + i; - } - } - return yCoord + 1; - } - - /** - * Finds the nearest floor level to the given y coord within the range specified at the given x and z coords. - * Only works if the block above the floor is actually air and the floor is solid or a liquid. - * @param world - * @param x The x coordinate to search in - * @param y The y coordinate to search from - * @param z The z coordinate to search in - * @param range The maximum distance from the given y coordinate to search. - * @return The y coordinate of the closest floor level, or -1 if there is none. Returns the actual level of the floor - * as would be seen in the debug screen when the player is standing on it. - * @see WizardryUtilities#getNearestFloorLevel(World, BlockPos, int) - */ - public static int getNearestFloorLevelB(World world, BlockPos pos, int range){ - int yCoord = -2; - for(int i = -range; i <= range; i++){ - if(world.isAirBlock(new BlockPos(pos.up(i+1))) && (world.getBlockState(pos.up(i)).getMaterial().isLiquid() || world.isSideSolid(pos.up(i), EnumFacing.UP)) - && (i < yCoord-pos.getY() || yCoord == -2)){ - // The last bit determines whether the block found to be a suitable floor is closer than the previous one found. - yCoord = pos.getY() + i; - } - } - return yCoord + 1; - } - - /** - * Finds the nearest floor level to the given y coord within the range specified at the given x and z coords. - * Everything that is not air is treated as floor, even stuff that can't be walked on. - * @param world - * @param x The x coordinate to search in - * @param y The y coordinate to search from - * @param z The z coordinate to search in - * @param range The maximum distance from the given y coordinate to search. - * @return The y coordinate of the closest floor level, or -1 if there is none. Returns the actual level of the floor - * as would be seen in the debug screen when the player is standing on it. - * @see WizardryUtilities#getNearestFloorLevel(World, BlockPos, int) - */ - public static int getNearestFloorLevelC(World world, BlockPos pos, int range){ - int yCoord = -2; - for(int i = -range; i <= range; i++){ - if(world.isAirBlock(new BlockPos(pos.up(i+1))) && (i < yCoord-pos.getY() || yCoord == -2)){ - // The last bit determines whether the block found to be a suitable floor is closer than the previous one found. - yCoord = pos.getY() + i; - } - } - return yCoord + 1; - } - - /** - * Gets a random position on the ground near the player within the specified horizontal and vertical ranges. Used - * to find a position to spawn entities in summoning spells. - * @param entity The entity around which to search - * @param horizontalRange The maximum number of blocks on the x or z axis the returned position can be from the - * given entity. The number of operations performed by this method is proportional to the square of this - * parameter, so for performance reasons it is recommended that it does not exceed around 10. - * @param verticalRange The maximum number of blocks on the y axis the returned position can be from the given - * entity - * @return A BlockPos with the coordinates of the block directly above the ground at the position found, or null - * if none were found within range. Importantly, since this method checks all possible positions within - * range (i.e. randomness only occurs when deciding between the possible positions), if it returns null once - * then it will always return null given the same circumstances and parameters. What this means is that you can - * (and should) immediately stop trying to cast a summoning spell if this returns null. - */ - @Nullable - public static BlockPos findNearbyFloorSpace(Entity entity, int horizontalRange, int verticalRange){ - - World world = entity.worldObj; - List possibleLocations = new ArrayList(); - BlockPos origin = new BlockPos(entity); - - for(int x = -horizontalRange; x <= horizontalRange; x++){ - for(int z = -horizontalRange; z <= horizontalRange; z++){ - int y = WizardryUtilities.getNearestFloorLevel(world, origin.add(x, 0, z), verticalRange); - if(y > -1) possibleLocations.add(new BlockPos(origin.getX() + x, y, origin.getZ() + z)); - } - } - - if(possibleLocations.isEmpty()){ - return null; - }else{ - return possibleLocations.get(world.rand.nextInt(possibleLocations.size())); - } - - } - - /** - * Gets the blockstate of the block the specified entity is standing on. Uses {@link MathHelper#floor_double(double)} because casting to int will not - * return the correct coordinate when x or z is negative. - */ - public static IBlockState getBlockEntityIsStandingOn(Entity entity){ - BlockPos pos = new BlockPos(MathHelper.floor_double(entity.posX), (int)entity.getEntityBoundingBox().minY-1, MathHelper.floor_double(entity.posZ)); - return entity.worldObj.getBlockState(pos); - } - - /** - * Shorthand for {@link WizardryUtilities#getEntitiesWithinRadius(double, double, double, double, World, Class)} - * with EntityLivingBase as the entity type. This is by far the most common use for that method, which is why this - * shorthand exists. - * @param radius The search radius - * @param x The x coordinate to search around - * @param y The y coordinate to search around - * @param z The z coordinate to search around - * @param world The world to search in - */ - public static List getEntitiesWithinRadius(double radius, double x, double y, double z, World world){ - return getEntitiesWithinRadius(radius, x, y, z, world, EntityLivingBase.class); - } - - /** - * Returns all entities of the specified type within the specified radius of the given coordinates. This is different to using - * a raw AABB because a raw AABB will search in a cube volume rather than a sphere. - * Note that this does not exclude any entities; if any specific entities are to be excluded this must be - * checked when iterating through the list. - * @see {@link WizardryUtilities#getEntitiesWithinRadius(double, double, double, double, World)} - * @param radius The search radius - * @param x The x coordinate to search around - * @param y The y coordinate to search around - * @param z The z coordinate to search around - * @param world The world to search in - * @param entityType The class of entity to search for; pass in Entity.class for all entities - */ - public static List getEntitiesWithinRadius(double radius, double x, double y, double z, World world, Class entityType){ - AxisAlignedBB aabb = new AxisAlignedBB(x - radius, y - radius, z - radius, x + radius, y + radius, z + radius); - List entityList = world.getEntitiesWithinAABB(entityType, aabb); - for(int i=0;i radius){ - entityList.remove(i); - } - } - return entityList; - } - - /** - * Gets an entity from its UUID. Note that you should check this isn't null. If the UUID is known to belong - * to an EntityPlayer, use the more efficient {@link World#getPlayerEntityByUUID(UUID)} instead. - * @param world The world the entity is in - * @param id The entity's UUID - * @return The Entity that has the given UUID, or null if no such entity exists in the specified world. - */ - @Nullable - public static Entity getEntityByUUID(World world, UUID id){ - - for(Entity entity : world.loadedEntityList){ - // This is a perfect example of where you need to use .equals() and not ==. For most applications, - // this was unnoticeable until world reload because the UUID instance or entity instance is stored. - // Fixed now though. - if(entity.getUniqueID().equals(id)){ - return entity; - } - } - return null; - } - - // No point allowing anything other than players for these methods since other entities can use Entity#playSound. - - /** Shortcut for {@link World#playSound(EntityPlayer, double, double, double, SoundEvent, SoundCategory, float, float)} - * where the player is null but the x, y and z coordinates are those of the passed in player. Use in preference to - * {@link EntityPlayer#playSound(SoundEvent, float, float)} if there are client-server discrepancies. */ - public static void playSoundAtPlayer(EntityPlayer player, SoundEvent sound, SoundCategory category, float volume, float pitch){ - player.worldObj.playSound(null, player.posX, player.posY, player.posZ, sound, category, volume, pitch); - } - - /** See {@link WizardryUtilities#playSoundAtPlayer(EntityPlayer, SoundEvent, SoundCategory, float, float)}. Category - * defaults to {@link SoundCategory#PLAYERS}. */ - public static void playSoundAtPlayer(EntityPlayer player, SoundEvent sound, float volume, float pitch){ - player.worldObj.playSound(null, player.posX, player.posY, player.posZ, sound, SoundCategory.PLAYERS, volume, pitch); - } - - /** - * Returns the entity riding the given entity, or null if there is none. Allows for neater code now that - * entities have a list of passengers, because it is necessary to check that the list is not null or empty first. - */ - @Nullable - public static Entity getRider(Entity entity){ - return entity.getPassengers() != null && !entity.getPassengers().isEmpty() ? entity.getPassengers().get(0) : null; - } - - /** - * Attacks the given entity with the given damage source and amount, but preserving the entity's original velocity - * instead of applying knockback, as would happen with {@link EntityLivingBase#attackEntityFrom(DamageSource, float)} - * (More accurately, calls that method as normal and then resets the entity's velocity to what it was before). - * Handy for when you need to damage an entity repeatedly in a short space of time. - * @param entity The entity to attack - * @param source The source of the damage - * @param amount The amount of damage to apply - * @return True if the attack succeeded, false if not. - */ - public static boolean attackEntityWithoutKnockback(Entity entity, DamageSource source, float amount){ - double vx = entity.motionX; double vy = entity.motionY; double vz = entity.motionZ; - boolean succeeded = entity.attackEntityFrom(source, amount); - entity.motionX = vx; entity.motionY = vy; entity.motionZ = vz; - return succeeded; - } - - /** - * Applies the standard (non-enchanted) amount of knockback to the given target, using the same calculation as - * {@link EntityLivingBase#attackEntityFrom(DamageSource, float)}. Use in conjunction with - * {@link WizardryUtilities#attackEntityWithoutKnockback(Entity, DamageSource, float)} to change the source of - * knockback for an attack. - * @param attacker The entity that caused the knockback; the target will be pushed away from this entity. - * @param target The entity to be knocked back. - */ - public static void applyStandardKnockback(Entity attacker, EntityLivingBase target){ - double dx = attacker.posX - target.posX; - double dz; - for(dz = attacker.posZ - target.posZ; dx * dx + dz * dz < 1.0E-4D; dz = (Math.random() - Math.random()) * 0.01D){ - dx = (Math.random() - Math.random()) * 0.01D; - } - // The first argument is never used. - target.knockBack(null, 0.4f, dx, dz); - } - - // Just what benefit does having posY be the eye position on the first person client actually give? - - /** - * Gets the y coordinate of the given player's eyes. This is to cover an inconsistency between the value of - * EntityPlayer.posY on the first person client and everywhere else; in first person (i.e. when - * Minecraft.getMinecraft().thePlayer == player) player.posY is the eye position, but everywhere else it is the - * feet position. This is intended for use when spawning particles, since this is the only situation where the - * discrepancy is likely to matter. - *

    - * As of Wizardry 1.2, this is just a shorthand for:

    - *

    player.getEntityBoundingBox().minY + player.getEyeHeight()
    - */ - public static double getPlayerEyesPos(EntityPlayer player){ - return player.getEntityBoundingBox().minY + player.getEyeHeight(); - } - - /** Returns a list of the itemstacks in the given player's hotbar. Defined here for convenience and to - * centralise the (unfortunately unavoidable) use of hardcoded numbers to reference the inventory slots. The - * returned list is a modifiable copy of part of the player's inventory stack list; as such, changes to the list - * are not written through to the player's inventory. However, the ItemStack instances themselves are not - * copied, so changes to any of their fields (size, metadata...) will change those in the player's inventory. - * @since Wizardry 1.2 */ - public static List getHotbar(EntityPlayer player){ - return new ArrayList(Arrays.asList(player.inventory.mainInventory).subList(0, 9)); - } - - /** Returns a list of the itemstacks in the given player's hotbar and offhand, sorted into the following order: - * main hand, offhand, rest of hotbar left-to-right. The returned list is a modifiable copy of part of the player's - * inventory stack list; as such, changes to the list are not written through to the player's inventory. - * However, the ItemStack instances themselves are not copied, so changes to any of their fields (size, metadata...) - * will change those in the player's inventory. - * @since Wizardry 1.2 */ - public static List getPrioritisedHotbarAndOffhand(EntityPlayer player){ - List hotbar = WizardryUtilities.getHotbar(player); - // Adds the offhand item to the beginning of the list so it is processed before the hotbar - hotbar.add(0, player.getHeldItemOffhand()); - // Moves the item in the main hand to the beginning of the list so it is processed first - hotbar.remove(player.getHeldItemMainhand()); - hotbar.add(0, player.getHeldItemMainhand()); - return hotbar; - } - - /** Tests whether the specified player has any of the specified item in their entire inventory, including armour - * slots and offhand. */ - public static boolean doesPlayerHaveItem(EntityPlayer player, Item item){ - - for(ItemStack stack : player.inventory.mainInventory){ - if(stack != null && stack.getItem() == item){ - return true; - } - } - - for(ItemStack stack : player.inventory.armorInventory){ - if(stack != null && stack.getItem() == item){ - return true; - } - } - - for(ItemStack stack : player.inventory.offHandInventory){ - if(stack != null && stack.getItem() == item){ - return true; - } - } - - return false; - } - - /** Checks if the given player is opped on the given server. If the server is a singleplayer or LAN server, this - * means they have cheats enabled. */ - public static boolean isPlayerOp(EntityPlayer player, MinecraftServer server){ - return server.getPlayerList().getOppedPlayers().getEntry(player.getGameProfile()) != null; - } - - /** Turns the given creeper into a charged creeper. In 1.10, this requires reflection since the DataManager keys - * are private. (You could call {@link EntityCreeper#onStruckByLightning(...)} and then heal it and - * extinguish it, but that's a bit awkward.) */ - public static void chargeCreeper(EntityCreeper creeper){ - creeper.getDataManager().set(POWERED, true); - } - - // SECTION Raytracing - // =============================================================================================================== - - /** - * Does a block ray trace (NOT entities) from an entity's eyes (i.e. properly...) - */ - @Nullable - public static RayTraceResult rayTrace(double range, World world, EntityLivingBase entity, boolean hitLiquids){ - - Vec3d start = new Vec3d(entity.posX, entity.getEntityBoundingBox().minY + entity.getEyeHeight(), entity.posZ); - Vec3d look = entity.getLookVec(); - Vec3d end = start.addVector(look.xCoord * range, look.yCoord * range, look.zCoord * range); - return world.rayTraceBlocks(start, end, hitLiquids); - } - - /** - * Helper method which does a rayTrace for entities from an entity's eye level in the direction they are looking - * with a specified range, using the tracePath method. Tidies up the code a bit. Border size defaults to 1. - * - * @param world - * @param entity - * @param range - * @return - */ - @Nullable - public static RayTraceResult standardEntityRayTrace(World world, EntityLivingBase entity, double range){ - double dx = entity.getLookVec().xCoord * range; - double dy = entity.getLookVec().yCoord * range; - double dz = entity.getLookVec().zCoord * range; - HashSet hashset = new HashSet(1); - hashset.add(entity); - return WizardryUtilities.tracePath(world, (float)entity.posX, (float)(entity.getEntityBoundingBox().minY + entity.getEyeHeight()), (float)entity.posZ, (float)(entity.posX + dx), (float)(entity.posY + entity.getEyeHeight() + dy), (float)(entity.posZ + dz), 1.0f, hashset, false); - } - - /** - * Helper method which does a rayTrace for entities from a entity's eye level in the direction they are looking - * with a specified range and radius, using the tracePath method. Tidies up the code a bit. - * - * @param world - * @param entity - * @param range - * @param borderSize - * @return - */ - @Nullable - public static RayTraceResult standardEntityRayTrace(World world, EntityLivingBase entity, double range, float borderSize){ - double dx = entity.getLookVec().xCoord * range; - double dy = entity.getLookVec().yCoord * range; - double dz = entity.getLookVec().zCoord * range; - HashSet hashset = new HashSet(1); - hashset.add(entity); - return WizardryUtilities.tracePath(world, (float)entity.posX, (float)(entity.getEntityBoundingBox().minY + entity.getEyeHeight()), (float)entity.posZ, (float)(entity.posX + dx), (float)(entity.posY + entity.getEyeHeight() + dy), (float)(entity.posZ + dz), borderSize, hashset, false); - } - - /** - * Method for ray tracing entities (the useless default method doesn't work, despite EnumHitType having an ENTITY field...) - * You can also use this for seeking. - * - * @param world - * @param x startX - * @param y startY - * @param z startZ - * @param tx endX - * @param ty endY - * @param tz endZ - * @param borderSize extra area to examine around line for entities - * @param excluded any excluded entities (the player, etc) - * @return a RayTraceResult of either the block hit (no entity hit), the entity hit (hit an entity), or null for nothing hit - */ - @Nullable - public static RayTraceResult tracePath(World world, float x, float y, float z, float tx, float ty, float tz, float borderSize, HashSet excluded, boolean collideablesOnly){ - - Vec3d startVec = new Vec3d(x, y, z); - //Vec3d lookVec = new Vec3d(tx-x, ty-y, tz-z); - Vec3d endVec = new Vec3d(tx, ty, tz); - float minX = x < tx ? x : tx; - float minY = y < ty ? y : ty; - float minZ = z < tz ? z : tz; - float maxX = x > tx ? x : tx; - float maxY = y > ty ? y : ty; - float maxZ = z > tz ? z : tz; - AxisAlignedBB bb = new AxisAlignedBB(minX, minY, minZ, maxX, maxY, maxZ).expand(borderSize, borderSize, borderSize); - List allEntities = world.getEntitiesWithinAABBExcludingEntity(null, bb); - RayTraceResult blockHit = world.rayTraceBlocks(startVec, endVec); - startVec = new Vec3d(x, y, z); - endVec = new Vec3d(tx, ty, tz); - float maxDistance = (float) endVec.distanceTo(startVec); - if(blockHit!=null) - { - maxDistance = (float) blockHit.hitVec.distanceTo(startVec); - } - Entity closestHitEntity = null; - float closestHit = maxDistance; - float currentHit = 0.f; - AxisAlignedBB entityBb;// = ent.getBoundingBox(); - RayTraceResult intercept; - for(Entity ent : allEntities) - { - if((ent.canBeCollidedWith() || !collideablesOnly) && ((excluded != null && !excluded.contains(ent)) || excluded == null)) - { - float entBorder = ent.getCollisionBorderSize(); - entityBb = ent.getEntityBoundingBox(); - if(entityBb!=null) - { - entityBb = entityBb.expand(entBorder, entBorder, entBorder); - intercept = entityBb.calculateIntercept(startVec, endVec); - if(intercept!=null) - { - currentHit = (float) intercept.hitVec.distanceTo(startVec); - if(currentHit < closestHit || currentHit==0) - { - closestHit = currentHit; - closestHitEntity = ent; - } - } - } - } - } - if(closestHitEntity!=null) - { - blockHit = new RayTraceResult(closestHitEntity); - } - return blockHit; - } - - // SECTION Rendering and GUIs - // =============================================================================================================== - - // Doesn't seem right to put this in the proxies since it should only ever be called from client-side code, and I'm - // not about to make a whole separate utilities class just for one method. Fully qualified names it is! - /** - * [Client-side only] Draws a textured rectangle, taking the size of the image and the bit needed into - * account, unlike {@link net.minecraft.client.gui.Gui#drawTexturedModalRect(int, int, int, int, int, int) - * Gui.drawTexturedModalRect(int, int, int, int, int, int)}, which is harcoded for only 256x256 textures. Also - * handy for custom potion icons. - * @param x The x position of the rectangle - * @param y The y position of the rectangle - * @param u The x position of the top left corner of the section of the image wanted - * @param v The y position of the top left corner of the section of the image wanted - * @param width The width of the section - * @param height The height of the section - * @param textureWidth The width of the actual image. - * @param textureHeight The height of the actual image. - */ - @SideOnly(Side.CLIENT) - public static void drawTexturedRect(int x, int y, int u, int v, int width, int height, int textureWidth, int textureHeight){ - - float f = 1F / (float)textureWidth; - float f1 = 1F / (float)textureHeight; - - // Essentially the same as getting the tessellator. For most code, you'll want the tessellator AND the vertexbuffer - // stored in local variables. - net.minecraft.client.renderer.VertexBuffer buffer = net.minecraft.client.renderer.Tessellator.getInstance().getBuffer(); - // Equivalent of tessellator.startDrawingQuads() - buffer.begin(org.lwjgl.opengl.GL11.GL_QUADS, net.minecraft.client.renderer.vertex.DefaultVertexFormats.POSITION_TEX); - // Equivalent of tessellator.addVertex() - buffer.pos((double)(x), (double)(y + height), 0).tex((double)((float)(u) * f), (double)((float)(v + height) * f1)).endVertex(); - buffer.pos((double)(x + width), (double)(y + height), 0).tex((double)((float)(u + width) * f), (double)((float)(v + height) * f1)).endVertex(); - buffer.pos((double)(x + width), (double)(y), 0).tex((double)((float)(u + width) * f), (double)((float)(v) * f1)).endVertex(); - buffer.pos((double)(x), (double)(y), 0).tex((double)((float)(u) * f), (double)((float)(v) * f1)).endVertex(); - // Exactly the same as before. - net.minecraft.client.renderer.Tessellator.getInstance().draw(); - } - - /** Shorthand for {@link WizardryUtilities#drawTexturedRect(int, int, int, int, int, int, int, int)} which draws - * the entire texture (u and v are set to 0 and textureWidth and textureHeight are the same as width and height). */ - @SideOnly(Side.CLIENT) - public static void drawTexturedRect(int x, int y, int width, int height){ - drawTexturedRect(x, y, 0, 0, width, height, width, height); - } - - // SECTION NBT and Data Storage - // =============================================================================================================== - - /** - * Verifies that the given string is a valid string representation of a UUID. More specifically, returns true if and - * only if the given string is not null and matches the regular expression:

    - *

    /^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$/

    - * which is the regex equivalent of the standard string representation of a UUID as described in {@link UUID#toString()}. - * This method is intended to be used as a check to prevent an {@link IllegalArgumentException} from occurring when - * calling {@link UUID#fromString(String)}. - * @param string The string to be checked - * @return Whether the given string is a valid string representation of a UUID - * @deprecated UUIDs can now be stored in NBT directly; use that in preference to storing them as strings. - */ - @Deprecated - public static boolean verifyUUIDString(String string){ - return string != null && string.matches("/^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$/"); - } - - /** Generic method that stores any Map to an NBTTagList, given two functions that convert the key and value - * types in that map to subclasses of NBTBase. For what it's worth, there is very little point in using this unless - * you can use something more concise than an anonymous class to do the conversion. A lambda expression, or better, - * a method reference, would fit nicely. For example, take ExtendedPlayer's use of this to store conjured item - * durations: - *

    - * properties.setTag("conjuredItems", WizardryUtilities.mapToNBT(this.conjuredItemDurations, - * item -> new NBTTagInt(Item.getIdFromItem((Item)item)), NBTTagInt::new)); - *

    - * This is a lot nicer than simply iterating through the map, because for that you need to use the entry list, - * which introduces local variables that aren't really necessary. Notice that, since the values V in the map are - * simply Integer objects, a simple constructor reference to NBTTagInt::new can be used instead of a lambda - * expression (the Integer is auto-unboxed to int). - * - * @param The type of key stored in the given Map. - * @param The type of value stored in the given Map. - * @param The subtype of NBTBase that the keys (of type K) will be converted to. - * @param The subtype of NBTBase that the values (of type V) will be converted to. - * @param map The Map to be stored. - * @param keyFunction A Function that converts the keys in the map to NBT objects that can be stored. - * @param valueFunction A Function that converts the values in the map to NBT objects that can be stored. - * @param keyTagName The tag name to use for the key tags. - * @param valueTagName The tag name to use for the value tags. - * @return An NBTTagList that represents the given Map. - */ - public static NBTTagList mapToNBT(Map map, Function keyFunction, Function valueFunction, String keyTagName, String valueTagName){ - - NBTTagList tagList = new NBTTagList(); - - for(Entry entry : map.entrySet()){ - NBTTagCompound mapping = new NBTTagCompound(); - mapping.setTag(keyTagName, keyFunction.apply(entry.getKey())); - mapping.setTag(valueTagName, valueFunction.apply(entry.getValue())); - tagList.appendTag(mapping); - } - - return tagList; - } - - /** See {@link WizardryUtilities#mapToNBT(Map, Function, Function, String, String)}; this version is for when the - * names of the individual key/value tags are unimportant (they default to "key" and "value" respectively). */ - public static NBTTagList mapToNBT(Map map, Function keyFunction, Function valueFunction){ - return mapToNBT(map, keyFunction, valueFunction, "key", "value"); - } - - /** Generic method that reads a Map from an NBTTagList, given two functions that convert the key and value tag types - * into the key and value types in the returned map. The given NBTTagList remains unchanged after calling this method. - * - * @param The type of key stored in the returned Map. - * @param The type of value stored in the returned Map. - * @param The subtype of NBTBase that the keys are stored as. - * @param The subtype of NBTBase that the values are stored as. - * @param tagList The NBTTagList to be converted. This must be a list of compound tags. - * @param keyFunction A Function that converts the generic NBTBase tags in the list to keys of type K for the map. - * @param valueFunction A Function that converts the generic NBTBase tags in the list to values of type V for the map. - * @param keyTagName The tag name used for the key tags. - * @param valueTagName The tag name used for the value tags. - * @return A Map containing the keys and values stored in the given NBTTagList. Can be empty, but not null. - * @throws ClassCastException If the tags are not of the expected type. - * @see WizardryUtilities#mapToNBT(Map, Function, Function, String, String) - */ - @SuppressWarnings("unchecked") // Intentional, because throwing an exception is appropriate here. - public static Map NBTToMap(NBTTagList tagList, Function keyFunction, Function valueFunction, String keyTagName, String valueTagName){ - - Map map = new HashMap(); - - for(int i=0; i Map NBTToMap(NBTTagList tagList, Function keyFunction, Function valueFunction){ - return NBTToMap(tagList, keyFunction, valueFunction, "key", "value"); - } - - /** Generic method that stores any Collection to an NBTTagList, given a function that converts the elements in - * that collection to subclasses of NBTBase. For what it's worth, there is very little point in using this unless - * you can use something more concise than an anonymous class to do the conversion. A lambda expression, or better, - * a method reference, would fit nicely. - * - * @param The type of element stored in the given Collection. - * @param The NBT tag type that the elements will be converted to. - * @param list The Collection to be stored. - * @param function A Function that converts the elements in the collection to NBT objects that can be stored. - * @return An NBTTagList that represents the given Collection. - */ - public static NBTTagList listToNBT(Collection list, Function function){ - - NBTTagList tagList = new NBTTagList(); - // If the collection is ordered, it will preserve the order, even though we don't know what type it is yet. - for(E element : list){ - tagList.appendTag(function.apply(element)); - } - - return tagList; - } - - /** Generic method that reads a Collection from an NBTTagList, given a function that converts the element tag - * types to the element types in the returned collection. The given NBTTagList remains unchanged after calling this - * method. Unless the target variable for this method is of type Collection, you will need to create a new - * collection containing the elements in the returned collection via that collection's constructor (e.g. {@code new - * HashSet(collection)}). Although this method returns a Collection rather than any of its subtypes, it - * uses an ArrayList internally to guarantee the order of the elements in the returned collection is the same - * as the order in which they were stored. As such, you may safely cast to List should you wish. - * - * @param The type of element stored in the returned Collection. - * @param The subtype of NBTBase that the elements are stored as. - * @param tagList The NBTTagList to be converted. - * @param function A Function that converts the generic NBTBase tags in the list to elements for the collection. - * Chances are you will need to cast the NBTBase tag to whichever NBT tag type you are expecting in order to - * access the appropriate getter method. - * @return A Collection containing the elements stored in the given NBTTagList. Can be empty, but not null. - * @throws ClassCastException If the tags are not of the expected type. - */ - @SuppressWarnings("unchecked") // Intentional, because throwing an exception is appropriate here. - public static Collection NBTToList(NBTTagList tagList, Function function){ - // Uses an ArrayList to guarantee iteration order, and also to permit duplicate elements (which are - // perfectly reasonable in this context). - Collection list = new ArrayList(); - // The original tag list should remain unchanged, hence the copy. - NBTTagList tagList2 = (NBTTagList) tagList.copy(); - - while(!tagList2.hasNoTags()){ - NBTBase tag = tagList2.removeTag(0); - // Why oh why is NBTTagList not parametrised? It even has a tagType field, so it must know! - try { list.add(function.apply((T)tag)); } catch (ClassCastException e){ - Wizardry.logger.error("Error when reading list from NBT: unexpected tag type " + NBTBase.NBT_TYPES[tag.getId()], e); - } - } - - return list; - - } - - // TODO: Backport: It has recently become apparent that storing UUIDs as strings is not good practice, so backport these two - // methods to 1.7.10 and replace tag.setUniqueId and tag.getUniqueId with their respective contents from 1.10.2. - - /** Returns an NBTTagCompound which contains only the given UUID, stored using - * {@link NBTTagCompound#setUniqueId(String, UUID)}. Allows for neater storage to NBTTagLists. */ - public static NBTTagCompound UUIDtoTagCompound(UUID id){ - NBTTagCompound tag = new NBTTagCompound(); - tag.setUniqueId("uuid", id); - return tag; - } - - /** Wrapper for {@link NBTTagCompound#getUniqueId(String)} which converts an NBTTagCompound directly to a UUID. - * Intended to be used as the inverse of {@link WizardryUtilities#UUIDtoTagCompound(UUID)}.*/ - public static UUID tagCompoundToUUID(NBTTagCompound tag){ - return tag.getUniqueId("uuid"); - } - - // SECTION Ally Designation System - // =============================================================================================================== - - /** - * Returns whether the given target can be attacked by the given attacker. It is up to the caller of this method - * to work out what this means; it doesn't necessarily mean the target is completely immune (for example, revenge - * targeting might reasonably bypass this). This method is intended for use where the damage is indirect and/or - * unavoidable; direct attacks should not check this method. Currently this means the following situations check - * this method: - *

    - * - AI targeting for summoned creatures
    - * - AI targeting for mind-controlled creatures
    - * - Constructs with an area of effect
    - * - Instantaneous spells with an area of effect around the caster (e.g. forest's curse, thunderstorm)
    - * - Any lightning chaining effects
    - * - Any projectiles which seek targets - *

    - * Also note that the friendly fire option is dealt with - * in the event handler. This method acts as a sort of wrapper for all the ADS stuff in {@link WizardData}; more - * details about the ally designation system can be found there. - * - * @param attacker The entity that cast the spell originally - * @param target The entity being attacked - * - * @return False under any of the following circumstances, true otherwise: - *

    - * - Either entity is null - *

    - * - The target is the attacker (this isn't as stupid as it sounds - anything with an AoE might cause this to be - * true, as can summoned creatures) - *

    - * - The target and the attacker are both players and the target is an ally of the attacker (but the - * attacker need not be an ally of the target) - *

    - * - The target is a creature that was summoned/controlled by the attacker or by an ally of the attacker. - */ - public static boolean isValidTarget(Entity attacker, Entity target){ - - if(attacker == null || target == null) return false; - - // Tests whether the target is the attacker - if(target == attacker) return false; - - // Tests whether the target is a creature that was summoned by the attacker - if(target instanceof ISummonedCreature && ((ISummonedCreature)target).getCaster() == attacker){ - return false; - } - - // Tests whether the target is a creature that was mind controlled by the attacker - if(target instanceof EntityLiving && ((EntityLivingBase)target).isPotionActive(WizardryPotions.mind_control)){ - - NBTTagCompound entityNBT = target.getEntityData(); - - if(entityNBT != null && entityNBT.hasKey(MindControl.NBT_KEY)){ - if(attacker == WizardryUtilities.getEntityByUUID(target.worldObj, entityNBT.getUniqueId(MindControl.NBT_KEY))){ - return false; - } - } - } - - // Ally section - if(attacker instanceof EntityPlayer && WizardData.get((EntityPlayer)attacker) != null){ - - if(target instanceof EntityPlayer){ - // Tests whether the target is an ally of the attacker - if(WizardData.get((EntityPlayer)attacker).isPlayerAlly((EntityPlayer)target)){ - return false; - } - - }else if(target instanceof ISummonedCreature){ - // Tests whether the target is a creature that was summoned by an ally of the attacker - if(((ISummonedCreature)target).getCaster() instanceof EntityPlayer - && WizardData.get((EntityPlayer)attacker).isPlayerAlly((EntityPlayer)((ISummonedCreature)target).getCaster())){ - return false; - } - - }else if(target instanceof EntityLiving && ((EntityLivingBase)target).isPotionActive(WizardryPotions.mind_control)){ - // Tests whether the target is a creature that was mind controlled by an ally of the attacker - NBTTagCompound entityNBT = target.getEntityData(); - - if(entityNBT != null && entityNBT.hasKey(MindControl.NBT_KEY)){ - - Entity controller = WizardryUtilities.getEntityByUUID(target.worldObj, entityNBT.getUniqueId(MindControl.NBT_KEY)); - - if(controller instanceof EntityPlayer && WizardData.get((EntityPlayer)attacker).isPlayerAlly((EntityPlayer)controller)){ - return false; - } - } - } - } - - return true; - } - - /** Helper method for testing if the second player is an ally of the first player. Makes the code neater. */ - public static boolean isPlayerAlly(EntityPlayer allyOf, EntityPlayer possibleAlly) { - - WizardData properties = WizardData.get(allyOf); - - if(properties != null && properties.isPlayerAlly(possibleAlly)) return true; - - return false; - } - - // SECTION Loot and Weighting - // =============================================================================================================== - - /** - * See {@link WizardryUtilities#getStandardWeightedRandomSpellId(Random, boolean)}. nonContinuous defaults to false. - */ - public static int getStandardWeightedRandomSpellId(Random random){ - return getStandardWeightedRandomSpellId(random, false); - } - - /** - * Helper method which gets a spell id according to the standard weighting. The tier is a weighted random value; - * the actual spell within that tier is completely random. Will not return the id of a spell which has been - * disabled in the config. This is for simple stuff like chests and drops; more complex generators like wizard - * trades don't use this method. - *

    - * For reference, the standard weighting is as follows: - * Basic: 60%, Apprentice: 25%, Advanced: 10%, Master: 5% - * - * @param random An instance of {@link Random} to use for RNG - * @param nonContinuous Whether the spells must be non-continuous (used for scrolls) - * @return A random spell id number - */ - public static int getStandardWeightedRandomSpellId(Random random, boolean nonContinuous){ - - Tier tier = Tier.getWeightedRandomTier(random); - - List spells = Spell.getSpells(new Spell.TierElementFilter(tier, null)); - if(nonContinuous) spells.retainAll(Spell.getSpells(Spell.nonContinuousSpells)); - - // Ensures the tier chosen actually has spells in it, and if not uses BASIC instead. - if(spells.isEmpty()){ - spells = Spell.getSpells(new Spell.TierElementFilter(Tier.BASIC, null)); - if(nonContinuous) spells.retainAll(Spell.getSpells(Spell.nonContinuousSpells)); - } - - // Finds a random spell in the list and returns its id. - return spells.get(random.nextInt(spells.size())).id(); - } - - // TODO: These methods need a rethink. What are we trying to achieve with them? Should each use case look in the same - // pool of items? For example, might we (or someone else) want to have a wand which can generate in chests, but is - // not used by wizards? - - // I reckon this should be strictly for cases where we only ever want the standard wand set, i.e. wizards' gear, etc. - - /** - * Helper method to return the appropriate armour item based on element and slot. As of Wizardry 2.1, this uses - * the immutable map stored in {@link WizardryItems#ARMOUR_MAP}. - * Currently used to iterate through armour for registering charging recipes and for chest generation. - * @param element The EnumElement of the armour required. Null will be converted to {@link Element#MAGIC}. - * @param slot EntityEquipmentSlot of the armour piece required - * @return The armour item which corresponds to the given element and slot, or null if no such item exists. - * @throws IllegalArgumentException if the given slot is not an armour slot. - */ - public static Item getArmour(Element element, EntityEquipmentSlot slot){ - if(slot == null || slot.getSlotType() != Type.ARMOR) throw new IllegalArgumentException("Must be a valid armour slot"); - if(element == null) element = Element.MAGIC; - return WizardryItems.ARMOUR_MAP.get(ImmutablePair.of(slot, element)); - } - - /** - * Helper method to return the appropriate wand based on tier and element.As of Wizardry 2.1, this uses - * the immutable map stored in {@link WizardryItems#WAND_MAP}. - * Currently used in the packet handler for upgrading wands, for chest generation and to iterate through - * wands for charging recipes. - * @param tier The tier of the wand required. - * @param element The element of the wand required. Null will be converted to {@link Element#MAGIC}. - * @return The wand item which corresponds to the given element and slot, or null if no such item exists. - * @throws NullPointerException if the given tier is null. - */ - public static Item getWand(Tier tier, Element element){ - if(tier == null) throw new NullPointerException("The given tier cannot be null."); - if(element == null) element = Element.MAGIC; - return WizardryItems.WAND_MAP.get(ImmutablePair.of(tier, element)); - } -} diff --git a/src/main/resources/assets/wizardry/blockstates/arcane_workbench.json b/src/main/resources/assets/wizardry/blockstates/arcane_workbench.json deleted file mode 100644 index c5741b8e..00000000 --- a/src/main/resources/assets/wizardry/blockstates/arcane_workbench.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "forge_marker": 1, - "variants": { - "normal": { "model": "wizardry:arcane_workbench" } - } -} diff --git a/src/main/resources/assets/wizardry/blockstates/crystal_block.json b/src/main/resources/assets/wizardry/blockstates/crystal_block.json deleted file mode 100644 index d5ed94e1..00000000 --- a/src/main/resources/assets/wizardry/blockstates/crystal_block.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "forge_marker": 1, - "variants": { - "normal": { "model": "wizardry:crystal_block" } - } -} diff --git a/src/main/resources/assets/wizardry/blockstates/crystal_flower.json b/src/main/resources/assets/wizardry/blockstates/crystal_flower.json deleted file mode 100644 index 61e2d72c..00000000 --- a/src/main/resources/assets/wizardry/blockstates/crystal_flower.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "variants": { - "normal": { "model": "wizardry:crystal_flower" } - } -} diff --git a/src/main/resources/assets/wizardry/blockstates/crystal_ore.json b/src/main/resources/assets/wizardry/blockstates/crystal_ore.json deleted file mode 100644 index 234c9a97..00000000 --- a/src/main/resources/assets/wizardry/blockstates/crystal_ore.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "forge_marker": 1, - "variants": { - "normal": { "model": "wizardry:crystal_ore" } - } -} diff --git a/src/main/resources/assets/wizardry/blockstates/ice_statue.json b/src/main/resources/assets/wizardry/blockstates/ice_statue.json deleted file mode 100644 index e2c0b099..00000000 --- a/src/main/resources/assets/wizardry/blockstates/ice_statue.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "forge_marker": 1, - "variants": { - "normal": { "model": "wizardry:ice_statue" } - } -} diff --git a/src/main/resources/assets/wizardry/blockstates/magic_light.json b/src/main/resources/assets/wizardry/blockstates/magic_light.json deleted file mode 100644 index df809b83..00000000 --- a/src/main/resources/assets/wizardry/blockstates/magic_light.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "forge_marker": 1, - "variants": { - "normal": { "model": "wizardry:magic_light" } - } -} diff --git a/src/main/resources/assets/wizardry/blockstates/meteor.json b/src/main/resources/assets/wizardry/blockstates/meteor.json deleted file mode 100644 index f1d33b5b..00000000 --- a/src/main/resources/assets/wizardry/blockstates/meteor.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "forge_marker": 1, - "variants": { - "normal": { "model": "wizardry:meteor" } - } -} diff --git a/src/main/resources/assets/wizardry/blockstates/petrified_stone.json b/src/main/resources/assets/wizardry/blockstates/petrified_stone.json deleted file mode 100644 index c8495ade..00000000 --- a/src/main/resources/assets/wizardry/blockstates/petrified_stone.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "forge_marker": 1, - "variants": { - "normal": { "model": "wizardry:petrified_stone" } - } -} diff --git a/src/main/resources/assets/wizardry/blockstates/snare.json b/src/main/resources/assets/wizardry/blockstates/snare.json deleted file mode 100644 index 594eb529..00000000 --- a/src/main/resources/assets/wizardry/blockstates/snare.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "forge_marker": 1, - "variants": { - "normal": { "model": "wizardry:snare" } - } -} diff --git a/src/main/resources/assets/wizardry/blockstates/spectral_block.json b/src/main/resources/assets/wizardry/blockstates/spectral_block.json deleted file mode 100644 index 79efd6b9..00000000 --- a/src/main/resources/assets/wizardry/blockstates/spectral_block.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "forge_marker": 1, - "variants": { - "normal": { "model": "wizardry:spectral_block" } - } -} diff --git a/src/main/resources/assets/wizardry/blockstates/transportation_stone.json b/src/main/resources/assets/wizardry/blockstates/transportation_stone.json deleted file mode 100644 index 67830c59..00000000 --- a/src/main/resources/assets/wizardry/blockstates/transportation_stone.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "forge_marker": 1, - "variants": { - "normal": { "model": "wizardry:transportation_stone" } - } -} diff --git a/src/main/resources/assets/wizardry/blockstates/vanishing_cobweb.json b/src/main/resources/assets/wizardry/blockstates/vanishing_cobweb.json deleted file mode 100644 index ce86f118..00000000 --- a/src/main/resources/assets/wizardry/blockstates/vanishing_cobweb.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "forge_marker": 1, - "variants": { - "normal": { "model": "wizardry:vanishing_cobweb" } - } -} diff --git a/src/main/resources/assets/wizardry/lang/en_GB.lang b/src/main/resources/assets/wizardry/lang/en_GB.lang index 8ae8a4d4..b7db3b1e 100644 --- a/src/main/resources/assets/wizardry/lang/en_GB.lang +++ b/src/main/resources/assets/wizardry/lang/en_GB.lang @@ -1,273 +1,272 @@ -tile.wizardry:arcane_workbench.name=Arcane Workbench -tile.wizardry:crystal_ore.name=Crystal Ore -tile.wizardry:petrified_stone.name=Petrified Stone -tile.wizardry:ice_statue.name=Ice Statue -tile.wizardry:crystal_flower.name=Crystal Flower -tile.wizardry:snare.name=Snare -tile.wizardry:transportation_stone.name=Stone of Transportation -tile.wizardry:spectral_block.name=Spectral Block -tile.wizardry:crystal_block.name=Block of Crystal +tile.arcaneWorkbench.name=Arcane Workbench +tile.crystalOre.name=Crystal Ore +tile.petrifiedStone.name=Petrified Stone +tile.iceStatue.name=Ice Statue +tile.crystalFlower.name=Crystal Flower +tile.snare.name=Snare +tile.transportationStone.name=Stone of Transportation +tile.spectralBlock.name=Spectral Block +tile.crystalBlock.name=Block of Crystal -item.wizardry:magic_crystal.name=Magic Crystal -item.wizardry:magic_wand.name=Magic Wand -item.wizardry:apprentice_wand.name=Apprentice Wand -item.wizardry:advanced_wand.name=Advanced Wand -item.wizardry:master_wand.name=Master Wand -item.wizardry:spell_book.name=Spell Book +item.magicCrystal.name=Magic Crystal +item.magicWand.name=Magic Wand +item.apprenticeWand.name=Apprentice Wand +item.advancedWand.name=Advanced Wand +item.masterWand.name=Master Wand +item.spellBook.name=Spell Book -item.wizardry:arcane_tome.name=Tome of Arcana -item.wizardry:arcane_tome.desc1=Upgrades any %1$s -item.wizardry:arcane_tome.desc2=wand to %1$s tier +item.arcaneTome.name=Tome of Arcana +item.arcaneTome.desc1=Upgrades any %1$s +item.arcaneTome.desc2=wand to %1$s tier -item.wizardry:wizard_handbook.name=The Wizard's Handbook -item.wizardry:wizard_handbook.desc=by %1$s +item.wizardHandbook.name=The Wizard's Handbook +item.wizardHandbook.desc=by %1$s -item.wizardry:wand.buff=+%1$s %2$s potency -item.wizardry:wand.spell=Current Spell: %1$s -item.wizardry:wand.mana=Mana: %1$s/%2$s +item.wand.buff=+%1$s %2$s potency +item.wand.spell=Current Spell: %1$s +item.wand.mana=Mana: %1$s/%2$s -item.wizardry:wand.addally=%1$s has been added to your list of allies -item.wizardry:wand.removeally=%1$s has been removed from your list of allies +item.wand.addally=%1$s has been added to your list of allies +item.wand.removeally=%1$s has been removed from your list of allies -item.wizardry:basic_fire_wand.name=Wand of Embers -item.wizardry:basic_ice_wand.name=Wand of Frost -item.wizardry:basic_lightning_wand.name=Wand of Sparks -item.wizardry:basic_necromancy_wand.name=Wand of Shadows -item.wizardry:basic_earth_wand.name=Wand of the Forest -item.wizardry:basic_sorcery_wand.name=Wand of Mystery -item.wizardry:basic_healing_wand.name=Wand of Healing +item.basicFireWand.name=Wand of Embers +item.basicIceWand.name=Wand of Frost +item.basicLightningWand.name=Wand of Sparks +item.basicNecromancyWand.name=Wand of Shadows +item.basicEarthWand.name=Wand of the Forest +item.basicSorceryWand.name=Wand of Mystery +item.basicHealingWand.name=Wand of Healing -item.wizardry:apprentice_fire_wand.name=Apprentice Pyromancer Wand -item.wizardry:apprentice_ice_wand.name=Apprentice Ice Mage Wand -item.wizardry:apprentice_lightning_wand.name=Apprentice Storm Mage Wand -item.wizardry:apprentice_necromancy_wand.name=Apprentice Necromancer Wand -item.wizardry:apprentice_earth_wand.name=Apprentice Earth Mage Wand -item.wizardry:apprentice_sorcery_wand.name=Apprentice Sorcerer Wand -item.wizardry:apprentice_healing_wand.name=Apprentice Healer Wand +item.apprenticeFireWand.name=Apprentice Pyromancer Wand +item.apprenticeIceWand.name=Apprentice Ice Mage Wand +item.apprenticeLightningWand.name=Apprentice Storm Mage Wand +item.apprenticeNecromancyWand.name=Apprentice Necromancer Wand +item.apprenticeEarthWand.name=Apprentice Earth Mage Wand +item.apprenticeSorceryWand.name=Apprentice Sorcerer Wand +item.apprenticeHealingWand.name=Apprentice Healer Wand -item.wizardry:advanced_fire_wand.name=Wand of the Pyromancer -item.wizardry:advanced_ice_wand.name=Wand of the Ice Mage -item.wizardry:advanced_lightning_wand.name=Wand of the Storm Mage -item.wizardry:advanced_necromancy_wand.name=Wand of the Necromancer -item.wizardry:advanced_earth_wand.name=Wand of the Earth Mage -item.wizardry:advanced_sorcery_wand.name=Wand of the Sorcerer -item.wizardry:advanced_healing_wand.name=Wand of the Healer +item.advancedFireWand.name=Wand of the Pyromancer +item.advancedIceWand.name=Wand of the Ice Mage +item.advancedLightningWand.name=Wand of the Storm Mage +item.advancedNecromancyWand.name=Wand of the Necromancer +item.advancedEarthWand.name=Wand of the Earth Mage +item.advancedSorceryWand.name=Wand of the Sorcerer +item.advancedHealingWand.name=Wand of the Healer -item.wizardry:master_fire_wand.name=Master Pyromancer Wand -item.wizardry:master_ice_wand.name=Master Ice Mage Wand -item.wizardry:master_lightning_wand.name=Master Storm Mage Wand -item.wizardry:master_necromancy_wand.name=Master Necromancer Wand -item.wizardry:master_earth_wand.name=Master Earth Mage Wand -item.wizardry:master_sorcery_wand.name=Master Sorcerer Wand -item.wizardry:master_healing_wand.name=Master Healer Wand +item.masterFireWand.name=Master Pyromancer Wand +item.masterIceWand.name=Master Ice Mage Wand +item.masterLightningWand.name=Master Storm Mage Wand +item.masterNecromancyWand.name=Master Necromancer Wand +item.masterEarthWand.name=Master Earth Mage Wand +item.masterSorceryWand.name=Master Sorcerer Wand +item.masterHealingWand.name=Master Healer Wand -item.wizardry:spectral_sword.name=Spectral Sword -item.wizardry:spectral_pickaxe.name=Spectral Pickaxe -item.wizardry:spectral_bow.name=Spectral Bow +item.spectralSword.name=Spectral Sword +item.spectralPickaxe.name=Spectral Pickaxe +item.spectralBow.name=Spectral Bow -item.wizardry:mana_flask.name=Mana Flask -item.wizardry:storage_upgrade.name=Wand Storage Upgrade -item.wizardry:siphon_upgrade.name=Wand Siphon Upgrade -item.wizardry:condenser_upgrade.name=Wand Condenser Upgrade -item.wizardry:range_upgrade.name=Wand Range Upgrade -item.wizardry:duration_upgrade.name=Wand Duration Upgrade -item.wizardry:cooldown_upgrade.name=Wand Cooldown Upgrade -item.wizardry:blast_upgrade.name=Wand Blast Upgrade -item.wizardry:attunement_upgrade.name=Wand Attunement Upgrade +item.manaFlask.name=Mana Flask +item.storageUpgrade.name=Wand Storage Upgrade +item.siphonUpgrade.name=Wand Siphon Upgrade +item.condenserUpgrade.name=Wand Condenser Upgrade +item.rangeUpgrade.name=Wand Range Upgrade +item.durationUpgrade.name=Wand Duration Upgrade +item.cooldownUpgrade.name=Wand Cooldown Upgrade +item.blastUpgrade.name=Wand Blast Upgrade +item.attunementUpgrade.name=Wand Attunement Upgrade -item.wizardry:flaming_axe.name=Flaming Axe -item.wizardry:frost_axe.name=Frost Axe +item.flamingAxe.name=Flaming Axe +item.frostAxe.name=Frost Axe -item.wizardry:firebomb.name=Firebomb -item.wizardry:poison_bomb.name=Poison Bomb -item.wizardry:smoke_bomb.name=Smoke Bomb +item.firebomb.name=Firebomb +item.poisonBomb.name=Poison Bomb +item.smoke_bomb.name=Smoke Bomb -item.wizardry:blank_scroll.name=Blank Scroll -item.wizardry:scroll.name=Scroll of %1$s -item.wizardry:scroll.undiscovered.name=Scroll "%1$s" -item.wizardry:identification_scroll.name=Scroll of Identification -item.wizardry:identification_scroll.desc1=%1$sIdentifies an unknown -item.wizardry:identification_scroll.desc2=%1$sspell book or scroll -item.wizardry:identification_scroll.nothing_to_identify=Nothing to identify! +item.blankScroll.name=Blank Scroll +item.scroll.name=Scroll of %1$s +item.scroll.undiscovered.name=Scroll "%1$s" +item.identification_scroll.name=Scroll of Identification +item.identification_scroll.desc1=%1$sIdentifies an unknown +item.identification_scroll.desc2=%1$sspell book or scroll +item.identification_scroll.nothing_to_identify=Nothing to identify! -item.wizardry:armour_upgrade.name=Arcane Seal of Protection -item.wizardry:armour_upgrade.desc1=%1$sUpgrades any wizard armour -item.wizardry:armour_upgrade.desc2=%1$sto make it %2$slegendary +item.armourUpgrade.name=Arcane Seal of Protection +item.armourUpgrade.desc1=%1$sUpgrades any wizard armour +item.armourUpgrade.desc2=%1$sto make it %2$slegendary -item.wizardry:magic_silk.name=Magical Silk +item.magicSilk.name=Magical Silk -item.wizardry:wizard_armour.legendary=Legendary -item.wizardry:wizard_armour.buff=-%1$s %2$s cost -item.wizardry:wizard_armour.mana=Mana: %1$s/%2$s +item.wizardArmour.legendary=Legendary +item.wizardArmour.buff=-%1$s %2$s cost +item.wizardArmour.mana=Mana: %1$s/%2$s -item.wizardry:wizard_hat.name=Wizard Hat -item.wizardry:wizard_robe.name=Wizard Robes -item.wizardry:wizard_leggings.name=Wizard Leggings -item.wizardry:wizard_boots.name=Wizard Boots +item.wizard_hat.name=Wizard Hat +item.wizard_robe.name=Wizard Robes +item.wizard_leggings.name=Wizard Leggings +item.wizard_boots.name=Wizard Boots -item.wizardry:wizard_hat_fire.name=Pyromancer Hat -item.wizardry:wizard_robe_fire.name=Pyromancer Robes -item.wizardry:wizard_leggings_fire.name=Pyromancer Leggings -item.wizardry:wizard_boots_fire.name=Pyromancer Boots +item.wizard_hat_fire.name=Pyromancer Hat +item.wizard_robe_fire.name=Pyromancer Robes +item.wizard_leggings_fire.name=Pyromancer Leggings +item.wizard_boots_fire.name=Pyromancer Boots -item.wizardry:wizard_hat_ice.name=Ice Mage Hat -item.wizardry:wizard_robe_ice.name=Ice Mage Robes -item.wizardry:wizard_leggings_ice.name=Ice Mage Leggings -item.wizardry:wizard_boots_ice.name=Ice Mage Boots +item.wizard_hat_ice.name=Ice Mage Hat +item.wizard_robe_ice.name=Ice Mage Robes +item.wizard_leggings_ice.name=Ice Mage Leggings +item.wizard_boots_ice.name=Ice Mage Boots -item.wizardry:wizard_hat_lightning.name=Storm Mage Hat -item.wizardry:wizard_robe_lightning.name=Storm Mage Robes -item.wizardry:wizard_leggings_lightning.name=Storm Mage Leggings -item.wizardry:wizard_boots_lightning.name=Storm Mage Boots +item.wizard_hat_lightning.name=Storm Mage Hat +item.wizard_robe_lightning.name=Storm Mage Robes +item.wizard_leggings_lightning.name=Storm Mage Leggings +item.wizard_boots_lightning.name=Storm Mage Boots -item.wizardry:wizard_hat_necromancy.name=Necromancer Hat -item.wizardry:wizard_robe_necromancy.name=Necromancer Robes -item.wizardry:wizard_leggings_necromancy.name=Necromancer Leggings -item.wizardry:wizard_boots_necromancy.name=Necromancer Boots +item.wizard_hat_necromancy.name=Necromancer Hat +item.wizard_robe_necromancy.name=Necromancer Robes +item.wizard_leggings_necromancy.name=Necromancer Leggings +item.wizard_boots_necromancy.name=Necromancer Boots -item.wizardry:wizard_hat_earth.name=Earth Mage Hat -item.wizardry:wizard_robe_earth.name=Earth Mage Robes -item.wizardry:wizard_leggings_earth.name=Earth Mage Leggings -item.wizardry:wizard_boots_earth.name=Earth Mage Boots +item.wizard_hat_earth.name=Earth Mage Hat +item.wizard_robe_earth.name=Earth Mage Robes +item.wizard_leggings_earth.name=Earth Mage Leggings +item.wizard_boots_earth.name=Earth Mage Boots -item.wizardry:wizard_hat_sorcery.name=Sorcerer Hat -item.wizardry:wizard_robe_sorcery.name=Sorcerer Robes -item.wizardry:wizard_leggings_sorcery.name=Sorcerer Leggings -item.wizardry:wizard_boots_sorcery.name=Sorcerer Boots +item.wizard_hat_sorcery.name=Sorcerer Hat +item.wizard_robe_sorcery.name=Sorcerer Robes +item.wizard_leggings_sorcery.name=Sorcerer Leggings +item.wizard_boots_sorcery.name=Sorcerer Boots -item.wizardry:wizard_hat_healing.name=Healer Hat -item.wizardry:wizard_robe_healing.name=Healer Robes -item.wizardry:wizard_leggings_healing.name=Healer Leggings -item.wizardry:wizard_boots_healing.name=Healer Boots +item.wizard_hat_healing.name=Healer Hat +item.wizard_robe_healing.name=Healer Robes +item.wizard_leggings_healing.name=Healer Leggings +item.wizard_boots_healing.name=Healer Boots -item.wizardry:spawn_wizard.name=Spawn Wizard -item.wizardry:spawn_evil_wizard.name=Spawn Evil Wizard +item.spawn_wizard.name=Spawn Wizard +item.spawn_evil_wizard.name=Spawn Evil Wizard -item.wizardry:spectral_helmet.name=Spectral Helmet -item.wizardry:spectral_chestplate.name=Spectral Chestplate -item.wizardry:spectral_leggings.name=Spectral Leggings -item.wizardry:spectral_boots.name=Spectral Boots +item.spectral_helmet.name=Spectral Helmet +item.spectral_chestplate.name=Spectral Chestplate +item.spectral_leggings.name=Spectral Leggings +item.spectral_boots.name=Spectral Boots entity.wizardry.summonedcreature.nameplate=%1$s's %2$s -entity.wizardry.summonedcreature.nameplate_fallback=Someone's %1$s -entity.wizardry.zombie_minion.name=Zombie -entity.wizardry.skeleton_minion.name=Skeleton -entity.wizardry.spider_minion.name=Spider -entity.wizardry.blaze_minion.name=Blaze -entity.wizardry.ice_wraith.name=Ice Wraith -entity.wizardry.lightning_wraith.name=Lightning Wraith -entity.wizardry.shadow_wraith.name=Shadow Wraith -entity.wizardry.spirit_wolf.name=Spirit Wolf -entity.wizardry.spirit_horse.name=Spirit Horse -entity.wizardry.ice_giant.name=Ice Giant -entity.wizardry.phoenix.name=Phoenix -entity.wizardry.wizard.name=Wizard -entity.wizardry.magic_slime.name=Magical Slime -entity.wizardry.silverfish_minion.name=Silverfish -entity.wizardry.storm_elemental.name=Storm Elemental -entity.wizardry.evil_wizard.name=Wizard -entity.wizardry.decoy.name=Decoy +entity.wizardry.Zombie Minion.name=Zombie +entity.wizardry.Skeleton Minion.name=Skeleton +entity.wizardry.Spider Minion.name=Spider +entity.wizardry.Blaze Minion.name=Blaze +entity.wizardry.Ice Wraith.name=Ice Wraith +entity.wizardry.Lightning Wraith.name=Lightning Wraith +entity.wizardry.Shadow Wraith.name=Shadow Wraith +entity.wizardry.Spirit Wolf.name=Spirit Wolf +entity.wizardry.Spirit Horse.name=Spirit Horse +entity.wizardry.Ice Giant.name=Ice Giant +entity.wizardry.Phoenix.name=Phoenix +entity.wizardry.Wizard.name=Wizard +entity.wizardry.Magic Slime.name=Magical Slime +entity.wizardry.Silverfish Minion.name=Silverfish +entity.wizardry.Storm Elemental.name=Storm Elemental +entity.wizardry.Evil Wizard.name=Wizard +entity.wizardry.Decoy.name=Decoy -entity.wizardry.magic_missile.name=Magic -entity.wizardry.arc.name=Magic -entity.wizardry.spark_bomb.name=Magic -entity.wizardry.ice_shard.name=Magic -entity.wizardry.firebomb.name=Magic -entity.wizardry.poison_bomb.name=Magic -entity.wizardry.force_orb.name=Magic -entity.wizardry.spark.name=Magic -entity.wizardry.darkness_orb.name=Magic -entity.wizardry.fire_sigil.name=Magic -entity.wizardry.frost_sigil.name=Magic -entity.wizardry.lightning_sigil.name=Magic -entity.wizardry.lightning_arrow.name=Magic -entity.wizardry.firebolt.name=Magic -entity.wizardry.ice_charge.name=Magic -entity.wizardry.force_arrow.name=Magic -entity.wizardry.dart.name=Magic -entity.wizardry.lightning_disc.name=Magic -entity.wizardry.thunderbolt.name=Magic -entity.wizardry.decay.name=Magic -entity.wizardry.ice_lance.name=Magic -entity.wizardry.smoke_bomb.name=Magic -entity.wizardry.ice_spike.name=Magic +entity.wizardry.Magic Missile.name=Magic +entity.wizardry.Arc.name=Magic +entity.wizardry.Spark Bomb.name=Magic +entity.wizardry.Ice Shard.name=Magic +entity.wizardry.Firebomb.name=Magic +entity.wizardry.Poison Bomb.name=Magic +entity.wizardry.Force Orb.name=Magic +entity.wizardry.Spark.name=Magic +entity.wizardry.Darkness Orb.name=Magic +entity.wizardry.Fire Sigil.name=Magic +entity.wizardry.Frost Sigil.name=Magic +entity.wizardry.Lightning Sigil.name=Magic +entity.wizardry.Lightning Arrow.name=Magic +entity.wizardry.Firebolt.name=Magic +entity.wizardry.Ice Charge.name=Magic +entity.wizardry.Force Arrow.name=Magic +entity.wizardry.Dart.name=Magic +entity.wizardry.Lightning Disc.name=Magic +entity.wizardry.Thunderbolt.name=Magic +entity.wizardry.Decay.name=Magic +entity.wizardry.Ice Lance.name=Magic +entity.wizardry.Smoke Bomb.name=Magic +entity.wizardry.Ice Spike.name=Magic -entity.wizardry.black_hole.name=Black Hole -entity.wizardry.shield.name=Shield -entity.wizardry.meteor.name=Meteor -entity.wizardry.blizzard.name=Blizzard -entity.wizardry.bubble.name=Bubble -entity.wizardry.tornado.name=Tornado -entity.wizardry.lightning_hammer.name=Lightning Hammer -entity.wizardry.arrow_rain.name=Arrow Rain -entity.wizardry.healing_aura.name=Healing Aura -entity.wizardry.forcefield.name=Forcefield -entity.wizardry.ring_of_fire.name=Ring of Fire -entity.wizardry.earthquake.name=Earthquake -entity.wizardry.falling_grass.name=Falling Grass -entity.wizardry.hailstorm.name=Hailstorm -entity.wizardry.lightning_pulse.name=Lightning Pulse +entity.wizardry.Black Hole.name=Black Hole +entity.wizardry.Shield.name=Shield +entity.wizardry.Meteor.name=Meteor +entity.wizardry.Blizzard.name=Blizzard +entity.wizardry.Bubble.name=Bubble +entity.wizardry.Tornado.name=Tornado +entity.wizardry.Lightning Hammer.name=Lightning Hammer +entity.wizardry.Arrow Rain.name=Arrow Rain +entity.wizardry.Healing Aura.name=Healing Aura +entity.wizardry.Forcefield.name=Forcefield +entity.wizardry.Ring of Fire.name=Ring of Fire +entity.wizardry.Earthquake.name=Earthquake +entity.wizardry.Falling Grass.name=Falling Grass +entity.wizardry.Hailstorm.name=Hailstorm +entity.wizardry.Lightning Pulse.name=Lightning Pulse -item_group.wizardry=Wizardry -item_group.wizardryspells=Spells +itemGroup.wizardry=Wizardry +itemGroup.wizardryspells=Wizardry Spells -achievement.crystal=A Curious Crystal... -achievement.crystal.desc=Mine a magic crystal -achievement.arcane_initiate=Arcane Initiate -achievement.arcane_initiate.desc=Craft a magic wand with a gold nugget, a stick and a magic crystal -achievement.apprentice=Wizard's Apprentice -achievement.apprentice.desc=Use a tome of arcana to upgrade your wand -achievement.master=Arcane Master -achievement.master.desc=Obtain a master wand -achievement.all_spells=Mage of All Trades -achievement.all_spells.desc=Cast every single spell in the game -achievement.wizard_trade=Magic Dealing -achievement.wizard_trade.desc=Purchase an item from a wizard -achievement.buy_master_spell=Knowledge is Power -achievement.buy_master_spell.wizardry:desc=Purchase a master spell from a wizard -achievement.freeze_blaze=Not So Hot Now -achievement.freeze_blaze.desc=Freeze a blaze solid -achievement.charge_creeper=It's Gonna Blow -achievement.charge_creeper.desc='Accidentally' charge a creeper -achievement.frankenstein=Frankenstein -achievement.frankenstein.desc=Turn a pig into a zombie pigman using the lightning bolt spell -achievement.special_upgrade=Arcane Tinkering -achievement.special_upgrade.desc=Apply a special upgrade to a wand -achievement.craft_flask=It's Magic, Bottled! -achievement.craft_flask.desc=Craft a mana flask -achievement.elemental=Elemental -achievement.elemental.desc=Obtain an elemental wand -achievement.armour_set=Now You're a Proper Wizard -achievement.armour_set.desc=Craft and equip a full set of wizard armour -achievement.legendary=Legendary -achievement.legendary.desc=Obtain a piece of legendary wizard armour -achievement.self_destruct=That Backfired -achievement.self_destruct.desc=Get killed by your own magic -achievement.pig_tornado=Not Again... -achievement.pig_tornado.desc=Ride a pig into a tornado -achievement.jam_wizard=Jamming Session -achievement.jam_wizard.desc=Use the arcane jammer spell on a wizard -achievement.slime_skeleton=Sticky Situation -achievement.slime_skeleton.desc=Engulf a skeleton in slime -achievement.anger_wizard=You'll Regret That -achievement.anger_wizard.desc=Make a wizard angry -achievement.defeat_evil_wizard=Righteousness -achievement.defeat_evil_wizard.desc=Defeat an evil wizard -achievement.max_out_wand=Fully Equipped -achievement.max_out_wand.desc=Apply the maximum number of upgrades to a master wand -achievement.element_master=Element Mastery -achievement.element_master.desc=Cast all the spells of any element -achievement.identify_spell=Arcane Appraisal -achievement.identify_spell.wizardry:desc=Use a scroll of identification to identify a spell book or scroll +achievement.Crystal=A Curious Crystal... +achievement.Crystal.desc=Mine a magic crystal +achievement.ArcaneInitiate=Arcane Initiate +achievement.ArcaneInitiate.desc=Craft a magic wand with a gold nugget, a stick and a magic crystal +achievement.Apprentice=Wizard's Apprentice +achievement.Apprentice.desc=Use a tome of arcana to upgrade your wand +achievement.Master=Arcane Master +achievement.Master.desc=Obtain a master wand +achievement.AllSpells=Mage of All Trades +achievement.AllSpells.desc=Cast every single spell in the game +achievement.WizardTrade=Magic Dealing +achievement.WizardTrade.desc=Purchase an item from a wizard +achievement.BuyMasterSpell=Knowledge is Power +achievement.BuyMasterSpell.desc=Purchase a master spell from a wizard +achievement.FreezeBlaze=Not So Hot Now +achievement.FreezeBlaze.desc=Freeze a blaze solid +achievement.ChargeCreeper=It's Gonna Blow +achievement.ChargeCreeper.desc='Accidentally' charge a creeper +achievement.Frankenstein=Frankenstein +achievement.Frankenstein.desc=Turn a pig into a zombie pigman using the lightning bolt spell +achievement.SpecialUpgrade=Arcane Tinkering +achievement.SpecialUpgrade.desc=Apply a special upgrade to a wand +achievement.CraftFlask=It's Magic, Bottled! +achievement.CraftFlask.desc=Craft a mana flask +achievement.Elemental=Elemental +achievement.Elemental.desc=Obtain an elemental wand +achievement.ArmourSet=Now You're a Proper Wizard +achievement.ArmourSet.desc=Craft and equip a full set of wizard armour +achievement.Legendary=Legendary +achievement.Legendary.desc=Obtain a piece of legendary wizard armour +achievement.SelfDestruct=That Backfired +achievement.SelfDestruct.desc=Get killed by your own magic +achievement.PigTornado=Not Again... +achievement.PigTornado.desc=Ride a pig into a tornado +achievement.JamWizard=Jamming Session +achievement.JamWizard.desc=Use the arcane jammer spell on a wizard +achievement.SlimeSkeleton=Sticky Situation +achievement.SlimeSkeleton.desc=Engulf a skeleton in slime +achievement.AngerWizard=You'll Regret That +achievement.AngerWizard.desc=Make a wizard angry +achievement.DefeatEvilWizard=Righteousness +achievement.DefeatEvilWizard.desc=Defeat an evil wizard +achievement.MaxOutWand=Fully Equipped +achievement.MaxOutWand.desc=Apply the maximum number of upgrades to a master wand +achievement.ElementMaster=Element Mastery +achievement.ElementMaster.desc=Cast all the spells of any element +achievement.IdentifySpell=Arcane Appraisal +achievement.IdentifySpell.desc=Use a scroll of identification to identify a spell book or scroll -tile.wizardry:transportation_stone.confirm=You will now be returned here upon casting %1$s -tile.wizardry:transportation_stone.invalid=You must make a circle with 8 stones of transportation first! +tile.transportationStone.confirm=You will now be returned here upon casting %1$s +tile.transportationStone.invalid=You must make a circle with 8 stones of transportation first! -container.wizardry:arcane_workbench=Arcane Workbench -container.wizardry:arcane_workbench.apply=Apply -container.wizardry:arcane_workbench.mana=Mana: -container.wizardry:arcane_workbench.upgrades=Applied Upgrades: +container.arcaneWorkbench=Arcane Workbench +container.arcaneWorkbench.apply=Apply +container.arcaneWorkbench.mana=Mana: +container.arcaneWorkbench.upgrades=Applied Upgrades: tier.basic=Novice tier.apprentice=Apprentice @@ -301,438 +300,471 @@ spell.disabled=%1$s has been disabled in the config spell.resist=%1$s resisted %2$s spell.discover=Discovered the spell %1$s! -spell.wizardry:agility=Agility -spell.wizardry:arc=Arc -spell.wizardry:arcane_jammer=Arcane Jammer -spell.wizardry:arrow_rain=Arrow Rain -spell.wizardry:banish=Banish -spell.wizardry:black_hole=Black Hole -spell.wizardry:blink=Blink -spell.wizardry:blizzard=Blizzard -spell.wizardry:bubble=Bubble -spell.wizardry:chain_lightning=Chain Lightning -spell.wizardry:clairvoyance=Clairvoyance -spell.wizardry:cobwebs=Cobwebs -spell.wizardry:conjure_armour=Conjure Armour -spell.wizardry:conjure_bow=Conjure Bow -spell.wizardry:conjure_pickaxe=Conjure Pickaxe -spell.wizardry:conjure_sword=Conjure Sword -spell.wizardry:cure_effects=Cure Effects -spell.wizardry:curse_of_soulbinding=Curse of Soulbinding -spell.wizardry:darkness_orb=Darkness Orb -spell.wizardry:darkvision=Darkvision -spell.wizardry:dart=Dart -spell.wizardry:decay=Decay -spell.wizardry:decoy=Decoy -spell.wizardry:detonate=Detonate -spell.wizardry:diamondflesh=Diamondflesh -spell.wizardry:earthquake=Earthquake -spell.wizardry:entrapment=Entrapment -spell.wizardry:fireball=Fireball -spell.wizardry:firebolt=Firebolt -spell.wizardry:firebomb=Firebomb -spell.wizardry:fire_resistance=Fire Resistance -spell.wizardry:fire_sigil=Fire Sigil -spell.wizardry:fireskin=Fireskin -spell.wizardry:firestorm=Firestorm -spell.wizardry:flame_ray=Flame Ray -spell.wizardry:flaming_axe=Flaming Axe -spell.wizardry:flaming_weapon=Flaming Weapon -spell.wizardry:flight=Flight -spell.wizardry:font_of_mana=Font of Mana -spell.wizardry:font_of_vitality=Font of Vitality -spell.wizardry:force_arrow=Force Arrow -spell.wizardry:forcefield=Forcefield -spell.wizardry:force_orb=Force Orb -spell.wizardry:forests_curse=Forest's Curse -spell.wizardry:freeze=Freeze -spell.wizardry:freezing_weapon=Freezing Weapon -spell.wizardry:frost_axe=Frost Axe -spell.wizardry:frost_ray=Frost Ray -spell.wizardry:frost_sigil=Frost Sigil -spell.wizardry:glide=Glide -spell.wizardry:greater_fireball=Greater Fireball -spell.wizardry:greater_heal=Greater Heal -spell.wizardry:group_heal=Group Heal -spell.wizardry:growth_aura=Growth Aura -spell.wizardry:hailstorm=Hailstorm -spell.wizardry:heal=Heal -spell.wizardry:heal_ally=Heal Ally -spell.wizardry:healing_aura=Healing Aura -spell.wizardry:homing_spark=Homing Spark -spell.wizardry:ice_age=Ice Age -spell.wizardry:ice_charge=Ice Charge -spell.wizardry:ice_lance=Ice Lance -spell.wizardry:ice_shard=Ice Shard -spell.wizardry:ice_shroud=Ice Shroud -spell.wizardry:ice_spikes=Ice Spikes -spell.wizardry:ice_statue=Ice Statue -spell.wizardry:ignite=Ignite -spell.wizardry:imbue_weapon=Imbue Weapon -spell.wizardry:intimidate=Intimidate -spell.wizardry:invigorating_presence=Invigorating Presence -spell.wizardry:invisibility=Invisibility -spell.wizardry:invoke_weather=Invoke Weather -spell.wizardry:ironflesh=Ironflesh -spell.wizardry:leap=Leap -spell.wizardry:levitation=Levitation -spell.wizardry:life_drain=Life Drain -spell.wizardry:light=Light -spell.wizardry:lightning_arrow=Lightning Arrow -spell.wizardry:lightning_bolt=Lightning Bolt -spell.wizardry:lightning_disc=Lightning Disc -spell.wizardry:lightning_hammer=Lightning Hammer -spell.wizardry:lightning_pulse=Lightning Pulse -spell.wizardry:lightning_ray=Lightning Ray -spell.wizardry:lightning_sigil=Lightning Sigil -spell.wizardry:lightning_web=Lightning Web -spell.wizardry:magic_missile=Magic Missile -spell.wizardry:metamorphosis=Metamorphosis -spell.wizardry:meteor=Meteor -spell.wizardry:mind_control=Mind Control -spell.wizardry:mind_trick=Mind Trick -spell.wizardry:none=[Empty Slot] -spell.wizardry:oakflesh=Oakflesh -spell.wizardry:petrify=Petrify -spell.wizardry:phase_step=Phase Step -spell.wizardry:plague_of_darkness=Plague of Darkness -spell.wizardry:pocket_furnace=Pocket Furnace -spell.wizardry:pocket_workbench=Pocket Workbench -spell.wizardry:poison=Poison -spell.wizardry:poison_bomb=Poison Bomb -spell.wizardry:replenish_hunger=Replenish Hunger -spell.wizardry:ring_of_fire=Ring of Fire -spell.wizardry:shadow_ward=Shadow Ward -spell.wizardry:shield=Shield -spell.wizardry:shockwave=Shockwave -spell.wizardry:silverfish_swarm=Silverfish Swarm -spell.wizardry:sixth_sense=Sixth Sense -spell.wizardry:slime=Slime -spell.wizardry:smoke_bomb=Smoke Bomb -spell.wizardry:snare=Snare -spell.wizardry:snowball=Snowball -spell.wizardry:spark_bomb=Spark Bomb -spell.wizardry:spectral_pathway=Spectral Pathway -spell.wizardry:spider_swarm=Spider Swarm -spell.wizardry:static_aura=Static Aura -spell.wizardry:summon_blaze=Summon Blaze -spell.wizardry:summon_ice_giant=Summon Ice Giant -spell.wizardry:summon_ice_wraith=Summon Ice Wraith -spell.wizardry:summon_iron_golem=Summon Iron Golem -spell.wizardry:summon_lightning_wraith=Summon Lightning Wraith -spell.wizardry:summon_phoenix=Summon Phoenix -spell.wizardry:summon_shadow_wraith=Summon Shadow Wraith -spell.wizardry:summon_skeleton=Summon Skeleton -spell.wizardry:summon_skeleton_legion=Summon Skeleton Legion -spell.wizardry:summon_snow_golem=Summon Snow Golem -spell.wizardry:summon_spirit_horse=Summon Spirit Horse -spell.wizardry:summon_spirit_wolf=Summon Spirit Wolf -spell.wizardry:summon_storm_elemental=Summon Storm Elemental -spell.wizardry:summon_wither_skeleton=Summon Wither Skeleton -spell.wizardry:summon_zombie=Summon Zombie -spell.wizardry:telekinesis=Telekinesis -spell.wizardry:thunderbolt=Thunderbolt -spell.wizardry:thunderstorm=Thunderstorm -spell.wizardry:tornado=Tornado -spell.wizardry:transience=Transience -spell.wizardry:transportation=Transportation -spell.wizardry:vanishing_box=Vanishing Box -spell.wizardry:wall_of_frost=Wall of Frost -spell.wizardry:water_breathing=Water Breathing -spell.wizardry:whirlwind=Whirlwind -spell.wizardry:wither=Wither -spell.wizardry:wither_skull=Wither Skull +spell.agility=Agility +spell.arc=Arc +spell.arcane_jammer=Arcane Jammer +spell.arrow_rain=Arrow Rain +spell.banish=Banish +spell.black_hole=Black Hole +spell.blink=Blink +spell.blizzard=Blizzard +spell.bubble=Bubble +spell.chain_lightning=Chain Lightning +spell.clairvoyance=Clairvoyance +spell.cobwebs=Cobwebs +spell.conjure_armour=Conjure Armour +spell.conjure_bow=Conjure Bow +spell.conjure_pickaxe=Conjure Pickaxe +spell.conjure_sword=Conjure Sword +spell.cure_effects=Cure Effects +spell.curse_of_soulbinding=Curse of Soulbinding +spell.darkness_orb=Darkness Orb +spell.darkvision=Darkvision +spell.dart=Dart +spell.decay=Decay +spell.decoy=Decoy +spell.detonate=Detonate +spell.diamondflesh=Diamondflesh +spell.earthquake=Earthquake +spell.entrapment=Entrapment +spell.fireball=Fireball +spell.firebolt=Firebolt +spell.firebomb=Firebomb +spell.fire_resistance=Fire Resistance +spell.fire_sigil=Fire Sigil +spell.fireskin=Fireskin +spell.firestorm=Firestorm +spell.flame_ray=Flame Ray +spell.flaming_axe=Flaming Axe +spell.flaming_weapon=Flaming Weapon +spell.flight=Flight +spell.font_of_mana=Font of Mana +spell.font_of_vitality=Font of Vitality +spell.force_arrow=Force Arrow +spell.forcefield=Forcefield +spell.force_orb=Force Orb +spell.forests_curse=Forest's Curse +spell.freeze=Freeze +spell.freezing_weapon=Freezing Weapon +spell.frost_axe=Frost Axe +spell.frost_ray=Frost Ray +spell.frost_sigil=Frost Sigil +spell.glide=Glide +spell.greater_fireball=Greater Fireball +spell.greater_heal=Greater Heal +spell.group_heal=Group Heal +spell.growth_aura=Growth Aura +spell.hailstorm=Hailstorm +spell.heal=Heal +spell.heal_ally=Heal Ally +spell.healing_aura=Healing Aura +spell.homing_spark=Homing Spark +spell.ice_age=Ice Age +spell.ice_charge=Ice Charge +spell.ice_lance=Ice Lance +spell.ice_shard=Ice Shard +spell.ice_shroud=Ice Shroud +spell.ice_spikes=Ice Spikes +spell.ice_statue=Ice Statue +spell.ignite=Ignite +spell.imbue_weapon=Imbue Weapon +spell.intimidate=Intimidate +spell.invigorating_presence=Invigorating Presence +spell.invisibility=Invisibility +spell.invoke_weather=Invoke Weather +spell.ironflesh=Ironflesh +spell.leap=Leap +spell.levitation=Levitation +spell.life_drain=Life Drain +spell.light=Light +spell.lightning_arrow=Lightning Arrow +spell.lightning_bolt=Lightning Bolt +spell.lightning_disc=Lightning Disc +spell.lightning_hammer=Lightning Hammer +spell.lightning_pulse=Lightning Pulse +spell.lightning_ray=Lightning Ray +spell.lightning_sigil=Lightning Sigil +spell.lightning_web=Lightning Web +spell.magic_missile=Magic Missile +spell.metamorphosis=Metamorphosis +spell.meteor=Meteor +spell.mind_control=Mind Control +spell.mind_trick=Mind Trick +spell.none=[Empty Slot] +spell.oakflesh=Oakflesh +spell.petrify=Petrify +spell.phase_step=Phase Step +spell.plague_of_darkness=Plague of Darkness +spell.pocket_furnace=Pocket Furnace +spell.pocket_workbench=Pocket Workbench +spell.poison=Poison +spell.poison_bomb=Poison Bomb +spell.replenish_hunger=Replenish Hunger +spell.ring_of_fire=Ring of Fire +spell.shadow_ward=Shadow Ward +spell.shield=Shield +spell.shockwave=Shockwave +spell.silverfish_swarm=Silverfish Swarm +spell.sixth_sense=Sixth Sense +spell.slime=Slime +spell.smoke_bomb=Smoke Bomb +spell.snare=Snare +spell.snowball=Snowball +spell.spark_bomb=Spark Bomb +spell.spectral_pathway=Spectral Pathway +spell.spider_swarm=Spider Swarm +spell.static_aura=Static Aura +spell.summon_blaze=Summon Blaze +spell.summon_ice_giant=Summon Ice Giant +spell.summon_ice_wraith=Summon Ice Wraith +spell.summon_iron_golem=Summon Iron Golem +spell.summon_lightning_wraith=Summon Lightning Wraith +spell.summon_phoenix=Summon Phoenix +spell.summon_shadow_wraith=Summon Shadow Wraith +spell.summon_skeleton=Summon Skeleton +spell.summon_skeleton_legion=Summon Skeleton Legion +spell.summon_snow_golem=Summon Snow Golem +spell.summon_spirit_horse=Summon Spirit Horse +spell.summon_spirit_wolf=Summon Spirit Wolf +spell.summon_storm_elemental=Summon Storm Elemental +spell.summon_wither_skeleton=Summon Wither Skeleton +spell.summon_zombie=Summon Zombie +spell.telekinesis=Telekinesis +spell.thunderbolt=Thunderbolt +spell.thunderstorm=Thunderstorm +spell.tornado=Tornado +spell.transience=Transience +spell.transportation=Transportation +spell.vanishing_box=Vanishing Box +spell.wall_of_frost=Wall of Frost +spell.water_breathing=Water Breathing +spell.whirlwind=Whirlwind +spell.wither=Wither +spell.wither_skull=Wither Skull -spell.wizardry:agility.desc=Grants the caster faster movement speed and greater jump height for 30 seconds. -spell.wizardry:arc.desc=Fires a spark of lightning at the target. -spell.wizardry:arcane_jammer.desc=Prevents the target from using magic for 15 seconds. -spell.wizardry:arrow_rain.desc="Archers, fire!" -spell.wizardry:banish.desc=Teleports the target against its will to a random location within a certain range. -spell.wizardry:black_hole.desc=Tear reality asunder. -spell.wizardry:blink.desc=Teleports the caster over a short distance to where they are pointing. -spell.wizardry:blizzard.desc=Creates a zone of swirling icy wind which slows and continually damages anything trapped inside. The caster is immune to the damage but is still slowed. -spell.wizardry:bubble.desc=Fires a jet of bubbles which causes anything it hits to float upwards helplessly. The target will fall after a certain time or if it is damaged. -spell.wizardry:chain_lightning.desc=Fires a spark of lightning at the target, which then chains to additional targets up to twice. -spell.wizardry:clairvoyance.desc=Reveals the path to a remembered location. With this spell selected, sneak-right-click on a block to set the location. Cast this spell normally to reveal the path. The path will fade after 90 seconds. -spell.wizardry:cobwebs.desc=Creates cobwebs where you are pointing, which greatly hamper the movement of any creatures caught amongst them. The cobwebs will vanish after 20 seconds or if broken. -spell.wizardry:conjure_armour.desc=Creates spectral armour around the caster which offers protection equal to that of iron armour. The armour lasts for 60 seconds. The caster must have an empty armour slot. -spell.wizardry:conjure_bow.desc=Creates a spectral bow with unlimited arrows that lasts for 30 seconds. -spell.wizardry:conjure_pickaxe.desc=Creates a spectral pickaxe of equal strength to an iron pickaxe that lasts for 30 seconds. -spell.wizardry:conjure_sword.desc=Creates a spectral sword of equal strength to an iron sword that lasts for 30 seconds. -spell.wizardry:cure_effects.desc=Removes all potion effects currently affecting the caster, good or bad. -spell.wizardry:curse_of_soulbinding.desc=Causes the target's soul to be inextricably bound to that of the caster, meaning all damage dealt to the caster is also dealt to the victim. Lasts until either the victim or the caster dies. -spell.wizardry:darkness_orb.desc=Fires a slow moving bolt of dark energy in the direction you are pointing, which withers whatever it hits. -spell.wizardry:darkvision.desc=Grants the caster night vision for 45 seconds. -spell.wizardry:dart.desc=Fires a dart in the direction you are pointing which damages and weakens its target. -spell.wizardry:decay.desc=Creates a patch of decay on the ground which infects any creature that touches it, causing lingering damage over time and spreading more decay wherever it walks. -spell.wizardry:decoy.desc=Creates an illusory clone of the caster which tricks mobs into attacking it instead. The decoy will vanish after 30 seconds. -spell.wizardry:detonate.desc=Causes an explosion where you are pointing, damaging all nearby creatures - including the caster, if they are too close. -spell.wizardry:diamondflesh.desc="Your arrows are no match for me!" -spell.wizardry:earthquake.desc=A true master of earth magic can move mountains. -spell.wizardry:entrapment.desc=Traps the target in a sphere of darkness which pulls it helplessly upwards and continually damages it. -spell.wizardry:fireball.desc=Launches a fireball in the direction you are pointing. -spell.wizardry:firebolt.desc=Shoots a jet of fire a short distance in front of you. -spell.wizardry:firebomb.desc=Lanches a firebomb in the direction you are pointing which explodes on impact, setting targets on fire. -spell.wizardry:fire_resistance.desc=Grants the caster fire resistance for 30 seconds. -spell.wizardry:fire_sigil.desc=Places a magical fire trap on the ground which damages and sets on fire the creature that triggers it. -spell.wizardry:fireskin.desc=Cloaks the caster in flames for 30 seconds, causing anything that attacks them to catch fire. -spell.wizardry:firestorm.desc="I am the dragon." -spell.wizardry:flame_ray.desc=Creates a stream of flames in the direction you are pointing which sets on fire and continually damages targets. -spell.wizardry:flaming_axe.desc=Creates a flaming axe which sets enemies on fire when hit. Lasts for 30 seconds. -spell.wizardry:flaming_weapon.desc=Temporarily imbues the first weapon on the caster's hotbar with the power of flame, causing it to set fire to its victims. The magic wears off after 45 seconds. -spell.wizardry:flight.desc=Soar like an eagle. -spell.wizardry:font_of_mana.desc="We were filled with an intense magical energy appearing to emanate from the centre of the..." - Extract from the journal of a forgotten mage; the rest of the page has been burnt away. -spell.wizardry:font_of_vitality.desc=It feels amazing. -spell.wizardry:force_arrow.desc=Shoots an arrow of force in the direction you are pointing. -spell.wizardry:forcefield.desc=Creates a forcefield around the caster which repels creatures and deflects projectiles. -spell.wizardry:force_orb.desc=Launches a sphere of force which damages and repels nearby creatures on impact. -spell.wizardry:forests_curse.desc="How dare you enter my forest!" -spell.wizardry:freeze.desc=Freezes the target for 10 seconds. Will also freeze water and create snow on the ground. -spell.wizardry:freezing_weapon.desc=Temporarily imbues the first weapon on the caster's hotbar with the power of frost, causing it to freeze its victims. The magic wears off after 45 seconds. -spell.wizardry:frost_axe.desc=Creates a frozen axe which freezes enemies when hit. Lasts for 30 seconds. -spell.wizardry:frost_ray.desc=Creates a stream of frost in the direction you are pointing which slows and continually damages targets. -spell.wizardry:frost_sigil.desc=Places a magical ice trap on the ground which damages and freezes the creature that triggers it. -spell.wizardry:glide.desc=Allows the caster to glide downwards while in the air and holding the use item button. -spell.wizardry:greater_fireball.desc=Launches a large fireball in the direction you are pointing which explodes on impact. -spell.wizardry:greater_heal.desc=Heals the caster by 4 hearts. -spell.wizardry:group_heal.desc=Heals the caster and all nearby allies and summoned creatures by 3 hearts. -spell.wizardry:growth_aura.desc=Grows all crops near the caster. Also grows tall grass and flowers on grass. -spell.wizardry:hailstorm.desc=It was during the great winter of the third age that the ice mages discovered their true power. -spell.wizardry:heal.desc=Heals the caster by 2 hearts. -spell.wizardry:heal_ally.desc=Heals the target by 2 and a half hearts. -spell.wizardry:healing_aura.desc=Creates a zone of healing energy which regenerates the health of any ally inside it. Any undead inside the healing aura will slowly take damage. -spell.wizardry:homing_spark.desc=Creates a floating spark which moves towards enemies. -spell.wizardry:ice_age.desc="You shall be frozen for an eternity!" -spell.wizardry:ice_charge.desc=Launches an ice charge which explodes on impact, freezing nearby creatures and releasing shards in all directions. -spell.wizardry:ice_lance.desc=Fires a great spear of ice in the direction you are pointing which overpenetrates targets, damaging and freezing them in the process. -spell.wizardry:ice_shard.desc=Fires a shard of ice in the direction you are pointing which damages and slows targets when hit. -spell.wizardry:ice_shroud.desc=Creates a shroud of cold around the caster for 30 seconds, causing anything that attacks them to be frozen. -spell.wizardry:ice_spikes.desc=Causes razor-sharp ice spikes to rise from the ground where you are pointing, skewering any creatures caught amongst them. -spell.wizardry:ice_statue.desc=Freezes the target solid for 20 seconds or until broken out. The target cannot move or do anything while frozen but is also impervious to all damage. -spell.wizardry:ignite.desc=Sets the target on fire for 10 seconds. Also works like a flint and steel. -spell.wizardry:imbue_weapon.desc=Temporarily imbues the first weapon on the caster's hotbar with magic, rendering it more effective. The magic wears off after 45 seconds. -spell.wizardry:intimidate.desc=Emits an intimidating growl which causes nearby creatures to run away in fear. Fear stricken creatures will recover after 30 seconds. -spell.wizardry:invigorating_presence.desc=Grants the caster and all nearby allies increased strength for 45 seconds. -spell.wizardry:invisibility.desc=Makes the caster invisible for 30 seconds. -spell.wizardry:invoke_weather.desc=Changes the weather in the world. -spell.wizardry:ironflesh.desc=Greatly improves the caster's damage resistance for 30 seconds. -spell.wizardry:leap.desc=Causes the caster to jump upwards several blocks and slightly forward. -spell.wizardry:levitation.desc=Raises the caster upwards while the use item button is held. Will also negate fall damage if used before hitting the ground. -spell.wizardry:life_drain.desc=Creates a stream of withering energy in the direction you are pointing which drains the life of the target and uses it to gradually regenerate your health. -spell.wizardry:light.desc=Creates a magical point of light which illuminates the surrounding area. Lasts for 30 seconds. -spell.wizardry:lightning_arrow.desc=Shoots an arrow of lightning in the direction you are pointing. -spell.wizardry:lightning_bolt.desc=Causes lightning to strike where you are pointing. -spell.wizardry:lightning_disc.desc=Sends a disc of lightning flying off in the direction you are pointing, which seeks targets. -spell.wizardry:lightning_hammer.desc="I smite you by the wrath of the heavens!" -spell.wizardry:lightning_pulse.desc=Charges the ground around the caster with lightning, damaging and repelling nearby creatures. -spell.wizardry:lightning_ray.desc=Creates a stream of lightning in the direction you are pointing which continually damages targets. -spell.wizardry:lightning_sigil.desc=Places a magical lightning trap on the ground which damages the creature that triggers it and chains lightning to other nearby creatures. -spell.wizardry:lightning_web.desc="Focus. Channel the storm in your mind through your wand and unleash its fury." -spell.wizardry:magic_missile.desc=Fires a bolt of magical energy in the direction you are pointing. -spell.wizardry:metamorphosis.desc=Changes the target into another form. Only works on some creatures. -spell.wizardry:meteor.desc=Some wizards just want to see the world burn... -spell.wizardry:mind_control.desc=Takes control of the target's mind for 30 seconds, causing it switch sides and fight for the caster instead. Will not work on creatures that are too strong-willed. -spell.wizardry:mind_trick.desc=Confuses and disorients the target for 15 seconds, rendering it unable to attack effectively. The effect will be dispelled if the target takes damage. -spell.wizardry:none.desc=To get a spell book with the /give command, use metadata: /give [player] wizardry:spell_book 1 [spell id] (if you found this book in a chest, some other mod has messed things up). -spell.wizardry:oakflesh.desc=Improves the caster's damage resistance for 30 seconds. -spell.wizardry:petrify.desc=Turns the target to stone until broken out, with a chance for it to break out when it is dark. The target cannot move or do anything while petrified but is also impervious to all damage. -spell.wizardry:phase_step.desc=Teleports the caster through a 1 block thick wall in front of them. Range upgrades will increase the thickness you can teleport through. -spell.wizardry:plague_of_darkness.desc=The darkness will consume them all... -spell.wizardry:pocket_furnace.desc=Smelts up to 5 smeltable items in the caster's inventory. Items on the hotbar will be smelted first. -spell.wizardry:pocket_workbench.desc=Allows the caster to craft items as if they were at a crafting table. -spell.wizardry:poison.desc=Fires poison in the direction you are pointing. -spell.wizardry:poison_bomb.desc=Lanches a poison bomb in the direction you are pointing which explodes on impact, poisoning nearby creatures. -spell.wizardry:replenish_hunger.desc=Replenishes the caster's food level by 6 hunger points. -spell.wizardry:ring_of_fire.desc=Creates a ring of fire around the caster, damaging all nearby enemies and setting them on fire. -spell.wizardry:shadow_ward.desc=Creates a wall of darkness in front of the caster which causes half of all incoming damage to be inflicted upon the attacker instead. -spell.wizardry:shield.desc=Creates a protective barrier of force that blocks projectiles and magic. Also grants the caster a weak resistance effect. -spell.wizardry:shockwave.desc=Boom. -spell.wizardry:silverfish_swarm.desc="Ahhhh! They're MULTIPLYING!" -spell.wizardry:sixth_sense.desc=Allows the caster to sense the locations of nearby creatures, even through walls, for 20 seconds. -spell.wizardry:slime.desc=Engulfs the target in slime which slows and continually damages it. The slime bursts after 10 seconds. -spell.wizardry:smoke_bomb.desc=Launches a smoke bomb in the direction you are pointing which explodes on impact, releasing smoke and blinding nearby creatures for a short time. -spell.wizardry:snare.desc=Sets a trap on the ground which damages and briefly slows the creature that triggers it. -spell.wizardry:snowball.desc=Launches a snowball in the direction you are pointing. -spell.wizardry:spark_bomb.desc=Launches a shock charge in the direction you are pointing which releases sparks at nearby enemies on impact. -spell.wizardry:spectral_pathway.desc=Creates an indestructible magical bridge in front of you which extends for 15 blocks. The bridge vanishes after 60 seconds. -spell.wizardry:spider_swarm.desc=Summons a swarm of venomous spiders to fight for you. The spiders will disappear after 30 seconds or if they are killed. -spell.wizardry:static_aura.desc=Surrounds the caster with lightning for 30 seconds, firing a spark of lightning at anything that hits them. -spell.wizardry:summon_blaze.desc=Summons a blaze to fight for you. The blaze will disappear after 30 seconds or if it is killed. -spell.wizardry:summon_ice_giant.desc="Smash them!" -spell.wizardry:summon_ice_wraith.desc=Summons an ice wraith to fight for you. The ice wraith will disappear after 30 seconds or if it is killed. -spell.wizardry:summon_iron_golem.desc=Automatic automated autonomous automaton. -spell.wizardry:summon_lightning_wraith.desc=Summons a lightning wraith to fight for you. The lightning wraith will disappear after 30 seconds or if it is killed. -spell.wizardry:summon_phoenix.desc=From the ashes... -spell.wizardry:summon_shadow_wraith.desc=Summons a shadow wraith to fight for you. -spell.wizardry:summon_skeleton.desc=Summons a skeleton to fight for you. The skeleton will disappear after 30 seconds or if it is killed. -spell.wizardry:summon_skeleton_legion.desc="Rise, undead army!" -spell.wizardry:summon_snow_golem.desc=Creates a snow golem to fight for you. Lasts until the snow golem dies. -spell.wizardry:summon_spirit_horse.desc=Summons a spirit horse for you to ride. The spirit horse will vanish a short while after it is dismounted, or you can dismiss it by shift-right-clicking on it with any wand. -spell.wizardry:summon_spirit_wolf.desc=Summons a spirit wolf companion to fight for you. The spirit wolf will only disappear if it is killed, or you can dismiss it by shift-right-clicking on it with any wand. -spell.wizardry:summon_storm_elemental.desc="Storm Elemental: An ancient manifestation of the elements, it can hardly contain the raw power churning within it." - The Wizard's Guide to Arcane Beings, Volume I_i -spell.wizardry:summon_wither_skeleton.desc=Summons a wither skeleton to fight for you. The wither skeleton will disappear after 30 seconds or if it is killed. -spell.wizardry:summon_zombie.desc=Summons a zombie to fight for you. The zombie will disappear after 30 seconds or if it is killed. -spell.wizardry:telekinesis.desc=Moves an item or other small object towards you, or right-clicks the block you are looking at. Can also be used to disarm players. -spell.wizardry:thunderbolt.desc=Shoots a bolt of thunder which knocks back targets. -spell.wizardry:thunderstorm.desc="Mwahahahahahaha!" -spell.wizardry:tornado.desc=Unleashes a tornado in the direction you are pointing which hurls anything in its path skywards. -spell.wizardry:transience.desc=Makes the caster transient for 20 seconds. The caster is immune to all damage while transient but cannot break or place blocks or cause any damage. -spell.wizardry:transportation.desc=Transports the caster to their remembered stone circle. To use this spell, make a circle of stones of transportation, then right click it with a wand. -spell.wizardry:vanishing_box.desc=Grants the caster access to their ender chest storage. -spell.wizardry:wall_of_frost.desc=Winter at your fingertips. -spell.wizardry:water_breathing.desc=Allows the caster to breathe underwater for 60 seconds. -spell.wizardry:whirlwind.desc=Causes the target to be blown upwards and away from you at speed. -spell.wizardry:wither.desc=Fires a ray of darkness which withers anything it touches. -spell.wizardry:wither_skull.desc=Launches a wither skull in the direction you are pointing. +spell.agility.desc=Grants the caster faster movement speed and greater jump height for 30 seconds. +spell.arc.desc=Fires a spark of lightning at the target. +spell.arcane_jammer.desc=Prevents the target from using magic for 15 seconds. +spell.arrow_rain.desc="Archers, fire!" +spell.banish.desc=Teleports the target against its will to a random location within a certain range. +spell.black_hole.desc=Tear reality asunder. +spell.blink.desc=Teleports the caster over a short distance to where they are pointing. +spell.blizzard.desc=Creates a zone of swirling icy wind which slows and continually damages anything trapped inside. The caster is immune to the damage but is still slowed. +spell.bubble.desc=Fires a jet of bubbles which causes anything it hits to float upwards helplessly. The target will fall after a certain time or if it is damaged. +spell.chain_lightning.desc=Fires a spark of lightning at the target, which then chains to additional targets up to twice. +spell.clairvoyance.desc=Reveals the path to a remembered location. With this spell selected, sneak-right-click on a block to set the location. Cast this spell normally to reveal the path. The path will fade after 90 seconds. +spell.cobwebs.desc=Creates cobwebs where you are pointing, which greatly hamper the movement of any creatures caught amongst them. The cobwebs will vanish after 20 seconds or if broken. +spell.conjure_armour.desc=Creates spectral armour around the caster which offers protection equal to that of iron armour. The armour lasts for 60 seconds. The caster must have an empty armour slot. +spell.conjure_bow.desc=Creates a spectral bow with unlimited arrows that lasts for 30 seconds. +spell.conjure_pickaxe.desc=Creates a spectral pickaxe of equal strength to an iron pickaxe that lasts for 30 seconds. +spell.conjure_sword.desc=Creates a spectral sword of equal strength to an iron sword that lasts for 30 seconds. +spell.cure_effects.desc=Removes all potion effects currently affecting the caster, good or bad. +spell.curse_of_soulbinding.desc=Causes the target's soul to be inextricably bound to that of the caster, meaning all damage dealt to the caster is also dealt to the victim. Lasts until either the victim or the caster dies. +spell.darkness_orb.desc=Fires a slow moving bolt of dark energy in the direction you are pointing, which withers whatever it hits. +spell.darkvision.desc=Grants the caster night vision for 45 seconds. +spell.dart.desc=Fires a dart in the direction you are pointing which damages and weakens its target. +spell.decay.desc=Creates a patch of decay on the ground which infects any creature that touches it, causing lingering damage over time and spreading more decay wherever it walks. +spell.decoy.desc=Creates an illusory clone of the caster which tricks mobs into attacking it instead. The decoy will vanish after 30 seconds. +spell.detonate.desc=Causes an explosion where you are pointing, damaging all nearby creatures - including the caster, if they are too close. +spell.diamondflesh.desc="Your arrows are no match for me!" +spell.earthquake.desc=A true master of earth magic can move mountains. +spell.entrapment.desc=Traps the target in a sphere of darkness which pulls it helplessly upwards and continually damages it. +spell.fireball.desc=Launches a fireball in the direction you are pointing. +spell.firebolt.desc=Shoots a jet of fire a short distance in front of you. +spell.firebomb.desc=Lanches a firebomb in the direction you are pointing which explodes on impact, setting targets on fire. +spell.fire_resistance.desc=Grants the caster fire resistance for 30 seconds. +spell.fire_sigil.desc=Places a magical fire trap on the ground which damages and sets on fire the creature that triggers it. +spell.fireskin.desc=Cloaks the caster in flames for 30 seconds, causing anything that attacks them to catch fire. +spell.firestorm.desc="I am the dragon." +spell.flame_ray.desc=Creates a stream of flames in the direction you are pointing which sets on fire and continually damages targets. +spell.flaming_axe.desc=Creates a flaming axe which sets enemies on fire when hit. Lasts for 30 seconds. +spell.flaming_weapon.desc=Temporarily imbues the first weapon on the caster's hotbar with the power of flame, causing it to set fire to its victims. The magic wears off after 45 seconds. +spell.flight.desc=Soar like an eagle. +spell.font_of_mana.desc="We were filled with an intense magical energy appearing to emanate from the centre of the..." - Extract from the journal of a forgotten mage; the rest of the page has been burnt away. +spell.font_of_vitality.desc=It feels amazing. +spell.force_arrow.desc=Shoots an arrow of force in the direction you are pointing. +spell.forcefield.desc=Creates a forcefield around the caster which repels creatures and deflects projectiles. +spell.force_orb.desc=Launches a sphere of force which damages and repels nearby creatures on impact. +spell.forests_curse.desc="How dare you enter my forest!" +spell.freeze.desc=Freezes the target for 10 seconds. Will also freeze water and create snow on the ground. +spell.freezing_weapon.desc=Temporarily imbues the first weapon on the caster's hotbar with the power of frost, causing it to freeze its victims. The magic wears off after 45 seconds. +spell.frost_axe.desc=Creates a frozen axe which freezes enemies when hit. Lasts for 30 seconds. +spell.frost_ray.desc=Creates a stream of frost in the direction you are pointing which slows and continually damages targets. +spell.frost_sigil.desc=Places a magical ice trap on the ground which damages and freezes the creature that triggers it. +spell.glide.desc=Allows the caster to glide downwards while in the air and holding the use item button. +spell.greater_fireball.desc=Launches a large fireball in the direction you are pointing which explodes on impact. +spell.greater_heal.desc=Heals the caster by 4 hearts. +spell.group_heal.desc=Heals the caster and all nearby allies and summoned creatures by 3 hearts. +spell.growth_aura.desc=Grows all crops near the caster. Also grows tall grass and flowers on grass. +spell.hailstorm.desc=It was during the great winter of the third age that the ice mages discovered their true power. +spell.heal.desc=Heals the caster by 2 hearts. +spell.heal_ally.desc=Heals the target by 2 and a half hearts. +spell.healing_aura.desc=Creates a zone of healing energy which regenerates the health of any ally inside it. Any undead inside the healing aura will slowly take damage. +spell.homing_spark.desc=Creates a floating spark which moves towards enemies. +spell.ice_age.desc="You shall be frozen for an eternity!" +spell.ice_charge.desc=Launches an ice charge which explodes on impact, freezing nearby creatures and releasing shards in all directions. +spell.ice_lance.desc=Fires a great spear of ice in the direction you are pointing which overpenetrates targets, damaging and freezing them in the process. +spell.ice_shard.desc=Fires a shard of ice in the direction you are pointing which damages and slows targets when hit. +spell.ice_shroud.desc=Creates a shroud of cold around the caster for 30 seconds, causing anything that attacks them to be frozen. +spell.ice_spikes.desc=Causes razor-sharp ice spikes to rise from the ground where you are pointing, skewering any creatures caught amongst them. +spell.ice_statue.desc=Freezes the target solid for 20 seconds or until broken out. The target cannot move or do anything while frozen but is also impervious to all damage. +spell.ignite.desc=Sets the target on fire for 10 seconds. Also works like a flint and steel. +spell.imbue_weapon.desc=Temporarily imbues the first weapon on the caster's hotbar with magic, rendering it more effective. The magic wears off after 45 seconds. +spell.intimidate.desc=Emits an intimidating growl which causes nearby creatures to run away in fear. Fear stricken creatures will recover after 30 seconds. +spell.invigorating_presence.desc=Grants the caster and all nearby allies increased strength for 45 seconds. +spell.invisibility.desc=Makes the caster invisible for 30 seconds. +spell.invoke_weather.desc=Changes the weather in the world. +spell.ironflesh.desc=Greatly improves the caster's damage resistance for 30 seconds. +spell.leap.desc=Causes the caster to jump upwards several blocks and slightly forward. +spell.levitation.desc=Raises the caster upwards while the use item button is held. Will also negate fall damage if used before hitting the ground. +spell.life_drain.desc=Creates a stream of withering energy in the direction you are pointing which drains the life of the target and uses it to gradually regenerate your health. +spell.light.desc=Creates a magical point of light which illuminates the surrounding area. Lasts for 30 seconds. +spell.lightning_arrow.desc=Shoots an arrow of lightning in the direction you are pointing. +spell.lightning_bolt.desc=Causes lightning to strike where you are pointing. +spell.lightning_disc.desc=Sends a disc of lightning flying off in the direction you are pointing, which seeks targets. +spell.lightning_hammer.desc="I smite you by the wrath of the heavens!" +spell.lightning_pulse.desc=Charges the ground around the caster with lightning, damaging and repelling nearby creatures. +spell.lightning_ray.desc=Creates a stream of lightning in the direction you are pointing which continually damages targets. +spell.lightning_sigil.desc=Places a magical lightning trap on the ground which damages the creature that triggers it and chains lightning to other nearby creatures. +spell.lightning_web.desc="Focus. Channel the storm in your mind through your wand and unleash its fury." +spell.magic_missile.desc=Fires a bolt of magical energy in the direction you are pointing. +spell.metamorphosis.desc=Changes the target into another form. Only works on some creatures. +spell.meteor.desc=Some wizards just want to see the world burn... +spell.mind_control.desc=Takes control of the target's mind for 30 seconds, causing it switch sides and fight for the caster instead. Will not work on creatures that are too strong-willed. +spell.mind_trick.desc=Confuses and disorients the target for 15 seconds, rendering it unable to attack effectively. The effect will be dispelled if the target takes damage. +spell.none.desc=To get a spell book with the /give command, use metadata: /give [player] wizardry:spell_book 1 [spell id] (if you found this book in a chest, some other mod has messed things up). +spell.oakflesh.desc=Improves the caster's damage resistance for 30 seconds. +spell.petrify.desc=Turns the target to stone until broken out, with a chance for it to break out when it is dark. The target cannot move or do anything while petrified but is also impervious to all damage. +spell.phase_step.desc=Teleports the caster through a 1 block thick wall in front of them. Range upgrades will increase the thickness you can teleport through. +spell.plague_of_darkness.desc=The darkness will consume them all... +spell.pocket_furnace.desc=Smelts up to 5 smeltable items in the caster's inventory. Items on the hotbar will be smelted first. +spell.pocket_workbench.desc=Allows the caster to craft items as if they were at a crafting table. +spell.poison.desc=Fires poison in the direction you are pointing. +spell.poison_bomb.desc=Lanches a poison bomb in the direction you are pointing which explodes on impact, poisoning nearby creatures. +spell.replenish_hunger.desc=Replenishes the caster's food level by 6 hunger points. +spell.ring_of_fire.desc=Creates a ring of fire around the caster, damaging all nearby enemies and setting them on fire. +spell.shadow_ward.desc=Creates a wall of darkness in front of the caster which causes half of all incoming damage to be inflicted upon the attacker instead. +spell.shield.desc=Creates a protective barrier of force that blocks projectiles and magic. Also grants the caster a weak resistance effect. +spell.shockwave.desc=Boom. +spell.silverfish_swarm.desc="Ahhhh! They're MULTIPLYING!" +spell.sixth_sense.desc=Allows the caster to sense the locations of nearby creatures, even through walls, for 20 seconds. +spell.slime.desc=Engulfs the target in slime which slows and continually damages it. The slime bursts after 10 seconds. +spell.smoke_bomb.desc=Launches a smoke bomb in the direction you are pointing which explodes on impact, releasing smoke and blinding nearby creatures for a short time. +spell.snare.desc=Sets a trap on the ground which damages and briefly slows the creature that triggers it. +spell.snowball.desc=Launches a snowball in the direction you are pointing. +spell.spark_bomb.desc=Launches a shock charge in the direction you are pointing which releases sparks at nearby enemies on impact. +spell.spectral_pathway.desc=Creates an indestructible magical bridge in front of you which extends for 15 blocks. The bridge vanishes after 60 seconds. +spell.spider_swarm.desc=Summons a swarm of venomous spiders to fight for you. The spiders will disappear after 30 seconds or if they are killed. +spell.static_aura.desc=Surrounds the caster with lightning for 30 seconds, firing a spark of lightning at anything that hits them. +spell.summon_blaze.desc=Summons a blaze to fight for you. The blaze will disappear after 30 seconds or if it is killed. +spell.summon_ice_giant.desc="Smash them!" +spell.summon_ice_wraith.desc=Summons an ice wraith to fight for you. The ice wraith will disappear after 30 seconds or if it is killed. +spell.summon_iron_golem.desc=Automatic automated autonomous automaton. +spell.summon_lightning_wraith.desc=Summons a lightning wraith to fight for you. The lightning wraith will disappear after 30 seconds or if it is killed. +spell.summon_phoenix.desc=From the ashes... +spell.summon_shadow_wraith.desc=Summons a shadow wraith to fight for you. +spell.summon_skeleton.desc=Summons a skeleton to fight for you. The skeleton will disappear after 30 seconds or if it is killed. +spell.summon_skeleton_legion.desc="Rise, undead army!" +spell.summon_snow_golem.desc=Creates a snow golem to fight for you. Lasts until the snow golem dies. +spell.summon_spirit_horse.desc=Summons a spirit horse for you to ride. The spirit horse will vanish a short while after it is dismounted, or you can dismiss it by shift-right-clicking on it with any wand. +spell.summon_spirit_wolf.desc=Summons a spirit wolf companion to fight for you. The spirit wolf will only disappear if it is killed, or you can dismiss it by shift-right-clicking on it with any wand. +spell.summon_storm_elemental.desc="Storm Elemental: An ancient manifestation of the elements, it can hardly contain the raw power churning within it." - The Wizard's Guide to Arcane Beings, Volume II +spell.summon_wither_skeleton.desc=Summons a wither skeleton to fight for you. The wither skeleton will disappear after 30 seconds or if it is killed. +spell.summon_zombie.desc=Summons a zombie to fight for you. The zombie will disappear after 30 seconds or if it is killed. +spell.telekinesis.desc=Moves an item or other small object towards you, or right-clicks the block you are looking at. Can also be used to disarm players. +spell.thunderbolt.desc=Shoots a bolt of thunder which knocks back targets. +spell.thunderstorm.desc="Mwahahahahahaha!" +spell.tornado.desc=Unleashes a tornado in the direction you are pointing which hurls anything in its path skywards. +spell.transience.desc=Makes the caster transient for 20 seconds. The caster is immune to all damage while transient but cannot break or place blocks or cause any damage. +spell.transportation.desc=Transports the caster to their remembered stone circle. To use this spell, make a circle of stones of transportation, then right click it with a wand. +spell.vanishing_box.desc=Grants the caster access to their ender chest storage. +spell.wall_of_frost.desc=Winter at your fingertips. +spell.water_breathing.desc=Allows the caster to breathe underwater for 60 seconds. +spell.whirlwind.desc=Causes the target to be blown upwards and away from you at speed. +spell.wither.desc=Fires a ray of darkness which withers anything it touches. +spell.wither_skull.desc=Launches a wither skull in the direction you are pointing. -spell.wizardry:invoke_weather.sun=The rain begins to stop... -spell.wizardry:invoke_weather.rain=The heavens open... -spell.wizardry:transportation.missing=Your remembered stone circle is missing or obstructed... -spell.wizardry:transportation.undefined=You must remember the location of a stone circle first! -spell.wizardry:transportation.wrongdimension=Your remembered stone circle is in another dimension... -spell.wizardry:clairvoyance.searching=Searching... -spell.wizardry:clairvoyance.confirm=The path revealed upon casting %1$s will now lead back to this point -spell.wizardry:clairvoyance.outofrange=Your remembered location is too far away or inaccessible... -spell.wizardry:clairvoyance.undefined=You must remember a location first! -spell.wizardry:clairvoyance.wrongdimension=Your remembered location is in another dimension... +spell.invoke_weather.sun=The rain begins to stop... +spell.invoke_weather.rain=The heavens open... +spell.transportation.missing=Your remembered stone circle is missing or obstructed... +spell.transportation.undefined=You must remember the location of a stone circle first! +spell.transportation.wrongdimension=Your remembered stone circle is in another dimension... +spell.clairvoyance.searching=Searching... +spell.clairvoyance.confirm=The path revealed upon casting %1$s will now lead back to this point +spell.clairvoyance.outofrange=Your remembered location is too far away or inaccessible... +spell.clairvoyance.undefined=You must remember a location first! +spell.clairvoyance.wrongdimension=Your remembered location is in another dimension... -potion.wizardry:frost=Frostbite -potion.wizardry:fireskin=Fireskin -potion.wizardry:ice_shroud=Ice Shroud -potion.wizardry:static_aura=Static Aura -potion.wizardry:transience=Transience -potion.wizardry:decay=Decay -potion.wizardry:sixth_sense=Sixth Sense -potion.wizardry:arcane_jammer=Arcane Jammer -potion.wizardry:mind_trick=Mind Trick -potion.wizardry:mind_control=Mind Control -potion.wizardry:font_of_mana=Font of Mana -potion.wizardry:fear=Fear +potion.frost=Frostbite +potion.fireskin=Fireskin +potion.ice_shroud=Ice Shroud +potion.static_aura=Static Aura +potion.transience=Transience +potion.decay=Decay +potion.sixth_sense=Sixth Sense +potion.arcane_jammer=Arcane Jammer +potion.mind_trick=Mind Trick +potion.mind_control=Mind Control +potion.font_of_mana=Font of Mana +potion.fear=Fear -enchantment.wizardry:magic_sword=Imbuement -enchantment.wizardry:magic_bow=Imbuement -enchantment.wizardry:flaming_weapon=Fire Imbuement -enchantment.wizardry:freezing_weapon=Frost Imbuement +enchantment.magic_sword=Imbuement +enchantment.magic_bow=Imbuement +enchantment.flaming_weapon=Fire Imbuement +enchantment.freezing_weapon=Frost Imbuement key.categories.wizardry=Wizardry -key.wizardry.next_spell=Next Spell -key.wizardry.previous_spell=Previous Spell +key.next_spell=Next Spell +key.previous_spell=Previous Spell -death.attack.wizardry_magic=%1$s was killed by %2$s using magic -death.attack.indirect_wizardry_magic=%1$s was killed by %2$s using magic +death.attack.wizardryMagic=%1$s was killed by %2$s using magic +death.attack.indirectWizardryMagic=%1$s was killed by %2$s using magic -commands.wizardry:cast.usage=/%1$s [player] [damage multiplier] [range multiplier] [duration multiplier] [blast multiplier] -commands.wizardry:cast.success=Successfully cast %1$s -commands.wizardry:cast.success_continuous=Successfully cast %1$s; repeat the command to stop -commands.wizardry:cast.success_remote=Successfully cast %1$s as %2$s -commands.wizardry:cast.success_remote_continuous=Successfully cast %1$s as %2$s; repeat the command to stop -commands.wizardry:cast.fail=Unable to cast %1$s -commands.wizardry:cast.not_found=There is no such spell with ID %1$s -commands.wizardry:cast.tag_error=Data tag parsing failed: %s +commands.cast.usage=/%1$s [player] [damage multiplier] [range multiplier] [duration multiplier] [blast multiplier] +commands.cast.success=Successfully cast %1$s +commands.cast.success_continuous=Successfully cast %1$s; repeat the command to stop +commands.cast.success_remote=Successfully cast %1$s as %2$s +commands.cast.success_remote_continuous=Successfully cast %1$s as %2$s; repeat the command to stop +commands.cast.fail=Unable to cast %1$s +commands.cast.not_found=There is no such spell with ID %1$s -commands.wizardry:ally.usage=/%1$s [player] -commands.wizardry:ally.addally=%1$s has been added to %2$s's list of allies -commands.wizardry:ally.removeally=%1$s has been removed from %2$s's list of allies -commands.wizardry:ally.self=Players cannot be an ally of themselves! -commands.wizardry:ally.permission=You do not have permission to change other players' allies +commands.ally.usage=/%1$s [player] +commands.ally.addally=%1$s has been added to %2$s's list of allies +commands.ally.removeally=%1$s has been removed from %2$s's list of allies +commands.ally.self=Players cannot be an ally of themselves! +commands.ally.permission=You do not have permission to change other players' allies -commands.wizardry:allies.usage=/%1$s [player] -commands.wizardry:allies.list=Players allied to you: %1$s -commands.wizardry:allies.list_other=Players allied to %1$s: %2$s -commands.wizardry:allies.permission=You do not have permission to view other players' allies -commands.wizardry:allies.none=None +commands.allies.usage=/%1$s [player] +commands.allies.list=Players allied to you: %1$s +commands.allies.list_other=Players allied to %1$s: %2$s +commands.allies.permission=You do not have permission to view other players' allies +commands.allies.none=None -commands.wizardry:discoverspell.usage=/%1$s [player] -commands.wizardry:discoverspell.not_found=There is no such spell with ID %1$s -commands.wizardry:discoverspell.clear=Cleared all spell discovery data for %1$s -commands.wizardry:discoverspell.all=Added all spells to %1$s's spell discovery data -commands.wizardry:discoverspell.addspell=Added %1$s to %2$s's spell discovery data -commands.wizardry:discoverspell.removespell=Removed %1$s from %2$s's spell discovery data +commands.discoverspell.usage=/%1$s [player] +commands.discoverspell.not_found=There is no such spell with ID %1$s +commands.discoverspell.clear=Cleared all spell discovery data for %1$s +commands.discoverspell.all=Added all spells to %1$s's spell discovery data +commands.discoverspell.addspell=Added %1$s to %2$s's spell discovery data +commands.discoverspell.removespell=Removed %1$s from %2$s's spell discovery data -config.wizardry.title.general=Mod Options +config.title.general=Mod Options -config.wizardry.category.spells=Configure Spells -config.wizardry.category.spells.tooltip=Select which spells are enabled -config.wizardry.title.spells=Spell Configuration -config.wizardry.subtitle.spells=Set a spell to false to disable it. +config.category.spells=Configure Spells +config.category.spells.tooltip=Select which spells are enabled +config.title.spells=Spell Configuration +config.subtitle.spells=Set a spell to false to disable it. -config.wizardry.category.resistances=Configure Resistances -config.wizardry.category.resistances.tooltip=Configure which mobs are immune to different types of magic -config.wizardry.title.resistances=Resistance Configuration -config.wizardry.subtitle.resistances=See descriptions of individual options for more details. +config.category.resistances=Configure Resistances +config.category.resistances.tooltip=Configure which mobs are immune to different types of magic +config.title.resistances=Resistance Configuration +config.subtitle.resistances=See descriptions of individual options for more details. -config.wizardry.category.ids=Configure IDs -config.wizardry.category.ids.tooltip=Change the IDs used by wizardry -config.wizardry.title.ids=ID Configuration -config.wizardry.subtitle.ids=Change these IDs if they conflict with another mod. +config.category.ids=Configure IDs +config.category.ids.tooltip=Change the IDs used by wizardry +config.title.ids=ID Configuration +config.subtitle.ids=Change these IDs if they conflict with another mod. -config.wizardry.tower_rarity=Tower Rarity -config.wizardry.ore_dimensions=Ore Dimensions -config.wizardry.flower_dimensions=Flower Dimensions -config.wizardry.tower_dimensions=Tower Dimensions -config.wizardry.spell_book_drop_chance=Spell Book Drop Chance -config.wizardry.generate_loot=Generate Loot -config.wizardry.firebomb_is_craftable=Firebomb Is Craftable -config.wizardry.poison_bomb_is_craftable=Poison Bomb Is Craftable -config.wizardry.smoke_bomb_is_craftable=Smoke Bomb Is Craftable -config.wizardry.use_alternate_scroll_recipe=Use Alternate Scroll Recipe -config.wizardry.teleport_through_unbreakable_blocks=Teleport Through Unbreakable Blocks -config.wizardry.show_summoned_creature_names=Show Summoned Creature Names -config.wizardry.friendly_fire=Friendly Fire -config.wizardry.telekinetic_disarmament=Telekinetic Disarmament -config.wizardry.discovery_mode=Discovery Mode -config.wizardry.enable_shift_scrolling=Enable Shift-_scrolling -config.wizardry.minion_revenge_targeting=Minion Revenge Targeting -config.wizardry.player_damage_scaling=Player Damage Scaling Factor -config.wizardry.npc_damage_scaling=NPC Damage Scaling Factor -config.wizardry.cast_command_multiplier_limit=Cast Command Multiplier Limit -config.wizardry.summoned_creature_targets_whitelist=Summoned Creature Target Whitelist -config.wizardry.summoned_creature_targets_blacklist=Summoned Creature Target Blacklist -config.wizardry.spell_hud_position=Spell HUD Position -config.wizardry.cast_command_name=Cast Spell Command Name -config.wizardry.discoverspell_command_name=Discover Spell Command Name -config.wizardry.ally_command_name=Set Ally Command Name -config.wizardry.allies_command_name=View Allies Command Name +config.tower_rarity=Tower Rarity +config.ore_dimensions=Ore Dimensions +config.flower_dimensions=Flower Dimensions +config.tower_dimensions=Tower Dimensions +config.spell_book_drop_chance=Spell Book Drop Chance +config.generate_loot=Generate Loot +config.firebomb_is_craftable=Firebomb Is Craftable +config.poison_bomb_is_craftable=Poison Bomb Is Craftable +config.smoke_bomb_is_craftable=Smoke Bomb Is Craftable +config.use_alternate_scroll_recipe=Use Alternate Scroll Recipe +config.teleport_through_unbreakable_blocks=Teleport Through Unbreakable Blocks +config.show_summoned_creature_names=Show Summoned Creature Names +config.friendly_fire=Friendly Fire +config.telekinetic_disarmament=Telekinetic Disarmament +config.discovery_mode=Discovery Mode +config.enable_shift_scrolling=Enable Shift-Scrolling +config.minion_revenge_targeting=Minion Revenge Targeting +config.player_damage_scaling=Player Damage Scaling Factor +config.npc_damage_scaling=NPC Damage Scaling Factor +config.cast_command_multiplier_limit=Cast Command Multiplier Limit +config.summoned_creature_targets_whitelist=Summoned Creature Target Whitelist +config.summoned_creature_targets_blacklist=Summoned Creature Target Blacklist +config.spell_hud_position=Spell HUD Position +config.cast_command_name=Cast Spell Command Name +config.discoverspell_command_name=Discover Spell Command Name +config.ally_command_name=Set Ally Command Name +config.allies_command_name=View Allies Command Name -config.wizardry.mobs_immune_to_fire=Mobs Immune To Fire -config.wizardry.mobs_immune_to_ice=Mobs Immune To Ice -config.wizardry.mobs_immune_to_lightning=Mobs Immune To Lightning -config.wizardry.mobs_immune_to_wither=Mobs Immune To Wither -config.wizardry.mobs_immune_to_poison=Mobs Immune To Poison +config.mobs_immune_to_fire=Mobs Immune To Fire +config.mobs_immune_to_ice=Mobs Immune To Ice +config.mobs_immune_to_lightning=Mobs Immune To Lightning +config.mobs_immune_to_wither=Mobs Immune To Wither +config.mobs_immune_to_poison=Mobs Immune To Poison -config.wizardry.tower_rarity.tooltip=Rarity of wizard towers. Higher numbers are rarer. Set to 0 to disable wizard towers completely. -config.wizardry.ore_dimensions.tooltip=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! -config.wizardry.flower_dimensions.tooltip=List of dimension ids in which crystal flowers will generate. -config.wizardry.tower_dimensions.tooltip=List of dimension ids in which wizard towers will generate. -config.wizardry.spell_book_drop_chance.tooltip=The chance for mobs to drop a spell book when killed. The greater this number, the more often they will drop. Set to 0 to disable spell book drops. Set to 200 for guaranteed drops. -config.wizardry.generate_loot.tooltip=Whether to generate wizardry loot in dungeon chests. -config.wizardry.firebomb_is_craftable.tooltip=Whether firebombs can be crafted or not. -config.wizardry.poison_bomb_is_craftable.tooltip=Whether poison bombs can be crafted or not. -config.wizardry.smoke_bomb_is_craftable.tooltip=Whether smoke bombs can be crafted or not. -config.wizardry.use_alternate_scroll_recipe.tooltip=Whether to require a magic crystal in the shapeless crafting recipe for blank scrolls. Set to true if another mod adds a conflicting recipe. -config.wizardry.teleport_through_unbreakable_blocks.tooltip=Whether players are allowed to teleport through unbreakable blocks (e.g. bedrock) using the phase step spell. -config.wizardry.show_summoned_creature_names.tooltip=Whether to show summoned creatures' names and owners above their heads. -config.wizardry.friendly_fire.tooltip=Whether to allow players to damage their designated allies using magic. -config.wizardry.telekinetic_disarmament.tooltip=Whether to allow players to disarm other players using the telekinesis spell.wizardry: Set to false to prevent stealing of items. -config.wizardry.discovery_mode.tooltip=For those who like a sense of mystery! When set to true, spells you haven't cast yet will be unreadable until you cast them (on a per-world basis). Has no effect when in creative mode. Spells of identification will be unobtainable in survival mode if this is false. -config.wizardry.enable_shift_scrolling.tooltip=Whether you can switch between spells on a wand by scrolling with the mouse wheel while sneaking. Note that this will only affect you; other players connected to the same server obey their own settings. -config.wizardry.minion_revenge_targeting.tooltip=Whether summoned creatures can revenge attack their owner if their owner attacks them. -config.wizardry.player_damage_scaling.tooltip=Global damage scaling factor for the damage dealt by players casting spells, relative to 1. -config.wizardry.npc_damage_scaling.tooltip=Global damage scaling factor for the damage dealt by NPCs casting spells, relative to 1. -config.wizardry.cast_command_multiplier_limit.tooltip=Upper limit for the multipliers passed into the /cast command. This is here to stop players from accidentally breaking a world/server. Large blast mutipliers can cause extreme lag - you have been warned! -config.wizardry.summoned_creature_targets_whitelist.tooltip=List of names of entities which summoned creatures and wizards are allowed to attack, in addition to the defaults. Add mod creatures to this list if you want summoned creatures to attack them and they aren't already doing so. Entity names are not case sensitive. For mod entities, prefix with the mod ID (e.g. wizardry.wizard). -config.wizardry.summoned_creature_targets_blacklist.tooltip=List of names of entities which summoned creatures and wizards are specifically not allowed to attack, overriding the defaults and the whitelist. Add creatures to this list if allowing them to be attacked causes problems or is too destructive (removing creepers from this list is done at your own risk!). Entity names are not case sensitive. For mod entities, prefix with the mod ID (e.g. wizardry.wizard). -config.wizardry.spell_hud_position.tooltip=The position of the spell HUD. -config.wizardry.cast_command_name.tooltip=The name of the /cast command. This is what you type directly after the /; for example if this was set to 'magic' then instead of typing /cast you would type /magic instead. -config.wizardry.discoverspell_command_name.tooltip=The name of the /discoverspell command. This is what you type directly after the /; for example if this was set to 'magic' then instead of typing /discoverspell you would type /magic instead. -config.wizardry.ally_command_name.tooltip=The name of the /ally command. This is what you type directly after the /; for example if this was set to 'magic' then instead of typing /ally you would type /magic instead. -config.wizardry.allies_command_name.tooltip=The name of the /allies command. This is what you type directly after the /; for example if this was set to 'magic' then instead of typing /allies you would type /magic instead. +config.frost_potion_id=Frost Potion ID +config.transience_potion_id=Transience Potion ID +config.fireskin_potion_id=Fireskin Potion ID +config.ice_shroud_potion_id=Ice Shroud Potion ID +config.static_aura_potion_id=Static Aura Potion ID +config.decay_potion_id=Decay Potion ID +config.sixth_sense_potion_id=Sixth Sense Potion ID +config.arcane_jammer_potion_id=Arcane Jammer Potion ID +config.mind_trick_potion_id=Mind Trick Potion ID +config.mind_control_potion_id=Mind Control Potion ID +config.font_of_mana_potion_id=Font of Mana Potion ID +config.fear_potion_id=Fear Potion ID +config.magic_sword_enchantment_id=Magic Sword Enchantment ID +config.magic_bow_enchantment_id=Magic Bow Enchantment ID +config.flaming_weapon_enchantment_id=Flaming Weapon Enchantment ID +config.freezing_weapon_enchantment_id=Freezing Weapon Enchantment ID -config.wizardry.mobs_immune_to_fire.tooltip=List of names of entities that are immune to fire, in addition to the defaults. Add mod creatures to this list if you want them to be immune to fire magic and they aren't already. Entity names are not case sensitive. For mod entities, prefix with the mod ID (e.g. wizardry.wizard). -config.wizardry.mobs_immune_to_ice.tooltip=List of names of entities that are immune to ice, in addition to the defaults. Add mod creatures to this list if you want them to be immune to ice magic and they aren't already. Entity names are not case sensitive. For mod entities, prefix with the mod ID (e.g. wizardry.wizard). -config.wizardry.mobs_immune_to_lightning.tooltip=List of names of entities that are immune to lightning, in addition to the defaults. Add mod creatures to this list if you want them to be immune to lightning magic and they aren't already. Entity names are not case sensitive. For mod entities, prefix with the mod ID (e.g. wizardry.wizard). -config.wizardry.mobs_immune_to_wither.tooltip=List of names of entities that are immune to wither effects, in addition to the defaults. Add mod creatures to this list if you want them to be immune to withering magic and they aren't already. Entity names are not case sensitive. For mod entities, prefix with the mod ID (e.g. wizardry.wizard). -config.wizardry.mobs_immune_to_poison.tooltip=List of names of entities that are immune to poison, in addition to the defaults. Add mod creatures to this list if you want them to be immune to poison magic and they aren't already. Entity names are not case sensitive. For mod entities, prefix with the mod ID (e.g. wizardry.wizard). +config.tower_rarity.tooltip=Rarity of wizard towers. Higher numbers are rarer. Set to 0 to disable wizard towers completely. +config.ore_dimensions.tooltip=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! +config.flower_dimensions.tooltip=List of dimension ids in which crystal flowers will generate. +config.tower_dimensions.tooltip=List of dimension ids in which wizard towers will generate. +config.spell_book_drop_chance.tooltip=The chance for mobs to drop a spell book when killed. The greater this number, the more often they will drop. Set to 0 to disable spell book drops. Set to 200 for guaranteed drops. +config.generate_loot.tooltip=Whether to generate wizardry loot in dungeon chests. +config.firebomb_is_craftable.tooltip=Whether firebombs can be crafted or not. +config.poison_bomb_is_craftable.tooltip=Whether poison bombs can be crafted or not. +config.smoke_bomb_is_craftable.tooltip=Whether smoke bombs can be crafted or not. +config.use_alternate_scroll_recipe.tooltip=Whether to require a magic crystal in the shapeless crafting recipe for blank scrolls. Set to true if another mod adds a conflicting recipe. +config.teleport_through_unbreakable_blocks.tooltip=Whether players are allowed to teleport through unbreakable blocks (e.g. bedrock) using the phase step spell. +config.show_summoned_creature_names.tooltip=Whether to show summoned creatures' names and owners above their heads. +config.friendly_fire.tooltip=Whether to allow players to damage their designated allies using magic. +config.telekinetic_disarmament.tooltip=Whether to allow players to disarm other players using the telekinesis spell. Set to false to prevent stealing of items. +config.discovery_mode.tooltip=For those who like a sense of mystery! When set to true, spells you haven't cast yet will be unreadable until you cast them (on a per-world basis). Has no effect when in creative mode. Spells of identification will be unobtainable in survival mode if this is false. +config.enable_shift_scrolling.tooltip=Whether you can switch between spells on a wand by scrolling with the mouse wheel while sneaking. Note that this will only affect you; other players connected to the same server obey their own settings. +config.minion_revenge_targeting.tooltip=Whether summoned creatures can revenge attack their owner if their owner attacks them. +config.player_damage_scaling.tooltip=Global damage scaling factor for the damage dealt by players casting spells, relative to 1. +config.npc_damage_scaling.tooltip=Global damage scaling factor for the damage dealt by NPCs casting spells, relative to 1. +config.cast_command_multiplier_limit.tooltip=Upper limit for the multipliers passed into the /cast command. This is here to stop players from accidentally breaking a world/server. Large blast mutipliers can cause extreme lag - you have been warned! +config.summoned_creature_targets_whitelist.tooltip=List of names of entities which summoned creatures and wizards are allowed to attack, in addition to the defaults. Add mod creatures to this list if you want summoned creatures to attack them and they aren't already doing so. Entity names are not case sensitive. For mod entities, prefix with the mod ID (e.g. wizardry:Wizard). +config.summoned_creature_targets_blacklist.tooltip=List of names of entities which summoned creatures and wizards are specifically not allowed to attack, overriding the defaults and the whitelist. Add creatures to this list if allowing them to be attacked causes problems or is too destructive (removing creepers from this list is done at your own risk!). Entity names are not case sensitive. For mod entities, prefix with the mod ID (e.g. wizardry:Wizard). +config.spell_hud_position.tooltip=The position of the spell HUD. +config.cast_command_name.tooltip=The name of the /cast command. This is what you type directly after the /; for example if this was set to 'magic' then instead of typing /cast you would type /magic instead. +config.discoverspell_command_name.tooltip=The name of the /discoverspell command. This is what you type directly after the /; for example if this was set to 'magic' then instead of typing /discoverspell you would type /magic instead. +config.ally_command_name.tooltip=The name of the /ally command. This is what you type directly after the /; for example if this was set to 'magic' then instead of typing /ally you would type /magic instead. +config.allies_command_name.tooltip=The name of the /allies command. This is what you type directly after the /; for example if this was set to 'magic' then instead of typing /allies you would type /magic instead. + +config.mobs_immune_to_fire.tooltip=List of names of entities that are immune to fire, in addition to the defaults. Add mod creatures to this list if you want them to be immune to fire magic and they aren't already. Entity names are not case sensitive. For mod entities, prefix with the mod ID (e.g. wizardry:Wizard). +config.mobs_immune_to_ice.tooltip=List of names of entities that are immune to ice, in addition to the defaults. Add mod creatures to this list if you want them to be immune to ice magic and they aren't already. Entity names are not case sensitive. For mod entities, prefix with the mod ID (e.g. wizardry:Wizard). +config.mobs_immune_to_lightning.tooltip=List of names of entities that are immune to lightning, in addition to the defaults. Add mod creatures to this list if you want them to be immune to lightning magic and they aren't already. Entity names are not case sensitive. For mod entities, prefix with the mod ID (e.g. wizardry:Wizard). +config.mobs_immune_to_wither.tooltip=List of names of entities that are immune to wither effects, in addition to the defaults. Add mod creatures to this list if you want them to be immune to withering magic and they aren't already. Entity names are not case sensitive. For mod entities, prefix with the mod ID (e.g. wizardry:Wizard). +config.mobs_immune_to_poison.tooltip=List of names of entities that are immune to poison, in addition to the defaults. Add mod creatures to this list if you want them to be immune to poison magic and they aren't already. Entity names are not case sensitive. For mod entities, prefix with the mod ID (e.g. wizardry:Wizard). + +config.frost_potion_id.tooltip=The ID of the frost potion effect. Change if this conflicts with another mod. +config.transience_potion_id.tooltip=The ID of the transience potion effect. Change if this conflicts with another mod. +config.fireskin_potion_id.tooltip=The ID of the fireskin potion effect. Change if this conflicts with another mod. +config.ice_shroud_potion_id.tooltip=The ID of the ice shroud potion effect. Change if this conflicts with another mod. +config.static_aura_potion_id.tooltip=The ID of the static aura potion effect. Change if this conflicts with another mod. +config.decay_potion_id.tooltip=The ID of the decay potion effect. Change if this conflicts with another mod. +config.sixth_sense_potion_id.tooltip=The ID of the sixth sense potion effect. Change if this conflicts with another mod. +config.arcane_jammer_potion_id.tooltip=The ID of the arcane jammer potion effect. Change if this conflicts with another mod. +config.mind_trick_potion_id.tooltip=The ID of the mind trick potion effect. Change if this conflicts with another mod. +config.mind_control_potion_id.tooltip=The ID of the mind control potion effect. Change if this conflicts with another mod. +config.font_of_mana_potion_id.tooltip=The ID of the font of mana potion effect. Change if this conflicts with another mod. +config.fear_potion_id.tooltip=The ID of the fear potion effect. Change if this conflicts with another mod. +config.magic_sword_enchantment_id.tooltip=The ID of the magic sword enchantment. Change if this conflicts with another mod. +config.magic_bow_enchantment_id.tooltip=The ID of the magic bow enchantment. Change if this conflicts with another mod. +config.flaming_weapon_enchantment_id.tooltip=The ID of the flaming weapon enchantment. Change if this conflicts with another mod. +config.freezing_weapon_enchantment_id.tooltip=The ID of the freezing weapon enchantment. Change if this conflicts with another mod. wizard.debug=%1$s, %2$s, %3$s \ No newline at end of file diff --git a/src/main/resources/assets/wizardry/lang/en_US.lang b/src/main/resources/assets/wizardry/lang/en_US.lang index 286ae49a..cff0d0cd 100644 --- a/src/main/resources/assets/wizardry/lang/en_US.lang +++ b/src/main/resources/assets/wizardry/lang/en_US.lang @@ -1,273 +1,272 @@ -tile.wizardry:arcane_workbench.name=Arcane Workbench -tile.wizardry:crystal_ore.name=Crystal Ore -tile.wizardry:petrified_stone.name=Petrified Stone -tile.wizardry:ice_statue.name=Ice Statue -tile.wizardry:crystal_flower.name=Crystal Flower -tile.wizardry:snare.name=Snare -tile.wizardry:transportation_stone.name=Stone of Transportation -tile.wizardry:spectral_block.name=Spectral Block -tile.wizardry:crystal_block.name=Block of Crystal +tile.arcaneWorkbench.name=Arcane Workbench +tile.crystalOre.name=Crystal Ore +tile.petrifiedStone.name=Petrified Stone +tile.iceStatue.name=Ice Statue +tile.crystalFlower.name=Crystal Flower +tile.snare.name=Snare +tile.transportationStone.name=Stone of Transportation +tile.spectralBlock.name=Spectral Block +tile.crystalBlock.name=Block of Crystal -item.wizardry:magic_crystal.name=Magic Crystal -item.wizardry:magic_wand.name=Magic Wand -item.wizardry:apprentice_wand.name=Apprentice Wand -item.wizardry:advanced_wand.name=Advanced Wand -item.wizardry:master_wand.name=Master Wand -item.wizardry:spell_book.name=Spell Book +item.magicCrystal.name=Magic Crystal +item.magicWand.name=Magic Wand +item.apprenticeWand.name=Apprentice Wand +item.advancedWand.name=Advanced Wand +item.masterWand.name=Master Wand +item.spellBook.name=Spell Book -item.wizardry:arcane_tome.name=Tome of Arcana -item.wizardry:arcane_tome.desc1=Upgrades any %1$s -item.wizardry:arcane_tome.desc2=wand to %1$s tier +item.arcaneTome.name=Tome of Arcana +item.arcaneTome.desc1=Upgrades any %1$s +item.arcaneTome.desc2=wand to %1$s tier -item.wizardry:wizard_handbook.name=The Wizard's Handbook -item.wizardry:wizard_handbook.desc=by %1$s +item.wizardHandbook.name=The Wizard's Handbook +item.wizardHandbook.desc=by %1$s -item.wizardry:wand.buff=+%1$s %2$s potency -item.wizardry:wand.spell=Current Spell: %1$s -item.wizardry:wand.mana=Mana: %1$s/%2$s +item.wand.buff=+%1$s %2$s potency +item.wand.spell=Current Spell: %1$s +item.wand.mana=Mana: %1$s/%2$s -item.wizardry:wand.addally=%1$s has been added to your list of allies -item.wizardry:wand.removeally=%1$s has been removed from your list of allies +item.wand.addally=%1$s has been added to your list of allies +item.wand.removeally=%1$s has been removed from your list of allies -item.wizardry:basic_fire_wand.name=Wand of Embers -item.wizardry:basic_ice_wand.name=Wand of Frost -item.wizardry:basic_lightning_wand.name=Wand of Sparks -item.wizardry:basic_necromancy_wand.name=Wand of Shadows -item.wizardry:basic_earth_wand.name=Wand of the Forest -item.wizardry:basic_sorcery_wand.name=Wand of Mystery -item.wizardry:basic_healing_wand.name=Wand of Healing +item.basicFireWand.name=Wand of Embers +item.basicIceWand.name=Wand of Frost +item.basicLightningWand.name=Wand of Sparks +item.basicNecromancyWand.name=Wand of Shadows +item.basicEarthWand.name=Wand of the Forest +item.basicSorceryWand.name=Wand of Mystery +item.basicHealingWand.name=Wand of Healing -item.wizardry:apprentice_fire_wand.name=Apprentice Pyromancer Wand -item.wizardry:apprentice_ice_wand.name=Apprentice Ice Mage Wand -item.wizardry:apprentice_lightning_wand.name=Apprentice Storm Mage Wand -item.wizardry:apprentice_necromancy_wand.name=Apprentice Necromancer Wand -item.wizardry:apprentice_earth_wand.name=Apprentice Earth Mage Wand -item.wizardry:apprentice_sorcery_wand.name=Apprentice Sorcerer Wand -item.wizardry:apprentice_healing_wand.name=Apprentice Healer Wand +item.apprenticeFireWand.name=Apprentice Pyromancer Wand +item.apprenticeIceWand.name=Apprentice Ice Mage Wand +item.apprenticeLightningWand.name=Apprentice Storm Mage Wand +item.apprenticeNecromancyWand.name=Apprentice Necromancer Wand +item.apprenticeEarthWand.name=Apprentice Earth Mage Wand +item.apprenticeSorceryWand.name=Apprentice Sorcerer Wand +item.apprenticeHealingWand.name=Apprentice Healer Wand -item.wizardry:advanced_fire_wand.name=Wand of the Pyromancer -item.wizardry:advanced_ice_wand.name=Wand of the Ice Mage -item.wizardry:advanced_lightning_wand.name=Wand of the Storm Mage -item.wizardry:advanced_necromancy_wand.name=Wand of the Necromancer -item.wizardry:advanced_earth_wand.name=Wand of the Earth Mage -item.wizardry:advanced_sorcery_wand.name=Wand of the Sorcerer -item.wizardry:advanced_healing_wand.name=Wand of the Healer +item.advancedFireWand.name=Wand of the Pyromancer +item.advancedIceWand.name=Wand of the Ice Mage +item.advancedLightningWand.name=Wand of the Storm Mage +item.advancedNecromancyWand.name=Wand of the Necromancer +item.advancedEarthWand.name=Wand of the Earth Mage +item.advancedSorceryWand.name=Wand of the Sorcerer +item.advancedHealingWand.name=Wand of the Healer -item.wizardry:master_fire_wand.name=Master Pyromancer Wand -item.wizardry:master_ice_wand.name=Master Ice Mage Wand -item.wizardry:master_lightning_wand.name=Master Storm Mage Wand -item.wizardry:master_necromancy_wand.name=Master Necromancer Wand -item.wizardry:master_earth_wand.name=Master Earth Mage Wand -item.wizardry:master_sorcery_wand.name=Master Sorcerer Wand -item.wizardry:master_healing_wand.name=Master Healer Wand +item.masterFireWand.name=Master Pyromancer Wand +item.masterIceWand.name=Master Ice Mage Wand +item.masterLightningWand.name=Master Storm Mage Wand +item.masterNecromancyWand.name=Master Necromancer Wand +item.masterEarthWand.name=Master Earth Mage Wand +item.masterSorceryWand.name=Master Sorcerer Wand +item.masterHealingWand.name=Master Healer Wand -item.wizardry:spectral_sword.name=Spectral Sword -item.wizardry:spectral_pickaxe.name=Spectral Pickaxe -item.wizardry:spectral_bow.name=Spectral Bow +item.spectralSword.name=Spectral Sword +item.spectralPickaxe.name=Spectral Pickaxe +item.spectralBow.name=Spectral Bow -item.wizardry:mana_flask.name=Mana Flask -item.wizardry:storage_upgrade.name=Wand Storage Upgrade -item.wizardry:siphon_upgrade.name=Wand Siphon Upgrade -item.wizardry:condenser_upgrade.name=Wand Condenser Upgrade -item.wizardry:range_upgrade.name=Wand Range Upgrade -item.wizardry:duration_upgrade.name=Wand Duration Upgrade -item.wizardry:cooldown_upgrade.name=Wand Cooldown Upgrade -item.wizardry:blast_upgrade.name=Wand Blast Upgrade -item.wizardry:attunement_upgrade.name=Wand Attunement Upgrade +item.manaFlask.name=Mana Flask +item.storageUpgrade.name=Wand Storage Upgrade +item.siphonUpgrade.name=Wand Siphon Upgrade +item.condenserUpgrade.name=Wand Condenser Upgrade +item.rangeUpgrade.name=Wand Range Upgrade +item.durationUpgrade.name=Wand Duration Upgrade +item.cooldownUpgrade.name=Wand Cooldown Upgrade +item.blastUpgrade.name=Wand Blast Upgrade +item.attunementUpgrade.name=Wand Attunement Upgrade -item.wizardry:flaming_axe.name=Flaming Axe -item.wizardry:frost_axe.name=Frost Axe +item.flamingAxe.name=Flaming Axe +item.frostAxe.name=Frost Axe -item.wizardry:firebomb.name=Firebomb -item.wizardry:poison_bomb.name=Poison Bomb -item.wizardry:smoke_bomb.name=Smoke Bomb +item.firebomb.name=Firebomb +item.poisonBomb.name=Poison Bomb +item.smoke_bomb.name=Smoke Bomb -item.wizardry:blank_scroll.name=Blank Scroll -item.wizardry:scroll.name=Scroll of %1$s -item.wizardry:scroll.undiscovered.name=Scroll "%1$s" -item.wizardry:identification_scroll.name=Scroll of Identification -item.wizardry:identification_scroll.desc1=%1$sIdentifies an unknown -item.wizardry:identification_scroll.desc2=%1$sspell book or scroll -item.wizardry:identification_scroll.nothing_to_identify=Nothing to identify! +item.blankScroll.name=Blank Scroll +item.scroll.name=Scroll of %1$s +item.scroll.undiscovered.name=Scroll "%1$s" +item.identification_scroll.name=Scroll of Identification +item.identification_scroll.desc1=%1$sIdentifies an unknown +item.identification_scroll.desc2=%1$sspell book or scroll +item.identification_scroll.nothing_to_identify=Nothing to identify! -item.wizardry:armour_upgrade.name=Arcane Seal of Protection -item.wizardry:armour_upgrade.desc1=%1$sUpgrades any wizard armour -item.wizardry:armour_upgrade.desc2=%1$sto make it %2$slegendary +item.armourUpgrade.name=Arcane Seal of Protection +item.armourUpgrade.desc1=%1$sUpgrades any wizard armour +item.armourUpgrade.desc2=%1$sto make it %2$slegendary -item.wizardry:magic_silk.name=Magical Silk +item.magicSilk.name=Magical Silk -item.wizardry:wizard_armour.legendary=Legendary -item.wizardry:wizard_armour.buff=-%1$s %2$s cost -item.wizardry:wizard_armour.mana=Mana: %1$s/%2$s +item.wizardArmour.legendary=Legendary +item.wizardArmour.buff=-%1$s %2$s cost +item.wizardArmour.mana=Mana: %1$s/%2$s -item.wizardry:wizard_hat.name=Wizard Hat -item.wizardry:wizard_robe.name=Wizard Robes -item.wizardry:wizard_leggings.name=Wizard Leggings -item.wizardry:wizard_boots.name=Wizard Boots +item.wizard_hat.name=Wizard Hat +item.wizard_robe.name=Wizard Robes +item.wizard_leggings.name=Wizard Leggings +item.wizard_boots.name=Wizard Boots -item.wizardry:wizard_hat_fire.name=Pyromancer Hat -item.wizardry:wizard_robe_fire.name=Pyromancer Robes -item.wizardry:wizard_leggings_fire.name=Pyromancer Leggings -item.wizardry:wizard_boots_fire.name=Pyromancer Boots +item.wizard_hat_fire.name=Pyromancer Hat +item.wizard_robe_fire.name=Pyromancer Robes +item.wizard_leggings_fire.name=Pyromancer Leggings +item.wizard_boots_fire.name=Pyromancer Boots -item.wizardry:wizard_hat_ice.name=Ice Mage Hat -item.wizardry:wizard_robe_ice.name=Ice Mage Robes -item.wizardry:wizard_leggings_ice.name=Ice Mage Leggings -item.wizardry:wizard_boots_ice.name=Ice Mage Boots +item.wizard_hat_ice.name=Ice Mage Hat +item.wizard_robe_ice.name=Ice Mage Robes +item.wizard_leggings_ice.name=Ice Mage Leggings +item.wizard_boots_ice.name=Ice Mage Boots -item.wizardry:wizard_hat_lightning.name=Storm Mage Hat -item.wizardry:wizard_robe_lightning.name=Storm Mage Robes -item.wizardry:wizard_leggings_lightning.name=Storm Mage Leggings -item.wizardry:wizard_boots_lightning.name=Storm Mage Boots +item.wizard_hat_lightning.name=Storm Mage Hat +item.wizard_robe_lightning.name=Storm Mage Robes +item.wizard_leggings_lightning.name=Storm Mage Leggings +item.wizard_boots_lightning.name=Storm Mage Boots -item.wizardry:wizard_hat_necromancy.name=Necromancer Hat -item.wizardry:wizard_robe_necromancy.name=Necromancer Robes -item.wizardry:wizard_leggings_necromancy.name=Necromancer Leggings -item.wizardry:wizard_boots_necromancy.name=Necromancer Boots +item.wizard_hat_necromancy.name=Necromancer Hat +item.wizard_robe_necromancy.name=Necromancer Robes +item.wizard_leggings_necromancy.name=Necromancer Leggings +item.wizard_boots_necromancy.name=Necromancer Boots -item.wizardry:wizard_hat_earth.name=Earth Mage Hat -item.wizardry:wizard_robe_earth.name=Earth Mage Robes -item.wizardry:wizard_leggings_earth.name=Earth Mage Leggings -item.wizardry:wizard_boots_earth.name=Earth Mage Boots +item.wizard_hat_earth.name=Earth Mage Hat +item.wizard_robe_earth.name=Earth Mage Robes +item.wizard_leggings_earth.name=Earth Mage Leggings +item.wizard_boots_earth.name=Earth Mage Boots -item.wizardry:wizard_hat_sorcery.name=Sorcerer Hat -item.wizardry:wizard_robe_sorcery.name=Sorcerer Robes -item.wizardry:wizard_leggings_sorcery.name=Sorcerer Leggings -item.wizardry:wizard_boots_sorcery.name=Sorcerer Boots +item.wizard_hat_sorcery.name=Sorcerer Hat +item.wizard_robe_sorcery.name=Sorcerer Robes +item.wizard_leggings_sorcery.name=Sorcerer Leggings +item.wizard_boots_sorcery.name=Sorcerer Boots -item.wizardry:wizard_hat_healing.name=Healer Hat -item.wizardry:wizard_robe_healing.name=Healer Robes -item.wizardry:wizard_leggings_healing.name=Healer Leggings -item.wizardry:wizard_boots_healing.name=Healer Boots +item.wizard_hat_healing.name=Healer Hat +item.wizard_robe_healing.name=Healer Robes +item.wizard_leggings_healing.name=Healer Leggings +item.wizard_boots_healing.name=Healer Boots -item.wizardry:spawn_wizard.name=Spawn Wizard -item.wizardry:spawn_evil_wizard.name=Spawn Evil Wizard +item.spawn_wizard.name=Spawn Wizard +item.spawn_evil_wizard.name=Spawn Evil Wizard -item.wizardry:spectral_helmet.name=Spectral Helmet -item.wizardry:spectral_chestplate.name=Spectral Chestplate -item.wizardry:spectral_leggings.name=Spectral Leggings -item.wizardry:spectral_boots.name=Spectral Boots +item.spectral_helmet.name=Spectral Helmet +item.spectral_chestplate.name=Spectral Chestplate +item.spectral_leggings.name=Spectral Leggings +item.spectral_boots.name=Spectral Boots entity.wizardry.summonedcreature.nameplate=%1$s's %2$s -entity.wizardry.summonedcreature.nameplate_fallback=Someone's %1$s -entity.wizardry.zombie_minion.name=Zombie -entity.wizardry.skeleton_minion.name=Skeleton -entity.wizardry.spider_minion.name=Spider -entity.wizardry.blaze_minion.name=Blaze -entity.wizardry.ice_wraith.name=Ice Wraith -entity.wizardry.lightning_wraith.name=Lightning Wraith -entity.wizardry.shadow_wraith.name=Shadow Wraith -entity.wizardry.spirit_wolf.name=Spirit Wolf -entity.wizardry.spirit_horse.name=Spirit Horse -entity.wizardry.ice_giant.name=Ice Giant -entity.wizardry.phoenix.name=Phoenix -entity.wizardry.wizard.name=Wizard -entity.wizardry.magic_slime.name=Magical Slime -entity.wizardry.silverfish_minion.name=Silverfish -entity.wizardry.storm_elemental.name=Storm Elemental -entity.wizardry.evil_wizard.name=Wizard -entity.wizardry.decoy.name=Decoy +entity.wizardry.Zombie Minion.name=Zombie +entity.wizardry.Skeleton Minion.name=Skeleton +entity.wizardry.Spider Minion.name=Spider +entity.wizardry.Blaze Minion.name=Blaze +entity.wizardry.Ice Wraith.name=Ice Wraith +entity.wizardry.Lightning Wraith.name=Lightning Wraith +entity.wizardry.Shadow Wraith.name=Shadow Wraith +entity.wizardry.Spirit Wolf.name=Spirit Wolf +entity.wizardry.Spirit Horse.name=Spirit Horse +entity.wizardry.Ice Giant.name=Ice Giant +entity.wizardry.Phoenix.name=Phoenix +entity.wizardry.Wizard.name=Wizard +entity.wizardry.Magic Slime.name=Magical Slime +entity.wizardry.Silverfish Minion.name=Silverfish +entity.wizardry.Storm Elemental.name=Storm Elemental +entity.wizardry.Evil Wizard.name=Wizard +entity.wizardry.Decoy.name=Decoy -entity.wizardry.magic_missile.name=Magic -entity.wizardry.arc.name=Magic -entity.wizardry.spark_bomb.name=Magic -entity.wizardry.ice_shard.name=Magic -entity.wizardry.firebomb.name=Magic -entity.wizardry.poison_bomb.name=Magic -entity.wizardry.force_orb.name=Magic -entity.wizardry.spark.name=Magic -entity.wizardry.darkness_orb.name=Magic -entity.wizardry.fire_sigil.name=Magic -entity.wizardry.frost_sigil.name=Magic -entity.wizardry.lightning_sigil.name=Magic -entity.wizardry.lightning_arrow.name=Magic -entity.wizardry.firebolt.name=Magic -entity.wizardry.ice_charge.name=Magic -entity.wizardry.force_arrow.name=Magic -entity.wizardry.dart.name=Magic -entity.wizardry.lightning_disc.name=Magic -entity.wizardry.thunderbolt.name=Magic -entity.wizardry.decay.name=Magic -entity.wizardry.ice_lance.name=Magic -entity.wizardry.smoke_bomb.name=Magic -entity.wizardry.ice_spike.name=Magic +entity.wizardry.Magic Missile.name=Magic +entity.wizardry.Arc.name=Magic +entity.wizardry.Spark Bomb.name=Magic +entity.wizardry.Ice Shard.name=Magic +entity.wizardry.Firebomb.name=Magic +entity.wizardry.Poison Bomb.name=Magic +entity.wizardry.Force Orb.name=Magic +entity.wizardry.Spark.name=Magic +entity.wizardry.Darkness Orb.name=Magic +entity.wizardry.Fire Sigil.name=Magic +entity.wizardry.Frost Sigil.name=Magic +entity.wizardry.Lightning Sigil.name=Magic +entity.wizardry.Lightning Arrow.name=Magic +entity.wizardry.Firebolt.name=Magic +entity.wizardry.Ice Charge.name=Magic +entity.wizardry.Force Arrow.name=Magic +entity.wizardry.Dart.name=Magic +entity.wizardry.Lightning Disc.name=Magic +entity.wizardry.Thunderbolt.name=Magic +entity.wizardry.Decay.name=Magic +entity.wizardry.Ice Lance.name=Magic +entity.wizardry.Smoke Bomb.name=Magic +entity.wizardry.Ice Spike.name=Magic -entity.wizardry.black_hole.name=Black Hole -entity.wizardry.shield.name=Shield -entity.wizardry.meteor.name=Meteor -entity.wizardry.blizzard.name=Blizzard -entity.wizardry.bubble.name=Bubble -entity.wizardry.tornado.name=Tornado -entity.wizardry.lightning_hammer.name=Lightning Hammer -entity.wizardry.arrow_rain.name=Arrow Rain -entity.wizardry.healing_aura.name=Healing Aura -entity.wizardry.forcefield.name=Forcefield -entity.wizardry.ring_of_fire.name=Ring of Fire -entity.wizardry.earthquake.name=Earthquake -entity.wizardry.falling_grass.name=Falling Grass -entity.wizardry.hailstorm.name=Hailstorm -entity.wizardry.lightning_pulse.name=Lightning Pulse +entity.wizardry.Black Hole.name=Black Hole +entity.wizardry.Shield.name=Shield +entity.wizardry.Meteor.name=Meteor +entity.wizardry.Blizzard.name=Blizzard +entity.wizardry.Bubble.name=Bubble +entity.wizardry.Tornado.name=Tornado +entity.wizardry.Lightning Hammer.name=Lightning Hammer +entity.wizardry.Arrow Rain.name=Arrow Rain +entity.wizardry.Healing Aura.name=Healing Aura +entity.wizardry.Forcefield.name=Forcefield +entity.wizardry.Ring of Fire.name=Ring of Fire +entity.wizardry.Earthquake.name=Earthquake +entity.wizardry.Falling Grass.name=Falling Grass +entity.wizardry.Hailstorm.name=Hailstorm +entity.wizardry.Lightning Pulse.name=Lightning Pulse itemGroup.wizardry=Wizardry -itemGroup.wizardryspells=Spells +itemGroup.wizardryspells=Wizardry Spells -achievement.crystal=A Curious Crystal... -achievement.crystal.desc=Mine a magic crystal -achievement.arcane_initiate=Arcane Initiate -achievement.arcane_initiate.desc=Craft a magic wand with a gold nugget, a stick and a magic crystal -achievement.apprentice=Wizard's Apprentice -achievement.apprentice.desc=Use a tome of arcana to upgrade your wand -achievement.master=Arcane Master -achievement.master.desc=Obtain a master wand -achievement.all_spells=Mage of All Trades -achievement.all_spells.desc=Cast every single spell in the game -achievement.wizard_trade=Magic Dealing -achievement.wizard_trade.desc=Purchase an item from a wizard -achievement.buy_master_spell=Knowledge is Power -achievement.buy_master_spell.wizardry:desc=Purchase a master spell from a wizard -achievement.freeze_blaze=Not So Hot Now -achievement.freeze_blaze.desc=Freeze a blaze solid -achievement.charge_creeper=It's Gonna Blow -achievement.charge_creeper.desc='Accidentally' charge a creeper -achievement.frankenstein=Frankenstein -achievement.frankenstein.desc=Turn a pig into a zombie pigman using the lightning bolt spell -achievement.special_upgrade=Arcane Tinkering -achievement.special_upgrade.desc=Apply a special upgrade to a wand -achievement.craft_flask=It's Magic, Bottled! -achievement.craft_flask.desc=Craft a mana flask -achievement.elemental=Elemental -achievement.elemental.desc=Obtain an elemental wand -achievement.armour_set=Now You're a Proper Wizard -achievement.armour_set.desc=Craft and equip a full set of wizard armor -achievement.legendary=Legendary -achievement.legendary.desc=Obtain a piece of legendary wizard armor -achievement.self_destruct=That Backfired -achievement.self_destruct.desc=Get killed by your own magic -achievement.pig_tornado=Not Again... -achievement.pig_tornado.desc=Ride a pig into a tornado -achievement.jam_wizard=Jamming Session -achievement.jam_wizard.desc=Use the arcane jammer spell on a wizard -achievement.slime_skeleton=Sticky Situation -achievement.slime_skeleton.desc=Engulf a skeleton in slime -achievement.anger_wizard=You'll Regret That -achievement.anger_wizard.desc=Make a wizard angry -achievement.defeat_evil_wizard=Righteousness -achievement.defeat_evil_wizard.desc=Defeat an evil wizard -achievement.max_out_wand=Fully Equipped -achievement.max_out_wand.desc=Apply the maximum number of upgrades to a master wand -achievement.element_master=Element Mastery -achievement.element_master.desc=Cast all the spells of any element -achievement.identify_spell=Arcane Appraisal -achievement.identify_spell.wizardry:desc=Use a scroll of identification to identify a spell book or scroll +achievement.Crystal=A Curious Crystal... +achievement.Crystal.desc=Mine a magic crystal +achievement.ArcaneInitiate=Arcane Initiate +achievement.ArcaneInitiate.desc=Craft a magic wand with a gold nugget, a stick and a magic crystal +achievement.Apprentice=Wizard's Apprentice +achievement.Apprentice.desc=Use a tome of arcana to upgrade your wand +achievement.Master=Arcane Master +achievement.Master.desc=Obtain a master wand +achievement.AllSpells=Mage of All Trades +achievement.AllSpells.desc=Cast every single spell in the game +achievement.WizardTrade=Magic Dealing +achievement.WizardTrade.desc=Purchase an item from a wizard +achievement.BuyMasterSpell=Knowledge is Power +achievement.BuyMasterSpell.desc=Purchase a master spell from a wizard +achievement.FreezeBlaze=Not So Hot Now +achievement.FreezeBlaze.desc=Freeze a blaze solid +achievement.ChargeCreeper=It's Gonna Blow +achievement.ChargeCreeper.desc='Accidentally' charge a creeper +achievement.Frankenstein=Frankenstein +achievement.Frankenstein.desc=Turn a pig into a zombie pigman using the lightning bolt spell +achievement.SpecialUpgrade=Arcane Tinkering +achievement.SpecialUpgrade.desc=Apply a special upgrade to a wand +achievement.CraftFlask=It's Magic, Bottled! +achievement.CraftFlask.desc=Craft a mana flask +achievement.Elemental=Elemental +achievement.Elemental.desc=Obtain an elemental wand +achievement.ArmourSet=Now You're a Proper Wizard +achievement.ArmourSet.desc=Craft and equip a full set of wizard armor +achievement.Legendary=Legendary +achievement.Legendary.desc=Obtain a piece of legendary wizard armor +achievement.SelfDestruct=That Backfired +achievement.SelfDestruct.desc=Get killed by your own magic +achievement.PigTornado=Not Again... +achievement.PigTornado.desc=Ride a pig into a tornado +achievement.JamWizard=Jamming Session +achievement.JamWizard.desc=Use the arcane jammer spell on a wizard +achievement.SlimeSkeleton=Sticky Situation +achievement.SlimeSkeleton.desc=Engulf a skeleton in slime +achievement.AngerWizard=You'll Regret That +achievement.AngerWizard.desc=Make a wizard angry +achievement.DefeatEvilWizard=Righteousness +achievement.DefeatEvilWizard.desc=Defeat an evil wizard +achievement.MaxOutWand=Fully Equipped +achievement.MaxOutWand.desc=Apply the maximum number of upgrades to a master wand +achievement.ElementMaster=Element Mastery +achievement.ElementMaster.desc=Cast all the spells of any element +achievement.IdentifySpell=Arcane Appraisal +achievement.IdentifySpell.desc=Use a scroll of identification to identify a spell book or scroll -tile.wizardry:transportation_stone.confirm=You will now be returned here upon casting %1$s -tile.wizardry:transportation_stone.invalid=You must make a circle with 8 stones of transportation first! +tile.transportationStone.confirm=You will now be returned here upon casting %1$s +tile.transportationStone.invalid=You must make a circle with 8 stones of transportation first! -container.wizardry:arcane_workbench=Arcane Workbench -container.wizardry:arcane_workbench.apply=Apply -container.wizardry:arcane_workbench.mana=Mana: -container.wizardry:arcane_workbench.upgrades=Applied Upgrades: +container.arcaneWorkbench=Arcane Workbench +container.arcaneWorkbench.apply=Apply +container.arcaneWorkbench.mana=Mana: +container.arcaneWorkbench.upgrades=Applied Upgrades: tier.basic=Novice tier.apprentice=Apprentice @@ -301,438 +300,471 @@ spell.disabled=%1$s has been disabled in the config spell.resist=%1$s resisted %2$s spell.discover=Discovered the spell %1$s! -spell.wizardry:agility=Agility -spell.wizardry:arc=Arc -spell.wizardry:arcane_jammer=Arcane Jammer -spell.wizardry:arrow_rain=Arrow Rain -spell.wizardry:banish=Banish -spell.wizardry:black_hole=Black Hole -spell.wizardry:blink=Blink -spell.wizardry:blizzard=Blizzard -spell.wizardry:bubble=Bubble -spell.wizardry:chain_lightning=Chain Lightning -spell.wizardry:clairvoyance=Clairvoyance -spell.wizardry:cobwebs=Cobwebs -spell.wizardry:conjure_armour=Conjure Armor -spell.wizardry:conjure_bow=Conjure Bow -spell.wizardry:conjure_pickaxe=Conjure Pickaxe -spell.wizardry:conjure_sword=Conjure Sword -spell.wizardry:cure_effects=Cure Effects -spell.wizardry:curse_of_soulbinding=Curse of Soulbinding -spell.wizardry:darkness_orb=Darkness Orb -spell.wizardry:darkvision=Darkvision -spell.wizardry:dart=Dart -spell.wizardry:decay=Decay -spell.wizardry:decoy=Decoy -spell.wizardry:detonate=Detonate -spell.wizardry:diamondflesh=Diamondflesh -spell.wizardry:earthquake=Earthquake -spell.wizardry:entrapment=Entrapment -spell.wizardry:fireball=Fireball -spell.wizardry:firebolt=Firebolt -spell.wizardry:firebomb=Firebomb -spell.wizardry:fire_resistance=Fire Resistance -spell.wizardry:fire_sigil=Fire Sigil -spell.wizardry:fireskin=Fireskin -spell.wizardry:firestorm=Firestorm -spell.wizardry:flame_ray=Flame Ray -spell.wizardry:flaming_axe=Flaming Axe -spell.wizardry:flaming_weapon=Flaming Weapon -spell.wizardry:flight=Flight -spell.wizardry:font_of_mana=Font of Mana -spell.wizardry:font_of_vitality=Font of Vitality -spell.wizardry:force_arrow=Force Arrow -spell.wizardry:forcefield=Forcefield -spell.wizardry:force_orb=Force Orb -spell.wizardry:forests_curse=Forest's Curse -spell.wizardry:freeze=Freeze -spell.wizardry:freezing_weapon=Freezing Weapon -spell.wizardry:frost_axe=Frost Axe -spell.wizardry:frost_ray=Frost Ray -spell.wizardry:frost_sigil=Frost Sigil -spell.wizardry:glide=Glide -spell.wizardry:greater_fireball=Greater Fireball -spell.wizardry:greater_heal=Greater Heal -spell.wizardry:group_heal=Group Heal -spell.wizardry:growth_aura=Growth Aura -spell.wizardry:hailstorm=Hailstorm -spell.wizardry:heal=Heal -spell.wizardry:heal_ally=Heal Ally -spell.wizardry:healing_aura=Healing Aura -spell.wizardry:homing_spark=Homing Spark -spell.wizardry:ice_age=Ice Age -spell.wizardry:ice_charge=Ice Charge -spell.wizardry:ice_lance=Ice Lance -spell.wizardry:ice_shard=Ice Shard -spell.wizardry:ice_shroud=Ice Shroud -spell.wizardry:ice_spikes=Ice Spikes -spell.wizardry:ice_statue=Ice Statue -spell.wizardry:ignite=Ignite -spell.wizardry:imbue_weapon=Imbue Weapon -spell.wizardry:intimidate=Intimidate -spell.wizardry:invigorating_presence=Invigorating Presence -spell.wizardry:invisibility=Invisibility -spell.wizardry:invoke_weather=Invoke Weather -spell.wizardry:ironflesh=Ironflesh -spell.wizardry:leap=Leap -spell.wizardry:levitation=Levitation -spell.wizardry:life_drain=Life Drain -spell.wizardry:light=Light -spell.wizardry:lightning_arrow=Lightning Arrow -spell.wizardry:lightning_bolt=Lightning Bolt -spell.wizardry:lightning_disc=Lightning Disk -spell.wizardry:lightning_hammer=Lightning Hammer -spell.wizardry:lightning_pulse=Lightning Pulse -spell.wizardry:lightning_ray=Lightning Ray -spell.wizardry:lightning_sigil=Lightning Sigil -spell.wizardry:lightning_web=Lightning Web -spell.wizardry:magic_missile=Magic Missile -spell.wizardry:metamorphosis=Metamorphosis -spell.wizardry:meteor=Meteor -spell.wizardry:mind_control=Mind Control -spell.wizardry:mind_trick=Mind Trick -spell.wizardry:none=[Empty Slot] -spell.wizardry:oakflesh=Oakflesh -spell.wizardry:petrify=Petrify -spell.wizardry:phase_step=Phase Step -spell.wizardry:plague_of_darkness=Plague of Darkness -spell.wizardry:pocket_furnace=Pocket Furnace -spell.wizardry:pocket_workbench=Pocket Workbench -spell.wizardry:poison=Poison -spell.wizardry:poison_bomb=Poison Bomb -spell.wizardry:replenish_hunger=Replenish Hunger -spell.wizardry:ring_of_fire=Ring of Fire -spell.wizardry:shadow_ward=Shadow Ward -spell.wizardry:shield=Shield -spell.wizardry:shockwave=Shockwave -spell.wizardry:silverfish_swarm=Silverfish Swarm -spell.wizardry:sixth_sense=Sixth Sense -spell.wizardry:slime=Slime -spell.wizardry:smoke_bomb=Smoke Bomb -spell.wizardry:snare=Snare -spell.wizardry:snowball=Snowball -spell.wizardry:spark_bomb=Spark Bomb -spell.wizardry:spectral_pathway=Spectral Pathway -spell.wizardry:spider_swarm=Spider Swarm -spell.wizardry:static_aura=Static Aura -spell.wizardry:summon_blaze=Summon Blaze -spell.wizardry:summon_ice_giant=Summon Ice Giant -spell.wizardry:summon_ice_wraith=Summon Ice Wraith -spell.wizardry:summon_iron_golem=Summon Iron Golem -spell.wizardry:summon_lightning_wraith=Summon Lightning Wraith -spell.wizardry:summon_phoenix=Summon Phoenix -spell.wizardry:summon_shadow_wraith=Summon Shadow Wraith -spell.wizardry:summon_skeleton=Summon Skeleton -spell.wizardry:summon_skeleton_legion=Summon Skeleton Legion -spell.wizardry:summon_snow_golem=Summon Snow Golem -spell.wizardry:summon_spirit_horse=Summon Spirit Horse -spell.wizardry:summon_spirit_wolf=Summon Spirit Wolf -spell.wizardry:summon_storm_elemental=Summon Storm Elemental -spell.wizardry:summon_wither_skeleton=Summon Wither Skeleton -spell.wizardry:summon_zombie=Summon Zombie -spell.wizardry:telekinesis=Telekinesis -spell.wizardry:thunderbolt=Thunderbolt -spell.wizardry:thunderstorm=Thunderstorm -spell.wizardry:tornado=Tornado -spell.wizardry:transience=Transience -spell.wizardry:transportation=Transportation -spell.wizardry:vanishing_box=Vanishing Box -spell.wizardry:wall_of_frost=Wall of Frost -spell.wizardry:water_breathing=Water Breathing -spell.wizardry:whirlwind=Whirlwind -spell.wizardry:wither=Wither -spell.wizardry:wither_skull=Wither Skull +spell.agility=Agility +spell.arc=Arc +spell.arcane_jammer=Arcane Jammer +spell.arrow_rain=Arrow Rain +spell.banish=Banish +spell.black_hole=Black Hole +spell.blink=Blink +spell.blizzard=Blizzard +spell.bubble=Bubble +spell.chain_lightning=Chain Lightning +spell.clairvoyance=Clairvoyance +spell.cobwebs=Cobwebs +spell.conjure_armour=Conjure Armor +spell.conjure_bow=Conjure Bow +spell.conjure_pickaxe=Conjure Pickaxe +spell.conjure_sword=Conjure Sword +spell.cure_effects=Cure Effects +spell.curse_of_soulbinding=Curse of Soulbinding +spell.darkness_orb=Darkness Orb +spell.darkvision=Darkvision +spell.dart=Dart +spell.decay=Decay +spell.decoy=Decoy +spell.detonate=Detonate +spell.diamondflesh=Diamondflesh +spell.earthquake=Earthquake +spell.entrapment=Entrapment +spell.fireball=Fireball +spell.firebolt=Firebolt +spell.firebomb=Firebomb +spell.fire_resistance=Fire Resistance +spell.fire_sigil=Fire Sigil +spell.fireskin=Fireskin +spell.firestorm=Firestorm +spell.flame_ray=Flame Ray +spell.flaming_axe=Flaming Axe +spell.flaming_weapon=Flaming Weapon +spell.flight=Flight +spell.font_of_mana=Font of Mana +spell.font_of_vitality=Font of Vitality +spell.force_arrow=Force Arrow +spell.forcefield=Forcefield +spell.force_orb=Force Orb +spell.forests_curse=Forest's Curse +spell.freeze=Freeze +spell.freezing_weapon=Freezing Weapon +spell.frost_axe=Frost Axe +spell.frost_ray=Frost Ray +spell.frost_sigil=Frost Sigil +spell.glide=Glide +spell.greater_fireball=Greater Fireball +spell.greater_heal=Greater Heal +spell.group_heal=Group Heal +spell.growth_aura=Growth Aura +spell.hailstorm=Hailstorm +spell.heal=Heal +spell.heal_ally=Heal Ally +spell.healing_aura=Healing Aura +spell.homing_spark=Homing Spark +spell.ice_age=Ice Age +spell.ice_charge=Ice Charge +spell.ice_lance=Ice Lance +spell.ice_shard=Ice Shard +spell.ice_shroud=Ice Shroud +spell.ice_spikes=Ice Spikes +spell.ice_statue=Ice Statue +spell.ignite=Ignite +spell.imbue_weapon=Imbue Weapon +spell.intimidate=Intimidate +spell.invigorating_presence=Invigorating Presence +spell.invisibility=Invisibility +spell.invoke_weather=Invoke Weather +spell.ironflesh=Ironflesh +spell.leap=Leap +spell.levitation=Levitation +spell.life_drain=Life Drain +spell.light=Light +spell.lightning_arrow=Lightning Arrow +spell.lightning_bolt=Lightning Bolt +spell.lightning_disc=Lightning Disk +spell.lightning_hammer=Lightning Hammer +spell.lightning_pulse=Lightning Pulse +spell.lightning_ray=Lightning Ray +spell.lightning_sigil=Lightning Sigil +spell.lightning_web=Lightning Web +spell.magic_missile=Magic Missile +spell.metamorphosis=Metamorphosis +spell.meteor=Meteor +spell.mind_control=Mind Control +spell.mind_trick=Mind Trick +spell.none=[Empty Slot] +spell.oakflesh=Oakflesh +spell.petrify=Petrify +spell.phase_step=Phase Step +spell.plague_of_darkness=Plague of Darkness +spell.pocket_furnace=Pocket Furnace +spell.pocket_workbench=Pocket Workbench +spell.poison=Poison +spell.poison_bomb=Poison Bomb +spell.replenish_hunger=Replenish Hunger +spell.ring_of_fire=Ring of Fire +spell.shadow_ward=Shadow Ward +spell.shield=Shield +spell.shockwave=Shockwave +spell.silverfish_swarm=Silverfish Swarm +spell.sixth_sense=Sixth Sense +spell.slime=Slime +spell.smoke_bomb=Smoke Bomb +spell.snare=Snare +spell.snowball=Snowball +spell.spark_bomb=Spark Bomb +spell.spectral_pathway=Spectral Pathway +spell.spider_swarm=Spider Swarm +spell.static_aura=Static Aura +spell.summon_blaze=Summon Blaze +spell.summon_ice_giant=Summon Ice Giant +spell.summon_ice_wraith=Summon Ice Wraith +spell.summon_iron_golem=Summon Iron Golem +spell.summon_lightning_wraith=Summon Lightning Wraith +spell.summon_phoenix=Summon Phoenix +spell.summon_shadow_wraith=Summon Shadow Wraith +spell.summon_skeleton=Summon Skeleton +spell.summon_skeleton_legion=Summon Skeleton Legion +spell.summon_snow_golem=Summon Snow Golem +spell.summon_spirit_horse=Summon Spirit Horse +spell.summon_spirit_wolf=Summon Spirit Wolf +spell.summon_storm_elemental=Summon Storm Elemental +spell.summon_wither_skeleton=Summon Wither Skeleton +spell.summon_zombie=Summon Zombie +spell.telekinesis=Telekinesis +spell.thunderbolt=Thunderbolt +spell.thunderstorm=Thunderstorm +spell.tornado=Tornado +spell.transience=Transience +spell.transportation=Transportation +spell.vanishing_box=Vanishing Box +spell.wall_of_frost=Wall of Frost +spell.water_breathing=Water Breathing +spell.whirlwind=Whirlwind +spell.wither=Wither +spell.wither_skull=Wither Skull -spell.wizardry:agility.desc=Grants the caster faster movement speed and greater jump height for 30 seconds. -spell.wizardry:arc.desc=Fires a spark of lightning at the target. -spell.wizardry:arcane_jammer.desc=Prevents the target from using magic for 15 seconds. -spell.wizardry:arrow_rain.desc="Archers, fire!" -spell.wizardry:banish.desc=Teleports the target against its will to a random location within a certain range. -spell.wizardry:black_hole.desc=Tear reality asunder. -spell.wizardry:blink.desc=Teleports the caster over a short distance to where they are pointing. -spell.wizardry:blizzard.desc=Creates a zone of swirling icy wind which slows and continually damages anything trapped inside. The caster is immune to the damage but is still slowed. -spell.wizardry:bubble.desc=Fires a jet of bubbles which causes anything it hits to float upwards helplessly. The target will fall after a certain time or if it is damaged. -spell.wizardry:chain_lightning.desc=Fires a spark of lightning at the target, which then chains to additional targets up to twice. -spell.wizardry:clairvoyance.desc=Reveals the path to a remembered location. With this spell selected, sneak-right-click on a block to set the location. Cast this spell normally to reveal the path. The path will fade after 90 seconds. -spell.wizardry:cobwebs.desc=Creates cobwebs where you are pointing, which greatly hamper the movement of any creatures caught amongst them. The cobwebs will vanish after 20 seconds or if broken. -spell.wizardry:conjure_armour.desc=Creates spectral armor around the caster which offers protection equal to that of iron armor. The armor lasts for 60 seconds. The caster must have an empty armor slot. -spell.wizardry:conjure_bow.desc=Creates a spectral bow with unlimited arrows that lasts for 30 seconds. -spell.wizardry:conjure_pickaxe.desc=Creates a spectral pickaxe of equal strength to an iron pickaxe that lasts for 30 seconds. -spell.wizardry:conjure_sword.desc=Creates a spectral sword of equal strength to an iron sword that lasts for 30 seconds. -spell.wizardry:cure_effects.desc=Removes all potion effects currently affecting the caster, good or bad. -spell.wizardry:curse_of_soulbinding.desc=Causes the target's soul to be inextricably bound to that of the caster, meaning all damage dealt to the caster is also dealt to the victim. Lasts until either the victim or the caster dies. -spell.wizardry:darkness_orb.desc=Fires a slow moving bolt of dark energy in the direction you are pointing, which withers whatever it hits. -spell.wizardry:darkvision.desc=Grants the caster night vision for 45 seconds. -spell.wizardry:dart.desc=Fires a dart in the direction you are pointing which damages and weakens its target. -spell.wizardry:decay.desc=Creates a patch of decay on the ground which infects any creature that touches it, causing lingering damage over time and spreading more decay wherever it walks. -spell.wizardry:decoy.desc=Creates an illusory clone of the caster which tricks mobs into attacking it instead. The decoy will vanish after 30 seconds. -spell.wizardry:detonate.desc=Causes an explosion where you are pointing, damaging all nearby creatures - including the caster, if they are too close. -spell.wizardry:diamondflesh.desc="Your arrows are no match for me!" -spell.wizardry:earthquake.desc=A true master of earth magic can move mountains. -spell.wizardry:entrapment.desc=Traps the target in a sphere of darkness which pulls it helplessly upwards and continually damages it. -spell.wizardry:fireball.desc=Launches a fireball in the direction you are pointing. -spell.wizardry:firebolt.desc=Shoots a jet of fire a short distance in front of you. -spell.wizardry:firebomb.desc=Lanches a firebomb in the direction you are pointing which explodes on impact, setting targets on fire. -spell.wizardry:fire_resistance.desc=Grants the caster fire resistance for 30 seconds. -spell.wizardry:fire_sigil.desc=Places a magical fire trap on the ground which damages and sets on fire the creature that triggers it. -spell.wizardry:fireskin.desc=Cloaks the caster in flames for 30 seconds, causing anything that attacks them to catch fire. -spell.wizardry:firestorm.desc="I am the dragon." -spell.wizardry:flame_ray.desc=Creates a stream of flames in the direction you are pointing which sets on fire and continually damages targets. -spell.wizardry:flaming_axe.desc=Creates a flaming axe which sets enemies on fire when hit. Lasts for 30 seconds. -spell.wizardry:flaming_weapon.desc=Temporarily imbues the first weapon on the caster's hotbar with the power of flame, causing it to set fire to its victims. The magic wears off after 45 seconds. -spell.wizardry:flight.desc=Soar like an eagle. -spell.wizardry:font_of_mana.desc="We were filled with an intense magical energy appearing to emanate from the centre of the..." - Extract from the journal of a forgotten mage; the rest of the page has been burnt away. -spell.wizardry:font_of_vitality.desc=It feels amazing. -spell.wizardry:force_arrow.desc=Shoots an arrow of force in the direction you are pointing. -spell.wizardry:forcefield.desc=Creates a forcefield around the caster which repels creatures and deflects projectiles. -spell.wizardry:force_orb.desc=Launches a sphere of force which damages and repels nearby creatures on impact. -spell.wizardry:forests_curse.desc="How dare you enter my forest!" -spell.wizardry:freeze.desc=Freezes the target for 10 seconds. Will also freeze water and create snow on the ground. -spell.wizardry:freezing_weapon.desc=Temporarily imbues the first weapon on the caster's hotbar with the power of frost, causing it to freeze its victims. The magic wears off after 45 seconds. -spell.wizardry:frost_axe.desc=Creates a frozen axe which freezes enemies when hit. Lasts for 30 seconds. -spell.wizardry:frost_ray.desc=Creates a stream of frost in the direction you are pointing which slows and continually damages targets. -spell.wizardry:frost_sigil.desc=Places a magical ice trap on the ground which damages and freezes the creature that triggers it. -spell.wizardry:glide.desc=Allows the caster to glide downwards while in the air and holding the use item button. -spell.wizardry:greater_fireball.desc=Launches a large fireball in the direction you are pointing which explodes on impact. -spell.wizardry:greater_heal.desc=Heals the caster by 4 hearts. -spell.wizardry:group_heal.desc=Heals the caster and all nearby allies and summoned creatures by 3 hearts. -spell.wizardry:growth_aura.desc=Grows all crops near the caster. Also grows tall grass and flowers on grass. -spell.wizardry:hailstorm.desc=It was during the great winter of the third age that the ice mages discovered their true power. -spell.wizardry:heal.desc=Heals the caster by 2 hearts. -spell.wizardry:heal_ally.desc=Heals the target by 2 and a half hearts. -spell.wizardry:healing_aura.desc=Creates a zone of healing energy which regenerates the health of any ally inside it. Any undead inside the healing aura will slowly take damage. -spell.wizardry:homing_spark.desc=Creates a floating spark which moves towards enemies. -spell.wizardry:ice_age.desc="You shall be frozen for an eternity!" -spell.wizardry:ice_charge.desc=Launches an ice charge which explodes on impact, freezing nearby creatures and releasing shards in all directions. -spell.wizardry:ice_lance.desc=Fires a great spear of ice in the direction you are pointing which overpenetrates targets, damaging and freezing them in the process. -spell.wizardry:ice_shard.desc=Fires a shard of ice in the direction you are pointing which damages and slows targets when hit. -spell.wizardry:ice_shroud.desc=Creates a shroud of cold around the caster for 30 seconds, causing anything that attacks them to be frozen. -spell.wizardry:ice_spikes.desc=Causes razor-sharp ice spikes to rise from the ground where you are pointing, skewering any creatures caught amongst them. -spell.wizardry:ice_statue.desc=Freezes the target solid for 20 seconds or until broken out. The target cannot move or do anything while frozen but is also impervious to all damage. -spell.wizardry:ignite.desc=Sets the target on fire for 10 seconds. Also works like a flint and steel. -spell.wizardry:imbue_weapon.desc=Temporarily imbues the first weapon on the caster's hotbar with magic, rendering it more effective. The magic wears off after 45 seconds. -spell.wizardry:intimidate.desc=Emits an intimidating growl which causes nearby creatures to run away in fear. Fear stricken creatures will recover after 30 seconds. -spell.wizardry:invigorating_presence.desc=Grants the caster and all nearby allies increased strength for 45 seconds. -spell.wizardry:invisibility.desc=Makes the caster invisible for 30 seconds. -spell.wizardry:invoke_weather.desc=Changes the weather in the world. -spell.wizardry:ironflesh.desc=Greatly improves the caster's damage resistance for 30 seconds. -spell.wizardry:leap.desc=Causes the caster to jump upwards several blocks and slightly forward. -spell.wizardry:levitation.desc=Raises the caster upwards while the use item button is held. Will also negate fall damage if used before hitting the ground. -spell.wizardry:life_drain.desc=Creates a stream of withering energy in the direction you are pointing which drains the life of the target and uses it to gradually regenerate your health. -spell.wizardry:light.desc=Creates a magical point of light which illuminates the surrounding area. Lasts for 30 seconds. -spell.wizardry:lightning_arrow.desc=Shoots an arrow of lightning in the direction you are pointing. -spell.wizardry:lightning_bolt.desc=Causes lightning to strike where you are pointing. -spell.wizardry:lightning_disc.desc=Sends a disk of lightning flying off in the direction you are pointing, which seeks targets. -spell.wizardry:lightning_hammer.desc="I smite you by the wrath of the heavens!" -spell.wizardry:lightning_pulse.desc=Charges the ground around the caster with lightning, damaging and repelling nearby creatures. -spell.wizardry:lightning_ray.desc=Creates a stream of lightning in the direction you are pointing which continually damages targets. -spell.wizardry:lightning_sigil.desc=Places a magical lightning trap on the ground which damages the creature that triggers it and chains lightning to other nearby creatures. -spell.wizardry:lightning_web.desc="Focus. Channel the storm in your mind through your wand and unleash its fury." -spell.wizardry:magic_missile.desc=Fires a bolt of magical energy in the direction you are pointing. -spell.wizardry:metamorphosis.desc=Changes the target into another form. Only works on some creatures. -spell.wizardry:meteor.desc=Some wizards just want to see the world burn... -spell.wizardry:mind_control.desc=Takes control of the target's mind for 30 seconds, causing it switch sides and fight for the caster instead. Will not work on creatures that are too strong-willed. -spell.wizardry:mind_trick.desc=Confuses and disorients the target for 15 seconds, rendering it unable to attack effectively. The effect will be dispelled if the target takes damage. -spell.wizardry:none.desc=To get a spell book with the /give command, use metadata: /give [player] wizardry:spell_book 1 [spell id] (if you found this book in a chest, some other mod has messed things up). -spell.wizardry:oakflesh.desc=Improves the caster's damage resistance for 30 seconds. -spell.wizardry:petrify.desc=Turns the target to stone until broken out, with a chance for it to break out when it is dark. The target cannot move or do anything while petrified but is also impervious to all damage. -spell.wizardry:phase_step.desc=Teleports the caster through a 1 block thick wall in front of them. Range upgrades will increase the thickness you can teleport through. -spell.wizardry:plague_of_darkness.desc=The darkness will consume them all... -spell.wizardry:pocket_furnace.desc=Smelts up to 5 smeltable items in the caster's inventory. Items on the hotbar will be smelted first. -spell.wizardry:pocket_workbench.desc=Allows the caster to craft items as if they were at a crafting table. -spell.wizardry:poison.desc=Fires poison in the direction you are pointing. -spell.wizardry:poison_bomb.desc=Lanches a poison bomb in the direction you are pointing which explodes on impact, poisoning nearby creatures. -spell.wizardry:replenish_hunger.desc=Replenishes the caster's food level by 6 hunger points. -spell.wizardry:ring_of_fire.desc=Creates a ring of fire around the caster, damaging all nearby enemies and setting them on fire. -spell.wizardry:shadow_ward.desc=Creates a wall of darkness in front of the caster which causes half of all incoming damage to be inflicted upon the attacker instead. -spell.wizardry:shield.desc=Creates a protective barrier of force that blocks projectiles and magic. Also grants the caster a weak resistance effect. -spell.wizardry:shockwave.desc=Boom. -spell.wizardry:silverfish_swarm.desc="Ahhhh! They're MULTIPLYING!" -spell.wizardry:sixth_sense.desc=Allows the caster to sense the locations of nearby creatures, even through walls, for 20 seconds. -spell.wizardry:slime.desc=Engulfs the target in slime which slows and continually damages it. The slime bursts after 10 seconds. -spell.wizardry:smoke_bomb.desc=Launches a smoke bomb in the direction you are pointing which explodes on impact, releasing smoke and blinding nearby creatures for a short time. -spell.wizardry:snare.desc=Sets a trap on the ground which damages and briefly slows the creature that triggers it. -spell.wizardry:snowball.desc=Launches a snowball in the direction you are pointing. -spell.wizardry:spark_bomb.desc=Launches a shock charge in the direction you are pointing which releases sparks at nearby enemies on impact. -spell.wizardry:spectral_pathway.desc=Creates an indestructible magical bridge in front of you which extends for 15 blocks. The bridge vanishes after 60 seconds. -spell.wizardry:spider_swarm.desc=Summons a swarm of venomous spiders to fight for you. The spiders will disappear after 30 seconds or if they are killed. -spell.wizardry:static_aura.desc=Surrounds the caster with lightning for 30 seconds, firing a spark of lightning at anything that hits them. -spell.wizardry:summon_blaze.desc=Summons a blaze to fight for you. The blaze will disappear after 30 seconds or if it is killed. -spell.wizardry:summon_ice_giant.desc="Smash them!" -spell.wizardry:summon_ice_wraith.desc=Summons an ice wraith to fight for you. The ice wraith will disappear after 30 seconds or if it is killed. -spell.wizardry:summon_iron_golem.desc=Automatic automated autonomous automaton. -spell.wizardry:summon_lightning_wraith.desc=Summons a lightning wraith to fight for you. The lightning wraith will disappear after 30 seconds or if it is killed. -spell.wizardry:summon_phoenix.desc=From the ashes... -spell.wizardry:summon_shadow_wraith.desc=Summons a shadow wraith to fight for you. -spell.wizardry:summon_skeleton.desc=Summons a skeleton to fight for you. The skeleton will disappear after 30 seconds or if it is killed. -spell.wizardry:summon_skeleton_legion.desc="Rise, undead army!" -spell.wizardry:summon_snow_golem.desc=Creates a snow golem to fight for you. Lasts until the snow golem dies. -spell.wizardry:summon_spirit_horse.desc=Summons a spirit horse for you to ride. The spirit horse will vanish a short while after it is dismounted, or you can dismiss it by shift-right-clicking on it with any wand. -spell.wizardry:summon_spirit_wolf.desc=Summons a spirit wolf companion to fight for you. The spirit wolf will only disappear if it is killed, or you can dismiss it by shift-right-clicking on it with any wand. -spell.wizardry:summon_storm_elemental.desc="Storm Elemental: An ancient manifestation of the elements, it can hardly contain the raw power churning within it." - The Wizard's Guide to Arcane Beings, Volume I_i -spell.wizardry:summon_wither_skeleton.desc=Summons a wither skeleton to fight for you. The wither skeleton will disappear after 30 seconds or if it is killed. -spell.wizardry:summon_zombie.desc=Summons a zombie to fight for you. The zombie will disappear after 30 seconds or if it is killed. -spell.wizardry:telekinesis.desc=Moves an item or other small object towards you, or right-clicks the block you are looking at. Can also be used to disarm players. -spell.wizardry:thunderbolt.desc=Shoots a bolt of thunder which knocks back targets. -spell.wizardry:thunderstorm.desc="Mwahahahahahaha!" -spell.wizardry:tornado.desc=Unleashes a tornado in the direction you are pointing which hurls anything in its path skywards. -spell.wizardry:transience.desc=Makes the caster transient for 20 seconds. The caster is immune to all damage while transient but cannot break or place blocks or cause any damage. -spell.wizardry:transportation.desc=Transports the caster to their remembered stone circle. To use this spell, make a circle of stones of transportation, then right click it with a wand. -spell.wizardry:vanishing_box.desc=Grants the caster access to their ender chest storage. -spell.wizardry:wall_of_frost.desc=Winter at your fingertips. -spell.wizardry:water_breathing.desc=Allows the caster to breathe underwater for 60 seconds. -spell.wizardry:whirlwind.desc=Causes the target to be blown upwards and away from you at speed. -spell.wizardry:wither.desc=Fires a ray of darkness which withers anything it touches. -spell.wizardry:wither_skull.desc=Launches a wither skull in the direction you are pointing. +spell.agility.desc=Grants the caster faster movement speed and greater jump height for 30 seconds. +spell.arc.desc=Fires a spark of lightning at the target. +spell.arcane_jammer.desc=Prevents the target from using magic for 15 seconds. +spell.arrow_rain.desc="Archers, fire!" +spell.banish.desc=Teleports the target against its will to a random location within a certain range. +spell.black_hole.desc=Tear reality asunder. +spell.blink.desc=Teleports the caster over a short distance to where they are pointing. +spell.blizzard.desc=Creates a zone of swirling icy wind which slows and continually damages anything trapped inside. The caster is immune to the damage but is still slowed. +spell.bubble.desc=Fires a jet of bubbles which causes anything it hits to float upwards helplessly. The target will fall after a certain time or if it is damaged. +spell.chain_lightning.desc=Fires a spark of lightning at the target, which then chains to additional targets up to twice. +spell.clairvoyance.desc=Reveals the path to a remembered location. With this spell selected, sneak-right-click on a block to set the location. Cast this spell normally to reveal the path. The path will fade after 90 seconds. +spell.cobwebs.desc=Creates cobwebs where you are pointing, which greatly hamper the movement of any creatures caught amongst them. The cobwebs will vanish after 20 seconds or if broken. +spell.conjure_armour.desc=Creates spectral armor around the caster which offers protection equal to that of iron armor. The armor lasts for 60 seconds. The caster must have an empty armor slot. +spell.conjure_bow.desc=Creates a spectral bow with unlimited arrows that lasts for 30 seconds. +spell.conjure_pickaxe.desc=Creates a spectral pickaxe of equal strength to an iron pickaxe that lasts for 30 seconds. +spell.conjure_sword.desc=Creates a spectral sword of equal strength to an iron sword that lasts for 30 seconds. +spell.cure_effects.desc=Removes all potion effects currently affecting the caster, good or bad. +spell.curse_of_soulbinding.desc=Causes the target's soul to be inextricably bound to that of the caster, meaning all damage dealt to the caster is also dealt to the victim. Lasts until either the victim or the caster dies. +spell.darkness_orb.desc=Fires a slow moving bolt of dark energy in the direction you are pointing, which withers whatever it hits. +spell.darkvision.desc=Grants the caster night vision for 45 seconds. +spell.dart.desc=Fires a dart in the direction you are pointing which damages and weakens its target. +spell.decay.desc=Creates a patch of decay on the ground which infects any creature that touches it, causing lingering damage over time and spreading more decay wherever it walks. +spell.decoy.desc=Creates an illusory clone of the caster which tricks mobs into attacking it instead. The decoy will vanish after 30 seconds. +spell.detonate.desc=Causes an explosion where you are pointing, damaging all nearby creatures - including the caster, if they are too close. +spell.diamondflesh.desc="Your arrows are no match for me!" +spell.earthquake.desc=A true master of earth magic can move mountains. +spell.entrapment.desc=Traps the target in a sphere of darkness which pulls it helplessly upwards and continually damages it. +spell.fireball.desc=Launches a fireball in the direction you are pointing. +spell.firebolt.desc=Shoots a jet of fire a short distance in front of you. +spell.firebomb.desc=Lanches a firebomb in the direction you are pointing which explodes on impact, setting targets on fire. +spell.fire_resistance.desc=Grants the caster fire resistance for 30 seconds. +spell.fire_sigil.desc=Places a magical fire trap on the ground which damages and sets on fire the creature that triggers it. +spell.fireskin.desc=Cloaks the caster in flames for 30 seconds, causing anything that attacks them to catch fire. +spell.firestorm.desc="I am the dragon." +spell.flame_ray.desc=Creates a stream of flames in the direction you are pointing which sets on fire and continually damages targets. +spell.flaming_axe.desc=Creates a flaming axe which sets enemies on fire when hit. Lasts for 30 seconds. +spell.flaming_weapon.desc=Temporarily imbues the first weapon on the caster's hotbar with the power of flame, causing it to set fire to its victims. The magic wears off after 45 seconds. +spell.flight.desc=Soar like an eagle. +spell.font_of_mana.desc="We were filled with an intense magical energy appearing to emanate from the centre of the..." - Extract from the journal of a forgotten mage; the rest of the page has been burnt away. +spell.font_of_vitality.desc=It feels amazing. +spell.force_arrow.desc=Shoots an arrow of force in the direction you are pointing. +spell.forcefield.desc=Creates a forcefield around the caster which repels creatures and deflects projectiles. +spell.force_orb.desc=Launches a sphere of force which damages and repels nearby creatures on impact. +spell.forests_curse.desc="How dare you enter my forest!" +spell.freeze.desc=Freezes the target for 10 seconds. Will also freeze water and create snow on the ground. +spell.freezing_weapon.desc=Temporarily imbues the first weapon on the caster's hotbar with the power of frost, causing it to freeze its victims. The magic wears off after 45 seconds. +spell.frost_axe.desc=Creates a frozen axe which freezes enemies when hit. Lasts for 30 seconds. +spell.frost_ray.desc=Creates a stream of frost in the direction you are pointing which slows and continually damages targets. +spell.frost_sigil.desc=Places a magical ice trap on the ground which damages and freezes the creature that triggers it. +spell.glide.desc=Allows the caster to glide downwards while in the air and holding the use item button. +spell.greater_fireball.desc=Launches a large fireball in the direction you are pointing which explodes on impact. +spell.greater_heal.desc=Heals the caster by 4 hearts. +spell.group_heal.desc=Heals the caster and all nearby allies and summoned creatures by 3 hearts. +spell.growth_aura.desc=Grows all crops near the caster. Also grows tall grass and flowers on grass. +spell.hailstorm.desc=It was during the great winter of the third age that the ice mages discovered their true power. +spell.heal.desc=Heals the caster by 2 hearts. +spell.heal_ally.desc=Heals the target by 2 and a half hearts. +spell.healing_aura.desc=Creates a zone of healing energy which regenerates the health of any ally inside it. Any undead inside the healing aura will slowly take damage. +spell.homing_spark.desc=Creates a floating spark which moves towards enemies. +spell.ice_age.desc="You shall be frozen for an eternity!" +spell.ice_charge.desc=Launches an ice charge which explodes on impact, freezing nearby creatures and releasing shards in all directions. +spell.ice_lance.desc=Fires a great spear of ice in the direction you are pointing which overpenetrates targets, damaging and freezing them in the process. +spell.ice_shard.desc=Fires a shard of ice in the direction you are pointing which damages and slows targets when hit. +spell.ice_shroud.desc=Creates a shroud of cold around the caster for 30 seconds, causing anything that attacks them to be frozen. +spell.ice_spikes.desc=Causes razor-sharp ice spikes to rise from the ground where you are pointing, skewering any creatures caught amongst them. +spell.ice_statue.desc=Freezes the target solid for 20 seconds or until broken out. The target cannot move or do anything while frozen but is also impervious to all damage. +spell.ignite.desc=Sets the target on fire for 10 seconds. Also works like a flint and steel. +spell.imbue_weapon.desc=Temporarily imbues the first weapon on the caster's hotbar with magic, rendering it more effective. The magic wears off after 45 seconds. +spell.intimidate.desc=Emits an intimidating growl which causes nearby creatures to run away in fear. Fear stricken creatures will recover after 30 seconds. +spell.invigorating_presence.desc=Grants the caster and all nearby allies increased strength for 45 seconds. +spell.invisibility.desc=Makes the caster invisible for 30 seconds. +spell.invoke_weather.desc=Changes the weather in the world. +spell.ironflesh.desc=Greatly improves the caster's damage resistance for 30 seconds. +spell.leap.desc=Causes the caster to jump upwards several blocks and slightly forward. +spell.levitation.desc=Raises the caster upwards while the use item button is held. Will also negate fall damage if used before hitting the ground. +spell.life_drain.desc=Creates a stream of withering energy in the direction you are pointing which drains the life of the target and uses it to gradually regenerate your health. +spell.light.desc=Creates a magical point of light which illuminates the surrounding area. Lasts for 30 seconds. +spell.lightning_arrow.desc=Shoots an arrow of lightning in the direction you are pointing. +spell.lightning_bolt.desc=Causes lightning to strike where you are pointing. +spell.lightning_disc.desc=Sends a disk of lightning flying off in the direction you are pointing, which seeks targets. +spell.lightning_hammer.desc="I smite you by the wrath of the heavens!" +spell.lightning_pulse.desc=Charges the ground around the caster with lightning, damaging and repelling nearby creatures. +spell.lightning_ray.desc=Creates a stream of lightning in the direction you are pointing which continually damages targets. +spell.lightning_sigil.desc=Places a magical lightning trap on the ground which damages the creature that triggers it and chains lightning to other nearby creatures. +spell.lightning_web.desc="Focus. Channel the storm in your mind through your wand and unleash its fury." +spell.magic_missile.desc=Fires a bolt of magical energy in the direction you are pointing. +spell.metamorphosis.desc=Changes the target into another form. Only works on some creatures. +spell.meteor.desc=Some wizards just want to see the world burn... +spell.mind_control.desc=Takes control of the target's mind for 30 seconds, causing it switch sides and fight for the caster instead. Will not work on creatures that are too strong-willed. +spell.mind_trick.desc=Confuses and disorients the target for 15 seconds, rendering it unable to attack effectively. The effect will be dispelled if the target takes damage. +spell.none.desc=To get a spell book with the /give command, use metadata: /give [player] wizardry:spell_book 1 [spell id] (if you found this book in a chest, some other mod has messed things up). +spell.oakflesh.desc=Improves the caster's damage resistance for 30 seconds. +spell.petrify.desc=Turns the target to stone until broken out, with a chance for it to break out when it is dark. The target cannot move or do anything while petrified but is also impervious to all damage. +spell.phase_step.desc=Teleports the caster through a 1 block thick wall in front of them. Range upgrades will increase the thickness you can teleport through. +spell.plague_of_darkness.desc=The darkness will consume them all... +spell.pocket_furnace.desc=Smelts up to 5 smeltable items in the caster's inventory. Items on the hotbar will be smelted first. +spell.pocket_workbench.desc=Allows the caster to craft items as if they were at a crafting table. +spell.poison.desc=Fires poison in the direction you are pointing. +spell.poison_bomb.desc=Lanches a poison bomb in the direction you are pointing which explodes on impact, poisoning nearby creatures. +spell.replenish_hunger.desc=Replenishes the caster's food level by 6 hunger points. +spell.ring_of_fire.desc=Creates a ring of fire around the caster, damaging all nearby enemies and setting them on fire. +spell.shadow_ward.desc=Creates a wall of darkness in front of the caster which causes half of all incoming damage to be inflicted upon the attacker instead. +spell.shield.desc=Creates a protective barrier of force that blocks projectiles and magic. Also grants the caster a weak resistance effect. +spell.shockwave.desc=Boom. +spell.silverfish_swarm.desc="Ahhhh! They're MULTIPLYING!" +spell.sixth_sense.desc=Allows the caster to sense the locations of nearby creatures, even through walls, for 20 seconds. +spell.slime.desc=Engulfs the target in slime which slows and continually damages it. The slime bursts after 10 seconds. +spell.smoke_bomb.desc=Launches a smoke bomb in the direction you are pointing which explodes on impact, releasing smoke and blinding nearby creatures for a short time. +spell.snare.desc=Sets a trap on the ground which damages and briefly slows the creature that triggers it. +spell.snowball.desc=Launches a snowball in the direction you are pointing. +spell.spark_bomb.desc=Launches a shock charge in the direction you are pointing which releases sparks at nearby enemies on impact. +spell.spectral_pathway.desc=Creates an indestructible magical bridge in front of you which extends for 15 blocks. The bridge vanishes after 60 seconds. +spell.spider_swarm.desc=Summons a swarm of venomous spiders to fight for you. The spiders will disappear after 30 seconds or if they are killed. +spell.static_aura.desc=Surrounds the caster with lightning for 30 seconds, firing a spark of lightning at anything that hits them. +spell.summon_blaze.desc=Summons a blaze to fight for you. The blaze will disappear after 30 seconds or if it is killed. +spell.summon_ice_giant.desc="Smash them!" +spell.summon_ice_wraith.desc=Summons an ice wraith to fight for you. The ice wraith will disappear after 30 seconds or if it is killed. +spell.summon_iron_golem.desc=Automatic automated autonomous automaton. +spell.summon_lightning_wraith.desc=Summons a lightning wraith to fight for you. The lightning wraith will disappear after 30 seconds or if it is killed. +spell.summon_phoenix.desc=From the ashes... +spell.summon_shadow_wraith.desc=Summons a shadow wraith to fight for you. +spell.summon_skeleton.desc=Summons a skeleton to fight for you. The skeleton will disappear after 30 seconds or if it is killed. +spell.summon_skeleton_legion.desc="Rise, undead army!" +spell.summon_snow_golem.desc=Creates a snow golem to fight for you. Lasts until the snow golem dies. +spell.summon_spirit_horse.desc=Summons a spirit horse for you to ride. The spirit horse will vanish a short while after it is dismounted, or you can dismiss it by shift-right-clicking on it with any wand. +spell.summon_spirit_wolf.desc=Summons a spirit wolf companion to fight for you. The spirit wolf will only disappear if it is killed, or you can dismiss it by shift-right-clicking on it with any wand. +spell.summon_storm_elemental.desc="Storm Elemental: An ancient manifestation of the elements, it can hardly contain the raw power churning within it." - The Wizard's Guide to Arcane Beings, Volume II +spell.summon_wither_skeleton.desc=Summons a wither skeleton to fight for you. The wither skeleton will disappear after 30 seconds or if it is killed. +spell.summon_zombie.desc=Summons a zombie to fight for you. The zombie will disappear after 30 seconds or if it is killed. +spell.telekinesis.desc=Moves an item or other small object towards you, or right-clicks the block you are looking at. Can also be used to disarm players. +spell.thunderbolt.desc=Shoots a bolt of thunder which knocks back targets. +spell.thunderstorm.desc="Mwahahahahahaha!" +spell.tornado.desc=Unleashes a tornado in the direction you are pointing which hurls anything in its path skywards. +spell.transience.desc=Makes the caster transient for 20 seconds. The caster is immune to all damage while transient but cannot break or place blocks or cause any damage. +spell.transportation.desc=Transports the caster to their remembered stone circle. To use this spell, make a circle of stones of transportation, then right click it with a wand. +spell.vanishing_box.desc=Grants the caster access to their ender chest storage. +spell.wall_of_frost.desc=Winter at your fingertips. +spell.water_breathing.desc=Allows the caster to breathe underwater for 60 seconds. +spell.whirlwind.desc=Causes the target to be blown upwards and away from you at speed. +spell.wither.desc=Fires a ray of darkness which withers anything it touches. +spell.wither_skull.desc=Launches a wither skull in the direction you are pointing. -spell.wizardry:invoke_weather.sun=The rain begins to stop... -spell.wizardry:invoke_weather.rain=The heavens open... -spell.wizardry:transportation.missing=Your remembered stone circle is missing or obstructed... -spell.wizardry:transportation.undefined=You must remember the location of a stone circle first! -spell.wizardry:transportation.wrongdimension=Your remembered stone circle is in another dimension... -spell.wizardry:clairvoyance.searching=Searching... -spell.wizardry:clairvoyance.confirm=The path revealed upon casting %1$s will now lead back to this point -spell.wizardry:clairvoyance.outofrange=Your remembered location is too far away or inaccessible... -spell.wizardry:clairvoyance.undefined=You must remember a location first! -spell.wizardry:clairvoyance.wrongdimension=Your remembered location is in another dimension... +spell.invoke_weather.sun=The rain begins to stop... +spell.invoke_weather.rain=The heavens open... +spell.transportation.missing=Your remembered stone circle is missing or obstructed... +spell.transportation.undefined=You must remember the location of a stone circle first! +spell.transportation.wrongdimension=Your remembered stone circle is in another dimension... +spell.clairvoyance.searching=Searching... +spell.clairvoyance.confirm=The path revealed upon casting %1$s will now lead back to this point +spell.clairvoyance.outofrange=Your remembered location is too far away or inaccessible... +spell.clairvoyance.undefined=You must remember a location first! +spell.clairvoyance.wrongdimension=Your remembered location is in another dimension... -potion.wizardry:frost=Frostbite -potion.wizardry:fireskin=Fireskin -potion.wizardry:ice_shroud=Ice Shroud -potion.wizardry:static_aura=Static Aura -potion.wizardry:transience=Transience -potion.wizardry:decay=Decay -potion.wizardry:sixth_sense=Sixth Sense -potion.wizardry:arcane_jammer=Arcane Jammer -potion.wizardry:mind_trick=Mind Trick -potion.wizardry:mind_control=Mind Control -potion.wizardry:font_of_mana=Font of Mana -potion.wizardry:fear=Fear +potion.frost=Frostbite +potion.fireskin=Fireskin +potion.ice_shroud=Ice Shroud +potion.static_aura=Static Aura +potion.transience=Transience +potion.decay=Decay +potion.sixth_sense=Sixth Sense +potion.arcane_jammer=Arcane Jammer +potion.mind_trick=Mind Trick +potion.mind_control=Mind Control +potion.font_of_mana=Font of Mana +potion.fear=Fear -enchantment.wizardry:magic_sword=Imbuement -enchantment.wizardry:magic_bow=Imbuement -enchantment.wizardry:flaming_weapon=Fire Imbuement -enchantment.wizardry:freezing_weapon=Frost Imbuement +enchantment.magic_sword=Imbuement +enchantment.magic_bow=Imbuement +enchantment.flaming_weapon=Fire Imbuement +enchantment.freezing_weapon=Frost Imbuement key.categories.wizardry=Wizardry -key.wizardry.next_spell=Next Spell -key.wizardry.previous_spell=Previous Spell +key.next_spell=Next Spell +key.previous_spell=Previous Spell -death.attack.wizardry_magic=%1$s was killed by %2$s using magic -death.attack.indirect_wizardry_magic=%1$s was killed by %2$s using magic +death.attack.wizardryMagic=%1$s was killed by %2$s using magic +death.attack.indirectWizardryMagic=%1$s was killed by %2$s using magic -commands.wizardry:cast.usage=/%1$s [player] [damage multiplier] [range multiplier] [duration multiplier] [blast multiplier] -commands.wizardry:cast.success=Successfully cast %1$s -commands.wizardry:cast.success_continuous=Successfully cast %1$s; repeat the command to stop -commands.wizardry:cast.success_remote=Successfully cast %1$s as %2$s -commands.wizardry:cast.success_remote_continuous=Successfully cast %1$s as %2$s; repeat the command to stop -commands.wizardry:cast.fail=Unable to cast %1$s -commands.wizardry:cast.not_found=There is no such spell with ID %1$s -commands.wizardry:cast.tag_error=Data tag parsing failed: %s +commands.cast.usage=/%1$s [player] [damage multiplier] [range multiplier] [duration multiplier] [blast multiplier] +commands.cast.success=Successfully cast %1$s +commands.cast.success_continuous=Successfully cast %1$s; repeat the command to stop +commands.cast.success_remote=Successfully cast %1$s as %2$s +commands.cast.success_remote_continuous=Successfully cast %1$s as %2$s; repeat the command to stop +commands.cast.fail=Unable to cast %1$s +commands.cast.not_found=There is no such spell with ID %1$s -commands.wizardry:ally.usage=/%1$s [player] -commands.wizardry:ally.addally=%1$s has been added to %2$s's list of allies -commands.wizardry:ally.removeally=%1$s has been removed from %2$s's list of allies -commands.wizardry:ally.self=Players cannot be an ally of themselves! -commands.wizardry:ally.permission=You do not have permission to change other players' allies +commands.ally.usage=/%1$s [player] +commands.ally.addally=%1$s has been added to %2$s's list of allies +commands.ally.removeally=%1$s has been removed from %2$s's list of allies +commands.ally.self=Players cannot be an ally of themselves! +commands.ally.permission=You do not have permission to change other players' allies -commands.wizardry:allies.usage=/%1$s [player] -commands.wizardry:allies.list=Players allied to you: %1$s -commands.wizardry:allies.list_other=Players allied to %1$s: %2$s -commands.wizardry:allies.permission=You do not have permission to view other players' allies -commands.wizardry:allies.none=None +commands.allies.usage=/%1$s [player] +commands.allies.list=Players allied to you: %1$s +commands.allies.list_other=Players allied to %1$s: %2$s +commands.allies.permission=You do not have permission to view other players' allies +commands.allies.none=None -commands.wizardry:discoverspell.usage=/%1$s [player] -commands.wizardry:discoverspell.not_found=There is no such spell with ID %1$s -commands.wizardry:discoverspell.clear=Cleared all spell discovery data for %1$s -commands.wizardry:discoverspell.all=Added all spells to %1$s's spell discovery data -commands.wizardry:discoverspell.addspell=Added %1$s to %2$s's spell discovery data -commands.wizardry:discoverspell.removespell=Removed %1$s from %2$s's spell discovery data +commands.discoverspell.usage=/%1$s [player] +commands.discoverspell.not_found=There is no such spell with ID %1$s +commands.discoverspell.clear=Cleared all spell discovery data for %1$s +commands.discoverspell.all=Added all spells to %1$s's spell discovery data +commands.discoverspell.addspell=Added %1$s to %2$s's spell discovery data +commands.discoverspell.removespell=Removed %1$s from %2$s's spell discovery data -config.wizardry.title.general=Mod Options +config.title.general=Mod Options -config.wizardry.category.spells=Configure Spells -config.wizardry.category.spells.tooltip=Select which spells are enabled -config.wizardry.title.spells=Spell Configuration -config.wizardry.subtitle.spells=Set a spell to false to disable it. +config.category.spells=Configure Spells +config.category.spells.tooltip=Select which spells are enabled +config.title.spells=Spell Configuration +config.subtitle.spells=Set a spell to false to disable it. -config.wizardry.category.resistances=Configure Resistances -config.wizardry.category.resistances.tooltip=Configure which mobs are immune to different types of magic -config.wizardry.title.resistances=Resistance Configuration -config.wizardry.subtitle.resistances=See descriptions of individual options for more details. +config.category.resistances=Configure Resistances +config.category.resistances.tooltip=Configure which mobs are immune to different types of magic +config.title.resistances=Resistance Configuration +config.subtitle.resistances=See descriptions of individual options for more details. -config.wizardry.category.ids=Configure IDs -config.wizardry.category.ids.tooltip=Change the IDs used by wizardry -config.wizardry.title.ids=ID Configuration -config.wizardry.subtitle.ids=Change these IDs if they conflict with another mod. +config.category.ids=Configure IDs +config.category.ids.tooltip=Change the IDs used by wizardry +config.title.ids=ID Configuration +config.subtitle.ids=Change these IDs if they conflict with another mod. -config.wizardry.tower_rarity=Tower Rarity -config.wizardry.ore_dimensions=Ore Dimensions -config.wizardry.flower_dimensions=Flower Dimensions -config.wizardry.tower_dimensions=Tower Dimensions -config.wizardry.spell_book_drop_chance=Spell Book Drop Chance -config.wizardry.generate_loot=Generate Loot -config.wizardry.firebomb_is_craftable=Firebomb Is Craftable -config.wizardry.poison_bomb_is_craftable=Poison Bomb Is Craftable -config.wizardry.smoke_bomb_is_craftable=Smoke Bomb Is Craftable -config.wizardry.use_alternate_scroll_recipe=Use Alternate Scroll Recipe -config.wizardry.teleport_through_unbreakable_blocks=Teleport Through Unbreakable Blocks -config.wizardry.show_summoned_creature_names=Show Summoned Creature Names -config.wizardry.friendly_fire=Friendly Fire -config.wizardry.telekinetic_disarmament=Telekinetic Disarmament -config.wizardry.discovery_mode=Discovery Mode -config.wizardry.enable_shift_scrolling=Enable Shift-_scrolling -config.wizardry.minion_revenge_targeting=Minion Revenge Targeting -config.wizardry.player_damage_scaling=Player Damage Scaling Factor -config.wizardry.npc_damage_scaling=NPC Damage Scaling Factor -config.wizardry.cast_command_multiplier_limit=Cast Command Multiplier Limit -config.wizardry.summoned_creature_targets_whitelist=Summoned Creature Target Whitelist -config.wizardry.summoned_creature_targets_blacklist=Summoned Creature Target Blacklist -config.wizardry.spell_hud_position=Spell HUD Position -config.wizardry.cast_command_name=Cast Spell Command Name -config.wizardry.discoverspell_command_name=Discover Spell Command Name -config.wizardry.ally_command_name=Set Ally Command Name -config.wizardry.allies_command_name=View Allies Command Name +config.tower_rarity=Tower Rarity +config.ore_dimensions=Ore Dimensions +config.flower_dimensions=Flower Dimensions +config.tower_dimensions=Tower Dimensions +config.spell_book_drop_chance=Spell Book Drop Chance +config.generate_loot=Generate Loot +config.firebomb_is_craftable=Firebomb Is Craftable +config.poison_bomb_is_craftable=Poison Bomb Is Craftable +config.smoke_bomb_is_craftable=Smoke Bomb Is Craftable +config.use_alternate_scroll_recipe=Use Alternate Scroll Recipe +config.teleport_through_unbreakable_blocks=Teleport Through Unbreakable Blocks +config.show_summoned_creature_names=Show Summoned Creature Names +config.friendly_fire=Friendly Fire +config.telekinetic_disarmament=Telekinetic Disarmament +config.discovery_mode=Discovery Mode +config.enable_shift_scrolling=Enable Shift-Scrolling +config.minion_revenge_targeting=Minion Revenge Targeting +config.player_damage_scaling=Player Damage Scaling Factor +config.npc_damage_scaling=NPC Damage Scaling Factor +config.cast_command_multiplier_limit=Cast Command Multiplier Limit +config.summoned_creature_targets_whitelist=Summoned Creature Target Whitelist +config.summoned_creature_targets_blacklist=Summoned Creature Target Blacklist +config.spell_hud_position=Spell HUD Position +config.cast_command_name=Cast Spell Command Name +config.discoverspell_command_name=Discover Spell Command Name +config.ally_command_name=Set Ally Command Name +config.allies_command_name=View Allies Command Name -config.wizardry.mobs_immune_to_fire=Mobs Immune To Fire -config.wizardry.mobs_immune_to_ice=Mobs Immune To Ice -config.wizardry.mobs_immune_to_lightning=Mobs Immune To Lightning -config.wizardry.mobs_immune_to_wither=Mobs Immune To Wither -config.wizardry.mobs_immune_to_poison=Mobs Immune To Poison +config.mobs_immune_to_fire=Mobs Immune To Fire +config.mobs_immune_to_ice=Mobs Immune To Ice +config.mobs_immune_to_lightning=Mobs Immune To Lightning +config.mobs_immune_to_wither=Mobs Immune To Wither +config.mobs_immune_to_poison=Mobs Immune To Poison -config.wizardry.tower_rarity.tooltip=Rarity of wizard towers. Higher numbers are rarer. Set to 0 to disable wizard towers completely. -config.wizardry.ore_dimensions.tooltip=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! -config.wizardry.flower_dimensions.tooltip=List of dimension ids in which crystal flowers will generate. -config.wizardry.tower_dimensions.tooltip=List of dimension ids in which wizard towers will generate. -config.wizardry.spell_book_drop_chance.tooltip=The chance for mobs to drop a spell book when killed. The greater this number, the more often they will drop. Set to 0 to disable spell book drops. Set to 200 for guaranteed drops. -config.wizardry.generate_loot.tooltip=Whether to generate wizardry loot in dungeon chests. -config.wizardry.firebomb_is_craftable.tooltip=Whether firebombs can be crafted or not. -config.wizardry.poison_bomb_is_craftable.tooltip=Whether poison bombs can be crafted or not. -config.wizardry.smoke_bomb_is_craftable.tooltip=Whether smoke bombs can be crafted or not. -config.wizardry.use_alternate_scroll_recipe.tooltip=Whether to require a magic crystal in the shapeless crafting recipe for blank scrolls. Set to true if another mod adds a conflicting recipe. -config.wizardry.teleport_through_unbreakable_blocks.tooltip=Whether players are allowed to teleport through unbreakable blocks (e.g. bedrock) using the phase step spell. -config.wizardry.show_summoned_creature_names.tooltip=Whether to show summoned creatures' names and owners above their heads. -config.wizardry.friendly_fire.tooltip=Whether to allow players to damage their designated allies using magic. -config.wizardry.telekinetic_disarmament.tooltip=Whether to allow players to disarm other players using the telekinesis spell.wizardry: Set to false to prevent stealing of items. -config.wizardry.discovery_mode.tooltip=For those who like a sense of mystery! When set to true, spells you haven't cast yet will be unreadable until you cast them (on a per-world basis). Has no effect when in creative mode. Spells of identification will be unobtainable in survival mode if this is false. -config.wizardry.enable_shift_scrolling.tooltip=Whether you can switch between spells on a wand by scrolling with the mouse wheel while sneaking. Note that this will only affect you; other players connected to the same server obey their own settings. -config.wizardry.minion_revenge_targeting.tooltip=Whether summoned creatures can revenge attack their owner if their owner attacks them. -config.wizardry.player_damage_scaling.tooltip=Global damage scaling factor for the damage dealt by players casting spells, relative to 1. -config.wizardry.npc_damage_scaling.tooltip=Global damage scaling factor for the damage dealt by NPCs casting spells, relative to 1. -config.wizardry.cast_command_multiplier_limit.tooltip=Upper limit for the multipliers passed into the /cast command. This is here to stop players from accidentally breaking a world/server. Large blast mutipliers can cause extreme lag - you have been warned! -config.wizardry.summoned_creature_targets_whitelist.tooltip=List of names of entities which summoned creatures and wizards are allowed to attack, in addition to the defaults. Add mod creatures to this list if you want summoned creatures to attack them and they aren't already doing so. Entity names are not case sensitive. For mod entities, prefix with the mod ID (e.g. wizardry.wizard). -config.wizardry.summoned_creature_targets_blacklist.tooltip=List of names of entities which summoned creatures and wizards are specifically not allowed to attack, overriding the defaults and the whitelist. Add creatures to this list if allowing them to be attacked causes problems or is too destructive (removing creepers from this list is done at your own risk!). Entity names are not case sensitive. For mod entities, prefix with the mod ID (e.g. wizardry.wizard). -config.wizardry.spell_hud_position.tooltip=The position of the spell HUD. -config.wizardry.cast_command_name.tooltip=The name of the /cast command. This is what you type directly after the /; for example if this was set to 'magic' then instead of typing /cast you would type /magic instead. -config.wizardry.discoverspell_command_name.tooltip=The name of the /discoverspell command. This is what you type directly after the /; for example if this was set to 'magic' then instead of typing /discoverspell you would type /magic instead. -config.wizardry.ally_command_name.tooltip=The name of the /ally command. This is what you type directly after the /; for example if this was set to 'magic' then instead of typing /ally you would type /magic instead. -config.wizardry.allies_command_name.tooltip=The name of the /allies command. This is what you type directly after the /; for example if this was set to 'magic' then instead of typing /allies you would type /magic instead. +config.frost_potion_id=Frost Potion ID +config.transience_potion_id=Transience Potion ID +config.fireskin_potion_id=Fireskin Potion ID +config.ice_shroud_potion_id=Ice Shroud Potion ID +config.static_aura_potion_id=Static Aura Potion ID +config.decay_potion_id=Decay Potion ID +config.sixth_sense_potion_id=Sixth Sense Potion ID +config.arcane_jammer_potion_id=Arcane Jammer Potion ID +config.mind_trick_potion_id=Mind Trick Potion ID +config.mind_control_potion_id=Mind Control Potion ID +config.font_of_mana_potion_id=Font of Mana Potion ID +config.fear_potion_id=Fear Potion ID +config.magic_sword_enchantment_id=Magic Sword Enchantment ID +config.magic_bow_enchantment_id=Magic Bow Enchantment ID +config.flaming_weapon_enchantment_id=Flaming Weapon Enchantment ID +config.freezing_weapon_enchantment_id=Freezing Weapon Enchantment ID -config.wizardry.mobs_immune_to_fire.tooltip=List of names of entities that are immune to fire, in addition to the defaults. Add mod creatures to this list if you want them to be immune to fire magic and they aren't already. Entity names are not case sensitive. For mod entities, prefix with the mod ID (e.g. wizardry.wizard). -config.wizardry.mobs_immune_to_ice.tooltip=List of names of entities that are immune to ice, in addition to the defaults. Add mod creatures to this list if you want them to be immune to ice magic and they aren't already. Entity names are not case sensitive. For mod entities, prefix with the mod ID (e.g. wizardry.wizard). -config.wizardry.mobs_immune_to_lightning.tooltip=List of names of entities that are immune to lightning, in addition to the defaults. Add mod creatures to this list if you want them to be immune to lightning magic and they aren't already. Entity names are not case sensitive. For mod entities, prefix with the mod ID (e.g. wizardry.wizard). -config.wizardry.mobs_immune_to_wither.tooltip=List of names of entities that are immune to wither effects, in addition to the defaults. Add mod creatures to this list if you want them to be immune to withering magic and they aren't already. Entity names are not case sensitive. For mod entities, prefix with the mod ID (e.g. wizardry.wizard). -config.wizardry.mobs_immune_to_poison.tooltip=List of names of entities that are immune to poison, in addition to the defaults. Add mod creatures to this list if you want them to be immune to poison magic and they aren't already. Entity names are not case sensitive. For mod entities, prefix with the mod ID (e.g. wizardry.wizard). +config.tower_rarity.tooltip=Rarity of wizard towers. Higher numbers are rarer. Set to 0 to disable wizard towers completely. +config.ore_dimensions.tooltip=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! +config.flower_dimensions.tooltip=List of dimension ids in which crystal flowers will generate. +config.tower_dimensions.tooltip=List of dimension ids in which wizard towers will generate. +config.spell_book_drop_chance.tooltip=The chance for mobs to drop a spell book when killed. The greater this number, the more often they will drop. Set to 0 to disable spell book drops. Set to 200 for guaranteed drops. +config.generate_loot.tooltip=Whether to generate wizardry loot in dungeon chests. +config.firebomb_is_craftable.tooltip=Whether firebombs can be crafted or not. +config.poison_bomb_is_craftable.tooltip=Whether poison bombs can be crafted or not. +config.smoke_bomb_is_craftable.tooltip=Whether smoke bombs can be crafted or not. +config.use_alternate_scroll_recipe.tooltip=Whether to require a magic crystal in the shapeless crafting recipe for blank scrolls. Set to true if another mod adds a conflicting recipe. +config.teleport_through_unbreakable_blocks.tooltip=Whether players are allowed to teleport through unbreakable blocks (e.g. bedrock) using the phase step spell. +config.show_summoned_creature_names.tooltip=Whether to show summoned creatures' names and owners above their heads. +config.friendly_fire.tooltip=Whether to allow players to damage their designated allies using magic. +config.telekinetic_disarmament.tooltip=Whether to allow players to disarm other players using the telekinesis spell. Set to false to prevent stealing of items. +config.discovery_mode.tooltip=For those who like a sense of mystery! When set to true, spells you haven't cast yet will be unreadable until you cast them (on a per-world basis). Has no effect when in creative mode. Spells of identification will be unobtainable in survival mode if this is false. +config.enable_shift_scrolling.tooltip=Whether you can switch between spells on a wand by scrolling with the mouse wheel while sneaking. Note that this will only affect you; other players connected to the same server obey their own settings. +config.minion_revenge_targeting.tooltip=Whether summoned creatures can revenge attack their owner if their owner attacks them. +config.player_damage_scaling.tooltip=Global damage scaling factor for the damage dealt by players casting spells, relative to 1. +config.npc_damage_scaling.tooltip=Global damage scaling factor for the damage dealt by NPCs casting spells, relative to 1. +config.cast_command_multiplier_limit.tooltip=Upper limit for the multipliers passed into the /cast command. This is here to stop players from accidentally breaking a world/server. Large blast mutipliers can cause extreme lag - you have been warned! +config.summoned_creature_targets_whitelist.tooltip=List of names of entities which summoned creatures and wizards are allowed to attack, in addition to the defaults. Add mod creatures to this list if you want summoned creatures to attack them and they aren't already doing so. Entity names are not case sensitive. For mod entities, prefix with the mod ID (e.g. wizardry:Wizard). +config.summoned_creature_targets_blacklist.tooltip=List of names of entities which summoned creatures and wizards are specifically not allowed to attack, overriding the defaults and the whitelist. Add creatures to this list if allowing them to be attacked causes problems or is too destructive (removing creepers from this list is done at your own risk!). Entity names are not case sensitive. For mod entities, prefix with the mod ID (e.g. wizardry:Wizard). +config.spell_hud_position.tooltip=The position of the spell HUD. +config.cast_command_name.tooltip=The name of the /cast command. This is what you type directly after the /; for example if this was set to 'magic' then instead of typing /cast you would type /magic instead. +config.discoverspell_command_name.tooltip=The name of the /discoverspell command. This is what you type directly after the /; for example if this was set to 'magic' then instead of typing /discoverspell you would type /magic instead. +config.ally_command_name.tooltip=The name of the /ally command. This is what you type directly after the /; for example if this was set to 'magic' then instead of typing /ally you would type /magic instead. +config.allies_command_name.tooltip=The name of the /allies command. This is what you type directly after the /; for example if this was set to 'magic' then instead of typing /allies you would type /magic instead. + +config.mobs_immune_to_fire.tooltip=List of names of entities that are immune to fire, in addition to the defaults. Add mod creatures to this list if you want them to be immune to fire magic and they aren't already. Entity names are not case sensitive. For mod entities, prefix with the mod ID (e.g. wizardry:Wizard). +config.mobs_immune_to_ice.tooltip=List of names of entities that are immune to ice, in addition to the defaults. Add mod creatures to this list if you want them to be immune to ice magic and they aren't already. Entity names are not case sensitive. For mod entities, prefix with the mod ID (e.g. wizardry:Wizard). +config.mobs_immune_to_lightning.tooltip=List of names of entities that are immune to lightning, in addition to the defaults. Add mod creatures to this list if you want them to be immune to lightning magic and they aren't already. Entity names are not case sensitive. For mod entities, prefix with the mod ID (e.g. wizardry:Wizard). +config.mobs_immune_to_wither.tooltip=List of names of entities that are immune to wither effects, in addition to the defaults. Add mod creatures to this list if you want them to be immune to withering magic and they aren't already. Entity names are not case sensitive. For mod entities, prefix with the mod ID (e.g. wizardry:Wizard). +config.mobs_immune_to_poison.tooltip=List of names of entities that are immune to poison, in addition to the defaults. Add mod creatures to this list if you want them to be immune to poison magic and they aren't already. Entity names are not case sensitive. For mod entities, prefix with the mod ID (e.g. wizardry:Wizard). + +config.frost_potion_id.tooltip=The ID of the frost potion effect. Change if this conflicts with another mod. +config.transience_potion_id.tooltip=The ID of the transience potion effect. Change if this conflicts with another mod. +config.fireskin_potion_id.tooltip=The ID of the fireskin potion effect. Change if this conflicts with another mod. +config.ice_shroud_potion_id.tooltip=The ID of the ice shroud potion effect. Change if this conflicts with another mod. +config.static_aura_potion_id.tooltip=The ID of the static aura potion effect. Change if this conflicts with another mod. +config.decay_potion_id.tooltip=The ID of the decay potion effect. Change if this conflicts with another mod. +config.sixth_sense_potion_id.tooltip=The ID of the sixth sense potion effect. Change if this conflicts with another mod. +config.arcane_jammer_potion_id.tooltip=The ID of the arcane jammer potion effect. Change if this conflicts with another mod. +config.mind_trick_potion_id.tooltip=The ID of the mind trick potion effect. Change if this conflicts with another mod. +config.mind_control_potion_id.tooltip=The ID of the mind control potion effect. Change if this conflicts with another mod. +config.font_of_mana_potion_id.tooltip=The ID of the font of mana potion effect. Change if this conflicts with another mod. +config.fear_potion_id.tooltip=The ID of the fear potion effect. Change if this conflicts with another mod. +config.magic_sword_enchantment_id.tooltip=The ID of the magic sword enchantment. Change if this conflicts with another mod. +config.magic_bow_enchantment_id.tooltip=The ID of the magic bow enchantment. Change if this conflicts with another mod. +config.flaming_weapon_enchantment_id.tooltip=The ID of the flaming weapon enchantment. Change if this conflicts with another mod. +config.freezing_weapon_enchantment_id.tooltip=The ID of the freezing weapon enchantment. Change if this conflicts with another mod. wizard.debug=%1$s, %2$s, %3$s \ No newline at end of file diff --git a/src/main/resources/assets/wizardry/lang/es_ES.lang b/src/main/resources/assets/wizardry/lang/es_ES.lang index 38ffd4fc..fefdc2b2 100644 --- a/src/main/resources/assets/wizardry/lang/es_ES.lang +++ b/src/main/resources/assets/wizardry/lang/es_ES.lang @@ -1,272 +1,272 @@ -tile.wizardry:arcane_workbench.name=Mesa de Trabajo Arcana -tile.wizardry:crystal_ore.name=Mena de Cristal Magico -tile.wizardry:petrified_stone.name=Piedra Petrificada -tile.wizardry:ice_statue.name=Estatua de Hielo -tile.wizardry:crystal_flower.name=Flor de Cristal Magico -tile.wizardry:snare.name=Trampa -tile.wizardry:transportation_stone.name=Piedra de Transportacion -tile.wizardry:spectral_block.name=Bloque Espectral -tile.wizardry:crystal_block.name=Bloque de Cristal Magico +tile.arcaneWorkbench.name=Mesa de Trabajo Arcana +tile.crystalOre.name=Mena de Cristal Magico +tile.petrifiedStone.name=Piedra Petrificada +tile.iceStatue.name=Estatua de Hielo +tile.crystalFlower.name=Flor de Cristal Magico +tile.snare.name=Trampa +tile.transportationStone.name=Piedra de Transportacion +tile.spectralBlock.name=Bloque Espectral +tile.crystalBlock.name=Bloque de Cristal Magico -item.wizardry:magic_crystal.name=Cristal Magico -item.wizardry:magic_wand.name=Varita Magica -item.wizardry:apprentice_wand.name=Varita de Aprendiz -item.wizardry:advanced_wand.name=Varita Avanzada -item.wizardry:master_wand.name=Varita de Maestro -item.wizardry:spell_book.name=Libro de Hechizo +item.magicCrystal.name=Cristal Magico +item.magicWand.name=Varita Magica +item.apprenticeWand.name=Varita de Aprendiz +item.advancedWand.name=Varita Avanzada +item.masterWand.name=Varita de Maestro +item.spellBook.name=Libro de Hechizo -item.wizardry:arcane_tome.name=Tomo de lo Arcano -item.wizardry:arcane_tome.desc1=Mejora cualquier %1$s -item.wizardry:arcane_tome.desc2=varita a nivel %1$s +item.arcaneTome.name=Tomo de lo Arcano +item.arcaneTome.desc1=Mejora cualquier %1$s +item.arcaneTome.desc2=varita a nivel %1$s -item.wizardry:wizard_handbook.name=El Manual del Hechizero -item.wizardry:wizard_handbook.desc=por %1$s +item.wizardHandbook.name=El Manual del Hechizero +item.wizardHandbook.desc=por %1$s -item.wizardry:wand.buff=+%1$s %2$s de potencia -item.wizardry:wand.spell=Hechizo actual: %1$s -item.wizardry:wand.mana=Mana: %1$s/%2$s +item.wand.buff=+%1$s %2$s de potencia +item.wand.spell=Hechizo actual: %1$s +item.wand.mana=Mana: %1$s/%2$s -item.wizardry:wand.addally=%1$s ha sido anadido a tu lista de aliados -item.wizardry:wand.removeally=%1$s ha sido removido a tu lista de aliados +item.wand.addally=%1$s ha sido anadido a tu lista de aliados +item.wand.removeally=%1$s ha sido removido a tu lista de aliados -item.wizardry:basic_fire_wand.name=Varita de Ascuas -item.wizardry:basic_ice_wand.name=Varita de Escarcha -item.wizardry:basic_lightning_wand.name=Varita de Chispas -item.wizardry:basic_necromancy_wand.name=Varita de las Sombras -item.wizardry:basic_earth_wand.name=Varita del Bosque -item.wizardry:basic_sorcery_wand.name=Varita del Misterio -item.wizardry:basic_healing_wand.name=Varita de la Curacion +item.basicFireWand.name=Varita de Ascuas +item.basicIceWand.name=Varita de Escarcha +item.basicLightningWand.name=Varita de Chispas +item.basicNecromancyWand.name=Varita de las Sombras +item.basicEarthWand.name=Varita del Bosque +item.basicSorceryWand.name=Varita del Misterio +item.basicHealingWand.name=Varita de la Curacion -item.wizardry:apprentice_fire_wand.name=Varita del Piromano Aprendiz -item.wizardry:apprentice_ice_wand.name=Varita del Mago de Hielo Aprendiz -item.wizardry:apprentice_lightning_wand.name=Varita del Mago de Tormenta Aprendiz -item.wizardry:apprentice_necromancy_wand.name=Varita del Nigromante Aprendiz -item.wizardry:apprentice_earth_wand.name=Varita del Mago de Tierra Aprendiz -item.wizardry:apprentice_sorcery_wand.name=Varita del Hechicero Aprendiz -item.wizardry:apprentice_healing_wand.name=Varita del Sanador Aprendiz +item.apprenticeFireWand.name=Varita del Piromano Aprendiz +item.apprenticeIceWand.name=Varita del Mago de Hielo Aprendiz +item.apprenticeLightningWand.name=Varita del Mago de Tormenta Aprendiz +item.apprenticeNecromancyWand.name=Varita del Nigromante Aprendiz +item.apprenticeEarthWand.name=Varita del Mago de Tierra Aprendiz +item.apprenticeSorceryWand.name=Varita del Hechicero Aprendiz +item.apprenticeHealingWand.name=Varita del Sanador Aprendiz -item.wizardry:advanced_fire_wand.name=Varita del Piromano -item.wizardry:advanced_ice_wand.name=Varita del Mago de Hielo -item.wizardry:advanced_lightning_wand.name=Varita del Mago de Tormenta -item.wizardry:advanced_necromancy_wand.name=Varita del Nigromante -item.wizardry:advanced_earth_wand.name=Varita del Mago de Tierra -item.wizardry:advanced_sorcery_wand.name=Varita del Hechicero -item.wizardry:advanced_healing_wand.name=Varita del Sanador +item.advancedFireWand.name=Varita del Piromano +item.advancedIceWand.name=Varita del Mago de Hielo +item.advancedLightningWand.name=Varita del Mago de Tormenta +item.advancedNecromancyWand.name=Varita del Nigromante +item.advancedEarthWand.name=Varita del Mago de Tierra +item.advancedSorceryWand.name=Varita del Hechicero +item.advancedHealingWand.name=Varita del Sanador -item.wizardry:master_fire_wand.name=Varita del Piromano Maestro -item.wizardry:master_ice_wand.name=Varita del Mago de Hielo Maestro -item.wizardry:master_lightning_wand.name=Varita del Mago de Tormenta Maestro -item.wizardry:master_necromancy_wand.name=Varita del Nigromante Maestro -item.wizardry:master_earth_wand.name=Varita del Mago de Tierra Maestro -item.wizardry:master_sorcery_wand.name=Varita del Hechicero Maestro -item.wizardry:master_healing_wand.name=Varita del Sanador Maestro +item.masterFireWand.name=Varita del Piromano Maestro +item.masterIceWand.name=Varita del Mago de Hielo Maestro +item.masterLightningWand.name=Varita del Mago de Tormenta Maestro +item.masterNecromancyWand.name=Varita del Nigromante Maestro +item.masterEarthWand.name=Varita del Mago de Tierra Maestro +item.masterSorceryWand.name=Varita del Hechicero Maestro +item.masterHealingWand.name=Varita del Sanador Maestro -item.wizardry:spectral_sword.name=Espada Espectral -item.wizardry:spectral_pickaxe.name=Pico Espectral -item.wizardry:spectral_bow.name=Arco Espectral +item.spectralSword.name=Espada Espectral +item.spectralPickaxe.name=Pico Espectral +item.spectralBow.name=Arco Espectral -item.wizardry:mana_flask.name=Frasco de Mana -item.wizardry:storage_upgrade.name=Mejora de Almacenamiento para Varita -item.wizardry:siphon_upgrade.name=Mejora de Sifon para Varita -item.wizardry:condenser_upgrade.name=Mejora de Condensador para Varita -item.wizardry:range_upgrade.name=Mejora de Rango para Varita -item.wizardry:duration_upgrade.name=Mejora de Duracion para Varita -item.wizardry:cooldown_upgrade.name=Mejora de Tiempo de Reactivacion para Varita -item.wizardry:blast_upgrade.name=Mejora de Explosion para Varita -item.wizardry:attunement_upgrade.name=Mejora de Sintonizacion para Varita +item.manaFlask.name=Frasco de Mana +item.storageUpgrade.name=Mejora de Almacenamiento para Varita +item.siphonUpgrade.name=Mejora de Sifon para Varita +item.condenserUpgrade.name=Mejora de Condensador para Varita +item.rangeUpgrade.name=Mejora de Rango para Varita +item.durationUpgrade.name=Mejora de Duracion para Varita +item.cooldownUpgrade.name=Mejora de Tiempo de Reactivacion para Varita +item.blastUpgrade.name=Mejora de Explosion para Varita +item.attunementUpgrade.name=Mejora de Sintonizacion para Varita -item.wizardry:flaming_axe.name=Hacha Llameante -item.wizardry:frost_axe.name=Hacha Escarchada +item.flamingAxe.name=Hacha Llameante +item.frostAxe.name=Hacha Escarchada -item.wizardry:firebomb.name=Bomba de fuego -item.wizardry:poison_bomb.name=Bomba de veneno -item.wizardry:smoke_bomb.name=Bomba de humo +item.firebomb.name=Bomba de fuego +item.poisonBomb.name=Bomba de veneno +item.smoke_bomb.name=Bomba de humo -item.wizardry:blank_scroll.name=Pergamino en blanco -item.wizardry:scroll.name=Pergamino de %1$s -item.wizardry:scroll.undiscovered.name=Pergamino "%1$s" -item.wizardry:identification_scroll.name=Pergamino de la Identificacion -item.wizardry:identification_scroll.desc1=%1$sIdentifica un libro o pergamino -item.wizardry:identification_scroll.desc2=%1$sdesconocido -item.wizardry:identification_scroll.nothing_to_identify=Nada que identificar! +item.blankScroll.name=Pergamino en blanco +item.scroll.name=Pergamino de %1$s +item.scroll.undiscovered.name=Pergamino "%1$s" +item.identification_scroll.name=Pergamino de la Identificacion +item.identification_scroll.desc1=%1$sIdentifica un libro o pergamino +item.identification_scroll.desc2=%1$sdesconocido +item.identification_scroll.nothing_to_identify=Nada que identificar! -item.wizardry:armour_upgrade.name=Sello Arcano de la Proteccion -item.wizardry:armour_upgrade.desc1=%1$sMejora cualquier armadura -item.wizardry:armour_upgrade.desc2=%1$spara volverla %2$slegendaria +item.armourUpgrade.name=Sello Arcano de la Proteccion +item.armourUpgrade.desc1=%1$sMejora cualquier armadura +item.armourUpgrade.desc2=%1$spara volverla %2$slegendaria -item.wizardry:magic_silk.name=Seda Magica +item.magicSilk.name=Seda Magica -item.wizardry:wizard_armour.legendary=Legendario -item.wizardry:wizard_armour.buff=-%1$s %2$s costo -item.wizardry:wizard_armour.mana=Mana: %1$s/%2$s +item.wizardArmour.legendary=Legendario +item.wizardArmour.buff=-%1$s %2$s costo +item.wizardArmour.mana=Mana: %1$s/%2$s -item.wizardry:wizard_hat.name=Sombrero de Mago -item.wizardry:wizard_robe.name=Tunica de Mago -item.wizardry:wizard_leggings.name=Pantalones de Mago -item.wizardry:wizard_boots.name=Botas de Mago +item.wizard_hat.name=Sombrero de Mago +item.wizard_robe.name=Tunica de Mago +item.wizard_leggings.name=Pantalones de Mago +item.wizard_boots.name=Botas de Mago -item.wizardry:wizard_hat_fire.name=Sombrero de Piromano -item.wizardry:wizard_robe_fire.name=Tunica de Piromano -item.wizardry:wizard_leggings_fire.name=Pantalones de Piromano -item.wizardry:wizard_boots_fire.name=Botas de Piromano +item.wizard_hat_fire.name=Sombrero de Piromano +item.wizard_robe_fire.name=Tunica de Piromano +item.wizard_leggings_fire.name=Pantalones de Piromano +item.wizard_boots_fire.name=Botas de Piromano -item.wizardry:wizard_hat_ice.name=Sombrero de Mago de Hielo -item.wizardry:wizard_robe_ice.name=Tunica de Mago de Hielo -item.wizardry:wizard_leggings_ice.name=Pantalones de Mago de Hielo -item.wizardry:wizard_boots_ice.name=Botas de Mago de Hielo +item.wizard_hat_ice.name=Sombrero de Mago de Hielo +item.wizard_robe_ice.name=Tunica de Mago de Hielo +item.wizard_leggings_ice.name=Pantalones de Mago de Hielo +item.wizard_boots_ice.name=Botas de Mago de Hielo -item.wizardry:wizard_hat_lightning.name=Sombrero de Mago de Tormenta -item.wizardry:wizard_robe_lightning.name=Tunica de Mago de Tormenta -item.wizardry:wizard_leggings_lightning.name=Pantalones de Mago de Tormenta -item.wizardry:wizard_boots_lightning.name=Botas de Mago de Tormenta +item.wizard_hat_lightning.name=Sombrero de Mago de Tormenta +item.wizard_robe_lightning.name=Tunica de Mago de Tormenta +item.wizard_leggings_lightning.name=Pantalones de Mago de Tormenta +item.wizard_boots_lightning.name=Botas de Mago de Tormenta -item.wizardry:wizard_hat_necromancy.name=Sombrero de Nigromante -item.wizardry:wizard_robe_necromancy.name=Tunica de Nigromante -item.wizardry:wizard_leggings_necromancy.name=Pantalones de Nigromante -item.wizardry:wizard_boots_necromancy.name=Botas de Nigromante +item.wizard_hat_necromancy.name=Sombrero de Nigromante +item.wizard_robe_necromancy.name=Tunica de Nigromante +item.wizard_leggings_necromancy.name=Pantalones de Nigromante +item.wizard_boots_necromancy.name=Botas de Nigromante -item.wizardry:wizard_hat_earth.name=Sombrero de Mago de Tierra -item.wizardry:wizard_robe_earth.name=Tunica de Mago de Tierra -item.wizardry:wizard_leggings_earth.name=Pantalones de Mago de Tierra -item.wizardry:wizard_boots_earth.name=Botas de Mago de Tierra +item.wizard_hat_earth.name=Sombrero de Mago de Tierra +item.wizard_robe_earth.name=Tunica de Mago de Tierra +item.wizard_leggings_earth.name=Pantalones de Mago de Tierra +item.wizard_boots_earth.name=Botas de Mago de Tierra -item.wizardry:wizard_hat_sorcery.name=Sombrero de Hechicero -item.wizardry:wizard_robe_sorcery.name=Tunica de Hechicero -item.wizardry:wizard_leggings_sorcery.name=Pantalones de Hechicero -item.wizardry:wizard_boots_sorcery.name=Botas de Hechicero +item.wizard_hat_sorcery.name=Sombrero de Hechicero +item.wizard_robe_sorcery.name=Tunica de Hechicero +item.wizard_leggings_sorcery.name=Pantalones de Hechicero +item.wizard_boots_sorcery.name=Botas de Hechicero -item.wizardry:wizard_hat_healing.name=Sombrero de Sanador -item.wizardry:wizard_robe_healing.name=Tunica de Sanador -item.wizardry:wizard_leggings_healing.name=Pantalones de Sanador -item.wizardry:wizard_boots_healing.name=Botas de Sanador +item.wizard_hat_healing.name=Sombrero de Sanador +item.wizard_robe_healing.name=Tunica de Sanador +item.wizard_leggings_healing.name=Pantalones de Sanador +item.wizard_boots_healing.name=Botas de Sanador -item.wizardry:spawn_wizard.name=Invocar Mago -item.wizardry:spawn_evil_wizard.name=Invocar Mago Malvado +item.spawn_wizard.name=Invocar Mago +item.spawn_evil_wizard.name=Invocar Mago Malvado -item.wizardry:spectral_helmet.name=Casco Espectral -item.wizardry:spectral_chestplate.name=Pechera Espectral -item.wizardry:spectral_leggings.name=Grebas Espectrales -item.wizardry:spectral_boots.name=Botas Espectrales +item.spectral_helmet.name=Casco Espectral +item.spectral_chestplate.name=Pechera Espectral +item.spectral_leggings.name=Grebas Espectrales +item.spectral_boots.name=Botas Espectrales entity.wizardry.summonedcreature.nameplate=%2$s de %1$s -entity.wizardry.zombie_minion.name=Zombi -entity.wizardry.skeleton_minion.name=Esqueleto -entity.wizardry.spider_minion.name=Arana -entity.wizardry.blaze_minion.name=Blaze -entity.wizardry.ice_wraith.name=Espectro de Hielo -entity.wizardry.lightning_wraith.name=Espectro de Relampago -entity.wizardry.shadow_wraith.name=Espectro de las Sombras -entity.wizardry.spirit_wolf.name=Lobo Espiritual -entity.wizardry.spirit_horse.name=Caballo Espiritual -entity.wizardry.ice_giant.name=Gigante de Hielo -entity.wizardry.phoenix.name=Fenix -entity.wizardry.wizard.name=Mago -entity.wizardry.magic_slime.name=Slime Magico -entity.wizardry.silverfish_minion.name=Lepisma -entity.wizardry.storm_elemental.name=Elemental de Tormenta -entity.wizardry.evil_wizard.name=Mago Malvado -entity.wizardry.decoy.name=Senuelo +entity.wizardry.Zombie Minion.name=Zombi +entity.wizardry.Skeleton Minion.name=Esqueleto +entity.wizardry.Spider Minion.name=Arana +entity.wizardry.Blaze Minion.name=Blaze +entity.wizardry.Ice Wraith.name=Espectro de Hielo +entity.wizardry.Lightning Wraith.name=Espectro de Relampago +entity.wizardry.Shadow Wraith.name=Espectro de las Sombras +entity.wizardry.Spirit Wolf.name=Lobo Espiritual +entity.wizardry.Spirit Horse.name=Caballo Espiritual +entity.wizardry.Ice Giant.name=Gigante de Hielo +entity.wizardry.Phoenix.name=Fenix +entity.wizardry.Wizard.name=Mago +entity.wizardry.Magic Slime.name=Slime Magico +entity.wizardry.Silverfish Minion.name=Lepisma +entity.wizardry.Storm Elemental.name=Elemental de Tormenta +entity.wizardry.Evil Wizard.name=Mago Malvado +entity.wizardry.Decoy.name=Senuelo -entity.wizardry.magic_missile.name=Magia -entity.wizardry.arc.name=Magia -entity.wizardry.spark_bomb.name=Magia -entity.wizardry.ice_shard.name=Magia -entity.wizardry.firebomb.name=Magic -entity.wizardry.poison_bomb.name=Magia -entity.wizardry.force_orb.name=Magia -entity.wizardry.spark.name=Magia -entity.wizardry.darkness_orb.name=Magia -entity.wizardry.fire_sigil.name=Magia -entity.wizardry.frost_sigil.name=Magia -entity.wizardry.lightning_sigil.name=Magia -entity.wizardry.lightning_arrow.name=Magia -entity.wizardry.firebolt.name=Magia -entity.wizardry.ice_charge.name=Magia -entity.wizardry.force_arrow.name=Magia -entity.wizardry.dart.name=Magia -entity.wizardry.lightning_disc.name=Magia -entity.wizardry.thunderbolt.name=Magia -entity.wizardry.decay.name=Magia -entity.wizardry.ice_lance.name=Magia -entity.wizardry.smoke_bomb.name=Magia -entity.wizardry.ice_spike.name=Magia +entity.wizardry.Magic Missile.name=Magia +entity.wizardry.Arc.name=Magia +entity.wizardry.Spark Bomb.name=Magia +entity.wizardry.Ice Shard.name=Magia +entity.wizardry.Firebomb.name=Magic +entity.wizardry.Poison Bomb.name=Magia +entity.wizardry.Force Orb.name=Magia +entity.wizardry.Spark.name=Magia +entity.wizardry.Darkness Orb.name=Magia +entity.wizardry.Fire Sigil.name=Magia +entity.wizardry.Frost Sigil.name=Magia +entity.wizardry.Lightning Sigil.name=Magia +entity.wizardry.Lightning Arrow.name=Magia +entity.wizardry.Firebolt.name=Magia +entity.wizardry.Ice Charge.name=Magia +entity.wizardry.Force Arrow.name=Magia +entity.wizardry.Dart.name=Magia +entity.wizardry.Lightning Disc.name=Magia +entity.wizardry.Thunderbolt.name=Magia +entity.wizardry.Decay.name=Magia +entity.wizardry.Ice Lance.name=Magia +entity.wizardry.Smoke Bomb.name=Magia +entity.wizardry.Ice Spike.name=Magia -entity.wizardry.black_hole.name=Agujero Negro -entity.wizardry.shield.name=Escudo -entity.wizardry.meteor.name=Meteoro -entity.wizardry.blizzard.name=Ventisca -entity.wizardry.bubble.name=Burbuja -entity.wizardry.tornado.name=Tornado -entity.wizardry.lightning_hammer.name=Martillo Relampago -entity.wizardry.arrow_rain.name=Lluvia de Flechas -entity.wizardry.healing_aura.name=Aura Curador -entity.wizardry.forcefield.name=Campo de Fuerza -entity.wizardry.ring_of_fire.name=Anillo de Fuego -entity.wizardry.earthquake.name=Terremoto -entity.wizardry.falling_grass.name=Hierba Descendiente -entity.wizardry.hailstorm.name=Tormenta de Granizo -entity.wizardry.lightning_pulse.name=Pulso de Relampago +entity.wizardry.Black Hole.name=Agujero Negro +entity.wizardry.Shield.name=Escudo +entity.wizardry.Meteor.name=Meteoro +entity.wizardry.Blizzard.name=Ventisca +entity.wizardry.Bubble.name=Burbuja +entity.wizardry.Tornado.name=Tornado +entity.wizardry.Lightning Hammer.name=Martillo Relampago +entity.wizardry.Arrow Rain.name=Lluvia de Flechas +entity.wizardry.Healing Aura.name=Aura Curador +entity.wizardry.Forcefield.name=Campo de Fuerza +entity.wizardry.Ring of Fire.name=Anillo de Fuego +entity.wizardry.Earthquake.name=Terremoto +entity.wizardry.Falling Grass.name=Hierba Descendiente +entity.wizardry.Hailstorm.name=Tormenta de Granizo +entity.wizardry.Lightning Pulse.name=Pulso de Relampago -item_group.wizardry=Wizardry -item_group.wizardryspells=Hechizos de Wizardry +itemGroup.wizardry=Wizardry +itemGroup.wizardryspells=Hechizos de Wizardry -achievement.crystal=Un Cristral Curioso... -achievement.crystal.desc=Pica un Cristal Magico -achievement.arcane_initiate=Inicio Arcano -achievement.arcane_initiate.desc=Craftea una varita magica con una pepita de oro, un palo y un cristal magico -achievement.apprentice=Aprendiz de Mago -achievement.apprentice.desc=Usa un tomo de lo arcano para mejorar tu varita -achievement.master=Maestro Arcano -achievement.master.desc=Obten una varita de maestro -achievement.all_spells=Mago de Todos los Oficios -achievement.all_spells.desc=Lanza todos los hechizos disponibles -achievement.wizard_trade=Comercio Magico -achievement.wizard_trade.desc=Compra un objeto a un mago -achievement.buy_master_spell=El Conocimiento es Poder -achievement.buy_master_spell.wizardry:desc=Compra un hechizo maestro a un mago -achievement.freeze_blaze=Ya No Estas Tan Caliente -achievement.freeze_blaze.desc=Congela a un blaze -achievement.charge_creeper=Va a Explotar!! -achievement.charge_creeper.desc='Accidentalmente' carga un creeper -achievement.frankenstein=Frankenstein -achievement.frankenstein.desc=Convierte a un cerdo en un hombrecerdo zombi usando el hechizo de rayo -achievement.special_upgrade=Remiendo Arcano -achievement.special_upgrade.desc=Aplica una mejora a una varita -achievement.craft_flask=Es Magia, Embotellada! -achievement.craft_flask.desc=Craftea un frasco de mana -achievement.elemental=Elemental -achievement.elemental.desc=Obten una varita elemental -achievement.armour_set=Now Eres un Mago Apropiado -achievement.armour_set.desc=Craftea y equipa el set completo de armadura de mago -achievement.legendary=Legendario -achievement.legendary.desc=Obten una pieza de armadura de mago legendaria -achievement.self_destruct=Tiro por la culata -achievement.self_destruct.desc=Muere por tu propia magia -achievement.pig_tornado=No De Nuevo... -achievement.pig_tornado.desc=Monta un cerdo hacia un tornado -achievement.jam_wizard=Sesion de Interferencia -achievement.jam_wizard.desc=Usa el hechizo arcano de interferencia en un mago -achievement.slime_skeleton=Situacion Pegajosa -achievement.slime_skeleton.desc=Sumerge un esqueleto en slime -achievement.anger_wizard=Te Arrepentiras de Eso -achievement.anger_wizard.desc=Haz que un mago se enoje -achievement.defeat_evil_wizard=Justicia -achievement.defeat_evil_wizard.desc=Derrota a un mago malvado -achievement.max_out_wand=Equipada Al Maximo! -achievement.max_out_wand.desc=Aplica el maximo numero de mejoras a una varita maestra -achievement.element_master=Maestria Elemental -achievement.element_master.desc=Lanza todos los hechizos de cualquier elemento -achievement.identify_spell=Apreciacion Arcana -achievement.identify_spell.wizardry:desc=Usa un pergamino de la identificacion para identificar un libro de hechizos o pergamino +achievement.Crystal=Un Cristral Curioso... +achievement.Crystal.desc=Pica un Cristal Magico +achievement.ArcaneInitiate=Inicio Arcano +achievement.ArcaneInitiate.desc=Craftea una varita magica con una pepita de oro, un palo y un cristal magico +achievement.Apprentice=Aprendiz de Mago +achievement.Apprentice.desc=Usa un tomo de lo arcano para mejorar tu varita +achievement.Master=Maestro Arcano +achievement.Master.desc=Obten una varita de maestro +achievement.AllSpells=Mago de Todos los Oficios +achievement.AllSpells.desc=Lanza todos los hechizos disponibles +achievement.WizardTrade=Comercio Magico +achievement.WizardTrade.desc=Compra un objeto a un mago +achievement.BuyMasterSpell=El Conocimiento es Poder +achievement.BuyMasterSpell.desc=Compra un hechizo maestro a un mago +achievement.FreezeBlaze=Ya No Estas Tan Caliente +achievement.FreezeBlaze.desc=Congela a un blaze +achievement.ChargeCreeper=Va a Explotar!! +achievement.ChargeCreeper.desc='Accidentalmente' carga un creeper +achievement.Frankenstein=Frankenstein +achievement.Frankenstein.desc=Convierte a un cerdo en un hombrecerdo zombi usando el hechizo de rayo +achievement.SpecialUpgrade=Remiendo Arcano +achievement.SpecialUpgrade.desc=Aplica una mejora a una varita +achievement.CraftFlask=Es Magia, Embotellada! +achievement.CraftFlask.desc=Craftea un frasco de mana +achievement.Elemental=Elemental +achievement.Elemental.desc=Obten una varita elemental +achievement.ArmourSet=Now Eres un Mago Apropiado +achievement.ArmourSet.desc=Craftea y equipa el set completo de armadura de mago +achievement.Legendary=Legendario +achievement.Legendary.desc=Obten una pieza de armadura de mago legendaria +achievement.SelfDestruct=Tiro por la culata +achievement.SelfDestruct.desc=Muere por tu propia magia +achievement.PigTornado=No De Nuevo... +achievement.PigTornado.desc=Monta un cerdo hacia un tornado +achievement.JamWizard=Sesion de Interferencia +achievement.JamWizard.desc=Usa el hechizo arcano de interferencia en un mago +achievement.SlimeSkeleton=Situacion Pegajosa +achievement.SlimeSkeleton.desc=Sumerge un esqueleto en slime +achievement.AngerWizard=Te Arrepentiras de Eso +achievement.AngerWizard.desc=Haz que un mago se enoje +achievement.DefeatEvilWizard=Justicia +achievement.DefeatEvilWizard.desc=Derrota a un mago malvado +achievement.MaxOutWand=Equipada Al Maximo! +achievement.MaxOutWand.desc=Aplica el maximo numero de mejoras a una varita maestra +achievement.ElementMaster=Maestria Elemental +achievement.ElementMaster.desc=Lanza todos los hechizos de cualquier elemento +achievement.IdentifySpell=Apreciacion Arcana +achievement.IdentifySpell.desc=Usa un pergamino de la identificacion para identificar un libro de hechizos o pergamino -tile.wizardry:transportation_stone.confirm=A partir de ahora retornaras a este lugar cuando lances %1$s -tile.wizardry:transportation_stone.invalid=Primero debes hacer un circulo con 8 piedras de transportacion! +tile.transportationStone.confirm=A partir de ahora retornaras a este lugar cuando lances %1$s +tile.transportationStone.invalid=Primero debes hacer un circulo con 8 piedras de transportacion! -container.wizardry:arcane_workbench=Mesa de Trabajo Arcana -container.wizardry:arcane_workbench.apply=Aplicar -container.wizardry:arcane_workbench.mana=Mana: -container.wizardry:arcane_workbench.upgrades=Mejoras Aplicadas: +container.arcaneWorkbench=Mesa de Trabajo Arcana +container.arcaneWorkbench.apply=Aplicar +container.arcaneWorkbench.mana=Mana: +container.arcaneWorkbench.upgrades=Mejoras Aplicadas: tier.basic=Novato tier.apprentice=Aprendiz @@ -300,437 +300,470 @@ spell.disabled=%1$s ha sido desactivado en la configuracion spell.resist=%1$s resistio %2$s spell.discover=Descubrio el hechizo %1$s! -spell.wizardry:agility=Agilidad -spell.wizardry:arc=Arco Electrico -spell.wizardry:arcane_jammer=Perturbador Arcano -spell.wizardry:arrow_rain=Lluvia de Flechas -spell.wizardry:banish=Desaparecer -spell.wizardry:black_hole=Agujero Negro -spell.wizardry:blink=Parpadeo -spell.wizardry:blizzard=Ventisca -spell.wizardry:bubble=Burbuja -spell.wizardry:chain_lightning=Cadena de Rayos -spell.wizardry:clairvoyance=Clarividencia -spell.wizardry:cobwebs=Telas de Araña -spell.wizardry:conjure_armour=Conjurar Armadura -spell.wizardry:conjure_bow=Conjurar Arco -spell.wizardry:conjure_pickaxe=Conjurar Pico -spell.wizardry:conjure_sword=Conjurar Espada -spell.wizardry:cure_effects=Purificar Efectos -spell.wizardry:curse_of_soulbinding=Maldicion de Atadura al Alma -spell.wizardry:darkness_orb=Orbe de la Oscuridad -spell.wizardry:darkvision=Vision Oscura -spell.wizardry:dart=Dardo -spell.wizardry:decay=Deterioro -spell.wizardry:decoy=Senuelo -spell.wizardry:detonate=Detonar -spell.wizardry:diamondflesh=Piel de Diamante -spell.wizardry:earthquake=Terremoto -spell.wizardry:entrapment=Atrapamiento -spell.wizardry:fireball=Bola de Fuego -spell.wizardry:firebolt=Disparo de Fuego -spell.wizardry:firebomb=Bomba de Fuego -spell.wizardry:fire_resistance=Resistencia al Fuego -spell.wizardry:fire_sigil=Sigilo de Fuego -spell.wizardry:fireskin=Piel de Fuego -spell.wizardry:firestorm=Tormenta de Fuego -spell.wizardry:flame_ray=Rayo de Fuego -spell.wizardry:flaming_axe=Hacha Llameante -spell.wizardry:flaming_weapon=Arma Llameante -spell.wizardry:flight=Vuelo -spell.wizardry:font_of_mana=Fuente de Mana -spell.wizardry:font_of_vitality=Fuente de Vitalidad -spell.wizardry:force_arrow=Flecha de Fuerza -spell.wizardry:forcefield=Campo de Fuerza -spell.wizardry:force_orb=Orbe de Fuerza -spell.wizardry:forests_curse=Maldicion del Bosque -spell.wizardry:freeze=Congelar -spell.wizardry:freezing_weapon=Arma Congelante -spell.wizardry:frost_axe=Hacha Escarchada -spell.wizardry:frost_ray=Rayo de Escarcha -spell.wizardry:frost_sigil=Sigilo de Escarcha -spell.wizardry:glide=Planear -spell.wizardry:greater_fireball=Bola de Fuego Mayor -spell.wizardry:greater_heal=Curacion Mayor -spell.wizardry:group_heal=Curacion Grupal -spell.wizardry:growth_aura= Aura de Crecimiento -spell.wizardry:hailstorm=Tormenta de Granizo -spell.wizardry:heal=Curar -spell.wizardry:heal_ally=curar Aliado -spell.wizardry:healing_aura=Aura de Curacion -spell.wizardry:homing_spark=Chispa Dirigida -spell.wizardry:ice_age=Era de Hielo -spell.wizardry:ice_charge=Carga de Hielo -spell.wizardry:ice_lance=Lanza de Hielo -spell.wizardry:ice_shard=Fragmento de Hielo -spell.wizardry:ice_shroud=Velo de Hielo -spell.wizardry:ice_spikes=Pinchos de Hielo -spell.wizardry:ice_statue=Estatua de Hielo -spell.wizardry:ignite=Encender -spell.wizardry:imbue_weapon=Imbuir Arma -spell.wizardry:intimidate=Intimidar -spell.wizardry:invigorating_presence=Presencia Vigorizante -spell.wizardry:invisibility=Invisibilidad -spell.wizardry:invoke_weather=Invocar clima -spell.wizardry:ironflesh=Piel de Hierro -spell.wizardry:leap=Salto -spell.wizardry:levitation=Levitacion -spell.wizardry:life_drain=Drenaje de Vida -spell.wizardry:light=Luz -spell.wizardry:lightning_arrow=Flecha de Rayo -spell.wizardry:lightning_bolt=Disparo de Rayo -spell.wizardry:lightning_disc=Disco de Rayo -spell.wizardry:lightning_hammer=Martillo Relampago -spell.wizardry:lightning_pulse=Pulso Electrico -spell.wizardry:lightning_ray=Rayo Electrico -spell.wizardry:lightning_sigil=Sigilo Electrico -spell.wizardry:lightning_web=Red Electrica -spell.wizardry:magic_missile=Misil Magico -spell.wizardry:metamorphosis=Metamorfosis -spell.wizardry:meteor=Meteoro -spell.wizardry:mind_control=Control Mental -spell.wizardry:mind_trick=Truco Mental -spell.wizardry:none=[Espacio Vacio] -spell.wizardry:oakflesh=Piel de Roble -spell.wizardry:petrify=Petrificar -spell.wizardry:phase_step=Paso de Fase -spell.wizardry:plague_of_darkness=Plaga de la Oscuridad -spell.wizardry:pocket_furnace=Horno del Bolsillo -spell.wizardry:pocket_workbench=Mesa de Trabajo de Bolsillo -spell.wizardry:poison=Veneno -spell.wizardry:poison_bomb=Bomba de Veneno -spell.wizardry:replenish_hunger=Rellenar hambre -spell.wizardry:ring_of_fire=Anillo de Fuego -spell.wizardry:shadow_ward=Guarda de las Sombras -spell.wizardry:shield=Escudo -spell.wizardry:shockwave=Onda de Choque -spell.wizardry:silverfish_swarm=Emjambre de Lepismas -spell.wizardry:sixth_sense=Sexto Sentido -spell.wizardry:slime=Slime -spell.wizardry:smoke_bomb=Bomba de Humo -spell.wizardry:snare=Trampa -spell.wizardry:snowball=Bola de Nieve -spell.wizardry:spark_bomb=Bomba de Chispa -spell.wizardry:spectral_pathway=Camino Espectral -spell.wizardry:spider_swarm=Emjambre de Arañas -spell.wizardry:static_aura=Aura Estatica -spell.wizardry:summon_blaze=Invocar Blaze -spell.wizardry:summon_ice_giant=Invocar Gigante de Hielo -spell.wizardry:summon_ice_wraith=Invocar Espectro de Hielo -spell.wizardry:summon_iron_golem=Invocar Golem de Hierro -spell.wizardry:summon_lightning_wraith=Invocar Espectro de Rayo -spell.wizardry:summon_phoenix=Invocar Fenix -spell.wizardry:summon_shadow_wraith=Invocar Espectro de las Sombras -spell.wizardry:summon_skeleton=Invocar Esqueleto -spell.wizardry:summon_skeleton_legion=Invocar Legion de Esqueletos -spell.wizardry:summon_snow_golem=Invocar Golem de Nieve -spell.wizardry:summon_spirit_horse=Invocar Caballo Espiritual -spell.wizardry:summon_spirit_wolf=Invocar Lobo Espiritual -spell.wizardry:summon_storm_elemental=Invocar Elemental del Rayo -spell.wizardry:summon_wither_skeleton=Invocar Esqueleto del Wither -spell.wizardry:summon_zombie=Invocar Zombi -spell.wizardry:telekinesis=Telequinesis -spell.wizardry:thunderbolt=Rayo -spell.wizardry:thunderstorm=Tormenta Electrica -spell.wizardry:tornado=Tornado -spell.wizardry:transience=Transitoriedad -spell.wizardry:transportation=Transportacion -spell.wizardry:vanishing_box=Caja Desvaneciente -spell.wizardry:wall_of_frost=Pared de Escarcha -spell.wizardry:water_breathing=Respiracion Acuatica -spell.wizardry:whirlwind=Remolino de Viento -spell.wizardry:wither=Marchitar -spell.wizardry:wither_skull=Craneo de Wither +spell.agility=Agilidad +spell.arc=Arco Electrico +spell.arcane_jammer=Perturbador Arcano +spell.arrow_rain=Lluvia de Flechas +spell.banish=Desaparecer +spell.black_hole=Agujero Negro +spell.blink=Parpadeo +spell.blizzard=Ventisca +spell.bubble=Burbuja +spell.chain_lightning=Cadena de Rayos +spell.clairvoyance=Clarividencia +spell.cobwebs=Telas de Araña +spell.conjure_armour=Conjurar Armadura +spell.conjure_bow=Conjurar Arco +spell.conjure_pickaxe=Conjurar Pico +spell.conjure_sword=Conjurar Espada +spell.cure_effects=Purificar Efectos +spell.curse_of_soulbinding=Maldicion de Atadura al Alma +spell.darkness_orb=Orbe de la Oscuridad +spell.darkvision=Vision Oscura +spell.dart=Dardo +spell.decay=Deterioro +spell.decoy=Senuelo +spell.detonate=Detonar +spell.diamondflesh=Piel de Diamante +spell.earthquake=Terremoto +spell.entrapment=Atrapamiento +spell.fireball=Bola de Fuego +spell.firebolt=Disparo de Fuego +spell.firebomb=Bomba de Fuego +spell.fire_resistance=Resistencia al Fuego +spell.fire_sigil=Sigilo de Fuego +spell.fireskin=Piel de Fuego +spell.firestorm=Tormenta de Fuego +spell.flame_ray=Rayo de Fuego +spell.flaming_axe=Hacha Llameante +spell.flaming_weapon=Arma Llameante +spell.flight=Vuelo +spell.font_of_mana=Fuente de Mana +spell.font_of_vitality=Fuente de Vitalidad +spell.force_arrow=Flecha de Fuerza +spell.forcefield=Campo de Fuerza +spell.force_orb=Orbe de Fuerza +spell.forests_curse=Maldicion del Bosque +spell.freeze=Congelar +spell.freezing_weapon=Arma Congelante +spell.frost_axe=Hacha Escarchada +spell.frost_ray=Rayo de Escarcha +spell.frost_sigil=Sigilo de Escarcha +spell.glide=Planear +spell.greater_fireball=Bola de Fuego Mayor +spell.greater_heal=Curacion Mayor +spell.group_heal=Curacion Grupal +spell.growth_aura= Aura de Crecimiento +spell.hailstorm=Tormenta de Granizo +spell.heal=Curar +spell.heal_ally=curar Aliado +spell.healing_aura=Aura de Curacion +spell.homing_spark=Chispa Dirigida +spell.ice_age=Era de Hielo +spell.ice_charge=Carga de Hielo +spell.ice_lance=Lanza de Hielo +spell.ice_shard=Fragmento de Hielo +spell.ice_shroud=Velo de Hielo +spell.ice_spikes=Pinchos de Hielo +spell.ice_statue=Estatua de Hielo +spell.ignite=Encender +spell.imbue_weapon=Imbuir Arma +spell.intimidate=Intimidar +spell.invigorating_presence=Presencia Vigorizante +spell.invisibility=Invisibilidad +spell.invoke_weather=Invocar clima +spell.ironflesh=Piel de Hierro +spell.leap=Salto +spell.levitation=Levitacion +spell.life_drain=Drenaje de Vida +spell.light=Luz +spell.lightning_arrow=Flecha de Rayo +spell.lightning_bolt=Disparo de Rayo +spell.lightning_disc=Disco de Rayo +spell.lightning_hammer=Martillo Relampago +spell.lightning_pulse=Pulso Electrico +spell.lightning_ray=Rayo Electrico +spell.lightning_sigil=Sigilo Electrico +spell.lightning_web=Red Electrica +spell.magic_missile=Misil Magico +spell.metamorphosis=Metamorfosis +spell.meteor=Meteoro +spell.mind_control=Control Mental +spell.mind_trick=Truco Mental +spell.none=[Espacio Vacio] +spell.oakflesh=Piel de Roble +spell.petrify=Petrificar +spell.phase_step=Paso de Fase +spell.plague_of_darkness=Plaga de la Oscuridad +spell.pocket_furnace=Horno del Bolsillo +spell.pocket_workbench=Mesa de Trabajo de Bolsillo +spell.poison=Veneno +spell.poison_bomb=Bomba de Veneno +spell.replenish_hunger=Rellenar hambre +spell.ring_of_fire=Anillo de Fuego +spell.shadow_ward=Guarda de las Sombras +spell.shield=Escudo +spell.shockwave=Onda de Choque +spell.silverfish_swarm=Emjambre de Lepismas +spell.sixth_sense=Sexto Sentido +spell.slime=Slime +spell.smoke_bomb=Bomba de Humo +spell.snare=Trampa +spell.snowball=Bola de Nieve +spell.spark_bomb=Bomba de Chispa +spell.spectral_pathway=Camino Espectral +spell.spider_swarm=Emjambre de Arañas +spell.static_aura=Aura Estatica +spell.summon_blaze=Invocar Blaze +spell.summon_ice_giant=Invocar Gigante de Hielo +spell.summon_ice_wraith=Invocar Espectro de Hielo +spell.summon_iron_golem=Invocar Golem de Hierro +spell.summon_lightning_wraith=Invocar Espectro de Rayo +spell.summon_phoenix=Invocar Fenix +spell.summon_shadow_wraith=Invocar Espectro de las Sombras +spell.summon_skeleton=Invocar Esqueleto +spell.summon_skeleton_legion=Invocar Legion de Esqueletos +spell.summon_snow_golem=Invocar Golem de Nieve +spell.summon_spirit_horse=Invocar Caballo Espiritual +spell.summon_spirit_wolf=Invocar Lobo Espiritual +spell.summon_storm_elemental=Invocar Elemental del Rayo +spell.summon_wither_skeleton=Invocar Esqueleto del Wither +spell.summon_zombie=Invocar Zombi +spell.telekinesis=Telequinesis +spell.thunderbolt=Rayo +spell.thunderstorm=Tormenta Electrica +spell.tornado=Tornado +spell.transience=Transitoriedad +spell.transportation=Transportacion +spell.vanishing_box=Caja Desvaneciente +spell.wall_of_frost=Pared de Escarcha +spell.water_breathing=Respiracion Acuatica +spell.whirlwind=Remolino de Viento +spell.wither=Marchitar +spell.wither_skull=Craneo de Wither -spell.wizardry:agility.desc=Concede al mago velocidad de movimiento incrementada y un salto mas alto por 30 segundos. -spell.wizardry:arc.desc=Dispara una chispa de rayo al objetivo. -spell.wizardry:arcane_jammer.desc=No permite que el usuario use magia en los proximos 15 segundos. -spell.wizardry:arrow_rain.desc="Arqueros, disparen!" -spell.wizardry:banish.desc=Teletransporta al objetivo una posicion cercana aleatoria en contra de su voluntad. -spell.wizardry:black_hole.desc=Desgarra la realidad. -spell.wizardry:blink.desc=Teletransporta al mago a la posicion que estan apuntando. -spell.wizardry:blizzard.desc=Crea una zona de aire frio, la cual ralentiza y hiere continuamente a cualquiera en su interior. El creador del hechizo es inmune al dano pero no al efecto. -spell.wizardry:bubble.desc=Dispara un chorro de burbujas que causa que lo primero que toque flote hacia el cielo. La burbuja explota despues de un tiempo o si el objetivo es golpeado. -spell.wizardry:chain_lightning.desc=Dispara un relampago a un objetivo, el cual hace cadena con objetivos cercanos hasta dos veces. -spell.wizardry:clairvoyance.desc=Revela el camino hacia una localizacion guardada. Con este hechizo seleccionado, haga shift-clic derecho en un bloque para guardarlo. Lanza este hechizo para revelar el camino. El camino desaparecera despues de 90 segundos. -spell.wizardry:cobwebs.desc=Crea telas de arana en la direccion a la cual estas apuntando, lo cual limita la movilidad de tu objetivo. Las telas de arana despareceran en 20 segundos o si son rotas. -spell.wizardry:conjure_armour.desc=Crea Armadura Espectral alrededor del mago lo cual ofrece proteccion similar a la armadura de hierro. La armadura dura 60 segundos. El mago debe tener el espacio de armadura vacio. -spell.wizardry:conjure_bow.desc=Crea un Arco Espectral con flechas ilimitadas que dura 30 segundos. -spell.wizardry:conjure_pickaxe.desc=Crea un Pico Espectral con fuerza igual a la de un Pico de Hierro, el cual dura 30 segundos. -spell.wizardry:conjure_sword.desc=Crea una Espada Espectral con igual fuerza que una de hierro y dura 30 segundos. -spell.wizardry:cure_effects.desc=Purifica al usuario de todos los efectos de pocion, ya sean buenos o malos. -spell.wizardry:curse_of_soulbinding.desc=Provoca que el alma del objetivo esta ligada al del mago, lo que significa que todo el dano que recibe el mago lo recibe el objetivo. La maldicion dura hasta que uno de los dos muera. -spell.wizardry:darkness_orb.desc=Dispara una bala de energia oscura en la direccion que apuntes, la cual aplicar wither. -spell.wizardry:darkvision.desc=Concede vision nocturna por 45 segundos. -spell.wizardry:dart.desc=Dispara un dardo en la direccion que estas apuntando el cual hiere y debilita al objetivo. -spell.wizardry:decay.desc=Crea un pezado de deterioro en el suelo lo cual infecta y hiere a cualquier criatura que lo pise, tambien causa que estas criatura sigan esparciendo el deterioro por donde caminen. -spell.wizardry:decoy.desc=Crea un clon ilusorio del mago que atrae a los enemigos. El senuelo desaparecera en 30 segundos. -spell.wizardry:detonate.desc=Causa un explosion donde estes mirando, hiriendo a todas las criaturas cercanas - incluyendo al mago si se encuentra muy cerca. -spell.wizardry:diamondflesh.desc="Tus flechas no son nada contra mi!" -spell.wizardry:earthquake.desc=Un verdadero de Mago de Tierra puede mover montanas. -spell.wizardry:entrapment.desc=Atrpa al objetivo en una esfera de oscuridad que lo eleva y hiere continuamente. -spell.wizardry:fireball.desc=Lanza una bola de fuego en la direccion a la que apuntes. -spell.wizardry:firebolt.desc=Dispara un chorro de fuego a corta distancia en frente de ti. -spell.wizardry:firebomb.desc=Lanza una bomba de fuego, la cual detona cuando impacta quemando todo a su alrededor. -spell.wizardry:fire_resistance.desc=Concede resistencia al fuego al mago por 30 segundos. -spell.wizardry:fire_sigil.desc=Coloca una trampa de fuego que quema y hiere a la criatura que la pise. -spell.wizardry:fireskin.desc=Envuelve al mago en fuego por 30 segundos, provocando que cualquier que lo toque se queme. -spell.wizardry:firestorm.desc="Yo soy el dragon." -spell.wizardry:flame_ray.desc=Crea una corriente de llamas en la direccion a la que apuntas, la cual quema todo a su paso. -spell.wizardry:flaming_axe.desc=Crea una Hacha Llameante que quema a todos los enemigos que golpees. Dura 30 segundos. -spell.wizardry:flaming_weapon.desc=Imbuye con llamas la primera arma en el inventario del mago, la magia de deshace despues de 45 segundos. -spell.wizardry:flight.desc=Vuela como un aguila. -spell.wizardry:font_of_mana.desc="Fuimos llenados con una intensa energia magica que parecia emanar del centro del..." - Extraido del diario de un mago olvidado; el resto de la pagina esta quemada. -spell.wizardry:font_of_vitality.desc=Se siente increible. -spell.wizardry:force_arrow.desc=Dispara una flecha de fuerza en la direccion que apuntes. -spell.wizardry:forcefield.desc=Crea un campo de fuerza alrededor del mago que repele criaturas y proyectiles. -spell.wizardry:force_orb.desc=Lanza una esfera de fuerza que hiere y empuja criaturas cuando impacta. -spell.wizardry:forests_curse.desc="Como te atreves a entrar en mi bosque!" -spell.wizardry:freeze.desc=Congela al objetivo por 10 segundos. Tambien congela el agua y crea nieve en el suelo. -spell.wizardry:freezing_weapon.desc=Imbuye el poder del frio a la primera arma en el inventario del mago, provocando que el arma congela a sus objetivos. La magia deshace despues de 45 segundos. -spell.wizardry:frost_axe.desc=Crea un Hacha Escarchada la cual congela los enemigos que golpees. Dura 30 segundos. -spell.wizardry:frost_ray.desc=Crea un corriente de hielo en la direccion que apuntes la cual ralentiza y hiere continuamente al objetivo. -spell.wizardry:frost_sigil.desc=Coloca un trampa de hielo en el suelo el cual hiere y congela al cualquiera que la pise. -spell.wizardry:glide.desc=Permite que el mago planee en el aire. Debe mantenerse el boton presionado. -spell.wizardry:greater_fireball.desc=Lanza una bola de fuego de mayor tamano. -spell.wizardry:greater_heal.desc=Cura 4 corazones del mago. -spell.wizardry:group_heal.desc=Cura 3 corazones al mago, a aliados cercanos y a criaturas invocadas. -spell.wizardry:growth_aura.desc=Crece todos los cultivos cercanos al mago. Tambien hace crecer flores y hierba alta. -spell.wizardry:hailstorm.desc=Fue durante el Gran Invierno de la 3era Edad que los Magos de Hielo descubrieron su verdadero potencial. -spell.wizardry:heal.desc=Cura 2 corazones del usuario. -spell.wizardry:heal_ally.desc=Cura 2.5 corazones al objetivo. -spell.wizardry:healing_aura.desc=Crea una zona de energia curadora que regenera la salud de todos los alidados cercanos. Los No-_muertos dentro reciben dano. -spell.wizardry:homing_spark.desc=Crea una chispa flotante que persigue enemigos. -spell.wizardry:ice_age.desc="Seras congelado por toda la eternidad!" -spell.wizardry:ice_charge.desc=Lanza una carga de hielo que explota cuando impacta, congelando todas las criaturas y soltando fragmentos en todas las direcciones. -spell.wizardry:ice_lance.desc=Dispara una gran lanza de hielo en la direccion a la que estas apuntando, la cual atraviesa los objetivos hieriendo y congelandolos. -spell.wizardry:ice_shard.desc=Dispara un fragemento de hielo que hace dano y ralentiza -spell.wizardry:ice_shroud.desc=Cubre al mago con hielo, lo que ocasiona que cualquier que lo ataque sea congelado. -spell.wizardry:ice_spikes.desc=Causa que pinchos de hielo crezcan desde el suelo hiriendo a los enemigos. -spell.wizardry:ice_statue.desc=Congela al objetivo durante 20 segundos. El objetivo no se puede mover ni atacar pero tambien es inmune al dano. -spell.wizardry:ignite.desc=Quema al objetivo. Tambien funciona como un mechero. -spell.wizardry:imbue_weapon.desc=Imbuye la primera arma en el inventario con magia, haciendola mas efectiva. La magia se deshace despues de 45 segundos. -spell.wizardry:intimidate.desc=Emite un grunido intimidante el cual provoca que las criaturas espacen. -spell.wizardry:invigorating_presence.desc=Concede al mago y a los aliados cercanos fuerza incrementada por 45 segundos. -spell.wizardry:invisibility.desc=Le otorga invisibilidad al mago por 30 segundos. -spell.wizardry:invoke_weather.desc=Cambia el clima del mundo. -spell.wizardry:ironflesh.desc=Mejora grandemente la resistencia del mago por 30 segundos. -spell.wizardry:leap.desc=Causa que el mago salte alto y se mueve levemente hacia adelante. -spell.wizardry:levitation.desc=Levanta al mago hacia el cielo mientras el boton siga presionado. Si es usando antes de golpear el suelo, anula el dano por caida. -spell.wizardry:life_drain.desc=Crea una correinte de energia oscura que drena la vida de tu objetivo y te cura. -spell.wizardry:light.desc=Crea una esfera de luz que alumbra el area. Dura 30 segundos. -spell.wizardry:lightning_arrow.desc=Lanza una flecha electrificada en la direccion que apuntes. -spell.wizardry:lightning_bolt.desc=Causa que un trueno caiga en la direccion que apuntes. -spell.wizardry:lightning_disc.desc=Envia un disco electrico en la direccion que apuntes, el cual busca objetivos. -spell.wizardry:lightning_hammer.desc="Te golpeare con la ira de los cielos!" -spell.wizardry:lightning_pulse.desc=Carga el suelo cerca del mago con corriente electrica, hiriendo y repeliendo criaturas cercanas. -spell.wizardry:lightning_ray.desc=Crea una corriente de energia electrica que hiere a tus objetivos continuamente. -spell.wizardry:lightning_sigil.desc=Coloca una trampa electrificada que electrocuta a la criatura que la pise. -spell.wizardry:lightning_web.desc="Concentrare. Canaliza la tormenta en tu mente a traves de tu varita y liberala." -spell.wizardry:magic_missile.desc=Dispara una bala de energia magica. -spell.wizardry:metamorphosis.desc=Cambia la forma del objetivo. Solo funciona en algunas criaturas. -spell.wizardry:meteor.desc=Algunos magos solo quieren ver el mundo arder... -spell.wizardry:mind_control.desc=Toma control de la mente del objetivo por 30 segundos, provocando que cambie de bando y peleen a tu favor. No funciona con criaturas de voluntad muy fuerte. -spell.wizardry:mind_trick.desc=Confunde y desorienta al objetivo por 15 segundos, volviendolo incapaz de atacar. El efecto se desactiva si el objetivo recibe dano. -spell.wizardry:none.desc=Para obtener un libro de hechizo con el comando /give, usa la metadata: /give [player] wizardry:spell_book 1 [spell id] (_si encontraste este libro en un cofre significa que otro mod esta alterando las cosas). -spell.wizardry:oakflesh.desc=Mejora la resistencia del amgo durante 30 segundos. -spell.wizardry:petrify.desc=Convierte al objetivo en piedra hasta que se libere, con un chance de liberarse cuando se pone de noche. El objetivo no se puede mover ni atacar y no puede ser atacado. -spell.wizardry:phase_step.desc=Transporta al mago a traves de una pared de 1 bloque de grosor. El rango mejora el grosor por el cual te puedes transportar. -spell.wizardry:plague_of_darkness.desc=La Oscuridad los consumira a todos... -spell.wizardry:pocket_furnace.desc=Cocina 5 objetos en el inventario. Los objetos en la barra seran cocinados primero. -spell.wizardry:pocket_workbench.desc=Permite al mago craftear objetos. -spell.wizardry:poison.desc=Dispara veneno en la direccion que apuntes. -spell.wizardry:poison_bomb.desc=Lanza una bomba de veneno, la cual detona esparciendo mas veneno. -spell.wizardry:replenish_hunger.desc=Rellena 6 puntos de hambre del mago. -spell.wizardry:ring_of_fire.desc=Crea un anillo de fuego alrededor del mago, hiriendo y quemando a los objetivos cercanos. -spell.wizardry:shadow_ward.desc=Crea una pared de oscurdiad en frente del mago lo cual hace que la mitad del dano recibido sea aplicado al atacante. -spell.wizardry:shield.desc=Crea una barrera protectora que bloquea proyectiles y magia. Tambien concede al mago un efecto debil de resistencia. -spell.wizardry:shockwave.desc=Boom. -spell.wizardry:silverfish_swarm.desc="Ahhhh! Se estan multiplicando!!" -spell.wizardry:sixth_sense.desc=Le permite al mago sentir las criaturas cercanas a el, incluso a traves de paredes, durante 20 segundos. -spell.wizardry:slime.desc=Envuelve al objetivo en slime lo cual ralentiza y hiere continuamente. El slime explota despues de 10 segundos. -spell.wizardry:smoke_bomb.desc=Lanza una bomba de humo la cual explota cuando impacta. Creando una nube de humo y dandole ceguera a las criaturas en el area de impacto. -spell.wizardry:snare.desc=Coloca un trampa en el suelo que hiere y ralentiza al que la pise. -spell.wizardry:snowball.desc=Lanza una bola de nieve. -spell.wizardry:spark_bomb.desc=Lanza una bomba electrica que electrocuta a los objetivos cerca de su zona de impacto. -spell.wizardry:spectral_pathway.desc=Crea un puente magico indestructible al frente del mago que se extiende a 15 bloques. El puente desaperece luego de 60 segundos. -spell.wizardry:spider_swarm.desc=Invoca un emjambre de aranas venenosas. Desapareceran despues de 3o segundos o si son eliminadas. -spell.wizardry:static_aura.desc=Rodea al mago de electricidad durante 30 segundos, disparando chispa de rayo a cualquiera que lo golpee. -spell.wizardry:summon_blaze.desc=Invoca un Blaze que pelea por ti. El blaze desparecera despues de 30 segundos o si es derrotado. -spell.wizardry:summon_ice_giant.desc="Smash them!" -spell.wizardry:summon_ice_wraith.desc=Invoca un Espectro de Hielo que pelea por ti. El Espectro de Hielo desparecera despues de 30 segundos o si es derrotado. -spell.wizardry:summon_iron_golem.desc=Autómata autónomo automatizado automático. -spell.wizardry:summon_lightning_wraith.desc=Invoca un Espectro de Rayo que pelea por ti. El Espectro de Rayo desparecera despues de 30 segundos o si es derrotado. -spell.wizardry:summon_phoenix.desc=Desde las cenizas... -spell.wizardry:summon_shadow_wraith.desc=Invoca un Espectro de las Sombras que pelea por ti. -spell.wizardry:summon_skeleton.desc=Invoca un Esqueleto que pelea por ti. El esqueleto desparecera despues de 30 segundos o si es derrotado. -spell.wizardry:summon_skeleton_legion.desc="Levantate, Ejercito No-_muerto!" -spell.wizardry:summon_snow_golem.desc=Crea un Golem de Nieve que pelea por ti. Dura hasta que el golem muera. -spell.wizardry:summon_spirit_horse.desc=Invoca un Caballo Espectral para que lo montes. El caballo desparecera despues de que dure mucho tiempo sin ser montado o hagas shift-clic derecho con cualquier varita. -spell.wizardry:summon_spirit_wolf.desc=Invoca un Lobo Espiritual que pelea por ti. El lobo solo desparecera si es matado o haces shift-clic derecho con cualquier varita. -spell.wizardry:summon_storm_elemental.desc="Elemental de Rayo: Una manifestacion antigua de los elementos, difícilmente puede contener la energía que se agita dentro de el." - Guia del Mago sobre Entidades Arcanas, Volumen I_i -spell.wizardry:summon_wither_skeleton.desc=Invoca un Esqueleto del Wither que pelea por ti. El esqueleto del wither desparecera despues de 30 segundos o si es derrotado. -spell.wizardry:summon_zombie.desc=Invoca un Zombi que pelea por ti. El zombi desparecera despues de 30 segundos o si es derrotado. -spell.wizardry:telekinesis.desc=Mueve un item o un objeto pequeno hacia ti si haces clic derecho sobre el bloque. Tambien puede desarmar jugadores. -spell.wizardry:thunderbolt.desc=Dispara una bala de trueno que empuja los enemigos. -spell.wizardry:thunderstorm.desc="Mwahahahahahaha!" -spell.wizardry:tornado.desc=Libera un tornado en la direccion que mires, el cual atrae y levanta a todos las criaturas en su paso. -spell.wizardry:transience.desc=Hace que el mago transcienda, volviendo inmune a todo dano pero volviendo incapaz de atacar, interactuar o romper bloques -spell.wizardry:transportation.desc=Teletransporta al mago a su circulo de piedra. Para guardar un circulo de piedra haz clic derecho con el hechizo seleccionado. -spell.wizardry:vanishing_box.desc=Le permite al mago accesar a su cofre de Ender. -spell.wizardry:wall_of_frost.desc=El invierno en la punta de tus dedos. -spell.wizardry:water_breathing.desc=Le permite al mago respirar debajo del agua durante 60 segundos. -spell.wizardry:whirlwind.desc=Causa que el objetivo sea soplado lejos de ti a gran velocidad. -spell.wizardry:wither.desc=Dispara un rayo de oscuridad que aplica wither a todo lo que toque. -spell.wizardry:wither_skull.desc=Lanza una Cabeza de Wither en la direccion que apuntes. +spell.agility.desc=Concede al mago velocidad de movimiento incrementada y un salto mas alto por 30 segundos. +spell.arc.desc=Dispara una chispa de rayo al objetivo. +spell.arcane_jammer.desc=No permite que el usuario use magia en los proximos 15 segundos. +spell.arrow_rain.desc="Arqueros, disparen!" +spell.banish.desc=Teletransporta al objetivo una posicion cercana aleatoria en contra de su voluntad. +spell.black_hole.desc=Desgarra la realidad. +spell.blink.desc=Teletransporta al mago a la posicion que estan apuntando. +spell.blizzard.desc=Crea una zona de aire frio, la cual ralentiza y hiere continuamente a cualquiera en su interior. El creador del hechizo es inmune al dano pero no al efecto. +spell.bubble.desc=Dispara un chorro de burbujas que causa que lo primero que toque flote hacia el cielo. La burbuja explota despues de un tiempo o si el objetivo es golpeado. +spell.chain_lightning.desc=Dispara un relampago a un objetivo, el cual hace cadena con objetivos cercanos hasta dos veces. +spell.clairvoyance.desc=Revela el camino hacia una localizacion guardada. Con este hechizo seleccionado, haga shift-clic derecho en un bloque para guardarlo. Lanza este hechizo para revelar el camino. El camino desaparecera despues de 90 segundos. +spell.cobwebs.desc=Crea telas de arana en la direccion a la cual estas apuntando, lo cual limita la movilidad de tu objetivo. Las telas de arana despareceran en 20 segundos o si son rotas. +spell.conjure_armour.desc=Crea Armadura Espectral alrededor del mago lo cual ofrece proteccion similar a la armadura de hierro. La armadura dura 60 segundos. El mago debe tener el espacio de armadura vacio. +spell.conjure_bow.desc=Crea un Arco Espectral con flechas ilimitadas que dura 30 segundos. +spell.conjure_pickaxe.desc=Crea un Pico Espectral con fuerza igual a la de un Pico de Hierro, el cual dura 30 segundos. +spell.conjure_sword.desc=Crea una Espada Espectral con igual fuerza que una de hierro y dura 30 segundos. +spell.cure_effects.desc=Purifica al usuario de todos los efectos de pocion, ya sean buenos o malos. +spell.curse_of_soulbinding.desc=Provoca que el alma del objetivo esta ligada al del mago, lo que significa que todo el dano que recibe el mago lo recibe el objetivo. La maldicion dura hasta que uno de los dos muera. +spell.darkness_orb.desc=Dispara una bala de energia oscura en la direccion que apuntes, la cual aplicar wither. +spell.darkvision.desc=Concede vision nocturna por 45 segundos. +spell.dart.desc=Dispara un dardo en la direccion que estas apuntando el cual hiere y debilita al objetivo. +spell.decay.desc=Crea un pezado de deterioro en el suelo lo cual infecta y hiere a cualquier criatura que lo pise, tambien causa que estas criatura sigan esparciendo el deterioro por donde caminen. +spell.decoy.desc=Crea un clon ilusorio del mago que atrae a los enemigos. El senuelo desaparecera en 30 segundos. +spell.detonate.desc=Causa un explosion donde estes mirando, hiriendo a todas las criaturas cercanas - incluyendo al mago si se encuentra muy cerca. +spell.diamondflesh.desc="Tus flechas no son nada contra mi!" +spell.earthquake.desc=Un verdadero de Mago de Tierra puede mover montanas. +spell.entrapment.desc=Atrpa al objetivo en una esfera de oscuridad que lo eleva y hiere continuamente. +spell.fireball.desc=Lanza una bola de fuego en la direccion a la que apuntes. +spell.firebolt.desc=Dispara un chorro de fuego a corta distancia en frente de ti. +spell.firebomb.desc=Lanza una bomba de fuego, la cual detona cuando impacta quemando todo a su alrededor. +spell.fire_resistance.desc=Concede resistencia al fuego al mago por 30 segundos. +spell.fire_sigil.desc=Coloca una trampa de fuego que quema y hiere a la criatura que la pise. +spell.fireskin.desc=Envuelve al mago en fuego por 30 segundos, provocando que cualquier que lo toque se queme. +spell.firestorm.desc="Yo soy el dragon." +spell.flame_ray.desc=Crea una corriente de llamas en la direccion a la que apuntas, la cual quema todo a su paso. +spell.flaming_axe.desc=Crea una Hacha Llameante que quema a todos los enemigos que golpees. Dura 30 segundos. +spell.flaming_weapon.desc=Imbuye con llamas la primera arma en el inventario del mago, la magia de deshace despues de 45 segundos. +spell.flight.desc=Vuela como un aguila. +spell.font_of_mana.desc="Fuimos llenados con una intensa energia magica que parecia emanar del centro del..." - Extraido del diario de un mago olvidado; el resto de la pagina esta quemada. +spell.font_of_vitality.desc=Se siente increible. +spell.force_arrow.desc=Dispara una flecha de fuerza en la direccion que apuntes. +spell.forcefield.desc=Crea un campo de fuerza alrededor del mago que repele criaturas y proyectiles. +spell.force_orb.desc=Lanza una esfera de fuerza que hiere y empuja criaturas cuando impacta. +spell.forests_curse.desc="Como te atreves a entrar en mi bosque!" +spell.freeze.desc=Congela al objetivo por 10 segundos. Tambien congela el agua y crea nieve en el suelo. +spell.freezing_weapon.desc=Imbuye el poder del frio a la primera arma en el inventario del mago, provocando que el arma congela a sus objetivos. La magia deshace despues de 45 segundos. +spell.frost_axe.desc=Crea un Hacha Escarchada la cual congela los enemigos que golpees. Dura 30 segundos. +spell.frost_ray.desc=Crea un corriente de hielo en la direccion que apuntes la cual ralentiza y hiere continuamente al objetivo. +spell.frost_sigil.desc=Coloca un trampa de hielo en el suelo el cual hiere y congela al cualquiera que la pise. +spell.glide.desc=Permite que el mago planee en el aire. Debe mantenerse el boton presionado. +spell.greater_fireball.desc=Lanza una bola de fuego de mayor tamano. +spell.greater_heal.desc=Cura 4 corazones del mago. +spell.group_heal.desc=Cura 3 corazones al mago, a aliados cercanos y a criaturas invocadas. +spell.growth_aura.desc=Crece todos los cultivos cercanos al mago. Tambien hace crecer flores y hierba alta. +spell.hailstorm.desc=Fue durante el Gran Invierno de la 3era Edad que los Magos de Hielo descubrieron su verdadero potencial. +spell.heal.desc=Cura 2 corazones del usuario. +spell.heal_ally.desc=Cura 2.5 corazones al objetivo. +spell.healing_aura.desc=Crea una zona de energia curadora que regenera la salud de todos los alidados cercanos. Los No-Muertos dentro reciben dano. +spell.homing_spark.desc=Crea una chispa flotante que persigue enemigos. +spell.ice_age.desc="Seras congelado por toda la eternidad!" +spell.ice_charge.desc=Lanza una carga de hielo que explota cuando impacta, congelando todas las criaturas y soltando fragmentos en todas las direcciones. +spell.ice_lance.desc=Dispara una gran lanza de hielo en la direccion a la que estas apuntando, la cual atraviesa los objetivos hieriendo y congelandolos. +spell.ice_shard.desc=Dispara un fragemento de hielo que hace dano y ralentiza +spell.ice_shroud.desc=Cubre al mago con hielo, lo que ocasiona que cualquier que lo ataque sea congelado. +spell.ice_spikes.desc=Causa que pinchos de hielo crezcan desde el suelo hiriendo a los enemigos. +spell.ice_statue.desc=Congela al objetivo durante 20 segundos. El objetivo no se puede mover ni atacar pero tambien es inmune al dano. +spell.ignite.desc=Quema al objetivo. Tambien funciona como un mechero. +spell.imbue_weapon.desc=Imbuye la primera arma en el inventario con magia, haciendola mas efectiva. La magia se deshace despues de 45 segundos. +spell.intimidate.desc=Emite un grunido intimidante el cual provoca que las criaturas espacen. +spell.invigorating_presence.desc=Concede al mago y a los aliados cercanos fuerza incrementada por 45 segundos. +spell.invisibility.desc=Le otorga invisibilidad al mago por 30 segundos. +spell.invoke_weather.desc=Cambia el clima del mundo. +spell.ironflesh.desc=Mejora grandemente la resistencia del mago por 30 segundos. +spell.leap.desc=Causa que el mago salte alto y se mueve levemente hacia adelante. +spell.levitation.desc=Levanta al mago hacia el cielo mientras el boton siga presionado. Si es usando antes de golpear el suelo, anula el dano por caida. +spell.life_drain.desc=Crea una correinte de energia oscura que drena la vida de tu objetivo y te cura. +spell.light.desc=Crea una esfera de luz que alumbra el area. Dura 30 segundos. +spell.lightning_arrow.desc=Lanza una flecha electrificada en la direccion que apuntes. +spell.lightning_bolt.desc=Causa que un trueno caiga en la direccion que apuntes. +spell.lightning_disc.desc=Envia un disco electrico en la direccion que apuntes, el cual busca objetivos. +spell.lightning_hammer.desc="Te golpeare con la ira de los cielos!" +spell.lightning_pulse.desc=Carga el suelo cerca del mago con corriente electrica, hiriendo y repeliendo criaturas cercanas. +spell.lightning_ray.desc=Crea una corriente de energia electrica que hiere a tus objetivos continuamente. +spell.lightning_sigil.desc=Coloca una trampa electrificada que electrocuta a la criatura que la pise. +spell.lightning_web.desc="Concentrare. Canaliza la tormenta en tu mente a traves de tu varita y liberala." +spell.magic_missile.desc=Dispara una bala de energia magica. +spell.metamorphosis.desc=Cambia la forma del objetivo. Solo funciona en algunas criaturas. +spell.meteor.desc=Algunos magos solo quieren ver el mundo arder... +spell.mind_control.desc=Toma control de la mente del objetivo por 30 segundos, provocando que cambie de bando y peleen a tu favor. No funciona con criaturas de voluntad muy fuerte. +spell.mind_trick.desc=Confunde y desorienta al objetivo por 15 segundos, volviendolo incapaz de atacar. El efecto se desactiva si el objetivo recibe dano. +spell.none.desc=Para obtener un libro de hechizo con el comando /give, usa la metadata: /give [player] wizardry:spell_book 1 [spell id] (Si encontraste este libro en un cofre significa que otro mod esta alterando las cosas). +spell.oakflesh.desc=Mejora la resistencia del amgo durante 30 segundos. +spell.petrify.desc=Convierte al objetivo en piedra hasta que se libere, con un chance de liberarse cuando se pone de noche. El objetivo no se puede mover ni atacar y no puede ser atacado. +spell.phase_step.desc=Transporta al mago a traves de una pared de 1 bloque de grosor. El rango mejora el grosor por el cual te puedes transportar. +spell.plague_of_darkness.desc=La Oscuridad los consumira a todos... +spell.pocket_furnace.desc=Cocina 5 objetos en el inventario. Los objetos en la barra seran cocinados primero. +spell.pocket_workbench.desc=Permite al mago craftear objetos. +spell.poison.desc=Dispara veneno en la direccion que apuntes. +spell.poison_bomb.desc=Lanza una bomba de veneno, la cual detona esparciendo mas veneno. +spell.replenish_hunger.desc=Rellena 6 puntos de hambre del mago. +spell.ring_of_fire.desc=Crea un anillo de fuego alrededor del mago, hiriendo y quemando a los objetivos cercanos. +spell.shadow_ward.desc=Crea una pared de oscurdiad en frente del mago lo cual hace que la mitad del dano recibido sea aplicado al atacante. +spell.shield.desc=Crea una barrera protectora que bloquea proyectiles y magia. Tambien concede al mago un efecto debil de resistencia. +spell.shockwave.desc=Boom. +spell.silverfish_swarm.desc="Ahhhh! Se estan multiplicando!!" +spell.sixth_sense.desc=Le permite al mago sentir las criaturas cercanas a el, incluso a traves de paredes, durante 20 segundos. +spell.slime.desc=Envuelve al objetivo en slime lo cual ralentiza y hiere continuamente. El slime explota despues de 10 segundos. +spell.smoke_bomb.desc=Lanza una bomba de humo la cual explota cuando impacta. Creando una nube de humo y dandole ceguera a las criaturas en el area de impacto. +spell.snare.desc=Coloca un trampa en el suelo que hiere y ralentiza al que la pise. +spell.snowball.desc=Lanza una bola de nieve. +spell.spark_bomb.desc=Lanza una bomba electrica que electrocuta a los objetivos cerca de su zona de impacto. +spell.spectral_pathway.desc=Crea un puente magico indestructible al frente del mago que se extiende a 15 bloques. El puente desaperece luego de 60 segundos. +spell.spider_swarm.desc=Invoca un emjambre de aranas venenosas. Desapareceran despues de 3o segundos o si son eliminadas. +spell.static_aura.desc=Rodea al mago de electricidad durante 30 segundos, disparando chispa de rayo a cualquiera que lo golpee. +spell.summon_blaze.desc=Invoca un Blaze que pelea por ti. El blaze desparecera despues de 30 segundos o si es derrotado. +spell.summon_ice_giant.desc="Smash them!" +spell.summon_ice_wraith.desc=Invoca un Espectro de Hielo que pelea por ti. El Espectro de Hielo desparecera despues de 30 segundos o si es derrotado. +spell.summon_iron_golem.desc=Autómata autónomo automatizado automático. +spell.summon_lightning_wraith.desc=Invoca un Espectro de Rayo que pelea por ti. El Espectro de Rayo desparecera despues de 30 segundos o si es derrotado. +spell.summon_phoenix.desc=Desde las cenizas... +spell.summon_shadow_wraith.desc=Invoca un Espectro de las Sombras que pelea por ti. +spell.summon_skeleton.desc=Invoca un Esqueleto que pelea por ti. El esqueleto desparecera despues de 30 segundos o si es derrotado. +spell.summon_skeleton_legion.desc="Levantate, Ejercito No-Muerto!" +spell.summon_snow_golem.desc=Crea un Golem de Nieve que pelea por ti. Dura hasta que el golem muera. +spell.summon_spirit_horse.desc=Invoca un Caballo Espectral para que lo montes. El caballo desparecera despues de que dure mucho tiempo sin ser montado o hagas shift-clic derecho con cualquier varita. +spell.summon_spirit_wolf.desc=Invoca un Lobo Espiritual que pelea por ti. El lobo solo desparecera si es matado o haces shift-clic derecho con cualquier varita. +spell.summon_storm_elemental.desc="Elemental de Rayo: Una manifestacion antigua de los elementos, difícilmente puede contener la energía que se agita dentro de el." - Guia del Mago sobre Entidades Arcanas, Volumen II +spell.summon_wither_skeleton.desc=Invoca un Esqueleto del Wither que pelea por ti. El esqueleto del wither desparecera despues de 30 segundos o si es derrotado. +spell.summon_zombie.desc=Invoca un Zombi que pelea por ti. El zombi desparecera despues de 30 segundos o si es derrotado. +spell.telekinesis.desc=Mueve un item o un objeto pequeno hacia ti si haces clic derecho sobre el bloque. Tambien puede desarmar jugadores. +spell.thunderbolt.desc=Dispara una bala de trueno que empuja los enemigos. +spell.thunderstorm.desc="Mwahahahahahaha!" +spell.tornado.desc=Libera un tornado en la direccion que mires, el cual atrae y levanta a todos las criaturas en su paso. +spell.transience.desc=Hace que el mago transcienda, volviendo inmune a todo dano pero volviendo incapaz de atacar, interactuar o romper bloques +spell.transportation.desc=Teletransporta al mago a su circulo de piedra. Para guardar un circulo de piedra haz clic derecho con el hechizo seleccionado. +spell.vanishing_box.desc=Le permite al mago accesar a su cofre de Ender. +spell.wall_of_frost.desc=El invierno en la punta de tus dedos. +spell.water_breathing.desc=Le permite al mago respirar debajo del agua durante 60 segundos. +spell.whirlwind.desc=Causa que el objetivo sea soplado lejos de ti a gran velocidad. +spell.wither.desc=Dispara un rayo de oscuridad que aplica wither a todo lo que toque. +spell.wither_skull.desc=Lanza una Cabeza de Wither en la direccion que apuntes. -spell.wizardry:invoke_weather.sun=La lluvia empieza a parar... -spell.wizardry:invoke_weather.rain=El cielo se abre... -spell.wizardry:transportation.missing=Tu circulo de piedra no se encuentra o esta siendo obstruido... -spell.wizardry:transportation.undefined=Debes guardar un circulo de piedra primero! -spell.wizardry:transportation.wrongdimension=Tu circulo de piedra se encuentra en otra dimension... -spell.wizardry:clairvoyance.searching=Buscando... -spell.wizardry:clairvoyance.confirm=El camino revelado al lanzar %1$s ahora te llevara a este punto -spell.wizardry:clairvoyance.outofrange=Tu punto guardado esta muy lejos o es inaccesible... -spell.wizardry:clairvoyance.undefined=Debes guardar una localizacion primero! -spell.wizardry:clairvoyance.wrongdimension=Tu localizacion guardada esta en otra dimension... +spell.invoke_weather.sun=La lluvia empieza a parar... +spell.invoke_weather.rain=El cielo se abre... +spell.transportation.missing=Tu circulo de piedra no se encuentra o esta siendo obstruido... +spell.transportation.undefined=Debes guardar un circulo de piedra primero! +spell.transportation.wrongdimension=Tu circulo de piedra se encuentra en otra dimension... +spell.clairvoyance.searching=Buscando... +spell.clairvoyance.confirm=El camino revelado al lanzar %1$s ahora te llevara a este punto +spell.clairvoyance.outofrange=Tu punto guardado esta muy lejos o es inaccesible... +spell.clairvoyance.undefined=Debes guardar una localizacion primero! +spell.clairvoyance.wrongdimension=Tu localizacion guardada esta en otra dimension... -potion.wizardry:frost=Congelacion -potion.wizardry:fireskin=Piel de Fuego -potion.wizardry:ice_shroud=Manto de Hielo -potion.wizardry:static_aura=Aura Estatica -potion.wizardry:transience=Transitoriedad -potion.wizardry:decay=Deterioro -potion.wizardry:sixth_sense=Sexto Sentido -potion.wizardry:arcane_jammer=Perturbador Arcano -potion.wizardry:mind_trick=Truco Mental -potion.wizardry:mind_control=Control Mental -potion.wizardry:font_of_mana=Fuente de Mana -potion.wizardry:fear=Miedo +potion.frost=Congelacion +potion.fireskin=Piel de Fuego +potion.ice_shroud=Manto de Hielo +potion.static_aura=Aura Estatica +potion.transience=Transitoriedad +potion.decay=Deterioro +potion.sixth_sense=Sexto Sentido +potion.arcane_jammer=Perturbador Arcano +potion.mind_trick=Truco Mental +potion.mind_control=Control Mental +potion.font_of_mana=Fuente de Mana +potion.fear=Miedo -enchantment.wizardry:magic_sword=Impregnacion -enchantment.wizardry:magic_bow=Impregnacion -enchantment.wizardry:flaming_weapon=Impregnacion de Fuego -enchantment.wizardry:freezing_weapon=Impregnacion de Fuego +enchantment.magic_sword=Impregnacion +enchantment.magic_bow=Impregnacion +enchantment.flaming_weapon=Impregnacion de Fuego +enchantment.freezing_weapon=Impregnacion de Fuego key.categories.wizardry=Wizardry -key.wizardry.next_spell=Hechizo siguiente -key.wizardry.previous_spell=Hechizo anterior +key.next_spell=Hechizo siguiente +key.previous_spell=Hechizo anterior -death.attack.wizardry_magic=%1$s fue matado por %2$s usando magia -death.attack.indirect_wizardry_magic=%1$s fue matado por %2$s usando magia +death.attack.wizardryMagic=%1$s fue matado por %2$s usando magia +death.attack.indirectWizardryMagic=%1$s fue matado por %2$s usando magia -commands.wizardry:cast.usage=/%1$s [player] [damage multiplier] [range multiplier] [duration multiplier] [blast multiplier] -commands.wizardry:cast.success=Lanzado con exito %1$s -commands.wizardry:cast.success_continuous=Lanzado con exito %1$s; repite el comando para parar -commands.wizardry:cast.success_remote=Lanzado con exito %1$s como %2$s -commands.wizardry:cast.success_remote_continuous=Lanzado con exito %1$s como %2$s; repite el comando para parar -commands.wizardry:cast.fail=Imposible lanzar %1$s -commands.wizardry:cast.not_found=No hay ningun hechizo con ID %1$s +commands.cast.usage=/%1$s [player] [damage multiplier] [range multiplier] [duration multiplier] [blast multiplier] +commands.cast.success=Lanzado con exito %1$s +commands.cast.success_continuous=Lanzado con exito %1$s; repite el comando para parar +commands.cast.success_remote=Lanzado con exito %1$s como %2$s +commands.cast.success_remote_continuous=Lanzado con exito %1$s como %2$s; repite el comando para parar +commands.cast.fail=Imposible lanzar %1$s +commands.cast.not_found=No hay ningun hechizo con ID %1$s -commands.wizardry:ally.usage=/%1$s [player] -commands.wizardry:ally.addally=%1$s ha sido anadido a la lista de aliados de %2$s -commands.wizardry:ally.removeally=%1$s ha sido removido de la lista de aliados de %2$s -commands.wizardry:ally.self=Los jugadores no pueden ser aliados de si mismos! -commands.wizardry:ally.permission=No tienes permiso para cambiar los aliados de otros jugadores +commands.ally.usage=/%1$s [player] +commands.ally.addally=%1$s ha sido anadido a la lista de aliados de %2$s +commands.ally.removeally=%1$s ha sido removido de la lista de aliados de %2$s +commands.ally.self=Los jugadores no pueden ser aliados de si mismos! +commands.ally.permission=No tienes permiso para cambiar los aliados de otros jugadores -commands.wizardry:allies.usage=/%1$s [player] -commands.wizardry:allies.list=Jugadores que son aliados: %1$s -commands.wizardry:allies.list_other=Jugadores que son aliados de %1$s: %2$s -commands.wizardry:allies.permission=No tienes permiso para ver los aliados de otros jugadores -commands.wizardry:allies.none=Ninguno +commands.allies.usage=/%1$s [player] +commands.allies.list=Jugadores que son aliados: %1$s +commands.allies.list_other=Jugadores que son aliados de %1$s: %2$s +commands.allies.permission=No tienes permiso para ver los aliados de otros jugadores +commands.allies.none=Ninguno -commands.wizardry:discoverspell.usage=/%1$s [player] -commands.wizardry:discoverspell.not_found=There is no such spell with ID %1$s -commands.wizardry:discoverspell.clear=Cleared all spell discovery data for %1$s -commands.wizardry:discoverspell.all=Added all spells to %1$s's spell discovery data -commands.wizardry:discoverspell.addspell=Added %1$s to %2$s's spell discovery data -commands.wizardry:discoverspell.removespell=Removed %1$s from %2$s's spell discovery data +commands.discoverspell.usage=/%1$s [player] +commands.discoverspell.not_found=There is no such spell with ID %1$s +commands.discoverspell.clear=Cleared all spell discovery data for %1$s +commands.discoverspell.all=Added all spells to %1$s's spell discovery data +commands.discoverspell.addspell=Added %1$s to %2$s's spell discovery data +commands.discoverspell.removespell=Removed %1$s from %2$s's spell discovery data -config.wizardry.title.general=Mod Options +config.title.general=Mod Options -config.wizardry.category.spells=Configurar Hechizos -config.wizardry.category.spells.tooltip=Seleccionar que hechizos estan activados -config.wizardry.title.spells=Configuracion de Hechizos -config.wizardry.subtitle.spells=Establece un hechizo en falso para desactivarlo. +config.category.spells=Configurar Hechizos +config.category.spells.tooltip=Seleccionar que hechizos estan activados +config.title.spells=Configuracion de Hechizos +config.subtitle.spells=Establece un hechizo en falso para desactivarlo. -config.wizardry.category.resistances=Configurar Resistencias -config.wizardry.category.resistances.tooltip=Configura cuales mobs son inmunes a los tipos de magia -config.wizardry.title.resistances=Configuracion de Resistencias -config.wizardry.subtitle.resistances=Ve las descripciones de opciones individuales para mas detalles. +config.category.resistances=Configurar Resistencias +config.category.resistances.tooltip=Configura cuales mobs son inmunes a los tipos de magia +config.title.resistances=Configuracion de Resistencias +config.subtitle.resistances=Ve las descripciones de opciones individuales para mas detalles. -config.wizardry.category.ids=Configurar IDs -config.wizardry.category.ids.tooltip=Cambiar los IDs usados por Wizardry -config.wizardry.title.ids=Configuracion de IDs -config.wizardry.subtitle.ids=Cambia estos IDs si tienen conflicto con otro mod. +config.category.ids=Configurar IDs +config.category.ids.tooltip=Cambiar los IDs usados por Wizardry +config.title.ids=Configuracion de IDs +config.subtitle.ids=Cambia estos IDs si tienen conflicto con otro mod. -config.wizardry.tower_rarity=Rareza de Torres -config.wizardry.ore_dimensions=Dimensiones de Mena -config.wizardry.flower_dimensions=Dimensiones de Flores -config.wizardry.tower_dimensions=Dimensiones de Torres -config.wizardry.spell_book_drop_chance=Oportunidad de Dropeo de Libro de Hechizo -config.wizardry.generate_loot=Generar Botin -config.wizardry.firebomb_is_craftable=Bomba de Fuego es crafteable -config.wizardry.poison_bomb_is_craftable=Bomba de Veneno es crafteable -config.wizardry.smoke_bomb_is_craftable=Bomba de Humo es crafteable -config.wizardry.use_alternate_scroll_recipe=Usa la receta alternativa de pergamino -config.wizardry.teleport_through_unbreakable_blocks=Teletransportarte a traves de bloques indestructibles -config.wizardry.show_summoned_creature_names=Mostrar los nombres de las criaturas invocadas -config.wizardry.friendly_fire=Fuego Amigo -config.wizardry.telekinetic_disarmament=Desarme Telequinetico -config.wizardry.discovery_mode=Modo Descubrir -config.wizardry.enable_shift_scrolling=Permitir shift-rueda del raton para cambiar de hechizo -config.wizardry.minion_revenge_targeting=Enfoque de venganza de minion -config.wizardry.player_damage_scaling=Factor de escala de dano del Jugador -config.wizardry.npc_damage_scaling=Factor de escala de dano de NPC -config.wizardry.cast_command_multiplier_limit=Limite del Multiplicador de lanzamiento de hechizo por Comando -config.wizardry.summoned_creature_targets_whitelist=Lista blanca de objetivos de criaturas invocadas -config.wizardry.summoned_creature_targets_blacklist=Lista negra de objetivos de criaturas invocadas -config.wizardry.spell_hud_position=Posicion del HUD de hechizos -config.wizardry.cast_command_name=Nombre del Hechizo lanzado por comando -config.wizardry.discoverspell_command_name=Descubrir el nombre del hechizo lanzado por comando -config.wizardry.ally_command_name=Colocar el nombre del comando de aliado -config.wizardry.allies_command_name=Ver el nombre del comandod de aliado +config.tower_rarity=Rareza de Torres +config.ore_dimensions=Dimensiones de Mena +config.flower_dimensions=Dimensiones de Flores +config.tower_dimensions=Dimensiones de Torres +config.spell_book_drop_chance=Oportunidad de Dropeo de Libro de Hechizo +config.generate_loot=Generar Botin +config.firebomb_is_craftable=Bomba de Fuego es crafteable +config.poison_bomb_is_craftable=Bomba de Veneno es crafteable +config.smoke_bomb_is_craftable=Bomba de Humo es crafteable +config.use_alternate_scroll_recipe=Usa la receta alternativa de pergamino +config.teleport_through_unbreakable_blocks=Teletransportarte a traves de bloques indestructibles +config.show_summoned_creature_names=Mostrar los nombres de las criaturas invocadas +config.friendly_fire=Fuego Amigo +config.telekinetic_disarmament=Desarme Telequinetico +config.discovery_mode=Modo Descubrir +config.enable_shift_scrolling=Permitir shift-rueda del raton para cambiar de hechizo +config.minion_revenge_targeting=Enfoque de venganza de minion +config.player_damage_scaling=Factor de escala de dano del Jugador +config.npc_damage_scaling=Factor de escala de dano de NPC +config.cast_command_multiplier_limit=Limite del Multiplicador de lanzamiento de hechizo por Comando +config.summoned_creature_targets_whitelist=Lista blanca de objetivos de criaturas invocadas +config.summoned_creature_targets_blacklist=Lista negra de objetivos de criaturas invocadas +config.spell_hud_position=Posicion del HUD de hechizos +config.cast_command_name=Nombre del Hechizo lanzado por comando +config.discoverspell_command_name=Descubrir el nombre del hechizo lanzado por comando +config.ally_command_name=Colocar el nombre del comando de aliado +config.allies_command_name=Ver el nombre del comandod de aliado -config.wizardry.mobs_immune_to_fire=Mobs Inmunes Al Fuego -config.wizardry.mobs_immune_to_ice=Mobs Inmunees Al Frio -config.wizardry.mobs_immune_to_lightning=Mobs Inmunes Al Rayo -config.wizardry.mobs_immune_to_wither=Mobs Inmunes Al Wither -config.wizardry.mobs_immune_to_poison=Mobs Inmunes Al Veneno +config.mobs_immune_to_fire=Mobs Inmunes Al Fuego +config.mobs_immune_to_ice=Mobs Inmunees Al Frio +config.mobs_immune_to_lightning=Mobs Inmunes Al Rayo +config.mobs_immune_to_wither=Mobs Inmunes Al Wither +config.mobs_immune_to_poison=Mobs Inmunes Al Veneno -config.wizardry.tower_rarity.tooltip=Rareza para las torres de magos. Numeros mayores suben la rareza. Coloque un 0 para desactivarlas. -config.wizardry.ore_dimensions.tooltip=Lista de dimensiones en las cuales la Mena de Cristal Magico aparecera. -config.wizardry.flower_dimensions.tooltip=Lista de dimensiones en las cuales apareceran flores de cristal magico. -config.wizardry.tower_dimensions.tooltip=Lista de dimensiones donde se generaran torres de magos. -config.wizardry.spell_book_drop_chance.tooltip=La probabilidad de que un mob arroje un libro de hechizos cuando muera. Mientras mas alto sea el numero, mas chance tendras. Coloca un 0 para desactivar el dropeo de libros. Coloque 200 para asegurar el dropeo 100%. -config.wizardry.generate_loot.tooltip=Si se debe generar botin del mod en cofres de mazmorras. -config.wizardry.firebomb_is_craftable.tooltip=Si las bombas de fuego son crafteables o no. -config.wizardry.poison_bomb_is_craftable.tooltip=Si las bombas de veneno son crafteables o no. -config.wizardry.smoke_bomb_is_craftable.tooltip=Si las bombas de humo son crafteables o no. -config.wizardry.use_alternate_scroll_recipe.tooltip=Si se requiere cristal magico para craftear pergaminos vacioes. Utilice esta opcion si otro mod tiene conflicto. -config.wizardry.teleport_through_unbreakable_blocks.tooltip=Si los jugadores son capaces de pasar a traves de bloques indestructibles (bedrock) usando Paso de Fase. -config.wizardry.show_summoned_creature_names.tooltip=Si mostrar los nombres de las criaturas invocadas y sus duenos sobre su cabeza. -config.wizardry.friendly_fire.tooltip=Permitir que los aliados se puedan herir con magia. -config.wizardry.telekinetic_disarmament.tooltip=Permitir que los jugadores puedan desarmar otros jugadores con el hechizo de Telequinesis. -config.wizardry.discovery_mode.tooltip=Para aquellos que le guste el misterio! Si esta en verdadero, los hechizos que no se hayan conjurado seran ilegibles. No tiene efecto en creativo. -config.wizardry.enable_shift_scrolling.tooltip=Si eres capaz de cambiar de hechizos al agacharte y usar la rueda del raton. Nota: Esta funcionalidad es del lado cliente, otros jugadores no dependen de esta, deben configurar su propia opcion. -config.wizardry.minion_revenge_targeting.tooltip=Si las criaturas invocadas atacan a su invocador si este los ataca. -config.wizardry.player_damage_scaling.tooltip=Factor de incremento de dano global para el dano hecho por jugadores, relativo a 1. -config.wizardry.npc_damage_scaling.tooltip=Factor de incremento de dano global para el dano hecho por NPCs, relativo a 1. -config.wizardry.cast_command_multiplier_limit.tooltip=Limite maximo usado con el comando /cast. Esto esta aqui para evitar que accidentalmente destruyan un mundo o servidor. -config.wizardry.summoned_creature_targets_whitelist.tooltip=Lista de nombres de entidades que los magos y criaturas invocadas pueden atacar. -config.wizardry.summoned_creature_targets_blacklist.tooltip=Lista de nombres de entidades que los magos y criaturas invocadas no pueden atacar. Si eliminas creeper hazlo bajo tu responsabilidad!! -config.wizardry.spell_hud_position.tooltip=La posicion de la interfaz de hechizos. -config.wizardry.cast_command_name.tooltip=El nombre del comando /cast. Esto es lo que escribes luego de /; por ejemplo si esto dijera 'magia', entonces el comando seria /magia. -config.wizardry.discoverspell_command_name.tooltip=El nombre del comando /discoverspell. Esto es lo que escribes luego de /; por ejemplo si esto dijera 'descubrir', entonces el comando seria /descubrir. -config.wizardry.ally_command_name.tooltip=El nombre del comando /ally. Esto es lo que escribes luego de /; por ejemplo si esto dijera 'aliado', entonces el comando seria /aliado. -config.wizardry.allies_command_name.tooltip=El nombre del comando /allies command. Esto es lo que escribes luego de /; por ejemplo si esto dijera 'aliados', entonces el comando seria /aliados. +config.frost_potion_id=ID de la pocion de Congelacion +config.transience_potion_id=ID de la pocion de Transitoriedad +config.fireskin_potion_id=ID de la pocion de Piel de Fuego +config.ice_shroud_potion_id=ID de la pocion de Manto de Hielo +config.static_aura_potion_id=ID de la pocion de Aura Estatica +config.decay_potion_id=ID de la pocion de Deterioro +config.sixth_sense_potion_id=ID de la pocion de Sexto Sentido +config.arcane_jammer_potion_id=ID de la pocion de Perturbador Arcano +config.mind_trick_potion_id=ID de la pocion de Truco Mental +config.mind_control_potion_id=ID de la pocion de Control Mental +config.font_of_mana_potion_id=ID de la pocion de Fuente de Mana +config.fear_potion_id=ID de la pocion de Miedo +config.magic_sword_enchantment_id=ID del Encantamiento de Espada Magica +config.magic_bow_enchantment_id=ID del Encantamiento de Arco Magico +config.flaming_weapon_enchantment_id=ID del Encantamiento de Arma Llameante +config.freezing_weapon_enchantment_id=ID del Encantamiento de Arma Congelada -config.wizardry.mobs_immune_to_fire.tooltip=Lista de nombres de entidades que son inmunes al fuego, a parte de los que estan por defecto. Agrega criaturas de otros mods si quieres que sean inmunes al fuego. Los nombres de la entidades no llevan mayusculas. Para entidades de mods, usa el ID del mod como prefijo (e.g. wizardry:_mago). -config.wizardry.mobs_immune_to_ice.tooltip=Lista de nombres de entidades que son inmunes al frio, a parte de los que estan por defecto. Agrega criaturas de otros mods si quieres que sean inmunes al frio. Los nombres de la entidades no llevan mayusculas. Para entidades de mods, usa el ID del mod como prefijo (e.g. wizardry:_mago). -config.wizardry.mobs_immune_to_lightning.tooltip=Lista de nombres de entidades que son inmunes al rayo, a parte de los que estan por defecto. Agrega criaturas de otros mods si quieres que sean inmunes al rayo. Los nombres de la entidades no llevan mayusculas. Para entidades de mods, usa el ID del mod como prefijo (e.g. wizardry:_mago). -config.wizardry.mobs_immune_to_wither.tooltip=Lista de nombres de entidades que son inmunes al wither, a parte de los que estan por defecto. Agrega criaturas de otros mods si quieres que sean inmunes al wither. Los nombres de la entidades no llevan mayusculas. Para entidades de mods, usa el ID del mod como prefijo (e.g. wizardry:_mago). -config.wizardry.mobs_immune_to_poison.tooltip=Lista de nombres de entidades que son inmunes al veneno, a parte de los que estan por defecto. Agrega criaturas de otros mods si quieres que sean inmunes al veneno. Los nombres de la entidades no llevan mayusculas. Para entidades de mods, usa el ID del mod como prefijo (e.g. wizardry:_mago). +config.tower_rarity.tooltip=Rareza para las torres de magos. Numeros mayores suben la rareza. Coloque un 0 para desactivarlas. +config.ore_dimensions.tooltip=Lista de dimensiones en las cuales la Mena de Cristal Magico aparecera. +config.flower_dimensions.tooltip=Lista de dimensiones en las cuales apareceran flores de cristal magico. +config.tower_dimensions.tooltip=Lista de dimensiones donde se generaran torres de magos. +config.spell_book_drop_chance.tooltip=La probabilidad de que un mob arroje un libro de hechizos cuando muera. Mientras mas alto sea el numero, mas chance tendras. Coloca un 0 para desactivar el dropeo de libros. Coloque 200 para asegurar el dropeo 100%. +config.generate_loot.tooltip=Si se debe generar botin del mod en cofres de mazmorras. +config.firebomb_is_craftable.tooltip=Si las bombas de fuego son crafteables o no. +config.poison_bomb_is_craftable.tooltip=Si las bombas de veneno son crafteables o no. +config.smoke_bomb_is_craftable.tooltip=Si las bombas de humo son crafteables o no. +config.use_alternate_scroll_recipe.tooltip=Si se requiere cristal magico para craftear pergaminos vacioes. Utilice esta opcion si otro mod tiene conflicto. +config.teleport_through_unbreakable_blocks.tooltip=Si los jugadores son capaces de pasar a traves de bloques indestructibles (bedrock) usando Paso de Fase. +config.show_summoned_creature_names.tooltip=Si mostrar los nombres de las criaturas invocadas y sus duenos sobre su cabeza. +config.friendly_fire.tooltip=Permitir que los aliados se puedan herir con magia. +config.telekinetic_disarmament.tooltip=Permitir que los jugadores puedan desarmar otros jugadores con el hechizo de Telequinesis. +config.discovery_mode.tooltip=Para aquellos que le guste el misterio! Si esta en verdadero, los hechizos que no se hayan conjurado seran ilegibles. No tiene efecto en creativo. +config.enable_shift_scrolling.tooltip=Si eres capaz de cambiar de hechizos al agacharte y usar la rueda del raton. Nota: Esta funcionalidad es del lado cliente, otros jugadores no dependen de esta, deben configurar su propia opcion. +config.minion_revenge_targeting.tooltip=Si las criaturas invocadas atacan a su invocador si este los ataca. +config.player_damage_scaling.tooltip=Factor de incremento de dano global para el dano hecho por jugadores, relativo a 1. +config.npc_damage_scaling.tooltip=Factor de incremento de dano global para el dano hecho por NPCs, relativo a 1. +config.cast_command_multiplier_limit.tooltip=Limite maximo usado con el comando /cast. Esto esta aqui para evitar que accidentalmente destruyan un mundo o servidor. +config.summoned_creature_targets_whitelist.tooltip=Lista de nombres de entidades que los magos y criaturas invocadas pueden atacar. +config.summoned_creature_targets_blacklist.tooltip=Lista de nombres de entidades que los magos y criaturas invocadas no pueden atacar. Si eliminas creeper hazlo bajo tu responsabilidad!! +config.spell_hud_position.tooltip=La posicion de la interfaz de hechizos. +config.cast_command_name.tooltip=El nombre del comando /cast. Esto es lo que escribes luego de /; por ejemplo si esto dijera 'magia', entonces el comando seria /magia. +config.discoverspell_command_name.tooltip=El nombre del comando /discoverspell. Esto es lo que escribes luego de /; por ejemplo si esto dijera 'descubrir', entonces el comando seria /descubrir. +config.ally_command_name.tooltip=El nombre del comando /ally. Esto es lo que escribes luego de /; por ejemplo si esto dijera 'aliado', entonces el comando seria /aliado. +config.allies_command_name.tooltip=El nombre del comando /allies command. Esto es lo que escribes luego de /; por ejemplo si esto dijera 'aliados', entonces el comando seria /aliados. +config.mobs_immune_to_fire.tooltip=Lista de nombres de entidades que son inmunes al fuego, a parte de los que estan por defecto. Agrega criaturas de otros mods si quieres que sean inmunes al fuego. Los nombres de la entidades no llevan mayusculas. Para entidades de mods, usa el ID del mod como prefijo (e.g. wizardry:Mago). +config.mobs_immune_to_ice.tooltip=Lista de nombres de entidades que son inmunes al frio, a parte de los que estan por defecto. Agrega criaturas de otros mods si quieres que sean inmunes al frio. Los nombres de la entidades no llevan mayusculas. Para entidades de mods, usa el ID del mod como prefijo (e.g. wizardry:Mago). +config.mobs_immune_to_lightning.tooltip=Lista de nombres de entidades que son inmunes al rayo, a parte de los que estan por defecto. Agrega criaturas de otros mods si quieres que sean inmunes al rayo. Los nombres de la entidades no llevan mayusculas. Para entidades de mods, usa el ID del mod como prefijo (e.g. wizardry:Mago). +config.mobs_immune_to_wither.tooltip=Lista de nombres de entidades que son inmunes al wither, a parte de los que estan por defecto. Agrega criaturas de otros mods si quieres que sean inmunes al wither. Los nombres de la entidades no llevan mayusculas. Para entidades de mods, usa el ID del mod como prefijo (e.g. wizardry:Mago). +config.mobs_immune_to_poison.tooltip=Lista de nombres de entidades que son inmunes al veneno, a parte de los que estan por defecto. Agrega criaturas de otros mods si quieres que sean inmunes al veneno. Los nombres de la entidades no llevan mayusculas. Para entidades de mods, usa el ID del mod como prefijo (e.g. wizardry:Mago). + +config.frost_potion_id.tooltip=ID de la pocion de Congelacion. Cambialo si hay conflicto con otro mod. +config.transience_potion_id.tooltip=ID de la pocion de Transitoriedad. Cambialo si hay conflicto con otro mod. +config.fireskin_potion_id.tooltip=ID de la pocion de Piel de Fuego. Cambialo si hay conflicto con otro mod. +config.ice_shroud_potion_id.tooltip=ID de la pocion de Velo de Hielo. Cambialo si hay conflicto con otro mod. +config.static_aura_potion_id.tooltip=ID de la pocion de Aura Estatica. Cambialo si hay conflicto con otro mod. +config.decay_potion_id.tooltip=ID de la pocion de Deterioro. Cambialo si hay conflicto con otro mod. +config.sixth_sense_potion_id.tooltip=ID de la pocion de Sexto Sentido. Cambialo si hay conflicto con otro mod. +config.arcane_jammer_potion_id.tooltip=ID de la pocion de Perturbador Arcano. Cambialo si hay conflicto con otro mod. +config.mind_trick_potion_id.tooltip=ID de la pocion de Truco Mental. Cambialo si hay conflicto con otro mod. +config.mind_control_potion_id.tooltip=ID de la pocion de Control Mental. Cambialo si hay conflicto con otro mod. +config.font_of_mana_potion_id.tooltip=ID de la pocion de Fuente de Mana. Cambialo si hay conflicto con otro mod. +config.fear_potion_id.tooltip=ID de la pocion de Miedo. Cambialo si hay conflicto con otro mod. +config.magic_sword_enchantment_id.tooltip=ID del Encantamiendo de Espada Magica. Cambialo si hay conflicto con otro mod. +config.magic_bow_enchantment_id.tooltip=ID del Encantamiento de Arco Magico. Cambialo si hay conflicto con otro mod. +config.flaming_weapon_enchantment_id.tooltip=ID del Encantamiento de Arma Llameante. Cambialo si hay conflicto con otro mod. +config.freezing_weapon_enchantment_id.tooltip=ID del Encantamiento de Arma Congelada. Cambialo si hay conflicto con otro mod. wizard.debug=%1$s, %2$s, %3$s \ No newline at end of file diff --git a/src/main/resources/assets/wizardry/lang/es_MX.lang b/src/main/resources/assets/wizardry/lang/es_MX.lang index 38ffd4fc..fefdc2b2 100644 --- a/src/main/resources/assets/wizardry/lang/es_MX.lang +++ b/src/main/resources/assets/wizardry/lang/es_MX.lang @@ -1,272 +1,272 @@ -tile.wizardry:arcane_workbench.name=Mesa de Trabajo Arcana -tile.wizardry:crystal_ore.name=Mena de Cristal Magico -tile.wizardry:petrified_stone.name=Piedra Petrificada -tile.wizardry:ice_statue.name=Estatua de Hielo -tile.wizardry:crystal_flower.name=Flor de Cristal Magico -tile.wizardry:snare.name=Trampa -tile.wizardry:transportation_stone.name=Piedra de Transportacion -tile.wizardry:spectral_block.name=Bloque Espectral -tile.wizardry:crystal_block.name=Bloque de Cristal Magico +tile.arcaneWorkbench.name=Mesa de Trabajo Arcana +tile.crystalOre.name=Mena de Cristal Magico +tile.petrifiedStone.name=Piedra Petrificada +tile.iceStatue.name=Estatua de Hielo +tile.crystalFlower.name=Flor de Cristal Magico +tile.snare.name=Trampa +tile.transportationStone.name=Piedra de Transportacion +tile.spectralBlock.name=Bloque Espectral +tile.crystalBlock.name=Bloque de Cristal Magico -item.wizardry:magic_crystal.name=Cristal Magico -item.wizardry:magic_wand.name=Varita Magica -item.wizardry:apprentice_wand.name=Varita de Aprendiz -item.wizardry:advanced_wand.name=Varita Avanzada -item.wizardry:master_wand.name=Varita de Maestro -item.wizardry:spell_book.name=Libro de Hechizo +item.magicCrystal.name=Cristal Magico +item.magicWand.name=Varita Magica +item.apprenticeWand.name=Varita de Aprendiz +item.advancedWand.name=Varita Avanzada +item.masterWand.name=Varita de Maestro +item.spellBook.name=Libro de Hechizo -item.wizardry:arcane_tome.name=Tomo de lo Arcano -item.wizardry:arcane_tome.desc1=Mejora cualquier %1$s -item.wizardry:arcane_tome.desc2=varita a nivel %1$s +item.arcaneTome.name=Tomo de lo Arcano +item.arcaneTome.desc1=Mejora cualquier %1$s +item.arcaneTome.desc2=varita a nivel %1$s -item.wizardry:wizard_handbook.name=El Manual del Hechizero -item.wizardry:wizard_handbook.desc=por %1$s +item.wizardHandbook.name=El Manual del Hechizero +item.wizardHandbook.desc=por %1$s -item.wizardry:wand.buff=+%1$s %2$s de potencia -item.wizardry:wand.spell=Hechizo actual: %1$s -item.wizardry:wand.mana=Mana: %1$s/%2$s +item.wand.buff=+%1$s %2$s de potencia +item.wand.spell=Hechizo actual: %1$s +item.wand.mana=Mana: %1$s/%2$s -item.wizardry:wand.addally=%1$s ha sido anadido a tu lista de aliados -item.wizardry:wand.removeally=%1$s ha sido removido a tu lista de aliados +item.wand.addally=%1$s ha sido anadido a tu lista de aliados +item.wand.removeally=%1$s ha sido removido a tu lista de aliados -item.wizardry:basic_fire_wand.name=Varita de Ascuas -item.wizardry:basic_ice_wand.name=Varita de Escarcha -item.wizardry:basic_lightning_wand.name=Varita de Chispas -item.wizardry:basic_necromancy_wand.name=Varita de las Sombras -item.wizardry:basic_earth_wand.name=Varita del Bosque -item.wizardry:basic_sorcery_wand.name=Varita del Misterio -item.wizardry:basic_healing_wand.name=Varita de la Curacion +item.basicFireWand.name=Varita de Ascuas +item.basicIceWand.name=Varita de Escarcha +item.basicLightningWand.name=Varita de Chispas +item.basicNecromancyWand.name=Varita de las Sombras +item.basicEarthWand.name=Varita del Bosque +item.basicSorceryWand.name=Varita del Misterio +item.basicHealingWand.name=Varita de la Curacion -item.wizardry:apprentice_fire_wand.name=Varita del Piromano Aprendiz -item.wizardry:apprentice_ice_wand.name=Varita del Mago de Hielo Aprendiz -item.wizardry:apprentice_lightning_wand.name=Varita del Mago de Tormenta Aprendiz -item.wizardry:apprentice_necromancy_wand.name=Varita del Nigromante Aprendiz -item.wizardry:apprentice_earth_wand.name=Varita del Mago de Tierra Aprendiz -item.wizardry:apprentice_sorcery_wand.name=Varita del Hechicero Aprendiz -item.wizardry:apprentice_healing_wand.name=Varita del Sanador Aprendiz +item.apprenticeFireWand.name=Varita del Piromano Aprendiz +item.apprenticeIceWand.name=Varita del Mago de Hielo Aprendiz +item.apprenticeLightningWand.name=Varita del Mago de Tormenta Aprendiz +item.apprenticeNecromancyWand.name=Varita del Nigromante Aprendiz +item.apprenticeEarthWand.name=Varita del Mago de Tierra Aprendiz +item.apprenticeSorceryWand.name=Varita del Hechicero Aprendiz +item.apprenticeHealingWand.name=Varita del Sanador Aprendiz -item.wizardry:advanced_fire_wand.name=Varita del Piromano -item.wizardry:advanced_ice_wand.name=Varita del Mago de Hielo -item.wizardry:advanced_lightning_wand.name=Varita del Mago de Tormenta -item.wizardry:advanced_necromancy_wand.name=Varita del Nigromante -item.wizardry:advanced_earth_wand.name=Varita del Mago de Tierra -item.wizardry:advanced_sorcery_wand.name=Varita del Hechicero -item.wizardry:advanced_healing_wand.name=Varita del Sanador +item.advancedFireWand.name=Varita del Piromano +item.advancedIceWand.name=Varita del Mago de Hielo +item.advancedLightningWand.name=Varita del Mago de Tormenta +item.advancedNecromancyWand.name=Varita del Nigromante +item.advancedEarthWand.name=Varita del Mago de Tierra +item.advancedSorceryWand.name=Varita del Hechicero +item.advancedHealingWand.name=Varita del Sanador -item.wizardry:master_fire_wand.name=Varita del Piromano Maestro -item.wizardry:master_ice_wand.name=Varita del Mago de Hielo Maestro -item.wizardry:master_lightning_wand.name=Varita del Mago de Tormenta Maestro -item.wizardry:master_necromancy_wand.name=Varita del Nigromante Maestro -item.wizardry:master_earth_wand.name=Varita del Mago de Tierra Maestro -item.wizardry:master_sorcery_wand.name=Varita del Hechicero Maestro -item.wizardry:master_healing_wand.name=Varita del Sanador Maestro +item.masterFireWand.name=Varita del Piromano Maestro +item.masterIceWand.name=Varita del Mago de Hielo Maestro +item.masterLightningWand.name=Varita del Mago de Tormenta Maestro +item.masterNecromancyWand.name=Varita del Nigromante Maestro +item.masterEarthWand.name=Varita del Mago de Tierra Maestro +item.masterSorceryWand.name=Varita del Hechicero Maestro +item.masterHealingWand.name=Varita del Sanador Maestro -item.wizardry:spectral_sword.name=Espada Espectral -item.wizardry:spectral_pickaxe.name=Pico Espectral -item.wizardry:spectral_bow.name=Arco Espectral +item.spectralSword.name=Espada Espectral +item.spectralPickaxe.name=Pico Espectral +item.spectralBow.name=Arco Espectral -item.wizardry:mana_flask.name=Frasco de Mana -item.wizardry:storage_upgrade.name=Mejora de Almacenamiento para Varita -item.wizardry:siphon_upgrade.name=Mejora de Sifon para Varita -item.wizardry:condenser_upgrade.name=Mejora de Condensador para Varita -item.wizardry:range_upgrade.name=Mejora de Rango para Varita -item.wizardry:duration_upgrade.name=Mejora de Duracion para Varita -item.wizardry:cooldown_upgrade.name=Mejora de Tiempo de Reactivacion para Varita -item.wizardry:blast_upgrade.name=Mejora de Explosion para Varita -item.wizardry:attunement_upgrade.name=Mejora de Sintonizacion para Varita +item.manaFlask.name=Frasco de Mana +item.storageUpgrade.name=Mejora de Almacenamiento para Varita +item.siphonUpgrade.name=Mejora de Sifon para Varita +item.condenserUpgrade.name=Mejora de Condensador para Varita +item.rangeUpgrade.name=Mejora de Rango para Varita +item.durationUpgrade.name=Mejora de Duracion para Varita +item.cooldownUpgrade.name=Mejora de Tiempo de Reactivacion para Varita +item.blastUpgrade.name=Mejora de Explosion para Varita +item.attunementUpgrade.name=Mejora de Sintonizacion para Varita -item.wizardry:flaming_axe.name=Hacha Llameante -item.wizardry:frost_axe.name=Hacha Escarchada +item.flamingAxe.name=Hacha Llameante +item.frostAxe.name=Hacha Escarchada -item.wizardry:firebomb.name=Bomba de fuego -item.wizardry:poison_bomb.name=Bomba de veneno -item.wizardry:smoke_bomb.name=Bomba de humo +item.firebomb.name=Bomba de fuego +item.poisonBomb.name=Bomba de veneno +item.smoke_bomb.name=Bomba de humo -item.wizardry:blank_scroll.name=Pergamino en blanco -item.wizardry:scroll.name=Pergamino de %1$s -item.wizardry:scroll.undiscovered.name=Pergamino "%1$s" -item.wizardry:identification_scroll.name=Pergamino de la Identificacion -item.wizardry:identification_scroll.desc1=%1$sIdentifica un libro o pergamino -item.wizardry:identification_scroll.desc2=%1$sdesconocido -item.wizardry:identification_scroll.nothing_to_identify=Nada que identificar! +item.blankScroll.name=Pergamino en blanco +item.scroll.name=Pergamino de %1$s +item.scroll.undiscovered.name=Pergamino "%1$s" +item.identification_scroll.name=Pergamino de la Identificacion +item.identification_scroll.desc1=%1$sIdentifica un libro o pergamino +item.identification_scroll.desc2=%1$sdesconocido +item.identification_scroll.nothing_to_identify=Nada que identificar! -item.wizardry:armour_upgrade.name=Sello Arcano de la Proteccion -item.wizardry:armour_upgrade.desc1=%1$sMejora cualquier armadura -item.wizardry:armour_upgrade.desc2=%1$spara volverla %2$slegendaria +item.armourUpgrade.name=Sello Arcano de la Proteccion +item.armourUpgrade.desc1=%1$sMejora cualquier armadura +item.armourUpgrade.desc2=%1$spara volverla %2$slegendaria -item.wizardry:magic_silk.name=Seda Magica +item.magicSilk.name=Seda Magica -item.wizardry:wizard_armour.legendary=Legendario -item.wizardry:wizard_armour.buff=-%1$s %2$s costo -item.wizardry:wizard_armour.mana=Mana: %1$s/%2$s +item.wizardArmour.legendary=Legendario +item.wizardArmour.buff=-%1$s %2$s costo +item.wizardArmour.mana=Mana: %1$s/%2$s -item.wizardry:wizard_hat.name=Sombrero de Mago -item.wizardry:wizard_robe.name=Tunica de Mago -item.wizardry:wizard_leggings.name=Pantalones de Mago -item.wizardry:wizard_boots.name=Botas de Mago +item.wizard_hat.name=Sombrero de Mago +item.wizard_robe.name=Tunica de Mago +item.wizard_leggings.name=Pantalones de Mago +item.wizard_boots.name=Botas de Mago -item.wizardry:wizard_hat_fire.name=Sombrero de Piromano -item.wizardry:wizard_robe_fire.name=Tunica de Piromano -item.wizardry:wizard_leggings_fire.name=Pantalones de Piromano -item.wizardry:wizard_boots_fire.name=Botas de Piromano +item.wizard_hat_fire.name=Sombrero de Piromano +item.wizard_robe_fire.name=Tunica de Piromano +item.wizard_leggings_fire.name=Pantalones de Piromano +item.wizard_boots_fire.name=Botas de Piromano -item.wizardry:wizard_hat_ice.name=Sombrero de Mago de Hielo -item.wizardry:wizard_robe_ice.name=Tunica de Mago de Hielo -item.wizardry:wizard_leggings_ice.name=Pantalones de Mago de Hielo -item.wizardry:wizard_boots_ice.name=Botas de Mago de Hielo +item.wizard_hat_ice.name=Sombrero de Mago de Hielo +item.wizard_robe_ice.name=Tunica de Mago de Hielo +item.wizard_leggings_ice.name=Pantalones de Mago de Hielo +item.wizard_boots_ice.name=Botas de Mago de Hielo -item.wizardry:wizard_hat_lightning.name=Sombrero de Mago de Tormenta -item.wizardry:wizard_robe_lightning.name=Tunica de Mago de Tormenta -item.wizardry:wizard_leggings_lightning.name=Pantalones de Mago de Tormenta -item.wizardry:wizard_boots_lightning.name=Botas de Mago de Tormenta +item.wizard_hat_lightning.name=Sombrero de Mago de Tormenta +item.wizard_robe_lightning.name=Tunica de Mago de Tormenta +item.wizard_leggings_lightning.name=Pantalones de Mago de Tormenta +item.wizard_boots_lightning.name=Botas de Mago de Tormenta -item.wizardry:wizard_hat_necromancy.name=Sombrero de Nigromante -item.wizardry:wizard_robe_necromancy.name=Tunica de Nigromante -item.wizardry:wizard_leggings_necromancy.name=Pantalones de Nigromante -item.wizardry:wizard_boots_necromancy.name=Botas de Nigromante +item.wizard_hat_necromancy.name=Sombrero de Nigromante +item.wizard_robe_necromancy.name=Tunica de Nigromante +item.wizard_leggings_necromancy.name=Pantalones de Nigromante +item.wizard_boots_necromancy.name=Botas de Nigromante -item.wizardry:wizard_hat_earth.name=Sombrero de Mago de Tierra -item.wizardry:wizard_robe_earth.name=Tunica de Mago de Tierra -item.wizardry:wizard_leggings_earth.name=Pantalones de Mago de Tierra -item.wizardry:wizard_boots_earth.name=Botas de Mago de Tierra +item.wizard_hat_earth.name=Sombrero de Mago de Tierra +item.wizard_robe_earth.name=Tunica de Mago de Tierra +item.wizard_leggings_earth.name=Pantalones de Mago de Tierra +item.wizard_boots_earth.name=Botas de Mago de Tierra -item.wizardry:wizard_hat_sorcery.name=Sombrero de Hechicero -item.wizardry:wizard_robe_sorcery.name=Tunica de Hechicero -item.wizardry:wizard_leggings_sorcery.name=Pantalones de Hechicero -item.wizardry:wizard_boots_sorcery.name=Botas de Hechicero +item.wizard_hat_sorcery.name=Sombrero de Hechicero +item.wizard_robe_sorcery.name=Tunica de Hechicero +item.wizard_leggings_sorcery.name=Pantalones de Hechicero +item.wizard_boots_sorcery.name=Botas de Hechicero -item.wizardry:wizard_hat_healing.name=Sombrero de Sanador -item.wizardry:wizard_robe_healing.name=Tunica de Sanador -item.wizardry:wizard_leggings_healing.name=Pantalones de Sanador -item.wizardry:wizard_boots_healing.name=Botas de Sanador +item.wizard_hat_healing.name=Sombrero de Sanador +item.wizard_robe_healing.name=Tunica de Sanador +item.wizard_leggings_healing.name=Pantalones de Sanador +item.wizard_boots_healing.name=Botas de Sanador -item.wizardry:spawn_wizard.name=Invocar Mago -item.wizardry:spawn_evil_wizard.name=Invocar Mago Malvado +item.spawn_wizard.name=Invocar Mago +item.spawn_evil_wizard.name=Invocar Mago Malvado -item.wizardry:spectral_helmet.name=Casco Espectral -item.wizardry:spectral_chestplate.name=Pechera Espectral -item.wizardry:spectral_leggings.name=Grebas Espectrales -item.wizardry:spectral_boots.name=Botas Espectrales +item.spectral_helmet.name=Casco Espectral +item.spectral_chestplate.name=Pechera Espectral +item.spectral_leggings.name=Grebas Espectrales +item.spectral_boots.name=Botas Espectrales entity.wizardry.summonedcreature.nameplate=%2$s de %1$s -entity.wizardry.zombie_minion.name=Zombi -entity.wizardry.skeleton_minion.name=Esqueleto -entity.wizardry.spider_minion.name=Arana -entity.wizardry.blaze_minion.name=Blaze -entity.wizardry.ice_wraith.name=Espectro de Hielo -entity.wizardry.lightning_wraith.name=Espectro de Relampago -entity.wizardry.shadow_wraith.name=Espectro de las Sombras -entity.wizardry.spirit_wolf.name=Lobo Espiritual -entity.wizardry.spirit_horse.name=Caballo Espiritual -entity.wizardry.ice_giant.name=Gigante de Hielo -entity.wizardry.phoenix.name=Fenix -entity.wizardry.wizard.name=Mago -entity.wizardry.magic_slime.name=Slime Magico -entity.wizardry.silverfish_minion.name=Lepisma -entity.wizardry.storm_elemental.name=Elemental de Tormenta -entity.wizardry.evil_wizard.name=Mago Malvado -entity.wizardry.decoy.name=Senuelo +entity.wizardry.Zombie Minion.name=Zombi +entity.wizardry.Skeleton Minion.name=Esqueleto +entity.wizardry.Spider Minion.name=Arana +entity.wizardry.Blaze Minion.name=Blaze +entity.wizardry.Ice Wraith.name=Espectro de Hielo +entity.wizardry.Lightning Wraith.name=Espectro de Relampago +entity.wizardry.Shadow Wraith.name=Espectro de las Sombras +entity.wizardry.Spirit Wolf.name=Lobo Espiritual +entity.wizardry.Spirit Horse.name=Caballo Espiritual +entity.wizardry.Ice Giant.name=Gigante de Hielo +entity.wizardry.Phoenix.name=Fenix +entity.wizardry.Wizard.name=Mago +entity.wizardry.Magic Slime.name=Slime Magico +entity.wizardry.Silverfish Minion.name=Lepisma +entity.wizardry.Storm Elemental.name=Elemental de Tormenta +entity.wizardry.Evil Wizard.name=Mago Malvado +entity.wizardry.Decoy.name=Senuelo -entity.wizardry.magic_missile.name=Magia -entity.wizardry.arc.name=Magia -entity.wizardry.spark_bomb.name=Magia -entity.wizardry.ice_shard.name=Magia -entity.wizardry.firebomb.name=Magic -entity.wizardry.poison_bomb.name=Magia -entity.wizardry.force_orb.name=Magia -entity.wizardry.spark.name=Magia -entity.wizardry.darkness_orb.name=Magia -entity.wizardry.fire_sigil.name=Magia -entity.wizardry.frost_sigil.name=Magia -entity.wizardry.lightning_sigil.name=Magia -entity.wizardry.lightning_arrow.name=Magia -entity.wizardry.firebolt.name=Magia -entity.wizardry.ice_charge.name=Magia -entity.wizardry.force_arrow.name=Magia -entity.wizardry.dart.name=Magia -entity.wizardry.lightning_disc.name=Magia -entity.wizardry.thunderbolt.name=Magia -entity.wizardry.decay.name=Magia -entity.wizardry.ice_lance.name=Magia -entity.wizardry.smoke_bomb.name=Magia -entity.wizardry.ice_spike.name=Magia +entity.wizardry.Magic Missile.name=Magia +entity.wizardry.Arc.name=Magia +entity.wizardry.Spark Bomb.name=Magia +entity.wizardry.Ice Shard.name=Magia +entity.wizardry.Firebomb.name=Magic +entity.wizardry.Poison Bomb.name=Magia +entity.wizardry.Force Orb.name=Magia +entity.wizardry.Spark.name=Magia +entity.wizardry.Darkness Orb.name=Magia +entity.wizardry.Fire Sigil.name=Magia +entity.wizardry.Frost Sigil.name=Magia +entity.wizardry.Lightning Sigil.name=Magia +entity.wizardry.Lightning Arrow.name=Magia +entity.wizardry.Firebolt.name=Magia +entity.wizardry.Ice Charge.name=Magia +entity.wizardry.Force Arrow.name=Magia +entity.wizardry.Dart.name=Magia +entity.wizardry.Lightning Disc.name=Magia +entity.wizardry.Thunderbolt.name=Magia +entity.wizardry.Decay.name=Magia +entity.wizardry.Ice Lance.name=Magia +entity.wizardry.Smoke Bomb.name=Magia +entity.wizardry.Ice Spike.name=Magia -entity.wizardry.black_hole.name=Agujero Negro -entity.wizardry.shield.name=Escudo -entity.wizardry.meteor.name=Meteoro -entity.wizardry.blizzard.name=Ventisca -entity.wizardry.bubble.name=Burbuja -entity.wizardry.tornado.name=Tornado -entity.wizardry.lightning_hammer.name=Martillo Relampago -entity.wizardry.arrow_rain.name=Lluvia de Flechas -entity.wizardry.healing_aura.name=Aura Curador -entity.wizardry.forcefield.name=Campo de Fuerza -entity.wizardry.ring_of_fire.name=Anillo de Fuego -entity.wizardry.earthquake.name=Terremoto -entity.wizardry.falling_grass.name=Hierba Descendiente -entity.wizardry.hailstorm.name=Tormenta de Granizo -entity.wizardry.lightning_pulse.name=Pulso de Relampago +entity.wizardry.Black Hole.name=Agujero Negro +entity.wizardry.Shield.name=Escudo +entity.wizardry.Meteor.name=Meteoro +entity.wizardry.Blizzard.name=Ventisca +entity.wizardry.Bubble.name=Burbuja +entity.wizardry.Tornado.name=Tornado +entity.wizardry.Lightning Hammer.name=Martillo Relampago +entity.wizardry.Arrow Rain.name=Lluvia de Flechas +entity.wizardry.Healing Aura.name=Aura Curador +entity.wizardry.Forcefield.name=Campo de Fuerza +entity.wizardry.Ring of Fire.name=Anillo de Fuego +entity.wizardry.Earthquake.name=Terremoto +entity.wizardry.Falling Grass.name=Hierba Descendiente +entity.wizardry.Hailstorm.name=Tormenta de Granizo +entity.wizardry.Lightning Pulse.name=Pulso de Relampago -item_group.wizardry=Wizardry -item_group.wizardryspells=Hechizos de Wizardry +itemGroup.wizardry=Wizardry +itemGroup.wizardryspells=Hechizos de Wizardry -achievement.crystal=Un Cristral Curioso... -achievement.crystal.desc=Pica un Cristal Magico -achievement.arcane_initiate=Inicio Arcano -achievement.arcane_initiate.desc=Craftea una varita magica con una pepita de oro, un palo y un cristal magico -achievement.apprentice=Aprendiz de Mago -achievement.apprentice.desc=Usa un tomo de lo arcano para mejorar tu varita -achievement.master=Maestro Arcano -achievement.master.desc=Obten una varita de maestro -achievement.all_spells=Mago de Todos los Oficios -achievement.all_spells.desc=Lanza todos los hechizos disponibles -achievement.wizard_trade=Comercio Magico -achievement.wizard_trade.desc=Compra un objeto a un mago -achievement.buy_master_spell=El Conocimiento es Poder -achievement.buy_master_spell.wizardry:desc=Compra un hechizo maestro a un mago -achievement.freeze_blaze=Ya No Estas Tan Caliente -achievement.freeze_blaze.desc=Congela a un blaze -achievement.charge_creeper=Va a Explotar!! -achievement.charge_creeper.desc='Accidentalmente' carga un creeper -achievement.frankenstein=Frankenstein -achievement.frankenstein.desc=Convierte a un cerdo en un hombrecerdo zombi usando el hechizo de rayo -achievement.special_upgrade=Remiendo Arcano -achievement.special_upgrade.desc=Aplica una mejora a una varita -achievement.craft_flask=Es Magia, Embotellada! -achievement.craft_flask.desc=Craftea un frasco de mana -achievement.elemental=Elemental -achievement.elemental.desc=Obten una varita elemental -achievement.armour_set=Now Eres un Mago Apropiado -achievement.armour_set.desc=Craftea y equipa el set completo de armadura de mago -achievement.legendary=Legendario -achievement.legendary.desc=Obten una pieza de armadura de mago legendaria -achievement.self_destruct=Tiro por la culata -achievement.self_destruct.desc=Muere por tu propia magia -achievement.pig_tornado=No De Nuevo... -achievement.pig_tornado.desc=Monta un cerdo hacia un tornado -achievement.jam_wizard=Sesion de Interferencia -achievement.jam_wizard.desc=Usa el hechizo arcano de interferencia en un mago -achievement.slime_skeleton=Situacion Pegajosa -achievement.slime_skeleton.desc=Sumerge un esqueleto en slime -achievement.anger_wizard=Te Arrepentiras de Eso -achievement.anger_wizard.desc=Haz que un mago se enoje -achievement.defeat_evil_wizard=Justicia -achievement.defeat_evil_wizard.desc=Derrota a un mago malvado -achievement.max_out_wand=Equipada Al Maximo! -achievement.max_out_wand.desc=Aplica el maximo numero de mejoras a una varita maestra -achievement.element_master=Maestria Elemental -achievement.element_master.desc=Lanza todos los hechizos de cualquier elemento -achievement.identify_spell=Apreciacion Arcana -achievement.identify_spell.wizardry:desc=Usa un pergamino de la identificacion para identificar un libro de hechizos o pergamino +achievement.Crystal=Un Cristral Curioso... +achievement.Crystal.desc=Pica un Cristal Magico +achievement.ArcaneInitiate=Inicio Arcano +achievement.ArcaneInitiate.desc=Craftea una varita magica con una pepita de oro, un palo y un cristal magico +achievement.Apprentice=Aprendiz de Mago +achievement.Apprentice.desc=Usa un tomo de lo arcano para mejorar tu varita +achievement.Master=Maestro Arcano +achievement.Master.desc=Obten una varita de maestro +achievement.AllSpells=Mago de Todos los Oficios +achievement.AllSpells.desc=Lanza todos los hechizos disponibles +achievement.WizardTrade=Comercio Magico +achievement.WizardTrade.desc=Compra un objeto a un mago +achievement.BuyMasterSpell=El Conocimiento es Poder +achievement.BuyMasterSpell.desc=Compra un hechizo maestro a un mago +achievement.FreezeBlaze=Ya No Estas Tan Caliente +achievement.FreezeBlaze.desc=Congela a un blaze +achievement.ChargeCreeper=Va a Explotar!! +achievement.ChargeCreeper.desc='Accidentalmente' carga un creeper +achievement.Frankenstein=Frankenstein +achievement.Frankenstein.desc=Convierte a un cerdo en un hombrecerdo zombi usando el hechizo de rayo +achievement.SpecialUpgrade=Remiendo Arcano +achievement.SpecialUpgrade.desc=Aplica una mejora a una varita +achievement.CraftFlask=Es Magia, Embotellada! +achievement.CraftFlask.desc=Craftea un frasco de mana +achievement.Elemental=Elemental +achievement.Elemental.desc=Obten una varita elemental +achievement.ArmourSet=Now Eres un Mago Apropiado +achievement.ArmourSet.desc=Craftea y equipa el set completo de armadura de mago +achievement.Legendary=Legendario +achievement.Legendary.desc=Obten una pieza de armadura de mago legendaria +achievement.SelfDestruct=Tiro por la culata +achievement.SelfDestruct.desc=Muere por tu propia magia +achievement.PigTornado=No De Nuevo... +achievement.PigTornado.desc=Monta un cerdo hacia un tornado +achievement.JamWizard=Sesion de Interferencia +achievement.JamWizard.desc=Usa el hechizo arcano de interferencia en un mago +achievement.SlimeSkeleton=Situacion Pegajosa +achievement.SlimeSkeleton.desc=Sumerge un esqueleto en slime +achievement.AngerWizard=Te Arrepentiras de Eso +achievement.AngerWizard.desc=Haz que un mago se enoje +achievement.DefeatEvilWizard=Justicia +achievement.DefeatEvilWizard.desc=Derrota a un mago malvado +achievement.MaxOutWand=Equipada Al Maximo! +achievement.MaxOutWand.desc=Aplica el maximo numero de mejoras a una varita maestra +achievement.ElementMaster=Maestria Elemental +achievement.ElementMaster.desc=Lanza todos los hechizos de cualquier elemento +achievement.IdentifySpell=Apreciacion Arcana +achievement.IdentifySpell.desc=Usa un pergamino de la identificacion para identificar un libro de hechizos o pergamino -tile.wizardry:transportation_stone.confirm=A partir de ahora retornaras a este lugar cuando lances %1$s -tile.wizardry:transportation_stone.invalid=Primero debes hacer un circulo con 8 piedras de transportacion! +tile.transportationStone.confirm=A partir de ahora retornaras a este lugar cuando lances %1$s +tile.transportationStone.invalid=Primero debes hacer un circulo con 8 piedras de transportacion! -container.wizardry:arcane_workbench=Mesa de Trabajo Arcana -container.wizardry:arcane_workbench.apply=Aplicar -container.wizardry:arcane_workbench.mana=Mana: -container.wizardry:arcane_workbench.upgrades=Mejoras Aplicadas: +container.arcaneWorkbench=Mesa de Trabajo Arcana +container.arcaneWorkbench.apply=Aplicar +container.arcaneWorkbench.mana=Mana: +container.arcaneWorkbench.upgrades=Mejoras Aplicadas: tier.basic=Novato tier.apprentice=Aprendiz @@ -300,437 +300,470 @@ spell.disabled=%1$s ha sido desactivado en la configuracion spell.resist=%1$s resistio %2$s spell.discover=Descubrio el hechizo %1$s! -spell.wizardry:agility=Agilidad -spell.wizardry:arc=Arco Electrico -spell.wizardry:arcane_jammer=Perturbador Arcano -spell.wizardry:arrow_rain=Lluvia de Flechas -spell.wizardry:banish=Desaparecer -spell.wizardry:black_hole=Agujero Negro -spell.wizardry:blink=Parpadeo -spell.wizardry:blizzard=Ventisca -spell.wizardry:bubble=Burbuja -spell.wizardry:chain_lightning=Cadena de Rayos -spell.wizardry:clairvoyance=Clarividencia -spell.wizardry:cobwebs=Telas de Araña -spell.wizardry:conjure_armour=Conjurar Armadura -spell.wizardry:conjure_bow=Conjurar Arco -spell.wizardry:conjure_pickaxe=Conjurar Pico -spell.wizardry:conjure_sword=Conjurar Espada -spell.wizardry:cure_effects=Purificar Efectos -spell.wizardry:curse_of_soulbinding=Maldicion de Atadura al Alma -spell.wizardry:darkness_orb=Orbe de la Oscuridad -spell.wizardry:darkvision=Vision Oscura -spell.wizardry:dart=Dardo -spell.wizardry:decay=Deterioro -spell.wizardry:decoy=Senuelo -spell.wizardry:detonate=Detonar -spell.wizardry:diamondflesh=Piel de Diamante -spell.wizardry:earthquake=Terremoto -spell.wizardry:entrapment=Atrapamiento -spell.wizardry:fireball=Bola de Fuego -spell.wizardry:firebolt=Disparo de Fuego -spell.wizardry:firebomb=Bomba de Fuego -spell.wizardry:fire_resistance=Resistencia al Fuego -spell.wizardry:fire_sigil=Sigilo de Fuego -spell.wizardry:fireskin=Piel de Fuego -spell.wizardry:firestorm=Tormenta de Fuego -spell.wizardry:flame_ray=Rayo de Fuego -spell.wizardry:flaming_axe=Hacha Llameante -spell.wizardry:flaming_weapon=Arma Llameante -spell.wizardry:flight=Vuelo -spell.wizardry:font_of_mana=Fuente de Mana -spell.wizardry:font_of_vitality=Fuente de Vitalidad -spell.wizardry:force_arrow=Flecha de Fuerza -spell.wizardry:forcefield=Campo de Fuerza -spell.wizardry:force_orb=Orbe de Fuerza -spell.wizardry:forests_curse=Maldicion del Bosque -spell.wizardry:freeze=Congelar -spell.wizardry:freezing_weapon=Arma Congelante -spell.wizardry:frost_axe=Hacha Escarchada -spell.wizardry:frost_ray=Rayo de Escarcha -spell.wizardry:frost_sigil=Sigilo de Escarcha -spell.wizardry:glide=Planear -spell.wizardry:greater_fireball=Bola de Fuego Mayor -spell.wizardry:greater_heal=Curacion Mayor -spell.wizardry:group_heal=Curacion Grupal -spell.wizardry:growth_aura= Aura de Crecimiento -spell.wizardry:hailstorm=Tormenta de Granizo -spell.wizardry:heal=Curar -spell.wizardry:heal_ally=curar Aliado -spell.wizardry:healing_aura=Aura de Curacion -spell.wizardry:homing_spark=Chispa Dirigida -spell.wizardry:ice_age=Era de Hielo -spell.wizardry:ice_charge=Carga de Hielo -spell.wizardry:ice_lance=Lanza de Hielo -spell.wizardry:ice_shard=Fragmento de Hielo -spell.wizardry:ice_shroud=Velo de Hielo -spell.wizardry:ice_spikes=Pinchos de Hielo -spell.wizardry:ice_statue=Estatua de Hielo -spell.wizardry:ignite=Encender -spell.wizardry:imbue_weapon=Imbuir Arma -spell.wizardry:intimidate=Intimidar -spell.wizardry:invigorating_presence=Presencia Vigorizante -spell.wizardry:invisibility=Invisibilidad -spell.wizardry:invoke_weather=Invocar clima -spell.wizardry:ironflesh=Piel de Hierro -spell.wizardry:leap=Salto -spell.wizardry:levitation=Levitacion -spell.wizardry:life_drain=Drenaje de Vida -spell.wizardry:light=Luz -spell.wizardry:lightning_arrow=Flecha de Rayo -spell.wizardry:lightning_bolt=Disparo de Rayo -spell.wizardry:lightning_disc=Disco de Rayo -spell.wizardry:lightning_hammer=Martillo Relampago -spell.wizardry:lightning_pulse=Pulso Electrico -spell.wizardry:lightning_ray=Rayo Electrico -spell.wizardry:lightning_sigil=Sigilo Electrico -spell.wizardry:lightning_web=Red Electrica -spell.wizardry:magic_missile=Misil Magico -spell.wizardry:metamorphosis=Metamorfosis -spell.wizardry:meteor=Meteoro -spell.wizardry:mind_control=Control Mental -spell.wizardry:mind_trick=Truco Mental -spell.wizardry:none=[Espacio Vacio] -spell.wizardry:oakflesh=Piel de Roble -spell.wizardry:petrify=Petrificar -spell.wizardry:phase_step=Paso de Fase -spell.wizardry:plague_of_darkness=Plaga de la Oscuridad -spell.wizardry:pocket_furnace=Horno del Bolsillo -spell.wizardry:pocket_workbench=Mesa de Trabajo de Bolsillo -spell.wizardry:poison=Veneno -spell.wizardry:poison_bomb=Bomba de Veneno -spell.wizardry:replenish_hunger=Rellenar hambre -spell.wizardry:ring_of_fire=Anillo de Fuego -spell.wizardry:shadow_ward=Guarda de las Sombras -spell.wizardry:shield=Escudo -spell.wizardry:shockwave=Onda de Choque -spell.wizardry:silverfish_swarm=Emjambre de Lepismas -spell.wizardry:sixth_sense=Sexto Sentido -spell.wizardry:slime=Slime -spell.wizardry:smoke_bomb=Bomba de Humo -spell.wizardry:snare=Trampa -spell.wizardry:snowball=Bola de Nieve -spell.wizardry:spark_bomb=Bomba de Chispa -spell.wizardry:spectral_pathway=Camino Espectral -spell.wizardry:spider_swarm=Emjambre de Arañas -spell.wizardry:static_aura=Aura Estatica -spell.wizardry:summon_blaze=Invocar Blaze -spell.wizardry:summon_ice_giant=Invocar Gigante de Hielo -spell.wizardry:summon_ice_wraith=Invocar Espectro de Hielo -spell.wizardry:summon_iron_golem=Invocar Golem de Hierro -spell.wizardry:summon_lightning_wraith=Invocar Espectro de Rayo -spell.wizardry:summon_phoenix=Invocar Fenix -spell.wizardry:summon_shadow_wraith=Invocar Espectro de las Sombras -spell.wizardry:summon_skeleton=Invocar Esqueleto -spell.wizardry:summon_skeleton_legion=Invocar Legion de Esqueletos -spell.wizardry:summon_snow_golem=Invocar Golem de Nieve -spell.wizardry:summon_spirit_horse=Invocar Caballo Espiritual -spell.wizardry:summon_spirit_wolf=Invocar Lobo Espiritual -spell.wizardry:summon_storm_elemental=Invocar Elemental del Rayo -spell.wizardry:summon_wither_skeleton=Invocar Esqueleto del Wither -spell.wizardry:summon_zombie=Invocar Zombi -spell.wizardry:telekinesis=Telequinesis -spell.wizardry:thunderbolt=Rayo -spell.wizardry:thunderstorm=Tormenta Electrica -spell.wizardry:tornado=Tornado -spell.wizardry:transience=Transitoriedad -spell.wizardry:transportation=Transportacion -spell.wizardry:vanishing_box=Caja Desvaneciente -spell.wizardry:wall_of_frost=Pared de Escarcha -spell.wizardry:water_breathing=Respiracion Acuatica -spell.wizardry:whirlwind=Remolino de Viento -spell.wizardry:wither=Marchitar -spell.wizardry:wither_skull=Craneo de Wither +spell.agility=Agilidad +spell.arc=Arco Electrico +spell.arcane_jammer=Perturbador Arcano +spell.arrow_rain=Lluvia de Flechas +spell.banish=Desaparecer +spell.black_hole=Agujero Negro +spell.blink=Parpadeo +spell.blizzard=Ventisca +spell.bubble=Burbuja +spell.chain_lightning=Cadena de Rayos +spell.clairvoyance=Clarividencia +spell.cobwebs=Telas de Araña +spell.conjure_armour=Conjurar Armadura +spell.conjure_bow=Conjurar Arco +spell.conjure_pickaxe=Conjurar Pico +spell.conjure_sword=Conjurar Espada +spell.cure_effects=Purificar Efectos +spell.curse_of_soulbinding=Maldicion de Atadura al Alma +spell.darkness_orb=Orbe de la Oscuridad +spell.darkvision=Vision Oscura +spell.dart=Dardo +spell.decay=Deterioro +spell.decoy=Senuelo +spell.detonate=Detonar +spell.diamondflesh=Piel de Diamante +spell.earthquake=Terremoto +spell.entrapment=Atrapamiento +spell.fireball=Bola de Fuego +spell.firebolt=Disparo de Fuego +spell.firebomb=Bomba de Fuego +spell.fire_resistance=Resistencia al Fuego +spell.fire_sigil=Sigilo de Fuego +spell.fireskin=Piel de Fuego +spell.firestorm=Tormenta de Fuego +spell.flame_ray=Rayo de Fuego +spell.flaming_axe=Hacha Llameante +spell.flaming_weapon=Arma Llameante +spell.flight=Vuelo +spell.font_of_mana=Fuente de Mana +spell.font_of_vitality=Fuente de Vitalidad +spell.force_arrow=Flecha de Fuerza +spell.forcefield=Campo de Fuerza +spell.force_orb=Orbe de Fuerza +spell.forests_curse=Maldicion del Bosque +spell.freeze=Congelar +spell.freezing_weapon=Arma Congelante +spell.frost_axe=Hacha Escarchada +spell.frost_ray=Rayo de Escarcha +spell.frost_sigil=Sigilo de Escarcha +spell.glide=Planear +spell.greater_fireball=Bola de Fuego Mayor +spell.greater_heal=Curacion Mayor +spell.group_heal=Curacion Grupal +spell.growth_aura= Aura de Crecimiento +spell.hailstorm=Tormenta de Granizo +spell.heal=Curar +spell.heal_ally=curar Aliado +spell.healing_aura=Aura de Curacion +spell.homing_spark=Chispa Dirigida +spell.ice_age=Era de Hielo +spell.ice_charge=Carga de Hielo +spell.ice_lance=Lanza de Hielo +spell.ice_shard=Fragmento de Hielo +spell.ice_shroud=Velo de Hielo +spell.ice_spikes=Pinchos de Hielo +spell.ice_statue=Estatua de Hielo +spell.ignite=Encender +spell.imbue_weapon=Imbuir Arma +spell.intimidate=Intimidar +spell.invigorating_presence=Presencia Vigorizante +spell.invisibility=Invisibilidad +spell.invoke_weather=Invocar clima +spell.ironflesh=Piel de Hierro +spell.leap=Salto +spell.levitation=Levitacion +spell.life_drain=Drenaje de Vida +spell.light=Luz +spell.lightning_arrow=Flecha de Rayo +spell.lightning_bolt=Disparo de Rayo +spell.lightning_disc=Disco de Rayo +spell.lightning_hammer=Martillo Relampago +spell.lightning_pulse=Pulso Electrico +spell.lightning_ray=Rayo Electrico +spell.lightning_sigil=Sigilo Electrico +spell.lightning_web=Red Electrica +spell.magic_missile=Misil Magico +spell.metamorphosis=Metamorfosis +spell.meteor=Meteoro +spell.mind_control=Control Mental +spell.mind_trick=Truco Mental +spell.none=[Espacio Vacio] +spell.oakflesh=Piel de Roble +spell.petrify=Petrificar +spell.phase_step=Paso de Fase +spell.plague_of_darkness=Plaga de la Oscuridad +spell.pocket_furnace=Horno del Bolsillo +spell.pocket_workbench=Mesa de Trabajo de Bolsillo +spell.poison=Veneno +spell.poison_bomb=Bomba de Veneno +spell.replenish_hunger=Rellenar hambre +spell.ring_of_fire=Anillo de Fuego +spell.shadow_ward=Guarda de las Sombras +spell.shield=Escudo +spell.shockwave=Onda de Choque +spell.silverfish_swarm=Emjambre de Lepismas +spell.sixth_sense=Sexto Sentido +spell.slime=Slime +spell.smoke_bomb=Bomba de Humo +spell.snare=Trampa +spell.snowball=Bola de Nieve +spell.spark_bomb=Bomba de Chispa +spell.spectral_pathway=Camino Espectral +spell.spider_swarm=Emjambre de Arañas +spell.static_aura=Aura Estatica +spell.summon_blaze=Invocar Blaze +spell.summon_ice_giant=Invocar Gigante de Hielo +spell.summon_ice_wraith=Invocar Espectro de Hielo +spell.summon_iron_golem=Invocar Golem de Hierro +spell.summon_lightning_wraith=Invocar Espectro de Rayo +spell.summon_phoenix=Invocar Fenix +spell.summon_shadow_wraith=Invocar Espectro de las Sombras +spell.summon_skeleton=Invocar Esqueleto +spell.summon_skeleton_legion=Invocar Legion de Esqueletos +spell.summon_snow_golem=Invocar Golem de Nieve +spell.summon_spirit_horse=Invocar Caballo Espiritual +spell.summon_spirit_wolf=Invocar Lobo Espiritual +spell.summon_storm_elemental=Invocar Elemental del Rayo +spell.summon_wither_skeleton=Invocar Esqueleto del Wither +spell.summon_zombie=Invocar Zombi +spell.telekinesis=Telequinesis +spell.thunderbolt=Rayo +spell.thunderstorm=Tormenta Electrica +spell.tornado=Tornado +spell.transience=Transitoriedad +spell.transportation=Transportacion +spell.vanishing_box=Caja Desvaneciente +spell.wall_of_frost=Pared de Escarcha +spell.water_breathing=Respiracion Acuatica +spell.whirlwind=Remolino de Viento +spell.wither=Marchitar +spell.wither_skull=Craneo de Wither -spell.wizardry:agility.desc=Concede al mago velocidad de movimiento incrementada y un salto mas alto por 30 segundos. -spell.wizardry:arc.desc=Dispara una chispa de rayo al objetivo. -spell.wizardry:arcane_jammer.desc=No permite que el usuario use magia en los proximos 15 segundos. -spell.wizardry:arrow_rain.desc="Arqueros, disparen!" -spell.wizardry:banish.desc=Teletransporta al objetivo una posicion cercana aleatoria en contra de su voluntad. -spell.wizardry:black_hole.desc=Desgarra la realidad. -spell.wizardry:blink.desc=Teletransporta al mago a la posicion que estan apuntando. -spell.wizardry:blizzard.desc=Crea una zona de aire frio, la cual ralentiza y hiere continuamente a cualquiera en su interior. El creador del hechizo es inmune al dano pero no al efecto. -spell.wizardry:bubble.desc=Dispara un chorro de burbujas que causa que lo primero que toque flote hacia el cielo. La burbuja explota despues de un tiempo o si el objetivo es golpeado. -spell.wizardry:chain_lightning.desc=Dispara un relampago a un objetivo, el cual hace cadena con objetivos cercanos hasta dos veces. -spell.wizardry:clairvoyance.desc=Revela el camino hacia una localizacion guardada. Con este hechizo seleccionado, haga shift-clic derecho en un bloque para guardarlo. Lanza este hechizo para revelar el camino. El camino desaparecera despues de 90 segundos. -spell.wizardry:cobwebs.desc=Crea telas de arana en la direccion a la cual estas apuntando, lo cual limita la movilidad de tu objetivo. Las telas de arana despareceran en 20 segundos o si son rotas. -spell.wizardry:conjure_armour.desc=Crea Armadura Espectral alrededor del mago lo cual ofrece proteccion similar a la armadura de hierro. La armadura dura 60 segundos. El mago debe tener el espacio de armadura vacio. -spell.wizardry:conjure_bow.desc=Crea un Arco Espectral con flechas ilimitadas que dura 30 segundos. -spell.wizardry:conjure_pickaxe.desc=Crea un Pico Espectral con fuerza igual a la de un Pico de Hierro, el cual dura 30 segundos. -spell.wizardry:conjure_sword.desc=Crea una Espada Espectral con igual fuerza que una de hierro y dura 30 segundos. -spell.wizardry:cure_effects.desc=Purifica al usuario de todos los efectos de pocion, ya sean buenos o malos. -spell.wizardry:curse_of_soulbinding.desc=Provoca que el alma del objetivo esta ligada al del mago, lo que significa que todo el dano que recibe el mago lo recibe el objetivo. La maldicion dura hasta que uno de los dos muera. -spell.wizardry:darkness_orb.desc=Dispara una bala de energia oscura en la direccion que apuntes, la cual aplicar wither. -spell.wizardry:darkvision.desc=Concede vision nocturna por 45 segundos. -spell.wizardry:dart.desc=Dispara un dardo en la direccion que estas apuntando el cual hiere y debilita al objetivo. -spell.wizardry:decay.desc=Crea un pezado de deterioro en el suelo lo cual infecta y hiere a cualquier criatura que lo pise, tambien causa que estas criatura sigan esparciendo el deterioro por donde caminen. -spell.wizardry:decoy.desc=Crea un clon ilusorio del mago que atrae a los enemigos. El senuelo desaparecera en 30 segundos. -spell.wizardry:detonate.desc=Causa un explosion donde estes mirando, hiriendo a todas las criaturas cercanas - incluyendo al mago si se encuentra muy cerca. -spell.wizardry:diamondflesh.desc="Tus flechas no son nada contra mi!" -spell.wizardry:earthquake.desc=Un verdadero de Mago de Tierra puede mover montanas. -spell.wizardry:entrapment.desc=Atrpa al objetivo en una esfera de oscuridad que lo eleva y hiere continuamente. -spell.wizardry:fireball.desc=Lanza una bola de fuego en la direccion a la que apuntes. -spell.wizardry:firebolt.desc=Dispara un chorro de fuego a corta distancia en frente de ti. -spell.wizardry:firebomb.desc=Lanza una bomba de fuego, la cual detona cuando impacta quemando todo a su alrededor. -spell.wizardry:fire_resistance.desc=Concede resistencia al fuego al mago por 30 segundos. -spell.wizardry:fire_sigil.desc=Coloca una trampa de fuego que quema y hiere a la criatura que la pise. -spell.wizardry:fireskin.desc=Envuelve al mago en fuego por 30 segundos, provocando que cualquier que lo toque se queme. -spell.wizardry:firestorm.desc="Yo soy el dragon." -spell.wizardry:flame_ray.desc=Crea una corriente de llamas en la direccion a la que apuntas, la cual quema todo a su paso. -spell.wizardry:flaming_axe.desc=Crea una Hacha Llameante que quema a todos los enemigos que golpees. Dura 30 segundos. -spell.wizardry:flaming_weapon.desc=Imbuye con llamas la primera arma en el inventario del mago, la magia de deshace despues de 45 segundos. -spell.wizardry:flight.desc=Vuela como un aguila. -spell.wizardry:font_of_mana.desc="Fuimos llenados con una intensa energia magica que parecia emanar del centro del..." - Extraido del diario de un mago olvidado; el resto de la pagina esta quemada. -spell.wizardry:font_of_vitality.desc=Se siente increible. -spell.wizardry:force_arrow.desc=Dispara una flecha de fuerza en la direccion que apuntes. -spell.wizardry:forcefield.desc=Crea un campo de fuerza alrededor del mago que repele criaturas y proyectiles. -spell.wizardry:force_orb.desc=Lanza una esfera de fuerza que hiere y empuja criaturas cuando impacta. -spell.wizardry:forests_curse.desc="Como te atreves a entrar en mi bosque!" -spell.wizardry:freeze.desc=Congela al objetivo por 10 segundos. Tambien congela el agua y crea nieve en el suelo. -spell.wizardry:freezing_weapon.desc=Imbuye el poder del frio a la primera arma en el inventario del mago, provocando que el arma congela a sus objetivos. La magia deshace despues de 45 segundos. -spell.wizardry:frost_axe.desc=Crea un Hacha Escarchada la cual congela los enemigos que golpees. Dura 30 segundos. -spell.wizardry:frost_ray.desc=Crea un corriente de hielo en la direccion que apuntes la cual ralentiza y hiere continuamente al objetivo. -spell.wizardry:frost_sigil.desc=Coloca un trampa de hielo en el suelo el cual hiere y congela al cualquiera que la pise. -spell.wizardry:glide.desc=Permite que el mago planee en el aire. Debe mantenerse el boton presionado. -spell.wizardry:greater_fireball.desc=Lanza una bola de fuego de mayor tamano. -spell.wizardry:greater_heal.desc=Cura 4 corazones del mago. -spell.wizardry:group_heal.desc=Cura 3 corazones al mago, a aliados cercanos y a criaturas invocadas. -spell.wizardry:growth_aura.desc=Crece todos los cultivos cercanos al mago. Tambien hace crecer flores y hierba alta. -spell.wizardry:hailstorm.desc=Fue durante el Gran Invierno de la 3era Edad que los Magos de Hielo descubrieron su verdadero potencial. -spell.wizardry:heal.desc=Cura 2 corazones del usuario. -spell.wizardry:heal_ally.desc=Cura 2.5 corazones al objetivo. -spell.wizardry:healing_aura.desc=Crea una zona de energia curadora que regenera la salud de todos los alidados cercanos. Los No-_muertos dentro reciben dano. -spell.wizardry:homing_spark.desc=Crea una chispa flotante que persigue enemigos. -spell.wizardry:ice_age.desc="Seras congelado por toda la eternidad!" -spell.wizardry:ice_charge.desc=Lanza una carga de hielo que explota cuando impacta, congelando todas las criaturas y soltando fragmentos en todas las direcciones. -spell.wizardry:ice_lance.desc=Dispara una gran lanza de hielo en la direccion a la que estas apuntando, la cual atraviesa los objetivos hieriendo y congelandolos. -spell.wizardry:ice_shard.desc=Dispara un fragemento de hielo que hace dano y ralentiza -spell.wizardry:ice_shroud.desc=Cubre al mago con hielo, lo que ocasiona que cualquier que lo ataque sea congelado. -spell.wizardry:ice_spikes.desc=Causa que pinchos de hielo crezcan desde el suelo hiriendo a los enemigos. -spell.wizardry:ice_statue.desc=Congela al objetivo durante 20 segundos. El objetivo no se puede mover ni atacar pero tambien es inmune al dano. -spell.wizardry:ignite.desc=Quema al objetivo. Tambien funciona como un mechero. -spell.wizardry:imbue_weapon.desc=Imbuye la primera arma en el inventario con magia, haciendola mas efectiva. La magia se deshace despues de 45 segundos. -spell.wizardry:intimidate.desc=Emite un grunido intimidante el cual provoca que las criaturas espacen. -spell.wizardry:invigorating_presence.desc=Concede al mago y a los aliados cercanos fuerza incrementada por 45 segundos. -spell.wizardry:invisibility.desc=Le otorga invisibilidad al mago por 30 segundos. -spell.wizardry:invoke_weather.desc=Cambia el clima del mundo. -spell.wizardry:ironflesh.desc=Mejora grandemente la resistencia del mago por 30 segundos. -spell.wizardry:leap.desc=Causa que el mago salte alto y se mueve levemente hacia adelante. -spell.wizardry:levitation.desc=Levanta al mago hacia el cielo mientras el boton siga presionado. Si es usando antes de golpear el suelo, anula el dano por caida. -spell.wizardry:life_drain.desc=Crea una correinte de energia oscura que drena la vida de tu objetivo y te cura. -spell.wizardry:light.desc=Crea una esfera de luz que alumbra el area. Dura 30 segundos. -spell.wizardry:lightning_arrow.desc=Lanza una flecha electrificada en la direccion que apuntes. -spell.wizardry:lightning_bolt.desc=Causa que un trueno caiga en la direccion que apuntes. -spell.wizardry:lightning_disc.desc=Envia un disco electrico en la direccion que apuntes, el cual busca objetivos. -spell.wizardry:lightning_hammer.desc="Te golpeare con la ira de los cielos!" -spell.wizardry:lightning_pulse.desc=Carga el suelo cerca del mago con corriente electrica, hiriendo y repeliendo criaturas cercanas. -spell.wizardry:lightning_ray.desc=Crea una corriente de energia electrica que hiere a tus objetivos continuamente. -spell.wizardry:lightning_sigil.desc=Coloca una trampa electrificada que electrocuta a la criatura que la pise. -spell.wizardry:lightning_web.desc="Concentrare. Canaliza la tormenta en tu mente a traves de tu varita y liberala." -spell.wizardry:magic_missile.desc=Dispara una bala de energia magica. -spell.wizardry:metamorphosis.desc=Cambia la forma del objetivo. Solo funciona en algunas criaturas. -spell.wizardry:meteor.desc=Algunos magos solo quieren ver el mundo arder... -spell.wizardry:mind_control.desc=Toma control de la mente del objetivo por 30 segundos, provocando que cambie de bando y peleen a tu favor. No funciona con criaturas de voluntad muy fuerte. -spell.wizardry:mind_trick.desc=Confunde y desorienta al objetivo por 15 segundos, volviendolo incapaz de atacar. El efecto se desactiva si el objetivo recibe dano. -spell.wizardry:none.desc=Para obtener un libro de hechizo con el comando /give, usa la metadata: /give [player] wizardry:spell_book 1 [spell id] (_si encontraste este libro en un cofre significa que otro mod esta alterando las cosas). -spell.wizardry:oakflesh.desc=Mejora la resistencia del amgo durante 30 segundos. -spell.wizardry:petrify.desc=Convierte al objetivo en piedra hasta que se libere, con un chance de liberarse cuando se pone de noche. El objetivo no se puede mover ni atacar y no puede ser atacado. -spell.wizardry:phase_step.desc=Transporta al mago a traves de una pared de 1 bloque de grosor. El rango mejora el grosor por el cual te puedes transportar. -spell.wizardry:plague_of_darkness.desc=La Oscuridad los consumira a todos... -spell.wizardry:pocket_furnace.desc=Cocina 5 objetos en el inventario. Los objetos en la barra seran cocinados primero. -spell.wizardry:pocket_workbench.desc=Permite al mago craftear objetos. -spell.wizardry:poison.desc=Dispara veneno en la direccion que apuntes. -spell.wizardry:poison_bomb.desc=Lanza una bomba de veneno, la cual detona esparciendo mas veneno. -spell.wizardry:replenish_hunger.desc=Rellena 6 puntos de hambre del mago. -spell.wizardry:ring_of_fire.desc=Crea un anillo de fuego alrededor del mago, hiriendo y quemando a los objetivos cercanos. -spell.wizardry:shadow_ward.desc=Crea una pared de oscurdiad en frente del mago lo cual hace que la mitad del dano recibido sea aplicado al atacante. -spell.wizardry:shield.desc=Crea una barrera protectora que bloquea proyectiles y magia. Tambien concede al mago un efecto debil de resistencia. -spell.wizardry:shockwave.desc=Boom. -spell.wizardry:silverfish_swarm.desc="Ahhhh! Se estan multiplicando!!" -spell.wizardry:sixth_sense.desc=Le permite al mago sentir las criaturas cercanas a el, incluso a traves de paredes, durante 20 segundos. -spell.wizardry:slime.desc=Envuelve al objetivo en slime lo cual ralentiza y hiere continuamente. El slime explota despues de 10 segundos. -spell.wizardry:smoke_bomb.desc=Lanza una bomba de humo la cual explota cuando impacta. Creando una nube de humo y dandole ceguera a las criaturas en el area de impacto. -spell.wizardry:snare.desc=Coloca un trampa en el suelo que hiere y ralentiza al que la pise. -spell.wizardry:snowball.desc=Lanza una bola de nieve. -spell.wizardry:spark_bomb.desc=Lanza una bomba electrica que electrocuta a los objetivos cerca de su zona de impacto. -spell.wizardry:spectral_pathway.desc=Crea un puente magico indestructible al frente del mago que se extiende a 15 bloques. El puente desaperece luego de 60 segundos. -spell.wizardry:spider_swarm.desc=Invoca un emjambre de aranas venenosas. Desapareceran despues de 3o segundos o si son eliminadas. -spell.wizardry:static_aura.desc=Rodea al mago de electricidad durante 30 segundos, disparando chispa de rayo a cualquiera que lo golpee. -spell.wizardry:summon_blaze.desc=Invoca un Blaze que pelea por ti. El blaze desparecera despues de 30 segundos o si es derrotado. -spell.wizardry:summon_ice_giant.desc="Smash them!" -spell.wizardry:summon_ice_wraith.desc=Invoca un Espectro de Hielo que pelea por ti. El Espectro de Hielo desparecera despues de 30 segundos o si es derrotado. -spell.wizardry:summon_iron_golem.desc=Autómata autónomo automatizado automático. -spell.wizardry:summon_lightning_wraith.desc=Invoca un Espectro de Rayo que pelea por ti. El Espectro de Rayo desparecera despues de 30 segundos o si es derrotado. -spell.wizardry:summon_phoenix.desc=Desde las cenizas... -spell.wizardry:summon_shadow_wraith.desc=Invoca un Espectro de las Sombras que pelea por ti. -spell.wizardry:summon_skeleton.desc=Invoca un Esqueleto que pelea por ti. El esqueleto desparecera despues de 30 segundos o si es derrotado. -spell.wizardry:summon_skeleton_legion.desc="Levantate, Ejercito No-_muerto!" -spell.wizardry:summon_snow_golem.desc=Crea un Golem de Nieve que pelea por ti. Dura hasta que el golem muera. -spell.wizardry:summon_spirit_horse.desc=Invoca un Caballo Espectral para que lo montes. El caballo desparecera despues de que dure mucho tiempo sin ser montado o hagas shift-clic derecho con cualquier varita. -spell.wizardry:summon_spirit_wolf.desc=Invoca un Lobo Espiritual que pelea por ti. El lobo solo desparecera si es matado o haces shift-clic derecho con cualquier varita. -spell.wizardry:summon_storm_elemental.desc="Elemental de Rayo: Una manifestacion antigua de los elementos, difícilmente puede contener la energía que se agita dentro de el." - Guia del Mago sobre Entidades Arcanas, Volumen I_i -spell.wizardry:summon_wither_skeleton.desc=Invoca un Esqueleto del Wither que pelea por ti. El esqueleto del wither desparecera despues de 30 segundos o si es derrotado. -spell.wizardry:summon_zombie.desc=Invoca un Zombi que pelea por ti. El zombi desparecera despues de 30 segundos o si es derrotado. -spell.wizardry:telekinesis.desc=Mueve un item o un objeto pequeno hacia ti si haces clic derecho sobre el bloque. Tambien puede desarmar jugadores. -spell.wizardry:thunderbolt.desc=Dispara una bala de trueno que empuja los enemigos. -spell.wizardry:thunderstorm.desc="Mwahahahahahaha!" -spell.wizardry:tornado.desc=Libera un tornado en la direccion que mires, el cual atrae y levanta a todos las criaturas en su paso. -spell.wizardry:transience.desc=Hace que el mago transcienda, volviendo inmune a todo dano pero volviendo incapaz de atacar, interactuar o romper bloques -spell.wizardry:transportation.desc=Teletransporta al mago a su circulo de piedra. Para guardar un circulo de piedra haz clic derecho con el hechizo seleccionado. -spell.wizardry:vanishing_box.desc=Le permite al mago accesar a su cofre de Ender. -spell.wizardry:wall_of_frost.desc=El invierno en la punta de tus dedos. -spell.wizardry:water_breathing.desc=Le permite al mago respirar debajo del agua durante 60 segundos. -spell.wizardry:whirlwind.desc=Causa que el objetivo sea soplado lejos de ti a gran velocidad. -spell.wizardry:wither.desc=Dispara un rayo de oscuridad que aplica wither a todo lo que toque. -spell.wizardry:wither_skull.desc=Lanza una Cabeza de Wither en la direccion que apuntes. +spell.agility.desc=Concede al mago velocidad de movimiento incrementada y un salto mas alto por 30 segundos. +spell.arc.desc=Dispara una chispa de rayo al objetivo. +spell.arcane_jammer.desc=No permite que el usuario use magia en los proximos 15 segundos. +spell.arrow_rain.desc="Arqueros, disparen!" +spell.banish.desc=Teletransporta al objetivo una posicion cercana aleatoria en contra de su voluntad. +spell.black_hole.desc=Desgarra la realidad. +spell.blink.desc=Teletransporta al mago a la posicion que estan apuntando. +spell.blizzard.desc=Crea una zona de aire frio, la cual ralentiza y hiere continuamente a cualquiera en su interior. El creador del hechizo es inmune al dano pero no al efecto. +spell.bubble.desc=Dispara un chorro de burbujas que causa que lo primero que toque flote hacia el cielo. La burbuja explota despues de un tiempo o si el objetivo es golpeado. +spell.chain_lightning.desc=Dispara un relampago a un objetivo, el cual hace cadena con objetivos cercanos hasta dos veces. +spell.clairvoyance.desc=Revela el camino hacia una localizacion guardada. Con este hechizo seleccionado, haga shift-clic derecho en un bloque para guardarlo. Lanza este hechizo para revelar el camino. El camino desaparecera despues de 90 segundos. +spell.cobwebs.desc=Crea telas de arana en la direccion a la cual estas apuntando, lo cual limita la movilidad de tu objetivo. Las telas de arana despareceran en 20 segundos o si son rotas. +spell.conjure_armour.desc=Crea Armadura Espectral alrededor del mago lo cual ofrece proteccion similar a la armadura de hierro. La armadura dura 60 segundos. El mago debe tener el espacio de armadura vacio. +spell.conjure_bow.desc=Crea un Arco Espectral con flechas ilimitadas que dura 30 segundos. +spell.conjure_pickaxe.desc=Crea un Pico Espectral con fuerza igual a la de un Pico de Hierro, el cual dura 30 segundos. +spell.conjure_sword.desc=Crea una Espada Espectral con igual fuerza que una de hierro y dura 30 segundos. +spell.cure_effects.desc=Purifica al usuario de todos los efectos de pocion, ya sean buenos o malos. +spell.curse_of_soulbinding.desc=Provoca que el alma del objetivo esta ligada al del mago, lo que significa que todo el dano que recibe el mago lo recibe el objetivo. La maldicion dura hasta que uno de los dos muera. +spell.darkness_orb.desc=Dispara una bala de energia oscura en la direccion que apuntes, la cual aplicar wither. +spell.darkvision.desc=Concede vision nocturna por 45 segundos. +spell.dart.desc=Dispara un dardo en la direccion que estas apuntando el cual hiere y debilita al objetivo. +spell.decay.desc=Crea un pezado de deterioro en el suelo lo cual infecta y hiere a cualquier criatura que lo pise, tambien causa que estas criatura sigan esparciendo el deterioro por donde caminen. +spell.decoy.desc=Crea un clon ilusorio del mago que atrae a los enemigos. El senuelo desaparecera en 30 segundos. +spell.detonate.desc=Causa un explosion donde estes mirando, hiriendo a todas las criaturas cercanas - incluyendo al mago si se encuentra muy cerca. +spell.diamondflesh.desc="Tus flechas no son nada contra mi!" +spell.earthquake.desc=Un verdadero de Mago de Tierra puede mover montanas. +spell.entrapment.desc=Atrpa al objetivo en una esfera de oscuridad que lo eleva y hiere continuamente. +spell.fireball.desc=Lanza una bola de fuego en la direccion a la que apuntes. +spell.firebolt.desc=Dispara un chorro de fuego a corta distancia en frente de ti. +spell.firebomb.desc=Lanza una bomba de fuego, la cual detona cuando impacta quemando todo a su alrededor. +spell.fire_resistance.desc=Concede resistencia al fuego al mago por 30 segundos. +spell.fire_sigil.desc=Coloca una trampa de fuego que quema y hiere a la criatura que la pise. +spell.fireskin.desc=Envuelve al mago en fuego por 30 segundos, provocando que cualquier que lo toque se queme. +spell.firestorm.desc="Yo soy el dragon." +spell.flame_ray.desc=Crea una corriente de llamas en la direccion a la que apuntas, la cual quema todo a su paso. +spell.flaming_axe.desc=Crea una Hacha Llameante que quema a todos los enemigos que golpees. Dura 30 segundos. +spell.flaming_weapon.desc=Imbuye con llamas la primera arma en el inventario del mago, la magia de deshace despues de 45 segundos. +spell.flight.desc=Vuela como un aguila. +spell.font_of_mana.desc="Fuimos llenados con una intensa energia magica que parecia emanar del centro del..." - Extraido del diario de un mago olvidado; el resto de la pagina esta quemada. +spell.font_of_vitality.desc=Se siente increible. +spell.force_arrow.desc=Dispara una flecha de fuerza en la direccion que apuntes. +spell.forcefield.desc=Crea un campo de fuerza alrededor del mago que repele criaturas y proyectiles. +spell.force_orb.desc=Lanza una esfera de fuerza que hiere y empuja criaturas cuando impacta. +spell.forests_curse.desc="Como te atreves a entrar en mi bosque!" +spell.freeze.desc=Congela al objetivo por 10 segundos. Tambien congela el agua y crea nieve en el suelo. +spell.freezing_weapon.desc=Imbuye el poder del frio a la primera arma en el inventario del mago, provocando que el arma congela a sus objetivos. La magia deshace despues de 45 segundos. +spell.frost_axe.desc=Crea un Hacha Escarchada la cual congela los enemigos que golpees. Dura 30 segundos. +spell.frost_ray.desc=Crea un corriente de hielo en la direccion que apuntes la cual ralentiza y hiere continuamente al objetivo. +spell.frost_sigil.desc=Coloca un trampa de hielo en el suelo el cual hiere y congela al cualquiera que la pise. +spell.glide.desc=Permite que el mago planee en el aire. Debe mantenerse el boton presionado. +spell.greater_fireball.desc=Lanza una bola de fuego de mayor tamano. +spell.greater_heal.desc=Cura 4 corazones del mago. +spell.group_heal.desc=Cura 3 corazones al mago, a aliados cercanos y a criaturas invocadas. +spell.growth_aura.desc=Crece todos los cultivos cercanos al mago. Tambien hace crecer flores y hierba alta. +spell.hailstorm.desc=Fue durante el Gran Invierno de la 3era Edad que los Magos de Hielo descubrieron su verdadero potencial. +spell.heal.desc=Cura 2 corazones del usuario. +spell.heal_ally.desc=Cura 2.5 corazones al objetivo. +spell.healing_aura.desc=Crea una zona de energia curadora que regenera la salud de todos los alidados cercanos. Los No-Muertos dentro reciben dano. +spell.homing_spark.desc=Crea una chispa flotante que persigue enemigos. +spell.ice_age.desc="Seras congelado por toda la eternidad!" +spell.ice_charge.desc=Lanza una carga de hielo que explota cuando impacta, congelando todas las criaturas y soltando fragmentos en todas las direcciones. +spell.ice_lance.desc=Dispara una gran lanza de hielo en la direccion a la que estas apuntando, la cual atraviesa los objetivos hieriendo y congelandolos. +spell.ice_shard.desc=Dispara un fragemento de hielo que hace dano y ralentiza +spell.ice_shroud.desc=Cubre al mago con hielo, lo que ocasiona que cualquier que lo ataque sea congelado. +spell.ice_spikes.desc=Causa que pinchos de hielo crezcan desde el suelo hiriendo a los enemigos. +spell.ice_statue.desc=Congela al objetivo durante 20 segundos. El objetivo no se puede mover ni atacar pero tambien es inmune al dano. +spell.ignite.desc=Quema al objetivo. Tambien funciona como un mechero. +spell.imbue_weapon.desc=Imbuye la primera arma en el inventario con magia, haciendola mas efectiva. La magia se deshace despues de 45 segundos. +spell.intimidate.desc=Emite un grunido intimidante el cual provoca que las criaturas espacen. +spell.invigorating_presence.desc=Concede al mago y a los aliados cercanos fuerza incrementada por 45 segundos. +spell.invisibility.desc=Le otorga invisibilidad al mago por 30 segundos. +spell.invoke_weather.desc=Cambia el clima del mundo. +spell.ironflesh.desc=Mejora grandemente la resistencia del mago por 30 segundos. +spell.leap.desc=Causa que el mago salte alto y se mueve levemente hacia adelante. +spell.levitation.desc=Levanta al mago hacia el cielo mientras el boton siga presionado. Si es usando antes de golpear el suelo, anula el dano por caida. +spell.life_drain.desc=Crea una correinte de energia oscura que drena la vida de tu objetivo y te cura. +spell.light.desc=Crea una esfera de luz que alumbra el area. Dura 30 segundos. +spell.lightning_arrow.desc=Lanza una flecha electrificada en la direccion que apuntes. +spell.lightning_bolt.desc=Causa que un trueno caiga en la direccion que apuntes. +spell.lightning_disc.desc=Envia un disco electrico en la direccion que apuntes, el cual busca objetivos. +spell.lightning_hammer.desc="Te golpeare con la ira de los cielos!" +spell.lightning_pulse.desc=Carga el suelo cerca del mago con corriente electrica, hiriendo y repeliendo criaturas cercanas. +spell.lightning_ray.desc=Crea una corriente de energia electrica que hiere a tus objetivos continuamente. +spell.lightning_sigil.desc=Coloca una trampa electrificada que electrocuta a la criatura que la pise. +spell.lightning_web.desc="Concentrare. Canaliza la tormenta en tu mente a traves de tu varita y liberala." +spell.magic_missile.desc=Dispara una bala de energia magica. +spell.metamorphosis.desc=Cambia la forma del objetivo. Solo funciona en algunas criaturas. +spell.meteor.desc=Algunos magos solo quieren ver el mundo arder... +spell.mind_control.desc=Toma control de la mente del objetivo por 30 segundos, provocando que cambie de bando y peleen a tu favor. No funciona con criaturas de voluntad muy fuerte. +spell.mind_trick.desc=Confunde y desorienta al objetivo por 15 segundos, volviendolo incapaz de atacar. El efecto se desactiva si el objetivo recibe dano. +spell.none.desc=Para obtener un libro de hechizo con el comando /give, usa la metadata: /give [player] wizardry:spell_book 1 [spell id] (Si encontraste este libro en un cofre significa que otro mod esta alterando las cosas). +spell.oakflesh.desc=Mejora la resistencia del amgo durante 30 segundos. +spell.petrify.desc=Convierte al objetivo en piedra hasta que se libere, con un chance de liberarse cuando se pone de noche. El objetivo no se puede mover ni atacar y no puede ser atacado. +spell.phase_step.desc=Transporta al mago a traves de una pared de 1 bloque de grosor. El rango mejora el grosor por el cual te puedes transportar. +spell.plague_of_darkness.desc=La Oscuridad los consumira a todos... +spell.pocket_furnace.desc=Cocina 5 objetos en el inventario. Los objetos en la barra seran cocinados primero. +spell.pocket_workbench.desc=Permite al mago craftear objetos. +spell.poison.desc=Dispara veneno en la direccion que apuntes. +spell.poison_bomb.desc=Lanza una bomba de veneno, la cual detona esparciendo mas veneno. +spell.replenish_hunger.desc=Rellena 6 puntos de hambre del mago. +spell.ring_of_fire.desc=Crea un anillo de fuego alrededor del mago, hiriendo y quemando a los objetivos cercanos. +spell.shadow_ward.desc=Crea una pared de oscurdiad en frente del mago lo cual hace que la mitad del dano recibido sea aplicado al atacante. +spell.shield.desc=Crea una barrera protectora que bloquea proyectiles y magia. Tambien concede al mago un efecto debil de resistencia. +spell.shockwave.desc=Boom. +spell.silverfish_swarm.desc="Ahhhh! Se estan multiplicando!!" +spell.sixth_sense.desc=Le permite al mago sentir las criaturas cercanas a el, incluso a traves de paredes, durante 20 segundos. +spell.slime.desc=Envuelve al objetivo en slime lo cual ralentiza y hiere continuamente. El slime explota despues de 10 segundos. +spell.smoke_bomb.desc=Lanza una bomba de humo la cual explota cuando impacta. Creando una nube de humo y dandole ceguera a las criaturas en el area de impacto. +spell.snare.desc=Coloca un trampa en el suelo que hiere y ralentiza al que la pise. +spell.snowball.desc=Lanza una bola de nieve. +spell.spark_bomb.desc=Lanza una bomba electrica que electrocuta a los objetivos cerca de su zona de impacto. +spell.spectral_pathway.desc=Crea un puente magico indestructible al frente del mago que se extiende a 15 bloques. El puente desaperece luego de 60 segundos. +spell.spider_swarm.desc=Invoca un emjambre de aranas venenosas. Desapareceran despues de 3o segundos o si son eliminadas. +spell.static_aura.desc=Rodea al mago de electricidad durante 30 segundos, disparando chispa de rayo a cualquiera que lo golpee. +spell.summon_blaze.desc=Invoca un Blaze que pelea por ti. El blaze desparecera despues de 30 segundos o si es derrotado. +spell.summon_ice_giant.desc="Smash them!" +spell.summon_ice_wraith.desc=Invoca un Espectro de Hielo que pelea por ti. El Espectro de Hielo desparecera despues de 30 segundos o si es derrotado. +spell.summon_iron_golem.desc=Autómata autónomo automatizado automático. +spell.summon_lightning_wraith.desc=Invoca un Espectro de Rayo que pelea por ti. El Espectro de Rayo desparecera despues de 30 segundos o si es derrotado. +spell.summon_phoenix.desc=Desde las cenizas... +spell.summon_shadow_wraith.desc=Invoca un Espectro de las Sombras que pelea por ti. +spell.summon_skeleton.desc=Invoca un Esqueleto que pelea por ti. El esqueleto desparecera despues de 30 segundos o si es derrotado. +spell.summon_skeleton_legion.desc="Levantate, Ejercito No-Muerto!" +spell.summon_snow_golem.desc=Crea un Golem de Nieve que pelea por ti. Dura hasta que el golem muera. +spell.summon_spirit_horse.desc=Invoca un Caballo Espectral para que lo montes. El caballo desparecera despues de que dure mucho tiempo sin ser montado o hagas shift-clic derecho con cualquier varita. +spell.summon_spirit_wolf.desc=Invoca un Lobo Espiritual que pelea por ti. El lobo solo desparecera si es matado o haces shift-clic derecho con cualquier varita. +spell.summon_storm_elemental.desc="Elemental de Rayo: Una manifestacion antigua de los elementos, difícilmente puede contener la energía que se agita dentro de el." - Guia del Mago sobre Entidades Arcanas, Volumen II +spell.summon_wither_skeleton.desc=Invoca un Esqueleto del Wither que pelea por ti. El esqueleto del wither desparecera despues de 30 segundos o si es derrotado. +spell.summon_zombie.desc=Invoca un Zombi que pelea por ti. El zombi desparecera despues de 30 segundos o si es derrotado. +spell.telekinesis.desc=Mueve un item o un objeto pequeno hacia ti si haces clic derecho sobre el bloque. Tambien puede desarmar jugadores. +spell.thunderbolt.desc=Dispara una bala de trueno que empuja los enemigos. +spell.thunderstorm.desc="Mwahahahahahaha!" +spell.tornado.desc=Libera un tornado en la direccion que mires, el cual atrae y levanta a todos las criaturas en su paso. +spell.transience.desc=Hace que el mago transcienda, volviendo inmune a todo dano pero volviendo incapaz de atacar, interactuar o romper bloques +spell.transportation.desc=Teletransporta al mago a su circulo de piedra. Para guardar un circulo de piedra haz clic derecho con el hechizo seleccionado. +spell.vanishing_box.desc=Le permite al mago accesar a su cofre de Ender. +spell.wall_of_frost.desc=El invierno en la punta de tus dedos. +spell.water_breathing.desc=Le permite al mago respirar debajo del agua durante 60 segundos. +spell.whirlwind.desc=Causa que el objetivo sea soplado lejos de ti a gran velocidad. +spell.wither.desc=Dispara un rayo de oscuridad que aplica wither a todo lo que toque. +spell.wither_skull.desc=Lanza una Cabeza de Wither en la direccion que apuntes. -spell.wizardry:invoke_weather.sun=La lluvia empieza a parar... -spell.wizardry:invoke_weather.rain=El cielo se abre... -spell.wizardry:transportation.missing=Tu circulo de piedra no se encuentra o esta siendo obstruido... -spell.wizardry:transportation.undefined=Debes guardar un circulo de piedra primero! -spell.wizardry:transportation.wrongdimension=Tu circulo de piedra se encuentra en otra dimension... -spell.wizardry:clairvoyance.searching=Buscando... -spell.wizardry:clairvoyance.confirm=El camino revelado al lanzar %1$s ahora te llevara a este punto -spell.wizardry:clairvoyance.outofrange=Tu punto guardado esta muy lejos o es inaccesible... -spell.wizardry:clairvoyance.undefined=Debes guardar una localizacion primero! -spell.wizardry:clairvoyance.wrongdimension=Tu localizacion guardada esta en otra dimension... +spell.invoke_weather.sun=La lluvia empieza a parar... +spell.invoke_weather.rain=El cielo se abre... +spell.transportation.missing=Tu circulo de piedra no se encuentra o esta siendo obstruido... +spell.transportation.undefined=Debes guardar un circulo de piedra primero! +spell.transportation.wrongdimension=Tu circulo de piedra se encuentra en otra dimension... +spell.clairvoyance.searching=Buscando... +spell.clairvoyance.confirm=El camino revelado al lanzar %1$s ahora te llevara a este punto +spell.clairvoyance.outofrange=Tu punto guardado esta muy lejos o es inaccesible... +spell.clairvoyance.undefined=Debes guardar una localizacion primero! +spell.clairvoyance.wrongdimension=Tu localizacion guardada esta en otra dimension... -potion.wizardry:frost=Congelacion -potion.wizardry:fireskin=Piel de Fuego -potion.wizardry:ice_shroud=Manto de Hielo -potion.wizardry:static_aura=Aura Estatica -potion.wizardry:transience=Transitoriedad -potion.wizardry:decay=Deterioro -potion.wizardry:sixth_sense=Sexto Sentido -potion.wizardry:arcane_jammer=Perturbador Arcano -potion.wizardry:mind_trick=Truco Mental -potion.wizardry:mind_control=Control Mental -potion.wizardry:font_of_mana=Fuente de Mana -potion.wizardry:fear=Miedo +potion.frost=Congelacion +potion.fireskin=Piel de Fuego +potion.ice_shroud=Manto de Hielo +potion.static_aura=Aura Estatica +potion.transience=Transitoriedad +potion.decay=Deterioro +potion.sixth_sense=Sexto Sentido +potion.arcane_jammer=Perturbador Arcano +potion.mind_trick=Truco Mental +potion.mind_control=Control Mental +potion.font_of_mana=Fuente de Mana +potion.fear=Miedo -enchantment.wizardry:magic_sword=Impregnacion -enchantment.wizardry:magic_bow=Impregnacion -enchantment.wizardry:flaming_weapon=Impregnacion de Fuego -enchantment.wizardry:freezing_weapon=Impregnacion de Fuego +enchantment.magic_sword=Impregnacion +enchantment.magic_bow=Impregnacion +enchantment.flaming_weapon=Impregnacion de Fuego +enchantment.freezing_weapon=Impregnacion de Fuego key.categories.wizardry=Wizardry -key.wizardry.next_spell=Hechizo siguiente -key.wizardry.previous_spell=Hechizo anterior +key.next_spell=Hechizo siguiente +key.previous_spell=Hechizo anterior -death.attack.wizardry_magic=%1$s fue matado por %2$s usando magia -death.attack.indirect_wizardry_magic=%1$s fue matado por %2$s usando magia +death.attack.wizardryMagic=%1$s fue matado por %2$s usando magia +death.attack.indirectWizardryMagic=%1$s fue matado por %2$s usando magia -commands.wizardry:cast.usage=/%1$s [player] [damage multiplier] [range multiplier] [duration multiplier] [blast multiplier] -commands.wizardry:cast.success=Lanzado con exito %1$s -commands.wizardry:cast.success_continuous=Lanzado con exito %1$s; repite el comando para parar -commands.wizardry:cast.success_remote=Lanzado con exito %1$s como %2$s -commands.wizardry:cast.success_remote_continuous=Lanzado con exito %1$s como %2$s; repite el comando para parar -commands.wizardry:cast.fail=Imposible lanzar %1$s -commands.wizardry:cast.not_found=No hay ningun hechizo con ID %1$s +commands.cast.usage=/%1$s [player] [damage multiplier] [range multiplier] [duration multiplier] [blast multiplier] +commands.cast.success=Lanzado con exito %1$s +commands.cast.success_continuous=Lanzado con exito %1$s; repite el comando para parar +commands.cast.success_remote=Lanzado con exito %1$s como %2$s +commands.cast.success_remote_continuous=Lanzado con exito %1$s como %2$s; repite el comando para parar +commands.cast.fail=Imposible lanzar %1$s +commands.cast.not_found=No hay ningun hechizo con ID %1$s -commands.wizardry:ally.usage=/%1$s [player] -commands.wizardry:ally.addally=%1$s ha sido anadido a la lista de aliados de %2$s -commands.wizardry:ally.removeally=%1$s ha sido removido de la lista de aliados de %2$s -commands.wizardry:ally.self=Los jugadores no pueden ser aliados de si mismos! -commands.wizardry:ally.permission=No tienes permiso para cambiar los aliados de otros jugadores +commands.ally.usage=/%1$s [player] +commands.ally.addally=%1$s ha sido anadido a la lista de aliados de %2$s +commands.ally.removeally=%1$s ha sido removido de la lista de aliados de %2$s +commands.ally.self=Los jugadores no pueden ser aliados de si mismos! +commands.ally.permission=No tienes permiso para cambiar los aliados de otros jugadores -commands.wizardry:allies.usage=/%1$s [player] -commands.wizardry:allies.list=Jugadores que son aliados: %1$s -commands.wizardry:allies.list_other=Jugadores que son aliados de %1$s: %2$s -commands.wizardry:allies.permission=No tienes permiso para ver los aliados de otros jugadores -commands.wizardry:allies.none=Ninguno +commands.allies.usage=/%1$s [player] +commands.allies.list=Jugadores que son aliados: %1$s +commands.allies.list_other=Jugadores que son aliados de %1$s: %2$s +commands.allies.permission=No tienes permiso para ver los aliados de otros jugadores +commands.allies.none=Ninguno -commands.wizardry:discoverspell.usage=/%1$s [player] -commands.wizardry:discoverspell.not_found=There is no such spell with ID %1$s -commands.wizardry:discoverspell.clear=Cleared all spell discovery data for %1$s -commands.wizardry:discoverspell.all=Added all spells to %1$s's spell discovery data -commands.wizardry:discoverspell.addspell=Added %1$s to %2$s's spell discovery data -commands.wizardry:discoverspell.removespell=Removed %1$s from %2$s's spell discovery data +commands.discoverspell.usage=/%1$s [player] +commands.discoverspell.not_found=There is no such spell with ID %1$s +commands.discoverspell.clear=Cleared all spell discovery data for %1$s +commands.discoverspell.all=Added all spells to %1$s's spell discovery data +commands.discoverspell.addspell=Added %1$s to %2$s's spell discovery data +commands.discoverspell.removespell=Removed %1$s from %2$s's spell discovery data -config.wizardry.title.general=Mod Options +config.title.general=Mod Options -config.wizardry.category.spells=Configurar Hechizos -config.wizardry.category.spells.tooltip=Seleccionar que hechizos estan activados -config.wizardry.title.spells=Configuracion de Hechizos -config.wizardry.subtitle.spells=Establece un hechizo en falso para desactivarlo. +config.category.spells=Configurar Hechizos +config.category.spells.tooltip=Seleccionar que hechizos estan activados +config.title.spells=Configuracion de Hechizos +config.subtitle.spells=Establece un hechizo en falso para desactivarlo. -config.wizardry.category.resistances=Configurar Resistencias -config.wizardry.category.resistances.tooltip=Configura cuales mobs son inmunes a los tipos de magia -config.wizardry.title.resistances=Configuracion de Resistencias -config.wizardry.subtitle.resistances=Ve las descripciones de opciones individuales para mas detalles. +config.category.resistances=Configurar Resistencias +config.category.resistances.tooltip=Configura cuales mobs son inmunes a los tipos de magia +config.title.resistances=Configuracion de Resistencias +config.subtitle.resistances=Ve las descripciones de opciones individuales para mas detalles. -config.wizardry.category.ids=Configurar IDs -config.wizardry.category.ids.tooltip=Cambiar los IDs usados por Wizardry -config.wizardry.title.ids=Configuracion de IDs -config.wizardry.subtitle.ids=Cambia estos IDs si tienen conflicto con otro mod. +config.category.ids=Configurar IDs +config.category.ids.tooltip=Cambiar los IDs usados por Wizardry +config.title.ids=Configuracion de IDs +config.subtitle.ids=Cambia estos IDs si tienen conflicto con otro mod. -config.wizardry.tower_rarity=Rareza de Torres -config.wizardry.ore_dimensions=Dimensiones de Mena -config.wizardry.flower_dimensions=Dimensiones de Flores -config.wizardry.tower_dimensions=Dimensiones de Torres -config.wizardry.spell_book_drop_chance=Oportunidad de Dropeo de Libro de Hechizo -config.wizardry.generate_loot=Generar Botin -config.wizardry.firebomb_is_craftable=Bomba de Fuego es crafteable -config.wizardry.poison_bomb_is_craftable=Bomba de Veneno es crafteable -config.wizardry.smoke_bomb_is_craftable=Bomba de Humo es crafteable -config.wizardry.use_alternate_scroll_recipe=Usa la receta alternativa de pergamino -config.wizardry.teleport_through_unbreakable_blocks=Teletransportarte a traves de bloques indestructibles -config.wizardry.show_summoned_creature_names=Mostrar los nombres de las criaturas invocadas -config.wizardry.friendly_fire=Fuego Amigo -config.wizardry.telekinetic_disarmament=Desarme Telequinetico -config.wizardry.discovery_mode=Modo Descubrir -config.wizardry.enable_shift_scrolling=Permitir shift-rueda del raton para cambiar de hechizo -config.wizardry.minion_revenge_targeting=Enfoque de venganza de minion -config.wizardry.player_damage_scaling=Factor de escala de dano del Jugador -config.wizardry.npc_damage_scaling=Factor de escala de dano de NPC -config.wizardry.cast_command_multiplier_limit=Limite del Multiplicador de lanzamiento de hechizo por Comando -config.wizardry.summoned_creature_targets_whitelist=Lista blanca de objetivos de criaturas invocadas -config.wizardry.summoned_creature_targets_blacklist=Lista negra de objetivos de criaturas invocadas -config.wizardry.spell_hud_position=Posicion del HUD de hechizos -config.wizardry.cast_command_name=Nombre del Hechizo lanzado por comando -config.wizardry.discoverspell_command_name=Descubrir el nombre del hechizo lanzado por comando -config.wizardry.ally_command_name=Colocar el nombre del comando de aliado -config.wizardry.allies_command_name=Ver el nombre del comandod de aliado +config.tower_rarity=Rareza de Torres +config.ore_dimensions=Dimensiones de Mena +config.flower_dimensions=Dimensiones de Flores +config.tower_dimensions=Dimensiones de Torres +config.spell_book_drop_chance=Oportunidad de Dropeo de Libro de Hechizo +config.generate_loot=Generar Botin +config.firebomb_is_craftable=Bomba de Fuego es crafteable +config.poison_bomb_is_craftable=Bomba de Veneno es crafteable +config.smoke_bomb_is_craftable=Bomba de Humo es crafteable +config.use_alternate_scroll_recipe=Usa la receta alternativa de pergamino +config.teleport_through_unbreakable_blocks=Teletransportarte a traves de bloques indestructibles +config.show_summoned_creature_names=Mostrar los nombres de las criaturas invocadas +config.friendly_fire=Fuego Amigo +config.telekinetic_disarmament=Desarme Telequinetico +config.discovery_mode=Modo Descubrir +config.enable_shift_scrolling=Permitir shift-rueda del raton para cambiar de hechizo +config.minion_revenge_targeting=Enfoque de venganza de minion +config.player_damage_scaling=Factor de escala de dano del Jugador +config.npc_damage_scaling=Factor de escala de dano de NPC +config.cast_command_multiplier_limit=Limite del Multiplicador de lanzamiento de hechizo por Comando +config.summoned_creature_targets_whitelist=Lista blanca de objetivos de criaturas invocadas +config.summoned_creature_targets_blacklist=Lista negra de objetivos de criaturas invocadas +config.spell_hud_position=Posicion del HUD de hechizos +config.cast_command_name=Nombre del Hechizo lanzado por comando +config.discoverspell_command_name=Descubrir el nombre del hechizo lanzado por comando +config.ally_command_name=Colocar el nombre del comando de aliado +config.allies_command_name=Ver el nombre del comandod de aliado -config.wizardry.mobs_immune_to_fire=Mobs Inmunes Al Fuego -config.wizardry.mobs_immune_to_ice=Mobs Inmunees Al Frio -config.wizardry.mobs_immune_to_lightning=Mobs Inmunes Al Rayo -config.wizardry.mobs_immune_to_wither=Mobs Inmunes Al Wither -config.wizardry.mobs_immune_to_poison=Mobs Inmunes Al Veneno +config.mobs_immune_to_fire=Mobs Inmunes Al Fuego +config.mobs_immune_to_ice=Mobs Inmunees Al Frio +config.mobs_immune_to_lightning=Mobs Inmunes Al Rayo +config.mobs_immune_to_wither=Mobs Inmunes Al Wither +config.mobs_immune_to_poison=Mobs Inmunes Al Veneno -config.wizardry.tower_rarity.tooltip=Rareza para las torres de magos. Numeros mayores suben la rareza. Coloque un 0 para desactivarlas. -config.wizardry.ore_dimensions.tooltip=Lista de dimensiones en las cuales la Mena de Cristal Magico aparecera. -config.wizardry.flower_dimensions.tooltip=Lista de dimensiones en las cuales apareceran flores de cristal magico. -config.wizardry.tower_dimensions.tooltip=Lista de dimensiones donde se generaran torres de magos. -config.wizardry.spell_book_drop_chance.tooltip=La probabilidad de que un mob arroje un libro de hechizos cuando muera. Mientras mas alto sea el numero, mas chance tendras. Coloca un 0 para desactivar el dropeo de libros. Coloque 200 para asegurar el dropeo 100%. -config.wizardry.generate_loot.tooltip=Si se debe generar botin del mod en cofres de mazmorras. -config.wizardry.firebomb_is_craftable.tooltip=Si las bombas de fuego son crafteables o no. -config.wizardry.poison_bomb_is_craftable.tooltip=Si las bombas de veneno son crafteables o no. -config.wizardry.smoke_bomb_is_craftable.tooltip=Si las bombas de humo son crafteables o no. -config.wizardry.use_alternate_scroll_recipe.tooltip=Si se requiere cristal magico para craftear pergaminos vacioes. Utilice esta opcion si otro mod tiene conflicto. -config.wizardry.teleport_through_unbreakable_blocks.tooltip=Si los jugadores son capaces de pasar a traves de bloques indestructibles (bedrock) usando Paso de Fase. -config.wizardry.show_summoned_creature_names.tooltip=Si mostrar los nombres de las criaturas invocadas y sus duenos sobre su cabeza. -config.wizardry.friendly_fire.tooltip=Permitir que los aliados se puedan herir con magia. -config.wizardry.telekinetic_disarmament.tooltip=Permitir que los jugadores puedan desarmar otros jugadores con el hechizo de Telequinesis. -config.wizardry.discovery_mode.tooltip=Para aquellos que le guste el misterio! Si esta en verdadero, los hechizos que no se hayan conjurado seran ilegibles. No tiene efecto en creativo. -config.wizardry.enable_shift_scrolling.tooltip=Si eres capaz de cambiar de hechizos al agacharte y usar la rueda del raton. Nota: Esta funcionalidad es del lado cliente, otros jugadores no dependen de esta, deben configurar su propia opcion. -config.wizardry.minion_revenge_targeting.tooltip=Si las criaturas invocadas atacan a su invocador si este los ataca. -config.wizardry.player_damage_scaling.tooltip=Factor de incremento de dano global para el dano hecho por jugadores, relativo a 1. -config.wizardry.npc_damage_scaling.tooltip=Factor de incremento de dano global para el dano hecho por NPCs, relativo a 1. -config.wizardry.cast_command_multiplier_limit.tooltip=Limite maximo usado con el comando /cast. Esto esta aqui para evitar que accidentalmente destruyan un mundo o servidor. -config.wizardry.summoned_creature_targets_whitelist.tooltip=Lista de nombres de entidades que los magos y criaturas invocadas pueden atacar. -config.wizardry.summoned_creature_targets_blacklist.tooltip=Lista de nombres de entidades que los magos y criaturas invocadas no pueden atacar. Si eliminas creeper hazlo bajo tu responsabilidad!! -config.wizardry.spell_hud_position.tooltip=La posicion de la interfaz de hechizos. -config.wizardry.cast_command_name.tooltip=El nombre del comando /cast. Esto es lo que escribes luego de /; por ejemplo si esto dijera 'magia', entonces el comando seria /magia. -config.wizardry.discoverspell_command_name.tooltip=El nombre del comando /discoverspell. Esto es lo que escribes luego de /; por ejemplo si esto dijera 'descubrir', entonces el comando seria /descubrir. -config.wizardry.ally_command_name.tooltip=El nombre del comando /ally. Esto es lo que escribes luego de /; por ejemplo si esto dijera 'aliado', entonces el comando seria /aliado. -config.wizardry.allies_command_name.tooltip=El nombre del comando /allies command. Esto es lo que escribes luego de /; por ejemplo si esto dijera 'aliados', entonces el comando seria /aliados. +config.frost_potion_id=ID de la pocion de Congelacion +config.transience_potion_id=ID de la pocion de Transitoriedad +config.fireskin_potion_id=ID de la pocion de Piel de Fuego +config.ice_shroud_potion_id=ID de la pocion de Manto de Hielo +config.static_aura_potion_id=ID de la pocion de Aura Estatica +config.decay_potion_id=ID de la pocion de Deterioro +config.sixth_sense_potion_id=ID de la pocion de Sexto Sentido +config.arcane_jammer_potion_id=ID de la pocion de Perturbador Arcano +config.mind_trick_potion_id=ID de la pocion de Truco Mental +config.mind_control_potion_id=ID de la pocion de Control Mental +config.font_of_mana_potion_id=ID de la pocion de Fuente de Mana +config.fear_potion_id=ID de la pocion de Miedo +config.magic_sword_enchantment_id=ID del Encantamiento de Espada Magica +config.magic_bow_enchantment_id=ID del Encantamiento de Arco Magico +config.flaming_weapon_enchantment_id=ID del Encantamiento de Arma Llameante +config.freezing_weapon_enchantment_id=ID del Encantamiento de Arma Congelada -config.wizardry.mobs_immune_to_fire.tooltip=Lista de nombres de entidades que son inmunes al fuego, a parte de los que estan por defecto. Agrega criaturas de otros mods si quieres que sean inmunes al fuego. Los nombres de la entidades no llevan mayusculas. Para entidades de mods, usa el ID del mod como prefijo (e.g. wizardry:_mago). -config.wizardry.mobs_immune_to_ice.tooltip=Lista de nombres de entidades que son inmunes al frio, a parte de los que estan por defecto. Agrega criaturas de otros mods si quieres que sean inmunes al frio. Los nombres de la entidades no llevan mayusculas. Para entidades de mods, usa el ID del mod como prefijo (e.g. wizardry:_mago). -config.wizardry.mobs_immune_to_lightning.tooltip=Lista de nombres de entidades que son inmunes al rayo, a parte de los que estan por defecto. Agrega criaturas de otros mods si quieres que sean inmunes al rayo. Los nombres de la entidades no llevan mayusculas. Para entidades de mods, usa el ID del mod como prefijo (e.g. wizardry:_mago). -config.wizardry.mobs_immune_to_wither.tooltip=Lista de nombres de entidades que son inmunes al wither, a parte de los que estan por defecto. Agrega criaturas de otros mods si quieres que sean inmunes al wither. Los nombres de la entidades no llevan mayusculas. Para entidades de mods, usa el ID del mod como prefijo (e.g. wizardry:_mago). -config.wizardry.mobs_immune_to_poison.tooltip=Lista de nombres de entidades que son inmunes al veneno, a parte de los que estan por defecto. Agrega criaturas de otros mods si quieres que sean inmunes al veneno. Los nombres de la entidades no llevan mayusculas. Para entidades de mods, usa el ID del mod como prefijo (e.g. wizardry:_mago). +config.tower_rarity.tooltip=Rareza para las torres de magos. Numeros mayores suben la rareza. Coloque un 0 para desactivarlas. +config.ore_dimensions.tooltip=Lista de dimensiones en las cuales la Mena de Cristal Magico aparecera. +config.flower_dimensions.tooltip=Lista de dimensiones en las cuales apareceran flores de cristal magico. +config.tower_dimensions.tooltip=Lista de dimensiones donde se generaran torres de magos. +config.spell_book_drop_chance.tooltip=La probabilidad de que un mob arroje un libro de hechizos cuando muera. Mientras mas alto sea el numero, mas chance tendras. Coloca un 0 para desactivar el dropeo de libros. Coloque 200 para asegurar el dropeo 100%. +config.generate_loot.tooltip=Si se debe generar botin del mod en cofres de mazmorras. +config.firebomb_is_craftable.tooltip=Si las bombas de fuego son crafteables o no. +config.poison_bomb_is_craftable.tooltip=Si las bombas de veneno son crafteables o no. +config.smoke_bomb_is_craftable.tooltip=Si las bombas de humo son crafteables o no. +config.use_alternate_scroll_recipe.tooltip=Si se requiere cristal magico para craftear pergaminos vacioes. Utilice esta opcion si otro mod tiene conflicto. +config.teleport_through_unbreakable_blocks.tooltip=Si los jugadores son capaces de pasar a traves de bloques indestructibles (bedrock) usando Paso de Fase. +config.show_summoned_creature_names.tooltip=Si mostrar los nombres de las criaturas invocadas y sus duenos sobre su cabeza. +config.friendly_fire.tooltip=Permitir que los aliados se puedan herir con magia. +config.telekinetic_disarmament.tooltip=Permitir que los jugadores puedan desarmar otros jugadores con el hechizo de Telequinesis. +config.discovery_mode.tooltip=Para aquellos que le guste el misterio! Si esta en verdadero, los hechizos que no se hayan conjurado seran ilegibles. No tiene efecto en creativo. +config.enable_shift_scrolling.tooltip=Si eres capaz de cambiar de hechizos al agacharte y usar la rueda del raton. Nota: Esta funcionalidad es del lado cliente, otros jugadores no dependen de esta, deben configurar su propia opcion. +config.minion_revenge_targeting.tooltip=Si las criaturas invocadas atacan a su invocador si este los ataca. +config.player_damage_scaling.tooltip=Factor de incremento de dano global para el dano hecho por jugadores, relativo a 1. +config.npc_damage_scaling.tooltip=Factor de incremento de dano global para el dano hecho por NPCs, relativo a 1. +config.cast_command_multiplier_limit.tooltip=Limite maximo usado con el comando /cast. Esto esta aqui para evitar que accidentalmente destruyan un mundo o servidor. +config.summoned_creature_targets_whitelist.tooltip=Lista de nombres de entidades que los magos y criaturas invocadas pueden atacar. +config.summoned_creature_targets_blacklist.tooltip=Lista de nombres de entidades que los magos y criaturas invocadas no pueden atacar. Si eliminas creeper hazlo bajo tu responsabilidad!! +config.spell_hud_position.tooltip=La posicion de la interfaz de hechizos. +config.cast_command_name.tooltip=El nombre del comando /cast. Esto es lo que escribes luego de /; por ejemplo si esto dijera 'magia', entonces el comando seria /magia. +config.discoverspell_command_name.tooltip=El nombre del comando /discoverspell. Esto es lo que escribes luego de /; por ejemplo si esto dijera 'descubrir', entonces el comando seria /descubrir. +config.ally_command_name.tooltip=El nombre del comando /ally. Esto es lo que escribes luego de /; por ejemplo si esto dijera 'aliado', entonces el comando seria /aliado. +config.allies_command_name.tooltip=El nombre del comando /allies command. Esto es lo que escribes luego de /; por ejemplo si esto dijera 'aliados', entonces el comando seria /aliados. +config.mobs_immune_to_fire.tooltip=Lista de nombres de entidades que son inmunes al fuego, a parte de los que estan por defecto. Agrega criaturas de otros mods si quieres que sean inmunes al fuego. Los nombres de la entidades no llevan mayusculas. Para entidades de mods, usa el ID del mod como prefijo (e.g. wizardry:Mago). +config.mobs_immune_to_ice.tooltip=Lista de nombres de entidades que son inmunes al frio, a parte de los que estan por defecto. Agrega criaturas de otros mods si quieres que sean inmunes al frio. Los nombres de la entidades no llevan mayusculas. Para entidades de mods, usa el ID del mod como prefijo (e.g. wizardry:Mago). +config.mobs_immune_to_lightning.tooltip=Lista de nombres de entidades que son inmunes al rayo, a parte de los que estan por defecto. Agrega criaturas de otros mods si quieres que sean inmunes al rayo. Los nombres de la entidades no llevan mayusculas. Para entidades de mods, usa el ID del mod como prefijo (e.g. wizardry:Mago). +config.mobs_immune_to_wither.tooltip=Lista de nombres de entidades que son inmunes al wither, a parte de los que estan por defecto. Agrega criaturas de otros mods si quieres que sean inmunes al wither. Los nombres de la entidades no llevan mayusculas. Para entidades de mods, usa el ID del mod como prefijo (e.g. wizardry:Mago). +config.mobs_immune_to_poison.tooltip=Lista de nombres de entidades que son inmunes al veneno, a parte de los que estan por defecto. Agrega criaturas de otros mods si quieres que sean inmunes al veneno. Los nombres de la entidades no llevan mayusculas. Para entidades de mods, usa el ID del mod como prefijo (e.g. wizardry:Mago). + +config.frost_potion_id.tooltip=ID de la pocion de Congelacion. Cambialo si hay conflicto con otro mod. +config.transience_potion_id.tooltip=ID de la pocion de Transitoriedad. Cambialo si hay conflicto con otro mod. +config.fireskin_potion_id.tooltip=ID de la pocion de Piel de Fuego. Cambialo si hay conflicto con otro mod. +config.ice_shroud_potion_id.tooltip=ID de la pocion de Velo de Hielo. Cambialo si hay conflicto con otro mod. +config.static_aura_potion_id.tooltip=ID de la pocion de Aura Estatica. Cambialo si hay conflicto con otro mod. +config.decay_potion_id.tooltip=ID de la pocion de Deterioro. Cambialo si hay conflicto con otro mod. +config.sixth_sense_potion_id.tooltip=ID de la pocion de Sexto Sentido. Cambialo si hay conflicto con otro mod. +config.arcane_jammer_potion_id.tooltip=ID de la pocion de Perturbador Arcano. Cambialo si hay conflicto con otro mod. +config.mind_trick_potion_id.tooltip=ID de la pocion de Truco Mental. Cambialo si hay conflicto con otro mod. +config.mind_control_potion_id.tooltip=ID de la pocion de Control Mental. Cambialo si hay conflicto con otro mod. +config.font_of_mana_potion_id.tooltip=ID de la pocion de Fuente de Mana. Cambialo si hay conflicto con otro mod. +config.fear_potion_id.tooltip=ID de la pocion de Miedo. Cambialo si hay conflicto con otro mod. +config.magic_sword_enchantment_id.tooltip=ID del Encantamiendo de Espada Magica. Cambialo si hay conflicto con otro mod. +config.magic_bow_enchantment_id.tooltip=ID del Encantamiento de Arco Magico. Cambialo si hay conflicto con otro mod. +config.flaming_weapon_enchantment_id.tooltip=ID del Encantamiento de Arma Llameante. Cambialo si hay conflicto con otro mod. +config.freezing_weapon_enchantment_id.tooltip=ID del Encantamiento de Arma Congelada. Cambialo si hay conflicto con otro mod. wizard.debug=%1$s, %2$s, %3$s \ No newline at end of file diff --git a/src/main/resources/assets/wizardry/lang/ru_RU.lang b/src/main/resources/assets/wizardry/lang/ru_RU.lang index b2d24126..51df6fa9 100644 --- a/src/main/resources/assets/wizardry/lang/ru_RU.lang +++ b/src/main/resources/assets/wizardry/lang/ru_RU.lang @@ -1,252 +1,230 @@ -tile.wizardry:arcane_workbench.name=ЧародейÑкий Стол -tile.wizardry:crystal_ore.name=КриÑÑ‚Ð°Ð»ÑŒÐ½Ð°Ñ Ð ÑƒÐ´Ð° -tile.wizardry:petrified_stone.name=Окаменелый Камень -tile.wizardry:ice_statue.name=ЛедÑÐ½Ð°Ñ Ð¡Ñ‚Ð°Ñ‚ÑƒÑ -tile.wizardry:crystal_flower.name=КриÑтальный Цветок -tile.wizardry:snare.name=Ловушка -tile.wizardry:transportation_stone.name=Камень ТранÑпортировки -tile.wizardry:spectral_block.name=Спектральный Блок -tile.wizardry:crystal_block.name=КриÑтальный Блок +tile.arcaneWorkbench.name=ЧародейÑкий Стол +tile.crystalOre.name=КриÑÑ‚Ð°Ð»ÑŒÐ½Ð°Ñ Ð ÑƒÐ´Ð° +tile.petrifiedStone.name=Окаменелый Камень +tile.iceStatue.name=ЛедÑÐ½Ð°Ñ Ð¡Ñ‚Ð°Ñ‚ÑƒÑ +tile.crystalFlower.name=КриÑтальный Цветок +tile.snare.name=Ловушка +tile.transportationStone.name=Камень ТранÑпортировки +tile.spectralBlock.name=Спектральный Блок +tile.crystalBlock.name=КриÑтальный Блок -item.wizardry:magic_crystal.name=МагичеÑкий КриÑталл -item.wizardry:magic_wand.name=Волшебный Жезл -item.wizardry:apprentice_wand.name=Жезл Ученика -item.wizardry:advanced_wand.name=Продвинутый Жезл -item.wizardry:master_wand.name=Жезл МаÑтера -item.wizardry:spell_book.name=Книга Заклинаний +item.magicCrystal.name=МагичеÑкий КриÑталл +item.magicWand.name=Волшебный Жезл +item.apprenticeWand.name=Жезл Ученика +item.advancedWand.name=Продвинутый Жезл +item.masterWand.name=Жезл МаÑтера +item.spellBook.name=Книга Заклинаний -item.wizardry:arcane_tome.name=Фолиант Ðрканы -item.wizardry:arcane_tome.desc1=Улучшает любые %1$s -item.wizardry:arcane_tome.desc2=жезл %1$s уровень +item.arcaneTome.name=Фолиант Ðрканы +item.arcaneTome.desc1=Улучшает любые %1$s +item.arcaneTome.desc2=жезл %1$s уровень -item.wizardry:wizard_handbook.name=Книга Волшебника -item.wizardry:wizard_handbook.desc=от %1$s +item.wizardHandbook.name=Книга Волшебника +item.wizardHandbook.desc=от %1$s -item.wizardry:wand.buff=+%1$s %2$s ÑффективноÑть -item.wizardry:wand.spell=Текущее Заклинание: %1$s -item.wizardry:wand.mana=Мана: %1$s/%2$s +item.wand.buff=+%1$s %2$s ÑффективноÑть +item.wand.spell=Текущее Заклинание: %1$s +item.wand.mana=Мана: %1$s/%2$s -item.wizardry:basic_fire_wand.name=Жезл ÐžÐ³Ð½Ñ -item.wizardry:basic_ice_wand.name=Жезл Мороза -item.wizardry:basic_lightning_wand.name=Жезл ИÑкр -item.wizardry:basic_necromancy_wand.name=Жезл Теней -item.wizardry:basic_earth_wand.name=Жезл ЛеÑа -item.wizardry:basic_sorcery_wand.name=Жезл Тайн -item.wizardry:basic_healing_wand.name=Жезл Ð›ÐµÑ‡ÐµÐ½Ð¸Ñ +item.basicFireWand.name=Жезл ÐžÐ³Ð½Ñ +item.basicIceWand.name=Жезл Мороза +item.basicLightningWand.name=Жезл ИÑкр +item.basicNecromancyWand.name=Жезл Теней +item.basicEarthWand.name=Жезл ЛеÑа +item.basicSorceryWand.name=Жезл Тайн +item.basicHealingWand.name=Жезл Ð›ÐµÑ‡ÐµÐ½Ð¸Ñ -item.wizardry:apprentice_fire_wand.name=Жезл Ученика Пироманта -item.wizardry:apprentice_ice_wand.name=Жезл Ученика ЛедÑного Мага -item.wizardry:apprentice_lightning_wand.name=Жезл Ученика Штормового Мага -item.wizardry:apprentice_necromancy_wand.name=Жезл Ученика Ðекроманта -item.wizardry:apprentice_earth_wand.name=Жезл Ученика ЗемлÑного Мага -item.wizardry:apprentice_sorcery_wand.name=Жезл Ученика Колдуна -item.wizardry:apprentice_healing_wand.name=Жезл Ученика Ð¦ÐµÐ»Ð¸Ñ‚ÐµÐ»Ñ +item.apprenticeFireWand.name=Жезл Ученика Пироманта +item.apprenticeIceWand.name=Жезл Ученика ЛедÑного Мага +item.apprenticeLightningWand.name=Жезл Ученика Штормового Мага +item.apprenticeNecromancyWand.name=Жезл Ученика Ðекроманта +item.apprenticeEarthWand.name=Жезл Ученика ЗемлÑного Мага +item.apprenticeSorceryWand.name=Жезл Ученика Колдуна +item.apprenticeHealingWand.name=Жезл Ученика Ð¦ÐµÐ»Ð¸Ñ‚ÐµÐ»Ñ -item.wizardry:advanced_fire_wand.name=Жезл Пироманта -item.wizardry:advanced_ice_wand.name=Жезл ЛедÑного Мага -item.wizardry:advanced_lightning_wand.name=Жезл Штормового Мага -item.wizardry:advanced_necromancy_wand.name=Жезл Ðекроманта -item.wizardry:advanced_earth_wand.name=Жезл ЗемлÑного Мага -item.wizardry:advanced_sorcery_wand.name=Жезл Колдуна -item.wizardry:advanced_healing_wand.name=Жезл Ð¦ÐµÐ»Ð¸Ñ‚ÐµÐ»Ñ +item.advancedFireWand.name=Жезл Пироманта +item.advancedIceWand.name=Жезл ЛедÑного Мага +item.advancedLightningWand.name=Жезл Штормового Мага +item.advancedNecromancyWand.name=Жезл Ðекроманта +item.advancedEarthWand.name=Жезл ЗемлÑного Мага +item.advancedSorceryWand.name=Жезл Колдуна +item.advancedHealingWand.name=Жезл Ð¦ÐµÐ»Ð¸Ñ‚ÐµÐ»Ñ -item.wizardry:master_fire_wand.name=Жезл МаÑтера Пироманта -item.wizardry:master_ice_wand.name=Жезл МаÑтера ЛедÑного Мага -item.wizardry:master_lightning_wand.name=Жезл МаÑтера Штормового Мага -item.wizardry:master_necromancy_wand.name=Жезл МаÑтера Ðекроманта -item.wizardry:master_earth_wand.name=Жезл МаÑтера ЗемлÑного Мага -item.wizardry:master_sorcery_wand.name=Жезл МаÑтера Колдуна -item.wizardry:master_healing_wand.name=Жезл МаÑтера Ð¦ÐµÐ»Ð¸Ñ‚ÐµÐ»Ñ +item.masterFireWand.name=Жезл МаÑтера Пироманта +item.masterIceWand.name=Жезл МаÑтера ЛедÑного Мага +item.masterLightningWand.name=Жезл МаÑтера Штормового Мага +item.masterNecromancyWand.name=Жезл МаÑтера Ðекроманта +item.masterEarthWand.name=Жезл МаÑтера ЗемлÑного Мага +item.masterSorceryWand.name=Жезл МаÑтера Колдуна +item.masterHealingWand.name=Жезл МаÑтера Ð¦ÐµÐ»Ð¸Ñ‚ÐµÐ»Ñ -item.wizardry:spectral_sword.name=Спектральный Меч -item.wizardry:spectral_pickaxe.name=Ð¡Ð¿ÐµÐºÑ‚Ñ€Ð°Ð»ÑŒÐ½Ð°Ñ ÐšÐ¸Ñ€ÐºÐ° -item.wizardry:spectral_bow.name=Спектральный Лук +item.spectralSword.name=Спектральный Меч +item.spectralPickaxe.name=Ð¡Ð¿ÐµÐºÑ‚Ñ€Ð°Ð»ÑŒÐ½Ð°Ñ ÐšÐ¸Ñ€ÐºÐ° +item.spectralBow.name=Спектральный Лук -item.wizardry:mana_flask.name=Бутылка Маны -item.wizardry:storage_upgrade.name=Улучшение Хранилища Маны Жезла -item.wizardry:siphon_upgrade.name=Улучшение Сифона Жезла -item.wizardry:condenser_upgrade.name=Улучшение КонденÑатора Жезла -item.wizardry:range_upgrade.name=Улучшение РадиуÑа Жезла -item.wizardry:duration_upgrade.name=Улучшение ДлительноÑти Жезла -item.wizardry:cooldown_upgrade.name=Улучшение Кулдауна Жезла -item.wizardry:blast_upgrade.name=Улучшение ÐžÐ±Ð½Ð¾Ð²Ð»ÐµÐ½Ð¸Ñ Ð–ÐµÐ·Ð»Ð° -item.wizardry:attunement_upgrade.name=Улучшение Хранилища Заклинаний Жезла +item.manaFlask.name=Бутылка Маны +item.storageUpgrade.name=Улучшение Хранилища Жезла +item.siphonUpgrade.name=Улучшение Сифона Жезла +item.condenserUpgrade.name=Улучшение КонденÑатора Жезла +item.rangeUpgrade.name=Улучшение РадиуÑа Жезла +item.durationUpgrade.name=Улучшение ДлительноÑти Жезла +item.cooldownUpgrade.name=Улучшение Кулдауна Жезла +item.blastUpgrade.name=Улучшение ÐžÐ±Ð½Ð¾Ð²Ð»ÐµÐ½Ð¸Ñ Ð–ÐµÐ·Ð»Ð° -item.wizardry:flaming_axe.name=Огненный Топор -item.wizardry:frost_axe.name=ЛедÑной Топор +item.flamingAxe.name=Огненный Топор +item.frostAxe.name=ЛедÑной Топор -item.wizardry:firebomb.name=ÐžÐ³Ð½ÐµÐ½Ð½Ð°Ñ Ð‘Ð¾Ð¼Ð±Ð° -item.wizardry:poison_bomb.name=Ð¯Ð´Ð¾Ð²Ð¸Ñ‚Ð°Ñ Ð‘Ð¾Ð¼Ð±Ð° -item.wizardry:smoke_bomb.name=Ð”Ñ‹Ð¼Ð¾Ð²Ð°Ñ Ð‘Ð¾Ð¼Ð±Ð° +item.firebomb.name=ÐžÐ³Ð½ÐµÐ½Ð½Ð°Ñ Ð‘Ð¾Ð¼Ð±Ð° +item.poisonBomb.name=Ð¯Ð´Ð¾Ð²Ð¸Ñ‚Ð°Ñ Ð‘Ð¾Ð¼Ð±Ð° -item.wizardry:blank_scroll.name=ПуÑтой Свиток -item.wizardry:scroll.name=Свиток %1$s -item.wizardry:identification_scroll.name=Свиток Идентификации -item.wizardry:identification_scroll.desc1=%1$sИдентифицирует неизвеÑтное -item.wizardry:identification_scroll.desc2=%1$sкнигу Ð·Ð°ÐºÐ»Ð¸Ð½Ð°Ð½Ð¸Ñ Ð¸Ð»Ð¸ Ñвиток -item.wizardry:identification_scroll.nothing_to_identify=Ðечего Идентифицировать +item.blankScroll.name=ПуÑтой Свиток +item.scroll.name=Свиток %1$s -item.wizardry:armour_upgrade.name=Печать МагичеÑкой Защиты -item.wizardry:armour_upgrade.desc1=%1$sУлучшает Любые ОдеÑÐ½Ð¸Ñ ÐœÐ°Ð³Ð° -item.wizardry:armour_upgrade.desc2=%1$sÑделать Ñто %2$sлегендарным +item.armourUpgrade.name=Печать МагичеÑкой Защиты +item.armourUpgrade.desc1=%1$sУлучшает Любые ОдеÑÐ½Ð¸Ñ ÐœÐ°Ð³Ð° +item.armourUpgrade.desc2=%1$sÑделать Ñто %2$sлегендарным -item.wizardry:magic_silk.name=МагичеÑкий Шёлк +item.magicSilk.name=МагичеÑкий Шёлк -item.wizardry:wizard_armour.legendary=Легендарный -item.wizardry:wizard_armour.buff=-%1$s %2$s ÑтоимоÑть -item.wizardry:wizard_armour.mana=Мана: %1$s/%2$s +item.wizardArmour.legendary=Легендарный +item.wizardArmour.buff=-%1$s %2$s ÑтоимоÑть +item.wizardArmour.mana=Мана: %1$s/%2$s -item.wizardry:wizard_hat.name=ШлÑпа Волшебника -item.wizardry:wizard_robe.name=Роба Волшебника -item.wizardry:wizard_leggings.name=Поножи Волшебника -item.wizardry:wizard_boots.name=Ботинки Волшебника +item.wizard_hat.name=ШлÑпа Волшебника +item.wizard_robe.name=Роба Волшебника +item.wizard_leggings.name=Поножи Волшебника +item.wizard_boots.name=Ботинки Волшебника -item.wizardry:wizard_hat_fire.name=ШлÑпа Пироманта -item.wizardry:wizard_robe_fire.name=Роба Пироманта -item.wizardry:wizard_leggings_fire.name=Поножи Пироманта -item.wizardry:wizard_boots_fire.name=Ботинки Пироманта +item.wizard_hat_fire.name=ШлÑпа Пироманта +item.wizard_robe_fire.name=Роба Пироманта +item.wizard_leggings_fire.name=Поножи Пироманта +item.wizard_boots_fire.name=Ботинки Пироманта -item.wizardry:wizard_hat_ice.name=ШлÑпа ЛедÑного Мага -item.wizardry:wizard_robe_ice.name=Роба ЛедÑного Мага -item.wizardry:wizard_leggings_ice.name=Поножи ЛедÑного Мага -item.wizardry:wizard_boots_ice.name=Ботинки ЛедÑного Мага +item.wizard_hat_ice.name=ШлÑпа ЛедÑного Мага +item.wizard_robe_ice.name=Роба ЛедÑного Мага +item.wizard_leggings_ice.name=Поножи ЛедÑного Мага +item.wizard_boots_ice.name=Ботинки ЛедÑного Мага -item.wizardry:wizard_hat_lightning.name=ШлÑпа Штормового Мага -item.wizardry:wizard_robe_lightning.name=Роба Штормового Мага -item.wizardry:wizard_leggings_lightning.name=Поножи Штормового Мага -item.wizardry:wizard_boots_lightning.name=Ботинки Штормового Мага +item.wizard_hat_lightning.name=ШлÑпа Штормового Мага +item.wizard_robe_lightning.name=Роба Штормового Мага +item.wizard_leggings_lightning.name=Поножи Штормового Мага +item.wizard_boots_lightning.name=Ботинки Штормового Мага -item.wizardry:wizard_hat_necromancy.name=ШлÑпа Ðекроманта -item.wizardry:wizard_robe_necromancy.name=Роба Ðекроманта -item.wizardry:wizard_leggings_necromancy.name=Поножи Ðекроманта -item.wizardry:wizard_boots_necromancy.name=Ботинки Ðекроманта +item.wizard_hat_necromancy.name=ШлÑпа Ðекроманта +item.wizard_robe_necromancy.name=Роба Ðекроманта +item.wizard_leggings_necromancy.name=Поножи Ðекроманта +item.wizard_boots_necromancy.name=Ботинки Ðекроманта -item.wizardry:wizard_hat_earth.name=ШлÑпа ЗемлÑного Мага -item.wizardry:wizard_robe_earth.name=Роба ЗемлÑного Мага -item.wizardry:wizard_leggings_earth.name=Поножи ЗемлÑного Мага -item.wizardry:wizard_boots_earth.name=Ботинки ЗемлÑного Мага +item.wizard_hat_earth.name=ШлÑпа ЗемлÑного Мага +item.wizard_robe_earth.name=Роба ЗемлÑного Мага +item.wizard_leggings_earth.name=Поножи ЗемлÑного Мага +item.wizard_boots_earth.name=Ботинки ЗемлÑного Мага -item.wizardry:wizard_hat_sorcery.name=ШлÑпа Колдуна -item.wizardry:wizard_robe_sorcery.name=Роба Колдуна -item.wizardry:wizard_leggings_sorcery.name=Поножи Колдуна -item.wizardry:wizard_boots_sorcery.name=Ботинки Колдуна +item.wizard_hat_sorcery.name=ШлÑпа Колдуна +item.wizard_robe_sorcery.name=Роба Колдуна +item.wizard_leggings_sorcery.name=Поножи Колдуна +item.wizard_boots_sorcery.name=Ботинки Колдуна -item.wizardry:wizard_hat_healing.name=ШлÑпа Ð¦ÐµÐ»Ð¸Ñ‚ÐµÐ»Ñ -item.wizardry:wizard_robe_healing.name=Роба Ð¦ÐµÐ»Ð¸Ñ‚ÐµÐ»Ñ -item.wizardry:wizard_leggings_healing.name=Поножи Ð¦ÐµÐ»Ð¸Ñ‚ÐµÐ»Ñ -item.wizardry:wizard_boots_healing.name=Ботинки Ð¦ÐµÐ»Ð¸Ñ‚ÐµÐ»Ñ +item.wizard_hat_healing.name=ШлÑпа Ð¦ÐµÐ»Ð¸Ñ‚ÐµÐ»Ñ +item.wizard_robe_healing.name=Роба Ð¦ÐµÐ»Ð¸Ñ‚ÐµÐ»Ñ +item.wizard_leggings_healing.name=Поножи Ð¦ÐµÐ»Ð¸Ñ‚ÐµÐ»Ñ +item.wizard_boots_healing.name=Ботинки Ð¦ÐµÐ»Ð¸Ñ‚ÐµÐ»Ñ -item.wizardry:spawn_wizard.name=Спаун Волшебника +item.spawn_wizard.name=Спаун Волшебника -entity.wizardry.zombie_minion.name=Зомби -entity.wizardry.skeleton_minion.name=Скелет -entity.wizardry.spider_minion.name=Паук -entity.wizardry.blaze_minion.name=Блейз -entity.wizardry.ice_wraith.name=ЛедÑной Призрак -entity.wizardry.lightning_wraith.name=Штормовой Призрак -entity.wizardry.shadow_wraith.name=Теневой Призрак -entity.wizardry.spirit_wolf.name=Призрачный Волк -entity.wizardry.spirit_horse.name=ÐŸÑ€Ð¸Ð·Ñ€Ð°Ñ‡Ð½Ð°Ñ Ð›Ð¾ÑˆÐ°Ð´ÑŒ -entity.wizardry.ice_giant.name=ЛедÑной Гигант -entity.wizardry.phoenix.name=Ð¤ÐµÐ½Ð¸ÐºÑ -entity.wizardry.wizard.name=Волшебник -entity.wizardry.magic_slime.name=МагичеÑкий Слизень -entity.wizardry.silverfish_minion.name=Чешуйница +entity.wizardry.Zombie Minion.name=Зомби +entity.wizardry.Skeleton Minion.name=Скелет +entity.wizardry.Spider Minion.name=Паук +entity.wizardry.Blaze Minion.name=Блейз +entity.wizardry.Ice Wraith.name=ЛедÑной Призрак +entity.wizardry.Lightning Wraith.name=Штормовой Призрак +entity.wizardry.Shadow Wraith.name=Теневой Призрак +entity.wizardry.Spirit Wolf.name=Призрачный Волк +entity.wizardry.Spirit Horse.name=ÐŸÑ€Ð¸Ð·Ñ€Ð°Ñ‡Ð½Ð°Ñ Ð›Ð¾ÑˆÐ°Ð´ÑŒ +entity.wizardry.Ice Giant.name=ЛедÑной Гигант +entity.wizardry.Phoenix.name=Ð¤ÐµÐ½Ð¸ÐºÑ +entity.wizardry.Wizard.name=Волшебник +entity.wizardry.Magic Slime.name=МагичеÑкий Слизень +entity.wizardry.Silverfish Minion.name=Чешуйница -entity.wizardry.magic_missile.name=ÐœÐ°Ð³Ð¸Ñ -entity.wizardry.arc.name=ÐœÐ°Ð³Ð¸Ñ -entity.wizardry.spark_bomb.name=ÐœÐ°Ð³Ð¸Ñ -entity.wizardry.ice_shard.name=ÐœÐ°Ð³Ð¸Ñ -entity.wizardry.firebomb.name=ÐœÐ°Ð³Ð¸Ñ -entity.wizardry.poison_bomb.name=ÐœÐ°Ð³Ð¸Ñ -entity.wizardry.force_orb.name=ÐœÐ°Ð³Ð¸Ñ -entity.wizardry.spark.name=ÐœÐ°Ð³Ð¸Ñ -entity.wizardry.darkness_orb.name=ÐœÐ°Ð³Ð¸Ñ -entity.wizardry.fire_sigil.name=ÐœÐ°Ð³Ð¸Ñ -entity.wizardry.frost_sigil.name=ÐœÐ°Ð³Ð¸Ñ -entity.wizardry.lightning_sigil.name=ÐœÐ°Ð³Ð¸Ñ -entity.wizardry.lightning_arrow.name=ÐœÐ°Ð³Ð¸Ñ -entity.wizardry.firebolt.name=ÐœÐ°Ð³Ð¸Ñ -entity.wizardry.ice_charge.name=ÐœÐ°Ð³Ð¸Ñ -entity.wizardry.force_arrow.name=ÐœÐ°Ð³Ð¸Ñ -entity.wizardry.dart.name=ÐœÐ°Ð³Ð¸Ñ -entity.wizardry.lightning_disc.name=ÐœÐ°Ð³Ð¸Ñ -entity.wizardry.thunderbolt.name=ÐœÐ°Ð³Ð¸Ñ -entity.wizardry.decay.name=ÐœÐ°Ð³Ð¸Ñ +entity.wizardry.Magic Missile.name=ÐœÐ°Ð³Ð¸Ñ +entity.wizardry.Arc.name=ÐœÐ°Ð³Ð¸Ñ +entity.wizardry.Spark Bomb.name=ÐœÐ°Ð³Ð¸Ñ +entity.wizardry.Ice Shard.name=ÐœÐ°Ð³Ð¸Ñ +entity.wizardry.Firebomb.name=ÐœÐ°Ð³Ð¸Ñ +entity.wizardry.Poison Bomb.name=ÐœÐ°Ð³Ð¸Ñ +entity.wizardry.Force Orb.name=ÐœÐ°Ð³Ð¸Ñ +entity.wizardry.Spark.name=ÐœÐ°Ð³Ð¸Ñ +entity.wizardry.Darkness Orb.name=ÐœÐ°Ð³Ð¸Ñ +entity.wizardry.Fire Sigil.name=ÐœÐ°Ð³Ð¸Ñ +entity.wizardry.Frost Sigil.name=ÐœÐ°Ð³Ð¸Ñ +entity.wizardry.Lightning Sigil.name=ÐœÐ°Ð³Ð¸Ñ +entity.wizardry.Lightning Arrow.name=ÐœÐ°Ð³Ð¸Ñ +entity.wizardry.Firebolt.name=ÐœÐ°Ð³Ð¸Ñ +entity.wizardry.Ice Charge.name=ÐœÐ°Ð³Ð¸Ñ +entity.wizardry.Force Arrow.name=ÐœÐ°Ð³Ð¸Ñ +entity.wizardry.Dart.name=ÐœÐ°Ð³Ð¸Ñ +entity.wizardry.Lightning Disc.name=ÐœÐ°Ð³Ð¸Ñ +entity.wizardry.Thunderbolt.name=ÐœÐ°Ð³Ð¸Ñ +entity.wizardry.Decay.name=ÐœÐ°Ð³Ð¸Ñ -entity.wizardry.black_hole.name=Ð§Ñ‘Ñ€Ð½Ð°Ñ Ð”Ñ‹Ñ€Ð° -entity.wizardry.shield.name=Щит -entity.wizardry.meteor.name=Метеор -entity.wizardry.blizzard.name=Метель -entity.wizardry.bubble.name=Пузыри -entity.wizardry.tornado.name=Торнадо -entity.wizardry.lightning_hammer.name=Молот Шторма -entity.wizardry.arrow_rain.name=Дождь Стрел -entity.wizardry.healing_aura.name=Ðура Ð›ÐµÑ‡ÐµÐ½Ð¸Ñ -entity.wizardry.forcefield.name=Силовое Поле -entity.wizardry.ring_of_fire.name=Кольцо ÐžÐ³Ð½Ñ +entity.wizardry.Black Hole.name=Ð§Ñ‘Ñ€Ð½Ð°Ñ Ð”Ñ‹Ñ€Ð° +entity.wizardry.Shield.name=Щит +entity.wizardry.Meteor.name=Метеор +entity.wizardry.Blizzard.name=Метель +entity.wizardry.Bubble.name=Пузыри +entity.wizardry.Tornado.name=Торнадо +entity.wizardry.Lightning Hammer.name=Молот Шторма +entity.wizardry.Arrow Rain.name=Дождь Стрел +entity.wizardry.Healing Aura.name=Ðура Ð›ÐµÑ‡ÐµÐ½Ð¸Ñ +entity.wizardry.Forcefield.name=Силовое Поле +entity.wizardry.Ring of Fire.name=Кольцо ÐžÐ³Ð½Ñ -item_group.wizardry=ВолшебÑтво -item_group.wizardryspells=Волшебные Ð—Ð°ÐºÐ»Ð¸Ð½Ð°Ð½Ð¸Ñ +itemGroup.wizardry=ВолшебÑтво +itemGroup.wizardryspells=Волшебные Ð—Ð°ÐºÐ»Ð¸Ð½Ð°Ð½Ð¸Ñ -achievement.crystal=Любопытный КриÑталл... -achievement.crystal.desc=Добудьте МагичеÑкий КриÑталл -achievement.arcane_initiate=ПоÑвÑщение Ð’ Магию -achievement.arcane_initiate.desc=Создайте Волшебную Палочку Из Золотого Самородка И МагичеÑкого КриÑталла -achievement.apprentice=Ученик Волшебника -achievement.apprentice.desc=ИÑпользуйте Фолиант Ðрканы Ð”Ð»Ñ Ð£Ð»ÑƒÑ‡ÑˆÐµÐ½Ð¸Ñ Ð–ÐµÐ·Ð»Ð° -achievement.master=МаÑтер Магии -achievement.master.desc=Получите Жезл МаÑтера -achievement.all_spells=Маг Ð’Ñех ПрофеÑÑий -achievement.all_spells.desc=ИÑпользуйте Каждое Заклинание Ð’ Игре -achievement.wizard_trade=МагичеÑÐºÐ°Ñ Ð¡Ð´ÐµÐ»ÐºÐ° -achievement.wizard_trade.desc=Купите Предмет У Волшебника -achievement.buy_master_spell=Знание-Ñила -achievement.buy_master_spell.wizardry:desc=Купите заклинание маÑтера у волшебника -achievement.freeze_blaze=Ðе Так Жарко -achievement.freeze_blaze.desc=Заморозьте Блейза -achievement.charge_creeper=Это Ð¡ÐµÐ¹Ñ‡Ð°Ñ Ð’Ð·Ð¾Ñ€Ð²Ñ‘Ñ‚ÑÑ -achievement.charge_creeper.desc="Случайно" ЗарÑдите Крипира -achievement.frankenstein=Франкенштейн -achievement.frankenstein.desc=Привратите Свинью Ð’ Свинозомби С Помощью Молнии -achievement.special_upgrade=ЧародейÑкое РемеÑло -achievement.special_upgrade.desc=Применить Специальное Улучшение Ð”Ð»Ñ Ð–ÐµÐ·Ð»Ð° -achievement.craft_flask=Это ÐœÐ°Ð³Ð¸Ñ Ð’ Бутылке! -achievement.craft_flask.desc=Создайте Ману Ð’ Бутылке -achievement.elemental=Стихии -achievement.elemental.desc=Получите Стихийный Жезл -achievement.armour_set=Теперь Ты ÐаÑтоÑщий Волшебник -achievement.armour_set.desc=Создайте И Экипируйте Полный Ðабор ОдеÑÐ½Ð¸Ñ Ð’Ð¾Ð»ÑˆÐµÐ±Ð½Ð¸ÐºÐ° -achievement.legendary=Легендарный -achievement.legendary.desc=Получите ЧаÑть Легендарного ОдеÑÐ½Ð¸Ñ Ð’Ð¾Ð»ÑˆÐµÐ±Ð½Ð¸ÐºÐ° -achievement.self_destruct=Обратно -achievement.self_destruct.desc=УбейÑÑ Ð¡Ð¾Ð±Ñтвенной Магией -achievement.pig_tornado=Ðе СейчаÑ... -achievement.pig_tornado.desc=Заедьте Ðа Свинье Ð’ Торнадо -achievement.max_out_wand=Полное ОÑнощение -achievement.max_out_wand.desc=Примените макÑимальное количеÑтво улучшений к жезлу маÑтера -achievement.slime_skeleton=Ð›Ð¸Ð¿ÐºÐ°Ñ Ð¡Ð¸Ñ‚ÑƒÐ°Ñ†Ð¸Ñ -achievement.slime_skeleton.desc=Захватите Ñкелета Ñлизьнем -achievement.jam_wizard=Помехи -achievement.jam_wizard.desc=ИÑпользуйте заклинание магичеÑÐºÐ°Ñ Ð³Ð»ÑƒÑˆÐ¸Ð»ÐºÐ° на волшебнике -achievement.identify_spell=Ð¢Ð°Ð¹Ð½Ð°Ñ Ð­ÐºÑпертиза -achievement.identify_spell.wizardry:desc=ИÑпользуйте Ñвиток идентификации Ð´Ð»Ñ Ð½ÐµÐ¸Ð·Ð²ÐµÑтной книги Ð·Ð°ÐºÐ»Ð¸Ð½Ð°Ð½Ð¸Ñ Ð¸Ð»Ð¸ Ñвитка -achievement.element_master=МаÑтер Элементов -achievement.element_master.desc=ИÑпользуйте вÑе Ð·Ð°ÐºÐ»Ð¸Ð½Ð°Ð½Ð¸Ñ Ð»ÑŽÐ±Ð¾Ð³Ð¾ Ñлемента -achievement.anger_wizard=Ты пожалеешь об Ñтом -achievement.anger_wizard.desc=Разозлите волшебника -achievement.defeat_evil_wizard=ПраведноÑть -achievement.defeat_evil_wizard.desc=Победите злого волшебника +achievement.Crystal=Любопытный КриÑталл... +achievement.Crystal.desc=Добудьте МагичеÑкий КриÑталл +achievement.ArcaneInitiate=ПоÑвÑщение Ð’ Магию +achievement.ArcaneInitiate.desc=Создайте Волшебную Палочку Из Золотого Самородка И МагичеÑкого КриÑталла +achievement.Apprentice=Ученик Волшебника +achievement.Apprentice.desc=ИÑпользуйте Фолиант Ðрканы Ð”Ð»Ñ Ð£Ð»ÑƒÑ‡ÑˆÐµÐ½Ð¸Ñ Ð–ÐµÐ·Ð»Ð° +achievement.Master=МаÑтер Магии +achievement.Master.desc=Получите Жезл МаÑтера +achievement.AllSpells=Маг Ð’Ñех ПрофеÑÑий +achievement.AllSpells.desc=ИÑпользуйте Каждое Заклинание Ð’ Игре +achievement.WizardTrade=МагичеÑÐºÐ°Ñ Ð¡Ð´ÐµÐ»ÐºÐ° +achievement.WizardTrade.desc=Купите Предмет У Волшебника +achievement.BuyMasterSpell=Знание-Сила +achievement.BuyMasterSpell.desc=Купите Заклинание МаÑтера У Волшебника +achievement.FreezeBlaze=Ðе Так Жарко +achievement.FreezeBlaze.desc=Заморозьте Блейза +achievement.ChargeCreeper=Это Ð¡ÐµÐ¹Ñ‡Ð°Ñ Ð’Ð·Ð¾Ñ€Ð²Ñ‘Ñ‚ÑÑ +achievement.ChargeCreeper.desc="Случайно" ЗарÑдите Крипира +achievement.Frankenstein=Франкенштейн +achievement.Frankenstein.desc=Привратите Свинью Ð’ Свинозомби С Помощью Молнии +achievement.SpecialUpgrade=ЧародейÑкое РемеÑло +achievement.SpecialUpgrade.desc=Применить Специальное Улучшение Ð”Ð»Ñ Ð–ÐµÐ·Ð»Ð° +achievement.CraftFlask=Это ÐœÐ°Ð³Ð¸Ñ Ð’ Бутылке! +achievement.CraftFlask.desc=Создайте Ману Ð’ Бутылке +achievement.Elemental=Стихии +achievement.Elemental.desc=Получите Стихийный Жезл +achievement.ArmourSet=Теперь Ты ÐаÑтоÑщий Волшебник +achievement.ArmourSet.desc=Создайте И Экипируйте Полный Ðабор ОдеÑÐ½Ð¸Ñ Ð’Ð¾Ð»ÑˆÐµÐ±Ð½Ð¸ÐºÐ° +achievement.Legendary=Легендарный +achievement.Legendary.desc=Получите ЧаÑть Легендарного ОдеÑÐ½Ð¸Ñ Ð’Ð¾Ð»ÑˆÐµÐ±Ð½Ð¸ÐºÐ° +achievement.SelfDestruct=Обратно +achievement.SelfDestruct.desc=УбейÑÑ Ð¡Ð¾Ð±Ñтвенной Магией +achievement.PigTornado=Ðе СейчаÑ... +achievement.PigTornado.desc=Заедьте Ðа Свинье Ð’ Торнадо +tile.transportationStone.confirm=Теперь вы будете возвращены Ñюда при иÑпользовании Ð·Ð°ÐºÐ»Ð¸Ð½Ð°Ð½Ð¸Ñ %1$s +tile.transportationStone.invalid=Сначала вы должны Ñделать круг из 8 камней транÑпортировки! - -tile.wizardry:transportation_stone.confirm=Теперь вы будете возвращены Ñюда при иÑпользовании Ð·Ð°ÐºÐ»Ð¸Ð½Ð°Ð½Ð¸Ñ %1$s -tile.wizardry:transportation_stone.invalid=Сначала вы должны Ñделать круг из 8 камней транÑпортировки! - -container.wizardry:arcane_workbench=ЧародейÑкий Стол -container.wizardry:arcane_workbench.apply=Применить -container.wizardry:arcane_workbench.mana=Мана: -container.wizardry:arcane_workbench.upgrades=Применённые УлучшениÑ: +container.arcaneWorkbench=ЧародейÑкий Стол +container.arcaneWorkbench.apply=Применить +container.arcaneWorkbench.mana=Мана: +container.arcaneWorkbench.upgrades=Применённые УлучшениÑ: tier.basic=Ðачинающий tier.apprentice=Ученик @@ -278,333 +256,270 @@ spelltype.minion=Миньон spell.disabled=%1$s было отключенно в конфиге -spell.wizardry:agility=ЛовкоÑть -spell.wizardry:arc=Дуга -spell.wizardry:arrow_rain=Дождь Стрел -spell.wizardry:black_hole=Ð§Ñ‘Ñ€Ð½Ð°Ñ Ð”Ñ‹Ñ€Ð° -spell.wizardry:blink=Блинк -spell.wizardry:blizzard=Метель -spell.wizardry:bubble=Пузыри -spell.wizardry:chain_lightning=Ð¦ÐµÐ¿Ð½Ð°Ñ ÐœÐ¾Ð»Ð½Ð¸Ñ -spell.wizardry:conjure_bow=Призвать Лук -spell.wizardry:conjure_pickaxe=Призвать Кирку -spell.wizardry:conjure_sword=Призвать Меч -spell.wizardry:cure_effects=Эффект Ð›ÐµÑ‡ÐµÐ½Ð¸Ñ -spell.wizardry:darkness_orb=Ð¢Ñ‘Ð¼Ð½Ð°Ñ Ð¡Ñ„ÐµÑ€Ð° -spell.wizardry:dart=Дротик -spell.wizardry:decay=Гниение -spell.wizardry:detonate=Взрыв -spell.wizardry:diamondflesh=ÐÐ»Ð¼Ð°Ð·Ð½Ð°Ñ ÐšÐ¾Ð¶Ð° -spell.wizardry:entrapment=Захват -spell.wizardry:fireball=Огненный Шар -spell.wizardry:firebolt=Огненный Болт -spell.wizardry:firebomb=ÐžÐ³ÐµÐ½Ð½Ð°Ñ Ð‘Ð¾Ð¼Ð±Ð° -spell.wizardry:fire_resistance=ОгеÑтойкоÑть -spell.wizardry:fire_sigil=ÐžÐ³Ð½ÐµÐ½Ð½Ð°Ñ ÐŸÐµÑ‡Ð°Ñ‚ÑŒ -spell.wizardry:fireskin=ÐžÐ³Ð½ÐµÐ½Ð½Ð°Ñ ÐšÐ¾Ð¶Ð° -spell.wizardry:firestorm=Огненный Шторм -spell.wizardry:flame_ray=Дождь ÐžÐ³Ð½Ñ -spell.wizardry:flaming_axe=Огненный Топор -spell.wizardry:flight=Полёт -spell.wizardry:font_of_vitality=ИÑточник Жизненной Силы -spell.wizardry:force_arrow=Ð¡Ð¸Ð»Ð¾Ð²Ð°Ñ Ð¡Ñ‚Ñ€ÐµÐ»Ð° -spell.wizardry:forcefield=Силовое Поле -spell.wizardry:force_orb=Ð¡Ð¸Ð»Ð¾Ð²Ð°Ñ Ð¡Ñ„ÐµÑ€Ð° -spell.wizardry:forests_curse=ПроклÑтие ЛеÑа -spell.wizardry:freeze=Замораживание -spell.wizardry:mind_trick=Обман Разума -spell.wizardry:frost_axe=ЛедÑной Топор -spell.wizardry:frost_ray=ЛедÑной Луч -spell.wizardry:frost_sigil=ЛедÑÐ½Ð°Ñ ÐŸÐµÑ‡Ð°Ñ‚ÑŒ -spell.wizardry:glide=Скольжение -spell.wizardry:greater_heal=Великое ИÑцеление -spell.wizardry:growth_aura=Ðура РоÑта -spell.wizardry:heal=ИÑцеление -spell.wizardry:heal_ally=ИÑцеление Союзника -spell.wizardry:healing_aura=Ðура ИÑÑ†ÐµÐ»ÐµÐ½Ð¸Ñ -spell.wizardry:homing_spark=СамонаводÑщаÑÑÑ Ð˜Ñкра -spell.wizardry:ice_age=Ледниковый Период -spell.wizardry:ice_charge=ЛедÑной ЗарÑд -spell.wizardry:ice_shard=ЛедÑной ОÑколок -spell.wizardry:ice_shroud=ЛедÑÐ½Ð°Ñ ÐšÐ¾Ð¶Ð° -spell.wizardry:ice_statue=ЛедÑÐ½Ð°Ñ Ð¡Ñ‚Ð°Ñ‚ÑƒÑ -spell.wizardry:ignite=ВоÑпламенение -spell.wizardry:invisibility=ÐевидимоÑть -spell.wizardry:invoke_weather=Вызов Погоды -spell.wizardry:ironflesh=Ð–ÐµÐ»ÐµÐ·Ð½Ð°Ñ ÐšÐ¾Ð¶Ð° -spell.wizardry:levitation=Ð›ÐµÐ²Ð¸Ñ‚Ð°Ñ†Ð¸Ñ -spell.wizardry:life_drain=Похищение Жизни -spell.wizardry:light=Свет -spell.wizardry:lightning_arrow=Стрела Молнии -spell.wizardry:lightning_bolt=Удар Молнии -spell.wizardry:lightning_disc=ДиÑк Молнии -spell.wizardry:lightning_hammer=Молот Молнии -spell.wizardry:lightning_ray=Луч Молнии -spell.wizardry:lightning_sigil=Печать Молнии -spell.wizardry:magic_missile=МагичеÑÐºÐ°Ñ Ð Ð°ÐºÐµÑ‚Ð° -spell.wizardry:metamorphosis=Превращение -spell.wizardry:meteor=Метеор -spell.wizardry:mind_control=Контроль Разума -spell.wizardry:none=[ПуÑтой Слот] -spell.wizardry:petrify=Окаменение -spell.wizardry:phase_step=Фазовый Шаг -spell.wizardry:plague_of_darkness=Чума Темноты -spell.wizardry:poison=Отравление -spell.wizardry:poison_bomb=Ð¯Ð´Ð¾Ð²Ð¸Ñ‚Ð°Ñ Ð‘Ð¾Ð¼Ð±Ð° -spell.wizardry:replenish_hunger=ÐаÑыщение -spell.wizardry:ring_of_fire=Кольцо ÐžÐ³Ð½Ñ -spell.wizardry:shadow_ward=Ð¢ÐµÐ½ÐµÐ²Ð°Ñ Ð—Ð°Ñ‰Ð¸Ñ‚Ð° -spell.wizardry:shield=Щит -spell.wizardry:shockwave=Ð£Ð´Ð°Ñ€Ð½Ð°Ñ Ð’Ð¾Ð»Ð½Ð° -spell.wizardry:silverfish_swarm=Рой Чешуйниц -spell.wizardry:slime=Слизь -spell.wizardry:snare=Ловушка -spell.wizardry:snowball=Снежок -spell.wizardry:smoke_bomb=Ð”Ñ‹Ð¼Ð¾Ð²Ð°Ñ Ð‘Ð¾Ð¼Ð±Ð° -spell.wizardry:spark_bomb=Бомба ИÑкр -spell.wizardry:spectral_pathway=Спектральный Путь -spell.wizardry:spider_swarm=Рой Пауков -spell.wizardry:static_aura=СтатичеÑÐºÐ°Ñ Ðура -spell.wizardry:summon_blaze=Призыв Блейза -spell.wizardry:summon_ice_giant=Призыв ЛедÑного Гиганта -spell.wizardry:summon_ice_wraith=Призыв ЛедÑного Призрака -spell.wizardry:summon_iron_golem=Призыв Железного Голема -spell.wizardry:summon_lightning_wraith=Призыв Штормового Призрака -spell.wizardry:summon_phoenix=Призыв ФеникÑа -spell.wizardry:summon_shadow_wraith=Призыв Тёмного Призрака -spell.wizardry:summon_skeleton=Призыв Скелета -spell.wizardry:summon_skeleton_legion=Призыв Легиона Скелета -spell.wizardry:summon_snow_golem=Призыв Снеговика -spell.wizardry:summon_spirit_horse=Призыв Призрачной Лошади -spell.wizardry:summon_spirit_wolf=Призыв Призрачного Волка -spell.wizardry:summon_wither_skeleton=Призыв Скелета-ИÑÑÑƒÑˆÐ¸Ñ‚ÐµÐ»Ñ -spell.wizardry:summon_zombie=Призыв Зомби -spell.wizardry:telekinesis=Телекинез -spell.wizardry:thunderbolt=Штормовой Удар -spell.wizardry:thunderstorm=Гроза -spell.wizardry:tornado=Торнадо -spell.wizardry:transience=БыÑтрота -spell.wizardry:transportation=ТранÑпортировка -spell.wizardry:vanishing_box=ИÑÑ‡ÐµÐ·Ð°ÑŽÑ‰Ð°Ñ ÐšÐ¾Ñ€Ð¾Ð±ÐºÐ° -spell.wizardry:wall_of_frost=Стена Мороза -spell.wizardry:water_breathing=Дыхание Под Водой -spell.wizardry:whirlwind=Вихрь -spell.wizardry:wither=ИÑÑушение -spell.wizardry:wither_skull=Череп ИÑÑÑƒÑˆÐ¸Ñ‚ÐµÐ»Ñ -spell.wizardry:leap=Прыжок -spell.wizardry:pocket_furnace=ÐšÐ°Ñ€Ð¼Ð°Ð½Ð½Ð°Ñ ÐŸÐµÑ‡ÑŒ -spell.wizardry:intimidate=УÑтрашение -spell.wizardry:banish=Изгнание -spell.wizardry:sixth_sense=ШеÑтое ЧувÑтво -spell.wizardry:darkvision=Тёмное Зрение -spell.wizardry:imbue_weapon=Зачарованное Оружие -spell.wizardry:pocket_workbench=Карманный ВерÑтак -spell.wizardry:clairvoyance=ЯÑновидение -spell.wizardry:invigorating_presence=Воодушевление -spell.wizardry:oakflesh=Ð”ÑƒÐ±Ð¾Ð²Ð°Ñ ÐŸÐ»Ð¾Ñ‚ÑŒ -spell.wizardry:flaming_weapon=Пылающее Оружие -spell.wizardry:greater_fireball=Большой Огненный Шар -spell.wizardry:ice_lance=ЛедÑное Копье -spell.wizardry:freezing_weapon=ЛедÑное Оружие -spell.wizardry:ice_spikes=ЛедÑные Шипы -spell.wizardry:lightning_pulse=Ð˜Ð¼Ð¿ÑƒÐ»ÑŒÑ ÐœÐ¾Ð»Ð½Ð¸Ð¸ -spell.wizardry:curse_of_soulbinding=ПроклÑтие СвзÑзанной Души -spell.wizardry:decoy=Приманка -spell.wizardry:arcane_jammer=МагичеÑÐºÐ°Ñ Ð“Ð»ÑƒÑˆÐ¸Ð»ÐºÐ° -spell.wizardry:conjure_armour=ÐŸÑ€Ð¸Ð·Ñ‹Ð²Ð½Ð°Ñ Ð‘Ñ€Ð¾Ð½Ñ -spell.wizardry:group_heal=МаÑÑовое ИÑцеление -spell.wizardry:summon_storm_elemental=Призыв Грозового Ð­Ð»ÐµÐ¼ÐµÐ½Ñ‚Ð°Ð»Ñ -spell.wizardry:lightning_web=Поток Молнии -spell.wizardry:font_of_mana=Купель Маны -spell.wizardry:earthquake=ЗемлетрÑÑение -spell.wizardry:hailstorm=Град +spell.agility=ЛовкоÑть +spell.arc=Дуга +spell.arrow_rain=Дождь Стрел +spell.black_hole=Ð§Ñ‘Ñ€Ð½Ð°Ñ Ð”Ñ‹Ñ€Ð° +spell.blink=Блинк +spell.blizzard=Метель +spell.bubble=Пузыри +spell.chain_lightning=Ð¦ÐµÐ¿Ð½Ð°Ñ ÐœÐ¾Ð»Ð½Ð¸Ñ +spell.conjure_bow=Призвать Лук +spell.conjure_pickaxe=Призвать Кирку +spell.conjure_sword=Призвать Меч +spell.cure_effects=Эффект Ð›ÐµÑ‡ÐµÐ½Ð¸Ñ +spell.darkness_orb=Ð¢Ñ‘Ð¼Ð½Ð°Ñ Ð¡Ñ„ÐµÑ€Ð° +spell.dart=Дротик +spell.decay=Гниение +spell.detonate=Взрыв +spell.diamondflesh=ÐÐ»Ð¼Ð°Ð·Ð½Ð°Ñ ÐšÐ¾Ð¶Ð° +spell.entrapment=Захват +spell.fireball=Огненный Шар +spell.firebolt=Огненный Болт +spell.firebomb=ÐžÐ³ÐµÐ½Ð½Ð°Ñ Ð‘Ð¾Ð¼Ð±Ð° +spell.fire_resistance=ОгеÑтойкоÑть +spell.fire_sigil=ÐžÐ³Ð½ÐµÐ½Ð½Ð°Ñ ÐŸÐµÑ‡Ð°Ñ‚ÑŒ +spell.fireskin=ÐžÐ³Ð½ÐµÐ½Ð½Ð°Ñ ÐšÐ¾Ð¶Ð° +spell.firestorm=Огненный Шторм +spell.flame_ray=Дождь ÐžÐ³Ð½Ñ +spell.flaming_axe=Огненный Топор +spell.flight=Полёт +spell.font_of_vitality=ИÑточник Жизненной Силы +spell.force_arrow=Ð¡Ð¸Ð»Ð¾Ð²Ð°Ñ Ð¡Ñ‚Ñ€ÐµÐ»Ð° +spell.forcefield=Силовое Поле +spell.force_orb=Ð¡Ð¸Ð»Ð¾Ð²Ð°Ñ Ð¡Ñ„ÐµÑ€Ð° +spell.forests_curse=ПроклÑтие ЛеÑа +spell.freeze=Замораживание +spell.frost_axe=ЛедÑной Топор +spell.frost_ray=ЛедÑной Луч +spell.frost_sigil=ЛедÑÐ½Ð°Ñ ÐŸÐµÑ‡Ð°Ñ‚ÑŒ +spell.glide=Скольжение +spell.greater_heal=Великое ИÑцеление +spell.growth_aura=Ðура РоÑта +spell.heal=ИÑцеление +spell.heal_ally=ИÑцеление Союзника +spell.healing_aura=Ðура ИÑÑ†ÐµÐ»ÐµÐ½Ð¸Ñ +spell.homing_spark=СамонаводÑщаÑÑÑ Ð˜Ñкра +spell.ice_age=Ледниковый Период +spell.ice_charge=ЛедÑной ЗарÑд +spell.ice_shard=ЛедÑной ОÑколок +spell.ice_shroud=ЛедÑÐ½Ð°Ñ ÐšÐ¾Ð¶Ð° +spell.ice_statue=ЛедÑÐ½Ð°Ñ Ð¡Ñ‚Ð°Ñ‚ÑƒÑ +spell.ignite=ВоÑпламенение +spell.invisibility=ÐевидимоÑть +spell.invoke_weather=Вызов Погоды +spell.ironflesh=Ð–ÐµÐ»ÐµÐ·Ð½Ð°Ñ ÐšÐ¾Ð¶Ð° +spell.levitation=Ð›ÐµÐ²Ð¸Ñ‚Ð°Ñ†Ð¸Ñ +spell.life_drain=Похищение Жизни +spell.light=Свет +spell.lightning_arrow=Стрела Молнии +spell.lightning_bolt=Удар Молнии +spell.lightning_disc=ДиÑк Молнии +spell.lightning_hammer=Молот Молнии +spell.lightning_ray=Луч Молнии +spell.lightning_sigil=Печать Молнии +spell.magic_missile=МагичеÑÐºÐ°Ñ Ð Ð°ÐºÐµÑ‚Ð° +spell.metamorphosis=Превращение +spell.meteor=Метеор +spell.mind_control=Контроль Разума +spell.none=[ПуÑтой Слот] +spell.petrify=Окаменение +spell.phase_step=Фазовый Шаг +spell.plague_of_darkness=Чума Темноты +spell.poison=Отравление +spell.poison_bomb=Ð¯Ð´Ð¾Ð²Ð¸Ñ‚Ð°Ñ Ð‘Ð¾Ð¼Ð±Ð° +spell.replenish_hunger=ÐаÑыщение +spell.ring_of_fire=Кольцо ÐžÐ³Ð½Ñ +spell.shadow_ward=Ð¢ÐµÐ½ÐµÐ²Ð°Ñ Ð—Ð°Ñ‰Ð¸Ñ‚Ð° +spell.shield=Щит +spell.shockwave=Ð£Ð´Ð°Ñ€Ð½Ð°Ñ Ð’Ð¾Ð»Ð½Ð° +spell.silverfish_swarm=Рой Чешуйниц +spell.slime=Слизь +spell.snare=Ловушка +spell.snowball=Снежок +spell.spark_bomb=Бомба ИÑкр +spell.spectral_pathway=Спектральный Путь +spell.spider_swarm=Рой Пауков +spell.static_aura=СтатичеÑÐºÐ°Ñ Ðура +spell.summon_blaze=Призыв Блейза +spell.summon_ice_giant=Призыв ЛедÑного Гиганта +spell.summon_ice_wraith=Призыв ЛедÑного Призрака +spell.summon_iron_golem=Призыв Железного Голема +spell.summon_lightning_wraith=Призыв Штормового Призрака +spell.summon_phoenix=Призыв ФеникÑа +spell.summon_shadow_wraith=Призыв Тёмного Призрака +spell.summon_skeleton=Призыв Скелета +spell.summon_skeleton_legion=Призыв Легиона Скелета +spell.summon_snow_golem=Призыв Снеговика +spell.summon_spirit_horse=Призыв Призрачной Лошади +spell.summon_spirit_wolf=Призыв Призрачного Волка +spell.summon_wither_skeleton=Призыв Скелета-ИÑÑÑƒÑˆÐ¸Ñ‚ÐµÐ»Ñ +spell.summon_zombie=Призыв Зомби +spell.telekinesis=Телекинез +spell.thunderbolt=Штормовой Удар +spell.thunderstorm=Гроза +spell.tornado=Торнадо +spell.transience=БыÑтрота +spell.transportation=ТранÑпортировка +spell.vanishing_box=ИÑÑ‡ÐµÐ·Ð°ÑŽÑ‰Ð°Ñ ÐšÐ¾Ñ€Ð¾Ð±ÐºÐ° +spell.wall_of_frost=Стена Мороза +spell.water_breathing=Дыхание Под Водой +spell.whirlwind=Вихрь +spell.wither=ИÑÑушение +spell.wither_skull=Череп ИÑÑÑƒÑˆÐ¸Ñ‚ÐµÐ»Ñ +spell.agility.desc=Дает Заклинателю Более Ð’Ñ‹Ñокую СкороÑть ÐŸÐµÑ€ÐµÐ´Ð²Ð¸Ð¶ÐµÐ½Ð¸Ñ Ð˜ Большой Прижок на 30 Секунд. +spell.arc.desc=Ð’Ñ‹Ñтреливает ИÑкрой Молнии Ð’ Цель +spell.arrow_rain.desc="Лучники, огонь!" +spell.black_hole.desc=Разрывай РеальноÑть Ðа ЧаÑти +spell.blink.desc=Телепортирует Ð—Ð°ÐºÐ»Ð¸Ð½Ð°Ñ‚ÐµÐ»Ñ Ð’ Любой Блок, Ðа Который Он Указывает, Ð’ Определенном РадиуÑе. +spell.blizzard.desc=Создает Зону Закрученного ЛедÑного Ветра, ÐšÐ¾Ñ‚Ð¾Ñ€Ð°Ñ Ð—Ð°Ð¼ÐµÐ´Ð»Ñет И ПоÑтоÑнно Повреждает Ð’Ñе, Что Попало Внутрь. Заклинатель ÐевоÑприимчив К Урону, Ðо Ð’Ñе Еще ЗамедлÑетÑÑ. +spell.bubble.desc=СтрелÑет Потоком Пузырьков, Который Помещает СущеÑтво Ð’ Большой Пузырь, При Это Поднимает Его. Цель Будет Падать Или ПроÑто Получит Урон. +spell.chain_lightning.desc=Ð’Ñ‹Ñтреливает ИÑкру Молнии У Цели, ÐšÐ¾Ñ‚Ð¾Ñ€Ð°Ñ Ð—Ð°Ñ‚ÐµÐ¼ ЦеплÑетÑÑ Ðš Дополнительным ЦелÑм До Двух Раз. +spell.conjure_bow.desc=Создает Спектральный Лук С Ðеограниченными Стрелами, Который ДлитÑÑ 30 Секунд. +spell.conjure_pickaxe.desc=Создает Спектральную Кирку Равной Силы Железной Кирке, ÐšÐ¾Ñ‚Ð¾Ñ€Ð°Ñ Ð”Ð»Ð¸Ñ‚ÑÑ 30 СÑекунд. +spell.conjure_sword.desc=Создает Спектральный Меч Одинаковой Силы С Железным Мечом, Который ДлитÑÑ 30 Секунд. +spell.cure_effects.desc=УдалÑет Ð’Ñе Эффекты Зелий, Которые Ð’ ÐаÑтоÑщее Ð’Ñ€ÐµÐ¼Ñ Ð’Ð¾Ð·Ð´ÐµÐ¹Ñтвуют Ðа ЗаклинателÑ, Хорошие Или Плохие. +spell.darkness_orb.desc=Ð’Ñ‹Ñтреливает Медленно ПеремещающимÑÑ Ð‘Ð¾Ð»Ñ‚Ð¾Ð¼ Тёмной Энергии Ð’ Ðаправлении, Ðа Которое Ð’Ñ‹ Указываете, Которое Вызывает Гниение Ð’Ñего, Что Попадает. +spell.dart.desc=Ð’Ñ‹Ñтреливает Дротик Ð’ Том Ðаправлении, Ð’ Котором Ð’Ñ‹ Указываете, Которое ÐаноÑит Урон И ОÑлаблÑет Его Цель. +spell.decay.desc=Создает ПÑтно Гнили Ðа Земле, Которое Заражает Любое СущеÑтво, Которое Его КаÑаетÑÑ, Ð’Ñ‹Ð·Ñ‹Ð²Ð°Ñ Ð”Ð»Ð¸Ñ‚ÐµÐ»ÑŒÐ½Ñ‹Ð¹ Урон Со Временем И РаÑпроÑтранÑÑ Ð‘Ð¾Ð»ÑŒÑˆÐµ Гнили, Где Бы Он Ðи Ходил. +spell.detonate.desc=Вызывает Взрыв, Ð’ Указанном МеÑте, ÐаноÑÑ Ð£Ñ€Ð¾Ð½ Ð’Ñем ÐаходÑщимÑÑ ÐŸÐ¾Ð±Ð»Ð¸Ð·Ð¾Ñти СущеÑтвам, Ð’ÐºÐ»ÑŽÑ‡Ð°Ñ Ð—Ð°ÐºÐ»Ð¸Ð½Ð°Ñ‚ÐµÐ»Ñ, ЕÑли Они Слишком Близко. +spell.diamondflesh.desc="Твои Стрелы Мне Ðе ПодходÑÑ‚!" +spell.entrapment.desc=Заманивает Цель Ð’ Сферу Темноты, ÐšÐ¾Ñ‚Ð¾Ñ€Ð°Ñ ÐŸÐ¾Ð´Ð½Ð¸Ð¼Ð°ÐµÑ‚ Её Вверх И ПоÑтоÑнно Её Повреждает. +spell.fireball.desc=ЗапуÑкает Огненный Шар Ð’ Том Ðаправлении, Ð’ Которое Ð’Ñ‹ Указали. +spell.firebolt.desc=СтрелÑет Ðа Ðебольшом РаÑÑтоÑнии От ВаÑ. +spell.firebomb.desc=БраÑает Зажигательную Бомбу Ð’ Ðаправлении, Которое Ð’Ñ‹ Указываете, ÐšÐ¾Ñ‚Ð¾Ñ€Ð°Ñ Ð’Ð·Ñ€Ñ‹Ð²Ð°ÐµÑ‚ÑÑ ÐŸÑ€Ð¸ Ударе, ÐŸÐ¾Ð´Ð¶Ð¸Ð³Ð°Ñ Ð¦ÐµÐ»Ð¸. +spell.fire_resistance.desc=Даёт Сопротивление Огню Ðа 30% +spell.fire_sigil.desc=Помещает МагичеÑкую Ловушку ÐžÐ³Ð½Ñ Ðа Землю, ÐšÐ¾Ñ‚Ð¾Ñ€Ð°Ñ ÐаноÑит Урон И Поджигает СущеÑтво, Которое ÐаÑтупило Ðа Ловушку +spell.fireskin.desc=Покрывает Ð—Ð°ÐºÐ»Ð¸Ð½Ð°Ñ‚ÐµÐ»Ñ ÐžÐ³Ð½Ñ‘Ð¼ Ðа 30 Секунд. ЕÑли Заклинатель Будет Ðтакован, То Враг ЗагоритÑÑ. +spell.firestorm.desc="Я Дракон." +spell.flame_ray.desc=Создает Поток Пламени Ð’ Ðаправлении, Ðа Которое Ð’Ñ‹ Указали, Который Поджигает И ПоÑтоÑнно Повреждает Цели. +spell.flaming_axe.desc=Создает Огненный Топор, Который Поджигает Врагов При Попадании. ДлитÑÑ 30 Секунд. +spell.flight.desc=ПарÑщий, Как Орёл. +spell.font_of_vitality.desc=Это ПотрÑÑающе. +spell.force_arrow.desc=СтрелÑет Стрелой Силы Ð’ Ðаправлении, Ðа Которое Ð’Ñ‹ Указываете. +spell.forcefield.desc=Создает Силовое Поле Вокруг ЗаклинателÑ, Который Отталкивает СущеÑтв И ОтклонÑет СнарÑды. +spell.force_orb.desc=ЗапуÑкает Сферу Силы, ÐšÐ¾Ñ‚Ð¾Ñ€Ð°Ñ ÐаноÑит Урон И ОтбраÑывает Близких СущеÑтв При Ударе. +spell.forests_curse.desc="Как Ты ПоÑмел Войти Ð’ Мой ЛеÑ?!" +spell.freeze.desc=Замораживает Цель Ðа 10 Секунд. Также Заморозит Воду И СоздаÑÑ‚ Снег Ðа Земле. +spell.frost_axe.desc=Создает ЛедÑной Топор, Который Замораживает Врагов При Попадании. ДлитÑÑ 30 Секунд. +spell.frost_ray.desc=Создает Поток Мороза Ð’ Ðаправлении, Ðа Которое Ð’Ñ‹ Указываете, Который ЗамедлÑет И ПоÑтоÑнно Повреждает Цели. +spell.frost_sigil.desc=Помещает МагичеÑкую ЛедÑную Ловушку Ðа Землю, ÐšÐ¾Ñ‚Ð¾Ñ€Ð°Ñ ÐаноÑит Урон И Замораживает СущеÑтво, Которое ÐаÑтупило Ðа Ðеё. +spell.glide.desc=ПозволÑет Заклинателю Скользить Вниз, ÐаходÑÑÑŒ Ð’ Воздухе, Ð£Ð´ÐµÑ€Ð¶Ð¸Ð²Ð°Ñ ÐšÐ½Ð¾Ð¿ÐºÑƒ ИÑÐ¿Ð¾Ð»ÑŒÐ·Ð¾Ð²Ð°Ð½Ð¸Ñ ÐŸÑ€ÐµÐ´Ð¼ÐµÑ‚Ð°. +spell.greater_heal.desc=ВоÑÑтанавливает Заклинателю 4 Сердца. +spell.growth_aura.desc=Выращивает Ð’Ñе Зерновые Культуры РÑдом С Заклинателем. +spell.heal.desc=ИÑцелÑет Ð—Ð°ÐºÐ»Ð¸Ð½Ð°Ñ‚ÐµÐ»Ñ Ðа 2 Сердца. +spell.heal_ally.desc=ВоÑÑтанавливает Цели 2 С Половиной Сердца. +spell.healing_aura.desc=Создает Зону ИÑцелÑющей Энергии, ÐšÐ¾Ñ‚Ð¾Ñ€Ð°Ñ Ð’Ð¾ÑÑтанавливает Здоровье Кого-либо Внутри Ðего, Кроме Ðежити, ÐšÐ¾Ñ‚Ð¾Ñ€Ð°Ñ ÐœÐµÐ´Ð»ÐµÐ½Ð½Ð¾ Получает Урон. +spell.homing_spark.desc=Создает Плавающую ИÑкру, ÐšÐ¾Ñ‚Ð¾Ñ€Ð°Ñ Ð”Ð²Ð¸Ð¶ÐµÑ‚ÑÑ Ðš Врагам. +spell.ice_age.desc="Ð’Ñ‹ Замерзнете ÐавÑегда!" +spell.ice_charge.desc=ЗапуÑкает ЗарÑд Льда, Который ВзрываетÑÑ ÐŸÑ€Ð¸ Ударе, Ð—Ð°Ð¼Ð¾Ñ€Ð°Ð¶Ð¸Ð²Ð°Ñ Ð‘Ð»Ð¸Ð·Ð»ÐµÐ¶Ð°Ñ‰Ð¸Ñ… СущеÑтв И ВыпуÑÐºÐ°Ñ ÐžÑколки Во Ð’Ñех ÐаправлениÑÑ…. +spell.ice_shard.desc=Ð’Ñ‹Ñтреливает ЛедÑной ОÑколок Ð’ Том Ðаправлении, Ð’ Котором Ð’Ñ‹ Указали, Который ÐаноÑит Урон И ЗамедлÑет Цель При Попадании. +spell.ice_shroud.desc=Окутывает Ð—Ð°ÐºÐ»Ð¸Ð½Ð°Ñ‚ÐµÐ»Ñ Ð’Ð¾ Льду Ðа 30 Секунд, Ð—Ð°Ð¼Ð¾Ñ€Ð°Ð¶Ð¸Ð²Ð°Ñ Ð’Ñе, Что Попадает Или Попадает. +spell.ice_statue.desc=Замораживает Цель Ðа 20 Секунд Или До Тех Пор, Пока Она Ðе ВырветÑÑ Ðаружу. Цель Ðе Может ДвигатьÑÑ Ð˜Ð»Ð¸ Делать Что-либо Ð’ Замороженном СоÑтоÑнии, Ðо Также ÐевоÑприимчива К Любому Урону. +spell.ignite.desc=УÑтанавливает Огонь Ð’ Течение 10 Секунд. Также Работает Как Кремень И Сталь. +spell.invisibility.desc=Делает Ð—Ð°ÐºÐ»Ð¸Ð½Ð°Ñ‚ÐµÐ»Ñ Ðевидимым Ð’ Течение 30 Секунд. +spell.invoke_weather.desc=ИзменÑет Погоду Ð’ Мире. +spell.ironflesh.desc=Увеличивает Сопротивление К Оглушению Ð—Ð°ÐºÐ»Ð¸Ð½Ð°Ñ‚ÐµÐ»Ñ Ðа 30 Секунд. +spell.levitation.desc=Поднимает Ð—Ð°ÐºÐ»Ð¸Ð½Ð°Ñ‚ÐµÐ»Ñ Ð’Ð²ÐµÑ€Ñ… При Ðажатой Кнопке ИÑÐ¿Ð¾Ð»ÑŒÐ·Ð¾Ð²Ð°Ð½Ð¸Ñ ÐŸÑ€ÐµÐ´Ð¼ÐµÑ‚Ð°. Также Будет Ðейтрализован Урон От ПадениÑ, ЕÑли Он ИÑпользуетÑÑ Ð”Ð¾ Удара Об Землю. +spell.life_drain.desc=Создает Поток ИÑÑушающей Энергии Ð’ Ðаправлении, Ðа Которое Ð’Ñ‹ Указываете, Который ИÑтощает Жизнь Цели И ИÑпользует Её Ð”Ð»Ñ ÐŸÐ¾Ñтепенного ВоÑÑÑ‚Ð°Ð½Ð¾Ð²Ð»ÐµÐ½Ð¸Ñ Ð—Ð´Ð¾Ñ€Ð¾Ð²ÑŒÑ. +spell.light.desc=Создает МагичеÑкую Точку Света, ÐšÐ¾Ñ‚Ð¾Ñ€Ð°Ñ ÐžÑвещает Окружающую ОблаÑть. ДлитÑÑ 30 Секунд. +spell.lightning_arrow.desc=СтрелÑет Стрелой Молнии Ð’ Ðаправлении, Ðа Которое Ð’Ñ‹ Указываете. +spell.lightning_bolt.desc=ЗаÑтавлÑет Молнию Ударить Туда, Куда Ð’Ñ‹ Указываете. +spell.lightning_disc.desc=ПоÑылает ДиÑк Молнии, ЛетÑщий Ð’ Ðаправлении, Ðа Которое Ð’Ñ‹ Указали И Ищет Цели. +spell.lightning_hammer.desc="Я Поражу Ð¢ÐµÐ±Ñ Ð“Ð½ÐµÐ²Ð¾Ð¼ ÐебеÑ!" +spell.lightning_ray.desc=Создает Поток Молнии Ð’ Ðаправлении, Ðа Которое Ð’Ñ‹ Указываете, Который ПоÑтоÑнно Повреждает Цели. +spell.lightning_sigil.desc=Помещает МагичеÑкую МолниеноÑную Ловушку Ðа Землю, ÐšÐ¾Ñ‚Ð¾Ñ€Ð°Ñ ÐаноÑит Урон СущеÑтву, Которое ÐаÑтупило Ðа Ловушку, И Приковывает Молнии К Другим Близлежащим СущеÑтвам. +spell.magic_missile.desc=Ð’Ñ‹Ñтреливает МагичеÑкий ЗарÑд Ð’ Том Ðаправлении, Ð’ Котором Ð’Ñ‹ Указали. +spell.metamorphosis.desc=ИзменÑет Цель Ð’ Другую Форму. Работает Только Ðа Ðекоторых СущеÑтвах. +spell.meteor.desc=Ðекоторые Волшебники ПроÑто ХотÑÑ‚, Чтобы Мир Горел ... +spell.mind_control.desc=Контролирует Ум Цели, ЗаÑтавлÑÑ Ð•Ñ‘ Ðтаковать Ближайшую Живую Цель, Отличную От ЗаклинателÑ. Когда Это СущеÑтво Будет Убито, Цель ВернетÑÑ Ðš Ðормальной Жизни. +spell.none.desc=Чтобы Получить Книгу Ð—Ð°ÐºÐ»Ð¸Ð½Ð°Ð½Ð¸Ñ /give command, иÑпользовать метаданные: /give [player] wizardry:spell_book 1 [spell id] (if you found this book in a chest, some other mod has messed things up). +spell.petrify.desc=Повергает Цель Ð’ Камень До Следующего Заката Или Пока Ðе Будет Разбит. Цель Ðе Может ДвигатьÑÑ Ð˜Ð»Ð¸ Делать Что-либо, Пока Она Окаменела, Ðо Также ÐевоÑприимчива К Любому Урону. +spell.phase_step.desc=Телепортирует Ð—Ð°ÐºÐ»Ð¸Ð½Ð°Ñ‚ÐµÐ»Ñ Ð§ÐµÑ€ÐµÐ· Стену Толщиной 1 Блок Перед Ðим. Улучшение ДальноÑти Увеличит Толщину, Через Которую Ð’Ñ‹ Сможете ТелепортироватьÑÑ. +spell.plague_of_darkness.desc=Тьма Поглотит Их Ð’Ñех... +spell.poison.desc=Ð’Ñ‹Ñтреливает Яд Ð’ Ðаправлении, Ðа Которое Ð’Ñ‹ Указываете. +spell.poison_bomb.desc=БроÑает Ядовитую Бомбу Ð’ Ðаправлении, Которое Ð’Ñ‹ Указываете, ÐšÐ¾Ñ‚Ð¾Ñ€Ð°Ñ Ð’Ð·Ñ€Ñ‹Ð²Ð°ÐµÑ‚ÑÑ ÐŸÑ€Ð¸ Ударе, ОтравлÑÑ Ð‘Ð»Ð¸Ð·ÐºÐ¸Ñ… СущеÑтв. +spell.replenish_hunger.desc=ПополнÑет Ð—Ð°Ð¿Ð°Ñ ÐŸÐ¸Ñ‰Ð¸ Ð—Ð°ÐºÐ»Ð¸Ð½Ð°Ñ‚ÐµÐ»Ñ Ðа 6 Пунктов Голода. +spell.ring_of_fire.desc=Создает Огненное Кольцо Вокруг ЗаклинателÑ, ÐаноÑÑ Ð£Ñ€Ð¾Ð½ Ð’Ñем Ближайшим Противникам И ÐŸÐ¾Ð´Ð¶Ð¸Ð³Ð°Ñ Ð˜Ñ…. +spell.shadow_ward.desc=Создает Стену Тьмы Перед Заклинателем, ÐšÐ¾Ñ‚Ð¾Ñ€Ð°Ñ ÐаноÑит Ðападающему Половину Ð’Ñего ВходÑщего Урона. +spell.shield.desc=Создает Защитный Барьер Силы, Который Блокирует СнарÑды И Магию. Также Дает Заклинателю Слабый Эффект СопротивлениÑ. +spell.shockwave.desc=Бум. +spell.silverfish_swarm.desc="ÐÐÐÐÐ! ИХ ОЧЕÐЬ МÐОГО!" +spell.slime.desc=Охватывает Цель Слизью, ÐšÐ¾Ñ‚Ð¾Ñ€Ð°Ñ Ð—Ð°Ð¼ÐµÐ´Ð»Ñет И ПоÑтоÑнно Её Повреждает. Слизь РазрываетÑÑ Ð§ÐµÑ€ÐµÐ· 10 Секунд. +spell.snare.desc=УÑтанавливает Ловушку Ðа Земле, ÐšÐ¾Ñ‚Ð¾Ñ€Ð°Ñ ÐаноÑит Урон И Кратко ЗамедлÑет СущеÑтво, Которое ÐаÑтупило Ðа Ловушку. +spell.snowball.desc=Launches a snowball in the direction you are pointing. +spell.spark_bomb.desc=ЗапуÑкает Бомбу ИÑкр Ð’ Том Ðаправлении, Ð’ Котором Ð’Ñ‹ Указываете. ИÑкры ПоÑвлÑÑŽÑ‚ÑÑ Ð£ Ближайших Противников При Ударе. +spell.spectral_pathway.desc=Создает Перед Вами Ðеразрушимый МагичеÑкий МоÑÑ‚, Который ПроÑтираетÑÑ Ðа 15 Блоков. Через 60 Секунд МоÑÑ‚ ИÑчезает. +spell.spider_swarm.desc=Призывает Рой Ядовитых Пауков, Чтобы СражатьÑÑ Ð—Ð° ВаÑ. Пауки ИÑчезнут Через 30 Секунд Или ЕÑли Они Будут Убиты. +spell.static_aura.desc=Окружает Ð—Ð°ÐºÐ»Ð¸Ð½Ð°Ñ‚ÐµÐ»Ñ ÐœÐ¾Ð»Ð½Ð¸ÐµÐ¹ Ð’ Течение 30 Секунд, СтрелÑет ИÑкрами Молнии Во Ð’Ñе, Что Ударит ВаÑ. +spell.summon_blaze.desc=Призывает Блейза, Чтобы СражатьÑÑ Ð—Ð° ВаÑ. Блейз ИÑчезнет Через 30 Секунд Или ЕÑли Он Будет Убит. +spell.summon_ice_giant.desc="Разбей Их!" +spell.summon_ice_wraith.desc=Призывает ЛедÑного Призрака, Который СражаетÑÑ Ð—Ð° ВаÑ. ЛедÑной Призрак ИÑчезнет Через 30 Секунд Или ЕÑли Он Будет Убит. +spell.summon_iron_golem.desc=ÐвтоматичеÑкий Ðвтономный Ðвтомат. +spell.summon_lightning_wraith.desc=Призывает МолниеноÑного Призрака СражатьÑÑ Ð—Ð° ВаÑ. МолниеноÑный Призрак ИÑчезнет Через 30 Секунд Или ЕÑли Он Будет Убит. +spell.summon_phoenix.desc=Из Пепла... +spell.summon_shadow_wraith.desc=Призывает Тёмного Призрака, Который Будет СражатьÑÑ Ð—Ð° ВаÑ. +spell.summon_skeleton.desc=Призывает Скелета, Который СражаетÑÑ Ð—Ð° ваÑ. Скелет ИÑчезнет Через 30 Секунд Или ЕÑли Он Будет Убит. +spell.summon_skeleton_legion.desc="ВоÑÑтань, ÐÑ€Ð¼Ð¸Ñ Ðежети!" +spell.summon_snow_golem.desc=Создает Снежного Голема, Который СражаетÑÑ Ð—Ð° ВаÑ. ДлитÑÑ Ð”Ð¾ Тех Пор, Пока Ðе Умрет Снежный Голем. +spell.summon_spirit_horse.desc=Призывает Ð”Ð»Ñ Ð’Ð°Ñ Ð’ÐµÑ€Ñ…Ð¾Ð²ÑƒÑŽ Лошадь. ÐŸÑ€Ð¸Ð·Ñ€Ð°Ñ‡Ð½Ð°Ñ Ð›Ð¾ÑˆÐ°Ð´ÑŒ ИÑчезнет Через Ðекоторое Ð’Ñ€ÐµÐ¼Ñ ÐŸÐ¾Ñле Того, Как Ð’Ñ‹ С Ðеё Слезите, Или Ð’Ñ‹ Можете Убрать Её С Помощью Жезла - Щелкните Правой Кнопкой Мыши Ðа Ðей. +spell.summon_spirit_wolf.desc=Призывает Спутника Духа Волка, Который СражаетÑÑ Ð—Ð° ВаÑ. Призрачный Волк ИÑчезнет, Только ЕÑли Его Убьют, Или Ð’Ñ‹ Можете Его Убрать, Щелкнув Правой Кнопкой Мыши Ðа Ðём С Помощью Жезла. +spell.summon_wither_skeleton.desc=Призывает Скелета-ИÑÑушителÑ, Который СражаетÑÑ Ð—Ð° ВаÑ. Скелет-ИÑÑушитель ИÑчезнет Через 30 Секунд Или ЕÑли Он Будет Убит. +spell.summon_zombie.desc=Призывает Зомби, Который СражаетÑÑ Ð—Ð° ВаÑ. Зомби ИÑчезнет Через 30 Секунд Или ЕÑли Он Будет Убит. +spell.telekinesis.desc=Перемещает Объект Или Другой Маленький Объект К Себе Или Щелкает Правой Кнопкой Мыши Ðа Блоке,Ðа Который Ð’Ñ‹ Смотрите. +spell.thunderbolt.desc=СтрелÑет Громом, Который ОтбраÑывает Цели. +spell.thunderstorm.desc="МУХÐÐ¥ÐÐ¥ÐÐ¥ÐÐ¥!" +spell.tornado.desc=РазвÑзывает Торнадо Ð’ Ðаправлении, Ðа Которое Ð’Ñ‹ Указываете, Который БроÑает Что-либо Ðа Своем Пути Ð’ Ðебо. +spell.transience.desc=Делает Переход Ð—Ð°ÐºÐ»Ð¸Ð½Ð°Ñ‚ÐµÐ»Ñ Ðа 20 Секунд. Заклинатель ÐевоÑприимчив Ко Ð’Ñему Урону Ð’ Переходный Период, Ðо Ðе Может Сломать Или ПомеÑтить Блоки Или ÐанеÑти Какой-либо Урон. +spell.transportation.desc=ТранÑпортирует Ð—Ð°ÐºÐ»Ð¸Ð½Ð°Ñ‚ÐµÐ»Ñ Ð’ Запомненный Каменный Круг. Чтобы ИÑпользовать Это Заклинание, Сделайте Круг Камней ТранÑпортировки, Затем Щелкните Правой Кнопкой Мыши С Помощью Жезла. +spell.vanishing_box.desc=Grants the caster access to their ender chest storage. +spell.wall_of_frost.desc=Зима У Ð’Ð°Ñ ÐŸÐ¾Ð´ Рукой. +spell.water_breathing.desc=ПозволÑет Заклинателю Дышать Под Водой Ð’ Течение 60 Секунд. +spell.whirlwind.desc=ЗаÑтавлÑет Цель Взлететь Вверх Далеко От Ð’Ð°Ñ Ðа СкороÑти. +spell.wither.desc=СтрелÑет Лучом Тьмы, Который УвÑдает Ð’ÑÑ‘, К Чему Он ПрикаÑаетÑÑ. +spell.wither_skull.desc=ЗапуÑкает Череп ИÑÑÑƒÑˆÐ¸Ñ‚ÐµÐ»Ñ Ð’ Ðаправлении, Ðа Которое Ð’Ñ‹ Указываете. +spell.invoke_weather.sun=Дождь Ðачинает ОÑтанавливатьÑÑ ... +spell.invoke_weather.rain=ÐебеÑа ОткрываютÑÑ ... +spell.transportation.missing=Ваш Запомненный Каменный Круг ОтÑутÑтвует Или Затруднен ... +spell.transportation.undefined=Ð’Ñ‹ Должны Сначала Запомнить МеÑтоположение Каменного Круга! +spell.transportation.wrongdimension=Ваш Запомненный Каменный Круг ÐаходитÑÑ Ð’ Другом Измерении ... - -spell.wizardry:agility.desc=Дает Заклинателю Более Ð’Ñ‹Ñокую СкороÑть ÐŸÐµÑ€ÐµÐ´Ð²Ð¸Ð¶ÐµÐ½Ð¸Ñ Ð˜ Большой Прыжок на 30 Секунд. -spell.wizardry:hailstorm.desc=Именно во Ð²Ñ€ÐµÐ¼Ñ Ð’ÐµÐ»Ð¸ÐºÐ¾Ð¹ зимы третьего века маги льда открыли Ñвою иÑтинную Ñилу. -spell.wizardry:earthquake.desc=ÐаÑтоÑщий маÑтер магии земли может двигать горы. -spell.wizardry:font_of_mana.desc="Мы были наполнены интенÑивной магичеÑкой Ñнергией, поÑвлÑющейÑÑ Ð¸Ð· центра...- ВыпиÑка из дневника забытого мага; оÑÑ‚Ð°Ð»ÑŒÐ½Ð°Ñ Ñ‡Ð°Ñть Ñтраницы Ñгорела. -spell.wizardry:lightning_web.desc="СфокуÑируйтеÑÑŒ. Ðаправьте шторм в вашем разуме через вашу палочку и дайте волю Ñвоей ÑроÑти." -spell.wizardry:summon_storm_elemental.desc="Грозовой Элементаль: древнее проÑвление Ñлементов, он врÑд ли может Ñодержать грубую Ñилу, ÑбивающуюÑÑ Ð²Ð½ÑƒÑ‚Ñ€Ð¸ него."- РуководÑтво маÑтера по тайным ÑущеÑтвам, том I_i -spell.wizardry:group_heal.desc=ВоÑÑтанавливает 3 Ñердца заклинателю и вÑем близлежащим Ñоюзникам и призванным ÑущеÑтвам. -spell.wizardry:conjure_armour.desc=Создаёт Ñпектральную броню вокруг Ð·Ð°ÐºÐ»Ð¸Ð½Ð°Ñ‚ÐµÐ»Ñ Ñ€Ð°Ð²Ð½ÑƒÑŽ по защите железной. Заклинание длитÑÑ 60 Ñекунд. Заклинатель должен иметь пуÑтой Ñлот Ð´Ð»Ñ Ð±Ñ€Ð¾Ð½Ð¸. -spell.wizardry:arcane_jammer.desc=Запрещает иÑпользовать магию цели в течение 15 Ñекунд. -spell.wizardry:decoy.desc=Создает иллюзорный клон заклинателÑ, который заÑтавлÑет мобов атаковать его. Приманка иÑчезнет через 30 Ñекунд. -spell.wizardry:curse_of_soulbinding.desc=СвÑзывает души Ð·Ð°ÐºÐ»Ð¸Ð½Ð°Ñ‚ÐµÐ»Ñ Ð¸ ÑущеÑтва в ÑледÑтвии чего ÑущеÑтво получает тот же урон, что и заклинатель. ДлитÑÑ Ð´Ð¾ тех пор, пока ÑущеÑтво или заклинатель не умрёт. -spell.wizardry:lightning_pulse.desc=ЗарÑжает землю вокруг Ð·Ð°ÐºÐ»Ð¸Ð½Ð°Ñ‚ÐµÐ»Ñ Ð¼Ð¾Ð»Ð½Ð¸ÐµÐ¹, Ð¿Ð¾Ð²Ñ€ÐµÐ¶Ð´Ð°Ñ Ð¸ Ð¾Ñ‚Ñ‚Ð°Ð»ÐºÐ¸Ð²Ð°Ñ Ð±Ð»Ð¸Ð·Ð»ÐµÐ¶Ð°Ñ‰Ð¸Ñ… ÑущеÑтв. -spell.wizardry:ice_spikes.desc=Призывает бритвенно-оÑтрые ледÑные шипы из-под земли в точке вашего курÑора, которые впиваютÑÑ Ð² любых ÑущеÑтв и Ñдерживают их. -spell.wizardry:freezing_weapon.desc=Временно наполнÑет первое оружие на панели Ð·Ð°ÐºÐ»Ð¸Ð½Ð°Ñ‚ÐµÐ»Ñ Ñилой льда, заÑтавлÑÑ ÐµÐ³Ð¾ замораживать Ñвоих жертв. ÐœÐ°Ð³Ð¸Ñ Ð¸Ñчезает через 45 Ñекунд. -spell.wizardry:ice_lance.desc=СтрелÑет большим копьём льда в направлении вашего курÑора,которе замедлÑет врага и наноÑит урон. -spell.wizardry:greater_fireball.desc=ЗапуÑкает большой огненный шар в направлении вашего курÑора, который взрываетÑÑ Ð¿Ñ€Ð¸ ударе. -spell.wizardry:flaming_weapon.desc=Временно наполнÑет первое оружие панели Ð·Ð°ÐºÐ»Ð¸Ð½Ð°Ñ‚ÐµÐ»Ñ Ñилой пламени, заÑтавлÑÑ ÐµÐ³Ð¾ поджигать Ñвоих жертв. ÐœÐ°Ð³Ð¸Ñ Ð¸Ñчезает через 45 Ñекунд. -spell.wizardry:oakflesh.desc=Улучшает Ñопротивление Ð·Ð°ÐºÐ»Ð¸Ð½Ð°Ñ‚ÐµÐ»Ñ Ð² течение 30 Ñекунд. -spell.wizardry:invigorating_presence.desc=Дарует заклинателю и вÑем ближайшим Ñоюзникам повышенную Ñилу в течение 45 Ñекунд. -spell.wizardry:clairvoyance.desc=Показывает путь к запоминающемуÑÑ Ð¼ÐµÑтоположению. Когда заклинание выбрано, приÑеÑть-Ð¿Ñ€Ð°Ð²Ð°Ñ ÐºÐ½Ð¾Ð¿ÐºÐ° мыши на блок, чтобы уÑтановить меÑтоположение. ИÑпользуйте заклинание, чтобы показать путь. Путь иÑчезнет через 90 Ñекунд. -spell.wizardry:pocket_workbench.desc=ПозволÑет заклинателю Ñоздавать предметы, так же как и в верÑтаке. -spell.wizardry:imbue_weapon.desc=Временно наполнÑет магией первое оружие на панели заклинателÑ, Ð´ÐµÐ»Ð°Ñ ÐµÐ³Ð¾ более Ñффективным. ÐœÐ°Ð³Ð¸Ñ Ð¸Ñчезает через 45 Ñекунд. -spell.wizardry:darkvision.desc=Дает заклинателю ночное зрение в течение 45 Ñекунд. -spell.wizardry:sixth_sense.desc=ПозволÑет заклинателю ощущать раÑположение близлежащих ÑущеÑтв, даже через Ñтены, в течение 20 Ñекунд. -spell.wizardry:banish.desc=Телепортирует цель против Ñвоей воли в Ñлучайное меÑто в пределах определенного диапазона. -spell.wizardry:arc.desc=Ð’Ñ‹Ñтреливает ИÑкрой Молнии Ð’ Цель -spell.wizardry:intimidate.desc=ИÑпуÑкает уÑтрашающее рычание, которое заÑтавлÑет близлежащих ÑущеÑтв убегать в Ñтрахе. Страх пораженных ÑущеÑтв воÑÑтановитÑÑ Ñ‡ÐµÑ€ÐµÐ· 30 Ñекунд. -spell.wizardry:arrow_rain.desc="Лучники, огонь!" -spell.wizardry:leap.desc=ЗаÑтавлÑет Ð·Ð°ÐºÐ»Ð¸Ð½Ð°Ñ‚ÐµÐ»Ñ Ð¿Ñ€Ñ‹Ð³Ð°Ñ‚ÑŒ вверх на неÑколько блоков и немного вперед. -spell.wizardry:black_hole.desc=Разрывай РеальноÑть Ðа ЧаÑти -spell.wizardry:blink.desc=Телепортирует Ð—Ð°ÐºÐ»Ð¸Ð½Ð°Ñ‚ÐµÐ»Ñ Ð’ Любой Блок, Ðа Который Он Указывает, Ð’ Определенном РадиуÑе. -spell.wizardry:blizzard.desc=Создает Зону Закрученного ЛедÑного Ветра, ÐšÐ¾Ñ‚Ð¾Ñ€Ð°Ñ Ð—Ð°Ð¼ÐµÐ´Ð»Ñет И ПоÑтоÑнно Повреждает Ð’Ñе, Что Попало Внутрь. Заклинатель ÐевоÑприимчив К Урону, Ðо Ð’Ñе Еще ЗамедлÑетÑÑ. -spell.wizardry:bubble.desc=СтрелÑет Потоком Пузырьков, Который Помещает СущеÑтво Ð’ Большой Пузырь, При Это Поднимает Его. Цель Будет Падать Или ПроÑто Получит Урон. -spell.wizardry:chain_lightning.desc=Ð’Ñ‹Ñтреливает ИÑкру Молнии У Цели, ÐšÐ¾Ñ‚Ð¾Ñ€Ð°Ñ Ð—Ð°Ñ‚ÐµÐ¼ ЦеплÑетÑÑ Ðš Дополнительным ЦелÑм До Двух Раз. -spell.wizardry:conjure_bow.desc=Создает Спектральный Лук С Ðеограниченными Стрелами, Который ДлитÑÑ 30 Секунд. -spell.wizardry:conjure_pickaxe.desc=Создает Спектральную Кирку Равной Силы Железной Кирке, ÐšÐ¾Ñ‚Ð¾Ñ€Ð°Ñ Ð”Ð»Ð¸Ñ‚ÑÑ 30 СÑекунд. -spell.wizardry:conjure_sword.desc=Создает Спектральный Меч Одинаковой Силы С Железным Мечом, Который ДлитÑÑ 30 Секунд. -spell.wizardry:cure_effects.desc=УдалÑет Ð’Ñе Эффекты Зелий, Которые Ð’ ÐаÑтоÑщее Ð’Ñ€ÐµÐ¼Ñ Ð’Ð¾Ð·Ð´ÐµÐ¹Ñтвуют Ðа ЗаклинателÑ, Хорошие Или Плохие. -spell.wizardry:darkness_orb.desc=Ð’Ñ‹Ñтреливает Медленно ПеремещающимÑÑ Ð‘Ð¾Ð»Ñ‚Ð¾Ð¼ Тёмной Энергии Ð’ Ðаправлении, Ðа Которое Ð’Ñ‹ Указываете, Которое Вызывает Гниение Ð’Ñего, Что Попадает. -spell.wizardry:dart.desc=Ð’Ñ‹Ñтреливает Дротик Ð’ Том Ðаправлении, Ð’ Котором Ð’Ñ‹ Указываете, Которое ÐаноÑит Урон И ОÑлаблÑет Его Цель. -spell.wizardry:decay.desc=Создает ПÑтно Гнили Ðа Земле, Которое Заражает Любое СущеÑтво, Которое Его КаÑаетÑÑ, Ð’Ñ‹Ð·Ñ‹Ð²Ð°Ñ Ð”Ð»Ð¸Ñ‚ÐµÐ»ÑŒÐ½Ñ‹Ð¹ Урон Со Временем И РаÑпроÑтранÑÑ Ð‘Ð¾Ð»ÑŒÑˆÐµ Гнили, Где Бы Он Ðи Ходил. -spell.wizardry:detonate.desc=Вызывает Взрыв, Ð’ Указанном МеÑте, ÐаноÑÑ Ð£Ñ€Ð¾Ð½ Ð’Ñем ÐаходÑщимÑÑ ÐŸÐ¾Ð±Ð»Ð¸Ð·Ð¾Ñти СущеÑтвам, Ð’ÐºÐ»ÑŽÑ‡Ð°Ñ Ð—Ð°ÐºÐ»Ð¸Ð½Ð°Ñ‚ÐµÐ»Ñ, ЕÑли Они Слишком Близко. -spell.wizardry:diamondflesh.desc="Твои Стрелы Мне Ðе ПодходÑÑ‚!" -spell.wizardry:smoke_bomb.desc=ЗапуÑкает дымовую бомбу в направлении вашего курÑора, ÐºÐ¾Ñ‚Ð¾Ñ€Ð°Ñ Ð²Ð·Ñ€Ñ‹Ð²Ð°ÐµÑ‚ÑÑ Ð²Ñ‹Ð¿ÑƒÐºÐ°Ñ Ð´Ñ‹Ð¼ и оÑлеплÑÑ Ð±Ð»Ð¸Ð·Ð»ÐµÐ¶Ð°Ñ‰Ð¸Ñ… ÑущеÑтв на короткое времÑ. -spell.wizardry:entrapment.desc=Заманивает Цель Ð’ Сферу Темноты, ÐšÐ¾Ñ‚Ð¾Ñ€Ð°Ñ ÐŸÐ¾Ð´Ð½Ð¸Ð¼Ð°ÐµÑ‚ Её Вверх И ПоÑтоÑнно Её Повреждает. -spell.wizardry:fireball.desc=ЗапуÑкает Огненный Шар Ð’ Том Ðаправлении, Ð’ Которое Ð’Ñ‹ Указали. -spell.wizardry:firebolt.desc=СтрелÑет Ðа Ðебольшом РаÑÑтоÑнии От ВаÑ. -spell.wizardry:firebomb.desc=БраÑает Зажигательную Бомбу Ð’ Ðаправлении, Которое Ð’Ñ‹ Указываете, ÐšÐ¾Ñ‚Ð¾Ñ€Ð°Ñ Ð’Ð·Ñ€Ñ‹Ð²Ð°ÐµÑ‚ÑÑ ÐŸÑ€Ð¸ Ударе, ÐŸÐ¾Ð´Ð¶Ð¸Ð³Ð°Ñ Ð¦ÐµÐ»Ð¸. -spell.wizardry:fire_resistance.desc=Даёт Сопротивление Огню Ðа 30% -spell.wizardry:fire_sigil.desc=Помещает МагичеÑкую Ловушку ÐžÐ³Ð½Ñ Ðа Землю, ÐšÐ¾Ñ‚Ð¾Ñ€Ð°Ñ ÐаноÑит Урон И Поджигает СущеÑтво, Которое ÐаÑтупило Ðа Ловушку -spell.wizardry:fireskin.desc=Покрывает Ð—Ð°ÐºÐ»Ð¸Ð½Ð°Ñ‚ÐµÐ»Ñ ÐžÐ³Ð½Ñ‘Ð¼ Ðа 30 Секунд. ЕÑли Заклинатель Будет Ðтакован, То Враг ЗагоритÑÑ. -spell.wizardry:firestorm.desc="Я Дракон." -spell.wizardry:flame_ray.desc=Создает Поток Пламени Ð’ Ðаправлении, Ðа Которое Ð’Ñ‹ Указали, Который Поджигает И ПоÑтоÑнно Повреждает Цели. -spell.wizardry:flaming_axe.desc=Создает Огненный Топор, Который Поджигает Врагов При Попадании. ДлитÑÑ 30 Секунд. -spell.wizardry:flight.desc=ПарÑщий, Как Орёл. -spell.wizardry:font_of_vitality.desc=Это ПотрÑÑающе. -spell.wizardry:force_arrow.desc=СтрелÑет Стрелой Силы Ð’ Ðаправлении, Ðа Которое Ð’Ñ‹ Указываете. -spell.wizardry:forcefield.desc=Создает Силовое Поле Вокруг ЗаклинателÑ, Который Отталкивает СущеÑтв И ОтклонÑет СнарÑды. -spell.wizardry:force_orb.desc=ЗапуÑкает Сферу Силы, ÐšÐ¾Ñ‚Ð¾Ñ€Ð°Ñ ÐаноÑит Урон И ОтбраÑывает Близких СущеÑтв При Ударе. -spell.wizardry:forests_curse.desc="Как Ты ПоÑмел Войти Ð’ Мой ЛеÑ?!" -spell.wizardry:freeze.desc=Замораживает Цель Ðа 10 Секунд. Также Заморозит Воду И СоздаÑÑ‚ Снег Ðа Земле. -spell.wizardry:frost_axe.desc=Создает ЛедÑной Топор, Который Замораживает Врагов При Попадании. ДлитÑÑ 30 Секунд. -spell.wizardry:frost_ray.desc=Создает Поток Мороза Ð’ Ðаправлении, Ðа Которое Ð’Ñ‹ Указываете, Который ЗамедлÑет И ПоÑтоÑнно Повреждает Цели. -spell.wizardry:frost_sigil.desc=Помещает МагичеÑкую ЛедÑную Ловушку Ðа Землю, ÐšÐ¾Ñ‚Ð¾Ñ€Ð°Ñ ÐаноÑит Урон И Замораживает СущеÑтво, Которое ÐаÑтупило Ðа Ðеё. -spell.wizardry:glide.desc=ПозволÑет Заклинателю Скользить Вниз, ÐаходÑÑÑŒ Ð’ Воздухе, Ð£Ð´ÐµÑ€Ð¶Ð¸Ð²Ð°Ñ ÐšÐ½Ð¾Ð¿ÐºÑƒ ИÑÐ¿Ð¾Ð»ÑŒÐ·Ð¾Ð²Ð°Ð½Ð¸Ñ ÐŸÑ€ÐµÐ´Ð¼ÐµÑ‚Ð°. -spell.wizardry:greater_heal.desc=ВоÑÑтанавливает Заклинателю 4 Сердца. -spell.wizardry:growth_aura.desc=Выращивает Ð’Ñе Зерновые Культуры РÑдом С Заклинателем. -spell.wizardry:heal.desc=ИÑцелÑет Ð—Ð°ÐºÐ»Ð¸Ð½Ð°Ñ‚ÐµÐ»Ñ Ðа 2 Сердца. -spell.wizardry:heal_ally.desc=ВоÑÑтанавливает Цели 2 С Половиной Сердца. -spell.wizardry:healing_aura.desc=Создает Зону ИÑцелÑющей Энергии, ÐšÐ¾Ñ‚Ð¾Ñ€Ð°Ñ Ð’Ð¾ÑÑтанавливает Здоровье Кого-либо Внутри Ðего, Кроме Ðежити, ÐšÐ¾Ñ‚Ð¾Ñ€Ð°Ñ ÐœÐµÐ´Ð»ÐµÐ½Ð½Ð¾ Получает Урон. -spell.wizardry:homing_spark.desc=Создает Плавающую ИÑкру, ÐšÐ¾Ñ‚Ð¾Ñ€Ð°Ñ Ð”Ð²Ð¸Ð¶ÐµÑ‚ÑÑ Ðš Врагам. -spell.wizardry:ice_age.desc="Ð’Ñ‹ Замерзнете ÐавÑегда!" -spell.wizardry:ice_charge.desc=ЗапуÑкает ЗарÑд Льда, Который ВзрываетÑÑ ÐŸÑ€Ð¸ Ударе, Ð—Ð°Ð¼Ð¾Ñ€Ð°Ð¶Ð¸Ð²Ð°Ñ Ð‘Ð»Ð¸Ð·Ð»ÐµÐ¶Ð°Ñ‰Ð¸Ñ… СущеÑтв И ВыпуÑÐºÐ°Ñ ÐžÑколки Во Ð’Ñех ÐаправлениÑÑ…. -spell.wizardry:ice_shard.desc=Ð’Ñ‹Ñтреливает ЛедÑной ОÑколок Ð’ Том Ðаправлении, Ð’ Котором Ð’Ñ‹ Указали, Который ÐаноÑит Урон И ЗамедлÑет Цель При Попадании. -spell.wizardry:ice_shroud.desc=Окутывает Ð—Ð°ÐºÐ»Ð¸Ð½Ð°Ñ‚ÐµÐ»Ñ Ð’Ð¾ Льду Ðа 30 Секунд, Ð—Ð°Ð¼Ð¾Ñ€Ð°Ð¶Ð¸Ð²Ð°Ñ Ð’Ñе, Что Попадает Или Попадает. -spell.wizardry:ice_statue.desc=Замораживает Цель Ðа 20 Секунд Или До Тех Пор, Пока Она Ðе ВырветÑÑ Ðаружу. Цель Ðе Может ДвигатьÑÑ Ð˜Ð»Ð¸ Делать Что-либо Ð’ Замороженном СоÑтоÑнии, Ðо Также ÐевоÑприимчива К Любому Урону. -spell.wizardry:ignite.desc=УÑтанавливает Огонь Ð’ Течение 10 Секунд. Также Работает Как Кремень И Сталь. -spell.wizardry:invisibility.desc=Делает Ð—Ð°ÐºÐ»Ð¸Ð½Ð°Ñ‚ÐµÐ»Ñ Ðевидимым Ð’ Течение 30 Секунд. -spell.wizardry:invoke_weather.desc=ИзменÑет Погоду Ð’ Мире. -spell.wizardry:ironflesh.desc=Увеличивает Сопротивление К Оглушению Ð—Ð°ÐºÐ»Ð¸Ð½Ð°Ñ‚ÐµÐ»Ñ Ðа 30 Секунд. -spell.wizardry:levitation.desc=Поднимает Ð—Ð°ÐºÐ»Ð¸Ð½Ð°Ñ‚ÐµÐ»Ñ Ð’Ð²ÐµÑ€Ñ… При Ðажатой Кнопке ИÑÐ¿Ð¾Ð»ÑŒÐ·Ð¾Ð²Ð°Ð½Ð¸Ñ ÐŸÑ€ÐµÐ´Ð¼ÐµÑ‚Ð°. Также Будет Ðейтрализован Урон От ПадениÑ, ЕÑли Он ИÑпользуетÑÑ Ð”Ð¾ Удара Об Землю. -spell.wizardry:life_drain.desc=Создает Поток ИÑÑушающей Энергии Ð’ Ðаправлении, Ðа Которое Ð’Ñ‹ Указываете, Который ИÑтощает Жизнь Цели И ИÑпользует Её Ð”Ð»Ñ ÐŸÐ¾Ñтепенного ВоÑÑÑ‚Ð°Ð½Ð¾Ð²Ð»ÐµÐ½Ð¸Ñ Ð—Ð´Ð¾Ñ€Ð¾Ð²ÑŒÑ. -spell.wizardry:light.desc=Создает МагичеÑкую Точку Света, ÐšÐ¾Ñ‚Ð¾Ñ€Ð°Ñ ÐžÑвещает Окружающую ОблаÑть. ДлитÑÑ 30 Секунд. -spell.wizardry:lightning_arrow.desc=СтрелÑет Стрелой Молнии Ð’ Ðаправлении, Ðа Которое Ð’Ñ‹ Указываете. -spell.wizardry:lightning_bolt.desc=ЗаÑтавлÑет Молнию Ударить Туда, Куда Ð’Ñ‹ Указываете. -spell.wizardry:lightning_disc.desc=ПоÑылает ДиÑк Молнии, ЛетÑщий Ð’ Ðаправлении, Ðа Которое Ð’Ñ‹ Указали И Ищет Цели. -spell.wizardry:lightning_hammer.desc="Я Поражу Ð¢ÐµÐ±Ñ Ð“Ð½ÐµÐ²Ð¾Ð¼ ÐебеÑ!" -spell.wizardry:lightning_ray.desc=Создает Поток Молнии Ð’ Ðаправлении, Ðа Которое Ð’Ñ‹ Указываете, Который ПоÑтоÑнно Повреждает Цели. -spell.wizardry:lightning_sigil.desc=Помещает МагичеÑкую МолниеноÑную Ловушку Ðа Землю, ÐšÐ¾Ñ‚Ð¾Ñ€Ð°Ñ ÐаноÑит Урон СущеÑтву, Которое ÐаÑтупило Ðа Ловушку, И Приковывает Молнии К Другим Близлежащим СущеÑтвам. -spell.wizardry:magic_missile.desc=Ð’Ñ‹Ñтреливает МагичеÑкий ЗарÑд Ð’ Том Ðаправлении, Ð’ Котором Ð’Ñ‹ Указали. -spell.wizardry:metamorphosis.desc=ИзменÑет Цель Ð’ Другую Форму. Работает Только Ðа Ðекоторых СущеÑтвах. -spell.wizardry:meteor.desc=Ðекоторые Волшебники ПроÑто ХотÑÑ‚, Чтобы Мир Горел ... -spell.wizardry:mind_control.desc=Контролирует Ум Цели, ЗаÑтавлÑÑ Ð•Ñ‘ Ðтаковать Ближайшую Живую Цель, Отличную От ЗаклинателÑ. Когда Это СущеÑтво Будет Убито, Цель ВернетÑÑ Ðš Ðормальной Жизни. -spell.wizardry:none.desc=Чтобы Получить Книгу Ð—Ð°ÐºÐ»Ð¸Ð½Ð°Ð½Ð¸Ñ /give command, иÑпользовать метаданные: /give [player] wizardry:spell_book 1 [spell id] (if you found this book in a chest, some other mod has messed things up). -spell.wizardry:petrify.desc=Повергает Цель Ð’ Камень До Следующего Заката Или Пока Ðе Будет Разбит. Цель Ðе Может ДвигатьÑÑ Ð˜Ð»Ð¸ Делать Что-либо, Пока Она Окаменела, Ðо Также ÐевоÑприимчива К Любому Урону. -spell.wizardry:phase_step.desc=Телепортирует Ð—Ð°ÐºÐ»Ð¸Ð½Ð°Ñ‚ÐµÐ»Ñ Ð§ÐµÑ€ÐµÐ· Стену Толщиной 1 Блок Перед Ðим. Улучшение ДальноÑти Увеличит Толщину, Через Которую Ð’Ñ‹ Сможете ТелепортироватьÑÑ. -spell.wizardry:plague_of_darkness.desc=Тьма Поглотит Их Ð’Ñех... -spell.wizardry:pocket_furnace.desc=ПереплавлÑет до 5 предметов в инвентаре заклинателÑ. Предметы на панели переплавлÑÑŽÑ‚ÑÑ Ð¿ÐµÑ€Ð²Ñ‹Ðµ. -spell.wizardry:poison.desc=Ð’Ñ‹Ñтреливает Яд Ð’ Ðаправлении, Ðа Которое Ð’Ñ‹ Указываете. -spell.wizardry:poison_bomb.desc=БроÑает Ядовитую Бомбу Ð’ Ðаправлении, Которое Ð’Ñ‹ Указываете, ÐšÐ¾Ñ‚Ð¾Ñ€Ð°Ñ Ð’Ð·Ñ€Ñ‹Ð²Ð°ÐµÑ‚ÑÑ ÐŸÑ€Ð¸ Ударе, ОтравлÑÑ Ð‘Ð»Ð¸Ð·ÐºÐ¸Ñ… СущеÑтв. -spell.wizardry:replenish_hunger.desc=ПополнÑет Ð—Ð°Ð¿Ð°Ñ ÐŸÐ¸Ñ‰Ð¸ Ð—Ð°ÐºÐ»Ð¸Ð½Ð°Ñ‚ÐµÐ»Ñ Ðа 6 Пунктов Голода. -spell.wizardry:ring_of_fire.desc=Создает Огненное Кольцо Вокруг ЗаклинателÑ, ÐаноÑÑ Ð£Ñ€Ð¾Ð½ Ð’Ñем Ближайшим Противникам И ÐŸÐ¾Ð´Ð¶Ð¸Ð³Ð°Ñ Ð˜Ñ…. -spell.wizardry:shadow_ward.desc=Создает Стену Тьмы Перед Заклинателем, ÐšÐ¾Ñ‚Ð¾Ñ€Ð°Ñ ÐаноÑит Ðападающему Половину Ð’Ñего ВходÑщего Урона. -spell.wizardry:shield.desc=Создает Защитный Барьер Силы, Который Блокирует СнарÑды И Магию. Также Дает Заклинателю Слабый Эффект СопротивлениÑ. -spell.wizardry:shockwave.desc=Бум. -spell.wizardry:silverfish_swarm.desc="ÐÐÐÐÐ! ИХ ОЧЕÐЬ МÐОГО!" -spell.wizardry:slime.desc=Охватывает Цель Слизью, ÐšÐ¾Ñ‚Ð¾Ñ€Ð°Ñ Ð—Ð°Ð¼ÐµÐ´Ð»Ñет И ПоÑтоÑнно Её Повреждает. Слизь РазрываетÑÑ Ð§ÐµÑ€ÐµÐ· 10 Секунд. -spell.wizardry:snare.desc=УÑтанавливает Ловушку Ðа Земле, ÐšÐ¾Ñ‚Ð¾Ñ€Ð°Ñ ÐаноÑит Урон И Кратко ЗамедлÑет СущеÑтво, Которое ÐаÑтупило Ðа Ловушку. -spell.wizardry:snowball.desc=Launches a snowball in the direction you are pointing. -spell.wizardry:spark_bomb.desc=ЗапуÑкает Бомбу ИÑкр Ð’ Том Ðаправлении, Ð’ Котором Ð’Ñ‹ Указываете. ИÑкры ПоÑвлÑÑŽÑ‚ÑÑ Ð£ Ближайших Противников При Ударе. -spell.wizardry:spectral_pathway.desc=Создает Перед Вами Ðеразрушимый МагичеÑкий МоÑÑ‚, Который ПроÑтираетÑÑ Ðа 15 Блоков. Через 60 Секунд МоÑÑ‚ ИÑчезает. -spell.wizardry:spider_swarm.desc=Призывает Рой Ядовитых Пауков, Чтобы СражатьÑÑ Ð—Ð° ВаÑ. Пауки ИÑчезнут Через 30 Секунд Или ЕÑли Они Будут Убиты. -spell.wizardry:static_aura.desc=Окружает Ð—Ð°ÐºÐ»Ð¸Ð½Ð°Ñ‚ÐµÐ»Ñ ÐœÐ¾Ð»Ð½Ð¸ÐµÐ¹ Ð’ Течение 30 Секунд, СтрелÑет ИÑкрами Молнии Во Ð’Ñе, Что Ударит ВаÑ. -spell.wizardry:summon_blaze.desc=Призывает Блейза, Чтобы СражатьÑÑ Ð—Ð° ВаÑ. Блейз ИÑчезнет Через 30 Секунд Или ЕÑли Он Будет Убит. -spell.wizardry:summon_ice_giant.desc="Разбей Их!" -spell.wizardry:summon_ice_wraith.desc=Призывает ЛедÑного Призрака, Который СражаетÑÑ Ð—Ð° ВаÑ. ЛедÑной Призрак ИÑчезнет Через 30 Секунд Или ЕÑли Он Будет Убит. -spell.wizardry:summon_iron_golem.desc=ÐвтоматичеÑкий Ðвтономный Ðвтомат. -spell.wizardry:summon_lightning_wraith.desc=Призывает МолниеноÑного Призрака СражатьÑÑ Ð—Ð° ВаÑ. МолниеноÑный Призрак ИÑчезнет Через 30 Секунд Или ЕÑли Он Будет Убит. -spell.wizardry:summon_phoenix.desc=Из Пепла... -spell.wizardry:summon_shadow_wraith.desc=Призывает Тёмного Призрака, Который Будет СражатьÑÑ Ð—Ð° ВаÑ. -spell.wizardry:summon_skeleton.desc=Призывает Скелета, Который СражаетÑÑ Ð—Ð° ваÑ. Скелет ИÑчезнет Через 30 Секунд Или ЕÑли Он Будет Убит. -spell.wizardry:summon_skeleton_legion.desc="ВоÑÑтань, ÐÑ€Ð¼Ð¸Ñ Ðежети!" -spell.wizardry:summon_snow_golem.desc=Создает Снежного Голема, Который СражаетÑÑ Ð—Ð° ВаÑ. ДлитÑÑ Ð”Ð¾ Тех Пор, Пока Ðе Умрет Снежный Голем. -spell.wizardry:summon_spirit_horse.desc=Призывает Ð”Ð»Ñ Ð’Ð°Ñ Ð’ÐµÑ€Ñ…Ð¾Ð²ÑƒÑŽ Лошадь. ÐŸÑ€Ð¸Ð·Ñ€Ð°Ñ‡Ð½Ð°Ñ Ð›Ð¾ÑˆÐ°Ð´ÑŒ ИÑчезнет Через Ðекоторое Ð’Ñ€ÐµÐ¼Ñ ÐŸÐ¾Ñле Того, Как Ð’Ñ‹ С Ðеё Слезите, Или Ð’Ñ‹ Можете Убрать Её С Помощью Жезла - Щелкните Правой Кнопкой Мыши Ðа Ðей. -spell.wizardry:summon_spirit_wolf.desc=Призывает Спутника Духа Волка, Который СражаетÑÑ Ð—Ð° ВаÑ. Призрачный Волк ИÑчезнет, Только ЕÑли Его Убьют, Или Ð’Ñ‹ Можете Его Убрать, Щелкнув Правой Кнопкой Мыши Ðа Ðём С Помощью Жезла. -spell.wizardry:summon_wither_skeleton.desc=Призывает Скелета-ИÑÑушителÑ, Который СражаетÑÑ Ð—Ð° ВаÑ. Скелет-ИÑÑушитель ИÑчезнет Через 30 Секунд Или ЕÑли Он Будет Убит. -spell.wizardry:summon_zombie.desc=Призывает Зомби, Который СражаетÑÑ Ð—Ð° ВаÑ. Зомби ИÑчезнет Через 30 Секунд Или ЕÑли Он Будет Убит. -spell.wizardry:telekinesis.desc=Перемещает Объект Или Другой Маленький Объект К Себе Или Щелкает Правой Кнопкой Мыши Ðа Блоке,Ðа Который Ð’Ñ‹ Смотрите. -spell.wizardry:thunderbolt.desc=СтрелÑет Громом, Который ОтбраÑывает Цели. -spell.wizardry:thunderstorm.desc="МУХÐÐ¥ÐÐ¥ÐÐ¥ÐÐ¥!" -spell.wizardry:tornado.desc=РазвÑзывает Торнадо Ð’ Ðаправлении, Ðа Которое Ð’Ñ‹ Указываете, Который БроÑает Что-либо Ðа Своем Пути Ð’ Ðебо. -spell.wizardry:transience.desc=Делает Переход Ð—Ð°ÐºÐ»Ð¸Ð½Ð°Ñ‚ÐµÐ»Ñ Ðа 20 Секунд. Заклинатель ÐевоÑприимчив Ко Ð’Ñему Урону Ð’ Переходный Период, Ðо Ðе Может Сломать Или ПомеÑтить Блоки Или ÐанеÑти Какой-либо Урон. -spell.wizardry:transportation.desc=ТранÑпортирует Ð—Ð°ÐºÐ»Ð¸Ð½Ð°Ñ‚ÐµÐ»Ñ Ð’ Запомненный Каменный Круг. Чтобы ИÑпользовать Это Заклинание, Сделайте Круг Камней ТранÑпортировки, Затем Щелкните Правой Кнопкой Мыши С Помощью Жезла. -spell.wizardry:vanishing_box.desc=Grants the caster access to their ender chest storage. -spell.wizardry:wall_of_frost.desc=Зима У Ð’Ð°Ñ ÐŸÐ¾Ð´ Рукой. -spell.wizardry:water_breathing.desc=ПозволÑет Заклинателю Дышать Под Водой Ð’ Течение 60 Секунд. -spell.wizardry:whirlwind.desc=ЗаÑтавлÑет Цель Взлететь Вверх Далеко От Ð’Ð°Ñ Ðа СкороÑти. -spell.wizardry:wither.desc=СтрелÑет Лучом Тьмы, Который УвÑдает Ð’ÑÑ‘, К Чему Он ПрикаÑаетÑÑ. -spell.wizardry:wither_skull.desc=ЗапуÑкает Череп ИÑÑÑƒÑˆÐ¸Ñ‚ÐµÐ»Ñ Ð’ Ðаправлении, Ðа Которое Ð’Ñ‹ Указываете. -spell.wizardry:mind_trick.desc=Путает и дезориентирует цель в течение 15 Ñекунд, что делает его не в ÑоÑтоÑнии атаковать. Эффект будет раÑÑеÑн, еÑли цель получит урон. - -spell.wizardry:invoke_weather.sun=Дождь Ðачинает ОÑтанавливатьÑÑ ... -spell.wizardry:invoke_weather.rain=ÐебеÑа ОткрываютÑÑ ... -spell.wizardry:transportation.missing=Ваш Запомненный Каменный Круг ОтÑутÑтвует Или Затруднен ... -spell.wizardry:transportation.undefined=Ð’Ñ‹ Должны Сначала Запомнить МеÑтоположение Каменного Круга! -spell.wizardry:transportation.wrongdimension=Ваш Запомненный Каменный Круг ÐаходитÑÑ Ð’ Другом Измерении ... -spell.wizardry:clairvoyance.searching=ПоиÑк... -spell.wizardry:clairvoyance.confirm=Путь, показанный при иÑпользовании Ð·Ð°ÐºÐ»Ð¸Ð½Ð°Ð½Ð¸Ñ %1$s теперь приведёт к Ñтой точке -spell.wizardry:clairvoyance.outofrange=Ваше запоминающееÑÑ Ð¼ÐµÑтоположение Ñлишком далеко или недоÑтупно... -spell.wizardry:clairvoyance.undefined=Ð’Ñ‹ должны запомнить первое меÑтоположение! -spell.wizardry:clairvoyance.wrongdimension=Ваше запоминающееÑÑ Ð¼ÐµÑтоположение находитÑÑ Ð² другом измерении... - -potion.wizardry:frost=Отморожение -potion.wizardry:fireskin=ÐžÐ³Ð½ÐµÐ½Ð½Ð°Ñ ÐšÐ¾Ð¶Ð° -potion.wizardry:ice_shroud=ЛедÑÐ½Ð°Ñ ÐšÐ¾Ð¶Ð° -potion.wizardry:static_aura=СтатичеÑÐºÐ°Ñ Ðура -potion.wizardry:transience=БыÑтрота -potion.wizardry:decay=Гниение -potion.wizardry:mind_trick=Обман Разума -potion.wizardry:sixth_sense=ШеÑтое ЧувÑтво -potion.wizardry:font_of_mana=Купель Маны +potion.frost=Отморожение +potion.fireskin=ÐžÐ³Ð½ÐµÐ½Ð½Ð°Ñ ÐšÐ¾Ð¶Ð° +potion.ice_shroud=ЛедÑÐ½Ð°Ñ ÐšÐ¾Ð¶Ð° +potion.static_aura=СтатичеÑÐºÐ°Ñ Ðура +potion.transience=БыÑтрота +potion.decay=Гниение key.categories.wizardry=Wizardry -death.attack.wizardry_magic=%1$s был убит %2$s иÑÐ¿Ð¾Ð»ÑŒÐ·ÑƒÑ Ð¼Ð°Ð³Ð¸ÑŽ -death.attack.indirect_wizardry_magic=%1$s был убит %2$s иÑÐ¿Ð¾Ð»ÑŒÐ·ÑƒÑ Ð¼Ð°Ð³Ð¸ÑŽ +death.attack.wizardryMagic=%1$s был убит %2$s иÑÐ¿Ð¾Ð»ÑŒÐ·ÑƒÑ Ð¼Ð°Ð³Ð¸ÑŽ +death.attack.indirectWizardryMagic=%1$s был убит %2$s иÑÐ¿Ð¾Ð»ÑŒÐ·ÑƒÑ Ð¼Ð°Ð³Ð¸ÑŽ -config.wizardry.title.general=Mod Options -config.wizardry.title.spells=Spell Configuration -config.wizardry.subtitle.spells=Set a spell to false to disable it. +config.title.general=Mod Options +config.title.spells=Spell Configuration +config.subtitle.spells=Set a spell to false to disable it. -config.wizardry.tower_rarity=Tower Rarity -config.wizardry.ore_dimensions=Ore Dimensions -config.wizardry.flower_dimensions=Flower Dimensions -config.wizardry.tower_dimensions=Tower Dimensions -config.wizardry.spell_book_drop_chance=Spell Book Drop Chance -config.wizardry.generate_loot=Generate Loot -config.wizardry.firebomb_is_craftable=Firebomb Is Craftable -config.wizardry.poison_bomb_is_craftable=Poison Bomb Is Craftable -config.wizardry.use_alternate_scroll_recipe=Use Alternate Scroll Recipe -config.wizardry.teleport_through_unbreakable_blocks=Teleport Through Unbreakable Blocks -config.wizardry.show_summoned_creature_names=Show Summoned Creature Names -config.wizardry.spell_hud_position=Spell HUD Position +config.tower_rarity=Tower Rarity +config.ore_dimensions=Ore Dimensions +config.flower_dimensions=Flower Dimensions +config.tower_dimensions=Tower Dimensions +config.spell_book_drop_chance=Spell Book Drop Chance +config.generate_loot=Generate Loot +config.firebomb_is_craftable=Firebomb Is Craftable +config.poison_bomb_is_craftable=Poison Bomb Is Craftable +config.use_alternate_scroll_recipe=Use Alternate Scroll Recipe +config.teleport_through_unbreakable_blocks=Teleport Through Unbreakable Blocks +config.show_summoned_creature_names=Show Summoned Creature Names +config.spell_hud_position=Spell HUD Position +config.frost_potion_id=Frost Potion ID +config.transience_potion_id=Transience Potion ID +config.fireskin_potion_id=Fireskin Potion ID +config.ice_shroud_potion_id=Ice Shroud Potion ID +config.static_aura_potion_id=Static Aura Potion ID +config.decay_potion_id=Decay Potion ID -config.wizardry.category.spells=Configure Spells -config.wizardry.category.spells.tooltip=Select which spells are enabled. \ No newline at end of file +config.category.spells=Configure Spells +config.category.spells.tooltip=Select which spells are enabled. \ No newline at end of file diff --git a/src/main/resources/assets/wizardry/loot_tables/chests/dungeon_additions.json b/src/main/resources/assets/wizardry/loot_tables/chests/dungeon_additions.json deleted file mode 100644 index 59c4bf30..00000000 --- a/src/main/resources/assets/wizardry/loot_tables/chests/dungeon_additions.json +++ /dev/null @@ -1,92 +0,0 @@ -{ - "pools": [ - { - "name": "wizardry", - "rolls": { - "min": 3, - "max": 7 - }, - "entries": [ - { - "type": "loot_table", - "name": "wizardry:subsets/novice_wands", - "weight": 3 - }, - { - "type": "loot_table", - "name": "wizardry:subsets/wizard_armour", - "weight": 6 - }, - { - "type": "loot_table", - "name": "wizardry:subsets/arcane_tomes", - "weight": 6 - }, - { - "type": "loot_table", - "name": "wizardry:subsets/wand_upgrades", - "weight": 2 - }, - { - "type": "item", - "name": "wizardry:magic_crystal", - "weight": 5, - "functions": [ - { - "function": "set_count", - "count": { - "min": 1, - "max": 4 - } - } - ] - }, - { - "type": "item", - "name": "wizardry:spell_book", - "weight": 20, - "functions": [ - { - "function": "wizardry:random_spell" - } - ] - }, - { - "type": "item", - "name": "wizardry:scroll", - "weight": 10, - "functions": [ - { - "function": "wizardry:random_spell" - } - ] - }, - { - "type": "item", - "name": "wizardry:armour_upgrade", - "weight": 1 - }, - { - "type": "item", - "name": "wizardry:identification_scroll", - "weight": 3 - }, - { - "type": "item", - "name": "wizardry:firebomb", - "weight": 4 - }, - { - "type": "item", - "name": "wizardry:poison_bomb", - "weight": 4 - }, - { - "type": "item", - "name": "wizardry:smoke_bomb", - "weight": 4 - } - ] - } - ] -} \ No newline at end of file diff --git a/src/main/resources/assets/wizardry/loot_tables/chests/wizard_tower.json b/src/main/resources/assets/wizardry/loot_tables/chests/wizard_tower.json deleted file mode 100644 index f7bba6ea..00000000 --- a/src/main/resources/assets/wizardry/loot_tables/chests/wizard_tower.json +++ /dev/null @@ -1,77 +0,0 @@ -{ - "pools": [ - { - "name": "wizardry", - "rolls": { - "min": 3, - "max": 7 - }, - "entries": [ - { - "type": "loot_table", - "name": "wizardry:subsets/novice_wands", - "weight": 3 - }, - { - "type": "loot_table", - "name": "wizardry:subsets/wizard_armour", - "weight": 6 - }, - { - "type": "loot_table", - "name": "wizardry:subsets/arcane_tomes", - "weight": 6 - }, - { - "type": "loot_table", - "name": "wizardry:subsets/wand_upgrades", - "weight": 2 - }, - { - "type": "item", - "name": "wizardry:magic_crystal", - "weight": 5, - "functions": [ - { - "function": "set_count", - "count": { - "min": 1, - "max": 4 - } - } - ] - }, - { - "type": "item", - "name": "wizardry:spell_book", - "weight": 20, - "functions": [ - { - "function": "wizardry:random_spell" - } - ] - }, - { - "type": "item", - "name": "wizardry:scroll", - "weight": 10, - "functions": [ - { - "function": "wizardry:random_spell" - } - ] - }, - { - "type": "item", - "name": "wizardry:armour_upgrade", - "weight": 1 - }, - { - "type": "item", - "name": "wizardry:identification_scroll", - "weight": 3 - } - ] - } - ] -} \ No newline at end of file diff --git a/src/main/resources/assets/wizardry/loot_tables/entities/evil_wizard.json b/src/main/resources/assets/wizardry/loot_tables/entities/evil_wizard.json deleted file mode 100644 index 8723be6f..00000000 --- a/src/main/resources/assets/wizardry/loot_tables/entities/evil_wizard.json +++ /dev/null @@ -1,55 +0,0 @@ -{ - "pools": [ - { - "name": "common", - "rolls": 1, - "entries": [ - { - "type": "item", - "name": "wizardry:magic_crystal", - "weight": 1, - "functions": [ - { - "function": "set_count", - "count": { - "min": 3, - "max": 5 - }, - "function": "looting_enchant", - "count": { - "min": 0, - "max": 1 - } - } - ] - } - ] - }, - { - "name": "rare", - "conditions": [ - { - "condition": "killed_by_player" - }, - { - "condition": "random_chance_with_looting", - "chance": 0.05, - "looting_multiplier": 0.01 - } - ], - "rolls": 1, - "entries": [ - { - "type": "item", - "name": "wizardry:spell_book", - "weight": 1, - "functions": [ - { - "function": "wizardry:wizard_spell" - } - ] - } - ] - } - ] -} \ No newline at end of file diff --git a/src/main/resources/assets/wizardry/loot_tables/entities/mob_additions.json b/src/main/resources/assets/wizardry/loot_tables/entities/mob_additions.json deleted file mode 100644 index dc872ac7..00000000 --- a/src/main/resources/assets/wizardry/loot_tables/entities/mob_additions.json +++ /dev/null @@ -1,29 +0,0 @@ -{ - "pools": [ - { - "conditions": [ - { - "condition": "killed_by_player" - }, - { - "condition": "random_chance_with_looting", - "chance": 0.02, - "looting_multiplier": 0.01 - } - ], - "rolls": 1, - "entries": [ - { - "type": "item", - "name": "wizardry:spell_book", - "weight": 1, - "functions": [ - { - "function": "wizardry:random_spell" - } - ] - } - ] - } - ] -} \ No newline at end of file diff --git a/src/main/resources/assets/wizardry/loot_tables/subsets/arcane_tomes.json b/src/main/resources/assets/wizardry/loot_tables/subsets/arcane_tomes.json deleted file mode 100644 index 14ca2ad4..00000000 --- a/src/main/resources/assets/wizardry/loot_tables/subsets/arcane_tomes.json +++ /dev/null @@ -1,46 +0,0 @@ -{ - "pools": [ - { - "name": "tomes", - "rolls": 1, - "entries": [ - { - "type": "item", - "entryName": "apprentice_tome", - "name": "wizardry:arcane_tome", - "weight": 3, - "functions": [ - { - "function": "set_data", - "data": 1 - } - ] - }, - { - "type": "item", - "entryName": "advanced_tome", - "name": "wizardry:arcane_tome", - "weight": 2, - "functions": [ - { - "function": "set_data", - "data": 2 - } - ] - }, - { - "type": "item", - "entryName": "master_tome", - "name": "wizardry:arcane_tome", - "weight": 1, - "functions": [ - { - "function": "set_data", - "data": 3 - } - ] - } - ] - } - ] -} \ No newline at end of file diff --git a/src/main/resources/assets/wizardry/loot_tables/subsets/novice_wands.json b/src/main/resources/assets/wizardry/loot_tables/subsets/novice_wands.json deleted file mode 100644 index 01f5c608..00000000 --- a/src/main/resources/assets/wizardry/loot_tables/subsets/novice_wands.json +++ /dev/null @@ -1,50 +0,0 @@ -{ - "pools": [ - { - "name": "wands", - "rolls": 1, - "entries": [ - { - "type": "item", - "name": "wizardry:magic_wand", - "weight": 1 - }, - { - "type": "item", - "name": "wizardry:basic_fire_wand", - "weight": 1 - }, - { - "type": "item", - "name": "wizardry:basic_ice_wand", - "weight": 1 - }, - { - "type": "item", - "name": "wizardry:basic_lightning_wand", - "weight": 1 - }, - { - "type": "item", - "name": "wizardry:basic_necromancy_wand", - "weight": 1 - }, - { - "type": "item", - "name": "wizardry:basic_earth_wand", - "weight": 1 - }, - { - "type": "item", - "name": "wizardry:basic_sorcery_wand", - "weight": 1 - }, - { - "type": "item", - "name": "wizardry:basic_healing_wand", - "weight": 1 - } - ] - } - ] -} \ No newline at end of file diff --git a/src/main/resources/assets/wizardry/loot_tables/subsets/wand_upgrades.json b/src/main/resources/assets/wizardry/loot_tables/subsets/wand_upgrades.json deleted file mode 100644 index 4ce76637..00000000 --- a/src/main/resources/assets/wizardry/loot_tables/subsets/wand_upgrades.json +++ /dev/null @@ -1,50 +0,0 @@ -{ - "pools": [ - { - "name": "upgrades", - "rolls": 1, - "entries": [ - { - "type": "item", - "name": "wizardry:condenser_upgrade", - "weight": 1 - }, - { - "type": "item", - "name": "wizardry:siphon_upgrade", - "weight": 1 - }, - { - "type": "item", - "name": "wizardry:storage_upgrade", - "weight": 1 - }, - { - "type": "item", - "name": "wizardry:range_upgrade", - "weight": 1 - }, - { - "type": "item", - "name": "wizardry:duration_upgrade", - "weight": 1 - }, - { - "type": "item", - "name": "wizardry:cooldown_upgrade", - "weight": 1 - }, - { - "type": "item", - "name": "wizardry:blast_upgrade", - "weight": 1 - }, - { - "type": "item", - "name": "wizardry:attunement_upgrade", - "weight": 1 - } - ] - } - ] -} \ No newline at end of file diff --git a/src/main/resources/assets/wizardry/loot_tables/subsets/wizard_armour.json b/src/main/resources/assets/wizardry/loot_tables/subsets/wizard_armour.json deleted file mode 100644 index 95f8ac07..00000000 --- a/src/main/resources/assets/wizardry/loot_tables/subsets/wizard_armour.json +++ /dev/null @@ -1,170 +0,0 @@ -{ - "pools": [ - { - "name": "armour", - "rolls": 1, - "entries": [ - { - "type": "item", - "name": "wizardry:wizard_hat", - "weight": 1 - }, - { - "type": "item", - "name": "wizardry:wizard_hat_fire", - "weight": 1 - }, - { - "type": "item", - "name": "wizardry:wizard_hat_ice", - "weight": 1 - }, - { - "type": "item", - "name": "wizardry:wizard_hat_lightning", - "weight": 1 - }, - { - "type": "item", - "name": "wizardry:wizard_hat_necromancy", - "weight": 1 - }, - { - "type": "item", - "name": "wizardry:wizard_hat_earth", - "weight": 1 - }, - { - "type": "item", - "name": "wizardry:wizard_hat_sorcery", - "weight": 1 - }, - { - "type": "item", - "name": "wizardry:wizard_hat_healing", - "weight": 1 - }, - { - "type": "item", - "name": "wizardry:wizard_robe", - "weight": 1 - }, - { - "type": "item", - "name": "wizardry:wizard_robe_fire", - "weight": 1 - }, - { - "type": "item", - "name": "wizardry:wizard_robe_ice", - "weight": 1 - }, - { - "type": "item", - "name": "wizardry:wizard_robe_lightning", - "weight": 1 - }, - { - "type": "item", - "name": "wizardry:wizard_robe_necromancy", - "weight": 1 - }, - { - "type": "item", - "name": "wizardry:wizard_robe_earth", - "weight": 1 - }, - { - "type": "item", - "name": "wizardry:wizard_robe_sorcery", - "weight": 1 - }, - { - "type": "item", - "name": "wizardry:wizard_robe_healing", - "weight": 1 - }, - { - "type": "item", - "name": "wizardry:wizard_leggings", - "weight": 1 - }, - { - "type": "item", - "name": "wizardry:wizard_leggings_fire", - "weight": 1 - }, - { - "type": "item", - "name": "wizardry:wizard_leggings_ice", - "weight": 1 - }, - { - "type": "item", - "name": "wizardry:wizard_leggings_lightning", - "weight": 1 - }, - { - "type": "item", - "name": "wizardry:wizard_leggings_necromancy", - "weight": 1 - }, - { - "type": "item", - "name": "wizardry:wizard_leggings_earth", - "weight": 1 - }, - { - "type": "item", - "name": "wizardry:wizard_leggings_sorcery", - "weight": 1 - }, - { - "type": "item", - "name": "wizardry:wizard_leggings_healing", - "weight": 1 - }, - { - "type": "item", - "name": "wizardry:wizard_boots", - "weight": 1 - }, - { - "type": "item", - "name": "wizardry:wizard_boots_fire", - "weight": 1 - }, - { - "type": "item", - "name": "wizardry:wizard_boots_ice", - "weight": 1 - }, - { - "type": "item", - "name": "wizardry:wizard_boots_lightning", - "weight": 1 - }, - { - "type": "item", - "name": "wizardry:wizard_boots_necromancy", - "weight": 1 - }, - { - "type": "item", - "name": "wizardry:wizard_boots_earth", - "weight": 1 - }, - { - "type": "item", - "name": "wizardry:wizard_boots_sorcery", - "weight": 1 - }, - { - "type": "item", - "name": "wizardry:wizard_boots_healing", - "weight": 1 - } - ] - } - ] -} \ No newline at end of file diff --git a/src/main/resources/assets/wizardry/models/block/arcane_workbench.json b/src/main/resources/assets/wizardry/models/block/arcane_workbench.json deleted file mode 100644 index 8a545d1c..00000000 --- a/src/main/resources/assets/wizardry/models/block/arcane_workbench.json +++ /dev/null @@ -1,21 +0,0 @@ -{ "parent": "block/block", - "textures": { - "particle": "wizardry:blocks/arcane_workbench_bottom", - "bottom": "wizardry:blocks/arcane_workbench_bottom", - "top": "wizardry:blocks/arcane_workbench_top", - "side": "wizardry:blocks/arcane_workbench_side" - }, - "elements": [ - { "from": [ 0, 0, 0 ], - "to": [ 16, 12, 16 ], - "faces": { - "down": { "uv": [ 0, 0, 16, 16 ], "texture": "#bottom", "cullface": "down" }, - "up": { "uv": [ 0, 0, 16, 16 ], "texture": "#top" }, - "north": { "uv": [ 0, 4, 16, 16 ], "texture": "#side", "cullface": "north" }, - "south": { "uv": [ 0, 4, 16, 16 ], "texture": "#side", "cullface": "south" }, - "west": { "uv": [ 0, 4, 16, 16 ], "texture": "#side", "cullface": "west" }, - "east": { "uv": [ 0, 4, 16, 16 ], "texture": "#side", "cullface": "east" } - } - } - ] -} diff --git a/src/main/resources/assets/wizardry/models/block/crystal_block.json b/src/main/resources/assets/wizardry/models/block/crystal_block.json deleted file mode 100644 index 898d595a..00000000 --- a/src/main/resources/assets/wizardry/models/block/crystal_block.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "parent": "block/cube_all", - "textures": { - "all": "wizardry:blocks/crystal_block" - } -} diff --git a/src/main/resources/assets/wizardry/models/block/crystal_flower.json b/src/main/resources/assets/wizardry/models/block/crystal_flower.json deleted file mode 100644 index 90f2f653..00000000 --- a/src/main/resources/assets/wizardry/models/block/crystal_flower.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "parent": "block/cross", - "textures": { - "cross": "wizardry:blocks/crystal_flower" - } -} diff --git a/src/main/resources/assets/wizardry/models/block/crystal_ore.json b/src/main/resources/assets/wizardry/models/block/crystal_ore.json deleted file mode 100644 index 80b71f13..00000000 --- a/src/main/resources/assets/wizardry/models/block/crystal_ore.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "parent": "block/cube_all", - "textures": { - "all": "wizardry:blocks/crystal_ore" - } -} diff --git a/src/main/resources/assets/wizardry/models/block/ice_statue.json b/src/main/resources/assets/wizardry/models/block/ice_statue.json deleted file mode 100644 index b7f5c4ad..00000000 --- a/src/main/resources/assets/wizardry/models/block/ice_statue.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "parent": "block/ice" -} diff --git a/src/main/resources/assets/wizardry/models/block/magic_light.json b/src/main/resources/assets/wizardry/models/block/magic_light.json deleted file mode 100644 index 1f4f2185..00000000 --- a/src/main/resources/assets/wizardry/models/block/magic_light.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "parent": "block/block", - "elements": [ - { "from": [ 0, 0, 0 ], - "to": [ 0, 0, 0 ], - "faces": { - "down": { "texture": "#down", "cullface": "down" }, - "up": { "texture": "#up", "cullface": "up" }, - "north": { "texture": "#north", "cullface": "north" }, - "south": { "texture": "#south", "cullface": "south" }, - "west": { "texture": "#west", "cullface": "west" }, - "east": { "texture": "#east", "cullface": "east" } - } - } - ] -} diff --git a/src/main/resources/assets/wizardry/models/block/meteor.json b/src/main/resources/assets/wizardry/models/block/meteor.json deleted file mode 100644 index 52676102..00000000 --- a/src/main/resources/assets/wizardry/models/block/meteor.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "parent": "block/cube_all", - "textures": { - "all": "wizardry:blocks/meteor" - } -} diff --git a/src/main/resources/assets/wizardry/models/block/petrified_stone.json b/src/main/resources/assets/wizardry/models/block/petrified_stone.json deleted file mode 100644 index 9ecdfbf2..00000000 --- a/src/main/resources/assets/wizardry/models/block/petrified_stone.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "parent": "block/block", - "textures": { - "all": "blocks/stone", - "particle": "blocks/stone" - }, - "elements": [ - { "from": [ 0, 0, 0 ], - "to": [ 0, 0, 0 ], - "faces": { - "down": { "texture": "#down", "cullface": "down" }, - "up": { "texture": "#up", "cullface": "up" }, - "north": { "texture": "#north", "cullface": "north" }, - "south": { "texture": "#south", "cullface": "south" }, - "west": { "texture": "#west", "cullface": "west" }, - "east": { "texture": "#east", "cullface": "east" } - } - } - ] -} diff --git a/src/main/resources/assets/wizardry/models/block/snare.json b/src/main/resources/assets/wizardry/models/block/snare.json deleted file mode 100644 index 52b4fbd9..00000000 --- a/src/main/resources/assets/wizardry/models/block/snare.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "ambientocclusion": false, - "textures": { - "particle": "wizardry:blocks/snare", - "texture": "wizardry:blocks/snare" - }, - "elements": [ - { "from": [ 0, 0.25, 0 ], - "to": [ 16, 0.25, 16 ], - "faces": { - "down": { "uv": [ 16, 16, 0, 0 ], "texture": "#texture"}, - "up": { "uv": [ 16, 0, 0, 16 ], "texture": "#texture"} - } - } - ] -} diff --git a/src/main/resources/assets/wizardry/models/block/spectral_block.json b/src/main/resources/assets/wizardry/models/block/spectral_block.json deleted file mode 100644 index fd4a9d44..00000000 --- a/src/main/resources/assets/wizardry/models/block/spectral_block.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "parent": "block/cube_all", - "textures": { - "all": "wizardry:blocks/spectral_block" - } -} diff --git a/src/main/resources/assets/wizardry/models/block/transportation_stone.json b/src/main/resources/assets/wizardry/models/block/transportation_stone.json deleted file mode 100644 index 9e235690..00000000 --- a/src/main/resources/assets/wizardry/models/block/transportation_stone.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "parent": "block/block", - "textures": { - "all": "wizardry:blocks/transportation_stone", - "particle": "wizardry:blocks/transportation_stone" - }, - "elements": [ - { "from": [ 5, 0, 5 ], - "to": [ 11, 6, 11 ], - "faces": { - "down": { "texture": "#all", "cullface": "down" }, - "up": { "texture": "#all"}, - "north": { "texture": "#all"}, - "south": { "texture": "#all"}, - "west": { "texture": "#all"}, - "east": { "texture": "#all"} - } - } - ] -} diff --git a/src/main/resources/assets/wizardry/models/block/vanishing_cobweb.json b/src/main/resources/assets/wizardry/models/block/vanishing_cobweb.json deleted file mode 100644 index f8865a41..00000000 --- a/src/main/resources/assets/wizardry/models/block/vanishing_cobweb.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "parent": "block/cross", - "textures": { - "cross": "blocks/web" - } -} diff --git a/src/main/resources/assets/wizardry/models/item/advanced_earth_wand.json b/src/main/resources/assets/wizardry/models/item/advanced_earth_wand.json deleted file mode 100644 index 3f23fbc6..00000000 --- a/src/main/resources/assets/wizardry/models/item/advanced_earth_wand.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "parent": "item/handheld", - "textures": { - "layer0": "wizardry:items/wand_advanced_earth" - } -} diff --git a/src/main/resources/assets/wizardry/models/item/advanced_fire_wand.json b/src/main/resources/assets/wizardry/models/item/advanced_fire_wand.json deleted file mode 100644 index beb8fc84..00000000 --- a/src/main/resources/assets/wizardry/models/item/advanced_fire_wand.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "parent": "item/handheld", - "textures": { - "layer0": "wizardry:items/wand_advanced_fire" - } -} diff --git a/src/main/resources/assets/wizardry/models/item/advanced_healing_wand.json b/src/main/resources/assets/wizardry/models/item/advanced_healing_wand.json deleted file mode 100644 index bba06e50..00000000 --- a/src/main/resources/assets/wizardry/models/item/advanced_healing_wand.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "parent": "item/handheld", - "textures": { - "layer0": "wizardry:items/wand_advanced_healing" - } -} diff --git a/src/main/resources/assets/wizardry/models/item/advanced_ice_wand.json b/src/main/resources/assets/wizardry/models/item/advanced_ice_wand.json deleted file mode 100644 index 35dadf70..00000000 --- a/src/main/resources/assets/wizardry/models/item/advanced_ice_wand.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "parent": "item/handheld", - "textures": { - "layer0": "wizardry:items/wand_advanced_ice" - } -} diff --git a/src/main/resources/assets/wizardry/models/item/advanced_lightning_wand.json b/src/main/resources/assets/wizardry/models/item/advanced_lightning_wand.json deleted file mode 100644 index 7c96d092..00000000 --- a/src/main/resources/assets/wizardry/models/item/advanced_lightning_wand.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "parent": "item/handheld", - "textures": { - "layer0": "wizardry:items/wand_advanced_lightning" - } -} diff --git a/src/main/resources/assets/wizardry/models/item/advanced_necromancy_wand.json b/src/main/resources/assets/wizardry/models/item/advanced_necromancy_wand.json deleted file mode 100644 index db36e099..00000000 --- a/src/main/resources/assets/wizardry/models/item/advanced_necromancy_wand.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "parent": "item/handheld", - "textures": { - "layer0": "wizardry:items/wand_advanced_necromancy" - } -} diff --git a/src/main/resources/assets/wizardry/models/item/advanced_sorcery_wand.json b/src/main/resources/assets/wizardry/models/item/advanced_sorcery_wand.json deleted file mode 100644 index 127415e7..00000000 --- a/src/main/resources/assets/wizardry/models/item/advanced_sorcery_wand.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "parent": "item/handheld", - "textures": { - "layer0": "wizardry:items/wand_advanced_sorcery" - } -} diff --git a/src/main/resources/assets/wizardry/models/item/advanced_wand.json b/src/main/resources/assets/wizardry/models/item/advanced_wand.json deleted file mode 100644 index bee14a76..00000000 --- a/src/main/resources/assets/wizardry/models/item/advanced_wand.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "parent": "item/handheld", - "textures": { - "layer0": "wizardry:items/wand_advanced" - } -} diff --git a/src/main/resources/assets/wizardry/models/item/apprentice_earth_wand.json b/src/main/resources/assets/wizardry/models/item/apprentice_earth_wand.json deleted file mode 100644 index 1ec46893..00000000 --- a/src/main/resources/assets/wizardry/models/item/apprentice_earth_wand.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "parent": "item/handheld", - "textures": { - "layer0": "wizardry:items/wand_apprentice_earth" - } -} diff --git a/src/main/resources/assets/wizardry/models/item/apprentice_fire_wand.json b/src/main/resources/assets/wizardry/models/item/apprentice_fire_wand.json deleted file mode 100644 index 0aa6cf8e..00000000 --- a/src/main/resources/assets/wizardry/models/item/apprentice_fire_wand.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "parent": "item/handheld", - "textures": { - "layer0": "wizardry:items/wand_apprentice_fire" - } -} diff --git a/src/main/resources/assets/wizardry/models/item/apprentice_healing_wand.json b/src/main/resources/assets/wizardry/models/item/apprentice_healing_wand.json deleted file mode 100644 index 118d72d0..00000000 --- a/src/main/resources/assets/wizardry/models/item/apprentice_healing_wand.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "parent": "item/handheld", - "textures": { - "layer0": "wizardry:items/wand_apprentice_healing" - } -} diff --git a/src/main/resources/assets/wizardry/models/item/apprentice_ice_wand.json b/src/main/resources/assets/wizardry/models/item/apprentice_ice_wand.json deleted file mode 100644 index 25bbce5c..00000000 --- a/src/main/resources/assets/wizardry/models/item/apprentice_ice_wand.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "parent": "item/handheld", - "textures": { - "layer0": "wizardry:items/wand_apprentice_ice" - } -} diff --git a/src/main/resources/assets/wizardry/models/item/apprentice_lightning_wand.json b/src/main/resources/assets/wizardry/models/item/apprentice_lightning_wand.json deleted file mode 100644 index 2f508113..00000000 --- a/src/main/resources/assets/wizardry/models/item/apprentice_lightning_wand.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "parent": "item/handheld", - "textures": { - "layer0": "wizardry:items/wand_apprentice_lightning" - } -} diff --git a/src/main/resources/assets/wizardry/models/item/apprentice_necromancy_wand.json b/src/main/resources/assets/wizardry/models/item/apprentice_necromancy_wand.json deleted file mode 100644 index eb662348..00000000 --- a/src/main/resources/assets/wizardry/models/item/apprentice_necromancy_wand.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "parent": "item/handheld", - "textures": { - "layer0": "wizardry:items/wand_apprentice_necromancy" - } -} diff --git a/src/main/resources/assets/wizardry/models/item/apprentice_sorcery_wand.json b/src/main/resources/assets/wizardry/models/item/apprentice_sorcery_wand.json deleted file mode 100644 index 4ac8e0da..00000000 --- a/src/main/resources/assets/wizardry/models/item/apprentice_sorcery_wand.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "parent": "item/handheld", - "textures": { - "layer0": "wizardry:items/wand_apprentice_sorcery" - } -} diff --git a/src/main/resources/assets/wizardry/models/item/apprentice_wand.json b/src/main/resources/assets/wizardry/models/item/apprentice_wand.json deleted file mode 100644 index 57a642e0..00000000 --- a/src/main/resources/assets/wizardry/models/item/apprentice_wand.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "parent": "item/handheld", - "textures": { - "layer0": "wizardry:items/wand_apprentice" - } -} diff --git a/src/main/resources/assets/wizardry/models/item/arcane_tome.json b/src/main/resources/assets/wizardry/models/item/arcane_tome.json deleted file mode 100644 index 9737284c..00000000 --- a/src/main/resources/assets/wizardry/models/item/arcane_tome.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "parent": "item/generated", - "textures": { - "layer0": "wizardry:items/arcane_tome" - } -} \ No newline at end of file diff --git a/src/main/resources/assets/wizardry/models/item/arcane_workbench.json b/src/main/resources/assets/wizardry/models/item/arcane_workbench.json deleted file mode 100644 index 35acd55b..00000000 --- a/src/main/resources/assets/wizardry/models/item/arcane_workbench.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "parent": "wizardry:block/arcane_workbench" -} diff --git a/src/main/resources/assets/wizardry/models/item/armour_upgrade.json b/src/main/resources/assets/wizardry/models/item/armour_upgrade.json deleted file mode 100644 index 08fd6606..00000000 --- a/src/main/resources/assets/wizardry/models/item/armour_upgrade.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "parent": "item/generated", - "textures": { - "layer0": "wizardry:items/armour_upgrade" - } -} \ No newline at end of file diff --git a/src/main/resources/assets/wizardry/models/item/attunement_upgrade.json b/src/main/resources/assets/wizardry/models/item/attunement_upgrade.json deleted file mode 100644 index 927fd9fe..00000000 --- a/src/main/resources/assets/wizardry/models/item/attunement_upgrade.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "parent": "item/generated", - "textures": { - "layer0": "wizardry:items/upgrade_attunement" - } -} \ No newline at end of file diff --git a/src/main/resources/assets/wizardry/models/item/basic_earth_wand.json b/src/main/resources/assets/wizardry/models/item/basic_earth_wand.json deleted file mode 100644 index ddbccfee..00000000 --- a/src/main/resources/assets/wizardry/models/item/basic_earth_wand.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "parent": "item/handheld", - "textures": { - "layer0": "wizardry:items/wand_basic_earth" - } -} diff --git a/src/main/resources/assets/wizardry/models/item/basic_fire_wand.json b/src/main/resources/assets/wizardry/models/item/basic_fire_wand.json deleted file mode 100644 index e323c2df..00000000 --- a/src/main/resources/assets/wizardry/models/item/basic_fire_wand.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "parent": "item/handheld", - "textures": { - "layer0": "wizardry:items/wand_basic_fire" - } -} diff --git a/src/main/resources/assets/wizardry/models/item/basic_healing_wand.json b/src/main/resources/assets/wizardry/models/item/basic_healing_wand.json deleted file mode 100644 index 80d6b33c..00000000 --- a/src/main/resources/assets/wizardry/models/item/basic_healing_wand.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "parent": "item/handheld", - "textures": { - "layer0": "wizardry:items/wand_basic_healing" - } -} diff --git a/src/main/resources/assets/wizardry/models/item/basic_ice_wand.json b/src/main/resources/assets/wizardry/models/item/basic_ice_wand.json deleted file mode 100644 index 8bd95b41..00000000 --- a/src/main/resources/assets/wizardry/models/item/basic_ice_wand.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "parent": "item/handheld", - "textures": { - "layer0": "wizardry:items/wand_basic_ice" - } -} diff --git a/src/main/resources/assets/wizardry/models/item/basic_lightning_wand.json b/src/main/resources/assets/wizardry/models/item/basic_lightning_wand.json deleted file mode 100644 index 2307f1a7..00000000 --- a/src/main/resources/assets/wizardry/models/item/basic_lightning_wand.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "parent": "item/handheld", - "textures": { - "layer0": "wizardry:items/wand_basic_lightning" - } -} diff --git a/src/main/resources/assets/wizardry/models/item/basic_necromancy_wand.json b/src/main/resources/assets/wizardry/models/item/basic_necromancy_wand.json deleted file mode 100644 index 0727fa69..00000000 --- a/src/main/resources/assets/wizardry/models/item/basic_necromancy_wand.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "parent": "item/handheld", - "textures": { - "layer0": "wizardry:items/wand_basic_necromancy" - } -} diff --git a/src/main/resources/assets/wizardry/models/item/basic_sorcery_wand.json b/src/main/resources/assets/wizardry/models/item/basic_sorcery_wand.json deleted file mode 100644 index 1a399976..00000000 --- a/src/main/resources/assets/wizardry/models/item/basic_sorcery_wand.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "parent": "item/handheld", - "textures": { - "layer0": "wizardry:items/wand_basic_sorcery" - } -} diff --git a/src/main/resources/assets/wizardry/models/item/blank_scroll.json b/src/main/resources/assets/wizardry/models/item/blank_scroll.json deleted file mode 100644 index a6a86d05..00000000 --- a/src/main/resources/assets/wizardry/models/item/blank_scroll.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "parent": "item/generated", - "textures": { - "layer0": "wizardry:items/scroll" - } -} \ No newline at end of file diff --git a/src/main/resources/assets/wizardry/models/item/blast_upgrade.json b/src/main/resources/assets/wizardry/models/item/blast_upgrade.json deleted file mode 100644 index 3f085c0a..00000000 --- a/src/main/resources/assets/wizardry/models/item/blast_upgrade.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "parent": "item/generated", - "textures": { - "layer0": "wizardry:items/upgrade_blast" - } -} \ No newline at end of file diff --git a/src/main/resources/assets/wizardry/models/item/condenser_upgrade.json b/src/main/resources/assets/wizardry/models/item/condenser_upgrade.json deleted file mode 100644 index 42a4c63f..00000000 --- a/src/main/resources/assets/wizardry/models/item/condenser_upgrade.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "parent": "item/generated", - "textures": { - "layer0": "wizardry:items/upgrade_condenser" - } -} \ No newline at end of file diff --git a/src/main/resources/assets/wizardry/models/item/cooldown_upgrade.json b/src/main/resources/assets/wizardry/models/item/cooldown_upgrade.json deleted file mode 100644 index 23addd92..00000000 --- a/src/main/resources/assets/wizardry/models/item/cooldown_upgrade.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "parent": "item/generated", - "textures": { - "layer0": "wizardry:items/upgrade_cooldown" - } -} \ No newline at end of file diff --git a/src/main/resources/assets/wizardry/models/item/crystal_block.json b/src/main/resources/assets/wizardry/models/item/crystal_block.json deleted file mode 100644 index 290f0923..00000000 --- a/src/main/resources/assets/wizardry/models/item/crystal_block.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "parent": "wizardry:block/crystal_block" -} diff --git a/src/main/resources/assets/wizardry/models/item/crystal_flower.json b/src/main/resources/assets/wizardry/models/item/crystal_flower.json deleted file mode 100644 index b71cf4d6..00000000 --- a/src/main/resources/assets/wizardry/models/item/crystal_flower.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "parent": "item/generated", - "textures": { - "layer0": "wizardry:blocks/crystal_flower" - } -} diff --git a/src/main/resources/assets/wizardry/models/item/crystal_ore.json b/src/main/resources/assets/wizardry/models/item/crystal_ore.json deleted file mode 100644 index f288786d..00000000 --- a/src/main/resources/assets/wizardry/models/item/crystal_ore.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "parent": "wizardry:block/crystal_ore" -} diff --git a/src/main/resources/assets/wizardry/models/item/duration_upgrade.json b/src/main/resources/assets/wizardry/models/item/duration_upgrade.json deleted file mode 100644 index 5acea158..00000000 --- a/src/main/resources/assets/wizardry/models/item/duration_upgrade.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "parent": "item/generated", - "textures": { - "layer0": "wizardry:items/upgrade_duration" - } -} \ No newline at end of file diff --git a/src/main/resources/assets/wizardry/models/item/firebomb.json b/src/main/resources/assets/wizardry/models/item/firebomb.json deleted file mode 100644 index a1c8a29f..00000000 --- a/src/main/resources/assets/wizardry/models/item/firebomb.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "parent": "item/generated", - "textures": { - "layer0": "wizardry:items/firebomb" - } -} \ No newline at end of file diff --git a/src/main/resources/assets/wizardry/models/item/flaming_axe.json b/src/main/resources/assets/wizardry/models/item/flaming_axe.json deleted file mode 100644 index 104d511f..00000000 --- a/src/main/resources/assets/wizardry/models/item/flaming_axe.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "parent": "item/handheld", - "textures": { - "layer0": "wizardry:items/flaming_axe" - } -} diff --git a/src/main/resources/assets/wizardry/models/item/frost_axe.json b/src/main/resources/assets/wizardry/models/item/frost_axe.json deleted file mode 100644 index 1204aac7..00000000 --- a/src/main/resources/assets/wizardry/models/item/frost_axe.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "parent": "item/handheld", - "textures": { - "layer0": "wizardry:items/frost_axe" - } -} diff --git a/src/main/resources/assets/wizardry/models/item/ice_statue.json b/src/main/resources/assets/wizardry/models/item/ice_statue.json deleted file mode 100644 index a3cfbe68..00000000 --- a/src/main/resources/assets/wizardry/models/item/ice_statue.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "parent": "wizardry:block/ice_statue" -} diff --git a/src/main/resources/assets/wizardry/models/item/identification_scroll.json b/src/main/resources/assets/wizardry/models/item/identification_scroll.json deleted file mode 100644 index 4c39a408..00000000 --- a/src/main/resources/assets/wizardry/models/item/identification_scroll.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "parent": "item/generated", - "textures": { - "layer0": "wizardry:items/identification_scroll" - } -} \ No newline at end of file diff --git a/src/main/resources/assets/wizardry/models/item/magic_crystal.json b/src/main/resources/assets/wizardry/models/item/magic_crystal.json deleted file mode 100644 index 1aae8ddb..00000000 --- a/src/main/resources/assets/wizardry/models/item/magic_crystal.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "parent": "item/generated", - "textures": { - "layer0": "wizardry:items/magic_crystal" - } -} \ No newline at end of file diff --git a/src/main/resources/assets/wizardry/models/item/magic_light.json b/src/main/resources/assets/wizardry/models/item/magic_light.json deleted file mode 100644 index c5a50b1a..00000000 --- a/src/main/resources/assets/wizardry/models/item/magic_light.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "parent": "wizardry:block/sea_lantern" -} diff --git a/src/main/resources/assets/wizardry/models/item/magic_silk.json b/src/main/resources/assets/wizardry/models/item/magic_silk.json deleted file mode 100644 index cec4cb55..00000000 --- a/src/main/resources/assets/wizardry/models/item/magic_silk.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "parent": "item/generated", - "textures": { - "layer0": "wizardry:items/magic_silk" - } -} \ No newline at end of file diff --git a/src/main/resources/assets/wizardry/models/item/magic_wand.json b/src/main/resources/assets/wizardry/models/item/magic_wand.json deleted file mode 100644 index 1bb79f4f..00000000 --- a/src/main/resources/assets/wizardry/models/item/magic_wand.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "parent": "item/handheld", - "textures": { - "layer0": "wizardry:items/wand_basic" - } -} diff --git a/src/main/resources/assets/wizardry/models/item/mana_flask.json b/src/main/resources/assets/wizardry/models/item/mana_flask.json deleted file mode 100644 index f898986f..00000000 --- a/src/main/resources/assets/wizardry/models/item/mana_flask.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "parent": "item/generated", - "textures": { - "layer0": "wizardry:items/mana_flask" - } -} \ No newline at end of file diff --git a/src/main/resources/assets/wizardry/models/item/master_earth_wand.json b/src/main/resources/assets/wizardry/models/item/master_earth_wand.json deleted file mode 100644 index a880c440..00000000 --- a/src/main/resources/assets/wizardry/models/item/master_earth_wand.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "parent": "item/handheld", - "textures": { - "layer0": "wizardry:items/wand_master_earth" - } -} diff --git a/src/main/resources/assets/wizardry/models/item/master_fire_wand.json b/src/main/resources/assets/wizardry/models/item/master_fire_wand.json deleted file mode 100644 index f2da76dc..00000000 --- a/src/main/resources/assets/wizardry/models/item/master_fire_wand.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "parent": "item/handheld", - "textures": { - "layer0": "wizardry:items/wand_master_fire" - } -} diff --git a/src/main/resources/assets/wizardry/models/item/master_healing_wand.json b/src/main/resources/assets/wizardry/models/item/master_healing_wand.json deleted file mode 100644 index 17c1dd41..00000000 --- a/src/main/resources/assets/wizardry/models/item/master_healing_wand.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "parent": "item/handheld", - "textures": { - "layer0": "wizardry:items/wand_master_healing" - } -} diff --git a/src/main/resources/assets/wizardry/models/item/master_ice_wand.json b/src/main/resources/assets/wizardry/models/item/master_ice_wand.json deleted file mode 100644 index 79f80cb6..00000000 --- a/src/main/resources/assets/wizardry/models/item/master_ice_wand.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "parent": "item/handheld", - "textures": { - "layer0": "wizardry:items/wand_master_ice" - } -} diff --git a/src/main/resources/assets/wizardry/models/item/master_lightning_wand.json b/src/main/resources/assets/wizardry/models/item/master_lightning_wand.json deleted file mode 100644 index 03e5f0ca..00000000 --- a/src/main/resources/assets/wizardry/models/item/master_lightning_wand.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "parent": "item/handheld", - "textures": { - "layer0": "wizardry:items/wand_master_lightning" - } -} diff --git a/src/main/resources/assets/wizardry/models/item/master_necromancy_wand.json b/src/main/resources/assets/wizardry/models/item/master_necromancy_wand.json deleted file mode 100644 index 5cc36467..00000000 --- a/src/main/resources/assets/wizardry/models/item/master_necromancy_wand.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "parent": "item/handheld", - "textures": { - "layer0": "wizardry:items/wand_master_necromancy" - } -} diff --git a/src/main/resources/assets/wizardry/models/item/master_sorcery_wand.json b/src/main/resources/assets/wizardry/models/item/master_sorcery_wand.json deleted file mode 100644 index 67cf62ec..00000000 --- a/src/main/resources/assets/wizardry/models/item/master_sorcery_wand.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "parent": "item/handheld", - "textures": { - "layer0": "wizardry:items/wand_master_sorcery" - } -} diff --git a/src/main/resources/assets/wizardry/models/item/master_wand.json b/src/main/resources/assets/wizardry/models/item/master_wand.json deleted file mode 100644 index e7875627..00000000 --- a/src/main/resources/assets/wizardry/models/item/master_wand.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "parent": "item/handheld", - "textures": { - "layer0": "wizardry:items/wand_master" - } -} diff --git a/src/main/resources/assets/wizardry/models/item/meteor.json b/src/main/resources/assets/wizardry/models/item/meteor.json deleted file mode 100644 index 14bb6068..00000000 --- a/src/main/resources/assets/wizardry/models/item/meteor.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "parent": "wizardry:block/meteor" -} diff --git a/src/main/resources/assets/wizardry/models/item/petrified_stone.json b/src/main/resources/assets/wizardry/models/item/petrified_stone.json deleted file mode 100644 index 68982f03..00000000 --- a/src/main/resources/assets/wizardry/models/item/petrified_stone.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "parent": "block/stone" -} diff --git a/src/main/resources/assets/wizardry/models/item/poison_bomb.json b/src/main/resources/assets/wizardry/models/item/poison_bomb.json deleted file mode 100644 index d4c0d24a..00000000 --- a/src/main/resources/assets/wizardry/models/item/poison_bomb.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "parent": "item/generated", - "textures": { - "layer0": "wizardry:items/poison_bomb" - } -} \ No newline at end of file diff --git a/src/main/resources/assets/wizardry/models/item/range_upgrade.json b/src/main/resources/assets/wizardry/models/item/range_upgrade.json deleted file mode 100644 index 10896bb2..00000000 --- a/src/main/resources/assets/wizardry/models/item/range_upgrade.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "parent": "item/generated", - "textures": { - "layer0": "wizardry:items/upgrade_range" - } -} \ No newline at end of file diff --git a/src/main/resources/assets/wizardry/models/item/scroll.json b/src/main/resources/assets/wizardry/models/item/scroll.json deleted file mode 100644 index a6a86d05..00000000 --- a/src/main/resources/assets/wizardry/models/item/scroll.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "parent": "item/generated", - "textures": { - "layer0": "wizardry:items/scroll" - } -} \ No newline at end of file diff --git a/src/main/resources/assets/wizardry/models/item/siphon_upgrade.json b/src/main/resources/assets/wizardry/models/item/siphon_upgrade.json deleted file mode 100644 index eec82d86..00000000 --- a/src/main/resources/assets/wizardry/models/item/siphon_upgrade.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "parent": "item/generated", - "textures": { - "layer0": "wizardry:items/upgrade_siphon" - } -} \ No newline at end of file diff --git a/src/main/resources/assets/wizardry/models/item/smoke_bomb.json b/src/main/resources/assets/wizardry/models/item/smoke_bomb.json deleted file mode 100644 index 0fffc501..00000000 --- a/src/main/resources/assets/wizardry/models/item/smoke_bomb.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "parent": "item/generated", - "textures": { - "layer0": "wizardry:items/smoke_bomb" - } -} \ No newline at end of file diff --git a/src/main/resources/assets/wizardry/models/item/snare.json b/src/main/resources/assets/wizardry/models/item/snare.json deleted file mode 100644 index ec41cce6..00000000 --- a/src/main/resources/assets/wizardry/models/item/snare.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "parent": "item/generated", - "textures": { - "layer0": "wizardry:blocks/snare" - } -} diff --git a/src/main/resources/assets/wizardry/models/item/spawn_wizard.json b/src/main/resources/assets/wizardry/models/item/spawn_wizard.json deleted file mode 100644 index 908f7fa0..00000000 --- a/src/main/resources/assets/wizardry/models/item/spawn_wizard.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "parent": "item/generated", - "textures": { - "layer0": "items/spawn_egg", - "layer1": "items/spawn_egg_overlay" - } -} diff --git a/src/main/resources/assets/wizardry/models/item/spectral_block.json b/src/main/resources/assets/wizardry/models/item/spectral_block.json deleted file mode 100644 index 9d577615..00000000 --- a/src/main/resources/assets/wizardry/models/item/spectral_block.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "parent": "wizardry:block/spectral_block" -} diff --git a/src/main/resources/assets/wizardry/models/item/spectral_boots.json b/src/main/resources/assets/wizardry/models/item/spectral_boots.json deleted file mode 100644 index e21ea294..00000000 --- a/src/main/resources/assets/wizardry/models/item/spectral_boots.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "parent": "item/generated", - "textures": { - "layer0": "wizardry:items/spectral_boots" - } -} \ No newline at end of file diff --git a/src/main/resources/assets/wizardry/models/item/spectral_bow.json b/src/main/resources/assets/wizardry/models/item/spectral_bow.json deleted file mode 100644 index 649b7409..00000000 --- a/src/main/resources/assets/wizardry/models/item/spectral_bow.json +++ /dev/null @@ -1,50 +0,0 @@ -{ - "parent": "item/generated", - "textures": { - "layer0": "wizardry:items/spectral_bow_standby" - }, - "display": { - "thirdperson_righthand": { - "rotation": [ -80, 260, -40 ], - "translation": [ -1, -2, 2.5 ], - "scale": [ 0.9, 0.9, 0.9 ] - }, - "thirdperson_lefthand": { - "rotation": [ -80, -280, 40 ], - "translation": [ -1, -2, 2.5 ], - "scale": [ 0.9, 0.9, 0.9 ] - }, - "firstperson_righthand": { - "rotation": [ 0, -90, 25 ], - "translation": [ 1.13, 3.2, 1.13], - "scale": [ 0.68, 0.68, 0.68 ] - }, - "firstperson_lefthand": { - "rotation": [ 0, 90, -25 ], - "translation": [ 1.13, 3.2, 1.13], - "scale": [ 0.68, 0.68, 0.68 ] - } - }, - "overrides": [ - { - "predicate": { - "pulling": 1 - }, - "model": "wizardry:item/spectral_bow_pulling_0" - }, - { - "predicate": { - "pulling": 1, - "pull": 0.65 - }, - "model": "wizardry:item/spectral_bow_pulling_1" - }, - { - "predicate": { - "pulling": 1, - "pull": 0.9 - }, - "model": "wizardry:item/spectral_bow_pulling_2" - } - ] -} diff --git a/src/main/resources/assets/wizardry/models/item/spectral_bow_pulling_0.json b/src/main/resources/assets/wizardry/models/item/spectral_bow_pulling_0.json deleted file mode 100644 index 94e5d474..00000000 --- a/src/main/resources/assets/wizardry/models/item/spectral_bow_pulling_0.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "parent": "item/bow", - "textures": { - "layer0": "wizardry:items/spectral_bow_pulling_0" - } -} diff --git a/src/main/resources/assets/wizardry/models/item/spectral_bow_pulling_1.json b/src/main/resources/assets/wizardry/models/item/spectral_bow_pulling_1.json deleted file mode 100644 index 1c711fa3..00000000 --- a/src/main/resources/assets/wizardry/models/item/spectral_bow_pulling_1.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "parent": "item/bow", - "textures": { - "layer0": "wizardry:items/spectral_bow_pulling_1" - } -} diff --git a/src/main/resources/assets/wizardry/models/item/spectral_bow_pulling_2.json b/src/main/resources/assets/wizardry/models/item/spectral_bow_pulling_2.json deleted file mode 100644 index b4762523..00000000 --- a/src/main/resources/assets/wizardry/models/item/spectral_bow_pulling_2.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "parent": "item/bow", - "textures": { - "layer0": "wizardry:items/spectral_bow_pulling_2" - } -} diff --git a/src/main/resources/assets/wizardry/models/item/spectral_chestplate.json b/src/main/resources/assets/wizardry/models/item/spectral_chestplate.json deleted file mode 100644 index 33de309e..00000000 --- a/src/main/resources/assets/wizardry/models/item/spectral_chestplate.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "parent": "item/generated", - "textures": { - "layer0": "wizardry:items/spectral_chestplate" - } -} \ No newline at end of file diff --git a/src/main/resources/assets/wizardry/models/item/spectral_helmet.json b/src/main/resources/assets/wizardry/models/item/spectral_helmet.json deleted file mode 100644 index 5a8a9433..00000000 --- a/src/main/resources/assets/wizardry/models/item/spectral_helmet.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "parent": "item/generated", - "textures": { - "layer0": "wizardry:items/spectral_helmet" - } -} \ No newline at end of file diff --git a/src/main/resources/assets/wizardry/models/item/spectral_leggings.json b/src/main/resources/assets/wizardry/models/item/spectral_leggings.json deleted file mode 100644 index e55f9c23..00000000 --- a/src/main/resources/assets/wizardry/models/item/spectral_leggings.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "parent": "item/generated", - "textures": { - "layer0": "wizardry:items/spectral_leggings" - } -} \ No newline at end of file diff --git a/src/main/resources/assets/wizardry/models/item/spectral_pickaxe.json b/src/main/resources/assets/wizardry/models/item/spectral_pickaxe.json deleted file mode 100644 index 45347c00..00000000 --- a/src/main/resources/assets/wizardry/models/item/spectral_pickaxe.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "parent": "item/handheld", - "textures": { - "layer0": "wizardry:items/spectral_pickaxe" - } -} diff --git a/src/main/resources/assets/wizardry/models/item/spectral_sword.json b/src/main/resources/assets/wizardry/models/item/spectral_sword.json deleted file mode 100644 index 4e88c159..00000000 --- a/src/main/resources/assets/wizardry/models/item/spectral_sword.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "parent": "item/handheld", - "textures": { - "layer0": "wizardry:items/spectral_sword" - } -} diff --git a/src/main/resources/assets/wizardry/models/item/spell_book.json b/src/main/resources/assets/wizardry/models/item/spell_book.json deleted file mode 100644 index 5a3d2fdb..00000000 --- a/src/main/resources/assets/wizardry/models/item/spell_book.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "parent": "item/generated", - "textures": { - "layer0": "wizardry:items/spell_book" - } -} \ No newline at end of file diff --git a/src/main/resources/assets/wizardry/models/item/storage_upgrade.json b/src/main/resources/assets/wizardry/models/item/storage_upgrade.json deleted file mode 100644 index 3673fa43..00000000 --- a/src/main/resources/assets/wizardry/models/item/storage_upgrade.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "parent": "item/generated", - "textures": { - "layer0": "wizardry:items/upgrade_storage" - } -} \ No newline at end of file diff --git a/src/main/resources/assets/wizardry/models/item/transportation_stone.json b/src/main/resources/assets/wizardry/models/item/transportation_stone.json deleted file mode 100644 index 2b0a1e09..00000000 --- a/src/main/resources/assets/wizardry/models/item/transportation_stone.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "parent": "wizardry:block/transportation_stone" -} diff --git a/src/main/resources/assets/wizardry/models/item/vanishing_cobweb.json b/src/main/resources/assets/wizardry/models/item/vanishing_cobweb.json deleted file mode 100644 index 1fe6d579..00000000 --- a/src/main/resources/assets/wizardry/models/item/vanishing_cobweb.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "parent": "item/generated", - "textures": { - "layer0": "blocks/web" - } -} diff --git a/src/main/resources/assets/wizardry/models/item/wizard_boots.json b/src/main/resources/assets/wizardry/models/item/wizard_boots.json deleted file mode 100644 index 39685945..00000000 --- a/src/main/resources/assets/wizardry/models/item/wizard_boots.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "parent": "item/generated", - "textures": { - "layer0": "wizardry:items/wizard_boots" - } -} \ No newline at end of file diff --git a/src/main/resources/assets/wizardry/models/item/wizard_boots_earth.json b/src/main/resources/assets/wizardry/models/item/wizard_boots_earth.json deleted file mode 100644 index 9ee903af..00000000 --- a/src/main/resources/assets/wizardry/models/item/wizard_boots_earth.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "parent": "item/generated", - "textures": { - "layer0": "wizardry:items/wizard_boots_earth" - } -} \ No newline at end of file diff --git a/src/main/resources/assets/wizardry/models/item/wizard_boots_fire.json b/src/main/resources/assets/wizardry/models/item/wizard_boots_fire.json deleted file mode 100644 index a1e664d0..00000000 --- a/src/main/resources/assets/wizardry/models/item/wizard_boots_fire.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "parent": "item/generated", - "textures": { - "layer0": "wizardry:items/wizard_boots_fire" - } -} \ No newline at end of file diff --git a/src/main/resources/assets/wizardry/models/item/wizard_boots_healing.json b/src/main/resources/assets/wizardry/models/item/wizard_boots_healing.json deleted file mode 100644 index 57c5b635..00000000 --- a/src/main/resources/assets/wizardry/models/item/wizard_boots_healing.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "parent": "item/generated", - "textures": { - "layer0": "wizardry:items/wizard_boots_healing" - } -} \ No newline at end of file diff --git a/src/main/resources/assets/wizardry/models/item/wizard_boots_ice.json b/src/main/resources/assets/wizardry/models/item/wizard_boots_ice.json deleted file mode 100644 index be14210e..00000000 --- a/src/main/resources/assets/wizardry/models/item/wizard_boots_ice.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "parent": "item/generated", - "textures": { - "layer0": "wizardry:items/wizard_boots_ice" - } -} \ No newline at end of file diff --git a/src/main/resources/assets/wizardry/models/item/wizard_boots_lightning.json b/src/main/resources/assets/wizardry/models/item/wizard_boots_lightning.json deleted file mode 100644 index d6904206..00000000 --- a/src/main/resources/assets/wizardry/models/item/wizard_boots_lightning.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "parent": "item/generated", - "textures": { - "layer0": "wizardry:items/wizard_boots_lightning" - } -} \ No newline at end of file diff --git a/src/main/resources/assets/wizardry/models/item/wizard_boots_necromancy.json b/src/main/resources/assets/wizardry/models/item/wizard_boots_necromancy.json deleted file mode 100644 index 65abcd7b..00000000 --- a/src/main/resources/assets/wizardry/models/item/wizard_boots_necromancy.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "parent": "item/generated", - "textures": { - "layer0": "wizardry:items/wizard_boots_necromancy" - } -} \ No newline at end of file diff --git a/src/main/resources/assets/wizardry/models/item/wizard_boots_sorcery.json b/src/main/resources/assets/wizardry/models/item/wizard_boots_sorcery.json deleted file mode 100644 index ffbf448b..00000000 --- a/src/main/resources/assets/wizardry/models/item/wizard_boots_sorcery.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "parent": "item/generated", - "textures": { - "layer0": "wizardry:items/wizard_boots_sorcery" - } -} \ No newline at end of file diff --git a/src/main/resources/assets/wizardry/models/item/wizard_handbook.json b/src/main/resources/assets/wizardry/models/item/wizard_handbook.json deleted file mode 100644 index c302b6b6..00000000 --- a/src/main/resources/assets/wizardry/models/item/wizard_handbook.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "parent": "item/generated", - "textures": { - "layer0": "wizardry:items/wizard_handbook" - } -} \ No newline at end of file diff --git a/src/main/resources/assets/wizardry/models/item/wizard_hat.json b/src/main/resources/assets/wizardry/models/item/wizard_hat.json deleted file mode 100644 index 7f1dad7a..00000000 --- a/src/main/resources/assets/wizardry/models/item/wizard_hat.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "parent": "item/generated", - "textures": { - "layer0": "wizardry:items/wizard_hat" - } -} \ No newline at end of file diff --git a/src/main/resources/assets/wizardry/models/item/wizard_hat_earth.json b/src/main/resources/assets/wizardry/models/item/wizard_hat_earth.json deleted file mode 100644 index 95273a7f..00000000 --- a/src/main/resources/assets/wizardry/models/item/wizard_hat_earth.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "parent": "item/generated", - "textures": { - "layer0": "wizardry:items/wizard_hat_earth" - } -} \ No newline at end of file diff --git a/src/main/resources/assets/wizardry/models/item/wizard_hat_fire.json b/src/main/resources/assets/wizardry/models/item/wizard_hat_fire.json deleted file mode 100644 index abecf19d..00000000 --- a/src/main/resources/assets/wizardry/models/item/wizard_hat_fire.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "parent": "item/generated", - "textures": { - "layer0": "wizardry:items/wizard_hat_fire" - } -} \ No newline at end of file diff --git a/src/main/resources/assets/wizardry/models/item/wizard_hat_healing.json b/src/main/resources/assets/wizardry/models/item/wizard_hat_healing.json deleted file mode 100644 index 0c453f01..00000000 --- a/src/main/resources/assets/wizardry/models/item/wizard_hat_healing.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "parent": "item/generated", - "textures": { - "layer0": "wizardry:items/wizard_hat_healing" - } -} \ No newline at end of file diff --git a/src/main/resources/assets/wizardry/models/item/wizard_hat_ice.json b/src/main/resources/assets/wizardry/models/item/wizard_hat_ice.json deleted file mode 100644 index 98430ed6..00000000 --- a/src/main/resources/assets/wizardry/models/item/wizard_hat_ice.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "parent": "item/generated", - "textures": { - "layer0": "wizardry:items/wizard_hat_ice" - } -} \ No newline at end of file diff --git a/src/main/resources/assets/wizardry/models/item/wizard_hat_lightning.json b/src/main/resources/assets/wizardry/models/item/wizard_hat_lightning.json deleted file mode 100644 index 408eae50..00000000 --- a/src/main/resources/assets/wizardry/models/item/wizard_hat_lightning.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "parent": "item/generated", - "textures": { - "layer0": "wizardry:items/wizard_hat_lightning" - } -} \ No newline at end of file diff --git a/src/main/resources/assets/wizardry/models/item/wizard_hat_necromancy.json b/src/main/resources/assets/wizardry/models/item/wizard_hat_necromancy.json deleted file mode 100644 index 8c62b1dd..00000000 --- a/src/main/resources/assets/wizardry/models/item/wizard_hat_necromancy.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "parent": "item/generated", - "textures": { - "layer0": "wizardry:items/wizard_hat_necromancy" - } -} \ No newline at end of file diff --git a/src/main/resources/assets/wizardry/models/item/wizard_hat_sorcery.json b/src/main/resources/assets/wizardry/models/item/wizard_hat_sorcery.json deleted file mode 100644 index 3cb674bb..00000000 --- a/src/main/resources/assets/wizardry/models/item/wizard_hat_sorcery.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "parent": "item/generated", - "textures": { - "layer0": "wizardry:items/wizard_hat_sorcery" - } -} \ No newline at end of file diff --git a/src/main/resources/assets/wizardry/models/item/wizard_leggings.json b/src/main/resources/assets/wizardry/models/item/wizard_leggings.json deleted file mode 100644 index 02aed97c..00000000 --- a/src/main/resources/assets/wizardry/models/item/wizard_leggings.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "parent": "item/generated", - "textures": { - "layer0": "wizardry:items/wizard_leggings" - } -} \ No newline at end of file diff --git a/src/main/resources/assets/wizardry/models/item/wizard_leggings_earth.json b/src/main/resources/assets/wizardry/models/item/wizard_leggings_earth.json deleted file mode 100644 index 8fcec98f..00000000 --- a/src/main/resources/assets/wizardry/models/item/wizard_leggings_earth.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "parent": "item/generated", - "textures": { - "layer0": "wizardry:items/wizard_leggings_earth" - } -} \ No newline at end of file diff --git a/src/main/resources/assets/wizardry/models/item/wizard_leggings_fire.json b/src/main/resources/assets/wizardry/models/item/wizard_leggings_fire.json deleted file mode 100644 index bcb63bac..00000000 --- a/src/main/resources/assets/wizardry/models/item/wizard_leggings_fire.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "parent": "item/generated", - "textures": { - "layer0": "wizardry:items/wizard_leggings_fire" - } -} \ No newline at end of file diff --git a/src/main/resources/assets/wizardry/models/item/wizard_leggings_healing.json b/src/main/resources/assets/wizardry/models/item/wizard_leggings_healing.json deleted file mode 100644 index 16c90a84..00000000 --- a/src/main/resources/assets/wizardry/models/item/wizard_leggings_healing.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "parent": "item/generated", - "textures": { - "layer0": "wizardry:items/wizard_leggings_healing" - } -} \ No newline at end of file diff --git a/src/main/resources/assets/wizardry/models/item/wizard_leggings_ice.json b/src/main/resources/assets/wizardry/models/item/wizard_leggings_ice.json deleted file mode 100644 index c1424913..00000000 --- a/src/main/resources/assets/wizardry/models/item/wizard_leggings_ice.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "parent": "item/generated", - "textures": { - "layer0": "wizardry:items/wizard_leggings_ice" - } -} \ No newline at end of file diff --git a/src/main/resources/assets/wizardry/models/item/wizard_leggings_lightning.json b/src/main/resources/assets/wizardry/models/item/wizard_leggings_lightning.json deleted file mode 100644 index f8e4824c..00000000 --- a/src/main/resources/assets/wizardry/models/item/wizard_leggings_lightning.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "parent": "item/generated", - "textures": { - "layer0": "wizardry:items/wizard_leggings_lightning" - } -} \ No newline at end of file diff --git a/src/main/resources/assets/wizardry/models/item/wizard_leggings_necromancy.json b/src/main/resources/assets/wizardry/models/item/wizard_leggings_necromancy.json deleted file mode 100644 index 9c97d209..00000000 --- a/src/main/resources/assets/wizardry/models/item/wizard_leggings_necromancy.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "parent": "item/generated", - "textures": { - "layer0": "wizardry:items/wizard_leggings_necromancy" - } -} \ No newline at end of file diff --git a/src/main/resources/assets/wizardry/models/item/wizard_leggings_sorcery.json b/src/main/resources/assets/wizardry/models/item/wizard_leggings_sorcery.json deleted file mode 100644 index 3ac6dd52..00000000 --- a/src/main/resources/assets/wizardry/models/item/wizard_leggings_sorcery.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "parent": "item/generated", - "textures": { - "layer0": "wizardry:items/wizard_leggings_sorcery" - } -} \ No newline at end of file diff --git a/src/main/resources/assets/wizardry/models/item/wizard_robe.json b/src/main/resources/assets/wizardry/models/item/wizard_robe.json deleted file mode 100644 index 7f8e266e..00000000 --- a/src/main/resources/assets/wizardry/models/item/wizard_robe.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "parent": "item/generated", - "textures": { - "layer0": "wizardry:items/wizard_robe" - } -} \ No newline at end of file diff --git a/src/main/resources/assets/wizardry/models/item/wizard_robe_earth.json b/src/main/resources/assets/wizardry/models/item/wizard_robe_earth.json deleted file mode 100644 index 66ed7826..00000000 --- a/src/main/resources/assets/wizardry/models/item/wizard_robe_earth.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "parent": "item/generated", - "textures": { - "layer0": "wizardry:items/wizard_robe_earth" - } -} \ No newline at end of file diff --git a/src/main/resources/assets/wizardry/models/item/wizard_robe_fire.json b/src/main/resources/assets/wizardry/models/item/wizard_robe_fire.json deleted file mode 100644 index 6ab282f2..00000000 --- a/src/main/resources/assets/wizardry/models/item/wizard_robe_fire.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "parent": "item/generated", - "textures": { - "layer0": "wizardry:items/wizard_robe_fire" - } -} \ No newline at end of file diff --git a/src/main/resources/assets/wizardry/models/item/wizard_robe_healing.json b/src/main/resources/assets/wizardry/models/item/wizard_robe_healing.json deleted file mode 100644 index 54cba900..00000000 --- a/src/main/resources/assets/wizardry/models/item/wizard_robe_healing.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "parent": "item/generated", - "textures": { - "layer0": "wizardry:items/wizard_robe_healing" - } -} \ No newline at end of file diff --git a/src/main/resources/assets/wizardry/models/item/wizard_robe_ice.json b/src/main/resources/assets/wizardry/models/item/wizard_robe_ice.json deleted file mode 100644 index 45398f00..00000000 --- a/src/main/resources/assets/wizardry/models/item/wizard_robe_ice.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "parent": "item/generated", - "textures": { - "layer0": "wizardry:items/wizard_robe_ice" - } -} \ No newline at end of file diff --git a/src/main/resources/assets/wizardry/models/item/wizard_robe_lightning.json b/src/main/resources/assets/wizardry/models/item/wizard_robe_lightning.json deleted file mode 100644 index ce513b4a..00000000 --- a/src/main/resources/assets/wizardry/models/item/wizard_robe_lightning.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "parent": "item/generated", - "textures": { - "layer0": "wizardry:items/wizard_robe_lightning" - } -} \ No newline at end of file diff --git a/src/main/resources/assets/wizardry/models/item/wizard_robe_necromancy.json b/src/main/resources/assets/wizardry/models/item/wizard_robe_necromancy.json deleted file mode 100644 index c3ff763a..00000000 --- a/src/main/resources/assets/wizardry/models/item/wizard_robe_necromancy.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "parent": "item/generated", - "textures": { - "layer0": "wizardry:items/wizard_robe_necromancy" - } -} \ No newline at end of file diff --git a/src/main/resources/assets/wizardry/models/item/wizard_robe_sorcery.json b/src/main/resources/assets/wizardry/models/item/wizard_robe_sorcery.json deleted file mode 100644 index 2b5224eb..00000000 --- a/src/main/resources/assets/wizardry/models/item/wizard_robe_sorcery.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "parent": "item/generated", - "textures": { - "layer0": "wizardry:items/wizard_robe_sorcery" - } -} \ No newline at end of file diff --git a/src/main/resources/assets/wizardry/sounds.json b/src/main/resources/assets/wizardry/sounds.json index 857b9a40..9111e00c 100644 --- a/src/main/resources/assets/wizardry/sounds.json +++ b/src/main/resources/assets/wizardry/sounds.json @@ -1,39 +1,38 @@ { "arc": {"category": "player","sounds": [ - {"name": "wizardry:arc1","stream": false}, - {"name": "wizardry:arc1","stream": false}, - {"name": "wizardry:arc3","stream": false} + {"name": "arc1","stream": false}, + {"name": "arc1","stream": false}, + {"name": "arc3","stream": false} ]}, - "aura": {"category": "player","sounds": [{"name": "wizardry:aura","stream": false}]}, - "boom": {"category": "player","sounds": [{"name": "wizardry:boom","stream": false}]}, - "crackle": {"category": "player","sounds": [{"name": "wizardry:crackle","stream": false}]}, + "aura": {"category": "player","sounds": [{"name": "aura","stream": false}]}, + "boom": {"category": "player","sounds": [{"name": "boom","stream": false}]}, + "crackle": {"category": "player","sounds": [{"name": "crackle","stream": false}]}, "darkaura": {"category": "player","sounds": [ - {"name": "wizardry:darkaura1","stream": false}, - {"name": "wizardry:darkaura2","stream": false} + {"name": "darkaura1","stream": false}, + {"name": "darkaura2","stream": false} ]}, "effect": {"category": "player","sounds": [ - {"name": "wizardry:effect1","stream": false}, - {"name": "wizardry:effect2","stream": false} + {"name": "effect1","stream": false}, + {"name": "effect2","stream": false} ]}, - "electricitya": {"category": "player","sounds": [{"name": "wizardry:electricitya","stream": false}]}, - "electricityb": {"category": "player","sounds": [{"name": "wizardry:electricityb","stream": false}]}, + "electricitya": {"category": "player","sounds": [{"name": "electricitya","stream": false}]}, + "electricityb": {"category": "player","sounds": [{"name": "electricityb","stream": false}]}, - "flameray": {"category": "player","sounds": [{"name": "wizardry:flameray2","stream": false}]}, - "force": {"category": "player","sounds": [{"name": "wizardry:force","stream": false}]}, + "flameray": {"category": "player","sounds": [{"name": "flameray2","stream": false}]}, - "freeze": {"category": "player","sounds": [{"name": "wizardry:freeze","stream": false}]}, - "frostray": {"category": "player","sounds": [{"name": "wizardry:frostray","stream": false}]}, - "heal": {"category": "player","sounds": [{"name": "wizardry:heal","stream": false}]}, - "ice": {"category": "player","sounds": [{"name": "wizardry:ice","stream": false}]}, - "largeaura": {"category": "player","sounds": [{"name": "wizardry:largeaura","stream": false}]}, + "freeze": {"category": "player","sounds": [{"name": "freeze","stream": false}]}, + "frostray": {"category": "player","sounds": [{"name": "frostray","stream": false}]}, + "heal": {"category": "player","sounds": [{"name": "heal","stream": false}]}, + "ice": {"category": "player","sounds": [{"name": "ice","stream": false}]}, + "largeaura": {"category": "player","sounds": [{"name": "largeaura","stream": false}]}, - "magic": {"category": "player","sounds": [{"name": "wizardry:magic1","stream": false}]}, + "magic": {"category": "player","sounds": [{"name": "magic1","stream": false}]}, - "sparkle": {"category": "player","sounds": [{"name": "wizardry:sparkle","stream": false}]}, - "wind": {"category": "player","sounds": [{"name": "wizardry:wind","stream": false}]}, - "rumble": {"category": "player","sounds": [{"name": "wizardry:rumble","stream": false}]} + "sparkle": {"category": "player","sounds": [{"name": "sparkle","stream": false}]}, + "wind": {"category": "player","sounds": [{"name": "wind","stream": false}]}, + "rumble": {"category": "player","sounds": [{"name": "rumble","stream": false}]} } \ No newline at end of file diff --git a/src/main/resources/assets/wizardry/sounds/force.ogg b/src/main/resources/assets/wizardry/sounds/force.ogg deleted file mode 100644 index 0da336b2c1af64d0fc37fa35ceaa1e3b18cc479b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 32548 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 UH|Q8ItD4N}!?=WxA>kAg04ZCes{jB1 diff --git a/src/main/resources/assets/wizardry/texts/handbook_en_GB.txt b/src/main/resources/assets/wizardry/texts/handbook_en_GB.txt index b2c9d08a..a5e68aec 100644 --- a/src/main/resources/assets/wizardry/texts/handbook_en_GB.txt +++ b/src/main/resources/assets/wizardry/texts/handbook_en_GB.txt @@ -199,7 +199,7 @@ Credits -------------------- Electroblob's Wizardry Version VERSION -For Minecraft 1.10.2 +For Minecraft 1.7.10 Designed, coded and textured by Electroblob @@ -212,6 +212,5 @@ In addition, I'd like to thank the following individuals for their contributions Translations: - Russian: VilagVil -- Spanish and Mexican Spanish: MadWrist Lightning ray sound effect from OhhWowProductions \ No newline at end of file diff --git a/src/main/resources/assets/wizardry/texts/handbook_en_US.txt b/src/main/resources/assets/wizardry/texts/handbook_en_US.txt index 95746130..d950d3f1 100644 --- a/src/main/resources/assets/wizardry/texts/handbook_en_US.txt +++ b/src/main/resources/assets/wizardry/texts/handbook_en_US.txt @@ -199,7 +199,7 @@ Credits -------------------- Electroblob's Wizardry Version VERSION -For Minecraft 1.10.2 +For Minecraft 1.7.10 Designed, coded and textured by Electroblob @@ -212,6 +212,5 @@ In addition, I'd like to thank the following individuals for their contributions Translations: - Russian: VilagVil -- Spanish and Mexican Spanish: MadWrist Lightning ray sound effect from OhhWowProductions \ No newline at end of file diff --git a/src/main/resources/assets/wizardry/texts/handbook_es_ES.txt b/src/main/resources/assets/wizardry/texts/handbook_es_ES.txt index 9747dc36..80549c4a 100644 --- a/src/main/resources/assets/wizardry/texts/handbook_es_ES.txt +++ b/src/main/resources/assets/wizardry/texts/handbook_es_ES.txt @@ -293,7 +293,7 @@ Creditos -------------------- Electroblob's Wizardry Version VERSION -Para Minecraft 1.10.2 +Para Minecraft 1.7.10 Disenado, codificado y texturizado por Electroblob @@ -306,6 +306,5 @@ In addition, I'd like to thank the following individuals for their contributions Translations: - Russian: VilagVil -- Spanish and Mexican Spanish: MadWrist Lightning ray sound effect from OhhWowProductions \ No newline at end of file diff --git a/src/main/resources/assets/wizardry/texts/handbook_es_MX.txt b/src/main/resources/assets/wizardry/texts/handbook_es_MX.txt index 9747dc36..80549c4a 100644 --- a/src/main/resources/assets/wizardry/texts/handbook_es_MX.txt +++ b/src/main/resources/assets/wizardry/texts/handbook_es_MX.txt @@ -293,7 +293,7 @@ Creditos -------------------- Electroblob's Wizardry Version VERSION -Para Minecraft 1.10.2 +Para Minecraft 1.7.10 Disenado, codificado y texturizado por Electroblob @@ -306,6 +306,5 @@ In addition, I'd like to thank the following individuals for their contributions Translations: - Russian: VilagVil -- Spanish and Mexican Spanish: MadWrist Lightning ray sound effect from OhhWowProductions \ No newline at end of file diff --git a/src/main/resources/assets/wizardry/texts/handbook_ru_RU.txt b/src/main/resources/assets/wizardry/texts/handbook_ru_RU.txt index 81d2fcb9..e85bfdbb 100644 --- a/src/main/resources/assets/wizardry/texts/handbook_ru_RU.txt +++ b/src/main/resources/assets/wizardry/texts/handbook_ru_RU.txt @@ -204,7 +204,7 @@ Credits ------------------------------ Electroblob's Wizardry Version VERSION -For Minecraft 1.10.2 +For Minecraft 1.7.10 Designed, coded and textured by Electroblob @@ -217,6 +217,5 @@ In addition, I'd like to thank the following individuals for their contributions Translations: - Russian: VilagVil -- Spanish and Mexican Spanish: MadWrist Lightning ray sound effect from OhhWowProductions \ No newline at end of file diff --git a/src/main/resources/assets/wizardry/textures/gui/arcane_workbench.png b/src/main/resources/assets/wizardry/textures/gui/arcane_workbench.png index 02a43a70761e37e0d575c8e9679d917ac8a5c33f..d73fb80320f2aa765b2957b7a0cf90b22e05a980 100644 GIT binary patch literal 5761 zcmeAS@N?(olHy`uVBq!ia0y~yU}OMc4mJh`hM1xiX$%YuY)RhkE(~)RTp0duSK0la zfq{Xuz$3Dlfr0NJ2s7@OnEjT4fq}im)7O>#A-9l_Jl8tK7#RizF+NWh$B>F!Z|COr z#2&3ZHa}AOTHcZF?Ke6*csg#FK6a6 zC+|$vGMUP5Z_73D@#B-XyOX^biKhcz-sUwY>OA?6u#=PqqsaP`go z{r2}JYvwVWxBqX$JLS-^Ug_yBOv)eamFixk3)Fu!lUnG>&?e5qz`)GFz{9{`z`$^T zfhbOf{OQc5S3ds!(|_Ih@x!8*UBY31_ue%VoTsriFn)3PVAts9?_d63Y;T{P^j^Pz zcSFPOy=OkaP^Iw47wLu+vg6htHbvE65-z6~VB~u_{0)uJ$&yP0u)T`oe%$vrPBI!HNN@X!q55p-V zqce}nzpja7F1Hgr=KN(x!DQy9b?0|nVvU!bw9;Wy4uety)82inr~67TEuZ?>m_2^I z{C}S?{ds0?6J$zMq!@S<+}F=n+p@(&QO_uT{ao?soZ~r_TZIl?SAjq zfw9V;BH1Q|F5hAl^RfYYZJe!c*U8NxlEMuO4I9=x-MY_lQ=Ndvz2}^l)=3LDG@3T-F|U|= zI_q-LmjEW8*^|zEw)A0OJI7FySU>OHMtM!4rxW-Z`X@Sy)PHh2Z`AbS`6}Zmc-R@K;i z#(eb}fzoqc#WO@3Cf147N2yG3pW&MS$7*q-(%!nb#~)ppa(&7dNd77lQ^=iS9diD? zu-A)EuVto|zm1=9wXA`0%f9dDt1hk2Th+B(^W)OofKytIK@I!58BVqQTj+FOi#cnd zyro{2*}j=f2NJdplt*ET!X~@>Ult_F?v`g{yWsKT{+C_fwp_01n&A+wG$ZA+4mdQ%&niz_5boMF$v}j*Z1?&h0h3Nx=!5nvrod?T1w~M>1jXDS*^3o z-OC`rxJBzf|J>l|hD%fO=ZgehyUD!oJb!XE6I(!qjDqeRKE=HG0X3DISBHPK?VXWs zqnvHFuUALHMf7prS5kjYS*5OcGpFX3;*!Wxj?%g7 zZZfd_H)sfRW7_w}$|WyGEoNPE|Npp{=J)06%GiP$uJy8tNUC#a{#x>*{N0_%oqswz zIn&?CTkSfzd!t4I!&;Hrd-u#;_~)82OxGz-N?LSjtFND5R$19K>sAS;IhE#oiDC^R z3rZ8~Zw6Q%d1vdpwy$`t0j7)4hm;TzU$?Dat?a%X*d{{jg zxjGlUx%E?PZ^h)`qEyz1GV71Kjs-F>uh40;>RFN#ap$$jiW6cF6>o7roXoUEkG13Z z=g@5*PQ8k({rrBfQR9{kHmmQ2ZWHFedCq|)pj*M|>#@r4FRcka`fKmyW~M7JaVWK<@Q0T!*?pqw#~RgLrBZPdpB_;3)8e{%u7Qz7 z-1VyTrc<2zeDzthikJdAKFn=(Si-Q@n{ksIE1zcFe1X|J-o;&=>E2}65Vo8lGqLiP z@7*uw4(=7&yMA4z82`_G(>WNJGeUms^EK_Zc^LLoEiX&fVTED)9^1-Wlc$|wGhn+g z%jAISnYdo@9cHg)%$RZE$&-{1**tp{Gp{EQdZtp_niBHzqUVMG?=FJ7y zL$3EY&wIgnaWc~uE7=pTRA29_e0^j8`s>~Kmu{T}xq8bnmP_Y0uQonZ@bswX!6`rG z?mH?}z7i;!qqur*qsW4b^Y-hyXSCW$ELo|ca8~5gGVTUOlWD)xc{5stnpUTOk$wKu zTGvANk6y7!#MRf{-8uqxY!_Z-D7;o%C$;a{&X1FuKF)m={I%eM75l4AelbpeBpOs$ z?|$F6&SSwz;SzUYi*D{sZ&8(HjbZ4z7pZu0x!a?)lcGVib(kB1Oi;8>GCvfqI z*X~q}m?b%7?qq(^py!nqno9~+xBMx5`1y%E+k!f?X0^NrPn1tp`aLSFO`CCx$Dl#s z;i~MGy!m#>_&KKWdH(t5+@he?(D!)&x`)@Dbo?z`D;^cJU0Ad#)1=HQzd!Q8W_JUSF8RjaM9t~)}P<>96nsQ&i3H;o=fJtV#`*)QEL@ibS`o^$Hm{t z3{oP(9&K+(X6VdFOMVl|lgr9$(#r&zRV{=V_o=SF@!h7MCToKocytbdW zFdlH|VVuG_xwf+ScC|~qNx-suU;a&yWNf+feV_QD4{t9#e&rhL-Qj#~vEId>+eC|F zqr;>bmPj4Aa%IZ0Wy`c`9bd>cewyVl@5A<&%nqw3Y+GWUZ7g(zf9G+AS@#4rQuyBp z`M-#}7~VeNkoNAw0*~4cv0Yf7%(O1R!k6W*0Pl}i)2A{i)O|2C>JWc=`{glpjqA@O za;w7Z>>OssJea&qN9yC@3$6_N0`5#V4!3=y{Jwm;?Zpw)P1OnlBxq^xoX! zy}tcsdTi6FUwvC-t3Su7AGjdVus+=9_gwv?6IMUfbBizRQI|-SEuXjTci@WLN~qIx6eKuTM;gO!|IRIbTJ%Nr3%u$kOo3CnOF`kZANjlDV0A-TvR-RCI*irRScG zO>qp2-CK7x^TDbwMN*X#l9JoFzxhr+>i+-po9okgPR6!3sK}rA`1DgGzuM1N$EE5I zFlf0kpKLV09vFG@g<^J(!s{PK(;H&|7T+u)1!vkwmsgPOlH7z7!3HgaCzVvWerpDe}LVkOa3q_I4FBEzL~_v0?s zuh}}c%sX$-fj);Rm)nXaSu?%8zrWs7ziry-!yutwMK=UH42f>-@-(P{8pa76iS7!g zGHWdZ%nygE|hQ3HqeAd@PP}h{{`BfPUVmlegG5^jbJQ%BUtjLEuIkOWzCZ2k`Rms%Z}=XW{eH?ByOZ};3MQWs+Pw4p4ORR4 zi?dX}RvhtrRem=l?kwe5>kudfTapY3_^O|Eqo- zeN86naW2d6mwg3i7hn51Z@zBC?~50gKH7RsYV-QfzgBsxdWyR(n!3c+D*yk*e+%u_ z=zCAnyRdZE`3PzAMvp)Ce|imd&*;CHbvM~+mwT91rpimCS{E02rWZ*;s=I%(?0iJUHr{@-lc%6{G}y1wd^-M@_+*X`&rycSh` zlK1}gnejD#zb;%ay%)9nki_NlXly)ykHTP8SEAv$ls@?N|*Z<7TowLnn9OX#(;v9c{rn$X-ZIM^i=g*UFF8_S( z*2MjW?{!vQ2>iP??^3MQs^~4D>2Gzvg>63k$K(2&xOC-**%w*9RH?t2f3^A6qS&fi zZ_FocIeO~cE625)^KVZ-qnLa~F8QtPw&3ZX9{G3GRaM>kx$oo0kE-vF@+{b^d0=gR zWUkcIz2D|sohJJJf9h$jo5|1D{QGp>%Kp6T#vi_ycDq}r8C-w)SM}uy?)6@;!%FS{ z?bW^h^1+_1@jHE#)8?PMZM}5$e%-wv&#hm=WVLvD>BhR1&%fCI-Sy8w{zB?5mvHY% za@8+fV>egNja91g{9OFms`f{Zzh;NX|9(mKO32^dvkBY zH@0L`^_LSDU+V1padz>w9NWrUar@Jz>cv;Lmd&+U`}+EtOMhR7y?hY)@&vc~m#O!6 zzB}Z)(Rko7~IE_EX~i37>p(`SwY#IR^4q z(%)V$@yqxArE9xPKYK6Vi;l1v*>Umd?&138pNl`=jjK3*Dm88W64_FuSO47cU~=1oUzSz9oDSOTjBkHkUt_uDSj5uvOgr5EKhL|D zuqA`ZJ-}2%eL2J8Oh-?vOukuP!@fDiDtP{q|GVCAR`cD+29G!I zU(P)0c+yraTTDUo-;4P0j`z~y1xwjxFl^~uGy6i?p6B8J^43o?)i19P|M5Ap{(jX1 zt=~Vsg=cBo>%J*p%9xo_doPt~z0Ggg+fPg$aI5>7S@Bxze!tUgIphA)C)S6z@-66a zym3D)-7LY+==`Z2N7kQK**oWULec-fW!k?#IfuXe_;&pa?e`Jc?&1Ae*KPOT^krT( zZNAvI_jh&{pR8WCY#Ep7rz=;csHv%WurGMeld$r~_SMF`KW3z;?#^KHKDyuW(&mR+ z5@#7~GUAM*`A@h#nm>Q`b-v_&X`%eC zBl{CS{%xEwfA;kkuL5H$k4IJC-MG2yNd2!5lEvS1lO3m@ReSz(^3OVrWY6Dz4c8eZ{d;FTTi_;tjYJ%0Iei+}M5~)U7fnR zPk;Znj7KT*`kG%k_x9X0F_k~h?0Dgqh=P!RoTK~u^G2O}_LfgPuku>Tym?RRwTWyS zSR~lecof!}_x@kIhEv3J!k$CtwKj_{w0vm)WiR6u*?rGV1?GQvJl}Ix=lU}#8gbV* zGqn7_5T4Ai?%occy!p>RuREdi>z&R0nKkoI`#f@csC9X(_<<)+RQB5Q$SmvlahYYt zo50CRJ^#;zPdl%0sYf|-zYWv*UCY$AvoL4)vP=m7<5{@t&}%FE_ePCdIu3l7{kWkf zE$!Gb=WkE=_J5SStNLrnhsV~O%5@og8AU|*>!tTfa@^z@@?&0%f~p-0~frCu~_*fd{Xm~qO^bxQ5cYQ8PeyiwTg>`I}aaX zU|`@Z@Q5sCVBk9l!i+m6X1`@%U|=ut^mS!_$jL1(B~kb_pr3(3@VTdpV@SoVw{!hF zRKjJC*PHUFISTo9csei%adx~*w_G14l*T`vKGUGThv08!8P!L^8}Oj_V!YLwNl24uUErgDsVqPH`iK} z^Uan29Ul+RIQV1s_ltb7Oa`$63=Av`3<3-c4h*1hBZi}3TlTW)-J0vK!{*OEn^q#H zz>sYIZCjhnCS3-W1cqjF$rn|7?S4jo-SsQyYt-M}>vZowW>jEcme_ap!Rz|<(mPK+ zxO#nA*{nJ23``fAw_lfB)bIGZZaJ6JWV6@%m9-dH5*+f^OD#IraW&w6>+8R}I2?V& zJGdAaUs#@5|NNucV)NFw+e8kin%F&G;Ks_qxFt3H)UoxS*Q`4<_aw(_#j3jnSDO~R zGu~Blu!Jv5Yr!^N2bK#5UccV6X06xy?BgnYYU|_w_nhv&oyC%MabgC800aN}d}EX7 zB?0C}ldpesRTok#{A3w^J)J>Ko5AXR#;>_&7r)Z_q{mn|ck)`^yOl4vCa+o`z{|jt z@bTBSf?Fd0%w^k(c|~`rG0s$Cxx4dKOJfFunm)s-#utamW8>~!d$IeG)wawKC-J#2 z)KnOK+!%5~tD_CiPLV6IEem!wS*mS!)O-2{#+&sJP{-xzTMT(E_kXSPksA5V-%GGv|6Q$AN2H4expHJ>L=)-6wa7A=i15-F#=3gx4$wM85s5IBs}J z<&>f-w8Fnx*xmz$}S%5BU@tcmLy!>dh=Jq%flN)nJyfgJ#*F5mC|jQ zeNMM$c62c0#BwEY*Uzqc8FDCP{^yS$zxfC>t?gpSVcq`AV5VmLHp$>%{+mk}ZZ5m4 z#;a+xcB?^wufC05B{2EeBrv~tZjOW z#TpoT`er1q*4w^KvOnrYcWO?{FN0H34Vp{dMlo}w3amSSTud!Nw3JiLLpa0s;*wtq0Pb&nYevn0s;eHQ9o-f26hlZ2zho!0d78_ivphNu{+J zMS06~kL5%+U)`HCJNaAT*=;|W99SlV{FAp@o~P`!YI}I^iLKuoUVaXKDo0P?T?tws9XLzU4hkOo!EsX zegc{@++$`K>$sGGiZC&wpN&A-!OaieSx1`cu)NIRF zeDOG@*0#Q97S{wO6Qize#m{$Fe4OE2l*_x}ZPBCJtAPxRFLHQlIXrJ~sCXf=;-t_+ z#b4Zq)mc6CSXVs%96RkpQDxZWAHM^X61Y#tKEIo4`EkNu9v6na5-u9~Z>$&VyC|;9 zW_hq#xIkn5c@GAr2`MZWj-T3U$0o8$O5RB5MfSbA_`f_$7#JK_CWK{J9TPe5eUB$& zLbeja^;cH0w?drbg3rkoOI+BJHvjFO;FswNOdLrZtJ+^jz05O`zh~;Y-Tz>=k*viL zKi7r7Bn28CxLy3(_GVLa{+mt*-!cX+!F|Sp9U=-lMGkz`DDZe}E}7KxTJF`HN)-u4 z4=dIUtxvbjw)%GJptI!Omoc?!{4@K!SsEBzOlvMLn&msM;B}Gk@mcHwtDZf0eKOZS z%1FwAS7D}!L;4KotK22rrDvWON9s*KxZdzf(}HzXOp_Y~8ZJKAot6K>!zFg>@~T)7 z*9+ONvX2~8uR8R?lW~$CdraJ>_p`V6fBoGyuO|7md8|D%(}W(58{am+Ha>LW>CsgO zw^;UNDhtdn=7>x$_-Z_X$>r;xl{pJVtCA-~1UC4J)LdqDU|96@-@2wS4d)A$ZkNmA zmfda2eONww?ZI7f^K~~J_#oJ@WHIA)-#ex?Uw3{CZ8m)S>T+rE1v~E2b+Xe9>R23H zR9@`*UhB$axc!1rrQh6zvs)Cm9sJ0m!1Tc6fnLb#d+jIo<{mCo%nMs_mA`aEz1-=?`Aam-&HzRdHjvN14EQ#oz}zR6|yx+QgheMbJQ}> zb=#>j!s6nV)rD=SC(e*%WX39a&9x3ZrG$< z4q330v1LYH&hB-~E;>wpUChao)WFaa#1i8skaf0ZcB6@Hn}77eYd^n>viHa{O6Y!- zzWL;<&+dQ6zWh>L#CUz{%xiiF9wc62N{HHXDdcY6?vOGcXU@WN>tb6z{NrfI>E>9W zxck?w?KiHag&e#daeLcqui}z<@z>d({0&GikJdY4s`cPEOJbMX#S%yV`F0F9x}+Rt z8{C{?zWkk_??s2%pO5`+HfG}3`R=bkTVm8g_vfyq!5z-?+~eKSqr!a3bJy81Y~VVO zwRO_ftg9~>7KXEY5@nqK!TcNBg4hkdFH3dZnm#s{^D2Dna!|D0aIV6s+V!$!i^-mC z+B}JWE<`XKixR%jddN&!!r3Ij%08T{;bU{>+}u4Hm%Yr@nlI`0O}w=E(^saYRtJxp z=}J9rKk3S_Ea1)*{+Jdwt7|^mw+XR&BXQ zl`zh5e`L))28 z*?;2Pm9ZN#XRL|)`#o~KKkv!X*$pfBpUfz#yx#1&>~nITy#j;mV#ci>mfhC!?v;x% zWxDH) zTihqG**nId!hyj}jYG{j@3xqO5tG3*(Z(PV)`pAR2Xf4k#l#MXZFUxCP+(waU|?Wi zVqoB4V8}5)!_1(}!>r6;kSM{X#){bhJl0hzVQ}P_z~`SaVXH;oUhsQeR&mE_5<^J5 zgZQ-5OV@;H-L+FJwhMiFA0j@x>QpP#$P$@{w_v@!8b%5=S0t@HEk`#A&{545;8 z#AO^PtWUkhp>XAxz!qm~XIG{f+v@8>|7)Kvx}5*&5ce7m7i-Nei`Q|KR8&-)>P|fU zHqlk#mghd%FqTDZU{9g=5Id)V;Q}kj|2_g?EQ{tox!w3L0cJ>-2qVaE5g*&ic5OS{ z<#2`Qwy*%hE>N?%f%8z8!wTL!)FoGmN?dhmQU8YT125*;K=-Z`b zz@x%&Rr~^L*xsdc*S!sxgDw2Ax5gb9UNC`-Q{!08blLCITZj4$S_VCQVXSZ?X2ks9 zPrM;IQRnm~qw~d9;t7ic9m)y{9P$rjEMSme+sf>{i^Z+#z0-9+o%i+Lo9ge}K0k4u z-qJtIJjxu+>^EP&fB*7Lw`w`tU#TX7RXZzU_kBOyeQ{rpb>9BaF7EX%*8jZM$4*=S z=S^qMsq6ngOz!tt%@!c}_vHEG_a?r16nl(+%6z-9KKs}I|9$`N{qu8S4B zQ2BQw|NS2`mp_^HXV0Ienvi|k{c_StJaymOl?4SYtDij%?E8Pdes6J+^05c4_J60n zo-eU`>hbkG@pXR%HNUU(__mclx99u5$L04b|9pP)Z2zB`F|Swt|2*C4lg;$z=4N5h z6PtWaZJKi4PXE)h=`QaozeR{_@|ia&`;&HM%)2T2r`ex=S1PhDlspi?RG{_c{%+4F zpG+sNzo+(e=ITkdCEY3cGSz=KD;<5jyti&|T(2hZw`6r40Rhl{b}7o|3+<%ckP<*ZnpXo2S(8(fV{WddZ5)-@7iJ zPcx2Oe_egHdD!}^-$h&l_Z5Ua+4ub0=KB5R$yx=j_a@wa5?kq7r>x}vVJ!pCG^Ut6 z-cNSC3;p+`>%>g{oR$ZlOi#+~dU|AXxLfS~m7%5;p#oRV$NmWkeE0r+`ghyyo~&l2 zjkljzf7)iFe}6*tQ%}L2hqf~r2v|S;n;-lC`o-VUkN>~?pJ(vzX3ny&@4nW*?uq{! z6Daa@_J7-2q58A$rky`+{HdX4Qa($<1&#+hI3n)-pOzy2eo~OxVun>8Z@U}_5x=mM zZSzf^Pv=Eazcv3|^JkmKH{FTir+rkkU7zF$KJ0X9ILNY~s?bPkfyDlEw^zHx9~b$z zI?(Z-Ltxs?uM?ZZPoF(4e_H=X7(<&_1J~8h-5ok&-NEl8*M{}-ZHii(VmJT3fZ&tL zxNGg)j0&3=UO1IaJo`#7HbQTDkVxycT!xOredm6^KJ-uEMNgcq8iR`v!`0v4j|v=T zXb@vsQ1yR8qSZosheb>R#v}BpX_~yh?LE8iYthGFCxa^ucRc9ZGGG8P!S|~Tn;D7q1@Xg-6wl{Czp1gSR;>rJCB`vUI z@2vQHr*ncE*8=X)-yHR`tKM&Gi_br<%Cs`p-rjl1E3Hn~)6TqRmk`SRcy^1>hVzpWR+sC%9dVSGf zqYGb)*`2Bc4;(z`IPH05B!A@9um4}m`IcW^ZC>~FR?45!$Jbh)o4sEz%oy#GVC813 zbN+Ko)xNrSb{@eDE-qr-u4NS!5wpzxU%2~WmE#I+&f7ivxtDPqNPA&-xT#c-qvr3O z$_XsiX8pS&bT(Dp%ZmCQe>VSWJ%8?>P{9prKVElT(Ej@Qx@MDXhODW3J}vpmYJDq* zH_TtR#_v?viOYM#)!EjG99X_!#WfbD82fUWC7KLskHdKmRu>1D&do?FP|Rj{m0s&E z9OHE1$uGkik0^#Kr*`(Ym*_qg&Cc5B&%0nP!;K>crr0s4^hcTAZ+pG*Dg%Qb{;;& zz`(#+;1OBOz`%D9gc)~C%zn$jz`$PO>Fdh=kds?jT;_UW7Z;}C>g2OVnsjloU*+aEnK^kTcAl{i>Dn2U+qH25+-Y1u7{idJ!J1U zxIcHk@tP+!GHaPG?9{R>7Pv2QSyHWP%MI(g2W7^GuXJcK?TGnwHhKHO6&y2Woo8Nl zY|D(GlYbhnhY24@?wWC{zVX%jU;Iz6&7Li_dA+2etCQ{m=G5Kx(>6Sq_HygLzBj)V z6GK?~H9PpOi1**p4y|0|ZQbAUcX|+mUA|h7{Yi$3eRa>T=b{;;& zz`(#+;1OBOz`%D9gc)~C%zn$jz`$PO>Fdh=kds?j(jX&+Ig5dTK{hiaqQp5rH#aq} zgaHJci&7IyQd1PlGfOfQ+&z5*QuI>U85kI9JY5_^Brd1+Z{%%u5OJQ*EF2Jc@!}Fs zu?0)d@H!mq*|J54!FWk>`w7h*&I^h^tJ=Rl-PV1l?&Op+pV@9Y`t4bg_a-X9^oM2z zv%bM5wbfUWEKhKLVC<6HCaG+H=V-b12G<|6`wbRs_EryOOy*g);N&jT?;FxpKbsz| Z`~1H@!Nf&f-(xSxfu62@F6*2UngGW+U(Nsk diff --git a/src/main/resources/assets/wizardry/textures/gui/element_icon_healing.png b/src/main/resources/assets/wizardry/textures/gui/element_icon_healing.png deleted file mode 100644 index 4225f1d2141edf94111aac7f6683fbc86c43074a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 312 zcmeAS@N?(olHy`uVBq!ia0y~yVBi2@4mJh`h9ms@x)~T4*pj^6T^Rl|7&0&>b{;;& zz`(#+;1OBOz`%D9gc)~C%zn$jz`$PO>Fdh=kds?jf+MoFYbyf-gKTCM)L!f zD+jAoR<*G3wy_*ub{;;& zz`(#+;1OBOz`%D9gc)~C%zn$jz`$PO>Fdh=kds?j*r=wp>kI<}gKTCjbrT2mZ0@lyx~5Z#a2^<3iUopOOWWo}T}j_$;wj$#sr) z+p@+jc?=8-GZik{UwizO%`sf*hF#`OKCQ?k%dZoz@oGgX&Hn9F;`M*hR{@5IZOxxk aRPIe+4_vr$f#@rcmponlT-G@yGywn$<#j>; diff --git a/src/main/resources/assets/wizardry/textures/gui/element_icon_lightning.png b/src/main/resources/assets/wizardry/textures/gui/element_icon_lightning.png deleted file mode 100644 index 287483d359f4ce1787412908cbacd393d394eb3d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 290 zcmeAS@N?(olHy`uVBq!ia0y~yVBi2@4mJh`h9ms@x)~T4*pj^6T^Rl|7&0&>b{;;& zz`(#+;1OBOz`%D9gc)~C%zn$jz`$PO>Fdh=kds?jgiWa6#XbfG2HDJzh!W@g+}zZ> z5(W@(E=o--Nlj5G&n(GMaQE~LNYP7WXJBAx^K@|xk+>Y&Z^*|ODB^m)( z7i&%m`4|cG7Q!92}sscIH)78&qol`;+0ERDQH2?qr diff --git a/src/main/resources/assets/wizardry/textures/gui/element_icon_necromancy.png b/src/main/resources/assets/wizardry/textures/gui/element_icon_necromancy.png deleted file mode 100644 index 38bcbc456967226fca972da52e474735c3b71aa4..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 338 zcmeAS@N?(olHy`uVBq!ia0y~yVBi2@4mJh`h9ms@x)~T4*pj^6T^Rl|7&0&>b{;;& zz`(#+;1OBOz`%D9gc)~C%zn$jz`$PO>Fdh=kds?jM2!FVk6R2346>Oa5hc#~xw)x% zB@7_oT$GwvlA5AWo>`Ki;O^-gkfN8$&cML1&C|s(MB;Mj8Ab2rM26OjpX(eC3p8)e z6l}Yg5iiKI?&PAA2}YfX2OmxO%axlXlO*tmRjr$cMNo&2XV)U0W2P$%2cZ9^Bk_o5#`AaNe}L7F+Xf z@70~{w(`N@$s5hf^>i{;ubjR8lX8hsc-hz9n5eQJ{;SVu^ew(RaVt-46??6_)VY|i jY27L%i$f>9eCZ~1-#p3NR3X0-b{;;& zz`(#+;1OBOz`%D9gc)~C%zn$jz`$PO>Fdh=kds?jSlHmrtVs+E46>Oa5hc#~xw)x% zB@7_oT$GwvlA5AWo>`Ki;O^-gkfN8$&cMJBmdKI;Vst0BJc@6#xJL diff --git a/src/main/resources/assets/wizardry/textures/gui/element_icon_sorcery.png b/src/main/resources/assets/wizardry/textures/gui/element_icon_sorcery.png deleted file mode 100644 index 3900024f690852924519a890d113a8227658db0c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 328 zcmeAS@N?(olHy`uVBq!ia0y~yVBi2@4mJh`h9ms@x)~T4*pj^6T^Rl|7&0&>b{;;& zz`(#+;1OBOz`%D9gc)~C%zn$jz`$PO>Fdh=kds?jT<6iVqSXux46>Oa5hc#~xw)x% zB@7_oT$GwvlA5AWo>`Ki;O^-gkfN8$&cML1#?!?yMB;MndBdzj1rlu^)t%02=yK>C zb^7;=XV*?n#)aO?J@gaUn1x<2PFfhZVDULVxl ZSp1!TwPv2l22GHMJYD@<);T3K0RTa@cz^%^ diff --git a/src/main/resources/assets/wizardry/textures/gui/empty_slot_crystal.png b/src/main/resources/assets/wizardry/textures/gui/empty_slot_crystal.png deleted file mode 100644 index a4893196d33d21e782b7d239302aa40d022dae10..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 240 zcmeAS@N?(olHy`uVBq!ia0y~yU=RRd4mJh`2Kmqb6B!s7*pj^6T^Rl|7&0&>b{;;& zz`(#+;1OBOz`%D9gc)~C%zn$jz`$PO>Fdh=kds?bTPXO$p@|F(46>Oa5hc#~xw)x% zB@7_oT$GwvlA5AWo>`Ki;O^-gkfN8$&cMJB;_2cTVsSb-LE_4RJ9qxrADwm5h$U9o z&UuE$S2gaPde)VnRFu0ITmEF_<^7Xi#u=&GCGI!R<|IdO3tvH}FpotL(-e*T33Dq7 iI*o-^&q$EqiDh7rVAtOFFz?wFkj0*^elF{r5}E)cwn&x$ diff --git a/src/main/resources/assets/wizardry/textures/gui/empty_slot_upgrade.png b/src/main/resources/assets/wizardry/textures/gui/empty_slot_upgrade.png deleted file mode 100644 index 22939c7e2ec9c8868ef3f4632d88e7581701ddb3..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 211 zcmeAS@N?(olHy`uVBq!ia0y~yU=RRd4mJh`2Kmqb6B!s7*pj^6T^Rl|7&0&>b{;;& zz`(#+;1OBOz`%D9gc)~C%zn$jz`$PO>Fdh=kds?bn`7N1>(>kn46>Oa5hc#~xw)x% zB@7_oT$GwvlA5AWo>`Ki;O^-gkfN8$&cMK6?&;zfVsSb-LE_$lJ9qxrADwm5h$U9o zv+Y4pkM|tG>5C?-`x^-fE$zopr E07|nxzyJUM diff --git a/src/main/resources/assets/wizardry/textures/gui/sixth_sense_overlay.png b/src/main/resources/assets/wizardry/textures/gui/sixth_sense_overlay.png index ba327477af346c517c676d092af9790d8f7d0452..8791893fddc792126cbf2ea208dc50ae1fc1d777 100644 GIT binary patch literal 45905 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 diff --git a/src/main/resources/mcmod.info b/src/main/resources/mcmod.info index 015fbd16..b11683b0 100644 --- a/src/main/resources/mcmod.info +++ b/src/main/resources/mcmod.info @@ -2,7 +2,7 @@ { "modid" : "wizardry", "name" : "Electroblob's Wizardry", - "version" : "2.1.2", + "version" : "1.1.5", "url" : "https://minecraft.curseforge.com/projects/electroblobs-wizardry", "credits" : "Made by Electroblob", "authors" : [