Compare commits

..

613 Commits

Author SHA1 Message Date
Electroblob77 32340191de Update version number 2021-02-01 16:49:30 +00:00
Electroblob77 0b09875bb3 Fix problems with combining spell modifiers 2021-02-01 16:41:31 +00:00
Electroblob77 078a36d304 Update The Wizard's Handbook 2021-01-31 20:34:57 +00:00
Electroblob77 3ef4ec522b Make mana flasks instant-use in creative mode 2021-01-31 16:17:18 +00:00
Electroblob77 cd6fa03990 Limit cooldowns to a minimum of 1 tick and replace armour cooldown reductions with correct values, fixes #603 2021-01-31 15:50:55 +00:00
Electroblob77 bba9fb68e6 Fix Hungarian handbook translation syntax error, fixes #610 2021-01-30 18:11:16 +00:00
Electroblob77 dcb7038833 Remove JSON generator code, fixes #615 2021-01-30 17:50:29 +00:00
Electroblob77 f8a058259d ItemArmor#getEquipmentSlot() is client-side only for some reason... fixes #608 2021-01-30 17:47:30 +00:00
Electroblob77 1696d5daeb Merge branch '1.12.2' of https://github.com/Electroblob77/Wizardry into 1.12.2 2021-01-30 17:46:25 +00:00
Electroblob77 ac69af012a Update Korean translations, courtesy of red1854th 2021-01-30 17:46:19 +00:00
Electroblob 692546a232 Merge pull request #606 from LWHK/armor-build
Update zh_cn.lang
2021-01-30 17:17:57 +00:00
TUsama b0478b627f some changes 2021-01-28 18:44:16 +08:00
Determancer 2cdca4b990 Merge pull request #1 from LWHK/Determancer-patch-1 2021-01-19 13:07:37 +08:00
Determancer 4c0b2adef4 武士 -> 巫师 2021-01-19 13:07:07 +08:00
Determancer 191dd078b8 Update src/main/resources/assets/ebwizardry/lang/zh_cn.lang
Co-authored-by: Clefal <1716976420@qq.com>
2021-01-19 13:04:35 +08:00
Determancer 2de7807bec Update src/main/resources/assets/ebwizardry/lang/zh_cn.lang
Co-authored-by: Clefal <1716976420@qq.com>
2021-01-19 13:04:13 +08:00
Determancer 9d4f1dd370 Translating All 2021-01-19 11:41:07 +08:00
Determancer 6f769150b1 Sync en_us.lang 2021-01-18 16:01:59 +08:00
TUsama 155dc0b621 update 2021-01-18 15:54:22 +08:00
Electroblob77 07acea53e3 A couple of tweaks for backwards compatiblity 2021-01-17 21:33:09 +00:00
Electroblob77 1fcb3a2b1b Reduce capacity of medium mana flask and reduce number of crystals required to craft it accordingly 2021-01-17 20:27:21 +00:00
Electroblob77 16eabc8deb Update version number 2021-01-17 20:23:28 +00:00
Electroblob77 e0efd8d407 Make mana flasks take time to use 2021-01-17 19:25:14 +00:00
Electroblob77 969f0b96fd Have custom large fireballs deal magic damage to ghasts in order to get around vanilla's hardcoded instanceof check, fixes #443 2021-01-17 17:40:09 +00:00
Electroblob77 8a9d88c9ac Guard against null item entities in LivingDropsEvent, fixes #464 2021-01-17 17:19:07 +00:00
Electroblob77 3b55728c75 Actually register font of mana with the event bus, fixes #477 2021-01-17 15:26:30 +00:00
Electroblob77 240bffa7ed Sometimes, I surprise even myself with my own stupidity... fixes #594
(This is literally why I have a helper method. To prevent exactly this error :/)
2021-01-17 15:14:23 +00:00
Electroblob77 c313236eec Disable weather forfeit when invoke weather is disabled, closes #554 2021-01-17 15:08:34 +00:00
Electroblob77 21f7928b9c Guard against null target in remant AI, fixes #600 2021-01-17 14:58:59 +00:00
Electroblob77 1304c11381 People don't listen to me, so let's spell it out to them 2021-01-17 14:41:21 +00:00
Electroblob77 79932e15ab Allow class armour to be imbued properly at the imbuement altar 2021-01-17 13:36:56 +00:00
Electroblob77 25ee166e41 Derp 2021-01-17 13:32:38 +00:00
Electroblob77 299510e663 Add armour classes!
- Added 3 new armour variants - the sage, the battlemage and the warlock - each in the 8 elemental varieties
- Added resplendent thread, crystal silver plating and ethereal crystalweave, used to upgrade regular armour into class armour
- Added the new armour upgrades to loot tables
- Added recipes for the new armour to JEI, along with info pages for the 3 upgrade items
- Added advancements for the new armour classes
- Removed legendary wizard armour
- Removed arcane seal of protection
- Removed the 'legendary' advancement
2021-01-16 23:02:03 +00:00
Electroblob77 d3d76cae16 Update Russian translations, courtesy of DrHesperus 2021-01-05 20:35:42 +00:00
Electroblob77 0e815c676d Fix conjured items not disappearing in offhand (actually fixes #493) 2021-01-04 00:18:37 +00:00
Electroblob77 f81ad670ed Change donation perk element sync to per-dimension and use EntityJoinWorldEvent instead of PlayerLoggedInEvent, fixes #541 2021-01-03 23:42:27 +00:00
Electroblob77 5b3aebaba1 Update version number 2021-01-03 23:40:57 +00:00
Electroblob77 3ef1afce2f Update credits 2021-01-03 23:27:53 +00:00
Electroblob77 a3c15d673b Set arcane lock data server-side and sync the tile entity properly, fixes #580 2021-01-03 23:18:20 +00:00
Electroblob77 d8f1c5f77c Merge branch '1.12.2' of https://github.com/Electroblob77/Wizardry into 1.12.2 2021-01-01 23:26:43 +00:00
Electroblob77 1ddcdafe50 Fix the utter mess that is spell property syncing (what was I thinking when I wrote this? and how did it ever work in the first place?!)
This fix is kinda cheating but I don't care, it works and it's certainly better than the mess that was there before
2021-01-01 23:26:24 +00:00
Electroblob 3c35eaaf54 Merge pull request #584 from SettingDust/bugfix/lighting-web-concurrent-modification
Try to fix lighting web concurrent modification
2020-12-21 16:39:15 +00:00
SettingDust 094ce5a427 Try to fix #513 #567
Try to fix  ConcurrentModificationException in LightningWeb
2020-12-21 22:38:15 +08:00
Electroblob77 e19d324dca Include offhand inventory in imbued item updating (and armour, while we're at it), fixes #493 2020-12-20 23:32:12 +00:00
Electroblob77 58f6d55b0d Add timeout when crystal flower charm's effect activates to prevent infinite looping when mod plants don't behave, fixes #488 2020-12-20 23:29:05 +00:00
Electroblob77 32e92b4b04 Guard against null element in writeEntityToNBT for wizards and evil wizards, fixes #491 2020-12-20 23:24:30 +00:00
Electroblob77 24a06e0bac Change dire wolf pelt's effect from different marker colours to different marker shapes 2020-12-20 23:12:27 +00:00
Electroblob77 ae9da5b7c1 Query the position using the block at that position, not the one below... 🤦Fixes #519. 2020-12-20 19:13:41 +00:00
Electroblob77 2e39a8bfc1 Remove spaces around hyperlinks in Chinese handbook translation file, fixes #527 2020-12-20 19:02:34 +00:00
Electroblob77 e3130ca936 Cleanup 2020-12-20 18:57:23 +00:00
Electroblob 378e19a614 Add translation tips for languages without spaces 2020-12-20 18:45:27 +00:00
Electroblob77 e2fd978d8b Catch NPEs when accessing IBakedModel#getOverrides from ModelBakeEvent, fixes #537 2020-12-20 17:07:58 +00:00
Electroblob77 8f8ce7a453 Populate model lists in PlayerAnimator when a new player renderer is found, fixes #551
Also adds the missing null check to RenderPlayerPostEvent, just in case
2020-12-20 14:45:20 +00:00
Electroblob77 ddfffd3d31 Guard against IOOBEs when rendering the new spell trade icon, fixes #565 2020-12-20 00:16:17 +00:00
Electroblob77 d23c15476a Guard against null spell HUD skins in all places, fixes #566 2020-12-20 00:10:16 +00:00
Electroblob77 55bb40a684 Copy contents of imbuement altar to new tile entity when activated or deactivated, fixes #576 2020-12-20 00:02:10 +00:00
Electroblob77 c056c490da Update credits 2020-12-19 23:47:46 +00:00
Electroblob77 c483591ea8 Merge branch '1.12.2' of https://github.com/Electroblob77/Wizardry into 1.12.2
# Conflicts:
#	src/main/resources/assets/ebwizardry/lang/ru_ru.lang
2020-12-19 23:41:22 +00:00
Electroblob77 4c3dfb3bc9 Update Russian translations, courtesy of DrHesperus 2020-12-19 23:34:02 +00:00
Electroblob77 0010fafc13 Update German translations, courtesy of Lemopav 2020-12-19 23:25:38 +00:00
Electroblob77 f4cf98089c New contributors 2020-12-19 23:24:38 +00:00
Electroblob77 8e3a33b084 Update Korean translations, courtesy of red1854th 2020-12-19 23:24:26 +00:00
Electroblob b1478de6fe Merge pull request #577 from Aralu115/Fix-Layer-Issue-LycaniteMobs
Re-implement layer fix from Pull Request 312
2020-12-18 16:23:01 +00:00
Aralu 1d247e2d56 Re-implment layer fix from Pull Request 312 2020-12-17 11:39:50 -06:00
Electroblob e8292f1e96 Merge pull request #574 from LWHK/TC
Fix uncorrect file path and add untranslated content to zh_tw.json
2020-12-13 12:00:22 +00:00
TUsama bc0b712e2b add untranslated content
2
2020-12-13 16:33:46 +08:00
TUsama 47135f00d7 fix the file path error 2020-12-13 16:21:53 +08:00
Electroblob c615ad365c Merge pull request #568 from LWHK/TC
Rollback the zh_tw.json to the last UTF-8 code version due to the bug fix
2020-12-11 11:56:30 +00:00
Electroblob 39c3989796 Merge pull request #571 from WinDanesz/patch-1
Update ru_ru.lang fixing mana info on the arcane workbench
2020-12-11 11:55:32 +00:00
WinDanesz e808ab2b1f Update ru_ru.lang fixing mana info on the arcane workbench 2020-12-11 00:37:17 +01:00
TUsama a25f942722 Rollback the zh_tw to the last UTF-8 code version because of the bug fix 2020-12-09 10:16:43 +08:00
Electroblob 14e481a71d Merge pull request #543 from Determancer/patch-3
New Chinese Simplified Translation for Imbuement
2020-11-05 11:39:07 +00:00
Determancer ac226f1444 an untranslated one, oops 2020-11-05 10:49:31 +08:00
Determancer 741d918d9b another tweak 2020-11-05 10:46:53 +08:00
Determancer f504505bf5 New Translation for Imbuement
Changing the current translation based on the latest discussion.
2020-11-05 10:44:49 +08:00
Electroblob77 fb010d3726 Update version number 2020-11-03 01:36:16 +00:00
Electroblob77 c64731e7a9 Fix ClassCastException in player animations, fixes #533
Somehow some unwrapped model parts managed to slip through (exactly *how* is a mystery!) so we're just safety-checking everything, I doubt this incurs much performance cost in the scheme of things.
2020-11-03 01:33:22 +00:00
Electroblob77 d3837a6185 Update credits 2020-11-03 01:20:16 +00:00
Electroblob77 80cf77ecc6 Fix GL stack overflow / weird rendering when arrows are stuck in the player, fixes #529 and #530 2020-11-03 01:08:14 +00:00
Electroblob77 3d3806c20b Prevent receptacles dropping on neighbour change when attached to an imbuement altar, fixes #520 2020-11-03 01:08:11 +00:00
Electroblob77 d952e9b255 Update Polish translations, courtesy of Olej 2020-11-01 15:06:49 +00:00
Electroblob77 33c8f08adf Update spanish handbook translations, courtesy of Alsentar 2020-11-01 10:41:45 +00:00
Electroblob77 315f2e4b02 Reinstate missing null check in SpellRay, fixes part of #519 2020-10-28 23:43:08 +00:00
Electroblob77 d3df59df73 Have PlayerAnimator ignore renderers for which no layers/models are stored, fixes #518
This will stop the crashing but might well mean broken animations - for now there's not much I can do about that
2020-10-28 23:37:51 +00:00
Electroblob77 cfd3e796e1 Make PlayerAnimator disable itself when MoBends is installed 2020-10-28 23:27:04 +00:00
Electroblob77 d97e843f7b Guard against all possible nulls to be on the safe side, fixes #517 2020-10-28 23:20:48 +00:00
Electroblob77 15a153fe11 Merge branch '1.12.2' of https://github.com/Electroblob77/Wizardry into 1.12.2 2020-10-28 23:20:13 +00:00
Electroblob77 d00b7279ee Update Hungarian translations, courtesy of Bombadil 2020-10-28 23:20:06 +00:00
Electroblob77 0142cc05bc Update Korean translations, courtesy of 방통 2020-10-28 23:19:47 +00:00
Electroblob 23621b8045 Merge pull request #526 from LWHK/1.12.2
Update CHS  translation version to 4.3
2020-10-26 23:26:18 +00:00
TUsama 7f2978bc7a Enhance some details
Co-Authored-By: Determancer <49300241+Determancer@users.noreply.github.com>
Co-Authored-By: WuzgXY <62000315+WuzgXY-GitHub@users.noreply.github.com>
2020-10-26 23:08:26 +08:00
TUsama 312364ba77 Update CHS translation version to 4.3
Co-Authored-By: Determancer <49300241+Determancer@users.noreply.github.com>
Co-Authored-By: Hokorizero <50108932+Hokorizero@users.noreply.github.com>
2020-10-26 09:50:21 +08:00
Electroblob77 3b4ce68606 Third time lucky 2020-10-23 15:55:03 +01:00
Electroblob77 164e9dbe36 And that one 2020-10-23 15:48:59 +01:00
Electroblob77 2a21eb4392 Fix NPE in donor perk packet 2020-10-23 15:38:29 +01:00
Electroblob77 97061f7a32 Merge branch '4.3-dev' into 1.12.2 2020-10-23 15:14:53 +01:00
Electroblob77 7f84f3daef Temporary fix for lag in imbuement updating, ultimately this needs a rewrite but *effort*... 2020-10-23 15:09:19 +01:00
Electroblob77 9f3e09498d Hopefully fixes #469 2020-10-23 15:06:41 +01:00
Electroblob77 55bcd92860 Replace dummy criteria with proper generic imbuement altar criterion 2020-10-23 14:48:02 +01:00
Electroblob77 1030a2a4f3 Disable this for now, it's not a big deal 2020-10-23 14:34:21 +01:00
Electroblob77 bc407187e1 This works now 2020-10-23 14:13:35 +01:00
Electroblob77 62c7b6b7bb Fix imbuement altar not dropping its contents 2020-10-23 14:13:26 +01:00
Electroblob77 35ac115e8b Change imbuement altar face shapes back to how they should be and give receptacles a special case for attachment 2020-10-23 14:13:09 +01:00
Electroblob77 f99f8c271e Add the rest of the new spells to the advancements 2020-10-23 13:23:32 +01:00
Electroblob77 21dabc9cfc Probably should be this one 2020-10-23 13:07:02 +01:00
Electroblob77 77d9bfc772 Blast modifier support for permafrost, plus various fixes and an attempt at slipperiness 2020-10-23 13:06:54 +01:00
Electroblob77 29617451d8 Add permafrost spell and fix dry frosted ice not being very icy 2020-10-23 01:22:28 +01:00
Electroblob77 e530695f54 Give conjured items a blue timer (durability) bar 2020-10-22 23:50:49 +01:00
Electroblob77 1f4de65356 Cleanup 2020-10-21 22:46:30 +01:00
Electroblob77 9dc186abf4 Fix the charge meter showing when holding a wand in the offhand but using the main hand item 2020-10-21 22:41:26 +01:00
Electroblob77 086d9f6af6 A few tweaks and fixes for flamecatcher 2020-10-21 22:37:53 +01:00
Electroblob77 7e658d74b5 Probably shouldn't be rendering the ends of this 2020-10-21 22:24:07 +01:00
Electroblob77 2bb242e6fb Add flamecatcher spell, and addItemExtras hook for SpellConjuration 2020-10-21 22:23:54 +01:00
Electroblob77 d85441867e Remove knockback from celestial smite 2020-10-21 21:20:18 +01:00
Electroblob77 38aa80d60a That didn't work :( 2020-10-20 00:22:17 +01:00
Electroblob77 5e35e53eb3 Update artefact advancements with new artefacts 2020-10-20 00:14:03 +01:00
Electroblob77 f41effd44f Add config option to change progression requirements 2020-10-19 23:52:27 +01:00
Electroblob77 17fad8b33c Reduce progression requirement for novice tier 2020-10-19 23:37:36 +01:00
Electroblob77 1c96c88082 Remove shadow from players under the effects of mirage 2020-10-19 23:37:12 +01:00
Electroblob77 2b884d0aea Add localisation for mark of sacrifice effect 2020-10-18 22:12:27 +01:00
Electroblob77 89345d4fbc Add mirage spell 2020-10-18 22:12:14 +01:00
Electroblob77 cf8de05d1b Make celestial smite ignore mobs when raytracing 2020-10-18 20:37:43 +01:00
Electroblob77 f9432c7c05 Add celestial smite spell 2020-10-18 01:17:31 +01:00
Electroblob77 d6f30d6519 Change guardian beam to use a single sound event and time it with the damage 2020-10-18 00:56:30 +01:00
Electroblob77 eb864338b4 Bring range for lightning bolt in line with other 'attack from above' spells 2020-10-18 00:24:10 +01:00
Electroblob77 bf13491987 A healthy splash of optimise imports 2020-10-18 00:12:29 +01:00
Electroblob77 0dd26de905 Add guardian beam spell 2020-10-17 15:28:51 +01:00
Electroblob77 795bdcd427 Fix player animations being applied to all instances of the model 2020-10-17 13:02:09 +01:00
Electroblob77 6f57d2366e Fix healing totem beam origin for healing allies 2020-10-16 19:43:13 +01:00
Electroblob77 8b86bb1b31 Add Lokenzen's ring 2020-10-16 19:42:56 +01:00
Electroblob77 0db343c959 Add enrage spell 2020-10-10 19:15:15 +01:00
Electroblob77 f5cd740497 Add amulet of esundor, plus fixes for frost barrier 2020-10-10 18:30:54 +01:00
Electroblob77 b6c67ddf34 Add ring of volthraa 2020-10-06 00:34:19 +01:00
Electroblob77 c51b08c827 Goddamnit you yanks 2020-10-03 00:55:32 +01:00
Electroblob77 970894ea21 Comments and TODOs 2020-10-03 00:54:29 +01:00
Electroblob77 d5d933b4e6 Add dire wolf pelt 2020-10-03 00:54:20 +01:00
Electroblob77 7e32263946 These should stack to 16 now or it'll be a bit too easy to take loads of mana with you 2020-09-30 19:21:24 +01:00
Electroblob77 543cc22c44 Nerf mark sacrifice 2020-09-30 19:19:47 +01:00
Electroblob77 c86cd88a57 Improvements to shield rendering, now with trippy animations! 2020-09-30 18:37:49 +01:00
Electroblob77 ac5b3466e7 A few little optimisations for PlayerAnimator 2020-09-30 17:05:57 +01:00
Electroblob77 3cdd564228 Allow frost barrier to be cast by dispensers 2020-09-27 22:02:24 +01:00
Electroblob77 a5fb3c6ac5 Send packets for player motion 2020-09-27 17:54:13 +01:00
Electroblob77 3f36f933ff Only display search tips on the spells tab! 2020-09-27 17:49:46 +01:00
Electroblob77 22b284063b Should be server-side only 2020-09-27 17:49:24 +01:00
Electroblob77 f32bcae8d3 Better particle interaction for custom hitboxes (now they can't pass through thin stuff) 2020-09-27 17:49:11 +01:00
Electroblob77 c05f4001fd Add frost barrier spell 2020-09-27 17:48:37 +01:00
Electroblob77 a72ac33768 Add a bit of aim assist to telekinesis to help with item entity hitboxes being weird 2020-09-25 17:53:29 +01:00
Electroblob77 c53c9b1793 Fix decay not saving properly 2020-09-24 18:56:35 +01:00
Electroblob77 c56128a7e2 Don't rely on class load order, do this properly during the init phase (and tell addon devs when to do theirs) 2020-09-24 18:47:41 +01:00
Electroblob77 b4e9d86d1d Make replaceVanillaFallDamae a synchronised setting, should fix the problems people were having on servers where turning the option off seemed to do nothing 2020-09-24 18:46:43 +01:00
Electroblob77 aac931f6b2 Clean up a bunch of TODOs that are no longer relevant 2020-09-23 23:49:36 +01:00
Electroblob77 ed9e9110e5 Remove all references to WandHelper from GuiSpellDisplay and abstract them behind ISpellCastingItem 2020-09-23 23:22:11 +01:00
Electroblob77 c4194e2b76 Fix being able to use master tomes on master wands 2020-09-23 14:05:00 +01:00
Electroblob77 84236093c8 Plug spell property value into black hole radius calculations 2020-09-23 14:04:23 +01:00
Electroblob77 77f698397a Add 2 new spell HUD skins 2020-09-23 14:04:03 +01:00
Electroblob77 b7329299f9 Make dispensers trigger break events properly too 2020-09-23 01:28:42 +01:00
Electroblob77 127723a1e7 Finish the job properly, EB 2020-09-23 01:02:37 +01:00
Electroblob77 4a7b8a2bb9 Various fixes for arcane lock 2020-09-23 00:59:46 +01:00
Electroblob77 2f91573284 Block protection compat overhaul
- Unifies all block protection checks into helper methods in BlockUtils which trigger all the necessary forge events and vanilla methods
- Implements these checks in all the appropriate places and removes calls to EntityUtils#canDamageBlocks(...) where they are no longer needed
- Changes the mine spell to respect the config settings in line with the rest of the spells
- Fixes earthquake not respecting the settings at all
- Adds a separate config option for dispenser block damage and updates the description for the existing settings to reflect the changes
2020-09-23 00:57:55 +01:00
Electroblob77 f49ef7594d Evade is now 140% less cheesable 2020-09-21 22:20:44 +01:00
Electroblob77 0022de499d Altar the weighting a bit :P 2020-09-21 18:15:44 +01:00
Electroblob77 359da9a688 Not that one 2020-09-21 16:59:34 +01:00
Electroblob77 1a636c8c40 Ugh really, Forge? 2020-09-21 16:54:21 +01:00
Electroblob77 2c95793293 Have the elements of spectral dust in the receptacles affect the element weighting for the resulting spell book 2020-09-21 16:43:51 +01:00
Electroblob77 93d2100546 Every day I'm shufflin' 2020-09-21 14:02:49 +01:00
Electroblob77 eeb408c68c Add imbuement altar 'recipes' to JEI 2020-09-21 13:55:55 +01:00
Electroblob77 d5ed705f22 Defer loading of TileEntityImbuementAltar#lastUser until after the world is set 2020-09-21 13:36:40 +01:00
Electroblob77 adf07a7c98 Tweak arcane workbench jei background to match vanilla output slots 2020-09-14 16:11:17 +01:00
Electroblob77 ea4e07b2c9 Add ability to restore ruined spell books with the imbuement altar
For now this ignores dust elements but may change this in future
2020-09-14 15:37:05 +01:00
Electroblob77 ac7e2fda94 Fix wizard armour charging always using 100 mana regardless of shards/grand crystals 2020-09-14 15:33:59 +01:00
Electroblob77 8690d406f3 Make spell enabled default to true, fixes the none spell displaying the disabled in config message 2020-09-14 15:12:57 +01:00
Electroblob77 ebcfd3963d Remove all references to Minecraft.getMinecraft().player from RenderPlayerEvent receivers because some mods render players in the main menu, fixes #497
(Actually, it probably doesn't matter because all the references were moved inside the situational checks, but it's better to use the event parameters anyway)
2020-09-12 14:16:59 +01:00
Electroblob77 ea9c9edff1 Add CME fix from remove curse to cure effects, fixes #489 2020-09-12 14:09:31 +01:00
Electroblob77 2e49f1d06b Replace some spell books dropped by mobs and found in library ruins with ruined spell books 2020-09-09 23:22:56 +01:00
Electroblob77 0981768c72 Drop a few more ruined spell books into the sea, and make them stack to 16 2020-09-09 23:13:33 +01:00
Electroblob77 70a55d1175 Chuck more ruined spell books into the ocean, and fish all the master spells back out 2020-09-09 22:40:55 +01:00
Electroblob77 7ae041595a Gone fishin' 🐟🐠🐡
Add spell books and ruined spell books to treasure and junk fishing loot respectively
2020-09-09 22:36:07 +01:00
Electroblob77 ac14c8f931 Add ruined spell book item (usage will be added later) 2020-09-06 23:24:36 +01:00
Electroblob77 e147ce5d37 Intimidate should be an alteration spell 2020-09-06 18:32:45 +01:00
Electroblob77 472727552e Fix CME in remove curse 2020-09-06 18:23:41 +01:00
Electroblob77 373ab0c3dc Handy as the search tips are, maybe we shouldn't show them *everywhere*... 2020-08-30 00:26:20 +01:00
Electroblob77 5bca261582 Add tip-top tooltip with top tips for snazzy super-speedy spell searching system
And confusingly convoluted commit comments
2020-08-30 00:06:01 +01:00
Electroblob77 99f71874c0 Various fixes for artefact loot tables 2020-08-28 23:51:55 +01:00
Electroblob77 1c8011ff74 It never fails to amaze me how many things that should never be null somehow end up being null for one single user... 2020-08-24 21:22:10 +01:00
Electroblob77 f9cd90bd49 Better layout 2020-08-22 00:01:11 +01:00
Electroblob77 988596075c More updates to the handbook 2020-08-21 23:53:55 +01:00
Electroblob77 2b27729062 Hey lectern, stop doing that, it's really annoying 2020-08-21 23:27:57 +01:00
Electroblob77 dac40ecf98 Extend mind control overlay effect to possessed creatures 2020-08-21 23:24:18 +01:00
Electroblob77 c48b4ea0fe Shorten description for withering totem 2020-08-21 23:23:17 +01:00
Electroblob77 7380da7b5e Update wizard's handbook and tidy up embedded image naming convention 2020-08-21 00:34:04 +01:00
Electroblob77 815bcf176c Retexture forest of thorns icon 2020-08-21 00:08:47 +01:00
Electroblob77 ba487bea41 Add withering totem spell 2020-08-21 00:08:31 +01:00
Electroblob77 6cdff07180 Derp 2020-08-16 00:21:39 +01:00
Electroblob77 87705f7e4b Add localisations for radiant totem entity 2020-08-15 23:58:17 +01:00
Electroblob77 c90ec248cd Still need to check this, since not being allied and being a valid target are not necessarily equivalent 2020-08-15 23:21:32 +01:00
Electroblob77 0c046e8c29 Add elemental crystal and crystal block recipes to imbuement altar 2020-08-15 23:09:57 +01:00
Electroblob77 b7a503e5c4 Add sound for mark sacrifice 2020-08-13 22:45:57 +01:00
Electroblob77 aaf5f1ff71 Automatic item model registry, because I'm fed up with registering all my items twice 2020-08-13 22:23:58 +01:00
Electroblob77 a5dd5ce9df Add evoker's ring 2020-08-09 23:23:12 +01:00
Electroblob77 b08e5e1d96 Add chapter on library ruins to wizards handbook 2020-08-09 22:47:41 +01:00
Electroblob77 b6bda04a7a Better advancement description 2020-08-08 22:02:54 +01:00
Electroblob77 1cf5808f4f So many things to fix... 2020-08-03 22:47:26 +01:00
Electroblob77 7401a8e3b4 First pass trying to optimise LivingUpdateEvent handlers, see issue #484 2020-08-03 22:47:08 +01:00
Electroblob c3ca12edb9 Merge pull request #483 from Determancer/patch-1
Fix an error in en_us and sync it to zh_cn
2020-08-03 22:00:28 +01:00
Electroblob77 9d8b0b3a90 Add advancements for library update features 2020-08-01 23:24:33 +01:00
Determancer 0a75e34f3f Update en_us.lang 2020-08-01 20:49:11 +08:00
Determancer 2f0fd9fd7b Update zh_cn.lang
error fix sync
2020-08-01 20:30:08 +08:00
Electroblob77 0d7d36c0b5 Add mark sacrifice spell 2020-07-30 22:54:37 +01:00
Electroblob77 a321e4ef66 Add Russian handbook translations, courtesy of MugGod2 2020-07-30 21:55:44 +01:00
Electroblob77 041f54352c Forgot that bit :/ 2020-07-30 21:54:55 +01:00
Electroblob77 09cc0a5c34 Actually make ward do something... oops :0 2020-07-27 23:49:20 +01:00
Electroblob 1e4117fcab Update CONTRIBUTING.md 2020-07-27 14:12:00 +01:00
Electroblob cfb537dc0f Merge pull request #479 from xinyuan-liu/1.12.2
USE UTF-8 TO DECODE EVERYTHING AS ANY OTHER MODS DOES!
2020-07-27 13:33:58 +01:00
Electroblob77 e5e01dca88 Client event handlers should be specified as such! 2020-07-27 13:10:05 +01:00
Electroblob77 eba219bcce Add getter for SpellBuff#potionSet 2020-07-27 12:32:05 +01:00
xinyuan liu 8632d71ed6 One more thing 2020-07-27 18:50:56 +08:00
xinyuan liu 5e0d096ec0 USE UTF-8 instead of gbk. 2020-07-27 17:24:21 +08:00
xinyuan liu 7c463c9eb9 Use UTF-8 too. 2020-07-27 17:15:14 +08:00
xinyuan liu cd83bbb67d Use UTF-8 to decode EVERYTHING! 2020-07-27 17:13:23 +08:00
Electroblob77 476b7a6a45 Add more users to donation perks 2020-07-23 23:03:22 +01:00
Electroblob77 e4290598d5 Tweak colours 2020-07-19 16:07:54 +01:00
Electroblob77 3343385a7c Add donor perks and tweak generalise rendering of healing totem 2020-07-19 15:56:07 +01:00
Electroblob77 a831c2ada3 Separate surface and underground library marker options 2020-07-15 00:30:08 +01:00
Electroblob77 a945201cec Use the same test in the new spell icon as the new spell book tooltip 2020-07-15 00:21:37 +01:00
Electroblob77 2e3df272ec Eliminate ALL cascading worldgen lag, with the following changes:
- Offset structures by minus half their width in x/z (as well as the usual +8), so they can't ever spill out of the 2x2 chunk area being generated (provided they are less than 16 blocks wide, which mine are)
- Pass the block placement flags 16 | 2 to Template#addBlocksToWorld instead of just 2, to prevent neighbour updates
- Ignore unloaded blocks when removing floating trees (in theory there can't be trees there anyway)
2020-07-13 21:38:37 +01:00
Electroblob77 ded4caca2f Tweak bookshelf model texture for special wand upgrades 2020-07-13 16:54:48 +01:00
Electroblob77 a12fc6e344 TODOs and cleanup 2020-07-13 16:54:13 +01:00
Electroblob77 fa0875e63b Add library ruins
- Adds two variants of library ruins: surface (generates in forested biomes) and underground (generates everywhere)
- Adds loot table support to bookshelves
- Adds antique atlas markers for library ruins
- Adds bookshelves and lecterns to the wood type template processor
- Makes bookshelves and lecterns rotate correctly in structure templates
- Generalises most of the methods in WorldGenSurfaceStructure to a new superclass WorldGenWizardryStructure
2020-07-13 16:51:01 +01:00
Electroblob77 05d33ec58e Fix broken encoding for traditional Chinese translations courtesy of alan 67160, fixes #440 2020-07-09 18:29:29 +01:00
Electroblob77 7013cffeaa Allow scrolls to be placed in bookshelves and add in-world textures for them 2020-07-07 00:19:36 +01:00
Electroblob77 a1a5af034d Make owned entities inherit their owner's allies (as well as their own - if, for example, you summon a mob that then summons its own mob, that mob will no longer attack you) 2020-07-06 23:02:03 +01:00
Electroblob77 9aac386ec9 Last few tweaks to make bookshelf loot generation work properly 2020-07-06 22:54:10 +01:00
Electroblob77 651eb8834b Make sure ParticleBuilder warnings only trigger when appropriate, plus misc cleanup 2020-07-06 22:53:10 +01:00
Electroblob77 48221582c3 Move archivist's eyeglass code inside null check, fixes NPE when generating loot 2020-07-06 17:47:03 +01:00
Electroblob77 e04e1e70af Change TileEntityBookshelf to extend TileEntityLockableLoot, allowing it to generate loot from a loot table when opened 2020-07-06 17:46:14 +01:00
Electroblob77 a78a4147db Fix summon animation being stuck on frame 0 for permanent summoned creatures 2020-07-06 15:18:10 +01:00
Electroblob77 657a014cae Boulder sound and credits 2020-07-06 12:09:56 +01:00
Electroblob77 de7226f792 Various tweaks to boulder 2020-07-05 15:46:49 +01:00
Electroblob77 22a125d720 Add boulder spell 2020-07-05 00:39:06 +01:00
Electroblob77 8ae6bc9102 Tidy up entity geometry stuff:
- Add a getCentre method for entities to GeometryUtils
- Replace getPositionEyes(0) with getPositionEyes(1) since it's more efficient, and by definition they must be the same
- Remove all the silly getEntityBoundingBox().minY stuff, this bug has been fixed since 1.8! (which begs the question: if it was considered a bug, how on earth did it go unfixed for that long?)
- A few other small things
2020-07-04 13:34:11 +01:00
Electroblob77 3f19e89b91 Add bonus special upgrade slots to non-elemental wands 2020-07-03 12:30:40 +01:00
Electroblob77 399c3017ad Downgrade talisman of transformation to uncommon 2020-07-03 12:05:14 +01:00
Electroblob77 183d5a2416 Add demonic seal 2020-07-03 12:04:48 +01:00
Electroblob77 ddd67c6034 Defaults should be true 2020-07-02 23:47:44 +01:00
Electroblob77 202552a967 Add cursed helm 2020-07-02 23:47:36 +01:00
Electroblob77 9d3439b25c Rename no icon skin to compact 2020-07-02 21:51:39 +01:00
Electroblob77 b6ccee010b Add config options to show/hide spell HUD and charge meter 2020-07-02 17:28:02 +01:00
Electroblob77 02ab4a9014 Make all radiant damage set fire to undead entities 2020-06-30 16:10:46 +01:00
Electroblob77 429f74393e Fix sixth sense colour 2020-06-30 15:53:25 +01:00
Electroblob77 a547b3a384 Fix spirit horse dispel sound/behaviour 2020-06-30 15:53:15 +01:00
Electroblob77 8e2b367e94 More mind control fixes 2020-06-30 15:38:00 +01:00
Electroblob77 ea6d338d71 Fix a stack overflow 2020-06-30 15:25:21 +01:00
Electroblob77 c2396bcd5e Fix NPE 2020-06-30 15:17:35 +01:00
Electroblob77 4e0b63f06b Apply lightning bolt changes to thunderstorm 2020-06-30 15:12:37 +01:00
Electroblob77 323b1e1799 Make fangs ignore allies 2020-06-30 15:11:54 +01:00
Electroblob77 148f527504 Damage modifier support for vanilla entities (throwables, shulker bullet and fangs) 2020-06-30 15:11:27 +01:00
Electroblob77 6a8951ab39 Event priority 2020-06-30 14:06:41 +01:00
Electroblob77 b76374fb58 Make lightning bolt count as player damage and support damage modifiers 2020-06-30 14:06:27 +01:00
Electroblob77 c435ca5fa7 Allow fangs to be cast by npcs and dispensers 2020-06-30 00:04:29 +01:00
Electroblob77 c9735e9a98 Prevent casting fangs directly down or up 2020-06-30 00:02:49 +01:00
Electroblob77 0400e9276d Normalise direction vectors 2020-06-29 23:56:45 +01:00
Electroblob77 632790fabd Don't spawn fangs at the caster's feet 2020-06-29 23:54:05 +01:00
Electroblob77 b61bbda668 Fix radiant totem sound causing a CME 2020-06-29 23:52:41 +01:00
Electroblob77 c2e99e2ce6 Add fangs spell 2020-06-29 23:52:05 +01:00
Electroblob77 cfca8f1fdd Also apply targeting fix when mind control effect is removed manually 2020-06-29 21:28:32 +01:00
Electroblob77 b0d44738c7 Refactoring and cleanup 2020-06-29 21:26:04 +01:00
Electroblob77 8088e21ba4 Remove attack target as soon as mind control wears off, fixes mind controlled entities continuing to attack a target that dies after the mind control wears off 2020-06-29 21:22:01 +01:00
Electroblob77 68dad2a148 Add mind control overlay 2020-06-29 21:16:10 +01:00
Electroblob77 a25b642164 Add sounds to radiant totem and update sounds for ray of purification 2020-06-29 18:43:54 +01:00
Electroblob77 b37fa47211 Tweaks to firestorm and ice age particles and sound 2020-06-29 18:43:07 +01:00
Electroblob77 aea65b3753 World#isBlockFullCube is different to Block#isFullCube, and it turns out we actually want Block#isNormalCube :/ 2020-06-28 20:22:27 +01:00
Electroblob77 3a68ba8927 Implement blast modifier support for all applicable constructs, plus misc construct-related cleanup and fixes 2020-06-28 16:56:18 +01:00
Electroblob77 97c21b5057 Remove unnecessary reflection 2020-06-28 14:56:35 +01:00
Electroblob77 5be939a39f Add key aliases to spell matching 2020-06-28 12:45:54 +01:00
Electroblob77 080022127c Add radiant totem spell 2020-06-26 17:46:47 +01:00
Electroblob eb017c01a9 Update README.md 2020-06-26 14:57:56 +01:00
Electroblob77 1ea6355a9e Update Discord link 2020-06-26 14:55:26 +01:00
Electroblob77 f6f766c5b6 Convert firestorm sound to mono 2020-06-25 14:06:45 +01:00
Electroblob77 d8bf437214 Update advancements with new spells and artefacts 2020-06-25 13:22:35 +01:00
Electroblob77 5dc3ebed4f Add firestorm spell 2020-06-25 13:21:26 +01:00
Electroblob77 43690ace29 Trigger unlock of tiers handbook section when a wand levels up, in addition to finding a tome of arcana 2020-06-25 11:24:28 +01:00
Electroblob77 e6696a1758 Add comparator output for receptacles 2020-06-24 16:01:56 +01:00
Electroblob77 93b0aa948f Disable depth mask for magical light rendering 2020-06-23 23:44:44 +01:00
Electroblob77 439faf425f Fix random spell loot function working in reverse 2020-06-23 22:09:30 +01:00
Electroblob77 33c5f79ed7 Add archivist's eyeglass and spectral tether, plus some improvements to teleportation 2020-06-23 22:09:06 +01:00
Electroblob77 0ca86eb01b Improve javadoc for RayTracer 2020-06-23 13:25:10 +01:00
Electroblob77 d69b736a72 Fix shockwave not casting with no targets within range 2020-06-20 22:54:32 +01:00
Electroblob77 ac4bb8bc11 Shady particles 2020-06-20 22:47:55 +01:00
Electroblob77 fbf8e7155c Cleanup 2020-06-20 21:50:22 +01:00
Electroblob77 f2de40e096 Put these arguments the right way round 2020-06-20 21:50:12 +01:00
Electroblob77 c6a3f090cd Add blinding flash spell 2020-06-20 21:43:10 +01:00
Electroblob77 46a467de46 Finish implementing SpellAreaEffect and retrofit various spell classes to extend it 2020-06-20 21:42:37 +01:00
Electroblob77 62b06f8fc6 Remove stray javadoc 2020-06-20 21:40:52 +01:00
Electroblob77 f18f1d20f3 Make NPCs respect continuous spell cooldowns 2020-06-20 18:35:43 +01:00
Electroblob77 a88aa57ef0 Use the more concise method 2020-06-20 18:35:15 +01:00
Electroblob77 ba1ed773af Increase distance for blindness tweak 2020-06-20 18:34:42 +01:00
Electroblob77 76f2145072 Remove hardcoded full brightness for flash particles 2020-06-20 13:27:01 +01:00
Electroblob77 ba856346a9 Should be @Nullable 2020-06-20 13:22:46 +01:00
Electroblob77 39e33848aa Rename to getLivingWithinRadius for clarity 2020-06-20 13:14:16 +01:00
Electroblob77 dfe6ecadac Generalise isAllied to all living entities 2020-06-20 13:10:44 +01:00
Electroblob77 e9093b9360 FastTESRs are a thing, they might be useful... 2020-06-20 13:10:16 +01:00
Electroblob77 cbc778bff7 Centralise some more particle stuff, still not fixed though :( 2020-06-20 13:08:55 +01:00
Electroblob77 b65709fca4 Add blindness tweak and stop smoke bombs giving the mind trick effect 2020-06-20 13:08:16 +01:00
Electroblob77 d45bdbc3d0 Reinstate arm swinging for ray spells with extra action check 2020-06-19 22:40:02 +01:00
Electroblob77 50f0072c7a Prevent forest of thorns overwriting blocks 2020-06-19 21:52:38 +01:00
Electroblob77 c443e0d39c Don't transfer slow time when possession ends, fixes #459 2020-06-19 21:36:29 +01:00
Electroblob77 14483629e5 Fade growth sound in and out 2020-06-19 21:08:32 +01:00
Electroblob77 3a5a97c6b9 Add potency modifier support for forest of thorns 2020-06-19 21:03:35 +01:00
Electroblob77 9936388600 Sync snare too 2020-06-19 21:03:12 +01:00
Electroblob77 6aacebcc74 Stop setting the block each tick to make thorns grow and do it properly in the tile entity and getActualState
Also fixes ownerless thorns not dealing damage and removes knockback
2020-06-19 20:46:46 +01:00
Electroblob77 c842fef2f7 Remove unnecessary tile entity from upper thorn blocks, and reminder to do the same for statue blocks 2020-06-19 16:37:03 +01:00
Electroblob77 a87eca3bd8 Store the caster UUID regardless of whether the entity actually exists, and only read the UUID if it exists in the tag compound 2020-06-19 16:33:57 +01:00
Electroblob77 daf174ec3f Remove unused summon particles, we have a fancier layer system now :D 2020-06-18 18:04:49 +01:00
Electroblob77 7afcdce21e Fix some particle weirdness (somehow the interp pos is wrong by the time it gets to fx layer 3) 2020-06-18 17:42:16 +01:00
Electroblob77 3bc2ff2853 Cleanup 2020-06-18 16:27:09 +01:00
Electroblob77 f7f9190227 For some reason getTotalWorldTime isn't incrementing client-side in some cases when the doDaylightCycle is off, so replace them with alternatives wherever possible
What's really weird is sometimes it works just fine, even in client-side stuff
2020-06-18 16:20:10 +01:00
Electroblob77 bfd7f6e5a5 Improvements to sound loops to try and remove gaps between parts 2020-06-18 15:41:04 +01:00
Electroblob77 8e8e249426 Add imbuement altar
- Adds imbuement altar block and associated tile entity
- Adds basic functionality for imbuing wizard armour (other stuff to be decided)
- Incorporates imbuement altar into glowing overlay model event (this could do with being tidied up and generalised)
- Adds a TESR for the ray effect (based on some of the oldest code in the entire mod, which definitely needs improving!)
- Adds a simple sound effect for the imbuement process, needs upgrading to a ticked sound later
2020-06-17 23:44:02 +01:00
Electroblob77 99d0d115c7 Retexture earth crystals 2020-06-17 23:12:25 +01:00
Electroblob77 798c015944 Retexture ice giant 2020-06-14 18:39:38 +01:00
Electroblob77 732621f37c Fix invisible entities showing the frost layer 2020-06-14 14:54:41 +01:00
Electroblob77 238b9d04bb Split up and reorganise WizardryUtilities 2020-06-13 23:29:01 +01:00
Electroblob77 57e96458d5 Fix squelch sound not playing for the slime spell 2020-06-13 17:24:41 +01:00
Electroblob77 9dbdd2b830 Sort out various problems with freezing things, fixes #460 2020-06-13 17:23:50 +01:00
Electroblob77 d60238a400 Remove animation from magic slime 2020-06-13 14:14:46 +01:00
Electroblob77 7319651ac4 Blacklist decoys from being possessed 2020-06-13 12:55:20 +01:00
Electroblob77 36a228489b Limit currency item stack size to between 1 and 64 (inclusive) 2020-06-13 12:45:27 +01:00
Electroblob77 27e9b1d6f2 Add play sound call to empowering presence, fixes #454 2020-06-13 12:21:03 +01:00
Electroblob77 4bf8aff2e6 Make ice age give frostbite II to players for 20 seconds 2020-06-12 23:31:50 +01:00
Electroblob77 1c0e046236 Snowstorms! 2020-06-12 23:27:20 +01:00
Electroblob77 a65c56eb41 Mooore smoooooth animations 2020-06-12 23:12:37 +01:00
Electroblob77 00f958a71e Increase volume of charge up sound 2020-06-12 22:20:43 +01:00
Electroblob77 8aadf5a446 Various improvements to sounds
- Added several new sounds, mostly for ice spells
- Added automatic registering of sound events
- Finally fixed the frost ray and wall of frost sounds
- Removed a few unused sound files
2020-06-12 22:15:44 +01:00
Electroblob77 a344011486 Limit the number of blocks black hole can unhook per tick 2020-06-12 14:34:31 +01:00
Electroblob77 a17a20fe8b Add spell charge-up sound 2020-06-12 14:28:37 +01:00
Electroblob77 ee56aa50be Cleanup 2020-06-12 13:48:21 +01:00
Electroblob77 b4b32d52e3 Update credits 2020-06-12 13:09:01 +01:00
Electroblob77 526dbaf396 Make isCasting account for charge up 2020-06-12 12:55:50 +01:00
Electroblob77 737f896070 Add support for charge-up modifiers 2020-06-11 22:49:08 +01:00
Electroblob77 fb56190f6f Allow the cooldown bar to render properly for continuous spells 2020-06-11 22:23:15 +01:00
Electroblob77 c4ac1d7e6c Add cooldowns for more powerful continuous spells 2020-06-11 22:20:58 +01:00
Electroblob77 7d650ebbc3 Add charge-up mechanic for more powerful spells
- Implements spell charge-up in ItemWand
- Assigns chargeup values in json files for spells that should have one
- Adds a charge meter to the crosshair in first-person
- Updates some javadoc comments appropriately
2020-06-11 22:20:04 +01:00
Electroblob77 72971b5f16 Fix continuous scrolls not tracking spell modifiers properly 2020-06-11 22:11:34 +01:00
Electroblob77 937c791196 Goddamnit MCP, neither of these two methods do what the comments say they do! 2020-06-09 23:25:30 +01:00
Electroblob77 3348938d46 Finally got the animation timings right 2020-06-09 17:55:04 +01:00
Electroblob77 f95819cf37 Only show new spell message in survival with discovery mode on 2020-06-09 17:43:39 +01:00
Electroblob77 9563d730bd Remove various unused textures 2020-06-09 17:28:46 +01:00
Electroblob77 5be8c8ed36 This should be in here 2020-06-09 17:15:48 +01:00
Electroblob77 38b6643d1d Optimise imports 2020-06-09 17:08:25 +01:00
Electroblob77 038ead0d55 Tidy up 2020-06-09 17:07:36 +01:00
Electroblob77 a4d342caa1 Nudge the zombies 2020-06-09 17:01:17 +01:00
Electroblob77 c00c04acd2 Must've missed that one 2020-06-09 16:55:28 +01:00
Electroblob77 7772f2e97f Add new spell indicator icon and tooltip to the wizard trade GUI 2020-06-09 16:55:13 +01:00
Electroblob77 99a9524ee7 Add config option to disable player animations 2020-06-07 23:05:50 +01:00
Electroblob77 840ef3b3a5 Cleanup 2020-06-06 23:40:20 +01:00
Electroblob77 009321d905 Some more cleanup 2020-06-06 23:30:24 +01:00
Electroblob77 1a094e1970 Reorganise and clean up client event handlers 2020-06-06 23:25:56 +01:00
Electroblob77 6df0d2dc6e Allow permanent constructs to be dispelled by shift-right-clicking with any wand 2020-06-06 22:16:39 +01:00
Electroblob77 5011246e79 Don't disable depth 2020-06-05 17:12:45 +01:00
Electroblob77 c8e07cf857 Make zombie apocalypse account for ceilings 2020-06-05 17:12:31 +01:00
Electroblob77 a54f00875f Add disintegrate animation, plus misc tweaks to disintegration 2020-06-05 17:11:19 +01:00
Electroblob77 ce92d564f4 Cleanup 2020-06-04 23:16:43 +01:00
Electroblob77 afabcbde49 Huskpocalypse! 2020-06-04 23:02:29 +01:00
Electroblob77 88d0780d69 Sort out summoned creature particles 2020-06-04 22:48:35 +01:00
Electroblob77 33d4652758 Add summoned creature appear/disappear animation 2020-06-04 22:45:39 +01:00
Electroblob77 09967ac256 This was indeed no longer necessary - presumably a hangover from older versions 2020-06-04 15:53:16 +01:00
Electroblob77 aa84b2b8cd Introduce abstract superclass for LayerFrost and LayerStone, and fix some weirdness in the process 2020-06-04 15:35:50 +01:00
Electroblob77 a8f1ac38fa Derp 2020-06-04 14:50:02 +01:00
Electroblob77 d11c3fcc2f Carry excess progression over when upgrading 2020-06-04 12:37:57 +01:00
Electroblob77 92b54edb4a Add 1.5x progression modifier for tiers the player has already reached 2020-06-04 12:27:10 +01:00
Electroblob77 e54453e6c6 Animation tweak 2020-06-04 12:07:48 +01:00
Electroblob77 c7e7bb5c25 Move conjure armour to sorcery 2020-06-03 23:16:12 +01:00
Electroblob77 596a17453d Reduce jump strength with the frostbite effect 2020-06-03 21:24:19 +01:00
Electroblob77 70a3dee588 Animate bottled thundercloud texture 2020-06-03 21:23:57 +01:00
Electroblob77 b4fc50ad71 Add amulet of vitality 2020-06-02 23:41:16 +01:00
Electroblob77 930ad77db5 ...well this is embarrassing :/ 2020-06-02 23:12:42 +01:00
Electroblob77 20de12792f Update banner textures 2020-06-01 23:21:12 +01:00
Electroblob77 6eaccb9976 Fix permanent magic light rendering 2020-06-01 22:56:20 +01:00
Electroblob77 a877275916 Add Hungarian translations, courtesy of Bombadil 2020-06-01 22:50:57 +01:00
Electroblob77 e4e8e0a444 Add elemental banner patterns 2020-06-01 22:34:53 +01:00
Electroblob77 ef222e66f2 Change arcane jammer so it only has a chance of preventing spells 2020-05-31 23:16:57 +01:00
Electroblob77 31eed1cb48 Add 'glitch' effect to spell HUD when arcane jammed 2020-05-31 21:15:21 +01:00
Electroblob77 0cd61b758e More tweaks 2020-05-31 19:03:24 +01:00
Electroblob77 4ef54cf8cb Add sounds to zombie apocalypse spell, plus misc tweaks 2020-05-31 18:47:24 +01:00
Electroblob77 5066439fef Cleanup 2020-05-31 17:34:21 +01:00
Electroblob77 dd12d2ebcb Hide zombie particles properly 2020-05-31 17:31:31 +01:00
Electroblob77 cfdda5716d Smooooooth animations 2020-05-31 17:22:23 +01:00
Electroblob77 a33715ad51 Add zombie apocalypse spell 2020-05-31 17:16:55 +01:00
Electroblob77 05aac02966 A good ol' splash of optimise imports 2020-05-24 23:59:13 +01:00
Electroblob77 1af0186886 Add config options for disabling specific artefacts 2020-05-24 23:58:20 +01:00
Electroblob77 3e0dbb9755 It's about time we sorted out all these translations 2020-05-24 23:57:46 +01:00
Electroblob77 d9f7d94115 Remove unused translation keys 2020-05-24 22:55:51 +01:00
Electroblob77 94f4e680af Sync tile entities with casters properly, and tidy up a few other related things 2020-05-24 22:09:09 +01:00
Electroblob77 e17b5d2300 Remove incorrect !world.isRemote check and redundant constructor, fixes #446 2020-05-23 21:04:29 +01:00
Electroblob77 601f8a97fa Helps if you actually save the settings... 2020-05-22 18:08:05 +01:00
Electroblob77 e6b7f25932 Apparently the default is false 2020-05-22 17:57:17 +01:00
Electroblob77 6c08b21f7f More accurate descriptions, and make it synchronised 2020-05-22 17:54:53 +01:00
Electroblob77 d22c530f79 Default value should be false 2020-05-22 17:37:58 +01:00
Electroblob77 ddcaffc1b1 Add config option to treat passive creatures as allies 2020-05-22 17:34:07 +01:00
Electroblob77 6696be59a4 Allow crystal flowers to be placed in flower pots 2020-05-22 16:43:24 +01:00
Electroblob77 e1df468d93 Change remnant spawn egg colours 2020-05-22 12:52:55 +01:00
Electroblob77 5a57e8341a Add icarus medallion and void opal 2020-05-22 12:49:45 +01:00
Electroblob77 dd137c68f6 Make remnants spawn from obelisk spawners (bye bye mobelisks o/) 2020-05-21 20:55:10 +01:00
Electroblob77 1275f1d31f Add remnants 2020-05-21 18:40:01 +01:00
Electroblob 27d53b4e93 Update README.md 2020-05-21 14:15:18 +01:00
Electroblob 46691e9934 Update README.md 2020-05-21 14:14:49 +01:00
Electroblob77 30f2a99a2c Not sure what was going on here 2020-05-19 22:08:36 +01:00
Electroblob77 650344a0c8 Add stormcloud spell 2020-05-18 20:41:41 +01:00
Electroblob77 ebde514c98 Fix scrolls not triggering spell casting finish method and event 2020-05-17 00:35:56 +01:00
Electroblob77 4739d9f9fc Add cooldowns (using the vanilla mechanic) to various consumable items 2020-05-17 00:32:02 +01:00
Electroblob77 ef39757229 Add right-click use for mana flasks - right-click while holding a mana flask to charge the emptiest item from the hotbar, offhand or armour slots 2020-05-17 00:04:38 +01:00
Electroblob77 e09f9313f9 This is nullable 2020-05-16 23:35:23 +01:00
Electroblob77 94a17a63c1 Sync tile entity when book texture changes 2020-05-16 22:24:14 +01:00
Electroblob77 594c546f11 Make all valid books work with shift-clicking and hopper insertion 2020-05-16 18:33:53 +01:00
Electroblob77 cda9947e17 All synchronised settings require world restart! 2020-05-16 18:26:59 +01:00
Electroblob77 48a350b63a Allow any book to be placed in a bookshelf
- Modifies the bookshelf model and blockstate to choose a texture dynamically based on the book item
- Changes bookshelf slots to accept all books from wizardry and vanilla minecraft by default
- Adds a couple of hooks to allow addons to register their own book items and model textures
- Adds a config option to allow modpack makers to add more book items
2020-05-15 22:52:32 +01:00
Electroblob77 28da6810d7 Oops 2020-05-15 21:21:21 +01:00
Electroblob77 f5ce186a7d Well this hasn't been looked at in a while 2020-05-15 20:30:56 +01:00
Electroblob77 04bebe5910 Allow receptacles to be ignited using dispensers 2020-05-14 17:20:19 +01:00
Electroblob77 fe5060a2c0 Don't decrement spell index if spells haven't been initialised, and add the missing safety check to getCurrentMaxCooldown - fixes #428 2020-05-13 12:06:41 +01:00
Electroblob 8de990fdde Merge pull request #439 from TUsama/1.12.2
update lang file and book
2020-05-13 11:18:42 +01:00
TUsama 8a92773a17 fix some problems 2020-05-13 12:06:02 +08:00
TUsama 9f8b2fee1e update lang file and book
resolve2

转码以及修改了部分标点符号

update lang file and book
2020-05-13 10:20:50 +08:00
Electroblob77 9205f082f1 Rename texture variable to something more sensible 2020-05-12 23:56:36 +01:00
Electroblob77 ff4c17d1c1 Add crafting recipe for receptacle 2020-05-12 23:49:18 +01:00
Electroblob77 34a4609af2 Tweak receptacle model 2020-05-12 23:46:05 +01:00
Electroblob77 25475632d9 Require line-of-sight for summoning spells 2020-05-12 23:28:05 +01:00
Electroblob77 bfc23ce6f7 Remove a bunch of deprecated methods 2020-05-12 23:09:48 +01:00
Electroblob77 a97abd26d0 Add receptacles 2020-05-11 18:42:00 +01:00
Electroblob77 11821ff14d Apparently we don't need this any more 2020-05-11 18:38:18 +01:00
Electroblob77 5c34b379d1 Update French translations, courtesy of Crowller 2020-05-11 18:36:58 +01:00
Electroblob77 c4d4c67302 Well this is awkward :/ 2020-05-11 18:35:37 +01:00
Electroblob77 c04ee35fc3 Helps if you actually define the curative items... fixes #435 2020-05-11 18:29:32 +01:00
Electroblob77 09b52a4586 Reorganise block registry 2020-05-10 16:21:24 +01:00
Electroblob77 1f9f5a1cc5 Add spectral dust 2020-05-10 11:10:52 +01:00
Electroblob77 be250e218e Add player animation system!
- Hooks into ModelRenderer by wrapping it in an extended version that allows modification of angles after the model has set them
- Is compatible with pretty much everything, yay (even modded armour!)
- Adds property overrides to ItemWand so that gets animated too
- Adds a system for generating override models from a template at runtime so I don't have to duplicate all the wand models
- Tweaks particles and ray spells so they line up with the end of the newly-animated wand
- Fixes a long-standing bug where wand usage actions would stop immediately for all non-continuous spells
2020-05-09 20:17:15 +01:00
Electroblob77 cf38a16f49 A slightly better way of loading the book model texture 2020-05-07 23:22:52 +01:00
Electroblob77 a31b1c79e3 Make indirect magic damage count as magic damage, fixes #396 2020-05-06 20:21:49 +01:00
Electroblob ad520eff77 Update CONTRIBUTING.md 2020-05-06 12:52:02 +01:00
Electroblob77 0354f99bd2 Make wizardry-specific overloads package-private where possible to avoid confusion 2020-04-25 13:00:40 +01:00
Electroblob77 301ec86c27 Add comparator behaviour to bookshelves 2020-04-22 17:14:21 +01:00
Electroblob77 ee96ff3ddd Reflect into Block#getSilkTouchDrop instead of copying it, fixes #418 2020-04-22 17:13:55 +01:00
Electroblob77 53f545caad Replace use of deprecated ReflectionHelper with ObfuscationReflectionHelper 2020-04-21 23:05:48 +01:00
Electroblob77 755933486c Also prevent these two from being cast through walls 2020-04-21 22:10:55 +01:00
Electroblob77 5d8a90167c Prevent ray spells being cast through walls (oops, my bad :P) 2020-04-21 18:55:58 +01:00
Electroblob77 f89c71f314 Make spell scrolls implement IWorkbenchItem and have 1 slot
This means the book doesn't leave the workbench until the scrolls are taken out, which is good for automation - however, canPlace always returns false so they can't be put back in, kinda like the furnace output
2020-04-21 18:55:13 +01:00
Electroblob77 5ad4cc0610 Fix hoppers putting spell books in hidden slots 2020-04-19 17:41:12 +01:00
Electroblob77 f0752317e6 Add spell quick access keys 2020-04-18 21:45:01 +01:00
Electroblob77 ed63704184 Add descriptions to mana flasks 2020-04-18 21:10:02 +01:00
Electroblob77 3316c8e5cb Note to self: syncing is important 2020-04-18 19:14:47 +01:00
Electroblob77 0a2368650a Tweak particle brightness and lifetime 2020-04-18 18:11:43 +01:00
Electroblob77 b9958ce16f Add cloud particle from TF spell pack, and change smoke bomb to use it instead of vanilla's large_smoke particles 2020-04-18 18:06:22 +01:00
Electroblob77 8cbe5dd602 Assign key conflict contexts to wizardry's keybinds, fixes them showing up as conflicting with GUI keys 2020-04-18 17:12:29 +01:00
Electroblob77 f1cb7dbbbe Make arcane lock work on both halves of a double chest at once 2020-04-18 16:12:05 +01:00
Electroblob77 5f8477a4ad Snowballs were a projectile last time I checked... 2020-04-17 23:56:38 +01:00
Electroblob77 d4c2397a68 Fix lectern model rotation in first person 2020-04-17 23:25:48 +01:00
Electroblob77 a7b608a06e This description was correct in the first place! Turns out it was the upgrading recipes' fault. 2020-04-17 23:16:48 +01:00
Electroblob77 50ad3655c9 IntelliJ is too clever for its own good sometimes 2020-04-17 23:08:58 +01:00
Electroblob77 abc598a722 Remove deprecated fields 2020-04-17 23:03:16 +01:00
Electroblob77 b23136d568 Add method for checking if a spell property exists 2020-04-17 23:02:45 +01:00
Electroblob77 8291373427 Say something a bit more useful about scrolls of identification 2020-04-17 21:53:05 +01:00
Electroblob77 618df1c668 Add descriptions to some wizardry items in JEI
Covers most of the items players might stumble across
2020-04-17 21:49:01 +01:00
Electroblob77 cdc326e36c Remove the none spell from scroll recipes in JEI 2020-04-17 21:42:39 +01:00
Electroblob77 4c024f9e46 Hmmm I hope this incorrect description hasn't resulted in any bugs :/ 2020-04-17 21:38:21 +01:00
Electroblob77 d93b9a6765 Make bottomless provisions work with satiety, and have them trigger as soon as the player has enough hunger to fill 2020-04-16 15:49:10 +01:00
Electroblob77 7b1283c418 Yes it does, I fixed that! 2020-04-16 15:46:50 +01:00
Electroblob77 6882fb4519 Prevent imbuements from showing up on enchanted books in JEI 2020-04-15 23:34:41 +01:00
Electroblob77 57f4704e18 Update Forge 2020-04-15 23:24:06 +01:00
Electroblob77 50ca70b0db Specify JEI and Baubles as dependencies 2020-04-15 23:23:31 +01:00
Electroblob77 821361f9bf Update JEI 2020-04-15 23:11:43 +01:00
Electroblob77 2c05e52912 Update spanish handbook translations, courtesy of Alsentar 2020-04-15 23:02:04 +01:00
Electroblob77 8301a24afe Just enough wizardry!
- Adds JEI support for arcane workbench 'recipes' (charging, upgrading, and scroll enchantment)
- Adds an item transfer handler for said recipes, including bookshelf virtual slot support
- Prevents conjured items from showing up in JEI
2020-04-15 23:01:45 +01:00
Electroblob77 945c789482 Must have missed those 2020-04-15 20:13:36 +01:00
Electroblob77 13ba19c1ff Purple mana bars for wizard robes too! 2020-04-15 18:57:31 +01:00
Electroblob77 9bc920073b Fix animation positions 2020-04-14 23:42:19 +01:00
Electroblob77 df526dc76f Search trees don't like this very much :( 2020-04-14 23:06:49 +01:00
Electroblob77 702398dd34 Fix spell book tooltips not showing up in JEI 2020-04-14 22:33:48 +01:00
Electroblob77 1e5add914e Fix incorrect rounding for whole numbers of crystals 2020-04-14 22:33:12 +01:00
Electroblob77 4980beea87 Wooden things are now more wooden 2020-04-14 18:42:50 +01:00
Electroblob77 91b2ee1a13 Merge branch '1.12.2' into 4.3-dev 2020-04-14 17:41:33 +01:00
Electroblob77 937617d7af Update credits 2020-04-13 22:02:21 +01:00
Electroblob77 94751a6fd5 Remove the debug particle from the divination spell 2020-04-13 21:08:47 +01:00
Electroblob77 01ab50cb27 Make targets dismount whatever they are riding when they get possessed 2020-04-13 21:04:11 +01:00
Electroblob77 d0bcbc8ed8 Let's do this properly, shall we? Fixes #397 2020-04-13 20:53:14 +01:00
Electroblob77 b660b02e0e Fix crash when throwing things with greater telekinesis using commands 2020-04-13 19:10:20 +01:00
Electroblob77 153ea74966 The translators seem to have used exotic characters that require fancy encoding 🤷‍♂️ 2020-04-13 19:07:57 +01:00
Electroblob77 c81830efec Neither of these should be null. I should not have to check they aren't null. But apparently they can be. So I do. Fixes #402. 2020-04-13 19:04:01 +01:00
Electroblob77 6ec2e716cb Register artefact loot tables properly so addon mods can inject loot into them, fixes #403 2020-04-13 19:00:21 +01:00
Electroblob77 96e7c5885b Try to optimise arcane lock rendering, fixes #399 2020-04-13 18:59:07 +01:00
Electroblob77 41d575c9c9 Add config options to disable the screen shake and blink effects
We're all about that accessibility here!
2020-04-13 18:48:30 +01:00
Electroblob77 7f31fc6259 Thanks for warning me, IntelliJ :/ 2020-04-13 18:38:06 +01:00
Electroblob77 817fd26dcf Merge branch '1.12.2' of https://github.com/Electroblob77/Wizardry into 1.12.2 2020-04-13 18:35:40 +01:00
Electroblob77 aecc8b0889 Okay it really is fixed now I promise 2020-04-13 17:20:43 +01:00
Electroblob77 b45c202ac5 Add tint to background for book-style GUIs 2020-04-13 15:41:59 +01:00
Electroblob77 16ad8543d8 Purple mana (durability) bar! 2020-04-13 15:32:07 +01:00
Electroblob77 e985bc6f69 Since we're syncing anyway, make the client do a deferred refresh to allow syncing to occur, rather than trying to keep track of prev stacks itself 2020-04-12 23:00:23 +01:00
Electroblob77 8682fcbe91 Only prevent syncing when first loading the world 2020-04-12 22:51:24 +01:00
Electroblob77 e8ca773557 Don't sync when loading from NBT 2020-04-12 21:59:22 +01:00
Electroblob77 c622158818 Fix syncing of everything 2020-04-12 18:35:30 +01:00
Electroblob 7237840a2a Merge pull request #400 from Hokorizero/1.12.2
Update Simplified Chinese Translation
2020-04-12 16:12:18 +01:00
Electroblob77 6254020024 Ugh just why 2020-04-11 00:12:07 +01:00
Electroblob77 1d161e613c Comments 2020-04-10 23:56:00 +01:00
Electroblob77 e8c1b4d581 All the synchronised settings require world restart 2020-04-10 23:55:54 +01:00
Electroblob77 b5f61205eb Implement custom search behaviour for the spells tab, so it works like the bookshelf interface 2020-04-10 23:42:05 +01:00
Electroblob77 6f0636da9f These don't need world restarts! Also I forgot the default values for bookshelf blocks... 2020-04-10 22:49:40 +01:00
Electroblob77 b3fd39c42c Add config options for bookshelf blocks and bookshelf search radius 2020-04-10 22:42:31 +01:00
Electroblob77 40ed431ee2 Fix crash when throwing stuff with greater telekinesis via commands 2020-04-10 16:01:18 +01:00
Electroblob77 15d3d9e70b Arcane workbench tooltip refactoring
- Reorganises the huge amount of code in the draw background/foreground methods into a hierarchical object system
- Makes the tooltip box change height to fit the content
- Adds text wrapping support for items/spells with super long names
- Adds proper format arguments to the mana readout translation
- The tooltip now shows the new spells that will be applied with a flashing animation
2020-04-10 15:56:34 +01:00
Clefal 8ad6b5590b Merge branch '1.12.2' into 1.12.2 2020-04-10 14:03:56 +08:00
Hokorizero 1c2c267e1a Update handbook_zh_cn.json 2020-04-10 12:47:00 +08:00
Hokorizero e45bfb9248 Update zh_cn.lang 2020-04-10 12:45:51 +08:00
Electroblob77 5df98d5b95 Was bound to mess up at least one of them 🙃 2020-04-10 00:21:13 +01:00
Electroblob77 a4fd88f21d Organise texture folders and misc cleanup 2020-04-10 00:17:01 +01:00
Electroblob77 793ba0eb05 Fix the scroll bar 2020-04-09 23:47:03 +01:00
Electroblob77 531700378f Use the correct progression bar width 2020-04-09 23:44:06 +01:00
Electroblob77 4875d5a970 Put excess upgrades onto a second row so they don't go off the edge 2020-04-09 23:33:40 +01:00
Electroblob77 3ff6be4cb0 Ignore empty slot books in the lectern GUI 2020-04-09 23:18:23 +01:00
Electroblob77 c418757042 Hide bookshelf interface when there are no nearby bookshelves 2020-04-09 23:13:05 +01:00
Electroblob77 e272d4f999 Add recipes for lecterns 2020-04-09 21:35:37 +01:00
Electroblob77 c50b344041 Remove usage of deprecated server-side I18n 2020-04-09 21:28:48 +01:00
Electroblob77 bd11c9c759 Cleanup 2020-04-09 21:22:43 +01:00
Electroblob77 f7b678e291 Reminder to fix this 2020-04-09 21:18:56 +01:00
Electroblob77 6cabe7ffd4 Make sure the slot order is retained! 2020-04-09 21:07:32 +01:00
Electroblob77 f973099fd3 Add sound for locating spells using the lectern 2020-04-09 21:07:06 +01:00
Electroblob77 1833ed9c56 Tweak block highlight scale to remove the tiny gap due to the anti-z-fighting offset 2020-04-09 20:09:13 +01:00
Electroblob77 d56cf37f2b Make spell books return to the slots they came from (provided the container isn't closed) 2020-04-09 20:08:28 +01:00
Electroblob77 c5b5172481 Add lecterns 2020-04-08 00:16:12 +01:00
Electroblob77 938d0df95c TODO: Convert magic light to a non-tileentity block 2020-04-05 16:12:46 +01:00
Electroblob77 525b7e13dd Super method should do just fine! 2020-04-05 15:33:40 +01:00
Electroblob77 a67eb2381d Improve bookshelf search and account for undiscovered spells 2020-04-04 21:32:57 +01:00
Clefal 0d48606b51 Merge pull request #4 from Hokorizero/Mineversion
Update zh_cn.lang
2020-04-03 13:21:07 +08:00
Determancer 3c0b65c485 Update zh_cn.lang 2020-04-03 12:15:31 +08:00
Hokorizero 771118afde Merge pull request #2 from Hokorizero/TUsama-patch-2
比对1
2020-04-03 11:49:45 +08:00
Determancer 29b9e0de8b Update zh_cn.lang 2020-04-03 11:45:47 +08:00
Clefal f26f537f50 比对1 2020-04-03 11:40:50 +08:00
Electroblob77 91c9a5392b Make replenish hunger and satiety fill the correct amount of food, and make them say the correct number in their descriptions 2020-03-31 23:16:44 +01:00
Electroblob77 6ae9df1204 Only receive RenderGameOverlayEvent.Post, and only process when it will actually draw something 2020-03-31 23:16:21 +01:00
Electroblob77 d46600ac86 Update version number 2020-03-31 22:06:49 +01:00
Electroblob77 004782a1e4 Update credits 2020-03-31 22:05:00 +01:00
Electroblob77 b2b48cd8a1 Remove old handbook translations 2020-03-31 21:59:02 +01:00
Electroblob77 2277f6b278 Helps if you actually copy the setting over after synchronising it... fixes #395 2020-03-31 21:57:37 +01:00
Electroblob77 ba59d9786f Add null check for grapple target when cast by NPCs, fixes #394 2020-03-31 21:56:20 +01:00
Electroblob77 bdf5aa5b64 Skip spells with missing glyph data and print a warning to the console instead, fixes #384 2020-03-31 21:54:15 +01:00
Electroblob77 a384a57622 Prevent mending enchantment being applied to wizard armour, closes #383 2020-03-31 21:47:53 +01:00
Electroblob77 586840aa53 Add safety checks to prevent itemblocks with invalid metadata turning into invalid blockstates, fixes #382
(Because *some* mod authors are not very careful...)
2020-03-31 21:42:16 +01:00
Electroblob77 1ab674136b Add null check for riding entity to magic slimes, fixes #379 2020-03-31 21:37:38 +01:00
Electroblob77 71302b3219 Add recipes for bookshelves 2020-03-31 21:33:18 +01:00
Electroblob77 c161a74d73 Add bookshelf interface to arcane workbench
Implements the main functionality:
- Access bookshelves from within the workbench
- Search by spell name, tier, element and type
- Sort by tier, element or alphabetically, ascending or descending
- Three-way shift-clicking support
- Automatic return of books to bookshelves when wand is removed
2020-03-31 21:33:10 +01:00
Electroblob77 df1f737539 Fancy new advancement background 2020-03-31 21:26:23 +01:00
Electroblob 64e7e3844e Merge pull request #391 from rafasoares/patch-1
Make Magic Silk recipe use ore_dict for string
2020-03-31 18:14:12 +01:00
Clefal 51a3d2a63e Update zh_cn.lang 2020-03-30 13:42:27 +08:00
Electroblob77 53a053bfff Make replenish hunger and satiety fill the correct amount of food, and make them say the correct number in their descriptions 2020-03-29 17:00:07 +01:00
Electroblob77 220bf15cfb Don't process unless the event type means it will actually draw something 2020-03-29 16:56:52 +01:00
Electroblob77 885e58c85c Receive only RenderGameOverlayEvent.Post, not both 2020-03-29 16:55:04 +01:00
Rafael Soares 7c2e786d0b Remove whitespace changes 2020-03-27 20:53:25 -03:00
Rafael Soares a7ee0ad847 Make Magic Silk recipe use ore_dict for string
Currently, the recipe requires string to be a Vanilla string, which might cause problems in packs with different methods for obtaining strings (eg: Mystical World's Silk Thread from silkworms).

This PR makes it compatible with Ore Dictionary, so the recipe accepts any type of string.
2020-03-27 20:49:51 -03:00
Clefal 033d90ed3e Update zh_cn.lang 2020-03-26 16:39:59 +08:00
Electroblob77 849066de8a Apparently we do need this after all 2020-03-25 15:20:24 +00:00
Electroblob77 eb65b7d7b7 Reorganise config again 2020-03-25 15:14:58 +00:00
Electroblob77 97499eb523 Update version number 2020-03-25 15:08:15 +00:00
Electroblob77 02480db158 Reorganise config file 2020-03-25 15:05:32 +00:00
Electroblob77 e35ee9d126 Merge branch '1.12.2' into 4.3-dev 2020-03-25 14:29:53 +00:00
Electroblob77 004c4c09e3 Add bookshelves 2020-03-25 14:29:27 +00:00
Clefal 52214f81cf Update zh_cn.lang 2020-03-25 11:49:38 +08:00
Clefal 516d7948ee Update zh_cn.lang
附魔
2020-03-25 11:21:18 +08:00
Electroblob77 a490168fa2 Derp 2020-03-19 15:46:42 +00:00
Electroblob77 306137a026 Update version number 2020-03-19 15:38:19 +00:00
Electroblob77 0a5527f3d4 Add backwards compatibility to convert old-format atlas markers to new ones 2020-03-19 15:35:09 +00:00
Electroblob77 4dbe24c8ea This is the problem with everything being a string... 2020-03-19 15:13:25 +00:00
Hokorizero 5477725b7e Update zh_cn.lang 2020-03-19 23:08:18 +08:00
Electroblob77 81bba5e7e0 Add null check to ice spikes, fixes #371 2020-03-19 13:35:36 +00:00
Electroblob77 9c29142e6a Maven-ify all the things and update Antique Atlas 2020-03-19 13:33:34 +00:00
Clefal 3dc3a3a378 二次润色,改掉了所有的“咒语”,修改了部分的语法排序 2020-03-19 11:05:59 +08:00
Electroblob77 3151d7d626 Require Antique Atlas 4.6 or newer 2020-03-18 17:18:41 +00:00
Electroblob77 d68f968632 Make sure fall damage stuff is processed on the server only, and reactivate the config option by default 2020-03-18 16:00:23 +00:00
Electroblob77 74d9198522 Convert replaceVanillaFireballs to a synchronised option, fixes #351 2020-03-18 15:50:01 +00:00
Electroblob77 0b56d47022 Replace wildcard with metadata 0 for display purposes, fixes #370
N.B. The output is still not visible for non-zero inputs when using wildcard trades, but for now this is good enough
2020-03-18 15:32:59 +00:00
Electroblob77 587ab1338e Make Magic Protection protect against vanilla magic damage, closes #337 2020-03-18 14:57:04 +00:00
Electroblob77 4816c48aa6 Disable replaceVanillaFallDamage by default 2020-03-18 14:49:12 +00:00
Hokorizero 2456589fa7 Update zh_cn.lang 2020-03-18 17:27:03 +08:00
Hokorizero d7849a94b1 Update zh_cn.lang 2020-03-18 17:13:11 +08:00
Hokorizero cfb9f4fe25 Update zh_cn.lang 2020-03-18 16:47:49 +08:00
Hokorizero a269cf339b Update zh_cn.lang 2020-03-18 16:20:48 +08:00
Hokorizero 1c8e4f86ee Update zh_cn.lang 2020-03-18 15:35:56 +08:00
Hokorizero f2835c245f Update zh_cn.lang 2020-03-18 15:30:49 +08:00
Hokorizero 462512ad30 Update zh_cn.lang 2020-03-18 15:17:43 +08:00
Hokorizero db5a7dcfde Update zh_cn.lang 2020-03-18 15:13:30 +08:00
Hokorizero 17bba5dea8 Update zh_cn.lang 2020-03-18 15:09:18 +08:00
Clefal fbdb5cd844 医命师修改 2020-03-18 14:58:13 +08:00
Hokorizero 393fb1c1b0 Merge pull request #1 from Hokorizero/润色1
润色
2020-03-18 14:56:42 +08:00
Hokorizero 08e89b1aef Update zh_cn.lang 2020-03-18 14:49:55 +08:00
Clefal 1f25d95b1b 润色 2020-03-18 14:48:27 +08:00
Hokorizero 1f3a71797e Update zh_cn.lang 2020-03-18 14:10:51 +08:00
Clefal a821ef2aef 小修改,图片标题,语法润色 2020-03-18 14:07:57 +08:00
Clefal 6845279c49 手册进度→进度 2020-03-18 13:37:24 +08:00
Electroblob77 79b745e005 Randomise y coordinate to search from when spawning structures in 'cavern' dimensions, fixes #372 2020-03-17 23:54:52 +00:00
Electroblob77 6fff0e31d1 *Facepalm* 2020-03-17 23:45:40 +00:00
Electroblob77 11efe56437 Replace old hardcoded spell book text with translation keys, fixes #368 2020-03-17 23:38:25 +00:00
Electroblob77 7f5786c28c Override override override! 2020-03-17 23:22:54 +00:00
Electroblob77 85b49b330a Make tomes of arcana more common in obelisks, shrines and wizard towers, and make tomes generated as loot more likely to be apprentice tier 2020-03-17 23:16:04 +00:00
Electroblob77 d797e72445 Reduce rarity of all structures a bit 2020-03-17 23:13:17 +00:00
Electroblob77 5f21b565cd Make fireballs, large fireballs and iceballs respect the playerBlockDamage config option 2020-03-17 22:44:30 +00:00
Electroblob77 9ed06c9a34 Stop wizards casting heal ally 2020-03-17 22:34:34 +00:00
Electroblob77 50f315666f Fix spanish handbook translations, fixes #365 2020-03-15 19:23:50 +00:00
Electroblob77 62bdbc81e4 Prevent players from picking up items when possessing and prevent players from possessing whilst sneaking, fixes #376 2020-03-15 19:15:28 +00:00
Electroblob77 d9c3d15b9b Fix gilded wood models and move to wizardry tab 2020-03-01 13:39:59 +00:00
Electroblob77 bb6bb78163 IntelliJ broke my formatting again >:( 2020-03-01 13:07:55 +00:00
Electroblob77 3f80c56163 Add gilded wood 2020-03-01 13:04:15 +00:00
Electroblob77 3a571fd239 Update credits 2020-02-29 11:25:35 +00:00
Electroblob 4fb5452dc4 Merge pull request #363 from chesterccj305/patch-1
Create handbook_zh_tw.json
2020-02-24 16:51:28 +00:00
Electroblob 652f1d2620 Merge pull request #364 from chesterccj305/patch-2
Create zh_tw.lang
2020-02-24 16:51:15 +00:00
chesterccj305 046ecb1e41 Create zh_tw.lang 2020-02-21 22:56:45 +08:00
chesterccj305 f3dd7bccd8 Create handbook_zh_tw.json 2020-02-21 22:55:22 +08:00
1433 changed files with 43461 additions and 11067 deletions
+1 -1
View File
@@ -10,7 +10,7 @@ assignees: ''
Please read the [guide for contributing](https://github.com/Electroblob77/Wizardry/blob/1.12.2/CONTRIBUTING.md) before posting.
Minecraft version: 1.12.2 [change as necessary]
Wizardry version: 4.2.9 [change as necessary]
Wizardry version: 4.3.4 [change as necessary]
Environment: Singleplayer/LAN game/Server [delete as necessary]
Issue details: [describe what you were doing when the issue occurred, what went wrong, what you expected to happen, etc.]
+1 -1
View File
@@ -10,7 +10,7 @@ assignees: ''
Please read the [guide for contributing](https://github.com/Electroblob77/Wizardry/blob/1.12.2/CONTRIBUTING.md) before posting.
Minecraft version: 1.12.2 [change as necessary]
Wizardry version: 4.2.9 [change as necessary]
Wizardry version: 4.3.4 [change as necessary]
Question details: [explain your question here]
+15 -2
View File
@@ -8,7 +8,7 @@ When submitting an issue, I would be grateful if you could follow these few guid
- Please stick to the issue template, it's there to help you provide the right information so I can fix the problem more easily. If you're not sure what to put for anything, that's ok, just leave it as it is.
- One issue per issue please! Make a separate issue for each bug/suggestion. If you think multiple different issues may be related, reference between them using # followed by the issue number.
- One issue per issue please! Make a separate issue for each bug/suggestion. If you think multiple different issues may be related, reference between them using # followed by the issue number, and github will automagically link them.
- Please give me as much information about the issue as you can, but keep it relevant. "This is really annoying me and my friend" does not help me fix the problem!
@@ -18,6 +18,8 @@ When submitting an issue, I would be grateful if you could follow these few guid
- Don't close an issue unless it has actually been fixed or is no longer relevant - otherwise I might not end up fixing it at all! Usually I'll take care of closing issues anyway, so you don't have to worry about it.
- Please don't revive old issues, _especially_ if they have been fixed, because I don't routinely check them. Make a new issue and link the old one to it if you think it's related.
- Most importantly, use gist or pastebin to post crash reports. Do not paste the entire crash report inline, not even in a code block!
> If you've got a git account (which you do, if you're posting an issue) you can access gist, and pastebin doesn't require an account at all, so there's no excuse!
@@ -43,4 +45,15 @@ Pull requests aren't just for code - if you would like to help translate Wizardr
- Firstly, the .lang file: if you're familiar with translating mods, this is all pretty standard - it holds the names of blocks, items, mobs and so on. Ignore the %X$s bits, they're placeholders for stuff that gets put in by Wizardry when the game is running.
- Secondly, the handbook text file: the US english version is named handbook_en_US.txt, and (rather obviously) other languages' text files replace 'en_US' with the name of their corresponding .lang file. Anything wirtten in BLOCK CAPITALS should be left alone, because Wizardry replaces it with the appropriate text when the game is running.
- Secondly, the handbook JSON file: the US english version is named handbook_en_us.json, and (rather obviously) other languages' text files replace 'en_us' with the name of their corresponding .lang file. Make sure you only change the _values_ (after each `:`), not the identifiers - for a summary of JSON syntax, see the [JSON webpage](https://www.json.org/json-en.html). If you're familiar with advancements, loot tables and similar files, this won't be new to you. The handbook file also applies its own form of markup to the main text, with the following features:
- `#image tag` adds an image to the text with the given tag. Image tags are defined in the `"images": {...}` block at the start of the file. Each named entry in this block defines a single image with that name as its tag. Each entry is a JSON object containing a file location and optional caption/dimensions.
- `#recipe tag` adds a crafting recipe to the text with the given tag. Similar to image tags, recipe tags are defined in the `"recipes": {...}` block at the start of the file. Each named entry in this block defines a recipe with that name as its tag. Each entry contains a list of one or more locations or recipes to display (if more than one is specified, the display will cycle through the different recipes).
- Anything else with a `#` directly before it (with no space) will be treated as a format argument if possible. This means the text after the `#` will be replaced with something else at runtime, similar to the `%X$s` bits in a normal .lang file, if it matches one of the available format arguments. The current format arguments can be found [here](https://github.com/Electroblob77/Wizardry/blob/1.12.2/src/main/java/electroblob/wizardry/client/gui/handbook/GuiWizardHandbook.java#L177-L203).
- `@section@` adds a hyperlink to the given section, which will display as the name of that section. `@section alt text@` is the same, except alt text is displayed instead of the section name (alt text may contain spaces). It is also possible to hyperlink to a webpage, simply replace the section name with the url (e.g. `@https://example.com/@` or `@https://example.com/ alt text@`).
> Translators for Chinese, Japanese, Korean and similar East Asian languages: Due to how Minecraft's word wrapping works, for best results you should _remove spaces before and after hyperlinks and formatting tags_, excluding images and recipes. However, do not remove the space between a hyperlink's destination and its alt text or the link will break!
If all of that confused you, the simple rule is: _Don't translate anything directly after an `@` symbol, directly after a `#`, or the first word after `#image` or `#recipe`_. However, you should translate the `"caption"` entries in the `images` block.
**Please ensure all translation files are encoded in UTF-8 format, otherwise they might crash the game!**
+19 -7
View File
@@ -1,7 +1,7 @@
# Credits
Electroblob's Wizardry
Version 4.2.9
Version 4.3.4
For Minecraft 1.12.2
Designed, coded and textured by Electroblob
@@ -24,17 +24,29 @@ In addition, I'd like to thank the following individuals for their contributions
- UltraHex
- Azim-Palmer
- raoulvdberge
- rafasoares
- xinyuan-liu
- SettingDust
#### Translations
- Spanish: MadWrist, Alsentar
- Mexican Spanish: MadWrist
- Russian: VilagVil, kellixon, bigenergy
- Russian: VilagVil, kellixon, bigenergy, MugGod2, DrHesperus
- French: Hahdrim
- Brazilian Portuguese: lorrampi
- Chinese: ZHENGLOC, dragon-evol, Hokorizero, TUsama
- Korean: shejery, rewi_wire
- Polish: Trozuu
- German: BirdyDragon
- Chinese (Simplified): ZHENGLOC, dragon-evol, Hokorizero, TUsama, Determancer
- Korean: shejery, rewi_wire, 방통, red1854th
- Polish: Trozuu, Olej
- German: BirdyDragon, Lemopav
- Chinese (Traditional): chesterccj305
- Hungarian: Bombadil
Lightning ray sound effect from OhhWowProductions
#### Sound Effects
- OhhWowProductions
- fredzed
- deleted_user_3277771
- DiscoveryME
- OGSoundFX
- leosalom
- juskiddink
+1 -1
View File
@@ -1,7 +1,7 @@
Please read the [guide for contributing](https://github.com/Electroblob77/Wizardry/blob/1.12.2/guide_for_contributing.md) before posting.
Minecraft version: 1.12.2 [change as necessary]
Wizardry version: 4.2.9 [change as necessary]
Wizardry version: 4.3.4 [change as necessary]
Environment: Singleplayer/LAN game/Server [delete as necessary]
Issue details: [describe what you were doing when the issue occurred, what went wrong, what you expected to happen, etc.]
+2 -2
View File
@@ -1,6 +1,6 @@
# Wizardry &nbsp;[![Curseforge](http://cf.way2muchnoise.eu/full_electroblobs-wizardry_downloads.svg)](https://minecraft.curseforge.com/projects/electroblobs-wizardry) [![Curseforge](http://cf.way2muchnoise.eu/versions/For%20MC_electroblobs-wizardry_all.svg)](https://minecraft.curseforge.com/projects/electroblobs-wizardry/files) [![Discord](https://img.shields.io/discord/417798297265242145.svg?colorB=7289DA&logo=data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAHYAAABWAgMAAABnZYq0AAAACVBMVEUAAB38%2FPz%2F%2F%2F%2Bm8P%2F9AAAAAXRSTlMAQObYZgAAAAFiS0dEAIgFHUgAAAAJcEhZcwAACxMAAAsTAQCanBgAAAAHdElNRQfhBxwQJhxy2iqrAAABoElEQVRIx7WWzdGEIAyGgcMeKMESrMJ6rILZCiiBg4eYKr%2Fd1ZAfgXFm98sJfAyGNwno3G9sLucgYGpQ4OGVRxQTREMDZjF7ILSWjoiHo1n%2BE03Aw8p7CNY5IhkYd%2F%2F6MtO3f8BNhR1QWnarCH4tr6myl0cWgUVNcfMcXACP1hKrGMt8wcAyxide7Ymcgqale7hN6846uJCkQxw6GG7h2MH4Czz3cLqD1zHu0VOXMfZjHLoYvsdd0Q7ZvsOkafJ1P4QXxrWFd14wMc60h8JKCbyQvImzlFjyGoZTKzohwWR2UzSONHhYXBQOaKKsySsahwGGDnb%2FiYPJw22sCqzirSULYy1qtHhXGbtgrM0oagBV4XiTJok3GoLoDNH8ooTmBm7ZMsbpFzi2bgPGoXWXME6XT%2BRJ4GLddxJ4PpQy7tmfoU2HPN6cKg%2BledKHBKlF8oNSt5w5g5o8eXhu1IOlpl5kGerDxIVT%2BztzKepulD8utXqpChamkzzuo7xYGk%2FkpSYuviLXun5bzdRf0Krejzqyz7Z3p0I1v2d6HmA07dofmS48njAiuMgAAAAASUVORK5CYII%3D)](https://discord.gg/MTmMzMv)
# Wizardry &nbsp;[![Curseforge](http://cf.way2muchnoise.eu/full_electroblobs-wizardry_downloads.svg)](https://minecraft.curseforge.com/projects/electroblobs-wizardry) [![Curseforge](http://cf.way2muchnoise.eu/versions/For%20MC_electroblobs-wizardry_all.svg)](https://minecraft.curseforge.com/projects/electroblobs-wizardry/files) [![Discord](https://img.shields.io/discord/417798297265242145.svg?colorB=7289DA&logo=data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAHYAAABWAgMAAABnZYq0AAAACVBMVEUAAB38%2FPz%2F%2F%2F%2Bm8P%2F9AAAAAXRSTlMAQObYZgAAAAFiS0dEAIgFHUgAAAAJcEhZcwAACxMAAAsTAQCanBgAAAAHdElNRQfhBxwQJhxy2iqrAAABoElEQVRIx7WWzdGEIAyGgcMeKMESrMJ6rILZCiiBg4eYKr%2Fd1ZAfgXFm98sJfAyGNwno3G9sLucgYGpQ4OGVRxQTREMDZjF7ILSWjoiHo1n%2BE03Aw8p7CNY5IhkYd%2F%2F6MtO3f8BNhR1QWnarCH4tr6myl0cWgUVNcfMcXACP1hKrGMt8wcAyxide7Ymcgqale7hN6846uJCkQxw6GG7h2MH4Czz3cLqD1zHu0VOXMfZjHLoYvsdd0Q7ZvsOkafJ1P4QXxrWFd14wMc60h8JKCbyQvImzlFjyGoZTKzohwWR2UzSONHhYXBQOaKKsySsahwGGDnb%2FiYPJw22sCqzirSULYy1qtHhXGbtgrM0oagBV4XiTJok3GoLoDNH8ooTmBm7ZMsbpFzi2bgPGoXWXME6XT%2BRJ4GLddxJ4PpQy7tmfoU2HPN6cKg%2BledKHBKlF8oNSt5w5g5o8eXhu1IOlpl5kGerDxIVT%2BztzKepulD8utXqpChamkzzuo7xYGk%2FkpSYuviLXun5bzdRf0Krejzqyz7Z3p0I1v2d6HmA07dofmS48njAiuMgAAAAASUVORK5CYII%3D)](https://discord.gg/hs8yJP2)
![Image not found](https://raw.githubusercontent.com/wiki/Electroblob77/Wizardry/images/banners/art_banner.png)
![Image not found](https://raw.githubusercontent.com/wiki/Electroblob77/Wizardry/en_gb/images/banners/art_banner.png)
Source code for Electroblob's Wizardry, a Minecraft mod currently for versions 1.7.10, 1.10.2, 1.11.2, and 1.12.2. Wizardry adds an RPG-style system of magic spells to Minecraft with the aim of being as playable as possible. No crazy constructs, no perk trees, no complex recipes - simply find spell books, cast spells, and master the arcane!
+9 -37
View File
@@ -11,7 +11,7 @@ apply plugin: 'net.minecraftforge.gradle.forge'
//Only edit below this line, the above code adds and enables the necessary things for Forge to be setup.
version = "4.2.9" // There, it matches semver, happy now?
version = "4.3.4" // There, it matches semver, happy now?
group= "electroblob.wizardry"// http://maven.apache.org/guides/mini/guide-naming-conventions.html
archivesBaseName = "ElectroblobsWizardry"
@@ -31,19 +31,14 @@ repositories {
name = "ModMaven"
url = "modmaven.k-4u.nl"
}
flatDir {
// evErYthINg hAS tO Be a mAVeN rEPoSiTOrY... (to use deobfProvided, for some reason)
// The lack of documentation is actually quite impressive, see the following threads:
// http://www.minecraftforge.net/forum/topic/44262-solved-how-to-build-mod-with-dependencies/
// https://www.minecraftforum.net/forums/mapping-and-modding-java-edition/mapping-and-modding-tutorials/2866817-setting-up-dependencies-in-minecraft-forge-with
// http://www.minecraftforge.net/forum/topic/44534-adding-dependencies-to-a-forge-project/
dirs "libsfordeobf" // Don't put src jars in libs or forge will think you have duplicate mods
maven {
name = "Curseforge Maven"
url = "https://minecraft.curseforge.com/api/maven/"
}
}
minecraft {
version = "1.12.2-14.23.5.2814"
version = "1.12.2-14.23.5.2847"
runDir = "run"
// the mappings can be changed at any time, and must be in the following format.
@@ -56,37 +51,14 @@ minecraft {
}
dependencies {
// you may put jars on which you depend on in ./libs
// or you may define them like so..
//compile "some.group:artifact:version:classifier"
//compile "some.group:artifact:version"
// compile against the JEI API but do not include it at runtime
// Compile against the JEI API but do not include it at runtime
deobfProvided "mezz.jei:jei_${mc_version}:${jei_version}:api"
// at runtime, use the full JEI jar
// At runtime, use the full JEI jar
runtime "mezz.jei:jei_${mc_version}:${jei_version}"
//deobfProvided "baubles:baubles-1.12:1.5.2:api" // #ihavenoideawhatimdoing #itjustworks
//runtime "baubles:baubles-1.12:1.5.2" // If in doubt, copy what already works
//deobfProvided "antiqueatlas:antiqueatlas-1.12.2:4.5.1:src"
//runtime "antiqueatlas:antiqueatlas-1.12.2:4.5.1"
// real examples
//compile 'com.mod-buildcraft:buildcraft:6.0.8:dev' // adds buildcraft to the dev env
//compile 'com.googlecode.efficient-java-matrix-library:ejml:0.24' // adds ejml to the dev env
// the 'provided' configuration is for optional dependencies that exist at compile-time but might not at runtime.
//provided 'com.mod-buildcraft:buildcraft:6.0.8:dev'
// the deobf configurations: 'deobfCompile' and 'deobfProvided' are the same as the normal compile and provided,
// except that these dependencies get remapped to your current MCP mappings
//deobfCompile 'com.mod-buildcraft:buildcraft:6.0.8:dev'
//deobfProvided 'com.mod-buildcraft:buildcraft:6.0.8:dev'
// for more info...
// http://www.gradle.org/docs/current/userguide/artifact_dependencies_tutorial.html
// http://www.gradle.org/docs/current/userguide/dependency_management.html
deobfCompile "baubles:Baubles:${mc_version_short}:${baubles_version}"
deobfCompile "antique-atlas:antiqueatlas:${mc_version}:${antique_atlas_version}"
}
+4 -1
View File
@@ -2,4 +2,7 @@
# This is required to provide enough memory for the Minecraft decompilation process.
org.gradle.jvmargs=-Xmx3G
mc_version=1.12.2
jei_version=4.13.1.225
mc_version_short=1.12
jei_version=4.15.0.291
baubles_version=1.5.2
antique_atlas_version=4.6.3
@@ -1,5 +1,7 @@
package electroblob.wizardry;
import electroblob.wizardry.block.BlockBookshelf;
import electroblob.wizardry.inventory.ContainerArcaneWorkbench;
import electroblob.wizardry.item.ItemSpectralBow;
import electroblob.wizardry.packet.*;
import electroblob.wizardry.registry.WizardryItems;
@@ -8,6 +10,7 @@ import net.minecraft.block.state.IBlockState;
import net.minecraft.entity.Entity;
import net.minecraft.entity.EntityLivingBase;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.item.ItemArmor.ArmorMaterial;
import net.minecraft.item.ItemStack;
import net.minecraft.util.ResourceLocation;
import net.minecraft.util.SoundCategory;
@@ -19,6 +22,7 @@ import net.minecraft.util.text.translation.I18n;
import net.minecraft.world.World;
import net.minecraftforge.common.config.Property;
import javax.annotation.Nullable;
import java.util.List;
import java.util.Set;
@@ -41,9 +45,11 @@ public class CommonProxy {
public void initialiseLayers(){}
public void initialiseAnimations(){}
public void registerKeyBindings(){}
public net.minecraft.client.model.ModelBiped getWizardArmourModel(){
public net.minecraft.client.model.ModelBiped getWizardArmourModel(ArmorMaterial material){
return null;
}
@@ -74,7 +80,7 @@ public class CommonProxy {
// SECTION Items
// ===============================================================================================================
public boolean shouldDisplayDiscovered(Spell spell, ItemStack stack){
public boolean shouldDisplayDiscovered(Spell spell, @Nullable ItemStack stack){
return false;
}
@@ -100,6 +106,29 @@ public class CommonProxy {
return ((ItemSpectralBow)WizardryItems.spectral_bow).getDefaultDurabilityForDisplay(stack);
}
/**
* Translates the given key with no specified style. Client-side only; on the server this simply returns
* the given translation key. Useful whenever translation from common classes is required, e.g. item tooltips.
* @param key The unlocalised name to be translated.
* @param args The format arguments to pass into the translation, if any.
* @return The resulting translated text.
*/
public String translate(String key, Object... args){
return translate(key, new Style(), args);
}
/**
* Translates the given key and formats it with the given style. Client-side only; on the server this simply returns
* the given translation key. Useful whenever translation from common classes is required, e.g. item tooltips.
* @param key The unlocalised name to be translated.
* @param style The {@link Style} to use for the displayed text.
* @param args The format arguments to pass into the translation, if any.
* @return The resulting translated text.
*/
public String translate(String key, Style style, Object... args){
return key;
}
/** Like {@link CommonProxy#addMultiLineDescription(List, String, Style, Object...)}, but style defaults to light grey. */
public void addMultiLineDescription(List<String> tooltip, String key, Object... args){
this.addMultiLineDescription(tooltip, key, new Style().setColor(TextFormatting.GRAY), args);
@@ -169,6 +198,12 @@ public class CommonProxy {
*/
public void playMovingSound(Entity entity, SoundEvent sound, SoundCategory category, float volume, float pitch, boolean repeat){}
/**
* Plays the spell charge-up sound at the given entity.
* @param entity The source of the sound
*/
public void playChargeupSound(EntityLivingBase entity){}
/**
* Plays a continuous spell sound which moves with the given entity.
*
@@ -247,17 +282,53 @@ public class CommonProxy {
* @param intensity The amplitude of the shaking (around 10 looks about right)
*/
public void shakeScreen(EntityPlayer player, float intensity){}
/**
* Loads the given shader for the given player, if they have shaders enabled.
* @param player The player to load the shader for
* @param shader The location of the shader to load
*/
public void loadShader(EntityPlayer player, ResourceLocation shader){}
/**
* Gets the client side world using Minecraft.getMinecraft().world. <b>Only to be called client side!</b> Returns
* null on the server side.
* Gets the client-side world using {@code Minecraft.getMinecraft().world}. <b>Only to be called client side!</b>
* Returns null on the server side.
*/
public World getTheWorld(){
return null;
}
/**
* Gets the client-side player using Minecraft.getMinecraft().player. <b>Only to be called client side!</b> Returns
* null on the server side.
*/
public EntityPlayer getThePlayer(){
return null;
}
/**
* Returns true if the game is being viewed from the perspective of the given entity and is set to first-person
* view. Always returns false on the server side.
*/
public boolean isFirstPerson(Entity entity){
return false;
}
/** Returns an unmodifiable set of the string keys for all of the loaded spell HUD skins. */
public Set<String> getSpellHUDSkins(){
return null;
}
/** Notifies nearby players of a bookshelf change, causing any lectern or arcane workbench GUI (client-side) or
* container (both sides) to refresh its linked bookshelves (does not send packets). */
public void notifyBookshelfChange(World world, BlockPos pos){
for(EntityPlayer player : world.playerEntities){
if(player.getDistanceSq(pos) < BlockBookshelf.PLAYER_NOTIFY_RANGE * BlockBookshelf.PLAYER_NOTIFY_RANGE){
if(player.openContainer instanceof ContainerArcaneWorkbench){
((ContainerArcaneWorkbench)player.openContainer).refreshBookshelfSlots();
}
}
}
}
}
+477 -151
View File
@@ -1,5 +1,7 @@
package electroblob.wizardry;
import electroblob.wizardry.constants.Element;
import electroblob.wizardry.item.ItemArtefact;
import electroblob.wizardry.packet.PacketSyncSettings;
import electroblob.wizardry.packet.WizardryPacketHandler;
import electroblob.wizardry.spell.Spell;
@@ -9,9 +11,9 @@ import electroblob.wizardry.util.MagicDamage.DamageType;
import net.minecraft.block.state.IBlockState;
import net.minecraft.entity.EntityList;
import net.minecraft.entity.player.EntityPlayerMP;
import net.minecraft.item.Item;
import net.minecraft.item.ItemStack;
import net.minecraft.util.ResourceLocation;
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;
@@ -24,6 +26,7 @@ import org.apache.commons.lang3.tuple.Pair;
import java.io.File;
import java.util.*;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
/**
* Singleton class which deals with everything related to wizardry's config file. To access individual settings, use
@@ -53,14 +56,19 @@ import java.util.regex.Pattern;
*/
// For the time being, I'm sticking with the old config system because @Config doesn't support custom config entry classes
// @Config(modid = Wizardry.MODID)
@SuppressWarnings("deprecation") // Used server I18n deliberately; we want to write the comments in the actual config file in english.
public final class Settings {
// Category names
/** The unlocalised name of the gameplay config category. */
public static final String GAMEPLAY_CATEGORY = "gameplay";
/** The unlocalised name of the difficulty config category. */
public static final String DIFFICULTY_CATEGORY = "difficulty";
/** The unlocalised name of the tweaks config category. */
public static final String TWEAKS_CATEGORY = "tweaks";
/** The unlocalised name of the spells config category. */
public static final String SPELLS_CATEGORY = "spells";
/** The unlocalised name of the artefacts config category. */
public static final String ARTEFACTS_CATEGORY = "artefacts";
/** The unlocalised name of the resistances config category. */
public static final String RESISTANCES_CATEGORY = "resistances";
/** The unlocalised name of the client config category. */
@@ -85,6 +93,8 @@ public final class Settings {
/** The wizardry config file. */
private Configuration config;
private List<String> redundantKeys = new ArrayList<>();
// 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.
@@ -94,40 +104,54 @@ public final class Settings {
/** <b>[Server-only]</b> List of dimension ids in which to generate wizard towers. */
public int[] towerDimensions = {0};
/** <b>[Server-only]</b> The rarity of wizard towers, used by the world generator. Larger numbers are rarer. */
public int towerRarity = 900;
public int towerRarity = 700;
/** <b>[Server-only]</b> List of structure file locations for wizard towers without loot chests. */
public ResourceLocation[] towerFiles = {new ResourceLocation(Wizardry.MODID, "wizard_tower_0"),
new ResourceLocation(Wizardry.MODID, "wizard_tower_1"),
new ResourceLocation(Wizardry.MODID, "wizard_tower_2"),
new ResourceLocation(Wizardry.MODID, "wizard_tower_3")};
new ResourceLocation(Wizardry.MODID, "wizard_tower_1"),
new ResourceLocation(Wizardry.MODID, "wizard_tower_2"),
new ResourceLocation(Wizardry.MODID, "wizard_tower_3")};
/** <b>[Server-only]</b> List of structure file locations for wizard towers with loot chests. */
public ResourceLocation[] towerWithChestFiles = {new ResourceLocation(Wizardry.MODID, "wizard_tower_chest_0"),
new ResourceLocation(Wizardry.MODID, "wizard_tower_chest_1"),
new ResourceLocation(Wizardry.MODID, "wizard_tower_chest_2"),
new ResourceLocation(Wizardry.MODID, "wizard_tower_chest_3")};
new ResourceLocation(Wizardry.MODID, "wizard_tower_chest_1"),
new ResourceLocation(Wizardry.MODID, "wizard_tower_chest_2"),
new ResourceLocation(Wizardry.MODID, "wizard_tower_chest_3")};
/** <b>[Server-only]</b> List of dimension ids in which to generate obelisks. */
public int[] obeliskDimensions = {0, -1};
/** <b>[Server-only]</b> The rarity of obelisks, used by the world generator. Larger numbers are rarer. */
public int obeliskRarity = 800;
public int obeliskRarity = 600;
/** <b>[Server-only]</b> List of structure file locations for obelisks. */
public ResourceLocation[] obeliskFiles = {new ResourceLocation(Wizardry.MODID, "obelisk_0"),
new ResourceLocation(Wizardry.MODID, "obelisk_1"),
new ResourceLocation(Wizardry.MODID, "obelisk_2"),
new ResourceLocation(Wizardry.MODID, "obelisk_3"),
new ResourceLocation(Wizardry.MODID, "obelisk_4")};
new ResourceLocation(Wizardry.MODID, "obelisk_1"),
new ResourceLocation(Wizardry.MODID, "obelisk_2"),
new ResourceLocation(Wizardry.MODID, "obelisk_3"),
new ResourceLocation(Wizardry.MODID, "obelisk_4")};
/** <b>[Server-only]</b> List of dimension ids in which to generate shrines. */
public int[] shrineDimensions = {0, -1};
/** <b>[Server-only]</b> The rarity of shrines, used by the world generator. Larger numbers are rarer. */
public int shrineRarity = 1500;
public int shrineRarity = 1100;
/** <b>[Server-only]</b> List of structure file locations for shrines. */
public ResourceLocation[] shrineFiles = {new ResourceLocation(Wizardry.MODID, "shrine_0"),
new ResourceLocation(Wizardry.MODID, "shrine_1"),
new ResourceLocation(Wizardry.MODID, "shrine_2"),
new ResourceLocation(Wizardry.MODID, "shrine_3"),
new ResourceLocation(Wizardry.MODID, "shrine_4"),
new ResourceLocation(Wizardry.MODID, "shrine_5"),
new ResourceLocation(Wizardry.MODID, "shrine_6"),
new ResourceLocation(Wizardry.MODID, "shrine_7")};
new ResourceLocation(Wizardry.MODID, "shrine_1"),
new ResourceLocation(Wizardry.MODID, "shrine_2"),
new ResourceLocation(Wizardry.MODID, "shrine_3"),
new ResourceLocation(Wizardry.MODID, "shrine_4"),
new ResourceLocation(Wizardry.MODID, "shrine_5"),
new ResourceLocation(Wizardry.MODID, "shrine_6"),
new ResourceLocation(Wizardry.MODID, "shrine_7")};
/** <b>[Server-only]</b> List of dimension ids in which to generate library ruins. */
public int[] libraryDimensions = {0};
/** <b>[Server-only]</b> The rarity of library ruins, used by the world generator. Larger numbers are rarer. */
public int libraryRarity = 900;
/** <b>[Server-only]</b> List of structure file locations for surface library ruins. */
public ResourceLocation[] libraryFiles = {new ResourceLocation(Wizardry.MODID, "library_ruins_0"),
new ResourceLocation(Wizardry.MODID, "library_ruins_1"),
new ResourceLocation(Wizardry.MODID, "library_ruins_2"),
new ResourceLocation(Wizardry.MODID, "library_ruins_3")};
/** <b>[Server-only]</b> List of structure file locations for underground library ruins. */
public ResourceLocation[] undergroundLibraryFiles = {new ResourceLocation(Wizardry.MODID, "underground_library_ruins_0"),
new ResourceLocation(Wizardry.MODID, "underground_library_ruins_1"),
new ResourceLocation(Wizardry.MODID, "underground_library_ruins_2"),
new ResourceLocation(Wizardry.MODID, "underground_library_ruins_3")};
/** <b>[Server-only]</b> List of solid blocks (usually trees) which are ignored by the structure generators. */
public Pair<ResourceLocation, Short>[] treeBlocks = parseItemMetaStrings(DEFAULT_TREE_BLOCKS);
/** <b>[Server-only]</b> The chance for wizard towers to generate with an evil wizard and chest inside. */
@@ -147,8 +171,8 @@ public final class Settings {
public ResourceLocation[] mobLootTableWhitelist = {};
/** <b>[Server-only]</b> Blacklist for loot tables to inject additional mob drops into. */
public ResourceLocation[] mobLootTableBlacklist = toResourceLocations("entities/vex", "entities/ender_dragon",
"entities/wither", "entities/silverfish", "entities/endermite",
Wizardry.MODID + "entities/evil_wizard");
"entities/wither", "entities/silverfish", "entities/endermite",
Wizardry.MODID + "entities/evil_wizard");
/**
* <b>[Server-only]</b> Whether or not players can teleport through unbreakable blocks (e.g. bedrock) using the
* phase step spell.
@@ -166,13 +190,12 @@ public final class Settings {
public boolean playersMoveEachOther = true;
/** <b>[Server-only]</b> Whether spells cast by players can destroy blocks in the world. */
public boolean playerBlockDamage = true;
/** <b>[Server-only]</b> Whether spells cast by dispensers can destroy blocks in the world. */
public boolean dispenserBlockDamage = true;
/** <b>[Server-only]</b> Whether to revert to the old wand upgrade system, which only requires tomes of arcana. */
public boolean legacyWandLevelling = false;
/** <b>[Server-only]</b> Whether to replace Minecraft's own fireballs with wizardry fireballs. */
public boolean replaceVanillaFireballs = true;
/** <b>[Server-only]</b> Whether to replace Minecraft's distance-based fall damage calculation with an equivalent,
* velocity-based one. */
public boolean replaceVanillaFallDamage = true;
/** <b>[Server-only]</b> Whether to tweak the blindness effect to reduce follow distance when used on non-players. */
public boolean blindnessTweak = true;
/** <b>[Server-only]</b> Whether using bonemeal on grass blocks has a chance to grow crystal flowers. */
public boolean bonemealGrowsCrystalFlowers = true;
/**
@@ -239,18 +262,24 @@ public final class Settings {
public String[] damageSourceBlacklist = {};
/** <b>[Server-only]</b> Whether to print compatibility warnings to the console. */
public boolean compatibilityWarnings = true;
// TODO: Should these really be server-only?
/** <b>[Server-only]</b> Whether Baubles integration features are enabled. */
public boolean baublesIntegration = true;
// /** <b>[Server-only]</b> Whether JEI integration features are enabled. */
// public boolean jeiIntegration = true;
/** <b>[Server-only]</b> Whether JEI integration features are enabled. */
public boolean jeiIntegration = true;
/** <b>[Server-only]</b> Whether Antique Atlas integration features are enabled. */
public boolean antiqueAtlasIntegration = true;
// These are server-only because that's where markers are stored
/** <b>[Server-only]</b> Whether global markers for wizard towers are added to antique atlases. */
public boolean autoTowerMarkers = true;
/** <b>[Server-only]</b> Whether global markers for obelisks are added to antique atlases. */
public boolean autoObeliskMarkers = true;
/** <b>[Server-only]</b> Whether global markers for shrines are added to antique atlases. */
public boolean autoShrineMarkers = true;
/** <b>[Server-only]</b> Whether global markers for library ruins are added to antique atlases. */
public boolean autoLibraryMarkers = true;
/** <b>[Server-only]</b> Whether global markers for underground library ruins are added to antique atlases. */
public boolean autoUndergroundLibraryMarkers = false;
// 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.
@@ -272,8 +301,46 @@ public final class Settings {
* effect.
*/
public boolean slowTimeAffectsPlayers = true;
/** <b>[Synchronised]</b> Chance of 'misreading' an undiscovered spell and triggering a forfeit instead. */
/** <b>[Synchronised]</b> Whether passive mobs should count as allies, i.e. they should not be damaged indirectly by spells */
public boolean passiveMobsAreAllies = false;
/** <b>[Synchronised]</b> Whether to replace Minecraft's own fireballs with wizardry fireballs. */
public boolean replaceVanillaFireballs = true;
/**
* <b>[Synchronised]</b> Whether to replace Minecraft's distance-based fall damage calculation with an equivalent,
* velocity-based one.
*/
public boolean replaceVanillaFallDamage = true;
/** <b>[Synchronised]</b> Chance of 'misreading' an undiscovered spell and triggering a forfeit instead. */
public double forfeitChance = 0.2;
/** <b>[Synchronised]</b> Progression requirements for upgrading a wand to each tier. */
public int[] progressionRequirements = {1500, 3500, 6000};
/**
* <b>[Synchronised]</b> The maximum number of blocks a bookshelf can be from an arcane workbench or lectern to be
* able to link to it.
*/
public int bookshelfSearchRadius = 4;
/**
* <b>[Synchronised]</b> List of registry names of blocks that count as bookshelves for the arcane workbench and
* lectern.
*/
public Pair<ResourceLocation, Short>[] bookshelfBlocks = parseItemMetaStrings(
Wizardry.MODID + ":oak_bookshelf",
Wizardry.MODID + ":spruce_bookshelf",
Wizardry.MODID + ":birch_bookshelf",
Wizardry.MODID + ":jungle_bookshelf",
Wizardry.MODID + ":acacia_bookshelf",
Wizardry.MODID + ":dark_oak_bookshelf"
);
/** <b>[Synchronised]</b> List of registry names of items that can be placed in a bookshelf. */
public Pair<ResourceLocation, Short>[] bookItems = parseItemMetaStrings();
/**
* <b>[Synchronised*]</b> The element to render for donation perks, or null if it should not be rendered.
* <p></p>
* * Unlike the other options, <b>this is synchronised client -> server</b>, then the data for all players is sent
* back to every client when they join the game, or whenever it changes.
*/
public Element donationPerkElement = Element.MAGIC;
// 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.
@@ -300,6 +367,16 @@ public final class Settings {
public boolean booksPauseGame = true;
/** <b>[Client-only]</b> Whether to use custom shaders for certain spells. */
public boolean useShaders = true;
/** <b>[Client-only]</b> Whether to use the screen shake effect for certain spells. */
public boolean screenShake = true;
/** <b>[Client-only]</b> Whether to use the screen blink effect for teleportation spells. */
public boolean blinkEffect = true;
/** <b>[Client-only]</b> Whether to use spellcasting animations for players. */
public boolean spellcastingAnimations = true;
/** <b>[Client-only]</b> Whether to show the spell HUD when holding a wand. */
public boolean showSpellHUD = true;
/** <b>[Client-only]</b> Whether to show the charge meter when charging up a spell. */
public boolean showChargeMeter = true;
/** <b>[Client-only]</b> The position of the spell HUD. */
public GuiPosition spellHUDPosition = GuiPosition.BOTTOM_LEFT;
@@ -322,7 +399,7 @@ public final class Settings {
/** Constant array storing the names of each of the constants, in the order they are declared. */
public static final String[] names;
static {
static{
names = new String[values().length];
for(GuiPosition position : values()){
names[position.ordinal()] = position.name;
@@ -371,13 +448,17 @@ public final class Settings {
*/
void initConfig(FMLPreInitializationEvent event){
redundantKeys.clear();
config = new Configuration(new File(Wizardry.configDirectory, Wizardry.MODID + ".cfg"));
config.load();
Wizardry.logger.info("Setting up main config");
setupGeneralConfig();
setupGameplayConfig();
setupDifficultyConfig();
setupWorldgenConfig();
setupTweaksConfig();
if(event.getSide() == Side.CLIENT) setupClientConfig(); // Server has no spell HUD skins so this would crash it
setupCommandsConfig();
setupCompatibilityConfig();
@@ -393,11 +474,12 @@ public final class Settings {
void initConfigExtras(){
Wizardry.logger.info("Setting up spells config for " + Spell.getTotalSpellCount() + " spells");
setupSpellsConfig();
Wizardry.logger.info("Setting up resistances config");
Wizardry.logger.info("Setting up artefacts config");
setupArtefactsConfig();
Wizardry.logger.info("Setting up resistances config");
setupResistancesConfig();
config.save();
@@ -406,19 +488,50 @@ public final class Settings {
/** Called to save changes to the config file after it has been edited in game from the menus. */
void saveConfigChanges(){
redundantKeys.clear();
Wizardry.logger.info("Saving in-game config changes");
setupGeneralConfig();
setupGameplayConfig();
setupDifficultyConfig();
setupWorldgenConfig();
setupTweaksConfig();
setupClientConfig();
setupCommandsConfig();
setupCompatibilityConfig();
setupSpellsConfig();
setupArtefactsConfig();
setupResistancesConfig();
config.save();
}
void checkForRedundantOptions(String categoryName, Collection<String> validKeys){
ConfigCategory category = config.getCategory(categoryName);
boolean redundantKeysFound = false;
for(String key : category.keySet()){
if(!validKeys.contains(key)){
redundantKeys.add(key);
if(!redundantKeysFound){
redundantKeysFound = true;
Wizardry.logger.info("Config category {} contains redundant options:", categoryName);
}
Wizardry.logger.info(key);
}
}
if(redundantKeysFound){
Wizardry.logger.info("These options will have no effect (they are probably left over from an older version of wizardry). It is recommended to either remove them manually, or delete ebwizardry.cfg and allow a fresh config to be generated.");
}
}
/** Returns true if the config file contains keys that are not valid options (usually because of updates). */
public boolean hasRedundantKeys(){
return !redundantKeys.isEmpty();
}
/** Sends a packet to the specified player's client containing all the <b>synchronised</b> settings. */
public void sync(EntityPlayerMP player){
Wizardry.logger.info("Synchronising config settings for " + player.getName());
@@ -437,18 +550,46 @@ public final class Settings {
Property property;
List<String> keys = new ArrayList<>();
for(Spell spell : Spell.getAllSpells()){
keys.add(spell.getRegistryName().toString());
property = config.get(SPELLS_CATEGORY, spell.getRegistryName().toString(), true,
I18n.translateToLocal("spell." + spell.getUnlocalisedName() + ".desc"));
"Set to false to disable this spell");
// Uses the same config key as the spell name, because - well, that's what it's called!
property.setLanguageKey("spell." + spell.getUnlocalisedName());
Wizardry.proxy.setToNamedBooleanEntry(property);
spell.setEnabled(property.getBoolean());
spell.setEnabled(property.getBoolean(true));
}
checkForRedundantOptions(SPELLS_CATEGORY, keys);
}
private void setupGeneralConfig(){
private void setupArtefactsConfig(){
config.addCustomCategoryComment(ARTEFACTS_CATEGORY,
"Set an item to false to disable it. Disabled items will still appear in-game but will have no effect when worn. It is also advisable to remove disabled items from wizardry's (and addons') loot tables. Disable an item if it is causing problems, conflicts with another mod or creates an unintended exploit.");
Property property;
List<String> keys = new ArrayList<>();
for(Item item : Item.REGISTRY){
if(item instanceof ItemArtefact){
keys.add(item.getRegistryName().toString());
property = config.get(ARTEFACTS_CATEGORY, item.getRegistryName().toString(), true,
"Set to false to disable this item");
// Uses the same config key as the item name, because - well, that's what it's called!
property.setLanguageKey(item.getTranslationKey() + ".name");
Wizardry.proxy.setToNamedBooleanEntry(property);
((ItemArtefact)item).setEnabled(property.getBoolean());
}
}
checkForRedundantOptions(ARTEFACTS_CATEGORY, keys);
}
private void setupGameplayConfig(){
// This trick is borrowed from forge; it sorts the config options into the order you want them.
List<String> propOrder = new ArrayList<>();
@@ -457,32 +598,18 @@ public final class Settings {
config.addCustomCategoryComment(GAMEPLAY_CATEGORY, "Global settings that affect game mechanics. In multiplayer, the server/LAN host settings will apply. Please note that changing some of these settings may make the mod very difficult to play.");
property = config.get(GAMEPLAY_CATEGORY, "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.MODID + ".discovery_mode");
property = config.get(GAMEPLAY_CATEGORY, "playerBlockDamage", true,
"Whether spells cast by players can destroy blocks in the world. Wizardry makes every attempt to respect protection mods and plugins, but cannot guarantee it will work in all cases for every mod. If you need absolutely watertight anti-grief, disable this setting. (N.B. This setting only affects players. To prevent mobs from destroying blocks with magic, use the mobGriefing gamerule.)");
property.setLanguageKey("config." + Wizardry.MODID + ".player_block_damage");
Wizardry.proxy.setToNamedBooleanEntry(property);
property.setRequiresWorldRestart(true);
discoveryMode = property.getBoolean();
playerBlockDamage = property.getBoolean();
propOrder.add(property.getName());
property = config.get(GAMEPLAY_CATEGORY, "legacyWandLevelling", false,
"Controls whether wands are required to gain progression before they can be upgraded to the next tier. Enable this option to revert to the pre-4.2 system, which only requires tomes of arcana. Wands will still gain progression even when this is enabled, so if you go back to the new system you won't lose any progress.");
property.setLanguageKey("config." + Wizardry.MODID + ".legacy_wand_levelling");
property = config.get(GAMEPLAY_CATEGORY, "dispenserBlockDamage", true,
"Whether spells cast by dispensers can destroy blocks in the world. Wizardry makes every attempt to respect protection mods and plugins, but cannot guarantee it will work in all cases for every mod. If you need absolutely watertight anti-grief, disable this setting.");
property.setLanguageKey("config." + Wizardry.MODID + ".dispenser_block_damage");
Wizardry.proxy.setToNamedBooleanEntry(property);
legacyWandLevelling = property.getBoolean();
propOrder.add(property.getName());
property = config.get(GAMEPLAY_CATEGORY, "friendlyFire", FriendlyFire.ALL.name, "Controls which creatures may be damaged by your magic when allied to you. Your spells will not target your allies or creatures summoned/owned by them regardless of this setting, but this setting prevents all magic damage to allies.", FriendlyFire.names);
property.setLanguageKey("config." + Wizardry.MODID + ".friendly_fire");
friendlyFire = FriendlyFire.fromName(property.getString());
propOrder.add(property.getName());
property = config.get(GAMEPLAY_CATEGORY, "minionRevengeTargeting", true,
"Whether summoned creatures can revenge attack their owner if their owner attacks them.");
property.setLanguageKey("config." + Wizardry.MODID + ".minion_revenge_targeting");
Wizardry.proxy.setToNamedBooleanEntry(property);
property.setRequiresWorldRestart(false);
minionRevengeTargeting = property.getBoolean();
dispenserBlockDamage = property.getBoolean();
propOrder.add(property.getName());
property = config.get(GAMEPLAY_CATEGORY, "playersMoveEachOther", true,
@@ -492,13 +619,6 @@ public final class Settings {
playersMoveEachOther = property.getBoolean();
propOrder.add(property.getName());
property = config.get(GAMEPLAY_CATEGORY, "playerBlockDamage", true,
"Whether spells cast by players can destroy blocks in the world. Set to false to prevent griefing. To prevent non-players from destroying blocks with magic, use the mobGriefing gamerule.");
property.setLanguageKey("config." + Wizardry.MODID + ".player_block_damage");
Wizardry.proxy.setToNamedBooleanEntry(property);
playerBlockDamage = property.getBoolean();
propOrder.add(property.getName());
property = config.get(GAMEPLAY_CATEGORY, "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.MODID + ".telekinetic_disarmament");
@@ -520,34 +640,22 @@ public final class Settings {
worldTimeManipulation = property.getBoolean();
propOrder.add(property.getName());
property = config.get(GAMEPLAY_CATEGORY, "replaceVanillaFireballs", true,
"Whether to replace Minecraft's own fireballs with wizardry fireballs. If this is disabled, only wizardry spells will use the custom fireballs.");
property.setLanguageKey("config." + Wizardry.MODID + ".replace_vanilla_fireballs");
property = config.get(GAMEPLAY_CATEGORY, "slowTimeAffectsPlayers", true,
"Whether players are slowed when another nearby player uses the slow time spell. If this is false, mobs and projectiles will still be affected but players will move at normal speed.");
property.setLanguageKey("config." + Wizardry.MODID + ".slow_time_affects_players");
Wizardry.proxy.setToNamedBooleanEntry(property);
replaceVanillaFireballs = property.getBoolean();
propOrder.add(property.getName());
property = config.get(GAMEPLAY_CATEGORY, "replaceVanillaFallDamage", true,
"Whether to replace Minecraft's distance-based fall damage calculation with an equivalent, velocity-based one. This is done such that mobs in freefall will take exactly the same damage as normal, so it will not break falling-based mob farms. Disable this if you experience falling-related weirdness! If this is disabled, some spells will use a more simplistic method of resetting the player's fall damage in certain cases.");
property.setLanguageKey("config." + Wizardry.MODID + ".replace_vanilla_fall_damage");
Wizardry.proxy.setToNamedBooleanEntry(property);
replaceVanillaFallDamage = property.getBoolean();
property.setRequiresWorldRestart(true);
slowTimeAffectsPlayers = property.getBoolean();
propOrder.add(property.getName());
property = config.get(GAMEPLAY_CATEGORY, "creativeBypassesArcaneLock", true,
"Whether any player in creative mode can bypass arcane-locked blocks. If this is false, players must also be op in order to do so.");
property.setLanguageKey("config." + Wizardry.MODID + ".creative_bypasses_arcane_lock");
Wizardry.proxy.setToNamedBooleanEntry(property);
property.setRequiresWorldRestart(true);
creativeBypassesArcaneLock = property.getBoolean();
propOrder.add(property.getName());
property = config.get(GAMEPLAY_CATEGORY, "slowTimeAffectsPlayers", true,
"Whether players are slowed when another nearby player uses the slow time spell. If this is false, mobs and projectiles will still be affected but players will move at normal speed.");
property.setLanguageKey("config." + Wizardry.MODID + ".slow_time_affects_players");
Wizardry.proxy.setToNamedBooleanEntry(property);
slowTimeAffectsPlayers = property.getBoolean();
propOrder.add(property.getName());
property = config.get(GAMEPLAY_CATEGORY, "bonemealGrowsCrystalFlowers", true,
"Whether using bonemeal on grass blocks has a chance to grow crystal flowers.");
property.setLanguageKey("config." + Wizardry.MODID + ".bonemeal_grows_crystal_flowers");
@@ -555,33 +663,88 @@ public final class Settings {
bonemealGrowsCrystalFlowers = property.getBoolean();
propOrder.add(property.getName());
property = config.get(GAMEPLAY_CATEGORY, "mobLootTableWhitelist", new String[0], "Whitelist for loot tables to inject additional mob drops (as specified in loot_tables/entities/mob_additions.json) into. Wizardry makes a best guess as to which loot tables belong to hostile mobs, but this may not always be correct or appropriate; add loot table locations (not entity IDs) to this list to manually include them.");
property.setLanguageKey("config." + Wizardry.MODID + ".mob_loot_table_whitelist");
property.setRequiresMcRestart(true);
mobLootTableWhitelist = getResourceLocationList(property);
checkForRedundantOptions(GAMEPLAY_CATEGORY, propOrder); // Must be before the order is set!
config.setCategoryPropertyOrder(GAMEPLAY_CATEGORY, propOrder);
}
private void setupDifficultyConfig(){
List<String> propOrder = new ArrayList<>();
Property property;
config.addCustomCategoryComment(DIFFICULTY_CATEGORY, "Settings that affect the mod's difficulty. In multiplayer, the server/LAN host settings will apply.");
property = config.get(DIFFICULTY_CATEGORY, "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.MODID + ".discovery_mode");
Wizardry.proxy.setToNamedBooleanEntry(property);
property.setRequiresWorldRestart(true);
discoveryMode = property.getBoolean();
propOrder.add(property.getName());
property = config.get(GAMEPLAY_CATEGORY, "mobLootTableBlacklist", new String[]{"entities/vex", "entities/ender_dragon", "entities/wither", Wizardry.MODID + ":entities/evil_wizard"}, "Blacklist for loot tables to inject additional mob drops (as specified in loot_tables/entities/mob_additions.json) into. Wizardry makes a best guess as to which loot tables belong to hostile mobs, but this may not always be correct or appropriate; add loot table locations (not entity IDs) to this list to manually exclude them.");
property.setLanguageKey("config." + Wizardry.MODID + ".mob_loot_table_blacklist");
property.setRequiresMcRestart(true);
mobLootTableBlacklist = getResourceLocationList(property);
property = config.get(DIFFICULTY_CATEGORY, "legacyWandLevelling", false,
"Controls whether wands are required to gain progression before they can be upgraded to the next tier. Enable this option to revert to the pre-4.2 system, which only requires tomes of arcana. Wands will still gain progression even when this is enabled, so if you go back to the new system you won't lose any progress.");
property.setLanguageKey("config." + Wizardry.MODID + ".legacy_wand_levelling");
Wizardry.proxy.setToNamedBooleanEntry(property);
legacyWandLevelling = property.getBoolean();
propOrder.add(property.getName());
property = config.get(GAMEPLAY_CATEGORY, "mobSpawnDimensions", new int[]{0},
"List of dimension ids in which wizardry's hostile mobs can spawn.");
property.setLanguageKey("config." + Wizardry.MODID + ".mob_spawn_dimensions");
property.setRequiresMcRestart(true);
mobSpawnDimensions = property.getIntList();
property = config.get(DIFFICULTY_CATEGORY, "friendlyFire", FriendlyFire.ALL.name,
"Controls which creatures may be damaged by your magic when allied to you. Your spells will not target your allies or creatures summoned/owned by them regardless of this setting, but this setting prevents all magic damage to allies.", FriendlyFire.names);
property.setLanguageKey("config." + Wizardry.MODID + ".friendly_fire");
friendlyFire = FriendlyFire.fromName(property.getString());
propOrder.add(property.getName());
property = config.get(GAMEPLAY_CATEGORY, "mobSpawnBiomeBlacklist", new String[]{"mushroom_island", "mushroom_island_shore"},
"List of names of biomes in which wizardry's hostile mobs cannot spawn. Biome names are not case-sensitive. For mod biomes, prefix with the mod ID (e.g. biomesoplenty:mystic_grove).");
property.setLanguageKey("config." + Wizardry.MODID + ".mob_spawn_biome_blacklist");
property.setRequiresMcRestart(true);
mobSpawnBiomeBlacklist = getResourceLocationList(property);
property = config.get(DIFFICULTY_CATEGORY, "passiveMobsAreAllies", false,
"Whether passive mobs should count as allies, i.e. they should not be damaged indirectly by spells.");
property.setLanguageKey("config." + Wizardry.MODID + ".passive_mobs_are_allies");
Wizardry.proxy.setToNamedBooleanEntry(property);
property.setRequiresWorldRestart(true);
passiveMobsAreAllies = property.getBoolean();
propOrder.add(property.getName());
property = config.get(GAMEPLAY_CATEGORY, "evilWizardSpawnRate", 3,
property = config.get(DIFFICULTY_CATEGORY, "minionRevengeTargeting", true,
"Whether summoned creatures can revenge attack their owner if their owner attacks them.");
property.setLanguageKey("config." + Wizardry.MODID + ".minion_revenge_targeting");
Wizardry.proxy.setToNamedBooleanEntry(property);
property.setRequiresWorldRestart(false);
minionRevengeTargeting = property.getBoolean();
propOrder.add(property.getName());
property = config.get(DIFFICULTY_CATEGORY, "forfeitChance", 0.2,
"The chance to 'misread' an undiscovered spell and trigger a forfeit instead. Setting this to 0 effectively disables the forfeit mechanic. Has no effect if discovery mode is disabled.",
0, 1);
property.setLanguageKey("config." + Wizardry.MODID + ".forfeit_chance");
Wizardry.proxy.setToNumberSliderEntry(property);
property.setRequiresWorldRestart(true);
forfeitChance = property.getDouble();
propOrder.add(property.getName());
property = config.get(DIFFICULTY_CATEGORY, "progressionRequirements", new int[]{1500, 3500, 6000},
"The amount of progression required to upgrade a wand to each tier (apprentice, advanced and master respectively).");
property.setLanguageKey("config." + Wizardry.MODID + ".progression_requirements");
property.setRequiresWorldRestart(true);
progressionRequirements = property.getIntList();
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(DIFFICULTY_CATEGORY, "playerDamageScaling", 1.0,
"Global damage scaling factor for the damage dealt by players casting spells, relative to 1.", 0, 255);
property.setLanguageKey("config." + Wizardry.MODID + ".player_damage_scaling");
playerDamageScale = property.getDouble();
propOrder.add(property.getName());
property = config.get(DIFFICULTY_CATEGORY, "npcDamageScaling", 1.0,
"Global damage scaling factor for the damage dealt by NPCs casting spells, relative to 1.", 0, 255);
property.setLanguageKey("config." + Wizardry.MODID + ".npc_damage_scaling");
npcDamageScale = property.getDouble();
propOrder.add(property.getName());
property = config.get(DIFFICULTY_CATEGORY, "evilWizardSpawnRate", 3,
"Spawn rate for naturally-spawned evil wizards; higher numbers mean more evil wizards will spawn. 5 is equivalent to witches, 100 is equivalent to zombies, skeletons and creepers. Set to 0 to disable evil wizard spawning entirely.",
0, 100);
property.setLanguageKey("config." + Wizardry.MODID + ".evil_wizard_spawn_rate");
@@ -590,7 +753,7 @@ public final class Settings {
evilWizardSpawnRate = property.getInt();
propOrder.add(property.getName());
property = config.get(GAMEPLAY_CATEGORY, "iceWraithSpawnRate", 3,
property = config.get(DIFFICULTY_CATEGORY, "iceWraithSpawnRate", 3,
"Spawn rate for naturally-spawned ice wraiths; higher numbers mean more ice wraiths will spawn. 5 is equivalent to witches, 100 is equivalent to zombies, skeletons and creepers. Set to 0 to disable ice wraith spawning entirely.",
0, 100);
property.setLanguageKey("config." + Wizardry.MODID + ".ice_wraith_spawn_rate");
@@ -599,7 +762,7 @@ public final class Settings {
iceWraithSpawnRate = property.getInt();
propOrder.add(property.getName());
property = config.get(GAMEPLAY_CATEGORY, "lightningWraithSpawnRate", 1,
property = config.get(DIFFICULTY_CATEGORY, "lightningWraithSpawnRate", 1,
"Spawn rate for naturally-spawned lightning wraiths; higher numbers mean more lightning wraiths will spawn. 5 is equivalent to witches, 100 is equivalent to zombies, skeletons and creepers. Set to 0 to disable lightning wraith spawning entirely.",
0, 100);
property.setLanguageKey("config." + Wizardry.MODID + ".lightning_wraith_spawn_rate");
@@ -608,79 +771,133 @@ public final class Settings {
lightningWraithSpawnRate = property.getInt();
propOrder.add(property.getName());
property = config.get(GAMEPLAY_CATEGORY, "forfeitChance", 0.2,
"The chance to 'misread' an undiscovered spell and trigger a forfeit instead. Setting this to 0 effectively disables the forfeit mechanic. Has no effect if discovery mode is disabled.",
0, 1);
property.setLanguageKey("config." + Wizardry.MODID + ".forfeit_chance");
Wizardry.proxy.setToNumberSliderEntry(property);
forfeitChance = property.getDouble();
checkForRedundantOptions(DIFFICULTY_CATEGORY, propOrder); // Must be before the order is set!
config.setCategoryPropertyOrder(DIFFICULTY_CATEGORY, propOrder);
}
private void setupTweaksConfig(){
List<String> propOrder = new ArrayList<>();
Property property;
config.addCustomCategoryComment(TWEAKS_CATEGORY, "Assorted settings for tweaking the mod's behaviour. In multiplayer, the server/LAN host settings will apply.");
property = config.get(TWEAKS_CATEGORY, "replaceVanillaFireballs", true,
"Whether to replace Minecraft's own fireballs with wizardry fireballs. If this is disabled, only wizardry spells will use the custom fireballs.");
property.setLanguageKey("config." + Wizardry.MODID + ".replace_vanilla_fireballs");
Wizardry.proxy.setToNamedBooleanEntry(property);
property.setRequiresWorldRestart(true);
replaceVanillaFireballs = 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(GAMEPLAY_CATEGORY, "playerDamageScaling", 1.0,
"Global damage scaling factor for the damage dealt by players casting spells, relative to 1.", 0, 255);
property.setLanguageKey("config." + Wizardry.MODID + ".player_damage_scaling");
playerDamageScale = property.getDouble();
property = config.get(TWEAKS_CATEGORY, "replaceVanillaFallDamage", true,
"Whether to replace Minecraft's distance-based fall damage calculation with an equivalent, velocity-based one. This is done such that mobs in freefall will take exactly the same damage as normal, so it will not break falling-based mob farms. Disable this if you experience falling-related weirdness! If this is disabled, some spells will use a more simplistic method of resetting the player's fall damage in certain cases.");
property.setLanguageKey("config." + Wizardry.MODID + ".replace_vanilla_fall_damage");
Wizardry.proxy.setToNamedBooleanEntry(property);
property.setRequiresWorldRestart(true);
replaceVanillaFallDamage = property.getBoolean();
propOrder.add(property.getName());
property = config.get(GAMEPLAY_CATEGORY, "npcDamageScaling", 1.0,
"Global damage scaling factor for the damage dealt by NPCs casting spells, relative to 1.", 0, 255);
property.setLanguageKey("config." + Wizardry.MODID + ".npc_damage_scaling");
npcDamageScale = property.getDouble();
property = config.get(TWEAKS_CATEGORY, "blindnessTweak", true,
"Whether to tweak the blindness effect to reduce follow distance when used on non-players. This automatically disables itself in favour of Potion Core's implementation if installed.");
property.setLanguageKey("config." + Wizardry.MODID + ".blindness_tweak");
Wizardry.proxy.setToNamedBooleanEntry(property);
blindnessTweak = property.getBoolean();
propOrder.add(property.getName());
property = config.get(GAMEPLAY_CATEGORY, "summonedCreatureTargetsWhitelist", new String[0],
property = config.get(TWEAKS_CATEGORY, "mobLootTableWhitelist", new String[0], "Whitelist for loot tables to inject additional mob drops (as specified in loot_tables/entities/mob_additions.json) into. Wizardry makes a best guess as to which loot tables belong to hostile mobs, but this may not always be correct or appropriate; add loot table locations (not entity IDs) to this list to manually include them.");
property.setLanguageKey("config." + Wizardry.MODID + ".mob_loot_table_whitelist");
property.setRequiresMcRestart(true);
mobLootTableWhitelist = getResourceLocationList(property);
propOrder.add(property.getName());
property = config.get(TWEAKS_CATEGORY, "mobLootTableBlacklist", new String[]{"entities/vex", "entities/ender_dragon", "entities/wither", Wizardry.MODID + ":entities/evil_wizard"}, "Blacklist for loot tables to inject additional mob drops (as specified in loot_tables/entities/mob_additions.json) into. Wizardry makes a best guess as to which loot tables belong to hostile mobs, but this may not always be correct or appropriate; add loot table locations (not entity IDs) to this list to manually exclude them.");
property.setLanguageKey("config." + Wizardry.MODID + ".mob_loot_table_blacklist");
property.setRequiresMcRestart(true);
mobLootTableBlacklist = getResourceLocationList(property);
propOrder.add(property.getName());
property = config.get(TWEAKS_CATEGORY, "mobSpawnDimensions", new int[]{0},
"List of dimension ids in which wizardry's hostile mobs can spawn.");
property.setLanguageKey("config." + Wizardry.MODID + ".mob_spawn_dimensions");
property.setRequiresMcRestart(true);
mobSpawnDimensions = property.getIntList();
propOrder.add(property.getName());
property = config.get(TWEAKS_CATEGORY, "mobSpawnBiomeBlacklist", new String[]{"mushroom_island", "mushroom_island_shore"},
"List of names of biomes in which wizardry's hostile mobs cannot spawn. Biome names are not case-sensitive. For mod biomes, prefix with the mod ID (e.g. biomesoplenty:mystic_grove).");
property.setLanguageKey("config." + Wizardry.MODID + ".mob_spawn_biome_blacklist");
property.setRequiresMcRestart(true);
mobSpawnBiomeBlacklist = getResourceLocationList(property);
propOrder.add(property.getName());
property = config.get(TWEAKS_CATEGORY, "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. SoundLoopSpellEntity names are not case sensitive. For mod entities, prefix with the mod ID (e.g. " + Wizardry.MODID + ":wizard).");
property.setLanguageKey("config." + Wizardry.MODID + ".summoned_creature_targets_whitelist");
property.setRequiresWorldRestart(true);
summonedCreatureTargetsWhitelist = getResourceLocationList(property);
propOrder.add(property.getName());
property = config.get(GAMEPLAY_CATEGORY, "summonedCreatureTargetsBlacklist", new String[]{"creeper"},
property = config.get(TWEAKS_CATEGORY, "summonedCreatureTargetsBlacklist", new String[]{"creeper"},
"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!). SoundLoopSpellEntity names are not case sensitive. For mod entities, prefix with the mod ID (e.g. " + Wizardry.MODID + ":wizard).");
property.setLanguageKey("config." + Wizardry.MODID + ".summoned_creature_targets_blacklist");
property.setRequiresWorldRestart(true);
summonedCreatureTargetsBlacklist = getResourceLocationList(property);
propOrder.add(property.getName());
property = config.get(GAMEPLAY_CATEGORY, "mindControlTargetsBlacklist", new String[]{},
property = config.get(TWEAKS_CATEGORY, "mindControlTargetsBlacklist", new String[]{},
"List of names of entities which cannot be mind controlled, in addition to the defaults. Add creatures to this list if allowing them to be mind-controlled causes problems or could be exploited. SoundLoopSpellEntity names are not case sensitive. For mod entities, prefix with the mod ID (e.g. " + Wizardry.MODID + ":wizard).");
property.setLanguageKey("config." + Wizardry.MODID + ".mind_control_targets_blacklist");
property.setRequiresWorldRestart(true);
mindControlTargetsBlacklist = getResourceLocationList(property);
propOrder.add(property.getName());
property = config.get(GAMEPLAY_CATEGORY, "pocketFurnaceItemBlacklist", new String[]{"cobblestone", "netherrack"},
property = config.get(TWEAKS_CATEGORY, "pocketFurnaceItemBlacklist", new String[]{"cobblestone", "netherrack"},
"List of registry names of blocks or items which cannot be smelted by the pocket furnace spell, in addition to armour, tools and weapons. Block/item names are not case sensitive. For mod items, prefix with the mod ID (e.g. " + Wizardry.MODID + ":crystal_ore).");
property.setLanguageKey("config." + Wizardry.MODID + ".pocket_furnace_item_blacklist");
property.setRequiresWorldRestart(true);
pocketFurnaceItemBlacklist = parseItemMetaStrings(property.getStringList());
propOrder.add(property.getName());
property = config.get(GAMEPLAY_CATEGORY, "divinationOreWhitelist", new String[0], "List of registry names of ore blocks which can be detected by the divination spell. Block names are not case sensitive. For mod blocks, prefix with the mod ID (e.g. " + Wizardry.MODID + ":crystal_ore).");
property = config.get(TWEAKS_CATEGORY, "divinationOreWhitelist", new String[0], "List of registry names of ore blocks which can be detected by the divination spell. Block names are not case sensitive. For mod blocks, prefix with the mod ID (e.g. " + Wizardry.MODID + ":crystal_ore).");
property.setLanguageKey("config." + Wizardry.MODID + ".divination_ore_whitelist");
property.setRequiresWorldRestart(true);
divinationOreWhitelist = parseItemMetaStrings(property.getStringList());
propOrder.add(property.getName());
property = config.get(GAMEPLAY_CATEGORY, "swordItemWhitelist", new String[0], "List of registry names of items which should count as swords for imbuement spells. Most swords should work automatically, but those that don't can be added manually here. Item names are not case sensitive. For mod items, prefix with the mod ID (e.g. tconstruct:broadsword).");
property = config.get(TWEAKS_CATEGORY, "swordItemWhitelist", new String[0], "List of registry names of items which should count as swords for imbuement spells. Most swords should work automatically, but those that don't can be added manually here. Item names are not case sensitive. For mod items, prefix with the mod ID (e.g. tconstruct:broadsword).");
property.setLanguageKey("config." + Wizardry.MODID + ".sword_item_whitelist");
property.setRequiresWorldRestart(true);
swordItemWhitelist = parseItemMetaStrings(property.getStringList());
propOrder.add(property.getName());
property = config.get(GAMEPLAY_CATEGORY, "bowItemWhitelist", new String[0], "List of registry names of items which should count as bows for imbuement spells. Most bows should work automatically, but those that don't can be added manually here. Item names are not case sensitive. For mod items, prefix with the mod ID (e.g. tconstruct:shortbow).");
property = config.get(TWEAKS_CATEGORY, "bowItemWhitelist", new String[0], "List of registry names of items which should count as bows for imbuement spells. Most bows should work automatically, but those that don't can be added manually here. Item names are not case sensitive. For mod items, prefix with the mod ID (e.g. tconstruct:shortbow).");
property.setLanguageKey("config." + Wizardry.MODID + ".bow_item_whitelist");
property.setRequiresWorldRestart(true);
bowItemWhitelist = parseItemMetaStrings(property.getStringList());
propOrder.add(property.getName());
property = config.get(GAMEPLAY_CATEGORY, "currencyItems", new String[]{"gold_ingot 3", "emerald 6"}, "List of registry names of items which wizard trades can use as currency (in the first slot; the second slot is unaffected). Each entry in this list should consist of an item registry name, followed by a single space, then an integer which defines the 'value' of the item. Higher values mean fewer of that currency item are required for a given trade.",
property = config.get(TWEAKS_CATEGORY, "bookshelfBlocks", new String[]{Wizardry.MODID + ":oak_bookshelf", Wizardry.MODID + ":spruce_bookshelf", Wizardry.MODID + ":birch_bookshelf", Wizardry.MODID + ":jungle_bookshelf", Wizardry.MODID + ":acacia_bookshelf", Wizardry.MODID + ":dark_oak_bookshelf"}, "List of registry names of blocks that count as bookshelves for the arcane workbench and lectern. Block names are not case sensitive. For mod blocks, prefix with the mod ID (e.g. " + Wizardry.MODID + ":oak_bookshelf).");
property.setLanguageKey("config." + Wizardry.MODID + ".bookshelf_blocks");
property.setRequiresWorldRestart(true);
bookshelfBlocks = parseItemMetaStrings(property.getStringList());
propOrder.add(property.getName());
property = config.get(TWEAKS_CATEGORY, "bookItems", new String[0], "List of registry names of items which can be placed in a bookshelf, in addition to the defaults. Item names are not case sensitive. For mod items, prefix with the mod ID (e.g. thaumcraft:thaumonomicon).");
property.setLanguageKey("config." + Wizardry.MODID + ".book_items");
property.setRequiresWorldRestart(true);
bookItems = parseItemMetaStrings(property.getStringList());
propOrder.add(property.getName());
property = config.get(TWEAKS_CATEGORY, "bookshelfSearchRadius", 4,
"The maximum number of blocks a bookshelf can be from an arcane workbench or lectern to be able to link to it.",
1, 10);
property.setLanguageKey("config." + Wizardry.MODID + ".bookshelf_search_radius");
Wizardry.proxy.setToNumberSliderEntry(property);
property.setRequiresWorldRestart(true);
bookshelfSearchRadius = property.getInt();
propOrder.add(property.getName());
property = config.get(TWEAKS_CATEGORY, "currencyItems", new String[]{"gold_ingot 3", "emerald 6"}, "List of registry names of items which wizard trades can use as currency (in the first slot; the second slot is unaffected). Each entry in this list should consist of an item registry name, followed by a single space, then an integer which defines the 'value' of the item. Higher values mean fewer of that currency item are required for a given trade.",
Pattern.compile("[A-Za-z0-9:_]+ [0-9]+"));
property.setLanguageKey("config." + Wizardry.MODID + ".currency_items");
property.setRequiresWorldRestart(true);
propOrder.add(property.getName());
currencyItems = new HashMap<>();
for(String string : property.getStringList()){
@@ -690,14 +907,15 @@ public final class Settings {
Wizardry.logger.warn("Invalid entry in currency items: {}", string);
continue; // Ignore invalid entries, the pattern above should ensure this never happens
}
try {
try{
currencyItems.put(parseItemMetaString(args[0]), Integer.parseInt(args[1]));
}catch(NumberFormatException e){
Wizardry.logger.warn("Invalid integer in currency items: {}", args[1]);
}
}
config.setCategoryPropertyOrder(GAMEPLAY_CATEGORY, propOrder);
checkForRedundantOptions(TWEAKS_CATEGORY, propOrder); // Must be before the order is set!
config.setCategoryPropertyOrder(TWEAKS_CATEGORY, propOrder);
}
@@ -793,7 +1011,34 @@ public final class Settings {
shrineFiles = getResourceLocationList(property);
propOrder.add(property.getName());
property = config.get(GAMEPLAY_CATEGORY, "treeBlocks", DEFAULT_TREE_BLOCKS, "List of registry names of blocks which can be overwritten by wizardry's structure generators, affecting both fast and fancy structure generation. Most tree blocks and other foliage should work automatically, but those that don't can be added manually here. Block names are not case sensitive. For mod blocks, prefix with the mod ID (e.g. dynamictrees:oakbranch).");
property = config.get(WORLDGEN_CATEGORY, "libraryDimensions", new int[]{0}, "List of dimension ids in which library ruins will generate. Remove all dimensions to disable library ruins completely.");
property.setLanguageKey("config." + Wizardry.MODID + ".library_dimensions");
property.setRequiresWorldRestart(true);
libraryDimensions = property.getIntList();
propOrder.add(property.getName());
property = config.get(WORLDGEN_CATEGORY, "libraryRarity", 900, "Rarity of library ruins. 1 in this many chunks will contain a library ruin, meaning higher numbers are rarer.", 20, 5000);
property.setLanguageKey("config." + Wizardry.MODID + ".library_rarity");
property.setRequiresWorldRestart(true);
Wizardry.proxy.setToNumberSliderEntry(property);
libraryRarity = property.getInt();
propOrder.add(property.getName());
property = config.get(WORLDGEN_CATEGORY, "libraryFiles", new String[]{Wizardry.MODID + ":library_ruins_0", Wizardry.MODID + ":library_ruins_1", Wizardry.MODID + ":library_ruins_2", Wizardry.MODID + ":library_ruins_3"},
"List of structure file locations for surface library ruins. One of these files will be randomly selected each time a surface library ruin is generated. File locations are of the format [mod id]:[filename], which refers to the file assets/[mod id]/structures/[filename].nbt. Duplicate entries are permitted, allowing for simple weighting without duplicating the structure files themselves. This list should not be empty; to disable library ruins, use the library dimensions setting.");
property.setLanguageKey("config." + Wizardry.MODID + ".library_files");
property.setRequiresWorldRestart(true);
libraryFiles = getResourceLocationList(property);
propOrder.add(property.getName());
property = config.get(WORLDGEN_CATEGORY, "undergroundLibraryFiles", new String[]{Wizardry.MODID + ":underground_library_ruins_0", Wizardry.MODID + ":underground_library_ruins_1", Wizardry.MODID + ":underground_library_ruins_2", Wizardry.MODID + ":underground_library_ruins_3"},
"List of structure file locations for underground library ruins. One of these files will be randomly selected each time an underground library ruin is generated. File locations are of the format [mod id]:[filename], which refers to the file assets/[mod id]/structures/[filename].nbt. Duplicate entries are permitted, allowing for simple weighting without duplicating the structure files themselves. This list should not be empty; to disable library ruins, use the library dimensions setting.");
property.setLanguageKey("config." + Wizardry.MODID + ".underground_library_files");
property.setRequiresWorldRestart(true);
undergroundLibraryFiles = getResourceLocationList(property);
propOrder.add(property.getName());
property = config.get(WORLDGEN_CATEGORY, "treeBlocks", DEFAULT_TREE_BLOCKS, "List of registry names of blocks which can be overwritten by wizardry's structure generators, affecting both fast and fancy structure generation. Most tree blocks and other foliage should work automatically, but those that don't can be added manually here. Block names are not case sensitive. For mod blocks, prefix with the mod ID (e.g. dynamictrees:oakbranch).");
property.setLanguageKey("config." + Wizardry.MODID + ".tree_blocks");
property.setRequiresWorldRestart(true);
treeBlocks = parseItemMetaStrings(property.getStringList());
@@ -817,6 +1062,7 @@ public final class Settings {
lootInjectionLocations = getResourceLocationList(property);
propOrder.add(property.getName());
checkForRedundantOptions(WORLDGEN_CATEGORY, propOrder); // Must be before the order is set!
config.setCategoryPropertyOrder(WORLDGEN_CATEGORY, propOrder);
}
@@ -843,6 +1089,20 @@ public final class Settings {
reverseScrollDirection = property.getBoolean();
propOrder.add(property.getName());
property = config.get(CLIENT_CATEGORY, "showSpellHUD", true, "Whether to show the spell HUD in the corner of the screen when holding a wand.");
property.setLanguageKey("config." + Wizardry.MODID + ".show_spell_hud");
property.setRequiresWorldRestart(false);
Wizardry.proxy.setToNamedBooleanEntry(property);
showSpellHUD = property.getBoolean();
propOrder.add(property.getName());
property = config.get(CLIENT_CATEGORY, "showChargeMeter", true, "Whether to show the spell charge-up meter around the crosshairs when charging up a spell.");
property.setLanguageKey("config." + Wizardry.MODID + ".show_charge_meter");
property.setRequiresWorldRestart(false);
Wizardry.proxy.setToNamedBooleanEntry(property);
showChargeMeter = property.getBoolean();
propOrder.add(property.getName());
property = config.get(CLIENT_CATEGORY, "spellHUDPosition", GuiPosition.BOTTOM_LEFT.name, "The position of the spell HUD.", GuiPosition.names);
property.setLanguageKey("config." + Wizardry.MODID + ".spell_hud_position");
spellHUDPosition = GuiPosition.fromName(property.getString());
@@ -878,6 +1138,33 @@ public final class Settings {
useShaders = property.getBoolean();
propOrder.add(property.getName());
property = config.get(CLIENT_CATEGORY, "screenShake", true, "Whether to use the screen shake effect for certain spells.");
property.setLanguageKey("config." + Wizardry.MODID + ".screen_shake");
Wizardry.proxy.setToNamedBooleanEntry(property);
screenShake = property.getBoolean();
propOrder.add(property.getName());
property = config.get(CLIENT_CATEGORY, "blinkEffect", true, "Whether to use the screen blink effect for teleportation spells.");
property.setLanguageKey("config." + Wizardry.MODID + ".blink_effect");
Wizardry.proxy.setToNamedBooleanEntry(property);
blinkEffect = property.getBoolean();
propOrder.add(property.getName());
property = config.get(CLIENT_CATEGORY, "spellcastingAnimations", true, "Whether to use custom animations for players casting spells. Disable this option if it conflicts with other mods that add player animations.");
property.setLanguageKey("config." + Wizardry.MODID + ".spellcasting_animations");
property.setRequiresMcRestart(true);
Wizardry.proxy.setToNamedBooleanEntry(property);
spellcastingAnimations = property.getBoolean();
propOrder.add(property.getName());
List<String> elementNames = Arrays.stream(Element.values()).map(Element::getName).collect(Collectors.toList());
elementNames.add("");
property = config.get(CLIENT_CATEGORY, "donationPerkElement", Element.MAGIC.getName(), "The element of the flying companion orb rendered on donor and contributor players, leave empty to disable the effect. If you're one of them, this setting will change how all players see you (if not, it won't do anything).", elementNames.toArray(new String[0]));
property.setLanguageKey("config." + Wizardry.MODID + ".donation_perk_element");
donationPerkElement = Element.fromName(property.getString(), null); // Fallback to null for no element
propOrder.add(property.getName());
checkForRedundantOptions(CLIENT_CATEGORY, propOrder); // Must be before the order is set!
config.setCategoryPropertyOrder(CLIENT_CATEGORY, propOrder);
}
@@ -924,6 +1211,7 @@ public final class Settings {
alliesCommandName = property.getString();
propOrder.add(property.getName());
checkForRedundantOptions(COMMANDS_CATEGORY, propOrder); // Must be before the order is set!
config.setCategoryPropertyOrder(COMMANDS_CATEGORY, propOrder);
}
@@ -1001,6 +1289,7 @@ public final class Settings {
}
propOrder.add(property.getName());
checkForRedundantOptions(RESISTANCES_CATEGORY, propOrder); // Must be before the order is set!
config.setCategoryPropertyOrder(RESISTANCES_CATEGORY, propOrder);
}
@@ -1034,13 +1323,13 @@ public final class Settings {
baublesIntegration = property.getBoolean();
propOrder.add(property.getName());
// property = config.get(COMPATIBILITY_CATEGORY, "jeiIntegration", true,
// "If JEI (Just Enough Items) is installed, controls whether JEI integration features are enabled. If this is disabled, wizardry will always behave as if JEI is not installed.");
// property.setLanguageKey("config." + Wizardry.MODID + ".jei_integration");
// property.setRequiresMcRestart(true);
// Wizardry.proxy.setToNamedBooleanEntry(property);
// jeiIntegration = property.getBoolean();
// propOrder.add(property.getName());
property = config.get(COMPATIBILITY_CATEGORY, "jeiIntegration", true,
"If JEI (Just Enough Items) is installed, controls whether JEI integration features are enabled. If this is disabled, wizardry will always behave as if JEI is not installed.");
property.setLanguageKey("config." + Wizardry.MODID + ".jei_integration");
property.setRequiresMcRestart(true);
Wizardry.proxy.setToNamedBooleanEntry(property);
jeiIntegration = property.getBoolean();
propOrder.add(property.getName());
property = config.get(COMPATIBILITY_CATEGORY, "antiqueAtlasIntegration", true,
"If Antique Atlas is installed, controls whether Antique Atlas integration features are enabled. If this is disabled, wizardry will always behave as if Antique Atlas is not installed.");
@@ -1074,6 +1363,23 @@ public final class Settings {
autoShrineMarkers = property.getBoolean();
propOrder.add(property.getName());
property = config.get(COMPATIBILITY_CATEGORY, "autoPlaceLibraryMarkers", true,
"Controls whether wizardry automatically places antique atlas markers at the locations of library ruins.");
property.setLanguageKey("config." + Wizardry.MODID + ".auto_place_library_markers");
property.setRequiresMcRestart(true);
Wizardry.proxy.setToNamedBooleanEntry(property);
autoLibraryMarkers = property.getBoolean();
propOrder.add(property.getName());
property = config.get(COMPATIBILITY_CATEGORY, "autoPlaceUndergroundLibraryMarkers", false,
"Controls whether wizardry automatically places antique atlas markers at the locations of underground library ruins.");
property.setLanguageKey("config." + Wizardry.MODID + ".auto_place_underground_library_markers");
property.setRequiresMcRestart(true);
Wizardry.proxy.setToNamedBooleanEntry(property);
autoUndergroundLibraryMarkers = property.getBoolean();
propOrder.add(property.getName());
checkForRedundantOptions(COMPATIBILITY_CATEGORY, propOrder); // Must be before the order is set!
config.setCategoryPropertyOrder(COMPATIBILITY_CATEGORY, propOrder);
}
@@ -1088,7 +1394,7 @@ public final class Settings {
return Arrays.stream(strings).map(s -> new ResourceLocation(s.toLowerCase(Locale.ROOT).trim())).toArray(ResourceLocation[]::new);
}
/** Applies {@link Settings#parseItemMetaString(String)} to each input string and returns and array of the resulting
/** Applies {@link Settings#parseItemMetaString(String)} to each input string and returns an array of the resulting
* {@link Pair}s. */
@SuppressWarnings("unchecked") // Shut up java
public static Pair<ResourceLocation, Short>[] parseItemMetaStrings(String... strings){
@@ -1116,16 +1422,36 @@ public final class Settings {
return Pair.of(new ResourceLocation(item), meta);
}
/**
* Checks a metadata-sensitive list option (see {@link Settings#parseItemMetaStrings(String...)} for the given block.
* @param array The config option to check
* @param block The block state to search for
* @return True if the given array contains an entry that matches the given block, false if not.
*/
public static boolean containsMetaBlock(Pair<ResourceLocation, Short>[] array, IBlockState block){
return containsMetaThing(array, block.getBlock().getRegistryName(), (short)block.getBlock().getMetaFromState(block));
}
/**
* Checks a metadata-sensitive list option (see {@link Settings#parseItemMetaStrings(String...)} for the given item.
* @param array The config option to check
* @param stack An item stack with the item and metadata to search for
* @return True if the given array contains an entry that matches the given stack, false if not.
*/
public static boolean containsMetaItem(Pair<ResourceLocation, Short>[] array, ItemStack stack){
return containsMetaThing(array, stack.getItem().getRegistryName(), (short)stack.getMetadata());
}
/**
* Checks a metadata-sensitive list option (see {@link Settings#parseItemMetaStrings(String...)} for the given
* id/metadata pair.
* @param array The config option to check
* @param id The id to search for
* @param metadata The metadata value to search for
* @return True if the given array contains the given id/metadata pair, or the given id paired with the wildcard value.
*/
public static boolean containsMetaThing(Pair<ResourceLocation, Short>[] array, ResourceLocation id, short metadata){
return Arrays.asList(array).contains(Pair.of(id, metadata)) || Arrays.asList(array).contains(Pair.of(id, OreDictionary.WILDCARD_VALUE));
return Arrays.asList(array).contains(Pair.of(id, metadata)) || Arrays.asList(array).contains(Pair.of(id, (short)OreDictionary.WILDCARD_VALUE));
}
}
@@ -1,5 +1,9 @@
package electroblob.wizardry;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonObject;
import electroblob.wizardry.block.BlockBookshelf;
import electroblob.wizardry.command.CommandCastSpell;
import electroblob.wizardry.command.CommandDiscoverSpell;
import electroblob.wizardry.command.CommandSetAlly;
@@ -8,11 +12,14 @@ import electroblob.wizardry.data.DispenserCastingData;
import electroblob.wizardry.data.WizardData;
import electroblob.wizardry.integration.antiqueatlas.WizardryAntiqueAtlasIntegration;
import electroblob.wizardry.integration.baubles.WizardryBaublesIntegration;
import electroblob.wizardry.inventory.ContainerBookshelf;
import electroblob.wizardry.misc.DonationPerksHandler;
import electroblob.wizardry.misc.Forfeit;
import electroblob.wizardry.packet.WizardryPacketHandler;
import electroblob.wizardry.registry.*;
import electroblob.wizardry.spell.Spell;
import electroblob.wizardry.util.SpellProperties;
import electroblob.wizardry.util.WandHelper;
import electroblob.wizardry.worldgen.*;
import net.minecraft.item.Item;
import net.minecraft.util.SoundCategory;
@@ -32,6 +39,8 @@ import net.minecraftforge.fml.common.registry.GameRegistry;
import org.apache.logging.log4j.Logger;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.util.Calendar;
/**
@@ -46,7 +55,8 @@ import java.util.Calendar;
*/
@Mod(modid = Wizardry.MODID, name = Wizardry.NAME, version = Wizardry.VERSION, acceptedMinecraftVersions = "[1.12.2]",
guiFactory = "electroblob.wizardry.WizardryGuiFactory", dependencies = "required-after:forge@[14.23.5.2814,)")
guiFactory = "electroblob.wizardry.WizardryGuiFactory",
dependencies = "required-after:forge@[14.23.5.2847,);after:jei@[4.15.0,);after:baubles@[1.5.2,);after:antiqueatlas@[4.6,)")
public class Wizardry {
@@ -64,7 +74,7 @@ public class Wizardry {
* 1.x.x represents Minecraft 1.7.x versions, 2.x.x represents Minecraft 1.10.x versions, 3.x.x represents Minecraft
* 1.11.x versions, and so on.
*/
public static final String VERSION = "4.2.9";
public static final String VERSION = "4.3.4";
// IDEA: Triggering of inbuilt Forge events in relevant places?
// IDEA: Abstract the vanilla particles behind the particle builder
@@ -122,6 +132,7 @@ public class Wizardry {
WizardryLoot.register();
WizardryAdvancementTriggers.register();
Forfeit.register();
BlockBookshelf.registerStandardBookModelTextures();
// Client-side stuff (via proxies)
proxy.registerRenderers();
@@ -150,6 +161,8 @@ public class Wizardry {
GameRegistry.registerWorldGenerator(new WorldGenWizardTower(), 20);
GameRegistry.registerWorldGenerator(new WorldGenObelisk(), 20);
GameRegistry.registerWorldGenerator(new WorldGenShrine(), 20);
GameRegistry.registerWorldGenerator(new WorldGenLibraryRuins(), 20);
GameRegistry.registerWorldGenerator(new WorldGenUndergroundLibraryRuins(), 20);
// This is for the config change and missing mappings events
MinecraftForge.EVENT_BUS.register(instance); // Since there's already an instance we might as well use it
@@ -158,9 +171,11 @@ public class Wizardry {
WizardryPacketHandler.initPackets();
// Post-registry extras
WizardryItems.populateWandMap();
WizardryItems.populateArmourMap();
BlockBookshelf.compileBookModelTextures();
ContainerBookshelf.initDefaultBookItems();
WizardryItems.registerDispenseBehaviours();
WizardryItems.registerBannerPatterns();
WandHelper.populateUpgradeMap();
Spell.registry.forEach(Spell::init);
SpellProperties.init();
@@ -173,6 +188,7 @@ public class Wizardry {
@EventHandler
public void postInit(FMLPostInitializationEvent event){
proxy.initialiseLayers();
proxy.initialiseAnimations();
}
@EventHandler
@@ -187,7 +203,12 @@ public class Wizardry {
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.
// All of the synchronised settings require a world restart anyway so don't need syncing, except for the
// donor perks which have special behaviour (since we have to update everyone when a donor logs in anyway,
// we might as well reuse the packet and let them change the setting mid-game)
if(event.isWorldRunning() && DonationPerksHandler.isDonor(proxy.getThePlayer())){
DonationPerksHandler.sendToServer(proxy.getThePlayer());
}
}
}
@@ -26,6 +26,7 @@ import net.minecraft.entity.EntityLivingBase;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.entity.player.EntityPlayerMP;
import net.minecraft.entity.projectile.EntityArrow;
import net.minecraft.init.MobEffects;
import net.minecraft.item.ItemStack;
import net.minecraft.potion.PotionEffect;
import net.minecraft.util.EnumHand;
@@ -78,12 +79,13 @@ public final class WizardryEventHandler {
public static void onPlayerLoggedInEvent(PlayerLoggedInEvent event){
// When a player logs in, they are sent the glyph data, server settings and spell properties.
if(event.player instanceof EntityPlayerMP){
SpellGlyphData.get(event.player.world).sync((EntityPlayerMP)event.player);
SpellEmitterData.get(event.player.world).sync((EntityPlayerMP)event.player);
Wizardry.settings.sync((EntityPlayerMP)event.player);
syncAdvancements((EntityPlayerMP)event.player, false);
EntityPlayerMP player = (EntityPlayerMP)event.player;
SpellGlyphData.get(player.world).sync(player);
SpellEmitterData.get(player.world).sync(player);
Wizardry.settings.sync(player);
syncAdvancements(player, false);
Spell.syncProperties(player);
}
Spell.syncProperties(event.player);
}
@SubscribeEvent(priority = EventPriority.HIGH)
@@ -173,7 +175,7 @@ public final class WizardryEventHandler {
&& 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, WizardrySounds.MISC_DISCOVER_SPELL, 1.25f, 1);
EntityUtils.playSoundAtPlayer(player, WizardrySounds.MISC_DISCOVER_SPELL, 1.25f, 1);
player.sendMessage(new TextComponentTranslation("spell.discover",
event.getSpell().getNameForTranslationFormatted()));
}
@@ -192,16 +194,33 @@ public final class WizardryEventHandler {
@SubscribeEvent
public static void onLivingSetAttackTargetEvent(LivingSetAttackTargetEvent event){
if(event.getTarget() != null && event.getEntityLiving() instanceof EntityLiving
&& event.getTarget().isPotionActive(WizardryPotions.muffle)){
if(event.getEntityLiving() instanceof EntityLiving && event.getTarget() != null){
Vec3d vec = event.getTarget().getPositionEyes(1).subtract(event.getEntity().getPositionEyes(1));
// Find the angle between the direction the mob is looking and the direction the player is in
// Angle between a and b = acos((a.b) / (|a|*|b|))
double angle = Math.acos(vec.dotProduct(event.getEntity().getLookVec()) / vec.length());
System.out.println(angle);
// If the player is not within the 144-degree arc in front of the mob, it won't detect them
if(angle > 0.4 * Math.PI){
// Muffle
if(event.getTarget().isPotionActive(WizardryPotions.muffle)){
Vec3d vec = event.getTarget().getPositionEyes(1).subtract(event.getEntity().getPositionEyes(1));
// Find the angle between the direction the mob is looking and the direction the player is in
// Angle between a and b = acos((a.b) / (|a|*|b|))
double angle = Math.acos(vec.dotProduct(event.getEntity().getLookVec()) / vec.length());
System.out.println(angle);
// If the player is not within the 144-degree arc in front of the mob, it won't detect them
if(angle > 0.4 * Math.PI){
((EntityLiving)event.getEntityLiving()).setAttackTarget(null);
}
}
// Mirage
if(event.getTarget().isPotionActive(WizardryPotions.mirage)){
// Can't find players under mirage at all! (Pretty sure this excludes revenge-targeting)
((EntityLiving)event.getEntityLiving()).setAttackTarget(null);
}
// Blindness tweak
// I'm not going as far as potion core's implementation, this is just so it does *something* to mobs
if(event.getEntityLiving().isPotionActive(MobEffects.BLINDNESS) && !Loader.isModLoaded("potioncore")
&& Wizardry.settings.blindnessTweak && event.getTarget().getDistanceSq(event.getEntity()) > 3.5 * 3.5){
// Can't detect anything more than 3.5 blocks away (roughly the player's view distance when blinded)
((EntityLiving)event.getEntityLiving()).setAttackTarget(null);
}
}
@@ -269,7 +288,7 @@ public final class WizardryEventHandler {
.target(attacker).spawn(world);
ParticleBuilder.spawnShockParticles(world, attacker.posX,
attacker.getEntityBoundingBox().minY + attacker.height / 2, attacker.posZ);
attacker.posY + attacker.height / 2, attacker.posZ);
}
DamageSafetyChecker.attackEntitySafely(attacker, MagicDamage.causeDirectMagicDamage(event.getEntityLiving(),
@@ -284,6 +303,13 @@ public final class WizardryEventHandler {
@SubscribeEvent(priority = EventPriority.LOW) // Again, we don't want these effects if the event is cancelled
public static void onLivingHurtEvent(LivingHurtEvent event){
// Ward
PotionEffect effect = event.getEntityLiving().getActivePotionEffect(WizardryPotions.ward);
if(effect != null && event.getSource().isMagicDamage()){
event.setAmount(event.getAmount() * Math.max(0, 1 - 0.4f * effect.getAmplifier())); // Resistance is 20%
}
// Flaming and freezing swords
if(event.getSource().getTrueSource() instanceof EntityLivingBase){
@@ -331,14 +357,6 @@ public final class WizardryEventHandler {
@SubscribeEvent
public static void onLivingUpdateEvent(LivingUpdateEvent event){
// Experimental animation feature
// if(event.getEntityLiving().isHandActive() && event.getEntityLiving().getActiveItemStack().getItemUseAction() == WizardryUtilities.POINT){
// event.getEntityLiving().isSwingInProgress = true;
// event.getEntityLiving().swingProgress = 1f;
// event.getEntityLiving().prevSwingProgress = 1;
// event.getEntityLiving().swingingHand = event.getEntityLiving().getActiveHand();
// }
if(event.getEntityLiving().world.isRemote){
// Client-side continuous spell casting for NPCs
@@ -358,10 +376,10 @@ public final class WizardryEventHandler {
// TODO: This implementation of modifiers relies on them being accessible client-side.
// Right now that doesn't matter because NPCs don't use modifiers, but they might in future
((EntityLiving)event.getEntity()).getAttackTarget(), modifiers);
((ISpellCaster)event.getEntity()).setSpellCounter(count + 1);
}
}
((ISpellCaster)event.getEntity()).setSpellCounter(count + 1);
}
}
}
@@ -389,7 +407,7 @@ public final class WizardryEventHandler {
}
}
for(ItemStack stack : WizardryUtilities.getPrioritisedHotbarAndOffhand(player)){
for(ItemStack stack : InventoryUtils.getPrioritisedHotbarAndOffhand(player)){
if(stack.getItem() instanceof IManaStoringItem && !((IManaStoringItem)stack.getItem()).isManaFull(stack)
&& WandHelper.getUpgradeLevel(stack, WizardryItems.siphon_upgrade) > 0){
@@ -420,7 +438,7 @@ public final class WizardryEventHandler {
@SubscribeEvent // Priority doesn't matter here, we're only setting event fields so if it's cancelled it won't matter
public static void onLivingFallEvent(LivingFallEvent event){
// Why is fall damage based on distance fallen? Why? Who on earth came up with that? It makes no sense whatsoever!
if(Wizardry.settings.replaceVanillaFallDamage && !Loader.isModLoaded("speedbasedfalldamage")){
if(!event.getEntity().world.isRemote && Wizardry.settings.replaceVanillaFallDamage && !Loader.isModLoaded("speedbasedfalldamage")){
// We want to keep the fall damage EXACTLY THE SAME for free, uninterrupted falls, but fix the weirdness
// caused when something else changes the entity's velocity
// All living entities have a gravity of 0.08b/t^2
@@ -1,10 +1,13 @@
package electroblob.wizardry;
import electroblob.wizardry.inventory.ContainerArcaneWorkbench;
import electroblob.wizardry.inventory.ContainerBookshelf;
import electroblob.wizardry.inventory.ContainerPortableWorkbench;
import electroblob.wizardry.item.ItemSpellBook;
import electroblob.wizardry.item.ItemWizardHandbook;
import electroblob.wizardry.tileentity.ContainerArcaneWorkbench;
import electroblob.wizardry.tileentity.ContainerPortableWorkbench;
import electroblob.wizardry.tileentity.TileEntityArcaneWorkbench;
import electroblob.wizardry.tileentity.TileEntityBookshelf;
import electroblob.wizardry.tileentity.TileEntityLectern;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.tileentity.TileEntity;
import net.minecraft.util.math.BlockPos;
@@ -20,40 +23,82 @@ public class WizardryGuiHandler implements IGuiHandler {
public static final int ARCANE_WORKBENCH = nextGuiId++;
public static final int WIZARD_HANDBOOK = nextGuiId++;
public static final int PORTABLE_CRAFTING = nextGuiId++;
public static final int BOOKSHELF = nextGuiId++;
public static final int LECTERN = nextGuiId++;
@Override
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));
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));
}else if(id == BOOKSHELF){
TileEntity tileEntity = world.getTileEntity(new BlockPos(x, y, z));
if(tileEntity instanceof TileEntityBookshelf){
return new ContainerBookshelf(player.inventory, (TileEntityBookshelf)tileEntity);
}
}
return null;
}
@Override
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));
if(tileEntity instanceof TileEntityArcaneWorkbench){
return new electroblob.wizardry.client.gui.GuiArcaneWorkbench(player.inventory,
(TileEntityArcaneWorkbench)tileEntity);
}
}else if(id == WIZARD_HANDBOOK && (player.getHeldItemMainhand().getItem() instanceof ItemWizardHandbook
|| player.getHeldItemOffhand().getItem() instanceof ItemWizardHandbook)){
return new electroblob.wizardry.client.gui.handbook.GuiWizardHandbook();
}else if(id == SPELL_BOOK){
if(player.getHeldItemMainhand().getItem() instanceof ItemSpellBook){
return new electroblob.wizardry.client.gui.GuiSpellBook(player.getHeldItemMainhand());
}else if(player.getHeldItemOffhand().getItem() instanceof ItemSpellBook){
return new electroblob.wizardry.client.gui.GuiSpellBook(player.getHeldItemOffhand());
}
}else if(id == PORTABLE_CRAFTING){
return new electroblob.wizardry.client.gui.GuiPortableCrafting(player.inventory, world, new BlockPos(x, y, z));
}else if(id == BOOKSHELF){
TileEntity tileEntity = world.getTileEntity(new BlockPos(x, y, z));
if(tileEntity instanceof TileEntityBookshelf){
return new electroblob.wizardry.client.gui.GuiBookshelf(player.inventory,
(TileEntityBookshelf)tileEntity);
}
}else if(id == LECTERN){
TileEntity tileEntity = world.getTileEntity(new BlockPos(x, y, z));
if(tileEntity instanceof TileEntityLectern){
return new electroblob.wizardry.client.gui.GuiLectern((TileEntityLectern)tileEntity);
}
}
return null;
}
}
@@ -5,7 +5,7 @@ import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import com.google.gson.JsonDeserializationContext;
import com.google.gson.JsonObject;
import electroblob.wizardry.worldgen.WorldGenSurfaceStructure;
import electroblob.wizardry.worldgen.WorldGenWizardryStructure;
import net.minecraft.advancements.ICriterionTrigger;
import net.minecraft.advancements.PlayerAdvancements;
import net.minecraft.advancements.critereon.AbstractCriterionInstance;
@@ -77,11 +77,11 @@ public class StructureTrigger implements ICriterionTrigger<StructureTrigger.Inst
public static class Instance extends AbstractCriterionInstance {
private final WorldGenSurfaceStructure structureType;
private final WorldGenWizardryStructure structureType;
public Instance(ResourceLocation criterionIn, String name){
super(criterionIn);
this.structureType = WorldGenSurfaceStructure.byName(name);
this.structureType = WorldGenWizardryStructure.byName(name);
}
public boolean test(WorldServer world, double x, double y, double z){
@@ -17,14 +17,14 @@ import java.util.List;
import java.util.Map;
import java.util.Set;
/** Advancement trigger for things done in the arcane workbench. The majority of any
/** Advancement trigger for things done in the arcane workbench or imbuement altar. The majority of any
* ICriterionTrigger class is just boilerplate, and this is no exception. */
public class ArcaneWorkbenchTrigger implements ICriterionTrigger<ArcaneWorkbenchTrigger.Instance> {
public class WizardryContainerTrigger implements ICriterionTrigger<WizardryContainerTrigger.Instance> {
private final ResourceLocation id;
private final Map<PlayerAdvancements, ArcaneWorkbenchTrigger.Listeners> listeners = Maps.newHashMap();
private final Map<PlayerAdvancements, WizardryContainerTrigger.Listeners> listeners = Maps.newHashMap();
public ArcaneWorkbenchTrigger(ResourceLocation id){
public WizardryContainerTrigger(ResourceLocation id){
this.id = id;
}
@@ -32,21 +32,21 @@ public class ArcaneWorkbenchTrigger implements ICriterionTrigger<ArcaneWorkbench
return this.id;
}
public void addListener(PlayerAdvancements advancements, Listener<ArcaneWorkbenchTrigger.Instance> listener){
public void addListener(PlayerAdvancements advancements, Listener<WizardryContainerTrigger.Instance> listener){
ArcaneWorkbenchTrigger.Listeners listeners = this.listeners.get(advancements);
WizardryContainerTrigger.Listeners listeners = this.listeners.get(advancements);
if(listeners == null){
listeners = new ArcaneWorkbenchTrigger.Listeners(advancements);
listeners = new WizardryContainerTrigger.Listeners(advancements);
this.listeners.put(advancements, listeners);
}
listeners.add(listener);
}
public void removeListener(PlayerAdvancements advancements, Listener<ArcaneWorkbenchTrigger.Instance> listener){
public void removeListener(PlayerAdvancements advancements, Listener<WizardryContainerTrigger.Instance> listener){
ArcaneWorkbenchTrigger.Listeners listeners = this.listeners.get(advancements);
WizardryContainerTrigger.Listeners listeners = this.listeners.get(advancements);
if(listeners != null){
listeners.remove(listener);
@@ -61,13 +61,13 @@ public class ArcaneWorkbenchTrigger implements ICriterionTrigger<ArcaneWorkbench
this.listeners.remove(advancements);
}
public ArcaneWorkbenchTrigger.Instance deserializeInstance(JsonObject json, JsonDeserializationContext context){
return new ArcaneWorkbenchTrigger.Instance(this.id, ItemPredicate.deserialize(json.get("item")));
public WizardryContainerTrigger.Instance deserializeInstance(JsonObject json, JsonDeserializationContext context){
return new WizardryContainerTrigger.Instance(this.id, ItemPredicate.deserialize(json.get("item")));
}
public void trigger(EntityPlayerMP player, ItemStack stack){
ArcaneWorkbenchTrigger.Listeners listeners = this.listeners.get(player.getAdvancements());
WizardryContainerTrigger.Listeners listeners = this.listeners.get(player.getAdvancements());
if(listeners != null){
listeners.trigger(stack);
@@ -91,7 +91,7 @@ public class ArcaneWorkbenchTrigger implements ICriterionTrigger<ArcaneWorkbench
static class Listeners {
private final PlayerAdvancements playerAdvancements;
private final Set<Listener<ArcaneWorkbenchTrigger.Instance>> listeners = Sets.newHashSet();
private final Set<Listener<WizardryContainerTrigger.Instance>> listeners = Sets.newHashSet();
public Listeners(PlayerAdvancements advancements){
this.playerAdvancements = advancements;
@@ -101,19 +101,19 @@ public class ArcaneWorkbenchTrigger implements ICriterionTrigger<ArcaneWorkbench
return this.listeners.isEmpty();
}
public void add(Listener<ArcaneWorkbenchTrigger.Instance> listener){
public void add(Listener<WizardryContainerTrigger.Instance> listener){
this.listeners.add(listener);
}
public void remove(Listener<ArcaneWorkbenchTrigger.Instance> listener){
public void remove(Listener<WizardryContainerTrigger.Instance> listener){
this.listeners.remove(listener);
}
public void trigger(ItemStack stack){
List<Listener<ArcaneWorkbenchTrigger.Instance>> list = null;
List<Listener<WizardryContainerTrigger.Instance>> list = null;
for(Listener<ArcaneWorkbenchTrigger.Instance> listener : this.listeners){
for(Listener<WizardryContainerTrigger.Instance> listener : this.listeners){
if(listener.getCriterionInstance().test(stack)){
@@ -126,7 +126,7 @@ public class ArcaneWorkbenchTrigger implements ICriterionTrigger<ArcaneWorkbench
}
if(list != null){
for(Listener<ArcaneWorkbenchTrigger.Instance> listener : list){
for(Listener<WizardryContainerTrigger.Instance> listener : list){
listener.grantCriterion(this.playerAdvancements);
}
}
@@ -0,0 +1,367 @@
package electroblob.wizardry.block;
import com.google.common.collect.ImmutableList;
import electroblob.wizardry.Settings;
import electroblob.wizardry.Wizardry;
import electroblob.wizardry.WizardryGuiHandler;
import electroblob.wizardry.registry.WizardryItems;
import electroblob.wizardry.registry.WizardryTabs;
import electroblob.wizardry.tileentity.TileEntityBookshelf;
import net.minecraft.block.BlockHorizontal;
import net.minecraft.block.ITileEntityProvider;
import net.minecraft.block.SoundType;
import net.minecraft.block.material.Material;
import net.minecraft.block.properties.PropertyInteger;
import net.minecraft.block.state.BlockFaceShape;
import net.minecraft.block.state.BlockStateContainer;
import net.minecraft.block.state.IBlockState;
import net.minecraft.entity.Entity;
import net.minecraft.entity.EntityLivingBase;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.init.Items;
import net.minecraft.inventory.IInventory;
import net.minecraft.inventory.InventoryHelper;
import net.minecraft.item.Item;
import net.minecraft.tileentity.TileEntity;
import net.minecraft.util.*;
import net.minecraft.util.math.BlockPos;
import net.minecraft.world.IBlockAccess;
import net.minecraft.world.IWorldEventListener;
import net.minecraft.world.World;
import net.minecraftforge.common.property.IExtendedBlockState;
import net.minecraftforge.common.property.Properties;
import net.minecraftforge.event.world.WorldEvent;
import net.minecraftforge.fml.common.Mod;
import net.minecraftforge.fml.common.event.FMLInitializationEvent;
import net.minecraftforge.fml.common.event.FMLPreInitializationEvent;
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
import org.apache.commons.lang3.ArrayUtils;
import javax.annotation.Nullable;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Supplier;
@Mod.EventBusSubscriber
public class BlockBookshelf extends BlockHorizontal implements ITileEntityProvider {
/** When a bookshelf block (of any kind specified in the config) is added or removed, players within this range will
* be notified of the change. */
public static final double PLAYER_NOTIFY_RANGE = 32;
public static final int SLOT_COUNT = 12;
public static final UnlistedPropertyInt[] BOOKS = new UnlistedPropertyInt[SLOT_COUNT];
private static final Map<Supplier<Item>, ResourceLocation> BOOK_TEXTURE_MAP = new HashMap<>();
private static ImmutableList<Item> bookItems;
private static ImmutableList<ResourceLocation> bookTextures;
public BlockBookshelf(){
super(Material.WOOD);
this.setDefaultState(this.blockState.getBaseState().withProperty(FACING, EnumFacing.NORTH));
this.setCreativeTab(WizardryTabs.WIZARDRY);
this.setHardness(2.0F);
this.setResistance(5.0F);
this.setSoundType(SoundType.WOOD);
}
@Override
protected BlockStateContainer createBlockState(){
// IProperty<?>[] properties = { FACING };
// return new BlockStateContainer(this, ArrayUtils.addAll(properties, BOOKS));
return new BlockStateContainer.Builder(this).add(FACING).add(BOOKS).build();
}
@Override
public BlockFaceShape getBlockFaceShape(IBlockAccess world, IBlockState state, BlockPos pos, EnumFacing face){
return state.getValue(FACING).getAxis() == face.getAxis() ? BlockFaceShape.UNDEFINED : BlockFaceShape.SOLID;
}
@Override
public IBlockState getStateForPlacement(World world, BlockPos pos, EnumFacing facing, float hitX, float hitY, float hitZ, int meta, EntityLivingBase placer){
return this.getDefaultState().withProperty(FACING, placer.getHorizontalFacing().getOpposite());
}
// Why on earth are these two not in BlockHorizontal?
@Override
public IBlockState withRotation(IBlockState state, Rotation rotation){
return state.withProperty(FACING, rotation.rotate(state.getValue(FACING)));
}
@Override
public IBlockState withMirror(IBlockState state, Mirror mirror){
return state.withRotation(mirror.toRotation(state.getValue(FACING)));
}
@Override
public void onBlockAdded(World worldIn, BlockPos pos, IBlockState state){
super.onBlockAdded(worldIn, pos, state);
}
@Override
public void breakBlock(World world, BlockPos pos, IBlockState block){
TileEntity tileentity = world.getTileEntity(pos);
if(tileentity instanceof TileEntityBookshelf){
InventoryHelper.dropInventoryItems(world, pos, (TileEntityBookshelf)tileentity);
}
super.breakBlock(world, pos, block); // For blocks that don't extend BlockContainer, this removes the TE
}
@Override
public IBlockState getStateFromMeta(int meta){
EnumFacing enumfacing = EnumFacing.byIndex(meta);
if(enumfacing.getAxis() == EnumFacing.Axis.Y) enumfacing = EnumFacing.NORTH;
return this.getDefaultState().withProperty(FACING, enumfacing);
}
@Override
public int getMetaFromState(IBlockState state){
return state.getValue(FACING).getIndex();
}
@Override
public IBlockState getExtendedState(IBlockState state, IBlockAccess world, BlockPos pos){
IExtendedBlockState s = (IExtendedBlockState)super.getExtendedState(state, world, pos);
if(world.getTileEntity(pos) instanceof TileEntityBookshelf){
TileEntityBookshelf tileentity = ((TileEntityBookshelf)world.getTileEntity(pos));
for(int i = 0; i < tileentity.getSizeInventory(); i++){
if(tileentity.getStackInSlot(i).isEmpty()){
s = s.withProperty(BOOKS[i], bookItems.size());
}else{
Item item = tileentity.getStackInSlot(i).getItem();
// Default to the standard spell book texture, which will always be the first item in the list
s = s.withProperty(BOOKS[i], bookItems.contains(item) ? bookItems.indexOf(item) : 0);
}
}
}
return s;
}
@Nullable
@Override
public TileEntity createNewTileEntity(World world, int meta){
return new TileEntityBookshelf();
}
@Override
public boolean onBlockActivated(World world, BlockPos pos, IBlockState block, EntityPlayer player, EnumHand hand,
EnumFacing side, float hitX, float hitY, float hitZ){
TileEntity tileEntity = world.getTileEntity(pos);
if(tileEntity == null || player.isSneaking()){
return false;
}
player.openGui(Wizardry.instance, WizardryGuiHandler.BOOKSHELF, world, pos.getX(), pos.getY(), pos.getZ());
return true;
}
@Override
public boolean hasComparatorInputOverride(IBlockState state){
return true;
}
@Override
public int getComparatorInputOverride(IBlockState state, World world, BlockPos pos){
TileEntity tileEntity = world.getTileEntity(pos);
if(tileEntity instanceof TileEntityBookshelf){
int slotsOccupied = 0;
for(int i = 0; i < ((TileEntityBookshelf)tileEntity).getSizeInventory(); i++){
if(!((TileEntityBookshelf)tileEntity).getStackInSlot(i).isEmpty()) slotsOccupied++;
}
return slotsOccupied;
}
return super.getComparatorInputOverride(state, world, pos);
}
@Override
public boolean eventReceived(IBlockState state, World world, BlockPos pos, int id, int param){
super.eventReceived(state, world, pos, id, param);
TileEntity tileentity = world.getTileEntity(pos);
return tileentity != null && tileentity.receiveClientEvent(id, param);
}
/** Returns the list of book textures, in ID order. */
public static ImmutableList<ResourceLocation> getBookTextures(){
// ModelBookshelf calls this before init() so we need to return the map values as a fallback
return bookTextures == null ? ImmutableList.copyOf(BOOK_TEXTURE_MAP.values()) : bookTextures;
}
/** Returns the list of registered book items, in ID order. */
public static ImmutableList<Item> getBookItems(){
return bookItems;
}
/** Called during block registration to initialise the block properties for each book. */
public static void initBookProperties(){
for(int i=0; i<SLOT_COUNT; i++){
BOOKS[i] = new UnlistedPropertyInt("book" + i);
}
}
// Based on BlockFluid's UnlistedPropertyBool; this is only needed because of Java's weird restrictions on generics
private static final class UnlistedPropertyInt extends Properties.PropertyAdapter<Integer> {
public UnlistedPropertyInt(String name){
// Max is inclusive here, but we're using the largest value for no book so there's an extra one (can't use -1)
super(PropertyInteger.create(name, 0, BOOK_TEXTURE_MAP.size()));
}
}
/** Called from {@link Wizardry#init(FMLInitializationEvent)} to retrieve the actual book items and compile them
* and their textures into immutable lists. */
public static void compileBookModelTextures(){
ImmutableList.Builder<Item> itemListBuider = ImmutableList.builder();
ImmutableList.Builder<ResourceLocation> textureListBuilder = ImmutableList.builder();
for(Map.Entry<Supplier<Item>, ResourceLocation> entry : BOOK_TEXTURE_MAP.entrySet()){
itemListBuider.add(entry.getKey().get());
textureListBuilder.add(entry.getValue());
}
bookItems = itemListBuider.build();
bookTextures = textureListBuilder.build();
}
/** Called from {@link Wizardry#preInit(FMLPreInitializationEvent)} to register the default set of book textures. */
public static void registerStandardBookModelTextures(){
// Vanilla Minecraft books
// Regular brown books are the default so they're registered first
registerBookModelTexture(() -> Items.BOOK, new ResourceLocation(Wizardry.MODID, "blocks/books_brown"));
registerBookModelTexture(() -> Items.WRITABLE_BOOK, new ResourceLocation(Wizardry.MODID, "blocks/books_brown"));
registerBookModelTexture(() -> Items.WRITTEN_BOOK, new ResourceLocation(Wizardry.MODID, "blocks/books_brown"));
registerBookModelTexture(() -> Items.ENCHANTED_BOOK, new ResourceLocation(Wizardry.MODID, "blocks/books_enchanted"));
// Wizardry books
registerBookModelTexture(() -> WizardryItems.spell_book, new ResourceLocation(Wizardry.MODID, "blocks/books_red"));
registerBookModelTexture(() -> WizardryItems.wizard_handbook, new ResourceLocation(Wizardry.MODID, "blocks/books_blue"));
registerBookModelTexture(() -> WizardryItems.arcane_tome, new ResourceLocation(Wizardry.MODID, "blocks/books_purple"));
registerBookModelTexture(() -> WizardryItems.ruined_spell_book, new ResourceLocation(Wizardry.MODID, "blocks/books_brown"));
registerBookModelTexture(() -> WizardryItems.scroll, new ResourceLocation(Wizardry.MODID, "blocks/scrolls_blue"));
registerBookModelTexture(() -> WizardryItems.blank_scroll, new ResourceLocation(Wizardry.MODID, "blocks/scrolls_blue"));
registerBookModelTexture(() -> WizardryItems.identification_scroll, new ResourceLocation(Wizardry.MODID, "blocks/scrolls_purple"));
// Can't use the map in WandHelper because this is called from preInit (addons will have to add theirs manually)
registerBookModelTexture(() -> WizardryItems.storage_upgrade, new ResourceLocation(Wizardry.MODID, "blocks/scrolls_wooden"));
registerBookModelTexture(() -> WizardryItems.siphon_upgrade, new ResourceLocation(Wizardry.MODID, "blocks/scrolls_wooden"));
registerBookModelTexture(() -> WizardryItems.condenser_upgrade, new ResourceLocation(Wizardry.MODID, "blocks/scrolls_wooden"));
registerBookModelTexture(() -> WizardryItems.range_upgrade, new ResourceLocation(Wizardry.MODID, "blocks/scrolls_wooden"));
registerBookModelTexture(() -> WizardryItems.duration_upgrade, new ResourceLocation(Wizardry.MODID, "blocks/scrolls_wooden"));
registerBookModelTexture(() -> WizardryItems.cooldown_upgrade, new ResourceLocation(Wizardry.MODID, "blocks/scrolls_wooden"));
registerBookModelTexture(() -> WizardryItems.blast_upgrade, new ResourceLocation(Wizardry.MODID, "blocks/scrolls_wooden"));
registerBookModelTexture(() -> WizardryItems.attunement_upgrade, new ResourceLocation(Wizardry.MODID, "blocks/scrolls_wooden"));
registerBookModelTexture(() -> WizardryItems.melee_upgrade, new ResourceLocation(Wizardry.MODID, "blocks/scrolls_wooden"));
}
/**
* Registers a book texture to be used for the book model when the given item is placed in a bookshelf. This method
* <b>must</b> be called from {@code preInit} as the registered item count is required during block registration.
* This also necessitates the use of a supplier to fetch the item, since the items will not yet be registered.
* @param itemFactory A {@link Supplier} that returns the book item to link the texture to. This item need not
* be an instance of {@link electroblob.wizardry.item.ItemSpellBook ItemSpellBook}. Duplicate
* items will result in an {@link IllegalArgumentException} being thrown later.
* @param texture The texture to apply to the book model.
*/
public static void registerBookModelTexture(Supplier<Item> itemFactory, ResourceLocation texture){
BOOK_TEXTURE_MAP.put(itemFactory, texture);
}
/**
* Returns a list of nearby bookshelves' inventories, where 'bookshelves' are any tile entities with inventories
* whose blocks are specified in the config file under the {@code bookshelfBlocks} option.
* @param world The world to search in
* @param centre The position to search around
* @param exclude Any tile entities that should be excluded from the returned list
* @return A list of nearby {@link IInventory} objects that count as valid bookshelves
*/
public static List<IInventory> findNearbyBookshelves(World world, BlockPos centre, TileEntity... exclude){
List<IInventory> bookshelves = new ArrayList<>();
int searchRadius = Wizardry.settings.bookshelfSearchRadius;
for(int x = -searchRadius; x <= searchRadius; x++){
for(int y = -searchRadius; y <= searchRadius; y++){
for(int z = -searchRadius; z <= searchRadius; z++){
BlockPos pos = centre.add(x, y, z);
if(Settings.containsMetaBlock(Wizardry.settings.bookshelfBlocks, world.getBlockState(pos))){
TileEntity te = world.getTileEntity(pos);
if(te instanceof IInventory && !ArrayUtils.contains(exclude, te)) bookshelves.add((IInventory)te);
}
}
}
}
return bookshelves;
}
@SubscribeEvent
public static void onWorldLoadEvent(WorldEvent.Load event){
event.getWorld().addEventListener(Listener.instance);
}
@SubscribeEvent
public static void onWorldUnloadEvent(WorldEvent.Unload event){
event.getWorld().removeEventListener(Listener.instance);
}
public static class Listener implements IWorldEventListener {
public static final Listener instance = new Listener();
private Listener(){}
@Override
public void notifyBlockUpdate(World world, BlockPos pos, IBlockState oldState, IBlockState newState, int flags){
if(oldState == newState) return; // Probably won't happen but just in case
if(Settings.containsMetaBlock(Wizardry.settings.bookshelfBlocks, oldState) // Bookshelf removed
|| Settings.containsMetaBlock(Wizardry.settings.bookshelfBlocks, newState)){ // Bookshelf placed
// It is also possible (with commands) for a bookshelf to be replaced with another bookshelf, in which
// case this should still just be called once
Wizardry.proxy.notifyBookshelfChange(world, pos);
}
}
// Dummy implementations
@Override public void notifyLightSet(BlockPos pos){}
@Override public void markBlockRangeForRenderUpdate(int x1, int y1, int z1, int x2, int y2, int z2){}
@Override public void playSoundToAllNearExcept(@Nullable EntityPlayer player, SoundEvent soundIn, SoundCategory category, double x, double y, double z, float volume, float pitch){}
@Override public void playRecord(SoundEvent soundIn, BlockPos pos){}
@Override public void spawnParticle(int particleID, boolean ignoreRange, double xCoord, double yCoord, double zCoord, double xSpeed, double ySpeed, double zSpeed, int... parameters){}
@Override public void spawnParticle(int id, boolean ignoreRange, boolean minimiseParticleLevel, double x, double y, double z, double xSpeed, double ySpeed, double zSpeed, int... parameters){}
@Override public void onEntityAdded(Entity entityIn){}
@Override public void onEntityRemoved(Entity entityIn){}
@Override public void broadcastSound(int soundID, BlockPos pos, int data){}
@Override public void playEvent(EntityPlayer player, int type, BlockPos blockPosIn, int data){}
@Override public void sendBlockBreakProgress(int breakerId, BlockPos pos, int progress){}
}
}
@@ -0,0 +1,148 @@
package electroblob.wizardry.block;
import electroblob.wizardry.registry.WizardryBlocks;
import electroblob.wizardry.util.ParticleBuilder;
import electroblob.wizardry.util.ParticleBuilder.Type;
import net.minecraft.block.Block;
import net.minecraft.block.material.Material;
import net.minecraft.block.state.BlockFaceShape;
import net.minecraft.block.state.IBlockState;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.init.Blocks;
import net.minecraft.init.Items;
import net.minecraft.item.Item;
import net.minecraft.item.ItemStack;
import net.minecraft.tileentity.TileEntity;
import net.minecraft.tileentity.TileEntityFlowerPot;
import net.minecraft.util.BlockRenderLayer;
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.math.RayTraceResult;
import net.minecraft.world.IBlockAccess;
import net.minecraft.world.World;
import javax.annotation.Nullable;
import java.util.Random;
public class BlockCrystalFlowerPot extends Block {
protected static final AxisAlignedBB BOUNDING_BOX = new AxisAlignedBB(0.3125D, 0.0D, 0.3125D, 0.6875D, 0.375D, 0.6875D);
public BlockCrystalFlowerPot(){
super(Material.CIRCUITS);
this.setLightLevel(0.4f);
this.setTickRandomly(true);
}
@Override
public void randomDisplayTick(IBlockState state, World world, BlockPos pos, Random random){
if(world.isRemote && random.nextBoolean()){
ParticleBuilder.create(Type.SPARKLE)
.pos(pos.getX() + 0.3 + random.nextDouble() * 0.4, pos.getY() + 0.6 + random.nextDouble() * 0.3, pos.getZ() + 0.3 + random.nextDouble() * 0.4)
.vel(0, 0.01, 0)
.time(20 + random.nextInt(10))
.clr(0.5f + (random.nextFloat() / 2), 0.5f + (random.nextFloat() / 2), 0.5f + (random.nextFloat() / 2))
.spawn(world);
}
}
@Nullable
@Override
public TileEntity createTileEntity(World world, IBlockState state){
return new TileEntityFlowerPot(Item.getItemFromBlock(WizardryBlocks.crystal_flower), 0);
}
@Override
public boolean hasTileEntity(IBlockState state){
return true;
}
@Override
public boolean onBlockActivated(World world, BlockPos pos, IBlockState state, EntityPlayer player, EnumHand hand, EnumFacing facing, float hitX, float hitY, float hitZ){
ItemStack stack = new ItemStack(WizardryBlocks.crystal_flower);
if(player.getHeldItem(hand).isEmpty()){
player.setHeldItem(hand, stack);
}else if(!player.addItemStackToInventory(stack)){
player.dropItem(stack, false);
}
world.setBlockState(pos, Blocks.FLOWER_POT.getDefaultState());
return true;
}
@Override
public ItemStack getPickBlock(IBlockState state, RayTraceResult target, World world, BlockPos pos, EntityPlayer player){
return new ItemStack(WizardryBlocks.crystal_flower);
}
@Override
public void getDrops(net.minecraft.util.NonNullList<ItemStack> drops, IBlockAccess world, BlockPos pos, IBlockState state, int fortune){
super.getDrops(drops, world, pos, state, fortune);
drops.add(new ItemStack(WizardryBlocks.crystal_flower));
}
@Override
public Item getItemDropped(IBlockState state, Random rand, int fortune){
return Items.FLOWER_POT;
}
// The rest are identical behaviour to BlockFlowerPot
@Override
public AxisAlignedBB getBoundingBox(IBlockState state, IBlockAccess source, BlockPos pos){
return BOUNDING_BOX;
}
@Override
public boolean isOpaqueCube(IBlockState state){
return false;
}
@Override
public boolean isFullCube(IBlockState state){
return false;
}
@Override
public boolean canPlaceBlockAt(World world, BlockPos pos){
IBlockState downState = world.getBlockState(pos.down());
return super.canPlaceBlockAt(world, pos) && (downState.isTopSolid() || downState.getBlockFaceShape(world, pos.down(), EnumFacing.UP) == BlockFaceShape.SOLID);
}
@Override
public void neighborChanged(IBlockState state, World world, BlockPos pos, Block block, BlockPos neighbour){
IBlockState downState = world.getBlockState(pos.down());
if(!downState.isTopSolid() && downState.getBlockFaceShape(world, pos.down(), EnumFacing.UP) != BlockFaceShape.SOLID){
this.dropBlockAsItem(world, pos, state, 0);
world.setBlockToAir(pos);
}
}
@Override
public BlockRenderLayer getRenderLayer(){
return BlockRenderLayer.CUTOUT;
}
@Override
public BlockFaceShape getBlockFaceShape(IBlockAccess worldIn, IBlockState state, BlockPos pos, EnumFacing face){
return BlockFaceShape.UNDEFINED;
}
@Override
public boolean removedByPlayer(IBlockState state, World world, BlockPos pos, EntityPlayer player, boolean willHarvest){
if(willHarvest) return true; // If it will harvest, delay deletion of the block until after getDrops
return super.removedByPlayer(state, world, pos, player, willHarvest);
}
@Override
public void harvestBlock(World world, EntityPlayer player, BlockPos pos, IBlockState state, @Nullable TileEntity te, ItemStack tool){
super.harvestBlock(world, player, pos, state, te, tool);
world.setBlockToAir(pos);
}
}
@@ -1,6 +1,8 @@
package electroblob.wizardry.block;
import net.minecraft.block.BlockFrostedIce;
import net.minecraft.block.SoundType;
import net.minecraft.block.material.Material;
import net.minecraft.block.state.IBlockState;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.MathHelper;
@@ -12,6 +14,17 @@ import java.util.Random;
* disappears instead of turning to water. */
public class BlockDryFrostedIce extends BlockFrostedIce {
public BlockDryFrostedIce(){
super();
setDefaultSlipperiness(0.98f); // For some strange reason BlockFrostedIce overrides BlockIce's icy-ness...
setSoundType(SoundType.GLASS);
}
@Override
public Material getMaterial(IBlockState state){
return Material.ICE; // For goodness sake
}
@Override
protected void turnIntoWater(World world, BlockPos pos){
world.destroyBlock(pos, false);
@@ -0,0 +1,17 @@
package electroblob.wizardry.block;
import electroblob.wizardry.registry.WizardryTabs;
import net.minecraft.block.BlockPlanks;
import net.minecraft.block.SoundType;
public class BlockGildedWood extends BlockPlanks {
public BlockGildedWood(){
super();
this.setCreativeTab(WizardryTabs.WIZARDRY);
this.setHardness(2.0F);
this.setResistance(5.0F);
this.setSoundType(SoundType.WOOD); // Why is this protected?!
}
}
@@ -0,0 +1,179 @@
package electroblob.wizardry.block;
import electroblob.wizardry.registry.WizardryBlocks;
import electroblob.wizardry.registry.WizardryTabs;
import electroblob.wizardry.tileentity.TileEntityImbuementAltar;
import net.minecraft.block.Block;
import net.minecraft.block.ITileEntityProvider;
import net.minecraft.block.material.Material;
import net.minecraft.block.properties.PropertyBool;
import net.minecraft.block.state.BlockFaceShape;
import net.minecraft.block.state.BlockStateContainer;
import net.minecraft.block.state.IBlockState;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.inventory.InventoryHelper;
import net.minecraft.item.ItemStack;
import net.minecraft.tileentity.TileEntity;
import net.minecraft.util.BlockRenderLayer;
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.world.World;
import java.util.Arrays;
public class BlockImbuementAltar extends Block implements ITileEntityProvider {
public static final PropertyBool ACTIVE = PropertyBool.create("active");
private static final AxisAlignedBB AABB = new AxisAlignedBB(0.0, 0.0, 0.0, 1.0, 0.75, 1.0);
public BlockImbuementAltar(){
super(Material.ROCK);
this.setBlockUnbreakable();
this.setResistance(6000000);
this.setLightLevel(0.4f);
this.setCreativeTab(WizardryTabs.WIZARDRY);
this.setDefaultState(this.blockState.getBaseState().withProperty(ACTIVE, false));
}
@Override
protected BlockStateContainer createBlockState(){
return new BlockStateContainer(this, ACTIVE);
}
@Override
public IBlockState getStateFromMeta(int meta){
return this.getDefaultState().withProperty(ACTIVE, meta == 1);
}
@Override
public int getMetaFromState(IBlockState state){
return state.getValue(ACTIVE) ? 1 : 0;
}
@Override
public AxisAlignedBB getBoundingBox(IBlockState state, IBlockAccess source, BlockPos pos){
return AABB;
}
@Override
public TileEntity createNewTileEntity(World world, int metadata){
return new TileEntityImbuementAltar();
}
@Override
public boolean isNormalCube(IBlockState state, IBlockAccess world, BlockPos pos){
return false;
}
@Override
public EnumBlockRenderType getRenderType(IBlockState state){
return EnumBlockRenderType.MODEL;
}
@Override
public BlockRenderLayer getRenderLayer(){
return BlockRenderLayer.CUTOUT; // Required to shade parts of the block faces differently to others
}
@Override
public boolean isOpaqueCube(IBlockState state){
return false;
}
@Override
public boolean isFullCube(IBlockState state){
return false;
}
@Override
public BlockFaceShape getBlockFaceShape(IBlockAccess world, IBlockState state, BlockPos pos, EnumFacing face){
return face == EnumFacing.DOWN ? BlockFaceShape.SOLID : BlockFaceShape.UNDEFINED;
}
@Override
public int getLightValue(IBlockState state, IBlockAccess world, BlockPos pos){
return state.getValue(ACTIVE) ? super.getLightValue(state, world, pos) : 0;
}
@Override
public void neighborChanged(IBlockState state, World world, BlockPos pos, Block block, BlockPos neighbour){
boolean shouldBeActive = Arrays.stream(EnumFacing.HORIZONTALS)
.allMatch(s -> world.getBlockState(pos.offset(s)).getBlock() == WizardryBlocks.receptacle
&& world.getBlockState(pos.offset(s)).getValue(BlockReceptacle.FACING) == s);
if(world.getBlockState(pos).getValue(ACTIVE) != shouldBeActive){ // Only set when it actually needs changing
// Get contents of altar before replacing it
TileEntity te = world.getTileEntity(pos);
ItemStack stack = ItemStack.EMPTY;
if(te instanceof TileEntityImbuementAltar) stack = ((TileEntityImbuementAltar)te).getStack();
world.setBlockState(pos, world.getBlockState(pos).withProperty(ACTIVE, shouldBeActive));
// Copy over contents of altar from before to new tile entity
te = world.getTileEntity(pos);
if(te instanceof TileEntityImbuementAltar) ((TileEntityImbuementAltar)te).setStack(stack);
world.checkLight(pos);
}
TileEntity tileEntity = world.getTileEntity(pos);
if(tileEntity instanceof TileEntityImbuementAltar){
((TileEntityImbuementAltar)tileEntity).checkRecipe();
}
}
@Override
public boolean onBlockActivated(World world, BlockPos pos, IBlockState block, EntityPlayer player, EnumHand hand,
EnumFacing side, float hitX, float hitY, float hitZ){
TileEntity tileEntity = world.getTileEntity(pos);
if(!(tileEntity instanceof TileEntityImbuementAltar) || player.isSneaking()){
return false;
}
ItemStack currentStack = ((TileEntityImbuementAltar)tileEntity).getStack();
ItemStack toInsert = player.getHeldItem(hand);
if(currentStack.isEmpty()){
ItemStack stack = toInsert.copy();
stack.setCount(1);
((TileEntityImbuementAltar)tileEntity).setStack(stack);
((TileEntityImbuementAltar)tileEntity).setLastUser(player);
if(!player.isCreative()) toInsert.shrink(1);
}else{
if(toInsert.isEmpty()){
player.setHeldItem(hand, currentStack);
}else if(!player.addItemStackToInventory(currentStack)){
player.dropItem(currentStack, false);
}
((TileEntityImbuementAltar)tileEntity).setStack(ItemStack.EMPTY);
((TileEntityImbuementAltar)tileEntity).setLastUser(null);
}
return true;
}
@Override
public void breakBlock(World world, BlockPos pos, IBlockState block){
TileEntity tileentity = world.getTileEntity(pos);
if(tileentity instanceof TileEntityImbuementAltar){
InventoryHelper.spawnItemStack(world, pos.getX(), pos.getY(), pos.getZ(), ((TileEntityImbuementAltar)tileentity).getStack());
}
super.breakBlock(world, pos, block);
}
}
@@ -0,0 +1,138 @@
package electroblob.wizardry.block;
import electroblob.wizardry.Wizardry;
import electroblob.wizardry.WizardryGuiHandler;
import electroblob.wizardry.registry.WizardryTabs;
import electroblob.wizardry.tileentity.TileEntityLectern;
import electroblob.wizardry.util.ParticleBuilder;
import electroblob.wizardry.util.ParticleBuilder.Type;
import net.minecraft.block.BlockHorizontal;
import net.minecraft.block.ITileEntityProvider;
import net.minecraft.block.SoundType;
import net.minecraft.block.material.Material;
import net.minecraft.block.state.BlockFaceShape;
import net.minecraft.block.state.BlockStateContainer;
import net.minecraft.block.state.IBlockState;
import net.minecraft.entity.EntityLivingBase;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.tileentity.TileEntity;
import net.minecraft.util.EnumFacing;
import net.minecraft.util.EnumHand;
import net.minecraft.util.Mirror;
import net.minecraft.util.Rotation;
import net.minecraft.util.math.BlockPos;
import net.minecraft.world.IBlockAccess;
import net.minecraft.world.World;
import net.minecraftforge.fml.relauncher.Side;
import net.minecraftforge.fml.relauncher.SideOnly;
import javax.annotation.Nullable;
import java.util.Random;
public class BlockLectern extends BlockHorizontal implements ITileEntityProvider {
public BlockLectern(){
super(Material.WOOD);
this.setDefaultState(this.blockState.getBaseState().withProperty(FACING, EnumFacing.NORTH));
this.setCreativeTab(WizardryTabs.WIZARDRY);
this.setHardness(2.0F);
this.setResistance(5.0F);
this.setSoundType(SoundType.WOOD);
}
@Override
protected BlockStateContainer createBlockState(){
return new BlockStateContainer.Builder(this).add(FACING).build();
}
@Override
public IBlockState getStateFromMeta(int meta){
EnumFacing enumfacing = EnumFacing.byIndex(meta);
if(enumfacing.getAxis() == EnumFacing.Axis.Y) enumfacing = EnumFacing.NORTH;
return this.getDefaultState().withProperty(FACING, enumfacing);
}
@Override
public int getMetaFromState(IBlockState state){
return state.getValue(FACING).getIndex();
}
@Override
@SideOnly(Side.CLIENT)
public void randomDisplayTick(IBlockState state, World world, BlockPos pos, Random rand){
EntityPlayer entityplayer = world.getClosestPlayer(pos.getX() + 0.5, pos.getY() + 0.5,
pos.getZ() + 0.5, TileEntityLectern.BOOK_OPEN_DISTANCE, false);
if(entityplayer != null){
ParticleBuilder.create(Type.DUST).pos(pos.getX() + rand.nextFloat(), pos.getY() + 1, pos.getZ() + rand.nextFloat())
.vel(0, 0.03, 0).clr(1, 1, 0.65f).fade(0.7f, 0, 1).shaded(false).spawn(world);
}
}
@Override
public boolean isOpaqueCube(IBlockState state){
return false;
}
@Override
public boolean isFullCube(IBlockState state){
return false;
}
@Override
public boolean isFullBlock(IBlockState state){
return false;
}
@Override
public boolean isNormalCube(IBlockState state){
return false;
}
@Override
public boolean canPlaceTorchOnTop(IBlockState state, IBlockAccess world, BlockPos pos){
return false;
}
@Override
public BlockFaceShape getBlockFaceShape(IBlockAccess world, IBlockState state, BlockPos pos, EnumFacing face){
return face == EnumFacing.DOWN ? BlockFaceShape.SOLID : BlockFaceShape.UNDEFINED;
}
@Override
public IBlockState getStateForPlacement(World world, BlockPos pos, EnumFacing facing, float hitX, float hitY, float hitZ, int meta, EntityLivingBase placer){
return this.getDefaultState().withProperty(FACING, placer.getHorizontalFacing().getOpposite());
}
@Override
public IBlockState withRotation(IBlockState state, Rotation rotation){
return state.withProperty(FACING, rotation.rotate(state.getValue(FACING)));
}
@Override
public IBlockState withMirror(IBlockState state, Mirror mirror){
return state.withRotation(mirror.toRotation(state.getValue(FACING)));
}
@Nullable
@Override
public TileEntity createNewTileEntity(World world, int meta){
return new TileEntityLectern();
}
@Override
public boolean onBlockActivated(World world, BlockPos pos, IBlockState block, EntityPlayer player, EnumHand hand,
EnumFacing side, float hitX, float hitY, float hitZ){
TileEntity tileEntity = world.getTileEntity(pos);
if(tileEntity == null || player.isSneaking()){
return false;
}
player.openGui(Wizardry.instance, WizardryGuiHandler.LECTERN, world, pos.getX(), pos.getY(), pos.getZ());
return true;
}
}
@@ -107,7 +107,9 @@ public class BlockPedestal extends Block implements ITileEntityProvider {
natural = true;
metadata -= ELEMENT.getAllowedValues().size();
}
return this.getDefaultState().withProperty(ELEMENT, Element.values()[metadata]).withProperty(NATURAL, natural);
Element element = Element.values()[metadata];
if(!ELEMENT.getAllowedValues().contains(element)) return this.getDefaultState().withProperty(NATURAL, natural);
return this.getDefaultState().withProperty(ELEMENT, element).withProperty(NATURAL, natural);
}
@Override
@@ -0,0 +1,91 @@
package electroblob.wizardry.block;
import electroblob.wizardry.registry.Spells;
import electroblob.wizardry.registry.WizardryPotions;
import electroblob.wizardry.spell.Spell;
import electroblob.wizardry.util.EntityUtils;
import net.minecraft.block.state.BlockFaceShape;
import net.minecraft.block.state.IBlockState;
import net.minecraft.entity.Entity;
import net.minecraft.entity.EntityLivingBase;
import net.minecraft.potion.PotionEffect;
import net.minecraft.util.DamageSource;
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.world.World;
import javax.annotation.Nullable;
public class BlockPermafrost extends BlockDryFrostedIce {
protected static final AxisAlignedBB BOUNDING_BOX = new AxisAlignedBB(0, 0, 0, 1, 0, 1);
protected static final AxisAlignedBB SELECTION_BOUNDING_BOX = new AxisAlignedBB(0, 0, 0, 1, 0.125, 1);
@Override
public AxisAlignedBB getBoundingBox(IBlockState state, IBlockAccess source, BlockPos pos){
return BOUNDING_BOX;
}
@Override
public AxisAlignedBB getSelectedBoundingBox(IBlockState state, World worldIn, BlockPos pos){
return SELECTION_BOUNDING_BOX;
}
@Nullable
@Override
public AxisAlignedBB getCollisionBoundingBox(IBlockState blockState, IBlockAccess worldIn, BlockPos pos){
return super.getCollisionBoundingBox(blockState, worldIn, pos);
}
@Override
public boolean isFullCube(IBlockState state){
return false;
}
@Override
public boolean isNormalCube(IBlockState state){
return false;
}
@Override
public BlockFaceShape getBlockFaceShape(IBlockAccess world, IBlockState state, BlockPos pos, EnumFacing face){
return face == EnumFacing.DOWN ? BlockFaceShape.SOLID : BlockFaceShape.UNDEFINED;
}
@Override
public void onEntityCollision(World world, BlockPos pos, IBlockState state, Entity entity){
if(EntityUtils.isLiving(entity) && entity.ticksExisted % 30 == 0){
// Can't make it player damage unless we make this block a tile entity, but there will be too many for that
entity.attackEntityFrom(DamageSource.MAGIC, Spells.permafrost.getProperty(Spell.DAMAGE).floatValue());
int duration = Spells.permafrost.getProperty(Spell.EFFECT_DURATION).intValue();
int amplifier = Spells.permafrost.getProperty(Spell.EFFECT_STRENGTH).intValue();
((EntityLivingBase)entity).addPotionEffect(new PotionEffect(WizardryPotions.frost, duration, amplifier));
}
// EntityLivingBase's slipperiness code doesn't get the block below it properly so slipperiness only works for
// full blocks...
if(entity.onGround){
// Not brilliant but it's about the best I can do
entity.motionX *= 1.12 - entity.motionX * entity.motionX;
entity.motionZ *= 1.12 - entity.motionZ * entity.motionZ;
// if(entity instanceof EntityLivingBase){
// double maxVel = 0.8;
// double x = entity.motionX;
// double y = entity.motionY;
// double z = entity.motionZ;
// double vel = MathHelper.sqrt(x*x + y*y + z*z);
// double m = vel / maxVel;
// if(m > 1){
// entity.motionX /= m;
// entity.motionZ /= m;
// }
// }
}
}
}
@@ -0,0 +1,310 @@
package electroblob.wizardry.block;
import com.google.common.collect.Maps;
import electroblob.wizardry.constants.Element;
import electroblob.wizardry.item.ItemSpectralDust;
import electroblob.wizardry.registry.*;
import electroblob.wizardry.tileentity.TileEntityReceptacle;
import electroblob.wizardry.util.GeometryUtils;
import electroblob.wizardry.util.ParticleBuilder;
import net.minecraft.block.Block;
import net.minecraft.block.BlockTorch;
import net.minecraft.block.ITileEntityProvider;
import net.minecraft.block.SoundType;
import net.minecraft.block.state.BlockFaceShape;
import net.minecraft.block.state.IBlockState;
import net.minecraft.entity.EntityLivingBase;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.item.ItemStack;
import net.minecraft.tileentity.TileEntity;
import net.minecraft.util.EnumFacing;
import net.minecraft.util.EnumHand;
import net.minecraft.util.NonNullList;
import net.minecraft.util.SoundCategory;
import net.minecraft.util.math.AxisAlignedBB;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.Vec3d;
import net.minecraft.world.IBlockAccess;
import net.minecraft.world.World;
import javax.annotation.Nullable;
import java.util.Arrays;
import java.util.Map;
import java.util.Random;
public class BlockReceptacle extends BlockTorch implements ITileEntityProvider {
protected static final AxisAlignedBB STANDING_AABB = new AxisAlignedBB(4 / 16d, 0 / 16d, 4 / 16d, 12 / 16d, 8 / 16d, 12 / 16d);
protected static final AxisAlignedBB NORTH_WALL_AABB = new AxisAlignedBB(4 / 16d, 2 / 16d, 7 / 16d, 12 / 16d, 10 / 16d, 16 / 16d);
protected static final AxisAlignedBB SOUTH_WALL_AABB = new AxisAlignedBB(4 / 16d, 2 / 16d, 0 / 16d, 12 / 16d, 10 / 16d, 9 / 16d);
protected static final AxisAlignedBB WEST_WALL_AABB = new AxisAlignedBB(7 / 16d, 2 / 16d, 4 / 16d, 16 / 16d, 10 / 16d, 12 / 16d);
protected static final AxisAlignedBB EAST_WALL_AABB = new AxisAlignedBB(0 / 16d, 2 / 16d, 4 / 16d, 9 / 16d, 10 / 16d, 12 / 16d);
private static final double WALL_PARTICLE_OFFSET = 3 / 16d;
public static final Map<Element, int[]> PARTICLE_COLOURS;
static{
Map<Element, int[]> map = Maps.newEnumMap(Element.class);
map.put(Element.MAGIC, new int[]{0xe4c7cd, 0xfeffbe, 0x9d2cf3});
map.put(Element.FIRE, new int[]{0xff9600, 0xfffe67, 0xd02700});
map.put(Element.ICE, new int[]{0xa3e8f4, 0xe9f9fc, 0x138397});
map.put(Element.LIGHTNING, new int[]{0x409ee1, 0xf5f0ff, 0x225474});
map.put(Element.NECROMANCY, new int[]{0xa811ce, 0xf575f5, 0x382366});
map.put(Element.EARTH, new int[]{0xa8f408, 0xc8ffb2, 0x795c28});
map.put(Element.SORCERY, new int[]{0x56e8e3, 0xe8fcfc, 0x16a64d});
map.put(Element.HEALING, new int[]{0xfff69e, 0xfffff6, 0xa18200});
PARTICLE_COLOURS = Maps.immutableEnumMap(map);
}
public BlockReceptacle(){
super();
this.setCreativeTab(WizardryTabs.WIZARDRY);
this.setHardness(0);
this.setLightLevel(0.5f);
this.setSoundType(SoundType.STONE);
}
@Override
public AxisAlignedBB getBoundingBox(IBlockState state, IBlockAccess world, BlockPos pos){
switch(state.getValue(FACING)){
case EAST:
return EAST_WALL_AABB;
case WEST:
return WEST_WALL_AABB;
case SOUTH:
return SOUTH_WALL_AABB;
case NORTH:
return NORTH_WALL_AABB;
default:
return STANDING_AABB;
}
}
@Override
public AxisAlignedBB getCollisionBoundingBox(IBlockState state, IBlockAccess world, BlockPos pos){
return state.getBoundingBox(world, pos);
}
@Override
public int getLightValue(IBlockState state, IBlockAccess world, BlockPos pos){
TileEntity tileEntity = world.getTileEntity(pos);
if(tileEntity instanceof TileEntityReceptacle && ((TileEntityReceptacle)tileEntity).getElement() != null){
return super.getLightValue(state, world, pos); // Return super to use float value from constructor
}
return 0;
}
@Override
public void getDrops(NonNullList<ItemStack> drops, IBlockAccess world, BlockPos pos, IBlockState state, int fortune){
super.getDrops(drops, world, pos, state, fortune);
TileEntity tileEntity = world.getTileEntity(pos);
if(tileEntity instanceof TileEntityReceptacle){
Element element = ((TileEntityReceptacle)tileEntity).getElement();
if(element != null) drops.add(new ItemStack(WizardryItems.spectral_dust, 1, element.ordinal()));
}
}
@Override
public boolean canPlaceBlockOnSide(World world, BlockPos pos, EnumFacing side){
if(side != EnumFacing.UP && side != EnumFacing.DOWN
&& world.getBlockState(pos.offset(side.getOpposite())).getBlock() instanceof BlockImbuementAltar){
return true;
}
return super.canPlaceBlockOnSide(world, pos, side);
}
@Override
protected boolean checkForDrop(World world, BlockPos pos, IBlockState state){
if(state.getBlock() == this && this.canPlaceAt(world, pos, state.getValue(FACING))){
return true;
}
return super.checkForDrop(world, pos, state);
}
@Override
protected boolean onNeighborChangeInternal(World world, BlockPos pos, IBlockState state){
// This is so stupid, BlockTorch DUPLICATES the code that checks for valid placement in the super method instead
// of checking the existing methods...
return !this.checkForDrop(world, pos, state); // Literally all we need. None of the rubbish in super.
}
@Override
public IBlockState getStateForPlacement(World world, BlockPos pos, EnumFacing facing, float hitX, float hitY, float hitZ, int meta, EntityLivingBase placer){
if(this.canPlaceAt(world, pos, facing)){
return this.getDefaultState().withProperty(FACING, facing);
}else{
for(EnumFacing enumfacing : EnumFacing.Plane.HORIZONTAL){
if(this.canPlaceAt(world, pos, enumfacing)){
return this.getDefaultState().withProperty(FACING, enumfacing);
}
}
return this.getDefaultState();
}
}
// Why is everything in BlockTorch private, for goodness sake...
private boolean canPlaceAt(World world, BlockPos pos, EnumFacing facing){
BlockPos blockpos = pos.offset(facing.getOpposite());
IBlockState state = world.getBlockState(blockpos);
Block block = state.getBlock();
BlockFaceShape blockfaceshape = state.getBlockFaceShape(world, blockpos, facing);
if(facing.equals(EnumFacing.UP) && this.canPlaceOn(world, blockpos)){
return true;
}else if(facing != EnumFacing.UP && facing != EnumFacing.DOWN){
return !isExceptBlockForAttachWithPiston(block) && (blockfaceshape == BlockFaceShape.SOLID || block instanceof BlockImbuementAltar);
}else{
return false;
}
}
private boolean canPlaceOn(World world, BlockPos pos){
IBlockState state = world.getBlockState(pos);
return state.getBlock().canPlaceTorchOnTop(state, world, pos);
}
// See BlockFlowerPot for these two (this class is essentially based on a flower pot)
@Override
public boolean removedByPlayer(IBlockState state, World world, BlockPos pos, EntityPlayer player, boolean willHarvest){
if(willHarvest) return true; // If it will harvest, delay deletion of the block until after getDrops
return super.removedByPlayer(state, world, pos, player, willHarvest);
}
@Override
public void harvestBlock(World world, EntityPlayer player, BlockPos pos, IBlockState state, @Nullable TileEntity te, ItemStack tool){
super.harvestBlock(world, player, pos, state, te, tool);
world.setBlockToAir(pos);
}
@Override
public boolean onBlockActivated(World world, BlockPos pos, IBlockState state, EntityPlayer player, EnumHand hand, EnumFacing facing, float hitX, float hitY, float hitZ){
TileEntity tileEntity = world.getTileEntity(pos);
ItemStack stack = player.getHeldItem(hand);
if(tileEntity instanceof TileEntityReceptacle){
Element currentElement = ((TileEntityReceptacle)tileEntity).getElement();
if(currentElement == null){
if(stack.getItem() instanceof ItemSpectralDust && stack.getMetadata() >= 0
&& stack.getMetadata() < Element.values().length){
((TileEntityReceptacle)tileEntity).setElement(Element.values()[stack.getMetadata()]);
if(!player.capabilities.isCreativeMode) stack.shrink(1);
world.playSound(pos.getX(), pos.getY(), pos.getZ(), WizardrySounds.BLOCK_RECEPTACLE_IGNITE,
SoundCategory.BLOCKS, 0.7f, 0.7f, false);
return true;
}
}else{
((TileEntityReceptacle)tileEntity).setElement(null);
ItemStack dust = new ItemStack(WizardryItems.spectral_dust, 1, currentElement.ordinal());
if(stack.isEmpty()){
player.setHeldItem(hand, dust);
}else if(!player.addItemStackToInventory(dust)){
player.dropItem(dust, false);
}
return true;
}
}
return super.onBlockActivated(world, pos, state, player, hand, facing, hitX, hitY, hitZ);
}
@Override
public void randomDisplayTick(IBlockState state, World world, BlockPos pos, Random rand){
TileEntity tileEntity = world.getTileEntity(pos);
if(tileEntity instanceof TileEntityReceptacle){
Element element = ((TileEntityReceptacle)tileEntity).getElement();
if(element != null){
EnumFacing facing = state.getValue(FACING).getOpposite();
Vec3d centre = GeometryUtils.getCentre(pos);
if(facing.getAxis().isHorizontal()){
centre = centre.add(new Vec3d(facing.getDirectionVec()).scale(WALL_PARTICLE_OFFSET)).add(0, 0.125, 0);
}
int[] colours = PARTICLE_COLOURS.get(element);
ParticleBuilder.create(ParticleBuilder.Type.FLASH).pos(centre).scale(0.35f).time(48).clr(colours[0]).spawn(world);
double r = 0.12;
for(int i = 0; i < 3; i++){
double x = r * (rand.nextDouble() * 2 - 1);
double y = r * (rand.nextDouble() * 2 - 1);
double z = r * (rand.nextDouble() * 2 - 1);
ParticleBuilder.create(ParticleBuilder.Type.DUST).pos(centre.x + x, centre.y + y, centre.z + z)
.vel(x * -0.03, 0.02, z * -0.03).time(24 + rand.nextInt(8)).clr(colours[1]).fade(colours[2]).spawn(world);
}
}
}
}
@Override
public void onBlockPlacedBy(World world, BlockPos pos, IBlockState state, EntityLivingBase placer, ItemStack stack){
if(placer instanceof EntityPlayer){
BlockPos centre = pos.offset(world.getBlockState(pos).getValue(FACING).getOpposite());
if(world.getBlockState(centre).getBlock() == WizardryBlocks.imbuement_altar
&& Arrays.stream(EnumFacing.HORIZONTALS).allMatch(f -> world.getBlockState(centre.offset(f)).getBlock() == WizardryBlocks.receptacle)){
WizardryAdvancementTriggers.restore_imbuement_altar.triggerFor((EntityPlayer)placer);
}
}
}
@Nullable
@Override
public TileEntity createNewTileEntity(World world, int meta){
return new TileEntityReceptacle();
}
@Override
public boolean hasComparatorInputOverride(IBlockState state){
return true;
}
@Override
public int getComparatorInputOverride(IBlockState state, World world, BlockPos pos){
TileEntity tileEntity = world.getTileEntity(pos);
if(tileEntity instanceof TileEntityReceptacle){
Element element = ((TileEntityReceptacle)tileEntity).getElement();
return element == null ? 0 : element.ordinal() + 1;
}
return super.getComparatorInputOverride(state, world, pos);
}
}
@@ -69,7 +69,9 @@ public class BlockRunestone extends Block {
@Override
public IBlockState getStateFromMeta(int metadata){
return this.getDefaultState().withProperty(ELEMENT, Element.values()[metadata]);
Element element = Element.values()[metadata];
if(!ELEMENT.getAllowedValues().contains(element)) return this.getDefaultState();
return this.getDefaultState().withProperty(ELEMENT, element);
}
@Override
@@ -1,13 +1,14 @@
package electroblob.wizardry.block;
import electroblob.wizardry.tileentity.TileEntityStatue;
import electroblob.wizardry.util.WizardryUtilities;
import electroblob.wizardry.util.BlockUtils;
import net.minecraft.block.Block;
import net.minecraft.block.ITileEntityProvider;
import net.minecraft.block.SoundType;
import net.minecraft.block.material.Material;
import net.minecraft.block.state.IBlockState;
import net.minecraft.entity.EntityLiving;
import net.minecraft.entity.EntityLivingBase;
import net.minecraft.tileentity.TileEntity;
import net.minecraft.util.BlockRenderLayer;
import net.minecraft.util.EnumBlockRenderType;
@@ -19,6 +20,7 @@ import net.minecraft.world.World;
import net.minecraftforge.fml.relauncher.Side;
import net.minecraftforge.fml.relauncher.SideOnly;
import javax.annotation.Nullable;
import java.util.Random;
public class BlockStatue extends Block implements ITileEntityProvider {
@@ -180,77 +182,77 @@ public class BlockStatue extends Block implements ITileEntityProvider {
/**
* Turns the given entity into a statue. The type of statue depends on the block instance this method was invoked on.
* @param entity The entity to turn into a statue.
* @param target The entity to turn into a statue.
* @param caster The entity that caused it, or null if it was not caused by an entity
* @param duration The time for which the entity should remain a statue. For petrified creatures, this is the minimum
* time it can stay as a statue.
* @return True if the entity was successfully turned into a statue, false if not (i.e. something was in the way).
*/
// Making this an instance method means it works equally well for both types of statue
public boolean convertToStatue(EntityLiving entity, int duration){
public boolean convertToStatue(EntityLiving target, @Nullable EntityLivingBase caster, int duration){
if(entity.deathTime > 0) return false;
if(target.deathTime > 0) return false;
BlockPos pos = new BlockPos(entity);
World world = entity.world;
BlockPos pos = new BlockPos(target);
World world = target.world;
entity.hurtTime = 0; // Stops the entity looking red while frozen and the resulting z-fighting
entity.extinguish();
target.hurtTime = 0; // Stops the entity looking red while frozen and the resulting z-fighting
target.extinguish();
// Short mobs such as spiders and pigs
if((entity.height < 1.2 || entity.isChild()) && WizardryUtilities.canBlockBeReplaced(world, pos)){
if((target.height < 1.2 || target.isChild()) && BlockUtils.canBlockBeReplaced(world, pos) && BlockUtils.canPlaceBlock(caster, world, pos)){
world.setBlockState(pos, this.getDefaultState());
if(world.getTileEntity(pos) instanceof TileEntityStatue){
((TileEntityStatue)world.getTileEntity(pos)).setCreatureAndPart(entity, 1, 1);
((TileEntityStatue)world.getTileEntity(pos)).setCreatureAndPart(target, 1, 1);
((TileEntityStatue)world.getTileEntity(pos)).setLifetime(duration);
}
entity.getEntityData().setBoolean(this.isIce ? FROZEN_NBT_KEY : PETRIFIED_NBT_KEY, true);
entity.setDead();
target.getEntityData().setBoolean(this.isIce ? FROZEN_NBT_KEY : PETRIFIED_NBT_KEY, true);
target.setDead();
return true;
}
// Normal sized mobs like zombies and skeletons
else if(entity.height < 2.5 && WizardryUtilities.canBlockBeReplaced(world, pos)
&& WizardryUtilities.canBlockBeReplaced(world, pos.up())){
else if(target.height < 2.5 && BlockUtils.canBlockBeReplaced(world, pos) && BlockUtils.canBlockBeReplaced(world, pos.up())
&& BlockUtils.canPlaceBlock(caster, world, pos) && BlockUtils.canPlaceBlock(caster, world, pos.up())){
world.setBlockState(pos, this.getDefaultState());
if(world.getTileEntity(pos) instanceof TileEntityStatue){
((TileEntityStatue)world.getTileEntity(pos)).setCreatureAndPart(entity, 1, 2);
((TileEntityStatue)world.getTileEntity(pos)).setCreatureAndPart(target, 1, 2);
((TileEntityStatue)world.getTileEntity(pos)).setLifetime(duration);
}
world.setBlockState(pos.up(), this.getDefaultState());
if(world.getTileEntity(pos.up()) instanceof TileEntityStatue){
((TileEntityStatue)world.getTileEntity(pos.up())).setCreatureAndPart(entity, 2, 2);
((TileEntityStatue)world.getTileEntity(pos.up())).setCreatureAndPart(target, 2, 2);
}
entity.getEntityData().setBoolean(this.isIce ? FROZEN_NBT_KEY : PETRIFIED_NBT_KEY, true);
entity.setDead();
target.getEntityData().setBoolean(this.isIce ? FROZEN_NBT_KEY : PETRIFIED_NBT_KEY, true);
target.setDead();
return true;
}
// Tall mobs like endermen
else if(WizardryUtilities.canBlockBeReplaced(world, pos)
&& WizardryUtilities.canBlockBeReplaced(world, pos.up())
&& WizardryUtilities.canBlockBeReplaced(world, pos.up(2))){
else if(BlockUtils.canBlockBeReplaced(world, pos) && BlockUtils.canBlockBeReplaced(world, pos.up()) && BlockUtils.canBlockBeReplaced(world, pos.up(2))
&& BlockUtils.canPlaceBlock(caster, world, pos) && BlockUtils.canPlaceBlock(caster, world, pos.up()) && BlockUtils.canPlaceBlock(caster, world, pos.up(2))){
world.setBlockState(pos, this.getDefaultState());
if(world.getTileEntity(pos) instanceof TileEntityStatue){
((TileEntityStatue)world.getTileEntity(pos)).setCreatureAndPart(entity, 1, 3);
((TileEntityStatue)world.getTileEntity(pos)).setCreatureAndPart(target, 1, 3);
((TileEntityStatue)world.getTileEntity(pos)).setLifetime(duration);
}
world.setBlockState(pos.up(), this.getDefaultState());
if(world.getTileEntity(pos.up()) instanceof TileEntityStatue){
((TileEntityStatue)world.getTileEntity(pos.up())).setCreatureAndPart(entity, 2, 3);
((TileEntityStatue)world.getTileEntity(pos.up())).setCreatureAndPart(target, 2, 3);
}
world.setBlockState(pos.up(2), this.getDefaultState());
if(world.getTileEntity(pos.up(2)) instanceof TileEntityStatue){
((TileEntityStatue)world.getTileEntity(pos.up(2))).setCreatureAndPart(entity, 3, 3);
((TileEntityStatue)world.getTileEntity(pos.up(2))).setCreatureAndPart(target, 3, 3);
}
entity.getEntityData().setBoolean(this.isIce ? FROZEN_NBT_KEY : PETRIFIED_NBT_KEY, true);
entity.setDead();
target.getEntityData().setBoolean(this.isIce ? FROZEN_NBT_KEY : PETRIFIED_NBT_KEY, true);
target.setDead();
return true;
}
@@ -3,8 +3,9 @@ package electroblob.wizardry.block;
import electroblob.wizardry.registry.Spells;
import electroblob.wizardry.registry.WizardryBlocks;
import electroblob.wizardry.spell.Spell;
import electroblob.wizardry.tileentity.TileEntityPlayerSaveTimed;
import electroblob.wizardry.tileentity.TileEntityThorns;
import electroblob.wizardry.util.AllyDesignationSystem;
import electroblob.wizardry.util.EntityUtils;
import electroblob.wizardry.util.MagicDamage;
import net.minecraft.block.*;
import net.minecraft.block.BlockDoublePlant.EnumBlockHalf;
@@ -22,8 +23,10 @@ import net.minecraft.tileentity.TileEntity;
import net.minecraft.util.DamageSource;
import net.minecraft.util.math.AxisAlignedBB;
import net.minecraft.util.math.BlockPos;
import net.minecraft.world.ChunkCache;
import net.minecraft.world.IBlockAccess;
import net.minecraft.world.World;
import net.minecraft.world.chunk.Chunk;
import net.minecraftforge.event.entity.player.PlayerInteractEvent;
import net.minecraftforge.fml.common.Mod;
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
@@ -34,6 +37,7 @@ import java.util.Random;
public class BlockThorns extends BlockBush implements ITileEntityProvider {
public static final int GROWTH_STAGES = 8;
public static final int GROWTH_STAGE_DURATION = 2;
public static final PropertyInteger AGE = PropertyInteger.create("age", 0, GROWTH_STAGES-1);
public static final PropertyEnum<EnumBlockHalf> HALF = PropertyEnum.create("half", EnumBlockHalf.class);
@@ -52,22 +56,27 @@ public class BlockThorns extends BlockBush implements ITileEntityProvider {
@Override
public IBlockState getStateFromMeta(int meta){
return this.getDefaultState().withProperty(HALF, EnumBlockHalf.values()[meta / GROWTH_STAGES]).withProperty(AGE, meta % GROWTH_STAGES);
return this.getDefaultState().withProperty(HALF, meta == 0 ? EnumBlockHalf.LOWER : EnumBlockHalf.UPPER);
}
@Override
public int getMetaFromState(IBlockState state){
return state.getValue(HALF).ordinal() * GROWTH_STAGES + state.getValue(AGE);
return state.getValue(HALF).ordinal();
}
// @Override
// public void updateTick(World world, BlockPos pos, IBlockState state, Random rand){
//
// super.updateTick(world, pos, state, rand);
//
// // Update the state, including on the client, but don't do a block update since it's only visual
// if(state.getValue(AGE) < GROWTH_STAGES-1) world.setBlockState(pos, state.withProperty(AGE, state.getValue(AGE) + 1), 2);
// }
@Override
public IBlockState getActualState(IBlockState state, IBlockAccess world, BlockPos pos){
if(state.getValue(HALF) == EnumBlockHalf.UPPER) pos = pos.down();
// Copied from BlockFlowerPot on authority of the Forge docs, which say it needs to be here
TileEntity tileentity = world instanceof ChunkCache ? ((ChunkCache)world).getTileEntity(pos, Chunk.EnumCreateEntityType.CHECK) : world.getTileEntity(pos);
if(tileentity instanceof TileEntityThorns){
return state.withProperty(AGE, ((TileEntityThorns)tileentity).getAge());
}else{
return state.withProperty(AGE, 7);
}
}
@Override
protected BlockStateContainer createBlockState(){
@@ -107,47 +116,36 @@ public class BlockThorns extends BlockBush implements ITileEntityProvider {
}
}
// @Override
// public IBlockState getActualState(IBlockState state, IBlockAccess world, BlockPos pos){
// // Copied from BlockFlowerPot on authority of the Forge docs, which says this check is necessary
// SoundLoopSpellDispenser tileentity = world instanceof ChunkCache ? ((ChunkCache)world).getTileEntity(pos, Chunk.EnumCreateEntityType.CHECK) : world.getTileEntity(pos);
//
// if(tileentity instanceof TileEntityPlayerSaveTimed){
// state = state.withProperty(AGE, Math.min(7, ((TileEntityPlayerSaveTimed)tileentity).timer/2));
// }else{
// state = state.withProperty(AGE, 7);
// }
//
// return state;
// }
@Override
public void onEntityCollision(World world, BlockPos pos, IBlockState state, Entity entity){
if(!world.isRemote){
if(applyThornDamage(world, pos, entity)){
entity.setInWeb();
}
if(applyThornDamage(world, pos, state, entity)){
entity.setInWeb(); // Needs to be called client-side for players (and besides, all of this is common code)
}
}
private static boolean applyThornDamage(World world, BlockPos pos, Entity target){
private static boolean applyThornDamage(World world, BlockPos pos, IBlockState state, Entity target){
DamageSource source = DamageSource.CACTUS;
float damage = Spells.forest_of_thorns.getProperty(Spell.DAMAGE).floatValue();
TileEntity tileentity = world.getTileEntity(pos);
TileEntity tileentity = world.getTileEntity(state.getValue(HALF) == EnumBlockHalf.UPPER ? pos.down() : pos);
if(tileentity instanceof TileEntityPlayerSaveTimed){
if(tileentity instanceof TileEntityThorns){
EntityLivingBase caster = ((TileEntityPlayerSaveTimed)tileentity).getCaster();
damage *= ((TileEntityThorns)tileentity).damageMultiplier;
if(caster != null && AllyDesignationSystem.isValidTarget(caster, target)){
EntityLivingBase caster = ((TileEntityThorns)tileentity).getCaster();
if(!AllyDesignationSystem.isValidTarget(caster, target)) return false; // Don't attack or slow allies of the caster
if(caster != null){
source = MagicDamage.causeDirectMagicDamage(caster, MagicDamage.DamageType.MAGIC);
}else{
return false; // Don't attack or slow allies of the caster
}
}
if(world.getTotalWorldTime() % 20 == 0) target.attackEntityFrom(source, Spells.forest_of_thorns.getProperty(Spell.DAMAGE).floatValue());
if(target.ticksExisted % 20 == 0){
EntityUtils.attackEntityWithoutKnockback(target, source, damage);
}
return true;
}
@@ -159,12 +157,12 @@ public class BlockThorns extends BlockBush implements ITileEntityProvider {
@Override
public TileEntity createNewTileEntity(World world, int metadata){
return new TileEntityPlayerSaveTimed(600);
return new TileEntityThorns();
}
@Override
public boolean hasTileEntity(IBlockState state){
return true;
return state.getValue(HALF) == EnumBlockHalf.LOWER;
}
@Override public boolean isReplaceable(IBlockAccess world, BlockPos pos){ return false; }
@@ -174,9 +172,8 @@ public class BlockThorns extends BlockBush implements ITileEntityProvider {
@SubscribeEvent
public static void onLeftClickBlockEvent(PlayerInteractEvent.LeftClickBlock event){
if(!event.getWorld().isRemote && event.getWorld().getTotalWorldTime() % 20 == 0
&& event.getWorld().getBlockState(event.getPos()).getBlock() == WizardryBlocks.thorns){
applyThornDamage(event.getWorld(), event.getPos(), event.getEntity());
if(!event.getWorld().isRemote && event.getWorld().getBlockState(event.getPos()).getBlock() == WizardryBlocks.thorns){
applyThornDamage(event.getWorld(), event.getPos(), event.getWorld().getBlockState(event.getPos()), event.getEntity());
}
}
@@ -8,9 +8,9 @@ import electroblob.wizardry.registry.Spells;
import electroblob.wizardry.registry.WizardryBlocks;
import electroblob.wizardry.registry.WizardryItems;
import electroblob.wizardry.spell.Transportation;
import electroblob.wizardry.util.GeometryUtils;
import electroblob.wizardry.util.Location;
import electroblob.wizardry.util.ParticleBuilder;
import electroblob.wizardry.util.WizardryUtilities;
import net.minecraft.block.Block;
import net.minecraft.block.material.Material;
import net.minecraft.block.state.IBlockState;
@@ -162,7 +162,7 @@ public class BlockTransportationStone extends Block {
for(int z = -1; z <= 1; z++){
if(x == 0 && z == 0) continue;
ParticleBuilder.create(ParticleBuilder.Type.PATH)
.pos(WizardryUtilities.getCentre(centre).add(x, -0.3125, z)).clr(0x86ff65)
.pos(GeometryUtils.getCentre(centre).add(x, -0.3125, z)).clr(0x86ff65)
.time(200).scale(2).spawn(world);
}
}
@@ -174,10 +174,11 @@ public class BlockTransportationStone extends Block {
return false;
}
/** Returns whether the specified location is surrounded by a complete cicle of 8 transportation stones. */
/** Returns whether the specified location is surrounded by a complete circle of 8 transportation stones. */
public static boolean testForCircle(World world, BlockPos pos){
if(world.getBlockState(pos).getMaterial().blocksMovement()) return false;
if(world.getBlockState(pos).getMaterial().blocksMovement() || world.getBlockState(pos.up()).getMaterial()
.blocksMovement()) return false;
for(int x = -1; x <= 1; x++){
for(int z = -1; z <= 1; z++){
@@ -2,18 +2,29 @@ package electroblob.wizardry.client;
import electroblob.wizardry.CommonProxy;
import electroblob.wizardry.Wizardry;
import electroblob.wizardry.block.BlockBookshelf;
import electroblob.wizardry.client.animation.ActionAnimation;
import electroblob.wizardry.client.animation.PlayerAnimator;
import electroblob.wizardry.client.audio.MovingSoundEntity;
import electroblob.wizardry.client.audio.MovingSoundSpellCharge;
import electroblob.wizardry.client.audio.SoundLoop;
import electroblob.wizardry.client.audio.SoundLoopSpell;
import electroblob.wizardry.client.gui.GuiLectern;
import electroblob.wizardry.client.gui.GuiSpellDisplay;
import electroblob.wizardry.client.gui.config.NamedBooleanEntry;
import electroblob.wizardry.client.gui.config.SpellHUDSkinChooserEntry;
import electroblob.wizardry.client.gui.handbook.GuiWizardHandbook;
import electroblob.wizardry.client.model.ModelRobeArmour;
import electroblob.wizardry.client.model.ModelSageArmour;
import electroblob.wizardry.client.model.ModelWizardArmour;
import electroblob.wizardry.client.particle.*;
import electroblob.wizardry.client.particle.ParticleWizardry.IWizardryParticleFactory;
import electroblob.wizardry.client.renderer.*;
import electroblob.wizardry.client.renderer.entity.*;
import electroblob.wizardry.client.renderer.entity.layers.*;
import electroblob.wizardry.client.renderer.overlay.RenderBlinkEffect;
import electroblob.wizardry.client.renderer.tileentity.*;
import electroblob.wizardry.command.SpellEmitter;
import electroblob.wizardry.constants.Constants;
import electroblob.wizardry.data.DispenserCastingData;
import electroblob.wizardry.data.SpellEmitterData;
import electroblob.wizardry.data.SpellGlyphData;
@@ -30,12 +41,10 @@ import electroblob.wizardry.item.ItemSpellBook;
import electroblob.wizardry.item.ItemWand;
import electroblob.wizardry.packet.*;
import electroblob.wizardry.registry.Spells;
import electroblob.wizardry.registry.WizardryItems.Materials;
import electroblob.wizardry.registry.WizardrySounds;
import electroblob.wizardry.spell.*;
import electroblob.wizardry.tileentity.TileEntityArcaneWorkbench;
import electroblob.wizardry.tileentity.TileEntityMagicLight;
import electroblob.wizardry.tileentity.TileEntityShrineCore;
import electroblob.wizardry.tileentity.TileEntityStatue;
import electroblob.wizardry.tileentity.*;
import electroblob.wizardry.util.ParticleBuilder;
import electroblob.wizardry.util.ParticleBuilder.Type;
import electroblob.wizardry.util.WandHelper;
@@ -59,6 +68,7 @@ import net.minecraft.entity.monster.EntityBlaze;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.inventory.ContainerMerchant;
import net.minecraft.item.Item;
import net.minecraft.item.ItemArmor.ArmorMaterial;
import net.minecraft.item.ItemStack;
import net.minecraft.tileentity.TileEntity;
import net.minecraft.tileentity.TileEntityDispenser;
@@ -72,6 +82,8 @@ import net.minecraft.util.text.Style;
import net.minecraft.util.text.TextComponentTranslation;
import net.minecraft.village.MerchantRecipeList;
import net.minecraft.world.World;
import net.minecraftforge.client.settings.KeyConflictContext;
import net.minecraftforge.client.settings.KeyModifier;
import net.minecraftforge.common.MinecraftForge;
import net.minecraftforge.common.config.Property;
import net.minecraftforge.fml.client.config.GuiConfigEntries.NumberSliderEntry;
@@ -79,6 +91,7 @@ import net.minecraftforge.fml.client.registry.ClientRegistry;
import net.minecraftforge.fml.client.registry.RenderingRegistry;
import org.lwjgl.input.Keyboard;
import javax.annotation.Nullable;
import java.lang.ref.WeakReference;
import java.util.HashMap;
import java.util.List;
@@ -100,27 +113,44 @@ public class ClientProxy extends CommonProxy {
private static final Map<ResourceLocation, IWizardryParticleFactory> factories = new HashMap<>();
// Key Bindings
public static final KeyBinding NEXT_SPELL = new KeyBinding("key." + Wizardry.MODID + ".next_spell", Keyboard.KEY_N, "key.categories." + Wizardry.MODID);
public static final KeyBinding PREVIOUS_SPELL = new KeyBinding("key." + Wizardry.MODID + ".previous_spell", Keyboard.KEY_B, "key.categories." + Wizardry.MODID);
public static final KeyBinding NEXT_SPELL = new KeyBinding("key." + Wizardry.MODID + ".next_spell", KeyConflictContext.IN_GAME, Keyboard.KEY_N, "key.categories." + Wizardry.MODID);
public static final KeyBinding PREVIOUS_SPELL = new KeyBinding("key." + Wizardry.MODID + ".previous_spell", KeyConflictContext.IN_GAME, Keyboard.KEY_B, "key.categories." + Wizardry.MODID);
public static final KeyBinding[] SPELL_QUICK_ACCESS = new KeyBinding[ItemWand.BASE_SPELL_SLOTS + Constants.UPGRADE_STACK_LIMIT];
// Armour Model
public static final ModelBiped WIZARD_ARMOUR_MODEL = new ModelWizardArmour(0.75f);
static {
for(int i = 0; i < SPELL_QUICK_ACCESS.length; i++){
SPELL_QUICK_ACCESS[i] = new KeyBinding("key." + Wizardry.MODID + ".spell_" + (i+1),
KeyConflictContext.IN_GAME, KeyModifier.ALT, Keyboard.KEY_1 + i, "key.categories." + Wizardry.MODID);
}
}
// Armour Models
// Can't use an EnumMap here because our additional values aren't really part of the enum
public static final Map<ArmorMaterial, ModelBiped> wizard_armour_models = new HashMap<>();
static {
wizard_armour_models.put(Materials.SILK, new ModelWizardArmour(0.75f));
wizard_armour_models.put(Materials.SAGE, new ModelSageArmour(0.75f));
wizard_armour_models.put(Materials.BATTLEMAGE, new ModelRobeArmour(0.75f, true));
wizard_armour_models.put(Materials.WARLOCK, new ModelRobeArmour(0.75f, false));
}
/** The wrap width for standard multi-line descriptions (see {@link ClientProxy#addMultiLineDescription(List, String, Style, Object...)}). */
private static final int TOOLTIP_WRAP_WIDTH = 140;
// SECTION Registry
// Registry
// ===============================================================================================================
@Override
public ModelBiped getWizardArmourModel(){
return WIZARD_ARMOUR_MODEL;
public ModelBiped getWizardArmourModel(ArmorMaterial material){
return wizard_armour_models.get(material);
}
@Override
public void registerKeyBindings(){
ClientRegistry.registerKeyBinding(NEXT_SPELL);
ClientRegistry.registerKeyBinding(PREVIOUS_SPELL);
for(KeyBinding key : SPELL_QUICK_ACCESS) ClientRegistry.registerKeyBinding(key);
}
@Override
@@ -128,7 +158,7 @@ public class ClientProxy extends CommonProxy {
mixedFontRenderer = new MixedFontRenderer(Minecraft.getMinecraft().gameSettings, new ResourceLocation("textures/font/ascii.png"),
Minecraft.getMinecraft().renderEngine, false);
}
@Override
public void registerResourceReloadListeners(){
IResourceManager manager = Minecraft.getMinecraft().getResourceManager();
@@ -147,14 +177,14 @@ public class ClientProxy extends CommonProxy {
WizardryAntiqueAtlasIntegration.registerMarkers();
}
// SECTION Misc
// Misc
// ===============================================================================================================
@Override
public void setToNumberSliderEntry(Property property){
property.setConfigEntryClass(NumberSliderEntry.class);
}
@Override
public void setToHUDChooserEntry(Property property){
property.setConfigEntryClass(SpellHUDSkinChooserEntry.class);
@@ -170,11 +200,65 @@ public class ClientProxy extends CommonProxy {
return Minecraft.getMinecraft().world;
}
@Override
public EntityPlayer getThePlayer(){
return Minecraft.getMinecraft().player;
}
@Override
public boolean isFirstPerson(Entity entity){
return entity == Minecraft.getMinecraft().getRenderViewEntity() && Minecraft.getMinecraft().gameSettings.thirdPersonView == 0;
}
@Override
public void playBlinkEffect(EntityPlayer player){
if(Minecraft.getMinecraft().player == player) RenderBlinkEffect.playBlinkEffect();
}
@Override
public void shakeScreen(EntityPlayer player, float intensity){
if(Minecraft.getMinecraft().player == player) ScreenShakeHandler.shakeScreen(intensity);
}
@Override
public void loadShader(EntityPlayer player, ResourceLocation shader){
if(Minecraft.getMinecraft().player == player && Wizardry.settings.useShaders
&& !Minecraft.getMinecraft().entityRenderer.isShaderActive())
Minecraft.getMinecraft().entityRenderer.loadShader(shader);
}
@Override
public Set<String> getSpellHUDSkins(){
return GuiSpellDisplay.getSkinKeys();
}
@Override
public void notifyBookshelfChange(World world, BlockPos pos){
super.notifyBookshelfChange(world, pos);
EntityPlayer player = Minecraft.getMinecraft().player;
if(player.getDistanceSq(pos) < BlockBookshelf.PLAYER_NOTIFY_RANGE * BlockBookshelf.PLAYER_NOTIFY_RANGE){
if(Minecraft.getMinecraft().currentScreen instanceof GuiLectern){
((GuiLectern)Minecraft.getMinecraft().currentScreen).refreshAvailableSpells();
}
}
}
// Sound
// ===============================================================================================================
@Override
public void playMovingSound(Entity entity, SoundEvent sound, SoundCategory category, float volume, float pitch, boolean repeat){
Minecraft.getMinecraft().getSoundHandler().playSound(new MovingSoundEntity<>(entity, sound, category, volume, pitch, repeat));
}
@Override
public void playChargeupSound(EntityLivingBase entity){
Minecraft.getMinecraft().getSoundHandler().playSound(new MovingSoundSpellCharge(entity, WizardrySounds.ITEM_WAND_CHARGEUP, WizardrySounds.SPELLS, 2.5f, 1.4f, false));
}
@Override
public void playSpellSoundLoop(EntityLivingBase entity, Spell spell, SoundEvent start, SoundEvent loop, SoundEvent end, SoundCategory category, float volume, float pitch){
SoundLoop.addLoop(new SoundLoopSpell.SoundLoopSpellEntity(start, loop, end, spell, entity, volume, pitch));
@@ -189,32 +273,20 @@ public class ClientProxy extends CommonProxy {
}
}
@Override
public void playBlinkEffect(EntityPlayer player){
if(Minecraft.getMinecraft().player == player) WizardryClientEventHandler.playBlinkEffect();
}
@Override
public void shakeScreen(EntityPlayer player, float intensity){
if(Minecraft.getMinecraft().player == player) WizardryClientEventHandler.shakeScreen(intensity);
}
@Override
public Set<String> getSpellHUDSkins(){
return GuiSpellDisplay.getSkinKeys();
}
// SECTION Items
// Items
// ===============================================================================================================
@Override
public boolean shouldDisplayDiscovered(Spell spell, ItemStack stack){
public boolean shouldDisplayDiscovered(Spell spell, @Nullable ItemStack stack){
EntityPlayerSP player = Minecraft.getMinecraft().player;
if(player == null) return false;
// Displayed recipe
// Weirdly, the gui is actually the only way of accessing the current IMerchant or their recipes, other than a
// brute-force search through all the entities in the world to find the merchant interacting with the player
// Since we only need this client-side anyway, we might as well go via the gui
if(Minecraft.getMinecraft().currentScreen instanceof GuiMerchant){
// It doesn't actually matter if the recipe is selected or not, since the itemstack will only ever
// match one of them anyway - and we'd have to reflect into GuiMerchant to get the selected recipe
@@ -291,12 +363,16 @@ public class ClientProxy extends CommonProxy {
}
@Override
public void addMultiLineDescription(List<String> tooltip, String key, Style style, Object... args){
String description = style.getFormattingCode() + I18n.format(key, args);
tooltip.addAll(Minecraft.getMinecraft().fontRenderer.listFormattedStringToWidth(description, TOOLTIP_WRAP_WIDTH));
public String translate(String key, Style style, Object... args){
return style.getFormattingCode() + I18n.format(key, args);
}
// SECTION Particles
@Override
public void addMultiLineDescription(List<String> tooltip, String key, Style style, Object... args){
tooltip.addAll(Minecraft.getMinecraft().fontRenderer.listFormattedStringToWidth(translate(key, style, args), TOOLTIP_WRAP_WIDTH));
}
// Particles
// ===============================================================================================================
/** Use {@link ParticleWizardry#registerParticle(ResourceLocation, IWizardryParticleFactory)}, this is internal. */
@@ -309,10 +385,13 @@ public class ClientProxy extends CommonProxy {
public void registerParticles(){
// I'll be a good programmer and use the API method rather than the one above. Lead by example, as they say...
ParticleWizardry.registerParticle(Type.BEAM, ParticleBeam::new);
ParticleWizardry.registerParticle(Type.BLOCK_HIGHLIGHT, ParticleBlockHighlight::new);
ParticleWizardry.registerParticle(Type.BUFF, ParticleBuff::new);
ParticleWizardry.registerParticle(Type.CLOUD, ParticleCloud::new);
ParticleWizardry.registerParticle(Type.DARK_MAGIC, ParticleDarkMagic::new);
ParticleWizardry.registerParticle(Type.DUST, ParticleDust::new);
ParticleWizardry.registerParticle(Type.FLASH, ParticleFlash::new);
ParticleWizardry.registerParticle(Type.GUARDIAN_BEAM, ParticleGuardianBeam::new);
ParticleWizardry.registerParticle(Type.ICE, ParticleIce::new);
ParticleWizardry.registerParticle(Type.LEAF, ParticleLeaf::new);
ParticleWizardry.registerParticle(Type.LIGHTNING, ParticleLightning::new);
@@ -325,10 +404,9 @@ public class ClientProxy extends CommonProxy {
ParticleWizardry.registerParticle(Type.SPARK, ParticleSpark::new);
ParticleWizardry.registerParticle(Type.SPARKLE, ParticleSparkle::new);
ParticleWizardry.registerParticle(Type.SPHERE, ParticleSphere::new);
ParticleWizardry.registerParticle(Type.SUMMON, ParticleSummon::new);
ParticleWizardry.registerParticle(Type.VINE, ParticleVine::new);
}
@Override
public ParticleWizardry createParticle(ResourceLocation type, World world, double x, double y, double z){
IWizardryParticleFactory factory = factories.get(type);
@@ -345,7 +423,7 @@ public class ClientProxy extends CommonProxy {
Minecraft.getMinecraft().effectRenderer.addEffect(new ParticleTornado(world, maxAge, x, z, radius, y, velX, velZ, block).setBlockPos(pos));// , world.rand.nextInt(6)));
}
// SECTION Packet Handlers
// Packet Handlers
// ===============================================================================================================
@Override
@@ -448,72 +526,74 @@ public class ClientProxy extends CommonProxy {
Wizardry.logger.warn("Recieved a PacketNPCCastSpell, but the caster ID was not the ID of an EntityLiving");
}
}
@Override
public void handleDispenserCastSpellPacket(PacketDispenserCastSpell.Message message){
World world = Minecraft.getMinecraft().world;
if(world.getTileEntity(message.pos) instanceof TileEntityDispenser){ // Should always be true
Spell spell = Spell.byNetworkID(message.spellID);
spell.cast(world, message.x, message.y, message.z, message.direction, 0, -1, message.modifiers);
// 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(Source.DISPENSER, spell, world, message.x, message.y,
message.z, message.direction, message.modifiers));
if(spell.isContinuous || spell instanceof None){
DispenserCastingData data = DispenserCastingData.get((TileEntityDispenser)world.getTileEntity(message.pos));
if(spell.isContinuous){
data.startCasting(spell, message.x, message.y, message.z, message.duration, message.modifiers);
}else{
data.stopCasting();
}
}
}else{
Wizardry.logger.warn("Recieved a PacketDispenserCastSpell, but no tileEntity was found at the supplied location.");
}
}
@Override
public void handleTransportationPacket(PacketTransportation.Message message){
World world = Minecraft.getMinecraft().world;
Entity caster = world.getEntityByID(message.casterID);
BlockPos pos = message.destination;
if(caster == null) return; // Shouldn't happen
Entity entity = world.getEntityByID(message.dismountEntityID);
if(message.dismountEntityID != -1 && entity != null) entity.dismountRidingEntity();
// Moved from when the packet is sent to when it is received; fixes the sound not playing in first person.
caster.playSound(WizardrySounds.SPELL_TRANSPORTATION_TRAVEL, 1, 1);
// Changed to a position to avoid syncing issues
world.playSound(pos.getX(), pos.getY(), pos.getZ(), WizardrySounds.SPELL_TRANSPORTATION_TRAVEL, WizardrySounds.SPELLS, 1, 1, false);
for(int i = 0; i < 20; i++){
double radius = 1;
float angle = world.rand.nextFloat() * (float)Math.PI * 2;
double x = caster.posX + radius * MathHelper.cos(angle);
double y = caster.getEntityBoundingBox().minY + world.rand.nextDouble() * 2;
double z = caster.posZ + radius * MathHelper.sin(angle);
double x = pos.getX() + 0.5 + radius * MathHelper.cos(angle);
double y = pos.getY() + world.rand.nextDouble() * 2;
double z = pos.getZ() + 0.5 + radius * MathHelper.sin(angle);
ParticleBuilder.create(Type.SPARKLE).pos(x, y, z).vel(0, 0.02, 0).clr(0.6f, 1, 0.6f)
.time(80 + world.rand.nextInt(10)).spawn(world);
}
for(int i = 0; i < 20; i++){
double radius = 1;
float angle = world.rand.nextFloat() * (float)Math.PI * 2;
double x = caster.posX + radius * MathHelper.cos(angle);
double y = caster.getEntityBoundingBox().minY + world.rand.nextDouble() * 2;
double z = caster.posZ + radius * MathHelper.sin(angle);
double x = pos.getX() + 0.5 + radius * MathHelper.cos(angle);
double y = pos.getY() + world.rand.nextDouble() * 2;
double z = pos.getZ() + 0.5 + radius * MathHelper.sin(angle);
world.spawnParticle(EnumParticleTypes.VILLAGER_HAPPY, x, y, z, 0, 0.02, 0);
}
for(int i = 0; i < 20; i++){
double radius = 1;
float angle = world.rand.nextFloat() * (float)Math.PI * 2;
double x = caster.posX + radius * MathHelper.cos(angle);
double y = caster.getEntityBoundingBox().minY + world.rand.nextDouble() * 2;
double z = caster.posZ + radius * MathHelper.sin(angle);
double x = pos.getX() + 0.5 + radius * MathHelper.cos(angle);
double y = pos.getY() + world.rand.nextDouble() * 2;
double z = pos.getZ() + 0.5 + radius * MathHelper.sin(angle);
world.spawnParticle(EnumParticleTypes.ENCHANTMENT_TABLE, x, y, z, 0, 0.02, 0);
}
}
@@ -553,6 +633,12 @@ public class ClientProxy extends CommonProxy {
data.randomDescriptions = new HashMap<>();
for(Spell spell : Spell.getAllSpells()){
if(spell.networkID() > message.names.size()){
Wizardry.logger.warn("Received no glyph data for spell {}, skipping", spell.getRegistryName());
continue;
}
// -1 because the none spell isn't included
// This is a case where we must use the network ID, not the metadata
data.randomNames.put(spell, message.names.get(spell.networkID() - 1));
@@ -626,7 +712,7 @@ public class ClientProxy extends CommonProxy {
}else Wizardry.logger.warn("Received a PacketConquerShrine, but there was no shrine core at the position sent");
}
// SECTION Rendering
// Rendering
// ===============================================================================================================
private static final ResourceLocation ICE_WRAITH_TEXTURE = new ResourceLocation(Wizardry.MODID, "textures/entity/ice_wraith.png");
@@ -637,8 +723,17 @@ public class ClientProxy extends CommonProxy {
@Override
public void initialiseLayers(){
LayerStone.initialiseLayers();
LayerFrost.initialiseLayers();
LayerTiledOverlay.initialiseLayers(LayerStone::new);
LayerTiledOverlay.initialiseLayers(LayerFrost::new);
LayerTiledOverlay.initialiseLayers(LayerMindControl::new);
LayerTiledOverlay.initialiseLayers(LayerSummonAnimation::new);
LayerTiledOverlay.initialiseLayers(LayerDisintegrateAnimation::new);
}
@Override
public void initialiseAnimations(){
PlayerAnimator.init();
ActionAnimation.register();
}
@Override
@@ -682,7 +777,9 @@ public class ClientProxy extends CommonProxy {
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));
new ResourceLocation(Wizardry.MODID, "textures/entity/ice_lance.png"), false, 16.0, 3.0, 22, 5, false));
RenderingRegistry.registerEntityRenderingHandler(EntityFlamecatcherArrow.class, manager -> new RenderMagicArrow(manager,
new ResourceLocation(Wizardry.MODID, "textures/entity/flamecatcher_arrow.png"), false, 8, 2.0, 16, 5, true));
RenderingRegistry.registerEntityRenderingHandler(EntityForceArrow.class, RenderForceArrow::new);
@@ -692,6 +789,7 @@ public class ClientProxy extends CommonProxy {
RenderingRegistry.registerEntityRenderingHandler(EntityWizard.class, RenderWizard::new);
RenderingRegistry.registerEntityRenderingHandler(EntityEvilWizard.class, RenderEvilWizard::new);
RenderingRegistry.registerEntityRenderingHandler(EntityDecoy.class, RenderDecoy::new);
RenderingRegistry.registerEntityRenderingHandler(EntityRemnant.class, RenderRemnant::new);
// Throwables
RenderingRegistry.registerEntityRenderingHandler(EntitySparkBomb.class,
@@ -730,6 +828,11 @@ public class ClientProxy extends CommonProxy {
RenderingRegistry.registerEntityRenderingHandler(EntityHammer.class, RenderHammer::new);
RenderingRegistry.registerEntityRenderingHandler(EntityIceSpike.class, RenderIceSpike::new);
RenderingRegistry.registerEntityRenderingHandler(EntityForcefield.class, RenderForcefield::new);
RenderingRegistry.registerEntityRenderingHandler(EntityZombieSpawner.class, RenderZombieSpawner::new);
RenderingRegistry.registerEntityRenderingHandler(EntityRadiantTotem.class, RenderRadiantTotem::new);
RenderingRegistry.registerEntityRenderingHandler(EntityBoulder.class, RenderBoulder::new);
RenderingRegistry.registerEntityRenderingHandler(EntityWitheringTotem.class, RenderWitheringTotem::new);
RenderingRegistry.registerEntityRenderingHandler(EntityIceBarrier.class, RenderIceBarrier::new);
//RenderingRegistry.registerEntityRenderingHandler(EntityContainmentField.class, RenderContainmentField::new);
// Stuff that doesn't render
@@ -741,26 +844,29 @@ public class ClientProxy extends CommonProxy {
RenderingRegistry.registerEntityRenderingHandler(EntityStormElemental.class, RenderBlank::new);
RenderingRegistry.registerEntityRenderingHandler(EntityEarthquake.class, RenderBlank::new);
RenderingRegistry.registerEntityRenderingHandler(EntityHailstorm.class, RenderBlank::new);
RenderingRegistry.registerEntityRenderingHandler(EntityStormcloud.class, RenderBlank::new);
// Runes on ground
RenderingRegistry.registerEntityRenderingHandler(EntityHealAura.class,
manager -> new RenderSigil(manager, new ResourceLocation(Wizardry.MODID, "textures/entity/healing_aura.png"), 5.0f, false));
manager -> new RenderSigil(manager, new ResourceLocation(Wizardry.MODID, "textures/entity/healing_aura.png"), 0.3f, false));
RenderingRegistry.registerEntityRenderingHandler(EntityFireSigil.class,
manager -> new RenderSigil(manager, new ResourceLocation(Wizardry.MODID, "textures/entity/fire_sigil.png"), 2.0f, true));
manager -> new RenderSigil(manager, new ResourceLocation(Wizardry.MODID, "textures/entity/fire_sigil.png"), 0, true));
RenderingRegistry.registerEntityRenderingHandler(EntityFrostSigil.class,
manager -> new RenderSigil(manager, new ResourceLocation(Wizardry.MODID, "textures/entity/frost_sigil.png"), 2.0f, true));
manager -> new RenderSigil(manager, new ResourceLocation(Wizardry.MODID, "textures/entity/frost_sigil.png"), 0, true));
RenderingRegistry.registerEntityRenderingHandler(EntityLightningSigil.class,
manager -> new RenderSigil(manager, new ResourceLocation(Wizardry.MODID, "textures/entity/lightning_sigil.png"), 2.0f, true));
manager -> new RenderSigil(manager, new ResourceLocation(Wizardry.MODID, "textures/entity/lightning_sigil.png"), 0, true));
RenderingRegistry.registerEntityRenderingHandler(EntityFireRing.class,
manager -> new RenderFireRing(manager, new ResourceLocation(Wizardry.MODID, "textures/entity/ring_of_fire.png"), 5.0f));
manager -> new RenderFireRing(manager, new ResourceLocation(Wizardry.MODID, "textures/entity/ring_of_fire.png")));
RenderingRegistry.registerEntityRenderingHandler(EntityDecay.class, RenderDecay::new);
RenderingRegistry.registerEntityRenderingHandler(EntityCombustionRune.class,
manager -> new RenderSigil(manager, new ResourceLocation(Wizardry.MODID, "textures/entity/combustion_rune.png"), 2.0f, true));
manager -> new RenderSigil(manager, new ResourceLocation(Wizardry.MODID, "textures/entity/combustion_rune.png"), 0, true));
// TESRs
ClientRegistry.bindTileEntitySpecialRenderer(TileEntityArcaneWorkbench.class, new RenderArcaneWorkbench());
ClientRegistry.bindTileEntitySpecialRenderer(TileEntityStatue.class, renderStatue = new RenderStatue());
ClientRegistry.bindTileEntitySpecialRenderer(TileEntityMagicLight.class, new RenderMagicLight());
ClientRegistry.bindTileEntitySpecialRenderer(TileEntityLectern.class, new RenderLectern());
ClientRegistry.bindTileEntitySpecialRenderer(TileEntityImbuementAltar.class, new RenderImbuementAltar());
}
}
@@ -9,9 +9,10 @@ import net.minecraft.item.ItemStack;
import net.minecraft.util.math.MathHelper;
import org.lwjgl.opengl.GL11;
import java.util.Random;
/**
* Utility class containing some useful static methods for drawing GUIs. Previously these were spread across the main
* {@code WizardryUtilities} class and various individual GUI classes.
* Utility class containing some useful static methods for drawing GUIs, as well as general rendering.
*
* @author Electroblob
* @since Wizardry 4.2
@@ -124,6 +125,29 @@ public final class DrawingUtils {
tessellator.draw();
}
/**
* Draws a 'glitch' rectangle, with some rows of pixels shifted randomly to give a broken effect.
*
* @param random A random number generator to use
* @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.
* @param flipX Whether to flip the texture in the x direction.
* @param flipY Whether to flip the texture in the y direction.
*/
public static void drawGlitchRect(Random random, int x, int y, int u, int v, int width, int height, int textureWidth, int textureHeight, boolean flipX, boolean flipY){
for(int i=0; i<height; i++){
if(flipY) i = height - i - 1;
int offset = random.nextInt(4) == 0 ? random.nextInt(6) - 3 : 0;
drawTexturedFlippedRect(x + offset, y + i, u, v + i, width, 1, textureWidth, textureHeight, flipX, flipY);
}
}
/**
* Mixes the two given opaque colours in the proportion specified.
* @param colour1 The first colour to mix, as a 6-digit hexadecimal.
@@ -156,7 +180,7 @@ public final class DrawingUtils {
* @return The resulting integer colour code, which will be an 8-digit hexadecimal.
*/
public static int makeTranslucent(int colour, float opacity){
return colour + ((int)(0xff * opacity * 0x01000000));
return colour + ((int)(opacity * 0xff) << 24);
}
/**
@@ -244,4 +268,37 @@ public final class DrawingUtils {
RenderHelper.enableStandardItemLighting();
}
/**
* Calculates a factor between 0 and 1 that results in a smooth, aesthetically-pleasing animation when used to scale
* things. Mainly for rendering, but can be used anywhere since it's just a mathematical formula.
* @param lifetime The lifetime of the thing being animated, in ticks (if this is negative, disappearing is ignored)
* @param ticksExisted The current age of the thing being animated, in ticks
* @param partialTicks The current partial tick time
* @param startLength The length of the appearing animation, in ticks
* @param endLength The length of the disappearing animation, in ticks
* @return A fraction between 0 and 1, with the value at the very start and end being 0 and the constant middle
* section having a value of 1.
*/
public static float smoothScaleFactor(int lifetime, int ticksExisted, float partialTicks, int startLength, int endLength){
float age = ticksExisted + partialTicks;
float s = MathHelper.clamp(age < startLength || lifetime < 0 ? age/startLength : (lifetime - age) / endLength, 0, 1);
s = (float)Math.pow(s, 0.4); // Smooths the animation
return s;
}
/**
* Tests if the point with the given coordinates lies within the rectangle specified. The check is identical to
* {@link GuiContainer#isPointInRegion(int, int, int, int, int, int)}, but without the adjustment relative to the GUI.
* @param left The minimum x coordinate of the rectangle
* @param top The minimum y coordinate of the rectangle
* @param width The width of the rectangle
* @param height The height of the rectangle
* @param x The x coordinate of the point to test
* @param y The y coordinate of the point to test
* @return True if the given point is in the rectangle, false otherwise
*/
public static boolean isPointInRegion(int left, int top, int width, int height, int x, int y){
return x >= left - 1 && x < left + width + 1 && y >= top - 1 && y < top + height + 1;
}
}
@@ -0,0 +1,42 @@
package electroblob.wizardry.client;
import electroblob.wizardry.Wizardry;
import net.minecraft.client.Minecraft;
import net.minecraftforge.fml.common.Mod.EventBusSubscriber;
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
import net.minecraftforge.fml.common.gameevent.TickEvent;
import net.minecraftforge.fml.relauncher.Side;
@EventBusSubscriber(Side.CLIENT)
public class ScreenShakeHandler {
/** The remaining time for which the screen shake effect will be active. */
private static int screenShakeCounter = 0;
private static final float SHAKINESS = 0.5f;
/** Starts the client-side screen shake effect. */
public static void shakeScreen(float intensity){
if(Wizardry.settings.screenShake){
screenShakeCounter = (int)(intensity / SHAKINESS);
Minecraft.getMinecraft().player.rotationPitch -= intensity * 0.5f; // Start halfway down
}
}
@SubscribeEvent
public static void onPlayerTickEvent(TickEvent.PlayerTickEvent event){
if(event.player == Minecraft.getMinecraft().player && event.phase == TickEvent.Phase.END){
if(Wizardry.settings.screenShake){
if(screenShakeCounter > 0){
float magnitude = screenShakeCounter * SHAKINESS;
Minecraft.getMinecraft().player.rotationPitch += screenShakeCounter % 2 == 0 ? magnitude : -magnitude;
screenShakeCounter--;
}
}else{
screenShakeCounter = 0;
}
}
}
}
@@ -1,41 +1,35 @@
package electroblob.wizardry.client;
import electroblob.wizardry.Wizardry;
import electroblob.wizardry.block.BlockMagicLight;
import electroblob.wizardry.constants.Constants;
import electroblob.wizardry.client.renderer.overlay.RenderBlinkEffect;
import electroblob.wizardry.data.DispenserCastingData;
import electroblob.wizardry.data.SpellEmitterData;
import electroblob.wizardry.data.WizardData;
import electroblob.wizardry.item.ISpellCastingItem;
import electroblob.wizardry.item.ItemArtefact;
import electroblob.wizardry.item.ItemFlamecatcher;
import electroblob.wizardry.item.ItemSpectralBow;
import electroblob.wizardry.item.ItemWand;
import electroblob.wizardry.potion.PotionSlowTime;
import electroblob.wizardry.registry.Spells;
import electroblob.wizardry.registry.WizardryItems;
import electroblob.wizardry.registry.WizardryPotions;
import electroblob.wizardry.spell.*;
import electroblob.wizardry.util.RayTracer;
import electroblob.wizardry.util.WizardryUtilities;
import electroblob.wizardry.spell.Possession;
import electroblob.wizardry.spell.SixthSense;
import electroblob.wizardry.spell.SlowTime;
import electroblob.wizardry.spell.Transience;
import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.GuiMerchant;
import net.minecraft.client.gui.ScaledResolution;
import net.minecraft.client.renderer.BufferBuilder;
import net.minecraft.client.renderer.GlStateManager;
import net.minecraft.client.renderer.OpenGlHelper;
import net.minecraft.client.renderer.Tessellator;
import net.minecraft.client.renderer.entity.RenderManager;
import net.minecraft.client.renderer.vertex.DefaultVertexFormats;
import net.minecraft.entity.EntityLiving;
import net.minecraft.entity.EntityLivingBase;
import net.minecraft.entity.item.EntityArmorStand;
import net.minecraft.inventory.Slot;
import net.minecraft.item.ItemStack;
import net.minecraft.item.Item;
import net.minecraft.tileentity.TileEntity;
import net.minecraft.tileentity.TileEntityDispenser;
import net.minecraft.util.ResourceLocation;
import net.minecraft.util.math.RayTraceResult;
import net.minecraft.village.MerchantRecipe;
import net.minecraft.world.World;
import net.minecraftforge.client.event.*;
import net.minecraftforge.client.event.FOVUpdateEvent;
import net.minecraftforge.client.event.InputUpdateEvent;
import net.minecraftforge.client.event.MouseEvent;
import net.minecraftforge.client.event.RenderHandEvent;
import net.minecraftforge.fml.common.Mod;
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
import net.minecraftforge.fml.common.gameevent.TickEvent;
@@ -46,8 +40,9 @@ import java.util.ArrayList;
import java.util.List;
/**
* Event handler responsible for client-side only events, mostly rendering.
*
* General-purpose client-side event handler for things that don't fit anywhere else or groups of related behaviours
* that are better kept together.
*
* @author Electroblob
* @since Wizardry 1.0
*/
@@ -55,57 +50,19 @@ import java.util.List;
@Mod.EventBusSubscriber(Side.CLIENT)
public final class WizardryClientEventHandler {
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 blinkOverlayTexture = new ResourceLocation(Wizardry.MODID, "textures/gui/blink_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 WizardryClientEventHandler(){} // No instances!
/** The remaining time for which the blink screen overlay effect will be displayed in first-person. Since this is
* only for the first-person player (the instance of which is itself stored in a static variable), this can simply
* be stored statically here, rather than needing to be in {@code WizardData}. */
private static int blinkEffectTimer;
/** The number of ticks the blink effect lasts for. */
private static final int BLINK_EFFECT_DURATION = 8;
// Tick Events
// ===============================================================================================================
/** The remaining time for which the screen shake effect will be active. */
private static int screenShakeCounter = 0;
private static final float SHAKINESS = 0.5f;
/** Starts the first-person blink overlay effect. */
public static void playBlinkEffect(){
blinkEffectTimer = BLINK_EFFECT_DURATION;
}
/** Starts the client-side screen shake effect. */
public static void shakeScreen(float intensity){
screenShakeCounter = (int)(intensity / SHAKINESS);
Minecraft.getMinecraft().player.rotationPitch -= intensity * 0.5f; // Start halfway down
}
@SubscribeEvent
public static void onPlayerTickEvent(TickEvent.PlayerTickEvent event){
if(event.player == Minecraft.getMinecraft().player && event.phase == TickEvent.Phase.END){
if(blinkEffectTimer > 0) blinkEffectTimer--;
if(screenShakeCounter > 0){
float magnitude = screenShakeCounter * SHAKINESS;
Minecraft.getMinecraft().player.rotationPitch += screenShakeCounter % 2 == 0 ? magnitude : -magnitude;
screenShakeCounter--;
}
// Only seems to work here...
// EntityLiving victim = Possession.getPossessee(Minecraft.getMinecraft().player);
// if(victim != null && victim.getHeldItemMainhand().isEmpty()){
// Minecraft.getMinecraft().player.setHeldItem(EnumHand.MAIN_HAND, ItemStack.EMPTY);
// }
// Reset shaders if their respective potions aren't active
// This is a player so the potion effects are synced by vanilla
if(Minecraft.getMinecraft().entityRenderer.getShaderGroup() != null){
if(Minecraft.getMinecraft().entityRenderer.getShaderGroup() != null){ // IntelliJ is wrong, this can be null
String activeShader = Minecraft.getMinecraft().entityRenderer.getShaderGroup().getShaderGroupName();
@@ -114,41 +71,16 @@ public final class WizardryClientEventHandler {
|| (activeShader.equals(Transience.SHADER.toString()) && !Minecraft.getMinecraft().player.isPotionActive(WizardryPotions.transience))){
if(activeShader.equals(SixthSense.SHADER.toString())
|| activeShader.equals(Transience.SHADER.toString())) playBlinkEffect();
|| activeShader.equals(Transience.SHADER.toString())) RenderBlinkEffect.playBlinkEffect();
Minecraft.getMinecraft().entityRenderer.stopUseShader();
}
}
}
}
@SubscribeEvent
public static void onRenderHandEvent(RenderHandEvent event){
EntityLiving victim = Possession.getPossessee(Minecraft.getMinecraft().player);
if(victim != null){
victim.rotationYawHead = Minecraft.getMinecraft().player.rotationYaw;
if(Minecraft.getMinecraft().player.getHeldItemMainhand().isEmpty()){
event.setCanceled(true);
}
}
}
// This event is called every tick, not just when a movement key is pressed
@SubscribeEvent
public static void onInputUpdateEvent(InputUpdateEvent event){
// Prevents the player moving when paralysed
if(event.getEntityPlayer().isPotionActive(WizardryPotions.paralysis)){
event.getMovementInput().moveForward = 0;
event.getMovementInput().moveStrafe = 0;
event.getMovementInput().jump = false;
event.getMovementInput().sneak = false;
}
}
// The classes referenced here used to handle their own client tick events, but since they're not client-only
// classes, that crashed the dedicated server...
@SubscribeEvent
public static void onClientTickEvent(TickEvent.ClientTickEvent event){
@@ -173,10 +105,13 @@ public final class WizardryClientEventHandler {
PotionSlowTime.cleanUpEntities(world);
}
}
// Input and Controls
// ===============================================================================================================
@SubscribeEvent
public static void onMouseEvent(MouseEvent event){
// Prevents the player looking around when paralysed
if(Minecraft.getMinecraft().player.isPotionActive(WizardryPotions.paralysis)
&& Minecraft.getMinecraft().inGameHasFocus){
@@ -188,267 +123,76 @@ public final class WizardryClientEventHandler {
}
}
// This event is called every tick, not just when a movement key is pressed
@SubscribeEvent
public static void onFOVUpdateEvent(FOVUpdateEvent event){
// Bow zoom. Taken directly from AbstractClientPlayer so it works exactly like vanilla.
if(event.getEntity().isHandActive() && event.getEntity().getActiveItemStack().getItem() instanceof ItemSpectralBow){
int maxUseTicks = event.getEntity().getItemInUseMaxCount();
float maxUseSeconds = (float)maxUseTicks / 20.0F;
if(maxUseSeconds > 1.0F){
maxUseSeconds = 1.0F;
}else{
maxUseSeconds = maxUseSeconds * maxUseSeconds;
}
event.setNewfov(event.getFov() * 1.0F - maxUseSeconds * 0.15F);
public static void onInputUpdateEvent(InputUpdateEvent event){
// Prevents the player moving when paralysed
if(event.getEntityPlayer().isPotionActive(WizardryPotions.paralysis)){
event.getMovementInput().moveForward = 0;
event.getMovementInput().moveStrafe = 0;
event.getMovementInput().jump = false;
event.getMovementInput().sneak = false;
}
if(blinkEffectTimer > 0){
float f = ((float)Math.max(blinkEffectTimer - 2, 0))/BLINK_EFFECT_DURATION;
event.setNewfov(event.getFov() + f * f * 0.7f);
if(ItemArtefact.isArtefactActive(event.getEntityPlayer(), WizardryItems.charm_move_speed)
&& event.getEntityPlayer().isHandActive()
&& event.getEntityPlayer().getActiveItemStack().getItem() instanceof ItemWand){
// Normally speed is set to 20% when using items, this makes it 80%
event.getMovementInput().moveStrafe *= 4;
event.getMovementInput().moveForward *= 4;
}
}
// Rendering
// ===============================================================================================================
@SubscribeEvent
public static void onDrawBlockHighlightEvent(DrawBlockHighlightEvent event){
// Hide the block outline for magic light blocks unless the player can dispel them
if(event.getTarget().typeOfHit == RayTraceResult.Type.BLOCK
&& event.getPlayer().world.getBlockState(event.getTarget().getBlockPos()).getBlock() instanceof BlockMagicLight){
public static void onRenderHandEvent(RenderHandEvent event){
if((!(event.getPlayer().getHeldItemMainhand().getItem() instanceof ISpellCastingItem)
&& !(event.getPlayer().getHeldItemOffhand().getItem() instanceof ISpellCastingItem))
|| !ItemArtefact.isArtefactActive(event.getPlayer(), WizardryItems.charm_light)){
// Hide the player's empty hand in first-person when possessing
EntityLiving victim = Possession.getPossessee(Minecraft.getMinecraft().player);
if(victim != null){
victim.rotationYawHead = Minecraft.getMinecraft().player.rotationYaw;
if(Minecraft.getMinecraft().player.getHeldItemMainhand().isEmpty()){
event.setCanceled(true);
}
}
}
// Brute-force fix for crystals not showing up when a wizard is given a spell book in the trade GUI.
@SubscribeEvent
public static void onGuiDrawForegroundEvent(GuiContainerEvent.DrawForeground event){
public static void onFOVUpdateEvent(FOVUpdateEvent event){
if(event.getGuiContainer() instanceof GuiMerchant){
GuiMerchant gui = (GuiMerchant)event.getGuiContainer();
// Note that gui.getMerchant() returns an NpcMerchant, not an EntityWizard.
// Using == the specific item rather than instanceof because that's how trades do it.
if(gui.inventorySlots.getSlot(0).getStack().getItem() == WizardryItems.spell_book
|| gui.inventorySlots.getSlot(1).getStack().getItem() == WizardryItems.spell_book){
for(MerchantRecipe trade : gui.getMerchant().getRecipes(Minecraft.getMinecraft().player)){
if(trade.getItemToBuy().getItem() == WizardryItems.spell_book && trade.getSecondItemToBuy().isEmpty()){
Slot slot = gui.inventorySlots.getSlot(2);
// It still doesn't look quite right because the slot highlight is behind the item, but it'll do
// until/unless I find a better solution.
DrawingUtils.drawItemAndTooltip(gui, trade.getItemToSell(), slot.xPos, slot.yPos, event.getMouseX(), event.getMouseY(),
gui.getSlotUnderMouse() == slot);
}
// Bow zoom. Taken directly from AbstractClientPlayer so it works exactly like vanilla.
if(event.getEntity().isHandActive()){
Item item = event.getEntity().getActiveItemStack().getItem();
if(item instanceof ItemSpectralBow || item instanceof ItemFlamecatcher){
int maxUseTicks = event.getEntity().getItemInUseMaxCount();
float maxUseSeconds = (float)maxUseTicks / (item instanceof ItemFlamecatcher ? ItemFlamecatcher.DRAW_TIME : 20);
if(maxUseSeconds > 1.0F){
maxUseSeconds = 1.0F;
}else{
maxUseSeconds = maxUseSeconds * maxUseSeconds;
}
event.setNewfov(event.getFov() * 1.0F - maxUseSeconds * 0.15F);
}
}
}
@SubscribeEvent
public static void onRenderLivingEvent(RenderLivingEvent.Post<EntityLivingBase> event){
Minecraft mc = Minecraft.getMinecraft();
WizardData data = WizardData.get(mc.player);
RenderManager renderManager = event.getRenderer().getRenderManager();
ItemStack wand = mc.player.getHeldItemMainhand();
if(!(wand.getItem() instanceof ISpellCastingItem)){
wand = mc.player.getHeldItemOffhand();
}
// Target selection pointer
if(mc.player.isSneaking() && wand.getItem() instanceof ISpellCastingItem && WizardryUtilities.isLiving(event.getEntity())
&& data != null && data.selectedMinion != null){
// -> Moved this in here so it isn't called every tick
RayTraceResult rayTrace = RayTracer.standardEntityRayTrace(mc.world, mc.player, 16, false);
if(rayTrace != null && rayTrace.entityHit == event.getEntity()){
Tessellator tessellator = Tessellator.getInstance();
BufferBuilder buffer = tessellator.getBuffer();
GlStateManager.pushMatrix();
GlStateManager.disableCull();
GlStateManager.disableLighting();
OpenGlHelper.setLightmapTextureCoords(OpenGlHelper.lightmapTexUnit, 240f, 240f);
// Disabling depth test allows it to be seen through everything.
GlStateManager.disableDepth();
GlStateManager.color(1, 1, 1, 1);
GlStateManager.translate(event.getX(), event.getY() + event.getEntity().height + 0.5, event.getZ());
// 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);
buffer.begin(GL11.GL_QUADS, DefaultVertexFormats.POSITION_TEX);
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.draw();
GlStateManager.enableCull();
GlStateManager.enableLighting();
GlStateManager.enableDepth();
GlStateManager.popMatrix();
}
}
// Summoned creature selection pointer
if(data != null && data.selectedMinion != null && data.selectedMinion.get() == event.getEntity()){
Tessellator tessellator = Tessellator.getInstance();
BufferBuilder buffer = tessellator.getBuffer();
GlStateManager.pushMatrix();
GlStateManager.disableCull();
GlStateManager.disableLighting();
OpenGlHelper.setLightmapTextureCoords(OpenGlHelper.lightmapTexUnit, 240f, 240f);
// Disabling depth test allows it to be seen through everything.
GlStateManager.disableDepth();
GlStateManager.color(1, 1, 1, 1);
GlStateManager.translate(event.getX(), event.getY() + event.getEntity().height + 0.5, event.getZ());
// 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);
buffer.begin(GL11.GL_QUADS, DefaultVertexFormats.POSITION_TEX);
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.draw();
GlStateManager.enableCull();
GlStateManager.enableLighting();
GlStateManager.enableDepth();
GlStateManager.popMatrix();
}
// Sixth sense
if(mc.player.isPotionActive(WizardryPotions.sixth_sense) && !(event.getEntity() instanceof EntityArmorStand)
&& event.getEntity() != mc.player && mc.player.getActivePotionEffect(WizardryPotions.sixth_sense) != null
&& event.getEntity().getDistance(mc.player) < Spells.sixth_sense.getProperty(Spell.EFFECT_RADIUS).floatValue()
* (1 + mc.player.getActivePotionEffect(WizardryPotions.sixth_sense).getAmplifier() * Constants.RANGE_INCREASE_PER_LEVEL)){
Tessellator tessellator = Tessellator.getInstance();
BufferBuilder buffer = tessellator.getBuffer();
GlStateManager.pushMatrix();
GlStateManager.disableCull();
GlStateManager.enableBlend();
GlStateManager.disableLighting();
OpenGlHelper.setLightmapTextureCoords(OpenGlHelper.lightmapTexUnit, 240f, 240f);
GlStateManager.blendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA);
// Disabling depth test allows it to be seen through everything.
GlStateManager.disableDepth();
GlStateManager.translate(event.getX(), event.getY() + event.getEntity().height * 0.6, event.getZ());
// 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);
buffer.begin(GL11.GL_QUADS, DefaultVertexFormats.POSITION_TEX);
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.draw();
GlStateManager.enableCull();
GlStateManager.disableBlend();
GlStateManager.enableLighting();
GlStateManager.enableDepth();
GlStateManager.popMatrix();
}
}
@SubscribeEvent
public static void onRenderGameOverlayEvent(RenderGameOverlayEvent.Post event){
if(event.getType() == RenderGameOverlayEvent.ElementType.HELMET){
if(Minecraft.getMinecraft().player.isPotionActive(WizardryPotions.sixth_sense)){
OpenGlHelper.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA, GL11.GL_ONE, GL11.GL_ZERO);
GlStateManager.color(1, 1, 1, 1);
GlStateManager.disableAlpha();
renderScreenOverlay(event, sixthSenseOverlayTexture);
GlStateManager.enableAlpha();
GlStateManager.color(1, 1, 1, 1);
}
if(Minecraft.getMinecraft().player.isPotionActive(WizardryPotions.frost)){
OpenGlHelper.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA, GL11.GL_ONE, GL11.GL_ZERO);
GlStateManager.color(1, 1, 1, 1);
GlStateManager.disableAlpha();
renderScreenOverlay(event, frostOverlayTexture);
GlStateManager.enableAlpha();
GlStateManager.color(1, 1, 1, 1);
}
if(blinkEffectTimer > 0){
float alpha = ((float)blinkEffectTimer)/BLINK_EFFECT_DURATION;
OpenGlHelper.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE, GL11.GL_ONE, GL11.GL_ZERO);
GlStateManager.color(1, 1, 1, alpha);
GlStateManager.disableAlpha();
renderScreenOverlay(event, blinkOverlayTexture);
GlStateManager.enableAlpha();
GlStateManager.color(1, 1, 1, 1);
}
}
}
private static void renderScreenOverlay(RenderGameOverlayEvent.Post event, ResourceLocation texture){
/**
* Renders an overlay across the entire screen.
* @param resolution The screen resolution
* @param texture The overlay texture to display
*/
public static void renderScreenOverlay(ScaledResolution resolution, ResourceLocation texture){
GlStateManager.pushMatrix();
@@ -461,11 +205,12 @@ public final class WizardryClientEventHandler {
BufferBuilder 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();
buffer.pos(0, resolution.getScaledHeight(), -90).tex(0, 1).endVertex();
buffer.pos(resolution.getScaledWidth(), resolution.getScaledHeight(), -90).tex(1, 1).endVertex();
buffer.pos(resolution.getScaledWidth(), 0, -90).tex(1, 0).endVertex();
buffer.pos(0, 0, -90).tex(0, 0).endVertex();
tessellator.draw();
GlStateManager.depthMask(true);
@@ -4,6 +4,7 @@ import electroblob.wizardry.Wizardry;
import electroblob.wizardry.client.gui.GuiSpellDisplay;
import electroblob.wizardry.item.ISpellCastingItem;
import electroblob.wizardry.packet.PacketControlInput;
import electroblob.wizardry.packet.PacketSpellQuickAccess;
import electroblob.wizardry.packet.WizardryPacketHandler;
import electroblob.wizardry.registry.WizardrySounds;
import net.minecraft.client.Minecraft;
@@ -24,6 +25,7 @@ public class WizardryControlHandler {
static boolean NkeyPressed = false;
static boolean BkeyPressed = false;
static boolean[] quickAccessKeyPressed = new boolean[ClientProxy.SPELL_QUICK_ACCESS.length];
// Changed to a tick event to allow mouse button keybinds
// The 'lag' that happened previously was actually because the code only fired when a keyboard key was pressed!
@@ -59,6 +61,19 @@ public class WizardryControlHandler {
}else{
BkeyPressed = false;
}
for(int i = 0; i < ClientProxy.SPELL_QUICK_ACCESS.length; i++){
if(ClientProxy.SPELL_QUICK_ACCESS[i].isKeyDown() && Minecraft.getMinecraft().inGameHasFocus){
if(!quickAccessKeyPressed[i]){
quickAccessKeyPressed[i] = true;
// Packet building
selectSpell(wand, i);
}
}else{
quickAccessKeyPressed[i] = false;
}
}
}
}
}
@@ -118,4 +133,17 @@ public class WizardryControlHandler {
GuiSpellDisplay.playSpellSwitchAnimation(false);
Minecraft.getMinecraft().getSoundHandler().playSound(PositionedSoundRecord.getMasterRecord(WizardrySounds.ITEM_WAND_SWITCH_SPELL, 1));
}
private static void selectSpell(ItemStack wand, int index){
// GUI switch animation
if(((ISpellCastingItem)wand.getItem()).selectSpell(wand, index)){ // Makes sure the spell is set immediately for the client
// Packet building (no point sending it unless the client-side spell selection succeeded
IMessage msg = new PacketSpellQuickAccess.Message(index);
WizardryPacketHandler.net.sendToServer(msg);
GuiSpellDisplay.playSpellSwitchAnimation(true); // This will do, it's only an animation
Minecraft.getMinecraft().getSoundHandler().playSound(PositionedSoundRecord.getMasterRecord(WizardrySounds.ITEM_WAND_SWITCH_SPELL, 1));
}
}
}
@@ -0,0 +1,155 @@
package electroblob.wizardry.client.animation;
import electroblob.wizardry.data.WizardData;
import electroblob.wizardry.item.SpellActions;
import electroblob.wizardry.spell.Grapple;
import electroblob.wizardry.util.GeometryUtils;
import electroblob.wizardry.util.InventoryUtils;
import net.minecraft.client.model.ModelBiped;
import net.minecraft.entity.EntityLivingBase;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.item.EnumAction;
import net.minecraft.util.EnumHandSide;
import net.minecraft.util.math.MathHelper;
import net.minecraft.util.math.RayTraceResult;
import net.minecraft.util.math.Vec3d;
/**
* An animation that is associated with a particular {@link net.minecraft.item.EnumAction}. This animation will display
* when a player is using an item with that action.
* @author Electroblob
* @since Wizardry 4.3
* @see electroblob.wizardry.item.SpellActions
*/
public abstract class ActionAnimation extends Animation {
private final EnumAction action;
public ActionAnimation(EnumAction action){
super(action.name());
this.action = action;
}
@Override
public boolean shouldDisplay(EntityPlayer player, boolean firstPerson){
return !firstPerson && player.getItemInUseMaxCount() > 0 && player.getActiveItemStack().getItemUseAction() == action;
}
/** Called from the client proxy to register all of wizardry's action animations. */
public static void register(){
PlayerAnimator.registerAnimation(new ActionAnimation(SpellActions.POINT){
@Override
public void setRotationAngles(EntityPlayer player, ModelBiped model, float partialTicks, boolean firstPerson){
// Something (probably some sort of race condition) causes getActiveItemStack to sometimes be wrong
if(player.getActiveItemStack() != player.getHeldItem(player.getActiveHand())) return;
EnumHandSide side = InventoryUtils.getSideForHand(player, player.getActiveHand());
float pitch = (float)Math.toRadians(player.prevRotationPitch + (player.rotationPitch - player.prevRotationPitch) * partialTicks);
float yaw = (float)Math.toRadians(player.prevRotationYawHead + (player.rotationYawHead - player.prevRotationYawHead) * partialTicks)
- (float)Math.toRadians(player.prevRenderYawOffset + (player.renderYawOffset - player.prevRenderYawOffset) * partialTicks);
float x = -((float)Math.PI / 2) + pitch + 0.2f;
float y = (side == EnumHandSide.RIGHT ? -0.25f : 0.25f) + yaw;
ModelRendererExtended arm = (ModelRendererExtended)getArmForSide(model, side);
arm.setRotation(x, y, 0);
}
});
PlayerAnimator.registerAnimation(new ActionAnimation(SpellActions.POINT_UP){
@Override
public void setRotationAngles(EntityPlayer player, ModelBiped model, float partialTicks, boolean firstPerson){
if(player.getActiveItemStack() != player.getHeldItem(player.getActiveHand())) return;
EnumHandSide side = InventoryUtils.getSideForHand(player, player.getActiveHand());
ModelRendererExtended arm = (ModelRendererExtended)getArmForSide(model, side);
arm.addRotation(-2.2f, side == EnumHandSide.RIGHT ? 0.2f : -0.2f, 0);
}
});
PlayerAnimator.registerAnimation(new ActionAnimation(SpellActions.POINT_DOWN){
@Override
public void setRotationAngles(EntityPlayer player, ModelBiped model, float partialTicks, boolean firstPerson){
float tick = player.getItemInUseMaxCount() + partialTicks; // It's not the "max" use count at all!
float y = Math.min(0.4f + tick * 0.05f, 0.7f);
EnumHandSide side = InventoryUtils.getSideForHand(player, player.getActiveHand());
ModelRendererExtended arm = (ModelRendererExtended)getArmForSide(model, side);
arm.addRotation(-0.2f, side == EnumHandSide.RIGHT ? y : -y, 0);
}
});
PlayerAnimator.registerAnimation(new ActionAnimation(SpellActions.SUMMON){
@Override
public void setRotationAngles(EntityPlayer player, ModelBiped model, float partialTicks, boolean firstPerson){
float tick = player.getItemInUseMaxCount() + partialTicks; // It's not the "max" use count at all!
float x = -Math.min(0.4f + tick * 0.2f, 2f);
((ModelRendererExtended)getArmForSide(model, EnumHandSide.RIGHT)).addRotation(x, 1.2f, 0);
((ModelRendererExtended)getArmForSide(model, EnumHandSide.LEFT)).addRotation(x, -1.2f, 0);
}
});
PlayerAnimator.registerAnimation(new ActionAnimation(SpellActions.GRAPPLE){
@Override
public void setRotationAngles(EntityPlayer player, ModelBiped model, float partialTicks, boolean firstPerson){
WizardData data = WizardData.get(player);
RayTraceResult hit = data.getVariable(Grapple.TARGET_KEY);
if(hit == null || hit.typeOfHit == RayTraceResult.Type.MISS) return;
Vec3d target = hit.hitVec;
if(hit.entityHit instanceof EntityLivingBase){
// If the target is an entity, we need to use the entity's centre rather than the original hit position
// because the entity will have moved!
target = GeometryUtils.getCentre(hit.entityHit);
}
EnumHandSide side = InventoryUtils.getSideForHand(player, player.getActiveHand());
ModelRendererExtended arm = (ModelRendererExtended)getArmForSide(model, side);
Vec3d direction = target.subtract(player.getPositionEyes(partialTicks));
float yaw = player.prevRenderYawOffset + (player.renderYawOffset - player.prevRenderYawOffset) * partialTicks;
float pitch = (float)MathHelper.atan2(MathHelper.sqrt(direction.x*direction.x + direction.z*direction.z), direction.y);
float x = pitch - (float)Math.PI * 0.9f;
float y = -(float)Math.toRadians(yaw) - (float)MathHelper.atan2(direction.x, direction.z);
y += (side == EnumHandSide.RIGHT ? -0.25f : 0.25f);
if(Math.abs(pitch) < 0.2f) y = arm.rotateAngleY;
arm.setRotation(x, y, 0);
}
});
PlayerAnimator.registerAnimation(new ActionAnimation(SpellActions.IMBUE){
@Override
public void setRotationAngles(EntityPlayer player, ModelBiped model, float partialTicks, boolean firstPerson){
float tick = player.getItemInUseMaxCount() + partialTicks; // It's not the "max" use count at all!
float z = Math.max(1.5f - tick * 0.1f, 0.8f);
EnumHandSide side = InventoryUtils.getSideForHand(player, player.getActiveHand());
ModelRendererExtended arm = (ModelRendererExtended)getArmForSide(model, side);
arm.addRotation(-1.2f, side == EnumHandSide.RIGHT ? -0.2f : 0.2f, side == EnumHandSide.RIGHT ? z : -z);
if(!player.getHeldItem(InventoryUtils.getHandForSide(player, side.opposite())).isEmpty()){
((ModelRendererExtended)getArmForSide(model, side.opposite())).addRotation(-0.8f, side == EnumHandSide.LEFT ? 0.3f : -0.3f, 0);
}
}
});
PlayerAnimator.registerAnimation(new ActionAnimation(SpellActions.THRUST){
@Override
public void setRotationAngles(EntityPlayer player, ModelBiped model, float partialTicks, boolean firstPerson){
if(player.getActiveItemStack() != player.getHeldItem(player.getActiveHand())) return;
EnumHandSide side = InventoryUtils.getSideForHand(player, player.getActiveHand());
ModelRendererExtended arm = (ModelRendererExtended)getArmForSide(model, side);
float y = side == EnumHandSide.RIGHT ? -0.6f : 0.6f;
arm.addRotation(-1.2f, y, 0);
if(player.getHeldItem(InventoryUtils.getHandForSide(player, side.opposite())).isEmpty()){
ModelRendererExtended otherArm = (ModelRendererExtended)getArmForSide(model, side.opposite());
otherArm.setRotation(arm.rotateAngleX - 1.2f, -arm.rotateAngleY - y, otherArm.rotateAngleZ);
}
}
});
}
}
@@ -0,0 +1,77 @@
package electroblob.wizardry.client.animation;
import net.minecraft.client.model.ModelBiped;
import net.minecraft.client.model.ModelRenderer;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.util.EnumHandSide;
/**
* Represents a player animation. Use one of the predefined subclasses below for common animation types, or extend this
* class directly to define a completely custom animation. Register instances of this class using
* {@link PlayerAnimator#registerAnimation(Animation)}.
* @author Electroblob
* @since Wizardry 4.3
* @see ActionAnimation
*/
public abstract class Animation {
private final String name;
public Animation(String name){
this.name = name;
}
/** Returns the name of this animation, currently only used for warning messages / debugging. */
public String getName(){
return name;
}
/**
* Returns whether this animation should be displayed for the given player. <i>Note that if two registered
* animations have overlapping conditions, both will attempt to display simultaneously, so if they both animate the
* same model part, that part may not behave as expected.</i>
* @param player The player being animated
* @param firstPerson True if the player being animated is the local client player, and they are in first-person
* view. In first-person, animations are only useful for animating the player's empty hand.
* @return True if the animation should be displayed, false if not.
*/
public abstract boolean shouldDisplay(EntityPlayer player, boolean firstPerson);
/**
* Sets the rotation of the model parts for this animation. This method is called every time the player is rendered
* when {@link Animation#shouldDisplay(EntityPlayer, boolean)} returns true.
* @param player The player being animated
* @param model The model to animate. All of the standard {@link ModelBiped} parts will already be wrapped and may
* be safely cast to {@code ModelRendererExtended} in order to override the rotations set by the model
* itself - see {@link ModelRendererExtended ModelRendererExtended} for details.
* @param partialTicks The current partial tick time
* @param firstPerson True if the player being animated is the local client player, and they are in first-person
* view. In first-person, animations are only useful for animating the player's empty hand.
*/
public abstract void setRotationAngles(EntityPlayer player, ModelBiped model, float partialTicks, boolean firstPerson);
/**
* Returns whether the boxes that form the second layer of the player's skin (for models that are instances of
* {@link net.minecraft.client.model.ModelPlayer ModelPlayer}) should automatically be set to the same angles as
* their corresponding first-layer parts.
* @param player The player being rendered, for reference
* @param firstPerson True if the player being animated is the local client player, and they are in first-person
* view. In first-person, animations are only useful for animating the player's empty hand.
* @return True to let {@link PlayerAnimator} auto-rotate the second skin layer, false to rotate them manually.
*/
public boolean autoRotateSecondLayer(EntityPlayer player, boolean firstPerson){
return true;
}
/**
* Returns the arm of the given model corresponding to the given side. Function is identical to the method of the
* same name in {@link ModelBiped}.
* @param model The model to get the arm of
* @param side The {@link EnumHandSide} to return the arm for
* @return The {@link ModelRenderer} corresponding to the given arm
*/
public static ModelRenderer getArmForSide(ModelBiped model, EnumHandSide side){
return side == EnumHandSide.LEFT ? model.bipedLeftArm : model.bipedRightArm;
}
}
@@ -0,0 +1,205 @@
package electroblob.wizardry.client.animation;
import net.minecraft.client.model.ModelBase;
import net.minecraft.client.model.ModelBiped;
import net.minecraft.client.model.ModelPlayer;
import net.minecraft.client.model.ModelRenderer;
/**
* A wrapper around {@link ModelRenderer} which can be swapped into models to allow render pre/post events to
* circumvent {@code ModelBase.setRotationAngles(...)} and set model part rotations themselves. Instances of this class
* keep a reference to the original {@code ModelRenderer} and delegate all the actual rendering to it.
* <p></p>
* {@link PlayerAnimator PlayerAnimator} does all the necessary setup with this
* class for player animations. It is also possible to set up custom animations for non-player entities with this class,
* see {@link ModelRendererExtended#wrap(ModelBiped)} and {@link ModelRendererExtended#wrap(ModelBase, ModelRenderer)}.
* @author Electroblob
* @since Wizardry 4.3
*/
public class ModelRendererExtended extends ModelRenderer {
// We probably could have copied all the fields over instead, but this way if anyone else modifies the boxes we
// won't overwrite their changes
// This way also avoids reflection because we don't need to copy private fields, which is better for performance
private ModelRenderer delegate;
private float actualRotationX;
private float actualRotationY;
private float actualRotationZ;
private float extraRotationX;
private float extraRotationY;
private float extraRotationZ;
private ModelRendererExtended(ModelBase baseModel, ModelRenderer delegate){
super(baseModel, delegate.boxName);
this.delegate = delegate;
// Copy over any public fields that might get changed
this.textureWidth = delegate.textureWidth;
this.textureHeight = delegate.textureHeight;
this.showModel = delegate.showModel;
this.isHidden = delegate.isHidden;
this.rotationPointX = delegate.rotationPointX;
this.rotationPointY = delegate.rotationPointY;
this.rotationPointZ = delegate.rotationPointZ;
this.cubeList = delegate.cubeList;
this.childModels = delegate.childModels;
this.resetRotation();
}
// Static initialisers
/**
* Replaces the given part of the given {@link ModelBase} with a new {@code ModelRendererExtended} that wraps the
* original part. This method takes care of modifying the model's box list, but <b>is unable to change any
* individual {@link ModelRenderer} fields</b> - this method instead returns the resulting
* {@code ModelRendererExtended} to be assigned to the appropriate field.
* @param model The model whose part is to be wrapped
* @param box The part of the model to wrap, must belong to the above model
* @return The resulting wrapped model part, this should be assigned to the appropriate part field in the model. If
* the given model part was already wrapped, this method simply returns it unchanged.
* @throws IllegalArgumentException if the given box does not belong to the given model
*/
public static ModelRendererExtended wrap(ModelBase model, ModelRenderer box){
if(box instanceof ModelRendererExtended) return (ModelRendererExtended)box; // Ignore already-wrapped parts
ModelRendererExtended wrapper = new ModelRendererExtended(model, box);
int index = model.boxList.indexOf(box);
if(index < 0) throw new IllegalArgumentException(String.format("The given ModelRenderer %s does not belong to the given model %s", box, model));
model.boxList.set(index, wrapper); // I doubt the order matters but we may as well put it at the same index
return wrapper;
}
/**
* Replaces the given {@link ModelBiped}'s parts with new {@code ModelRendererExtended} versions that wrap the
* original parts. If the given model is a {@link ModelPlayer}, the extra boxes for player skin overlays will also
* be wrapped.
*/
public static void wrap(ModelBiped model){
// MMmmmmmm wraps
model.bipedHead = wrap(model, model.bipedHead);
model.bipedBody = wrap(model, model.bipedBody);
model.bipedRightArm = wrap(model, model.bipedRightArm);
model.bipedLeftArm = wrap(model, model.bipedLeftArm);
model.bipedRightLeg = wrap(model, model.bipedRightLeg);
model.bipedLeftLeg = wrap(model, model.bipedLeftLeg);
model.bipedHeadwear = wrap(model, model.bipedHeadwear);
if(model instanceof ModelPlayer){
((ModelPlayer)model).bipedBodyWear = wrap(model, ((ModelPlayer)model).bipedBodyWear);
((ModelPlayer)model).bipedRightArmwear = wrap(model, ((ModelPlayer)model).bipedRightArmwear);
((ModelPlayer)model).bipedLeftArmwear = wrap(model, ((ModelPlayer)model).bipedLeftArmwear);
((ModelPlayer)model).bipedRightLegwear = wrap(model, ((ModelPlayer)model).bipedRightLegwear);
((ModelPlayer)model).bipedLeftLegwear = wrap(model, ((ModelPlayer)model).bipedLeftLegwear);
}
}
/**
* Returns whether the given {@link ModelBiped} is fully wrapped, i.e. all its parts are instances of
* {@code ModelRendererExtended}. If the given model is a {@link ModelPlayer}, this also checks the extra boxes for
* player skin overlays.
*/
public static boolean isWrapped(ModelBiped model){
boolean flag = true;
if(model instanceof ModelPlayer){
flag = flag && ((ModelPlayer)model).bipedBodyWear instanceof ModelRendererExtended;
flag = flag && ((ModelPlayer)model).bipedRightArmwear instanceof ModelRendererExtended;
flag = flag && ((ModelPlayer)model).bipedLeftArmwear instanceof ModelRendererExtended;
flag = flag && ((ModelPlayer)model).bipedRightLegwear instanceof ModelRendererExtended;
flag = flag && ((ModelPlayer)model).bipedLeftLegwear instanceof ModelRendererExtended;
}
return flag
&& model.bipedHead instanceof ModelRendererExtended
&& model.bipedBody instanceof ModelRendererExtended
&& model.bipedRightArm instanceof ModelRendererExtended
&& model.bipedLeftArm instanceof ModelRendererExtended
&& model.bipedRightLeg instanceof ModelRendererExtended
&& model.bipedLeftLeg instanceof ModelRendererExtended
&& model.bipedHeadwear instanceof ModelRendererExtended;
}
/** Resets the rotation of this model part to the angle set by the parent model. */
public void resetRotation(){
this.actualRotationX = Float.NaN;
this.actualRotationY = Float.NaN;
this.actualRotationZ = Float.NaN;
this.extraRotationX = 0;
this.extraRotationY = 0;
this.extraRotationZ = 0;
}
/** Sets the rotation of this model part, which will overwrite the angle set by the parent model. */
public void setRotation(float x, float y, float z){
this.actualRotationX = x;
this.actualRotationY = y;
this.actualRotationZ = z;
}
/** Sets the extra rotation of this model part, which will be added onto the angle set by the parent model. */
public void addRotation(float x, float y, float z){
this.extraRotationX = x;
this.extraRotationY = y;
this.extraRotationZ = z;
}
/** Sets the rotation of this model part to the same values as the given box. */
public void setRotationTo(ModelRendererExtended box){
this.actualRotationX = box.actualRotationX;
this.actualRotationY = box.actualRotationY;
this.actualRotationZ = box.actualRotationZ;
this.extraRotationX = box.extraRotationX;
this.extraRotationY = box.extraRotationY;
this.extraRotationZ = box.extraRotationZ;
}
// Delegate rendering, but fiddle with the angles first
@Override
public void render(float scale){
// Need to copy these over each time in case they were changed
delegate.showModel = this.showModel;
delegate.isHidden = this.isHidden;
delegate.rotationPointX = this.rotationPointX;
delegate.rotationPointY = this.rotationPointY;
delegate.rotationPointZ = this.rotationPointZ;
if(!Float.isNaN(actualRotationX) && !Float.isNaN(actualRotationY) && !Float.isNaN(actualRotationZ)){
delegate.rotateAngleX = actualRotationX;
delegate.rotateAngleY = actualRotationY;
delegate.rotateAngleZ = actualRotationZ;
}else{
delegate.rotateAngleX = this.rotateAngleX + extraRotationX;
delegate.rotateAngleY = this.rotateAngleY + extraRotationY;
delegate.rotateAngleZ = this.rotateAngleZ + extraRotationZ;
}
delegate.render(scale);
}
// Delegate all other methods
@Override
public void addChild(ModelRenderer renderer){
delegate.addChild(renderer);
}
@Override
public void renderWithRotation(float scale){
delegate.renderWithRotation(scale);
}
@Override
public void postRender(float scale){
// Exactly the same setup as above, just add item rotation/translation fields and setters
// float angle = 1;
// float radius = 10;
// delegate.rotationPointY -= radius * MathHelper.cos(angle);
// delegate.rotationPointZ -= radius * MathHelper.sin(angle);
// delegate.rotateAngleX += angle;
delegate.postRender(scale);
}
}
@@ -0,0 +1,255 @@
package electroblob.wizardry.client.animation;
import electroblob.wizardry.Wizardry;
import electroblob.wizardry.item.SpellActions;
import electroblob.wizardry.util.InventoryUtils;
import electroblob.wizardry.util.JavaUtils;
import net.minecraft.client.Minecraft;
import net.minecraft.client.entity.AbstractClientPlayer;
import net.minecraft.client.model.ModelBiped;
import net.minecraft.client.model.ModelPlayer;
import net.minecraft.client.model.ModelRenderer;
import net.minecraft.client.renderer.GlStateManager;
import net.minecraft.client.renderer.entity.Render;
import net.minecraft.client.renderer.entity.RenderLivingBase;
import net.minecraft.client.renderer.entity.RenderPlayer;
import net.minecraft.client.renderer.entity.layers.LayerBipedArmor;
import net.minecraft.client.renderer.entity.layers.LayerRenderer;
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.ItemStack;
import net.minecraft.util.EnumHand;
import net.minecraft.util.EnumHandSide;
import net.minecraftforge.client.ForgeHooksClient;
import net.minecraftforge.client.event.RenderPlayerEvent;
import net.minecraftforge.client.event.RenderSpecificHandEvent;
import net.minecraftforge.fml.common.Loader;
import net.minecraftforge.fml.common.Mod;
import net.minecraftforge.fml.common.ObfuscationReflectionHelper;
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
import net.minecraftforge.fml.relauncher.Side;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
/**
* Handles the setup and rendering events for custom player animations, as well as registering of animations. Addons
* can also register their own animations here, see {@link PlayerAnimator#registerAnimation(Animation)}.
* @author Electroblob
* @since Wizardry 4.3
* @see electroblob.wizardry.item.SpellActions SpellActions
*/
@Mod.EventBusSubscriber(Side.CLIENT)
public class PlayerAnimator {
// TODO: Attempt to extend this to all bipeds
private static final List<Animation> animations = new ArrayList<>();
/** Stores all registered layer renderers for lazy-loading of armour models later. */
private static final Map<RenderPlayer, List<LayerRenderer<? extends EntityLivingBase>>> playerLayers = new HashMap<>();
/** Stores the main model for each renderer plus all models for registered layer renderers. */
private static final Map<RenderPlayer, List<ModelBiped>> playerLayerModels = new HashMap<>();
/** Reflected into {@link RenderLivingBase}{@code #layerRenderers}. */
private static final Field layerRenderers;
/** True if wizardry's player animations have been overridden with another mod (currently only MoBends) */
private static boolean overridden;
static {
layerRenderers = ObfuscationReflectionHelper.findField(RenderLivingBase.class, "field_177097_h");
}
/**
* Registers a new player animation. This method should be called from the {@code init()} phase via a client proxy.
* @param animation The animation to register
*/
// N.B. This can (and will) be called before PlayerAnimator#init() is called
public static void registerAnimation(Animation animation){
if(animations.contains(animation)){
Wizardry.logger.warn("Animation {} is already registered!", animation.getName());
}else{
animations.add(animation);
}
}
/** Returns true if spellcasting animations are enabled, false otherwise. */
public static boolean areAnimationsEnabled(){
return Wizardry.settings.spellcastingAnimations && !overridden;
}
public static void init(){
if(Loader.isModLoaded("mobends")) overridden = true;
if(!areAnimationsEnabled()) return;
for(RenderPlayer renderer : Minecraft.getMinecraft().getRenderManager().getSkinMap().values()){
loadLayersForRenderer(renderer);
}
}
@SuppressWarnings("unchecked")
private static void loadLayersForRenderer(RenderPlayer renderer){
List<ModelBiped> models = new ArrayList<>();
ModelRendererExtended.wrap(renderer.getMainModel());
models.add(renderer.getMainModel());
try {
List<LayerRenderer<? extends EntityLivingBase>> layers = (List<LayerRenderer<? extends EntityLivingBase>>)layerRenderers.get(renderer);
// Save armour layers for lazy-loading later (ignore other layers so iteration is faster)
playerLayers.put(renderer, layers.stream().filter(l -> l instanceof LayerBipedArmor).collect(Collectors.toList()));
for(LayerRenderer<?> layer : layers){
for(Field field : JavaUtils.getAllFields(layer.getClass())){
field.setAccessible(true);
// If your layer model doesn't extend ModelBiped, you DESERVE to be incompatible!
// (Just kidding... there's nothing I can do about it anyway)
if(field.get(layer) instanceof ModelBiped){
ModelBiped model = (ModelBiped)field.get(layer);
ModelRendererExtended.wrap(model);
models.add(model);
}
}
}
}catch(IllegalAccessException e){
Wizardry.logger.error("Error during reflective access of render layers: ", e);
}
playerLayerModels.put(renderer, models);
}
private static void updateModels(EntityPlayer player, Render<?> renderer, float partialTicks, boolean firstPerson){
// Lazy-load models from custom renderers (some mods assign their custom renderers in postInit, so they might
// not be picked up from PlayerAnimator#init)
if(renderer instanceof RenderPlayer && !playerLayerModels.containsKey(renderer)){
loadLayersForRenderer((RenderPlayer)renderer);
}
List<ModelBiped> models = playerLayerModels.get(renderer);
// If no models were registered for the given renderer, don't do anything
if(models != null){
List<LayerRenderer<? extends EntityLivingBase>> layers = playerLayers.get(renderer);
// If no layers were registered for the given renderer there's no point trying to lazy-load anything!
if(layers != null){
// Biped armour is special because of Forge's armour item render hook
// This needs to be lazy-loaded because we need access to an actual item
for(LayerRenderer<? extends EntityLivingBase> layer : layers){
if(layer instanceof LayerBipedArmor){
for(EntityEquipmentSlot slot : InventoryUtils.ARMOUR_SLOTS){
ItemStack armour = player.getItemStackFromSlot(slot);
// This method could behave dynamically depending on stuff like NBT so there's not really any way to
// optimise it further, we *have* to retrieve the model every time (shouldn't be too bad though)
ModelBiped model = ForgeHooksClient.getArmorModel(player, armour, slot, ((LayerBipedArmor)layer).getModelFromSlot(slot));
if(!models.contains(model)){ // Ignore already-wrapped models
models.add(model);
ModelRendererExtended.wrap(model);
}
}
}
}
}
for(Animation animation : animations){
if(animation.shouldDisplay(player, firstPerson)){
boolean autoRotateSecondLayer = animation.autoRotateSecondLayer(player, firstPerson);
for(ModelBiped model : models){
if(!ModelRendererExtended.isWrapped(model)) continue; // Should never happen but somehow it does
animation.setRotationAngles(player, model, partialTicks, firstPerson);
if(autoRotateSecondLayer && model instanceof ModelPlayer){
alignSecondLayer((ModelPlayer)model);
}
}
}
}
}
}
/** Rotates the second layer boxes of the given (wrapped) player model to match the first layer. */
public static void alignSecondLayer(ModelPlayer model){
((ModelRendererExtended)model.bipedBodyWear) .setRotationTo((ModelRendererExtended)model.bipedBody);
((ModelRendererExtended)model.bipedRightArmwear).setRotationTo((ModelRendererExtended)model.bipedRightArm);
((ModelRendererExtended)model.bipedLeftArmwear) .setRotationTo((ModelRendererExtended)model.bipedLeftArm);
((ModelRendererExtended)model.bipedRightLegwear).setRotationTo((ModelRendererExtended)model.bipedBody);
((ModelRendererExtended)model.bipedLeftLegwear) .setRotationTo((ModelRendererExtended)model.bipedBody);
}
@SubscribeEvent
public static void onRenderHandEvent(RenderSpecificHandEvent event){
if(!areAnimationsEnabled()) return;
AbstractClientPlayer player = Minecraft.getMinecraft().player;
EnumAction action = event.getItemStack().getItemUseAction();
if(player.isHandActive() && player.getActiveHand() == event.getHand() && SpellActions.getSpellActions().contains(action)){
// Minecraft's item renderer helpfully doesn't have a default case for the usage action so it doesn't do any
// transformations at all (which is VERY ANNOYING!) - the following lines are from ItemRenderer#transformSideFirstPerson
int i = (player.getPrimaryHand() == EnumHandSide.RIGHT) == (event.getHand() == EnumHand.MAIN_HAND) ? 1 : -1;
GlStateManager.translate((float)i * 0.56F, -0.52F + event.getEquipProgress() * -0.6F, -0.72F);
}
updateModels(player, Minecraft.getMinecraft().getRenderManager().getEntityRenderObject(player), event.getPartialTicks(), true);
}
@SubscribeEvent
public static void onRenderPlayerPreEvent(RenderPlayerEvent.Pre event){
if(!areAnimationsEnabled()) return;
// boolean firstPerson = event.getEntityPlayer() == Minecraft.getMinecraft().player
// && Minecraft.getMinecraft().gameSettings.thirdPersonView == 0;
updateModels(event.getEntityPlayer(), event.getRenderer(), event.getPartialRenderTick(), false);
}
@SubscribeEvent
public static void onRenderPlayerPostEvent(RenderPlayerEvent.Post event){
if(!areAnimationsEnabled()) return;
List<ModelBiped> models = playerLayerModels.get(event.getRenderer());
if(models != null){
for(ModelBiped model : models){
for(ModelRenderer box : model.boxList){ // For ModelPlayer, this will include the second layer
// Some models have extra boxes, they (probably) don't need wrapping but we need this check!
if(box instanceof ModelRendererExtended){
((ModelRendererExtended)box).resetRotation();
}
}
}
}
}
}
@@ -0,0 +1,36 @@
package electroblob.wizardry.client.audio;
import electroblob.wizardry.item.ISpellCastingItem;
import net.minecraft.entity.EntityLivingBase;
import net.minecraft.item.ItemStack;
import net.minecraft.util.SoundCategory;
import net.minecraft.util.SoundEvent;
/** A special type of moving sound that stops when the player stops charging up a spell, either by releasing the mouse
* button or when the charge-up time has elapsed. */
public class MovingSoundSpellCharge extends MovingSoundEntity<EntityLivingBase> {
public MovingSoundSpellCharge(EntityLivingBase entity, SoundEvent sound, SoundCategory category, float volume, float pitch, boolean repeat){
super(entity, sound, category, volume, pitch, repeat);
}
@Override
public void update(){
if(source.isHandActive()){
ItemStack stack = source.getActiveItemStack();
if(stack.getItem() instanceof ISpellCastingItem){
if(source.getItemInUseMaxCount() < ((ISpellCastingItem)stack.getItem()).getCurrentSpell(stack).getChargeup()){
super.update();
return;
}
}
}
this.donePlaying = true;
}
}
@@ -27,6 +27,16 @@ public abstract class SoundLoop implements ITickable {
private static final Set<SoundLoop> activeLoops = new HashSet<>();
// The issue we had here was that there is no way of hooking into the client tick loop between the sound manager
// update and the world tick (other than via the sounds themselves, but playing other sounds from there results in
// CMEs, which is why we made this class in the first place). This meant there was always a gap between consecutive
// sounds, which could be heard in-game as a weird audio artefact. The solution is simply to overlap the sounds by
// 1 tick, which is fairly straightforward for the end sound, but because the start sound has to finish for us to
// know when to play the others, this doubling-up solution is required (bit of a waste of resources but meh).
/** A dummy sound with effectively zero volume, played one tick in advance of the actual start sound so the loop
* sound can start one tick before the actual start sound ends. */
private final ISound dummyStart; // It's the stupidest system ever but it works!
private final ISound start;
private final ISound loop;
private final ISound end;
@@ -34,29 +44,28 @@ public abstract class SoundLoop implements ITickable {
private boolean looping = false;
private boolean needsRemoving = false;
public SoundLoop(SoundEvent start, SoundEvent loop, SoundEvent end, SoundCategory category, ISoundFactory factory){
public SoundLoop(SoundEvent start, SoundEvent loop, SoundEvent end, SoundCategory category, float volume, ISoundFactory factory){
// The reason I've gone to the effort of having a factory for these is that we need SoundLoop to have control
// over which sounds are repeated and which aren't whilst keeping them private.
this.start = factory.create(start, category, false);
this.loop = factory.create(loop, category, true);
this.end = factory.create(end, category, false);
this.dummyStart = factory.create(start, category, 0.00001f, false); // Inaudible but not actually 0
this.start = factory.create(start, category, volume, false);
this.loop = factory.create(loop, category, volume, true);
this.end = factory.create(end, category, volume, false);
}
@Override
public void update(){
// Check every tick if the start sound is done playing and if so, start the loop sound
if(!looping && !Minecraft.getMinecraft().getSoundHandler().isSoundPlaying(start)){
if(!looping && !Minecraft.getMinecraft().getSoundHandler().isSoundPlaying(dummyStart)){
Minecraft.getMinecraft().getSoundHandler().playSound(loop);
looping = true;
}
}
/** Stops the loop part of the sound immediately and starts playing the end part. This may be called from subclasses
* or externally depending on the implementation. */
/** Starts playing the end part immediately and marks the loop part to stop next tick. This may be called from
* subclasses or externally depending on the implementation. */
// For continuous spell sounds it's internally
public void endLoop(){
Minecraft.getMinecraft().getSoundHandler().stopSound(start);
Minecraft.getMinecraft().getSoundHandler().stopSound(loop);
Minecraft.getMinecraft().getSoundHandler().playSound(end);
// Can't modify activeLoops directly since we'll probably be calling this method from update(), which is
// during iteration of activeLoops so it could cause a ConcurrentModificationException
@@ -68,12 +77,20 @@ public abstract class SoundLoop implements ITickable {
this.needsRemoving = true;
}
/** Stops the start and loop sounds immediately. */
protected void stopStartAndLoop(){
Minecraft.getMinecraft().getSoundHandler().stopSound(dummyStart);
Minecraft.getMinecraft().getSoundHandler().stopSound(start);
Minecraft.getMinecraft().getSoundHandler().stopSound(loop);
}
// Static methods
public static void addLoop(SoundLoop loop){
activeLoops.add(loop);
// Do this here rather than in the constructor in case someone wants to play the loop later or reuse it
Minecraft.getMinecraft().getSoundHandler().playSound(loop.start);
Minecraft.getMinecraft().getSoundHandler().playSound(loop.dummyStart);
Minecraft.getMinecraft().getSoundHandler().playDelayedSound(loop.start, 2); // 1 seems to be insufficient
}
@SubscribeEvent
@@ -82,13 +99,14 @@ public abstract class SoundLoop implements ITickable {
// meaning we don't get the 'cut' between the start and loop sounds
// FIXME: Apparently this only works for dispensers. What the heck is the difference?!
if(event.phase == TickEvent.Phase.END){
activeLoops.forEach(SoundLoop::update);
activeLoops.stream().filter(s -> s.needsRemoving).forEach(SoundLoop::stopStartAndLoop);
activeLoops.removeIf(s -> s.needsRemoving);
activeLoops.forEach(SoundLoop::update); // Do this last to allow a 1-tick overlap
}
}
@FunctionalInterface
public interface ISoundFactory {
ISound create(SoundEvent sound, SoundCategory category, boolean repeat);
ISound create(SoundEvent sound, SoundCategory category, float volume, boolean repeat);
}
}
@@ -3,7 +3,7 @@ package electroblob.wizardry.client.audio;
import electroblob.wizardry.data.DispenserCastingData;
import electroblob.wizardry.registry.WizardrySounds;
import electroblob.wizardry.spell.Spell;
import electroblob.wizardry.util.WizardryUtilities;
import electroblob.wizardry.util.EntityUtils;
import net.minecraft.client.audio.PositionedSound;
import net.minecraft.entity.EntityLivingBase;
import net.minecraft.tileentity.TileEntity;
@@ -17,8 +17,8 @@ public abstract class SoundLoopSpell extends SoundLoop {
private final Spell spell;
public SoundLoopSpell(SoundEvent start, SoundEvent loop, SoundEvent end, ISoundFactory factory, Spell spell){
super(start, loop, end, WizardrySounds.SPELLS, factory);
public SoundLoopSpell(SoundEvent start, SoundEvent loop, SoundEvent end, float volume, ISoundFactory factory, Spell spell){
super(start, loop, end, WizardrySounds.SPELLS, volume, factory);
this.spell = spell;
}
@@ -43,13 +43,13 @@ public abstract class SoundLoopSpell extends SoundLoop {
private final EntityLivingBase source;
public SoundLoopSpellEntity(SoundEvent start, SoundEvent loop, SoundEvent end, Spell spell, EntityLivingBase source, float volume, float pitch){
super(start, loop, end, (sound, category, repeat) -> new MovingSoundEntity<>(source, sound, category, volume, pitch, repeat), spell);
super(start, loop, end, volume, (sound, category, v, repeat) -> new MovingSoundEntity<>(source, sound, category, v, pitch, repeat), spell);
this.source = source;
}
@Override
protected boolean stillCasting(Spell spell){
return WizardryUtilities.isCasting(source, spell);
return EntityUtils.isCasting(source, spell);
}
}
@@ -58,13 +58,13 @@ public abstract class SoundLoopSpell extends SoundLoop {
public SoundLoopSpellPosition (SoundEvent start, SoundEvent loop, SoundEvent end, Spell spell,
double x, double y, double z, float sndVolume, float sndPitch){
// Huh, I actually found a use for a non-static initialiser block - hence the double curly brackets...
super(start, loop, end, (sound, category, r) -> new PositionedSound(sound, category){{
super(start, loop, end, sndVolume, (sound, category, v, r) -> new PositionedSound(sound, category){{
// ...et voila, we can just set protected fields as we please using external variables
this.xPosF = (float)x;
this.yPosF = (float)y;
this.zPosF = (float)z;
this.repeat = r;
this.volume = sndVolume;
this.volume = v;
this.pitch = sndPitch;
}}, spell);
}
@@ -0,0 +1,109 @@
package electroblob.wizardry.client.gui;
import electroblob.wizardry.Wizardry;
import electroblob.wizardry.client.DrawingUtils;
import electroblob.wizardry.registry.WizardryTabs;
import electroblob.wizardry.spell.Spell;
import net.minecraft.client.gui.GuiTextField;
import net.minecraft.client.gui.inventory.GuiContainerCreative;
import net.minecraft.client.resources.I18n;
import net.minecraft.creativetab.CreativeTabs;
import net.minecraft.util.text.Style;
import net.minecraft.util.text.TextFormatting;
import net.minecraftforge.client.event.GuiScreenEvent;
import net.minecraftforge.fml.common.Mod.EventBusSubscriber;
import net.minecraftforge.fml.common.ObfuscationReflectionHelper;
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
import net.minecraftforge.fml.common.gameevent.TickEvent.ClientTickEvent;
import net.minecraftforge.fml.common.gameevent.TickEvent.Phase;
import net.minecraftforge.fml.relauncher.Side;
import java.lang.reflect.Field;
import java.util.Locale;
@EventBusSubscriber(Side.CLIENT)
public class CustomCreativeSearchHandler {
private static final int SEARCH_TOOLTIP_HOVER_TIME = 20;
private static final Style TOOLTIP_SYNTAX = new Style().setColor(TextFormatting.YELLOW);
private static final Style TOOLTIP_BODY = new Style().setColor(TextFormatting.WHITE);
/** Reflected into {@code GuiContainerCreative#searchField} */
private static final Field searchField;
private static GuiTextField currentSearchField = null;
private static int searchBarHoverTime;
static {
searchField = ObfuscationReflectionHelper.findField(GuiContainerCreative.class, "field_147062_A");
}
@SubscribeEvent
public static void onInitGuiEvent(GuiScreenEvent.InitGuiEvent.Post event){
// Reduces reflection as much as possible - searchField is created on GUI init and never reassigned so we need
// not (and should not!) use reflection to retrieve it every time a key is typed
if(event.getGui() instanceof GuiContainerCreative){
try{
currentSearchField = (GuiTextField)searchField.get(event.getGui());
}catch(IllegalAccessException e){
e.printStackTrace();
}
}
}
@SubscribeEvent
public static void onKeyboardInputEvent(GuiScreenEvent.KeyboardInputEvent.Post event){
// Custom creative tab search behaviour
if(event.getGui() instanceof GuiContainerCreative){
GuiContainerCreative gui = (GuiContainerCreative)event.getGui();
CreativeTabs tab = CreativeTabs.CREATIVE_TAB_ARRAY[gui.getSelectedTabIndex()];
if(tab == WizardryTabs.SPELLS){
GuiContainerCreative.ContainerCreative container = (GuiContainerCreative.ContainerCreative)gui.inventorySlots;
container.itemList.clear(); // Required or duplicates will appear!
String searchText = currentSearchField.getText().toLowerCase(Locale.ROOT);
tab.displayAllRelevantItems(container.itemList);
if(!searchText.isEmpty()){
container.itemList.removeIf(s -> !Spell.byMetadata(s.getMetadata()).matches(searchText));
container.scrollTo(0); // Seems to refresh the GUI somehow so it displays correctly
}
}
}
}
@SubscribeEvent
public static void onClientTickEvent(ClientTickEvent event){
if(event.phase == Phase.END && searchBarHoverTime > 0 && searchBarHoverTime < SEARCH_TOOLTIP_HOVER_TIME){
searchBarHoverTime++;
}
}
@SubscribeEvent
public static void onDrawScreenPostEvent(GuiScreenEvent.DrawScreenEvent.Post event){
if(event.getGui() instanceof GuiContainerCreative && currentSearchField != null){
GuiContainerCreative gui = (GuiContainerCreative)event.getGui();
CreativeTabs tab = CreativeTabs.CREATIVE_TAB_ARRAY[gui.getSelectedTabIndex()];
if(tab == WizardryTabs.SPELLS && DrawingUtils.isPointInRegion(currentSearchField.x, currentSearchField.y, currentSearchField.width, currentSearchField.height, event.getMouseX(), event.getMouseY())){
if(searchBarHoverTime == 0){
searchBarHoverTime++;
}else if(searchBarHoverTime == SEARCH_TOOLTIP_HOVER_TIME){
event.getGui().drawHoveringText(I18n.format("container." + Wizardry.MODID + ":arcane_workbench.search_tooltip",
TOOLTIP_SYNTAX.getFormattingCode(), TOOLTIP_BODY.getFormattingCode()), event.getMouseX(), event.getMouseY());
}
}
}else{
searchBarHoverTime = 0;
}
}
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,54 @@
package electroblob.wizardry.client.gui;
import electroblob.wizardry.Wizardry;
import electroblob.wizardry.inventory.ContainerBookshelf;
import electroblob.wizardry.tileentity.TileEntityBookshelf;
import net.minecraft.client.gui.inventory.GuiContainer;
import net.minecraft.client.renderer.GlStateManager;
import net.minecraft.entity.player.InventoryPlayer;
import net.minecraft.inventory.IInventory;
import net.minecraft.util.ResourceLocation;
import net.minecraftforge.fml.relauncher.Side;
import net.minecraftforge.fml.relauncher.SideOnly;
@SideOnly(Side.CLIENT)
public class GuiBookshelf extends GuiContainer {
private static final ResourceLocation TEXTURE = new ResourceLocation(Wizardry.MODID, "textures/gui/container/bookshelf.png");
/** The player inventory bound to this GUI. */
private final InventoryPlayer playerInventory;
/** The inventory contained within the corresponding bookshelf. */
public IInventory bookshelfInventory;
public GuiBookshelf(InventoryPlayer playerInv, TileEntityBookshelf bookshelfInv){
super(new ContainerBookshelf(playerInv, bookshelfInv));
this.playerInventory = playerInv;
this.bookshelfInventory = bookshelfInv;
this.allowUserInput = false;
this.ySize = 148;
}
@Override
public void drawScreen(int mouseX, int mouseY, float partialTicks){
this.drawDefaultBackground();
super.drawScreen(mouseX, mouseY, partialTicks);
this.renderHoveredToolTip(mouseX, mouseY);
}
@Override
protected void drawGuiContainerForegroundLayer(int mouseX, int mouseY){
String s = this.bookshelfInventory.getDisplayName().getUnformattedText();
this.fontRenderer.drawString(s, 8, 6, 4210752);
this.fontRenderer.drawString(this.playerInventory.getDisplayName().getUnformattedText(), 8, this.ySize - 96 + 2, 4210752);
}
@Override
protected void drawGuiContainerBackgroundLayer(float partialTicks, int mouseX, int mouseY){
GlStateManager.color(1.0F, 1.0F, 1.0F, 1.0F);
this.mc.getTextureManager().bindTexture(TEXTURE);
int i = (this.width - this.xSize) / 2;
int j = (this.height - this.ySize) / 2;
this.drawTexturedModalRect(i, j, 0, 0, this.xSize, this.ySize);
}
}
@@ -7,8 +7,8 @@ import electroblob.wizardry.packet.WizardryPacketHandler;
import electroblob.wizardry.registry.Spells;
import electroblob.wizardry.registry.WizardryItems;
import electroblob.wizardry.spell.Resurrection;
import electroblob.wizardry.util.InventoryUtils;
import electroblob.wizardry.util.SpellModifiers;
import electroblob.wizardry.util.WizardryUtilities;
import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.GuiButton;
import net.minecraft.client.gui.GuiGameOver;
@@ -52,7 +52,7 @@ public class GuiButtonResurrect extends GuiButton {
public static void onGuiScreenInitEvent(GuiScreenEvent.InitGuiEvent event){
if(event.getGui() instanceof GuiGameOver && ItemArtefact.isArtefactActive(Minecraft.getMinecraft().player, WizardryItems.amulet_resurrection)
&& WizardryUtilities.getHotbar(Minecraft.getMinecraft().player).stream().anyMatch(s -> Resurrection.canStackResurrect(s, Minecraft.getMinecraft().player))){
&& InventoryUtils.getHotbar(Minecraft.getMinecraft().player).stream().anyMatch(s -> Resurrection.canStackResurrect(s, Minecraft.getMinecraft().player))){
event.getButtonList().add(new GuiButtonResurrect(event.getButtonList().size(), event.getGui().width / 2 - 100,
event.getGui().height / 4 + 120, "spell." + Spells.resurrection.getRegistryName() + ".button"));
@@ -65,7 +65,7 @@ public class GuiButtonResurrect extends GuiButton {
if(event.getGui() instanceof GuiGameOver){
ItemStack stack = WizardryUtilities.getHotbar(Minecraft.getMinecraft().player).stream()
ItemStack stack = InventoryUtils.getHotbar(Minecraft.getMinecraft().player).stream()
.filter(s -> Resurrection.canStackResurrect(s, Minecraft.getMinecraft().player)).findFirst().orElse(null);
if(stack != null){
@@ -0,0 +1,57 @@
package electroblob.wizardry.client.gui;
import electroblob.wizardry.Wizardry;
import electroblob.wizardry.client.DrawingUtils;
import electroblob.wizardry.util.ISpellSortable;
import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.GuiButton;
import net.minecraft.client.gui.GuiScreen;
import net.minecraft.client.resources.I18n;
import net.minecraft.util.ResourceLocation;
public class GuiButtonSpellSort extends GuiButton {
private static final ResourceLocation TEXTURE = new ResourceLocation(Wizardry.MODID, "textures/gui/container/spell_sort_buttons.png");
private static final int TEXTURE_WIDTH = 32;
private static final int TEXTURE_HEIGHT = 32;
public final ISpellSortable.SortType sortType;
private final ISpellSortable sortable;
private final GuiScreen parent;
public GuiButtonSpellSort(int id, int x, int y, ISpellSortable.SortType sortType, ISpellSortable sortable, GuiScreen parent){
super(id, x, y, 10, 10, I18n.format("container." + Wizardry.MODID + ":arcane_workbench.sort_" + sortType.name));
this.sortType = sortType;
this.sortable = sortable;
this.parent = parent;
}
@Override
public void drawButton(Minecraft minecraft, int mouseX, int mouseY, float partialTicks){
if(this.visible){
// Whether the button is highlighted
this.hovered = mouseX >= this.x && mouseY >= this.y && mouseX < this.x + this.width && mouseY < this.y + this.height;
int k = 0;
int l = this.sortType.ordinal() * this.height;
if(sortType == sortable.getSortType()){
k += this.width;
if(sortable.isSortDescending()) k += this.width;
}
parent.mc.getTextureManager().bindTexture(TEXTURE);
DrawingUtils.drawTexturedRect(this.x, this.y, k, l, this.width, this.height, TEXTURE_WIDTH, TEXTURE_HEIGHT);
}
}
@Override
public void drawButtonForegroundLayer(int mouseX, int mouseY){
if(hovered) parent.drawHoveringText(this.displayString, mouseX, mouseY);
}
}
@@ -1,6 +1,5 @@
package electroblob.wizardry.client.gui.handbook;
package electroblob.wizardry.client.gui;
import electroblob.wizardry.Wizardry;
import electroblob.wizardry.client.DrawingUtils;
import electroblob.wizardry.registry.WizardrySounds;
import net.minecraft.client.Minecraft;
@@ -11,12 +10,12 @@ import net.minecraft.client.renderer.GlStateManager;
import net.minecraft.util.ResourceLocation;
//@SideOnly(Side.CLIENT)
class GuiButtonTurnPage extends GuiButton {
public class GuiButtonTurnPage extends GuiButton {
static final int WIDTH = 20;
static final int HEIGHT = 12;
public static final int WIDTH = 20;
public static final int HEIGHT = 12;
enum Type {
public enum Type {
NEXT_PAGE(0, 196),
PREVIOUS_PAGE(0, 208),
@@ -34,11 +33,15 @@ class GuiButtonTurnPage extends GuiButton {
public final Type type;
private static final ResourceLocation texture = new ResourceLocation(Wizardry.MODID, "textures/gui/handbook.png");
private final ResourceLocation texture;
private final int textureWidth, textureHeight;
public GuiButtonTurnPage(int id, int x, int y, Type type){
public GuiButtonTurnPage(int id, int x, int y, Type type, ResourceLocation texture, int textureWidth, int textureHeight){
super(id, x, y, WIDTH, HEIGHT, "");
this.type = type;
this.texture = texture;
this.textureWidth = textureWidth;
this.textureHeight = textureHeight;
}
@Override
@@ -50,12 +53,13 @@ class GuiButtonTurnPage extends GuiButton {
public void drawButton(Minecraft minecraft, int mouseX, int mouseY, float partialTicks){
if(this.visible){
boolean flag = mouseX >= this.x && mouseY >= this.y && mouseX < this.x + this.width && mouseY < this.y + this.height;
GlStateManager.color(1.0F, 1.0F, 1.0F, 1.0F);
minecraft.getTextureManager().bindTexture(texture);
DrawingUtils.drawTexturedRect(this.x, this.y, flag ? type.u + width : type.u, type.v, width, height, 512, 256);
DrawingUtils.drawTexturedRect(this.x, this.y, flag ? type.u + width : type.u, type.v, width, height, textureWidth, textureHeight);
}
}
}
@@ -0,0 +1,499 @@
package electroblob.wizardry.client.gui;
import electroblob.wizardry.Wizardry;
import electroblob.wizardry.block.BlockBookshelf;
import electroblob.wizardry.client.DrawingUtils;
import electroblob.wizardry.client.gui.GuiButtonTurnPage.Type;
import electroblob.wizardry.data.SpellGlyphData;
import electroblob.wizardry.item.ItemSpellBook;
import electroblob.wizardry.packet.PacketLectern;
import electroblob.wizardry.packet.WizardryPacketHandler;
import electroblob.wizardry.registry.Spells;
import electroblob.wizardry.registry.WizardrySounds;
import electroblob.wizardry.spell.Spell;
import electroblob.wizardry.tileentity.TileEntityLectern;
import electroblob.wizardry.util.GeometryUtils;
import electroblob.wizardry.util.ISpellSortable;
import electroblob.wizardry.util.ParticleBuilder;
import net.minecraft.client.Minecraft;
import net.minecraft.client.audio.PositionedSoundRecord;
import net.minecraft.client.audio.SoundHandler;
import net.minecraft.client.gui.GuiButton;
import net.minecraft.client.gui.GuiTextField;
import net.minecraft.client.renderer.GlStateManager;
import net.minecraft.client.resources.I18n;
import net.minecraft.inventory.IInventory;
import net.minecraft.item.ItemStack;
import net.minecraft.network.play.client.CPacketCloseWindow;
import net.minecraft.tileentity.TileEntity;
import net.minecraft.util.EnumFacing;
import net.minecraft.util.ResourceLocation;
import net.minecraft.util.SoundCategory;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.MathHelper;
import net.minecraft.util.math.Vec3d;
import net.minecraft.util.text.Style;
import net.minecraft.util.text.TextFormatting;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Locale;
import java.util.stream.Collectors;
public class GuiLectern extends GuiSpellInfo implements ISpellSortable {
private static final ResourceLocation TEXTURE = new ResourceLocation(Wizardry.MODID, "textures/gui/container/lectern.png");
/** The distance of the page buttons from the bottom outside corners of the GUI. */
private static final int PAGE_BUTTON_INSET_X = 22, PAGE_BUTTON_INSET_Y = 13;
/** The distance between adjacent page turn buttons. */
private static final int PAGE_BUTTON_SPACING = 20;
/** The distance of the sort buttons from the top left corner of the GUI. */
private static final int SORT_BUTTON_INSET_X = 96, SORT_BUTTON_INSET_Y = 20;
/** The distance between adjacent sort buttons. */
private static final int SORT_BUTTON_SPACING = 13;
/** The distance of the spell buttons from the top outside corners of the GUI. */
private static final int SPELL_BUTTON_INSET_X = 23, SPELL_BUTTON_INSET_Y = 44;
/** The distance between adjacent spell buttons (in both x and y). */
private static final int SPELL_BUTTON_SPACING = 38;
private static final int SPELL_ROWS = 3, SPELL_COLUMNS = 3;
public static final int SPELL_BUTTON_COUNT = SPELL_ROWS * SPELL_COLUMNS * 2; // x2 because there are 2 pages
private static final int SEARCH_TOOLTIP_HOVER_TIME = 20;
private static final Style TOOLTIP_SYNTAX = new Style().setColor(TextFormatting.YELLOW);
private static final Style TOOLTIP_BODY = new Style().setColor(TextFormatting.WHITE);
private final TileEntityLectern lectern;
private GuiButton nextPageButton;
private GuiButton prevPageButton;
private GuiButton lastPageButton;
private GuiButton firstPageButton;
private GuiButton indexButton;
private GuiButton locateButton;
private GuiButton[] sortButtons = new GuiButton[3];
private GuiButtonSpell[] spellButtons = new GuiButtonSpell[SPELL_BUTTON_COUNT];
/** The spell currently being viewed, or null if the index is being viewed. */
private Spell currentSpell;
/** The available spells in nearby bookshelves; should not contain duplicates. */
private List<Spell> availableSpells = new ArrayList<>();
/** The spells matching the current search criteria, sorted according to the current sort settings. Will be a subset
* of {@link GuiLectern#availableSpells}. */
private List<Spell> matchingSpells;
private ISpellSortable.SortType sortType = ISpellSortable.SortType.TIER;
private boolean sortDescending = false;
private GuiTextField searchField;
private boolean searchNeedsClearing;
private int searchBarHoverTime;
private int currentPage = 0;
public GuiLectern(TileEntityLectern lectern){
super(288, 180);
this.lectern = lectern;
this.currentSpell = lectern.currentSpell;
this.setTextureSize(512, 512);
}
@Override
public Spell getSpell(){
return currentSpell;
}
@Override
public ResourceLocation getTexture(){
return TEXTURE;
}
@Override
public SortType getSortType(){
return sortType;
}
@Override
public boolean isSortDescending(){
return sortDescending;
}
private int getPageCount(){
return MathHelper.ceil((float)matchingSpells.size() / SPELL_BUTTON_COUNT);
}
private Spell getSpellForButton(GuiButtonSpell button){
return matchingSpells.get(currentPage * SPELL_BUTTON_COUNT + button.index);
}
@Override
public void initGui(){
super.initGui();
final int left = this.width / 2 - this.xSize / 2;
final int top = this.height / 2 - this.ySize / 2;
int buttonID = 0;
// Page buttons
this.buttonList.add(nextPageButton = new GuiButtonTurnPage(buttonID++, left + xSize - PAGE_BUTTON_INSET_X - GuiButtonTurnPage.WIDTH,
top + ySize - PAGE_BUTTON_INSET_Y - GuiButtonTurnPage.HEIGHT, Type.NEXT_PAGE, TEXTURE, textureWidth, textureHeight));
this.buttonList.add(prevPageButton = new GuiButtonTurnPage(buttonID++, left + PAGE_BUTTON_INSET_X,
top + ySize - PAGE_BUTTON_INSET_Y - GuiButtonTurnPage.HEIGHT, Type.PREVIOUS_PAGE, TEXTURE, textureWidth, textureHeight));
this.buttonList.add(lastPageButton = new GuiButtonTurnPage(buttonID++, left + xSize - PAGE_BUTTON_INSET_X - GuiButtonTurnPage.WIDTH - PAGE_BUTTON_SPACING,
top + ySize - PAGE_BUTTON_INSET_Y - GuiButtonTurnPage.HEIGHT, Type.NEXT_SECTION, TEXTURE, textureWidth, textureHeight));
this.buttonList.add(firstPageButton = new GuiButtonTurnPage(buttonID++, left + PAGE_BUTTON_INSET_X + PAGE_BUTTON_SPACING,
top + ySize - PAGE_BUTTON_INSET_Y - GuiButtonTurnPage.HEIGHT, Type.PREVIOUS_SECTION, TEXTURE, textureWidth, textureHeight));
this.buttonList.add(indexButton = new GuiButtonTurnPage(buttonID++, left + xSize/2 - 23,
top + ySize - PAGE_BUTTON_INSET_Y - GuiButtonTurnPage.HEIGHT, Type.CONTENTS, TEXTURE, textureWidth, textureHeight));
this.buttonList.add(locateButton = new GuiButtonLocateBook(buttonID++, left + xSize/2 - 34,
top + ySize - PAGE_BUTTON_INSET_Y - GuiButtonTurnPage.HEIGHT));
// Sort buttons
for(SortType sortType : SortType.values()){
this.buttonList.add(sortButtons[sortType.ordinal()] = new GuiButtonSpellSort(buttonID++,
left + SORT_BUTTON_INSET_X + SORT_BUTTON_SPACING * sortType.ordinal(),
top + SORT_BUTTON_INSET_Y, sortType, this, this));
}
// Spell buttons
for(int i = 0; i < SPELL_BUTTON_COUNT; i++){
int row = i % SPELL_COLUMNS;
int column = (i / SPELL_COLUMNS) % SPELL_ROWS;
int x = i < SPELL_BUTTON_COUNT/2 ? SPELL_BUTTON_INSET_X + row * SPELL_BUTTON_SPACING
: xSize - SPELL_BUTTON_INSET_X - GuiButtonSpell.WIDTH - (2-row) * SPELL_BUTTON_SPACING;
int y = SPELL_BUTTON_INSET_Y + column * SPELL_BUTTON_SPACING;
this.buttonList.add(spellButtons[i] = new GuiButtonSpell(buttonID++, left + x, top + y, i));
}
this.searchField = new GuiTextField(0, this.fontRenderer, left + 157, top + 21, 106, this.fontRenderer.FONT_HEIGHT);
this.searchField.setMaxStringLength(50);
this.searchField.setEnableBackgroundDrawing(false);
this.searchField.setVisible(true);
this.searchField.setTextColor(16777215);
this.searchField.setCanLoseFocus(false);
this.searchField.setFocused(true);
refreshAvailableSpells(); // Must be done last
}
@Override
public void updateScreen(){
super.updateScreen();
if(searchBarHoverTime > 0 && searchBarHoverTime < SEARCH_TOOLTIP_HOVER_TIME) searchBarHoverTime++;
}
@Override
public void onGuiClosed(){
WizardryPacketHandler.net.sendToServer(new PacketLectern.Message(lectern.getPos(), currentSpell));
super.onGuiClosed();
}
@Override
protected void drawBackgroundLayer(int left, int top, int mouseX, int mouseY){
if(currentSpell == Spells.none){
drawIndexPage(left, top);
}else{
super.drawBackgroundLayer(left, top, mouseX, mouseY);
}
}
@Override
protected void drawForegroundLayer(int left, int top, int mouseX, int mouseY){
if(currentSpell == Spells.none){
this.fontRenderer.drawString(I18n.format("gui." + Wizardry.MODID + ":lectern.title"),
left + 20, top + SORT_BUTTON_INSET_Y, 0);
}else{
super.drawForegroundLayer(left, top, mouseX, mouseY);
}
this.buttonList.forEach(b -> b.drawButtonForegroundLayer(mouseX, mouseY));
// Search tooltip
if(DrawingUtils.isPointInRegion(searchField.x, searchField.y, searchField.width, searchField.height, mouseX, mouseY)){
if(searchBarHoverTime == 0){
searchBarHoverTime++;
}else if(searchBarHoverTime == SEARCH_TOOLTIP_HOVER_TIME){
drawHoveringText(I18n.format("container." + Wizardry.MODID + ":arcane_workbench.search_tooltip",
TOOLTIP_SYNTAX.getFormattingCode(), TOOLTIP_BODY.getFormattingCode()), mouseX, mouseY);
}
}else{
searchBarHoverTime = 0;
}
}
private void drawIndexPage(int left, int top){
for(int i = 0; i < SPELL_BUTTON_COUNT; i++){
int index = currentPage * SPELL_BUTTON_COUNT + i;
Spell spell = index < matchingSpells.size() ? matchingSpells.get(index) : Spells.none;
boolean discovered = Wizardry.proxy.shouldDisplayDiscovered(spell, null);
Minecraft.getMinecraft().renderEngine.bindTexture(discovered ? spell.getIcon() : Spells.none.getIcon());
int row = i % SPELL_COLUMNS;
int column = (i / SPELL_COLUMNS) % SPELL_ROWS;
int x = i < SPELL_BUTTON_COUNT/2 ? SPELL_BUTTON_INSET_X + row * SPELL_BUTTON_SPACING
: xSize - SPELL_BUTTON_INSET_X - GuiButtonSpell.WIDTH - (2-row) * SPELL_BUTTON_SPACING;
int y = SPELL_BUTTON_INSET_Y + column * SPELL_BUTTON_SPACING;
DrawingUtils.drawTexturedRect(left + x + 1, top + y + 1, 0, 0, 32, 32, 32, 32);
}
mc.renderEngine.bindTexture(getTexture());
DrawingUtils.drawTexturedRect(left, top, 0, 256, xSize, ySize, textureWidth, textureHeight);
this.searchField.drawTextBox();
GlStateManager.color(1, 1, 1, 1);
mc.renderEngine.bindTexture(getTexture());
}
@Override
protected void mouseClicked(int mouseX, int mouseY, int mouseButton) throws IOException {
super.mouseClicked(mouseX, mouseY, mouseButton);
searchNeedsClearing = true;
}
@Override
protected void actionPerformed(GuiButton button){
int lastPage = getPageCount() - 1;
if(button == indexButton){
currentSpell = Spells.none;
// currentPage = 0;
}else if(button == locateButton){
// Close the GUI
this.mc.player.connection.sendPacket(new CPacketCloseWindow(this.mc.player.openContainer.windowId));
this.mc.displayGuiScreen(null);
// Find the location(s) of the current spell's book(s) and highlight them
for(IInventory bookshelf : BlockBookshelf.findNearbyBookshelves(lectern.getWorld(), lectern.getPos())){
for(int i = 0; i < bookshelf.getSizeInventory(); i++){
ItemStack stack = bookshelf.getStackInSlot(i);
if(stack.getItem() instanceof ItemSpellBook){
Spell spell = Spell.byMetadata(stack.getMetadata());
if(spell == this.currentSpell){
BlockPos pos = (((TileEntity)bookshelf).getPos());
for(EnumFacing side : EnumFacing.VALUES){
ParticleBuilder.create(ParticleBuilder.Type.BLOCK_HIGHLIGHT).pos(
GeometryUtils.getFaceCentre(pos, side)
.add(new Vec3d(side.getDirectionVec())
.scale(GeometryUtils.ANTI_Z_FIGHTING_OFFSET)))
.face(side).clr(0.9f, 0.5f, 0.8f).fade(0.7f, 0, 1).spawn(mc.world);
}
mc.world.playSound(pos, WizardrySounds.BLOCK_LECTERN_LOCATE_SPELL, SoundCategory.BLOCKS, 1, 0.7f, false);
break; // This bookshelf has the spell, skip to the next bookshelf
}
}
}
}
}else if(button == nextPageButton){
if(currentPage < lastPage) currentPage++;
}else if(button == prevPageButton){
if(currentPage > 0) currentPage--;
}else if(button == lastPageButton){
currentPage = lastPage;
}else if(button == firstPageButton){
currentPage = 0;
}else if(button instanceof GuiButtonSpell){
currentSpell = getSpellForButton((GuiButtonSpell)button);
}else if(button instanceof GuiButtonSpellSort){
SortType sortType = (((GuiButtonSpellSort)button).sortType);
if(this.sortType == sortType){
this.sortDescending = !this.sortDescending;
}else{
this.sortType = sortType;
this.sortDescending = false;
}
updateMatchingSpells();
}
updateButtonVisiblity();
}
@Override
protected void keyTyped(char typedChar, int keyCode) throws IOException {
if(this.searchNeedsClearing){
this.searchNeedsClearing = false;
this.searchField.setText("");
this.currentPage = 0;
}
if(this.searchField.textboxKeyTyped(typedChar, keyCode)){
this.currentPage = 0;
updateMatchingSpells();
updateButtonVisiblity();
}else{
super.keyTyped(typedChar, keyCode);
}
}
private void updateButtonVisiblity(){
if(currentSpell == Spells.none){
this.searchField.setVisible(true);
int lastPage = getPageCount() - 1;
prevPageButton.visible = currentPage > 0;
firstPageButton.visible = currentPage > 0;
nextPageButton.visible = currentPage < lastPage;
lastPageButton.visible = currentPage < lastPage;
indexButton.visible = false;
locateButton.visible = false;
for(GuiButton button : sortButtons) button.visible = true;
for(GuiButtonSpell button : spellButtons){
button.visible = currentPage * SPELL_BUTTON_COUNT + button.index < matchingSpells.size();
}
}else{
this.searchField.setVisible(false);
this.buttonList.forEach(b -> b.visible = false); // Hide all buttons...
indexButton.visible = true; // ... except the index button and locate button
locateButton.visible = true;
}
}
private void updateMatchingSpells(){
matchingSpells = availableSpells.stream()
.filter(s -> s.matches(searchField.getText().toLowerCase(Locale.ROOT)))
.sorted(sortDescending ? sortType.comparator.reversed() : sortType.comparator)
.collect(Collectors.toList());
}
// TODO: Config option to always display all spells when in creative
/** Called on initialisation and whenever a bookshelf is added or removed, to update the list of spells. */
public void refreshAvailableSpells(){
availableSpells.clear();
// No need to exclude the lectern TE because it's not an IInventory!
for(IInventory bookshelf : BlockBookshelf.findNearbyBookshelves(lectern.getWorld(), lectern.getPos())){
for(int i=0; i<bookshelf.getSizeInventory(); i++){
ItemStack stack = bookshelf.getStackInSlot(i);
if(stack.getItem() instanceof ItemSpellBook){
Spell spell = Spell.byMetadata(stack.getMetadata());
if(spell != Spells.none && !availableSpells.contains(spell)) availableSpells.add(spell);
}
}
}
if(!availableSpells.contains(currentSpell)) currentSpell = Spells.none;
updateMatchingSpells();
updateButtonVisiblity();
}
private class GuiButtonLocateBook extends GuiButton {
public GuiButtonLocateBook(int id, int x, int y){
super(id, x, y, 12, 12, "");
}
@Override
public void drawButton(Minecraft minecraft, int mouseX, int mouseY, float partialTicks){
if(this.visible){
boolean flag = mouseX >= this.x && mouseY >= this.y && mouseX < this.x + this.width && mouseY < this.y + this.height;
GlStateManager.color(1, 1, 1, 1);
minecraft.getTextureManager().bindTexture(TEXTURE);
DrawingUtils.drawTexturedRect(this.x, this.y, flag ? width : 0, 184, width, height, textureWidth, textureHeight);
}
}
}
private class GuiButtonSpell extends GuiButtonInvisible {
private static final int WIDTH = 34, HEIGHT = 34;
private final int index;
public GuiButtonSpell(int id, int x, int y, int index){
super(id, x, y, WIDTH, HEIGHT);
this.index = index;
}
@Override
public void playPressSound(SoundHandler soundHandler){
soundHandler.playSound(PositionedSoundRecord.getMasterRecord(WizardrySounds.MISC_PAGE_TURN, 1));
}
@Override
public void drawButton(Minecraft minecraft, int mouseX, int mouseY, float partialTicks){
if(visible){
super.drawButton(minecraft, mouseX, mouseY, partialTicks);
if(hovered){
mc.renderEngine.bindTexture(getTexture());
DrawingUtils.drawTexturedRect(x, y, 40, 180, width, height, textureWidth, textureHeight);
}
}
}
@Override
public void drawButtonForegroundLayer(int mouseX, int mouseY){
if(visible && hovered){
Spell spell = getSpellForButton(this);
if(Wizardry.proxy.shouldDisplayDiscovered(spell, null)){
drawHoveringText(Collections.singletonList(spell.getDisplayName()), mouseX, mouseY, fontRenderer);
}else{
drawHoveringText(Collections.singletonList(SpellGlyphData.getGlyphName(spell, mc.world)),
mouseX, mouseY, mc.standardGalacticFontRenderer);
}
}
}
}
}
@@ -1,116 +1,35 @@
package electroblob.wizardry.client.gui;
import electroblob.wizardry.Wizardry;
import electroblob.wizardry.client.DrawingUtils;
import electroblob.wizardry.constants.Tier;
import electroblob.wizardry.data.SpellGlyphData;
import electroblob.wizardry.data.WizardData;
import electroblob.wizardry.item.ItemSpellBook;
import electroblob.wizardry.registry.Spells;
import electroblob.wizardry.registry.WizardrySounds;
import electroblob.wizardry.spell.Spell;
import net.minecraft.client.Minecraft;
import net.minecraft.client.audio.PositionedSoundRecord;
import net.minecraft.client.gui.GuiScreen;
import net.minecraft.client.renderer.GlStateManager;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.item.ItemStack;
import org.lwjgl.input.Keyboard;
import net.minecraft.util.ResourceLocation;
public class GuiSpellBook extends GuiScreen {
public class GuiSpellBook extends GuiSpellInfo {
private int xSize, ySize;
private ItemSpellBook book;
private Spell spell;
private final ItemSpellBook book;
private final Spell spell;
public GuiSpellBook(ItemStack stack){
super();
xSize = 288;
ySize = 180;
if(!(stack.getItem() instanceof ItemSpellBook)) throw new ClassCastException("Cannot create spell book GUI for item that does not extend ItemSpellBook!");
super(288, 180);
if(!(stack.getItem() instanceof ItemSpellBook)){
throw new ClassCastException("Cannot create spell book GUI for item that does not extend ItemSpellBook!");
}
this.book = (ItemSpellBook)stack.getItem();
this.spell = Spell.byMetadata(stack.getItemDamage());
}
/**
* Draws the screen and all the components in it.
*/
public void drawScreen(int par1, int par2, float par3){
int xPos = this.width / 2 - xSize / 2;
int yPos = this.height / 2 - this.ySize / 2;
EntityPlayer player = Minecraft.getMinecraft().player;
boolean discovered = true;
if(Wizardry.settings.discoveryMode && !player.isCreative() && WizardData.get(player) != null
&& !WizardData.get(player).hasSpellBeenDiscovered(spell)){
discovered = false;
}
GlStateManager.color(1, 1, 1, 1); // Just in case
// 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());
DrawingUtils.drawTexturedRect(xPos + 146, yPos + 20, 0, 0, 128, 128, 128, 128);
Minecraft.getMinecraft().renderEngine.bindTexture(book.getGuiTexture(spell));
DrawingUtils.drawTexturedRect(xPos, yPos, 0, 0, xSize, ySize, xSize, 256);
super.drawScreen(par1, par2, par3);
if(discovered){
this.fontRenderer.drawString(spell.getDisplayName(), xPos + 17, yPos + 15, 0);
this.fontRenderer.drawString(spell.getType().getDisplayName(), xPos + 17, yPos + 26, 0x777777);
}else{
this.mc.standardGalacticFontRenderer.drawString(SpellGlyphData.getGlyphName(spell, player.world), xPos + 17,
yPos + 15, 0);
this.mc.standardGalacticFontRenderer.drawString(spell.getType().getDisplayName(), xPos + 17, yPos + 26,
0x777777);
}
//this.fontRenderer.drawString("-------------------", xPos + 17, yPos + 35, 0);
if(spell.getTier() == Tier.NOVICE){
// Novice is usually white but this doesn't show up.
this.fontRenderer.drawString("Tier: \u00A77" + Tier.NOVICE.getDisplayName(), xPos + 17, yPos + 45, 0);
}else{
this.fontRenderer.drawString("Tier: " + spell.getTier().getDisplayNameWithFormatting(), xPos + 17, yPos + 45, 0);
}
String element = "Element: " + spell.getElement().getFormattingCode() + spell.getElement().getDisplayName();
if(!discovered) element = "Element: ?";
this.fontRenderer.drawString(element, xPos + 17, yPos + 57, 0);
String manaCost = "Mana Cost: " + spell.getCost();
if(spell.isContinuous) manaCost = "Mana Cost: " + spell.getCost() + "/second";
if(!discovered) manaCost = "Mana Cost: ?";
this.fontRenderer.drawString(manaCost, xPos + 17, yPos + 69, 0);
if(discovered){
this.fontRenderer.drawSplitString(spell.getDescription(), xPos + 17, yPos + 83, 118, 0);
}else{
this.mc.standardGalacticFontRenderer.drawSplitString(
SpellGlyphData.getGlyphDescription(spell, player.world), xPos + 17, yPos + 83, 118, 0);
}
}
public void initGui(){
super.initGui();
Keyboard.enableRepeatEvents(true);
this.buttonList.clear();
this.mc.getSoundHandler().playSound(PositionedSoundRecord.getMasterRecord(WizardrySounds.MISC_BOOK_OPEN, 1));
}
public void onGuiClosed(){
super.onGuiClosed();
Keyboard.enableRepeatEvents(false);
this.spell = (Spell.byMetadata(stack.getItemDamage()));
}
@Override
public boolean doesGuiPauseGame(){
return Wizardry.settings.booksPauseGame;
public Spell getSpell(){
return spell;
}
@Override
public ResourceLocation getTexture(){
return book.getGuiTexture(spell);
}
}
@@ -14,7 +14,7 @@ import electroblob.wizardry.item.ISpellCastingItem;
import electroblob.wizardry.registry.Spells;
import electroblob.wizardry.registry.WizardryPotions;
import electroblob.wizardry.spell.Spell;
import electroblob.wizardry.util.WandHelper;
import electroblob.wizardry.util.SpellModifiers;
import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.FontRenderer;
import net.minecraft.client.renderer.GlStateManager;
@@ -34,9 +34,11 @@ import net.minecraftforge.fml.common.Mod;
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
import net.minecraftforge.fml.relauncher.Side;
import javax.annotation.Nullable;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.nio.charset.StandardCharsets;
import java.util.*;
import java.util.Map.Entry;
@@ -44,7 +46,8 @@ import java.util.Map.Entry;
public class GuiSpellDisplay {
private static final ResourceLocation INDEX = new ResourceLocation(Wizardry.MODID, "textures/gui/spell_hud/_index.json");
private static final ResourceLocation CHARGE_METER = new ResourceLocation(Wizardry.MODID, "textures/gui/spell_charge_meter.png");
/** A map which stores all loaded HUD skin objects. This gets wiped on resource pack reload and repopulated with
* mappings as specified by {@code _index.json} (these stack between resource packs). The keys in the map correspond
* to the keys in {@code _index.json}, and are sorted in that order, with skins belonging to resource packs sorted
@@ -53,6 +56,12 @@ public class GuiSpellDisplay {
private static final Map<String, Skin> skins = new LinkedHashMap<>(14); // 14 is the number of skins packaged with the mod
private static final Gson gson = new Gson();
private static final Random random = new Random();
/** Width of the charge meter. */
private static final int CHARGE_METER_WIDTH = 25;
/** Height of the charge meter. */
private static final int CHARGE_METER_HEIGHT = 9;
/** Width and height of the spell icon (very unlikely to change!) */
private static final int SPELL_ICON_SIZE = 32;
@@ -88,19 +97,38 @@ public class GuiSpellDisplay {
return Collections.unmodifiableMap(skins);
}
/** Returns the skin that corresponds to the given key. */
/** Returns the skin that corresponds to the given key. If no such skin exists, returns the default skin as a
* fallback. If the fallback skin is missing, prints a warning to the console and returns null. */
@Nullable
public static Skin getSkin(String key){
return skins.get(key);
Skin skin = skins.get(key);
if(skin == null){
Wizardry.logger.info("The spell HUD skin '" + Wizardry.settings.spellHUDSkin + "' specified in the config"
+ " did not match any of the loaded skins; using the default skin as a fallback.");
skin = skins.get(Settings.DEFAULT_HUD_SKIN_KEY);
if(skin == null){
Wizardry.logger.warn("The default spell HUD skin is missing! A resource pack must have overridden it"
+ " with an invalid JSON file (default.json), please try again without any resource packs.");
return null;
}
}
return skin;
}
// Normally when extending Gui, you'd have to have an instance to access its methods. However, we're not actually
// using any of them, so this class may as well not bother and just be a static event handler. Neat!
@SubscribeEvent
public static void draw(RenderGameOverlayEvent event){
Minecraft mc = Minecraft.getMinecraft();
public static void draw(RenderGameOverlayEvent.Post event){
EntityPlayer player = mc.player;
if(!Wizardry.settings.showSpellHUD && !Wizardry.settings.showChargeMeter) return; // Optimisation
EntityPlayer player = Minecraft.getMinecraft().player;
if(player.isSpectator()) return; // Spectators shouldn't have the spell HUD!
@@ -113,12 +141,89 @@ public class GuiSpellDisplay {
wand = player.getHeldItemOffhand();
mainHand = false;
// If the player isn't holding a spellcasting item that shows the HUD, then nothing else needs to be done.
if(!(wand.getItem() instanceof ISpellCastingItem && ((ISpellCastingItem)wand.getItem()).showSpellHUD(player, wand))) return;
if(!(wand.getItem() instanceof ISpellCastingItem && ((ISpellCastingItem)wand.getItem()).showSpellHUD(player, wand)))
return;
}
int width = event.getResolution().getScaledWidth();
int height = event.getResolution().getScaledHeight();
switch(event.getType()){
case CROSSHAIRS:
renderChargeMeter(player, wand, width, height, event.getPartialTicks());
break;
case HOTBAR:
renderSpellHUD(player, wand, mainHand, width, height, event.getPartialTicks(), false);
break;
case TEXT:
renderSpellHUD(player, wand, mainHand, width, height, event.getPartialTicks(), true);
break;
}
}
/**
* Renders the spell charge meter around the crosshairs.
* @param player A reference to the client player
* @param wand The wand the HUD is for
* @param width The width of the screen
* @param height The height of the screen
* @param partialTicks The current partial tick time
*/
private static void renderChargeMeter(EntityPlayer player, ItemStack wand, int width, int height, float partialTicks){
if(!Wizardry.settings.showChargeMeter) return;
if(Minecraft.getMinecraft().gameSettings.showDebugInfo) return; // Don't show charge meter in the debug screen
if(Minecraft.getMinecraft().gameSettings.thirdPersonView != 0) return; // Don't show in third person
if(wand != player.getActiveItemStack()) return; // Don't show when using the other held item
if(!(wand.getItem() instanceof ISpellCastingItem)) throw new IllegalArgumentException("The given stack must contain an ISpellCastingItem!");
Spell spell = ((ISpellCastingItem)wand.getItem()).getCurrentSpell(wand);
int chargeup = spell.getChargeup();
if(WizardData.get(player) != null){
// Pretty sure this is accessible client-side since it's only assigned from common code
chargeup = (int)(chargeup * WizardData.get(player).itemCastingModifiers.get(SpellModifiers.CHARGEUP));
}
if(chargeup <= 0) return;
// WHY WHY WHY are these methods named so misleadingly?! Sort yourselves out MCP!
// (getItemInUseCount returns the max count MINUS the use count, and getItemInUseMaxCount returns the use count)
if(player.getItemInUseMaxCount() == 0) return; // Not charging
float charge = (player.getItemInUseMaxCount() + partialTicks) / chargeup;
if(charge > 1) return; // Done charging
Minecraft.getMinecraft().renderEngine.bindTexture(CHARGE_METER);
int x1 = width/2 - CHARGE_METER_WIDTH/2;
int y = height/2 - CHARGE_METER_HEIGHT/2;
int w = (int)(CHARGE_METER_WIDTH/2 * charge);
int u = CHARGE_METER_WIDTH - w;
DrawingUtils.drawTexturedRect(x1, y, 0, 0, w, CHARGE_METER_HEIGHT, 32, 32);
DrawingUtils.drawTexturedRect(x1 + u, y, u, 0, w, CHARGE_METER_HEIGHT, 32, 32);
}
/**
* Renders the main spell HUD in the corner of the screen.
* @param player A reference to the client player
* @param wand The wand the HUD is for
* @param mainHand True if the wand is in the player's main hand, false if it is in their offhand
* @param width The width of the screen
* @param height The height of the screen
* @param partialTicks The current partial tick time
* @param textLayer True to render the text layer, false to render the background (hotbar layer)
*/
private static void renderSpellHUD(EntityPlayer player, ItemStack wand, boolean mainHand, int width, int height, float partialTicks, boolean textLayer){
if(!Wizardry.settings.showSpellHUD) return;
if(!(wand.getItem() instanceof ISpellCastingItem)) throw new IllegalArgumentException("The given stack must contain an ISpellCastingItem!");
boolean flipX = Wizardry.settings.spellHUDPosition.flipX;
boolean flipY = Wizardry.settings.spellHUDPosition.flipY;
@@ -126,25 +231,13 @@ public class GuiSpellDisplay {
// ............. | This bit is true if the wand is on the left, false if it is on the right
flipX = flipX == ((mainHand ? player.getPrimaryHand() : player.getPrimaryHand().opposite()) == EnumHandSide.LEFT);
}
Skin skin = skins.get(Wizardry.settings.spellHUDSkin);
if(skin == null){
Wizardry.logger.info("The spell HUD skin '" + Wizardry.settings.spellHUDSkin + "' specified in the config"
+ " did not match any of the loaded skins; using the default skin as a fallback.");
skin = skins.get(Settings.DEFAULT_HUD_SKIN_KEY);
if(skin == null){
Wizardry.logger.warn("The default spell HUD skin is missing! A resource pack must have overridden it"
+ " with an invalid JSON file (default.json), please try again without any resource packs.");
return;
}
}
Skin skin = getSkin(Wizardry.settings.spellHUDSkin);
if(skin == null) return; // Can't draw anything if there is no skin loaded!
GlStateManager.pushMatrix();
// 'Origin' of the spell hud (bottom left corner of the actual texture, always in the corner of the screen)
int x = flipX ? width : 0;
int y = flipY ? 0: height;
@@ -165,45 +258,45 @@ public class GuiSpellDisplay {
y = MathHelper.ceil(y/scale);
}
Spell spell = WandHelper.getCurrentSpell(wand);
int cooldown = WandHelper.getCurrentCooldown(wand);
int maxCooldown = WandHelper.getCurrentMaxCooldown(wand);
Spell spell = ((ISpellCastingItem)wand.getItem()).getCurrentSpell(wand);
int cooldown = ((ISpellCastingItem)wand.getItem()).getCurrentCooldown(wand);
int maxCooldown = ((ISpellCastingItem)wand.getItem()).getCurrentMaxCooldown(wand);
if(textLayer){
if(event.getType() == RenderGameOverlayEvent.ElementType.TEXT){
float animationProgress = Math.signum(switchTimer) * ((SPELL_SWITCH_TIME - Math.abs(switchTimer) +
event.getPartialTicks()) / SPELL_SWITCH_TIME);
String prevSpellName = getFormattedSpellName(WandHelper.getPreviousSpell(wand), player, WandHelper.getPreviousCooldown(wand));
partialTicks) / SPELL_SWITCH_TIME);
String prevSpellName = getFormattedSpellName(((ISpellCastingItem)wand.getItem()).getPreviousSpell(wand), player, 0);
String spellName = getFormattedSpellName(spell, player, cooldown);
String nextSpellName = getFormattedSpellName(WandHelper.getNextSpell(wand), player, WandHelper.getNextCooldown(wand));
String nextSpellName = getFormattedSpellName(((ISpellCastingItem)wand.getItem()).getNextSpell(wand), player, 0);
skin.drawText(x, y, flipX, flipY, prevSpellName, spellName, nextSpellName, animationProgress);
}else if(event.getType() == RenderGameOverlayEvent.ElementType.HOTBAR){
}else{
boolean discovered = true;
if(!player.isCreative() && WizardData.get(player) != null){
discovered = WizardData.get(player).hasSpellBeenDiscovered(spell);
}
ResourceLocation icon = discovered ? spell.getIcon() : Spells.none.getIcon();
float progress = 1;
// Doesn't really matter what progress is when in creative, but we might as well avoid the calculation.
if(!player.isCreative() && !spell.isContinuous){
if(!player.isCreative()){
// Subtracted partial tick time to make it smoother
progress = maxCooldown == 0 ? 1 : (maxCooldown - (float)cooldown + event.getPartialTicks()) / maxCooldown;
progress = maxCooldown == 0 ? 1 : (maxCooldown - (float)cooldown + partialTicks) / maxCooldown;
}
skin.drawBackground(x, y, flipX, flipY, icon, progress, player.isCreative());
skin.drawBackground(x, y, flipX, flipY, icon, progress, player.isCreative(), player.isPotionActive(WizardryPotions.arcane_jammer));
}
GlStateManager.popMatrix();
}
/**
* Gets the name of the given spell, with formatting added according to its cooldown and whether the given player
* has discovered it.
@@ -220,9 +313,11 @@ public class GuiSpellDisplay {
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 format = cooldown > 0 || player.isPotionActive(WizardryPotions.arcane_jammer) ? "\u00A78" : spell.getElement().getFormattingCode();
// Makes spells greyed out if they are in cooldown
String format = cooldown > 0 ? "\u00A78" : spell.getElement().getFormattingCode();
if(!discovered) format = "\u00A79";
// Obfuscates the spell name if the player has the arcane jammer effect
if(player.isPotionActive(WizardryPotions.arcane_jammer)) format = format + "\u00A7k";
String name = discovered ? spell.getDisplayName() : SpellGlyphData.getGlyphName(spell, player.world);
name = format + name;
@@ -251,7 +346,7 @@ public class GuiSpellDisplay {
for(IResource indexFile : indexFiles){
BufferedReader reader = new BufferedReader(new InputStreamReader(indexFile.getInputStream()));
BufferedReader reader = new BufferedReader(new InputStreamReader(indexFile.getInputStream(), StandardCharsets.UTF_8));
JsonElement je = gson.fromJson(reader, JsonElement.class);
JsonObject json = je.getAsJsonObject();
@@ -361,7 +456,7 @@ public class GuiSpellDisplay {
try {
// This time we only want the highest priority file
IResource metadataFile = Minecraft.getMinecraft().getResourceManager().getResource(metadata);
BufferedReader reader = new BufferedReader(new InputStreamReader(metadataFile.getInputStream()));
BufferedReader reader = new BufferedReader(new InputStreamReader(metadataFile.getInputStream(), StandardCharsets.UTF_8));
JsonElement je = gson.fromJson(reader, JsonElement.class);
@@ -454,8 +549,9 @@ public class GuiSpellDisplay {
* @param icon A {@code ResourceLocation} corresponding to the icon of the selected spell.
* @param cooldownBarProgress The fraction of the cooldown bar to draw; must be between 0 and 1 (inclusive).
* @param creativeMode True to draw the creative mode HUD, false for the survival mode version.
* @param jammed True to show the 'glitch' effect (user for arcane jammer), false to draw normally.
*/
public void drawBackground(int x, int y, boolean flipX, boolean flipY, ResourceLocation icon, float cooldownBarProgress, boolean creativeMode){
public void drawBackground(int x, int y, boolean flipX, boolean flipY, ResourceLocation icon, float cooldownBarProgress, boolean creativeMode, boolean jammed){
// Moves the origin if the HUD does not mirror; neatens the rest of the code.
if(flipX && !mirrorX) x -= width;
@@ -472,8 +568,13 @@ public class GuiSpellDisplay {
int x1 = flipX && mirrorX ? x - spellIconInsetX - SPELL_ICON_SIZE : x + spellIconInsetX;
// y is upside-down so this is the other way round
int y1 = flipY && mirrorY ? y + spellIconInsetY : y - spellIconInsetY - SPELL_ICON_SIZE;
DrawingUtils.drawTexturedRect(x1, y1, 0, 0, SPELL_ICON_SIZE, SPELL_ICON_SIZE, SPELL_ICON_SIZE, SPELL_ICON_SIZE);
if(jammed){
random.setSeed(Minecraft.getMinecraft().world.getTotalWorldTime() / 2);
DrawingUtils.drawGlitchRect(random, x1, y1, 0, 0, SPELL_ICON_SIZE, SPELL_ICON_SIZE, SPELL_ICON_SIZE, SPELL_ICON_SIZE, false, false);
}else{
DrawingUtils.drawTexturedRect(x1, y1, 0, 0, SPELL_ICON_SIZE, SPELL_ICON_SIZE, SPELL_ICON_SIZE, SPELL_ICON_SIZE);
}
// Background of spell hud
mc.renderEngine.bindTexture(texture);
@@ -482,7 +583,11 @@ public class GuiSpellDisplay {
y1 = flipY && mirrorY ? y : y - height;
// The 128 here is a uv value, not a dimension, and hence is left as a hardcoded number.
// TODO: Since the HUD is wider than it is tall, perhaps the creative mode texture should be in the bottom half instead of the right half?
DrawingUtils.drawTexturedFlippedRect(x1, y1, creativeMode ? 128 : 0, 0, width, height, 256, 256, flipX && mirrorX, flipY && mirrorY);
if(jammed){
DrawingUtils.drawGlitchRect(random, x1, y1, creativeMode ? 128 : 0, 0, width, height, 256, 256, flipX && mirrorX, flipY && mirrorY);
}else{
DrawingUtils.drawTexturedFlippedRect(x1, y1, creativeMode ? 128 : 0, 0, width, height, 256, 256, flipX && mirrorX, flipY && mirrorY);
}
// Cooldown bar
if(!creativeMode && cooldownBarProgress > 0 && (showCooldownWhenFull || cooldownBarProgress < 1)){
@@ -494,8 +599,12 @@ public class GuiSpellDisplay {
int u = cooldownBarX; // This doesn't change, even when cooldownBarMirrorX is true, because it should
int v = height; // always start with the left-hand in the actual texture file
DrawingUtils.drawTexturedFlippedRect(x1, y1, u, v, l, cooldownBarHeight, 256, 256, flipX && cooldownBarMirrorX, flipY && cooldownBarMirrorY);
if(jammed){
DrawingUtils.drawGlitchRect(random, x1, y1, u, v, l, cooldownBarHeight, 256, 256, flipX && cooldownBarMirrorX, flipY && cooldownBarMirrorY);
}else{
DrawingUtils.drawTexturedFlippedRect(x1, y1, u, v, l, cooldownBarHeight, 256, 256, flipX && cooldownBarMirrorX, flipY && cooldownBarMirrorY);
}
}
GlStateManager.popMatrix();
@@ -0,0 +1,138 @@
package electroblob.wizardry.client.gui;
import electroblob.wizardry.Wizardry;
import electroblob.wizardry.client.DrawingUtils;
import electroblob.wizardry.constants.Tier;
import electroblob.wizardry.data.SpellGlyphData;
import electroblob.wizardry.registry.Spells;
import electroblob.wizardry.registry.WizardrySounds;
import electroblob.wizardry.spell.Spell;
import net.minecraft.client.Minecraft;
import net.minecraft.client.audio.PositionedSoundRecord;
import net.minecraft.client.gui.GuiScreen;
import net.minecraft.client.renderer.GlStateManager;
import net.minecraft.client.resources.I18n;
import net.minecraft.util.ResourceLocation;
import org.lwjgl.input.Keyboard;
/** Abstract base class for both {@link GuiSpellBook} and {@link GuiLectern}. Centralises code common to both
* those classes. */
public abstract class GuiSpellInfo extends GuiScreen {
protected static final String TRANSLATION_KEY_PREFIX = "gui." + Wizardry.MODID + ":spell_book";
protected final int xSize, ySize;
protected int textureWidth = 512;
protected int textureHeight = 256;
public GuiSpellInfo(int xSize, int ySize){
super();
this.xSize = xSize;
this.ySize = ySize;
}
protected void setTextureSize(int width, int height){
this.textureWidth = width;
this.textureHeight = height;
}
/** Returns the spell to be displayed by this GUI. */
public abstract Spell getSpell();
/** Returns the main texture for the background of this GUI. */
public abstract ResourceLocation getTexture();
@Override
public void drawScreen(int mouseX, int mouseY, float partialTicks){
int left = this.width/2 - xSize/2;
int top = this.height/2 - this.ySize/2;
this.drawDefaultBackground();
this.drawBackgroundLayer(left, top, mouseX, mouseY);
super.drawScreen(mouseX, mouseY, partialTicks); // Just draws the buttons
this.drawForegroundLayer(left, top, mouseX, mouseY);
}
/**
* Draws the background of the spell info GUI. This is called before buttons are drawn.
* @param left The x-coordinate of the left-hand edge of the GUI
* @param top The y-coordinate of the top edge of the GUI
* @param mouseX The current x position of the mouse pointer
* @param mouseY The current y position of the mouse pointer
*/
protected void drawBackgroundLayer(int left, int top, int mouseX, int mouseY){
boolean discovered = Wizardry.proxy.shouldDisplayDiscovered(getSpell(), null);
GlStateManager.color(1, 1, 1, 1); // Just in case
// Draws spell illustration on opposite page, underneath the book so it shows through the hole.
Minecraft.getMinecraft().renderEngine.bindTexture(discovered ? getSpell().getIcon() : Spells.none.getIcon());
DrawingUtils.drawTexturedRect(left + 146, top + 20, 0, 0, 128, 128, 128, 128);
Minecraft.getMinecraft().renderEngine.bindTexture(getTexture());
DrawingUtils.drawTexturedRect(left, top, 0, 0, xSize, ySize, textureWidth, textureHeight);
}
/**
* Draws the foreground of the spell info GUI. This is called after buttons are drawn.
* @param left The x-coordinate of the left-hand edge of the GUI
* @param top The y-coordinate of the top edge of the GUI
* @param mouseX The current x position of the mouse pointer
* @param mouseY The current y position of the mouse pointer
*/
protected void drawForegroundLayer(int left, int top, int mouseX, int mouseY){
boolean discovered = Wizardry.proxy.shouldDisplayDiscovered(getSpell(), null);
if(discovered){
this.fontRenderer.drawString(getSpell().getDisplayName(), left + 17, top + 15, 0);
this.fontRenderer.drawString(getSpell().getType().getDisplayName(), left + 17, top + 26, 0x777777);
}else{
this.mc.standardGalacticFontRenderer.drawString(SpellGlyphData.getGlyphName(getSpell(), mc.world), left + 17,
top + 15, 0);
this.mc.standardGalacticFontRenderer.drawString(getSpell().getType().getDisplayName(), left + 17, top + 26,
0x777777);
}
// Novice is usually white but this doesn't show up
String tier = I18n.format(TRANSLATION_KEY_PREFIX + ".tier", getSpell().getTier() == Tier.NOVICE ?
"\u00A77" + getSpell().getTier().getDisplayName() : getSpell().getTier().getDisplayNameWithFormatting());
this.fontRenderer.drawString(tier, left + 17, top + 45, 0);
String element = I18n.format(TRANSLATION_KEY_PREFIX + ".element", getSpell().getElement().getFormattingCode() + getSpell().getElement().getDisplayName());
if(!discovered) element = I18n.format(TRANSLATION_KEY_PREFIX + ".element_undiscovered");
this.fontRenderer.drawString(element, left + 17, top + 57, 0);
String manaCost = I18n.format(TRANSLATION_KEY_PREFIX + ".mana_cost", getSpell().getCost());
if(getSpell().isContinuous) manaCost = I18n.format(TRANSLATION_KEY_PREFIX + ".mana_cost_continuous", getSpell().getCost());
if(!discovered) manaCost = I18n.format(TRANSLATION_KEY_PREFIX + ".mana_cost_undiscovered");
this.fontRenderer.drawString(manaCost, left + 17, top + 69, 0);
if(discovered){
this.fontRenderer.drawSplitString(getSpell().getDescription(), left + 17, top + 83, 118, 0);
}else{
this.mc.standardGalacticFontRenderer.drawSplitString(
SpellGlyphData.getGlyphDescription(getSpell(), mc.world), left + 17, top + 83, 118, 0);
}
}
@Override
public void initGui(){
super.initGui();
Keyboard.enableRepeatEvents(true);
this.buttonList.clear();
this.mc.getSoundHandler().playSound(PositionedSoundRecord.getMasterRecord(WizardrySounds.MISC_BOOK_OPEN, 1));
}
@Override
public void onGuiClosed(){
super.onGuiClosed();
Keyboard.enableRepeatEvents(false);
}
@Override
public boolean doesGuiPauseGame(){
return Wizardry.settings.booksPauseGame;
}
}
@@ -0,0 +1,132 @@
package electroblob.wizardry.client.gui;
import electroblob.wizardry.Wizardry;
import electroblob.wizardry.client.DrawingUtils;
import electroblob.wizardry.data.WizardData;
import electroblob.wizardry.item.ItemSpellBook;
import electroblob.wizardry.registry.WizardryItems;
import electroblob.wizardry.spell.Spell;
import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.GuiMerchant;
import net.minecraft.client.renderer.GlStateManager;
import net.minecraft.client.renderer.RenderHelper;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.inventory.ContainerMerchant;
import net.minecraft.inventory.Slot;
import net.minecraft.util.ResourceLocation;
import net.minecraft.village.MerchantRecipe;
import net.minecraft.village.MerchantRecipeList;
import net.minecraftforge.client.event.GuiContainerEvent;
import net.minecraftforge.client.event.GuiOpenEvent;
import net.minecraftforge.client.event.GuiScreenEvent.ActionPerformedEvent;
import net.minecraftforge.fml.common.Mod.EventBusSubscriber;
import net.minecraftforge.fml.common.eventhandler.EventPriority;
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
import net.minecraftforge.fml.relauncher.Side;
@EventBusSubscriber(Side.CLIENT)
public class WizardTradeTweaksHandler {
private static final ResourceLocation NEW_SPELL_ICON = new ResourceLocation(Wizardry.MODID, "textures/gui/container/new_spell_indicator.png");
private static final int ICON_WIDTH = 8;
private static final int ICON_HEIGHT = 8;
private static final int ANIMATION_FRAMES = 4;
private static final int ANIMATION_FRAME_TIME = 2; // In ticks
private static final int ANIMATION_PERIOD = 40; // In frame durations
private static int tradeIndex; // Mirrors GuiMerchant#selectedMerchantRecipe (don't want to reflect into it every frame)
@SubscribeEvent(priority = EventPriority.LOWEST)
public static void onGuiOpenEvent(GuiOpenEvent event){
if(event.getGui() instanceof GuiMerchant) tradeIndex = 0;
}
@SubscribeEvent
public static void onActionPerformedPostEvent(ActionPerformedEvent.Post event){
if(event.getGui() instanceof GuiMerchant){
MerchantRecipeList recipes = ((GuiMerchant)event.getGui()).getMerchant().getRecipes(Minecraft.getMinecraft().player);
if(recipes == null) return;
if(event.getButton().id == 1){ // Next
tradeIndex = Math.min(tradeIndex + 1, recipes.size());
}else if(event.getButton().id == 2){ // Previous
tradeIndex = Math.max(tradeIndex - 1, 0);
}
}
}
// Brute-force fix for crystals not showing up when a wizard is given a spell book in the trade GUI.
@SubscribeEvent
public static void onGuiDrawForegroundEvent(GuiContainerEvent.DrawForeground event){
if(event.getGuiContainer() instanceof GuiMerchant){
GuiMerchant gui = (GuiMerchant)event.getGuiContainer();
// Note that gui.getMerchant() returns an NpcMerchant, not an EntityWizard.
MerchantRecipeList trades = gui.getMerchant().getRecipes(Minecraft.getMinecraft().player);
if(trades == null) return;
// Using == the specific item rather than instanceof because that's how trades do it.
if(gui.inventorySlots.getSlot(0).getStack().getItem() == WizardryItems.spell_book
|| gui.inventorySlots.getSlot(1).getStack().getItem() == WizardryItems.spell_book){
for(MerchantRecipe trade : trades){
if(trade.getItemToBuy().getItem() == WizardryItems.spell_book && trade.getSecondItemToBuy().isEmpty()){
Slot slot = gui.inventorySlots.getSlot(2);
// It still doesn't look quite right because the slot highlight is behind the item, but it'll do
// until/unless I find a better solution.
DrawingUtils.drawItemAndTooltip(gui, trade.getItemToSell(), slot.xPos, slot.yPos, event.getMouseX(), event.getMouseY(),
gui.getSlotUnderMouse() == slot);
}
}
}
// New spell indicator
if(gui.inventorySlots instanceof ContainerMerchant){
if(tradeIndex < trades.size()){ // Seems to happen with certain mods' GUIs
// Can't use getCurrentRecipe because that only gets updated when the correct items are given
MerchantRecipe recipe = trades.get(tradeIndex);
if(recipe != null && recipe.getItemToSell().getItem() instanceof ItemSpellBook){
EntityPlayer player = Minecraft.getMinecraft().player;
Spell spell = Spell.byMetadata(recipe.getItemToSell().getMetadata());
if(Wizardry.settings.discoveryMode && !player.isCreative() && Wizardry.proxy.shouldDisplayDiscovered(spell, recipe.getItemToSell())
&& WizardData.get(player) != null && !WizardData.get(player).hasSpellBeenDiscovered(spell)){
int x = gui.inventorySlots.getSlot(2).xPos + 14;
int y = gui.inventorySlots.getSlot(2).yPos - 17;
RenderHelper.enableGUIStandardItemLighting();
GlStateManager.color(1, 1, 1);
Minecraft.getMinecraft().renderEngine.bindTexture(NEW_SPELL_ICON);
int frame = Math.max(player.ticksExisted / ANIMATION_FRAME_TIME % ANIMATION_PERIOD - (ANIMATION_PERIOD - ANIMATION_FRAMES), 0);
DrawingUtils.drawTexturedRect(x, y, 0, frame * ICON_HEIGHT, ICON_WIDTH, ICON_HEIGHT, ICON_WIDTH, ICON_HEIGHT * ANIMATION_FRAMES);
RenderHelper.disableStandardItemLighting();
// int mouseX = event.getMouseX() - gui.getGuiLeft();
// int mouseY = event.getMouseY() - gui.getGuiTop();
//
// if(mouseX >= x + 1 && mouseX < x + w + 1 && mouseY >= y - 1 && mouseY < y + h + 1){
// GuiUtils.drawHoveringText(Collections.singletonList("You haven't discovered this spell yet"), mouseX,
// mouseY, gui.width, gui.height, 150, Minecraft.getMinecraft().fontRenderer);
// }
}
}
}
}
}
}
}
@@ -1,5 +1,6 @@
package electroblob.wizardry.client.gui.config;
import com.mojang.realmsclient.gui.ChatFormatting;
import electroblob.wizardry.Settings;
import electroblob.wizardry.Wizardry;
import net.minecraft.client.gui.GuiScreen;
@@ -18,22 +19,29 @@ import java.util.List;
public class GuiConfigWizardry extends GuiConfig {
public GuiConfigWizardry(GuiScreen parent){
super(parent, getConfigEntries(), Wizardry.MODID, false, false,
Wizardry.NAME + " - " + I18n.format("config." + Wizardry.MODID + ".title.general"));
// this.titleLine2 = "File location: " + Wizardry.config.getConfigFile().getAbsolutePath();
if(Wizardry.settings.hasRedundantKeys()){
this.titleLine2 = ChatFormatting.GOLD.toString() + I18n.format("config." + Wizardry.MODID + ".redundant_keys");
}
}
private static List<IConfigElement> getConfigEntries(){
List<IConfigElement> configList = new ArrayList<IConfigElement>(1);
List<IConfigElement> configList = new ArrayList<>(1);
configList.add(new DummyCategoryElement("gameplayConfig", "config." + Wizardry.MODID + ".category." + Settings.GAMEPLAY_CATEGORY, GameplayCategory.class));
configList.add(new DummyCategoryElement("worldgenConfig", "config." + Wizardry.MODID + ".category." + Settings.WORLDGEN_CATEGORY, WorldgenCategory.class));
configList.add(new DummyCategoryElement("commandsConfig", "config." + Wizardry.MODID + ".category." + Settings.COMMANDS_CATEGORY, CommandsCategory.class));
configList.add(new DummyCategoryElement("clientConfig", "config." + Wizardry.MODID + ".category." + Settings.CLIENT_CATEGORY, ClientCategory.class));
configList.add(new DummyCategoryElement("spellsConfig", "config." + Wizardry.MODID + ".category." + Settings.SPELLS_CATEGORY, SpellsCategory.class));
configList.add(new DummyCategoryElement("resistancesConfig", "config." + Wizardry.MODID + ".category." + Settings.RESISTANCES_CATEGORY, ResistancesCategory.class));
configList.add(new DummyCategoryElement("compatibilityConfig", "config." + Wizardry.MODID + ".category." + Settings.COMPATIBILITY_CATEGORY, CompatibilityCategory.class));
configList.add(new DummyCategoryElement("gameplayConfig", "config." + Wizardry.MODID + ".category." + Settings.GAMEPLAY_CATEGORY, GameplayCategory.class));
configList.add(new DummyCategoryElement("difficultyConfig", "config." + Wizardry.MODID + ".category." + Settings.DIFFICULTY_CATEGORY, DifficultyCategory.class));
configList.add(new DummyCategoryElement("worldgenConfig", "config." + Wizardry.MODID + ".category." + Settings.WORLDGEN_CATEGORY, WorldgenCategory.class));
configList.add(new DummyCategoryElement("tweaksConfig", "config." + Wizardry.MODID + ".category." + Settings.TWEAKS_CATEGORY, TweaksCategory.class));
configList.add(new DummyCategoryElement("commandsConfig", "config." + Wizardry.MODID + ".category." + Settings.COMMANDS_CATEGORY, CommandsCategory.class));
configList.add(new DummyCategoryElement("clientConfig", "config." + Wizardry.MODID + ".category." + Settings.CLIENT_CATEGORY, ClientCategory.class));
configList.add(new DummyCategoryElement("spellsConfig", "config." + Wizardry.MODID + ".category." + Settings.SPELLS_CATEGORY, SpellsCategory.class));
configList.add(new DummyCategoryElement("artefactsConfig", "config." + Wizardry.MODID + ".category." + Settings.ARTEFACTS_CATEGORY, ArtefactsCategory.class));
configList.add(new DummyCategoryElement("resistancesConfig", "config." + Wizardry.MODID + ".category." + Settings.RESISTANCES_CATEGORY, ResistancesCategory.class));
configList.add(new DummyCategoryElement("compatibilityConfig", "config." + Wizardry.MODID + ".category." + Settings.COMPATIBILITY_CATEGORY, CompatibilityCategory.class));
configList.addAll(new ConfigElement(Wizardry.settings.getConfigCategory(Configuration.CATEGORY_GENERAL)).getChildElements());
@@ -79,6 +87,16 @@ public class GuiConfigWizardry extends GuiConfig {
@Override protected String getCategory() { return Settings.GAMEPLAY_CATEGORY; }
}
/** Difficulty category of the config gui. */
public static class DifficultyCategory extends CategoryBase {
public DifficultyCategory(GuiConfig owningScreen, GuiConfigEntries owningEntryList, IConfigElement prop){
super(owningScreen, owningEntryList, prop);
}
@Override protected String getCategory() { return Settings.DIFFICULTY_CATEGORY; }
}
/** Worldgen category of the config gui. */
public static class WorldgenCategory extends CategoryBase {
@@ -89,6 +107,16 @@ public class GuiConfigWizardry extends GuiConfig {
@Override protected String getCategory() { return Settings.WORLDGEN_CATEGORY; }
}
/** Tweaks category of the config gui. */
public static class TweaksCategory extends CategoryBase {
public TweaksCategory(GuiConfig owningScreen, GuiConfigEntries owningEntryList, IConfigElement prop){
super(owningScreen, owningEntryList, prop);
}
@Override protected String getCategory() { return Settings.TWEAKS_CATEGORY; }
}
/** Commands category of the config gui. */
public static class CommandsCategory extends CategoryBase {
@@ -119,6 +147,16 @@ public class GuiConfigWizardry extends GuiConfig {
@Override protected String getCategory() { return Settings.SPELLS_CATEGORY; }
}
/** Artefacts category of the config gui. */
public static class ArtefactsCategory extends CategoryBase {
public ArtefactsCategory(GuiConfig owningScreen, GuiConfigEntries owningEntryList, IConfigElement prop){
super(owningScreen, owningEntryList, prop);
}
@Override protected String getCategory() { return Settings.ARTEFACTS_CATEGORY; }
}
/** Resistances category of the config gui. */
public static class ResistancesCategory extends CategoryBase {
@@ -59,31 +59,34 @@ public class GuiSelectHUDSkin extends GuiSelectString {
this.drawGradientRect(previewLeft, previewTop, previewRight, previewBottom, 0x88000000, 0x88000000);
int previewBorder = 10;
Skin skin = GuiSpellDisplay.getSkin((String)this.currentValue);
float scale = Math.min((previewRight - previewLeft - 2*previewBorder)/(float)skin.getWidth(),
(previewBottom - previewTop - 2*previewBorder)/(float)skin.getHeight());
float x = (previewLeft + previewRight)/2 - (skin.getWidth()*scale)/2;
float y = (previewBottom + previewTop)/2 + (skin.getHeight()*scale)/2;
GlStateManager.pushMatrix();
GlStateManager.scale(scale, scale, scale);
skin.drawBackground((int)(x/scale), (int)(y/scale), false, false,
Spells.magic_missile.getIcon(), 0.6f, false);
skin.drawText((int)(x/scale), (int)(y/scale), false, false,
Spells.none.getDisplayNameWithFormatting(),
Spells.magic_missile.getDisplayNameWithFormatting(),
Spells.none.getDisplayNameWithFormatting(), 0);
GlStateManager.popMatrix();
if(skin != null){
float scale = Math.min((previewRight - previewLeft - 2 * previewBorder) / (float)skin.getWidth(),
(previewBottom - previewTop - 2 * previewBorder) / (float)skin.getHeight());
float x = (previewLeft + previewRight) / 2f - (skin.getWidth() * scale) / 2;
float y = (previewBottom + previewTop) / 2f + (skin.getHeight() * scale) / 2;
GlStateManager.pushMatrix();
GlStateManager.scale(scale, scale, scale);
skin.drawBackground((int)(x / scale), (int)(y / scale), false, false,
Spells.magic_missile.getIcon(), 0.6f, false, false);
skin.drawText((int)(x / scale), (int)(y / scale), false, false,
Spells.none.getDisplayNameWithFormatting(),
Spells.magic_missile.getDisplayNameWithFormatting(),
Spells.none.getDisplayNameWithFormatting(), 0);
GlStateManager.popMatrix();
}
Skin hovered = getHoveredSkin(mouseX, mouseY);
if(hovered != null){
this.drawToolTip(Lists.newArrayList("\u00A7a" + hovered.getName(), "\u00A7e" + hovered.getDescription()),
mouseX, mouseY);
@@ -3,7 +3,7 @@ package electroblob.wizardry.client.gui.handbook;
import com.google.gson.JsonObject;
import com.google.gson.JsonSyntaxException;
import electroblob.wizardry.client.DrawingUtils;
import electroblob.wizardry.util.WizardryUtilities;
import electroblob.wizardry.util.JavaUtils;
import net.minecraft.client.gui.FontRenderer;
import net.minecraft.client.gui.GuiButton;
import net.minecraft.util.JsonUtils;
@@ -50,7 +50,7 @@ class Contents {
/** Returns an unmodifiable, flattened collection of all the buttons in this contents. */
Collection<GuiButton> getButtons(){
return WizardryUtilities.flatten(buttons);
return JavaUtils.flatten(buttons);
}
void addEntry(Section section){
@@ -8,7 +8,8 @@ import electroblob.wizardry.Wizardry;
import electroblob.wizardry.client.ClientProxy;
import electroblob.wizardry.client.DrawingUtils;
import electroblob.wizardry.client.gui.GuiButtonInvisible;
import electroblob.wizardry.client.gui.handbook.GuiButtonTurnPage.Type;
import electroblob.wizardry.client.gui.GuiButtonTurnPage;
import electroblob.wizardry.client.gui.GuiButtonTurnPage.Type;
import electroblob.wizardry.constants.Constants;
import electroblob.wizardry.constants.Element;
import electroblob.wizardry.constants.Tier;
@@ -34,6 +35,7 @@ import java.io.IOException;
import java.io.InputStreamReader;
import java.util.List;
import java.util.*;
import java.nio.charset.StandardCharsets;
/**
* GUI class for the wizard's handbook. Like any GUI class, this is instantiated each time the book is opened. As of
@@ -53,7 +55,7 @@ public class GuiWizardHandbook extends GuiScreen {
private static final ResourceLocation DEFAULT = new ResourceLocation(Wizardry.MODID, "texts/handbook_en_us.json");
static final ResourceLocation texture = new ResourceLocation(Wizardry.MODID, "textures/gui/handbook.png");
static final ResourceLocation texture = new ResourceLocation(Wizardry.MODID, "textures/gui/handbook/handbook.png");
/** Global Gson instance for the handbook. */
private static final Gson gson = new Gson();
@@ -243,6 +245,8 @@ public class GuiWizardHandbook extends GuiScreen {
@Override
public void drawScreen(int mouseX, int mouseY, float partialTicks){
this.drawDefaultBackground();
int left = this.width / 2 - GUI_WIDTH / 2;
int top = this.height / 2 - GUI_HEIGHT / 2;
@@ -348,19 +352,19 @@ public class GuiWizardHandbook extends GuiScreen {
this.buttonList.clear();
this.buttonList.add(next = new GuiButtonTurnPage(nextButtonId++, left + GUI_WIDTH - BUTTON_INSET_X - GuiButtonTurnPage.WIDTH,
top + GUI_HEIGHT - BUTTON_INSET_Y - GuiButtonTurnPage.HEIGHT, Type.NEXT_PAGE));
top + GUI_HEIGHT - BUTTON_INSET_Y - GuiButtonTurnPage.HEIGHT, Type.NEXT_PAGE, texture, TEXTURE_WIDTH, TEXTURE_HEIGHT));
this.buttonList.add(previous = new GuiButtonTurnPage(nextButtonId++, left + BUTTON_INSET_X,
top + GUI_HEIGHT - BUTTON_INSET_Y - GuiButtonTurnPage.HEIGHT, Type.PREVIOUS_PAGE));
top + GUI_HEIGHT - BUTTON_INSET_Y - GuiButtonTurnPage.HEIGHT, Type.PREVIOUS_PAGE, texture, TEXTURE_WIDTH, TEXTURE_HEIGHT));
this.buttonList.add(nextSection = new GuiButtonTurnPage(nextButtonId++, left + GUI_WIDTH - BUTTON_INSET_X - GuiButtonTurnPage.WIDTH - BUTTON_SPACING,
top + GUI_HEIGHT - BUTTON_INSET_Y - GuiButtonTurnPage.HEIGHT, Type.NEXT_SECTION));
top + GUI_HEIGHT - BUTTON_INSET_Y - GuiButtonTurnPage.HEIGHT, Type.NEXT_SECTION, texture, TEXTURE_WIDTH, TEXTURE_HEIGHT));
this.buttonList.add(previousSection = new GuiButtonTurnPage(nextButtonId++, left + BUTTON_INSET_X + BUTTON_SPACING,
top + GUI_HEIGHT - BUTTON_INSET_Y - GuiButtonTurnPage.HEIGHT, Type.PREVIOUS_SECTION));
top + GUI_HEIGHT - BUTTON_INSET_Y - GuiButtonTurnPage.HEIGHT, Type.PREVIOUS_SECTION, texture, TEXTURE_WIDTH, TEXTURE_HEIGHT));
this.buttonList.add(menu = new GuiButtonTurnPage(nextButtonId++, left + GUI_WIDTH/2 - 28,
top + GUI_HEIGHT - BUTTON_INSET_Y - GuiButtonTurnPage.HEIGHT, Type.CONTENTS));
top + GUI_HEIGHT - BUTTON_INSET_Y - GuiButtonTurnPage.HEIGHT, Type.CONTENTS, texture, TEXTURE_WIDTH, TEXTURE_HEIGHT));
this.buttonList.add(bookmark = new GuiButtonInvisible(nextButtonId++, left + 130, top + 172, 11, 19) {
@Override
@@ -397,6 +401,11 @@ public class GuiWizardHandbook extends GuiScreen {
*/
public static void loadHandbookFile(IResourceManager manager){
if(manager == null){
Wizardry.logger.error("Tried to reload the handbook file, but received a null resource manager. Aborting!");
return;
}
IResource handbookFile = getHandbookResource(manager);
if(handbookFile != null){
@@ -409,7 +418,7 @@ public class GuiWizardHandbook extends GuiScreen {
bookmarkSection = null; // Also need to wipe the reference to the old bookmarked section
BufferedReader reader = new BufferedReader(new InputStreamReader(handbookFile.getInputStream()));
BufferedReader reader = new BufferedReader(new InputStreamReader(handbookFile.getInputStream(), StandardCharsets.UTF_8));
JsonElement je = gson.fromJson(reader, JsonElement.class);
JsonObject json = je.getAsJsonObject();
@@ -561,4 +570,4 @@ public class GuiWizardHandbook extends GuiScreen {
sections.values().forEach(s -> s.updateUnlockStatus(showToasts, completedAdvancements));
}
}
}
@@ -6,7 +6,7 @@ import com.google.gson.JsonObject;
import com.google.gson.JsonSyntaxException;
import electroblob.wizardry.Wizardry;
import electroblob.wizardry.client.DrawingUtils;
import electroblob.wizardry.util.WizardryUtilities;
import electroblob.wizardry.util.JavaUtils;
import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.FontRenderer;
import net.minecraft.client.gui.GuiButton;
@@ -19,7 +19,7 @@ import java.util.*;
/**
* Instances of this class represent sections in the wizard's handbook. As of wizardry 4.2, this class handles
* everything within the section itself, including JSON parsing, unlock triggers and drawing the actual rawText.
* everything within the section itself, including JSON parsing, unlock triggers and drawing the actual text.
* Sections may now also be nested and have other elements within them, such as images and a table of contents, a
* behaviour which is also handled within this class.
* <p></p>
@@ -63,7 +63,7 @@ class Section {
}
Collection<GuiButton> getButtons(){
return WizardryUtilities.flatten(buttons);
return JavaUtils.flatten(buttons);
}
/**
@@ -313,8 +313,8 @@ class Section {
// The button id only does what you use it for, so we're just not using it at all.
this.buttons.get(pageRelative).add(GuiButtonHyperlink.create(x, y, font, upToLink, arguments, suffix, maxLineNumber - lineNumber - 1, GuiWizardHandbook.isRightPage(page)));
// The link button should exactly overlay the display rawText in the main string
// If the link has no display rawText specified, it displays the unformatted target string
// The link button should exactly overlay the display text in the main string
// If the link has no display text specified, it displays the unformatted target string
paragraph = paragraph.replace(linkRaw, arguments[arguments.length - 1]);
}
@@ -385,7 +385,7 @@ class Section {
if(JsonUtils.hasField(json, "text")){
section.rawText = Streams.stream(JsonUtils.getJsonArray(json, "text"))
.map(e -> JsonUtils.getString(e, "element of array rawText"))
.map(e -> JsonUtils.getString(e, "element of array text"))
.toArray(String[]::new);
}
@@ -0,0 +1,74 @@
package electroblob.wizardry.client.model;
import electroblob.wizardry.block.BlockBookshelf;
import net.minecraft.block.state.IBlockState;
import net.minecraft.client.Minecraft;
import net.minecraft.client.renderer.block.model.BakedQuad;
import net.minecraft.client.renderer.block.model.IBakedModel;
import net.minecraft.client.renderer.block.model.ItemOverrideList;
import net.minecraft.client.renderer.texture.TextureAtlasSprite;
import net.minecraft.util.EnumFacing;
import net.minecraftforge.common.property.IExtendedBlockState;
import javax.annotation.Nullable;
import java.util.ArrayList;
import java.util.List;
public class BakedModelBookshelf implements IBakedModel {
private final IBakedModel bookshelf;
private final IBakedModel[][] books;
public BakedModelBookshelf(IBakedModel bookshelf, IBakedModel[][] books){
this.bookshelf = bookshelf;
this.books = books;
}
@Override
public List<BakedQuad> getQuads(@Nullable IBlockState state, @Nullable EnumFacing side, long rand){
IBakedModel fallback = Minecraft.getMinecraft().getBlockRendererDispatcher().getBlockModelShapes().getModelManager().getMissingModel();
if(state == null) return fallback.getQuads(null, side, rand);
// It took me waaay too long to realise I needed a new list here
List<BakedQuad> quads = new ArrayList<>(bookshelf.getQuads(state, side, rand)); // Lists ain't immutable, chief
IExtendedBlockState extendedState = (IExtendedBlockState)state;
for(int i = 0; i<BlockBookshelf.SLOT_COUNT; i++){
Integer value = extendedState.getValue(BlockBookshelf.BOOKS[i]);
if(value == null || value < 0) return fallback.getQuads(null, side, rand);
if(value < books.length) quads.addAll(books[value][i].getQuads(state, side, rand)); // Empty slots use books.length
}
return quads;
}
@Override
public boolean isBuiltInRenderer(){
return false;
}
// Delegate everything else to the main bookshelf model
@Override
public boolean isAmbientOcclusion(){
return bookshelf.isAmbientOcclusion();
}
@Override
public boolean isGui3d(){
return bookshelf.isGui3d();
}
@Override
public TextureAtlasSprite getParticleTexture(){
return bookshelf.getParticleTexture();
}
@Override
public ItemOverrideList getOverrides(){
return bookshelf.getOverrides();
}
}
@@ -0,0 +1,171 @@
package electroblob.wizardry.client.model;
import com.google.common.collect.ImmutableMap;
import electroblob.wizardry.Wizardry;
import net.minecraft.block.state.IBlockState;
import net.minecraft.client.renderer.block.model.*;
import net.minecraft.client.renderer.texture.TextureAtlasSprite;
import net.minecraft.client.renderer.vertex.DefaultVertexFormats;
import net.minecraft.util.EnumFacing;
import net.minecraft.util.ResourceLocation;
import net.minecraftforge.client.event.ModelBakeEvent;
import net.minecraftforge.client.model.IModel;
import net.minecraftforge.client.model.ModelLoader;
import net.minecraftforge.client.model.ModelLoaderRegistry;
import net.minecraftforge.fml.common.Mod;
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
import net.minecraftforge.fml.relauncher.Side;
import org.apache.commons.lang3.tuple.Pair;
import javax.annotation.Nullable;
import javax.vecmath.Matrix4f;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* A special type of model that generates override models dynamically. This allows the same overrides to be applied to
* multiple item models without needing an override version of each one just to point to a texture.
* <p></p>
* To use this system in a model file:<p></p>
* 1. Make a base model file and define the item overrides in it. <b>This model must be placed in the folder
* {@code models/item/override_generators}</b> (unfortunately the folder has to be predefined, see below).<br>
* 2. Make the model files your overrides point to. These should define everything about the model except textures.<br>
* 3. In each model that should generate overrides dynamically, define a single override that points to the location of
* the model from step 1 (the predicate doesn't matter).<br>
* <p></p>
* It is advisable to make the parent of the override model (from step 2) the same as the parent of the model to
* be overridden, so that they share any necessary properties and texture variables. For example, both
* {@code ebwizardry:item/magic_wand.json} and {@code ebwizardry:item/wand_point.json} have {@code item/handheld} as
* their parent. The override model will reference the textures in the original model, so if the override defines a
* face with the texture {@code "#face"}, the texture used will be the file associated with {@code "face"} in the
* original model. For simple models, this will just be the standard {@code "layer0"}.
* @since Wizardry 4.3
* @author Electroblob
*/
// Okay, it's not *that* bad if you only have one override, but imagine having to duplicate every single wand model 5
// times if we wanted 5 animations...
// The nice thing is resource packs don't lose any flexibility this way, they can still redefine the models themselves
// and specify custom models for the overrides if they want, or they can keep using this system - in fact, they GAIN
// flexibility because they can also overwrite the base wand.json model and specify global overrides too
@Mod.EventBusSubscriber(Side.CLIENT)
public class BakedModelGenerateOverrides implements IBakedModel {
// I tried having this as a prefix that gets removed at runtime, which worked fine but it spammed the log with
// errors because the prefixed file doesn't actually exist, so even though it's never used the game still complains
private static final String OVERRIDE_GENERATORS = "override_generators";
private final IBakedModel delegate;
private final ItemOverrideList overrides;
public BakedModelGenerateOverrides(IBakedModel delegate, ItemOverrideList overrides){
this.delegate = delegate;
this.overrides = overrides;
}
@Override
public ItemOverrideList getOverrides(){
return overrides; // The only thing that's not delegated
}
// Delegate everything else
@Override
public List<BakedQuad> getQuads(@Nullable IBlockState state, @Nullable EnumFacing side, long rand){
return delegate.getQuads(state, side, rand);
}
@Override
public boolean isAmbientOcclusion(){
return delegate.isAmbientOcclusion();
}
@Override
public boolean isGui3d(){
return delegate.isGui3d();
}
@Override
public boolean isBuiltInRenderer(){
return delegate.isBuiltInRenderer();
}
@Override
public TextureAtlasSprite getParticleTexture(){
return delegate.getParticleTexture();
}
@Override
public ItemCameraTransforms getItemCameraTransforms(){
return delegate.getItemCameraTransforms();
}
@Override
public boolean isAmbientOcclusion(IBlockState state){
return delegate.isAmbientOcclusion(state);
}
@Override
public Pair<? extends IBakedModel, Matrix4f> handlePerspective(ItemCameraTransforms.TransformType cameraTransformType){
return delegate.handlePerspective(cameraTransformType);
}
// Baking
@SubscribeEvent
public static void bake(ModelBakeEvent event){
for(ModelResourceLocation location : event.getModelRegistry().getKeys()){
IBakedModel original = event.getModelRegistry().getObject(location);
try{
// Ignore the warnings, IntelliJ is right that these can't be null in the dev environment but certain
// mods seem to be doing ASM hackery that makes it so they can be null
if(original != null && original.getOverrides() != null && original.getOverrides().getOverrides() != null){
original.getOverrides().getOverrides().stream().map(ItemOverride::getLocation)
.filter(l -> l.getPath().contains(OVERRIDE_GENERATORS)).findFirst()
.ifPresent(l -> event.getModelRegistry().putObject(location,
substituteWandModel(event.getModelManager(), location, original, l)));
}
}catch(NullPointerException e){
Wizardry.logger.info("The model {} threw an error when trying to access item overrides, it will be ignored. If you're an addon dev and you made this model, something is wrong with it! Otherwise, please ignore this message.", location);
}
}
}
// We have to use ModelBakeEvent for this because we need the original model to be baked with the vanilla system
private static IBakedModel substituteWandModel(ModelManager modelManager, ModelResourceLocation location, IBakedModel original, ResourceLocation overrideLocation){
try {
IModel unbakedOriginal = ModelLoaderRegistry.getModel(location);
IModel referenceModel = ModelLoaderRegistry.getModel(overrideLocation);
ModelBlock vanillaOriginal = unbakedOriginal.asVanillaModel().orElse(null);
ModelBlock vanillaRefModel = referenceModel.asVanillaModel().orElse(null);
if(vanillaOriginal != null && vanillaRefModel != null){
// So... many... data structures...
List<ItemOverride> overrides = vanillaRefModel.getOverrides();
Map<ResourceLocation, IBakedModel> replacementMap = new HashMap<>();
for(ItemOverride override : overrides){
IModel replacement = ModelLoaderRegistry.getModel(override.getLocation()).retexture(ImmutableMap.copyOf(vanillaOriginal.textures));
replacementMap.put(override.getLocation(), replacement.bake(unbakedOriginal.getDefaultState(), DefaultVertexFormats.ITEM, ModelLoader.defaultTextureGetter()));
}
ItemOverrideListMapped overrideList = new ItemOverrideListMapped(overrides, replacementMap);
return new BakedModelGenerateOverrides(original, overrideList);
}
}catch(Exception exception){
Wizardry.logger.error("Error baking item display override models: ", exception);
}
return modelManager.getMissingModel();
}
}
@@ -28,7 +28,7 @@ import java.util.List;
* <br>
* <i>N.B. This doesn't cover item models, and all of the code/model solutions to this that I have tried haven't worked.
* However, I noticed that the shade tag seems to work perfectly for items, so instead I've simply duplicated the block
* models into the item models folder and add {@code "shade":false} where appropriate. (If it works, it works, right?)</i>
* models into the item models folder and add {@code "shade":false} where appropriate. (If it works, it works, right?)</i>
*
* @author Electroblob
* @author raoulvdberge
@@ -0,0 +1,42 @@
package electroblob.wizardry.client.model;
import com.google.common.collect.ImmutableMap;
import net.minecraft.client.renderer.block.model.IBakedModel;
import net.minecraft.client.renderer.block.model.ItemOverride;
import net.minecraft.client.renderer.block.model.ItemOverrideList;
import net.minecraft.entity.EntityLivingBase;
import net.minecraft.item.ItemStack;
import net.minecraft.util.ResourceLocation;
import net.minecraft.world.World;
import javax.annotation.Nullable;
import java.util.List;
import java.util.Map;
/** A version of {@link ItemOverrideList} that uses a predefined map to substitute a specific baked model depending on
* the location the currently-applicable override points to. */
public class ItemOverrideListMapped extends ItemOverrideList {
private final Map<ResourceLocation, IBakedModel> replacementMap;
public ItemOverrideListMapped(List<ItemOverride> overrides, Map<ResourceLocation, IBakedModel> replacementMap){
super(overrides);
this.replacementMap = ImmutableMap.copyOf(replacementMap);
}
@Override
public IBakedModel handleItemState(IBakedModel originalModel, ItemStack stack, @Nullable World world, @Nullable EntityLivingBase entity){
if(!stack.isEmpty() && stack.getItem().hasCustomProperties()){
// Get the location the original override points to...
ResourceLocation location = applyOverride(stack, world, entity); // I wonder why this is deprecated?
if(location != null){
return replacementMap.get(location); // ... then substitute in the corresponding generated model
}
}
return originalModel;
}
}
@@ -0,0 +1,99 @@
package electroblob.wizardry.client.model;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import electroblob.wizardry.Wizardry;
import electroblob.wizardry.block.BlockBookshelf;
import net.minecraft.client.renderer.block.model.IBakedModel;
import net.minecraft.client.renderer.block.model.ModelResourceLocation;
import net.minecraft.client.renderer.texture.TextureAtlasSprite;
import net.minecraft.client.renderer.vertex.VertexFormat;
import net.minecraft.util.ResourceLocation;
import net.minecraftforge.client.model.IModel;
import net.minecraftforge.client.model.ModelLoaderRegistry;
import net.minecraftforge.common.model.IModelState;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.function.Function;
public class ModelBookshelf implements IModel {
private static final ResourceLocation DEFAULT_BOOK_TEXTURE = new ResourceLocation(Wizardry.MODID, "blocks/books");
private static final List<ResourceLocation> bookModelLocations = new ArrayList<>();
private final ModelResourceLocation bookshelfModelLocation;
private final String variant;
static {
for(int i = 0; i < BlockBookshelf.SLOT_COUNT; i++){
bookModelLocations.add(new ResourceLocation(Wizardry.MODID, "bookshelf_parts/books" + i));
}
}
public ModelBookshelf(String bookshelfModelName){
String[] args = bookshelfModelName.split("#", 2);
String model = args[0];
variant = args.length == 2 ? args[1] : null;
bookshelfModelLocation = new ModelResourceLocation(new ResourceLocation(Wizardry.MODID, "bookshelf_parts/" + model), variant);
}
@Override
public IBakedModel bake(IModelState state, VertexFormat format, Function<ResourceLocation, TextureAtlasSprite> bakedTextureGetter){
try {
IModel bookshelfModel = ModelLoaderRegistry.getModel(bookshelfModelLocation);
// I don't know why default state works here (surely it ought to be the state param?), but it works so who cares
IBakedModel bookshelf = bookshelfModel.bake(bookshelfModel.getDefaultState(), format, bakedTextureGetter);
ImmutableList<ResourceLocation> textures = BlockBookshelf.getBookTextures();
IBakedModel[][] books = new IBakedModel[textures.size()][BlockBookshelf.SLOT_COUNT];
for(int i = 0; i < textures.size(); i++){
ImmutableMap<String, String> retexturer = ImmutableMap.of("books", textures.get(i).toString());
for(int j = 0; j < BlockBookshelf.SLOT_COUNT; j++){
IModel bookModel = ModelLoaderRegistry.getModel(new ModelResourceLocation(bookModelLocations.get(j), variant)).retexture(retexturer);
books[i][j] = bookModel.bake(bookModel.getDefaultState(), format, bakedTextureGetter); // Same here!
}
}
// To clarify: the whole point of doing this was to avoid having to bake 4 * 2^12 models per bookshelf type
// Therefore we have to have a baked bookshelf model that's kind of dynamic
return new BakedModelBookshelf(bookshelf, books);
}catch(Exception exception){
Wizardry.logger.error("Error baking bookshelf models: ", exception);
return ModelLoaderRegistry.getMissingModel().bake(state, format, bakedTextureGetter);
}
}
@Override
public Collection<ResourceLocation> getDependencies(){
return bookModelLocations;
}
@Override
public Collection<ResourceLocation> getTextures(){
// I simply have no idea how I'm *supposed* to do it, so I'm using the best solution I can come up with
// This is at least better than manually registering it with TextureStitchEvent
// However I'm also pretty sure that if a texture pack decides to change the texture that the book models point
// to, it won't work... but since nobody ever will, I don't really care!
// Why am I confused?
// 1. How come it magically finds the bookshelf texture, but not the book texture?
// 2. Why don't the textures get retrieved automatically based on getDependencies? Surely that's the point?
// 3. Is it correct to use getModel() to get the dependency models upon construction of this class so I can
// access their textures properly? Or is that too early?
// 4. If so, what's the point of getDependencies, and why should I override it?
return BlockBookshelf.getBookTextures();// ImmutableList.of(DEFAULT_BOOK_TEXTURE);
}
}
@@ -0,0 +1,74 @@
package electroblob.wizardry.client.model;
import net.minecraft.client.model.ModelBase;
import net.minecraft.client.model.ModelBox;
import net.minecraft.client.model.ModelRenderer;
import net.minecraft.entity.Entity;
// Made with Blockbench 3.5.2
public class ModelBoulder extends ModelBase {
private final ModelRenderer mainGroup;
private final ModelRenderer box1;
private final ModelRenderer box2;
private final ModelRenderer box3;
private final ModelRenderer box4;
private final ModelRenderer box5;
private final ModelRenderer box6;
public ModelBoulder(){
textureWidth = 64;
textureHeight = 32;
mainGroup = new ModelRenderer(this);
mainGroup.setRotationPoint(0.0F, 24.0F, 0.0F);
box1 = new ModelRenderer(this);
box1.setRotationPoint(0.0F, -19.0F, 0.0F);
mainGroup.addChild(box1);
box1.cubeList.add(new ModelBox(box1, 0, 0, -11.0F, -11.0F, 11.0F, 22, 22, 8, 0.0F, false));
box2 = new ModelRenderer(this);
box2.setRotationPoint(0.0F, -19.0F, 0.0F);
mainGroup.addChild(box2);
setRotationAngle(box2, -1.5708F, 0.0F, 0.0F);
box2.cubeList.add(new ModelBox(box2, 0, 0, -11.0F, -11.0F, 11.0F, 22, 22, 8, 0.0F, false));
box3 = new ModelRenderer(this);
box3.setRotationPoint(0.0F, -19.0F, 0.0F);
mainGroup.addChild(box3);
setRotationAngle(box3, 0.0F, -1.5708F, 0.0F);
box3.cubeList.add(new ModelBox(box3, 0, 0, -11.0F, -11.0F, 11.0F, 22, 22, 8, 0.0F, false));
box4 = new ModelRenderer(this);
box4.setRotationPoint(0.0F, -19.0F, 0.0F);
mainGroup.addChild(box4);
setRotationAngle(box4, 0.0F, 1.5708F, 0.0F);
box4.cubeList.add(new ModelBox(box4, 0, 0, -11.0F, -11.0F, 11.0F, 22, 22, 8, 0.0F, false));
box5 = new ModelRenderer(this);
box5.setRotationPoint(0.0F, -19.0F, 0.0F);
mainGroup.addChild(box5);
setRotationAngle(box5, 1.5708F, 0.0F, 0.0F);
box5.cubeList.add(new ModelBox(box5, 0, 0, -11.0F, -11.0F, 11.0F, 22, 22, 8, 0.0F, false));
box6 = new ModelRenderer(this);
box6.setRotationPoint(0.0F, -19.0F, 0.0F);
mainGroup.addChild(box6);
setRotationAngle(box6, 3.1416F, 0.0F, 0.0F);
box6.cubeList.add(new ModelBox(box6, 0, 0, -11.0F, -11.0F, 11.0F, 22, 22, 8, 0.0F, false));
}
@Override
public void render(Entity entity, float f, float f1, float f2, float f3, float f4, float f5){
mainGroup.render(f5);
}
public void setRotationAngle(ModelRenderer modelRenderer, float x, float y, float z){
modelRenderer.rotateAngleX = x;
modelRenderer.rotateAngleY = y;
modelRenderer.rotateAngleZ = z;
}
}
@@ -0,0 +1,35 @@
package electroblob.wizardry.client.model;
import net.minecraft.client.model.ModelBase;
import net.minecraft.client.model.ModelBox;
import net.minecraft.client.model.ModelRenderer;
import net.minecraft.entity.Entity;
// Made with Blockbench 3.5.4
public class ModelIceBarrier extends ModelBase {
private final ModelRenderer mainGroup;
public ModelIceBarrier(){
textureWidth = 64;
textureHeight = 64;
mainGroup = new ModelRenderer(this);
mainGroup.setRotationPoint(0.0F, 24.0F, 0.0F);
setRotationAngle(mainGroup, 0.0F, 0.0F, 0.7854F);
mainGroup.cubeList.add(new ModelBox(mainGroup, 0, 0, -14.5F, -14.5F, -1.5F, 29, 29, 3, 0.0F, false));
mainGroup.cubeList.add(new ModelBox(mainGroup, 0, 32, -9.0F, -9.0F, -4.0F, 18, 18, 8, 0.0F, false));
}
@Override
public void render(Entity entity, float f, float f1, float f2, float f3, float f4, float f5) {
mainGroup.render(f5);
}
public void setRotationAngle(ModelRenderer modelRenderer, float x, float y, float z) {
modelRenderer.rotateAngleX = x;
modelRenderer.rotateAngleY = y;
modelRenderer.rotateAngleZ = z;
}
}
@@ -0,0 +1,28 @@
package electroblob.wizardry.client.model;
import electroblob.wizardry.Wizardry;
import net.minecraft.client.resources.IResourceManager;
import net.minecraft.util.ResourceLocation;
import net.minecraftforge.client.model.ICustomModelLoader;
import net.minecraftforge.client.model.IModel;
public class ModelLoaderBookshelf implements ICustomModelLoader {
private static final String SMART_MODEL_PREFIX = "models/block/smart_model/";
@Override
public void onResourceManagerReload(IResourceManager resourceManager){
// Do nothing
}
@Override
public boolean accepts(ResourceLocation modelLocation){
return modelLocation.getNamespace().equals(Wizardry.MODID) && modelLocation.getPath().startsWith(SMART_MODEL_PREFIX);
}
@Override
public IModel loadModel(ResourceLocation modelLocation){
return new ModelBookshelf(modelLocation.getPath().substring(SMART_MODEL_PREFIX.length()));
}
}
@@ -0,0 +1,48 @@
package electroblob.wizardry.client.model;
import net.minecraft.client.model.ModelBase;
import net.minecraft.client.model.ModelRenderer;
import net.minecraft.client.renderer.GlStateManager;
import net.minecraft.entity.Entity;
import net.minecraft.util.math.MathHelper;
/** Based on ModelEnderCrystal */
public class ModelRemnant extends ModelBase {
private final ModelRenderer cube = new ModelRenderer(this, "cube");
public ModelRemnant(){
this.cube.setTextureOffset(0, 0).addBox(-4.0F, -4.0F, -4.0F, 8, 8, 8);
}
@Override
public void render(Entity entity, float rotationSpeed, float expandFraction, float age, float netHeadYaw, float headPitch, float scale){
GlStateManager.pushMatrix();
// GlStateManager.scale(2.0F, 2.0F, 2.0F);
// GlStateManager.translate(0.0F, -0.5F, 0.0F);
GlStateManager.disableCull();
GlStateManager.translate(0.0F, entity.height/2, 0.0F);
float s = expandFraction + MathHelper.sin(age * 0.1f) * 0.06f;
GlStateManager.scale(s, s, s);
GlStateManager.rotate(age * rotationSpeed/2, 0.0F, 1.0F, 0.0F);
GlStateManager.rotate(60.0F, 0.7071F, 0.0F, 0.7071F);
GlStateManager.rotate(age * rotationSpeed, 0.0F, 1.0F, 0.0F);
this.cube.render(scale);
GlStateManager.scale(0.875F, 0.875F, 0.875F);
GlStateManager.rotate(60.0F, 0.7071F, 0.0F, 0.7071F);
GlStateManager.rotate(age * rotationSpeed, 0.0F, 1.0F, 0.0F);
this.cube.render(scale);
GlStateManager.enableCull();
GlStateManager.popMatrix();
}
}
@@ -0,0 +1,66 @@
package electroblob.wizardry.client.model;
import electroblob.wizardry.block.BlockStatue;
import net.minecraft.client.model.ModelRenderer;
import net.minecraft.entity.Entity;
public class ModelRobeArmour extends ModelArmourFixer {
ModelRenderer robe;
private boolean showHeadWithBody;
public ModelRobeArmour(float delta, boolean showHeadWithBody){
super(delta, 0, 64, 64);
this.showHeadWithBody = showHeadWithBody;
bipedBody = new ModelRenderer(this, 16, 16);
bipedBody.addBox(-4F, 0F, -2F, 8, 11, 4, delta);
bipedBody.setRotationPoint(0F, 0F, 0F);
bipedBody.setTextureSize(64, 64);
bipedBody.mirror = true;
setRotation(bipedBody, 0F, 0F, 0F);
robe = new ModelRenderer(this, 40, 32);
robe.addBox(-4F, 0F, -2F, 8, 9, 4, delta);
robe.setRotationPoint(0F, 12, 0F); // 12.5 accounts for the expansion of each box
robe.setTextureSize(64, 64);
robe.mirror = true;
setRotation(robe, 0F, 0F, 0F);
}
public void render(Entity entity, float f, float f1, float f2, float f3, float f4, float f5){
if(entity.isInvisible() && !entity.getEntityData().getBoolean(BlockStatue.PETRIFIED_NBT_KEY)
&& !entity.getEntityData().getBoolean(BlockStatue.FROZEN_NBT_KEY)) return;
super.render(entity, f, f1, f2, f3, f4, f5);
setRotationAngles(f, f1, f2, f3, f4, f5, entity);
this.robe.render(f5);
}
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);
this.robe.showModel = this.bipedBody.showModel;
if(showHeadWithBody) this.bipedHead.showModel = this.bipedBody.showModel;
if(this.isSneak){
//this.robe.rotationPointY = 10.5f;
this.robe.rotationPointZ = 4;
}else{
//this.robe.rotationPointY = 12.5f;
this.robe.rotationPointZ = 0;
}
// The bottom part of the robe takes the y rotation from the rest of the robe but the x/z rotation
// from the average of the two legs
this.robe.rotateAngleX = (this.bipedLeftLeg.rotateAngleX + this.bipedRightLeg.rotateAngleX) / 2f;
this.robe.rotateAngleY = this.bipedBody.rotateAngleY;
this.robe.rotateAngleZ = (this.bipedLeftLeg.rotateAngleZ + this.bipedRightLeg.rotateAngleZ) / 2f;
}
}
@@ -0,0 +1,146 @@
package electroblob.wizardry.client.model;
import electroblob.wizardry.block.BlockStatue;
import net.minecraft.client.model.ModelRenderer;
import net.minecraft.entity.Entity;
public class ModelSageArmour extends ModelArmourFixer {
ModelRenderer hatBrim;
ModelRenderer hatSegment1;
ModelRenderer hatSegment2;
ModelRenderer hatSegment3;
ModelRenderer hatSegment4;
ModelRenderer hatSegment5;
ModelRenderer hatSegment6;
ModelRenderer robe;
ModelRenderer collar;
public ModelSageArmour(float delta){
super(delta, 0, 64, 64);
// This is necessary to stop the head from scaling.
this.bipedHead = new ModelRenderer(this, 0, 0);
// The hat layer has an offset of 0.5, so 0.6 is about the smallest we can get away with
this.bipedHead.addBox(-4.0F, -8.0F, -4.0F, 8, 8, 8, 0.6f);
this.bipedHead.setRotationPoint(0.0F, 0.0F + 0, 0.0F);
hatBrim = new ModelRenderer(this, 10, 45);
// Making the height 1 stops the top and bottom z-fighting when the hat is enchanted
hatBrim.addBox(-9F, -5.7F, -9F, 18, 1, 18, 0.6f);
hatBrim.setRotationPoint(0F, 0F, 0F);
hatBrim.setTextureSize(64, 64);
hatBrim.mirror = true;
setRotation(hatBrim, 0F, 0F, 0F);
hatSegment1 = new ModelRenderer(this, 0, 42);
hatSegment1.addBox(0F, 0F, 0F, 6, 2, 6, 0.2f);
hatSegment1.setRotationPoint(-3F, -10.6F, -3F);
hatSegment1.setTextureSize(64, 64);
hatSegment1.mirror = true;
setRotation(hatSegment1, -0.1396263F, 0F, 0F);
hatSegment2 = new ModelRenderer(this, 0, 50);
hatSegment2.addBox(0F, 0F, 0F, 5, 2, 5, 0.1f);
hatSegment2.setRotationPoint(-2.5F, -12.13333F, -2F);
hatSegment2.setTextureSize(64, 64);
hatSegment2.mirror = true;
setRotation(hatSegment2, -0.2443461F, 0F, 0F);
hatSegment3 = new ModelRenderer(this, 0, 57);
hatSegment3.addBox(0F, 0F, 0F, 4, 2, 4);
hatSegment3.setRotationPoint(-2F, -13.6F, -1F);
hatSegment3.setTextureSize(64, 64);
hatSegment3.mirror = true;
setRotation(hatSegment3, -0.4014257F, 0F, 0F);
hatSegment4 = new ModelRenderer(this, 16, 58);
hatSegment4.addBox(0F, 0F, 0F, 3, 2, 3);
hatSegment4.setRotationPoint(-1.5F, -14.6F, 0F);
hatSegment4.setTextureSize(64, 64);
hatSegment4.mirror = true;
setRotation(hatSegment4, -0.5759587F, 0F, 0F);
hatSegment5 = new ModelRenderer(this, 20, 54);
hatSegment5.addBox(0F, 0F, 0F, 2, 2, 2);
hatSegment5.setRotationPoint(-1F, -14.6F, 0F);
hatSegment5.setTextureSize(64, 64);
hatSegment5.mirror = true;
setRotation(hatSegment5, 0.3316126F, 0F, 0F);
hatSegment6 = new ModelRenderer(this, 20, 50);
hatSegment6.addBox(0F, 0F, 0F, 1, 1, 3);
hatSegment6.setRotationPoint(-0.5F, -15.1F, 2F);
hatSegment6.setTextureSize(64, 64);
hatSegment6.mirror = true;
setRotation(hatSegment6, -0.5585054F, 0F, 0F);
bipedBody = new ModelRenderer(this, 16, 16);
bipedBody.addBox(-4F, 0F, -2F, 8, 11, 4, delta);
bipedBody.setRotationPoint(0F, 0F, 0F);
bipedBody.setTextureSize(64, 64);
bipedBody.mirror = true;
setRotation(bipedBody, 0F, 0F, 0F);
robe = new ModelRenderer(this, 40, 32);
robe.addBox(-4F, 0F, -2F, 8, 9, 4, delta);
robe.setRotationPoint(0F, 12, 0F); // 12.5 accounts for the expansion of each box
robe.setTextureSize(64, 64);
robe.mirror = true;
setRotation(robe, 0F, 0F, 0F);
collar = new ModelRenderer(this, 0, 32);
collar.addBox(-7F, 0F, -2F, 14, 4, 4, 1);
collar.setRotationPoint(0F, 0F, 0F);
collar.setTextureSize(64, 64);
collar.mirror = true;
setRotation(collar, 0F, 0F, 0F);
// Makes the hat rotate with the head.
bipedHead.addChild(hatBrim);
bipedHead.addChild(hatSegment1);
bipedHead.addChild(hatSegment2);
bipedHead.addChild(hatSegment3);
bipedHead.addChild(hatSegment4);
bipedHead.addChild(hatSegment5);
bipedHead.addChild(hatSegment6);
bipedBody.addChild(collar);
}
public void render(Entity entity, float f, float f1, float f2, float f3, float f4, float f5){
if(entity.isInvisible() && !entity.getEntityData().getBoolean(BlockStatue.PETRIFIED_NBT_KEY)
&& !entity.getEntityData().getBoolean(BlockStatue.FROZEN_NBT_KEY)) return;
super.render(entity, f, f1, f2, f3, f4, f5);
setRotationAngles(f, f1, f2, f3, f4, f5, entity);
this.robe.render(f5);
}
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);
this.robe.showModel = this.bipedBody.showModel;
if(this.isSneak){
//this.robe.rotationPointY = 10.5f;
this.robe.rotationPointZ = 4;
}else{
//this.robe.rotationPointY = 12.5f;
this.robe.rotationPointZ = 0;
}
// The bottom part of the robe takes the y rotation from the rest of the robe but the x/z rotation
// from the average of the two legs
this.robe.rotateAngleX = (this.bipedLeftLeg.rotateAngleX + this.bipedRightLeg.rotateAngleX) / 2f;
this.robe.rotateAngleY = this.bipedBody.rotateAngleY;
this.robe.rotateAngleZ = (this.bipedLeftLeg.rotateAngleZ + this.bipedRightLeg.rotateAngleZ) / 2f;
}
}
@@ -5,10 +5,12 @@ import electroblob.wizardry.block.BlockCrystal;
import electroblob.wizardry.block.BlockPedestal;
import electroblob.wizardry.block.BlockRunestone;
import electroblob.wizardry.item.IMultiTexturedItem;
import electroblob.wizardry.item.ItemBlockMultiTexturedElemental;
import electroblob.wizardry.item.ItemBlockMultiTextured;
import electroblob.wizardry.item.ItemCrystal;
import electroblob.wizardry.item.ItemSpectralDust;
import electroblob.wizardry.registry.WizardryBlocks;
import electroblob.wizardry.registry.WizardryItems;
import net.minecraft.block.BlockPlanks;
import net.minecraft.client.renderer.block.model.IBakedModel;
import net.minecraft.client.renderer.block.model.ModelBakery;
import net.minecraft.client.renderer.block.model.ModelResourceLocation;
@@ -20,11 +22,15 @@ import net.minecraft.util.NonNullList;
import net.minecraftforge.client.event.ModelBakeEvent;
import net.minecraftforge.client.event.ModelRegistryEvent;
import net.minecraftforge.client.model.ModelLoader;
import net.minecraftforge.client.model.ModelLoaderRegistry;
import net.minecraftforge.fml.common.Mod;
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
import net.minecraftforge.fml.relauncher.Side;
import net.minecraftforge.oredict.OreDictionary;
import java.util.ArrayList;
import java.util.List;
/**
* Class responsible for registering all of wizardry's item and block models.
*
@@ -37,32 +43,39 @@ public final class WizardryModels {
private WizardryModels(){} // No instances!
/** Keeps track of all items whose models have been registered manually to exclude them from automatic registry of
* standard item models. Internal only, this gets cleared once model registry is complete. */
private static final List<Item> registeredItems = new ArrayList<>();
@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));
ModelLoader.setCustomStateMapper(WizardryBlocks.crystal_block, new StateMap.Builder()
.withName(BlockCrystal.ELEMENT).withSuffix("_crystal_block").build());
// Yay unchecked casting! But we know it's always ok here, and it makes everything much neater.
ItemBlockMultiTexturedElemental crystalBlockItem = (ItemBlockMultiTexturedElemental)Item.getItemFromBlock(WizardryBlocks.crystal_block);
ItemBlockMultiTextured crystalBlockItem = (ItemBlockMultiTextured)Item.getItemFromBlock(WizardryBlocks.crystal_block);
registerMultiTexturedModel(crystalBlockItem);
ModelLoader.setCustomStateMapper(WizardryBlocks.runestone, new StateMap.Builder()
.withName(BlockRunestone.ELEMENT).withSuffix("_runestone").build());
ItemBlockMultiTexturedElemental runestoneItem = (ItemBlockMultiTexturedElemental)Item.getItemFromBlock(WizardryBlocks.runestone);
ItemBlockMultiTextured runestoneItem = (ItemBlockMultiTextured)Item.getItemFromBlock(WizardryBlocks.runestone);
registerMultiTexturedModel(runestoneItem);
ModelLoader.setCustomStateMapper(WizardryBlocks.runestone_pedestal, new StateMap.Builder()
.withName(BlockPedestal.ELEMENT).ignore(BlockPedestal.NATURAL).withSuffix("_runestone_pedestal").build()); // Don't care about NATURAL property
ItemBlockMultiTexturedElemental pedestalItem = (ItemBlockMultiTexturedElemental)Item.getItemFromBlock(WizardryBlocks.runestone_pedestal);
ItemBlockMultiTextured pedestalItem = (ItemBlockMultiTextured)Item.getItemFromBlock(WizardryBlocks.runestone_pedestal);
registerMultiTexturedModel(pedestalItem);
ModelLoader.setCustomStateMapper(WizardryBlocks.gilded_wood, new StateMap.Builder()
.withName(BlockPlanks.VARIANT).withSuffix("_gilded_wood").build());
ItemBlockMultiTextured gildedWoodItem = (ItemBlockMultiTextured)Item.getItemFromBlock(WizardryBlocks.gilded_wood);
registerMultiTexturedModel(gildedWoodItem);
// Explanation for all this here -> https://github.com/TheGreyGhost/MinecraftByExample/tree/master/src/main/java/minecraftbyexample/mbe05_block_dynamic_block_model2
ModelLoaderRegistry.registerLoader(new ModelLoaderBookshelf());
// Items
registerMultiTexturedModel((ItemCrystal)WizardryItems.magic_crystal);
@@ -72,11 +85,9 @@ public final class WizardryModels {
registerWandModel(WizardryItems.advanced_wand);
registerWandModel(WizardryItems.master_wand);
registerItemModel(WizardryItems.spell_book);
registerItemModel(WizardryItems.spell_book); // Also need this or auto-registry will ignore it
// Wildcard registered for wizard trades.
registerItemModel(WizardryItems.spell_book, OreDictionary.WILDCARD_VALUE, "normal");
registerItemModel(WizardryItems.arcane_tome);
registerItemModel(WizardryItems.wizard_handbook);
registerWandModel(WizardryItems.novice_fire_wand);
registerWandModel(WizardryItems.novice_ice_wand);
@@ -110,160 +121,16 @@ public final class WizardryModels {
registerWandModel(WizardryItems.master_sorcery_wand);
registerWandModel(WizardryItems.master_healing_wand);
registerItemModel(WizardryItems.spectral_sword);
registerItemModel(WizardryItems.spectral_pickaxe);
registerItemModel(WizardryItems.spectral_bow);
registerMultiTexturedModel((ItemSpectralDust)WizardryItems.spectral_dust);
registerItemModel(WizardryItems.small_mana_flask);
registerItemModel(WizardryItems.medium_mana_flask);
registerItemModel(WizardryItems.large_mana_flask);
registerItemModel(WizardryItems.crystal_shard);
registerItemModel(WizardryItems.grand_crystal);
registerItemModel(WizardryItems.astral_diamond);
registerItemModel(WizardryItems.purifying_elixir);
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.melee_upgrade);
registerItemModel(WizardryItems.flaming_axe);
registerItemModel(WizardryItems.frost_axe);
registerItemModel(WizardryItems.firebomb);
registerItemModel(WizardryItems.poison_bomb);
registerItemModel(WizardryItems.smoke_bomb);
registerItemModel(WizardryItems.spark_bomb);
registerItemModel(WizardryItems.blank_scroll);
registerItemModel(WizardryItems.scroll);
registerItemModel(WizardryItems.identification_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.lightning_hammer);
registerItemModel(WizardryItems.ring_condensing);
registerItemModel(WizardryItems.ring_siphoning);
registerItemModel(WizardryItems.ring_battlemage);
registerItemModel(WizardryItems.ring_combustion);
registerItemModel(WizardryItems.ring_fire_melee);
registerItemModel(WizardryItems.ring_fire_biome);
registerItemModel(WizardryItems.ring_disintegration);
registerItemModel(WizardryItems.ring_ice_melee);
registerItemModel(WizardryItems.ring_ice_biome);
registerItemModel(WizardryItems.ring_arcane_frost);
registerItemModel(WizardryItems.ring_shattering);
registerItemModel(WizardryItems.ring_lightning_melee);
registerItemModel(WizardryItems.ring_storm);
registerItemModel(WizardryItems.ring_seeking);
registerItemModel(WizardryItems.ring_hammer);
registerItemModel(WizardryItems.ring_soulbinding);
registerItemModel(WizardryItems.ring_leeching);
registerItemModel(WizardryItems.ring_necromancy_melee);
registerItemModel(WizardryItems.ring_mind_control);
registerItemModel(WizardryItems.ring_poison);
registerItemModel(WizardryItems.ring_earth_melee);
registerItemModel(WizardryItems.ring_earth_biome);
registerItemModel(WizardryItems.ring_full_moon);
registerItemModel(WizardryItems.ring_extraction);
registerItemModel(WizardryItems.ring_mana_return);
registerItemModel(WizardryItems.ring_blockwrangler);
registerItemModel(WizardryItems.ring_conjurer);
registerItemModel(WizardryItems.ring_defender);
registerItemModel(WizardryItems.ring_paladin);
registerItemModel(WizardryItems.ring_interdiction);
registerItemModel(WizardryItems.amulet_arcane_defence);
registerItemModel(WizardryItems.amulet_warding);
registerItemModel(WizardryItems.amulet_wisdom);
registerItemModel(WizardryItems.amulet_fire_protection);
registerItemModel(WizardryItems.amulet_fire_cloaking);
registerItemModel(WizardryItems.amulet_ice_immunity);
registerItemModel(WizardryItems.amulet_ice_protection);
registerItemModel(WizardryItems.amulet_potential);
registerItemModel(WizardryItems.amulet_channeling);
registerItemModel(WizardryItems.amulet_lich);
registerItemModel(WizardryItems.amulet_wither_immunity);
registerItemModel(WizardryItems.amulet_glide);
registerItemModel(WizardryItems.amulet_banishing);
registerItemModel(WizardryItems.amulet_anchoring);
registerItemModel(WizardryItems.amulet_recovery);
registerItemModel(WizardryItems.amulet_transience);
registerItemModel(WizardryItems.amulet_resurrection);
registerItemModel(WizardryItems.amulet_auto_shield);
registerItemModel(WizardryItems.charm_haggler);
registerItemModel(WizardryItems.charm_experience_tome);
registerItemModel(WizardryItems.charm_auto_smelt);
registerItemModel(WizardryItems.charm_lava_walking);
registerItemModel(WizardryItems.charm_storm);
registerItemModel(WizardryItems.charm_minion_health);
registerItemModel(WizardryItems.charm_minion_variants);
registerItemModel(WizardryItems.charm_flight);
registerItemModel(WizardryItems.charm_growth);
registerItemModel(WizardryItems.charm_abseiling);
registerItemModel(WizardryItems.charm_silk_touch);
registerItemModel(WizardryItems.charm_stop_time);
registerItemModel(WizardryItems.charm_light);
registerItemModel(WizardryItems.charm_transportation);
registerItemModel(WizardryItems.charm_feeding);
// Automatic item model registry
for(Item item : Item.REGISTRY){
if(!registeredItems.contains(item) && item.getRegistryName().getNamespace().equals(Wizardry.MODID)){
registerItemModel(item); // Standard item model
}
}
registeredItems.clear(); // Might as well clean this up
}
@SubscribeEvent
@@ -280,11 +147,12 @@ public final class WizardryModels {
if(location.getNamespace().equals(Wizardry.MODID)){
if(location.getPath().contains("runestone") || location.getPath().contains("runestone_pedestal")){
IBakedModel original = event.getModelRegistry().getObject(location);
IBakedModel original = event.getModelRegistry().getObject(location);
if(location.getPath().contains("runestone") || location.getPath().contains("runestone_pedestal")
|| location.getPath().endsWith("imbuement_altar")){ // Ends with to exclude inactive version
event.getModelRegistry().putObject(location, new BakedModelGlowingOverlay(original, "overlay"));
}else if(location.getPath().contains("spectral_block")){
IBakedModel original = event.getModelRegistry().getObject(location);
event.getModelRegistry().putObject(location, new BakedModelGlowingOverlay(original, "spectral_block"));
}
}
@@ -302,6 +170,7 @@ public final class WizardryModels {
ModelBakery.registerItemVariants(item, new ModelResourceLocation(item.getRegistryName(), "inventory"));
// Assigns the model for all metadata values
ModelLoader.setCustomMeshDefinition(item, s -> new ModelResourceLocation(item.getRegistryName(), "inventory"));
registeredItems.add(item);
}
/**
@@ -315,6 +184,7 @@ public final class WizardryModels {
ModelBakery.registerItemVariants(item, new ModelResourceLocation("ebwizardry:festive_wand", "inventory"));
// Assigns the model for all metadata values
ModelLoader.setCustomMeshDefinition(item, s -> new ModelResourceLocation("ebwizardry:festive_wand", "inventory"));
registeredItems.add(item);
}else{
registerItemModel(item);
}
@@ -338,6 +208,8 @@ public final class WizardryModels {
new ModelResourceLocation(item.getModelName(stack), "inventory"));
}
}
registeredItems.add(item);
}
/**
@@ -348,6 +220,7 @@ public final class WizardryModels {
private static void registerItemModel(Item item, int metadata, String variant){
ModelLoader.setCustomModelResourceLocation(item, metadata,
new ModelResourceLocation(item.getRegistryName(), variant));
registeredItems.add(item); // Still ought to do this in case I ever use this method alone for an item
}
}
@@ -0,0 +1,60 @@
package electroblob.wizardry.client.particle;
import electroblob.wizardry.Wizardry;
import electroblob.wizardry.util.GeometryUtils;
import net.minecraft.util.EnumFacing;
import net.minecraft.util.ResourceLocation;
import net.minecraft.util.math.BlockPos;
import net.minecraft.world.World;
import net.minecraftforge.client.event.TextureStitchEvent;
import net.minecraftforge.fml.common.Mod;
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
import net.minecraftforge.fml.relauncher.Side;
//@SideOnly(Side.CLIENT)
@Mod.EventBusSubscriber(Side.CLIENT)
public class ParticleBlockHighlight extends ParticleWizardry {
private static final ResourceLocation TEXTURE = new ResourceLocation(Wizardry.MODID, "particle/block_highlight");
public ParticleBlockHighlight(World world, double x, double y, double z){
super(world, x, y, z, TEXTURE);
this.particleGravity = 0;
this.setMaxAge(160);
// 5 makes it the exact size of a block face, plus a little bit to account for the anti-z-fighting offset
this.particleScale = 5 * (1 + 2 * (float)GeometryUtils.ANTI_Z_FIGHTING_OFFSET);
this.shaded = false;
}
@Override
public boolean shouldDisableDepth(){
return true;
}
@Override
public void onUpdate(){
super.onUpdate();
// Fading
if(this.particleAge > this.particleMaxAge/2){
this.setAlphaF(1 - ((float)this.particleAge - this.particleMaxAge/2f) / (this.particleMaxAge/2f));
}
EnumFacing facing = EnumFacing.fromAngle(yaw);
if(pitch == 90) facing = EnumFacing.UP;
if(pitch == -90) facing = EnumFacing.DOWN;
// Disappears if there is no block behind it (this is the same check used to spawn it)
if(!world.getBlockState(new BlockPos(posX, posY, posZ).offset(facing.getOpposite())).getMaterial().isSolid()){
this.setExpired();
}
}
@SubscribeEvent
public static void onTextureStitchEvent(TextureStitchEvent.Pre event){
event.getMap().registerSprite(TEXTURE);
}
}
@@ -59,7 +59,7 @@ public class ParticleBuff extends ParticleWizardry {
float rotationYZ, float rotationXY, float rotationXZ){
// Copied from ParticleWizardry, needs to be here since we're not calling super
updateEntityLinking(partialTicks);
updateEntityLinking(viewer, partialTicks);
GlStateManager.pushMatrix();
GlStateManager.pushAttrib();
@@ -0,0 +1,52 @@
package electroblob.wizardry.client.particle;
import net.minecraft.util.ResourceLocation;
import net.minecraft.util.math.MathHelper;
import net.minecraft.world.World;
import net.minecraftforge.client.event.TextureStitchEvent;
import net.minecraftforge.fml.common.Mod;
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
import net.minecraftforge.fml.relauncher.Side;
@Mod.EventBusSubscriber(Side.CLIENT)
public class ParticleCloud extends ParticleWizardry {
private static final ResourceLocation[] TEXTURES = generateTextures("cloud", 4);
public ParticleCloud(World world, double x, double y, double z){
super(world, x, y, z, TEXTURES[world.rand.nextInt(TEXTURES.length)]);
this.setRBGColorF(1, 1, 1);
this.particleMaxAge = 48 + this.rand.nextInt(12);
this.particleScale *= 6;
this.setGravity(false);
this.setAlphaF(0);
this.canCollide = false;
this.shaded = true;
}
@Override
public boolean shouldDisableDepth(){
return true;
}
@Override
public void onUpdate(){
super.onUpdate();
// Fading
float fadeTime = this.particleMaxAge * 0.3f;
this.setAlphaF(MathHelper.clamp(Math.min(this.particleAge / fadeTime, (this.particleMaxAge - this.particleAge) / fadeTime), 0, 1));
}
@SubscribeEvent
public static void onTextureStitchEvent(TextureStitchEvent.Pre event){
for(ResourceLocation texture : TEXTURES){
event.getMap().registerSprite(texture);
}
}
}
@@ -17,15 +17,4 @@ public class ParticleDust extends ParticleWizardry {
this.setRBGColorF(1, 1, 1);
}
@Override
public void onUpdate(){
this.prevPosX = this.posX;
this.prevPosY = this.posY;
this.prevPosZ = this.posZ;
this.move(this.motionX, this.motionY, this.motionZ);
if(this.particleMaxAge-- <= 0){
this.setExpired();
}
}
}
@@ -35,15 +35,10 @@ public class ParticleFlash extends ParticleWizardry {
int i = this.getBrightnessForRender(partialTicks);
int j = i >> 16 & 65535;
int k = i & 65535;
buffer.pos((double)(f5 - rotationX * f4 - rotationXY * f4), (double)(f6 - rotationZ * f4), (double)(f7 - rotationYZ * f4 - rotationXZ * f4)).tex(0.5D, 0.375D).color(this.particleRed, this.particleGreen, this.particleBlue, this.particleAlpha).lightmap(j, k).endVertex();
buffer.pos((double)(f5 - rotationX * f4 + rotationXY * f4), (double)(f6 + rotationZ * f4), (double)(f7 - rotationYZ * f4 + rotationXZ * f4)).tex(0.5D, 0.125D).color(this.particleRed, this.particleGreen, this.particleBlue, this.particleAlpha).lightmap(j, k).endVertex();
buffer.pos((double)(f5 + rotationX * f4 + rotationXY * f4), (double)(f6 + rotationZ * f4), (double)(f7 + rotationYZ * f4 + rotationXZ * f4)).tex(0.25D, 0.125D).color(this.particleRed, this.particleGreen, this.particleBlue, this.particleAlpha).lightmap(j, k).endVertex();
buffer.pos((double)(f5 + rotationX * f4 - rotationXY * f4), (double)(f6 - rotationZ * f4), (double)(f7 + rotationYZ * f4 - rotationXZ * f4)).tex(0.25D, 0.375D).color(this.particleRed, this.particleGreen, this.particleBlue, this.particleAlpha).lightmap(j, k).endVertex();
}
@Override
public int getBrightnessForRender(float partialTicks){
return 15728880;
buffer.pos(f5 - rotationX * f4 - rotationXY * f4, f6 - rotationZ * f4, f7 - rotationYZ * f4 - rotationXZ * f4).tex(0.5D, 0.375D).color(this.particleRed, this.particleGreen, this.particleBlue, this.particleAlpha).lightmap(j, k).endVertex();
buffer.pos(f5 - rotationX * f4 + rotationXY * f4, f6 + rotationZ * f4, f7 - rotationYZ * f4 + rotationXZ * f4).tex(0.5D, 0.125D).color(this.particleRed, this.particleGreen, this.particleBlue, this.particleAlpha).lightmap(j, k).endVertex();
buffer.pos(f5 + rotationX * f4 + rotationXY * f4, f6 + rotationZ * f4, f7 + rotationYZ * f4 + rotationXZ * f4).tex(0.25D, 0.125D).color(this.particleRed, this.particleGreen, this.particleBlue, this.particleAlpha).lightmap(j, k).endVertex();
buffer.pos(f5 + rotationX * f4 - rotationXY * f4, f6 - rotationZ * f4, f7 + rotationYZ * f4 - rotationXZ * f4).tex(0.25D, 0.375D).color(this.particleRed, this.particleGreen, this.particleBlue, this.particleAlpha).lightmap(j, k).endVertex();
}
}
@@ -0,0 +1,78 @@
package electroblob.wizardry.client.particle;
import net.minecraft.client.Minecraft;
import net.minecraft.client.renderer.BufferBuilder;
import net.minecraft.client.renderer.GlStateManager;
import net.minecraft.client.renderer.OpenGlHelper;
import net.minecraft.client.renderer.Tessellator;
import net.minecraft.client.renderer.vertex.DefaultVertexFormats;
import net.minecraft.util.ResourceLocation;
import net.minecraft.world.World;
import org.lwjgl.opengl.GL11;
public class ParticleGuardianBeam extends ParticleTargeted {
/** Half the width of the beam. */
private static final float THICKNESS = 0.15f;
private static final ResourceLocation TEXTURE = new ResourceLocation("minecraft:textures/entity/guardian_beam.png");
public ParticleGuardianBeam(World world, double x, double y, double z){
super(world, x, y, z); // Does't use TextureAtlasSprite
this.setRBGColorF(1, 1, 1);
this.setMaxAge(3);
this.particleScale = 1;
}
@Override
public int getFXLayer(){
return 3;
}
@Override
protected void draw(Tessellator tessellator, double length, float partialTicks){
float scale = this.particleScale;
BufferBuilder buffer = tessellator.getBuffer();
GlStateManager.disableLighting();
OpenGlHelper.setLightmapTextureCoords(OpenGlHelper.lightmapTexUnit, 240f, 240f);
GlStateManager.pushMatrix();
// Enables texture tiling (Also used for guardian beam, beacon beam and ender crystal beam, amongst others)
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.rotate(Minecraft.getMinecraft().player.ticksExisted + partialTicks, 0, 0, 1);
Minecraft.getMinecraft().renderEngine.bindTexture(TEXTURE);
buffer.begin(GL11.GL_QUADS, DefaultVertexFormats.POSITION_TEX_COLOR);
float t = THICKNESS * scale;
float v1 = 2 * (particleAge + partialTicks) / particleMaxAge;
float v2 = v1 + (float)length * 2; // Multiply by 2 to make it squished like it is in vanilla
buffer.pos(-t, 0, 0) .tex(0, v1) .color(particleRed, particleGreen, particleBlue, 1).endVertex();
buffer.pos( t, 0, 0) .tex(0.5, v1) .color(particleRed, particleGreen, particleBlue, 1).endVertex();
buffer.pos( t, 0, length).tex(0.5, v2).color(particleRed, particleGreen, particleBlue, 1).endVertex();
buffer.pos(-t, 0, length).tex(0, v2).color(particleRed, particleGreen, particleBlue, 1).endVertex();
buffer.pos(0, -t, 0) .tex(0, v1) .color(particleRed, particleGreen, particleBlue, 1).endVertex();
buffer.pos(0, t, 0) .tex(0.5, v1) .color(particleRed, particleGreen, particleBlue, 1).endVertex();
buffer.pos(0, t, length).tex(0.5, v2).color(particleRed, particleGreen, particleBlue, 1).endVertex();
buffer.pos(0, -t, length).tex(0, v2).color(particleRed, particleGreen, particleBlue, 1).endVertex();
tessellator.draw();
// 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.popMatrix();
// Makes the rain go weird
//GlStateManager.enableLighting();
}
}
@@ -41,7 +41,7 @@ public class ParticleSphere extends ParticleWizardry {
float rotationXY, float rotationXZ){
// Copied from ParticleWizardry, needs to be here since we're not calling super
updateEntityLinking(partialTicks);
updateEntityLinking(viewer, partialTicks);
float x = (float)(this.prevPosX + (this.posX - this.prevPosX) * (double)partialTicks);
float y = (float)(this.prevPosY + (this.posY - this.prevPosY) * (double)partialTicks);
@@ -1,132 +0,0 @@
package electroblob.wizardry.client.particle;
import electroblob.wizardry.Wizardry;
import net.minecraft.client.Minecraft;
import net.minecraft.client.renderer.*;
import net.minecraft.client.renderer.GlStateManager.DestFactor;
import net.minecraft.client.renderer.GlStateManager.SourceFactor;
import net.minecraft.client.renderer.vertex.DefaultVertexFormats;
import net.minecraft.entity.Entity;
import net.minecraft.util.ResourceLocation;
import net.minecraft.util.math.MathHelper;
import net.minecraft.world.World;
import org.lwjgl.opengl.GL11;
//@SideOnly(Side.CLIENT)
public class ParticleSummon extends ParticleWizardry {
private static final ResourceLocation TEXTURE = new ResourceLocation(Wizardry.MODID, "textures/particle/summon.png");
private final boolean mirror;
public ParticleSummon(World world, double x, double y, double z){
super(world, x, y, z);
this.mirror = random.nextBoolean();
this.setMaxAge(10);
this.setGravity(false);
this.canCollide = false;
}
// @Override
// public void onUpdate(){
// super.onUpdate();
// if(this.particleAge > this.particleMaxAge/2) this.particleAlpha = 2f - 2f*(float)this.particleAge/(float)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(BufferBuilder buffer, Entity viewer, float partialTicks, float rotationX, float rotationZ,
float rotationYZ, float rotationXY, float rotationXZ){
// Copied from ParticleWizardry, needs to be here since we're not calling super
updateEntityLinking(partialTicks);
GlStateManager.pushMatrix();
GlStateManager.pushAttrib();
float scale = 0.6f;
GlStateManager.scale(scale, scale, scale);
if(mirror) GlStateManager.scale(-1, 1, 1);
GlStateManager.enableBlend();
GlStateManager.disableAlpha();
GlStateManager.disableCull();
GlStateManager.disableLighting();
GlStateManager.blendFunc(SourceFactor.SRC_ALPHA, DestFactor.ONE);
// Makes the particle colour add to the colour of the texture pixels, rather than the default multiplying
GlStateManager.glTexEnvi(GL11.GL_TEXTURE_ENV, GL11.GL_TEXTURE_ENV_MODE, GL11.GL_ADD);
OpenGlHelper.setLightmapTextureCoords(OpenGlHelper.lightmapTexUnit, 240f, 240f);
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);
// Does the texture translation wrapping thing (the cool stuff)
// GlStateManager.matrixMode(GL11.GL_TEXTURE);
// GlStateManager.loadIdentity();
//
// GlStateManager.translate((this.particleAge + partialTicks)/(float)this.particleMaxAge * -2, 0, 0);
//
// GlStateManager.matrixMode(GL11.GL_MODELVIEW);
RenderHelper.disableStandardItemLighting();
Minecraft.getMinecraft().getTextureManager().bindTexture(TEXTURE);
buffer.begin(GL11.GL_TRIANGLE_STRIP, DefaultVertexFormats.POSITION_TEX_COLOR);
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);
// Increases from 0 to 1 in steps of 0.125 evenly throughout the particle's lifetime
float f = 0.125f * MathHelper.floor((float)this.particleAge/(float)this.particleMaxAge * 8 - 0.000001f);
float g = f + 0.125f;
float hrepeat = 1;
float yScale = 3f;
this.setRBGColorF(1, 1, 1);
buffer.pos(x-1, y, z-1).tex(0, g).color(particleRed, particleGreen, particleBlue, particleAlpha).endVertex();
buffer.pos(x-1, y+yScale, z-1).tex(0, f).color(particleRed, particleGreen, particleBlue, particleAlpha).endVertex();
buffer.pos(x+1, y, z-1).tex(0.25*hrepeat, g).color(particleRed, particleGreen, particleBlue, particleAlpha).endVertex();
buffer.pos(x+1, y+yScale, z-1).tex(0.25*hrepeat, f).color(particleRed, particleGreen, particleBlue, particleAlpha).endVertex();
buffer.pos(x+1, y, z+1).tex(0.5*hrepeat, g).color(particleRed, particleGreen, particleBlue, particleAlpha).endVertex();
buffer.pos(x+1, y+yScale, z+1).tex(0.5*hrepeat, f).color(particleRed, particleGreen, particleBlue, particleAlpha).endVertex();
buffer.pos(x-1, y, z+1).tex(0.75*hrepeat, g).color(particleRed, particleGreen, particleBlue, particleAlpha).endVertex();
buffer.pos(x-1, y+yScale, z+1).tex(0.75*hrepeat, f).color(particleRed, particleGreen, particleBlue, particleAlpha).endVertex();
buffer.pos(x-1, y, z-1).tex(hrepeat, g).color(particleRed, particleGreen, particleBlue, particleAlpha).endVertex();
buffer.pos(x-1, y+yScale, z-1).tex(hrepeat, f).color(particleRed, particleGreen, particleBlue, particleAlpha).endVertex();
Tessellator.getInstance().draw();
// Undoes the texture transformations
// GlStateManager.matrixMode(GL11.GL_TEXTURE);
// GlStateManager.loadIdentity();
// GlStateManager.matrixMode(GL11.GL_MODELVIEW);
GlStateManager.disableBlend();
GlStateManager.enableAlpha();
GlStateManager.enableCull();
GlStateManager.enableLighting();
// Reverses the colour addition change from before
GlStateManager.glTexEnvi(GL11.GL_TEXTURE_ENV, GL11.GL_TEXTURE_ENV_MODE, GL11.GL_MODULATE);
GlStateManager.popAttrib();
GlStateManager.popMatrix();
}
}
@@ -1,6 +1,7 @@
package electroblob.wizardry.client.particle;
import electroblob.wizardry.Wizardry;
import net.minecraft.client.Minecraft;
import net.minecraft.client.renderer.BufferBuilder;
import net.minecraft.client.renderer.GlStateManager;
import net.minecraft.client.renderer.Tessellator;
@@ -15,6 +16,8 @@ import javax.annotation.Nullable;
/** Superclass for particles with a second target entity or target position. */
public abstract class ParticleTargeted extends ParticleWizardry {
private static final double THIRD_PERSON_AXIAL_OFFSET = 1.2;
protected double targetX;
protected double targetY;
protected double targetZ;
@@ -73,16 +76,26 @@ public abstract class ParticleTargeted extends ParticleWizardry {
float rotationXY, float rotationXZ){
// Copied from ParticleWizardry, needs to be here since we're not calling super
updateEntityLinking(partialTicks);
updateEntityLinking(viewer, partialTicks);
float x = (float)(this.prevPosX + (this.posX - this.prevPosX) * (double)partialTicks);
float y = (float)(this.prevPosY + (this.posY - this.prevPosY) * (double)partialTicks);
float z = (float)(this.prevPosZ + (this.posZ - this.prevPosZ) * (double)partialTicks);
// Translates the particle a short distance in front of the entity
if(this.entity != null && this.shouldApplyOriginOffset()){
if(this.entity != viewer || Minecraft.getMinecraft().gameSettings.thirdPersonView != 0){
Vec3d look = entity.getLook(partialTicks).scale(THIRD_PERSON_AXIAL_OFFSET);
x += look.x;
y += look.y;
z += look.z;
}
}
if(this.target != null){
this.targetX = this.target.prevPosX + (this.target.posX - this.target.prevPosX) * partialTicks;
double correction = this.target.getEntityBoundingBox().minY - this.target.posY;
double correction = this.target.posY - this.target.posY;
this.targetY = this.target.prevPosY + (this.target.posY - this.target.prevPosY) * partialTicks
+ target.height/2 + correction;
this.targetZ = this.target.prevPosZ + (this.target.posZ - this.target.prevPosZ) * partialTicks;
@@ -133,6 +146,12 @@ public abstract class ParticleTargeted extends ParticleWizardry {
GlStateManager.popMatrix();
}
/** Returns whether the origin of this particle should be moved a short distance in front of the entity it is
* linked to, if any. */
protected boolean shouldApplyOriginOffset(){
return true;
}
/** Called from {@link ParticleTargeted#renderParticle(BufferBuilder, Entity, float, float, float, float, float, float)},
* once the appropriate calculations and transformations have been applied, to actually render the particle. Subclasses
* override this <i>instead</i> of overriding {@code renderParticle} directly, and inside render the particle <b>along
@@ -46,6 +46,11 @@ public class ParticleVine extends ParticleTargeted {
buffer.begin(GL11.GL_QUADS, DefaultVertexFormats.PARTICLE_POSITION_TEX_COLOR_LMAP);
}
@Override
protected boolean shouldApplyOriginOffset(){
return false;
}
@Override
protected void draw(Tessellator tessellator, double length, float partialTicks){
@@ -3,7 +3,7 @@ package electroblob.wizardry.client.particle;
import electroblob.wizardry.Wizardry;
import electroblob.wizardry.client.ClientProxy;
import electroblob.wizardry.entity.ICustomHitbox;
import electroblob.wizardry.util.WizardryUtilities;
import electroblob.wizardry.util.EntityUtils;
import net.minecraft.client.Minecraft;
import net.minecraft.client.particle.Particle;
import net.minecraft.client.renderer.BufferBuilder;
@@ -27,7 +27,7 @@ import java.util.stream.Collectors;
/**
* Abstract superclass for all of wizardry's particles. This replaces {@code ParticleCustomTexture} (the functionality of
* which is no longer necessary since wizardry now uses {@code TextureAtlasSprite}s to do the rendering), and fits into
* {@code ParticleBuilder} by exposing all the necessary variables through getters, allowing them to be set on the fly
* {@code ParticleBuilder} by exposing all the necessary variables through setters, allowing them to be set on the fly
* rather than needing to be passed into the constructor.
* <p></p>
* The new system is as follows:
@@ -206,7 +206,7 @@ public abstract class ParticleWizardry extends Particle {
this.entity = entity;
// Set these to the correct values
if(entity != null){
this.setPosition(this.entity.posX + relativeX, this.entity.getEntityBoundingBox().minY
this.setPosition(this.entity.posX + relativeX, this.entity.posY
+ relativeY, this.entity.posZ + relativeZ);
this.prevPosX = this.posX;
this.prevPosY = this.posY;
@@ -336,7 +336,7 @@ public abstract class ParticleWizardry extends Particle {
public void renderParticle(BufferBuilder buffer, Entity viewer, float partialTicks, float lookZ, float lookY,
float lookX, float lookXY, float lookYZ){
updateEntityLinking(partialTicks);
updateEntityLinking(viewer, partialTicks);
if(Float.isNaN(this.yaw) || Float.isNaN(this.pitch)){
// Normal behaviour (rotates to face the viewer)
@@ -369,12 +369,18 @@ public abstract class ParticleWizardry extends Particle {
super.renderParticle(buffer, viewer, partialTicks, rotationX, rotationY, rotationZ, rotationYZ, rotationXY);
}
protected void updateEntityLinking(float partialTicks){
protected void updateEntityLinking(Entity viewer, float partialTicks){
// TODO: Still not working, it seems this bug was a thing back in 4.2.x anyway
if(this.entity != null){
// This is kind of cheating but we know it's always a constant velocity so it works fine
prevPosX = posX + entity.prevPosX - entity.posX - relativeMotionX * (1-partialTicks);
prevPosY = posY + entity.prevPosY - entity.posY - relativeMotionY * (1-partialTicks);
prevPosZ = posZ + entity.prevPosZ - entity.posZ - relativeMotionZ * (1-partialTicks);
}else if(this.getFXLayer() == 3){
// Not sure why, but when fx layer is 3, the interp pos is wrong when not linked to an entity
interpPosX = viewer.lastTickPosX + (viewer.posX - viewer.lastTickPosX) * (double)partialTicks;
interpPosY = viewer.lastTickPosY + (viewer.posY - viewer.lastTickPosY) * (double)partialTicks;
interpPosZ = viewer.lastTickPosZ + (viewer.posZ - viewer.lastTickPosZ) * (double)partialTicks;
}
}
@@ -467,12 +473,12 @@ public abstract class ParticleWizardry extends Particle {
double searchRadius = 20;
List<Entity> nearbyEntities = WizardryUtilities.getEntitiesWithinRadius(searchRadius, this.posX,
List<Entity> nearbyEntities = EntityUtils.getEntitiesWithinRadius(searchRadius, this.posX,
this.posY, this.posZ, world, Entity.class);
nearbyEntities.removeIf(e -> !(e instanceof ICustomHitbox && ((ICustomHitbox)e).contains(new Vec3d(this.posX, this.posY, this.posZ))));
if(nearbyEntities.size() > 0) this.setExpired();
if(nearbyEntities.stream().anyMatch(e -> e instanceof ICustomHitbox
&& ((ICustomHitbox)e).calculateIntercept(new Vec3d(posX, posY, posZ),
new Vec3d(prevPosX, prevPosY, prevPosZ), 0) != null)) this.setExpired();
}
@@ -547,7 +553,7 @@ public abstract class ParticleWizardry extends Particle {
/** Internal overload for {@link ParticleWizardry#generateTextures(String, String, int, int)} which uses wizardry's
* mod ID automatically. */
public static ResourceLocation[][] generateTextures(String stem, int m, int n){
static ResourceLocation[][] generateTextures(String stem, int m, int n){
return generateTextures(Wizardry.MODID, stem, m, n);
}
@@ -1,176 +0,0 @@
package electroblob.wizardry.client.renderer;
import electroblob.wizardry.Wizardry;
import electroblob.wizardry.block.BlockStatue;
import electroblob.wizardry.registry.WizardryPotions;
import net.minecraft.client.Minecraft;
import net.minecraft.client.model.ModelBase;
import net.minecraft.client.model.ModelBiped;
import net.minecraft.client.model.ModelPlayer;
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.RenderPlayer;
import net.minecraft.client.renderer.entity.layers.LayerRenderer;
import net.minecraft.entity.Entity;
import net.minecraft.entity.EntityLivingBase;
import net.minecraft.util.ResourceLocation;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.MathHelper;
import org.lwjgl.opengl.GL11;
/**
* Layer used to render the frost texture on a creature with the frostbite effect. Handles dynamic tiling of the texture.
*
* @author Electroblob
* @since Wizardry 1.2
*/
public class LayerFrost implements LayerRenderer<EntityLivingBase> {
protected ModelBase model;
private final RenderLivingBase<?> renderer;
private static final ResourceLocation texture = new ResourceLocation(Wizardry.MODID, "textures/entity/frost_overlay.png");
public static void initialiseLayers(){
for(Render<? extends Entity> renderer : Minecraft.getMinecraft().getRenderManager().entityRenderMap.values()){
// Because the zombie classes are now split properly, their renderers play nicely like everything else.
if(renderer instanceof RenderLivingBase){
// Adds a frost 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<?>)renderer).addLayer(new LayerFrost((RenderLivingBase<?>)renderer));
}
}
for(RenderPlayer renderer : Minecraft.getMinecraft().getRenderManager().getSkinMap().values()){
renderer.addLayer(new LayerFrost(renderer));
}
}
public LayerFrost(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.isPotionActive(WizardryPotions.frost) || entity.getEntityData().getBoolean(BlockStatue.FROZEN_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);
// Frost texture
GlStateManager.enableBlend();
GlStateManager.blendFunc(SourceFactor.SRC_ALPHA, DestFactor.ONE_MINUS_SRC_ALPHA);
this.renderer.bindTexture(texture);
this.renderEntityModel(entity, limbSwing, limbSwingAmount, partialTicks, ageInTicks, netHeadYaw,
headPitch, scale);
GlStateManager.disableBlend();
}
}
private int getBlockBrightnessForEntity(Entity entity, float partialTicks){
BlockPos.MutableBlockPos pos = new BlockPos.MutableBlockPos(MathHelper.floor(entity.posX), 0,
MathHelper.floor(entity.posZ));
if(entity.world.isBlockLoaded(pos)){
pos.setY(MathHelper.floor(entity.posY + (double)entity.getEyeHeight()));
return entity.world.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)
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.
// NOTE: You can do all sorts of fun stuff with this, just by applying transformations in the 2D texture space.
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.size() >= 1 && 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);
boolean headWearHidden = false;
boolean bodyWearHidden = false;
boolean leftArmWearHidden = false;
boolean rightArmWearHidden = false;
boolean leftLegWearHidden = false;
boolean rightLegWearHidden = false;
// Hides the hat layer for bipeds
if(this.model instanceof ModelBiped){
headWearHidden = ((ModelBiped)this.model).bipedHeadwear.isHidden;
((ModelBiped)this.model).bipedHeadwear.isHidden = true;
}
if(this.model instanceof ModelPlayer){
bodyWearHidden = ((ModelPlayer)this.model).bipedBodyWear.isHidden;
leftArmWearHidden = ((ModelPlayer)this.model).bipedLeftArmwear.isHidden;
rightArmWearHidden = ((ModelPlayer)this.model).bipedRightArmwear.isHidden;
leftLegWearHidden = ((ModelPlayer)this.model).bipedLeftLegwear.isHidden;
rightLegWearHidden = ((ModelPlayer)this.model).bipedRightLegwear.isHidden;
((ModelPlayer)this.model).bipedBodyWear.isHidden = true;
((ModelPlayer)this.model).bipedLeftArmwear.isHidden = true;
((ModelPlayer)this.model).bipedRightArmwear.isHidden = true;
((ModelPlayer)this.model).bipedLeftLegwear.isHidden = true;
((ModelPlayer)this.model).bipedRightLegwear.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 = headWearHidden;
if(this.model instanceof ModelPlayer){
((ModelPlayer)this.model).bipedBodyWear.isHidden = bodyWearHidden;
((ModelPlayer)this.model).bipedLeftArmwear.isHidden = leftArmWearHidden;
((ModelPlayer)this.model).bipedRightArmwear.isHidden = rightArmWearHidden;
((ModelPlayer)this.model).bipedLeftLegwear.isHidden = leftLegWearHidden;
((ModelPlayer)this.model).bipedRightLegwear.isHidden = rightLegWearHidden;
}
// Undoes the texture scaling
GlStateManager.matrixMode(GL11.GL_TEXTURE);
GlStateManager.loadIdentity();
GlStateManager.matrixMode(GL11.GL_MODELVIEW);
GlStateManager.popMatrix();
}
@Override
public boolean shouldCombineTextures(){
return false;
}
}
@@ -1,151 +0,0 @@
package electroblob.wizardry.client.renderer;
import electroblob.wizardry.block.BlockStatue;
import electroblob.wizardry.client.ClientProxy;
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.layers.LayerRenderer;
import net.minecraft.entity.Entity;
import net.minecraft.entity.EntityLivingBase;
import net.minecraft.util.ResourceLocation;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.MathHelper;
import org.lwjgl.opengl.GL11;
/**
* Layer used to render the stone texture on a petrified creature. Handles dynamic tiling of the stone texture.
*
* @author Electroblob
* @since Wizardry 1.2
*/
public class LayerStone implements LayerRenderer<EntityLivingBase> {
protected ModelBase model;
private final RenderLivingBase<?> renderer;
private static final ResourceLocation texture = new ResourceLocation("textures/blocks/stone.png");
public static void initialiseLayers(){
for(Render<? extends Entity> renderer : Minecraft.getMinecraft().getRenderManager().entityRenderMap.values()){
// Because the zombie classes are now split properly, their renderers play nicely like everything else.
if(renderer 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<?>)renderer).addLayer(new LayerStone((RenderLivingBase<?>)renderer));
}
}
}
public LayerStone(RenderLivingBase<?> renderer){
this.renderer = renderer;
this.model = renderer.getMainModel();
}
// FIXME: Does not work with zombie pigmen, I have no idea why.
// I believe the issue is with the TESR actually, since LayerFrost works fine
@Override
public void doRenderLayer(EntityLivingBase entity, float limbSwing, float limbSwingAmount, float partialTicks,
float ageInTicks, float netHeadYaw, float headPitch, float scale){
if(entity.getEntityData().getBoolean(BlockStatue.PETRIFIED_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(entity.posX), 0,
MathHelper.floor(entity.posZ));
if(entity.world.isBlockLoaded(pos)){
pos.setY(MathHelper.floor(entity.posY + (double)entity.getEyeHeight()));
return entity.world.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)
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.
// NOTE: You can do all sorts of fun stuff with this, just by applying transformations in the 2D texture space.
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);
// 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;
}
}
@@ -1,14 +1,14 @@
package electroblob.wizardry.client.renderer;
/** Instances of this class can calculate their distance from the camera viewpoint and sort themselves accordingly. */
class RayHelper implements Comparable<RayHelper> {
public class RayHelper implements Comparable<RayHelper> {
int ordinal;
double x1, y1, z1;
double x2, y2, z2;
public double x1, y1, z1;
public double x2, y2, z2;
double offsetX, offsetY, offsetZ;
RayHelper(int ordinal, double x1, double y1, double z1, double x2, double y2, double z2, double offsetX,
public RayHelper(int ordinal, double x1, double y1, double z1, double x2, double y2, double z2, double offsetX,
double offsetY, double offsetZ){
this.ordinal = ordinal;
this.x1 = x1;
@@ -37,13 +37,7 @@ class RayHelper implements Comparable<RayHelper> {
@Override
public int compareTo(RayHelper ray){
if(this.getDistanceFromViewpoint() > ray.getDistanceFromViewpoint()){
return -1;
}else if(this.getDistanceFromViewpoint() < ray.getDistanceFromViewpoint()){
return 1;
}else{
return 0;
}
return Double.compare(ray.getDistanceFromViewpoint(), this.getDistanceFromViewpoint());
}
}
@@ -1,186 +0,0 @@
package electroblob.wizardry.client.renderer;
import electroblob.wizardry.entity.construct.EntityFireRing;
import net.minecraft.client.Minecraft;
import net.minecraft.client.renderer.BufferBuilder;
import net.minecraft.client.renderer.GlStateManager;
import net.minecraft.client.renderer.OpenGlHelper;
import net.minecraft.client.renderer.Tessellator;
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.init.Blocks;
import net.minecraft.util.ResourceLocation;
import org.lwjgl.opengl.GL11;
public class RenderFireRing extends Render<EntityFireRing> {
private final ResourceLocation texture;
private float scale = 1.0f;
public RenderFireRing(RenderManager renderManager, ResourceLocation texture, float scale){
super(renderManager);
this.texture = texture;
this.scale = scale;
}
@Override
public void doRender(EntityFireRing entity, double par2, double par4, double par6, float par8, float par9){
GlStateManager.pushMatrix();
GlStateManager.enableBlend();
GlStateManager.disableLighting();
OpenGlHelper.setLightmapTextureCoords(OpenGlHelper.lightmapTexUnit, 240, 240);
GlStateManager.blendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA);
float yOffset = 0;
GlStateManager.translate((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);
GlStateManager.scale(scale, scale, scale);
Tessellator tessellator = Tessellator.getInstance();
BufferBuilder 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.draw();
GlStateManager.disableBlend();
GlStateManager.enableLighting();
GlStateManager.disableRescaleNormal();
GlStateManager.popMatrix();
// Fire
GlStateManager.disableLighting();
TextureAtlasSprite icon = Minecraft.getMinecraft().getBlockRendererDispatcher()
.getModelForState(Blocks.FIRE.getDefaultState()).getParticleTexture();
int sides = 16;
float height = 1.0f;
for(int k = 0; k < sides; k++){
GlStateManager.pushMatrix();
GlStateManager.translate((float)par2, (float)par4 + 0.05f, (float)par6);
float f1 = 1.0f;
GlStateManager.scale(f1, f1, f1);
float f2 = 0.5F;
float f3 = 0.0F;
float f4 = 0.2f;
float f5 = (float)(entity.posY - entity.getEntityBoundingBox().minY);
GlStateManager.color(1.0F, 1.0F, 1.0F, 1.0F);
float f61 = 0.0F;
int i = 0;
GlStateManager.rotate((360f / (float)sides) * k, 0, 1, 0);
GlStateManager.translate(0, 0, -2.3f);
buffer.begin(GL11.GL_QUADS, DefaultVertexFormats.POSITION_TEX);
while(f4 > 0.0F){
this.bindTexture(TextureMap.LOCATION_BLOCKS_TEXTURE);
float f71 = icon.getMinU();
float f81 = icon.getMinV();
float f9 = icon.getMaxU();
float f10 = icon.getMaxV();
if(i / 2 % 2 == 0){
float f11 = f9;
f9 = f71;
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();
f4 -= 0.45F;
f5 -= 0.45F;
f2 *= 0.9F;
f61 += 0.03F;
++i;
}
tessellator.draw();
GlStateManager.popMatrix();
}
for(int k = 0; k < sides; k++){
GlStateManager.pushMatrix();
GlStateManager.translate((float)par2, (float)par4 + 0.05f, (float)par6);
float f1 = 1.0f;
GlStateManager.scale(f1, f1, f1);
float f2 = 0.5F;
float f3 = 0.0F;
float f4 = 0.2f;
float f5 = (float)(entity.posY - entity.getEntityBoundingBox().minY);
GlStateManager.color(1.0F, 1.0F, 1.0F, 1.0F);
float f61 = 0.0F;
int i = 0;
GlStateManager.rotate((360f / (float)sides) * k, 0, 1, 0);
GlStateManager.translate(0, 0, 2.3f);
buffer.begin(GL11.GL_QUADS, DefaultVertexFormats.POSITION_TEX);
while(f4 > 0.0F){
this.bindTexture(TextureMap.LOCATION_BLOCKS_TEXTURE);
float f71 = icon.getMinU();
float f81 = icon.getMinV();
float f9 = icon.getMaxU();
float f10 = icon.getMaxV();
if(i / 2 % 2 == 0){
float f11 = f9;
f9 = f71;
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();
f4 -= 0.45F;
f5 -= 0.45F;
f2 *= 0.9F;
f61 += 0.03F;
++i;
}
tessellator.draw();
GlStateManager.popMatrix();
}
GlStateManager.enableLighting();
}
@Override
protected ResourceLocation getEntityTexture(EntityFireRing entity){
return null;
}
}
@@ -1,9 +1,9 @@
package electroblob.wizardry.client.renderer;
package electroblob.wizardry.client.renderer.effect;
import electroblob.wizardry.Wizardry;
import electroblob.wizardry.potion.PotionContainment;
import electroblob.wizardry.registry.WizardryPotions;
import electroblob.wizardry.util.WizardryUtilities;
import electroblob.wizardry.util.GeometryUtils;
import net.minecraft.client.Minecraft;
import net.minecraft.client.renderer.BufferBuilder;
import net.minecraft.client.renderer.GlStateManager;
@@ -24,14 +24,14 @@ import org.lwjgl.opengl.GL11;
@Mod.EventBusSubscriber(Side.CLIENT)
public class RenderContainmentField {
private static final ResourceLocation[] textures = new ResourceLocation[8];
private static final ResourceLocation[] TEXTURES = new ResourceLocation[8];
private static final float ANIMATION_SPEED = 0.004f;
private static final float FADE_DISTANCE_SQUARED = 15;
static {
for(int i=0; i<textures.length; i++){
textures[i] = new ResourceLocation(Wizardry.MODID, "textures/entity/containment_field_" + i + ".png");
for(int i = 0; i< TEXTURES.length; i++){
TEXTURES[i] = new ResourceLocation(Wizardry.MODID, "textures/environment/containment_field_" + i + ".png");
}
}
@@ -42,7 +42,7 @@ public class RenderContainmentField {
if(player.isPotionActive(WizardryPotions.containment)){
Vec3d centre = WizardryUtilities.getCentre(NBTUtil.getPosFromTag(player.getEntityData().getCompoundTag(PotionContainment.ENTITY_TAG)));
Vec3d centre = GeometryUtils.getCentre(NBTUtil.getPosFromTag(player.getEntityData().getCompoundTag(PotionContainment.ENTITY_TAG)));
float r = PotionContainment.getContainmentDistance(player.getActivePotionEffect(WizardryPotions.containment).getAmplifier());
GlStateManager.pushMatrix();
@@ -65,7 +65,7 @@ public class RenderContainmentField {
GlStateManager.color(1, 1, 1, 1);
Minecraft.getMinecraft().renderEngine.bindTexture(textures[(player.ticksExisted % (textures.length * 2))/2]);
Minecraft.getMinecraft().renderEngine.bindTexture(TEXTURES[(player.ticksExisted % (TEXTURES.length * 2))/2]);
Tessellator tessellator = Tessellator.getInstance();
@@ -245,24 +245,4 @@ public class RenderContainmentField {
buffer.pos(x, y, z).tex(u, v).color(1, 1, 1, f * alpha).endVertex();
}
// Useful to keep this mapping in case I ever want to draw cubes again
// Vec3d[] vertices = WizardryUtilities.getVertices(entity.getEntityBoundingBox().offset(entity.getPositionVector().scale(-1)));
//
// // Outside
// drawFace(tessellator, vertices[0], vertices[1], vertices[3], vertices[2], u1, v1, u2, v2); // Bottom
// drawFace(tessellator, vertices[6], vertices[7], vertices[2], vertices[3], u1, v1, u2, v2); // South
// drawFace(tessellator, vertices[5], vertices[6], vertices[1], vertices[2], u1, v1, u2, v2); // East
// drawFace(tessellator, vertices[4], vertices[5], vertices[0], vertices[1], u1, v1, u2, v2); // North
// drawFace(tessellator, vertices[7], vertices[4], vertices[3], vertices[0], u1, v1, u2, v2); // West
// drawFace(tessellator, vertices[5], vertices[4], vertices[6], vertices[7], u1, v1, u2, v2); // Top
//
// // Inside
// drawFace(tessellator, vertices[1], vertices[0], vertices[2], vertices[3], u1, v1, u2, v2); // Bottom
// drawFace(tessellator, vertices[7], vertices[6], vertices[3], vertices[2], u1, v1, u2, v2); // South
// drawFace(tessellator, vertices[6], vertices[5], vertices[2], vertices[1], u1, v1, u2, v2); // East
// drawFace(tessellator, vertices[5], vertices[4], vertices[1], vertices[0], u1, v1, u2, v2); // North
// drawFace(tessellator, vertices[4], vertices[7], vertices[0], vertices[3], u1, v1, u2, v2); // West
// drawFace(tessellator, vertices[4], vertices[5], vertices[7], vertices[6], u1, v1, u2, v2); // Top
}
@@ -0,0 +1,191 @@
package electroblob.wizardry.client.renderer.effect;
import electroblob.wizardry.Wizardry;
import electroblob.wizardry.block.BlockReceptacle;
import electroblob.wizardry.constants.Element;
import electroblob.wizardry.misc.DonationPerksHandler;
import electroblob.wizardry.util.GeometryUtils;
import net.minecraft.client.Minecraft;
import net.minecraft.client.renderer.BufferBuilder;
import net.minecraft.client.renderer.GlStateManager;
import net.minecraft.client.renderer.OpenGlHelper;
import net.minecraft.client.renderer.Tessellator;
import net.minecraft.client.renderer.vertex.DefaultVertexFormats;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.util.ResourceLocation;
import net.minecraft.util.math.AxisAlignedBB;
import net.minecraft.util.math.MathHelper;
import net.minecraft.util.math.Vec3d;
import net.minecraftforge.client.event.RenderPlayerEvent;
import net.minecraftforge.fml.common.Mod.EventBusSubscriber;
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
import net.minecraftforge.fml.relauncher.Side;
import org.lwjgl.opengl.GL11;
@EventBusSubscriber(Side.CLIENT)
public class RenderDonationPerks {
private static final ResourceLocation FLARE_TEXTURE = new ResourceLocation(Wizardry.MODID, "textures/entity/totem/flare.png");
private static final ResourceLocation[] CUBE_TEXTURES = new ResourceLocation[14];
private static final float BOBBING_PERIOD = 25;
private static final float ROTATION_PERIOD = 60;
private static final double ROTATION_RADIUS = 0.7;
private static final double HEIGHT_FRACTION = 0.8;
private static final double BOBBING_DISTANCE = 0.2;
private static final double FOLLOW_DISTANCE = 1.4;
static {
for(int i = 0; i< CUBE_TEXTURES.length; i++) CUBE_TEXTURES[i] = new ResourceLocation(Wizardry.MODID, "textures/entity/totem/cube_" + i + ".png");
}
@SubscribeEvent
public static void onRenderPlayerEvent(RenderPlayerEvent.Post event){
EntityPlayer player = event.getEntityPlayer();
float partialTicks = event.getPartialRenderTick();
Element element = DonationPerksHandler.getElement(player);
if(element != null){
int c1 = BlockReceptacle.PARTICLE_COLOURS.get(element)[0];
int r1 = (c1 & 0xFF0000) >> 16;
int g1 = (c1 & 0xFF00) >> 8;
int b1 = (c1 & 0xFF);
int c2 = BlockReceptacle.PARTICLE_COLOURS.get(element)[2];
int r2 = (c2 & 0xFF0000) >> 16;
int g2 = (c2 & 0xFF00) >> 8;
int b2 = (c2 & 0xFF);
Tessellator tessellator = Tessellator.getInstance();
GlStateManager.pushMatrix();
GlStateManager.enableBlend();
GlStateManager.disableLighting();
OpenGlHelper.setLightmapTextureCoords(OpenGlHelper.lightmapTexUnit, 240, 240);
GlStateManager.depthMask(false);
GlStateManager.disableCull();
double x1 = player.chasingPosX - player.posX;
double y1 = player.chasingPosY - player.posY;
double z1 = player.chasingPosZ - player.posZ;
double x2 = player.prevChasingPosX - player.prevPosX;
double y2 = player.prevChasingPosY - player.prevPosY;
double z2 = player.prevChasingPosZ - player.prevPosZ;
double dx = x2 + (x1 - x2) * partialTicks;
double dy = y2 + (y1 - y2) * partialTicks;
double dz = z2 + (z1 - z2) * partialTicks;
float t = player.ticksExisted + partialTicks;
float hMoveFraction = MathHelper.clamp((float)(dx * dx + dz * dz) / 0.3f, 0, 1);
float vMoveFraction = MathHelper.clamp((float)(dy * dy) / 0.3f, 0, 1);
double x = event.getX() + ROTATION_RADIUS * MathHelper.sin(t / ROTATION_PERIOD) * (1 - hMoveFraction) + FOLLOW_DISTANCE * dx;
double y = event.getY() + HEIGHT_FRACTION * player.height + BOBBING_DISTANCE * MathHelper.sin(t / BOBBING_PERIOD) * (1 - vMoveFraction) + FOLLOW_DISTANCE * dy;
double z = event.getZ() + ROTATION_RADIUS * MathHelper.cos(t / ROTATION_PERIOD) * (1 - hMoveFraction) + FOLLOW_DISTANCE * dz;
GlStateManager.translate(x, y, z);
GlStateManager.scale(0.4, 0.4, 0.4);
// Makes the colour add to the colour of the texture pixels, rather than the default multiplying
GlStateManager.glTexEnvi(GL11.GL_TEXTURE_ENV, GL11.GL_TEXTURE_ENV_MODE, GL11.GL_ADD);
GlStateManager.color(r1/255f, g1/255f, b1/255f);
drawFlare(tessellator);
// Reverses the colour addition change
GlStateManager.glTexEnvi(GL11.GL_TEXTURE_ENV, GL11.GL_TEXTURE_ENV_MODE, GL11.GL_MODULATE);
GlStateManager.color(r2/255f, g2/255f, b2/255f);
drawCube(player, tessellator, partialTicks);
GlStateManager.color(1, 1, 1,1);
GlStateManager.disableBlend();
GlStateManager.enableLighting();
GlStateManager.depthMask(true);
GlStateManager.enableCull();
GlStateManager.popMatrix();
}
}
private static void drawFlare(Tessellator tessellator){
BufferBuilder buffer = tessellator.getBuffer();
GlStateManager.pushMatrix();
GlStateManager.blendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA);
Minecraft.getMinecraft().renderEngine.bindTexture(FLARE_TEXTURE);
// 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);
float r = 0.5f;
buffer.pos(-r, r, 0).tex(0, 0).endVertex();
buffer.pos( r, r, 0).tex(1, 0).endVertex();
buffer.pos( r, -r, 0).tex(1, 1).endVertex();
buffer.pos(-r, -r, 0).tex(0, 1).endVertex();
tessellator.draw();
GlStateManager.popMatrix();
}
private static void drawCube(EntityPlayer player, Tessellator tessellator, float partialTicks){
BufferBuilder buffer = tessellator.getBuffer();
GlStateManager.pushMatrix();
float age = player.ticksExisted + partialTicks;
float rotationSpeed = 2;
GlStateManager.rotate(age * rotationSpeed/2, 0.0F, 1.0F, 0.0F);
GlStateManager.rotate(60.0F, 0.7071F, 0.0F, 0.7071F);
GlStateManager.rotate(age * rotationSpeed, 0.0F, 1.0F, 0.0F);
GlStateManager.scale(0.5, 0.5, 0.5);
GlStateManager.blendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE);
Minecraft.getMinecraft().renderEngine.bindTexture(CUBE_TEXTURES[player.ticksExisted % CUBE_TEXTURES.length]);
Vec3d[] vertices = GeometryUtils.getVertices(new AxisAlignedBB(-0.5, -0.5, -0.5, 0.5, 0.5, 0.5));
buffer.begin(GL11.GL_QUADS, DefaultVertexFormats.POSITION_TEX);
// Outside
drawFace(buffer, vertices[0], vertices[1], vertices[3], vertices[2], 0.5f, 0, 0.75f, 0.5f); // Bottom
drawFace(buffer, vertices[6], vertices[7], vertices[2], vertices[3], 0.75f, 0.5f, 1, 1); // South
drawFace(buffer, vertices[5], vertices[6], vertices[1], vertices[2], 0, 0.5f, 0.25f, 1); // East
drawFace(buffer, vertices[4], vertices[5], vertices[0], vertices[1], 0.25f, 0.5f, 0.5f, 1); // North
drawFace(buffer, vertices[7], vertices[4], vertices[3], vertices[0], 0.5f, 0.5f, 0.75f, 1); // West
drawFace(buffer, vertices[5], vertices[4], vertices[6], vertices[7], 0.25f, 0, 0.5f, 0.5f); // Top
tessellator.draw();
GlStateManager.popMatrix();
}
private static void drawFace(BufferBuilder buffer, Vec3d topLeft, Vec3d topRight, Vec3d bottomLeft, Vec3d bottomRight, float u1, float v1, float u2, float v2){
buffer.pos(topLeft.x, topLeft.y, topLeft.z).tex(u1, v1).endVertex();
buffer.pos(topRight.x, topRight.y, topRight.z).tex(u2, v1).endVertex();
buffer.pos(bottomRight.x, bottomRight.y, bottomRight.z).tex(u2, v2).endVertex();
buffer.pos(bottomLeft.x, bottomLeft.y, bottomLeft.z).tex(u1, v2).endVertex();
}
}
@@ -0,0 +1,45 @@
package electroblob.wizardry.client.renderer.effect;
import electroblob.wizardry.registry.WizardryPotions;
import net.minecraft.client.renderer.GlStateManager;
import net.minecraft.client.renderer.GlStateManager.DestFactor;
import net.minecraft.client.renderer.GlStateManager.SourceFactor;
import net.minecraftforge.client.event.RenderLivingEvent;
import net.minecraftforge.fml.common.Mod.EventBusSubscriber;
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
import net.minecraftforge.fml.relauncher.Side;
import java.util.Random;
@EventBusSubscriber(Side.CLIENT)
public class RenderMirage {
private static final int BLINK_PERIOD_1 = 47; // Use two prime numbers to make it look as chaotic as possible
private static final int BLINK_PERIOD_2 = 71;
private static final double MAX_OFFSET = 3;
private static final Random random = new Random(); // Seed will be set later
@SubscribeEvent
public static void onRenderLivingEvent(RenderLivingEvent.Pre<?> event){
if(event.getEntity().isPotionActive(WizardryPotions.mirage)){
random.setSeed(event.getEntity().ticksExisted / BLINK_PERIOD_1
+ event.getEntity().ticksExisted / BLINK_PERIOD_2);
GlStateManager.pushMatrix();
GlStateManager.blendFunc(SourceFactor.SRC_ALPHA, DestFactor.ONE); // GhoOOoOostly OooOOOo00oOOo
GlStateManager.color(1, 1, 1, 0.5f);
GlStateManager.translate((random.nextDouble() * 2 - 1) * MAX_OFFSET,
(random.nextDouble() * 2 - 1) * MAX_OFFSET, (random.nextDouble() * 2 - 1) * MAX_OFFSET);
}
}
@SubscribeEvent
public static void onRenderLivingEvent(RenderLivingEvent.Post<?> event){
if(event.getEntity().isPotionActive(WizardryPotions.mirage)){
GlStateManager.blendFunc(SourceFactor.SRC_ALPHA, DestFactor.ONE_MINUS_SRC_ALPHA);
GlStateManager.popMatrix();
}
}
}
@@ -1,8 +1,8 @@
package electroblob.wizardry.client.renderer;
package electroblob.wizardry.client.renderer.effect;
import electroblob.wizardry.Wizardry;
import electroblob.wizardry.registry.Spells;
import electroblob.wizardry.util.WizardryUtilities;
import electroblob.wizardry.util.EntityUtils;
import net.minecraft.client.Minecraft;
import net.minecraft.client.renderer.BufferBuilder;
import net.minecraft.client.renderer.GlStateManager;
@@ -11,7 +11,6 @@ import net.minecraft.client.renderer.Tessellator;
import net.minecraft.client.renderer.vertex.DefaultVertexFormats;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.util.ResourceLocation;
import net.minecraft.util.math.Vec3d;
import net.minecraftforge.client.event.RenderPlayerEvent;
import net.minecraftforge.client.event.RenderWorldLastEvent;
import net.minecraftforge.fml.common.Mod;
@@ -32,7 +31,7 @@ public class RenderShadowWard {
EntityPlayer player = Minecraft.getMinecraft().player;
if(WizardryUtilities.isCasting(player, Spells.shadow_ward)){
if(EntityUtils.isCasting(player, Spells.shadow_ward)){
GlStateManager.pushMatrix();
@@ -52,7 +51,7 @@ public class RenderShadowWard {
GlStateManager.pushMatrix();
GlStateManager.translate(0, 0, 1.2);
GlStateManager.rotate(player.world.getTotalWorldTime() * -2, 0, 0, 1);
GlStateManager.rotate(player.ticksExisted * -2, 0, 0, 1);
GlStateManager.scale(1.1, 1.1, 1.1);
Tessellator tessellator = Tessellator.getInstance();
@@ -94,7 +93,7 @@ public class RenderShadowWard {
EntityPlayer player = event.getEntityPlayer();
if(WizardryUtilities.isCasting(player, Spells.shadow_ward)){
if(EntityUtils.isCasting(player, Spells.shadow_ward)){
GlStateManager.pushMatrix();
@@ -103,9 +102,7 @@ public class RenderShadowWard {
GlStateManager.disableLighting();
OpenGlHelper.setLightmapTextureCoords(OpenGlHelper.lightmapTexUnit, 240f, 240f);
Vec3d delta = player.getPositionEyes(event.getPartialRenderTick())
.subtract(Minecraft.getMinecraft().player.getPositionEyes(event.getPartialRenderTick()));
GlStateManager.translate(delta.x, delta.y, delta.z);
GlStateManager.translate(event.getX(), event.getY(), event.getZ());
GlStateManager.rotate(180, 0, 1, 0);
GlStateManager.rotate(-player.renderYawOffset, 0, 1, 0);
@@ -116,7 +113,7 @@ public class RenderShadowWard {
BufferBuilder buffer = tessellator.getBuffer();
GlStateManager.translate(0, 1.2, 0);
GlStateManager.rotate(player.world.getTotalWorldTime() * -2, 0, 0, 1);
GlStateManager.rotate(player.ticksExisted * -2, 0, 0, 1);
GlStateManager.scale(1.1, 1.1, 1.1);
buffer.begin(GL11.GL_QUADS, DefaultVertexFormats.POSITION_TEX);
@@ -1,10 +1,10 @@
package electroblob.wizardry.client.renderer;
package electroblob.wizardry.client.renderer.effect;
import electroblob.wizardry.Wizardry;
import electroblob.wizardry.data.WizardData;
import electroblob.wizardry.registry.Spells;
import electroblob.wizardry.spell.Shield;
import electroblob.wizardry.util.WizardryUtilities;
import electroblob.wizardry.util.EntityUtils;
import net.minecraft.client.Minecraft;
import net.minecraft.client.renderer.BufferBuilder;
import net.minecraft.client.renderer.GlStateManager;
@@ -13,7 +13,6 @@ import net.minecraft.client.renderer.Tessellator;
import net.minecraft.client.renderer.vertex.DefaultVertexFormats;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.util.ResourceLocation;
import net.minecraft.util.math.Vec3d;
import net.minecraftforge.client.event.RenderPlayerEvent;
import net.minecraftforge.client.event.RenderWorldLastEvent;
import net.minecraftforge.fml.common.Mod;
@@ -34,7 +33,7 @@ public class RenderShield {
EntityPlayer player = Minecraft.getMinecraft().player;
if(WizardData.get(player).getVariable(Shield.SHIELD_KEY) != null && WizardryUtilities.isCasting(player, Spells.shield)){
if(WizardData.get(player).getVariable(Shield.SHIELD_KEY) != null && EntityUtils.isCasting(player, Spells.shield)){
GlStateManager.pushMatrix();
@@ -50,7 +49,25 @@ public class RenderShield {
GlStateManager.rotate(-player.rotationYaw, 0, 1, 0);
GlStateManager.rotate(player.rotationPitch, 1, 0, 0);
GlStateManager.translate(0, 0, 0.8);
GlStateManager.translate(0, 0, 1);
GlStateManager.pushMatrix();
// Changes the scale at which the texture is applied to the model. See LayerCreeper for a similar example,
// but with translation instead of scaling.
// You can do all sorts of fun stuff with this, just by applying transformations in the 2D texture space.
GlStateManager.matrixMode(GL11.GL_TEXTURE); // Switch to the 2D texture space
GlStateManager.loadIdentity();
GlStateManager.translate(0.5, 0.5, 0);
float s = 1 - ((player.ticksExisted + event.getPartialTicks()) % 5) / 5 * 0.52f;
s = s*s;
GlStateManager.scale(s, s, 1);
GlStateManager.translate(-0.5, -0.5, 0);
GlStateManager.matrixMode(GL11.GL_MODELVIEW); // Switch back to the 3D model space
Tessellator tessellator = Tessellator.getInstance();
@@ -58,6 +75,13 @@ public class RenderShield {
render(tessellator);
// Undo the texture scaling
GlStateManager.matrixMode(GL11.GL_TEXTURE);
GlStateManager.loadIdentity();
GlStateManager.matrixMode(GL11.GL_MODELVIEW);
GlStateManager.popMatrix();
GlStateManager.enableLighting();
GlStateManager.shadeModel(GL11.GL_FLAT);
@@ -76,7 +100,7 @@ public class RenderShield {
EntityPlayer player = event.getEntityPlayer();
if(WizardData.get(player).getVariable(Shield.SHIELD_KEY) != null && WizardryUtilities.isCasting(player, Spells.shield)){
if(WizardData.get(player).getVariable(Shield.SHIELD_KEY) != null && EntityUtils.isCasting(player, Spells.shield)){
GlStateManager.pushMatrix();
@@ -89,9 +113,7 @@ public class RenderShield {
GlStateManager.disableLighting();
OpenGlHelper.setLightmapTextureCoords(OpenGlHelper.lightmapTexUnit, 240f, 240f);
Vec3d delta = player.getPositionEyes(event.getPartialRenderTick())
.subtract(Minecraft.getMinecraft().player.getPositionEyes(event.getPartialRenderTick()));
GlStateManager.translate(delta.x, delta.y, delta.z);
GlStateManager.translate(event.getX(), event.getY(), event.getZ());
GlStateManager.translate(0, 1.3, 0);
@@ -99,7 +121,25 @@ public class RenderShield {
GlStateManager.rotate(-player.renderYawOffset, 0, 1, 0);
// GlStateManager.rotate(-player.rotationPitch, 1, 0, 0);
GlStateManager.translate(0, 0, 0.8);
GlStateManager.translate(0, 0, 1);
GlStateManager.pushMatrix();
// Changes the scale at which the texture is applied to the model. See LayerCreeper for a similar example,
// but with translation instead of scaling.
// You can do all sorts of fun stuff with this, just by applying transformations in the 2D texture space.
GlStateManager.matrixMode(GL11.GL_TEXTURE); // Switch to the 2D texture space
GlStateManager.loadIdentity();
GlStateManager.translate(0.5, 0.5, 0);
float s = 1 - ((player.ticksExisted + event.getPartialRenderTick()) % 5) / 5 * 0.52f;
s = s*s;
GlStateManager.scale(s, s, 1);
GlStateManager.translate(-0.5, -0.5, 0);
GlStateManager.matrixMode(GL11.GL_MODELVIEW); // Switch back to the 3D model space
Tessellator tessellator = Tessellator.getInstance();
@@ -107,6 +147,13 @@ public class RenderShield {
render(tessellator);
// Undo the texture scaling
GlStateManager.matrixMode(GL11.GL_TEXTURE);
GlStateManager.loadIdentity();
GlStateManager.matrixMode(GL11.GL_MODELVIEW);
GlStateManager.popMatrix();
GlStateManager.enableLighting();
GlStateManager.shadeModel(GL11.GL_FLAT);
@@ -1,8 +1,8 @@
package electroblob.wizardry.client.renderer;
package electroblob.wizardry.client.renderer.effect;
import electroblob.wizardry.Wizardry;
import electroblob.wizardry.registry.Spells;
import electroblob.wizardry.util.WizardryUtilities;
import electroblob.wizardry.util.EntityUtils;
import net.minecraft.client.Minecraft;
import net.minecraft.client.renderer.BufferBuilder;
import net.minecraft.client.renderer.GlStateManager;
@@ -12,7 +12,6 @@ import net.minecraft.client.renderer.vertex.DefaultVertexFormats;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.util.ResourceLocation;
import net.minecraft.util.math.MathHelper;
import net.minecraft.util.math.Vec3d;
import net.minecraftforge.client.event.RenderPlayerEvent;
import net.minecraftforge.fml.common.Mod;
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
@@ -32,7 +31,7 @@ public class RenderWings {
EntityPlayer player = event.getEntityPlayer();
if(WizardryUtilities.isCasting(player, Spells.flight)){
if(EntityUtils.isCasting(player, Spells.flight)){
GlStateManager.pushMatrix();
@@ -41,9 +40,7 @@ public class RenderWings {
GlStateManager.disableLighting();
OpenGlHelper.setLightmapTextureCoords(OpenGlHelper.lightmapTexUnit, 240f, 240f);
Vec3d delta = player.getPositionEyes(event.getPartialRenderTick())
.subtract(Minecraft.getMinecraft().player.getPositionEyes(event.getPartialRenderTick()));
GlStateManager.translate(delta.x, delta.y, delta.z);
GlStateManager.translate(event.getX(), event.getY(), event.getZ());
// GlStateManager.rotate(-entityplayer.rotationYawHead, 0, 1, 0);
GlStateManager.rotate(-player.renderYawOffset, 0, 1, 0);
@@ -1,6 +1,8 @@
package electroblob.wizardry.client.renderer;
package electroblob.wizardry.client.renderer.entity;
import electroblob.wizardry.Wizardry;
import electroblob.wizardry.client.DrawingUtils;
import electroblob.wizardry.client.renderer.RayHelper;
import electroblob.wizardry.entity.construct.EntityBlackHole;
import net.minecraft.client.Minecraft;
import net.minecraft.client.renderer.*;
@@ -16,17 +18,18 @@ import java.util.Collections;
public class RenderBlackHole extends Render<EntityBlackHole> {
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");
// TODO: Remove RayHelper and the ray texture entirely and use colours with depth mask off (like imbuement altar)
private static final ResourceLocation RAY_TEXTURE = new ResourceLocation(Wizardry.MODID,
"textures/entity/black_hole/ray.png");
private static final ResourceLocation CENTRE_TEXTURE = new ResourceLocation(Wizardry.MODID,
"textures/entity/black_hole/centre.png");
public RenderBlackHole(RenderManager renderManager){
super(renderManager);
}
@Override
public void doRender(EntityBlackHole blackhole, double x, double y, double z, float fa, float fb){
public void doRender(EntityBlackHole entity, double x, double y, double z, float entityYaw, float partialTicks){
GlStateManager.pushMatrix();
@@ -48,16 +51,10 @@ public class RenderBlackHole extends Render<EntityBlackHole> {
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){
float scale = Math.max(0, (float)(blackhole.lifetime - blackhole.ticksExisted) / 10);
GlStateManager.scale(scale, scale, scale);
}
float s = DrawingUtils.smoothScaleFactor(entity.lifetime, entity.ticksExisted, partialTicks, 10, 10);
GlStateManager.scale(s, s, s);
this.bindTexture(texture);
this.bindTexture(RAY_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)
@@ -65,24 +62,24 @@ public class RenderBlackHole extends Render<EntityBlackHole> {
// 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<RayHelper> rays = new ArrayList<RayHelper>(1);
ArrayList<RayHelper> rays = new ArrayList<>(1);
for(int j = 0; j < 30; j++){
float scale = 3.0f;
float radius = 3.0f * entity.getSizeMultiplier();
int a = blackhole.randomiser[j];
int b = blackhole.randomiser2[j];
int a = entity.randomiser[j];
int b = entity.randomiser2[j];
int sliceAngle = 20 + a;
double x1 = scale * MathHelper.sin((blackhole.ticksExisted + 40 * j) * ((float)Math.PI / 180f));
double x1 = radius * MathHelper.sin((entity.ticksExisted + 40 * j) * ((float)Math.PI / 180f));
// double y1 = 0.7*MathHelper.cos((blackhole.timer - 40*j)*(Math.PI/180))*j/10;
double z1 = scale * MathHelper.cos((blackhole.ticksExisted + 40 * j) * ((float)Math.PI / 180));
double z1 = radius * MathHelper.cos((entity.ticksExisted + 40 * j) * ((float)Math.PI / 180));
double x2 = scale * MathHelper.sin((blackhole.ticksExisted + 40 * j - sliceAngle) * ((float)Math.PI / 180));
double x2 = radius * MathHelper.sin((entity.ticksExisted + 40 * j - sliceAngle) * ((float)Math.PI / 180));
// double y2 = 0.7*MathHelper.sin((blackhole.timer - 40*j)*(Math.PI/180))*j/10;
double z2 = scale * MathHelper.cos((blackhole.ticksExisted + 40 * j - sliceAngle) * ((float)Math.PI / 180));
double z2 = radius * MathHelper.cos((entity.ticksExisted + 40 * j - sliceAngle) * ((float)Math.PI / 180));
double absoluteX = x1 * MathHelper.cos(31 * b);
double absoluteY = z1 * MathHelper.sin(31 * a) + x1 * MathHelper.cos(31 * a) * MathHelper.sin(31 * b);
@@ -142,7 +139,7 @@ public class RenderBlackHole extends Render<EntityBlackHole> {
buffer.begin(GL11.GL_QUADS, DefaultVertexFormats.POSITION_TEX);
this.bindTexture(texture2);
this.bindTexture(CENTRE_TEXTURE);
buffer.pos(-0.4, 0.4, 0).tex(0, 0).endVertex();
buffer.pos(0.4, 0.4, 0).tex(1, 0).endVertex();
@@ -164,7 +161,7 @@ public class RenderBlackHole extends Render<EntityBlackHole> {
@Override
protected ResourceLocation getEntityTexture(EntityBlackHole entity){
return texture;
return RAY_TEXTURE;
}
}
@@ -1,4 +1,4 @@
package electroblob.wizardry.client.renderer;
package electroblob.wizardry.client.renderer.entity;
import net.minecraft.client.renderer.entity.Render;
import net.minecraft.client.renderer.entity.RenderManager;
@@ -0,0 +1,51 @@
package electroblob.wizardry.client.renderer.entity;
import electroblob.wizardry.Wizardry;
import electroblob.wizardry.client.DrawingUtils;
import electroblob.wizardry.client.model.ModelBoulder;
import electroblob.wizardry.entity.construct.EntityBoulder;
import net.minecraft.client.renderer.GlStateManager;
import net.minecraft.client.renderer.entity.Render;
import net.minecraft.client.renderer.entity.RenderManager;
import net.minecraft.util.ResourceLocation;
public class RenderBoulder extends Render<EntityBoulder> {
private static final ResourceLocation TEXTURE = new ResourceLocation(Wizardry.MODID, "textures/entity/boulder.png");
private ModelBoulder model = new ModelBoulder();
public RenderBoulder(RenderManager manager){
super(manager);
}
@Override
public void doRender(EntityBoulder entity, double x, double y, double z, float yaw, float partialTicks){
GlStateManager.pushMatrix();
GlStateManager.translate(x, y + entity.height/2, z);
GlStateManager.rotate(180, 0F, 0F, 1F);
GlStateManager.rotate(yaw - 90, 0, 1, 0);
GlStateManager.rotate(entity.prevRotationPitch + (entity.rotationPitch - entity.prevRotationPitch) * partialTicks, 0, 0, 1);
GlStateManager.translate(0, -entity.height/2, 0);
// Pass in -1 for the lifetime as the boulder crumbles when the time expires
float s = DrawingUtils.smoothScaleFactor(-1, entity.ticksExisted, partialTicks, 10, 10);
s *= entity.getSizeMultiplier();
GlStateManager.scale(s, s, s);
GlStateManager.translate(0, 0.875, 0); // No idea why it starts 7/8 of a block too low, but it does
this.bindTexture(TEXTURE);
model.render(entity, 0, 0, 0, 0, 0, 0.0625f);
GlStateManager.popMatrix();
}
@Override
protected ResourceLocation getEntityTexture(EntityBoulder entity){
return TEXTURE;
}
}
@@ -1,8 +1,9 @@
package electroblob.wizardry.client.renderer;
package electroblob.wizardry.client.renderer.entity;
import electroblob.wizardry.Wizardry;
import electroblob.wizardry.client.DrawingUtils;
import electroblob.wizardry.entity.construct.EntityBubble;
import electroblob.wizardry.util.WizardryUtilities;
import electroblob.wizardry.util.EntityUtils;
import net.minecraft.client.Minecraft;
import net.minecraft.client.renderer.BufferBuilder;
import net.minecraft.client.renderer.GlStateManager;
@@ -11,21 +12,21 @@ import net.minecraft.client.renderer.Tessellator;
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;
import org.lwjgl.opengl.GL11;
public class RenderBubble extends Render<EntityBubble> {
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");
private static final ResourceLocation PARTICLE_TEXTURES = new ResourceLocation("textures/particle/particles.png");
private static final ResourceLocation ENTRAPMENT_TEXTURE = new ResourceLocation(Wizardry.MODID, "textures/entity/entrapment.png");
public RenderBubble(RenderManager renderManager){
super(renderManager);
}
@Override
public void doRender(EntityBubble entity, double par2, double par4, double par6, float par8, float par9){
public void doRender(EntityBubble entity, double x, double y, double z, float entityYaw, float partialTicks){
GlStateManager.pushMatrix();
GlStateManager.enableBlend();
@@ -33,18 +34,20 @@ public class RenderBubble extends Render<EntityBubble> {
float yOffset = 0;
if(WizardryUtilities.getRider(entity) != null){
yOffset = WizardryUtilities.getRider(entity).height / 2;
Entity rider = EntityUtils.getRider(entity);
if(rider != null){
yOffset = rider.height / 2;
}
GlStateManager.translate((float)par2, (float)par4 + yOffset, (float)par6);
GlStateManager.translate((float)x, (float)y + yOffset, (float)z);
this.bindTexture(((EntityBubble)entity).isDarkOrb ? darkOrbTexture : particleTextures);
this.bindTexture(entity.isDarkOrb ? ENTRAPMENT_TEXTURE : PARTICLE_TEXTURES);
float f6 = 1.0F;
float f7 = 0.5F;
float f8 = 0.5F;
if(((EntityBubble)entity).isDarkOrb){
if(entity.isDarkOrb){
GlStateManager.disableLighting();
OpenGlHelper.setLightmapTextureCoords(OpenGlHelper.lightmapTexUnit, 240, 240);
}else{
@@ -63,35 +66,32 @@ public class RenderBubble extends Render<EntityBubble> {
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);
// Bubble 'bursts' so doesn't shrink when is disappears
float s = 3 * DrawingUtils.smoothScaleFactor(entity.isDarkOrb ? entity.lifetime : -1, entity.ticksExisted, partialTicks, 10, 10);
GlStateManager.scale(s, s, s);
double pixelwidth = (1.0d / 128);
Tessellator tessellator = Tessellator.getInstance();
BufferBuilder 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();
if(entity.isDarkOrb){
buffer.pos(0.0F - f7, 0.0F - f8, 0.0D).tex(0, 1).endVertex();
buffer.pos(f6 - f7, 0.0F - f8, 0.0D).tex(1, 1).endVertex();
buffer.pos(f6 - f7, 1.0F - f8, 0.0D).tex(1, 0).endVertex();
buffer.pos(0.0F - f7, 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();
buffer.pos(0.0F - f7, 0.0F - f8, 0.0D).tex(pixelwidth, pixelwidth * 24).endVertex();
buffer.pos(f6 - f7, 0.0F - f8, 0.0D).tex(pixelwidth * 8, pixelwidth * 24).endVertex();
buffer.pos(f6 - f7, 1.0F - f8, 0.0D).tex(pixelwidth * 8, pixelwidth * 17).endVertex();
buffer.pos(0.0F - f7, 1.0F - f8, 0.0D).tex(pixelwidth, pixelwidth * 17).endVertex();
}
tessellator.draw();
GlStateManager.disableBlend();
if(((EntityBubble)entity).isDarkOrb){
GlStateManager.enableLighting();
}
if(entity.isDarkOrb) GlStateManager.enableLighting();
GlStateManager.disableRescaleNormal();
GlStateManager.popMatrix();
@@ -1,6 +1,7 @@
package electroblob.wizardry.client.renderer;
package electroblob.wizardry.client.renderer.entity;
import electroblob.wizardry.Wizardry;
import electroblob.wizardry.client.DrawingUtils;
import electroblob.wizardry.entity.construct.EntityDecay;
import net.minecraft.client.renderer.BufferBuilder;
import net.minecraft.client.renderer.GlStateManager;
@@ -14,17 +15,17 @@ import org.lwjgl.opengl.GL11;
public class RenderDecay extends Render<EntityDecay> {
private static final ResourceLocation[] textures = new ResourceLocation[10];
private static final ResourceLocation[] TEXTURES = new ResourceLocation[10];
public RenderDecay(RenderManager renderManager){
super(renderManager);
for(int i = 0; i < 10; i++){
textures[i] = new ResourceLocation(Wizardry.MODID, "textures/entity/decay_" + i + ".png");
TEXTURES[i] = new ResourceLocation(Wizardry.MODID, "textures/entity/decay/decay_" + i + ".png");
}
}
@Override
public void doRender(EntityDecay entity, double par2, double par4, double par6, float par8, float par9){
public void doRender(EntityDecay entity, double x, double y, double z, float entityYaw, float partialTicks){
GlStateManager.pushMatrix();
GlStateManager.enableBlend();
@@ -34,28 +35,26 @@ public class RenderDecay extends Render<EntityDecay> {
float yOffset = 0;
GlStateManager.translate((float)par2, (float)par4 + yOffset, (float)par6);
GlStateManager.translate((float)x, (float)y + yOffset, (float)z);
this.bindTexture(textures[((EntityDecay)entity).textureIndex]);
this.bindTexture(TEXTURES[entity.textureIndex]);
float f6 = 1.0F;
float f7 = 0.5F;
float f8 = 0.5F;
GlStateManager.rotate(-90, 1, 0, 0);
float scale = 2 * Math.min(1, (float)(entity.lifetime - entity.ticksExisted) / 50f);
GlStateManager.scale(scale, scale, scale);
float s = 2 * DrawingUtils.smoothScaleFactor(entity.lifetime, entity.ticksExisted, partialTicks, 10, 50);
GlStateManager.scale(s, s, s);
Tessellator tessellator = Tessellator.getInstance();
BufferBuilder buffer = tessellator.getBuffer();
buffer.begin(GL11.GL_QUADS, DefaultVertexFormats.POSITION_TEX);
// 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();
buffer.pos(0.0F - f7, 0.0F - f8, 0.01).tex(0, 1).endVertex();
buffer.pos(f6 - f7, 0.0F - f8, 0.01).tex(1, 1).endVertex();
buffer.pos(f6 - f7, 1.0F - f8, 0.01).tex(1, 0).endVertex();
buffer.pos(0.0F - f7, 1.0F - f8, 0.01).tex(0, 0).endVertex();
tessellator.draw();
@@ -1,4 +1,4 @@
package electroblob.wizardry.client.renderer;
package electroblob.wizardry.client.renderer.entity;
import electroblob.wizardry.Wizardry;
import electroblob.wizardry.entity.living.EntityDecoy;
@@ -8,7 +8,7 @@ import net.minecraft.client.renderer.entity.RenderBiped;
import net.minecraft.client.renderer.entity.RenderManager;
import net.minecraft.entity.Entity;
import net.minecraft.util.ResourceLocation;
import net.minecraftforge.fml.relauncher.ReflectionHelper;
import net.minecraftforge.fml.common.ObfuscationReflectionHelper;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
@@ -18,8 +18,8 @@ public class RenderDecoy extends RenderBiped<EntityDecoy> {
private static final ResourceLocation steveTextures = new ResourceLocation("textures/entity/steve.png");
private static final Method getEntityTexture = ReflectionHelper.findMethod(Render.class, "getEntityTexture",
"func_110775_a", Entity.class); // Generic parameter T is erased to Entity at runtime.
private static final Method getEntityTexture = ObfuscationReflectionHelper.findMethod(Render.class, "func_110775_a",
ResourceLocation.class, Entity.class); // Generic parameter T is erased to Entity at runtime.
public RenderDecoy(RenderManager manager){
super(manager, new ModelBiped(0.0f), 0.5f);
@@ -1,4 +1,4 @@
package electroblob.wizardry.client.renderer;
package electroblob.wizardry.client.renderer.entity;
import electroblob.wizardry.Wizardry;
import electroblob.wizardry.client.model.ModelWizard;
@@ -10,14 +10,15 @@ import net.minecraft.util.ResourceLocation;
//@SideOnly(Side.CLIENT)
public class RenderEvilWizard extends RenderBiped<EntityEvilWizard> {
static final ResourceLocation[] textures = new ResourceLocation[6];
static final ResourceLocation[] TEXTURES = new ResourceLocation[6];
public RenderEvilWizard(RenderManager renderManager){
super(renderManager, new ModelWizard(), 0.5F);
for(int i = 0; i < 6; i++){
textures[i] = new ResourceLocation(Wizardry.MODID, "textures/entity/evil_wizard_" + i + ".png");
TEXTURES[i] = new ResourceLocation(Wizardry.MODID, "textures/entity/evil_wizard/evil_wizard_" + i + ".png");
}
// Just using the default without overriding models, since the armour sets its own model anyway.
this.addLayer(new LayerBipedArmor(this));
@@ -25,7 +26,7 @@ public class RenderEvilWizard extends RenderBiped<EntityEvilWizard> {
@Override
protected ResourceLocation getEntityTexture(EntityEvilWizard wizard){
return textures[wizard.textureIndex];
return TEXTURES[wizard.textureIndex];
}
}
@@ -0,0 +1,180 @@
package electroblob.wizardry.client.renderer.entity;
import electroblob.wizardry.client.DrawingUtils;
import electroblob.wizardry.entity.construct.EntityFireRing;
import net.minecraft.client.Minecraft;
import net.minecraft.client.renderer.BufferBuilder;
import net.minecraft.client.renderer.GlStateManager;
import net.minecraft.client.renderer.OpenGlHelper;
import net.minecraft.client.renderer.Tessellator;
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.init.Blocks;
import net.minecraft.util.ResourceLocation;
import org.lwjgl.opengl.GL11;
public class RenderFireRing extends Render<EntityFireRing> {
private final ResourceLocation texture;
public RenderFireRing(RenderManager renderManager, ResourceLocation texture){
super(renderManager);
this.texture = texture;
}
@Override
public void doRender(EntityFireRing entity, double x, double y, double z, float entityYaw, float partialTicks){
GlStateManager.pushMatrix();
GlStateManager.enableBlend();
GlStateManager.disableLighting();
OpenGlHelper.setLightmapTextureCoords(OpenGlHelper.lightmapTexUnit, 240, 240);
GlStateManager.blendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA);
float yOffset = 0;
GlStateManager.translate(x, y + yOffset, z);
this.bindTexture(texture);
float f6 = 1.0F;
float f7 = 0.5F;
float f8 = 0.5F;
GlStateManager.rotate(-90, 1, 0, 0);
float s = DrawingUtils.smoothScaleFactor(entity.lifetime, entity.ticksExisted, partialTicks, 10, 10);
GlStateManager.scale(entity.width * s, entity.width * s, entity.width * s);
Tessellator tessellator = Tessellator.getInstance();
BufferBuilder buffer = tessellator.getBuffer();
buffer.begin(GL11.GL_QUADS, DefaultVertexFormats.POSITION_TEX);
buffer.pos(0.0F - f7, 0.0F - f8, 0.01).tex(0, 1).endVertex();
buffer.pos(f6 - f7, 0.0F - f8, 0.01).tex(1, 1).endVertex();
buffer.pos(f6 - f7, 1.0F - f8, 0.01).tex(1, 0).endVertex();
buffer.pos(0.0F - f7, 1.0F - f8, 0.01).tex(0, 0).endVertex();
tessellator.draw();
GlStateManager.disableBlend();
GlStateManager.enableLighting();
GlStateManager.disableRescaleNormal();
GlStateManager.popMatrix();
// Fire
// TODO: Bleeeuuurugh this is disgusting must fix :0
if(s >= 1){
GlStateManager.disableLighting();
TextureAtlasSprite icon = Minecraft.getMinecraft().getBlockRendererDispatcher()
.getModelForState(Blocks.FIRE.getDefaultState()).getParticleTexture();
float s1 = entity.width/5;
int sides = (int)(16 * s1);
float height = 1.0f;
for(int k = 0; k < sides; k++){
GlStateManager.pushMatrix();
GlStateManager.translate((float)x, (float)y + 0.05f, (float)z);
// GlStateManager.scale(s1, s1, s1);
float f2 = 0.5F;
float f3 = 0.0F;
float f4 = 0.2f;
float f5 = (float)(entity.posY - entity.posY);
GlStateManager.color(1.0F, 1.0F, 1.0F, 1.0F);
float f61 = 0.0F;
int i = 0;
GlStateManager.rotate((360f / (float)sides) * k, 0, 1, 0);
GlStateManager.translate(0, 0, -2.3f * s1);
buffer.begin(GL11.GL_QUADS, DefaultVertexFormats.POSITION_TEX);
while(f4 > 0.0F){
this.bindTexture(TextureMap.LOCATION_BLOCKS_TEXTURE);
float f71 = icon.getMinU();
float f81 = icon.getMinV();
float f9 = icon.getMaxU();
float f10 = icon.getMaxV();
if(i / 2 % 2 == 0){
float f11 = f9;
f9 = f71;
f71 = f11;
}
buffer.pos((f2 - f3), (0.0F - f5), f61).tex(f9, f10).endVertex();
buffer.pos((-f2 - f3), (0.0F - f5), f61).tex(f71, f10).endVertex();
buffer.pos((-f2 - f3), (height - f5), f61).tex(f71, f81).endVertex();
buffer.pos((f2 - f3), (height - f5), f61).tex(f9, f81).endVertex();
f4 -= 0.45F;
f5 -= 0.45F;
f2 *= 0.9F;
f61 += 0.03F;
++i;
}
tessellator.draw();
GlStateManager.popMatrix();
}
for(int k = 0; k < sides; k++){
GlStateManager.pushMatrix();
GlStateManager.translate((float)x, (float)y + 0.05f, (float)z);
// GlStateManager.scale(s1, s1, s1);
float f2 = 0.5F;
float f3 = 0.0F;
float f4 = 0.2f;
float f5 = (float)(entity.posY - entity.posY);
GlStateManager.color(1.0F, 1.0F, 1.0F, 1.0F);
float f61 = 0.0F;
int i = 0;
GlStateManager.rotate((360f / (float)sides) * k, 0, 1, 0);
GlStateManager.translate(0, 0, 2.3f * s1);
buffer.begin(GL11.GL_QUADS, DefaultVertexFormats.POSITION_TEX);
while(f4 > 0.0F){
this.bindTexture(TextureMap.LOCATION_BLOCKS_TEXTURE);
float f71 = icon.getMinU();
float f81 = icon.getMinV();
float f9 = icon.getMaxU();
float f10 = icon.getMaxV();
if(i / 2 % 2 == 0){
float f11 = f9;
f9 = f71;
f71 = f11;
}
buffer.pos((f2 - f3), (0.0F - f5), f61).tex(f9, f10).endVertex();
buffer.pos((-f2 - f3), (0.0F - f5), f61).tex(f71, f10).endVertex();
buffer.pos((-f2 - f3), (height - f5), f61).tex(f71, f81).endVertex();
buffer.pos((f2 - f3), (height - f5), f61).tex(f9, f81).endVertex();
f4 -= 0.45F;
f5 -= 0.45F;
f2 *= 0.9F;
f61 += 0.03F;
++i;
}
tessellator.draw();
GlStateManager.popMatrix();
}
}
GlStateManager.enableLighting();
}
@Override
protected ResourceLocation getEntityTexture(EntityFireRing entity){
return null;
}
}
@@ -1,4 +1,4 @@
package electroblob.wizardry.client.renderer;
package electroblob.wizardry.client.renderer.entity;
import electroblob.wizardry.Wizardry;
import electroblob.wizardry.entity.projectile.EntityForceArrow;
@@ -1,4 +1,4 @@
package electroblob.wizardry.client.renderer;
package electroblob.wizardry.client.renderer.entity;
import electroblob.wizardry.entity.construct.EntityForcefield;
import net.minecraft.client.renderer.BufferBuilder;
@@ -1,4 +1,4 @@
package electroblob.wizardry.client.renderer;
package electroblob.wizardry.client.renderer.entity;
import electroblob.wizardry.Wizardry;
import electroblob.wizardry.client.model.ModelHammer;

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