Compare commits

..

65 Commits

Author SHA1 Message Date
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
232 changed files with 10478 additions and 7419 deletions
+2 -2
View File
@@ -1,7 +1,7 @@
{
"homepage": "https://www.curseforge.com/minecraft/mc-mods/electroblobs-wizardry",
"promos": {
"1.12.2-latest": "4.3.7",
"1.12.2-recommended": "4.3.7"
"1.12.2-latest": "4.3.15",
"1.12.2-recommended": "4.3.15"
}
}
+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.8"
version = "4.3.15"
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
+2 -2
View File
@@ -1,5 +1,5 @@
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-4.9-bin.zip
zipStorePath=wrapper/dists
Vendored
+175 -115
View File
@@ -1,172 +1,232 @@
#!/usr/bin/env sh
#!/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/.
#
##############################################################################
# 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
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
SAVED="`pwd`"
cd "`dirname \"$PRG\"`/" >/dev/null
APP_HOME="`pwd -P`"
cd "$SAVED" >/dev/null
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=""
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 () {
echo "$*"
}
} >&2
die () {
echo
echo "$*"
echo
exit 1
}
} >&2
# OS specific support (must be 'true' or 'false').
cygwin=false
msys=false
darwin=false
nonstop=false
case "`uname`" in
CYGWIN* )
cygwin=true
;;
Darwin* )
darwin=true
;;
MINGW* )
msys=true
;;
NONSTOP* )
nonstop=true
;;
case "$( uname )" in #(
CYGWIN* ) cygwin=true ;; #(
Darwin* ) darwin=true ;; #(
MSYS* | MINGW* ) msys=true ;; #(
NONSTOP* ) nonstop=true ;;
esac
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
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
location of your Java installation."
fi
# Increase the maximum file descriptors if we can.
if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "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"`
JAVACMD=`cygpath --unix "$JAVACMD"`
# 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
# Escape application args
save () {
for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
echo " "
}
APP_ARGS=$(save "$@")
# 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.
# Collect all arguments for the java command, following the shell quoting and substitution rules
eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
# 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" )
# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
cd "$(dirname "$0")"
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
exec "$JAVACMD" "$@"
# 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
+25 -20
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
@@ -13,15 +29,18 @@ 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=
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.
@@ -35,7 +54,7 @@ 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%
@@ -45,28 +64,14 @@ echo location of your Java installation.
goto fail
:init
@rem Get command-line arguments, handling Windows variants
if not "%OS%" == "Windows_NT" goto win9xME_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=%*
: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
@@ -81,4 +86,4 @@ exit /b 1
:mainEnd
if "%OS%"=="Windows_NT" endlocal
:omega
:omega
@@ -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,14 +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.
@@ -243,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
@@ -316,6 +359,34 @@ public final class Settings {
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.
@@ -382,6 +453,8 @@ public final class Settings {
/** <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;
@@ -508,9 +581,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);
@@ -617,6 +713,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");
@@ -668,6 +843,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);
@@ -734,6 +916,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.
@@ -819,6 +1117,14 @@ public final class Settings {
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);
@@ -926,6 +1232,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);
@@ -1023,6 +1380,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);
@@ -1114,13 +1487,19 @@ public final class Settings {
Wizardry.proxy.setToNamedBooleanEntry(property);
showChargeMeter = 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");
@@ -1282,6 +1661,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");
@@ -82,7 +82,7 @@ public class Wizardry {
* 1.x.x represents Minecraft 1.7.x versions, 2.x.x represents Minecraft 1.10.x versions, 3.x.x represents Minecraft
* 1.11.x versions, and so on.
*/
public static final String VERSION = "4.3.7";
public static final String VERSION = "4.3.15";
/**
* Json file used by Forge's built-in <a href="https://mcforge.readthedocs.io/en/1.12.x/gettingstarted/autoupdate/">update checker</a>.
@@ -130,7 +130,6 @@ public class Wizardry {
configDirectory = new File(event.getModConfigurationDirectory(), Wizardry.MODID);
settings.initConfig(event);
proxy.registerResourceReloadListeners();
Calendar calendar = Calendar.getInstance();
tisTheSeason = calendar.get(Calendar.MONTH) + 1 == 12 && calendar.get(Calendar.DAY_OF_MONTH) >= 24
@@ -161,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
@@ -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)
@@ -491,4 +490,4 @@ public final class WizardryEventHandler {
}
}
}
}
@@ -1,8 +0,0 @@
package electroblob.wizardry.api;
import electroblob.wizardry.constants.Element;
public interface IElemental {
Element getElement();
}
@@ -18,7 +18,7 @@ public final class WizardryEnumHelper {
// Make sure these are updated if the relevant constructors are updated!
// Can't we do some kind of reflection to access them and generate these arrays?
private static final Class[] TIER_ARGUMENTS = new Class[]{Integer.class, Integer.class, Integer.class, Style.class, String.class};
private static final Class[] ELEMENT_ARGUMENTS = new Class[]{Style.class, String.class, String.class, boolean.class, boolean.class};
private static final Class[] ELEMENT_ARGUMENTS = new Class[]{Style.class, String.class, String.class};
private static final Class[] SPELL_TYPE_ARGUMENTS = new Class[]{String.class};
private static final Class[] SPELL_CONTEXT_ARGUMENTS = new Class[]{String.class};
@@ -55,14 +55,12 @@ public final class WizardryEnumHelper {
* @param colour The colour of text associated with this element, as a style object.
* @param name The unlocalised name of this element, as used in translation keys.
* @param modID The mod ID of the mod that added this element, for icon rendering purposes.
* @param worldgen Whether this element should have obelisks and shrines.
* @param wizards Whether this element should have naturally occurring evil and good wizards.
* @return The resulting {@code Element} enum constant.
*/
// This is the only one that needs the mod ID argument because elements are the only ones that have icons
// For some reason Minecraft doesn't seem to care about the resource domain for lang files, it just pools them
public static Element addElement(String codeName, Style colour, String name, String modID, boolean worldgen, boolean wizards){
return EnumHelper.addEnum(Element.class, codeName, ELEMENT_ARGUMENTS, colour, name, modID, worldgen, wizards);
public static Element addElement(String codeName, Style colour, String name, String modID){
return EnumHelper.addEnum(Element.class, codeName, ELEMENT_ARGUMENTS, colour, name, modID);
}
/**
@@ -256,9 +256,7 @@ public class BlockBookshelf extends BlockHorizontal implements ITileEntityProvid
// Wizardry books
registerBookModelTexture(() -> WizardryItems.spell_book, new ResourceLocation(Wizardry.MODID, "blocks/books_red"));
registerBookModelTexture(() -> WizardryItems.wizard_handbook, new ResourceLocation(Wizardry.MODID, "blocks/books_blue"));
registerBookModelTexture(() -> WizardryItems.arcane_tome_apprentice,new ResourceLocation(Wizardry.MODID, "blocks/books_purple"));
registerBookModelTexture(() -> WizardryItems.arcane_tome_advanced, new ResourceLocation(Wizardry.MODID, "blocks/books_purple"));
registerBookModelTexture(() -> WizardryItems.arcane_tome_master, new ResourceLocation(Wizardry.MODID, "blocks/books_purple"));
registerBookModelTexture(() -> WizardryItems.arcane_tome, new ResourceLocation(Wizardry.MODID, "blocks/books_purple"));
registerBookModelTexture(() -> WizardryItems.ruined_spell_book, new ResourceLocation(Wizardry.MODID, "blocks/books_brown"));
registerBookModelTexture(() -> WizardryItems.scroll, new ResourceLocation(Wizardry.MODID, "blocks/scrolls_blue"));
registerBookModelTexture(() -> WizardryItems.blank_scroll, new ResourceLocation(Wizardry.MODID, "blocks/scrolls_blue"));
@@ -1,23 +1,29 @@
package electroblob.wizardry.block;
import electroblob.wizardry.api.IElemental;
import electroblob.wizardry.constants.Element;
import electroblob.wizardry.registry.WizardryTabs;
import net.minecraft.block.Block;
import net.minecraft.block.material.MapColor;
import net.minecraft.block.material.Material;
import net.minecraft.block.properties.PropertyEnum;
import net.minecraft.block.state.BlockStateContainer;
import net.minecraft.block.state.IBlockState;
import net.minecraft.creativetab.CreativeTabs;
import net.minecraft.item.ItemStack;
import net.minecraft.util.NonNullList;
import net.minecraft.util.math.BlockPos;
import net.minecraft.world.IBlockAccess;
import java.util.EnumMap;
public class BlockCrystal extends Block implements IElemental {
public class BlockCrystal extends Block {
private static final EnumMap<Element, MapColor> map_colours = new EnumMap<>(Element.class);
public static final PropertyEnum<Element> ELEMENT = PropertyEnum.create("element", Element.class);
static {
map_colours.put(Element.MAGIC, MapColor.PINK);
private static final EnumMap<Element, MapColor> map_colours = new EnumMap<>(Element.class);
static {
map_colours.put(Element.MAGIC, MapColor.PINK);
map_colours.put(Element.FIRE, MapColor.ORANGE_STAINED_HARDENED_CLAY);
map_colours.put(Element.ICE, MapColor.LIGHT_BLUE);
map_colours.put(Element.LIGHTNING, MapColor.CYAN);
@@ -26,22 +32,45 @@ public class BlockCrystal extends Block implements IElemental {
map_colours.put(Element.SORCERY, MapColor.LIME);
map_colours.put(Element.HEALING, MapColor.YELLOW);
}
@Override
public Element getElement() { return element; }
private final Element element;
public BlockCrystal(Element element) {
super(Material.IRON);
this.setCreativeTab(WizardryTabs.WIZARDRY);
public BlockCrystal(Material material){
super(material);
this.setDefaultState(this.blockState.getBaseState().withProperty(ELEMENT, Element.MAGIC));
this.setCreativeTab(WizardryTabs.WIZARDRY);
this.setHarvestLevel("pickaxe", 2);
this.element = element;
}
@Override
public int damageDropped(IBlockState state){
return (state.getValue(ELEMENT)).ordinal();
}
@Override
public MapColor getMapColor(IBlockState state, IBlockAccess world, BlockPos pos){
return map_colours.get(state.getProperties().get(ELEMENT));
}
@Override
public MapColor getMapColor(IBlockState state, IBlockAccess world, BlockPos pos) {
return map_colours.get(state.getProperties().get(element));
public void getSubBlocks(CreativeTabs tab, NonNullList<ItemStack> items){
if(this.getCreativeTab() == tab){
for(Element element : Element.values()){
items.add(new ItemStack(this, 1, element.ordinal()));
}
}
}
@Override
public IBlockState getStateFromMeta(int metadata){
return this.getDefaultState().withProperty(ELEMENT, Element.values()[metadata]);
}
@Override
public int getMetaFromState(IBlockState state){
return (state.getValue(ELEMENT)).ordinal();
}
@Override
protected BlockStateContainer createBlockState(){
return new BlockStateContainer(this, ELEMENT);
}
}
@@ -1,30 +1,17 @@
package electroblob.wizardry.block;
import electroblob.wizardry.registry.WizardryTabs;
import net.minecraft.block.Block;
import net.minecraft.block.BlockPlanks;
import net.minecraft.block.SoundType;
import net.minecraft.block.material.MapColor;
import net.minecraft.block.material.Material;
import net.minecraft.block.properties.IProperty;
import net.minecraft.block.properties.PropertyEnum;
import net.minecraft.block.state.BlockStateContainer;
import net.minecraft.block.state.IBlockState;
import net.minecraft.creativetab.CreativeTabs;
import net.minecraft.item.ItemStack;
import net.minecraft.util.IStringSerializable;
import net.minecraft.util.NonNullList;
import net.minecraft.util.math.BlockPos;
import net.minecraft.world.IBlockAccess;
public class BlockGildedWood extends Block {
public class BlockGildedWood extends BlockPlanks {
public BlockGildedWood()
{
super(Material.WOOD);
public BlockGildedWood(){
super();
this.setCreativeTab(WizardryTabs.WIZARDRY);
this.setHardness(2.0F);
this.setResistance(5.0F);
this.setSoundType(SoundType.WOOD); // Why is this protected?!
}
}
}
@@ -0,0 +1,140 @@
package electroblob.wizardry.block;
import electroblob.wizardry.Wizardry;
import electroblob.wizardry.constants.Element;
import electroblob.wizardry.registry.WizardryTabs;
import electroblob.wizardry.tileentity.TileEntityShrineCore;
import net.minecraft.block.Block;
import net.minecraft.block.ITileEntityProvider;
import net.minecraft.block.material.MapColor;
import net.minecraft.block.material.Material;
import net.minecraft.block.properties.PropertyBool;
import net.minecraft.block.properties.PropertyEnum;
import net.minecraft.block.state.BlockStateContainer;
import net.minecraft.block.state.IBlockState;
import net.minecraft.creativetab.CreativeTabs;
import net.minecraft.entity.Entity;
import net.minecraft.item.ItemStack;
import net.minecraft.tileentity.TileEntity;
import net.minecraft.util.BlockRenderLayer;
import net.minecraft.util.NonNullList;
import net.minecraft.util.math.BlockPos;
import net.minecraft.world.Explosion;
import net.minecraft.world.IBlockAccess;
import net.minecraft.world.World;
import javax.annotation.Nullable;
import java.util.Arrays;
import java.util.EnumMap;
public class BlockPedestal extends Block implements ITileEntityProvider {
public static final PropertyEnum<Element> ELEMENT = PropertyEnum.create("element", Element.class,
Arrays.copyOfRange(Element.values(), 1, Element.values().length)); // Everything except MAGIC
// A 'natural' pedestal is one that was generated as part of a structure, is unbreakable and has a tileentity
public static final PropertyBool NATURAL = PropertyBool.create("natural");
private static final EnumMap<Element, MapColor> map_colours = new EnumMap<>(Element.class);
static {
map_colours.put(Element.FIRE, MapColor.RED_STAINED_HARDENED_CLAY);
map_colours.put(Element.ICE, MapColor.LIGHT_BLUE_STAINED_HARDENED_CLAY);
map_colours.put(Element.LIGHTNING, MapColor.CYAN_STAINED_HARDENED_CLAY);
map_colours.put(Element.NECROMANCY, MapColor.PURPLE_STAINED_HARDENED_CLAY);
map_colours.put(Element.EARTH, MapColor.BROWN_STAINED_HARDENED_CLAY);
map_colours.put(Element.SORCERY, MapColor.GRAY);
map_colours.put(Element.HEALING, MapColor.YELLOW_STAINED_HARDENED_CLAY);
}
public BlockPedestal(Material material){
super(material);
this.setDefaultState(this.blockState.getBaseState().withProperty(ELEMENT, Element.FIRE).withProperty(NATURAL, false));
this.setCreativeTab(WizardryTabs.WIZARDRY);
this.setHardness(1.5F);
this.setResistance(10.0F);
}
@Override
public int damageDropped(IBlockState state){
return state.getValue(ELEMENT).ordinal(); // Ignore the NATURAL state here, it's unobtainable
}
@Override
public MapColor getMapColor(IBlockState state, IBlockAccess world, BlockPos pos){
return map_colours.get(state.getProperties().get(ELEMENT));
}
@Override
public void getSubBlocks(CreativeTabs tab, NonNullList<ItemStack> items){
// Ignore the NATURAL state here, it's unobtainable
if(this.getCreativeTab() == tab){
for(Element element : Arrays.copyOfRange(Element.values(), 1, Element.values().length)){
items.add(new ItemStack(this, 1, element.ordinal()));
}
}
}
@Override
public BlockRenderLayer getRenderLayer(){
return BlockRenderLayer.CUTOUT; // Required to shade parts of the block faces differently to others
}
@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);
}
@Override
public boolean hasTileEntity(IBlockState state){
return state.getValue(NATURAL); // Only naturally-generated pedestals have a (shrine core) tile entity
}
@Nullable
@Override
public TileEntity createNewTileEntity(World world, int meta){
return new TileEntityShrineCore();
}
@Override
public IBlockState getStateFromMeta(int metadata){
boolean natural = false;
if(metadata > ELEMENT.getAllowedValues().size()){
natural = true;
metadata -= ELEMENT.getAllowedValues().size();
}
Element element = Element.values()[metadata];
if(!ELEMENT.getAllowedValues().contains(element)) return this.getDefaultState().withProperty(NATURAL, natural);
return this.getDefaultState().withProperty(ELEMENT, element).withProperty(NATURAL, natural);
}
@Override
public int getMetaFromState(IBlockState state){
return state.getValue(ELEMENT).ordinal() + (state.getValue(NATURAL) ? ELEMENT.getAllowedValues().size() : 0);
}
@Override
protected BlockStateContainer createBlockState(){
return new BlockStateContainer(this, ELEMENT, NATURAL);
}
}
@@ -1,7 +1,6 @@
package electroblob.wizardry.block;
import com.google.common.collect.Maps;
import electroblob.wizardry.Wizardry;
import electroblob.wizardry.constants.Element;
import electroblob.wizardry.item.ItemSpectralDust;
import electroblob.wizardry.registry.*;
@@ -21,14 +20,12 @@ import net.minecraft.tileentity.TileEntity;
import net.minecraft.util.EnumFacing;
import net.minecraft.util.EnumHand;
import net.minecraft.util.NonNullList;
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.util.math.Vec3d;
import net.minecraft.world.IBlockAccess;
import net.minecraft.world.World;
import net.minecraftforge.fml.common.registry.ForgeRegistries;
import javax.annotation.Nullable;
import java.util.Arrays;
@@ -113,7 +110,7 @@ public class BlockReceptacle extends BlockTorch implements ITileEntityProvider {
if(tileEntity instanceof TileEntityReceptacle){
Element element = ((TileEntityReceptacle)tileEntity).getElement();
if(element != null) drops.add(new ItemStack(ForgeRegistries.ITEMS.getValue(new ResourceLocation(Wizardry.MODID, "spectral_dust_" + element.name().toLowerCase())), 1));
if(element != null) drops.add(new ItemStack(WizardryItems.spectral_dust, 1, element.ordinal()));
}
}
@@ -222,7 +219,7 @@ public class BlockReceptacle extends BlockTorch implements ITileEntityProvider {
((TileEntityReceptacle)tileEntity).setElement(null);
ItemStack dust = new ItemStack(ForgeRegistries.ITEMS.getValue(new ResourceLocation(Wizardry.MODID, "spectral_dust_" + currentElement.name().toLowerCase())));
ItemStack dust = new ItemStack(WizardryItems.spectral_dust, 1, currentElement.ordinal());
if(stack.isEmpty()){
player.setHeldItem(hand, dust);
@@ -5,15 +5,24 @@ import electroblob.wizardry.registry.WizardryTabs;
import net.minecraft.block.Block;
import net.minecraft.block.material.MapColor;
import net.minecraft.block.material.Material;
import net.minecraft.block.properties.PropertyEnum;
import net.minecraft.block.state.BlockStateContainer;
import net.minecraft.block.state.IBlockState;
import net.minecraft.creativetab.CreativeTabs;
import net.minecraft.item.ItemStack;
import net.minecraft.util.BlockRenderLayer;
import net.minecraft.util.NonNullList;
import net.minecraft.util.math.BlockPos;
import net.minecraft.world.IBlockAccess;
import java.util.Arrays;
import java.util.EnumMap;
public class BlockRunestone extends Block {
public static final PropertyEnum<Element> ELEMENT = PropertyEnum.create("element", Element.class,
Arrays.copyOfRange(Element.values(), 1, Element.values().length)); // Everything except MAGIC
private static final EnumMap<Element, MapColor> map_colours = new EnumMap<>(Element.class);
static {
@@ -25,24 +34,54 @@ public class BlockRunestone extends Block {
map_colours.put(Element.SORCERY, MapColor.GRAY);
map_colours.put(Element.HEALING, MapColor.YELLOW_STAINED_HARDENED_CLAY);
}
private final Element element;
public BlockRunestone(Material material, Element element) {
public BlockRunestone(Material material){
super(material);
this.setCreativeTab(WizardryTabs.WIZARDRY);
this.setDefaultState(this.blockState.getBaseState().withProperty(ELEMENT, Element.FIRE));
this.setCreativeTab(WizardryTabs.WIZARDRY);
this.setHardness(1.5F);
this.setResistance(10.0F);
this.element = element;
}
@Override
public int damageDropped(IBlockState state){
return state.getValue(ELEMENT).ordinal();
}
@Override
public MapColor getMapColor(IBlockState state, IBlockAccess world, BlockPos pos){
return map_colours.get(state.getProperties().get(ELEMENT));
}
@Override
public void getSubBlocks(CreativeTabs tab, NonNullList<ItemStack> items){
if(this.getCreativeTab() == tab){
for(Element element : Arrays.copyOfRange(Element.values(), 1, Element.values().length)){
items.add(new ItemStack(this, 1, element.ordinal()));
}
}
}
public Element getElement() { return element; }
@Override
public MapColor getMapColor(IBlockState state, IBlockAccess world, BlockPos pos) { return map_colours.get(element); }
@Override
public BlockRenderLayer getRenderLayer() {
public BlockRenderLayer getRenderLayer(){
return BlockRenderLayer.CUTOUT; // Required to shade parts of the block faces differently to others
}
@Override
public IBlockState getStateFromMeta(int metadata){
Element element = Element.values()[metadata];
if(!ELEMENT.getAllowedValues().contains(element)) return this.getDefaultState();
return this.getDefaultState().withProperty(ELEMENT, element);
}
@Override
public int getMetaFromState(IBlockState state){
return state.getValue(ELEMENT).ordinal();
}
@Override
protected BlockStateContainer createBlockState(){
return new BlockStateContainer(this, ELEMENT);
}
}
@@ -1,104 +0,0 @@
package electroblob.wizardry.block;
import electroblob.wizardry.api.IElemental;
import electroblob.wizardry.constants.Element;
import electroblob.wizardry.registry.WizardryTabs;
import electroblob.wizardry.tileentity.TileEntityShrineCore;
import net.minecraft.block.Block;
import net.minecraft.block.ITileEntityProvider;
import net.minecraft.block.material.MapColor;
import net.minecraft.block.material.Material;
import net.minecraft.block.properties.PropertyBool;
import net.minecraft.block.state.BlockStateContainer;
import net.minecraft.block.state.IBlockState;
import net.minecraft.entity.Entity;
import net.minecraft.tileentity.TileEntity;
import net.minecraft.util.BlockRenderLayer;
import net.minecraft.util.math.BlockPos;
import net.minecraft.world.Explosion;
import net.minecraft.world.IBlockAccess;
import net.minecraft.world.World;
import javax.annotation.Nullable;
import java.util.EnumMap;
public class BlockRunestonePedestal extends Block implements ITileEntityProvider, IElemental {
// A 'natural' pedestal is one that was generated as part of a structure, is unbreakable and has a tileentity
public static final PropertyBool NATURAL = PropertyBool.create("natural");
private static final EnumMap<Element, MapColor> map_colours = new EnumMap<>(Element.class);
static {
map_colours.put(Element.FIRE, MapColor.RED_STAINED_HARDENED_CLAY);
map_colours.put(Element.ICE, MapColor.LIGHT_BLUE_STAINED_HARDENED_CLAY);
map_colours.put(Element.LIGHTNING, MapColor.CYAN_STAINED_HARDENED_CLAY);
map_colours.put(Element.NECROMANCY, MapColor.PURPLE_STAINED_HARDENED_CLAY);
map_colours.put(Element.EARTH, MapColor.BROWN_STAINED_HARDENED_CLAY);
map_colours.put(Element.SORCERY, MapColor.GRAY);
map_colours.put(Element.HEALING, MapColor.YELLOW_STAINED_HARDENED_CLAY);
}
private final Element element;
public BlockRunestonePedestal(Material material, Element element) {
super(material);
this.setDefaultState(this.blockState.getBaseState().withProperty(NATURAL, false));
this.setCreativeTab(WizardryTabs.WIZARDRY);
this.setHardness(1.5F);
this.setResistance(10.0F);
this.element = element;
}
@Override
public Element getElement() {
return element;
}
@Override
public MapColor getMapColor(IBlockState state, IBlockAccess world, BlockPos pos) {
return map_colours.get(element);
}
@Override
public BlockRenderLayer getRenderLayer() {
return BlockRenderLayer.CUTOUT; // Required to shade parts of the block faces differently to others
}
@Override
public float getBlockHardness(IBlockState state, World world, BlockPos pos) {
return state.getValue(NATURAL) ? -1 : super.getBlockHardness(state, world, pos);
}
@Override
public float getExplosionResistance(World world, BlockPos pos, @Nullable Entity exploder, Explosion explosion) {
return world.getBlockState(pos).getValue(NATURAL) ? 6000000.0F : super.getExplosionResistance(world, pos, exploder, explosion);
}
@Override
public boolean hasTileEntity(IBlockState state) {
return state.getValue(NATURAL); // Only naturally-generated pedestals have a (shrine core) tile entity
}
@Nullable
@Override
public TileEntity createNewTileEntity(World world, int meta) {
return new TileEntityShrineCore();
}
@Override
public IBlockState getStateFromMeta(int meta) {
return this.getDefaultState().withProperty(NATURAL, meta == 1);
}
@Override
public int getMetaFromState(IBlockState state) {
return state.getValue(NATURAL) ? 1 : 0;
}
@Override
protected BlockStateContainer createBlockState() {
return new BlockStateContainer(this, NATURAL);
}
}
@@ -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;
@@ -3,10 +3,8 @@ package electroblob.wizardry.client;
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;
@@ -15,7 +13,9 @@ import electroblob.wizardry.spell.SixthSense;
import electroblob.wizardry.spell.SlowTime;
import electroblob.wizardry.spell.Transience;
import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.GuiMainMenu;
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,10 +26,7 @@ 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;
@@ -120,6 +117,7 @@ public final class WizardryClientEventHandler {
Minecraft.getMinecraft().player.prevRotationPitch = 0;
Minecraft.getMinecraft().player.rotationYaw = 0;
Minecraft.getMinecraft().player.rotationPitch = 0;
}
}
@@ -133,10 +131,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 +185,17 @@ 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);
}
}
}
/**
* 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);
}
}
@@ -1006,4 +1030,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
@@ -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
@@ -576,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);
}
}
}
@@ -299,7 +299,7 @@ class Section {
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().getLanguageCode());
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
@@ -1,9 +1,13 @@
package electroblob.wizardry.client.model;
import electroblob.wizardry.Wizardry;
import electroblob.wizardry.block.BlockRunestonePedestal;
import electroblob.wizardry.block.BlockCrystal;
import electroblob.wizardry.block.BlockPedestal;
import electroblob.wizardry.block.BlockRunestone;
import electroblob.wizardry.item.IMultiTexturedItem;
import electroblob.wizardry.item.ItemBlockMultiTextured;
import electroblob.wizardry.item.ItemCrystal;
import electroblob.wizardry.item.ItemSpectralDust;
import electroblob.wizardry.registry.WizardryBlocks;
import electroblob.wizardry.registry.WizardryItems;
import net.minecraft.block.BlockPlanks;
@@ -46,12 +50,35 @@ public final class WizardryModels {
@SubscribeEvent
public static void register(ModelRegistryEvent event){
// ItemBlocks
ModelLoader.setCustomStateMapper(WizardryBlocks.crystal_block, new StateMap.Builder()
.withName(BlockCrystal.ELEMENT).withSuffix("_crystal_block").build());
// Yay unchecked casting! But we know it's always ok here, and it makes everything much neater.
ItemBlockMultiTextured crystalBlockItem = (ItemBlockMultiTextured)Item.getItemFromBlock(WizardryBlocks.crystal_block);
registerMultiTexturedModel(crystalBlockItem);
ModelLoader.setCustomStateMapper(WizardryBlocks.runestone, new StateMap.Builder()
.withName(BlockRunestone.ELEMENT).withSuffix("_runestone").build());
ItemBlockMultiTextured runestoneItem = (ItemBlockMultiTextured)Item.getItemFromBlock(WizardryBlocks.runestone);
registerMultiTexturedModel(runestoneItem);
ModelLoader.setCustomStateMapper(WizardryBlocks.runestone_pedestal, new StateMap.Builder()
.withName(BlockPedestal.ELEMENT).ignore(BlockPedestal.NATURAL).withSuffix("_runestone_pedestal").build()); // Don't care about NATURAL property
ItemBlockMultiTextured pedestalItem = (ItemBlockMultiTextured)Item.getItemFromBlock(WizardryBlocks.runestone_pedestal);
registerMultiTexturedModel(pedestalItem);
ModelLoader.setCustomStateMapper(WizardryBlocks.gilded_wood, new StateMap.Builder()
.withName(BlockPlanks.VARIANT).withSuffix("_gilded_wood").build());
ItemBlockMultiTextured gildedWoodItem = (ItemBlockMultiTextured)Item.getItemFromBlock(WizardryBlocks.gilded_wood);
registerMultiTexturedModel(gildedWoodItem);
// Explanation for all this here -> https://github.com/TheGreyGhost/MinecraftByExample/tree/master/src/main/java/minecraftbyexample/mbe05_block_dynamic_block_model2
ModelLoaderRegistry.registerLoader(new ModelLoaderBookshelf());
// Items
//registerMultiTexturedModel((ItemCrystal)WizardryItems.magic_crystal);
registerMultiTexturedModel((ItemCrystal)WizardryItems.magic_crystal);
registerWandModel(WizardryItems.magic_wand);
registerWandModel(WizardryItems.apprentice_wand);
@@ -94,6 +121,8 @@ public final class WizardryModels {
registerWandModel(WizardryItems.master_sorcery_wand);
registerWandModel(WizardryItems.master_healing_wand);
registerMultiTexturedModel((ItemSpectralDust)WizardryItems.spectral_dust);
// Automatic item model registry
for(Item item : Item.REGISTRY){
if(!registeredItems.contains(item) && item.getRegistryName().getNamespace().equals(Wizardry.MODID)){
@@ -42,8 +42,8 @@ public class LayerDiamond extends LayerTiledOverlay<EntityLivingBase> {
int j = entity.getBrightnessForRender();
int k = j % 65536;
int l = j / 65536;
OpenGlHelper.setLightmapTextureCoords(OpenGlHelper.lightmapTexUnit, (float) k, (float) l);
int l = j / 65536;
OpenGlHelper.setLightmapTextureCoords(OpenGlHelper.lightmapTexUnit, (float) k, (float) l);
super.doRenderLayer(entity, limbSwing, limbSwingAmount, partialTicks, ageInTicks, netHeadYaw, headPitch, scale);
@@ -43,7 +43,7 @@ public class LayerOak extends LayerTiledOverlay<EntityLivingBase> {
int j = entity.getBrightnessForRender();
int k = j % 65536;
int l = j / 65536;
OpenGlHelper.setLightmapTextureCoords(OpenGlHelper.lightmapTexUnit, (float) k, (float) l);
OpenGlHelper.setLightmapTextureCoords(OpenGlHelper.lightmapTexUnit, (float) k, (float) l);
super.doRenderLayer(entity, limbSwing, limbSwingAmount, partialTicks, ageInTicks, netHeadYaw, headPitch, scale);
@@ -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,44 +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;
// 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;
}
}
@@ -30,23 +30,14 @@ public enum Element implements IStringSerializable {
/** The {@link ResourceLocation} for this element's 8x8 icon (displayed in the arcane workbench GUI) */
private final ResourceLocation icon;
private String modid;
/** true if this element should have worldgen structures generated (Obelisk, Shrine) */
private final boolean worldgen;
/** true if evil/good wizards of this element should naturally spawn */
private final boolean wizards;
Element(Style colour, String name){
this(colour, name, Wizardry.MODID, true, true);
this(colour, name, Wizardry.MODID);
}
Element(Style colour, String name, String modid, boolean worldgen, boolean wizards){
Element(Style colour, String name, String modid){
this.colour = colour;
this.unlocalisedName = name;
this.icon = new ResourceLocation(modid, "textures/gui/container/element_icon_" + unlocalisedName + ".png");
this.modid = modid;
this.worldgen = worldgen;
this.wizards = wizards;
}
/** Returns the element with the given name, or throws an {@link java.lang.IllegalArgumentException} if no such
@@ -102,10 +93,4 @@ public enum Element implements IStringSerializable {
public ResourceLocation getIcon(){
return icon;
}
public boolean hasWorldgen() { return worldgen; }
public boolean hasWizards() { return wizards; }
public String getModid() { return modid; }
}
@@ -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
@@ -47,6 +47,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 +74,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 +127,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 +200,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 +295,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
@@ -382,6 +392,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
}
}
@@ -507,6 +518,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 +590,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 +625,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)));
@@ -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
@@ -34,7 +34,7 @@ public class EntityFireRing extends EntityScaledConstruct {
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,23 +146,28 @@ 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));
@@ -174,6 +179,11 @@ public class EntityForcefield extends EntityMagicConstruct implements ICustomHit
}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){
@@ -365,8 +365,7 @@ public class EntityEvilWizard extends EntityMob implements ISpellCaster, IEntity
if(getElement() == null){
if(rand.nextBoolean()){
Element[] elements = (Element[]) Arrays.stream(Element.values()).filter(Element::hasWizards).toArray();
this.setElement(elements[rand.nextInt(elements.length - 1) + 1]);
this.setElement(Element.values()[rand.nextInt(Element.values().length - 1) + 1]);
}else{
this.setElement(Element.MAGIC);
}
@@ -68,11 +68,11 @@ 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;
private int timeUntilReset;
/** addDefaultEquipmentAndRecipies is called if this is true */
@@ -130,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())))
@@ -258,11 +262,11 @@ public class EntityWizard extends EntityCreature implements INpc, IMerchant, ISp
@Override
public ITextComponent getDisplayName(){
if(this.hasCustomName()){
return super.getDisplayName();
}
return this.getElement().getWizardName();
}
@@ -270,7 +274,7 @@ public class EntityWizard extends EntityCreature implements INpc, IMerchant, ISp
protected boolean canDespawn(){
return false;
}
@Override
protected SoundEvent getAmbientSound(){
if(Wizardry.tisTheSeason) return WizardrySounds.ENTITY_WIZARD_HOHOHO;
@@ -673,7 +677,7 @@ public class EntityWizard extends EntityCreature implements INpc, IMerchant, ISp
WizardryItems.getWand(tier, Element.values()[rand.nextInt(Element.values().length)]));
}
}else if(randomiser < 8){
return new ItemStack(WizardryItems.arcane_tome_apprentice, 1);
return new ItemStack(WizardryItems.arcane_tome, 1, 1);
}else if(randomiser < 10){
EntityEquipmentSlot slot = InventoryUtils.ARMOUR_SLOTS[rand.nextInt(InventoryUtils.ARMOUR_SLOTS.length)];
if(this.getElement() != Element.MAGIC && rand.nextInt(4) > 0){
@@ -709,7 +713,7 @@ public class EntityWizard extends EntityCreature implements INpc, IMerchant, ISp
WizardryItems.getWand(tier, Element.values()[rand.nextInt(Element.values().length)]));
}
}else if(randomiser < 8){
return new ItemStack(WizardryItems.arcane_tome_advanced, 1);
return new ItemStack(WizardryItems.arcane_tome, 1, 2);
}else{
List<Item> upgrades = new ArrayList<Item>(WandHelper.getSpecialUpgrades());
randomiser = rand.nextInt(upgrades.size());
@@ -732,7 +736,7 @@ public class EntityWizard extends EntityCreature implements INpc, IMerchant, ISp
return new ItemStack(WizardryItems.master_wand);
}
}else{
return new ItemStack(WizardryItems.arcane_tome_master, 1);
return new ItemStack(WizardryItems.arcane_tome, 1, 3);
}
}
@@ -747,8 +751,7 @@ public class EntityWizard extends EntityCreature implements INpc, IMerchant, ISp
textureIndex = this.rand.nextInt(6);
if(rand.nextBoolean()){
Element[] elements = (Element[]) Arrays.stream(Element.values()).filter(Element::hasWizards).toArray();
this.setElement(elements[rand.nextInt(elements.length - 1) + 1]);
this.setElement(Element.values()[rand.nextInt(Element.values().length - 1) + 1]);
}else{
this.setElement(Element.MAGIC);
}
@@ -885,7 +888,7 @@ public class EntityWizard extends EntityCreature implements INpc, IMerchant, ISp
/**
* Sets the list of blocks that are part of this wizard's tower. If a player breaks any of these blocks, the wizard
* will get angry and attack them.
*
*
* @param blocks A Set of BlockPos objects representing the blocks in the tower.
*/
public void setTowerBlocks(Set<BlockPos> blocks){
@@ -917,11 +920,11 @@ public class EntityWizard extends EntityCreature implements INpc, IMerchant, ISp
}
}
}
// Copied from their respective AI classes
public static class EntityAILookAtTradePlayer extends EntityAIWatchClosest {
private final EntityWizard wizard;
public EntityAILookAtTradePlayer(EntityWizard wizard){
@@ -939,9 +942,9 @@ public class EntityWizard extends EntityCreature implements INpc, IMerchant, ISp
}
}
}
public static class EntityAITradePlayer extends EntityAIBase {
private final EntityWizard wizard;
public EntityAITradePlayer(EntityWizard wizard){
@@ -951,7 +954,7 @@ public class EntityWizard extends EntityCreature implements INpc, IMerchant, ISp
@Override
public boolean shouldExecute(){
if(!this.wizard.isEntityAlive()){
return false;
}else if(this.wizard.isInWater()){
@@ -961,7 +964,7 @@ public class EntityWizard extends EntityCreature implements INpc, IMerchant, ISp
}else if(this.wizard.velocityChanged){
return false;
}else{
EntityPlayer entityplayer = this.wizard.getCustomer();
if(entityplayer == null){
@@ -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;
}
}
@@ -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(){
@@ -1,6 +1,5 @@
package electroblob.wizardry.integration.jei;
import electroblob.wizardry.Wizardry;
import electroblob.wizardry.client.DrawingUtils;
import electroblob.wizardry.constants.Constants;
import electroblob.wizardry.constants.Element;
@@ -12,9 +11,7 @@ import mezz.jei.api.ingredients.VanillaTypes;
import mezz.jei.api.recipe.IRecipeWrapper;
import net.minecraft.client.Minecraft;
import net.minecraft.item.ItemStack;
import net.minecraft.util.ResourceLocation;
import net.minecraft.util.math.MathHelper;
import net.minecraftforge.fml.common.registry.ForgeRegistries;
import java.util.ArrayList;
import java.util.Collections;
@@ -98,8 +95,8 @@ public class ArcaneWorkbenchRecipe implements IRecipeWrapper {
// A stack of crystals will almost certainly be enough mana, but you never know!
// Using ItemStack.EMPTY to avoid deprecated method; crystals' stack size is not stack-sensitive so it doesn't matter
if(count <= WizardryItems.magic_crystal.getItemStackLimit(ItemStack.EMPTY)){
for (Element element : Element.values()) {
crystalStacks.add(new ItemStack(ForgeRegistries.ITEMS.getValue(new ResourceLocation(Wizardry.MODID, element.name().toLowerCase() + "_crystal")), count));
for(int meta = 0; meta < Element.values().length; meta++){
crystalStacks.add(new ItemStack(WizardryItems.magic_crystal, count, meta));
}
}
@@ -23,7 +23,6 @@ import net.minecraft.item.Item;
import net.minecraft.item.ItemStack;
import net.minecraft.util.NonNullList;
import net.minecraft.util.ResourceLocation;
import net.minecraftforge.fml.common.registry.ForgeRegistries;
import java.util.ArrayList;
import java.util.Collection;
@@ -215,9 +214,7 @@ public class ArcaneWorkbenchRecipeCategory implements IRecipeCategory<ArcaneWork
List<ArcaneWorkbenchRecipe> recipes = new ArrayList<>();
List<ItemStack> crystals = new ArrayList<>();
for (Element element : Element.values()) {
crystals.add(new ItemStack(ForgeRegistries.ITEMS.getValue(new ResourceLocation(Wizardry.MODID, "crystal_" + element.name().toLowerCase()))));
}
for(int meta = 0; meta < Element.values().length; meta++) crystals.add(new ItemStack(WizardryItems.magic_crystal, 1, meta));
List<ItemStack> shard = Collections.singletonList(new ItemStack(WizardryItems.crystal_shard));
List<ItemStack> grandCrystal = Collections.singletonList(new ItemStack(WizardryItems.grand_crystal));
@@ -19,7 +19,6 @@ import net.minecraft.item.Item;
import net.minecraft.item.ItemStack;
import net.minecraft.util.NonNullList;
import net.minecraft.util.ResourceLocation;
import net.minecraftforge.fml.common.registry.ForgeRegistries;
import net.minecraftforge.oredict.OreDictionary;
import java.util.ArrayList;
@@ -119,13 +118,7 @@ public class ImbuementAltarRecipeCategory implements IRecipeCategory<ImbuementAl
List<ImbuementAltarRecipe> recipes = new ArrayList<>();
NonNullList<ItemStack> variants = NonNullList.create();
variants.add(new ItemStack(WizardryItems.spectral_dust_earth));
variants.add(new ItemStack(WizardryItems.spectral_dust_fire));
variants.add(new ItemStack(WizardryItems.spectral_dust_healing));
variants.add(new ItemStack(WizardryItems.spectral_dust_ice));
variants.add(new ItemStack(WizardryItems.spectral_dust_lightning));
variants.add(new ItemStack(WizardryItems.spectral_dust_necromancy));
variants.add(new ItemStack(WizardryItems.spectral_dust_sorcery));
WizardryItems.spectral_dust.getSubItems(WizardryItems.spectral_dust.getCreativeTab(), variants);
List<List<ItemStack>> dusts = new ArrayList<>();
// Generate 4 separate lists, each in a different order to make it obvious they can be any element
@@ -146,10 +139,9 @@ public class ImbuementAltarRecipeCategory implements IRecipeCategory<ImbuementAl
ItemStack input = new ItemStack(WizardryItems.magic_crystal);
for(Element element : Element.values()){
List<List<ItemStack>> dusts = Collections.nCopies(4, Collections.singletonList(new ItemStack(ForgeRegistries.ITEMS.getValue(new ResourceLocation(Wizardry.MODID,
"spectral_dust_" + element.name().toLowerCase())))));
ItemStack output = new ItemStack(ForgeRegistries.ITEMS.getValue(new ResourceLocation(Wizardry.MODID, element.name().toLowerCase() + "_crystal")));
for(int meta = 1; meta < Element.values().length; meta++){
List<List<ItemStack>> dusts = Collections.nCopies(4, Collections.singletonList(new ItemStack(WizardryItems.spectral_dust, 1, meta)));
ItemStack output = new ItemStack(WizardryItems.magic_crystal, 1, meta);
recipes.add(new ImbuementAltarRecipe(input, dusts, output));
}
@@ -161,16 +153,11 @@ public class ImbuementAltarRecipeCategory implements IRecipeCategory<ImbuementAl
List<ImbuementAltarRecipe> recipes = new ArrayList<>();
ItemStack input = new ItemStack(WizardryBlocks.magic_crystal_block);
ItemStack input = new ItemStack(WizardryBlocks.crystal_block);
for (Element element : Element.values()) {
if (element == Element.MAGIC) { continue; }
List<List<ItemStack>> dusts = Collections.nCopies(4, Collections.singletonList(new ItemStack(ForgeRegistries.ITEMS.getValue(
new ResourceLocation(Wizardry.MODID, "spectral_dust_" + element.name().toLowerCase())))));
ItemStack output = new ItemStack(ForgeRegistries.ITEMS.getValue(new ResourceLocation(Wizardry.MODID, element.getName().toLowerCase()
+ "_crystal_block")));
for(int meta = 1; meta < Element.values().length; meta++){
List<List<ItemStack>> dusts = Collections.nCopies(4, Collections.singletonList(new ItemStack(WizardryItems.spectral_dust, 1, meta)));
ItemStack output = new ItemStack(WizardryBlocks.crystal_block, 1, meta);
recipes.add(new ImbuementAltarRecipe(input, dusts, output));
}
@@ -192,8 +179,7 @@ public class ImbuementAltarRecipeCategory implements IRecipeCategory<ImbuementAl
if(e == Element.MAGIC) continue;
List<List<ItemStack>> dusts = Collections.nCopies(4, Collections.singletonList(new ItemStack(
ForgeRegistries.ITEMS.getValue(new ResourceLocation(Wizardry.MODID, "spectral_dust_" + e.name().toLowerCase())))));
List<List<ItemStack>> dusts = Collections.nCopies(4, Collections.singletonList(new ItemStack(WizardryItems.spectral_dust, 1, e.ordinal())));
ItemStack output = TileEntityImbuementAltar.getImbuementResult(input, new Element[]{e, e, e, e}, false, null, null);
if(!output.isEmpty()) recipes.add(new ImbuementAltarRecipe(input, dusts, output));
@@ -98,9 +98,7 @@ public class ContainerArcaneWorkbench extends Container implements ISpellSortabl
this.addSlotToContainer(new SlotWorkbenchItem(tileentity, CENTRE_SLOT, 80, 64, this));
Set<Item> upgrades = new HashSet<>(WandHelper.getSpecialUpgrades()); // Can't be done statically.
upgrades.add(WizardryItems.arcane_tome_apprentice);
upgrades.add(WizardryItems.arcane_tome_advanced);
upgrades.add(WizardryItems.arcane_tome_master);
upgrades.add(WizardryItems.arcane_tome);
upgrades.add(WizardryItems.resplendent_thread);
upgrades.add(WizardryItems.crystal_silver_plating);
upgrades.add(WizardryItems.ethereal_crystalweave);
@@ -454,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;
@@ -127,9 +127,7 @@ public class ContainerBookshelf extends Container {
registerBookItem(Items.WRITABLE_BOOK);
registerBookItem(Items.ENCHANTED_BOOK);
registerBookItem(WizardryItems.spell_book);
registerBookItem(WizardryItems.arcane_tome_apprentice);
registerBookItem(WizardryItems.arcane_tome_advanced);
registerBookItem(WizardryItems.arcane_tome_master);
registerBookItem(WizardryItems.arcane_tome);
registerBookItem(WizardryItems.wizard_handbook);
registerBookItem(WizardryItems.ruined_spell_book);
registerBookItem(WizardryItems.scroll);
@@ -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.
@@ -3,9 +3,11 @@ package electroblob.wizardry.item;
import electroblob.wizardry.Wizardry;
import electroblob.wizardry.constants.Tier;
import electroblob.wizardry.registry.WizardryTabs;
import net.minecraft.creativetab.CreativeTabs;
import net.minecraft.item.EnumRarity;
import net.minecraft.item.Item;
import net.minecraft.item.ItemStack;
import net.minecraft.util.NonNullList;
import net.minecraft.world.World;
import net.minecraftforge.fml.relauncher.Side;
import net.minecraftforge.fml.relauncher.SideOnly;
@@ -14,33 +16,54 @@ import java.util.List;
public class ItemArcaneTome extends Item {
private final EnumRarity rarity;
private final Tier tier;
public ItemArcaneTome(EnumRarity rarity, Tier tier){
public ItemArcaneTome(){
super();
setHasSubtypes(true);
setMaxStackSize(1);
setCreativeTab(WizardryTabs.WIZARDRY);
this.rarity = rarity;
this.tier = tier;
}
public Tier getTier() { return tier; }
@Override
public void getSubItems(CreativeTabs tab, NonNullList<ItemStack> list){
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));
}
}
}
@Override
@SideOnly(Side.CLIENT)
public boolean hasEffect(ItemStack stack){ return true; }
public boolean hasEffect(ItemStack stack){
return true;
}
@Override
public EnumRarity getRarity(ItemStack stack){ return rarity; }
public EnumRarity getRarity(ItemStack stack){
switch(this.getDamage(stack)){
case 1:
return EnumRarity.UNCOMMON;
case 2:
return EnumRarity.RARE;
case 3:
return EnumRarity.EPIC;
}
return EnumRarity.COMMON;
}
@SideOnly(Side.CLIENT)
@Override
public void addInformation(ItemStack stack, World world, List<String> tooltip, net.minecraft.client.util.ITooltipFlag showAdvanced){
tooltip.add(tier.getDisplayNameWithFormatting());
Tier tier2 = Tier.values()[tier.ordinal() - 1];
Wizardry.proxy.addMultiLineDescription(tooltip, "item." + Wizardry.MODID + ":arcane_tome.desc",
if(stack.getItemDamage() < 1){
return; // If something's up with the metadata it will display a 'generic' tome of arcana with no info
}
Tier tier = Tier.values()[stack.getItemDamage()];
Tier tier2 = Tier.values()[stack.getItemDamage() - 1];
tooltip.add(tier.getDisplayNameWithFormatting());
Wizardry.proxy.addMultiLineDescription(tooltip, "item." + this.getRegistryName() + ".desc",
tier2.getDisplayNameWithFormatting() + "\u00A77", tier.getDisplayNameWithFormatting() + "\u00A77");
}
@@ -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;
@@ -178,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{
@@ -1,21 +1,44 @@
package electroblob.wizardry.item;
import electroblob.wizardry.api.IElemental;
import electroblob.wizardry.Wizardry;
import electroblob.wizardry.constants.Element;
import electroblob.wizardry.registry.WizardryTabs;
import net.minecraft.creativetab.CreativeTabs;
import net.minecraft.item.Item;
import net.minecraft.item.ItemStack;
import net.minecraft.util.NonNullList;
import net.minecraft.util.ResourceLocation;
public class ItemCrystal extends Item implements IElemental {
/** Note that in 1.13, <i>the flattening</i> will make this class redundant, much like ItemCoal, which is probably its
* closest analog in vanilla. */
public class ItemCrystal extends Item implements IMultiTexturedItem {
private final Element element;
public ItemCrystal(Element element) {
public ItemCrystal(){
super();
this.setMaxDamage(0);
this.setCreativeTab(WizardryTabs.WIZARDRY);
this.element = element;
this.setHasSubtypes(true);
this.setMaxDamage(0);
this.setCreativeTab(WizardryTabs.WIZARDRY);
}
@Override
public ResourceLocation getModelName(ItemStack stack){
int metadata = stack.getMetadata();
if(metadata >= Element.values().length) metadata = 0;
return new ResourceLocation(Wizardry.MODID, "crystal_" + Element.values()[metadata].getName());
}
@Override
public Element getElement() { return element; }
public String getTranslationKey(ItemStack stack){
return "item." + this.getModelName(stack).toString();
}
@Override
public void getSubItems(CreativeTabs tab, NonNullList<ItemStack> items){
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);
@@ -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);
@@ -1,13 +1,39 @@
package electroblob.wizardry.item;
import electroblob.wizardry.Wizardry;
import electroblob.wizardry.constants.Element;
import electroblob.wizardry.registry.WizardryTabs;
import net.minecraft.creativetab.CreativeTabs;
import net.minecraft.item.Item;
import net.minecraft.item.ItemStack;
import net.minecraft.util.NonNullList;
import net.minecraft.util.ResourceLocation;
public class ItemSpectralDust extends Item {
import java.util.Arrays;
public class ItemSpectralDust extends Item implements IMultiTexturedItem {
public ItemSpectralDust(){
super();
this.setHasSubtypes(true);
this.setMaxDamage(0);
this.setCreativeTab(WizardryTabs.WIZARDRY);
}
@Override
public ResourceLocation getModelName(ItemStack stack){
int metadata = stack.getMetadata();
if(metadata >= Element.values().length) metadata = 0;
return new ResourceLocation(Wizardry.MODID, "spectral_dust_" + Element.values()[metadata].getName());
}
@Override
public void getSubItems(CreativeTabs tab, NonNullList<ItemStack> items){
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()));
}
}
}
}
@@ -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){
if(isHeld) WandHelper.decrementCooldowns(stack);
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());
// 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);
}
}
@@ -687,9 +697,9 @@ public class ItemWand extends Item implements IWorkbenchItem, ISpellCastingItem,
// Upgrades wand if necessary. Damage is copied, preserving remaining durability,
// and also the entire NBT tag compound.
if(upgrade.getItem() instanceof ItemArcaneTome){
if(upgrade.getItem() == WizardryItems.arcane_tome){
Tier tier = ((ItemArcaneTome) upgrade.getItem()).getTier();
Tier tier = Tier.values()[upgrade.getItemDamage()];
// Checks the wand upgrade is for the tier above the wand's tier, and that either the wand has enough
// progression or the player is in creative mode.
@@ -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,36 +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());
// 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;
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){
@@ -486,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) -> {
@@ -51,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){
@@ -133,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){
@@ -1,6 +1,7 @@
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;
@@ -45,8 +46,12 @@ public class CurseUndeath extends Curse {
} else {
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 ItemWizardArmour) {
entitylivingbase.setFire(8);
} else {
entitylivingbase.renderBrokenItemStack(itemstack);
entitylivingbase.setItemStackToSlot(EntityEquipmentSlot.HEAD, ItemStack.EMPTY);
}
}
}
}
@@ -13,12 +13,15 @@ public class PotionDiamondflesh extends PotionMagicEffect {
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");
this.registerPotionAttributeModifier(SharedMonsterAttributes.MOVEMENT_SPEED,
"158a8af2-6db0-4340-a01c-a7b60d10ddf4", -0.1f, EntityUtils.Operations.MULTIPLY_CUMULATIVE);
// 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", 3.0f, EntityUtils.Operations.ADD);
"a68d4532-5847-426c-9b03-d541b113cec2", (float)Wizardry.settings.diamondFleshArmorToughnessBonus, EntityUtils.Operations.ADD);
this.registerPotionAttributeModifier(SharedMonsterAttributes.ARMOR,
"46a095be-82dd-43fd-8b67-13f51591eb8e", 4.0f, EntityUtils.Operations.ADD);
"46a095be-82dd-43fd-8b67-13f51591eb8e", (float)Wizardry.settings.diamondFleshArmorBonus, EntityUtils.Operations.ADD);
}
@Override
@@ -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);
}
}
}
}
@@ -13,12 +13,15 @@ public class PotionIronflesh extends PotionMagicEffect {
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");
this.registerPotionAttributeModifier(SharedMonsterAttributes.MOVEMENT_SPEED,
"fe607d55-50e3-4f4f-a959-6571503f92f4", -0.1f, EntityUtils.Operations.MULTIPLY_CUMULATIVE);
// 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", 4.0f, EntityUtils.Operations.ADD);
"e1adff1e-8510-4a09-96ed-ef677cad20c1", (float)Wizardry.settings.ironFleshArmorBonus, EntityUtils.Operations.ADD);
}
@Override
@@ -13,12 +13,15 @@ public class PotionOakflesh extends PotionMagicEffect {
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");
this.registerPotionAttributeModifier(SharedMonsterAttributes.MOVEMENT_SPEED,
"98b4ba66-7c50-4a4c-9f3f-40bcb37313b5", -0.1f, EntityUtils.Operations.MULTIPLY_CUMULATIVE);
// 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", 0.2f, EntityUtils.Operations.MULTIPLY_FLAT);
"ed9d0423-60f4-4998-bd8d-dc7c33bd45b8", (float)Wizardry.settings.oakFleshHealthBonus, EntityUtils.Operations.MULTIPLY_FLAT);
this.registerPotionAttributeModifier(SharedMonsterAttributes.ARMOR,
"0b607c3f-fb14-43d7-96b5-1c1b6f6da242", 3.0f, EntityUtils.Operations.ADD);
"0b607c3f-fb14-43d7-96b5-1c1b6f6da242", (float)Wizardry.settings.oakFleshArmorBonus, EntityUtils.Operations.ADD);
}
@Override
@@ -2,7 +2,6 @@ package electroblob.wizardry.registry;
import electroblob.wizardry.Wizardry;
import electroblob.wizardry.block.*;
import electroblob.wizardry.constants.Element;
import electroblob.wizardry.tileentity.*;
import net.minecraft.block.Block;
import net.minecraft.block.material.Material;
@@ -44,15 +43,7 @@ public final class WizardryBlocks {
public static final Block crystal_ore = placeholder();
public static final Block crystal_flower = placeholder();
public static final Block transportation_stone = placeholder();
public static final Block magic_crystal_block = placeholder();
public static final Block fire_crystal_block = placeholder();
public static final Block ice_crystal_block = placeholder();
public static final Block lightning_crystal_block = placeholder();
public static final Block necromancy_crystal_block = placeholder();
public static final Block earth_crystal_block = placeholder();
public static final Block sorcery_crystal_block = placeholder();
public static final Block healing_crystal_block = placeholder();
public static final Block crystal_block = placeholder();
public static final Block petrified_stone = placeholder();
public static final Block ice_statue = placeholder();
@@ -68,28 +59,9 @@ public final class WizardryBlocks {
public static final Block permafrost = placeholder();
public static final Block runestone = placeholder();
public static final Block fire_runestone = placeholder();
public static final Block ice_runestone = placeholder();
public static final Block lightning_runestone = placeholder();
public static final Block necromancy_runestone = placeholder();
public static final Block earth_runestone = placeholder();
public static final Block sorcery_runestone = placeholder();
public static final Block healing_runestone = placeholder();
public static final Block runestone_pedestal = placeholder();
public static final Block fire_runestone_pedestal = placeholder();
public static final Block ice_runestone_pedestal = placeholder();
public static final Block lightning_runestone_pedestal = placeholder();
public static final Block necromancy_runestone_pedestal = placeholder();
public static final Block earth_runestone_pedestal = placeholder();
public static final Block healing_runestone_pedestal = placeholder();
public static final Block sorcery_runestone_pedestal = placeholder();
public static final Block acacia_gilded_wood = placeholder();
public static final Block birch_gilded_wood = placeholder();
public static final Block dark_oak_gilded_wood = placeholder();
public static final Block jungle_gilded_wood = placeholder();
public static final Block oak_gilded_wood = placeholder();
public static final Block spruce_gilded_wood = placeholder();
public static final Block gilded_wood = placeholder();
public static final Block oak_bookshelf = placeholder();
public static final Block spruce_bookshelf = placeholder();
@@ -136,15 +108,7 @@ public final class WizardryBlocks {
registerBlock(registry, "crystal_ore", new BlockCrystalOre(Material.ROCK).setHardness(3.0F).setCreativeTab(WizardryTabs.WIZARDRY));
registerBlock(registry, "crystal_flower", new BlockCrystalFlower(Material.PLANTS).setHardness(0.0F).setCreativeTab(WizardryTabs.WIZARDRY));
registerBlock(registry, "transportation_stone", new BlockTransportationStone(Material.ROCK).setHardness(0.3F).setLightLevel(0.5f).setLightOpacity(0).setCreativeTab(WizardryTabs.WIZARDRY));
registerBlock(registry, "magic_crystal_block", new BlockCrystal(Element.MAGIC).setHardness(5.0F).setResistance(10.0F).setCreativeTab(WizardryTabs.WIZARDRY));
registerBlock(registry, "fire_crystal_block", new BlockCrystal(Element.MAGIC).setHardness(5.0F).setResistance(10.0F).setCreativeTab(WizardryTabs.WIZARDRY));
registerBlock(registry, "ice_crystal_block", new BlockCrystal(Element.MAGIC).setHardness(5.0F).setResistance(10.0F).setCreativeTab(WizardryTabs.WIZARDRY));
registerBlock(registry, "lightning_crystal_block", new BlockCrystal(Element.MAGIC).setHardness(5.0F).setResistance(10.0F).setCreativeTab(WizardryTabs.WIZARDRY));
registerBlock(registry, "necromancy_crystal_block", new BlockCrystal(Element.MAGIC).setHardness(5.0F).setResistance(10.0F).setCreativeTab(WizardryTabs.WIZARDRY));
registerBlock(registry, "earth_crystal_block", new BlockCrystal(Element.MAGIC).setHardness(5.0F).setResistance(10.0F).setCreativeTab(WizardryTabs.WIZARDRY));
registerBlock(registry, "sorcery_crystal_block", new BlockCrystal(Element.MAGIC).setHardness(5.0F).setResistance(10.0F).setCreativeTab(WizardryTabs.WIZARDRY));
registerBlock(registry, "healing_crystal_block", new BlockCrystal(Element.MAGIC).setHardness(5.0F).setResistance(10.0F).setCreativeTab(WizardryTabs.WIZARDRY));
registerBlock(registry, "crystal_block", new BlockCrystal(Material.IRON).setHardness(5.0F).setResistance(10.0F).setCreativeTab(WizardryTabs.WIZARDRY));
registerBlock(registry, "petrified_stone", new BlockStatue(Material.ROCK).setHardness(1.5F).setResistance(10.0F));
registerBlock(registry, "ice_statue", new BlockStatue(Material.ICE).setHardness(0.5F).setLightOpacity(3));
@@ -159,28 +123,10 @@ public final class WizardryBlocks {
registerBlock(registry, "crystal_flower_pot", new BlockCrystalFlowerPot());
registerBlock(registry, "permafrost", new BlockPermafrost());
registerBlock(registry, "fire_runestone", new BlockRunestone(Material.ROCK, Element.FIRE));
registerBlock(registry, "ice_runestone", new BlockRunestone(Material.ROCK, Element.ICE));
registerBlock(registry, "lightning_runestone", new BlockRunestone(Material.ROCK, Element.LIGHTNING));
registerBlock(registry, "necromancy_runestone", new BlockRunestone(Material.ROCK, Element.NECROMANCY));
registerBlock(registry, "earth_runestone", new BlockRunestone(Material.ROCK, Element.EARTH));
registerBlock(registry, "sorcery_runestone", new BlockRunestone(Material.ROCK, Element.SORCERY));
registerBlock(registry, "healing_runestone", new BlockRunestone(Material.ROCK, Element.HEALING));
registerBlock(registry, "runestone", new BlockRunestone(Material.ROCK));
registerBlock(registry, "runestone_pedestal", new BlockPedestal(Material.ROCK));
registerBlock(registry, "fire_runestone_pedestal", new BlockRunestonePedestal(Material.ROCK, Element.FIRE));
registerBlock(registry, "ice_runestone_pedestal", new BlockRunestonePedestal(Material.ROCK, Element.ICE));
registerBlock(registry, "lightning_runestone_pedestal", new BlockRunestonePedestal(Material.ROCK, Element.LIGHTNING));
registerBlock(registry, "necromancy_runestone_pedestal", new BlockRunestonePedestal(Material.ROCK, Element.NECROMANCY));
registerBlock(registry, "earth_runestone_pedestal", new BlockRunestonePedestal(Material.ROCK, Element.EARTH));
registerBlock(registry, "healing_runestone_pedestal", new BlockRunestonePedestal(Material.ROCK, Element.HEALING));
registerBlock(registry, "sorcery_runestone_pedestal", new BlockRunestonePedestal(Material.ROCK, Element.SORCERY));
registerBlock(registry, "acacia_gilded_wood", new BlockGildedWood());
registerBlock(registry, "birch_gilded_wood", new BlockGildedWood());
registerBlock(registry, "dark_oak_gilded_wood", new BlockGildedWood());
registerBlock(registry, "jungle_gilded_wood", new BlockGildedWood());
registerBlock(registry, "oak_gilded_wood", new BlockGildedWood());
registerBlock(registry, "spruce_gilded_wood", new BlockGildedWood());
registerBlock(registry, "gilded_wood", new BlockGildedWood());
registerBlock(registry, "oak_bookshelf", new BlockBookshelf());
registerBlock(registry, "spruce_bookshelf", new BlockBookshelf());
@@ -26,13 +26,11 @@ import net.minecraft.item.ItemArmor.ArmorMaterial;
import net.minecraft.item.ItemBlock;
import net.minecraft.item.ItemStack;
import net.minecraft.tileentity.BannerPattern;
import net.minecraft.util.ResourceLocation;
import net.minecraft.world.World;
import net.minecraftforge.common.util.EnumHelper;
import net.minecraftforge.event.RegistryEvent;
import net.minecraftforge.fml.common.Mod;
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
import net.minecraftforge.fml.common.registry.ForgeRegistries;
import net.minecraftforge.fml.common.registry.GameRegistry.ObjectHolder;
import net.minecraftforge.registries.IForgeRegistry;
@@ -81,21 +79,12 @@ public final class WizardryItems {
// which means you lose the individual fields...
public static final Item magic_crystal = placeholder();
public static final Item earth_crystal = placeholder();
public static final Item fire_crystal = placeholder();
public static final Item healing_crystal = placeholder();
public static final Item ice_crystal = placeholder();
public static final Item lightning_crystal = placeholder();
public static final Item necromancy_crystal = placeholder();
public static final Item sorcery_crystal = placeholder();
public static final Item grand_crystal = placeholder();
public static final Item crystal_shard = placeholder();
public static final Item wizard_handbook = placeholder();
public static final Item arcane_tome_apprentice = placeholder();
public static final Item arcane_tome_advanced = placeholder();
public static final Item arcane_tome_master = placeholder();
public static final Item arcane_tome = placeholder();
public static final Item spell_book = placeholder();
public static final Item scroll = placeholder();
public static final Item ruined_spell_book = placeholder();
@@ -173,13 +162,7 @@ public final class WizardryItems {
public static final Item smoke_bomb = placeholder();
public static final Item spark_bomb = placeholder();
public static final Item spectral_dust_earth = placeholder();
public static final Item spectral_dust_fire = placeholder();
public static final Item spectral_dust_healing = placeholder();
public static final Item spectral_dust_ice = placeholder();
public static final Item spectral_dust_lightning = placeholder();
public static final Item spectral_dust_necromancy = placeholder();
public static final Item spectral_dust_sorcery = placeholder();
public static final Item spectral_dust = placeholder();
public static final Item wizard_hat = placeholder();
public static final Item wizard_robe = placeholder();
@@ -560,37 +543,10 @@ public final class WizardryItems {
String[] elements = Arrays.stream(Element.values()).map(Element::getName).toArray(String[]::new);
String[] woodTypes = Arrays.stream(BlockPlanks.EnumType.values()).map(BlockPlanks.EnumType::getName).toArray(String[]::new);
registerItemBlock(registry, WizardryBlocks.magic_crystal_block);
registerItemBlock(registry, WizardryBlocks.fire_crystal_block);
registerItemBlock(registry, WizardryBlocks.ice_crystal_block);
registerItemBlock(registry, WizardryBlocks.lightning_crystal_block);
registerItemBlock(registry, WizardryBlocks.necromancy_crystal_block);
registerItemBlock(registry, WizardryBlocks.earth_crystal_block);
registerItemBlock(registry, WizardryBlocks.sorcery_crystal_block);
registerItemBlock(registry, WizardryBlocks.healing_crystal_block);
registerItemBlock(registry, WizardryBlocks.fire_runestone);
registerItemBlock(registry, WizardryBlocks.ice_runestone);
registerItemBlock(registry, WizardryBlocks.lightning_runestone);
registerItemBlock(registry, WizardryBlocks.necromancy_runestone);
registerItemBlock(registry, WizardryBlocks.earth_runestone);
registerItemBlock(registry, WizardryBlocks.sorcery_runestone);
registerItemBlock(registry, WizardryBlocks.healing_runestone);
registerItemBlock(registry, WizardryBlocks.fire_runestone_pedestal);
registerItemBlock(registry, WizardryBlocks.ice_runestone_pedestal);
registerItemBlock(registry, WizardryBlocks.lightning_runestone_pedestal);
registerItemBlock(registry, WizardryBlocks.necromancy_runestone_pedestal);
registerItemBlock(registry, WizardryBlocks.earth_runestone_pedestal);
registerItemBlock(registry, WizardryBlocks.healing_runestone_pedestal);
registerItemBlock(registry, WizardryBlocks.sorcery_runestone_pedestal);
registerItemBlock(registry, WizardryBlocks.acacia_gilded_wood);
registerItemBlock(registry, WizardryBlocks.birch_gilded_wood);
registerItemBlock(registry, WizardryBlocks.dark_oak_gilded_wood);
registerItemBlock(registry, WizardryBlocks.jungle_gilded_wood);
registerItemBlock(registry, WizardryBlocks.oak_gilded_wood);
registerItemBlock(registry, WizardryBlocks.spruce_gilded_wood);
registerMultiTexturedItemBlock(registry, WizardryBlocks.crystal_block, true, elements);
registerMultiTexturedItemBlock(registry, WizardryBlocks.runestone, false, elements);
registerMultiTexturedItemBlock(registry, WizardryBlocks.runestone_pedestal, false, elements);
registerMultiTexturedItemBlock(registry, WizardryBlocks.gilded_wood, true, woodTypes);
registerItemBlock(registry, WizardryBlocks.oak_bookshelf);
registerItemBlock(registry, WizardryBlocks.spruce_bookshelf);
@@ -611,22 +567,13 @@ public final class WizardryItems {
// Items
registerItem(registry, "magic_crystal", new ItemCrystal(Element.MAGIC));
registerItem(registry, "earth_crystal", new ItemCrystal(Element.EARTH));
registerItem(registry, "fire_crystal", new ItemCrystal(Element.FIRE));
registerItem(registry, "healing_crystal", new ItemCrystal(Element.HEALING));
registerItem(registry, "ice_crystal", new ItemCrystal(Element.ICE));
registerItem(registry, "lightning_crystal", new ItemCrystal(Element.LIGHTNING));
registerItem(registry, "necromancy_crystal", new ItemCrystal(Element.NECROMANCY));
registerItem(registry, "sorcery_crystal", new ItemCrystal(Element.SORCERY));
registerItem(registry, "magic_crystal", new ItemCrystal());
registerItem(registry, "crystal_shard", new Item().setCreativeTab(WizardryTabs.WIZARDRY));
registerItem(registry, "grand_crystal", new Item().setCreativeTab(WizardryTabs.WIZARDRY));
registerItem(registry, "wizard_handbook", new ItemWizardHandbook(), true);
registerItem(registry, "arcane_tome_apprentice", new ItemArcaneTome(EnumRarity.UNCOMMON, Tier.APPRENTICE));
registerItem(registry, "arcane_tome_advanced", new ItemArcaneTome(EnumRarity.RARE, Tier.ADVANCED));
registerItem(registry, "arcane_tome_master", new ItemArcaneTome(EnumRarity.EPIC, Tier.MASTER));
registerItem(registry, "arcane_tome", new ItemArcaneTome());
registerItem(registry, "spell_book", new ItemSpellBook(), true);
registerItem(registry, "scroll", new ItemScroll());
registerItem(registry, "ruined_spell_book", new Item().setCreativeTab(WizardryTabs.WIZARDRY).setMaxStackSize(16));
@@ -707,13 +654,7 @@ public final class WizardryItems {
registerItem(registry, "smoke_bomb", new ItemSmokeBomb());
registerItem(registry, "spark_bomb", new ItemSparkBomb());
registerItem(registry, "spectral_dust_earth", new ItemSpectralDust());
registerItem(registry, "spectral_dust_fire", new ItemSpectralDust());
registerItem(registry, "spectral_dust_healing", new ItemSpectralDust());
registerItem(registry, "spectral_dust_ice", new ItemSpectralDust());
registerItem(registry, "spectral_dust_lightning", new ItemSpectralDust());
registerItem(registry, "spectral_dust_necromancy", new ItemSpectralDust());
registerItem(registry, "spectral_dust_sorcery", new ItemSpectralDust());
registerItem(registry, "spectral_dust", new ItemSpectralDust());
registerItem(registry, "wizard_hat", new ItemWizardArmour(ArmourClass.WIZARD, EntityEquipmentSlot.HEAD, null), true);
registerItem(registry, "wizard_robe", new ItemWizardArmour(ArmourClass.WIZARD, EntityEquipmentSlot.CHEST, null));
@@ -1050,11 +991,9 @@ public final class WizardryItems {
/** Called from init() in the main mod class to register wizardry's banner patterns. */
public static void registerBannerPatterns(){
for(Element element : Element.values()){
if (element != Element.MAGIC) {
addBannerPattern("WIZARDRY_" + element.getName().toUpperCase(Locale.ROOT),
"wizardry_" + element.getName(), "eb" + element.getName().charAt(0),
new ItemStack(ForgeRegistries.ITEMS.getValue(new ResourceLocation(Wizardry.MODID, "crystal_" + element.name().toLowerCase()))));
}
if(element != Element.MAGIC) addBannerPattern("WIZARDRY_" + element.getName().toUpperCase(Locale.ROOT),
"wizardry_" + element.getName(), "eb" + element.getName().charAt(0),
new ItemStack(WizardryItems.magic_crystal, 1, element.ordinal()));
}
}
@@ -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;
@@ -95,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")){
@@ -130,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")));
@@ -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));
}
}
@@ -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),
@@ -65,7 +65,7 @@ public class FrostRay extends SpellRay {
(int)(getProperty(EFFECT_DURATION).floatValue() * modifiers.get(WizardryItems.duration_upgrade)),
getProperty(EFFECT_STRENGTH).intValue()));
if(ticksInUse % ((EntityLivingBase)target).maxHurtResistantTime == 1){
if(ticksInUse % 10 == 0){
float damage = getProperty(DAMAGE).floatValue() * modifiers.get(SpellModifiers.POTENCY);
if(target instanceof EntityBlaze || target instanceof EntityMagmaCube) damage *= 2;
@@ -38,7 +38,7 @@ public class Heal extends SpellBuff {
entity.heal(health);
// If the player is able to heal, they can't possibly have absorption hearts, so no need to check!
if(excessHealth > 0 && entity instanceof EntityPlayer
if(excessHealth > entity.getAbsorptionAmount() && entity instanceof EntityPlayer
&& ItemArtefact.isArtefactActive((EntityPlayer)entity, WizardryItems.amulet_absorption)){
entity.setAbsorptionAmount(excessHealth);
}
@@ -47,7 +47,7 @@ public class LifeDrain extends SpellRay {
if(EntityUtils.isLiving(target)){
if(ticksInUse % 12 == 0){
if(ticksInUse % 10 == 0){
float damage = getProperty(DAMAGE).floatValue() * modifiers.get(SpellModifiers.POTENCY);
@@ -51,7 +51,7 @@ public class LightningRay 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){
EntityUtils.attackEntityWithoutKnockback(target,
MagicDamage.causeDirectMagicDamage(caster, DamageType.SHOCK),
getProperty(DAMAGE).floatValue() * modifiers.get(SpellModifiers.POTENCY));
@@ -53,7 +53,7 @@ public class LightningWeb extends SpellRay {
@Override
protected boolean onEntityHit(World world, Entity target, Vec3d hit, EntityLivingBase caster, Vec3d origin, int ticksInUse, SpellModifiers modifiers){
if(EntityUtils.isLiving(target)){
if(EntityUtils.isLiving(target) && ticksInUse % 10 == 0){
electrocute(world, caster, origin, target, getProperty(PRIMARY_DAMAGE).floatValue()
* modifiers.get(SpellModifiers.POTENCY), ticksInUse);
@@ -63,7 +63,7 @@ public class MarkSacrifice extends SpellRay {
PotionEffect effect = event.getEntityLiving().getActivePotionEffect(WizardryPotions.mark_of_sacrifice);
if(effect != null && event.getSource().isMagicDamage()){
event.setAmount(event.getAmount() * (1 + effect.getAmplifier()) * DAMAGE_INCREASE_PER_LEVEL);
event.setAmount(event.getAmount() * (1 + (1 + effect.getAmplifier()) * DAMAGE_INCREASE_PER_LEVEL));
event.getEntityLiving().removePotionEffect(WizardryPotions.mark_of_sacrifice);
}
}
@@ -58,7 +58,7 @@ public class RayOfPurification extends SpellRay {
if(!world.isRemote && ticksInUse == 1 && caster instanceof EntityPlayer) ((EntityPlayer)caster)
.sendStatusMessage(new TextComponentTranslation("spell.resist", target.getName(),
this.getNameForTranslationFormatted()), true);
}else{
}else if (ticksInUse % 10 == 0) {
float damage = getProperty(DAMAGE).floatValue() * modifiers.get(SpellModifiers.POTENCY);
// Fire
@@ -922,6 +922,11 @@ public abstract class Spell extends IForgeRegistryEntry.Impl<Spell> implements C
return Arrays.asList(applicableItems).contains(item);
}
/** Returns the list of items this spell is applicable to. */
public List<Item> getApplicableItems() {
return Arrays.asList(applicableItems);
}
/**
* Sets which items this spell can appear on (these default to the regular spell book and scroll).
* @param applicableItems The items this spell should naturally appear on (or no items at all).
@@ -125,7 +125,8 @@ public class SpellBuff extends Spell {
@Override
public boolean cast(World world, EntityLiving caster, EnumHand hand, int ticksInUse, EntityLivingBase target, SpellModifiers modifiers){
// Wizards can only cast a buff spell if they don't already have its effects.
if(caster.getActivePotionMap().keySet().containsAll(potionSet)) return false;
// Some buff spells doesn't add any potion effects, those are ignored by this check
if(!potionSet.isEmpty() && caster.getActivePotionMap().keySet().containsAll(potionSet)) return false;
// Only return on the server side or the client probably won't spawn particles
if(!this.applyEffects(caster, modifiers) && !world.isRemote) return false;
if(world.isRemote) this.spawnParticles(world, caster, modifiers);
@@ -47,7 +47,7 @@ import javax.annotation.Nullable;
public abstract class SpellRay extends Spell {
/** The distance below the caster's eyes that the bolt particles start from. */
protected static final double Y_OFFSET = 0.25;
public static final double Y_OFFSET = 0.25;
/** The distance between spawned particles. Defaults to 0.85. */
// 0.85 was chosen to keep it similar to the most common method used previously, which gave an effective spacing of
@@ -1,7 +1,9 @@
package electroblob.wizardry.spell;
import electroblob.wizardry.Wizardry;
import electroblob.wizardry.item.ItemArtefact;
import electroblob.wizardry.item.SpellActions;
import electroblob.wizardry.registry.WizardryItems;
import electroblob.wizardry.util.SpellModifiers;
import net.minecraft.block.state.IBlockState;
import net.minecraft.entity.Entity;
@@ -34,15 +36,15 @@ public class Telekinesis extends SpellRay {
target.motionZ = (origin.z - target.posZ) / 6;
return true;
}else if(target instanceof EntityPlayer && (Wizardry.settings.telekineticDisarmament || !(caster instanceof EntityPlayer))){
} else if (target instanceof EntityPlayer && (Wizardry.settings.telekineticDisarmament && !ItemArtefact.isArtefactActive((EntityPlayer) target, WizardryItems.amulet_anchoring))) {
EntityPlayer player = (EntityPlayer) target;
EntityPlayer player = (EntityPlayer)target;
// IDEA: Disarm the offhand if the mainhand is empty or otherwise harmless?
if(!player.getHeldItemMainhand().isEmpty()){
if (!player.getHeldItemMainhand().isEmpty()) {
if(!world.isRemote){
if (!world.isRemote) {
EntityItem item = player.entityDropItem(player.getHeldItemMainhand(), 0);
// Makes the item move towards the caster
item.motionX = (origin.x - player.posX) / 20;
@@ -50,7 +52,7 @@ public class Telekinesis extends SpellRay {
}
player.setHeldItem(EnumHand.MAIN_HAND, ItemStack.EMPTY);
return true;
}
}
@@ -67,7 +67,9 @@ public class TileEntityArcaneWorkbench extends TileEntity implements IInventory,
if(stack.getItem() instanceof IManaStoringItem && !this.world.isRemote && !((IManaStoringItem)stack.getItem()).isManaFull(stack)
&& this.world.getTotalWorldTime() % electroblob.wizardry.constants.Constants.CONDENSER_TICK_INTERVAL == 0){
// If the upgrade level is 0, this does nothing anyway.
((IManaStoringItem)stack.getItem()).rechargeMana(stack, WandHelper.getUpgradeLevel(stack, WizardryItems.condenser_upgrade));
int baseAmount = WandHelper.getUpgradeLevel(stack, WizardryItems.condenser_upgrade);
int amount = (int)(baseAmount * Wizardry.settings.condenserAmountMultiplier);
((IManaStoringItem)stack.getItem()).rechargeMana(stack, amount);
}
// The server doesn't care what these are, and there's no need for them to be synced or saved.
@@ -188,9 +190,7 @@ public class TileEntityArcaneWorkbench extends TileEntity implements IInventory,
}else if(slotNumber == ContainerArcaneWorkbench.UPGRADE_SLOT){
Set<Item> upgrades = new HashSet<>(WandHelper.getSpecialUpgrades());
upgrades.add(WizardryItems.arcane_tome_apprentice);
upgrades.add(WizardryItems.arcane_tome_advanced);
upgrades.add(WizardryItems.arcane_tome_master);
upgrades.add(WizardryItems.arcane_tome);
upgrades.add(WizardryItems.resplendent_thread);
upgrades.add(WizardryItems.crystal_silver_plating);
upgrades.add(WizardryItems.ethereal_crystalweave);
@@ -256,7 +256,8 @@ public class TileEntityImbuementAltar extends TileEntity implements ITickable {
}
}
if((input.getItem() == WizardryItems.magic_crystal || input.getItem() == Item.getItemFromBlock(WizardryBlocks.magic_crystal_block))){
if((input.getItem() == WizardryItems.magic_crystal || input.getItem() == Item.getItemFromBlock(WizardryBlocks.crystal_block))
&& input.getMetadata() == 0){
if(Arrays.stream(receptacleElements).distinct().count() == 1 && receptacleElements[0] != null){ // All the same element
return new ItemStack(input.getItem(), input.getCount(), receptacleElements[0].ordinal());
@@ -27,7 +27,7 @@ public class TileEntityReceptacle extends TileEntity {
public void setElement(@Nullable Element element){
this.element = element;
world.notifyNeighborsRespectDebug(pos, blockType, true); // Update altar if connected
world.notifyNeighborsRespectDebug(pos, getBlockType(), true); // Update altar if connected
world.checkLight(pos);
}
@@ -1,8 +1,7 @@
package electroblob.wizardry.tileentity;
import electroblob.wizardry.Wizardry;
import electroblob.wizardry.api.IElemental;
import electroblob.wizardry.block.BlockRunestonePedestal;
import electroblob.wizardry.block.BlockPedestal;
import electroblob.wizardry.constants.Element;
import electroblob.wizardry.entity.living.EntityEvilWizard;
import electroblob.wizardry.entity.living.EntityWizard;
@@ -15,87 +14,120 @@ import electroblob.wizardry.registry.WizardrySounds;
import electroblob.wizardry.spell.ArcaneLock;
import electroblob.wizardry.util.*;
import electroblob.wizardry.util.ParticleBuilder.Type;
import net.minecraft.block.state.IBlockState;
import net.minecraft.entity.Entity;
import net.minecraft.entity.EntityLivingBase;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.init.Blocks;
import net.minecraft.nbt.NBTBase;
import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.nbt.NBTTagList;
import net.minecraft.nbt.NBTUtil;
import net.minecraft.potion.PotionEffect;
import net.minecraft.tileentity.TileEntity;
import net.minecraft.tileentity.TileEntityChest;
import net.minecraft.util.ITickable;
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.util.math.MathHelper;
import net.minecraft.util.text.TextComponentTranslation;
import net.minecraftforge.common.util.Constants;
import net.minecraftforge.fml.common.network.NetworkRegistry;
import java.util.Arrays;
import java.util.List;
import java.util.UUID;
import java.util.*;
public class TileEntityShrineCore extends TileEntity implements ITickable {
private static final double ACTIVATION_RADIUS = 5;
private boolean activated = false;
private boolean conquered = false;
private long regenerationTime = 0;
private long lastRegenerationTime = 0;
private Element shrineElement;
private AxisAlignedBB containmentField;
private final UUID[] linkedWizards = new UUID[3];
private TileEntity linkedContainer;
private BlockPos linkedContainerPos; // Temporary stores the container position read from NBT until the world is set
private final Set<UUID> lootedPlayers = new HashSet<>();
@Override
public void setPos(BlockPos pos){
public void setPos(BlockPos pos) {
super.setPos(pos);
initContainmentField(pos);
}
private void initContainmentField(BlockPos pos){
private void initContainmentField(BlockPos pos) {
float r = PotionContainment.getContainmentDistance(0);
this.containmentField = new AxisAlignedBB(-r, -r, -r, r, r, r).offset(GeometryUtils.getCentre(pos));
}
public void linkContainer(TileEntity container){
public void linkContainer(TileEntity container) {
this.linkedContainer = container;
}
@Override
public void update(){
public void setShrineElement(Element element) {
this.shrineElement = element;
}
if(this.linkedContainer == null && this.linkedContainerPos != null){
public boolean canPlayerLoot(EntityPlayer player) {
if (Wizardry.settings.shrineAllowMultipleLoot) {
return true; // Allow multiple looting if enabled
}
return !lootedPlayers.contains(player.getUniqueID());
}
public void recordPlayerLoot(EntityPlayer player) {
if (!Wizardry.settings.shrineAllowMultipleLoot) {
lootedPlayers.add(player.getUniqueID());
this.markDirty();
}
}
@Override
public void update() {
if (this.linkedContainer == null && this.linkedContainerPos != null) {
this.linkContainer(world.getTileEntity(this.linkedContainerPos));
}
// Handle shrine regeneration
if (conquered && Wizardry.settings.shrineRegenerationEnabled && regenerationTime > 0) {
if (world.getTotalWorldTime() >= regenerationTime) {
regenerate();
return; // Don't process other logic during regeneration
}
}
double x = this.pos.getX() + 0.5;
double y = this.pos.getY() + 0.5;
double z = this.pos.getZ() + 0.5;
if(!activated && world.getClosestPlayer(x, y, z, ACTIVATION_RADIUS, false) != null){
if (!activated && !conquered && world.getClosestPlayer(x, y, z, ACTIVATION_RADIUS, false) != null && (lastRegenerationTime == 0 || world.getTotalWorldTime() - lastRegenerationTime > 100)) { // 5 second delay after regeneration
this.activated = true;
if(world.isRemote){
if (world.isRemote) {
ParticleBuilder.create(Type.SPHERE).pos(x, y + 1, z).clr(0xf06495).scale(5).time(12).spawn(world);
}
world.playSound(x, y, z,
WizardrySounds.BLOCK_PEDESTAL_ACTIVATE, SoundCategory.BLOCKS, 1.5f, 1, false);
world.playSound(x, y, z, WizardrySounds.BLOCK_PEDESTAL_ACTIVATE, SoundCategory.BLOCKS, 1.5f, 1, false);
if(!world.isRemote){
if (!world.isRemote) {
EntityEvilWizard[] wizards = new EntityEvilWizard[linkedWizards.length];
for(int i = 0; i < linkedWizards.length; i++){
for (int i = 0; i < linkedWizards.length; i++) {
EntityEvilWizard wizard = new EntityEvilWizard(world);
float angle = world.rand.nextFloat() * 2 * (float)Math.PI;
float angle = world.rand.nextFloat() * 2 * (float) Math.PI;
double x1 = this.pos.getX() + 0.5 + 5 * MathHelper.sin(angle);
double z1 = this.pos.getZ() + 0.5 + 5 * MathHelper.cos(angle);
Integer y1 = BlockUtils.getNearestFloor(world, new BlockPos(x1, this.pos.getY(), z1), 8);
if(y1 == null){
if (y1 == null) {
// Fallback to the position of the shrine core if it failed to find a position (unlikely)
x1 = this.pos.getX() + 1; // Offset it so the wizard isn't inside the block
y1 = this.pos.getY();
@@ -103,8 +135,7 @@ public class TileEntityShrineCore extends TileEntity implements ITickable {
}
wizard.setLocationAndAngles(x1, y1 + 0.5, z1, 0, 0);
wizard.setElement(world.getBlockState(pos).getBlock() instanceof IElemental
? ((IElemental) world.getBlockState(pos).getBlock()).getElement() : Element.MAGIC);
wizard.setElement(world.getBlockState(pos).getValue(BlockPedestal.ELEMENT));
wizard.onInitialSpawn(world.getDifficultyForLocation(pos), null);
wizard.hasStructure = true;
@@ -113,109 +144,279 @@ public class TileEntityShrineCore extends TileEntity implements ITickable {
linkedWizards[i] = wizard.getUniqueID();
}
for(EntityEvilWizard wizard : wizards) wizard.groupUUIDs.addAll(Arrays.asList(linkedWizards));
for (EntityEvilWizard wizard : wizards) wizard.groupUUIDs.addAll(Arrays.asList(linkedWizards));
}
containNearbyTargets();
}
if(activated && world.getTotalWorldTime() % 20L == 0) containNearbyTargets();
if (!areWizardsDead() && activated && world.getTotalWorldTime() % 20L == 0) containNearbyTargets();
if(activated && areWizardsDead() && !world.isRemote){
if (activated && areWizardsDead() && !world.isRemote) {
conquer();
}
}
private boolean areWizardsDead(){
private boolean areWizardsDead() {
for(UUID uuid : linkedWizards){
for (UUID uuid : linkedWizards) {
Entity entity = EntityUtils.getEntityByUUID(world, uuid);
if(entity instanceof EntityEvilWizard && entity.isEntityAlive()) return false;
if (entity instanceof EntityEvilWizard && entity.isEntityAlive()) return false;
}
return true;
}
public void conquer(){
public void conquer() {
double x = this.pos.getX() + 0.5;
double y = this.pos.getY() + 0.5;
double z = this.pos.getZ() + 0.5;
if(!world.isRemote){
if (!world.isRemote) {
WizardryPacketHandler.net.sendToAllAround(new PacketConquerShrine.Message(this.pos),
new NetworkRegistry.TargetPoint(this.world.provider.getDimension(), x, y, z, 64));
WizardryPacketHandler.net.sendToAllAround(new PacketConquerShrine.Message(this.pos), new NetworkRegistry.TargetPoint(this.world.provider.getDimension(), x, y, z, 64));
if(world.getBlockState(pos).getBlock() instanceof BlockRunestonePedestal){
world.setBlockState(pos, world.getBlockState(pos).getBlock().getDefaultState());
}else{
Wizardry.logger.warn("What's going on?! A shrine core is being conquered but the block at its position is not a runestone pedestal!");
// Remove containment effects from nearby targets when shrine is conquered
removeContainmentFromNearbyTargets();
// If regeneration is enabled, schedule regeneration instead of removing the tile entity
if (Wizardry.settings.shrineRegenerationEnabled) {
this.conquered = true;
this.regenerationTime = world.getTotalWorldTime() + (Wizardry.settings.shrineRegenerationTime * 1200L); // Convert minutes to ticks (20 ticks per second * 60 seconds)
this.activated = false;
Arrays.fill(this.linkedWizards, null); // Clear wizard references
// Keep lootedPlayers list - it's permanent tracking for this shrine instance
// Handle chest breaking and loot dropping
handleChestLootDrop();
// Mark the tile entity for update
this.markDirty();
if (world.getBlockState(pos).getBlock() == WizardryBlocks.runestone_pedestal) {
// Keep the block state the same, just mark as conquered
this.shrineElement = world.getBlockState(pos).getValue(BlockPedestal.ELEMENT);
} else {
Wizardry.logger.warn("What's going on?! A shrine core is being conquered but the block at its position is not a runestone pedestal!");
}
} else {
// Original behavior: remove the tile entity
BlockPos chestPos = this.pos.up();
TileEntity chestTileEntity = world.getTileEntity(chestPos);
if (chestTileEntity instanceof TileEntityChest) {
TileEntityChest chest = (TileEntityChest) chestTileEntity;
NBTExtras.removeUniqueId(chest.getTileData(), ArcaneLock.NBT_KEY);
chest.markDirty();
world.markAndNotifyBlock(pos, null, world.getBlockState(pos), world.getBlockState(pos), 3);
}
if (world.getBlockState(pos).getBlock() == WizardryBlocks.runestone_pedestal) {
world.setBlockState(pos, WizardryBlocks.runestone_pedestal.getDefaultState().withProperty(BlockPedestal.ELEMENT, world.getBlockState(pos).getValue(BlockPedestal.ELEMENT)));
} else {
Wizardry.logger.warn("What's going on?! A shrine core is being conquered but the block at its position is not a runestone pedestal!");
}
world.markTileEntityForRemoval(this);
}
}
world.markTileEntityForRemoval(this);
if(!world.isRemote){
if(linkedContainer != null) NBTExtras.removeUniqueId(linkedContainer.getTileData(), ArcaneLock.NBT_KEY);
}else{
TileEntity tileEntity = world.getTileEntity(this.pos.up());
if(tileEntity != null){ // Bit of a dirty fix but it's only visual, so meh
NBTExtras.removeUniqueId(tileEntity.getTileData(), ArcaneLock.NBT_KEY);
}
if (linkedContainer == null) {
linkedContainer = world.getTileEntity(pos.up());
}
if (linkedContainer != null) {
NBTExtras.removeUniqueId(linkedContainer.getTileData(), ArcaneLock.NBT_KEY);
//linkedContainer.getTileData().removeTag("arcaneLockOwnerMost");
//linkedContainer.getTileData().removeTag("arcaneLockOwnerLeast");
}
world.playSound(x, y, z, WizardrySounds.BLOCK_PEDESTAL_CONQUER, SoundCategory.BLOCKS, 1, 1, false);
if(world.isRemote){
if (world.isRemote) {
ParticleBuilder.create(Type.SPHERE).scale(5).pos(x, y + 1, z).clr(0xf06495).time(12).spawn(world);
for(int i=0; i<5; i++){
for (int i = 0; i < 5; i++) {
float brightness = 0.8f + world.rand.nextFloat() * 0.2f;
ParticleBuilder.create(Type.SPARKLE, world.rand, x, y + 1, z, 1, true)
.clr(1, brightness, brightness).spawn(world);
ParticleBuilder.create(Type.SPARKLE, world.rand, x, y + 1, z, 1, true).clr(1, brightness, brightness).spawn(world);
}
}
}
private void containNearbyTargets(){
private void regenerate() {
List<EntityLivingBase> entities = world.getEntitiesWithinAABB(EntityLivingBase.class, containmentField,
e -> e instanceof EntityPlayer || e instanceof EntityWizard || e instanceof EntityEvilWizard);
if (world.isRemote) return;
for(EntityLivingBase entity : entities){
// Remove containment effects from nearby targets when shrine regenerates
removeContainmentFromNearbyTargets();
// Reset shrine state
this.conquered = false;
this.regenerationTime = 0;
this.activated = false;
Arrays.fill(this.linkedWizards, null);
// Keep lootedPlayers list - it's permanent for this shrine instance
// Force a short delay before allowing reactivation to prevent immediate re-activation
this.lastRegenerationTime = world.getTotalWorldTime();
// Forcibly replace whatever block is above the altar with a fresh chest
BlockPos chestPos = this.pos.up();
// Always replace the block above with a fresh chest during regeneration
world.setBlockState(chestPos, Blocks.CHEST.getDefaultState());
// Set up the loot table for the shrine chest
TileEntity chestTileEntity = world.getTileEntity(chestPos);
if (chestTileEntity instanceof TileEntityChest) {
TileEntityChest chest = (TileEntityChest) chestTileEntity;
chest.setLootTable(new ResourceLocation(Wizardry.MODID, "chests/shrine"), world.rand.nextLong());
}
// Link and apply arcane lock to the container
if (chestTileEntity != null) {
this.linkContainer(chestTileEntity);
chestTileEntity.getTileData().setUniqueId(ArcaneLock.NBT_KEY, new UUID(0, 0)); // Nil UUID for shrine lock
chestTileEntity.markDirty(); // Mark tile entity as dirty for client sync
// Trigger visual update for arcane lock effect
IBlockState blockState = world.getBlockState(chestPos);
world.markAndNotifyBlock(chestPos, null, blockState, blockState, 3);
world.notifyBlockUpdate(chestPos, blockState, blockState, 3); // Additional client sync
}
// Visual and audio effects for regeneration
double x = this.pos.getX() + 0.5;
double y = this.pos.getY() + 0.5;
double z = this.pos.getZ() + 0.5;
// WizardryPacketHandler.net.sendToAllAround(new PacketConquerShrine.Message(this.pos), new NetworkRegistry.TargetPoint(this.world.provider.getDimension(), x, y, z, 64));
if (world.isRemote) {
ParticleBuilder.create(Type.SPHERE).pos(x, y + 1, z).clr(0xf06495).scale(5).time(12).spawn(world);
}
world.playSound(x, y, z, WizardrySounds.BLOCK_PEDESTAL_ACTIVATE, SoundCategory.BLOCKS, 1.5f, 1, false);
this.markDirty();
}
private void handleChestLootDrop() {
BlockPos chestPos = this.pos.up();
// Check if there's a chest at the expected position
if (world.getBlockState(chestPos).getBlock() == Blocks.CHEST) {
TileEntity chestTileEntity = world.getTileEntity(chestPos);
if (chestTileEntity instanceof TileEntityChest) {
TileEntityChest chest = (TileEntityChest) chestTileEntity;
// Find the player who conquered the shrine (closest player)
EntityPlayer conqueringPlayer = world.getClosestPlayer(pos.getX() + 0.5, pos.getY() + 0.5, pos.getZ() + 0.5, 20.0, false);
if (conqueringPlayer != null && !canPlayerLoot(conqueringPlayer)) {
// Send message to player that they've already looted this shrine
if (!world.isRemote) {
conqueringPlayer.sendMessage(new TextComponentTranslation("wizardry.shrine_already_looted"));
}
chest.setLootTable(null, world.rand.nextLong());
if (world.getBlockState(chestPos).getBlock() == Blocks.CHEST) {
world.setBlockToAir(chestPos);
}
return;
}
if (conqueringPlayer != null && canPlayerLoot(conqueringPlayer)) {
// Record that this player has looted the shrine
recordPlayerLoot(conqueringPlayer);
}
// If player has already looted, don't drop anything
}
}
// Break the chest regardless
if (world.getBlockState(chestPos).getBlock() == Blocks.CHEST) {
world.setBlockToAir(chestPos);
}
}
private void containNearbyTargets() {
List<EntityLivingBase> entities = world.getEntitiesWithinAABB(EntityLivingBase.class, containmentField, e -> e instanceof EntityPlayer || e instanceof EntityWizard || e instanceof EntityEvilWizard);
for (EntityLivingBase entity : entities) {
entity.addPotionEffect(new PotionEffect(WizardryPotions.containment, 219));
NBTExtras.storeTagSafely(entity.getEntityData(), PotionContainment.ENTITY_TAG, NBTUtil.createPosTag(this.pos));
}
}
private void removeContainmentFromNearbyTargets() {
// List<EntityLivingBase> entities = world.getEntitiesWithinAABB(EntityLivingBase.class, containmentField,
// e -> e instanceof EntityPlayer || e instanceof EntityWizard || e instanceof EntityEvilWizard);
//
// for(EntityLivingBase entity : entities){
// // Remove the containment potion effect
// if(entity.isPotionActive(WizardryPotions.containment)){
// entity.removePotionEffect(WizardryPotions.containment);
// }
// // Also remove the containment position tag
// if(entity.getEntityData().hasKey(PotionContainment.ENTITY_TAG)){
// BlockPos containmentPos = NBTUtil.getPosFromTag(entity.getEntityData().getCompoundTag(PotionContainment.ENTITY_TAG));
// // Only remove if the containment position matches this shrine
// if(containmentPos.equals(this.pos)){
// entity.getEntityData().removeTag(PotionContainment.ENTITY_TAG);
// }
// }
// }
}
@Override
public NBTTagCompound writeToNBT(NBTTagCompound compound){
public NBTTagCompound writeToNBT(NBTTagCompound compound) {
compound.setBoolean("activated", this.activated);
if(linkedContainer != null) NBTExtras.storeTagSafely(compound, "linkedContainerPos", NBTUtil.createPosTag(linkedContainer.getPos()));
compound.setBoolean("conquered", this.conquered);
compound.setLong("regenerationTime", this.regenerationTime);
compound.setLong("lastRegenerationTime", this.lastRegenerationTime);
if (shrineElement != null) compound.setInteger("shrineElement", this.shrineElement.ordinal());
if (linkedContainer != null)
NBTExtras.storeTagSafely(compound, "linkedContainerPos", NBTUtil.createPosTag(linkedContainer.getPos()));
NBTTagList tagList = new NBTTagList();
for(UUID uuid : linkedWizards){
if(uuid != null) tagList.appendTag(NBTUtil.createUUIDTag(uuid));
NBTTagList wizardTagList = new NBTTagList();
for (UUID uuid : linkedWizards) {
if (uuid != null) wizardTagList.appendTag(NBTUtil.createUUIDTag(uuid));
}
NBTExtras.storeTagSafely(compound, "wizards", tagList);
NBTExtras.storeTagSafely(compound, "wizards", wizardTagList);
NBTTagList playerTagList = new NBTTagList();
for (UUID uuid : lootedPlayers) {
playerTagList.appendTag(NBTUtil.createUUIDTag(uuid));
}
NBTExtras.storeTagSafely(compound, "lootedPlayers", playerTagList);
return super.writeToNBT(compound);
}
@Override
public void readFromNBT(NBTTagCompound compound){
public void readFromNBT(NBTTagCompound compound) {
this.activated = compound.getBoolean("activated");
this.conquered = compound.getBoolean("conquered");
this.regenerationTime = compound.getLong("regenerationTime");
this.lastRegenerationTime = compound.getLong("lastRegenerationTime");
if (compound.hasKey("shrineElement"))
this.shrineElement = Element.values()[compound.getInteger("shrineElement")];
this.linkedContainerPos = NBTUtil.getPosFromTag(compound.getCompoundTag("linkedContainerPos"));
NBTTagList tagList = compound.getTagList("wizards", Constants.NBT.TAG_COMPOUND);
NBTTagList wizardTagList = compound.getTagList("wizards", Constants.NBT.TAG_COMPOUND);
int i = 0;
for(NBTBase tag : tagList){
if(tag instanceof NBTTagCompound) linkedWizards[i++] = NBTUtil.getUUIDFromTag((NBTTagCompound)tag);
for (NBTBase tag : wizardTagList) {
if (tag instanceof NBTTagCompound) linkedWizards[i++] = NBTUtil.getUUIDFromTag((NBTTagCompound) tag);
else Wizardry.logger.warn("Unexpected tag type in NBT tag list of compound tags!");
}
this.lootedPlayers.clear();
if (compound.hasKey("lootedPlayers")) {
NBTTagList playerTagList = compound.getTagList("lootedPlayers", Constants.NBT.TAG_COMPOUND);
for (NBTBase tag : playerTagList) {
if (tag instanceof NBTTagCompound) lootedPlayers.add(NBTUtil.getUUIDFromTag((NBTTagCompound) tag));
}
}
super.readFromNBT(compound);
// Must be after super
initContainmentField(this.pos);
@@ -141,6 +141,11 @@ public final class AllyDesignationSystem {
return false;
}
// Tests whether the target is a creature that was summoned/tamed (or is otherwise owned) by the attacker
if(target instanceof IEntityOwnable && attacker instanceof EntityLiving && !(((EntityLiving)attacker).getRevengeTarget() == ((IEntityOwnable)target).getOwner() || ((EntityLiving)attacker).getAttackTarget() == ((IEntityOwnable)target).getOwner())){
return false;
}
// Tests whether the target is a creature that was mind controlled by the attacker
if(target instanceof EntityLiving && ((EntityLivingBase)target).isPotionActive(WizardryPotions.mind_control)){
@@ -201,7 +206,7 @@ public final class AllyDesignationSystem {
// Owned entities inherit their owner's allies
if(allyOf instanceof IEntityOwnable){
Entity owner = ((IEntityOwnable)allyOf).getOwner();
if(owner instanceof EntityLivingBase && isAllied((EntityLivingBase)owner, possibleAlly)) return true;
if(owner instanceof EntityLivingBase && (owner == possibleAlly || isAllied((EntityLivingBase)owner, possibleAlly))) return true;
}
if(allyOf instanceof EntityPlayer && possibleAlly instanceof EntityPlayer
@@ -215,6 +220,16 @@ public final class AllyDesignationSystem {
if(allyOf instanceof EntityPlayer && isOwnerAlly((EntityPlayer)allyOf, pet)) return true;
}
// Check if the possibleAlly is mind controlled by the allyOf entity
if(possibleAlly instanceof EntityLiving && possibleAlly.isPotionActive(WizardryPotions.mind_control)){
NBTTagCompound entityNBT = possibleAlly.getEntityData();
if(entityNBT != null && entityNBT.hasUniqueId(MindControl.NBT_KEY)){
Entity controller = EntityUtils.getEntityByUUID(possibleAlly.world, entityNBT.getUniqueId(MindControl.NBT_KEY));
return controller == allyOf;
}
}
return false;
}
@@ -117,6 +117,45 @@ public final class EntityUtils {
return entityList;
}
/**
* Returns all EntityLivingBase within the cylinder radius of the given coordinates. This should
* used by circle effects.
*
* @param radius The search radius
* @param x The x coordinate to search around
* @param y The y coordinate to search around
* @param z The z coordinate to search around
* @param height The height of the cylinder
* @param world The world to search in
*/
public static List<EntityLivingBase> getLivingWithinCylinder(double radius, double x, double y, double z, double height, World world) {
return getEntitiesWithinCylinder(radius, x, y, z, height, world, EntityLivingBase.class);
}
/**
* Returns all entities of the specified type within the cylinder radius of the given coordinates. This should
* used by circle effects.
*
* @param radius The search radius
* @param x The x coordinate to search around
* @param y The y coordinate to search around
* @param z The z coordinate to search around
* @param height The height of the cylinder
* @param world The world to search in
* @param entityType The class of entity to search for; pass in Entity.class for all entities
*/
public static <T extends Entity> List<T> getEntitiesWithinCylinder(double radius, double x, double y, double z, double height, World world, Class<T> entityType) {
AxisAlignedBB aabb = new AxisAlignedBB(x - radius, y, z - radius, x + radius, y + height, z + radius);
List<T> entityList = world.getEntitiesWithinAABB(entityType, aabb);
for(T entity : entityList) {
if (entity.getDistance(x, entity.posY, z) > radius) {
entityList.remove(entity);
break;
}
}
return entityList;
}
/**
* Gets an entity from its UUID. If the UUID is known to belong to an {@code EntityPlayer}, use the more efficient
* {@link World#getPlayerEntityByUUID(UUID)} instead.
@@ -20,8 +20,7 @@ public interface ISpellSortable {
TIER("tier", Comparator.naturalOrder()),
ELEMENT("element", Comparator.comparing(Spell::getElement).thenComparing(Spell::getTier)),
ALPHABETICAL("alphabetical", Comparator.comparing(Spell::getUnlocalisedName));
ALPHABETICAL("alphabetical", Comparator.comparing(s -> s.getRegistryName().getPath().toString()));
public String name;
public Comparator<? super Spell> comparator;
@@ -1,5 +1,7 @@
package electroblob.wizardry.util;
import electroblob.wizardry.Settings;
import electroblob.wizardry.Wizardry;
import electroblob.wizardry.entity.living.*;
import net.minecraft.entity.Entity;
import net.minecraft.entity.boss.EntityDragon;
@@ -195,7 +197,7 @@ public class MagicDamage extends EntityDamageSource implements IElementalDamage
* @return A damagesource object of type EntityDamageSource
*/
public static DamageSource causeDirectMagicDamage(Entity caster, DamageType type, boolean isRetaliatory){
return new MagicDamage(DIRECT_MAGIC_DAMAGE, caster, type, isRetaliatory);
return new MagicDamage(getDirectDamageNameForType(type), caster, type, isRetaliatory);
}
/**
@@ -241,7 +243,15 @@ public class MagicDamage extends EntityDamageSource implements IElementalDamage
*/
public static DamageSource causeIndirectMagicDamage(Entity magic, Entity caster, DamageType type,
boolean isRetaliatory){
return new IndirectMagicDamage(INDIRECT_MAGIC_DAMAGE, magic, caster, type, isRetaliatory);
return new IndirectMagicDamage(getIndirectDamageNameForType(type), magic, caster, type, isRetaliatory);
}
public static String getIndirectDamageNameForType(DamageType type) {
return Wizardry.settings.damageTypePerElement ? type.name().toLowerCase() + "_" + INDIRECT_MAGIC_DAMAGE : INDIRECT_MAGIC_DAMAGE;
}
public static String getDirectDamageNameForType(DamageType type) {
return Wizardry.settings.damageTypePerElement ? type.name().toLowerCase() + "_" + DIRECT_MAGIC_DAMAGE : DIRECT_MAGIC_DAMAGE;
}
@Override
@@ -1,9 +1,14 @@
package electroblob.wizardry.util;
import electroblob.wizardry.constants.Constants;
import electroblob.wizardry.item.IManaStoringItem;
import electroblob.wizardry.item.IWorkbenchItem;
import electroblob.wizardry.item.ItemCrystal;
import electroblob.wizardry.item.ItemWand;
import electroblob.wizardry.registry.Spells;
import electroblob.wizardry.registry.WizardryItems;
import electroblob.wizardry.spell.Spell;
import net.minecraft.inventory.Slot;
import net.minecraft.item.Item;
import net.minecraft.item.ItemStack;
import net.minecraft.nbt.NBTTagCompound;
@@ -494,4 +499,49 @@ public final class WandHelper {
setProgression(wand, getProgression(wand) + progression);
}
/**
* Recharges the mana of an item when the apply button is pressed in the arcane workbench GUI.
* This method requires the central item to implement both the {@link IWorkbenchItem} and {@link IManaStoringItem} interfaces.
*
* @param centre The slot representing the central item to be recharged.
* @param crystals The slot containing mana crystals for recharging.
* @return {@code true} if the mana of the central item was successfully recharged, {@code false} otherwise.
*/
public static boolean rechargeManaOnApplyButtonPressed(Slot centre, Slot crystals) {
boolean changed = false;
if (!(centre.getStack().getItem() instanceof IWorkbenchItem) || !(centre.getStack().getItem() instanceof IManaStoringItem)) {
return false;
}
IManaStoringItem iManaStoringItem = (IManaStoringItem) centre.getStack().getItem();
// Charges the item by appropriate amount
if (crystals.getStack() != ItemStack.EMPTY && !iManaStoringItem.isManaFull(centre.getStack())) {
int chargeDepleted = iManaStoringItem.getManaCapacity(centre.getStack()) - iManaStoringItem.getMana(centre.getStack());
// 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;}
if (crystals.getStack().getCount() * manaPerItem < chargeDepleted) {
// If there aren't enough crystals to fully charge the item
iManaStoringItem.rechargeMana(centre.getStack(), crystals.getStack().getCount() * manaPerItem);
crystals.decrStackSize(crystals.getStack().getCount());
} else {
// If there are excess crystals (or just enough)
iManaStoringItem.setMana(centre.getStack(), iManaStoringItem.getManaCapacity(centre.getStack()));
crystals.decrStackSize((int) Math.ceil(((double) chargeDepleted) / manaPerItem));
}
changed = true;
}
return changed;
}
}
@@ -1,6 +1,5 @@
package electroblob.wizardry.worldgen;
import electroblob.wizardry.block.BlockGildedWood;
import electroblob.wizardry.registry.WizardryBlocks;
import electroblob.wizardry.util.BlockUtils;
import net.minecraft.block.Block;
@@ -90,24 +89,11 @@ public class WoodTypeTemplateProcessor implements ITemplateProcessor {
public Template.BlockInfo processBlock(World world, BlockPos pos, Template.BlockInfo info){
// Why do these each have their own property key?
if(info.blockState.getBlock() instanceof BlockPlanks){ // This doesn't cover gilded wood
if(info.blockState.getBlock() instanceof BlockPlanks){ // This covers gilded wood too
return new Template.BlockInfo(info.pos, info.blockState.withProperty(BlockPlanks.VARIANT, woodType), info.tileentityData);
}else if(info.blockState.getBlock() instanceof BlockGildedWood){
switch (woodType) {
case OAK:
return new Template.BlockInfo(info.pos, WizardryBlocks.oak_gilded_wood.getDefaultState(), info.tileentityData);
case SPRUCE:
return new Template.BlockInfo(info.pos, WizardryBlocks.spruce_gilded_wood.getDefaultState(), info.tileentityData);
case BIRCH:
return new Template.BlockInfo(info.pos, WizardryBlocks.birch_gilded_wood.getDefaultState(), info.tileentityData);
case JUNGLE:
return new Template.BlockInfo(info.pos, WizardryBlocks.jungle_gilded_wood.getDefaultState(), info.tileentityData);
case ACACIA:
return new Template.BlockInfo(info.pos, WizardryBlocks.acacia_gilded_wood.getDefaultState(), info.tileentityData);
case DARK_OAK:
return new Template.BlockInfo(info.pos, WizardryBlocks.dark_oak_gilded_wood.getDefaultState(), info.tileentityData);
}
// This is a mess, no wonder the flattening happened
}else if(info.blockState.getBlock() instanceof BlockWoodSlab){
return new Template.BlockInfo(info.pos, info.blockState.withProperty(BlockWoodSlab.VARIANT, woodType), info.tileentityData);
// This is a mess, no wonder the flattening happened
}else if(DOORS.containsValue(info.blockState.getBlock())){
return new Template.BlockInfo(info.pos, BlockUtils.copyState(DOORS.get(woodType), info.blockState), info.tileentityData);
}else if(STAIRS.containsValue(info.blockState.getBlock())){
@@ -5,7 +5,6 @@ import electroblob.wizardry.block.BlockRunestone;
import electroblob.wizardry.constants.Element;
import electroblob.wizardry.entity.living.EntityRemnant;
import electroblob.wizardry.integration.antiqueatlas.WizardryAntiqueAtlasIntegration;
import net.minecraft.block.Block;
import net.minecraft.init.Blocks;
import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.tileentity.MobSpawnerBaseLogic;
@@ -18,10 +17,8 @@ import net.minecraft.world.World;
import net.minecraft.world.gen.structure.template.ITemplateProcessor;
import net.minecraft.world.gen.structure.template.PlacementSettings;
import net.minecraft.world.gen.structure.template.Template;
import net.minecraftforge.fml.common.registry.ForgeRegistries;
import org.apache.commons.lang3.ArrayUtils;
import java.util.Arrays;
import java.util.Map;
import java.util.Random;
@@ -60,12 +57,10 @@ public class WorldGenObelisk extends WorldGenSurfaceStructure {
@Override
public void spawnStructure(Random random, World world, BlockPos origin, Template template, PlacementSettings settings, ResourceLocation structureFile){
Element[] elements = (Element[]) Arrays.stream(Element.values()).filter(Element::hasWorldgen).toArray();
final Element element = elements[1 + random.nextInt(elements.length-1)];
Block runeStone = ForgeRegistries.BLOCKS.getValue(new ResourceLocation(element.getModid(), element.getName().toLowerCase() + "_runestone"));
final Element element = Element.values()[1 + random.nextInt(Element.values().length-1)];
ITemplateProcessor processor = (w, p, i) -> i.blockState.getBlock() instanceof BlockRunestone ? new Template.BlockInfo(
i.pos, runeStone.getDefaultState(), i.tileentityData) : i;
i.pos, i.blockState.withProperty(BlockRunestone.ELEMENT, element), i.tileentityData) : i;
template.addBlocksToWorld(world, origin, processor, settings, 2 | 16);
@@ -1,13 +1,13 @@
package electroblob.wizardry.worldgen;
import electroblob.wizardry.Wizardry;
import electroblob.wizardry.block.BlockPedestal;
import electroblob.wizardry.block.BlockRunestone;
import electroblob.wizardry.block.BlockRunestonePedestal;
import electroblob.wizardry.constants.Element;
import electroblob.wizardry.integration.antiqueatlas.WizardryAntiqueAtlasIntegration;
import electroblob.wizardry.registry.WizardryBlocks;
import electroblob.wizardry.spell.ArcaneLock;
import electroblob.wizardry.tileentity.TileEntityShrineCore;
import net.minecraft.block.Block;
import net.minecraft.tileentity.TileEntity;
import net.minecraft.util.ResourceLocation;
import net.minecraft.util.math.BlockPos;
@@ -15,10 +15,8 @@ import net.minecraft.world.World;
import net.minecraft.world.gen.structure.template.ITemplateProcessor;
import net.minecraft.world.gen.structure.template.PlacementSettings;
import net.minecraft.world.gen.structure.template.Template;
import net.minecraftforge.fml.common.registry.ForgeRegistries;
import org.apache.commons.lang3.ArrayUtils;
import java.util.Arrays;
import java.util.Map;
import java.util.Random;
import java.util.UUID;
@@ -51,12 +49,10 @@ public class WorldGenShrine extends WorldGenSurfaceStructure {
@Override
public void spawnStructure(Random random, World world, BlockPos origin, Template template, PlacementSettings settings, ResourceLocation structureFile){
Element[] elements = (Element[]) Arrays.stream(Element.values()).filter(Element::hasWorldgen).toArray();
final Element element = elements[1 + random.nextInt(elements.length-1)];
Block runeStone = ForgeRegistries.BLOCKS.getValue(new ResourceLocation(element.getModid(), element.getName().toLowerCase() + "_runestone"));
final Element element = Element.values()[1 + random.nextInt(Element.values().length-1)];
ITemplateProcessor processor = (w, p, i) -> i.blockState.getBlock() instanceof BlockRunestone ? new Template.BlockInfo(
i.pos, runeStone.getDefaultState(), i.tileentityData) : i;
i.pos, i.blockState.withProperty(BlockRunestone.ELEMENT, element), i.tileentityData) : i;
template.addBlocksToWorld(world, origin, processor, settings, 2 | 16);
@@ -69,8 +65,8 @@ public class WorldGenShrine extends WorldGenSurfaceStructure {
if(entry.getValue().equals(CORE_DATA_BLOCK_TAG)){
// This bit could have been done with a template processor, but we also need to link the chest and lock it
world.setBlockState(entry.getKey(), ForgeRegistries.BLOCKS.getValue(new ResourceLocation(Wizardry.MODID, element.getName().toLowerCase()
+ "_runestone_pedestal")).getDefaultState().withProperty(BlockRunestonePedestal.NATURAL, true));
world.setBlockState(entry.getKey(), WizardryBlocks.runestone_pedestal.getDefaultState()
.withProperty(BlockPedestal.ELEMENT, element).withProperty(BlockPedestal.NATURAL, true));
TileEntity core = world.getTileEntity(entry.getKey());
TileEntity container = world.getTileEntity(entry.getKey().up());
@@ -78,13 +74,27 @@ public class WorldGenShrine extends WorldGenSurfaceStructure {
if(container != null){
container.getTileData().setUniqueId(ArcaneLock.NBT_KEY, new UUID(0, 0)); // Nil UUID
container.markDirty(); // Mark tile entity as dirty for client sync
if(core instanceof TileEntityShrineCore){
((TileEntityShrineCore)core).linkContainer(container);
}else{
Wizardry.logger.info("What?!");
// Trigger visual update for arcane lock effect
BlockPos chestPos = entry.getKey().up();
net.minecraft.block.state.IBlockState blockState = world.getBlockState(chestPos);
world.markAndNotifyBlock(chestPos, null, blockState, blockState, 3);
world.notifyBlockUpdate(chestPos, blockState, blockState, 3); // Additional client sync
// Set up the loot table for the shrine chest
if(container instanceof net.minecraft.tileentity.TileEntityChest){
net.minecraft.tileentity.TileEntityChest chest = (net.minecraft.tileentity.TileEntityChest) container;
chest.setLootTable(new net.minecraft.util.ResourceLocation(Wizardry.MODID, "chests/shrine"), world.rand.nextLong());
}
if(core instanceof TileEntityShrineCore){
((TileEntityShrineCore)core).linkContainer(container);
((TileEntityShrineCore)core).setShrineElement(element);
}else{
Wizardry.logger.info("What?!");
}
}else{
Wizardry.logger.info("Expected chest or other container at {} in structure {}, found no tile entity", entry.getKey(), structureFile);
}
@@ -7,7 +7,8 @@
"translate": "advancement.ebwizardry:crystal.desc"
},
"icon": {
"item": "ebwizardry:magic_crystal"
"item": "ebwizardry:magic_crystal",
"data": 0
}
},
"parent": "ebwizardry:root",
@@ -17,28 +18,8 @@
"conditions": {
"items": [
{
"item": "ebwizardry:magic_crystal"
},
{
"item": "ebwizardry:fire_crystal"
},
{
"item": "ebwizardry:ice_crystal"
},
{
"item": "ebwizardry:lightning_crystal"
},
{
"item": "ebwizardry:necromancy_crystal"
},
{
"item": "ebwizardry:earth_crystal"
},
{
"item": "ebwizardry:sorcery_crystal"
},
{
"item": "ebwizardry:healing_crystal"
"item": "ebwizardry:magic_crystal",
"data": 0
}
]
}
@@ -7,7 +7,8 @@
"translate": "advancement.ebwizardry:defeat_remnant.desc"
},
"icon": {
"item": "ebwizardry:spectral_dust_sorcery"
"item": "ebwizardry:spectral_dust",
"data": 6
}
},
"parent": "ebwizardry:arcane_initiate",
@@ -17,7 +18,8 @@
"conditions": {
"items": [
{
"item": "ebwizardry:spectral_dust_fire"
"item": "ebwizardry:spectral_dust",
"data": 1
}
]
}
@@ -27,7 +29,8 @@
"conditions": {
"items": [
{
"item": "ebwizardry:spectral_dust_ice"
"item": "ebwizardry:spectral_dust",
"data": 2
}
]
}
@@ -37,7 +40,8 @@
"conditions": {
"items": [
{
"item": "ebwizardry:spectral_dust_lightning"
"item": "ebwizardry:spectral_dust",
"data": 3
}
]
}
@@ -47,7 +51,8 @@
"conditions": {
"items": [
{
"item": "ebwizardry:spectral_dust_necromancy"
"item": "ebwizardry:spectral_dust",
"data": 4
}
]
}
@@ -57,7 +62,8 @@
"conditions": {
"items": [
{
"item": "ebwizardry:spectral_dust_earth"
"item": "ebwizardry:spectral_dust",
"data": 5
}
]
}
@@ -67,7 +73,8 @@
"conditions": {
"items": [
{
"item": "ebwizardry:spectral_dust_sorcery"
"item": "ebwizardry:spectral_dust",
"data": 6
}
]
}
@@ -77,7 +84,7 @@
"conditions": {
"items": [
{
"item": "ebwizardry:spectral_dust_healing",
"item": "ebwizardry:spectral_dust",
"data": 7
}
]
@@ -5,13 +5,7 @@
"conditions": {
"items": [
{
"item": "ebwizardry:arcane_tome_apprentice"
},
{
"item": "ebwizardry:arcane_tome_advanced"
},
{
"item": "ebwizardry:arcane_tome_master"
"item": "ebwizardry:arcane_tome"
}
]
}
@@ -7,7 +7,7 @@
"translate": "advancement.ebwizardry:max_out_wand.desc"
},
"icon": {
"item": "ebwizardry:arcane_tome_master"
"item": "ebwizardry:arcane_tome"
},
"frame": "goal"
},

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