Compare commits
174 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| f37812be3e | |||
| 2680d02304 | |||
| f564fdce60 | |||
| 3bdee06738 | |||
| 31673cf247 | |||
| f6f36a30fd | |||
| 2db96cf18d | |||
| b8ba7d1ea4 | |||
| b33037fd1b | |||
| a378fe86f5 | |||
| 5dfa58bf27 | |||
| 740bfb1a8f | |||
| 03e8682260 | |||
| 4ca89b2dca | |||
| 212fbe51df | |||
| eb2abdfbe9 | |||
| f8cfc59415 | |||
| 13569058ae | |||
| 78189ed862 | |||
| 09185e5f1c | |||
| 201f276c8b | |||
| 224ee281ef | |||
| f907d50333 | |||
| 95ce659ec3 | |||
| f9f6b34549 | |||
| bee7e51d06 | |||
| 51a2ff0976 | |||
| 1b298ff18b | |||
| 86d13d3a6e | |||
| 5421372754 | |||
| 53b29939f1 | |||
| 7bda8db86f | |||
| a7a48cd32b | |||
| b8e0836a6d | |||
| a7e6b2892c | |||
| f886ebfd75 | |||
| 98b334e6d7 | |||
| 83edb8007e | |||
| 67dda448f3 | |||
| 09953c0299 | |||
| 38b0fa65bc | |||
| 7200182c82 | |||
| e73e998de2 | |||
| b374f71533 | |||
| a69da2eb2a | |||
| a00c594d83 | |||
| 3df99552e1 | |||
| e2bc2824f6 | |||
| 36f4728f3a | |||
| 6ca72e7965 | |||
| 151dcc06da | |||
| 5737109dec | |||
| 4bd3a46f32 | |||
| 73e2d64fcd | |||
| 1f008b405d | |||
| 00e1e2602f | |||
| 98445da971 | |||
| ad363c64af | |||
| 82eed83dcc | |||
| 7c7a69c755 | |||
| af01beb583 | |||
| b7cd3db8bc | |||
| 056c35ac3f | |||
| f6a24038cc | |||
| fde5935adc | |||
| f8eecfc6d5 | |||
| 53aa19d1a1 | |||
| a49756ddaa | |||
| ee3fca35bd | |||
| 55ae4b1161 | |||
| 76473f7185 | |||
| ff87729ee8 | |||
| 9464b3a084 | |||
| d0bf90b900 | |||
| fec4b755bd | |||
| 73d973783d | |||
| 75e5de603e | |||
| 6da42a18c7 | |||
| f3c9fbdd2e | |||
| c7c7d57e7e | |||
| c3abdcf68b | |||
| 8f0727403d | |||
| 2301e54de4 | |||
| be952e65f4 | |||
| 24457ebe36 | |||
| a523d3dd2b | |||
| 373fd4c8e8 | |||
| 106148fd9e | |||
| 29261082f1 | |||
| dbc1490e10 | |||
| b6400c72a7 | |||
| 719c6d5fbb | |||
| f33a0aceb2 | |||
| 7141ae9544 | |||
| af638ab2f7 | |||
| a75a6c6114 | |||
| 438f5d7a2e | |||
| dec5b1ac2c | |||
| 726a306830 | |||
| 330c78a92c | |||
| dcec7c7281 | |||
| b9ba25c116 | |||
| 0341a603d5 | |||
| d91a3e056a | |||
| 9c1042f686 | |||
| cad3fee1b9 | |||
| 5947b2ee62 | |||
| 177a39bad1 | |||
| cdf8f6e32d | |||
| f88aac11e9 | |||
| 05b905b988 | |||
| 669cd2892d | |||
| dd07d08ff0 | |||
| 54bb327fdf | |||
| 2cd8b46c1f | |||
| b61413f7f1 | |||
| 4e7765a7e7 | |||
| 49a6dc43c1 | |||
| b82bc6698f | |||
| 1e43c67660 | |||
| c129571167 | |||
| b23a643c0e | |||
| 194bff3e6a | |||
| 426ca78288 | |||
| f15919fdce | |||
| 2124842136 | |||
| 70e697876b | |||
| 9b5e5a871c | |||
| 5ba92e5de5 | |||
| 4e6780a3cf | |||
| 00584ad635 | |||
| f72ed33515 | |||
| c2366ed1ba | |||
| 46df8d7311 | |||
| d4579b9184 | |||
| ee6f0cfe70 | |||
| 6155fd9c68 | |||
| 32c0608b51 | |||
| 404df6af23 | |||
| 764e10538b | |||
| 6b1b590e30 | |||
| 8360e520cd | |||
| 005a17d750 | |||
| 28abe036a0 | |||
| ca96112b67 | |||
| b8e847c6a6 | |||
| 35cb2c31fc | |||
| 30dd587d34 | |||
| 4644814582 | |||
| 7db3151d8a | |||
| c8ca73cca9 | |||
| d5a0479ebe | |||
| 466b3bbc27 | |||
| df14912693 | |||
| 8a53cc6150 | |||
| 339ec69a72 | |||
| b2cb717aa3 | |||
| cf4a02f37f | |||
| 84304dd7ce | |||
| e3b44eb194 | |||
| 2cbb0a590f | |||
| 6b9c864890 | |||
| f5730bf945 | |||
| a1ae3a2b8b | |||
| c15a0763d5 | |||
| 4a23d88f30 | |||
| 286705c79b | |||
| 3895f5c531 | |||
| c51e0bd218 | |||
| 766570aeb2 | |||
| 4e91b0d19d | |||
| 65f7630546 | |||
| 8cf7572041 | |||
| db057a5ca9 |
+5
-1
@@ -20,5 +20,9 @@ build
|
||||
# other
|
||||
eclipse
|
||||
run
|
||||
misc
|
||||
logs
|
||||
/misc
|
||||
/libs
|
||||
libsfordeobf
|
||||
generated
|
||||
Thumbs.db
|
||||
+6
-2
@@ -1,8 +1,8 @@
|
||||
# Credits
|
||||
|
||||
Electroblob's Wizardry
|
||||
Version 3.1.0
|
||||
For Minecraft 1.11.2
|
||||
Version 4.1.4
|
||||
For Minecraft 1.12.2
|
||||
|
||||
Designed, coded and textured by Electroblob
|
||||
|
||||
@@ -15,10 +15,14 @@ In addition, I'd like to thank the following individuals for their contributions
|
||||
#### Code
|
||||
|
||||
- Corail31
|
||||
- 12foo
|
||||
- Shadows-of-Fire
|
||||
- HellFirePvP
|
||||
|
||||
#### Translations
|
||||
|
||||
- Russian: VilagVil
|
||||
- Spanish and Mexican Spanish: MadWrist
|
||||
- Chinese: ZHENGLOC and dragon-evol
|
||||
|
||||
Lightning ray sound effect from OhhWowProductions
|
||||
|
||||
+4
-2
@@ -1,5 +1,7 @@
|
||||
Minecraft version: 1.10.2 [change as necessary]
|
||||
Wizardry version: 2.1.0 [change as necessary]
|
||||
Please read the [guide for contributing](https://github.com/Electroblob77/Wizardry/blob/1.12.2/guide_for_contributing.md) before posting.
|
||||
|
||||
Minecraft version: 1.12.2 [change as necessary]
|
||||
Wizardry version: 4.1.4 [change as necessary]
|
||||
Environment: Singleplayer/LAN game/Server [delete as necessary]
|
||||
|
||||
Issue details: [describe what you were doing when the issue occurred, what went wrong, what you expected to happen, etc.]
|
||||
|
||||
+2
-2
@@ -1,7 +1,7 @@
|
||||
# License
|
||||
This repository is copyright © Electroblob 2018. The following license is provided to allow you to creatively contribute to and improve the repository.
|
||||
|
||||
This license applies only to the source code and assets present in this repository. The compiled mod (available at https://minecraft.curseforge.com/projects/electroblobs-wizardry) comes under its own separate license, see the aforementioned webpage for details.
|
||||
This license applies only to the source code, assets and wiki pages present in this repository. The compiled mod (available at https://minecraft.curseforge.com/projects/electroblobs-wizardry) comes under its own separate license, see the aforementioned webpage for details.
|
||||
|
||||
TL;DR: Do what you want with this repo on Github, but ask me first if you want to put it anywhere else or publish your version. Don't try and make money off the repo or anything in it. I'm not responsible if the code breaks things, and if you help me out by submitting a PR, I can use and modify the contents of that PR as long as I give you credit. (This brief summary is non-binding and does not override or replace the full license below.)
|
||||
|
||||
@@ -17,7 +17,7 @@ TL;DR: Do what you want with this repo on Github, but ask me first if you want t
|
||||
|
||||
'Electroblob's Wizardry', 'Wizardry', 'the mod' - The Minecraft modification Electroblob's Wizardry
|
||||
|
||||
'Repository' - This repository on Github, containing this license, other informational files, configurations, code and assets which are part of Electroblob's Wizardry
|
||||
'Repository' - This repository on Github, containing this license, the wiki, other informational files, configurations, code and assets which are part of Electroblob's Wizardry
|
||||
|
||||
'Curseforge' - The webpage https://minecraft.curseforge.com/projects/electroblobs-wizardry
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||

|
||||
|
||||
Source code for Electroblob's Wizardry, a Minecraft mod currently for versions 1.7.10, 1.10.2 and 1.11.2. Wizardry adds an RPG-style system of magic spells to Minecraft with the aim of being as playable as possible. No crazy constructs, no perk trees, no complex recipes - simply find spell books, cast spells, and master the arcane!
|
||||
Source code for Electroblob's Wizardry, a Minecraft mod currently for versions 1.7.10, 1.10.2, 1.11.2, and 1.12.2. Wizardry adds an RPG-style system of magic spells to Minecraft with the aim of being as playable as possible. No crazy constructs, no perk trees, no complex recipes - simply find spell books, cast spells, and master the arcane!
|
||||
|
||||
Please read the [guide for contributing](guide_for_contributing.md) before submitting an issue or a PR.
|
||||
|
||||
|
||||
+42
-4
@@ -11,17 +11,39 @@ apply plugin: 'net.minecraftforge.gradle.forge'
|
||||
//Only edit below this line, the above code adds and enables the necessary things for Forge to be setup.
|
||||
|
||||
|
||||
version = " 4.1.0 - MC 1.12.2"
|
||||
version = "4.2.0" // There, it matches semver, happy now? (I am just gonna rename the file afterwards, you know that right?)
|
||||
group= "electroblob.wizardry"// http://maven.apache.org/guides/mini/guide-naming-conventions.html
|
||||
archivesBaseName = "Electroblob's Wizardry "
|
||||
archivesBaseName = "ElectroblobsWizardry"
|
||||
|
||||
sourceCompatibility = targetCompatibility = "1.8" // Need this here so eclipse task generates correctly.
|
||||
compileJava {
|
||||
sourceCompatibility = targetCompatibility = "1.8"
|
||||
}
|
||||
|
||||
repositories {
|
||||
maven {
|
||||
// location of the maven that hosts JEI files
|
||||
name = "Progwml6 maven"
|
||||
url = "http://dvs1.progwml6.com/files/maven"
|
||||
}
|
||||
maven {
|
||||
// location of a maven mirror for JEI files, as a fallback
|
||||
name = "ModMaven"
|
||||
url = "modmaven.k-4u.nl"
|
||||
}
|
||||
flatDir {
|
||||
// evErYthINg hAS tO Be a mAVeN rEPoSiTOrY... (to use deobfProvided, for some reason)
|
||||
// The lack of documentation is actually quite impressive, see the following threads:
|
||||
// http://www.minecraftforge.net/forum/topic/44262-solved-how-to-build-mod-with-dependencies/
|
||||
// https://www.minecraftforum.net/forums/mapping-and-modding-java-edition/mapping-and-modding-tutorials/2866817-setting-up-dependencies-in-minecraft-forge-with
|
||||
// http://www.minecraftforge.net/forum/topic/44534-adding-dependencies-to-a-forge-project/
|
||||
dirs "libsfordeobf" // Don't use libs or forge will think you have duplicate mods
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
minecraft {
|
||||
version = "1.12.2-14.23.2.2611"
|
||||
version = "1.12.2-14.23.5.2814"
|
||||
runDir = "run"
|
||||
|
||||
// the mappings can be changed at any time, and must be in the following format.
|
||||
@@ -29,7 +51,7 @@ minecraft {
|
||||
// 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 = "snapshot_20180316"
|
||||
mappings = "stable_39"
|
||||
// makeObfSourceJar = false // an Srg named sources jar is made by default. uncomment this to disable.
|
||||
}
|
||||
|
||||
@@ -38,6 +60,17 @@ dependencies {
|
||||
// or you may define them like so..
|
||||
//compile "some.group:artifact:version:classifier"
|
||||
//compile "some.group:artifact:version"
|
||||
|
||||
// compile against the JEI API but do not include it at runtime
|
||||
deobfProvided "mezz.jei:jei_${mc_version}:${jei_version}:api"
|
||||
// at runtime, use the full JEI jar
|
||||
runtime "mezz.jei:jei_${mc_version}:${jei_version}"
|
||||
|
||||
//deobfProvided "baubles:baubles-1.12:1.5.2:api" // #ihavenoideawhatimdoing #itjustworks
|
||||
//runtime "baubles:baubles-1.12:1.5.2" // If in doubt, copy what already works
|
||||
|
||||
//deobfProvided "antiqueatlas:antiqueatlas-1.12.2:4.5.1:src"
|
||||
//runtime "antiqueatlas:antiqueatlas-1.12.2:4.5.1"
|
||||
|
||||
// real examples
|
||||
//compile 'com.mod-buildcraft:buildcraft:6.0.8:dev' // adds buildcraft to the dev env
|
||||
@@ -57,6 +90,11 @@ dependencies {
|
||||
|
||||
}
|
||||
|
||||
task deobfJar(type: Jar) {
|
||||
from sourceSets.main.output
|
||||
classifier = 'deobf'
|
||||
}
|
||||
|
||||
processResources {
|
||||
// this will ensure that this task is redone when the versions change.
|
||||
inputs.property "version", project.version
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
# 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
|
||||
mc_version=1.12.2
|
||||
jei_version=4.13.1.225
|
||||
@@ -79,14 +79,14 @@ if [ -n "$JAVA_HOME" ] ; 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."
|
||||
locations of your Java installation."
|
||||
fi
|
||||
else
|
||||
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."
|
||||
locations of your Java installation."
|
||||
fi
|
||||
|
||||
# Increase the maximum file descriptors if we can.
|
||||
|
||||
Vendored
+2
-2
@@ -27,7 +27,7 @@ echo.
|
||||
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||
echo.
|
||||
echo Please set the JAVA_HOME variable in your environment to match the
|
||||
echo location of your Java installation.
|
||||
echo locations of your Java installation.
|
||||
|
||||
goto fail
|
||||
|
||||
@@ -41,7 +41,7 @@ echo.
|
||||
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
|
||||
echo.
|
||||
echo Please set the JAVA_HOME variable in your environment to match the
|
||||
echo location of your Java installation.
|
||||
echo locations of your Java installation.
|
||||
|
||||
goto fail
|
||||
|
||||
|
||||
@@ -2,7 +2,20 @@
|
||||
|
||||
## Submitting an issue
|
||||
|
||||
When submitting an issue, please stick to the issue template. Please give me as much information about the issue as you can, but keep it relevant. "This is really annoying me and my friend" does not help me fix the problem! Most importantly, use gist or pastebin to post crash reports.
|
||||
When submitting an issue, I would be grateful if you could follow these few guidelines:
|
||||
|
||||
- First and foremost, please check your issue hasn't already been posted. The issue tracker is searchable, so try typing in a few keywords and see if anything comes up.
|
||||
|
||||
- Please stick to the issue template, it's there to help you provide the right information so I can fix the problem more easily. If you're not sure what to put for anything, that's ok, just leave it as it is.
|
||||
|
||||
- Please give me as much information about the issue as you can, but keep it relevant. "This is really annoying me and my friend" does not help me fix the problem!
|
||||
|
||||
- There's no need to put 'tags' in square brackets in the issue title, I'll put proper github issue tags on it which will also help organise it. You can use them to search for issues too!
|
||||
|
||||
- Don't close an issue unless it has actually been fixed or is no longer relevant - otherwise I might not end up fixing it at all! Usually I'll take care of closing issues anyway, so you don't have to worry about it.
|
||||
|
||||
- Most importantly, use gist or pastebin to post crash reports. Do not paste the entire crash report inline, not even in a code block!
|
||||
> If you've got a git account (which you do, if you're posting an issue) you can access gist, and pastebin doesn't require an account at all, so there's no excuse!
|
||||
|
||||
## Submitting a PR
|
||||
|
||||
|
||||
@@ -1,32 +1,32 @@
|
||||
package electroblob.wizardry;
|
||||
|
||||
import electroblob.wizardry.item.ItemSpectralBow;
|
||||
import electroblob.wizardry.packet.PacketCastContinuousSpell;
|
||||
import electroblob.wizardry.packet.PacketCastSpell;
|
||||
import electroblob.wizardry.packet.PacketClairvoyance;
|
||||
import electroblob.wizardry.packet.PacketGlyphData;
|
||||
import electroblob.wizardry.packet.PacketNPCCastSpell.Message;
|
||||
import electroblob.wizardry.packet.PacketPlayerSync;
|
||||
import electroblob.wizardry.packet.PacketTransportation;
|
||||
import electroblob.wizardry.registry.Spells;
|
||||
import electroblob.wizardry.packet.*;
|
||||
import electroblob.wizardry.registry.WizardryItems;
|
||||
import electroblob.wizardry.spell.Spell;
|
||||
import electroblob.wizardry.util.WizardryParticleType;
|
||||
import net.minecraft.block.state.IBlockState;
|
||||
import net.minecraft.entity.Entity;
|
||||
import net.minecraft.entity.EntityLivingBase;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
import net.minecraft.util.SoundCategory;
|
||||
import net.minecraft.util.SoundEvent;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.util.text.Style;
|
||||
import net.minecraft.util.text.TextFormatting;
|
||||
import net.minecraft.util.text.translation.I18n;
|
||||
import net.minecraft.world.World;
|
||||
import net.minecraftforge.common.config.Property;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* The common proxy for wizardry, serving the usual purpose of dealing with all things that need to be handled
|
||||
* differently on the client and the server. A lot of the methods here appear to do absolutely nothing; this is because
|
||||
* they do client-only things which are only handled in the client proxy.
|
||||
*
|
||||
* @see {@link electroblob.wizardry.client.ClientProxy}
|
||||
* @see electroblob.wizardry.client.ClientProxy ClientProxy
|
||||
* @author Electroblob
|
||||
* @since Wizardry 1.0
|
||||
*/
|
||||
@@ -36,90 +36,34 @@ public class CommonProxy {
|
||||
// SECTION Registry
|
||||
// ===============================================================================================================
|
||||
|
||||
public void registerRenderers(){
|
||||
}
|
||||
public void registerRenderers(){}
|
||||
|
||||
public void initialiseLayers(){
|
||||
}
|
||||
public void initialiseLayers(){}
|
||||
|
||||
public void registerKeyBindings(){
|
||||
}
|
||||
|
||||
public void registerSpellHUD(){}
|
||||
public void registerKeyBindings(){}
|
||||
|
||||
public net.minecraft.client.model.ModelBiped getWizardArmourModel(){
|
||||
return null;
|
||||
}
|
||||
|
||||
public void initGuiBits(){}
|
||||
|
||||
public void registerResourceReloadListeners(){}
|
||||
|
||||
public void registerSoundEventListener(){}
|
||||
|
||||
public void registerAtlasMarkers(){}
|
||||
|
||||
// SECTION Particles
|
||||
// ===============================================================================================================
|
||||
|
||||
/**
|
||||
* Spawns a custom particle of the specified type.
|
||||
*
|
||||
* @param type EnumParticleType of the particle
|
||||
* @param world Reference to the World object
|
||||
* @param x Particle x position
|
||||
* @param y Particle y position
|
||||
* @param z Particle z position
|
||||
* @param velX Particle x velocity
|
||||
* @param velY Particle y velocity
|
||||
* @param velZ Particle z velocity
|
||||
* @param maxAge Lifetime of the particle in ticks
|
||||
* @param r Red component of particle colour; will be clamped to between 0 and 1
|
||||
* @param g Red component of particle colour; will be clamped to between 0 and 1
|
||||
* @param b Red component of particle colour; will be clamped to between 0 and 1
|
||||
* @param doGravity Whether the particle is affected by gravity (only affects SPARKLE at the moment)
|
||||
* @param radius The radius of the particle's motion, for cirular motion particles
|
||||
*/
|
||||
public void spawnParticle(WizardryParticleType type, World world, double x, double y, double z, double velX,
|
||||
double velY, double velZ, int maxAge, float r, float g, float b, boolean doGravity, double radius){
|
||||
// Does nothing since particles are client-side only
|
||||
}
|
||||
/** Called from init() in the main mod class to initialise the particle factories. */
|
||||
public void registerParticles(){} // Does nothing since particles are client-side only
|
||||
|
||||
/**
|
||||
* Spawns a custom particle of the specified type. doGravity defaults to false and radius defaults to 0. Note that
|
||||
* some of these settings may not affect the particle; some particles are always affected by gravity, for instance.
|
||||
*
|
||||
* @param type EnumParticleType of the particle
|
||||
* @param world Reference to the World object
|
||||
* @param x Particle x position
|
||||
* @param y Particle y position
|
||||
* @param z Particle z position
|
||||
* @param velX Particle x velocity
|
||||
* @param velY Particle y velocity
|
||||
* @param velZ Particle z velocity
|
||||
* @param maxAge Lifetime of the particle in ticks
|
||||
* @param r Red component of particle colour; will be clamped to between 0 and 1 (unless this is a MAGIC_FIRE
|
||||
* particle, in which case this is the scale)
|
||||
* @param g Red component of particle colour; will be clamped to between 0 and 1
|
||||
* @param b Red component of particle colour; will be clamped to between 0 and 1
|
||||
*/
|
||||
public void spawnParticle(WizardryParticleType type, World world, double x, double y, double z, double velX,
|
||||
double velY, double velZ, int maxAge, float r, float g, float b){
|
||||
this.spawnParticle(type, world, x, y, z, velX, velY, velZ, maxAge, r, g, b, false, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Spawns a custom particle of the specified type. Colour defaults to white, doGravity defaults to false and radius
|
||||
* defaults to 0. Note that some of these settings may not affect the particle; some particles are always affected
|
||||
* by gravity, for instance.
|
||||
*
|
||||
* @param type EnumParticleType of the particle
|
||||
* @param world Reference to the World object
|
||||
* @param x Particle x position
|
||||
* @param y Particle y position
|
||||
* @param z Particle z position
|
||||
* @param velX Particle x velocity
|
||||
* @param velY Particle y velocity
|
||||
* @param velZ Particle z velocity
|
||||
* @param maxAge Lifetime of the particle in ticks
|
||||
*/
|
||||
public void spawnParticle(WizardryParticleType type, World world, double x, double y, double z, double velX,
|
||||
double velY, double velZ, int maxAge){
|
||||
this.spawnParticle(type, world, x, y, z, velX, velY, velZ, maxAge, 1, 1, 1, false, 0);
|
||||
/** Creates a new particle of the specified type from the appropriate particle factory. <i>Does not actually spawn the
|
||||
* particle; use {@link electroblob.wizardry.util.ParticleBuilder ParticleBuilder} to spawn particles.</i> */
|
||||
public electroblob.wizardry.client.particle.ParticleWizardry createParticle(ResourceLocation type, World world, double x, double y, double z){
|
||||
return null;
|
||||
}
|
||||
|
||||
public void spawnTornadoParticle(World world, double x, double y, double z, double velX, double velZ, double radius,
|
||||
@@ -129,26 +73,23 @@ public class CommonProxy {
|
||||
// SECTION Items
|
||||
// ===============================================================================================================
|
||||
|
||||
public boolean shouldDisplayDiscovered(Spell spell, ItemStack stack){
|
||||
return false;
|
||||
}
|
||||
|
||||
public net.minecraft.client.gui.FontRenderer getFontRenderer(ItemStack stack){
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the translated name of the scroll, taking spell discovery into account. If, for some reason, this gets
|
||||
* called server-side, it uses the deprecated server version of I18n, with a warning. Since any likely server-side
|
||||
* use of this method will be for text-based logic purposes (like Bibliocraft's book checking system), and since no
|
||||
* particular player instance can be accessed, spell discovery is ignored.
|
||||
* called server-side, it uses the deprecated server version of I18n. Since any likely server-side
|
||||
* use of this method will be for text-based logic purposes (like Bibliocraft's book checking system), and since
|
||||
* no particular player instance can be accessed, spell discovery is ignored.
|
||||
*/
|
||||
public String getScrollDisplayName(ItemStack scroll){
|
||||
|
||||
// I have now learnt that the server side I18n always translates to the default en_US, so I could just return
|
||||
// a hardcoded name in English instead.
|
||||
Wizardry.logger.info("A mod has called ItemScroll#getItemStackDisplayName from the server side. Using the"
|
||||
+ "deprecated server-side translation methods as a fallback.");
|
||||
|
||||
// Displays [Empty slot] if spell is continuous.
|
||||
Spell spell = Spell.get(scroll.getItemDamage());
|
||||
if(spell.isContinuous) spell = Spells.none;
|
||||
Spell spell = Spell.byMetadata(scroll.getItemDamage());
|
||||
|
||||
return I18n.translateToLocalFormatted("item." + Wizardry.MODID + ":scroll.name",
|
||||
I18n.translateToLocal("spell." + spell.getUnlocalisedName())).trim();
|
||||
@@ -158,35 +99,63 @@ public class CommonProxy {
|
||||
return ((ItemSpectralBow)WizardryItems.spectral_bow).getDefaultDurabilityForDisplay(stack);
|
||||
}
|
||||
|
||||
/** Like {@link CommonProxy#addMultiLineDescription(List, String, Style)}, but style defaults to light grey. */
|
||||
public void addMultiLineDescription(List<String> tooltip, String key){
|
||||
this.addMultiLineDescription(tooltip, key, new Style().setColor(TextFormatting.GRAY));
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a multi-line description to the given tooltip list. The description is first translated using the given
|
||||
* translation key, then the formatting code for the given style is appended, and finally the string is word-wrapped
|
||||
* to the standard width (100).
|
||||
* @param tooltip The tooltip list to add to
|
||||
* @param key The translation key for the description
|
||||
* @param style A style to apply
|
||||
*/
|
||||
public void addMultiLineDescription(List<String> tooltip, String key, Style style){}
|
||||
|
||||
// SECTION Packet Handlers
|
||||
// ===============================================================================================================
|
||||
|
||||
public void handlePlayerSyncPacket(PacketPlayerSync.Message message){
|
||||
}
|
||||
public void handlePlayerSyncPacket(PacketPlayerSync.Message message){}
|
||||
|
||||
public void handleGlyphDataPacket(PacketGlyphData.Message message){
|
||||
}
|
||||
public void handleGlyphDataPacket(PacketGlyphData.Message message){}
|
||||
|
||||
public void handleCastSpellPacket(PacketCastSpell.Message message){
|
||||
}
|
||||
public void handleEmitterDataPacket(PacketEmitterData.Message message){}
|
||||
|
||||
public void handleCastContinuousSpellPacket(PacketCastContinuousSpell.Message message){
|
||||
}
|
||||
public void handleCastSpellPacket(PacketCastSpell.Message message){}
|
||||
|
||||
public void handleNPCCastSpellPacket(Message message){
|
||||
}
|
||||
public void handleCastContinuousSpellPacket(PacketCastContinuousSpell.Message message){}
|
||||
|
||||
public void handleTransportationPacket(PacketTransportation.Message message){
|
||||
}
|
||||
public void handleNPCCastSpellPacket(PacketNPCCastSpell.Message message){}
|
||||
|
||||
public void handleClairvoyancePacket(PacketClairvoyance.Message message){
|
||||
}
|
||||
public void handleDispenserCastSpellPacket(PacketDispenserCastSpell.Message message){}
|
||||
|
||||
public void handleCastSpellAtPosPacket(PacketCastSpellAtPos.Message message){}
|
||||
|
||||
public void handleTransportationPacket(PacketTransportation.Message message){}
|
||||
|
||||
public void handleClairvoyancePacket(PacketClairvoyance.Message message){}
|
||||
|
||||
public void handleAdvancementSyncPacket(PacketSyncAdvancements.Message message){}
|
||||
|
||||
public void handleEndSlowTimePacket(PacketEndSlowTime.Message message){}
|
||||
|
||||
public void handleResurrectionPacket(PacketResurrection.Message message){}
|
||||
|
||||
public void handlePossessionPacket(PacketPossession.Message message){}
|
||||
|
||||
public void handleConquerShrinePacket(PacketConquerShrine.Message message){}
|
||||
|
||||
// SECTION Misc
|
||||
// ===============================================================================================================
|
||||
|
||||
public void setToNumberSliderEntry(Property property){
|
||||
}
|
||||
public void setToNumberSliderEntry(Property property){}
|
||||
|
||||
public void setToHUDChooserEntry(Property property){}
|
||||
|
||||
public void setToNamedBooleanEntry(Property property){}
|
||||
|
||||
// public void setToEntityNameEntry(Property property){}
|
||||
|
||||
/**
|
||||
@@ -194,13 +163,82 @@ public class CommonProxy {
|
||||
*
|
||||
* @param entity The source of the sound
|
||||
* @param sound The SoundEvent to play
|
||||
* @param category The SoundCategory to use
|
||||
* @param volume Volume relative to 1
|
||||
* @param pitch Pitch relative to 1
|
||||
* @param repeat Whether to repeat the sound for as long as the entity is alive (or until stopped manually)
|
||||
*/
|
||||
public void playMovingSound(Entity entity, SoundEvent sound, float volume, float pitch, boolean repeat){
|
||||
public void playMovingSound(Entity entity, SoundEvent sound, SoundCategory category, float volume, float pitch, boolean repeat){}
|
||||
|
||||
/**
|
||||
* Plays a continuous spell sound which moves with the given entity.
|
||||
*
|
||||
* @param entity The source of the sound
|
||||
* @param spell The spell this sound is associated with
|
||||
* @param start The starting SoundEvent to play
|
||||
* @param loop The main looped SoundEvent to play
|
||||
* @param end The ending SoundEvent to play
|
||||
* @param category The SoundCategory to use
|
||||
* @param volume Volume relative to 1
|
||||
* @param pitch Pitch relative to 1
|
||||
*/
|
||||
public void playSpellSoundLoop(EntityLivingBase entity, Spell spell, SoundEvent start, SoundEvent loop, SoundEvent end, SoundCategory category, float volume, float pitch){}
|
||||
|
||||
/**
|
||||
* Plays a continuous spell sound which moves with the given entity.
|
||||
*
|
||||
* @param entity The source of the sound
|
||||
* @param spell The spell this sound is associated with
|
||||
* @param sounds An array of the sound events to play, which must have three elements in the order: start, loop, end
|
||||
* @param category The SoundCategory to use
|
||||
* @param volume Volume relative to 1
|
||||
* @param pitch Pitch relative to 1
|
||||
* @throws IllegalArgumentException if the given array contains less than 3 sound events
|
||||
*/
|
||||
public void playSpellSoundLoop(EntityLivingBase entity, Spell spell, SoundEvent[] sounds, SoundCategory category, float volume, float pitch){
|
||||
if(sounds.length < 3) throw new IllegalArgumentException("Tried to play a continuous spell sound using an array "
|
||||
+ "of sound events, but the given array contained less than 3 sound events!");
|
||||
playSpellSoundLoop(entity, spell, sounds[0], sounds[1], sounds[2], category, volume, pitch);
|
||||
}
|
||||
|
||||
/**
|
||||
* Plays a continuous spell sound at the given position.
|
||||
* @param world The world in which to play the sound
|
||||
* @param x The x coordinate to play the sound at
|
||||
* @param y The y coordinate to play the sound at
|
||||
* @param z The z coordinate to play the sound at
|
||||
* @param spell The spell this sound is associated with
|
||||
* @param start The starting SoundEvent to play
|
||||
* @param loop The main looped SoundEvent to play
|
||||
* @param end The ending SoundEvent to play
|
||||
* @param category The SoundCategory to use
|
||||
* @param volume Volume relative to 1
|
||||
* @param pitch Pitch relative to 1
|
||||
* @param duration The duration of the sound, or -1 to link it to a dispenser at the given coordinates
|
||||
*/
|
||||
public void playSpellSoundLoop(World world, double x, double y, double z, Spell spell, SoundEvent start, SoundEvent loop, SoundEvent end, SoundCategory category, float volume, float pitch, int duration){}
|
||||
|
||||
/**
|
||||
* Plays a continuous spell sound at the given position.
|
||||
*
|
||||
* @param world The world in which to play the sound
|
||||
* @param x The x coordinate to play the sound at
|
||||
* @param y The y coordinate to play the sound at
|
||||
* @param z The z coordinate to play the sound at
|
||||
* @param spell The spell this sound is associated with
|
||||
* @param sounds An array of the sound events to play, which must have three elements in the order: start, loop, end
|
||||
* @param category The SoundCategory to use
|
||||
* @param volume Volume relative to 1
|
||||
* @param pitch Pitch relative to 1
|
||||
* @param duration The duration of the sound, or -1 to link it to a dispenser at the given coordinates
|
||||
* @throws IllegalArgumentException if the given array contains less than 3 sound events
|
||||
*/
|
||||
public void playSpellSoundLoop(World world, double x, double y, double z, Spell spell, SoundEvent[] sounds, SoundCategory category, float volume, float pitch, int duration){
|
||||
if(sounds.length < 3) throw new IllegalArgumentException("Tried to play a continuous spell sound using an array "
|
||||
+ "of sound events, but the given array contained less than 3 sound events!");
|
||||
playSpellSoundLoop(world, x, y, z, spell, sounds[0], sounds[1], sounds[2], category, volume, pitch, duration);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the client side world using Minecraft.getMinecraft().world. <b>Only to be called client side!</b> Returns
|
||||
* null on the server side.
|
||||
@@ -209,4 +247,8 @@ public class CommonProxy {
|
||||
return null;
|
||||
}
|
||||
|
||||
/** Returns an unmodifiable set of the string keys for all of the loaded spell HUD skins. */
|
||||
public Set<String> getSpellHUDSkins(){
|
||||
return null;
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,715 +0,0 @@
|
||||
package electroblob.wizardry;
|
||||
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
|
||||
import electroblob.wizardry.constants.Element;
|
||||
import electroblob.wizardry.enchantment.Imbuement;
|
||||
import electroblob.wizardry.entity.EntityShield;
|
||||
import electroblob.wizardry.entity.living.ISummonedCreature;
|
||||
import electroblob.wizardry.event.SpellCastEvent;
|
||||
import electroblob.wizardry.event.SpellCastEvent.Source;
|
||||
import electroblob.wizardry.packet.PacketCastContinuousSpell;
|
||||
import electroblob.wizardry.packet.PacketPlayerSync;
|
||||
import electroblob.wizardry.packet.PacketTransportation;
|
||||
import electroblob.wizardry.packet.WizardryPacketHandler;
|
||||
import electroblob.wizardry.registry.Spells;
|
||||
import electroblob.wizardry.registry.WizardryAdvancementTriggers;
|
||||
import electroblob.wizardry.spell.None;
|
||||
import electroblob.wizardry.spell.Spell;
|
||||
import electroblob.wizardry.util.MagicDamage;
|
||||
import electroblob.wizardry.util.MagicDamage.DamageType;
|
||||
import electroblob.wizardry.util.SpellModifiers;
|
||||
import electroblob.wizardry.util.WizardryUtilities;
|
||||
import net.minecraft.enchantment.Enchantment;
|
||||
import net.minecraft.enchantment.EnchantmentHelper;
|
||||
import net.minecraft.entity.Entity;
|
||||
import net.minecraft.entity.EntityLivingBase;
|
||||
import net.minecraft.entity.player.EntityPlayer;
|
||||
import net.minecraft.entity.player.EntityPlayerMP;
|
||||
import net.minecraft.init.MobEffects;
|
||||
import net.minecraft.init.SoundEvents;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.nbt.NBTTagCompound;
|
||||
import net.minecraft.nbt.NBTTagInt;
|
||||
import net.minecraft.nbt.NBTTagString;
|
||||
import net.minecraft.potion.PotionEffect;
|
||||
import net.minecraft.util.EnumFacing;
|
||||
import net.minecraft.util.EnumHand;
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraftforge.common.MinecraftForge;
|
||||
import net.minecraftforge.common.capabilities.Capability;
|
||||
import net.minecraftforge.common.capabilities.CapabilityInject;
|
||||
import net.minecraftforge.common.capabilities.ICapabilitySerializable;
|
||||
import net.minecraftforge.common.util.Constants.NBT;
|
||||
import net.minecraftforge.common.util.INBTSerializable;
|
||||
import net.minecraftforge.event.AttachCapabilitiesEvent;
|
||||
import net.minecraftforge.event.entity.EntityJoinWorldEvent;
|
||||
import net.minecraftforge.event.entity.living.LivingEvent.LivingUpdateEvent;
|
||||
import net.minecraftforge.event.entity.player.PlayerEvent;
|
||||
import net.minecraftforge.fml.common.Mod;
|
||||
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
|
||||
import net.minecraftforge.fml.common.network.simpleimpl.IMessage;
|
||||
|
||||
/**
|
||||
* Capability-based replacement for the old ExtendedPlayer class from 1.7.10. This has been reworked to leave minimum
|
||||
* external changes (for my own sanity, mainly!). Turns out the only major difference between an internal capability and
|
||||
* an IEEP is a couple of redundant classes and a different way of registering it.
|
||||
* <p>
|
||||
* Forge seems to have separate classes to hold the Capability<...> instance ('key') and methods for getting the
|
||||
* capability, but in my opinion there are already too many classes to deal with, so I'm not adding any more than are
|
||||
* necessary, meaning those constants and values are kept here instead.
|
||||
*
|
||||
* @since Wizardry 2.1
|
||||
* @author Electroblob
|
||||
*/
|
||||
// On the plus side, having to rethink this class allowed me to clean it up a lot.
|
||||
@Mod.EventBusSubscriber
|
||||
public class WizardData implements INBTSerializable<NBTTagCompound> {
|
||||
|
||||
/** Static instance of what I like to refer to as the capability key. Private because, well, it's internal! */
|
||||
// This annotation does some crazy Forge magic behind the scenes and assigns this field a value.
|
||||
@CapabilityInject(WizardData.class)
|
||||
private static final Capability<WizardData> WIZARD_DATA_CAPABILITY = null;
|
||||
|
||||
/** The player this WizardData instance belongs to. */
|
||||
private final EntityPlayer player;
|
||||
|
||||
// This one is still necessary, because I can't override the equip animation for items that aren't from Wizardry.
|
||||
private Map<Imbuement, Integer> imbuementDurations;
|
||||
|
||||
public boolean hasSpiritWolf;
|
||||
public boolean hasSpiritHorse;
|
||||
|
||||
/**
|
||||
* Whether this player is currently casting a continuous spell via commands. Not saved over world reload and reset
|
||||
* on player death.
|
||||
*/
|
||||
private Spell currentlyCasting;
|
||||
/**
|
||||
* The time for which this player has been casting a continuous spell via commands. Increments by 1 each tick. Not
|
||||
* saved over world reload and reset on player death.
|
||||
*/
|
||||
private int castingTick;
|
||||
/**
|
||||
* SpellModifiers object for the current continuous spell cast via commands. Not saved over world reload and reset
|
||||
* on player death.
|
||||
*/
|
||||
private SpellModifiers spellModifiers;
|
||||
/** Coordinates for the saved transportation stone circle location. Will be null if no location is saved. */
|
||||
private BlockPos stoneCircleLocation;
|
||||
/** Dimension id which the saved stone circle is in. */
|
||||
private int stoneCircleDimension;
|
||||
/** Time left until the player teleports under the effect of transportation */
|
||||
private int tpCountdown;
|
||||
|
||||
/** Coordinates for the saved clairvoyance location. Will be null if no location is saved. */
|
||||
private BlockPos clairvoyanceLocation;
|
||||
/** Dimension id which the saved clairvoyance point is in. */
|
||||
private int clairvoyanceDimension;
|
||||
|
||||
public EntityShield shield;
|
||||
|
||||
public WeakReference<ISummonedCreature> selectedMinion;
|
||||
|
||||
/**
|
||||
* Set of this player's discovered spells. <b>Do not write to this list directly</b>, use
|
||||
* {@link WizardData#discoverSpell(Spell)} instead.
|
||||
*/
|
||||
public Set<Spell> spellsDiscovered;
|
||||
|
||||
private Set<UUID> allies;
|
||||
/**
|
||||
* List of usernames of this player's allies. May not be accurate 100% of the time. This is here so that a player
|
||||
* can view the usernames of their allies even when those allies are not online. <b> Do not use this for any other
|
||||
* purpose than displaying the names!
|
||||
*/
|
||||
public Set<String> allyNames;
|
||||
|
||||
private Set<UUID> soulboundCreatures;
|
||||
|
||||
public WizardData(){
|
||||
this(null); // Nullary constructor for the registration method factory parameter
|
||||
}
|
||||
|
||||
public WizardData(EntityPlayer player){
|
||||
this.player = player;
|
||||
this.imbuementDurations = new HashMap<Imbuement, Integer>();
|
||||
this.spellsDiscovered = new HashSet<Spell>();
|
||||
// All players can recognise magic missile. This is not done using discoverSpell because that seems to cause
|
||||
// a crash on load occasionally (probably something to do with achievements being initalised)
|
||||
this.spellsDiscovered.add(Spells.magic_missile);
|
||||
this.hasSpiritWolf = false;
|
||||
this.hasSpiritHorse = false;
|
||||
this.currentlyCasting = Spells.none;
|
||||
this.spellModifiers = new SpellModifiers();
|
||||
this.castingTick = 0;
|
||||
this.stoneCircleDimension = 0;
|
||||
this.clairvoyanceDimension = 0;
|
||||
this.setTpCountdown(0);
|
||||
this.allies = new HashSet<UUID>();
|
||||
this.allyNames = new HashSet<String>();
|
||||
this.soulboundCreatures = new HashSet<UUID>();
|
||||
}
|
||||
|
||||
public boolean hasSpellBeenDiscovered(Spell spell){
|
||||
return spellsDiscovered.contains(spell) || spell instanceof None;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the given spell to the list of discovered spells for this player. Automatically takes into account whether
|
||||
* the spell has been discovered. Use this method rather than adding directly to the list because it handles
|
||||
* achievements.
|
||||
*
|
||||
* @param spell The spell to be discovered
|
||||
* @return True if the spell had not already been discovered; false otherwise.
|
||||
*/
|
||||
public boolean discoverSpell(Spell spell){
|
||||
|
||||
if(spellsDiscovered == null){
|
||||
spellsDiscovered = new HashSet<Spell>();
|
||||
}
|
||||
// The 'none' spell cannot be discovered
|
||||
if(spell instanceof None) return false;
|
||||
// Tries to add the spell to the list of discovered spells, and returns false if it was already present
|
||||
if(!spellsDiscovered.add(spell)) return false;
|
||||
// If the spell had not already been discovered, achievements can be triggered and the method returns true
|
||||
if(spellsDiscovered.containsAll(Spell.getSpells(Spell::isEnabled))){
|
||||
WizardryAdvancementTriggers.all_spells.triggerFor(this.player);
|
||||
}
|
||||
|
||||
for(Element element : Element.values()){
|
||||
if(element != Element.MAGIC
|
||||
&& spellsDiscovered.containsAll(Spell.getSpells(new Spell.TierElementFilter(null, element)))){
|
||||
WizardryAdvancementTriggers.element_master.triggerFor(this.player);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/** Sets the player's saved transportation stone location and dimension. */
|
||||
public void setStoneCircleLocation(BlockPos pos, int dimensionID){
|
||||
this.stoneCircleLocation = pos;
|
||||
this.stoneCircleDimension = dimensionID;
|
||||
}
|
||||
|
||||
/** Returns the coordinates of the associated player's saved transportation stone circle. */
|
||||
public BlockPos getStoneCircleLocation(){
|
||||
return stoneCircleLocation;
|
||||
}
|
||||
|
||||
/** Returns the dimension ID of the associated player's saved transportation stone circle. */
|
||||
public int getStoneCircleDimension(){
|
||||
return stoneCircleDimension;
|
||||
}
|
||||
|
||||
public int getTpCountdown(){
|
||||
return tpCountdown;
|
||||
}
|
||||
|
||||
public void setTpCountdown(int tpCountdown){
|
||||
this.tpCountdown = tpCountdown;
|
||||
}
|
||||
|
||||
/** Sets the player's saved clairvoyance location. */
|
||||
public void setClairvoyancePoint(BlockPos pos, int dimensionID){
|
||||
this.clairvoyanceLocation = pos;
|
||||
this.clairvoyanceDimension = dimensionID;
|
||||
}
|
||||
|
||||
/** Returns the coordinates for the saved clairvoyance location. Will be null if no location is saved. */
|
||||
public BlockPos getClairvoyanceLocation(){
|
||||
return clairvoyanceLocation;
|
||||
}
|
||||
|
||||
/** Returns the dimension ID for the saved clairvoyance location. Will be null if no location is saved. */
|
||||
public int getClairvoyanceDimension(){
|
||||
return clairvoyanceDimension;
|
||||
}
|
||||
|
||||
/**
|
||||
* Overwrites the imbuement duration associated with the given imubement for this player, or creates it if there was
|
||||
* none previously.
|
||||
*
|
||||
* @throws IllegalArgumentException if the given {@link Enchantment} is not an {@link Imbuement}.
|
||||
*/
|
||||
public void setImbuementDuration(Enchantment enchantment, int duration){
|
||||
// It is best to throw an exception here, because otherwise the error would either go unnoticed (if
|
||||
// non-imbuements
|
||||
// were ignored) or cause a ClassCastException later (if non-imbuements were allowed to be added).
|
||||
if(enchantment instanceof Imbuement){
|
||||
this.imbuementDurations.put((Imbuement)enchantment, duration);
|
||||
}else{
|
||||
throw new IllegalArgumentException(
|
||||
"Attempted to set an imbuement duration for something that isn't an Imbuement! (This exception has been thrown now to prevent a ClassCastException from occurring later.)");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the imbuement duration associated with the given imbuement for this player, or 0 if it does not exist.
|
||||
*/
|
||||
@SuppressWarnings("unlikely-arg-type")
|
||||
public int getImbuementDuration(Enchantment enchantment){
|
||||
// Need to check that i is not null, otherwise it throws an NPE when Java auto-unboxes it.
|
||||
// What's nice here is that the map simply accepts objects as keys, so there's no need to cast or throw
|
||||
// exceptions.
|
||||
Integer i = this.imbuementDurations.get(enchantment);
|
||||
// If i is null, returns 0; otherwise returns i, auto-unboxed to an int.
|
||||
return i == null ? 0 : i;
|
||||
}
|
||||
|
||||
/**
|
||||
* Decrements the duration for each conjured item by 1, and removes from the map any that are 0 or less or that the
|
||||
* player no longer has. Also deletes the item from the player's inventory if it runs out of time.
|
||||
*/
|
||||
private void updateImbuedItems(){
|
||||
|
||||
Set<Imbuement> activeImbuements = new HashSet<Imbuement>();
|
||||
|
||||
// For each item in the player's inventory
|
||||
for(ItemStack stack : player.inventory.mainInventory){
|
||||
if(stack.isItemEnchanted()){
|
||||
|
||||
Map<Enchantment, Integer> enchantments = EnchantmentHelper.getEnchantments(stack);
|
||||
|
||||
Iterator<Entry<Enchantment, Integer>> iterator = enchantments.entrySet().iterator();
|
||||
// For each of the item's enchantments
|
||||
while(iterator.hasNext()){
|
||||
Enchantment enchantment = iterator.next().getKey();
|
||||
// Ignores the enchantment unless it is an imbuement
|
||||
if(enchantment instanceof Imbuement){
|
||||
int duration = this.getImbuementDuration(enchantment);
|
||||
// If the imbuement is still active:
|
||||
if(duration > 0){
|
||||
// Decrements the timer
|
||||
this.imbuementDurations.put((Imbuement)enchantment, duration - 1);
|
||||
// Adds this imbuement to the set of imbuements that need to be kept
|
||||
activeImbuements.add((Imbuement)enchantment);
|
||||
// Otherwise:
|
||||
}else{
|
||||
// Removes the enchantment from the enchantment map
|
||||
iterator.remove();
|
||||
// Applies the new enchantment map to the item
|
||||
EnchantmentHelper.setEnchantments(enchantments, stack);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// Removes all imbuements from the map that are no longer active
|
||||
this.imbuementDurations.keySet().retainAll(activeImbuements);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the given player to the list of allies belonging to the associated player, or removes the player if they are
|
||||
* already in the list of allies. Returns true if the player was added, false if they were removed.
|
||||
*/
|
||||
public boolean toggleAlly(EntityPlayer player){
|
||||
if(this.isPlayerAlly(player)){
|
||||
this.allies.remove(player.getUniqueID());
|
||||
// The remove method uses .equals() rather than == so this will work fine.
|
||||
this.allyNames.remove(player.getName());
|
||||
return false;
|
||||
}else{
|
||||
this.allies.add(player.getUniqueID());
|
||||
this.allyNames.add(player.getName());
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/** Returns whether the given player is in this player's list of allies, or is on the same team as this player. */
|
||||
public boolean isPlayerAlly(EntityPlayer player){
|
||||
return this.allies.contains(player.getUniqueID()) || this.player.isOnSameTeam(player);
|
||||
}
|
||||
|
||||
/** Adds the given entity to this player's list of soulbound creatures, and returns whether it succeeded. */
|
||||
public boolean soulbind(EntityLivingBase target){
|
||||
return this.soulboundCreatures.add(target.getUniqueID());
|
||||
}
|
||||
|
||||
/** Returns whether the given entity has been soulbound to this player. */
|
||||
public boolean isCreatureSoulbound(EntityPlayer target){
|
||||
return this.soulboundCreatures.contains(target.getUniqueID());
|
||||
}
|
||||
|
||||
/**
|
||||
* Damages all creatures soulbound to this player by the given amount, and removes from the list any that no longer
|
||||
* exist.
|
||||
*/
|
||||
public void damageAllSoulboundCreatures(float damage){
|
||||
|
||||
for(Iterator<UUID> iterator = this.soulboundCreatures.iterator(); iterator.hasNext();){
|
||||
|
||||
Entity entity = WizardryUtilities.getEntityByUUID(this.player.world, iterator.next());
|
||||
|
||||
if(entity == null) iterator.remove();
|
||||
|
||||
if(entity instanceof EntityLivingBase){
|
||||
// Retaliatory effect
|
||||
if(entity.attackEntityFrom(MagicDamage.causeDirectMagicDamage(this.player, DamageType.MAGIC, true),
|
||||
damage)){
|
||||
// Sound only plays if the damage succeeds
|
||||
player.playSound(SoundEvents.ENTITY_WITHER_HURT, 1.0F, player.world.rand.nextFloat() * 0.2F + 1.0F);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Starts casting the given spell with the given modifiers. */
|
||||
public void startCastingContinuousSpell(Spell spell, SpellModifiers modifiers){
|
||||
|
||||
this.currentlyCasting = spell;
|
||||
this.spellModifiers = modifiers;
|
||||
|
||||
if(!this.player.world.isRemote){
|
||||
PacketCastContinuousSpell.Message message = new PacketCastContinuousSpell.Message(this.player.getEntityId(),
|
||||
spell.id(), this.spellModifiers);
|
||||
WizardryPacketHandler.net.sendToDimension(message, this.player.world.provider.getDimension());
|
||||
}
|
||||
}
|
||||
|
||||
/** Stops casting the current spell. */
|
||||
public void stopCastingContinuousSpell(){
|
||||
|
||||
this.currentlyCasting = Spells.none;
|
||||
this.castingTick = 0;
|
||||
this.spellModifiers.reset();
|
||||
|
||||
if(!this.player.world.isRemote){
|
||||
PacketCastContinuousSpell.Message message = new PacketCastContinuousSpell.Message(this.player.getEntityId(),
|
||||
Spells.none.id(), this.spellModifiers);
|
||||
WizardryPacketHandler.net.sendToDimension(message, this.player.world.provider.getDimension());
|
||||
}
|
||||
}
|
||||
|
||||
/** Casts the current continuous spell, fires relevant events and updates the castingTick field. */
|
||||
public void updateContinuousSpellCasting(){
|
||||
|
||||
if(this.currentlyCasting != null && this.currentlyCasting.isContinuous){
|
||||
|
||||
if(MinecraftForge.EVENT_BUS.post(
|
||||
new SpellCastEvent.Tick(player, currentlyCasting, spellModifiers, Source.COMMAND, castingTick))){
|
||||
this.stopCastingContinuousSpell();
|
||||
return;
|
||||
}
|
||||
|
||||
if(this.currentlyCasting.cast(player.world, player, EnumHand.MAIN_HAND, castingTick, this.spellModifiers)
|
||||
&& this.castingTick == 0){
|
||||
// On the first tick casting a continuous spell via commands, SpellCastEvent.Post is fired.
|
||||
MinecraftForge.EVENT_BUS
|
||||
.post(new SpellCastEvent.Post(player, currentlyCasting, spellModifiers, Source.COMMAND));
|
||||
}
|
||||
|
||||
castingTick++;
|
||||
|
||||
}else{
|
||||
// Why is this here? Surely castingTick will always be 0 if currentlyCasting is null?
|
||||
this.castingTick = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/** Returns whether this player is currently casting a continuous spell via commands. */
|
||||
public boolean isCasting(){
|
||||
return this.currentlyCasting != null && this.currentlyCasting != Spells.none;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the continuous spell this player is currently casting via commands, or the 'none' spell if they aren't
|
||||
* casting anything.
|
||||
*/
|
||||
public Spell currentlyCasting(){
|
||||
return currentlyCasting;
|
||||
}
|
||||
|
||||
/** Called each time the associated player is updated. */
|
||||
private void update(){
|
||||
|
||||
if(this.selectedMinion != null && this.selectedMinion.get() == null) this.selectedMinion = null;
|
||||
|
||||
// This new system removes a lot of repetitive event handler code and inflexible variables which had duplicate
|
||||
// functions, just for different enchantments.
|
||||
updateImbuedItems();
|
||||
|
||||
if(!player.world.isRemote){
|
||||
if(getTpCountdown() == 1){
|
||||
player.setPositionAndUpdate(this.stoneCircleLocation.getX() + 0.5, this.stoneCircleLocation.getY(),
|
||||
this.stoneCircleLocation.getZ() + 0.5);
|
||||
player.addPotionEffect(new PotionEffect(MobEffects.BLINDNESS, 50, 0));
|
||||
IMessage msg = new PacketTransportation.Message(player.getEntityId());
|
||||
WizardryPacketHandler.net.sendToDimension(msg, player.world.provider.getDimension());
|
||||
}
|
||||
|
||||
if(getTpCountdown() > 0){
|
||||
setTpCountdown(getTpCountdown() - 1);
|
||||
}
|
||||
}
|
||||
|
||||
updateContinuousSpellCasting();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the WizardData instance for the specified player.
|
||||
*/
|
||||
public static final WizardData get(EntityPlayer player){
|
||||
return player.getCapability(WIZARD_DATA_CAPABILITY, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Called from the event handler each time the associated player entity is cloned, i.e. on respawn or when
|
||||
* travelling to a different dimension. Used to copy over any variables that should persist over player death. This
|
||||
* is the inverse of the old onPlayerDeath method, which reset the variables that shouldn't persist.
|
||||
*
|
||||
* @param data The old WizardData whose variables are to be copied over.
|
||||
* @param respawn True if the player died and is respawning, false if they are just travelling between dimensions.
|
||||
*/
|
||||
public void copyFrom(WizardData data, boolean respawn){
|
||||
// TODO: What happens with spirit wolf and spirit horse?
|
||||
this.hasSpiritHorse = data.hasSpiritHorse;
|
||||
this.hasSpiritWolf = data.hasSpiritWolf;
|
||||
this.allies = data.allies;
|
||||
this.allyNames = data.allyNames;
|
||||
this.clairvoyanceDimension = data.clairvoyanceDimension;
|
||||
this.clairvoyanceLocation = data.clairvoyanceLocation;
|
||||
this.selectedMinion = data.selectedMinion;
|
||||
// Curse of soulbinding is lifted when the caster dies, but not when they switch dimensions.
|
||||
if(!respawn) this.soulboundCreatures = data.soulboundCreatures;
|
||||
this.spellsDiscovered = data.spellsDiscovered;
|
||||
this.stoneCircleDimension = data.stoneCircleDimension;
|
||||
this.stoneCircleLocation = data.stoneCircleLocation;
|
||||
|
||||
// Imbuements are lost on death so their durations do not persist.
|
||||
// Command spell casting is reset on death so the associated variables do not persist.
|
||||
// tpCountdown is reset both when the player dies and when they switch dimensions.
|
||||
|
||||
}
|
||||
|
||||
/** Sends a packet to this player's client to synchronise necessary information. Only called server side. */
|
||||
public void sync(){
|
||||
if(this.player instanceof EntityPlayerMP){
|
||||
int id = -1;
|
||||
if(this.selectedMinion != null && this.selectedMinion.get() instanceof Entity)
|
||||
id = ((Entity)this.selectedMinion.get()).getEntityId();
|
||||
IMessage msg = new PacketPlayerSync.Message(this.spellsDiscovered, id);
|
||||
WizardryPacketHandler.net.sendTo(msg, (EntityPlayerMP)this.player);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public NBTTagCompound serializeNBT(){
|
||||
|
||||
NBTTagCompound properties = new NBTTagCompound();
|
||||
|
||||
// ...so Java 8 allows you to do stuff like this:
|
||||
properties.setTag("imbuements", WizardryUtilities.mapToNBT(this.imbuementDurations,
|
||||
imbuement -> new NBTTagInt(Enchantment.getEnchantmentID((Enchantment)imbuement)), NBTTagInt::new));
|
||||
|
||||
properties.setBoolean("hasSpiritWolf", this.hasSpiritWolf);
|
||||
properties.setBoolean("hasSpiritHorse", this.hasSpiritHorse);
|
||||
|
||||
if(this.stoneCircleLocation != null)
|
||||
properties.setLong("stoneCircleLocation", this.stoneCircleLocation.toLong());
|
||||
properties.setInteger("stoneCircleDimension", this.stoneCircleDimension);
|
||||
properties.setInteger("tpCountdown", this.tpCountdown);
|
||||
|
||||
if(this.clairvoyanceLocation != null)
|
||||
properties.setLong("clairvoyanceLocation", this.clairvoyanceLocation.toLong());
|
||||
properties.setInteger("clairvoyanceDimension", this.getClairvoyanceDimension());
|
||||
|
||||
// THIS is why I wrote the list/map <-> NBT methods. Look how neat this is!
|
||||
properties.setTag("allies", WizardryUtilities.listToNBT(this.allies, WizardryUtilities::UUIDtoTagCompound));
|
||||
properties.setTag("allyNames", WizardryUtilities.listToNBT(this.allyNames, NBTTagString::new));
|
||||
properties.setTag("soulboundCreatures",
|
||||
WizardryUtilities.listToNBT(this.soulboundCreatures, WizardryUtilities::UUIDtoTagCompound));
|
||||
|
||||
// Might be worth converting this over to WizardryUtilities.listToNBT.
|
||||
int[] spells = new int[this.spellsDiscovered.size()];
|
||||
int i = 0;
|
||||
for(Spell spell : this.spellsDiscovered){
|
||||
spells[i] = spell.id();
|
||||
i++;
|
||||
}
|
||||
properties.setIntArray("discoveredSpells", spells);
|
||||
|
||||
return properties;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deserializeNBT(NBTTagCompound nbt){
|
||||
|
||||
if(nbt != null){
|
||||
|
||||
this.imbuementDurations = WizardryUtilities.NBTToMap(nbt.getTagList("imbuements", NBT.TAG_COMPOUND),
|
||||
(NBTTagInt tag) -> (Imbuement)Enchantment.getEnchantmentByID(tag.getInt()), NBTTagInt::getInt);
|
||||
|
||||
this.hasSpiritWolf = nbt.getBoolean("hasSpiritWolf");
|
||||
this.hasSpiritHorse = nbt.getBoolean("hasSpiritHorse");
|
||||
|
||||
this.stoneCircleLocation = BlockPos.fromLong(nbt.getLong("stoneCircleLocation"));
|
||||
this.stoneCircleDimension = nbt.getInteger("stoneCircleDimension");
|
||||
this.tpCountdown = nbt.getInteger("tpCountdown");
|
||||
|
||||
this.clairvoyanceLocation = BlockPos.fromLong(nbt.getLong("clairvoyanceLocation"));
|
||||
this.clairvoyanceDimension = nbt.getInteger("clairvoyanceDimension");
|
||||
|
||||
this.allies = new HashSet<UUID>(WizardryUtilities.NBTToList(nbt.getTagList("allies", NBT.TAG_COMPOUND),
|
||||
WizardryUtilities::tagCompoundToUUID));
|
||||
|
||||
this.allyNames = new HashSet<String>(
|
||||
WizardryUtilities.NBTToList(nbt.getTagList("allyNames", NBT.TAG_STRING), NBTTagString::getString));
|
||||
|
||||
this.soulboundCreatures = new HashSet<UUID>(WizardryUtilities.NBTToList(
|
||||
nbt.getTagList("soulboundCreatures", NBT.TAG_COMPOUND), WizardryUtilities::tagCompoundToUUID));
|
||||
|
||||
this.spellsDiscovered = new HashSet<Spell>();
|
||||
for(int id : nbt.getIntArray("discoveredSpells")){
|
||||
spellsDiscovered.add(Spell.get(id));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Event handlers
|
||||
|
||||
@SubscribeEvent
|
||||
// The type parameter here has to be Entity, not EntityPlayer, or the event won't get fired.
|
||||
public static void onCapabilityLoad(AttachCapabilitiesEvent<Entity> event){
|
||||
|
||||
if(event.getObject() instanceof EntityPlayer)
|
||||
event.addCapability(new ResourceLocation(Wizardry.MODID, "WizardData"),
|
||||
new WizardData.Provider((EntityPlayer)event.getObject()));
|
||||
|
||||
// This demonstrates why capabilities are badly structured: The following code compiles, but what it does is put
|
||||
// a player into a CapabilityDispatcher, which is in turn stored in that very same player, which makes no sense
|
||||
// at all!
|
||||
// event.addCapability(new ResourceLocation(Wizardry.MODID, "WizardData"), event.getObject());
|
||||
}
|
||||
|
||||
@SubscribeEvent
|
||||
public static void onPlayerCloneEvent(PlayerEvent.Clone event){
|
||||
|
||||
WizardData newData = WizardData.get(event.getEntityPlayer());
|
||||
WizardData oldData = WizardData.get(event.getOriginal());
|
||||
|
||||
newData.copyFrom(oldData, event.isWasDeath());
|
||||
}
|
||||
|
||||
@SubscribeEvent
|
||||
public static void onEntityJoinWorld(EntityJoinWorldEvent event){
|
||||
if(!event.getEntity().world.isRemote && event.getEntity() instanceof EntityPlayerMP){
|
||||
// Synchronises wizard data after loading.
|
||||
WizardData data = WizardData.get((EntityPlayer)event.getEntity());
|
||||
if(data != null) data.sync();
|
||||
}
|
||||
}
|
||||
|
||||
@SubscribeEvent
|
||||
public static void onLivingUpdateEvent(LivingUpdateEvent event){
|
||||
|
||||
if(event.getEntityLiving() instanceof EntityPlayer){
|
||||
|
||||
EntityPlayer player = (EntityPlayer)event.getEntityLiving();
|
||||
|
||||
if(WizardData.get(player) != null){
|
||||
WizardData.get(player).update();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This is a nested class for a few reasons: firstly, it makes sense because instances of this and WizardData go
|
||||
* hand-in-hand; secondly, it's too short to be worth a separate file; and thirdly (and most importantly) it allows
|
||||
* me to access WIZARD_DATA_CAPABILITY while keeping it private.
|
||||
*/
|
||||
public static class Provider implements ICapabilitySerializable<NBTTagCompound> {
|
||||
|
||||
private final WizardData data;
|
||||
|
||||
public Provider(EntityPlayer player){
|
||||
data = new WizardData(player);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasCapability(Capability<?> capability, EnumFacing facing){
|
||||
return capability == WIZARD_DATA_CAPABILITY;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> T getCapability(Capability<T> capability, EnumFacing facing){
|
||||
|
||||
if(capability == WIZARD_DATA_CAPABILITY){
|
||||
return WIZARD_DATA_CAPABILITY.cast(data);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public NBTTagCompound serializeNBT(){
|
||||
return data.serializeNBT();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deserializeNBT(NBTTagCompound nbt){
|
||||
data.deserializeNBT(nbt);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Ended up deleting IWizardData because it was unnecessary. This is the comment that was at the start of it:
|
||||
|
||||
/* I'm not going to lie, I will never find the capabilities system even remotely intuitive so this is a bare-minimum
|
||||
* approach just to get things working (four classes where one would have done?!) At one point I considered simply
|
||||
* wrapping my old IEEP inside a single-field capability, but I eventually decided I would at least *try* to do it
|
||||
* properly.
|
||||
*
|
||||
* "...without having to directly implement many interfaces." - Forge Docs. I still can't see what's wrong with
|
||||
* implementing many interfaces; surely that's what Java interfaces are designed for?
|
||||
*
|
||||
* Other things I find annoying: - IStorage. It's completely redundant in the majority of cases, and I don't
|
||||
* understand why we need yet another separate class. - Making an interface, only to implement it once and once
|
||||
* only. This completely defeats the point of interfaces. - The EnumFacing parameter, which is again redundant for
|
||||
* everything that isn't a tile entity. So much for a clean, neat system.
|
||||
*
|
||||
* What Forge has effectively done is conflated two different functions: attaching data to stuff and cross-mod
|
||||
* integration/soft dependencies. I think this is bad design; it would have been better to keep the two features
|
||||
* separate.
|
||||
*
|
||||
* Here's my current understanding of how the capability system works: - You make an interface which defines the
|
||||
* things your capability can do (this class). I will call this the TEMPLATE. - You implement that interface with
|
||||
* your default implementation (WizardData). This is the closest analog to your old IEEP implementation class. THIS
|
||||
* CLASS STORES ALL THE VARIABLES, and hence has one instance for each instance of whatever it is attached to. I
|
||||
* will call this the DATA. - The DATA class implements INBTSerializable (assuming you want it to be saved, which is
|
||||
* nearly always the case) - Despite its name, Capability<T> does NOT represent a capability itself. Instead, it
|
||||
* acts as a sort of identifier/key, the idea being that you can access a particular instance of your DATA given the
|
||||
* key (which tells forge that you want a capability of type TEMPLATE) and the object you want the DATA for. This is
|
||||
* what Entity.getCapability(...) does.
|
||||
*
|
||||
* To really understand what's going on though, you need to sift through Forge's verbose data structures and find
|
||||
* where capabilities are actually hooked into vanilla: - Anything that implements ICapabilityProvider will have a
|
||||
* private CapabilityDispatcher field. This holds other ICapabilityProviders. (I know. This inheritance pattern DOES
|
||||
* NOT MAKE SENSE, because these could, in theory, be OTHER ENTITIES!) - This field is assigned a value through
|
||||
* Forge's event factory, which, as we are all familiar with, calls all the methods marked with @SubscribeEvent.
|
||||
* These methods add individual ICapabilityProviders to a Map stored in the event, which the event factory then
|
||||
* wraps in a CapabilityDispatcher (which is itself an ICapabilityProvider) for the object that called it. - In your
|
||||
* event handler, you return a custom ICapabilityProvider which is effectively bolted on to the player, and
|
||||
* duplicates the ICapabilityProvider methods so you can hook into them and return an instance of your DATA class. -
|
||||
* Where before there was a simple collection of IEEPs stored in the player, there is now a tree of
|
||||
* ICapabilityProviders:
|
||||
*
|
||||
* - Entity/TileEntity/ItemStack - Vanilla ICapabilityProviders, mostly IItemHandlers, stored as fields. -
|
||||
* CapabilityDispatcher, stored as a field. - Custom ICapabilityProviders - Custom CapabilityDispatchers - ...
|
||||
*
|
||||
* Most importantly, EACH PLAYER HOLDS THEIR OWN INSTANCE OF THIS TREE.
|
||||
*
|
||||
* When a capability is retrieved, the following process happens: 1. For the Entity/TileEntity/ItemStack instance,
|
||||
* ICapabilityProvider.getCapability(...) is called. 2. The request propogates through the tree and finds the
|
||||
* requested capability. */
|
||||
|
||||
}
|
||||
@@ -1,23 +1,22 @@
|
||||
package electroblob.wizardry;
|
||||
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
import electroblob.wizardry.command.CommandCastSpell;
|
||||
import electroblob.wizardry.command.CommandDiscoverSpell;
|
||||
import electroblob.wizardry.command.CommandSetAlly;
|
||||
import electroblob.wizardry.command.CommandViewAllies;
|
||||
import electroblob.wizardry.data.DispenserCastingData;
|
||||
import electroblob.wizardry.data.WizardData;
|
||||
import electroblob.wizardry.integration.antiqueatlas.WizardryAntiqueAtlasIntegration;
|
||||
import electroblob.wizardry.integration.baubles.WizardryBaublesIntegration;
|
||||
import electroblob.wizardry.misc.Forfeit;
|
||||
import electroblob.wizardry.packet.WizardryPacketHandler;
|
||||
import electroblob.wizardry.registry.WizardryAdvancementTriggers;
|
||||
import electroblob.wizardry.registry.WizardryItems;
|
||||
import electroblob.wizardry.registry.WizardryRegistry;
|
||||
import electroblob.wizardry.registry.WizardryTabs;
|
||||
import electroblob.wizardry.registry.*;
|
||||
import electroblob.wizardry.spell.Spell;
|
||||
import electroblob.wizardry.util.CustomSoundCategory;
|
||||
import electroblob.wizardry.util.SpellProperties;
|
||||
import electroblob.wizardry.worldgen.*;
|
||||
import net.minecraft.item.Item;
|
||||
import net.minecraft.nbt.NBTBase;
|
||||
import net.minecraft.util.EnumFacing;
|
||||
import net.minecraftforge.common.MinecraftForge;
|
||||
import net.minecraftforge.common.capabilities.Capability;
|
||||
import net.minecraftforge.common.capabilities.Capability.IStorage;
|
||||
import net.minecraftforge.common.capabilities.CapabilityManager;
|
||||
import net.minecraftforge.event.RegistryEvent;
|
||||
import net.minecraftforge.fml.common.Mod;
|
||||
import net.minecraftforge.fml.common.Mod.EventHandler;
|
||||
@@ -30,73 +29,59 @@ import net.minecraftforge.fml.common.event.FMLServerStartingEvent;
|
||||
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
|
||||
import net.minecraftforge.fml.common.network.NetworkRegistry;
|
||||
import net.minecraftforge.fml.common.registry.GameRegistry;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
/**
|
||||
* <i>"Electroblob's Wizardry adds an RPG-like system of spells to Minecraft, with the aim of being as playable as
|
||||
* possible. No crazy constructs, no perk trees, no complex recipes - simply find spell books, cast spells, and master
|
||||
* the arcane! - But you knew that, right?"</i>
|
||||
* <p></p>
|
||||
* Main mod class for Wizardry. Contains the logger and settings instances, along with all the other stuff that's normally
|
||||
* in a main mod class.
|
||||
* @author Electroblob
|
||||
* @since Wizardry 1.0
|
||||
*/
|
||||
@Mod(modid = Wizardry.MODID, name = Wizardry.NAME, version = Wizardry.VERSION, guiFactory = "electroblob.wizardry.WizardryGuiFactory")
|
||||
public class Wizardry {
|
||||
|
||||
/** Wizardry's mod ID. */
|
||||
// This is going to have to change for 1.12 or it'll conflict with the other wizardry mod.
|
||||
// They were there first, it's only fair... although I wonder if that will have unintended side-effects?
|
||||
public static final String MODID = "ebwizardry";
|
||||
/** Wizardry's mod name, in readable form. */
|
||||
public static final String NAME = "Electroblob's Wizardry";
|
||||
/**
|
||||
* The version number for this version of wizardry. The following system is used for version numbers:
|
||||
* <p>
|
||||
* <p></p>
|
||||
* <center><b>[major Minecraft version].[major mod version].[minor mod version/patch]
|
||||
* <p>
|
||||
* <p></p>
|
||||
* </center></b> The major mod version is consistent across Minecraft versions, i.e. Wizardry 1.1 has the same
|
||||
* features as Wizardry 2.1, but they are for different versions of Minecraft and have separate minor versioning.
|
||||
* 1.x.x represents Minecraft 1.7.x versions, 2.x.x represents Minecraft 1.10.x versions, 3.x.x represents Minecraft
|
||||
* 1.11.x versions, and so on.
|
||||
*/
|
||||
public static final String VERSION = "4.1.0";
|
||||
public static final String VERSION = "4.2.0";
|
||||
|
||||
// IDEA: Improve the algorithm that finds a place to summon creatures to take walls into account.
|
||||
// IDEA: Replace all uses of Math.cos and Math.sin with MathHelper versions
|
||||
// IDEA: Triggering of inbuilt Forge events in relevant places?
|
||||
|
||||
/* Minor bugs that need fixing at some point:
|
||||
* - Player skin hat layer shows through wizard hats - Wizard armour breaks rather than just running out of mana (I
|
||||
* can't seem to replicate this bug, but I have had it happen to me before...)
|
||||
* - Shift-clicking a stack of special upgrades when in the arcane workbench causes the whole stack to be
|
||||
* transferred when it should be just one (this is a bug with vanilla as well - try putting a stack of bottles into
|
||||
* a brewing stand). I have at least made it so only one gets used now, so it has no impact on the game.
|
||||
* - When a spell is on cooldown, you can't break blocks when holding a wand. */
|
||||
|
||||
// TODO: So somehow I have managed to overlook the fact that health is actually a float. What this means is that
|
||||
// I can make the healing spells use damage multipliers - hurrah!
|
||||
|
||||
// TODO: Switch from IInventory to IItemHandler (Or don't. It's only useful for automation really.)
|
||||
// TODO: Rearrange the config gui and file
|
||||
|
||||
// Updating:
|
||||
// TODO: Make sure all obf reflection names are still correct
|
||||
// TODO: Go through each of the summoned creatures and check there are no extra vanilla methods to override
|
||||
// TODO: Check whether Minecraft has added some methods that do the same job as my utilities, particularly in
|
||||
// relation to itemstacks, hotbar, armour slots and inventory slots.
|
||||
// TODO: Add wizard towers to the /locate command
|
||||
// TODO: Fix cascading worldgen lag and backport (offset area into loaded chunks, possibly use WorldGenMinable)
|
||||
// TODO: Change evil wizard sounds again to use illager sounds?
|
||||
// TODO: Chnage all instanceof checks on split entity variant classes (zombie, skeleton, etc.) to their abstract
|
||||
// base classes where appropriate
|
||||
// IDEA: Abstract the vanilla particles behind the particle builder
|
||||
|
||||
// TODO: Have particles obey Minecraft's particle setting where appropriate
|
||||
// (see https://github.com/RootsTeam/Embers/blob/master/src/main/java/teamroots/embers/particle/ParticleUtil.java)
|
||||
|
||||
// NOTE: Add melee upgrades to loot tables when they are added.
|
||||
// TODO: TileEntityArcaneWorkbench needs looking at, esp. regarding inventory and markDirty
|
||||
// TODO: See what can be done with the illager spell sounds (and go over sounds in general)
|
||||
// TODO: Fix possession
|
||||
// TODO: Fix charge
|
||||
|
||||
/** Static instance of the {@link Settings} object for Wizardry. */
|
||||
public static final Settings settings = new Settings();
|
||||
|
||||
/** Static instance of the {@link Logger} object for Wizardry. */
|
||||
/** Static instance of the {@link Logger} object for Wizardry.
|
||||
* <p></p>
|
||||
* Logging conventions for wizardry (only these levels are used currently):
|
||||
* <p></p>
|
||||
* - <b>ERROR</b>: Anything that threw an exception; may or may not crash the game.<br>
|
||||
* - <b>WARN</b>: Anything that isn't supposed to happen during normal operation, but didn't throw an exception.<br>
|
||||
* - <b>INFO</b>: Anything that might happen during normal mod operation that the user needs to know about. */
|
||||
public static Logger logger;
|
||||
|
||||
// private static Pattern entityNamePattern;
|
||||
|
||||
// EventManager
|
||||
WizardryWorldGenerator generator = new WizardryWorldGenerator();
|
||||
|
||||
// The instance of wizardry that Forge uses.
|
||||
@Instance(Wizardry.MODID)
|
||||
public static Wizardry instance;
|
||||
@@ -109,39 +94,28 @@ public class Wizardry {
|
||||
public void preInit(FMLPreInitializationEvent event){
|
||||
|
||||
logger = event.getModLog();
|
||||
|
||||
proxy.registerResourceReloadListeners();
|
||||
|
||||
settings.initConfig(event);
|
||||
|
||||
// Yes - by the looks of it, having an interface is completely unnecessary in this case.
|
||||
CapabilityManager.INSTANCE.register(WizardData.class, new IStorage<WizardData>(){
|
||||
// These methods are only called by Capability.writeNBT() or Capability.readNBT(), which in turn are
|
||||
// NEVER CALLED. Unless I'm missing some reflective invocation, that means this entire class serves only
|
||||
// to allow capabilities to be saved and loaded manually. What that would be useful for I don't know.
|
||||
// (If an API forces most users to write redundant code for no reason, it's not user friendly, is it?)
|
||||
// ... well, that's my rant for today!
|
||||
@Override
|
||||
public NBTBase writeNBT(Capability<WizardData> capability, WizardData instance, EnumFacing side){
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void readNBT(Capability<WizardData> capability, WizardData instance, EnumFacing side, NBTBase nbt){
|
||||
}
|
||||
}, WizardData::new);
|
||||
|
||||
WizardryRegistry.registerTileEntities();
|
||||
|
||||
WizardryRegistry.registerEntities();
|
||||
// The check for the generateLoot setting is now done within this method.
|
||||
WizardryRegistry.registerLoot();
|
||||
// Capabilities
|
||||
WizardData.register();
|
||||
DispenserCastingData.register();
|
||||
|
||||
// Register things that don't have registries
|
||||
WizardryBlocks.registerTileEntities();
|
||||
WizardryLoot.register();
|
||||
WizardryAdvancementTriggers.register();
|
||||
Forfeit.register();
|
||||
|
||||
// Moved to preInit, because apparently it has to be here now.
|
||||
// Client-side stuff (via proxies)
|
||||
proxy.registerRenderers();
|
||||
// It seems this also has to be here
|
||||
proxy.registerKeyBindings();
|
||||
|
||||
WizardryBaublesIntegration.init();
|
||||
WizardryAntiqueAtlasIntegration.init();
|
||||
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
@@ -149,27 +123,41 @@ public class Wizardry {
|
||||
|
||||
settings.initConfigExtras();
|
||||
|
||||
// Event Handlers
|
||||
GameRegistry.registerWorldGenerator(generator, 0);
|
||||
MinecraftForge.EVENT_BUS.register(new WizardryKeyHandler());
|
||||
MinecraftForge.EVENT_BUS.register(instance);
|
||||
proxy.registerSpellHUD(); // This can't easily be converted to use the new @Mod.EventBusSubscriber system
|
||||
// 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
|
||||
// Examples I've managed to find:
|
||||
// - Tinker's construct slime islands use 25
|
||||
GameRegistry.registerWorldGenerator(new WorldGenCrystalOre(), 0);
|
||||
GameRegistry.registerWorldGenerator(new WorldGenCrystalFlower(), 50);
|
||||
GameRegistry.registerWorldGenerator(new WorldGenWizardTower(), 20);
|
||||
GameRegistry.registerWorldGenerator(new WorldGenObelisk(), 20);
|
||||
GameRegistry.registerWorldGenerator(new WorldGenShrine(), 20);
|
||||
|
||||
// This is for the config change and missing mappings events
|
||||
MinecraftForge.EVENT_BUS.register(instance); // Since there's already an instance we might as well use it
|
||||
|
||||
NetworkRegistry.INSTANCE.registerGuiHandler(this, new WizardryGuiHandler());
|
||||
WizardryPacketHandler.initPackets();
|
||||
|
||||
// Post-registry extras
|
||||
WizardryItems.populateWandMap();
|
||||
WizardryItems.populateArmourMap();
|
||||
WizardryItems.registerDispenseBehaviours();
|
||||
Spell.registry.forEach(Spell::init);
|
||||
SpellProperties.init();
|
||||
|
||||
// Client-side stuff (via proxies)
|
||||
proxy.initGuiBits();
|
||||
proxy.registerParticles();
|
||||
proxy.registerSoundEventListener();
|
||||
|
||||
WizardrySounds.SPELLS = CustomSoundCategory.add(Wizardry.MODID + ":spells");
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
public void postInit(FMLPostInitializationEvent event){
|
||||
// This needs to be here or it won't necessarily include all the mods' entities.
|
||||
// TODO: Re-implement this when the Forge bug is fixed.
|
||||
/* Doesn't seem to be doing anything... String entityNames = ""; for(Object name :
|
||||
* EntityList.classToStringMapping.values()){ if(name instanceof String){ entityNames = entityNames + name +
|
||||
* '|'; } } // Cuts off the last '|' entityNames = entityNames.substring(0, entityNames.length()-1);
|
||||
* entityNamePattern = Pattern.compile(entityNames); */
|
||||
proxy.initialiseLayers();
|
||||
WizardryTabs.sort();
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
@@ -190,24 +178,31 @@ public class Wizardry {
|
||||
|
||||
// 2.1 changed some item ids, so this fixes them for existing worlds
|
||||
// Nobody updates minecraft versions when using mods, but I may as well leave this here just in case.
|
||||
@EventHandler
|
||||
public static void onMissingMappingEvent(RegistryEvent.MissingMappings<Item> event){
|
||||
@SubscribeEvent
|
||||
public static void onMissingItemMappingEvent(RegistryEvent.MissingMappings<Item> event){
|
||||
// Just get, not getAll, since the mod id didn't change!
|
||||
for(RegistryEvent.MissingMappings.Mapping<Item> mapping : event.getAllMappings()){
|
||||
if(mapping.key.getResourceDomain().equals(Wizardry.MODID)){
|
||||
if(mapping.key.getNamespace().equals(Wizardry.MODID)){
|
||||
|
||||
Item replacement = null;
|
||||
Item replacement;
|
||||
|
||||
switch(mapping.key.getResourcePath()){
|
||||
switch(mapping.key.getPath()){
|
||||
|
||||
case "wand_basic": replacement = WizardryItems.magic_wand; break;
|
||||
case "wand_basic_fire": replacement = WizardryItems.basic_fire_wand; break;
|
||||
case "wand_basic_ice": replacement = WizardryItems.basic_ice_wand; break;
|
||||
case "wand_basic_lightning": replacement = WizardryItems.basic_lightning_wand; break;
|
||||
case "wand_basic_necromancy": replacement = WizardryItems.basic_necromancy_wand; break;
|
||||
case "wand_basic_earth": replacement = WizardryItems.basic_earth_wand; break;
|
||||
case "wand_basic_sorcery": replacement = WizardryItems.basic_sorcery_wand; break;
|
||||
case "wand_basic_healing": replacement = WizardryItems.basic_healing_wand; break;
|
||||
case "wand_basic_fire": replacement = WizardryItems.novice_fire_wand; break;
|
||||
case "wand_basic_ice": replacement = WizardryItems.novice_ice_wand; break;
|
||||
case "wand_basic_lightning": replacement = WizardryItems.novice_lightning_wand; break;
|
||||
case "wand_basic_necromancy": replacement = WizardryItems.novice_necromancy_wand; break;
|
||||
case "wand_basic_earth": replacement = WizardryItems.novice_earth_wand; break;
|
||||
case "wand_basic_sorcery": replacement = WizardryItems.novice_sorcery_wand; break;
|
||||
case "wand_basic_healing": replacement = WizardryItems.novice_healing_wand; break;
|
||||
case "basic_fire_wand": replacement = WizardryItems.novice_fire_wand; break;
|
||||
case "basic_ice_wand": replacement = WizardryItems.novice_ice_wand; break;
|
||||
case "basic_lightning_wand": replacement = WizardryItems.novice_lightning_wand; break;
|
||||
case "basic_necromancy_wand": replacement = WizardryItems.novice_necromancy_wand; break;
|
||||
case "basic_earth_wand": replacement = WizardryItems.novice_earth_wand; break;
|
||||
case "basic_sorcery_wand": replacement = WizardryItems.novice_sorcery_wand; break;
|
||||
case "basic_healing_wand": replacement = WizardryItems.novice_healing_wand; break;
|
||||
case "wand_apprentice": replacement = WizardryItems.apprentice_wand; break;
|
||||
case "wand_apprentice_fire": replacement = WizardryItems.apprentice_fire_wand; break;
|
||||
case "wand_apprentice_ice": replacement = WizardryItems.apprentice_ice_wand; break;
|
||||
@@ -250,4 +245,13 @@ public class Wizardry {
|
||||
}
|
||||
}
|
||||
|
||||
@SubscribeEvent
|
||||
public static void onMissingSpellMappingEvent(RegistryEvent.MissingMappings<Spell> event){
|
||||
for(RegistryEvent.MissingMappings.Mapping<Spell> mapping : event.getAllMappings()){
|
||||
if(mapping.key.getNamespace().equals(Wizardry.MODID)){
|
||||
if(mapping.key.getPath().equals("firestorm")) mapping.remap(Spells.fire_breath);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,118 +1,137 @@
|
||||
package electroblob.wizardry;
|
||||
|
||||
import electroblob.wizardry.constants.Constants;
|
||||
import electroblob.wizardry.entity.EntityArc;
|
||||
import electroblob.wizardry.entity.living.EntityEvilWizard;
|
||||
import electroblob.wizardry.data.SpellEmitterData;
|
||||
import electroblob.wizardry.data.SpellGlyphData;
|
||||
import electroblob.wizardry.data.WizardData;
|
||||
import electroblob.wizardry.entity.living.ISpellCaster;
|
||||
import electroblob.wizardry.entity.living.ISummonedCreature;
|
||||
import electroblob.wizardry.event.DiscoverSpellEvent;
|
||||
import electroblob.wizardry.event.SpellCastEvent;
|
||||
import electroblob.wizardry.item.ItemWand;
|
||||
import electroblob.wizardry.item.ItemWizardArmour;
|
||||
import electroblob.wizardry.registry.Spells;
|
||||
import electroblob.wizardry.registry.WizardryAdvancementTriggers;
|
||||
import electroblob.wizardry.registry.WizardryEnchantments;
|
||||
import electroblob.wizardry.registry.WizardryItems;
|
||||
import electroblob.wizardry.registry.WizardryPotions;
|
||||
import electroblob.wizardry.registry.WizardrySounds;
|
||||
import electroblob.wizardry.integration.DamageSafetyChecker;
|
||||
import electroblob.wizardry.item.IManaStoringItem;
|
||||
import electroblob.wizardry.item.ItemArtefact;
|
||||
import electroblob.wizardry.packet.PacketSyncAdvancements;
|
||||
import electroblob.wizardry.packet.WizardryPacketHandler;
|
||||
import electroblob.wizardry.registry.*;
|
||||
import electroblob.wizardry.spell.FreezingWeapon;
|
||||
import electroblob.wizardry.spell.ImbueWeapon;
|
||||
import electroblob.wizardry.spell.Spell;
|
||||
import electroblob.wizardry.util.IElementalDamage;
|
||||
import electroblob.wizardry.util.MagicDamage;
|
||||
import electroblob.wizardry.util.*;
|
||||
import electroblob.wizardry.util.MagicDamage.DamageType;
|
||||
import electroblob.wizardry.util.SpellModifiers;
|
||||
import electroblob.wizardry.util.WandHelper;
|
||||
import electroblob.wizardry.util.WizardryParticleType;
|
||||
import electroblob.wizardry.util.WizardryUtilities;
|
||||
import electroblob.wizardry.util.ParticleBuilder.Type;
|
||||
import net.minecraft.advancements.Advancement;
|
||||
import net.minecraft.enchantment.EnchantmentHelper;
|
||||
import net.minecraft.entity.EntityLiving;
|
||||
import net.minecraft.entity.EntityLivingBase;
|
||||
import net.minecraft.entity.item.EntityItem;
|
||||
import net.minecraft.entity.monster.IMob;
|
||||
import net.minecraft.entity.player.EntityPlayer;
|
||||
import net.minecraft.entity.player.EntityPlayerMP;
|
||||
import net.minecraft.entity.projectile.EntityArrow;
|
||||
import net.minecraft.init.SoundEvents;
|
||||
import net.minecraft.inventory.ContainerPlayer;
|
||||
import net.minecraft.inventory.ContainerWorkbench;
|
||||
import net.minecraft.inventory.IInventory;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.item.ItemSword;
|
||||
import net.minecraft.potion.PotionEffect;
|
||||
import net.minecraft.util.EnumHand;
|
||||
import net.minecraft.util.EnumParticleTypes;
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
import net.minecraft.util.math.Vec3d;
|
||||
import net.minecraft.util.text.TextComponentTranslation;
|
||||
import net.minecraft.world.World;
|
||||
import net.minecraft.world.storage.loot.LootEntry;
|
||||
import net.minecraft.world.storage.loot.LootEntryTable;
|
||||
import net.minecraft.world.storage.loot.LootPool;
|
||||
import net.minecraft.world.storage.loot.RandomValueRange;
|
||||
import net.minecraft.world.storage.loot.conditions.LootCondition;
|
||||
import net.minecraftforge.common.MinecraftForge;
|
||||
import net.minecraftforge.event.LootTableLoadEvent;
|
||||
import net.minecraftforge.event.entity.living.LivingAttackEvent;
|
||||
import net.minecraftforge.event.entity.living.LivingDeathEvent;
|
||||
import net.minecraftforge.event.entity.living.LivingDropsEvent;
|
||||
import net.minecraftforge.common.util.FakePlayer;
|
||||
import net.minecraftforge.event.entity.PlaySoundAtEntityEvent;
|
||||
import net.minecraftforge.event.entity.living.*;
|
||||
import net.minecraftforge.event.entity.living.LivingEvent.LivingUpdateEvent;
|
||||
import net.minecraftforge.event.entity.living.LivingHurtEvent;
|
||||
import net.minecraftforge.event.entity.player.AdvancementEvent;
|
||||
import net.minecraftforge.fml.common.Loader;
|
||||
import net.minecraftforge.fml.common.Mod;
|
||||
import net.minecraftforge.fml.common.eventhandler.EventPriority;
|
||||
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
|
||||
import net.minecraftforge.fml.common.gameevent.PlayerEvent.PlayerLoggedInEvent;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
/**
|
||||
* As of Wizardry 2.1, most of the code in this class has been relocated somewhere sensible, leaving only a few
|
||||
* miscellaneous things that don't make much sense anywhere else, or that are better kept together. Previously, this was
|
||||
* a gigantic class with about half of the entire mod's logic in it!
|
||||
* General-purpose event handler for things that don't fit anywhere else or groups of related behaviours that are better
|
||||
* kept together. As of Wizardry 2.1, most of the code in this class has been relocated somewhere sensible, leaving only
|
||||
* a few miscellaneous things that don't make much sense anywhere else, or that are better kept together (previously,
|
||||
* this was a gigantic class with about half of the entire mod's logic in it!)
|
||||
*
|
||||
* @author Electroblob
|
||||
* @since Wizardry 1.0
|
||||
*/
|
||||
// The general rules for where event-based logic goes are:
|
||||
// - If the logic relates only to vanilla things or is general (e.g. gameplay settings) it lives in here
|
||||
// - If there is an obvious class for the thing relevant to the logic being performed, it goes in there for the sake
|
||||
// of modularity/separation-of-concerns (e.g. armour cost reductions go in ItemWizardArmour)
|
||||
// - If the thing has an instance but not a separate class (e.g. most custom potions), it goes in a related class
|
||||
// where applicable (e.g. a spell class), or if not it goes in here
|
||||
// - If several things share a significant amount of logic, to avoid duplicate code and potentially improve efficiency
|
||||
// they should be kept together either in a class relevant to all of them (probably a common superclass) or in here
|
||||
// - Client-side logic goes in WizardryClientEventHandler or another relevant client-side class
|
||||
@Mod.EventBusSubscriber
|
||||
public final class WizardryEventHandler {
|
||||
|
||||
// IDEA: Config option allowing users to specify loot locations
|
||||
private static final String[] LOOT_INJECTION_LOCATIONS = {"minecraft:chests/simple_dungeon",
|
||||
"minecraft:chests/abandoned_mineshaft", "minecraft:chests/desert_pyramid", "minecraft:chests/jungle_temple",
|
||||
"minecraft:chests/stronghold_corridor", "minecraft:chests/stronghold_crossing",
|
||||
"minecraft:chests/stronghold_library", "minecraft:chests/igloo_chest", "minecraft:chests/woodland_mansion",
|
||||
"minecraft:chests/end_city_treasure"};
|
||||
|
||||
@SubscribeEvent
|
||||
public static void onLootTableLoadEvent(LootTableLoadEvent event){
|
||||
if(Wizardry.settings.generateLoot){
|
||||
for(String location : LOOT_INJECTION_LOCATIONS){
|
||||
if(event.getName().toString().matches(location)){
|
||||
event.getTable().addPool(getAdditive(Wizardry.MODID + ":chests/dungeon_additions"));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static LootPool getAdditive(String entryName){
|
||||
return new LootPool(new LootEntry[]{getAdditiveEntry(entryName, 1)}, new LootCondition[0],
|
||||
new RandomValueRange(1), new RandomValueRange(0, 1), Wizardry.MODID + "_additive_pool");
|
||||
}
|
||||
|
||||
private static LootEntryTable getAdditiveEntry(String name, int weight){
|
||||
return new LootEntryTable(new ResourceLocation(name), weight, 0, new LootCondition[0],
|
||||
Wizardry.MODID + "_additive_entry");
|
||||
}
|
||||
private WizardryEventHandler(){} // No instances!
|
||||
|
||||
@SubscribeEvent
|
||||
public static void onPlayerLoggedInEvent(PlayerLoggedInEvent event){
|
||||
// When a player logs in, they are sent the glyph data and the server's settings.
|
||||
// When a player logs in, they are sent the glyph data, server settings and spell properties.
|
||||
if(event.player instanceof EntityPlayerMP){
|
||||
SpellGlyphData.get(event.player.world).sync((EntityPlayerMP)event.player);
|
||||
SpellEmitterData.get(event.player.world).sync((EntityPlayerMP)event.player);
|
||||
Wizardry.settings.sync((EntityPlayerMP)event.player);
|
||||
syncAdvancements((EntityPlayerMP)event.player, false);
|
||||
}
|
||||
Spell.syncProperties(event.player);
|
||||
}
|
||||
|
||||
@SubscribeEvent(priority = EventPriority.HIGH)
|
||||
public static void onPlaySoundAtEntityEvent(PlaySoundAtEntityEvent event){
|
||||
// Muffle (there's no spell class for it so it's here instead)
|
||||
if(event.getEntity() instanceof EntityLivingBase
|
||||
&& ((EntityLivingBase)event.getEntity()).isPotionActive(WizardryPotions.muffle)){
|
||||
event.setCanceled(true);
|
||||
}
|
||||
}
|
||||
|
||||
@SubscribeEvent
|
||||
public static void onAdvancementEvent(AdvancementEvent event){
|
||||
// Forge has no hook for revoked advancements :(
|
||||
// Guess we'll just have to make do
|
||||
// Also, this seems to get fired on player login, so to prevent the toasts from appearing every login the
|
||||
// only way I can see to do it is by testing the player has been around long enough.
|
||||
if(event.getEntityPlayer() instanceof EntityPlayerMP && event.getEntityPlayer().ticksExisted > 0){
|
||||
syncAdvancements((EntityPlayerMP)event.getEntityPlayer(), true);
|
||||
}
|
||||
}
|
||||
|
||||
private static void syncAdvancements(EntityPlayerMP player, boolean showToasts){
|
||||
|
||||
Wizardry.logger.info("Synchronising advancements for " + player.getName());
|
||||
|
||||
ArrayList<ResourceLocation> advancements = new ArrayList<>();
|
||||
|
||||
for(Advancement advancement : player.getServer().getAdvancementManager().getAdvancements()){
|
||||
if(player.getAdvancements().getProgress(advancement).isDone()) advancements.add(advancement.getId());
|
||||
}
|
||||
|
||||
WizardryPacketHandler.net.sendTo(new PacketSyncAdvancements.Message(showToasts, advancements.toArray(new ResourceLocation[0])), player);
|
||||
}
|
||||
|
||||
@SubscribeEvent(priority = EventPriority.HIGH) // Disabling of specific spells comes after arcane jammer but before everything else
|
||||
public static void onSpellCastPreEvent(SpellCastEvent.Pre event){
|
||||
|
||||
boolean enabled = true;
|
||||
|
||||
switch(event.getSource()){
|
||||
case WAND: enabled = event.getSpell().isEnabled(SpellProperties.Context.WANDS); break;
|
||||
case SCROLL: enabled = event.getSpell().isEnabled(SpellProperties.Context.SCROLL); break;
|
||||
case COMMAND: enabled = event.getSpell().isEnabled(SpellProperties.Context.COMMANDS); break;
|
||||
case NPC: enabled = event.getSpell().isEnabled(SpellProperties.Context.NPCS); break;
|
||||
case DISPENSER: enabled = event.getSpell().isEnabled(SpellProperties.Context.DISPENSERS); break;
|
||||
case OTHER: enabled = event.getSpell().isEnabled(); break; // Any enabled context will do for this one
|
||||
}
|
||||
|
||||
// If a spell is disabled in the config, it will not work.
|
||||
if(!event.getSpell().isEnabled()){
|
||||
if(!event.getEntityLiving().world.isRemote) event.getEntity().sendMessage(
|
||||
if(!enabled){
|
||||
if(event.getCaster() != null && !event.getCaster().world.isRemote) event.getCaster().sendMessage(
|
||||
new TextComponentTranslation("spell.disabled", event.getSpell().getNameForTranslationFormatted()));
|
||||
event.setCanceled(true);
|
||||
}
|
||||
@@ -121,32 +140,36 @@ public final class WizardryEventHandler {
|
||||
@SubscribeEvent
|
||||
public static void onSpellCastPostEvent(SpellCastEvent.Post event){
|
||||
|
||||
// Spell discovery (only players can discover spells, obviously)
|
||||
if(event.getEntity() instanceof EntityPlayer){
|
||||
if(event.getCaster() instanceof EntityPlayer){
|
||||
|
||||
EntityPlayer player = (EntityPlayer)event.getEntity();
|
||||
EntityPlayer player = (EntityPlayer)event.getCaster();
|
||||
|
||||
// Advancement triggers
|
||||
if(player instanceof EntityPlayerMP){
|
||||
WizardryAdvancementTriggers.cast_spell.trigger((EntityPlayerMP)player, event.getSpell(), player.getHeldItem(player.getActiveHand()));
|
||||
}
|
||||
|
||||
// Spell discovery (only players can discover spells, obviously)
|
||||
WizardData data = WizardData.get(player);
|
||||
|
||||
if(data != null){
|
||||
// Data is updated on both sides (This line was added client-side to fix a bug back in 1.1.3, so now
|
||||
// it's in common code, which is nice!)
|
||||
// Short-circuiting AND means that discoverSpell is only called if the event isn't cancelled.
|
||||
if(!MinecraftForge.EVENT_BUS
|
||||
.post(new DiscoverSpellEvent(player, event.getSpell(), DiscoverSpellEvent.Source.CASTING))
|
||||
if(!MinecraftForge.EVENT_BUS.post(new DiscoverSpellEvent(player, event.getSpell(), DiscoverSpellEvent.Source.CASTING))
|
||||
&& data.discoverSpell(event.getSpell())){
|
||||
|
||||
// If the spell wasn't already discovered, other stuff happens:
|
||||
if(event.getSource() == SpellCastEvent.Source.COMMAND){
|
||||
// If the spell didn't send a packet itself, the extended player needs to be synced so the
|
||||
// spell discovery updates on the client.
|
||||
if(!event.getSpell().doesSpellRequirePacket()) data.sync();
|
||||
if(!event.getSpell().requiresPacket()) data.sync();
|
||||
|
||||
}else if(!event.getEntity().world.isRemote && !player.capabilities.isCreativeMode
|
||||
}else if(!event.getCaster().world.isRemote && !player.isCreative()
|
||||
&& Wizardry.settings.discoveryMode){
|
||||
// Sound and text only happen server-side, in survival, with discovery mode on, and only when
|
||||
// the spell wasn't cast using commands.
|
||||
WizardryUtilities.playSoundAtPlayer(player, SoundEvents.ENTITY_PLAYER_LEVELUP, 1.25f, 1);
|
||||
WizardryUtilities.playSoundAtPlayer(player, WizardrySounds.MISC_DISCOVER_SPELL, 1.25f, 1);
|
||||
player.sendMessage(new TextComponentTranslation("spell.discover",
|
||||
event.getSpell().getNameForTranslationFormatted()));
|
||||
}
|
||||
@@ -155,33 +178,59 @@ public final class WizardryEventHandler {
|
||||
}
|
||||
}
|
||||
|
||||
@SubscribeEvent(priority = EventPriority.LOW)
|
||||
public static void onDiscoverSpellEvent(DiscoverSpellEvent event){
|
||||
if(event.getEntityPlayer() instanceof EntityPlayerMP){
|
||||
WizardryAdvancementTriggers.discover_spell.trigger((EntityPlayerMP)event.getEntityPlayer(), event.getSpell(), event.getSource());
|
||||
}
|
||||
}
|
||||
|
||||
@SubscribeEvent
|
||||
public static void onLivingSetAttackTargetEvent(LivingSetAttackTargetEvent event){
|
||||
|
||||
if(event.getTarget() != null && event.getEntityLiving() instanceof EntityLiving
|
||||
&& event.getTarget().isPotionActive(WizardryPotions.muffle)){
|
||||
|
||||
Vec3d vec = event.getTarget().getPositionEyes(1).subtract(event.getEntity().getPositionEyes(1));
|
||||
// Find the angle between the direction the mob is looking and the direction the player is in
|
||||
// Angle between a and b = acos((a.b) / (|a|*|b|))
|
||||
double angle = Math.acos(vec.dotProduct(event.getEntity().getLookVec()) / vec.length());
|
||||
System.out.println(angle);
|
||||
// If the player is not within the 144-degree arc in front of the mob, it won't detect them
|
||||
if(angle > 0.4 * Math.PI){
|
||||
((EntityLiving)event.getEntityLiving()).setAttackTarget(null);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* There is a subtle but important difference between LivingAttackEvent and LivingHurtEvent - LivingAttackEvent
|
||||
* fires immediately when attackEntityFrom is called, whereas LivingHurtEvent only fires if the attack actually
|
||||
* succeeded, i.e. if the entity in question takes damage (though the event is fired before that so you can cancel
|
||||
* the damage). Things are processed in the following order: * LivingAttackEvent * - Invulnerability -
|
||||
* Already-dead-ness - Fire resistance - Helmets vs. falling things - Hurt resistant time - Invulnerability (again)
|
||||
* * LivingHurtEvent * - Armour - Potions - Health is finally changed Of course, there are no guarantees that other
|
||||
* the damage). Things are processed in the following order:
|
||||
*
|
||||
* * LivingAttackEvent *
|
||||
* - Invulnerability
|
||||
* - Already-dead-ness
|
||||
* - Fire resistance
|
||||
* - Helmets vs. falling things
|
||||
* - Hurt resistant time
|
||||
* - Invulnerability (again)
|
||||
* * LivingHurtEvent *
|
||||
* - Armour
|
||||
* - Potions
|
||||
* * LivingDamageEvent *
|
||||
* - Health is finally changed
|
||||
*
|
||||
* Of course, there are no guarantees that other
|
||||
* mods hooking into these two events will be called before or after yours, but you can have some degree of control
|
||||
* by choosing which event to use. EDIT: Actually, there are. Firstly, you can set a priority in the @SubscribeEvent
|
||||
* annotation which defines how early (higher priority) or late (lower priority) the method is called. Methods with
|
||||
* the same priority are sorted alphabetically by mod id (so it's safe to assume wizardry would be fairly late on!).
|
||||
* I wonder if there are any conventions for what sort of things take what priority...? */
|
||||
|
||||
@SubscribeEvent
|
||||
@SubscribeEvent(priority = EventPriority.LOW) // Low priority in case the event gets cancelled at default priority
|
||||
public static void onLivingAttackEvent(LivingAttackEvent event){
|
||||
|
||||
// Prevents any damage to allies from magic if friendly fire is disabled
|
||||
if(!Wizardry.settings.friendlyFire && event.getSource() != null
|
||||
&& event.getSource().getTrueSource() instanceof EntityPlayer && event.getEntity() instanceof EntityPlayer
|
||||
&& event.getSource() instanceof IElementalDamage){
|
||||
if(WizardryUtilities.isPlayerAlly((EntityPlayer)event.getSource().getTrueSource(),
|
||||
(EntityPlayer)event.getEntity())){
|
||||
event.setCanceled(true);
|
||||
// This needs to be here, since if the event is cancelled nothing else needs to happen.
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Retaliatory effects
|
||||
// These are better off here because the revenge effects are pretty similar, and I'd rather keep the (lengthy)
|
||||
// if statement in one place.
|
||||
@@ -192,47 +241,43 @@ public final class WizardryEventHandler {
|
||||
EntityLivingBase attacker = (EntityLivingBase)event.getSource().getTrueSource();
|
||||
World world = event.getEntityLiving().world;
|
||||
|
||||
// Fireskin
|
||||
if(event.getEntityLiving().isPotionActive(WizardryPotions.fireskin)
|
||||
&& !MagicDamage.isEntityImmune(DamageType.FIRE, event.getEntityLiving()))
|
||||
attacker.setFire(5);
|
||||
if(attacker.getDistance(event.getEntityLiving()) < 10){
|
||||
|
||||
// Ice Shroud
|
||||
if(event.getEntityLiving().isPotionActive(WizardryPotions.ice_shroud)
|
||||
&& !MagicDamage.isEntityImmune(DamageType.FROST, event.getEntityLiving()))
|
||||
attacker.addPotionEffect(new PotionEffect(WizardryPotions.frost, 100, 0));
|
||||
// Fireskin
|
||||
if(event.getEntityLiving().isPotionActive(WizardryPotions.fireskin)
|
||||
&& !MagicDamage.isEntityImmune(DamageType.FIRE, event.getEntityLiving()))
|
||||
attacker.setFire(Spells.fire_breath.getProperty(Spell.BURN_DURATION).intValue());
|
||||
|
||||
// Static Aura
|
||||
if(event.getEntityLiving().isPotionActive(WizardryPotions.static_aura)){
|
||||
// Ice Shroud
|
||||
if(event.getEntityLiving().isPotionActive(WizardryPotions.ice_shroud)
|
||||
&& !MagicDamage.isEntityImmune(DamageType.FROST, event.getEntityLiving())
|
||||
&& !(attacker instanceof FakePlayer)) // Fake players cause problems
|
||||
attacker.addPotionEffect(new PotionEffect(WizardryPotions.frost,
|
||||
Spells.ice_shroud.getProperty(Spell.EFFECT_DURATION).intValue(),
|
||||
Spells.ice_shroud.getProperty(Spell.EFFECT_STRENGTH).intValue()));
|
||||
|
||||
if(!world.isRemote){
|
||||
EntityArc arc = new EntityArc(world);
|
||||
arc.setEndpointCoords(event.getEntityLiving().posX, event.getEntityLiving().posY + 1,
|
||||
event.getEntityLiving().posZ, attacker.posX, attacker.posY + attacker.height / 2,
|
||||
attacker.posZ);
|
||||
world.spawnEntity(arc);
|
||||
}else{
|
||||
for(int i = 0; i < 8; i++){
|
||||
Wizardry.proxy.spawnParticle(WizardryParticleType.SPARK, world,
|
||||
attacker.posX + world.rand.nextFloat() - 0.5, attacker.getEntityBoundingBox().minY
|
||||
+ attacker.height / 2 + world.rand.nextFloat() * 2 - 1,
|
||||
attacker.posZ + world.rand.nextFloat() - 0.5, 0, 0, 0, 3);
|
||||
world.spawnParticle(EnumParticleTypes.SMOKE_LARGE, attacker.posX + world.rand.nextFloat() - 0.5,
|
||||
attacker.getEntityBoundingBox().minY + attacker.height / 2 + world.rand.nextFloat() * 2
|
||||
- 1,
|
||||
attacker.posZ + world.rand.nextFloat() - 0.5, 0, 0, 0);
|
||||
// Static Aura
|
||||
if(event.getEntityLiving().isPotionActive(WizardryPotions.static_aura)){
|
||||
|
||||
if(world.isRemote){
|
||||
|
||||
ParticleBuilder.create(Type.LIGHTNING).entity(event.getEntity()).pos(0, event.getEntity().height / 2, 0)
|
||||
.target(attacker).spawn(world);
|
||||
|
||||
ParticleBuilder.spawnShockParticles(world, attacker.posX,
|
||||
attacker.getEntityBoundingBox().minY + attacker.height / 2, attacker.posZ);
|
||||
}
|
||||
}
|
||||
|
||||
attacker.attackEntityFrom(
|
||||
MagicDamage.causeDirectMagicDamage(event.getEntityLiving(), DamageType.SHOCK, true), 4.0f);
|
||||
attacker.playSound(WizardrySounds.SPELL_SPARK, 1.0F, world.rand.nextFloat() * 0.4F + 1.5F);
|
||||
DamageSafetyChecker.attackEntitySafely(attacker, MagicDamage.causeDirectMagicDamage(event.getEntityLiving(),
|
||||
DamageType.SHOCK, true), Spells.static_aura.getProperty(Spell.DAMAGE).floatValue(), event.getSource().getDamageType());
|
||||
attacker.playSound(WizardrySounds.SPELL_STATIC_AURA_RETALIATE, 1.0F, world.rand.nextFloat() * 0.4F + 1.5F);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@SubscribeEvent
|
||||
@SubscribeEvent(priority = EventPriority.LOW) // Again, we don't want these effects if the event is cancelled
|
||||
public static void onLivingHurtEvent(LivingHurtEvent event){
|
||||
|
||||
// Flaming and freezing swords
|
||||
@@ -241,7 +286,7 @@ public final class WizardryEventHandler {
|
||||
EntityLivingBase attacker = (EntityLivingBase)event.getSource().getTrueSource();
|
||||
|
||||
// Players can only ever attack with their main hand, so this is the right method to use here.
|
||||
if(!attacker.getHeldItemMainhand().isEmpty() && attacker.getHeldItemMainhand().getItem() instanceof ItemSword){
|
||||
if(!attacker.getHeldItemMainhand().isEmpty() && ImbueWeapon.isSword(attacker.getHeldItemMainhand().getItem())){
|
||||
|
||||
int level = EnchantmentHelper.getEnchantmentLevel(WizardryEnchantments.flaming_weapon,
|
||||
attacker.getHeldItemMainhand());
|
||||
@@ -282,30 +327,13 @@ public final class WizardryEventHandler {
|
||||
@SubscribeEvent
|
||||
public static void onLivingUpdateEvent(LivingUpdateEvent event){
|
||||
|
||||
if(event.getEntityLiving() instanceof EntityPlayer){
|
||||
|
||||
EntityPlayer player = (EntityPlayer)event.getEntityLiving();
|
||||
|
||||
if(player.world.isRemote) hackilyFixContinuousSpellCasting(player);
|
||||
|
||||
// Mana flask crafting
|
||||
if(player.openContainer instanceof ContainerWorkbench){
|
||||
|
||||
IInventory craftMatrix = ((ContainerWorkbench)player.openContainer).craftMatrix;
|
||||
ItemStack output = ((ContainerWorkbench)player.openContainer).craftResult.getStackInSlot(0);
|
||||
processManaFlaskCrafting(craftMatrix, output);
|
||||
|
||||
}else if(player.openContainer instanceof ContainerPlayer){
|
||||
|
||||
IInventory craftMatrix = ((ContainerPlayer)player.openContainer).craftMatrix;
|
||||
ItemStack output = ((ContainerPlayer)player.openContainer).craftResult.getStackInSlot(0);
|
||||
// Unfortunately I have no choice but to call this method every tick when the player isn't using another
|
||||
// inventory, since the only thing tracking whether the player is looking at their inventory is the GUI
|
||||
// itself, which is client-side only.
|
||||
processManaFlaskCrafting(craftMatrix, output);
|
||||
}
|
||||
|
||||
}
|
||||
// Experimental animation feature
|
||||
// if(event.getEntityLiving().isHandActive() && event.getEntityLiving().getActiveItemStack().getItemUseAction() == WizardryUtilities.POINT){
|
||||
// event.getEntityLiving().isSwingInProgress = true;
|
||||
// event.getEntityLiving().swingProgress = 1f;
|
||||
// event.getEntityLiving().prevSwingProgress = 1;
|
||||
// event.getEntityLiving().swingingHand = event.getEntityLiving().getActiveHand();
|
||||
// }
|
||||
|
||||
if(event.getEntityLiving().world.isRemote){
|
||||
|
||||
@@ -316,13 +344,14 @@ public final class WizardryEventHandler {
|
||||
Spell spell = ((ISpellCaster)event.getEntity()).getContinuousSpell();
|
||||
SpellModifiers modifiers = ((ISpellCaster)event.getEntity()).getModifiers();
|
||||
|
||||
if(spell != null && spell != Spells.none){
|
||||
if(spell != null && spell != Spells.none){ // IntelliJ is wrong, do NOT remove the null check!
|
||||
|
||||
if(!MinecraftForge.EVENT_BUS.post(new SpellCastEvent.Tick(event.getEntityLiving(), spell, modifiers,
|
||||
SpellCastEvent.Source.NPC, 0))){
|
||||
if(!MinecraftForge.EVENT_BUS.post(new SpellCastEvent.Tick(SpellCastEvent.Source.NPC, spell, event.getEntityLiving(),
|
||||
modifiers, 0))){
|
||||
|
||||
spell.cast(event.getEntity().world, (EntityLiving)event.getEntity(), EnumHand.MAIN_HAND, 0,
|
||||
// TODO: This implementation of modifiers relies on them being accessible client-side.
|
||||
// Right now that doesn't matter because NPCs don't use modifiers, but they might in future
|
||||
((EntityLiving)event.getEntity()).getAttackTarget(), modifiers);
|
||||
}
|
||||
}
|
||||
@@ -330,7 +359,7 @@ public final class WizardryEventHandler {
|
||||
}
|
||||
}
|
||||
|
||||
@SubscribeEvent
|
||||
@SubscribeEvent(priority = EventPriority.LOWEST) // No siphoning if the event is cancelled, that could be exploited...
|
||||
public static void onLivingDeathEvent(LivingDeathEvent event){
|
||||
|
||||
if(event.getSource().getTrueSource() instanceof EntityPlayer){
|
||||
@@ -339,108 +368,83 @@ public final class WizardryEventHandler {
|
||||
|
||||
for(ItemStack stack : WizardryUtilities.getPrioritisedHotbarAndOffhand(player)){
|
||||
|
||||
if(stack.getItem() instanceof ItemWand && stack.isItemDamaged()
|
||||
if(stack.getItem() instanceof IManaStoringItem && !((IManaStoringItem)stack.getItem()).isManaFull(stack)
|
||||
&& WandHelper.getUpgradeLevel(stack, WizardryItems.siphon_upgrade) > 0){
|
||||
int damage = stack.getItemDamage()
|
||||
- Constants.SIPHON_MANA_PER_LEVEL
|
||||
|
||||
int mana = Constants.SIPHON_MANA_PER_LEVEL
|
||||
* WandHelper.getUpgradeLevel(stack, WizardryItems.siphon_upgrade)
|
||||
- player.world.rand.nextInt(Constants.SIPHON_MANA_PER_LEVEL);
|
||||
if(damage < 0) damage = 0;
|
||||
stack.setItemDamage(damage);
|
||||
break;
|
||||
+ player.world.rand.nextInt(Constants.SIPHON_MANA_PER_LEVEL);
|
||||
|
||||
if(ItemArtefact.isArtefactActive(player, WizardryItems.ring_siphoning)) mana *= 1.3f;
|
||||
|
||||
((IManaStoringItem)stack.getItem()).rechargeMana(stack, mana);
|
||||
|
||||
break; // Only recharge one item per kill
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// These two are lifted from EntityLivingBase#travel
|
||||
private static final double LIVING_ENTITY_GRAVITY = 0.08;
|
||||
private static final double LIVING_ENTITY_DRAG = 0.98;
|
||||
|
||||
private static final double LIVING_ENTITY_TERMINAL_VELOCITY = 3.92; // From Minecraft Wiki!
|
||||
|
||||
private static final double LOG_LIVING_ENTITY_DRAG = Math.log(LIVING_ENTITY_DRAG);
|
||||
private static final double FALL_TICKS_ERROR_CORRECTION = 0.500841776608447;
|
||||
|
||||
@SubscribeEvent // Priority doesn't matter here, we're only setting event fields so if it's cancelled it won't matter
|
||||
public static void onLivingFallEvent(LivingFallEvent event){
|
||||
// Why is fall damage based on distance fallen? Why? Who on earth came up with that? It makes no sense whatsoever!
|
||||
if(Wizardry.settings.replaceVanillaFallDamage && !Loader.isModLoaded("speedbasedfalldamage")){
|
||||
// We want to keep the fall damage EXACTLY THE SAME for free, uninterrupted falls, but fix the weirdness
|
||||
// caused when something else changes the entity's velocity
|
||||
// All living entities have a gravity of 0.08b/t^2
|
||||
// Therefore it would be simple to say v^2 = u^2 + 2gs gives the equivalent fall distance as motionY^2 / 0.16
|
||||
// However, Minecraft also has a drag of 0.02 * the velocity, so we actually expect a slightly different value
|
||||
// Much maths later...
|
||||
|
||||
double v = event.getEntity().motionY;
|
||||
// Players are weird, their velocity somehow resets on the server just before this event fires so
|
||||
// instead we're storing the y velocity from the previous tick in WizardData and retrieving it here
|
||||
// Of course, if another mod screws things up and sets a player's velocity client-side only then this won't
|
||||
// work ...but it's better than having clients calculate their own fall damage
|
||||
if(event.getEntity() instanceof EntityPlayer){
|
||||
WizardData data = WizardData.get((EntityPlayer)event.getEntity());
|
||||
if(data != null){
|
||||
v = data.prevMotionY;
|
||||
}
|
||||
}
|
||||
|
||||
if(event.getEntityLiving() == player && event.getSource() instanceof IElementalDamage){
|
||||
WizardryAdvancementTriggers.self_destruct.triggerFor(player);
|
||||
}
|
||||
}
|
||||
}
|
||||
// At terminal velocity, there's no way of finding fall distance from velocity, and the entity is probably dead anyway!
|
||||
if(v > -3.9){
|
||||
|
||||
@SubscribeEvent
|
||||
public static void onLivingDropsEvent(LivingDropsEvent event){
|
||||
// TODO: Really, this should be in a loot table (mob_additions), however I can't seem to find a way of
|
||||
// automatically adding it to all subclasses of IMob.
|
||||
// Evil wizards drop spell books themselves
|
||||
if(event.getEntityLiving() instanceof IMob && !(event.getEntityLiving() instanceof EntityEvilWizard)
|
||||
// TODO: Backport when you backport the new summoned creature system.
|
||||
&& !(event.getEntityLiving() instanceof ISummonedCreature)
|
||||
&& event.getSource().getTrueSource() instanceof EntityPlayer && Wizardry.settings.spellBookDropChance > 0){
|
||||
// Just to make the code more readable, java will replace them all with numbers anyway
|
||||
double g = LIVING_ENTITY_GRAVITY;
|
||||
double f = LIVING_ENTITY_DRAG;
|
||||
double lnf = LOG_LIVING_ENTITY_DRAG;
|
||||
double tv = LIVING_ENTITY_TERMINAL_VELOCITY;
|
||||
|
||||
// This does exactly what the entity drop method does, but with a different random number so that the
|
||||
// spell book doesn't always drop with other rare drops.
|
||||
int rareDropNumber = event.getEntity().world.rand.nextInt(200) - event.getLootingLevel();
|
||||
if(rareDropNumber < Wizardry.settings.spellBookDropChance){
|
||||
// Drops a spell book
|
||||
int id = WizardryUtilities.getStandardWeightedRandomSpellId(event.getEntity().world.rand);
|
||||
// Work backwards from y velocity to get fall time
|
||||
// logs are probably slow but it's not like this gets calculated every tick, and we only need one log
|
||||
double t = Math.log(((-v - tv) * lnf) / g) / lnf; // Log the number over log the base
|
||||
|
||||
event.getDrops()
|
||||
.add(new EntityItem(event.getEntityLiving().world, event.getEntityLiving().posX,
|
||||
event.getEntityLiving().posY, event.getEntityLiving().posZ,
|
||||
new ItemStack(WizardryItems.spell_book, 1, id)));
|
||||
}
|
||||
}
|
||||
}
|
||||
// Because time is in discrete ticks, the above equation for t results in a constant error of
|
||||
// +0.500841776608447, so I guess we can just subtract it... if it works it works I guess!
|
||||
t -= FALL_TICKS_ERROR_CORRECTION; // Don't cast to int or perform any rounding
|
||||
|
||||
// Private helper methods
|
||||
// ================================================================================================================
|
||||
// Now work forwards from t to find the effective fall distance, i.e. the distance the entity would
|
||||
// have to freefall to reach the same velocity
|
||||
// Don't ask me where the 196 is from, again, it just works!
|
||||
double y = (g * Math.pow(f, t)) / (lnf*lnf) + tv * (t) - 196;
|
||||
|
||||
/**
|
||||
* Detects inconsistencies between player.getActiveItemStack and the actual itemstack and forces them to be equal.
|
||||
* Fixes issue #25.
|
||||
*
|
||||
* @param player
|
||||
*/
|
||||
private static void hackilyFixContinuousSpellCasting(EntityPlayer player){
|
||||
if(player.isHandActive() && player.getHeldItem(player.getActiveHand()).getItem() instanceof ItemWand
|
||||
&& WandHelper.getCurrentSpell(player.getHeldItem(player.getActiveHand())).isContinuous){
|
||||
if(player.getActiveItemStack() != player.getHeldItem(player.getActiveHand())){
|
||||
player.setHeldItem(player.getActiveHand(), player.getActiveItemStack());
|
||||
}
|
||||
}
|
||||
}
|
||||
// DEBUG
|
||||
// if(event.getEntity() instanceof EntityPlayer){
|
||||
// Wizardry.logger.info("Replaced fall distance {} with effective distance {} based on entity velocity", event.getDistance(), y);
|
||||
// }
|
||||
|
||||
private static void processManaFlaskCrafting(IInventory craftMatrix, ItemStack output){
|
||||
|
||||
// Charges wand using mana flask. It is here rather than in the crafting handler so the result displays
|
||||
// the proper damage before it is actually crafted.
|
||||
|
||||
boolean flag = false;
|
||||
ItemStack wand = ItemStack.EMPTY;
|
||||
ItemStack armour = ItemStack.EMPTY;
|
||||
|
||||
for(int i = 0; i < craftMatrix.getSizeInventory(); i++){
|
||||
|
||||
ItemStack itemstack = craftMatrix.getStackInSlot(i);
|
||||
|
||||
if(itemstack.getItem() == WizardryItems.mana_flask){
|
||||
flag = true;
|
||||
}
|
||||
|
||||
if(itemstack.getItem() instanceof ItemWand){
|
||||
wand = itemstack;
|
||||
}
|
||||
|
||||
if(itemstack.getItem() instanceof ItemWizardArmour){
|
||||
armour = itemstack;
|
||||
}
|
||||
}
|
||||
|
||||
if(output.getItem() instanceof ItemWand && flag && !wand.isEmpty()){
|
||||
output.setTagCompound((wand.getTagCompound()));
|
||||
if(wand.getItemDamage() - Constants.MANA_PER_FLASK < 0){
|
||||
output.setItemDamage(0);
|
||||
}else{
|
||||
output.setItemDamage(wand.getItemDamage() - Constants.MANA_PER_FLASK);
|
||||
}
|
||||
}
|
||||
|
||||
if(output.getItem() instanceof ItemWizardArmour && flag && !armour.isEmpty()){
|
||||
output.setTagCompound((armour.getTagCompound()));
|
||||
if(armour.getItemDamage() - Constants.MANA_PER_FLASK < 0){
|
||||
output.setItemDamage(0);
|
||||
}else{
|
||||
output.setItemDamage(wand.getItemDamage() - Constants.MANA_PER_FLASK);
|
||||
event.setDistance((float)y);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
package electroblob.wizardry;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
import electroblob.wizardry.client.GuiConfigWizardry;
|
||||
import electroblob.wizardry.client.gui.config.GuiConfigWizardry;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.gui.GuiScreen;
|
||||
import net.minecraftforge.fml.client.IModGuiFactory;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
public class WizardryGuiFactory implements IModGuiFactory {
|
||||
|
||||
@Override
|
||||
|
||||
@@ -40,20 +40,20 @@ public class WizardryGuiHandler implements IGuiHandler {
|
||||
if(id == ARCANE_WORKBENCH){
|
||||
TileEntity tileEntity = world.getTileEntity(new BlockPos(x, y, z));
|
||||
if(tileEntity instanceof TileEntityArcaneWorkbench){
|
||||
return new electroblob.wizardry.client.GuiArcaneWorkbench(player.inventory,
|
||||
return new electroblob.wizardry.client.gui.GuiArcaneWorkbench(player.inventory,
|
||||
(TileEntityArcaneWorkbench)tileEntity);
|
||||
}
|
||||
}else if(id == WIZARD_HANDBOOK && (player.getHeldItemMainhand().getItem() instanceof ItemWizardHandbook
|
||||
|| player.getHeldItemOffhand().getItem() instanceof ItemWizardHandbook)){
|
||||
return new electroblob.wizardry.client.GuiWizardHandbook();
|
||||
return new electroblob.wizardry.client.gui.handbook.GuiWizardHandbook();
|
||||
}else if(id == SPELL_BOOK){
|
||||
if(player.getHeldItemMainhand().getItem() instanceof ItemSpellBook){
|
||||
return new electroblob.wizardry.client.GuiSpellBook(Spell.get(player.getHeldItemMainhand().getItemDamage()));
|
||||
return new electroblob.wizardry.client.gui.GuiSpellBook(Spell.byMetadata(player.getHeldItemMainhand().getItemDamage()));
|
||||
}else if(player.getHeldItemOffhand().getItem() instanceof ItemSpellBook){
|
||||
return new electroblob.wizardry.client.GuiSpellBook(Spell.get(player.getHeldItemOffhand().getItemDamage()));
|
||||
return new electroblob.wizardry.client.gui.GuiSpellBook(Spell.byMetadata(player.getHeldItemOffhand().getItemDamage()));
|
||||
}
|
||||
}else if(id == PORTABLE_CRAFTING){
|
||||
return new electroblob.wizardry.client.GuiPortableCrafting(player.inventory, world, new BlockPos(x, y, z));
|
||||
return new electroblob.wizardry.client.gui.GuiPortableCrafting(player.inventory, world, new BlockPos(x, y, z));
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -1,68 +0,0 @@
|
||||
package electroblob.wizardry;
|
||||
|
||||
import org.lwjgl.input.Keyboard;
|
||||
|
||||
import electroblob.wizardry.client.ClientProxy;
|
||||
import electroblob.wizardry.packet.PacketControlInput;
|
||||
import electroblob.wizardry.packet.WizardryPacketHandler;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
|
||||
import net.minecraftforge.fml.common.gameevent.InputEvent;
|
||||
import net.minecraftforge.fml.common.network.simpleimpl.IMessage;
|
||||
|
||||
public class WizardryKeyHandler {
|
||||
|
||||
boolean NkeyPressed = false;
|
||||
boolean BkeyPressed = false;
|
||||
boolean NkeyAlreadyPressed = false;
|
||||
boolean BkeyAlreadyPressed = false;
|
||||
|
||||
@SubscribeEvent
|
||||
public void onKeyInput(InputEvent.KeyInputEvent event){
|
||||
|
||||
// Key pressed
|
||||
if(Keyboard.getEventKeyState()){
|
||||
|
||||
if(Wizardry.proxy instanceof ClientProxy){
|
||||
|
||||
if(ClientProxy.NEXT_SPELL.isPressed() && Minecraft.getMinecraft().inGameHasFocus){
|
||||
if(!NkeyPressed){
|
||||
NkeyPressed = true;
|
||||
}else{
|
||||
NkeyAlreadyPressed = true;
|
||||
}
|
||||
if(!NkeyAlreadyPressed){
|
||||
// Packet building
|
||||
IMessage msg = new PacketControlInput.Message(PacketControlInput.ControlType.NEXT_SPELL_KEY);
|
||||
WizardryPacketHandler.net.sendToServer(msg);
|
||||
}
|
||||
}
|
||||
|
||||
if(ClientProxy.PREVIOUS_SPELL.isPressed() && Minecraft.getMinecraft().inGameHasFocus){
|
||||
if(!BkeyPressed){
|
||||
BkeyPressed = true;
|
||||
}else{
|
||||
BkeyAlreadyPressed = true;
|
||||
}
|
||||
if(!BkeyAlreadyPressed){
|
||||
// Packet building
|
||||
IMessage msg = new PacketControlInput.Message(
|
||||
PacketControlInput.ControlType.PREVIOUS_SPELL_KEY);
|
||||
WizardryPacketHandler.net.sendToServer(msg);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Key released
|
||||
else{
|
||||
if(NkeyPressed){
|
||||
NkeyPressed = false;
|
||||
NkeyAlreadyPressed = false;
|
||||
}else if(BkeyPressed){
|
||||
BkeyPressed = false;
|
||||
BkeyAlreadyPressed = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,948 +0,0 @@
|
||||
package electroblob.wizardry;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Random;
|
||||
import java.util.Set;
|
||||
|
||||
import org.apache.commons.lang3.ArrayUtils;
|
||||
|
||||
import electroblob.wizardry.entity.living.EntityEvilWizard;
|
||||
import electroblob.wizardry.entity.living.EntityWizard;
|
||||
import electroblob.wizardry.registry.WizardryBlocks;
|
||||
import electroblob.wizardry.util.WizardryUtilities;
|
||||
import net.minecraft.block.BlockChest;
|
||||
import net.minecraft.block.BlockColored;
|
||||
import net.minecraft.block.BlockLeaves;
|
||||
import net.minecraft.block.BlockLiquid;
|
||||
import net.minecraft.block.BlockPlanks;
|
||||
import net.minecraft.block.BlockSlab;
|
||||
import net.minecraft.block.BlockSlab.EnumBlockHalf;
|
||||
import net.minecraft.block.BlockTorch;
|
||||
import net.minecraft.block.state.IBlockState;
|
||||
import net.minecraft.init.Biomes;
|
||||
import net.minecraft.init.Blocks;
|
||||
import net.minecraft.inventory.IInventory;
|
||||
import net.minecraft.item.EnumDyeColor;
|
||||
import net.minecraft.item.ItemDoor;
|
||||
import net.minecraft.util.EnumFacing;
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.world.World;
|
||||
import net.minecraft.world.WorldServer;
|
||||
import net.minecraft.world.biome.Biome;
|
||||
import net.minecraft.world.chunk.IChunkProvider;
|
||||
import net.minecraft.world.gen.IChunkGenerator;
|
||||
import net.minecraft.world.gen.feature.WorldGenMinable;
|
||||
import net.minecraft.world.storage.loot.LootContext;
|
||||
import net.minecraft.world.storage.loot.LootTable;
|
||||
import net.minecraftforge.common.BiomeDictionary;
|
||||
import net.minecraftforge.common.IPlantable;
|
||||
import net.minecraftforge.fml.common.IWorldGenerator;
|
||||
|
||||
public class WizardryWorldGenerator implements IWorldGenerator {
|
||||
|
||||
/** The string identifier for wizard tower chests, used in ChestGenHooks. */
|
||||
public static final String WIZARD_TOWER = Wizardry.MODID + "wizardTower";
|
||||
|
||||
@Override
|
||||
public void generate(Random random, int chunkX, int chunkZ, World world, IChunkGenerator chunkGenerator,
|
||||
IChunkProvider chunkProvider){
|
||||
|
||||
for(int id : Wizardry.settings.oreDimensions){
|
||||
if(id == world.provider.getDimension()) this.addOreSpawn(WizardryBlocks.crystal_ore.getDefaultState(),
|
||||
world, random, chunkX * 16, chunkZ * 16, 16, 16, 5, 7, 5, 30);
|
||||
}
|
||||
|
||||
for(int id : Wizardry.settings.flowerDimensions){
|
||||
if(id == world.provider.getDimension()) this.generatePlant(WizardryBlocks.crystal_flower.getDefaultState(),
|
||||
world, random, chunkX * 16, chunkZ * 16, 2, 20);
|
||||
}
|
||||
|
||||
if(world.getWorldInfo().isMapFeaturesEnabled()){
|
||||
for(int id : Wizardry.settings.towerDimensions){
|
||||
if(id == world.provider.getDimension())
|
||||
this.generateWizardTower(world, random, chunkX * 16, chunkZ * 16);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds an Ore Spawn to Minecraft. Simply register all Ores to spawn with this method in your Generation method in
|
||||
* your IWorldGeneration extending Class
|
||||
*
|
||||
* @param The Block to spawn
|
||||
* @param The World to spawn in
|
||||
* @param A Random object for retrieving random positions within the world to spawn the Block
|
||||
* @param An int for passing the X-Coordinate for the Generation method
|
||||
* @param An int for passing the Z-Coordinate for the Generation method
|
||||
* @param An int for setting the maximum X-Coordinate values for spawning on the X-Axis on a Per-Chunk basis
|
||||
* @param An int for setting the maximum Z-Coordinate values for spawning on the Z-Axis on a Per-Chunk basis
|
||||
* @param An int for setting the maximum size of a vein
|
||||
* @param An int for the Number of chances available for the Block to spawn per-chunk
|
||||
* @param An int for the minimum Y-Coordinate height at which this block may spawn
|
||||
* @param An int for the maximum Y-Coordinate height at which this block may spawn
|
||||
**/
|
||||
public void addOreSpawn(IBlockState state, World world, Random random, int blockXPos, int blockZPos, int maxX,
|
||||
int maxZ, int maxVeinSize, int chancesToSpawn, int minY, int maxY){
|
||||
// int maxPossY = minY + (maxY - 1);
|
||||
assert maxY > minY : "The maximum Y must be greater than the Minimum Y";
|
||||
assert maxX > 0 && maxX <= 16 : "addOreSpawn: The Maximum X must be greater than 0 and less than 16";
|
||||
assert minY > 0 : "addOreSpawn: The Minimum Y must be greater than 0";
|
||||
assert maxY < 256 && maxY > 0 : "addOreSpawn: The Maximum Y must be less than 256 but greater than 0";
|
||||
assert maxZ > 0 && maxZ <= 16 : "addOreSpawn: The Maximum Z must be greater than 0 and less than 16";
|
||||
|
||||
int diffBtwnMinMaxY = maxY - minY;
|
||||
for(int x = 0; x < chancesToSpawn; x++){
|
||||
int posX = blockXPos + random.nextInt(maxX);
|
||||
int posY = minY + random.nextInt(diffBtwnMinMaxY);
|
||||
int posZ = blockZPos + random.nextInt(maxZ);
|
||||
(new WorldGenMinable(state, maxVeinSize)).generate(world, random, new BlockPos(posX, posY, posZ));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates the specified plant randomly throughout the world.
|
||||
*
|
||||
* @param block The plant block
|
||||
* @param world The world
|
||||
* @param random A random instance
|
||||
* @param x The x coord of the first block in the chunk
|
||||
* @param z The y coord of the first block in the chunk
|
||||
* @param chancesToSpawn Number of chances to spawn a flower patch
|
||||
* @param groupSize The number of times to try generating a flower per flower patch spawn
|
||||
*/
|
||||
public void generatePlant(IBlockState state, World world, Random random, int x, int z, int chancesToSpawn,
|
||||
int groupSize){
|
||||
|
||||
for(int i = 0; i < chancesToSpawn; i++){
|
||||
int randPosX = x + random.nextInt(16);
|
||||
int randPosY = random.nextInt(256);
|
||||
int randPosZ = z + random.nextInt(16);
|
||||
for(int l = 0; l < groupSize; ++l){
|
||||
int i1 = randPosX + random.nextInt(8) - random.nextInt(8);
|
||||
int j1 = randPosY + random.nextInt(4) - random.nextInt(4);
|
||||
int k1 = randPosZ + random.nextInt(8) - random.nextInt(8);
|
||||
|
||||
BlockPos pos = new BlockPos(i1, j1, k1);
|
||||
|
||||
if(world.isBlockLoaded(pos) && world.isAirBlock(pos) && (!world.provider.isNether() || j1 < 127)
|
||||
&& state.getBlock().canPlaceBlockOnSide(world, pos, EnumFacing.UP)){
|
||||
|
||||
world.setBlockState(pos, state, 2);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates wizard towers randomly throughout the world.
|
||||
*/
|
||||
public void generateWizardTower(World world, Random random, int chunkX, int chunkZ){
|
||||
|
||||
// Allows the config file to set the rarity value to 0 to disable tower generation completely.
|
||||
if(Wizardry.settings.towerRarity == 0) return;
|
||||
|
||||
// Compensates for the lack of space in forests. Math.max is required since treeless biomes have treesPerChunk =
|
||||
// -999
|
||||
// double treeFactor = 70 - Math.max((double)world.getBiomeGenForCoords(chunkX,
|
||||
// chunkZ).theBiomeDecorator.treesPerChunk, 0) * 1.5d;
|
||||
|
||||
// Multiplied by 70 to (roughly) retain the old rarity scale
|
||||
if(random.nextInt((int)(Wizardry.settings.towerRarity * 70)) == 0){
|
||||
|
||||
BlockPos origin = new BlockPos(chunkX + random.nextInt(16), 0, chunkZ + random.nextInt(16));
|
||||
|
||||
// Despite what its name suggests, this method does not return the position of a liquid. It is in fact
|
||||
// exactly what is needed here since it is used for placing villages and stuff, and doesn't include leaves
|
||||
// or other foliage.
|
||||
origin = origin.up(world.getTopSolidOrLiquidBlock(origin).getY() - 1);
|
||||
|
||||
int[][][] towerBlueprint = towerBlueprintSmall;
|
||||
|
||||
switch(random.nextInt(4)){
|
||||
case 0:
|
||||
towerBlueprint = towerBlueprintSmall;
|
||||
break;
|
||||
case 1:
|
||||
towerBlueprint = towerBlueprintMedium;
|
||||
break;
|
||||
case 2:
|
||||
towerBlueprint = towerBlueprintTall;
|
||||
break;
|
||||
case 3:
|
||||
towerBlueprint = towerBlueprintDouble;
|
||||
break;
|
||||
}
|
||||
|
||||
// 0 = West, 1 = North, 2 = East, 3 = South (The way you would face when walking out of the door)
|
||||
EnumFacing orientation = EnumFacing.getHorizontal(random.nextInt(4));
|
||||
boolean flip = random.nextBoolean();
|
||||
|
||||
if(checkSpaceForTower(world, origin, towerBlueprint, orientation, flip)){
|
||||
|
||||
// == Setup ==
|
||||
|
||||
boolean evilWizard = random.nextInt(5) == 0;
|
||||
|
||||
IBlockState wallMaterial = Blocks.COBBLESTONE.getDefaultState();
|
||||
BlockPlanks.EnumType woodType = BlockPlanks.EnumType.OAK;
|
||||
|
||||
Biome biome = world.getBiome(origin);
|
||||
|
||||
// The order of these is somewhat important in that biomes can be many types, so the last of these
|
||||
// checks
|
||||
// that is true gets priority. Generally speaking, the later the check, the more specific it is.
|
||||
if(BiomeDictionary.hasType(biome, BiomeDictionary.Type.DENSE))
|
||||
wallMaterial = Blocks.MOSSY_COBBLESTONE.getDefaultState();
|
||||
if(BiomeDictionary.hasType(biome, BiomeDictionary.Type.SWAMP))
|
||||
wallMaterial = Blocks.MOSSY_COBBLESTONE.getDefaultState();
|
||||
if(BiomeDictionary.hasType(biome, BiomeDictionary.Type.SANDY))
|
||||
wallMaterial = Blocks.SANDSTONE.getDefaultState();
|
||||
if(BiomeDictionary.hasType(biome, BiomeDictionary.Type.NETHER))
|
||||
wallMaterial = Blocks.NETHER_BRICK.getDefaultState();
|
||||
if(BiomeDictionary.hasType(biome, BiomeDictionary.Type.MOUNTAIN))
|
||||
wallMaterial = Blocks.STONEBRICK.getDefaultState();
|
||||
if(BiomeDictionary.hasType(biome, BiomeDictionary.Type.MESA))
|
||||
wallMaterial = Blocks.HARDENED_CLAY.getDefaultState();
|
||||
|
||||
// Unfortunately, I can't check all the wood types with the biome dictionary
|
||||
if(BiomeDictionary.hasType(biome, BiomeDictionary.Type.CONIFEROUS))
|
||||
woodType = BlockPlanks.EnumType.SPRUCE;
|
||||
if(biome == Biomes.BIRCH_FOREST || biome == Biomes.BIRCH_FOREST_HILLS)
|
||||
woodType = BlockPlanks.EnumType.BIRCH;
|
||||
if(BiomeDictionary.hasType(biome, BiomeDictionary.Type.JUNGLE)) woodType = BlockPlanks.EnumType.JUNGLE;
|
||||
if(BiomeDictionary.hasType(biome, BiomeDictionary.Type.SAVANNA)) woodType = BlockPlanks.EnumType.ACACIA;
|
||||
// Not technically a tree type, but I think it fits quite well anyway
|
||||
if(BiomeDictionary.hasType(biome, BiomeDictionary.Type.SPOOKY))
|
||||
woodType = BlockPlanks.EnumType.DARK_OAK;
|
||||
|
||||
IBlockState[] blockStateList = new IBlockState[]{null, Blocks.AIR.getDefaultState(),
|
||||
Blocks.PLANKS.getDefaultState().withProperty(BlockPlanks.VARIANT, woodType),
|
||||
Blocks.BOOKSHELF.getDefaultState(),
|
||||
Blocks.STAINED_HARDENED_CLAY.getDefaultState().withProperty(BlockColored.COLOR,
|
||||
EnumDyeColor.values()[random.nextInt(EnumDyeColor.values().length)]),
|
||||
Blocks.WOODEN_SLAB.getDefaultState().withProperty(BlockSlab.HALF, EnumBlockHalf.BOTTOM),
|
||||
Blocks.WOODEN_SLAB.getDefaultState().withProperty(BlockSlab.HALF, EnumBlockHalf.TOP),
|
||||
wallMaterial, Blocks.GLASS_PANE.getDefaultState(), Blocks.OAK_DOOR.getDefaultState(),
|
||||
WizardryBlocks.arcane_workbench.getDefaultState(), Blocks.TORCH.getDefaultState(),
|
||||
evilWizard ? Blocks.CHEST.getDefaultState() : Blocks.BOOKSHELF.getDefaultState()};
|
||||
|
||||
Set<BlockPos> blocksPlaced = new HashSet<BlockPos>();
|
||||
|
||||
// == Foundations ==
|
||||
|
||||
// Fills in foundations. This is done first so the door always has something to be placed on.
|
||||
boolean flag = true;
|
||||
|
||||
// BlockPos is immutable, so I'm not sure if simply saying pos1 = pos will be sufficient.
|
||||
BlockPos layerCentre = new BlockPos(origin);
|
||||
|
||||
while(flag){
|
||||
|
||||
flag = false;
|
||||
|
||||
for(BlockPos offset : foundationLayer){
|
||||
if(!world.isBlockNormalCube(layerCentre.add(offset), false)){
|
||||
world.setBlockState(layerCentre.add(offset), wallMaterial);
|
||||
blocksPlaced.add(layerCentre.add(offset));
|
||||
// Keeps going as long as something was filled in.
|
||||
flag = true;
|
||||
}
|
||||
}
|
||||
|
||||
layerCentre = layerCentre.down();
|
||||
}
|
||||
|
||||
// == Main Structure ==
|
||||
|
||||
// It is assumed that the width of the blueprint is the same all the way up, and that the layers are
|
||||
// square.
|
||||
int width = towerBlueprint[0].length - 1;
|
||||
|
||||
// x, y and z are the position the block is being put in.
|
||||
// x1, y and z1 are the position in the blueprint which determines which block is being placed.
|
||||
|
||||
int x1 = 0, z1 = 0;
|
||||
|
||||
for(int y = 0; y < towerBlueprint.length; y++){
|
||||
for(int z = 0; z < towerBlueprint[y].length; z++){
|
||||
for(int x = 0; x < towerBlueprint[y][z].length; x++){
|
||||
|
||||
BlockPos pos = origin.add(x - width / 2, y, z - width / 2);
|
||||
|
||||
switch(orientation){
|
||||
case WEST:
|
||||
x1 = flip ? width - x : x;
|
||||
z1 = z;
|
||||
break;
|
||||
case NORTH:
|
||||
x1 = z;
|
||||
z1 = flip ? x : width - x;
|
||||
break;
|
||||
case EAST:
|
||||
x1 = flip ? x : width - x;
|
||||
z1 = width - z;
|
||||
break;
|
||||
case SOUTH:
|
||||
x1 = width - z;
|
||||
z1 = flip ? width - x : x;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if(blockStateList[towerBlueprint[y][z1][x1]] != null
|
||||
&& blockStateList[towerBlueprint[y][z1][x1]].getBlock() != Blocks.TORCH
|
||||
&& blockStateList[towerBlueprint[y][z1][x1]].getBlock() != Blocks.CHEST){
|
||||
|
||||
if(blockStateList[towerBlueprint[y][z1][x1]].getBlock() == Blocks.OAK_DOOR){
|
||||
// Rotates the door depending on whether flip is true.
|
||||
ItemDoor.placeDoor(
|
||||
world, pos, flip
|
||||
? EnumFacing.getHorizontal(3 - orientation.getHorizontalIndex())
|
||||
.getOpposite()
|
||||
: orientation.rotateYCCW(),
|
||||
Blocks.OAK_DOOR, false);
|
||||
}else{
|
||||
world.setBlockState(pos, blockStateList[towerBlueprint[y][z1][x1]], 2);
|
||||
}
|
||||
|
||||
blocksPlaced.add(pos);
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// == Extras ==
|
||||
|
||||
// Torches are done afterwards so they don't fall off
|
||||
// Chests are also done afterwards, because for some reason the chest decides which way round to face
|
||||
// itself, after it has been placed, based on the surrounding blocks... but if it was placed during
|
||||
// the rest of the tower generation, some of those blocks wouldn't exist, hence it must be done here.
|
||||
for(int y = 0; y < towerBlueprint.length; y++){
|
||||
for(int z = 0; z < towerBlueprint[y].length; z++){
|
||||
for(int x = 0; x < towerBlueprint[y][z].length; x++){
|
||||
|
||||
BlockPos pos = origin.add(x - width / 2, y, z - width / 2);
|
||||
|
||||
switch(orientation){
|
||||
case WEST:
|
||||
x1 = flip ? width - x : x;
|
||||
z1 = z;
|
||||
break;
|
||||
case NORTH:
|
||||
x1 = z;
|
||||
z1 = flip ? x : width - x;
|
||||
break;
|
||||
case EAST:
|
||||
x1 = flip ? x : width - x;
|
||||
z1 = width - z;
|
||||
break;
|
||||
case SOUTH:
|
||||
x1 = width - z;
|
||||
z1 = flip ? width - x : x;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if(blockStateList[towerBlueprint[y][z1][x1]] != null){
|
||||
|
||||
if(blockStateList[towerBlueprint[y][z1][x1]].getBlock() == Blocks.TORCH){
|
||||
if(placeTorch(world, pos, true)){
|
||||
blocksPlaced.add(pos);
|
||||
}else{
|
||||
Wizardry.logger.info("Attempted to generate a torch at " + pos + " in " + world
|
||||
+ ", but failed!");
|
||||
}
|
||||
}
|
||||
|
||||
// World should always be a WorldServer, but it's worth checking anyway.
|
||||
if(blockStateList[towerBlueprint[y][z1][x1]].getBlock() == Blocks.CHEST
|
||||
&& world instanceof WorldServer){
|
||||
if(placeChest(world, pos)){
|
||||
blocksPlaced.add(pos);
|
||||
LootTable table = world.getLootTableManager().getLootTableFromLocation(
|
||||
new ResourceLocation(Wizardry.MODID, "chests/wizard_tower"));
|
||||
IInventory inventory = (IInventory)world.getTileEntity(pos);
|
||||
LootContext context = new LootContext.Builder((WorldServer)world).build();
|
||||
table.fillInventory(inventory, random, context);
|
||||
}else{
|
||||
Wizardry.logger.info("Attempted to generate a chest at " + pos + " in " + world
|
||||
+ ", but failed!");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(evilWizard){
|
||||
|
||||
EntityEvilWizard wizard = new EntityEvilWizard(world);
|
||||
wizard.hasTower = true;
|
||||
wizard.setLocationAndAngles(origin.getX() + 1.5, origin.getY() + towerBlueprint.length - 9.5,
|
||||
origin.getZ() + 1.5, 0, 0);
|
||||
wizard.onInitialSpawn(world.getDifficultyForLocation(origin), null);
|
||||
|
||||
world.spawnEntity(wizard);
|
||||
|
||||
}else{
|
||||
|
||||
EntityWizard wizard = new EntityWizard(world);
|
||||
wizard.setLocationAndAngles(origin.getX() + 1.5, origin.getY() + towerBlueprint.length - 9.5,
|
||||
origin.getZ() + 1.5, 0, 0);
|
||||
wizard.onInitialSpawn(world.getDifficultyForLocation(origin), null);
|
||||
wizard.setTowerBlocks(blocksPlaced);
|
||||
|
||||
world.spawnEntity(wizard);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Places a torch at the given position in the given world and automatically assigns an appropriate state. Order of
|
||||
* priority is U-S-W-N-E, or S-W-N-E-U if wallPriority is true.
|
||||
*
|
||||
* @param world The world to place the torch in.
|
||||
* @param pos The position to place the torch at.
|
||||
* @param wallPriority True to prioritise wall torches, false to prioritise floor torches.
|
||||
* @return True if the torch was placed, false if it is not possible.
|
||||
*/
|
||||
private static boolean placeTorch(World world, BlockPos pos, boolean wallPriority){
|
||||
|
||||
for(EnumFacing facing : ArrayUtils.add(EnumFacing.HORIZONTALS, wallPriority ? 4 : 0, EnumFacing.UP)){
|
||||
if(world.isSideSolid(pos.offset(facing.getOpposite()), facing)){
|
||||
world.setBlockState(pos, Blocks.TORCH.getDefaultState().withProperty(BlockTorch.FACING, facing));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Places a chest at the given position in the given world and automatically assigns an appropriate state. Order of
|
||||
* priority is S-W-N-E.
|
||||
*
|
||||
* @param world The world to chest the torch in.
|
||||
* @param pos The position to chest the torch at.
|
||||
* @return True if the chest was placed, false if it is not possible.
|
||||
*/
|
||||
private static boolean placeChest(World world, BlockPos pos){
|
||||
|
||||
for(EnumFacing facing : EnumFacing.HORIZONTALS){
|
||||
if(world.isAirBlock(pos.offset(facing))){
|
||||
world.setBlockState(pos, Blocks.CHEST.getDefaultState().withProperty(BlockChest.FACING, facing));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether a tower generated at the given coordinates will intersect any solid or liquid blocks. Only tests
|
||||
* for liquids (not solid blocks) for the first four layers to account for the floor and for sloping terrain.
|
||||
*
|
||||
* @return True if none of the blocks which the tower would replace are solid or liquid. Dirt, stone etc., water and
|
||||
* lava count, as do logs, but leaves and plants don't.
|
||||
*/
|
||||
private static boolean checkSpaceForTower(World world, BlockPos pos, int[][][] towerBlueprint,
|
||||
EnumFacing orientation, boolean flip){
|
||||
|
||||
// x, y and z are the position the block is being put in.
|
||||
// x1, y and z1 are the position in the blueprint which determines which block is being placed.
|
||||
|
||||
int x1 = 0, z1 = 0;
|
||||
|
||||
// It is assumed that the width of the blueprint is the same all the way up, and that the layers are square.
|
||||
int width = towerBlueprint[0].length - 1;
|
||||
|
||||
for(int y = 0; y < towerBlueprint.length; y++){
|
||||
for(int z = 0; z < towerBlueprint[y].length; z++){
|
||||
for(int x = 0; x < towerBlueprint[y][z].length; x++){
|
||||
|
||||
BlockPos pos1 = pos.add(x - width / 2, y, z - width / 2);
|
||||
|
||||
switch(orientation){
|
||||
case WEST:
|
||||
x1 = flip ? width - x : x;
|
||||
z1 = z;
|
||||
break;
|
||||
case NORTH:
|
||||
x1 = z;
|
||||
z1 = flip ? x : width - x;
|
||||
break;
|
||||
case EAST:
|
||||
x1 = flip ? x : width - x;
|
||||
z1 = width - z;
|
||||
break;
|
||||
case SOUTH:
|
||||
x1 = width - z;
|
||||
z1 = flip ? width - x : x;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if(towerBlueprint[y][z1][x1] != 0 && !WizardryUtilities.canBlockBeReplacedB(world, pos1)
|
||||
// TODO: Is this a better replacement for the subsequent two lines?
|
||||
// && !world.getBlockState(pos1).getBlock().isFoliage(world, pos1)
|
||||
&& !(world.getBlockState(pos1).getBlock() instanceof IPlantable)
|
||||
&& !(world.getBlockState(pos1).getBlock() instanceof BlockLeaves)
|
||||
&& (y > 3 || world.getBlockState(pos1).getBlock() instanceof BlockLiquid)){
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/** Array of relative positions of each block in a layer of foundations. */
|
||||
private static final BlockPos[] foundationLayer = new BlockPos[]{new BlockPos(-2, 0, -1), new BlockPos(-2, 0, 0),
|
||||
new BlockPos(-2, 0, 1), new BlockPos(2, 0, -1), new BlockPos(2, 0, 0), new BlockPos(2, 0, 1),
|
||||
new BlockPos(-1, 0, -2), new BlockPos(0, 0, -2), new BlockPos(1, 0, -2), new BlockPos(-1, 0, 2),
|
||||
new BlockPos(0, 0, 2), new BlockPos(1, 0, 2),};
|
||||
|
||||
/**
|
||||
* 3D matrix of integers representing the different blocks which make up the wizard tower. The blocks corresponding
|
||||
* to each integer are as follows (note that some blocks change depending on the biome):
|
||||
* <p>
|
||||
* 0 Nothing (keep existing block)<br>
|
||||
* 1 Air (remove existing block)<br>
|
||||
* 2 Floor (planks)<br>
|
||||
* 3 Bookshelf<br>
|
||||
* 4 Roof (stained clay)<br>
|
||||
* 5 Floor slab (lower half)<br>
|
||||
* 6 Floor slab (upper half)<br>
|
||||
* 7 Wall (cobblestone by default)<br>
|
||||
* 8 Glass pane<br>
|
||||
* 9 Door (metadata is handled by the built-in vanilla method)<br>
|
||||
* 10 Arcane workbench<br>
|
||||
* 11 Torch (metadata is handled separately depending on adjacent blocks)<br>
|
||||
* 12 Chest (only generates if the wizard is evil, otherwise places a bookshelf instead)
|
||||
*/
|
||||
private static final int[][][] towerBlueprintSmall = {
|
||||
// x is horizontal, z is vertical, y is layers
|
||||
{{0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
{0, 0, 0, 2, 2, 2, 0, 0, 0}, {0, 0, 0, 2, 2, 2, 0, 0, 0}, {0, 0, 0, 2, 2, 2, 0, 0, 0},
|
||||
{0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0},},
|
||||
{{0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 7, 7, 7, 0, 0, 0},
|
||||
{0, 0, 7, 1, 6, 5, 7, 0, 0}, {0, 0, 7, 1, 1, 1, 7, 0, 0}, {0, 0, 7, 1, 1, 1, 7, 0, 0},
|
||||
{0, 0, 0, 7, 9, 7, 0, 0, 0}, {0, 0, 0, 1, 1, 1, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0},},
|
||||
{{0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 7, 7, 7, 0, 0, 0},
|
||||
{0, 0, 7, 5, 1, 1, 7, 0, 0}, {0, 0, 7, 6, 1, 1, 7, 0, 0}, {0, 0, 7, 1, 1, 1, 7, 0, 0},
|
||||
{0, 0, 0, 7, 0, 7, 0, 0, 0}, {0, 0, 0, 11, 1, 11, 0, 0, 0}, {0, 0, 0, 1, 1, 1, 0, 0, 0},},
|
||||
{{0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 7, 8, 7, 0, 0, 0},
|
||||
{0, 0, 7, 1, 1, 1, 7, 0, 0}, {0, 0, 7, 1, 1, 11, 7, 0, 0}, {0, 0, 7, 5, 6, 1, 7, 0, 0},
|
||||
{0, 0, 0, 7, 7, 7, 0, 0, 0}, {0, 0, 0, 1, 1, 1, 0, 0, 0}, {0, 0, 0, 1, 1, 1, 0, 0, 0},},
|
||||
{{0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 7, 7, 7, 0, 0, 0},
|
||||
{0, 0, 7, 1, 1, 1, 7, 0, 0}, {0, 0, 7, 1, 1, 6, 7, 0, 0}, {0, 0, 7, 1, 1, 5, 7, 0, 0},
|
||||
{0, 0, 0, 7, 7, 7, 0, 0, 0}, {0, 0, 0, 1, 1, 1, 0, 0, 0}, {0, 0, 0, 1, 1, 1, 0, 0, 0},},
|
||||
{{0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 7, 7, 7, 0, 0, 0},
|
||||
{0, 0, 7, 1, 6, 5, 7, 0, 0}, {0, 0, 7, 11, 1, 1, 7, 0, 0}, {0, 0, 7, 1, 1, 1, 7, 0, 0},
|
||||
{0, 0, 0, 7, 8, 7, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0},},
|
||||
{{0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 7, 7, 7, 0, 0, 0},
|
||||
{0, 0, 7, 5, 1, 1, 7, 0, 0}, {0, 0, 7, 1, 1, 1, 7, 0, 0}, {0, 0, 7, 1, 1, 1, 7, 0, 0},
|
||||
{0, 0, 0, 7, 7, 7, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0},},
|
||||
{{0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 7, 7, 7, 0, 0, 0}, {0, 0, 7, 2, 2, 2, 7, 0, 0},
|
||||
{0, 7, 1, 1, 1, 1, 2, 7, 0}, {0, 7, 5, 2, 2, 6, 2, 7, 0}, {0, 7, 2, 2, 2, 2, 2, 7, 0},
|
||||
{0, 0, 7, 2, 2, 2, 7, 0, 0}, {0, 0, 0, 7, 7, 7, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0},},
|
||||
{{0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 7, 7, 7, 0, 0, 0}, {0, 0, 7, 3, 3, 3, 7, 0, 0},
|
||||
{0, 7, 1, 1, 1, 1, 3, 7, 0}, {0, 7, 1, 1, 1, 1, 3, 7, 0}, {0, 7, 1, 1, 10, 1, 3, 7, 0},
|
||||
{0, 0, 7, 1, 1, 1, 7, 0, 0}, {0, 0, 0, 7, 7, 7, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0},},
|
||||
{{0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 7, 8, 7, 0, 0, 0}, {0, 0, 7, 11, 1, 11, 7, 0, 0},
|
||||
{0, 7, 1, 1, 1, 1, 3, 7, 0}, {0, 8, 1, 1, 1, 1, 3, 7, 0}, {0, 7, 1, 1, 1, 1, 3, 7, 0},
|
||||
{0, 0, 7, 11, 1, 11, 7, 0, 0}, {0, 0, 0, 7, 8, 7, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0},},
|
||||
{{0, 0, 0, 4, 4, 4, 0, 0, 0}, {0, 0, 4, 7, 7, 7, 4, 0, 0}, {0, 4, 7, 1, 1, 1, 7, 4, 0},
|
||||
{4, 7, 1, 1, 1, 1, 3, 7, 4}, {4, 7, 1, 1, 1, 1, 3, 7, 4}, {4, 7, 1, 1, 1, 1, 12, 7, 4},
|
||||
{0, 4, 7, 1, 1, 1, 7, 4, 0}, {0, 0, 4, 7, 7, 7, 4, 0, 0}, {0, 0, 0, 4, 4, 4, 0, 0, 0},},
|
||||
{{0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 4, 4, 4, 0, 0, 0}, {0, 0, 4, 1, 1, 1, 4, 0, 0},
|
||||
{0, 4, 1, 1, 1, 1, 1, 4, 0}, {0, 4, 1, 1, 1, 1, 1, 4, 0}, {0, 4, 1, 1, 1, 1, 1, 4, 0},
|
||||
{0, 0, 4, 1, 1, 1, 4, 0, 0}, {0, 0, 0, 4, 4, 4, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0},},
|
||||
{{0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 4, 4, 4, 0, 0, 0},
|
||||
{0, 0, 4, 1, 1, 1, 4, 0, 0}, {0, 0, 4, 1, 1, 1, 4, 0, 0}, {0, 0, 4, 1, 1, 1, 4, 0, 0},
|
||||
{0, 0, 0, 4, 4, 4, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0},},
|
||||
{{0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 4, 0, 0, 0, 0},
|
||||
{0, 0, 0, 4, 1, 4, 0, 0, 0}, {0, 0, 4, 1, 1, 1, 4, 0, 0}, {0, 0, 0, 4, 1, 4, 0, 0, 0},
|
||||
{0, 0, 0, 0, 4, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0},},
|
||||
{{0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
{0, 0, 0, 4, 4, 4, 0, 0, 0}, {0, 0, 0, 4, 1, 4, 0, 0, 0}, {0, 0, 0, 4, 4, 4, 0, 0, 0},
|
||||
{0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0},},
|
||||
{{0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
{0, 0, 0, 0, 4, 0, 0, 0, 0}, {0, 0, 0, 4, 1, 4, 0, 0, 0}, {0, 0, 0, 0, 4, 0, 0, 0, 0},
|
||||
{0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0},},
|
||||
{{0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
{0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 4, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
{0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0},},
|
||||
{{0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
{0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 4, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
{0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0},}};
|
||||
|
||||
/**
|
||||
* 3D matrix of integers representing the different blocks which make up the wizard tower. The blocks corresponding
|
||||
* to each integer are as follows (note that some blocks change depending on the biome):
|
||||
* <p>
|
||||
* 0 Nothing (keep existing block)<br>
|
||||
* 1 Air (remove existing block)<br>
|
||||
* 2 Floor (planks)<br>
|
||||
* 3 Bookshelf<br>
|
||||
* 4 Roof (stained clay)<br>
|
||||
* 5 Floor slab (lower half)<br>
|
||||
* 6 Floor slab (upper half)<br>
|
||||
* 7 Wall (cobblestone by default)<br>
|
||||
* 8 Glass pane<br>
|
||||
* 9 Door (metadata is handled by the built-in vanilla method)<br>
|
||||
* 10 Arcane workbench<br>
|
||||
* 11 Torch (metadata is handled separately depending on adjacent blocks)<br>
|
||||
* 12 Chest (only generates if the wizard is evil, otherwise places a bookshelf instead)
|
||||
*/
|
||||
private static final int[][][] towerBlueprintMedium = {
|
||||
// x is horizontal, z is vertical, y is layers
|
||||
{{0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
{0, 0, 0, 2, 2, 2, 0, 0, 0}, {0, 0, 0, 2, 2, 2, 0, 0, 0}, {0, 0, 0, 2, 2, 2, 0, 0, 0},
|
||||
{0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0},},
|
||||
{{0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 7, 7, 7, 0, 0, 0},
|
||||
{0, 0, 7, 1, 6, 5, 7, 0, 0}, {0, 0, 7, 1, 1, 1, 7, 0, 0}, {0, 0, 7, 1, 1, 1, 7, 0, 0},
|
||||
{0, 0, 0, 7, 9, 7, 0, 0, 0}, {0, 0, 0, 1, 1, 1, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0},},
|
||||
{{0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 7, 7, 7, 0, 0, 0},
|
||||
{0, 0, 7, 5, 1, 1, 7, 0, 0}, {0, 0, 7, 6, 1, 1, 7, 0, 0}, {0, 0, 7, 1, 1, 1, 7, 0, 0},
|
||||
{0, 0, 0, 7, 0, 7, 0, 0, 0}, {0, 0, 0, 11, 1, 11, 0, 0, 0}, {0, 0, 0, 1, 1, 1, 0, 0, 0},},
|
||||
{{0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 7, 8, 7, 0, 0, 0},
|
||||
{0, 0, 7, 1, 1, 1, 7, 0, 0}, {0, 0, 7, 1, 1, 11, 7, 0, 0}, {0, 0, 7, 5, 6, 1, 7, 0, 0},
|
||||
{0, 0, 0, 7, 7, 7, 0, 0, 0}, {0, 0, 0, 1, 1, 1, 0, 0, 0}, {0, 0, 0, 1, 1, 1, 0, 0, 0},},
|
||||
{{0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 7, 7, 7, 0, 0, 0},
|
||||
{0, 0, 7, 1, 1, 1, 7, 0, 0}, {0, 0, 7, 1, 1, 6, 7, 0, 0}, {0, 0, 7, 1, 1, 5, 7, 0, 0},
|
||||
{0, 0, 0, 7, 7, 7, 0, 0, 0}, {0, 0, 0, 1, 1, 1, 0, 0, 0}, {0, 0, 0, 1, 1, 1, 0, 0, 0},},
|
||||
{{0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 7, 7, 7, 0, 0, 0},
|
||||
{0, 0, 7, 1, 6, 5, 7, 0, 0}, {0, 0, 7, 11, 1, 1, 7, 0, 0}, {0, 0, 7, 1, 1, 1, 7, 0, 0},
|
||||
{0, 0, 0, 7, 8, 7, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0},},
|
||||
{{0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 7, 7, 7, 0, 0, 0},
|
||||
{0, 0, 7, 5, 1, 1, 7, 0, 0}, {0, 0, 7, 6, 1, 1, 7, 0, 0}, {0, 0, 7, 1, 1, 1, 7, 0, 0},
|
||||
{0, 0, 0, 7, 7, 7, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0},},
|
||||
{{0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 7, 8, 7, 0, 0, 0},
|
||||
{0, 0, 7, 1, 1, 1, 7, 0, 0}, {0, 0, 7, 1, 1, 11, 7, 0, 0}, {0, 0, 7, 5, 6, 1, 7, 0, 0},
|
||||
{0, 0, 0, 7, 7, 7, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0},},
|
||||
{{0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 7, 7, 7, 0, 0, 0},
|
||||
{0, 0, 7, 1, 1, 1, 7, 0, 0}, {0, 0, 7, 1, 1, 6, 7, 0, 0}, {0, 0, 7, 1, 1, 5, 7, 0, 0},
|
||||
{0, 0, 0, 7, 7, 7, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0},},
|
||||
{{0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 7, 7, 7, 0, 0, 0},
|
||||
{0, 0, 7, 1, 6, 5, 7, 0, 0}, {0, 0, 7, 11, 1, 1, 7, 0, 0}, {0, 0, 7, 1, 1, 1, 7, 0, 0},
|
||||
{0, 0, 0, 7, 8, 7, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0},},
|
||||
{{0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 7, 7, 7, 0, 0, 0},
|
||||
{0, 0, 7, 5, 1, 1, 7, 0, 0}, {0, 0, 7, 1, 1, 1, 7, 0, 0}, {0, 0, 7, 1, 1, 1, 7, 0, 0},
|
||||
{0, 0, 0, 7, 7, 7, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0},},
|
||||
{{0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 7, 7, 7, 0, 0, 0}, {0, 0, 7, 2, 2, 2, 7, 0, 0},
|
||||
{0, 7, 1, 1, 1, 1, 2, 7, 0}, {0, 7, 5, 2, 2, 6, 2, 7, 0}, {0, 7, 2, 2, 2, 2, 2, 7, 0},
|
||||
{0, 0, 7, 2, 2, 2, 7, 0, 0}, {0, 0, 0, 7, 7, 7, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0},},
|
||||
{{0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 7, 7, 7, 0, 0, 0}, {0, 0, 7, 3, 3, 3, 7, 0, 0},
|
||||
{0, 7, 1, 1, 1, 1, 3, 7, 0}, {0, 7, 1, 1, 1, 1, 3, 7, 0}, {0, 7, 1, 1, 10, 1, 3, 7, 0},
|
||||
{0, 0, 7, 1, 1, 1, 7, 0, 0}, {0, 0, 0, 7, 7, 7, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0},},
|
||||
{{0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 7, 8, 7, 0, 0, 0}, {0, 0, 7, 11, 1, 11, 7, 0, 0},
|
||||
{0, 7, 1, 1, 1, 1, 3, 7, 0}, {0, 8, 1, 1, 1, 1, 3, 7, 0}, {0, 7, 1, 1, 1, 1, 3, 7, 0},
|
||||
{0, 0, 7, 11, 1, 11, 7, 0, 0}, {0, 0, 0, 7, 8, 7, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0},},
|
||||
{{0, 0, 0, 4, 4, 4, 0, 0, 0}, {0, 0, 4, 7, 7, 7, 4, 0, 0}, {0, 4, 7, 1, 1, 1, 7, 4, 0},
|
||||
{4, 7, 1, 1, 1, 1, 3, 7, 4}, {4, 7, 1, 1, 1, 1, 3, 7, 4}, {4, 7, 1, 1, 1, 1, 12, 7, 4},
|
||||
{0, 4, 7, 1, 1, 1, 7, 4, 0}, {0, 0, 4, 7, 7, 7, 4, 0, 0}, {0, 0, 0, 4, 4, 4, 0, 0, 0},},
|
||||
{{0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 4, 4, 4, 0, 0, 0}, {0, 0, 4, 1, 1, 1, 4, 0, 0},
|
||||
{0, 4, 1, 1, 1, 1, 1, 4, 0}, {0, 4, 1, 1, 1, 1, 1, 4, 0}, {0, 4, 1, 1, 1, 1, 1, 4, 0},
|
||||
{0, 0, 4, 1, 1, 1, 4, 0, 0}, {0, 0, 0, 4, 4, 4, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0},},
|
||||
{{0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 4, 4, 4, 0, 0, 0},
|
||||
{0, 0, 4, 1, 1, 1, 4, 0, 0}, {0, 0, 4, 1, 1, 1, 4, 0, 0}, {0, 0, 4, 1, 1, 1, 4, 0, 0},
|
||||
{0, 0, 0, 4, 4, 4, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0},},
|
||||
{{0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 4, 0, 0, 0, 0},
|
||||
{0, 0, 0, 4, 1, 4, 0, 0, 0}, {0, 0, 4, 1, 1, 1, 4, 0, 0}, {0, 0, 0, 4, 1, 4, 0, 0, 0},
|
||||
{0, 0, 0, 0, 4, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0},},
|
||||
{{0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
{0, 0, 0, 4, 4, 4, 0, 0, 0}, {0, 0, 0, 4, 1, 4, 0, 0, 0}, {0, 0, 0, 4, 4, 4, 0, 0, 0},
|
||||
{0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0},},
|
||||
{{0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
{0, 0, 0, 0, 4, 0, 0, 0, 0}, {0, 0, 0, 4, 1, 4, 0, 0, 0}, {0, 0, 0, 0, 4, 0, 0, 0, 0},
|
||||
{0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0},},
|
||||
{{0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
{0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 4, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
{0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0},},
|
||||
{{0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
{0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 4, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
{0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0},}};
|
||||
|
||||
/**
|
||||
* 3D matrix of integers representing the different blocks which make up the wizard tower. The blocks corresponding
|
||||
* to each integer are as follows (note that some blocks change depending on the biome):
|
||||
* <p>
|
||||
* 0 Nothing (keep existing block)<br>
|
||||
* 1 Air (remove existing block)<br>
|
||||
* 2 Floor (planks)<br>
|
||||
* 3 Bookshelf<br>
|
||||
* 4 Roof (stained clay)<br>
|
||||
* 5 Floor slab (lower half)<br>
|
||||
* 6 Floor slab (upper half)<br>
|
||||
* 7 Wall (cobblestone by default)<br>
|
||||
* 8 Glass pane<br>
|
||||
* 9 Door (metadata is handled by the built-in vanilla method)<br>
|
||||
* 10 Arcane workbench<br>
|
||||
* 11 Torch (metadata is handled separately depending on adjacent blocks)<br>
|
||||
* 12 Chest (only generates if the wizard is evil, otherwise places a bookshelf instead)
|
||||
*/
|
||||
private static final int[][][] towerBlueprintTall = {
|
||||
// x is horizontal, z is vertical, y is layers
|
||||
{{0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
{0, 0, 0, 2, 2, 2, 0, 0, 0}, {0, 0, 0, 2, 2, 2, 0, 0, 0}, {0, 0, 0, 2, 2, 2, 0, 0, 0},
|
||||
{0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0},},
|
||||
{{0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 7, 7, 7, 0, 0, 0},
|
||||
{0, 0, 7, 1, 6, 5, 7, 0, 0}, {0, 0, 7, 1, 1, 1, 7, 0, 0}, {0, 0, 7, 1, 1, 1, 7, 0, 0},
|
||||
{0, 0, 0, 7, 9, 7, 0, 0, 0}, {0, 0, 0, 1, 1, 1, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0},},
|
||||
{{0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 7, 7, 7, 0, 0, 0},
|
||||
{0, 0, 7, 5, 1, 1, 7, 0, 0}, {0, 0, 7, 6, 1, 1, 7, 0, 0}, {0, 0, 7, 1, 1, 1, 7, 0, 0},
|
||||
{0, 0, 0, 7, 0, 7, 0, 0, 0}, {0, 0, 0, 11, 1, 11, 0, 0, 0}, {0, 0, 0, 1, 1, 1, 0, 0, 0},},
|
||||
{{0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 7, 8, 7, 0, 0, 0},
|
||||
{0, 0, 7, 1, 1, 1, 7, 0, 0}, {0, 0, 7, 1, 1, 11, 7, 0, 0}, {0, 0, 7, 5, 6, 1, 7, 0, 0},
|
||||
{0, 0, 0, 7, 7, 7, 0, 0, 0}, {0, 0, 0, 1, 1, 1, 0, 0, 0}, {0, 0, 0, 1, 1, 1, 0, 0, 0},},
|
||||
{{0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 7, 7, 7, 0, 0, 0},
|
||||
{0, 0, 7, 1, 1, 1, 7, 0, 0}, {0, 0, 7, 1, 1, 6, 7, 0, 0}, {0, 0, 7, 1, 1, 5, 7, 0, 0},
|
||||
{0, 0, 0, 7, 7, 7, 0, 0, 0}, {0, 0, 0, 1, 1, 1, 0, 0, 0}, {0, 0, 0, 1, 1, 1, 0, 0, 0},},
|
||||
{{0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 7, 7, 7, 0, 0, 0},
|
||||
{0, 0, 7, 1, 6, 5, 7, 0, 0}, {0, 0, 7, 11, 1, 1, 7, 0, 0}, {0, 0, 7, 1, 1, 1, 7, 0, 0},
|
||||
{0, 0, 0, 7, 8, 7, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0},},
|
||||
{{0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 7, 7, 7, 0, 0, 0},
|
||||
{0, 0, 7, 5, 1, 1, 7, 0, 0}, {0, 0, 7, 6, 1, 1, 7, 0, 0}, {0, 0, 7, 1, 1, 1, 7, 0, 0},
|
||||
{0, 0, 0, 7, 7, 7, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0},},
|
||||
{{0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 7, 8, 7, 0, 0, 0},
|
||||
{0, 0, 7, 1, 1, 1, 7, 0, 0}, {0, 0, 7, 1, 1, 11, 7, 0, 0}, {0, 0, 7, 5, 6, 1, 7, 0, 0},
|
||||
{0, 0, 0, 7, 7, 7, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0},},
|
||||
{{0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 7, 7, 7, 0, 0, 0},
|
||||
{0, 0, 7, 1, 1, 1, 7, 0, 0}, {0, 0, 7, 1, 1, 6, 7, 0, 0}, {0, 0, 7, 1, 1, 5, 7, 0, 0},
|
||||
{0, 0, 0, 7, 7, 7, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0},},
|
||||
{{0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 7, 7, 7, 0, 0, 0},
|
||||
{0, 0, 7, 1, 6, 5, 7, 0, 0}, {0, 0, 7, 11, 1, 1, 7, 0, 0}, {0, 0, 7, 1, 1, 1, 7, 0, 0},
|
||||
{0, 0, 0, 7, 8, 7, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0},},
|
||||
{{0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 7, 7, 7, 0, 0, 0},
|
||||
{0, 0, 7, 5, 1, 1, 7, 0, 0}, {0, 0, 7, 6, 1, 1, 7, 0, 0}, {0, 0, 7, 1, 1, 1, 7, 0, 0},
|
||||
{0, 0, 0, 7, 7, 7, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0},},
|
||||
{{0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 7, 8, 7, 0, 0, 0},
|
||||
{0, 0, 7, 1, 1, 1, 7, 0, 0}, {0, 0, 7, 1, 1, 11, 7, 0, 0}, {0, 0, 7, 5, 6, 1, 7, 0, 0},
|
||||
{0, 0, 0, 7, 7, 7, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0},},
|
||||
{{0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 7, 7, 7, 0, 0, 0},
|
||||
{0, 0, 7, 1, 1, 1, 7, 0, 0}, {0, 0, 7, 1, 1, 6, 7, 0, 0}, {0, 0, 7, 1, 1, 5, 7, 0, 0},
|
||||
{0, 0, 0, 7, 7, 7, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0},},
|
||||
{{0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 7, 7, 7, 0, 0, 0},
|
||||
{0, 0, 7, 1, 6, 5, 7, 0, 0}, {0, 0, 7, 11, 1, 1, 7, 0, 0}, {0, 0, 7, 1, 1, 1, 7, 0, 0},
|
||||
{0, 0, 0, 7, 8, 7, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0},},
|
||||
{{0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 7, 7, 7, 0, 0, 0},
|
||||
{0, 0, 7, 5, 1, 1, 7, 0, 0}, {0, 0, 7, 1, 1, 1, 7, 0, 0}, {0, 0, 7, 1, 1, 1, 7, 0, 0},
|
||||
{0, 0, 0, 7, 7, 7, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0},},
|
||||
{{0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 7, 7, 7, 0, 0, 0}, {0, 0, 7, 2, 2, 2, 7, 0, 0},
|
||||
{0, 7, 1, 1, 1, 1, 2, 7, 0}, {0, 7, 5, 2, 2, 6, 2, 7, 0}, {0, 7, 2, 2, 2, 2, 2, 7, 0},
|
||||
{0, 0, 7, 2, 2, 2, 7, 0, 0}, {0, 0, 0, 7, 7, 7, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0},},
|
||||
{{0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 7, 7, 7, 0, 0, 0}, {0, 0, 7, 3, 3, 3, 7, 0, 0},
|
||||
{0, 7, 1, 1, 1, 1, 3, 7, 0}, {0, 7, 1, 1, 1, 1, 3, 7, 0}, {0, 7, 1, 1, 10, 1, 3, 7, 0},
|
||||
{0, 0, 7, 1, 1, 1, 7, 0, 0}, {0, 0, 0, 7, 7, 7, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0},},
|
||||
{{0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 7, 8, 7, 0, 0, 0}, {0, 0, 7, 11, 1, 11, 7, 0, 0},
|
||||
{0, 7, 1, 1, 1, 1, 3, 7, 0}, {0, 8, 1, 1, 1, 1, 3, 7, 0}, {0, 7, 1, 1, 1, 1, 3, 7, 0},
|
||||
{0, 0, 7, 11, 1, 11, 7, 0, 0}, {0, 0, 0, 7, 8, 7, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0},},
|
||||
{{0, 0, 0, 4, 4, 4, 0, 0, 0}, {0, 0, 4, 7, 7, 7, 4, 0, 0}, {0, 4, 7, 1, 1, 1, 7, 4, 0},
|
||||
{4, 7, 1, 1, 1, 1, 3, 7, 4}, {4, 7, 1, 1, 1, 1, 3, 7, 4}, {4, 7, 1, 1, 1, 1, 12, 7, 4},
|
||||
{0, 4, 7, 1, 1, 1, 7, 4, 0}, {0, 0, 4, 7, 7, 7, 4, 0, 0}, {0, 0, 0, 4, 4, 4, 0, 0, 0},},
|
||||
{{0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 4, 4, 4, 0, 0, 0}, {0, 0, 4, 1, 1, 1, 4, 0, 0},
|
||||
{0, 4, 1, 1, 1, 1, 1, 4, 0}, {0, 4, 1, 1, 1, 1, 1, 4, 0}, {0, 4, 1, 1, 1, 1, 1, 4, 0},
|
||||
{0, 0, 4, 1, 1, 1, 4, 0, 0}, {0, 0, 0, 4, 4, 4, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0},},
|
||||
{{0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 4, 4, 4, 0, 0, 0},
|
||||
{0, 0, 4, 1, 1, 1, 4, 0, 0}, {0, 0, 4, 1, 1, 1, 4, 0, 0}, {0, 0, 4, 1, 1, 1, 4, 0, 0},
|
||||
{0, 0, 0, 4, 4, 4, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0},},
|
||||
{{0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 4, 0, 0, 0, 0},
|
||||
{0, 0, 0, 4, 1, 4, 0, 0, 0}, {0, 0, 4, 1, 1, 1, 4, 0, 0}, {0, 0, 0, 4, 1, 4, 0, 0, 0},
|
||||
{0, 0, 0, 0, 4, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0},},
|
||||
{{0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
{0, 0, 0, 4, 4, 4, 0, 0, 0}, {0, 0, 0, 4, 1, 4, 0, 0, 0}, {0, 0, 0, 4, 4, 4, 0, 0, 0},
|
||||
{0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0},},
|
||||
{{0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
{0, 0, 0, 0, 4, 0, 0, 0, 0}, {0, 0, 0, 4, 1, 4, 0, 0, 0}, {0, 0, 0, 0, 4, 0, 0, 0, 0},
|
||||
{0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0},},
|
||||
{{0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
{0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 4, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
{0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0},},
|
||||
{{0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
{0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 4, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
{0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0},}};
|
||||
|
||||
/**
|
||||
* 3D matrix of integers representing the different blocks which make up the wizard tower. The blocks corresponding
|
||||
* to each integer are as follows (note that some blocks change depending on the biome):
|
||||
* <p>
|
||||
* 0 Nothing (keep existing block)<br>
|
||||
* 1 Air (remove existing block)<br>
|
||||
* 2 Floor (planks)<br>
|
||||
* 3 Bookshelf<br>
|
||||
* 4 Roof (stained clay)<br>
|
||||
* 5 Floor slab (lower half)<br>
|
||||
* 6 Floor slab (upper half)<br>
|
||||
* 7 Wall (cobblestone by default)<br>
|
||||
* 8 Glass pane<br>
|
||||
* 9 Door (metadata is handled by the built-in vanilla method)<br>
|
||||
* 10 Arcane workbench<br>
|
||||
* 11 Torch (metadata is handled separately depending on adjacent blocks)<br>
|
||||
* 12 Chest (only generates if the wizard is evil, otherwise places a bookshelf instead)
|
||||
*/
|
||||
private static final int[][][] towerBlueprintDouble = {
|
||||
// x is horizontal, z is vertical, y is layers
|
||||
{{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 2, 2, 2, 0, 0, 0, 0, 0},
|
||||
{0, 0, 0, 0, 0, 2, 2, 2, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 2, 2, 2, 0, 0, 0, 0, 0},
|
||||
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},},
|
||||
{{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
{0, 0, 0, 0, 0, 7, 7, 7, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 7, 1, 6, 5, 7, 0, 0, 0, 0},
|
||||
{0, 0, 0, 0, 7, 1, 1, 1, 7, 0, 0, 0, 0}, {0, 0, 0, 0, 7, 1, 1, 1, 7, 0, 0, 0, 0},
|
||||
{0, 0, 0, 0, 0, 7, 9, 7, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0},
|
||||
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},},
|
||||
{{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
{0, 0, 0, 0, 0, 7, 7, 7, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 7, 5, 1, 1, 7, 0, 0, 0, 0},
|
||||
{0, 0, 0, 0, 7, 6, 1, 1, 7, 0, 0, 0, 0}, {0, 0, 0, 0, 7, 1, 1, 1, 7, 0, 0, 0, 0},
|
||||
{0, 0, 0, 0, 0, 7, 0, 7, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 11, 1, 11, 0, 0, 0, 0, 0},
|
||||
{0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},},
|
||||
{{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
{0, 0, 0, 0, 0, 7, 8, 7, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 7, 1, 1, 1, 7, 0, 0, 0, 0},
|
||||
{0, 0, 0, 0, 7, 1, 1, 11, 7, 0, 0, 0, 0}, {0, 0, 0, 0, 7, 5, 6, 1, 7, 0, 0, 0, 0},
|
||||
{0, 0, 0, 0, 0, 7, 7, 7, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0},
|
||||
{0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},},
|
||||
{{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
{0, 0, 0, 0, 0, 7, 7, 7, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 7, 1, 1, 1, 7, 0, 0, 0, 0},
|
||||
{0, 0, 0, 0, 7, 1, 1, 6, 7, 7, 7, 0, 0}, {0, 0, 0, 0, 7, 1, 1, 5, 7, 0, 0, 0, 0},
|
||||
{0, 0, 0, 0, 0, 7, 7, 7, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0},
|
||||
{0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},},
|
||||
{{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
{0, 0, 0, 0, 0, 7, 7, 7, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 7, 1, 6, 5, 7, 7, 7, 0, 0},
|
||||
{0, 0, 0, 0, 7, 11, 1, 1, 1, 1, 1, 7, 0}, {0, 0, 0, 0, 7, 1, 1, 1, 7, 7, 7, 0, 0},
|
||||
{0, 0, 0, 0, 0, 7, 8, 7, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},},
|
||||
{{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
{0, 0, 0, 0, 0, 7, 7, 7, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 7, 5, 1, 1, 7, 7, 8, 7, 0},
|
||||
{0, 0, 0, 0, 7, 6, 1, 1, 1, 1, 1, 8, 0}, {0, 0, 0, 0, 7, 1, 1, 1, 7, 7, 8, 7, 0},
|
||||
{0, 0, 0, 0, 0, 7, 7, 7, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},},
|
||||
{{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
{0, 0, 0, 0, 0, 7, 8, 7, 0, 4, 4, 4, 0}, {0, 0, 0, 0, 7, 1, 1, 1, 7, 7, 7, 7, 4},
|
||||
{0, 0, 0, 0, 7, 1, 1, 11, 7, 1, 1, 7, 4}, {0, 0, 0, 0, 7, 5, 6, 1, 7, 7, 7, 7, 4},
|
||||
{0, 0, 0, 0, 0, 7, 7, 7, 0, 4, 4, 4, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},},
|
||||
{{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
{0, 0, 0, 0, 0, 7, 7, 7, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 7, 1, 1, 1, 7, 4, 4, 4, 0},
|
||||
{0, 0, 0, 0, 7, 1, 1, 6, 7, 4, 1, 4, 0}, {0, 0, 0, 0, 7, 1, 1, 5, 7, 4, 4, 4, 0},
|
||||
{0, 0, 0, 0, 0, 7, 7, 7, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0},
|
||||
{0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},},
|
||||
{{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
{0, 0, 0, 0, 0, 7, 7, 7, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 7, 1, 6, 5, 7, 0, 4, 0, 0},
|
||||
{0, 0, 0, 0, 7, 11, 1, 1, 7, 4, 1, 4, 0}, {0, 0, 0, 0, 7, 1, 1, 1, 7, 0, 4, 0, 0},
|
||||
{0, 0, 0, 0, 0, 7, 8, 7, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},},
|
||||
{{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
{0, 0, 0, 0, 0, 7, 7, 7, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 7, 5, 1, 1, 7, 0, 0, 0, 0},
|
||||
{0, 0, 0, 0, 7, 1, 1, 1, 7, 0, 4, 0, 0}, {0, 0, 0, 0, 7, 1, 1, 1, 7, 0, 0, 0, 0},
|
||||
{0, 0, 0, 0, 0, 7, 7, 7, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},},
|
||||
{{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 7, 7, 7, 0, 0, 0, 0, 0},
|
||||
{0, 0, 0, 0, 7, 2, 2, 2, 7, 0, 0, 0, 0}, {0, 0, 0, 7, 1, 1, 1, 1, 2, 7, 0, 0, 0},
|
||||
{0, 0, 0, 7, 5, 2, 2, 6, 2, 7, 4, 0, 0}, {0, 0, 0, 7, 2, 2, 2, 2, 2, 7, 0, 0, 0},
|
||||
{0, 0, 0, 0, 7, 2, 2, 2, 7, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 7, 7, 7, 0, 0, 0, 0, 0},
|
||||
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},},
|
||||
{{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 7, 7, 7, 0, 0, 0, 0, 0},
|
||||
{0, 0, 0, 0, 7, 3, 3, 3, 7, 0, 0, 0, 0}, {0, 0, 0, 7, 1, 1, 1, 1, 3, 7, 0, 0, 0},
|
||||
{0, 0, 0, 7, 1, 1, 1, 1, 3, 7, 0, 0, 0}, {0, 0, 0, 7, 1, 1, 10, 1, 3, 7, 0, 0, 0},
|
||||
{0, 0, 0, 0, 7, 1, 1, 1, 7, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 7, 7, 7, 0, 0, 0, 0, 0},
|
||||
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},},
|
||||
{{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 7, 8, 7, 0, 0, 0, 0, 0},
|
||||
{0, 0, 0, 0, 7, 11, 1, 11, 7, 0, 0, 0, 0}, {0, 0, 0, 7, 1, 1, 1, 1, 3, 7, 0, 0, 0},
|
||||
{0, 0, 0, 8, 1, 1, 1, 1, 3, 7, 0, 0, 0}, {0, 0, 0, 7, 1, 1, 1, 1, 3, 7, 0, 0, 0},
|
||||
{0, 0, 0, 0, 7, 11, 1, 11, 7, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 7, 8, 7, 0, 0, 0, 0, 0},
|
||||
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},},
|
||||
{{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
{0, 0, 0, 0, 0, 4, 4, 4, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 4, 7, 7, 7, 4, 0, 0, 0, 0},
|
||||
{0, 0, 0, 4, 7, 1, 1, 1, 7, 4, 0, 0, 0}, {0, 0, 4, 7, 1, 1, 1, 1, 3, 7, 4, 0, 0},
|
||||
{0, 0, 4, 7, 1, 1, 1, 1, 3, 7, 4, 0, 0}, {0, 0, 4, 7, 1, 1, 1, 1, 12, 7, 4, 0, 0},
|
||||
{0, 0, 0, 4, 7, 1, 1, 1, 7, 4, 0, 0, 0}, {0, 0, 0, 0, 4, 7, 7, 7, 4, 0, 0, 0, 0},
|
||||
{0, 0, 0, 0, 0, 4, 4, 4, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},},
|
||||
{{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 4, 4, 4, 0, 0, 0, 0, 0},
|
||||
{0, 0, 0, 0, 4, 1, 1, 1, 4, 0, 0, 0, 0}, {0, 0, 0, 4, 1, 1, 1, 1, 1, 4, 0, 0, 0},
|
||||
{0, 0, 0, 4, 1, 1, 1, 1, 1, 4, 0, 0, 0}, {0, 0, 0, 4, 1, 1, 1, 1, 1, 4, 0, 0, 0},
|
||||
{0, 0, 0, 0, 4, 1, 1, 1, 4, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 4, 4, 4, 0, 0, 0, 0, 0},
|
||||
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},},
|
||||
{{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
{0, 0, 0, 0, 0, 4, 4, 4, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 4, 1, 1, 1, 4, 0, 0, 0, 0},
|
||||
{0, 0, 0, 0, 4, 1, 1, 1, 4, 0, 0, 0, 0}, {0, 0, 0, 0, 4, 1, 1, 1, 4, 0, 0, 0, 0},
|
||||
{0, 0, 0, 0, 0, 4, 4, 4, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},},
|
||||
{{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
{0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 4, 1, 4, 0, 0, 0, 0, 0},
|
||||
{0, 0, 0, 0, 4, 1, 1, 1, 4, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 4, 1, 4, 0, 0, 0, 0, 0},
|
||||
{0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},},
|
||||
{{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 4, 4, 4, 0, 0, 0, 0, 0},
|
||||
{0, 0, 0, 0, 0, 4, 1, 4, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 4, 4, 4, 0, 0, 0, 0, 0},
|
||||
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},},
|
||||
{{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0},
|
||||
{0, 0, 0, 0, 0, 4, 1, 4, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0},
|
||||
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},},
|
||||
{{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
{0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},},
|
||||
{{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
{0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},}};
|
||||
|
||||
}
|
||||
@@ -1,61 +0,0 @@
|
||||
package electroblob.wizardry.advancement;
|
||||
|
||||
import electroblob.wizardry.Wizardry;
|
||||
import net.minecraft.advancements.Advancement;
|
||||
import net.minecraft.advancements.AdvancementManager;
|
||||
import net.minecraft.advancements.AdvancementProgress;
|
||||
import net.minecraft.entity.player.EntityPlayer;
|
||||
import net.minecraft.entity.player.EntityPlayerMP;
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
|
||||
/**
|
||||
* This is a provisory class for the transition to 1.12
|
||||
* It contains an enum with all the advancements of this mod
|
||||
* and an helper to grant advancements on the server side only (like the command)
|
||||
* @author Corail31
|
||||
* @since Wizardry 4.1
|
||||
*/
|
||||
public class AdvancementHelper {
|
||||
public enum EnumAdvancement {
|
||||
crystal,
|
||||
arcane_initiate,
|
||||
apprentice,
|
||||
master,
|
||||
all_spells,
|
||||
wizard_trade,
|
||||
buy_master_spell,
|
||||
freeze_blaze,
|
||||
charge_creeper,
|
||||
frankenstein,
|
||||
special_upgrade,
|
||||
craft_flask,
|
||||
elemental,
|
||||
armour_set,
|
||||
legendary,
|
||||
self_destruct,
|
||||
pig_tornado,
|
||||
jam_wizard,
|
||||
slime_skeleton,
|
||||
anger_wizard,
|
||||
defeat_evil_wizard,
|
||||
max_out_wand,
|
||||
element_master,
|
||||
identify_spell;
|
||||
}
|
||||
|
||||
public static boolean grantAdvancement(EntityPlayer player, EnumAdvancement advancementName) {
|
||||
if (player == null) { return false; }
|
||||
if (player.world.isRemote) { return true; }
|
||||
EntityPlayerMP player_mp = player.getServer().getPlayerList().getPlayerByUUID(player.getUniqueID());
|
||||
AdvancementManager am = player_mp.getServerWorld().getAdvancementManager();
|
||||
Advancement advancement = am.getAdvancement(new ResourceLocation(Wizardry.MODID, advancementName.name()));
|
||||
if (advancement == null) { return false; }
|
||||
AdvancementProgress advancementprogress = player_mp.getAdvancements().getProgress(advancement);
|
||||
if (!advancementprogress.isDone()) {
|
||||
for (String criteria : advancementprogress.getRemaningCriteria()) {
|
||||
player_mp.getAdvancements().grantCriterion(advancement, criteria);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,135 @@
|
||||
package electroblob.wizardry.advancement;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
import com.google.common.collect.Maps;
|
||||
import com.google.common.collect.Sets;
|
||||
import com.google.gson.JsonDeserializationContext;
|
||||
import com.google.gson.JsonObject;
|
||||
import net.minecraft.advancements.ICriterionTrigger;
|
||||
import net.minecraft.advancements.PlayerAdvancements;
|
||||
import net.minecraft.advancements.critereon.AbstractCriterionInstance;
|
||||
import net.minecraft.advancements.critereon.ItemPredicate;
|
||||
import net.minecraft.entity.player.EntityPlayerMP;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
/** Advancement trigger for things done in the arcane workbench. The majority of any
|
||||
* ICriterionTrigger class is just boilerplate, and this is no exception. */
|
||||
public class ArcaneWorkbenchTrigger implements ICriterionTrigger<ArcaneWorkbenchTrigger.Instance> {
|
||||
|
||||
private final ResourceLocation id;
|
||||
private final Map<PlayerAdvancements, ArcaneWorkbenchTrigger.Listeners> listeners = Maps.newHashMap();
|
||||
|
||||
public ArcaneWorkbenchTrigger(ResourceLocation id){
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public ResourceLocation getId(){
|
||||
return this.id;
|
||||
}
|
||||
|
||||
public void addListener(PlayerAdvancements advancements, Listener<ArcaneWorkbenchTrigger.Instance> listener){
|
||||
|
||||
ArcaneWorkbenchTrigger.Listeners listeners = this.listeners.get(advancements);
|
||||
|
||||
if(listeners == null){
|
||||
listeners = new ArcaneWorkbenchTrigger.Listeners(advancements);
|
||||
this.listeners.put(advancements, listeners);
|
||||
}
|
||||
|
||||
listeners.add(listener);
|
||||
}
|
||||
|
||||
public void removeListener(PlayerAdvancements advancements, Listener<ArcaneWorkbenchTrigger.Instance> listener){
|
||||
|
||||
ArcaneWorkbenchTrigger.Listeners listeners = this.listeners.get(advancements);
|
||||
|
||||
if(listeners != null){
|
||||
listeners.remove(listener);
|
||||
|
||||
if(listeners.isEmpty()){
|
||||
this.listeners.remove(advancements);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void removeAllListeners(PlayerAdvancements advancements){
|
||||
this.listeners.remove(advancements);
|
||||
}
|
||||
|
||||
public ArcaneWorkbenchTrigger.Instance deserializeInstance(JsonObject json, JsonDeserializationContext context){
|
||||
return new ArcaneWorkbenchTrigger.Instance(this.id, ItemPredicate.deserialize(json.get("item")));
|
||||
}
|
||||
|
||||
public void trigger(EntityPlayerMP player, ItemStack stack){
|
||||
|
||||
ArcaneWorkbenchTrigger.Listeners listeners = this.listeners.get(player.getAdvancements());
|
||||
|
||||
if(listeners != null){
|
||||
listeners.trigger(stack);
|
||||
}
|
||||
}
|
||||
|
||||
public static class Instance extends AbstractCriterionInstance {
|
||||
|
||||
private final ItemPredicate item;
|
||||
|
||||
public Instance(ResourceLocation criterionIn, ItemPredicate item){
|
||||
super(criterionIn);
|
||||
this.item = item;
|
||||
}
|
||||
|
||||
public boolean test(ItemStack stack){
|
||||
return this.item.test(stack);
|
||||
}
|
||||
}
|
||||
|
||||
static class Listeners {
|
||||
|
||||
private final PlayerAdvancements playerAdvancements;
|
||||
private final Set<Listener<ArcaneWorkbenchTrigger.Instance>> listeners = Sets.newHashSet();
|
||||
|
||||
public Listeners(PlayerAdvancements advancements){
|
||||
this.playerAdvancements = advancements;
|
||||
}
|
||||
|
||||
public boolean isEmpty(){
|
||||
return this.listeners.isEmpty();
|
||||
}
|
||||
|
||||
public void add(Listener<ArcaneWorkbenchTrigger.Instance> listener){
|
||||
this.listeners.add(listener);
|
||||
}
|
||||
|
||||
public void remove(Listener<ArcaneWorkbenchTrigger.Instance> listener){
|
||||
this.listeners.remove(listener);
|
||||
}
|
||||
|
||||
public void trigger(ItemStack stack){
|
||||
|
||||
List<Listener<ArcaneWorkbenchTrigger.Instance>> list = null;
|
||||
|
||||
for(Listener<ArcaneWorkbenchTrigger.Instance> listener : this.listeners){
|
||||
|
||||
if(listener.getCriterionInstance().test(stack)){
|
||||
|
||||
if(list == null){
|
||||
list = Lists.newArrayList();
|
||||
}
|
||||
|
||||
list.add(listener);
|
||||
}
|
||||
}
|
||||
|
||||
if(list != null){
|
||||
for(Listener<ArcaneWorkbenchTrigger.Instance> listener : list){
|
||||
listener.grantCriterion(this.playerAdvancements);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+2
-3
@@ -1,10 +1,9 @@
|
||||
package electroblob.wizardry.util;
|
||||
package electroblob.wizardry.advancement;
|
||||
|
||||
import com.google.common.collect.HashMultimap;
|
||||
import com.google.common.collect.SetMultimap;
|
||||
import com.google.gson.JsonDeserializationContext;
|
||||
import com.google.gson.JsonObject;
|
||||
|
||||
import electroblob.wizardry.Wizardry;
|
||||
import net.minecraft.advancements.ICriterionInstance;
|
||||
import net.minecraft.advancements.ICriterionTrigger;
|
||||
@@ -34,7 +33,7 @@ public class CustomAdvancementTrigger implements ICriterionTrigger<CustomAdvance
|
||||
* advancement to happen.
|
||||
*/
|
||||
public static class Instance extends AbstractCriterionInstance {
|
||||
public Instance(ResourceLocation triggerId) {
|
||||
public Instance(ResourceLocation triggerId) {
|
||||
super(triggerId);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,139 @@
|
||||
package electroblob.wizardry.advancement;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
import com.google.common.collect.Maps;
|
||||
import com.google.common.collect.Sets;
|
||||
import com.google.gson.JsonDeserializationContext;
|
||||
import com.google.gson.JsonObject;
|
||||
import electroblob.wizardry.spell.Spell;
|
||||
import net.minecraft.advancements.ICriterionTrigger;
|
||||
import net.minecraft.advancements.PlayerAdvancements;
|
||||
import net.minecraft.advancements.critereon.AbstractCriterionInstance;
|
||||
import net.minecraft.advancements.critereon.ItemPredicate;
|
||||
import net.minecraft.entity.player.EntityPlayerMP;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
/** Advancement trigger which is triggered when a spell is cast. The majority of any
|
||||
* ICriterionTrigger class is just boilerplate, and this is no exception. */
|
||||
public class SpellCastTrigger implements ICriterionTrigger<SpellCastTrigger.Instance> {
|
||||
|
||||
private final ResourceLocation id;
|
||||
private final Map<PlayerAdvancements, SpellCastTrigger.Listeners> listeners = Maps.newHashMap();
|
||||
|
||||
public SpellCastTrigger(ResourceLocation id){
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public ResourceLocation getId(){
|
||||
return this.id;
|
||||
}
|
||||
|
||||
public void addListener(PlayerAdvancements advancements, Listener<SpellCastTrigger.Instance> listener){
|
||||
|
||||
SpellCastTrigger.Listeners listeners = this.listeners.get(advancements);
|
||||
|
||||
if(listeners == null){
|
||||
listeners = new SpellCastTrigger.Listeners(advancements);
|
||||
this.listeners.put(advancements, listeners);
|
||||
}
|
||||
|
||||
listeners.add(listener);
|
||||
}
|
||||
|
||||
public void removeListener(PlayerAdvancements advancements, Listener<SpellCastTrigger.Instance> listener){
|
||||
|
||||
SpellCastTrigger.Listeners listeners = this.listeners.get(advancements);
|
||||
|
||||
if(listeners != null){
|
||||
listeners.remove(listener);
|
||||
|
||||
if(listeners.isEmpty()){
|
||||
this.listeners.remove(advancements);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void removeAllListeners(PlayerAdvancements advancements){
|
||||
this.listeners.remove(advancements);
|
||||
}
|
||||
|
||||
public SpellCastTrigger.Instance deserializeInstance(JsonObject json, JsonDeserializationContext context){
|
||||
return new SpellCastTrigger.Instance(this.id, SpellPredicate.deserialize(json.get("spell")),
|
||||
ItemPredicate.deserialize(json.get("item")));
|
||||
}
|
||||
|
||||
public void trigger(EntityPlayerMP player, Spell spell, ItemStack stack){
|
||||
|
||||
SpellCastTrigger.Listeners listeners = this.listeners.get(player.getAdvancements());
|
||||
|
||||
if(listeners != null){
|
||||
listeners.trigger(spell, stack);
|
||||
}
|
||||
}
|
||||
|
||||
public static class Instance extends AbstractCriterionInstance {
|
||||
|
||||
private final SpellPredicate spell;
|
||||
private final ItemPredicate item;
|
||||
|
||||
public Instance(ResourceLocation criterion, SpellPredicate spell, ItemPredicate item){
|
||||
super(criterion);
|
||||
this.spell = spell;
|
||||
this.item = item;
|
||||
}
|
||||
|
||||
public boolean test(Spell spell, ItemStack stack){
|
||||
return this.spell.test(spell) && item.test(stack);
|
||||
}
|
||||
}
|
||||
|
||||
static class Listeners {
|
||||
|
||||
private final PlayerAdvancements playerAdvancements;
|
||||
private final Set<Listener<SpellCastTrigger.Instance>> listeners = Sets.newHashSet();
|
||||
|
||||
public Listeners(PlayerAdvancements advancements){
|
||||
this.playerAdvancements = advancements;
|
||||
}
|
||||
|
||||
public boolean isEmpty(){
|
||||
return this.listeners.isEmpty();
|
||||
}
|
||||
|
||||
public void add(Listener<SpellCastTrigger.Instance> listener){
|
||||
this.listeners.add(listener);
|
||||
}
|
||||
|
||||
public void remove(Listener<SpellCastTrigger.Instance> listener){
|
||||
this.listeners.remove(listener);
|
||||
}
|
||||
|
||||
public void trigger(Spell spell, ItemStack stack){
|
||||
|
||||
List<Listener<SpellCastTrigger.Instance>> list = null;
|
||||
|
||||
for(Listener<SpellCastTrigger.Instance> listener : this.listeners){
|
||||
|
||||
if(listener.getCriterionInstance().test(spell, stack)){
|
||||
|
||||
if(list == null){
|
||||
list = Lists.newArrayList();
|
||||
}
|
||||
|
||||
list.add(listener);
|
||||
}
|
||||
}
|
||||
|
||||
if(list != null){
|
||||
for(Listener<SpellCastTrigger.Instance> listener : list){
|
||||
listener.grantCriterion(this.playerAdvancements);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,143 @@
|
||||
package electroblob.wizardry.advancement;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
import com.google.common.collect.Maps;
|
||||
import com.google.common.collect.Sets;
|
||||
import com.google.gson.JsonDeserializationContext;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.google.gson.JsonSyntaxException;
|
||||
import electroblob.wizardry.event.DiscoverSpellEvent;
|
||||
import electroblob.wizardry.spell.Spell;
|
||||
import net.minecraft.advancements.ICriterionTrigger;
|
||||
import net.minecraft.advancements.PlayerAdvancements;
|
||||
import net.minecraft.advancements.critereon.AbstractCriterionInstance;
|
||||
import net.minecraft.entity.player.EntityPlayerMP;
|
||||
import net.minecraft.util.JsonUtils;
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
/** Advancement trigger that is triggered when a spell is discovered. The majority of any
|
||||
* ICriterionTrigger class is just boilerplate, and this is no exception. */
|
||||
public class SpellDiscoveryTrigger implements ICriterionTrigger<SpellDiscoveryTrigger.Instance> {
|
||||
|
||||
private final ResourceLocation id;
|
||||
private final Map<PlayerAdvancements, SpellDiscoveryTrigger.Listeners> listeners = Maps.newHashMap();
|
||||
|
||||
public SpellDiscoveryTrigger(ResourceLocation id){
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public ResourceLocation getId(){
|
||||
return this.id;
|
||||
}
|
||||
|
||||
public void addListener(PlayerAdvancements advancements, Listener<SpellDiscoveryTrigger.Instance> listener){
|
||||
|
||||
SpellDiscoveryTrigger.Listeners listeners = this.listeners.get(advancements);
|
||||
|
||||
if(listeners == null){
|
||||
listeners = new SpellDiscoveryTrigger.Listeners(advancements);
|
||||
this.listeners.put(advancements, listeners);
|
||||
}
|
||||
|
||||
listeners.add(listener);
|
||||
}
|
||||
|
||||
public void removeListener(PlayerAdvancements advancements, Listener<SpellDiscoveryTrigger.Instance> listener){
|
||||
|
||||
SpellDiscoveryTrigger.Listeners listeners = this.listeners.get(advancements);
|
||||
|
||||
if(listeners != null){
|
||||
listeners.remove(listener);
|
||||
|
||||
if(listeners.isEmpty()){
|
||||
this.listeners.remove(advancements);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void removeAllListeners(PlayerAdvancements advancements){
|
||||
this.listeners.remove(advancements);
|
||||
}
|
||||
|
||||
public SpellDiscoveryTrigger.Instance deserializeInstance(JsonObject json, JsonDeserializationContext context){
|
||||
|
||||
String s = JsonUtils.getString(json, "source");
|
||||
DiscoverSpellEvent.Source source = DiscoverSpellEvent.Source.byName(s);
|
||||
if(source == null) throw new JsonSyntaxException("No such spell discovery source: " + s);
|
||||
return new SpellDiscoveryTrigger.Instance(this.id, SpellPredicate.deserialize(json.get("spell")), source);
|
||||
}
|
||||
|
||||
public void trigger(EntityPlayerMP player, Spell spell, DiscoverSpellEvent.Source source){
|
||||
|
||||
SpellDiscoveryTrigger.Listeners listeners = this.listeners.get(player.getAdvancements());
|
||||
|
||||
if(listeners != null){
|
||||
listeners.trigger(spell, source);
|
||||
}
|
||||
}
|
||||
|
||||
public static class Instance extends AbstractCriterionInstance {
|
||||
|
||||
private final SpellPredicate spell;
|
||||
private final DiscoverSpellEvent.Source source;
|
||||
|
||||
public Instance(ResourceLocation criterion, SpellPredicate spell, DiscoverSpellEvent.Source source){
|
||||
super(criterion);
|
||||
this.spell = spell;
|
||||
this.source = source;
|
||||
}
|
||||
|
||||
public boolean test(Spell spell, DiscoverSpellEvent.Source source){
|
||||
return this.spell.test(spell) && source == this.source;
|
||||
}
|
||||
}
|
||||
|
||||
static class Listeners {
|
||||
|
||||
private final PlayerAdvancements playerAdvancements;
|
||||
private final Set<Listener<SpellDiscoveryTrigger.Instance>> listeners = Sets.newHashSet();
|
||||
|
||||
public Listeners(PlayerAdvancements advancements){
|
||||
this.playerAdvancements = advancements;
|
||||
}
|
||||
|
||||
public boolean isEmpty(){
|
||||
return this.listeners.isEmpty();
|
||||
}
|
||||
|
||||
public void add(Listener<SpellDiscoveryTrigger.Instance> listener){
|
||||
this.listeners.add(listener);
|
||||
}
|
||||
|
||||
public void remove(Listener<SpellDiscoveryTrigger.Instance> listener){
|
||||
this.listeners.remove(listener);
|
||||
}
|
||||
|
||||
public void trigger(Spell spell, DiscoverSpellEvent.Source source){
|
||||
|
||||
List<Listener<SpellDiscoveryTrigger.Instance>> list = null;
|
||||
|
||||
for(Listener<SpellDiscoveryTrigger.Instance> listener : this.listeners){
|
||||
|
||||
if(listener.getCriterionInstance().test(spell, source)){
|
||||
|
||||
if(list == null){
|
||||
list = Lists.newArrayList();
|
||||
}
|
||||
|
||||
list.add(listener);
|
||||
}
|
||||
}
|
||||
|
||||
if(list != null){
|
||||
for(Listener<SpellDiscoveryTrigger.Instance> listener : list){
|
||||
listener.grantCriterion(this.playerAdvancements);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,117 @@
|
||||
package electroblob.wizardry.advancement;
|
||||
|
||||
import com.google.common.collect.Streams;
|
||||
import com.google.gson.JsonArray;
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.google.gson.JsonSyntaxException;
|
||||
import electroblob.wizardry.constants.Element;
|
||||
import electroblob.wizardry.constants.Tier;
|
||||
import electroblob.wizardry.spell.Spell;
|
||||
import net.minecraft.util.JsonUtils;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.Arrays;
|
||||
|
||||
/** Predicate used by advancement triggers to match spells. */
|
||||
public class SpellPredicate {
|
||||
|
||||
public static final SpellPredicate ANY = new SpellPredicate();
|
||||
private final Spell spell;
|
||||
private final Tier[] tiers;
|
||||
private final Element[] elements;
|
||||
|
||||
public SpellPredicate(){
|
||||
this.spell = null;
|
||||
this.tiers = Tier.values();
|
||||
this.elements = Element.values();
|
||||
}
|
||||
|
||||
public SpellPredicate(@Nullable Spell spell, Tier[] tiers, Element[] elements){
|
||||
this.spell = spell;
|
||||
this.tiers = tiers;
|
||||
this.elements = elements;
|
||||
}
|
||||
|
||||
public boolean test(Spell spell){
|
||||
|
||||
if(this.spell != null && spell != this.spell){
|
||||
return false;
|
||||
}else if(!Arrays.asList(this.tiers).contains(spell.getTier())){
|
||||
return false;
|
||||
}else if(!Arrays.asList(this.elements).contains(spell.getElement())){
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public static SpellPredicate deserialize(@Nullable JsonElement element){
|
||||
|
||||
if(element != null && !element.isJsonNull()){
|
||||
|
||||
JsonObject jsonobject = JsonUtils.getJsonObject(element, "spell");
|
||||
|
||||
Spell spell = null;
|
||||
|
||||
if(jsonobject.has("spell")){
|
||||
|
||||
String s = JsonUtils.getString(jsonobject, "spell");
|
||||
spell = Spell.get(s);
|
||||
|
||||
if(spell == null){
|
||||
throw new JsonSyntaxException("Unknown spell id '" + s + "'");
|
||||
}
|
||||
}
|
||||
|
||||
Tier[] tiers = Tier.values();
|
||||
|
||||
if(jsonobject.has("tiers")){
|
||||
try{
|
||||
JsonArray array = JsonUtils.getJsonArray(jsonobject, "tiers");
|
||||
tiers = Streams.stream(array)
|
||||
.map(je -> Tier.fromName(JsonUtils.getString(je, "element of array tiers")))
|
||||
.toArray(Tier[]::new);
|
||||
}catch(IllegalArgumentException e){
|
||||
throw new JsonSyntaxException("Incorrect spell predicate value", e);
|
||||
}
|
||||
}
|
||||
|
||||
Element[] elements = Element.values();
|
||||
|
||||
if(jsonobject.has("elements")){
|
||||
try{
|
||||
JsonArray array = JsonUtils.getJsonArray(jsonobject, "elements");
|
||||
elements = Streams.stream(array)
|
||||
.map(je -> Element.fromName(JsonUtils.getString(je, "element of array elements")))
|
||||
.toArray(Element[]::new);
|
||||
}catch(IllegalArgumentException e){
|
||||
throw new JsonSyntaxException("Incorrect spell predicate value", e);
|
||||
}
|
||||
}
|
||||
|
||||
return new SpellPredicate(spell, tiers, elements);
|
||||
|
||||
}else{
|
||||
return ANY;
|
||||
}
|
||||
}
|
||||
|
||||
public static SpellPredicate[] deserializeArray(@Nullable JsonElement element){
|
||||
|
||||
if(element != null && !element.isJsonNull()){
|
||||
|
||||
JsonArray jsonarray = JsonUtils.getJsonArray(element, "spells");
|
||||
SpellPredicate[] predicates = new SpellPredicate[jsonarray.size()];
|
||||
|
||||
for(int i = 0; i < predicates.length; ++i){
|
||||
predicates[i] = deserialize(jsonarray.get(i));
|
||||
}
|
||||
|
||||
return predicates;
|
||||
|
||||
}else{
|
||||
return new SpellPredicate[0];
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,136 @@
|
||||
package electroblob.wizardry.advancement;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
import com.google.common.collect.Maps;
|
||||
import com.google.common.collect.Sets;
|
||||
import com.google.gson.JsonDeserializationContext;
|
||||
import com.google.gson.JsonObject;
|
||||
import electroblob.wizardry.worldgen.WorldGenSurfaceStructure;
|
||||
import net.minecraft.advancements.ICriterionTrigger;
|
||||
import net.minecraft.advancements.PlayerAdvancements;
|
||||
import net.minecraft.advancements.critereon.AbstractCriterionInstance;
|
||||
import net.minecraft.entity.player.EntityPlayerMP;
|
||||
import net.minecraft.util.JsonUtils;
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
import net.minecraft.world.WorldServer;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
/** Copied from PositionTrigger and modified to work with wizardry's structures. The majority of any
|
||||
* ICriterionTrigger class is just boilerplate, and this is no exception. */
|
||||
public class StructureTrigger implements ICriterionTrigger<StructureTrigger.Instance> {
|
||||
|
||||
private final ResourceLocation id;
|
||||
private final Map<PlayerAdvancements, StructureTrigger.Listeners> listeners = Maps.newHashMap();
|
||||
|
||||
public StructureTrigger(ResourceLocation id){
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public ResourceLocation getId(){
|
||||
return this.id;
|
||||
}
|
||||
|
||||
public void addListener(PlayerAdvancements advancements, Listener<StructureTrigger.Instance> listener){
|
||||
|
||||
StructureTrigger.Listeners listeners = this.listeners.get(advancements);
|
||||
|
||||
if(listeners == null){
|
||||
listeners = new StructureTrigger.Listeners(advancements);
|
||||
this.listeners.put(advancements, listeners);
|
||||
}
|
||||
|
||||
listeners.add(listener);
|
||||
}
|
||||
|
||||
public void removeListener(PlayerAdvancements advancements, Listener<StructureTrigger.Instance> listener){
|
||||
|
||||
StructureTrigger.Listeners listeners = this.listeners.get(advancements);
|
||||
|
||||
if(listeners != null){
|
||||
listeners.remove(listener);
|
||||
|
||||
if(listeners.isEmpty()){
|
||||
this.listeners.remove(advancements);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void removeAllListeners(PlayerAdvancements advancements){
|
||||
this.listeners.remove(advancements);
|
||||
}
|
||||
|
||||
public StructureTrigger.Instance deserializeInstance(JsonObject json, JsonDeserializationContext context){
|
||||
return new StructureTrigger.Instance(this.id, JsonUtils.getString(json, "structure_type"));
|
||||
}
|
||||
|
||||
public void trigger(EntityPlayerMP player){
|
||||
|
||||
StructureTrigger.Listeners listeners = this.listeners.get(player.getAdvancements());
|
||||
|
||||
if(listeners != null){
|
||||
listeners.trigger(player.getServerWorld(), player.posX, player.posY, player.posZ);
|
||||
}
|
||||
}
|
||||
|
||||
public static class Instance extends AbstractCriterionInstance {
|
||||
|
||||
private final WorldGenSurfaceStructure structureType;
|
||||
|
||||
public Instance(ResourceLocation criterionIn, String name){
|
||||
super(criterionIn);
|
||||
this.structureType = WorldGenSurfaceStructure.byName(name);
|
||||
}
|
||||
|
||||
public boolean test(WorldServer world, double x, double y, double z){
|
||||
return structureType.isInsideStructure(world, x, y, z);
|
||||
}
|
||||
}
|
||||
|
||||
static class Listeners {
|
||||
|
||||
private final PlayerAdvancements playerAdvancements;
|
||||
private final Set<Listener<StructureTrigger.Instance>> listeners = Sets.newHashSet();
|
||||
|
||||
public Listeners(PlayerAdvancements advancements){
|
||||
this.playerAdvancements = advancements;
|
||||
}
|
||||
|
||||
public boolean isEmpty(){
|
||||
return this.listeners.isEmpty();
|
||||
}
|
||||
|
||||
public void add(Listener<StructureTrigger.Instance> listener){
|
||||
this.listeners.add(listener);
|
||||
}
|
||||
|
||||
public void remove(Listener<StructureTrigger.Instance> listener){
|
||||
this.listeners.remove(listener);
|
||||
}
|
||||
|
||||
public void trigger(WorldServer world, double x, double y, double z){
|
||||
|
||||
List<Listener<StructureTrigger.Instance>> list = null;
|
||||
|
||||
for(Listener<StructureTrigger.Instance> listener : this.listeners){
|
||||
|
||||
if(listener.getCriterionInstance().test(world, x, y, z)){
|
||||
|
||||
if(list == null){
|
||||
list = Lists.newArrayList();
|
||||
}
|
||||
|
||||
list.add(listener);
|
||||
}
|
||||
}
|
||||
|
||||
if(list != null){
|
||||
for(Listener<StructureTrigger.Instance> listener : list){
|
||||
listener.grantCriterion(this.playerAdvancements);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,96 @@
|
||||
package electroblob.wizardry.api;
|
||||
|
||||
import electroblob.wizardry.constants.Element;
|
||||
import electroblob.wizardry.constants.SpellType;
|
||||
import electroblob.wizardry.constants.Tier;
|
||||
import electroblob.wizardry.util.SpellProperties;
|
||||
import net.minecraft.util.text.Style;
|
||||
import net.minecraftforge.common.util.EnumHelper;
|
||||
|
||||
/**
|
||||
* This class contains methods similar to those in {@link EnumHelper} specific to wizardry's enum types.
|
||||
*
|
||||
* @author Electroblob
|
||||
* @since Wizardry 4.2
|
||||
*/
|
||||
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};
|
||||
private static final Class[] SPELL_TYPE_ARGUMENTS = new Class[]{String.class};
|
||||
private static final Class[] SPELL_CONTEXT_ARGUMENTS = new Class[]{String.class};
|
||||
|
||||
/**
|
||||
* Wrapper for the generic method {@link EnumHelper#addEnum(Class, String, Class[], Object...)} which is
|
||||
* specifically for adding new tiers. Use this method in preference to the generic one in case the constructor
|
||||
* parameters change for {@code Tier}.
|
||||
* <p></p>
|
||||
* As of version 4.2, wizardry now has partial support for externally-added tiers; you'll need to do some of the
|
||||
* legwork yourself though.
|
||||
*
|
||||
* @param codeName The name of the enum constant in the code. This will be returned if you call toString() on the
|
||||
* resulting enum constant; other than that it doesn't really make much difference.
|
||||
* @param maxCharge The maximum charge for wands of this tier.
|
||||
* @param upgradeLimit The maximum total number of special upgrades that can be applied to wands of this tier.
|
||||
* @param weight The weight of this tier in the standard weighting.
|
||||
* @param colour The colour of text associated with this tier, as a style object.
|
||||
* @param name The unlocalised name of this tier, as used in translation keys.
|
||||
* @return The resulting {@code Tier} enum constant.
|
||||
*/
|
||||
public static Tier addTier(String codeName, int maxCharge, int upgradeLimit, int weight, Style colour, String name){
|
||||
return EnumHelper.addEnum(Tier.class, codeName, TIER_ARGUMENTS, maxCharge, upgradeLimit, weight, colour, name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrapper for the generic method {@link EnumHelper#addEnum(Class, String, Class[], Object...)} which is
|
||||
* specifically for adding new elements. Use this method in preference to the generic one in case the constructor
|
||||
* parameters change for {@code Element}.
|
||||
* <p></p>
|
||||
* As of version 4.2, wizardry now has full support for externally-added elements.
|
||||
*
|
||||
* @param codeName The name of the enum constant in the code. This will be returned if you call toString() on the
|
||||
* resulting enum constant; other than that it doesn't really make much difference.
|
||||
* @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.
|
||||
* @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){
|
||||
return EnumHelper.addEnum(Element.class, codeName, ELEMENT_ARGUMENTS, colour, name, modID);
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrapper for the generic method {@link EnumHelper#addEnum(Class, String, Class[], Object...)} which is
|
||||
* specifically for adding new spell types. Use this method in preference to the generic one in case the constructor
|
||||
* parameters change for {@code SpellType}.
|
||||
* <p></p>
|
||||
* As of version 4.2, wizardry now has full support for externally-added spell types.
|
||||
*
|
||||
* @param codeName The name of the enum constant in the code. This will be returned if you call toString() on the
|
||||
* resulting enum constant; other than that it doesn't really make much difference.
|
||||
* @param name The unlocalised name of this spell type, as used in translation keys.
|
||||
* @return The resulting {@code SpellType} enum constant.
|
||||
*/
|
||||
public static SpellType addSpellType(String codeName, String name){
|
||||
return EnumHelper.addEnum(SpellType.class, codeName, SPELL_TYPE_ARGUMENTS, name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrapper for the generic method {@link EnumHelper#addEnum(Class, String, Class[], Object...)} which is
|
||||
* specifically for adding new spell contexts (for use in spell property JSON files). Use this method in preference
|
||||
* to the generic one in case the constructor parameters change for {@code Context}.
|
||||
*
|
||||
* @param codeName The name of the enum constant in the code. This will be returned if you call toString() on the
|
||||
* resulting enum constant; other than that it doesn't really make much difference.
|
||||
* @param name The identifier for this spell context, as used in the JSON file.
|
||||
* @return The resulting {@code Context} enum constant.
|
||||
*/
|
||||
public static SpellProperties.Context addSpellContext(String codeName, String name){
|
||||
return EnumHelper.addEnum(SpellProperties.Context.class, codeName, SPELL_CONTEXT_ARGUMENTS, name);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,17 +1,14 @@
|
||||
package electroblob.wizardry.block;
|
||||
|
||||
import java.util.Random;
|
||||
|
||||
import electroblob.wizardry.Wizardry;
|
||||
import electroblob.wizardry.WizardryGuiHandler;
|
||||
import electroblob.wizardry.tileentity.TileEntityArcaneWorkbench;
|
||||
import net.minecraft.block.BlockContainer;
|
||||
import net.minecraft.block.material.Material;
|
||||
import net.minecraft.block.state.BlockFaceShape;
|
||||
import net.minecraft.block.state.IBlockState;
|
||||
import net.minecraft.entity.item.EntityItem;
|
||||
import net.minecraft.entity.player.EntityPlayer;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.nbt.NBTTagCompound;
|
||||
import net.minecraft.inventory.InventoryHelper;
|
||||
import net.minecraft.tileentity.TileEntity;
|
||||
import net.minecraft.util.EnumBlockRenderType;
|
||||
import net.minecraft.util.EnumFacing;
|
||||
@@ -55,6 +52,16 @@ public class BlockArcaneWorkbench extends BlockContainer {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isFullCube(IBlockState state){
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BlockFaceShape getBlockFaceShape(IBlockAccess world, IBlockState state, BlockPos pos, EnumFacing face){
|
||||
return face == EnumFacing.DOWN ? BlockFaceShape.SOLID : BlockFaceShape.UNDEFINED;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onBlockActivated(World world, BlockPos pos, IBlockState block, EntityPlayer player, EnumHand hand,
|
||||
EnumFacing side, float hitX, float hitY, float hitZ){
|
||||
@@ -65,59 +72,20 @@ public class BlockArcaneWorkbench extends BlockContainer {
|
||||
return false;
|
||||
}
|
||||
|
||||
player.openGui(Wizardry.instance, WizardryGuiHandler.ARCANE_WORKBENCH, world, pos.getX(), pos.getY(),
|
||||
pos.getZ());
|
||||
player.openGui(Wizardry.instance, WizardryGuiHandler.ARCANE_WORKBENCH, world, pos.getX(), pos.getY(), pos.getZ());
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void breakBlock(World world, BlockPos pos, IBlockState block){
|
||||
|
||||
TileEntity tileentity = world.getTileEntity(pos);
|
||||
|
||||
Random random = new Random();
|
||||
TileEntityArcaneWorkbench tileentityarcaneworkbench = (TileEntityArcaneWorkbench)world.getTileEntity(pos);
|
||||
if(tileentity instanceof TileEntityArcaneWorkbench){
|
||||
InventoryHelper.dropInventoryItems(world, pos, (TileEntityArcaneWorkbench)tileentity);
|
||||
}
|
||||
|
||||
if(tileentityarcaneworkbench != null){
|
||||
|
||||
for(int j1 = 0; j1 < tileentityarcaneworkbench.getSizeInventory(); ++j1){
|
||||
|
||||
ItemStack itemstack = tileentityarcaneworkbench.getStackInSlot(j1);
|
||||
|
||||
if(!itemstack.isEmpty()){
|
||||
|
||||
float f = random.nextFloat() * 0.8F + 0.1F;
|
||||
float f1 = random.nextFloat() * 0.8F + 0.1F;
|
||||
EntityItem entityitem;
|
||||
|
||||
for(float f2 = random.nextFloat() * 0.8F + 0.1F; itemstack.getCount() > 0; world
|
||||
.spawnEntity(entityitem)){
|
||||
|
||||
int k1 = random.nextInt(21) + 10;
|
||||
|
||||
if(k1 > itemstack.getCount()){
|
||||
k1 = itemstack.getCount();
|
||||
}
|
||||
|
||||
itemstack.shrink(k1);
|
||||
|
||||
entityitem = new EntityItem(world, (double)((float)pos.getX() + f),
|
||||
(double)((float)pos.getY() + f1), (double)((float)pos.getZ() + f2),
|
||||
new ItemStack(itemstack.getItem(), k1, itemstack.getItemDamage()));
|
||||
float f3 = 0.05F;
|
||||
entityitem.motionX = (double)((float)random.nextGaussian() * f3);
|
||||
entityitem.motionY = (double)((float)random.nextGaussian() * f3 + 0.2F);
|
||||
entityitem.motionZ = (double)((float)random.nextGaussian() * f3);
|
||||
|
||||
if(itemstack.hasTagCompound()){
|
||||
entityitem.getItem().setTagCompound(((NBTTagCompound)itemstack.getTagCompound().copy()));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// par1World.func_96440_m(par2, par3, par4, block);
|
||||
}
|
||||
|
||||
super.breakBlock(world, pos, block);
|
||||
super.breakBlock(world, pos, block);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,76 @@
|
||||
package electroblob.wizardry.block;
|
||||
|
||||
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 {
|
||||
|
||||
public static final PropertyEnum<Element> ELEMENT = PropertyEnum.create("element", Element.class);
|
||||
|
||||
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);
|
||||
map_colours.put(Element.NECROMANCY, MapColor.PURPLE);
|
||||
map_colours.put(Element.EARTH, MapColor.GREEN);
|
||||
map_colours.put(Element.SORCERY, MapColor.LIME);
|
||||
map_colours.put(Element.HEALING, MapColor.YELLOW);
|
||||
}
|
||||
|
||||
public BlockCrystal(Material material){
|
||||
super(material);
|
||||
this.setDefaultState(this.blockState.getBaseState().withProperty(ELEMENT, Element.MAGIC));
|
||||
this.setCreativeTab(WizardryTabs.WIZARDRY);
|
||||
this.setHarvestLevel("pickaxe", 2);
|
||||
}
|
||||
|
||||
@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 : 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,10 +1,8 @@
|
||||
package electroblob.wizardry.block;
|
||||
|
||||
import java.util.Random;
|
||||
|
||||
import electroblob.wizardry.Wizardry;
|
||||
import electroblob.wizardry.registry.WizardryBlocks;
|
||||
import electroblob.wizardry.util.WizardryParticleType;
|
||||
import electroblob.wizardry.util.ParticleBuilder;
|
||||
import electroblob.wizardry.util.ParticleBuilder.Type;
|
||||
import net.minecraft.block.BlockBush;
|
||||
import net.minecraft.block.SoundType;
|
||||
import net.minecraft.block.material.Material;
|
||||
@@ -19,6 +17,8 @@ import net.minecraftforge.event.entity.player.BonemealEvent;
|
||||
import net.minecraftforge.fml.common.Mod;
|
||||
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
|
||||
|
||||
import java.util.Random;
|
||||
|
||||
// Extending BlockBush allows me to remove nearly everything from this class.
|
||||
@Mod.EventBusSubscriber
|
||||
public class BlockCrystalFlower extends BlockBush {
|
||||
@@ -41,10 +41,10 @@ public class BlockCrystalFlower extends BlockBush {
|
||||
@Override
|
||||
public void randomDisplayTick(IBlockState state, World world, BlockPos pos, Random random){
|
||||
if(world.isRemote && random.nextBoolean()){
|
||||
Wizardry.proxy.spawnParticle(WizardryParticleType.SPARKLE, world, pos.getX() + random.nextDouble(),
|
||||
pos.getY() + random.nextDouble() / 2 + 0.5, pos.getZ() + random.nextDouble(), 0d, 0.01, 0d,
|
||||
20 + random.nextInt(10), 0.5f + (random.nextFloat() / 2), 0.5f + (random.nextFloat() / 2),
|
||||
0.5f + (random.nextFloat() / 2));
|
||||
ParticleBuilder.create(Type.SPARKLE)
|
||||
.pos(pos.getX() + random.nextDouble(), pos.getY() + random.nextDouble() / 2 + 0.5, pos.getZ() + random.nextDouble()).vel(0, 0.01, 0)
|
||||
.time(20 + random.nextInt(10)).clr(0.5f + (random.nextFloat() / 2), 0.5f + (random.nextFloat() / 2),
|
||||
0.5f + (random.nextFloat() / 2)).spawn(world);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,13 +1,17 @@
|
||||
package electroblob.wizardry.block;
|
||||
|
||||
import java.util.Random;
|
||||
|
||||
import electroblob.wizardry.registry.WizardryItems;
|
||||
import net.minecraft.block.Block;
|
||||
import net.minecraft.block.SoundType;
|
||||
import net.minecraft.block.material.Material;
|
||||
import net.minecraft.block.state.IBlockState;
|
||||
import net.minecraft.item.Item;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.util.math.MathHelper;
|
||||
import net.minecraft.world.IBlockAccess;
|
||||
import net.minecraft.world.World;
|
||||
|
||||
import java.util.Random;
|
||||
|
||||
public class BlockCrystalOre extends Block {
|
||||
|
||||
@@ -20,10 +24,17 @@ public class BlockCrystalOre extends Block {
|
||||
|
||||
@Override
|
||||
public int quantityDropped(IBlockState state, int fortune, Random random){
|
||||
// This now works the same way as vanilla ores
|
||||
if(fortune > 0){
|
||||
return random.nextInt(2) + 1 + random.nextInt(fortune);
|
||||
|
||||
int i = random.nextInt(fortune + 2) - 1;
|
||||
|
||||
if(i < 0) i = 0;
|
||||
|
||||
return (random.nextInt(3) + 1) * (i + 1);
|
||||
|
||||
}else{
|
||||
return random.nextInt(2) + 1;
|
||||
return random.nextInt(3) + 1;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -31,4 +42,16 @@ public class BlockCrystalOre extends Block {
|
||||
public Item getItemDropped(IBlockState state, Random random, int fortune){
|
||||
return WizardryItems.magic_crystal;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getExpDrop(IBlockState state, IBlockAccess world, BlockPos pos, int fortune){
|
||||
|
||||
Random rand = world instanceof World ? ((World)world).rand : RANDOM;
|
||||
|
||||
if(this.getItemDropped(state, rand, fortune) != Item.getItemFromBlock(this)){
|
||||
return MathHelper.getInt(rand, 1, 4);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
package electroblob.wizardry.block;
|
||||
|
||||
import net.minecraft.block.BlockFrostedIce;
|
||||
import net.minecraft.block.state.IBlockState;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.util.math.MathHelper;
|
||||
import net.minecraft.world.World;
|
||||
|
||||
import java.util.Random;
|
||||
|
||||
/** Like {@link BlockFrostedIce}, but melting does not depend on light level or neighbouring blocks, and it just
|
||||
* disappears instead of turning to water. */
|
||||
public class BlockDryFrostedIce extends BlockFrostedIce {
|
||||
|
||||
@Override
|
||||
protected void turnIntoWater(World world, BlockPos pos){
|
||||
world.destroyBlock(pos, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateTick(World worldIn, BlockPos pos, IBlockState state, Random rand){
|
||||
if(rand.nextInt(3) == 0){
|
||||
this.slightlyMelt(worldIn, pos, state, rand, true);
|
||||
}else{
|
||||
worldIn.scheduleUpdate(pos, this, MathHelper.getInt(rand, 20, 40));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,23 +1,31 @@
|
||||
package electroblob.wizardry.block;
|
||||
|
||||
import electroblob.wizardry.item.ISpellCastingItem;
|
||||
import electroblob.wizardry.item.ItemArtefact;
|
||||
import electroblob.wizardry.registry.WizardryItems;
|
||||
import electroblob.wizardry.tileentity.TileEntityMagicLight;
|
||||
import net.minecraft.block.BlockContainer;
|
||||
import net.minecraft.block.Block;
|
||||
import net.minecraft.block.ITileEntityProvider;
|
||||
import net.minecraft.block.material.Material;
|
||||
import net.minecraft.block.state.IBlockState;
|
||||
import net.minecraft.entity.player.EntityPlayer;
|
||||
import net.minecraft.tileentity.TileEntity;
|
||||
import net.minecraft.util.EnumBlockRenderType;
|
||||
import net.minecraft.util.EnumFacing;
|
||||
import net.minecraft.util.EnumHand;
|
||||
import net.minecraft.util.math.AxisAlignedBB;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.world.IBlockAccess;
|
||||
import net.minecraft.world.World;
|
||||
|
||||
public class BlockMagicLight extends BlockContainer {
|
||||
public class BlockMagicLight extends Block implements ITileEntityProvider {
|
||||
|
||||
private static final AxisAlignedBB AABB = new AxisAlignedBB(0, 0, 0, 0, 0, 0);
|
||||
//private static final AxisAlignedBB AABB = new AxisAlignedBB(0, 0, 0, 0, 0, 0);
|
||||
|
||||
public BlockMagicLight(Material par2Material){
|
||||
super(par2Material);
|
||||
public BlockMagicLight(Material material){
|
||||
super(material);
|
||||
this.setLightLevel(1.0f);
|
||||
this.setBlockUnbreakable();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -28,13 +36,38 @@ public class BlockMagicLight extends BlockContainer {
|
||||
}
|
||||
|
||||
@Override
|
||||
public AxisAlignedBB getBoundingBox(IBlockState state, IBlockAccess source, BlockPos pos){
|
||||
return AABB;
|
||||
public boolean onBlockActivated(World world, BlockPos pos, IBlockState state, EntityPlayer player, EnumHand hand, EnumFacing facing, float hitX, float hitY, float hitZ){
|
||||
// Let the player dispel any lights if they have the lantern charm, not just the permanent ones because that would be annoying!
|
||||
if(player.getHeldItem(hand).getItem() instanceof ISpellCastingItem && ItemArtefact.isArtefactActive(player, WizardryItems.charm_light)){
|
||||
|
||||
world.setBlockToAir(pos);
|
||||
return true;
|
||||
|
||||
}else{
|
||||
return super.onBlockActivated(world, pos, state, player, hand, facing, hitX, hitY, hitZ);
|
||||
}
|
||||
}
|
||||
|
||||
// @Override
|
||||
// public AxisAlignedBB getBoundingBox(IBlockState state, IBlockAccess source, BlockPos pos){
|
||||
// return AABB;
|
||||
// }
|
||||
|
||||
@Override
|
||||
public boolean isCollidable(){
|
||||
return false;
|
||||
// This method has nothing to do with entity movement, it's just for raytracing
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean addDestroyEffects(World world, BlockPos pos, net.minecraft.client.particle.ParticleManager manager){
|
||||
if(world.getBlockState(pos).getBlock() == this) return true; // No break particles!
|
||||
else return super.addDestroyEffects(world, pos, manager);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasTileEntity(IBlockState state){
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -0,0 +1,116 @@
|
||||
package electroblob.wizardry.block;
|
||||
|
||||
import net.minecraft.block.Block;
|
||||
import net.minecraft.block.BlockObsidian;
|
||||
import net.minecraft.block.properties.PropertyInteger;
|
||||
import net.minecraft.block.state.BlockStateContainer;
|
||||
import net.minecraft.block.state.IBlockState;
|
||||
import net.minecraft.init.Blocks;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.util.EnumFacing;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.util.math.MathHelper;
|
||||
import net.minecraft.world.World;
|
||||
|
||||
import java.util.Random;
|
||||
|
||||
/** Like {@link net.minecraft.block.BlockFrostedIce}, but for lava instead of water. */
|
||||
// This is mostly copied from that class, with a few changes
|
||||
public class BlockObsidianCrust extends BlockObsidian {
|
||||
|
||||
public static final PropertyInteger AGE = PropertyInteger.create("age", 0, 3);
|
||||
|
||||
public BlockObsidianCrust(){
|
||||
this.setDefaultState(this.blockState.getBaseState().withProperty(AGE, 0));
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMetaFromState(IBlockState state){
|
||||
return state.getValue(AGE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public IBlockState getStateFromMeta(int meta){
|
||||
return this.getDefaultState().withProperty(AGE, MathHelper.clamp(meta, 0, 3));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateTick(World world, BlockPos pos, IBlockState state, Random random){
|
||||
if((random.nextInt(3) == 0 || this.countNeighbors(world, pos) < 4) && world.getLightFromNeighbors(pos) > 11 - state.getValue(AGE) - state.getLightOpacity()){
|
||||
this.slightlyMelt(world, pos, state, random, true);
|
||||
}else{
|
||||
world.scheduleUpdate(pos, this, MathHelper.getInt(random, 20, 40));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void neighborChanged(IBlockState state, World world, BlockPos pos, Block block, BlockPos fromPos){
|
||||
if(block == this){
|
||||
int i = this.countNeighbors(world, pos);
|
||||
|
||||
if(i < 2){
|
||||
this.melt(world, pos);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private int countNeighbors(World world, BlockPos pos){
|
||||
|
||||
int i = 0;
|
||||
|
||||
for(EnumFacing enumfacing : EnumFacing.values()){
|
||||
if(world.getBlockState(pos.offset(enumfacing)).getBlock() == this){
|
||||
++i;
|
||||
|
||||
if(i >= 4){
|
||||
return i;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return i;
|
||||
}
|
||||
|
||||
protected void slightlyMelt(World world, BlockPos pos, IBlockState state, Random random, boolean meltNeighbours){
|
||||
|
||||
int i = state.getValue(AGE);
|
||||
|
||||
if(i < 3){
|
||||
|
||||
world.setBlockState(pos, state.withProperty(AGE, i + 1), 2);
|
||||
world.scheduleUpdate(pos, this, MathHelper.getInt(random, 20, 40));
|
||||
|
||||
}else{
|
||||
|
||||
this.melt(world, pos);
|
||||
|
||||
if(meltNeighbours){
|
||||
|
||||
for(EnumFacing enumfacing : EnumFacing.values()){
|
||||
|
||||
BlockPos blockpos = pos.offset(enumfacing);
|
||||
IBlockState iblockstate = world.getBlockState(blockpos);
|
||||
|
||||
if(iblockstate.getBlock() == this){
|
||||
this.slightlyMelt(world, blockpos, iblockstate, random, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected void melt(World world, BlockPos pos){
|
||||
world.setBlockState(pos, Blocks.LAVA.getDefaultState());
|
||||
world.neighborChanged(pos, Blocks.LAVA, pos);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected BlockStateContainer createBlockState(){
|
||||
return new BlockStateContainer(this, AGE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemStack getItem(World world, BlockPos pos, IBlockState state){
|
||||
return ItemStack.EMPTY;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,123 @@
|
||||
package electroblob.wizardry.block;
|
||||
|
||||
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){
|
||||
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 metadata){
|
||||
boolean natural = false;
|
||||
if(metadata > ELEMENT.getAllowedValues().size()){
|
||||
natural = true;
|
||||
metadata -= ELEMENT.getAllowedValues().size();
|
||||
}
|
||||
return this.getDefaultState().withProperty(ELEMENT, Element.values()[metadata]).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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
package electroblob.wizardry.block;
|
||||
|
||||
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.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 {
|
||||
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 BlockRunestone(Material material){
|
||||
super(material);
|
||||
this.setDefaultState(this.blockState.getBaseState().withProperty(ELEMENT, Element.FIRE));
|
||||
this.setCreativeTab(WizardryTabs.WIZARDRY);
|
||||
this.setHardness(1.5F);
|
||||
this.setResistance(10.0F);
|
||||
}
|
||||
|
||||
@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()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public BlockRenderLayer getRenderLayer(){
|
||||
return BlockRenderLayer.CUTOUT; // Required to shade parts of the block faces differently to others
|
||||
}
|
||||
|
||||
@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,13 +1,13 @@
|
||||
package electroblob.wizardry.block;
|
||||
|
||||
import java.util.Random;
|
||||
|
||||
import electroblob.wizardry.registry.Spells;
|
||||
import electroblob.wizardry.spell.Spell;
|
||||
import electroblob.wizardry.tileentity.TileEntityPlayerSave;
|
||||
import electroblob.wizardry.util.AllyDesignationSystem;
|
||||
import electroblob.wizardry.util.MagicDamage;
|
||||
import electroblob.wizardry.util.MagicDamage.DamageType;
|
||||
import electroblob.wizardry.util.WizardryUtilities;
|
||||
import net.minecraft.block.Block;
|
||||
import net.minecraft.block.BlockContainer;
|
||||
import net.minecraft.block.ITileEntityProvider;
|
||||
import net.minecraft.block.SoundType;
|
||||
import net.minecraft.block.material.Material;
|
||||
import net.minecraft.block.state.IBlockState;
|
||||
@@ -25,8 +25,9 @@ import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.world.IBlockAccess;
|
||||
import net.minecraft.world.World;
|
||||
|
||||
// TODO: Apparently you shouldn't extend BlockContainer. I feel like BlockArcaneWorkbench should, but what about the rest?
|
||||
public class BlockSnare extends BlockContainer {
|
||||
import java.util.Random;
|
||||
|
||||
public class BlockSnare extends Block implements ITileEntityProvider {
|
||||
|
||||
private static final AxisAlignedBB AABB = new AxisAlignedBB(0.0f, 0.0f, 0.0f, 1.0f, 0.0625f, 1.0f);
|
||||
|
||||
@@ -51,17 +52,21 @@ public class BlockSnare extends BlockContainer {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onEntityCollidedWithBlock(World world, BlockPos pos, IBlockState state, Entity entity){
|
||||
public void onEntityCollision(World world, BlockPos pos, IBlockState state, Entity entity){
|
||||
|
||||
if(!world.isRemote && entity instanceof EntityLivingBase){
|
||||
if(world.getTileEntity(pos) instanceof TileEntityPlayerSave){
|
||||
|
||||
TileEntityPlayerSave tileentity = (TileEntityPlayerSave)world.getTileEntity(pos);
|
||||
|
||||
if(WizardryUtilities.isValidTarget(tileentity.getCaster(), entity)){
|
||||
((EntityLivingBase)entity).attackEntityFrom(
|
||||
MagicDamage.causeDirectMagicDamage(tileentity.getCaster(), DamageType.MAGIC), 6);
|
||||
((EntityLivingBase)entity).addPotionEffect(new PotionEffect(MobEffects.SLOWNESS, 100, 2));
|
||||
if(AllyDesignationSystem.isValidTarget(tileentity.getCaster(), entity)){
|
||||
|
||||
entity.attackEntityFrom(MagicDamage.causeDirectMagicDamage(tileentity.getCaster(), DamageType.MAGIC),
|
||||
Spells.snare.getProperty(Spell.DAMAGE).floatValue());
|
||||
|
||||
((EntityLivingBase)entity).addPotionEffect(new PotionEffect(MobEffects.SLOWNESS,
|
||||
Spells.snare.getProperty(Spell.EFFECT_DURATION).intValue(),
|
||||
Spells.snare.getProperty(Spell.EFFECT_STRENGTH).intValue()));
|
||||
|
||||
world.destroyBlock(pos, false);
|
||||
}
|
||||
@@ -80,7 +85,7 @@ public class BlockSnare extends BlockContainer {
|
||||
}
|
||||
|
||||
@Override
|
||||
public BlockRenderLayer getBlockLayer(){
|
||||
public BlockRenderLayer getRenderLayer() {
|
||||
return BlockRenderLayer.CUTOUT;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,13 +1,11 @@
|
||||
package electroblob.wizardry.block;
|
||||
|
||||
import java.util.Random;
|
||||
|
||||
import electroblob.wizardry.Wizardry;
|
||||
import electroblob.wizardry.registry.WizardryBlocks;
|
||||
import electroblob.wizardry.tileentity.TileEntityTimer;
|
||||
import electroblob.wizardry.util.WizardryParticleType;
|
||||
import electroblob.wizardry.util.ParticleBuilder;
|
||||
import electroblob.wizardry.util.ParticleBuilder.Type;
|
||||
import net.minecraft.block.Block;
|
||||
import net.minecraft.block.BlockContainer;
|
||||
import net.minecraft.block.ITileEntityProvider;
|
||||
import net.minecraft.block.SoundType;
|
||||
import net.minecraft.block.material.Material;
|
||||
import net.minecraft.block.state.IBlockState;
|
||||
@@ -24,10 +22,10 @@ import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
|
||||
import net.minecraftforge.fml.relauncher.Side;
|
||||
import net.minecraftforge.fml.relauncher.SideOnly;
|
||||
|
||||
// For future reference - extend BlockContainer whenever possible because it has methods for removing tile entities on
|
||||
// block break.
|
||||
import java.util.Random;
|
||||
|
||||
@Mod.EventBusSubscriber
|
||||
public class BlockSpectral extends BlockContainer {
|
||||
public class BlockSpectral extends Block implements ITileEntityProvider {
|
||||
|
||||
public BlockSpectral(Material material){
|
||||
super(material);
|
||||
@@ -36,7 +34,7 @@ public class BlockSpectral extends BlockContainer {
|
||||
|
||||
// Replaces getRenderBlockPass
|
||||
@Override
|
||||
public BlockRenderLayer getBlockLayer(){
|
||||
public BlockRenderLayer getRenderLayer(){
|
||||
return BlockRenderLayer.TRANSLUCENT;
|
||||
}
|
||||
|
||||
@@ -45,10 +43,8 @@ public class BlockSpectral extends BlockContainer {
|
||||
return EnumBlockRenderType.MODEL;
|
||||
}
|
||||
|
||||
// Apparently it's OK to override this, despite it being deprecated. More
|
||||
// importantly, it being deprecated is not
|
||||
// Forge's doing, rather it is Mojang themselves misusing the @Deprecated
|
||||
// annotation to mean 'internal, don't call'.
|
||||
// Apparently it's OK to override this, despite it being deprecated. More importantly, it being deprecated is not
|
||||
// Forge's doing, rather it is Mojang themselves misusing the @Deprecated annotation to mean 'internal, don't call'.
|
||||
@Override
|
||||
public boolean isOpaqueCube(IBlockState state){
|
||||
return false;
|
||||
@@ -61,18 +57,14 @@ public class BlockSpectral extends BlockContainer {
|
||||
|
||||
@Override
|
||||
public void randomDisplayTick(IBlockState state, World world, BlockPos pos, Random random){
|
||||
// Middle of block
|
||||
Wizardry.proxy.spawnParticle(WizardryParticleType.DUST, world, pos.getX() + random.nextDouble(),
|
||||
pos.getY() + random.nextDouble(), pos.getZ() + random.nextDouble(), 0, 0, 0,
|
||||
(int)(16.0D / (Math.random() * 0.8D + 0.2D)), 0.4f + random.nextFloat() * 0.2f,
|
||||
0.6f + random.nextFloat() * 0.4f, 0.6f + random.nextFloat() * 0.4f);
|
||||
// Top surface
|
||||
Wizardry.proxy.spawnParticle(WizardryParticleType.DUST, world, pos.getX() + random.nextDouble(), pos.getY() + 1,
|
||||
pos.getZ() + random.nextDouble(), 0, 0, 0, (int)(16.0D / (Math.random() * 0.8D + 0.2D)),
|
||||
0.4f + random.nextFloat() * 0.2f, 0.6f + random.nextFloat() * 0.4f, 0.6f + random.nextFloat() * 0.4f);
|
||||
Wizardry.proxy.spawnParticle(WizardryParticleType.DUST, world, pos.getX() + random.nextDouble(), pos.getY() + 1,
|
||||
pos.getZ() + random.nextDouble(), 0, 0, 0, (int)(16.0D / (Math.random() * 0.8D + 0.2D)),
|
||||
0.4f + random.nextFloat() * 0.2f, 0.6f + random.nextFloat() * 0.4f, 0.6f + random.nextFloat() * 0.4f);
|
||||
|
||||
for(int i=0; i<2; i++){
|
||||
ParticleBuilder.create(Type.DUST)
|
||||
.pos(pos.getX() + random.nextDouble(), pos.getY() + random.nextDouble(), pos.getZ() + random.nextDouble())
|
||||
.time((int)(16.0D / (Math.random() * 0.8D + 0.2D)))
|
||||
.clr(0.4f + random.nextFloat() * 0.2f, 0.6f + random.nextFloat() * 0.4f, 0.6f + random.nextFloat() * 0.4f)
|
||||
.shaded(true).spawn(world);
|
||||
}
|
||||
}
|
||||
|
||||
// Overriden to make the block always look full brightness despite not emitting
|
||||
@@ -82,6 +74,11 @@ public class BlockSpectral extends BlockContainer {
|
||||
return 15;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasTileEntity(IBlockState state){
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public TileEntity createNewTileEntity(World world, int metadata){
|
||||
return new TileEntityTimer(1200);
|
||||
|
||||
@@ -1,14 +1,13 @@
|
||||
package electroblob.wizardry.block;
|
||||
|
||||
import java.util.Random;
|
||||
|
||||
import electroblob.wizardry.spell.Petrify;
|
||||
import electroblob.wizardry.tileentity.TileEntityStatue;
|
||||
import electroblob.wizardry.util.WizardryUtilities;
|
||||
import net.minecraft.block.Block;
|
||||
import net.minecraft.block.BlockContainer;
|
||||
import net.minecraft.block.ITileEntityProvider;
|
||||
import net.minecraft.block.SoundType;
|
||||
import net.minecraft.block.material.Material;
|
||||
import net.minecraft.block.state.IBlockState;
|
||||
import net.minecraft.entity.EntityLiving;
|
||||
import net.minecraft.tileentity.TileEntity;
|
||||
import net.minecraft.util.BlockRenderLayer;
|
||||
import net.minecraft.util.EnumBlockRenderType;
|
||||
@@ -20,10 +19,17 @@ import net.minecraft.world.World;
|
||||
import net.minecraftforge.fml.relauncher.Side;
|
||||
import net.minecraftforge.fml.relauncher.SideOnly;
|
||||
|
||||
public class BlockStatue extends BlockContainer {
|
||||
import java.util.Random;
|
||||
|
||||
public class BlockStatue extends Block implements ITileEntityProvider {
|
||||
|
||||
private boolean isIce;
|
||||
|
||||
/** The NBT tag name for storing the petrified flag (used for rendering) in the target's tag compound. */
|
||||
public static final String PETRIFIED_NBT_KEY = "petrified";
|
||||
/** The NBT tag name for storing the frozen flag (used for rendering) in the target's tag compound. */
|
||||
public static final String FROZEN_NBT_KEY = "frozen";
|
||||
|
||||
public BlockStatue(Material material){
|
||||
super(material);
|
||||
this.isIce = material == Material.ICE;
|
||||
@@ -97,7 +103,7 @@ public class BlockStatue extends BlockContainer {
|
||||
}
|
||||
|
||||
@Override
|
||||
public BlockRenderLayer getBlockLayer(){
|
||||
public BlockRenderLayer getRenderLayer(){
|
||||
return this.isIce ? BlockRenderLayer.TRANSLUCENT : BlockRenderLayer.SOLID;
|
||||
}
|
||||
|
||||
@@ -106,6 +112,11 @@ public class BlockStatue extends BlockContainer {
|
||||
return this.isIce ? EnumBlockRenderType.MODEL : EnumBlockRenderType.ENTITYBLOCK_ANIMATED;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasTileEntity(IBlockState state){
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public TileEntity createNewTileEntity(World world, int metadata){
|
||||
return new TileEntityStatue(this.isIce);
|
||||
@@ -146,7 +157,7 @@ public class BlockStatue extends BlockContainer {
|
||||
|
||||
// This is only when position == 1 because world.destroyBlock calls this function for the other blocks.
|
||||
if(tileentity != null && tileentity.position == 1 && tileentity.creature != null){
|
||||
tileentity.creature.getEntityData().removeTag(Petrify.NBT_KEY);
|
||||
tileentity.creature.getEntityData().removeTag(BlockStatue.PETRIFIED_NBT_KEY);
|
||||
tileentity.creature.isDead = false;
|
||||
world.spawnEntity(tileentity.creature);
|
||||
}
|
||||
@@ -166,4 +177,84 @@ public class BlockStatue extends BlockContainer {
|
||||
|
||||
return this.isIce && block == this ? false : super.shouldSideBeRendered(blockState, blockAccess, pos, side);
|
||||
}
|
||||
|
||||
/**
|
||||
* Turns the given entity into a statue. The type of statue depends on the block instance this method was invoked on.
|
||||
* @param entity The entity to turn into a statue.
|
||||
* @param duration The time for which the entity should remain a statue. For petrified creatures, this is the minimum
|
||||
* time it can stay as a statue.
|
||||
* @return True if the entity was successfully turned into a statue, false if not (i.e. something was in the way).
|
||||
*/
|
||||
// Making this an instance method means it works equally well for both types of statue
|
||||
public boolean convertToStatue(EntityLiving entity, int duration){
|
||||
|
||||
if(entity.deathTime > 0) return false;
|
||||
|
||||
BlockPos pos = new BlockPos(entity);
|
||||
World world = entity.world;
|
||||
|
||||
entity.hurtTime = 0; // Stops the entity looking red while frozen and the resulting z-fighting
|
||||
entity.extinguish();
|
||||
|
||||
// Short mobs such as spiders and pigs
|
||||
if((entity.height < 1.2 || entity.isChild()) && WizardryUtilities.canBlockBeReplaced(world, pos)){
|
||||
|
||||
world.setBlockState(pos, this.getDefaultState());
|
||||
if(world.getTileEntity(pos) instanceof TileEntityStatue){
|
||||
((TileEntityStatue)world.getTileEntity(pos)).setCreatureAndPart(entity, 1, 1);
|
||||
((TileEntityStatue)world.getTileEntity(pos)).setLifetime(duration);
|
||||
}
|
||||
|
||||
entity.getEntityData().setBoolean(this.isIce ? FROZEN_NBT_KEY : PETRIFIED_NBT_KEY, true);
|
||||
entity.setDead();
|
||||
return true;
|
||||
}
|
||||
// Normal sized mobs like zombies and skeletons
|
||||
else if(entity.height < 2.5 && WizardryUtilities.canBlockBeReplaced(world, pos)
|
||||
&& WizardryUtilities.canBlockBeReplaced(world, pos.up())){
|
||||
|
||||
world.setBlockState(pos, this.getDefaultState());
|
||||
if(world.getTileEntity(pos) instanceof TileEntityStatue){
|
||||
((TileEntityStatue)world.getTileEntity(pos)).setCreatureAndPart(entity, 1, 2);
|
||||
((TileEntityStatue)world.getTileEntity(pos)).setLifetime(duration);
|
||||
}
|
||||
|
||||
world.setBlockState(pos.up(), this.getDefaultState());
|
||||
if(world.getTileEntity(pos.up()) instanceof TileEntityStatue){
|
||||
((TileEntityStatue)world.getTileEntity(pos.up())).setCreatureAndPart(entity, 2, 2);
|
||||
}
|
||||
|
||||
entity.getEntityData().setBoolean(this.isIce ? FROZEN_NBT_KEY : PETRIFIED_NBT_KEY, true);
|
||||
entity.setDead();
|
||||
return true;
|
||||
}
|
||||
// Tall mobs like endermen
|
||||
else if(WizardryUtilities.canBlockBeReplaced(world, pos)
|
||||
&& WizardryUtilities.canBlockBeReplaced(world, pos.up())
|
||||
&& WizardryUtilities.canBlockBeReplaced(world, pos.up(2))){
|
||||
|
||||
world.setBlockState(pos, this.getDefaultState());
|
||||
if(world.getTileEntity(pos) instanceof TileEntityStatue){
|
||||
((TileEntityStatue)world.getTileEntity(pos)).setCreatureAndPart(entity, 1, 3);
|
||||
((TileEntityStatue)world.getTileEntity(pos)).setLifetime(duration);
|
||||
}
|
||||
|
||||
world.setBlockState(pos.up(), this.getDefaultState());
|
||||
if(world.getTileEntity(pos.up()) instanceof TileEntityStatue){
|
||||
((TileEntityStatue)world.getTileEntity(pos.up())).setCreatureAndPart(entity, 2, 3);
|
||||
}
|
||||
|
||||
world.setBlockState(pos.up(2), this.getDefaultState());
|
||||
if(world.getTileEntity(pos.up(2)) instanceof TileEntityStatue){
|
||||
((TileEntityStatue)world.getTileEntity(pos.up(2))).setCreatureAndPart(entity, 3, 3);
|
||||
}
|
||||
|
||||
entity.getEntityData().setBoolean(this.isIce ? FROZEN_NBT_KEY : PETRIFIED_NBT_KEY, true);
|
||||
entity.setDead();
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,181 @@
|
||||
package electroblob.wizardry.block;
|
||||
|
||||
import electroblob.wizardry.registry.Spells;
|
||||
import electroblob.wizardry.registry.WizardryBlocks;
|
||||
import electroblob.wizardry.spell.Spell;
|
||||
import electroblob.wizardry.tileentity.TileEntityPlayerSaveTimed;
|
||||
import electroblob.wizardry.util.AllyDesignationSystem;
|
||||
import electroblob.wizardry.util.MagicDamage;
|
||||
import net.minecraft.block.*;
|
||||
import net.minecraft.block.BlockDoublePlant.EnumBlockHalf;
|
||||
import net.minecraft.block.properties.PropertyEnum;
|
||||
import net.minecraft.block.properties.PropertyInteger;
|
||||
import net.minecraft.block.state.BlockStateContainer;
|
||||
import net.minecraft.block.state.IBlockState;
|
||||
import net.minecraft.entity.Entity;
|
||||
import net.minecraft.entity.EntityLivingBase;
|
||||
import net.minecraft.entity.player.EntityPlayer;
|
||||
import net.minecraft.init.Items;
|
||||
import net.minecraft.item.Item;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.tileentity.TileEntity;
|
||||
import net.minecraft.util.DamageSource;
|
||||
import net.minecraft.util.math.AxisAlignedBB;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.world.IBlockAccess;
|
||||
import net.minecraft.world.World;
|
||||
import net.minecraftforge.event.entity.player.PlayerInteractEvent;
|
||||
import net.minecraftforge.fml.common.Mod;
|
||||
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
|
||||
|
||||
import java.util.Random;
|
||||
|
||||
@Mod.EventBusSubscriber
|
||||
public class BlockThorns extends BlockBush implements ITileEntityProvider {
|
||||
|
||||
public static final int GROWTH_STAGES = 8;
|
||||
|
||||
public static final PropertyInteger AGE = PropertyInteger.create("age", 0, GROWTH_STAGES-1);
|
||||
public static final PropertyEnum<EnumBlockHalf> HALF = PropertyEnum.create("half", EnumBlockHalf.class);
|
||||
|
||||
public BlockThorns(){
|
||||
this.setDefaultState(this.blockState.getBaseState().withProperty(HALF, EnumBlockHalf.LOWER).withProperty(AGE, 7));
|
||||
this.setHardness(4);
|
||||
this.setSoundType(SoundType.PLANT);
|
||||
this.setCreativeTab(null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public AxisAlignedBB getBoundingBox(IBlockState state, IBlockAccess source, BlockPos pos){
|
||||
return FULL_BLOCK_AABB;
|
||||
}
|
||||
|
||||
@Override
|
||||
public IBlockState getStateFromMeta(int meta){
|
||||
return this.getDefaultState().withProperty(HALF, EnumBlockHalf.values()[meta / GROWTH_STAGES]).withProperty(AGE, meta % GROWTH_STAGES);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMetaFromState(IBlockState state){
|
||||
return state.getValue(HALF).ordinal() * GROWTH_STAGES + state.getValue(AGE);
|
||||
}
|
||||
|
||||
// @Override
|
||||
// public void updateTick(World world, BlockPos pos, IBlockState state, Random rand){
|
||||
//
|
||||
// super.updateTick(world, pos, state, rand);
|
||||
//
|
||||
// // Update the state, including on the client, but don't do a block update since it's only visual
|
||||
// if(state.getValue(AGE) < GROWTH_STAGES-1) world.setBlockState(pos, state.withProperty(AGE, state.getValue(AGE) + 1), 2);
|
||||
// }
|
||||
|
||||
@Override
|
||||
protected BlockStateContainer createBlockState(){
|
||||
return new BlockStateContainer(this, HALF, AGE);
|
||||
}
|
||||
|
||||
public void placeAt(World world, BlockPos lowerPos, int flags){
|
||||
world.setBlockState(lowerPos, this.getDefaultState().withProperty(HALF, EnumBlockHalf.LOWER).withProperty(AGE, 0), flags);
|
||||
world.setBlockState(lowerPos.up(), this.getDefaultState().withProperty(HALF, EnumBlockHalf.UPPER).withProperty(AGE, 0), flags);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBlockPlacedBy(World world, BlockPos pos, IBlockState state, EntityLivingBase placer, ItemStack stack){
|
||||
world.setBlockState(pos.up(), this.getDefaultState().withProperty(HALF, EnumBlockHalf.UPPER), 2);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void breakBlock(World world, BlockPos pos, IBlockState state){
|
||||
super.breakBlock(world, pos, state);
|
||||
if(state.getValue(HALF) == EnumBlockHalf.LOWER){
|
||||
if(world.getBlockState(pos.up()).getBlock() == this){
|
||||
world.destroyBlock(pos.up(), false);
|
||||
}
|
||||
}else{
|
||||
if(world.getBlockState(pos.down()).getBlock() == this){
|
||||
world.destroyBlock(pos.down(), false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public boolean canBlockStay(World worldIn, BlockPos pos, IBlockState state){
|
||||
if(state.getValue(HALF) == BlockDoublePlant.EnumBlockHalf.UPPER){
|
||||
return worldIn.getBlockState(pos.down()).getBlock() == this;
|
||||
}else{
|
||||
IBlockState iblockstate = worldIn.getBlockState(pos.up());
|
||||
return iblockstate.getBlock() == this && this.canSustainBush(worldIn.getBlockState(pos.down()));
|
||||
}
|
||||
}
|
||||
|
||||
// @Override
|
||||
// public IBlockState getActualState(IBlockState state, IBlockAccess world, BlockPos pos){
|
||||
// // Copied from BlockFlowerPot on authority of the Forge docs, which says this check is necessary
|
||||
// SoundLoopSpellDispenser tileentity = world instanceof ChunkCache ? ((ChunkCache)world).getTileEntity(pos, Chunk.EnumCreateEntityType.CHECK) : world.getTileEntity(pos);
|
||||
//
|
||||
// if(tileentity instanceof TileEntityPlayerSaveTimed){
|
||||
// state = state.withProperty(AGE, Math.min(7, ((TileEntityPlayerSaveTimed)tileentity).timer/2));
|
||||
// }else{
|
||||
// state = state.withProperty(AGE, 7);
|
||||
// }
|
||||
//
|
||||
// return state;
|
||||
// }
|
||||
|
||||
@Override
|
||||
public void onEntityCollision(World world, BlockPos pos, IBlockState state, Entity entity){
|
||||
if(!world.isRemote){
|
||||
if(applyThornDamage(world, pos, entity)){
|
||||
entity.setInWeb();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean applyThornDamage(World world, BlockPos pos, Entity target){
|
||||
|
||||
DamageSource source = DamageSource.CACTUS;
|
||||
|
||||
TileEntity tileentity = world.getTileEntity(pos);
|
||||
|
||||
if(tileentity instanceof TileEntityPlayerSaveTimed){
|
||||
if(AllyDesignationSystem.isValidTarget(((TileEntityPlayerSaveTimed)tileentity).getCaster(), target)){
|
||||
source = MagicDamage.causeDirectMagicDamage(((TileEntityPlayerSaveTimed)tileentity).getCaster(),
|
||||
MagicDamage.DamageType.MAGIC);
|
||||
}else{
|
||||
return false; // Don't attack or slow allies of the caster
|
||||
}
|
||||
}
|
||||
|
||||
if(world.getTotalWorldTime() % 20 == 0) target.attackEntityFrom(source, Spells.forest_of_thorns.getProperty(Spell.DAMAGE).floatValue());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Block.EnumOffsetType getOffsetType(){
|
||||
return Block.EnumOffsetType.XZ;
|
||||
}
|
||||
|
||||
@Override
|
||||
public TileEntity createNewTileEntity(World world, int metadata){
|
||||
return new TileEntityPlayerSaveTimed(600);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasTileEntity(IBlockState state){
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override public boolean isReplaceable(IBlockAccess world, BlockPos pos){ return false; }
|
||||
@Override protected boolean canSustainBush(IBlockState state){ return state.isNormalCube(); }
|
||||
@Override public Item getItemDropped(IBlockState state, Random rand, int fortune){ return Items.AIR; }
|
||||
@Override public boolean canSilkHarvest(World world, BlockPos pos, IBlockState state, EntityPlayer player){ return false; }
|
||||
|
||||
@SubscribeEvent
|
||||
public static void onLeftClickBlockEvent(PlayerInteractEvent.LeftClickBlock event){
|
||||
if(!event.getWorld().isRemote && event.getWorld().getTotalWorldTime() % 20 == 0
|
||||
&& event.getWorld().getBlockState(event.getPos()).getBlock() == WizardryBlocks.thorns){
|
||||
applyThornDamage(event.getWorld(), event.getPos(), event.getEntity());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,12 +1,16 @@
|
||||
package electroblob.wizardry.block;
|
||||
|
||||
import java.util.Random;
|
||||
|
||||
import electroblob.wizardry.WizardData;
|
||||
import electroblob.wizardry.Wizardry;
|
||||
import electroblob.wizardry.item.ItemWand;
|
||||
import electroblob.wizardry.data.WizardData;
|
||||
import electroblob.wizardry.item.ISpellCastingItem;
|
||||
import electroblob.wizardry.item.ItemArtefact;
|
||||
import electroblob.wizardry.registry.Spells;
|
||||
import electroblob.wizardry.registry.WizardryBlocks;
|
||||
import electroblob.wizardry.registry.WizardryItems;
|
||||
import electroblob.wizardry.spell.Transportation;
|
||||
import electroblob.wizardry.util.Location;
|
||||
import electroblob.wizardry.util.ParticleBuilder;
|
||||
import electroblob.wizardry.util.WizardryUtilities;
|
||||
import net.minecraft.block.Block;
|
||||
import net.minecraft.block.material.Material;
|
||||
import net.minecraft.block.state.IBlockState;
|
||||
@@ -20,6 +24,10 @@ import net.minecraft.util.text.TextComponentTranslation;
|
||||
import net.minecraft.world.IBlockAccess;
|
||||
import net.minecraft.world.World;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Random;
|
||||
|
||||
public class BlockTransportationStone extends Block {
|
||||
|
||||
private static final AxisAlignedBB AABB = new AxisAlignedBB(0.0625f * 5, 0, 0.0625f * 5, 0.0625f * 11, 0.0625f * 6,
|
||||
@@ -65,15 +73,21 @@ public class BlockTransportationStone extends Block {
|
||||
public boolean isOpaqueCube(IBlockState state){
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void onNeighborChange(IBlockAccess world, BlockPos pos, BlockPos neighbor){
|
||||
public boolean isSideSolid(IBlockState base_state, IBlockAccess world, BlockPos pos, EnumFacing side){
|
||||
return side == EnumFacing.DOWN;
|
||||
}
|
||||
|
||||
super.onNeighborChange(world, pos, neighbor);
|
||||
@SuppressWarnings("deprecation")
|
||||
@Override
|
||||
public void neighborChanged(IBlockState state, World world, BlockPos pos, Block block, BlockPos fromPos){
|
||||
|
||||
if(!world.isSideSolid(pos.down(), EnumFacing.UP, false) && world instanceof World){
|
||||
this.dropBlockAsItem((World)world, pos, world.getBlockState(pos), 0);
|
||||
((World)world).setBlockToAir(pos);
|
||||
super.neighborChanged(state, world, pos, block, fromPos);
|
||||
|
||||
if(!world.isSideSolid(pos.down(), EnumFacing.UP, false)){
|
||||
this.dropBlockAsItem(world, pos, world.getBlockState(pos), 0);
|
||||
world.setBlockToAir(pos);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -97,7 +111,7 @@ public class BlockTransportationStone extends Block {
|
||||
|
||||
ItemStack stack = player.getHeldItem(hand);
|
||||
|
||||
if(stack.getItem() instanceof ItemWand){
|
||||
if(stack.getItem() instanceof ISpellCastingItem){
|
||||
if(WizardData.get(player) != null){
|
||||
|
||||
WizardData data = WizardData.get(player);
|
||||
@@ -106,17 +120,59 @@ public class BlockTransportationStone extends Block {
|
||||
for(int z = -1; z <= 1; z++){
|
||||
BlockPos pos1 = pos.add(x, 0, z);
|
||||
if(testForCircle(world, pos1)){
|
||||
data.setStoneCircleLocation(pos1, world.provider.getDimension());
|
||||
if(!world.isRemote) player.sendMessage(
|
||||
new TextComponentTranslation("tile." + Wizardry.MODID + ":transportation_stone.confirm",
|
||||
Spells.transportation.getNameForTranslationFormatted()));
|
||||
|
||||
Location here = new Location(pos1, player.dimension);
|
||||
|
||||
List<Location> locations = data.getVariable(Transportation.LOCATIONS_KEY);
|
||||
if(locations == null) data.setVariable(Transportation.LOCATIONS_KEY, locations = new ArrayList<>(Transportation.MAX_REMEMBERED_LOCATIONS));
|
||||
|
||||
if(ItemArtefact.isArtefactActive(player, WizardryItems.charm_transportation)){
|
||||
|
||||
if(locations.contains(here)){
|
||||
locations.remove(here);
|
||||
if(!world.isRemote) player.sendStatusMessage(new TextComponentTranslation("tile." + Wizardry.MODID + ":transportation_stone.forget", here.pos.getX(), here.pos.getY(), here.pos.getZ(), here.dimension), true);
|
||||
|
||||
}else{
|
||||
|
||||
locations.add(here);
|
||||
if(!world.isRemote) player.sendStatusMessage(new TextComponentTranslation("tile." + Wizardry.MODID + ":transportation_stone.remember", here.pos.getX(), here.pos.getY(), here.pos.getZ(), here.dimension), true);
|
||||
|
||||
if(locations.size() > Transportation.MAX_REMEMBERED_LOCATIONS){
|
||||
Location removed = locations.remove(0);
|
||||
if(!world.isRemote) player.sendStatusMessage(new TextComponentTranslation("tile." + Wizardry.MODID + ":transportation_stone.forget", removed.pos.getX(), removed.pos.getY(), removed.pos.getZ(), removed.dimension), true);
|
||||
}
|
||||
}
|
||||
|
||||
}else{
|
||||
if(locations.isEmpty()) locations.add(here);
|
||||
else{
|
||||
locations.remove(here); // Prevents duplicates
|
||||
locations.set(locations.size() - 1, here);
|
||||
}
|
||||
if(!world.isRemote) player.sendStatusMessage(new TextComponentTranslation("tile." + Wizardry.MODID + ":transportation_stone.confirm", Spells.transportation.getNameForTranslationFormatted()), true);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(!world.isRemote)
|
||||
player.sendMessage(new TextComponentTranslation("tile." + Wizardry.MODID + ":transportation_stone.invalid"));
|
||||
if(!world.isRemote){
|
||||
player.sendStatusMessage(new TextComponentTranslation("tile." + Wizardry.MODID + ":transportation_stone.invalid"), true);
|
||||
}else{
|
||||
|
||||
BlockPos centre = findMostLikelyCircle(world, pos);
|
||||
// Displays particles in the required shape
|
||||
for(int x = -1; x <= 1; x++){
|
||||
for(int z = -1; z <= 1; z++){
|
||||
if(x == 0 && z == 0) continue;
|
||||
ParticleBuilder.create(ParticleBuilder.Type.PATH)
|
||||
.pos(WizardryUtilities.getCentre(centre).add(x, -0.3125, z)).clr(0x86ff65)
|
||||
.time(200).scale(2).spawn(world);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -130,12 +186,47 @@ public class BlockTransportationStone extends Block {
|
||||
|
||||
for(int x = -1; x <= 1; x++){
|
||||
for(int z = -1; z <= 1; z++){
|
||||
if(x == 0 && z == 0) continue;
|
||||
if(world.getBlockState(pos.add(x, 0, z)).getBlock() != WizardryBlocks.transportation_stone){
|
||||
if(x != 0 || z != 0) return false;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private static BlockPos findMostLikelyCircle(World world, BlockPos pos){
|
||||
|
||||
int bestSoFar = 0;
|
||||
BlockPos result = null;
|
||||
|
||||
for(int x = -1; x <= 1; x++){
|
||||
for(int z = -1; z <= 1; z++){
|
||||
if(x == 0 && z == 0) continue;
|
||||
BlockPos pos1 = pos.add(x, 0, z);
|
||||
int n = getCircleCompleteness(world, pos1);
|
||||
if(n > bestSoFar){
|
||||
bestSoFar = n;
|
||||
result = pos1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private static int getCircleCompleteness(World world, BlockPos pos){
|
||||
|
||||
int n = 0;
|
||||
|
||||
for(int x = -1; x <= 1; x++){
|
||||
for(int z = -1; z <= 1; z++){
|
||||
if(x == 0 && z == 0) continue;
|
||||
if(world.getBlockState(pos.add(x, 0, z)).getBlock() == WizardryBlocks.transportation_stone) n++;
|
||||
}
|
||||
}
|
||||
|
||||
return n;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
package electroblob.wizardry.block;
|
||||
|
||||
import java.util.Random;
|
||||
|
||||
import electroblob.wizardry.tileentity.TileEntityTimer;
|
||||
import net.minecraft.block.BlockContainer;
|
||||
import net.minecraft.block.Block;
|
||||
import net.minecraft.block.ITileEntityProvider;
|
||||
import net.minecraft.block.material.Material;
|
||||
import net.minecraft.block.state.IBlockState;
|
||||
import net.minecraft.entity.Entity;
|
||||
@@ -17,15 +16,17 @@ import net.minecraft.world.World;
|
||||
import net.minecraftforge.fml.relauncher.Side;
|
||||
import net.minecraftforge.fml.relauncher.SideOnly;
|
||||
|
||||
// For future reference - extend BlockContainer whenever possible because it has methods for removing tile entities on block break.
|
||||
public class BlockVanishingCobweb extends BlockContainer {
|
||||
import java.util.Random;
|
||||
|
||||
public class BlockVanishingCobweb extends Block implements ITileEntityProvider {
|
||||
|
||||
public BlockVanishingCobweb(Material material){
|
||||
super(material);
|
||||
}
|
||||
|
||||
@SideOnly(Side.CLIENT)
|
||||
public BlockRenderLayer getBlockLayer(){
|
||||
@Override
|
||||
public BlockRenderLayer getRenderLayer(){
|
||||
return BlockRenderLayer.CUTOUT;
|
||||
}
|
||||
|
||||
@@ -49,13 +50,18 @@ public class BlockVanishingCobweb extends BlockContainer {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasTileEntity(IBlockState state){
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public TileEntity createNewTileEntity(World world, int metadata){
|
||||
return new TileEntityTimer(400);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onEntityCollidedWithBlock(World world, BlockPos pos, IBlockState state, Entity entity){
|
||||
public void onEntityCollision(World world, BlockPos pos, IBlockState state, Entity entity){
|
||||
entity.setInWeb();
|
||||
}
|
||||
|
||||
|
||||
@@ -1,147 +1,90 @@
|
||||
package electroblob.wizardry.client;
|
||||
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.util.HashMap;
|
||||
|
||||
import org.lwjgl.input.Keyboard;
|
||||
|
||||
import electroblob.wizardry.CommonProxy;
|
||||
import electroblob.wizardry.SpellGlyphData;
|
||||
import electroblob.wizardry.WizardData;
|
||||
import electroblob.wizardry.Wizardry;
|
||||
import electroblob.wizardry.client.audio.MovingSoundEntity;
|
||||
import electroblob.wizardry.client.audio.SoundLoop;
|
||||
import electroblob.wizardry.client.audio.SoundLoopSpell;
|
||||
import electroblob.wizardry.client.gui.GuiSpellDisplay;
|
||||
import electroblob.wizardry.client.gui.config.NamedBooleanEntry;
|
||||
import electroblob.wizardry.client.gui.config.SpellHUDSkinChooserEntry;
|
||||
import electroblob.wizardry.client.gui.handbook.GuiWizardHandbook;
|
||||
import electroblob.wizardry.client.model.ModelWizardArmour;
|
||||
import electroblob.wizardry.client.particle.ParticleBlizzard;
|
||||
import electroblob.wizardry.client.particle.ParticleDarkMagic;
|
||||
import electroblob.wizardry.client.particle.ParticleDust;
|
||||
import electroblob.wizardry.client.particle.ParticleGiantBubble;
|
||||
import electroblob.wizardry.client.particle.ParticleIce;
|
||||
import electroblob.wizardry.client.particle.ParticleLeaf;
|
||||
import electroblob.wizardry.client.particle.ParticleMagicFlame;
|
||||
import electroblob.wizardry.client.particle.ParticlePath;
|
||||
import electroblob.wizardry.client.particle.ParticleRotatingSparkle;
|
||||
import electroblob.wizardry.client.particle.ParticleSnow;
|
||||
import electroblob.wizardry.client.particle.ParticleSpark;
|
||||
import electroblob.wizardry.client.particle.ParticleSparkle;
|
||||
import electroblob.wizardry.client.particle.ParticleTornado;
|
||||
import electroblob.wizardry.client.renderer.LayerStone;
|
||||
import electroblob.wizardry.client.renderer.RenderArc;
|
||||
import electroblob.wizardry.client.renderer.RenderArcaneWorkbench;
|
||||
import electroblob.wizardry.client.renderer.RenderBlackHole;
|
||||
import electroblob.wizardry.client.renderer.RenderBlank;
|
||||
import electroblob.wizardry.client.renderer.RenderBubble;
|
||||
import electroblob.wizardry.client.renderer.RenderDecay;
|
||||
import electroblob.wizardry.client.renderer.RenderDecoy;
|
||||
import electroblob.wizardry.client.renderer.RenderEvilWizard;
|
||||
import electroblob.wizardry.client.renderer.RenderFireRing;
|
||||
import electroblob.wizardry.client.renderer.RenderForceArrow;
|
||||
import electroblob.wizardry.client.renderer.RenderHammer;
|
||||
import electroblob.wizardry.client.renderer.RenderIceGiant;
|
||||
import electroblob.wizardry.client.renderer.RenderIceSpike;
|
||||
import electroblob.wizardry.client.renderer.RenderLightningDisc;
|
||||
import electroblob.wizardry.client.renderer.RenderLightningPulse;
|
||||
import electroblob.wizardry.client.renderer.RenderMagicArrow;
|
||||
import electroblob.wizardry.client.renderer.RenderMagicLight;
|
||||
import electroblob.wizardry.client.renderer.RenderPhoenix;
|
||||
import electroblob.wizardry.client.renderer.RenderProjectile;
|
||||
import electroblob.wizardry.client.renderer.RenderSigil;
|
||||
import electroblob.wizardry.client.renderer.RenderSpiritHorse;
|
||||
import electroblob.wizardry.client.renderer.RenderSpiritWolf;
|
||||
import electroblob.wizardry.client.renderer.RenderStatue;
|
||||
import electroblob.wizardry.client.renderer.RenderWizard;
|
||||
import electroblob.wizardry.entity.EntityArc;
|
||||
import electroblob.wizardry.client.particle.*;
|
||||
import electroblob.wizardry.client.particle.ParticleWizardry.IWizardryParticleFactory;
|
||||
import electroblob.wizardry.client.renderer.*;
|
||||
import electroblob.wizardry.command.SpellEmitter;
|
||||
import electroblob.wizardry.data.DispenserCastingData;
|
||||
import electroblob.wizardry.data.SpellEmitterData;
|
||||
import electroblob.wizardry.data.SpellGlyphData;
|
||||
import electroblob.wizardry.data.WizardData;
|
||||
import electroblob.wizardry.entity.EntityShield;
|
||||
import electroblob.wizardry.entity.construct.EntityArrowRain;
|
||||
import electroblob.wizardry.entity.construct.EntityBlackHole;
|
||||
import electroblob.wizardry.entity.construct.EntityBlizzard;
|
||||
import electroblob.wizardry.entity.construct.EntityBubble;
|
||||
import electroblob.wizardry.entity.construct.EntityDecay;
|
||||
import electroblob.wizardry.entity.construct.EntityEarthquake;
|
||||
import electroblob.wizardry.entity.construct.EntityFireRing;
|
||||
import electroblob.wizardry.entity.construct.EntityFireSigil;
|
||||
import electroblob.wizardry.entity.construct.EntityForcefield;
|
||||
import electroblob.wizardry.entity.construct.EntityFrostSigil;
|
||||
import electroblob.wizardry.entity.construct.EntityHailstorm;
|
||||
import electroblob.wizardry.entity.construct.EntityHammer;
|
||||
import electroblob.wizardry.entity.construct.EntityHealAura;
|
||||
import electroblob.wizardry.entity.construct.EntityIceSpike;
|
||||
import electroblob.wizardry.entity.construct.EntityLightningPulse;
|
||||
import electroblob.wizardry.entity.construct.EntityLightningSigil;
|
||||
import electroblob.wizardry.entity.construct.EntityTornado;
|
||||
import electroblob.wizardry.entity.living.EntityDecoy;
|
||||
import electroblob.wizardry.entity.living.EntityEvilWizard;
|
||||
import electroblob.wizardry.entity.living.EntityIceGiant;
|
||||
import electroblob.wizardry.entity.living.EntityIceWraith;
|
||||
import electroblob.wizardry.entity.living.EntityLightningWraith;
|
||||
import electroblob.wizardry.entity.living.EntityPhoenix;
|
||||
import electroblob.wizardry.entity.living.EntityShadowWraith;
|
||||
import electroblob.wizardry.entity.living.EntitySpiritHorse;
|
||||
import electroblob.wizardry.entity.living.EntitySpiritWolf;
|
||||
import electroblob.wizardry.entity.living.EntityStormElemental;
|
||||
import electroblob.wizardry.entity.living.EntityWizard;
|
||||
import electroblob.wizardry.entity.living.ISpellCaster;
|
||||
import electroblob.wizardry.entity.living.ISummonedCreature;
|
||||
import electroblob.wizardry.entity.projectile.EntityDarknessOrb;
|
||||
import electroblob.wizardry.entity.projectile.EntityDart;
|
||||
import electroblob.wizardry.entity.projectile.EntityFirebolt;
|
||||
import electroblob.wizardry.entity.projectile.EntityFirebomb;
|
||||
import electroblob.wizardry.entity.projectile.EntityForceArrow;
|
||||
import electroblob.wizardry.entity.projectile.EntityForceOrb;
|
||||
import electroblob.wizardry.entity.projectile.EntityIceCharge;
|
||||
import electroblob.wizardry.entity.projectile.EntityIceLance;
|
||||
import electroblob.wizardry.entity.projectile.EntityIceShard;
|
||||
import electroblob.wizardry.entity.projectile.EntityLightningArrow;
|
||||
import electroblob.wizardry.entity.projectile.EntityLightningDisc;
|
||||
import electroblob.wizardry.entity.projectile.EntityMagicMissile;
|
||||
import electroblob.wizardry.entity.projectile.EntityPoisonBomb;
|
||||
import electroblob.wizardry.entity.projectile.EntitySmokeBomb;
|
||||
import electroblob.wizardry.entity.projectile.EntitySpark;
|
||||
import electroblob.wizardry.entity.projectile.EntitySparkBomb;
|
||||
import electroblob.wizardry.entity.projectile.EntityThunderbolt;
|
||||
import electroblob.wizardry.entity.construct.*;
|
||||
import electroblob.wizardry.entity.living.*;
|
||||
import electroblob.wizardry.entity.projectile.*;
|
||||
import electroblob.wizardry.event.SpellCastEvent;
|
||||
import electroblob.wizardry.event.SpellCastEvent.Source;
|
||||
import electroblob.wizardry.integration.antiqueatlas.WizardryAntiqueAtlasIntegration;
|
||||
import electroblob.wizardry.item.ItemScroll;
|
||||
import electroblob.wizardry.item.ItemSpellBook;
|
||||
import electroblob.wizardry.item.ItemWand;
|
||||
import electroblob.wizardry.packet.PacketCastContinuousSpell;
|
||||
import electroblob.wizardry.packet.PacketCastSpell;
|
||||
import electroblob.wizardry.packet.PacketNPCCastSpell;
|
||||
import electroblob.wizardry.packet.PacketPlayerSync.Message;
|
||||
import electroblob.wizardry.packet.PacketTransportation;
|
||||
import electroblob.wizardry.packet.*;
|
||||
import electroblob.wizardry.potion.PotionSlowTime;
|
||||
import electroblob.wizardry.registry.Spells;
|
||||
import electroblob.wizardry.spell.Clairvoyance;
|
||||
import electroblob.wizardry.spell.None;
|
||||
import electroblob.wizardry.spell.Spell;
|
||||
import electroblob.wizardry.registry.WizardrySounds;
|
||||
import electroblob.wizardry.spell.*;
|
||||
import electroblob.wizardry.tileentity.TileEntityArcaneWorkbench;
|
||||
import electroblob.wizardry.tileentity.TileEntityMagicLight;
|
||||
import electroblob.wizardry.tileentity.TileEntityShrineCore;
|
||||
import electroblob.wizardry.tileentity.TileEntityStatue;
|
||||
import electroblob.wizardry.util.ParticleBuilder;
|
||||
import electroblob.wizardry.util.ParticleBuilder.Type;
|
||||
import electroblob.wizardry.util.WandHelper;
|
||||
import electroblob.wizardry.util.WizardryParticleType;
|
||||
import net.minecraft.block.state.IBlockState;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.entity.EntityPlayerSP;
|
||||
import net.minecraft.client.gui.FontRenderer;
|
||||
import net.minecraft.client.gui.GuiMerchant;
|
||||
import net.minecraft.client.model.ModelBiped;
|
||||
import net.minecraft.client.renderer.entity.RenderBlaze;
|
||||
import net.minecraft.client.renderer.entity.RenderHusk;
|
||||
import net.minecraft.client.renderer.entity.RenderSkeleton;
|
||||
import net.minecraft.client.resources.I18n;
|
||||
import net.minecraft.client.resources.IReloadableResourceManager;
|
||||
import net.minecraft.client.resources.IResourceManager;
|
||||
import net.minecraft.client.settings.KeyBinding;
|
||||
import net.minecraft.entity.Entity;
|
||||
import net.minecraft.entity.EntityLiving;
|
||||
import net.minecraft.entity.EntityLivingBase;
|
||||
import net.minecraft.entity.monster.EntityBlaze;
|
||||
import net.minecraft.entity.player.EntityPlayer;
|
||||
import net.minecraft.init.SoundEvents;
|
||||
import net.minecraft.inventory.ContainerMerchant;
|
||||
import net.minecraft.item.Item;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.tileentity.TileEntity;
|
||||
import net.minecraft.tileentity.TileEntityDispenser;
|
||||
import net.minecraft.util.EnumParticleTypes;
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
import net.minecraft.util.SoundCategory;
|
||||
import net.minecraft.util.SoundEvent;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.util.math.MathHelper;
|
||||
import net.minecraft.util.text.Style;
|
||||
import net.minecraft.util.text.TextComponentTranslation;
|
||||
import net.minecraft.village.MerchantRecipeList;
|
||||
import net.minecraft.world.World;
|
||||
import net.minecraftforge.common.MinecraftForge;
|
||||
import net.minecraftforge.common.config.Property;
|
||||
import net.minecraftforge.fml.client.config.GuiConfigEntries.NumberSliderEntry;
|
||||
import net.minecraftforge.fml.client.registry.ClientRegistry;
|
||||
import net.minecraftforge.fml.client.registry.RenderingRegistry;
|
||||
import org.lwjgl.input.Keyboard;
|
||||
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* The client proxy for wizardry.
|
||||
@@ -154,6 +97,9 @@ public class ClientProxy extends CommonProxy {
|
||||
/** Static instance of the mixed font renderer */
|
||||
public static MixedFontRenderer mixedFontRenderer;
|
||||
|
||||
/** Static particle factory map */
|
||||
private static final Map<ResourceLocation, IWizardryParticleFactory> factories = new HashMap<>();
|
||||
|
||||
// Key Bindings
|
||||
public static final KeyBinding NEXT_SPELL = new KeyBinding("key." + Wizardry.MODID + ".next_spell", Keyboard.KEY_N, "key.categories." + Wizardry.MODID);
|
||||
public static final KeyBinding PREVIOUS_SPELL = new KeyBinding("key." + Wizardry.MODID + ".previous_spell", Keyboard.KEY_B, "key.categories." + Wizardry.MODID);
|
||||
@@ -161,6 +107,9 @@ public class ClientProxy extends CommonProxy {
|
||||
// Armour Model
|
||||
public static final ModelBiped WIZARD_ARMOUR_MODEL = new ModelWizardArmour(0.75f);
|
||||
|
||||
/** The wrap width for standard multi-line descriptions (see {@link ClientProxy#addMultiLineDescription(List, String, Style)}). */
|
||||
private static final int TOOLTIP_WRAP_WIDTH = 140;
|
||||
|
||||
// SECTION Registry
|
||||
// ===============================================================================================================
|
||||
|
||||
@@ -175,16 +124,28 @@ public class ClientProxy extends CommonProxy {
|
||||
ClientRegistry.registerKeyBinding(PREVIOUS_SPELL);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void registerSpellHUD(){
|
||||
MinecraftForge.EVENT_BUS.register(new GuiSpellDisplay(Minecraft.getMinecraft()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initGuiBits(){
|
||||
mixedFontRenderer = new MixedFontRenderer(Minecraft.getMinecraft().gameSettings, new ResourceLocation("textures/font/ascii.png"),
|
||||
Minecraft.getMinecraft().renderEngine, false);
|
||||
GuiWizardHandbook.initDisplayRecipes();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void registerResourceReloadListeners(){
|
||||
IResourceManager manager = Minecraft.getMinecraft().getResourceManager();
|
||||
if(manager instanceof IReloadableResourceManager){
|
||||
((IReloadableResourceManager)manager).registerReloadListener(GuiSpellDisplay::loadSkins);
|
||||
((IReloadableResourceManager)manager).registerReloadListener(GuiWizardHandbook::loadHandbookFile);
|
||||
}
|
||||
}
|
||||
|
||||
// @Override
|
||||
// public void registerSoundEventListener(){
|
||||
// Minecraft.getMinecraft().getSoundHandler().addListener(ContinuousSpellSoundEntity::soundPlayed);
|
||||
// }
|
||||
|
||||
public void registerAtlasMarkers(){
|
||||
WizardryAntiqueAtlasIntegration.registerMarkers();
|
||||
}
|
||||
|
||||
// SECTION Misc
|
||||
@@ -194,6 +155,16 @@ public class ClientProxy extends CommonProxy {
|
||||
public void setToNumberSliderEntry(Property property){
|
||||
property.setConfigEntryClass(NumberSliderEntry.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setToHUDChooserEntry(Property property){
|
||||
property.setConfigEntryClass(SpellHUDSkinChooserEntry.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setToNamedBooleanEntry(Property property){
|
||||
property.setConfigEntryClass(NamedBooleanEntry.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public World getTheWorld(){
|
||||
@@ -201,13 +172,66 @@ public class ClientProxy extends CommonProxy {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void playMovingSound(Entity entity, SoundEvent sound, float volume, float pitch, boolean repeat){
|
||||
Minecraft.getMinecraft().getSoundHandler().playSound(new MovingSoundEntity(entity, sound, volume, pitch, repeat));
|
||||
public void playMovingSound(Entity entity, SoundEvent sound, SoundCategory category, float volume, float pitch, boolean repeat){
|
||||
Minecraft.getMinecraft().getSoundHandler().playSound(new MovingSoundEntity<>(entity, sound, category, volume, pitch, repeat));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void playSpellSoundLoop(EntityLivingBase entity, Spell spell, SoundEvent start, SoundEvent loop, SoundEvent end, SoundCategory category, float volume, float pitch){
|
||||
SoundLoop.addLoop(new SoundLoopSpell.SoundLoopSpellEntity(start, loop, end, spell, entity, volume, pitch));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void playSpellSoundLoop(World world, double x, double y, double z, Spell spell, SoundEvent start, SoundEvent loop, SoundEvent end, SoundCategory category, float volume, float pitch, int duration){
|
||||
if(duration == -1){
|
||||
SoundLoop.addLoop(new SoundLoopSpell.SoundLoopSpellDispenser(start, loop, end, spell, world, x, y, z, volume, pitch));
|
||||
}else{
|
||||
SoundLoop.addLoop(new SoundLoopSpell.SoundLoopSpellPosTimed(start, loop, end, spell, duration, x, y, z, volume, pitch));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<String> getSpellHUDSkins(){
|
||||
return GuiSpellDisplay.getSkinKeys();
|
||||
}
|
||||
|
||||
// SECTION Items
|
||||
// ===============================================================================================================
|
||||
|
||||
@Override
|
||||
public boolean shouldDisplayDiscovered(Spell spell, ItemStack stack){
|
||||
|
||||
EntityPlayerSP player = Minecraft.getMinecraft().player;
|
||||
|
||||
if(player == null) return false;
|
||||
|
||||
// Displayed recipe
|
||||
if(Minecraft.getMinecraft().currentScreen instanceof GuiMerchant){
|
||||
// It doesn't actually matter if the recipe is selected or not, since the itemstack will only ever
|
||||
// match one of them anyway - and we'd have to reflect into GuiMerchant to get the selected recipe
|
||||
MerchantRecipeList recipes = ((GuiMerchant)Minecraft.getMinecraft().currentScreen).getMerchant().getRecipes(player);
|
||||
if(recipes != null && recipes.stream().anyMatch(r -> r.getItemToSell() == stack)){
|
||||
// Spell books are always discovered when wizards are selling them
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// Recipe output slot
|
||||
// Required or players would be able to find out what the spell is without actually completing the trade
|
||||
if(player.openContainer instanceof ContainerMerchant){
|
||||
|
||||
if(((ContainerMerchant)player.openContainer).getMerchantInventory().getStackInSlot(2) == stack){
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if(!Wizardry.settings.discoveryMode) return true;
|
||||
if(player.isCreative()) return true;
|
||||
if(WizardData.get(player) != null && WizardData.get(player).hasSpellBeenDiscovered(spell)) return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public FontRenderer getFontRenderer(ItemStack stack){
|
||||
|
||||
@@ -216,12 +240,10 @@ public class ClientProxy extends CommonProxy {
|
||||
if(stack.getItem() instanceof ItemWand){
|
||||
spell = WandHelper.getCurrentSpell(stack);
|
||||
}else if(stack.getItem() instanceof ItemSpellBook || stack.getItem() instanceof ItemScroll){
|
||||
spell = Spell.get(stack.getItemDamage());
|
||||
spell = Spell.byMetadata(stack.getItemDamage());
|
||||
}
|
||||
|
||||
if(Minecraft.getMinecraft().player != null && Wizardry.settings.discoveryMode && WizardData.get(Minecraft.getMinecraft().player) != null
|
||||
&& !Minecraft.getMinecraft().player.capabilities.isCreativeMode
|
||||
&& !WizardData.get(Minecraft.getMinecraft().player).hasSpellBeenDiscovered(spell)){
|
||||
if(!shouldDisplayDiscovered(spell, stack)){
|
||||
return mixedFontRenderer;
|
||||
}
|
||||
|
||||
@@ -231,16 +253,14 @@ public class ClientProxy extends CommonProxy {
|
||||
@Override
|
||||
public String getScrollDisplayName(ItemStack scroll){
|
||||
|
||||
// Displays [Empty slot] if spell is continuous.
|
||||
Spell spell = Spell.get(scroll.getItemDamage());
|
||||
if(spell.isContinuous) spell = Spells.none;
|
||||
Spell spell = Spell.byMetadata(scroll.getItemDamage());
|
||||
|
||||
EntityPlayer player = Minecraft.getMinecraft().player;
|
||||
|
||||
boolean discovered = true;
|
||||
// It seems that this method is called when the world is loading, before thePlayer has been initialised.
|
||||
// If the player is null, the spell is assumed to be discovered.
|
||||
if(player != null && Wizardry.settings.discoveryMode && !player.capabilities.isCreativeMode && WizardData.get(player) != null
|
||||
if(player != null && Wizardry.settings.discoveryMode && !player.isCreative() && WizardData.get(player) != null
|
||||
&& !WizardData.get(player).hasSpellBeenDiscovered(spell)){
|
||||
discovered = false;
|
||||
}
|
||||
@@ -261,71 +281,59 @@ public class ClientProxy extends CommonProxy {
|
||||
return super.getConjuredBowDurability(stack);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addMultiLineDescription(List<String> tooltip, String key, Style style){
|
||||
String description = style.getFormattingCode() + I18n.format(key);
|
||||
tooltip.addAll(Minecraft.getMinecraft().fontRenderer.listFormattedStringToWidth(description, TOOLTIP_WRAP_WIDTH));
|
||||
}
|
||||
|
||||
// SECTION Particles
|
||||
// ===============================================================================================================
|
||||
|
||||
/** Use {@link ParticleWizardry#registerParticle(ResourceLocation, IWizardryParticleFactory)}, this is internal. */
|
||||
// I mean, it does exactly the same thing but I might want to make it do something else in future...
|
||||
public static void addParticleFactory(ResourceLocation name, IWizardryParticleFactory factory){
|
||||
factories.put(name, factory);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void spawnParticle(WizardryParticleType type, World world, double x, double y, double z, double velX, double velY, double velZ, int maxAge,
|
||||
float r, float g, float b, boolean doGravity, double radius){
|
||||
|
||||
// Colour values are now automatically clamped to between 0 and 1, as values outside this range seem to
|
||||
// cause strange effects in 1.10 (or more specifically, particles that are bright pink!)
|
||||
// TODO: This is a terrible dirty fix, but it'll do for now. Find a nicer way in future.
|
||||
if(type != WizardryParticleType.MAGIC_FIRE) r = MathHelper.clamp(r, 0, 1);
|
||||
g = MathHelper.clamp(g, 0, 1);
|
||||
b = MathHelper.clamp(b, 0, 1);
|
||||
|
||||
switch(type){
|
||||
|
||||
case BLIZZARD:
|
||||
Minecraft.getMinecraft().effectRenderer.addEffect(new ParticleBlizzard(world, maxAge, x, z, radius, y));
|
||||
break;
|
||||
case BRIGHT_DUST:
|
||||
Minecraft.getMinecraft().effectRenderer.addEffect(new ParticleDust(world, x, y, z, velX, velY, velZ, r, g, b, false));
|
||||
break;
|
||||
case DARK_MAGIC:
|
||||
Minecraft.getMinecraft().effectRenderer.addEffect(new ParticleDarkMagic(world, x, y, z, velX, velY, velZ, r, g, b));
|
||||
break;
|
||||
case DUST:
|
||||
Minecraft.getMinecraft().effectRenderer.addEffect(new ParticleDust(world, x, y, z, velX, velY, velZ, r, g, b, true));
|
||||
break;
|
||||
case ICE:
|
||||
Minecraft.getMinecraft().effectRenderer.addEffect(new ParticleIce(world, x, y, z, velX, velY, velZ, maxAge));
|
||||
break;
|
||||
case LEAF:
|
||||
Minecraft.getMinecraft().effectRenderer.addEffect(new ParticleLeaf(world, x, y, z, velX, velY, velZ, maxAge));
|
||||
break;
|
||||
case MAGIC_BUBBLE:
|
||||
Minecraft.getMinecraft().effectRenderer.addEffect(new ParticleGiantBubble(world, x, y, z, velX, velY, velZ));
|
||||
break;
|
||||
case MAGIC_FIRE:
|
||||
Minecraft.getMinecraft().effectRenderer.addEffect(new ParticleMagicFlame(world, x, y, z, velX, velY, velZ, maxAge, r == 0 ? 1 + world.rand.nextFloat() : r));
|
||||
break;
|
||||
case PATH:
|
||||
Minecraft.getMinecraft().effectRenderer.addEffect(new ParticlePath(world, x, y, z, velX, velY, velZ, r, g, b, maxAge));
|
||||
break;
|
||||
case SNOW:
|
||||
Minecraft.getMinecraft().effectRenderer.addEffect(new ParticleSnow(world, x, y, z, velX, velY, velZ));
|
||||
break;
|
||||
case SPARK:
|
||||
Minecraft.getMinecraft().effectRenderer.addEffect(new ParticleSpark(world, x, y, z, velX, velY, velZ));
|
||||
break;
|
||||
case SPARKLE:
|
||||
Minecraft.getMinecraft().effectRenderer.addEffect(new ParticleSparkle(world, x, y, z, velX, velY, velZ, r, g, b, maxAge, doGravity));
|
||||
break;
|
||||
case SPARKLE_ROTATING:
|
||||
Minecraft.getMinecraft().effectRenderer.addEffect(new ParticleRotatingSparkle(world, maxAge, x, z, radius, y, r, g, b));
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
public void registerParticles(){
|
||||
// I'll be a good programmer and use the API method rather than the one above. Lead by example, as they say...
|
||||
ParticleWizardry.registerParticle(Type.BEAM, ParticleBeam::new);
|
||||
ParticleWizardry.registerParticle(Type.BUFF, ParticleBuff::new);
|
||||
ParticleWizardry.registerParticle(Type.DARK_MAGIC, ParticleDarkMagic::new);
|
||||
ParticleWizardry.registerParticle(Type.DUST, ParticleDust::new);
|
||||
ParticleWizardry.registerParticle(Type.FLASH, ParticleFlash::new);
|
||||
ParticleWizardry.registerParticle(Type.ICE, ParticleIce::new);
|
||||
ParticleWizardry.registerParticle(Type.LEAF, ParticleLeaf::new);
|
||||
ParticleWizardry.registerParticle(Type.LIGHTNING, ParticleLightning::new);
|
||||
ParticleWizardry.registerParticle(Type.LIGHTNING_PULSE, ParticleLightningPulse::new);
|
||||
ParticleWizardry.registerParticle(Type.MAGIC_BUBBLE, ParticleMagicBubble::new);
|
||||
ParticleWizardry.registerParticle(Type.MAGIC_FIRE, ParticleMagicFlame::new);
|
||||
ParticleWizardry.registerParticle(Type.PATH, ParticlePath::new);
|
||||
ParticleWizardry.registerParticle(Type.SCORCH, ParticleScorch::new);
|
||||
ParticleWizardry.registerParticle(Type.SNOW, ParticleSnow::new);
|
||||
ParticleWizardry.registerParticle(Type.SPARK, ParticleSpark::new);
|
||||
ParticleWizardry.registerParticle(Type.SPARKLE, ParticleSparkle::new);
|
||||
ParticleWizardry.registerParticle(Type.SPHERE, ParticleSphere::new);
|
||||
ParticleWizardry.registerParticle(Type.SUMMON, ParticleSummon::new);
|
||||
ParticleWizardry.registerParticle(Type.VINE, ParticleVine::new);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ParticleWizardry createParticle(ResourceLocation type, World world, double x, double y, double z){
|
||||
IWizardryParticleFactory factory = factories.get(type);
|
||||
if(factory == null){
|
||||
Wizardry.logger.warn("Unrecognised particle type {} ! Ensure the particle is properly registered.", type);
|
||||
return null;
|
||||
}
|
||||
return factory.createParticle(world, x, y, z);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void spawnTornadoParticle(World world, double x, double y, double z, double velX, double velZ, double radius, int maxAge,
|
||||
IBlockState block, BlockPos pos){
|
||||
Minecraft.getMinecraft().effectRenderer.addEffect(new ParticleTornado(world, maxAge, x, z, radius, y, velX, velZ, block).setBlockPos(pos));// ,
|
||||
// world.rand.nextInt(6)));
|
||||
Minecraft.getMinecraft().effectRenderer.addEffect(new ParticleTornado(world, maxAge, x, z, radius, y, velX, velZ, block).setBlockPos(pos));// , world.rand.nextInt(6)));
|
||||
}
|
||||
|
||||
// SECTION Packet Handlers
|
||||
@@ -336,14 +344,12 @@ public class ClientProxy extends CommonProxy {
|
||||
|
||||
World world = Minecraft.getMinecraft().world;
|
||||
Entity caster = world.getEntityByID(message.casterID);
|
||||
Spell spell = Spell.get(message.spellID);
|
||||
Spell spell = Spell.byNetworkID(message.spellID);
|
||||
// Should always be true
|
||||
if(caster instanceof EntityPlayer){
|
||||
|
||||
((EntityPlayer)caster).setActiveHand(message.hand);
|
||||
|
||||
// Duration isn't needed because it only ever affects things server-side, and anything that is
|
||||
// seen client-side gets synced elsewhere.
|
||||
spell.cast(world, (EntityPlayer)caster, message.hand, 0, message.modifiers);
|
||||
|
||||
Source source = Source.OTHER;
|
||||
@@ -358,19 +364,34 @@ public class ClientProxy extends CommonProxy {
|
||||
|
||||
// No need to check if the spell succeeded, because the packet is only ever sent when it succeeds.
|
||||
// The handler for this event now deals with discovery.
|
||||
MinecraftForge.EVENT_BUS.post(new SpellCastEvent.Post((EntityPlayer)caster, spell, message.modifiers, source));
|
||||
MinecraftForge.EVENT_BUS.post(new SpellCastEvent.Post(source, spell, (EntityPlayer)caster, message.modifiers));
|
||||
|
||||
}else{
|
||||
Wizardry.logger.warn("Recieved a PacketCastSpell, but the caster ID was not the ID of a player");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleCastSpellAtPosPacket(PacketCastSpellAtPos.Message message){
|
||||
|
||||
World world = Minecraft.getMinecraft().world;
|
||||
Spell spell = Spell.byNetworkID(message.spellID);
|
||||
|
||||
spell.cast(world, message.position.x, message.position.y, message.position.z, message.direction, 0, message.duration, message.modifiers);
|
||||
|
||||
MinecraftForge.EVENT_BUS.post(new SpellCastEvent.Post(Source.COMMAND, spell, world, message.position.x, message.position.y, message.position.z, message.direction, message.modifiers));
|
||||
|
||||
if(spell.isContinuous){
|
||||
SpellEmitter.add(spell, world, message.position.x, message.position.y, message.position.z, message.direction, message.duration, message.modifiers);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleCastContinuousSpellPacket(PacketCastContinuousSpell.Message message){
|
||||
|
||||
World world = Minecraft.getMinecraft().world;
|
||||
Entity caster = world.getEntityByID(message.casterID);
|
||||
Spell spell = Spell.get(message.spellID);
|
||||
Spell spell = Spell.byNetworkID(message.spellID);
|
||||
// Should always be true
|
||||
if(caster instanceof EntityPlayer){
|
||||
|
||||
@@ -380,7 +401,7 @@ public class ClientProxy extends CommonProxy {
|
||||
if(data.isCasting()){
|
||||
WizardData.get((EntityPlayer)caster).stopCastingContinuousSpell();
|
||||
}else{
|
||||
WizardData.get((EntityPlayer)caster).startCastingContinuousSpell(spell, message.modifiers);
|
||||
WizardData.get((EntityPlayer)caster).startCastingContinuousSpell(spell, message.modifiers, message.duration);
|
||||
}
|
||||
}
|
||||
}else{
|
||||
@@ -394,7 +415,7 @@ public class ClientProxy extends CommonProxy {
|
||||
World world = Minecraft.getMinecraft().world;
|
||||
Entity caster = world.getEntityByID(message.casterID);
|
||||
Entity target = message.targetID == -1 ? null : world.getEntityByID(message.targetID);
|
||||
Spell spell = Spell.get(message.spellID);
|
||||
Spell spell = Spell.byNetworkID(message.spellID);
|
||||
// Should always be true
|
||||
if(caster instanceof EntityLiving){
|
||||
|
||||
@@ -403,7 +424,7 @@ public class ClientProxy extends CommonProxy {
|
||||
spell.cast(world, (EntityLiving)caster, message.hand, 0, (EntityLivingBase)target, message.modifiers);
|
||||
// Again, no need to check if the spell succeeded, because the packet is only ever sent when it
|
||||
// succeeds.
|
||||
MinecraftForge.EVENT_BUS.post(new SpellCastEvent.Post((EntityLiving)caster, spell, message.modifiers, Source.NPC));
|
||||
MinecraftForge.EVENT_BUS.post(new SpellCastEvent.Post(Source.NPC, spell, (EntityLiving)caster, message.modifiers));
|
||||
}
|
||||
|
||||
if(caster instanceof ISpellCaster){
|
||||
@@ -412,10 +433,41 @@ public class ClientProxy extends CommonProxy {
|
||||
((EntityLiving)caster).setAttackTarget((EntityLivingBase)target);
|
||||
}
|
||||
}
|
||||
}else{
|
||||
}else if(caster != null){
|
||||
Wizardry.logger.warn("Recieved a PacketNPCCastSpell, but the caster ID was not the ID of an EntityLiving");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleDispenserCastSpellPacket(PacketDispenserCastSpell.Message message){
|
||||
|
||||
World world = Minecraft.getMinecraft().world;
|
||||
|
||||
if(world.getTileEntity(message.pos) instanceof TileEntityDispenser){ // Should always be true
|
||||
|
||||
Spell spell = Spell.byNetworkID(message.spellID);
|
||||
|
||||
spell.cast(world, message.x, message.y, message.z, message.direction, 0, -1, message.modifiers);
|
||||
// No need to check if the spell succeeded, because the packet is only ever sent when it succeeds.
|
||||
MinecraftForge.EVENT_BUS.post(new SpellCastEvent.Post(Source.DISPENSER, spell, world, message.x, message.y,
|
||||
message.z, message.direction, message.modifiers));
|
||||
|
||||
if(spell.isContinuous || spell instanceof None){
|
||||
|
||||
DispenserCastingData data = DispenserCastingData.get((TileEntityDispenser)world.getTileEntity(message.pos));
|
||||
|
||||
if(spell.isContinuous){
|
||||
data.startCasting(spell, message.x, message.y, message.z, message.duration, message.modifiers);
|
||||
}else{
|
||||
data.stopCasting();
|
||||
}
|
||||
}
|
||||
|
||||
}else{
|
||||
Wizardry.logger.warn("Recieved a PacketDispenserCastSpell, but no tileEntity was found at the supplied location.");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleTransportationPacket(PacketTransportation.Message message){
|
||||
@@ -423,78 +475,150 @@ public class ClientProxy extends CommonProxy {
|
||||
World world = Minecraft.getMinecraft().world;
|
||||
Entity caster = world.getEntityByID(message.casterID);
|
||||
// Moved from when the packet is sent to when it is received; fixes the sound not playing in first person.
|
||||
caster.playSound(SoundEvents.BLOCK_PORTAL_TRAVEL, 1, 1);
|
||||
caster.playSound(WizardrySounds.SPELL_TRANSPORTATION_TRAVEL, 1, 1);
|
||||
|
||||
for(int i = 0; i < 20; i++){
|
||||
double radius = 1;
|
||||
double angle = world.rand.nextDouble() * Math.PI * 2;
|
||||
double x = caster.posX + radius * Math.cos(angle);
|
||||
float angle = world.rand.nextFloat() * (float)Math.PI * 2;
|
||||
double x = caster.posX + radius * MathHelper.cos(angle);
|
||||
double y = caster.getEntityBoundingBox().minY + world.rand.nextDouble() * 2;
|
||||
double z = caster.posZ + radius * Math.sin(angle);
|
||||
Minecraft.getMinecraft().effectRenderer
|
||||
.addEffect(new ParticleSparkle(world, x, y, z, 0, 0.02, 0, 0.6f, 1.0f, 0.6f, 80 + world.rand.nextInt(10)));
|
||||
double z = caster.posZ + radius * MathHelper.sin(angle);
|
||||
ParticleBuilder.create(Type.SPARKLE).pos(x, y, z).vel(0, 0.02, 0).clr(0.6f, 1, 0.6f)
|
||||
.time(80 + world.rand.nextInt(10)).spawn(world);
|
||||
}
|
||||
for(int i = 0; i < 20; i++){
|
||||
double radius = 1;
|
||||
double angle = world.rand.nextDouble() * Math.PI * 2;
|
||||
double x = caster.posX + radius * Math.cos(angle);
|
||||
float angle = world.rand.nextFloat() * (float)Math.PI * 2;
|
||||
double x = caster.posX + radius * MathHelper.cos(angle);
|
||||
double y = caster.getEntityBoundingBox().minY + world.rand.nextDouble() * 2;
|
||||
double z = caster.posZ + radius * Math.sin(angle);
|
||||
double z = caster.posZ + radius * MathHelper.sin(angle);
|
||||
world.spawnParticle(EnumParticleTypes.VILLAGER_HAPPY, x, y, z, 0, 0.02, 0);
|
||||
}
|
||||
for(int i = 0; i < 20; i++){
|
||||
double radius = 1;
|
||||
double angle = world.rand.nextDouble() * Math.PI * 2;
|
||||
double x = caster.posX + radius * Math.cos(angle);
|
||||
float angle = world.rand.nextFloat() * (float)Math.PI * 2;
|
||||
double x = caster.posX + radius * MathHelper.cos(angle);
|
||||
double y = caster.getEntityBoundingBox().minY + world.rand.nextDouble() * 2;
|
||||
double z = caster.posZ + radius * Math.sin(angle);
|
||||
double z = caster.posZ + radius * MathHelper.sin(angle);
|
||||
world.spawnParticle(EnumParticleTypes.ENCHANTMENT_TABLE, x, y, z, 0, 0.02, 0);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handlePlayerSyncPacket(Message message){
|
||||
public void handlePlayerSyncPacket(PacketPlayerSync.Message message){
|
||||
|
||||
WizardData properties = WizardData.get(Minecraft.getMinecraft().player);
|
||||
WizardData data = WizardData.get(Minecraft.getMinecraft().player);
|
||||
|
||||
if(properties != null){
|
||||
if(data != null){
|
||||
|
||||
properties.spellsDiscovered = message.spellsDiscovered;
|
||||
data.synchronisedRandom.setSeed(message.seed);
|
||||
data.spellsDiscovered = message.spellsDiscovered;
|
||||
|
||||
message.spellData.forEach(data::setVariable);
|
||||
|
||||
if(message.selectedMinionID == -1){
|
||||
properties.selectedMinion = null;
|
||||
data.selectedMinion = null;
|
||||
}else{
|
||||
Entity entity = Minecraft.getMinecraft().world.getEntityByID(message.selectedMinionID);
|
||||
|
||||
if(entity instanceof ISummonedCreature){
|
||||
properties.selectedMinion = new WeakReference<ISummonedCreature>((ISummonedCreature)entity);
|
||||
data.selectedMinion = new WeakReference<>((ISummonedCreature)entity);
|
||||
}else{
|
||||
properties.selectedMinion = null;
|
||||
data.selectedMinion = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleGlyphDataPacket(electroblob.wizardry.packet.PacketGlyphData.Message message){
|
||||
public void handleGlyphDataPacket(PacketGlyphData.Message message){
|
||||
|
||||
SpellGlyphData data = SpellGlyphData.get(Minecraft.getMinecraft().world);
|
||||
|
||||
data.randomNames = new HashMap<Spell, String>();
|
||||
data.randomDescriptions = new HashMap<Spell, String>();
|
||||
data.randomNames = new HashMap<>();
|
||||
data.randomDescriptions = new HashMap<>();
|
||||
|
||||
for(Spell spell : Spell.getSpells(Spell.allSpells)){
|
||||
// -1 because the none spell isn't included
|
||||
data.randomNames.put(spell, message.names.get(spell.id() - 1));
|
||||
data.randomDescriptions.put(spell, message.descriptions.get(spell.id() - 1));
|
||||
// This is a case where we must use the network ID, not the metadata
|
||||
data.randomNames.put(spell, message.names.get(spell.networkID() - 1));
|
||||
data.randomDescriptions.put(spell, message.descriptions.get(spell.networkID() - 1));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleClairvoyancePacket(electroblob.wizardry.packet.PacketClairvoyance.Message message){
|
||||
public void handleEmitterDataPacket(PacketEmitterData.Message message){
|
||||
message.emitters.forEach(e -> e.setWorld(Minecraft.getMinecraft().world)); // Do this as soon as possible!
|
||||
SpellEmitterData data = SpellEmitterData.get(Minecraft.getMinecraft().world);
|
||||
// We shouldn't need to clear the emitters because when a player logs in or changes dimension the client world
|
||||
// is wiped anyway, so the call to get() above should result in a fresh SpellEmitterData instance
|
||||
message.emitters.forEach(data::add);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleClairvoyancePacket(PacketClairvoyance.Message message){
|
||||
Clairvoyance.spawnPathPaticles(Minecraft.getMinecraft().world, message.path, message.durationMultiplier);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleAdvancementSyncPacket(PacketSyncAdvancements.Message message){
|
||||
GuiWizardHandbook.updateUnlockStatus(message.showToasts, message.completedAdvancements);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleEndSlowTimePacket(PacketEndSlowTime.Message message){
|
||||
Entity entity = Minecraft.getMinecraft().world.getEntityByID(message.hostID);
|
||||
if(entity instanceof EntityLivingBase) PotionSlowTime.unblockNearbyEntities((EntityLivingBase)entity);
|
||||
else Wizardry.logger.warn("Received a PacketEndSlowTime, but the entity ID did not match any living entity");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleResurrectionPacket(PacketResurrection.Message message){
|
||||
Entity entity = Minecraft.getMinecraft().world.getEntityByID(message.playerID);
|
||||
if(entity instanceof EntityPlayer){
|
||||
((Resurrection)Spells.resurrection).resurrect((EntityPlayer)entity);
|
||||
if(entity == Minecraft.getMinecraft().player){
|
||||
Minecraft.getMinecraft().world.spawnEntity(entity);
|
||||
Minecraft.getMinecraft().displayGuiScreen(null);
|
||||
}
|
||||
}
|
||||
else Wizardry.logger.warn("Received a PacketResurrection, but the entity ID did not match any player");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handlePossessionPacket(PacketPossession.Message message){
|
||||
|
||||
Entity entity = Minecraft.getMinecraft().world.getEntityByID(message.playerID);
|
||||
|
||||
if(entity instanceof EntityPlayer){
|
||||
|
||||
EntityPlayer player = (EntityPlayer)entity;
|
||||
|
||||
if(message.targetID == -1){
|
||||
((Possession)Spells.possession).endPossession(player);
|
||||
}else{
|
||||
Entity target = Minecraft.getMinecraft().world.getEntityByID(message.targetID);
|
||||
if(target instanceof EntityLiving){
|
||||
((Possession)Spells.possession).possess(player, (EntityLiving)target, message.duration);
|
||||
player.sendStatusMessage(new TextComponentTranslation("spell." + Spells.possession.getRegistryName()
|
||||
+ ".success", Minecraft.getMinecraft().gameSettings.keyBindSneak.getDisplayName()), true);
|
||||
}
|
||||
else Wizardry.logger.warn("Received a PacketPossession, but the target ID did not match any living entity");
|
||||
}
|
||||
}
|
||||
else Wizardry.logger.warn("Received a PacketPossession, but the player ID did not match any player");
|
||||
}
|
||||
|
||||
public void handleConquerShrinePacket(PacketConquerShrine.Message message){
|
||||
|
||||
TileEntity tileEntity = Minecraft.getMinecraft().world.getTileEntity(new BlockPos(message.x, message.y, message.z));
|
||||
|
||||
if(tileEntity instanceof TileEntityShrineCore){
|
||||
((TileEntityShrineCore)tileEntity).conquer();
|
||||
|
||||
}else Wizardry.logger.warn("Received a PacketConquerShrine, but there was no shrine core at the position sent");
|
||||
}
|
||||
|
||||
// SECTION Rendering
|
||||
// ===============================================================================================================
|
||||
|
||||
@@ -507,6 +631,7 @@ public class ClientProxy extends CommonProxy {
|
||||
@Override
|
||||
public void initialiseLayers(){
|
||||
LayerStone.initialiseLayers();
|
||||
LayerFrost.initialiseLayers();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -516,6 +641,12 @@ public class ClientProxy extends CommonProxy {
|
||||
// Yet another advantage to the new system: turns out you don't even need to register the renderer if you
|
||||
// just want the vanilla one for the mob you're extending.
|
||||
|
||||
// Luckily for us, the vanilla husk renderer is only parametrised to EntityZombie
|
||||
RenderingRegistry.registerEntityRenderingHandler(EntityHuskMinion.class, RenderHusk::new);
|
||||
// This now extends AbstractSkeleton so we need to bind the renderer ourselves
|
||||
RenderingRegistry.registerEntityRenderingHandler(EntitySkeletonMinion.class, RenderSkeleton::new);
|
||||
RenderingRegistry.registerEntityRenderingHandler(EntityStrayMinion.class, RenderStrayMinion::new);
|
||||
|
||||
// An anonymous class in a lambda expression! No point writing a separate class really, is there?
|
||||
RenderingRegistry.registerEntityRenderingHandler(EntityLightningWraith.class, manager -> new RenderBlaze(manager){
|
||||
@Override
|
||||
@@ -549,15 +680,15 @@ public class ClientProxy extends CommonProxy {
|
||||
RenderingRegistry.registerEntityRenderingHandler(EntityForceArrow.class, RenderForceArrow::new);
|
||||
|
||||
// Creatures
|
||||
RenderingRegistry.registerEntityRenderingHandler(EntitySpiritWolf.class, manager -> new RenderSpiritWolf(manager));
|
||||
RenderingRegistry.registerEntityRenderingHandler(EntitySpiritHorse.class, manager -> new RenderSpiritHorse(manager));
|
||||
RenderingRegistry.registerEntityRenderingHandler(EntitySpiritWolf.class, RenderSpiritWolf::new);
|
||||
RenderingRegistry.registerEntityRenderingHandler(EntitySpiritHorse.class, RenderSpiritHorse::new);
|
||||
RenderingRegistry.registerEntityRenderingHandler(EntityWizard.class, RenderWizard::new);
|
||||
RenderingRegistry.registerEntityRenderingHandler(EntityEvilWizard.class, RenderEvilWizard::new);
|
||||
RenderingRegistry.registerEntityRenderingHandler(EntityDecoy.class, RenderDecoy::new);
|
||||
|
||||
// Throwables
|
||||
RenderingRegistry.registerEntityRenderingHandler(EntitySparkBomb.class,
|
||||
manager -> new RenderProjectile(manager, 0.6f, new ResourceLocation(Wizardry.MODID, "textures/entity/spark_bomb.png"), false));
|
||||
manager -> new RenderProjectile(manager, 0.6f, new ResourceLocation(Wizardry.MODID, "textures/items/spark_bomb.png"), false));
|
||||
RenderingRegistry.registerEntityRenderingHandler(EntityFirebomb.class,
|
||||
manager -> new RenderProjectile(manager, 0.6f, new ResourceLocation(Wizardry.MODID, "textures/items/firebomb.png"), false));
|
||||
RenderingRegistry.registerEntityRenderingHandler(EntityPoisonBomb.class,
|
||||
@@ -576,21 +707,29 @@ public class ClientProxy extends CommonProxy {
|
||||
manager -> new RenderLightningDisc(manager, new ResourceLocation(Wizardry.MODID, "textures/entity/lightning_sigil.png"), 2.0f));
|
||||
RenderingRegistry.registerEntityRenderingHandler(EntitySmokeBomb.class,
|
||||
manager -> new RenderProjectile(manager, 0.6f, new ResourceLocation(Wizardry.MODID, "textures/items/smoke_bomb.png"), false));
|
||||
RenderingRegistry.registerEntityRenderingHandler(EntityEmber.class,
|
||||
manager -> new RenderProjectile(manager, 0.15f, new ResourceLocation(Wizardry.MODID, "textures/entity/ember.png"), false));
|
||||
RenderingRegistry.registerEntityRenderingHandler(EntityMagicFireball.class,
|
||||
manager -> new RenderProjectile(manager, 0.7f, new ResourceLocation(Wizardry.MODID, "textures/entity/fireball.png"), false));
|
||||
RenderingRegistry.registerEntityRenderingHandler(EntityLargeMagicFireball.class,
|
||||
manager -> new RenderProjectile(manager, 1.5f, new ResourceLocation(Wizardry.MODID, "textures/entity/fireball.png"), false));
|
||||
RenderingRegistry.registerEntityRenderingHandler(EntityIceball.class,
|
||||
manager -> new RenderProjectile(manager, 0.7f, new ResourceLocation(Wizardry.MODID, "textures/entity/iceball.png"), false));
|
||||
|
||||
// Effects and constructs
|
||||
RenderingRegistry.registerEntityRenderingHandler(EntityArc.class, RenderArc::new);
|
||||
RenderingRegistry.registerEntityRenderingHandler(EntityBlackHole.class, RenderBlackHole::new);
|
||||
RenderingRegistry.registerEntityRenderingHandler(EntityShield.class, RenderBlank::new);
|
||||
RenderingRegistry.registerEntityRenderingHandler(EntityBubble.class, RenderBubble::new);
|
||||
RenderingRegistry.registerEntityRenderingHandler(EntityHammer.class, RenderHammer::new);
|
||||
RenderingRegistry.registerEntityRenderingHandler(EntityIceSpike.class, RenderIceSpike::new);
|
||||
RenderingRegistry.registerEntityRenderingHandler(EntityForcefield.class, RenderForcefield::new);
|
||||
//RenderingRegistry.registerEntityRenderingHandler(EntityContainmentField.class, RenderContainmentField::new);
|
||||
|
||||
// Stuff that doesn't render
|
||||
RenderingRegistry.registerEntityRenderingHandler(EntityBlizzard.class, RenderBlank::new);
|
||||
RenderingRegistry.registerEntityRenderingHandler(EntityTornado.class, RenderBlank::new);
|
||||
RenderingRegistry.registerEntityRenderingHandler(EntityArrowRain.class, RenderBlank::new);
|
||||
RenderingRegistry.registerEntityRenderingHandler(EntityShadowWraith.class, RenderBlank::new);
|
||||
RenderingRegistry.registerEntityRenderingHandler(EntityForcefield.class, RenderBlank::new);
|
||||
RenderingRegistry.registerEntityRenderingHandler(EntityThunderbolt.class, RenderBlank::new);
|
||||
RenderingRegistry.registerEntityRenderingHandler(EntityStormElemental.class, RenderBlank::new);
|
||||
RenderingRegistry.registerEntityRenderingHandler(EntityEarthquake.class, RenderBlank::new);
|
||||
@@ -608,7 +747,8 @@ public class ClientProxy extends CommonProxy {
|
||||
RenderingRegistry.registerEntityRenderingHandler(EntityFireRing.class,
|
||||
manager -> new RenderFireRing(manager, new ResourceLocation(Wizardry.MODID, "textures/entity/ring_of_fire.png"), 5.0f));
|
||||
RenderingRegistry.registerEntityRenderingHandler(EntityDecay.class, RenderDecay::new);
|
||||
RenderingRegistry.registerEntityRenderingHandler(EntityLightningPulse.class, manager -> new RenderLightningPulse(manager, 8.0f));
|
||||
RenderingRegistry.registerEntityRenderingHandler(EntityCombustionRune.class,
|
||||
manager -> new RenderSigil(manager, new ResourceLocation(Wizardry.MODID, "textures/entity/combustion_rune.png"), 2.0f, true));
|
||||
|
||||
// TESRs
|
||||
ClientRegistry.bindTileEntitySpecialRenderer(TileEntityArcaneWorkbench.class, new RenderArcaneWorkbench());
|
||||
|
||||
@@ -0,0 +1,247 @@
|
||||
package electroblob.wizardry.client;
|
||||
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.gui.FontRenderer;
|
||||
import net.minecraft.client.gui.inventory.GuiContainer;
|
||||
import net.minecraft.client.renderer.*;
|
||||
import net.minecraft.client.renderer.vertex.DefaultVertexFormats;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.util.math.MathHelper;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
|
||||
/**
|
||||
* Utility class containing some useful static methods for drawing GUIs. Previously these were spread across the main
|
||||
* {@code WizardryUtilities} class and various individual GUI classes.
|
||||
*
|
||||
* @author Electroblob
|
||||
* @since Wizardry 4.2
|
||||
* @see MixedFontRenderer
|
||||
*/
|
||||
//@SideOnly(Side.CLIENT)
|
||||
public final class DrawingUtils {
|
||||
|
||||
/**
|
||||
* The integer colour for black passed into the font renderer methods. This used to be 0 but that's now white for
|
||||
* some reason, so I've made a it a constant in case it changes again.
|
||||
*/
|
||||
// I think this is actually ever-so-slightly lighter than pure black, but the difference is unnoticeable.
|
||||
public static final int BLACK = 1;
|
||||
|
||||
/**
|
||||
* Shorthand for {@link DrawingUtils#drawTexturedRect(int, int, int, int, int, int, int, int)} which draws the
|
||||
* entire texture (u and v are set to 0 and textureWidth and textureHeight are the same as width and height).
|
||||
*/
|
||||
public static void drawTexturedRect(int x, int y, int width, int height){
|
||||
drawTexturedRect(x, y, 0, 0, width, height, width, height);
|
||||
}
|
||||
|
||||
/**
|
||||
* Draws a textured rectangle, taking the size of the image and the bit needed into
|
||||
* account, unlike {@link net.minecraft.client.gui.Gui#drawTexturedModalRect(int, int, int, int, int, int)
|
||||
* Gui.drawTexturedModalRect(int, int, int, int, int, int)}, which is harcoded for only 256x256 textures. Also handy
|
||||
* for custom potion icons.
|
||||
*
|
||||
* @param x The x position of the rectangle
|
||||
* @param y The y position of the rectangle
|
||||
* @param u The x position of the top left corner of the section of the image wanted
|
||||
* @param v The y position of the top left corner of the section of the image wanted
|
||||
* @param width The width of the section
|
||||
* @param height The height of the section
|
||||
* @param textureWidth The width of the actual image.
|
||||
* @param textureHeight The height of the actual image.
|
||||
*/
|
||||
public static void drawTexturedRect(int x, int y, int u, int v, int width, int height, int textureWidth, int textureHeight){
|
||||
DrawingUtils.drawTexturedFlippedRect(x, y, u, v, width, height, textureWidth, textureHeight, false, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Draws a textured rectangle, taking the size of the image and the bit needed into
|
||||
* account, unlike {@link net.minecraft.client.gui.Gui#drawTexturedModalRect(int, int, int, int, int, int)
|
||||
* Gui.drawTexturedModalRect(int, int, int, int, int, int)}, which is harcoded for only 256x256 textures. Also handy
|
||||
* for custom potion icons. This version allows the texture to additionally be flipped in x and/or y.
|
||||
*
|
||||
* @param x The x position of the rectangle
|
||||
* @param y The y position of the rectangle
|
||||
* @param u The x position of the top left corner of the section of the image wanted
|
||||
* @param v The y position of the top left corner of the section of the image wanted
|
||||
* @param width The width of the section
|
||||
* @param height The height of the section
|
||||
* @param textureWidth The width of the actual image.
|
||||
* @param textureHeight The height of the actual image.
|
||||
* @param flipX Whether to flip the texture in the x direction.
|
||||
* @param flipY Whether to flip the texture in the y direction.
|
||||
*/
|
||||
public static void drawTexturedFlippedRect(int x, int y, int u, int v, int width, int height, int textureWidth, int textureHeight, boolean flipX, boolean flipY){
|
||||
|
||||
float f = 1F / (float)textureWidth;
|
||||
float f1 = 1F / (float)textureHeight;
|
||||
|
||||
int u1 = flipX ? u + width : u;
|
||||
int u2 = flipX ? u : u + width;
|
||||
int v1 = flipY ? v + height : v;
|
||||
int v2 = flipY ? v : v + height;
|
||||
|
||||
Tessellator tessellator = Tessellator.getInstance();
|
||||
BufferBuilder buffer = tessellator.getBuffer();
|
||||
|
||||
buffer.begin(org.lwjgl.opengl.GL11.GL_QUADS, net.minecraft.client.renderer.vertex.DefaultVertexFormats.POSITION_TEX);
|
||||
|
||||
buffer.pos((double)(x), (double)(y + height), 0).tex((double)((float)(u1) * f), (double)((float)(v2) * f1)).endVertex();
|
||||
buffer.pos((double)(x + width), (double)(y + height), 0).tex((double)((float)(u2) * f), (double)((float)(v2) * f1)).endVertex();
|
||||
buffer.pos((double)(x + width), (double)(y), 0).tex((double)((float)(u2) * f), (double)((float)(v1) * f1)).endVertex();
|
||||
buffer.pos((double)(x), (double)(y), 0).tex((double)((float)(u1) * f), (double)((float)(v1) * f1)).endVertex();
|
||||
|
||||
tessellator.draw();
|
||||
}
|
||||
|
||||
/**
|
||||
* Draws a textured rectangle, stretching the section of the image to fit the size given.
|
||||
*
|
||||
* @param x The x position of the rectangle
|
||||
* @param y The y position of the rectangle
|
||||
* @param u The x position of the top left corner of the section of the image wanted, expressed as a fraction of the
|
||||
* image width
|
||||
* @param v The y position of the top left corner of the section of the image wanted, expressed as a fraction of the
|
||||
* image width
|
||||
* @param finalWidth The width as rendered
|
||||
* @param finalHeight The height as rendered
|
||||
* @param width The width of the section, expressed as a fraction of the image width
|
||||
* @param height The height of the section, expressed as a fraction of the image width
|
||||
*/
|
||||
public static void drawTexturedStretchedRect(int x, int y, int u, int v, int finalWidth, int finalHeight, int width,
|
||||
int height){
|
||||
|
||||
Tessellator tessellator = Tessellator.getInstance();
|
||||
BufferBuilder buffer = tessellator.getBuffer();
|
||||
|
||||
buffer.begin(GL11.GL_QUADS, DefaultVertexFormats.POSITION_TEX);
|
||||
|
||||
buffer.pos((x), y + finalHeight, 0).tex(u, v + height).endVertex();
|
||||
buffer.pos(x + finalWidth, y + finalHeight, 0).tex(u + width, v + height).endVertex();
|
||||
buffer.pos(x + finalWidth, (y), 0).tex(u + width, v).endVertex();
|
||||
buffer.pos((x), (y), 0).tex(u, v).endVertex();
|
||||
|
||||
tessellator.draw();
|
||||
}
|
||||
|
||||
/**
|
||||
* Mixes the two given opaque colours in the proportion specified.
|
||||
* @param colour1 The first colour to mix, as a 6-digit hexadecimal.
|
||||
* @param colour2 The second colour to mix, as a 6-digit hexadecimal.
|
||||
* @param proportion The proportion of the second colour; will be clamped to between 0 and 1.
|
||||
* @return The resulting colour, as a 6-digit hexadecimal.
|
||||
*/
|
||||
public static int mix(int colour1, int colour2, float proportion){
|
||||
|
||||
proportion = MathHelper.clamp(proportion, 0, 1);
|
||||
|
||||
int r1 = colour1 >> 16 & 255;
|
||||
int g1 = colour1 >> 8 & 255;
|
||||
int b1 = colour1 & 255;
|
||||
int r2 = colour2 >> 16 & 255;
|
||||
int g2 = colour2 >> 8 & 255;
|
||||
int b2 = colour2 & 255;
|
||||
|
||||
int r = (int)(r1 + (r2-r1) * proportion);
|
||||
int g = (int)(g1 + (g2-g1) * proportion);
|
||||
int b = (int)(b1 + (b2-b1) * proportion);
|
||||
|
||||
return (r << 16) + (g << 8) + b;
|
||||
}
|
||||
|
||||
/**
|
||||
* Makes the given opaque colour translucent with the given opacity.
|
||||
* @param colour An integer colour code, should be a 6-digit hexadecimal (i.e. opaque).
|
||||
* @param opacity The opacity to apply to the given colour, as a fraction between 0 and 1.
|
||||
* @return The resulting integer colour code, which will be an 8-digit hexadecimal.
|
||||
*/
|
||||
public static int makeTranslucent(int colour, float opacity){
|
||||
return colour + ((int)(0xff * opacity * 0x01000000));
|
||||
}
|
||||
|
||||
/**
|
||||
* Draws the given string at the given position, scaling it if it does not fit within the given width.
|
||||
* @param font A {@code FontRenderer} object.
|
||||
* @param text The text to display.
|
||||
* @param x The x position of the top-left corner of the text.
|
||||
* @param y The y position of the top-left corner of the text.
|
||||
* @param scale The scale that the text should normally be if it does not exceed the maximum width.
|
||||
* @param colour The colour to render the text in, supports translucency.
|
||||
* @param width The maximum width of the text. <i>This is not scaled; you should pass in the width of the actual
|
||||
* area of the screen in which the text needs to fit, regardless of the scale parameter.</i>
|
||||
* @param centre Whether to adjust the y position such that the centre of the text lines up with where its centre
|
||||
* would be if it was not scaled (automatically or manually).
|
||||
* @param alignR True to right-align the text, false for normal left alignment.
|
||||
*/
|
||||
public static void drawScaledStringToWidth(FontRenderer font, String text, float x, float y, float scale, int colour, float width, boolean centre, boolean alignR){
|
||||
|
||||
float textWidth = font.getStringWidth(text) * scale;
|
||||
float textHeight = font.FONT_HEIGHT * scale;
|
||||
|
||||
if(textWidth > width){
|
||||
scale *= width/textWidth;
|
||||
}else if(alignR){ // Alignment makes no difference if the string fills the entire width
|
||||
x += width - textWidth;
|
||||
}
|
||||
|
||||
if(centre) y += (font.FONT_HEIGHT - textHeight)/2;
|
||||
|
||||
DrawingUtils.drawScaledTranslucentString(font, text, x, y, scale, colour);
|
||||
}
|
||||
|
||||
/** Draws the given string at the given position, scaling the text by the specified factor. Also enables blending to
|
||||
* render text in semitransparent colours (e.g. 0x88ffffff). */
|
||||
public static void drawScaledTranslucentString(FontRenderer font, String text, float x, float y, float scale, int colour){
|
||||
|
||||
GlStateManager.pushMatrix();
|
||||
GlStateManager.enableBlend();
|
||||
GlStateManager.scale(scale, scale, scale);
|
||||
// Because we scaled the entire rendering space, the coordinates have to be scaled inversely
|
||||
x /= scale;
|
||||
y /= scale;
|
||||
font.drawStringWithShadow(text, x, y, colour);
|
||||
GlStateManager.disableBlend();
|
||||
GlStateManager.popMatrix();
|
||||
}
|
||||
|
||||
/**
|
||||
* Draws an itemstack and (optionally) its tooltip, directly. Mainly intended for use outside of GUI classes, since
|
||||
* most of the GL state changes done in this method (which are the main reason it exists at all) are already done
|
||||
* when drawing a GUI.
|
||||
*
|
||||
* @param gui An instance of a GUI class.
|
||||
* @param stack The itemstack to draw.
|
||||
* @param x The x position of the left-hand edge of the itemstack.
|
||||
* @param y The y position of the top edge of the itemstack.
|
||||
* @param mouseX The x position of the mouse, used for tooltip positioning.
|
||||
* @param mouseY The y position of the mouse, used for tooltip positioning.
|
||||
* @param tooltip Whether to draw the tooltip.
|
||||
*/
|
||||
public static void drawItemAndTooltip(GuiContainer gui, ItemStack stack, int x, int y, int mouseX, int mouseY, boolean tooltip){
|
||||
|
||||
RenderItem renderItem = Minecraft.getMinecraft().getRenderItem();
|
||||
GlStateManager.pushMatrix();
|
||||
RenderHelper.enableGUIStandardItemLighting();
|
||||
GlStateManager.disableLighting();
|
||||
GlStateManager.enableRescaleNormal();
|
||||
GlStateManager.enableColorMaterial();
|
||||
GlStateManager.enableLighting();
|
||||
renderItem.zLevel = 100.0F;
|
||||
|
||||
if(!stack.isEmpty()){
|
||||
renderItem.renderItemAndEffectIntoGUI(stack, x, y);
|
||||
renderItem.renderItemOverlays(Minecraft.getMinecraft().fontRenderer, stack, x, y);
|
||||
|
||||
if(tooltip){
|
||||
gui.drawHoveringText(gui.getItemToolTip(stack), mouseX + gui.getXSize()/2 - gui.width/2,
|
||||
mouseY + gui.getYSize()/2 - gui.height/2);
|
||||
}
|
||||
}
|
||||
|
||||
GlStateManager.popMatrix();
|
||||
GlStateManager.enableLighting();
|
||||
GlStateManager.enableDepth();
|
||||
RenderHelper.enableStandardItemLighting();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,253 +0,0 @@
|
||||
package electroblob.wizardry.client;
|
||||
|
||||
import org.lwjgl.input.Keyboard;
|
||||
|
||||
import electroblob.wizardry.SpellGlyphData;
|
||||
import electroblob.wizardry.WizardData;
|
||||
import electroblob.wizardry.Wizardry;
|
||||
import electroblob.wizardry.constants.Element;
|
||||
import electroblob.wizardry.item.ItemWand;
|
||||
import electroblob.wizardry.packet.PacketControlInput;
|
||||
import electroblob.wizardry.packet.WizardryPacketHandler;
|
||||
import electroblob.wizardry.spell.Spell;
|
||||
import electroblob.wizardry.tileentity.ContainerArcaneWorkbench;
|
||||
import electroblob.wizardry.tileentity.TileEntityArcaneWorkbench;
|
||||
import electroblob.wizardry.util.WandHelper;
|
||||
import electroblob.wizardry.util.WizardryUtilities;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.gui.GuiButton;
|
||||
import net.minecraft.client.gui.inventory.GuiContainer;
|
||||
import net.minecraft.client.renderer.GlStateManager;
|
||||
import net.minecraft.client.resources.I18n;
|
||||
import net.minecraft.entity.player.InventoryPlayer;
|
||||
import net.minecraft.inventory.IInventory;
|
||||
import net.minecraft.inventory.Slot;
|
||||
import net.minecraft.item.Item;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
import net.minecraftforge.fml.common.network.simpleimpl.IMessage;
|
||||
|
||||
public class GuiArcaneWorkbench extends GuiContainer {
|
||||
|
||||
private GuiButton applyBtn;
|
||||
private static final ResourceLocation texture = new ResourceLocation(Wizardry.MODID,
|
||||
"textures/gui/arcane_workbench.png");
|
||||
|
||||
private IInventory playerInventory;
|
||||
private IInventory arcaneWorkbenchInventory;
|
||||
|
||||
private final int tooltipWidth = 164;
|
||||
|
||||
// We report the actual size of the GUI to Minecraft when a wand is in so JEI doesn't overdraw it.
|
||||
// For calculations, we use the size without the tooltip.
|
||||
private final int xSizeNoTip = 176;
|
||||
|
||||
public GuiArcaneWorkbench(InventoryPlayer invPlayer, TileEntityArcaneWorkbench entity){
|
||||
super(new ContainerArcaneWorkbench(invPlayer, entity));
|
||||
this.playerInventory = invPlayer;
|
||||
this.arcaneWorkbenchInventory = entity;
|
||||
xSize = xSizeNoTip;
|
||||
ySize = 220;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void drawScreen(int p_73863_1_, int p_73863_2_, float p_73863_3_){
|
||||
|
||||
// Tests if there is a wand in the workbench and edits the positioning accordingly
|
||||
if(this.inventorySlots.getSlot(ContainerArcaneWorkbench.WAND_SLOT).getHasStack() && this.inventorySlots
|
||||
.getSlot(ContainerArcaneWorkbench.WAND_SLOT).getStack().getItem() instanceof ItemWand){
|
||||
xSize = xSizeNoTip + tooltipWidth;
|
||||
guiLeft = (this.width - this.xSize) / 2;
|
||||
this.applyBtn.x = (this.width - tooltipWidth) / 2 + 48;
|
||||
}else{
|
||||
xSize = xSizeNoTip;
|
||||
guiLeft = (this.width - this.xSize) / 2;
|
||||
this.applyBtn.x = this.width / 2 + 48;
|
||||
}
|
||||
|
||||
if(this.inventorySlots.getSlot(ContainerArcaneWorkbench.WAND_SLOT).getHasStack()){
|
||||
this.applyBtn.enabled = true;
|
||||
}else{
|
||||
this.applyBtn.enabled = false;
|
||||
}
|
||||
|
||||
super.drawScreen(p_73863_1_, p_73863_2_, p_73863_3_);
|
||||
|
||||
// Required now, or item mouseover tooltips won't render.
|
||||
this.renderHoveredToolTip(p_73863_1_, p_73863_2_);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void drawGuiContainerBackgroundLayer(float f, int mouseX, int mouseY){
|
||||
|
||||
GlStateManager.pushAttrib();
|
||||
|
||||
GlStateManager.color(1F, 1F, 1F, 1F);
|
||||
Minecraft.getMinecraft().renderEngine.bindTexture(texture);
|
||||
|
||||
// Main inventory
|
||||
drawTexturedModalRect(guiLeft, guiTop, 0, 0, xSizeNoTip, ySize);
|
||||
|
||||
// Changing slots
|
||||
for(int i = 0; i < ContainerArcaneWorkbench.CRYSTAL_SLOT; i++){
|
||||
Slot slot = this.inventorySlots.getSlot(i);
|
||||
if(slot.xPos >= 0 && slot.yPos >= 0)
|
||||
this.drawTexturedModalRect(guiLeft + slot.xPos - 10, guiTop + slot.yPos - 10, 0, 220, 36, 36);
|
||||
}
|
||||
|
||||
// Tooltip only drawn if there is a wand
|
||||
if(this.inventorySlots.getSlot(ContainerArcaneWorkbench.WAND_SLOT).getHasStack() && this.inventorySlots
|
||||
.getSlot(ContainerArcaneWorkbench.WAND_SLOT).getStack().getItem() instanceof ItemWand){
|
||||
|
||||
// Tooltip box
|
||||
drawTexturedModalRect(guiLeft + xSizeNoTip, guiTop, xSizeNoTip, 0, 256 - xSizeNoTip - 4, ySize);
|
||||
drawTexturedModalRect(guiLeft + 252, guiTop, xSizeNoTip + 4, 0, tooltipWidth - 2 * (256 - xSizeNoTip - 4), ySize);
|
||||
drawTexturedModalRect(guiLeft + xSize - (256 - xSizeNoTip - 4), guiTop, xSizeNoTip + 4, 0,
|
||||
256 - xSizeNoTip - 4, ySize);
|
||||
|
||||
ItemStack wand = this.inventorySlots.getSlot(ContainerArcaneWorkbench.WAND_SLOT).getStack();
|
||||
|
||||
Spell[] spells = WandHelper.getSpells(wand);
|
||||
|
||||
int i = 0;
|
||||
|
||||
for(Spell spell : spells){
|
||||
|
||||
boolean discovered = true;
|
||||
|
||||
if(!this.mc.player.capabilities.isCreativeMode && WizardData.get(this.mc.player) != null){
|
||||
discovered = WizardData.get(this.mc.player).hasSpellBeenDiscovered(spell);
|
||||
}
|
||||
// As of Wizardry 1.2, the icons have been split off into their own texture files to allow for add-on
|
||||
// mods to add their own.
|
||||
Minecraft.getMinecraft().renderEngine
|
||||
.bindTexture(discovered ? spell.element.getIcon() : Element.MAGIC.getIcon());
|
||||
|
||||
// Renders the little element icon
|
||||
WizardryUtilities.drawTexturedRect(guiLeft + xSizeNoTip + 5, guiTop + 34 + 10 * i++, 8, 8);
|
||||
}
|
||||
|
||||
int x = 0;
|
||||
int y = guiTop + 50 + spells.length * 10;
|
||||
|
||||
// Look how much shorter this is with the WandHelper class!
|
||||
for(Item item : WandHelper.getSpecialUpgrades()){
|
||||
|
||||
int level = WandHelper.getUpgradeLevel(wand, item);
|
||||
|
||||
if(level > 0){
|
||||
ItemStack stack = new ItemStack(item, level);
|
||||
GlStateManager.enableDepth();
|
||||
this.itemRender.renderItemAndEffectIntoGUI(stack, guiLeft + xSizeNoTip + 6 + x, y);
|
||||
this.itemRender.renderItemOverlayIntoGUI(this.fontRenderer, stack, guiLeft + xSizeNoTip + 6 + x, y,
|
||||
null);
|
||||
x += 18;
|
||||
GlStateManager.disableDepth();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Minecraft.getMinecraft().renderEngine.bindTexture(texture);
|
||||
|
||||
// Fixes the bug that caused the slot hightlight to render opaque. I don't know why it works, it just works!
|
||||
GlStateManager.disableBlend();
|
||||
GlStateManager.enableAlpha();
|
||||
|
||||
GlStateManager.popAttrib();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void drawGuiContainerForegroundLayer(int mouseX, int mouseY){
|
||||
|
||||
this.fontRenderer
|
||||
.drawString(this.arcaneWorkbenchInventory.hasCustomName() ? this.arcaneWorkbenchInventory.getName()
|
||||
: I18n.format(this.arcaneWorkbenchInventory.getName()), 8, 6, 4210752);
|
||||
this.fontRenderer.drawString(this.playerInventory.hasCustomName() ? this.playerInventory.getName()
|
||||
: I18n.format(this.playerInventory.getName()), 8, this.ySize - 96 + 2, 4210752);
|
||||
|
||||
if(this.inventorySlots.getSlot(ContainerArcaneWorkbench.WAND_SLOT).getHasStack() && this.inventorySlots
|
||||
.getSlot(ContainerArcaneWorkbench.WAND_SLOT).getStack().getItem() instanceof ItemWand){
|
||||
|
||||
ItemStack wand = this.inventorySlots.getSlot(ContainerArcaneWorkbench.WAND_SLOT).getStack();
|
||||
|
||||
this.fontRenderer.drawStringWithShadow("\u00A7f" + wand.getDisplayName(), xSizeNoTip + 6, 6, 0);
|
||||
this.fontRenderer.drawStringWithShadow(
|
||||
"\u00A77" + I18n.format("container." + Wizardry.MODID + ":arcane_workbench.mana") + " "
|
||||
+ (wand.getMaxDamage() - wand.getItemDamage()) + "/" + wand.getMaxDamage(),
|
||||
xSizeNoTip + 6, 20, 0);
|
||||
|
||||
Spell[] spells = WandHelper.getSpells(wand);
|
||||
|
||||
int y = 34;
|
||||
|
||||
for(Spell spell : spells){
|
||||
|
||||
boolean discovered = true;
|
||||
|
||||
if(!this.mc.player.capabilities.isCreativeMode && WizardData.get(this.mc.player) != null){
|
||||
discovered = WizardData.get(this.mc.player).hasSpellBeenDiscovered(spell);
|
||||
}
|
||||
|
||||
if(discovered){
|
||||
this.fontRenderer.drawStringWithShadow(spell.getDisplayNameWithFormatting(), xSizeNoTip + 16, y, 0);
|
||||
}else{
|
||||
this.mc.standardGalacticFontRenderer.drawStringWithShadow(
|
||||
"\u00A79" + SpellGlyphData.getGlyphName(spell, this.mc.world), xSizeNoTip + 16, y, 0);
|
||||
}
|
||||
y += 10;
|
||||
}
|
||||
|
||||
if(WandHelper.getTotalUpgrades(wand) > 0){
|
||||
|
||||
this.fontRenderer.drawStringWithShadow(
|
||||
"\u00A7f" + I18n.format("container." + Wizardry.MODID + ":arcane_workbench.upgrades"), xSizeNoTip + 6, y + 6, 0);
|
||||
|
||||
int x = 0;
|
||||
y = 50 + spells.length * 10;
|
||||
// Wand upgrade tooltips
|
||||
for(Item item : WandHelper.getSpecialUpgrades()){
|
||||
|
||||
int level = WandHelper.getUpgradeLevel(wand, item);
|
||||
|
||||
if(level > 0){
|
||||
// The javadoc for isPointInRegion is ambiguous; what it means is that the REGION is
|
||||
// relative to the GUI but the POINT isn't.
|
||||
if(isPointInRegion(xSizeNoTip + 6 + x, y, 16, 16, mouseX, mouseY)){
|
||||
ItemStack stack = new ItemStack(item, level);
|
||||
this.renderToolTip(stack, mouseX - guiLeft, mouseY - guiTop);
|
||||
}
|
||||
x += 18;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initGui(){
|
||||
this.mc.player.openContainer = this.inventorySlots;
|
||||
this.guiLeft = (this.width - this.xSize) / 2;
|
||||
this.guiTop = (this.height - this.ySize) / 2;
|
||||
Keyboard.enableRepeatEvents(true);
|
||||
this.buttonList.clear();
|
||||
this.buttonList.add(this.applyBtn = new GuiButtonApply(0, this.width / 2 + 48, this.height / 2 + 3));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onGuiClosed(){
|
||||
super.onGuiClosed();
|
||||
Keyboard.enableRepeatEvents(false);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void actionPerformed(GuiButton button){
|
||||
if(button.enabled){
|
||||
if(button.id == 0){
|
||||
// Packet building
|
||||
IMessage msg = new PacketControlInput.Message(PacketControlInput.ControlType.APPLY_BUTTON);
|
||||
WizardryPacketHandler.net.sendToServer(msg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,42 +0,0 @@
|
||||
package electroblob.wizardry.client;
|
||||
|
||||
import electroblob.wizardry.Wizardry;
|
||||
import electroblob.wizardry.util.WizardryUtilities;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.gui.GuiButton;
|
||||
import net.minecraft.client.resources.I18n;
|
||||
import net.minecraftforge.fml.relauncher.Side;
|
||||
import net.minecraftforge.fml.relauncher.SideOnly;
|
||||
|
||||
@SideOnly(Side.CLIENT)
|
||||
class GuiButtonApply extends GuiButton {
|
||||
|
||||
public GuiButtonApply(int id, int x, int y){
|
||||
super(id, x, y, 32, 16, I18n.format("container." + Wizardry.MODID + ":arcane_workbench.apply"));
|
||||
}
|
||||
|
||||
@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 = 36;
|
||||
int l = 220;
|
||||
int colour = 14737632;
|
||||
|
||||
if(this.enabled){
|
||||
if(this.hovered){
|
||||
k += this.width * 2;
|
||||
colour = 16777120;
|
||||
}
|
||||
}else{
|
||||
k += this.width;
|
||||
colour = 10526880;
|
||||
}
|
||||
|
||||
WizardryUtilities.drawTexturedRect(this.x, this.y, k, l, this.width, this.height, 256, 256);
|
||||
this.drawCenteredString(minecraft.fontRenderer, this.displayString, this.x + this.width / 2,
|
||||
this.y + (this.height - 8) / 2, colour);
|
||||
}
|
||||
}
|
||||
@@ -1,47 +0,0 @@
|
||||
package electroblob.wizardry.client;
|
||||
|
||||
import electroblob.wizardry.Wizardry;
|
||||
import electroblob.wizardry.util.WizardryUtilities;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.gui.GuiButton;
|
||||
import net.minecraft.client.renderer.GlStateManager;
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
import net.minecraftforge.fml.relauncher.Side;
|
||||
import net.minecraftforge.fml.relauncher.SideOnly;
|
||||
|
||||
@SideOnly(Side.CLIENT)
|
||||
class GuiButtonTurnPage extends GuiButton {
|
||||
|
||||
/** True for pointing right (next page), false for pointing left (previous page). */
|
||||
private final boolean nextPage;
|
||||
|
||||
private static final ResourceLocation texture = new ResourceLocation(Wizardry.MODID, "textures/gui/handbook.png");
|
||||
|
||||
public GuiButtonTurnPage(int id, int x, int y, boolean isNextPage){
|
||||
super(id, x, y, 23, 13, "");
|
||||
this.nextPage = isNextPage;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void drawButton(Minecraft minecraft, int mouseX, int mouseY, float partialTicks){
|
||||
|
||||
if(this.visible){
|
||||
|
||||
boolean flag = mouseX >= this.x && mouseY >= this.y && mouseX < this.x + this.width && mouseY < this.y + this.height;
|
||||
GlStateManager.color(1.0F, 1.0F, 1.0F, 1.0F);
|
||||
minecraft.getTextureManager().bindTexture(texture);
|
||||
int k = 0;
|
||||
int l = 192;
|
||||
|
||||
if(flag){
|
||||
k += 23;
|
||||
}
|
||||
|
||||
if(!this.nextPage){
|
||||
l += 13;
|
||||
}
|
||||
|
||||
WizardryUtilities.drawTexturedRect(this.x, this.y, k, l, 23, 13, 288, 256);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,80 +0,0 @@
|
||||
package electroblob.wizardry.client;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import electroblob.wizardry.Settings;
|
||||
import electroblob.wizardry.Wizardry;
|
||||
import net.minecraft.client.gui.GuiScreen;
|
||||
import net.minecraft.client.resources.I18n;
|
||||
import net.minecraftforge.common.config.ConfigElement;
|
||||
import net.minecraftforge.common.config.Configuration;
|
||||
import net.minecraftforge.fml.client.config.DummyConfigElement.DummyCategoryElement;
|
||||
import net.minecraftforge.fml.client.config.GuiConfig;
|
||||
import net.minecraftforge.fml.client.config.GuiConfigEntries;
|
||||
import net.minecraftforge.fml.client.config.GuiConfigEntries.CategoryEntry;
|
||||
import net.minecraftforge.fml.client.config.IConfigElement;
|
||||
|
||||
public class GuiConfigWizardry extends GuiConfig {
|
||||
|
||||
public GuiConfigWizardry(GuiScreen parent){
|
||||
super(parent, getConfigEntries(), Wizardry.MODID, false, false,
|
||||
Wizardry.NAME + " - " + I18n.format("config." + Wizardry.MODID + ".title.general"));
|
||||
// this.titleLine2 = "File location: " + Wizardry.config.getConfigFile().getAbsolutePath();
|
||||
}
|
||||
|
||||
private static List<IConfigElement> getConfigEntries(){
|
||||
List<IConfigElement> configList = new ArrayList<IConfigElement>(1);
|
||||
configList.add(new DummyCategoryElement("spellsConfig", "config." + Wizardry.MODID + ".category." + Settings.SPELLS_CATEGORY,
|
||||
SpellsCategory.class));
|
||||
configList.add(new DummyCategoryElement("resistancesConfig",
|
||||
"config.wizardry.category." + Settings.RESISTANCES_CATEGORY, ResistancesCategory.class));
|
||||
configList.addAll(new ConfigElement(Wizardry.settings.getConfigCategory(Configuration.CATEGORY_GENERAL))
|
||||
.getChildElements());
|
||||
return configList;
|
||||
}
|
||||
|
||||
/** Spells category of the config gui. This adds a button which opens up the spells category config. */
|
||||
public static class SpellsCategory extends CategoryEntry {
|
||||
public SpellsCategory(GuiConfig owningScreen, GuiConfigEntries owningEntryList, IConfigElement prop){
|
||||
super(owningScreen, owningEntryList, prop);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected GuiScreen buildChildScreen(){
|
||||
// This GuiConfig object specifies the configID of the object and as such will force-save when it is closed.
|
||||
// The parent GuiConfig object's entryList will also be refreshed to reflect the changes.
|
||||
GuiConfig spellsMenu = new GuiConfig(this.owningScreen,
|
||||
(new ConfigElement(Wizardry.settings.getConfigCategory(Settings.SPELLS_CATEGORY)))
|
||||
.getChildElements(),
|
||||
this.owningScreen.modID, Settings.SPELLS_CATEGORY, false, false,
|
||||
Wizardry.NAME + " - " + I18n.format("config." + Wizardry.MODID + ".title." + Settings.SPELLS_CATEGORY));
|
||||
|
||||
spellsMenu.titleLine2 = I18n.format("config." + Wizardry.MODID + ".subtitle." + Settings.SPELLS_CATEGORY);
|
||||
|
||||
return spellsMenu;
|
||||
}
|
||||
}
|
||||
|
||||
/** Resistances category of the config gui. */
|
||||
public static class ResistancesCategory extends CategoryEntry {
|
||||
public ResistancesCategory(GuiConfig owningScreen, GuiConfigEntries owningEntryList, IConfigElement prop){
|
||||
super(owningScreen, owningEntryList, prop);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected GuiScreen buildChildScreen(){
|
||||
// This GuiConfig object specifies the configID of the object and as such will force-save when it is closed.
|
||||
// The parent GuiConfig object's entryList will also be refreshed to reflect the changes.
|
||||
GuiConfig idsMenu = new GuiConfig(this.owningScreen,
|
||||
(new ConfigElement(Wizardry.settings.getConfigCategory(Settings.RESISTANCES_CATEGORY)))
|
||||
.getChildElements(),
|
||||
this.owningScreen.modID, Settings.RESISTANCES_CATEGORY, false, false,
|
||||
Wizardry.NAME + " - " + I18n.format("config." + Wizardry.MODID + ".title." + Settings.RESISTANCES_CATEGORY));
|
||||
|
||||
idsMenu.titleLine2 = I18n.format("config." + Wizardry.MODID + ".subtitle." + Settings.RESISTANCES_CATEGORY);
|
||||
|
||||
return idsMenu;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,112 +0,0 @@
|
||||
package electroblob.wizardry.client;
|
||||
|
||||
import org.lwjgl.input.Keyboard;
|
||||
|
||||
import electroblob.wizardry.SpellGlyphData;
|
||||
import electroblob.wizardry.WizardData;
|
||||
import electroblob.wizardry.Wizardry;
|
||||
import electroblob.wizardry.constants.Tier;
|
||||
import electroblob.wizardry.registry.Spells;
|
||||
import electroblob.wizardry.spell.Spell;
|
||||
import electroblob.wizardry.util.WizardryUtilities;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.gui.GuiScreen;
|
||||
import net.minecraft.entity.player.EntityPlayer;
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
|
||||
public class GuiSpellBook extends GuiScreen {
|
||||
|
||||
private int xSize, ySize;
|
||||
private Spell spell;
|
||||
|
||||
private static final ResourceLocation texture = new ResourceLocation(Wizardry.MODID, "textures/gui/spellbook.png");
|
||||
|
||||
public GuiSpellBook(Spell spell){
|
||||
super();
|
||||
xSize = 288;
|
||||
ySize = 180;
|
||||
this.spell = spell;
|
||||
}
|
||||
|
||||
/**
|
||||
* Draws the screen and all the components in it.
|
||||
*/
|
||||
public void drawScreen(int par1, int par2, float par3){
|
||||
|
||||
int xPos = this.width / 2 - xSize / 2;
|
||||
int yPos = this.height / 2 - this.ySize / 2;
|
||||
|
||||
EntityPlayer player = Minecraft.getMinecraft().player;
|
||||
|
||||
boolean discovered = true;
|
||||
if(Wizardry.settings.discoveryMode && !player.capabilities.isCreativeMode && WizardData.get(player) != null
|
||||
&& !WizardData.get(player).hasSpellBeenDiscovered(spell)){
|
||||
discovered = false;
|
||||
}
|
||||
|
||||
// Draws spell illustration on opposite page, underneath the book so it shows through the hole.
|
||||
Minecraft.getMinecraft().renderEngine.bindTexture(discovered ? spell.getIcon() : Spells.none.getIcon());
|
||||
WizardryUtilities.drawTexturedRect(xPos + 145, yPos + 20, 0, 0, 128, 128, 128, 128);
|
||||
|
||||
Minecraft.getMinecraft().renderEngine.bindTexture(texture);
|
||||
WizardryUtilities.drawTexturedRect(xPos, yPos, 0, 0, xSize, ySize, xSize, 256);
|
||||
|
||||
super.drawScreen(par1, par2, par3);
|
||||
|
||||
if(discovered){
|
||||
this.fontRenderer.drawString(spell.getDisplayName(), xPos + 17, yPos + 14, 0);
|
||||
this.fontRenderer.drawString(spell.type.getDisplayName(), xPos + 17, yPos + 25, 0x777777);
|
||||
}else{
|
||||
this.mc.standardGalacticFontRenderer.drawString(SpellGlyphData.getGlyphName(spell, player.world), xPos + 17,
|
||||
yPos + 14, 0);
|
||||
this.mc.standardGalacticFontRenderer.drawString(spell.type.getDisplayName(), xPos + 17, yPos + 25,
|
||||
0x777777);
|
||||
}
|
||||
|
||||
this.fontRenderer.drawString("-------------------", xPos + 17, yPos + 34, 0);
|
||||
|
||||
if(spell.tier == Tier.BASIC){
|
||||
// Basic is usually white but this doesn't show up.
|
||||
this.fontRenderer.drawString("Tier: \u00A77" + Tier.BASIC.getDisplayName(), xPos + 17, yPos + 44, 0);
|
||||
}else{
|
||||
this.fontRenderer.drawString("Tier: " + spell.tier.getDisplayNameWithFormatting(), xPos + 17, yPos + 44,
|
||||
0);
|
||||
}
|
||||
|
||||
String element = "Element: " + spell.element.getFormattingCode() + spell.element.getDisplayName();
|
||||
if(!discovered) element = "Element: ?";
|
||||
this.fontRenderer.drawString(element, xPos + 17, yPos + 56, 0);
|
||||
|
||||
String manaCost = "Mana Cost: " + spell.cost;
|
||||
if(spell.isContinuous) manaCost = "Mana Cost: " + spell.cost + "/second";
|
||||
if(!discovered) manaCost = "Mana Cost: ?";
|
||||
this.fontRenderer.drawString(manaCost, xPos + 17, yPos + 68, 0);
|
||||
|
||||
if(discovered){
|
||||
this.fontRenderer.drawSplitString(spell.getDescription(), xPos + 17, yPos + 82, 118, 0);
|
||||
}else{
|
||||
this.mc.standardGalacticFontRenderer.drawSplitString(
|
||||
SpellGlyphData.getGlyphDescription(spell, player.world), xPos + 17, yPos + 82, 118, 0);
|
||||
}
|
||||
|
||||
/* // Word wrapping int charNumber = 0; int lineNumber = 0;
|
||||
*
|
||||
* while(charNumber < spell.desc.length()){ int lineLength = 0; String line; if(spell.desc.length() - charNumber
|
||||
* > 22){ for(int i = charNumber; i < charNumber+23; i++){ if(spell.desc.charAt(i) == ' '){ lineLength = i -
|
||||
* charNumber; } } line = spell.desc.substring(charNumber, charNumber + lineLength); }else{ line =
|
||||
* spell.desc.substring(charNumber, spell.desc.length()); charNumber = spell.desc.length(); }
|
||||
* this.fontRendererObj.drawString("\u00A7o" + line, xPos+17, yPos+82+10*lineNumber, 0);
|
||||
* charNumber+=(lineLength+1); lineNumber++; } */
|
||||
}
|
||||
|
||||
public void initGui(){
|
||||
super.initGui();
|
||||
Keyboard.enableRepeatEvents(true);
|
||||
this.buttonList.clear();
|
||||
}
|
||||
|
||||
public void onGuiClosed(){
|
||||
super.onGuiClosed();
|
||||
Keyboard.enableRepeatEvents(false);
|
||||
}
|
||||
}
|
||||
@@ -1,154 +0,0 @@
|
||||
package electroblob.wizardry.client;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import electroblob.wizardry.Settings.GuiPosition;
|
||||
import electroblob.wizardry.SpellGlyphData;
|
||||
import electroblob.wizardry.WizardData;
|
||||
import electroblob.wizardry.Wizardry;
|
||||
import electroblob.wizardry.constants.Constants;
|
||||
import electroblob.wizardry.item.ItemWand;
|
||||
import electroblob.wizardry.registry.Spells;
|
||||
import electroblob.wizardry.registry.WizardryItems;
|
||||
import electroblob.wizardry.registry.WizardryPotions;
|
||||
import electroblob.wizardry.spell.Spell;
|
||||
import electroblob.wizardry.util.WandHelper;
|
||||
import electroblob.wizardry.util.WizardryUtilities;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.gui.FontRenderer;
|
||||
import net.minecraft.client.gui.Gui;
|
||||
import net.minecraft.client.renderer.GlStateManager;
|
||||
import net.minecraft.client.renderer.GlStateManager.DestFactor;
|
||||
import net.minecraft.client.renderer.GlStateManager.SourceFactor;
|
||||
import net.minecraft.entity.player.EntityPlayer;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
import net.minecraftforge.client.event.RenderGameOverlayEvent;
|
||||
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
|
||||
|
||||
public class GuiSpellDisplay extends Gui {
|
||||
|
||||
private Minecraft mc;
|
||||
|
||||
private static final ResourceLocation hudTexture = new ResourceLocation(Wizardry.MODID, "textures/gui/spell_hud.png");
|
||||
|
||||
public GuiSpellDisplay(Minecraft par1Minecraft){
|
||||
super();
|
||||
this.mc = par1Minecraft;
|
||||
}
|
||||
|
||||
@SubscribeEvent
|
||||
public void draw(RenderGameOverlayEvent event){
|
||||
|
||||
EntityPlayer player = this.mc.player;
|
||||
|
||||
// If the player has a wand in each hand, only displays for the one in the main hand.
|
||||
|
||||
ItemStack wand = player.getHeldItemMainhand();
|
||||
|
||||
if(!(wand.getItem() instanceof ItemWand)){
|
||||
wand = player.getHeldItemOffhand();
|
||||
// If the player isn't holding a wand, then nothing else needs to be done.
|
||||
if(!(wand.getItem() instanceof ItemWand)) return;
|
||||
}
|
||||
|
||||
int width = event.getResolution().getScaledWidth();
|
||||
int height = event.getResolution().getScaledHeight();
|
||||
|
||||
Spell spell = WandHelper.getCurrentSpell(wand);
|
||||
int cooldown = WandHelper.getCurrentCooldown(wand);
|
||||
|
||||
float cooldownMultiplier = 1.0f - WandHelper.getUpgradeLevel(wand, WizardryItems.cooldown_upgrade) * Constants.COOLDOWN_REDUCTION_PER_LEVEL;
|
||||
|
||||
if(player.isPotionActive(WizardryPotions.font_of_mana)){
|
||||
// Dividing by this rather than setting it takes upgrades and font of mana into account simultaneously
|
||||
cooldownMultiplier /= 2 + player.getActivePotionEffect(WizardryPotions.font_of_mana).getAmplifier();
|
||||
}
|
||||
|
||||
// Coordinates of the top left corner of the HUD.
|
||||
int left = 0;
|
||||
int top = 0;
|
||||
boolean mirror = false;
|
||||
|
||||
if(Wizardry.settings.spellHUDPosition == GuiPosition.BOTTOM_LEFT){
|
||||
left = 0;
|
||||
top = height - 36;
|
||||
}else if(Wizardry.settings.spellHUDPosition == GuiPosition.TOP_LEFT){
|
||||
left = 0;
|
||||
top = 0;
|
||||
}else if(Wizardry.settings.spellHUDPosition == GuiPosition.TOP_RIGHT){
|
||||
left = width - 128;
|
||||
top = 0;
|
||||
mirror = true;
|
||||
}else if(Wizardry.settings.spellHUDPosition == GuiPosition.BOTTOM_RIGHT){
|
||||
left = width - 128;
|
||||
top = height - 36;
|
||||
mirror = true;
|
||||
}
|
||||
|
||||
boolean discovered = true;
|
||||
|
||||
if(!player.capabilities.isCreativeMode && WizardData.get(player) != null){
|
||||
discovered = WizardData.get(player).hasSpellBeenDiscovered(spell);
|
||||
}
|
||||
|
||||
if(event.getType() == RenderGameOverlayEvent.ElementType.TEXT){
|
||||
|
||||
// Makes spells greyed out if they are in cooldown or if the player has the arcane jammer effect
|
||||
String colour = cooldown > 0 || player.isPotionActive(WizardryPotions.arcane_jammer) ? "\u00A78" : spell.element.getFormattingCode();
|
||||
if(!discovered) colour = "\u00A79";
|
||||
String spellName = discovered ? spell.getDisplayName() : SpellGlyphData.getGlyphName(spell, player.world);
|
||||
FontRenderer font = discovered ? this.mc.fontRenderer : this.mc.standardGalacticFontRenderer;
|
||||
|
||||
int maxWidth = 90;
|
||||
|
||||
if(font.getStringWidth(spellName) <= maxWidth){
|
||||
// Single line is rendered more centrally
|
||||
font.drawStringWithShadow(colour + spellName, mirror ? left + 5 : left + 41, top + 13, 0xffffffff);
|
||||
|
||||
}else{
|
||||
|
||||
int lineNumber = 0;
|
||||
|
||||
List<String> lines = font.listFormattedStringToWidth(spellName, maxWidth);
|
||||
|
||||
for(Object line : lines){
|
||||
if(line instanceof String){
|
||||
font.drawStringWithShadow(colour + (String)line, mirror ? left + 5 : left + 41, top + 6 + 11 * lineNumber, 0xffffffff);
|
||||
}
|
||||
lineNumber++;
|
||||
}
|
||||
}
|
||||
|
||||
}else if(event.getType() == RenderGameOverlayEvent.ElementType.HOTBAR){
|
||||
|
||||
GlStateManager.pushAttrib();
|
||||
|
||||
GlStateManager.enableBlend();
|
||||
GlStateManager.blendFunc(SourceFactor.SRC_ALPHA, DestFactor.ONE_MINUS_SRC_ALPHA);
|
||||
GlStateManager.color(1, 1, 1);
|
||||
|
||||
this.mc.renderEngine.bindTexture(hudTexture);
|
||||
|
||||
// Background of spell hud
|
||||
this.drawTexturedModalRect(left, top, 0, mirror ? 36 : 0, 128, 36);
|
||||
|
||||
// Cooldown bar
|
||||
if(cooldown > 0){
|
||||
this.drawTexturedModalRect(mirror ? left + 5 : left + 41, height - 8, 128, 6, 82, 6);
|
||||
|
||||
int l = (int)(((double)(spell.cooldown * cooldownMultiplier - cooldown) / (double)(spell.cooldown * cooldownMultiplier)) * 82);
|
||||
|
||||
this.drawTexturedModalRect(mirror ? left + 5 : left + 41, height - 8, 128, 0, l, 6);
|
||||
}
|
||||
|
||||
// Spell illustration
|
||||
this.mc.renderEngine.bindTexture(discovered ? spell.getIcon() : Spells.none.getIcon());
|
||||
|
||||
WizardryUtilities.drawTexturedRect(mirror ? left + 94 : left + 2, top + 2, 0, 0, 32, 32, 32, 32);
|
||||
|
||||
GlStateManager.popAttrib();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,709 +0,0 @@
|
||||
package electroblob.wizardry.client;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.commons.lang3.tuple.ImmutablePair;
|
||||
import org.apache.commons.lang3.tuple.Pair;
|
||||
import org.lwjgl.input.Keyboard;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
|
||||
import electroblob.wizardry.Wizardry;
|
||||
import electroblob.wizardry.constants.Constants;
|
||||
import electroblob.wizardry.constants.Element;
|
||||
import electroblob.wizardry.constants.Tier;
|
||||
import electroblob.wizardry.registry.WizardryBlocks;
|
||||
import electroblob.wizardry.registry.WizardryItems;
|
||||
import electroblob.wizardry.util.WizardryUtilities;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.gui.GuiButton;
|
||||
import net.minecraft.client.gui.GuiScreen;
|
||||
import net.minecraft.client.renderer.BufferBuilder;
|
||||
import net.minecraft.client.renderer.GlStateManager;
|
||||
import net.minecraft.client.renderer.RenderHelper;
|
||||
import net.minecraft.client.renderer.Tessellator;
|
||||
import net.minecraft.client.renderer.vertex.DefaultVertexFormats;
|
||||
import net.minecraft.init.Blocks;
|
||||
import net.minecraft.init.Items;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.util.NonNullList;
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
|
||||
public class GuiWizardHandbook extends GuiScreen {
|
||||
|
||||
private int xSize, ySize;
|
||||
private int pageNumber = 0;
|
||||
|
||||
private static final int PAGE_WIDTH = 120;
|
||||
/**
|
||||
* The integer colour for black passed into the font renderer methods. This used to be 0 but that's now white for
|
||||
* some reason, so I've made a it a constant in case it changes again.
|
||||
*/
|
||||
// I think this is actually ever-so-slightly lighter than pure black, but the difference is unnoticeable.
|
||||
private static final int BLACK = 1;
|
||||
|
||||
public static final ResourceLocation regularHandbook = new ResourceLocation(Wizardry.MODID, "textures/gui/handbook.png");
|
||||
public static final ResourceLocation ore = new ResourceLocation(Wizardry.MODID, "textures/gui/ore_picture.png");
|
||||
public static final ResourceLocation crystal = new ResourceLocation(Wizardry.MODID, "textures/items/magic_crystal.png");
|
||||
public static final ResourceLocation workbenchGui = new ResourceLocation(Wizardry.MODID, "textures/gui/arcane_workbench.png");
|
||||
public static final ResourceLocation craftingGrids = new ResourceLocation(Wizardry.MODID, "textures/gui/handbook_recipes.png");
|
||||
|
||||
private List<ArrayList<String>> text;
|
||||
private List<Section> sections;
|
||||
|
||||
private static final List<Pair<ItemStack, NonNullList<NonNullList<ItemStack>>>> RECIPES = new ArrayList<>();
|
||||
|
||||
private int guiPage, imagePage;
|
||||
|
||||
public GuiWizardHandbook(){
|
||||
super();
|
||||
xSize = 288;
|
||||
ySize = 180;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void drawScreen(int mouseX, int mouseY, float par3){
|
||||
|
||||
int xPos = this.width / 2 - xSize / 2;
|
||||
int yPos = this.height / 2 - this.ySize / 2;
|
||||
|
||||
// Tests for crafting recipes section
|
||||
if(pageNumber >= (sections.get(sections.size() - 1).pageNumber - 1) / 2
|
||||
&& pageNumber < (sections.get(sections.size() - 1).pageNumber - 1) / 2 + 4){
|
||||
Minecraft.getMinecraft().renderEngine.bindTexture(craftingGrids);
|
||||
}else{
|
||||
Minecraft.getMinecraft().renderEngine.bindTexture(regularHandbook);
|
||||
}
|
||||
|
||||
WizardryUtilities.drawTexturedRect(xPos, yPos, 0, 0, xSize, ySize, xSize, 256);
|
||||
|
||||
// Arcane workbench gui picture
|
||||
if(pageNumber == (this.guiPage - 1) / 2){
|
||||
Minecraft.getMinecraft().renderEngine.bindTexture(workbenchGui);
|
||||
this.drawTexturedModalRect(this.guiPage % 2 == 1 ? xPos + 17 : this.width / 2 + 7, yPos + 14, 28, 12, 120,
|
||||
118);
|
||||
}
|
||||
|
||||
// Magic crystal and crystal ore images
|
||||
if(pageNumber == (this.imagePage - 1) / 2){
|
||||
|
||||
Minecraft.getMinecraft().renderEngine.bindTexture(ore);
|
||||
WizardryUtilities.drawTexturedRect(this.imagePage % 2 == 1 ? xPos + 17 : this.width / 2 + 7, yPos + 80, 0,
|
||||
0, 64, 64, 64, 64);
|
||||
|
||||
Minecraft.getMinecraft().renderEngine.bindTexture(crystal);
|
||||
drawTexturedStretchedRect(this.imagePage % 2 == 1 ? xPos + 17 + 64 : this.width / 2 + 7 + 62, yPos + 80, 0,
|
||||
0, 64, 64, 1, 1);
|
||||
|
||||
}
|
||||
|
||||
this.fontRenderer.drawString("" + (pageNumber * 2 + 1), xPos + xSize / 4 - 3, yPos + ySize - 20, 0);
|
||||
this.fontRenderer.drawString("" + (pageNumber * 2 + 2), xPos + 3 * xSize / 4 - 5, yPos + ySize - 20, 0);
|
||||
|
||||
super.drawScreen(mouseX, mouseY, par3);
|
||||
|
||||
int lineNumber = 0;
|
||||
|
||||
if(pageNumber == 1){
|
||||
for(Section s : sections){
|
||||
s.drawContents();
|
||||
}
|
||||
}else{
|
||||
for(Section s : sections){
|
||||
s.hideButton();
|
||||
}
|
||||
}
|
||||
|
||||
for(String paragraph : text.get(pageNumber * 2)){
|
||||
|
||||
this.fontRenderer.drawSplitString(paragraph, xPos + 17,
|
||||
yPos + 14 + lineNumber * this.fontRenderer.FONT_HEIGHT, PAGE_WIDTH, BLACK);
|
||||
|
||||
List<String> list = new ArrayList<String>(
|
||||
this.fontRenderer.listFormattedStringToWidth(paragraph, GuiWizardHandbook.PAGE_WIDTH));
|
||||
|
||||
lineNumber += list.size();
|
||||
}
|
||||
|
||||
lineNumber = 0;
|
||||
|
||||
// Prevents crash when the last page is blank (and hence is not in the list of pages)
|
||||
if(text.size() > pageNumber * 2 + 1){
|
||||
for(String paragraph : text.get(pageNumber * 2 + 1)){
|
||||
|
||||
// First page is centred
|
||||
if(pageNumber == 0){
|
||||
int startX = this.width / 2 + 7 + PAGE_WIDTH / 2
|
||||
- this.fontRenderer.getStringWidth(paragraph) / 2;
|
||||
this.fontRenderer.drawSplitString(paragraph, startX,
|
||||
yPos + 14 + lineNumber * this.fontRenderer.FONT_HEIGHT, PAGE_WIDTH, BLACK);
|
||||
}else{
|
||||
this.fontRenderer.drawSplitString(paragraph, this.width / 2 + 7,
|
||||
yPos + 14 + lineNumber * this.fontRenderer.FONT_HEIGHT, PAGE_WIDTH, BLACK);
|
||||
}
|
||||
|
||||
List<String> list = new ArrayList<String>(
|
||||
this.fontRenderer.listFormattedStringToWidth(paragraph, GuiWizardHandbook.PAGE_WIDTH));
|
||||
|
||||
lineNumber += list.size();
|
||||
}
|
||||
}
|
||||
|
||||
// Which page of the recipes this is
|
||||
int recipePage = pageNumber - (sections.get(sections.size() - 1).pageNumber - 1) / 2;
|
||||
|
||||
if(recipePage >= 0 && recipePage < 4){
|
||||
// 4 recipes per page, hence the recipePage*4
|
||||
this.renderCraftingRecipe(xPos + 23, yPos + 39, mouseX, mouseY, RECIPES.get(recipePage*4).getRight(), RECIPES.get(recipePage*4).getLeft());
|
||||
this.renderCraftingRecipe(xPos + 23, yPos + 98, mouseX, mouseY, RECIPES.get(recipePage*4+1).getRight(), RECIPES.get(recipePage*4+1).getLeft());
|
||||
this.renderCraftingRecipe(xPos + 156, yPos + 39, mouseX, mouseY, RECIPES.get(recipePage*4+2).getRight(), RECIPES.get(recipePage*4+2).getLeft());
|
||||
this.renderCraftingRecipe(xPos + 156, yPos + 98, mouseX, mouseY, RECIPES.get(recipePage*4+3).getRight(), RECIPES.get(recipePage*4+3).getLeft());
|
||||
|
||||
// Tooltips are rendered after recipes to prevent tooltips on the left appearing behind items on the right.
|
||||
this.renderCraftingTooltips(xPos + 23, yPos + 39, mouseX, mouseY, RECIPES.get(recipePage*4).getRight(), RECIPES.get(recipePage*4).getLeft());
|
||||
this.renderCraftingTooltips(xPos + 23, yPos + 98, mouseX, mouseY, RECIPES.get(recipePage*4+1).getRight(), RECIPES.get(recipePage*4+1).getLeft());
|
||||
this.renderCraftingTooltips(xPos + 156, yPos + 39, mouseX, mouseY, RECIPES.get(recipePage*4+2).getRight(), RECIPES.get(recipePage*4+2).getLeft());
|
||||
this.renderCraftingTooltips(xPos + 156, yPos + 98, mouseX, mouseY, RECIPES.get(recipePage*4+3).getRight(), RECIPES.get(recipePage*4+3).getLeft());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// TODO: In 1.12, this all needs redoing nicely. With the crafting system halfway through changing in 1.11.2, this
|
||||
// isn't worth doing until then.
|
||||
|
||||
private void renderCraftingRecipe(int xPos, int yPos, int mouseX, int mouseY, NonNullList<NonNullList<ItemStack>> craftingGrid,
|
||||
ItemStack craftingResult){
|
||||
|
||||
GlStateManager.pushMatrix();
|
||||
RenderHelper.enableGUIStandardItemLighting();
|
||||
GlStateManager.disableLighting();
|
||||
GlStateManager.enableRescaleNormal();
|
||||
GL11.glEnable(GL11.GL_COLOR_MATERIAL);
|
||||
GlStateManager.enableLighting();
|
||||
itemRender.zLevel = 100.0F;
|
||||
|
||||
for(int i = 0; i < craftingGrid.size(); i++){
|
||||
for(int j = 0; j < craftingGrid.get(i).size(); j++){
|
||||
ItemStack stack = craftingGrid.get(i).get(j);
|
||||
if(!stack.isEmpty()){
|
||||
itemRender.renderItemAndEffectIntoGUI(stack, xPos + 18 * i, yPos + 18 * j);
|
||||
itemRender.renderItemOverlays(this.fontRenderer, stack, xPos + 18 * i,
|
||||
yPos + 18 * j);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(!craftingResult.isEmpty()){
|
||||
itemRender.renderItemAndEffectIntoGUI(craftingResult, xPos + 86, yPos + 18);
|
||||
itemRender.renderItemOverlays(this.fontRenderer, craftingResult, xPos + 86, yPos + 18);
|
||||
}
|
||||
|
||||
GlStateManager.popMatrix();
|
||||
GlStateManager.enableLighting();
|
||||
GL11.glEnable(GL11.GL_DEPTH_TEST);
|
||||
RenderHelper.enableStandardItemLighting();
|
||||
|
||||
}
|
||||
|
||||
private void renderCraftingTooltips(int xPos, int yPos, int mouseX, int mouseY, NonNullList<NonNullList<ItemStack>> craftingGrid,
|
||||
ItemStack craftingResult){
|
||||
|
||||
int guiLeft = this.width / 2 - xSize / 2;
|
||||
int guiTop = this.height / 2 - this.ySize / 2;
|
||||
|
||||
GlStateManager.pushMatrix();
|
||||
RenderHelper.enableGUIStandardItemLighting();
|
||||
GlStateManager.disableLighting();
|
||||
GlStateManager.enableRescaleNormal();
|
||||
GL11.glEnable(GL11.GL_COLOR_MATERIAL);
|
||||
itemRender.zLevel = 0.0F;
|
||||
GlStateManager.disableLighting();
|
||||
|
||||
for(int i = 0; i < craftingGrid.size(); i++){
|
||||
for(int j = 0; j < craftingGrid.get(i).size(); j++){
|
||||
ItemStack stack = craftingGrid.get(i).get(j);
|
||||
if(!stack.isEmpty()
|
||||
&& isPointInRegion(xPos + 18 * i, yPos + 18 * j, 16, 16, mouseX + guiLeft, mouseY + guiTop)){
|
||||
this.renderToolTip(stack, mouseX, mouseY);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(!craftingResult.isEmpty() && isPointInRegion(xPos + 86, yPos + 18, 16, 16, mouseX + guiLeft, mouseY + guiTop)){
|
||||
this.renderToolTip(craftingResult, mouseX, mouseY);
|
||||
}
|
||||
|
||||
GlStateManager.popMatrix();
|
||||
GlStateManager.enableLighting();
|
||||
GL11.glEnable(GL11.GL_DEPTH_TEST);
|
||||
RenderHelper.enableStandardItemLighting();
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initGui(){
|
||||
|
||||
super.initGui();
|
||||
Keyboard.enableRepeatEvents(true);
|
||||
|
||||
int nextButtonId = 0;
|
||||
|
||||
this.buttonList.clear();
|
||||
this.buttonList.add(new GuiButtonTurnPage(nextButtonId++, this.width / 2 + this.xSize / 2 - 22 - 23,
|
||||
this.height / 2 + this.ySize / 2 - 10 - 13, true));
|
||||
this.buttonList.add(new GuiButtonTurnPage(nextButtonId++, this.width / 2 - this.xSize / 2 + 21,
|
||||
this.height / 2 + this.ySize / 2 - 10 - 13, false));
|
||||
|
||||
text = new ArrayList<ArrayList<String>>(1);
|
||||
sections = new ArrayList<Section>(1);
|
||||
|
||||
BufferedReader bufferedreader = null;
|
||||
|
||||
String textFilepath = Wizardry.MODID + ":texts/handbook_"
|
||||
+ Minecraft.getMinecraft().getLanguageManager().getCurrentLanguage().getLanguageCode() + ".txt";
|
||||
|
||||
try{
|
||||
|
||||
bufferedreader = new BufferedReader(new InputStreamReader(
|
||||
this.mc.getResourceManager().getResource(new ResourceLocation(textFilepath)).getInputStream(),
|
||||
StandardCharsets.UTF_8));
|
||||
|
||||
}catch (IOException e){
|
||||
|
||||
Wizardry.logger.info(
|
||||
"Wizard handbook text file missing for the current language. Using default (English - US) instead.");
|
||||
|
||||
textFilepath = Wizardry.MODID + ":texts/handbook_en_us.txt";
|
||||
|
||||
try {
|
||||
|
||||
bufferedreader = new BufferedReader(new InputStreamReader(
|
||||
this.mc.getResourceManager().getResource(new ResourceLocation(textFilepath)).getInputStream(),
|
||||
StandardCharsets.UTF_8));
|
||||
|
||||
} catch (IOException x){
|
||||
Wizardry.logger.error("Couldn't find file: " + Wizardry.MODID + "/assets/texts/handbook_en_us.txt. The file may be"
|
||||
+ "missing; please try re-downloading and reinstalling Wizardry.", x);
|
||||
}
|
||||
}
|
||||
|
||||
if(bufferedreader != null){
|
||||
|
||||
try{
|
||||
|
||||
String paragraph = bufferedreader.readLine();
|
||||
ArrayList<String> page = new ArrayList<String>(1);
|
||||
|
||||
int linesPerPage = 16;
|
||||
|
||||
int lineNumber = 0;
|
||||
|
||||
while(paragraph != null){
|
||||
|
||||
// System.out.println(paragraph);
|
||||
|
||||
if(paragraph.contains("PAGEBREAK") || lineNumber >= linesPerPage){
|
||||
|
||||
text.add(page);
|
||||
|
||||
page = new ArrayList<String>(1);
|
||||
|
||||
lineNumber = 0;
|
||||
|
||||
if(paragraph.contains("PAGEBREAK")) paragraph = bufferedreader.readLine();
|
||||
|
||||
}else if(paragraph.contains("LINEBREAK")){
|
||||
|
||||
lineNumber++;
|
||||
|
||||
page.add("");
|
||||
|
||||
paragraph = bufferedreader.readLine();
|
||||
|
||||
}else if(paragraph.contains("SECTION")){
|
||||
|
||||
sections.add(
|
||||
new Section(paragraph.replace("SECTION ", ""), text.size() + 1, this.width / 2 + 7,
|
||||
this.height / 2 - this.ySize / 2 + 14
|
||||
+ (sections.size() + 2) * this.fontRenderer.FONT_HEIGHT,
|
||||
nextButtonId++));
|
||||
paragraph = bufferedreader.readLine();
|
||||
|
||||
}else if(paragraph.contains("IMAGE")){
|
||||
|
||||
if(paragraph.contains("WORKBENCH")){
|
||||
this.guiPage = text.size() + 1;
|
||||
}else if(paragraph.contains("CRYSTAL")){
|
||||
this.imagePage = text.size() + 1;
|
||||
}
|
||||
|
||||
paragraph = bufferedreader.readLine();
|
||||
|
||||
}else{
|
||||
|
||||
paragraph = paragraph.replaceAll("NEXT_SPELL_KEY", Keyboard.getKeyName(ClientProxy.NEXT_SPELL.getKeyCode()));
|
||||
paragraph = paragraph.replaceAll("PREVIOUS_SPELL_KEY", Keyboard.getKeyName(ClientProxy.PREVIOUS_SPELL.getKeyCode()));
|
||||
paragraph = paragraph.replaceAll("MANA_PER_CRYSTAL_MINUS_30", "" + (Constants.MANA_PER_CRYSTAL - 30));
|
||||
paragraph = paragraph.replaceAll("MANA_PER_CRYSTAL", "" + Constants.MANA_PER_CRYSTAL);
|
||||
paragraph = paragraph.replaceAll("BASIC_MAX_CHARGE", "" + Tier.BASIC.maxCharge);
|
||||
paragraph = paragraph.replaceAll("APPRENTICE_MAX_CHARGE", "" + Tier.APPRENTICE.maxCharge);
|
||||
paragraph = paragraph.replaceAll("ADVANCED_MAX_CHARGE", "" + Tier.ADVANCED.maxCharge);
|
||||
paragraph = paragraph.replaceAll("MASTER_MAX_CHARGE", "" + Tier.MASTER.maxCharge);
|
||||
paragraph = paragraph.replaceAll("BASIC_COLOUR", "\u00A77");
|
||||
paragraph = paragraph.replaceAll("APPRENTICE_COLOUR", Tier.APPRENTICE.getFormattingCode());
|
||||
paragraph = paragraph.replaceAll("ADVANCED_COLOUR", Tier.ADVANCED.getFormattingCode());
|
||||
paragraph = paragraph.replaceAll("MASTER_COLOUR", Tier.MASTER.getFormattingCode());
|
||||
paragraph = paragraph.replaceAll("FIRE_COLOUR", Element.FIRE.getFormattingCode());
|
||||
paragraph = paragraph.replaceAll("ICE_COLOUR", Element.ICE.getFormattingCode());
|
||||
paragraph = paragraph.replaceAll("LIGHTNING_COLOUR", Element.LIGHTNING.getFormattingCode());
|
||||
paragraph = paragraph.replaceAll("NECROMANCY_COLOUR", Element.NECROMANCY.getFormattingCode());
|
||||
paragraph = paragraph.replaceAll("EARTH_COLOUR", Element.EARTH.getFormattingCode());
|
||||
paragraph = paragraph.replaceAll("SORCERY_COLOUR", Element.SORCERY.getFormattingCode());
|
||||
paragraph = paragraph.replaceAll("HEALING_COLOUR", Element.HEALING.getFormattingCode());
|
||||
paragraph = paragraph.replaceAll("RESET_COLOUR", "\u00A70");
|
||||
paragraph = paragraph.replaceAll("MCVERSION", "1.11.2");
|
||||
paragraph = paragraph.replaceAll("VERSION", Wizardry.VERSION);
|
||||
|
||||
int linesInParagraph = this.fontRenderer
|
||||
.listFormattedStringToWidth(paragraph, GuiWizardHandbook.PAGE_WIDTH).size();
|
||||
|
||||
// Ignores empty lines at the top of a page.
|
||||
if(paragraph.isEmpty() && lineNumber == 0){
|
||||
|
||||
paragraph = bufferedreader.readLine();
|
||||
|
||||
// Normal paragraph, all on one page
|
||||
}else if(lineNumber + linesInParagraph <= linesPerPage){
|
||||
|
||||
page.add(paragraph);
|
||||
|
||||
lineNumber += linesInParagraph;
|
||||
|
||||
paragraph = bufferedreader.readLine();
|
||||
|
||||
// Paragraphs split across two pages (or more?)
|
||||
}else{
|
||||
|
||||
int linesInFirstPart = linesPerPage - lineNumber;
|
||||
|
||||
String paragraphFirstPart = "";
|
||||
String paragraphLastPart = "";
|
||||
|
||||
int i = 0;
|
||||
|
||||
List<String> strings = this.fontRenderer.listFormattedStringToWidth(paragraph,
|
||||
GuiWizardHandbook.PAGE_WIDTH);
|
||||
|
||||
for(Object s : strings){
|
||||
if(i < linesInFirstPart){
|
||||
paragraphFirstPart = paragraphFirstPart.concat((String)s + " ");
|
||||
}else{
|
||||
paragraphLastPart = paragraphLastPart.concat((String)s + " ");
|
||||
}
|
||||
i++;
|
||||
}
|
||||
|
||||
// System.out.println("Paragraph crosses page boundary; string split into: \"" +
|
||||
// paragraphFirstPart + "\" and \"" + paragraphLastPart + "\"");
|
||||
|
||||
page.add(paragraphFirstPart);
|
||||
|
||||
lineNumber += linesInFirstPart;
|
||||
|
||||
paragraph = paragraphLastPart;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
text.add(page);
|
||||
|
||||
}catch (IOException e){
|
||||
Wizardry.logger.error("Something went wrong reading file: " + textFilepath
|
||||
+ ". The file may be damaged; please try re-downloading and reinstalling wizardry.", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class Section {
|
||||
|
||||
/** The integer text colour used for the section when it is moused over. Currently orange. */
|
||||
private static final int HIGHLIGHT_COLOUR = 0xdd4c1d;
|
||||
|
||||
String name;
|
||||
int pageNumber;
|
||||
int x, y;
|
||||
int buttonId;
|
||||
|
||||
Section(String name, int pageNumber, int x, int y, int id){
|
||||
this.name = name;
|
||||
this.pageNumber = pageNumber;
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
this.buttonId = id;
|
||||
GuiWizardHandbook.this.buttonList.add(new GuiButtonInvisible(id, x, y, GuiWizardHandbook.PAGE_WIDTH,
|
||||
GuiWizardHandbook.this.fontRenderer.FONT_HEIGHT));
|
||||
}
|
||||
|
||||
void hideButton(){
|
||||
GuiWizardHandbook.this.buttonList.get(buttonId).visible = false;
|
||||
}
|
||||
|
||||
void drawContents(){
|
||||
|
||||
GuiWizardHandbook.this.buttonList.get(buttonId).visible = true;
|
||||
|
||||
GuiWizardHandbook.this.fontRenderer.drawString(name, x, y,
|
||||
GuiWizardHandbook.this.buttonList.get(buttonId).isMouseOver() ? HIGHLIGHT_COLOUR : BLACK);
|
||||
|
||||
int nameWidth = GuiWizardHandbook.this.fontRenderer.getStringWidth(name);
|
||||
|
||||
String dotsAndNumber = " " + this.pageNumber;
|
||||
|
||||
while(GuiWizardHandbook.this.fontRenderer.getStringWidth(dotsAndNumber) < GuiWizardHandbook.PAGE_WIDTH
|
||||
- nameWidth - 2){
|
||||
dotsAndNumber = "." + dotsAndNumber;
|
||||
}
|
||||
|
||||
GuiWizardHandbook.this.fontRenderer.drawString(dotsAndNumber, x + GuiWizardHandbook.PAGE_WIDTH
|
||||
- GuiWizardHandbook.this.fontRenderer.getStringWidth(dotsAndNumber), y, BLACK);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onGuiClosed(){
|
||||
super.onGuiClosed();
|
||||
Keyboard.enableRepeatEvents(false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Fired when a control is clicked. This is the equivalent of ActionListener.actionPerformed(ActionEvent e).
|
||||
*/
|
||||
@Override
|
||||
protected void actionPerformed(GuiButton par1GuiButton){
|
||||
|
||||
if(par1GuiButton.enabled){
|
||||
if(par1GuiButton.id == 0){
|
||||
if(pageNumber < (text.size() - 1) / 2) pageNumber++;
|
||||
}else if(par1GuiButton.id == 1){
|
||||
if(pageNumber > 0) pageNumber--;
|
||||
}else{
|
||||
if(pageNumber == 1) pageNumber = (sections.get(par1GuiButton.id - 2).pageNumber - 1) / 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Args: left, top, width, height, pointX, pointY. Note: left, top are local to Gui, pointX, pointY are local to
|
||||
* screen
|
||||
*/
|
||||
protected boolean isPointInRegion(int par1, int par2, int par3, int par4, int par5, int par6){
|
||||
int k1 = this.width / 2 - xSize / 2;
|
||||
int l1 = this.height / 2 - this.ySize / 2;
|
||||
par5 -= k1;
|
||||
par6 -= l1;
|
||||
return par5 >= par1 - 1 && par5 < par1 + par3 + 1 && par6 >= par2 - 1 && par6 < par2 + par4 + 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Draws a textured rectangle, stretching the section of the image to fit the size given.
|
||||
*
|
||||
* @param x The x position of the rectangle
|
||||
* @param y The y position of the rectangle
|
||||
* @param u The x position of the top left corner of the section of the image wanted, expressed as a fraction of the
|
||||
* image width
|
||||
* @param v The y position of the top left corner of the section of the image wanted, expressed as a fraction of the
|
||||
* image width
|
||||
* @param finalWidth The width as rendered
|
||||
* @param finalHeight The height as rendered
|
||||
* @param width The width of the section, expressed as a fraction of the image width
|
||||
* @param height The height of the section, expressed as a fraction of the image width
|
||||
*/
|
||||
public static void drawTexturedStretchedRect(int x, int y, int u, int v, int finalWidth, int finalHeight, int width,
|
||||
int height){
|
||||
|
||||
Tessellator tessellator = Tessellator.getInstance();
|
||||
BufferBuilder buffer = tessellator.getBuffer();
|
||||
buffer.begin(GL11.GL_QUADS, DefaultVertexFormats.POSITION_TEX);
|
||||
buffer.pos((x), y + finalHeight, 0).tex(u, v + height).endVertex();
|
||||
buffer.pos(x + finalWidth, y + finalHeight, 0).tex(u + width, v + height).endVertex();
|
||||
buffer.pos(x + finalWidth, (y), 0).tex(u + width, v).endVertex();
|
||||
buffer.pos((x), (y), 0).tex(u, v).endVertex();
|
||||
tessellator.draw();
|
||||
}
|
||||
|
||||
private static NonNullList<NonNullList<ItemStack>> createGrid(){
|
||||
NonNullList<NonNullList<ItemStack>> grid = NonNullList.withSize(3, NonNullList.create());
|
||||
for(int i=0; i<3; i++){
|
||||
grid.set(i, NonNullList.withSize(3, ItemStack.EMPTY));
|
||||
}
|
||||
return grid;
|
||||
}
|
||||
|
||||
/** Called from init() in the main mod class to initialise the recipes for display in the handbook. */
|
||||
public static void initDisplayRecipes(){
|
||||
|
||||
NonNullList<NonNullList<ItemStack>> craftingGrid;
|
||||
ItemStack craftingResult;
|
||||
|
||||
craftingGrid = createGrid();
|
||||
craftingGrid.get(0).set(0, new ItemStack(Items.GOLD_NUGGET));
|
||||
craftingGrid.get(1).set(0, new ItemStack(Blocks.CARPET, 1, 10));
|
||||
craftingGrid.get(2).set(0, new ItemStack(Items.GOLD_NUGGET));
|
||||
craftingGrid.get(0).set(1, new ItemStack(WizardryItems.magic_crystal));
|
||||
craftingGrid.get(1).set(1, new ItemStack(Blocks.LAPIS_BLOCK));
|
||||
craftingGrid.get(2).set(1, new ItemStack(WizardryItems.magic_crystal));
|
||||
craftingGrid.get(0).set(2, new ItemStack(Blocks.STONE));
|
||||
craftingGrid.get(1).set(2, new ItemStack(Blocks.STONE));
|
||||
craftingGrid.get(2).set(2, new ItemStack(Blocks.STONE));
|
||||
craftingResult = new ItemStack(WizardryBlocks.arcane_workbench);
|
||||
RECIPES.add(ImmutablePair.of(craftingResult, craftingGrid));
|
||||
|
||||
craftingGrid = createGrid();
|
||||
craftingGrid.get(2).set(0, new ItemStack(WizardryItems.magic_crystal));
|
||||
craftingGrid.get(1).set(1, new ItemStack(Items.STICK));
|
||||
craftingGrid.get(0).set(2, new ItemStack(Items.GOLD_NUGGET));
|
||||
craftingResult = new ItemStack(WizardryItems.magic_wand);
|
||||
RECIPES.add(ImmutablePair.of(craftingResult, craftingGrid));
|
||||
|
||||
craftingGrid = createGrid();
|
||||
craftingGrid.get(1).set(0, new ItemStack(WizardryItems.magic_crystal));
|
||||
craftingGrid.get(0).set(1, new ItemStack(WizardryItems.magic_crystal));
|
||||
craftingGrid.get(1).set(1, new ItemStack(Items.BOOK));
|
||||
craftingGrid.get(1).set(2, new ItemStack(WizardryItems.magic_crystal));
|
||||
craftingGrid.get(2).set(1, new ItemStack(WizardryItems.magic_crystal));
|
||||
craftingResult = new ItemStack(WizardryItems.spell_book, 1, 1);
|
||||
RECIPES.add(ImmutablePair.of(craftingResult, craftingGrid));
|
||||
|
||||
craftingGrid = createGrid();
|
||||
craftingGrid.get(0).set(0, new ItemStack(Items.BOOK));
|
||||
craftingGrid.get(1).set(0, new ItemStack(WizardryItems.magic_crystal));
|
||||
craftingResult = new ItemStack(WizardryItems.wizard_handbook);
|
||||
RECIPES.add(ImmutablePair.of(craftingResult, craftingGrid));
|
||||
|
||||
craftingGrid = createGrid();
|
||||
craftingGrid.get(0).set(0, new ItemStack(WizardryBlocks.crystal_flower));
|
||||
craftingResult = new ItemStack(WizardryItems.magic_crystal, 2);
|
||||
RECIPES.add(ImmutablePair.of(craftingResult, craftingGrid));
|
||||
|
||||
craftingGrid = createGrid();
|
||||
craftingGrid.get(0).set(0, new ItemStack(WizardryItems.magic_crystal));
|
||||
craftingGrid.get(1).set(0, new ItemStack(WizardryItems.magic_crystal));
|
||||
craftingGrid.get(2).set(0, new ItemStack(WizardryItems.magic_crystal));
|
||||
craftingGrid.get(0).set(1, new ItemStack(WizardryItems.magic_crystal));
|
||||
craftingGrid.get(1).set(1, new ItemStack(Items.GLASS_BOTTLE));
|
||||
craftingGrid.get(2).set(1, new ItemStack(WizardryItems.magic_crystal));
|
||||
craftingGrid.get(0).set(2, new ItemStack(WizardryItems.magic_crystal));
|
||||
craftingGrid.get(1).set(2, new ItemStack(WizardryItems.magic_crystal));
|
||||
craftingGrid.get(2).set(2, new ItemStack(WizardryItems.magic_crystal));
|
||||
craftingResult = new ItemStack(WizardryItems.mana_flask);
|
||||
RECIPES.add(ImmutablePair.of(craftingResult, craftingGrid));
|
||||
|
||||
craftingGrid = createGrid();
|
||||
craftingGrid.get(1).set(0, new ItemStack(Blocks.STONE));
|
||||
craftingGrid.get(0).set(1, new ItemStack(Blocks.STONE));
|
||||
craftingGrid.get(1).set(1, new ItemStack(WizardryItems.magic_crystal));
|
||||
craftingGrid.get(1).set(2, new ItemStack(Blocks.STONE));
|
||||
craftingGrid.get(2).set(1, new ItemStack(Blocks.STONE));
|
||||
craftingResult = new ItemStack(WizardryBlocks.transportation_stone, 2);
|
||||
RECIPES.add(ImmutablePair.of(craftingResult, craftingGrid));
|
||||
|
||||
craftingGrid = createGrid();
|
||||
craftingGrid.get(1).set(0, new ItemStack(Items.STRING));
|
||||
craftingGrid.get(0).set(1, new ItemStack(Items.STRING));
|
||||
craftingGrid.get(1).set(1, new ItemStack(WizardryItems.magic_crystal));
|
||||
craftingGrid.get(1).set(2, new ItemStack(Items.STRING));
|
||||
craftingGrid.get(2).set(1, new ItemStack(Items.STRING));
|
||||
craftingResult = new ItemStack(WizardryItems.magic_silk, 2);
|
||||
RECIPES.add(ImmutablePair.of(craftingResult, craftingGrid));
|
||||
|
||||
craftingGrid = createGrid();
|
||||
craftingGrid.get(0).set(0, new ItemStack(WizardryItems.magic_silk));
|
||||
craftingGrid.get(1).set(0, new ItemStack(WizardryItems.magic_silk));
|
||||
craftingGrid.get(2).set(0, new ItemStack(WizardryItems.magic_silk));
|
||||
craftingGrid.get(0).set(1, new ItemStack(WizardryItems.magic_silk));
|
||||
craftingGrid.get(2).set(1, new ItemStack(WizardryItems.magic_silk));
|
||||
craftingResult = new ItemStack(WizardryItems.wizard_hat);
|
||||
RECIPES.add(ImmutablePair.of(craftingResult, craftingGrid));
|
||||
|
||||
craftingGrid = createGrid();
|
||||
craftingGrid.get(0).set(0, new ItemStack(WizardryItems.magic_silk));
|
||||
craftingGrid.get(2).set(0, new ItemStack(WizardryItems.magic_silk));
|
||||
craftingGrid.get(0).set(1, new ItemStack(WizardryItems.magic_silk));
|
||||
craftingGrid.get(1).set(1, new ItemStack(WizardryItems.magic_silk));
|
||||
craftingGrid.get(2).set(1, new ItemStack(WizardryItems.magic_silk));
|
||||
craftingGrid.get(0).set(2, new ItemStack(WizardryItems.magic_silk));
|
||||
craftingGrid.get(1).set(2, new ItemStack(WizardryItems.magic_silk));
|
||||
craftingGrid.get(2).set(2, new ItemStack(WizardryItems.magic_silk));
|
||||
craftingResult = new ItemStack(WizardryItems.wizard_robe);
|
||||
RECIPES.add(ImmutablePair.of(craftingResult, craftingGrid));
|
||||
|
||||
craftingGrid = createGrid();
|
||||
craftingGrid.get(0).set(0, new ItemStack(WizardryItems.magic_silk));
|
||||
craftingGrid.get(1).set(0, new ItemStack(WizardryItems.magic_silk));
|
||||
craftingGrid.get(2).set(0, new ItemStack(WizardryItems.magic_silk));
|
||||
craftingGrid.get(0).set(1, new ItemStack(WizardryItems.magic_silk));
|
||||
craftingGrid.get(2).set(1, new ItemStack(WizardryItems.magic_silk));
|
||||
craftingGrid.get(0).set(2, new ItemStack(WizardryItems.magic_silk));
|
||||
craftingGrid.get(2).set(2, new ItemStack(WizardryItems.magic_silk));
|
||||
craftingResult = new ItemStack(WizardryItems.wizard_leggings);
|
||||
RECIPES.add(ImmutablePair.of(craftingResult, craftingGrid));
|
||||
|
||||
craftingGrid = createGrid();
|
||||
craftingGrid.get(0).set(0, new ItemStack(WizardryItems.magic_silk));
|
||||
craftingGrid.get(2).set(0, new ItemStack(WizardryItems.magic_silk));
|
||||
craftingGrid.get(0).set(1, new ItemStack(WizardryItems.magic_silk));
|
||||
craftingGrid.get(2).set(1, new ItemStack(WizardryItems.magic_silk));
|
||||
craftingResult = new ItemStack(WizardryItems.wizard_boots);
|
||||
RECIPES.add(ImmutablePair.of(craftingResult, craftingGrid));
|
||||
|
||||
if(Wizardry.settings.useAlternateScrollRecipe){
|
||||
craftingGrid = createGrid();
|
||||
craftingGrid.get(0).set(0, new ItemStack(Items.PAPER));
|
||||
craftingGrid.get(1).set(0, new ItemStack(Items.STRING));
|
||||
craftingGrid.get(2).set(0, new ItemStack(WizardryItems.magic_crystal));
|
||||
craftingResult = new ItemStack(WizardryItems.blank_scroll);
|
||||
RECIPES.add(ImmutablePair.of(craftingResult, craftingGrid));
|
||||
}else{
|
||||
craftingGrid = createGrid();
|
||||
craftingGrid.get(0).set(0, new ItemStack(Items.PAPER));
|
||||
craftingGrid.get(1).set(0, new ItemStack(Items.STRING));
|
||||
craftingResult = new ItemStack(WizardryItems.blank_scroll);
|
||||
RECIPES.add(ImmutablePair.of(craftingResult, craftingGrid));
|
||||
}
|
||||
|
||||
if(Wizardry.settings.firebombIsCraftable){
|
||||
craftingGrid = createGrid();
|
||||
craftingGrid.get(0).set(0, new ItemStack(Items.BLAZE_POWDER));
|
||||
craftingGrid.get(1).set(0, new ItemStack(Items.BLAZE_POWDER));
|
||||
craftingGrid.get(0).set(1, new ItemStack(Items.GLASS_BOTTLE));
|
||||
craftingGrid.get(1).set(1, new ItemStack(Items.GUNPOWDER));
|
||||
craftingResult = new ItemStack(WizardryItems.firebomb, 3);
|
||||
RECIPES.add(ImmutablePair.of(craftingResult, craftingGrid));
|
||||
}
|
||||
|
||||
if(Wizardry.settings.poisonBombIsCraftable){
|
||||
craftingGrid = createGrid();
|
||||
craftingGrid.get(0).set(0, new ItemStack(Items.SPIDER_EYE));
|
||||
craftingGrid.get(1).set(0, new ItemStack(Items.SPIDER_EYE));
|
||||
craftingGrid.get(0).set(1, new ItemStack(Items.GLASS_BOTTLE));
|
||||
craftingGrid.get(1).set(1, new ItemStack(Items.GUNPOWDER));
|
||||
craftingResult = new ItemStack(WizardryItems.poison_bomb, 3);
|
||||
RECIPES.add(ImmutablePair.of(craftingResult, craftingGrid));
|
||||
}
|
||||
|
||||
if(Wizardry.settings.smokeBombIsCraftable){
|
||||
craftingGrid = createGrid();
|
||||
craftingGrid.get(0).set(0, new ItemStack(Items.COAL));
|
||||
craftingGrid.get(1).set(0, new ItemStack(Items.COAL));
|
||||
craftingGrid.get(0).set(1, new ItemStack(Items.GLASS_BOTTLE));
|
||||
craftingGrid.get(1).set(1, new ItemStack(Items.GUNPOWDER));
|
||||
craftingResult = new ItemStack(WizardryItems.smoke_bomb, 3);
|
||||
RECIPES.add(ImmutablePair.of(craftingResult, craftingGrid));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -5,15 +5,13 @@ import net.minecraft.client.gui.FontRenderer;
|
||||
import net.minecraft.client.renderer.texture.TextureManager;
|
||||
import net.minecraft.client.settings.GameSettings;
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
import net.minecraftforge.fml.relauncher.Side;
|
||||
import net.minecraftforge.fml.relauncher.SideOnly;
|
||||
|
||||
/**
|
||||
* Font renderer that renders parts of strings surrounded by '#' (without quotes) in the SGA instead of normal text.
|
||||
*
|
||||
* @since Wizardry 1.1
|
||||
*/
|
||||
@SideOnly(Side.CLIENT)
|
||||
//@SideOnly(Side.CLIENT)
|
||||
public class MixedFontRenderer extends FontRenderer {
|
||||
|
||||
public MixedFontRenderer(GameSettings p_i1035_1_, ResourceLocation p_i1035_2_, TextureManager p_i1035_3_,
|
||||
|
||||
@@ -1,52 +0,0 @@
|
||||
package electroblob.wizardry.client;
|
||||
|
||||
import net.minecraft.client.audio.MovingSound;
|
||||
import net.minecraft.entity.Entity;
|
||||
import net.minecraft.util.SoundCategory;
|
||||
import net.minecraft.util.SoundEvent;
|
||||
import net.minecraft.util.math.MathHelper;
|
||||
import net.minecraftforge.fml.relauncher.Side;
|
||||
import net.minecraftforge.fml.relauncher.SideOnly;
|
||||
|
||||
// Copied from MovingSoundMinecart; if it ever breaks between updates take a look at that.
|
||||
@SideOnly(Side.CLIENT)
|
||||
public class MovingSoundEntity extends MovingSound {
|
||||
private final Entity source;
|
||||
private float distance = 0.0F;
|
||||
|
||||
public MovingSoundEntity(Entity entity, SoundEvent sound, float volume, float pitch, boolean repeat){
|
||||
// Uses BLOCKS because that's the closest thing to inanimate entities. Could use NEUTRAL like
|
||||
// MovingSoundMinecart.
|
||||
super(sound, SoundCategory.BLOCKS);
|
||||
this.source = entity;
|
||||
this.repeat = repeat;
|
||||
this.volume = volume;
|
||||
this.pitch = pitch;
|
||||
this.repeatDelay = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the JList with a new model.
|
||||
*/
|
||||
@Override
|
||||
public void update(){
|
||||
if(this.source.isDead && repeat){
|
||||
this.donePlaying = true;
|
||||
}else{
|
||||
this.xPosF = (float)this.source.posX;
|
||||
this.yPosF = (float)this.source.posY;
|
||||
this.zPosF = (float)this.source.posZ;
|
||||
float f = MathHelper.sqrt(this.source.motionX * this.source.motionX
|
||||
+ this.source.motionY * this.source.motionY + this.source.motionZ * this.source.motionZ);
|
||||
|
||||
// Is this something to do with the Doppler effect?
|
||||
if((double)f >= 0.01D){
|
||||
this.distance = MathHelper.clamp(this.distance + 0.0025F, 0.0F, 1.0F);
|
||||
this.volume = 0.0F + MathHelper.clamp(f, 0.0F, 0.5F) * 0.7F;
|
||||
}else{
|
||||
// this.pitch = 0.0F;
|
||||
// this.volume = 0.0F;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,98 +1,179 @@
|
||||
package electroblob.wizardry.client;
|
||||
|
||||
import org.lwjgl.opengl.GL11;
|
||||
|
||||
import electroblob.wizardry.WizardData;
|
||||
import electroblob.wizardry.Wizardry;
|
||||
import electroblob.wizardry.block.BlockMagicLight;
|
||||
import electroblob.wizardry.constants.Constants;
|
||||
import electroblob.wizardry.data.DispenserCastingData;
|
||||
import electroblob.wizardry.data.SpellEmitterData;
|
||||
import electroblob.wizardry.data.WizardData;
|
||||
import electroblob.wizardry.item.ISpellCastingItem;
|
||||
import electroblob.wizardry.item.ItemArtefact;
|
||||
import electroblob.wizardry.item.ItemSpectralBow;
|
||||
import electroblob.wizardry.item.ItemWand;
|
||||
import electroblob.wizardry.packet.PacketControlInput;
|
||||
import electroblob.wizardry.packet.WizardryPacketHandler;
|
||||
import electroblob.wizardry.registry.Spells;
|
||||
import electroblob.wizardry.registry.WizardryItems;
|
||||
import electroblob.wizardry.registry.WizardryPotions;
|
||||
import electroblob.wizardry.spell.Flight;
|
||||
import electroblob.wizardry.spell.ShadowWard;
|
||||
import electroblob.wizardry.spell.Shield;
|
||||
import electroblob.wizardry.tileentity.ContainerArcaneWorkbench;
|
||||
import electroblob.wizardry.util.WandHelper;
|
||||
import electroblob.wizardry.spell.*;
|
||||
import electroblob.wizardry.util.RayTracer;
|
||||
import electroblob.wizardry.util.WizardryUtilities;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.gui.GuiMerchant;
|
||||
import net.minecraft.client.renderer.BufferBuilder;
|
||||
import net.minecraft.client.renderer.GlStateManager;
|
||||
import net.minecraft.client.renderer.OpenGlHelper;
|
||||
import net.minecraft.client.renderer.Tessellator;
|
||||
import net.minecraft.client.renderer.entity.RenderManager;
|
||||
import net.minecraft.client.renderer.vertex.DefaultVertexFormats;
|
||||
import net.minecraft.client.settings.KeyBinding;
|
||||
import net.minecraft.entity.EntityLiving;
|
||||
import net.minecraft.entity.EntityLivingBase;
|
||||
import net.minecraft.entity.SharedMonsterAttributes;
|
||||
import net.minecraft.entity.ai.attributes.AttributeModifier;
|
||||
import net.minecraft.entity.item.EntityArmorStand;
|
||||
import net.minecraft.entity.player.EntityPlayer;
|
||||
import net.minecraft.inventory.Slot;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.tileentity.TileEntity;
|
||||
import net.minecraft.tileentity.TileEntityDispenser;
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
import net.minecraft.util.math.RayTraceResult;
|
||||
import net.minecraftforge.client.event.FOVUpdateEvent;
|
||||
import net.minecraftforge.client.event.MouseEvent;
|
||||
import net.minecraftforge.client.event.RenderGameOverlayEvent;
|
||||
import net.minecraftforge.client.event.RenderLivingEvent;
|
||||
import net.minecraftforge.client.event.RenderPlayerEvent;
|
||||
import net.minecraftforge.client.event.RenderWorldLastEvent;
|
||||
import net.minecraftforge.client.event.TextureStitchEvent;
|
||||
import net.minecraft.util.math.Vec3d;
|
||||
import net.minecraft.village.MerchantRecipe;
|
||||
import net.minecraft.world.World;
|
||||
import net.minecraftforge.client.event.*;
|
||||
import net.minecraftforge.fml.common.Mod;
|
||||
import net.minecraftforge.fml.common.ObfuscationReflectionHelper;
|
||||
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
|
||||
import net.minecraftforge.fml.common.network.simpleimpl.IMessage;
|
||||
import net.minecraftforge.fml.common.gameevent.TickEvent;
|
||||
import net.minecraftforge.fml.relauncher.Side;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
/**
|
||||
* Event handler responsible for all client-side only events, mostly rendering.
|
||||
* Event handler responsible for client-side only events, mostly rendering.
|
||||
*
|
||||
* @author Electroblob
|
||||
* @since Wizardry 1.0
|
||||
*/
|
||||
//@SideOnly(Side.CLIENT)
|
||||
@Mod.EventBusSubscriber(Side.CLIENT)
|
||||
public final class WizardryClientEventHandler {
|
||||
|
||||
private static final ResourceLocation shieldTexture = new ResourceLocation(Wizardry.MODID, "textures/entity/shield.png");
|
||||
private static final ResourceLocation wingTexture = new ResourceLocation(Wizardry.MODID, "textures/entity/wing.png");
|
||||
private static final ResourceLocation shadowWardTexture = new ResourceLocation(Wizardry.MODID, "textures/entity/shadow_ward.png");
|
||||
private static final ResourceLocation sixthSenseTexture = new ResourceLocation(Wizardry.MODID, "textures/entity/sixth_sense.png");
|
||||
private static final ResourceLocation sixthSenseOverlayTexture = new ResourceLocation(Wizardry.MODID, "textures/gui/sixth_sense_overlay.png");
|
||||
private static final ResourceLocation frostOverlayTexture = new ResourceLocation(Wizardry.MODID, "textures/gui/frost_overlay.png");
|
||||
private static final ResourceLocation blinkOverlayTexture = new ResourceLocation(Wizardry.MODID, "textures/gui/blink_overlay.png");
|
||||
private static final ResourceLocation pointerTexture = new ResourceLocation(Wizardry.MODID, "textures/entity/pointer.png");
|
||||
private static final ResourceLocation targetPointerTexture = new ResourceLocation(Wizardry.MODID, "textures/entity/target_pointer.png");
|
||||
|
||||
/** The remaining time for which the blink screen overlay effect will be displayed in first-person. Since this is
|
||||
* only for the first-person player (the instance of which is itself stored in a static variable), this can simply
|
||||
* be stored statically here, rather than needing to be in {@code WizardData}. */
|
||||
private static int blinkEffectTimer;
|
||||
/** The number of ticks the blink effect lasts for. */
|
||||
private static final int BLINK_EFFECT_DURATION = 8;
|
||||
|
||||
private static final Method unpressKey;
|
||||
|
||||
static {
|
||||
unpressKey = ObfuscationReflectionHelper.findMethod(KeyBinding.class, "func_74505_d", void.class);
|
||||
}
|
||||
|
||||
/** Starts the first person blink overlay effect. */
|
||||
public static void playBlinkEffect(){
|
||||
blinkEffectTimer = BLINK_EFFECT_DURATION;
|
||||
}
|
||||
|
||||
@SubscribeEvent
|
||||
public static void onTextureStitchEvent(TextureStitchEvent.Pre event){
|
||||
event.getMap().registerSprite(ContainerArcaneWorkbench.EMPTY_SLOT_CRYSTAL);
|
||||
event.getMap().registerSprite(ContainerArcaneWorkbench.EMPTY_SLOT_UPGRADE);
|
||||
public static void onPlayerTickEvent(TickEvent.PlayerTickEvent event){
|
||||
|
||||
if(event.player == Minecraft.getMinecraft().player){
|
||||
|
||||
if(blinkEffectTimer > 0) blinkEffectTimer--;
|
||||
|
||||
// Only seems to work here...
|
||||
// EntityLiving victim = Possession.getPossessee(Minecraft.getMinecraft().player);
|
||||
// if(victim != null && victim.getHeldItemMainhand().isEmpty()){
|
||||
// Minecraft.getMinecraft().player.setHeldItem(EnumHand.MAIN_HAND, ItemStack.EMPTY);
|
||||
// }
|
||||
|
||||
// Reset shaders if their respective potions aren't active
|
||||
// This is a player so the potion effects are synced by vanilla
|
||||
if(Minecraft.getMinecraft().entityRenderer.getShaderGroup() != null){
|
||||
|
||||
String activeShader = Minecraft.getMinecraft().entityRenderer.getShaderGroup().getShaderGroupName();
|
||||
|
||||
if((activeShader.equals(SlowTime.SHADER.toString()) && !Minecraft.getMinecraft().player.isPotionActive(WizardryPotions.slow_time))
|
||||
|| (activeShader.equals(SixthSense.SHADER.toString()) && !Minecraft.getMinecraft().player.isPotionActive(WizardryPotions.sixth_sense))
|
||||
|| (activeShader.equals(Transience.SHADER.toString()) && !Minecraft.getMinecraft().player.isPotionActive(WizardryPotions.transience))){
|
||||
|
||||
if(activeShader.equals(SixthSense.SHADER.toString())
|
||||
|| activeShader.equals(Transience.SHADER.toString())) playBlinkEffect();
|
||||
|
||||
Minecraft.getMinecraft().entityRenderer.stopUseShader();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@SubscribeEvent
|
||||
public static void onRenderHandEvent(RenderHandEvent event){
|
||||
|
||||
EntityLiving victim = Possession.getPossessee(Minecraft.getMinecraft().player);
|
||||
|
||||
if(victim != null){
|
||||
|
||||
victim.rotationYawHead = Minecraft.getMinecraft().player.rotationYaw;
|
||||
|
||||
if(Minecraft.getMinecraft().player.getHeldItemMainhand().isEmpty()){
|
||||
event.setCanceled(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Shift-scrolling to change spells
|
||||
// This event is called every tick, not just when a movement key is pressed
|
||||
@SubscribeEvent
|
||||
public static void onInputUpdateEvent(InputUpdateEvent event){
|
||||
// Prevents the player moving when paralysed
|
||||
if(event.getEntityPlayer().isPotionActive(WizardryPotions.paralysis)){
|
||||
event.getMovementInput().moveForward = 0;
|
||||
event.getMovementInput().moveStrafe = 0;
|
||||
event.getMovementInput().jump = false;
|
||||
event.getMovementInput().sneak = false;
|
||||
}
|
||||
}
|
||||
|
||||
@SubscribeEvent
|
||||
public static void onClientTickEvent(TickEvent.ClientTickEvent event){
|
||||
|
||||
if(event.phase == TickEvent.Phase.END && !net.minecraft.client.Minecraft.getMinecraft().isGamePaused()){
|
||||
|
||||
World world = net.minecraft.client.Minecraft.getMinecraft().world;
|
||||
|
||||
if(world == null) return;
|
||||
|
||||
for(TileEntity tileentity : world.loadedTileEntityList){
|
||||
if(tileentity instanceof TileEntityDispenser){
|
||||
if(DispenserCastingData.get((TileEntityDispenser)tileentity) != null){
|
||||
DispenserCastingData.get((TileEntityDispenser)tileentity).update();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SpellEmitterData.update(world);
|
||||
}
|
||||
}
|
||||
|
||||
@SubscribeEvent
|
||||
public static void onMouseEvent(MouseEvent event){
|
||||
|
||||
EntityPlayer player = Minecraft.getMinecraft().player;
|
||||
ItemStack wand = player.getHeldItemMainhand();
|
||||
|
||||
if(!(wand.getItem() instanceof ItemWand)){
|
||||
wand = player.getHeldItemOffhand();
|
||||
// If the player isn't holding a wand, then nothing else needs to be done.
|
||||
if(!(wand.getItem() instanceof ItemWand)) return;
|
||||
}
|
||||
|
||||
if(Minecraft.getMinecraft().inGameHasFocus && !wand.isEmpty() && event.getDwheel() != 0 && player.isSneaking()
|
||||
&& Wizardry.settings.enableShiftScrolling){
|
||||
|
||||
|
||||
// Prevents the player looking around when paralysed
|
||||
if(Minecraft.getMinecraft().player.isPotionActive(WizardryPotions.paralysis)
|
||||
&& Minecraft.getMinecraft().inGameHasFocus){
|
||||
event.setCanceled(true);
|
||||
|
||||
if(event.getDwheel() > 0){
|
||||
// Packet building
|
||||
IMessage msg = new PacketControlInput.Message(PacketControlInput.ControlType.PREVIOUS_SPELL_KEY);
|
||||
WizardryPacketHandler.net.sendToServer(msg);
|
||||
|
||||
}else if(event.getDwheel() < 0){
|
||||
// Packet building
|
||||
IMessage msg = new PacketControlInput.Message(PacketControlInput.ControlType.NEXT_SPELL_KEY);
|
||||
WizardryPacketHandler.net.sendToServer(msg);
|
||||
}
|
||||
Minecraft.getMinecraft().player.prevRotationYaw = 0;
|
||||
Minecraft.getMinecraft().player.prevRotationPitch = 0;
|
||||
Minecraft.getMinecraft().player.rotationYaw = 0;
|
||||
Minecraft.getMinecraft().player.rotationPitch = 0;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -114,23 +195,51 @@ public final class WizardryClientEventHandler {
|
||||
|
||||
event.setNewfov(event.getFov() * 1.0F - maxUseSeconds * 0.15F);
|
||||
}
|
||||
|
||||
if(blinkEffectTimer > 0){
|
||||
float f = ((float)Math.max(blinkEffectTimer - 2, 0))/BLINK_EFFECT_DURATION;
|
||||
event.setNewfov(event.getFov() + f * f * 0.7f);
|
||||
}
|
||||
}
|
||||
|
||||
// Third person
|
||||
@SubscribeEvent
|
||||
public static void onRenderPlayerEvent(RenderPlayerEvent.Post event){
|
||||
renderShieldIfActive(event.getEntityPlayer());
|
||||
renderWingsIfActive(event.getEntityPlayer(), event.getPartialRenderTick());
|
||||
renderShadowWardIfActive(event.getEntityPlayer());
|
||||
public static void onDrawBlockHighlightEvent(DrawBlockHighlightEvent event){
|
||||
// Hide the block outline for magic light blocks unless the player can dispel them
|
||||
if(event.getTarget().typeOfHit == RayTraceResult.Type.BLOCK
|
||||
&& event.getPlayer().world.getBlockState(event.getTarget().getBlockPos()).getBlock() instanceof BlockMagicLight){
|
||||
|
||||
if((!(event.getPlayer().getHeldItemMainhand().getItem() instanceof ISpellCastingItem)
|
||||
&& !(event.getPlayer().getHeldItemOffhand().getItem() instanceof ISpellCastingItem))
|
||||
|| !ItemArtefact.isArtefactActive(event.getPlayer(), WizardryItems.charm_light)){
|
||||
|
||||
event.setCanceled(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// First person
|
||||
// Brute-force fix for crystals not showing up when a wizard is given a spell book in the trade GUI.
|
||||
@SubscribeEvent
|
||||
public static void onRenderWorldLastEvent(RenderWorldLastEvent event){
|
||||
// Now only fires in first person.
|
||||
if(Minecraft.getMinecraft().gameSettings.thirdPersonView == 0){
|
||||
renderShieldFirstPerson(Minecraft.getMinecraft().player);
|
||||
renderShadowWardFirstPerson(Minecraft.getMinecraft().player);
|
||||
public static void onGuiDrawForegroundEvent(GuiContainerEvent.DrawForeground event){
|
||||
|
||||
if(event.getGuiContainer() instanceof GuiMerchant){
|
||||
|
||||
GuiMerchant gui = (GuiMerchant)event.getGuiContainer();
|
||||
// Note that gui.getMerchant() returns an NpcMerchant, not an EntityWizard.
|
||||
|
||||
// Using == the specific item rather than instanceof because that's how trades do it.
|
||||
if(gui.inventorySlots.getSlot(0).getStack().getItem() == WizardryItems.spell_book
|
||||
|| gui.inventorySlots.getSlot(1).getStack().getItem() == WizardryItems.spell_book){
|
||||
|
||||
for(MerchantRecipe trade : gui.getMerchant().getRecipes(Minecraft.getMinecraft().player)){
|
||||
if(trade.getItemToBuy().getItem() == WizardryItems.spell_book && trade.getSecondItemToBuy().isEmpty()){
|
||||
Slot slot = gui.inventorySlots.getSlot(2);
|
||||
// It still doesn't look quite right because the slot highlight is behind the item, but it'll do
|
||||
// until/unless I find a better solution.
|
||||
DrawingUtils.drawItemAndTooltip(gui, trade.getItemToSell(), slot.xPos, slot.yPos, event.getMouseX(), event.getMouseY(),
|
||||
gui.getSlotUnderMouse() == slot);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -138,60 +247,65 @@ public final class WizardryClientEventHandler {
|
||||
public static void onRenderLivingEvent(RenderLivingEvent.Post<EntityLivingBase> event){
|
||||
|
||||
Minecraft mc = Minecraft.getMinecraft();
|
||||
WizardData properties = WizardData.get(mc.player);
|
||||
RayTraceResult rayTrace = WizardryUtilities.standardEntityRayTrace(mc.world, mc.player, 16);
|
||||
WizardData data = WizardData.get(mc.player);
|
||||
RenderManager renderManager = event.getRenderer().getRenderManager();
|
||||
|
||||
ItemStack wand = mc.player.getHeldItemMainhand();
|
||||
|
||||
if(!(wand.getItem() instanceof ItemWand)){
|
||||
if(!(wand.getItem() instanceof ISpellCastingItem)){
|
||||
wand = mc.player.getHeldItemOffhand();
|
||||
}
|
||||
|
||||
// Target selection pointer
|
||||
if(mc.player.isSneaking() && wand.getItem() instanceof ItemWand && rayTrace != null && !(event.getEntity() instanceof EntityArmorStand)
|
||||
&& rayTrace.entityHit == event.getEntity() && properties != null && properties.selectedMinion != null){
|
||||
if(mc.player.isSneaking() && wand.getItem() instanceof ISpellCastingItem && WizardryUtilities.isLiving(event.getEntity())
|
||||
&& data != null && data.selectedMinion != null){
|
||||
|
||||
// -> Moved this in here so it isn't called every tick
|
||||
RayTraceResult rayTrace = RayTracer.standardEntityRayTrace(mc.world, mc.player, 16, false);
|
||||
|
||||
if(rayTrace != null && rayTrace.entityHit == event.getEntity()){
|
||||
|
||||
Tessellator tessellator = Tessellator.getInstance();
|
||||
BufferBuilder buffer = tessellator.getBuffer();
|
||||
Tessellator tessellator = Tessellator.getInstance();
|
||||
BufferBuilder buffer = tessellator.getBuffer();
|
||||
|
||||
GlStateManager.pushMatrix();
|
||||
GlStateManager.pushMatrix();
|
||||
|
||||
GlStateManager.disableCull();
|
||||
GlStateManager.disableLighting();
|
||||
OpenGlHelper.setLightmapTextureCoords(OpenGlHelper.lightmapTexUnit, 240f, 240f);
|
||||
// Disabling depth test allows it to be seen through everything.
|
||||
GL11.glDisable(GL11.GL_DEPTH_TEST);
|
||||
GlStateManager.color(1, 1, 1, 1);
|
||||
GlStateManager.disableCull();
|
||||
GlStateManager.disableLighting();
|
||||
OpenGlHelper.setLightmapTextureCoords(OpenGlHelper.lightmapTexUnit, 240f, 240f);
|
||||
// Disabling depth test allows it to be seen through everything.
|
||||
GlStateManager.disableDepth();
|
||||
GlStateManager.color(1, 1, 1, 1);
|
||||
|
||||
GlStateManager.translate(event.getX(), event.getY() + event.getEntity().height + 0.5, event.getZ());
|
||||
GlStateManager.translate(event.getX(), event.getY() + event.getEntity().height + 0.5, event.getZ());
|
||||
|
||||
// This counteracts the reverse rotation behaviour when in front f5 view.
|
||||
// Fun fact: this is a bug with vanilla too! Look at a snowball in front f5 view, for example.
|
||||
float yaw = mc.gameSettings.thirdPersonView == 2 ? renderManager.playerViewX : -renderManager.playerViewX;
|
||||
GlStateManager.rotate(180 - renderManager.playerViewY, 0.0F, 1.0F, 0.0F);
|
||||
GlStateManager.rotate(yaw, 1.0F, 0.0F, 0.0F);
|
||||
// This counteracts the reverse rotation behaviour when in front f5 view.
|
||||
// Fun fact: this is a bug with vanilla too! Look at a snowball in front f5 view, for example.
|
||||
float yaw = mc.gameSettings.thirdPersonView == 2 ? renderManager.playerViewX : -renderManager.playerViewX;
|
||||
GlStateManager.rotate(180 - renderManager.playerViewY, 0.0F, 1.0F, 0.0F);
|
||||
GlStateManager.rotate(yaw, 1.0F, 0.0F, 0.0F);
|
||||
|
||||
buffer.begin(GL11.GL_QUADS, DefaultVertexFormats.POSITION_TEX);
|
||||
buffer.begin(GL11.GL_QUADS, DefaultVertexFormats.POSITION_TEX);
|
||||
|
||||
mc.renderEngine.bindTexture(targetPointerTexture);
|
||||
mc.renderEngine.bindTexture(targetPointerTexture);
|
||||
|
||||
buffer.pos(-0.2, 0.24, 0).tex(0, 0).endVertex();
|
||||
buffer.pos(0.2, 0.24, 0).tex(9f / 16f, 0).endVertex();
|
||||
buffer.pos(0.2, -0.24, 0).tex(9f / 16f, 11f / 16f).endVertex();
|
||||
buffer.pos(-0.2, -0.24, 0).tex(0, 11f / 16f).endVertex();
|
||||
buffer.pos(-0.2, 0.24, 0).tex(0, 0).endVertex();
|
||||
buffer.pos(0.2, 0.24, 0).tex(9f / 16f, 0).endVertex();
|
||||
buffer.pos(0.2, -0.24, 0).tex(9f / 16f, 11f / 16f).endVertex();
|
||||
buffer.pos(-0.2, -0.24, 0).tex(0, 11f / 16f).endVertex();
|
||||
|
||||
tessellator.draw();
|
||||
tessellator.draw();
|
||||
|
||||
GlStateManager.enableCull();
|
||||
GlStateManager.enableLighting();
|
||||
GL11.glEnable(GL11.GL_DEPTH_TEST);
|
||||
GlStateManager.enableCull();
|
||||
GlStateManager.enableLighting();
|
||||
GlStateManager.enableDepth();
|
||||
|
||||
GlStateManager.popMatrix();
|
||||
GlStateManager.popMatrix();
|
||||
}
|
||||
}
|
||||
|
||||
// Summoned creature selection pointer
|
||||
if(properties != null && properties.selectedMinion != null && properties.selectedMinion.get() == event.getEntity()){
|
||||
if(data != null && data.selectedMinion != null && data.selectedMinion.get() == event.getEntity()){
|
||||
|
||||
Tessellator tessellator = Tessellator.getInstance();
|
||||
BufferBuilder buffer = tessellator.getBuffer();
|
||||
@@ -202,7 +316,7 @@ public final class WizardryClientEventHandler {
|
||||
GlStateManager.disableLighting();
|
||||
OpenGlHelper.setLightmapTextureCoords(OpenGlHelper.lightmapTexUnit, 240f, 240f);
|
||||
// Disabling depth test allows it to be seen through everything.
|
||||
GL11.glDisable(GL11.GL_DEPTH_TEST);
|
||||
GlStateManager.disableDepth();
|
||||
GlStateManager.color(1, 1, 1, 1);
|
||||
|
||||
GlStateManager.translate(event.getX(), event.getY() + event.getEntity().height + 0.5, event.getZ());
|
||||
@@ -226,14 +340,15 @@ public final class WizardryClientEventHandler {
|
||||
|
||||
GlStateManager.enableCull();
|
||||
GlStateManager.enableLighting();
|
||||
GL11.glEnable(GL11.GL_DEPTH_TEST);
|
||||
GlStateManager.enableDepth();
|
||||
|
||||
GlStateManager.popMatrix();
|
||||
}
|
||||
|
||||
// Sixth sense
|
||||
if(mc.player.isPotionActive(WizardryPotions.sixth_sense) && !(event.getEntity() instanceof EntityArmorStand) && event.getEntity() != mc.player
|
||||
&& mc.player.getActivePotionEffect(WizardryPotions.sixth_sense) != null && event.getEntity().getDistance(mc.player) < 20
|
||||
if(mc.player.isPotionActive(WizardryPotions.sixth_sense) && !(event.getEntity() instanceof EntityArmorStand)
|
||||
&& event.getEntity() != mc.player && mc.player.getActivePotionEffect(WizardryPotions.sixth_sense) != null
|
||||
&& event.getEntity().getDistance(mc.player) < Spells.sixth_sense.getProperty(Spell.EFFECT_RADIUS).floatValue()
|
||||
* (1 + mc.player.getActivePotionEffect(WizardryPotions.sixth_sense).getAmplifier() * Constants.RANGE_INCREASE_PER_LEVEL)){
|
||||
|
||||
Tessellator tessellator = Tessellator.getInstance();
|
||||
@@ -247,7 +362,7 @@ public final class WizardryClientEventHandler {
|
||||
OpenGlHelper.setLightmapTextureCoords(OpenGlHelper.lightmapTexUnit, 240f, 240f);
|
||||
GlStateManager.blendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA);
|
||||
// Disabling depth test allows it to be seen through everything.
|
||||
GL11.glDisable(GL11.GL_DEPTH_TEST);
|
||||
GlStateManager.disableDepth();
|
||||
|
||||
GlStateManager.translate(event.getX(), event.getY() + event.getEntity().height * 0.6, event.getZ());
|
||||
|
||||
@@ -271,7 +386,7 @@ public final class WizardryClientEventHandler {
|
||||
GlStateManager.enableCull();
|
||||
GlStateManager.disableBlend();
|
||||
GlStateManager.enableLighting();
|
||||
GL11.glEnable(GL11.GL_DEPTH_TEST);
|
||||
GlStateManager.enableDepth();
|
||||
|
||||
GlStateManager.popMatrix();
|
||||
}
|
||||
@@ -280,384 +395,72 @@ public final class WizardryClientEventHandler {
|
||||
@SubscribeEvent
|
||||
public static void onRenderGameOverlayEvent(RenderGameOverlayEvent.Post event){
|
||||
|
||||
if(event.getType() == RenderGameOverlayEvent.ElementType.HELMET
|
||||
&& Minecraft.getMinecraft().player.isPotionActive(WizardryPotions.sixth_sense)){
|
||||
|
||||
GlStateManager.pushMatrix();
|
||||
|
||||
GL11.glDisable(GL11.GL_DEPTH_TEST);
|
||||
GL11.glDepthMask(false);
|
||||
OpenGlHelper.glBlendFunc(770, 771, 1, 0);
|
||||
GlStateManager.color(1.0F, 1.0F, 1.0F, 1.0F);
|
||||
GlStateManager.disableAlpha();
|
||||
Minecraft.getMinecraft().renderEngine.bindTexture(sixthSenseOverlayTexture);
|
||||
|
||||
Tessellator tessellator = Tessellator.getInstance();
|
||||
BufferBuilder buffer = tessellator.getBuffer();
|
||||
|
||||
buffer.begin(GL11.GL_QUADS, DefaultVertexFormats.POSITION_TEX);
|
||||
buffer.pos(0.0D, (double)event.getResolution().getScaledHeight(), -90.0D).tex(0.0D, 1.0D).endVertex();
|
||||
buffer.pos((double)event.getResolution().getScaledWidth(), (double)event.getResolution().getScaledHeight(), -90.0D).tex(1.0D, 1.0D)
|
||||
.endVertex();
|
||||
buffer.pos((double)event.getResolution().getScaledWidth(), 0.0D, -90.0D).tex(1.0D, 0.0D).endVertex();
|
||||
buffer.pos(0.0D, 0.0D, -90.0D).tex(0.0D, 0.0D).endVertex();
|
||||
tessellator.draw();
|
||||
|
||||
GL11.glDepthMask(true);
|
||||
GL11.glEnable(GL11.GL_DEPTH_TEST);
|
||||
GlStateManager.enableAlpha();
|
||||
GlStateManager.color(1.0F, 1.0F, 1.0F, 1.0F);
|
||||
|
||||
GlStateManager.popMatrix();
|
||||
}
|
||||
|
||||
if(event.getType() == RenderGameOverlayEvent.ElementType.HELMET && Minecraft.getMinecraft().player.isPotionActive(WizardryPotions.frost)){
|
||||
|
||||
GlStateManager.pushMatrix();
|
||||
|
||||
GL11.glDisable(GL11.GL_DEPTH_TEST);
|
||||
GL11.glDepthMask(false);
|
||||
OpenGlHelper.glBlendFunc(770, 771, 1, 0);
|
||||
GlStateManager.color(1.0F, 1.0F, 1.0F, 1.0F);
|
||||
GlStateManager.disableAlpha();
|
||||
Minecraft.getMinecraft().renderEngine.bindTexture(frostOverlayTexture);
|
||||
|
||||
Tessellator tessellator = Tessellator.getInstance();
|
||||
BufferBuilder buffer = tessellator.getBuffer();
|
||||
|
||||
buffer.begin(GL11.GL_QUADS, DefaultVertexFormats.POSITION_TEX);
|
||||
buffer.pos(0.0D, (double)event.getResolution().getScaledHeight(), -90.0D).tex(0.0D, 1.0D).endVertex();
|
||||
buffer.pos((double)event.getResolution().getScaledWidth(), (double)event.getResolution().getScaledHeight(), -90.0D).tex(1.0D, 1.0D)
|
||||
.endVertex();
|
||||
buffer.pos((double)event.getResolution().getScaledWidth(), 0.0D, -90.0D).tex(1.0D, 0.0D).endVertex();
|
||||
buffer.pos(0.0D, 0.0D, -90.0D).tex(0.0D, 0.0D).endVertex();
|
||||
|
||||
tessellator.draw();
|
||||
GL11.glDepthMask(true);
|
||||
GL11.glEnable(GL11.GL_DEPTH_TEST);
|
||||
GlStateManager.enableAlpha();
|
||||
GlStateManager.color(1.0F, 1.0F, 1.0F, 1.0F);
|
||||
|
||||
GlStateManager.popMatrix();
|
||||
if(event.getType() == RenderGameOverlayEvent.ElementType.HELMET){
|
||||
|
||||
if(Minecraft.getMinecraft().player.isPotionActive(WizardryPotions.sixth_sense)){
|
||||
|
||||
OpenGlHelper.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA, GL11.GL_ONE, GL11.GL_ZERO);
|
||||
GlStateManager.color(1, 1, 1, 1);
|
||||
GlStateManager.disableAlpha();
|
||||
|
||||
renderScreenOverlay(event, sixthSenseOverlayTexture);
|
||||
|
||||
GlStateManager.enableAlpha();
|
||||
GlStateManager.color(1, 1, 1, 1);
|
||||
}
|
||||
|
||||
if(Minecraft.getMinecraft().player.isPotionActive(WizardryPotions.frost)){
|
||||
|
||||
OpenGlHelper.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA, GL11.GL_ONE, GL11.GL_ZERO);
|
||||
GlStateManager.color(1, 1, 1, 1);
|
||||
GlStateManager.disableAlpha();
|
||||
|
||||
renderScreenOverlay(event, frostOverlayTexture);
|
||||
|
||||
GlStateManager.enableAlpha();
|
||||
GlStateManager.color(1, 1, 1, 1);
|
||||
}
|
||||
|
||||
if(blinkEffectTimer > 0){
|
||||
|
||||
float alpha = ((float)blinkEffectTimer)/BLINK_EFFECT_DURATION;
|
||||
|
||||
OpenGlHelper.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE, GL11.GL_ONE, GL11.GL_ZERO);
|
||||
GlStateManager.color(1, 1, 1, alpha);
|
||||
GlStateManager.disableAlpha();
|
||||
|
||||
renderScreenOverlay(event, blinkOverlayTexture);
|
||||
|
||||
GlStateManager.enableAlpha();
|
||||
GlStateManager.color(1, 1, 1, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void renderScreenOverlay(RenderGameOverlayEvent.Post event, ResourceLocation texture){
|
||||
|
||||
GlStateManager.pushMatrix();
|
||||
|
||||
// FIXME: Something in here is making the first person shadow ward rather translucent.
|
||||
private static void renderShadowWardFirstPerson(EntityPlayer entityplayer){
|
||||
ItemStack wand = entityplayer.getActiveItemStack();
|
||||
if(WizardData.get(entityplayer) != null && WizardData.get(entityplayer).currentlyCasting() instanceof ShadowWard
|
||||
|| (entityplayer.isHandActive() && wand.getItemDamage() < wand.getMaxDamage() && wand.getItem() instanceof ItemWand
|
||||
&& WandHelper.getCurrentSpell(wand) instanceof ShadowWard)){
|
||||
|
||||
GlStateManager.pushMatrix();
|
||||
|
||||
GlStateManager.enableBlend();
|
||||
GlStateManager.blendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA);
|
||||
GlStateManager.shadeModel(GL11.GL_SMOOTH);
|
||||
GlStateManager.disableLighting();
|
||||
GlStateManager.disableAlpha();
|
||||
OpenGlHelper.setLightmapTextureCoords(OpenGlHelper.lightmapTexUnit, 240f, 240f);
|
||||
|
||||
GlStateManager.translate(0, 1.2, 0);
|
||||
GlStateManager.rotate(-entityplayer.rotationYaw, 0, 1, 0);
|
||||
GlStateManager.rotate(entityplayer.rotationPitch, 1, 0, 0);
|
||||
|
||||
Minecraft.getMinecraft().renderEngine.bindTexture(shadowWardTexture);
|
||||
|
||||
GlStateManager.pushMatrix();
|
||||
|
||||
GlStateManager.translate(0, 0, 1.2);
|
||||
GlStateManager.rotate(entityplayer.world.getWorldTime() * -2, 0, 0, 1);
|
||||
GlStateManager.scale(1.1, 1.1, 1.1);
|
||||
|
||||
Tessellator tessellator = Tessellator.getInstance();
|
||||
BufferBuilder buffer = tessellator.getBuffer();
|
||||
|
||||
buffer.begin(GL11.GL_QUADS, DefaultVertexFormats.POSITION_TEX);
|
||||
|
||||
buffer.pos(-0.5, 0.5, -0.5).tex(0, 0).endVertex();
|
||||
buffer.pos(0.5, 0.5, -0.5).tex(1, 0).endVertex();
|
||||
buffer.pos(0.5, -0.5, -0.5).tex(1, 1).endVertex();
|
||||
buffer.pos(-0.5, -0.5, -0.5).tex(0, 1).endVertex();
|
||||
|
||||
tessellator.draw();
|
||||
|
||||
buffer.begin(GL11.GL_QUADS, DefaultVertexFormats.POSITION_TEX);
|
||||
|
||||
buffer.pos(-0.5, 0.5, -0.5).tex(0, 0).endVertex();
|
||||
buffer.pos(-0.5, -0.5, -0.5).tex(0, 1).endVertex();
|
||||
buffer.pos(0.5, -0.5, -0.5).tex(1, 1).endVertex();
|
||||
buffer.pos(0.5, 0.5, -0.5).tex(1, 0).endVertex();
|
||||
|
||||
tessellator.draw();
|
||||
|
||||
GlStateManager.popMatrix();
|
||||
|
||||
GlStateManager.shadeModel(GL11.GL_FLAT);
|
||||
GlStateManager.enableLighting();
|
||||
GlStateManager.disableBlend();
|
||||
|
||||
GlStateManager.popMatrix();
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
private static void renderShadowWardIfActive(EntityPlayer entityplayer){
|
||||
ItemStack wand = entityplayer.getActiveItemStack();
|
||||
if(WizardData.get(entityplayer).currentlyCasting() instanceof ShadowWard
|
||||
|| (entityplayer.isHandActive() && wand.getItemDamage() < wand.getMaxDamage() && wand.getItem() instanceof ItemWand
|
||||
&& WandHelper.getCurrentSpell(wand) instanceof ShadowWard)){
|
||||
|
||||
GlStateManager.pushMatrix();
|
||||
|
||||
GlStateManager.enableBlend();
|
||||
GlStateManager.blendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA);
|
||||
GlStateManager.disableLighting();
|
||||
OpenGlHelper.setLightmapTextureCoords(OpenGlHelper.lightmapTexUnit, 240f, 240f);
|
||||
|
||||
GlStateManager.rotate(180, 0, 1, 0);
|
||||
GlStateManager.rotate(-entityplayer.renderYawOffset, 0, 1, 0);
|
||||
|
||||
Minecraft.getMinecraft().renderEngine.bindTexture(shadowWardTexture);
|
||||
|
||||
Tessellator tessellator = Tessellator.getInstance();
|
||||
BufferBuilder buffer = tessellator.getBuffer();
|
||||
|
||||
GlStateManager.translate(0, 1.2, 0);
|
||||
GlStateManager.rotate(entityplayer.world.getWorldTime() * -2, 0, 0, 1);
|
||||
GlStateManager.scale(1.1, 1.1, 1.1);
|
||||
|
||||
buffer.begin(GL11.GL_QUADS, DefaultVertexFormats.POSITION_TEX);
|
||||
|
||||
buffer.pos(-0.5, 0.5, -0.5).tex(0, 0).endVertex();
|
||||
buffer.pos(0.5, 0.5, -0.5).tex(1, 0).endVertex();
|
||||
buffer.pos(0.5, -0.5, -0.5).tex(1, 1).endVertex();
|
||||
buffer.pos(-0.5, -0.5, -0.5).tex(0, 1).endVertex();
|
||||
|
||||
tessellator.draw();
|
||||
|
||||
buffer.begin(GL11.GL_QUADS, DefaultVertexFormats.POSITION_TEX);
|
||||
|
||||
buffer.pos(-0.5, 0.5, -0.5).tex(0, 0).endVertex();
|
||||
buffer.pos(-0.5, -0.5, -0.5).tex(0, 1).endVertex();
|
||||
buffer.pos(0.5, -0.5, -0.5).tex(1, 1).endVertex();
|
||||
buffer.pos(0.5, 0.5, -0.5).tex(1, 0).endVertex();
|
||||
|
||||
tessellator.draw();
|
||||
|
||||
GlStateManager.enableLighting();
|
||||
GlStateManager.disableBlend();
|
||||
|
||||
GlStateManager.popMatrix();
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
private static void renderWingsIfActive(EntityPlayer entityplayer, float partialTickTime){
|
||||
ItemStack wand = entityplayer.getActiveItemStack();
|
||||
if(WizardData.get(entityplayer).currentlyCasting() instanceof Flight
|
||||
|| (entityplayer.isHandActive() && wand.getItemDamage() < wand.getMaxDamage() && wand.getItem() instanceof ItemWand
|
||||
&& WandHelper.getCurrentSpell(wand) instanceof Flight)){
|
||||
|
||||
GlStateManager.pushMatrix();
|
||||
|
||||
GlStateManager.enableBlend();
|
||||
GlStateManager.blendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA);
|
||||
GlStateManager.disableLighting();
|
||||
OpenGlHelper.setLightmapTextureCoords(OpenGlHelper.lightmapTexUnit, 240f, 240f);
|
||||
|
||||
// GlStateManager.rotate(-entityplayer.rotationYawHead, 0, 1, 0);
|
||||
GlStateManager.rotate(-entityplayer.renderYawOffset, 0, 1, 0);
|
||||
// GlStateManager.rotate(180, 1, 0, 0);
|
||||
|
||||
Minecraft.getMinecraft().renderEngine.bindTexture(wingTexture);
|
||||
Tessellator tessellator = Tessellator.getInstance();
|
||||
BufferBuilder buffer = tessellator.getBuffer();
|
||||
|
||||
GlStateManager.pushMatrix();
|
||||
|
||||
GlStateManager.translate(0.1, 0.4, -0.15);
|
||||
GlStateManager.rotate(20 + 20 * (float)Math.sin(entityplayer.world.getWorldTime() * 0.3), 0, 1, 0);
|
||||
|
||||
buffer.begin(GL11.GL_QUADS, DefaultVertexFormats.POSITION_TEX);
|
||||
|
||||
buffer.pos(0, 2, 0).tex(0, 0).endVertex();
|
||||
buffer.pos(2, 2, 0).tex(1, 0).endVertex();
|
||||
buffer.pos(2, 0, 0).tex(1, 1).endVertex();
|
||||
buffer.pos(0, 0, 0).tex(0, 1).endVertex();
|
||||
|
||||
tessellator.draw();
|
||||
|
||||
buffer.begin(GL11.GL_QUADS, DefaultVertexFormats.POSITION_TEX);
|
||||
|
||||
buffer.pos(0, 2, 0).tex(0, 0).endVertex();
|
||||
buffer.pos(0, 0, 0).tex(0, 1).endVertex();
|
||||
buffer.pos(2, 0, 0).tex(1, 1).endVertex();
|
||||
buffer.pos(2, 2, 0).tex(1, 0).endVertex();
|
||||
|
||||
tessellator.draw();
|
||||
|
||||
GlStateManager.popMatrix();
|
||||
|
||||
GlStateManager.pushMatrix();
|
||||
|
||||
GlStateManager.translate(-0.1, 0.4, -0.15);
|
||||
GlStateManager.rotate(-200 - 20 * (float)Math.sin(entityplayer.world.getWorldTime() * 0.3), 0, 1, 0);
|
||||
|
||||
buffer.begin(GL11.GL_QUADS, DefaultVertexFormats.POSITION_TEX);
|
||||
|
||||
buffer.pos(0, 2, 0).tex(0, 0).endVertex();
|
||||
buffer.pos(2, 2, 0).tex(1, 0).endVertex();
|
||||
buffer.pos(2, 0, 0).tex(1, 1).endVertex();
|
||||
buffer.pos(0, 0, 0).tex(0, 1).endVertex();
|
||||
|
||||
tessellator.draw();
|
||||
|
||||
buffer.begin(GL11.GL_QUADS, DefaultVertexFormats.POSITION_TEX);
|
||||
|
||||
buffer.pos(0, 2, 0).tex(0, 0).endVertex();
|
||||
buffer.pos(0, 0, 0).tex(0, 1).endVertex();
|
||||
buffer.pos(2, 0, 0).tex(1, 1).endVertex();
|
||||
buffer.pos(2, 2, 0).tex(1, 0).endVertex();
|
||||
|
||||
tessellator.draw();
|
||||
|
||||
GlStateManager.popMatrix();
|
||||
|
||||
GlStateManager.enableLighting();
|
||||
GlStateManager.disableBlend();
|
||||
|
||||
GlStateManager.popMatrix();
|
||||
}
|
||||
}
|
||||
|
||||
private static void renderShieldFirstPerson(EntityPlayer entityplayer){
|
||||
ItemStack wand = entityplayer.getActiveItemStack();
|
||||
if(WizardData.get(entityplayer) != null && WizardData.get(entityplayer).shield != null
|
||||
&& (WizardData.get(entityplayer).currentlyCasting() instanceof Shield
|
||||
|| (entityplayer.isHandActive() && wand.getItemDamage() < wand.getMaxDamage() && wand.getItem() instanceof ItemWand
|
||||
&& WandHelper.getCurrentSpell(wand) instanceof Shield))){
|
||||
|
||||
GlStateManager.pushMatrix();
|
||||
|
||||
GlStateManager.disableCull();
|
||||
GlStateManager.enableBlend();
|
||||
GlStateManager.blendFunc(GL11.GL_ONE, GL11.GL_SRC_ALPHA);
|
||||
GlStateManager.shadeModel(GL11.GL_SMOOTH);
|
||||
GlStateManager.disableLighting();
|
||||
OpenGlHelper.setLightmapTextureCoords(OpenGlHelper.lightmapTexUnit, 240f, 240f);
|
||||
|
||||
GlStateManager.translate(0, 1.4, 0);
|
||||
|
||||
GlStateManager.rotate(-entityplayer.rotationYaw, 0, 1, 0);
|
||||
GlStateManager.rotate(entityplayer.rotationPitch, 1, 0, 0);
|
||||
|
||||
GlStateManager.translate(0, 0, 0.8);
|
||||
|
||||
Tessellator tessellator = Tessellator.getInstance();
|
||||
|
||||
Minecraft.getMinecraft().renderEngine.bindTexture(shieldTexture);
|
||||
|
||||
renderShield(tessellator);
|
||||
|
||||
GlStateManager.enableLighting();
|
||||
|
||||
GlStateManager.shadeModel(GL11.GL_FLAT);
|
||||
GlStateManager.enableCull();
|
||||
GlStateManager.disableBlend();
|
||||
// RenderHelper.enableStandardItemLighting();
|
||||
|
||||
GlStateManager.popMatrix();
|
||||
}
|
||||
}
|
||||
|
||||
private static void renderShieldIfActive(EntityPlayer entityplayer){
|
||||
ItemStack wand = entityplayer.getActiveItemStack();
|
||||
if(WizardData.get(entityplayer).shield != null && (WizardData.get(entityplayer).currentlyCasting() instanceof Shield
|
||||
|| (entityplayer.isHandActive() && wand.getItemDamage() < wand.getMaxDamage() && wand.getItem() instanceof ItemWand
|
||||
&& WandHelper.getCurrentSpell(wand) instanceof Shield))){
|
||||
|
||||
GlStateManager.pushMatrix();
|
||||
|
||||
GlStateManager.disableCull();
|
||||
GlStateManager.enableBlend();
|
||||
// For some reason, the old blend function (GL11.GL_SRC_ALPHA, GL11.GL_SRC_ALPHA) caused the inner
|
||||
// edges to appear black, so I have changed it to this, which looks very slightly different.
|
||||
GlStateManager.blendFunc(GL11.GL_ONE, GL11.GL_SRC_ALPHA);
|
||||
GlStateManager.shadeModel(GL11.GL_SMOOTH);
|
||||
GlStateManager.disableLighting();
|
||||
OpenGlHelper.setLightmapTextureCoords(OpenGlHelper.lightmapTexUnit, 240f, 240f);
|
||||
|
||||
GlStateManager.translate(0, 1.3, 0);
|
||||
|
||||
// GlStateManager.rotate(180, 0, 1, 0);
|
||||
GlStateManager.rotate(-entityplayer.renderYawOffset, 0, 1, 0);
|
||||
// GlStateManager.rotate(-entityplayer.rotationPitch, 1, 0, 0);
|
||||
|
||||
GlStateManager.translate(0, 0, 0.8);
|
||||
|
||||
Tessellator tessellator = Tessellator.getInstance();
|
||||
|
||||
Minecraft.getMinecraft().renderEngine.bindTexture(shieldTexture);
|
||||
|
||||
renderShield(tessellator);
|
||||
|
||||
GlStateManager.enableLighting();
|
||||
|
||||
GlStateManager.shadeModel(GL11.GL_FLAT);
|
||||
GlStateManager.enableCull();
|
||||
GlStateManager.disableBlend();
|
||||
// RenderHelper.enableStandardItemLighting();
|
||||
|
||||
GlStateManager.popMatrix();
|
||||
}
|
||||
}
|
||||
|
||||
private static void renderShield(Tessellator tessellator){
|
||||
GlStateManager.disableDepth();
|
||||
GlStateManager.depthMask(false);
|
||||
|
||||
Minecraft.getMinecraft().renderEngine.bindTexture(texture);
|
||||
|
||||
Tessellator tessellator = Tessellator.getInstance();
|
||||
BufferBuilder buffer = tessellator.getBuffer();
|
||||
|
||||
double widthOuter = 0.6d;
|
||||
double heightOuter = 0.7d;
|
||||
double widthInner = 0.3d;
|
||||
double heightInner = 0.4d;
|
||||
double depth = 0.2d;
|
||||
|
||||
buffer.begin(GL11.GL_TRIANGLE_STRIP, DefaultVertexFormats.POSITION_TEX_COLOR);
|
||||
|
||||
buffer.pos(-widthOuter, heightInner, -depth).tex(0, 0.2).color(0, 0, 0, 255).endVertex();
|
||||
buffer.pos(-widthInner, heightInner, 0).tex(0.2, 0.2).color(200, 200, 255, 255).endVertex();
|
||||
buffer.pos(-widthInner, heightOuter, -depth).tex(0.2, 0).color(0, 0, 0, 255).endVertex();
|
||||
buffer.pos(-widthInner, heightInner, 0).tex(0.2, 0.2).color(200, 200, 255, 255).endVertex();
|
||||
|
||||
buffer.pos(widthInner, heightOuter, -depth).tex(0.8, 0).color(0, 0, 0, 255).endVertex();
|
||||
buffer.pos(widthInner, heightInner, 0).tex(0.8, 0.2).color(200, 200, 255, 255).endVertex();
|
||||
buffer.pos(widthOuter, heightInner, -depth).tex(1, 0.2).color(0, 0, 0, 255).endVertex();
|
||||
buffer.pos(widthInner, heightInner, 0).tex(0.8, 0.2).color(200, 200, 255, 255).endVertex();
|
||||
|
||||
buffer.pos(widthOuter, -heightInner, -depth).tex(1, 0.8).color(0, 0, 0, 255).endVertex();
|
||||
buffer.pos(widthInner, -heightInner, 0).tex(0.8, 0.8).color(200, 200, 255, 255).endVertex();
|
||||
buffer.pos(widthInner, -heightOuter, -depth).tex(0.8, 1).color(0, 0, 0, 255).endVertex();
|
||||
buffer.pos(widthInner, -heightInner, 0).tex(0.8, 0.8).color(200, 200, 255, 255).endVertex();
|
||||
|
||||
buffer.pos(-widthInner, -heightOuter, -depth).tex(0.2, 1).color(0, 0, 0, 255).endVertex();
|
||||
buffer.pos(-widthInner, -heightInner, 0).tex(0.2, 0.8).color(200, 200, 255, 255).endVertex();
|
||||
buffer.pos(-widthOuter, -heightInner, -depth).tex(0, 0.8).color(0, 0, 0, 255).endVertex();
|
||||
buffer.pos(-widthInner, -heightInner, 0).tex(0.2, 0.8).color(200, 200, 255, 255).endVertex();
|
||||
|
||||
buffer.pos(-widthOuter, heightInner, -depth).tex(0, 0.2).color(0, 0, 0, 255).endVertex();
|
||||
buffer.pos(-widthInner, heightInner, 0).tex(0.2, 0.2).color(200, 200, 255, 255).endVertex();
|
||||
|
||||
buffer.begin(GL11.GL_QUADS, DefaultVertexFormats.POSITION_TEX);
|
||||
buffer.pos(0.0D, (double)event.getResolution().getScaledHeight(), -90.0D).tex(0.0D, 1.0D).endVertex();
|
||||
buffer.pos((double)event.getResolution().getScaledWidth(), (double)event.getResolution().getScaledHeight(), -90.0D).tex(1.0D, 1.0D)
|
||||
.endVertex();
|
||||
buffer.pos((double)event.getResolution().getScaledWidth(), 0.0D, -90.0D).tex(1.0D, 0.0D).endVertex();
|
||||
buffer.pos(0.0D, 0.0D, -90.0D).tex(0.0D, 0.0D).endVertex();
|
||||
tessellator.draw();
|
||||
|
||||
GlStateManager.depthMask(true);
|
||||
GlStateManager.enableDepth();
|
||||
|
||||
buffer.begin(GL11.GL_TRIANGLE_STRIP, DefaultVertexFormats.POSITION_TEX_COLOR);
|
||||
|
||||
buffer.pos(-widthInner, heightInner, 0).tex(0.2, 0.2).color(200, 200, 255, 255).endVertex();
|
||||
buffer.pos(widthInner, heightInner, 0).tex(0.8, 0.2).color(200, 200, 255, 255).endVertex();
|
||||
buffer.pos(-widthInner, -heightInner, 0).tex(0.2, 0.8).color(200, 200, 255, 255).endVertex();
|
||||
buffer.pos(widthInner, -heightInner, 0).tex(0.8, 0.8).color(200, 200, 255, 255).endVertex();
|
||||
|
||||
tessellator.draw();
|
||||
GlStateManager.popMatrix();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,121 @@
|
||||
package electroblob.wizardry.client;
|
||||
|
||||
import electroblob.wizardry.Wizardry;
|
||||
import electroblob.wizardry.client.gui.GuiSpellDisplay;
|
||||
import electroblob.wizardry.item.ISpellCastingItem;
|
||||
import electroblob.wizardry.packet.PacketControlInput;
|
||||
import electroblob.wizardry.packet.WizardryPacketHandler;
|
||||
import electroblob.wizardry.registry.WizardrySounds;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.audio.PositionedSoundRecord;
|
||||
import net.minecraft.entity.player.EntityPlayer;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraftforge.client.event.MouseEvent;
|
||||
import net.minecraftforge.fml.common.Mod;
|
||||
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
|
||||
import net.minecraftforge.fml.common.gameevent.TickEvent;
|
||||
import net.minecraftforge.fml.common.network.simpleimpl.IMessage;
|
||||
import net.minecraftforge.fml.relauncher.Side;
|
||||
|
||||
/** Event handler class responsible for handling wizardry's controls. */
|
||||
//@SideOnly(Side.CLIENT)
|
||||
@Mod.EventBusSubscriber(Side.CLIENT)
|
||||
public class WizardryControlHandler {
|
||||
|
||||
static boolean NkeyPressed = false;
|
||||
static boolean BkeyPressed = false;
|
||||
|
||||
// Changed to a tick event to allow mouse button keybinds
|
||||
// The 'lag' that happened previously was actually because the code only fired when a keyboard key was pressed!
|
||||
@SubscribeEvent
|
||||
public static void onTickEvent(TickEvent.ClientTickEvent event){
|
||||
|
||||
if(event.phase == TickEvent.Phase.END) return; // Only really needs to be once per tick
|
||||
|
||||
if(Wizardry.proxy instanceof ClientProxy){
|
||||
|
||||
EntityPlayer player = Minecraft.getMinecraft().player;
|
||||
|
||||
if(player != null){
|
||||
|
||||
ItemStack wand = getWandInUse(player);
|
||||
if(wand == null) return;
|
||||
|
||||
if(ClientProxy.NEXT_SPELL.isKeyDown() && Minecraft.getMinecraft().inGameHasFocus){
|
||||
if(!NkeyPressed){
|
||||
NkeyPressed = true;
|
||||
selectNextSpell(wand);
|
||||
}
|
||||
}else{
|
||||
NkeyPressed = false;
|
||||
}
|
||||
|
||||
if(ClientProxy.PREVIOUS_SPELL.isKeyDown() && Minecraft.getMinecraft().inGameHasFocus){
|
||||
if(!BkeyPressed){
|
||||
BkeyPressed = true;
|
||||
// Packet building
|
||||
selectPreviousSpell(wand);
|
||||
}
|
||||
}else{
|
||||
BkeyPressed = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Shift-scrolling to change spells
|
||||
@SubscribeEvent
|
||||
public static void onMouseEvent(MouseEvent event){
|
||||
|
||||
EntityPlayer player = Minecraft.getMinecraft().player;
|
||||
ItemStack wand = getWandInUse(player);
|
||||
if(wand == null) return;
|
||||
|
||||
if(Minecraft.getMinecraft().inGameHasFocus && !wand.isEmpty() && event.getDwheel() != 0 && player.isSneaking()
|
||||
&& Wizardry.settings.shiftScrolling){
|
||||
|
||||
event.setCanceled(true);
|
||||
|
||||
int d = Wizardry.settings.reverseScrollDirection ? -event.getDwheel() : event.getDwheel();
|
||||
|
||||
if(d > 0){
|
||||
selectNextSpell(wand);
|
||||
}else if(d < 0){
|
||||
selectPreviousSpell(wand);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static ItemStack getWandInUse(EntityPlayer player){
|
||||
|
||||
ItemStack wand = player.getHeldItemMainhand();
|
||||
|
||||
// Only bother sending packets if the player is holding a spellcasting item with more than one spell slot
|
||||
if(!(wand.getItem() instanceof ISpellCastingItem) || ((ISpellCastingItem)wand.getItem()).getSpells(wand).length < 2){
|
||||
wand = player.getHeldItemOffhand();
|
||||
if(!(wand.getItem() instanceof ISpellCastingItem) || ((ISpellCastingItem)wand.getItem()).getSpells(wand).length < 2) return null;
|
||||
}
|
||||
|
||||
return wand;
|
||||
}
|
||||
|
||||
private static void selectNextSpell(ItemStack wand){
|
||||
// Packet building
|
||||
IMessage msg = new PacketControlInput.Message(PacketControlInput.ControlType.NEXT_SPELL_KEY);
|
||||
WizardryPacketHandler.net.sendToServer(msg);
|
||||
// GUI switch animation
|
||||
((ISpellCastingItem)wand.getItem()).selectNextSpell(wand); // Makes sure the spell is set immediately for the client
|
||||
GuiSpellDisplay.playSpellSwitchAnimation(true);
|
||||
Minecraft.getMinecraft().getSoundHandler().playSound(PositionedSoundRecord.getMasterRecord(WizardrySounds.ITEM_WAND_SWITCH_SPELL, 1));
|
||||
}
|
||||
|
||||
private static void selectPreviousSpell(ItemStack wand){
|
||||
// Packet building
|
||||
IMessage msg = new PacketControlInput.Message(PacketControlInput.ControlType.PREVIOUS_SPELL_KEY);
|
||||
WizardryPacketHandler.net.sendToServer(msg);
|
||||
// GUI switch animation
|
||||
((ISpellCastingItem)wand.getItem()).selectPreviousSpell(wand); // Makes sure the spell is set immediately for the client
|
||||
GuiSpellDisplay.playSpellSwitchAnimation(false);
|
||||
Minecraft.getMinecraft().getSoundHandler().playSound(PositionedSoundRecord.getMasterRecord(WizardrySounds.ITEM_WAND_SWITCH_SPELL, 1));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
package electroblob.wizardry.client.audio;
|
||||
|
||||
import net.minecraft.client.audio.MovingSound;
|
||||
import net.minecraft.entity.Entity;
|
||||
import net.minecraft.util.SoundCategory;
|
||||
import net.minecraft.util.SoundEvent;
|
||||
|
||||
// Copied from MovingSoundMinecart; if it ever breaks between updates take a look at that.
|
||||
//@SideOnly(Side.CLIENT)
|
||||
public class MovingSoundEntity<T extends Entity> extends MovingSound {
|
||||
|
||||
protected final T source;
|
||||
protected float distance = 0.0F;
|
||||
|
||||
public MovingSoundEntity(T entity, SoundEvent sound, SoundCategory category, float volume, float pitch, boolean repeat){
|
||||
super(sound, category);
|
||||
this.source = entity;
|
||||
this.repeat = repeat;
|
||||
this.volume = volume;
|
||||
this.pitch = pitch;
|
||||
this.repeatDelay = 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void update(){
|
||||
|
||||
if(this.source.isDead){
|
||||
this.donePlaying = true;
|
||||
}else{
|
||||
this.xPosF = (float)this.source.posX;
|
||||
this.yPosF = (float)this.source.posY;
|
||||
this.zPosF = (float)this.source.posZ;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,94 @@
|
||||
package electroblob.wizardry.client.audio;
|
||||
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.audio.ISound;
|
||||
import net.minecraft.util.ITickable;
|
||||
import net.minecraft.util.SoundCategory;
|
||||
import net.minecraft.util.SoundEvent;
|
||||
import net.minecraftforge.fml.common.Mod;
|
||||
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
|
||||
import net.minecraftforge.fml.common.gameevent.TickEvent;
|
||||
import net.minecraftforge.fml.relauncher.Side;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Instances of this class represent a set of sounds which together form a looped sound: start, loop and end.
|
||||
* Currently this is only used for continuous spell sounds in wizardry itself, but feel free to make your own
|
||||
* implementations - this class will take care of the internals.
|
||||
*
|
||||
* @since Wizardry 4.2
|
||||
* @author Electroblob
|
||||
*/
|
||||
// See MusicTicker, this is the same idea of just storing the sounds statically client-side and ticking them
|
||||
@Mod.EventBusSubscriber(Side.CLIENT)
|
||||
public abstract class SoundLoop implements ITickable {
|
||||
|
||||
private static final Set<SoundLoop> activeLoops = new HashSet<>();
|
||||
|
||||
private final ISound start;
|
||||
private final ISound loop;
|
||||
private final ISound end;
|
||||
|
||||
private boolean looping = false;
|
||||
private boolean needsRemoving = false;
|
||||
|
||||
public SoundLoop(SoundEvent start, SoundEvent loop, SoundEvent end, SoundCategory category, ISoundFactory factory){
|
||||
// The reason I've gone to the effort of having a factory for these is that we need SoundLoop to have control
|
||||
// over which sounds are repeated and which aren't whilst keeping them private.
|
||||
this.start = factory.create(start, category, false);
|
||||
this.loop = factory.create(loop, category, true);
|
||||
this.end = factory.create(end, category, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void update(){
|
||||
// Check every tick if the start sound is done playing and if so, start the loop sound
|
||||
if(!looping && !Minecraft.getMinecraft().getSoundHandler().isSoundPlaying(start)){
|
||||
Minecraft.getMinecraft().getSoundHandler().playSound(loop);
|
||||
looping = true;
|
||||
}
|
||||
}
|
||||
|
||||
/** Stops the loop part of the sound immediately and starts playing the end part. This may be called from subclasses
|
||||
* or externally depending on the implementation. */
|
||||
// For continuous spell sounds it's internally
|
||||
public void endLoop(){
|
||||
Minecraft.getMinecraft().getSoundHandler().stopSound(start);
|
||||
Minecraft.getMinecraft().getSoundHandler().stopSound(loop);
|
||||
Minecraft.getMinecraft().getSoundHandler().playSound(end);
|
||||
// Can't modify activeLoops directly since we'll probably be calling this method from update(), which is
|
||||
// during iteration of activeLoops so it could cause a ConcurrentModificationException
|
||||
this.markForRemoval();
|
||||
}
|
||||
|
||||
/** Marks this sound loop to be removed next tick. */
|
||||
protected void markForRemoval(){
|
||||
this.needsRemoving = true;
|
||||
}
|
||||
|
||||
// Static methods
|
||||
|
||||
public static void addLoop(SoundLoop loop){
|
||||
activeLoops.add(loop);
|
||||
// Do this here rather than in the constructor in case someone wants to play the loop later or reuse it
|
||||
Minecraft.getMinecraft().getSoundHandler().playSound(loop.start);
|
||||
}
|
||||
|
||||
@SubscribeEvent
|
||||
public static void tick(TickEvent.ClientTickEvent event){
|
||||
// Using the END phase means we can check for stopped sounds as soon as they are stopped (effectively),
|
||||
// meaning we don't get the 'cut' between the start and loop sounds
|
||||
// FIXME: Apparently this only works for dispensers. What the heck is the difference?!
|
||||
if(event.phase == TickEvent.Phase.END){
|
||||
activeLoops.forEach(SoundLoop::update);
|
||||
activeLoops.removeIf(s -> s.needsRemoving);
|
||||
}
|
||||
}
|
||||
|
||||
@FunctionalInterface
|
||||
public interface ISoundFactory {
|
||||
ISound create(SoundEvent sound, SoundCategory category, boolean repeat);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,117 @@
|
||||
package electroblob.wizardry.client.audio;
|
||||
|
||||
import electroblob.wizardry.data.DispenserCastingData;
|
||||
import electroblob.wizardry.registry.WizardrySounds;
|
||||
import electroblob.wizardry.spell.Spell;
|
||||
import electroblob.wizardry.util.WizardryUtilities;
|
||||
import net.minecraft.client.audio.PositionedSound;
|
||||
import net.minecraft.entity.EntityLivingBase;
|
||||
import net.minecraft.tileentity.TileEntity;
|
||||
import net.minecraft.tileentity.TileEntityDispenser;
|
||||
import net.minecraft.util.SoundEvent;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.world.World;
|
||||
|
||||
/** Abstract base class for sound loops associated with spells; see subclasses below for implementations. */
|
||||
public abstract class SoundLoopSpell extends SoundLoop {
|
||||
|
||||
private final Spell spell;
|
||||
|
||||
public SoundLoopSpell(SoundEvent start, SoundEvent loop, SoundEvent end, ISoundFactory factory, Spell spell){
|
||||
super(start, loop, end, WizardrySounds.SPELLS, factory);
|
||||
this.spell = spell;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void update(){
|
||||
// This may be a bit overkill but I might as well put functionality in superclasses where possible
|
||||
if(stillCasting(spell)){
|
||||
// This can't called in the same tick as endLoop because otherwise, if the spell casting stops during the
|
||||
// same tick as the transition to the loop sound it will try and stop the loop immediately after it has
|
||||
// started - and for whatever reason, this causes the 'Channel null in method stop' error.
|
||||
super.update();
|
||||
}else{
|
||||
endLoop();
|
||||
}
|
||||
}
|
||||
|
||||
protected abstract boolean stillCasting(Spell spell);
|
||||
|
||||
/** Implements a sound loop for continuous spells cast by entities. */
|
||||
public static class SoundLoopSpellEntity extends SoundLoopSpell {
|
||||
|
||||
private final EntityLivingBase source;
|
||||
|
||||
public SoundLoopSpellEntity(SoundEvent start, SoundEvent loop, SoundEvent end, Spell spell, EntityLivingBase source, float volume, float pitch){
|
||||
super(start, loop, end, (sound, category, repeat) -> new MovingSoundEntity<>(source, sound, category, volume, pitch, repeat), spell);
|
||||
this.source = source;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean stillCasting(Spell spell){
|
||||
return WizardryUtilities.isCasting(source, spell);
|
||||
}
|
||||
}
|
||||
|
||||
public static abstract class SoundLoopSpellPosition extends SoundLoopSpell {
|
||||
|
||||
public SoundLoopSpellPosition (SoundEvent start, SoundEvent loop, SoundEvent end, Spell spell,
|
||||
double x, double y, double z, float sndVolume, float sndPitch){
|
||||
// Huh, I actually found a use for a non-static initialiser block - hence the double curly brackets...
|
||||
super(start, loop, end, (sound, category, r) -> new PositionedSound(sound, category){{
|
||||
// ...et voila, we can just set protected fields as we please using external variables
|
||||
this.xPosF = (float)x;
|
||||
this.yPosF = (float)y;
|
||||
this.zPosF = (float)z;
|
||||
this.repeat = r;
|
||||
this.volume = sndVolume;
|
||||
this.pitch = sndPitch;
|
||||
}}, spell);
|
||||
}
|
||||
}
|
||||
|
||||
/** Implements a sound loop for continuous spells cast by dispensers. */
|
||||
public static class SoundLoopSpellDispenser extends SoundLoopSpellPosition {
|
||||
|
||||
private final TileEntityDispenser source;
|
||||
|
||||
public SoundLoopSpellDispenser(SoundEvent start, SoundEvent loop, SoundEvent end, Spell spell, World world,
|
||||
double x, double y, double z, float sndVolume, float sndPitch){
|
||||
super(start, loop, end, spell, x, y, z, sndVolume, sndPitch);
|
||||
|
||||
TileEntity tileentity = world.getTileEntity(new BlockPos(x, y, z));
|
||||
|
||||
if(tileentity instanceof TileEntityDispenser) this.source = (TileEntityDispenser)tileentity;
|
||||
else throw new NullPointerException(String.format("Playing continuous spell sound: no dispenser found at %s, %s, %s", x, y, z));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean stillCasting(Spell spell){
|
||||
return DispenserCastingData.get(source).currentlyCasting() == spell;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/** Implements a sound loop for continuous spells cast at a position (via commands). */
|
||||
public static class SoundLoopSpellPosTimed extends SoundLoopSpellPosition {
|
||||
|
||||
private int timeLeft;
|
||||
|
||||
public SoundLoopSpellPosTimed(SoundEvent start, SoundEvent loop, SoundEvent end, Spell spell, int duration,
|
||||
double x, double y, double z, float sndVolume, float sndPitch){
|
||||
super(start, loop, end, spell, x, y, z, sndVolume, sndPitch);
|
||||
this.timeLeft = duration;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void update(){
|
||||
super.update();
|
||||
timeLeft--;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean stillCasting(Spell spell){
|
||||
return timeLeft > 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,449 @@
|
||||
package electroblob.wizardry.client.gui;
|
||||
|
||||
import electroblob.wizardry.Wizardry;
|
||||
import electroblob.wizardry.client.DrawingUtils;
|
||||
import electroblob.wizardry.constants.Element;
|
||||
import electroblob.wizardry.data.SpellGlyphData;
|
||||
import electroblob.wizardry.data.WizardData;
|
||||
import electroblob.wizardry.item.IManaStoringItem;
|
||||
import electroblob.wizardry.item.ISpellCastingItem;
|
||||
import electroblob.wizardry.item.IWorkbenchItem;
|
||||
import electroblob.wizardry.packet.PacketControlInput;
|
||||
import electroblob.wizardry.packet.WizardryPacketHandler;
|
||||
import electroblob.wizardry.registry.WizardrySounds;
|
||||
import electroblob.wizardry.spell.Spell;
|
||||
import electroblob.wizardry.tileentity.ContainerArcaneWorkbench;
|
||||
import electroblob.wizardry.tileentity.TileEntityArcaneWorkbench;
|
||||
import electroblob.wizardry.util.WandHelper;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.audio.PositionedSoundRecord;
|
||||
import net.minecraft.client.gui.GuiButton;
|
||||
import net.minecraft.client.gui.inventory.GuiContainer;
|
||||
import net.minecraft.client.renderer.GlStateManager;
|
||||
import net.minecraft.client.renderer.GlStateManager.DestFactor;
|
||||
import net.minecraft.client.renderer.GlStateManager.SourceFactor;
|
||||
import net.minecraft.client.resources.I18n;
|
||||
import net.minecraft.entity.player.InventoryPlayer;
|
||||
import net.minecraft.inventory.IInventory;
|
||||
import net.minecraft.inventory.Slot;
|
||||
import net.minecraft.item.Item;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
import net.minecraftforge.client.event.TextureStitchEvent;
|
||||
import net.minecraftforge.fml.common.Mod;
|
||||
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
|
||||
import net.minecraftforge.fml.common.network.simpleimpl.IMessage;
|
||||
import net.minecraftforge.fml.relauncher.Side;
|
||||
import org.lwjgl.input.Keyboard;
|
||||
|
||||
@Mod.EventBusSubscriber(Side.CLIENT)
|
||||
public class GuiArcaneWorkbench extends GuiContainer {
|
||||
|
||||
private GuiButton applyBtn;
|
||||
public static final ResourceLocation texture = new ResourceLocation(Wizardry.MODID,
|
||||
"textures/gui/arcane_workbench.png");
|
||||
|
||||
private IInventory playerInventory;
|
||||
private IInventory arcaneWorkbenchInventory;
|
||||
|
||||
private static final int TOOLTIP_WIDTH = 164;
|
||||
|
||||
/** We report the actual size of the GUI to Minecraft when a wand is in so JEI doesn't overdraw it.
|
||||
* For calculations, we use the size without the tooltip, which is stored in this constant. */
|
||||
private static final int MAIN_GUI_WIDTH = 176;
|
||||
|
||||
private static final int RUNE_LEFT = 38;
|
||||
private static final int RUNE_TOP = 22;
|
||||
private static final int RUNE_WIDTH = 100;
|
||||
private static final int RUNE_HEIGHT = 100;
|
||||
|
||||
private static final int HALO_DIAMETER = 156;
|
||||
|
||||
private static final int TEXTURE_WIDTH = 512;
|
||||
private static final int TEXTURE_HEIGHT = 256;
|
||||
|
||||
private int animationTimer = 0;
|
||||
private static final int ANIMATION_DURATION = 20;
|
||||
|
||||
public GuiArcaneWorkbench(InventoryPlayer invPlayer, TileEntityArcaneWorkbench entity){
|
||||
super(new ContainerArcaneWorkbench(invPlayer, entity));
|
||||
this.playerInventory = invPlayer;
|
||||
this.arcaneWorkbenchInventory = entity;
|
||||
xSize = MAIN_GUI_WIDTH;
|
||||
ySize = 220;
|
||||
}
|
||||
|
||||
// Huh, didn't realise this method existed. Pretty neat.
|
||||
@Override
|
||||
public void updateScreen(){
|
||||
if(animationTimer > 0) animationTimer--;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void drawScreen(int mouseX, int mouseY, float partialTicks){
|
||||
|
||||
this.drawDefaultBackground();
|
||||
|
||||
GlStateManager.color(1, 1, 1, 1); // Just in case
|
||||
|
||||
Slot slot = this.inventorySlots.getSlot(ContainerArcaneWorkbench.CENTRE_SLOT);
|
||||
|
||||
// Tests if there is a wand in the workbench and edits the positioning accordingly
|
||||
if(slot.getHasStack() && slot.getStack().getItem() instanceof IWorkbenchItem
|
||||
&& ((IWorkbenchItem)slot.getStack().getItem()).showTooltip(slot.getStack())){
|
||||
xSize = MAIN_GUI_WIDTH + TOOLTIP_WIDTH;
|
||||
guiLeft = (this.width - this.xSize) / 2;
|
||||
this.applyBtn.x = (this.width - TOOLTIP_WIDTH) / 2 + 64;
|
||||
}else{
|
||||
xSize = MAIN_GUI_WIDTH;
|
||||
guiLeft = (this.width - this.xSize) / 2;
|
||||
this.applyBtn.x = this.width / 2 + 64;
|
||||
}
|
||||
|
||||
this.applyBtn.enabled = slot.getHasStack();
|
||||
|
||||
super.drawScreen(mouseX, mouseY, partialTicks);
|
||||
|
||||
// Required now, or item mouseover tooltips won't render.
|
||||
this.renderHoveredToolTip(mouseX, mouseY);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void drawGuiContainerBackgroundLayer(float partialTicks, int mouseX, int mouseY){
|
||||
|
||||
GlStateManager.color(1, 1, 1, 1);
|
||||
Minecraft.getMinecraft().renderEngine.bindTexture(texture);
|
||||
|
||||
// Animation
|
||||
|
||||
// Grey background
|
||||
DrawingUtils.drawTexturedRect(guiLeft + RUNE_LEFT, guiTop + RUNE_TOP, MAIN_GUI_WIDTH + TOOLTIP_WIDTH, 0,
|
||||
RUNE_WIDTH, RUNE_HEIGHT, TEXTURE_WIDTH, TEXTURE_HEIGHT);
|
||||
|
||||
// Yellow 'halo'
|
||||
if(animationTimer > 0){
|
||||
|
||||
GlStateManager.pushMatrix();
|
||||
GlStateManager.enableBlend();
|
||||
GlStateManager.blendFunc(SourceFactor.SRC_ALPHA, DestFactor.ONE_MINUS_SRC_ALPHA);
|
||||
|
||||
int x = guiLeft + RUNE_LEFT + RUNE_WIDTH/2;
|
||||
int y = guiTop + RUNE_TOP + RUNE_HEIGHT/2;
|
||||
|
||||
float scale = (animationTimer + partialTicks)/ANIMATION_DURATION;
|
||||
scale = (float)(1 - Math.pow(1-scale, 1.4f)); // Makes it slower at the start and speed up
|
||||
GlStateManager.scale(scale, scale, 1);
|
||||
GlStateManager.translate(x/scale, y/scale, 0);
|
||||
|
||||
DrawingUtils.drawTexturedRect(-HALO_DIAMETER /2, -HALO_DIAMETER /2, MAIN_GUI_WIDTH + TOOLTIP_WIDTH, RUNE_HEIGHT,
|
||||
HALO_DIAMETER, HALO_DIAMETER, TEXTURE_WIDTH, TEXTURE_HEIGHT);
|
||||
|
||||
GlStateManager.disableBlend();
|
||||
GlStateManager.popMatrix();
|
||||
}
|
||||
|
||||
// Main inventory
|
||||
DrawingUtils.drawTexturedRect(guiLeft, guiTop, 0, 0, MAIN_GUI_WIDTH, ySize, TEXTURE_WIDTH, TEXTURE_HEIGHT);
|
||||
|
||||
float opacity = (animationTimer + partialTicks)/ANIMATION_DURATION;
|
||||
|
||||
// Changing slots
|
||||
for(int i = 0; i < ContainerArcaneWorkbench.CRYSTAL_SLOT; i++){
|
||||
|
||||
Slot slot = this.inventorySlots.getSlot(i);
|
||||
|
||||
if(slot.xPos >= 0 && slot.yPos >= 0){
|
||||
// Slot background
|
||||
DrawingUtils.drawTexturedRect(guiLeft + slot.xPos - 10, guiTop + slot.yPos - 10, 0, 220, 36, 36, TEXTURE_WIDTH, TEXTURE_HEIGHT);
|
||||
|
||||
// Slot animation
|
||||
// IDEA: Somehow replace with intelligent check for whether the spell actually got applied
|
||||
if(animationTimer > 0 && slot.getHasStack()){
|
||||
|
||||
GlStateManager.pushMatrix();
|
||||
GlStateManager.enableBlend();
|
||||
GlStateManager.blendFunc(SourceFactor.SRC_ALPHA, DestFactor.ONE_MINUS_SRC_ALPHA);
|
||||
GlStateManager.color(1, 1, 1, opacity);
|
||||
|
||||
DrawingUtils.drawTexturedRect(guiLeft + slot.xPos - 10, guiTop + slot.yPos - 10, 36, 220, 36, 36, TEXTURE_WIDTH, TEXTURE_HEIGHT);
|
||||
|
||||
GlStateManager.color(1, 1, 1, 1);
|
||||
GlStateManager.disableBlend();
|
||||
GlStateManager.popMatrix();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Crystal + upgrade slot animations
|
||||
if(animationTimer > 0){
|
||||
|
||||
Slot crystals = this.inventorySlots.getSlot(ContainerArcaneWorkbench.CRYSTAL_SLOT);
|
||||
Slot upgrades = this.inventorySlots.getSlot(ContainerArcaneWorkbench.UPGRADE_SLOT);
|
||||
|
||||
if(crystals.getHasStack()){
|
||||
|
||||
GlStateManager.pushMatrix();
|
||||
GlStateManager.enableBlend();
|
||||
GlStateManager.blendFunc(SourceFactor.SRC_ALPHA, DestFactor.ONE_MINUS_SRC_ALPHA);
|
||||
GlStateManager.color(1, 1, 1, opacity);
|
||||
|
||||
DrawingUtils.drawTexturedRect(guiLeft + crystals.xPos - 8, guiTop + crystals.yPos - 8,
|
||||
MAIN_GUI_WIDTH + TOOLTIP_WIDTH + RUNE_WIDTH, 0, 32, 32, TEXTURE_WIDTH, TEXTURE_HEIGHT);
|
||||
|
||||
GlStateManager.color(1, 1, 1, 1);
|
||||
GlStateManager.disableBlend();
|
||||
GlStateManager.popMatrix();
|
||||
}
|
||||
|
||||
if(upgrades.getHasStack()){
|
||||
|
||||
GlStateManager.pushMatrix();
|
||||
GlStateManager.enableBlend();
|
||||
GlStateManager.blendFunc(SourceFactor.SRC_ALPHA, DestFactor.ONE_MINUS_SRC_ALPHA);
|
||||
GlStateManager.color(1, 1, 1, opacity);
|
||||
|
||||
DrawingUtils.drawTexturedRect(guiLeft + upgrades.xPos - 8, guiTop + upgrades.yPos - 8,
|
||||
MAIN_GUI_WIDTH + TOOLTIP_WIDTH + RUNE_WIDTH, 0, 32, 32, TEXTURE_WIDTH, TEXTURE_HEIGHT);
|
||||
|
||||
GlStateManager.color(1, 1, 1, 1);
|
||||
GlStateManager.disableBlend();
|
||||
GlStateManager.popMatrix();
|
||||
}
|
||||
}
|
||||
|
||||
// Tooltip only drawn if there is a wand
|
||||
if(this.inventorySlots.getSlot(ContainerArcaneWorkbench.CENTRE_SLOT).getHasStack()){
|
||||
|
||||
ItemStack stack = this.inventorySlots.getSlot(ContainerArcaneWorkbench.CENTRE_SLOT).getStack();
|
||||
|
||||
if(!(stack.getItem() instanceof IWorkbenchItem)){
|
||||
Wizardry.logger.warn("Invalid item in central slot of arcane workbench, how did that get there?!");
|
||||
return;
|
||||
}
|
||||
|
||||
if(((IWorkbenchItem)stack.getItem()).showTooltip(stack)){
|
||||
|
||||
// Tooltip box
|
||||
DrawingUtils.drawTexturedRect(guiLeft + MAIN_GUI_WIDTH, guiTop, MAIN_GUI_WIDTH, 0, TOOLTIP_WIDTH, ySize, TEXTURE_WIDTH, TEXTURE_HEIGHT);
|
||||
|
||||
int y = guiTop + 20;
|
||||
|
||||
if(stack.getItem() instanceof IManaStoringItem && ((IManaStoringItem)stack.getItem()).showManaInWorkbench(this.mc.player, stack)){
|
||||
y += 14;
|
||||
}
|
||||
|
||||
if(stack.getItem() instanceof ISpellCastingItem && ((ISpellCastingItem)stack.getItem()).showSpellsInWorkbench(this.mc.player, stack)){
|
||||
|
||||
Spell[] spells = ((ISpellCastingItem)stack.getItem()).getSpells(stack);
|
||||
|
||||
GlStateManager.enableBlend();
|
||||
|
||||
for(Spell spell : spells){
|
||||
|
||||
boolean discovered = true;
|
||||
|
||||
if(!this.mc.player.isCreative() && WizardData.get(this.mc.player) != null){
|
||||
discovered = WizardData.get(this.mc.player).hasSpellBeenDiscovered(spell);
|
||||
}
|
||||
// As of Wizardry 1.2, the icons have been split off into their own texture files to allow for add-on
|
||||
// mods to add their own.
|
||||
Minecraft.getMinecraft().renderEngine
|
||||
.bindTexture(discovered ? spell.getElement().getIcon() : Element.MAGIC.getIcon());
|
||||
|
||||
// Renders the little element icon
|
||||
DrawingUtils.drawTexturedRect(guiLeft + MAIN_GUI_WIDTH + 5, y, 8, 8);
|
||||
|
||||
y += 10;
|
||||
}
|
||||
}
|
||||
|
||||
GlStateManager.disableBlend();
|
||||
|
||||
int x = 0;
|
||||
y += 16;
|
||||
|
||||
// Look how much shorter this is with the WandHelper class!
|
||||
for(Item item : WandHelper.getSpecialUpgrades()){
|
||||
|
||||
int level = WandHelper.getUpgradeLevel(stack, item);
|
||||
|
||||
if(level > 0){
|
||||
ItemStack stack1 = new ItemStack(item, level);
|
||||
GlStateManager.enableDepth();
|
||||
this.itemRender.renderItemAndEffectIntoGUI(stack1, guiLeft + MAIN_GUI_WIDTH + 6 + x, y);
|
||||
this.itemRender.renderItemOverlayIntoGUI(this.fontRenderer, stack1, guiLeft + MAIN_GUI_WIDTH + 6 + x, y,
|
||||
null);
|
||||
x += 18;
|
||||
GlStateManager.disableDepth();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Minecraft.getMinecraft().renderEngine.bindTexture(texture);
|
||||
|
||||
// Fixes the bug that caused the slot highlight to render opaque. I don't know why it works, it just works!
|
||||
GlStateManager.disableBlend();
|
||||
GlStateManager.enableAlpha();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void drawGuiContainerForegroundLayer(int mouseX, int mouseY){
|
||||
|
||||
GlStateManager.color(1, 1, 1, 1); // Just in case
|
||||
|
||||
this.fontRenderer
|
||||
.drawString(this.arcaneWorkbenchInventory.hasCustomName() ? this.arcaneWorkbenchInventory.getName()
|
||||
: I18n.format(this.arcaneWorkbenchInventory.getName()), 8, 6, 4210752);
|
||||
this.fontRenderer.drawString(this.playerInventory.hasCustomName() ? this.playerInventory.getName()
|
||||
: I18n.format(this.playerInventory.getName()), 8, this.ySize - 96 + 2, 4210752);
|
||||
|
||||
if(this.inventorySlots.getSlot(ContainerArcaneWorkbench.CENTRE_SLOT).getHasStack()){
|
||||
|
||||
ItemStack stack = this.inventorySlots.getSlot(ContainerArcaneWorkbench.CENTRE_SLOT).getStack();
|
||||
|
||||
if(!(stack.getItem() instanceof IWorkbenchItem)){
|
||||
Wizardry.logger.warn("Invalid item in central slot of arcane workbench, how did that get there?!");
|
||||
return;
|
||||
}
|
||||
|
||||
if(((IWorkbenchItem)stack.getItem()).showTooltip(stack)){
|
||||
|
||||
int y = 6;
|
||||
|
||||
this.fontRenderer.drawStringWithShadow("\u00A7f" + stack.getDisplayName(), MAIN_GUI_WIDTH + 6, y, 0);
|
||||
|
||||
if(stack.getItem() instanceof IManaStoringItem && ((IManaStoringItem)stack.getItem()).showManaInWorkbench(this.mc.player, stack)){
|
||||
y += 14;
|
||||
this.fontRenderer.drawStringWithShadow(
|
||||
"\u00A77" + I18n.format("container." + Wizardry.MODID + ":arcane_workbench.mana")
|
||||
+ " " + ((IManaStoringItem)stack.getItem()).getMana(stack) + "/"
|
||||
+ ((IManaStoringItem)stack.getItem()).getManaCapacity(stack),
|
||||
MAIN_GUI_WIDTH + 6, y, 0);
|
||||
}
|
||||
|
||||
y += 14;
|
||||
|
||||
if(stack.getItem() instanceof ISpellCastingItem && ((ISpellCastingItem)stack.getItem()).showSpellsInWorkbench(this.mc.player, stack)){
|
||||
|
||||
Spell[] spells = ((ISpellCastingItem)stack.getItem()).getSpells(stack);
|
||||
|
||||
for(Spell spell : spells){
|
||||
|
||||
boolean discovered = true;
|
||||
|
||||
if(!this.mc.player.isCreative() && WizardData.get(this.mc.player) != null){
|
||||
discovered = WizardData.get(this.mc.player).hasSpellBeenDiscovered(spell);
|
||||
}
|
||||
|
||||
if(discovered){
|
||||
this.fontRenderer.drawStringWithShadow(spell.getDisplayNameWithFormatting(), MAIN_GUI_WIDTH + 16, y, 0);
|
||||
}else{
|
||||
this.mc.standardGalacticFontRenderer.drawStringWithShadow(
|
||||
"\u00A79" + SpellGlyphData.getGlyphName(spell, this.mc.world), MAIN_GUI_WIDTH + 16, y, 0);
|
||||
}
|
||||
y += 10;
|
||||
}
|
||||
}
|
||||
|
||||
if(WandHelper.getTotalUpgrades(stack) > 0){
|
||||
|
||||
y += 6;
|
||||
|
||||
this.fontRenderer.drawStringWithShadow("\u00A7f" + I18n.format("container."
|
||||
+ Wizardry.MODID + ":arcane_workbench.upgrades"), MAIN_GUI_WIDTH + 6, y, 0);
|
||||
|
||||
int x = 0;
|
||||
y += 10;
|
||||
|
||||
// Wand upgrade tooltips
|
||||
for(Item item : WandHelper.getSpecialUpgrades()){
|
||||
|
||||
int level = WandHelper.getUpgradeLevel(stack, item);
|
||||
|
||||
if(level > 0){
|
||||
// The javadoc for isPointInRegion is ambiguous; what it means is that the REGION is
|
||||
// relative to the GUI but the POINT isn't.
|
||||
if(isPointInRegion(MAIN_GUI_WIDTH + 6 + x, y, 16, 16, mouseX, mouseY)){
|
||||
ItemStack stack1 = new ItemStack(item, level);
|
||||
this.renderToolTip(stack1, mouseX - guiLeft, mouseY - guiTop);
|
||||
}
|
||||
x += 18;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initGui(){
|
||||
this.mc.player.openContainer = this.inventorySlots;
|
||||
this.guiLeft = (this.width - this.xSize) / 2;
|
||||
this.guiTop = (this.height - this.ySize) / 2;
|
||||
Keyboard.enableRepeatEvents(true);
|
||||
this.buttonList.clear();
|
||||
this.buttonList.add(this.applyBtn = new GuiButtonApply(0, this.width / 2 + 64, this.height / 2 + 3));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onGuiClosed(){
|
||||
super.onGuiClosed();
|
||||
Keyboard.enableRepeatEvents(false);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void actionPerformed(GuiButton button){
|
||||
if(button.enabled){
|
||||
if(button.id == 0){
|
||||
// Packet building
|
||||
IMessage msg = new PacketControlInput.Message(PacketControlInput.ControlType.APPLY_BUTTON);
|
||||
WizardryPacketHandler.net.sendToServer(msg);
|
||||
// Sound
|
||||
Minecraft.getMinecraft().getSoundHandler().playSound(PositionedSoundRecord.getMasterRecord(
|
||||
WizardrySounds.BLOCK_ARCANE_WORKBENCH_SPELLBIND, 1));
|
||||
// Animation
|
||||
animationTimer = 20;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class GuiButtonApply extends GuiButton {
|
||||
|
||||
public GuiButtonApply(int id, int x, int y){
|
||||
super(id, x, y, 16, 16, I18n.format("container." + Wizardry.MODID + ":arcane_workbench.apply"));
|
||||
}
|
||||
|
||||
@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 = 220;
|
||||
//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, 512, 256);
|
||||
//this.drawCenteredString(minecraft.fontRenderer, this.displayString, this.x + this.width / 2,
|
||||
// this.y + (this.height - 8) / 2, colour);
|
||||
}
|
||||
}
|
||||
|
||||
@SubscribeEvent
|
||||
public static void onTextureStitchEvent(TextureStitchEvent.Pre event){
|
||||
event.getMap().registerSprite(ContainerArcaneWorkbench.EMPTY_SLOT_CRYSTAL);
|
||||
event.getMap().registerSprite(ContainerArcaneWorkbench.EMPTY_SLOT_UPGRADE);
|
||||
}
|
||||
|
||||
}
|
||||
+3
-5
@@ -1,12 +1,10 @@
|
||||
package electroblob.wizardry.client;
|
||||
package electroblob.wizardry.client.gui;
|
||||
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.gui.GuiButton;
|
||||
import net.minecraftforge.fml.relauncher.Side;
|
||||
import net.minecraftforge.fml.relauncher.SideOnly;
|
||||
|
||||
@SideOnly(Side.CLIENT)
|
||||
class GuiButtonInvisible extends GuiButton {
|
||||
//@SideOnly(Side.CLIENT)
|
||||
public class GuiButtonInvisible extends GuiButton {
|
||||
|
||||
public GuiButtonInvisible(int id, int x, int y, int width, int height){
|
||||
super(id, x, y, width, height, "");
|
||||
@@ -0,0 +1,90 @@
|
||||
package electroblob.wizardry.client.gui;
|
||||
|
||||
import electroblob.wizardry.item.ISpellCastingItem;
|
||||
import electroblob.wizardry.item.ItemArtefact;
|
||||
import electroblob.wizardry.packet.PacketControlInput;
|
||||
import electroblob.wizardry.packet.WizardryPacketHandler;
|
||||
import electroblob.wizardry.registry.Spells;
|
||||
import electroblob.wizardry.registry.WizardryItems;
|
||||
import electroblob.wizardry.spell.Resurrection;
|
||||
import electroblob.wizardry.util.SpellModifiers;
|
||||
import electroblob.wizardry.util.WizardryUtilities;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.gui.GuiButton;
|
||||
import net.minecraft.client.gui.GuiGameOver;
|
||||
import net.minecraft.client.resources.I18n;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.util.EnumHand;
|
||||
import net.minecraftforge.client.event.GuiScreenEvent;
|
||||
import net.minecraftforge.fml.common.Mod;
|
||||
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
|
||||
import net.minecraftforge.fml.common.gameevent.TickEvent;
|
||||
import net.minecraftforge.fml.relauncher.Side;
|
||||
|
||||
@Mod.EventBusSubscriber(Side.CLIENT)
|
||||
public class GuiButtonResurrect extends GuiButton {
|
||||
|
||||
private static int timeSinceDeath = -1;
|
||||
|
||||
private final String translationKey;
|
||||
|
||||
public GuiButtonResurrect(int id, int x, int y, String translationKey){
|
||||
super(id, x, y, I18n.format(translationKey + "_wait", Resurrection.getRemainingWaitTime(timeSinceDeath)));
|
||||
this.translationKey = translationKey;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void drawButton(Minecraft mc, int mouseX, int mouseY, float partialTicks){
|
||||
int waitTime = Resurrection.getRemainingWaitTime(timeSinceDeath);
|
||||
this.enabled = waitTime == 0;
|
||||
this.displayString = I18n.format(translationKey + (waitTime == 0 ? "_ready" : "_wait"), waitTime);
|
||||
super.drawButton(mc, mouseX, mouseY, partialTicks);
|
||||
}
|
||||
|
||||
// Event handlers
|
||||
|
||||
@SubscribeEvent
|
||||
public static void onClientTickEvent(TickEvent.ClientTickEvent event){
|
||||
if(event.phase == TickEvent.Phase.START && timeSinceDeath >= 0) timeSinceDeath++;
|
||||
}
|
||||
|
||||
@SubscribeEvent
|
||||
public static void onGuiScreenInitEvent(GuiScreenEvent.InitGuiEvent event){
|
||||
|
||||
if(event.getGui() instanceof GuiGameOver && ItemArtefact.isArtefactActive(Minecraft.getMinecraft().player, WizardryItems.amulet_resurrection)
|
||||
&& WizardryUtilities.getHotbar(Minecraft.getMinecraft().player).stream().anyMatch(s -> Resurrection.canStackResurrect(s, Minecraft.getMinecraft().player))){
|
||||
|
||||
event.getButtonList().add(new GuiButtonResurrect(event.getButtonList().size(), event.getGui().width / 2 - 100,
|
||||
event.getGui().height / 4 + 120, "spell." + Spells.resurrection.getRegistryName() + ".button"));
|
||||
timeSinceDeath = 0;
|
||||
}
|
||||
}
|
||||
|
||||
@SubscribeEvent
|
||||
public static void onGuiScreenActionPerformedEvent(GuiScreenEvent.ActionPerformedEvent event){
|
||||
|
||||
if(event.getGui() instanceof GuiGameOver){
|
||||
|
||||
ItemStack stack = WizardryUtilities.getHotbar(Minecraft.getMinecraft().player).stream()
|
||||
.filter(s -> Resurrection.canStackResurrect(s, Minecraft.getMinecraft().player)).findFirst().orElse(null);
|
||||
|
||||
if(stack != null){
|
||||
|
||||
if(event.getButton() instanceof GuiButtonResurrect && timeSinceDeath >= 0){
|
||||
// Cast resurrection on the client player and notify the server to do the same
|
||||
// ISpellCastingItem#canCast already checked in Resurrection#canStackResurrect
|
||||
((ISpellCastingItem)stack.getItem()).cast(stack, Spells.resurrection, Minecraft.getMinecraft().player, EnumHand.MAIN_HAND, 0, new SpellModifiers());
|
||||
WizardryPacketHandler.net.sendToServer(new PacketControlInput.Message(PacketControlInput.ControlType.RESURRECT_BUTTON));
|
||||
|
||||
}else if(!Minecraft.getMinecraft().world.getGameRules().getBoolean("keepInventory")){
|
||||
// Any other button drops the wand (N.B. this should be inside the stack != null check or it'll send
|
||||
// packets unnecessarily and generate incorrect warnings
|
||||
WizardryPacketHandler.net.sendToServer(new PacketControlInput.Message(PacketControlInput.ControlType.CANCEL_RESURRECT));
|
||||
}
|
||||
|
||||
timeSinceDeath = -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
package electroblob.wizardry.client;
|
||||
package electroblob.wizardry.client.gui;
|
||||
|
||||
import net.minecraft.client.gui.inventory.GuiContainer;
|
||||
import net.minecraft.client.renderer.GlStateManager;
|
||||
@@ -0,0 +1,121 @@
|
||||
package electroblob.wizardry.client.gui;
|
||||
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import electroblob.wizardry.Wizardry;
|
||||
import electroblob.wizardry.client.DrawingUtils;
|
||||
import electroblob.wizardry.constants.Tier;
|
||||
import electroblob.wizardry.data.SpellGlyphData;
|
||||
import electroblob.wizardry.data.WizardData;
|
||||
import electroblob.wizardry.registry.Spells;
|
||||
import electroblob.wizardry.registry.WizardrySounds;
|
||||
import electroblob.wizardry.spell.Spell;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.audio.PositionedSoundRecord;
|
||||
import net.minecraft.client.gui.GuiScreen;
|
||||
import net.minecraft.client.renderer.GlStateManager;
|
||||
import net.minecraft.entity.player.EntityPlayer;
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
import org.lwjgl.input.Keyboard;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
public class GuiSpellBook extends GuiScreen {
|
||||
|
||||
private int xSize, ySize;
|
||||
private Spell spell;
|
||||
|
||||
private static final Map<Tier, ResourceLocation> textures = ImmutableMap.of(
|
||||
Tier.NOVICE, new ResourceLocation(Wizardry.MODID, "textures/gui/spell_book_novice.png"),
|
||||
Tier.APPRENTICE, new ResourceLocation(Wizardry.MODID, "textures/gui/spell_book_apprentice.png"),
|
||||
Tier.ADVANCED, new ResourceLocation(Wizardry.MODID, "textures/gui/spell_book_advanced.png"),
|
||||
Tier.MASTER, new ResourceLocation(Wizardry.MODID, "textures/gui/spell_book_master.png"));
|
||||
|
||||
public GuiSpellBook(Spell spell){
|
||||
super();
|
||||
xSize = 288;
|
||||
ySize = 180;
|
||||
this.spell = spell;
|
||||
}
|
||||
|
||||
/**
|
||||
* Draws the screen and all the components in it.
|
||||
*/
|
||||
public void drawScreen(int par1, int par2, float par3){
|
||||
|
||||
int xPos = this.width / 2 - xSize / 2;
|
||||
int yPos = this.height / 2 - this.ySize / 2;
|
||||
|
||||
EntityPlayer player = Minecraft.getMinecraft().player;
|
||||
|
||||
boolean discovered = true;
|
||||
if(Wizardry.settings.discoveryMode && !player.isCreative() && WizardData.get(player) != null
|
||||
&& !WizardData.get(player).hasSpellBeenDiscovered(spell)){
|
||||
discovered = false;
|
||||
}
|
||||
|
||||
GlStateManager.color(1, 1, 1, 1); // Just in case
|
||||
|
||||
// Draws spell illustration on opposite page, underneath the book so it shows through the hole.
|
||||
Minecraft.getMinecraft().renderEngine.bindTexture(discovered ? spell.getIcon() : Spells.none.getIcon());
|
||||
DrawingUtils.drawTexturedRect(xPos + 146, yPos + 20, 0, 0, 128, 128, 128, 128);
|
||||
|
||||
Minecraft.getMinecraft().renderEngine.bindTexture(textures.get(spell.getTier()));
|
||||
DrawingUtils.drawTexturedRect(xPos, yPos, 0, 0, xSize, ySize, xSize, 256);
|
||||
|
||||
super.drawScreen(par1, par2, par3);
|
||||
|
||||
if(discovered){
|
||||
this.fontRenderer.drawString(spell.getDisplayName(), xPos + 17, yPos + 15, 0);
|
||||
this.fontRenderer.drawString(spell.getType().getDisplayName(), xPos + 17, yPos + 26, 0x777777);
|
||||
}else{
|
||||
this.mc.standardGalacticFontRenderer.drawString(SpellGlyphData.getGlyphName(spell, player.world), xPos + 17,
|
||||
yPos + 15, 0);
|
||||
this.mc.standardGalacticFontRenderer.drawString(spell.getType().getDisplayName(), xPos + 17, yPos + 26,
|
||||
0x777777);
|
||||
}
|
||||
|
||||
//this.fontRenderer.drawString("-------------------", xPos + 17, yPos + 35, 0);
|
||||
|
||||
if(spell.getTier() == Tier.NOVICE){
|
||||
// Basic is usually white but this doesn't show up.
|
||||
this.fontRenderer.drawString("Tier: \u00A77" + Tier.NOVICE.getDisplayName(), xPos + 17, yPos + 45, 0);
|
||||
}else{
|
||||
this.fontRenderer.drawString("Tier: " + spell.getTier().getDisplayNameWithFormatting(), xPos + 17, yPos + 45, 0);
|
||||
}
|
||||
|
||||
String element = "Element: " + spell.getElement().getFormattingCode() + spell.getElement().getDisplayName();
|
||||
if(!discovered) element = "Element: ?";
|
||||
this.fontRenderer.drawString(element, xPos + 17, yPos + 57, 0);
|
||||
|
||||
String manaCost = "Mana Cost: " + spell.getCost();
|
||||
if(spell.isContinuous) manaCost = "Mana Cost: " + spell.getCost() + "/second";
|
||||
if(!discovered) manaCost = "Mana Cost: ?";
|
||||
this.fontRenderer.drawString(manaCost, xPos + 17, yPos + 69, 0);
|
||||
|
||||
if(discovered){
|
||||
this.fontRenderer.drawSplitString(spell.getDescription(), xPos + 17, yPos + 83, 118, 0);
|
||||
}else{
|
||||
this.mc.standardGalacticFontRenderer.drawSplitString(
|
||||
SpellGlyphData.getGlyphDescription(spell, player.world), xPos + 17, yPos + 83, 118, 0);
|
||||
}
|
||||
}
|
||||
|
||||
public void initGui(){
|
||||
super.initGui();
|
||||
Keyboard.enableRepeatEvents(true);
|
||||
this.buttonList.clear();
|
||||
|
||||
this.mc.getSoundHandler().playSound(PositionedSoundRecord.getMasterRecord(WizardrySounds.MISC_BOOK_OPEN, 1));
|
||||
}
|
||||
|
||||
public void onGuiClosed(){
|
||||
super.onGuiClosed();
|
||||
Keyboard.enableRepeatEvents(false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean doesGuiPauseGame(){
|
||||
return Wizardry.settings.booksPauseGame;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,589 @@
|
||||
package electroblob.wizardry.client.gui;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonObject;
|
||||
import electroblob.wizardry.Settings;
|
||||
import electroblob.wizardry.Wizardry;
|
||||
import electroblob.wizardry.client.ClientProxy;
|
||||
import electroblob.wizardry.client.DrawingUtils;
|
||||
import electroblob.wizardry.client.MixedFontRenderer;
|
||||
import electroblob.wizardry.data.SpellGlyphData;
|
||||
import electroblob.wizardry.data.WizardData;
|
||||
import electroblob.wizardry.item.ISpellCastingItem;
|
||||
import electroblob.wizardry.registry.Spells;
|
||||
import electroblob.wizardry.registry.WizardryPotions;
|
||||
import electroblob.wizardry.spell.Spell;
|
||||
import electroblob.wizardry.util.WandHelper;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.gui.FontRenderer;
|
||||
import net.minecraft.client.renderer.GlStateManager;
|
||||
import net.minecraft.client.renderer.GlStateManager.DestFactor;
|
||||
import net.minecraft.client.renderer.GlStateManager.SourceFactor;
|
||||
import net.minecraft.client.resources.IResource;
|
||||
import net.minecraft.client.resources.IResourceManager;
|
||||
import net.minecraft.entity.player.EntityPlayer;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.util.EnumHandSide;
|
||||
import net.minecraft.util.JsonUtils;
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
import net.minecraft.util.math.MathHelper;
|
||||
import net.minecraftforge.client.event.RenderGameOverlayEvent;
|
||||
import net.minecraftforge.event.entity.living.LivingEvent.LivingUpdateEvent;
|
||||
import net.minecraftforge.fml.common.Mod;
|
||||
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
|
||||
import net.minecraftforge.fml.relauncher.Side;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.util.*;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
@Mod.EventBusSubscriber(Side.CLIENT)
|
||||
public class GuiSpellDisplay {
|
||||
|
||||
private static final ResourceLocation INDEX = new ResourceLocation(Wizardry.MODID, "textures/gui/spell_hud/_index.json");
|
||||
|
||||
/** A map which stores all loaded HUD skin objects. This gets wiped on resource pack reload and repopulated with
|
||||
* mappings as specified by {@code _index.json} (these stack between resource packs). The keys in the map correspond
|
||||
* to the keys in {@code _index.json}, and are sorted in that order, with skins belonging to resource packs sorted
|
||||
* from lowest to highest priority. The skins in the base mod will therefore always be first. (It should be noted,
|
||||
* however, that in the gui itself the skins are always sorted in alphabetical order for some reason.) */
|
||||
private static final Map<String, Skin> skins = new LinkedHashMap<>(14); // 14 is the number of skins packaged with the mod
|
||||
|
||||
private static final Gson gson = new Gson();
|
||||
|
||||
/** Width and height of the spell icon (very unlikely to change!) */
|
||||
private static final int SPELL_ICON_SIZE = 32;
|
||||
/** Number of ticks the spell switching animation plays for. */
|
||||
private static final int SPELL_SWITCH_TIME = 4;
|
||||
/** Scale of the next/previous spell names. */
|
||||
private static final float SPELL_NAME_SCALE = 0.5f;
|
||||
/** Opacity of the next/previous spell names, as a fraction. */
|
||||
private static final float SPELL_NAME_OPACITY = 0.3f;
|
||||
|
||||
private static final int HALF_HOTBAR_WIDTH = 97; // Half the width of the hotbar, plus a bit for clearance
|
||||
private static final int OFFHAND_SLOT_WIDTH = 29; // Width of the offhand slot plus the gap between it and the hotbar
|
||||
|
||||
/** Controls the spell switching animation. Positive when switching to the next spell, negative when switching to
|
||||
* the previous spell. Decremented in magnitude by 1 each tick until it reaches 0 again. */
|
||||
private static int switchTimer = 0;
|
||||
|
||||
/**
|
||||
* Starts the spell switching animation.
|
||||
* @param next True to switch to the next spell, false for the previous spell.
|
||||
*/
|
||||
public static void playSpellSwitchAnimation(boolean next){
|
||||
switchTimer = next ? SPELL_SWITCH_TIME : -SPELL_SWITCH_TIME;
|
||||
}
|
||||
|
||||
/** Returns an unmodifiable set of the string keys for all of the loaded spell HUD skins. */
|
||||
public static Set<String> getSkinKeys(){
|
||||
return Collections.unmodifiableSet(skins.keySet());
|
||||
}
|
||||
|
||||
/** Returns an unmodifiable view of the loaded spell HUD skins map. */
|
||||
public static Map<String, Skin> getSkins(){
|
||||
return Collections.unmodifiableMap(skins);
|
||||
}
|
||||
|
||||
/** Returns the skin that corresponds to the given key. */
|
||||
public static Skin getSkin(String key){
|
||||
return skins.get(key);
|
||||
}
|
||||
|
||||
// Normally when extending Gui, you'd have to have an instance to access its methods. However, we're not actually
|
||||
// using any of them, so this class may as well not bother and just be a static event handler. Neat!
|
||||
@SubscribeEvent
|
||||
public static void draw(RenderGameOverlayEvent event){
|
||||
|
||||
Minecraft mc = Minecraft.getMinecraft();
|
||||
|
||||
EntityPlayer player = mc.player;
|
||||
|
||||
if(player.isSpectator()) return; // Spectators shouldn't have the spell HUD!
|
||||
|
||||
// If the player has a wand in each hand, only displays for the one in the main hand.
|
||||
|
||||
ItemStack wand = player.getHeldItemMainhand();
|
||||
boolean mainHand = true;
|
||||
|
||||
if(!(wand.getItem() instanceof ISpellCastingItem && ((ISpellCastingItem)wand.getItem()).showSpellHUD(player, wand))){
|
||||
wand = player.getHeldItemOffhand();
|
||||
mainHand = false;
|
||||
// If the player isn't holding a spellcasting item that shows the HUD, then nothing else needs to be done.
|
||||
if(!(wand.getItem() instanceof ISpellCastingItem && ((ISpellCastingItem)wand.getItem()).showSpellHUD(player, wand))) return;
|
||||
}
|
||||
|
||||
int width = event.getResolution().getScaledWidth();
|
||||
int height = event.getResolution().getScaledHeight();
|
||||
|
||||
boolean flipX = Wizardry.settings.spellHUDPosition.flipX;
|
||||
boolean flipY = Wizardry.settings.spellHUDPosition.flipY;
|
||||
|
||||
if(Wizardry.settings.spellHUDPosition.dynamic){
|
||||
// ............. | This bit is true if the wand is on the left, false if it is on the right
|
||||
flipX = flipX == ((mainHand ? player.getPrimaryHand() : player.getPrimaryHand().opposite()) == EnumHandSide.LEFT);
|
||||
}
|
||||
|
||||
Skin skin = skins.get(Wizardry.settings.spellHUDSkin);
|
||||
|
||||
if(skin == null){
|
||||
|
||||
Wizardry.logger.info("The spell HUD skin '" + Wizardry.settings.spellHUDSkin + "' specified in the config"
|
||||
+ " did not match any of the loaded skins; using the default skin as a fallback.");
|
||||
|
||||
skin = skins.get(Settings.DEFAULT_HUD_SKIN_KEY);
|
||||
|
||||
if(skin == null){
|
||||
Wizardry.logger.warn("The default spell HUD skin is missing! A resource pack must have overridden it"
|
||||
+ " with an invalid JSON file (default.json), please try again without any resource packs.");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
GlStateManager.pushMatrix();
|
||||
|
||||
// 'Origin' of the spell hud (bottom left corner of the actual texture, always in the corner of the screen)
|
||||
int x = flipX ? width : 0;
|
||||
int y = flipY ? 0: height;
|
||||
|
||||
// The space available to render the spell HUD
|
||||
float xSpace = (float)(width/2 - HALF_HOTBAR_WIDTH);
|
||||
if(!player.getHeldItemOffhand().isEmpty()
|
||||
// Tests whether the offhand slot is rendered on the same side of the hotbar as the spell HUD
|
||||
&& (player.getPrimaryHand() == EnumHandSide.LEFT) == flipX){
|
||||
xSpace -= OFFHAND_SLOT_WIDTH;
|
||||
}
|
||||
|
||||
// If the skin is at the bottom and the screen width is too small, scale it to avoid the hotbar and offhand
|
||||
if(!flipY && skin.getWidth() > xSpace){ // width/2 - 91 - 29 taken from GuiInGame line 547
|
||||
float scale = xSpace / skin.getWidth();
|
||||
GlStateManager.scale(scale, scale, 1);
|
||||
x = MathHelper.ceil(x/scale);
|
||||
y = MathHelper.ceil(y/scale);
|
||||
}
|
||||
|
||||
Spell spell = WandHelper.getCurrentSpell(wand);
|
||||
int cooldown = WandHelper.getCurrentCooldown(wand);
|
||||
int maxCooldown = WandHelper.getCurrentMaxCooldown(wand);
|
||||
|
||||
if(event.getType() == RenderGameOverlayEvent.ElementType.TEXT){
|
||||
|
||||
float animationProgress = Math.signum(switchTimer) * ((SPELL_SWITCH_TIME - Math.abs(switchTimer) +
|
||||
event.getPartialTicks()) / SPELL_SWITCH_TIME);
|
||||
|
||||
String prevSpellName = getFormattedSpellName(WandHelper.getPreviousSpell(wand), player, WandHelper.getPreviousCooldown(wand));
|
||||
String spellName = getFormattedSpellName(spell, player, cooldown);
|
||||
String nextSpellName = getFormattedSpellName(WandHelper.getNextSpell(wand), player, WandHelper.getNextCooldown(wand));
|
||||
|
||||
skin.drawText(x, y, flipX, flipY, prevSpellName, spellName, nextSpellName, animationProgress);
|
||||
|
||||
}else if(event.getType() == RenderGameOverlayEvent.ElementType.HOTBAR){
|
||||
|
||||
boolean discovered = true;
|
||||
|
||||
if(!player.isCreative() && WizardData.get(player) != null){
|
||||
discovered = WizardData.get(player).hasSpellBeenDiscovered(spell);
|
||||
}
|
||||
|
||||
ResourceLocation icon = discovered ? spell.getIcon() : Spells.none.getIcon();
|
||||
|
||||
float progress = 1;
|
||||
// Doesn't really matter what progress is when in creative, but we might as well avoid the calculation.
|
||||
if(!player.isCreative() && !spell.isContinuous){
|
||||
// Subtracted partial tick time to make it smoother
|
||||
progress = maxCooldown == 0 ? 1 : (maxCooldown - (float)cooldown + event.getPartialTicks()) / maxCooldown;
|
||||
}
|
||||
|
||||
skin.drawBackground(x, y, flipX, flipY, icon, progress, player.isCreative());
|
||||
|
||||
}
|
||||
|
||||
GlStateManager.popMatrix();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the name of the given spell, with formatting added according to its cooldown and whether the given player
|
||||
* has discovered it.
|
||||
* @param spell The spell to get the name of.
|
||||
* @param player The player to test for having discovered the given spell.
|
||||
* @param cooldown The spell's current cooldown.
|
||||
* @return The spell name, with relevant formatting added, for use with the {@link MixedFontRenderer}.
|
||||
*/
|
||||
private static String getFormattedSpellName(Spell spell, EntityPlayer player, int cooldown){
|
||||
|
||||
boolean discovered = true;
|
||||
|
||||
if(!player.isCreative() && WizardData.get(player) != null){
|
||||
discovered = WizardData.get(player).hasSpellBeenDiscovered(spell);
|
||||
}
|
||||
|
||||
// Makes spells greyed out if they are in cooldown or if the player has the arcane jammer effect
|
||||
String format = cooldown > 0 || player.isPotionActive(WizardryPotions.arcane_jammer) ? "\u00A78" : spell.getElement().getFormattingCode();
|
||||
if(!discovered) format = "\u00A79";
|
||||
|
||||
String name = discovered ? spell.getDisplayName() : SpellGlyphData.getGlyphName(spell, player.world);
|
||||
name = format + name;
|
||||
if(!discovered) name = "#" + name + "#";
|
||||
|
||||
return name;
|
||||
}
|
||||
|
||||
@SubscribeEvent
|
||||
public static void onLivingUpdateEvent(LivingUpdateEvent event){
|
||||
if(event.getEntity() == Minecraft.getMinecraft().player){ // Makes sure this only gets called once each tick.
|
||||
if(switchTimer > 0) switchTimer--;
|
||||
else if(switchTimer < 0) switchTimer++;
|
||||
}
|
||||
}
|
||||
|
||||
/** Called from preInit in the main mod class (via the proxies) to initialise the HUD skins, and again on each
|
||||
* resource reload. */
|
||||
public static void loadSkins(IResourceManager manager){
|
||||
|
||||
try {
|
||||
|
||||
List<IResource> indexFiles = manager.getAllResources(INDEX);
|
||||
|
||||
skins.clear(); // Wipes the skins map before repopulating it
|
||||
|
||||
for(IResource indexFile : indexFiles){
|
||||
|
||||
BufferedReader reader = new BufferedReader(new InputStreamReader(indexFile.getInputStream()));
|
||||
|
||||
JsonElement je = gson.fromJson(reader, JsonElement.class);
|
||||
JsonObject json = je.getAsJsonObject();
|
||||
|
||||
// Need to iterate over these since we don't know what they're called or how many there are
|
||||
for(Entry<String, JsonElement> entry : json.entrySet()){
|
||||
|
||||
String key = entry.getKey(); // Find out what each element is called, this will be the skins map key
|
||||
|
||||
// It's a good idea to use JsonUtils because it produces more helpful error messages (that pack
|
||||
// makers should understand).
|
||||
|
||||
JsonObject skinData = JsonUtils.getJsonObject(json, key);
|
||||
|
||||
String[] splitName = ResourceLocation.splitObjectName(JsonUtils.getString(skinData, "texture"));
|
||||
ResourceLocation texture = new ResourceLocation(splitName[0], "textures/" + splitName[1] + ".png");
|
||||
|
||||
splitName = ResourceLocation.splitObjectName(JsonUtils.getString(skinData, "metadata"));
|
||||
ResourceLocation metadata = new ResourceLocation(splitName[0], "textures/" + splitName[1] + ".json");
|
||||
|
||||
// The nice thing about this is it overwrites the existing mapping, and since the index files are in
|
||||
// ascending order of resource pack priority, this means resource packs can override existing skins
|
||||
// by specifying one with the same key.
|
||||
skins.put(key, new Skin(texture, metadata));
|
||||
}
|
||||
}
|
||||
|
||||
} catch (IOException e){
|
||||
// If an exception is thrown, chances are the resource pack did not have a spell_hud folder, so nothing
|
||||
// else needs to be done.
|
||||
Wizardry.logger.error("Error reading spell HUD skin index file: ", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Instances of this class represent individual HUD skins, complete with texture and all necessary metadata. This
|
||||
* class serves to separate the logic behind the spell HUD from its actual rendering.
|
||||
* All information and processing done within this class relates only to the actual drawing; spells and such like
|
||||
* must be queried outside of this class and fed into the methods as appropriate.
|
||||
*
|
||||
* @author Electroblob
|
||||
* @since Wizardry 4.2
|
||||
*/
|
||||
public static class Skin {
|
||||
|
||||
/** The texture file for this skin. */
|
||||
private final ResourceLocation texture;
|
||||
|
||||
/** The display name of the skin in the config menu. */
|
||||
private String name;
|
||||
/** The description of the skin shown when its button is hovered over in the config menu. */
|
||||
private String description;
|
||||
|
||||
/** Width of the entire spell HUD. */
|
||||
private int width;
|
||||
/** Height of the entire spell HUD. */
|
||||
private int height;
|
||||
|
||||
/** Whether the entire HUD is flipped when on the right-hand side of the screen. If this is false, the HUD will
|
||||
* still appear on the right-hand side of the screen, but in the same orientation as on the left-hand side. */
|
||||
private boolean mirrorX;
|
||||
/** Whether the entire HUD is flipped when at the top of the screen. If this is false, the HUD will
|
||||
* still appear at the top of the screen, but in the same orientation as at the bottom. */
|
||||
private boolean mirrorY;
|
||||
|
||||
/** Distance of the spell icon from the left edge of the screen (or right edge when flipped). */
|
||||
private int spellIconInsetX;
|
||||
/** Distance of the spell icon from the bottom edge of the screen (or top edge when flipped). */
|
||||
private int spellIconInsetY;
|
||||
|
||||
/** Distance of the spell name from the left edge of the screen (or right edge when flipped). */
|
||||
private int textInsetX;
|
||||
/** Distance of the spell name from the bottom edge of the screen (or the top edge when flipped). */
|
||||
private int textInsetY;
|
||||
|
||||
/** Horizontal distance between the start of adjacent spell names. */
|
||||
private int cascadeOffsetX;
|
||||
/** Vertical distance between the start of adjacent spell names. */
|
||||
private int cascadeOffsetY;
|
||||
|
||||
/** Distance of the cooldown bar from the left edge of the screen (or right edge when flipped). */
|
||||
private int cooldownBarX;
|
||||
/** Distance of the cooldown bar from the bottom edge of the screen (or top edge when flipped). */
|
||||
private int cooldownBarY;
|
||||
/** Length of the cooldown bar. */
|
||||
private int cooldownBarLength;
|
||||
/** Height of the cooldown bar. */
|
||||
private int cooldownBarHeight;
|
||||
|
||||
/** Whether the cooldown bar is flipped horizontally when the HUD is on the right-hand side of the screen. */
|
||||
private boolean cooldownBarMirrorX;
|
||||
/** Whether the cooldown bar is flipped vertically when the HUD is at the top of the screen. */
|
||||
private boolean cooldownBarMirrorY;
|
||||
|
||||
/** Whether the cooldown bar progress overlay is shown when the cooldown bar is full (i.e. when progress = 1). */
|
||||
private boolean showCooldownWhenFull;
|
||||
|
||||
private final Minecraft mc;
|
||||
|
||||
/** Creates a new skin with the given texture and reads its values from the given metadata json file. */
|
||||
public Skin(ResourceLocation texture, ResourceLocation metadata){
|
||||
|
||||
mc = Minecraft.getMinecraft();
|
||||
|
||||
this.texture = texture;
|
||||
|
||||
try {
|
||||
// This time we only want the highest priority file
|
||||
IResource metadataFile = Minecraft.getMinecraft().getResourceManager().getResource(metadata);
|
||||
BufferedReader reader = new BufferedReader(new InputStreamReader(metadataFile.getInputStream()));
|
||||
|
||||
JsonElement je = gson.fromJson(reader, JsonElement.class);
|
||||
|
||||
parseJson(je.getAsJsonObject());
|
||||
|
||||
} catch (IOException e){
|
||||
// If an exception is thrown, chances are the resource pack did not have a spell_hud folder, so nothing
|
||||
// else needs to be done.
|
||||
Wizardry.logger.error("Error reading spell HUD skin metadata file: ", e);
|
||||
}
|
||||
}
|
||||
|
||||
/** Returns the display name of this HUD skin, which is shown in the config GUI. */
|
||||
public String getName(){
|
||||
return name;
|
||||
}
|
||||
|
||||
/** Returns the description of this HUD skin, which is shown in its tooltip in the config GUI. */
|
||||
public String getDescription(){
|
||||
return description;
|
||||
}
|
||||
|
||||
/** Returns the overall width of this spell HUD skin. */
|
||||
public int getWidth(){
|
||||
return width;
|
||||
}
|
||||
|
||||
/** Returns the overall height of this spell HUD skin. */
|
||||
public int getHeight(){
|
||||
return height;
|
||||
}
|
||||
|
||||
/** Actually reads the metadata values for this skin from the json file. */
|
||||
private void parseJson(JsonObject json){
|
||||
|
||||
// For now, all the keys must be present for the metadata file to work (the only ones that could reasonably
|
||||
// have a default anyway are the mirror values).
|
||||
|
||||
name = JsonUtils.getString(json, "name");
|
||||
description = JsonUtils.getString(json, "description");
|
||||
|
||||
width = JsonUtils.getInt(json, "width");
|
||||
if(width > 128) Wizardry.logger.warn("The width of the spell HUD skin " + name + " exceeds 128, this may cause it to render strangely.");
|
||||
height = JsonUtils.getInt(json, "height");
|
||||
|
||||
JsonObject mirror = JsonUtils.getJsonObject(json, "mirror");
|
||||
mirrorX = JsonUtils.getBoolean(mirror, "x");
|
||||
mirrorY = JsonUtils.getBoolean(mirror, "y");
|
||||
|
||||
JsonObject spellIconInset = JsonUtils.getJsonObject(json, "spell_icon_inset");
|
||||
spellIconInsetX = JsonUtils.getInt(spellIconInset, "x");
|
||||
spellIconInsetY = JsonUtils.getInt(spellIconInset, "y");
|
||||
|
||||
JsonObject textInset = JsonUtils.getJsonObject(json, "text_inset");
|
||||
textInsetX = JsonUtils.getInt(textInset, "x");
|
||||
textInsetY = JsonUtils.getInt(textInset, "y");
|
||||
|
||||
JsonObject cascadeOffset = JsonUtils.getJsonObject(json, "spell_cascade_offset");
|
||||
cascadeOffsetX = JsonUtils.getInt(cascadeOffset, "x");
|
||||
cascadeOffsetY = JsonUtils.getInt(cascadeOffset, "y");
|
||||
|
||||
JsonObject cooldownBar = JsonUtils.getJsonObject(json, "cooldown_bar");
|
||||
cooldownBarX = JsonUtils.getInt(cooldownBar, "x");
|
||||
cooldownBarY = JsonUtils.getInt(cooldownBar, "y");
|
||||
cooldownBarLength = JsonUtils.getInt(cooldownBar, "length");
|
||||
cooldownBarHeight = JsonUtils.getInt(cooldownBar, "height");
|
||||
|
||||
JsonObject cooldownBarMirror = JsonUtils.getJsonObject(cooldownBar, "mirror");
|
||||
cooldownBarMirrorX = JsonUtils.getBoolean(cooldownBarMirror, "x");
|
||||
cooldownBarMirrorY = JsonUtils.getBoolean(cooldownBarMirror, "y");
|
||||
|
||||
showCooldownWhenFull = JsonUtils.getBoolean(cooldownBar, "show_when_full");
|
||||
|
||||
}
|
||||
|
||||
// The idea of these methods is that everything in here relates only to the actual drawing of the HUD. In other
|
||||
// words, all processing of which spells to draw and so on is done outside of here. This means that the config
|
||||
// GUI can easily display its preview without having a player or wand stack object to query.
|
||||
|
||||
/**
|
||||
* Draws the background layer of this HUD skin at the given position with the given orientations, with the given
|
||||
* spell icon and cooldown bar progress.
|
||||
*
|
||||
* @param x The x-coordinate of the corner of the spell HUD. The bottom left corner <i>of the actual texture</i>
|
||||
* will always be at this position unless mirrorX/Y is false, so for example if flipX is false and flipY is true,
|
||||
* this will be the corner of the HUD that is closest to the top left corner of the screen.
|
||||
* @param y The y-coordinate of the corner of the spell HUD; see above.
|
||||
* @param flipX Whether to flip the HUD horizontally.
|
||||
* @param flipY Whether to flip the HUD vertically.
|
||||
* @param icon A {@code ResourceLocation} corresponding to the icon of the selected spell.
|
||||
* @param cooldownBarProgress The fraction of the cooldown bar to draw; must be between 0 and 1 (inclusive).
|
||||
* @param creativeMode True to draw the creative mode HUD, false for the survival mode version.
|
||||
*/
|
||||
public void drawBackground(int x, int y, boolean flipX, boolean flipY, ResourceLocation icon, float cooldownBarProgress, boolean creativeMode){
|
||||
|
||||
// Moves the origin if the HUD does not mirror; neatens the rest of the code.
|
||||
if(flipX && !mirrorX) x -= width;
|
||||
if(flipY && !mirrorY) y += height;
|
||||
|
||||
GlStateManager.pushMatrix();
|
||||
GlStateManager.enableBlend();
|
||||
GlStateManager.blendFunc(SourceFactor.SRC_ALPHA, DestFactor.ONE_MINUS_SRC_ALPHA);
|
||||
GlStateManager.color(1, 1, 1);
|
||||
|
||||
// Spell illustration - this is now done first so it is behind the HUD texture
|
||||
mc.renderEngine.bindTexture(icon);
|
||||
|
||||
int x1 = flipX && mirrorX ? x - spellIconInsetX - SPELL_ICON_SIZE : x + spellIconInsetX;
|
||||
// y is upside-down so this is the other way round
|
||||
int y1 = flipY && mirrorY ? y + spellIconInsetY : y - spellIconInsetY - SPELL_ICON_SIZE;
|
||||
|
||||
DrawingUtils.drawTexturedRect(x1, y1, 0, 0, SPELL_ICON_SIZE, SPELL_ICON_SIZE, SPELL_ICON_SIZE, SPELL_ICON_SIZE);
|
||||
|
||||
// Background of spell hud
|
||||
mc.renderEngine.bindTexture(texture);
|
||||
|
||||
x1 = flipX && mirrorX ? x - width : x;
|
||||
y1 = flipY && mirrorY ? y : y - height;
|
||||
// The 128 here is a uv value, not a dimension, and hence is left as a hardcoded number.
|
||||
// TODO: Since the HUD is wider than it is tall, perhaps the creative mode texture should be in the bottom half instead of the right half?
|
||||
DrawingUtils.drawTexturedFlippedRect(x1, y1, creativeMode ? 128 : 0, 0, width, height, 256, 256, flipX && mirrorX, flipY && mirrorY);
|
||||
|
||||
// Cooldown bar
|
||||
if(!creativeMode && cooldownBarProgress > 0 && (showCooldownWhenFull || cooldownBarProgress < 1)){
|
||||
|
||||
int l = (int)(cooldownBarProgress * cooldownBarLength);
|
||||
|
||||
x1 = flipX && mirrorX ? x - cooldownBarX - (cooldownBarMirrorX ? l : cooldownBarLength) : x + cooldownBarX;
|
||||
y1 = flipY && mirrorY ? y + cooldownBarY : y - cooldownBarY - cooldownBarHeight;
|
||||
|
||||
int u = cooldownBarX; // This doesn't change, even when cooldownBarMirrorX is true, because it should
|
||||
int v = height; // always start with the left-hand in the actual texture file
|
||||
|
||||
DrawingUtils.drawTexturedFlippedRect(x1, y1, u, v, l, cooldownBarHeight, 256, 256, flipX && cooldownBarMirrorX, flipY && cooldownBarMirrorY);
|
||||
}
|
||||
|
||||
GlStateManager.popMatrix();
|
||||
|
||||
// Blend needs to be left enabled here because otherwise the hotbar becomes opaque
|
||||
}
|
||||
|
||||
/**
|
||||
* Draws the text layer of this HUD skin at the given position with the given orientations, with the given
|
||||
* spell name strings.
|
||||
*
|
||||
* @param x The x-coordinate of the corner of the spell HUD. The bottom left corner <i>of the actual texture</i>
|
||||
* will always be at this position, so for example if flipX is false and flipY is true, this will be the corner
|
||||
* of the HUD that is closest to the top left corner of the screen.
|
||||
* @param y The y-coordinate of the corner of the spell HUD; see above.
|
||||
* @param flipX Whether to flip the HUD horizontally.
|
||||
* @param flipY Whether to flip the HUD vertically.
|
||||
* @param prevSpellName The name of the previous spell. This string will be drawn <i>directly</i> using the
|
||||
* {@link MixedFontRenderer}; as such it should be supplied with formatting codes and # characters already
|
||||
* appended.
|
||||
* @param spellName The name of the currently selected spell; see above.
|
||||
* @param nextSpellName The name of the next spell; see above.
|
||||
* @param animationProgress The progress of the spell switching animation, as a fraction between 0 and 1
|
||||
* (inclusive). Positive values indicate switching forwards, negative values indicate switching backwards, and
|
||||
* a value of zero indicates that the spell is not currently being switched.
|
||||
*/
|
||||
public void drawText(int x, int y, boolean flipX, boolean flipY, String prevSpellName, String spellName, String nextSpellName, float animationProgress){
|
||||
|
||||
// Moves the origin if the HUD does not mirror; neatens the rest of the code.
|
||||
if(flipX && !mirrorX) x -= width;
|
||||
if(flipY && !mirrorY) y += height;
|
||||
|
||||
FontRenderer font = ClientProxy.mixedFontRenderer; // On this occasion we're client-side so this is OK
|
||||
|
||||
// Position of the selected spell name in normal display, also used for interpolation when animating
|
||||
int x1 = flipX && mirrorX ? x - width : x + textInsetX;
|
||||
// The text is an odd number of pixels high so we need to subtract an extra 1 when not flipped
|
||||
int y1 = flipY && mirrorY ? y + textInsetY - font.FONT_HEIGHT/2 + 2 : y - textInsetY - font.FONT_HEIGHT/2 - 1;
|
||||
|
||||
int maxWidth = width - textInsetX; // Maximum width of the text
|
||||
|
||||
if(animationProgress == 0){ // Normal display
|
||||
|
||||
float xPrev = flipX && mirrorX ? x - width : x + textInsetX - (flipY ? -1 : 1) * cascadeOffsetX;
|
||||
float xNext = flipX && mirrorX ? x - width : x + textInsetX + (flipY ? -1 : 1) * cascadeOffsetX;
|
||||
// Don't ask me why adding 1 to this makes it look more even, it just does!
|
||||
float yPrev = y1 - (cascadeOffsetY + 1); // No need to account for flipY because previous is always above.
|
||||
float yNext = y1 + cascadeOffsetY; // No need to account for flipY because next is always below.
|
||||
float maxWidthPrev = maxWidth + (flipY ? -1 : 1) * cascadeOffsetX;
|
||||
float maxWidthNext = maxWidth - (flipY ? -1 : 1) * cascadeOffsetX;
|
||||
int nextPrevClr = DrawingUtils.makeTranslucent(0xffffff, SPELL_NAME_OPACITY);
|
||||
|
||||
DrawingUtils.drawScaledStringToWidth(font, prevSpellName, xPrev, yPrev, SPELL_NAME_SCALE, nextPrevClr, maxWidthPrev, true, flipX && mirrorX);
|
||||
DrawingUtils.drawScaledStringToWidth(font, spellName, x1, y1, 1, 0xffffffff, maxWidth, true, flipX && mirrorX);
|
||||
DrawingUtils.drawScaledStringToWidth(font, nextSpellName, xNext, yNext, SPELL_NAME_SCALE, nextPrevClr, maxWidthNext, true, flipX && mirrorX);
|
||||
|
||||
}else{ // Switching spells
|
||||
|
||||
boolean reverse = animationProgress < 0;
|
||||
if(reverse) animationProgress = 1 - Math.abs(animationProgress); // Simplest way of reversing the animation
|
||||
|
||||
float xPrev = flipX && mirrorX ? x - width : x + textInsetX - (flipY ? -1 : 1) * cascadeOffsetX * animationProgress;
|
||||
float xNext = flipX && mirrorX ? x - width : x + textInsetX + (flipY ? -1 : 1) * cascadeOffsetX * (1 - animationProgress);
|
||||
float yPrev = y1 - (cascadeOffsetY + 1) * animationProgress; // No need to account for flipY because previous is always above.
|
||||
float yNext = y1 + cascadeOffsetY * (1 - animationProgress); // No need to account for flipY because next is always below.
|
||||
float maxWidthPrev = maxWidth + (flipY ? -1 : 1) * cascadeOffsetX * animationProgress;
|
||||
float maxWidthNext = maxWidth - (flipY ? -1 : 1) * cascadeOffsetX * (1 - animationProgress);
|
||||
float scalePrev = SPELL_NAME_SCALE + (1 - SPELL_NAME_SCALE) * (1 - animationProgress);
|
||||
float scaleNext = SPELL_NAME_SCALE + (1 - SPELL_NAME_SCALE) * (animationProgress);
|
||||
int clrPrev = DrawingUtils.makeTranslucent(0xffffff, SPELL_NAME_OPACITY + (1 - SPELL_NAME_OPACITY) * (1 - animationProgress));
|
||||
int clrNext = DrawingUtils.makeTranslucent(0xffffff, SPELL_NAME_OPACITY + (1 - SPELL_NAME_OPACITY) * animationProgress);
|
||||
|
||||
if(reverse){ // Switching to previous spell
|
||||
|
||||
// Only renders the next spell and the current one
|
||||
DrawingUtils.drawScaledStringToWidth(font, spellName, xPrev, yPrev, scalePrev, clrPrev, maxWidthPrev, true, flipX && mirrorX);
|
||||
DrawingUtils.drawScaledStringToWidth(font, nextSpellName, xNext, yNext, scaleNext, clrNext, maxWidthNext, true, flipX && mirrorX);
|
||||
|
||||
}else{ // Switching to next spell
|
||||
|
||||
// Only renders the previous spell and the current one
|
||||
DrawingUtils.drawScaledStringToWidth(font, prevSpellName, xPrev, yPrev, scalePrev, clrPrev, maxWidthPrev, true, flipX && mirrorX);
|
||||
DrawingUtils.drawScaledStringToWidth(font, spellName, xNext, yNext, scaleNext, clrNext, maxWidthNext, true, flipX && mirrorX);
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
+6
-10
@@ -1,21 +1,17 @@
|
||||
package electroblob.wizardry.client;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.stream.Collectors;
|
||||
package electroblob.wizardry.client.gui.config;
|
||||
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.resources.I18n;
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
import net.minecraftforge.fml.client.config.GuiButtonExt;
|
||||
import net.minecraftforge.fml.client.config.GuiEditArray;
|
||||
import net.minecraftforge.fml.client.config.GuiEditArrayEntries;
|
||||
import net.minecraftforge.fml.client.config.*;
|
||||
import net.minecraftforge.fml.client.config.GuiEditArrayEntries.StringEntry;
|
||||
import net.minecraftforge.fml.client.config.GuiSelectString;
|
||||
import net.minecraftforge.fml.client.config.IConfigElement;
|
||||
import net.minecraftforge.fml.common.registry.EntityEntry;
|
||||
import net.minecraftforge.fml.common.registry.ForgeRegistries;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* [NYI] Intended as a way of choosing entities by name from all those currently registered, within the config file, so
|
||||
* that users don't have to look up the entity IDs. I can't get this to work correctly at the moment.
|
||||
@@ -0,0 +1,141 @@
|
||||
package electroblob.wizardry.client.gui.config;
|
||||
|
||||
import electroblob.wizardry.Settings;
|
||||
import electroblob.wizardry.Wizardry;
|
||||
import net.minecraft.client.gui.GuiScreen;
|
||||
import net.minecraft.client.resources.I18n;
|
||||
import net.minecraftforge.common.config.ConfigElement;
|
||||
import net.minecraftforge.common.config.Configuration;
|
||||
import net.minecraftforge.fml.client.config.DummyConfigElement.DummyCategoryElement;
|
||||
import net.minecraftforge.fml.client.config.GuiConfig;
|
||||
import net.minecraftforge.fml.client.config.GuiConfigEntries;
|
||||
import net.minecraftforge.fml.client.config.GuiConfigEntries.CategoryEntry;
|
||||
import net.minecraftforge.fml.client.config.IConfigElement;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class GuiConfigWizardry extends GuiConfig {
|
||||
|
||||
public GuiConfigWizardry(GuiScreen parent){
|
||||
super(parent, getConfigEntries(), Wizardry.MODID, false, false,
|
||||
Wizardry.NAME + " - " + I18n.format("config." + Wizardry.MODID + ".title.general"));
|
||||
// this.titleLine2 = "File location: " + Wizardry.config.getConfigFile().getAbsolutePath();
|
||||
}
|
||||
|
||||
private static List<IConfigElement> getConfigEntries(){
|
||||
|
||||
List<IConfigElement> configList = new ArrayList<IConfigElement>(1);
|
||||
|
||||
configList.add(new DummyCategoryElement("gameplayConfig", "config." + Wizardry.MODID + ".category." + Settings.GAMEPLAY_CATEGORY, GameplayCategory.class));
|
||||
configList.add(new DummyCategoryElement("worldgenConfig", "config." + Wizardry.MODID + ".category." + Settings.WORLDGEN_CATEGORY, WorldgenCategory.class));
|
||||
configList.add(new DummyCategoryElement("commandsConfig", "config." + Wizardry.MODID + ".category." + Settings.COMMANDS_CATEGORY, CommandsCategory.class));
|
||||
configList.add(new DummyCategoryElement("clientConfig", "config." + Wizardry.MODID + ".category." + Settings.CLIENT_CATEGORY, ClientCategory.class));
|
||||
configList.add(new DummyCategoryElement("spellsConfig", "config." + Wizardry.MODID + ".category." + Settings.SPELLS_CATEGORY, SpellsCategory.class));
|
||||
configList.add(new DummyCategoryElement("resistancesConfig", "config." + Wizardry.MODID + ".category." + Settings.RESISTANCES_CATEGORY, ResistancesCategory.class));
|
||||
configList.add(new DummyCategoryElement("compatibilityConfig", "config." + Wizardry.MODID + ".category." + Settings.COMPATIBILITY_CATEGORY, CompatibilityCategory.class));
|
||||
|
||||
configList.addAll(new ConfigElement(Wizardry.settings.getConfigCategory(Configuration.CATEGORY_GENERAL)).getChildElements());
|
||||
|
||||
return configList;
|
||||
}
|
||||
|
||||
// The reason this system is so convoluted is that it's designed for use with the @Config annotation. The problem is,
|
||||
// I'm not sure whether that will play well with the load phases. Hmmm...
|
||||
|
||||
public static abstract class CategoryBase extends CategoryEntry {
|
||||
|
||||
public CategoryBase(GuiConfig owningScreen, GuiConfigEntries owningEntryList, IConfigElement prop){
|
||||
super(owningScreen, owningEntryList, prop);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected GuiScreen buildChildScreen(){
|
||||
|
||||
String category = this.getCategory();
|
||||
|
||||
// This GuiConfig object specifies the configID of the object and as such will force-save when it is closed.
|
||||
// The parent GuiConfig object's entryList will also be refreshed to reflect the changes.
|
||||
GuiConfig childScreen = new GuiConfig(this.owningScreen,
|
||||
(new ConfigElement(Wizardry.settings.getConfigCategory(category))).getChildElements(),
|
||||
this.owningScreen.modID, category, false, false,
|
||||
Wizardry.NAME + " - " + I18n.format("config." + Wizardry.MODID + ".title." + category));
|
||||
|
||||
childScreen.titleLine2 = I18n.format("config." + Wizardry.MODID + ".subtitle." + category);
|
||||
|
||||
return childScreen;
|
||||
}
|
||||
|
||||
protected abstract String getCategory();
|
||||
}
|
||||
|
||||
/** Gameplay category of the config gui. */
|
||||
public static class GameplayCategory extends CategoryBase {
|
||||
|
||||
public GameplayCategory(GuiConfig owningScreen, GuiConfigEntries owningEntryList, IConfigElement prop){
|
||||
super(owningScreen, owningEntryList, prop);
|
||||
}
|
||||
|
||||
@Override protected String getCategory() { return Settings.GAMEPLAY_CATEGORY; }
|
||||
}
|
||||
|
||||
/** Worldgen category of the config gui. */
|
||||
public static class WorldgenCategory extends CategoryBase {
|
||||
|
||||
public WorldgenCategory(GuiConfig owningScreen, GuiConfigEntries owningEntryList, IConfigElement prop){
|
||||
super(owningScreen, owningEntryList, prop);
|
||||
}
|
||||
|
||||
@Override protected String getCategory() { return Settings.WORLDGEN_CATEGORY; }
|
||||
}
|
||||
|
||||
/** Commands category of the config gui. */
|
||||
public static class CommandsCategory extends CategoryBase {
|
||||
|
||||
public CommandsCategory(GuiConfig owningScreen, GuiConfigEntries owningEntryList, IConfigElement prop){
|
||||
super(owningScreen, owningEntryList, prop);
|
||||
}
|
||||
|
||||
@Override protected String getCategory() { return Settings.COMMANDS_CATEGORY; }
|
||||
}
|
||||
|
||||
/** Client category of the config gui. */
|
||||
public static class ClientCategory extends CategoryBase {
|
||||
|
||||
public ClientCategory(GuiConfig owningScreen, GuiConfigEntries owningEntryList, IConfigElement prop){
|
||||
super(owningScreen, owningEntryList, prop);
|
||||
}
|
||||
|
||||
@Override protected String getCategory() { return Settings.CLIENT_CATEGORY; }
|
||||
}
|
||||
|
||||
/** Spells category of the config gui. */
|
||||
public static class SpellsCategory extends CategoryBase {
|
||||
|
||||
public SpellsCategory(GuiConfig owningScreen, GuiConfigEntries owningEntryList, IConfigElement prop){
|
||||
super(owningScreen, owningEntryList, prop);
|
||||
}
|
||||
|
||||
@Override protected String getCategory() { return Settings.SPELLS_CATEGORY; }
|
||||
}
|
||||
|
||||
/** Resistances category of the config gui. */
|
||||
public static class ResistancesCategory extends CategoryBase {
|
||||
|
||||
public ResistancesCategory(GuiConfig owningScreen, GuiConfigEntries owningEntryList, IConfigElement prop){
|
||||
super(owningScreen, owningEntryList, prop);
|
||||
}
|
||||
|
||||
@Override protected String getCategory() { return Settings.RESISTANCES_CATEGORY; }
|
||||
}
|
||||
|
||||
/** Commands category of the config gui. */
|
||||
public static class CompatibilityCategory extends CategoryBase {
|
||||
|
||||
public CompatibilityCategory(GuiConfig owningScreen, GuiConfigEntries owningEntryList, IConfigElement prop){
|
||||
super(owningScreen, owningEntryList, prop);
|
||||
}
|
||||
|
||||
@Override protected String getCategory() { return Settings.COMPATIBILITY_CATEGORY; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,119 @@
|
||||
package electroblob.wizardry.client.gui.config;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
import electroblob.wizardry.Wizardry;
|
||||
import electroblob.wizardry.client.gui.GuiSpellDisplay;
|
||||
import electroblob.wizardry.client.gui.GuiSpellDisplay.Skin;
|
||||
import electroblob.wizardry.registry.Spells;
|
||||
import net.minecraft.client.gui.GuiButton;
|
||||
import net.minecraft.client.gui.GuiScreen;
|
||||
import net.minecraft.client.renderer.GlStateManager;
|
||||
import net.minecraft.client.resources.I18n;
|
||||
import net.minecraftforge.fml.client.config.GuiSelectString;
|
||||
import net.minecraftforge.fml.client.config.IConfigElement;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.Map;
|
||||
|
||||
public class GuiSelectHUDSkin extends GuiSelectString {
|
||||
|
||||
public GuiSelectHUDSkin(GuiScreen parentScreen, IConfigElement configElement, int slotIndex, Map<Object, String> selectableValues, Object currentValue, boolean enabled){
|
||||
super(parentScreen, configElement, slotIndex, selectableValues, currentValue, enabled);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initGui(){
|
||||
super.initGui();
|
||||
setEntryListDimensions();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void actionPerformed(GuiButton button){
|
||||
super.actionPerformed(button);
|
||||
setEntryListDimensions(); // Stops the entry list from resizing when a button is pressed
|
||||
}
|
||||
|
||||
private void setEntryListDimensions(){
|
||||
this.entryList.setDimensions(150, height, 43, height-43);
|
||||
this.entryList.left = 10;
|
||||
this.entryList.maxEntryWidth = 120;
|
||||
this.entryList.headerPadding = 5;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void drawScreen(int mouseX, int mouseY, float partialTicks){
|
||||
|
||||
super.drawScreen(mouseX, mouseY, partialTicks);
|
||||
|
||||
GlStateManager.disableLighting();
|
||||
|
||||
if(this.currentValue instanceof String){
|
||||
|
||||
this.drawString(this.fontRenderer, I18n.format("config." + Wizardry.MODID + ".spell_hud_skin.preview"), 170, 44, 0xffffff);
|
||||
|
||||
int previewLeft = 170;
|
||||
int previewRight = width-10;
|
||||
int previewTop = 60;
|
||||
int previewBottom = height-43;
|
||||
|
||||
this.drawGradientRect(previewLeft, previewTop, previewRight, previewBottom, 0x88000000, 0x88000000);
|
||||
|
||||
int previewBorder = 10;
|
||||
|
||||
Skin skin = GuiSpellDisplay.getSkin((String)this.currentValue);
|
||||
|
||||
float scale = Math.min((previewRight - previewLeft - 2*previewBorder)/(float)skin.getWidth(),
|
||||
(previewBottom - previewTop - 2*previewBorder)/(float)skin.getHeight());
|
||||
|
||||
float x = (previewLeft + previewRight)/2 - (skin.getWidth()*scale)/2;
|
||||
float y = (previewBottom + previewTop)/2 + (skin.getHeight()*scale)/2;
|
||||
|
||||
GlStateManager.pushMatrix();
|
||||
|
||||
GlStateManager.scale(scale, scale, scale);
|
||||
|
||||
skin.drawBackground((int)(x/scale), (int)(y/scale), false, false,
|
||||
Spells.magic_missile.getIcon(), 0.6f, false);
|
||||
|
||||
skin.drawText((int)(x/scale), (int)(y/scale), false, false,
|
||||
Spells.none.getDisplayNameWithFormatting(),
|
||||
Spells.magic_missile.getDisplayNameWithFormatting(),
|
||||
Spells.none.getDisplayNameWithFormatting(), 0);
|
||||
|
||||
GlStateManager.popMatrix();
|
||||
|
||||
Skin hovered = getHoveredSkin(mouseX, mouseY);
|
||||
|
||||
if(hovered != null){
|
||||
this.drawToolTip(Lists.newArrayList("\u00A7a" + hovered.getName(), "\u00A7e" + hovered.getDescription()),
|
||||
mouseX, mouseY);
|
||||
}
|
||||
}
|
||||
|
||||
GlStateManager.enableLighting();
|
||||
}
|
||||
|
||||
/** Returns the skin corresponding to the list entry being hovered over, or null if there is none. */
|
||||
@Nullable
|
||||
private Skin getHoveredSkin(int mouseX, int mouseY){
|
||||
|
||||
int index = this.entryList.getSlotIndexFromScreenCoords(mouseX, mouseY);
|
||||
|
||||
if(index >= 0 && index <= this.entryList.listEntries.size() && mouseY <= this.entryList.bottom){
|
||||
|
||||
Object object = entryList.getListEntry(index).getValue();
|
||||
|
||||
if(object instanceof String){
|
||||
return GuiSpellDisplay.getSkin((String)object);
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override // Stops the world being visible behind the GUI when configuring from within a world
|
||||
public void drawWorldBackground(int tint){
|
||||
this.drawBackground(tint);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,93 @@
|
||||
package electroblob.wizardry.client.gui.config;
|
||||
|
||||
import net.minecraft.client.resources.I18n;
|
||||
import net.minecraftforge.fml.client.config.GuiConfig;
|
||||
import net.minecraftforge.fml.client.config.GuiConfigEntries;
|
||||
import net.minecraftforge.fml.client.config.GuiUtils;
|
||||
import net.minecraftforge.fml.client.config.IConfigElement;
|
||||
|
||||
/**
|
||||
* Same as {@link net.minecraftforge.fml.client.config.GuiConfigEntries.BooleanEntry}, but instead of simply
|
||||
* displaying 'true' or 'false', allows the two display strings to be specified in the lang file.
|
||||
*/
|
||||
// BooleanEntry's constructors are private, so I had to copy the whole goddamn class to change one method. Thanks Forge.
|
||||
public class NamedBooleanEntry extends GuiConfigEntries.ButtonEntry {
|
||||
|
||||
protected final boolean beforeValue;
|
||||
protected boolean currentValue;
|
||||
|
||||
private static final String DEFAULT_KEY = "config.ebwizardry.generic";
|
||||
|
||||
public NamedBooleanEntry(GuiConfig owningScreen, GuiConfigEntries owningEntryList, IConfigElement configElement){
|
||||
super(owningScreen, owningEntryList, configElement);
|
||||
this.beforeValue = Boolean.valueOf(configElement.get().toString());
|
||||
this.currentValue = beforeValue;
|
||||
this.btnValue.enabled = enabled();
|
||||
updateValueButtonText();
|
||||
}
|
||||
|
||||
// This is the only method that's any different
|
||||
@Override
|
||||
public void updateValueButtonText(){
|
||||
|
||||
String langKey = configElement.getLanguageKey() + "." + currentValue;
|
||||
this.btnValue.displayString = I18n.format(langKey);
|
||||
// If the key is unspecified, it defaults to the generic 'Enabled'/'Disabled' keys and adds a red/green colour
|
||||
if(this.btnValue.displayString.equals(langKey)){
|
||||
this.btnValue.displayString = I18n.format(DEFAULT_KEY + "." + currentValue);
|
||||
btnValue.packedFGColour = currentValue ? GuiUtils.getColorCode('a', true) : GuiUtils.getColorCode('c', true);
|
||||
}
|
||||
}
|
||||
|
||||
// Everything from here down is the same as BooleanEntry
|
||||
|
||||
@Override
|
||||
public void valueButtonPressed(int slotIndex){
|
||||
if(enabled()) currentValue = !currentValue;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isDefault(){
|
||||
return currentValue == Boolean.valueOf(configElement.getDefault().toString());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setToDefault(){
|
||||
if(enabled()){
|
||||
currentValue = Boolean.valueOf(configElement.getDefault().toString());
|
||||
updateValueButtonText();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isChanged(){
|
||||
return currentValue != beforeValue;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void undoChanges(){
|
||||
if(enabled()){
|
||||
currentValue = beforeValue;
|
||||
updateValueButtonText();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean saveConfigElement(){
|
||||
if(enabled() && isChanged()){
|
||||
configElement.set(currentValue);
|
||||
return configElement.requiresMcRestart();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean getCurrentValue(){
|
||||
return currentValue;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean[] getCurrentValues(){
|
||||
return new Boolean[]{getCurrentValue()};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
package electroblob.wizardry.client.gui.config;
|
||||
|
||||
import electroblob.wizardry.client.gui.GuiSpellDisplay;
|
||||
import net.minecraftforge.client.gui.ForgeGuiFactory.ForgeConfigGui.ModIDEntry;
|
||||
import net.minecraftforge.fml.client.config.GuiConfig;
|
||||
import net.minecraftforge.fml.client.config.GuiConfigEntries;
|
||||
import net.minecraftforge.fml.client.config.GuiConfigEntries.SelectValueEntry;
|
||||
import net.minecraftforge.fml.client.config.IConfigElement;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* Custom config GUI for spell HUD skin selection; displays a list of all the loaded skins and a preview of the currently
|
||||
* selected skin. based off of {@link ModIDEntry} from Forge.
|
||||
*/
|
||||
public class SpellHUDSkinChooserEntry extends SelectValueEntry {
|
||||
|
||||
public SpellHUDSkinChooserEntry(GuiConfig owningScreen, GuiConfigEntries owningEntryList, IConfigElement prop){
|
||||
super(owningScreen, owningEntryList, prop, getSelectableValues());
|
||||
if(this.selectableValues.size() == 0) this.btnValue.enabled = false;
|
||||
}
|
||||
|
||||
private static Map<Object, String> getSelectableValues(){
|
||||
return GuiSpellDisplay.getSkins().entrySet().stream().collect(Collectors.toMap(Entry::getKey,
|
||||
e -> e.getValue().getName()));
|
||||
}
|
||||
|
||||
@Override // Copied from superclass to use custom child screen GUI class
|
||||
public void valueButtonPressed(int slotIndex){
|
||||
mc.displayGuiScreen(new GuiSelectHUDSkin(this.owningScreen, configElement, slotIndex, selectableValues, currentValue, enabled()));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,194 @@
|
||||
package electroblob.wizardry.client.gui.handbook;
|
||||
|
||||
import com.google.gson.JsonObject;
|
||||
import com.google.gson.JsonSyntaxException;
|
||||
import electroblob.wizardry.client.DrawingUtils;
|
||||
import electroblob.wizardry.util.WizardryUtilities;
|
||||
import net.minecraft.client.gui.FontRenderer;
|
||||
import net.minecraft.client.gui.GuiButton;
|
||||
import net.minecraft.util.JsonUtils;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Instances of this class represent tables of contents in the wizard's handbook. Each {@link Section} can have a
|
||||
* single table of contents, which can reference any other sections in the handbook (though it is normal to list
|
||||
* top-level sections in a main contents and have subsections listed in their respective parent sections' contents).
|
||||
*
|
||||
* This class handles JSON parsing, formatting and drawing of the contents itself, working on a line-by-line basis
|
||||
* (as opposed to sections, which work on a page-by-page basis). It also stores its own list of buttons.
|
||||
*
|
||||
* @author Electroblob
|
||||
* @since Wizardry 4.2
|
||||
*/
|
||||
class Contents {
|
||||
|
||||
// Final fields are mandatory, the rest are optional
|
||||
final String id;
|
||||
final Section section;
|
||||
private boolean hyperlinks = true;
|
||||
private boolean pageNumbers = true;
|
||||
private String separator = ".";
|
||||
// Derived fields, not specifically defined in JSON
|
||||
private int startPage;
|
||||
private int startLine;
|
||||
private final List<List<GuiButton>> buttons;
|
||||
|
||||
private final List<Section> entries;
|
||||
|
||||
private List<Section> visibleEntries;
|
||||
|
||||
private Contents(String id, Section section){
|
||||
this.id = id;
|
||||
this.section = section;
|
||||
this.entries = new ArrayList<>();
|
||||
this.buttons = new ArrayList<>();
|
||||
this.visibleEntries = new ArrayList<>();
|
||||
}
|
||||
|
||||
/** Returns an unmodifiable, flattened collection of all the buttons in this contents. */
|
||||
Collection<GuiButton> getButtons(){
|
||||
return WizardryUtilities.flatten(buttons);
|
||||
}
|
||||
|
||||
void addEntry(Section section){
|
||||
entries.add(section);
|
||||
}
|
||||
|
||||
/**
|
||||
* Draws this contents for the given double-page spread and shows/hides buttons accordingly. Will draw nothing
|
||||
* if the given page is outside of this contents.
|
||||
*
|
||||
* @param font The font renderer object.
|
||||
* @param doublePage The index of the <b>double-page</b> to be drawn.
|
||||
* @param left The x coordinate of the left side of the GUI.
|
||||
* @param top The y coordinate of the top of the GUI.
|
||||
*/
|
||||
void draw(FontRenderer font, int doublePage, int left, int top){
|
||||
|
||||
// Show/hide buttons
|
||||
|
||||
int i = 0;
|
||||
|
||||
for(List<GuiButton> list : buttons){
|
||||
final int i1 = i++;
|
||||
list.forEach(b -> b.visible = GuiWizardHandbook.singleToDoublePage(startPage + i1) == doublePage);
|
||||
}
|
||||
|
||||
if(!pageNumbers) return; // No page numbers means only the buttons are drawn
|
||||
|
||||
// FONT_HEIGHT may change between fonts, so this is calculated here. With the default font it's 14.
|
||||
final int maxLineNumber = GuiWizardHandbook.PAGE_HEIGHT / font.FONT_HEIGHT;
|
||||
|
||||
int leftIndex = GuiWizardHandbook.doubleToSinglePage(doublePage, false);
|
||||
// Relative indices of the pages to be rendered - often these will be outside the section entirely
|
||||
int[] visiblePages = {leftIndex - startPage, leftIndex - startPage + 1};
|
||||
|
||||
for(int page : visiblePages){
|
||||
|
||||
if(page >= 0 && page < visibleEntries.size() / maxLineNumber + 1){
|
||||
|
||||
int x = left + (GuiWizardHandbook.isRightPage(startPage + page) ? GuiWizardHandbook.GUI_WIDTH - GuiWizardHandbook.TEXT_INSET_X - GuiWizardHandbook.PAGE_WIDTH : GuiWizardHandbook.TEXT_INSET_X);
|
||||
int y = top + GuiWizardHandbook.TEXT_INSET_Y + startLine * font.FONT_HEIGHT;
|
||||
|
||||
for(Section entry : this.visibleEntries){
|
||||
|
||||
if(entry.isUnlocked()){
|
||||
|
||||
int nameWidth = font.getStringWidth(entry.title);
|
||||
|
||||
String dotsAndNumber = " " + entry.startPage;
|
||||
|
||||
while(font.getStringWidth(dotsAndNumber) < GuiWizardHandbook.PAGE_WIDTH - nameWidth - 2){
|
||||
dotsAndNumber = separator + dotsAndNumber;
|
||||
}
|
||||
|
||||
font.drawString(dotsAndNumber, x + GuiWizardHandbook.PAGE_WIDTH - font.getStringWidth(dotsAndNumber), y, DrawingUtils.BLACK, false);
|
||||
|
||||
if(!hyperlinks) font.drawString(entry.title, x, y, DrawingUtils.BLACK, false);
|
||||
|
||||
y += font.FONT_HEIGHT;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called on GUI load to format the section and all subsections, contents tables and other elements. Does not
|
||||
* perform any actual drawing.
|
||||
*
|
||||
* @param font The font renderer object, for measurement purposes.
|
||||
* @param startPage The index of the first page (single side, not double-page) of this section.
|
||||
* @param startLine The index of the first line of this contents.
|
||||
* @param left The x coordinate of the left side of the GUI.
|
||||
* @param top The y coordinate of the top of the GUI.
|
||||
* @return The number of lines this contents takes up.
|
||||
* @throws JsonSyntaxException if at any point the formatting is found to be invalid.
|
||||
*/
|
||||
int format(FontRenderer font, int startPage, int startLine, int left, int top){
|
||||
|
||||
this.buttons.clear();
|
||||
|
||||
this.visibleEntries = new ArrayList<>(entries); // Need to copy the collection first!
|
||||
|
||||
this.visibleEntries.removeIf(s -> !s.isUnlocked());
|
||||
|
||||
if(hyperlinks){
|
||||
|
||||
// FONT_HEIGHT may change between fonts, so this is calculated here. With the default font it's 14.
|
||||
final int maxLineNumber = GuiWizardHandbook.PAGE_HEIGHT / font.FONT_HEIGHT;
|
||||
|
||||
this.startPage = startPage;
|
||||
this.startLine = startLine;
|
||||
|
||||
List<GuiButton> list = new ArrayList<>(maxLineNumber);
|
||||
|
||||
for(Section entry : this.visibleEntries){
|
||||
|
||||
int x = GuiWizardHandbook.isRightPage(startPage) ? left + GuiWizardHandbook.GUI_WIDTH - GuiWizardHandbook.TEXT_INSET_X - GuiWizardHandbook.PAGE_WIDTH : left + GuiWizardHandbook.TEXT_INSET_X;
|
||||
int y = top + GuiWizardHandbook.TEXT_INSET_Y + startLine * font.FONT_HEIGHT;
|
||||
|
||||
list.add(new GuiButtonHyperlink.Internal(0, x, y, font, entry.title, entry, 0, "", maxLineNumber-startLine, GuiWizardHandbook.isRightPage(startPage)));
|
||||
|
||||
startLine++;
|
||||
|
||||
if(startLine == maxLineNumber){
|
||||
startLine = 0;
|
||||
startPage++;
|
||||
buttons.add(list);
|
||||
list = new ArrayList<>(maxLineNumber); // If there are no more entries this will be discarded anyway
|
||||
}
|
||||
}
|
||||
|
||||
buttons.add(list);
|
||||
}
|
||||
|
||||
// Returning this is kind of trivial at the moment but if we ever wanted to add a header or something,
|
||||
// it would be more useful.
|
||||
return visibleEntries.size();
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses the given JSON object and constructs a new {@code Contents} from it, setting all the relevant fields
|
||||
* and references.
|
||||
*
|
||||
* @param parent The parent section for this contents.
|
||||
* @param json A JSON object representing the contents to be constructed. This must contain at least an "id"
|
||||
* string.
|
||||
* @return The resulting {@code Contents} object.
|
||||
* @throws JsonSyntaxException if at any point the JSON object is found to be invalid.
|
||||
*/
|
||||
static Contents fromJson(Section parent, JsonObject json){
|
||||
|
||||
Contents contents = new Contents(JsonUtils.getString(json, "id"), parent);
|
||||
|
||||
contents.hyperlinks = JsonUtils.getBoolean(json, "hyperlinks", true);
|
||||
contents.pageNumbers = JsonUtils.getBoolean(json, "page_numbers", true);
|
||||
contents.separator = JsonUtils.getString(json, "separator", ".");
|
||||
|
||||
return contents;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,226 @@
|
||||
package electroblob.wizardry.client.gui.handbook;
|
||||
|
||||
import com.google.common.collect.Streams;
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.google.gson.JsonSyntaxException;
|
||||
import electroblob.wizardry.client.DrawingUtils;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.gui.FontRenderer;
|
||||
import net.minecraft.client.renderer.GlStateManager;
|
||||
import net.minecraft.client.renderer.RenderHelper;
|
||||
import net.minecraft.client.renderer.RenderItem;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.item.crafting.CraftingManager;
|
||||
import net.minecraft.item.crafting.IRecipe;
|
||||
import net.minecraft.item.crafting.Ingredient;
|
||||
import net.minecraft.util.JsonUtils;
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
class CraftingRecipe {
|
||||
|
||||
static final int BORDER = 7;
|
||||
static final int TEXTURE_INSET_X = 40, TEXTURE_INSET_Y = 190;
|
||||
static final int WIDTH = 121, HEIGHT = 66;
|
||||
|
||||
// Final fields are mandatory, the rest are optional
|
||||
private final ResourceLocation[] locations;
|
||||
// Derived fields, not specifically defined in JSON
|
||||
private List<IRecipe> recipes;
|
||||
private final Set<int[]> instances = new HashSet<>();
|
||||
|
||||
private CraftingRecipe(ResourceLocation[] locations){
|
||||
this.locations = locations;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds an instance of this recipe to the list.
|
||||
*
|
||||
* @param page The index of the <b>single</b> page this image is on.
|
||||
* @param x The x-coordinate of the top-left corner of the image, <i>relative</i> to the top-left corner of the GUI.
|
||||
* @param y The y-coordinate of the top-left corner of the image, <i>relative</i> to the top-left corner of the GUI.
|
||||
*/
|
||||
void addInstance(int page, int x, int y){
|
||||
instances.add(new int[]{page, x, y});
|
||||
}
|
||||
|
||||
/** Removes all instances of this recipe from the list. */
|
||||
void clearInstances(){
|
||||
instances.clear();
|
||||
}
|
||||
|
||||
/** Called on GUI open to load the actual recipe object from the registry. This cannot be done on JSON load since
|
||||
* the recipes aren't necessarily loaded at that point. */
|
||||
void load(){
|
||||
|
||||
recipes = new ArrayList<>(locations.length);
|
||||
|
||||
for(ResourceLocation location : locations){
|
||||
|
||||
IRecipe recipe = CraftingManager.getRecipe(location);
|
||||
if(recipe == null) throw new JsonSyntaxException("No such recipe: " + location);
|
||||
recipes.add(recipe);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Draws all instances of this recipe that are located on the given double-page spread.
|
||||
*
|
||||
* @param font The font renderer object.
|
||||
* @param itemRenderer The item renderer object.
|
||||
* @param doublePage The double-page index of the page to be drawn.
|
||||
* @param left The x coordinate of the left side of the GUI.
|
||||
* @param top The y coordinate of the top of the GUI.
|
||||
*/
|
||||
void draw(FontRenderer font, RenderItem itemRenderer, int doublePage, int left, int top){
|
||||
|
||||
int index = (int)(Minecraft.getSystemTime() % Integer.MAX_VALUE)/2000;
|
||||
|
||||
for(int[] instance : instances){
|
||||
if(GuiWizardHandbook.singleToDoublePage(instance[0]) == doublePage){
|
||||
renderCraftingRecipe(font, itemRenderer, left + instance[1], top + instance[2], recipes.get(index % recipes.size()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Draws the tooltips for all instances of this recipe that are located on the given double-page spread. This has to
|
||||
* be done separately so that the tooltips are on top of everything else.
|
||||
*
|
||||
* @param itemRenderer The item renderer object.
|
||||
* @param doublePage The double-page index of the page to be drawn.
|
||||
* @param left The x coordinate of the left side of the GUI.
|
||||
* @param top The y coordinate of the top of the GUI.
|
||||
*/
|
||||
void drawTooltips(GuiWizardHandbook gui, FontRenderer font, RenderItem itemRenderer, int doublePage, int left, int top, int mouseX, int mouseY){
|
||||
|
||||
int index = (int)(Minecraft.getSystemTime() % Integer.MAX_VALUE)/2000;
|
||||
|
||||
for(int[] instance : instances){
|
||||
if(GuiWizardHandbook.singleToDoublePage(instance[0]) == doublePage){
|
||||
renderCraftingTooltips(gui, itemRenderer, left + instance[1], top + instance[2], mouseX, mouseY, recipes.get(index % recipes.size()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses the given JSON object and constructs a new {@code Image} from it, setting all the relevant fields
|
||||
* and references.
|
||||
*
|
||||
* @param json A JSON object representing the image to be constructed. This must contain at least a "locations"
|
||||
* string.
|
||||
* @return The resulting {@code Image} object.
|
||||
* @throws JsonSyntaxException if at any point the JSON object is found to be invalid.
|
||||
*/
|
||||
static CraftingRecipe fromJson(JsonObject json){
|
||||
|
||||
ResourceLocation[] locations = Streams.stream(JsonUtils.getJsonArray(json, "locations"))
|
||||
.map(je -> new ResourceLocation(je.getAsString())).toArray(ResourceLocation[]::new);
|
||||
return new CraftingRecipe(locations);
|
||||
}
|
||||
|
||||
static void populate(Map<String, CraftingRecipe> map, JsonObject json){
|
||||
|
||||
JsonObject sectionsObject = JsonUtils.getJsonObject(json, "recipes");
|
||||
|
||||
// Need to iterate over these since we don't know what they're called or how many there are
|
||||
for(Map.Entry<String, JsonElement> entry : sectionsObject.entrySet()){
|
||||
|
||||
String key = entry.getKey(); // Find out what each element is called, this will be the sections map key
|
||||
|
||||
CraftingRecipe recipe = fromJson(entry.getValue().getAsJsonObject());
|
||||
map.put(key, recipe);
|
||||
}
|
||||
}
|
||||
|
||||
private static void renderCraftingRecipe(FontRenderer font, RenderItem itemRenderer, int x, int y, IRecipe recipe){
|
||||
|
||||
ItemStack result = recipe.getRecipeOutput();
|
||||
|
||||
GlStateManager.color(1, 1, 1, 1);
|
||||
Minecraft.getMinecraft().renderEngine.bindTexture(GuiWizardHandbook.texture);
|
||||
|
||||
DrawingUtils.drawTexturedRect(x, y, TEXTURE_INSET_X, TEXTURE_INSET_Y, WIDTH, HEIGHT, GuiWizardHandbook.TEXTURE_WIDTH, GuiWizardHandbook.TEXTURE_HEIGHT);
|
||||
|
||||
GlStateManager.pushMatrix();
|
||||
RenderHelper.enableGUIStandardItemLighting();
|
||||
GlStateManager.disableLighting();
|
||||
GlStateManager.enableRescaleNormal();
|
||||
GlStateManager.enableColorMaterial();
|
||||
itemRenderer.zLevel = 100.0F;
|
||||
|
||||
int index = (int)(Minecraft.getSystemTime() % Integer.MAX_VALUE)/2000;
|
||||
|
||||
int i = 0;
|
||||
|
||||
for(Ingredient ingredient : recipe.getIngredients()){
|
||||
|
||||
if(ingredient != Ingredient.EMPTY){
|
||||
ItemStack stack = ingredient.getMatchingStacks()[index % ingredient.getMatchingStacks().length];
|
||||
if(!stack.isEmpty()){
|
||||
itemRenderer.renderItemAndEffectIntoGUI(stack, x + BORDER + 18 * (i%3), y + BORDER + 18 * (i/3));
|
||||
itemRenderer.renderItemOverlays(font, stack, x + BORDER + 18 * (i%3), y + BORDER + 18 * (i/3));
|
||||
}
|
||||
}
|
||||
|
||||
i++;
|
||||
}
|
||||
|
||||
if(!result.isEmpty()){
|
||||
itemRenderer.renderItemAndEffectIntoGUI(result, x + BORDER + 86, y + BORDER + 18);
|
||||
itemRenderer.renderItemOverlays(font, result, x + BORDER + 86, y + BORDER + 18);
|
||||
}
|
||||
|
||||
GlStateManager.popMatrix();
|
||||
GlStateManager.enableDepth();
|
||||
GlStateManager.disableColorMaterial();
|
||||
itemRenderer.zLevel = 0.0F;
|
||||
RenderHelper.disableStandardItemLighting();
|
||||
|
||||
}
|
||||
|
||||
private static void renderCraftingTooltips(GuiWizardHandbook gui, RenderItem itemRenderer, int x, int y, int mouseX, int mouseY, IRecipe recipe){
|
||||
|
||||
ItemStack result = recipe.getRecipeOutput();
|
||||
|
||||
GlStateManager.pushMatrix();
|
||||
RenderHelper.enableGUIStandardItemLighting();
|
||||
GlStateManager.disableLighting();
|
||||
GlStateManager.enableRescaleNormal();
|
||||
GlStateManager.enableColorMaterial();
|
||||
itemRenderer.zLevel = 0.0F;
|
||||
|
||||
int index = (int)(Minecraft.getSystemTime() % Integer.MAX_VALUE)/2000;
|
||||
|
||||
int i = 0;
|
||||
|
||||
for(Ingredient ingredient : recipe.getIngredients()){
|
||||
|
||||
if(ingredient != Ingredient.EMPTY){
|
||||
ItemStack stack = ingredient.getMatchingStacks()[index % ingredient.getMatchingStacks().length];
|
||||
if(!stack.isEmpty() && isPointInRegion(x + BORDER + 18 * (i%3), y + BORDER + 18 * (i/3), 16, 16, mouseX, mouseY)){
|
||||
gui.renderToolTip(stack, mouseX, mouseY);
|
||||
}
|
||||
}
|
||||
|
||||
i++;
|
||||
}
|
||||
|
||||
if(!result.isEmpty() && isPointInRegion(x + BORDER + 86, y + BORDER + 18, 16, 16, mouseX, mouseY)){
|
||||
gui.renderToolTip(result, mouseX, mouseY);
|
||||
}
|
||||
|
||||
GlStateManager.popMatrix();
|
||||
GlStateManager.enableDepth();
|
||||
GlStateManager.disableColorMaterial();
|
||||
RenderHelper.disableStandardItemLighting();
|
||||
|
||||
}
|
||||
|
||||
private static boolean isPointInRegion(int left, int top, int width, int height, int mouseX, int mouseY){
|
||||
return mouseX >= left - 1 && mouseX < left + width + 1 && mouseY >= top - 1 && mouseY < top + height + 1;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,227 @@
|
||||
package electroblob.wizardry.client.gui.handbook;
|
||||
|
||||
import com.google.gson.JsonSyntaxException;
|
||||
import electroblob.wizardry.client.DrawingUtils;
|
||||
import electroblob.wizardry.registry.WizardrySounds;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.audio.PositionedSoundRecord;
|
||||
import net.minecraft.client.audio.SoundHandler;
|
||||
import net.minecraft.client.gui.FontRenderer;
|
||||
import net.minecraft.client.gui.GuiButton;
|
||||
import net.minecraft.util.math.MathHelper;
|
||||
import net.minecraft.util.text.ITextComponent;
|
||||
import net.minecraft.util.text.TextComponentString;
|
||||
import net.minecraft.util.text.TextFormatting;
|
||||
import net.minecraft.util.text.event.ClickEvent;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public abstract class GuiButtonHyperlink extends GuiButton {
|
||||
|
||||
public static final String URL_REGEX = "^((https?|ftp)://|(www|ftp)\\.)?[a-z0-9-]+(\\.[a-z0-9-]+)+([/?].*)?$";
|
||||
|
||||
/** Pulse period of links to new sections, in milliseconds. */
|
||||
private static final float PULSATION_PERIOD = 1500;
|
||||
|
||||
final int indent;
|
||||
final List<String> lines;
|
||||
final int linesLeft;
|
||||
|
||||
GuiButtonHyperlink(int id, int x, int y, FontRenderer font, String text, int indent, String suffix, int linesLeft, boolean rightPage){
|
||||
|
||||
super(id, x, y, font.getStringWidth(text), font.FONT_HEIGHT, text);
|
||||
|
||||
// Sometimes a link has punctuation or something after it that causes it to wrap onto a new line
|
||||
String linkWithSuffix = text + suffix;
|
||||
|
||||
// If the string won't fit any words at the end of the current line, treat it as if we started a new line
|
||||
if(font.getStringWidth(linkWithSuffix.split("\\s")[0]) > GuiWizardHandbook.PAGE_WIDTH - indent){
|
||||
indent = 0;
|
||||
this.y += font.FONT_HEIGHT;
|
||||
}
|
||||
|
||||
this.indent = indent; // Assigned here in case it was corrected above
|
||||
this.linesLeft = linesLeft;
|
||||
|
||||
String line1 = font.listFormattedStringToWidth(linkWithSuffix, GuiWizardHandbook.PAGE_WIDTH - indent).get(0);
|
||||
// Without trim(), there will be at least 1 leading space due to the custom wrapping
|
||||
String remainder = linkWithSuffix.substring(line1.length()).trim();
|
||||
|
||||
// ... then wrap the rest to the normal width.
|
||||
lines = new ArrayList<>();
|
||||
lines.add(line1);
|
||||
// Some links are only one line, if this wasn't checked they would cause a StackOverflowError
|
||||
if(!remainder.isEmpty()) lines.addAll(font.listFormattedStringToWidth(remainder, GuiWizardHandbook.PAGE_WIDTH));
|
||||
|
||||
// Removes the suffix if it exists (ugly as heck, but it works)
|
||||
if(!suffix.isEmpty()){
|
||||
for(int i=lines.size()-1; i>=0; i--){
|
||||
String line = lines.get(i);
|
||||
if(suffix.endsWith(line)){
|
||||
lines.remove(i);
|
||||
}else if(line.endsWith(suffix)){
|
||||
lines.set(i, line.substring(0, line.length() - suffix.length()));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Remove any lines that overflowed onto the next double-page
|
||||
if(rightPage){
|
||||
while(lines.size() > linesLeft) lines.remove(lines.size() - 1);
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isHovered(net.minecraft.client.gui.FontRenderer font, int mouseX, int mouseY){
|
||||
|
||||
int i = 0;
|
||||
|
||||
for(String line : lines){
|
||||
|
||||
int l = x;
|
||||
if(i == 0) l += indent;
|
||||
|
||||
int t = y + font.FONT_HEIGHT * i;
|
||||
|
||||
if(i > linesLeft){
|
||||
l = l + GuiWizardHandbook.GUI_WIDTH - 2 * GuiWizardHandbook.TEXT_INSET_X - GuiWizardHandbook.PAGE_WIDTH;
|
||||
t -= GuiWizardHandbook.PAGE_HEIGHT - (GuiWizardHandbook.PAGE_HEIGHT % font.FONT_HEIGHT);
|
||||
}
|
||||
|
||||
if(mouseX >= l && mouseY >= t && mouseX < l + font.getStringWidth(line) && mouseY < t + font.FONT_HEIGHT){
|
||||
return true;
|
||||
}
|
||||
|
||||
i++;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean mousePressed(Minecraft minecraft, int mouseX, int mouseY){
|
||||
return this.enabled && this.visible && isHovered(minecraft.fontRenderer, mouseX, mouseY);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void drawButton(Minecraft minecraft, int mouseX, int mouseY, float partialTicks){
|
||||
|
||||
if(this.visible){
|
||||
|
||||
this.hovered = isHovered(minecraft.fontRenderer, mouseX, mouseY);
|
||||
|
||||
int i = 0;
|
||||
|
||||
for(String line : lines){
|
||||
|
||||
int l = x;
|
||||
if(i == 0) l += indent;
|
||||
|
||||
int t = y + minecraft.fontRenderer.FONT_HEIGHT * i;
|
||||
|
||||
if(i > linesLeft){
|
||||
l = l + GuiWizardHandbook.GUI_WIDTH - 2 * GuiWizardHandbook.TEXT_INSET_X - GuiWizardHandbook.PAGE_WIDTH;
|
||||
t -= GuiWizardHandbook.PAGE_HEIGHT - (GuiWizardHandbook.PAGE_HEIGHT % minecraft.fontRenderer.FONT_HEIGHT);
|
||||
}
|
||||
|
||||
minecraft.fontRenderer.drawString(line, l, t, getColour());
|
||||
|
||||
i++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected int getColour(){
|
||||
return hovered ? GuiWizardHandbook.colours.get("highlight") : GuiWizardHandbook.colours.get("hyperlink");
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new hyperlink button from the given arguments, automatically differentiating between URLs and sections.
|
||||
* @param x The x position of the button
|
||||
* @param y The y position of the button
|
||||
* @param font A reference to the FontRenderer object
|
||||
* @param upToLink The paragraph (as a list of lines) up to the link, used to determine positioning and word wrap
|
||||
* @param arguments The link arguments - that is, everything between the two @ signs, split by spaces
|
||||
* @param suffix The text directly after the link, up to the first whitespace; used for word wrap. Usually this is
|
||||
* either empty or contains a single punctuation mark.
|
||||
* @return The resulting button
|
||||
* @throws IllegalArgumentException if the given argument array is empty or contains more than 2 arguments
|
||||
* @throws JsonSyntaxException if the specified link target is not a URL or a valid section ID
|
||||
*/
|
||||
public static GuiButtonHyperlink create(int x, int y, FontRenderer font, List<String> upToLink, String[] arguments, String suffix, int linesLeft, boolean rightPage){
|
||||
|
||||
if(arguments.length == 0 || arguments.length > 2) throw new IllegalArgumentException("Incorrect array length!");
|
||||
|
||||
GuiButtonHyperlink button;
|
||||
|
||||
if(arguments[0].matches(URL_REGEX)){
|
||||
|
||||
button = new GuiButtonHyperlink.External(0, x, y, font, arguments[arguments.length - 1], arguments[0],
|
||||
font.getStringWidth(upToLink.get(upToLink.size() - 1)), suffix, linesLeft, rightPage);
|
||||
|
||||
}else{
|
||||
|
||||
Section target = GuiWizardHandbook.sections.get(arguments[0]);
|
||||
|
||||
if(target == null) throw new JsonSyntaxException("Hyperlink points to nonexistent section id " + arguments[0]);
|
||||
|
||||
button = new GuiButtonHyperlink.Internal(0, x, y, font, arguments[arguments.length - 1],
|
||||
target, font.getStringWidth(upToLink.get(upToLink.size() - 1)), suffix, linesLeft, rightPage);
|
||||
}
|
||||
|
||||
return button;
|
||||
}
|
||||
|
||||
static class Internal extends GuiButtonHyperlink {
|
||||
|
||||
final Section target;
|
||||
|
||||
Internal(int id, int x, int y, FontRenderer font, String text, Section target, int indent, String suffix, int linesLeft, boolean rightPage){
|
||||
super(id, x, y, font, text, indent, suffix, linesLeft, rightPage);
|
||||
this.target = target;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean mousePressed(Minecraft minecraft, int mouseX, int mouseY){
|
||||
if(!target.isUnlocked()) return false;
|
||||
return super.mousePressed(minecraft, mouseX, mouseY);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void playPressSound(SoundHandler soundHandler){
|
||||
soundHandler.playSound(PositionedSoundRecord.getMasterRecord(WizardrySounds.MISC_PAGE_TURN, 1));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int getColour(){
|
||||
|
||||
if(!target.isUnlocked()) return GuiWizardHandbook.colours.get("text");
|
||||
|
||||
if(!hovered && target.isNew() && !Minecraft.getMinecraft().player.isCreative()){
|
||||
|
||||
int c = GuiWizardHandbook.colours.get("new_section");
|
||||
int d = GuiWizardHandbook.colours.get("hyperlink");
|
||||
float f = (MathHelper.sin((Minecraft.getSystemTime() % PULSATION_PERIOD) / PULSATION_PERIOD * 2 * (float)Math.PI) + 1) / 2f;
|
||||
|
||||
return DrawingUtils.mix(c, d, f);
|
||||
}
|
||||
|
||||
return super.getColour();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static class External extends GuiButtonHyperlink {
|
||||
|
||||
final ITextComponent link;
|
||||
|
||||
External(int id, int x, int y, FontRenderer font, String text, String url, int indent, String suffix, int linesLeft, boolean rightPage){
|
||||
super(id, x, y, font, text, indent, suffix, linesLeft, rightPage);
|
||||
this.link = new TextComponentString(text);
|
||||
link.getStyle().setClickEvent(new ClickEvent(ClickEvent.Action.OPEN_URL, url)).setColor(TextFormatting.DARK_BLUE);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
package electroblob.wizardry.client.gui.handbook;
|
||||
|
||||
import electroblob.wizardry.Wizardry;
|
||||
import electroblob.wizardry.client.DrawingUtils;
|
||||
import electroblob.wizardry.registry.WizardrySounds;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.audio.PositionedSoundRecord;
|
||||
import net.minecraft.client.audio.SoundHandler;
|
||||
import net.minecraft.client.gui.GuiButton;
|
||||
import net.minecraft.client.renderer.GlStateManager;
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
|
||||
//@SideOnly(Side.CLIENT)
|
||||
class GuiButtonTurnPage extends GuiButton {
|
||||
|
||||
static final int WIDTH = 20;
|
||||
static final int HEIGHT = 12;
|
||||
|
||||
enum Type {
|
||||
|
||||
NEXT_PAGE(0, 196),
|
||||
PREVIOUS_PAGE(0, 208),
|
||||
NEXT_SECTION(0, 220),
|
||||
PREVIOUS_SECTION(0, 232),
|
||||
CONTENTS(0, 244);
|
||||
|
||||
private final int u, v;
|
||||
|
||||
Type(int u, int v){
|
||||
this.u = u;
|
||||
this.v = v;
|
||||
}
|
||||
}
|
||||
|
||||
public final Type type;
|
||||
|
||||
private static final ResourceLocation texture = new ResourceLocation(Wizardry.MODID, "textures/gui/handbook.png");
|
||||
|
||||
public GuiButtonTurnPage(int id, int x, int y, Type type){
|
||||
super(id, x, y, WIDTH, HEIGHT, "");
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void playPressSound(SoundHandler soundHandler){
|
||||
soundHandler.playSound(PositionedSoundRecord.getMasterRecord(WizardrySounds.MISC_PAGE_TURN, 1));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void drawButton(Minecraft minecraft, int mouseX, int mouseY, float partialTicks){
|
||||
|
||||
if(this.visible){
|
||||
|
||||
boolean flag = mouseX >= this.x && mouseY >= this.y && mouseX < this.x + this.width && mouseY < this.y + this.height;
|
||||
GlStateManager.color(1.0F, 1.0F, 1.0F, 1.0F);
|
||||
minecraft.getTextureManager().bindTexture(texture);
|
||||
|
||||
DrawingUtils.drawTexturedRect(this.x, this.y, flag ? type.u + width : type.u, type.v, width, height, 512, 256);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,564 @@
|
||||
package electroblob.wizardry.client.gui.handbook;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.google.gson.JsonSyntaxException;
|
||||
import electroblob.wizardry.Wizardry;
|
||||
import electroblob.wizardry.client.ClientProxy;
|
||||
import electroblob.wizardry.client.DrawingUtils;
|
||||
import electroblob.wizardry.client.gui.GuiButtonInvisible;
|
||||
import electroblob.wizardry.client.gui.handbook.GuiButtonTurnPage.Type;
|
||||
import electroblob.wizardry.constants.Constants;
|
||||
import electroblob.wizardry.constants.Element;
|
||||
import electroblob.wizardry.constants.Tier;
|
||||
import electroblob.wizardry.packet.PacketRequestAdvancementSync;
|
||||
import electroblob.wizardry.packet.WizardryPacketHandler;
|
||||
import electroblob.wizardry.registry.WizardrySounds;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.audio.PositionedSoundRecord;
|
||||
import net.minecraft.client.audio.SoundHandler;
|
||||
import net.minecraft.client.gui.GuiButton;
|
||||
import net.minecraft.client.gui.GuiScreen;
|
||||
import net.minecraft.client.renderer.GlStateManager;
|
||||
import net.minecraft.client.resources.IResource;
|
||||
import net.minecraft.client.resources.IResourceManager;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.util.JsonUtils;
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
import org.lwjgl.input.Keyboard;
|
||||
|
||||
import java.awt.*;
|
||||
import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.util.List;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* GUI class for the wizard's handbook. Like any GUI class, this is instantiated each time the book is opened. As of
|
||||
* Wizardry 4.2, the handbook text is defined as a JSON file rather than a plain text file, and is loaded only on
|
||||
* resource pack reload, rather than every time the book is opened. This means all the data structures (sections, images,
|
||||
* etc.) are built before the GUI instance exists at all. However, since some things depend on positioning, these have to
|
||||
* be initialised on GUI creation. (Previously, everything was done on GUI load)
|
||||
*
|
||||
* @author Electroblob
|
||||
* @since Wizardry 1.0
|
||||
* @see Section
|
||||
* @see Contents
|
||||
* @see Image
|
||||
* @see CraftingRecipe
|
||||
*/
|
||||
public class GuiWizardHandbook extends GuiScreen {
|
||||
|
||||
private static final ResourceLocation DEFAULT = new ResourceLocation(Wizardry.MODID, "texts/handbook_en_us.json");
|
||||
|
||||
static final ResourceLocation texture = new ResourceLocation(Wizardry.MODID, "textures/gui/handbook.png");
|
||||
|
||||
/** Global Gson instance for the handbook. */
|
||||
private static final Gson gson = new Gson();
|
||||
|
||||
// Formatting markup
|
||||
|
||||
static final char FORMAT_MARKER = '#';
|
||||
static final char HYPERLINK_MARKER = '@';
|
||||
|
||||
static final String IMAGE_TAG = "image";
|
||||
static final String RECIPE_TAG = "recipe";
|
||||
static final String RULER_TAG = "ruler";
|
||||
|
||||
static final Map<String, String> FORMAT_TAGS = new HashMap<>();
|
||||
|
||||
// Dimension constants
|
||||
// Private constants are not relevant to book elements, package-protected ones are
|
||||
|
||||
/** The dimensions of the rendered GUI area. */
|
||||
static final int GUI_WIDTH = 288, GUI_HEIGHT = 180;
|
||||
/** The dimensions of the GUI texture itself. */
|
||||
static final int TEXTURE_WIDTH = 512, TEXTURE_HEIGHT = 256;
|
||||
/** The dimensions of the area of a single page in which text can be drawn. */
|
||||
static final int PAGE_WIDTH = 120, PAGE_HEIGHT = 140;
|
||||
/** The distance of the text from the top outside corner of each page. */
|
||||
static final int TEXT_INSET_X = 17, TEXT_INSET_Y = 16;
|
||||
/** The distance of the buttons from the bottom outside corners of the GUI. */
|
||||
private static final int BUTTON_INSET_X = 22, BUTTON_INSET_Y = 13;
|
||||
/** The distance between adjacent buttons. */
|
||||
private static final int BUTTON_SPACING = 20;
|
||||
/** The distance of the page numbers from the bottom of the GUI. */
|
||||
private static final int PAGE_NUMBER_INSET = 22;
|
||||
|
||||
// IDEA: Constant dimensions could be converted to JSON like the spell HUD ones
|
||||
|
||||
// Global variables
|
||||
|
||||
/**
|
||||
* The <b>double-page</b> currently being viewed. Each double-page spread counts as a single page, with the inside
|
||||
* of the front cover being page 0.
|
||||
*/
|
||||
private int currentPage = 0;
|
||||
/**
|
||||
* The number of <b>single</b> pages currently in the book. This is calculated on GUI load based on visible sections.
|
||||
*/
|
||||
private int pageCount = 1; // Starts at 1 because the first single-page is the inside of the cover
|
||||
/**
|
||||
* The <b>double-page</b> number where the bookmark is currently set, <b>relative to the section stored in
|
||||
* {@link GuiWizardHandbook#bookmarkSection}</b>. Static because it persists when the book is closed.
|
||||
*/
|
||||
private static int bookmarkPage = 0;
|
||||
/**
|
||||
* The key corresponding to the section in which the bookmark is currently set. Static because it persists when the
|
||||
* book is closed. Storing a section means the bookmark doesn't change location when new sections are unlocked.
|
||||
*/
|
||||
private static String bookmarkSection;
|
||||
|
||||
// Buttons
|
||||
private GuiButton bookmark, next, previous, nextSection, previousSection, menu;
|
||||
|
||||
// Handbook content
|
||||
|
||||
// As a general rule, I prefer to make static final fields lowercase if they're collections that change, because even
|
||||
// though the collection itself is constant, the stuff in it is not, so being lowercase highlights this difference.
|
||||
|
||||
/**
|
||||
* A map which stores all loaded section objects, including subsections. This gets wiped on resource pack reload and
|
||||
* repopulated with mappings as specified by the handbook JSON file for the current language. The keys in the map
|
||||
* correspond to the keys in the sections object in that file, and are sorted in that order.
|
||||
*/
|
||||
static final Map<String, Section> sections = new LinkedHashMap<>();
|
||||
|
||||
/**
|
||||
* A list which stores all loaded section objects, including subsections. This is an unmodifiable list view of the
|
||||
* values in {@link GuiWizardHandbook#sections}, sorted in the same (page number) order. This exists only to allow
|
||||
* sections to be accessed by ordinal index for the various navigation buttons, hence why it is private.
|
||||
*/
|
||||
private static List<Section> sectionList;
|
||||
|
||||
/**
|
||||
* A map which stores all loaded contents objects. This gets wiped on resource pack reload and repopulated with
|
||||
* mappings as specified by the handbook JSON file for the current language. The keys in the map correspond to the
|
||||
* id strings for the contents objects in that file. This map is not sorted.
|
||||
*/
|
||||
static final Map<String, Contents> contentsList = new HashMap<>();
|
||||
|
||||
/**
|
||||
* A map which stores all loaded hex colour values. This gets wiped on resource pack reload and repopulated with
|
||||
* mappings as specified by the handbook JSON file for the current language. The keys in the map correspond to the
|
||||
* keys in the colours object in that file. This map is not sorted.
|
||||
*/
|
||||
static final Map<String, Integer> colours = new HashMap<>();
|
||||
|
||||
/**
|
||||
* A map which stores all loaded image objects. This gets wiped on resource pack reload and repopulated with
|
||||
* mappings as specified by the handbook JSON file for the current language. The keys in the map correspond to the
|
||||
* keys in the images object in that file. This map is not sorted.
|
||||
*/
|
||||
static final Map<String, Image> images = new HashMap<>();
|
||||
|
||||
/**
|
||||
* A map which stores all loaded crafting recipe objects. This gets wiped on resource pack reload and repopulated
|
||||
* with mappings as specified by the handbook JSON file for the current language. The keys in the map correspond to
|
||||
* the keys in the recipes object in that file. This map is not sorted.
|
||||
*/
|
||||
static final Map<String, CraftingRecipe> recipes = new HashMap<>();
|
||||
|
||||
/**
|
||||
* Adds a format tag to the handbook. All occurrences of the given tag string preceded by a # will be replaced with
|
||||
* the result of the given value string on GUI load. The value string, therefore, can be anything that should be
|
||||
* input dynamically, as long as it does not change while the GUI is open. Examples include wizardry's version,
|
||||
* the various element colours and the keys assigned to wizardry's controls.
|
||||
* @param tag The tag string, as defined in the handbook JSON file, excluding the # character. Cannot include spaces.
|
||||
* @param value The string to replace occurrences of the given format tag with. Can include spaces but not the # character.
|
||||
*/
|
||||
public static void addFormatTag(String tag, String value){
|
||||
FORMAT_TAGS.put(tag, value);
|
||||
}
|
||||
|
||||
private static void initFormatTags(){
|
||||
|
||||
addFormatTag("next_spell_key", ClientProxy.NEXT_SPELL.getDisplayName());
|
||||
addFormatTag("previous_spell_key", ClientProxy.PREVIOUS_SPELL.getDisplayName());
|
||||
addFormatTag("example_charging_loss", "" + (Constants.MANA_PER_CRYSTAL - 30));
|
||||
addFormatTag("mana_per_crystal", "" + Constants.MANA_PER_CRYSTAL);
|
||||
addFormatTag("novice_max_charge", "" + Tier.NOVICE.maxCharge);
|
||||
addFormatTag("apprentice_max_charge", "" + Tier.APPRENTICE.maxCharge);
|
||||
addFormatTag("advanced_max_charge", "" + Tier.ADVANCED.maxCharge);
|
||||
addFormatTag("master_max_charge", "" + Tier.MASTER.maxCharge);
|
||||
addFormatTag("version", Wizardry.VERSION);
|
||||
addFormatTag("mcversion", Minecraft.getMinecraft().getVersion());
|
||||
|
||||
addFormatTag("colour_novice", "\u00A77");
|
||||
addFormatTag("colour_apprentice", Tier.APPRENTICE.getFormattingCode());
|
||||
addFormatTag("colour_advanced", Tier.ADVANCED.getFormattingCode());
|
||||
addFormatTag("colour_master", Tier.MASTER.getFormattingCode());
|
||||
|
||||
addFormatTag("colour_fire", Element.FIRE.getFormattingCode());
|
||||
addFormatTag("colour_ice", Element.ICE.getFormattingCode());
|
||||
addFormatTag("colour_lightning", Element.LIGHTNING.getFormattingCode());
|
||||
addFormatTag("colour_necromancy", Element.NECROMANCY.getFormattingCode());
|
||||
addFormatTag("colour_earth", Element.EARTH.getFormattingCode());
|
||||
addFormatTag("colour_sorcery", Element.SORCERY.getFormattingCode());
|
||||
addFormatTag("colour_healing", Element.HEALING.getFormattingCode());
|
||||
|
||||
addFormatTag("colour_reset", "\u00A70");
|
||||
}
|
||||
|
||||
// Helper methods
|
||||
|
||||
/**
|
||||
* Converts the given single page index to a double-page index. Inverse of
|
||||
* {@link GuiWizardHandbook#doubleToSinglePage(int, boolean)}.
|
||||
*
|
||||
* @param singlePageIndex The single-page index, which is the same as the page numbers actually displayed.
|
||||
* @return The corresponding double-page index.
|
||||
*/
|
||||
static int singleToDoublePage(int singlePageIndex){
|
||||
// Yes, this is trivial, but if I ever change the numbering it'll be useful. It's also more descriptive.
|
||||
return singlePageIndex / 2;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts the given double-page index to a single-page index. Inverse of
|
||||
* {@link GuiWizardHandbook#singleToDoublePage(int)}.
|
||||
*
|
||||
* @param doublePageIndex The double-page index, as stored in {@link GuiWizardHandbook#currentPage}.
|
||||
* @param rightHandPage True to return the page on the right (1 greater), false for the left-hand page.
|
||||
* @return The corresponding single-page index.
|
||||
*/
|
||||
static int doubleToSinglePage(int doublePageIndex, boolean rightHandPage){
|
||||
return rightHandPage ? doublePageIndex * 2 + 1 : doublePageIndex * 2;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the given page index refers to a right-hand page or a left-hand page.
|
||||
*
|
||||
* @param page The single-page index, which is the same as the page number actually displayed.
|
||||
* @return True if the given page index refers to a right-hand page, false if it is a left-hand page.
|
||||
*/
|
||||
static boolean isRightPage(int page){
|
||||
return page % 2 == 1;
|
||||
}
|
||||
|
||||
// Drawing
|
||||
|
||||
@Override
|
||||
public void drawScreen(int mouseX, int mouseY, float partialTicks){
|
||||
|
||||
int left = this.width / 2 - GUI_WIDTH / 2;
|
||||
int top = this.height / 2 - GUI_HEIGHT / 2;
|
||||
|
||||
Minecraft.getMinecraft().renderEngine.bindTexture(texture);
|
||||
|
||||
GlStateManager.color(1, 1, 1, 1);
|
||||
|
||||
// Main background
|
||||
DrawingUtils.drawTexturedRect(left, top, 0, 0, GUI_WIDTH, GUI_HEIGHT, TEXTURE_WIDTH, TEXTURE_HEIGHT);
|
||||
|
||||
// First page background
|
||||
if(currentPage == 0){
|
||||
DrawingUtils.drawTexturedRect(left, top, 368, 0, GUI_WIDTH / 2, GUI_HEIGHT, TEXTURE_WIDTH, TEXTURE_HEIGHT);
|
||||
previous.visible = false;
|
||||
previousSection.visible = false; // Not worth testing if we're in the first section every frame
|
||||
menu.visible = false;
|
||||
}else{
|
||||
previous.visible = true;
|
||||
previousSection.visible = true;
|
||||
menu.visible = true;
|
||||
}
|
||||
|
||||
// Last page background
|
||||
if(currentPage == singleToDoublePage(pageCount)){
|
||||
DrawingUtils.drawTexturedFlippedRect(left + GUI_WIDTH / 2, top, 368, 0, GUI_WIDTH / 2, GUI_HEIGHT, TEXTURE_WIDTH, TEXTURE_HEIGHT, true, false);
|
||||
next.visible = false;
|
||||
nextSection.visible = false;
|
||||
}else{
|
||||
next.visible = true;
|
||||
nextSection.visible = true;
|
||||
}
|
||||
|
||||
// Page numbers
|
||||
if(currentPage > 0){
|
||||
String pageNumber = "" + doubleToSinglePage(currentPage, false);
|
||||
this.fontRenderer.drawString(pageNumber, left + TEXT_INSET_X + PAGE_WIDTH / 2
|
||||
- fontRenderer.getStringWidth(pageNumber)/2, top + GUI_HEIGHT - PAGE_NUMBER_INSET, DrawingUtils.BLACK);
|
||||
}
|
||||
if(currentPage < singleToDoublePage(pageCount)){
|
||||
String pageNumber = "" + doubleToSinglePage(currentPage, true);
|
||||
this.fontRenderer.drawString(pageNumber, left + GUI_WIDTH - TEXT_INSET_X - PAGE_WIDTH / 2
|
||||
- fontRenderer.getStringWidth(pageNumber)/2, top + GUI_HEIGHT - PAGE_NUMBER_INSET, DrawingUtils.BLACK);
|
||||
}
|
||||
|
||||
// Main content
|
||||
contentsList.values().forEach(c -> { if(c.section.isUnlocked()) c.draw(fontRenderer, currentPage, left, top); } );
|
||||
sections.values().forEach(s -> { if(s.isUnlocked()) s.draw(fontRenderer, currentPage, left, top); } );
|
||||
// These only get populated if the sections are unlocked so no checks are necessary
|
||||
images.values().forEach(i -> i.draw(fontRenderer, currentPage, left, top));
|
||||
recipes.values().forEach(r -> r.draw(fontRenderer, itemRender, currentPage, left, top));
|
||||
|
||||
// Buttons
|
||||
super.drawScreen(mouseX, mouseY, partialTicks);
|
||||
|
||||
// Bookmark
|
||||
GlStateManager.color(1, 1, 1, 1);
|
||||
Minecraft.getMinecraft().renderEngine.bindTexture(texture);
|
||||
|
||||
if(currentPage == singleToDoublePage(sections.get(bookmarkSection).startPage) + bookmarkPage){
|
||||
// If the current page is the bookmarked page, the (invisible) bookmark button is disabled
|
||||
bookmark.visible = false;
|
||||
DrawingUtils.drawTexturedRect(left + 138, top, 299, 0, 11, 191, TEXTURE_WIDTH, TEXTURE_HEIGHT);
|
||||
}else{
|
||||
bookmark.visible = true;
|
||||
bookmark.x = left + (currentPage > singleToDoublePage(sections.get(bookmarkSection).startPage) + bookmarkPage ? 130 : 147);
|
||||
DrawingUtils.drawTexturedRect(bookmark.x, top,
|
||||
bookmark.isMouseOver() ? 310 : 288, 0, 11, 191, TEXTURE_WIDTH, TEXTURE_HEIGHT);
|
||||
}
|
||||
|
||||
// Recipe tooltips
|
||||
recipes.values().forEach(r -> r.drawTooltips(this, fontRenderer, itemRender, currentPage, left, top, mouseX, mouseY));
|
||||
|
||||
}
|
||||
|
||||
// GUI Initialisation / Close
|
||||
|
||||
@Override
|
||||
public void onResize(Minecraft minecraft, int width, int height){
|
||||
initGui();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onGuiClosed(){
|
||||
super.onGuiClosed();
|
||||
Keyboard.enableRepeatEvents(false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initGui(){
|
||||
|
||||
super.initGui();
|
||||
Keyboard.enableRepeatEvents(true);
|
||||
|
||||
initFormatTags();
|
||||
|
||||
final int left = this.width / 2 - GUI_WIDTH / 2;
|
||||
final int top = this.height / 2 - GUI_HEIGHT / 2;
|
||||
|
||||
recipes.values().forEach(CraftingRecipe::load);
|
||||
|
||||
int nextButtonId = 0;
|
||||
|
||||
this.buttonList.clear();
|
||||
|
||||
this.buttonList.add(next = new GuiButtonTurnPage(nextButtonId++, left + GUI_WIDTH - BUTTON_INSET_X - GuiButtonTurnPage.WIDTH,
|
||||
top + GUI_HEIGHT - BUTTON_INSET_Y - GuiButtonTurnPage.HEIGHT, Type.NEXT_PAGE));
|
||||
|
||||
this.buttonList.add(previous = new GuiButtonTurnPage(nextButtonId++, left + BUTTON_INSET_X,
|
||||
top + GUI_HEIGHT - BUTTON_INSET_Y - GuiButtonTurnPage.HEIGHT, Type.PREVIOUS_PAGE));
|
||||
|
||||
this.buttonList.add(nextSection = new GuiButtonTurnPage(nextButtonId++, left + GUI_WIDTH - BUTTON_INSET_X - GuiButtonTurnPage.WIDTH - BUTTON_SPACING,
|
||||
top + GUI_HEIGHT - BUTTON_INSET_Y - GuiButtonTurnPage.HEIGHT, Type.NEXT_SECTION));
|
||||
|
||||
this.buttonList.add(previousSection = new GuiButtonTurnPage(nextButtonId++, left + BUTTON_INSET_X + BUTTON_SPACING,
|
||||
top + GUI_HEIGHT - BUTTON_INSET_Y - GuiButtonTurnPage.HEIGHT, Type.PREVIOUS_SECTION));
|
||||
|
||||
this.buttonList.add(menu = new GuiButtonTurnPage(nextButtonId++, left + GUI_WIDTH/2 - 28,
|
||||
top + GUI_HEIGHT - BUTTON_INSET_Y - GuiButtonTurnPage.HEIGHT, Type.CONTENTS));
|
||||
|
||||
this.buttonList.add(bookmark = new GuiButtonInvisible(nextButtonId++, left + 130, top + 172, 11, 19) {
|
||||
@Override
|
||||
public void playPressSound(SoundHandler soundHandler){
|
||||
soundHandler.playSound(PositionedSoundRecord.getMasterRecord(WizardrySounds.MISC_PAGE_TURN, 1));
|
||||
}
|
||||
});
|
||||
|
||||
pageCount = 1;
|
||||
|
||||
// Clears instances of all images and recipes
|
||||
images.values().forEach(Image::clearInstances);
|
||||
recipes.values().forEach(CraftingRecipe::clearInstances);
|
||||
|
||||
// Formats all the unlocked sections in order
|
||||
for(Section section : sections.values()){
|
||||
if(section.isUnlocked()){
|
||||
pageCount = section.format(this.fontRenderer, pageCount, left, top);
|
||||
buttonList.addAll(section.getButtons());
|
||||
}
|
||||
}
|
||||
|
||||
contentsList.values().forEach(c -> buttonList.addAll(c.getButtons()));
|
||||
|
||||
this.mc.getSoundHandler().playSound(PositionedSoundRecord.getMasterRecord(WizardrySounds.MISC_BOOK_OPEN, 1));
|
||||
}
|
||||
|
||||
// JSON Parsing / Data Construction
|
||||
|
||||
/**
|
||||
* Called from preInit in the main mod class (via the proxies) to initialise the handbook (parses the JSON file
|
||||
* and constructs the relevant data structures), and again on each resource reload (changing the language triggers
|
||||
* a resource reload).
|
||||
*/
|
||||
public static void loadHandbookFile(IResourceManager manager){
|
||||
|
||||
IResource handbookFile = getHandbookResource(manager);
|
||||
|
||||
if(handbookFile != null){
|
||||
|
||||
// Wipes all the maps before repopulating them
|
||||
images.clear();
|
||||
sections.clear();
|
||||
contentsList.clear();
|
||||
colours.clear();
|
||||
|
||||
bookmarkSection = null; // Also need to wipe the reference to the old bookmarked section
|
||||
|
||||
BufferedReader reader = new BufferedReader(new InputStreamReader(handbookFile.getInputStream()));
|
||||
|
||||
JsonElement je = gson.fromJson(reader, JsonElement.class);
|
||||
JsonObject json = je.getAsJsonObject();
|
||||
|
||||
JsonUtils.getJsonObject(json, "colours").entrySet().forEach(e -> colours.put(e.getKey(),
|
||||
Color.decode(e.getValue().getAsString()).getRGB()));
|
||||
|
||||
// Repopulates the remaining maps
|
||||
Image.populate(images, json);
|
||||
CraftingRecipe.populate(recipes, json);
|
||||
Section.populate(sections, json);
|
||||
|
||||
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");
|
||||
}
|
||||
|
||||
// The first resource load on startup is done before the packet handler is loaded
|
||||
if(WizardryPacketHandler.net != null) WizardryPacketHandler.net.sendToServer(new PacketRequestAdvancementSync.Message());
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the handbook JSON file for the current language and returns its IResource object. If a handbook file
|
||||
* cannot be found for the current language, a message is printed to the console and the method attempts to retrieve
|
||||
* the default file instead (English-US). If this file cannot be found, the resulting error is printed to the
|
||||
* console and the method returns null.
|
||||
*
|
||||
* @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){
|
||||
|
||||
// TODO: Implement resource pack stacking to allow addon mods and texture packs to add/overwrite content
|
||||
|
||||
IResource handbookFile = null;
|
||||
|
||||
try{
|
||||
handbookFile = manager.getResource(new ResourceLocation(Wizardry.MODID, "texts/handbook_"
|
||||
+ Minecraft.getMinecraft().getLanguageManager().getCurrentLanguage().getLanguageCode() + ".json"));
|
||||
}catch(IOException 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(DEFAULT);
|
||||
}catch(IOException x){
|
||||
Wizardry.logger.error("Couldn't find file: " + DEFAULT + ". The file may be missing; please try re-downloading and reinstalling Wizardry.", x);
|
||||
}
|
||||
}
|
||||
|
||||
return handbookFile;
|
||||
}
|
||||
|
||||
// Controls
|
||||
|
||||
@Override
|
||||
protected void actionPerformed(GuiButton button){
|
||||
|
||||
if(button.enabled){
|
||||
|
||||
if(button == next){
|
||||
if(currentPage < singleToDoublePage(pageCount)) currentPage++;
|
||||
|
||||
}else if(button == previous){
|
||||
if(currentPage > 0) currentPage--;
|
||||
|
||||
}else if(button == nextSection || button == previousSection){
|
||||
|
||||
Section currentSection = null;
|
||||
|
||||
for(Section section : sections.values()){
|
||||
// We always want this button to do something, and taking the right-hand page means it always does
|
||||
if(section.containsPage(doubleToSinglePage(currentPage, true))){
|
||||
currentSection = section;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(currentSection != null){
|
||||
|
||||
List<Section> visibleSections = new ArrayList<>(sectionList);
|
||||
visibleSections.removeIf(s -> !s.isUnlocked());
|
||||
|
||||
int index = visibleSections.indexOf(currentSection);
|
||||
|
||||
if(button == nextSection && index + 1 < visibleSections.size()){
|
||||
currentPage = singleToDoublePage(visibleSections.get(index + 1).startPage);
|
||||
}else if(index > 0){
|
||||
currentPage = singleToDoublePage(visibleSections.get(index - 1).startPage);
|
||||
}
|
||||
}
|
||||
|
||||
}else if(button == menu){
|
||||
currentPage = singleToDoublePage(sections.get("main_contents").startPage);
|
||||
|
||||
}else if(button == bookmark && bookmarkSection != null){
|
||||
currentPage = singleToDoublePage(sections.get(bookmarkSection).startPage) + bookmarkPage;
|
||||
|
||||
}else{
|
||||
if(button instanceof GuiButtonHyperlink.Internal){
|
||||
currentPage = singleToDoublePage(((GuiButtonHyperlink.Internal)button).target.startPage);
|
||||
}else if(button instanceof GuiButtonHyperlink.External){
|
||||
this.handleComponentClick(((GuiButtonHyperlink.External)button).link);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void mouseClicked(int mouseX, int mouseY, int mouseButton) throws IOException{
|
||||
if(mouseButton == 1){
|
||||
// Right-clicking of bookmark
|
||||
if(bookmark.mousePressed(this.mc, mouseX, mouseY)){
|
||||
|
||||
this.selectedButton = bookmark;
|
||||
|
||||
for(String key : sections.keySet()){
|
||||
// The bookmark is assumed to bookmark the left-hand page
|
||||
if(sections.get(key).containsPage(doubleToSinglePage(currentPage, false))) bookmarkSection = key;
|
||||
}
|
||||
|
||||
bookmarkPage = currentPage - singleToDoublePage(sections.get(bookmarkSection).startPage);
|
||||
}
|
||||
}else{
|
||||
super.mouseClicked(mouseX, mouseY, mouseButton);
|
||||
}
|
||||
}
|
||||
|
||||
// Overridden to make it public
|
||||
@Override
|
||||
public void renderToolTip(ItemStack stack, int x, int y){
|
||||
super.renderToolTip(stack, x, y);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean doesGuiPauseGame(){
|
||||
return Wizardry.settings.booksPauseGame;
|
||||
}
|
||||
|
||||
|
||||
public static void updateUnlockStatus(boolean showToasts, ResourceLocation... completedAdvancements){
|
||||
sections.values().forEach(s -> s.updateUnlockStatus(showToasts, completedAdvancements));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
package electroblob.wizardry.client.gui.handbook;
|
||||
|
||||
import electroblob.wizardry.registry.WizardryItems;
|
||||
import net.minecraft.client.gui.toasts.GuiToast;
|
||||
import net.minecraft.client.gui.toasts.IToast;
|
||||
import net.minecraft.client.renderer.GlStateManager;
|
||||
import net.minecraft.client.renderer.RenderHelper;
|
||||
import net.minecraft.client.resources.I18n;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.util.math.MathHelper;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
//@SideOnly(Side.CLIENT)
|
||||
public class HandbookToast implements IToast {
|
||||
|
||||
private final Section section;
|
||||
|
||||
public HandbookToast(Section section){
|
||||
this.section = section;
|
||||
}
|
||||
|
||||
public IToast.Visibility draw(GuiToast toastGui, long delta){
|
||||
|
||||
toastGui.getMinecraft().getTextureManager().bindTexture(TEXTURE_TOASTS);
|
||||
|
||||
GlStateManager.color(1.0F, 1.0F, 1.0F);
|
||||
toastGui.drawTexturedModalRect(0, 0, 0, 32, 160, 32);
|
||||
|
||||
boolean firstPart = delta < 1500L;
|
||||
|
||||
int a = firstPart ? MathHelper.floor(MathHelper.clamp((float)(1500L - delta) / 300.0F, 0.0F, 1.0F) * 255.0F) << 24 | 67108864
|
||||
: MathHelper.floor(MathHelper.clamp((float)(delta - 1500L) / 300.0F, 0.0F, 1.0F) * 252.0F) << 24 | 67108864;
|
||||
|
||||
String s = firstPart ? I18n.format("handbook.toast.title") : section.title;
|
||||
|
||||
int c = firstPart ? -11534256 : -16777216;
|
||||
|
||||
List<String> list = toastGui.getMinecraft().fontRenderer.listFormattedStringToWidth(s, 125);
|
||||
|
||||
int h = 16 - list.size() * toastGui.getMinecraft().fontRenderer.FONT_HEIGHT / 2;
|
||||
|
||||
for(String line : list){
|
||||
toastGui.getMinecraft().fontRenderer.drawString(line, 30, h, c | a);
|
||||
h += toastGui.getMinecraft().fontRenderer.FONT_HEIGHT;
|
||||
}
|
||||
|
||||
RenderHelper.enableGUIStandardItemLighting();
|
||||
toastGui.getMinecraft().getRenderItem().renderItemAndEffectIntoGUI(null, new ItemStack(WizardryItems.wizard_handbook), 8, 8);
|
||||
|
||||
return delta >= 5000L ? IToast.Visibility.HIDE : IToast.Visibility.SHOW;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,148 @@
|
||||
package electroblob.wizardry.client.gui.handbook;
|
||||
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.google.gson.JsonSyntaxException;
|
||||
import electroblob.wizardry.client.DrawingUtils;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.gui.FontRenderer;
|
||||
import net.minecraft.client.renderer.GlStateManager;
|
||||
import net.minecraft.util.JsonUtils;
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
import net.minecraft.util.math.MathHelper;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
class Image {
|
||||
|
||||
// Final fields are mandatory, the rest are optional
|
||||
private final ResourceLocation location;
|
||||
private final int width, height;
|
||||
private int textureWidth, textureHeight;
|
||||
private int u = 0, v = 0;
|
||||
private String caption = "";
|
||||
private boolean border = true;
|
||||
// Derived fields, not specifically defined in JSON
|
||||
private final Set<int[]> instances = new HashSet<>();
|
||||
|
||||
private static final int CAPTION_OFFSET = 4;
|
||||
|
||||
private static final int TEXTURE_INSET_X = 180;
|
||||
private static final int BORDER = 1;
|
||||
|
||||
private Image(ResourceLocation location, int width, int height){
|
||||
this.location = location;
|
||||
this.width = width;
|
||||
this.height = height;
|
||||
}
|
||||
|
||||
/** Returns the width of the image. */
|
||||
int getWidth(){
|
||||
return width;
|
||||
}
|
||||
|
||||
/** Returns the total height of the image, including caption if it has one. */
|
||||
int getHeight(FontRenderer font){
|
||||
return caption.isEmpty() ? height : height + CAPTION_OFFSET + font.FONT_HEIGHT;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds an instance of this image to the list.
|
||||
*
|
||||
* @param page The index of the <b>single</b> page this image is on.
|
||||
* @param x The x-coordinate of the top-left corner of the image, <i>relative</i> to the top-left corner of the GUI.
|
||||
* @param y The y-coordinate of the top-left corner of the image, <i>relative</i> to the top-left corner of the GUI.
|
||||
*/
|
||||
void addInstance(int page, int x, int y){
|
||||
instances.add(new int[]{page, x, y});
|
||||
}
|
||||
|
||||
/** Removes all instances of this image from the list. */
|
||||
void clearInstances(){
|
||||
instances.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* Draws all instances of this image that are located on the given double-page spread.
|
||||
*
|
||||
* @param font The font renderer object.
|
||||
* @param doublePage The double-page index of the page to be drawn.
|
||||
* @param left The x coordinate of the left side of the GUI.
|
||||
* @param top The y coordinate of the top of the GUI.
|
||||
*/
|
||||
void draw(FontRenderer font, int doublePage, int left, int top){
|
||||
// Images
|
||||
for(int[] instance : instances){
|
||||
if(GuiWizardHandbook.singleToDoublePage(instance[0]) == doublePage){
|
||||
Minecraft.getMinecraft().renderEngine.bindTexture(location);
|
||||
GlStateManager.color(1, 1, 1, 1);
|
||||
DrawingUtils.drawTexturedRect(left + instance[1], top + instance[2], u, v, width, height, textureWidth, textureHeight);
|
||||
font.drawString("\u00A7o" + caption, left + instance[1] + width/ 2 - font.getStringWidth(caption)/2,
|
||||
top + instance[2] + height + CAPTION_OFFSET, GuiWizardHandbook.colours.get("caption"));
|
||||
}
|
||||
}
|
||||
|
||||
if(border){
|
||||
// Borders - do this after all the images are drawn so we only have to bind the handbook texture again once
|
||||
Minecraft.getMinecraft().renderEngine.bindTexture(GuiWizardHandbook.texture);
|
||||
GlStateManager.color(1, 1, 1, 1);
|
||||
for(int[] instance : instances){
|
||||
if(GuiWizardHandbook.singleToDoublePage(instance[0]) == doublePage){
|
||||
// Math.ceil accounts for odd-numbered image dimensions
|
||||
DrawingUtils.drawTexturedFlippedRect(left + instance[1] - BORDER, top + instance[2] - BORDER,
|
||||
TEXTURE_INSET_X, GuiWizardHandbook.GUI_HEIGHT, width / 2 + BORDER, height / 2 + BORDER,
|
||||
GuiWizardHandbook.TEXTURE_WIDTH, GuiWizardHandbook.TEXTURE_HEIGHT, false, false);
|
||||
DrawingUtils.drawTexturedFlippedRect(left + instance[1] + width / 2, top + instance[2] - BORDER,
|
||||
TEXTURE_INSET_X, GuiWizardHandbook.GUI_HEIGHT, MathHelper.ceil(width / 2f) + BORDER, height / 2 + BORDER,
|
||||
GuiWizardHandbook.TEXTURE_WIDTH, GuiWizardHandbook.TEXTURE_HEIGHT, true, false);
|
||||
DrawingUtils.drawTexturedFlippedRect(left + instance[1] - BORDER, top + instance[2] + height / 2,
|
||||
TEXTURE_INSET_X, GuiWizardHandbook.GUI_HEIGHT, width / 2 + BORDER, MathHelper.ceil(height / 2f) + BORDER,
|
||||
GuiWizardHandbook.TEXTURE_WIDTH, GuiWizardHandbook.TEXTURE_HEIGHT, false, true);
|
||||
DrawingUtils.drawTexturedFlippedRect(left + instance[1] + width / 2, top + instance[2] + height / 2,
|
||||
TEXTURE_INSET_X, GuiWizardHandbook.GUI_HEIGHT, MathHelper.ceil(width / 2f) + BORDER, MathHelper.ceil(height / 2f) + BORDER,
|
||||
GuiWizardHandbook.TEXTURE_WIDTH, GuiWizardHandbook.TEXTURE_HEIGHT, true, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses the given JSON object and constructs a new {@code Image} from it, setting all the relevant fields
|
||||
* and references.
|
||||
*
|
||||
* @param json A JSON object representing the image to be constructed. This must contain at least a "location"
|
||||
* string.
|
||||
* @return The resulting {@code Image} object.
|
||||
* @throws JsonSyntaxException if at any point the JSON object is found to be invalid.
|
||||
*/
|
||||
static Image fromJson(JsonObject json){
|
||||
|
||||
Image image = new Image(new ResourceLocation(JsonUtils.getString(json, "location")),
|
||||
JsonUtils.getInt(json, "width"), JsonUtils.getInt(json, "height"));
|
||||
|
||||
image.u = JsonUtils.getInt(json, "u", 0);
|
||||
image.v = JsonUtils.getInt(json, "v", 0);
|
||||
image.textureWidth = JsonUtils.getInt(json, "texture_width", image.width);
|
||||
image.textureHeight = JsonUtils.getInt(json, "texture_height", image.height);
|
||||
image.caption = JsonUtils.getString(json, "caption", "");
|
||||
image.border = JsonUtils.getBoolean(json, "border", true);
|
||||
|
||||
return image;
|
||||
}
|
||||
|
||||
static void populate(Map<String, Image> map, JsonObject json){
|
||||
|
||||
JsonObject sectionsObject = JsonUtils.getJsonObject(json, "images");
|
||||
|
||||
// Need to iterate over these since we don't know what they're called or how many there are
|
||||
for(Map.Entry<String, JsonElement> entry : sectionsObject.entrySet()){
|
||||
|
||||
String key = entry.getKey(); // Find out what each element is called, this will be the sections map key
|
||||
|
||||
Image image = fromJson(entry.getValue().getAsJsonObject());
|
||||
map.put(key, image);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,451 @@
|
||||
package electroblob.wizardry.client.gui.handbook;
|
||||
|
||||
import com.google.common.collect.Streams;
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.google.gson.JsonSyntaxException;
|
||||
import electroblob.wizardry.Wizardry;
|
||||
import electroblob.wizardry.client.DrawingUtils;
|
||||
import electroblob.wizardry.util.WizardryUtilities;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.gui.FontRenderer;
|
||||
import net.minecraft.client.gui.GuiButton;
|
||||
import net.minecraft.client.renderer.GlStateManager;
|
||||
import net.minecraft.util.JsonUtils;
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* Instances of this class represent sections in the wizard's handbook. As of wizardry 4.2, this class handles
|
||||
* everything within the section itself, including JSON parsing, unlock triggers and drawing the actual rawText.
|
||||
* Sections may now also be nested and have other elements within them, such as images and a table of contents, a
|
||||
* behaviour which is also handled within this class.
|
||||
* <p></p>
|
||||
* The formatting of the book is now done 'dynamically' - that is, the exact positions and page numbers of
|
||||
* sections, images and so on are determined on GUI load and depend on which of the previous sections have been
|
||||
* unlocked, amongst other factors. This means that all of the unlocked sections must be formatted in order on GUI
|
||||
* load, so that each section knows the previous section's length and therefore where to start.
|
||||
*
|
||||
* @author Electroblob
|
||||
* @since Wizardry 4.2
|
||||
*/
|
||||
// Because these are now generated on resource pack reload (not on handbook open, as before), this class can no longer
|
||||
// be a non-static inner class
|
||||
class Section {
|
||||
|
||||
// Final fields are mandatory (none here though), the rest are optional
|
||||
String title;
|
||||
private String[] rawText;
|
||||
private Contents contents;
|
||||
private ResourceLocation[] triggers;
|
||||
private Map<String, Section> subsections;
|
||||
private boolean centreX, centreY;
|
||||
|
||||
// Derived fields, not explicitly defined in JSON
|
||||
/** The <b>single-page</b> index of the first page of this section. */
|
||||
int startPage;
|
||||
private final List<List<GuiButton>> buttons;
|
||||
/**
|
||||
* A list of <b>single</b> pages, which are themselves lists of paragraphs (each paragraph is a single
|
||||
* string which may include line breaks and other escape characters).
|
||||
*/
|
||||
private final List<List<String>> pages;
|
||||
|
||||
private boolean unlocked = false;
|
||||
private boolean isNew = false;
|
||||
|
||||
private Section(){
|
||||
this.buttons = new ArrayList<>();
|
||||
this.pages = new ArrayList<>();
|
||||
this.subsections = new LinkedHashMap<>();
|
||||
}
|
||||
|
||||
Collection<GuiButton> getButtons(){
|
||||
return WizardryUtilities.flatten(buttons);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the given page is within this section, false if not (or if the section is locked).
|
||||
*/
|
||||
boolean containsPage(int page){
|
||||
return this.isUnlocked() && startPage <= page && startPage + pages.size() > page;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if this section is unlocked for the client player, false if not. Always returns true if
|
||||
* handbook progression is disabled in the config.
|
||||
*/
|
||||
boolean isUnlocked(){
|
||||
|
||||
if(Minecraft.getMinecraft().player.isCreative()) return true;
|
||||
if(!Wizardry.settings.handbookProgression) return true; // Always unlocked if handbook progression is off
|
||||
if(triggers == null) return true; // If no triggers were defined, the section is unlocked from the start
|
||||
|
||||
// A section is automatically unlocked if one of its subsections is unlocked
|
||||
for(Section subsection : subsections.values()){
|
||||
if(subsection.isUnlocked()) return true;
|
||||
}
|
||||
|
||||
return unlocked;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if this section has been unlocked and not read yet. Also returns true if any subsections are new.
|
||||
*/
|
||||
boolean isNew(){
|
||||
if(!Wizardry.settings.handbookProgression) return false;
|
||||
return isNew || this.subsections.values().stream().anyMatch(Section::isNew);
|
||||
}
|
||||
|
||||
/**
|
||||
* Actually draws the contents of the given section for the given double-page spread. Will do nothing if the
|
||||
* given page is outside of this section.
|
||||
*
|
||||
* @param font The font renderer object.
|
||||
* @param doublePage The index of the <b>double-page</b> to be drawn.
|
||||
* @param left The x coordinate of the left side of the GUI.
|
||||
* @param top The y coordinate of the top of the GUI.
|
||||
*/
|
||||
// This method is supposed to be 'idiot-proof' in the sense that the code calling it need not check whether the
|
||||
// section actually needs drawing, so it can just dumbly call draw(...) for all the sections in order.
|
||||
void draw(FontRenderer font, int doublePage, int left, int top){
|
||||
|
||||
// Show/hide buttons
|
||||
|
||||
int i = 0;
|
||||
|
||||
for(List<GuiButton> list : buttons){
|
||||
final int i1 = i++;
|
||||
list.forEach(b -> b.visible = GuiWizardHandbook.singleToDoublePage(startPage + i1) == doublePage);
|
||||
}
|
||||
|
||||
int leftIndex = GuiWizardHandbook.doubleToSinglePage(doublePage, false);
|
||||
// Relative indices of the pages to be rendered - often these will be outside the section entirely
|
||||
int[] visiblePages = {leftIndex - startPage, leftIndex - startPage + 1};
|
||||
|
||||
for(int page : visiblePages){
|
||||
|
||||
if(page >= 0 && page < pages.size()){
|
||||
|
||||
List<String> lines = pages.get(page);
|
||||
|
||||
int x = left + (GuiWizardHandbook.isRightPage(startPage + page) ? GuiWizardHandbook.GUI_WIDTH - GuiWizardHandbook.TEXT_INSET_X - GuiWizardHandbook.PAGE_WIDTH : GuiWizardHandbook.TEXT_INSET_X);
|
||||
int y = top + GuiWizardHandbook.TEXT_INSET_Y;
|
||||
if(centreY) y += GuiWizardHandbook.PAGE_HEIGHT / 2 - lines.size() / 2 * font.FONT_HEIGHT;
|
||||
|
||||
for(String line : lines){
|
||||
|
||||
if(line.startsWith(GuiWizardHandbook.FORMAT_MARKER + GuiWizardHandbook.RULER_TAG)){
|
||||
GlStateManager.color(1, 1, 1, 1);
|
||||
Minecraft.getMinecraft().renderEngine.bindTexture(GuiWizardHandbook.texture);
|
||||
DrawingUtils.drawTexturedRect(x-1, y-1, 0, GuiWizardHandbook.GUI_HEIGHT, GuiWizardHandbook.PAGE_WIDTH + 2, 9, GuiWizardHandbook.TEXTURE_WIDTH, GuiWizardHandbook.TEXTURE_HEIGHT);
|
||||
}else{
|
||||
int lx = centreX ? x + GuiWizardHandbook.PAGE_WIDTH / 2 - font.getStringWidth(line) / 2 : x;
|
||||
font.drawString(line, lx, y, DrawingUtils.BLACK, false);
|
||||
}
|
||||
|
||||
y += font.FONT_HEIGHT;
|
||||
}
|
||||
|
||||
isNew = false; // Now a page has been drawn, the player must have seen it so it's not new any more
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called on GUI load to format the section, contents tables and other elements, <b>excluding</b> subsections.
|
||||
* Does not perform any actual drawing.
|
||||
*
|
||||
* @param font The font renderer object, for measurement purposes.
|
||||
* @param startPage The index of the first page (single side, not double-page) of this section.
|
||||
* @param left The x coordinate of the left side of the GUI.
|
||||
* @param top The y coordinate of the top of the GUI.
|
||||
* @return The <b>single-page</b> index of the next blank page after the end of this section.
|
||||
* @throws JsonSyntaxException if at any point the formatting is found to be invalid.
|
||||
*/
|
||||
int format(FontRenderer font, int startPage, int left, int top){
|
||||
|
||||
this.buttons.clear();
|
||||
this.pages.clear();
|
||||
|
||||
// FONT_HEIGHT may change between fonts, so this is calculated here. With the default font it's 14.
|
||||
final int maxLineNumber = GuiWizardHandbook.PAGE_HEIGHT / font.FONT_HEIGHT;
|
||||
|
||||
this.startPage = startPage;
|
||||
|
||||
// First everything is added to a single list of lines, then it is split into pages.
|
||||
List<String> lines = new ArrayList<>();
|
||||
|
||||
// Adds the header if present
|
||||
if(!this.title.isEmpty()){
|
||||
lines.add(this.title);
|
||||
lines.add(GuiWizardHandbook.FORMAT_MARKER + GuiWizardHandbook.RULER_TAG);
|
||||
}
|
||||
|
||||
// Adds space for the contents if it exists
|
||||
if(this.contents != null){
|
||||
lines.addAll(Collections.nCopies(this.contents.format(font, startPage, lines.size(), left, top), ""));
|
||||
// Line break between contents and first paragraph
|
||||
if((lines.size() % maxLineNumber) != 0) lines.add("");
|
||||
}
|
||||
|
||||
if(this.rawText != null){
|
||||
// Paragraphs are defined as a JSON list because it makes it easier to arrange them properly across pages
|
||||
// - using multiple line breaks would mean having to find and remove them when at the top of a page.
|
||||
for(String paragraph : this.rawText){
|
||||
|
||||
// (lines.size() % maxLineNumber) gives the number of lines on the current page
|
||||
// (lines.size() / maxLineNumber) gives the index of the current page minus the value of startPage
|
||||
|
||||
// Formats the paragraph
|
||||
|
||||
String raw = paragraph; // For error messages
|
||||
|
||||
// Images (images must be separate paragraphs)
|
||||
|
||||
if(paragraph.startsWith(GuiWizardHandbook.FORMAT_MARKER + GuiWizardHandbook.IMAGE_TAG)){
|
||||
|
||||
String[] arguments = paragraph.split("\\s", 2);
|
||||
|
||||
if(arguments.length < 2) throw new JsonSyntaxException("Missing image name in string "
|
||||
+ StringUtils.abbreviate(raw, 50));
|
||||
|
||||
Image image = GuiWizardHandbook.images.get(arguments[1]);
|
||||
if(image == null) throw new JsonSyntaxException("Image with id " + arguments[1] + " is undefined");
|
||||
|
||||
// Starts a new page if the image will not fit on the current one
|
||||
if((lines.size() % maxLineNumber) * font.FONT_HEIGHT + image.getHeight(font) > GuiWizardHandbook.PAGE_HEIGHT){
|
||||
// Remaining number of lines on the page
|
||||
lines.addAll(Collections.nCopies(maxLineNumber - (lines.size() % maxLineNumber), ""));
|
||||
}
|
||||
|
||||
if(image.getWidth() > GuiWizardHandbook.PAGE_WIDTH) Wizardry.logger.warn("Image with id " + arguments[1]
|
||||
+ "has a width (" + image.getWidth() + ") greater than the maximum page width (" + GuiWizardHandbook.PAGE_WIDTH
|
||||
+ "), it will extend beyond the page area.");
|
||||
|
||||
if(image.getHeight(font) > GuiWizardHandbook.PAGE_HEIGHT) Wizardry.logger.warn("Image with id " + arguments[1]
|
||||
+ "has a height (" + image.getHeight(font) + ") greater than the maximum page height (" + GuiWizardHandbook.PAGE_HEIGHT
|
||||
+ "), it will extend beyond the page area.");
|
||||
|
||||
int page = startPage + (lines.size() / maxLineNumber);
|
||||
|
||||
image.addInstance(page, GuiWizardHandbook.PAGE_WIDTH / 2 - image.getWidth() / 2
|
||||
+ (GuiWizardHandbook.isRightPage(page) ? GuiWizardHandbook.GUI_WIDTH - GuiWizardHandbook.TEXT_INSET_X - GuiWizardHandbook.PAGE_WIDTH : GuiWizardHandbook.TEXT_INSET_X),
|
||||
GuiWizardHandbook.TEXT_INSET_Y + (lines.size() % maxLineNumber) * font.FONT_HEIGHT);
|
||||
|
||||
// Height of the image in lines, rounded up
|
||||
// Uses a single space instead of an empty string so that the page trimming doesn't remove them
|
||||
lines.addAll(Collections.nCopies(image.getHeight(font) / font.FONT_HEIGHT, " "));
|
||||
lines.add(""); // The last one is removable though, since it's actually extra space
|
||||
|
||||
// Recipes (recipes must be separate paragraphs)
|
||||
}else if(paragraph.startsWith(GuiWizardHandbook.FORMAT_MARKER + GuiWizardHandbook.RECIPE_TAG)){
|
||||
|
||||
String[] arguments = paragraph.split("\\s", 2);
|
||||
|
||||
if(arguments.length < 2) throw new JsonSyntaxException("Missing recipe name in string "
|
||||
+ StringUtils.abbreviate(raw, 50));
|
||||
|
||||
CraftingRecipe recipe = GuiWizardHandbook.recipes.get(arguments[1]);
|
||||
if(recipe == null) throw new JsonSyntaxException("Recipe with id " + arguments[1] + " is undefined");
|
||||
|
||||
// Starts a new page if the recipe will not fit on the current one
|
||||
if((lines.size() % maxLineNumber) * font.FONT_HEIGHT + CraftingRecipe.HEIGHT > GuiWizardHandbook.PAGE_HEIGHT){
|
||||
// Remaining number of lines on the page, plus the first blank one on the new page
|
||||
lines.addAll(Collections.nCopies(maxLineNumber - (lines.size() % maxLineNumber), " "));
|
||||
}
|
||||
|
||||
int page = startPage + (lines.size() / maxLineNumber);
|
||||
|
||||
if(lines.size() % maxLineNumber == 0) lines.add(" ");
|
||||
int startLine = lines.size() % maxLineNumber - 1;
|
||||
|
||||
recipe.addInstance(page, GuiWizardHandbook.PAGE_WIDTH / 2 - CraftingRecipe.WIDTH / 2
|
||||
+ (GuiWizardHandbook.isRightPage(page) ? GuiWizardHandbook.GUI_WIDTH - GuiWizardHandbook.TEXT_INSET_X - GuiWizardHandbook.PAGE_WIDTH : GuiWizardHandbook.TEXT_INSET_X),
|
||||
GuiWizardHandbook.TEXT_INSET_Y + startLine * font.FONT_HEIGHT);
|
||||
|
||||
// Height of the recipe in lines, rounded up
|
||||
// Uses a single space instead of an empty string so that the page trimming doesn't remove them
|
||||
lines.addAll(Collections.nCopies(CraftingRecipe.HEIGHT / font.FONT_HEIGHT - 1, " "));
|
||||
// This time we're not adding an extra space because it's not really needed
|
||||
|
||||
}else{ // All other paragraphs
|
||||
|
||||
// Formatting
|
||||
for(Map.Entry<String, String> entry : GuiWizardHandbook.FORMAT_TAGS.entrySet()){
|
||||
paragraph = paragraph.replace(GuiWizardHandbook.FORMAT_MARKER + entry.getKey(), entry.getValue());
|
||||
}
|
||||
|
||||
// Hyperlinks
|
||||
|
||||
int linkStart;
|
||||
|
||||
while((linkStart = paragraph.indexOf(GuiWizardHandbook.HYPERLINK_MARKER)) > -1){ // Ooh an assignment and a comparison in one...
|
||||
|
||||
int linkEnd = paragraph.indexOf(GuiWizardHandbook.HYPERLINK_MARKER, linkStart + 1);
|
||||
|
||||
if(linkEnd < 0) throw new JsonSyntaxException("Un-closed hyperlink marker in string "
|
||||
+ StringUtils.abbreviate(raw, 50));
|
||||
|
||||
List<String> upToLink = font.listFormattedStringToWidth(paragraph.substring(0, linkStart), GuiWizardHandbook.PAGE_WIDTH);
|
||||
|
||||
String linkRaw = paragraph.substring(linkStart, linkEnd + 1);
|
||||
String[] arguments = paragraph.substring(linkStart + 1, linkEnd).split("\\s", 2);
|
||||
String suffix = paragraph.substring(linkEnd).split("\\s", 2)[0].substring(1); // substring(1) to remove the @
|
||||
|
||||
// The index of the single page currently being formatted, relative to the section
|
||||
int pageRelative = (lines.size() + upToLink.size() - 1) / maxLineNumber;
|
||||
// The overall index of the single page currently being formatted
|
||||
int page = startPage + pageRelative;
|
||||
// The line number on this page
|
||||
int lineNumber = (lines.size() + upToLink.size() - 1) % maxLineNumber;
|
||||
|
||||
int x = GuiWizardHandbook.isRightPage(page) ? left + GuiWizardHandbook.GUI_WIDTH - GuiWizardHandbook.TEXT_INSET_X - GuiWizardHandbook.PAGE_WIDTH : left + GuiWizardHandbook.TEXT_INSET_X;
|
||||
int y = top + GuiWizardHandbook.TEXT_INSET_Y + lineNumber * font.FONT_HEIGHT;
|
||||
|
||||
// Adds any missing sub-lists
|
||||
while(this.buttons.size() <= pageRelative){
|
||||
this.buttons.add(new ArrayList<>());
|
||||
}
|
||||
|
||||
// The button id only does what you use it for, so we're just not using it at all.
|
||||
this.buttons.get(pageRelative).add(GuiButtonHyperlink.create(x, y, font, upToLink, arguments, suffix, maxLineNumber - lineNumber - 1, GuiWizardHandbook.isRightPage(page)));
|
||||
|
||||
// The link button should exactly overlay the display rawText in the main string
|
||||
// If the link has no display rawText specified, it displays the unformatted target string
|
||||
paragraph = paragraph.replace(linkRaw, arguments[arguments.length - 1]);
|
||||
}
|
||||
|
||||
lines.addAll(font.listFormattedStringToWidth(paragraph, GuiWizardHandbook.PAGE_WIDTH));
|
||||
}
|
||||
|
||||
// Line break between paragraphs (the last one will just be deleted later)
|
||||
if((lines.size() % maxLineNumber) != 0) lines.add("");
|
||||
}
|
||||
}
|
||||
|
||||
// Splits lines into pages
|
||||
|
||||
List<String> page = new ArrayList<>();
|
||||
pages.add(page);
|
||||
|
||||
while(!lines.isEmpty()){
|
||||
|
||||
if(page.size() == maxLineNumber){
|
||||
// Removes blank lines at the end of the page
|
||||
while(page.get(page.size() - 1).isEmpty()) page.remove(page.size() - 1);
|
||||
// Adds a new page
|
||||
pages.add(page = new ArrayList<>());
|
||||
}
|
||||
|
||||
String line = lines.remove(0);
|
||||
|
||||
// Prevents blank lines at the start of the page
|
||||
if(!page.isEmpty() || !line.isEmpty()) page.add(line);
|
||||
}
|
||||
|
||||
return startPage + pages.size();
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses the given JSON object and constructs a new {@code Section} from it, setting all the relevant fields
|
||||
* and references. This method converts the JSON object to a {@code Section} object and retrieves any resources;
|
||||
* the section is not formatted in any way until GUI load, in {@link Section#format(FontRenderer, int, int, int)}.
|
||||
*
|
||||
* @param json A JSON object representing the section to be constructed. This must contain at least a "title"
|
||||
* string.
|
||||
* @return The resulting {@code Section} object.
|
||||
* @throws JsonSyntaxException if at any point the JSON object is found to be invalid.
|
||||
*/
|
||||
static Section fromJson(JsonObject json){
|
||||
|
||||
Section section = new Section();
|
||||
|
||||
section.title = JsonUtils.getString(json, "title", "");
|
||||
|
||||
if(JsonUtils.hasField(json, "include_in_contents")){
|
||||
|
||||
String id = JsonUtils.getString(json, "include_in_contents");
|
||||
|
||||
Contents belongsTo = GuiWizardHandbook.contentsList.get(id);
|
||||
|
||||
if(belongsTo == null){
|
||||
throw new JsonSyntaxException("Expected include_in_contents to be the id of a previously defined contents, but no contents with the id " + id + " exists yet.");
|
||||
}else{
|
||||
belongsTo.addEntry(section);
|
||||
}
|
||||
}
|
||||
|
||||
if(JsonUtils.hasField(json, "contents")){
|
||||
section.contents = Contents.fromJson(section, JsonUtils.getJsonObject(json, "contents"));
|
||||
GuiWizardHandbook.contentsList.put(section.contents.id, section.contents);
|
||||
}
|
||||
|
||||
if(JsonUtils.hasField(json, "text")){
|
||||
section.rawText = Streams.stream(JsonUtils.getJsonArray(json, "text"))
|
||||
.map(e -> JsonUtils.getString(e, "element of array rawText"))
|
||||
.toArray(String[]::new);
|
||||
}
|
||||
|
||||
if(JsonUtils.hasField(json, "triggers")){
|
||||
section.triggers = Streams.stream(JsonUtils.getJsonArray(json, "triggers"))
|
||||
.map(e -> new ResourceLocation(JsonUtils.getString(e, "element of array triggers")))
|
||||
.toArray(ResourceLocation[]::new);
|
||||
// TODO: Can we validate this and throw a JSON exception if no such advancement exists?
|
||||
}
|
||||
|
||||
if(JsonUtils.hasField(json, "centre")){
|
||||
JsonObject centre = JsonUtils.getJsonObject(json,"centre");
|
||||
section.centreX = JsonUtils.getBoolean(centre, "x", false);
|
||||
section.centreY = JsonUtils.getBoolean(centre, "y", false);
|
||||
}
|
||||
|
||||
// The only benefit of having subsections (other than logical grouping) is that the parent section can
|
||||
// automatically be unlocked if one of the subsections is.
|
||||
if(JsonUtils.hasField(json, "sections")){
|
||||
populate(section.subsections, json);
|
||||
}
|
||||
|
||||
return section;
|
||||
}
|
||||
|
||||
static void populate(Map<String, Section> map, JsonObject json){
|
||||
|
||||
JsonObject sectionsObject = JsonUtils.getJsonObject(json, "sections");
|
||||
|
||||
// Need to iterate over these since we don't know what they're called or how many there are
|
||||
for(Map.Entry<String, JsonElement> entry : sectionsObject.entrySet()){
|
||||
|
||||
String key = entry.getKey(); // Find out what each element is called, this will be the sections map key
|
||||
|
||||
Section section = fromJson(entry.getValue().getAsJsonObject());
|
||||
map.put(key, section);
|
||||
map.putAll(section.subsections);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called on login and advancement completion to update this section's unlock status and display toast
|
||||
* notifications if applicable.
|
||||
*/
|
||||
public void updateUnlockStatus(boolean showToasts, ResourceLocation... completedAdvancements){
|
||||
|
||||
if(triggers == null) return;
|
||||
|
||||
List<ResourceLocation> completed = new ArrayList<>(Arrays.asList(completedAdvancements));
|
||||
completed.retainAll(Arrays.asList(triggers));
|
||||
|
||||
// Only shows the toast when the section was locked before and is now unlocked
|
||||
if(!this.unlocked && !completed.isEmpty() && showToasts && Wizardry.settings.handbookProgression){
|
||||
// Mmmmm toast...
|
||||
Minecraft minecraft = Minecraft.getMinecraft();
|
||||
minecraft.getToastGui().add(new HandbookToast(this));
|
||||
this.isNew = true;
|
||||
}
|
||||
|
||||
// Currently, this will not take subsections into account
|
||||
this.unlocked = !completed.isEmpty();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,234 @@
|
||||
package electroblob.wizardry.client.model;
|
||||
|
||||
import net.minecraft.block.state.IBlockState;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.renderer.block.model.BakedQuad;
|
||||
import net.minecraft.client.renderer.block.model.IBakedModel;
|
||||
import net.minecraft.client.renderer.block.model.ItemCameraTransforms;
|
||||
import net.minecraft.client.renderer.block.model.ItemOverrideList;
|
||||
import net.minecraft.client.renderer.texture.TextureAtlasSprite;
|
||||
import net.minecraft.client.renderer.vertex.DefaultVertexFormats;
|
||||
import net.minecraft.client.renderer.vertex.VertexFormat;
|
||||
import net.minecraft.util.EnumFacing;
|
||||
import net.minecraftforge.client.model.pipeline.UnpackedBakedQuad;
|
||||
import net.minecraftforge.client.model.pipeline.VertexLighterFlat;
|
||||
import net.minecraftforge.common.ForgeModContainer;
|
||||
import net.minecraftforge.fml.client.FMLClientHandler;
|
||||
import org.apache.commons.lang3.tuple.Pair;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import javax.vecmath.Matrix4f;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Custom baked model that stores a list of texture names and sets the lighting to full brightness for any quads
|
||||
* with one of those textures. Most of this code was copied and adapted from refined storage, which is licensed under
|
||||
* the MIT license. https://github.com/raoulvdberge/refinedstorage<br>
|
||||
* <br>
|
||||
* <i>N.B. This doesn't cover item models, and all of the code/model solutions to this that I have tried haven't worked.
|
||||
* However, I noticed that the shade tag seems to work perfectly for items, so instead I've simply duplicated the block
|
||||
* models into the item models folder and add {@code "shade":false} where appropriate. (If it works, it works, right?)</i>
|
||||
*
|
||||
* @author Electroblob
|
||||
* @author raoulvdberge
|
||||
*/
|
||||
public class BakedModelGlowingOverlay implements IBakedModel {
|
||||
|
||||
// Something something something cache, says Forge
|
||||
// This breaks randomised block models (because it's cached, duh...) but simply including the rand parameter will
|
||||
// completely defeat the point of the cache so I need some way of storing the randomised model variants... hmmm...
|
||||
// See WeightedBakedModel for more on randomisation (it's pretty similar to my 1.7 implementation from years ago)
|
||||
|
||||
// private class CacheKey {
|
||||
//
|
||||
// private IBakedModel base;
|
||||
// private String suffix;
|
||||
// private IBlockState state;
|
||||
// private EnumFacing side;
|
||||
//
|
||||
// public CacheKey(IBakedModel base, String suffix, IBlockState state, EnumFacing side){
|
||||
// this.base = base;
|
||||
// this.suffix = suffix;
|
||||
// this.state = state;
|
||||
// this.side = side;
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public boolean equals(Object o){
|
||||
//
|
||||
// if(this == o) return true;
|
||||
// if(o == null || getClass() != o.getClass()) return false;
|
||||
//
|
||||
// CacheKey cacheKey = (CacheKey)o;
|
||||
//
|
||||
// if(cacheKey.side != side) return false;
|
||||
// if(!state.equals(cacheKey.state)) return false;
|
||||
//
|
||||
// return true;
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public int hashCode() {
|
||||
// return state.hashCode() + (31 * (side != null ? side.hashCode() : 0));
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// private static final LoadingCache<CacheKey, List<BakedQuad>> CACHE = CacheBuilder.newBuilder().build(new CacheLoader<CacheKey, List<BakedQuad>>() {
|
||||
// @Override
|
||||
// public List<BakedQuad> load(CacheKey key) {
|
||||
// return transformQuads(key.base.getQuads(key.state, key.side, 0), key.suffix);
|
||||
// }
|
||||
// });
|
||||
|
||||
private final IBakedModel delegate;
|
||||
private String suffix;
|
||||
|
||||
public BakedModelGlowingOverlay(IBakedModel delegate, String suffix){
|
||||
this.delegate = delegate;
|
||||
this.suffix = suffix;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<BakedQuad> getQuads(@Nullable IBlockState state, @Nullable EnumFacing side, long rand){
|
||||
if(state == null) return delegate.getQuads(state, side, rand);
|
||||
return transformQuads(delegate.getQuads(state, side, rand), suffix);
|
||||
//return CACHE.getUnchecked(new CacheKey(delegate, suffix, state instanceof IExtendedBlockState ? ((IExtendedBlockState) state).getClean() : state, side));
|
||||
}
|
||||
|
||||
// I would write these myself but I'd end up with almost the exact same thing anyway
|
||||
// They replace the quads from the original (delegate) model with full-brightness quads if their texture has the given suffix
|
||||
|
||||
private static List<BakedQuad> transformQuads(List<BakedQuad> oldQuads, String suffix){
|
||||
|
||||
List<BakedQuad> quads = new ArrayList<>(oldQuads);
|
||||
|
||||
for(int i = 0; i < quads.size(); ++i){
|
||||
BakedQuad quad = quads.get(i);
|
||||
|
||||
if(quad.getSprite().getIconName().endsWith(suffix)){
|
||||
quads.set(i, transformQuad(quad, 0.007F)); // What's the significance of 0.007?
|
||||
}
|
||||
}
|
||||
|
||||
return quads;
|
||||
}
|
||||
|
||||
private static BakedQuad transformQuad(BakedQuad quad, float light){
|
||||
|
||||
if(isLightMapDisabled()){
|
||||
return quad;
|
||||
}
|
||||
|
||||
VertexFormat newFormat = getFormatWithLightMap(quad.getFormat());
|
||||
|
||||
UnpackedBakedQuad.Builder builder = new UnpackedBakedQuad.Builder(newFormat);
|
||||
|
||||
VertexLighterFlat trans = new VertexLighterFlat(Minecraft.getMinecraft().getBlockColors()) {
|
||||
@Override
|
||||
protected void updateLightmap(float[] normal, float[] lightmap, float x, float y, float z){
|
||||
lightmap[0] = light;
|
||||
lightmap[1] = light;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setQuadTint(int tint){
|
||||
// NO OP
|
||||
}
|
||||
};
|
||||
|
||||
trans.setParent(builder);
|
||||
|
||||
quad.pipe(trans);
|
||||
|
||||
builder.setQuadTint(quad.getTintIndex());
|
||||
builder.setQuadOrientation(quad.getFace());
|
||||
builder.setTexture(quad.getSprite());
|
||||
builder.setApplyDiffuseLighting(false);
|
||||
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAmbientOcclusion(){
|
||||
return delegate.isAmbientOcclusion();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isGui3d(){
|
||||
return delegate.isGui3d();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isBuiltInRenderer(){
|
||||
return delegate.isBuiltInRenderer();
|
||||
}
|
||||
|
||||
@Override
|
||||
public TextureAtlasSprite getParticleTexture(){
|
||||
return delegate.getParticleTexture();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemCameraTransforms getItemCameraTransforms(){
|
||||
return delegate.getItemCameraTransforms();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemOverrideList getOverrides(){
|
||||
return delegate.getOverrides();//BakedModelItemOverride.instance;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAmbientOcclusion(IBlockState state){
|
||||
return delegate.isAmbientOcclusion(state);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Pair<? extends IBakedModel, Matrix4f> handlePerspective(ItemCameraTransforms.TransformType cameraTransformType){
|
||||
return delegate.handlePerspective(cameraTransformType);
|
||||
}
|
||||
|
||||
// Utilities
|
||||
|
||||
private static boolean isLightMapDisabled(){
|
||||
return FMLClientHandler.instance().hasOptifine() || !ForgeModContainer.forgeLightPipelineEnabled;
|
||||
}
|
||||
|
||||
private static final VertexFormat ITEM_FORMAT_WITH_LIGHTMAP = new VertexFormat(DefaultVertexFormats.ITEM).addElement(DefaultVertexFormats.TEX_2S);
|
||||
|
||||
private static VertexFormat getFormatWithLightMap(VertexFormat format){
|
||||
|
||||
if(isLightMapDisabled()){
|
||||
return format;
|
||||
}
|
||||
|
||||
if(format == DefaultVertexFormats.BLOCK){
|
||||
return DefaultVertexFormats.BLOCK;
|
||||
}else if(format == DefaultVertexFormats.ITEM){
|
||||
return ITEM_FORMAT_WITH_LIGHTMAP;
|
||||
}else if(!format.hasUvOffset(1)){
|
||||
VertexFormat result = new VertexFormat(format);
|
||||
result.addElement(DefaultVertexFormats.TEX_2S);
|
||||
return result;
|
||||
}
|
||||
|
||||
return format;
|
||||
}
|
||||
|
||||
// Bit of a weird way of doing things if you ask me, but as far as I can tell it's how you're supposed to do it
|
||||
// public static final class BakedModelItemOverride extends ItemOverrideList {
|
||||
//
|
||||
// // This class doesn't contain any data of its own so it can be a singleton
|
||||
// public static final BakedModelItemOverride instance = new BakedModelItemOverride();
|
||||
//
|
||||
// private BakedModelItemOverride(){
|
||||
// super(ImmutableList.of()); // We're not using the list functionality
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public IBakedModel handleItemState(IBakedModel originalModel, ItemStack stack, @Nullable World world, @Nullable EntityLivingBase entity){
|
||||
// return new BakedModelGlowingOverlay(originalModel, "overlay"); // Bish bash bosh
|
||||
// }
|
||||
// }
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
package electroblob.wizardry.client.model;
|
||||
|
||||
import net.minecraft.client.model.ModelBiped;
|
||||
import net.minecraft.entity.Entity;
|
||||
import net.minecraft.entity.item.EntityArmorStand;
|
||||
|
||||
/**
|
||||
* Fixes custom armour models 'breathing' on the stand and rotates the helmet properly.
|
||||
* @author Shadows-of-Fire
|
||||
* @since Wizardry 4.1.2
|
||||
*/
|
||||
public class ModelArmourFixer extends ModelBiped {
|
||||
|
||||
public ModelArmourFixer(float modelSize, float rotationYOffset, int textureWidth, int textureHeight) {
|
||||
super(modelSize, rotationYOffset, textureWidth, textureHeight);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setRotationAngles(float limbSwing, float limbSwingAmount, float ageInTicks, float netHeadYaw, float headPitch, float scaleFactor, Entity entityIn) {
|
||||
// [VanillaCopy] ModelArmorStandArmor
|
||||
// this prevents helmets from always facing south, and the armor "breathing" on the stand
|
||||
if (entityIn instanceof EntityArmorStand) {
|
||||
EntityArmorStand entityarmorstand = (EntityArmorStand) entityIn;
|
||||
this.bipedHead.rotateAngleX = 0.017453292F * entityarmorstand.getHeadRotation().getX();
|
||||
this.bipedHead.rotateAngleY = 0.017453292F * entityarmorstand.getHeadRotation().getY();
|
||||
this.bipedHead.rotateAngleZ = 0.017453292F * entityarmorstand.getHeadRotation().getZ();
|
||||
this.bipedHead.setRotationPoint(0.0F, 1.0F, 0.0F);
|
||||
this.bipedBody.rotateAngleX = 0.017453292F * entityarmorstand.getBodyRotation().getX();
|
||||
this.bipedBody.rotateAngleY = 0.017453292F * entityarmorstand.getBodyRotation().getY();
|
||||
this.bipedBody.rotateAngleZ = 0.017453292F * entityarmorstand.getBodyRotation().getZ();
|
||||
this.bipedLeftArm.rotateAngleX = 0.017453292F * entityarmorstand.getLeftArmRotation().getX();
|
||||
this.bipedLeftArm.rotateAngleY = 0.017453292F * entityarmorstand.getLeftArmRotation().getY();
|
||||
this.bipedLeftArm.rotateAngleZ = 0.017453292F * entityarmorstand.getLeftArmRotation().getZ();
|
||||
this.bipedRightArm.rotateAngleX = 0.017453292F * entityarmorstand.getRightArmRotation().getX();
|
||||
this.bipedRightArm.rotateAngleY = 0.017453292F * entityarmorstand.getRightArmRotation().getY();
|
||||
this.bipedRightArm.rotateAngleZ = 0.017453292F * entityarmorstand.getRightArmRotation().getZ();
|
||||
this.bipedLeftLeg.rotateAngleX = 0.017453292F * entityarmorstand.getLeftLegRotation().getX();
|
||||
this.bipedLeftLeg.rotateAngleY = 0.017453292F * entityarmorstand.getLeftLegRotation().getY();
|
||||
this.bipedLeftLeg.rotateAngleZ = 0.017453292F * entityarmorstand.getLeftLegRotation().getZ();
|
||||
this.bipedLeftLeg.setRotationPoint(1.9F, 11.0F, 0.0F);
|
||||
this.bipedRightLeg.rotateAngleX = 0.017453292F * entityarmorstand.getRightLegRotation().getX();
|
||||
this.bipedRightLeg.rotateAngleY = 0.017453292F * entityarmorstand.getRightLegRotation().getY();
|
||||
this.bipedRightLeg.rotateAngleZ = 0.017453292F * entityarmorstand.getRightLegRotation().getZ();
|
||||
this.bipedRightLeg.setRotationPoint(-1.9F, 11.0F, 0.0F);
|
||||
copyModelAngles(this.bipedHead, this.bipedHeadwear);
|
||||
} else super.setRotationAngles(limbSwing, limbSwingAmount, ageInTicks, netHeadYaw, headPitch, scaleFactor, entityIn);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -5,64 +5,71 @@ import net.minecraft.client.model.ModelRenderer;
|
||||
import net.minecraft.entity.Entity;
|
||||
|
||||
public class ModelHammer extends ModelBase {
|
||||
ModelRenderer Shape1;
|
||||
ModelRenderer Shape2;
|
||||
ModelRenderer Shape3;
|
||||
ModelRenderer Shape4;
|
||||
ModelRenderer Shape5;
|
||||
ModelRenderer Shape6;
|
||||
|
||||
ModelRenderer hammerHead;
|
||||
ModelRenderer handle;
|
||||
ModelRenderer handleEnd;
|
||||
ModelRenderer handleBase;
|
||||
ModelRenderer ring1;
|
||||
ModelRenderer ring2;
|
||||
|
||||
public ModelHammer(){
|
||||
|
||||
textureWidth = 64;
|
||||
textureHeight = 64;
|
||||
|
||||
Shape1 = new ModelRenderer(this, 0, 0);
|
||||
Shape1.addBox(0F, 0F, 0F, 20, 12, 12);
|
||||
Shape1.setRotationPoint(-10F, 12F, -6F);
|
||||
Shape1.setTextureSize(64, 64);
|
||||
Shape1.mirror = true;
|
||||
setRotation(Shape1, 0F, 0F, 0F);
|
||||
Shape2 = new ModelRenderer(this, 0, 24);
|
||||
Shape2.addBox(0F, 0F, 0F, 4, 14, 4);
|
||||
Shape2.setRotationPoint(-2F, -2F, -2F);
|
||||
Shape2.setTextureSize(64, 64);
|
||||
Shape2.mirror = true;
|
||||
setRotation(Shape2, 0F, 0F, 0F);
|
||||
Shape3 = new ModelRenderer(this, 0, 49);
|
||||
Shape3.addBox(0F, 0F, 0F, 5, 5, 5);
|
||||
Shape3.setRotationPoint(-2.5F, -7F, -2.5F);
|
||||
Shape3.setTextureSize(64, 64);
|
||||
Shape3.mirror = true;
|
||||
setRotation(Shape3, 0F, 0F, 0F);
|
||||
Shape4 = new ModelRenderer(this, 0, 42);
|
||||
Shape4.addBox(0F, 0F, 0F, 5, 2, 5);
|
||||
Shape4.setRotationPoint(-2.5F, 10F, -2.5F);
|
||||
Shape4.setTextureSize(64, 64);
|
||||
Shape4.mirror = true;
|
||||
setRotation(Shape4, 0F, 0F, 0F);
|
||||
Shape5 = new ModelRenderer(this, 20, 24);
|
||||
Shape5.addBox(0F, 0F, 0F, 2, 14, 14);
|
||||
Shape5.setRotationPoint(-8F, 11F, -7F);
|
||||
Shape5.setTextureSize(64, 64);
|
||||
Shape5.mirror = true;
|
||||
setRotation(Shape5, 0F, 0F, 0F);
|
||||
Shape6 = new ModelRenderer(this, 20, 24);
|
||||
Shape6.addBox(0F, 0F, 0F, 2, 14, 14);
|
||||
Shape6.setRotationPoint(6F, 11F, -7F);
|
||||
Shape6.setTextureSize(64, 64);
|
||||
Shape6.mirror = true;
|
||||
setRotation(Shape6, 0F, 0F, 0F);
|
||||
hammerHead = new ModelRenderer(this, 0, 0);
|
||||
hammerHead.addBox(0F, 0F, 0F, 20, 12, 12);
|
||||
hammerHead.setRotationPoint(-10F, 12F, -6F);
|
||||
hammerHead.setTextureSize(64, 64);
|
||||
hammerHead.mirror = true;
|
||||
setRotation(hammerHead, 0F, 0F, 0F);
|
||||
|
||||
handle = new ModelRenderer(this, 0, 24);
|
||||
handle.addBox(0F, 0F, 0F, 4, 14, 4);
|
||||
handle.setRotationPoint(-2F, -2F, -2F);
|
||||
handle.setTextureSize(64, 64);
|
||||
handle.mirror = true;
|
||||
setRotation(handle, 0F, 0F, 0F);
|
||||
|
||||
handleEnd = new ModelRenderer(this, 0, 49);
|
||||
handleEnd.addBox(0F, 0F, 0F, 5, 5, 5);
|
||||
handleEnd.setRotationPoint(-2.5F, -7F, -2.5F);
|
||||
handleEnd.setTextureSize(64, 64);
|
||||
handleEnd.mirror = true;
|
||||
setRotation(handleEnd, 0F, 0F, 0F);
|
||||
|
||||
handleBase = new ModelRenderer(this, 0, 42);
|
||||
handleBase.addBox(0F, 0F, 0F, 5, 2, 5);
|
||||
handleBase.setRotationPoint(-2.5F, 10F, -2.5F);
|
||||
handleBase.setTextureSize(64, 64);
|
||||
handleBase.mirror = true;
|
||||
setRotation(handleBase, 0F, 0F, 0F);
|
||||
|
||||
ring1 = new ModelRenderer(this, 20, 24);
|
||||
ring1.addBox(0F, 0F, 0F, 2, 14, 14);
|
||||
ring1.setRotationPoint(-8F, 11F, -7F);
|
||||
ring1.setTextureSize(64, 64);
|
||||
ring1.mirror = true;
|
||||
setRotation(ring1, 0F, 0F, 0F);
|
||||
|
||||
ring2 = new ModelRenderer(this, 20, 24);
|
||||
ring2.addBox(0F, 0F, 0F, 2, 14, 14);
|
||||
ring2.setRotationPoint(6F, 11F, -7F);
|
||||
ring2.setTextureSize(64, 64);
|
||||
ring2.mirror = true;
|
||||
setRotation(ring2, 0F, 0F, 0F);
|
||||
}
|
||||
|
||||
public void render(Entity entity, float f, float f1, float f2, float f3, float f4, float f5){
|
||||
super.render(entity, f, f1, f2, f3, f4, f5);
|
||||
setRotationAngles(f, f1, f2, f3, f4, f5, entity);
|
||||
Shape1.render(f5);
|
||||
Shape2.render(f5);
|
||||
Shape3.render(f5);
|
||||
Shape4.render(f5);
|
||||
Shape5.render(f5);
|
||||
Shape6.render(f5);
|
||||
hammerHead.render(f5);
|
||||
handle.render(f5);
|
||||
handleEnd.render(f5);
|
||||
handleBase.render(f5);
|
||||
ring1.render(f5);
|
||||
ring2.render(f5);
|
||||
}
|
||||
|
||||
private void setRotation(ModelRenderer model, float x, float y, float z){
|
||||
|
||||
@@ -1,18 +1,16 @@
|
||||
package electroblob.wizardry.client.model;
|
||||
|
||||
import javax.vecmath.Matrix4f;
|
||||
import javax.vecmath.Vector3f;
|
||||
|
||||
import electroblob.wizardry.entity.living.EntityIceGiant;
|
||||
import net.minecraft.client.model.ModelBase;
|
||||
import net.minecraft.client.model.ModelRenderer;
|
||||
import net.minecraft.entity.Entity;
|
||||
import net.minecraft.entity.EntityLivingBase;
|
||||
import net.minecraft.util.math.MathHelper;
|
||||
import net.minecraftforge.fml.relauncher.Side;
|
||||
import net.minecraftforge.fml.relauncher.SideOnly;
|
||||
|
||||
@SideOnly(Side.CLIENT)
|
||||
import javax.vecmath.Matrix4f;
|
||||
import javax.vecmath.Vector3f;
|
||||
|
||||
//@SideOnly(Side.CLIENT)
|
||||
public class ModelIceGiant extends ModelBase {
|
||||
/** The head model for the iron golem. */
|
||||
public ModelRenderer iceGiantHead;
|
||||
|
||||
@@ -97,9 +97,9 @@ public class ModelWizard extends ModelBiped {
|
||||
setRotation(Shape13, 0F, 0F, 0F);
|
||||
|
||||
// Makes head bits move with head
|
||||
// bipedHead.addChild(Shape5);
|
||||
// bipedHead.addChild(hatSegment4);
|
||||
bipedHead.addChild(beard);
|
||||
// bipedHead.addChild(Shape7);
|
||||
// bipedHead.addChild(hatSegment6);
|
||||
// bipedHead.addChild(Shape8);
|
||||
// bipedHead.addChild(Shape9);
|
||||
// bipedHead.addChild(Shape10);
|
||||
@@ -116,8 +116,8 @@ public class ModelWizard extends ModelBiped {
|
||||
/* public void render(Entity entity, float f, float f1, float f2, float f3, float f4, float f5) {
|
||||
* super.render(entity, f, f1, f2, f3, f4, f5); setRotationAngles(f, f1, f2, f3, f4, f5, entity);
|
||||
* bipedRightLeg.render(f5); bipedLeftLeg.render(f5); bipedBody.render(f5); bipedLeftArm.render(f5);
|
||||
* bipedRightArm.render(f5); bipedHead.render(f5); Shape5.render(f5); Shape8.render(f5); Shape9.render(f5);
|
||||
* Shape10.render(f5); Shape7.render(f5); Shape11.render(f5); Shape12.render(f5); Shape6.render(f5);
|
||||
* bipedRightArm.render(f5); bipedHead.render(f5); hatSegment4.render(f5); Shape8.render(f5); Shape9.render(f5);
|
||||
* Shape10.render(f5); hatSegment6.render(f5); Shape11.render(f5); Shape12.render(f5); hatSegment5.render(f5);
|
||||
* Shape13.render(f5); } */
|
||||
private void setRotation(ModelRenderer model, float x, float y, float z){
|
||||
model.rotateAngleX = x;
|
||||
|
||||
@@ -1,100 +1,110 @@
|
||||
package electroblob.wizardry.client.model;
|
||||
|
||||
import net.minecraft.client.model.ModelBiped;
|
||||
import electroblob.wizardry.block.BlockStatue;
|
||||
import net.minecraft.client.model.ModelRenderer;
|
||||
import net.minecraft.entity.Entity;
|
||||
|
||||
public class ModelWizardArmour extends ModelBiped {
|
||||
ModelRenderer Shape1;
|
||||
ModelRenderer Shape2;
|
||||
ModelRenderer Shape3;
|
||||
ModelRenderer Shape4;
|
||||
ModelRenderer Shape5;
|
||||
ModelRenderer Shape6;
|
||||
ModelRenderer Shape7;
|
||||
public class ModelWizardArmour extends ModelArmourFixer {
|
||||
|
||||
ModelRenderer hatBrim;
|
||||
ModelRenderer hatSegment1;
|
||||
ModelRenderer hatSegment2;
|
||||
ModelRenderer hatSegment3;
|
||||
ModelRenderer hatSegment4;
|
||||
ModelRenderer hatSegment5;
|
||||
ModelRenderer hatSegment6;
|
||||
ModelRenderer robe;
|
||||
|
||||
public ModelWizardArmour(float scale){
|
||||
public ModelWizardArmour(float delta){
|
||||
|
||||
super(scale, 0, 64, 64);
|
||||
super(delta, 0, 64, 64);
|
||||
|
||||
// This is necessary to stop the head from scaling.
|
||||
this.bipedHead = new ModelRenderer(this, 0, 0);
|
||||
this.bipedHead.addBox(-4.0F, -8.0F, -4.0F, 8, 8, 8, 0.1f);
|
||||
// The hat layer has an offset of 0.5, so 0.6 is about the smallest we can get away with
|
||||
this.bipedHead.addBox(-4.0F, -8.0F, -4.0F, 8, 8, 8, 0.6f);
|
||||
this.bipedHead.setRotationPoint(0.0F, 0.0F + 0, 0.0F);
|
||||
|
||||
Shape1 = new ModelRenderer(this, -16, 32);
|
||||
Shape1.addBox(-8F, -7F, -8F, 16, 0, 16);
|
||||
Shape1.setRotationPoint(0F, 0F, 0F);
|
||||
Shape1.setTextureSize(64, 64);
|
||||
Shape1.mirror = true;
|
||||
setRotation(Shape1, 0F, 0F, 0F);
|
||||
hatBrim = new ModelRenderer(this, 0, 47);
|
||||
// Making the height 1 stops the top and bottom z-fighting when the hat is enchanted
|
||||
hatBrim.addBox(-8F, -6.85F, -8F, 16, 1, 16, 0.6f);
|
||||
hatBrim.setRotationPoint(0F, 0F, 0F);
|
||||
hatBrim.setTextureSize(64, 64);
|
||||
hatBrim.mirror = true;
|
||||
setRotation(hatBrim, 0F, 0F, 0F);
|
||||
|
||||
Shape2 = new ModelRenderer(this, 0, 48);
|
||||
Shape2.addBox(0F, 0F, 0F, 6, 2, 6);
|
||||
Shape2.setRotationPoint(-3F, -10F, -3F);
|
||||
Shape2.setTextureSize(64, 64);
|
||||
Shape2.mirror = true;
|
||||
setRotation(Shape2, -0.1396263F, 0F, 0F);
|
||||
hatSegment1 = new ModelRenderer(this, 0, 32);
|
||||
hatSegment1.addBox(0F, 0F, 0F, 6, 2, 6, 0.2f);
|
||||
hatSegment1.setRotationPoint(-3F, -10.6F, -3F);
|
||||
hatSegment1.setTextureSize(64, 64);
|
||||
hatSegment1.mirror = true;
|
||||
setRotation(hatSegment1, -0.1396263F, 0F, 0F);
|
||||
|
||||
Shape3 = new ModelRenderer(this, 0, 56);
|
||||
Shape3.addBox(0F, 0F, 0F, 5, 2, 5);
|
||||
Shape3.setRotationPoint(-2.5F, -11.53333F, -2F);
|
||||
Shape3.setTextureSize(64, 64);
|
||||
Shape3.mirror = true;
|
||||
setRotation(Shape3, -0.2443461F, 0F, 0F);
|
||||
hatSegment2 = new ModelRenderer(this, 0, 40);
|
||||
hatSegment2.addBox(0F, 0F, 0F, 5, 2, 5, 0.1f);
|
||||
hatSegment2.setRotationPoint(-2.5F, -12.13333F, -2F);
|
||||
hatSegment2.setTextureSize(64, 64);
|
||||
hatSegment2.mirror = true;
|
||||
setRotation(hatSegment2, -0.2443461F, 0F, 0F);
|
||||
|
||||
Shape4 = new ModelRenderer(this, 24, 48);
|
||||
Shape4.addBox(0F, 0F, 0F, 4, 2, 4);
|
||||
Shape4.setRotationPoint(-2F, -13F, -1F);
|
||||
Shape4.setTextureSize(64, 64);
|
||||
Shape4.mirror = true;
|
||||
setRotation(Shape4, -0.4014257F, 0F, 0F);
|
||||
hatSegment3 = new ModelRenderer(this, 24, 32);
|
||||
hatSegment3.addBox(0F, 0F, 0F, 4, 2, 4);
|
||||
hatSegment3.setRotationPoint(-2F, -13.6F, -1F);
|
||||
hatSegment3.setTextureSize(64, 64);
|
||||
hatSegment3.mirror = true;
|
||||
setRotation(hatSegment3, -0.4014257F, 0F, 0F);
|
||||
|
||||
Shape5 = new ModelRenderer(this, 24, 54);
|
||||
Shape5.addBox(0F, 0F, 0F, 3, 2, 3);
|
||||
Shape5.setRotationPoint(-1.5F, -14F, 0F);
|
||||
Shape5.setTextureSize(64, 64);
|
||||
Shape5.mirror = true;
|
||||
setRotation(Shape5, -0.5759587F, 0F, 0F);
|
||||
hatSegment4 = new ModelRenderer(this, 24, 38);
|
||||
hatSegment4.addBox(0F, 0F, 0F, 3, 2, 3);
|
||||
hatSegment4.setRotationPoint(-1.5F, -14.6F, 0F);
|
||||
hatSegment4.setTextureSize(64, 64);
|
||||
hatSegment4.mirror = true;
|
||||
setRotation(hatSegment4, -0.5759587F, 0F, 0F);
|
||||
|
||||
Shape6 = new ModelRenderer(this, 20, 59);
|
||||
Shape6.addBox(0F, 0F, 0F, 2, 2, 2);
|
||||
Shape6.setRotationPoint(-1F, -14F, 0F);
|
||||
Shape6.setTextureSize(64, 64);
|
||||
Shape6.mirror = true;
|
||||
setRotation(Shape6, 0.3316126F, 0F, 0F);
|
||||
hatSegment5 = new ModelRenderer(this, 20, 43);
|
||||
hatSegment5.addBox(0F, 0F, 0F, 2, 2, 2);
|
||||
hatSegment5.setRotationPoint(-1F, -14.6F, 0F);
|
||||
hatSegment5.setTextureSize(64, 64);
|
||||
hatSegment5.mirror = true;
|
||||
setRotation(hatSegment5, 0.3316126F, 0F, 0F);
|
||||
|
||||
Shape7 = new ModelRenderer(this, 28, 59);
|
||||
Shape7.addBox(0F, 0F, 0F, 1, 1, 3);
|
||||
Shape7.setRotationPoint(-0.5F, -14.5F, 2F);
|
||||
Shape7.setTextureSize(64, 64);
|
||||
Shape7.mirror = true;
|
||||
setRotation(Shape7, -0.5585054F, 0F, 0F);
|
||||
hatSegment6 = new ModelRenderer(this, 28, 43);
|
||||
hatSegment6.addBox(0F, 0F, 0F, 1, 1, 3);
|
||||
hatSegment6.setRotationPoint(-0.5F, -15.1F, 2F);
|
||||
hatSegment6.setTextureSize(64, 64);
|
||||
hatSegment6.mirror = true;
|
||||
setRotation(hatSegment6, -0.5585054F, 0F, 0F);
|
||||
|
||||
// The robe is now the body
|
||||
bipedBody = new ModelRenderer(this, 40, 42);
|
||||
bipedBody.addBox(-4F, 0F, -2F, 8, 18, 4, scale);
|
||||
bipedBody = new ModelRenderer(this, 16, 16);
|
||||
bipedBody.addBox(-4F, 0F, -2F, 8, 11, 4, delta);
|
||||
bipedBody.setRotationPoint(0F, 0F, 0F);
|
||||
bipedBody.setTextureSize(64, 64);
|
||||
bipedBody.mirror = true;
|
||||
setRotation(bipedBody, 0F, 0F, 0F);
|
||||
|
||||
robe = new ModelRenderer(this, 40, 32);
|
||||
robe.addBox(-4F, 0F, -2F, 8, 7, 4, delta);
|
||||
robe.setRotationPoint(0F, 12, 0F); // 12.5 accounts for the expansion of each box
|
||||
robe.setTextureSize(64, 64);
|
||||
robe.mirror = true;
|
||||
setRotation(robe, 0F, 0F, 0F);
|
||||
|
||||
// Makes the hat rotate with the head.
|
||||
bipedHead.addChild(Shape1);
|
||||
bipedHead.addChild(Shape2);
|
||||
bipedHead.addChild(Shape3);
|
||||
bipedHead.addChild(Shape4);
|
||||
bipedHead.addChild(Shape5);
|
||||
bipedHead.addChild(Shape6);
|
||||
bipedHead.addChild(Shape7);
|
||||
// Makes the robe move with the body
|
||||
// bipedBody.addChild(robe);
|
||||
bipedHead.addChild(hatBrim);
|
||||
bipedHead.addChild(hatSegment1);
|
||||
bipedHead.addChild(hatSegment2);
|
||||
bipedHead.addChild(hatSegment3);
|
||||
bipedHead.addChild(hatSegment4);
|
||||
bipedHead.addChild(hatSegment5);
|
||||
bipedHead.addChild(hatSegment6);
|
||||
}
|
||||
|
||||
public void render(Entity entity, float f, float f1, float f2, float f3, float f4, float f5){
|
||||
if(entity.isInvisible() && !entity.getEntityData().getBoolean(BlockStatue.PETRIFIED_NBT_KEY)
|
||||
&& !entity.getEntityData().getBoolean(BlockStatue.FROZEN_NBT_KEY)) return;
|
||||
super.render(entity, f, f1, f2, f3, f4, f5);
|
||||
setRotationAngles(f, f1, f2, f3, f4, f5, entity);
|
||||
this.robe.render(f5);
|
||||
}
|
||||
|
||||
private void setRotation(ModelRenderer model, float x, float y, float z){
|
||||
@@ -105,6 +115,20 @@ public class ModelWizardArmour extends ModelBiped {
|
||||
|
||||
public void setRotationAngles(float f, float f1, float f2, float f3, float f4, float f5, Entity entity){
|
||||
super.setRotationAngles(f, f1, f2, f3, f4, f5, entity);
|
||||
this.robe.showModel = this.bipedBody.showModel;
|
||||
if(this.isSneak){
|
||||
//this.robe.rotationPointY = 10.5f;
|
||||
this.robe.rotationPointZ = 4;
|
||||
}else{
|
||||
//this.robe.rotationPointY = 12.5f;
|
||||
this.robe.rotationPointZ = 0;
|
||||
}
|
||||
|
||||
// The bottom part of the robe takes the y rotation from the rest of the robe but the x/z rotation
|
||||
// from the average of the two legs
|
||||
this.robe.rotateAngleX = (this.bipedLeftLeg.rotateAngleX + this.bipedRightLeg.rotateAngleX) / 2f;
|
||||
this.robe.rotateAngleY = this.bipedBody.rotateAngleY;
|
||||
this.robe.rotateAngleZ = (this.bipedLeftLeg.rotateAngleZ + this.bipedRightLeg.rotateAngleZ) / 2f;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,198 +0,0 @@
|
||||
package electroblob.wizardry.client.model;
|
||||
|
||||
import electroblob.wizardry.registry.WizardryBlocks;
|
||||
import electroblob.wizardry.registry.WizardryItems;
|
||||
import net.minecraft.client.renderer.block.model.ModelResourceLocation;
|
||||
import net.minecraft.item.Item;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.util.NonNullList;
|
||||
import net.minecraftforge.client.event.ModelRegistryEvent;
|
||||
import net.minecraftforge.client.model.ModelLoader;
|
||||
import net.minecraftforge.fml.common.Mod;
|
||||
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
|
||||
import net.minecraftforge.fml.relauncher.Side;
|
||||
import net.minecraftforge.fml.relauncher.SideOnly;
|
||||
import net.minecraftforge.oredict.OreDictionary;
|
||||
|
||||
/**
|
||||
* Class responsible for registering all of wizardry's item (and itemblock) models.
|
||||
*
|
||||
* @author Electroblob
|
||||
* @since Wizardry 2.1
|
||||
*/
|
||||
@SideOnly(Side.CLIENT)
|
||||
@Mod.EventBusSubscriber(Side.CLIENT)
|
||||
public final class WizardryItemModels {
|
||||
|
||||
@SubscribeEvent
|
||||
public static void register(ModelRegistryEvent event){
|
||||
|
||||
// ItemBlocks
|
||||
|
||||
registerItemModel(Item.getItemFromBlock(WizardryBlocks.arcane_workbench));
|
||||
registerItemModel(Item.getItemFromBlock(WizardryBlocks.crystal_ore));
|
||||
registerItemModel(Item.getItemFromBlock(WizardryBlocks.crystal_flower));
|
||||
registerItemModel(Item.getItemFromBlock(WizardryBlocks.transportation_stone));
|
||||
registerItemModel(Item.getItemFromBlock(WizardryBlocks.crystal_block));
|
||||
|
||||
// Items
|
||||
|
||||
registerItemModel(WizardryItems.magic_crystal);
|
||||
|
||||
registerItemModel(WizardryItems.magic_wand);
|
||||
registerItemModel(WizardryItems.apprentice_wand);
|
||||
registerItemModel(WizardryItems.advanced_wand);
|
||||
registerItemModel(WizardryItems.master_wand);
|
||||
|
||||
registerItemModel(WizardryItems.spell_book);
|
||||
// Wildcard registered for wizard trades.
|
||||
registerItemModel(WizardryItems.spell_book, OreDictionary.WILDCARD_VALUE, "normal");
|
||||
registerItemModel(WizardryItems.arcane_tome);
|
||||
registerItemModel(WizardryItems.wizard_handbook);
|
||||
|
||||
registerItemModel(WizardryItems.basic_fire_wand);
|
||||
registerItemModel(WizardryItems.basic_ice_wand);
|
||||
registerItemModel(WizardryItems.basic_lightning_wand);
|
||||
registerItemModel(WizardryItems.basic_necromancy_wand);
|
||||
registerItemModel(WizardryItems.basic_earth_wand);
|
||||
registerItemModel(WizardryItems.basic_sorcery_wand);
|
||||
registerItemModel(WizardryItems.basic_healing_wand);
|
||||
|
||||
registerItemModel(WizardryItems.apprentice_fire_wand);
|
||||
registerItemModel(WizardryItems.apprentice_ice_wand);
|
||||
registerItemModel(WizardryItems.apprentice_lightning_wand);
|
||||
registerItemModel(WizardryItems.apprentice_necromancy_wand);
|
||||
registerItemModel(WizardryItems.apprentice_earth_wand);
|
||||
registerItemModel(WizardryItems.apprentice_sorcery_wand);
|
||||
registerItemModel(WizardryItems.apprentice_healing_wand);
|
||||
|
||||
registerItemModel(WizardryItems.advanced_fire_wand);
|
||||
registerItemModel(WizardryItems.advanced_ice_wand);
|
||||
registerItemModel(WizardryItems.advanced_lightning_wand);
|
||||
registerItemModel(WizardryItems.advanced_necromancy_wand);
|
||||
registerItemModel(WizardryItems.advanced_earth_wand);
|
||||
registerItemModel(WizardryItems.advanced_sorcery_wand);
|
||||
registerItemModel(WizardryItems.advanced_healing_wand);
|
||||
|
||||
registerItemModel(WizardryItems.master_fire_wand);
|
||||
registerItemModel(WizardryItems.master_ice_wand);
|
||||
registerItemModel(WizardryItems.master_lightning_wand);
|
||||
registerItemModel(WizardryItems.master_necromancy_wand);
|
||||
registerItemModel(WizardryItems.master_earth_wand);
|
||||
registerItemModel(WizardryItems.master_sorcery_wand);
|
||||
registerItemModel(WizardryItems.master_healing_wand);
|
||||
|
||||
registerItemModel(WizardryItems.spectral_sword);
|
||||
registerItemModel(WizardryItems.spectral_pickaxe);
|
||||
registerItemModel(WizardryItems.spectral_bow);
|
||||
|
||||
registerItemModel(WizardryItems.mana_flask);
|
||||
|
||||
registerItemModel(WizardryItems.storage_upgrade);
|
||||
registerItemModel(WizardryItems.siphon_upgrade);
|
||||
registerItemModel(WizardryItems.condenser_upgrade);
|
||||
registerItemModel(WizardryItems.range_upgrade);
|
||||
registerItemModel(WizardryItems.duration_upgrade);
|
||||
registerItemModel(WizardryItems.cooldown_upgrade);
|
||||
registerItemModel(WizardryItems.blast_upgrade);
|
||||
registerItemModel(WizardryItems.attunement_upgrade);
|
||||
|
||||
registerItemModel(WizardryItems.flaming_axe);
|
||||
registerItemModel(WizardryItems.frost_axe);
|
||||
|
||||
registerItemModel(WizardryItems.firebomb);
|
||||
registerItemModel(WizardryItems.poison_bomb);
|
||||
|
||||
registerItemModel(WizardryItems.blank_scroll);
|
||||
registerItemModel(WizardryItems.scroll);
|
||||
|
||||
registerItemModel(WizardryItems.armour_upgrade);
|
||||
|
||||
registerItemModel(WizardryItems.magic_silk);
|
||||
|
||||
registerItemModel(WizardryItems.wizard_hat);
|
||||
registerItemModel(WizardryItems.wizard_robe);
|
||||
registerItemModel(WizardryItems.wizard_leggings);
|
||||
registerItemModel(WizardryItems.wizard_boots);
|
||||
|
||||
registerItemModel(WizardryItems.wizard_hat_fire);
|
||||
registerItemModel(WizardryItems.wizard_robe_fire);
|
||||
registerItemModel(WizardryItems.wizard_leggings_fire);
|
||||
registerItemModel(WizardryItems.wizard_boots_fire);
|
||||
|
||||
registerItemModel(WizardryItems.wizard_hat_ice);
|
||||
registerItemModel(WizardryItems.wizard_robe_ice);
|
||||
registerItemModel(WizardryItems.wizard_leggings_ice);
|
||||
registerItemModel(WizardryItems.wizard_boots_ice);
|
||||
|
||||
registerItemModel(WizardryItems.wizard_hat_lightning);
|
||||
registerItemModel(WizardryItems.wizard_robe_lightning);
|
||||
registerItemModel(WizardryItems.wizard_leggings_lightning);
|
||||
registerItemModel(WizardryItems.wizard_boots_lightning);
|
||||
|
||||
registerItemModel(WizardryItems.wizard_hat_necromancy);
|
||||
registerItemModel(WizardryItems.wizard_robe_necromancy);
|
||||
registerItemModel(WizardryItems.wizard_leggings_necromancy);
|
||||
registerItemModel(WizardryItems.wizard_boots_necromancy);
|
||||
|
||||
registerItemModel(WizardryItems.wizard_hat_earth);
|
||||
registerItemModel(WizardryItems.wizard_robe_earth);
|
||||
registerItemModel(WizardryItems.wizard_leggings_earth);
|
||||
registerItemModel(WizardryItems.wizard_boots_earth);
|
||||
|
||||
registerItemModel(WizardryItems.wizard_hat_sorcery);
|
||||
registerItemModel(WizardryItems.wizard_robe_sorcery);
|
||||
registerItemModel(WizardryItems.wizard_leggings_sorcery);
|
||||
registerItemModel(WizardryItems.wizard_boots_sorcery);
|
||||
|
||||
registerItemModel(WizardryItems.wizard_hat_healing);
|
||||
registerItemModel(WizardryItems.wizard_robe_healing);
|
||||
registerItemModel(WizardryItems.wizard_leggings_healing);
|
||||
registerItemModel(WizardryItems.wizard_boots_healing);
|
||||
|
||||
registerItemModel(WizardryItems.spectral_helmet);
|
||||
registerItemModel(WizardryItems.spectral_chestplate);
|
||||
registerItemModel(WizardryItems.spectral_leggings);
|
||||
registerItemModel(WizardryItems.spectral_boots);
|
||||
|
||||
registerItemModel(WizardryItems.smoke_bomb);
|
||||
|
||||
registerItemModel(WizardryItems.identification_scroll);
|
||||
}
|
||||
|
||||
// Moved from the proxies
|
||||
|
||||
/**
|
||||
* Registers an item model, using the item's registry name as the model name (this convention makes it easier to
|
||||
* keep track of everything). Variant defaults to "normal". Registers the model for metadata 0 automatically, plus
|
||||
* all the other metadata values that the item can take, as defined in
|
||||
* {@link Item#getSubItems(Item, net.minecraft.creativetab.CreativeTabs, java.util.List)}. The passed in item
|
||||
* <b>must</b> allow null to be passed in for the creative tab parameter in the aforementioned method, or a
|
||||
* {@link NullPointerException} will result.
|
||||
*/
|
||||
private static void registerItemModel(Item item){
|
||||
|
||||
if(item.getHasSubtypes()){
|
||||
NonNullList<ItemStack> items = NonNullList.create();
|
||||
item.getSubItems(item.getCreativeTab(), items); // Client-only method, but we're client-side so this is OK.
|
||||
for(ItemStack stack : items){
|
||||
ModelLoader.setCustomModelResourceLocation(item, stack.getMetadata(),
|
||||
new ModelResourceLocation(item.getRegistryName(), "inventory"));
|
||||
}
|
||||
}
|
||||
// Changing the last parameter from null to "inventory" fixed the item/block model weirdness. No idea why!
|
||||
ModelLoader.setCustomModelResourceLocation(item, 0,
|
||||
new ModelResourceLocation(item.getRegistryName(), "inventory"));
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers an item model for the given metadata, using the item's registry name as the model name (this convention
|
||||
* makes it easier to keep track of everything). This is intended for registering additional metadata values which
|
||||
* aren't displayed in the creative menu, for example the wildcard spell book used in wizard trades.
|
||||
*/
|
||||
private static void registerItemModel(Item item, int metadata, String variant){
|
||||
ModelLoader.setCustomModelResourceLocation(item, metadata,
|
||||
new ModelResourceLocation(item.getRegistryName(), variant));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,343 @@
|
||||
package electroblob.wizardry.client.model;
|
||||
|
||||
import electroblob.wizardry.Wizardry;
|
||||
import electroblob.wizardry.block.BlockCrystal;
|
||||
import electroblob.wizardry.block.BlockPedestal;
|
||||
import electroblob.wizardry.block.BlockRunestone;
|
||||
import electroblob.wizardry.item.IMultiTexturedItem;
|
||||
import electroblob.wizardry.item.ItemBlockMultiTexturedElemental;
|
||||
import electroblob.wizardry.item.ItemCrystal;
|
||||
import electroblob.wizardry.registry.WizardryBlocks;
|
||||
import electroblob.wizardry.registry.WizardryItems;
|
||||
import net.minecraft.client.renderer.block.model.IBakedModel;
|
||||
import net.minecraft.client.renderer.block.model.ModelResourceLocation;
|
||||
import net.minecraft.client.renderer.block.statemap.StateMap;
|
||||
import net.minecraft.creativetab.CreativeTabs;
|
||||
import net.minecraft.item.Item;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.util.NonNullList;
|
||||
import net.minecraftforge.client.event.ModelBakeEvent;
|
||||
import net.minecraftforge.client.event.ModelRegistryEvent;
|
||||
import net.minecraftforge.client.model.ModelLoader;
|
||||
import net.minecraftforge.fml.common.Mod;
|
||||
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
|
||||
import net.minecraftforge.fml.relauncher.Side;
|
||||
import net.minecraftforge.oredict.OreDictionary;
|
||||
|
||||
/**
|
||||
* Class responsible for registering all of wizardry's item and block models.
|
||||
*
|
||||
* @author Electroblob
|
||||
* @since Wizardry 2.1
|
||||
*/
|
||||
//@SideOnly(Side.CLIENT)
|
||||
@Mod.EventBusSubscriber(Side.CLIENT)
|
||||
public final class WizardryModels {
|
||||
|
||||
private WizardryModels(){} // No instances!
|
||||
|
||||
@SubscribeEvent
|
||||
public static void register(ModelRegistryEvent event){
|
||||
|
||||
// ItemBlocks
|
||||
|
||||
registerItemModel(Item.getItemFromBlock(WizardryBlocks.arcane_workbench));
|
||||
registerItemModel(Item.getItemFromBlock(WizardryBlocks.crystal_ore));
|
||||
registerItemModel(Item.getItemFromBlock(WizardryBlocks.crystal_flower));
|
||||
registerItemModel(Item.getItemFromBlock(WizardryBlocks.transportation_stone));
|
||||
|
||||
ModelLoader.setCustomStateMapper(WizardryBlocks.crystal_block, new StateMap.Builder()
|
||||
.withName(BlockCrystal.ELEMENT).withSuffix("_crystal_block").build());
|
||||
// Yay unchecked casting! But we know it's always ok here, and it makes everything much neater.
|
||||
ItemBlockMultiTexturedElemental crystalBlockItem = (ItemBlockMultiTexturedElemental)Item.getItemFromBlock(WizardryBlocks.crystal_block);
|
||||
registerMultiTexturedModel(crystalBlockItem);
|
||||
|
||||
ModelLoader.setCustomStateMapper(WizardryBlocks.runestone, new StateMap.Builder()
|
||||
.withName(BlockRunestone.ELEMENT).withSuffix("_runestone").build());
|
||||
ItemBlockMultiTexturedElemental runestoneItem = (ItemBlockMultiTexturedElemental)Item.getItemFromBlock(WizardryBlocks.runestone);
|
||||
registerMultiTexturedModel(runestoneItem);
|
||||
|
||||
ModelLoader.setCustomStateMapper(WizardryBlocks.runestone_pedestal, new StateMap.Builder()
|
||||
.withName(BlockPedestal.ELEMENT).ignore(BlockPedestal.NATURAL).withSuffix("_runestone_pedestal").build()); // Don't care about NATURAL property
|
||||
ItemBlockMultiTexturedElemental pedestalItem = (ItemBlockMultiTexturedElemental)Item.getItemFromBlock(WizardryBlocks.runestone_pedestal);
|
||||
registerMultiTexturedModel(pedestalItem);
|
||||
|
||||
// Items
|
||||
|
||||
registerMultiTexturedModel((ItemCrystal)WizardryItems.magic_crystal);
|
||||
|
||||
registerItemModel(WizardryItems.magic_wand);
|
||||
registerItemModel(WizardryItems.apprentice_wand);
|
||||
registerItemModel(WizardryItems.advanced_wand);
|
||||
registerItemModel(WizardryItems.master_wand);
|
||||
|
||||
registerItemModel(WizardryItems.spell_book);
|
||||
// Wildcard registered for wizard trades.
|
||||
registerItemModel(WizardryItems.spell_book, OreDictionary.WILDCARD_VALUE, "normal");
|
||||
registerItemModel(WizardryItems.arcane_tome);
|
||||
registerItemModel(WizardryItems.wizard_handbook);
|
||||
|
||||
registerItemModel(WizardryItems.novice_fire_wand);
|
||||
registerItemModel(WizardryItems.novice_ice_wand);
|
||||
registerItemModel(WizardryItems.novice_lightning_wand);
|
||||
registerItemModel(WizardryItems.novice_necromancy_wand);
|
||||
registerItemModel(WizardryItems.novice_earth_wand);
|
||||
registerItemModel(WizardryItems.novice_sorcery_wand);
|
||||
registerItemModel(WizardryItems.novice_healing_wand);
|
||||
|
||||
registerItemModel(WizardryItems.apprentice_fire_wand);
|
||||
registerItemModel(WizardryItems.apprentice_ice_wand);
|
||||
registerItemModel(WizardryItems.apprentice_lightning_wand);
|
||||
registerItemModel(WizardryItems.apprentice_necromancy_wand);
|
||||
registerItemModel(WizardryItems.apprentice_earth_wand);
|
||||
registerItemModel(WizardryItems.apprentice_sorcery_wand);
|
||||
registerItemModel(WizardryItems.apprentice_healing_wand);
|
||||
|
||||
registerItemModel(WizardryItems.advanced_fire_wand);
|
||||
registerItemModel(WizardryItems.advanced_ice_wand);
|
||||
registerItemModel(WizardryItems.advanced_lightning_wand);
|
||||
registerItemModel(WizardryItems.advanced_necromancy_wand);
|
||||
registerItemModel(WizardryItems.advanced_earth_wand);
|
||||
registerItemModel(WizardryItems.advanced_sorcery_wand);
|
||||
registerItemModel(WizardryItems.advanced_healing_wand);
|
||||
|
||||
registerItemModel(WizardryItems.master_fire_wand);
|
||||
registerItemModel(WizardryItems.master_ice_wand);
|
||||
registerItemModel(WizardryItems.master_lightning_wand);
|
||||
registerItemModel(WizardryItems.master_necromancy_wand);
|
||||
registerItemModel(WizardryItems.master_earth_wand);
|
||||
registerItemModel(WizardryItems.master_sorcery_wand);
|
||||
registerItemModel(WizardryItems.master_healing_wand);
|
||||
|
||||
registerItemModel(WizardryItems.spectral_sword);
|
||||
registerItemModel(WizardryItems.spectral_pickaxe);
|
||||
registerItemModel(WizardryItems.spectral_bow);
|
||||
|
||||
registerItemModel(WizardryItems.small_mana_flask);
|
||||
registerItemModel(WizardryItems.medium_mana_flask);
|
||||
registerItemModel(WizardryItems.large_mana_flask);
|
||||
|
||||
registerItemModel(WizardryItems.crystal_shard);
|
||||
registerItemModel(WizardryItems.grand_crystal);
|
||||
|
||||
registerItemModel(WizardryItems.astral_diamond);
|
||||
|
||||
registerItemModel(WizardryItems.purifying_elixir);
|
||||
|
||||
registerItemModel(WizardryItems.storage_upgrade);
|
||||
registerItemModel(WizardryItems.siphon_upgrade);
|
||||
registerItemModel(WizardryItems.condenser_upgrade);
|
||||
registerItemModel(WizardryItems.range_upgrade);
|
||||
registerItemModel(WizardryItems.duration_upgrade);
|
||||
registerItemModel(WizardryItems.cooldown_upgrade);
|
||||
registerItemModel(WizardryItems.blast_upgrade);
|
||||
registerItemModel(WizardryItems.attunement_upgrade);
|
||||
registerItemModel(WizardryItems.melee_upgrade);
|
||||
|
||||
registerItemModel(WizardryItems.flaming_axe);
|
||||
registerItemModel(WizardryItems.frost_axe);
|
||||
|
||||
registerItemModel(WizardryItems.firebomb);
|
||||
registerItemModel(WizardryItems.poison_bomb);
|
||||
registerItemModel(WizardryItems.smoke_bomb);
|
||||
registerItemModel(WizardryItems.spark_bomb);
|
||||
|
||||
registerItemModel(WizardryItems.blank_scroll);
|
||||
registerItemModel(WizardryItems.scroll);
|
||||
registerItemModel(WizardryItems.identification_scroll);
|
||||
|
||||
registerItemModel(WizardryItems.armour_upgrade);
|
||||
|
||||
registerItemModel(WizardryItems.magic_silk);
|
||||
|
||||
registerItemModel(WizardryItems.wizard_hat);
|
||||
registerItemModel(WizardryItems.wizard_robe);
|
||||
registerItemModel(WizardryItems.wizard_leggings);
|
||||
registerItemModel(WizardryItems.wizard_boots);
|
||||
|
||||
registerItemModel(WizardryItems.wizard_hat_fire);
|
||||
registerItemModel(WizardryItems.wizard_robe_fire);
|
||||
registerItemModel(WizardryItems.wizard_leggings_fire);
|
||||
registerItemModel(WizardryItems.wizard_boots_fire);
|
||||
|
||||
registerItemModel(WizardryItems.wizard_hat_ice);
|
||||
registerItemModel(WizardryItems.wizard_robe_ice);
|
||||
registerItemModel(WizardryItems.wizard_leggings_ice);
|
||||
registerItemModel(WizardryItems.wizard_boots_ice);
|
||||
|
||||
registerItemModel(WizardryItems.wizard_hat_lightning);
|
||||
registerItemModel(WizardryItems.wizard_robe_lightning);
|
||||
registerItemModel(WizardryItems.wizard_leggings_lightning);
|
||||
registerItemModel(WizardryItems.wizard_boots_lightning);
|
||||
|
||||
registerItemModel(WizardryItems.wizard_hat_necromancy);
|
||||
registerItemModel(WizardryItems.wizard_robe_necromancy);
|
||||
registerItemModel(WizardryItems.wizard_leggings_necromancy);
|
||||
registerItemModel(WizardryItems.wizard_boots_necromancy);
|
||||
|
||||
registerItemModel(WizardryItems.wizard_hat_earth);
|
||||
registerItemModel(WizardryItems.wizard_robe_earth);
|
||||
registerItemModel(WizardryItems.wizard_leggings_earth);
|
||||
registerItemModel(WizardryItems.wizard_boots_earth);
|
||||
|
||||
registerItemModel(WizardryItems.wizard_hat_sorcery);
|
||||
registerItemModel(WizardryItems.wizard_robe_sorcery);
|
||||
registerItemModel(WizardryItems.wizard_leggings_sorcery);
|
||||
registerItemModel(WizardryItems.wizard_boots_sorcery);
|
||||
|
||||
registerItemModel(WizardryItems.wizard_hat_healing);
|
||||
registerItemModel(WizardryItems.wizard_robe_healing);
|
||||
registerItemModel(WizardryItems.wizard_leggings_healing);
|
||||
registerItemModel(WizardryItems.wizard_boots_healing);
|
||||
|
||||
registerItemModel(WizardryItems.spectral_helmet);
|
||||
registerItemModel(WizardryItems.spectral_chestplate);
|
||||
registerItemModel(WizardryItems.spectral_leggings);
|
||||
registerItemModel(WizardryItems.spectral_boots);
|
||||
|
||||
registerItemModel(WizardryItems.lightning_hammer);
|
||||
|
||||
registerItemModel(WizardryItems.ring_condensing);
|
||||
registerItemModel(WizardryItems.ring_siphoning);
|
||||
registerItemModel(WizardryItems.ring_battlemage);
|
||||
registerItemModel(WizardryItems.ring_combustion);
|
||||
registerItemModel(WizardryItems.ring_fire_melee);
|
||||
registerItemModel(WizardryItems.ring_fire_biome);
|
||||
registerItemModel(WizardryItems.ring_disintegration);
|
||||
registerItemModel(WizardryItems.ring_ice_melee);
|
||||
registerItemModel(WizardryItems.ring_ice_biome);
|
||||
registerItemModel(WizardryItems.ring_arcane_frost);
|
||||
registerItemModel(WizardryItems.ring_shattering);
|
||||
registerItemModel(WizardryItems.ring_lightning_melee);
|
||||
registerItemModel(WizardryItems.ring_storm);
|
||||
registerItemModel(WizardryItems.ring_seeking);
|
||||
registerItemModel(WizardryItems.ring_hammer);
|
||||
registerItemModel(WizardryItems.ring_soulbinding);
|
||||
registerItemModel(WizardryItems.ring_leeching);
|
||||
registerItemModel(WizardryItems.ring_necromancy_melee);
|
||||
registerItemModel(WizardryItems.ring_mind_control);
|
||||
registerItemModel(WizardryItems.ring_poison);
|
||||
registerItemModel(WizardryItems.ring_earth_melee);
|
||||
registerItemModel(WizardryItems.ring_earth_biome);
|
||||
registerItemModel(WizardryItems.ring_full_moon);
|
||||
registerItemModel(WizardryItems.ring_extraction);
|
||||
registerItemModel(WizardryItems.ring_mana_return);
|
||||
registerItemModel(WizardryItems.ring_blockwrangler);
|
||||
registerItemModel(WizardryItems.ring_conjurer);
|
||||
registerItemModel(WizardryItems.ring_defender);
|
||||
registerItemModel(WizardryItems.ring_paladin);
|
||||
registerItemModel(WizardryItems.ring_interdiction);
|
||||
|
||||
registerItemModel(WizardryItems.amulet_arcane_defence);
|
||||
registerItemModel(WizardryItems.amulet_warding);
|
||||
registerItemModel(WizardryItems.amulet_wisdom);
|
||||
registerItemModel(WizardryItems.amulet_fire_protection);
|
||||
registerItemModel(WizardryItems.amulet_fire_cloaking);
|
||||
registerItemModel(WizardryItems.amulet_ice_immunity);
|
||||
registerItemModel(WizardryItems.amulet_ice_protection);
|
||||
registerItemModel(WizardryItems.amulet_potential);
|
||||
registerItemModel(WizardryItems.amulet_channeling);
|
||||
registerItemModel(WizardryItems.amulet_lich);
|
||||
registerItemModel(WizardryItems.amulet_wither_immunity);
|
||||
registerItemModel(WizardryItems.amulet_glide);
|
||||
registerItemModel(WizardryItems.amulet_banishing);
|
||||
registerItemModel(WizardryItems.amulet_anchoring);
|
||||
registerItemModel(WizardryItems.amulet_recovery);
|
||||
registerItemModel(WizardryItems.amulet_transience);
|
||||
registerItemModel(WizardryItems.amulet_resurrection);
|
||||
registerItemModel(WizardryItems.amulet_auto_shield);
|
||||
|
||||
registerItemModel(WizardryItems.charm_haggler);
|
||||
registerItemModel(WizardryItems.charm_experience_tome);
|
||||
registerItemModel(WizardryItems.charm_auto_smelt);
|
||||
registerItemModel(WizardryItems.charm_lava_walking);
|
||||
registerItemModel(WizardryItems.charm_storm);
|
||||
registerItemModel(WizardryItems.charm_minion_health);
|
||||
registerItemModel(WizardryItems.charm_minion_variants);
|
||||
registerItemModel(WizardryItems.charm_flight);
|
||||
registerItemModel(WizardryItems.charm_growth);
|
||||
registerItemModel(WizardryItems.charm_abseiling);
|
||||
registerItemModel(WizardryItems.charm_silk_touch);
|
||||
registerItemModel(WizardryItems.charm_stop_time);
|
||||
registerItemModel(WizardryItems.charm_light);
|
||||
registerItemModel(WizardryItems.charm_transportation);
|
||||
registerItemModel(WizardryItems.charm_feeding);
|
||||
|
||||
}
|
||||
|
||||
@SubscribeEvent
|
||||
public static void bake(ModelBakeEvent event){
|
||||
// MMMmmmm I love the smell of freshly-baked models...
|
||||
// This stuff is the boilerplate for making runestone overlay render with full brightness
|
||||
// See https://www.minecraftforge.net/forum/topic/66005-how-do-i-make-a-tileentityspecialrenderer-solved-with-ibakedmodel/
|
||||
// As usual the Forge documentation is just a description of each class and not an explanation of how to use them
|
||||
// I had to work out where this goes from the refined storage repo linked in the above thread, which is mixed in
|
||||
// with a more extensive registration system (which is super neat, but it's overkill for our purposes)
|
||||
// https://github.com/raoulvdberge/refinedstorage/blob/13d6e7f2b92f41b5009187aa2cbde50dbc72082f/src/main/java/com/raoulvdberge/refinedstorage/proxy/ProxyClient.java#L59
|
||||
|
||||
for(ModelResourceLocation location : event.getModelRegistry().getKeys()){
|
||||
|
||||
if(location.getNamespace().equals(Wizardry.MODID)){
|
||||
|
||||
if(location.getPath().contains("runestone") || location.getPath().contains("runestone_pedestal")){
|
||||
IBakedModel original = event.getModelRegistry().getObject(location);
|
||||
event.getModelRegistry().putObject(location, new BakedModelGlowingOverlay(original, "overlay"));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Moved from the proxies
|
||||
|
||||
/**
|
||||
* Registers an item model, using the item's registry name as the model name (this convention makes it easier to
|
||||
* keep track of everything). Variant defaults to "normal". Registers the model for metadata 0 automatically, plus
|
||||
* all the other metadata values that the item can take, as defined in
|
||||
* {@link Item#getSubItems(CreativeTabs, NonNullList)}. The creative tab supplied
|
||||
* to the aforementioned method will be whichever one the item is in.
|
||||
*/
|
||||
private static void registerItemModel(Item item){
|
||||
|
||||
if(item.getHasSubtypes()){
|
||||
NonNullList<ItemStack> items = NonNullList.create();
|
||||
item.getSubItems(item.getCreativeTab(), items); // Client-only method, but we're client-side so this is OK.
|
||||
for(ItemStack stack : items){
|
||||
ModelLoader.setCustomModelResourceLocation(item, stack.getMetadata(),
|
||||
new ModelResourceLocation(item.getRegistryName(), "inventory"));
|
||||
}
|
||||
}
|
||||
// Changing the last parameter from null to "inventory" fixed the item/block model weirdness. No idea why!
|
||||
ModelLoader.setCustomModelResourceLocation(item, 0,
|
||||
new ModelResourceLocation(item.getRegistryName(), "inventory"));
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers an item model, using the itemstack-sensitive {@link IMultiTexturedItem#getModelName(ItemStack)} as the
|
||||
* model name. This allows items to change their texture based on metadata/NBT. Variant defaults to "normal". Registers the
|
||||
* model for metadata 0 automatically, plus all the other metadata values that the item can take, as defined in
|
||||
* {@link Item#getSubItems(CreativeTabs, NonNullList)}. The creative tab supplied
|
||||
* to the aforementioned method will be whichever one the item is in.
|
||||
*/
|
||||
private static <T extends Item & IMultiTexturedItem> void registerMultiTexturedModel(T item){
|
||||
|
||||
if(item.getHasSubtypes()){
|
||||
NonNullList<ItemStack> items = NonNullList.create();
|
||||
item.getSubItems(item.getCreativeTab(), items);
|
||||
for(ItemStack stack : items){
|
||||
ModelLoader.setCustomModelResourceLocation(item, stack.getMetadata(),
|
||||
new ModelResourceLocation(item.getModelName(stack), "inventory"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers an item model for the given metadata, using the item's registry name as the model name (this convention
|
||||
* makes it easier to keep track of everything). This is intended for registering additional metadata values which
|
||||
* aren't displayed in the creative menu, for example the wildcard spell book used in wizard trades.
|
||||
*/
|
||||
private static void registerItemModel(Item item, int metadata, String variant){
|
||||
ModelLoader.setCustomModelResourceLocation(item, metadata,
|
||||
new ModelResourceLocation(item.getRegistryName(), variant));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,99 @@
|
||||
package electroblob.wizardry.client.particle;
|
||||
|
||||
import net.minecraft.client.renderer.BufferBuilder;
|
||||
import net.minecraft.client.renderer.GlStateManager;
|
||||
import net.minecraft.client.renderer.OpenGlHelper;
|
||||
import net.minecraft.client.renderer.Tessellator;
|
||||
import net.minecraft.client.renderer.vertex.DefaultVertexFormats;
|
||||
import net.minecraft.world.World;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
|
||||
public class ParticleBeam extends ParticleTargeted {
|
||||
|
||||
/** Half the width of the outermost layer. */
|
||||
private static final float THICKNESS = 0.1f;
|
||||
|
||||
public ParticleBeam(World world, double x, double y, double z){
|
||||
super(world, x, y, z); // Does not have a texture!
|
||||
this.setRBGColorF(1, 1, 1);
|
||||
this.setMaxAge(0);
|
||||
this.particleScale = 1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean shouldDisableDepth(){
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getFXLayer(){
|
||||
return 3;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void draw(Tessellator tessellator, double length, float partialTicks){
|
||||
|
||||
float scale = this.particleScale;
|
||||
|
||||
if(this.particleMaxAge > 0){
|
||||
float ageFraction = (particleAge + partialTicks - 1)/particleMaxAge;
|
||||
// Squaring this makes it look smoother than a linear shrinking effect
|
||||
scale = this.particleScale * (1 - ageFraction*ageFraction);
|
||||
}
|
||||
|
||||
GlStateManager.disableLighting();
|
||||
GlStateManager.enableBlend();
|
||||
GlStateManager.disableTexture2D();
|
||||
GlStateManager.blendFunc(GlStateManager.SourceFactor.SRC_ALPHA, GlStateManager.DestFactor.ONE);
|
||||
OpenGlHelper.setLightmapTextureCoords(OpenGlHelper.lightmapTexUnit, 240f, 240f);
|
||||
|
||||
for(int layer=0; layer<3; layer++){
|
||||
drawSegment(tessellator, layer, 0, 0, 0, 0, 0, length, THICKNESS * scale);
|
||||
}
|
||||
|
||||
GlStateManager.enableTexture2D();
|
||||
GlStateManager.enableLighting();
|
||||
GlStateManager.disableBlend();
|
||||
}
|
||||
|
||||
/** Draws the given layer of a segment of the arc, from the point (x1, y1, z1) to the point (x2, y2, z2), with the given thickness. */
|
||||
private void drawSegment(Tessellator tessellator, int layer, double x1, double y1, double z1, double x2, double y2, double z2, float thickness){
|
||||
|
||||
BufferBuilder buffer = tessellator.getBuffer();
|
||||
buffer.begin(GL11.GL_TRIANGLE_STRIP, DefaultVertexFormats.POSITION_COLOR);
|
||||
|
||||
switch(layer){
|
||||
|
||||
case 0:
|
||||
drawShearedBox(buffer, x1, y1, z1, x2, y2, z2, 0.25f*thickness, 1, 1, 1, 1);
|
||||
break;
|
||||
|
||||
case 1:
|
||||
drawShearedBox(buffer, x1, y1, z1, x2, y2, z2, 0.6f*thickness, (particleRed + 1)/2, (particleGreen + 1)/2,
|
||||
(particleBlue + 1)/2, 0.65f);
|
||||
break;
|
||||
|
||||
case 2:
|
||||
drawShearedBox(buffer, x1, y1, z1, x2, y2, z2, thickness, particleRed, particleGreen, particleBlue, 0.3f);
|
||||
break;
|
||||
}
|
||||
|
||||
tessellator.draw();
|
||||
}
|
||||
|
||||
/** Draws a single box for one segment of the arc, from the point (x1, y1, z1) to the point (x2, y2, z2), with given width and colour. */
|
||||
private void drawShearedBox(BufferBuilder buffer, double x1, double y1, double z1, double x2, double y2, double z2, float width, float r, float g, float b, float a){
|
||||
|
||||
buffer.pos(x1-width, y1-width, z1).color(r, g, b, a).endVertex();
|
||||
buffer.pos(x2-width, y2-width, z2).color(r, g, b, a).endVertex();
|
||||
buffer.pos(x1-width, y1+width, z1).color(r, g, b, a).endVertex();
|
||||
buffer.pos(x2-width, y2+width, z2).color(r, g, b, a).endVertex();
|
||||
buffer.pos(x1+width, y1+width, z1).color(r, g, b, a).endVertex();
|
||||
buffer.pos(x2+width, y2+width, z2).color(r, g, b, a).endVertex();
|
||||
buffer.pos(x1+width, y1-width, z1).color(r, g, b, a).endVertex();
|
||||
buffer.pos(x2+width, y2-width, z2).color(r, g, b, a).endVertex();
|
||||
buffer.pos(x1-width, y1-width, z1).color(r, g, b, a).endVertex();
|
||||
buffer.pos(x2-width, y2-width, z2).color(r, g, b, a).endVertex();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,74 +0,0 @@
|
||||
package electroblob.wizardry.client.particle;
|
||||
|
||||
import net.minecraft.world.World;
|
||||
import net.minecraftforge.fml.relauncher.Side;
|
||||
import net.minecraftforge.fml.relauncher.SideOnly;
|
||||
|
||||
@SideOnly(Side.CLIENT)
|
||||
public class ParticleBlizzard extends ParticleSnow {
|
||||
|
||||
private double angle;
|
||||
private double radius;
|
||||
private double speed;
|
||||
|
||||
public ParticleBlizzard(World world, int maxAge, double originX, double originZ, double radius, double yPos){
|
||||
super(world, 0, 0, 0, 0, 0, 0, maxAge);
|
||||
this.angle = this.rand.nextDouble() * Math.PI * 2;
|
||||
double x = originX - Math.cos(angle) * radius;
|
||||
double z = originZ + radius * Math.sin(angle);
|
||||
this.radius = radius;
|
||||
this.setPosition(x, yPos, z);
|
||||
this.prevPosX = x;
|
||||
this.prevPosY = yPos;
|
||||
this.prevPosZ = z;
|
||||
if(rand.nextBoolean()){
|
||||
speed = rand.nextDouble() * 2 + 1;
|
||||
}else{
|
||||
speed = rand.nextDouble() * -2 - 1;
|
||||
}
|
||||
this.multipleParticleScaleBy(1.5f);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void init(){
|
||||
super.init();
|
||||
this.fullBrightness = true;
|
||||
}
|
||||
|
||||
// @Override
|
||||
// public void renderParticle(VertexBuffer buffer, Entity entity, float partialTicks, float rotationX, float
|
||||
// rotationZ, float rotationYZ, float rotationXY, float rotationXZ){
|
||||
// if(this.particleAge < this.particleMaxAge / 3 || (this.particleAge + this.particleMaxAge) / 3 % 2 == 0){
|
||||
// super.renderParticle(buffer, entity, partialTicks, rotationX, rotationZ, rotationYZ, rotationXY, rotationXZ);
|
||||
// }
|
||||
// }
|
||||
|
||||
@Override
|
||||
public void onUpdate(){
|
||||
|
||||
this.prevPosX = this.posX;
|
||||
this.prevPosY = this.posY;
|
||||
this.prevPosZ = this.posZ;
|
||||
|
||||
if(this.particleAge++ >= this.particleMaxAge){
|
||||
this.setExpired();
|
||||
}
|
||||
|
||||
// This is in radians per tick...
|
||||
double omega = Math.signum(speed) * ((Math.PI * 2) / 20 - speed / (20 * radius));
|
||||
|
||||
// v = r times omega; therefore the normalised velocity vector needs to be r times the angle increment / 2 pi.
|
||||
this.angle += omega;
|
||||
|
||||
this.motionY -= 0.04D * (double)this.particleGravity;
|
||||
this.motionZ = radius * omega * Math.cos(angle);
|
||||
this.motionX = radius * omega * Math.sin(angle);
|
||||
this.move(motionX, motionY, motionZ);
|
||||
|
||||
if(this.particleAge > this.particleMaxAge / 2){
|
||||
this.setAlphaF(
|
||||
1.0F - ((float)this.particleAge - (float)(this.particleMaxAge / 2)) / (float)this.particleMaxAge);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,136 @@
|
||||
package electroblob.wizardry.client.particle;
|
||||
|
||||
import electroblob.wizardry.Wizardry;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.renderer.*;
|
||||
import net.minecraft.client.renderer.GlStateManager.DestFactor;
|
||||
import net.minecraft.client.renderer.GlStateManager.SourceFactor;
|
||||
import net.minecraft.client.renderer.vertex.DefaultVertexFormats;
|
||||
import net.minecraft.entity.Entity;
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
import net.minecraft.util.math.MathHelper;
|
||||
import net.minecraft.world.World;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
|
||||
//@SideOnly(Side.CLIENT)
|
||||
public class ParticleBuff extends ParticleWizardry {
|
||||
|
||||
private static final ResourceLocation TEXTURE = new ResourceLocation(Wizardry.MODID, "textures/particle/buff.png");
|
||||
private final boolean mirror;
|
||||
|
||||
public ParticleBuff(World world, double x, double y, double z){
|
||||
super(world, x, y, z);
|
||||
this.setVelocity(0, 0.162, 0); // Approximately what it was before
|
||||
this.mirror = random.nextBoolean();
|
||||
this.setMaxAge(15);
|
||||
this.setGravity(false);
|
||||
this.canCollide = false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean shouldDisableDepth(){
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void onUpdate(){
|
||||
super.onUpdate();
|
||||
if(this.particleAge > this.particleMaxAge/2) this.particleAlpha = 2f - 2f*(float)this.particleAge/(float)this.particleMaxAge;
|
||||
}
|
||||
|
||||
/* There are 4 layers of particles, specified as 0-3 by the method below. - Layer 0 causes the normal particles.png
|
||||
* to be bound to the render engine for normal particles. - Layer 1 causes the block textures to be bound to the
|
||||
* render engine for digging fx and falling fx. - Layer 2 causes the item textures to be bound to the render engine
|
||||
* for tool breaking fx, snowballpoofs, slime particles, etc. - Layer 3 is not used in vanilla minecraft and was
|
||||
* presumably added by forge for exactly this reason. This means no texture is bound by vanilla minecraft, meaning
|
||||
* you are free to do as you wish without possibly overwriting vanilla particles. Mod particles won't be overwritten
|
||||
* anyway since they bind their own textures. It is of course important to bind the texture every time you render a
|
||||
* custom particle, but I don't see how you could do it any other way, since you don't have access to
|
||||
* EffectRenderer. */
|
||||
@Override
|
||||
public int getFXLayer(){
|
||||
// This can only be 0-3 or it will cause an ArrayIndexOutOfBoundsException in EffectRenderer.
|
||||
return 3;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void renderParticle(BufferBuilder buffer, Entity viewer, float partialTicks, float rotationX, float rotationZ,
|
||||
float rotationYZ, float rotationXY, float rotationXZ){
|
||||
|
||||
// Copied from ParticleWizardry, needs to be here since we're not calling super
|
||||
updateEntityLinking(partialTicks);
|
||||
|
||||
GlStateManager.pushMatrix();
|
||||
GlStateManager.pushAttrib();
|
||||
|
||||
GlStateManager.enableBlend();
|
||||
GlStateManager.disableAlpha();
|
||||
GlStateManager.disableCull();
|
||||
GlStateManager.disableLighting();
|
||||
GlStateManager.blendFunc(SourceFactor.SRC_ALPHA, DestFactor.ONE);
|
||||
// Makes the particle colour add to the colour of the texture pixels, rather than the default multiplying
|
||||
GlStateManager.glTexEnvi(GL11.GL_TEXTURE_ENV, GL11.GL_TEXTURE_ENV_MODE, GL11.GL_ADD);
|
||||
|
||||
OpenGlHelper.setLightmapTextureCoords(OpenGlHelper.lightmapTexUnit, 240f, 240f);
|
||||
|
||||
GlStateManager.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_WRAP_S, GL11.GL_REPEAT);
|
||||
GlStateManager.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_WRAP_T, GL11.GL_REPEAT);
|
||||
|
||||
// Does the texture translation wrapping thing (the cool stuff)
|
||||
GlStateManager.matrixMode(GL11.GL_TEXTURE);
|
||||
GlStateManager.loadIdentity();
|
||||
|
||||
GlStateManager.translate((this.particleAge + partialTicks)/(float)this.particleMaxAge * -2, 0, 0);
|
||||
|
||||
GlStateManager.matrixMode(GL11.GL_MODELVIEW);
|
||||
|
||||
RenderHelper.disableStandardItemLighting();
|
||||
|
||||
Minecraft.getMinecraft().getTextureManager().bindTexture(TEXTURE);
|
||||
|
||||
buffer.begin(GL11.GL_TRIANGLE_STRIP, DefaultVertexFormats.POSITION_TEX_COLOR);
|
||||
|
||||
float x = (float)(this.prevPosX + (this.posX - this.prevPosX) * (double)partialTicks - interpPosX);
|
||||
float y = (float)(this.prevPosY + (this.posY - this.prevPosY) * (double)partialTicks - interpPosY);
|
||||
float z = (float)(this.prevPosZ + (this.posZ - this.prevPosZ) * (double)partialTicks - interpPosZ);
|
||||
|
||||
// Increases from 0 to 1 in steps of 0.125 evenly throughout the particle's lifetime
|
||||
float f = 0.875f - 0.125f * MathHelper.floor((float)this.particleAge/(float)this.particleMaxAge * 8 - 0.000001f);
|
||||
float g = f + 0.125f;
|
||||
float hrepeat = 1;
|
||||
float scale = 0.6f;
|
||||
float yScale = 0.7f * scale;
|
||||
float dx = mirror ? -scale : scale;
|
||||
float dz = scale;
|
||||
|
||||
buffer.pos(x-dx, y-yScale, z-dz).tex(0, g).color(particleRed, particleGreen, particleBlue, particleAlpha).endVertex();
|
||||
buffer.pos(x-dx, y+yScale, z-dz).tex(0, f).color(particleRed, particleGreen, particleBlue, particleAlpha).endVertex();
|
||||
buffer.pos(x+dx, y-yScale, z-dz).tex(0.25*hrepeat, g).color(particleRed, particleGreen, particleBlue, particleAlpha).endVertex();
|
||||
buffer.pos(x+dx, y+yScale, z-dz).tex(0.25*hrepeat, f).color(particleRed, particleGreen, particleBlue, particleAlpha).endVertex();
|
||||
buffer.pos(x+dx, y-yScale, z+dz).tex(0.5*hrepeat, g).color(particleRed, particleGreen, particleBlue, particleAlpha).endVertex();
|
||||
buffer.pos(x+dx, y+yScale, z+dz).tex(0.5*hrepeat, f).color(particleRed, particleGreen, particleBlue, particleAlpha).endVertex();
|
||||
buffer.pos(x-dx, y-yScale, z+dz).tex(0.75*hrepeat, g).color(particleRed, particleGreen, particleBlue, particleAlpha).endVertex();
|
||||
buffer.pos(x-dx, y+yScale, z+dz).tex(0.75*hrepeat, f).color(particleRed, particleGreen, particleBlue, particleAlpha).endVertex();
|
||||
buffer.pos(x-dx, y-yScale, z-dz).tex(hrepeat, g).color(particleRed, particleGreen, particleBlue, particleAlpha).endVertex();
|
||||
buffer.pos(x-dx, y+yScale, z-dz).tex(hrepeat, f).color(particleRed, particleGreen, particleBlue, particleAlpha).endVertex();
|
||||
|
||||
Tessellator.getInstance().draw();
|
||||
|
||||
// Undoes the texture transformations
|
||||
GlStateManager.matrixMode(GL11.GL_TEXTURE);
|
||||
GlStateManager.loadIdentity();
|
||||
GlStateManager.matrixMode(GL11.GL_MODELVIEW);
|
||||
|
||||
GlStateManager.disableBlend();
|
||||
GlStateManager.enableAlpha();
|
||||
GlStateManager.enableCull();
|
||||
GlStateManager.enableLighting();
|
||||
// Reverses the colour addition change from before
|
||||
GlStateManager.glTexEnvi(GL11.GL_TEXTURE_ENV, GL11.GL_TEXTURE_ENV_MODE, GL11.GL_MODULATE);
|
||||
|
||||
GlStateManager.popAttrib();
|
||||
GlStateManager.popMatrix();
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,219 +0,0 @@
|
||||
package electroblob.wizardry.client.particle;
|
||||
|
||||
import org.lwjgl.opengl.GL11;
|
||||
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.particle.Particle;
|
||||
import net.minecraft.client.renderer.BufferBuilder;
|
||||
import net.minecraft.client.renderer.GlStateManager;
|
||||
import net.minecraft.client.renderer.OpenGlHelper;
|
||||
import net.minecraft.client.renderer.RenderHelper;
|
||||
import net.minecraft.client.renderer.Tessellator;
|
||||
import net.minecraft.client.renderer.vertex.DefaultVertexFormats;
|
||||
import net.minecraft.entity.Entity;
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
import net.minecraft.world.World;
|
||||
import net.minecraftforge.fml.relauncher.Side;
|
||||
import net.minecraftforge.fml.relauncher.SideOnly;
|
||||
|
||||
/**
|
||||
* Abstract superclass for all particles that use custom textures. This is intended to centralise as much code as
|
||||
* possible; all subclasses need to do is to define the texture to use, how the frames are arranged (and which to
|
||||
* choose), and any properties like gravity and collisions.
|
||||
*
|
||||
* @author Electroblob
|
||||
* @since Wizardry 1.2
|
||||
*/
|
||||
@SideOnly(Side.CLIENT)
|
||||
public abstract class ParticleCustomTexture extends Particle {
|
||||
|
||||
/** True if the particle always renders at full brightness. Defaults to false. */
|
||||
protected boolean fullBrightness = false;
|
||||
|
||||
public ParticleCustomTexture(World world, double x, double y, double z, double vx, double vy, double vz){
|
||||
super(world, x, y, z, vx, vy, vz);
|
||||
this.motionX = vx;
|
||||
this.motionY = vy;
|
||||
this.motionZ = vz;
|
||||
this.init();
|
||||
}
|
||||
|
||||
public ParticleCustomTexture(World world, double x, double y, double z, double vx, double vy, double vz,
|
||||
int maxAge){
|
||||
this(world, x, y, z, vx, vy, vz);
|
||||
this.particleMaxAge = maxAge;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called from both constructors to set constants, avoiding duplicate code. Common fields to set here include:
|
||||
* particleScale, particleGravity, canCollide, fullBrightness and setting the texture index.
|
||||
*/
|
||||
public abstract void init();
|
||||
|
||||
/**
|
||||
* Returns a ResourceLocation for the particle's texture sheet. Do not create a new ResourceLocation in this method,
|
||||
* only return a constant.
|
||||
*/
|
||||
public abstract ResourceLocation getTexture();
|
||||
|
||||
/** Returns how many 'frames' there are in the x direction on the texture. */
|
||||
protected abstract int getXFrames();
|
||||
|
||||
/** Returns how many 'frames' there are in the y direction on the texture. */
|
||||
protected abstract int getYFrames();
|
||||
|
||||
/* There are 4 layers of particles, specified as 0-3 by the method below. - Layer 0 causes the normal particles.png
|
||||
* to be bound to the render engine for normal particles. - Layer 1 causes the block textures to be bound to the
|
||||
* render engine for digging fx and falling fx. - Layer 2 causes the item textures to be bound to the render engine
|
||||
* for tool breaking fx, snowballpoofs, slime particles, etc. - Layer 3 is not used in vanilla minecraft and was
|
||||
* presumably added by forge for exactly this reason. This means no texture is bound by vanilla minecraft, meaning
|
||||
* you are free to do as you wish without possibly overwriting vanilla particles. Mod particles won't be overwritten
|
||||
* anyway since they bind their own textures. It is of course important to bind the texture every time you render a
|
||||
* custom particle, but I don't see how you could do it any other way, since you don't have access to
|
||||
* EffectRenderer. */
|
||||
@Override
|
||||
public int getFXLayer(){
|
||||
// This can only be 0-3 or it will cause an ArrayIndexOutOfBoundsException in EffectRenderer.
|
||||
return 3;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setParticleTextureIndex(int index){
|
||||
this.particleTextureIndexX = index % getXFrames();
|
||||
this.particleTextureIndexY = index / getYFrames();
|
||||
}
|
||||
|
||||
// Overridden to fix the bug with vanilla that makes particles frictionless. (y != y... seriously, Mojang?)
|
||||
// TESTME: Probably no longer necessary.
|
||||
// @Override
|
||||
// public void move(double x, double y, double z){
|
||||
//
|
||||
// double d0 = y;
|
||||
//
|
||||
// if (this.canCollide)
|
||||
// {
|
||||
// List<AxisAlignedBB> list = this.world.getCollisionBoxes((Entity)null, this.getBoundingBox().addCoord(x, y, z));
|
||||
//
|
||||
// for (AxisAlignedBB axisalignedbb : list)
|
||||
// {
|
||||
// y = axisalignedbb.calculateYOffset(this.getBoundingBox(), y);
|
||||
// }
|
||||
//
|
||||
// this.setBoundingBox(this.getBoundingBox().offset(0.0D, y, 0.0D));
|
||||
//
|
||||
// for (AxisAlignedBB axisalignedbb1 : list)
|
||||
// {
|
||||
// x = axisalignedbb1.calculateXOffset(this.getBoundingBox(), x);
|
||||
// }
|
||||
//
|
||||
// this.setBoundingBox(this.getBoundingBox().offset(x, 0.0D, 0.0D));
|
||||
//
|
||||
// for (AxisAlignedBB axisalignedbb2 : list)
|
||||
// {
|
||||
// z = axisalignedbb2.calculateZOffset(this.getBoundingBox(), z);
|
||||
// }
|
||||
//
|
||||
// this.setBoundingBox(this.getBoundingBox().offset(0.0D, 0.0D, z));
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// this.setBoundingBox(this.getBoundingBox().offset(x, y, z));
|
||||
// }
|
||||
//
|
||||
// this.resetPositionToBB();
|
||||
// this.onGround = d0 != y && d0 < 0.0D;
|
||||
//
|
||||
// /* Can never be true! - But this doesn't seem to make any difference anyway.
|
||||
// if (x != x)
|
||||
// {
|
||||
// this.motionX = 0.0D;
|
||||
// }
|
||||
//
|
||||
// if (z != z)
|
||||
// {
|
||||
// this.motionZ = 0.0D;
|
||||
// }
|
||||
// */
|
||||
// }
|
||||
|
||||
// Overridden to bind the new texture. I think this can be done with TextureAtlasSprite, but this works as it is
|
||||
// so I'm not changing it for the time being.
|
||||
@Override
|
||||
public void renderParticle(BufferBuilder buffer, Entity viewer, float partialTicks, float rotationX, float rotationZ,
|
||||
float rotationYZ, float rotationXY, float rotationXZ){
|
||||
|
||||
GlStateManager.pushMatrix();
|
||||
GlStateManager.pushAttrib();
|
||||
|
||||
this.applyGLStateChanges();
|
||||
|
||||
// This stuff does the shading. It vanilla does this later on for each point, but this also seems to work.
|
||||
int brightness = this.getBrightnessForRender(partialTicks);
|
||||
int lightmapX = brightness % 65536;
|
||||
int lightmapY = brightness / 65536;
|
||||
OpenGlHelper.setLightmapTextureCoords(OpenGlHelper.lightmapTexUnit, (float)lightmapX / 1.0F,
|
||||
(float)lightmapY / 1.0F);
|
||||
|
||||
RenderHelper.disableStandardItemLighting();
|
||||
|
||||
Minecraft.getMinecraft().getTextureManager().bindTexture(getTexture());
|
||||
|
||||
buffer.begin(GL11.GL_QUADS, DefaultVertexFormats.POSITION_TEX_COLOR);
|
||||
|
||||
float u1 = (float)this.particleTextureIndexX / (float)getXFrames();
|
||||
float u2 = u1 + 1.0f / getXFrames();
|
||||
float v1 = (float)this.particleTextureIndexY / (float)getYFrames();
|
||||
float v2 = v1 + 1.0f / getYFrames();
|
||||
float scale = 0.1F * this.particleScale;
|
||||
|
||||
// I'm pretty sure these were always static.
|
||||
Particle.interpPosX = viewer.lastTickPosX + (viewer.posX - viewer.lastTickPosX) * (double)partialTicks;
|
||||
Particle.interpPosY = viewer.lastTickPosY + (viewer.posY - viewer.lastTickPosY) * (double)partialTicks;
|
||||
Particle.interpPosZ = viewer.lastTickPosZ + (viewer.posZ - viewer.lastTickPosZ) * (double)partialTicks;
|
||||
|
||||
float x = (float)(this.prevPosX + (this.posX - this.prevPosX) * (double)partialTicks - interpPosX);
|
||||
float y = (float)(this.prevPosY + (this.posY - this.prevPosY) * (double)partialTicks - interpPosY);
|
||||
float z = (float)(this.prevPosZ + (this.posZ - this.prevPosZ) * (double)partialTicks - interpPosZ);
|
||||
|
||||
buffer.pos((double)(x - rotationX * scale - rotationXY * scale), (double)(y - rotationZ * scale),
|
||||
(double)(z - rotationYZ * scale - rotationXZ * scale)).tex(u2, v2)
|
||||
.color(particleRed, particleGreen, particleBlue, particleAlpha).endVertex();
|
||||
buffer.pos((double)(x - rotationX * scale + rotationXY * scale), (double)(y + rotationZ * scale),
|
||||
(double)(z - rotationYZ * scale + rotationXZ * scale)).tex(u2, v1)
|
||||
.color(particleRed, particleGreen, particleBlue, particleAlpha).endVertex();
|
||||
buffer.pos((double)(x + rotationX * scale + rotationXY * scale), (double)(y + rotationZ * scale),
|
||||
(double)(z + rotationYZ * scale + rotationXZ * scale)).tex(u1, v1)
|
||||
.color(particleRed, particleGreen, particleBlue, particleAlpha).endVertex();
|
||||
buffer.pos((double)(x + rotationX * scale - rotationXY * scale), (double)(y - rotationZ * scale),
|
||||
(double)(z + rotationYZ * scale - rotationXZ * scale)).tex(u1, v2)
|
||||
.color(particleRed, particleGreen, particleBlue, particleAlpha).endVertex();
|
||||
;
|
||||
|
||||
Tessellator.getInstance().draw();
|
||||
|
||||
this.undoGLStateChanges();
|
||||
|
||||
GlStateManager.popAttrib();
|
||||
GlStateManager.popMatrix();
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Override to add any GL state changes, like blending. Does nothing by default. <b>State changes should be done
|
||||
* using GLStateManager, not using GL11 directly</b> (as is the case with all rendering code now).
|
||||
*/
|
||||
public void applyGLStateChanges(){
|
||||
}
|
||||
|
||||
/**
|
||||
* Override to undo any GL state changes, like blending. Does nothing by default. <b>State changes should be done
|
||||
* using GLStateManager, not using GL11 directly</b> (as is the case with all rendering code now).
|
||||
*/
|
||||
public void undoGLStateChanges(){
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getBrightnessForRender(float partialTick){
|
||||
return fullBrightness ? 15728880 : super.getBrightnessForRender(partialTick);
|
||||
}
|
||||
}
|
||||
@@ -1,27 +1,20 @@
|
||||
package electroblob.wizardry.client.particle;
|
||||
|
||||
import net.minecraft.client.particle.Particle;
|
||||
import net.minecraft.client.renderer.BufferBuilder;
|
||||
import net.minecraft.entity.Entity;
|
||||
import net.minecraft.world.World;
|
||||
import net.minecraftforge.fml.relauncher.Side;
|
||||
import net.minecraftforge.fml.relauncher.SideOnly;
|
||||
|
||||
@SideOnly(Side.CLIENT)
|
||||
public class ParticleDarkMagic extends Particle {
|
||||
//@SideOnly(Side.CLIENT)
|
||||
public class ParticleDarkMagic extends ParticleWizardry {
|
||||
|
||||
/** Base spell texture index */
|
||||
private int baseSpellTextureIndex = 128;
|
||||
|
||||
public ParticleDarkMagic(World par1World, double par2, double par4, double par6, double par8, double par10,
|
||||
double par12, float r, float g, float b){
|
||||
super(par1World, par2, par4, par6, par8, par10, par12);
|
||||
public ParticleDarkMagic(World world, double x, double y, double z){
|
||||
super(world, x, y, z);
|
||||
|
||||
this.motionY *= 0.20000000298023224D;
|
||||
|
||||
this.particleRed = r;
|
||||
this.particleGreen = g;
|
||||
this.particleBlue = b;
|
||||
|
||||
this.setRBGColorF(1, 1, 1);
|
||||
this.particleScale *= 0.75F;
|
||||
this.particleMaxAge = (int)(8.0D / (Math.random() * 0.8D + 0.2D));
|
||||
this.canCollide = true;
|
||||
@@ -43,9 +36,7 @@ public class ParticleDarkMagic extends Particle {
|
||||
super.renderParticle(buffer, entity, partialTicks, rotationX, rotationZ, rotationYZ, rotationXY, rotationXZ);
|
||||
}
|
||||
|
||||
/**
|
||||
* Called to update the entity's position/logic.
|
||||
*/
|
||||
@Override
|
||||
public void onUpdate(){
|
||||
this.prevPosX = this.posX;
|
||||
this.prevPosY = this.posY;
|
||||
|
||||
@@ -1,48 +1,31 @@
|
||||
package electroblob.wizardry.client.particle;
|
||||
|
||||
import net.minecraft.client.particle.Particle;
|
||||
import net.minecraft.world.World;
|
||||
import net.minecraftforge.fml.relauncher.Side;
|
||||
import net.minecraftforge.fml.relauncher.SideOnly;
|
||||
|
||||
@SideOnly(Side.CLIENT)
|
||||
public class ParticleDust extends Particle {
|
||||
//@SideOnly(Side.CLIENT)
|
||||
public class ParticleDust extends ParticleWizardry {
|
||||
|
||||
private final boolean shaded;
|
||||
|
||||
public ParticleDust(World par1World, double x, double y, double z, double par8, double par10, double par12, float r,
|
||||
float g, float b, boolean shaded){
|
||||
super(par1World, x, y, z, par8, par10, par12);
|
||||
this.particleRed = r;
|
||||
this.particleGreen = g;
|
||||
this.particleBlue = b;
|
||||
public ParticleDust(World world, double x, double y, double z){
|
||||
super(world, x, y, z);
|
||||
|
||||
this.setParticleTextureIndex(0);
|
||||
this.setSize(0.01F, 0.01F);
|
||||
|
||||
// Defaults
|
||||
this.particleScale *= this.rand.nextFloat() + 0.2F;
|
||||
this.motionX = par8;
|
||||
this.motionY = par10;
|
||||
this.motionZ = par12;
|
||||
this.particleMaxAge = (int)(16.0D / (Math.random() * 0.8D + 0.2D));
|
||||
this.shaded = shaded;
|
||||
this.setRBGColorF(1, 1, 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Called to update the entity's position/logic.
|
||||
*/
|
||||
@Override
|
||||
public void onUpdate(){
|
||||
this.prevPosX = this.posX;
|
||||
this.prevPosY = this.posY;
|
||||
this.prevPosZ = this.posZ;
|
||||
// this.moveEntity(this.motionX, this.motionY, this.motionZ);
|
||||
this.move(this.motionX, this.motionY, this.motionZ);
|
||||
|
||||
if(this.particleMaxAge-- <= 0){
|
||||
this.setExpired();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getBrightnessForRender(float par1){
|
||||
return shaded ? super.getBrightnessForRender(par1) : 15728880;
|
||||
}
|
||||
/* @Override public float getBrightness(float par1) { return shaded ? super.getBrightness(par1) : 1.0F; } */
|
||||
}
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
package electroblob.wizardry.client.particle;
|
||||
|
||||
import net.minecraft.client.renderer.BufferBuilder;
|
||||
import net.minecraft.entity.Entity;
|
||||
import net.minecraft.util.math.MathHelper;
|
||||
import net.minecraft.world.World;
|
||||
|
||||
/**
|
||||
* Copied from ParticleFirework.Overlay; for some reason that class has no public constructors, plus I want to change the
|
||||
* scale and a few other things
|
||||
* @author Electroblob
|
||||
* @since Wizardry 4.2.0
|
||||
*/
|
||||
public class ParticleFlash extends ParticleWizardry {
|
||||
|
||||
public ParticleFlash(World world, double x, double y, double z){
|
||||
super(world, x, y, z);
|
||||
this.setRBGColorF(1, 1, 1);
|
||||
this.particleScale = 0.6f; // 7.1f is the value used in fireworks
|
||||
this.particleMaxAge = 6;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean shouldDisableDepth(){
|
||||
return true; // Well this fixes everything... let's hope it doesn't cause any side-effects!
|
||||
}
|
||||
|
||||
@Override
|
||||
public void drawParticle(BufferBuilder buffer, Entity entityIn, float partialTicks, float rotationX, float rotationZ, float rotationYZ, float rotationXY, float rotationXZ){
|
||||
float f4 = particleScale * MathHelper.sin(((float)this.particleAge + partialTicks - 1.0F)/particleMaxAge * (float)Math.PI);
|
||||
this.setAlphaF(0.6F - ((float)this.particleAge + partialTicks - 1.0F)/particleMaxAge * 0.5F);
|
||||
float f5 = (float)(this.prevPosX + (this.posX - this.prevPosX) * (double)partialTicks - interpPosX);
|
||||
float f6 = (float)(this.prevPosY + (this.posY - this.prevPosY) * (double)partialTicks - interpPosY);
|
||||
float f7 = (float)(this.prevPosZ + (this.posZ - this.prevPosZ) * (double)partialTicks - interpPosZ);
|
||||
int i = this.getBrightnessForRender(partialTicks);
|
||||
int j = i >> 16 & 65535;
|
||||
int k = i & 65535;
|
||||
buffer.pos((double)(f5 - rotationX * f4 - rotationXY * f4), (double)(f6 - rotationZ * f4), (double)(f7 - rotationYZ * f4 - rotationXZ * f4)).tex(0.5D, 0.375D).color(this.particleRed, this.particleGreen, this.particleBlue, this.particleAlpha).lightmap(j, k).endVertex();
|
||||
buffer.pos((double)(f5 - rotationX * f4 + rotationXY * f4), (double)(f6 + rotationZ * f4), (double)(f7 - rotationYZ * f4 + rotationXZ * f4)).tex(0.5D, 0.125D).color(this.particleRed, this.particleGreen, this.particleBlue, this.particleAlpha).lightmap(j, k).endVertex();
|
||||
buffer.pos((double)(f5 + rotationX * f4 + rotationXY * f4), (double)(f6 + rotationZ * f4), (double)(f7 + rotationYZ * f4 + rotationXZ * f4)).tex(0.25D, 0.125D).color(this.particleRed, this.particleGreen, this.particleBlue, this.particleAlpha).lightmap(j, k).endVertex();
|
||||
buffer.pos((double)(f5 + rotationX * f4 - rotationXY * f4), (double)(f6 - rotationZ * f4), (double)(f7 + rotationYZ * f4 - rotationXZ * f4)).tex(0.25D, 0.375D).color(this.particleRed, this.particleGreen, this.particleBlue, this.particleAlpha).lightmap(j, k).endVertex();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getBrightnessForRender(float partialTicks){
|
||||
return 15728880;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,49 +0,0 @@
|
||||
package electroblob.wizardry.client.particle;
|
||||
|
||||
import electroblob.wizardry.Wizardry;
|
||||
import net.minecraft.client.particle.Particle;
|
||||
import net.minecraft.world.World;
|
||||
import net.minecraftforge.fml.relauncher.Side;
|
||||
import net.minecraftforge.fml.relauncher.SideOnly;
|
||||
|
||||
@SideOnly(Side.CLIENT)
|
||||
public class ParticleGiantBubble extends Particle {
|
||||
/**
|
||||
* The name used to identify this particle. Uses the mod id to avoid any possible conflicts (Not that there would be
|
||||
* any, but I may as well.)
|
||||
*/
|
||||
public static final String NAME = Wizardry.MODID + "magicbubble";
|
||||
|
||||
public ParticleGiantBubble(World par1World, double par2, double par4, double par6, double par8, double par10,
|
||||
double par12){
|
||||
super(par1World, par2, par4, par6, par8, par10, par12);
|
||||
this.particleRed = 1.0F;
|
||||
this.particleGreen = 1.0F;
|
||||
this.particleBlue = 1.0F;
|
||||
this.setParticleTextureIndex(32);
|
||||
this.setSize(0.02F, 0.02F);
|
||||
this.particleScale *= this.rand.nextFloat() * 0.6F + 0.2F;
|
||||
this.motionX = par8 * 0.20000000298023224D + (double)((float)(Math.random() * 2.0D - 1.0D) * 0.02F);
|
||||
this.motionY = par10 * 0.20000000298023224D + (double)((float)(Math.random() * 2.0D - 1.0D) * 0.02F);
|
||||
this.motionZ = par12 * 0.20000000298023224D + (double)((float)(Math.random() * 2.0D - 1.0D) * 0.02F);
|
||||
this.particleMaxAge = (int)(8.0D / (Math.random() * 0.8D + 0.2D));
|
||||
}
|
||||
|
||||
/**
|
||||
* Called to update the entity's position/logic.
|
||||
*/
|
||||
public void onUpdate(){
|
||||
this.prevPosX = this.posX;
|
||||
this.prevPosY = this.posY;
|
||||
this.prevPosZ = this.posZ;
|
||||
this.motionY += 0.002D;
|
||||
this.move(this.motionX, this.motionY, this.motionZ);
|
||||
this.motionX *= 0.8500000238418579D;
|
||||
this.motionY *= 0.8500000238418579D;
|
||||
this.motionZ *= 0.8500000238418579D;
|
||||
|
||||
if(this.particleMaxAge-- <= 0){
|
||||
this.setExpired();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,46 +1,35 @@
|
||||
package electroblob.wizardry.client.particle;
|
||||
|
||||
import electroblob.wizardry.Wizardry;
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
import net.minecraft.world.World;
|
||||
import net.minecraftforge.client.event.TextureStitchEvent;
|
||||
import net.minecraftforge.fml.common.Mod;
|
||||
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
|
||||
import net.minecraftforge.fml.relauncher.Side;
|
||||
import net.minecraftforge.fml.relauncher.SideOnly;
|
||||
|
||||
@SideOnly(Side.CLIENT)
|
||||
public class ParticleIce extends ParticleCustomTexture {
|
||||
//@SideOnly(Side.CLIENT)
|
||||
@Mod.EventBusSubscriber(Side.CLIENT)
|
||||
public class ParticleIce extends ParticleWizardry {
|
||||
|
||||
private static final ResourceLocation TEXTURE = new ResourceLocation(Wizardry.MODID,
|
||||
"textures/particle/ice_particles.png");
|
||||
|
||||
public ParticleIce(World world, double x, double y, double z, double vx, double vy, double vz){
|
||||
super(world, x, y, z, vx, vy, vz);
|
||||
}
|
||||
|
||||
public ParticleIce(World world, double x, double y, double z, double vx, double vy, double vz, int maxAge){
|
||||
super(world, x, y, z, vx, vy, vz, maxAge);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void init(){
|
||||
this.setParticleTextureIndex(rand.nextInt(8));
|
||||
this.particleScale *= 0.75f;
|
||||
this.particleGravity = 1;
|
||||
private static final ResourceLocation[] TEXTURES = generateTextures("ice", 8);
|
||||
|
||||
public ParticleIce(World world, double x, double y, double z){
|
||||
|
||||
super(world, x, y, z, TEXTURES[world.rand.nextInt(TEXTURES.length)]);
|
||||
|
||||
this.canCollide = true;
|
||||
this.fullBrightness = true;
|
||||
|
||||
// Defaults
|
||||
this.setRBGColorF(1, 1, 1);
|
||||
this.particleScale *= 0.75f;
|
||||
this.setGravity(true);
|
||||
this.shaded = false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ResourceLocation getTexture(){
|
||||
return TEXTURE;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int getXFrames(){
|
||||
return 4;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int getYFrames(){
|
||||
return 4;
|
||||
|
||||
@SubscribeEvent
|
||||
public static void onTextureStitchEvent(TextureStitchEvent.Pre event){
|
||||
for(ResourceLocation texture : TEXTURES){
|
||||
event.getMap().registerSprite(texture);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,45 +1,46 @@
|
||||
package electroblob.wizardry.client.particle;
|
||||
|
||||
import electroblob.wizardry.Wizardry;
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
import net.minecraft.world.World;
|
||||
import net.minecraftforge.client.event.TextureStitchEvent;
|
||||
import net.minecraftforge.fml.common.Mod;
|
||||
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
|
||||
import net.minecraftforge.fml.relauncher.Side;
|
||||
import net.minecraftforge.fml.relauncher.SideOnly;
|
||||
|
||||
@SideOnly(Side.CLIENT)
|
||||
public class ParticleLeaf extends ParticleCustomTexture {
|
||||
//@SideOnly(Side.CLIENT)
|
||||
@Mod.EventBusSubscriber(Side.CLIENT)
|
||||
public class ParticleLeaf extends ParticleWizardry {
|
||||
|
||||
private static final ResourceLocation TEXTURE = new ResourceLocation(Wizardry.MODID,
|
||||
"textures/particle/leaf_particles.png");
|
||||
private static final ResourceLocation[] TEXTURES = generateTextures("leaf", 16);
|
||||
|
||||
public ParticleLeaf(World world, double x, double y, double z, double vx, double vy, double vz){
|
||||
super(world, x, y, z, vx, vy, vz);
|
||||
}
|
||||
|
||||
public ParticleLeaf(World world, double x, double y, double z, double vx, double vy, double vz, int maxAge){
|
||||
super(world, x, y, z, vx, vy, vz, maxAge);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void init(){
|
||||
this.setParticleTextureIndex(rand.nextInt(16));
|
||||
public ParticleLeaf(World world, double x, double y, double z){
|
||||
|
||||
super(world, x, y, z, TEXTURES[world.rand.nextInt(TEXTURES.length)]);
|
||||
|
||||
this.setVelocity(0, -0.03, 0);
|
||||
this.setMaxAge(10 + rand.nextInt(5));
|
||||
this.particleScale *= 1.4f;
|
||||
this.particleGravity = 0;
|
||||
this.canCollide = true;
|
||||
// Produces a variety of browns and greens
|
||||
this.setRBGColorF(0.1f + 0.3f * random.nextFloat(), 0.5f + 0.3f * random.nextFloat(), 0.1f);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public ResourceLocation getTexture(){
|
||||
return TEXTURE;
|
||||
public void onUpdate(){
|
||||
|
||||
super.onUpdate();
|
||||
|
||||
// Fading
|
||||
if(this.particleAge > this.particleMaxAge / 2){
|
||||
this.setAlphaF(1 - ((float)this.particleAge - (float)(this.particleMaxAge / 2)) / (float)this.particleMaxAge);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int getXFrames(){
|
||||
return 4;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int getYFrames(){
|
||||
return 4;
|
||||
|
||||
@SubscribeEvent
|
||||
public static void onTextureStitchEvent(TextureStitchEvent.Pre event){
|
||||
for(ResourceLocation texture : TEXTURES){
|
||||
event.getMap().registerSprite(texture);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,165 @@
|
||||
package electroblob.wizardry.client.particle;
|
||||
|
||||
import net.minecraft.client.renderer.BufferBuilder;
|
||||
import net.minecraft.client.renderer.GlStateManager;
|
||||
import net.minecraft.client.renderer.OpenGlHelper;
|
||||
import net.minecraft.client.renderer.Tessellator;
|
||||
import net.minecraft.client.renderer.vertex.DefaultVertexFormats;
|
||||
import net.minecraft.world.World;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
|
||||
public class ParticleLightning extends ParticleTargeted {
|
||||
|
||||
/** Half the width of the outermost layer. */
|
||||
private static final float THICKNESS = 0.04f;
|
||||
/** Maximum length of a segment. */
|
||||
private static final double MAX_SEGMENT_LENGTH = 0.6;
|
||||
/** Minimum length of a segment. */
|
||||
private static final double MIN_SEGMENT_LENGTH = 0.2;
|
||||
/** Maximum deviation (in x or y, as drawn before transformations) from the centreline. */
|
||||
private static final double VERTEX_JITTER = 0.15;
|
||||
/** Maximum number of segments a fork can have before ending. */
|
||||
private static final int MAX_FORK_SEGMENTS = 3;
|
||||
/** Probability (as a fraction) that a vertex will have a fork. */
|
||||
private static final float FORK_CHANCE = 0.3f;
|
||||
/** Number of ticks to wait before the arc changes shape again. */
|
||||
private static final int UPDATE_PERIOD = 1;
|
||||
|
||||
public ParticleLightning(World world, double x, double y, double z){
|
||||
super(world, x, y, z); // Does not have a texture!
|
||||
seed = this.rand.nextLong();
|
||||
this.setRBGColorF(0.2f, 0.6f, 1); // Default blue colour
|
||||
this.setMaxAge(3);
|
||||
this.particleScale = 1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean shouldDisableDepth(){
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getFXLayer(){
|
||||
return 3;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void draw(Tessellator tessellator, double length, float partialTicks){
|
||||
|
||||
GlStateManager.disableLighting();
|
||||
GlStateManager.enableBlend();
|
||||
GlStateManager.disableTexture2D();
|
||||
GlStateManager.blendFunc(GlStateManager.SourceFactor.SRC_ALPHA, GlStateManager.DestFactor.ONE);
|
||||
OpenGlHelper.setLightmapTextureCoords(OpenGlHelper.lightmapTexUnit, 240f, 240f);
|
||||
|
||||
// The direction of the arc drawn by the tessellator is always along the z axis and is rotated to the
|
||||
// correct orientation, that way there isn't a ton of trigonometry and the code is way neater.
|
||||
|
||||
boolean freeEnd = this.target == null;
|
||||
|
||||
int numberOfSegments = (int)Math.round(length/MAX_SEGMENT_LENGTH); // Number of segments
|
||||
|
||||
for(int layer=0; layer<3; layer++){
|
||||
|
||||
double px=0, py=0, pz=0;
|
||||
// Creates a random from the arc's seed field + the number of ticks it has existed/the update period.
|
||||
// By using a seed, we can ensure the vertex positions and forks are identical a) for each layer, even
|
||||
// though they are rendered sequentially, and b) across many frames (and ticks, if updateTime > 1).
|
||||
random.setSeed(this.seed + this.particleAge/UPDATE_PERIOD);
|
||||
|
||||
// numberOfSegments-1 because the last segment is handled separately.
|
||||
for(int i=0; i<numberOfSegments-1; i++){
|
||||
|
||||
double px2 = (random.nextDouble()*2-1)*VERTEX_JITTER*particleScale;
|
||||
double py2 = (random.nextDouble()*2-1)*VERTEX_JITTER*particleScale;
|
||||
double pz2 = pz + length/(double)numberOfSegments; // For now they are all the same length
|
||||
|
||||
drawSegment(tessellator, layer, px, py, pz, px2, py2, pz2, THICKNESS*particleScale);
|
||||
|
||||
// Forks
|
||||
if(random.nextFloat() < FORK_CHANCE){
|
||||
|
||||
double px3=px, py3=py, pz3=pz;
|
||||
|
||||
for(int j=0; j<random.nextInt(MAX_FORK_SEGMENTS-1)+1; j++){
|
||||
// Forks set their centreline to the x/y coordinates of the vertex they originate from
|
||||
double px4 = px3 + (random.nextDouble()*2-1)*VERTEX_JITTER*particleScale;
|
||||
double py4 = py3 + (random.nextDouble()*2-1)*VERTEX_JITTER*particleScale;
|
||||
double pz4 = pz3 + MIN_SEGMENT_LENGTH + random.nextDouble()*(MAX_SEGMENT_LENGTH - MIN_SEGMENT_LENGTH);
|
||||
|
||||
drawSegment(tessellator, layer, px3, py3, pz3, px4, py4, pz4, THICKNESS*0.8f*particleScale);
|
||||
|
||||
// Forks of forks
|
||||
if(random.nextFloat() < FORK_CHANCE){
|
||||
|
||||
double px5 = px3 + (random.nextDouble()*2-1)*VERTEX_JITTER*particleScale;
|
||||
double py5 = py3 + (random.nextDouble()*2-1)*VERTEX_JITTER*particleScale;
|
||||
double pz5 = pz3 + MIN_SEGMENT_LENGTH + random.nextDouble()*(MAX_SEGMENT_LENGTH - MIN_SEGMENT_LENGTH);
|
||||
|
||||
drawSegment(tessellator, layer, px3, py3, pz3, px5, py5, pz5, THICKNESS*0.6f*particleScale);
|
||||
}
|
||||
|
||||
px3 = px4;
|
||||
py3 = py4;
|
||||
pz3 = pz4;
|
||||
}
|
||||
}
|
||||
|
||||
px = px2;
|
||||
py = py2;
|
||||
pz = pz2;
|
||||
}
|
||||
|
||||
// Last segment has a specific end position and cannot fork.
|
||||
double px2 = freeEnd ? (random.nextDouble()*2-1)*VERTEX_JITTER*particleScale : 0;
|
||||
double py2 = freeEnd ? (random.nextDouble()*2-1)*VERTEX_JITTER*particleScale : 0;
|
||||
drawSegment(tessellator, layer, px, py, pz, px2, py2, length, THICKNESS*particleScale);
|
||||
|
||||
}
|
||||
|
||||
GlStateManager.enableTexture2D();
|
||||
GlStateManager.enableLighting();
|
||||
GlStateManager.disableBlend();
|
||||
}
|
||||
|
||||
/** Draws the given layer of a segment of the arc, from the point (x1, y1, z1) to the point (x2, y2, z2), with the given thickness. */
|
||||
private void drawSegment(Tessellator tessellator, int layer, double x1, double y1, double z1, double x2, double y2, double z2, float thickness){
|
||||
|
||||
BufferBuilder buffer = tessellator.getBuffer();
|
||||
buffer.begin(GL11.GL_TRIANGLE_STRIP, DefaultVertexFormats.POSITION_COLOR);
|
||||
|
||||
switch(layer){
|
||||
|
||||
case 0:
|
||||
drawShearedBox(buffer, x1, y1, z1, x2, y2, z2, 0.25f*thickness, 1, 1, 1, 1);
|
||||
break;
|
||||
|
||||
case 1:
|
||||
drawShearedBox(buffer, x1, y1, z1, x2, y2, z2, 0.6f*thickness, (particleRed + 1)/2, (particleGreen + 1)/2,
|
||||
(particleBlue + 1)/2, 0.65f);
|
||||
break;
|
||||
|
||||
case 2:
|
||||
drawShearedBox(buffer, x1, y1, z1, x2, y2, z2, thickness, particleRed, particleGreen, particleBlue, 0.3f);
|
||||
break;
|
||||
}
|
||||
|
||||
tessellator.draw();
|
||||
}
|
||||
|
||||
/** Draws a single box for one segment of the arc, from the point (x1, y1, z1) to the point (x2, y2, z2), with given width and colour. */
|
||||
private void drawShearedBox(BufferBuilder buffer, double x1, double y1, double z1, double x2, double y2, double z2, float width, float r, float g, float b, float a){
|
||||
|
||||
buffer.pos(x1-width, y1-width, z1).color(r, g, b, a).endVertex();
|
||||
buffer.pos(x2-width, y2-width, z2).color(r, g, b, a).endVertex();
|
||||
buffer.pos(x1-width, y1+width, z1).color(r, g, b, a).endVertex();
|
||||
buffer.pos(x2-width, y2+width, z2).color(r, g, b, a).endVertex();
|
||||
buffer.pos(x1+width, y1+width, z1).color(r, g, b, a).endVertex();
|
||||
buffer.pos(x2+width, y2+width, z2).color(r, g, b, a).endVertex();
|
||||
buffer.pos(x1+width, y1-width, z1).color(r, g, b, a).endVertex();
|
||||
buffer.pos(x2+width, y2-width, z2).color(r, g, b, a).endVertex();
|
||||
buffer.pos(x1-width, y1-width, z1).color(r, g, b, a).endVertex();
|
||||
buffer.pos(x2-width, y2-width, z2).color(r, g, b, a).endVertex();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
package electroblob.wizardry.client.particle;
|
||||
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
import net.minecraft.world.World;
|
||||
import net.minecraftforge.client.event.TextureStitchEvent;
|
||||
import net.minecraftforge.fml.common.Mod;
|
||||
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
|
||||
import net.minecraftforge.fml.relauncher.Side;
|
||||
|
||||
//@SideOnly(Side.CLIENT)
|
||||
@Mod.EventBusSubscriber(Side.CLIENT)
|
||||
public class ParticleLightningPulse extends ParticleWizardry {
|
||||
|
||||
private static final ResourceLocation[] TEXTURES = generateTextures("lightning_pulse", 8);
|
||||
|
||||
public ParticleLightningPulse(World world, double x, double y, double z){
|
||||
|
||||
super(world, x, y, z, TEXTURES);
|
||||
|
||||
this.particleScale = 32f;
|
||||
this.setRBGColorF(1, 1, 1);
|
||||
this.shaded = false;
|
||||
this.canCollide = false;
|
||||
this.setMaxAge(7); // Lifetime defaults to 7 (and is very unlikely to be changed)
|
||||
// Faces up by default
|
||||
this.pitch = 90;
|
||||
this.yaw = 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean shouldDisableDepth(){
|
||||
return true;
|
||||
}
|
||||
|
||||
@SubscribeEvent
|
||||
public static void onTextureStitchEvent(TextureStitchEvent.Pre event){
|
||||
for(ResourceLocation texture : TEXTURES){
|
||||
event.getMap().registerSprite(texture);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
package electroblob.wizardry.client.particle;
|
||||
|
||||
import net.minecraft.world.World;
|
||||
|
||||
//@SideOnly(Side.CLIENT)
|
||||
public class ParticleMagicBubble extends ParticleWizardry {
|
||||
|
||||
public ParticleMagicBubble(World world, double x, double y, double z){
|
||||
super(world, x, y, z);
|
||||
this.setRBGColorF(1, 1, 1);
|
||||
this.setParticleTextureIndex(32);
|
||||
this.setSize(0.02F, 0.02F);
|
||||
this.particleScale *= this.rand.nextFloat() * 0.6F + 0.2F;
|
||||
// this.motionX = par8 * 0.20000000298023224D + (double)((float)(Math.random() * 2.0D - 1.0D) * 0.02F);
|
||||
// this.motionY = par10 * 0.20000000298023224D + (double)((float)(Math.random() * 2.0D - 1.0D) * 0.02F);
|
||||
// this.motionZ = par12 * 0.20000000298023224D + (double)((float)(Math.random() * 2.0D - 1.0D) * 0.02F);
|
||||
this.particleMaxAge = (int)(8.0D / (Math.random() * 0.8D + 0.2D));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onUpdate(){
|
||||
this.prevPosX = this.posX;
|
||||
this.prevPosY = this.posY;
|
||||
this.prevPosZ = this.posZ;
|
||||
this.motionY += 0.002D;
|
||||
this.move(this.motionX, this.motionY, this.motionZ);
|
||||
this.motionX *= 0.8500000238418579D;
|
||||
this.motionY *= 0.8500000238418579D;
|
||||
this.motionZ *= 0.8500000238418579D;
|
||||
|
||||
if(this.particleMaxAge-- <= 0){
|
||||
this.setExpired();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,46 +1,36 @@
|
||||
package electroblob.wizardry.client.particle;
|
||||
|
||||
import net.minecraft.client.particle.Particle;
|
||||
import net.minecraft.client.renderer.BufferBuilder;
|
||||
import net.minecraft.entity.Entity;
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
import net.minecraft.world.World;
|
||||
import net.minecraftforge.client.event.TextureStitchEvent;
|
||||
import net.minecraftforge.fml.common.Mod;
|
||||
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
|
||||
import net.minecraftforge.fml.relauncher.Side;
|
||||
import net.minecraftforge.fml.relauncher.SideOnly;
|
||||
|
||||
@SideOnly(Side.CLIENT)
|
||||
public class ParticleMagicFlame extends Particle {
|
||||
//@SideOnly(Side.CLIENT)
|
||||
@Mod.EventBusSubscriber(Side.CLIENT)
|
||||
public class ParticleMagicFlame extends ParticleWizardry {
|
||||
|
||||
/** The scale of the flame particle */
|
||||
private float flameScale;
|
||||
// 4 different animation strips, 8 frames in each strip
|
||||
private static final ResourceLocation[][] TEXTURES = generateTextures("flame", 4, 8);
|
||||
|
||||
public ParticleMagicFlame(World par1World, double par2, double par4, double par6, double par8, double par10,
|
||||
double par12, int maxAge, float scale){
|
||||
super(par1World, par2, par4, par6, par8, par10, par12);
|
||||
this.motionX = this.motionX * 0.009999999776482582D + par8;
|
||||
this.motionY = this.motionY * 0.009999999776482582D + par10;
|
||||
this.motionZ = this.motionZ * 0.009999999776482582D + par12;
|
||||
this.flameScale = scale;
|
||||
this.particleRed = this.particleGreen = this.particleBlue = 1.0F;
|
||||
if(maxAge == 0){
|
||||
this.particleMaxAge = (int)(2.0D / (Math.random() * 0.8D + 0.2D));
|
||||
}else{
|
||||
this.particleMaxAge = maxAge;
|
||||
public ParticleMagicFlame(World world, double x, double y, double z){
|
||||
|
||||
super(world, x, y, z, TEXTURES[world.rand.nextInt(TEXTURES.length)]);
|
||||
|
||||
this.setRBGColorF(1, 1, 1);
|
||||
this.particleAlpha = 1;
|
||||
this.particleMaxAge = 12 + rand.nextInt(4);
|
||||
this.shaded = false;
|
||||
this.canCollide = true;
|
||||
}
|
||||
|
||||
@SubscribeEvent
|
||||
public static void onTextureStitchEvent(TextureStitchEvent.Pre event){
|
||||
for(ResourceLocation[] array : TEXTURES){
|
||||
for(ResourceLocation texture : array){
|
||||
event.getMap().registerSprite(texture);
|
||||
}
|
||||
}
|
||||
// IDEA: Make the particles for ray spells collide properly and not spawn on the other side of stuff.
|
||||
this.canCollide = false;
|
||||
this.setParticleTextureIndex(48);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void renderParticle(BufferBuilder buffer, Entity entity, float partialTicks, float rotationX, float rotationZ,
|
||||
float rotationYZ, float rotationXY, float rotationXZ){
|
||||
float f6 = ((float)this.particleAge + partialTicks) / (float)this.particleMaxAge;
|
||||
this.particleScale = this.flameScale * (1.0F - f6 * f6 * 0.5F);
|
||||
super.renderParticle(buffer, entity, partialTicks, rotationX, rotationZ, rotationYZ, rotationXY, rotationXZ);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getBrightnessForRender(float par1){
|
||||
return 256;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,69 +1,47 @@
|
||||
package electroblob.wizardry.client.particle;
|
||||
|
||||
import org.lwjgl.opengl.GL11;
|
||||
|
||||
import electroblob.wizardry.Wizardry;
|
||||
import electroblob.wizardry.spell.Clairvoyance;
|
||||
import net.minecraft.client.renderer.GlStateManager;
|
||||
import net.minecraft.client.renderer.OpenGlHelper;
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
import net.minecraft.world.World;
|
||||
import net.minecraftforge.client.event.TextureStitchEvent;
|
||||
import net.minecraftforge.fml.common.Mod;
|
||||
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
|
||||
import net.minecraftforge.fml.relauncher.Side;
|
||||
import net.minecraftforge.fml.relauncher.SideOnly;
|
||||
|
||||
@SideOnly(Side.CLIENT)
|
||||
public class ParticlePath extends ParticleCustomTexture {
|
||||
//@SideOnly(Side.CLIENT)
|
||||
@Mod.EventBusSubscriber(Side.CLIENT)
|
||||
public class ParticlePath extends ParticleWizardry {
|
||||
|
||||
private static final ResourceLocation TEXTURE = new ResourceLocation(Wizardry.MODID,
|
||||
"textures/particle/path_particles.png");
|
||||
private static final ResourceLocation TEXTURE = new ResourceLocation(Wizardry.MODID, "particle/path");
|
||||
|
||||
private final double originX, originY, originZ;
|
||||
|
||||
public ParticlePath(World world, double x, double y, double z, double vx, double vy, double vz, float r, float g,
|
||||
float b){
|
||||
super(world, x, y, z, vx, vy, vz);
|
||||
this.setRBGColorF(r, g, b);
|
||||
public ParticlePath(World world, double x, double y, double z){
|
||||
|
||||
super(world, x, y, z, TEXTURE); // This particle only has 1 texture
|
||||
|
||||
this.originX = x;
|
||||
this.originY = y;
|
||||
this.originZ = z;
|
||||
}
|
||||
|
||||
public ParticlePath(World world, double x, double y, double z, double vx, double vy, double vz, float r, float g,
|
||||
float b, int maxAge){
|
||||
super(world, x, y, z, vx, vy, vz, maxAge);
|
||||
this.setRBGColorF(r, g, b);
|
||||
this.originX = x;
|
||||
this.originY = y;
|
||||
this.originZ = z;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void init(){
|
||||
this.setParticleTextureIndex(0);
|
||||
|
||||
// Set to a constant to remove the randomness from Particle.
|
||||
this.particleScale = 1.25f;
|
||||
this.particleGravity = 0;
|
||||
this.fullBrightness = true;
|
||||
this.shaded = false;
|
||||
this.canCollide = false;
|
||||
this.setRBGColorF(1, 1, 1);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ResourceLocation getTexture(){
|
||||
return TEXTURE;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int getXFrames(){
|
||||
return 1;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int getYFrames(){
|
||||
return 1;
|
||||
public boolean shouldDisableDepth(){
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onUpdate(){
|
||||
|
||||
super.onUpdate();
|
||||
|
||||
this.prevPosX = this.posX;
|
||||
this.prevPosY = this.posY;
|
||||
@@ -89,19 +67,10 @@ public class ParticlePath extends ParticleCustomTexture {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void applyGLStateChanges(){
|
||||
GlStateManager.enableBlend();
|
||||
GlStateManager.blendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA);
|
||||
// TESTME: Are these two actually necessary?
|
||||
GlStateManager.disableLighting();
|
||||
OpenGlHelper.setLightmapTextureCoords(OpenGlHelper.lightmapTexUnit, 240, 240);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void undoGLStateChanges(){
|
||||
GlStateManager.disableBlend();
|
||||
GlStateManager.enableLighting();
|
||||
|
||||
@SubscribeEvent
|
||||
public static void onTextureStitchEvent(TextureStitchEvent.Pre event){
|
||||
event.getMap().registerSprite(TEXTURE);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user