Compare commits

..

169 Commits

Author SHA1 Message Date
WinDanesz bbe4634535 Some bomb fixes, colored spell book overlay are now separate files 2026-05-05 02:43:22 +02:00
WinDanesz e7b8f7681c Fix ImbuementActivateEvent, fixes #901 2026-04-18 23:45:32 +02:00
ipdnaeip 64168d103d AllyDesignationSystem update
Added isOnSameTeam check to  isValidTarget and isAllied
2026-04-18 23:38:00 +02:00
ipdnaeip 600a321f49 Fixed spelling error in updateImbuedItem, Fixed improper evaluation for command spells causing them to not trigger spell.onFinish 2026-04-18 23:37:38 +02:00
WinDanesz e753f4df32 Fix: Ensure spell properties are initialized before checking if the spell is enabled 2026-04-18 23:34:56 +02:00
WinDanesz cbe7353122 Update version to 4.3.16 2025-12-31 00:37:10 +01:00
WinDanesz de5682a960 Add colorized bookmarks based on book element, requires spells to be discovered. It can be turned off in the Client settings. Another tweak setting can make it depend on the Archivist's Eyeglass artefact 2025-12-30 18:35:35 +01:00
Lumberjacksparrow b451399323 Fix for inventory key not exiting the Arcane Workbench GUI #897 (Lumberjacksparrow) 2025-12-29 22:17:18 +01:00
ipdnaeip e547aa32ca Fix: Modifiers persist in continuous spells cast by scrolls (#895) (ipdnaeip)
* ItemScroll Modifier Fix

Modifiers persist in continuous spells cast by scrolls.

Added check for tick 0 as to not reapply SpellModifier changes

* Fix comment formatting in ItemScroll.java

---------

Co-authored-by: WinDanesz <31292708+WinDanesz@users.noreply.github.com>
2025-12-29 22:16:24 +01:00
WinDanesz 3fe9d762da Added support for item metadata for "currencyItems" settings (List of registry names of items which wizard trades can use as currency) 2025-12-29 22:11:21 +01:00
WinDanesz 20b53596e2 fix: Forfeit sounds are properly registered for add-on forfeits 2025-10-22 23:50:04 +02:00
WinDanesz c04a9fd37b feat: Add config for flesh spells: fleshSpellsCauseSlowness, diamondFleshArmorBonus, diamondFleshArmorToughnessBonus, ironFleshArmorBonus, oakFleshArmorBonus, oakFleshHealthBonus 2025-09-01 01:51:48 +02:00
WinDanesz fe59a3c377 feat: Add configurable condenser amount multiplier
- Add condenserAmountMultiplier setting with default value 1.0
2025-08-31 23:00:01 +02:00
WinDanesz fc9c6bc7e4 feat: Make tier values configurable through Settings, per tier:
- maxCharge (wand mana)
- upgradeLimit (number of possible upgrade scrolls applied to a specific tier, and weight
2025-08-31 22:46:57 +02:00
WinDanesz e3fa7a5eed feat: Added BASE_SPELL_SLOTS config option
Gameplay Category:
- baseSpellSlots: Number of spell slots a wand has with no attunement upgrades applied (default: 5, range: 1-5)
2025-08-31 17:54:02 +02:00
WinDanesz de135a8528 feat: Added new config options:
Gameplay Category:
- manaPerShard: Amount of mana a crystal shard is worth (default: 10)
- manaPerCrystal: Amount of mana each magic crystal is worth (default: 100)
- grandCrystalMana: Amount of mana a grand magic crystal is worth (default: 400)
- nonElementalUpgradeBonus: Bonus wand upgrades for non-elemental wands (default: 3)
- siphonManaPerLevel: Mana gained per siphon upgrade level (default: 5)
- condenserTickInterval: Ticks between condenser mana increases (default: 50)
Tweaks Category:
- storageIncreasePerLevel: Charge increase per storage upgrade level (default: 0.15)
2025-08-31 17:47:39 +02:00
WinDanesz 5ac053ddd9 feat: Regenerating Shrines. The following config options were added:
- 'shrineRegenerationEnabled': Whether conquered shrines should regenerate after a period of time. Regeneration means wizards and loot will spawn again after a while.
      - 'shrineRegenerationTime': Time in minutes for a conquered shrine to regenerate.
      - 'shrineAllowMultipleLoot': Whether players can loot shrines multiple times. If false, each player can only loot each shrine once.
      When shrineRegenerationEnabled, Shrine chests behave a bit differently - upon defeating all wizards, the chest breaks immediately dropping or not dropping loot based on shrineAllowMultipleLoot. When shrineRegenerationEnabled, the shrine's altar block is unbreakable.
2025-08-31 17:39:22 +02:00
WinDanesz f817f06dbe feat: Mind controlled creatures are considered allies by AoE spells. Fixes #890 2025-08-31 14:56:41 +02:00
WinDanesz 5d5acf253b feat: Fireskin burns away cobweb blocks on contact 2025-08-31 14:49:21 +02:00
WinDanesz bb4faf8ec9 fix: All items are searchable in creative search (crystals, dusts, tomes) 2025-08-31 14:26:37 +02:00
WinDanesz 7f9edcfaef feat: Added new config: "recentSpellExpiryTime": The time in ticks after which recent spell casts expire and no longer count toward progression penalties. Default is 1200 ticks (1 minute). Lower values make progression penalties shorter-lived, higher values make them last longer. 2025-08-31 13:58:52 +02:00
WinDanesz 1c1e6d1e00 fix: change Y_OFFSET from protected to public in SpellRay class 2025-06-09 14:26:21 +02:00
WinDanesz 550b600424 feat: add option to allow unfocused search bars in Arcane Workbench and Lectern 2025-06-07 01:34:59 +02:00
WinDanesz 9673a6c0f6 version bump to 4.3.15 2025-06-06 23:13:44 +02:00
WinDanesz 20b56cfcbd fix: GuiContainers can no longer be opened while possessing something. Fixes #888 2025-06-06 23:03:55 +02:00
19 947af8727d Fixed barriers not allowing ally players in (#868)
Had to edit telekinesis because it was had errors do to syntax. See comments in forcefield class for more information

Co-authored-by: WinDanesz <31292708+WinDanesz@users.noreply.github.com>
2025-06-06 22:19:17 +02:00
Yong Jun 23b950d5b8 add Immune to magic damage config option (#887)
* added more config

* Update src/main/java/electroblob/wizardry/Settings.java

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-04-07 23:23:46 +02:00
WinDanesz 2eaa9b445c fix: fix receptacle crash with FluidLoggedAPI. Fixes #875 2025-03-28 22:49:49 +01:00
WinDanesz c166bac323 fix: convertToStatue shouldn't run on the clientside 2025-03-28 22:13:58 +01:00
Suelvys Silva Bezerra 074a429c6a fix. Updated Brazilian Portuguese translation (by Suelvys) (#870) 2025-03-28 22:02:06 +01:00
Heimdallr-1 3fab85e134 fix: Updated Russian translation (by Heimdallr-1) (#873) 2025-03-28 22:01:20 +01:00
Koko Ayame 5aec954061 fix: Fix Korean translation (by Preta-Crowz) (#879)
One bracket was missing and caused crash
2025-03-28 21:59:04 +01:00
ipdnaeip 094a5f615f Bug Fixes and Sixth Sense Visual Improvement (#886) 2025-03-28 21:55:16 +01:00
WinDanesz 65ed0e8a22 version bump to 4.3.14 2024-05-18 16:07:11 +02:00
WinDanesz 936601cbad fix: Fixed a bug that caused stackable artefacts to alway double their effect 2024-05-18 15:57:15 +02:00
WinDanesz f9cf1b39db Fix telekineticDisarmament setting and Amulet of Anchoring 2024-03-29 18:07:19 +01:00
WinDanesz 3fb3c1c039 feat: made EntityWizard.trades public so that addon devs can manipulate trades 2024-03-28 01:00:26 +01:00
WinDanesz e8bc6ca725 version bump to 4.3.13 2024-03-28 01:00:26 +01:00
WinDanesz df531d3724 fix: Fixed all synchronized WizardData stored variables on dedicated servers. This issue was causing e.g. the Ancient Compass to not display its targets on servers. Fixes #857 2024-03-28 00:46:32 +01:00
ScoTWT 2728012f33 Update zh_tw.lang (#709) 2024-03-27 22:19:46 +01:00
red1854th 853df2eb42 Update ko_kr.lang (#714)
* Update Korean translations, courtesy of red1854th

* Update Korean translations, courtesy of red1854th
2024-03-27 22:18:20 +01:00
ipdnaeip 0cb2165ddf Added methods for retrieving entities within a cylinder, replaced radius-based methods, implemented DPS check for heal aura, adjusted targeting criteria for summoned creatures, tweaked heal method, fixed flamecatcher ammo loss, updated Enrage target and expanded spellcasting item compatibility for movement speed charm. - by ipdnaeip (#864)
Added getEntitiesWithinCylinder and getLivingWithinCylinder methods to return all entities within a cylinder.

Replaced getLivingWithinRadius for spells that should use a cylinder instead. The height of the cylinder is determined by the height of the entities.

Added a DPS checker on the heal aura damage just in case someone has damage immunity disabled.

Modified the AllyDesignationSystem and Wizard classes to no longer target summoned creatures automatically. They will still target hostile summoned creatures or creatures from players that have aggressed on them.

Modified the heal method in the Heal class to not reduce absorption amount. It should never really happen, but just in case.

Modified the flamecatcher to no longer lose ammo if it was used but no arrow was fired.

Enrage now targets EntityLiving instead of EntityCreature.

The movement speed charm now works with any spellcasting item as opposed to just wands.
2024-03-27 22:11:29 +01:00
WinDanesz ca7e9034ed feat: Added WandHelper.rechargeManaOnApplyButtonPressed static method for easier mana recharging across addons 2024-03-27 22:03:01 +01:00
WinDanesz 1322c920f5 fix: Spells are now sorted alphabetically by their names, instead of first being sorted by modid 2024-03-15 01:31:51 +01:00
WinDanesz e5c00e3882 feat: The Amulet of Anchoring also prevents the Telekinesis spell to knock items out of the wearer's hand. (Previously it only granted protection against being moved by Greater Telekinesis) 2024-03-14 00:05:40 +01:00
WinDanesz c030629bab fix: Fixed clear button and Attunement 2023-12-30 21:08:14 +01:00
WinDanesz 165d079251 Added getApplicableItems() to Spell.java 2023-10-25 10:10:20 +02:00
WinDanesz 7cc9200adf feat: added teleport_self_large_distance forfeit 2023-10-18 22:53:00 +02:00
WinDanesz 01a9d8c075 chore: 4.3.11 -> 4.3.12 version bump 2023-10-18 22:53:00 +02:00
WinDanesz 0a0a08d220 fix: Probably fixed a recurring Handbook crash for good 2023-10-18 22:53:00 +02:00
WinDanesz 410768e6f3 feat: Support for addon Wizard's Handbook
content
2023-10-18 22:53:00 +02:00
WinDanesz bbbe024bb3 build: Updated to ForgeGradle5 with FancyGradle 2023-10-18 22:53:00 +02:00
WinDanesz 93112d5192 docs: Update ArtefactCheckEvent.java javadoc 2023-09-10 16:10:15 +02:00
ipdnaeip 112a562b2d fix: Increased the tick rate of continuous spells (#849)
... and added some checks to make sure the spells don't deal too much with mods that interact with the hurt resistance timer.
2023-09-09 18:08:38 +02:00
WinDanesz 85ca4fd100 build: 4.3.10 -> 4.3.11 version bump 2023-06-30 16:55:40 +02:00
WinDanesz 136b1cc924 feat: Added preventBindingSameSpellTwiceToWands setting (disabled by default). Controls whether to prevent binding the same spell to a wand multiple times 2023-06-30 16:06:01 +02:00
WinDanesz e4b7d528f5 feat: Added singleUseSpellBooks setting (disabled by default). Controls whether spell books are consumed when they are bound to a wand 2023-06-30 15:47:24 +02:00
WinDanesz 2903ebfbac feat: Added damageTypePerElement setting (disabled by default). Pack makers now can implement support for .e.g Distinct Damage Definitions or other mods 2023-06-30 15:41:26 +02:00
WinDanesz 931ca1b2c0 feat: Added a Clear button to the Arcane Workbench. Clicking it removes all spell bindings of the item in the center slot 2023-06-28 20:53:55 +02:00
WinDanesz 85e724d88a fix: Allied spell casters of a player (owned by the player as an IEntityOwnable creature) now have their spells affect the player too where applicable. Previously spells like Group Heal casted by the NPC was not affecting the owner. 2023-06-28 19:14:53 +02:00
WinDanesz e41f83a2dd build: 4.3.9 -> 4.3.10 version bump 2023-06-28 16:06:37 +02:00
WinDanesz 69b52cfe88 feat: A new ArtefactCheckEvent for addons, allowing devs to consider an artefact active (in slot) conditionally, or even prevent its usage based on custom conditions 2023-06-28 16:06:07 +02:00
WinDanesz 7591821e57 feat: A more Wizardry-native support for BELT, BODY, HEAD and TRINKET artefact types (expanded ItemArtefact.Type) intended for addons 2023-06-28 16:02:32 +02:00
WinDanesz 0ce0dcf2e9 feat: Configuration options for the values of: POTENCY_INCREASE_PER_TIER, COOLDOWN_REDUCTION_PER_LEVEL, DURATION_INCREASE_PER_LEVEL, RANGE_INCREASE_PER_LEVEL, BLAST_RADIUS_INCREASE_PER_LEVEL, RANGE_INCREASE_PER_LEVEL 2023-06-28 15:20:00 +02:00
WinDanesz 73811c3a6b feat: Config option to toggle wands not decrementing cooldowns outside of hands 2023-06-28 14:56:01 +02:00
WinDanesz 2a887b7794 fix: Fixed bug with wands not decrementing cooldowns in the offhand 2023-06-28 14:25:15 +02:00
WinDanesz ad9f482e76 fix: Fixed a bug that prevented NPCs (wizards) from casting certain buff spells 2023-06-28 13:55:09 +02:00
WinDanesz 6eaef94459 Fixed Mark Sacrifice reducing damage at amplifier level 0 2023-03-23 22:01:51 +01:00
ZettaSword 4f5c616240 Added missing sound for the Mirage spell (#835)
Because of "[Client thread/WARN]: Missing sound for event: ebwizardry:spell.mirage" in latest.log i found that Mirage spell has no effect, even if it's registered. That's why with this request i add it to sounds.json.
2023-01-11 15:50:07 +01:00
WinDanesz f5947dded9 chore: version bump to 4.3.9 2022-12-31 20:23:14 +01:00
WinDanesz 77e0acc16b fix: Fixed Curse of Undeath breaking wizard headgear. It no longer breaks hats, and the curse sets the player on fire if the hat has no mana. Fixes #820 2022-12-31 20:21:19 +01:00
WinDanesz e48646c5ff feat: Conjured bows will now shoot conjured arrows 2022-12-31 20:14:47 +01:00
WinDanesz 5e48fe51ea fix: Added missing import 2022-12-31 19:58:02 +01:00
WinDanesz 6b30dbef65 fix: Fixed onImbuementRemoval() not being called for imbuements expired in the inventory. Currently this bug was only affecting adons. Fixes #833 2022-12-31 19:52:11 +01:00
WinDanesz d6508a2be4 fix: Fixed Fire Skin potion effect's retaliatory burn duration (was 20x shorter than intended) 2022-12-31 19:51:43 +01:00
WinDanesz dfcadfb3bd fix: No more console spam from Muffle spell 2022-10-04 00:55:06 +02:00
ZettaSword 7888b92129 feat: Wand cooldown only decrements if the player holds the wand 2022-08-04 22:17:31 +02:00
ZettaSword 5b48dffa77 fix: Fixed the adaptive brightness of Oak/Iron/Diamondflesh 2022-08-04 22:15:06 +02:00
WinDanesz 5bf4650da1 fix: Fixed wizards not using addon book spells 2022-07-11 21:14:45 +02:00
WinDanesz 8e9c270b77 fix: Fixed wizards selling addon book spells with the default book 2022-07-11 21:13:38 +02:00
WinDanesz 00080331df feat: Reworked the Summon Iron Golem spell, its new name is Summon Spectral Golem 2022-07-08 22:18:59 +02:00
WinDanesz 27e40e2069 fix: Fixed the opacity of conjured arrows 2022-07-08 21:37:55 +02:00
WinDanesz 3a93877de3 fix: Fire Ring shouldn't instantly melt away players on servers anymore 2022-07-08 21:11:10 +02:00
WinDanesz 7f70784cd3 fix: Fixed Scrolls of continuous spells having a cooldown in creative mode 2022-07-08 20:36:46 +02:00
WinDanesz ed6e3df66e feat: The Arrow Rain spell summons conjured arrows 2022-07-08 20:32:22 +02:00
WinDanesz eb5becbbd4 fix: Fixed some addon crystals restoring more mana than they should
Fixes #816
2022-07-08 19:41:25 +02:00
WinDanesz 9599316fbc fix: Added a config option to prevent crash with guidebook loading on some locales. Note that if you use this config, you won't have the Wizard's Handbook in an usable state!
Fixes #815, Fixes #814, Fixes #800, Fixes #768
2022-07-08 19:30:03 +02:00
WinDanesz cc1c951ef8 feat: Soulwalker's Ring only soulbounds an entity for 20 seconds 2022-07-08 19:20:16 +02:00
WinDanesz d23c4cba18 feat: Forcefield loses 1% durability each time it repulses an enemy or a projectile 2022-07-08 19:16:54 +02:00
WinDanesz 3d30b0b96f feat: Forcefield loses 2% durability each time it repulses an enemy or a projectile 2022-06-22 22:38:54 +02:00
WinDanesz ad1e5d555a feat: Reworked the Oakflesh, Ironflesh and Diamondflesh spells 2022-06-22 22:24:30 +02:00
WinDanesz 14ea5f1c72 fix: Fixed Containment spell for entities 2022-06-19 23:25:36 +02:00
WinDanesz 709d44e4d9 feat: Class Armour sets (Sage, Warlock, Battlemage) only grant their set bonuses if each armour piece has mana 2022-06-19 12:44:14 +02:00
WinDanesz 8df00e9c41 feat: Added potency_per_level spell property for Empowerment, reduced potency gain per level 0.25->0.15 2022-06-13 23:12:14 +02:00
WinDanesz 8dbb0ec058 fix: Fixed 'Ward I' effect not giving any protection against magic 2022-06-12 23:33:07 +02:00
WinDanesz b8310eedae feat: Added a player_effect_duration_multiplier for the Paralysis spell, defaults to 0.5 2022-06-12 00:12:24 +02:00
WinDanesz c04754afe1 chore: version bump to v4.3.7 2022-04-17 21:37:20 +02:00
WinDanesz 3155aa6815 fix: Should fix the disconnect issue when pretty much all existing spell packs are present in the game
Mods were registering spells on the client and server side in a different order (perhaps related to the OS), causing a mismatch in spell networkIDs. This will ensure that spells are sorted alphabetically and redistributes a new ID.
2022-04-17 20:43:59 +02:00
WinDanesz ea27b1a081 Revert "Merge pull request #750 from WinDanesz/patch-1226-2" 2022-04-17 15:53:48 +02:00
WinDanesz 4d078f9597 Revert "Fixed crash on dedicated servers (PacketSpellProperties), Fixes #780, #779, #773, #774, #771, #768"
This reverts commit 6690564017.
2022-04-17 15:46:11 +02:00
WinDanesz 67459cbbbd chore: Delete feature_request.md 2022-04-15 14:06:33 +02:00
WinDanesz 932de4e969 chore: Delete question.md 2022-04-15 13:58:44 +02:00
WinDanesz 41e28291ad chore: create config.yml 2022-04-15 13:58:29 +02:00
WinDanesz 6ae6324368 chore: Rename bug_report.md to bug_report.yml 2022-04-15 13:52:10 +02:00
WinDanesz 4858716f05 chore: update bug_report.md 2022-04-15 13:51:44 +02:00
WinDanesz 24fb4cfe4a v4.3.6 2022-03-06 19:26:45 +01:00
WinDanesz 186138d0c0 fixed visibility modifier of ImbuementActivateEvent's fields 2022-03-06 19:26:04 +01:00
WinDanesz 6690564017 Fixed crash on dedicated servers (PacketSpellProperties), Fixes #780, #779, #773, #774, #771, #768 2022-03-06 19:14:47 +01:00
WinDanesz 89a0a900cc v4.3.5 2022-02-07 23:06:39 +01:00
WinDanesz 391a24db3f Fixes integration with Construct's Armory and other mod's unbreakable helmets. #547 2022-02-07 22:10:28 +01:00
WinDanesz 4b546af642 Added a new ImbuementActivateEvent to allow addons hooking into the imbuement process 2022-02-07 21:50:26 +01:00
WinDanesz ee8aabbf97 This might fix the lingering Frost effect (from Frost Ray) 2022-02-07 21:37:01 +01:00
Rodro 45994e2b94 Fixed player rubberbanding when touching the wall of a containment field. Fixes #469 (Rodrogamby)
This fixed player rubberbanding when touching the wall of a containment field for me.

I took the liberty of uncommenting some code which produces a sound when the player tries to leave the field, and also deleted line 113 along with everything related to it since it seemed unnecessary.

I've tested this on singleplayer and on my 200ms multiplayer server and appears to be working perfectly in both.
(It works even if allow-flight is set to false on server.properties; I also tried using an elytra, levitation effect and extra speed, which all worked well too).
All tests were done using Forge-2860
2022-02-06 22:19:32 +01:00
WinDanesz 13bdc69da8 Added support for Forge's in-game mod version update notifier 2022-02-05 23:04:14 +01:00
WinDanesz c0184fde8e Fixed a bug with EntityWizard using one of EntityEvilWizard's DataParameter 2022-02-05 22:48:35 +01:00
WinDanesz cbd297e59d updated gradle version 2022-02-05 22:48:34 +01:00
WinDanesz c6cf7c950e increased duplicate detect threshold 2022-02-03 14:49:37 +01:00
WinDanesz fe5d80e017 Fixed dispenser crash with Conjure Block spell. Fixes #735 2022-02-03 11:57:22 +01:00
WinDanesz e985a8f15b Fixed dispenser crash with Slime spell. Fixes #735 2022-02-03 11:57:22 +01:00
Electroblob 615767f758 Merge pull request #688 from WinDanesz/1.12.2.dev
added onImbuementRemoval(ItemStack stack) to Imbuement interface
2022-02-01 18:34:24 +00:00
Electroblob 65a24b2295 Merge pull request #744 from WinDanesz/patch-2
Added duplicate issue detector & labeling tool
2022-02-01 18:33:18 +00:00
Electroblob 94c2afe67a Merge pull request #747 from WinDanesz/patch-1225-2
Fixes Mark Sacrifice not doing damage with low spell potency
2022-02-01 18:32:02 +00:00
Electroblob 9bbca37ecd Merge pull request #749 from WinDanesz/patch-1225-4
Scrolls no longer have cooldown in Creative mode
2022-02-01 18:30:47 +00:00
Electroblob 58241a0f5c Merge pull request #748 from WinDanesz/patch-1225-3
Added getter for ItemArtefact.enabled
2022-02-01 18:28:21 +00:00
Electroblob a960a01ca1 Merge pull request #750 from WinDanesz/patch-1226-2
Splitting PacketSpellProperties into batches of 100 spells.
2022-02-01 18:27:47 +00:00
Electroblob 67d8ee773c Merge pull request #751 from WinDanesz/patch-1227-1
Added tooltip for Spell Book and Scroll items, displaying the spell's…
2022-02-01 18:27:25 +00:00
Electroblob c6f2675278 Merge pull request #753 from WinDanesz/patch-1228-1
Fixed bug with wizard armours always providing protection even withou…
2022-02-01 18:26:54 +00:00
Electroblob77 2380be230e Merge branch '1.12.2' of https://github.com/Electroblob77/Wizardry into 1.12.2 2022-02-01 18:24:34 +00:00
Electroblob77 833276b7b3 TODOs 2022-02-01 18:24:26 +00:00
Electroblob77 9b32835720 Add resurrection event 2022-02-01 18:24:17 +00:00
Electroblob77 61634a9cc3 Change resurrection wait time and add particles to indicate where an ally died 2022-02-01 18:23:47 +00:00
Electroblob 408a35db8d Merge pull request #754 from WinDanesz/patch-1228-2
Remove soulbinding curse effect if the potion is no longer active. fi…
2022-02-01 18:20:37 +00:00
Electroblob 9695a85131 Merge pull request #755 from WinDanesz/patch-0102-1
Frostbite effect and burning now cancels each other
2022-02-01 18:20:20 +00:00
Electroblob d027cf4f2a Merge pull request #756 from WinDanesz/patch-0103-1
Added injectMobDrops setting to easily disable mob loot injection for…
2022-02-01 18:19:29 +00:00
WinDanesz c2f8bfc5b4 Added injectMobDrops setting to easily disable mob loot injection for all entities 2022-01-03 14:47:33 +01:00
WinDanesz 486425a62c Frostbite effect and entity burning now cancels each other 2022-01-02 22:07:05 +01:00
WinDanesz f9681d4c66 Remove soulbinding curse effect if the potion is no longer active. fixes #619. 2021-12-28 16:10:31 +01:00
WinDanesz cd0671dc59 Fixed bug with wizard armours always providing protection even without mana. Fixes #752 2021-12-28 16:08:18 +01:00
WinDanesz 7ebca16948 Added tooltip for Spell Book and Scroll items, displaying the spell's source mod if it's from an addon 2021-12-27 11:31:07 +01:00
WinDanesz e8cbdde518 Splitting PacketSpellProperties into batches of 100 spells. Fixes world load issue with large list of spells 2021-12-26 22:08:29 +01:00
WinDanesz 7ca6f05151 Scrolls no longer have cooldown in Creative mode 2021-12-25 18:16:32 +01:00
WinDanesz f4f17e90e5 Added getter for ItemArtefact.enabled 2021-12-25 18:10:09 +01:00
WinDanesz 3ea48efb1d Fixes Mark Sacrifice not doing damage with a potion amplifier of 0 2021-12-25 18:01:43 +01:00
WinDanesz ffc3533688 Create potential-duplicates.yml 2021-12-20 18:36:08 +01:00
Electroblob bd309855a2 Merge pull request #721 from WinDanesz/1.12.2-PR-10-02
Fixes wizards being stuck in a continuous, client-side only spellcast…
2021-10-03 18:10:51 +01:00
WinDanesz 1473c7e9b7 Fixes wizards being stuck in a continuous, client-side only spellcasting loop of their continuous spells if their target dies 2021-10-02 00:26:31 +02:00
WinDanesz 15cc7b6d18 added isEnabled() public method to ItemArtefact 2021-07-07 13:26:21 +02:00
WinDanesz 1c89c5f779 added onImbuementRemoval(ItemStack stack) to Imbuement interface 2021-07-07 12:40:05 +02:00
Electroblob77 ca7f1cc8ca Prevent Hyeleth's amulet from casting the shield spell from a scroll, fixes #680 2021-06-20 23:44:04 +01:00
Electroblob77 8fd1de84e4 Fix next/previous section button weirdness 2021-06-19 16:19:22 +01:00
Electroblob77 95d9168b6b Fix hyperlink wrapping for spaceless languages and remove remaining newlines 2021-06-19 16:01:23 +01:00
Electroblob77 c1db827132 Remove manual newline characters from Chinese handbook translations
I'm not 100% sure which to remove so I've only removed single newlines in block text for now
2021-06-19 13:01:58 +01:00
Electroblob77 d10b78ab46 Ignore hyperlink suffixes in spaceless languages, fixes #620
There's no real way to do this other than hardcoding which languages are exceptions (currently only the two Chinese locales)
2021-06-19 13:01:47 +01:00
Electroblob77 2d70523f43 Remove obsolete plain text Russian handbook translation 2021-06-19 12:47:00 +01:00
Electroblob77 42fb7ad3df Have all the baked bookshelf models use the same set of book models, fixes #621
This effectively cuts the number of book models by a factor equal to the number of bookshelf variants, which is 6 in the base mod
2021-06-19 00:01:24 +01:00
Electroblob77 e90d0950d4 Replace glide end sound with dummy sound (which for some reason seems not to play at all? Oh well ¯\_(ツ)_/¯), fixes #646 2021-06-18 17:47:55 +01:00
Electroblob77 888d4190d1 Store static map of mana flask charging recipes rather than iterating, fixes #671 2021-06-18 17:26:40 +01:00
Electroblob77 57696b2538 Make all forfeits respect claims via the methods in BlockUtils, fixes #672 2021-06-18 16:27:57 +01:00
Electroblob77 1440537f6b Missed one! 2021-06-18 14:23:40 +01:00
Electroblob77 a16ff88c8d Update credits (and put the discord mods in their rightful place at the top of the list <3) 2021-06-18 14:21:21 +01:00
Electroblob77 041de12ea7 Update Russian translations, courtesy of DrHesperus 2021-06-18 13:52:01 +01:00
Electroblob77 5e36f66c4e Update Korean translations, courtesy of red1854th 2021-06-18 13:51:32 +01:00
Electroblob77 a6a5a6104d Update French translations, courtesy of Crowller 2021-06-18 13:50:53 +01:00
Electroblob77 6f7b225ce8 ParticleBuilder is not thread-safe! 2021-06-18 13:49:45 +01:00
Electroblob77 81a85883d2 Consume mana/scrolls when forfeits are triggered, fixes #673 2021-06-18 13:49:29 +01:00
Electroblob 3f06d2fea4 Update bug_report.md 2021-06-18 00:08:29 +01:00
Electroblob 26b390268a Update ISSUE_TEMPLATE.md 2021-06-18 00:08:12 +01:00
Electroblob 486d55f349 Merge pull request #636 from LWHK/class-build
Add LF for CHS translation
2021-03-03 16:08:01 +00:00
TUsama 5948090d63 add LF manually
add LF manually
2021-03-01 17:55:32 +08:00
177 changed files with 12986 additions and 8088 deletions
+7
View File
@@ -0,0 +1,7 @@
{
"homepage": "https://www.curseforge.com/minecraft/mc-mods/electroblobs-wizardry",
"promos": {
"1.12.2-latest": "4.3.16",
"1.12.2-recommended": "4.3.16"
}
}
-20
View File
@@ -1,20 +0,0 @@
---
name: Bug report
about: Report a crash or something that isn't working as it should
title: "[Give a brief, relevant title here]"
labels: bug
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.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.]
Other mods involved: [only list mods that are relevant to the problem]
Link to crash report (if applicable): [give a link to the crash report on gist, pastebin, etc.]
+85
View File
@@ -0,0 +1,85 @@
name: 🐛 Bug Report
description: Something is not working as expected
title: "[Bug] <write the title here>"
labels: [ "bug", "Needs Triage" ]
body:
- type: markdown
attributes:
value: |
Please read the [guide for contributing](https://github.com/Electroblob77/Wizardry/blob/1.12.2/CONTRIBUTING.md) before posting. You may also find the [troubleshooting page](https://github.com/Electroblob77/Wizardry/wiki/Troubleshooting) helpful.
Please try the latest version of the mod and check if the issue persist before submitting a report.
- type: checkboxes
attributes:
label: Is there an existing issue for this?
description: Please search to see if an issue already exists for the bug you encountered.
options:
- label: I have searched the existing issues
required: true
- type: textarea
id: observed
attributes:
label: Observed behaviour
description: Describe what happened and when this happened
validations:
required: true
- type: textarea
id: expected
attributes:
label: Expected behaviour
description: Describe what you expected to happen
validations:
required: true
- type: textarea
id: reproduction
attributes:
label: Steps to reproduce
description: Step by step instructions on how to reproduce the observed behaviour
placeholder: |
1. Join a server
2. Cast the [spell]
3. ...
validations:
required: true
- type: input
id: crashlog
attributes:
label: Crashlog
description: If this was a crash, grab [minecraft instance]/logs/debug.log, upload it as a [gist](https://gist.github.com/) and paste the link here. Alternatively, give a link to the crash report on gist, pastebin if you already have one there.
placeholder: https://gist.github.com/<your-user>/abcdef124
- type: dropdown
id: environment
attributes:
label: Environment (Singleplayer/Server, etc.)
options:
- Singleplayer
- LAN game/Server
- Issue present on both sides
- Not sure
validations:
required: true
- type: input
id: mod-version
attributes:
label: Mod version
placeholder: 1.0.0
validations:
required: true
- type: input
id: minecraft-version
attributes:
label: Minecraft version
placeholder: 12.2.2
validations:
required: true
- type: input
id: forge-version
attributes:
label: Forge version
placeholder: 1.12.2 - 14.23.5.2860
validations:
required: true
- type: textarea
id: other-mods
attributes:
label: Other mods
description: List any other mods present when the bug was encountered, include the mod versions. You only have to fill this if no crash log was attached
+14
View File
@@ -0,0 +1,14 @@
blank_issues_enabled: false
contact_links:
- name: 📖 Mod Wiki Page
url: https://github.com/Electroblob77/Wizardry/wiki
about: Most features are documented in the mod's wiki page --- Link --->
- name: 💬 Electroblob's Wizardry discord
url: https://discord.gg/hs8yJP2
about: Ask for help or get more info on Discord. --- Discord Server link --->
- name: 💡 Feature requests / suggestions
url: https://discord.gg/hs8yJP2
about: Please use the discord server to suggest stuff. --- Discord Server link --->
- name: ❓ Question
url: https://discord.gg/hs8yJP2
about: Ask a question about the mod or the code. --- Discord Server link --->
-16
View File
@@ -1,16 +0,0 @@
---
name: Feature request
about: Make a suggestion for improving the mod
title: "[Give a brief, relevant title here]"
labels: enhancement
assignees: ''
---
Please read the [guide for contributing](https://github.com/Electroblob77/Wizardry/blob/1.12.2/CONTRIBUTING.md) before posting.
Minecraft versions: [if this suggestion only applies to some versions, list them here]
Suggestion details: [describe what you would like to see added to the mod and why]
Other mods involved: [if this suggestion involves integration with other mods, list them here]
-17
View File
@@ -1,17 +0,0 @@
---
name: Question
about: Ask a question about the mod or the code
title: "[Give a brief, relevant title here]"
labels: question
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.3.4 [change as necessary]
Question details: [explain your question here]
Other mods involved: [if this question involves other mods, list them here]
@@ -0,0 +1,38 @@
name: Potential Duplicates
on:
issues:
types: [opened, edited]
jobs:
run:
runs-on: ubuntu-latest
steps:
- uses: bubkoo/potential-duplicates@v1
with:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
label: potential duplicate
exclude: |
adding
add
creating
create
icons
icon
logos
logo
missing
miss
needing
need
please
requesting
request
1.12.2
wizardry
state: all
threshold: 0.7
comment: |
This issue is potentially a duplicate of one of the following issues:
{{#issues}}
- [#{{ number }}] {{ title }} ({{ accuracy }}% match)
{{/issues}}
+8 -2
View File
@@ -1,7 +1,7 @@
# Credits
Electroblob's Wizardry
Version 4.3.4
Version 4.3.7
For Minecraft 1.12.2
Designed, coded and textured by Electroblob
@@ -12,6 +12,11 @@ Thanks also to the Minecraft modding community, which always has an answer to my
In addition, I'd like to thank the following individuals for their contributions to the mod:
#### Discord Moderators (and all-round awesome people)
- FavouriteDragon
- WinDanesz
#### Code
- Corail31
@@ -27,13 +32,14 @@ In addition, I'd like to thank the following individuals for their contributions
- rafasoares
- xinyuan-liu
- SettingDust
- Aralu115
#### Translations
- Spanish: MadWrist, Alsentar
- Mexican Spanish: MadWrist
- Russian: VilagVil, kellixon, bigenergy, MugGod2, DrHesperus
- French: Hahdrim
- French: Hahdrim, Crowller
- Brazilian Portuguese: lorrampi
- Chinese (Simplified): ZHENGLOC, dragon-evol, Hokorizero, TUsama, Determancer
- Korean: shejery, rewi_wire, 방통, red1854th
+2 -2
View File
@@ -1,7 +1,7 @@
Please read the [guide for contributing](https://github.com/Electroblob77/Wizardry/blob/1.12.2/guide_for_contributing.md) before posting.
Please read the [guide for contributing](https://github.com/Electroblob77/Wizardry/blob/1.12.2/guide_for_contributing.md) before posting. You may also find the [troubleshooting page](https://github.com/Electroblob77/Wizardry/wiki/Troubleshooting) helpful.
Minecraft version: 1.12.2 [change as necessary]
Wizardry version: 4.3.4 [change as necessary]
Wizardry version: [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.]
+115 -50
View File
@@ -1,64 +1,130 @@
import groovy.json.JsonOutput
buildscript {
repositories {
jcenter()
maven { url = "http://files.minecraftforge.net/maven" }
gradlePluginPortal()
maven {
name 'MinecraftForge'
url 'https://maven.minecraftforge.net/'
}
maven {
name 'Garden of Fancy'
url 'https://maven.gofancy.wtf/releases'
}
//fallback for fancygradle maven
//mavenLocal()
maven {
name 'Sponge'
url 'https://repo.spongepowered.org/repository/maven-public/'
}
}
dependencies {
classpath 'net.minecraftforge.gradle:ForgeGradle:2.3-SNAPSHOT'
classpath 'net.minecraftforge.gradle:ForgeGradle:5.+'
classpath 'org.ajoberstar.grgit:grgit-gradle:3.1.1'
classpath group: 'wtf.gofancy.fancygradle', name: 'wtf.gofancy.fancygradle.gradle.plugin', version: '1.1.+'
}
repositories.each {
if (it instanceof MavenArtifactRepository && it.url.toString() == "https://files.minecraftforge.net/maven") {
it.url = "https://maven.minecraftforge.net"
}
}
}
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.
apply plugin: 'net.minecraftforge.gradle'
apply plugin: 'idea'
apply plugin: 'maven-publish'
apply plugin: org.ajoberstar.grgit.gradle.GrgitPlugin
apply plugin: 'wtf.gofancy.fancygradle'
version = "4.3.4" // There, it matches semver, happy now?
version = "4.3.16"
group= "electroblob.wizardry"// http://maven.apache.org/guides/mini/guide-naming-conventions.html
archivesBaseName = "ElectroblobsWizardry"
sourceCompatibility = targetCompatibility = "1.8" // Need this here so eclipse task generates correctly.
compileJava {
sourceCompatibility = targetCompatibility = "1.8"
sourceCompatibility = targetCompatibility = compileJava.sourceCompatibility = compileJava.targetCompatibility = '1.8' // Need this here so eclipse task generates correctly.
minecraft {
// The mappings can be changed at any time, and must be in the following format.
// snapshot_YYYYMMDD Snapshot are built nightly.
// stable_# Stables are built at the discretion of the MCP team.
// Use non-default mappings at your own risk. they may not always work.
// Simply re-run your setup task after changing the mappings to update your workspace.
//mappings channel: 'snapshot', version: '20171003-1.12'
mappings channel: "stable", version: "39-1.12"
// makeObfSourceJar = false // an Srg named sources jar is made by default. uncomment this to disable.
// accessTransformer = file('src/main/resources/META-INF/accesstransformer.cfg')
// Default run configurations.
// These can be tweaked, removed, or duplicated as needed.
def argsz = ['--username', 'WinDanesz', '--user', 'WinDanesz', '--uuid', '7faee354-8c60-4f5c-9862-fc0ce5f7f575']
runs {
client {
workingDirectory project.file('run')
// Recommended logging data for a userdev environment
property 'forge.logging.markers', 'SCAN,REGISTRIES,REGISTRYDUMP'
// Recommended logging level for the console
property 'forge.logging.console.level', 'info'
args argsz
}
server {
// Recommended logging data for a userdev environment
property 'forge.logging.markers', 'SCAN,REGISTRIES,REGISTRYDUMP'
// Recommended logging level for the console
property 'forge.logging.console.level', 'info'
}
}
}
repositories {
google()
mavenCentral()
maven {
// location of the maven that hosts JEI files
name = "Progwml6 maven"
url = "http://dvs1.progwml6.com/files/maven"
url = uri('https://www.cursemaven.com')
content {
includeGroup 'curse.maven'
}
}
maven {
// location of a maven mirror for JEI files, as a fallback
name = "ModMaven"
url = "modmaven.k-4u.nl"
name = 'Modrinth'
url = uri('https://api.modrinth.com/maven')
content {
includeGroup 'maven.modrinth'
}
}
maven {
name = "Curseforge Maven"
url = "https://minecraft.curseforge.com/api/maven/"
name = 'Sponge'
url = uri('https://repo.spongepowered.org/maven')
}
maven {
url = uri('https://maven.blamejared.com')
}
maven {
url = uri('https://m2.dv8tion.net/releases')
}
maven {
url = uri('https://jitpack.io')
}
}
minecraft {
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.
// snapshot_YYYYMMDD snapshot are built nightly.
// stable_# stables are built at the discretion of the MCP team.
// Use non-default mappings at your own risk. they may not always work.
// simply re-run your setup task after changing the mappings to update your workspace.
mappings = "stable_39"
// makeObfSourceJar = false // an Srg named sources jar is made by default. uncomment this to disable.
}
dependencies {
// 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
runtime "mezz.jei:jei_${mc_version}:${jei_version}"
deobfCompile "baubles:Baubles:${mc_version_short}:${baubles_version}"
deobfCompile "antique-atlas:antiqueatlas:${mc_version}:${antique_atlas_version}"
//// MC version ////
// Specify the version of Minecraft to use, If this is any group other then 'net.minecraft' it is assumed
// that the dep is a ForgeGradle 'patcher' dependency. And it's patches will be applied.
// The userdev artifact is a special name and will get all sorts of transformations applied to it.
minecraft "net.minecraftforge:forge:${project.mc_version}-${project.forge_version}"
//// MC version ////
implementation fg.deobf("curse.maven:baubles-${baubles_projectid}:${baubles_fileid}")
implementation fg.deobf("mezz.jei:jei_${mc_version}:${jei_version}")
implementation fg.deobf("curse.maven:antique-atlas-${antiqueatlas_projectid}:${antiqueatlas_fileid}")
}
@@ -68,20 +134,19 @@ task deobfJar(type: Jar) {
}
processResources {
// this will ensure that this task is redone when the versions change.
inputs.property "version", project.version
inputs.property "mcversion", project.minecraft.version
// replace stuff in mcmod.info, nothing else
// replace tokens in mcmod.info, and pack.mcmeta
from(sourceSets.main.resources.srcDirs) {
include 'pack.mcmeta'
include 'mcmod.info'
// replace version and mcversion
expand 'version':project.version, 'mcversion':project.minecraft.version
}
// copy everything else except the mcmod.info
from(sourceSets.main.resources.srcDirs) {
exclude 'mcmod.info'
duplicatesStrategy = 'include'
}
fancyGradle {
patches {
resources
coremods
asm
}
}
+15 -1
View File
@@ -1,8 +1,22 @@
# Sets default memory used for gradle commands. Can be overridden by user or command line properties.
# This is required to provide enough memory for the Minecraft decompilation process.
org.gradle.jvmargs=-Xmx3G
mod_id=ebwizardry
mc_version=1.12.2
mc_version_short=1.12
jei_version=4.15.0.291
baubles_version=1.5.2
antique_atlas_version=4.6.3
antique_atlas_version=4.6.3
forge_version=14.23.5.2860
mappings_version=39
mappings_channel=stable
mappings_mc_version=1.12
baubles_projectid=227083
baubles_fileid=2518667
jei_projectid=238222
artemislib_projectid=313590
artemislib_version=1.0.6
artemislib_fileid=2741812
antiqueatlas_projectid=227795
antiqueatlas_fileid=2823030
org.gradle.daemon=false
Binary file not shown.
+2 -3
View File
@@ -1,6 +1,5 @@
#Mon Sep 14 12:28:28 PDT 2015
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-7.3.3-all.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-2.14-bin.zip
zipStorePath=wrapper/dists
Vendored
+188 -120
View File
@@ -1,164 +1,232 @@
#!/usr/bin/env bash
#!/bin/sh
#
# Copyright © 2015-2021 the original authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
##############################################################################
##
## Gradle start up script for UN*X
##
#
# Gradle start up script for POSIX generated by Gradle.
#
# Important for running:
#
# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is
# noncompliant, but you have some other compliant shell such as ksh or
# bash, then to run this script, type that shell name before the whole
# command line, like:
#
# ksh Gradle
#
# Busybox and similar reduced shells will NOT work, because this script
# requires all of these POSIX shell features:
# * functions;
# * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,
# «${var#prefix}», «${var%suffix}», and «$( cmd )»;
# * compound commands having a testable exit status, especially «case»;
# * various built-in commands including «command», «set», and «ulimit».
#
# Important for patching:
#
# (2) This script targets any POSIX shell, so it avoids extensions provided
# by Bash, Ksh, etc; in particular arrays are avoided.
#
# The "traditional" practice of packing multiple parameters into a
# space-separated string is a well documented source of bugs and security
# problems, so this is (mostly) avoided, by progressively accumulating
# options in "$@", and eventually passing that to Java.
#
# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,
# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;
# see the in-line comments for details.
#
# There are tweaks for specific operating systems such as AIX, CygWin,
# Darwin, MinGW, and NonStop.
#
# (3) This script is generated from the Groovy template
# https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
# within the Gradle project.
#
# You can find Gradle at https://github.com/gradle/gradle/.
#
##############################################################################
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS=""
# Attempt to set APP_HOME
# Resolve links: $0 may be a link
app_path=$0
# Need this for daisy-chained symlinks.
while
APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path
[ -h "$app_path" ]
do
ls=$( ls -ld "$app_path" )
link=${ls#*' -> '}
case $link in #(
/*) app_path=$link ;; #(
*) app_path=$APP_HOME$link ;;
esac
done
APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit
APP_NAME="Gradle"
APP_BASE_NAME=`basename "$0"`
APP_BASE_NAME=${0##*/}
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD="maximum"
MAX_FD=maximum
warn ( ) {
warn () {
echo "$*"
}
} >&2
die ( ) {
die () {
echo
echo "$*"
echo
exit 1
}
} >&2
# OS specific support (must be 'true' or 'false').
cygwin=false
msys=false
darwin=false
case "`uname`" in
CYGWIN* )
cygwin=true
;;
Darwin* )
darwin=true
;;
MINGW* )
msys=true
;;
nonstop=false
case "$( uname )" in #(
CYGWIN* ) cygwin=true ;; #(
Darwin* ) darwin=true ;; #(
MSYS* | MINGW* ) msys=true ;; #(
NONSTOP* ) nonstop=true ;;
esac
# For Cygwin, ensure paths are in UNIX format before anything is touched.
if $cygwin ; then
[ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"`
fi
# Attempt to set APP_HOME
# Resolve links: $0 may be a link
PRG="$0"
# Need this for relative symlinks.
while [ -h "$PRG" ] ; do
ls=`ls -ld "$PRG"`
link=`expr "$ls" : '.*-> \(.*\)$'`
if expr "$link" : '/.*' > /dev/null; then
PRG="$link"
else
PRG=`dirname "$PRG"`"/$link"
fi
done
SAVED="`pwd`"
cd "`dirname \"$PRG\"`/" >&-
APP_HOME="`pwd -P`"
cd "$SAVED" >&-
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
# Determine the Java command to use to start the JVM.
if [ -n "$JAVA_HOME" ] ; then
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
# IBM's JDK on AIX uses strange locations for the executables
JAVACMD="$JAVA_HOME/jre/sh/java"
JAVACMD=$JAVA_HOME/jre/sh/java
else
JAVACMD="$JAVA_HOME/bin/java"
JAVACMD=$JAVA_HOME/bin/java
fi
if [ ! -x "$JAVACMD" ] ; then
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
Please set the JAVA_HOME variable in your environment to match the
locations of your Java installation."
location of your Java installation."
fi
else
JAVACMD="java"
JAVACMD=java
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
Please set the JAVA_HOME variable in your environment to match the
locations of your Java installation."
location of your Java installation."
fi
# Increase the maximum file descriptors if we can.
if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
MAX_FD_LIMIT=`ulimit -H -n`
if [ $? -eq 0 ] ; then
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
MAX_FD="$MAX_FD_LIMIT"
fi
ulimit -n $MAX_FD
if [ $? -ne 0 ] ; then
warn "Could not set maximum file descriptor limit: $MAX_FD"
fi
else
warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
fi
fi
# For Darwin, add options to specify how the application appears in the dock
if $darwin; then
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
fi
# For Cygwin, switch paths to Windows format before running java
if $cygwin ; then
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
# We build the pattern for arguments to be converted via cygpath
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
SEP=""
for dir in $ROOTDIRSRAW ; do
ROOTDIRS="$ROOTDIRS$SEP$dir"
SEP="|"
done
OURCYGPATTERN="(^($ROOTDIRS))"
# Add a user-defined pattern to the cygpath arguments
if [ "$GRADLE_CYGPATTERN" != "" ] ; then
OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
fi
# Now convert the arguments - kludge to limit ourselves to /bin/sh
i=0
for arg in "$@" ; do
CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
else
eval `echo args$i`="\"$arg\""
fi
i=$((i+1))
done
case $i in
(0) set -- ;;
(1) set -- "$args0" ;;
(2) set -- "$args0" "$args1" ;;
(3) set -- "$args0" "$args1" "$args2" ;;
(4) set -- "$args0" "$args1" "$args2" "$args3" ;;
(5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
(6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
(7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
(8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
(9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
case $MAX_FD in #(
max*)
MAX_FD=$( ulimit -H -n ) ||
warn "Could not query maximum file descriptor limit"
esac
case $MAX_FD in #(
'' | soft) :;; #(
*)
ulimit -n "$MAX_FD" ||
warn "Could not set maximum file descriptor limit to $MAX_FD"
esac
fi
# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
function splitJvmOpts() {
JVM_OPTS=("$@")
}
eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
# Collect all arguments for the java command, stacking in reverse order:
# * args from the command line
# * the main class name
# * -classpath
# * -D...appname settings
# * --module-path (only if needed)
# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.
exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
# For Cygwin or MSYS, switch paths to Windows format before running java
if "$cygwin" || "$msys" ; then
APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" )
JAVACMD=$( cygpath --unix "$JAVACMD" )
# Now convert the arguments - kludge to limit ourselves to /bin/sh
for arg do
if
case $arg in #(
-*) false ;; # don't mess with options #(
/?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath
[ -e "$t" ] ;; #(
*) false ;;
esac
then
arg=$( cygpath --path --ignore --mixed "$arg" )
fi
# Roll the args list around exactly as many times as the number of
# args, so each arg winds up back in the position where it started, but
# possibly modified.
#
# NB: a `for` loop captures its iteration list before it begins, so
# changing the positional parameters here affects neither the number of
# iterations, nor the values presented in `arg`.
shift # remove old arg
set -- "$@" "$arg" # push replacement arg
done
fi
# Collect all arguments for the java command;
# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of
# shell script including quotes and variable substitutions, so put them in
# double quotes to make sure that they get re-expanded; and
# * put everything else in single quotes, so that it's not re-expanded.
set -- \
"-Dorg.gradle.appname=$APP_BASE_NAME" \
-classpath "$CLASSPATH" \
org.gradle.wrapper.GradleWrapperMain \
"$@"
# Use "xargs" to parse quoted args.
#
# With -n1 it outputs one arg per line, with the quotes and backslashes removed.
#
# In Bash we could simply go:
#
# readarray ARGS < <( xargs -n1 <<<"$var" ) &&
# set -- "${ARGS[@]}" "$@"
#
# but POSIX shell has neither arrays nor command substitution, so instead we
# post-process each arg (as a line of input to sed) to backslash-escape any
# character that might be a shell metacharacter, then use eval to reverse
# that process (while maintaining the separation between arguments), and wrap
# the whole thing up as a single "set" statement.
#
# This will of course break if any of these variables contains a newline or
# an unmatched quote.
#
eval "set -- $(
printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" |
xargs -n1 |
sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' |
tr '\n' ' '
)" '"$@"'
exec "$JAVACMD" "$@"
Vendored
+29 -30
View File
@@ -1,3 +1,19 @@
@rem
@rem Copyright 2015 the original author or authors.
@rem
@rem Licensed under the Apache License, Version 2.0 (the "License");
@rem you may not use this file except in compliance with the License.
@rem You may obtain a copy of the License at
@rem
@rem https://www.apache.org/licenses/LICENSE-2.0
@rem
@rem Unless required by applicable law or agreed to in writing, software
@rem distributed under the License is distributed on an "AS IS" BASIS,
@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@rem See the License for the specific language governing permissions and
@rem limitations under the License.
@rem
@if "%DEBUG%" == "" @echo off
@rem ##########################################################################
@rem
@@ -8,26 +24,29 @@
@rem Set local scope for the variables with windows NT shell
if "%OS%"=="Windows_NT" setlocal
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
set DEFAULT_JVM_OPTS=
set DIRNAME=%~dp0
if "%DIRNAME%" == "" set DIRNAME=.
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%
@rem Resolve any "." and ".." in APP_HOME to make it shorter.
for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
@rem Find java.exe
if defined JAVA_HOME goto findJavaFromJavaHome
set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
if "%ERRORLEVEL%" == "0" goto init
if "%ERRORLEVEL%" == "0" goto execute
echo.
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo locations of your Java installation.
echo location of your Java installation.
goto fail
@@ -35,44 +54,24 @@ goto fail
set JAVA_HOME=%JAVA_HOME:"=%
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
if exist "%JAVA_EXE%" goto init
if exist "%JAVA_EXE%" goto execute
echo.
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo locations of your Java installation.
echo location of your Java installation.
goto fail
:init
@rem Get command-line arguments, handling Windowz variants
if not "%OS%" == "Windows_NT" goto win9xME_args
if "%@eval[2+2]" == "4" goto 4NT_args
:win9xME_args
@rem Slurp the command line arguments.
set CMD_LINE_ARGS=
set _SKIP=2
:win9xME_args_slurp
if "x%~1" == "x" goto execute
set CMD_LINE_ARGS=%*
goto execute
:4NT_args
@rem Get arguments from the 4NT Shell from JP Software
set CMD_LINE_ARGS=%$
:execute
@rem Setup the command line
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
@rem Execute Gradle
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
:end
@rem End local scope for the variables with windows NT shell
@@ -87,4 +86,4 @@ exit /b 1
:mainEnd
if "%OS%"=="Windows_NT" endlocal
:omega
:omega
@@ -43,6 +43,10 @@ public class CommonProxy {
public void registerRenderers(){}
public void registerItemColorHandlers(){}
public void registerModelProperties(){}
public void initialiseLayers(){}
public void initialiseAnimations(){}
@@ -175,6 +179,8 @@ public class CommonProxy {
public void handleConquerShrinePacket(PacketConquerShrine.Message message){}
public void handleBombExplosionPacket(PacketBombExplosion.Message message){}
// SECTION Misc
// ===============================================================================================================
@@ -138,6 +138,12 @@ public final class Settings {
new ResourceLocation(Wizardry.MODID, "shrine_5"),
new ResourceLocation(Wizardry.MODID, "shrine_6"),
new ResourceLocation(Wizardry.MODID, "shrine_7")};
/** <b>[Server-only]</b> Whether conquered shrines should regenerate after a period of time. */
public boolean shrineRegenerationEnabled = true;
/** <b>[Server-only]</b> Time in minutes for a conquered shrine to regenerate. */
public int shrineRegenerationTime = 1;
/** <b>[Server-only]</b> Whether players can loot shrines multiple times. If false, each player can only loot each shrine once. */
public boolean shrineAllowMultipleLoot = false;
/** <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. */
@@ -156,6 +162,12 @@ public final class Settings {
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. */
public double evilWizardChance = 0.2;
public double cooldownReductionPerLevel = 0.15;
public double potencyIncreasePerTier = 0.15;
public double durationIncreasePerLevel = 0.25;
public double rangeIncreasePerLevel = 0.25;
public double blastIncreasePerLevel = 0.25;
public double frostSlownessIncreasePerLevel = 0.5;
/** <b>[Server-only]</b> List of dimension ids in which to generate crystal ore. */
public int[] oreDimensions = {0};
/** <b>[Server-only]</b> List of dimension ids in which to generate crystal flowers. */
@@ -192,12 +204,25 @@ public final class Settings {
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 damage should be registered with the old system (wizardry_magic/indirect_wizardry_magic) prefixed damage with the elements like
* necromancy_indirect_wizardry_magic, necromancy_wizardry_magic*/
public boolean damageTypePerElement = false;
/** <b>[Server-only]</b> Whether spell books are consumed when they are bound to a wand.*/
public boolean singleUseSpellBooks = false;
/** <b>[Server-only]</b> Whether to prevent binding the same spell to a wand multiple times*/
public boolean preventBindingSameSpellTwiceToWands = false;
/** <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 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;
/** <b>[Server-only]</b> Whether wands only decrement their cooldowns if a player holds them. */
public boolean wandsMustBeHeldToDecrementCooldown = false;
/** <b>[Server-only]</b> Whether to enable Wizardry mob loot injection. Allows an easier switch instead of blacklisting all entities. */
public boolean injectMobDrops = true;
/** <b>[Server-only]</b> The time in ticks after which recent spell casts expire and no longer count toward progression penalties. */
public int recentSpellExpiryTime = 1200;
/**
* <b>[Server-only]</b> List of registry names of entities which summoned creatures are allowed to attack, in addition
* to the defaults.
@@ -241,6 +266,26 @@ public final class Settings {
/** <b>[Server-only]</b> List of registry names of biomes in which wizardry's hostile mobs cannot spawn. */
public ResourceLocation[] mobSpawnBiomeBlacklist = toResourceLocations("mushroom_island", "mushroom_island_shore");
// Mana and upgrade constants
/** <b>[Server-only]</b> The amount of mana a crystal shard is worth */
public int manaPerShard = 10;
/** <b>[Server-only]</b> The amount of mana each magic crystal is worth */
public int manaPerCrystal = 100;
/** <b>[Server-only]</b> The amount of mana a grand magic crystal is worth */
public int grandCrystalMana = 400;
/** <b>[Server-only]</b> The maximum number of one type of wand upgrade which can be applied to a wand. */
public int upgradeStackLimit = 3;
/** <b>[Server-only]</b> The bonus amount of wand upgrades that can be applied to a non-elemental wand. */
public int nonElementalUpgradeBonus = 3;
/** <b>[Server-only]</b> The fraction by which maximum charge is increased for each level of storage upgrade. */
public float storageIncreasePerLevel = 0.15f;
/** <b>[Server-only]</b> The amount of mana given for a kill for each level of siphon upgrade. */
public int siphonManaPerLevel = 5;
/** <b>[Server-only]</b> The number of ticks between each mana increase for wands with the condenser upgrade. */
public int condenserTickInterval = 50;
/** <b>[Server-only]</b> The number of spell slots a wand has with no attunement upgrades applied. */
public int baseSpellSlots = 5;
// Commands (these don't need synchronising since typing a command always queries the server).
/**
* <b>[Server-only]</b> The maximum allowed multiplier for the /cast command. This limit is here to stop people from
@@ -310,10 +355,40 @@ public final class Settings {
* velocity-based one.
*/
public boolean replaceVanillaFallDamage = true;
/** <b>[Synchronised]</b> Whether spell book colors are only shown when the player has the Archivist's Eyeglass equipped. */
public boolean spellBookColorsRequireArchivistsEyeglass = false;
/** <b>[Synchronised]</b> Chance of 'misreading' an undiscovered spell and triggering a forfeit instead. */
public double forfeitChance = 0.2;
/** <b>[Synchronised]</b> Progression requirements for upgrading a wand to each tier. */
public int[] progressionRequirements = {1500, 3500, 6000};
// Integer wrappers for tier values
public Integer noviceMaxCharge = 700;
public Integer apprenticeMaxCharge = 1000;
public Integer advancedMaxCharge = 1500;
public Integer masterMaxCharge = 2500;
public Integer noviceUpgradeLimit = 3;
public Integer apprenticeUpgradeLimit = 5;
public Integer advancedUpgradeLimit = 7;
public Integer masterUpgradeLimit = 9;
/** <b>[Synchronised]</b> Multiplier for condenser upgrade mana regeneration amount */
public double condenserAmountMultiplier = 1.0;
/** <b>[Synchronised]</b> Whether flesh spells (DiamondFlesh, IronFlesh, OakFlesh) apply slowness */
public boolean fleshSpellsCauseSlowness = true;
/** <b>[Synchronised]</b> Armor bonus for DiamondFlesh spell */
public double diamondFleshArmorBonus = 4.0;
/** <b>[Synchronised]</b> Armor toughness bonus for DiamondFlesh spell */
public double diamondFleshArmorToughnessBonus = 3.0;
/** <b>[Synchronised]</b> Armor bonus for IronFlesh spell */
public double ironFleshArmorBonus = 4.0;
/** <b>[Synchronised]</b> Armor bonus for OakFlesh spell */
public double oakFleshArmorBonus = 3.0;
/** <b>[Synchronised]</b> Health bonus for OakFlesh spell */
public double oakFleshHealthBonus = 0.2;
/**
* <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.
@@ -377,12 +452,19 @@ public final class Settings {
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> Whether to initialise the handbook's data. Setting this to false will break the in-game handbook, but might help with some
* startup crashes */
public boolean loadHandbook = true;
/** <b>[Client-only]</b> Whether to allow the Arcane Workbench and lectern search field to lose focus and start unfocused. */
public boolean unfocusedSearchBars = false;
/** <b>[Client-only]</b> The position of the spell HUD. */
public GuiPosition spellHUDPosition = GuiPosition.BOTTOM_LEFT;
public static final String DEFAULT_HUD_SKIN_KEY = "default"; // Defined here so it's not in a client-only class.
/** <b>[Client-only]</b> The string identifier of the skin used for the spell HUD. */
public String spellHUDSkin = DEFAULT_HUD_SKIN_KEY;
/** <b>[Client-only]</b> Whether to show elemental colors on spell books for discovered spells. */
public boolean spellBookColors = true;
/** Set of constants for each of the eight positions that the spell HUD can be in. */
public enum GuiPosition {
@@ -503,9 +585,32 @@ public final class Settings {
setupArtefactsConfig();
setupResistancesConfig();
// Update the constants with new values
updateConstantsFromSettings();
config.save();
}
/** Updates the Constants class with the current settings values */
public void updateConstantsFromSettings(){
electroblob.wizardry.constants.Constants.MANA_PER_SHARD = this.manaPerShard;
electroblob.wizardry.constants.Constants.MANA_PER_CRYSTAL = this.manaPerCrystal;
electroblob.wizardry.constants.Constants.GRAND_CRYSTAL_MANA = this.grandCrystalMana;
electroblob.wizardry.constants.Constants.UPGRADE_STACK_LIMIT = this.upgradeStackLimit;
electroblob.wizardry.constants.Constants.NON_ELEMENTAL_UPGRADE_BONUS = this.nonElementalUpgradeBonus;
electroblob.wizardry.constants.Constants.COOLDOWN_REDUCTION_PER_LEVEL = (float) this.cooldownReductionPerLevel;
electroblob.wizardry.constants.Constants.STORAGE_INCREASE_PER_LEVEL = this.storageIncreasePerLevel;
electroblob.wizardry.constants.Constants.POTENCY_INCREASE_PER_TIER = (float) this.potencyIncreasePerTier;
electroblob.wizardry.constants.Constants.DURATION_INCREASE_PER_LEVEL = (float) this.durationIncreasePerLevel;
electroblob.wizardry.constants.Constants.RANGE_INCREASE_PER_LEVEL = (float) this.rangeIncreasePerLevel;
electroblob.wizardry.constants.Constants.BLAST_RADIUS_INCREASE_PER_LEVEL = (float) this.blastIncreasePerLevel;
electroblob.wizardry.constants.Constants.FROST_SLOWNESS_PER_LEVEL = (float) this.frostSlownessIncreasePerLevel;
electroblob.wizardry.constants.Constants.SIPHON_MANA_PER_LEVEL = this.siphonManaPerLevel;
electroblob.wizardry.constants.Constants.CONDENSER_TICK_INTERVAL = this.condenserTickInterval;
electroblob.wizardry.item.ItemWand.BASE_SPELL_SLOTS = this.baseSpellSlots;
electroblob.wizardry.data.WizardData.MAX_RECENT_SPELLS = this.baseSpellSlots;
}
void checkForRedundantOptions(String categoryName, Collection<String> validKeys){
ConfigCategory category = config.getCategory(categoryName);
@@ -612,6 +717,85 @@ public final class Settings {
dispenserBlockDamage = property.getBoolean();
propOrder.add(property.getName());
property = config.get(GAMEPLAY_CATEGORY, "damageTypePerElement", false,
"Whether damage should be registered with the old system (wizardry_magic/indirect_wizardry_magic) prefixed damage with the elements like "
+ "necromancy_indirect_wizardry_magic, necromancy_wizardry_magic. This is disabled by default to not break existing modpacks."
+ "The intention of this setting is to allow differentiating various damage types for e.g. the Distinct Damage Descriptions mod");
property.setLanguageKey("config." + Wizardry.MODID + ".damage_type_per_element");
Wizardry.proxy.setToNamedBooleanEntry(property);
damageTypePerElement = property.getBoolean();
propOrder.add(property.getName());
property = config.get(GAMEPLAY_CATEGORY, "singleUseSpellBooks", false,
"Whether spell books are consumed when they are bound to a wand.");
property.setLanguageKey("config." + Wizardry.MODID + ".single_use_spell_books");
Wizardry.proxy.setToNamedBooleanEntry(property);
singleUseSpellBooks = property.getBoolean();
propOrder.add(property.getName());
property = config.get(GAMEPLAY_CATEGORY, "preventBindingSameSpellTwiceToWands", false,
"Whether to prevent binding the same spell to a wand multiple times");
property.setLanguageKey("config." + Wizardry.MODID + ".prevent_binding_same_spell_twice_to_wands");
Wizardry.proxy.setToNamedBooleanEntry(property);
preventBindingSameSpellTwiceToWands = property.getBoolean();
propOrder.add(property.getName());
property = config.get(GAMEPLAY_CATEGORY, "manaPerShard", 10,
"The amount of mana a crystal shard is worth.", 1, 1000);
property.setLanguageKey("config." + Wizardry.MODID + ".mana_per_shard");
Wizardry.proxy.setToNumberSliderEntry(property);
manaPerShard = property.getInt();
propOrder.add(property.getName());
property = config.get(GAMEPLAY_CATEGORY, "manaPerCrystal", 100,
"The amount of mana each magic crystal is worth.", 1, 10000);
property.setLanguageKey("config." + Wizardry.MODID + ".mana_per_crystal");
Wizardry.proxy.setToNumberSliderEntry(property);
manaPerCrystal = property.getInt();
propOrder.add(property.getName());
property = config.get(GAMEPLAY_CATEGORY, "grandCrystalMana", 400,
"The amount of mana a grand magic crystal is worth.", 1, 10000);
property.setLanguageKey("config." + Wizardry.MODID + ".grand_crystal_mana");
Wizardry.proxy.setToNumberSliderEntry(property);
grandCrystalMana = property.getInt();
propOrder.add(property.getName());
property = config.get(GAMEPLAY_CATEGORY, "upgradeStackLimit", 3,
"The maximum number of one type of wand upgrade which can be applied to a wand.", 1, 10);
property.setLanguageKey("config." + Wizardry.MODID + ".upgrade_stack_limit");
Wizardry.proxy.setToNumberSliderEntry(property);
upgradeStackLimit = property.getInt();
propOrder.add(property.getName());
property = config.get(GAMEPLAY_CATEGORY, "nonElementalUpgradeBonus", 3,
"The bonus amount of wand upgrades that can be applied to a non-elemental wand.", 0, 10);
property.setLanguageKey("config." + Wizardry.MODID + ".non_elemental_upgrade_bonus");
Wizardry.proxy.setToNumberSliderEntry(property);
nonElementalUpgradeBonus = property.getInt();
propOrder.add(property.getName());
property = config.get(GAMEPLAY_CATEGORY, "siphonManaPerLevel", 5,
"The amount of mana given for a kill for each level of siphon upgrade.", 0, 100);
property.setLanguageKey("config." + Wizardry.MODID + ".siphon_mana_per_level");
Wizardry.proxy.setToNumberSliderEntry(property);
siphonManaPerLevel = property.getInt();
propOrder.add(property.getName());
property = config.get(GAMEPLAY_CATEGORY, "condenserTickInterval", 50,
"The number of ticks between each mana increase for wands with the condenser upgrade.", 1, 1000);
property.setLanguageKey("config." + Wizardry.MODID + ".condenser_tick_interval");
Wizardry.proxy.setToNumberSliderEntry(property);
condenserTickInterval = property.getInt();
propOrder.add(property.getName());
property = config.get(GAMEPLAY_CATEGORY, "baseSpellSlots", 5,
"The number of spell slots a wand has with no attunement upgrades applied.", 1, 5);
property.setLanguageKey("config." + Wizardry.MODID + ".base_spell_slots");
Wizardry.proxy.setToNumberSliderEntry(property);
baseSpellSlots = property.getInt();
propOrder.add(property.getName());
property = config.get(GAMEPLAY_CATEGORY, "playersMoveEachOther", true,
"Whether to allow players to move other players around using magic.");
property.setLanguageKey("config." + Wizardry.MODID + ".players_move_each_other");
@@ -663,6 +847,13 @@ public final class Settings {
bonemealGrowsCrystalFlowers = property.getBoolean();
propOrder.add(property.getName());
property = config.get(GAMEPLAY_CATEGORY, "wandsMustBeHeldToDecrementCooldown", false,
"Whether wands only decrement their cooldowns if a player holds them.");
property.setLanguageKey("config." + Wizardry.MODID + ".wands_must_be_held_to_decrement_cooldown");
Wizardry.proxy.setToNamedBooleanEntry(property);
wandsMustBeHeldToDecrementCooldown = property.getBoolean();
propOrder.add(property.getName());
checkForRedundantOptions(GAMEPLAY_CATEGORY, propOrder); // Must be before the order is set!
config.setCategoryPropertyOrder(GAMEPLAY_CATEGORY, propOrder);
@@ -729,6 +920,122 @@ public final class Settings {
progressionRequirements = property.getIntList();
propOrder.add(property.getName());
property = config.get(DIFFICULTY_CATEGORY, "tierMaxCharges", new int[]{700, 1000, 1500, 2500},
"Maximum mana each tier can store (novice, apprentice, advanced, master respectively).");
property.setLanguageKey("config." + Wizardry.MODID + ".tier_max_charges");
property.setRequiresWorldRestart(true);
//tierMaxCharges = property.getIntList();
propOrder.add(property.getName());
// Individual tier configuration
property = config.get(DIFFICULTY_CATEGORY, "noviceMaxCharge", 700,
"Maximum mana a novice wand can store.");
property.setLanguageKey("config." + Wizardry.MODID + ".novice_max_charge");
property.setRequiresWorldRestart(true);
noviceMaxCharge = property.getInt();
propOrder.add(property.getName());
property = config.get(DIFFICULTY_CATEGORY, "apprenticeMaxCharge", 1000,
"Maximum mana an apprentice wand can store.");
property.setLanguageKey("config." + Wizardry.MODID + ".apprentice_max_charge");
property.setRequiresWorldRestart(true);
apprenticeMaxCharge = property.getInt();
propOrder.add(property.getName());
property = config.get(DIFFICULTY_CATEGORY, "advancedMaxCharge", 1500,
"Maximum mana an advanced wand can store.");
property.setLanguageKey("config." + Wizardry.MODID + ".advanced_max_charge");
property.setRequiresWorldRestart(true);
advancedMaxCharge = property.getInt();
propOrder.add(property.getName());
property = config.get(DIFFICULTY_CATEGORY, "masterMaxCharge", 2500,
"Maximum mana a master wand can store.");
property.setLanguageKey("config." + Wizardry.MODID + ".master_max_charge");
property.setRequiresWorldRestart(true);
masterMaxCharge = property.getInt();
propOrder.add(property.getName());
property = config.get(DIFFICULTY_CATEGORY, "noviceUpgradeLimit", 3,
"Maximum number of upgrades a novice wand can have.");
property.setLanguageKey("config." + Wizardry.MODID + ".novice_upgrade_limit");
property.setRequiresWorldRestart(true);
noviceUpgradeLimit = property.getInt();
propOrder.add(property.getName());
property = config.get(DIFFICULTY_CATEGORY, "apprenticeUpgradeLimit", 5,
"Maximum number of upgrades an apprentice wand can have.");
property.setLanguageKey("config." + Wizardry.MODID + ".apprentice_upgrade_limit");
property.setRequiresWorldRestart(true);
apprenticeUpgradeLimit = property.getInt();
propOrder.add(property.getName());
property = config.get(DIFFICULTY_CATEGORY, "advancedUpgradeLimit", 7,
"Maximum number of upgrades an advanced wand can have.");
property.setLanguageKey("config." + Wizardry.MODID + ".advanced_upgrade_limit");
property.setRequiresWorldRestart(true);
advancedUpgradeLimit = property.getInt();
propOrder.add(property.getName());
property = config.get(DIFFICULTY_CATEGORY, "masterUpgradeLimit", 9,
"Maximum number of upgrades a master wand can have.");
property.setLanguageKey("config." + Wizardry.MODID + ".master_upgrade_limit");
property.setRequiresWorldRestart(true);
masterUpgradeLimit = property.getInt();
propOrder.add(property.getName());
property = config.get(DIFFICULTY_CATEGORY, "condenserAmountMultiplier", 1.0,
"Multiplier for condenser upgrade mana regeneration amount. Higher values make condensers more effective.");
property.setLanguageKey("config." + Wizardry.MODID + ".condenser_amount_multiplier");
property.setRequiresWorldRestart(true);
condenserAmountMultiplier = property.getDouble();
propOrder.add(property.getName());
property = config.get(DIFFICULTY_CATEGORY, "fleshSpellsCauseSlowness", true,
"Whether flesh spells (DiamondFlesh, IronFlesh, OakFlesh) apply slowness. When disabled, these spells only provide their defensive benefits without movement penalty.");
property.setLanguageKey("config." + Wizardry.MODID + ".flesh_spells_cause_slowness");
Wizardry.proxy.setToNamedBooleanEntry(property);
property.requiresMcRestart();
fleshSpellsCauseSlowness = property.getBoolean();
propOrder.add(property.getName());
property = config.get(DIFFICULTY_CATEGORY, "diamondFleshArmorBonus", 4.0,
"Armor bonus provided by the DiamondFlesh spell.");
property.setLanguageKey("config." + Wizardry.MODID + ".diamond_flesh_armor_bonus");
property.setRequiresWorldRestart(true);
diamondFleshArmorBonus = property.getDouble();
propOrder.add(property.getName());
property = config.get(DIFFICULTY_CATEGORY, "diamondFleshArmorToughnessBonus", 3.0,
"Armor toughness bonus provided by the DiamondFlesh spell.");
property.setLanguageKey("config." + Wizardry.MODID + ".diamond_flesh_armor_toughness_bonus");
property.setRequiresWorldRestart(true);
diamondFleshArmorToughnessBonus = property.getDouble();
propOrder.add(property.getName());
property = config.get(DIFFICULTY_CATEGORY, "ironFleshArmorBonus", 4.0,
"Armor bonus provided by the IronFlesh spell.");
property.setLanguageKey("config." + Wizardry.MODID + ".iron_flesh_armor_bonus");
property.setRequiresWorldRestart(true);
ironFleshArmorBonus = property.getDouble();
propOrder.add(property.getName());
property = config.get(DIFFICULTY_CATEGORY, "oakFleshArmorBonus", 3.0,
"Armor bonus provided by the OakFlesh spell.");
property.setLanguageKey("config." + Wizardry.MODID + ".oak_flesh_armor_bonus");
property.setRequiresWorldRestart(true);
oakFleshArmorBonus = property.getDouble();
propOrder.add(property.getName());
property = config.get(DIFFICULTY_CATEGORY, "oakFleshHealthBonus", 0.2,
"Health bonus provided by the OakFlesh spell (as a multiplier, e.g., 0.2 = 20% increase).");
property.setLanguageKey("config." + Wizardry.MODID + ".oak_flesh_health_bonus");
property.setRequiresWorldRestart(true);
oakFleshHealthBonus = property.getDouble();
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.
@@ -800,6 +1107,13 @@ public final class Settings {
replaceVanillaFallDamage = property.getBoolean();
propOrder.add(property.getName());
property = config.get(TWEAKS_CATEGORY, "spellBookColorsRequireArchivistsEyeglass", false,
"If true, spell book colors are only shown when the player has the charm of spell discovery equipped.");
property.setLanguageKey("config." + Wizardry.MODID + ".spell_book_colors_require_charm");
Wizardry.proxy.setToNamedBooleanEntry(property);
spellBookColorsRequireArchivistsEyeglass = property.getBoolean();
propOrder.add(property.getName());
property = config.get(TWEAKS_CATEGORY, "blindnessTweak", true,
"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");
@@ -807,6 +1121,21 @@ public final class Settings {
blindnessTweak = property.getBoolean();
propOrder.add(property.getName());
property = config.get(TWEAKS_CATEGORY, "injectMobDrops", true,
"Whether to inject Wizardry loot to mobs, based on the loot table 'Mob Loot Table Whitelist' / 'Mob Loot Table Blacklist' settings. If disabled, the lists won't have any effect!");
property.setLanguageKey("config." + Wizardry.MODID + ".inject_mob_drops");
Wizardry.proxy.setToNamedBooleanEntry(property);
injectMobDrops = property.getBoolean();
propOrder.add(property.getName());
property = config.get(TWEAKS_CATEGORY, "recentSpellExpiryTime", 1200,
"The time in ticks after which recent spell casts expire and no longer count toward progression penalties. Default is 1200 ticks (1 minute). Lower values make progression penalties shorter-lived, higher values make them last longer.",
60, 72000); // Between 3 seconds and 1 hour
property.setLanguageKey("config." + Wizardry.MODID + ".recent_spell_expiry_time");
Wizardry.proxy.setToNumberSliderEntry(property);
recentSpellExpiryTime = property.getInt();
propOrder.add(property.getName());
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);
@@ -895,7 +1224,7 @@ public final class Settings {
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.",
property = config.get(TWEAKS_CATEGORY, "currencyItems", new String[]{"gold_ingot 3", "emerald 6"}, "List of registry names of items which wizard trades can use as currency (in the first slot; the second slot is unaffected). Each entry in this list should consist of an item registry name, followed by a single space, then an integer which defines the 'value' of the item. Higher values mean fewer of that currency item are required for a given trade. To specify metadata, use the format 'modid:item:meta value'. For example, 'minecraft:wool:1 5'. If no metadata is given, any metadata will be accepted.",
Pattern.compile("[A-Za-z0-9:_]+ [0-9]+"));
property.setLanguageKey("config." + Wizardry.MODID + ".currency_items");
propOrder.add(property.getName());
@@ -914,6 +1243,57 @@ public final class Settings {
}
}
property = config.get(TWEAKS_CATEGORY, "cooldown_reduction_per_level", 0.15,
"The fraction by which cooldowns are reduced for each level of cooldown upgrade.",
0.05, Integer.MAX_VALUE); // Sure, I mean you COULD set it to 2^31-1... what could possibly go wrong?
property.setLanguageKey("config." + Wizardry.MODID + ".cooldown_reduction_per_level");
cooldownReductionPerLevel = property.getDouble();
propOrder.add(property.getName());
property = config.get(TWEAKS_CATEGORY, "storage_increase_per_level", 0.15,
"The fraction by which maximum charge is increased for each level of storage upgrade.",
0.05, Integer.MAX_VALUE);
property.setLanguageKey("config." + Wizardry.MODID + ".storage_increase_per_level");
storageIncreasePerLevel = (float) property.getDouble();
propOrder.add(property.getName());
property = config.get(TWEAKS_CATEGORY, "potency_increase_per_tier", 0.15,
"The fraction by which potency is increased for each tier of matching wand. May cause extreme lag with high values!",
0.05, Integer.MAX_VALUE);
property.setLanguageKey("config." + Wizardry.MODID + ".potency_increase_per_tier");
potencyIncreasePerTier = property.getDouble();
propOrder.add(property.getName());
property = config.get(TWEAKS_CATEGORY, "duration_increase_per_level", 0.25,
"The fraction by which spell duration is increased for each level of duration upgrade.",
0.05, Integer.MAX_VALUE);
property.setLanguageKey("config." + Wizardry.MODID + ".duration_increase_per_level");
durationIncreasePerLevel = property.getDouble();
propOrder.add(property.getName());
property = config.get(TWEAKS_CATEGORY, "range_increase_per_level", 0.25,
"The fraction by which spell range is increased for each level of range upgrade. May cause extreme lag with high values!",
0.05, Integer.MAX_VALUE);
property.setLanguageKey("config." + Wizardry.MODID + ".range_increase_per_level");
rangeIncreasePerLevel = property.getDouble();
propOrder.add(property.getName());
property = config.get(TWEAKS_CATEGORY, "blast_increase_per_level", 0.25,
"The fraction by which spell blast is increased for each level of blast upgrade. May cause extreme lag with high values!",
0.05, Integer.MAX_VALUE);
property.setLanguageKey("config." + Wizardry.MODID + ".blast_increase_per_level");
blastIncreasePerLevel = property.getDouble();
propOrder.add(property.getName());
property = config.get(TWEAKS_CATEGORY, "frost_slowness_increase_per_level", 0.5,
"The fraction by which movement speed is reduced per level of frost effect.",
0.05, Integer.MAX_VALUE);
property.setLanguageKey("config." + Wizardry.MODID + ".frost_slowness_increase_per_level");
frostSlownessIncreasePerLevel = property.getDouble();
propOrder.add(property.getName());
checkForRedundantOptions(TWEAKS_CATEGORY, propOrder); // Must be before the order is set!
config.setCategoryPropertyOrder(TWEAKS_CATEGORY, propOrder);
@@ -1011,6 +1391,22 @@ public final class Settings {
shrineFiles = getResourceLocationList(property);
propOrder.add(property.getName());
property = config.get(WORLDGEN_CATEGORY, "shrineRegenerationEnabled", false, "Whether conquered shrines should regenerate after a period of time. When disabled, shrines remain conquered permanently.");
property.setLanguageKey("config." + Wizardry.MODID + ".shrine_regeneration_enabled");
shrineRegenerationEnabled = property.getBoolean();
propOrder.add(property.getName());
property = config.get(WORLDGEN_CATEGORY, "shrineRegenerationTime", 20, "Time in minutes for a conquered shrine to regenerate. Minimum 1 minute, maximum 1440 minutes (24 hours).", 1, 1440);
property.setLanguageKey("config." + Wizardry.MODID + ".shrine_regeneration_time");
Wizardry.proxy.setToNumberSliderEntry(property);
shrineRegenerationTime = property.getInt();
propOrder.add(property.getName());
property = config.get(WORLDGEN_CATEGORY, "shrineAllowMultipleLoot", false, "Whether players can loot shrines multiple times. If false, each player can only loot each shrine once until it regenerates.");
property.setLanguageKey("config." + Wizardry.MODID + ".shrine_allow_multiple_loot");
shrineAllowMultipleLoot = property.getBoolean();
propOrder.add(property.getName());
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);
@@ -1103,6 +1499,27 @@ public final class Settings {
showChargeMeter = property.getBoolean();
propOrder.add(property.getName());
property = config.get(CLIENT_CATEGORY, "spellBookColors", true, "Whether to show elemental colors on spell books for discovered spells.");
property.setLanguageKey("config." + Wizardry.MODID + ".spell_book_colors");
property.setRequiresWorldRestart(false);
Wizardry.proxy.setToNamedBooleanEntry(property);
spellBookColors = property.getBoolean();
propOrder.add(property.getName());
property = config.get(CLIENT_CATEGORY, "loadHandbook", true, "Whether to initialise the in-game handbook. Setting this to false will brick the in-game handbook, but it might help if you have startup crashes.");
property.setLanguageKey("config." + Wizardry.MODID + ".load_handbook");
property.setRequiresWorldRestart(false);
Wizardry.proxy.setToNamedBooleanEntry(property);
loadHandbook = property.getBoolean();
propOrder.add(property.getName());
property = config.get(CLIENT_CATEGORY, "unfocusedSearchBars", false, "Whether to allow the Arcane Workbench and lectern search field to lose focus and start unfocused. If true, the search field won't automatically capture keyboard input.");
property.setLanguageKey("config." + Wizardry.MODID + ".unfocused_search_bars");
property.setRequiresWorldRestart(false);
Wizardry.proxy.setToNamedBooleanEntry(property);
unfocusedSearchBars = 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());
@@ -1263,6 +1680,18 @@ public final class Settings {
}
propOrder.add(property.getName());
property = config.get(RESISTANCES_CATEGORY, "mobsImmuneToMagic", new String[]{},
"List of names of entities that are immune to magic, in addition to the defaults. Add mod creatures to this list if you want them to be immune to magic damage and they aren't already. SoundLoopSpellEntity names are not case sensitive. For mod entities, prefix with the mod ID (e.g. " + Wizardry.MODID + ":wizard).");
property.setLanguageKey("config." + Wizardry.MODID + ".mobs_immune_to_magic");
property.setRequiresMcRestart(true);
// Wizardry.proxy.setToEntityNameEntry(property);
// Converts all strings in the list to lower case, to ignore case sensitivity, and trims them.
for(int i = 0; i < property.getStringList().length; i++){
property.getStringList()[i] = property.getStringList()[i].toLowerCase(Locale.ROOT).trim();
MagicDamage.addEntityImmunity(EntityList.getClass(new ResourceLocation(property.getStringList()[i])),
DamageType.MAGIC);
}
propOrder.add(property.getName());
property = config.get(RESISTANCES_CATEGORY, "mobsImmuneToWither", new String[]{},
"List of names of entities that are immune to wither effects, in addition to the defaults. Add mod creatures to this list if you want them to be immune to withering magic and they aren't already. SoundLoopSpellEntity names are not case sensitive. For mod entities, prefix with the mod ID (e.g. " + Wizardry.MODID + ":wizard).");
property.setLanguageKey("config." + Wizardry.MODID + ".mobs_immune_to_wither");
@@ -1407,16 +1836,46 @@ public final class Settings {
string = string.toLowerCase(Locale.ROOT).trim();
String[] itemArgs = string.split(":");
String item;
short meta;
try {
meta = Short.parseShort(itemArgs[itemArgs.length-1]);
item = String.join(":", Arrays.copyOfRange(itemArgs, 0, itemArgs.length-1));
}catch(NumberFormatException e){ // If no metadata is specified
meta = OreDictionary.WILDCARD_VALUE;
int lastColon = string.lastIndexOf(':');
if (lastColon > -1 && lastColon < string.length() - 1) { // if there is a colon and it's not the last char
String metaString = string.substring(lastColon + 1);
try {
short parsedMeta = Short.parseShort(metaString);
String itemString = string.substring(0, lastColon);
// If itemString has no colon, it could be 'item:meta' or 'namespace:numeric_path'.
// e.g., 'wool:1' vs 'mod:123'. We want to support 'wool:1' as item 'wool' with meta 1.
// We can't reliably distinguish the two cases without knowing all namespaces, so we have to use
// a heuristic. The original code favoured the 'item:meta' interpretation, let's stick with that.
// The issue was with 'namespace:numeric_path' which this logic now handles.
if (itemString.indexOf(':') == -1) {
item = itemString;
meta = parsedMeta;
} else {
// Disambiguate things like 'mod:123:4' (item 'mod:123' with meta 4) vs 'mod:item:123' (item 'mod:item' with meta 123)
// If the bit before the last colon is a valid resource location, it's probably item:meta
try {
new ResourceLocation(itemString);
item = itemString;
meta = parsedMeta;
} catch (Exception e) {
item = string;
meta = OreDictionary.WILDCARD_VALUE;
}
}
} catch (NumberFormatException e) {
// The part after colon is not a number, so it's part of the name
item = string;
meta = OreDictionary.WILDCARD_VALUE;
}
} else {
item = string;
meta = OreDictionary.WILDCARD_VALUE;
}
return Pair.of(new ResourceLocation(item), meta);
@@ -1,8 +1,5 @@
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;
@@ -16,11 +13,23 @@ 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.registry.Spells;
import electroblob.wizardry.registry.WizardryAdvancementTriggers;
import electroblob.wizardry.registry.WizardryBlocks;
import electroblob.wizardry.registry.WizardryItems;
import electroblob.wizardry.registry.WizardryLoot;
import electroblob.wizardry.registry.WizardrySounds;
import electroblob.wizardry.spell.Spell;
import electroblob.wizardry.util.SpellNetworkIDSorter;
import electroblob.wizardry.util.SpellProperties;
import electroblob.wizardry.util.WandHelper;
import electroblob.wizardry.worldgen.*;
import electroblob.wizardry.worldgen.WorldGenCrystalFlower;
import electroblob.wizardry.worldgen.WorldGenCrystalOre;
import electroblob.wizardry.worldgen.WorldGenLibraryRuins;
import electroblob.wizardry.worldgen.WorldGenObelisk;
import electroblob.wizardry.worldgen.WorldGenShrine;
import electroblob.wizardry.worldgen.WorldGenUndergroundLibraryRuins;
import electroblob.wizardry.worldgen.WorldGenWizardTower;
import net.minecraft.item.Item;
import net.minecraft.util.SoundCategory;
import net.minecraftforge.common.MinecraftForge;
@@ -39,8 +48,6 @@ 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;
/**
@@ -56,6 +63,7 @@ import java.util.Calendar;
@Mod(modid = Wizardry.MODID, name = Wizardry.NAME, version = Wizardry.VERSION, acceptedMinecraftVersions = "[1.12.2]",
guiFactory = "electroblob.wizardry.WizardryGuiFactory",
updateJSON = Wizardry.updateJSON,
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 {
@@ -74,7 +82,13 @@ 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.3.4";
public static final String VERSION = "4.3.16";
/**
* Json file used by Forge's built-in <a href="https://mcforge.readthedocs.io/en/1.12.x/gettingstarted/autoupdate/">update checker</a>.
* Used by the in-game mod menu to show that green icon whenever a newer version is available of the mod.
*/
public static final String updateJSON = "https://github.com/Electroblob77/Wizardry/blob/1.12.2/.forge/update.json";
// IDEA: Triggering of inbuilt Forge events in relevant places?
// IDEA: Abstract the vanilla particles behind the particle builder
@@ -113,8 +127,6 @@ public class Wizardry {
public void preInit(FMLPreInitializationEvent event){
logger = event.getModLog();
proxy.registerResourceReloadListeners();
configDirectory = new File(event.getModConfigurationDirectory(), Wizardry.MODID);
settings.initConfig(event);
@@ -148,9 +160,13 @@ public class Wizardry {
@EventHandler
public void init(FMLInitializationEvent event){
proxy.registerResourceReloadListeners();
settings.initConfigExtras();
// Update constants with configured values
settings.updateConstantsFromSettings();
// World generators
// Weight is a misnomer, it's actually the priority (where lower numbers get generated first)
// Literally nothing on typical 'weight' values here, there isn't even an upper limit
@@ -182,6 +198,8 @@ public class Wizardry {
// Client-side stuff (via proxies)
proxy.initGuiBits();
proxy.registerParticles();
proxy.registerItemColorHandlers();
proxy.registerModelProperties();
proxy.registerSoundEventListener();
}
@@ -189,6 +207,8 @@ public class Wizardry {
public void postInit(FMLPostInitializationEvent event){
proxy.initialiseLayers();
proxy.initialiseAnimations();
SpellNetworkIDSorter.init();
}
@EventHandler
@@ -203,7 +203,6 @@ public final class WizardryEventHandler {
// 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);
@@ -269,7 +268,7 @@ public final class WizardryEventHandler {
// Fireskin
if(event.getEntityLiving().isPotionActive(WizardryPotions.fireskin)
&& !MagicDamage.isEntityImmune(DamageType.FIRE, event.getEntityLiving()))
attacker.setFire(Spells.fire_breath.getProperty(Spell.BURN_DURATION).intValue());
attacker.setFire(Spells.fire_breath.getProperty(Spell.BURN_DURATION).intValue() * 20);
// Ice Shroud
if(event.getEntityLiving().isPotionActive(WizardryPotions.ice_shroud)
@@ -307,7 +306,7 @@ public final class WizardryEventHandler {
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%
event.setAmount(event.getAmount() * Math.max(0, 1 - 0.2f * (1 + effect.getAmplifier()))); // Resistance is 20%
}
// Flaming and freezing swords
@@ -491,4 +490,4 @@ public final class WizardryEventHandler {
}
}
}
}
@@ -1,5 +1,6 @@
package electroblob.wizardry.block;
import electroblob.wizardry.Wizardry;
import electroblob.wizardry.constants.Element;
import electroblob.wizardry.registry.WizardryTabs;
import electroblob.wizardry.tileentity.TileEntityShrineCore;
@@ -81,11 +82,25 @@ public class BlockPedestal extends Block implements ITileEntityProvider {
@Override
public float getBlockHardness(IBlockState state, World world, BlockPos pos){
// If shrine regeneration is enabled, make pedestal blocks with shrine cores unbreakable to prevent exploitation
if(!world.isRemote && Wizardry.settings != null && Wizardry.settings.shrineRegenerationEnabled){
TileEntity tileEntity = world.getTileEntity(pos);
if(tileEntity instanceof TileEntityShrineCore){
return -1; // Unbreakable if it has a shrine core
}
}
return state.getValue(NATURAL) ? -1 : super.getBlockHardness(state, world, pos);
}
@Override
public float getExplosionResistance(World world, BlockPos pos, @Nullable Entity exploder, Explosion explosion){
// If shrine regeneration is enabled, make pedestal blocks with shrine cores unbreakable to prevent exploitation
if(!world.isRemote && Wizardry.settings != null && Wizardry.settings.shrineRegenerationEnabled){
TileEntity tileEntity = world.getTileEntity(pos);
if(tileEntity instanceof TileEntityShrineCore){
return 6000000.0F; // Unbreakable if it has a shrine core
}
}
return world.getBlockState(pos).getValue(NATURAL) ? 6000000.0F : super.getExplosionResistance(world, pos, exploder, explosion);
}
@@ -191,7 +191,7 @@ public class BlockStatue extends Block implements ITileEntityProvider {
// Making this an instance method means it works equally well for both types of statue
public boolean convertToStatue(EntityLiving target, @Nullable EntityLivingBase caster, int duration){
if(target.deathTime > 0) return false;
if(target.deathTime > 0 || target.world.isRemote) return false;
BlockPos pos = new BlockPos(target);
World world = target.world;
@@ -18,7 +18,10 @@ 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.constants.Element;
import electroblob.wizardry.registry.WizardryItems;
import electroblob.wizardry.client.particle.ParticleWizardry.IWizardryParticleFactory;
import electroblob.wizardry.client.renderer.RenderSpectralGolem;
import electroblob.wizardry.client.renderer.entity.*;
import electroblob.wizardry.client.renderer.entity.layers.*;
import electroblob.wizardry.client.renderer.overlay.RenderBlinkEffect;
@@ -36,6 +39,7 @@ import electroblob.wizardry.entity.projectile.*;
import electroblob.wizardry.event.SpellCastEvent;
import electroblob.wizardry.event.SpellCastEvent.Source;
import electroblob.wizardry.integration.antiqueatlas.WizardryAntiqueAtlasIntegration;
import electroblob.wizardry.item.ItemArtefact;
import electroblob.wizardry.item.ItemScroll;
import electroblob.wizardry.item.ItemSpellBook;
import electroblob.wizardry.item.ItemWand;
@@ -164,7 +168,8 @@ public class ClientProxy extends CommonProxy {
IResourceManager manager = Minecraft.getMinecraft().getResourceManager();
if(manager instanceof IReloadableResourceManager){
((IReloadableResourceManager)manager).registerReloadListener(GuiSpellDisplay::loadSkins);
((IReloadableResourceManager)manager).registerReloadListener(GuiWizardHandbook::loadHandbookFile);
if (Wizardry.settings.loadHandbook)
((IReloadableResourceManager)manager).registerReloadListener(GuiWizardHandbook::loadHandbookFile);
}
}
@@ -712,6 +717,65 @@ public class ClientProxy extends CommonProxy {
}else Wizardry.logger.warn("Received a PacketConquerShrine, but there was no shrine core at the position sent");
}
@Override
public void handleBombExplosionPacket(PacketBombExplosion.Message message){
net.minecraft.world.World world = Minecraft.getMinecraft().world;
double x = message.x, y = message.y, z = message.z;
float blastMultiplier = message.blastMultiplier;
switch(message.bombType){
case PacketBombExplosion.FIREBOMB:
ParticleBuilder.create(Type.FLASH).pos(x, y, z).scale(5 * blastMultiplier).clr(1, 0.6f, 0).spawn(world);
for(int i = 0; i < 60 * blastMultiplier; i++){
ParticleBuilder.create(Type.MAGIC_FIRE, world.rand, x, y, z, 2 * blastMultiplier, false)
.time(10 + world.rand.nextInt(4)).scale(2 + world.rand.nextFloat()).spawn(world);
ParticleBuilder.create(Type.DARK_MAGIC, world.rand, x, y, z, 2 * blastMultiplier, false)
.clr(1.0f, 0.2f + world.rand.nextFloat() * 0.4f, 0.0f).spawn(world);
}
world.spawnParticle(EnumParticleTypes.EXPLOSION_LARGE, x, y, z, 0, 0, 0);
break;
case PacketBombExplosion.POISON_BOMB:
ParticleBuilder.create(Type.FLASH).pos(x, y, z).scale(5 * blastMultiplier)
.clr(0.2f + world.rand.nextFloat() * 0.3f, 0.6f, 0.0f).spawn(world);
for(int i = 0; i < 60 * blastMultiplier; i++){
ParticleBuilder.create(Type.SPARKLE, world.rand, x, y, z, 2 * blastMultiplier, false).time(35)
.scale(2).clr(0.2f + world.rand.nextFloat() * 0.3f, 0.6f, 0.0f).spawn(world);
ParticleBuilder.create(Type.DARK_MAGIC, world.rand, x, y, z, 2 * blastMultiplier, false)
.clr(0.2f + world.rand.nextFloat() * 0.2f, 0.8f, 0.0f).spawn(world);
}
world.spawnParticle(EnumParticleTypes.EXPLOSION_LARGE, x, y, z, 0, 0, 0);
break;
case PacketBombExplosion.SMOKE_BOMB:
ParticleBuilder.create(Type.FLASH).pos(x, y, z).scale(5 * blastMultiplier).clr(0, 0, 0).spawn(world);
world.spawnParticle(EnumParticleTypes.EXPLOSION_LARGE, x, y, z, 0, 0, 0);
for(int i = 0; i < 60 * blastMultiplier; i++){
float brightness = world.rand.nextFloat() * 0.1f + 0.1f;
ParticleBuilder.create(Type.CLOUD, world.rand, x, y, z, 2 * blastMultiplier, false)
.clr(brightness, brightness, brightness).time(80 + world.rand.nextInt(12)).shaded(true).spawn(world);
brightness = world.rand.nextFloat() * 0.3f;
ParticleBuilder.create(Type.DARK_MAGIC, world.rand, x, y, z, 2 * blastMultiplier, false)
.clr(brightness, brightness, brightness).spawn(world);
}
break;
case PacketBombExplosion.SPARK_BOMB:
ParticleBuilder.spawnShockParticles(world, x, y, z);
for(int id : message.secondaryTargetIDs){
net.minecraft.entity.Entity target = world.getEntityByID(id);
if(target instanceof net.minecraft.entity.EntityLivingBase){
ParticleBuilder.create(Type.LIGHTNING).pos(x, y, z).target(target).spawn(world);
ParticleBuilder.spawnShockParticles(world, target.posX,
target.posY + target.height / 2, target.posZ);
}
}
break;
}
}
// Rendering
// ===============================================================================================================
@@ -728,6 +792,9 @@ public class ClientProxy extends CommonProxy {
LayerTiledOverlay.initialiseLayers(LayerMindControl::new);
LayerTiledOverlay.initialiseLayers(LayerSummonAnimation::new);
LayerTiledOverlay.initialiseLayers(LayerDisintegrateAnimation::new);
LayerTiledOverlay.initialiseLayers(LayerOak::new);
LayerTiledOverlay.initialiseLayers(LayerIron::new);
LayerTiledOverlay.initialiseLayers(LayerDiamond::new);
}
@Override
@@ -766,6 +833,7 @@ public class ClientProxy extends CommonProxy {
RenderingRegistry.registerEntityRenderingHandler(EntityIceGiant.class, RenderIceGiant::new);
RenderingRegistry.registerEntityRenderingHandler(EntityPhoenix.class, RenderPhoenix::new);
RenderingRegistry.registerEntityRenderingHandler(EntitySpectralGolem.class, RenderSpectralGolem::new);
// Projectiles
RenderingRegistry.registerEntityRenderingHandler(EntityMagicMissile.class, manager -> new RenderMagicArrow(manager,
@@ -780,6 +848,7 @@ public class ClientProxy extends CommonProxy {
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(EntityConjuredArrow.class, RenderConjuredArrow::new);
RenderingRegistry.registerEntityRenderingHandler(EntityForceArrow.class, RenderForceArrow::new);
@@ -869,4 +938,51 @@ public class ClientProxy extends CommonProxy {
ClientRegistry.bindTileEntitySpecialRenderer(TileEntityImbuementAltar.class, new RenderImbuementAltar());
}
public void registerItemColorHandlers() {
// Spell book overlay textures are now per-element, so no runtime tinting is needed.
}
public void registerModelProperties() {
Item spellBook = WizardryItems.spell_book;
spellBook.addPropertyOverride(new ResourceLocation(Wizardry.MODID, "discovered"), (stack, world, entity) -> {
if (!Wizardry.settings.spellBookColors) return 0.0f;
boolean discovered = shouldDisplayDiscovered(Spell.byMetadata(stack.getMetadata()), stack);
if(discovered && Wizardry.settings.spellBookColorsRequireArchivistsEyeglass){
// Entity can be null so we have to check
if(entity instanceof EntityPlayer){
return ItemArtefact.isArtefactActive((EntityPlayer)entity, WizardryItems.charm_spell_discovery) ? 1.0f : 0.0f;
}
// If there's no entity, there's no charm, so no colour
return 0.0f;
}
return discovered ? 1.0f : 0.0f;
});
// Returns ordinal+1 for each element (1=MAGIC through 8=HEALING), or 0 if not yet discovered.
// Drives per-element bookmark texture selection via model overrides in spell_book.json.
spellBook.addPropertyOverride(new ResourceLocation(Wizardry.MODID, "element"), (stack, world, entity) -> {
if (!Wizardry.settings.spellBookColors) return 0.0f;
Spell spell = Spell.byMetadata(stack.getMetadata());
boolean discovered = shouldDisplayDiscovered(spell, stack);
if(!discovered) return 0.0f;
if(Wizardry.settings.spellBookColorsRequireArchivistsEyeglass){
if(entity instanceof EntityPlayer){
if(!ItemArtefact.isArtefactActive((EntityPlayer)entity, WizardryItems.charm_spell_discovery)) return 0.0f;
} else {
return 0.0f;
}
}
Element element = spell.getElement();
return element == null ? 0.0f : (float)(element.ordinal() + 1);
});
}
}
@@ -1,12 +1,11 @@
package electroblob.wizardry.client;
import electroblob.wizardry.Wizardry;
import electroblob.wizardry.client.renderer.overlay.RenderBlinkEffect;
import electroblob.wizardry.data.DispenserCastingData;
import electroblob.wizardry.data.SpellEmitterData;
import electroblob.wizardry.item.ItemArtefact;
import electroblob.wizardry.item.ItemFlamecatcher;
import electroblob.wizardry.item.ItemSpectralBow;
import electroblob.wizardry.item.ItemWand;
import electroblob.wizardry.data.WizardData;
import electroblob.wizardry.item.*;
import electroblob.wizardry.potion.PotionSlowTime;
import electroblob.wizardry.registry.WizardryItems;
import electroblob.wizardry.registry.WizardryPotions;
@@ -16,6 +15,7 @@ import electroblob.wizardry.spell.SlowTime;
import electroblob.wizardry.spell.Transience;
import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.ScaledResolution;
import net.minecraft.client.gui.inventory.GuiContainer;
import net.minecraft.client.renderer.BufferBuilder;
import net.minecraft.client.renderer.GlStateManager;
import net.minecraft.client.renderer.Tessellator;
@@ -26,14 +26,14 @@ import net.minecraft.tileentity.TileEntity;
import net.minecraft.tileentity.TileEntityDispenser;
import net.minecraft.util.ResourceLocation;
import net.minecraft.world.World;
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.client.event.*;
import net.minecraftforge.fml.common.Mod;
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
import net.minecraftforge.fml.common.gameevent.TickEvent;
import net.minecraftforge.fml.relauncher.Side;
import net.minecraft.client.resources.I18n;
import net.minecraft.util.text.TextFormatting;
import net.minecraftforge.event.entity.player.ItemTooltipEvent;
import org.lwjgl.opengl.GL11;
import java.util.ArrayList;
@@ -120,6 +120,7 @@ public final class WizardryClientEventHandler {
Minecraft.getMinecraft().player.prevRotationPitch = 0;
Minecraft.getMinecraft().player.rotationYaw = 0;
Minecraft.getMinecraft().player.rotationPitch = 0;
}
}
@@ -133,10 +134,10 @@ public final class WizardryClientEventHandler {
event.getMovementInput().jump = false;
event.getMovementInput().sneak = false;
}
if(ItemArtefact.isArtefactActive(event.getEntityPlayer(), WizardryItems.charm_move_speed)
&& event.getEntityPlayer().isHandActive()
&& event.getEntityPlayer().getActiveItemStack().getItem() instanceof ItemWand){
&& event.getEntityPlayer().getActiveItemStack().getItem() instanceof ISpellCastingItem){
// Normally speed is set to 20% when using items, this makes it 80%
event.getMovementInput().moveStrafe *= 4;
event.getMovementInput().moveForward *= 4;
@@ -187,6 +188,26 @@ public final class WizardryClientEventHandler {
}
}
@SubscribeEvent
public static void onGuiOpenEvent(GuiOpenEvent event){
if(Minecraft.getMinecraft().player != null && event.getGui() instanceof GuiContainer) {
WizardData data = WizardData.get(Minecraft.getMinecraft().player);
if (data != null && data.getVariable(Possession.POSSESSEE_KEY) != null) {
event.setCanceled(true);
}
}
}
@SubscribeEvent
public static void onItemTooltip(ItemTooltipEvent event) {
if (event.getItemStack().getItem() == WizardryItems.charm_spell_discovery) {
if (Wizardry.settings.spellBookColorsRequireArchivistsEyeglass) {
event.getToolTip().add(TextFormatting.GRAY + I18n.format("item.ebwizardry:charm_spell_discovery.desc.color"));
}
}
}
/**
* Renders an overlay across the entire screen.
* @param resolution The screen resolution
@@ -93,6 +93,7 @@ public class GuiArcaneWorkbench extends GuiContainer {
private ContainerArcaneWorkbench arcaneWorkbenchContainer;
private GuiButton applyBtn;
private GuiButton clearBtn;
private GuiButton[] sortButtons = new GuiButton[3];
private GuiTextField searchField;
@@ -126,6 +127,7 @@ public class GuiArcaneWorkbench extends GuiContainer {
this.buttonList.clear();
this.buttonList.add(this.applyBtn = new GuiButtonApply(0, this.width / 2 + 64, this.height / 2 + 3));
this.buttonList.add(this.clearBtn = new GuiButtonClear(0, this.width / 2 + 64, this.height / 2 - 16));
this.buttonList.add(sortButtons[0] = new GuiButtonSpellSort(1, this.guiLeft - 44, this.guiTop + 8, ISpellSortable.SortType.TIER, arcaneWorkbenchContainer, this));
this.buttonList.add(sortButtons[1] = new GuiButtonSpellSort(2, this.guiLeft - 31, this.guiTop + 8, ISpellSortable.SortType.ELEMENT, arcaneWorkbenchContainer, this));
this.buttonList.add(sortButtons[2] = new GuiButtonSpellSort(3, this.guiLeft - 18, this.guiTop + 8, ISpellSortable.SortType.ALPHABETICAL, arcaneWorkbenchContainer, this));
@@ -135,8 +137,8 @@ public class GuiArcaneWorkbench extends GuiContainer {
this.searchField.setEnableBackgroundDrawing(false);
this.searchField.setVisible(true);
this.searchField.setTextColor(16777215);
this.searchField.setCanLoseFocus(false);
this.searchField.setFocused(true);
this.searchField.setCanLoseFocus(Wizardry.settings.unfocusedSearchBars); // false by default
this.searchField.setFocused(!Wizardry.settings.unfocusedSearchBars); // true by default
this.tooltipElements.clear();
this.tooltipElements.add(new TooltipElementItemName(new Style().setColor(TextFormatting.WHITE), LINE_SPACING_WIDE));
@@ -205,6 +207,7 @@ public class GuiArcaneWorkbench extends GuiContainer {
// Show/hide the relevant gui elements
this.applyBtn.enabled = centreSlot.getHasStack();
this.clearBtn.enabled = centreSlot.getHasStack() && centreSlot.getStack().getItem() instanceof IWorkbenchItem && ((IWorkbenchItem) centreSlot.getStack().getItem()).isClearable();
for(GuiButton button : this.sortButtons) button.visible = arcaneWorkbenchContainer.hasBookshelves();
this.searchField.setVisible(arcaneWorkbenchContainer.hasBookshelves());
@@ -428,6 +431,16 @@ public class GuiArcaneWorkbench extends GuiContainer {
// Controls
@Override
protected void mouseClicked(int mouseX, int mouseY, int mouseButton) throws IOException {
super.mouseClicked(mouseX, mouseY, mouseButton);
if (this.searchField != null) {
this.searchField.mouseClicked(mouseX, mouseY, mouseButton);
// Set focus depending on whether the click was inside the search field
this.searchField.setFocused(this.searchField.isFocused());
}
}
@Override
protected void actionPerformed(GuiButton button){
@@ -444,6 +457,17 @@ public class GuiArcaneWorkbench extends GuiContainer {
animationTimer = 20;
}
if(button == clearBtn){
// Packet building
IMessage msg = new PacketControlInput.Message(PacketControlInput.ControlType.CLEAR_BUTTON);
WizardryPacketHandler.net.sendToServer(msg);
// Sound
Minecraft.getMinecraft().getSoundHandler().playSound(PositionedSoundRecord.getMasterRecord(
WizardrySounds.BLOCK_ARCANE_WORKBENCH_SPELLBIND, 0.8f));
// Animation
animationTimer = 20;
}
if(button instanceof GuiButtonSpellSort) this.arcaneWorkbenchContainer.setSortType(((GuiButtonSpellSort)button).sortType);
}
}
@@ -491,12 +515,13 @@ public class GuiArcaneWorkbench extends GuiContainer {
@Override
protected void keyTyped(char typedChar, int keyCode) throws IOException {
if(this.searchNeedsClearing){
this.searchNeedsClearing = false;
if(this.searchNeedsClearing){
this.searchNeedsClearing = false;
this.searchField.setText("");
}
if(this.searchField.textboxKeyTyped(typedChar, keyCode)){
// Allow exiting the GUI by pressing the inventory button. Does not work if bookshelves are present by default
if(this.searchField.getVisible() && this.searchField.textboxKeyTyped(typedChar, keyCode)){
arcaneWorkbenchContainer.setSearchText(searchField.getText().toLowerCase(Locale.ROOT));
}else{
super.keyTyped(typedChar, keyCode);
@@ -1006,4 +1031,36 @@ public class GuiArcaneWorkbench extends GuiContainer {
}
}
}
private static class GuiButtonClear extends GuiButton {
public GuiButtonClear(int id, int x, int y){
super(id, x, y, 16, 16, I18n.format("container." + Wizardry.MODID + ":arcane_workbench.clear"));
}
@Override
public void drawButton(Minecraft minecraft, int mouseX, int mouseY, float partialTicks){
// 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 = 72;
int l = 236;
//int colour = 14737632;
if(this.enabled){
if(this.hovered){
k += this.width * 2;
//colour = 16777120;
}
}else{
k += this.width;
//colour = 10526880;
}
DrawingUtils.drawTexturedRect(this.x, this.y, k, l, this.width, this.height, TEXTURE_WIDTH, TEXTURE_HEIGHT);
//this.drawCenteredString(minecraft.fontRenderer, this.displayString, this.x + this.width / 2,
// this.y + (this.height - 8) / 2, colour);
}
}
}
@@ -184,9 +184,8 @@ public class GuiLectern extends GuiSpellInfo implements ISpellSortable {
this.searchField.setEnableBackgroundDrawing(false);
this.searchField.setVisible(true);
this.searchField.setTextColor(16777215);
this.searchField.setCanLoseFocus(false);
this.searchField.setFocused(true);
this.searchField.setCanLoseFocus(Wizardry.settings.unfocusedSearchBars); // false by default
this.searchField.setFocused(!Wizardry.settings.unfocusedSearchBars); // true by default
refreshAvailableSpells(); // Must be done last
}
@@ -269,8 +268,13 @@ public class GuiLectern extends GuiSpellInfo implements ISpellSortable {
@Override
protected void mouseClicked(int mouseX, int mouseY, int mouseButton) throws IOException {
super.mouseClicked(mouseX, mouseY, mouseButton);
searchNeedsClearing = true;
super.mouseClicked(mouseX, mouseY, mouseButton);
if (this.searchField != null) {
this.searchField.mouseClicked(mouseX, mouseY, mouseButton);
// Set focus depending on whether the click was inside the search field
this.searchField.setFocused(this.searchField.isFocused());
}
searchNeedsClearing = true;
}
@Override
@@ -151,7 +151,7 @@ class Contents {
int x = GuiWizardHandbook.isRightPage(startPage) ? left + GuiWizardHandbook.GUI_WIDTH - GuiWizardHandbook.TEXT_INSET_X - GuiWizardHandbook.PAGE_WIDTH : left + GuiWizardHandbook.TEXT_INSET_X;
int y = top + GuiWizardHandbook.TEXT_INSET_Y + startLine * font.FONT_HEIGHT;
list.add(new GuiButtonHyperlink.Internal(0, x, y, font, entry.title, entry, 0, "", maxLineNumber-startLine, GuiWizardHandbook.isRightPage(startPage)));
list.add(new GuiButtonHyperlink.Internal(0, x, y, font, entry.title, entry, 0, "", maxLineNumber-startLine, GuiWizardHandbook.isRightPage(startPage), false));
startLine++;
@@ -28,15 +28,17 @@ public abstract class GuiButtonHyperlink extends GuiButton {
final List<String> lines;
final int linesLeft;
GuiButtonHyperlink(int id, int x, int y, FontRenderer font, String text, int indent, String suffix, int linesLeft, boolean rightPage){
GuiButtonHyperlink(int id, int x, int y, FontRenderer font, String text, int indent, String suffix, int linesLeft, boolean rightPage, boolean spaceless){
super(id, x, y, font.getStringWidth(text), font.FONT_HEIGHT, text);
// Sometimes a link has punctuation or something after it that causes it to wrap onto a new line
String linkWithSuffix = text + suffix;
// If the string won't fit any words at the end of the current line, treat it as if we started a new line
if(font.getStringWidth(linkWithSuffix.split("\\s")[0]) > GuiWizardHandbook.PAGE_WIDTH - indent){
// If the string won't fit any words (or characters in a spaceless language) at the end of the current line,
// treat it as if we started a new line
String firstWord = spaceless ? linkWithSuffix.substring(0, 1) : linkWithSuffix.split("\\s")[0];
if(font.getStringWidth(firstWord) > GuiWizardHandbook.PAGE_WIDTH - indent){
indent = 0;
this.y += font.FONT_HEIGHT;
}
@@ -149,7 +151,7 @@ public abstract class GuiButtonHyperlink extends GuiButton {
* @throws IllegalArgumentException if the given argument array is empty or contains more than 2 arguments
* @throws JsonSyntaxException if the specified link target is not a URL or a valid section ID
*/
public static GuiButtonHyperlink create(int x, int y, FontRenderer font, List<String> upToLink, String[] arguments, String suffix, int linesLeft, boolean rightPage){
public static GuiButtonHyperlink create(int x, int y, FontRenderer font, List<String> upToLink, String[] arguments, String suffix, int linesLeft, boolean rightPage, boolean spaceless){
if(arguments.length == 0 || arguments.length > 2) throw new IllegalArgumentException("Incorrect array length!");
@@ -158,7 +160,7 @@ public abstract class GuiButtonHyperlink extends GuiButton {
if(arguments[0].matches(URL_REGEX)){
button = new GuiButtonHyperlink.External(0, x, y, font, arguments[arguments.length - 1], arguments[0],
font.getStringWidth(upToLink.get(upToLink.size() - 1)), suffix, linesLeft, rightPage);
font.getStringWidth(upToLink.get(upToLink.size() - 1)), suffix, linesLeft, rightPage, spaceless);
}else{
@@ -167,7 +169,7 @@ public abstract class GuiButtonHyperlink extends GuiButton {
if(target == null) throw new JsonSyntaxException("Hyperlink points to nonexistent section id " + arguments[0]);
button = new GuiButtonHyperlink.Internal(0, x, y, font, arguments[arguments.length - 1],
target, font.getStringWidth(upToLink.get(upToLink.size() - 1)), suffix, linesLeft, rightPage);
target, font.getStringWidth(upToLink.get(upToLink.size() - 1)), suffix, linesLeft, rightPage, spaceless);
}
return button;
@@ -177,8 +179,8 @@ public abstract class GuiButtonHyperlink extends GuiButton {
final Section target;
Internal(int id, int x, int y, FontRenderer font, String text, Section target, int indent, String suffix, int linesLeft, boolean rightPage){
super(id, x, y, font, text, indent, suffix, linesLeft, rightPage);
Internal(int id, int x, int y, FontRenderer font, String text, Section target, int indent, String suffix, int linesLeft, boolean rightPage, boolean spaceless){
super(id, x, y, font, text, indent, suffix, linesLeft, rightPage, spaceless);
this.target = target;
}
@@ -216,8 +218,8 @@ public abstract class GuiButtonHyperlink extends GuiButton {
final ITextComponent link;
External(int id, int x, int y, FontRenderer font, String text, String url, int indent, String suffix, int linesLeft, boolean rightPage){
super(id, x, y, font, text, indent, suffix, linesLeft, rightPage);
External(int id, int x, int y, FontRenderer font, String text, String url, int indent, String suffix, int linesLeft, boolean rightPage, boolean spaceless){
super(id, x, y, font, text, indent, suffix, linesLeft, rightPage, spaceless);
this.link = new TextComponentString(text);
link.getStyle().setClickEvent(new ClickEvent(ClickEvent.Action.OPEN_URL, url)).setColor(TextFormatting.DARK_BLUE);
}
@@ -33,9 +33,13 @@ import java.awt.*;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.List;
import java.util.*;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
/**
* GUI class for the wizard's handbook. Like any GUI class, this is instantiated each time the book is opened. As of
@@ -55,6 +59,8 @@ public class GuiWizardHandbook extends GuiScreen {
private static final ResourceLocation DEFAULT = new ResourceLocation(Wizardry.MODID, "texts/handbook_en_us.json");
private static List<String> ADDONS = new ArrayList<>();
static final ResourceLocation texture = new ResourceLocation(Wizardry.MODID, "textures/gui/handbook/handbook.png");
/** Global Gson instance for the handbook. */
@@ -406,9 +412,10 @@ public class GuiWizardHandbook extends GuiScreen {
return;
}
IResource handbookFile = getHandbookResource(manager);
// IResource handbookFile = getHandbookResource(manager);
List<IResource> handbookFiles = getHandbookResource(manager);
if(handbookFile != null){
if(!handbookFiles.isEmpty()){
// Wipes all the maps before repopulating them
images.clear();
@@ -418,28 +425,31 @@ 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(), StandardCharsets.UTF_8));
for (IResource handbookFile : handbookFiles) {
JsonElement je = gson.fromJson(reader, JsonElement.class);
JsonObject json = je.getAsJsonObject();
BufferedReader reader = new BufferedReader(new InputStreamReader(handbookFile.getInputStream(), StandardCharsets.UTF_8));
JsonUtils.getJsonObject(json, "colours").entrySet().forEach(e -> colours.put(e.getKey(),
Color.decode(e.getValue().getAsString()).getRGB()));
JsonElement je = gson.fromJson(reader, JsonElement.class);
JsonObject json = je.getAsJsonObject();
// Repopulates the remaining maps
Image.populate(images, json);
CraftingRecipe.populate(recipes, json);
Section.populate(sections, json);
JsonUtils.getJsonObject(json, "colours").entrySet().forEach(e -> colours.put(e.getKey(),
Color.decode(e.getValue().getAsString()).getRGB()));
sectionList = Collections.unmodifiableList(new ArrayList<>(sections.values()));
// Repopulates the remaining maps
Image.populate(images, json);
CraftingRecipe.populate(recipes, json);
Section.populate(sections, json);
if(sections.isEmpty()){
Wizardry.logger.warn("Handbook has no sections! Aborting loading...");
return;
sectionList = Collections.unmodifiableList(new ArrayList<>(sections.values()));
if(sections.isEmpty()){
Wizardry.logger.warn("Handbook has no sections! Aborting loading...");
return;
}
bookmarkSection = JsonUtils.getString(json, "bookmark_start_section");
if(!sections.containsKey(bookmarkSection)) throw new JsonSyntaxException("Section with id " + bookmarkSection + " is undefined");
}
bookmarkSection = JsonUtils.getString(json, "bookmark_start_section");
if(!sections.containsKey(bookmarkSection)) throw new JsonSyntaxException("Section with id " + bookmarkSection + " is undefined");
}
// The first resource load on startup is done before the packet handler is loaded
@@ -455,16 +465,17 @@ public class GuiWizardHandbook extends GuiScreen {
* @param manager The resource manager instance to use.
* @return The handbook JSON file, as an IResource, or null if it was not found.
*/
private static IResource getHandbookResource(IResourceManager manager){
private static List<IResource> getHandbookResource(IResourceManager manager){
// TODO: Implement resource pack stacking to allow addon mods and texture packs to add/overwrite content
IResource handbookFile = null;
List<IResource> handbookFiles = new ArrayList<>();
try{
handbookFile = manager.getResource(new ResourceLocation(Wizardry.MODID, "texts/handbook_"
+ Minecraft.getMinecraft().getLanguageManager().getCurrentLanguage().getLanguageCode() + ".json"));
}catch(IOException e){
}catch(Exception e){
Wizardry.logger.info("Wizard handbook JSON file missing for the current language (" + Minecraft.getMinecraft()
.getLanguageManager().getCurrentLanguage() + "). Using default (English-US) instead.");
@@ -476,7 +487,29 @@ public class GuiWizardHandbook extends GuiScreen {
}
}
return handbookFile;
handbookFiles.add(handbookFile);
for (String modid : ADDONS) {
// Addons
Wizardry.logger.info("Registering addon Wizard's Handbook contents for " + modid);
try{
handbookFile = manager.getResource(new ResourceLocation(modid, "texts/handbook_"
+ Minecraft.getMinecraft().getLanguageManager().getCurrentLanguage().getLanguageCode() + ".json"));
}catch(Exception e){
Wizardry.logger.info("Wizard handbook JSON file missing for the current language (" + Minecraft.getMinecraft()
.getLanguageManager().getCurrentLanguage() + "). Using default (English-US) instead.");
try{
handbookFile = manager.getResource(new ResourceLocation(modid, "texts/handbook_en_us.json"));
}catch(IOException x){
Wizardry.logger.error("Couldn't find file: " + DEFAULT + ". The file may be missing; please try re-downloading and reinstalling Wizardry.", x);
}
}
handbookFiles.add(handbookFile);
}
return handbookFiles;
}
// Controls
@@ -496,24 +529,30 @@ public class GuiWizardHandbook extends GuiScreen {
Section currentSection = null;
for(Section section : sections.values()){
// We always want this button to do something, and taking the right-hand page means it always does
if(section.containsPage(doubleToSinglePage(currentPage, true))){
currentSection = section;
break;
if(currentPage < singleToDoublePage(pageCount)){
// Find current section
for(Section section : sections.values()){
// Take the right-hand page if going to the next section, and the left-hand page otherwise
if(section.containsPage(doubleToSinglePage(currentPage, button == nextSection))){
currentSection = section;
break;
}
}
}
if(currentSection != null){
List<Section> visibleSections = new ArrayList<>(sectionList);
visibleSections.removeIf(s -> !s.isUnlocked());
List<Section> visibleSections = new ArrayList<>(sectionList);
visibleSections.removeIf(s -> !s.isUnlocked());
int index = currentSection == null ? visibleSections.size() : visibleSections.indexOf(currentSection);
int index = visibleSections.indexOf(currentSection);
if(button == nextSection && index + 1 < visibleSections.size()){
if(button == nextSection){
if(index + 1 < visibleSections.size()){
currentPage = singleToDoublePage(visibleSections.get(index + 1).startPage);
}else if(index > 0){
}else{
currentPage = singleToDoublePage(pageCount);
}
}else{
if(index > 0){
currentPage = singleToDoublePage(visibleSections.get(index - 1).startPage);
}
}
@@ -570,4 +609,9 @@ public class GuiWizardHandbook extends GuiScreen {
sections.values().forEach(s -> s.updateUnlockStatus(showToasts, completedAdvancements));
}
public static void registerAddonHandbookContent(String modid) {
if (!ADDONS.contains(modid)) {
ADDONS.add(modid);
}
}
}
@@ -1,5 +1,6 @@
package electroblob.wizardry.client.gui.handbook;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Streams;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
@@ -35,6 +36,8 @@ import java.util.*;
// be a non-static inner class
class Section {
private static final Set<String> SPACELESS_LANGUAGES = ImmutableSet.of("zh_cn", "zh_tw");
// Final fields are mandatory (none here though), the rest are optional
String title;
private String[] rawText;
@@ -293,7 +296,11 @@ class Section {
String linkRaw = paragraph.substring(linkStart, linkEnd + 1);
String[] arguments = paragraph.substring(linkStart + 1, linkEnd).split("\\s", 2);
String suffix = paragraph.substring(linkEnd).split("\\s", 2)[0].substring(1); // substring(1) to remove the @
String suffix = "";
// Account for trailing punctuation, except in languages that don't use spaces such as Chinese
boolean spaceless = SPACELESS_LANGUAGES.contains(Minecraft.getMinecraft().getLanguageManager().getCurrentLanguage() == null ? "en_us" : Minecraft.getMinecraft().getLanguageManager().getCurrentLanguage().getLanguageCode());
if(!spaceless) paragraph.substring(linkEnd).split("\\s", 2)[0].substring(1); // substring(1) to remove the @
// The index of the single page currently being formatted, relative to the section
int pageRelative = (lines.size() + upToLink.size() - 1) / maxLineNumber;
@@ -311,7 +318,7 @@ 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)));
this.buttons.get(pageRelative).add(GuiButtonHyperlink.create(x, y, font, upToLink, arguments, suffix, maxLineNumber - lineNumber - 1, GuiWizardHandbook.isRightPage(page), spaceless));
// 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
@@ -13,17 +13,17 @@ 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.*;
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 ResourceLocation DEFAULT_BOOK_TEXTURE = new ResourceLocation(Wizardry.MODID, "blocks/books");
private static final List<ResourceLocation> bookModelLocations = new ArrayList<>();
private static final Map<String, IBakedModel[][]> bakedBookModels = new HashMap<>();
private final ModelResourceLocation bookshelfModelLocation;
private final String variant;
@@ -49,19 +49,7 @@ public class ModelBookshelf implements IModel {
// 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!
}
}
IBakedModel[][] books = getBakedBookModels(format, bakedTextureGetter);
// 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
@@ -74,6 +62,29 @@ public class ModelBookshelf implements IModel {
}
/** Retrieves the baked book models, or bakes them if they haven't been created yet. */
protected IBakedModel[][] getBakedBookModels(VertexFormat format, Function<ResourceLocation, TextureAtlasSprite> bakedTextureGetter) throws Exception{
if(bakedBookModels.get(variant) == null){
ImmutableList<ResourceLocation> textures = BlockBookshelf.getBookTextures();
bakedBookModels.put(variant, 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);
bakedBookModels.get(variant)[i][j] = bookModel.bake(bookModel.getDefaultState(), format, bakedTextureGetter); // Same here!
}
}
}
return bakedBookModels.get(variant);
}
@Override
public Collection<ResourceLocation> getDependencies(){
return bookModelLocations;
@@ -0,0 +1,52 @@
package electroblob.wizardry.client.renderer;
import electroblob.wizardry.Wizardry;
import net.minecraft.client.model.ModelIronGolem;
import net.minecraft.client.renderer.GlStateManager;
import net.minecraft.client.renderer.entity.RenderLiving;
import net.minecraft.client.renderer.entity.RenderManager;
import net.minecraft.entity.monster.EntityIronGolem;
import net.minecraft.util.ResourceLocation;
import net.minecraftforge.fml.relauncher.Side;
import net.minecraftforge.fml.relauncher.SideOnly;
import org.lwjgl.opengl.GL11;
@SideOnly(Side.CLIENT)
public class RenderSpectralGolem extends RenderLiving<EntityIronGolem>
{
private static final ResourceLocation SPECTRAL_GOLEM_TEXTURES = new ResourceLocation(Wizardry.MODID, "textures/entity/spectral_golem.png");
public RenderSpectralGolem(RenderManager renderManagerIn)
{
super(renderManagerIn, new ModelIronGolem(), 0.5F);
}
/**
* Returns the location of an entity's texture. Doesn't seem to be called unless you call Render.bindEntityTexture.
*/
protected ResourceLocation getEntityTexture(EntityIronGolem entity)
{
return SPECTRAL_GOLEM_TEXTURES;
}
@Override
protected void preRenderCallback(EntityIronGolem entity, float partialTickTime){
super.preRenderCallback(entity, partialTickTime);
GlStateManager.enableBlend();
GlStateManager.blendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA);
GlStateManager.color(1, 1, 1, 0.7f);
}
protected void applyRotations(EntityIronGolem entityLiving, float ageInTicks, float rotationYaw, float partialTicks)
{
super.applyRotations(entityLiving, ageInTicks, rotationYaw, partialTicks);
if ((double)entityLiving.limbSwingAmount >= 0.01D)
{
float f = 13.0F;
float f1 = entityLiving.limbSwing - entityLiving.limbSwingAmount * (1.0F - partialTicks) + 6.0F;
float f2 = (Math.abs(f1 % 13.0F - 6.5F) - 3.25F) / 3.25F;
GlStateManager.rotate(6.5F * f2, 0.0F, 0.0F, 1.0F);
}
}
}
@@ -0,0 +1,37 @@
package electroblob.wizardry.client.renderer.entity;
import electroblob.wizardry.Wizardry;
import electroblob.wizardry.entity.projectile.EntityConjuredArrow;
import net.minecraft.client.renderer.GlStateManager;
import net.minecraft.client.renderer.entity.RenderArrow;
import net.minecraft.client.renderer.entity.RenderManager;
import net.minecraft.util.ResourceLocation;
import net.minecraftforge.fml.relauncher.Side;
import net.minecraftforge.fml.relauncher.SideOnly;
import org.lwjgl.opengl.GL11;
@SideOnly(Side.CLIENT)
public class RenderConjuredArrow extends RenderArrow<EntityConjuredArrow> {
public static final ResourceLocation CONJURED_ARROW_TEXTURE = new ResourceLocation(Wizardry.MODID, "textures/entity/conjured_arrow.png");
public RenderConjuredArrow(RenderManager manager) {
super(manager);
}
@Override
protected ResourceLocation getEntityTexture(EntityConjuredArrow entity) {
return CONJURED_ARROW_TEXTURE;
}
@Override
public void doRender(EntityConjuredArrow entity, double x, double y, double z, float entityYaw, float partialTicks) {
GlStateManager.pushMatrix();
GlStateManager.enableBlend();
GlStateManager.blendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA);
GlStateManager.color(1, 1, 1, 0.4f);
super.doRender(entity, x, y, z, entityYaw, partialTicks);
GlStateManager.popMatrix();
}
}
@@ -0,0 +1,53 @@
package electroblob.wizardry.client.renderer.entity.layers;
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.minecraft.client.renderer.OpenGlHelper;
import net.minecraft.client.renderer.entity.RenderLivingBase;
import net.minecraft.entity.EntityLivingBase;
import net.minecraft.util.ResourceLocation;
/**
* Layer used to render the diamondflesh texture on creatures with the diamondflesh effect.
*
* @author WinDanesz
* @since Wizardry 4.3.7
*/
public class LayerDiamond extends LayerTiledOverlay<EntityLivingBase> {
private static final ResourceLocation TEXTURE = new ResourceLocation("textures/blocks/diamond_block.png");
public LayerDiamond(RenderLivingBase<?> renderer){
super(renderer);
}
@Override
public boolean shouldRender(EntityLivingBase entity, float partialTicks){
return !entity.isInvisible() && entity.isPotionActive(WizardryPotions.diamondflesh);
}
@Override
public ResourceLocation getTexture(EntityLivingBase entity, float partialTicks){
return TEXTURE;
}
@Override
public void doRenderLayer(EntityLivingBase entity, float limbSwing, float limbSwingAmount, float partialTicks,
float ageInTicks, float netHeadYaw, float headPitch, float scale){
GlStateManager.enableBlend();
GlStateManager.blendFunc(SourceFactor.SRC_ALPHA, DestFactor.ONE_MINUS_SRC_ALPHA);
int j = entity.getBrightnessForRender();
int k = j % 65536;
int l = j / 65536;
OpenGlHelper.setLightmapTextureCoords(OpenGlHelper.lightmapTexUnit, (float) k, (float) l);
super.doRenderLayer(entity, limbSwing, limbSwingAmount, partialTicks, ageInTicks, netHeadYaw, headPitch, scale);
GlStateManager.disableBlend();
}
}
@@ -0,0 +1,53 @@
package electroblob.wizardry.client.renderer.entity.layers;
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.minecraft.client.renderer.OpenGlHelper;
import net.minecraft.client.renderer.entity.RenderLivingBase;
import net.minecraft.entity.EntityLivingBase;
import net.minecraft.util.ResourceLocation;
/**
* Layer used to render the ironflesh texture on creatures with the ironflesh effect.
*
* @author WinDanesz
* @since Wizardry 4.3.7
*/
public class LayerIron extends LayerTiledOverlay<EntityLivingBase> {
private static final ResourceLocation TEXTURE = new ResourceLocation("textures/blocks/iron_block.png");
public LayerIron(RenderLivingBase<?> renderer){
super(renderer);
}
@Override
public boolean shouldRender(EntityLivingBase entity, float partialTicks){
return !entity.isInvisible() && entity.isPotionActive(WizardryPotions.ironflesh);
}
@Override
public ResourceLocation getTexture(EntityLivingBase entity, float partialTicks){
return TEXTURE;
}
@Override
public void doRenderLayer(EntityLivingBase entity, float limbSwing, float limbSwingAmount, float partialTicks,
float ageInTicks, float netHeadYaw, float headPitch, float scale){
GlStateManager.enableBlend();
GlStateManager.blendFunc(SourceFactor.SRC_ALPHA, DestFactor.ONE_MINUS_SRC_ALPHA);
int j = entity.getBrightnessForRender();
int k = j % 65536;
int l = j / 65536;
OpenGlHelper.setLightmapTextureCoords(OpenGlHelper.lightmapTexUnit, (float) k, (float) l);
super.doRenderLayer(entity, limbSwing, limbSwingAmount, partialTicks, ageInTicks, netHeadYaw, headPitch, scale);
GlStateManager.disableBlend();
}
}
@@ -0,0 +1,53 @@
package electroblob.wizardry.client.renderer.entity.layers;
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.minecraft.client.renderer.OpenGlHelper;
import net.minecraft.client.renderer.entity.RenderLivingBase;
import net.minecraft.entity.EntityLivingBase;
import net.minecraft.util.ResourceLocation;
/**
* Layer used to render the oakflesh texture on creatures with the oakflesh effect.
*
* @author WinDanesz
* @since Wizardry 4.3.7
*/
public class LayerOak extends LayerTiledOverlay<EntityLivingBase> {
private static final ResourceLocation TEXTURE = new ResourceLocation("textures/blocks/log_oak.png");
public LayerOak(RenderLivingBase<?> renderer){
super(renderer);
}
@Override
public boolean shouldRender(EntityLivingBase entity, float partialTicks){
return !entity.isInvisible() && entity.isPotionActive(WizardryPotions.oakflesh);
}
@Override
public ResourceLocation getTexture(EntityLivingBase entity, float partialTicks){
return TEXTURE;
}
@Override
public void doRenderLayer(EntityLivingBase entity, float limbSwing, float limbSwingAmount, float partialTicks,
float ageInTicks, float netHeadYaw, float headPitch, float scale){
GlStateManager.enableBlend();
GlStateManager.blendFunc(SourceFactor.SRC_ALPHA, DestFactor.ONE_MINUS_SRC_ALPHA);
int j = entity.getBrightnessForRender();
int k = j % 65536;
int l = j / 65536;
OpenGlHelper.setLightmapTextureCoords(OpenGlHelper.lightmapTexUnit, (float) k, (float) l);
super.doRenderLayer(entity, limbSwing, limbSwingAmount, partialTicks, ageInTicks, netHeadYaw, headPitch, scale);
GlStateManager.disableBlend();
}
}
@@ -60,10 +60,12 @@ public class RenderSixthSense {
Minecraft mc = Minecraft.getMinecraft();
RenderManager renderManager = event.getRenderer().getRenderManager();
float effectRadius = Spells.sixth_sense.getProperty(Spell.EFFECT_RADIUS).floatValue();
float distance = event.getEntity().getDistance(mc.player);
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()
&& distance < effectRadius
* (1 + mc.player.getActivePotionEffect(WizardryPotions.sixth_sense).getAmplifier() * Constants.RANGE_INCREASE_PER_LEVEL)){
Tessellator tessellator = Tessellator.getInstance();
@@ -87,7 +89,13 @@ public class RenderSixthSense {
GlStateManager.rotate(180 - renderManager.playerViewY, 0.0F, 1.0F, 0.0F);
GlStateManager.rotate(yaw, 1.0F, 0.0F, 0.0F);
GlStateManager.color(1, 1, 1, 1);
//Decreases the opacity of the marker after 80% of the effect distance
float alpha = 1f;
float f = 5 * (1f - distance / effectRadius);
if (f <= 1) {
alpha = f;
}
GlStateManager.color(1, 1, 1, alpha);
ResourceLocation texture = PASSIVE_MOB_MARKER_TEXTURE;
@@ -1,5 +1,6 @@
package electroblob.wizardry.constants;
import electroblob.wizardry.Wizardry;
import electroblob.wizardry.WizardryEventHandler;
/** Stores various global constants used in Wizardry. */
@@ -7,46 +8,62 @@ public final class Constants {
/** The amount of mana a crystal shard is worth */
// 100 doesn't divide nicely by 9 so we're calling this 10. I guess you lose a little bit by smashing a crystal.
public static final int MANA_PER_SHARD = 10;
public static int MANA_PER_SHARD;
/** The amount of mana each magic crystal is worth */
public static final int MANA_PER_CRYSTAL = 100;
public static int MANA_PER_CRYSTAL;
/** The amount of mana a grand magic crystal is worth */
public static final int GRAND_CRYSTAL_MANA = 400;
public static int GRAND_CRYSTAL_MANA;
/** The maximum number of one type of wand upgrade which can be applied to a wand. */
public static final int UPGRADE_STACK_LIMIT = 3;
public static int UPGRADE_STACK_LIMIT;
/** The bonus amount of wand upgrades that can be applied to a non-elemental wand. */
public static final int NON_ELEMENTAL_UPGRADE_BONUS = 3;
public static int NON_ELEMENTAL_UPGRADE_BONUS;
/** The fraction by which cooldowns are reduced for each level of cooldown upgrade. */
public static final float COOLDOWN_REDUCTION_PER_LEVEL = 0.15f;
public static float COOLDOWN_REDUCTION_PER_LEVEL;
/** The fraction by which maximum charge is increased for each level of storage upgrade. */
public static final float STORAGE_INCREASE_PER_LEVEL = 0.15f;
public static float STORAGE_INCREASE_PER_LEVEL;
/** The fraction by which potency is increased for each tier of matching wand. */
public static final float POTENCY_INCREASE_PER_TIER = 0.15f;
public static float POTENCY_INCREASE_PER_TIER;
/** The fraction by which spell duration is increased for each level of duration upgrade. */
public static final float DURATION_INCREASE_PER_LEVEL = 0.25f;
public static float DURATION_INCREASE_PER_LEVEL;
/** The fraction by which spell range is increased for each level of range upgrade. */
public static final float RANGE_INCREASE_PER_LEVEL = 0.25f;
public static float RANGE_INCREASE_PER_LEVEL;
/** The fraction by which spell blast radius is increased for each level of range upgrade. */
public static final float BLAST_RADIUS_INCREASE_PER_LEVEL = 0.25f;
public static float BLAST_RADIUS_INCREASE_PER_LEVEL;
/** The fraction by which movement speed is reduced per level of frost effect. */
public static final double FROST_SLOWNESS_PER_LEVEL = 0.5;
public static double FROST_SLOWNESS_PER_LEVEL;
/** The fraction by which movement speed is reduced per level of decay effect. */
public static final double DECAY_SLOWNESS_PER_LEVEL = 0.2;
/** The fraction by which dig speed is reduced per level of frostbite effect. */
public static final float FROST_FATIGUE_PER_LEVEL = 0.45f;
/** The number of ticks between each mana increase for wands with the condenser upgrade. */
public static final int CONDENSER_TICK_INTERVAL = 50;
public static int CONDENSER_TICK_INTERVAL;
/**
* The amount of mana given for a kill for each level of siphon upgrade. A random amount from 0 to this number - 1
* is also added. See {@link WizardryEventHandler#onLivingDeathEvent} for more details.
*/
public static final int SIPHON_MANA_PER_LEVEL = 5;
public static int SIPHON_MANA_PER_LEVEL;
/**
* The number of ticks between the spawning of patches of decay when an entity has the decay effect. Note that decay
* won't spawn again if something is already standing in it.
*/
public static final int DECAY_SPREAD_INTERVAL = 8;
/** The fraction by which potency is increased per level of the empowerment effect. */
public static final float EMPOWERMENT_POTENCY_PER_LEVEL = 0.25f;
// making this as an update to the existing values to not break addons directly relying on the fields
static {
MANA_PER_SHARD = Wizardry.settings.manaPerShard;
MANA_PER_CRYSTAL = Wizardry.settings.manaPerCrystal;
GRAND_CRYSTAL_MANA = Wizardry.settings.grandCrystalMana;
UPGRADE_STACK_LIMIT = Wizardry.settings.upgradeStackLimit;
NON_ELEMENTAL_UPGRADE_BONUS = Wizardry.settings.nonElementalUpgradeBonus;
COOLDOWN_REDUCTION_PER_LEVEL = (float) Wizardry.settings.cooldownReductionPerLevel;
STORAGE_INCREASE_PER_LEVEL = Wizardry.settings.storageIncreasePerLevel;
POTENCY_INCREASE_PER_TIER = (float) Wizardry.settings.potencyIncreasePerTier;
DURATION_INCREASE_PER_LEVEL = (float) Wizardry.settings.durationIncreasePerLevel;
RANGE_INCREASE_PER_LEVEL = (float) Wizardry.settings.rangeIncreasePerLevel;
BLAST_RADIUS_INCREASE_PER_LEVEL = (float) Wizardry.settings.blastIncreasePerLevel;
FROST_SLOWNESS_PER_LEVEL = (float) Wizardry.settings.frostSlownessIncreasePerLevel;
SIPHON_MANA_PER_LEVEL = Wizardry.settings.siphonManaPerLevel;
CONDENSER_TICK_INTERVAL = Wizardry.settings.condenserTickInterval;
}
}
@@ -10,10 +10,10 @@ import java.util.Random;
public enum Tier {
NOVICE(700, 3, 12, new Style().setColor(TextFormatting.WHITE), "novice"),
APPRENTICE(1000, 5, 5, new Style().setColor(TextFormatting.AQUA), "apprentice"),
ADVANCED(1500, 7, 2, new Style().setColor(TextFormatting.DARK_BLUE), "advanced"),
MASTER(2500, 9, 1, new Style().setColor(TextFormatting.DARK_PURPLE), "master");
NOVICE(Wizardry.settings.noviceMaxCharge, Wizardry.settings.noviceUpgradeLimit, 12, new Style().setColor(TextFormatting.WHITE), "novice"),
APPRENTICE(Wizardry.settings.apprenticeMaxCharge, Wizardry.settings.apprenticeUpgradeLimit, 5, new Style().setColor(TextFormatting.AQUA), "apprentice"),
ADVANCED(Wizardry.settings.advancedMaxCharge, Wizardry.settings.advancedUpgradeLimit, 2, new Style().setColor(TextFormatting.DARK_BLUE), "advanced"),
MASTER(Wizardry.settings.masterMaxCharge, Wizardry.settings.masterUpgradeLimit, 1, new Style().setColor(TextFormatting.DARK_PURPLE), "master");
/** Maximum mana a wand of this tier can store. */
public final int maxCharge;
@@ -64,6 +64,11 @@ public interface IStoredVariable<T> extends IVariable<T> {
this.ticker = (p, t) -> t; // Initialise this with a do-nothing function, can be overwritten later
}
@Override
public String getKey() {
return key;
}
/**
* Replaces this variable's update method with the given update function. <i>Beware of auto-unboxing of
* primitive types! For lambda expressions, check the second parameter isn't null before operating on it.
@@ -47,7 +47,10 @@ public interface IVariable<T> {
*/
void write(ByteBuf buf, T value);
String getKey();
/**
*
* Reads this variable's value from the given {@link ByteBuf}.
*/
T read(ByteBuf buf);
@@ -110,6 +113,11 @@ public interface IVariable<T> {
// NYI
}
@Override
public String getKey() {
return "none"; // we don't mind as these are never synced (electroblob.wizardry.data.IVariable.Variable.isSynced)
}
@Override
public T read(ByteBuf buf){
return null; // NYI
@@ -7,7 +7,6 @@ import electroblob.wizardry.enchantment.Imbuement;
import electroblob.wizardry.entity.living.ISummonedCreature;
import electroblob.wizardry.event.SpellCastEvent;
import electroblob.wizardry.event.SpellCastEvent.Source;
import electroblob.wizardry.item.ItemWand;
import electroblob.wizardry.packet.PacketCastContinuousSpell;
import electroblob.wizardry.packet.PacketPlayerSync;
import electroblob.wizardry.packet.WizardryPacketHandler;
@@ -47,6 +46,7 @@ import javax.annotation.Nullable;
import java.lang.ref.WeakReference;
import java.util.*;
import java.util.stream.Collectors;
import java.util.AbstractMap.SimpleEntry;
/**
* Capability-based replacement for the old ExtendedPlayer class from 1.7.10. This has been reworked to leave minimum
@@ -73,7 +73,7 @@ public class WizardData implements INBTSerializable<NBTTagCompound> {
private static final Set<IStoredVariable> storedVariables = new HashSet<>();
/** The maximum number of recent spells to track. */
public static final int MAX_RECENT_SPELLS = ItemWand.BASE_SPELL_SLOTS;
public static int MAX_RECENT_SPELLS;
private static final int IMBUEMENT_UPDATE_INTERVAL = 20;
@@ -126,7 +126,7 @@ public class WizardData implements INBTSerializable<NBTTagCompound> {
* be modified via {@link WizardData#setVariable(IVariable, Object)}, which (as a method) is able to enforce it. */
private final Map<IVariable, Object> spellData;
private Queue<Spell> recentSpells;
private Queue<SimpleEntry<Spell, Long>> recentSpells;
// This one is still necessary, because I can't override the equip animation for items that aren't from Wizardry.
// Leaving this for now because merging it into the spell data system will be more tricky
@@ -199,9 +199,14 @@ public class WizardData implements INBTSerializable<NBTTagCompound> {
}
/** Returns a set containing the registered {@link IStoredVariable} objects for which {@link IVariable#isSynced()}
* returns true. Used internally for packet reading. */
public static Set<IVariable> getSyncedVariables(){
return storedVariables.stream().filter(IVariable::isSynced).collect(Collectors.toSet());
* returns true, ordered by their keys obtained from {@link IVariable#getKey()}. Used internally for packets. */
public static Set<IVariable> getSyncedVariablesOrderedByKey(){
Comparator<IVariable> keyComparator = Comparator.comparing(IVariable::getKey);
return storedVariables.stream()
.filter(IVariable::isSynced)
.sorted(keyComparator)
.collect(Collectors.toCollection(LinkedHashSet::new));
}
/**
@@ -289,15 +294,19 @@ public class WizardData implements INBTSerializable<NBTTagCompound> {
* @param spell The spell to be tracked.
*/
public void trackRecentSpell(Spell spell){
this.recentSpells.add(spell);
this.recentSpells.add(new SimpleEntry<>(spell, player.world.getTotalWorldTime()));
}
/**
* Returns the number of times the given spell is tracked in this player's recently-cast spells.
* Only counts spells cast within the configured expiry time.
* @param spell The spell to count casts for.
*/
public int countRecentCasts(Spell spell){
return (int)this.recentSpells.stream().filter(s -> s == spell).count(); // We know this can't be more than 5
long currentTime = player.world.getTotalWorldTime();
return (int)this.recentSpells.stream()
.filter(entry -> entry.getKey() == spell && (currentTime - entry.getValue()) < Wizardry.settings.recentSpellExpiryTime)
.count(); // We know this can't be more than 5
}
// Imbuements
@@ -346,20 +355,20 @@ public class WizardData implements INBTSerializable<NBTTagCompound> {
// For each item in the player's inventory
for(ItemStack stack : player.inventory.mainInventory){
updateImbutedItem(stack, activeImbuements);
updateImbuedItem(stack, activeImbuements);
}
for(ItemStack stack : player.inventory.armorInventory){
updateImbutedItem(stack, activeImbuements);
updateImbuedItem(stack, activeImbuements);
}
for(ItemStack stack : player.inventory.offHandInventory){
updateImbutedItem(stack, activeImbuements);
updateImbuedItem(stack, activeImbuements);
}
// Removes all imbuements from the map that are no longer active
this.imbuementDurations.keySet().retainAll(activeImbuements);
}
private void updateImbutedItem(ItemStack stack, Set<Imbuement> activeImbuements){
private void updateImbuedItem(ItemStack stack, Set<Imbuement> activeImbuements){
if(stack.isItemEnchanted()){
@@ -382,6 +391,7 @@ public class WizardData implements INBTSerializable<NBTTagCompound> {
activeImbuements.add((Imbuement)enchantment);
}else{
// Otherwise, removes the enchantment from the item
((Imbuement) enchantment).onImbuementRemoval(stack);
iterator.remove(); // FIXME: Apparently this can cause a CME
}
}
@@ -455,7 +465,7 @@ public class WizardData implements INBTSerializable<NBTTagCompound> {
if(this.castCommandSpell != null && this.castCommandSpell.isContinuous){
if(castCommandTick >= castCommandDuration){
if(castCommandTick > castCommandDuration){
this.stopCastingContinuousSpell();
return;
}
@@ -507,6 +517,12 @@ public class WizardData implements INBTSerializable<NBTTagCompound> {
if(player.ticksExisted % IMBUEMENT_UPDATE_INTERVAL == 0) updateImbuedItems();
updateContinuousSpellCasting();
// Clean up expired recent spells every 60 ticks (1 second)
if(player.ticksExisted % 60 == 0) {
long currentTime = player.world.getTotalWorldTime();
this.recentSpells.removeIf(entry -> (currentTime - entry.getValue()) >= Wizardry.settings.recentSpellExpiryTime);
}
this.spellData.forEach((k, v) -> this.spellData.put(k, k.update(player, v)));
this.spellData.keySet().removeIf(k -> k.canPurge(player, this.spellData.get(k)));
}
@@ -573,7 +589,15 @@ public class WizardData implements INBTSerializable<NBTTagCompound> {
properties.setInteger("maxTierReached", maxTierReached.ordinal());
NBTExtras.storeTagSafely(properties, "recentSpells", NBTExtras.listToNBT(recentSpells, s -> new NBTTagInt(s.metadata())));
// Serialize recent spells with timestamps as compound tags
NBTTagList recentSpellsList = new NBTTagList();
for(SimpleEntry<Spell, Long> entry : recentSpells) {
NBTTagCompound spellTag = new NBTTagCompound();
spellTag.setInteger("spellId", entry.getKey().metadata());
spellTag.setLong("timestamp", entry.getValue());
recentSpellsList.appendTag(spellTag);
}
NBTExtras.storeTagSafely(properties, "recentSpells", recentSpellsList);
storedVariables.forEach(k -> k.write(properties, this.spellData.get(k)));
@@ -600,8 +624,14 @@ public class WizardData implements INBTSerializable<NBTTagCompound> {
// Probably won't be null but we may as well just reinitialise it instead of clearing it
this.recentSpells = EvictingQueue.create(MAX_RECENT_SPELLS);
this.recentSpells.addAll(NBTExtras.NBTToList(nbt.getTagList("recentSpells", NBT.TAG_INT),
(NBTTagInt tag) -> Spell.byMetadata(tag.getInt())));
// Deserialize recent spells with timestamps
NBTTagList recentSpellsList = nbt.getTagList("recentSpells", NBT.TAG_COMPOUND);
for(int i = 0; i < recentSpellsList.tagCount(); i++) {
NBTTagCompound spellTag = recentSpellsList.getCompoundTagAt(i);
Spell spell = Spell.byMetadata(spellTag.getInteger("spellId"));
long timestamp = spellTag.getLong("timestamp");
this.recentSpells.add(new SimpleEntry<>(spell, timestamp));
}
try{
storedVariables.forEach(k -> this.spellData.put(k, k.read(nbt)));
@@ -33,7 +33,7 @@ import java.util.Iterator;
* than having to deal with each of them separately, which would be inefficient and cumbersome (the former of those
* classes cannot extend the latter because they both need to extend different subclasses of
* {@link net.minecraft.enchantment.Enchantment}).
*
*
* @since Wizardry 1.2
*/
@Mod.EventBusSubscriber
@@ -69,12 +69,16 @@ public interface Imbuement {
Enchantment enchantment = Enchantment.getEnchantmentByID(enchantmentTag.getShort("id"));
// If the item contains a magic weapon enchantment, remove it from the item
if(enchantment instanceof Imbuement){
((Imbuement) enchantment).onImbuementRemoval(stack);
enchantmentIt.remove();
}
}
}
}
/** Allows executing some custom logic before this imbuement is being removed from the stack. Called when the imbuement is about to be removed. */
default void onImbuementRemoval(ItemStack stack){}
@SubscribeEvent
public static void onPlayerOpenContainerEvent(PlayerContainerEvent event){
// Brute-force fix to stop enchanted books in dungeon chests from having imbuements on them.
@@ -1,10 +1,8 @@
package electroblob.wizardry.entity.construct;
import electroblob.wizardry.entity.projectile.EntityConjuredArrow;
import electroblob.wizardry.registry.Spells;
import electroblob.wizardry.spell.Spell;
import net.minecraft.entity.projectile.EntityTippedArrow;
import net.minecraft.init.Items;
import net.minecraft.item.ItemStack;
import net.minecraft.util.math.MathHelper;
import net.minecraft.world.World;
@@ -30,7 +28,7 @@ public class EntityArrowRain extends EntityScaledConstruct {
double y = posY + world.rand.nextDouble() * (double)height;
double z = posZ + (world.rand.nextDouble() - 0.5D) * (double)width;
EntityTippedArrow arrow = new EntityTippedArrow(world, x, y, z);
EntityConjuredArrow arrow = new EntityConjuredArrow(world, x, y, z);
arrow.motionX = MathHelper.cos((float)Math.toRadians(this.rotationYaw + 90));
arrow.motionY = -0.6;
@@ -38,7 +36,6 @@ public class EntityArrowRain extends EntityScaledConstruct {
arrow.shootingEntity = this.getCaster();
arrow.setDamage(7.0d * damageMultiplier);
arrow.setPotionEffect(new ItemStack(Items.ARROW));
this.world.spawnEntity(arrow);
}
@@ -35,8 +35,8 @@ public class EntityDecay extends EntityMagicConstruct {
0.6F + rand.nextFloat() * 0.15F);
if(!this.world.isRemote){
List<EntityLivingBase> targets = EntityUtils.getLivingWithinRadius(1.0d, this.posX, this.posY,
this.posZ, this.world);
List<EntityLivingBase> targets = EntityUtils.getLivingWithinCylinder(this.width/2f, this.posX, this.posY,
this.posZ, this.height, this.world);
for(EntityLivingBase target : targets){
if(target != this.getCaster()){
// If this check wasn't here the potion would be reapplied every tick and hence the entity would be
@@ -32,9 +32,9 @@ public class EntityFireRing extends EntityScaledConstruct {
super.onUpdate();
if(!this.world.isRemote){
if(this.ticksExisted % 5 == 0 && !this.world.isRemote){
List<EntityLivingBase> targets = EntityUtils.getLivingWithinRadius(width/2, this.posX, this.posY, this.posZ, this.world);
List<EntityLivingBase> targets = EntityUtils.getLivingWithinCylinder(this.width/2, this.posX, this.posY, this.posZ, this.height, this.world);
for(EntityLivingBase target : targets){
@@ -34,7 +34,7 @@ public class EntityFireSigil extends EntityScaledConstruct {
if(!this.world.isRemote){
List<EntityLivingBase> targets = EntityUtils.getLivingWithinRadius(width/2, posX, posY, posZ, world);
List<EntityLivingBase> targets = EntityUtils.getLivingWithinCylinder(this.width/2, this.posX, this.posY, this.posZ, this.height, this.world);
for(EntityLivingBase target : targets){
@@ -146,30 +146,44 @@ public class EntityForcefield extends EntityMagicConstruct implements ICustomHit
if(EntityUtils.isLiving(target)) nudgeVelocity = 0.25;
Vec3d extraVelocity = targetRelativePos.normalize().scale(nudgeVelocity);
// ...make it bounce off!
target.motionX = target.motionX * -BOUNCINESS + extraVelocity.x;
target.motionY = target.motionY * -BOUNCINESS + extraVelocity.y;
target.motionZ = target.motionZ * -BOUNCINESS + extraVelocity.z;
// Prevents the forcefield bouncing things into the floor
if(target.onGround && target.motionY < 0) target.motionY = 0.1;
// How far the target needs to move towards the centre (negative means away from the centre)
double distanceTowardsCentre = -(targetRelativePos.length() - radius) - (radius - nextTickDistance);
Vec3d targetNewPos = target.getPositionVector().add(targetRelativePos.normalize().scale(distanceTowardsCentre));
target.setPosition(targetNewPos.x, targetNewPos.y, targetNewPos.z);
world.playSound(target.posX, target.posY, target.posZ, WizardrySounds.ENTITY_FORCEFIELD_DEFLECT,
WizardrySounds.SPELLS, 0.3f, 1.3f, false);
//Moved up the check by "19" so that way allied players aren't being moved out of the forcefield because
//allied players aren't synced to client like minions. Minions are only synced because it's saved to their
//entity data which is synced in their serialize/deserialize methods
if(!world.isRemote){
// ...make it bounce off!
target.motionX = target.motionX * -BOUNCINESS + extraVelocity.x;
target.motionY = target.motionY * -BOUNCINESS + extraVelocity.y;
target.motionZ = target.motionZ * -BOUNCINESS + extraVelocity.z;
// Prevents the forcefield bouncing things into the floor
if(target.onGround && target.motionY < 0) target.motionY = 0.1;
// How far the target needs to move towards the centre (negative means away from the centre)
double distanceTowardsCentre = -(targetRelativePos.length() - radius) - (radius - nextTickDistance);
Vec3d targetNewPos = target.getPositionVector().add(targetRelativePos.normalize().scale(distanceTowardsCentre));
target.setPosition(targetNewPos.x, targetNewPos.y, targetNewPos.z);
world.playSound(target.posX, target.posY, target.posZ, WizardrySounds.ENTITY_FORCEFIELD_DEFLECT,
WizardrySounds.SPELLS, 0.3f, 1.3f, false);
// Player motion is handled on that player's client so needs packets
if(target instanceof EntityPlayerMP){
((EntityPlayerMP)target).connection.sendPacket(new SPacketEntityVelocity(target));
}
if (this.ticksExisted % 5 == 0) {
this.lifetime = (int) (lifetime * 0.99);
}
}else{
//This is a super lazy way to make sure the visual isn't playing for players and probably won't display the visual
//for players if their velocity towards the forcefield is high. But it prevents the visual being spammed when ally player
//is in forcefield so it's fine #19
if(target instanceof EntityPlayer && target.getPositionVector().distanceTo(this.getPositionVector()) < this.getRadius()) return;
Vec3d relativeImpactPos = targetRelativePos.normalize().scale(radius);
float yaw = (float)Math.atan2(relativeImpactPos.x, -relativeImpactPos.z);
@@ -36,8 +36,8 @@ public class EntityFrostSigil extends EntityScaledConstruct {
if(!this.world.isRemote){
List<EntityLivingBase> targets = EntityUtils.getLivingWithinRadius(width/2, this.posX, this.posY,
this.posZ, this.world);
List<EntityLivingBase> targets = EntityUtils.getLivingWithinCylinder(width/2, this.posX, this.posY,
this.posZ, this.height, this.world);
for(EntityLivingBase target : targets){
@@ -33,30 +33,32 @@ public class EntityHealAura extends EntityScaledConstruct {
if(!this.world.isRemote){
List<EntityLivingBase> targets = EntityUtils.getLivingWithinRadius(width/2, posX, posY, posZ, world);
List<EntityLivingBase> targets = EntityUtils.getLivingWithinCylinder(width/2, posX, posY, posZ, this.height, world);
for(EntityLivingBase target : targets){
if(this.isValidTarget(target)){
if(target.isEntityUndead()){
if(target.isEntityUndead()) {
double velX = target.motionX;
double velY = target.motionY;
double velZ = target.motionZ;
if(this.getCaster() != null){
target.attackEntityFrom(
MagicDamage.causeIndirectMagicDamage(this, getCaster(), DamageType.RADIANT),
Spells.healing_aura.getProperty(Spell.DAMAGE).floatValue() * damageMultiplier);
}else{
target.attackEntityFrom(DamageSource.MAGIC, Spells.healing_aura.getProperty(Spell.DAMAGE).floatValue() * damageMultiplier);
}
if (this.ticksExisted % 10 == 1) {
if (this.getCaster() != null) {
target.attackEntityFrom(
MagicDamage.causeIndirectMagicDamage(this, getCaster(), DamageType.RADIANT),
Spells.healing_aura.getProperty(Spell.DAMAGE).floatValue() * damageMultiplier);
} else {
target.attackEntityFrom(DamageSource.MAGIC, Spells.healing_aura.getProperty(Spell.DAMAGE).floatValue() * damageMultiplier);
}
// Removes knockback
target.motionX = velX;
target.motionY = velY;
target.motionZ = velZ;
// Removes knockback
target.motionX = velX;
target.motionY = velY;
target.motionZ = velZ;
}
}
}else if(target.getHealth() < target.getMaxHealth() && target.ticksExisted % 5 == 0){
@@ -39,8 +39,8 @@ public class EntityLightningSigil extends EntityScaledConstruct {
this.setDead();
}
List<EntityLivingBase> targets = EntityUtils.getLivingWithinRadius(width/2, this.posX, this.posY,
this.posZ, this.world);
List<EntityLivingBase> targets = EntityUtils.getLivingWithinCylinder(this.width/2, this.posX, this.posY,
this.posZ, this.height, this.world);
for(EntityLivingBase target : targets){
@@ -68,13 +68,15 @@ public class EntityEvilWizard extends EntityMob implements ISpellCaster, IEntity
/** Data parameter for the wizard's element. */
private static final DataParameter<Integer> ELEMENT = EntityDataManager.createKey(EntityEvilWizard.class,
DataSerializers.VARINT);
/** Data parameters for the wizard's current continuous spell. */
private static final DataParameter<String> CONTINUOUS_SPELL = EntityDataManager.createKey(EntityEvilWizard.class, DataSerializers.STRING);
private static final DataParameter<Integer> SPELL_COUNTER = EntityDataManager.createKey(EntityEvilWizard.class, DataSerializers.VARINT);
/** The resource location for the evil wizard's loot table. */
private static final ResourceLocation LOOT_TABLE = new ResourceLocation(Wizardry.MODID, "entities/evil_wizard");
// Field implementations
private List<Spell> spells = new ArrayList<Spell>(4);
private Spell continuousSpell;
private int spellCounter;
public EntityEvilWizard(World world){
@@ -93,6 +95,8 @@ public class EntityEvilWizard extends EntityMob implements ISpellCaster, IEntity
super.entityInit();
this.dataManager.register(HEAL_COOLDOWN, -1);
this.dataManager.register(ELEMENT, -1);
this.dataManager.register(CONTINUOUS_SPELL, "ebwizardry:none");
this.dataManager.register(SPELL_COUNTER, 0);
}
@Override
@@ -168,23 +172,23 @@ public class EntityEvilWizard extends EntityMob implements ISpellCaster, IEntity
}
@Override
public void setContinuousSpell(Spell spell){
this.continuousSpell = spell;
public void setContinuousSpell(Spell spell) {
this.dataManager.set(CONTINUOUS_SPELL, spell.getRegistryName().toString());
}
@Override
public Spell getContinuousSpell(){
return this.continuousSpell;
public Spell getContinuousSpell() {
return Spell.get(this.dataManager.get(CONTINUOUS_SPELL));
}
@Override
public void setSpellCounter(int count){
spellCounter = count;
public void setSpellCounter(int count) {
this.dataManager.set(SPELL_COUNTER, count);
}
@Override
public int getSpellCounter(){
return spellCounter;
public int getSpellCounter() {
return this.dataManager.get(SPELL_COUNTER);
}
@Override
@@ -52,7 +52,7 @@ public class EntityMagicSlime extends EntitySlime implements ISummonedCreature {
super(world);
this.setPosition(target.posX, target.posY, target.posZ);
this.startRiding(target);
this.setOwnerId(caster.getUniqueID());
if (caster != null) this.setOwnerId(caster.getUniqueID());
this.setSlimeSize(2, false); // Needs to be called before setting the experience value to 0
this.experienceValue = 0;
this.lifetime = lifetime;
@@ -0,0 +1,192 @@
package electroblob.wizardry.entity.living;
import electroblob.wizardry.Wizardry;
import electroblob.wizardry.util.ParticleBuilder;
import net.minecraft.entity.EntityFlying;
import net.minecraft.entity.EntityLivingBase;
import net.minecraft.entity.ai.EntityAIAttackMelee;
import net.minecraft.entity.ai.EntityAIHurtByTarget;
import net.minecraft.entity.ai.EntityAILookAtVillager;
import net.minecraft.entity.ai.EntityAILookIdle;
import net.minecraft.entity.ai.EntityAIMoveThroughVillage;
import net.minecraft.entity.ai.EntityAIMoveTowardsRestriction;
import net.minecraft.entity.ai.EntityAIMoveTowardsTarget;
import net.minecraft.entity.ai.EntityAINearestAttackableTarget;
import net.minecraft.entity.ai.EntityAIWanderAvoidWater;
import net.minecraft.entity.ai.EntityAIWatchClosest;
import net.minecraft.entity.monster.EntityIronGolem;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.item.Item;
import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.network.datasync.DataParameter;
import net.minecraft.network.datasync.DataSerializers;
import net.minecraft.network.datasync.EntityDataManager;
import net.minecraft.util.EnumHand;
import net.minecraft.util.EnumParticleTypes;
import net.minecraft.util.ResourceLocation;
import net.minecraft.util.text.ITextComponent;
import net.minecraft.util.text.TextComponentTranslation;
import net.minecraft.world.DifficultyInstance;
import net.minecraft.world.EnumDifficulty;
import net.minecraft.world.World;
import java.util.UUID;
public class EntitySpectralGolem extends EntityIronGolem implements ISummonedCreature {
private static final DataParameter<Boolean> SPAWN_PARTICLES = EntityDataManager.createKey(EntitySpectralGolem.class, DataSerializers.BOOLEAN);
// Field implementations
private int lifetime = -1;
private UUID casterUUID;
// Setter + getter implementations
@Override public int getLifetime(){ return lifetime; }
@Override public void setLifetime(int lifetime){ this.lifetime = lifetime; }
@Override public UUID getOwnerId(){ return casterUUID; }
@Override public void setOwnerId(UUID uuid){ this.casterUUID = uuid; }
/** Creates a new golem minion in the given world. */
public EntitySpectralGolem(World world){
super(world);
this.experienceValue = 0;
}
@Override
protected void entityInit(){
super.entityInit();
this.dataManager.register(SPAWN_PARTICLES, true);
}
@Override
protected void initEntityAI() {
this.tasks.addTask(1, new EntityAIAttackMelee(this, 1.0D, true));
this.tasks.addTask(2, new EntityAIMoveTowardsTarget(this, 0.9D, 32.0F));
this.tasks.addTask(3, new EntityAIMoveThroughVillage(this, 0.6D, true));
this.tasks.addTask(4, new EntityAIMoveTowardsRestriction(this, 1.0D));
this.tasks.addTask(5, new EntityAILookAtVillager(this));
this.tasks.addTask(6, new EntityAIWanderAvoidWater(this, 0.6D));
this.tasks.addTask(7, new EntityAIWatchClosest(this, EntityPlayer.class, 6.0F));
this.tasks.addTask(8, new EntityAILookIdle(this));
this.targetTasks.addTask(1, new EntityAIHurtByTarget(this, false));
this.targetTasks.addTask(2, new EntityAINearestAttackableTarget<>(this, EntityLivingBase.class,
0, false, true, this.getTargetSelector()));
}
@Override protected void setEquipmentBasedOnDifficulty(DifficultyInstance difficulty){} // They don't have equipment!
// Implementations
@Override
public void setRevengeTarget(EntityLivingBase entity){
if(this.shouldRevengeTarget(entity)) super.setRevengeTarget(entity);
}
@Override
public void onUpdate(){
super.onUpdate();
this.updateDelegate();
// Adds a dust particle effect
if(this.world.isRemote){
double x = this.posX - this.width / 2 + this.rand.nextFloat() * width;
double y = this.posY + this.height * this.rand.nextFloat() + 0.2f;
double z = this.posZ - this.width / 2 + this.rand.nextFloat() * width;
ParticleBuilder.create(ParticleBuilder.Type.DUST).pos(x, y, z).clr(0.7f, 0.9f, 1).shaded(true).spawn(world);
}
}
@Override
public void onSpawn(){
if(this.dataManager.get(SPAWN_PARTICLES)) this.spawnParticleEffect();
}
@Override
public void onDespawn(){
this.spawnParticleEffect();
}
private void spawnParticleEffect(){
if(this.world.isRemote){
for(int i = 0; i < 15; i++){
this.world.spawnParticle(EnumParticleTypes.SMOKE_LARGE, this.posX + this.rand.nextFloat() - 0.5f,
this.posY + this.rand.nextFloat() * 2, this.posZ + this.rand.nextFloat() - 0.5f, 0, 0, 0);
}
}
}
@Override
public boolean hasParticleEffect(){
return true;
}
@Override
public boolean hasAnimation(){
return this.dataManager.get(SPAWN_PARTICLES) || this.ticksExisted > 20;
}
public void hideParticles(){
this.dataManager.set(SPAWN_PARTICLES, false);
}
@Override
protected boolean processInteract(EntityPlayer player, EnumHand hand){
// In this case, the delegate method determines whether super is called.
// Rather handily, we can make use of Java's short-circuiting method of evaluating OR statements.
return this.interactDelegate(player, hand) || super.processInteract(player, hand);
}
@Override
public void writeEntityToNBT(NBTTagCompound nbttagcompound){
super.writeEntityToNBT(nbttagcompound);
this.writeNBTDelegate(nbttagcompound);
}
@Override
public void readEntityFromNBT(NBTTagCompound nbttagcompound){
super.readEntityFromNBT(nbttagcompound);
this.readNBTDelegate(nbttagcompound);
}
// Recommended overrides
@Override protected int getExperiencePoints(EntityPlayer player){ return 0; }
@Override protected boolean canDropLoot(){ return false; }
@Override protected Item getDropItem(){ return null; }
@Override protected ResourceLocation getLootTable(){ return null; }
@Override public boolean canPickUpLoot(){ return false; }
// This vanilla method has nothing to do with the custom despawn() method.
@Override protected boolean canDespawn(){
return getCaster() == null && getOwnerId() == null;
}
@Override
public boolean getCanSpawnHere(){
return this.world.getDifficulty() != EnumDifficulty.PEACEFUL;
}
@Override
public boolean canAttackClass(Class<? extends EntityLivingBase> entityType){
// Returns true unless the given entity type is a flying entity.
return !EntityFlying.class.isAssignableFrom(entityType);
}
@Override
public ITextComponent getDisplayName(){
if(getCaster() != null){
return new TextComponentTranslation(NAMEPLATE_TRANSLATION_KEY, getCaster().getName(),
new TextComponentTranslation("entity." + this.getEntityString() + ".name"));
}else{
return super.getDisplayName();
}
}
@Override
public boolean hasCustomName(){
// If this returns true, the renderer will show the nameplate when looking directly at the entity
return Wizardry.settings.summonedCreatureNames && getCaster() != null;
}
}
@@ -47,6 +47,7 @@ import net.minecraftforge.common.util.FakePlayer;
import net.minecraftforge.event.world.BlockEvent;
import net.minecraftforge.fml.common.Mod;
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
import net.minecraftforge.fml.common.registry.ForgeRegistries;
import net.minecraftforge.fml.common.registry.IEntityAdditionalSpawnData;
import net.minecraftforge.fml.relauncher.Side;
import net.minecraftforge.fml.relauncher.SideOnly;
@@ -67,7 +68,7 @@ public class EntityWizard extends EntityCreature implements INpc, IMerchant, ISp
protected Predicate<Entity> targetSelector;
/** The wizard's trades. */
private MerchantRecipeList trades;
public MerchantRecipeList trades;
/** The wizard's current customer. */
@Nullable
private EntityPlayer customer;
@@ -81,11 +82,12 @@ public class EntityWizard extends EntityCreature implements INpc, IMerchant, ISp
private static final DataParameter<Integer> HEAL_COOLDOWN = EntityDataManager.createKey(EntityWizard.class, DataSerializers.VARINT);
/** Data parameter for the wizard's element. */
private static final DataParameter<Integer> ELEMENT = EntityDataManager.createKey(EntityWizard.class, DataSerializers.VARINT);
/** Data parameters for the wizard's current continuous spell. */
private static final DataParameter<String> CONTINUOUS_SPELL = EntityDataManager.createKey(EntityWizard.class, DataSerializers.STRING);
private static final DataParameter<Integer> SPELL_COUNTER = EntityDataManager.createKey(EntityWizard.class, DataSerializers.VARINT);
// Field implementations
private List<Spell> spells = new ArrayList<Spell>(4);
private Spell continuousSpell;
private int spellCounter;
/** A set of the positions of the blocks that are part of this wizard's tower. */
private Set<BlockPos> towerBlocks;
@@ -102,6 +104,8 @@ public class EntityWizard extends EntityCreature implements INpc, IMerchant, ISp
super.entityInit();
this.dataManager.register(HEAL_COOLDOWN, -1);
this.dataManager.register(ELEMENT, 0);
this.dataManager.register(CONTINUOUS_SPELL, "ebwizardry:none");
this.dataManager.register(SPELL_COUNTER, 0);
}
@Override
@@ -126,8 +130,12 @@ public class EntityWizard extends EntityCreature implements INpc, IMerchant, ISp
if(entity != null && !entity.isInvisible()
&& AllyDesignationSystem.isValidTarget(EntityWizard.this, entity)){
// ... and is a mob, a summoned creature ...
if((entity instanceof IMob || entity instanceof ISummonedCreature
// ... and is a non summoned creature mob ...
if((entity instanceof IMob && !(entity instanceof ISummonedCreature)
// or is a summoned creature with a mob owner or an owner who has attacked the wizard ...
|| entity instanceof ISummonedCreature && (((ISummonedCreature)entity).getOwner() instanceof IMob || ((ISummonedCreature)entity).getOwner() == this.getRevengeTarget() || ((ISummonedCreature) entity).getOwner() == this.getAttackTarget())
// ... or in the whitelist ...
|| Arrays.asList(Wizardry.settings.summonedCreatureTargetsWhitelist)
.contains(EntityList.getKey(entity.getClass())))
@@ -182,23 +190,23 @@ public class EntityWizard extends EntityCreature implements INpc, IMerchant, ISp
}
@Override
public void setContinuousSpell(Spell spell){
this.continuousSpell = spell;
public void setContinuousSpell(Spell spell) {
this.dataManager.set(CONTINUOUS_SPELL, spell.getRegistryName().toString());
}
@Override
public Spell getContinuousSpell(){
return this.continuousSpell;
public Spell getContinuousSpell() {
return Spell.get(this.dataManager.get(CONTINUOUS_SPELL));
}
@Override
public void setSpellCounter(int count){
spellCounter = count;
public void setSpellCounter(int count) {
this.dataManager.set(SPELL_COUNTER, count);
}
@Override
public int getSpellCounter(){
return spellCounter;
public int getSpellCounter() {
return this.dataManager.get(SPELL_COUNTER);
}
@Override
@@ -634,10 +642,9 @@ public class EntityWizard extends EntityCreature implements INpc, IMerchant, ISp
if(this.getElement() != Element.MAGIC && rand.nextInt(4) > 0 && !specialismSpells.isEmpty()){
// This means it is more likely for spell books sold to be of the same element as the wizard if the
// wizard has an element.
return new ItemStack(WizardryItems.spell_book, 1,
specialismSpells.get(rand.nextInt(specialismSpells.size())).metadata());
return getBookStackForSpell(specialismSpells.get(rand.nextInt(specialismSpells.size())));
}else{
return new ItemStack(WizardryItems.spell_book, 1, spells.get(rand.nextInt(spells.size())).metadata());
return getBookStackForSpell(spells.get(rand.nextInt(spells.size())));
}
}else{
if(this.getElement() != Element.MAGIC && rand.nextInt(4) > 0){
@@ -656,10 +663,9 @@ public class EntityWizard extends EntityCreature implements INpc, IMerchant, ISp
if(this.getElement() != Element.MAGIC && rand.nextInt(4) > 0 && !specialismSpells.isEmpty()){
// This means it is more likely for spell books sold to be of the same element as the wizard if the
// wizard has an element.
return new ItemStack(WizardryItems.spell_book, 1,
specialismSpells.get(rand.nextInt(specialismSpells.size())).metadata());
return getBookStackForSpell(specialismSpells.get(rand.nextInt(specialismSpells.size())));
}else{
return new ItemStack(WizardryItems.spell_book, 1, spells.get(rand.nextInt(spells.size())).metadata());
return getBookStackForSpell(spells.get(rand.nextInt(spells.size())));
}
}else if(randomiser < 6){
if(this.getElement() != Element.MAGIC && rand.nextInt(4) > 0){
@@ -693,10 +699,9 @@ public class EntityWizard extends EntityCreature implements INpc, IMerchant, ISp
if(this.getElement() != Element.MAGIC && rand.nextInt(4) > 0 && !specialismSpells.isEmpty()){
// This means it is more likely for spell books sold to be of the same element as the wizard if the
// wizard has an element.
return new ItemStack(WizardryItems.spell_book, 1,
specialismSpells.get(rand.nextInt(specialismSpells.size())).metadata());
return getBookStackForSpell(specialismSpells.get(rand.nextInt(specialismSpells.size())));
}else{
return new ItemStack(WizardryItems.spell_book, 1, spells.get(rand.nextInt(spells.size())).metadata());
return getBookStackForSpell(spells.get(rand.nextInt(spells.size())));
}
}else if(randomiser < 6){
if(this.getElement() != Element.MAGIC && rand.nextInt(4) > 0){
@@ -721,8 +726,7 @@ public class EntityWizard extends EntityCreature implements INpc, IMerchant, ISp
if(randomiser < 5 && this.getElement() != Element.MAGIC && !specialismSpells.isEmpty()){
// Master spells can only be sold by a specialist in that element.
return new ItemStack(WizardryItems.spell_book, 1,
specialismSpells.get(rand.nextInt(specialismSpells.size())).metadata());
return getBookStackForSpell(specialismSpells.get(rand.nextInt(specialismSpells.size())));
}else if(randomiser < 6){
if(this.getElement() != Element.MAGIC && rand.nextInt(4) > 0){
@@ -798,7 +802,6 @@ public class EntityWizard extends EntityCreature implements INpc, IMerchant, ISp
Tier maxTier = Tier.NOVICE;
List<Spell> npcSpells = Spell.getSpells(s -> s.canBeCastBy(wizard, false));
npcSpells.removeIf(s -> !s.applicableForItem(WizardryItems.spell_book));
for(int i = 0; i < n; i++){
@@ -846,6 +849,22 @@ public class EntityWizard extends EntityCreature implements INpc, IMerchant, ISp
return maxTier;
}
/**
* Helper method to handle addon spell books.
* @param spell the spell to look up.
* @return Checks all items of the spell's namespace (modid) if they are applicable for the spell and returns the first match, or falls back to WizardryItems.spell_book if no match was found.
*/
public static ItemStack getBookStackForSpell(Spell spell) {
String modid = spell.getRegistryName().getNamespace();
if (modid.equals(Wizardry.MODID)) {
return new ItemStack(WizardryItems.spell_book, 1, spell.metadata());
}
Optional<Item> firstMatch = ForgeRegistries.ITEMS.getValuesCollection().stream().filter(v -> v instanceof ItemSpellBook && spell.applicableForItem(v)
&& v.getRegistryName().getNamespace().equals(modid)).findFirst();
return firstMatch.map(item -> new ItemStack(item, 1, spell.metadata())).orElseGet(() -> new ItemStack(WizardryItems.spell_book, 1, spell.metadata()));
}
@Override
public void writeSpawnData(ByteBuf data){
data.writeInt(textureIndex);
@@ -0,0 +1,34 @@
package electroblob.wizardry.entity.projectile;
import net.minecraft.entity.EntityLivingBase;
import net.minecraft.entity.projectile.EntityArrow;
import net.minecraft.item.ItemStack;
import net.minecraft.world.World;
public class EntityConjuredArrow extends EntityArrow {
public EntityConjuredArrow(World worldIn) {
super(worldIn);
}
public EntityConjuredArrow(World worldIn, double x, double y, double z) {
super(worldIn, x, y, z);
}
public EntityConjuredArrow(World worldIn, EntityLivingBase shooter) {
super(worldIn, shooter);
}
@Override
public void onUpdate() {
super.onUpdate();
if (this.timeInGround > 400) {
this.setDead();
}
}
@Override
protected ItemStack getArrowStack() {
return ItemStack.EMPTY;
}
}
@@ -1,18 +1,18 @@
package electroblob.wizardry.entity.projectile;
import electroblob.wizardry.packet.PacketBombExplosion;
import electroblob.wizardry.packet.WizardryPacketHandler;
import electroblob.wizardry.registry.Spells;
import electroblob.wizardry.registry.WizardrySounds;
import electroblob.wizardry.spell.Spell;
import electroblob.wizardry.util.EntityUtils;
import electroblob.wizardry.util.MagicDamage;
import electroblob.wizardry.util.MagicDamage.DamageType;
import electroblob.wizardry.util.ParticleBuilder;
import electroblob.wizardry.util.ParticleBuilder.Type;
import net.minecraft.entity.Entity;
import net.minecraft.entity.EntityLivingBase;
import net.minecraft.util.EnumParticleTypes;
import net.minecraft.util.math.RayTraceResult;
import net.minecraft.world.World;
import net.minecraftforge.fml.common.network.NetworkRegistry;
import java.util.List;
@@ -29,41 +29,28 @@ public class EntityFirebomb extends EntityBomb {
@Override
protected void onImpact(RayTraceResult rayTrace){
Entity entityHit = rayTrace.entityHit;
if(entityHit != null){
// This is if the firebomb gets a direct hit
float damage = Spells.firebomb.getProperty(Spell.DIRECT_DAMAGE).floatValue() * damageMultiplier;
entityHit.attackEntityFrom(
MagicDamage.causeIndirectMagicDamage(this, this.getThrower(), DamageType.FIRE).setProjectile(),
damage);
if(!MagicDamage.isEntityImmune(DamageType.FIRE, entityHit))
entityHit.setFire(Spells.firebomb.getProperty(Spell.BURN_DURATION).intValue());
}
// Particle effect
if(world.isRemote){
ParticleBuilder.create(Type.FLASH).pos(this.getPositionVector()).scale(5 * blastMultiplier).clr(1, 0.6f, 0)
.spawn(world);
for(int i = 0; i < 60 * blastMultiplier; i++){
ParticleBuilder.create(Type.MAGIC_FIRE, rand, posX, posY, posZ, 2*blastMultiplier, false)
.time(10 + rand.nextInt(4)).scale(2 + rand.nextFloat()).spawn(world);
ParticleBuilder.create(Type.DARK_MAGIC, rand, posX, posY, posZ, 2*blastMultiplier, false)
.clr(1.0f, 0.2f + rand.nextFloat() * 0.4f, 0.0f).spawn(world);
}
this.world.spawnParticle(EnumParticleTypes.EXPLOSION_LARGE, this.posX, this.posY, this.posZ, 0, 0, 0);
}
if(!this.world.isRemote){
Entity entityHit = rayTrace.entityHit;
if(entityHit != null){
// This is if the firebomb gets a direct hit
float damage = Spells.firebomb.getProperty(Spell.DIRECT_DAMAGE).floatValue() * damageMultiplier;
entityHit.attackEntityFrom(
MagicDamage.causeIndirectMagicDamage(this, this.getThrower(), DamageType.FIRE).setProjectile(),
damage);
if(!MagicDamage.isEntityImmune(DamageType.FIRE, entityHit))
entityHit.setFire(Spells.firebomb.getProperty(Spell.BURN_DURATION).intValue());
}
// Notify clients to play the explosion effect
WizardryPacketHandler.net.sendToAllAround(
new PacketBombExplosion.Message(PacketBombExplosion.FIREBOMB, posX, posY, posZ, blastMultiplier),
new NetworkRegistry.TargetPoint(world.provider.getDimension(), posX, posY, posZ, 64));
this.playSound(WizardrySounds.ENTITY_FIREBOMB_SMASH, 1.5F, rand.nextFloat() * 0.4F + 0.6F);
this.playSound(WizardrySounds.ENTITY_FIREBOMB_FIRE, 1, 1);
@@ -82,9 +69,9 @@ public class EntityFirebomb extends EntityBomb {
target.setFire(Spells.firebomb.getProperty(Spell.BURN_DURATION).intValue());
}
}
this.setDead();
}
this.setDead();
}
}
@@ -1,20 +1,20 @@
package electroblob.wizardry.entity.projectile;
import electroblob.wizardry.packet.PacketBombExplosion;
import electroblob.wizardry.packet.WizardryPacketHandler;
import electroblob.wizardry.registry.Spells;
import electroblob.wizardry.registry.WizardrySounds;
import electroblob.wizardry.spell.Spell;
import electroblob.wizardry.util.EntityUtils;
import electroblob.wizardry.util.MagicDamage;
import electroblob.wizardry.util.MagicDamage.DamageType;
import electroblob.wizardry.util.ParticleBuilder;
import electroblob.wizardry.util.ParticleBuilder.Type;
import net.minecraft.entity.Entity;
import net.minecraft.entity.EntityLivingBase;
import net.minecraft.init.MobEffects;
import net.minecraft.potion.PotionEffect;
import net.minecraft.util.EnumParticleTypes;
import net.minecraft.util.math.RayTraceResult;
import net.minecraft.world.World;
import net.minecraftforge.fml.common.network.NetworkRegistry;
import java.util.List;
@@ -31,44 +31,30 @@ public class EntityPoisonBomb extends EntityBomb {
@Override
protected void onImpact(RayTraceResult rayTrace){
Entity entityHit = rayTrace.entityHit;
if(entityHit != null){
// This is if the poison bomb gets a direct hit
float damage = Spells.poison_bomb.getProperty(Spell.DIRECT_DAMAGE).floatValue() * damageMultiplier;
entityHit.attackEntityFrom(
MagicDamage.causeIndirectMagicDamage(this, this.getThrower(), DamageType.POISON).setProjectile(),
damage);
if(entityHit instanceof EntityLivingBase && !MagicDamage.isEntityImmune(DamageType.POISON, entityHit))
((EntityLivingBase)entityHit).addPotionEffect(new PotionEffect(MobEffects.POISON,
Spells.poison_bomb.getProperty(Spell.DIRECT_EFFECT_DURATION).intValue(),
Spells.poison_bomb.getProperty(Spell.DIRECT_EFFECT_STRENGTH).intValue()));
}
// Particle effect
if(world.isRemote){
ParticleBuilder.create(Type.FLASH).pos(this.getPositionVector()).scale(5 * blastMultiplier)
.clr(0.2f + rand.nextFloat() * 0.3f, 0.6f, 0.0f).spawn(world);
for(int i = 0; i < 60 * blastMultiplier; i++){
ParticleBuilder.create(Type.SPARKLE, rand, posX, posY, posZ, 2*blastMultiplier, false).time(35)
.scale(2).clr(0.2f + rand.nextFloat() * 0.3f, 0.6f, 0.0f).spawn(world);
ParticleBuilder.create(Type.DARK_MAGIC, rand, posX, posY, posZ, 2*blastMultiplier, false)
.clr(0.2f + rand.nextFloat() * 0.2f, 0.8f, 0.0f).spawn(world);
}
// Spawning this after the other particles fixes the rendering colour bug. It's a bit of a cheat, but it
// works pretty well.
this.world.spawnParticle(EnumParticleTypes.EXPLOSION_LARGE, this.posX, this.posY, this.posZ, 0, 0, 0);
}
if(!this.world.isRemote){
Entity entityHit = rayTrace.entityHit;
if(entityHit != null){
// This is if the poison bomb gets a direct hit
float damage = Spells.poison_bomb.getProperty(Spell.DIRECT_DAMAGE).floatValue() * damageMultiplier;
entityHit.attackEntityFrom(
MagicDamage.causeIndirectMagicDamage(this, this.getThrower(), DamageType.POISON).setProjectile(),
damage);
if(entityHit instanceof EntityLivingBase && !MagicDamage.isEntityImmune(DamageType.POISON, entityHit))
((EntityLivingBase)entityHit).addPotionEffect(new PotionEffect(MobEffects.POISON,
Spells.poison_bomb.getProperty(Spell.DIRECT_EFFECT_DURATION).intValue(),
Spells.poison_bomb.getProperty(Spell.DIRECT_EFFECT_STRENGTH).intValue()));
}
// Notify clients to play the explosion effect
WizardryPacketHandler.net.sendToAllAround(
new PacketBombExplosion.Message(PacketBombExplosion.POISON_BOMB, posX, posY, posZ, blastMultiplier),
new NetworkRegistry.TargetPoint(world.provider.getDimension(), posX, posY, posZ, 64));
this.playSound(WizardrySounds.ENTITY_POISON_BOMB_SMASH, 1.5F, rand.nextFloat() * 0.4F + 0.6F);
this.playSound(WizardrySounds.ENTITY_POISON_BOMB_POISON, 1.2F, 1.0f);
@@ -88,8 +74,8 @@ public class EntityPoisonBomb extends EntityBomb {
Spells.poison_bomb.getProperty(Spell.SPLASH_EFFECT_STRENGTH).intValue()));
}
}
this.setDead();
}
this.setDead();
}
}
@@ -1,17 +1,17 @@
package electroblob.wizardry.entity.projectile;
import electroblob.wizardry.packet.PacketBombExplosion;
import electroblob.wizardry.packet.WizardryPacketHandler;
import electroblob.wizardry.registry.Spells;
import electroblob.wizardry.registry.WizardrySounds;
import electroblob.wizardry.spell.Spell;
import electroblob.wizardry.util.EntityUtils;
import electroblob.wizardry.util.ParticleBuilder;
import electroblob.wizardry.util.ParticleBuilder.Type;
import net.minecraft.entity.EntityLivingBase;
import net.minecraft.init.MobEffects;
import net.minecraft.potion.PotionEffect;
import net.minecraft.util.EnumParticleTypes;
import net.minecraft.util.math.RayTraceResult;
import net.minecraft.world.World;
import net.minecraftforge.fml.common.network.NetworkRegistry;
import java.util.List;
@@ -29,27 +29,13 @@ public class EntitySmokeBomb extends EntityBomb {
@Override
protected void onImpact(RayTraceResult rayTrace){
// Particle effect
if(world.isRemote){
ParticleBuilder.create(Type.FLASH).pos(this.getPositionVector()).scale(5 * blastMultiplier).clr(0, 0, 0).spawn(world);
this.world.spawnParticle(EnumParticleTypes.EXPLOSION_LARGE, this.posX, this.posY, this.posZ, 0, 0, 0);
for(int i = 0; i < 60 * blastMultiplier; i++){
float brightness = rand.nextFloat() * 0.1f + 0.1f;
ParticleBuilder.create(Type.CLOUD, rand, posX, posY, posZ, 2*blastMultiplier, false)
.clr(brightness, brightness, brightness).time(80 + this.rand.nextInt(12)).shaded(true).spawn(world);
brightness = rand.nextFloat() * 0.3f;
ParticleBuilder.create(Type.DARK_MAGIC, rand, posX, posY, posZ, 2*blastMultiplier, false)
.clr(brightness, brightness, brightness).spawn(world);
}
}
if(!this.world.isRemote){
// Notify clients to play the explosion effect
WizardryPacketHandler.net.sendToAllAround(
new PacketBombExplosion.Message(PacketBombExplosion.SMOKE_BOMB, posX, posY, posZ, blastMultiplier),
new NetworkRegistry.TargetPoint(world.provider.getDimension(), posX, posY, posZ, 64));
this.playSound(WizardrySounds.ENTITY_SMOKE_BOMB_SMASH, 1.5F, rand.nextFloat() * 0.4F + 0.6F);
this.playSound(WizardrySounds.ENTITY_SMOKE_BOMB_SMOKE, 1.2F, 1.0f);
@@ -65,8 +51,8 @@ public class EntitySmokeBomb extends EntityBomb {
target.addPotionEffect(new PotionEffect(MobEffects.BLINDNESS, duration, 0));
}
}
this.setDead();
}
this.setDead();
}
}
@@ -1,5 +1,7 @@
package electroblob.wizardry.entity.projectile;
import electroblob.wizardry.packet.PacketBombExplosion;
import electroblob.wizardry.packet.WizardryPacketHandler;
import electroblob.wizardry.registry.Spells;
import electroblob.wizardry.registry.WizardrySounds;
import electroblob.wizardry.spell.Spell;
@@ -13,7 +15,9 @@ import net.minecraft.entity.EntityLivingBase;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.util.math.RayTraceResult;
import net.minecraft.world.World;
import net.minecraftforge.fml.common.network.NetworkRegistry;
import java.util.ArrayList;
import java.util.List;
public class EntitySparkBomb extends EntityBomb {
@@ -31,47 +35,37 @@ public class EntitySparkBomb extends EntityBomb {
@Override
protected void onImpact(RayTraceResult rayTrace){
this.playSound(WizardrySounds.ENTITY_SPARK_BOMB_HIT_BLOCK, 0.5f, 0.5f);
Entity entityHit = rayTrace.entityHit;
if(!this.world.isRemote){
if(entityHit != null){
// This is if the spark bomb gets a direct hit
float damage = Spells.spark_bomb.getProperty(Spell.DIRECT_DAMAGE).floatValue() * damageMultiplier;
Entity entityHit = rayTrace.entityHit;
this.playSound(WizardrySounds.ENTITY_SPARK_BOMB_HIT, 1.0F, 1.2F / (this.rand.nextFloat() * 0.2F + 0.9F));
if(entityHit != null){
// This is if the spark bomb gets a direct hit
float damage = Spells.spark_bomb.getProperty(Spell.DIRECT_DAMAGE).floatValue() * damageMultiplier;
entityHit.attackEntityFrom(
MagicDamage.causeIndirectMagicDamage(this, this.getThrower(), DamageType.SHOCK).setProjectile(),
damage);
this.playSound(WizardrySounds.ENTITY_SPARK_BOMB_HIT, 1.0F, 1.2F / (this.rand.nextFloat() * 0.2F + 0.9F));
}
entityHit.attackEntityFrom(
MagicDamage.causeIndirectMagicDamage(this, this.getThrower(), DamageType.SHOCK).setProjectile(),
damage);
}
// Particle effect
if(world.isRemote){
ParticleBuilder.spawnShockParticles(world, posX, posY + height/2, posZ);
}
double seekerRange = Spells.spark_bomb.getProperty(Spell.EFFECT_RADIUS).doubleValue() * blastMultiplier;
double seekerRange = Spells.spark_bomb.getProperty(Spell.EFFECT_RADIUS).doubleValue() * blastMultiplier;
List<EntityLivingBase> targets = EntityUtils.getLivingWithinRadius(seekerRange, this.posX, this.posY,
this.posZ, this.world);
List<EntityLivingBase> targets = EntityUtils.getLivingWithinRadius(seekerRange, this.posX, this.posY,
this.posZ, this.world);
List<Integer> secondaryTargetIDs = new ArrayList<>();
for(int i = 0; i < Math.min(targets.size(), Spells.spark_bomb.getProperty(SECONDARY_MAX_TARGETS).intValue()); i++){
for(int i = 0; i < Math.min(targets.size(), Spells.spark_bomb.getProperty(SECONDARY_MAX_TARGETS).intValue()); i++){
boolean flag = targets.get(i) != entityHit && targets.get(i) != this.getThrower()
&& !(targets.get(i) instanceof EntityPlayer
&& ((EntityPlayer)targets.get(i)).isCreative());
boolean flag = targets.get(i) != entityHit && targets.get(i) != this.getThrower()
&& !(targets.get(i) instanceof EntityPlayer
&& ((EntityPlayer)targets.get(i)).isCreative());
// Detects (client side) if target is the thrower, to stop particles being spawned around them.
//if(flag && world.isRemote && targets.get(i).getEntityId() == this.playerID) flag = false;
if(flag){
EntityLivingBase target = targets.get(i);
if(!this.world.isRemote){
if(flag){
EntityLivingBase target = targets.get(i);
target.playSound(WizardrySounds.ENTITY_SPARK_BOMB_CHAIN, 1.0F, rand.nextFloat() * 0.4F + 1.5F);
@@ -79,11 +73,18 @@ public class EntitySparkBomb extends EntityBomb {
MagicDamage.causeIndirectMagicDamage(this, this.getThrower(), DamageType.SHOCK),
Spells.spark_bomb.getProperty(Spell.SPLASH_DAMAGE).floatValue() * damageMultiplier);
}else{
ParticleBuilder.create(Type.LIGHTNING).pos(this.getPositionVector()).target(target).spawn(world);
ParticleBuilder.spawnShockParticles(world, target.posX, target.posY + target.height/2, target.posZ);
secondaryTargetIDs.add(target.getEntityId());
}
}
// Notify clients to play the explosion effect, including lightning arcs to secondary targets
int[] idArray = secondaryTargetIDs.stream().mapToInt(Integer::intValue).toArray();
WizardryPacketHandler.net.sendToAllAround(
new PacketBombExplosion.Message(PacketBombExplosion.SPARK_BOMB, posX, posY + height / 2, posZ,
blastMultiplier, idArray),
new NetworkRegistry.TargetPoint(world.provider.getDimension(), posX, posY, posZ, 64));
this.playSound(WizardrySounds.ENTITY_SPARK_BOMB_HIT_BLOCK, 0.5f, 0.5f);
}
this.setDead();
@@ -0,0 +1,45 @@
package electroblob.wizardry.event;
import electroblob.wizardry.item.ItemArtefact;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraftforge.common.MinecraftForge;
import net.minecraftforge.event.entity.player.PlayerEvent;
import net.minecraftforge.fml.common.eventhandler.Cancelable;
import net.minecraftforge.fml.common.eventhandler.Event;
/**
* ArtefactCheckEvent is fired when a check happens for an ItemArtefact using {@link electroblob.wizardry.item.ItemArtefact#isArtefactActive(net.minecraft.entity.player.EntityPlayer, net.minecraft.item.Item)}
* <i>Fired on both sides.</i><br>
* <br>
* This event is {@link Cancelable}. <br>
* <br>
* This event has a result. {@link HasResult}. Set the result to Result.ALLOW to consider an artefact "active"<br>
* <br>
* This event is fired on the {@link MinecraftForge#EVENT_BUS}.
*
* @author WinDanesz
* @since Wizardry 4.3.10
*/
@Cancelable
@Event.HasResult
public class ArtefactCheckEvent extends PlayerEvent {
ItemArtefact artefact;
EntityPlayer player;
public ArtefactCheckEvent(EntityPlayer player, ItemArtefact artefact) {
super(player);
this.player = player;
this.artefact = artefact;
setResult(Result.DENY);
}
public ItemArtefact getArtefact() {
return artefact;
}
public EntityPlayer getPlayer() {
return player;
}
}
@@ -0,0 +1,56 @@
package electroblob.wizardry.event;
import electroblob.wizardry.constants.Element;
import electroblob.wizardry.tileentity.TileEntityImbuementAltar;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.item.ItemStack;
import net.minecraft.world.World;
import net.minecraftforge.common.MinecraftForge;
import net.minecraftforge.fml.common.eventhandler.Cancelable;
import net.minecraftforge.fml.common.eventhandler.Event;
/**
* ImbuementActivateEvent is fired when {@link TileEntityImbuementAltar#getImbuementResult(net.minecraft.item.ItemStack, electroblob.wizardry.constants.Element[],
* boolean, net.minecraft.world.World, net.minecraft.entity.player.EntityPlayer)} is called to allow adding in imbuement "recipes" dynamically, based on the
* contents of the Imbuement altar's receptacles and the item placed on the altar.
*
* <i>Note that this event is only fired on the server side.</i><br>
* <br>
* To alter the result of the imbuement process, change the {@link electroblob.wizardry.event.ImbuementActivateEvent#result} result of the process.<br>
* <br>
* This event is {@link Cancelable}. If this event is canceled, no further processing takes place.<br>
* <br>
* This event does not have a result. {@link Event.HasResult}<br>
* <br>
* This event is fired on the {@link MinecraftForge#EVENT_BUS}.
*
* @author WinDanesz
* @since Wizardry 4.3.5
*/
@Cancelable
public class ImbuementActivateEvent extends Event {
/** The item stack being imbued */
public ItemStack input;
/** The elements of the four receptacles */
public Element[] receptacleElements;
/** A reference to the current world object (may be null if {@code fullLootGen} is false) */
public World world;
/** The player that last interacted with the imbuement altar, or null if there isn't one (or if this is being queried for other reasons, e.g. JEI) */
public EntityPlayer lastUser;
/** The resulting item stack of the imbuement process */
public ItemStack result;
public ImbuementActivateEvent(ItemStack input, Element[] receptacleElements, World world, EntityPlayer lastUser, ItemStack result) {
super();
this.input = input;
this.receptacleElements = receptacleElements;
this.world = world;
this.lastUser = lastUser;
this.result = result;
}
}
@@ -0,0 +1,37 @@
package electroblob.wizardry.event;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraftforge.common.MinecraftForge;
import net.minecraftforge.event.entity.player.PlayerEvent;
import net.minecraftforge.fml.common.eventhandler.Cancelable;
/**
* ResurrectionEvent is fired when a player is about to be resurrected, after all other checks have been performed.
* <i>Note that this event is only fired on the server side.</i><br>
* <br>
* This event is {@link Cancelable}. If this event is canceled, the player is not resurrected.<br>
* <br>
* This event does not have a result. {@link HasResult}<br>
* <br>
* This event is fired on the {@link MinecraftForge#EVENT_BUS}.
*
* @author Electroblob
* @since Wizardry 2.1
*/
@Cancelable
public class ResurrectionEvent extends PlayerEvent {
private final EntityPlayer caster;
public ResurrectionEvent(EntityPlayer player, EntityPlayer caster){
super(player);
this.caster = caster;
}
/** Returns the player that cast the resurrection spell. If the player resurrected themselves, this will be the
* same as {@link ResurrectionEvent#getEntityPlayer()}. */
public EntityPlayer getCaster(){
return caster;
}
}
@@ -46,6 +46,9 @@ public final class WizardryBaublesIntegration {
ARTEFACT_TYPE_MAP.put(ItemArtefact.Type.RING, BaubleType.RING);
ARTEFACT_TYPE_MAP.put(ItemArtefact.Type.AMULET, BaubleType.AMULET);
ARTEFACT_TYPE_MAP.put(ItemArtefact.Type.CHARM, BaubleType.CHARM);
ARTEFACT_TYPE_MAP.put(ItemArtefact.Type.BELT, BaubleType.BELT);
ARTEFACT_TYPE_MAP.put(ItemArtefact.Type.BODY, BaubleType.BODY);
ARTEFACT_TYPE_MAP.put(ItemArtefact.Type.HEAD, BaubleType.HEAD);
}
public static boolean enabled(){
@@ -452,6 +452,24 @@ public class ContainerArcaneWorkbench extends Container implements ISpellSortabl
}
}
/**
* Called (via {@link electroblob.wizardry.packet.PacketControlInput PacketControlInput}) when the clear button in
* the arcane workbench GUI is pressed.
*/
// As of 2.1, for the sake of events and neatness of code, this was moved here from TileEntityArcaneWorkbench.
// As of 4.2, the spell binding/charging/upgrading code was delegated (via IWorkbenchItem) to the items themselves.
public void onClearButtonPressed(EntityPlayer player){
Slot centre = this.getSlot(CENTRE_SLOT);
if(centre.getStack().getItem() instanceof IWorkbenchItem){ // Should always be true, but no harm in checking.
Slot[] spellBooks = this.inventorySlots.subList(0, 8).toArray(new Slot[8]);
((IWorkbenchItem) centre.getStack().getItem()).onClearButtonPressed(player, centre, this.getSlot(CRYSTAL_SLOT), this.getSlot(UPGRADE_SLOT), spellBooks);
}
}
/** Scrolls to the given row number. */
public void scrollTo(int row){
this.scroll = row;
@@ -55,6 +55,26 @@ public interface IWorkbenchItem {
*/
boolean onApplyButtonPressed(EntityPlayer player, Slot centre, Slot crystals, Slot upgrade, Slot[] spellBooks);
/**
* Called when this item is in the central slot of an arcane workbench and the apply clear is pressed. Items must
* implement this method to define what happens when the apply button is pressed.
* @param player The player that pressed the apply button.
* @param centre The central slot in the arcane workbench. This slot will always contain a stack of the implementing
* item, or in other words, <i>it is guaranteed that</i> {@code this == centre.getStack().getItem()}.
* @param crystals The magic crystal slot of the arcane workbench.
* @param upgrade The upgrade slot of the arcane workbench.
* @param spellBooks An array of the <i>active</i> (visible) spell book slots in the arcane workbench. The length of
* the array will be equal to the value returned by {@link IWorkbenchItem#getSpellSlotCount(ItemStack)}.
* */
default void onClearButtonPressed(EntityPlayer player, Slot centre, Slot crystals, Slot upgrade, Slot[] spellBooks){};
/**
* Must be overridden in the item class to make the clear button in the Arcane Workbench clickable.
* */
default boolean isClearable() {
return false;
}
/**
* Returns whether the tooltip (dark grey box) should be drawn when this item is in an arcane workbench. Only
* called client-side.
@@ -25,7 +25,7 @@ public class ItemArcaneTome extends Item {
@Override
public void getSubItems(CreativeTabs tab, NonNullList<ItemStack> list){
if(tab == WizardryTabs.WIZARDRY){ // Don't use isInCreativeTab here.
if(tab == WizardryTabs.WIZARDRY || tab == CreativeTabs.SEARCH){ // Don't use isInCreativeTab here.
for(int i = 1; i < Tier.values().length; i++){
list.add(new ItemStack(this, 1, i));
}
@@ -10,6 +10,7 @@ import electroblob.wizardry.entity.living.ISummonedCreature;
import electroblob.wizardry.entity.projectile.EntityDart;
import electroblob.wizardry.entity.projectile.EntityForceOrb;
import electroblob.wizardry.entity.projectile.EntityIceShard;
import electroblob.wizardry.event.ArtefactCheckEvent;
import electroblob.wizardry.event.SpellCastEvent;
import electroblob.wizardry.event.SpellCastEvent.Source;
import electroblob.wizardry.integration.DamageSafetyChecker;
@@ -37,6 +38,7 @@ import net.minecraft.util.text.TextFormatting;
import net.minecraft.world.World;
import net.minecraft.world.biome.Biome;
import net.minecraftforge.common.BiomeDictionary;
import net.minecraftforge.common.MinecraftForge;
import net.minecraftforge.common.capabilities.ICapabilityProvider;
import net.minecraftforge.event.entity.living.LivingDeathEvent;
import net.minecraftforge.event.entity.living.LivingEvent;
@@ -88,7 +90,10 @@ public class ItemArtefact extends Item {
/** An artefact that improves attacking spells. Two of these can be active at any one time. */ RING(2),
/** An artefact that improves defensive spells. One of these can be active at any one time. */ AMULET(1),
/** An artefact that improves utility spells. One of these can be active at any one time. */ CHARM(1);
/** An artefact that improves utility spells. One of these can be active at any one time. */ CHARM(1),
/** Added for add-on artefacts. */ BELT(1),
/** Added for add-on artefacts. */ BODY(1),
/** Added for add-on artefacts. */ HEAD(1);
public final int maxAtOnce;
@@ -123,6 +128,8 @@ public class ItemArtefact extends Item {
this.enabled = enabled;
}
public boolean isEnabled() { return enabled; }
@Override
public EnumRarity getRarity(ItemStack stack){
return rarity;
@@ -176,6 +183,13 @@ public class ItemArtefact extends Item {
if(!((ItemArtefact)artefact).enabled) return false; // Disabled in the config
ArtefactCheckEvent event = new ArtefactCheckEvent(player, (ItemArtefact) artefact);
if(MinecraftForge.EVENT_BUS.post(event)) return false;
if (event.getResult() == Event.Result.ALLOW) {
return true;
}
if(WizardryBaublesIntegration.enabled()){
return WizardryBaublesIntegration.isBaubleEquipped(player, artefact);
}else{
@@ -344,6 +358,8 @@ public class ItemArtefact extends Item {
findMatchingWandAndExecute(player, Spells.shield, wand -> {
if(wand.getItem() instanceof ItemScroll) return; // Ignore scrolls since they shouldn't work
List<Entity> projectiles = EntityUtils.getEntitiesWithinRadius(5, player.posX, player.posY, player.posZ, world, Entity.class);
projectiles.removeIf(e -> !(e instanceof IProjectile));
Vec3d look = player.getLookVec();
@@ -765,6 +781,7 @@ public class ItemArtefact extends Item {
&& Streams.stream(player.getHeldEquipment()).anyMatch(s -> s.getItem() instanceof ISpellCastingItem
&& ((ISpellCastingItem)s.getItem()).getCurrentSpell(s).getElement() == Element.NECROMANCY))){
event.getEntityLiving().addPotionEffect(new PotionEffect(WizardryPotions.curse_of_soulbinding, 400));
CurseOfSoulbinding.getSoulboundCreatures(WizardData.get(player)).add(event.getEntity().getUniqueID());
}
@@ -34,7 +34,7 @@ public class ItemCrystal extends Item implements IMultiTexturedItem {
@Override
public void getSubItems(CreativeTabs tab, NonNullList<ItemStack> items){
if(tab == WizardryTabs.WIZARDRY){
if(tab == WizardryTabs.WIZARDRY || tab == CreativeTabs.SEARCH){
for(Element element : Element.values()){
items.add(new ItemStack(this, 1, element.ordinal()));
}
@@ -193,14 +193,6 @@ public class ItemFlamecatcher extends ItemBow implements IConjuredItem {
charge = net.minecraftforge.event.ForgeEventFactory.onArrowLoose(stack, world, (EntityPlayer)entity, charge, true);
if(charge < 0) return;
if(stack.getTagCompound() != null){
int shotsLeft = stack.getTagCompound().getInteger(Flamecatcher.SHOTS_REMAINING_NBT_KEY) - 1;
stack.getTagCompound().setInteger(Flamecatcher.SHOTS_REMAINING_NBT_KEY, shotsLeft);
if(shotsLeft == 0 && !world.isRemote){
stack.setItemDamage(getMaxDamage(stack) - getAnimationFrames());
}
}
float velocity = (float)charge / DRAW_TIME;
velocity = (velocity * velocity + velocity * 2) / 3;
@@ -208,6 +200,14 @@ public class ItemFlamecatcher extends ItemBow implements IConjuredItem {
if((double)velocity >= 0.1D){
if(stack.getTagCompound() != null){
int shotsLeft = stack.getTagCompound().getInteger(Flamecatcher.SHOTS_REMAINING_NBT_KEY) - 1;
stack.getTagCompound().setInteger(Flamecatcher.SHOTS_REMAINING_NBT_KEY, shotsLeft);
if(shotsLeft == 0 && !world.isRemote){
stack.setItemDamage(getMaxDamage(stack) - getAnimationFrames());
}
}
if(!world.isRemote){
EntityFlamecatcherArrow arrow = new EntityFlamecatcherArrow(world);
arrow.aim(player, EntityFlamecatcherArrow.SPEED * velocity);
@@ -15,9 +15,16 @@ import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.inventory.Slot;
import net.minecraft.item.Item;
import net.minecraft.item.ItemStack;
import net.minecraft.util.*;
import net.minecraft.util.ActionResult;
import net.minecraft.util.EnumActionResult;
import net.minecraft.util.EnumHand;
import net.minecraft.util.NonNullList;
import net.minecraft.util.ResourceLocation;
import net.minecraft.util.text.Style;
import net.minecraft.util.text.TextFormatting;
import net.minecraft.world.World;
import net.minecraftforge.common.MinecraftForge;
import net.minecraftforge.fml.common.Loader;
import net.minecraftforge.fml.common.network.simpleimpl.IMessage;
import net.minecraftforge.fml.relauncher.Side;
import net.minecraftforge.fml.relauncher.SideOnly;
@@ -100,6 +107,13 @@ public class ItemScroll extends Item implements ISpellCastingItem, IWorkbenchIte
tooltip.add(spell.getElement().getDisplayName());
tooltip.add(spell.getType().getDisplayName());
}
// Advanced tooltips displays the source mod's name if the spell is not from Wizardry
if (advanced.isAdvanced() && this.getRegistryName().toString().equals(Wizardry.MODID + ":scroll") && !spell.getRegistryName().getNamespace().equals(Wizardry.MODID)) {
String modId = spell.getRegistryName().getNamespace();
String name = new Style().setColor(TextFormatting.BLUE).setItalic(true).getFormattingCode() +
Loader.instance().getIndexedModList().get(modId).getMetadata().name;
tooltip.add(name);
}
}
}
@@ -146,12 +160,21 @@ public class ItemScroll extends Item implements ISpellCastingItem, IWorkbenchIte
Spell spell = Spell.byMetadata(stack.getItemDamage());
// By default, scrolls have no modifiers - but with the event system, they could be added.
SpellModifiers modifiers = new SpellModifiers();
SpellModifiers modifiers;
if(WizardData.get(player) != null){
modifiers = WizardData.get(player).itemCastingModifiers;
}else{
modifiers = new SpellModifiers();
}
int castingTick = stack.getMaxItemUseDuration() - count;
// Continuous spells (these must check if they can be cast each tick since the mana changes)
// In theory the spell is always continuous here but just in case it isn't...
if(spell.isContinuous && canCast(stack, spell, player, player.getActiveHand(), castingTick, modifiers)){
// Do not check canCast() on tick 0 as it is already done in onItemRightClick() and would duplicate modifiers
if(spell.isContinuous && (castingTick == 0 || canCast(stack, spell, player, player.getActiveHand(), castingTick, modifiers))){
cast(stack, spell, player, player.getActiveHand(), castingTick, modifiers);
}else{
// Scrolls normally work on the max use duration so this isn't ever reached by wizardry, but if the
@@ -195,7 +218,7 @@ public class ItemScroll extends Item implements ISpellCastingItem, IWorkbenchIte
if(!spell.isContinuous && !caster.isCreative()) stack.shrink(1);
// Now uses the vanilla cooldown mechanic to prevent spamming of spells
if(!spell.isContinuous) caster.getCooldownTracker().setCooldown(this, spell.getCooldown());
if(!spell.isContinuous && !caster.isCreative()) caster.getCooldownTracker().setCooldown(this, spell.getCooldown());
}
return true;
@@ -230,7 +253,7 @@ public class ItemScroll extends Item implements ISpellCastingItem, IWorkbenchIte
MinecraftForge.EVENT_BUS.post(new SpellCastEvent.Finish(Source.SCROLL, spell, user, modifiers, castingTick));
spell.finishCasting(user.world, user, Double.NaN, Double.NaN, Double.NaN, null, castingTick, modifiers);
if(user instanceof EntityPlayer){
if(user instanceof EntityPlayer && !((EntityPlayer)user).isCreative()){
((EntityPlayer)user).getCooldownTracker().setCooldown(this, spell.getCooldown());
}
}
@@ -1,6 +1,7 @@
package electroblob.wizardry.item;
import electroblob.wizardry.Wizardry;
import electroblob.wizardry.entity.projectile.EntityConjuredArrow;
import electroblob.wizardry.registry.Spells;
import electroblob.wizardry.util.InventoryUtils;
import net.minecraft.enchantment.EnchantmentHelper;
@@ -14,6 +15,7 @@ import net.minecraft.init.SoundEvents;
import net.minecraft.item.IItemPropertyGetter;
import net.minecraft.item.ItemArrow;
import net.minecraft.item.ItemBow;
import net.minecraft.item.ItemSpectralArrow;
import net.minecraft.item.ItemStack;
import net.minecraft.stats.StatList;
import net.minecraft.util.*;
@@ -197,8 +199,7 @@ public class ItemSpectralBow extends ItemBow implements IConjuredItem {
if(!world.isRemote){
ItemArrow itemarrow = (ItemArrow)Items.ARROW;
EntityArrow entityarrow = itemarrow.createArrow(world, new ItemStack(itemarrow), entityplayer);
EntityConjuredArrow entityarrow = new EntityConjuredArrow(world, entityplayer);
entityarrow.shoot(entityplayer, entityplayer.rotationPitch, entityplayer.rotationYaw, 0.0F,
f * 3.0F, 1.0F);
@@ -29,7 +29,7 @@ public class ItemSpectralDust extends Item implements IMultiTexturedItem {
@Override
public void getSubItems(CreativeTabs tab, NonNullList<ItemStack> items){
if(tab == WizardryTabs.WIZARDRY){
if(tab == WizardryTabs.WIZARDRY || tab == CreativeTabs.SEARCH){
for(Element element : Arrays.copyOfRange(Element.values(), 1, Element.values().length)){
items.add(new ItemStack(this, 1, element.ordinal()));
}
@@ -12,10 +12,15 @@ import net.minecraft.creativetab.CreativeTabs;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.item.Item;
import net.minecraft.item.ItemStack;
import net.minecraft.util.*;
import net.minecraft.util.ActionResult;
import net.minecraft.util.EnumActionResult;
import net.minecraft.util.EnumHand;
import net.minecraft.util.NonNullList;
import net.minecraft.util.ResourceLocation;
import net.minecraft.util.text.Style;
import net.minecraft.util.text.TextFormatting;
import net.minecraft.world.World;
import net.minecraftforge.fml.common.Loader;
import net.minecraftforge.fml.relauncher.Side;
import net.minecraftforge.fml.relauncher.SideOnly;
import net.minecraftforge.oredict.OreDictionary;
@@ -96,6 +101,13 @@ public class ItemSpellBook extends Item {
tooltip.add(spell.getElement().getDisplayName());
tooltip.add(spell.getType().getDisplayName());
}
// Advanced tooltips displays the source mod's name if the spell is not from Wizardry
if (advanced.isAdvanced() && this.getRegistryName().toString().equals(Wizardry.MODID + ":spell_book") && !spell.getRegistryName().getNamespace().equals(Wizardry.MODID)) {
String modId = spell.getRegistryName().getNamespace();
String name = new Style().setColor(TextFormatting.BLUE).setItalic(true).getFormattingCode() +
Loader.instance().getIndexedModList().get(modId).getMetadata().name;
tooltip.add(name);
}
}
}
@@ -28,6 +28,7 @@ import net.minecraft.inventory.Slot;
import net.minecraft.item.EnumAction;
import net.minecraft.item.Item;
import net.minecraft.item.ItemStack;
import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.util.*;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.RayTraceResult;
@@ -45,6 +46,7 @@ import net.minecraftforge.fml.relauncher.Side;
import net.minecraftforge.fml.relauncher.SideOnly;
import javax.annotation.Nullable;
import java.util.Arrays;
import java.util.List;
import java.util.Random;
@@ -68,7 +70,7 @@ import java.util.Random;
public class ItemWand extends Item implements IWorkbenchItem, ISpellCastingItem, IManaStoringItem {
/** The number of spell slots a wand has with no attunement upgrades applied. */
public static final int BASE_SPELL_SLOTS = 5;
public static int BASE_SPELL_SLOTS;
/** The number of ticks between each time a continuous spell is added to the player's recently-cast spells. */
private static final int CONTINUOUS_TRACKING_INTERVAL = 20;
@@ -226,14 +228,22 @@ public class ItemWand extends Item implements IWorkbenchItem, ISpellCastingItem,
}
@Override
public void onUpdate(ItemStack stack, World world, Entity entity, int slot, boolean isHeld){
public void onUpdate(ItemStack stack, World world, Entity entity, int slot, boolean isHeldInMainhand){
boolean isHeld = isHeldInMainhand || entity instanceof EntityLivingBase && ItemStack.areItemStacksEqual(stack, ((EntityLivingBase) entity).getHeldItemOffhand());
WandHelper.decrementCooldowns(stack);
// If Wizardry.settings.wandsMustBeHeldToDecrementCooldown is false, the cooldowns will be decremented.
// If Wizardry.settings.wandsMustBeHeldToDecrementCooldown is true and isHeld is true, the cooldowns will also be decremented.
// If Wizardry.settings.wandsMustBeHeldToDecrementCooldown is true and isHeld is false, the cooldowns will not be decremented.
if (!Wizardry.settings.wandsMustBeHeldToDecrementCooldown || isHeld) {
WandHelper.decrementCooldowns(stack);
}
// Decrements wand damage (increases mana) every 1.5 seconds if it has a condenser upgrade
if(!world.isRemote && !this.isManaFull(stack) && world.getTotalWorldTime() % Constants.CONDENSER_TICK_INTERVAL == 0){
// If the upgrade level is 0, this does nothing anyway.
this.rechargeMana(stack, WandHelper.getUpgradeLevel(stack, WizardryItems.condenser_upgrade));
int baseAmount = WandHelper.getUpgradeLevel(stack, WizardryItems.condenser_upgrade);
int amount = (int)(baseAmount * Wizardry.settings.condenserAmountMultiplier);
this.rechargeMana(stack, amount);
}
}
@@ -727,7 +737,7 @@ public class ItemWand extends Item implements IWorkbenchItem, ISpellCastingItem,
Item specialUpgrade = upgrade.getItem();
int maxUpgrades = this.tier.upgradeLimit;
if(this.element == Element.MAGIC) maxUpgrades += Constants.NON_ELEMENTAL_UPGRADE_BONUS;
if(this.element == null) maxUpgrades += Constants.NON_ELEMENTAL_UPGRADE_BONUS;
if(WandHelper.getTotalUpgrades(wand) < maxUpgrades
&& WandHelper.getUpgradeLevel(wand, specialUpgrade) < Constants.UPGRADE_STACK_LIMIT){
@@ -836,8 +846,19 @@ public class ItemWand extends Item implements IWorkbenchItem, ISpellCastingItem,
Spell spell = Spell.byMetadata(spellBooks[i].getStack().getItemDamage());
// If the wand is powerful enough for the spell, it's not already bound to that slot and it's enabled for wands
if(!(spell.getTier().level > this.tier.level) && spells[i] != spell && spell.isEnabled(SpellProperties.Context.WANDS)){
// Decide if we can bind this multiple times
if (Wizardry.settings.preventBindingSameSpellTwiceToWands && Arrays.stream(spells).anyMatch(s -> s == spell)) {
continue;
}
spells[i] = spell;
changed = true;
// setting to consume books upon use
if (Wizardry.settings.singleUseSpellBooks) {
spellBooks[i].getStack().shrink(1);
}
}
}
}
@@ -845,31 +866,35 @@ public class ItemWand extends Item implements IWorkbenchItem, ISpellCastingItem,
WandHelper.setSpells(centre.getStack(), spells);
// Charges wand by appropriate amount
if(crystals.getStack() != ItemStack.EMPTY && !this.isManaFull(centre.getStack())){
int chargeDepleted = this.getManaCapacity(centre.getStack()) - this.getMana(centre.getStack());
int manaPerItem = Constants.MANA_PER_CRYSTAL;
if(crystals.getStack().getItem() == WizardryItems.crystal_shard) manaPerItem = Constants.MANA_PER_SHARD;
if(crystals.getStack().getItem() == WizardryItems.grand_crystal) manaPerItem = Constants.GRAND_CRYSTAL_MANA;
if(crystals.getStack().getCount() * manaPerItem < chargeDepleted){
// If there aren't enough crystals to fully charge the wand
this.rechargeMana(centre.getStack(), crystals.getStack().getCount() * manaPerItem);
crystals.decrStackSize(crystals.getStack().getCount());
}else{
// If there are excess crystals (or just enough)
this.setMana(centre.getStack(), this.getManaCapacity(centre.getStack()));
crystals.decrStackSize((int)Math.ceil(((double)chargeDepleted) / manaPerItem));
}
if (WandHelper.rechargeManaOnApplyButtonPressed(centre, crystals)) {
changed = true;
}
return changed;
}
@Override
public void onClearButtonPressed(EntityPlayer player, Slot centre, Slot crystals, Slot upgrade, Slot[] spellBooks){
ItemStack stack = centre.getStack();
if (stack.hasTagCompound() && stack.getTagCompound().hasKey(WandHelper.SPELL_ARRAY_KEY)) {
NBTTagCompound nbt = stack.getTagCompound();
int[] spells = nbt.getIntArray(WandHelper.SPELL_ARRAY_KEY);
int expectedSlotCount = BASE_SPELL_SLOTS + WandHelper.getUpgradeLevel(stack,
WizardryItems.attunement_upgrade);
// unbrick broken wands
if (spells.length < expectedSlotCount) {
spells = new int[expectedSlotCount];
}
Arrays.fill(spells, 0);
nbt.setIntArray(WandHelper.SPELL_ARRAY_KEY, spells);
stack.setTagCompound(nbt);
}
}
@Override
public boolean isClearable() { return true; }
// hitEntity is only called server-side, so we'll have to use events
@SubscribeEvent
public static void onAttackEntityEvent(AttackEntityEvent event){
@@ -1,5 +1,7 @@
package electroblob.wizardry.item;
import com.google.common.collect.ArrayListMultimap;
import com.google.common.collect.Multimap;
import com.google.common.collect.Streams;
import electroblob.wizardry.Wizardry;
import electroblob.wizardry.client.DrawingUtils;
@@ -44,7 +46,11 @@ import net.minecraftforge.fml.relauncher.Side;
import net.minecraftforge.fml.relauncher.SideOnly;
import javax.annotation.Nullable;
import java.util.*;
import java.util.Arrays;
import java.util.EnumMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.function.Supplier;
@Mod.EventBusSubscriber
@@ -54,6 +60,28 @@ public class ItemWizardArmour extends ItemArmor implements IWorkbenchItem, IMana
private static final float SAGE_OTHER_COST_REDUCTION = 0.2f;
private static final float WARLOCK_SPEED_BOOST = 0.2f;
private static final UUID WARLOCK_SPEED_BOOST_UUID = UUID.fromString("4bad7152-2663-4b1b-bb59-552e92847031");
// Standard ItemArmor modifiers
private static final UUID[] ARMOR_MODIFIERS = new UUID[] {UUID.fromString("845DB27C-C624-495F-8C9F-6020A9A58B6B"), UUID.fromString("D8499B04-0E66-4726-AB29-64469D734E0D"), UUID.fromString("9F3D476D-C118-4544-8365-64846904B48E"), UUID.fromString("2AD3F246-FEE1-4E67-B886-69FD380BB150")};
@Override
public Multimap<String, AttributeModifier> getItemAttributeModifiers(EntityEquipmentSlot slot) {
// The vanilla armor handling logic from ItemArmor.getItemAttributeModifiers was moved to ItemWizardArmour.getAttributeModifiers to allow checking if the stack has enough mana
Multimap<String, AttributeModifier> multimap = ArrayListMultimap.create();
return multimap;
}
@Override
public Multimap<String, AttributeModifier> getAttributeModifiers(EntityEquipmentSlot slot, ItemStack stack) {
Multimap<String, AttributeModifier> multimap = super.getAttributeModifiers(slot, stack);
// only grant armor if the equipment has mana
if (slot == this.armorType && !((ItemWizardArmour) stack.getItem()).isManaEmpty(stack)) {
multimap.put(SharedMonsterAttributes.ARMOR.getName(), new AttributeModifier(ARMOR_MODIFIERS[slot.getIndex()], "Armor modifier", this.damageReduceAmount, 0));
multimap.put(SharedMonsterAttributes.ARMOR_TOUGHNESS.getName(), new AttributeModifier(ARMOR_MODIFIERS[slot.getIndex()], "Armor toughness", this.toughness, 0));
}
return multimap;
}
public enum ArmourClass {
@@ -181,8 +209,8 @@ public class ItemWizardArmour extends ItemArmor implements IWorkbenchItem, IMana
@Override
public void onArmorTick(World world, EntityPlayer player, ItemStack itemStack){
if(armorType == EntityEquipmentSlot.HEAD && isWearingFullSet(player, element, ArmourClass.BATTLEMAGE)
&& player.ticksExisted % 20 == 0){
if(armorType == EntityEquipmentSlot.HEAD && player.ticksExisted % 20 == 0
&& isWearingFullSet(player, element, ArmourClass.BATTLEMAGE) && doAllArmourPiecesHaveMana(player)){
player.addPotionEffect(new PotionEffect(WizardryPotions.ward, 219, 0, true, false));
}
}
@@ -209,7 +237,7 @@ public class ItemWizardArmour extends ItemArmor implements IWorkbenchItem, IMana
modifiers.set(WizardryItems.cooldown_upgrade, modifiers.get(WizardryItems.cooldown_upgrade) - armourClass.cooldownReduction, true);
// Full set bonuses
if(this.armorType == EntityEquipmentSlot.HEAD && isWearingFullSet(caster, element, armourClass)){
if(this.armorType == EntityEquipmentSlot.HEAD && isWearingFullSet(caster, element, armourClass) && doAllArmourPiecesHaveMana(caster)){
if(armourClass == ArmourClass.SAGE && spell.getElement() != this.element){
modifiers.set(SpellModifiers.COST, 1 - SAGE_OTHER_COST_REDUCTION, false);
}
@@ -306,7 +334,12 @@ public class ItemWizardArmour extends ItemArmor implements IWorkbenchItem, IMana
int chargeDepleted = this.getManaCapacity(centre.getStack()) - this.getMana(centre.getStack());
int manaPerItem = Constants.MANA_PER_CRYSTAL;
// Not too pretty but allows addons implementing the IManaStoringItem interface to provide their mana amount for custom crystals,
// previously this was defaulted to the regular crystal's amount, allowing players to exploit it if a crystal was worth less mana than that.
int manaPerItem = crystals.getStack().getItem() instanceof IManaStoringItem ?
((IManaStoringItem) crystals.getStack().getItem()).getMana(crystals.getStack()) :
crystals.getStack().getItem() instanceof ItemCrystal ? Constants.MANA_PER_CRYSTAL : Constants.MANA_PER_SHARD;
if(crystals.getStack().getItem() == WizardryItems.crystal_shard) manaPerItem = Constants.MANA_PER_SHARD;
if(crystals.getStack().getItem() == WizardryItems.grand_crystal) manaPerItem = Constants.GRAND_CRYSTAL_MANA;
@@ -335,7 +368,7 @@ public class ItemWizardArmour extends ItemArmor implements IWorkbenchItem, IMana
IAttributeInstance movementSpeed = event.getEntityLiving().getAttributeMap().getAttributeInstance(SharedMonsterAttributes.MOVEMENT_SPEED);
if(isWearingFullSet(event.getEntityLiving(), null, ArmourClass.WARLOCK)){
if(isWearingFullSet(event.getEntityLiving(), null, ArmourClass.WARLOCK) && doAllArmourPiecesHaveMana(event.getEntityLiving())){
// Only apply the modifier once (can't just check this is the helmet since it might not be the last piece equipped)
if(movementSpeed.getModifier(WARLOCK_SPEED_BOOST_UUID) == null){
movementSpeed.applyModifier(new AttributeModifier(WARLOCK_SPEED_BOOST_UUID, "Warlock set bonus", WARLOCK_SPEED_BOOST, Operations.MULTIPLY_FLAT));
@@ -405,7 +438,7 @@ public class ItemWizardArmour extends ItemArmor implements IWorkbenchItem, IMana
* @param armourClass The class to check, or null to accept any class as long as they are all the same.
* @return True if the entity is wearing a full set of the given element and class, false otherwise.
*/
private static boolean isWearingFullSet(EntityLivingBase entity, @Nullable Element element, @Nullable ArmourClass armourClass){
public static boolean isWearingFullSet(EntityLivingBase entity, @Nullable Element element, @Nullable ArmourClass armourClass){
ItemStack helmet = entity.getItemStackFromSlot(EntityEquipmentSlot.HEAD);
if(!(helmet.getItem() instanceof ItemWizardArmour)) return false;
Element e = element == null ? ((ItemWizardArmour)helmet.getItem()).element : element;
@@ -416,6 +449,16 @@ public class ItemWizardArmour extends ItemArmor implements IWorkbenchItem, IMana
&& ((ItemWizardArmour)entity.getItemStackFromSlot(s).getItem()).armourClass == ac);
}
/**
* Returns whether the given entity's armour pieces which implement {@link IManaStoringItem} has mana. Ignores pieces which doesn't have a mana storage.
* @param entity The entity to query.
* @return True if all armour pieces which implement {@link IManaStoringItem} has mana, false otherwise.
*/
public static boolean doAllArmourPiecesHaveMana(EntityLivingBase entity){
return Arrays.stream(InventoryUtils.ARMOUR_SLOTS).noneMatch(s -> entity.getItemStackFromSlot(s).getItem() instanceof IManaStoringItem
&& ((IManaStoringItem) entity.getItemStackFromSlot(s).getItem()).isManaEmpty(entity.getItemStackFromSlot(s)));
}
@SubscribeEvent
public static void onLivingSetAttackTargetEvent(LivingSetAttackTargetEvent event){
// Undo the mob detection penalty for wearing armour when invisible
@@ -10,6 +10,7 @@ import net.minecraft.util.ActionResult;
import net.minecraft.util.EnumActionResult;
import net.minecraft.util.EnumHand;
import net.minecraft.util.text.Style;
import net.minecraft.util.text.TextComponentTranslation;
import net.minecraft.util.text.TextFormatting;
import net.minecraft.world.World;
@@ -36,7 +37,11 @@ public class ItemWizardHandbook extends Item {
@Override
public ActionResult<ItemStack> onItemRightClick(World world, EntityPlayer player, EnumHand hand){
ItemStack stack = player.getHeldItem(hand);
player.openGui(Wizardry.instance, WizardryGuiHandler.WIZARD_HANDBOOK, world, 0, 0, 0);
if (Wizardry.settings.loadHandbook) {
player.openGui(Wizardry.instance, WizardryGuiHandler.WIZARD_HANDBOOK, world, 0, 0, 0);
} else if (!world.isRemote){
player.sendStatusMessage(new TextComponentTranslation("item." + Wizardry.MODID + ":wizard_handbook.disabled"), false);
}
return ActionResult.newResult(EnumActionResult.SUCCESS, stack);
}
@@ -12,12 +12,15 @@ import electroblob.wizardry.entity.living.*;
import electroblob.wizardry.entity.projectile.EntityFirebomb;
import electroblob.wizardry.entity.projectile.EntityMagicFireball;
import electroblob.wizardry.event.SpellCastEvent;
import electroblob.wizardry.event.SpellCastEvent.Source;
import electroblob.wizardry.item.IManaStoringItem;
import electroblob.wizardry.item.ISpellCastingItem;
import electroblob.wizardry.item.ItemArtefact;
import electroblob.wizardry.registry.*;
import electroblob.wizardry.spell.Banish;
import electroblob.wizardry.spell.Spell;
import electroblob.wizardry.util.BlockUtils;
import electroblob.wizardry.util.EntityUtils;
import electroblob.wizardry.util.SpellModifiers;
import electroblob.wizardry.util.SpellProperties.Context;
import net.minecraft.entity.effect.EntityLightningBolt;
import net.minecraft.entity.item.EntityFallingBlock;
@@ -75,7 +78,7 @@ public abstract class Forfeit {
public Forfeit(ResourceLocation name){
this.name = name;
this.sound = WizardrySounds.createSound("forfeit." + name.getPath());
this.sound = WizardrySounds.createSound(name.getNamespace(), "forfeit." + name.getPath());
}
public abstract void apply(World world, EntityPlayer player);
@@ -174,6 +177,23 @@ public abstract class Forfeit {
forfeit.apply(event.getWorld(), player);
ItemStack stack = player.getHeldItemMainhand();
if(!(stack.getItem() instanceof ISpellCastingItem)){
stack = player.getHeldItemOffhand();
if(!(stack.getItem() instanceof ISpellCastingItem)) stack = ItemStack.EMPTY;
}
if(!stack.isEmpty()){
// Still need to charge the player mana or consume the scroll
if(event.getSource() == Source.SCROLL){
if(!player.isCreative()) stack.shrink(1);
}else if(stack.getItem() instanceof IManaStoringItem){
int cost = (int)(event.getSpell().getCost() * event.getModifiers().get(SpellModifiers.COST) + 0.1f); // Weird floaty rounding
((IManaStoringItem)stack.getItem()).consumeMana(stack, cost, player);
}
}
WizardryAdvancementTriggers.spell_failure.triggerFor(player);
EntityUtils.playSoundAtPlayer(player, forfeit.getSound(), WizardrySounds.SPELLS, 1, 1);
@@ -222,10 +242,11 @@ public abstract class Forfeit {
}));
add(Tier.MASTER, Element.FIRE, create("burn_surroundings", (w, p) -> {
if(!w.isRemote){
if(!w.isRemote && EntityUtils.canDamageBlocks(p, w)){
List<BlockPos> sphere = BlockUtils.getBlockSphere(p.getPosition(), 6);
for(BlockPos pos : sphere){
if(w.rand.nextBoolean() && w.isAirBlock(pos)) w.setBlockState(pos, Blocks.FIRE.getDefaultState());
if(w.rand.nextBoolean() && w.isAirBlock(pos) && BlockUtils.canPlaceBlock(p, w, pos))
w.setBlockState(pos, Blocks.FIRE.getDefaultState());
}
}
}));
@@ -376,10 +397,11 @@ public abstract class Forfeit {
}));
add(Tier.NOVICE, Element.EARTH, create("snares", (w, p) -> {
if(!w.isRemote){
if(!w.isRemote && EntityUtils.canDamageBlocks(p, w)){
for(EnumFacing direction : EnumFacing.HORIZONTALS){
BlockPos pos = p.getPosition().offset(direction);
if(BlockUtils.canBlockBeReplaced(w, pos)) w.setBlockState(pos, WizardryBlocks.snare.getDefaultState());
if(BlockUtils.canBlockBeReplaced(w, pos) && BlockUtils.canPlaceBlock(p, w, pos))
w.setBlockState(pos, WizardryBlocks.snare.getDefaultState());
}
}
}));
@@ -393,9 +415,9 @@ public abstract class Forfeit {
}));
add(Tier.APPRENTICE, Element.EARTH, create("uproot_plants", (w, p) -> {
if(!w.isRemote){
if(!w.isRemote && EntityUtils.canDamageBlocks(p, w)){
List<BlockPos> sphere = BlockUtils.getBlockSphere(p.getPosition(), 5);
sphere.removeIf(pos -> !(w.getBlockState(pos).getBlock() instanceof IPlantable));
sphere.removeIf(pos -> !(w.getBlockState(pos).getBlock() instanceof IPlantable) || !BlockUtils.canBreakBlock(p, w, pos));
sphere.forEach(pos -> w.destroyBlock(pos, true));
}
}));
@@ -403,9 +425,9 @@ public abstract class Forfeit {
add(Tier.APPRENTICE, Element.EARTH, create("poison_self", (w, p) -> p.addPotionEffect(new PotionEffect(MobEffects.POISON, 400, 1))));
add(Tier.ADVANCED, Element.EARTH, create("flood", (w, p) -> {
if(!w.isRemote){
if(!w.isRemote && EntityUtils.canDamageBlocks(p, w)){
List<BlockPos> sphere = BlockUtils.getBlockSphere(p.getPosition().up(), 2);
sphere.removeIf(pos -> !BlockUtils.canBlockBeReplaced(w, pos, true));
sphere.removeIf(pos -> !BlockUtils.canBlockBeReplaced(w, pos, true) || !BlockUtils.canPlaceBlock(p, w, pos));
sphere.forEach(pos -> w.setBlockState(pos, Blocks.WATER.getDefaultState()));
}
}));
@@ -413,8 +435,7 @@ public abstract class Forfeit {
add(Tier.MASTER, Element.EARTH, create("bury_self", (w, p) -> {
if(!w.isRemote){
List<BlockPos> sphere = BlockUtils.getBlockSphere(p.getPosition(), 4);
sphere.removeIf(pos -> !w.getBlockState(pos).isFullCube());
sphere.removeIf(pos -> BlockUtils.isBlockUnbreakable(w, pos));
sphere.removeIf(pos -> !w.getBlockState(pos).isFullCube() || BlockUtils.isBlockUnbreakable(w, pos) || BlockUtils.canBreakBlock(p, w, pos));
sphere.forEach(pos -> {
EntityFallingBlock block = new EntityFallingBlock(w, pos.getX() + 0.5, pos.getY() + 0.5,
pos.getZ() + 0.5, w.getBlockState(pos));
@@ -465,6 +486,8 @@ public abstract class Forfeit {
}
}));
add(Tier.MASTER, Element.SORCERY, create("teleport_self_large_distance", (w, p) -> ((Banish)Spells.banish).teleport(p, w, 8 + w.rand.nextDouble() * 700)));
add(Tier.NOVICE, Element.HEALING, create("damage_self", (w, p) -> p.attackEntityFrom(DamageSource.MAGIC, 4)));
add(Tier.NOVICE, Element.HEALING, create("spill_armour", (w, p) -> {
@@ -1,13 +1,13 @@
package electroblob.wizardry.misc;
import com.google.common.collect.HashMultimap;
import com.google.common.collect.Multimap;
import electroblob.wizardry.Wizardry;
import electroblob.wizardry.item.IManaStoringItem;
import electroblob.wizardry.item.ItemManaFlask;
import net.minecraft.inventory.InventoryCrafting;
import net.minecraft.item.Item;
import net.minecraft.item.ItemStack;
import net.minecraft.item.crafting.CraftingManager;
import net.minecraft.item.crafting.IRecipe;
import net.minecraft.world.World;
import net.minecraftforge.fml.common.Mod;
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
@@ -15,6 +15,8 @@ import net.minecraftforge.fml.common.gameevent.PlayerEvent;
import net.minecraftforge.oredict.OreDictionary;
import net.minecraftforge.oredict.ShapelessOreRecipe;
import java.util.Collection;
/**
* Implements a dynamic crafting recipe for recharging items with mana flasks.
*
@@ -24,6 +26,8 @@ import net.minecraftforge.oredict.ShapelessOreRecipe;
@Mod.EventBusSubscriber
public class RecipeRechargeWithFlask extends ShapelessOreRecipe {
private static final Multimap<Item, RecipeRechargeWithFlask> FLASK_RECIPES = HashMultimap.create();
private final IManaStoringItem chargeable;
private final ItemManaFlask flask;
@@ -38,6 +42,7 @@ public class RecipeRechargeWithFlask extends ShapelessOreRecipe {
if(!(chargeable instanceof IManaStoringItem)) throw new IllegalArgumentException("Item to be charged must be an instance of IManaStoringItem");
this.chargeable = (IManaStoringItem)chargeable;
this.flask = flask;
FLASK_RECIPES.put(chargeable, this);
}
// Commented out for now because JEI spams the log with errors about the recipe having no output
@@ -91,11 +96,11 @@ public class RecipeRechargeWithFlask extends ShapelessOreRecipe {
// This means that although I no longer have to replace the result every tick, I still need to do it here
// ... I thought the whole point of the new recipe system was so that I DIDN'T have to do this?!
if(event.craftMatrix instanceof InventoryCrafting){
for(IRecipe recipe : CraftingManager.REGISTRY){
if(recipe instanceof RecipeRechargeWithFlask
&& recipe.matches((InventoryCrafting)event.craftMatrix, event.player.world)){
Collection<RecipeRechargeWithFlask> recipes = FLASK_RECIPES.get(event.crafting.getItem());
for(RecipeRechargeWithFlask recipe : recipes){
if(recipe.matches((InventoryCrafting)event.craftMatrix, event.player.world)){
// Have to modify the itemstack in the actual event, it cannot be replaced
((RecipeRechargeWithFlask)recipe).rechargeItemAndCopyNBT(event.crafting, (InventoryCrafting)event.craftMatrix);
recipe.rechargeItemAndCopyNBT(event.crafting, (InventoryCrafting)event.craftMatrix);
}
}
}
@@ -0,0 +1,79 @@
package electroblob.wizardry.packet;
import electroblob.wizardry.Wizardry;
import io.netty.buffer.ByteBuf;
import net.minecraftforge.fml.common.network.simpleimpl.IMessage;
import net.minecraftforge.fml.common.network.simpleimpl.IMessageHandler;
import net.minecraftforge.fml.common.network.simpleimpl.MessageContext;
/**
* <b>[Server -> Client]</b> This packet is sent when a bomb entity impacts something. It is used to guarantee that the
* client always sees the explosion effect, even in singleplayer where the server may remove the entity before the
* client's own simulation can call {@code onImpact()}.
*/
public class PacketBombExplosion implements IMessageHandler<PacketBombExplosion.Message, IMessage> {
/** Bomb type constants, matching the order bombs are handled in {@link electroblob.wizardry.CommonProxy}. */
public static final int FIREBOMB = 0;
public static final int POISON_BOMB = 1;
public static final int SMOKE_BOMB = 2;
public static final int SPARK_BOMB = 3;
@Override
public IMessage onMessage(Message message, MessageContext ctx){
if(ctx.side.isClient()){
net.minecraft.client.Minecraft.getMinecraft().addScheduledTask(
() -> Wizardry.proxy.handleBombExplosionPacket(message));
}
return null;
}
public static class Message implements IMessage {
/** One of the bomb type constants defined in {@link PacketBombExplosion}. */
public int bombType;
public double x, y, z;
public float blastMultiplier;
/** Entity IDs of secondary targets struck by the spark bomb chain. Empty for other bomb types. */
public int[] secondaryTargetIDs;
// Required no-arg constructor
public Message(){}
public Message(int bombType, double x, double y, double z, float blastMultiplier){
this(bombType, x, y, z, blastMultiplier, new int[0]);
}
public Message(int bombType, double x, double y, double z, float blastMultiplier, int[] secondaryTargetIDs){
this.bombType = bombType;
this.x = x;
this.y = y;
this.z = z;
this.blastMultiplier = blastMultiplier;
this.secondaryTargetIDs = secondaryTargetIDs;
}
@Override
public void fromBytes(ByteBuf buf){
bombType = buf.readInt();
x = buf.readDouble();
y = buf.readDouble();
z = buf.readDouble();
blastMultiplier = buf.readFloat();
int count = buf.readInt();
secondaryTargetIDs = new int[count];
for(int i = 0; i < count; i++) secondaryTargetIDs[i] = buf.readInt();
}
@Override
public void toBytes(ByteBuf buf){
buf.writeInt(bombType);
buf.writeDouble(x);
buf.writeDouble(y);
buf.writeDouble(z);
buf.writeFloat(blastMultiplier);
buf.writeInt(secondaryTargetIDs.length);
for(int id : secondaryTargetIDs) buf.writeInt(id);
}
}
}
@@ -1,6 +1,7 @@
package electroblob.wizardry.packet;
import electroblob.wizardry.Wizardry;
import electroblob.wizardry.event.ResurrectionEvent;
import electroblob.wizardry.inventory.ContainerArcaneWorkbench;
import electroblob.wizardry.item.ISpellCastingItem;
import electroblob.wizardry.packet.PacketControlInput.Message;
@@ -13,6 +14,7 @@ import io.netty.buffer.ByteBuf;
import net.minecraft.entity.player.EntityPlayerMP;
import net.minecraft.item.ItemStack;
import net.minecraft.util.EnumHand;
import net.minecraftforge.common.MinecraftForge;
import net.minecraftforge.fml.common.network.simpleimpl.IMessage;
import net.minecraftforge.fml.common.network.simpleimpl.IMessageHandler;
import net.minecraftforge.fml.common.network.simpleimpl.MessageContext;
@@ -49,6 +51,17 @@ public class PacketControlInput implements IMessageHandler<Message, IMessage> {
break;
case CLEAR_BUTTON:
if(!(player.openContainer instanceof ContainerArcaneWorkbench)){
Wizardry.logger.warn("Received a PacketControlInput, but the player that sent it was not " +
"currently using an arcane workbench. This should not happen!");
}else{
((ContainerArcaneWorkbench)player.openContainer).onClearButtonPressed(player);
}
break;
case NEXT_SPELL_KEY:
if(wand.getItem() instanceof ISpellCastingItem){
@@ -79,6 +92,7 @@ public class PacketControlInput implements IMessageHandler<Message, IMessage> {
.filter(s -> Resurrection.canStackResurrect(s, player)).findFirst().orElse(null);
if(stack != null){
if(MinecraftForge.EVENT_BUS.post(new ResurrectionEvent(player, player))) break;
// This should suffice, since this is the only way a player can cast resurrection when dead!
((ISpellCastingItem)stack.getItem()).cast(stack, Spells.resurrection, player, EnumHand.MAIN_HAND, 0, new SpellModifiers());
break;
@@ -130,7 +144,7 @@ public class PacketControlInput implements IMessageHandler<Message, IMessage> {
}
public enum ControlType {
APPLY_BUTTON, NEXT_SPELL_KEY, PREVIOUS_SPELL_KEY, RESURRECT_BUTTON, CANCEL_RESURRECT, POSSESSION_PROJECTILE
APPLY_BUTTON, NEXT_SPELL_KEY, PREVIOUS_SPELL_KEY, RESURRECT_BUTTON, CANCEL_RESURRECT, POSSESSION_PROJECTILE, CLEAR_BUTTON
}
public static class Message implements IMessage {
@@ -10,7 +10,11 @@ import net.minecraftforge.fml.common.network.simpleimpl.IMessage;
import net.minecraftforge.fml.common.network.simpleimpl.IMessageHandler;
import net.minecraftforge.fml.common.network.simpleimpl.MessageContext;
import java.util.*;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
/**
* <b>[Server -> Client]</b> This packet is sent to synchronise any fields that need synchronising in
@@ -54,9 +58,8 @@ public class PacketPlayerSync implements IMessageHandler<Message, IMessage> {
this.seed = buf.readLong();
this.selectedMinionID = buf.readInt();
this.spellData = new HashMap<>();
WizardData.getSyncedVariables().forEach(v -> spellData.put(v, v.read(buf)));
WizardData.getSyncedVariablesOrderedByKey().forEach(v -> spellData.put(v, v.read(buf)));
// Have to send empty tags to guarantee correct ByteBuf size/order, but no point keeping the resulting nulls
spellData.values().removeIf(Objects::isNull);
@@ -73,7 +76,7 @@ public class PacketPlayerSync implements IMessageHandler<Message, IMessage> {
buf.writeLong(seed);
buf.writeInt(selectedMinionID);
WizardData.getSyncedVariables().forEach(v -> v.write(buf, spellData.get(v)));
WizardData.getSyncedVariablesOrderedByKey().forEach(v -> v.write(buf, spellData.get(v)));
if(this.spellsDiscovered == null) return;
for(Spell spell : this.spellsDiscovered){
@@ -35,6 +35,7 @@ public class WizardryPacketHandler {
registerMessage(PacketSpellQuickAccess.class, PacketSpellQuickAccess.Message.class);
registerMessage(PacketRequestDonationPerks.class, PacketRequestDonationPerks.Message.class);
registerMessage(PacketSyncDonationPerks.class, PacketSyncDonationPerks.Message.class);
registerMessage(PacketBombExplosion.class, PacketBombExplosion.Message.class);
}
private static int nextPacketId = 0;
@@ -1,11 +1,14 @@
package electroblob.wizardry.potion;
import electroblob.wizardry.Wizardry;
import electroblob.wizardry.item.ItemWizardArmour;
import net.minecraft.entity.EntityLivingBase;
import net.minecraft.inventory.EntityEquipmentSlot;
import net.minecraft.item.ItemStack;
import net.minecraft.util.DamageSource;
import net.minecraft.util.ResourceLocation;
import net.minecraft.util.math.BlockPos;
import net.minecraftforge.common.ISpecialArmor;
public class CurseUndeath extends Curse {
@@ -25,7 +28,7 @@ public class CurseUndeath extends Curse {
// Adapted from EntityZombie
if(entitylivingbase.world.isDaytime() && !entitylivingbase.world.isRemote){
float f = entitylivingbase.getBrightness();
if(f > 0.5F && entitylivingbase.world.rand.nextFloat() * 30.0F < (f - 0.4F) * 2.0F
@@ -35,14 +38,21 @@ public class CurseUndeath extends Curse {
boolean flag = true;
ItemStack itemstack = entitylivingbase.getItemStackFromSlot(EntityEquipmentSlot.HEAD);
if(!itemstack.isEmpty()){
if(itemstack.isItemStackDamageable()){
if (!itemstack.isEmpty()) {
if (itemstack.isItemStackDamageable()) {
itemstack.setItemDamage(itemstack.getItemDamage() + entitylivingbase.world.rand.nextInt(2));
if(itemstack.getItemDamage() >= itemstack.getMaxDamage()){
entitylivingbase.renderBrokenItemStack(itemstack);
entitylivingbase.setItemStackToSlot(EntityEquipmentSlot.HEAD, ItemStack.EMPTY);
if (itemstack.getItem() instanceof ISpecialArmor) {
((ISpecialArmor) itemstack.getItem()).damageArmor(entitylivingbase, itemstack, DamageSource.ON_FIRE, entitylivingbase.world.rand.nextInt(2), EntityEquipmentSlot.HEAD.getSlotIndex());
} else {
itemstack.setItemDamage(itemstack.getItemDamage() + entitylivingbase.world.rand.nextInt(2));
if (itemstack.getItemDamage() >= itemstack.getMaxDamage()) {
if (itemstack.getItem() instanceof ItemWizardArmour) {
entitylivingbase.setFire(8);
} else {
entitylivingbase.renderBrokenItemStack(itemstack);
entitylivingbase.setItemStackToSlot(EntityEquipmentSlot.HEAD, ItemStack.EMPTY);
}
}
}
}
@@ -2,17 +2,14 @@ package electroblob.wizardry.potion;
import electroblob.wizardry.Wizardry;
import electroblob.wizardry.registry.WizardryPotions;
import electroblob.wizardry.registry.WizardrySounds;
import electroblob.wizardry.util.EntityUtils;
import electroblob.wizardry.util.GeometryUtils;
import electroblob.wizardry.util.NBTExtras;
import net.minecraft.entity.EntityLivingBase;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.entity.player.EntityPlayerMP;
import net.minecraft.nbt.NBTUtil;
import net.minecraft.network.play.server.SPacketEntityVelocity;
import net.minecraft.util.ResourceLocation;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.MathHelper;
import net.minecraft.util.math.Vec3d;
import net.minecraftforge.event.entity.living.LivingEvent.LivingUpdateEvent;
import net.minecraftforge.fml.common.Mod;
@@ -23,8 +20,6 @@ public class PotionContainment extends PotionMagicEffect {
public static final String ENTITY_TAG = "containmentPos";
private static final double RUBBERBAND_THRESHOLD = 0.24;
public PotionContainment(boolean isBadEffect, int liquidColour){
super(isBadEffect, liquidColour, new ResourceLocation(Wizardry.MODID, "textures/gui/potion_icons/containment.png"));
this.setPotionName("potion." + Wizardry.MODID + ":containment");
@@ -41,7 +36,6 @@ public class PotionContainment extends PotionMagicEffect {
@Override
public void performEffect(EntityLivingBase target, int strength){
float maxDistance = getContainmentDistance(strength);
// Initialise the containment position to the entity's position if it wasn't set already
@@ -63,59 +57,14 @@ public class PotionContainment extends PotionMagicEffect {
if(target.getEntityBoundingBox().maxZ > origin.z + maxDistance) z = origin.z + maxDistance - target.width/2;
if(target.getEntityBoundingBox().minZ < origin.z - maxDistance) z = origin.z - maxDistance + target.width/2;
if(x != target.posX || y != target.posY || z != target.posZ){
// if(target.world.isRemote){
//
// if(x != target.posX){
// for(int i = 0; i < 20; i++){
// ParticleBuilder.create(ParticleBuilder.Type.DUST).pos(
// x,
// target.posY + target.height * target.world.rand.nextFloat(),
// target.posZ + target.width * (target.world.rand.nextFloat() - 0.5f))
// .face(EnumFacing.EAST).clr(0.8f, 0.9f, 1).spawn(target.world);
// }
// }
//
// if(y != target.posY){
// for(int i = 0; i < 20; i++){
// ParticleBuilder.create(ParticleBuilder.Type.DUST).pos(
// target.posX + target.width * (target.world.rand.nextFloat() - 0.5f),
// y,
// target.posZ + target.width * (target.world.rand.nextFloat() - 0.5f))
// .face(EnumFacing.UP).clr(0.8f, 0.9f, 1).spawn(target.world);
// }
// }
//
// if(z != target.posZ){
// for(int i = 0; i < 20; i++){
// ParticleBuilder.create(ParticleBuilder.Type.DUST).pos(
// target.posX + target.width * (target.world.rand.nextFloat() - 0.5f),
// target.posY + target.height * target.world.rand.nextFloat(),
// z)
// .face(EnumFacing.SOUTH).clr(0.8f, 0.9f, 1).spawn(target.world);
// }
// }
// }
// Rubberbanding prevention (the 0.25 limit is from NetHandlerPlayServer, just search "moved wrongly"
if(target instanceof EntityPlayer){
x = MathHelper.clamp(x, target.posX - RUBBERBAND_THRESHOLD, target.posX + RUBBERBAND_THRESHOLD);
y = MathHelper.clamp(y, target.posY - RUBBERBAND_THRESHOLD, target.posY + RUBBERBAND_THRESHOLD);
z = MathHelper.clamp(z, target.posZ - RUBBERBAND_THRESHOLD, target.posZ + RUBBERBAND_THRESHOLD);
}
if(x != target.posX || y != target.posY || z != target.posZ)
{
target.addVelocity(0.15 * Math.signum(x - target.posX), 0.15 * Math.signum(y - target.posY), 0.15 * Math.signum(z - target.posZ));
EntityUtils.undoGravity(target);
target.addVelocity(0.35 * Math.signum(x - target.posX), 0.35 * Math.signum(y - target.posY), 0.35 * Math.signum(z - target.posZ));
target.setPositionAndUpdate(x, y, z); // FIXME: This line must be causing the server rubberbanding, not sure why
// Player motion is handled on that player's client so needs packets
if(target instanceof EntityPlayerMP){
((EntityPlayerMP)target).connection.sendPacket(new SPacketEntityVelocity(target));
if(target.world.isRemote){
target.world.playSound(target.posX, target.posY, target.posZ, WizardrySounds.ENTITY_FORCEFIELD_DEFLECT,
WizardrySounds.SPELLS, 0.3f, 1f, false);
}
//
// target.world.playSound(target.posX, target.posY, target.posZ, WizardrySounds.ENTITY_FORCEFIELD_DEFLECT,
// WizardrySounds.SPELLS, 0.3f, 1f, false);
}
// Need to do this here because it's the only way to hook into potion ending both client- and server-side
@@ -0,0 +1,31 @@
package electroblob.wizardry.potion;
import electroblob.wizardry.Wizardry;
import electroblob.wizardry.util.EntityUtils;
import net.minecraft.entity.SharedMonsterAttributes;
import net.minecraft.util.ResourceLocation;
import net.minecraftforge.fml.common.Mod;
@Mod.EventBusSubscriber
public class PotionDiamondflesh extends PotionMagicEffect {
public PotionDiamondflesh(boolean isBadEffect, int liquidColour) {
super(isBadEffect, liquidColour, new ResourceLocation(Wizardry.MODID, "textures/gui/potion_icons/diamondflesh.png"));
// This needs to be here because registerPotionAttributeModifier doesn't like it if the potion has no name yet.
this.setPotionName("potion." + Wizardry.MODID + ":ironflesh");
// Only apply slowness if the setting allows it
if(Wizardry.settings.fleshSpellsCauseSlowness) {
this.registerPotionAttributeModifier(SharedMonsterAttributes.MOVEMENT_SPEED,
"158a8af2-6db0-4340-a01c-a7b60d10ddf4", -0.1f, EntityUtils.Operations.MULTIPLY_CUMULATIVE);
}
this.registerPotionAttributeModifier(SharedMonsterAttributes.ARMOR_TOUGHNESS,
"a68d4532-5847-426c-9b03-d541b113cec2", (float)Wizardry.settings.diamondFleshArmorToughnessBonus, EntityUtils.Operations.ADD);
this.registerPotionAttributeModifier(SharedMonsterAttributes.ARMOR,
"46a095be-82dd-43fd-8b67-13f51591eb8e", (float)Wizardry.settings.diamondFleshArmorBonus, EntityUtils.Operations.ADD);
}
@Override
public boolean isReady(int duration, int amplifier) {
return true;
}
}
@@ -0,0 +1,97 @@
package electroblob.wizardry.potion;
import electroblob.wizardry.registry.WizardrySounds;
import net.minecraft.init.SoundEvents;
import net.minecraft.entity.EntityLivingBase;
import net.minecraft.init.Blocks;
import net.minecraft.util.EnumParticleTypes;
import net.minecraft.util.ResourceLocation;
import net.minecraft.util.SoundCategory;
import net.minecraft.util.math.AxisAlignedBB;
import net.minecraft.util.math.BlockPos;
import net.minecraft.world.World;
public class PotionFireskin extends PotionMagicEffectParticles {
public PotionFireskin(boolean isBadEffect, int liquidColour, ResourceLocation texture) {
super(isBadEffect, liquidColour, texture);
this.setBeneficial();
}
@Override
public void spawnCustomParticle(World world, double x, double y, double z){
if(world.isRemote){
// Spawn ambient fire particles around the entity
world.spawnParticle(EnumParticleTypes.FLAME, x, y, z, 0, 0, 0);
}
}
@Override
public boolean isReady(int duration, int amplifier) {
return true;
}
@Override
public void performEffect(EntityLivingBase entitylivingbase, int strength){
// Original fireskin behavior: extinguish the entity to prevent fire damage from melee attacks
entitylivingbase.extinguish();
// Server-side cobweb burning logic
World world = entitylivingbase.world;
if(!world.isRemote){
AxisAlignedBB entityBox = entitylivingbase.getEntityBoundingBox();
// Expand the bounding box slightly to catch cobwebs the entity is touching
entityBox = entityBox.expand(0.1, 0.1, 0.1);
BlockPos minPos = new BlockPos(entityBox.minX, entityBox.minY, entityBox.minZ);
BlockPos maxPos = new BlockPos(entityBox.maxX, entityBox.maxY, entityBox.maxZ);
boolean burnedCobweb = false;
// Check all blocks in the entity's bounding box
for(int x = minPos.getX(); x <= maxPos.getX(); x++){
for(int y = minPos.getY(); y <= maxPos.getY(); y++){
for(int z = minPos.getZ(); z <= maxPos.getZ(); z++){
BlockPos pos = new BlockPos(x, y, z);
if(world.getBlockState(pos).getBlock() == Blocks.WEB){
// Burn the cobweb
world.setBlockToAir(pos);
burnedCobweb = true;
// Add visual effects - spawn particles on client side
if(world.rand.nextFloat() < 0.7f){ // 70% chance to spawn particles for better visibility
float offsetX = world.rand.nextFloat() * 0.1f - 0.05f;
float offsetY = world.rand.nextFloat() * 0.1f + 0.05f;
float offsetZ = world.rand.nextFloat() * 0.1f - 0.05f;
// Main flame particle rising up
world.spawnParticle(EnumParticleTypes.FLAME,
pos.getX() + 0.5, pos.getY() + 0.5, pos.getZ() + 0.5,
0, 0.1, 0);
// Two additional flame particles with random movement
world.spawnParticle(EnumParticleTypes.FLAME,
pos.getX() + 0.5, pos.getY() + 0.5, pos.getZ() + 0.5,
offsetX, offsetY, offsetZ);
world.spawnParticle(EnumParticleTypes.FLAME,
pos.getX() + 0.5, pos.getY() + 0.5, pos.getZ() + 0.5,
offsetX * -0.5f, offsetY * 0.8f, offsetZ * -0.5f);
}
}
}
}
}
// Play the fire extinguish sound only when cobwebs were actually burned
if(burnedCobweb){
world.playSound(null, entitylivingbase.getPosition(), SoundEvents.BLOCK_FIRE_EXTINGUISH, SoundCategory.BLOCKS, 0.6f, 0.8f + world.rand.nextFloat() * 0.4f);
}
}
}
}
@@ -5,6 +5,7 @@ import electroblob.wizardry.constants.Constants;
import electroblob.wizardry.registry.WizardryPotions;
import electroblob.wizardry.util.ParticleBuilder;
import electroblob.wizardry.util.ParticleBuilder.Type;
import net.minecraft.entity.EntityLivingBase;
import net.minecraft.entity.SharedMonsterAttributes;
import net.minecraft.util.ResourceLocation;
import net.minecraft.world.World;
@@ -50,4 +51,18 @@ public class PotionFrost extends PotionMagicEffect implements ICustomPotionParti
}
}
@Override
public void performEffect(EntityLivingBase entitylivingbase, int strength) {
if (entitylivingbase.isBurning()) {
if (entitylivingbase.isPotionActive(WizardryPotions.frost)) {
entitylivingbase.removePotionEffect(WizardryPotions.frost);
entitylivingbase.extinguish();
}
}
}
@Override
public boolean isReady(int duration, int amplifier) {
return true;
}
}
@@ -0,0 +1,31 @@
package electroblob.wizardry.potion;
import electroblob.wizardry.Wizardry;
import electroblob.wizardry.util.EntityUtils;
import net.minecraft.entity.SharedMonsterAttributes;
import net.minecraft.util.ResourceLocation;
import net.minecraftforge.fml.common.Mod;
@Mod.EventBusSubscriber
public class PotionIronflesh extends PotionMagicEffect {
public PotionIronflesh(boolean isBadEffect, int liquidColour) {
super(isBadEffect, liquidColour, new ResourceLocation(Wizardry.MODID, "textures/gui/potion_icons/ironflesh.png"));
// This needs to be here because registerPotionAttributeModifier doesn't like it if the potion has no name yet.
this.setPotionName("potion." + Wizardry.MODID + ":ironflesh");
// Only apply slowness if the setting allows it
if(Wizardry.settings.fleshSpellsCauseSlowness) {
this.registerPotionAttributeModifier(SharedMonsterAttributes.MOVEMENT_SPEED,
"fe607d55-50e3-4f4f-a959-6571503f92f4", -0.1f, EntityUtils.Operations.MULTIPLY_CUMULATIVE);
}
this.registerPotionAttributeModifier(SharedMonsterAttributes.KNOCKBACK_RESISTANCE,
"6f78206e-8dd4-4d44-9792-d7a882111951", 0.3f, EntityUtils.Operations.ADD);
this.registerPotionAttributeModifier(SharedMonsterAttributes.ARMOR,
"e1adff1e-8510-4a09-96ed-ef677cad20c1", (float)Wizardry.settings.ironFleshArmorBonus, EntityUtils.Operations.ADD);
}
@Override
public boolean isReady(int duration, int amplifier) {
return true;
}
}
@@ -0,0 +1,31 @@
package electroblob.wizardry.potion;
import electroblob.wizardry.Wizardry;
import electroblob.wizardry.util.EntityUtils;
import net.minecraft.entity.SharedMonsterAttributes;
import net.minecraft.util.ResourceLocation;
import net.minecraftforge.fml.common.Mod;
@Mod.EventBusSubscriber
public class PotionOakflesh extends PotionMagicEffect {
public PotionOakflesh(boolean isBadEffect, int liquidColour) {
super(isBadEffect, liquidColour, new ResourceLocation(Wizardry.MODID, "textures/gui/potion_icons/oakflesh.png"));
// This needs to be here because registerPotionAttributeModifier doesn't like it if the potion has no name yet.
this.setPotionName("potion." + Wizardry.MODID + ":ironflesh");
// Only apply slowness if the setting allows it
if(Wizardry.settings.fleshSpellsCauseSlowness) {
this.registerPotionAttributeModifier(SharedMonsterAttributes.MOVEMENT_SPEED,
"98b4ba66-7c50-4a4c-9f3f-40bcb37313b5", -0.1f, EntityUtils.Operations.MULTIPLY_CUMULATIVE);
}
this.registerPotionAttributeModifier(SharedMonsterAttributes.MAX_HEALTH,
"ed9d0423-60f4-4998-bd8d-dc7c33bd45b8", (float)Wizardry.settings.oakFleshHealthBonus, EntityUtils.Operations.MULTIPLY_FLAT);
this.registerPotionAttributeModifier(SharedMonsterAttributes.ARMOR,
"0b607c3f-fb14-43d7-96b5-1c1b6f6da242", (float)Wizardry.settings.oakFleshArmorBonus, EntityUtils.Operations.ADD);
}
@Override
public boolean isReady(int duration, int amplifier) {
return true;
}
}
@@ -359,7 +359,7 @@ public final class Spells {
registry.register(new GreaterHeal());
registry.register(new SpellConstruct<>("healing_aura", SpellActions.POINT_DOWN, EntityHealAura::new, false).addProperties(Spell.EFFECT_RADIUS, Spell.DAMAGE, Spell.HEALTH));
registry.register(new Forcefield());
registry.register(new SpellBuff("ironflesh", 0.4f, 0.5f, 0.6f, () -> MobEffects.RESISTANCE).soundValues(0.7f, 1.2f, 0.4f));
registry.register(new SpellBuff("ironflesh", 0.4f, 0.5f, 0.6f, () -> WizardryPotions.ironflesh).soundValues(0.7f, 1.2f, 0.4f));
registry.register(new Transience());
registry.register(new Meteor());
@@ -378,9 +378,10 @@ public final class Spells {
registry.register(new SpellMinion<>("silverfish_swarm", EntitySilverfishMinion::new).soundValues(1, 1.1f, 0.1f));
registry.register(new SpellConstructRanged<>("black_hole", EntityBlackHole::new, false).addProperties(Spell.EFFECT_RADIUS).soundValues(2, 0.7f, 0));
registry.register(new Shockwave());
registry.register(new SummonIronGolem());
registry.register(new SpellMinion<>("summon_iron_golem", EntitySpectralGolem::new).soundValues(1, 1.1f, 0.1f));
registry.register(new ArrowRain());
registry.register(new SpellBuff("diamondflesh", 0.1f, 0.7f, 1, () -> MobEffects.RESISTANCE).soundValues(0.7f, 1.2f, 0.4f));
registry.register(new SpellBuff("diamondflesh", 0.1f, 0.7f, 1, () -> WizardryPotions.diamondflesh).soundValues(0.7f, 1.2f, 0.4f));
registry.register(new SpellBuff("font_of_vitality", 1, 0.8f, 0.3f, () -> MobEffects.ABSORPTION, () -> MobEffects.REGENERATION).soundValues(0.7f, 1.2f, 0.4f));
// Wizardry 1.1 spells
@@ -398,7 +399,7 @@ public final class Spells {
registry.register(new PocketWorkbench());
registry.register(new ImbueWeapon());
registry.register(new InvigoratingPresence());
registry.register(new SpellBuff("oakflesh", 0.6f, 0.5f, 0.4f, () -> MobEffects.RESISTANCE).soundValues(0.7f, 1.2f, 0.4f));
registry.register(new SpellBuff("oakflesh", 0.6f, 0.5f, 0.4f, () -> WizardryPotions.oakflesh).soundValues(0.7f, 1.2f, 0.4f));
registry.register(new SpellProjectile<>("greater_fireball", EntityLargeMagicFireball::new).addProperties(Spell.DAMAGE, EntityLargeMagicFireball.EXPLOSION_POWER));//new GreaterFireball());
registry.register(new FlamingWeapon());
@@ -88,6 +88,7 @@ public class WizardryEntities {
registry.register(createEntry(EntitySpiritHorse.class, "spirit_horse", TrackingType.LIVING).egg(0x5464c6, 0xbcc2e8).build());
registry.register(createEntry(EntityPhoenix.class, "phoenix", TrackingType.LIVING).egg(0xff4900, 0xfde535).build());
registry.register(createEntry(EntityIceGiant.class, "ice_giant", TrackingType.LIVING).egg(0x5bacd9, 0xeffaff).build());
registry.register(createEntry(EntitySpectralGolem.class, "spectral_golem", TrackingType.LIVING).egg(0x5bacd9, 0xeffaff).build());
registry.register(createEntry(EntityMagicSlime.class, "magic_slime", TrackingType.LIVING).build());
registry.register(createEntry(EntityDecoy.class, "decoy", TrackingType.LIVING).build());
@@ -113,6 +114,7 @@ public class WizardryEntities {
registry.register(createEntry(EntityDart.class, "dart", TrackingType.PROJECTILE).build());
registry.register(createEntry(EntityIceLance.class, "ice_lance", TrackingType.PROJECTILE).build());
registry.register(createEntry(EntityFlamecatcherArrow.class, "flamecatcher_arrow", TrackingType.PROJECTILE).build());
registry.register(createEntry(EntityConjuredArrow.class, "conjured_arrow", TrackingType.PROJECTILE).build());
// Directionless projectiles
registry.register(createEntry(EntityFirebomb.class, "firebomb", TrackingType.PROJECTILE).build());
@@ -6,7 +6,11 @@ import electroblob.wizardry.loot.RandomSpell;
import electroblob.wizardry.loot.WizardSpell;
import net.minecraft.entity.EnumCreatureType;
import net.minecraft.util.ResourceLocation;
import net.minecraft.world.storage.loot.*;
import net.minecraft.world.storage.loot.LootEntry;
import net.minecraft.world.storage.loot.LootEntryTable;
import net.minecraft.world.storage.loot.LootPool;
import net.minecraft.world.storage.loot.LootTableList;
import net.minecraft.world.storage.loot.RandomValueRange;
import net.minecraft.world.storage.loot.conditions.LootCondition;
import net.minecraft.world.storage.loot.functions.LootFunctionManager;
import net.minecraftforge.event.LootTableLoadEvent;
@@ -90,23 +94,27 @@ public final class WizardryLoot {
event.getTable().addPool(getAdditive(Wizardry.MODID + ":chests/jungle_dispenser_additions", Wizardry.MODID + "_additional_dispenser_loot"));
}
// Mob drops
// Let's hope mods will play nice and store their entity loot tables under 'entities' or 'entity'
// If not, packmakers will have to sort it out themselves using the whitelist/blacklist
if(Arrays.asList(Wizardry.settings.mobLootTableWhitelist).contains(event.getName())){
event.getTable().addPool(getAdditive(Wizardry.MODID + ":entities/mob_additions", Wizardry.MODID + "_additional_mob_drops"));
}else if(!Arrays.asList(Wizardry.settings.mobLootTableBlacklist).contains(event.getName())
&& event.getName().getPath().contains("entities") || event.getName().getPath().contains("entity")){
// Get the filename of the loot table json, for well-behaved mods this will be the entity name
String[] split = event.getName().getPath().split("/");
String entityName = split[split.length - 1];
EntityEntry entry = ForgeRegistries.ENTITIES.getValue(new ResourceLocation(entityName));
if(entry == null) return; // If this is true it didn't work :(
Class entityClass = entry.getEntityClass();
if(EnumCreatureType.MONSTER.getCreatureClass().isAssignableFrom(entityClass)){
if (Wizardry.settings.injectMobDrops) {
// Let's hope mods will play nice and store their entity loot tables under 'entities' or 'entity'
// If not, packmakers will have to sort it out themselves using the whitelist/blacklist
if (Arrays.asList(Wizardry.settings.mobLootTableWhitelist).contains(event.getName())) {
event.getTable().addPool(getAdditive(Wizardry.MODID + ":entities/mob_additions", Wizardry.MODID + "_additional_mob_drops"));
} else if (!Arrays.asList(Wizardry.settings.mobLootTableBlacklist).contains(event.getName())
&& event.getName().getPath().contains("entities") || event.getName().getPath().contains("entity")) {
// Get the filename of the loot table json, for well-behaved mods this will be the entity name
String[] split = event.getName().getPath().split("/");
String entityName = split[split.length - 1];
EntityEntry entry = ForgeRegistries.ENTITIES.getValue(new ResourceLocation(entityName));
if (entry == null) {
return; // If this is true it didn't work :(
}
Class entityClass = entry.getEntityClass();
if (EnumCreatureType.MONSTER.getCreatureClass().isAssignableFrom(entityClass)) {
event.getTable().addPool(getAdditive(Wizardry.MODID + ":entities/mob_additions", Wizardry.MODID + "_additional_mob_drops"));
}
}
}
// Fishing loot
@@ -2,6 +2,7 @@ package electroblob.wizardry.registry;
import electroblob.wizardry.Wizardry;
import electroblob.wizardry.potion.*;
import electroblob.wizardry.potion.PotionFireskin;
import electroblob.wizardry.util.ParticleBuilder;
import electroblob.wizardry.util.ParticleBuilder.Type;
import net.minecraft.entity.EntityLivingBase;
@@ -57,6 +58,9 @@ public final class WizardryPotions {
public static final Potion frost_step = placeholder();
public static final Potion mark_of_sacrifice = placeholder();
public static final Potion mirage = placeholder();
public static final Potion oakflesh = placeholder();
public static final Potion ironflesh = placeholder();
public static final Potion diamondflesh = placeholder();
/**
* Sets both the registry and unlocalised names of the given potion, then registers it with the given registry. Use
@@ -92,19 +96,8 @@ public final class WizardryPotions {
}
}.setBeneficial()); // 0xffe89b
registerPotion(registry, "fireskin", new PotionMagicEffectParticles(false, 0,
new ResourceLocation(Wizardry.MODID, "textures/gui/potion_icons/fireskin.png")){
@Override
public void spawnCustomParticle(World world, double x, double y, double z){
world.spawnParticle(EnumParticleTypes.FLAME, x, y, z, 0, 0, 0);
}
@Override
public void performEffect(EntityLivingBase entitylivingbase, int strength){
entitylivingbase.extinguish(); // Stops melee mobs that are on fire from setting the player on fire,
// without allowing the player to actually stand in fire or swim in lava without taking damage.
}
}.setBeneficial()); // 0xff2f02
registerPotion(registry, "fireskin", new PotionFireskin(false, 0xff2f02,
new ResourceLocation(Wizardry.MODID, "textures/gui/potion_icons/fireskin.png")));
registerPotion(registry, "ice_shroud", new PotionMagicEffectParticles(false, 0,
new ResourceLocation(Wizardry.MODID, "textures/gui/potion_icons/ice_shroud.png")){
@@ -127,17 +120,7 @@ public final class WizardryPotions {
registerPotion(registry, "decay", new PotionDecay(true, 0x3c006c));
registerPotion(registry, "sixth_sense", new PotionMagicEffect(false, 0xc6ff01,
new ResourceLocation(Wizardry.MODID, "textures/gui/potion_icons/sixth_sense.png")){
@Override
public void performEffect(EntityLivingBase target, int strength){
// Reset the shader (a bit dirty but both the potion expiry hooks are only fired server-side, and
// there's no point sending packets unnecessarily if we can just do this instead)
if(target.getActivePotionEffect(this).getDuration() <= 1 && target.world.isRemote
&& target == net.minecraft.client.Minecraft.getMinecraft().player){
net.minecraft.client.Minecraft.getMinecraft().entityRenderer.stopUseShader();
}
}
}.setBeneficial());
new ResourceLocation(Wizardry.MODID, "textures/gui/potion_icons/sixth_sense.png")).setBeneficial());
registerPotion(registry, "arcane_jammer", new PotionMagicEffect(true, 0xcf4aa2,
new ResourceLocation(Wizardry.MODID, "textures/gui/potion_icons/arcane_jammer.png")));
@@ -201,6 +184,10 @@ public final class WizardryPotions {
public void spawnCustomParticle(World world, double x, double y, double z){} // We only want the syncing
}.setBeneficial());
registerPotion(registry, "oakflesh", new PotionOakflesh(false, 0).setBeneficial());
registerPotion(registry, "ironflesh", new PotionIronflesh(false, 0).setBeneficial());
registerPotion(registry, "diamondflesh", new PotionDiamondflesh(false, 0).setBeneficial());
}
}
@@ -13,14 +13,18 @@ import net.minecraft.tileentity.TileEntityDispenser;
import net.minecraft.util.EnumFacing;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.Vec3d;
import net.minecraft.util.text.TextComponentTranslation;
import net.minecraft.world.World;
import net.minecraftforge.event.entity.living.LivingDestroyBlockEvent;
import net.minecraftforge.event.entity.player.PlayerInteractEvent;
import net.minecraftforge.event.world.BlockEvent;
import net.minecraftforge.event.world.ExplosionEvent;
import net.minecraftforge.fml.common.Mod;
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
@Mod.EventBusSubscriber
public class ArcaneLock extends SpellRay {
@@ -167,4 +171,8 @@ public class ArcaneLock extends SpellRay {
&& event.getWorld().getTileEntity(pos).getTileData().hasUniqueId(NBT_KEY));
}
}
@@ -33,7 +33,7 @@ public class ConjureBlock extends SpellRay {
@Override
protected boolean onBlockHit(World world, BlockPos pos, EnumFacing side, Vec3d hit, EntityLivingBase caster, Vec3d origin, int ticksInUse, SpellModifiers modifiers){
if(caster.isSneaking() && world.getBlockState(pos).getBlock() == WizardryBlocks.spectral_block){
if(caster != null && caster.isSneaking() && world.getBlockState(pos).getBlock() == WizardryBlocks.spectral_block){
if(!world.isRemote){
// Dispelling of blocks
@@ -53,7 +53,7 @@ public class CurseOfSoulbinding extends SpellRay {
@Override
protected boolean onEntityHit(World world, Entity target, Vec3d hit, EntityLivingBase caster, Vec3d origin, int ticksInUse, SpellModifiers modifiers){
if(EntityUtils.isLiving(target) && caster instanceof EntityPlayer){
WizardData data = WizardData.get((EntityPlayer)caster);
if(data != null){
@@ -66,7 +66,7 @@ public class CurseOfSoulbinding extends SpellRay {
}
}
}
return true;
}
@@ -79,7 +79,7 @@ public class CurseOfSoulbinding extends SpellRay {
protected boolean onMiss(World world, EntityLivingBase caster, Vec3d origin, Vec3d direction, int ticksInUse, SpellModifiers modifiers){
return true;
}
@Override
protected void spawnParticle(World world, double x, double y, double z, double vx, double vy, double vz){
ParticleBuilder.create(Type.DARK_MAGIC).pos(x, y, z).clr(0.4f, 0, 0).spawn(world);
@@ -103,9 +103,9 @@ public class CurseOfSoulbinding extends SpellRay {
Entity entity = EntityUtils.getEntityByUUID(player.world, iterator.next());
if(entity == null) iterator.remove();
if(entity instanceof EntityLivingBase){
if (entity == null || (entity instanceof EntityLivingBase && !((EntityLivingBase) entity).isPotionActive(WizardryPotions.curse_of_soulbinding))) {
iterator.remove();
} else if (entity instanceof EntityLivingBase) {
// Retaliatory effect
if(DamageSafetyChecker.attackEntitySafely(entity, MagicDamage.causeDirectMagicDamage(player,
MagicDamage.DamageType.MAGIC, true), event.getAmount(), event.getSource().getDamageType(),
@@ -5,6 +5,7 @@ import electroblob.wizardry.constants.Constants;
import electroblob.wizardry.entity.living.ISpellCaster;
import electroblob.wizardry.event.SpellCastEvent;
import electroblob.wizardry.item.SpellActions;
import electroblob.wizardry.registry.Spells;
import electroblob.wizardry.registry.WizardryItems;
import electroblob.wizardry.registry.WizardryPotions;
import electroblob.wizardry.util.ParticleBuilder;
@@ -24,11 +25,14 @@ import javax.annotation.Nullable;
@Mod.EventBusSubscriber
public class EmpoweringPresence extends SpellAreaEffect {
/** The fraction by which potency is increased per level of the empowerment effect. */
public static final String POTENCY_PER_LEVEL = "potency_per_level";
public EmpoweringPresence(){
super("empowering_presence", SpellActions.POINT_UP, false);
this.alwaysSucceed(true);
this.targetAllies(true);
addProperties(EFFECT_DURATION, EFFECT_STRENGTH);
addProperties(EFFECT_DURATION, EFFECT_STRENGTH, POTENCY_PER_LEVEL);
}
@Override
@@ -62,7 +66,7 @@ public class EmpoweringPresence extends SpellAreaEffect {
if(event.getCaster() != null && event.getCaster().isPotionActive(WizardryPotions.empowerment)
&& !(event.getSpell() instanceof EmpoweringPresence)){ // Prevent exponential empowerment stacking!
float potency = 1 + Constants.EMPOWERMENT_POTENCY_PER_LEVEL
float potency = 1 + Spells.empowering_presence.getProperty(POTENCY_PER_LEVEL).floatValue()
* (event.getCaster().getActivePotionEffect(WizardryPotions.empowerment).getAmplifier() + 1);
event.getModifiers().set(SpellModifiers.POTENCY,
@@ -4,7 +4,7 @@ import electroblob.wizardry.item.SpellActions;
import electroblob.wizardry.util.ParticleBuilder;
import electroblob.wizardry.util.ParticleBuilder.Type;
import electroblob.wizardry.util.SpellModifiers;
import net.minecraft.entity.EntityCreature;
import net.minecraft.entity.EntityLiving;
import net.minecraft.entity.EntityLivingBase;
import net.minecraft.tileentity.TileEntityDispenser;
import net.minecraft.util.math.Vec3d;
@@ -29,7 +29,7 @@ public class Enrage extends SpellAreaEffect {
@Override
protected boolean affectEntity(World world, Vec3d origin, @Nullable EntityLivingBase caster, EntityLivingBase target, int targetCount, int ticksInUse, SpellModifiers modifiers){
if(caster != null && target instanceof EntityCreature){
if(caster != null && target instanceof EntityLiving){
target.setRevengeTarget(caster); // Yours truly, angry mobs
}
@@ -53,7 +53,7 @@ public class FireBreath extends SpellRay {
this.getNameForTranslationFormatted()), true);
// This now only damages in line with the maxHurtResistantTime. Some mods don't play nicely and fiddle
// with this mechanic for their own purposes, so this line makes sure that doesn't affect wizardry.
}else if(ticksInUse % ((EntityLivingBase)target).maxHurtResistantTime == 1){
}else if(ticksInUse % 10 == 0){
target.setFire((int)(getProperty(BURN_DURATION).floatValue() * modifiers.get(WizardryItems.duration_upgrade)));
EntityUtils.attackEntityWithoutKnockback(target,
MagicDamage.causeDirectMagicDamage(caster, DamageType.FIRE),
@@ -56,7 +56,7 @@ public class FlameRay extends SpellRay {
this.getNameForTranslationFormatted()), true);
// This now only damages in line with the maxHurtResistantTime. Some mods don't play nicely and fiddle
// with this mechanic for their own purposes, so this line makes sure that doesn't affect wizardry.
}else if(ticksInUse % ((EntityLivingBase)target).maxHurtResistantTime == 1){
}else if(ticksInUse % 10 == 0){
target.setFire((int)(getProperty(BURN_DURATION).floatValue() * modifiers.get(WizardryItems.duration_upgrade)));
EntityUtils.attackEntityWithoutKnockback(target,
MagicDamage.causeDirectMagicDamage(caster, DamageType.FIRE),

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