Compare commits

..

19 Commits

Author SHA1 Message Date
彼梦Archi 4ebfcec051 feat: Add Chinese handbook translation (by ArchiDreamZ) (#865)
has been used by many people and is guaranteed to be correct.
2025-03-28 22:05:55 +01:00
ArchiDreamZ 0c2da4fb81 Fixed a crash when switching spells (#839)
Switching spells causes ArrayIndexOutOfBoundsException: -1 when the next spell is empty.I made a simple fix
2024-03-27 22:17:46 +01:00
glowredman 2655d5c2ae [1.7.10] Fix incompatibility with unofficial NEI (#823)
* Update buildscript

Gradle update done via `gradlew wrapper --gradle-version=4.4.1`

* Move NEI around GUI
2024-03-27 22:17:01 +01:00
ArchiDreamZ 4cbfafaf6d Add Simplified Chinese translation by ArchiDreamZ (#840)
Add Simplified Chinese translation
I fixed a very common crash in another pull request, hope you can update again
2024-03-27 22:15:33 +01:00
WinDanesz 98997d90ac Fake players don't drop spell books (#818)
Magic shouldn't be automated

Co-authored-by: DrParadox7 <47131096+DrParadox7@users.noreply.github.com>
2022-07-09 23:29:24 +02:00
Electroblob 09f858af20 Replace branch with old 1.7.10 code and assets 2018-06-07 11:58:24 +01:00
Electroblob 78b61cf573 Update README.md 2018-04-09 23:09:22 +01:00
Electroblob bf223ed5ab Update README.md 2018-03-09 10:04:14 +00:00
Electroblob 93b6b41a24 Update README.md 2018-02-26 22:54:03 +00:00
Electroblob 79a5cc4a67 Update ru_RU.lang (credit: VilagVil) 2018-02-25 18:21:30 +00:00
Electroblob f4d7ba6030 Fix inability to upgrade non-elemental wands using tomes of arcana 2018-02-18 23:33:54 +00:00
Electroblob a4695bcce1 Fix curseforge issue #30, where the game would crash when casting mind control on a mob that already had the mind trick status effect 2018-02-18 23:32:54 +00:00
Electroblob 52d12c0c02 Fix curseforge issue #28, where keybinds would be reset on game launch 2018-02-18 23:31:42 +00:00
Electroblob 56861b1ff5 Add coordinate fix from 1.11.2 branch, see 2dd52ce 2018-02-18 23:29:47 +00:00
Electroblob d2129ba256 Update CREDITS.md
Undoes previous commit, was made on the wrong branch
2018-02-13 16:28:32 +00:00
Electroblob 00a6b34310 Update CREDITS.md 2018-02-13 16:26:17 +00:00
Electroblob a2f1463939 Update README.md 2018-02-11 18:27:57 +00:00
Electroblob a9dbf47c4a Fix sparkle particles not being animated (this was accidentally disabled when testing a new feature) 2018-02-10 18:23:25 +00:00
Electroblob 2c8435392e Update version number 2018-02-10 18:22:49 +00:00
3233 changed files with 45818 additions and 124645 deletions
-7
View File
@@ -1,7 +0,0 @@
{
"homepage": "https://www.curseforge.com/minecraft/mc-mods/electroblobs-wizardry",
"promos": {
"1.12.2-latest": "4.3.15",
"1.12.2-recommended": "4.3.15"
}
}
-2
View File
@@ -1,2 +0,0 @@
# Auto detect text files and perform LF normalization
* text=auto
-85
View File
@@ -1,85 +0,0 @@
name: 🐛 Bug Report
description: Something is not working as expected
title: "[Bug] <write the title here>"
labels: [ "bug", "Needs Triage" ]
body:
- type: markdown
attributes:
value: |
Please read the [guide for contributing](https://github.com/Electroblob77/Wizardry/blob/1.12.2/CONTRIBUTING.md) before posting. You may also find the [troubleshooting page](https://github.com/Electroblob77/Wizardry/wiki/Troubleshooting) helpful.
Please try the latest version of the mod and check if the issue persist before submitting a report.
- type: checkboxes
attributes:
label: Is there an existing issue for this?
description: Please search to see if an issue already exists for the bug you encountered.
options:
- label: I have searched the existing issues
required: true
- type: textarea
id: observed
attributes:
label: Observed behaviour
description: Describe what happened and when this happened
validations:
required: true
- type: textarea
id: expected
attributes:
label: Expected behaviour
description: Describe what you expected to happen
validations:
required: true
- type: textarea
id: reproduction
attributes:
label: Steps to reproduce
description: Step by step instructions on how to reproduce the observed behaviour
placeholder: |
1. Join a server
2. Cast the [spell]
3. ...
validations:
required: true
- type: input
id: crashlog
attributes:
label: Crashlog
description: If this was a crash, grab [minecraft instance]/logs/debug.log, upload it as a [gist](https://gist.github.com/) and paste the link here. Alternatively, give a link to the crash report on gist, pastebin if you already have one there.
placeholder: https://gist.github.com/<your-user>/abcdef124
- type: dropdown
id: environment
attributes:
label: Environment (Singleplayer/Server, etc.)
options:
- Singleplayer
- LAN game/Server
- Issue present on both sides
- Not sure
validations:
required: true
- type: input
id: mod-version
attributes:
label: Mod version
placeholder: 1.0.0
validations:
required: true
- type: input
id: minecraft-version
attributes:
label: Minecraft version
placeholder: 12.2.2
validations:
required: true
- type: input
id: forge-version
attributes:
label: Forge version
placeholder: 1.12.2 - 14.23.5.2860
validations:
required: true
- type: textarea
id: other-mods
attributes:
label: Other mods
description: List any other mods present when the bug was encountered, include the mod versions. You only have to fill this if no crash log was attached
-14
View File
@@ -1,14 +0,0 @@
blank_issues_enabled: false
contact_links:
- name: 📖 Mod Wiki Page
url: https://github.com/Electroblob77/Wizardry/wiki
about: Most features are documented in the mod's wiki page --- Link --->
- name: 💬 Electroblob's Wizardry discord
url: https://discord.gg/hs8yJP2
about: Ask for help or get more info on Discord. --- Discord Server link --->
- name: 💡 Feature requests / suggestions
url: https://discord.gg/hs8yJP2
about: Please use the discord server to suggest stuff. --- Discord Server link --->
- name: ❓ Question
url: https://discord.gg/hs8yJP2
about: Ask a question about the mod or the code. --- Discord Server link --->
@@ -1,38 +0,0 @@
name: Potential Duplicates
on:
issues:
types: [opened, edited]
jobs:
run:
runs-on: ubuntu-latest
steps:
- uses: bubkoo/potential-duplicates@v1
with:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
label: potential duplicate
exclude: |
adding
add
creating
create
icons
icon
logos
logo
missing
miss
needing
need
please
requesting
request
1.12.2
wizardry
state: all
threshold: 0.7
comment: |
This issue is potentially a duplicate of one of the following issues:
{{#issues}}
- [#{{ number }}] {{ title }} ({{ accuracy }}% match)
{{/issues}}
+1 -6
View File
@@ -20,10 +20,5 @@ build
# other
eclipse
run
logs
/misc
/libs
libs_src
libsfordeobf
generated
misc
Thumbs.db
-59
View File
@@ -1,59 +0,0 @@
# Guide for contributing to this repository
## Submitting an issue
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.
- One issue per issue please! Make a separate issue for each bug/suggestion. If you think multiple different issues may be related, reference between them using # followed by the issue number, and github will automagically link them.
- Please give me as much information about the issue as you can, but keep it relevant. "This is really annoying me and my friend" does not help me fix the problem!
- Make sure your wizardry and Forge versions are both up-to-date for the game version you are using.
- 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.
- Please don't revive old issues, _especially_ if they have been fixed, because I don't routinely check them. Make a new issue and link the old one to it if you think it's related.
- Most importantly, use gist or pastebin to post crash reports. Do not paste the entire crash report inline, not even in a code block!
> If you've got a git account (which you do, if you're posting an issue) you can access gist, and pastebin doesn't require an account at all, so there's no excuse!
## Submitting a PR
I like my code to be tidy, so it saves me time if yours is too. Here are a few general guidelines for how to submit a good PR:
- Use proper indentation and try to space your code out nicely. This really goes without saying - select all your code and hit ctrl-i before you submit it, and try to use my spacing if you can (for instance, don't do what the Minecraft source does and put a { on a separate line, it's a waste of space).
- Commented code is dead code, UNLESS it has an accompanying comment detailing an appropriate reason for it being commented. (something like "DO NOT REMOVE, commented because it will be useful in 1.12" or "DO NOT REMOVE, commented for debugging purposes, uncomment when fixed")
- Please comment your methods/fields/classes! Preferably using Javadoc, but for small/private methods one line comments are OK. I find it hard enough to remember what I wrote a method for if it isn't commented, let alone trying to work out what yours are for. That said, you should try to name the method/field/class so it's obvious what it is anyway.
- Please use @Override when overriding any methods.
- Follow naming conventions. BLOCK_CAPITALS for constants, CapitalisedWords for classes, camelCase for almost everything else.
- This isn't code related, but when you submit a PR, please tell me what it does! You don't need much, just something like "Makes item X do X" or "Fixes #162". If you're resolving an issue, put "Fixes", "Closes" or "Resolves" and then the issue number and Github will close it automatically.
## Guide for translators
Pull requests aren't just for code - if you would like to help translate Wizardry, you can do that here too! Simply fork the repository, add your translations and submit a PR when you're done. Wizardry has TWO files you'll need to translate: the usual .lang file and the handbook text file, which can be found in resources/assets/wizardry/texts.
- Firstly, the .lang file: if you're familiar with translating mods, this is all pretty standard - it holds the names of blocks, items, mobs and so on. Ignore the %X$s bits, they're placeholders for stuff that gets put in by Wizardry when the game is running.
- Secondly, the handbook JSON file: the US english version is named handbook_en_us.json, and (rather obviously) other languages' text files replace 'en_us' with the name of their corresponding .lang file. Make sure you only change the _values_ (after each `:`), not the identifiers - for a summary of JSON syntax, see the [JSON webpage](https://www.json.org/json-en.html). If you're familiar with advancements, loot tables and similar files, this won't be new to you. The handbook file also applies its own form of markup to the main text, with the following features:
- `#image tag` adds an image to the text with the given tag. Image tags are defined in the `"images": {...}` block at the start of the file. Each named entry in this block defines a single image with that name as its tag. Each entry is a JSON object containing a file location and optional caption/dimensions.
- `#recipe tag` adds a crafting recipe to the text with the given tag. Similar to image tags, recipe tags are defined in the `"recipes": {...}` block at the start of the file. Each named entry in this block defines a recipe with that name as its tag. Each entry contains a list of one or more locations or recipes to display (if more than one is specified, the display will cycle through the different recipes).
- Anything else with a `#` directly before it (with no space) will be treated as a format argument if possible. This means the text after the `#` will be replaced with something else at runtime, similar to the `%X$s` bits in a normal .lang file, if it matches one of the available format arguments. The current format arguments can be found [here](https://github.com/Electroblob77/Wizardry/blob/1.12.2/src/main/java/electroblob/wizardry/client/gui/handbook/GuiWizardHandbook.java#L177-L203).
- `@section@` adds a hyperlink to the given section, which will display as the name of that section. `@section alt text@` is the same, except alt text is displayed instead of the section name (alt text may contain spaces). It is also possible to hyperlink to a webpage, simply replace the section name with the url (e.g. `@https://example.com/@` or `@https://example.com/ alt text@`).
> Translators for Chinese, Japanese, Korean and similar East Asian languages: Due to how Minecraft's word wrapping works, for best results you should _remove spaces before and after hyperlinks and formatting tags_, excluding images and recipes. However, do not remove the space between a hyperlink's destination and its alt text or the link will break!
If all of that confused you, the simple rule is: _Don't translate anything directly after an `@` symbol, directly after a `#`, or the first word after `#image` or `#recipe`_. However, you should translate the `"caption"` entries in the `images` block.
**Please ensure all translation files are encoded in UTF-8 format, otherwise they might crash the game!**
+5 -43
View File
@@ -1,8 +1,8 @@
# Credits
Electroblob's Wizardry
Version 4.3.7
For Minecraft 1.12.2
Version 2.1.0
For Minecraft 1.10.2
Designed, coded and textured by Electroblob
@@ -12,47 +12,9 @@ Thanks also to the Minecraft modding community, which always has an answer to my
In addition, I'd like to thank the following individuals for their contributions to the mod:
#### Discord Moderators (and all-round awesome people)
- FavouriteDragon
- WinDanesz
#### Code
- Corail31
- 12foo
- Shadows-of-Fire
- HellFirePvP
- Tora-B
- Avatair
- Aeronica
- UltraHex
- Azim-Palmer
- raoulvdberge
- rafasoares
- xinyuan-liu
- SettingDust
- Aralu115
#### Translations
- Spanish: MadWrist, Alsentar
- Mexican Spanish: MadWrist
- Russian: VilagVil, kellixon, bigenergy, MugGod2, DrHesperus
- French: Hahdrim, Crowller
- Brazilian Portuguese: lorrampi
- Chinese (Simplified): ZHENGLOC, dragon-evol, Hokorizero, TUsama, Determancer
- Korean: shejery, rewi_wire, 방통, red1854th
- Polish: Trozuu, Olej
- German: BirdyDragon, Lemopav
- Chinese (Traditional): chesterccj305
- Hungarian: Bombadil
- Russian: VilagVil
- Spanish and Mexican Spanish: MadWrist
#### Sound Effects
- OhhWowProductions
- fredzed
- deleted_user_3277771
- DiscoveryME
- OGSoundFX
- leosalom
- juskiddink
Lightning ray sound effect from OhhWowProductions
+2 -4
View File
@@ -1,7 +1,5 @@
Please read the [guide for contributing](https://github.com/Electroblob77/Wizardry/blob/1.12.2/guide_for_contributing.md) before posting. You may also find the [troubleshooting page](https://github.com/Electroblob77/Wizardry/wiki/Troubleshooting) helpful.
Minecraft version: 1.12.2 [change as necessary]
Wizardry version: [change as necessary]
Minecraft version: 1.10.2 [change as necessary]
Wizardry version: 2.1.0 [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.]
+4 -4
View File
@@ -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, 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. By downloading, running, forking, contributing to or otherwise interacting with this repository, you agree to the license set out below.
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.
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, the wiki, other informational files, configurations, code and assets which are part of 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
'Curseforge' - The webpage https://minecraft.curseforge.com/projects/electroblobs-wizardry
@@ -27,7 +27,7 @@ TL;DR: Do what you want with this repo on Github, but ask me first if you want t
### 1. Warranty and usage
This repository, the source code for the Minecraft modification Electroblob's Wizardry ('Wizardry'), is provided in the hope that it will be useful and/or educational. You are free to download and modify this repository for personal use. See section 2 for details regarding public sharing of this repository.
This repository, the source code for the Minecraft modification Electroblob's Wizardry ('Wizardry'), is provided in the hope that it will be useful and/or educational. You are free to download and modify this repository for personal use only. See section 2 for details regarding public sharing of this repository.
THIS REPOSITORY IS PROVIDED AS-IS WITH NO WARRANTIES, IMPLIED OR OTHERWISE. ELECTROBLOB TAKES NO RESPONSIBILITY FOR ANY DAMAGES INCURRED BY THE USE OR MISUSE OF THIS REPOSITORY.
@@ -37,7 +37,7 @@ In addition to private use as set out in section 1, you are free to fork this re
### 3. Distribution
Distribution of this repository anywhere except on Github, in whole or in part, modified or otherwise, is forbidden without advance written permission from Electroblob. Electroblob reserves the right to refuse or withdraw such permission at any time.
Distribution, in whole or in part, of compiled versions of wizardry, and distribution of the source code anywhere except on Github, modified or otherwise, is forbidden without advance written permission from Electroblob. Electroblob reserves the right to refuse or withdraw such permission at any time.
### 4. Commercial use
+3 -3
View File
@@ -1,10 +1,10 @@
# Wizardry &nbsp;[![Curseforge](http://cf.way2muchnoise.eu/full_electroblobs-wizardry_downloads.svg)](https://minecraft.curseforge.com/projects/electroblobs-wizardry) [![Curseforge](http://cf.way2muchnoise.eu/versions/For%20MC_electroblobs-wizardry_all.svg)](https://minecraft.curseforge.com/projects/electroblobs-wizardry/files) [![Discord](https://img.shields.io/discord/417798297265242145.svg?colorB=7289DA&logo=data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAHYAAABWAgMAAABnZYq0AAAACVBMVEUAAB38%2FPz%2F%2F%2F%2Bm8P%2F9AAAAAXRSTlMAQObYZgAAAAFiS0dEAIgFHUgAAAAJcEhZcwAACxMAAAsTAQCanBgAAAAHdElNRQfhBxwQJhxy2iqrAAABoElEQVRIx7WWzdGEIAyGgcMeKMESrMJ6rILZCiiBg4eYKr%2Fd1ZAfgXFm98sJfAyGNwno3G9sLucgYGpQ4OGVRxQTREMDZjF7ILSWjoiHo1n%2BE03Aw8p7CNY5IhkYd%2F%2F6MtO3f8BNhR1QWnarCH4tr6myl0cWgUVNcfMcXACP1hKrGMt8wcAyxide7Ymcgqale7hN6846uJCkQxw6GG7h2MH4Czz3cLqD1zHu0VOXMfZjHLoYvsdd0Q7ZvsOkafJ1P4QXxrWFd14wMc60h8JKCbyQvImzlFjyGoZTKzohwWR2UzSONHhYXBQOaKKsySsahwGGDnb%2FiYPJw22sCqzirSULYy1qtHhXGbtgrM0oagBV4XiTJok3GoLoDNH8ooTmBm7ZMsbpFzi2bgPGoXWXME6XT%2BRJ4GLddxJ4PpQy7tmfoU2HPN6cKg%2BledKHBKlF8oNSt5w5g5o8eXhu1IOlpl5kGerDxIVT%2BztzKepulD8utXqpChamkzzuo7xYGk%2FkpSYuviLXun5bzdRf0Krejzqyz7Z3p0I1v2d6HmA07dofmS48njAiuMgAAAAASUVORK5CYII%3D)](https://discord.gg/hs8yJP2)
# Wizardry &nbsp;[![Curseforge](http://cf.way2muchnoise.eu/full_electroblobs-wizardry_downloads.svg)](https://minecraft.curseforge.com/projects/electroblobs-wizardry) [![Curseforge](http://cf.way2muchnoise.eu/versions/For%20MC_electroblobs-wizardry_all.svg)](https://minecraft.curseforge.com/projects/electroblobs-wizardry) [![Discord](https://img.shields.io/discord/417798297265242145.svg?colorB=7289DA&logo=data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAHYAAABWAgMAAABnZYq0AAAACVBMVEUAAB38%2FPz%2F%2F%2F%2Bm8P%2F9AAAAAXRSTlMAQObYZgAAAAFiS0dEAIgFHUgAAAAJcEhZcwAACxMAAAsTAQCanBgAAAAHdElNRQfhBxwQJhxy2iqrAAABoElEQVRIx7WWzdGEIAyGgcMeKMESrMJ6rILZCiiBg4eYKr%2Fd1ZAfgXFm98sJfAyGNwno3G9sLucgYGpQ4OGVRxQTREMDZjF7ILSWjoiHo1n%2BE03Aw8p7CNY5IhkYd%2F%2F6MtO3f8BNhR1QWnarCH4tr6myl0cWgUVNcfMcXACP1hKrGMt8wcAyxide7Ymcgqale7hN6846uJCkQxw6GG7h2MH4Czz3cLqD1zHu0VOXMfZjHLoYvsdd0Q7ZvsOkafJ1P4QXxrWFd14wMc60h8JKCbyQvImzlFjyGoZTKzohwWR2UzSONHhYXBQOaKKsySsahwGGDnb%2FiYPJw22sCqzirSULYy1qtHhXGbtgrM0oagBV4XiTJok3GoLoDNH8ooTmBm7ZMsbpFzi2bgPGoXWXME6XT%2BRJ4GLddxJ4PpQy7tmfoU2HPN6cKg%2BledKHBKlF8oNSt5w5g5o8eXhu1IOlpl5kGerDxIVT%2BztzKepulD8utXqpChamkzzuo7xYGk%2FkpSYuviLXun5bzdRf0Krejzqyz7Z3p0I1v2d6HmA07dofmS48njAiuMgAAAAASUVORK5CYII%3D)](https://discord.gg/MTmMzMv)
![Image not found](https://raw.githubusercontent.com/wiki/Electroblob77/Wizardry/en_gb/images/banners/art_banner.png)
![Image not found](https://media-elerium.cursecdn.com/attachments/203/387/banner7.png)
Source code for Electroblob's Wizardry, a Minecraft mod currently for versions 1.7.10, 1.10.2, 1.11.2, and 1.12.2. Wizardry adds an RPG-style system of magic spells to Minecraft with the aim of being as playable as possible. No crazy constructs, no perk trees, no complex recipes - simply find spell books, cast spells, and master the arcane!
Please read the [guide for contributing](CONTRIBUTING.md) before submitting an issue or a PR.
Please read the [guide for contributing](guide_for_contributing.md) before submitting an issue or a PR.
<b>Links</b>
+41 -119
View File
@@ -1,152 +1,74 @@
import groovy.json.JsonOutput
buildscript {
repositories {
gradlePluginPortal()
mavenCentral()
maven {
name 'MinecraftForge'
url 'https://maven.minecraftforge.net/'
name = "forge"
url = "https://maven.minecraftforge.net/"
}
maven {
name 'Garden of Fancy'
url 'https://maven.gofancy.wtf/releases'
}
//fallback for fancygradle maven
//mavenLocal()
maven {
name 'Sponge'
url 'https://repo.spongepowered.org/repository/maven-public/'
name = "sonatype"
url = "https://oss.sonatype.org/content/repositories/snapshots/"
}
}
dependencies {
classpath 'net.minecraftforge.gradle:ForgeGradle:5.+'
classpath 'org.ajoberstar.grgit:grgit-gradle:3.1.1'
classpath group: 'wtf.gofancy.fancygradle', name: 'wtf.gofancy.fancygradle.gradle.plugin', version: '1.1.+'
}
repositories.each {
if (it instanceof MavenArtifactRepository && it.url.toString() == "https://files.minecraftforge.net/maven") {
it.url = "https://maven.minecraftforge.net"
}
classpath 'net.minecraftforge.gradle:ForgeGradle:1.2-SNAPSHOT'
}
}
apply plugin: 'net.minecraftforge.gradle'
apply plugin: 'java'
apply plugin: 'idea'
apply plugin: 'maven-publish'
apply plugin: org.ajoberstar.grgit.gradle.GrgitPlugin
apply plugin: 'wtf.gofancy.fancygradle'
version = "4.3.15"
group= "electroblob.wizardry"// http://maven.apache.org/guides/mini/guide-naming-conventions.html
archivesBaseName = "ElectroblobsWizardry"
sourceCompatibility = targetCompatibility = compileJava.sourceCompatibility = compileJava.targetCompatibility = '1.8' // Need this here so eclipse task generates correctly.
apply plugin: 'forge'
version = " 1.1.5 - MC 1.7.10"
group = "electroblob.wizardry"// http://maven.apache.org/guides/mini/guide-naming-conventions.html
archivesBaseName = "Electroblob's Wizardry "
minecraft {
// The mappings can be changed at any time, and must be in the following format.
// snapshot_YYYYMMDD Snapshot are built nightly.
// stable_# Stables are built at the discretion of the MCP team.
// Use non-default mappings at your own risk. they may not always work.
// Simply re-run your setup task after changing the mappings to update your workspace.
//mappings channel: 'snapshot', version: '20171003-1.12'
mappings channel: "stable", version: "39-1.12"
// makeObfSourceJar = false // an Srg named sources jar is made by default. uncomment this to disable.
version = "1.7.10-10.13.4.1614-1.7.10"
runDir = "eclipse"
}
// accessTransformer = file('src/main/resources/META-INF/accesstransformer.cfg')
// Default run configurations.
// These can be tweaked, removed, or duplicated as needed.
def argsz = ['--username', 'WinDanesz', '--user', 'WinDanesz', '--uuid', '7faee354-8c60-4f5c-9862-fc0ce5f7f575']
runs {
client {
workingDirectory project.file('run')
// Recommended logging data for a userdev environment
property 'forge.logging.markers', 'SCAN,REGISTRIES,REGISTRYDUMP'
// Recommended logging level for the console
property 'forge.logging.console.level', 'info'
args argsz
}
server {
// Recommended logging data for a userdev environment
property 'forge.logging.markers', 'SCAN,REGISTRIES,REGISTRYDUMP'
// Recommended logging level for the console
property 'forge.logging.console.level', 'info'
}
}
task deobfJar(type: Jar) { // Generate deobfuscated
from sourceSets.main.output
classifier = 'deobf'
}
task sourceJar(type: Jar) { // Generate sources
from sourceSets.main.allSource
classifier = 'sources'
}
tasks.build.dependsOn('sourceJar', 'deobfJar')
artifacts {
archives deobfJar
archives sourceJar
archives jar
}
repositories {
google()
mavenCentral()
maven {
url = uri('https://www.cursemaven.com')
content {
includeGroup 'curse.maven'
}
}
maven {
name = 'Modrinth'
url = uri('https://api.modrinth.com/maven')
content {
includeGroup 'maven.modrinth'
}
}
maven {
name = 'Sponge'
url = uri('https://repo.spongepowered.org/maven')
}
maven {
url = uri('https://maven.blamejared.com')
}
maven {
url = uri('https://m2.dv8tion.net/releases')
}
maven {
url = uri('https://jitpack.io')
name 'ChickenBones maven'
url 'https://chickenbones.net/maven/'
}
}
dependencies {
//// MC version ////
// Specify the version of Minecraft to use, If this is any group other then 'net.minecraft' it is assumed
// that the dep is a ForgeGradle 'patcher' dependency. And it's patches will be applied.
// The userdev artifact is a special name and will get all sorts of transformations applied to it.
minecraft "net.minecraftforge:forge:${project.mc_version}-${project.forge_version}"
//// MC version ////
implementation fg.deobf("curse.maven:baubles-${baubles_projectid}:${baubles_fileid}")
implementation fg.deobf("mezz.jei:jei_${mc_version}:${jei_version}")
implementation fg.deobf("curse.maven:antique-atlas-${antiqueatlas_projectid}:${antiqueatlas_fileid}")
}
task deobfJar(type: Jar) {
from sourceSets.main.output
classifier = 'deobf'
compileOnly 'codechicken:NotEnoughItems:1.7.10-1.0.5.120:dev'
}
processResources {
// replace tokens in mcmod.info, and pack.mcmeta
// this will ensure that this task is redone when the versions change.
inputs.property "version", project.version
inputs.property "mcversion", project.minecraft.version
// replace stuff in mcmod.info, nothing else
from(sourceSets.main.resources.srcDirs) {
include 'pack.mcmeta'
include 'mcmod.info'
// replace version and mcversion
expand 'version':project.version, 'mcversion':project.minecraft.version
}
duplicatesStrategy = 'include'
}
fancyGradle {
patches {
resources
coremods
asm
// copy everything else, thats not the mcmod.info
from(sourceSets.main.resources.srcDirs) {
exclude 'mcmod.info'
}
}
-22
View File
@@ -1,22 +0,0 @@
# Sets default memory used for gradle commands. Can be overridden by user or command line properties.
# This is required to provide enough memory for the Minecraft decompilation process.
org.gradle.jvmargs=-Xmx3G
mod_id=ebwizardry
mc_version=1.12.2
mc_version_short=1.12
jei_version=4.15.0.291
baubles_version=1.5.2
antique_atlas_version=4.6.3
forge_version=14.23.5.2860
mappings_version=39
mappings_channel=stable
mappings_mc_version=1.12
baubles_projectid=227083
baubles_fileid=2518667
jei_projectid=238222
artemislib_projectid=313590
artemislib_version=1.0.6
artemislib_fileid=2741812
antiqueatlas_projectid=227795
antiqueatlas_fileid=2823030
org.gradle.daemon=false
Binary file not shown.
+2 -2
View File
@@ -1,5 +1,5 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-7.3.3-all.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-4.4.1-bin.zip
Vendored
+115 -175
View File
@@ -1,232 +1,172 @@
#!/bin/sh
#
# Copyright © 2015-2021 the original authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
#!/usr/bin/env sh
##############################################################################
#
# Gradle start up script for POSIX generated by Gradle.
#
# Important for running:
#
# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is
# noncompliant, but you have some other compliant shell such as ksh or
# bash, then to run this script, type that shell name before the whole
# command line, like:
#
# ksh Gradle
#
# Busybox and similar reduced shells will NOT work, because this script
# requires all of these POSIX shell features:
# * functions;
# * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,
# «${var#prefix}», «${var%suffix}», and «$( cmd )»;
# * compound commands having a testable exit status, especially «case»;
# * various built-in commands including «command», «set», and «ulimit».
#
# Important for patching:
#
# (2) This script targets any POSIX shell, so it avoids extensions provided
# by Bash, Ksh, etc; in particular arrays are avoided.
#
# The "traditional" practice of packing multiple parameters into a
# space-separated string is a well documented source of bugs and security
# problems, so this is (mostly) avoided, by progressively accumulating
# options in "$@", and eventually passing that to Java.
#
# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,
# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;
# see the in-line comments for details.
#
# There are tweaks for specific operating systems such as AIX, CygWin,
# Darwin, MinGW, and NonStop.
#
# (3) This script is generated from the Groovy template
# https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
# within the Gradle project.
#
# You can find Gradle at https://github.com/gradle/gradle/.
#
##
## Gradle start up script for UN*X
##
##############################################################################
# Attempt to set APP_HOME
# Resolve links: $0 may be a link
app_path=$0
# Need this for daisy-chained symlinks.
while
APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path
[ -h "$app_path" ]
do
ls=$( ls -ld "$app_path" )
link=${ls#*' -> '}
case $link in #(
/*) app_path=$link ;; #(
*) app_path=$APP_HOME$link ;;
esac
PRG="$0"
# Need this for relative symlinks.
while [ -h "$PRG" ] ; do
ls=`ls -ld "$PRG"`
link=`expr "$ls" : '.*-> \(.*\)$'`
if expr "$link" : '/.*' > /dev/null; then
PRG="$link"
else
PRG=`dirname "$PRG"`"/$link"
fi
done
APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit
SAVED="`pwd`"
cd "`dirname \"$PRG\"`/" >/dev/null
APP_HOME="`pwd -P`"
cd "$SAVED" >/dev/null
APP_NAME="Gradle"
APP_BASE_NAME=${0##*/}
APP_BASE_NAME=`basename "$0"`
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
DEFAULT_JVM_OPTS=""
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD=maximum
MAX_FD="maximum"
warn () {
echo "$*"
} >&2
}
die () {
echo
echo "$*"
echo
exit 1
} >&2
}
# OS specific support (must be 'true' or 'false').
cygwin=false
msys=false
darwin=false
nonstop=false
case "$( uname )" in #(
CYGWIN* ) cygwin=true ;; #(
Darwin* ) darwin=true ;; #(
MSYS* | MINGW* ) msys=true ;; #(
NONSTOP* ) nonstop=true ;;
case "`uname`" in
CYGWIN* )
cygwin=true
;;
Darwin* )
darwin=true
;;
MINGW* )
msys=true
;;
NONSTOP* )
nonstop=true
;;
esac
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
# Determine the Java command to use to start the JVM.
if [ -n "$JAVA_HOME" ] ; then
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
# IBM's JDK on AIX uses strange locations for the executables
JAVACMD=$JAVA_HOME/jre/sh/java
JAVACMD="$JAVA_HOME/jre/sh/java"
else
JAVACMD=$JAVA_HOME/bin/java
JAVACMD="$JAVA_HOME/bin/java"
fi
if [ ! -x "$JAVACMD" ] ; then
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
else
JAVACMD=java
JAVACMD="java"
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
# Increase the maximum file descriptors if we can.
if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
case $MAX_FD in #(
max*)
MAX_FD=$( ulimit -H -n ) ||
warn "Could not query maximum file descriptor limit"
esac
case $MAX_FD in #(
'' | soft) :;; #(
*)
ulimit -n "$MAX_FD" ||
warn "Could not set maximum file descriptor limit to $MAX_FD"
esac
fi
# Collect all arguments for the java command, stacking in reverse order:
# * args from the command line
# * the main class name
# * -classpath
# * -D...appname settings
# * --module-path (only if needed)
# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.
# For Cygwin or MSYS, switch paths to Windows format before running java
if "$cygwin" || "$msys" ; then
APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" )
JAVACMD=$( cygpath --unix "$JAVACMD" )
# Now convert the arguments - kludge to limit ourselves to /bin/sh
for arg do
if
case $arg in #(
-*) false ;; # don't mess with options #(
/?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath
[ -e "$t" ] ;; #(
*) false ;;
esac
then
arg=$( cygpath --path --ignore --mixed "$arg" )
if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
MAX_FD_LIMIT=`ulimit -H -n`
if [ $? -eq 0 ] ; then
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
MAX_FD="$MAX_FD_LIMIT"
fi
# Roll the args list around exactly as many times as the number of
# args, so each arg winds up back in the position where it started, but
# possibly modified.
#
# NB: a `for` loop captures its iteration list before it begins, so
# changing the positional parameters here affects neither the number of
# iterations, nor the values presented in `arg`.
shift # remove old arg
set -- "$@" "$arg" # push replacement arg
done
ulimit -n $MAX_FD
if [ $? -ne 0 ] ; then
warn "Could not set maximum file descriptor limit: $MAX_FD"
fi
else
warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
fi
fi
# Collect all arguments for the java command;
# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of
# shell script including quotes and variable substitutions, so put them in
# double quotes to make sure that they get re-expanded; and
# * put everything else in single quotes, so that it's not re-expanded.
# For Darwin, add options to specify how the application appears in the dock
if $darwin; then
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
fi
set -- \
"-Dorg.gradle.appname=$APP_BASE_NAME" \
-classpath "$CLASSPATH" \
org.gradle.wrapper.GradleWrapperMain \
"$@"
# For Cygwin, switch paths to Windows format before running java
if $cygwin ; then
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
JAVACMD=`cygpath --unix "$JAVACMD"`
# Use "xargs" to parse quoted args.
#
# With -n1 it outputs one arg per line, with the quotes and backslashes removed.
#
# In Bash we could simply go:
#
# readarray ARGS < <( xargs -n1 <<<"$var" ) &&
# set -- "${ARGS[@]}" "$@"
#
# but POSIX shell has neither arrays nor command substitution, so instead we
# post-process each arg (as a line of input to sed) to backslash-escape any
# character that might be a shell metacharacter, then use eval to reverse
# that process (while maintaining the separation between arguments), and wrap
# the whole thing up as a single "set" statement.
#
# This will of course break if any of these variables contains a newline or
# an unmatched quote.
#
# We build the pattern for arguments to be converted via cygpath
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
SEP=""
for dir in $ROOTDIRSRAW ; do
ROOTDIRS="$ROOTDIRS$SEP$dir"
SEP="|"
done
OURCYGPATTERN="(^($ROOTDIRS))"
# Add a user-defined pattern to the cygpath arguments
if [ "$GRADLE_CYGPATTERN" != "" ] ; then
OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
fi
# Now convert the arguments - kludge to limit ourselves to /bin/sh
i=0
for arg in "$@" ; do
CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
eval "set -- $(
printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" |
xargs -n1 |
sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' |
tr '\n' ' '
)" '"$@"'
if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
else
eval `echo args$i`="\"$arg\""
fi
i=$((i+1))
done
case $i in
(0) set -- ;;
(1) set -- "$args0" ;;
(2) set -- "$args0" "$args1" ;;
(3) set -- "$args0" "$args1" "$args2" ;;
(4) set -- "$args0" "$args1" "$args2" "$args3" ;;
(5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
(6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
(7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
(8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
(9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
esac
fi
exec "$JAVACMD" "$@"
# Escape application args
save () {
for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
echo " "
}
APP_ARGS=$(save "$@")
# Collect all arguments for the java command, following the shell quoting and substitution rules
eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
cd "$(dirname "$0")"
fi
exec "$JAVACMD" "$@"
Vendored
+20 -25
View File
@@ -1,19 +1,3 @@
@rem
@rem Copyright 2015 the original author or authors.
@rem
@rem Licensed under the Apache License, Version 2.0 (the "License");
@rem you may not use this file except in compliance with the License.
@rem You may obtain a copy of the License at
@rem
@rem https://www.apache.org/licenses/LICENSE-2.0
@rem
@rem Unless required by applicable law or agreed to in writing, software
@rem distributed under the License is distributed on an "AS IS" BASIS,
@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@rem See the License for the specific language governing permissions and
@rem limitations under the License.
@rem
@if "%DEBUG%" == "" @echo off
@rem ##########################################################################
@rem
@@ -29,18 +13,15 @@ if "%DIRNAME%" == "" set DIRNAME=.
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%
@rem Resolve any "." and ".." in APP_HOME to make it shorter.
for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
set DEFAULT_JVM_OPTS=
@rem Find java.exe
if defined JAVA_HOME goto findJavaFromJavaHome
set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
if "%ERRORLEVEL%" == "0" goto execute
if "%ERRORLEVEL%" == "0" goto init
echo.
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
@@ -54,7 +35,7 @@ goto fail
set JAVA_HOME=%JAVA_HOME:"=%
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
if exist "%JAVA_EXE%" goto execute
if exist "%JAVA_EXE%" goto init
echo.
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
@@ -64,14 +45,28 @@ echo location of your Java installation.
goto fail
:init
@rem Get command-line arguments, handling Windows variants
if not "%OS%" == "Windows_NT" goto win9xME_args
:win9xME_args
@rem Slurp the command line arguments.
set CMD_LINE_ARGS=
set _SKIP=2
:win9xME_args_slurp
if "x%~1" == "x" goto execute
set CMD_LINE_ARGS=%*
:execute
@rem Setup the command line
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
@rem Execute Gradle
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
:end
@rem End local scope for the variables with windows NT shell
@@ -86,4 +81,4 @@ exit /b 1
:mainEnd
if "%OS%"=="Windows_NT" endlocal
:omega
:omega
+29
View File
@@ -0,0 +1,29 @@
# Guide for contributing to this repository
## 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.
## Submitting a PR
I like my code to be tidy, so it saves me time if yours is too. Here are a few general guidelines for how to submit a good PR:
- Use proper indentation and try to space your code out nicely. This really goes without saying - select all your code and hit ctrl-i before you submit it, and try to use my spacing if you can (for instance, don't do what the Minecraft source does and put a { on a separate line, it's a waste of space).
- Commented code is dead code, UNLESS it has an accompanying comment detailing an appropriate reason for it being commented. (something like "DO NOT REMOVE, commented because it will be useful in 1.12" or "DO NOT REMOVE, commented for debugging purposes, uncomment when fixed")
- Please comment your methods/fields/classes! Preferably using Javadoc, but for small/private methods one line comments are OK. I find it hard enough to remember what I wrote a method for if it isn't commented, let alone trying to work out what yours are for. That said, you should try to name the method/field/class so it's obvious what it is anyway.
- Please use @Override when overriding any methods.
- Follow naming conventions. BLOCK_CAPITALS for constants, CapitalisedWords for classes, camelCase for almost everything else.
- This isn't code related, but when you submit a PR, please tell me what it does! You don't need much, just something like "Makes item X do X" or "Fixes #162". If you're resolving an issue, put "Fixes", "Closes" or "Resolves" and then the issue number and Github will close it automatically.
## Guide for translators
Pull requests aren't just for code - if you would like to help translate Wizardry, you can do that here too! Simply fork the repository, add your translations and submit a PR when you're done. Wizardry has TWO files you'll need to translate: the usual .lang file and the handbook text file, which can be found in resources/assets/wizardry/texts.
- Firstly, the .lang file: if you're familiar with translating mods, this is all pretty standard - it holds the names of blocks, items, mobs and so on. Ignore the %X$s bits, they're placeholders for stuff that gets put in by Wizardry when the game is running.
- Secondly, the handbook text file: the US english version is named handbook_en_US.txt, and (rather obviously) other languages' text files replace 'en_US' with the name of their corresponding .lang file. Anything wirtten in BLOCK CAPITALS should be left alone, because Wizardry replaces it with the appropriate text when the game is running.
Binary file not shown.
Binary file not shown.
@@ -1,334 +1,203 @@
package electroblob.wizardry;
import electroblob.wizardry.block.BlockBookshelf;
import electroblob.wizardry.inventory.ContainerArcaneWorkbench;
import electroblob.wizardry.item.ItemSpectralBow;
import electroblob.wizardry.packet.*;
import electroblob.wizardry.registry.WizardryItems;
import java.util.HashMap;
import java.util.Map;
import java.util.Vector;
import cpw.mods.fml.common.FMLCommonHandler;
import electroblob.wizardry.packet.PacketCastContinuousSpell;
import electroblob.wizardry.packet.PacketCastSpell;
import electroblob.wizardry.packet.PacketClairvoyance;
import electroblob.wizardry.packet.PacketGlyphData;
import electroblob.wizardry.packet.PacketPlayerSync;
import electroblob.wizardry.packet.PacketTransportation;
import electroblob.wizardry.spell.Spell;
import net.minecraft.block.state.IBlockState;
import net.minecraft.block.Block;
import net.minecraft.client.gui.FontRenderer;
import net.minecraft.client.model.ModelBiped;
import net.minecraft.entity.Entity;
import net.minecraft.entity.EntityLivingBase;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.item.ItemArmor.ArmorMaterial;
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.nbt.NBTTagCompound;
import net.minecraft.util.StatCollector;
import net.minecraft.util.Vec3;
import net.minecraft.world.World;
import net.minecraftforge.common.MinecraftForge;
import net.minecraftforge.common.config.Property;
import javax.annotation.Nullable;
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 electroblob.wizardry.client.ClientProxy ClientProxy
* @author Electroblob
* @since Wizardry 1.0
*/
@SuppressWarnings("deprecation")
/** 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} */
public class CommonProxy {
// SECTION Registry
// ===============================================================================================================
public void registerRenderers(){}
public void initialiseLayers(){}
public void initialiseAnimations(){}
public void registerKeyBindings(){}
public net.minecraft.client.model.ModelBiped getWizardArmourModel(ArmorMaterial material){
return null;
}
public void initGuiBits(){}
/** Used to store IExtendedEntityProperties data temporarily between player death and respawn */
private static final Map<String, NBTTagCompound> extendedEntityData = new HashMap<String, NBTTagCompound>();
public void registerResourceReloadListeners(){}
public void registerSoundEventListener(){}
public void registerAtlasMarkers(){}
// SECTION Particles
// ===============================================================================================================
/** Called from init() in the main mod class to initialise the particle factories. */
public void registerParticles(){} // Does nothing since particles are client-side only
/** 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){
public void registerRenderers(){}
public void registerKeyBindings(){}
public void registerSpellHUD(){}
public ModelBiped getWizardArmourModel(){
return null;
}
/**
* Adds an entity's custom data to the map for temporary storage
* @param compound An NBT Tag Compound that stores the IExtendedEntityProperties data only
*/
public static void storeEntityData(String name, NBTTagCompound compound){
extendedEntityData.put(name, compound);
}
public void spawnTornadoParticle(World world, double x, double y, double z, double velX, double velZ, double radius,
int maxAge, IBlockState block, BlockPos pos){
/**
* Removes the compound from the map and returns the NBT tag stored for name or null if none exists
*/
public static NBTTagCompound getEntityData(String name){
return extendedEntityData.remove(name);
}
public double getPlayerEyesPos(EntityPlayer player){
return player.posY + player.eyeHeight;
}
/** Gets the absolute coordinates of the tip of the wand belonging to the given entity. If this is a client in
* first person view, the coordinates will instead be the entity's position + 1.2. */
public Vec3 getWandTipPosition(EntityLivingBase entity){
// 0.7 forwards, 0.4 to the right, and 1.35 upwards.
double x = entity.posX - 0.7*Math.sin(Math.toRadians(entity.renderYawOffset)) - 0.4*Math.cos(Math.toRadians(entity.renderYawOffset));
double y = entity.boundingBox.minY + 1.35;
double z = entity.posZ + 0.7*Math.cos(Math.toRadians(entity.renderYawOffset)) - 0.4*Math.sin(Math.toRadians(entity.renderYawOffset));
return Vec3.createVectorHelper(x, y, z);
}
/**
* Spawns a custom particle of the specified type.
*
* @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
* @param g Red component of particle colour
* @param b Red component of particle colour
* @param doGravity Whether the particle is affected by gravity (only affects SPARKLE at the moment)
* @param radius The radius of the particle's motion, for cirular motion particles
*/
public void spawnParticle(EnumParticleType type, World world, double x, double y, double z, double velX, double velY, double velZ, int maxAge, float r, float g, float b, boolean doGravity, double radius){
// Does nothing since particles are client-side only
}
/** 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 (unless this is a MAGIC_FIRE particle, in which case this is the scale)
* @param g Red component of particle colour
* @param b Red component of particle colour
*/
public void spawnParticle(EnumParticleType type, World world, double x, double y, double z, double velX, double velY, double velZ, int maxAge, float r, float g, float b){
this.spawnParticle(type, world, x, y, z, velX, velY, velZ, maxAge, r, g, b, false, 0);
}
/** 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(EnumParticleType type, World world, double x, double y, double z, double velX, double velY, double velZ, int maxAge){
this.spawnParticle(type, world, x, y, z, velX, velY, velZ, maxAge, 1, 1, 1, false, 0);
}
public void spawnDigParticle(World world, double x, double y, double z, double velX, double velY, double velZ, Block block){}
public void spawnTornadoParticle(World world, double x, double y, double z, double velX, double velZ, double radius, int maxAge, Block block, int metadata){}
/** Plays a sound which moves with the given entity.
*
* @param entity The source of the sound
* @param soundName String resource path of the sound
* @param volume Volume relative to 1
* @param pitch Pitch relative to 1
* @param repeat Whether to repeat the sound for as long as the entity is alive (or until stopped manually)
*/
public void playMovingSound(Entity entity, String soundName, float volume, float pitch, boolean repeat){}
public void handleCastSpellPacket(PacketCastSpell.Message message){}
public double getWandDisplayDamage(ItemStack stack){
return 1.0d;
}
public int getConjuredItemDisplayDamage(ItemStack stack){
return 0;
}
// SECTION Items
// ===============================================================================================================
public boolean shouldDisplayDiscovered(Spell spell, @Nullable ItemStack stack){
return false;
public void registerEventHandlers(){
MinecraftForge.EVENT_BUS.register(new WizardryEventHandler());
}
public net.minecraft.client.gui.FontRenderer getFontRenderer(ItemStack stack){
public void handleTransportationPacket(PacketTransportation.Message message){}
public 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. 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.
*/
/** 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. */
public String getScrollDisplayName(ItemStack scroll){
Spell spell = Spell.byMetadata(scroll.getItemDamage());
return I18n.translateToLocalFormatted("item." + Wizardry.MODID + ":scroll.name",
I18n.translateToLocal("spell." + spell.getUnlocalisedName())).trim();
// 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 = WizardryRegistry.none;
return StatCollector.translateToLocalFormatted("item.scroll.name", StatCollector.translateToLocal("spell." + spell.getUnlocalisedName())).trim();
}
public double getConjuredBowDurability(ItemStack stack){
return ((ItemSpectralBow)WizardryItems.spectral_bow).getDefaultDurabilityForDisplay(stack);
}
/**
* Translates the given key with no specified style. Client-side only; on the server this simply returns
* the given translation key. Useful whenever translation from common classes is required, e.g. item tooltips.
* @param key The unlocalised name to be translated.
* @param args The format arguments to pass into the translation, if any.
* @return The resulting translated text.
*/
public String translate(String key, Object... args){
return translate(key, new Style(), args);
}
/**
* Translates the given key and formats it with the given style. Client-side only; on the server this simply returns
* the given translation key. Useful whenever translation from common classes is required, e.g. item tooltips.
* @param key The unlocalised name to be translated.
* @param style The {@link Style} to use for the displayed text.
* @param args The format arguments to pass into the translation, if any.
* @return The resulting translated text.
*/
public String translate(String key, Style style, Object... args){
return key;
}
/** Like {@link CommonProxy#addMultiLineDescription(List, String, Style, Object...)}, but style defaults to light grey. */
public void addMultiLineDescription(List<String> tooltip, String key, Object... args){
this.addMultiLineDescription(tooltip, key, new Style().setColor(TextFormatting.GRAY), args);
}
/**
* 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, Object... args){}
// SECTION Packet Handlers
// ===============================================================================================================
public void handlePlayerSyncPacket(PacketPlayerSync.Message message){}
public void handleGlyphDataPacket(PacketGlyphData.Message message){}
public void handleEmitterDataPacket(PacketEmitterData.Message message){}
public void handleCastSpellPacket(PacketCastSpell.Message message){}
public void handleCastContinuousSpellPacket(PacketCastContinuousSpell.Message message){}
public void handleNPCCastSpellPacket(PacketNPCCastSpell.Message message){}
public void handleDispenserCastSpellPacket(PacketDispenserCastSpell.Message message){}
public void handleCastSpellAtPosPacket(PacketCastSpellAtPos.Message message){}
public void handleTransportationPacket(PacketTransportation.Message message){}
public void setToNumberSliderEntry(Property property){}
public void handleClairvoyancePacket(PacketClairvoyance.Message message){}
public void handleAdvancementSyncPacket(PacketSyncAdvancements.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 setToHUDChooserEntry(Property property){}
public void setToNamedBooleanEntry(Property property){}
// public void setToEntityNameEntry(Property property){}
/**
* Plays a sound which moves with the given entity.
*
* @param entity The source of the sound
* @param sound The SoundEvent to play
* @param 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, SoundCategory category, float volume, float pitch, boolean repeat){}
/**
* Plays the spell charge-up sound at the given entity.
* @param entity The source of the sound
*/
public void playChargeupSound(EntityLivingBase entity){}
/**
* Plays a continuous spell sound which moves with the given entity.
*
* @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);
}
/** Starts the first-person blink overlay effect for the specified player. */
public void playBlinkEffect(EntityPlayer player){}
/**
* Starts the client-side screen shake effect for the specified player.
* @param player The player whose screen is to be shaken
* @param intensity The amplitude of the shaking (around 10 looks about right)
*/
public void shakeScreen(EntityPlayer player, float intensity){}
/**
* Loads the given shader for the given player, if they have shaders enabled.
* @param player The player to load the shader for
* @param shader The location of the shader to load
*/
public void loadShader(EntityPlayer player, ResourceLocation shader){}
/**
* Gets the client-side world using {@code Minecraft.getMinecraft().world}. <b>Only to be called client side!</b>
* Returns null on the server side.
*/
public World getTheWorld(){
return null;
}
/**
* Gets the client-side player using Minecraft.getMinecraft().player. <b>Only to be called client side!</b> Returns
* null on the server side.
*/
public EntityPlayer getThePlayer(){
return null;
}
/**
* Returns true if the game is being viewed from the perspective of the given entity and is set to first-person
* view. Always returns false on the server side.
*/
public boolean isFirstPerson(Entity entity){
return false;
}
/** Returns an unmodifiable set of the string keys for all of the loaded spell HUD skins. */
public Set<String> getSpellHUDSkins(){
return null;
}
/** Notifies nearby players of a bookshelf change, causing any lectern or arcane workbench GUI (client-side) or
* container (both sides) to refresh its linked bookshelves (does not send packets). */
public void notifyBookshelfChange(World world, BlockPos pos){
for(EntityPlayer player : world.playerEntities){
if(player.getDistanceSq(pos) < BlockBookshelf.PLAYER_NOTIFY_RANGE * BlockBookshelf.PLAYER_NOTIFY_RANGE){
if(player.openContainer instanceof ContainerArcaneWorkbench){
((ContainerArcaneWorkbench)player.openContainer).refreshBookshelfSlots();
}
}
}
}
}
@@ -0,0 +1,39 @@
package electroblob.wizardry;
import net.minecraft.util.StatCollector;
public enum EnumElement {
MAGIC("\u00A77", "simple"),
FIRE("\u00A74", "fire"),
ICE("\u00A7b", "ice"),
LIGHTNING("\u00A73", "lightning"),
NECROMANCY("\u00A75", "necromancy"),
EARTH("\u00A72", "earth"),
SORCERY("\u00A7a", "sorcery"),
HEALING("\u00A7e", "healing");
/** Display colour for this element */
public final String colour;
/** Unlocalised name for this element */
public final String unlocalisedName;
private EnumElement(String colour, String name){
this.colour = colour;
this.unlocalisedName = name;
}
public EnumElement get(int id){
return this.values()[id];
}
/** Returns the translated display name of this element, without formatting. */
public String getDisplayName() {
return StatCollector.translateToLocal("element." + unlocalisedName);
}
/** Returns the translated display name for wizards of this element, shown in the trading GUI */
public String getWizardName() {
return StatCollector.translateToLocal("element." + unlocalisedName + ".wizard");
}
}
@@ -0,0 +1,16 @@
package electroblob.wizardry;
public enum EnumParticleType {
BLIZZARD,
BRIGHT_DUST,
DARK_MAGIC,
DUST,
ICE,
LEAF,
MAGIC_BUBBLE,
MAGIC_FIRE,
PATH,
SNOW,
SPARK,
SPARKLE
}
@@ -0,0 +1,21 @@
package electroblob.wizardry;
import net.minecraft.util.StatCollector;
public enum EnumSpellType {
ATTACK("attack"),
DEFENCE("defence"),
UTILITY("utility"),
MINION("minion");
private final String unlocalisedName;
EnumSpellType(String name){
this.unlocalisedName = name;
}
public String getDisplayName() {
return StatCollector.translateToLocal("spelltype." + unlocalisedName);
}
}
@@ -0,0 +1,38 @@
package electroblob.wizardry;
import net.minecraft.util.StatCollector;
public enum EnumTier {
BASIC(700, 3, "\u00A7f", "basic"),
APPRENTICE(1000, 4, "\u00A7b", "apprentice"),
ADVANCED(1500, 5, "\u00A71", "advanced"),
MASTER(2500, 6, "\u00A75", "master");
/** Maximum mana a wand of this tier can store. */
public final int maxCharge;
/** Just an ordinal. Shouldn't really be needed but no point changing it now. */
public final int level;
/** The maximum number of upgrades that can be applied to a wand of this tier. */
public final int upgradeLimit;
/** The colour of text associated with this tier */
public final String colour;
private final String unlocalisedName;
private EnumTier(int maxCharge, int upgradeLimit, String colour, String name){
this.maxCharge = maxCharge;
this.level = ordinal();
this.upgradeLimit = upgradeLimit;
this.colour = colour;
this.unlocalisedName = name;
}
public String getDisplayName() {
return StatCollector.translateToLocal("tier." + unlocalisedName);
}
public String getDisplayNameWithFormatting() {
return this.colour + StatCollector.translateToLocal("tier." + unlocalisedName);
}
}
@@ -0,0 +1,485 @@
package electroblob.wizardry;
import java.lang.ref.WeakReference;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Random;
import java.util.UUID;
import cpw.mods.fml.common.network.simpleimpl.IMessage;
import electroblob.wizardry.MagicDamage.DamageType;
import electroblob.wizardry.entity.EntityShield;
import electroblob.wizardry.entity.living.EntitySummonedCreature;
import electroblob.wizardry.packet.PacketCastContinuousSpell;
import electroblob.wizardry.packet.PacketPlayerSync;
import electroblob.wizardry.packet.PacketTransportation;
import electroblob.wizardry.packet.WizardryPacketHandler;
import electroblob.wizardry.spell.None;
import electroblob.wizardry.spell.Spell;
import net.minecraft.entity.Entity;
import net.minecraft.entity.EntityLivingBase;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.entity.player.EntityPlayerMP;
import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.nbt.NBTTagList;
import net.minecraft.nbt.NBTTagString;
import net.minecraft.potion.Potion;
import net.minecraft.potion.PotionEffect;
import net.minecraft.world.World;
import net.minecraftforge.common.IExtendedEntityProperties;
import net.minecraftforge.common.util.Constants.NBT;
/** IExtendedEntityProperties for players. Everything player-data-related that is needed by wizardry is handled here,
* including the ADS, soulbinding, conjured items, summoned wolves and horses, transportation and clairvoyance. */
public class ExtendedPlayer implements IExtendedEntityProperties {
public final static String name = "WizardryExtendedPlayer";
private final EntityPlayer player;
public int damageToApply;
// These were originally done using the item damage directly, but this caused the annoying re-equip animation
// to keep happening so they were moved to here instead.
public int conjuredSwordDuration;
public int conjuredPickaxeDuration;
public int conjuredBowDuration;
public int conjuredArmourDuration;
public int flamingAxeDuration;
public int frostAxeDuration;
public int magicWeaponDuration;
public int flamingWeaponDuration;
public int freezingWeaponDuration;
public boolean hasSpiritWolf;
public boolean hasSpiritHorse;
/** Whether this player is currently casting a continuous spell via commands. Not saved over world reload
* and reset on player death. */
private Spell currentlyCasting;
/** The time for which this player has been casting a continuous spell via commands. Increments by 1 each tick.
* Not saved over world reload and reset on player death. */
private int castingTick;
/** The damage multiplier for the current continuous spell cast via commands. Not saved over world reload and
* reset on player death. */
private float spellDamageMultiplier;
/** The range multiplier for the current continuous spell cast via commands. Not saved over world reload and
* reset on player death. */
private float spellRangeMultiplier;
/** The duration multiplier for the current continuous spell cast via commands. Not saved over world reload and
* reset on player death. */
private float spellDurationMultiplier;
/** The blast multiplier for the current continuous spell cast via commands. Not saved over world reload and
* reset on player death. */
private float spellBlastMultiplier;
/** Coordinate for the saved transportation stone location. Will be -1 if no location is saved. */
public int transportX, transportY, transportZ;
/** Dimension id which the saved stone circle is in. */
public int transportDimension;
/** Time left until the player teleports under the effect of transportation */
public int tpTimer;
/** Coordinate for the saved clairvoyance location. Will be -1 if no location is saved. */
public int clairvoyanceX, clairvoyanceY, clairvoyanceZ;
/** Dimension id which the saved clairvoyance point is in. */
public int clairvoyanceDimension;
public EntityShield shield;
public WeakReference<EntitySummonedCreature> selectedMinion;
public HashSet<Spell> spellsDiscovered;
private HashSet<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 HashSet<String> allyNames;
private HashSet<UUID> soulboundCreatures;
public ExtendedPlayer(EntityPlayer player){
this.player = player;
this.damageToApply = 0;
this.conjuredSwordDuration = 0;
this.conjuredPickaxeDuration = 0;
this.conjuredBowDuration = 0;
this.conjuredArmourDuration = 0;
this.flamingAxeDuration = 0;
this.frostAxeDuration = 0;
this.magicWeaponDuration = 0;
this.flamingWeaponDuration = 0;
this.freezingWeaponDuration = 0;
this.spellsDiscovered = new HashSet<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(WizardryRegistry.magicMissile);
this.hasSpiritWolf = false;
this.hasSpiritHorse = false;
this.currentlyCasting = WizardryRegistry.none;
this.spellDamageMultiplier = 1;
this.spellRangeMultiplier = 1;
this.spellDurationMultiplier = 1;
this.spellBlastMultiplier = 1;
this.castingTick = 0;
this.transportX = -1;
this.transportY = -1;
this.transportZ = -1;
this.transportDimension = 0;
this.clairvoyanceX = -1;
this.clairvoyanceY = -1;
this.clairvoyanceZ = -1;
this.clairvoyanceDimension = 0;
this.tpTimer = 0;
this.allies = new HashSet<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 spell to the list of discovered spells for this player. Automatically takes into account
* whether the spell has been discovered. Use this method rather than adding directly to the list
* because it handles the mage of all trades achievement.
* @param spell
*/
public void discoverSpell(Spell spell){
if(spellsDiscovered == null){
spellsDiscovered = new HashSet<Spell>();
}
if(!(spell instanceof None)){
spellsDiscovered.add(spell);
}
if(spellsDiscovered.containsAll(Spell.getSpells(Spell.enabledSpells))){
this.player.triggerAchievement(Wizardry.allSpells);
}
for(EnumElement element : EnumElement.values()){
if(element != EnumElement.MAGIC && spellsDiscovered.containsAll(Spell.getSpells(new Spell.TierElementFilter(null, element)))){
this.player.triggerAchievement(Wizardry.elementMaster);
}
}
}
/** Sets the player's saved transportation stone location. */
public void setStoneLocation(int x, int y, int z, int dimensionID){
this.transportX = x;
this.transportY = y;
this.transportZ = z;
this.transportDimension = dimensionID;
}
/** Sets the player's saved clairvoyance location. */
public void setClairvoyancePoint(int x, int y, int z, int dimensionID){
this.clairvoyanceX = x;
this.clairvoyanceY = y;
this.clairvoyanceZ = z;
this.clairvoyanceDimension = dimensionID;
}
/** Adds the given player to the list of allies belonging to the associated player, or removes the player if
* they are already in the list of allies. Returns true if the player was added, false if they were removed. */
public boolean toggleAlly(EntityPlayer player){
if(this.isPlayerAlly(player)){
this.allies.remove(player.getUniqueID());
// The remove method uses .equals() rather than == so this will work fine.
this.allyNames.remove(player.getCommandSenderName());
return false;
}else{
this.allies.add(player.getUniqueID());
this.allyNames.add(player.getCommandSenderName());
return true;
}
}
/** Returns whether the given player is in this player's list of allies, or is on the same team as this player. */
public boolean isPlayerAlly(EntityPlayer player){
return this.allies.contains(player.getUniqueID()) || this.player.isOnSameTeam(player);
}
/** Adds the given entity to this player's list of soulbound creatures, and returns whether it succeeded. */
public boolean soulbind(EntityLivingBase target){
return this.soulboundCreatures.add(target.getUniqueID());
}
/** Returns whether the given entity has been soulbound to this player. */
public boolean isCreatureSoulbound(EntityPlayer target){
return this.soulboundCreatures.contains(target.getUniqueID());
}
/** Damages all creatures soulbound to this player by the given amount, and removes from the list any that no
* longer exist. */
public void damageAllSoulboundCreatures(float damage){
for(Iterator<UUID> iterator = this.soulboundCreatures.iterator(); iterator.hasNext();){
Entity entity = WizardryUtilities.getEntityByUUID(this.player.worldObj, iterator.next());
if(entity == null) iterator.remove();
if(entity instanceof EntityLivingBase){
if(entity.attackEntityFrom(MagicDamage.causeDirectMagicDamage(this.player, DamageType.MAGIC), damage)){
// Sound only plays if the damage succeeds
player.worldObj.playSoundAtEntity(entity, "mob.wither.hurt", 1.0F, player.worldObj.rand.nextFloat() * 0.2F + 1.0F);
}
}
}
}
/** Starts casting the given spell with the given modifiers. */
public void startCastingContinuousSpell(Spell spell, float damageMultiplier, float rangeMultiplier, float durationMultiplier, float blastMultiplier){
this.currentlyCasting = spell;
this.spellDamageMultiplier = damageMultiplier;
this.spellRangeMultiplier = rangeMultiplier;
this.spellDurationMultiplier = durationMultiplier;
this.spellBlastMultiplier = blastMultiplier;
if(!this.player.worldObj.isRemote){
PacketCastContinuousSpell.Message message = new PacketCastContinuousSpell.Message(this.player.getEntityId(), spell.id(), damageMultiplier, rangeMultiplier, blastMultiplier);
WizardryPacketHandler.net.sendToDimension(message, this.player.worldObj.provider.dimensionId);
}
}
/** Stops casting the current spell. */
public void stopCastingContinuousSpell(){
this.currentlyCasting = WizardryRegistry.none;
this.castingTick = 0;
this.spellDamageMultiplier = 1;
this.spellRangeMultiplier = 1;
this.spellDurationMultiplier = 1;
this.spellBlastMultiplier = 1;
if(!this.player.worldObj.isRemote){
PacketCastContinuousSpell.Message message = new PacketCastContinuousSpell.Message(this.player.getEntityId(), WizardryRegistry.none.id(), 1, 1, 1);
WizardryPacketHandler.net.sendToDimension(message, this.player.worldObj.provider.dimensionId);
}
}
/** Returns whether this player is currently casting a continuous spell via commands. */
public boolean isCasting(){
return this.currentlyCasting != null && this.currentlyCasting != WizardryRegistry.none;
}
/** Returns the continuous spell this player is currently casting via commands, or the 'none' spell if they aren't
* casting anything. */
public Spell currentlyCasting(){
return currentlyCasting;
}
/** Called from the event handler each time the associated player is updated. */
public void update(EntityPlayer entityplayer){
if(this.selectedMinion != null && this.selectedMinion.get() == null) this.selectedMinion = null;
Random random = entityplayer.worldObj.rand;
// Annoyingly, it seems that the armour inventory is dealt with completely separately.
if(entityplayer.inventory.hasItem(Wizardry.spectralHelmet)
|| entityplayer.inventory.hasItem(Wizardry.spectralChestplate)
|| entityplayer.inventory.hasItem(Wizardry.spectralLeggings)
|| entityplayer.inventory.hasItem(Wizardry.spectralBoots)
|| (entityplayer.inventory.armorInventory[3] != null && entityplayer.inventory.armorInventory[3].getItem() == Wizardry.spectralHelmet)
|| (entityplayer.inventory.armorInventory[2] != null && entityplayer.inventory.armorInventory[2].getItem() == Wizardry.spectralChestplate)
|| (entityplayer.inventory.armorInventory[1] != null && entityplayer.inventory.armorInventory[1].getItem() == Wizardry.spectralLeggings)
|| (entityplayer.inventory.armorInventory[0] != null && entityplayer.inventory.armorInventory[0].getItem() == Wizardry.spectralBoots)){
this.conjuredArmourDuration++;
}else{
this.conjuredArmourDuration = 0;
}
if(!entityplayer.worldObj.isRemote){
if(tpTimer == 1){
entityplayer.setPositionAndUpdate(this.transportX + 0.5, this.transportY, this.transportZ + 0.5);
entityplayer.worldObj.playSoundAtEntity(entityplayer, "portal.travel", 1.0f, 1.0f);
entityplayer.addPotionEffect(new PotionEffect(Potion.blindness.id, 50, 0));
IMessage msg = new PacketTransportation.Message(entityplayer.getEntityId());
WizardryPacketHandler.net.sendToDimension(msg, entityplayer.worldObj.provider.dimensionId);
}
if(tpTimer > 0){
tpTimer--;
}
}
if(this.currentlyCasting != null && this.currentlyCasting.isContinuous){
this.currentlyCasting.cast(player.worldObj, player, castingTick++, spellDamageMultiplier, spellRangeMultiplier, spellDurationMultiplier, spellBlastMultiplier);
}else{
this.castingTick = 0;
}
}
/** Called from the event handler each time the associated player dies. Used to reset any variables that shouldn't
* persist over player death. */
public void onPlayerDeath(){
this.conjuredSwordDuration = 0;
this.conjuredPickaxeDuration = 0;
this.conjuredBowDuration = 0;
this.conjuredArmourDuration = 0;
this.flamingAxeDuration = 0;
this.frostAxeDuration = 0;
this.magicWeaponDuration = 0;
this.flamingWeaponDuration = 0;
this.freezingWeaponDuration = 0;
// Death cancels transportation spell.
this.tpTimer = 0;
this.soulboundCreatures = new HashSet<UUID>();
this.currentlyCasting = WizardryRegistry.none;
this.castingTick = 0;
this.spellDamageMultiplier = 1;
this.spellRangeMultiplier = 1;
this.spellDurationMultiplier = 1;
this.spellBlastMultiplier = 1;
}
/**
* Returns the string key to be used when saving and loading the extended player data on respawn.
*/
public static String getSaveKey(EntityPlayer player) {
return player.getUniqueID().toString() + ":" + name;
}
/**
* Used to register these extended properties for the player during EntityConstructing event
* This method is for convenience only; it will make your code look nicer
*/
public static final void register(EntityPlayer player){
player.registerExtendedProperties(ExtendedPlayer.name, new ExtendedPlayer(player));
}
/**
* Returns the ExtendedPlayer properties for the specified player.
*/
public static final ExtendedPlayer get(EntityPlayer player){
return (ExtendedPlayer) player.getExtendedProperties(name);
}
/** Sends a packet to this player's client to synchronise necessary information. Only called server side. */
public void sync(){
if(this.player instanceof EntityPlayerMP){
int id = -1;
if(this.selectedMinion != null && this.selectedMinion.get() != null) id = this.selectedMinion.get().getEntityId();
IMessage msg = new PacketPlayerSync.Message(this.spellsDiscovered, id);
WizardryPacketHandler.net.sendTo(msg, (EntityPlayerMP)this.player);
}
}
@Override
public void saveNBTData(NBTTagCompound compound) {
// NBTTagCompound is for Maps, NBTTagList is for Lists/Sets
NBTTagCompound properties = new NBTTagCompound();
properties.setInteger("conjuredSwordDuration", this.conjuredSwordDuration);
properties.setInteger("conjuredPickaxeDuration", this.conjuredPickaxeDuration);
properties.setInteger("conjuredBowDuration", this.conjuredBowDuration);
properties.setInteger("conjuredArmourDuration", this.conjuredArmourDuration);
properties.setInteger("flamingAxeDuration", this.flamingAxeDuration);
properties.setInteger("frostAxeDuration", this.frostAxeDuration);
properties.setInteger("magicWeaponDuration", this.magicWeaponDuration);
properties.setInteger("flamingWeaponDuration", this.flamingWeaponDuration);
properties.setInteger("freezingWeaponDuration", this.freezingWeaponDuration);
properties.setBoolean("hasSpiritWolf", this.hasSpiritWolf);
properties.setBoolean("hasSpiritHorse", this.hasSpiritHorse);
properties.setInteger("x", this.transportX);
properties.setInteger("y", this.transportY);
properties.setInteger("z", this.transportZ);
properties.setInteger("dimension", this.transportDimension);
properties.setInteger("x2", this.clairvoyanceX);
properties.setInteger("y2", this.clairvoyanceY);
properties.setInteger("z2", this.clairvoyanceZ);
properties.setInteger("dimension2", this.clairvoyanceDimension);
properties.setInteger("tpTimer", this.tpTimer);
NBTTagList tagList = new NBTTagList();
for(UUID id : this.allies){
tagList.appendTag(new NBTTagString(id.toString()));
}
properties.setTag("allies", tagList);
NBTTagList tagList2 = new NBTTagList();
for(String name : this.allyNames){
tagList2.appendTag(new NBTTagString(name));
}
properties.setTag("allyNames", tagList2);
NBTTagList tagList3 = new NBTTagList();
for(UUID id : this.soulboundCreatures){
tagList3.appendTag(new NBTTagString(id.toString()));
}
properties.setTag("soulboundCreatures", tagList3);
int[] spells = new int[this.spellsDiscovered.size()];
int i=0;
for(Spell spell : this.spellsDiscovered){
spells[i] = spell.id();
i++;
}
properties.setIntArray("discoveredSpells", spells);
compound.setTag(name, properties);
}
@Override
public void loadNBTData(NBTTagCompound compound) {
NBTTagCompound properties = (NBTTagCompound) compound.getTag(name);
if(properties != null){
this.conjuredSwordDuration = properties.getInteger("conjuredSwordDuration");
this.conjuredPickaxeDuration = properties.getInteger("conjuredPickaxeDuration");
this.conjuredBowDuration = properties.getInteger("conjuredBowDuration");
this.conjuredArmourDuration = properties.getInteger("conjuredArmourDuration");
this.flamingAxeDuration = properties.getInteger("flamingAxeDuration");
this.frostAxeDuration = properties.getInteger("frostAxeDuration");
this.magicWeaponDuration = properties.getInteger("magicWeaponDuration");
this.flamingWeaponDuration = properties.getInteger("flamingWeaponDuration");
this.freezingWeaponDuration = properties.getInteger("freezingWeaponDuration");
this.hasSpiritWolf = properties.getBoolean("hasSpiritWolf");
this.hasSpiritHorse = properties.getBoolean("hasSpiritHorse");
this.transportX = properties.getInteger("x");
this.transportY = properties.getInteger("y");
this.transportZ = properties.getInteger("z");
this.transportDimension = properties.getInteger("dimension");
this.clairvoyanceX = properties.getInteger("x2");
this.clairvoyanceY = properties.getInteger("y2");
this.clairvoyanceZ = properties.getInteger("z2");
this.clairvoyanceDimension = properties.getInteger("dimension2");
this.tpTimer = properties.getInteger("tpTimer");
this.allies = new HashSet<UUID>();
NBTTagList tagList = properties.getTagList("allies", NBT.TAG_STRING);
for(int i=0; i<tagList.tagCount(); i++){
allies.add(UUID.fromString(tagList.getStringTagAt(i)));
}
this.allyNames = new HashSet<String>();
NBTTagList tagList2 = properties.getTagList("allyNames", NBT.TAG_STRING);
for(int i=0; i<tagList2.tagCount(); i++){
allyNames.add(tagList2.getStringTagAt(i));
}
this.soulboundCreatures = new HashSet<UUID>();
NBTTagList tagList3 = properties.getTagList("soulboundCreatures", NBT.TAG_STRING);
for(int i=0; i<tagList3.tagCount(); i++){
soulboundCreatures.add(UUID.fromString(tagList3.getStringTagAt(i)));
}
this.spellsDiscovered = new HashSet<Spell>();
for(int id : properties.getIntArray("discoveredSpells")){
spellsDiscovered.add(Spell.get(id));
}
}
}
@Override
public void init(Entity entity, World world) {
// For reference, apparently there is a strange error that causes 'this' to be null here.
}
}
@@ -0,0 +1,16 @@
package electroblob.wizardry;
import electroblob.wizardry.MagicDamage.DamageType;
import net.minecraft.entity.Entity;
import scala.actors.threadpool.Arrays;
/** This interface allows {@link MagicDamage} and {@link IndirectMagicDamage} to both be treated as instances of a
* single type so that the damage type field can be accessed, rather than having to deal with each of them separately,
* which would be inefficient and cumbersome (the latter of those classes cannot extend the former because they both
* need to extend different subclasses of {@link net.minecraft.util.DamageSource DamageSource}).
* @since Wizardry 1.1 */
public interface IElementalDamage {
DamageType getType();
}
@@ -0,0 +1,21 @@
package electroblob.wizardry;
import electroblob.wizardry.MagicDamage.DamageType;
import net.minecraft.entity.Entity;
import net.minecraft.util.EntityDamageSourceIndirect;
public class IndirectMagicDamage extends EntityDamageSourceIndirect implements IElementalDamage {
private final DamageType type;
public IndirectMagicDamage(String name, Entity magic, Entity caster, DamageType type) {
super(name, magic, caster);
this.type = type;
}
@Override
public DamageType getType(){
return type;
}
}
@@ -0,0 +1,216 @@
package electroblob.wizardry;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import electroblob.wizardry.entity.living.EntityBlazeMinion;
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.EntitySkeletonMinion;
import electroblob.wizardry.entity.living.EntitySpiderMinion;
import electroblob.wizardry.entity.living.EntityStormElemental;
import electroblob.wizardry.entity.living.EntityZombieMinion;
import net.minecraft.entity.Entity;
import net.minecraft.entity.EntityLivingBase;
import net.minecraft.entity.boss.EntityDragon;
import net.minecraft.entity.boss.EntityWither;
import net.minecraft.entity.monster.EntityBlaze;
import net.minecraft.entity.monster.EntityCaveSpider;
import net.minecraft.entity.monster.EntityGhast;
import net.minecraft.entity.monster.EntityMagmaCube;
import net.minecraft.entity.monster.EntityPigZombie;
import net.minecraft.entity.monster.EntitySkeleton;
import net.minecraft.entity.monster.EntitySnowman;
import net.minecraft.entity.monster.EntitySpider;
import net.minecraft.entity.monster.EntityZombie;
import net.minecraft.util.DamageSource;
import net.minecraft.util.EntityDamageSource;
// A note on the use of the vanilla damagesources:
// When using indirect damage sources, the SECOND argument is the original entity (i.e. the caster), and the
// FIRST argument is the actual projectile or whatever that does the damage. getEntity() will return
// the original entity, and getSourceOfDamage() will return the projectile.
// The vanilla approach to damage types is inconsistent, to say the least. Poison is simply 'magic', and relies on
// EntityLivingBase.isPotionApplicable to determine whether an entity is affected or not. Wither, on the other hand, is
// its own damage type, but again relies on the potion to determine what is immune (which in this case is nothing). Fire
// has a proper implementation of course, with both block-based and projectile-based types, and any other damagesource
// can also be designated as fire damage with setFireDamage(). Likewise, projectile and explosion damage can also be
// applied to any damagesource, but these aren't condsidered types in their own right; rather, they seem to be damage
// type 'attributes'.
/** As of wizardry 1.1, this class has replaced the damagesource-related methods in WizardryUtilities, allowing a
* {@link DamageType} to be specified with the damage. The main reason for this is so that damage sources can fit with the
* vanilla behaviour on armour enchantments and such like whilst still being classified as wizardry damage for the purposes
* of friendly fire, etc.
* <p><i>
* In the future, there is scope for an entirely standalone mod based on this idea. Perhaps 'Elemental Damage' could be
* a config-only mod which allows its users to define any number of specific damage types, then give any creature a
* resistance, immunity or vulnerability to each, as well as being able to specify the sources for each type, such as
* enchantments, potions, specific items, certain entities, even particular situations and string damagesource names
* from other mods.
* </i>
* @see IndirectMagicDamage
* @see IElementalDamage
* @since Wizardry 1.1 */
public class MagicDamage extends EntityDamageSource implements IElementalDamage {
/** The name of the damagesource for direct magic damage from the wizardry mod. Defined here so that any
* references get changed automatically. */
public static final String DIRECT_MAGIC_DAMAGE = "wizardryMagic";
/** The name of the damagesource for indirect magic damage from the wizardry mod. Defined here so that any
* references get changed automatically. */
public static final String INDIRECT_MAGIC_DAMAGE = "indirectWizardryMagic";
// Technically, I don't need to specify that the classes in this map must extend entity, but it's good practice to.
private static final Map<Class<? extends Entity>, DamageType[]> immunityMapping = new HashMap<Class<? extends Entity>, DamageType[]>();
private final DamageType type;
/** A simple set of constants for the types of damage. The names are deliberately different to those in EnumElement
* to avoid confusion. All types are classified as magic damage in the vanilla system, so witches are resistant to them. */
public enum DamageType {
/** Generic magic damage from the wizardry mod. Like vanilla magic damage, except it doesn't bypass armour. */
MAGIC,
/** Fire damage from the wizardry mod. Counts as fire damage in the vanilla system, so is blocked by any mobs that
* are immune to fire and entities with the fire resistance effect. */
FIRE,
/** Frost (ice) damage from the wizardry mod. Snow golems, ice wraiths and ice giants are immune. */
FROST,
/** Shock (lightning) damage from the wizardry mod. Lightning wraiths and storm elementals are immune. */
SHOCK,
/** Wither damage from the wizardry mod. Withers, wither skeletons and shadow wraiths are immune. */
WITHER,
/** Poison damage from the wizardry mod. Spiders, cave spiders and undead mobs are immune. */
POISON,
/** Force damage from the wizardry mod. Insubstantial creatures (ghast, shadow wraith, etc.) are immune. */
FORCE,
/** Blast damage from the wizardry mod. Affected by the blast protection enchantment. */
BLAST,
/** Radiant damage from the wizardry mod. */
RADIANT;
}
static {
// Of course, the entities that are immune to fire already are since there's a vanilla system for that, but
// they're included here anyway for completeness and in case anyone wants to check if an entity is immune to
// an unspecified element for reasons other than dealing damage.
setEntityImmunities(EntityPhoenix.class, DamageType.FIRE);
setEntityImmunities(EntityBlaze.class, DamageType.FIRE);
setEntityImmunities(EntityBlazeMinion.class, DamageType.FIRE);
setEntityImmunities(EntityPigZombie.class, DamageType.FIRE, DamageType.POISON);
setEntityImmunities(EntityMagmaCube.class, DamageType.FIRE);
setEntityImmunities(EntityGhast.class, DamageType.FIRE);
setEntityImmunities(EntityDragon.class, DamageType.FIRE);
setEntityImmunities(EntityStormElemental.class, DamageType.FIRE, DamageType.SHOCK);
setEntityImmunities(EntityWither.class, DamageType.FIRE, DamageType.WITHER);
setEntityImmunities(EntitySnowman.class, DamageType.FROST);
setEntityImmunities(EntityIceWraith.class, DamageType.FROST);
setEntityImmunities(EntityIceGiant.class, DamageType.FROST);
setEntityImmunities(EntityLightningWraith.class, DamageType.SHOCK);
setEntityImmunities(EntityShadowWraith.class, DamageType.WITHER);
setEntityImmunities(EntitySpider.class, DamageType.POISON);
setEntityImmunities(EntitySpiderMinion.class, DamageType.POISON);
setEntityImmunities(EntityCaveSpider.class, DamageType.POISON);
setEntityImmunities(EntityZombie.class, DamageType.POISON);
setEntityImmunities(EntitySkeleton.class, DamageType.POISON);
setEntityImmunities(EntityZombieMinion.class, DamageType.POISON);
setEntityImmunities(EntitySkeletonMinion.class, DamageType.POISON);
}
public MagicDamage(String name, Entity caster, DamageType type) {
super(name, caster);
this.type = type;
this.setMagicDamage();
if(type == DamageType.FIRE) this.setFireDamage();
if(type == DamageType.BLAST) this.setExplosion();
}
/** Returns true if the given entity is immune to the given damage type according to the entity immunity mappings,
* false otherwise. When you want to check for resistances, check this method rather than just checking the result
* of attackEntityFrom, since that could return false for all sorts of reasons besides immunities. However, if you
* don't need to know whether the damage succeeded or not, there's no point in checking this method. A common use
* of this method is to check for immunity and if so display the "[mob] resisted [spell]" chat message. See
* {@link electroblob.wizardry.spell.Arc Arc} for a good example of this, and also of when not to use it. */
public static boolean isEntityImmune(DamageType type, Entity entity){
// Because Mojang, in their infinite wisdom, did not make wither skeletons their own separate class (despite the
// fact that cave spiders are), I have to test for this manually. Realistically, no mod author would be stupid
// enough to put two entities in one class, so this should be the only time I ever have to do this.
if(type == DamageType.WITHER && (
(entity instanceof EntitySkeleton && ((EntitySkeleton)entity).getSkeletonType() == 1)
|| (entity instanceof EntitySkeletonMinion && ((EntitySkeletonMinion)entity).getSkeletonType() == 1))){
return true;
}
if(type == DamageType.FIRE && entity.isImmuneToFire()) return true;
DamageType[] immunities = MagicDamage.immunityMapping.get(entity.getClass());
return immunities != null && Arrays.asList(immunities).contains(type);
}
// I found a use for varargs!
/** Registers the given type of entity as immune to all of the passed in damage types. */
public static void setEntityImmunities(Class<? extends Entity> entityType, DamageType... immunities){
immunityMapping.put(entityType, immunities);
}
/** Registers the given type of entity as immune to all of the passed in damage types. */
public static void addEntityImmunity(Class<? extends Entity> entityType, DamageType immunity){
List<DamageType> immunities = Arrays.asList(immunityMapping.get(entityType));
immunities.add(immunity);
// Apparently putting 0 here works just fine.
immunityMapping.put(entityType, immunities.toArray(new DamageType[0]));
}
/**
* Returns a DamageSource called "wizardryMagic" with the given entity as the caster. Use in preference to vanilla
* types to allow things to distinguish between magic and regular melee/swords. Unlike DamageSource.magic, it does
* not bypass armour and has a player as the source (rather than nothing). It is still classed as magic damage (for
* the record, all this does in vanilla is make witches 85% resistant to it - but that seems kinda right anyway).
* <p>
* Now that this is its own class, this static method is largely redundant, but it's not worth refactoring the entire
* mod just to get rid of this method and use the constructor instead.
* @param caster The player or other living entity causing the damage
* @param type The type that this damage belongs to; used for resistances and wand perks. Use
* {@link EnumElement#MAGIC} for regular, non-elemental magic damage (sometimes you might not want an element
* even though the spell has one - for example, not all necromancy spells are 'withery', so some of them might
* reasonably affect creatures that are ususally immune to wither effects).
* @return A damagesource object of type EntityDamageSource
*/
public static DamageSource causeDirectMagicDamage(EntityLivingBase caster, DamageType type)
{
return new MagicDamage(DIRECT_MAGIC_DAMAGE, caster, type);
}
/**
* Returns a DamageSource called "indirectWizardryMagic" with the player as the caster. Use in preference to vanilla
* types to allow things to distinguish between magic and regular arrows/throwables. Unlike
* DamageSource.causeIndirectMagicDamage, it does not bypass armour. It is still classed as magic damage (for the
* record, all this does in vanilla is make witches 85% resistant to it - but that seems kinda right anyway).
* <p>
* Now that this is its own class, this static method is largely redundant, but it's not worth refactoring the entire
* mod just to get rid of this method and use the constructor instead.
* @param magic The entity that actually caused the damage
* @param caster The player or other living entity that cast the spell originally
* @param type The type that this damage belongs to; used for resistances and wand perks. Use
* {@link EnumElement#MAGIC} for regular, non-elemental magic damage (sometimes you might not want an element
* even though the spell has one - for example, not all necromancy spells are 'withery', so some of them might
* reasonably affect creatures that are ususally immune to wither effects).
* @return A damagesource object of type EntityDamageSourceIndirect
*/
public static DamageSource causeIndirectEntityMagicDamage(Entity magic, EntityLivingBase caster, DamageType type)
{
return new IndirectMagicDamage(INDIRECT_MAGIC_DAMAGE, magic, caster, type);
}
@Override
public DamageType getType(){
return type;
}
}
File diff suppressed because it is too large Load Diff
@@ -1,142 +1,124 @@
package electroblob.wizardry.data;
package electroblob.wizardry;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Random;
import org.apache.commons.lang3.RandomStringUtils;
import electroblob.wizardry.Wizardry;
import electroblob.wizardry.packet.PacketGlyphData;
import electroblob.wizardry.packet.WizardryPacketHandler;
import electroblob.wizardry.spell.Spell;
import electroblob.wizardry.util.NBTExtras;
import net.minecraft.entity.player.EntityPlayerMP;
import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.nbt.NBTTagList;
import net.minecraft.world.World;
import net.minecraft.world.storage.WorldSavedData;
import net.minecraft.world.WorldSavedData;
import net.minecraftforge.common.util.Constants.NBT;
import net.minecraftforge.event.world.WorldEvent;
import net.minecraftforge.fml.common.Mod;
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
import org.apache.commons.lang3.RandomStringUtils;
import java.util.*;
/**
* Class responsible for generating and storing the randomised spell names and descriptions for each world, which are
/** Class responsible for generating and storing the randomised spell names and descriptions for each world, which are
* displayed as glyphs using the SGA font renderer.
*
* @since Wizardry 1.1
*/
@Mod.EventBusSubscriber
* @since Wizardry 1.1 */
public class SpellGlyphData extends WorldSavedData {
public static final String NAME = Wizardry.MODID + "_glyphData";
public Map<Spell, String> randomNames = new HashMap<>(Spell.getTotalSpellCount());
public Map<Spell, String> randomDescriptions = new HashMap<>(Spell.getTotalSpellCount());
private static final String NAME = Wizardry.MODID + "_glyphData";
public Map<Spell, String> randomNames = new HashMap<Spell, String>(Spell.getTotalSpellCount());
public Map<Spell, String> randomDescriptions = new HashMap<Spell, String>(Spell.getTotalSpellCount());
// Required constructors
public SpellGlyphData(){
public SpellGlyphData() {
this(NAME);
}
public SpellGlyphData(String name){
super(name);
}
/** Generates random names and descriptions for any spells which don't already have them. */
public void generateGlyphNames(World world){
for(Spell spell : Spell.getAllSpells()){
for(Spell spell : Spell.getSpells(Spell.allSpells)){
if(!randomNames.containsKey(spell)) randomNames.put(spell, generateRandomName(world.rand));
}
for(Spell spell : Spell.getAllSpells()){
if(!randomDescriptions.containsKey(spell))
randomDescriptions.put(spell, generateRandomDescription(world.rand));
for(Spell spell : Spell.getSpells(Spell.allSpells)){
if(!randomDescriptions.containsKey(spell)) randomDescriptions.put(spell, generateRandomDescription(world.rand));
}
this.markDirty();
}
private String generateRandomName(Random random){
String name = "";
for(int i = 0; i < random.nextInt(2) + 2; i++){
for(int i=0; i<random.nextInt(2)+2; i++){
name = name + RandomStringUtils.random(3 + random.nextInt(5), "abcdefghijklmnopqrstuvwxyz") + " ";
}
return name.trim();
}
private String generateRandomDescription(Random random){
String name = "";
for(int i = 0; i < random.nextInt(16) + 8; i++){
for(int i=0; i<random.nextInt(16)+8; i++){
name = name + RandomStringUtils.random(2 + random.nextInt(7), "abcdefghijklmnopqrstuvwxyz") + " ";
}
return name.trim();
}
/**
* Returns the spell glyph data for this world, or creates a new instance if it doesn't exist yet. Also checks for
* any spells that are missing glyph data and adds it accordingly.
*/
public static SpellGlyphData get(World world){
/** Returns the spell glyph data for this world, or creates a new instance if it doesn't exist yet. Also checks
* for any spells that are missing glyph data and adds it accordingly. */
public static SpellGlyphData get(World world) {
SpellGlyphData instance = (SpellGlyphData)world.loadData(SpellGlyphData.class, NAME);
SpellGlyphData instance = (SpellGlyphData) world.loadItemData(SpellGlyphData.class, NAME);
if(instance == null){
instance = new SpellGlyphData();
}
// These two conditions are a bit of backwards compatibility from when I added the descriptions to the
// glyph data. Shouldn't be needed in normal operation, but I might as well leave it here.
// Edit: More backwards compatibility, this time for the future - should any new spells be added, this now
// ensures
// Edit: More backwards compatibility, this time for the future - should any new spells be added, this now ensures
// existing worlds will generate random names and descriptions for any new spells whilst keeping the old ones.
if(instance.randomNames.size() < Spell.getTotalSpellCount()
|| instance.randomDescriptions.size() < Spell.getTotalSpellCount()){
if(instance.randomNames.size() < Spell.getTotalSpellCount() || instance.randomDescriptions.size() < Spell.getTotalSpellCount()){
instance.generateGlyphNames(world);
world.setData(NAME, instance);
world.setItemData(NAME, instance);
}
return instance;
}
/** Sends the random spell names for this world to the specified player's client. */
public void sync(EntityPlayerMP player){
List<String> names = new ArrayList<>();
List<String> descriptions = new ArrayList<>();
int id = 0;
while(id < Spell.getTotalSpellCount()){
Spell spell = Spell.byNetworkID(id + 1); // +1 because the None spell is not included
List<String> names = new ArrayList<String>();
List<String> descriptions = new ArrayList<String>();
for(Spell spell : Spell.getSpells(Spell.allSpells)){
names.add(this.randomNames.get(spell));
descriptions.add(this.randomDescriptions.get(spell));
id++;
}
PacketGlyphData.Message msg = new PacketGlyphData.Message(names, descriptions);
WizardryPacketHandler.net.sendTo(msg, player);
Wizardry.logger.info("Synchronising spell glyph data for " + player.getName());
}
/** Helper method to retrieve the random glyph name for the given spell from the map stored in the given world. */
public static String getGlyphName(Spell spell, World world){
Map<Spell, String> names = SpellGlyphData.get(world).randomNames;
return names == null ? "" : names.get(spell);
}
/**
* Helper method to retrieve the random glyph description for the given spell from the map stored in the given
* world.
*/
/** Helper method to retrieve the random glyph description for the given spell from the map stored in the given world. */
public static String getGlyphDescription(Spell spell, World world){
Map<Spell, String> descriptions = SpellGlyphData.get(world).randomDescriptions;
return descriptions == null ? "" : descriptions.get(spell);
@@ -144,44 +126,34 @@ public class SpellGlyphData extends WorldSavedData {
@Override
public void readFromNBT(NBTTagCompound nbt){
this.randomNames = new HashMap<>();
this.randomDescriptions = new HashMap<>();
this.randomNames = new HashMap<Spell, String>();
this.randomDescriptions = new HashMap<Spell, String>();
NBTTagList tagList = nbt.getTagList("spellGlyphData", NBT.TAG_COMPOUND);
for(int i = 0; i < tagList.tagCount(); i++){
for(int i=0; i<tagList.tagCount(); i++){
NBTTagCompound tag = tagList.getCompoundTagAt(i);
randomNames.put(Spell.byMetadata(tag.getInteger("spell")), tag.getString("name"));
randomDescriptions.put(Spell.byMetadata(tag.getInteger("spell")), tag.getString("description"));
randomNames.put(Spell.get(tag.getInteger("spell")), tag.getString("name"));
randomDescriptions.put(Spell.get(tag.getInteger("spell")), tag.getString("description"));
}
}
@Override
public NBTTagCompound writeToNBT(NBTTagCompound nbt){
public void writeToNBT(NBTTagCompound nbt){
NBTTagList tagList = new NBTTagList();
for(Spell spell : Spell.getAllSpells()){
for(Spell spell : Spell.getSpells(Spell.allSpells)){
// Much like the enchantments tag for items, this stores a list of spell-id-to-name tag pairs
// The description is now also included; there's no point in making a second compound tag!
NBTTagCompound tag = new NBTTagCompound();
tag.setInteger("spell", spell.metadata());
tag.setInteger("spell", spell.id());
tag.setString("name", this.randomNames.get(spell));
tag.setString("description", this.randomDescriptions.get(spell));
tagList.appendTag(tag);
}
NBTExtras.storeTagSafely(nbt, "spellGlyphData", tagList);
return nbt;
}
@SubscribeEvent
public static void onWorldLoadEvent(WorldEvent.Load event){
if(!event.getWorld().isRemote && event.getWorld().provider.getDimension() == 0){
// Called to initialise the spell glyph data when a world loads, if it isn't already.
SpellGlyphData.get(event.getWorld());
}
nbt.setTag("spellGlyphData", tagList);
}
}
@@ -0,0 +1,277 @@
package electroblob.wizardry;
import java.util.HashMap;
import java.util.Set;
import electroblob.wizardry.spell.Spell;
import net.minecraft.item.Item;
import net.minecraft.item.ItemStack;
import net.minecraft.nbt.NBTTagCompound;
/** Much like {@link net.minecraft.enchantment.EnchantmentHelper EnchantmentHelper}, this class has some static methods
* which allow cleaner and more concise interaction with the wand NBT data, which is quite a complex structure. Such
* interaction previously resulted in rather verbose and repetitive code which was hard to read and even harder to debug!
* For example, this class allowed {@link electroblob.wizardry.item.ItemWand ItemWand} to be shortened by about 80 lines.
* In addition, by having all the various null checks and array size checks in one place, the chance of accidental errors
* due to forgetting to check these things is greatly reduced.
* <p>
* Note that these methods contain no game logic at all; they are purely for interacting with the NBT data. Conversely,
* you should never need to access the wand's NBT data directly when using this class, but the keys are public in the
* unlikely case that this is necessary.
* <p>
* Also note that none of the methods in this class actually check that the given ItemStack contains an ItemWand; you
* can, for example, pass in a stack of snowballs without causing problems, but that is of course pointless!
* <p>
* All <b>get</b> methods in this class return some kind of default if the passed-in wand stack has no nbt data.<br>
* All <b>set</b> methods in this class create a new nbt data for the passed-in wand if it has none, before doing
* whatever else they do.
* @see electroblob.wizardry.item.ItemWand ItemWand
* @see electroblob.wizardry.packet.PacketControlInput PacketControlInput
* @since Wizardry 1.1 */
public class WandHelper {
// NBT tag keys
public static final String SPELL_ARRAY_KEY = "spells";
public static final String SELECTED_SPELL_KEY = "selectedSpell";
public static final String COOLDOWN_ARRAY_KEY = "cooldown";
public static final String UPGRADES_KEY = "upgrades";
private static final HashMap<Item, String> upgradeMap = new HashMap<Item, String>();
static {
upgradeMap.put(Wizardry.condenserUpgrade, "condenser");
upgradeMap.put(Wizardry.storageUpgrade, "storage");
upgradeMap.put(Wizardry.siphonUpgrade, "siphon");
upgradeMap.put(Wizardry.rangeUpgrade, "range");
upgradeMap.put(Wizardry.durationUpgrade, "duration");
upgradeMap.put(Wizardry.cooldownUpgrade, "cooldown");
upgradeMap.put(Wizardry.blastUpgrade, "blast");
upgradeMap.put(Wizardry.attunementUpgrade, "attunement");
}
/** Returns an array containing the spells currently bound to the given wand. As of Wizardry 1.1, this array is not
* always the same size; it can be anywhere between 5 and 8 (inclusive) in length. If the wand has no spell data,
* returns an array of length 0. */
public static Spell[] getSpells(ItemStack wand){
Spell[] spells = new Spell[0];
if(wand.stackTagCompound != null){
int[] spellIDs = wand.stackTagCompound.getIntArray(SPELL_ARRAY_KEY);
spells = new Spell[spellIDs.length];
for(int i=0; i<spellIDs.length; i++){
spells[i] = Spell.get(spellIDs[i]);
}
}
return spells;
}
/** Binds the given array of spells to the given wand. The array can be anywhere between 5 and 8 (inclusive) in length. */
public static void setSpells(ItemStack wand, Spell[] spells){
if(wand.stackTagCompound == null) wand.stackTagCompound = new NBTTagCompound();
int[] spellIDs = new int[spells.length];
for(int i=0; i<spells.length; i++){
spellIDs[i] = spells[i] != null ? spells[i].id() : WizardryRegistry.none.id();
}
wand.stackTagCompound.setIntArray(SPELL_ARRAY_KEY, spellIDs);
}
/** Returns the currently selected spell for the given wand, or the 'none' spell if the wand has no spell data. */
public static Spell getCurrentSpell(ItemStack wand){
Spell[] spells = getSpells(wand);
if(wand.stackTagCompound != null){
int selectedSpell = wand.stackTagCompound.getInteger(SELECTED_SPELL_KEY);
if(selectedSpell < spells.length && selectedSpell >= 0){
return spells[selectedSpell];
}
}
return WizardryRegistry.none;
}
/** Selects the next spell in this wand's list of spells. */
public static void selectNextSpell(ItemStack wand){
// 5 here because if the spell array doesn't exist, the wand can't possibly have attunement upgrades
if(getSpells(wand).length < 0) setSpells(wand, new Spell[5]);
if(wand.stackTagCompound != null){
int numberOfSpells = getSpells(wand).length;
int selectedSpell = wand.stackTagCompound.getInteger(SELECTED_SPELL_KEY);
// Greater than or equal to so that if attunement upgrades are somehow removed by NBT modification it just
// resets.
if(selectedSpell >= numberOfSpells - 1){
selectedSpell = 0;
}else{
selectedSpell++;
}
wand.stackTagCompound.setInteger(SELECTED_SPELL_KEY, selectedSpell);
}
}
/** Selects the previous spell in this wand's list of spells. */
public static void selectPreviousSpell(ItemStack wand){
// 5 here because if the spell array doesn't exist, the wand can't possibly have attunement upgrades
if(getSpells(wand).length < 0) setSpells(wand, new Spell[5]);
// This cannot possibly be null here, and yet I am getting an NPE...
if(wand.stackTagCompound != null){
int numberOfSpells = getSpells(wand).length;
int selectedSpell = wand.stackTagCompound.getInteger(SELECTED_SPELL_KEY);
if(selectedSpell <= 0){
selectedSpell = numberOfSpells - 1;
}else{
selectedSpell--;
}
wand.stackTagCompound.setInteger(SELECTED_SPELL_KEY, selectedSpell);
}
}
/** Returns an array of the cooldowns for each spell bound to the given wand. As of Wizardry 1.1, this array is not
* always the same size; it can be anywhere between 5 and 8 (inclusive) in length. If the wand has no cooldown data,
* returns an array of length 0. */
public static int[] getCooldowns(ItemStack wand){
int[] cooldowns = new int[0];
if(wand.stackTagCompound != null){
return wand.stackTagCompound.getIntArray(COOLDOWN_ARRAY_KEY);
}
return cooldowns;
}
/** Sets the given wand's cooldown array. The array can be anywhere between 5 and 8 (inclusive) in length. */
public static void setCooldowns(ItemStack wand, int[] cooldowns){
if(wand.stackTagCompound == null) wand.stackTagCompound = new NBTTagCompound();
wand.stackTagCompound.setIntArray(COOLDOWN_ARRAY_KEY, cooldowns);
}
/** Decrements the cooldown for each spell bound to the given wand by 1, if that cooldown is greater than 0. */
public static void decrementCooldowns(ItemStack wand){
int[] cooldowns = getCooldowns(wand);
// If there are no cooldowns, it is assumed that they are all zero and therefore nothing needs to be done.
if(cooldowns.length == 0) return;
for(int i=0; i<cooldowns.length; i++){
if(cooldowns[i] > 0) cooldowns[i]--;
}
setCooldowns(wand, cooldowns);
}
/** Returns the given wand's cooldown for the currently selected spell, or 0 if the wand has no cooldown data. */
public static int getCurrentCooldown(ItemStack wand){
int[] cooldowns = getCooldowns(wand);
if(cooldowns.length == 0) return 0;
// Don't need to check if the tag compound is null since the above check is equivalent.
return cooldowns[wand.stackTagCompound.getInteger(SELECTED_SPELL_KEY)];
}
/** Sets the given wand's cooldown for the currently selected spell. */
public static void setCurrentCooldown(ItemStack wand, int cooldown){
if(wand.stackTagCompound == null) wand.stackTagCompound = new NBTTagCompound();
int[] cooldowns = getCooldowns(wand);
// The length of the spells array must be greater than 0 since this method can only be called if a spell is
// cast, which is impossible if there are no spells.
if(cooldowns.length == 0) cooldowns = new int[getSpells(wand).length];
cooldowns[wand.stackTagCompound.getInteger(SELECTED_SPELL_KEY)] = cooldown;
setCooldowns(wand, cooldowns);
}
/** Returns the number of upgrades of the given type that have been applied to the given wand, or 0 if the wand has
* no upgrade data or the given item is not a valid wand upgrade. */
public static int getUpgradeLevel(ItemStack wand, Item upgrade){
String key = upgradeMap.get(upgrade);
if(wand.stackTagCompound != null && wand.stackTagCompound.hasKey(UPGRADES_KEY) && key != null){
return wand.stackTagCompound.getCompoundTag(UPGRADES_KEY).getInteger(key);
}
return 0;
}
/** Returns the total number of upgrades that have been applied to the given wand, or 0 if the wand has no upgrade
* data. */
public static int getTotalUpgrades(ItemStack wand){
int totalUpgrades = 0;
for(Item item : upgradeMap.keySet()){
totalUpgrades += getUpgradeLevel(wand, item);
}
return totalUpgrades;
}
/** Applies the given upgrade to the given wand, or in other words increases the level for that upgrade by 1. This
* does <b>not</b> account for the individual or total upgrade stack limits. */
public static void applyUpgrade(ItemStack wand, Item upgrade){
if(wand.stackTagCompound == null) wand.stackTagCompound = new NBTTagCompound();
if(!wand.stackTagCompound.hasKey(UPGRADES_KEY)) wand.stackTagCompound.setTag(UPGRADES_KEY, new NBTTagCompound());
NBTTagCompound upgrades = wand.stackTagCompound.getCompoundTag(UPGRADES_KEY);
String key = upgradeMap.get(upgrade);
if(key != null) upgrades.setInteger(key, upgrades.getInteger(key) + 1);
wand.stackTagCompound.setTag(UPGRADES_KEY, upgrades);
}
/** Returns true if the given item is a valid special wand upgrade. */
public static boolean isWandUpgrade(Item upgrade){
return upgradeMap.containsKey(upgrade);
}
/** Returns a set of all the items which are valid special wand upgrades. */
public static Set<Item> getSpecialUpgrades(){
return WandHelper.upgradeMap.keySet();
}
/** Registers a special upgrade with wizardry. Not used in the base mod, but I've put it here to make it easy
* for add-ons to add new wand upgrades.
* @param upgrade The wand upgrade item
* @param identifier A unique string, used as a key for wand nbt tags
* @throws IllegalArgumentException if the passed in identifier is already used for another wand upgrade */
public static void registerSpecialUpgrade(Item upgrade, String identifier){
// Throwing an exception is the best thing to do here, since if a duplicate was allowed weird things would
// happen later with wand NBT.
if(upgradeMap.containsValue(identifier)) throw new IllegalArgumentException("Duplicate wand upgrade identifier: " + identifier);
upgradeMap.put(upgrade, identifier);
}
}
@@ -0,0 +1,64 @@
package electroblob.wizardry;
import java.util.Random;
import electroblob.wizardry.item.ItemScroll;
import electroblob.wizardry.item.ItemSpellBook;
import electroblob.wizardry.item.ItemWand;
import electroblob.wizardry.item.ItemWizardArmour;
import electroblob.wizardry.spell.Spell;
import net.minecraft.inventory.IInventory;
import net.minecraft.item.ItemStack;
import net.minecraft.util.WeightedRandomChestContent;
import net.minecraftforge.common.ChestGenHooks;
public class WeightedRandomSpellBook extends WeightedRandomChestContent{
public WeightedRandomSpellBook(ItemStack par1ItemStack, int par2, int par3,
int par4) {
super(par1ItemStack, par2, par3, par4);
}
/* A brief comment about dungeon loot:
* Tiers are weighted as per usual. Elements all have equal chance, INCLUDING simple stuff. The main reason for
* this is that dungeon loot is not that common anyway, so making elemental gear even rarer would be too much.
*/
protected ItemStack[] generateChestContent(Random random, IInventory newInventory){
if(theItemId.getItem() instanceof ItemSpellBook){
theItemId = new ItemStack(theItemId.getItem(), 1, WizardryUtilities.getStandardWeightedRandomSpellId(random));
}else if(theItemId.getItem() instanceof ItemWand){
theItemId = new ItemStack(WizardryUtilities.getWand(EnumTier.BASIC, EnumElement.values()[random.nextInt(EnumElement.values().length)]));
}else if(theItemId.getItem() instanceof ItemWizardArmour){
theItemId = new ItemStack(WizardryUtilities.getArmour(EnumElement.values()[random.nextInt(EnumElement.values().length)], random.nextInt(3)));
}else if(theItemId.getItem() instanceof ItemScroll){
int spellId = WizardryUtilities.getStandardWeightedRandomSpellId(random, true);
theItemId = new ItemStack(theItemId.getItem(), 1, spellId);
}else if(theItemId.getItem() == Wizardry.condenserUpgrade){
switch(random.nextInt(8)){
case 0: theItemId = new ItemStack(Wizardry.condenserUpgrade); break;
case 1: theItemId = new ItemStack(Wizardry.siphonUpgrade); break;
case 2: theItemId = new ItemStack(Wizardry.rangeUpgrade); break;
case 3: theItemId = new ItemStack(Wizardry.cooldownUpgrade); break;
case 4: theItemId = new ItemStack(Wizardry.durationUpgrade); break;
case 5: theItemId = new ItemStack(Wizardry.storageUpgrade); break;
case 6: theItemId = new ItemStack(Wizardry.blastUpgrade); break;
case 7: theItemId = new ItemStack(Wizardry.attunementUpgrade); break;
}
}
return ChestGenHooks.generateStacks(random, theItemId, theMinimumChanceToGenerateItem, theMaximumChanceToGenerateItem);
}
}
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -1,30 +1,35 @@
package electroblob.wizardry;
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;
import cpw.mods.fml.client.IModGuiFactory;
import electroblob.wizardry.client.GuiConfigWizardry;
import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.GuiScreen;
public class WizardryGuiFactory implements IModGuiFactory {
@Override
public void initialize(Minecraft minecraftInstance){
}
@Override
public Set<RuntimeOptionCategoryElement> runtimeGuiCategories(){
return null;
}
@Override
public boolean hasConfigGui(){
return true;
}
@Override
public GuiScreen createConfigGui(GuiScreen parentScreen){
return new GuiConfigWizardry(parentScreen);
}
@Override
public void initialize(Minecraft minecraftInstance)
{
}
@Override
public Class<? extends GuiScreen> mainConfigGuiClass()
{
return GuiConfigWizardry.class;
}
@Override
public Set<RuntimeOptionCategoryElement> runtimeGuiCategories()
{
return null;
}
@Override
public RuntimeOptionGuiHandler getHandlerFor(RuntimeOptionCategoryElement element)
{
return null;
}
}
@@ -1,104 +1,63 @@
package electroblob.wizardry;
import electroblob.wizardry.inventory.ContainerArcaneWorkbench;
import electroblob.wizardry.inventory.ContainerBookshelf;
import electroblob.wizardry.inventory.ContainerPortableWorkbench;
import cpw.mods.fml.common.network.IGuiHandler;
import electroblob.wizardry.client.GuiArcaneWorkbench;
import electroblob.wizardry.client.GuiPortableCrafting;
import electroblob.wizardry.client.GuiSpellBook;
import electroblob.wizardry.client.GuiWizardHandbook;
import electroblob.wizardry.item.ItemSpellBook;
import electroblob.wizardry.item.ItemWizardHandbook;
import electroblob.wizardry.spell.Spell;
import electroblob.wizardry.tileentity.ContainerArcaneWorkbench;
import electroblob.wizardry.tileentity.ContainerPortableWorkbench;
import electroblob.wizardry.tileentity.TileEntityArcaneWorkbench;
import electroblob.wizardry.tileentity.TileEntityBookshelf;
import electroblob.wizardry.tileentity.TileEntityLectern;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.tileentity.TileEntity;
import net.minecraft.util.math.BlockPos;
import net.minecraft.world.World;
import net.minecraftforge.fml.common.network.IGuiHandler;
public class WizardryGuiHandler implements IGuiHandler {
/** Incrementable index for the gui ID */
private static int nextGuiId = 0;
public static final int SPELL_BOOK = nextGuiId++;
public static final int ARCANE_WORKBENCH = nextGuiId++;
public static final int WIZARD_HANDBOOK = nextGuiId++;
public static final int PORTABLE_CRAFTING = nextGuiId++;
public static final int BOOKSHELF = nextGuiId++;
public static final int LECTERN = nextGuiId++;
@Override
public Object getServerGuiElement(int id, EntityPlayer player, World world, int x, int y, int z){
public Object getServerGuiElement(int id, EntityPlayer player, World world,
int x, int y, int z) {
if(id == ARCANE_WORKBENCH){
TileEntity tileEntity = world.getTileEntity(new BlockPos(x, y, z));
TileEntity tileEntity = world.getTileEntity(x, y, z);
if(tileEntity instanceof TileEntityArcaneWorkbench){
return new ContainerArcaneWorkbench(player.inventory, (TileEntityArcaneWorkbench)tileEntity);
}
}else if(id == PORTABLE_CRAFTING){
return new ContainerPortableWorkbench(player.inventory, world, new BlockPos(x, y, z));
}else if(id == BOOKSHELF){
TileEntity tileEntity = world.getTileEntity(new BlockPos(x, y, z));
if(tileEntity instanceof TileEntityBookshelf){
return new ContainerBookshelf(player.inventory, (TileEntityBookshelf)tileEntity);
return new ContainerArcaneWorkbench(player.inventory, (TileEntityArcaneWorkbench) tileEntity);
}
}
else if(id == PORTABLE_CRAFTING){
return new ContainerPortableWorkbench(player.inventory, world, x, y, z);
}
return null;
}
@Override
public Object getClientGuiElement(int id, EntityPlayer player, World world, int x, int y, int z){
public Object getClientGuiElement(int id, EntityPlayer player, World world,
int x, int y, int z) {
if(id == ARCANE_WORKBENCH){
TileEntity tileEntity = world.getTileEntity(new BlockPos(x, y, z));
TileEntity tileEntity = world.getTileEntity(x, y, z);
if(tileEntity instanceof TileEntityArcaneWorkbench){
return new electroblob.wizardry.client.gui.GuiArcaneWorkbench(player.inventory,
(TileEntityArcaneWorkbench)tileEntity);
return new GuiArcaneWorkbench(player.inventory, (TileEntityArcaneWorkbench) tileEntity);
}
}else if(id == WIZARD_HANDBOOK && (player.getHeldItemMainhand().getItem() instanceof ItemWizardHandbook
|| player.getHeldItemOffhand().getItem() instanceof ItemWizardHandbook)){
return new electroblob.wizardry.client.gui.handbook.GuiWizardHandbook();
}else if(id == SPELL_BOOK){
if(player.getHeldItemMainhand().getItem() instanceof ItemSpellBook){
return new electroblob.wizardry.client.gui.GuiSpellBook(player.getHeldItemMainhand());
}else if(player.getHeldItemOffhand().getItem() instanceof ItemSpellBook){
return new electroblob.wizardry.client.gui.GuiSpellBook(player.getHeldItemOffhand());
}
}else if(id == PORTABLE_CRAFTING){
return new electroblob.wizardry.client.gui.GuiPortableCrafting(player.inventory, world, new BlockPos(x, y, z));
}else if(id == BOOKSHELF){
TileEntity tileEntity = world.getTileEntity(new BlockPos(x, y, z));
if(tileEntity instanceof TileEntityBookshelf){
return new electroblob.wizardry.client.gui.GuiBookshelf(player.inventory,
(TileEntityBookshelf)tileEntity);
}
}else if(id == LECTERN){
TileEntity tileEntity = world.getTileEntity(new BlockPos(x, y, z));
if(tileEntity instanceof TileEntityLectern){
return new electroblob.wizardry.client.gui.GuiLectern((TileEntityLectern)tileEntity);
}
}
else if(id == WIZARD_HANDBOOK && player.getHeldItem().getItem() instanceof ItemWizardHandbook){
return new GuiWizardHandbook();
}
else if(id == SPELL_BOOK && player.getHeldItem().getItem() instanceof ItemSpellBook){
return new GuiSpellBook(Spell.get(player.getHeldItem().getItemDamage()));
}
else if(id == PORTABLE_CRAFTING){
return new GuiPortableCrafting(player.inventory, world, x, y, z);
}
return null;
}
}
@@ -0,0 +1,69 @@
package electroblob.wizardry;
import org.lwjgl.input.Keyboard;
import cpw.mods.fml.common.eventhandler.SubscribeEvent;
import cpw.mods.fml.common.gameevent.InputEvent;
import cpw.mods.fml.common.network.simpleimpl.IMessage;
import electroblob.wizardry.client.ClientProxy;
import electroblob.wizardry.packet.PacketControlInput;
import electroblob.wizardry.packet.WizardryPacketHandler;
import net.minecraft.client.Minecraft;
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){
ClientProxy proxy = (ClientProxy)Wizardry.proxy;
if(proxy.nextSpell.isPressed() && Minecraft.getMinecraft().inGameHasFocus){
if(!NkeyPressed){
NkeyPressed = true;
}else{
NkeyAlreadyPressed = true;
}
if(!NkeyAlreadyPressed){
// Packet building
IMessage msg = new PacketControlInput.Message(1);
WizardryPacketHandler.net.sendToServer(msg);
}
}
if(proxy.previousSpell.isPressed() && Minecraft.getMinecraft().inGameHasFocus){
if(!BkeyPressed){
BkeyPressed = true;
}else{
BkeyAlreadyPressed = true;
}
if(!BkeyAlreadyPressed){
// Packet building
IMessage msg = new PacketControlInput.Message(2);
WizardryPacketHandler.net.sendToServer(msg);
}
}
}
}
// Key released
else{
if(NkeyPressed){
NkeyPressed = false;
NkeyAlreadyPressed = false;
}else if(BkeyPressed){
BkeyPressed = false;
BkeyAlreadyPressed = false;
}
}
}
}
@@ -0,0 +1,631 @@
package electroblob.wizardry;
import java.util.ArrayList;
import java.util.List;
import cpw.mods.fml.common.registry.EntityRegistry;
import cpw.mods.fml.common.registry.GameRegistry;
import electroblob.wizardry.entity.EntityArc;
import electroblob.wizardry.entity.EntityFallingGrass;
import electroblob.wizardry.entity.EntityMagicSlime;
import electroblob.wizardry.entity.EntityMeteor;
import electroblob.wizardry.entity.EntityShield;
import electroblob.wizardry.entity.construct.EntityArrowRain;
import electroblob.wizardry.entity.construct.EntityBlackHole;
import electroblob.wizardry.entity.construct.EntityBlizzard;
import electroblob.wizardry.entity.construct.EntityBubble;
import electroblob.wizardry.entity.construct.EntityDecay;
import electroblob.wizardry.entity.construct.EntityEarthquake;
import electroblob.wizardry.entity.construct.EntityFireRing;
import electroblob.wizardry.entity.construct.EntityFireSigil;
import electroblob.wizardry.entity.construct.EntityForcefield;
import electroblob.wizardry.entity.construct.EntityFrostSigil;
import electroblob.wizardry.entity.construct.EntityHailstorm;
import electroblob.wizardry.entity.construct.EntityHammer;
import electroblob.wizardry.entity.construct.EntityHealAura;
import electroblob.wizardry.entity.construct.EntityIceSpike;
import electroblob.wizardry.entity.construct.EntityLightningPulse;
import electroblob.wizardry.entity.construct.EntityLightningSigil;
import electroblob.wizardry.entity.construct.EntityTornado;
import electroblob.wizardry.entity.living.EntityBlazeMinion;
import electroblob.wizardry.entity.living.EntityDecoy;
import electroblob.wizardry.entity.living.EntityEvilWizard;
import electroblob.wizardry.entity.living.EntityIceGiant;
import electroblob.wizardry.entity.living.EntityIceWraith;
import electroblob.wizardry.entity.living.EntityLightningWraith;
import electroblob.wizardry.entity.living.EntityPhoenix;
import electroblob.wizardry.entity.living.EntityShadowWraith;
import electroblob.wizardry.entity.living.EntitySilverfishMinion;
import electroblob.wizardry.entity.living.EntitySkeletonMinion;
import electroblob.wizardry.entity.living.EntitySpiderMinion;
import electroblob.wizardry.entity.living.EntitySpiritHorse;
import electroblob.wizardry.entity.living.EntitySpiritWolf;
import electroblob.wizardry.entity.living.EntityStormElemental;
import electroblob.wizardry.entity.living.EntityWizard;
import electroblob.wizardry.entity.living.EntityZombieMinion;
import electroblob.wizardry.entity.projectile.EntityDarknessOrb;
import electroblob.wizardry.entity.projectile.EntityDart;
import electroblob.wizardry.entity.projectile.EntityFirebolt;
import electroblob.wizardry.entity.projectile.EntityFirebomb;
import electroblob.wizardry.entity.projectile.EntityForceArrow;
import electroblob.wizardry.entity.projectile.EntityForceOrb;
import electroblob.wizardry.entity.projectile.EntityIceCharge;
import electroblob.wizardry.entity.projectile.EntityIceLance;
import electroblob.wizardry.entity.projectile.EntityIceShard;
import electroblob.wizardry.entity.projectile.EntityLightningArrow;
import electroblob.wizardry.entity.projectile.EntityLightningDisc;
import electroblob.wizardry.entity.projectile.EntityMagicMissile;
import electroblob.wizardry.entity.projectile.EntityPoisonBomb;
import electroblob.wizardry.entity.projectile.EntitySmokeBomb;
import electroblob.wizardry.entity.projectile.EntitySpark;
import electroblob.wizardry.entity.projectile.EntitySparkBomb;
import electroblob.wizardry.entity.projectile.EntityThunderbolt;
import electroblob.wizardry.spell.*;
import electroblob.wizardry.tileentity.TileEntityArcaneWorkbench;
import electroblob.wizardry.tileentity.TileEntityMagicLight;
import electroblob.wizardry.tileentity.TileEntityPlayerSave;
import electroblob.wizardry.tileentity.TileEntityStatue;
import electroblob.wizardry.tileentity.TileEntityTimer;
import net.minecraft.entity.EnumCreatureType;
import net.minecraft.init.Blocks;
import net.minecraft.init.Items;
import net.minecraft.item.ItemStack;
import net.minecraft.util.WeightedRandomChestContent;
import net.minecraft.world.biome.BiomeGenBase;
import net.minecraftforge.common.ChestGenHooks;
import net.minecraftforge.oredict.OreDictionary;
public final class WizardryRegistry {
// Usage note on ChestGenHooks: First number is minimum, second is maximum. Third is rarity, lower numbers are more rare [bread = 100].
// i starts at 1 here so that 'none' does not spawn.
// Earlier problem solved using clever forge hook - extend WeightedRandomChestContent and override generateChestContent.
// Apparently this seems to be incompatible with roguelike dungeons (any others?), in that it generates the same spell book in
// all of the chests that have a spell book in the same dungeon (usually, though it has been known to do different ones on
// different floors). It also appears to be able to generate the 'none' book, which should be impossible... never mind.
// I don't think that's my fault, or I would have seen a 'none' book in a vanilla dungeon by now.
// Another curious observation: Recurrent complex's generators also seem to always generate the same book/wand/armour
// until you restart the game and can generate the 'none' book as well.
// My best guess as to what's happening is that some mods hook into the dungeon loot differently to others, in that
// they take all the WeightedRandomChestContent objects once and pick from them each time rather than making new ones
// for each chest - which is, in theory, a more efficient way of doing things and will speed up world gen a bit, but
// does of course result in custom chest content implementations behaving strangely. The 'none' book thing remains a
// mystery, however.
/** Called from the preInit method in the main mod class to register the custom dungeon loot. */
public static void registerLoot(){
// The generate loot option only applies to vanilla chests, not wizard tower chests.
if(Wizardry.generateLoot){
String[] chestTypes = {
ChestGenHooks.DUNGEON_CHEST,
ChestGenHooks.PYRAMID_JUNGLE_CHEST,
ChestGenHooks.PYRAMID_DESERT_CHEST,
ChestGenHooks.MINESHAFT_CORRIDOR,
ChestGenHooks.STRONGHOLD_CORRIDOR,
ChestGenHooks.STRONGHOLD_CROSSING,
ChestGenHooks.STRONGHOLD_LIBRARY
};
for(String chest : chestTypes){
ChestGenHooks.addItem(chest, new WeightedRandomSpellBook(new ItemStack(Wizardry.spellBook), 1, 1, Wizardry.SPELL_BOOK_FREQUENCY));
ChestGenHooks.addItem(chest, new WeightedRandomSpellBook(new ItemStack(Wizardry.magicWand), 1, 1, Wizardry.WAND_FREQUENCY));
ChestGenHooks.addItem(chest, new WeightedRandomSpellBook(new ItemStack(Wizardry.wizardHat), 1, 1, Wizardry.ARMOUR_FREQUENCY));
ChestGenHooks.addItem(chest, new WeightedRandomSpellBook(new ItemStack(Wizardry.scroll), 1, 1, Wizardry.SCROLL_FREQUENCY));
ChestGenHooks.addItem(chest, new WeightedRandomSpellBook(new ItemStack(Wizardry.condenserUpgrade), 1, 1, Wizardry.WAND_UPGRADE_FREQUENCY));
ChestGenHooks.addItem(chest, new WeightedRandomChestContent(new ItemStack(Wizardry.magicCrystal), 1, 4, Wizardry.CRYSTAL_FREQUENCY));
ChestGenHooks.addItem(chest, new WeightedRandomChestContent(new ItemStack(Wizardry.arcaneTome, 1, 1), 1, 1, Wizardry.APPRENTICE_TOME_FREQUENCY));
ChestGenHooks.addItem(chest, new WeightedRandomChestContent(new ItemStack(Wizardry.arcaneTome, 1, 2), 1, 1, Wizardry.ADVANCED_TOME_FREQUENCY));
ChestGenHooks.addItem(chest, new WeightedRandomChestContent(new ItemStack(Wizardry.arcaneTome, 1, 3), 1, 1, Wizardry.MASTER_TOME_FREQUENCY));
ChestGenHooks.addItem(chest, new WeightedRandomChestContent(new ItemStack(Wizardry.armourUpgrade), 1, 1, Wizardry.ARMOUR_UPGRADE_FREQUENCY));
ChestGenHooks.addItem(chest, new WeightedRandomChestContent(new ItemStack(Wizardry.firebomb), 1, 5, Wizardry.FIREBOMB_FREQUENCY));
ChestGenHooks.addItem(chest, new WeightedRandomChestContent(new ItemStack(Wizardry.poisonBomb), 1, 5, Wizardry.POISON_BOMB_FREQUENCY));
ChestGenHooks.addItem(chest, new WeightedRandomChestContent(new ItemStack(Wizardry.smokeBomb), 1, 5, Wizardry.SMOKE_BOMB_FREQUENCY));
if(Wizardry.discoveryMode) ChestGenHooks.addItem(chest, new WeightedRandomChestContent(new ItemStack(Wizardry.identificationScroll), 1, 1, Wizardry.IDENTIFICATION_SCROLL_FREQUENCY));
}
}
// Creates a new ChestGenHooks for wizard towers.
ChestGenHooks chestGen = ChestGenHooks.getInfo(WizardryWorldGenerator.WIZARD_TOWER);
chestGen.setMax(7);
chestGen.setMin(3);
// Wizard tower chests use exactly the same things, minus the firebombs, poison bombs and smoke bombs
ChestGenHooks.addItem(WizardryWorldGenerator.WIZARD_TOWER, new WeightedRandomSpellBook(new ItemStack(Wizardry.spellBook), 1, 1, Wizardry.SPELL_BOOK_FREQUENCY));
ChestGenHooks.addItem(WizardryWorldGenerator.WIZARD_TOWER, new WeightedRandomSpellBook(new ItemStack(Wizardry.magicWand), 1, 1, Wizardry.WAND_FREQUENCY));
ChestGenHooks.addItem(WizardryWorldGenerator.WIZARD_TOWER, new WeightedRandomSpellBook(new ItemStack(Wizardry.wizardHat), 1, 1, Wizardry.ARMOUR_FREQUENCY));
ChestGenHooks.addItem(WizardryWorldGenerator.WIZARD_TOWER, new WeightedRandomSpellBook(new ItemStack(Wizardry.scroll), 1, 1, Wizardry.SCROLL_FREQUENCY));
ChestGenHooks.addItem(WizardryWorldGenerator.WIZARD_TOWER, new WeightedRandomSpellBook(new ItemStack(Wizardry.condenserUpgrade), 1, 1, Wizardry.WAND_UPGRADE_FREQUENCY));
ChestGenHooks.addItem(WizardryWorldGenerator.WIZARD_TOWER, new WeightedRandomChestContent(new ItemStack(Wizardry.magicCrystal), 1, 4, Wizardry.CRYSTAL_FREQUENCY));
ChestGenHooks.addItem(WizardryWorldGenerator.WIZARD_TOWER, new WeightedRandomChestContent(new ItemStack(Wizardry.arcaneTome, 1, 1), 1, 1, Wizardry.APPRENTICE_TOME_FREQUENCY));
ChestGenHooks.addItem(WizardryWorldGenerator.WIZARD_TOWER, new WeightedRandomChestContent(new ItemStack(Wizardry.arcaneTome, 1, 2), 1, 1, Wizardry.ADVANCED_TOME_FREQUENCY));
ChestGenHooks.addItem(WizardryWorldGenerator.WIZARD_TOWER, new WeightedRandomChestContent(new ItemStack(Wizardry.arcaneTome, 1, 3), 1, 1, Wizardry.MASTER_TOME_FREQUENCY));
ChestGenHooks.addItem(WizardryWorldGenerator.WIZARD_TOWER, new WeightedRandomChestContent(new ItemStack(Wizardry.armourUpgrade), 1, 1, Wizardry.ARMOUR_UPGRADE_FREQUENCY));
if(Wizardry.discoveryMode) ChestGenHooks.addItem(WizardryWorldGenerator.WIZARD_TOWER, new WeightedRandomChestContent(new ItemStack(Wizardry.identificationScroll), 1, 1, Wizardry.IDENTIFICATION_SCROLL_FREQUENCY));
}
/* The name defined here in the regitry is the one Minecraft uses in game for commands like /give, as
* opposed to the one defined earlier with setBlockName() or setUnlocalizedName() which is the one used by
* the lang files. Ideally these should both be the same, but I've written the lang file now, so... */
/** Called from the preInit method in the main mod class to register all the blocks. */
public static void registerBlocks(){
GameRegistry.registerBlock(Wizardry.arcaneWorkbench, "arcane_workbench");
GameRegistry.registerBlock(Wizardry.crystalOre, "crystal_ore");
GameRegistry.registerBlock(Wizardry.petrifiedStone, "petrified_stone");
GameRegistry.registerBlock(Wizardry.iceStatue, "ice_statue");
GameRegistry.registerBlock(Wizardry.magicLight, "magic_light");
GameRegistry.registerBlock(Wizardry.crystalFlower, "crystal_flower");
GameRegistry.registerBlock(Wizardry.snare, "snare");
GameRegistry.registerBlock(Wizardry.transportationStone, "transportation_stone");
GameRegistry.registerBlock(Wizardry.spectralBlock, "spectral_block");
GameRegistry.registerBlock(Wizardry.crystalBlock, "crystal_block");
GameRegistry.registerBlock(Wizardry.meteor, "meteor");
GameRegistry.registerBlock(Wizardry.vanishingCobweb, "vanishing_cobweb");
}
/** Called from the preInit method in the main mod class to register all the items. */
public static void registerItems(){
GameRegistry.registerItem(Wizardry.magicCrystal, "magic_crystal");
GameRegistry.registerItem(Wizardry.magicWand, "magic_wand");
GameRegistry.registerItem(Wizardry.apprenticeWand, "apprentice_wand");
GameRegistry.registerItem(Wizardry.advancedWand, "advanced_wand");
GameRegistry.registerItem(Wizardry.masterWand, "master_wand");
GameRegistry.registerItem(Wizardry.spellBook, "spell_book");
GameRegistry.registerItem(Wizardry.arcaneTome, "arcane_tome");
GameRegistry.registerItem(Wizardry.wizardHandbook, "wizard_handbook");
GameRegistry.registerItem(Wizardry.basicFireWand, "basic_fire_wand");
GameRegistry.registerItem(Wizardry.basicIceWand, "basic_ice_wand");
GameRegistry.registerItem(Wizardry.basicLightningWand, "basic_lightning_wand");
GameRegistry.registerItem(Wizardry.basicNecromancyWand, "basic_necromancy_wand");
GameRegistry.registerItem(Wizardry.basicEarthWand, "basic_earth_wand");
GameRegistry.registerItem(Wizardry.basicSorceryWand, "basic_sorcery_wand");
GameRegistry.registerItem(Wizardry.basicHealingWand, "basic_healing_wand");
GameRegistry.registerItem(Wizardry.apprenticeFireWand, "apprentice_fire_wand");
GameRegistry.registerItem(Wizardry.apprenticeIceWand, "apprentice_ice_wand");
GameRegistry.registerItem(Wizardry.apprenticeLightningWand, "apprentice_lightning_wand");
GameRegistry.registerItem(Wizardry.apprenticeNecromancyWand, "apprentice_necromancy_wand");
GameRegistry.registerItem(Wizardry.apprenticeEarthWand, "apprentice_earth_wand");
GameRegistry.registerItem(Wizardry.apprenticeSorceryWand, "apprentice_sorcery_wand");
GameRegistry.registerItem(Wizardry.apprenticeHealingWand, "apprentice_healing_wand");
GameRegistry.registerItem(Wizardry.advancedFireWand, "advanced_fire_wand");
GameRegistry.registerItem(Wizardry.advancedIceWand, "advanced_ice_wand");
GameRegistry.registerItem(Wizardry.advancedLightningWand, "advanced_lightning_wand");
GameRegistry.registerItem(Wizardry.advancedNecromancyWand, "advanced_necromancy_wand");
GameRegistry.registerItem(Wizardry.advancedEarthWand, "advanced_earth_wand");
GameRegistry.registerItem(Wizardry.advancedSorceryWand, "advanced_sorcery_wand");
GameRegistry.registerItem(Wizardry.advancedHealingWand, "advanced_healing_wand");
GameRegistry.registerItem(Wizardry.masterFireWand, "master_fire_wand");
GameRegistry.registerItem(Wizardry.masterIceWand, "master_ice_wand");
GameRegistry.registerItem(Wizardry.masterLightningWand, "master_lightning_wand");
GameRegistry.registerItem(Wizardry.masterNecromancyWand, "master_necromancy_wand");
GameRegistry.registerItem(Wizardry.masterEarthWand, "master_earth_wand");
GameRegistry.registerItem(Wizardry.masterSorceryWand, "master_sorcery_wand");
GameRegistry.registerItem(Wizardry.masterHealingWand, "master_healing_wand");
GameRegistry.registerItem(Wizardry.spectralSword, "spectral_sword");
GameRegistry.registerItem(Wizardry.spectralPickaxe, "spectral_pickaxe");
GameRegistry.registerItem(Wizardry.spectralBow, "spectral_bow");
GameRegistry.registerItem(Wizardry.manaFlask, "mana_flask");
GameRegistry.registerItem(Wizardry.storageUpgrade, "storage_upgrade");
GameRegistry.registerItem(Wizardry.siphonUpgrade, "siphon_upgrade");
GameRegistry.registerItem(Wizardry.condenserUpgrade, "condenser_upgrade");
GameRegistry.registerItem(Wizardry.rangeUpgrade, "range_upgrade");
GameRegistry.registerItem(Wizardry.durationUpgrade, "duration_upgrade");
GameRegistry.registerItem(Wizardry.cooldownUpgrade, "cooldown_upgrade");
GameRegistry.registerItem(Wizardry.blastUpgrade, "blast_upgrade");
GameRegistry.registerItem(Wizardry.attunementUpgrade, "attunement_upgrade");
GameRegistry.registerItem(Wizardry.flamingAxe, "flaming_axe");
GameRegistry.registerItem(Wizardry.frostAxe, "frost_axe");
GameRegistry.registerItem(Wizardry.firebomb, "firebomb");
GameRegistry.registerItem(Wizardry.poisonBomb, "poison_bomb");
GameRegistry.registerItem(Wizardry.blankScroll, "blank_scroll");
GameRegistry.registerItem(Wizardry.scroll, "scroll");
GameRegistry.registerItem(Wizardry.armourUpgrade, "armour_upgrade");
GameRegistry.registerItem(Wizardry.magicSilk, "magic_silk");
GameRegistry.registerItem(Wizardry.wizardHat, "wizard_hat");
GameRegistry.registerItem(Wizardry.wizardRobe, "wizard_robe");
GameRegistry.registerItem(Wizardry.wizardLeggings, "wizard_leggings");
GameRegistry.registerItem(Wizardry.wizardBoots, "wizard_boots");
GameRegistry.registerItem(Wizardry.wizardHatFire, "wizard_hat_fire");
GameRegistry.registerItem(Wizardry.wizardRobeFire, "wizard_robe_fire");
GameRegistry.registerItem(Wizardry.wizardLeggingsFire, "wizard_leggings_fire");
GameRegistry.registerItem(Wizardry.wizardBootsFire, "wizard_boots_fire");
GameRegistry.registerItem(Wizardry.wizardHatIce, "wizard_hat_ice");
GameRegistry.registerItem(Wizardry.wizardRobeIce, "wizard_robe_ice");
GameRegistry.registerItem(Wizardry.wizardLeggingsIce, "wizard_leggings_ice");
GameRegistry.registerItem(Wizardry.wizardBootsIce, "wizard_boots_ice");
GameRegistry.registerItem(Wizardry.wizardHatLightning, "wizard_hat_lightning");
GameRegistry.registerItem(Wizardry.wizardRobeLightning, "wizard_robe_lightning");
GameRegistry.registerItem(Wizardry.wizardLeggingsLightning, "wizard_leggings_lightning");
GameRegistry.registerItem(Wizardry.wizardBootsLightning, "wizard_boots_lightning");
GameRegistry.registerItem(Wizardry.wizardHatNecromancy, "wizard_hat_necromancy");
GameRegistry.registerItem(Wizardry.wizardRobeNecromancy, "wizard_robe_necromancy");
GameRegistry.registerItem(Wizardry.wizardLeggingsNecromancy, "wizard_leggings_necromancy");
GameRegistry.registerItem(Wizardry.wizardBootsNecromancy, "wizard_boots_necromancy");
GameRegistry.registerItem(Wizardry.wizardHatEarth, "wizard_hat_earth");
GameRegistry.registerItem(Wizardry.wizardRobeEarth, "wizard_robe_earth");
GameRegistry.registerItem(Wizardry.wizardLeggingsEarth, "wizard_leggings_earth");
GameRegistry.registerItem(Wizardry.wizardBootsEarth, "wizard_boots_earth");
GameRegistry.registerItem(Wizardry.wizardHatSorcery, "wizard_hat_sorcery");
GameRegistry.registerItem(Wizardry.wizardRobeSorcery, "wizard_robe_sorcery");
GameRegistry.registerItem(Wizardry.wizardLeggingsSorcery, "wizard_leggings_sorcery");
GameRegistry.registerItem(Wizardry.wizardBootsSorcery, "wizard_boots_sorcery");
GameRegistry.registerItem(Wizardry.wizardHatHealing, "wizard_hat_healing");
GameRegistry.registerItem(Wizardry.wizardRobeHealing, "wizard_robe_healing");
GameRegistry.registerItem(Wizardry.wizardLeggingsHealing, "wizard_leggings_healing");
GameRegistry.registerItem(Wizardry.wizardBootsHealing, "wizard_boots_healing");
GameRegistry.registerItem(Wizardry.spawnWizard, "spawn_wizard");
GameRegistry.registerItem(Wizardry.spectralHelmet, "spectral_helmet");
GameRegistry.registerItem(Wizardry.spectralChestplate, "spectral_chestplate");
GameRegistry.registerItem(Wizardry.spectralLeggings, "spectral_leggings");
GameRegistry.registerItem(Wizardry.spectralBoots, "spectral_boots");
GameRegistry.registerItem(Wizardry.smokeBomb, "smoke_bomb");
GameRegistry.registerItem(Wizardry.identificationScroll, "identification_scroll");
}
/** Called from the preInit method in the main mod class to register all the tileentities. */
public static void registerTileEntities(){
GameRegistry.registerTileEntity(TileEntityArcaneWorkbench.class, Wizardry.MODID + "ArcaneWorkbenchTileEntity");
GameRegistry.registerTileEntity(TileEntityStatue.class, Wizardry.MODID + "PetrifiedStoneTileEntity");
GameRegistry.registerTileEntity(TileEntityMagicLight.class, Wizardry.MODID + "MagicLightTileEntity");
GameRegistry.registerTileEntity(TileEntityTimer.class, Wizardry.MODID + "TimerTileEntity");
GameRegistry.registerTileEntity(TileEntityPlayerSave.class, Wizardry.MODID + "TileEntityPlayerSave");
}
/** Called from the preInit method in the main mod class to register all the entities. */
public static void registerEntities(Wizardry wizardry){
int id = 0; // Incrementable index for the mod specific entity id.
EntityRegistry.registerModEntity(EntityZombieMinion.class, "Zombie Minion", id++, wizardry, 128, 3, true);
EntityRegistry.registerModEntity(EntityMagicMissile.class, "Magic Missile", id++, wizardry, 128, 10, true);
EntityRegistry.registerModEntity(EntityArc.class, "Arc", id++, wizardry, 128, 10, false);
EntityRegistry.registerModEntity(EntitySkeletonMinion.class, "Skeleton Minion", id++, wizardry, 128, 3, true);
EntityRegistry.registerModEntity(EntitySparkBomb.class, "Spark Bomb", id++, wizardry, 128, 10, true);
EntityRegistry.registerModEntity(EntitySpiritWolf.class, "Spirit Wolf", id++, wizardry, 128, 3, true);
EntityRegistry.registerModEntity(EntityIceShard.class, "Ice Shard", id++, wizardry, 128, 10, true);
EntityRegistry.registerModEntity(EntityBlazeMinion.class, "Blaze Minion", id++, wizardry, 128, 10, true);
EntityRegistry.registerModEntity(EntityIceWraith.class, "Ice Wraith", id++, wizardry, 128, 10, true);
EntityRegistry.registerModEntity(EntityLightningWraith.class, "Lightning Wraith", id++, wizardry, 128, 10, true);
EntityRegistry.registerModEntity(EntityBlackHole.class, "Black Hole", id++, wizardry, 128, 10, false);
EntityRegistry.registerModEntity(EntityShield.class, "Shield", id++, wizardry, 128, 1, true);
EntityRegistry.registerModEntity(EntityMeteor.class, "Meteor", id++, wizardry, 128, 5, true);
EntityRegistry.registerModEntity(EntityBlizzard.class, "Blizzard", id++, wizardry, 128, 10, false);
EntityRegistry.registerModEntity(EntityWizard.class, "Wizard", id++, wizardry, 128, 3, true);
EntityRegistry.registerModEntity(EntityBubble.class, "Bubble", id++, wizardry, 128, 3, false);
EntityRegistry.registerModEntity(EntityTornado.class, "Tornado", id++, wizardry, 128, 1, false);
EntityRegistry.registerModEntity(EntityHammer.class, "Lightning Hammer", id++, wizardry, 128, 1, true);
EntityRegistry.registerModEntity(EntityFirebomb.class, "Firebomb", id++, wizardry, 128, 10, true);
EntityRegistry.registerModEntity(EntityForceOrb.class, "Force Orb", id++, wizardry, 128, 10, true);
EntityRegistry.registerModEntity(EntityArrowRain.class, "Arrow Rain", id++, wizardry, 128, 10, false);
EntityRegistry.registerModEntity(EntitySpark.class, "Spark", id++, wizardry, 128, 10, true);
EntityRegistry.registerModEntity(EntityShadowWraith.class, "Shadow Wraith", id++, wizardry, 128, 10, true);
EntityRegistry.registerModEntity(EntityDarknessOrb.class, "Darkness Orb", id++, wizardry, 128, 10, true);
EntityRegistry.registerModEntity(EntitySpiderMinion.class, "Spider Minion", id++, wizardry, 128, 3, true);
EntityRegistry.registerModEntity(EntityHealAura.class, "Healing Aura", id++, wizardry, 128, 10, false);
EntityRegistry.registerModEntity(EntityFireSigil.class, "Fire Sigil", id++, wizardry, 128, 10, false);
EntityRegistry.registerModEntity(EntityFrostSigil.class, "Frost Sigil", id++, wizardry, 128, 10, false);
EntityRegistry.registerModEntity(EntityLightningSigil.class, "Lightning Sigil", id++, wizardry, 128, 10, false);
EntityRegistry.registerModEntity(EntityLightningArrow.class, "Lightning Arrow", id++, wizardry, 128, 10, true);
EntityRegistry.registerModEntity(EntityFirebolt.class, "Firebolt", id++, wizardry, 128, 10, true);
EntityRegistry.registerModEntity(EntityPoisonBomb.class, "Poison Bomb", id++, wizardry, 128, 10, true);
EntityRegistry.registerModEntity(EntityIceCharge.class, "Ice Charge", id++, wizardry, 128, 10, true);
EntityRegistry.registerModEntity(EntityForceArrow.class, "Force Arrow", id++, wizardry, 128, 10, true);
EntityRegistry.registerModEntity(EntityDart.class, "Dart", id++, wizardry, 128, 10, true);
EntityRegistry.registerModEntity(EntityMagicSlime.class, "Magic Slime", id++, wizardry, 128, 3, true);
EntityRegistry.registerModEntity(EntityForcefield.class, "Forcefield", id++, wizardry, 128, 10, false);
EntityRegistry.registerModEntity(EntityFireRing.class, "Ring of Fire", id++, wizardry, 128, 10, false);
EntityRegistry.registerModEntity(EntityLightningDisc.class, "Lightning Disc", id++, wizardry, 128, 10, true);
EntityRegistry.registerModEntity(EntityThunderbolt.class, "Thunderbolt", id++, wizardry, 128, 10, true);
EntityRegistry.registerModEntity(EntityIceGiant.class, "Ice Giant", id++, wizardry, 128, 3, true);
EntityRegistry.registerModEntity(EntitySpiritHorse.class, "Spirit Horse", id++, wizardry, 128, 3, true);
EntityRegistry.registerModEntity(EntityPhoenix.class, "Phoenix", id++, wizardry, 128, 3, true);
EntityRegistry.registerModEntity(EntitySilverfishMinion.class, "Silverfish Minion", id++, wizardry, 128, 3, true);
EntityRegistry.registerModEntity(EntityDecay.class, "Decay", id++, wizardry, 128, 10, false);
EntityRegistry.registerModEntity(EntityStormElemental.class, "Storm Elemental", id++, wizardry, 128, 10, true);
EntityRegistry.registerModEntity(EntityEarthquake.class, "Earthquake", id++, wizardry, 128, 10, false);
EntityRegistry.registerModEntity(EntityFallingGrass.class, "Falling Grass", id++, wizardry, 128, 5, true);
EntityRegistry.registerModEntity(EntityIceLance.class, "Ice Lance", id++, wizardry, 128, 10, true);
EntityRegistry.registerModEntity(EntityHailstorm.class, "Hailstorm", id++, wizardry, 128, 10, false);
EntityRegistry.registerModEntity(EntitySmokeBomb.class, "Smoke Bomb", id++, wizardry, 128, 10, true);
EntityRegistry.registerModEntity(EntityEvilWizard.class, "Evil Wizard", id++, wizardry, 128, 3, true);
EntityRegistry.registerModEntity(EntityDecoy.class, "Decoy", id++, wizardry, 128, 3, true);
EntityRegistry.registerModEntity(EntityIceSpike.class, "Ice Spike", id++, wizardry, 128, 1, true);
EntityRegistry.registerModEntity(EntityLightningPulse.class, "Lightning Pulse", id++, wizardry, 128, 10, false);
List<BiomeGenBase> biomes = new ArrayList<BiomeGenBase>();
for(BiomeGenBase biome : BiomeGenBase.getBiomeGenArray()){
if(biome != null){
biomes.add(biome);
}
}
// For reference: 5, 1, 1 are the parameters for the witch in vanilla.
EntityRegistry.addSpawn(EntityEvilWizard.class, 3, 1, 1, EnumCreatureType.monster, biomes.toArray(new BiomeGenBase[biomes.size()]));
}
/** Static instance of the none spell, used for various purposes. */
public static Spell none;
/** Static instance of the magic missile spell, used for crafting spell books and by wizards. */
public static Spell magicMissile;
/** Static instance of the transportation spell, used for chat messages. */
public static Spell transportation;
/** Static instance of the clairvoyance spell, used for chat messages. */
public static Spell clairvoyance;
/** Called from the preInit method in the main mod class to register all the spells in wizardry. */
public static void registerSpells(){
// Wizardry 1.0 spells
Spell.register(none = new None());
Spell.register(magicMissile = new MagicMissile());
Spell.register(new Ignite());
Spell.register(new Freeze());
Spell.register(new Snowball());
Spell.register(new Arc());
Spell.register(new Thunderbolt());
Spell.register(new SummonZombie());
Spell.register(new Snare());
Spell.register(new Dart());
Spell.register(new Light());
Spell.register(new Telekinesis());
Spell.register(new Heal());
Spell.register(new Fireball());
Spell.register(new FlameRay());
Spell.register(new Firebomb());
Spell.register(new FireSigil());
Spell.register(new Firebolt());
Spell.register(new FrostRay());
Spell.register(new SummonSnowGolem());
Spell.register(new IceShard());
Spell.register(new IceStatue());
Spell.register(new FrostSigil());
Spell.register(new LightningRay());
Spell.register(new SparkBomb());
Spell.register(new HomingSpark());
Spell.register(new LightningSigil());
Spell.register(new LightningArrow());
Spell.register(new LifeDrain());
Spell.register(new SummonSkeleton());
Spell.register(new Metamorphosis());
Spell.register(new Wither());
Spell.register(new Poison());
Spell.register(new GrowthAura());
Spell.register(new Bubble());
Spell.register(new Whirlwind());
Spell.register(new PoisonBomb());
Spell.register(new SummonSpiritWolf());
Spell.register(new Blink());
Spell.register(new Agility());
Spell.register(new ConjureSword());
Spell.register(new ConjurePickaxe());
Spell.register(new ConjureBow());
Spell.register(new ForceArrow());
Spell.register(new Shield());
Spell.register(new ReplenishHunger());
Spell.register(new CureEffects());
Spell.register(new HealAlly());
Spell.register(new SummonBlaze());
Spell.register(new RingOfFire());
Spell.register(new Detonate());
Spell.register(new FireResistance());
Spell.register(new Fireskin());
Spell.register(new FlamingAxe());
Spell.register(new Blizzard());
Spell.register(new SummonIceWraith());
Spell.register(new IceShroud());
Spell.register(new IceCharge());
Spell.register(new FrostAxe());
Spell.register(new InvokeWeather());
Spell.register(new ChainLightning());
Spell.register(new LightningBolt());
Spell.register(new SummonLightningWraith());
Spell.register(new StaticAura());
Spell.register(new LightningDisc());
Spell.register(new MindControl());
Spell.register(new SummonWitherSkeleton());
Spell.register(new Entrapment());
Spell.register(new WitherSkull());
Spell.register(new DarknessOrb());
Spell.register(new ShadowWard());
Spell.register(new Decay());
Spell.register(new WaterBreathing());
Spell.register(new Tornado());
Spell.register(new Glide());
Spell.register(new SummonSpiritHorse());
Spell.register(new SpiderSwarm());
Spell.register(new Slime());
Spell.register(new Petrify());
Spell.register(new Invisibility());
Spell.register(new Levitation());
Spell.register(new ForceOrb());
Spell.register(transportation = new Transportation());
Spell.register(new SpectralPathway());
Spell.register(new PhaseStep());
Spell.register(new VanishingBox());
Spell.register(new GreaterHeal());
Spell.register(new HealingAura());
Spell.register(new Forcefield());
Spell.register(new Ironflesh());
Spell.register(new Transience());
Spell.register(new Meteor());
Spell.register(new Firestorm());
Spell.register(new SummonPhoenix());
Spell.register(new IceAge());
Spell.register(new WallOfFrost());
Spell.register(new SummonIceGiant());
Spell.register(new Thunderstorm());
Spell.register(new LightningHammer());
Spell.register(new PlagueOfDarkness());
Spell.register(new SummonSkeletonLegion());
Spell.register(new SummonShadowWraith());
Spell.register(new ForestsCurse());
Spell.register(new Flight());
Spell.register(new SilverfishSwarm());
Spell.register(new BlackHole());
Spell.register(new Shockwave());
Spell.register(new SummonIronGolem());
Spell.register(new ArrowRain());
Spell.register(new Diamondflesh());
Spell.register(new FontOfVitality());
// Wizardry 1.1 spells
Spell.register(new SmokeBomb());
Spell.register(new MindTrick());
Spell.register(new Leap());
Spell.register(new PocketFurnace());
Spell.register(new Intimidate());
Spell.register(new Banish());
Spell.register(new SixthSense());
Spell.register(new Darkvision());
Spell.register(clairvoyance = new Clairvoyance());
Spell.register(new PocketWorkbench());
Spell.register(new ImbueWeapon());
Spell.register(new InvigoratingPresence());
Spell.register(new Oakflesh());
Spell.register(new GreaterFireball());
Spell.register(new FlamingWeapon());
Spell.register(new IceLance());
Spell.register(new FreezingWeapon());
Spell.register(new IceSpikes());
Spell.register(new LightningPulse());
Spell.register(new CurseOfSoulbinding());
Spell.register(new Cobwebs());
Spell.register(new Decoy());
Spell.register(new ArcaneJammer());
Spell.register(new ConjureArmour());
Spell.register(new GroupHeal());
Spell.register(new Hailstorm());
Spell.register(new LightningWeb());
Spell.register(new SummonStormElemental());
Spell.register(new Earthquake());
Spell.register(new FontOfMana());
}
/** Called from the init method in the main mod class to register all the recipes. */
public static void registerRecipes(){
ItemStack ironIngotStack = new ItemStack(Items.iron_ingot);
ItemStack magicCrystalStack = new ItemStack(Wizardry.magicCrystal);
ItemStack magicWandStack = new ItemStack(Wizardry.magicWand, 1, EnumTier.BASIC.maxCharge);
ItemStack goldNuggetStack = new ItemStack(Items.gold_nugget);
ItemStack stickStack = new ItemStack(Items.stick);
ItemStack bookStack = new ItemStack(Items.book);
ItemStack stringStack = new ItemStack(Items.string);
ItemStack spellBookStack = new ItemStack(Wizardry.spellBook, 1, WizardryRegistry.magicMissile.id());
ItemStack arcaneWorkbenchStack = new ItemStack(Wizardry.arcaneWorkbench);
ItemStack stoneStack = new ItemStack(Blocks.stone);
ItemStack lapisBlockStack = new ItemStack(Blocks.lapis_block);
ItemStack purpleCarpetStack = new ItemStack(Blocks.carpet, 1, 10);
ItemStack wizardHandbookStack = new ItemStack(Wizardry.wizardHandbook);
ItemStack crystalFlowerStack = new ItemStack(Wizardry.crystalFlower);
ItemStack magicCrystalStack1 = new ItemStack(Wizardry.magicCrystal, 2);
ItemStack magicCrystalStack2 = new ItemStack(Wizardry.magicCrystal, 9);
ItemStack crystalBlockStack = new ItemStack(Wizardry.crystalBlock);
ItemStack manaFlaskStack = new ItemStack(Wizardry.manaFlask);
ItemStack bottleStack = new ItemStack(Items.glass_bottle);
ItemStack gunpowderStack = new ItemStack(Items.gunpowder);
ItemStack blazePowderStack = new ItemStack(Items.blaze_powder);
ItemStack spiderEyeStack = new ItemStack(Items.spider_eye);
// Coal or charcoal is equally fine, hence the wildcard value
ItemStack coalStack = new ItemStack(Items.coal, 1, OreDictionary.WILDCARD_VALUE);
ItemStack firebombStack = new ItemStack(Wizardry.firebomb, 3);
ItemStack poisonBombStack = new ItemStack(Wizardry.poisonBomb, 3);
ItemStack smokeBombStack = new ItemStack(Wizardry.smokeBomb, 3);
ItemStack transportationStoneStack = new ItemStack(Wizardry.transportationStone, 2);
ItemStack silkStack = new ItemStack(Wizardry.magicSilk);
ItemStack silkStack1 = new ItemStack(Wizardry.magicSilk, 2);
ItemStack hatStack = new ItemStack(Wizardry.wizardHat);
ItemStack robeStack = new ItemStack(Wizardry.wizardRobe);
ItemStack leggingsStack = new ItemStack(Wizardry.wizardLeggings);
ItemStack bootsStack = new ItemStack(Wizardry.wizardBoots);
ItemStack scrollStack = new ItemStack(Wizardry.blankScroll);
ItemStack paperStack = new ItemStack(Items.paper);
GameRegistry.addRecipe(magicWandStack, " x", " y ", "z ", 'x', magicCrystalStack, 'y', stickStack, 'z', goldNuggetStack);
GameRegistry.addRecipe(spellBookStack, " x ", "xyx", " x ", 'x', magicCrystalStack, 'y', bookStack);
GameRegistry.addRecipe(arcaneWorkbenchStack, "vwv", "xyx", "zzz", 'v', goldNuggetStack, 'w', purpleCarpetStack, 'x', magicCrystalStack, 'y', lapisBlockStack, 'z', stoneStack);
GameRegistry.addRecipe(manaFlaskStack, "yyy", "yxy", "yyy", 'x', bottleStack, 'y', magicCrystalStack);
GameRegistry.addRecipe(transportationStoneStack, " x ", "xyx", " x ", 'x', stoneStack, 'y', magicCrystalStack);
GameRegistry.addRecipe(hatStack, "yyy", "y y", 'y', silkStack);
GameRegistry.addRecipe(robeStack, "y y", "yyy", "yyy", 'y', silkStack);
GameRegistry.addRecipe(leggingsStack, "yyy", "y y", "y y", 'y', silkStack);
GameRegistry.addRecipe(bootsStack, "y y", "y y", 'y', silkStack);
GameRegistry.addRecipe(silkStack1, " x ", "xyx", " x ", 'x', stringStack, 'y', magicCrystalStack);
GameRegistry.addRecipe(crystalBlockStack, "zzz", "zzz", "zzz", 'z', magicCrystalStack);
GameRegistry.addShapelessRecipe(wizardHandbookStack, bookStack, magicCrystalStack);
GameRegistry.addShapelessRecipe(magicCrystalStack1, crystalFlowerStack);
GameRegistry.addShapelessRecipe(magicCrystalStack2, crystalBlockStack);
if(Wizardry.firebombIsCraftable) GameRegistry.addShapelessRecipe(firebombStack, bottleStack, gunpowderStack, blazePowderStack, blazePowderStack);
if(Wizardry.poisonBombIsCraftable) GameRegistry.addShapelessRecipe(poisonBombStack, bottleStack, gunpowderStack, spiderEyeStack, spiderEyeStack);
if(Wizardry.smokeBombIsCraftable) GameRegistry.addShapelessRecipe(smokeBombStack, bottleStack, gunpowderStack, coalStack, coalStack);
if(Wizardry.useAlternateScrollRecipe){
GameRegistry.addShapelessRecipe(scrollStack, paperStack, stringStack, magicCrystalStack);
}else{
GameRegistry.addShapelessRecipe(scrollStack, paperStack, stringStack);
}
// Mana flask recipes
ItemStack miscWandStack;
for(int i=0; i<EnumElement.values().length; i++){
for(int j=0; j<EnumTier.values().length; j++){
miscWandStack = new ItemStack(WizardryUtilities.getWand(EnumTier.values()[j], EnumElement.values()[i]), 1, OreDictionary.WILDCARD_VALUE);
GameRegistry.addShapelessRecipe(miscWandStack, miscWandStack, manaFlaskStack);
}
}
ItemStack miscArmourStack;
for(int i=0; i<EnumElement.values().length; i++){
for(int j=0; j<4; j++){
miscArmourStack = new ItemStack(WizardryUtilities.getArmour(EnumElement.values()[i], j), 1, OreDictionary.WILDCARD_VALUE);
GameRegistry.addShapelessRecipe(miscArmourStack, miscArmourStack, manaFlaskStack);
}
}
}
}
@@ -0,0 +1,881 @@
package electroblob.wizardry;
import java.util.HashSet;
import java.util.List;
import java.util.Random;
import java.util.UUID;
import cpw.mods.fml.common.registry.EntityRegistry;
import electroblob.wizardry.entity.living.EntitySummonedCreature;
import electroblob.wizardry.entity.living.EntityWizard;
import electroblob.wizardry.spell.MindControl;
import electroblob.wizardry.spell.Spell;
import net.minecraft.block.Block;
import net.minecraft.block.material.Material;
import net.minecraft.command.IEntitySelector;
import net.minecraft.entity.Entity;
import net.minecraft.entity.EntityList;
import net.minecraft.entity.EntityLiving;
import net.minecraft.entity.EntityList.EntityEggInfo;
import net.minecraft.entity.monster.IMob;
import net.minecraft.entity.EntityLivingBase;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.init.Blocks;
import net.minecraft.item.Item;
import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.potion.Potion;
import net.minecraft.util.AxisAlignedBB;
import net.minecraft.util.MathHelper;
import net.minecraft.util.MovingObjectPosition;
import net.minecraft.util.Vec3;
import net.minecraft.world.World;
/** This class contains some useful static methods for use anywhere - items, entities, spells, events, blocks, etc.
* @see CommonProxy
* @see WandHelper
* @since Wizardry 1.0 */
public final class WizardryUtilities {
/**
* Finds the nearest floor level to the given y coord within the range specified at the given x and z coords.
* Liquids and other blocks that cannot be built on top of do not count, but stuff like signs does.
* (Technically any block is allowed to be the floor according to the code, but seeing as it searches upwards and
* non-solid blocks usually need a supporting block, the floor is likely to always be solid).
* @param world
* @param x The x coordinate to search in
* @param y The y coordinate to search from
* @param z The z coordinate to search in
* @param range The maximum distance from the given y coordinate to search.
* @return The y coordinate of the closest floor level, or -1 if there is none. Returns the actual level of the floor
* as would be seen in the debug screen when the player is standing on it.
* @see WizardryUtilities#getNearestFloorLevelB(World, int, int, int, int)
*/
public static int getNearestFloorLevel(World world, int x, int y, int z, int range){
int yCoord = -2;
for(int i=y-range;i<=y+range;i++){
// The last bit determines whether the block found to be a suitable floor is closer than the previous one found.
// Edit: Changed the first condition from blockExists() since that method includes air blocks!
if(world.doesBlockHaveSolidTopSurface(world, x, i, z) && (world.isAirBlock(x, i+1, z) || !world.doesBlockHaveSolidTopSurface(world, x, i+1, z))
&& (i-y < yCoord-y || yCoord == -2)){
yCoord = i;
}
}
return yCoord + 1;
}
/**
* Returns whether the block at the given coordinates can be replaced by another one (works as if a block is being placed by a player).
* True for air, liquids, vines, tall grass and snow layers but not for flowers, signs etc.
* This is a shortcut for world.getBlock(x, y, z).isReplaceable(world, x, y, z);
* Not much of a shortcut any more, since block ids have been phased out.
* @see WizardryUtilities#canBlockBeReplacedB(World, int, int, int)
*/
public static boolean canBlockBeReplaced(World world, int x, int y, int z){
// Why does isReplaceable have those arguments? I don't know. They aren't used. It's probably for overriding
// stuff in case these arguments affect whether it is replaceable or not. It is a forge method, after all.
return world.isAirBlock(x, y, z) || world.getBlock(x, y, z).isReplaceable(world, x, y, z);
}
/**
* Returns whether the block at the given coordinates can be replaced by another one (works as if a block is being placed by a player)
* and is not a liquid.
* True for air, vines, tall grass and snow layers but not for flowers, signs etc. or any liquids.
* @see WizardryUtilities#canBlockBeReplaced(World, int, int, int)
*/
public static boolean canBlockBeReplacedB(World world, int x, int y, int z){
return (world.isAirBlock(x, y, z) || world.getBlock(x, y, z).isReplaceable(world, x, y, z)) && !world.getBlock(x, y, z).getMaterial().isLiquid();
}
/**
* Returns whether the block at the given coordinates is unbreakable in survival mode. In vanilla this is true for
* bedrock and end portal frame, for example.
* This is a shortcut for world.getBlockId(x, y, z).getBlockHardness == -1.0f;
* Not much of a shortcut any more, since block ids have been phased out.
*/
public static boolean isBlockUnbreakable(World world, int x, int y, int z){
return world.isAirBlock(x, y, z) ? false : world.getBlock(x, y, z).getBlockHardness(world, x, y, z) == -1.0f;
}
/**
* Finds the nearest floor level to the given y coord within the range specified at the given x and z coords.
* Only works if the block above the floor is actually air and the floor is solid or a liquid.
* @param world
* @param x The x coordinate to search in
* @param y The y coordinate to search from
* @param z The z coordinate to search in
* @param range The maximum distance from the given y coordinate to search.
* @return The y coordinate of the closest floor level, or -1 if there is none. Returns the actual level of the floor
* as would be seen in the debug screen when the player is standing on it.
* @see WizardryUtilities#getNearestFloorLevel(World, int, int, int, int)
*/
public static int getNearestFloorLevelB(World world, int x, int y, int z, int range){
int yCoord = -2;
for(int i=y-range;i<=y+range;i++){
if(world.isAirBlock(x, i+1, z) && (world.getBlock(x, i, z).getMaterial().isLiquid() || world.doesBlockHaveSolidTopSurface(world, x, i, z))
&& (i-y < yCoord-y || yCoord == -2)){
// The last bit determines whether the block found to be a suitable floor is closer than the previous one found.
yCoord = i;
}
}
return yCoord + 1;
}
/**
* Finds the nearest floor level to the given y coord within the range specified at the given x and z coords.
* Everything that is not air is treated as floor, even stuff that can't be walked on.
* @param world
* @param x The x coordinate to search in
* @param y The y coordinate to search from
* @param z The z coordinate to search in
* @param range The maximum distance from the given y coordinate to search.
* @return The y coordinate of the closest floor level, or -1 if there is none. Returns the actual level of the floor
* as would be seen in the debug screen when the player is standing on it.
* @see WizardryUtilities#getNearestFloorLevel(World, int, int, int, int)
*/
public static int getNearestFloorLevelC(World world, int x, int y, int z, int range){
int yCoord = -2;
for(int i=y-range;i<=y+range;i++){
if(world.isAirBlock(x, i+1, z) && (i-y < yCoord-y || yCoord == -2)){
// The last bit determines whether the block found to be a suitable floor is closer than the previous one found.
yCoord = i;
}
}
return yCoord + 1;
}
/**
* Gets the block the specified entity is standing on. Uses {@link MathHelper#floor_double(double)} because casting to int will not
* return the correct coordinate when x or z is negative.
*/
public static Block getBlockEntityIsStandingOn(Entity entity){
return entity.worldObj.getBlock(MathHelper.floor_double(entity.posX), (int)WizardryUtilities.getEntityFeetPos(entity)-1, MathHelper.floor_double(entity.posZ));
}
/**
* Shorthand for {@link WizardryUtilities#getEntitiesWithinRadius(double, double, double, double, World, Class)}
* with EntityLivingBase as the entity type. This is by far the most common use for that method, which is why this
* shorthand exists.
* @param radius The search radius
* @param x The x coordinate to search around
* @param y The y coordinate to search around
* @param z The z coordinate to search around
* @param world The world to search in
*/
public static List<EntityLivingBase> getEntitiesWithinRadius(double radius, double x, double y, double z, World world){
return getEntitiesWithinRadius(radius, x, y, z, world, EntityLivingBase.class);
}
/**
* Returns all entities of the specified type within the specified radius of the given coordinates. This is different to using
* a raw AABB because a raw AABB will search in a cube volume rather than a sphere.
* Note that this does not exclude any entities; if any specific entities are to be excluded this must be
* checked when iterating through the list.
* @see {@link WizardryUtilities#getEntitiesWithinRadius(double, double, double, double, World)}
* @param radius The search radius
* @param x The x coordinate to search around
* @param y The y coordinate to search around
* @param z The z coordinate to search around
* @param world The world to search in
* @param entityType The class of entity to search for; pass in Entity.class for all entities
*/
public static <T extends Entity> List<T> getEntitiesWithinRadius(double radius, double x, double y, double z, World world, Class<T> entityType){
AxisAlignedBB aabb = AxisAlignedBB.getBoundingBox(x - radius, y - radius, z - radius, x + radius, y + radius, z + radius);
List entityList = world.getEntitiesWithinAABB(entityType, aabb);
for(int i=0;i<entityList.size();i++){
if(((Entity)entityList.get(i)).getDistance(x, y, z) > radius){
entityList.remove(i);
}
}
return entityList;
}
// ^ Handy syntax using type parameters here!
// I *think* that eclipse, for whatever reason, was not showing an 'unchecked' warning here. If you think about it,
// if this method returns a generic list type I could, in theory, put strings or something in it and then when
// invoking the method store its result as a List<EntityLivingBase>, which would cause a ClassCastException. Using
// this handy syntax prevents that from happening. TL;DR: Always use type parameters when available, they're there
// for a reason. Minecraft is bad in this respect because it doesn't use them in places like World.
/**
* Gets an entity from its UUID. Note that you should check this isn't null.
* @param world The world the entity is in
* @param id The entity's UUID
* @return The Entity that has the given UUID, or null if no such entity exists in the specified world.
*/
public static Entity getEntityByUUID(World world, UUID id){
for(Object object : world.loadedEntityList){
if(object instanceof Entity){
Entity entity = (Entity)object;
// This is a perfect example of where you need to use .equals() and not ==. For most applications,
// this was unnoticeable until world reload because the UUID instance or entity instance is stored.
// Fixed now though. Of course, you could alternatively compare the string representations.
if(entity.getUniqueID().equals(id)){
return entity;
}
}
}
return null;
}
/**
* Does a block ray trace (NOT entities) from an entity's eyes (i.e. properly...)
*/
public static MovingObjectPosition rayTrace(double range, World world, EntityLivingBase entity, boolean hitLiquids)
{
Vec3 vec3 = Vec3.createVectorHelper(entity.posX, entity.posY + entity.getEyeHeight(), entity.posZ);
Vec3 vec31 = entity.getLookVec();
Vec3 vec32 = vec3.addVector(vec31.xCoord * range, vec31.yCoord * range, vec31.zCoord * range);
return world.rayTraceBlocks(vec3, vec32, hitLiquids);
}
/**
* Helper method which does a rayTrace for entities from an entity's eye level in the direction they are looking
* with a specified range, using the tracePath method. Tidies up the code a bit. Border size defaults to 1.
*
* @param world
* @param entity
* @param range
* @return
*/
public static MovingObjectPosition standardEntityRayTrace(World world, EntityLivingBase entity, double range){
double dx = entity.getLookVec().xCoord * range;
double dy = entity.getLookVec().yCoord * range;
double dz = entity.getLookVec().zCoord * range;
HashSet hashset = new HashSet(1);
hashset.add(entity);
return WizardryUtilities.tracePath(world, (float)entity.posX, (float)(entity.posY + entity.getEyeHeight()), (float)entity.posZ, (float)(entity.posX + dx), (float)(entity.posY + entity.getEyeHeight() + dy), (float)(entity.posZ + dz), 1.0f, hashset, false);
}
/**
* Helper method which does a rayTrace for entities from a entity's eye level in the direction they are looking
* with a specified range and radius, using the tracePath method. Tidies up the code a bit.
*
* @param world
* @param entity
* @param range
* @param borderSize
* @return
*/
public static MovingObjectPosition standardEntityRayTrace(World world, EntityLivingBase entity, double range, float borderSize){
double dx = entity.getLookVec().xCoord * range;
double dy = entity.getLookVec().yCoord * range;
double dz = entity.getLookVec().zCoord * range;
HashSet hashset = new HashSet(1);
hashset.add(entity);
return WizardryUtilities.tracePath(world, (float)entity.posX, (float)(entity.posY + entity.getEyeHeight()), (float)entity.posZ, (float)(entity.posX + dx), (float)(entity.posY + entity.getEyeHeight() + dy), (float)(entity.posZ + dz), borderSize, hashset, false);
}
/**
* Method for ray tracing entities (the useless default method doesn't work, despite EnumHitType having an ENTITY field...)
* You can also use this for seeking.
*
* @param world
* @param x startX
* @param y startY
* @param z startZ
* @param tx endX
* @param ty endY
* @param tz endZ
* @param borderSize extra area to examine around line for entities
* @param excluded any excluded entities (the player, etc)
* @return a MovingObjectPosition of either the block hit (no entity hit), the entity hit (hit an entity), or null for nothing hit
*/
public static MovingObjectPosition tracePath(World world, float x, float y, float z, float tx, float ty, float tz, float borderSize, HashSet<Entity> excluded, boolean collideablesOnly){
Vec3 startVec = Vec3.createVectorHelper(x, y, z);
Vec3 lookVec = Vec3.createVectorHelper(tx-x, ty-y, tz-z);
Vec3 endVec = Vec3.createVectorHelper(tx, ty, tz);
float minX = x < tx ? x : tx;
float minY = y < ty ? y : ty;
float minZ = z < tz ? z : tz;
float maxX = x > tx ? x : tx;
float maxY = y > ty ? y : ty;
float maxZ = z > tz ? z : tz;
AxisAlignedBB bb = AxisAlignedBB.getBoundingBox(minX, minY, minZ, maxX, maxY, maxZ).expand(borderSize, borderSize, borderSize);
List<Entity> allEntities = world.getEntitiesWithinAABBExcludingEntity(null, bb);
MovingObjectPosition blockHit = world.rayTraceBlocks(startVec, endVec);
startVec = Vec3.createVectorHelper(x, y, z);
endVec = Vec3.createVectorHelper(tx, ty, tz);
float maxDistance = (float) endVec.distanceTo(startVec);
if(blockHit!=null)
{
maxDistance = (float) blockHit.hitVec.distanceTo(startVec);
}
Entity closestHitEntity = null;
float closestHit = maxDistance;
float currentHit = 0.f;
AxisAlignedBB entityBb;// = ent.getBoundingBox();
MovingObjectPosition intercept;
for(Entity ent : allEntities)
{
if((ent.canBeCollidedWith() || !collideablesOnly) && ((excluded != null && !excluded.contains(ent)) || excluded == null))
{
float entBorder = ent.getCollisionBorderSize();
entityBb = ent.boundingBox;
if(entityBb!=null)
{
entityBb = entityBb.expand(entBorder, entBorder, entBorder);
intercept = entityBb.calculateIntercept(startVec, endVec);
if(intercept!=null)
{
currentHit = (float) intercept.hitVec.distanceTo(startVec);
if(currentHit < closestHit || currentHit==0)
{
closestHit = currentHit;
closestHitEntity = ent;
}
}
}
}
}
if(closestHitEntity!=null)
{
blockHit = new MovingObjectPosition(closestHitEntity);
}
return blockHit;
}
// Just what benefit does having posY be the eye position on the first person client actually give?
/**
* Gets the y coordinate of the given player's eyes. This is to cover an inconsistency between the value of
* EntityPlayer.posY on the first person client and everywhere else; in first person (i.e. when
* Minecraft.getMinecraft().thePlayer == player) player.posY is the eye position, but everywhere else it is the
* feet position. This is intended for use when spawning particles, since this is the only situation where the
* discrepancy is likely to matter.
*/
public static double getPlayerEyesPos(EntityPlayer player){
return Wizardry.proxy.getPlayerEyesPos(player);
}
/**
* Gets the y coordinate of the given entity's feet. This is to cover an inconsistency between the value of
* EntityPlayer.posY on the first person client and everywhere else; in first person (i.e. when
* Minecraft.getMinecraft().thePlayer == player) player.posY is the eye position, but everywhere else it is the
* feet position. This is intended for use when spawning particles, since this is the only situation where the
* discrepancy is likely to matter.
*/
public static double getEntityFeetPos(Entity entity){
if(entity instanceof EntityPlayer){
// For some reason, EntityPlayer.getEyeHeight() always returns 0.12, so I have replicated the behaviour
// of the same method from EntityLivingBase and subtracted that instead.
return getPlayerEyesPos((EntityPlayer)entity) - entity.height*0.85f;
}else{
return entity.posY;
}
}
/**
* Verifies that the given string is a valid string representation of a UUID. More specifically, returns true if and
* only if the given string is not null and matches the regular expression:<p>
* <center><code>/^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$/<p></code></center>
* which is the regex equivlent of the standard string representation of a UUID as described in {@link UUID#toString()}.
* This method is intended to be used as a check to prevent an {@link IllegalArgumentException} from occurring when
* calling {@link UUID#fromString(String)}.
* @param string The string to be checked
* @return Whether the given string is a valid string representation of a UUID
*/
public static boolean verifyUUIDString(String string){
return string != null && string.matches("/^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$/");
}
/**
* Returns whether the given target can be attacked by the given attacker. It is up to the caller of this method
* to work out what this means; it doesn't necessarily mean the target is completely immune (for example, revenge
* targeting might reasonably bypass this). This method is intended for use where the damage is indirect and/or
* unavoidable; direct attacks should not check this method. Currently this means the following situations check
* this method:
* <p>
* - AI targeting for summoned creatures<br>
* - AI targeting for mind-controlled creatures<br>
* - Constructs with an area of effect<br>
* - Instantaneous spells with an area of effect around the caster (e.g. forest's curse, thunderstorm)<br>
* - Any lightning chaining effects<br>
* - Any projectiles which seek targets
* <p>
* Also note that the friendly fire option is dealt with
* in the event handler. This method acts as a sort of wrapper for all the ADS stuff in {@link ExtendedPlayer}; more
* details about the ally designation system can be found there.
*
* @param attacker The entity that cast the spell originally
* @param target The entity being attacked
*
* @return False under any of the following circumstances, true otherwise:
* <p>
* - The target is the attacker (this isn't as stupid as it sounds - anything with an AoE might cause this to be
* true, as can summoned creatures)
* <p>
* - The target and the attacker are both players and the target is an ally of the attacker (but the
* attacker need not be an ally of the target)
* <p>
* - The target is a creature that was summoned/controlled by the attacker or by an ally of the attacker.
*/
public static boolean isValidTarget(Entity attacker, Entity target){
// Don't need to check if either entity is null since instanceof does this anyway.
// Tests whether the target is the attacker
if(target == attacker) return false;
// Tests whether the target is a creature that was summoned by the attacker
if(target instanceof EntitySummonedCreature && ((EntitySummonedCreature)target).getCaster() == attacker){
return false;
}
// Tests whether the target is a creature that was mind controlled by the attacker
if(target instanceof EntityLiving && ((EntityLivingBase)target).isPotionActive(Wizardry.mindControl)){
NBTTagCompound entityNBT = target.getEntityData();
if(entityNBT != null && entityNBT.hasKey(MindControl.NBT_KEY)){
if(attacker == WizardryUtilities.getEntityByUUID(target.worldObj, UUID.fromString(entityNBT.getString(MindControl.NBT_KEY)))){
return false;
}
}
}
// Ally section
if(attacker instanceof EntityPlayer && ExtendedPlayer.get((EntityPlayer)attacker) != null){
if(target instanceof EntityPlayer){
// Tests whether the target is an ally of the attacker
if(ExtendedPlayer.get((EntityPlayer)attacker).isPlayerAlly((EntityPlayer)target)){
return false;
}
}else if(target instanceof EntitySummonedCreature){
// Tests whether the target is a creature that was summoned by an ally of the attacker
if(((EntitySummonedCreature)target).getCaster() instanceof EntityPlayer
&& ExtendedPlayer.get((EntityPlayer)attacker).isPlayerAlly((EntityPlayer)((EntitySummonedCreature)target).getCaster())){
return false;
}
}else if(target instanceof EntityLiving && ((EntityLivingBase)target).isPotionActive(Wizardry.mindControl)){
// Tests whether the target is a creature that was mind controlled by an ally of the attacker
NBTTagCompound entityNBT = target.getEntityData();
if(entityNBT != null && entityNBT.hasKey(MindControl.NBT_KEY)){
Entity controller = WizardryUtilities.getEntityByUUID(target.worldObj, UUID.fromString(entityNBT.getString(MindControl.NBT_KEY)));
if(controller instanceof EntityPlayer && ExtendedPlayer.get((EntityPlayer)attacker).isPlayerAlly((EntityPlayer)controller)){
return false;
}
}
}
}
return true;
}
/** Helper method for testing if the second player is an ally of the first player. Makes the code neater. */
public static boolean isPlayerAlly(EntityPlayer allyOf, EntityPlayer possibleAlly) {
ExtendedPlayer properties = ExtendedPlayer.get(allyOf);
if(properties != null && properties.isPlayerAlly(possibleAlly)) return true;
return false;
}
/**
* See {@link WizardryUtilities#getStandardWeightedRandomSpellId(Random, boolean)}. isContinuous defaults to false.
*/
public static int getStandardWeightedRandomSpellId(Random random){
return getStandardWeightedRandomSpellId(random, false);
}
/**
* Helper method which gets a spell id according to the standard weighting. The tier is a weighted random value;
* the actual spell within that tier is completely random. Will not return the id of a spell which has been
* disabled in the config. This is for simple stuff like chests and drops; more complex generators like wizard
* trades don't use this method.
* <p>
* For reference, the standard weighting is as follows:
* Basic: 60%, Apprentice: 25%, Advanced: 10%, Master: 5%
*
* @param random An instance of the Random class
* @param nonContinuous Whether the spells must be non-continuous (used for scrolls)
* @return A random spell id number
*/
public static int getStandardWeightedRandomSpellId(Random random, boolean nonContinuous){
int randomiser = random.nextInt(20);
EnumTier tier;
if(randomiser < 12){
tier = EnumTier.BASIC;
}else if(randomiser < 17){
tier = EnumTier.APPRENTICE;
}else if(randomiser < 19){
tier = EnumTier.ADVANCED;
}else{
tier = EnumTier.MASTER;
}
List<Spell> spells = Spell.getSpells(new Spell.TierElementFilter(tier, null));
if(nonContinuous) spells.retainAll(Spell.getSpells(Spell.nonContinuousSpells));
// Ensures the tier chosen actually has spells in it, and if not uses BASIC instead. BASIC always has at least
// the NONE spell since this spell cannot be disabled.
if(spells.isEmpty()){
spells = Spell.getSpells(new Spell.TierElementFilter(EnumTier.BASIC, null));
if(nonContinuous) spells.retainAll(Spell.getSpells(Spell.nonContinuousSpells));
}
// Finds a random spell in the list and returns its id.
return spells.get(random.nextInt(spells.size())).id();
}
/**
* Helper method to return the appropriate armour item based on element and slot.
* Currently used to iterate through armour for registering charging recipes and for chest generation.
* @param slot 0 = head, 1 = body, 2 = legs, 3 = feet
*/
public static Item getArmour(EnumElement element, int slot){
Item armour = null;
if(element == null){
switch(slot){
case 0:
armour = Wizardry.wizardHat;
break;
case 1:
armour = Wizardry.wizardRobe;
break;
case 2:
armour = Wizardry.wizardLeggings;
break;
case 3:
armour = Wizardry.wizardBoots;
break;
default:
break;
}
}else{
switch(element){
case FIRE:
switch(slot){
case 0:
armour = Wizardry.wizardHatFire;
break;
case 1:
armour = Wizardry.wizardRobeFire;
break;
case 2:
armour = Wizardry.wizardLeggingsFire;
break;
case 3:
armour = Wizardry.wizardBootsFire;
break;
default:
break;
}
break;
case HEALING:
switch(slot){
case 0:
armour = Wizardry.wizardHatHealing;
break;
case 1:
armour = Wizardry.wizardRobeHealing;
break;
case 2:
armour = Wizardry.wizardLeggingsHealing;
break;
case 3:
armour = Wizardry.wizardBootsHealing;
break;
default:
break;
}
break;
case ICE:
switch(slot){
case 0:
armour = Wizardry.wizardHatIce;
break;
case 1:
armour = Wizardry.wizardRobeIce;
break;
case 2:
armour = Wizardry.wizardLeggingsIce;
break;
case 3:
armour = Wizardry.wizardBootsIce;
break;
default:
break;
}
break;
case LIGHTNING:
switch(slot){
case 0:
armour = Wizardry.wizardHatLightning;
break;
case 1:
armour = Wizardry.wizardRobeLightning;
break;
case 2:
armour = Wizardry.wizardLeggingsLightning;
break;
case 3:
armour = Wizardry.wizardBootsLightning;
break;
default:
break;
}
break;
case NECROMANCY:
switch(slot){
case 0:
armour = Wizardry.wizardHatNecromancy;
break;
case 1:
armour = Wizardry.wizardRobeNecromancy;
break;
case 2:
armour = Wizardry.wizardLeggingsNecromancy;
break;
case 3:
armour = Wizardry.wizardBootsNecromancy;
break;
default:
break;
}
break;
case SORCERY:
switch(slot){
case 0:
armour = Wizardry.wizardHatSorcery;
break;
case 1:
armour = Wizardry.wizardRobeSorcery;
break;
case 2:
armour = Wizardry.wizardLeggingsSorcery;
break;
case 3:
armour = Wizardry.wizardBootsSorcery;
break;
default:
break;
}
break;
case EARTH:
switch(slot){
case 0:
armour = Wizardry.wizardHatEarth;
break;
case 1:
armour = Wizardry.wizardRobeEarth;
break;
case 2:
armour = Wizardry.wizardLeggingsEarth;
break;
case 3:
armour = Wizardry.wizardBootsEarth;
break;
default:
break;
}
break;
default:
switch(slot){
case 0:
armour = Wizardry.wizardHat;
break;
case 1:
armour = Wizardry.wizardRobe;
break;
case 2:
armour = Wizardry.wizardLeggings;
break;
case 3:
armour = Wizardry.wizardBoots;
break;
default:
break;
}
break;
}
}
return armour;
}
/**
* Helper method to return the appropriate wand based on tier and element.
* Currently used in the packet handler for upgrading wands, for chest generation and to iterate through
* wands for charging recipes.
*/
public static Item getWand(EnumTier tier, EnumElement element){
Item wand = null;
if(element == null){
switch(tier){
case BASIC:
wand = Wizardry.magicWand;
break;
case APPRENTICE:
wand = Wizardry.apprenticeWand;
break;
case ADVANCED:
wand = Wizardry.advancedWand;
break;
case MASTER:
wand = Wizardry.masterWand;
break;
default:
break;
}
}else{
switch(element){
case FIRE:
switch(tier){
case BASIC:
wand = Wizardry.basicFireWand;
break;
case APPRENTICE:
wand = Wizardry.apprenticeFireWand;
break;
case ADVANCED:
wand = Wizardry.advancedFireWand;
break;
case MASTER:
wand = Wizardry.masterFireWand;
break;
default:
break;
}
break;
case HEALING:
switch(tier){
case BASIC:
wand = Wizardry.basicHealingWand;
break;
case APPRENTICE:
wand = Wizardry.apprenticeHealingWand;
break;
case ADVANCED:
wand = Wizardry.advancedHealingWand;
break;
case MASTER:
wand = Wizardry.masterHealingWand;
break;
default:
break;
}
break;
case ICE:
switch(tier){
case BASIC:
wand = Wizardry.basicIceWand;
break;
case APPRENTICE:
wand = Wizardry.apprenticeIceWand;
break;
case ADVANCED:
wand = Wizardry.advancedIceWand;
break;
case MASTER:
wand = Wizardry.masterIceWand;
break;
default:
break;
}
break;
case LIGHTNING:
switch(tier){
case BASIC:
wand = Wizardry.basicLightningWand;
break;
case APPRENTICE:
wand = Wizardry.apprenticeLightningWand;
break;
case ADVANCED:
wand = Wizardry.advancedLightningWand;
break;
case MASTER:
wand = Wizardry.masterLightningWand;
break;
default:
break;
}
break;
case NECROMANCY:
switch(tier){
case BASIC:
wand = Wizardry.basicNecromancyWand;
break;
case APPRENTICE:
wand = Wizardry.apprenticeNecromancyWand;
break;
case ADVANCED:
wand = Wizardry.advancedNecromancyWand;
break;
case MASTER:
wand = Wizardry.masterNecromancyWand;
break;
default:
break;
}
break;
case SORCERY:
switch(tier){
case BASIC:
wand = Wizardry.basicSorceryWand;
break;
case APPRENTICE:
wand = Wizardry.apprenticeSorceryWand;
break;
case ADVANCED:
wand = Wizardry.advancedSorceryWand;
break;
case MASTER:
wand = Wizardry.masterSorceryWand;
break;
default:
break;
}
break;
case EARTH:
switch(tier){
case BASIC:
wand = Wizardry.basicEarthWand;
break;
case APPRENTICE:
wand = Wizardry.apprenticeEarthWand;
break;
case ADVANCED:
wand = Wizardry.advancedEarthWand;
break;
case MASTER:
wand = Wizardry.masterEarthWand;
break;
default:
break;
}
break;
default:
switch(tier){
case BASIC:
wand = Wizardry.magicWand;
break;
case APPRENTICE:
wand = Wizardry.apprenticeWand;
break;
case ADVANCED:
wand = Wizardry.advancedWand;
break;
case MASTER:
wand = Wizardry.masterWand;
break;
default:
break;
}
break;
}
}
return wand;
}
}
File diff suppressed because it is too large Load Diff
@@ -1,81 +0,0 @@
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;
import net.minecraft.advancements.PlayerAdvancements;
import net.minecraft.advancements.critereon.AbstractCriterionInstance;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.entity.player.EntityPlayerMP;
import net.minecraft.util.ResourceLocation;
/**
* This class implements a generic custom advancement trigger that can be fired from any point in
* the code. This replaces the achievement system in instances where the JSON advancement descriptions
* cannot properly capture the advancement-worthy events. Where possible, advancement conditions
* should be triggered by JSON descriptions and vanilla advancement triggers.
*
* @author 12foo
* @since 4.1.0
*/
public class CustomAdvancementTrigger implements ICriterionTrigger<CustomAdvancementTrigger.Instance> {
private final ResourceLocation id;
private final SetMultimap<PlayerAdvancements, Listener<? extends ICriterionInstance>> listeners = HashMultimap.create();
/**
* This is a dummy criterion instance that does nothing on its own (but it is bound to this
* trigger, and via listeners to the player). We later fire this manually when we want the
* advancement to happen.
*/
public static class Instance extends AbstractCriterionInstance {
public Instance(ResourceLocation triggerId) {
super(triggerId);
}
}
public CustomAdvancementTrigger(String name) {
super();
id = new ResourceLocation(Wizardry.MODID, name);
}
@Override
public ResourceLocation getId() {
return id;
}
@Override
public void addListener(PlayerAdvancements playerAdvancementsIn, Listener<Instance> listener) {
listeners.put(playerAdvancementsIn, listener);
}
@Override
public void removeListener(PlayerAdvancements playerAdvancementsIn, Listener<Instance> listener) {
listeners.remove(playerAdvancementsIn, listener);
}
@Override
public void removeAllListeners(PlayerAdvancements playerAdvancementsIn) {
listeners.removeAll(playerAdvancementsIn);
}
@Override
public Instance deserializeInstance(JsonObject json, JsonDeserializationContext context) {
// Every time a trigger with this name is deserialized from the JSON, we just return a new
// dummy criterion instance.
return new CustomAdvancementTrigger.Instance(id);
}
public void triggerFor(EntityPlayer player) {
// Fire our dummy criterion manually on all advancements of the player, thereby granting
// the ones that match it.
if (player instanceof EntityPlayerMP) {
final PlayerAdvancements advances = ((EntityPlayerMP) player).getAdvancements();
listeners.get(advances).forEach((listener) -> listener.grantCriterion(advances));
}
}
}
@@ -1,139 +0,0 @@
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);
}
}
}
}
}
@@ -1,143 +0,0 @@
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);
}
}
}
}
}
@@ -1,117 +0,0 @@
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];
}
}
}
@@ -1,136 +0,0 @@
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.WorldGenWizardryStructure;
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 WorldGenWizardryStructure structureType;
public Instance(ResourceLocation criterionIn, String name){
super(criterionIn);
this.structureType = WorldGenWizardryStructure.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);
}
}
}
}
}
@@ -1,135 +0,0 @@
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 or imbuement altar. The majority of any
* ICriterionTrigger class is just boilerplate, and this is no exception. */
public class WizardryContainerTrigger implements ICriterionTrigger<WizardryContainerTrigger.Instance> {
private final ResourceLocation id;
private final Map<PlayerAdvancements, WizardryContainerTrigger.Listeners> listeners = Maps.newHashMap();
public WizardryContainerTrigger(ResourceLocation id){
this.id = id;
}
public ResourceLocation getId(){
return this.id;
}
public void addListener(PlayerAdvancements advancements, Listener<WizardryContainerTrigger.Instance> listener){
WizardryContainerTrigger.Listeners listeners = this.listeners.get(advancements);
if(listeners == null){
listeners = new WizardryContainerTrigger.Listeners(advancements);
this.listeners.put(advancements, listeners);
}
listeners.add(listener);
}
public void removeListener(PlayerAdvancements advancements, Listener<WizardryContainerTrigger.Instance> listener){
WizardryContainerTrigger.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 WizardryContainerTrigger.Instance deserializeInstance(JsonObject json, JsonDeserializationContext context){
return new WizardryContainerTrigger.Instance(this.id, ItemPredicate.deserialize(json.get("item")));
}
public void trigger(EntityPlayerMP player, ItemStack stack){
WizardryContainerTrigger.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<WizardryContainerTrigger.Instance>> listeners = Sets.newHashSet();
public Listeners(PlayerAdvancements advancements){
this.playerAdvancements = advancements;
}
public boolean isEmpty(){
return this.listeners.isEmpty();
}
public void add(Listener<WizardryContainerTrigger.Instance> listener){
this.listeners.add(listener);
}
public void remove(Listener<WizardryContainerTrigger.Instance> listener){
this.listeners.remove(listener);
}
public void trigger(ItemStack stack){
List<Listener<WizardryContainerTrigger.Instance>> list = null;
for(Listener<WizardryContainerTrigger.Instance> listener : this.listeners){
if(listener.getCriterionInstance().test(stack)){
if(list == null){
list = Lists.newArrayList();
}
list.add(listener);
}
}
if(list != null){
for(Listener<WizardryContainerTrigger.Instance> listener : list){
listener.grantCriterion(this.playerAdvancements);
}
}
}
}
}
@@ -1,96 +0,0 @@
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,91 +1,126 @@
package electroblob.wizardry.block;
import java.util.Random;
import electroblob.wizardry.Wizardry;
import electroblob.wizardry.WizardryGuiHandler;
import electroblob.wizardry.client.GuiArcaneWorkbench;
import electroblob.wizardry.tileentity.TileEntityArcaneWorkbench;
import net.minecraft.block.Block;
import net.minecraft.block.BlockContainer;
import net.minecraft.block.material.Material;
import net.minecraft.block.state.BlockFaceShape;
import net.minecraft.block.state.IBlockState;
import net.minecraft.client.renderer.texture.IIconRegister;
import net.minecraft.creativetab.CreativeTabs;
import net.minecraft.entity.item.EntityItem;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.inventory.InventoryHelper;
import net.minecraft.item.ItemStack;
import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.tileentity.TileEntity;
import net.minecraft.util.EnumBlockRenderType;
import net.minecraft.util.EnumFacing;
import net.minecraft.util.EnumHand;
import net.minecraft.util.math.AxisAlignedBB;
import net.minecraft.util.math.BlockPos;
import net.minecraft.world.IBlockAccess;
import net.minecraft.util.IIcon;
import net.minecraft.world.World;
public class BlockArcaneWorkbench extends BlockContainer {
private static final AxisAlignedBB AABB = new AxisAlignedBB(0.0D, 0.0D, 0.0D, 1.0D, 0.75D, 1.0D);
public class BlockArcaneWorkbench extends BlockContainer{
public IIcon[] icons = new IIcon[6];
public BlockArcaneWorkbench(){
super(Material.ROCK);
this.setLightLevel(0.8f);
super(Material.rock);
this.setBlockBounds(0.0F, 0.0F, 0.0F, 1.0F, 0.75F, 1.0F);
this.setLightLevel(0.8f);
this.setCreativeTab(CreativeTabs.tabDecorations);
this.setBlockTextureName("wizardry:arcane_workbench");
}
@Override
public AxisAlignedBB getBoundingBox(IBlockState state, IBlockAccess source, BlockPos pos){
return AABB;
public void registerBlockIcons(IIconRegister par1IconRegister) {
this.icons[0] = par1IconRegister.registerIcon(this.textureName + "_bottom");
this.icons[1] = par1IconRegister.registerIcon(this.textureName + "_top");
this.icons[2] = par1IconRegister.registerIcon(this.textureName + "_side");
this.icons[3] = par1IconRegister.registerIcon(this.textureName + "_side");
this.icons[4] = par1IconRegister.registerIcon(this.textureName + "_side");
this.icons[5] = par1IconRegister.registerIcon(this.textureName + "_side");
}
@Override
public IIcon getIcon(int side, int meta) {
return this.icons[side];
}
@Override
public TileEntity createNewTileEntity(World world, int metadata){
public TileEntity createNewTileEntity(World world, int metadata) {
return new TileEntityArcaneWorkbench();
}
@Override
public boolean isNormalCube(IBlockState state, IBlockAccess world, BlockPos pos){
return false;
}
public boolean renderAsNormalBlock()
{
return false;
}
@Override
public EnumBlockRenderType getRenderType(IBlockState state){
return EnumBlockRenderType.MODEL;
}
public boolean isOpaqueCube()
{
return false;
}
@Override
public boolean isOpaqueCube(IBlockState state){
return false;
}
@Override
public boolean 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){
TileEntity tileEntity = world.getTileEntity(pos);
if(tileEntity == null || player.isSneaking()){
public boolean onBlockActivated(World world, int x, int y, int z, EntityPlayer player, int metadata, float what, float these, float are) {
TileEntity tileEntity = world.getTileEntity(x, y, z);
if (tileEntity == null || player.isSneaking()) {
return false;
}
player.openGui(Wizardry.instance, WizardryGuiHandler.ARCANE_WORKBENCH, world, pos.getX(), pos.getY(), pos.getZ());
player.openGui(Wizardry.instance, WizardryGuiHandler.ARCANE_WORKBENCH, world, x, y, z);
return true;
}
@Override
public void breakBlock(World world, BlockPos pos, IBlockState block){
TileEntity tileentity = world.getTileEntity(pos);
public void breakBlock(World par1World, int par2, int par3, int par4, Block block, int par6)
{
Random random = new Random();
TileEntityArcaneWorkbench tileentityarcaneworkbench = (TileEntityArcaneWorkbench)par1World.getTileEntity(par2, par3, par4);
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 != null)
{
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.stackSize > 0; par1World.spawnEntityInWorld(entityitem))
{
int k1 = random.nextInt(21) + 10;
if (k1 > itemstack.stackSize)
{
k1 = itemstack.stackSize;
}
itemstack.stackSize -= k1;
entityitem = new EntityItem(par1World, (double)((float)par2 + f), (double)((float)par3 + f1), (double)((float)par4 + f2), new ItemStack(itemstack.getItem(), k1, itemstack.getItemDamage()));
float f3 = 0.05F;
entityitem.motionX = (double)((float)random.nextGaussian() * f3);
entityitem.motionY = (double)((float)random.nextGaussian() * f3 + 0.2F);
entityitem.motionZ = (double)((float)random.nextGaussian() * f3);
if (itemstack.hasTagCompound())
{
entityitem.getEntityItem().setTagCompound((NBTTagCompound)itemstack.getTagCompound().copy());
}
}
}
}
//par1World.func_96440_m(par2, par3, par4, block);
}
super.breakBlock(world, pos, block);
}
super.breakBlock(par1World, par2, par3, par4, block, par6);
}
}
@@ -1,367 +0,0 @@
package electroblob.wizardry.block;
import com.google.common.collect.ImmutableList;
import electroblob.wizardry.Settings;
import electroblob.wizardry.Wizardry;
import electroblob.wizardry.WizardryGuiHandler;
import electroblob.wizardry.registry.WizardryItems;
import electroblob.wizardry.registry.WizardryTabs;
import electroblob.wizardry.tileentity.TileEntityBookshelf;
import net.minecraft.block.BlockHorizontal;
import net.minecraft.block.ITileEntityProvider;
import net.minecraft.block.SoundType;
import net.minecraft.block.material.Material;
import net.minecraft.block.properties.PropertyInteger;
import net.minecraft.block.state.BlockFaceShape;
import net.minecraft.block.state.BlockStateContainer;
import net.minecraft.block.state.IBlockState;
import net.minecraft.entity.Entity;
import net.minecraft.entity.EntityLivingBase;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.init.Items;
import net.minecraft.inventory.IInventory;
import net.minecraft.inventory.InventoryHelper;
import net.minecraft.item.Item;
import net.minecraft.tileentity.TileEntity;
import net.minecraft.util.*;
import net.minecraft.util.math.BlockPos;
import net.minecraft.world.IBlockAccess;
import net.minecraft.world.IWorldEventListener;
import net.minecraft.world.World;
import net.minecraftforge.common.property.IExtendedBlockState;
import net.minecraftforge.common.property.Properties;
import net.minecraftforge.event.world.WorldEvent;
import net.minecraftforge.fml.common.Mod;
import net.minecraftforge.fml.common.event.FMLInitializationEvent;
import net.minecraftforge.fml.common.event.FMLPreInitializationEvent;
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
import org.apache.commons.lang3.ArrayUtils;
import javax.annotation.Nullable;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Supplier;
@Mod.EventBusSubscriber
public class BlockBookshelf extends BlockHorizontal implements ITileEntityProvider {
/** When a bookshelf block (of any kind specified in the config) is added or removed, players within this range will
* be notified of the change. */
public static final double PLAYER_NOTIFY_RANGE = 32;
public static final int SLOT_COUNT = 12;
public static final UnlistedPropertyInt[] BOOKS = new UnlistedPropertyInt[SLOT_COUNT];
private static final Map<Supplier<Item>, ResourceLocation> BOOK_TEXTURE_MAP = new HashMap<>();
private static ImmutableList<Item> bookItems;
private static ImmutableList<ResourceLocation> bookTextures;
public BlockBookshelf(){
super(Material.WOOD);
this.setDefaultState(this.blockState.getBaseState().withProperty(FACING, EnumFacing.NORTH));
this.setCreativeTab(WizardryTabs.WIZARDRY);
this.setHardness(2.0F);
this.setResistance(5.0F);
this.setSoundType(SoundType.WOOD);
}
@Override
protected BlockStateContainer createBlockState(){
// IProperty<?>[] properties = { FACING };
// return new BlockStateContainer(this, ArrayUtils.addAll(properties, BOOKS));
return new BlockStateContainer.Builder(this).add(FACING).add(BOOKS).build();
}
@Override
public BlockFaceShape getBlockFaceShape(IBlockAccess world, IBlockState state, BlockPos pos, EnumFacing face){
return state.getValue(FACING).getAxis() == face.getAxis() ? BlockFaceShape.UNDEFINED : BlockFaceShape.SOLID;
}
@Override
public IBlockState getStateForPlacement(World world, BlockPos pos, EnumFacing facing, float hitX, float hitY, float hitZ, int meta, EntityLivingBase placer){
return this.getDefaultState().withProperty(FACING, placer.getHorizontalFacing().getOpposite());
}
// Why on earth are these two not in BlockHorizontal?
@Override
public IBlockState withRotation(IBlockState state, Rotation rotation){
return state.withProperty(FACING, rotation.rotate(state.getValue(FACING)));
}
@Override
public IBlockState withMirror(IBlockState state, Mirror mirror){
return state.withRotation(mirror.toRotation(state.getValue(FACING)));
}
@Override
public void onBlockAdded(World worldIn, BlockPos pos, IBlockState state){
super.onBlockAdded(worldIn, pos, state);
}
@Override
public void breakBlock(World world, BlockPos pos, IBlockState block){
TileEntity tileentity = world.getTileEntity(pos);
if(tileentity instanceof TileEntityBookshelf){
InventoryHelper.dropInventoryItems(world, pos, (TileEntityBookshelf)tileentity);
}
super.breakBlock(world, pos, block); // For blocks that don't extend BlockContainer, this removes the TE
}
@Override
public IBlockState getStateFromMeta(int meta){
EnumFacing enumfacing = EnumFacing.byIndex(meta);
if(enumfacing.getAxis() == EnumFacing.Axis.Y) enumfacing = EnumFacing.NORTH;
return this.getDefaultState().withProperty(FACING, enumfacing);
}
@Override
public int getMetaFromState(IBlockState state){
return state.getValue(FACING).getIndex();
}
@Override
public IBlockState getExtendedState(IBlockState state, IBlockAccess world, BlockPos pos){
IExtendedBlockState s = (IExtendedBlockState)super.getExtendedState(state, world, pos);
if(world.getTileEntity(pos) instanceof TileEntityBookshelf){
TileEntityBookshelf tileentity = ((TileEntityBookshelf)world.getTileEntity(pos));
for(int i = 0; i < tileentity.getSizeInventory(); i++){
if(tileentity.getStackInSlot(i).isEmpty()){
s = s.withProperty(BOOKS[i], bookItems.size());
}else{
Item item = tileentity.getStackInSlot(i).getItem();
// Default to the standard spell book texture, which will always be the first item in the list
s = s.withProperty(BOOKS[i], bookItems.contains(item) ? bookItems.indexOf(item) : 0);
}
}
}
return s;
}
@Nullable
@Override
public TileEntity createNewTileEntity(World world, int meta){
return new TileEntityBookshelf();
}
@Override
public boolean onBlockActivated(World world, BlockPos pos, IBlockState block, EntityPlayer player, EnumHand hand,
EnumFacing side, float hitX, float hitY, float hitZ){
TileEntity tileEntity = world.getTileEntity(pos);
if(tileEntity == null || player.isSneaking()){
return false;
}
player.openGui(Wizardry.instance, WizardryGuiHandler.BOOKSHELF, world, pos.getX(), pos.getY(), pos.getZ());
return true;
}
@Override
public boolean hasComparatorInputOverride(IBlockState state){
return true;
}
@Override
public int getComparatorInputOverride(IBlockState state, World world, BlockPos pos){
TileEntity tileEntity = world.getTileEntity(pos);
if(tileEntity instanceof TileEntityBookshelf){
int slotsOccupied = 0;
for(int i = 0; i < ((TileEntityBookshelf)tileEntity).getSizeInventory(); i++){
if(!((TileEntityBookshelf)tileEntity).getStackInSlot(i).isEmpty()) slotsOccupied++;
}
return slotsOccupied;
}
return super.getComparatorInputOverride(state, world, pos);
}
@Override
public boolean eventReceived(IBlockState state, World world, BlockPos pos, int id, int param){
super.eventReceived(state, world, pos, id, param);
TileEntity tileentity = world.getTileEntity(pos);
return tileentity != null && tileentity.receiveClientEvent(id, param);
}
/** Returns the list of book textures, in ID order. */
public static ImmutableList<ResourceLocation> getBookTextures(){
// ModelBookshelf calls this before init() so we need to return the map values as a fallback
return bookTextures == null ? ImmutableList.copyOf(BOOK_TEXTURE_MAP.values()) : bookTextures;
}
/** Returns the list of registered book items, in ID order. */
public static ImmutableList<Item> getBookItems(){
return bookItems;
}
/** Called during block registration to initialise the block properties for each book. */
public static void initBookProperties(){
for(int i=0; i<SLOT_COUNT; i++){
BOOKS[i] = new UnlistedPropertyInt("book" + i);
}
}
// Based on BlockFluid's UnlistedPropertyBool; this is only needed because of Java's weird restrictions on generics
private static final class UnlistedPropertyInt extends Properties.PropertyAdapter<Integer> {
public UnlistedPropertyInt(String name){
// Max is inclusive here, but we're using the largest value for no book so there's an extra one (can't use -1)
super(PropertyInteger.create(name, 0, BOOK_TEXTURE_MAP.size()));
}
}
/** Called from {@link Wizardry#init(FMLInitializationEvent)} to retrieve the actual book items and compile them
* and their textures into immutable lists. */
public static void compileBookModelTextures(){
ImmutableList.Builder<Item> itemListBuider = ImmutableList.builder();
ImmutableList.Builder<ResourceLocation> textureListBuilder = ImmutableList.builder();
for(Map.Entry<Supplier<Item>, ResourceLocation> entry : BOOK_TEXTURE_MAP.entrySet()){
itemListBuider.add(entry.getKey().get());
textureListBuilder.add(entry.getValue());
}
bookItems = itemListBuider.build();
bookTextures = textureListBuilder.build();
}
/** Called from {@link Wizardry#preInit(FMLPreInitializationEvent)} to register the default set of book textures. */
public static void registerStandardBookModelTextures(){
// Vanilla Minecraft books
// Regular brown books are the default so they're registered first
registerBookModelTexture(() -> Items.BOOK, new ResourceLocation(Wizardry.MODID, "blocks/books_brown"));
registerBookModelTexture(() -> Items.WRITABLE_BOOK, new ResourceLocation(Wizardry.MODID, "blocks/books_brown"));
registerBookModelTexture(() -> Items.WRITTEN_BOOK, new ResourceLocation(Wizardry.MODID, "blocks/books_brown"));
registerBookModelTexture(() -> Items.ENCHANTED_BOOK, new ResourceLocation(Wizardry.MODID, "blocks/books_enchanted"));
// Wizardry books
registerBookModelTexture(() -> WizardryItems.spell_book, new ResourceLocation(Wizardry.MODID, "blocks/books_red"));
registerBookModelTexture(() -> WizardryItems.wizard_handbook, new ResourceLocation(Wizardry.MODID, "blocks/books_blue"));
registerBookModelTexture(() -> WizardryItems.arcane_tome, new ResourceLocation(Wizardry.MODID, "blocks/books_purple"));
registerBookModelTexture(() -> WizardryItems.ruined_spell_book, new ResourceLocation(Wizardry.MODID, "blocks/books_brown"));
registerBookModelTexture(() -> WizardryItems.scroll, new ResourceLocation(Wizardry.MODID, "blocks/scrolls_blue"));
registerBookModelTexture(() -> WizardryItems.blank_scroll, new ResourceLocation(Wizardry.MODID, "blocks/scrolls_blue"));
registerBookModelTexture(() -> WizardryItems.identification_scroll, new ResourceLocation(Wizardry.MODID, "blocks/scrolls_purple"));
// Can't use the map in WandHelper because this is called from preInit (addons will have to add theirs manually)
registerBookModelTexture(() -> WizardryItems.storage_upgrade, new ResourceLocation(Wizardry.MODID, "blocks/scrolls_wooden"));
registerBookModelTexture(() -> WizardryItems.siphon_upgrade, new ResourceLocation(Wizardry.MODID, "blocks/scrolls_wooden"));
registerBookModelTexture(() -> WizardryItems.condenser_upgrade, new ResourceLocation(Wizardry.MODID, "blocks/scrolls_wooden"));
registerBookModelTexture(() -> WizardryItems.range_upgrade, new ResourceLocation(Wizardry.MODID, "blocks/scrolls_wooden"));
registerBookModelTexture(() -> WizardryItems.duration_upgrade, new ResourceLocation(Wizardry.MODID, "blocks/scrolls_wooden"));
registerBookModelTexture(() -> WizardryItems.cooldown_upgrade, new ResourceLocation(Wizardry.MODID, "blocks/scrolls_wooden"));
registerBookModelTexture(() -> WizardryItems.blast_upgrade, new ResourceLocation(Wizardry.MODID, "blocks/scrolls_wooden"));
registerBookModelTexture(() -> WizardryItems.attunement_upgrade, new ResourceLocation(Wizardry.MODID, "blocks/scrolls_wooden"));
registerBookModelTexture(() -> WizardryItems.melee_upgrade, new ResourceLocation(Wizardry.MODID, "blocks/scrolls_wooden"));
}
/**
* Registers a book texture to be used for the book model when the given item is placed in a bookshelf. This method
* <b>must</b> be called from {@code preInit} as the registered item count is required during block registration.
* This also necessitates the use of a supplier to fetch the item, since the items will not yet be registered.
* @param itemFactory A {@link Supplier} that returns the book item to link the texture to. This item need not
* be an instance of {@link electroblob.wizardry.item.ItemSpellBook ItemSpellBook}. Duplicate
* items will result in an {@link IllegalArgumentException} being thrown later.
* @param texture The texture to apply to the book model.
*/
public static void registerBookModelTexture(Supplier<Item> itemFactory, ResourceLocation texture){
BOOK_TEXTURE_MAP.put(itemFactory, texture);
}
/**
* Returns a list of nearby bookshelves' inventories, where 'bookshelves' are any tile entities with inventories
* whose blocks are specified in the config file under the {@code bookshelfBlocks} option.
* @param world The world to search in
* @param centre The position to search around
* @param exclude Any tile entities that should be excluded from the returned list
* @return A list of nearby {@link IInventory} objects that count as valid bookshelves
*/
public static List<IInventory> findNearbyBookshelves(World world, BlockPos centre, TileEntity... exclude){
List<IInventory> bookshelves = new ArrayList<>();
int searchRadius = Wizardry.settings.bookshelfSearchRadius;
for(int x = -searchRadius; x <= searchRadius; x++){
for(int y = -searchRadius; y <= searchRadius; y++){
for(int z = -searchRadius; z <= searchRadius; z++){
BlockPos pos = centre.add(x, y, z);
if(Settings.containsMetaBlock(Wizardry.settings.bookshelfBlocks, world.getBlockState(pos))){
TileEntity te = world.getTileEntity(pos);
if(te instanceof IInventory && !ArrayUtils.contains(exclude, te)) bookshelves.add((IInventory)te);
}
}
}
}
return bookshelves;
}
@SubscribeEvent
public static void onWorldLoadEvent(WorldEvent.Load event){
event.getWorld().addEventListener(Listener.instance);
}
@SubscribeEvent
public static void onWorldUnloadEvent(WorldEvent.Unload event){
event.getWorld().removeEventListener(Listener.instance);
}
public static class Listener implements IWorldEventListener {
public static final Listener instance = new Listener();
private Listener(){}
@Override
public void notifyBlockUpdate(World world, BlockPos pos, IBlockState oldState, IBlockState newState, int flags){
if(oldState == newState) return; // Probably won't happen but just in case
if(Settings.containsMetaBlock(Wizardry.settings.bookshelfBlocks, oldState) // Bookshelf removed
|| Settings.containsMetaBlock(Wizardry.settings.bookshelfBlocks, newState)){ // Bookshelf placed
// It is also possible (with commands) for a bookshelf to be replaced with another bookshelf, in which
// case this should still just be called once
Wizardry.proxy.notifyBookshelfChange(world, pos);
}
}
// Dummy implementations
@Override public void notifyLightSet(BlockPos pos){}
@Override public void markBlockRangeForRenderUpdate(int x1, int y1, int z1, int x2, int y2, int z2){}
@Override public void playSoundToAllNearExcept(@Nullable EntityPlayer player, SoundEvent soundIn, SoundCategory category, double x, double y, double z, float volume, float pitch){}
@Override public void playRecord(SoundEvent soundIn, BlockPos pos){}
@Override public void spawnParticle(int particleID, boolean ignoreRange, double xCoord, double yCoord, double zCoord, double xSpeed, double ySpeed, double zSpeed, int... parameters){}
@Override public void spawnParticle(int id, boolean ignoreRange, boolean minimiseParticleLevel, double x, double y, double z, double xSpeed, double ySpeed, double zSpeed, int... parameters){}
@Override public void onEntityAdded(Entity entityIn){}
@Override public void onEntityRemoved(Entity entityIn){}
@Override public void broadcastSound(int soundID, BlockPos pos, int data){}
@Override public void playEvent(EntityPlayer player, int type, BlockPos blockPosIn, int data){}
@Override public void sendBlockBreakProgress(int breakerId, BlockPos pos, int progress){}
}
}
@@ -1,76 +1,16 @@
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.Random;
import java.util.EnumMap;
import electroblob.wizardry.Wizardry;
import net.minecraft.block.Block;
import net.minecraft.block.material.Material;
import net.minecraft.item.Item;
public class BlockCrystal extends Block {
public 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){
public BlockCrystal(Material material) {
super(material);
this.setDefaultState(this.blockState.getBaseState().withProperty(ELEMENT, Element.MAGIC));
this.setCreativeTab(WizardryTabs.WIZARDRY);
this.setHarvestLevel("pickaxe", 2);
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,73 +1,143 @@
package electroblob.wizardry.block;
import electroblob.wizardry.Wizardry;
import electroblob.wizardry.registry.WizardryBlocks;
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;
import net.minecraft.block.state.IBlockState;
import net.minecraft.init.Blocks;
import net.minecraft.util.math.AxisAlignedBB;
import net.minecraft.util.math.BlockPos;
import net.minecraft.world.IBlockAccess;
import net.minecraft.world.World;
import net.minecraftforge.common.EnumPlantType;
import net.minecraftforge.event.entity.player.BonemealEvent;
import net.minecraftforge.fml.common.Mod;
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
import java.util.Random;
// Extending BlockBush allows me to remove nearly everything from this class.
@Mod.EventBusSubscriber
public class BlockCrystalFlower extends BlockBush {
import electroblob.wizardry.EnumParticleType;
import electroblob.wizardry.Wizardry;
import net.minecraft.block.Block;
import net.minecraft.block.material.Material;
import net.minecraft.init.Blocks;
import net.minecraft.util.AxisAlignedBB;
import net.minecraft.world.IBlockAccess;
import net.minecraft.world.World;
import net.minecraftforge.common.EnumPlantType;
import net.minecraftforge.common.IPlantable;
import net.minecraftforge.common.util.ForgeDirection;
private static final AxisAlignedBB AABB = new AxisAlignedBB(0.5F - 0.2f, 0.0F, 0.5F - 0.2f, 0.5F + 0.2f,
0.2f * 3.0F, 0.5F + 0.2f);
public class BlockCrystalFlower extends Block implements IPlantable {
public BlockCrystalFlower(Material par2Material){
public BlockCrystalFlower(Material par2Material) {
super(par2Material);
this.setLightLevel(0.5f);
this.setTickRandomly(true);
this.setSoundType(SoundType.PLANT);
this.setBlockBounds(0.5F - 0.2f, 0.0F, 0.5F - 0.2f, 0.5F + 0.2f, 0.2f * 3.0F, 0.5F + 0.2f);
this.setTickRandomly(true);
}
@Override
public AxisAlignedBB getBoundingBox(IBlockState state, IBlockAccess source, BlockPos pos){
return AABB;
}
@Override
public void randomDisplayTick(IBlockState state, World world, BlockPos pos, Random random){
public void randomDisplayTick(World world, int x, int y, int z, Random random){
if(world.isRemote && random.nextBoolean()){
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);
Wizardry.proxy.spawnParticle(EnumParticleType.SPARKLE, world, x+random.nextDouble(), y+random.nextDouble()/2+0.5, z+random.nextDouble(), 0d, 0.01, 0d, 20 + random.nextInt(10), 0.5f + (random.nextFloat()/2), 0.5f + (random.nextFloat()/2), 0.5f + (random.nextFloat()/2));
}
}
/**
* Checks to see if its valid to put this block at the specified coordinates. Args: world, x, y, z
*/
@Override
public boolean canPlaceBlockAt(World par1World, int par2, int par3, int par4)
{
return super.canPlaceBlockAt(par1World, par2, par3, par4) && canBlockStay(par1World, par2, par3, par4);
}
/**
* Returns a bounding box from the pool of bounding boxes (this means this box can change after the pool has been
* cleared to be reused)
*/
@Override
public AxisAlignedBB getCollisionBoundingBoxFromPool(World par1World, int par2, int par3, int par4)
{
return null;
}
/**
* Gets passed in the blockID of the block below and supposed to return true if its allowed to grow on the type of
* blockID passed in. Args: blockID
*/
protected boolean canThisPlantGrowOnThisBlockID(Block par1)
{
return par1 == Blocks.grass || par1 == Blocks.dirt || par1 == Blocks.farmland;
}
/**
* Lets the block know when one of its neighbor changes. Doesn't know which neighbor changed (coordinates passed are
* their own) Args: x, y, z, neighbor blockID
*/
@Override
public void onNeighborBlockChange(World par1World, int par2, int par3, int par4, Block par5)
{
super.onNeighborBlockChange(par1World, par2, par3, par4, par5);
this.checkFlowerChange(par1World, par2, par3, par4);
}
/**
* Ticks the block if it's been scheduled
*/
@Override
public void updateTick(World par1World, int par2, int par3, int par4, Random par5Random)
{
this.checkFlowerChange(par1World, par2, par3, par4);
}
protected final void checkFlowerChange(World par1World, int par2, int par3, int par4)
{
if (!this.canBlockStay(par1World, par2, par3, par4))
{
this.dropBlockAsItem(par1World, par2, par3, par4, par1World.getBlockMetadata(par2, par3, par4), 0);
par1World.setBlock(par2, par3, par4, Blocks.air, 0, 2);
}
}
/**
* Can this block stay at this position. Similar to canPlaceBlockAt except gets checked often with plants.
*/
@Override
public boolean canBlockStay(World par1World, int par2, int par3, int par4)
{
Block soil = par1World.getBlock(par2, par3 - 1, par4);
return (par1World.getFullBlockLightValue(par2, par3, par4) >= 8 || par1World.canBlockSeeTheSky(par2, par3, par4)) &&
(soil != null && soil.canSustainPlant(par1World, par2, par3 - 1, par4, ForgeDirection.UP, this));
}
/**
* Is this block (a) opaque and (b) a full 1m cube? This determines whether or not to render the shared face of two
* adjacent blocks and also whether the player can attach torches, redstone wire, etc to this block.
*/
@Override
public boolean isOpaqueCube()
{
return false;
}
/**
* If this block doesn't render as an ordinary block it will return False (examples: signs, buttons, stairs, etc)
*/
@Override
public boolean renderAsNormalBlock()
{
return false;
}
/**
* The type of render function that is called for this block
*/
@Override
public int getRenderType()
{
return 1;
}
@Override
public EnumPlantType getPlantType(IBlockAccess world, BlockPos pos){
public EnumPlantType getPlantType(IBlockAccess world, int x, int y, int z) {
return EnumPlantType.Plains;
}
@SubscribeEvent
public static void onBonemealEvent(BonemealEvent event){
// Grows crystal flowers when bonemeal is used on grass
if(Wizardry.settings.bonemealGrowsCrystalFlowers && event.getBlock().getBlock() == Blocks.GRASS){
@Override
public Block getPlant(IBlockAccess world, int x, int y, int z) {
return this;
}
BlockPos pos = event.getPos().add(event.getWorld().rand.nextInt(8) - event.getWorld().rand.nextInt(8),
event.getWorld().rand.nextInt(4) - event.getWorld().rand.nextInt(4),
event.getWorld().rand.nextInt(8) - event.getWorld().rand.nextInt(8));
if(event.getWorld().isAirBlock(new BlockPos(pos))
&& (!event.getWorld().provider.isNether() || pos.getY() < 127)
&& WizardryBlocks.crystal_flower.canPlaceBlockAt(event.getWorld(), pos)){
event.getWorld().setBlockState(pos, WizardryBlocks.crystal_flower.getDefaultState(), 2);
}
}
@Override
public int getPlantMetadata(IBlockAccess world, int x, int y, int z) {
return 0;
}
}
@@ -1,148 +0,0 @@
package electroblob.wizardry.block;
import electroblob.wizardry.registry.WizardryBlocks;
import electroblob.wizardry.util.ParticleBuilder;
import electroblob.wizardry.util.ParticleBuilder.Type;
import net.minecraft.block.Block;
import net.minecraft.block.material.Material;
import net.minecraft.block.state.BlockFaceShape;
import net.minecraft.block.state.IBlockState;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.init.Blocks;
import net.minecraft.init.Items;
import net.minecraft.item.Item;
import net.minecraft.item.ItemStack;
import net.minecraft.tileentity.TileEntity;
import net.minecraft.tileentity.TileEntityFlowerPot;
import net.minecraft.util.BlockRenderLayer;
import net.minecraft.util.EnumFacing;
import net.minecraft.util.EnumHand;
import net.minecraft.util.math.AxisAlignedBB;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.RayTraceResult;
import net.minecraft.world.IBlockAccess;
import net.minecraft.world.World;
import javax.annotation.Nullable;
import java.util.Random;
public class BlockCrystalFlowerPot extends Block {
protected static final AxisAlignedBB BOUNDING_BOX = new AxisAlignedBB(0.3125D, 0.0D, 0.3125D, 0.6875D, 0.375D, 0.6875D);
public BlockCrystalFlowerPot(){
super(Material.CIRCUITS);
this.setLightLevel(0.4f);
this.setTickRandomly(true);
}
@Override
public void randomDisplayTick(IBlockState state, World world, BlockPos pos, Random random){
if(world.isRemote && random.nextBoolean()){
ParticleBuilder.create(Type.SPARKLE)
.pos(pos.getX() + 0.3 + random.nextDouble() * 0.4, pos.getY() + 0.6 + random.nextDouble() * 0.3, pos.getZ() + 0.3 + random.nextDouble() * 0.4)
.vel(0, 0.01, 0)
.time(20 + random.nextInt(10))
.clr(0.5f + (random.nextFloat() / 2), 0.5f + (random.nextFloat() / 2), 0.5f + (random.nextFloat() / 2))
.spawn(world);
}
}
@Nullable
@Override
public TileEntity createTileEntity(World world, IBlockState state){
return new TileEntityFlowerPot(Item.getItemFromBlock(WizardryBlocks.crystal_flower), 0);
}
@Override
public boolean hasTileEntity(IBlockState state){
return true;
}
@Override
public boolean onBlockActivated(World world, BlockPos pos, IBlockState state, EntityPlayer player, EnumHand hand, EnumFacing facing, float hitX, float hitY, float hitZ){
ItemStack stack = new ItemStack(WizardryBlocks.crystal_flower);
if(player.getHeldItem(hand).isEmpty()){
player.setHeldItem(hand, stack);
}else if(!player.addItemStackToInventory(stack)){
player.dropItem(stack, false);
}
world.setBlockState(pos, Blocks.FLOWER_POT.getDefaultState());
return true;
}
@Override
public ItemStack getPickBlock(IBlockState state, RayTraceResult target, World world, BlockPos pos, EntityPlayer player){
return new ItemStack(WizardryBlocks.crystal_flower);
}
@Override
public void getDrops(net.minecraft.util.NonNullList<ItemStack> drops, IBlockAccess world, BlockPos pos, IBlockState state, int fortune){
super.getDrops(drops, world, pos, state, fortune);
drops.add(new ItemStack(WizardryBlocks.crystal_flower));
}
@Override
public Item getItemDropped(IBlockState state, Random rand, int fortune){
return Items.FLOWER_POT;
}
// The rest are identical behaviour to BlockFlowerPot
@Override
public AxisAlignedBB getBoundingBox(IBlockState state, IBlockAccess source, BlockPos pos){
return BOUNDING_BOX;
}
@Override
public boolean isOpaqueCube(IBlockState state){
return false;
}
@Override
public boolean isFullCube(IBlockState state){
return false;
}
@Override
public boolean canPlaceBlockAt(World world, BlockPos pos){
IBlockState downState = world.getBlockState(pos.down());
return super.canPlaceBlockAt(world, pos) && (downState.isTopSolid() || downState.getBlockFaceShape(world, pos.down(), EnumFacing.UP) == BlockFaceShape.SOLID);
}
@Override
public void neighborChanged(IBlockState state, World world, BlockPos pos, Block block, BlockPos neighbour){
IBlockState downState = world.getBlockState(pos.down());
if(!downState.isTopSolid() && downState.getBlockFaceShape(world, pos.down(), EnumFacing.UP) != BlockFaceShape.SOLID){
this.dropBlockAsItem(world, pos, state, 0);
world.setBlockToAir(pos);
}
}
@Override
public BlockRenderLayer getRenderLayer(){
return BlockRenderLayer.CUTOUT;
}
@Override
public BlockFaceShape getBlockFaceShape(IBlockAccess worldIn, IBlockState state, BlockPos pos, EnumFacing face){
return BlockFaceShape.UNDEFINED;
}
@Override
public boolean removedByPlayer(IBlockState state, World world, BlockPos pos, EntityPlayer player, boolean willHarvest){
if(willHarvest) return true; // If it will harvest, delay deletion of the block until after getDrops
return super.removedByPlayer(state, world, pos, player, willHarvest);
}
@Override
public void harvestBlock(World world, EntityPlayer player, BlockPos pos, IBlockState state, @Nullable TileEntity te, ItemStack tool){
super.harvestBlock(world, player, pos, state, te, tool);
world.setBlockToAir(pos);
}
}
@@ -1,57 +1,34 @@
package electroblob.wizardry.block;
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;
import electroblob.wizardry.Wizardry;
import net.minecraft.block.Block;
import net.minecraft.block.material.Material;
import net.minecraft.item.Item;
public class BlockCrystalOre extends Block {
public BlockCrystalOre(Material material){
public BlockCrystalOre(Material material) {
super(material);
this.setSoundType(SoundType.STONE);
setStepSound(Block.soundTypeStone);
setBlockName("crystalOre");
setResistance(5.0F);
setHarvestLevel("pickaxe", 2);
}
@Override
public int quantityDropped(IBlockState state, int fortune, Random random){
// This now works the same way as vanilla ores
if(fortune > 0){
int i = random.nextInt(fortune + 2) - 1;
if(i < 0) i = 0;
return (random.nextInt(3) + 1) * (i + 1);
}else{
return random.nextInt(3) + 1;
}
}
@Override
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;
public int quantityDropped(int meta, int fortune, Random random)
{
if(fortune > 0){
return random.nextInt(2) + 1 + random.nextInt(fortune);
}else{
return random.nextInt(2) + 1;
}
}
@Override
public Item getItemDropped(int Metadata, Random random, int fortune){
return Wizardry.magicCrystal;
}
}
@@ -1,41 +0,0 @@
package electroblob.wizardry.block;
import net.minecraft.block.BlockFrostedIce;
import net.minecraft.block.SoundType;
import net.minecraft.block.material.Material;
import net.minecraft.block.state.IBlockState;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.MathHelper;
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 {
public BlockDryFrostedIce(){
super();
setDefaultSlipperiness(0.98f); // For some strange reason BlockFrostedIce overrides BlockIce's icy-ness...
setSoundType(SoundType.GLASS);
}
@Override
public Material getMaterial(IBlockState state){
return Material.ICE; // For goodness sake
}
@Override
protected void turnIntoWater(World world, BlockPos pos){
world.destroyBlock(pos, false);
}
@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,17 +0,0 @@
package electroblob.wizardry.block;
import electroblob.wizardry.registry.WizardryTabs;
import net.minecraft.block.BlockPlanks;
import net.minecraft.block.SoundType;
public class BlockGildedWood extends BlockPlanks {
public BlockGildedWood(){
super();
this.setCreativeTab(WizardryTabs.WIZARDRY);
this.setHardness(2.0F);
this.setResistance(5.0F);
this.setSoundType(SoundType.WOOD); // Why is this protected?!
}
}
@@ -1,179 +0,0 @@
package electroblob.wizardry.block;
import electroblob.wizardry.registry.WizardryBlocks;
import electroblob.wizardry.registry.WizardryTabs;
import electroblob.wizardry.tileentity.TileEntityImbuementAltar;
import net.minecraft.block.Block;
import net.minecraft.block.ITileEntityProvider;
import net.minecraft.block.material.Material;
import net.minecraft.block.properties.PropertyBool;
import net.minecraft.block.state.BlockFaceShape;
import net.minecraft.block.state.BlockStateContainer;
import net.minecraft.block.state.IBlockState;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.inventory.InventoryHelper;
import net.minecraft.item.ItemStack;
import net.minecraft.tileentity.TileEntity;
import net.minecraft.util.BlockRenderLayer;
import net.minecraft.util.EnumBlockRenderType;
import net.minecraft.util.EnumFacing;
import net.minecraft.util.EnumHand;
import net.minecraft.util.math.AxisAlignedBB;
import net.minecraft.util.math.BlockPos;
import net.minecraft.world.IBlockAccess;
import net.minecraft.world.World;
import java.util.Arrays;
public class BlockImbuementAltar extends Block implements ITileEntityProvider {
public static final PropertyBool ACTIVE = PropertyBool.create("active");
private static final AxisAlignedBB AABB = new AxisAlignedBB(0.0, 0.0, 0.0, 1.0, 0.75, 1.0);
public BlockImbuementAltar(){
super(Material.ROCK);
this.setBlockUnbreakable();
this.setResistance(6000000);
this.setLightLevel(0.4f);
this.setCreativeTab(WizardryTabs.WIZARDRY);
this.setDefaultState(this.blockState.getBaseState().withProperty(ACTIVE, false));
}
@Override
protected BlockStateContainer createBlockState(){
return new BlockStateContainer(this, ACTIVE);
}
@Override
public IBlockState getStateFromMeta(int meta){
return this.getDefaultState().withProperty(ACTIVE, meta == 1);
}
@Override
public int getMetaFromState(IBlockState state){
return state.getValue(ACTIVE) ? 1 : 0;
}
@Override
public AxisAlignedBB getBoundingBox(IBlockState state, IBlockAccess source, BlockPos pos){
return AABB;
}
@Override
public TileEntity createNewTileEntity(World world, int metadata){
return new TileEntityImbuementAltar();
}
@Override
public boolean isNormalCube(IBlockState state, IBlockAccess world, BlockPos pos){
return false;
}
@Override
public EnumBlockRenderType getRenderType(IBlockState state){
return EnumBlockRenderType.MODEL;
}
@Override
public BlockRenderLayer getRenderLayer(){
return BlockRenderLayer.CUTOUT; // Required to shade parts of the block faces differently to others
}
@Override
public boolean isOpaqueCube(IBlockState state){
return false;
}
@Override
public boolean isFullCube(IBlockState state){
return false;
}
@Override
public BlockFaceShape getBlockFaceShape(IBlockAccess world, IBlockState state, BlockPos pos, EnumFacing face){
return face == EnumFacing.DOWN ? BlockFaceShape.SOLID : BlockFaceShape.UNDEFINED;
}
@Override
public int getLightValue(IBlockState state, IBlockAccess world, BlockPos pos){
return state.getValue(ACTIVE) ? super.getLightValue(state, world, pos) : 0;
}
@Override
public void neighborChanged(IBlockState state, World world, BlockPos pos, Block block, BlockPos neighbour){
boolean shouldBeActive = Arrays.stream(EnumFacing.HORIZONTALS)
.allMatch(s -> world.getBlockState(pos.offset(s)).getBlock() == WizardryBlocks.receptacle
&& world.getBlockState(pos.offset(s)).getValue(BlockReceptacle.FACING) == s);
if(world.getBlockState(pos).getValue(ACTIVE) != shouldBeActive){ // Only set when it actually needs changing
// Get contents of altar before replacing it
TileEntity te = world.getTileEntity(pos);
ItemStack stack = ItemStack.EMPTY;
if(te instanceof TileEntityImbuementAltar) stack = ((TileEntityImbuementAltar)te).getStack();
world.setBlockState(pos, world.getBlockState(pos).withProperty(ACTIVE, shouldBeActive));
// Copy over contents of altar from before to new tile entity
te = world.getTileEntity(pos);
if(te instanceof TileEntityImbuementAltar) ((TileEntityImbuementAltar)te).setStack(stack);
world.checkLight(pos);
}
TileEntity tileEntity = world.getTileEntity(pos);
if(tileEntity instanceof TileEntityImbuementAltar){
((TileEntityImbuementAltar)tileEntity).checkRecipe();
}
}
@Override
public boolean onBlockActivated(World world, BlockPos pos, IBlockState block, EntityPlayer player, EnumHand hand,
EnumFacing side, float hitX, float hitY, float hitZ){
TileEntity tileEntity = world.getTileEntity(pos);
if(!(tileEntity instanceof TileEntityImbuementAltar) || player.isSneaking()){
return false;
}
ItemStack currentStack = ((TileEntityImbuementAltar)tileEntity).getStack();
ItemStack toInsert = player.getHeldItem(hand);
if(currentStack.isEmpty()){
ItemStack stack = toInsert.copy();
stack.setCount(1);
((TileEntityImbuementAltar)tileEntity).setStack(stack);
((TileEntityImbuementAltar)tileEntity).setLastUser(player);
if(!player.isCreative()) toInsert.shrink(1);
}else{
if(toInsert.isEmpty()){
player.setHeldItem(hand, currentStack);
}else if(!player.addItemStackToInventory(currentStack)){
player.dropItem(currentStack, false);
}
((TileEntityImbuementAltar)tileEntity).setStack(ItemStack.EMPTY);
((TileEntityImbuementAltar)tileEntity).setLastUser(null);
}
return true;
}
@Override
public void breakBlock(World world, BlockPos pos, IBlockState block){
TileEntity tileentity = world.getTileEntity(pos);
if(tileentity instanceof TileEntityImbuementAltar){
InventoryHelper.spawnItemStack(world, pos.getX(), pos.getY(), pos.getZ(), ((TileEntityImbuementAltar)tileentity).getStack());
}
super.breakBlock(world, pos, block);
}
}
@@ -1,138 +0,0 @@
package electroblob.wizardry.block;
import electroblob.wizardry.Wizardry;
import electroblob.wizardry.WizardryGuiHandler;
import electroblob.wizardry.registry.WizardryTabs;
import electroblob.wizardry.tileentity.TileEntityLectern;
import electroblob.wizardry.util.ParticleBuilder;
import electroblob.wizardry.util.ParticleBuilder.Type;
import net.minecraft.block.BlockHorizontal;
import net.minecraft.block.ITileEntityProvider;
import net.minecraft.block.SoundType;
import net.minecraft.block.material.Material;
import net.minecraft.block.state.BlockFaceShape;
import net.minecraft.block.state.BlockStateContainer;
import net.minecraft.block.state.IBlockState;
import net.minecraft.entity.EntityLivingBase;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.tileentity.TileEntity;
import net.minecraft.util.EnumFacing;
import net.minecraft.util.EnumHand;
import net.minecraft.util.Mirror;
import net.minecraft.util.Rotation;
import net.minecraft.util.math.BlockPos;
import net.minecraft.world.IBlockAccess;
import net.minecraft.world.World;
import net.minecraftforge.fml.relauncher.Side;
import net.minecraftforge.fml.relauncher.SideOnly;
import javax.annotation.Nullable;
import java.util.Random;
public class BlockLectern extends BlockHorizontal implements ITileEntityProvider {
public BlockLectern(){
super(Material.WOOD);
this.setDefaultState(this.blockState.getBaseState().withProperty(FACING, EnumFacing.NORTH));
this.setCreativeTab(WizardryTabs.WIZARDRY);
this.setHardness(2.0F);
this.setResistance(5.0F);
this.setSoundType(SoundType.WOOD);
}
@Override
protected BlockStateContainer createBlockState(){
return new BlockStateContainer.Builder(this).add(FACING).build();
}
@Override
public IBlockState getStateFromMeta(int meta){
EnumFacing enumfacing = EnumFacing.byIndex(meta);
if(enumfacing.getAxis() == EnumFacing.Axis.Y) enumfacing = EnumFacing.NORTH;
return this.getDefaultState().withProperty(FACING, enumfacing);
}
@Override
public int getMetaFromState(IBlockState state){
return state.getValue(FACING).getIndex();
}
@Override
@SideOnly(Side.CLIENT)
public void randomDisplayTick(IBlockState state, World world, BlockPos pos, Random rand){
EntityPlayer entityplayer = world.getClosestPlayer(pos.getX() + 0.5, pos.getY() + 0.5,
pos.getZ() + 0.5, TileEntityLectern.BOOK_OPEN_DISTANCE, false);
if(entityplayer != null){
ParticleBuilder.create(Type.DUST).pos(pos.getX() + rand.nextFloat(), pos.getY() + 1, pos.getZ() + rand.nextFloat())
.vel(0, 0.03, 0).clr(1, 1, 0.65f).fade(0.7f, 0, 1).shaded(false).spawn(world);
}
}
@Override
public boolean isOpaqueCube(IBlockState state){
return false;
}
@Override
public boolean isFullCube(IBlockState state){
return false;
}
@Override
public boolean isFullBlock(IBlockState state){
return false;
}
@Override
public boolean isNormalCube(IBlockState state){
return false;
}
@Override
public boolean canPlaceTorchOnTop(IBlockState state, IBlockAccess world, BlockPos pos){
return false;
}
@Override
public BlockFaceShape getBlockFaceShape(IBlockAccess world, IBlockState state, BlockPos pos, EnumFacing face){
return face == EnumFacing.DOWN ? BlockFaceShape.SOLID : BlockFaceShape.UNDEFINED;
}
@Override
public IBlockState getStateForPlacement(World world, BlockPos pos, EnumFacing facing, float hitX, float hitY, float hitZ, int meta, EntityLivingBase placer){
return this.getDefaultState().withProperty(FACING, placer.getHorizontalFacing().getOpposite());
}
@Override
public IBlockState withRotation(IBlockState state, Rotation rotation){
return state.withProperty(FACING, rotation.rotate(state.getValue(FACING)));
}
@Override
public IBlockState withMirror(IBlockState state, Mirror mirror){
return state.withRotation(mirror.toRotation(state.getValue(FACING)));
}
@Nullable
@Override
public TileEntity createNewTileEntity(World world, int meta){
return new TileEntityLectern();
}
@Override
public boolean onBlockActivated(World world, BlockPos pos, IBlockState block, EntityPlayer player, EnumHand hand,
EnumFacing side, float hitX, float hitY, float hitZ){
TileEntity tileEntity = world.getTileEntity(pos);
if(tileEntity == null || player.isSneaking()){
return false;
}
player.openGui(Wizardry.instance, WizardryGuiHandler.LECTERN, world, pos.getX(), pos.getY(), pos.getZ());
return true;
}
}
@@ -1,92 +1,40 @@
package electroblob.wizardry.block;
import electroblob.wizardry.item.ISpellCastingItem;
import electroblob.wizardry.item.ItemArtefact;
import electroblob.wizardry.registry.WizardryItems;
import java.util.Random;
import electroblob.wizardry.tileentity.TileEntityMagicLight;
import net.minecraft.block.Block;
import net.minecraft.block.ITileEntityProvider;
import net.minecraft.block.BlockContainer;
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.util.AxisAlignedBB;
import net.minecraft.world.World;
public class BlockMagicLight extends Block implements ITileEntityProvider {
public class BlockMagicLight extends BlockContainer {
//private static final AxisAlignedBB AABB = new AxisAlignedBB(0, 0, 0, 0, 0, 0);
public BlockMagicLight(Material material){
super(material);
public BlockMagicLight(Material par2Material) {
super(par2Material);
this.setLightLevel(1.0f);
this.setBlockUnbreakable();
this.setBlockBounds(0, 0, 0, 0, 0, 0);
}
public AxisAlignedBB getCollisionBoundingBoxFromPool(World par1World, int par2, int par3, int par4)
{
return null;
}
@Override
public AxisAlignedBB getCollisionBoundingBox(IBlockState blockState, IBlockAccess worldIn, BlockPos pos){
// The other two bounding box methods in Block aren't nullable, so this is the
// only one that can return NULL_AABB.
return NULL_AABB;
}
@Override
public 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(){
// 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
public TileEntity createNewTileEntity(World world, int metadata){
public TileEntity createNewTileEntity(World world, int metadata) {
return new TileEntityMagicLight(600);
}
@Override
public boolean isOpaqueCube(IBlockState state){
public boolean renderAsNormalBlock()
{
return false;
}
@Override
public boolean isOpaqueCube(){
return false;
}
@Override
public boolean isNormalCube(IBlockState state, IBlockAccess world, BlockPos pos){
return false;
}
@Override
public EnumBlockRenderType getRenderType(IBlockState state){
return EnumBlockRenderType.ENTITYBLOCK_ANIMATED;
}
}
@@ -1,116 +0,0 @@
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;
}
}
@@ -1,140 +0,0 @@
package electroblob.wizardry.block;
import electroblob.wizardry.Wizardry;
import electroblob.wizardry.constants.Element;
import electroblob.wizardry.registry.WizardryTabs;
import electroblob.wizardry.tileentity.TileEntityShrineCore;
import net.minecraft.block.Block;
import net.minecraft.block.ITileEntityProvider;
import net.minecraft.block.material.MapColor;
import net.minecraft.block.material.Material;
import net.minecraft.block.properties.PropertyBool;
import net.minecraft.block.properties.PropertyEnum;
import net.minecraft.block.state.BlockStateContainer;
import net.minecraft.block.state.IBlockState;
import net.minecraft.creativetab.CreativeTabs;
import net.minecraft.entity.Entity;
import net.minecraft.item.ItemStack;
import net.minecraft.tileentity.TileEntity;
import net.minecraft.util.BlockRenderLayer;
import net.minecraft.util.NonNullList;
import net.minecraft.util.math.BlockPos;
import net.minecraft.world.Explosion;
import net.minecraft.world.IBlockAccess;
import net.minecraft.world.World;
import javax.annotation.Nullable;
import java.util.Arrays;
import java.util.EnumMap;
public class BlockPedestal extends Block implements ITileEntityProvider {
public static final PropertyEnum<Element> ELEMENT = PropertyEnum.create("element", Element.class,
Arrays.copyOfRange(Element.values(), 1, Element.values().length)); // Everything except MAGIC
// A 'natural' pedestal is one that was generated as part of a structure, is unbreakable and has a tileentity
public static final PropertyBool NATURAL = PropertyBool.create("natural");
private static final EnumMap<Element, MapColor> map_colours = new EnumMap<>(Element.class);
static {
map_colours.put(Element.FIRE, MapColor.RED_STAINED_HARDENED_CLAY);
map_colours.put(Element.ICE, MapColor.LIGHT_BLUE_STAINED_HARDENED_CLAY);
map_colours.put(Element.LIGHTNING, MapColor.CYAN_STAINED_HARDENED_CLAY);
map_colours.put(Element.NECROMANCY, MapColor.PURPLE_STAINED_HARDENED_CLAY);
map_colours.put(Element.EARTH, MapColor.BROWN_STAINED_HARDENED_CLAY);
map_colours.put(Element.SORCERY, MapColor.GRAY);
map_colours.put(Element.HEALING, MapColor.YELLOW_STAINED_HARDENED_CLAY);
}
public BlockPedestal(Material material){
super(material);
this.setDefaultState(this.blockState.getBaseState().withProperty(ELEMENT, Element.FIRE).withProperty(NATURAL, false));
this.setCreativeTab(WizardryTabs.WIZARDRY);
this.setHardness(1.5F);
this.setResistance(10.0F);
}
@Override
public int damageDropped(IBlockState state){
return state.getValue(ELEMENT).ordinal(); // Ignore the NATURAL state here, it's unobtainable
}
@Override
public MapColor getMapColor(IBlockState state, IBlockAccess world, BlockPos pos){
return map_colours.get(state.getProperties().get(ELEMENT));
}
@Override
public void getSubBlocks(CreativeTabs tab, NonNullList<ItemStack> items){
// Ignore the NATURAL state here, it's unobtainable
if(this.getCreativeTab() == tab){
for(Element element : Arrays.copyOfRange(Element.values(), 1, Element.values().length)){
items.add(new ItemStack(this, 1, element.ordinal()));
}
}
}
@Override
public BlockRenderLayer getRenderLayer(){
return BlockRenderLayer.CUTOUT; // Required to shade parts of the block faces differently to others
}
@Override
public float getBlockHardness(IBlockState state, World world, BlockPos pos){
// If shrine regeneration is enabled, make pedestal blocks with shrine cores unbreakable to prevent exploitation
if(!world.isRemote && Wizardry.settings != null && Wizardry.settings.shrineRegenerationEnabled){
TileEntity tileEntity = world.getTileEntity(pos);
if(tileEntity instanceof TileEntityShrineCore){
return -1; // Unbreakable if it has a shrine core
}
}
return state.getValue(NATURAL) ? -1 : super.getBlockHardness(state, world, pos);
}
@Override
public float getExplosionResistance(World world, BlockPos pos, @Nullable Entity exploder, Explosion explosion){
// If shrine regeneration is enabled, make pedestal blocks with shrine cores unbreakable to prevent exploitation
if(!world.isRemote && Wizardry.settings != null && Wizardry.settings.shrineRegenerationEnabled){
TileEntity tileEntity = world.getTileEntity(pos);
if(tileEntity instanceof TileEntityShrineCore){
return 6000000.0F; // Unbreakable if it has a shrine core
}
}
return world.getBlockState(pos).getValue(NATURAL) ? 6000000.0F : super.getExplosionResistance(world, pos, exploder, explosion);
}
@Override
public boolean hasTileEntity(IBlockState state){
return state.getValue(NATURAL); // Only naturally-generated pedestals have a (shrine core) tile entity
}
@Nullable
@Override
public TileEntity createNewTileEntity(World world, int meta){
return new TileEntityShrineCore();
}
@Override
public IBlockState getStateFromMeta(int metadata){
boolean natural = false;
if(metadata > ELEMENT.getAllowedValues().size()){
natural = true;
metadata -= ELEMENT.getAllowedValues().size();
}
Element element = Element.values()[metadata];
if(!ELEMENT.getAllowedValues().contains(element)) return this.getDefaultState().withProperty(NATURAL, natural);
return this.getDefaultState().withProperty(ELEMENT, element).withProperty(NATURAL, natural);
}
@Override
public int getMetaFromState(IBlockState state){
return state.getValue(ELEMENT).ordinal() + (state.getValue(NATURAL) ? ELEMENT.getAllowedValues().size() : 0);
}
@Override
protected BlockStateContainer createBlockState(){
return new BlockStateContainer(this, ELEMENT, NATURAL);
}
}
@@ -1,91 +0,0 @@
package electroblob.wizardry.block;
import electroblob.wizardry.registry.Spells;
import electroblob.wizardry.registry.WizardryPotions;
import electroblob.wizardry.spell.Spell;
import electroblob.wizardry.util.EntityUtils;
import net.minecraft.block.state.BlockFaceShape;
import net.minecraft.block.state.IBlockState;
import net.minecraft.entity.Entity;
import net.minecraft.entity.EntityLivingBase;
import net.minecraft.potion.PotionEffect;
import net.minecraft.util.DamageSource;
import net.minecraft.util.EnumFacing;
import net.minecraft.util.math.AxisAlignedBB;
import net.minecraft.util.math.BlockPos;
import net.minecraft.world.IBlockAccess;
import net.minecraft.world.World;
import javax.annotation.Nullable;
public class BlockPermafrost extends BlockDryFrostedIce {
protected static final AxisAlignedBB BOUNDING_BOX = new AxisAlignedBB(0, 0, 0, 1, 0, 1);
protected static final AxisAlignedBB SELECTION_BOUNDING_BOX = new AxisAlignedBB(0, 0, 0, 1, 0.125, 1);
@Override
public AxisAlignedBB getBoundingBox(IBlockState state, IBlockAccess source, BlockPos pos){
return BOUNDING_BOX;
}
@Override
public AxisAlignedBB getSelectedBoundingBox(IBlockState state, World worldIn, BlockPos pos){
return SELECTION_BOUNDING_BOX;
}
@Nullable
@Override
public AxisAlignedBB getCollisionBoundingBox(IBlockState blockState, IBlockAccess worldIn, BlockPos pos){
return super.getCollisionBoundingBox(blockState, worldIn, pos);
}
@Override
public boolean isFullCube(IBlockState state){
return false;
}
@Override
public boolean isNormalCube(IBlockState state){
return false;
}
@Override
public BlockFaceShape getBlockFaceShape(IBlockAccess world, IBlockState state, BlockPos pos, EnumFacing face){
return face == EnumFacing.DOWN ? BlockFaceShape.SOLID : BlockFaceShape.UNDEFINED;
}
@Override
public void onEntityCollision(World world, BlockPos pos, IBlockState state, Entity entity){
if(EntityUtils.isLiving(entity) && entity.ticksExisted % 30 == 0){
// Can't make it player damage unless we make this block a tile entity, but there will be too many for that
entity.attackEntityFrom(DamageSource.MAGIC, Spells.permafrost.getProperty(Spell.DAMAGE).floatValue());
int duration = Spells.permafrost.getProperty(Spell.EFFECT_DURATION).intValue();
int amplifier = Spells.permafrost.getProperty(Spell.EFFECT_STRENGTH).intValue();
((EntityLivingBase)entity).addPotionEffect(new PotionEffect(WizardryPotions.frost, duration, amplifier));
}
// EntityLivingBase's slipperiness code doesn't get the block below it properly so slipperiness only works for
// full blocks...
if(entity.onGround){
// Not brilliant but it's about the best I can do
entity.motionX *= 1.12 - entity.motionX * entity.motionX;
entity.motionZ *= 1.12 - entity.motionZ * entity.motionZ;
// if(entity instanceof EntityLivingBase){
// double maxVel = 0.8;
// double x = entity.motionX;
// double y = entity.motionY;
// double z = entity.motionZ;
// double vel = MathHelper.sqrt(x*x + y*y + z*z);
// double m = vel / maxVel;
// if(m > 1){
// entity.motionX /= m;
// entity.motionZ /= m;
// }
// }
}
}
}
@@ -1,310 +0,0 @@
package electroblob.wizardry.block;
import com.google.common.collect.Maps;
import electroblob.wizardry.constants.Element;
import electroblob.wizardry.item.ItemSpectralDust;
import electroblob.wizardry.registry.*;
import electroblob.wizardry.tileentity.TileEntityReceptacle;
import electroblob.wizardry.util.GeometryUtils;
import electroblob.wizardry.util.ParticleBuilder;
import net.minecraft.block.Block;
import net.minecraft.block.BlockTorch;
import net.minecraft.block.ITileEntityProvider;
import net.minecraft.block.SoundType;
import net.minecraft.block.state.BlockFaceShape;
import net.minecraft.block.state.IBlockState;
import net.minecraft.entity.EntityLivingBase;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.item.ItemStack;
import net.minecraft.tileentity.TileEntity;
import net.minecraft.util.EnumFacing;
import net.minecraft.util.EnumHand;
import net.minecraft.util.NonNullList;
import net.minecraft.util.SoundCategory;
import net.minecraft.util.math.AxisAlignedBB;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.Vec3d;
import net.minecraft.world.IBlockAccess;
import net.minecraft.world.World;
import javax.annotation.Nullable;
import java.util.Arrays;
import java.util.Map;
import java.util.Random;
public class BlockReceptacle extends BlockTorch implements ITileEntityProvider {
protected static final AxisAlignedBB STANDING_AABB = new AxisAlignedBB(4 / 16d, 0 / 16d, 4 / 16d, 12 / 16d, 8 / 16d, 12 / 16d);
protected static final AxisAlignedBB NORTH_WALL_AABB = new AxisAlignedBB(4 / 16d, 2 / 16d, 7 / 16d, 12 / 16d, 10 / 16d, 16 / 16d);
protected static final AxisAlignedBB SOUTH_WALL_AABB = new AxisAlignedBB(4 / 16d, 2 / 16d, 0 / 16d, 12 / 16d, 10 / 16d, 9 / 16d);
protected static final AxisAlignedBB WEST_WALL_AABB = new AxisAlignedBB(7 / 16d, 2 / 16d, 4 / 16d, 16 / 16d, 10 / 16d, 12 / 16d);
protected static final AxisAlignedBB EAST_WALL_AABB = new AxisAlignedBB(0 / 16d, 2 / 16d, 4 / 16d, 9 / 16d, 10 / 16d, 12 / 16d);
private static final double WALL_PARTICLE_OFFSET = 3 / 16d;
public static final Map<Element, int[]> PARTICLE_COLOURS;
static{
Map<Element, int[]> map = Maps.newEnumMap(Element.class);
map.put(Element.MAGIC, new int[]{0xe4c7cd, 0xfeffbe, 0x9d2cf3});
map.put(Element.FIRE, new int[]{0xff9600, 0xfffe67, 0xd02700});
map.put(Element.ICE, new int[]{0xa3e8f4, 0xe9f9fc, 0x138397});
map.put(Element.LIGHTNING, new int[]{0x409ee1, 0xf5f0ff, 0x225474});
map.put(Element.NECROMANCY, new int[]{0xa811ce, 0xf575f5, 0x382366});
map.put(Element.EARTH, new int[]{0xa8f408, 0xc8ffb2, 0x795c28});
map.put(Element.SORCERY, new int[]{0x56e8e3, 0xe8fcfc, 0x16a64d});
map.put(Element.HEALING, new int[]{0xfff69e, 0xfffff6, 0xa18200});
PARTICLE_COLOURS = Maps.immutableEnumMap(map);
}
public BlockReceptacle(){
super();
this.setCreativeTab(WizardryTabs.WIZARDRY);
this.setHardness(0);
this.setLightLevel(0.5f);
this.setSoundType(SoundType.STONE);
}
@Override
public AxisAlignedBB getBoundingBox(IBlockState state, IBlockAccess world, BlockPos pos){
switch(state.getValue(FACING)){
case EAST:
return EAST_WALL_AABB;
case WEST:
return WEST_WALL_AABB;
case SOUTH:
return SOUTH_WALL_AABB;
case NORTH:
return NORTH_WALL_AABB;
default:
return STANDING_AABB;
}
}
@Override
public AxisAlignedBB getCollisionBoundingBox(IBlockState state, IBlockAccess world, BlockPos pos){
return state.getBoundingBox(world, pos);
}
@Override
public int getLightValue(IBlockState state, IBlockAccess world, BlockPos pos){
TileEntity tileEntity = world.getTileEntity(pos);
if(tileEntity instanceof TileEntityReceptacle && ((TileEntityReceptacle)tileEntity).getElement() != null){
return super.getLightValue(state, world, pos); // Return super to use float value from constructor
}
return 0;
}
@Override
public void getDrops(NonNullList<ItemStack> drops, IBlockAccess world, BlockPos pos, IBlockState state, int fortune){
super.getDrops(drops, world, pos, state, fortune);
TileEntity tileEntity = world.getTileEntity(pos);
if(tileEntity instanceof TileEntityReceptacle){
Element element = ((TileEntityReceptacle)tileEntity).getElement();
if(element != null) drops.add(new ItemStack(WizardryItems.spectral_dust, 1, element.ordinal()));
}
}
@Override
public boolean canPlaceBlockOnSide(World world, BlockPos pos, EnumFacing side){
if(side != EnumFacing.UP && side != EnumFacing.DOWN
&& world.getBlockState(pos.offset(side.getOpposite())).getBlock() instanceof BlockImbuementAltar){
return true;
}
return super.canPlaceBlockOnSide(world, pos, side);
}
@Override
protected boolean checkForDrop(World world, BlockPos pos, IBlockState state){
if(state.getBlock() == this && this.canPlaceAt(world, pos, state.getValue(FACING))){
return true;
}
return super.checkForDrop(world, pos, state);
}
@Override
protected boolean onNeighborChangeInternal(World world, BlockPos pos, IBlockState state){
// This is so stupid, BlockTorch DUPLICATES the code that checks for valid placement in the super method instead
// of checking the existing methods...
return !this.checkForDrop(world, pos, state); // Literally all we need. None of the rubbish in super.
}
@Override
public IBlockState getStateForPlacement(World world, BlockPos pos, EnumFacing facing, float hitX, float hitY, float hitZ, int meta, EntityLivingBase placer){
if(this.canPlaceAt(world, pos, facing)){
return this.getDefaultState().withProperty(FACING, facing);
}else{
for(EnumFacing enumfacing : EnumFacing.Plane.HORIZONTAL){
if(this.canPlaceAt(world, pos, enumfacing)){
return this.getDefaultState().withProperty(FACING, enumfacing);
}
}
return this.getDefaultState();
}
}
// Why is everything in BlockTorch private, for goodness sake...
private boolean canPlaceAt(World world, BlockPos pos, EnumFacing facing){
BlockPos blockpos = pos.offset(facing.getOpposite());
IBlockState state = world.getBlockState(blockpos);
Block block = state.getBlock();
BlockFaceShape blockfaceshape = state.getBlockFaceShape(world, blockpos, facing);
if(facing.equals(EnumFacing.UP) && this.canPlaceOn(world, blockpos)){
return true;
}else if(facing != EnumFacing.UP && facing != EnumFacing.DOWN){
return !isExceptBlockForAttachWithPiston(block) && (blockfaceshape == BlockFaceShape.SOLID || block instanceof BlockImbuementAltar);
}else{
return false;
}
}
private boolean canPlaceOn(World world, BlockPos pos){
IBlockState state = world.getBlockState(pos);
return state.getBlock().canPlaceTorchOnTop(state, world, pos);
}
// See BlockFlowerPot for these two (this class is essentially based on a flower pot)
@Override
public boolean removedByPlayer(IBlockState state, World world, BlockPos pos, EntityPlayer player, boolean willHarvest){
if(willHarvest) return true; // If it will harvest, delay deletion of the block until after getDrops
return super.removedByPlayer(state, world, pos, player, willHarvest);
}
@Override
public void harvestBlock(World world, EntityPlayer player, BlockPos pos, IBlockState state, @Nullable TileEntity te, ItemStack tool){
super.harvestBlock(world, player, pos, state, te, tool);
world.setBlockToAir(pos);
}
@Override
public boolean onBlockActivated(World world, BlockPos pos, IBlockState state, EntityPlayer player, EnumHand hand, EnumFacing facing, float hitX, float hitY, float hitZ){
TileEntity tileEntity = world.getTileEntity(pos);
ItemStack stack = player.getHeldItem(hand);
if(tileEntity instanceof TileEntityReceptacle){
Element currentElement = ((TileEntityReceptacle)tileEntity).getElement();
if(currentElement == null){
if(stack.getItem() instanceof ItemSpectralDust && stack.getMetadata() >= 0
&& stack.getMetadata() < Element.values().length){
((TileEntityReceptacle)tileEntity).setElement(Element.values()[stack.getMetadata()]);
if(!player.capabilities.isCreativeMode) stack.shrink(1);
world.playSound(pos.getX(), pos.getY(), pos.getZ(), WizardrySounds.BLOCK_RECEPTACLE_IGNITE,
SoundCategory.BLOCKS, 0.7f, 0.7f, false);
return true;
}
}else{
((TileEntityReceptacle)tileEntity).setElement(null);
ItemStack dust = new ItemStack(WizardryItems.spectral_dust, 1, currentElement.ordinal());
if(stack.isEmpty()){
player.setHeldItem(hand, dust);
}else if(!player.addItemStackToInventory(dust)){
player.dropItem(dust, false);
}
return true;
}
}
return super.onBlockActivated(world, pos, state, player, hand, facing, hitX, hitY, hitZ);
}
@Override
public void randomDisplayTick(IBlockState state, World world, BlockPos pos, Random rand){
TileEntity tileEntity = world.getTileEntity(pos);
if(tileEntity instanceof TileEntityReceptacle){
Element element = ((TileEntityReceptacle)tileEntity).getElement();
if(element != null){
EnumFacing facing = state.getValue(FACING).getOpposite();
Vec3d centre = GeometryUtils.getCentre(pos);
if(facing.getAxis().isHorizontal()){
centre = centre.add(new Vec3d(facing.getDirectionVec()).scale(WALL_PARTICLE_OFFSET)).add(0, 0.125, 0);
}
int[] colours = PARTICLE_COLOURS.get(element);
ParticleBuilder.create(ParticleBuilder.Type.FLASH).pos(centre).scale(0.35f).time(48).clr(colours[0]).spawn(world);
double r = 0.12;
for(int i = 0; i < 3; i++){
double x = r * (rand.nextDouble() * 2 - 1);
double y = r * (rand.nextDouble() * 2 - 1);
double z = r * (rand.nextDouble() * 2 - 1);
ParticleBuilder.create(ParticleBuilder.Type.DUST).pos(centre.x + x, centre.y + y, centre.z + z)
.vel(x * -0.03, 0.02, z * -0.03).time(24 + rand.nextInt(8)).clr(colours[1]).fade(colours[2]).spawn(world);
}
}
}
}
@Override
public void onBlockPlacedBy(World world, BlockPos pos, IBlockState state, EntityLivingBase placer, ItemStack stack){
if(placer instanceof EntityPlayer){
BlockPos centre = pos.offset(world.getBlockState(pos).getValue(FACING).getOpposite());
if(world.getBlockState(centre).getBlock() == WizardryBlocks.imbuement_altar
&& Arrays.stream(EnumFacing.HORIZONTALS).allMatch(f -> world.getBlockState(centre.offset(f)).getBlock() == WizardryBlocks.receptacle)){
WizardryAdvancementTriggers.restore_imbuement_altar.triggerFor((EntityPlayer)placer);
}
}
}
@Nullable
@Override
public TileEntity createNewTileEntity(World world, int meta){
return new TileEntityReceptacle();
}
@Override
public boolean hasComparatorInputOverride(IBlockState state){
return true;
}
@Override
public int getComparatorInputOverride(IBlockState state, World world, BlockPos pos){
TileEntity tileEntity = world.getTileEntity(pos);
if(tileEntity instanceof TileEntityReceptacle){
Element element = ((TileEntityReceptacle)tileEntity).getElement();
return element == null ? 0 : element.ordinal() + 1;
}
return super.getComparatorInputOverride(state, world, pos);
}
}
@@ -1,87 +0,0 @@
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){
Element element = Element.values()[metadata];
if(!ELEMENT.getAllowedValues().contains(element)) return this.getDefaultState();
return this.getDefaultState().withProperty(ELEMENT, element);
}
@Override
public int getMetaFromState(IBlockState state){
return state.getValue(ELEMENT).ordinal();
}
@Override
protected BlockStateContainer createBlockState(){
return new BlockStateContainer(this, ELEMENT);
}
}
@@ -1,120 +1,90 @@
package electroblob.wizardry.block;
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 net.minecraft.block.Block;
import net.minecraft.block.ITileEntityProvider;
import net.minecraft.block.SoundType;
import net.minecraft.block.material.Material;
import net.minecraft.block.state.IBlockState;
import net.minecraft.entity.Entity;
import net.minecraft.entity.EntityLivingBase;
import net.minecraft.init.MobEffects;
import net.minecraft.item.Item;
import net.minecraft.potion.PotionEffect;
import net.minecraft.tileentity.TileEntity;
import net.minecraft.util.BlockRenderLayer;
import net.minecraft.util.DamageSource;
import net.minecraft.util.EnumBlockRenderType;
import net.minecraft.util.EnumFacing;
import net.minecraft.util.math.AxisAlignedBB;
import net.minecraft.util.math.BlockPos;
import net.minecraft.world.IBlockAccess;
import net.minecraft.world.World;
import java.util.Random;
public class BlockSnare extends Block implements ITileEntityProvider {
import electroblob.wizardry.EnumElement;
import electroblob.wizardry.MagicDamage;
import electroblob.wizardry.MagicDamage.DamageType;
import electroblob.wizardry.WizardryUtilities;
import electroblob.wizardry.tileentity.TileEntityPlayerSave;
import net.minecraft.block.Block;
import net.minecraft.block.BlockContainer;
import net.minecraft.block.material.Material;
import net.minecraft.entity.Entity;
import net.minecraft.entity.EntityLivingBase;
import net.minecraft.item.Item;
import net.minecraft.potion.Potion;
import net.minecraft.potion.PotionEffect;
import net.minecraft.tileentity.TileEntity;
import net.minecraft.util.AxisAlignedBB;
import net.minecraft.util.DamageSource;
import net.minecraft.world.World;
import net.minecraftforge.common.util.ForgeDirection;
private static final AxisAlignedBB AABB = new AxisAlignedBB(0.0f, 0.0f, 0.0f, 1.0f, 0.0625f, 1.0f);
public class BlockSnare extends BlockContainer {
public BlockSnare(Material material){
super(material);
this.setSoundType(SoundType.PLANT);
public BlockSnare(Material par2Material){
super(par2Material);
this.setBlockBounds(0.0f, 0.0f, 0.0f, 1.0f, 0.0625f, 1.0f);
}
@Override
public AxisAlignedBB getBoundingBox(IBlockState state, IBlockAccess source, BlockPos pos){
return AABB;
public AxisAlignedBB getCollisionBoundingBoxFromPool(World par1World, int par2, int par3, int par4){
return null;
}
@Override
public AxisAlignedBB getCollisionBoundingBox(IBlockState state, IBlockAccess world, BlockPos pos){
return NULL_AABB;
}
@Override
public boolean hasTileEntity(IBlockState state){
public boolean hasTileEntity(int metadata){
return true;
}
@Override
public void onEntityCollision(World world, BlockPos pos, IBlockState state, Entity entity){
if(entity instanceof EntityLivingBase){
if(world.getTileEntity(pos) instanceof TileEntityPlayerSave){
TileEntityPlayerSave tileentity = (TileEntityPlayerSave)world.getTileEntity(pos);
if(AllyDesignationSystem.isValidTarget(tileentity.getCaster(), entity)){
DamageSource source = tileentity.getCaster() == null ? DamageSource.CACTUS
: MagicDamage.causeDirectMagicDamage(tileentity.getCaster(), DamageType.MAGIC);
entity.attackEntityFrom(source, 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()));
if(!world.isRemote) world.destroyBlock(pos, false);
public void onEntityCollidedWithBlock(World world, int x, int y, int z, Entity entity){
if(!world.isRemote && entity instanceof EntityLivingBase){
if(world.getTileEntity(x, y, z) instanceof TileEntityPlayerSave){
TileEntityPlayerSave tileentity = (TileEntityPlayerSave)world.getTileEntity(x, y, z);
if(WizardryUtilities.isValidTarget(tileentity.getCaster(), entity)){
((EntityLivingBase)entity).attackEntityFrom(MagicDamage.causeDirectMagicDamage(tileentity.getCaster(), DamageType.MAGIC), 6);
((EntityLivingBase)entity).addPotionEffect(new PotionEffect(Potion.moveSlowdown.id, 100, 2));
world.func_147480_a(x, y, z, false);
}
}
}
}
// The similarly named onNeighborChange method does NOT do the same thing.
@SuppressWarnings("deprecation")
@Override
public void neighborChanged(IBlockState state, World world, BlockPos pos, Block block, BlockPos fromPos){
super.neighborChanged(state, world, pos, block, fromPos);
if(!world.isSideSolid(pos.down(), EnumFacing.UP, false)){
world.setBlockToAir(pos);
}
public void onNeighborBlockChange(World world, int x, int y, int z, Block block)
{
super.onNeighborBlockChange(world, x, y, z, block);
if(!world.isSideSolid(x, y-1, z, ForgeDirection.UP)){
world.setBlockToAir(x, y, z);
}
}
@Override
public boolean renderAsNormalBlock()
{
return false;
}
@Override
public boolean isOpaqueCube()
{
return false;
}
@Override
public int getRenderType(){
return 23;
}
@Override
public BlockRenderLayer getRenderLayer() {
return BlockRenderLayer.CUTOUT;
}
@Override
public EnumBlockRenderType getRenderType(IBlockState state){
return EnumBlockRenderType.MODEL;
}
@Override
public boolean isOpaqueCube(IBlockState state){
return false;
}
@Override
public boolean isFullCube(IBlockState state){
return false;
}
@Override
public Item getItemDropped(IBlockState state, Random rand, int fortune){
public Item getItemDropped(int a, Random random, int b){
return null;
}
@Override
public TileEntity createNewTileEntity(World world, int metadata){
public TileEntity createNewTileEntity(World world, int metadata) {
return new TileEntityPlayerSave();
}
@@ -1,101 +1,74 @@
package electroblob.wizardry.block;
import electroblob.wizardry.registry.WizardryBlocks;
import electroblob.wizardry.tileentity.TileEntityTimer;
import electroblob.wizardry.util.ParticleBuilder;
import electroblob.wizardry.util.ParticleBuilder.Type;
import net.minecraft.block.Block;
import net.minecraft.block.ITileEntityProvider;
import net.minecraft.block.SoundType;
import net.minecraft.block.material.Material;
import net.minecraft.block.state.IBlockState;
import net.minecraft.tileentity.TileEntity;
import net.minecraft.util.BlockRenderLayer;
import net.minecraft.util.EnumFacing;
import net.minecraft.util.math.BlockPos;
import net.minecraft.world.IBlockAccess;
import net.minecraft.world.World;
import net.minecraftforge.event.world.BlockEvent;
import net.minecraftforge.fml.common.Mod;
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
import net.minecraftforge.fml.relauncher.Side;
import net.minecraftforge.fml.relauncher.SideOnly;
import java.util.Random;
@Mod.EventBusSubscriber
public class BlockSpectral extends Block implements ITileEntityProvider {
import electroblob.wizardry.EnumParticleType;
import electroblob.wizardry.Wizardry;
import electroblob.wizardry.tileentity.TileEntityTimer;
import net.minecraft.block.Block;
import net.minecraft.block.BlockContainer;
import net.minecraft.block.material.Material;
import net.minecraft.tileentity.TileEntity;
import net.minecraft.world.IBlockAccess;
import net.minecraft.world.World;
public BlockSpectral(Material material){
// For future reference - extend BlockContainer whenever possible because it has methods for removing tile entities on block break.
public class BlockSpectral extends BlockContainer {
public BlockSpectral(Material material) {
super(material);
this.setSoundType(SoundType.GLASS);
}
// Replaces getRenderBlockPass
@Override
@SideOnly(Side.CLIENT)
public BlockRenderLayer getRenderLayer(){
return BlockRenderLayer.TRANSLUCENT;
}
// See BlockTransportationStone for what all these do
@Override public boolean isFullCube(IBlockState state){ return false; }
@Override public boolean isBlockNormalCube(IBlockState state){ return false; }
@Override public boolean isNormalCube(IBlockState state){ return false; }
@Override public boolean isOpaqueCube(IBlockState state){ return false; }
public int getRenderBlockPass()
{
return 1;
}
@Override
public void randomDisplayTick(IBlockState state, World world, BlockPos pos, Random random){
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
// // full light.
// @Override
// public int getPackedLightmapCoords(IBlockState state, IBlockAccess source, BlockPos pos){
// return 15;
// }
public boolean isOpaqueCube()
{
return false;
}
@Override
public boolean hasTileEntity(IBlockState state){
return true;
}
public boolean shouldSideBeRendered(IBlockAccess par1IBlockAccess, int par2, int par3, int par4, int par5)
{
Block block = par1IBlockAccess.getBlock(par2, par3, par4);
return block == this ? false : super.shouldSideBeRendered(par1IBlockAccess, par2, par3, par4, par5);
}
@Override
public TileEntity createNewTileEntity(World world, int metadata){
public void randomDisplayTick(World world, int x, int y, int z, Random random){
// Middle of block
Wizardry.proxy.spawnParticle(EnumParticleType.DUST, world, x+random.nextDouble(), y+random.nextDouble(), z+random.nextDouble(), 0, 0, 0, (int)(16.0D / (Math.random() * 0.8D + 0.2D)),
0.4f + random.nextFloat()*0.2f, 0.6f + random.nextFloat()*0.4f, 0.6f + random.nextFloat()*0.4f);
// Top surface
Wizardry.proxy.spawnParticle(EnumParticleType.DUST, world, x+random.nextDouble(), y+1, z+random.nextDouble(), 0, 0, 0, (int)(16.0D / (Math.random() * 0.8D + 0.2D)),
0.4f + random.nextFloat()*0.2f, 0.6f + random.nextFloat()*0.4f, 0.6f + random.nextFloat()*0.4f);
Wizardry.proxy.spawnParticle(EnumParticleType.DUST, world, x+random.nextDouble(), y+1, z+random.nextDouble(), 0, 0, 0, (int)(16.0D / (Math.random() * 0.8D + 0.2D)),
0.4f + random.nextFloat()*0.2f, 0.6f + random.nextFloat()*0.4f, 0.6f + random.nextFloat()*0.4f);
}
// Overriden to make the block always look full brightness despite not emitting full light.
@Override
public int getMixedBrightnessForBlock(IBlockAccess par1IBlockAccess, int par2, int par3, int par4)
{
return 15;
}
@Override
public TileEntity createNewTileEntity(World world, int metadata) {
return new TileEntityTimer(1200);
}
@Override
public int quantityDropped(Random par1Random){
return 0;
}
@SuppressWarnings("deprecation")
@SideOnly(Side.CLIENT)
@Override
public boolean shouldSideBeRendered(IBlockState blockState, IBlockAccess blockAccess, BlockPos pos,
EnumFacing side){
IBlockState iblockstate = blockAccess.getBlockState(pos.offset(side));
Block block = iblockstate.getBlock();
return block == this ? false : super.shouldSideBeRendered(blockState, blockAccess, pos, side);
}
@SubscribeEvent
public static void onBlockPlaceEvent(BlockEvent.PlaceEvent event){
// Spectral blocks cannot be built on
if(event.getPlacedAgainst() == WizardryBlocks.spectral_block){
event.setCanceled(true);
}
}
/**
* Returns the quantity of items to drop on block destruction.
*/
@Override
public int quantityDropped(Random par1Random)
{
return 0;
}
}
@@ -1,262 +1,172 @@
package electroblob.wizardry.block;
import electroblob.wizardry.tileentity.TileEntityStatue;
import electroblob.wizardry.util.BlockUtils;
import net.minecraft.block.Block;
import net.minecraft.block.ITileEntityProvider;
import net.minecraft.block.SoundType;
import net.minecraft.block.material.Material;
import net.minecraft.block.state.IBlockState;
import net.minecraft.entity.EntityLiving;
import net.minecraft.entity.EntityLivingBase;
import net.minecraft.tileentity.TileEntity;
import net.minecraft.util.BlockRenderLayer;
import net.minecraft.util.EnumBlockRenderType;
import net.minecraft.util.EnumFacing;
import net.minecraft.util.math.AxisAlignedBB;
import net.minecraft.util.math.BlockPos;
import net.minecraft.world.IBlockAccess;
import net.minecraft.world.World;
import net.minecraftforge.fml.relauncher.Side;
import net.minecraftforge.fml.relauncher.SideOnly;
import javax.annotation.Nullable;
import java.util.Random;
public class BlockStatue extends Block implements ITileEntityProvider {
import cpw.mods.fml.relauncher.Side;
import cpw.mods.fml.relauncher.SideOnly;
import electroblob.wizardry.tileentity.TileEntityStatue;
import net.minecraft.block.Block;
import net.minecraft.block.BlockContainer;
import net.minecraft.block.material.Material;
import net.minecraft.init.Blocks;
import net.minecraft.tileentity.TileEntity;
import net.minecraft.util.AxisAlignedBB;
import net.minecraft.util.Facing;
import net.minecraft.world.IBlockAccess;
import net.minecraft.world.World;
public class BlockStatue extends BlockContainer {
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;
public BlockStatue(Material par2Material) {
super(par2Material);
this.isIce = par2Material == Material.ice;
if(this.isIce){
this.setDefaultSlipperiness(0.98f);
this.setSoundType(SoundType.GLASS);
this.slipperiness = 0.98F;
}
}
@Override
public AxisAlignedBB getBoundingBox(IBlockState state, IBlockAccess world, BlockPos pos){
public void setBlockBoundsBasedOnState(IBlockAccess world, int x, int y, int z) {
// Not a good idea to call getBlockBoundsMinX() or whatever from in here, since this method changes those!
if(!this.isIce){
if(world.getTileEntity(pos) instanceof TileEntityStatue){
if(world.getTileEntity(x, y, z) instanceof TileEntityStatue){
TileEntityStatue statue = (TileEntityStatue)world.getTileEntity(pos);
TileEntityStatue statue = (TileEntityStatue)world.getTileEntity(x, y, z);
if(statue.creature != null){
// Block bounds are set to match the width and height of the entity, clamped to within 1 block.
return new AxisAlignedBB((float)Math.max(0.5 - statue.creature.width / 2, 0), 0,
(float)Math.max(0.5 - statue.creature.width / 2, 0),
(float)Math.min(0.5 + statue.creature.width / 2, 1),
// This checks if the block is the top one and if so reduces its height so the top lines up
// with
this.setBlockBounds((float)Math.max(0.5 - statue.creature.width/2, 0), 0,
(float)Math.max(0.5 - statue.creature.width/2, 0),
(float)Math.min(0.5 + statue.creature.width/2, 1),
// This checks if the block is the top one and if so reduces its height so the top lines up with
// the top of the entity model.
statue.position == statue.parts
? (float)Math.min(statue.creature.height - statue.parts + 1, 1)
: 1,
(float)Math.min(0.5 + statue.creature.width / 2, 1));
statue.position == statue.parts ? (float)Math.min(statue.creature.height - statue.parts + 1, 1) : 1,
(float)Math.min(0.5 + statue.creature.width/2, 1));
}
}
}
}
@Override
public AxisAlignedBB getCollisionBoundingBoxFromPool(World world, int x, int y, int z) {
if(!this.isIce){
if(world.getTileEntity(x, y, z) instanceof TileEntityStatue){
TileEntityStatue statue = (TileEntityStatue)world.getTileEntity(x, y, z);
if(statue.creature != null){
// Block bounds are set to match the width and height of the entity, clamped to within 1 block.
return AxisAlignedBB.getBoundingBox(x + Math.max(0.5 - statue.creature.width/2, 0),
y, z + Math.max(0.5 - statue.creature.width/2, 0),
x + Math.min(0.5 + statue.creature.width/2, 1),
// This checks if the block is the top one and if so reduces its height so the top lines up with
// the top of the entity model
statue.position == statue.parts ? y + (float)Math.min(statue.creature.height - statue.parts + 1, 1) : y + 1,
z + Math.min(0.5 + statue.creature.width/2, 1));
}
}
}
return FULL_BLOCK_AABB;
return super.getCollisionBoundingBoxFromPool(world, x, y, z);
}
// getCollisionBoundingBox eventually calls getBoundingBox anyway, and since I want the collision box and the block
// outline to be the same here, I've removed that getCollisionBoundingBox entirely.
// The number of these methods is quite simply ridiculous. This one seems to be for placement logic and block
// connections (fences, glass panes, etc.)...
@Override
public boolean isFullCube(IBlockState state){
return false;
}
// ...this one isn't used much but has something to do with redstone...
@Override
public boolean isBlockNormalCube(IBlockState state){
return false;
}
// ... this one is for most other game logic...
@Override
public boolean isNormalCube(IBlockState state){
return false;
}
// Forge version of the above method. I still need to override both though because vanilla uses the other one.
@Override
public boolean isNormalCube(IBlockState state, IBlockAccess world, BlockPos pos){
return false;
}
// ... and this one is for rendering.
@Override
public boolean isOpaqueCube(IBlockState state){
public boolean renderAsNormalBlock(){
return false;
}
@Override
public BlockRenderLayer getRenderLayer(){
return this.isIce ? BlockRenderLayer.TRANSLUCENT : BlockRenderLayer.SOLID;
public boolean canRenderInPass(int pass){
return this.isIce ? super.canRenderInPass(pass) : false;
}
@Override
public EnumBlockRenderType getRenderType(IBlockState state){
return this.isIce ? EnumBlockRenderType.MODEL : EnumBlockRenderType.ENTITYBLOCK_ANIMATED;
public boolean isOpaqueCube(){
return false;// !this.isIce;
}
@Override
public boolean hasTileEntity(IBlockState state){
return true;
public int getRenderBlockPass()
{
return this.isIce? 1 : 0;
}
@Override
public TileEntity createNewTileEntity(World world, int metadata){
public TileEntity createNewTileEntity(World world, int metadata) {
return new TileEntityStatue(this.isIce);
}
/*
@Override
public int quantityDropped(Random random){
public void randomDisplayTick(World world, int x, int y, int z, Random random){
if(this.isIce){
float brightness = 0.5f + (random.nextFloat()/2);
Minecraft.getMinecraft().effectRenderer.addEffect(new EntitySparkleFX(world, x + random.nextDouble(), y + random.nextDouble(), z + random.nextDouble(), 0, 0, 0, null, brightness, brightness + 0.1f, 1.0f, true));
}
}
*/
/**
* Returns the quantity of items to drop on block destruction.
*/
@Override
public int quantityDropped(Random par1Random)
{
return 0;
}
/**
* Returns true if the given side of this block type should be rendered, if the adjacent block is at the given
* coordinates. Args: blockAccess, x, y, z, side
*/
@Override
public void breakBlock(World world, BlockPos pos, IBlockState state){
@SideOnly(Side.CLIENT)
public boolean shouldSideBeRendered(IBlockAccess p_149646_1_, int p_149646_2_, int p_149646_3_, int p_149646_4_, int p_149646_5_)
{
Block block = p_149646_1_.getBlock(p_149646_2_, p_149646_3_, p_149646_4_);
if(!world.isRemote){
TileEntityStatue tileentity = (TileEntityStatue)world.getTileEntity(pos);
return block == this ? false : super.shouldSideBeRendered(p_149646_1_, p_149646_2_, p_149646_3_, p_149646_4_, p_149646_5_);
}
@Override
public void breakBlock(World par1World, int x, int y, int z, Block block, int par6)
{
if(!par1World.isRemote){
TileEntityStatue tileentity = (TileEntityStatue)par1World.getTileEntity(x, y, z);
if(tileentity != null){
if(tileentity.parts == 2){
if(tileentity.position == 2){
world.destroyBlock(pos.down(), false);
// func_147480_a is the method that causes the block to smash as if broken by a player.
// Used to be called destroyBlock. The boolean is whether the block should drop items.
par1World.func_147480_a(x, y-1, z, false);
}else{
world.destroyBlock(pos.up(), false);
par1World.func_147480_a(x, y+1, z, false);
}
}else if(tileentity.parts == 3){
if(tileentity.position == 3){
world.destroyBlock(pos.down(), false);
world.destroyBlock(pos.down(2), false);
par1World.func_147480_a(x, y-1, z, false);
par1World.func_147480_a(x, y-2, z, false);
}else if(tileentity.position == 2){
world.destroyBlock(pos.down(), false);
world.destroyBlock(pos.up(), false);
par1World.func_147480_a(x, y-1, z, false);
par1World.func_147480_a(x, y+1, z, false);
}else{
world.destroyBlock(pos.up(), false);
world.destroyBlock(pos.up(2), false);
par1World.func_147480_a(x, y+1, z, false);
par1World.func_147480_a(x, y+2, z, false);
}
}
}
// This is only when position == 1 because world.destroyBlock calls this function for the other blocks.
if(tileentity != null && tileentity.position == 1 && tileentity.creature != null){
tileentity.creature.getEntityData().removeTag(BlockStatue.PETRIFIED_NBT_KEY);
tileentity.creature.isDead = false;
world.spawnEntity(tileentity.creature);
par1World.spawnEntityInWorld(tileentity.creature);
}
}
super.breakBlock(world, pos, state);
super.breakBlock(par1World, x, y, z, block, par6);
}
@SuppressWarnings("deprecation")
@SideOnly(Side.CLIENT)
@Override
public boolean shouldSideBeRendered(IBlockState blockState, IBlockAccess blockAccess, BlockPos pos,
EnumFacing side){
IBlockState iblockstate = blockAccess.getBlockState(pos.offset(side));
Block block = iblockstate.getBlock();
return this.isIce && block == this ? false : super.shouldSideBeRendered(blockState, blockAccess, pos, side);
}
/**
* Turns the given entity into a statue. The type of statue depends on the block instance this method was invoked on.
* @param target The entity to turn into a statue.
* @param caster The entity that caused it, or null if it was not caused by an entity
* @param duration The time for which the entity should remain a statue. For petrified creatures, this is the minimum
* time it can stay as a statue.
* @return True if the entity was successfully turned into a statue, false if not (i.e. something was in the way).
*/
// Making this an instance method means it works equally well for both types of statue
public boolean convertToStatue(EntityLiving target, @Nullable EntityLivingBase caster, int duration){
if(target.deathTime > 0 || target.world.isRemote) return false;
BlockPos pos = new BlockPos(target);
World world = target.world;
target.hurtTime = 0; // Stops the entity looking red while frozen and the resulting z-fighting
target.extinguish();
// Short mobs such as spiders and pigs
if((target.height < 1.2 || target.isChild()) && BlockUtils.canBlockBeReplaced(world, pos) && BlockUtils.canPlaceBlock(caster, world, pos)){
world.setBlockState(pos, this.getDefaultState());
if(world.getTileEntity(pos) instanceof TileEntityStatue){
((TileEntityStatue)world.getTileEntity(pos)).setCreatureAndPart(target, 1, 1);
((TileEntityStatue)world.getTileEntity(pos)).setLifetime(duration);
}
target.getEntityData().setBoolean(this.isIce ? FROZEN_NBT_KEY : PETRIFIED_NBT_KEY, true);
target.setDead();
return true;
}
// Normal sized mobs like zombies and skeletons
else if(target.height < 2.5 && BlockUtils.canBlockBeReplaced(world, pos) && BlockUtils.canBlockBeReplaced(world, pos.up())
&& BlockUtils.canPlaceBlock(caster, world, pos) && BlockUtils.canPlaceBlock(caster, world, pos.up())){
world.setBlockState(pos, this.getDefaultState());
if(world.getTileEntity(pos) instanceof TileEntityStatue){
((TileEntityStatue)world.getTileEntity(pos)).setCreatureAndPart(target, 1, 2);
((TileEntityStatue)world.getTileEntity(pos)).setLifetime(duration);
}
world.setBlockState(pos.up(), this.getDefaultState());
if(world.getTileEntity(pos.up()) instanceof TileEntityStatue){
((TileEntityStatue)world.getTileEntity(pos.up())).setCreatureAndPart(target, 2, 2);
}
target.getEntityData().setBoolean(this.isIce ? FROZEN_NBT_KEY : PETRIFIED_NBT_KEY, true);
target.setDead();
return true;
}
// Tall mobs like endermen
else if(BlockUtils.canBlockBeReplaced(world, pos) && BlockUtils.canBlockBeReplaced(world, pos.up()) && BlockUtils.canBlockBeReplaced(world, pos.up(2))
&& BlockUtils.canPlaceBlock(caster, world, pos) && BlockUtils.canPlaceBlock(caster, world, pos.up()) && BlockUtils.canPlaceBlock(caster, world, pos.up(2))){
world.setBlockState(pos, this.getDefaultState());
if(world.getTileEntity(pos) instanceof TileEntityStatue){
((TileEntityStatue)world.getTileEntity(pos)).setCreatureAndPart(target, 1, 3);
((TileEntityStatue)world.getTileEntity(pos)).setLifetime(duration);
}
world.setBlockState(pos.up(), this.getDefaultState());
if(world.getTileEntity(pos.up()) instanceof TileEntityStatue){
((TileEntityStatue)world.getTileEntity(pos.up())).setCreatureAndPart(target, 2, 3);
}
world.setBlockState(pos.up(2), this.getDefaultState());
if(world.getTileEntity(pos.up(2)) instanceof TileEntityStatue){
((TileEntityStatue)world.getTileEntity(pos.up(2))).setCreatureAndPart(target, 3, 3);
}
target.getEntityData().setBoolean(this.isIce ? FROZEN_NBT_KEY : PETRIFIED_NBT_KEY, true);
target.setDead();
return true;
}
return false;
}
}
@@ -1,180 +0,0 @@
package electroblob.wizardry.block;
import electroblob.wizardry.registry.Spells;
import electroblob.wizardry.registry.WizardryBlocks;
import electroblob.wizardry.spell.Spell;
import electroblob.wizardry.tileentity.TileEntityThorns;
import electroblob.wizardry.util.AllyDesignationSystem;
import electroblob.wizardry.util.EntityUtils;
import electroblob.wizardry.util.MagicDamage;
import net.minecraft.block.*;
import net.minecraft.block.BlockDoublePlant.EnumBlockHalf;
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.ChunkCache;
import net.minecraft.world.IBlockAccess;
import net.minecraft.world.World;
import net.minecraft.world.chunk.Chunk;
import net.minecraftforge.event.entity.player.PlayerInteractEvent;
import net.minecraftforge.fml.common.Mod;
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
import java.util.Random;
@Mod.EventBusSubscriber
public class BlockThorns extends BlockBush implements ITileEntityProvider {
public static final int GROWTH_STAGES = 8;
public static final int GROWTH_STAGE_DURATION = 2;
public static final PropertyInteger AGE = PropertyInteger.create("age", 0, GROWTH_STAGES-1);
public static final PropertyEnum<EnumBlockHalf> HALF = PropertyEnum.create("half", EnumBlockHalf.class);
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, meta == 0 ? EnumBlockHalf.LOWER : EnumBlockHalf.UPPER);
}
@Override
public int getMetaFromState(IBlockState state){
return state.getValue(HALF).ordinal();
}
@Override
public IBlockState getActualState(IBlockState state, IBlockAccess world, BlockPos pos){
if(state.getValue(HALF) == EnumBlockHalf.UPPER) pos = pos.down();
// Copied from BlockFlowerPot on authority of the Forge docs, which say it needs to be here
TileEntity tileentity = world instanceof ChunkCache ? ((ChunkCache)world).getTileEntity(pos, Chunk.EnumCreateEntityType.CHECK) : world.getTileEntity(pos);
if(tileentity instanceof TileEntityThorns){
return state.withProperty(AGE, ((TileEntityThorns)tileentity).getAge());
}else{
return state.withProperty(AGE, 7);
}
}
@Override
protected BlockStateContainer createBlockState(){
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 void onEntityCollision(World world, BlockPos pos, IBlockState state, Entity entity){
if(applyThornDamage(world, pos, state, entity)){
entity.setInWeb(); // Needs to be called client-side for players (and besides, all of this is common code)
}
}
private static boolean applyThornDamage(World world, BlockPos pos, IBlockState state, Entity target){
DamageSource source = DamageSource.CACTUS;
float damage = Spells.forest_of_thorns.getProperty(Spell.DAMAGE).floatValue();
TileEntity tileentity = world.getTileEntity(state.getValue(HALF) == EnumBlockHalf.UPPER ? pos.down() : pos);
if(tileentity instanceof TileEntityThorns){
damage *= ((TileEntityThorns)tileentity).damageMultiplier;
EntityLivingBase caster = ((TileEntityThorns)tileentity).getCaster();
if(!AllyDesignationSystem.isValidTarget(caster, target)) return false; // Don't attack or slow allies of the caster
if(caster != null){
source = MagicDamage.causeDirectMagicDamage(caster, MagicDamage.DamageType.MAGIC);
}
}
if(target.ticksExisted % 20 == 0){
EntityUtils.attackEntityWithoutKnockback(target, source, damage);
}
return true;
}
@Override
public Block.EnumOffsetType getOffsetType(){
return Block.EnumOffsetType.XZ;
}
@Override
public TileEntity createNewTileEntity(World world, int metadata){
return new TileEntityThorns();
}
@Override
public boolean hasTileEntity(IBlockState state){
return state.getValue(HALF) == EnumBlockHalf.LOWER;
}
@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().getBlockState(event.getPos()).getBlock() == WizardryBlocks.thorns){
applyThornDamage(event.getWorld(), event.getPos(), event.getWorld().getBlockState(event.getPos()), event.getEntity());
}
}
}
@@ -1,228 +1,149 @@
package electroblob.wizardry.block;
import electroblob.wizardry.Wizardry;
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.GeometryUtils;
import electroblob.wizardry.util.Location;
import electroblob.wizardry.util.ParticleBuilder;
import net.minecraft.block.Block;
import net.minecraft.block.material.Material;
import net.minecraft.block.state.IBlockState;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.item.ItemStack;
import net.minecraft.util.EnumFacing;
import net.minecraft.util.EnumHand;
import net.minecraft.util.math.AxisAlignedBB;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.text.TextComponentTranslation;
import net.minecraft.world.IBlockAccess;
import net.minecraft.world.World;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
public class BlockTransportationStone extends Block {
import electroblob.wizardry.ExtendedPlayer;
import electroblob.wizardry.Wizardry;
import electroblob.wizardry.WizardryRegistry;
import electroblob.wizardry.item.ItemWand;
import electroblob.wizardry.spell.Spell;
import net.minecraft.block.Block;
import net.minecraft.block.material.Material;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.util.ChatComponentTranslation;
import net.minecraft.world.World;
import net.minecraftforge.common.util.ForgeDirection;
private static final AxisAlignedBB AABB = new AxisAlignedBB(0.0625f * 5, 0, 0.0625f * 5, 0.0625f * 11, 0.0625f * 6,
0.0625f * 11);
public class BlockTransportationStone extends Block{
public BlockTransportationStone(Material material){
super(material);
this.setTickRandomly(true);
this.setBlockBounds(0.0625f*5, 0, 0.0625f*5, 0.0625f*11, 0.0625f*6, 0.0625f*11);
this.setTickRandomly(true);
}
@Override
public AxisAlignedBB getBoundingBox(IBlockState state, IBlockAccess source, BlockPos pos){
return AABB;
}
// The number of these methods is quite simply ridiculous. This one seems to be for placement logic and block
// connections (fences, glass panes, etc.)...
@Override
public boolean isFullCube(IBlockState state){
public boolean isOpaqueCube(){
return false;
}
// ...this one isn't used much but has something to do with redstone...
@Override
public boolean isBlockNormalCube(IBlockState state){
public boolean renderAsNormalBlock(){
return false;
}
// ... this one is for most other game logic...
@Override
public boolean isNormalCube(IBlockState state){
return false;
}
// ... and this one is for rendering.
public void onNeighborBlockChange(World world, int x, int y, int z, Block block)
{
super.onNeighborBlockChange(world, x, y, z, block);
if(!world.isSideSolid(x, y-1, z, ForgeDirection.UP)){
this.dropBlockAsItem(world, x, y, z, world.getBlockMetadata(x, y, z), 0);
world.setBlockToAir(x, y, z);
}
}
@Override
public boolean isOpaqueCube(IBlockState state){
return false;
}
public void updateTick(World world, int x, int y, int z, Random random)
{
if(!world.isSideSolid(x, y-1, z, ForgeDirection.UP)){
this.dropBlockAsItem(world, x, y, z, world.getBlockMetadata(x, y, z), 0);
world.setBlockToAir(x, y, z);
}
}
@Override
public boolean isSideSolid(IBlockState base_state, IBlockAccess world, BlockPos pos, EnumFacing side){
return side == EnumFacing.DOWN;
}
@SuppressWarnings("deprecation")
public boolean canPlaceBlockAt(World par1World, int x, int y, int z)
{
return super.canPlaceBlockAt(par1World, x, y, z) && par1World.isSideSolid(x, y-1, z, ForgeDirection.UP);
}
@Override
public void neighborChanged(IBlockState state, World world, BlockPos pos, Block block, BlockPos fromPos){
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);
}
}
@Override
public void updateTick(World world, BlockPos pos, IBlockState state, Random random){
if(!world.isSideSolid(pos.down(), EnumFacing.UP)){
this.dropBlockAsItem(world, pos, world.getBlockState(pos), 0);
world.setBlockToAir(pos);
}
}
@Override
public boolean canPlaceBlockAt(World world, BlockPos pos){
return super.canPlaceBlockAt(world, pos) && world.isSideSolid(pos.down(), EnumFacing.UP);
}
@Override
public boolean onBlockActivated(World world, BlockPos pos, IBlockState state, EntityPlayer player, EnumHand hand,
EnumFacing side, float hitX, float hitY, float hitZ){
ItemStack stack = player.getHeldItem(hand);
if(stack.getItem() instanceof ISpellCastingItem){
if(WizardData.get(player) != null){
WizardData data = WizardData.get(player);
for(int x = -1; x <= 1; x++){
for(int z = -1; z <= 1; z++){
BlockPos pos1 = pos.add(x, 0, z);
if(testForCircle(world, pos1)){
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
if(locations.isEmpty()) locations.add(here);
else locations.set(Math.max(locations.size() - 1, 0), here);
}
if(!world.isRemote) player.sendStatusMessage(new TextComponentTranslation("tile." + Wizardry.MODID + ":transportation_stone.confirm", Spells.transportation.getNameForTranslationFormatted()), true);
}
return true;
}
}
public boolean onBlockActivated(World world, int x, int y, int z, EntityPlayer entityplayer, int par6, float par7, float par8, float par9)
{
if(entityplayer.getHeldItem() != null && entityplayer.getHeldItem().getItem() instanceof ItemWand){
if(ExtendedPlayer.get(entityplayer) != null){
ExtendedPlayer extendedplayer = ExtendedPlayer.get(entityplayer);
int x1=x-1, z1=z-1;
if(testForCircle(world, x1, y, z1)){
extendedplayer.setStoneLocation(x1, y, z1, world.provider.dimensionId);
if(!world.isRemote) entityplayer.addChatMessage(new ChatComponentTranslation("tile.transportationStone.confirm", WizardryRegistry.transportation.getDisplayNameWithFormatting()));//"You will now be returned here upon casting " + EnumSpell.TRANSPORTATION.name);
return true;
}
if(!world.isRemote){
player.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(GeometryUtils.getCentre(centre).add(x, -0.3125, z)).clr(0x86ff65)
.time(200).scale(2).spawn(world);
}
}
x1=x;
z1=z-1;
if(testForCircle(world, x1, y, z1)){
extendedplayer.setStoneLocation(x1, y, z1, world.provider.dimensionId);
if(!world.isRemote) entityplayer.addChatMessage(new ChatComponentTranslation("tile.transportationStone.confirm", WizardryRegistry.transportation.getDisplayNameWithFormatting()));//"You will now be returned here upon casting " + EnumSpell.TRANSPORTATION.name);
return true;
}
x1=x+1;
z1=z-1;
if(testForCircle(world, x1, y, z1)){
extendedplayer.setStoneLocation(x1, y, z1, world.provider.dimensionId);
if(!world.isRemote) entityplayer.addChatMessage(new ChatComponentTranslation("tile.transportationStone.confirm", WizardryRegistry.transportation.getDisplayNameWithFormatting()));//"You will now be returned here upon casting " + EnumSpell.TRANSPORTATION.name);
return true;
}
x1=x-1;
z1=z;
if(testForCircle(world, x1, y, z1)){
extendedplayer.setStoneLocation(x1, y, z1, world.provider.dimensionId);
if(!world.isRemote) entityplayer.addChatMessage(new ChatComponentTranslation("tile.transportationStone.confirm", WizardryRegistry.transportation.getDisplayNameWithFormatting()));//"You will now be returned here upon casting " + EnumSpell.TRANSPORTATION.name);
return true;
}
x1=x+1;
z1=z;
if(testForCircle(world, x1, y, z1)){
extendedplayer.setStoneLocation(x1, y, z1, world.provider.dimensionId);
if(!world.isRemote) entityplayer.addChatMessage(new ChatComponentTranslation("tile.transportationStone.confirm", WizardryRegistry.transportation.getDisplayNameWithFormatting()));//"You will now be returned here upon casting " + EnumSpell.TRANSPORTATION.name);
return true;
}
x1=x-1;
z1=z+1;
if(testForCircle(world, x1, y, z1)){
extendedplayer.setStoneLocation(x1, y, z1, world.provider.dimensionId);
if(!world.isRemote) entityplayer.addChatMessage(new ChatComponentTranslation("tile.transportationStone.confirm", WizardryRegistry.transportation.getDisplayNameWithFormatting()));//"You will now be returned here upon casting " + EnumSpell.TRANSPORTATION.name);
return true;
}
x1=x;
z1=z+1;
if(testForCircle(world, x1, y, z1)){
extendedplayer.setStoneLocation(x1, y, z1, world.provider.dimensionId);
if(!world.isRemote) entityplayer.addChatMessage(new ChatComponentTranslation("tile.transportationStone.confirm", WizardryRegistry.transportation.getDisplayNameWithFormatting()));//"You will now be returned here upon casting " + EnumSpell.TRANSPORTATION.name);
return true;
}
x1=x+1;
z1=z+1;
if(testForCircle(world, x1, y, z1)){
extendedplayer.setStoneLocation(x1, y, z1, world.provider.dimensionId);
if(!world.isRemote) entityplayer.addChatMessage(new ChatComponentTranslation("tile.transportationStone.confirm", WizardryRegistry.transportation.getDisplayNameWithFormatting()));//"You will now be returned here upon casting " + EnumSpell.TRANSPORTATION.name);
return true;
}
if(!world.isRemote) entityplayer.addChatMessage(new ChatComponentTranslation("tile.transportationStone.invalid"));//"You must make a circle with 8 stones of transportation first!");
return true;
}
}
return false;
}
/** Returns whether the specified location is surrounded by a complete circle of 8 transportation stones. */
public static boolean testForCircle(World world, BlockPos pos){
if(world.getBlockState(pos).getMaterial().blocksMovement() || world.getBlockState(pos.up()).getMaterial()
.blocksMovement()) return false;
for(int x = -1; x <= 1; x++){
for(int z = -1; z <= 1; z++){
if(x == 0 && z == 0) continue;
if(world.getBlockState(pos.add(x, 0, z)).getBlock() != WizardryBlocks.transportation_stone){
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;
return false;
}
/** Returns whether the specified location is surrounded by a complete cicle of 8 transportation stones. */
public static boolean testForCircle(World world, int x, int y, int z){
return world.getBlock(x-1, y, z-1) == Wizardry.transportationStone
&& world.getBlock(x, y, z-1) == Wizardry.transportationStone
&& world.getBlock(x+1, y, z-1) == Wizardry.transportationStone
&& world.getBlock(x-1, y, z) == Wizardry.transportationStone
&& !world.getBlock(x, y, z).getMaterial().blocksMovement()
&& world.getBlock(x+1, y, z) == Wizardry.transportationStone
&& world.getBlock(x-1, y, z+1) == Wizardry.transportationStone
&& world.getBlock(x, y, z+1) == Wizardry.transportationStone
&& world.getBlock(x+1, y, z+1) == Wizardry.transportationStone;
}
}
@@ -1,73 +1,61 @@
package electroblob.wizardry.block;
import electroblob.wizardry.tileentity.TileEntityTimer;
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;
import net.minecraft.tileentity.TileEntity;
import net.minecraft.util.BlockRenderLayer;
import net.minecraft.util.EnumBlockRenderType;
import net.minecraft.util.math.AxisAlignedBB;
import net.minecraft.util.math.BlockPos;
import net.minecraft.world.IBlockAccess;
import net.minecraft.world.World;
import net.minecraftforge.fml.relauncher.Side;
import net.minecraftforge.fml.relauncher.SideOnly;
import java.util.Random;
public class BlockVanishingCobweb extends Block implements ITileEntityProvider {
import electroblob.wizardry.EnumParticleType;
import electroblob.wizardry.Wizardry;
import electroblob.wizardry.tileentity.TileEntityTimer;
import net.minecraft.block.Block;
import net.minecraft.block.BlockContainer;
import net.minecraft.block.material.Material;
import net.minecraft.entity.Entity;
import net.minecraft.tileentity.TileEntity;
import net.minecraft.util.AxisAlignedBB;
import net.minecraft.world.IBlockAccess;
import net.minecraft.world.World;
public BlockVanishingCobweb(Material material){
// For future reference - extend BlockContainer whenever possible because it has methods for removing tile entities on block break.
public class BlockVanishingCobweb extends BlockContainer {
public BlockVanishingCobweb(Material material) {
super(material);
}
@SideOnly(Side.CLIENT)
@Override
public BlockRenderLayer getRenderLayer(){
return BlockRenderLayer.CUTOUT;
}
public int getRenderType()
{
return 1;
}
@Override
public EnumBlockRenderType getRenderType(IBlockState state){
return EnumBlockRenderType.MODEL;
}
@Override
public boolean isOpaqueCube(IBlockState state){
return false;
}
@Override
public AxisAlignedBB getCollisionBoundingBox(IBlockState state, IBlockAccess world, BlockPos pos){
return NULL_AABB;
}
@Override
public boolean isFullCube(IBlockState state){
return false;
}
@Override
public boolean hasTileEntity(IBlockState state){
return true;
}
@Override
public TileEntity createNewTileEntity(World world, int metadata){
public boolean isOpaqueCube()
{
return false;
}
@Override
public TileEntity createNewTileEntity(World world, int metadata) {
return new TileEntityTimer(400);
}
@Override
public void onEntityCollision(World world, BlockPos pos, IBlockState state, Entity entity){
entity.setInWeb();
}
@Override
public int quantityDropped(Random par1Random){
return 0;
}
@Override
public void onEntityCollidedWithBlock(World world, int x, int y, int z, Entity entity)
{
entity.setInWeb();
}
@Override
public AxisAlignedBB getCollisionBoundingBoxFromPool(World p_149668_1_, int p_149668_2_, int p_149668_3_, int p_149668_4_) {
return null;
}
/**
* Returns the quantity of items to drop on block destruction.
*/
@Override
public int quantityDropped(Random par1Random)
{
return 0;
}
}
File diff suppressed because it is too large Load Diff
@@ -1,304 +0,0 @@
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;
import java.util.Random;
/**
* Utility class containing some useful static methods for drawing GUIs, as well as general rendering.
*
* @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();
}
/**
* Draws a 'glitch' rectangle, with some rows of pixels shifted randomly to give a broken effect.
*
* @param random A random number generator to use
* @param x The x position of the rectangle
* @param y The y position of the rectangle
* @param u The x position of the top left corner of the section of the image wanted
* @param v The y position of the top left corner of the section of the image wanted
* @param width The width of the section
* @param height The height of the section
* @param textureWidth The width of the actual image.
* @param textureHeight The height of the actual image.
* @param flipX Whether to flip the texture in the x direction.
* @param flipY Whether to flip the texture in the y direction.
*/
public static void drawGlitchRect(Random random, int x, int y, int u, int v, int width, int height, int textureWidth, int textureHeight, boolean flipX, boolean flipY){
for(int i=0; i<height; i++){
if(flipY) i = height - i - 1;
int offset = random.nextInt(4) == 0 ? random.nextInt(6) - 3 : 0;
drawTexturedFlippedRect(x + offset, y + i, u, v + i, width, 1, textureWidth, textureHeight, flipX, flipY);
}
}
/**
* Mixes the two given opaque colours in the proportion specified.
* @param colour1 The first colour to mix, as a 6-digit hexadecimal.
* @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)(opacity * 0xff) << 24);
}
/**
* 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();
}
/**
* Calculates a factor between 0 and 1 that results in a smooth, aesthetically-pleasing animation when used to scale
* things. Mainly for rendering, but can be used anywhere since it's just a mathematical formula.
* @param lifetime The lifetime of the thing being animated, in ticks (if this is negative, disappearing is ignored)
* @param ticksExisted The current age of the thing being animated, in ticks
* @param partialTicks The current partial tick time
* @param startLength The length of the appearing animation, in ticks
* @param endLength The length of the disappearing animation, in ticks
* @return A fraction between 0 and 1, with the value at the very start and end being 0 and the constant middle
* section having a value of 1.
*/
public static float smoothScaleFactor(int lifetime, int ticksExisted, float partialTicks, int startLength, int endLength){
float age = ticksExisted + partialTicks;
float s = MathHelper.clamp(age < startLength || lifetime < 0 ? age/startLength : (lifetime - age) / endLength, 0, 1);
s = (float)Math.pow(s, 0.4); // Smooths the animation
return s;
}
/**
* Tests if the point with the given coordinates lies within the rectangle specified. The check is identical to
* {@link GuiContainer#isPointInRegion(int, int, int, int, int, int)}, but without the adjustment relative to the GUI.
* @param left The minimum x coordinate of the rectangle
* @param top The minimum y coordinate of the rectangle
* @param width The width of the rectangle
* @param height The height of the rectangle
* @param x The x coordinate of the point to test
* @param y The y coordinate of the point to test
* @return True if the given point is in the rectangle, false otherwise
*/
public static boolean isPointInRegion(int left, int top, int width, int height, int x, int y){
return x >= left - 1 && x < left + width + 1 && y >= top - 1 && y < top + height + 1;
}
}
@@ -0,0 +1,77 @@
package electroblob.wizardry.client;
import cpw.mods.fml.client.config.GuiButtonExt;
import cpw.mods.fml.client.config.GuiEditArray;
import cpw.mods.fml.client.config.GuiEditArrayEntries;
import cpw.mods.fml.client.config.GuiSelectString;
import cpw.mods.fml.client.config.GuiUtils;
import cpw.mods.fml.client.config.GuiEditArrayEntries.StringEntry;
import net.minecraft.client.Minecraft;
import net.minecraft.client.renderer.Tessellator;
import net.minecraft.client.resources.I18n;
import net.minecraft.entity.EntityList;
import cpw.mods.fml.client.config.IConfigElement;
public class EntityNameEntry extends StringEntry {
protected final GuiButtonExt btnValue;
protected Object entityClass;
public EntityNameEntry(GuiEditArray owningScreen, GuiEditArrayEntries owningEntryList, IConfigElement configElement, Object value)
{
super(owningScreen, owningEntryList, configElement, value);
this.btnValue = new GuiButtonExt(0, 0, 0, owningEntryList.controlWidth, 18, I18n.format(this.textFieldValue.getText()));
//this.btnValue.enabled = owningScreen.enabled;
}
@Override
public void drawEntry(int slotIndex, int x, int y, int listWidth, int slotHeight, Tessellator tessellator, int mouseX, int mouseY, boolean isSelected)
{
//super.drawEntry(slotIndex, x, y, listWidth, slotHeight, tessellator, mouseX, mouseY, isSelected);
this.btnValue.xPosition = listWidth / 4;
this.btnValue.yPosition = y;
String trans = I18n.format(this.textFieldValue.getText());
if (!trans.equals(this.textFieldValue.getText()))
this.btnValue.displayString = trans;
else
this.btnValue.displayString = this.textFieldValue.getText();
//btnValue.packedFGColour = value ? GuiUtils.getColorCode('2', true) : GuiUtils.getColorCode('4', true);
this.btnValue.drawButton(owningEntryList.mc, mouseX, mouseY);
}
/**
* Returns true if the mouse has been pressed on this control.
*/
@Override
public boolean mousePressed(int index, int x, int y, int mouseEvent, int relativeX, int relativeY)
{
if (this.btnValue.mousePressed(owningEntryList.mc, x, y))
{
btnValue.func_146113_a(owningEntryList.mc.getSoundHandler());
Minecraft.getMinecraft().displayGuiScreen(new GuiSelectString(this.owningScreen, configElement, index, EntityList.classToStringMapping, this.getValue(), true));
owningEntryList.recalculateState();
return true;
}
return super.mousePressed(index, x, y, mouseEvent, relativeX, relativeY);
}
/**
* Fired when the mouse button is released. Arguments: index, x, y, mouseEvent, relativeX, relativeY
*/
@Override
public void mouseReleased(int index, int x, int y, int mouseEvent, int relativeX, int relativeY)
{
this.btnValue.mouseReleased(x, y);
super.mouseReleased(index, x, y, mouseEvent, relativeX, relativeY);
}
@Override
public Object getValue()
{
return this.textFieldValue.getText();
}
}
@@ -0,0 +1,425 @@
package electroblob.wizardry.client;
import codechicken.nei.VisiblityData;
import codechicken.nei.api.INEIGuiHandler;
import codechicken.nei.api.TaggedInventoryArea;
import cpw.mods.fml.common.Optional.Interface;
import cpw.mods.fml.common.Optional.Method;
import cpw.mods.fml.common.network.simpleimpl.IMessage;
import electroblob.wizardry.ExtendedPlayer;
import electroblob.wizardry.SpellGlyphData;
import electroblob.wizardry.WandHelper;
import electroblob.wizardry.Wizardry;
import electroblob.wizardry.item.ItemWand;
import electroblob.wizardry.packet.PacketControlInput;
import electroblob.wizardry.packet.WizardryPacketHandler;
import electroblob.wizardry.spell.Spell;
import electroblob.wizardry.tileentity.ContainerArcaneWorkbench;
import electroblob.wizardry.tileentity.SlotWizardry;
import electroblob.wizardry.tileentity.TileEntityArcaneWorkbench;
import java.awt.Rectangle;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.FontRenderer;
import net.minecraft.client.gui.GuiButton;
import net.minecraft.client.gui.inventory.GuiContainer;
import net.minecraft.client.renderer.RenderHelper;
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.EnumChatFormatting;
import net.minecraft.util.ResourceLocation;
import net.minecraft.util.StatCollector;
import org.lwjgl.input.Keyboard;
import org.lwjgl.opengl.GL11;
import org.lwjgl.opengl.GL12;
@Interface(iface = "codechicken.nei.api.INEIGuiHandler", modid = "NotEnoughItems")
public class GuiArcaneWorkbench extends GuiContainer implements INEIGuiHandler {
private GuiButton applyBtn;
private static final ResourceLocation texture = new ResourceLocation(Wizardry.MODID, "textures/gui/arcane_workbench.png");
private IInventory playerInventory;
private IInventory arcaneWorkbenchInventory;
private static final int tooltipWidth = 164;
private static final int tooltipHeight = 155;
public GuiArcaneWorkbench(InventoryPlayer invPlayer, TileEntityArcaneWorkbench entity) {
super(new ContainerArcaneWorkbench(invPlayer, entity));
this.playerInventory = invPlayer;
this.arcaneWorkbenchInventory = entity;
xSize = 176;
ySize = 220;
}
@Override
public void drawScreen(int p_73863_1_, int p_73863_2_, float p_73863_3_){
if(this.hasWand()){
this.applyBtn.enabled = true;
}else{
this.applyBtn.enabled = false;
}
super.drawScreen(p_73863_1_, p_73863_2_, p_73863_3_);
}
/**
* Draw the foreground layer for the GuiContainer (everything in front of the items)
*/
protected void drawGuiContainerForegroundLayer(int mouseX, int mouseY){
this.fontRendererObj.drawString(this.arcaneWorkbenchInventory.hasCustomInventoryName() ? this.arcaneWorkbenchInventory.getInventoryName() : I18n.format(this.arcaneWorkbenchInventory.getInventoryName()), 8, 6, 4210752);
this.fontRendererObj.drawString(this.playerInventory.hasCustomInventoryName() ? this.playerInventory.getInventoryName() : I18n.format(this.playerInventory.getInventoryName()), 8, this.ySize - 96 + 2, 4210752);
if(this.hasWand()){
ItemStack wand = this.inventorySlots.getSlot(ContainerArcaneWorkbench.WAND_SLOT).getStack();
this.fontRendererObj.drawStringWithShadow("\u00A7f" + wand.getDisplayName(), xSize + 6, 6, 0);
this.fontRendererObj.drawStringWithShadow("\u00A77" + StatCollector.translateToLocal("container.arcaneWorkbench.mana") + " " + (wand.getMaxDamage() - wand.getItemDamage()) + "/" + wand.getMaxDamage(), xSize + 6, 20, 0);
Spell[] spells = WandHelper.getSpells(wand);
int y = 34;
for(Spell spell : spells){
boolean discovered = true;
if(!this.mc.thePlayer.capabilities.isCreativeMode && ExtendedPlayer.get(this.mc.thePlayer) != null){
discovered = ExtendedPlayer.get(this.mc.thePlayer).hasSpellBeenDiscovered(spell);
}
if(discovered){
this.fontRendererObj.drawStringWithShadow(spell.getDisplayNameWithFormatting(), xSize + 16, y, 0);
}else{
this.mc.standardGalacticFontRenderer.drawStringWithShadow("\u00A79" + SpellGlyphData.getGlyphName(spell, this.mc.theWorld), xSize + 16, y, 0);
}
y += 10;
}
if(WandHelper.getTotalUpgrades(wand) > 0){
this.fontRendererObj.drawStringWithShadow("\u00A7f" + StatCollector.translateToLocal("container.arcaneWorkbench.upgrades"), xSize + 6, y + 6, 0);
int x = 0;
y = guiTop + 50 + spells.length*10;
// Wand upgrade tooltips
for(Item item : WandHelper.getSpecialUpgrades()){
int level = WandHelper.getUpgradeLevel(wand, item);
if(level > 0){
// No idea why this is -16 instead of +6, but it works!
if(isPointInRegion(xSize -16 + x, y, 16, 16, mouseX + guiLeft, mouseY + guiTop)){
ItemStack stack = new ItemStack(item, level);
this.drawItemStackTooltip(stack, mouseX - guiLeft, mouseY - guiTop);
}
x += 18;
}
}
}
}
}
@Override
public void drawGuiContainerBackgroundLayer(float f, int mouseX, int mouseY) {
GL11.glColor4f(1F, 1F, 1F, 1F);
Minecraft.getMinecraft().renderEngine.bindTexture(texture);
// Main inventory
drawTexturedModalRect(guiLeft, guiTop, 0, 0, xSize, ySize);
// Changing slots
for(int i=0; i<ContainerArcaneWorkbench.CRYSTAL_SLOT; i++){
Slot slot = this.inventorySlots.getSlot(i);
if(slot.xDisplayPosition >=0 && slot.yDisplayPosition >= 0)
this.drawTexturedModalRect(guiLeft + slot.xDisplayPosition - 10, guiTop + slot.yDisplayPosition - 10,
64, 220, 36, 36);
}
// Tooltip only drawn if there is a wand
if(this.hasWand()){
// Tooltip box
drawTexturedModalRect(guiLeft + xSize, guiTop, xSize, 0, 256 - xSize - 4, ySize);
drawTexturedModalRect(guiLeft + 252, guiTop, xSize + 4, 0, tooltipWidth - 2*(256 - xSize - 4), ySize);
drawTexturedModalRect(guiLeft + xSize + tooltipWidth - (256 - xSize - 4), guiTop, xSize + 4, 0, 256 - xSize - 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.thePlayer.capabilities.isCreativeMode && ExtendedPlayer.get(this.mc.thePlayer) != null){
discovered = ExtendedPlayer.get(this.mc.thePlayer).hasSpellBeenDiscovered(spell);
}
// Renders the little element icon if there is an element
this.drawTexturedModalRect(guiLeft + xSize + 5, guiTop + 34 + 10*i++, discovered ? spell.element.ordinal()*8 : 0, 220, 8, 8);
}
int x = 0;
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);
itemRender.renderItemAndEffectIntoGUI(this.fontRendererObj, this.mc.getTextureManager(), stack, guiLeft + xSize + 6 + x, y);
itemRender.renderItemOverlayIntoGUI(this.fontRendererObj, this.mc.getTextureManager(), stack, guiLeft + xSize + 6 + x, y);
x += 18;
}
}
}
Minecraft.getMinecraft().renderEngine.bindTexture(texture);
for(Object object : this.inventorySlots.inventorySlots){
if(object instanceof Slot){
Slot slot = (Slot)object;
// Tests whether I have added a background texture and if so actually renders it
if(!slot.getHasStack() && slot instanceof SlotWizardry && ((SlotWizardry)slot).backgroundIndex > -1){
// Taken directly from GuiContainer#func_146977_a, with a few changes.
this.zLevel = 100.0F;
itemRender.zLevel = 100.0F;
GL11.glDisable(GL11.GL_LIGHTING);
GL11.glEnable(GL11.GL_BLEND);
this.drawTexturedModalRect(guiLeft + slot.xDisplayPosition, guiTop + slot.yDisplayPosition, ((SlotWizardry)slot).backgroundIndex*16, 240, 16, 16);
GL11.glDisable(GL11.GL_BLEND);
GL11.glEnable(GL11.GL_LIGHTING);
itemRender.zLevel = 0.0F;
this.zLevel = 0.0F;
}
}
}
}
@SuppressWarnings("unchecked")
public void initGui()
{
this.mc.thePlayer.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));
}
public void onGuiClosed()
{
super.onGuiClosed();
Keyboard.enableRepeatEvents(false);
}
/**
* Fired when a control is clicked. This is the equivalent of ActionListener.actionPerformed(ActionEvent e).
*/
protected void actionPerformed(GuiButton par1GuiButton)
{
if (par1GuiButton.enabled)
{
if (par1GuiButton.id == 0)
{
// Packet building
IMessage msg = new PacketControlInput.Message(0);
WizardryPacketHandler.net.sendToServer(msg);
}
}
}
protected boolean isPointInRegion(int par1, int par2, int par3, int par4, int par5, int par6)
{
int k1 = this.width/2 - xSize/2;
int l1 = this.height/2 - this.ySize/2;
par5 -= k1;
par6 -= l1;
return par5 >= par1 - 1 && par5 < par1 + par3 + 1 && par6 >= par2 - 1 && par6 < par2 + par4 + 1;
}
@SuppressWarnings({ "rawtypes", "unchecked" })
protected void drawItemStackTooltip(ItemStack par1ItemStack, int par2, int par3)
{
List list = par1ItemStack.getTooltip(this.mc.thePlayer, this.mc.gameSettings.advancedItemTooltips);
for (int k = 0; k < list.size(); ++k)
{
if (k == 0)
{
list.set(k, par1ItemStack.getRarity().rarityColor + (String)list.get(k));
}
else
{
list.set(k, EnumChatFormatting.GRAY + (String)list.get(k));
}
}
FontRenderer font = par1ItemStack.getItem().getFontRenderer(par1ItemStack);
drawHoveringText(list, par2, par3, (font == null ? fontRendererObj : font));
}
@SuppressWarnings("rawtypes")
protected void drawHoveringText(List par1List, int par2, int par3, FontRenderer font)
{
if (!par1List.isEmpty())
{
GL11.glDisable(GL12.GL_RESCALE_NORMAL);
RenderHelper.disableStandardItemLighting();
GL11.glDisable(GL11.GL_LIGHTING);
GL11.glDisable(GL11.GL_DEPTH_TEST);
int k = 0;
Iterator iterator = par1List.iterator();
while (iterator.hasNext())
{
String s = (String)iterator.next();
int l = font.getStringWidth(s);
if (l > k)
{
k = l;
}
}
int i1 = par2 + 12;
int j1 = par3 - 12;
int k1 = 8;
if (par1List.size() > 1)
{
k1 += 2 + (par1List.size() - 1) * 10;
}
if (i1 + k > this.width)
{
i1 -= 28 + k;
}
if (j1 + k1 + 6 > this.height)
{
j1 = this.height - k1 - 6;
}
this.zLevel = 300.0F;
itemRender.zLevel = 300.0F;
int l1 = -267386864;
this.drawGradientRect(i1 - 3, j1 - 4, i1 + k + 3, j1 - 3, l1, l1);
this.drawGradientRect(i1 - 3, j1 + k1 + 3, i1 + k + 3, j1 + k1 + 4, l1, l1);
this.drawGradientRect(i1 - 3, j1 - 3, i1 + k + 3, j1 + k1 + 3, l1, l1);
this.drawGradientRect(i1 - 4, j1 - 3, i1 - 3, j1 + k1 + 3, l1, l1);
this.drawGradientRect(i1 + k + 3, j1 - 3, i1 + k + 4, j1 + k1 + 3, l1, l1);
int i2 = 1347420415;
int j2 = (i2 & 16711422) >> 1 | i2 & -16777216;
this.drawGradientRect(i1 - 3, j1 - 3 + 1, i1 - 3 + 1, j1 + k1 + 3 - 1, i2, j2);
this.drawGradientRect(i1 + k + 2, j1 - 3 + 1, i1 + k + 3, j1 + k1 + 3 - 1, i2, j2);
this.drawGradientRect(i1 - 3, j1 - 3, i1 + k + 3, j1 - 3 + 1, i2, i2);
this.drawGradientRect(i1 - 3, j1 + k1 + 2, i1 + k + 3, j1 + k1 + 3, j2, j2);
for(int k2 = 0; k2 < par1List.size(); ++k2){
String s1 = (String)par1List.get(k2);
font.drawStringWithShadow(s1, i1, j1, -1);
if(k2 == 0){
j1 += 2;
}
j1 += 10;
}
this.zLevel = 0.0F;
itemRender.zLevel = 0.0F;
GL11.glEnable(GL11.GL_LIGHTING);
GL11.glEnable(GL11.GL_DEPTH_TEST);
RenderHelper.enableStandardItemLighting();
GL11.glEnable(GL12.GL_RESCALE_NORMAL);
}
}
/**
* @return A list of TaggedInventoryAreas that will be used with the savestates.
*/
@Method(modid = "NotEnoughItems")
@Override
public List<TaggedInventoryArea> getInventoryAreas(GuiContainer gui) {
return Collections.emptyList();
}
/**
* NEI will give the specified item to the InventoryRange returned if the player's inventory is full.
* Should not return null, just an empty list
*/
@Method(modid = "NotEnoughItems")
@Override
public Iterable<Integer> getItemSpawnSlots(GuiContainer gui, ItemStack item) {
return Collections.emptyList();
}
/**
* Handles clicks while an itemstack has been dragged from the item panel. Use this to set configurable slots and the like.
* Changes made to the stackSize of the dragged stack will be kept
* @param gui The current gui instance
* @param mousex The x position of the mouse
* @param mousey The y position of the mouse
* @param draggedStack The stack being dragged from the item panel
* @param button The button presed
* @return True if the drag n drop was handled. False to resume processing through other routes. The held stack will be deleted if draggedStack.stackSize == 0
*/
@Method(modid = "NotEnoughItems")
@Override
public boolean handleDragNDrop(GuiContainer gui, int mousex, int mousey, ItemStack draggedStack, int button) {
return false;
}
/**
* Used to prevent the item panel from drawing on top of other gui elements.
* This function will also be called with a 1x1 size rectangle on the mouse position for determining if the given coordinate should override item panel functions such as scrolling
* @param x The x coordinate of the rectangle bounding the slot
* @param y The y coordinate of the rectangle bounding the slot
* @param w The w coordinate of the rectangle bounding the slot
* @param h The h coordinate of the rectangle bounding the slot
* @return true if the item panel slot within the specified rectangle should not be rendered.
*/
@Method(modid = "NotEnoughItems")
@Override
public boolean hideItemPanelSlot(GuiContainer gui, int x, int y, int w, int h) {
if(this.hasWand()) {
Rectangle itemPanel = new Rectangle(x, y, w, h);
Rectangle tooltip = new Rectangle(this.guiLeft + this.xSize, this.guiTop, tooltipWidth, tooltipHeight);
return itemPanel.intersects(tooltip);
}
return false;
}
@Method(modid = "NotEnoughItems")
@Override
public VisiblityData modifyVisiblity(GuiContainer gui, VisiblityData currentVisibility) {
return currentVisibility;
}
private boolean hasWand() {
return this.inventorySlots.getSlot(ContainerArcaneWorkbench.WAND_SLOT).getHasStack()
&& this.inventorySlots.getSlot(ContainerArcaneWorkbench.WAND_SLOT).getStack().getItem() instanceof ItemWand;
}
}
@@ -0,0 +1,73 @@
package electroblob.wizardry.client;
import cpw.mods.fml.relauncher.Side;
import cpw.mods.fml.relauncher.SideOnly;
import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.FontRenderer;
import net.minecraft.client.gui.GuiButton;
import net.minecraft.client.renderer.Tessellator;
import net.minecraft.util.ResourceLocation;
import net.minecraft.util.StatCollector;
import org.lwjgl.opengl.GL11;
@SideOnly(Side.CLIENT)
class GuiButtonApply extends GuiButton {
private static final ResourceLocation texture = new ResourceLocation("wizardry:textures/gui/handbook.png");
public GuiButtonApply(int id, int x, int y){
super(id, x, y, 32, 16, StatCollector.translateToLocal("container.arcaneWorkbench.apply"));
}
/**
* Draws this button to the screen.
*/
public void drawButton(Minecraft minecraft, int par2, int par3){
// Whether the button is highlighted
this.field_146123_n = par2 >= this.xPosition && par3 >= this.yPosition && par2 < this.xPosition + this.width && par3 < this.yPosition + this.height;
int k = 100;
int l = 220;
int colour = 14737632;
if(this.enabled){
if(this.field_146123_n){
k += this.width*2;
colour = 16777120;
}
}else{
k += this.width;
colour = 10526880;
}
this.drawTexturedRect(this.xPosition, this.yPosition, k, l, this.width, this.height, 256, 256);
this.drawCenteredString(minecraft.fontRenderer, this.displayString, this.xPosition + this.width / 2, this.yPosition + (this.height - 8) / 2, colour);
}
/**
* Draws a textured rectangle, taking the size of the image and the bit needed into account.
* @param x The x position of the rectangle
* @param y The y position of the rectangle
* @param u The x position of the top left corner of the section of the image wanted
* @param v The y position of the top left corner of the section of the image wanted
* @param width The width of the section
* @param height The height of the section
* @param textureWidth The width of the actual image.
* @param textureHeight The height of the actual image.
*/
public void drawTexturedRect(int x, int y, int u, int v, int width, int height, int textureWidth, int textureHeight)
{
float f = 1F / (float)textureWidth;
float f1 = 1F / (float)textureHeight;
Tessellator tessellator = Tessellator.instance;
tessellator.startDrawingQuads();
tessellator.addVertexWithUV((double)(x), (double)(y + height), 0, (double)((float)(u) * f), (double)((float)(v + height) * f1));
tessellator.addVertexWithUV((double)(x + width), (double)(y + height), 0, (double)((float)(u + width) * f), (double)((float)(v + height) * f1));
tessellator.addVertexWithUV((double)(x + width), (double)(y), 0, (double)((float)(u + width) * f), (double)((float)(v) * f1));
tessellator.addVertexWithUV((double)(x), (double)(y), 0, (double)((float)(u) * f), (double)((float)(v) * f1));
tessellator.draw();
}
}
@@ -0,0 +1,27 @@
package electroblob.wizardry.client;
import cpw.mods.fml.relauncher.Side;
import cpw.mods.fml.relauncher.SideOnly;
import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.GuiButton;
import net.minecraft.client.renderer.Tessellator;
import net.minecraft.util.ResourceLocation;
import org.lwjgl.opengl.GL11;
@SideOnly(Side.CLIENT)
class GuiButtonInvisible extends GuiButton {
public GuiButtonInvisible(int id, int x, int y, int width, int height){
super(id, x, y, width, height, "");
}
/**
* Draws this button to the screen.
*/
public void drawButton(Minecraft par1Minecraft, int par2, int par3){
this.field_146123_n = par2 >= this.xPosition && par3 >= this.yPosition && par2 < this.xPosition + this.width && par3 < this.yPosition + this.height;
}
}
@@ -0,0 +1,78 @@
package electroblob.wizardry.client;
import cpw.mods.fml.relauncher.Side;
import cpw.mods.fml.relauncher.SideOnly;
import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.GuiButton;
import net.minecraft.client.renderer.Tessellator;
import net.minecraft.util.ResourceLocation;
import org.lwjgl.opengl.GL11;
@SideOnly(Side.CLIENT)
class GuiButtonNextPage extends GuiButton
{
/**
* True for pointing right (next page), false for pointing left (previous page).
*/
private final boolean nextPage;
private static final ResourceLocation texture = new ResourceLocation("wizardry:textures/gui/handbook.png");
public GuiButtonNextPage(int id, int x, int y, boolean par4)
{
super(id, x, y, 23, 13, "");
this.nextPage = par4;
}
/**
* Draws this button to the screen.
*/
public void drawButton(Minecraft par1Minecraft, int par2, int par3)
{
if (this.visible)
{
boolean flag = par2 >= this.xPosition && par3 >= this.yPosition && par2 < this.xPosition + this.width && par3 < this.yPosition + this.height;
GL11.glColor4f(1.0F, 1.0F, 1.0F, 1.0F);
par1Minecraft.getTextureManager().bindTexture(texture);
int k = 0;
int l = 192;
if (flag)
{
k += 23;
}
if (!this.nextPage)
{
l += 13;
}
this.drawTexturedRect(this.xPosition, this.yPosition, k, l, 23, 13, 288, 256);
}
}
/**
* Draws a textured rectangle, taking the size of the image and the bit needed into account.
* @param x The x position of the rectangle
* @param y The y position of the rectangle
* @param u The x position of the top left corner of the section of the image wanted
* @param v The y position of the top left corner of the section of the image wanted
* @param width The width of the section
* @param height The height of the section
* @param textureWidth The width of the actual image.
* @param textureHeight The height of the actual image.
*/
public void drawTexturedRect(int x, int y, int u, int v, int width, int height, int textureWidth, int textureHeight)
{
float f = 1F / (float)textureWidth;
float f1 = 1F / (float)textureHeight;
Tessellator tessellator = Tessellator.instance;
tessellator.startDrawingQuads();
tessellator.addVertexWithUV((double)(x), (double)(y + height), 0, (double)((float)(u) * f), (double)((float)(v + height) * f1));
tessellator.addVertexWithUV((double)(x + width), (double)(y + height), 0, (double)((float)(u + width) * f), (double)((float)(v + height) * f1));
tessellator.addVertexWithUV((double)(x + width), (double)(y), 0, (double)((float)(u + width) * f), (double)((float)(v) * f1));
tessellator.addVertexWithUV((double)(x), (double)(y), 0, (double)((float)(u) * f), (double)((float)(v) * f1));
tessellator.draw();
}
}
@@ -0,0 +1,104 @@
package electroblob.wizardry.client;
import java.util.ArrayList;
import java.util.List;
import cpw.mods.fml.client.config.DummyConfigElement.DummyCategoryElement;
import cpw.mods.fml.client.config.GuiConfig;
import cpw.mods.fml.client.config.GuiConfigEntries;
import cpw.mods.fml.client.config.GuiConfigEntries.CategoryEntry;
import cpw.mods.fml.client.config.IConfigElement;
import electroblob.wizardry.Wizardry;
import net.minecraft.client.gui.GuiScreen;
import net.minecraft.util.StatCollector;
import net.minecraftforge.common.config.ConfigElement;
import net.minecraftforge.common.config.Configuration;
public class GuiConfigWizardry extends GuiConfig {
public GuiConfigWizardry(GuiScreen parent){
super(parent, getConfigEntries(), Wizardry.MODID, false, false, Wizardry.NAME + " - " + StatCollector.translateToLocal("config.title.general"));
//this.titleLine2 = "File location: " + Wizardry.config.getConfigFile().getAbsolutePath();
}
private static List<IConfigElement> getConfigEntries(){
List<IConfigElement> configList = new ArrayList<IConfigElement>(1);
configList.add(new DummyCategoryElement("spellsConfig", "config.category.spells", SpellsCategory.class));
configList.add(new DummyCategoryElement("resistancesConfig", "config.category.resistances", ResistancesCategory.class));
configList.add(new DummyCategoryElement("idsConfig", "config.category.ids", IDsCategory.class));
configList.addAll(new ConfigElement(Wizardry.config.getCategory(Configuration.CATEGORY_GENERAL)).getChildElements());
return configList;
}
/** 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.config.getCategory(Wizardry.SPELLS_CATEGORY))).getChildElements(),
this.owningScreen.modID, Wizardry.SPELLS_CATEGORY, false, false,
Wizardry.NAME + " - " + StatCollector.translateToLocal("config.title.spells"));
spellsMenu.titleLine2 = StatCollector.translateToLocal("config.subtitle.spells");
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.config.getCategory(Wizardry.RESISTANCES_CATEGORY))).getChildElements(),
this.owningScreen.modID, Wizardry.RESISTANCES_CATEGORY, false, false,
Wizardry.NAME + " - " + StatCollector.translateToLocal("config.title.resistances"));
idsMenu.titleLine2 = StatCollector.translateToLocal("config.subtitle.resistances");
return idsMenu;
}
}
/** IDs category of the config gui. */
public static class IDsCategory extends CategoryEntry
{
public IDsCategory(GuiConfig owningScreen, GuiConfigEntries owningEntryList, IConfigElement prop)
{
super(owningScreen, owningEntryList, prop);
}
@Override
protected GuiScreen buildChildScreen()
{
// This GuiConfig object specifies the configID of the object and as such will force-save when it is closed.
// The parent GuiConfig object's entryList will also be refreshed to reflect the changes.
GuiConfig idsMenu = new GuiConfig(this.owningScreen,
(new ConfigElement(Wizardry.config.getCategory(Wizardry.IDS_CATEGORY))).getChildElements(),
this.owningScreen.modID, Wizardry.IDS_CATEGORY, false, false,
Wizardry.NAME + " - " + StatCollector.translateToLocal("config.title.ids"));
idsMenu.titleLine2 = StatCollector.translateToLocal("config.subtitle.ids");
return idsMenu;
}
}
}
@@ -0,0 +1,40 @@
package electroblob.wizardry.client;
import org.lwjgl.opengl.GL11;
import electroblob.wizardry.WizardryGuiHandler;
import net.minecraft.client.gui.inventory.GuiContainer;
import net.minecraft.client.resources.I18n;
import net.minecraft.entity.player.InventoryPlayer;
import net.minecraft.inventory.ContainerWorkbench;
import net.minecraft.util.ResourceLocation;
import net.minecraft.world.World;
/** Crafting table GUI that doesn't require a crafting table container object. */
public class GuiPortableCrafting extends GuiContainer {
private static final ResourceLocation craftingTableGuiTextures = new ResourceLocation("textures/gui/container/crafting_table.png");
public GuiPortableCrafting(InventoryPlayer p_i1084_1_, World p_i1084_2_, int p_i1084_3_, int p_i1084_4_, int p_i1084_5_)
{
super(new ContainerWorkbench(p_i1084_1_, p_i1084_2_, p_i1084_3_, p_i1084_4_, p_i1084_5_));
}
/**
* Draw the foreground layer for the GuiContainer (everything in front of the items)
*/
protected void drawGuiContainerForegroundLayer(int p_146979_1_, int p_146979_2_)
{
this.fontRendererObj.drawString(I18n.format("container.crafting"), 28, 6, 4210752);
this.fontRendererObj.drawString(I18n.format("container.inventory"), 8, this.ySize - 96 + 2, 4210752);
}
protected void drawGuiContainerBackgroundLayer(float p_146976_1_, int p_146976_2_, int p_146976_3_)
{
GL11.glColor4f(1.0F, 1.0F, 1.0F, 1.0F);
this.mc.getTextureManager().bindTexture(craftingTableGuiTextures);
int k = (this.width - this.xSize) / 2;
int l = (this.height - this.ySize) / 2;
this.drawTexturedModalRect(k, l, 0, 0, this.xSize, this.ySize);
}
}
@@ -0,0 +1,153 @@
package electroblob.wizardry.client;
import org.lwjgl.input.Keyboard;
import electroblob.wizardry.EnumTier;
import electroblob.wizardry.ExtendedPlayer;
import electroblob.wizardry.SpellGlyphData;
import electroblob.wizardry.Wizardry;
import electroblob.wizardry.WizardryGuiHandler;
import electroblob.wizardry.WizardryRegistry;
import electroblob.wizardry.spell.Spell;
import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.GuiScreen;
import net.minecraft.client.renderer.Tessellator;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.util.EnumChatFormatting;
import net.minecraft.util.ResourceLocation;
public class GuiSpellBook extends GuiScreen {
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().thePlayer;
boolean discovered = true;
if(Wizardry.discoveryMode && !player.capabilities.isCreativeMode && ExtendedPlayer.get(player) != null
&& !ExtendedPlayer.get(player).hasSpellBeenDiscovered(spell)){
discovered = false;
}
// Draws spell illustration on opposite page, underneath the book so it shows through the hole.
Minecraft.getMinecraft().renderEngine.bindTexture(discovered ? spell.icon : WizardryRegistry.none.icon);
this.drawTexturedRect(xPos + 145, yPos + 20, 0, 0, 128, 128, 128, 128);
Minecraft.getMinecraft().renderEngine.bindTexture(texture);
this.drawTexturedRect(xPos, yPos, 0, 0, xSize, ySize, xSize, 256);
super.drawScreen(par1, par2, par3);
if(discovered){
this.fontRendererObj.drawString(spell.getDisplayName(), xPos+17, yPos+14, 0);
this.fontRendererObj.drawString(spell.type.getDisplayName(), xPos+17, yPos+25, 0x777777);
}else{
this.mc.standardGalacticFontRenderer.drawString(SpellGlyphData.getGlyphName(spell, player.worldObj), xPos+17, yPos+14, 0);
this.mc.standardGalacticFontRenderer.drawString(spell.type.getDisplayName(), xPos+17, yPos+25, 0x777777);
}
this.fontRendererObj.drawString("-------------------", xPos+17, yPos+34, 0);
if(spell.tier == EnumTier.BASIC){
// Basic is usually white but this doesn't show up.
this.fontRendererObj.drawString("Tier: \u00A77" + EnumTier.BASIC.getDisplayName(), xPos+17, yPos+44, 0);
}else{
this.fontRendererObj.drawString("Tier: " + spell.tier.getDisplayNameWithFormatting(), xPos+17, yPos+44, 0);
}
String element = "Element: " + spell.element.colour + spell.element.getDisplayName();
if(!discovered) element = "Element: ?";
this.fontRendererObj.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.fontRendererObj.drawString(manaCost, xPos+17, yPos+68, 0);
if(discovered){
this.fontRendererObj.drawSplitString(spell.getDescription(), xPos+17, yPos+82, 118, 0);
}else{
this.mc.standardGalacticFontRenderer.drawSplitString(SpellGlyphData.getGlyphDescription(spell, player.worldObj), 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);
}
/**
* Draws a textured rectangle, taking the size of the image and the bit needed into account. Client side only, of course.
* @param x The x position of the rectangle
* @param y The y position of the rectangle
* @param u The x position of the top left corner of the section of the image wanted
* @param v The y position of the top left corner of the section of the image wanted
* @param width The width of the section
* @param height The height of the section
* @param textureWidth The width of the actual image.
* @param textureHeight The height of the actual image.
*/
public static void drawTexturedRect(int x, int y, int u, int v, int width, int height, int textureWidth, int textureHeight)
{
float f = 1F / (float)textureWidth;
float f1 = 1F / (float)textureHeight;
Tessellator tessellator = Tessellator.instance;
tessellator.startDrawingQuads();
tessellator.addVertexWithUV((double)(x), (double)(y + height), 0, (double)((float)(u) * f), (double)((float)(v + height) * f1));
tessellator.addVertexWithUV((double)(x + width), (double)(y + height), 0, (double)((float)(u + width) * f), (double)((float)(v + height) * f1));
tessellator.addVertexWithUV((double)(x + width), (double)(y), 0, (double)((float)(u + width) * f), (double)((float)(v) * f1));
tessellator.addVertexWithUV((double)(x), (double)(y), 0, (double)((float)(u) * f), (double)((float)(v) * f1));
tessellator.draw();
}
}
@@ -0,0 +1,169 @@
package electroblob.wizardry.client;
import java.util.List;
import org.lwjgl.opengl.GL11;
import cpw.mods.fml.common.eventhandler.SubscribeEvent;
import electroblob.wizardry.ExtendedPlayer;
import electroblob.wizardry.SpellGlyphData;
import electroblob.wizardry.WandHelper;
import electroblob.wizardry.Wizardry;
import electroblob.wizardry.WizardryRegistry;
import electroblob.wizardry.item.ItemWand;
import electroblob.wizardry.spell.None;
import electroblob.wizardry.spell.Spell;
import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.FontRenderer;
import net.minecraft.client.gui.Gui;
import net.minecraft.client.renderer.Tessellator;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.nbt.NBTTagIntArray;
import net.minecraft.util.ResourceLocation;
import net.minecraftforge.client.event.RenderGameOverlayEvent;
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.thePlayer;
if(player.getHeldItem() != null && player.getHeldItem().getItem() instanceof ItemWand){
int width = event.resolution.getScaledWidth();
int height = event.resolution.getScaledHeight();
Spell spell = WandHelper.getCurrentSpell(player.getHeldItem());
int cooldown = WandHelper.getCurrentCooldown(player.getHeldItem());
float cooldownMultiplier = 1.0f - WandHelper.getUpgradeLevel(player.getHeldItem(), Wizardry.cooldownUpgrade)*Wizardry.COOLDOWN_REDUCTION_PER_LEVEL;
if(player.isPotionActive(Wizardry.fontOfMana)){
// Dividing by this rather than setting it takes upgrades and font of mana into account simultaneously
cooldownMultiplier /= 2 + player.getActivePotionEffect(Wizardry.fontOfMana).getAmplifier();
}
// Coordinates of the top left corner of the HUD.
int left = 0;
int top = 0;
boolean mirror = false;
if(Wizardry.spellHUDPosition.equals("Bottom left")){
left = 0;
top = height-36;
}else if(Wizardry.spellHUDPosition.equals("Top left")){
left = 0;
top = 0;
}else if(Wizardry.spellHUDPosition.equals("Top right")){
left = width-128;
top = 0;
mirror = true;
}else if(Wizardry.spellHUDPosition.equals("Bottom right")){
left = width-128;
top = height-36;
mirror = true;
}
boolean discovered = true;
if(!player.capabilities.isCreativeMode && ExtendedPlayer.get(player) != null){
discovered = ExtendedPlayer.get(player).hasSpellBeenDiscovered(spell);
}
if(event.type == 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(Wizardry.arcaneJammer) ? "\u00A78" : spell.element.colour;
if(!discovered) colour = "\u00A79";
String spellName = discovered ? spell.getDisplayName() : SpellGlyphData.getGlyphName(spell, player.worldObj);
FontRenderer font = discovered ? this.mc.fontRenderer : this.mc.standardGalacticFontRenderer;
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 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.type == RenderGameOverlayEvent.ElementType.HOTBAR){
GL11.glPushMatrix();
GL11.glEnable(GL11.GL_BLEND);
GL11.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA);
GL11.glColor3f(1.0f, 1.0f, 1.0f);
this.mc.renderEngine.bindTexture(hudTexture);
// Background of spell hud
this.drawTexturedModalRect(left, top, 0, mirror ? 36 : 0, 128, 36);
// Cooldown bar
if(cooldown > 0){
this.drawTexturedModalRect(mirror ? left+5 : left+41, height-8, 128, 6, 82, 6);
int l = (int)(((double)(spell.cooldown * cooldownMultiplier - cooldown)
/ (double)(spell.cooldown * cooldownMultiplier)) * 82);
this.drawTexturedModalRect(mirror ? left+5 : left+41, height-8, 128, 0, l, 6);
}
// Spell illustration
this.mc.renderEngine.bindTexture(discovered ? spell.icon : WizardryRegistry.none.icon);
this.drawTexturedRect(mirror ? left+94 : left+2, top+2, 0, 0, 32, 32, 32, 32);
GL11.glDisable(GL11.GL_BLEND);
GL11.glPopMatrix();
}
}
}
/**
* Draws a textured rectangle, taking the size of the image and the bit needed into account. Client side only, of course.
* @param x The x position of the rectangle
* @param y The y position of the rectangle
* @param u The x position of the top left corner of the section of the image wanted
* @param v The y position of the top left corner of the section of the image wanted
* @param width The width of the section
* @param height The height of the section
* @param textureWidth The width of the actual image.
* @param textureHeight The height of the actual image.
*/
public static void drawTexturedRect(int x, int y, int u, int v, int width, int height, int textureWidth, int textureHeight)
{
float f = 1F / (float)textureWidth;
float f1 = 1F / (float)textureHeight;
Tessellator tessellator = Tessellator.instance;
tessellator.startDrawingQuads();
tessellator.addVertexWithUV((double)(x), (double)(y + height), 0, (double)((float)(u) * f), (double)((float)(v + height) * f1));
tessellator.addVertexWithUV((double)(x + width), (double)(y + height), 0, (double)((float)(u + width) * f), (double)((float)(v + height) * f1));
tessellator.addVertexWithUV((double)(x + width), (double)(y), 0, (double)((float)(u + width) * f), (double)((float)(v) * f1));
tessellator.addVertexWithUV((double)(x), (double)(y), 0, (double)((float)(u) * f), (double)((float)(v) * f1));
tessellator.draw();
}
}
@@ -0,0 +1,945 @@
package electroblob.wizardry.client;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import org.apache.commons.io.Charsets;
import org.lwjgl.input.Keyboard;
import org.lwjgl.opengl.GL11;
import org.lwjgl.opengl.GL12;
import electroblob.wizardry.EnumElement;
import electroblob.wizardry.EnumTier;
import electroblob.wizardry.Wizardry;
import electroblob.wizardry.WizardryGuiHandler;
import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.FontRenderer;
import net.minecraft.client.gui.GuiButton;
import net.minecraft.client.gui.GuiScreen;
import net.minecraft.client.renderer.RenderHelper;
import net.minecraft.client.renderer.Tessellator;
import net.minecraft.client.renderer.entity.RenderItem;
import net.minecraft.client.resources.LanguageManager;
import net.minecraft.init.Blocks;
import net.minecraft.init.Items;
import net.minecraft.item.ItemStack;
import net.minecraft.util.EnumChatFormatting;
import net.minecraft.util.ResourceLocation;
import net.minecraft.util.StatCollector;
public class GuiWizardHandbook extends GuiScreen {
private GuiButtonNextPage nextPageBtn, prevPageBtn;
private int xSize, ySize;
private int pageNumber = 0;
private static final int pageWidth = 120;
public static final ResourceLocation regularHandbook = new ResourceLocation(Wizardry.MODID, "textures/gui/handbook.png");
public static final ResourceLocation ore = new ResourceLocation(Wizardry.MODID, "textures/gui/ore_picture.png");
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 ArrayList<ArrayList<String>> text;
private ArrayList<Section> sections;
private int guiPage, imagePage;
public GuiWizardHandbook() {
super();
xSize = 288;
ySize = 180;
}
/**
* Draws the screen and all the components in it.
*/
public void drawScreen(int mouseX, int mouseY, float par3){
int xPos = this.width/2 - xSize/2;
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);
}
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);
drawTexturedRect(this.imagePage % 2 == 1 ? xPos + 17 : this.width/2 + 7, yPos + 80, 0, 0, 64, 64, 64, 64);
Minecraft.getMinecraft().renderEngine.bindTexture(crystal);
drawTexturedStretchedRect(this.imagePage % 2 == 1 ? xPos + 17 + 64 : this.width/2 + 7 + 62, yPos + 80, 0, 0, 64, 64, 1, 1);
}
this.fontRendererObj.drawString("" + (pageNumber*2 + 1), xPos + xSize/4 - 3, yPos + ySize - 20, 0);
this.fontRendererObj.drawString("" + (pageNumber*2 + 2), xPos + 3*xSize/4 - 5, yPos + ySize - 20, 0);
super.drawScreen(mouseX, mouseY, par3);
int lineNumber = 0;
if(pageNumber == 1){
int row = 2;
for(Section s : sections){
s.drawContents();
row++;
}
}else{
for(Section s : sections){
s.hideButton();
}
}
for(String paragraph : text.get(pageNumber*2)){
this.fontRendererObj.drawSplitString(paragraph, xPos + 17, yPos + 14 + lineNumber*this.fontRendererObj.FONT_HEIGHT, pageWidth, 0);
ArrayList list = new ArrayList(this.fontRendererObj.listFormattedStringToWidth(paragraph, this.pageWidth));
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 + pageWidth/2 - this.fontRendererObj.getStringWidth(paragraph)/2;
this.fontRendererObj.drawSplitString(paragraph, startX, yPos + 14 + lineNumber*this.fontRendererObj.FONT_HEIGHT, pageWidth, 0);
}else{
this.fontRendererObj.drawSplitString(paragraph, this.width/2 + 7, yPos + 14 + lineNumber*this.fontRendererObj.FONT_HEIGHT, pageWidth, 0);
}
ArrayList list = new ArrayList(this.fontRendererObj.listFormattedStringToWidth(paragraph, this.pageWidth));
lineNumber += list.size();
}
}
/*
for(int i=0;i<(Math.min(text[pageNumber].length, 14));i++){
this.fontRendererObj.drawString(text[pageNumber][i], xPos + 16, yPos + 14 + i*10, 0);
}
for(int i=0;i<(Math.min(text[pageNumber].length - 14, 14));i++){
this.fontRendererObj.drawString(text[pageNumber][i+14], this.width/2 + 5, yPos + 14 + i*10, 0);
}
*/
ItemStack[][] craftingGrid;
ItemStack craftingResult;
// Tooltips are rendered after recipes to prevent tooltips on the left appearing behind items on the right.
if(pageNumber == (sections.get(sections.size()-1).pageNumber-1)/2){
craftingGrid = new ItemStack[3][3];
craftingGrid[0][0] = new ItemStack(Items.gold_nugget);
craftingGrid[1][0] = new ItemStack(Blocks.carpet, 1, 10);
craftingGrid[2][0] = new ItemStack(Items.gold_nugget);
craftingGrid[0][1] = new ItemStack(Wizardry.magicCrystal);
craftingGrid[1][1] = new ItemStack(Blocks.lapis_block);
craftingGrid[2][1] = new ItemStack(Wizardry.magicCrystal);
craftingGrid[0][2] = new ItemStack(Blocks.stone);
craftingGrid[1][2] = new ItemStack(Blocks.stone);
craftingGrid[2][2] = new ItemStack(Blocks.stone);
craftingResult = new ItemStack(Wizardry.arcaneWorkbench);
this.renderCraftingRecipe(xPos + 23, yPos + 39, mouseX, mouseY, craftingGrid, craftingResult);
craftingGrid = new ItemStack[3][3];
craftingGrid[2][0] = new ItemStack(Wizardry.magicCrystal);
craftingGrid[1][1] = new ItemStack(Items.stick);
craftingGrid[0][2] = new ItemStack(Items.gold_nugget);
craftingResult = new ItemStack(Wizardry.magicWand);
this.renderCraftingRecipe(xPos + 23, yPos + 98, mouseX, mouseY, craftingGrid, craftingResult);
craftingGrid = new ItemStack[3][3];
craftingGrid[1][0] = new ItemStack(Wizardry.magicCrystal);
craftingGrid[0][1] = new ItemStack(Wizardry.magicCrystal);
craftingGrid[1][1] = new ItemStack(Items.book);
craftingGrid[1][2] = new ItemStack(Wizardry.magicCrystal);
craftingGrid[2][1] = new ItemStack(Wizardry.magicCrystal);
craftingResult = new ItemStack(Wizardry.spellBook, 1, 1);
this.renderCraftingRecipe(xPos + 156, yPos + 39, mouseX, mouseY, craftingGrid, craftingResult);
craftingGrid = new ItemStack[3][3];
craftingGrid[0][0] = new ItemStack(Items.book);
craftingGrid[1][0] = new ItemStack(Wizardry.magicCrystal);
craftingResult = new ItemStack(Wizardry.wizardHandbook);
this.renderCraftingRecipe(xPos + 156, yPos + 98, mouseX, mouseY, craftingGrid, craftingResult);
}else if(pageNumber == (sections.get(sections.size()-1).pageNumber-1)/2 + 1){
craftingGrid = new ItemStack[3][3];
craftingGrid[0][0] = new ItemStack(Wizardry.crystalFlower);
craftingResult = new ItemStack(Wizardry.magicCrystal, 2);
this.renderCraftingRecipe(xPos + 23, yPos + 39, mouseX, mouseY, craftingGrid, craftingResult);
craftingGrid = new ItemStack[3][3];
craftingGrid[0][0] = new ItemStack(Wizardry.magicCrystal);
craftingGrid[1][0] = new ItemStack(Wizardry.magicCrystal);
craftingGrid[2][0] = new ItemStack(Wizardry.magicCrystal);
craftingGrid[0][1] = new ItemStack(Wizardry.magicCrystal);
craftingGrid[1][1] = new ItemStack(Items.glass_bottle);
craftingGrid[2][1] = new ItemStack(Wizardry.magicCrystal);
craftingGrid[0][2] = new ItemStack(Wizardry.magicCrystal);
craftingGrid[1][2] = new ItemStack(Wizardry.magicCrystal);
craftingGrid[2][2] = new ItemStack(Wizardry.magicCrystal);
craftingResult = new ItemStack(Wizardry.manaFlask);
this.renderCraftingRecipe(xPos + 23, yPos + 98, mouseX, mouseY, craftingGrid, craftingResult);
craftingGrid = new ItemStack[3][3];
craftingGrid[1][0] = new ItemStack(Blocks.stone);
craftingGrid[0][1] = new ItemStack(Blocks.stone);
craftingGrid[1][1] = new ItemStack(Wizardry.magicCrystal);
craftingGrid[1][2] = new ItemStack(Blocks.stone);
craftingGrid[2][1] = new ItemStack(Blocks.stone);
craftingResult = new ItemStack(Wizardry.transportationStone, 2);
this.renderCraftingRecipe(xPos + 156, yPos + 39, mouseX, mouseY, craftingGrid, craftingResult);
craftingGrid = new ItemStack[3][3];
craftingGrid[1][0] = new ItemStack(Items.string);
craftingGrid[0][1] = new ItemStack(Items.string);
craftingGrid[1][1] = new ItemStack(Wizardry.magicCrystal);
craftingGrid[1][2] = new ItemStack(Items.string);
craftingGrid[2][1] = new ItemStack(Items.string);
craftingResult = new ItemStack(Wizardry.magicSilk, 2);
this.renderCraftingRecipe(xPos + 156, yPos + 98, mouseX, mouseY, craftingGrid, craftingResult);
}else if(pageNumber == (sections.get(sections.size()-1).pageNumber-1)/2 + 2){
craftingGrid = new ItemStack[3][3];
craftingGrid[0][0] = new ItemStack(Wizardry.magicSilk);
craftingGrid[1][0] = new ItemStack(Wizardry.magicSilk);
craftingGrid[2][0] = new ItemStack(Wizardry.magicSilk);
craftingGrid[0][1] = new ItemStack(Wizardry.magicSilk);
craftingGrid[2][1] = new ItemStack(Wizardry.magicSilk);
craftingResult = new ItemStack(Wizardry.wizardHat);
this.renderCraftingRecipe(xPos + 23, yPos + 39, mouseX, mouseY, craftingGrid, craftingResult);
craftingGrid = new ItemStack[3][3];
craftingGrid[0][0] = new ItemStack(Wizardry.magicSilk);
craftingGrid[2][0] = new ItemStack(Wizardry.magicSilk);
craftingGrid[0][1] = new ItemStack(Wizardry.magicSilk);
craftingGrid[1][1] = new ItemStack(Wizardry.magicSilk);
craftingGrid[2][1] = new ItemStack(Wizardry.magicSilk);
craftingGrid[0][2] = new ItemStack(Wizardry.magicSilk);
craftingGrid[1][2] = new ItemStack(Wizardry.magicSilk);
craftingGrid[2][2] = new ItemStack(Wizardry.magicSilk);
craftingResult = new ItemStack(Wizardry.wizardRobe);
this.renderCraftingRecipe(xPos + 23, yPos + 98, mouseX, mouseY, craftingGrid, craftingResult);
craftingGrid = new ItemStack[3][3];
craftingGrid[0][0] = new ItemStack(Wizardry.magicSilk);
craftingGrid[1][0] = new ItemStack(Wizardry.magicSilk);
craftingGrid[2][0] = new ItemStack(Wizardry.magicSilk);
craftingGrid[0][1] = new ItemStack(Wizardry.magicSilk);
craftingGrid[2][1] = new ItemStack(Wizardry.magicSilk);
craftingGrid[0][2] = new ItemStack(Wizardry.magicSilk);
craftingGrid[2][2] = new ItemStack(Wizardry.magicSilk);
craftingResult = new ItemStack(Wizardry.wizardLeggings);
this.renderCraftingRecipe(xPos + 156, yPos + 39, mouseX, mouseY, craftingGrid, craftingResult);
craftingGrid = new ItemStack[3][3];
craftingGrid[0][0] = new ItemStack(Wizardry.magicSilk);
craftingGrid[2][0] = new ItemStack(Wizardry.magicSilk);
craftingGrid[0][1] = new ItemStack(Wizardry.magicSilk);
craftingGrid[2][1] = new ItemStack(Wizardry.magicSilk);
craftingResult = new ItemStack(Wizardry.wizardBoots);
this.renderCraftingRecipe(xPos + 156, yPos + 98, mouseX, mouseY, craftingGrid, craftingResult);
}else if(pageNumber == (sections.get(sections.size()-1).pageNumber-1)/2 + 3){
if(Wizardry.useAlternateScrollRecipe){
craftingGrid = new ItemStack[3][3];
craftingGrid[0][0] = new ItemStack(Items.paper);
craftingGrid[1][0] = new ItemStack(Items.string);
craftingGrid[2][0] = new ItemStack(Wizardry.magicCrystal);
craftingResult = new ItemStack(Wizardry.blankScroll);
this.renderCraftingRecipe(xPos + 23, yPos + 39, mouseX, mouseY, craftingGrid, craftingResult);
}else{
craftingGrid = new ItemStack[3][3];
craftingGrid[0][0] = new ItemStack(Items.paper);
craftingGrid[1][0] = new ItemStack(Items.string);
craftingResult = new ItemStack(Wizardry.blankScroll);
this.renderCraftingRecipe(xPos + 23, yPos + 39, mouseX, mouseY, craftingGrid, craftingResult);
}
if(Wizardry.firebombIsCraftable){
craftingGrid = new ItemStack[3][3];
craftingGrid[0][0] = new ItemStack(Items.blaze_powder);
craftingGrid[1][0] = new ItemStack(Items.blaze_powder);
craftingGrid[0][1] = new ItemStack(Items.glass_bottle);
craftingGrid[1][1] = new ItemStack(Items.gunpowder);
craftingResult = new ItemStack(Wizardry.firebomb, 3);
this.renderCraftingRecipe(xPos + 23, yPos + 98, mouseX, mouseY, craftingGrid, craftingResult);
}
if(Wizardry.poisonBombIsCraftable){
craftingGrid = new ItemStack[3][3];
craftingGrid[0][0] = new ItemStack(Items.spider_eye);
craftingGrid[1][0] = new ItemStack(Items.spider_eye);
craftingGrid[0][1] = new ItemStack(Items.glass_bottle);
craftingGrid[1][1] = new ItemStack(Items.gunpowder);
craftingResult = new ItemStack(Wizardry.poisonBomb, 3);
this.renderCraftingRecipe(xPos + 156, yPos + 39, mouseX, mouseY, craftingGrid, craftingResult);
}
if(Wizardry.smokeBombIsCraftable){
craftingGrid = new ItemStack[3][3];
craftingGrid[0][0] = new ItemStack(Items.coal);
craftingGrid[1][0] = new ItemStack(Items.coal);
craftingGrid[0][1] = new ItemStack(Items.glass_bottle);
craftingGrid[1][1] = new ItemStack(Items.gunpowder);
craftingResult = new ItemStack(Wizardry.smokeBomb, 3);
this.renderCraftingRecipe(xPos + 156, yPos + 98, mouseX, mouseY, craftingGrid, craftingResult);
}
}
if(pageNumber == (sections.get(sections.size()-1).pageNumber-1)/2){
craftingGrid = new ItemStack[3][3];
craftingGrid[0][0] = new ItemStack(Items.gold_nugget);
craftingGrid[1][0] = new ItemStack(Blocks.carpet, 1, 10);
craftingGrid[2][0] = new ItemStack(Items.gold_nugget);
craftingGrid[0][1] = new ItemStack(Wizardry.magicCrystal);
craftingGrid[1][1] = new ItemStack(Blocks.lapis_block);
craftingGrid[2][1] = new ItemStack(Wizardry.magicCrystal);
craftingGrid[0][2] = new ItemStack(Blocks.stone);
craftingGrid[1][2] = new ItemStack(Blocks.stone);
craftingGrid[2][2] = new ItemStack(Blocks.stone);
craftingResult = new ItemStack(Wizardry.arcaneWorkbench);
this.renderCraftingTooltips(xPos + 23, yPos + 39, mouseX, mouseY, craftingGrid, craftingResult);
craftingGrid = new ItemStack[3][3];
craftingGrid[2][0] = new ItemStack(Wizardry.magicCrystal);
craftingGrid[1][1] = new ItemStack(Items.stick);
craftingGrid[0][2] = new ItemStack(Items.gold_nugget);
craftingResult = new ItemStack(Wizardry.magicWand);
this.renderCraftingTooltips(xPos + 23, yPos + 98, mouseX, mouseY, craftingGrid, craftingResult);
craftingGrid = new ItemStack[3][3];
craftingGrid[1][0] = new ItemStack(Wizardry.magicCrystal);
craftingGrid[0][1] = new ItemStack(Wizardry.magicCrystal);
craftingGrid[1][1] = new ItemStack(Items.book);
craftingGrid[1][2] = new ItemStack(Wizardry.magicCrystal);
craftingGrid[2][1] = new ItemStack(Wizardry.magicCrystal);
craftingResult = new ItemStack(Wizardry.spellBook, 1, 1);
this.renderCraftingTooltips(xPos + 156, yPos + 39, mouseX, mouseY, craftingGrid, craftingResult);
craftingGrid = new ItemStack[3][3];
craftingGrid[0][0] = new ItemStack(Items.book);
craftingGrid[1][0] = new ItemStack(Wizardry.magicCrystal);
craftingResult = new ItemStack(Wizardry.wizardHandbook);
this.renderCraftingTooltips(xPos + 156, yPos + 98, mouseX, mouseY, craftingGrid, craftingResult);
}else if(pageNumber == (sections.get(sections.size()-1).pageNumber-1)/2 + 1){
craftingGrid = new ItemStack[3][3];
craftingGrid[0][0] = new ItemStack(Wizardry.crystalFlower);
craftingResult = new ItemStack(Wizardry.magicCrystal, 2);
this.renderCraftingTooltips(xPos + 23, yPos + 39, mouseX, mouseY, craftingGrid, craftingResult);
craftingGrid = new ItemStack[3][3];
craftingGrid[0][0] = new ItemStack(Wizardry.magicCrystal);
craftingGrid[1][0] = new ItemStack(Wizardry.magicCrystal);
craftingGrid[2][0] = new ItemStack(Wizardry.magicCrystal);
craftingGrid[0][1] = new ItemStack(Wizardry.magicCrystal);
craftingGrid[1][1] = new ItemStack(Items.glass_bottle);
craftingGrid[2][1] = new ItemStack(Wizardry.magicCrystal);
craftingGrid[0][2] = new ItemStack(Wizardry.magicCrystal);
craftingGrid[1][2] = new ItemStack(Wizardry.magicCrystal);
craftingGrid[2][2] = new ItemStack(Wizardry.magicCrystal);
craftingResult = new ItemStack(Wizardry.manaFlask);
this.renderCraftingTooltips(xPos + 23, yPos + 98, mouseX, mouseY, craftingGrid, craftingResult);
craftingGrid = new ItemStack[3][3];
craftingGrid[1][0] = new ItemStack(Blocks.stone);
craftingGrid[0][1] = new ItemStack(Blocks.stone);
craftingGrid[1][1] = new ItemStack(Wizardry.magicCrystal);
craftingGrid[1][2] = new ItemStack(Blocks.stone);
craftingGrid[2][1] = new ItemStack(Blocks.stone);
craftingResult = new ItemStack(Wizardry.transportationStone, 2);
this.renderCraftingTooltips(xPos + 156, yPos + 39, mouseX, mouseY, craftingGrid, craftingResult);
craftingGrid = new ItemStack[3][3];
craftingGrid[1][0] = new ItemStack(Items.string);
craftingGrid[0][1] = new ItemStack(Items.string);
craftingGrid[1][1] = new ItemStack(Wizardry.magicCrystal);
craftingGrid[1][2] = new ItemStack(Items.string);
craftingGrid[2][1] = new ItemStack(Items.string);
craftingResult = new ItemStack(Wizardry.magicSilk, 2);
this.renderCraftingTooltips(xPos + 156, yPos + 98, mouseX, mouseY, craftingGrid, craftingResult);
}else if(pageNumber == (sections.get(sections.size()-1).pageNumber-1)/2 + 2){
craftingGrid = new ItemStack[3][3];
craftingGrid[0][0] = new ItemStack(Wizardry.magicSilk);
craftingGrid[1][0] = new ItemStack(Wizardry.magicSilk);
craftingGrid[2][0] = new ItemStack(Wizardry.magicSilk);
craftingGrid[0][1] = new ItemStack(Wizardry.magicSilk);
craftingGrid[2][1] = new ItemStack(Wizardry.magicSilk);
craftingResult = new ItemStack(Wizardry.wizardHat);
this.renderCraftingTooltips(xPos + 23, yPos + 39, mouseX, mouseY, craftingGrid, craftingResult);
craftingGrid = new ItemStack[3][3];
craftingGrid[0][0] = new ItemStack(Wizardry.magicSilk);
craftingGrid[2][0] = new ItemStack(Wizardry.magicSilk);
craftingGrid[0][1] = new ItemStack(Wizardry.magicSilk);
craftingGrid[1][1] = new ItemStack(Wizardry.magicSilk);
craftingGrid[2][1] = new ItemStack(Wizardry.magicSilk);
craftingGrid[0][2] = new ItemStack(Wizardry.magicSilk);
craftingGrid[1][2] = new ItemStack(Wizardry.magicSilk);
craftingGrid[2][2] = new ItemStack(Wizardry.magicSilk);
craftingResult = new ItemStack(Wizardry.wizardRobe);
this.renderCraftingTooltips(xPos + 23, yPos + 98, mouseX, mouseY, craftingGrid, craftingResult);
craftingGrid = new ItemStack[3][3];
craftingGrid[0][0] = new ItemStack(Wizardry.magicSilk);
craftingGrid[1][0] = new ItemStack(Wizardry.magicSilk);
craftingGrid[2][0] = new ItemStack(Wizardry.magicSilk);
craftingGrid[0][1] = new ItemStack(Wizardry.magicSilk);
craftingGrid[2][1] = new ItemStack(Wizardry.magicSilk);
craftingGrid[0][2] = new ItemStack(Wizardry.magicSilk);
craftingGrid[2][2] = new ItemStack(Wizardry.magicSilk);
craftingResult = new ItemStack(Wizardry.wizardLeggings);
this.renderCraftingTooltips(xPos + 156, yPos + 39, mouseX, mouseY, craftingGrid, craftingResult);
craftingGrid = new ItemStack[3][3];
craftingGrid[0][0] = new ItemStack(Wizardry.magicSilk);
craftingGrid[2][0] = new ItemStack(Wizardry.magicSilk);
craftingGrid[0][1] = new ItemStack(Wizardry.magicSilk);
craftingGrid[2][1] = new ItemStack(Wizardry.magicSilk);
craftingResult = new ItemStack(Wizardry.wizardBoots);
this.renderCraftingTooltips(xPos + 156, yPos + 98, mouseX, mouseY, craftingGrid, craftingResult);
}else if(pageNumber == (sections.get(sections.size()-1).pageNumber-1)/2 + 3){
if(Wizardry.useAlternateScrollRecipe){
craftingGrid = new ItemStack[3][3];
craftingGrid[0][0] = new ItemStack(Items.paper);
craftingGrid[1][0] = new ItemStack(Items.string);
craftingGrid[2][0] = new ItemStack(Wizardry.magicCrystal);
craftingResult = new ItemStack(Wizardry.blankScroll);
this.renderCraftingTooltips(xPos + 23, yPos + 39, mouseX, mouseY, craftingGrid, craftingResult);
}else{
craftingGrid = new ItemStack[3][3];
craftingGrid[0][0] = new ItemStack(Items.paper);
craftingGrid[1][0] = new ItemStack(Items.string);
craftingResult = new ItemStack(Wizardry.blankScroll);
this.renderCraftingTooltips(xPos + 23, yPos + 39, mouseX, mouseY, craftingGrid, craftingResult);
}
if(Wizardry.firebombIsCraftable){
craftingGrid = new ItemStack[3][3];
craftingGrid[0][0] = new ItemStack(Items.blaze_powder);
craftingGrid[1][0] = new ItemStack(Items.blaze_powder);
craftingGrid[0][1] = new ItemStack(Items.glass_bottle);
craftingGrid[1][1] = new ItemStack(Items.gunpowder);
craftingResult = new ItemStack(Wizardry.firebomb, 3);
this.renderCraftingTooltips(xPos + 23, yPos + 98, mouseX, mouseY, craftingGrid, craftingResult);
}
if(Wizardry.poisonBombIsCraftable){
craftingGrid = new ItemStack[3][3];
craftingGrid[0][0] = new ItemStack(Items.spider_eye);
craftingGrid[1][0] = new ItemStack(Items.spider_eye);
craftingGrid[0][1] = new ItemStack(Items.glass_bottle);
craftingGrid[1][1] = new ItemStack(Items.gunpowder);
craftingResult = new ItemStack(Wizardry.poisonBomb, 3);
this.renderCraftingTooltips(xPos + 156, yPos + 39, mouseX, mouseY, craftingGrid, craftingResult);
}
if(Wizardry.smokeBombIsCraftable){
craftingGrid = new ItemStack[3][3];
craftingGrid[0][0] = new ItemStack(Items.coal);
craftingGrid[1][0] = new ItemStack(Items.coal);
craftingGrid[0][1] = new ItemStack(Items.glass_bottle);
craftingGrid[1][1] = new ItemStack(Items.gunpowder);
craftingResult = new ItemStack(Wizardry.smokeBomb, 3);
this.renderCraftingTooltips(xPos + 156, yPos + 98, mouseX, mouseY, craftingGrid, craftingResult);
}
}
}
private void renderCraftingRecipe(int xPos, int yPos, int mouseX, int mouseY, ItemStack[][] craftingGrid, ItemStack craftingResult) {
int guiLeft = this.width/2 - xSize/2;
int guiTop = this.height/2 - this.ySize/2;
GL11.glPushMatrix();
RenderHelper.enableGUIStandardItemLighting();
GL11.glDisable(GL11.GL_LIGHTING);
GL11.glEnable(GL12.GL_RESCALE_NORMAL);
GL11.glEnable(GL11.GL_COLOR_MATERIAL);
GL11.glEnable(GL11.GL_LIGHTING);
itemRender.zLevel = 100.0F;
for(int i=0; i<craftingGrid.length; i++){
for(int j=0; j<craftingGrid[i].length; j++){
if(craftingGrid[i][j] != null){
itemRender.renderItemAndEffectIntoGUI(this.fontRendererObj, this.mc.getTextureManager(), craftingGrid[i][j], xPos + 18*i, yPos + 18*j);
itemRender.renderItemOverlayIntoGUI(this.fontRendererObj, this.mc.getTextureManager(), craftingGrid[i][j], xPos + 18*i, yPos + 18*j);
}
}
}
if(craftingResult != null){
itemRender.renderItemAndEffectIntoGUI(this.fontRendererObj, this.mc.getTextureManager(), craftingResult, xPos + 86, yPos + 18);
itemRender.renderItemOverlayIntoGUI(this.fontRendererObj, this.mc.getTextureManager(), craftingResult, xPos + 86, yPos + 18);
}
GL11.glPopMatrix();
GL11.glEnable(GL11.GL_LIGHTING);
GL11.glEnable(GL11.GL_DEPTH_TEST);
RenderHelper.enableStandardItemLighting();
}
private void renderCraftingTooltips(int xPos, int yPos, int mouseX, int mouseY, ItemStack[][] craftingGrid, ItemStack craftingResult) {
int guiLeft = this.width/2 - xSize/2;
int guiTop = this.height/2 - this.ySize/2;
GL11.glPushMatrix();
RenderHelper.enableGUIStandardItemLighting();
GL11.glDisable(GL11.GL_LIGHTING);
GL11.glEnable(GL12.GL_RESCALE_NORMAL);
GL11.glEnable(GL11.GL_COLOR_MATERIAL);
itemRender.zLevel = 0.0F;
GL11.glDisable(GL11.GL_LIGHTING);
for(int i=0; i<craftingGrid.length; i++){
for(int j=0; j<craftingGrid[i].length; j++){
if(craftingGrid[i][j] != null && isPointInRegion(xPos + 18*i, yPos + 18*j, 16, 16, mouseX + guiLeft, mouseY + guiTop)){
this.drawItemStackTooltip(craftingGrid[i][j], mouseX, mouseY);
}
}
}
if(craftingResult != null && isPointInRegion(xPos + 86, yPos + 18, 16, 16, mouseX + guiLeft, mouseY + guiTop)){
this.drawItemStackTooltip(craftingResult, mouseX, mouseY);
}
GL11.glPopMatrix();
GL11.glEnable(GL11.GL_LIGHTING);
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(this.nextPageBtn = new GuiButtonNextPage(nextButtonId++, this.width/2 + this.xSize/2 - 22 - 23, this.height/2 + this.ySize/2 - 10 - 13, true));
this.buttonList.add(this.prevPageBtn = new GuiButtonNextPage(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:texts/handbook_" + Minecraft.getMinecraft().getLanguageManager().getCurrentLanguage().getLanguageCode() + ".txt";
try {
bufferedreader = new BufferedReader(new InputStreamReader(this.mc.getResourceManager().getResource(new ResourceLocation(textFilepath)).getInputStream(), Charsets.UTF_8));
} catch (IOException e){
System.out.println("Warning: wizard handbook text file missing for the current language. Using default (English - US) instead.");
textFilepath = "wizardry:texts/handbook_en_US.txt";
try {
bufferedreader = new BufferedReader(new InputStreamReader(this.mc.getResourceManager().getResource(new ResourceLocation(textFilepath)).getInputStream(), Charsets.UTF_8));
} catch (IOException x){
System.err.println("Couldn't find file: wizardry:assets/texts/handbook_en_US.txt. The file may be missing; please try re-downloading and reinstalling wizardry.");
x.printStackTrace();
}
}
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.fontRendererObj.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.nextSpell.getKeyCode()));
paragraph = paragraph.replaceAll("PREVIOUS_SPELL_KEY", Keyboard.getKeyName(ClientProxy.previousSpell.getKeyCode()));
paragraph = paragraph.replaceAll("MANA_PER_CRYSTAL_MINUS_30", "" + (Wizardry.MANA_PER_CRYSTAL - 30));
paragraph = paragraph.replaceAll("MANA_PER_CRYSTAL", "" + Wizardry.MANA_PER_CRYSTAL);
paragraph = paragraph.replaceAll("BASIC_MAX_CHARGE", "" + EnumTier.BASIC.maxCharge);
paragraph = paragraph.replaceAll("APPRENTICE_MAX_CHARGE", "" + EnumTier.APPRENTICE.maxCharge);
paragraph = paragraph.replaceAll("ADVANCED_MAX_CHARGE", "" + EnumTier.ADVANCED.maxCharge);
paragraph = paragraph.replaceAll("MASTER_MAX_CHARGE", "" + EnumTier.MASTER.maxCharge);
paragraph = paragraph.replaceAll("BASIC_COLOUR", "\u00A77");
paragraph = paragraph.replaceAll("APPRENTICE_COLOUR", EnumTier.APPRENTICE.colour);
paragraph = paragraph.replaceAll("ADVANCED_COLOUR", EnumTier.ADVANCED.colour);
paragraph = paragraph.replaceAll("MASTER_COLOUR", EnumTier.MASTER.colour);
paragraph = paragraph.replaceAll("FIRE_COLOUR", EnumElement.FIRE.colour);
paragraph = paragraph.replaceAll("ICE_COLOUR", EnumElement.ICE.colour);
paragraph = paragraph.replaceAll("LIGHTNING_COLOUR", EnumElement.LIGHTNING.colour);
paragraph = paragraph.replaceAll("NECROMANCY_COLOUR", EnumElement.NECROMANCY.colour);
paragraph = paragraph.replaceAll("EARTH_COLOUR", EnumElement.EARTH.colour);
paragraph = paragraph.replaceAll("SORCERY_COLOUR", EnumElement.SORCERY.colour);
paragraph = paragraph.replaceAll("HEALING_COLOUR", EnumElement.HEALING.colour);
paragraph = paragraph.replaceAll("RESET_COLOUR", "\u00A70");
paragraph = paragraph.replaceAll("VERSION", Wizardry.VERSION);
int linesInParagraph = this.fontRendererObj.listFormattedStringToWidth(paragraph, this.pageWidth).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 strings = this.fontRendererObj.listFormattedStringToWidth(paragraph, this.pageWidth);
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){
System.err.println("Something went wrong reading file: " + textFilepath + ". The file may be damaged; please try re-downloading and reinstalling wizardry.");
e.printStackTrace();
}
}
/*
for(ArrayList<String> a : text){
for(String s : a){
System.out.println(s);
}
}
*/
}
private class Section {
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.this.pageWidth, GuiWizardHandbook.this.fontRendererObj.FONT_HEIGHT));
}
void hideButton(){
((GuiButton)GuiWizardHandbook.this.buttonList.get(buttonId)).visible = false;
}
void drawContents(){
((GuiButton)GuiWizardHandbook.this.buttonList.get(buttonId)).visible = true;
GuiWizardHandbook.this.fontRendererObj.drawString(name, x, y, ((GuiButton)GuiWizardHandbook.this.buttonList.get(buttonId)).func_146115_a() ? 0xdd4c1d : 0);
int nameWidth = GuiWizardHandbook.this.fontRendererObj.getStringWidth(name);
String dotsAndNumber = " " + this.pageNumber;
while(GuiWizardHandbook.this.fontRendererObj.getStringWidth(dotsAndNumber) < GuiWizardHandbook.pageWidth - nameWidth - 2){
dotsAndNumber = "." + dotsAndNumber;
}
GuiWizardHandbook.this.fontRendererObj.drawString(dotsAndNumber, x + GuiWizardHandbook.pageWidth - GuiWizardHandbook.this.fontRendererObj.getStringWidth(dotsAndNumber), y, 0);
}
}
public void onGuiClosed()
{
super.onGuiClosed();
Keyboard.enableRepeatEvents(false);
}
/**
* Fired when a control is clicked. This is the equivalent of ActionListener.actionPerformed(ActionEvent e).
*/
protected void actionPerformed(GuiButton par1GuiButton){
if(par1GuiButton.enabled){
if(par1GuiButton.id == 0){
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;
}
protected void drawItemStackTooltip(ItemStack par1ItemStack, int par2, int par3)
{
List list = par1ItemStack.getTooltip(this.mc.thePlayer, this.mc.gameSettings.advancedItemTooltips);
for (int k = 0; k < list.size(); ++k)
{
if (k == 0)
{
list.set(k, par1ItemStack.getRarity().rarityColor + (String)list.get(k));
}
else
{
list.set(k, EnumChatFormatting.GRAY + (String)list.get(k));
}
}
FontRenderer font = par1ItemStack.getItem().getFontRenderer(par1ItemStack);
drawHoveringText(list, par2, par3, (font == null ? fontRendererObj : font));
}
protected void drawHoveringText(List par1List, int par2, int par3, FontRenderer font)
{
if (!par1List.isEmpty())
{
GL11.glDisable(GL12.GL_RESCALE_NORMAL);
RenderHelper.disableStandardItemLighting();
GL11.glDisable(GL11.GL_LIGHTING);
GL11.glDisable(GL11.GL_DEPTH_TEST);
int k = 0;
Iterator iterator = par1List.iterator();
while (iterator.hasNext())
{
String s = (String)iterator.next();
int l = font.getStringWidth(s);
if (l > k)
{
k = l;
}
}
int i1 = par2 + 12;
int j1 = par3 - 12;
int k1 = 8;
if (par1List.size() > 1)
{
k1 += 2 + (par1List.size() - 1) * 10;
}
if (i1 + k > this.width)
{
i1 -= 28 + k;
}
if (j1 + k1 + 6 > this.height)
{
j1 = this.height - k1 - 6;
}
this.zLevel = 300.0F;
itemRender.zLevel = 300.0F;
int l1 = -267386864;
this.drawGradientRect(i1 - 3, j1 - 4, i1 + k + 3, j1 - 3, l1, l1);
this.drawGradientRect(i1 - 3, j1 + k1 + 3, i1 + k + 3, j1 + k1 + 4, l1, l1);
this.drawGradientRect(i1 - 3, j1 - 3, i1 + k + 3, j1 + k1 + 3, l1, l1);
this.drawGradientRect(i1 - 4, j1 - 3, i1 - 3, j1 + k1 + 3, l1, l1);
this.drawGradientRect(i1 + k + 3, j1 - 3, i1 + k + 4, j1 + k1 + 3, l1, l1);
int i2 = 1347420415;
int j2 = (i2 & 16711422) >> 1 | i2 & -16777216;
this.drawGradientRect(i1 - 3, j1 - 3 + 1, i1 - 3 + 1, j1 + k1 + 3 - 1, i2, j2);
this.drawGradientRect(i1 + k + 2, j1 - 3 + 1, i1 + k + 3, j1 + k1 + 3 - 1, i2, j2);
this.drawGradientRect(i1 - 3, j1 - 3, i1 + k + 3, j1 - 3 + 1, i2, i2);
this.drawGradientRect(i1 - 3, j1 + k1 + 2, i1 + k + 3, j1 + k1 + 3, j2, j2);
for(int k2 = 0; k2 < par1List.size(); ++k2){
String s1 = (String)par1List.get(k2);
font.drawStringWithShadow(s1, i1, j1, -1);
if(k2 == 0){
j1 += 2;
}
j1 += 10;
}
this.zLevel = 0.0F;
itemRender.zLevel = 0.0F;
GL11.glEnable(GL11.GL_LIGHTING);
GL11.glEnable(GL11.GL_DEPTH_TEST);
RenderHelper.enableStandardItemLighting();
GL11.glEnable(GL12.GL_RESCALE_NORMAL);
}
}
/**
* Draws a textured rectangle, taking the size of the image and the bit needed into account.
* @param x The x position of the rectangle
* @param y The y position of the rectangle
* @param u The x position of the top left corner of the section of the image wanted
* @param v The y position of the top left corner of the section of the image wanted
* @param width The width of the section
* @param height The height of the section
* @param textureWidth The width of the actual image.
* @param textureHeight The height of the actual image.
*/
public void drawTexturedRect(int x, int y, int u, int v, int width, int height, int textureWidth, int textureHeight)
{
float f = 1F / (float)textureWidth;
float f1 = 1F / (float)textureHeight;
Tessellator tessellator = Tessellator.instance;
tessellator.startDrawingQuads();
tessellator.addVertexWithUV((double)(x), (double)(y + height), 0, (double)((float)(u) * f), (double)((float)(v + height) * f1));
tessellator.addVertexWithUV((double)(x + width), (double)(y + height), 0, (double)((float)(u + width) * f), (double)((float)(v + height) * f1));
tessellator.addVertexWithUV((double)(x + width), (double)(y), 0, (double)((float)(u + width) * f), (double)((float)(v) * f1));
tessellator.addVertexWithUV((double)(x), (double)(y), 0, (double)((float)(u) * f), (double)((float)(v) * f1));
tessellator.draw();
}
/**
* Draws a textured rectangle, stretching the section of the image to fit the size given.
* @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 void drawTexturedStretchedRect(int x, int y, int u, int v, int finalWidth, int finalHeight, int width, int height)
{
Tessellator tessellator = Tessellator.instance;
tessellator.startDrawingQuads();
tessellator.addVertexWithUV((double)(x), (double)(y + finalHeight), 0, u, v + height);
tessellator.addVertexWithUV((double)(x + finalWidth), (double)(y + finalHeight), 0, u + width, v + height);
tessellator.addVertexWithUV((double)(x + finalWidth), (double)(y), 0, u + width, v);
tessellator.addVertexWithUV((double)(x), (double)(y), 0, u, v);
tessellator.draw();
}
}
@@ -1,96 +1,97 @@
package electroblob.wizardry.client;
import java.util.ArrayList;
import java.util.List;
import cpw.mods.fml.relauncher.Side;
import cpw.mods.fml.relauncher.SideOnly;
import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.FontRenderer;
import net.minecraft.client.renderer.texture.TextureManager;
import net.minecraft.client.settings.GameSettings;
import net.minecraft.util.ResourceLocation;
/**
* 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)
/** 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)
public class MixedFontRenderer extends FontRenderer {
public MixedFontRenderer(GameSettings p_i1035_1_, ResourceLocation p_i1035_2_, TextureManager p_i1035_3_,
boolean p_i1035_4_){
boolean p_i1035_4_) {
super(p_i1035_1_, p_i1035_2_, p_i1035_3_, p_i1035_4_);
}
@Override
public int drawString(String string, float x, float y, int colour, boolean shadow){
public int drawString(String string, int x, int y, int colour, boolean shadow){
int l = 0;
boolean sga = false;
while(string.indexOf('#') > -1){
String section = string.substring(0, string.indexOf('#'));
if(sga){
l += Minecraft.getMinecraft().standardGalacticFontRenderer.drawString(section, x, y, colour, shadow);
l += Minecraft.getMinecraft().standardGalacticFontRenderer.drawString(section, x, y, colour, shadow);
x += Minecraft.getMinecraft().standardGalacticFontRenderer.getStringWidth(section);
}else{
l += Minecraft.getMinecraft().fontRenderer.drawString(section, x, y, colour, shadow);
}else{
l += Minecraft.getMinecraft().fontRenderer.drawString(section, x, y, colour, shadow);
x += Minecraft.getMinecraft().fontRenderer.getStringWidth(section);
}
}
string = string.substring(string.indexOf('#') + 1);
sga = !sga;
}
if(sga){
l += Minecraft.getMinecraft().standardGalacticFontRenderer.drawString(string, x, y, colour, shadow);
}else{
l += Minecraft.getMinecraft().fontRenderer.drawString(string, x, y, colour, shadow);
}
return l;
}
l += Minecraft.getMinecraft().standardGalacticFontRenderer.drawString(string, x, y, colour, shadow);
}else{
l += Minecraft.getMinecraft().fontRenderer.drawString(string, x, y, colour, shadow);
}
return l;
}
@Override
public int getStringWidth(String string){
int l = 0;
boolean sga = false;
while(string.indexOf('#') > -1){
String section = string.substring(0, string.indexOf('#'));
if(sga){
l += Minecraft.getMinecraft().standardGalacticFontRenderer.getStringWidth(section);
}else{
l += Minecraft.getMinecraft().fontRenderer.getStringWidth(section);
}
l += Minecraft.getMinecraft().standardGalacticFontRenderer.getStringWidth(section);
}else{
l += Minecraft.getMinecraft().fontRenderer.getStringWidth(section);
}
string = string.substring(string.indexOf('#') + 1);
sga = !sga;
}
if(sga){
l += Minecraft.getMinecraft().standardGalacticFontRenderer.getStringWidth(string);
}else{
l += Minecraft.getMinecraft().fontRenderer.getStringWidth(string);
}
return l;
l += Minecraft.getMinecraft().standardGalacticFontRenderer.getStringWidth(string);
}else{
l += Minecraft.getMinecraft().fontRenderer.getStringWidth(string);
}
return l;
}
// This doesn't work the same way yet
@Override
public void drawSplitString(String string, int x, int y, int width, int colour){
public void drawSplitString(String string, int x, int y, int width, int colour){
if(string.contains("#")){
Minecraft.getMinecraft().standardGalacticFontRenderer.drawSplitString(string.substring(1), x, y, width,
colour);
}else{
Minecraft.getMinecraft().fontRenderer.drawSplitString(string, x, y, width, colour);
}
}
Minecraft.getMinecraft().standardGalacticFontRenderer.drawSplitString(string.substring(1), x, y, width, colour);
}else{
Minecraft.getMinecraft().fontRenderer.drawSplitString(string, x, y, width, colour);
}
}
}
@@ -0,0 +1,55 @@
package electroblob.wizardry.client;
import cpw.mods.fml.relauncher.Side;
import cpw.mods.fml.relauncher.SideOnly;
import net.minecraft.client.audio.MovingSound;
import net.minecraft.entity.Entity;
import net.minecraft.util.MathHelper;
import net.minecraft.util.ResourceLocation;
@SideOnly(Side.CLIENT)
public class MovingSoundEntity extends MovingSound
{
private final Entity source;
private float pitch = 0.0F;
public MovingSoundEntity(Entity entity, String soundName, float volume, float pitch, boolean repeat)
{
super(new ResourceLocation(soundName));
this.source = entity;
this.repeat = repeat;
this.volume = volume;
this.field_147663_c = pitch;
this.field_147665_h = 0;
}
/**
* Updates the JList with a new model.
*/
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_double(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.pitch = MathHelper.clamp_float(this.pitch + 0.0025F, 0.0F, 1.0F);
this.volume = 0.0F + MathHelper.clamp_float(f, 0.0F, 0.5F) * 0.7F;
}
else
{
//this.pitch = 0.0F;
//this.volume = 0.0F;
}
}
}
}
@@ -1,42 +0,0 @@
package electroblob.wizardry.client;
import electroblob.wizardry.Wizardry;
import net.minecraft.client.Minecraft;
import net.minecraftforge.fml.common.Mod.EventBusSubscriber;
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
import net.minecraftforge.fml.common.gameevent.TickEvent;
import net.minecraftforge.fml.relauncher.Side;
@EventBusSubscriber(Side.CLIENT)
public class ScreenShakeHandler {
/** The remaining time for which the screen shake effect will be active. */
private static int screenShakeCounter = 0;
private static final float SHAKINESS = 0.5f;
/** Starts the client-side screen shake effect. */
public static void shakeScreen(float intensity){
if(Wizardry.settings.screenShake){
screenShakeCounter = (int)(intensity / SHAKINESS);
Minecraft.getMinecraft().player.rotationPitch -= intensity * 0.5f; // Start halfway down
}
}
@SubscribeEvent
public static void onPlayerTickEvent(TickEvent.PlayerTickEvent event){
if(event.player == Minecraft.getMinecraft().player && event.phase == TickEvent.Phase.END){
if(Wizardry.settings.screenShake){
if(screenShakeCounter > 0){
float magnitude = screenShakeCounter * SHAKINESS;
Minecraft.getMinecraft().player.rotationPitch += screenShakeCounter % 2 == 0 ? magnitude : -magnitude;
screenShakeCounter--;
}
}else{
screenShakeCounter = 0;
}
}
}
}
@@ -1,231 +1,793 @@
package electroblob.wizardry.client;
import electroblob.wizardry.client.renderer.overlay.RenderBlinkEffect;
import electroblob.wizardry.data.DispenserCastingData;
import electroblob.wizardry.data.SpellEmitterData;
import electroblob.wizardry.data.WizardData;
import electroblob.wizardry.item.*;
import electroblob.wizardry.potion.PotionSlowTime;
import electroblob.wizardry.registry.WizardryItems;
import electroblob.wizardry.registry.WizardryPotions;
import electroblob.wizardry.spell.Possession;
import electroblob.wizardry.spell.SixthSense;
import electroblob.wizardry.spell.SlowTime;
import electroblob.wizardry.spell.Transience;
import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.GuiMainMenu;
import net.minecraft.client.gui.ScaledResolution;
import net.minecraft.client.gui.inventory.GuiContainer;
import net.minecraft.client.renderer.BufferBuilder;
import net.minecraft.client.renderer.GlStateManager;
import net.minecraft.client.renderer.Tessellator;
import net.minecraft.client.renderer.vertex.DefaultVertexFormats;
import net.minecraft.entity.EntityLiving;
import net.minecraft.item.Item;
import net.minecraft.tileentity.TileEntity;
import net.minecraft.tileentity.TileEntityDispenser;
import net.minecraft.util.ResourceLocation;
import net.minecraft.world.World;
import net.minecraftforge.client.event.*;
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 org.lwjgl.opengl.GL11;
import java.util.ArrayList;
import java.util.List;
import cpw.mods.fml.common.eventhandler.SubscribeEvent;
import cpw.mods.fml.common.network.simpleimpl.IMessage;
import electroblob.wizardry.ExtendedPlayer;
import electroblob.wizardry.WandHelper;
import electroblob.wizardry.Wizardry;
import electroblob.wizardry.WizardryUtilities;
import electroblob.wizardry.entity.EntityShield;
import electroblob.wizardry.item.ItemSpectralArmour;
import electroblob.wizardry.item.ItemSpectralBow;
import electroblob.wizardry.item.ItemWand;
import electroblob.wizardry.packet.PacketControlInput;
import electroblob.wizardry.packet.WizardryPacketHandler;
import electroblob.wizardry.spell.Flight;
import electroblob.wizardry.spell.ShadowWard;
import electroblob.wizardry.spell.Shield;
import electroblob.wizardry.spell.Spell;
import net.minecraft.client.Minecraft;
import net.minecraft.client.renderer.OpenGlHelper;
import net.minecraft.client.renderer.Tessellator;
import net.minecraft.client.renderer.entity.RenderManager;
import net.minecraft.entity.EntityLivingBase;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.item.ItemStack;
import net.minecraft.util.MovingObjectPosition;
import net.minecraft.util.ResourceLocation;
import net.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;
/**
* General-purpose client-side event handler for things that don't fit anywhere else or groups of related behaviours
* that are better kept together.
*
* @author Electroblob
* @since Wizardry 1.0
*/
//@SideOnly(Side.CLIENT)
@Mod.EventBusSubscriber(Side.CLIENT)
public final class WizardryClientEventHandler {
/** Event handler responsible for all client-side only events, mostly rendering.
* @since Wizardry 1.0 */
public class WizardryClientEventHandler {
private WizardryClientEventHandler(){} // No instances!
// Tick Events
// ===============================================================================================================
private static final ResourceLocation shieldTexture = new ResourceLocation("wizardry:textures/entity/shield.png");
private static final ResourceLocation wingTexture = new ResourceLocation("wizardry:textures/entity/wing.png");
private static final ResourceLocation shadowWardTexture = new ResourceLocation("wizardry:textures/entity/shadow_ward.png");
private static final ResourceLocation sixthSenseTexture = new ResourceLocation("wizardry:textures/entity/sixth_sense.png");
private static final ResourceLocation sixthSenseOverlayTexture = new ResourceLocation("wizardry:textures/gui/sixth_sense_overlay.png");
private static final ResourceLocation frostOverlayTexture = new ResourceLocation("wizardry:textures/gui/frost_overlay.png");
private static final ResourceLocation pointerTexture = new ResourceLocation("wizardry:textures/entity/pointer.png");
private static final ResourceLocation targetPointerTexture = new ResourceLocation("wizardry:textures/entity/target_pointer.png");
@SubscribeEvent
public static void onPlayerTickEvent(TickEvent.PlayerTickEvent event){
public void onRenderPlayerArmourEvent(RenderPlayerEvent.SetArmorModel event){
if(event.player == Minecraft.getMinecraft().player && event.phase == TickEvent.Phase.END){
ItemStack stack = event.entityPlayer.inventory.armorInventory[event.slot];
// 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){ // IntelliJ is wrong, this can be 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())) RenderBlinkEffect.playBlinkEffect();
Minecraft.getMinecraft().entityRenderer.stopUseShader();
}
}
if(stack != null && stack.getItem() instanceof ItemSpectralArmour){
GL11.glEnable(GL11.GL_BLEND);
}
}
// The classes referenced here used to handle their own client tick events, but since they're not client-only
// classes, that crashed the dedicated server...
// Shift-scrolling to change spells
@SubscribeEvent
public static void onClientTickEvent(TickEvent.ClientTickEvent event){
public void onMouseEvent(MouseEvent event){
if(Minecraft.getMinecraft().inGameHasFocus && Minecraft.getMinecraft().thePlayer.getHeldItem() != null
&& Minecraft.getMinecraft().thePlayer.getHeldItem().getItem() instanceof ItemWand && event.dwheel != 0
&& Minecraft.getMinecraft().thePlayer.isSneaking() && Wizardry.enableShiftScrolling){
if(event.phase == TickEvent.Phase.END && !net.minecraft.client.Minecraft.getMinecraft().isGamePaused()){
World world = net.minecraft.client.Minecraft.getMinecraft().world;
if(world == null) return;
// Somehow this was throwing a CME, I have no idea why so I'm just going to cheat and copy the list
List<TileEntity> tileEntities = new ArrayList<>(world.loadedTileEntityList);
for(TileEntity tileentity : tileEntities){
if(tileentity instanceof TileEntityDispenser){
if(DispenserCastingData.get((TileEntityDispenser)tileentity) != null){
DispenserCastingData.get((TileEntityDispenser)tileentity).update();
}
}
}
SpellEmitterData.update(world);
PotionSlowTime.cleanUpEntities(world);
}
}
// Input and Controls
// ===============================================================================================================
@SubscribeEvent
public static void onMouseEvent(MouseEvent event){
// Prevents the player looking around when paralysed
if(Minecraft.getMinecraft().player.isPotionActive(WizardryPotions.paralysis)
&& Minecraft.getMinecraft().inGameHasFocus){
event.setCanceled(true);
Minecraft.getMinecraft().player.prevRotationYaw = 0;
Minecraft.getMinecraft().player.prevRotationPitch = 0;
Minecraft.getMinecraft().player.rotationYaw = 0;
Minecraft.getMinecraft().player.rotationPitch = 0;
}
}
if(event.dwheel > 0){
// Packet building
IMessage msg = new PacketControlInput.Message(2);
WizardryPacketHandler.net.sendToServer(msg);
// 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;
}
if(ItemArtefact.isArtefactActive(event.getEntityPlayer(), WizardryItems.charm_move_speed)
&& event.getEntityPlayer().isHandActive()
&& event.getEntityPlayer().getActiveItemStack().getItem() instanceof ISpellCastingItem){
// Normally speed is set to 20% when using items, this makes it 80%
event.getMovementInput().moveStrafe *= 4;
event.getMovementInput().moveForward *= 4;
}
}
// Rendering
// ===============================================================================================================
@SubscribeEvent
public static void onRenderHandEvent(RenderHandEvent event){
// Hide the player's empty hand in first-person when possessing
EntityLiving victim = Possession.getPossessee(Minecraft.getMinecraft().player);
if(victim != null){
victim.rotationYawHead = Minecraft.getMinecraft().player.rotationYaw;
if(Minecraft.getMinecraft().player.getHeldItemMainhand().isEmpty()){
event.setCanceled(true);
}else if(event.dwheel < 0){
// Packet building
IMessage msg = new PacketControlInput.Message(1);
WizardryPacketHandler.net.sendToServer(msg);
}
}
}
@SubscribeEvent
public static void onFOVUpdateEvent(FOVUpdateEvent event){
public void onFOVUpdateEvent(FOVUpdateEvent event){
// Bow zoom. Taken directly from AbstractClientPlayer so it works exactly like vanilla.
if(event.getEntity().isHandActive()){
// Bow zoom. Taken directly from EntityPlayerSP so it works exactly like vanilla.
if (event.entity.isUsingItem() && event.entity.getItemInUse().getItem() instanceof ItemSpectralBow)
{
int i = event.entity.getItemInUseDuration();
float f1 = (float)i / 20.0F;
Item item = event.getEntity().getActiveItemStack().getItem();
if (f1 > 1.0F)
{
f1 = 1.0F;
}
else
{
f1 *= f1;
}
if(item instanceof ItemSpectralBow || item instanceof ItemFlamecatcher){
event.newfov *= 1.0F - f1 * 0.15F;
}
}
int maxUseTicks = event.getEntity().getItemInUseMaxCount();
// Third person
@SubscribeEvent
public void onRenderPlayerEvent(RenderPlayerEvent.Specials.Post event){
renderShieldIfActive(event.entityPlayer);
renderWingsIfActive(event.entityPlayer, event.partialRenderTick);
renderShadowWardIfActive(event.entityPlayer);
}
float maxUseSeconds = (float)maxUseTicks / (item instanceof ItemFlamecatcher ? ItemFlamecatcher.DRAW_TIME : 20);
// First person
@SubscribeEvent
public void onRenderWorldLastEvent(RenderWorldLastEvent event){
// Now only fires in first person.
if(Minecraft.getMinecraft().gameSettings.thirdPersonView == 0){
renderShieldFirstPerson(Minecraft.getMinecraft().thePlayer);
renderShadowWardFirstPerson(Minecraft.getMinecraft().thePlayer);
}
}
if(maxUseSeconds > 1.0F){
maxUseSeconds = 1.0F;
}else{
maxUseSeconds = maxUseSeconds * maxUseSeconds;
@SubscribeEvent
public void onRenderLivingEvent(RenderLivingEvent.Post event){
/*
// Frost effect
if(event.entity.isPotionActive(Wizardry.frost)){
GL11.glPushMatrix();
GL11.glDisable(GL11.GL_TEXTURE_2D);
GL11.glEnable(GL11.GL_BLEND);
GL11.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA);
float someScalingFactor = 0.0625f;
float yaw = event.entity.prevRotationYaw;
//int brightness = event.entity.getBrightnessForRender(0);
//int j = brightness % 65536;
//int k = brightness / 65536;
//OpenGlHelper.setLightmapTextureCoords(OpenGlHelper.lightmapTexUnit, (float)j / 1.0F, (float)k / 1.0F);
//GL11.glColor4f(1.0F, 1.0F, 1.0F, 1.0F);
GL11.glTranslated(event.x, event.y, event.z);
GL11.glRotatef(-yaw + 180, 0F, 1F, 0F);
Render render = RenderManager.instance.getEntityRenderObject(event.entity);
RendererLivingEntity renderliving = event.renderer;
// Reflection
try {
Timer timer = ReflectionHelper.getPrivateValue(Minecraft.class, Minecraft.getMinecraft(), "timer");
// Chooses the appropriate zombie model, normal or villager
// Fixed by moving before the model fields are accessed
if(render instanceof RenderZombie && event.entity instanceof EntityZombie){
// The second argument is never used...
ReflectionHelper.findMethod(RenderZombie.class, (RenderZombie)render, new String[]{"func_82427_a"}, EntityZombie.class)
.invoke(renderliving, (EntityZombie)event.entity);
}
// Turns out that java automatically infers the type parameter T in this method from the type
// I am assigning the returned value to. Neat!
ModelBase mainModel = ReflectionHelper.getPrivateValue(RendererLivingEntity.class, renderliving, "mainModel");
mainModel.isRiding = event.entity.isRiding();
mainModel.isChild = event.entity.isChild();
event.setNewfov(event.getFov() * 1.0F - maxUseSeconds * 0.15F);
GL11.glEnable(GL12.GL_RESCALE_NORMAL);
GL11.glScalef(-1.0F, -1.0F, 1.0F);
// The second argument is never used...
ReflectionHelper.findMethod(RendererLivingEntity.class, renderliving, new String[]{"preRenderCallback"}, EntityLivingBase.class, float.class)
.invoke(renderliving, event.entity, someScalingFactor);
// Why is this -1.5f? No idea!
GL11.glTranslatef(0, -1.5f, 0);
float f6 = event.entity.prevLimbSwingAmount + (event.entity.limbSwingAmount - event.entity.prevLimbSwingAmount) * timer.renderPartialTicks;
float f7 = event.entity.limbSwing - event.entity.limbSwingAmount * (1.0F - timer.renderPartialTicks);
if (event.entity.isChild())
{
f7 *= 3.0F;
}
if (f6 > 1.0F)
{
f6 = 1.0F;
}
mainModel.setLivingAnimations(event.entity, f7, f6, timer.renderPartialTicks);
GL11.glEnable(GL11.GL_ALPHA_TEST);
GL11.glColor4f(0.5f, 0.7f, 1, 0.5f);
mainModel.render(event.entity, f7, f6, 0, 0, 0, someScalingFactor);
GL11.glDepthMask(true);
// 'Pokemon' exception handling... Because why not?!
} catch (Exception e) {
System.err.println("Something went very wrong! Error while rendering frost effect:");
e.printStackTrace();
}
GL11.glDisable(GL11.GL_ALPHA_TEST);
GL11.glDisable(GL11.GL_BLEND);
GL11.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA);
GL11.glDisable(GL12.GL_RESCALE_NORMAL);
GL11.glEnable(GL11.GL_TEXTURE_2D);
GL11.glPopMatrix();
}
*/
Minecraft mc = Minecraft.getMinecraft();
ExtendedPlayer properties = ExtendedPlayer.get(mc.thePlayer);
MovingObjectPosition rayTrace = WizardryUtilities.standardEntityRayTrace(mc.theWorld, mc.thePlayer, 16);
// Target selection pointer
if(mc.thePlayer.isSneaking() && mc.thePlayer.getHeldItem() != null
&& mc.thePlayer.getHeldItem().getItem() instanceof ItemWand && rayTrace != null
&& rayTrace.entityHit instanceof EntityLivingBase && rayTrace.entityHit == event.entity
&& properties != null && properties.selectedMinion != null){
Tessellator tessellator = Tessellator.instance;
GL11.glPushMatrix();
GL11.glDisable(GL11.GL_CULL_FACE);
GL11.glDisable(GL11.GL_LIGHTING);
OpenGlHelper.setLightmapTextureCoords(OpenGlHelper.lightmapTexUnit, 240f, 240f);
// Disabling depth test allows it to be seen through everything.
GL11.glDisable(GL11.GL_DEPTH_TEST);
GL11.glColor4f(1, 1, 1, 1);
GL11.glTranslated(event.x, event.y + event.entity.height + 0.5, event.z);
// This counteracts the reverse rotation behaviour when in front f5 view.
// Fun fact: this is a bug with vanilla too! Look at a snowball in front f5 view, for example.
float yaw = mc.gameSettings.thirdPersonView == 2 ? RenderManager.instance.playerViewX : -RenderManager.instance.playerViewX;
GL11.glRotatef(180 - RenderManager.instance.playerViewY, 0.0F, 1.0F, 0.0F);
GL11.glRotatef(yaw, 1.0F, 0.0F, 0.0F);
tessellator.startDrawingQuads();
mc.renderEngine.bindTexture(targetPointerTexture);
tessellator.addVertexWithUV(-0.2, 0.24, 0, 0, 0);
tessellator.addVertexWithUV(0.2, 0.24, 0, 9f/16f, 0);
tessellator.addVertexWithUV(0.2, -0.24, 0, 9f/16f, 11f/16f);
tessellator.addVertexWithUV(-0.2, -0.24, 0, 0, 11f/16f);
tessellator.draw();
GL11.glEnable(GL11.GL_CULL_FACE);
GL11.glEnable(GL11.GL_LIGHTING);
GL11.glEnable(GL11.GL_DEPTH_TEST);
GL11.glPopMatrix();
}
// Summoned creature selection pointer
if(properties != null && properties.selectedMinion != null && properties.selectedMinion.get() == event.entity){
Tessellator tessellator = Tessellator.instance;
GL11.glPushMatrix();
GL11.glDisable(GL11.GL_CULL_FACE);
GL11.glDisable(GL11.GL_LIGHTING);
OpenGlHelper.setLightmapTextureCoords(OpenGlHelper.lightmapTexUnit, 240f, 240f);
// Disabling depth test allows it to be seen through everything.
GL11.glDisable(GL11.GL_DEPTH_TEST);
GL11.glColor4f(1, 1, 1, 1);
GL11.glTranslated(event.x, event.y + event.entity.height + 0.5, event.z);
// This counteracts the reverse rotation behaviour when in front f5 view.
// Fun fact: this is a bug with vanilla too! Look at a snowball in front f5 view, for example.
float yaw = mc.gameSettings.thirdPersonView == 2 ? RenderManager.instance.playerViewX : -RenderManager.instance.playerViewX;
GL11.glRotatef(180 - RenderManager.instance.playerViewY, 0.0F, 1.0F, 0.0F);
GL11.glRotatef(yaw, 1.0F, 0.0F, 0.0F);
tessellator.startDrawingQuads();
mc.renderEngine.bindTexture(pointerTexture);
tessellator.addVertexWithUV(-0.2, 0.24, 0, 0, 0);
tessellator.addVertexWithUV(0.2, 0.24, 0, 9f/16f, 0);
tessellator.addVertexWithUV(0.2, -0.24, 0, 9f/16f, 11f/16f);
tessellator.addVertexWithUV(-0.2, -0.24, 0, 0, 11f/16f);
tessellator.draw();
GL11.glEnable(GL11.GL_CULL_FACE);
GL11.glEnable(GL11.GL_LIGHTING);
GL11.glEnable(GL11.GL_DEPTH_TEST);
GL11.glPopMatrix();
}
// Sixth sense
if(mc.thePlayer.isPotionActive(Wizardry.sixthSense) && event.entity != mc.thePlayer
&& mc.thePlayer.getActivePotionEffect(Wizardry.sixthSense) != null
&& event.entity.getDistanceToEntity(mc.thePlayer) < 20*(1+mc.thePlayer.getActivePotionEffect(Wizardry.sixthSense).getAmplifier()*Wizardry.RANGE_INCREASE_PER_LEVEL)){
Tessellator tessellator = Tessellator.instance;
GL11.glPushMatrix();
GL11.glDisable(GL11.GL_CULL_FACE);
GL11.glEnable(GL11.GL_BLEND);
GL11.glDisable(GL11.GL_LIGHTING);
OpenGlHelper.setLightmapTextureCoords(OpenGlHelper.lightmapTexUnit, 240f, 240f);
GL11.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA);
// Disabling depth test allows it to be seen through everything.
GL11.glDisable(GL11.GL_DEPTH_TEST);
GL11.glTranslated(event.x, event.y + event.entity.height * 0.6, event.z);
// This counteracts the reverse rotation behaviour when in front f5 view.
// Fun fact: this is a bug with vanilla too! Look at a snowball in front f5 view, for example.
float yaw = mc.gameSettings.thirdPersonView == 2 ? RenderManager.instance.playerViewX : -RenderManager.instance.playerViewX;
GL11.glRotatef(180 - RenderManager.instance.playerViewY, 0.0F, 1.0F, 0.0F);
GL11.glRotatef(yaw, 1.0F, 0.0F, 0.0F);
tessellator.startDrawingQuads();
mc.renderEngine.bindTexture(sixthSenseTexture);
tessellator.addVertexWithUV(-0.6, 0.6, 0, 0, 0);
tessellator.addVertexWithUV(0.6, 0.6, 0, 1, 0);
tessellator.addVertexWithUV(0.6, -0.6, 0, 1, 1);
tessellator.addVertexWithUV(-0.6, -0.6, 0, 0, 1);
tessellator.draw();
GL11.glEnable(GL11.GL_CULL_FACE);
GL11.glDisable(GL11.GL_BLEND);
GL11.glEnable(GL11.GL_LIGHTING);
GL11.glEnable(GL11.GL_DEPTH_TEST);
GL11.glPopMatrix();
}
}
@SubscribeEvent
public static void onGuiOpenEvent(GuiOpenEvent event){
public void onRenderGameOverlayEvent(RenderGameOverlayEvent.Post event){
if(event.type == RenderGameOverlayEvent.ElementType.HELMET
&& Minecraft.getMinecraft().thePlayer.isPotionActive(Wizardry.sixthSense)){
if(Minecraft.getMinecraft().player != null && event.getGui() instanceof GuiContainer) {
WizardData data = WizardData.get(Minecraft.getMinecraft().player);
if (data != null && data.getVariable(Possession.POSSESSEE_KEY) != null) {
event.setCanceled(true);
}
GL11.glPushMatrix();
GL11.glDisable(GL11.GL_DEPTH_TEST);
GL11.glDepthMask(false);
OpenGlHelper.glBlendFunc(770, 771, 1, 0);
GL11.glColor4f(1.0F, 1.0F, 1.0F, 1.0F);
GL11.glDisable(GL11.GL_ALPHA_TEST);
Minecraft.getMinecraft().renderEngine.bindTexture(sixthSenseOverlayTexture);
Tessellator tessellator = Tessellator.instance;
tessellator.startDrawingQuads();
tessellator.addVertexWithUV(0.0D, (double)event.resolution.getScaledHeight(), -90.0D, 0.0D, 1.0D);
tessellator.addVertexWithUV((double)event.resolution.getScaledWidth(), (double)event.resolution.getScaledHeight(), -90.0D, 1.0D, 1.0D);
tessellator.addVertexWithUV((double)event.resolution.getScaledWidth(), 0.0D, -90.0D, 1.0D, 0.0D);
tessellator.addVertexWithUV(0.0D, 0.0D, -90.0D, 0.0D, 0.0D);
tessellator.draw();
GL11.glDepthMask(true);
GL11.glEnable(GL11.GL_DEPTH_TEST);
GL11.glEnable(GL11.GL_ALPHA_TEST);
GL11.glColor4f(1.0F, 1.0F, 1.0F, 1.0F);
GL11.glPopMatrix();
}
if(event.type == RenderGameOverlayEvent.ElementType.HELMET
&& Minecraft.getMinecraft().thePlayer.isPotionActive(Wizardry.frost)){
GL11.glPushMatrix();
GL11.glDisable(GL11.GL_DEPTH_TEST);
GL11.glDepthMask(false);
OpenGlHelper.glBlendFunc(770, 771, 1, 0);
GL11.glColor4f(1.0F, 1.0F, 1.0F, 1.0F);
GL11.glDisable(GL11.GL_ALPHA_TEST);
Minecraft.getMinecraft().renderEngine.bindTexture(frostOverlayTexture);
Tessellator tessellator = Tessellator.instance;
tessellator.startDrawingQuads();
tessellator.addVertexWithUV(0.0D, (double)event.resolution.getScaledHeight(), -90.0D, 0.0D, 1.0D);
tessellator.addVertexWithUV((double)event.resolution.getScaledWidth(), (double)event.resolution.getScaledHeight(), -90.0D, 1.0D, 1.0D);
tessellator.addVertexWithUV((double)event.resolution.getScaledWidth(), 0.0D, -90.0D, 1.0D, 0.0D);
tessellator.addVertexWithUV(0.0D, 0.0D, -90.0D, 0.0D, 0.0D);
tessellator.draw();
GL11.glDepthMask(true);
GL11.glEnable(GL11.GL_DEPTH_TEST);
GL11.glEnable(GL11.GL_ALPHA_TEST);
GL11.glColor4f(1.0F, 1.0F, 1.0F, 1.0F);
GL11.glPopMatrix();
}
}
/**
* Renders an overlay across the entire screen.
* @param resolution The screen resolution
* @param texture The overlay texture to display
*/
public static void renderScreenOverlay(ScaledResolution resolution, ResourceLocation texture){
GlStateManager.pushMatrix();
private static void renderShadowWardFirstPerson(EntityPlayer entityplayer){
ItemStack wand = entityplayer.getHeldItem();
if(ExtendedPlayer.get(entityplayer).currentlyCasting() instanceof ShadowWard || (entityplayer.isUsingItem() && wand != null && wand.getItemDamage() < wand.getMaxDamage()
&& wand.getItem() instanceof ItemWand && WandHelper.getCurrentSpell(wand) instanceof ShadowWard)){
GlStateManager.disableDepth();
GlStateManager.depthMask(false);
Minecraft.getMinecraft().renderEngine.bindTexture(texture);
GL11.glPushMatrix();
Tessellator tessellator = Tessellator.getInstance();
BufferBuilder buffer = tessellator.getBuffer();
GL11.glEnable(GL11.GL_BLEND);
GL11.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA);
GL11.glDisable(GL11.GL_LIGHTING);
OpenGlHelper.setLightmapTextureCoords(OpenGlHelper.lightmapTexUnit, 240f, 240f);
buffer.begin(GL11.GL_QUADS, DefaultVertexFormats.POSITION_TEX);
GL11.glRotatef(-1*entityplayer.rotationYaw, 0, 1, 0);
GL11.glRotatef(entityplayer.rotationPitch, 1, 0, 0);
Minecraft.getMinecraft().renderEngine.bindTexture(shadowWardTexture);
Tessellator tessellator = Tessellator.instance;
buffer.pos(0, resolution.getScaledHeight(), -90).tex(0, 1).endVertex();
buffer.pos(resolution.getScaledWidth(), resolution.getScaledHeight(), -90).tex(1, 1).endVertex();
buffer.pos(resolution.getScaledWidth(), 0, -90).tex(1, 0).endVertex();
buffer.pos(0, 0, -90).tex(0, 0).endVertex();
GL11.glPushMatrix();
GL11.glTranslated(0, -0.3, 1.2);
GL11.glRotated(entityplayer.worldObj.getWorldTime()*-2, 0, 0, 1);
GL11.glScaled(1.1, 1.1, 1.1);
tessellator.startDrawingQuads();
tessellator.addVertexWithUV(-0.5, 0.5, -0.5, 0, 0);
tessellator.addVertexWithUV(0.5, 0.5, -0.5, 1, 0);
tessellator.addVertexWithUV(0.5, -0.5, -0.5, 1, 1);
tessellator.addVertexWithUV(-0.5, -0.5, -0.5, 0, 1);
tessellator.draw();
tessellator.startDrawingQuads();
tessellator.addVertexWithUV(-0.5, 0.5, -0.5, 0, 0);
tessellator.addVertexWithUV(-0.5, -0.5, -0.5, 0, 1);
tessellator.addVertexWithUV(0.5, -0.5, -0.5, 1, 1);
tessellator.addVertexWithUV(0.5, 0.5, -0.5, 1, 0);
tessellator.draw();
GL11.glPopMatrix();
GL11.glEnable(GL11.GL_LIGHTING);
GL11.glDisable(GL11.GL_BLEND);
GL11.glPopMatrix();
}
}
private static void renderShadowWardIfActive(EntityPlayer entityplayer){
ItemStack wand = entityplayer.getHeldItem();
if(ExtendedPlayer.get(entityplayer).currentlyCasting() instanceof ShadowWard || (entityplayer.isUsingItem() && wand != null
&& wand.getItemDamage() < wand.getMaxDamage() && wand.getItem() instanceof ItemWand && WandHelper.getCurrentSpell(wand) instanceof ShadowWard)){
GL11.glPushMatrix();
GL11.glEnable(GL11.GL_BLEND);
GL11.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA);
GL11.glDisable(GL11.GL_LIGHTING);
OpenGlHelper.setLightmapTextureCoords(OpenGlHelper.lightmapTexUnit, 240f, 240f);
GL11.glRotatef(180, 0, 1, 0);
//GL11.glRotatef(entityplayer.rotationYaw, 0, 1, 0);
//GL11.glRotatef(-entityplayer.rotationPitch, 1, 0, 0);
Minecraft.getMinecraft().renderEngine.bindTexture(shadowWardTexture);
Tessellator tessellator = Tessellator.instance;
GL11.glPushMatrix();
// was 0, -0.3, 1.2
GL11.glTranslated(0, 0, 1.2);
GL11.glRotated(entityplayer.worldObj.getWorldTime()*-2, 0, 0, 1);
GL11.glScaled(1.1, 1.1, 1.1);
tessellator.startDrawingQuads();
tessellator.addVertexWithUV(-0.5, 0.5, -0.5, 0, 0);
tessellator.addVertexWithUV(0.5, 0.5, -0.5, 1, 0);
tessellator.addVertexWithUV(0.5, -0.5, -0.5, 1, 1);
tessellator.addVertexWithUV(-0.5, -0.5, -0.5, 0, 1);
tessellator.draw();
tessellator.startDrawingQuads();
tessellator.addVertexWithUV(-0.5, 0.5, -0.5, 0, 0);
tessellator.addVertexWithUV(-0.5, -0.5, -0.5, 0, 1);
tessellator.addVertexWithUV(0.5, -0.5, -0.5, 1, 1);
tessellator.addVertexWithUV(0.5, 0.5, -0.5, 1, 0);
tessellator.draw();
GL11.glPopMatrix();
GL11.glEnable(GL11.GL_LIGHTING);
GL11.glDisable(GL11.GL_BLEND);
GL11.glPopMatrix();
}
}
private static void renderWingsIfActive(EntityPlayer entityplayer, float partialTickTime){
ItemStack wand = entityplayer.getHeldItem();
if(ExtendedPlayer.get(entityplayer).currentlyCasting() instanceof Flight || (entityplayer.isUsingItem() && wand != null
&& wand.getItemDamage() < wand.getMaxDamage() && wand.getItem() instanceof ItemWand && WandHelper.getCurrentSpell(wand) instanceof Flight)){
GL11.glPushMatrix();
GL11.glEnable(GL11.GL_BLEND);
GL11.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA);
GL11.glDisable(GL11.GL_LIGHTING);
OpenGlHelper.setLightmapTextureCoords(OpenGlHelper.lightmapTexUnit, 240f, 240f);
GL11.glRotatef(-entityplayer.rotationYawHead, 0, 1, 0);
GL11.glRotatef(entityplayer.rotationYaw, 0, 1, 0);
GL11.glRotatef(180, 1, 0, 0);
Minecraft.getMinecraft().renderEngine.bindTexture(wingTexture);
Tessellator tessellator = Tessellator.instance;
GL11.glPushMatrix();
GL11.glTranslated(0.1, -1.0, -0.15);
GL11.glRotatef(20 + 20*(float)Math.sin(entityplayer.worldObj.getWorldTime()*0.3), 0, 1, 0);
tessellator.startDrawingQuads();
tessellator.addVertexWithUV(0, 2, 0, 0, 0);
tessellator.addVertexWithUV(2, 2, 0, 1, 0);
tessellator.addVertexWithUV(2, 0, 0, 1, 1);
tessellator.addVertexWithUV(0, 0, 0, 0, 1);
tessellator.draw();
tessellator.startDrawingQuads();
tessellator.addVertexWithUV(0, 2, 0, 0, 0);
tessellator.addVertexWithUV(0, 0, 0, 0, 1);
tessellator.addVertexWithUV(2, 0, 0, 1, 1);
tessellator.addVertexWithUV(2, 2, 0, 1, 0);
tessellator.draw();
GL11.glPopMatrix();
GL11.glPushMatrix();
GL11.glTranslated(-0.1, -1.0, -0.15);
GL11.glRotatef(-200 - 20*(float)Math.sin(entityplayer.worldObj.getWorldTime()*0.3), 0, 1, 0);
tessellator.startDrawingQuads();
tessellator.addVertexWithUV(0, 2, 0, 0, 0);
tessellator.addVertexWithUV(2, 2, 0, 1, 0);
tessellator.addVertexWithUV(2, 0, 0, 1, 1);
tessellator.addVertexWithUV(0, 0, 0, 0, 1);
tessellator.draw();
tessellator.startDrawingQuads();
tessellator.addVertexWithUV(0, 2, 0, 0, 0);
tessellator.addVertexWithUV(0, 0, 0, 0, 1);
tessellator.addVertexWithUV(2, 0, 0, 1, 1);
tessellator.addVertexWithUV(2, 2, 0, 1, 0);
tessellator.draw();
GL11.glPopMatrix();
GL11.glEnable(GL11.GL_LIGHTING);
GL11.glDisable(GL11.GL_BLEND);
GL11.glPopMatrix();
}
}
private static void renderShieldFirstPerson(EntityPlayer entityplayer){
ItemStack wand = entityplayer.getHeldItem();
if(ExtendedPlayer.get(entityplayer).shield != null && (ExtendedPlayer.get(entityplayer).currentlyCasting() instanceof Shield || (entityplayer.isUsingItem() && wand != null
&& wand.getItemDamage() < wand.getMaxDamage() && wand.getItem() instanceof ItemWand && WandHelper.getCurrentSpell(wand) instanceof Shield))){
double x = entityplayer.posX;
double y = entityplayer.posY;
double z = entityplayer.posZ;
EntityShield shield = (EntityShield)ExtendedPlayer.get(entityplayer).shield;
GL11.glPushMatrix();
GL11.glDisable(GL11.GL_CULL_FACE);
GL11.glEnable(GL11.GL_BLEND);
GL11.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_SRC_ALPHA);
GL11.glShadeModel(GL11.GL_SMOOTH);
GL11.glDisable(GL11.GL_LIGHTING);
OpenGlHelper.setLightmapTextureCoords(OpenGlHelper.lightmapTexUnit, 240f, 240f);
GL11.glTranslated(0, -0.3, 0);
GL11.glRotatef(-1*entityplayer.rotationYaw, 0, 1, 0);
GL11.glRotatef(entityplayer.rotationPitch, 1, 0, 0);
GL11.glTranslated(0, 0, 0.8);
Tessellator tessellator = Tessellator.instance;
Minecraft.getMinecraft().renderEngine.bindTexture(shieldTexture);
renderShield(tessellator, -1);
// Enchantment effect
/*
GL11.glPushMatrix();
GL11.glDisable(GL11.GL_LIGHTING);
//this.bindTexture(RES_ITEM_GLINT);
GL11.glBlendFunc(GL11.GL_SRC_COLOR, GL11.GL_ONE);
float f7 = 0.76F;
GL11.glColor4f(0.5F * f7, 0.25F * f7, 0.8F * f7, 1.0F);
float f8 = 0.125F;
//GL11.glScalef(f8, f8, f8);
float f9 = (float)(Minecraft.getSystemTime() % 3000L) / 3000.0F * 0.8f;
//GL11.glTranslatef(f9, 0.0F, 0.0F);
//GL11.glRotatef(-50.0F, 0.0F, 0.0F, 1.0F);
//this.renderShield(tessellator, f9);
GL11.glPopMatrix();
*/
GL11.glEnable(GL11.GL_LIGHTING);
GL11.glShadeModel(GL11.GL_FLAT);
GL11.glEnable(GL11.GL_CULL_FACE);
GL11.glDisable(GL11.GL_BLEND);
//RenderHelper.enableStandardItemLighting();
GL11.glPopMatrix();
}
}
private static void renderShieldIfActive(EntityPlayer entityplayer){
ItemStack wand = entityplayer.getHeldItem();
if(ExtendedPlayer.get(entityplayer).shield != null && (ExtendedPlayer.get(entityplayer).currentlyCasting() instanceof Shield || (entityplayer.isUsingItem() && wand != null
&& wand.getItemDamage() < wand.getMaxDamage() && wand.getItem() instanceof ItemWand && WandHelper.getCurrentSpell(wand) instanceof Shield))){
double x = entityplayer.posX;
double y = entityplayer.posY;
double z = entityplayer.posZ;
EntityShield shield = (EntityShield)ExtendedPlayer.get(entityplayer).shield;
GL11.glPushMatrix();
GL11.glDisable(GL11.GL_CULL_FACE);
GL11.glEnable(GL11.GL_BLEND);
GL11.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_SRC_ALPHA);
GL11.glShadeModel(GL11.GL_SMOOTH);
GL11.glDisable(GL11.GL_LIGHTING);
OpenGlHelper.setLightmapTextureCoords(OpenGlHelper.lightmapTexUnit, 240f, 240f);
GL11.glTranslated(0, 0, 0);
GL11.glRotatef(180, 0, 1, 0);
//GL11.glRotatef(entityplayer.rotationYaw, 0, 1, 0);
//GL11.glRotatef(-entityplayer.rotationPitch, 1, 0, 0);
GL11.glTranslated(0, 0, 0.8);
Tessellator tessellator = Tessellator.instance;
Minecraft.getMinecraft().renderEngine.bindTexture(shieldTexture);
renderShield(tessellator, -1);
// Enchantment effect
/*
GL11.glPushMatrix();
GL11.glDisable(GL11.GL_LIGHTING);
//this.bindTexture(RES_ITEM_GLINT);
GL11.glBlendFunc(GL11.GL_SRC_COLOR, GL11.GL_ONE);
float f7 = 0.76F;
GL11.glColor4f(0.5F * f7, 0.25F * f7, 0.8F * f7, 1.0F);
float f8 = 0.125F;
//GL11.glScalef(f8, f8, f8);
float f9 = (float)(Minecraft.getSystemTime() % 3000L) / 3000.0F * 0.8f;
//GL11.glTranslatef(f9, 0.0F, 0.0F);
//GL11.glRotatef(-50.0F, 0.0F, 0.0F, 1.0F);
//this.renderShield(tessellator, f9);
GL11.glPopMatrix();
*/
GL11.glEnable(GL11.GL_LIGHTING);
GL11.glShadeModel(GL11.GL_FLAT);
GL11.glEnable(GL11.GL_CULL_FACE);
GL11.glDisable(GL11.GL_BLEND);
//RenderHelper.enableStandardItemLighting();
GL11.glPopMatrix();
}
}
private static void renderShield(Tessellator tessellator, float textureOffset){
double widthOuter = 0.6d;
double heightOuter = 0.7d;
double widthInner = 0.3d;
double heightInner = 0.4d;
double depth = 0.2d;
double textureSection = 1.0d;
double textureU = 0.0d;
if(textureOffset != -1){
textureSection = 0.2d;
textureU = textureOffset;
}
tessellator.startDrawing(5);
tessellator.setColorRGBA(0, 0, 0, 255);
tessellator.addVertexWithUV(-widthOuter, heightOuter - 0.3, -depth, 0, 0);
tessellator.setColorOpaque(200, 200, 255);
tessellator.addVertexWithUV(-widthInner, heightInner, 0, textureU, 0.2);
tessellator.setColorRGBA(0, 0, 0, 255);
tessellator.addVertexWithUV(-widthOuter + 0.3, heightOuter, -depth, 0, 0);
tessellator.setColorOpaque(200, 200, 255);
tessellator.addVertexWithUV(-widthInner, heightInner, 0, textureU, 0.2);
tessellator.setColorRGBA(0, 0, 0, 255);
tessellator.addVertexWithUV(widthOuter - 0.3, heightOuter, -depth, 1, 0);
tessellator.setColorOpaque(200, 200, 255);
tessellator.addVertexWithUV(widthInner, heightInner, 0, textureU + textureSection, 0.2);
tessellator.setColorRGBA(0, 0, 0, 255);
tessellator.addVertexWithUV(widthOuter, heightOuter - 0.3, -depth, 1, 0);
tessellator.setColorOpaque(200, 200, 255);
tessellator.addVertexWithUV(widthInner, heightInner, 0, textureU + textureSection, 0.2);
tessellator.setColorRGBA(0, 0, 0, 255);
tessellator.addVertexWithUV(widthOuter, -heightOuter + 0.3, -depth, 1, 1);
tessellator.setColorOpaque(200, 200, 255);
tessellator.addVertexWithUV(widthInner, -heightInner, 0, textureU + textureSection, 0.8);
tessellator.setColorRGBA(0, 0, 0, 255);
tessellator.addVertexWithUV(widthOuter - 0.3, -heightOuter, -depth, 1, 1);
tessellator.setColorOpaque(200, 200, 255);
tessellator.addVertexWithUV(widthInner, -heightInner, 0, textureU + textureSection, 0.8);
tessellator.setColorRGBA(0, 0, 0, 255);
tessellator.addVertexWithUV(-widthOuter + 0.3, -heightOuter, -depth, 0, 1);
tessellator.setColorOpaque(200, 200, 255);
tessellator.addVertexWithUV(-widthInner, -heightInner, 0, textureU, 0.8);
tessellator.setColorRGBA(0, 0, 0, 255);
tessellator.addVertexWithUV(-widthOuter, -heightOuter + 0.3, -depth, 0, 1);
tessellator.setColorOpaque(200, 200, 255);
tessellator.addVertexWithUV(-widthInner, -heightInner, 0, textureU, 0.8);
tessellator.setColorRGBA(0, 0, 0, 255);
tessellator.addVertexWithUV(-widthOuter, heightOuter - 0.3, -depth, 0, 0);
tessellator.setColorOpaque(200, 200, 255);
tessellator.addVertexWithUV(-widthInner, heightInner, 0, textureU, 0.2);
tessellator.draw();
GlStateManager.depthMask(true);
GlStateManager.enableDepth();
GlStateManager.popMatrix();
tessellator.startDrawing(5);
tessellator.setColorOpaque(200, 200, 255);
tessellator.addVertexWithUV(-widthInner, heightInner, 0, textureU, 0.2);
tessellator.setColorOpaque(200, 200, 255);
tessellator.addVertexWithUV(widthInner, heightInner, 0, textureU + textureSection, 0.2);
tessellator.setColorOpaque(200, 200, 255);
tessellator.addVertexWithUV(-widthInner, -heightInner, 0, textureU, 0.8);
tessellator.setColorOpaque(200, 200, 255);
tessellator.addVertexWithUV(widthInner, -heightInner, 0, textureU + textureSection, 0.8);
tessellator.draw();
}
}
@@ -1,149 +0,0 @@
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.PacketSpellQuickAccess;
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;
static boolean[] quickAccessKeyPressed = new boolean[ClientProxy.SPELL_QUICK_ACCESS.length];
// Changed to a tick event to allow mouse button keybinds
// The 'lag' that happened previously was actually because the code only fired when a keyboard key was pressed!
@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;
}
for(int i = 0; i < ClientProxy.SPELL_QUICK_ACCESS.length; i++){
if(ClientProxy.SPELL_QUICK_ACCESS[i].isKeyDown() && Minecraft.getMinecraft().inGameHasFocus){
if(!quickAccessKeyPressed[i]){
quickAccessKeyPressed[i] = true;
// Packet building
selectSpell(wand, i);
}
}else{
quickAccessKeyPressed[i] = false;
}
}
}
}
}
// 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));
}
private static void selectSpell(ItemStack wand, int index){
// GUI switch animation
if(((ISpellCastingItem)wand.getItem()).selectSpell(wand, index)){ // Makes sure the spell is set immediately for the client
// Packet building (no point sending it unless the client-side spell selection succeeded
IMessage msg = new PacketSpellQuickAccess.Message(index);
WizardryPacketHandler.net.sendToServer(msg);
GuiSpellDisplay.playSpellSwitchAnimation(true); // This will do, it's only an animation
Minecraft.getMinecraft().getSoundHandler().playSound(PositionedSoundRecord.getMasterRecord(WizardrySounds.ITEM_WAND_SWITCH_SPELL, 1));
}
}
}
@@ -1,155 +0,0 @@
package electroblob.wizardry.client.animation;
import electroblob.wizardry.data.WizardData;
import electroblob.wizardry.item.SpellActions;
import electroblob.wizardry.spell.Grapple;
import electroblob.wizardry.util.GeometryUtils;
import electroblob.wizardry.util.InventoryUtils;
import net.minecraft.client.model.ModelBiped;
import net.minecraft.entity.EntityLivingBase;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.item.EnumAction;
import net.minecraft.util.EnumHandSide;
import net.minecraft.util.math.MathHelper;
import net.minecraft.util.math.RayTraceResult;
import net.minecraft.util.math.Vec3d;
/**
* An animation that is associated with a particular {@link net.minecraft.item.EnumAction}. This animation will display
* when a player is using an item with that action.
* @author Electroblob
* @since Wizardry 4.3
* @see electroblob.wizardry.item.SpellActions
*/
public abstract class ActionAnimation extends Animation {
private final EnumAction action;
public ActionAnimation(EnumAction action){
super(action.name());
this.action = action;
}
@Override
public boolean shouldDisplay(EntityPlayer player, boolean firstPerson){
return !firstPerson && player.getItemInUseMaxCount() > 0 && player.getActiveItemStack().getItemUseAction() == action;
}
/** Called from the client proxy to register all of wizardry's action animations. */
public static void register(){
PlayerAnimator.registerAnimation(new ActionAnimation(SpellActions.POINT){
@Override
public void setRotationAngles(EntityPlayer player, ModelBiped model, float partialTicks, boolean firstPerson){
// Something (probably some sort of race condition) causes getActiveItemStack to sometimes be wrong
if(player.getActiveItemStack() != player.getHeldItem(player.getActiveHand())) return;
EnumHandSide side = InventoryUtils.getSideForHand(player, player.getActiveHand());
float pitch = (float)Math.toRadians(player.prevRotationPitch + (player.rotationPitch - player.prevRotationPitch) * partialTicks);
float yaw = (float)Math.toRadians(player.prevRotationYawHead + (player.rotationYawHead - player.prevRotationYawHead) * partialTicks)
- (float)Math.toRadians(player.prevRenderYawOffset + (player.renderYawOffset - player.prevRenderYawOffset) * partialTicks);
float x = -((float)Math.PI / 2) + pitch + 0.2f;
float y = (side == EnumHandSide.RIGHT ? -0.25f : 0.25f) + yaw;
ModelRendererExtended arm = (ModelRendererExtended)getArmForSide(model, side);
arm.setRotation(x, y, 0);
}
});
PlayerAnimator.registerAnimation(new ActionAnimation(SpellActions.POINT_UP){
@Override
public void setRotationAngles(EntityPlayer player, ModelBiped model, float partialTicks, boolean firstPerson){
if(player.getActiveItemStack() != player.getHeldItem(player.getActiveHand())) return;
EnumHandSide side = InventoryUtils.getSideForHand(player, player.getActiveHand());
ModelRendererExtended arm = (ModelRendererExtended)getArmForSide(model, side);
arm.addRotation(-2.2f, side == EnumHandSide.RIGHT ? 0.2f : -0.2f, 0);
}
});
PlayerAnimator.registerAnimation(new ActionAnimation(SpellActions.POINT_DOWN){
@Override
public void setRotationAngles(EntityPlayer player, ModelBiped model, float partialTicks, boolean firstPerson){
float tick = player.getItemInUseMaxCount() + partialTicks; // It's not the "max" use count at all!
float y = Math.min(0.4f + tick * 0.05f, 0.7f);
EnumHandSide side = InventoryUtils.getSideForHand(player, player.getActiveHand());
ModelRendererExtended arm = (ModelRendererExtended)getArmForSide(model, side);
arm.addRotation(-0.2f, side == EnumHandSide.RIGHT ? y : -y, 0);
}
});
PlayerAnimator.registerAnimation(new ActionAnimation(SpellActions.SUMMON){
@Override
public void setRotationAngles(EntityPlayer player, ModelBiped model, float partialTicks, boolean firstPerson){
float tick = player.getItemInUseMaxCount() + partialTicks; // It's not the "max" use count at all!
float x = -Math.min(0.4f + tick * 0.2f, 2f);
((ModelRendererExtended)getArmForSide(model, EnumHandSide.RIGHT)).addRotation(x, 1.2f, 0);
((ModelRendererExtended)getArmForSide(model, EnumHandSide.LEFT)).addRotation(x, -1.2f, 0);
}
});
PlayerAnimator.registerAnimation(new ActionAnimation(SpellActions.GRAPPLE){
@Override
public void setRotationAngles(EntityPlayer player, ModelBiped model, float partialTicks, boolean firstPerson){
WizardData data = WizardData.get(player);
RayTraceResult hit = data.getVariable(Grapple.TARGET_KEY);
if(hit == null || hit.typeOfHit == RayTraceResult.Type.MISS) return;
Vec3d target = hit.hitVec;
if(hit.entityHit instanceof EntityLivingBase){
// If the target is an entity, we need to use the entity's centre rather than the original hit position
// because the entity will have moved!
target = GeometryUtils.getCentre(hit.entityHit);
}
EnumHandSide side = InventoryUtils.getSideForHand(player, player.getActiveHand());
ModelRendererExtended arm = (ModelRendererExtended)getArmForSide(model, side);
Vec3d direction = target.subtract(player.getPositionEyes(partialTicks));
float yaw = player.prevRenderYawOffset + (player.renderYawOffset - player.prevRenderYawOffset) * partialTicks;
float pitch = (float)MathHelper.atan2(MathHelper.sqrt(direction.x*direction.x + direction.z*direction.z), direction.y);
float x = pitch - (float)Math.PI * 0.9f;
float y = -(float)Math.toRadians(yaw) - (float)MathHelper.atan2(direction.x, direction.z);
y += (side == EnumHandSide.RIGHT ? -0.25f : 0.25f);
if(Math.abs(pitch) < 0.2f) y = arm.rotateAngleY;
arm.setRotation(x, y, 0);
}
});
PlayerAnimator.registerAnimation(new ActionAnimation(SpellActions.IMBUE){
@Override
public void setRotationAngles(EntityPlayer player, ModelBiped model, float partialTicks, boolean firstPerson){
float tick = player.getItemInUseMaxCount() + partialTicks; // It's not the "max" use count at all!
float z = Math.max(1.5f - tick * 0.1f, 0.8f);
EnumHandSide side = InventoryUtils.getSideForHand(player, player.getActiveHand());
ModelRendererExtended arm = (ModelRendererExtended)getArmForSide(model, side);
arm.addRotation(-1.2f, side == EnumHandSide.RIGHT ? -0.2f : 0.2f, side == EnumHandSide.RIGHT ? z : -z);
if(!player.getHeldItem(InventoryUtils.getHandForSide(player, side.opposite())).isEmpty()){
((ModelRendererExtended)getArmForSide(model, side.opposite())).addRotation(-0.8f, side == EnumHandSide.LEFT ? 0.3f : -0.3f, 0);
}
}
});
PlayerAnimator.registerAnimation(new ActionAnimation(SpellActions.THRUST){
@Override
public void setRotationAngles(EntityPlayer player, ModelBiped model, float partialTicks, boolean firstPerson){
if(player.getActiveItemStack() != player.getHeldItem(player.getActiveHand())) return;
EnumHandSide side = InventoryUtils.getSideForHand(player, player.getActiveHand());
ModelRendererExtended arm = (ModelRendererExtended)getArmForSide(model, side);
float y = side == EnumHandSide.RIGHT ? -0.6f : 0.6f;
arm.addRotation(-1.2f, y, 0);
if(player.getHeldItem(InventoryUtils.getHandForSide(player, side.opposite())).isEmpty()){
ModelRendererExtended otherArm = (ModelRendererExtended)getArmForSide(model, side.opposite());
otherArm.setRotation(arm.rotateAngleX - 1.2f, -arm.rotateAngleY - y, otherArm.rotateAngleZ);
}
}
});
}
}
@@ -1,77 +0,0 @@
package electroblob.wizardry.client.animation;
import net.minecraft.client.model.ModelBiped;
import net.minecraft.client.model.ModelRenderer;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.util.EnumHandSide;
/**
* Represents a player animation. Use one of the predefined subclasses below for common animation types, or extend this
* class directly to define a completely custom animation. Register instances of this class using
* {@link PlayerAnimator#registerAnimation(Animation)}.
* @author Electroblob
* @since Wizardry 4.3
* @see ActionAnimation
*/
public abstract class Animation {
private final String name;
public Animation(String name){
this.name = name;
}
/** Returns the name of this animation, currently only used for warning messages / debugging. */
public String getName(){
return name;
}
/**
* Returns whether this animation should be displayed for the given player. <i>Note that if two registered
* animations have overlapping conditions, both will attempt to display simultaneously, so if they both animate the
* same model part, that part may not behave as expected.</i>
* @param player The player being animated
* @param firstPerson True if the player being animated is the local client player, and they are in first-person
* view. In first-person, animations are only useful for animating the player's empty hand.
* @return True if the animation should be displayed, false if not.
*/
public abstract boolean shouldDisplay(EntityPlayer player, boolean firstPerson);
/**
* Sets the rotation of the model parts for this animation. This method is called every time the player is rendered
* when {@link Animation#shouldDisplay(EntityPlayer, boolean)} returns true.
* @param player The player being animated
* @param model The model to animate. All of the standard {@link ModelBiped} parts will already be wrapped and may
* be safely cast to {@code ModelRendererExtended} in order to override the rotations set by the model
* itself - see {@link ModelRendererExtended ModelRendererExtended} for details.
* @param partialTicks The current partial tick time
* @param firstPerson True if the player being animated is the local client player, and they are in first-person
* view. In first-person, animations are only useful for animating the player's empty hand.
*/
public abstract void setRotationAngles(EntityPlayer player, ModelBiped model, float partialTicks, boolean firstPerson);
/**
* Returns whether the boxes that form the second layer of the player's skin (for models that are instances of
* {@link net.minecraft.client.model.ModelPlayer ModelPlayer}) should automatically be set to the same angles as
* their corresponding first-layer parts.
* @param player The player being rendered, for reference
* @param firstPerson True if the player being animated is the local client player, and they are in first-person
* view. In first-person, animations are only useful for animating the player's empty hand.
* @return True to let {@link PlayerAnimator} auto-rotate the second skin layer, false to rotate them manually.
*/
public boolean autoRotateSecondLayer(EntityPlayer player, boolean firstPerson){
return true;
}
/**
* Returns the arm of the given model corresponding to the given side. Function is identical to the method of the
* same name in {@link ModelBiped}.
* @param model The model to get the arm of
* @param side The {@link EnumHandSide} to return the arm for
* @return The {@link ModelRenderer} corresponding to the given arm
*/
public static ModelRenderer getArmForSide(ModelBiped model, EnumHandSide side){
return side == EnumHandSide.LEFT ? model.bipedLeftArm : model.bipedRightArm;
}
}
@@ -1,205 +0,0 @@
package electroblob.wizardry.client.animation;
import net.minecraft.client.model.ModelBase;
import net.minecraft.client.model.ModelBiped;
import net.minecraft.client.model.ModelPlayer;
import net.minecraft.client.model.ModelRenderer;
/**
* A wrapper around {@link ModelRenderer} which can be swapped into models to allow render pre/post events to
* circumvent {@code ModelBase.setRotationAngles(...)} and set model part rotations themselves. Instances of this class
* keep a reference to the original {@code ModelRenderer} and delegate all the actual rendering to it.
* <p></p>
* {@link PlayerAnimator PlayerAnimator} does all the necessary setup with this
* class for player animations. It is also possible to set up custom animations for non-player entities with this class,
* see {@link ModelRendererExtended#wrap(ModelBiped)} and {@link ModelRendererExtended#wrap(ModelBase, ModelRenderer)}.
* @author Electroblob
* @since Wizardry 4.3
*/
public class ModelRendererExtended extends ModelRenderer {
// We probably could have copied all the fields over instead, but this way if anyone else modifies the boxes we
// won't overwrite their changes
// This way also avoids reflection because we don't need to copy private fields, which is better for performance
private ModelRenderer delegate;
private float actualRotationX;
private float actualRotationY;
private float actualRotationZ;
private float extraRotationX;
private float extraRotationY;
private float extraRotationZ;
private ModelRendererExtended(ModelBase baseModel, ModelRenderer delegate){
super(baseModel, delegate.boxName);
this.delegate = delegate;
// Copy over any public fields that might get changed
this.textureWidth = delegate.textureWidth;
this.textureHeight = delegate.textureHeight;
this.showModel = delegate.showModel;
this.isHidden = delegate.isHidden;
this.rotationPointX = delegate.rotationPointX;
this.rotationPointY = delegate.rotationPointY;
this.rotationPointZ = delegate.rotationPointZ;
this.cubeList = delegate.cubeList;
this.childModels = delegate.childModels;
this.resetRotation();
}
// Static initialisers
/**
* Replaces the given part of the given {@link ModelBase} with a new {@code ModelRendererExtended} that wraps the
* original part. This method takes care of modifying the model's box list, but <b>is unable to change any
* individual {@link ModelRenderer} fields</b> - this method instead returns the resulting
* {@code ModelRendererExtended} to be assigned to the appropriate field.
* @param model The model whose part is to be wrapped
* @param box The part of the model to wrap, must belong to the above model
* @return The resulting wrapped model part, this should be assigned to the appropriate part field in the model. If
* the given model part was already wrapped, this method simply returns it unchanged.
* @throws IllegalArgumentException if the given box does not belong to the given model
*/
public static ModelRendererExtended wrap(ModelBase model, ModelRenderer box){
if(box instanceof ModelRendererExtended) return (ModelRendererExtended)box; // Ignore already-wrapped parts
ModelRendererExtended wrapper = new ModelRendererExtended(model, box);
int index = model.boxList.indexOf(box);
if(index < 0) throw new IllegalArgumentException(String.format("The given ModelRenderer %s does not belong to the given model %s", box, model));
model.boxList.set(index, wrapper); // I doubt the order matters but we may as well put it at the same index
return wrapper;
}
/**
* Replaces the given {@link ModelBiped}'s parts with new {@code ModelRendererExtended} versions that wrap the
* original parts. If the given model is a {@link ModelPlayer}, the extra boxes for player skin overlays will also
* be wrapped.
*/
public static void wrap(ModelBiped model){
// MMmmmmmm wraps
model.bipedHead = wrap(model, model.bipedHead);
model.bipedBody = wrap(model, model.bipedBody);
model.bipedRightArm = wrap(model, model.bipedRightArm);
model.bipedLeftArm = wrap(model, model.bipedLeftArm);
model.bipedRightLeg = wrap(model, model.bipedRightLeg);
model.bipedLeftLeg = wrap(model, model.bipedLeftLeg);
model.bipedHeadwear = wrap(model, model.bipedHeadwear);
if(model instanceof ModelPlayer){
((ModelPlayer)model).bipedBodyWear = wrap(model, ((ModelPlayer)model).bipedBodyWear);
((ModelPlayer)model).bipedRightArmwear = wrap(model, ((ModelPlayer)model).bipedRightArmwear);
((ModelPlayer)model).bipedLeftArmwear = wrap(model, ((ModelPlayer)model).bipedLeftArmwear);
((ModelPlayer)model).bipedRightLegwear = wrap(model, ((ModelPlayer)model).bipedRightLegwear);
((ModelPlayer)model).bipedLeftLegwear = wrap(model, ((ModelPlayer)model).bipedLeftLegwear);
}
}
/**
* Returns whether the given {@link ModelBiped} is fully wrapped, i.e. all its parts are instances of
* {@code ModelRendererExtended}. If the given model is a {@link ModelPlayer}, this also checks the extra boxes for
* player skin overlays.
*/
public static boolean isWrapped(ModelBiped model){
boolean flag = true;
if(model instanceof ModelPlayer){
flag = flag && ((ModelPlayer)model).bipedBodyWear instanceof ModelRendererExtended;
flag = flag && ((ModelPlayer)model).bipedRightArmwear instanceof ModelRendererExtended;
flag = flag && ((ModelPlayer)model).bipedLeftArmwear instanceof ModelRendererExtended;
flag = flag && ((ModelPlayer)model).bipedRightLegwear instanceof ModelRendererExtended;
flag = flag && ((ModelPlayer)model).bipedLeftLegwear instanceof ModelRendererExtended;
}
return flag
&& model.bipedHead instanceof ModelRendererExtended
&& model.bipedBody instanceof ModelRendererExtended
&& model.bipedRightArm instanceof ModelRendererExtended
&& model.bipedLeftArm instanceof ModelRendererExtended
&& model.bipedRightLeg instanceof ModelRendererExtended
&& model.bipedLeftLeg instanceof ModelRendererExtended
&& model.bipedHeadwear instanceof ModelRendererExtended;
}
/** Resets the rotation of this model part to the angle set by the parent model. */
public void resetRotation(){
this.actualRotationX = Float.NaN;
this.actualRotationY = Float.NaN;
this.actualRotationZ = Float.NaN;
this.extraRotationX = 0;
this.extraRotationY = 0;
this.extraRotationZ = 0;
}
/** Sets the rotation of this model part, which will overwrite the angle set by the parent model. */
public void setRotation(float x, float y, float z){
this.actualRotationX = x;
this.actualRotationY = y;
this.actualRotationZ = z;
}
/** Sets the extra rotation of this model part, which will be added onto the angle set by the parent model. */
public void addRotation(float x, float y, float z){
this.extraRotationX = x;
this.extraRotationY = y;
this.extraRotationZ = z;
}
/** Sets the rotation of this model part to the same values as the given box. */
public void setRotationTo(ModelRendererExtended box){
this.actualRotationX = box.actualRotationX;
this.actualRotationY = box.actualRotationY;
this.actualRotationZ = box.actualRotationZ;
this.extraRotationX = box.extraRotationX;
this.extraRotationY = box.extraRotationY;
this.extraRotationZ = box.extraRotationZ;
}
// Delegate rendering, but fiddle with the angles first
@Override
public void render(float scale){
// Need to copy these over each time in case they were changed
delegate.showModel = this.showModel;
delegate.isHidden = this.isHidden;
delegate.rotationPointX = this.rotationPointX;
delegate.rotationPointY = this.rotationPointY;
delegate.rotationPointZ = this.rotationPointZ;
if(!Float.isNaN(actualRotationX) && !Float.isNaN(actualRotationY) && !Float.isNaN(actualRotationZ)){
delegate.rotateAngleX = actualRotationX;
delegate.rotateAngleY = actualRotationY;
delegate.rotateAngleZ = actualRotationZ;
}else{
delegate.rotateAngleX = this.rotateAngleX + extraRotationX;
delegate.rotateAngleY = this.rotateAngleY + extraRotationY;
delegate.rotateAngleZ = this.rotateAngleZ + extraRotationZ;
}
delegate.render(scale);
}
// Delegate all other methods
@Override
public void addChild(ModelRenderer renderer){
delegate.addChild(renderer);
}
@Override
public void renderWithRotation(float scale){
delegate.renderWithRotation(scale);
}
@Override
public void postRender(float scale){
// Exactly the same setup as above, just add item rotation/translation fields and setters
// float angle = 1;
// float radius = 10;
// delegate.rotationPointY -= radius * MathHelper.cos(angle);
// delegate.rotationPointZ -= radius * MathHelper.sin(angle);
// delegate.rotateAngleX += angle;
delegate.postRender(scale);
}
}
@@ -1,255 +0,0 @@
package electroblob.wizardry.client.animation;
import electroblob.wizardry.Wizardry;
import electroblob.wizardry.item.SpellActions;
import electroblob.wizardry.util.InventoryUtils;
import electroblob.wizardry.util.JavaUtils;
import net.minecraft.client.Minecraft;
import net.minecraft.client.entity.AbstractClientPlayer;
import net.minecraft.client.model.ModelBiped;
import net.minecraft.client.model.ModelPlayer;
import net.minecraft.client.model.ModelRenderer;
import net.minecraft.client.renderer.GlStateManager;
import net.minecraft.client.renderer.entity.Render;
import net.minecraft.client.renderer.entity.RenderLivingBase;
import net.minecraft.client.renderer.entity.RenderPlayer;
import net.minecraft.client.renderer.entity.layers.LayerBipedArmor;
import net.minecraft.client.renderer.entity.layers.LayerRenderer;
import net.minecraft.entity.EntityLivingBase;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.inventory.EntityEquipmentSlot;
import net.minecraft.item.EnumAction;
import net.minecraft.item.ItemStack;
import net.minecraft.util.EnumHand;
import net.minecraft.util.EnumHandSide;
import net.minecraftforge.client.ForgeHooksClient;
import net.minecraftforge.client.event.RenderPlayerEvent;
import net.minecraftforge.client.event.RenderSpecificHandEvent;
import net.minecraftforge.fml.common.Loader;
import net.minecraftforge.fml.common.Mod;
import net.minecraftforge.fml.common.ObfuscationReflectionHelper;
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
import net.minecraftforge.fml.relauncher.Side;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
/**
* Handles the setup and rendering events for custom player animations, as well as registering of animations. Addons
* can also register their own animations here, see {@link PlayerAnimator#registerAnimation(Animation)}.
* @author Electroblob
* @since Wizardry 4.3
* @see electroblob.wizardry.item.SpellActions SpellActions
*/
@Mod.EventBusSubscriber(Side.CLIENT)
public class PlayerAnimator {
// TODO: Attempt to extend this to all bipeds
private static final List<Animation> animations = new ArrayList<>();
/** Stores all registered layer renderers for lazy-loading of armour models later. */
private static final Map<RenderPlayer, List<LayerRenderer<? extends EntityLivingBase>>> playerLayers = new HashMap<>();
/** Stores the main model for each renderer plus all models for registered layer renderers. */
private static final Map<RenderPlayer, List<ModelBiped>> playerLayerModels = new HashMap<>();
/** Reflected into {@link RenderLivingBase}{@code #layerRenderers}. */
private static final Field layerRenderers;
/** True if wizardry's player animations have been overridden with another mod (currently only MoBends) */
private static boolean overridden;
static {
layerRenderers = ObfuscationReflectionHelper.findField(RenderLivingBase.class, "field_177097_h");
}
/**
* Registers a new player animation. This method should be called from the {@code init()} phase via a client proxy.
* @param animation The animation to register
*/
// N.B. This can (and will) be called before PlayerAnimator#init() is called
public static void registerAnimation(Animation animation){
if(animations.contains(animation)){
Wizardry.logger.warn("Animation {} is already registered!", animation.getName());
}else{
animations.add(animation);
}
}
/** Returns true if spellcasting animations are enabled, false otherwise. */
public static boolean areAnimationsEnabled(){
return Wizardry.settings.spellcastingAnimations && !overridden;
}
public static void init(){
if(Loader.isModLoaded("mobends")) overridden = true;
if(!areAnimationsEnabled()) return;
for(RenderPlayer renderer : Minecraft.getMinecraft().getRenderManager().getSkinMap().values()){
loadLayersForRenderer(renderer);
}
}
@SuppressWarnings("unchecked")
private static void loadLayersForRenderer(RenderPlayer renderer){
List<ModelBiped> models = new ArrayList<>();
ModelRendererExtended.wrap(renderer.getMainModel());
models.add(renderer.getMainModel());
try {
List<LayerRenderer<? extends EntityLivingBase>> layers = (List<LayerRenderer<? extends EntityLivingBase>>)layerRenderers.get(renderer);
// Save armour layers for lazy-loading later (ignore other layers so iteration is faster)
playerLayers.put(renderer, layers.stream().filter(l -> l instanceof LayerBipedArmor).collect(Collectors.toList()));
for(LayerRenderer<?> layer : layers){
for(Field field : JavaUtils.getAllFields(layer.getClass())){
field.setAccessible(true);
// If your layer model doesn't extend ModelBiped, you DESERVE to be incompatible!
// (Just kidding... there's nothing I can do about it anyway)
if(field.get(layer) instanceof ModelBiped){
ModelBiped model = (ModelBiped)field.get(layer);
ModelRendererExtended.wrap(model);
models.add(model);
}
}
}
}catch(IllegalAccessException e){
Wizardry.logger.error("Error during reflective access of render layers: ", e);
}
playerLayerModels.put(renderer, models);
}
private static void updateModels(EntityPlayer player, Render<?> renderer, float partialTicks, boolean firstPerson){
// Lazy-load models from custom renderers (some mods assign their custom renderers in postInit, so they might
// not be picked up from PlayerAnimator#init)
if(renderer instanceof RenderPlayer && !playerLayerModels.containsKey(renderer)){
loadLayersForRenderer((RenderPlayer)renderer);
}
List<ModelBiped> models = playerLayerModels.get(renderer);
// If no models were registered for the given renderer, don't do anything
if(models != null){
List<LayerRenderer<? extends EntityLivingBase>> layers = playerLayers.get(renderer);
// If no layers were registered for the given renderer there's no point trying to lazy-load anything!
if(layers != null){
// Biped armour is special because of Forge's armour item render hook
// This needs to be lazy-loaded because we need access to an actual item
for(LayerRenderer<? extends EntityLivingBase> layer : layers){
if(layer instanceof LayerBipedArmor){
for(EntityEquipmentSlot slot : InventoryUtils.ARMOUR_SLOTS){
ItemStack armour = player.getItemStackFromSlot(slot);
// This method could behave dynamically depending on stuff like NBT so there's not really any way to
// optimise it further, we *have* to retrieve the model every time (shouldn't be too bad though)
ModelBiped model = ForgeHooksClient.getArmorModel(player, armour, slot, ((LayerBipedArmor)layer).getModelFromSlot(slot));
if(!models.contains(model)){ // Ignore already-wrapped models
models.add(model);
ModelRendererExtended.wrap(model);
}
}
}
}
}
for(Animation animation : animations){
if(animation.shouldDisplay(player, firstPerson)){
boolean autoRotateSecondLayer = animation.autoRotateSecondLayer(player, firstPerson);
for(ModelBiped model : models){
if(!ModelRendererExtended.isWrapped(model)) continue; // Should never happen but somehow it does
animation.setRotationAngles(player, model, partialTicks, firstPerson);
if(autoRotateSecondLayer && model instanceof ModelPlayer){
alignSecondLayer((ModelPlayer)model);
}
}
}
}
}
}
/** Rotates the second layer boxes of the given (wrapped) player model to match the first layer. */
public static void alignSecondLayer(ModelPlayer model){
((ModelRendererExtended)model.bipedBodyWear) .setRotationTo((ModelRendererExtended)model.bipedBody);
((ModelRendererExtended)model.bipedRightArmwear).setRotationTo((ModelRendererExtended)model.bipedRightArm);
((ModelRendererExtended)model.bipedLeftArmwear) .setRotationTo((ModelRendererExtended)model.bipedLeftArm);
((ModelRendererExtended)model.bipedRightLegwear).setRotationTo((ModelRendererExtended)model.bipedBody);
((ModelRendererExtended)model.bipedLeftLegwear) .setRotationTo((ModelRendererExtended)model.bipedBody);
}
@SubscribeEvent
public static void onRenderHandEvent(RenderSpecificHandEvent event){
if(!areAnimationsEnabled()) return;
AbstractClientPlayer player = Minecraft.getMinecraft().player;
EnumAction action = event.getItemStack().getItemUseAction();
if(player.isHandActive() && player.getActiveHand() == event.getHand() && SpellActions.getSpellActions().contains(action)){
// Minecraft's item renderer helpfully doesn't have a default case for the usage action so it doesn't do any
// transformations at all (which is VERY ANNOYING!) - the following lines are from ItemRenderer#transformSideFirstPerson
int i = (player.getPrimaryHand() == EnumHandSide.RIGHT) == (event.getHand() == EnumHand.MAIN_HAND) ? 1 : -1;
GlStateManager.translate((float)i * 0.56F, -0.52F + event.getEquipProgress() * -0.6F, -0.72F);
}
updateModels(player, Minecraft.getMinecraft().getRenderManager().getEntityRenderObject(player), event.getPartialTicks(), true);
}
@SubscribeEvent
public static void onRenderPlayerPreEvent(RenderPlayerEvent.Pre event){
if(!areAnimationsEnabled()) return;
// boolean firstPerson = event.getEntityPlayer() == Minecraft.getMinecraft().player
// && Minecraft.getMinecraft().gameSettings.thirdPersonView == 0;
updateModels(event.getEntityPlayer(), event.getRenderer(), event.getPartialRenderTick(), false);
}
@SubscribeEvent
public static void onRenderPlayerPostEvent(RenderPlayerEvent.Post event){
if(!areAnimationsEnabled()) return;
List<ModelBiped> models = playerLayerModels.get(event.getRenderer());
if(models != null){
for(ModelBiped model : models){
for(ModelRenderer box : model.boxList){ // For ModelPlayer, this will include the second layer
// Some models have extra boxes, they (probably) don't need wrapping but we need this check!
if(box instanceof ModelRendererExtended){
((ModelRendererExtended)box).resetRotation();
}
}
}
}
}
}
@@ -1,35 +0,0 @@
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;
}
}
}
@@ -1,36 +0,0 @@
package electroblob.wizardry.client.audio;
import electroblob.wizardry.item.ISpellCastingItem;
import net.minecraft.entity.EntityLivingBase;
import net.minecraft.item.ItemStack;
import net.minecraft.util.SoundCategory;
import net.minecraft.util.SoundEvent;
/** A special type of moving sound that stops when the player stops charging up a spell, either by releasing the mouse
* button or when the charge-up time has elapsed. */
public class MovingSoundSpellCharge extends MovingSoundEntity<EntityLivingBase> {
public MovingSoundSpellCharge(EntityLivingBase entity, SoundEvent sound, SoundCategory category, float volume, float pitch, boolean repeat){
super(entity, sound, category, volume, pitch, repeat);
}
@Override
public void update(){
if(source.isHandActive()){
ItemStack stack = source.getActiveItemStack();
if(stack.getItem() instanceof ISpellCastingItem){
if(source.getItemInUseMaxCount() < ((ISpellCastingItem)stack.getItem()).getCurrentSpell(stack).getChargeup()){
super.update();
return;
}
}
}
this.donePlaying = true;
}
}
@@ -1,112 +0,0 @@
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<>();
// The issue we had here was that there is no way of hooking into the client tick loop between the sound manager
// update and the world tick (other than via the sounds themselves, but playing other sounds from there results in
// CMEs, which is why we made this class in the first place). This meant there was always a gap between consecutive
// sounds, which could be heard in-game as a weird audio artefact. The solution is simply to overlap the sounds by
// 1 tick, which is fairly straightforward for the end sound, but because the start sound has to finish for us to
// know when to play the others, this doubling-up solution is required (bit of a waste of resources but meh).
/** A dummy sound with effectively zero volume, played one tick in advance of the actual start sound so the loop
* sound can start one tick before the actual start sound ends. */
private final ISound dummyStart; // It's the stupidest system ever but it works!
private final ISound start;
private final ISound loop;
private final ISound end;
private boolean looping = false;
private boolean needsRemoving = false;
public SoundLoop(SoundEvent start, SoundEvent loop, SoundEvent end, SoundCategory category, float volume, ISoundFactory factory){
// The reason I've gone to the effort of having a factory for these is that we need SoundLoop to have control
// over which sounds are repeated and which aren't whilst keeping them private.
this.dummyStart = factory.create(start, category, 0.00001f, false); // Inaudible but not actually 0
this.start = factory.create(start, category, volume, false);
this.loop = factory.create(loop, category, volume, true);
this.end = factory.create(end, category, volume, false);
}
@Override
public void update(){
// Check every tick if the start sound is done playing and if so, start the loop sound
if(!looping && !Minecraft.getMinecraft().getSoundHandler().isSoundPlaying(dummyStart)){
Minecraft.getMinecraft().getSoundHandler().playSound(loop);
looping = true;
}
}
/** Starts playing the end part immediately and marks the loop part to stop next tick. This may be called from
* subclasses or externally depending on the implementation. */
// For continuous spell sounds it's internally
public void endLoop(){
Minecraft.getMinecraft().getSoundHandler().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;
}
/** Stops the start and loop sounds immediately. */
protected void stopStartAndLoop(){
Minecraft.getMinecraft().getSoundHandler().stopSound(dummyStart);
Minecraft.getMinecraft().getSoundHandler().stopSound(start);
Minecraft.getMinecraft().getSoundHandler().stopSound(loop);
}
// Static methods
public static void addLoop(SoundLoop loop){
activeLoops.add(loop);
// Do this here rather than in the constructor in case someone wants to play the loop later or reuse it
Minecraft.getMinecraft().getSoundHandler().playSound(loop.dummyStart);
Minecraft.getMinecraft().getSoundHandler().playDelayedSound(loop.start, 2); // 1 seems to be insufficient
}
@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.stream().filter(s -> s.needsRemoving).forEach(SoundLoop::stopStartAndLoop);
activeLoops.removeIf(s -> s.needsRemoving);
activeLoops.forEach(SoundLoop::update); // Do this last to allow a 1-tick overlap
}
}
@FunctionalInterface
public interface ISoundFactory {
ISound create(SoundEvent sound, SoundCategory category, float volume, boolean repeat);
}
}
@@ -1,117 +0,0 @@
package electroblob.wizardry.client.audio;
import electroblob.wizardry.data.DispenserCastingData;
import electroblob.wizardry.registry.WizardrySounds;
import electroblob.wizardry.spell.Spell;
import electroblob.wizardry.util.EntityUtils;
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, float volume, ISoundFactory factory, Spell spell){
super(start, loop, end, WizardrySounds.SPELLS, volume, 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, volume, (sound, category, v, repeat) -> new MovingSoundEntity<>(source, sound, category, v, pitch, repeat), spell);
this.source = source;
}
@Override
protected boolean stillCasting(Spell spell){
return EntityUtils.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, sndVolume, (sound, category, v, r) -> new PositionedSound(sound, category){{
// ...et voila, we can just set protected fields as we please using external variables
this.xPosF = (float)x;
this.yPosF = (float)y;
this.zPosF = (float)z;
this.repeat = r;
this.volume = v;
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;
}
}
}
@@ -1,109 +0,0 @@
package electroblob.wizardry.client.gui;
import electroblob.wizardry.Wizardry;
import electroblob.wizardry.client.DrawingUtils;
import electroblob.wizardry.registry.WizardryTabs;
import electroblob.wizardry.spell.Spell;
import net.minecraft.client.gui.GuiTextField;
import net.minecraft.client.gui.inventory.GuiContainerCreative;
import net.minecraft.client.resources.I18n;
import net.minecraft.creativetab.CreativeTabs;
import net.minecraft.util.text.Style;
import net.minecraft.util.text.TextFormatting;
import net.minecraftforge.client.event.GuiScreenEvent;
import net.minecraftforge.fml.common.Mod.EventBusSubscriber;
import net.minecraftforge.fml.common.ObfuscationReflectionHelper;
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
import net.minecraftforge.fml.common.gameevent.TickEvent.ClientTickEvent;
import net.minecraftforge.fml.common.gameevent.TickEvent.Phase;
import net.minecraftforge.fml.relauncher.Side;
import java.lang.reflect.Field;
import java.util.Locale;
@EventBusSubscriber(Side.CLIENT)
public class CustomCreativeSearchHandler {
private static final int SEARCH_TOOLTIP_HOVER_TIME = 20;
private static final Style TOOLTIP_SYNTAX = new Style().setColor(TextFormatting.YELLOW);
private static final Style TOOLTIP_BODY = new Style().setColor(TextFormatting.WHITE);
/** Reflected into {@code GuiContainerCreative#searchField} */
private static final Field searchField;
private static GuiTextField currentSearchField = null;
private static int searchBarHoverTime;
static {
searchField = ObfuscationReflectionHelper.findField(GuiContainerCreative.class, "field_147062_A");
}
@SubscribeEvent
public static void onInitGuiEvent(GuiScreenEvent.InitGuiEvent.Post event){
// Reduces reflection as much as possible - searchField is created on GUI init and never reassigned so we need
// not (and should not!) use reflection to retrieve it every time a key is typed
if(event.getGui() instanceof GuiContainerCreative){
try{
currentSearchField = (GuiTextField)searchField.get(event.getGui());
}catch(IllegalAccessException e){
e.printStackTrace();
}
}
}
@SubscribeEvent
public static void onKeyboardInputEvent(GuiScreenEvent.KeyboardInputEvent.Post event){
// Custom creative tab search behaviour
if(event.getGui() instanceof GuiContainerCreative){
GuiContainerCreative gui = (GuiContainerCreative)event.getGui();
CreativeTabs tab = CreativeTabs.CREATIVE_TAB_ARRAY[gui.getSelectedTabIndex()];
if(tab == WizardryTabs.SPELLS){
GuiContainerCreative.ContainerCreative container = (GuiContainerCreative.ContainerCreative)gui.inventorySlots;
container.itemList.clear(); // Required or duplicates will appear!
String searchText = currentSearchField.getText().toLowerCase(Locale.ROOT);
tab.displayAllRelevantItems(container.itemList);
if(!searchText.isEmpty()){
container.itemList.removeIf(s -> !Spell.byMetadata(s.getMetadata()).matches(searchText));
container.scrollTo(0); // Seems to refresh the GUI somehow so it displays correctly
}
}
}
}
@SubscribeEvent
public static void onClientTickEvent(ClientTickEvent event){
if(event.phase == Phase.END && searchBarHoverTime > 0 && searchBarHoverTime < SEARCH_TOOLTIP_HOVER_TIME){
searchBarHoverTime++;
}
}
@SubscribeEvent
public static void onDrawScreenPostEvent(GuiScreenEvent.DrawScreenEvent.Post event){
if(event.getGui() instanceof GuiContainerCreative && currentSearchField != null){
GuiContainerCreative gui = (GuiContainerCreative)event.getGui();
CreativeTabs tab = CreativeTabs.CREATIVE_TAB_ARRAY[gui.getSelectedTabIndex()];
if(tab == WizardryTabs.SPELLS && DrawingUtils.isPointInRegion(currentSearchField.x, currentSearchField.y, currentSearchField.width, currentSearchField.height, event.getMouseX(), event.getMouseY())){
if(searchBarHoverTime == 0){
searchBarHoverTime++;
}else if(searchBarHoverTime == SEARCH_TOOLTIP_HOVER_TIME){
event.getGui().drawHoveringText(I18n.format("container." + Wizardry.MODID + ":arcane_workbench.search_tooltip",
TOOLTIP_SYNTAX.getFormattingCode(), TOOLTIP_BODY.getFormattingCode()), event.getMouseX(), event.getMouseY());
}
}
}else{
searchBarHoverTime = 0;
}
}
}
File diff suppressed because it is too large Load Diff
@@ -1,54 +0,0 @@
package electroblob.wizardry.client.gui;
import electroblob.wizardry.Wizardry;
import electroblob.wizardry.inventory.ContainerBookshelf;
import electroblob.wizardry.tileentity.TileEntityBookshelf;
import net.minecraft.client.gui.inventory.GuiContainer;
import net.minecraft.client.renderer.GlStateManager;
import net.minecraft.entity.player.InventoryPlayer;
import net.minecraft.inventory.IInventory;
import net.minecraft.util.ResourceLocation;
import net.minecraftforge.fml.relauncher.Side;
import net.minecraftforge.fml.relauncher.SideOnly;
@SideOnly(Side.CLIENT)
public class GuiBookshelf extends GuiContainer {
private static final ResourceLocation TEXTURE = new ResourceLocation(Wizardry.MODID, "textures/gui/container/bookshelf.png");
/** The player inventory bound to this GUI. */
private final InventoryPlayer playerInventory;
/** The inventory contained within the corresponding bookshelf. */
public IInventory bookshelfInventory;
public GuiBookshelf(InventoryPlayer playerInv, TileEntityBookshelf bookshelfInv){
super(new ContainerBookshelf(playerInv, bookshelfInv));
this.playerInventory = playerInv;
this.bookshelfInventory = bookshelfInv;
this.allowUserInput = false;
this.ySize = 148;
}
@Override
public void drawScreen(int mouseX, int mouseY, float partialTicks){
this.drawDefaultBackground();
super.drawScreen(mouseX, mouseY, partialTicks);
this.renderHoveredToolTip(mouseX, mouseY);
}
@Override
protected void drawGuiContainerForegroundLayer(int mouseX, int mouseY){
String s = this.bookshelfInventory.getDisplayName().getUnformattedText();
this.fontRenderer.drawString(s, 8, 6, 4210752);
this.fontRenderer.drawString(this.playerInventory.getDisplayName().getUnformattedText(), 8, this.ySize - 96 + 2, 4210752);
}
@Override
protected void drawGuiContainerBackgroundLayer(float partialTicks, int mouseX, int mouseY){
GlStateManager.color(1.0F, 1.0F, 1.0F, 1.0F);
this.mc.getTextureManager().bindTexture(TEXTURE);
int i = (this.width - this.xSize) / 2;
int j = (this.height - this.ySize) / 2;
this.drawTexturedModalRect(i, j, 0, 0, this.xSize, this.ySize);
}
}
@@ -1,18 +0,0 @@
package electroblob.wizardry.client.gui;
import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.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, "");
}
@Override
public void drawButton(Minecraft minecraft, int mouseX, int mouseY, float partialTicks){
this.hovered = mouseX >= this.x && mouseY >= this.y && mouseX < this.x + this.width && mouseY < this.y + this.height;
}
}
@@ -1,90 +0,0 @@
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.InventoryUtils;
import electroblob.wizardry.util.SpellModifiers;
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)
&& InventoryUtils.getHotbar(Minecraft.getMinecraft().player).stream().anyMatch(s -> Resurrection.canStackResurrect(s, Minecraft.getMinecraft().player))){
event.getButtonList().add(new GuiButtonResurrect(event.getButtonList().size(), event.getGui().width / 2 - 100,
event.getGui().height / 4 + 120, "spell." + Spells.resurrection.getRegistryName() + ".button"));
timeSinceDeath = 0;
}
}
@SubscribeEvent
public static void onGuiScreenActionPerformedEvent(GuiScreenEvent.ActionPerformedEvent event){
if(event.getGui() instanceof GuiGameOver){
ItemStack stack = InventoryUtils.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;
}
}
}
}

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