Compare commits

...

76 Commits
1.0 ... master

Author SHA1 Message Date
5381263113 Auto-Publish
All checks were successful
SorceryCraft/pipeline/head This commit looks good
2020-12-15 14:04:06 -05:00
263466eed3 1.16.4
All checks were successful
SorceryCraft/pipeline/head This commit looks good
2020-12-15 14:00:42 -05:00
10bfbdb53d Update Loom
All checks were successful
SorceryCraft/pipeline/head This commit looks good
2020-06-24 18:09:42 -04:00
162df5c962 Port To 1.16.1
All checks were successful
SorceryCraft/pipeline/head This commit looks good
2020-06-24 11:43:53 -04:00
3f06723a1f 1.2.9
All checks were successful
SorceryCraft/pipeline/head This commit looks good
Fix Dedicated Server Crash
2020-05-24 16:55:23 -04:00
700b970a04 Fix Config
All checks were successful
SorceryCraft/pipeline/head This commit looks good
2020-05-02 15:21:29 -04:00
3bfe235d97 Fix HTML Publish
All checks were successful
SorceryCraft/pipeline/head This commit looks good
2020-05-02 14:54:43 -04:00
48fef73096 20w18a
Some checks failed
SorceryCraft/pipeline/head There was a failure building this commit
2020-05-02 14:42:16 -04:00
504a52597d 1.2.8
All checks were successful
SorceryCraft/pipeline/head This commit looks good
Register Loot Table Function
2020-04-11 13:49:08 -04:00
55808ca049 Fix Gradle
All checks were successful
SorceryCraft/pipeline/head This commit looks good
2020-04-03 11:47:55 -04:00
e5aa71f751 20w14a
All checks were successful
SorceryCraft/pipeline/head This commit looks good
2020-04-03 11:45:44 -04:00
0664de5de5 Use @Unique
All checks were successful
SorceryCraft/pipeline/head This commit looks good
2020-03-27 14:07:47 -04:00
4d0e4b6411 20w13b
All checks were successful
SorceryCraft/pipeline/head This commit looks good
2020-03-26 11:48:22 -04:00
114c3a03d2 1.2.7
All checks were successful
SorceryCraft/pipeline/head This commit looks good
Fix Cooling Spell Bug
2020-03-25 16:19:41 -04:00
6b3445e1f6 Update Fabric API
All checks were successful
SorceryCraft/pipeline/head This commit looks good
2020-03-25 15:04:33 -04:00
14f254af3d Update Cloth Config
All checks were successful
SorceryCraft/pipeline/head This commit looks good
2020-03-25 14:02:03 -04:00
8a23afeed0 20w13a
All checks were successful
SorceryCraft/pipeline/head This commit looks good
2020-03-25 13:58:00 -04:00
8ca4986714 1.2.6
All checks were successful
SorceryCraft/pipeline/head This commit looks good
Fix Potential Client-Server De-sync Bug
2020-03-24 15:31:35 -04:00
fb3ba1411f Remove Unused Import
All checks were successful
SorceryCraft/pipeline/head This commit looks good
2020-03-24 13:12:12 -04:00
3b91e3daf8 1.2.5
All checks were successful
SorceryCraft/pipeline/head This commit looks good
Fix Casting Table Bug
2020-03-24 13:05:03 -04:00
acf08c7fa6 1.2.4
All checks were successful
SorceryCraft/pipeline/head This commit looks good
Optimize Packets
Allow Command Blocks to use ```/spell``` Command
Namespace Statistics
2020-03-24 09:38:29 -04:00
5552fe5cd8 Optimize Packets
Allow Command Blocks to use /spell Command
2020-03-24 09:35:42 -04:00
d381beff16 Namespace Statistics
All checks were successful
SorceryCraft/pipeline/head This commit looks good
2020-03-23 15:13:17 -04:00
ff50bd3851 Update CurseForge Script
All checks were successful
SorceryCraft/pipeline/head This commit looks good
2020-03-22 15:48:13 -04:00
76f316f7bd 1.2.3
All checks were successful
SorceryCraft/pipeline/head This commit looks good
Tweak Cooling Spell
Tweak Versioning
2020-03-22 15:30:46 -04:00
df3c1dcffd Add Annotations
All checks were successful
SorceryCraft/pipeline/head This commit looks good
2020-03-21 15:30:05 -04:00
d2d33c6159 1.2.2
All checks were successful
SorceryCraft/pipeline/head This commit looks good
Allow /spell command to work with multiple players
2020-03-21 13:25:28 -04:00
f40e44e366 1.2.1
All checks were successful
SorceryCraft/pipeline/head This commit looks good
Fix Launching Without ModMenu
2020-03-21 11:49:58 -04:00
4ebae792ac 1.2
All checks were successful
SorceryCraft/pipeline/head This commit looks good
Update API (More Consistent Package Names)
Fix Bug When Casting Spell
2020-03-20 22:05:52 -04:00
3fe89d2d95 1.1.21
All checks were successful
SorceryCraft/pipeline/head This commit looks good
Update Mappings
Tweak Teleport and Flame Spells
2020-03-19 23:06:38 -04:00
b44ca31175 1.1.20
All checks were successful
SorceryCraft/pipeline/head This commit looks good
Update to 20w12a
2020-03-18 17:21:39 -04:00
9d1cad6223 1.1.19
All checks were successful
SorceryCraft/pipeline/head This commit looks good
Update API Docs
Allow Setting Custom Display name for Spell
2020-03-18 11:56:25 -04:00
373f0ad8d3 1.1.18
All checks were successful
SorceryCraft/pipeline/head This commit looks good
Fix Crash When Using Dedicated Server
2020-03-17 21:30:59 -04:00
1c033d5a0d 1.1.17
All checks were successful
SorceryCraft/pipeline/head This commit looks good
Update Mappings
Add Spell-related Advancements
2020-03-17 20:42:02 -04:00
1dbbd55376 Add JavaDoc Link
All checks were successful
SorceryCraft/pipeline/head This commit looks good
2020-03-17 14:34:42 -04:00
019c588add 1.1.16
All checks were successful
SorceryCraft/pipeline/head This commit looks good
Consistent Gradle Project Name
2020-03-17 14:25:35 -04:00
5e24602ca8 1.1.15
All checks were successful
SorceryCraft/pipeline/head This commit looks good
Build JavaDoc Jar
2020-03-17 14:08:55 -04:00
ee78088250 1.1.14
All checks were successful
SorceryCraft/pipeline/head This commit looks good
Update Config Lang
2020-03-17 13:36:53 -04:00
c592b9e9af 1.1.13
All checks were successful
SorceryCraft/pipeline/head This commit looks good
Update Mappings
Improve /spell command
2020-03-17 13:26:10 -04:00
db54bdf2ad Fix Mod Menu Version
All checks were successful
SorceryCraft/pipeline/head This commit looks good
2020-03-16 15:07:18 -04:00
a3be3712d4 1.1.12
All checks were successful
SorceryCraft/pipeline/head This commit looks good
Update Mappings
Add Config Screen
Clarify Terms
2020-03-16 11:21:06 -04:00
998e514cd9 Minor changes
All checks were successful
SorceryCraft/pipeline/head This commit looks good
2020-03-14 18:11:28 -04:00
4bc4821543 1.1.11
All checks were successful
SorceryCraft/pipeline/head This commit looks good
Update Mappings
Add Lightning Spell
2020-03-13 18:05:12 -04:00
5a4cd89d68 1.1.10
All checks were successful
SorceryCraft/pipeline/head This commit looks good
Update to 20w11a
Enhance Flame Spell
2020-03-11 17:34:52 -04:00
df7826d584 1.1.9
All checks were successful
SorceryCraft/pipeline/head This commit looks good
Fix Scrolling Bug
2020-03-11 09:06:10 -04:00
6d8376b8a9 1.1.8
All checks were successful
SorceryCraft/pipeline/head This commit looks good
Tweak Casting Table UI
2020-03-10 20:47:27 -04:00
3a173b25d1 1.1.7
All checks were successful
SorceryCraft/pipeline/head This commit looks good
Improve JavaDoc
Update Mappings
Tweak Spell Texture
2020-03-10 17:32:00 -04:00
dca3e4718d 1.1.6
All checks were successful
SorceryCraft/pipeline/head This commit looks good
Add Spell-related Statistics
2020-03-09 21:29:35 -04:00
815fd3d7a7 Restructure README
All checks were successful
SorceryCraft/pipeline/head This commit looks good
2020-03-09 20:49:40 -04:00
b22deba559 1.1.5
All checks were successful
SorceryCraft/pipeline/head This commit looks good
Tweak Spell Particles
2020-03-09 20:39:25 -04:00
a8b12d9d5c 1.1.4
All checks were successful
SorceryCraft/pipeline/head This commit looks good
Tweak Casting Table Texture
Tweak Casting Table Recipe
Update Mappings
2020-03-09 18:47:31 -04:00
7dac950cca 1.1.3
All checks were successful
SorceryCraft/pipeline/head This commit looks good
Fix Shift-Clicking in Casting Table
2020-03-08 22:00:36 -04:00
738037137d 1.1.2
All checks were successful
SorceryCraft/pipeline/head This commit looks good
Update Casting Table Texture
Generate Spells in Pillager Outposts and Woodland Mansions
2020-03-08 19:49:37 -04:00
2f262b9cf4 1.1.1
All checks were successful
SorceryCraft/pipeline/head This commit looks good
Add a unique texture for the Spell
2020-03-08 17:50:44 -04:00
e431ddb057 1.1
All checks were successful
SorceryCraft/pipeline/head This commit looks good
API is now stable
Fix /spell suggestions
2020-03-08 13:45:52 -04:00
f106b485fb Update API Docs
All checks were successful
SorceryCraft/pipeline/head This commit looks good
Update Gradle
2020-03-08 12:26:12 -04:00
27cdf09bc2 1.0.14
All checks were successful
SorceryCraft/pipeline/head This commit looks good
Update Spell API
2020-03-08 12:06:43 -04:00
8cf31594e0 1.0.13
All checks were successful
SorceryCraft/pipeline/head This commit looks good
Rename SpellRegistry.registerSpell to SpellRegistry.register
2020-03-08 11:41:16 -04:00
963900a65d 1.0.12
All checks were successful
SorceryCraft/pipeline/head This commit looks good
Update Mappings
Add API Docs
2020-03-08 11:34:47 -04:00
516d7909f1 Update API Docs
All checks were successful
SorceryCraft/pipeline/head This commit looks good
2020-03-08 11:15:34 -04:00
cf3376ec90 Add API Docs
All checks were successful
SorceryCraft/pipeline/head This commit looks good
2020-03-08 11:10:50 -04:00
07d1d44f52 1.0.11
All checks were successful
SorceryCraft/pipeline/head This commit looks good
Allow Spells to Affect Blocks
Add Cooling Spell
2020-03-07 15:12:46 -05:00
b88132eb29 1.0.10
All checks were successful
SorceryCraft/pipeline/head This commit looks good
Add Issues Link
2020-03-06 20:33:54 -05:00
bbfad9303a 1.0.9
All checks were successful
SorceryCraft/pipeline/head This commit looks good
Update Particles
2020-03-06 19:21:27 -05:00
abc16c2bbb Fix CurseForge Changelog
All checks were successful
SorceryCraft/pipeline/head This commit looks good
2020-03-06 18:52:13 -05:00
c1e935764d 1.0.8
All checks were successful
SorceryCraft/pipeline/head This commit looks good
Update to 20w10a
Add the /spell learn, /spell add, and /spell remove commands
Use CurseForge
2020-03-06 18:41:25 -05:00
eabca94f00 1.0.7
All checks were successful
SorceryCraft/pipeline/head This commit looks good
Play Failure Sound when Inward Spell Fails
2020-03-03 21:38:15 -05:00
8448acca6a 1.0.6
All checks were successful
SorceryCraft/pipeline/head This commit looks good
Namespace PlayerEntity Mixin
2020-03-03 20:38:30 -05:00
dd228acf79 1.0.5
All checks were successful
SorceryCraft/pipeline/head This commit looks good
Clean up code
2020-03-03 20:22:40 -05:00
e2cfd1d656 1.0.4
All checks were successful
SorceryCraft/pipeline/head This commit looks good
Migrate old worlds to new ID system
2020-03-03 19:09:07 -05:00
28fa7115c6 1.0.3
All checks were successful
SorceryCraft/pipeline/head This commit looks good
Tweak Loot Tables
Fix Dispenser Bugs
Fix Inward Bugs
2020-03-03 18:08:22 -05:00
cdaac350db 1.0.2
All checks were successful
SorceryCraft/pipeline/head This commit looks good
Add Inward Spell
Tweak Rebound Mechanic
Allow Dispensers to Shoot Spells
2020-03-02 21:43:25 -05:00
1d070db3a7 Improve Crafting Recipes in README.md
All checks were successful
SorceryCraft/pipeline/head This commit looks good
2020-03-02 18:23:12 -05:00
0430a5724e Add CHANGELOG.md
All checks were successful
SorceryCraft/pipeline/head This commit looks good
Update README.md
2020-03-02 18:06:47 -05:00
b7bc20b9e8 1.0.1
All checks were successful
SorceryCraft/pipeline/head This commit looks good
2020-03-02 17:30:12 -05:00
32f7371291 Fix Crash
All checks were successful
SorceryCraft/pipeline/head This commit looks good
Add Teleport Spell
2020-03-02 17:11:37 -05:00
67 changed files with 2563 additions and 931 deletions

4
.gitignore vendored
View File

@ -22,4 +22,6 @@ bin/
# fabric # fabric
run/ run/
remappedSrc/

115
API.md Normal file
View File

@ -0,0 +1,115 @@
# API
## Getting Started
1. Add Dependency to Gradle
```gradle
repositories {
maven { url 'https://maven.thebrokenrail.com' }
}
dependencies {
modImplementation 'com.thebrokenrail:sorcerycraft:VERSION'
// VERSION = "<Mod Version>+<MC Version>", for example "1.2.4+20w12a"
}
```
2. Add Dependency to ```fabric.mod.json```
```json
{
"depends": {
"sorcerycraft": "1.2.x"
}
}
```
3. Create a class extending ```com.thebrokenrail.sorcerycraft.spell.api.Spell```
```java
public class ExampleSpell extends Spell {
public ExampleSpell(Identifier id, int level) {
super(id, level);
}
@Override
public void execute(Entity target, Entity source, Entity attacker) {
// OPTIONAL
// Called when ExampleSpell hits an entity
// Override this if you want the spell to affect entities
}
@Override
public void execute(World world, BlockHitResult hitResult) {
// OPTIONAL
// Called when ExampleSpell hits a block
// Override this if you want the spell to affect blocks
}
@Override
public int getXPCost() {
// REQUIRED
// Return the amount of levels required to make ExampleSpell in a Casting Table
switch (getLevel()) {
case 0: {
return 4;
}
case 1: {
return 8;
}
}
return -1;
}
@Override
public ItemStack getItemCost() {
// REQUIRED
// Return the item(s) required to make ExampleSpell in a Casting Table
// Return ItemStack.EMPTY if an item is not required
switch (getLevel()) {
case 0: {
return new ItemStack(Items.SUGAR_CANE);
}
case 1: {
return new ItemStack(Items.BOOK);
}
}
return ItemStack.EMPTY;
}
@Override
public int getMaxLevel() {
// REQUIRED
// Return ExampleSpell's maximum level
return 2;
}
@Override
public Text getTranslation() {
// OPTIONAL
// Return a custom display name for ExampleSpell
// Override this only if you want a custom display name
return new LiteralText("Example " + (getLevel() + 1));
}
}
```
4. Register the spell in your ModInitializer
```java
public class ExampleMod implements ModInitializer {
public static final Identifier EXAMPLE_SPELL = SpellRegistry.register(new Identifier("modid", "example_spell"), ExampleSpell.class);
}
```
5. Add Spell Translation (skip this if you have overridden ```Spell.getTranslation()``)
```json
{
"spell.modid.example_spell": "Example"
}
```
## Useful Methods
- ```(non-static) Spell.getLevel()```
- ```(non-static) Spell.getID()```
- ```(static) SpellRegistry.register()```
## API Stability
APIs are only guaranteed to be stable in the ```com.thebrokenrail.sorcerycraft.api``` package, it is unrecommended to rely on any SorceryCraft classes outside of this package.
## Spell Levels
Spell levels are 0-indexed, if you have a level 1 Example Spell, ```Spell.getLevel()``` wil return 0, and if it is level 2 ```Spell.getLevel()``` wil return 1, ```Spell.getMaxSpell()``` should be the maximum-acceptable value of ```Spell.getLevel() + 1```, so if Example Spell has levels 1-2, ```Spell.getMaxLevel()``` should return 2.
## JavaDoc
[View JavaDoc](https://jenkins.thebrokenrail.com/job/SorceryCraft/job/master/JavaDoc/)

169
CHANGELOG.md Normal file
View File

@ -0,0 +1,169 @@
# Changelog
**1.2.9**
* Fix Dedicated Server Crash
**1.2.8**
* Register Loot Table Function
**1.2.7**
* Fix Cooling Spell Bug
**1.2.6**
* Fix Potential Client-Server De-sync Bug
**1.2.5**
* Fix Casting Table Bug
**1.2.4**
* Optimize Packets
* Allow Command Blocks to use ```/spell``` Command
* Namespace Statistics
**1.2.3**
* Tweak Cooling Spell
* Tweak Versioning
**1.2.2**
* Allow ```/spell``` command to work with multiple players
**1.2.1**
* Fix Launching Without ModMenu
**1.2**
* Update Mappings
* Update API (More Consistent Package Names)
* Fix Bug When Casting Spell
**1.1.21**
* Update Mappings
* Tweak Teleport and Flame Spells
**1.1.20**
* Update to 20w12a
**1.1.19**
* Update API Docs
* Allow Setting Custom Display name for Spell
**1.1.18**
* Fix Crash When Using Dedicated Server
**1.1.17**
* Update Mappings
* Add Spell-related Advancements
**1.1.16**
* Consistent Gradle Project Name
**1.1.15**
* Build JavaDoc Jar
**1.1.14**
* Update Config Lang
**1.1.13**
* Update Mappings
* Improve ```/spell``` command
**1.1.12**
* Update Mappings
* Add Config Screen
* Clarify Terms
**1.1.11**
* Update Mappings
* Add Lightning Spell
**1.1.10**
* Update to 20w11a
* Enhance Flame Spell
**1.1.9**
* Fix Scrolling Bug
**1.1.8**
* Tweak Casting Table UI
**1.1.7**
* Improve JavaDoc
* Update Mappings
* Tweak Spell Texture
**1.1.6**
* Add Spell-related Statistics
**1.1.5**
* Tweak Spell Particles
**1.1.4**
* Tweak Casting Table Texture
* Tweak Casting Table Recipe
* Update Mappings
**1.1.3**
* Fix Shift-Clicking in Casting Table
**1.1.2**
* Update Casting Table Texture
* Generate Spells in Pillager Outposts and Woodland Mansions
**1.1.1**
* Add a unique texture for the Spell
**1.1**
* API is now stable
* Fix ```/spell``` suggestions
**1.0.14**
* Update Spell API
**1.0.13**
* Rename ```SpellRegistry.registerSpell``` to ```SpellRegistry.register```
**1.0.12**
* Update Mappings
* Add API Docs
**1.0.11**
* Allow Spells to Affect Blocks
* Add Cooling Spell
**1.0.10**
* Add Issues Link
**1.0.9**
* Update Particles
**1.0.8**
* Update to 20w10a
* Add the ```/spell learn```, ```/spell add```, and ```/spell remove``` commands
* Use CurseForge
**1.0.7**
* Play Failure Sound when Inward Spell Fails
**1.0.6**
* Namespace PlayerEntity Mixin
**1.0.5**
* Clean up code
**1.0.4**
* Migrate old worlds to new ID system
**1.0.3**
* Tweak Loot Tables
* Fix Dispenser Bugs
* Fix Inward Bugs
**1.0.2**
* Add Inward Spell
* Tweak Rebound Mechanic
* Allow Dispensers to Shoot Spells
**1.0.1**
* Add Teleport Spell
**1.0**
* Initial Release

23
Jenkinsfile vendored
View File

@ -7,11 +7,32 @@ pipeline {
stages { stages {
stage('Build') { stage('Build') {
steps { steps {
sh './gradlew build' sh './gradlew build javadoc'
} }
post { post {
success { success {
archiveArtifacts artifacts: 'build/libs/*', fingerprint: true archiveArtifacts artifacts: 'build/libs/*', fingerprint: true
publishHTML target: [
allowMissing: false,
alwaysLinkToLastBuild: false,
keepAll: false,
reportDir: 'build/docs/javadoc',
reportFiles: 'index.html',
reportName: 'JavaDoc'
]
}
}
}
stage('Publish') {
when {
expression {
return sh(returnStdout: true, script: 'git tag --contains').trim().length() > 0
}
}
steps {
withCredentials([string(credentialsId: 'curseforge_key', variable: 'CURSEFORGE_KEY')]) {
sh './gradlew -Pcurseforge.api_key="${CURSEFORGE_KEY}" curseforge publish'
} }
} }
} }

View File

@ -1,17 +1,93 @@
# SorceryCraft # SorceryCraft
Cast Spells in Minecraft! Cast Spells in Minecraft!
This mod supports the Minecraft 1.16 snapshots. This mod currently supports the Minecraft 1.16 snapshots.
## Spells ## What are Spells?
Spells are found throughout the world in chests. When you pick up a Spell you will learn it, one you learn a Spell you can apply it to a blank or existing Spell in the Casting Table. You can cast Spells by right-clicking. There is also a 30% chance the spell will rebound and hit you instead, unless the "Steadfast" Spell is applied. Spells are found throughout the world in chests. When you pick up a Spell you will "discover" it, once you "discover" a spell you can apply it to a blank or existing spell in the Casting Table. You can cast spells by right-clicking. There is also a 30% chance (by default) that the spell will rebound and hit you instead, unless the "Steadfast" spell is applied.
## Casting Table ## What's a Casting Table?
You can apply Spells to blank or existing Spells in the Casting Table. Doing so will require a certain amount of levels, and sometimes may require an item. You can apply Spells to blank or existing spells in the Casting Table. Doing so will require a certain amount of levels, and may require an item.
## Crafting ## Crafting
#### Blank Spell #### Blank Spell
Craft a blank Spell by placing a Paper in the center of a Crafting Table, and surround it with 4 Lapis Lazuli in a diamond shape. <table>
<tr>
<td></td>
<td>Lapis Lazuli</td>
<td></td>
</tr>
<tr>
<td>Lapis Lazuli</td>
<td>Paper</td>
<td>Lapis Lazuli</td>
</tr>
<tr>
<td></td>
<td>Lapis Lazuli</td>
<td></td>
</tr>
</table>
#### Casting table #### Casting Table
Craft a Casting Table by placing a Diamond in the center of a Crafting Table, place 4 Lapis Lazuli in the corners, and fill in the rest with 4 Cobblestone. <table>
<tr>
<td>Lapis Lazuli</td>
<td>Cobblestone</td>
<td>Lapis Lazuli</td>
</tr>
<tr>
<td>Cobblestone</td>
<td>Diamond</td>
<td>Cobblestone</td>
</tr>
<tr>
<td>Lapis Lazuli</td>
<td>Cobblestone</td>
<td>Lapis Lazuli</td>
</tr>
</table>
## Spells
| Spell | Max Level | Description |
| --- | --- | --- |
| Flame | 2 | Sets target on fire. |
| Damage | 2 | Damages target. |
| Heal | 2 | Heals target. |
| Dissolve | 1 | Removes target's status effects. |
| Levitate | 2 | Gives target the Levitation effect. |
| Steadfast | 1 | Prevents the spell from failing. This spell does nothing on its own. |
| Teleport | 3 | Teleports the target to a random location. |
| Inward | 1 | Causes the spell to target the caster. If the spell fails instead of rebounding, it will just do nothing. This spell does nothing on its own. |
| Cooling | 1 | Extinguish the target if they are on fire. |
| Lightning | 1 | Strikes the target with lightning. |
## ```/spell``` Command
This command requires OP permissions.
#### ```/spell forget <player>```
This command clears all known spells from the given player.
#### ```/spell forget <player> <spell-id>```
This command clears the specified spell from the given player.
#### ```/spell list <player>```
This lists all the spells the given player knows.
#### ```/spell discover <player>```
This adds all spells to the specified player's discovered spells list.
#### ```/spell discover <player> <spell-id> <level>```
This adds the specified spell to the specified player's discovered spells list.
#### ```/spell apply <player> <spell-id> <level>```
This adds the specified spell to the item in the specified player's main hand.
#### ```/spell remove <player> <spell-id>```
This removes the specified spell from the item in the specified player's main hand.
## API
[View API](API.md)
## Changelog
[View Changelog](CHANGELOG.md)

View File

@ -1,36 +1,53 @@
plugins { plugins {
id 'fabric-loom' version '0.2.6-SNAPSHOT' id 'fabric-loom' version '0.4-SNAPSHOT'
id 'com.matthewprenger.cursegradle' version '1.4.0'
id 'maven-publish' id 'maven-publish'
} }
sourceCompatibility = JavaVersion.VERSION_1_8 compileJava {
targetCompatibility = JavaVersion.VERSION_1_8 sourceCompatibility = JavaVersion.VERSION_1_8
targetCompatibility = JavaVersion.VERSION_1_8
}
archivesBaseName = project.archives_base_name archivesBaseName = project.archives_base_name
version = project.mod_version def mod_version = project.mod_version as Object
group = project.maven_group version = "${mod_version}+${project.minecraft_version}"
group = project.maven_group as Object
minecraft { minecraft {
} }
repositories {
jcenter()
maven {
url "https://dl.bintray.com/shedaniel/autoconfig1u/"
}
}
dependencies { dependencies {
minecraft "com.mojang:minecraft:${project.minecraft_version}" minecraft "com.mojang:minecraft:${project.minecraft_version}"
mappings "net.fabricmc:yarn:${project.yarn_mappings}:v2" mappings "net.fabricmc:yarn:${project.minecraft_version}+build.${project.yarn_build}:v2"
modCompile "net.fabricmc:fabric-loader:${project.loader_version}" modImplementation "net.fabricmc:fabric-loader:${project.fabric_loader_version}"
modImplementation "net.fabricmc.fabric-api:fabric-api:${project.fabric_api_version}"
modCompile "net.fabricmc.fabric-api:fabric-api:${project.fabric_version}" modImplementation "me.shedaniel.cloth:config-2:${project.cloth_config_version}"
include "me.shedaniel.cloth:config-2:${project.cloth_config_version}"
modImplementation "me.sargunvohra.mcmods:autoconfig1u:${project.auto_config_version}"
include "me.sargunvohra.mcmods:autoconfig1u:${project.auto_config_version}"
modImplementation "io.github.prospector:modmenu:${project.mod_menu_version}"
} }
processResources { processResources {
inputs.property "version", project.version inputs.property 'version', project.version
inputs.property 'name', rootProject.name
from(sourceSets.main.resources.srcDirs) { from(sourceSets.main.resources.srcDirs) {
include "fabric.mod.json" include 'fabric.mod.json'
expand "version": project.version expand 'version': project.version, 'name': rootProject.name
} }
from(sourceSets.main.resources.srcDirs) { from(sourceSets.main.resources.srcDirs) {
exclude "fabric.mod.json" exclude 'fabric.mod.json'
} }
} }
@ -38,38 +55,69 @@ processResources {
// this fixes some edge cases with special characters not displaying correctly // this fixes some edge cases with special characters not displaying correctly
// see http://yodaconditions.net/blog/fix-for-java-file-encoding-problems-with-gradle.html // see http://yodaconditions.net/blog/fix-for-java-file-encoding-problems-with-gradle.html
tasks.withType(JavaCompile) { tasks.withType(JavaCompile) {
options.encoding = "UTF-8" options.encoding = 'UTF-8'
} }
// Loom will automatically attach sourcesJar to a RemapSourcesJar task and to the "build" task // Loom will automatically attach sourcesJar to a RemapSourcesJar task and to the "build" task
// if it is present. // if it is present.
// If you remove this task, sources will not be generated. // If you remove this task, sources will not be generated.
task sourcesJar(type: Jar, dependsOn: classes) { task sourcesJar(type: Jar, dependsOn: classes) {
classifier "sources" classifier 'sources'
from sourceSets.main.allSource from sourceSets.main.allSource
} }
task javadocJar(type: Jar, dependsOn: javadoc) {
classifier 'javadoc'
from javadoc.destinationDir
}
artifacts {
archives sourcesJar
archives javadocJar
}
jar { jar {
from "LICENSE" from "LICENSE"
} }
// configure the maven publication
publishing { publishing {
publications { publications {
mavenJava(MavenPublication) { mavenJava(MavenPublication) {
// add all the jars that should be included when publishing to maven artifactId archivesBaseName
artifact(remapJar) { artifact(remapJar) {
builtBy remapJar builtBy remapJar
} }
artifact(sourcesJar) {
builtBy remapSourcesJar
}
} }
} }
// select the repositories you want to publish to
repositories { repositories {
// uncomment to publish to the local maven maven {
// mavenLocal() url '/data/maven'
}
} }
} }
if (project.hasProperty('curseforge.api_key')) {
curseforge {
apiKey = project.getProperty('curseforge.api_key')
project {
id = project.curseforge_id
changelog = 'A changelog can be found at https://gitea.thebrokenrail.com/TheBrokenRail/SorceryCraft/src/branch/master/CHANGELOG.md'
releaseType = 'release'
addGameVersion project.simple_minecraft_version
addGameVersion 'Fabric'
mainArtifact(remapJar) {
displayName = "SorceryCraft v${mod_version} for ${project.minecraft_version}"
}
afterEvaluate {
uploadTask.dependsOn('remapJar')
}
relations {
requiredDependency 'fabric-api'
}
}
options {
forgeGradleIntegration = false
}
}
}

View File

@ -3,15 +3,20 @@ org.gradle.jvmargs = -Xmx1G
# Fabric Properties # Fabric Properties
# check these on https://fabricmc.net/use # check these on https://fabricmc.net/use
minecraft_version = 20w09a minecraft_version = 1.16.4
yarn_mappings = 20w09a+build.6 curseforge_id = 365308
loader_version = 0.7.8+build.184 simple_minecraft_version = 1.16.4
yarn_build = 7
fabric_loader_version = 0.10.8
# Mod Properties # Mod Properties
mod_version = 1.0.0 mod_version = 1.2.9
maven_group = com.thebrokenrail maven_group = com.thebrokenrail
archives_base_name = sorcerycraft archives_base_name = sorcerycraft
# Dependencies # Dependencies
# currently not on the main fabric site, check on the maven: https://maven.fabricmc.net/net/fabricmc/fabric-api/fabric-api # currently not on the main fabric site, check on the maven: https://maven.fabricmc.net/net/fabricmc/fabric-api/fabric-api
fabric_version = 0.4.33+build.301-1.16 fabric_api_version = 0.28.1+1.16
cloth_config_version = 4.8.3
auto_config_version = 3.3.1
mod_menu_version = 1.14.13+build.19

View File

@ -1,5 +1,5 @@
#Sat Feb 29 21:58:32 EST 2020 #Sat Feb 29 21:58:32 EST 2020
distributionUrl=https\://services.gradle.org/distributions/gradle-5.5.1-all.zip distributionUrl=https\://services.gradle.org/distributions/gradle-6.2.2-all.zip
distributionBase=GRADLE_USER_HOME distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists distributionPath=wrapper/dists
zipStorePath=wrapper/dists zipStorePath=wrapper/dists

View File

@ -8,3 +8,5 @@ pluginManagement {
gradlePluginPortal() gradlePluginPortal()
} }
} }
rootProject.name = 'SorceryCraft'

View File

@ -0,0 +1,32 @@
package com.thebrokenrail.sorcerycraft;
import me.sargunvohra.mcmods.autoconfig1u.ConfigData;
import me.sargunvohra.mcmods.autoconfig1u.annotation.Config;
import me.sargunvohra.mcmods.autoconfig1u.annotation.ConfigEntry;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Config(name = SorceryCraft.NAMESPACE)
public class ModConfig implements ConfigData {
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD})
public @interface UsePercentage {
double min();
double max();
}
@UsePercentage(min = 0, max = 1)
public double failureChance = 0.3;
@ConfigEntry.Gui.CollapsibleObject(startExpanded = true)
public LimitCastingTable limitCastingTable = new LimitCastingTable();
public static class LimitCastingTable {
public boolean creative = false;
public boolean survival = true;
}
}

View File

@ -1,53 +1,67 @@
package com.thebrokenrail.sorcerycraft; package com.thebrokenrail.sorcerycraft;
import com.thebrokenrail.sorcerycraft.advancement.CreateSpellCriterion;
import com.thebrokenrail.sorcerycraft.advancement.DiscoverAllSpellsCriterion;
import com.thebrokenrail.sorcerycraft.block.CastingTableBlock; import com.thebrokenrail.sorcerycraft.block.CastingTableBlock;
import com.thebrokenrail.sorcerycraft.block.CastingTableContainer;
import com.thebrokenrail.sorcerycraft.client.block.CastingTableScreen;
import com.thebrokenrail.sorcerycraft.client.entity.SpellEntityRenderer;
import com.thebrokenrail.sorcerycraft.command.SpellCommand; import com.thebrokenrail.sorcerycraft.command.SpellCommand;
import com.thebrokenrail.sorcerycraft.entity.SpellEntity; import com.thebrokenrail.sorcerycraft.entity.SpellEntity;
import com.thebrokenrail.sorcerycraft.item.SpellItem; import com.thebrokenrail.sorcerycraft.item.SpellItem;
import com.thebrokenrail.sorcerycraft.packet.LearnedNewSpellS2CPacket; import com.thebrokenrail.sorcerycraft.mixin.CriteriaRegistryHook;
import com.thebrokenrail.sorcerycraft.packet.SelectSpellC2SPacket; import com.thebrokenrail.sorcerycraft.packet.SelectSpellC2SPacket;
import com.thebrokenrail.sorcerycraft.packet.UpdateKnownSpellsS2CPacket; import com.thebrokenrail.sorcerycraft.spell.util.RandomSpellLootTableFunction;
import com.thebrokenrail.sorcerycraft.spell.RandomSpellLootTableFunction; import com.thebrokenrail.sorcerycraft.spell.api.registry.Spells;
import com.thebrokenrail.sorcerycraft.spell.SpellRegistry; import me.sargunvohra.mcmods.autoconfig1u.AutoConfig;
import net.fabricmc.api.ClientModInitializer; import me.sargunvohra.mcmods.autoconfig1u.serializer.GsonConfigSerializer;
import net.fabricmc.api.ModInitializer; import net.fabricmc.api.ModInitializer;
import net.fabricmc.fabric.api.client.itemgroup.FabricItemGroupBuilder; import net.fabricmc.fabric.api.client.itemgroup.FabricItemGroupBuilder;
import net.fabricmc.fabric.api.client.rendereregistry.v1.EntityRendererRegistry;
import net.fabricmc.fabric.api.client.screen.ScreenProviderRegistry;
import net.fabricmc.fabric.api.container.ContainerProviderRegistry; import net.fabricmc.fabric.api.container.ContainerProviderRegistry;
import net.fabricmc.fabric.api.entity.FabricEntityTypeBuilder; import net.fabricmc.fabric.api.entity.FabricEntityTypeBuilder;
import net.fabricmc.fabric.api.loot.v1.FabricLootPoolBuilder; import net.fabricmc.fabric.api.loot.v1.FabricLootPoolBuilder;
import net.fabricmc.fabric.api.loot.v1.event.LootTableLoadingCallback; import net.fabricmc.fabric.api.loot.v1.event.LootTableLoadingCallback;
import net.fabricmc.fabric.api.registry.CommandRegistry; import net.fabricmc.fabric.api.registry.CommandRegistry;
import net.fabricmc.fabric.impl.networking.ClientSidePacketRegistryImpl;
import net.fabricmc.fabric.impl.networking.ServerSidePacketRegistryImpl; import net.fabricmc.fabric.impl.networking.ServerSidePacketRegistryImpl;
import net.minecraft.client.MinecraftClient; import net.minecraft.block.DispenserBlock;
import net.minecraft.entity.EntityCategory; import net.minecraft.block.dispenser.ProjectileDispenserBehavior;
import net.minecraft.entity.EntityDimensions; import net.minecraft.entity.EntityDimensions;
import net.minecraft.entity.EntityType; import net.minecraft.entity.EntityType;
import net.minecraft.item.*; import net.minecraft.entity.SpawnGroup;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.entity.projectile.ProjectileEntity;
import net.minecraft.item.BlockItem;
import net.minecraft.item.Item;
import net.minecraft.item.ItemGroup;
import net.minecraft.item.ItemStack;
import net.minecraft.loot.BinomialLootTableRange; import net.minecraft.loot.BinomialLootTableRange;
import net.minecraft.loot.LootTables; import net.minecraft.loot.LootTables;
import net.minecraft.loot.entry.ItemEntry; import net.minecraft.loot.entry.ItemEntry;
import net.minecraft.text.TranslatableText; import net.minecraft.loot.function.LootFunctionType;
import net.minecraft.sound.SoundCategory;
import net.minecraft.sound.SoundEvent;
import net.minecraft.sound.SoundEvents;
import net.minecraft.stat.StatFormatter;
import net.minecraft.stat.Stats;
import net.minecraft.util.Identifier; import net.minecraft.util.Identifier;
import net.minecraft.util.math.BlockPointer;
import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.Position;
import net.minecraft.util.registry.Registry; import net.minecraft.util.registry.Registry;
import net.minecraft.world.World; import net.minecraft.world.World;
import java.util.Objects; import java.util.Objects;
public class SorceryCraft implements ModInitializer, ClientModInitializer { public class SorceryCraft implements ModInitializer {
public static final String NAMESPACE = "sorcerycraft"; public static final String NAMESPACE = "sorcerycraft";
public static SpellItem SPELL_ITEM; public static SpellItem SPELL_ITEM;
public static CastingTableBlock CASTING_TABLE_BLOCK; public static CastingTableBlock CASTING_TABLE_BLOCK;
public static BlockItem CASTING_TABLE_BLOCK_ITEM; public static BlockItem CASTING_TABLE_BLOCK_ITEM;
public static ItemGroup ITEM_GROUP; public static ItemGroup ITEM_GROUP;
public static EntityType<SpellEntity> SPELL_ENTITY; public static EntityType<SpellEntity> SPELL_ENTITY;
public static final Identifier[] LOOT_TABLES = new Identifier[] {
public static final Identifier[] LOOT_TABLES = new Identifier[]{
LootTables.SIMPLE_DUNGEON_CHEST, LootTables.SIMPLE_DUNGEON_CHEST,
LootTables.END_CITY_TREASURE_CHEST, LootTables.END_CITY_TREASURE_CHEST,
LootTables.NETHER_BRIDGE_CHEST, LootTables.NETHER_BRIDGE_CHEST,
@ -55,9 +69,23 @@ public class SorceryCraft implements ModInitializer, ClientModInitializer {
LootTables.SHIPWRECK_TREASURE_CHEST, LootTables.SHIPWRECK_TREASURE_CHEST,
LootTables.DESERT_PYRAMID_CHEST, LootTables.DESERT_PYRAMID_CHEST,
LootTables.JUNGLE_TEMPLE_CHEST, LootTables.JUNGLE_TEMPLE_CHEST,
LootTables.STRONGHOLD_LIBRARY_CHEST LootTables.STRONGHOLD_LIBRARY_CHEST,
LootTables.PILLAGER_OUTPOST_CHEST,
LootTables.WOODLAND_MANSION_CHEST,
LootTables.BURIED_TREASURE_CHEST,
LootTables.FISHING_TREASURE_GAMEPLAY
}; };
public static Identifier INTERACT_WITH_CASTING_TABLE_STAT;
public static Identifier CAST_SPELL_STAT;
public static DiscoverAllSpellsCriterion DISCOVER_ALL_SPELLS_CRITERION;
public static CreateSpellCriterion CREATE_SPELL_CRITERION;
public static ModConfig getConfig() {
return AutoConfig.getConfigHolder(ModConfig.class).getConfig();
}
private boolean isSelectedLootTable(Identifier lootTable) { private boolean isSelectedLootTable(Identifier lootTable) {
for (Identifier id : LOOT_TABLES) { for (Identifier id : LOOT_TABLES) {
if (id.equals(lootTable)) { if (id.equals(lootTable)) {
@ -69,6 +97,11 @@ public class SorceryCraft implements ModInitializer, ClientModInitializer {
@Override @Override
public void onInitialize() { public void onInitialize() {
//noinspection InstantiationOfUtilityClass
new Spells();
AutoConfig.register(ModConfig.class, GsonConfigSerializer::new);
ITEM_GROUP = FabricItemGroupBuilder.create( ITEM_GROUP = FabricItemGroupBuilder.create(
new Identifier(NAMESPACE, "spells")) new Identifier(NAMESPACE, "spells"))
.icon(() -> new ItemStack(SPELL_ITEM)) .icon(() -> new ItemStack(SPELL_ITEM))
@ -78,45 +111,74 @@ public class SorceryCraft implements ModInitializer, ClientModInitializer {
CASTING_TABLE_BLOCK_ITEM = new BlockItem(CASTING_TABLE_BLOCK, new Item.Settings().group(ITEM_GROUP)); CASTING_TABLE_BLOCK_ITEM = new BlockItem(CASTING_TABLE_BLOCK, new Item.Settings().group(ITEM_GROUP));
SPELL_ITEM = new SpellItem(); SPELL_ITEM = new SpellItem();
SPELL_ENTITY = FabricEntityTypeBuilder.<SpellEntity>create(EntityCategory.MISC, SpellEntity::new).size(EntityDimensions.fixed(0.25f, 0.25f)).build(); SPELL_ENTITY = FabricEntityTypeBuilder.create(SpawnGroup.MISC, (EntityType.EntityFactory<SpellEntity>) SpellEntity::new).size(EntityDimensions.fixed(0.25f, 0.25f)).build();
Registry.register(Registry.ITEM, new Identifier(NAMESPACE, "spell"), SPELL_ITEM); Registry.register(Registry.ITEM, new Identifier(NAMESPACE, "spell"), SPELL_ITEM);
Registry.register(Registry.BLOCK, new Identifier(NAMESPACE, "casting_table"), CASTING_TABLE_BLOCK); Registry.register(Registry.BLOCK, new Identifier(NAMESPACE, "casting_table"), CASTING_TABLE_BLOCK);
Registry.register(Registry.ITEM, new Identifier(NAMESPACE, "casting_table"), CASTING_TABLE_BLOCK_ITEM); Registry.register(Registry.ITEM, new Identifier(NAMESPACE, "casting_table"), CASTING_TABLE_BLOCK_ITEM);
Registry.register(Registry.ENTITY_TYPE, new Identifier(NAMESPACE, "spell"), SPELL_ENTITY); Registry.register(Registry.ENTITY_TYPE, new Identifier(NAMESPACE, "spell"), SPELL_ENTITY);
SpellRegistry.init();
ContainerProviderRegistry.INSTANCE.registerFactory(new Identifier(NAMESPACE, "casting_table"), (syncId, identifier, player, buf) -> { ContainerProviderRegistry.INSTANCE.registerFactory(new Identifier(NAMESPACE, "casting_table"), (syncId, identifier, player, buf) -> {
final World world = player.world; final World world = player.world;
final BlockPos pos = buf.readBlockPos(); final BlockPos pos = buf.readBlockPos();
return Objects.requireNonNull(world.getBlockState(pos).createContainerFactory(player.world, pos)).createMenu(syncId, player.inventory, player); return Objects.requireNonNull(world.getBlockState(pos).createScreenHandlerFactory(player.world, pos)).createMenu(syncId, player.inventory, player);
}); });
CommandRegistry.INSTANCE.register(false, SpellCommand::register); CommandRegistry.INSTANCE.register(false, SpellCommand::register);
ServerSidePacketRegistryImpl.INSTANCE.register(new Identifier(NAMESPACE, "select_spell"), SelectSpellC2SPacket::handle); ServerSidePacketRegistryImpl.INSTANCE.register(new Identifier(NAMESPACE, "select_spell"), SelectSpellC2SPacket::handle);
ClientSidePacketRegistryImpl.INSTANCE.register(new Identifier(NAMESPACE, "update_known_spells"), UpdateKnownSpellsS2CPacket::handle);
ClientSidePacketRegistryImpl.INSTANCE.register(new Identifier(NAMESPACE, "learned_new_spell"), LearnedNewSpellS2CPacket::handle);
LootTableLoadingCallback.EVENT.register((resourceManager, lootManager, id, supplier, setter) -> { LootTableLoadingCallback.EVENT.register((resourceManager, lootManager, id, supplier, setter) -> {
if (isSelectedLootTable(id)) { if (isSelectedLootTable(id)) {
FabricLootPoolBuilder poolBuilder = FabricLootPoolBuilder.builder() FabricLootPoolBuilder poolBuilder = FabricLootPoolBuilder.builder()
.withRolls(new BinomialLootTableRange(2, 0.5f)) .rolls(new BinomialLootTableRange(2, 0.5f))
.withEntry(ItemEntry.builder(SPELL_ITEM)) .withEntry(ItemEntry.builder(SPELL_ITEM).build())
.withFunction(new RandomSpellLootTableFunction.Builder()); .withFunction(new RandomSpellLootTableFunction.Builder().build());
supplier.withPool(poolBuilder); supplier.withPool(poolBuilder.build());
} }
}); });
Registry.register(Registry.LOOT_FUNCTION_TYPE, new Identifier(NAMESPACE, "random_spell"), new LootFunctionType(new RandomSpellLootTableFunction.Factory()));
DispenserBlock.registerBehavior(SorceryCraft.SPELL_ITEM, new ProjectileDispenserBehavior() {
@Override
protected ProjectileEntity createProjectile(World position, Position stack, ItemStack itemStack) {
SpellEntity entity = new SpellEntity(position, stack.getX(), stack.getY(), stack.getZ());
entity.setItem(itemStack);
return entity;
}
@Override
protected void playSound(BlockPointer pointer) {
playSpellSound(pointer);
}
});
INTERACT_WITH_CASTING_TABLE_STAT = registerStat("interact_with_casting_table");
CAST_SPELL_STAT = registerStat("cast_spell");
DISCOVER_ALL_SPELLS_CRITERION = CriteriaRegistryHook.callRegister(new DiscoverAllSpellsCriterion());
CREATE_SPELL_CRITERION = CriteriaRegistryHook.callRegister(new CreateSpellCriterion());
} }
@Override private Identifier registerStat(String name) {
public void onInitializeClient() { Identifier statID = new Identifier(NAMESPACE, name);
EntityRendererRegistry.INSTANCE.register(SPELL_ENTITY, (entityRenderDispatcher, context) -> new SpellEntityRenderer(entityRenderDispatcher)); Registry.register(Registry.CUSTOM_STAT, statID, statID);
ScreenProviderRegistry.INSTANCE.<CastingTableContainer>registerFactory(new Identifier(NAMESPACE, "casting_table"), (container) -> { Stats.CUSTOM.getOrCreateStat(statID, StatFormatter.DEFAULT);
assert MinecraftClient.getInstance().player != null; return statID;
return new CastingTableScreen(container, MinecraftClient.getInstance().player.inventory, new TranslatableText("block.sorcerycraft.casting_table")); }
});
private static final SoundEvent SPELL_SOUND_EFFECT = SoundEvents.BLOCK_ENCHANTMENT_TABLE_USE;
public static void playSpellSound(World world, BlockPos pos) {
world.playSound(null, pos, SPELL_SOUND_EFFECT, SoundCategory.BLOCKS, 1.0f, 1.0f);
}
public static void playSpellSound(BlockPointer block) {
playSpellSound(block.getWorld(), block.getBlockPos());
}
public static void playSpellSound(PlayerEntity player) {
player.playSound(SPELL_SOUND_EFFECT, SoundCategory.PLAYERS, 1.0f, 1.0f);
} }
} }

View File

@ -0,0 +1,37 @@
package com.thebrokenrail.sorcerycraft.advancement;
import com.google.gson.JsonObject;
import com.thebrokenrail.sorcerycraft.SorceryCraft;
import net.minecraft.advancement.criterion.AbstractCriterion;
import net.minecraft.advancement.criterion.AbstractCriterionConditions;
import net.minecraft.predicate.entity.AdvancementEntityPredicateDeserializer;
import net.minecraft.predicate.entity.EntityPredicate;
import net.minecraft.server.network.ServerPlayerEntity;
import net.minecraft.util.Identifier;
public class CreateSpellCriterion extends AbstractCriterion<CreateSpellCriterion.Conditions> {
private static final Identifier ID = new Identifier(SorceryCraft.NAMESPACE, "create_spell");
public CreateSpellCriterion() {
}
@Override
protected Conditions conditionsFromJson(JsonObject obj, EntityPredicate.Extended playerPredicate, AdvancementEntityPredicateDeserializer predicateDeserializer) {
return new CreateSpellCriterion.Conditions(playerPredicate);
}
@Override
public Identifier getId() {
return ID;
}
public void trigger(ServerPlayerEntity player) {
test(player, (conditions) -> true);
}
public static class Conditions extends AbstractCriterionConditions {
public Conditions(EntityPredicate.Extended player) {
super(ID, player);
}
}
}

View File

@ -0,0 +1,54 @@
package com.thebrokenrail.sorcerycraft.advancement;
import com.google.gson.JsonObject;
import com.thebrokenrail.sorcerycraft.SorceryCraft;
import com.thebrokenrail.sorcerycraft.spell.api.Spell;
import com.thebrokenrail.sorcerycraft.spell.api.registry.SpellRegistry;
import com.thebrokenrail.sorcerycraft.spell.util.SpellPlayerEntity;
import net.minecraft.advancement.criterion.AbstractCriterion;
import net.minecraft.advancement.criterion.AbstractCriterionConditions;
import net.minecraft.predicate.entity.AdvancementEntityPredicateDeserializer;
import net.minecraft.predicate.entity.EntityPredicate;
import net.minecraft.server.network.ServerPlayerEntity;
import net.minecraft.util.Identifier;
import java.util.Map;
public class DiscoverAllSpellsCriterion extends AbstractCriterion<DiscoverAllSpellsCriterion.Conditions> {
private static final Identifier ID = new Identifier(SorceryCraft.NAMESPACE, "discover_all_spells");
public DiscoverAllSpellsCriterion() {
}
@Override
public Identifier getId() {
return ID;
}
@Override
protected DiscoverAllSpellsCriterion.Conditions conditionsFromJson(JsonObject obj, EntityPredicate.Extended playerPredicate, AdvancementEntityPredicateDeserializer predicateDeserializer) {
return new DiscoverAllSpellsCriterion.Conditions(playerPredicate);
}
public void trigger(ServerPlayerEntity player) {
test(player, (conditions) -> {
SpellPlayerEntity spellPlayer = (SpellPlayerEntity) player;
Map<Identifier, Integer> spells = spellPlayer.getDiscoveredSpells();
Spell[] maxSpells = SpellRegistry.getMaxSpells();
boolean match = true;
for (Spell spell : maxSpells) {
if (!spells.containsKey(spell.getID()) || spells.get(spell.getID()) < (spell.getLevel() - 1)) {
match = false;
break;
}
}
return match;
});
}
public static class Conditions extends AbstractCriterionConditions {
public Conditions(EntityPredicate.Extended player) {
super(ID, player);
}
}
}

View File

@ -1,15 +1,17 @@
package com.thebrokenrail.sorcerycraft.block; package com.thebrokenrail.sorcerycraft.block;
import com.thebrokenrail.sorcerycraft.SorceryCraft; import com.thebrokenrail.sorcerycraft.SorceryCraft;
import com.thebrokenrail.sorcerycraft.gui.CastingTableScreenHandler;
import com.thebrokenrail.sorcerycraft.spell.util.SpellServerPlayerEntity;
import net.fabricmc.fabric.api.block.FabricBlockSettings; import net.fabricmc.fabric.api.block.FabricBlockSettings;
import net.fabricmc.fabric.api.container.ContainerProviderRegistry; import net.fabricmc.fabric.api.container.ContainerProviderRegistry;
import net.minecraft.block.Block; import net.minecraft.block.Block;
import net.minecraft.block.BlockState; import net.minecraft.block.BlockState;
import net.minecraft.block.Material; import net.minecraft.block.Material;
import net.minecraft.container.BlockContext;
import net.minecraft.container.NameableContainerFactory;
import net.minecraft.container.SimpleNamedContainerFactory;
import net.minecraft.entity.player.PlayerEntity; import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.screen.NamedScreenHandlerFactory;
import net.minecraft.screen.ScreenHandlerContext;
import net.minecraft.screen.SimpleNamedScreenHandlerFactory;
import net.minecraft.text.TranslatableText; import net.minecraft.text.TranslatableText;
import net.minecraft.util.ActionResult; import net.minecraft.util.ActionResult;
import net.minecraft.util.Hand; import net.minecraft.util.Hand;
@ -28,12 +30,18 @@ public class CastingTableBlock extends Block {
public ActionResult onUse(BlockState state, World world, BlockPos pos, PlayerEntity player, Hand hand, BlockHitResult hit) { public ActionResult onUse(BlockState state, World world, BlockPos pos, PlayerEntity player, Hand hand, BlockHitResult hit) {
if (!world.isClient()) { if (!world.isClient()) {
ContainerProviderRegistry.INSTANCE.openContainer(new Identifier(SorceryCraft.NAMESPACE, "casting_table"), player, (buf) -> buf.writeBlockPos(pos)); ContainerProviderRegistry.INSTANCE.openContainer(new Identifier(SorceryCraft.NAMESPACE, "casting_table"), player, (buf) -> buf.writeBlockPos(pos));
player.incrementStat(SorceryCraft.INTERACT_WITH_CASTING_TABLE_STAT);
} }
return ActionResult.SUCCESS; return ActionResult.SUCCESS;
} }
@Override @Override
public NameableContainerFactory createContainerFactory(BlockState state, World world, BlockPos pos) { public NamedScreenHandlerFactory createScreenHandlerFactory(BlockState state, World world, BlockPos pos) {
return new SimpleNamedContainerFactory((i, playerInventory, playerEntity) -> new CastingTableContainer(i, playerInventory, BlockContext.create(world, pos)), new TranslatableText("container.sorcerycraft.casting_table")); return new SimpleNamedScreenHandlerFactory((i, playerInventory, playerEntity) -> {
if (!playerEntity.getEntityWorld().isClient()) {
((SpellServerPlayerEntity) playerEntity).sync();
}
return new CastingTableScreenHandler(i, playerInventory, ScreenHandlerContext.create(world, pos));
}, new TranslatableText("container." + SorceryCraft.NAMESPACE + ".casting_table"));
} }
} }

View File

@ -0,0 +1,24 @@
package com.thebrokenrail.sorcerycraft.client;
import com.thebrokenrail.sorcerycraft.ModConfig;
import com.thebrokenrail.sorcerycraft.SorceryCraft;
import io.github.prospector.modmenu.api.ConfigScreenFactory;
import io.github.prospector.modmenu.api.ModMenuApi;
import me.sargunvohra.mcmods.autoconfig1u.AutoConfig;
import net.fabricmc.api.EnvType;
import net.fabricmc.api.Environment;
import net.minecraft.client.gui.screen.Screen;
@SuppressWarnings("unused")
@Environment(EnvType.CLIENT)
public class ModMenu implements ModMenuApi {
@Override
public String getModId() {
return SorceryCraft.NAMESPACE;
}
@Override
public ConfigScreenFactory<?> getModConfigScreenFactory() {
return (ConfigScreenFactory<Screen>) screen -> AutoConfig.getConfigScreen(ModConfig.class, screen).get();
}
}

View File

@ -0,0 +1,46 @@
package com.thebrokenrail.sorcerycraft.client;
import com.thebrokenrail.sorcerycraft.ModConfig;
import com.thebrokenrail.sorcerycraft.SorceryCraft;
import com.thebrokenrail.sorcerycraft.client.entity.SpellEntityRenderer;
import com.thebrokenrail.sorcerycraft.client.gui.CastingTableScreen;
import com.thebrokenrail.sorcerycraft.gui.CastingTableScreenHandler;
import com.thebrokenrail.sorcerycraft.packet.UpdateKnownSpellsS2CPacket;
import me.sargunvohra.mcmods.autoconfig1u.AutoConfig;
import me.sargunvohra.mcmods.autoconfig1u.gui.registry.GuiRegistry;
import me.sargunvohra.mcmods.autoconfig1u.util.Utils;
import me.shedaniel.clothconfig2.api.ConfigEntryBuilder;
import net.fabricmc.api.ClientModInitializer;
import net.fabricmc.api.EnvType;
import net.fabricmc.api.Environment;
import net.fabricmc.fabric.api.client.rendereregistry.v1.EntityRendererRegistry;
import net.fabricmc.fabric.api.client.screen.ScreenProviderRegistry;
import net.fabricmc.fabric.impl.networking.ClientSidePacketRegistryImpl;
import net.minecraft.client.MinecraftClient;
import net.minecraft.text.LiteralText;
import net.minecraft.text.TranslatableText;
import net.minecraft.util.Identifier;
import net.minecraft.util.math.MathHelper;
import java.util.Collections;
@SuppressWarnings("unused")
@Environment(EnvType.CLIENT)
public class SorceryCraftClient implements ClientModInitializer {
@Override
public void onInitializeClient() {
GuiRegistry guiRegistry = AutoConfig.getGuiRegistry(ModConfig.class);
guiRegistry.registerAnnotationProvider((s, field, config, defaults, guiRegistryAccess) -> {
ModConfig.UsePercentage bounds = field.getAnnotation(ModConfig.UsePercentage.class);
return Collections.singletonList(ConfigEntryBuilder.create().startIntSlider(new TranslatableText(s), MathHelper.ceil(Utils.getUnsafely(field, config, 0.0) * 100), MathHelper.ceil(bounds.min() * 100), MathHelper.ceil(bounds.max() * 100)).setDefaultValue(() -> MathHelper.ceil((double) Utils.getUnsafely(field, defaults) * 100)).setSaveConsumer((newValue) -> Utils.setUnsafely(field, config, newValue / 100d)).setTextGetter(integer -> new LiteralText(String.format("%d%%", integer))).build());
}, field -> field.getType() == Double.TYPE || field.getType() == Double.class, ModConfig.UsePercentage.class);
EntityRendererRegistry.INSTANCE.register(SorceryCraft.SPELL_ENTITY, (entityRenderDispatcher, context) -> new SpellEntityRenderer(entityRenderDispatcher));
ScreenProviderRegistry.INSTANCE.<CastingTableScreenHandler>registerFactory(new Identifier(SorceryCraft.NAMESPACE, "casting_table"), (container) -> {
assert MinecraftClient.getInstance().player != null;
return new CastingTableScreen(container, MinecraftClient.getInstance().player.inventory, new TranslatableText("block." + SorceryCraft.NAMESPACE + ".casting_table"));
});
ClientSidePacketRegistryImpl.INSTANCE.register(new Identifier(SorceryCraft.NAMESPACE, "update_known_spells"), UpdateKnownSpellsS2CPacket::handle);
}
}

View File

@ -1,235 +0,0 @@
package com.thebrokenrail.sorcerycraft.client.block;
import com.mojang.blaze3d.systems.RenderSystem;
import com.thebrokenrail.sorcerycraft.block.CastingTableContainer;
import com.thebrokenrail.sorcerycraft.packet.SelectSpellC2SPacket;
import com.thebrokenrail.sorcerycraft.spell.Spell;
import com.thebrokenrail.sorcerycraft.spell.SpellTag;
import net.fabricmc.api.EnvType;
import net.fabricmc.api.Environment;
import net.minecraft.client.font.TextRenderer;
import net.minecraft.client.gui.screen.ingame.ContainerScreen;
import net.minecraft.client.gui.widget.ButtonWidget;
import net.minecraft.entity.player.PlayerInventory;
import net.minecraft.item.ItemStack;
import net.minecraft.text.Text;
import net.minecraft.text.TranslatableText;
import net.minecraft.util.Identifier;
import net.minecraft.util.math.MathHelper;
@Environment(EnvType.CLIENT)
public class CastingTableScreen extends ContainerScreen<CastingTableContainer> {
private static final Identifier TEXTURE = new Identifier("textures/gui/container/villager2.png");
private int selectedIndex;
private int indexStartOffset;
private boolean scrolling;
public CastingTableScreen(CastingTableContainer container, PlayerInventory inventory, Text title) {
super(container, inventory, title);
containerWidth = 276;
}
@Override
protected void drawForeground(int mouseX, int mouseY) {
int j = containerHeight - 94;
font.draw(title.asFormattedString(), (float) (49 + this.containerWidth / 2 - font.getStringWidth(title.asFormattedString()) / 2), 6.0F, 4210752);
font.draw(playerInventory.getDisplayName().asFormattedString(), 107.0F, (float) j, 4210752);
String spells = new TranslatableText("container.sorcerycraft.spells").getString();
font.draw(spells, (float) (5 - font.getStringWidth(spells) / 2 + 48), 6.0F, 4210752);
renderXPCost();
}
@Override
protected void drawBackground(float delta, int mouseX, int mouseY) {
RenderSystem.color4f(1.0F, 1.0F, 1.0F, 1.0F);
assert minecraft != null;
minecraft.getTextureManager().bindTexture(TEXTURE);
int i = (width - containerWidth) / 2;
int j = (height - containerHeight) / 2;
blit(i, j, this.getBlitOffset(), 0.0F, 0.0F, containerWidth, containerHeight, 256, 512);
}
@Override
public void render(int mouseX, int mouseY, float delta) {
renderBackground();
super.render(mouseX, mouseY, delta);
drawMouseoverTooltip(mouseX, mouseY);
renderScrollbar();
renderItems();
for (WidgetButtonPage button : buttons) {
if (button.isHovered()) {
button.renderToolTip(mouseX, mouseY);
}
button.visible = button.index < container.getRecipes().length;
if (button.visible) {
Spell spell = container.getRecipes()[button.getIndex() + indexStartOffset];
button.setMessage(SpellTag.getTranslatedSpell(spell.getID(), spell.getLevel()).getString());
button.setFocused((button.getIndex() + indexStartOffset) == selectedIndex);
}
}
}
private void renderScrollbar() {
Spell[] spells = container.getRecipes();
assert minecraft != null;
minecraft.getTextureManager().bindTexture(TEXTURE);
int i = (width - containerWidth) / 2;
int j = (height - containerHeight) / 2;
int k = spells.length + 1 - 7;
if (k > 1) {
int l = 139 - (27 + (k - 1) * 139 / k);
int m = 1 + l / k + 139 / k;
int o = Math.min(113, indexStartOffset * m);
if (this.indexStartOffset == k - 1) {
o = 113;
}
blit(i + 94, j + 18 + o, getBlitOffset(), 0.0F, 199.0F, 6, 27, 256, 512);
} else {
blit(i + 94, j + 18, getBlitOffset(), 6.0F, 199.0F, 6, 27, 256, 512);
}
}
private void renderItems() {
int i = (width - containerWidth) / 2;
int j = (height - containerHeight) / 2;
int k = j + 16 + 1;
Spell[] spells = container.getRecipes();
for (int x = 0; x < spells.length; x++) {
if (!canScroll(spells.length) || (x >= indexStartOffset && x < 7 + indexStartOffset)) {
ItemStack itemStack = spells[x].getItemCost();
itemRenderer.zOffset = 100.0F;
int n = k + 2;
itemRenderer.renderGuiItem(itemStack, i + 5 + 68, n);
itemRenderer.renderGuiItemOverlay(font, itemStack, i + 5 + 68, n);
itemRenderer.zOffset = 0.0F;
k += 20;
}
}
}
private void renderXPCost() {
if (container.getRecipes().length > 0) {
int i = container.getRecipes()[selectedIndex].getXPCost();
int j = 8453920;
assert minecraft != null;
assert minecraft.player != null;
String string = new TranslatableText("container.repair.cost", i).getString();
if (!container.canTakeResult(playerInventory.player)) {
j = 16736352;
}
int x2 = containerWidth - 8;
int x1 = x2 - font.getStringWidth(string);
fill(x1, 65, x2, 77, 1325400064);
font.drawWithShadow(string, (float) x1, 67.0F, j);
}
}
private boolean canScroll(int listSize) {
return listSize > 7;
}
@Override
public boolean mouseScrolled(double d, double e, double amount) {
int i = container.getRecipes().length;
if (this.canScroll(i)) {
int j = i - 7;
indexStartOffset = (int) ((double) indexStartOffset - amount);
indexStartOffset = MathHelper.clamp(indexStartOffset, 0, j);
}
return true;
}
@Override
public boolean mouseDragged(double mouseX, double mouseY, int button, double deltaX, double deltaY) {
int i = container.getRecipes().length;
if (scrolling) {
int j = y + 18;
int k = j + 139;
int l = i - 7;
float f = ((float) mouseY - (float) j - 13.5F) / ((float) (k - j) - 27.0F);
f = f * (float)l + 0.5F;
indexStartOffset = MathHelper.clamp((int) f, 0, l);
return true;
} else {
return super.mouseDragged(mouseX, mouseY, button, deltaX, deltaY);
}
}
@Override
public boolean mouseClicked(double mouseX, double mouseY, int button) {
scrolling = false;
int i = (width - containerWidth) / 2;
int j = (height - containerHeight) / 2;
if (this.canScroll(container.getRecipes().length) && mouseX > (double)(i + 94) && mouseX < (double) (i + 94 + 6) && mouseY > (double) (j + 18) && mouseY <= (double) (j + 18 + 139 + 1)) {
scrolling = true;
}
return super.mouseClicked(mouseX, mouseY, button);
}
private void syncRecipeIndex() {
container.setIndex(selectedIndex);
assert minecraft != null;
SelectSpellC2SPacket.send(minecraft, selectedIndex);
}
private final WidgetButtonPage[] buttons = new WidgetButtonPage[7];
@Override
protected void init() {
super.init();
int i = (width - containerWidth) / 2;
int j = (height - containerHeight) / 2;
int k = j + 16 + 2;
for (int l = 0; l < 7; ++l) {
buttons[l] = addButton(new WidgetButtonPage(i + 5, k, l, (buttonWidget) -> {
selectedIndex = ((WidgetButtonPage) buttonWidget).getIndex() + indexStartOffset;
syncRecipeIndex();
}));
k += 20;
}
}
@Environment(EnvType.CLIENT)
private class WidgetButtonPage extends ButtonWidget {
final int index;
public WidgetButtonPage(int i, int j, int k, PressAction pressAction) {
super(i, j, 89, 20, "", pressAction);
index = k;
visible = false;
}
@Override
public void setFocused(boolean state) {
super.setFocused(state);
}
public int getIndex() {
return this.index;
}
public void renderToolTip(int mouseX, int mouseY) {
if (isHovered && container.getRecipes().length > index + indexStartOffset && mouseX > this.x + 65) {
ItemStack itemStack = container.getRecipes()[index + indexStartOffset].getItemCost();
if (!itemStack.isEmpty()) {
renderTooltip(itemStack, mouseX, mouseY);
}
}
}
@Override
public void drawCenteredString(TextRenderer textRenderer, String str, int ignored, int y, int color) {
drawString(textRenderer, str, x + 5, y, color);
}
}
}

View File

@ -1,21 +1,20 @@
package com.thebrokenrail.sorcerycraft.client.entity; package com.thebrokenrail.sorcerycraft.client.entity;
import com.thebrokenrail.sorcerycraft.entity.SpellEntity;
import net.fabricmc.api.EnvType; import net.fabricmc.api.EnvType;
import net.fabricmc.api.Environment; import net.fabricmc.api.Environment;
import net.minecraft.client.render.entity.EntityRenderDispatcher; import net.minecraft.client.render.entity.EntityRenderDispatcher;
import net.minecraft.client.render.entity.EntityRenderer; import net.minecraft.client.render.entity.EntityRenderer;
import net.minecraft.container.PlayerContainer;
import net.minecraft.entity.AreaEffectCloudEntity;
import net.minecraft.util.Identifier; import net.minecraft.util.Identifier;
@Environment(EnvType.CLIENT) @Environment(EnvType.CLIENT)
public class SpellEntityRenderer extends EntityRenderer<AreaEffectCloudEntity> { public class SpellEntityRenderer extends EntityRenderer<SpellEntity> {
public SpellEntityRenderer(EntityRenderDispatcher entityRenderDispatcher) { public SpellEntityRenderer(EntityRenderDispatcher entityRenderDispatcher) {
super(entityRenderDispatcher); super(entityRenderDispatcher);
} }
@Override @Override
public Identifier getTexture(AreaEffectCloudEntity areaEffectCloudEntity) { public Identifier getTexture(SpellEntity spellEntity) {
return PlayerContainer.BLOCK_ATLAS_TEXTURE; return null;
} }
} }

View File

@ -0,0 +1,231 @@
package com.thebrokenrail.sorcerycraft.client.gui;
import com.mojang.blaze3d.systems.RenderSystem;
import com.thebrokenrail.sorcerycraft.SorceryCraft;
import com.thebrokenrail.sorcerycraft.gui.CastingTableScreenHandler;
import com.thebrokenrail.sorcerycraft.packet.SelectSpellC2SPacket;
import com.thebrokenrail.sorcerycraft.spell.api.Spell;
import com.thebrokenrail.sorcerycraft.spell.util.SpellHelper;
import net.fabricmc.api.EnvType;
import net.fabricmc.api.Environment;
import net.minecraft.client.gui.screen.ingame.HandledScreen;
import net.minecraft.client.gui.widget.ButtonWidget;
import net.minecraft.client.util.math.MatrixStack;
import net.minecraft.entity.player.PlayerInventory;
import net.minecraft.item.ItemStack;
import net.minecraft.text.LiteralText;
import net.minecraft.text.Text;
import net.minecraft.text.TranslatableText;
import net.minecraft.util.Identifier;
import net.minecraft.util.math.MathHelper;
@Environment(EnvType.CLIENT)
public class CastingTableScreen extends HandledScreen<CastingTableScreenHandler> {
private static final Identifier TEXTURE = new Identifier("textures/gui/container/villager2.png");
private int selectedIndex = 0;
private int indexStartOffset = 0;
private boolean scrolling = false;
public CastingTableScreen(CastingTableScreenHandler container, PlayerInventory inventory, Text title) {
super(container, inventory, title);
backgroundWidth = 276;
}
@Override
protected void drawForeground(MatrixStack matrixStack, int i, int j) {
int titleY = backgroundHeight - 94;
textRenderer.draw(matrixStack, title, (float) (49 + this.backgroundWidth / 2 - textRenderer.getWidth(title) / 2), 6.0F, 4210752);
textRenderer.draw(matrixStack, playerInventory.getDisplayName(), 107.0F, (float) titleY, 4210752);
Text spells = new TranslatableText("container." + SorceryCraft.NAMESPACE + ".spells");
textRenderer.draw(matrixStack, spells, (float) (5 - textRenderer.getWidth(spells) / 2 + 48), 6.0F, 4210752);
renderXPCost(matrixStack);
}
public void resetIndex() {
selectedIndex = 0;
indexStartOffset = 0;
}
@Override
protected void drawBackground(MatrixStack matrixStack, float delta, int mouseX, int mouseY) {
RenderSystem.color4f(1.0F, 1.0F, 1.0F, 1.0F);
assert client != null;
client.getTextureManager().bindTexture(TEXTURE);
int i = (width - backgroundWidth) / 2;
int j = (height - backgroundHeight) / 2;
drawTexture(matrixStack, i, j, getZOffset(), 0.0F, 0.0F, backgroundWidth, backgroundHeight, 256, 512);
}
@Override
public void render(MatrixStack matrixStack, int mouseX, int mouseY, float delta) {
renderBackground(matrixStack);
super.render(matrixStack, mouseX, mouseY, delta);
drawMouseoverTooltip(matrixStack, mouseX, mouseY);
renderScrollbar(matrixStack);
renderItems();
for (WidgetButtonPage button : buttons) {
if (button.isHovered()) {
button.renderToolTip(matrixStack, mouseX, mouseY);
}
button.visible = button.index < handler.getRecipes().length;
if (button.visible) {
Spell spell = handler.getRecipes()[button.getIndex() + indexStartOffset];
button.setMessage(SpellHelper.getTranslatedSpell(spell.getID(), spell.getLevel()));
}
}
}
private void renderScrollbar(MatrixStack matrixStack) {
Spell[] spells = handler.getRecipes();
assert client != null;
client.getTextureManager().bindTexture(TEXTURE);
int i = (width - backgroundWidth) / 2;
int j = (height - backgroundHeight) / 2;
int k = spells.length - 7;
if (k > 0) {
int modifier = (int) (((float) indexStartOffset / k) * (1 + 139 - 27));
drawTexture(matrixStack, i + 94, j + 18 + modifier, getZOffset(), 0.0F, 199.0F, 6, 27, 256, 512);
} else {
drawTexture(matrixStack, i + 94, j + 18, getZOffset(), 6.0F, 199.0F, 6, 27, 256, 512);
}
}
private void renderItems() {
int i = (width - backgroundWidth) / 2;
int j = (height - backgroundHeight) / 2;
int k = j + 16 + 1;
Spell[] spells = handler.getRecipes();
for (int x = 0; x < spells.length; x++) {
if (!canScroll(spells.length) || (x >= indexStartOffset && x < 7 + indexStartOffset)) {
ItemStack itemStack = spells[x].getItemCost();
itemRenderer.zOffset = 100.0F;
int y = k + 2;
itemRenderer.renderInGuiWithOverrides(itemStack, i + 5 + 68, y);
itemRenderer.renderGuiItemOverlay(textRenderer, itemStack, i + 5 + 68, y);
itemRenderer.zOffset = 0.0F;
k += 20;
}
}
}
private void renderXPCost(MatrixStack matrixStack) {
if (handler.getRecipes().length > 0) {
int cost = handler.getRecipes()[selectedIndex].getXPCost();
int color = 8453920;
assert client != null;
assert client.player != null;
Text string = new TranslatableText("container.repair.cost", cost);
if (!handler.canTakeResult(playerInventory.player)) {
color = 16736352;
}
int x2 = backgroundWidth - 8;
int x1 = x2 - textRenderer.getWidth(string);
fill(matrixStack, x1, 65, x2, 77, 1325400064);
textRenderer.drawWithShadow(matrixStack, string, (float) x1, 67.0F, color);
}
}
private boolean canScroll(int listSize) {
return listSize > 7;
}
@Override
public boolean mouseScrolled(double d, double e, double amount) {
int i = handler.getRecipes().length;
if (canScroll(i)) {
int j = i - 7;
indexStartOffset = (int) ((double) indexStartOffset - amount);
indexStartOffset = MathHelper.clamp(indexStartOffset, 0, j);
}
return true;
}
@Override
public boolean mouseDragged(double mouseX, double mouseY, int button, double deltaX, double deltaY) {
int i = handler.getRecipes().length;
if (scrolling) {
int j = y + 18;
int k = j + 139;
int l = i - 7;
float f = ((float) mouseY - (float) j - 13.5F) / ((float) (k - j) - 27.0F);
f = f * (float) l + 0.5F;
indexStartOffset = MathHelper.clamp((int) f, 0, l);
return true;
} else {
return super.mouseDragged(mouseX, mouseY, button, deltaX, deltaY);
}
}
@Override
public boolean mouseClicked(double mouseX, double mouseY, int button) {
scrolling = false;
int i = (width - backgroundWidth) / 2;
int j = (height - backgroundHeight) / 2;
if (canScroll(handler.getRecipes().length) && mouseX > (double) (i + 94) && mouseX < (double) (i + 94 + 6) && mouseY > (double) (j + 18) && mouseY <= (double) (j + 18 + 139 + 1)) {
scrolling = true;
}
return super.mouseClicked(mouseX, mouseY, button);
}
private void syncRecipeIndex() {
handler.setIndex(selectedIndex);
assert client != null;
SelectSpellC2SPacket.send(client, selectedIndex);
}
private final WidgetButtonPage[] buttons = new WidgetButtonPage[7];
@Override
protected void init() {
super.init();
int i = (width - backgroundWidth) / 2;
int j = (height - backgroundHeight) / 2;
int k = j + 16 + 2;
for (int l = 0; l < 7; ++l) {
buttons[l] = addButton(new WidgetButtonPage(i + 5, k, l, (buttonWidget) -> {
selectedIndex = ((WidgetButtonPage) buttonWidget).getIndex() + indexStartOffset;
syncRecipeIndex();
}));
k += 20;
}
}
@Environment(EnvType.CLIENT)
public class WidgetButtonPage extends ButtonWidget {
final int index;
public WidgetButtonPage(int i, int j, int k, PressAction pressAction) {
super(i, j, 89, 20, new LiteralText(""), pressAction);
index = k;
visible = false;
}
@Override
public void render(MatrixStack matrixStack, int mouseX, int mouseY, float delta) {
active = (index + CastingTableScreen.this.indexStartOffset) != CastingTableScreen.this.selectedIndex;
super.render(matrixStack, mouseX, mouseY, delta);
}
public int getIndex() {
return this.index;
}
public void renderToolTip(MatrixStack matrixStack, int mouseX, int mouseY) {
if (hovered && handler.getRecipes().length > index + indexStartOffset && mouseX > this.x + 65) {
ItemStack itemStack = handler.getRecipes()[index + indexStartOffset].getItemCost();
if (!itemStack.isEmpty()) {
renderTooltip(matrixStack, itemStack, mouseX, mouseY);
}
}
}
}
}

View File

@ -0,0 +1,57 @@
package com.thebrokenrail.sorcerycraft.command;
import com.mojang.brigadier.StringReader;
import com.mojang.brigadier.arguments.ArgumentType;
import com.mojang.brigadier.context.CommandContext;
import com.mojang.brigadier.exceptions.CommandSyntaxException;
import com.mojang.brigadier.exceptions.DynamicCommandExceptionType;
import java.util.Arrays;
import java.util.Collection;
import java.util.concurrent.CompletableFuture;
import com.mojang.brigadier.suggestion.Suggestions;
import com.mojang.brigadier.suggestion.SuggestionsBuilder;
import com.thebrokenrail.sorcerycraft.SorceryCraft;
import com.thebrokenrail.sorcerycraft.spell.api.Spell;
import com.thebrokenrail.sorcerycraft.spell.api.registry.SpellRegistry;
import net.minecraft.command.CommandSource;
import net.minecraft.server.command.ServerCommandSource;
import net.minecraft.text.TranslatableText;
import net.minecraft.util.Identifier;
public class SpellArgumentType implements ArgumentType<Identifier> {
private static final Collection<String> EXAMPLES = Arrays.asList("sorcerycraft:heal_spell", "sorcerycraft:damage_spell");
public static final DynamicCommandExceptionType NOT_FOUND_EXCEPTION = new DynamicCommandExceptionType((object) -> new TranslatableText("command." + SorceryCraft.NAMESPACE + ".spell.unknown_spell", object));
public SpellArgumentType() {
}
public static SpellArgumentType spell() {
return new SpellArgumentType();
}
public static Identifier getSpell(CommandContext<ServerCommandSource> context, String name) throws CommandSyntaxException {
return validate(context.getArgument(name, Identifier.class));
}
private static Identifier validate(Identifier identifier) throws CommandSyntaxException {
Spell testSpell = SpellRegistry.getSpell(identifier, 0);
if (testSpell == null) {
throw NOT_FOUND_EXCEPTION.create(identifier);
}
return identifier;
}
@Override
public <S> CompletableFuture<Suggestions> listSuggestions(CommandContext<S> context, SuggestionsBuilder builder) {
return CommandSource.suggestIdentifiers(Arrays.asList(SpellRegistry.getSpellsID()), builder);
}
public Identifier parse(StringReader stringReader) throws CommandSyntaxException {
return validate(Identifier.fromCommandInput(stringReader));
}
public Collection<String> getExamples() {
return EXAMPLES;
}
}

View File

@ -1,75 +1,175 @@
package com.thebrokenrail.sorcerycraft.command; package com.thebrokenrail.sorcerycraft.command;
import com.mojang.brigadier.CommandDispatcher; import com.mojang.brigadier.CommandDispatcher;
import com.mojang.brigadier.arguments.StringArgumentType; import com.mojang.brigadier.arguments.IntegerArgumentType;
import com.mojang.brigadier.suggestion.SuggestionProvider; import com.mojang.brigadier.exceptions.DynamicCommandExceptionType;
import com.mojang.brigadier.suggestion.Suggestions; import com.thebrokenrail.sorcerycraft.SorceryCraft;
import com.mojang.brigadier.suggestion.SuggestionsBuilder; import com.thebrokenrail.sorcerycraft.spell.api.Spell;
import com.thebrokenrail.sorcerycraft.spell.SpellPlayerEntity; import com.thebrokenrail.sorcerycraft.spell.api.registry.SpellRegistry;
import com.thebrokenrail.sorcerycraft.spell.SpellTag; import com.thebrokenrail.sorcerycraft.spell.util.SpellHelper;
import net.minecraft.command.arguments.EntityArgumentType; import com.thebrokenrail.sorcerycraft.spell.util.SpellPlayerEntity;
import net.minecraft.command.argument.EntityArgumentType;
import net.minecraft.entity.player.PlayerEntity; import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.item.ItemStack;
import net.minecraft.server.command.CommandManager; import net.minecraft.server.command.CommandManager;
import net.minecraft.server.command.ServerCommandSource; import net.minecraft.server.command.ServerCommandSource;
import net.minecraft.server.network.ServerPlayerEntity;
import net.minecraft.text.Texts;
import net.minecraft.text.TranslatableText; import net.minecraft.text.TranslatableText;
import net.minecraft.util.Formatting; import net.minecraft.util.Identifier;
import java.util.Collection;
import java.util.HashMap; import java.util.HashMap;
import java.util.Locale;
import java.util.Map; import java.util.Map;
import java.util.concurrent.CompletableFuture;
public class SpellCommand { public class SpellCommand {
public static SuggestionProvider<ServerCommandSource> getOptions() { private static final DynamicCommandExceptionType NOT_HOLDING_SPELL_EXCEPTION = new DynamicCommandExceptionType(object -> new TranslatableText("command."+ SorceryCraft.NAMESPACE + ".spell.not_holding_spell", object));
return (ctx, builder) -> getSuggestionsBuilder(builder, new String[]{"clear", "list"});
}
private static CompletableFuture<Suggestions> getSuggestionsBuilder(SuggestionsBuilder builder, String[] list) {
String remaining = builder.getRemaining().toLowerCase(Locale.ROOT);
if (list.length < 1) {
return Suggestions.empty();
}
for (String str : list) {
if (str.toLowerCase(Locale.ROOT).startsWith(remaining)) {
builder.suggest(str);
}
}
return builder.buildFuture();
}
public static void register(CommandDispatcher<ServerCommandSource> dispatcher) { public static void register(CommandDispatcher<ServerCommandSource> dispatcher) {
dispatcher.register(CommandManager.literal("spell") dispatcher.register(CommandManager.literal("spell")
.requires(source -> source.hasPermissionLevel(4)) .requires(source -> source.hasPermissionLevel(2))
.then(CommandManager.argument("action", StringArgumentType.word()) .then(CommandManager.literal("list")
.suggests(getOptions()) .then(CommandManager.argument("player", EntityArgumentType.players())
.then(CommandManager.argument("player", EntityArgumentType.player()) .executes(ctx -> {
int i = 0;
Collection<ServerPlayerEntity> players = EntityArgumentType.getPlayers(ctx, "player");
for (PlayerEntity player : players) {
SpellPlayerEntity spellPlayer = (SpellPlayerEntity) player;
Map<Identifier, Integer> spellMap = spellPlayer.getDiscoveredSpells();
ctx.getSource().sendFeedback(new TranslatableText("command." + SorceryCraft.NAMESPACE + ".spell.listing_spells", player.getDisplayName(), Texts.join(spellMap.entrySet(), spell -> SpellHelper.getTranslatedSpell(spell.getKey(), spell.getValue()))), false);
i++;
}
return i;
})
)
)
.then(CommandManager.literal("forget")
.then(CommandManager.argument("player", EntityArgumentType.players())
.executes(ctx -> { .executes(ctx -> {
String action = StringArgumentType.getString(ctx, "action"); int i = 0;
PlayerEntity player = EntityArgumentType.getPlayer(ctx, "player"); Collection<ServerPlayerEntity> players = EntityArgumentType.getPlayers(ctx, "player");
switch (action) { for (PlayerEntity player : players) {
case "clear": { SpellPlayerEntity spellPlayer = (SpellPlayerEntity) player;
SpellPlayerEntity spellPlayer = (SpellPlayerEntity) player; Map<Identifier, Integer> spells = spellPlayer.getDiscoveredSpells();
spellPlayer.setSpells(new HashMap<>()); for (Map.Entry<Identifier, Integer> entry : spells.entrySet()) {
ctx.getSource().sendFeedback(new TranslatableText("command.sorcerycraft.spell.cleared_spells", player.getDisplayName()), true); ctx.getSource().sendFeedback(new TranslatableText("command." + SorceryCraft.NAMESPACE + ".spell.forgotten_spell", player.getDisplayName(), SpellHelper.getTranslatedSpellChat(entry.getKey(), entry.getValue())), true);
return 1; i++;
}
case "list": {
ctx.getSource().sendFeedback(new TranslatableText("command.sorcerycraft.spell.listing_spells", player.getDisplayName()), false);
SpellPlayerEntity spellPlayer = (SpellPlayerEntity) player;
Map<String, Integer> spells = spellPlayer.getSpells();
for (Map.Entry<String, Integer> entry : spells.entrySet()) {
ctx.getSource().sendFeedback(SpellTag.getTranslatedSpell(entry.getKey(), entry.getValue()).formatted(Formatting.YELLOW), false);
}
return 0;
}
default: {
ctx.getSource().sendFeedback(new TranslatableText("command.sorcerycraft.spell.invalid_action", action).formatted(Formatting.RED), true);
return -1;
} }
spellPlayer.setDiscoveredSpells(new HashMap<>());
} }
return i;
}) })
.then(CommandManager.argument("spell", SpellArgumentType.spell())
.executes(ctx -> {
int i = 0;
Collection<ServerPlayerEntity> players = EntityArgumentType.getPlayers(ctx, "player");
for (PlayerEntity player : players) {
Identifier spell = SpellArgumentType.getSpell(ctx, "spell");
SpellPlayerEntity spellPlayer = (SpellPlayerEntity) player;
Map<Identifier, Integer> spells = spellPlayer.getDiscoveredSpells();
if (spells.containsKey(spell)) {
ctx.getSource().sendFeedback(new TranslatableText("command." + SorceryCraft.NAMESPACE + ".spell.forgotten_spell", player.getDisplayName(), SpellHelper.getTranslatedSpellChat(spell, spells.get(spell))), true);
spells.remove(spell);
i++;
}
spellPlayer.setDiscoveredSpells(spells);
}
return i;
})
)
)
)
.then(CommandManager.literal("discover")
.then(CommandManager.argument("player", EntityArgumentType.players())
.executes(ctx -> {
int i = 0;
Collection<ServerPlayerEntity> players = EntityArgumentType.getPlayers(ctx, "player");
for (PlayerEntity player : players) {
Map<Identifier, Integer> spellMap = new HashMap<>();
Spell[] maxSpells = SpellRegistry.getMaxSpells();
for (Spell spell : maxSpells) {
spellMap.put(spell.getID(), spell.getLevel());
i++;
}
SpellHelper.learnSpells(player, spellMap);
}
return i;
})
.then(CommandManager.argument("spell", SpellArgumentType.spell())
.then(CommandManager.argument("level", IntegerArgumentType.integer())
.executes(ctx -> {
int i = 0;
Collection<ServerPlayerEntity> players = EntityArgumentType.getPlayers(ctx, "player");
for (PlayerEntity player : players) {
Identifier spell = SpellArgumentType.getSpell(ctx, "spell");
int level = IntegerArgumentType.getInteger(ctx, "level") - 1;
Map<Identifier, Integer> spellMap = new HashMap<>();
spellMap.put(spell, level);
i++;
SpellHelper.learnSpells(player, spellMap);
}
return i;
})
)
)
)
)
.then(CommandManager.literal("apply")
.then(CommandManager.argument("player", EntityArgumentType.players())
.then(CommandManager.argument("spell", SpellArgumentType.spell())
.then(CommandManager.argument("level", IntegerArgumentType.integer())
.executes(ctx -> {
int i = 0;
Collection<ServerPlayerEntity> players = EntityArgumentType.getPlayers(ctx, "player");
for (PlayerEntity player : players) {
Identifier spell = SpellArgumentType.getSpell(ctx, "spell");
int level = IntegerArgumentType.getInteger(ctx, "level") - 1;
ItemStack stack = player.getMainHandStack();
if (stack.getItem() != SorceryCraft.SPELL_ITEM) {
throw NOT_HOLDING_SPELL_EXCEPTION.create(player);
}
Map<Identifier, Integer> spellMap = SpellHelper.getSpells(stack);
spellMap.put(spell, level);
i++;
SpellHelper.setSpells(stack, spellMap);
ctx.getSource().sendFeedback(new TranslatableText("command." + SorceryCraft.NAMESPACE + ".spell.applied_spell", SpellHelper.getTranslatedSpell(spell, level)), true);
}
return i;
})
)
)
)
)
.then(CommandManager.literal("remove")
.then(CommandManager.argument("player", EntityArgumentType.players())
.then(CommandManager.argument("spell", SpellArgumentType.spell())
.executes(ctx -> {
int i = 0;
Collection<ServerPlayerEntity> players = EntityArgumentType.getPlayers(ctx, "player");
for (PlayerEntity player : players) {
Identifier spell = SpellArgumentType.getSpell(ctx, "spell");
ItemStack stack = player.getMainHandStack();
if (stack.getItem() != SorceryCraft.SPELL_ITEM) {
throw NOT_HOLDING_SPELL_EXCEPTION.create(player);
}
Map<Identifier, Integer> spellMap = SpellHelper.getSpells(stack);
if (spellMap.containsKey(spell)) {
ctx.getSource().sendFeedback(new TranslatableText("command." + SorceryCraft.NAMESPACE + ".spell.removed_spell", SpellHelper.getTranslatedSpell(spell, spellMap.get(spell))), true);
spellMap.remove(spell);
i++;
}
SpellHelper.setSpells(stack, spellMap);
}
return i;
})
)
) )
) )
); );

View File

@ -1,26 +1,36 @@
package com.thebrokenrail.sorcerycraft.entity; package com.thebrokenrail.sorcerycraft.entity;
import com.thebrokenrail.sorcerycraft.SorceryCraft; import com.thebrokenrail.sorcerycraft.SorceryCraft;
import com.thebrokenrail.sorcerycraft.spell.Spell; import com.thebrokenrail.sorcerycraft.spell.api.Spell;
import com.thebrokenrail.sorcerycraft.spell.SpellRegistry; import com.thebrokenrail.sorcerycraft.spell.api.registry.SpellRegistry;
import com.thebrokenrail.sorcerycraft.spell.SpellTag; import com.thebrokenrail.sorcerycraft.spell.util.SpellHelper;
import net.fabricmc.api.EnvType; import com.thebrokenrail.sorcerycraft.spell.api.registry.Spells;
import net.fabricmc.api.Environment; import net.minecraft.entity.Entity;
import net.minecraft.entity.*; import net.minecraft.entity.EntityType;
import net.minecraft.entity.thrown.ThrownItemEntity; import net.minecraft.entity.LivingEntity;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.entity.projectile.thrown.ThrownItemEntity;
import net.minecraft.item.Item; import net.minecraft.item.Item;
import net.minecraft.network.Packet; import net.minecraft.network.Packet;
import net.minecraft.network.packet.s2c.play.EntitySpawnS2CPacket; import net.minecraft.network.packet.s2c.play.EntitySpawnS2CPacket;
import net.minecraft.particle.ParticleTypes; import net.minecraft.particle.ParticleTypes;
import net.minecraft.server.network.ServerPlayerEntity;
import net.minecraft.server.world.ServerWorld;
import net.minecraft.sound.SoundCategory;
import net.minecraft.sound.SoundEvents; import net.minecraft.sound.SoundEvents;
import net.minecraft.util.Identifier;
import net.minecraft.util.hit.BlockHitResult;
import net.minecraft.util.hit.EntityHitResult; import net.minecraft.util.hit.EntityHitResult;
import net.minecraft.util.hit.HitResult; import net.minecraft.util.hit.HitResult;
import net.minecraft.world.World; import net.minecraft.world.World;
import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Objects;
@SuppressWarnings("unused")
public class SpellEntity extends ThrownItemEntity { public class SpellEntity extends ThrownItemEntity {
public SpellEntity(EntityType<? extends SpellEntity> entityType, World world) { public SpellEntity(EntityType<SpellEntity> entityType, World world) {
super(entityType, world); super(entityType, world);
} }
@ -32,41 +42,42 @@ public class SpellEntity extends ThrownItemEntity {
super(SorceryCraft.SPELL_ENTITY, x, y, z, world); super(SorceryCraft.SPELL_ENTITY, x, y, z, world);
} }
@Override public SpellEntity(World world) {
protected void onCollision(HitResult hitResult) { super(SorceryCraft.SPELL_ENTITY, world);
boolean remove = false; }
if (hitResult.getType() == HitResult.Type.BLOCK) {
remove = true;
} else if (hitResult.getType() == HitResult.Type.ENTITY) {
Entity entity = ((EntityHitResult) hitResult).getEntity();
Map<String, Integer> spells = SpellTag.getSpells(getItem()); private boolean didSpellSucceed(Map<Identifier, Integer> spells) {
return Math.random() > SorceryCraft.getConfig().failureChance || spells.containsKey(Spells.STEADFAST_SPELL);
for (Map.Entry<String, Integer> entry : spells.entrySet()) {
Spell spell = SpellRegistry.getSpell(entry.getKey(), entry.getValue());
if (spell != null) {
if (Math.random() > 0.3 || spells.containsKey("steadfast_spell")) {
spell.execute(entity, this, getOwner());
} else if (getOwner() != null) {
getOwner().playSound(SoundEvents.ENCHANT_THORNS_HIT, 1.0f, 1.0f);
spell.execute(getOwner(), this, getOwner());
}
}
}
remove = true;
}
if (remove && !getEntityWorld().isClient()) {
remove();
}
} }
@Override @Override
@Environment(EnvType.CLIENT) protected void onCollision(HitResult hitResult) {
public void handleStatus(byte status) { super.onCollision(hitResult);
if (status == 0) { if (!getEntityWorld().isClient()) {
for (int i = 0; i < 12; i++) { Map<Identifier, Integer> spells = SpellHelper.getSpells(getItem());
getEntityWorld().addParticle(ParticleTypes.WITCH, getX(), getY(), getZ(), 0.0d, 0.0d, 0.0d); if (!spells.containsKey(Spells.INWARD_SPELL)) {
boolean success = didSpellSucceed(spells);
for (Map.Entry<Identifier, Integer> entry : spells.entrySet()) {
Spell spell = SpellRegistry.getSpell(entry);
if (spell != null) {
if (success) {
if (hitResult.getType() == HitResult.Type.BLOCK) {
BlockHitResult blockHitResult = (BlockHitResult) hitResult;
spell.execute(getEntityWorld(), this, getOwner(), blockHitResult);
} else if (hitResult.getType() == HitResult.Type.ENTITY) {
Entity entity = ((EntityHitResult) hitResult).getEntity();
spell.execute(world, this, getOwner(), entity);
}
} else if (getOwner() != null) {
if (getOwner() instanceof PlayerEntity) {
PlayerEntity player = (PlayerEntity) getOwner();
player.playSound(SoundEvents.ENCHANT_THORNS_HIT, SoundCategory.PLAYERS, 1.0f, 1.0f);
}
spell.execute(world, this, getOwner(), getOwner());
}
}
}
remove();
} }
} }
} }
@ -75,7 +86,30 @@ public class SpellEntity extends ThrownItemEntity {
public void tick() { public void tick() {
super.tick(); super.tick();
if (!getEntityWorld().isClient()) { if (!getEntityWorld().isClient()) {
getEntityWorld().sendEntityStatus(this, (byte) 0); Map<Identifier, Integer> spells = SpellHelper.getSpells(getItem());
if (spells.containsKey(Spells.INWARD_SPELL)) {
if (getOwner() != null) {
boolean success = didSpellSucceed(spells);
for (Map.Entry<Identifier, Integer> entry : spells.entrySet()) {
Spell spell = SpellRegistry.getSpell(entry);
if (spell != null) {
if (success) {
spell.execute(world, this, getOwner(), getOwner());
} else if (getOwner() instanceof PlayerEntity) {
PlayerEntity player = (PlayerEntity) getOwner();
player.playSound(SoundEvents.ENCHANT_THORNS_HIT, SoundCategory.PLAYERS, 1.0f, 1.0f);
}
}
}
}
remove();
return;
}
List<ServerPlayerEntity> viewers = Objects.requireNonNull(getServer()).getPlayerManager().getPlayerList();
for (ServerPlayerEntity viewer : viewers) {
((ServerWorld) getEntityWorld()).spawnParticles(viewer, ParticleTypes.WITCH, true, getX(), getY(), getZ(), 8, 0.1d, 0.1d, 0.1d, 0d);
}
} }
} }

View File

@ -1,57 +1,50 @@
package com.thebrokenrail.sorcerycraft.block; package com.thebrokenrail.sorcerycraft.gui;
import com.thebrokenrail.sorcerycraft.SorceryCraft; import com.thebrokenrail.sorcerycraft.SorceryCraft;
import com.thebrokenrail.sorcerycraft.spell.Spell; import com.thebrokenrail.sorcerycraft.spell.api.Spell;
import com.thebrokenrail.sorcerycraft.spell.SpellPlayerEntity; import com.thebrokenrail.sorcerycraft.spell.util.SpellPlayerEntity;
import com.thebrokenrail.sorcerycraft.spell.SpellRegistry; import com.thebrokenrail.sorcerycraft.spell.api.registry.SpellRegistry;
import com.thebrokenrail.sorcerycraft.spell.SpellTag; import com.thebrokenrail.sorcerycraft.spell.util.SpellHelper;
import net.minecraft.container.*;
import net.minecraft.entity.player.PlayerEntity; import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.entity.player.PlayerInventory; import net.minecraft.entity.player.PlayerInventory;
import net.minecraft.inventory.BasicInventory;
import net.minecraft.inventory.CraftingResultInventory; import net.minecraft.inventory.CraftingResultInventory;
import net.minecraft.inventory.Inventory; import net.minecraft.inventory.Inventory;
import net.minecraft.inventory.SimpleInventory;
import net.minecraft.item.ItemStack; import net.minecraft.item.ItemStack;
import net.minecraft.sound.SoundEvents; import net.minecraft.screen.ScreenHandler;
import net.minecraft.screen.ScreenHandlerContext;
import net.minecraft.screen.ScreenHandlerType;
import net.minecraft.screen.slot.Slot;
import net.minecraft.server.network.ServerPlayerEntity;
import net.minecraft.util.Identifier;
import net.minecraft.util.math.BlockPos;
import net.minecraft.world.World;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.function.BiConsumer;
public class CastingTableContainer extends Container { public class CastingTableScreenHandler extends ScreenHandler {
private final Inventory inventory; private final Inventory inventory;
private final Inventory result; private final Inventory result;
private final Spell[] spells; private Spell[] spells = new Spell[0];
private final BlockContext context; private final ScreenHandlerContext context;
private int index = 0; private int index = 0;
public CastingTableContainer(int syncId, PlayerInventory playerInventory, BlockContext blockContext) { public CastingTableScreenHandler(int syncId, PlayerInventory playerInventory, ScreenHandlerContext blockContext) {
super(ContainerType.STONECUTTER, syncId); super(ScreenHandlerType.STONECUTTER, syncId);
inventory = new BasicInventory(2) { inventory = new SimpleInventory(2) {
public void markDirty() { public void markDirty() {
super.markDirty(); super.markDirty();
CastingTableContainer.this.onContentChanged(this); CastingTableScreenHandler.this.onContentChanged(this);
} }
}; };
context = blockContext; context = blockContext;
result = new CraftingResultInventory(); result = new CraftingResultInventory();
if (playerInventory.player.isCreative()) { setSpells(playerInventory.player);
spells = SpellRegistry.getSpells();
} else {
SpellPlayerEntity spellPlayer = (SpellPlayerEntity) playerInventory.player;
Map<String, Integer> spellsMap = spellPlayer.getSpells();
List<Spell> spellsArray = new ArrayList<>();
Spell[] allSpells = SpellRegistry.getSpells();
for (Spell spell : allSpells) {
if (spellsMap.containsKey(spell.getID()) && spellsMap.get(spell.getID()) >= spell.getLevel()) {
spellsArray.add(spell);
}
}
spells = spellsArray.toArray(new Spell[0]);
}
addSlot(new Slot(inventory, 0, 136, 37) { addSlot(new Slot(inventory, 0, 136, 37) {
@Override @Override
@ -60,7 +53,7 @@ public class CastingTableContainer extends Container {
} }
@Override @Override
public int getMaxStackAmount() { public int getMaxItemCount() {
return 1; return 1;
} }
@ -91,17 +84,22 @@ public class CastingTableContainer extends Container {
player.addExperienceLevels(-spells[index].getXPCost()); player.addExperienceLevels(-spells[index].getXPCost());
} }
player.playSound(SoundEvents.BLOCK_ENCHANTMENT_TABLE_USE, 1.0f, 1.0f); context.run((world, blockPos) -> {
SorceryCraft.playSpellSound(world, blockPos);
if (!world.isClient()) {
SorceryCraft.CREATE_SPELL_CRITERION.trigger((ServerPlayerEntity) player);
}
});
CastingTableContainer.this.inventory.setInvStack(0, ItemStack.EMPTY); CastingTableScreenHandler.this.inventory.setStack(0, ItemStack.EMPTY);
CastingTableContainer.this.inventory.takeInvStack(1, spells[index].getItemCost().getCount()); CastingTableScreenHandler.this.inventory.removeStack(1, spells[index].getItemCost().getCount());
return stack; return stack;
} }
}); });
int k; int k;
for (k = 0; k < 3; ++k) { for (k = 0; k < 3; ++k) {
for(int j = 0; j < 9; ++j) { for (int j = 0; j < 9; ++j) {
addSlot(new Slot(playerInventory, j + k * 9 + 9, 108 + j * 18, 84 + k * 18)); addSlot(new Slot(playerInventory, j + k * 9 + 9, 108 + j * 18, 84 + k * 18));
} }
} }
@ -111,15 +109,39 @@ public class CastingTableContainer extends Container {
} }
} }
private void resetIndex() {
index = 0;
onContentChanged(inventory);
}
public void setSpells(PlayerEntity player) {
if (player.isCreative() ? SorceryCraft.getConfig().limitCastingTable.creative : SorceryCraft.getConfig().limitCastingTable.survival) {
SpellPlayerEntity spellPlayer = (SpellPlayerEntity) player;
Map<Identifier, Integer> spellsMap = spellPlayer.getDiscoveredSpells();
List<Spell> spellsArray = new ArrayList<>();
Spell[] allSpells = SpellRegistry.getSpells();
for (Spell spell : allSpells) {
if (spellsMap.containsKey(spell.getID()) && spellsMap.get(spell.getID()) >= spell.getLevel()) {
spellsArray.add(spell);
}
}
spells = spellsArray.toArray(new Spell[0]);
} else {
spells = SpellRegistry.getSpells();
}
resetIndex();
}
public boolean canTakeResult(PlayerEntity playerEntity) { public boolean canTakeResult(PlayerEntity playerEntity) {
return (playerEntity.isCreative() || playerEntity.experienceLevel >= spells[index].getXPCost()) && spells[index].getXPCost() > 0; return playerEntity.isCreative() || playerEntity.experienceLevel >= spells[index].getXPCost();
} }
public void setIndex(int index) { public void setIndex(int index) {
this.index = index; this.index = index;
onContentChanged(inventory); onContentChanged(inventory);
if (inventory.getInvStack(0).isEmpty() && inventory.getInvStack(1).isEmpty()) { if (inventory.getStack(0).isEmpty() && inventory.getStack(1).isEmpty()) {
ItemStack spellItem = new ItemStack(SorceryCraft.SPELL_ITEM); ItemStack spellItem = new ItemStack(SorceryCraft.SPELL_ITEM);
autoFill(0, spellItem, true); autoFill(0, spellItem, true);
ItemStack paymentItem = getRecipes()[index].getItemCost(); ItemStack paymentItem = getRecipes()[index].getItemCost();
@ -127,33 +149,71 @@ public class CastingTableContainer extends Container {
} }
} }
public ItemStack transferSlot(PlayerEntity player, int invSlot) {
ItemStack itemStack = ItemStack.EMPTY;
Slot slot = slots.get(invSlot);
if (slot != null && slot.hasStack()) {
ItemStack itemStack2 = slot.getStack();
itemStack = itemStack2.copy();
if (invSlot == 2) {
if (!insertItem(itemStack2, 3, 39, true)) {
return ItemStack.EMPTY;
}
slot.onStackChanged(itemStack2, itemStack);
} else if (invSlot != 0 && invSlot != 1) {
if (invSlot < 30) {
if (!insertItem(itemStack2, 30, 39, false)) {
return ItemStack.EMPTY;
}
} else if (invSlot < 39 && !insertItem(itemStack2, 3, 30, false)) {
return ItemStack.EMPTY;
}
} else if (!insertItem(itemStack2, 3, 39, false)) {
return ItemStack.EMPTY;
}
if (itemStack2.isEmpty()) {
slot.setStack(ItemStack.EMPTY);
} else {
slot.markDirty();
}
if (itemStack2.getCount() == itemStack.getCount()) {
return ItemStack.EMPTY;
}
slot.onTakeItem(player, itemStack2);
}
return itemStack;
}
@Override @Override
public void close(PlayerEntity player) { public void close(PlayerEntity player) {
super.close(player); super.close(player);
context.run((world, blockPos) -> { context.run((BiConsumer<World, BlockPos>) (world, blockPos) -> dropInventory(player, world, inventory));
dropInventory(player, world, inventory);
});
} }
@Override @Override
public void onContentChanged(Inventory inventory) { public void onContentChanged(Inventory inventory) {
super.onContentChanged(inventory); super.onContentChanged(inventory);
ItemStack item = inventory.getInvStack(0); ItemStack item = inventory.getStack(0);
ItemStack cost = inventory.getInvStack(1); ItemStack cost = inventory.getStack(1);
if (inventory == this.inventory) { if (inventory == this.inventory) {
if (spells.length > 0 && if (spells.length > 0 &&
!item.isEmpty() && !item.isEmpty() &&
cost.getItem() == spells[index].getItemCost().getItem() && cost.getItem() == spells[index].getItemCost().getItem() &&
cost.getCount() >= spells[index].getItemCost().getCount()) { cost.getCount() >= spells[index].getItemCost().getCount()) {
ItemStack resultItem = item.copy(); ItemStack resultItem = item.copy();
Map<String, Integer> resultSpells = SpellTag.getSpells(resultItem); Map<Identifier, Integer> resultSpells = SpellHelper.getSpells(resultItem);
if (!resultSpells.containsKey(spells[index].getID()) || resultSpells.get(spells[index].getID()) <= spells[index].getLevel()) { if (!resultSpells.containsKey(spells[index].getID()) || resultSpells.get(spells[index].getID()) <= spells[index].getLevel()) {
resultSpells.put(spells[index].getID(), spells[index].getLevel()); resultSpells.put(spells[index].getID(), spells[index].getLevel());
} }
SpellTag.setSpells(resultItem, resultSpells); SpellHelper.setSpells(resultItem, resultSpells);
result.setInvStack(2, resultItem); result.setStack(2, resultItem);
} else { } else {
result.setInvStack(2, ItemStack.EMPTY); result.setStack(2, ItemStack.EMPTY);
} }
} }
} }
@ -162,15 +222,15 @@ public class CastingTableContainer extends Container {
if (!stack.isEmpty()) { if (!stack.isEmpty()) {
for (int i = 3; i < 39; ++i) { for (int i = 3; i < 39; ++i) {
ItemStack itemStack = slots.get(i).getStack(); ItemStack itemStack = slots.get(i).getStack();
if (!itemStack.isEmpty() && stack.getItem() == itemStack.getItem()) { if (!itemStack.isEmpty() && itemCompatible(stack, itemStack)) {
ItemStack invSlot = inventory.getInvStack(slot); ItemStack invSlot = inventory.getStack(slot);
int count = invSlot.isEmpty() ? 0 : invSlot.getCount(); int count = invSlot.isEmpty() ? 0 : invSlot.getCount();
int requiredCount = Math.min((onlyOne ? 1 : stack.getMaxCount()) - count, itemStack.getCount()); int requiredCount = Math.min((onlyOne ? 1 : stack.getMaxCount()) - count, itemStack.getCount());
ItemStack modifiedItem = itemStack.copy(); ItemStack modifiedItem = itemStack.copy();
int totalCount = count + requiredCount; int totalCount = count + requiredCount;
itemStack.decrement(requiredCount); itemStack.decrement(requiredCount);
modifiedItem.setCount(totalCount); modifiedItem.setCount(totalCount);
inventory.setInvStack(slot, modifiedItem); inventory.setStack(slot, modifiedItem);
if (totalCount >= stack.getMaxCount() || onlyOne) { if (totalCount >= stack.getMaxCount() || onlyOne) {
break; break;
} }
@ -179,9 +239,13 @@ public class CastingTableContainer extends Container {
} }
} }
private boolean itemCompatible(ItemStack itemStack, ItemStack otherItemStack) {
return itemStack.getItem() == otherItemStack.getItem();
}
@Override @Override
public boolean canUse(PlayerEntity player) { public boolean canUse(PlayerEntity player) {
return true; return context.run((world, blockPos) -> world.getBlockState(blockPos).getBlock().equals(SorceryCraft.CASTING_TABLE_BLOCK) && player.squaredDistanceTo((double) blockPos.getX() + 0.5D, (double) blockPos.getY() + 0.5D, (double) blockPos.getZ() + 0.5D) <= 64.0D, true);
} }
public Spell[] getRecipes() { public Spell[] getRecipes() {

View File

@ -2,20 +2,23 @@ package com.thebrokenrail.sorcerycraft.item;
import com.thebrokenrail.sorcerycraft.SorceryCraft; import com.thebrokenrail.sorcerycraft.SorceryCraft;
import com.thebrokenrail.sorcerycraft.entity.SpellEntity; import com.thebrokenrail.sorcerycraft.entity.SpellEntity;
import com.thebrokenrail.sorcerycraft.packet.LearnedNewSpellS2CPacket; import com.thebrokenrail.sorcerycraft.spell.api.Spell;
import com.thebrokenrail.sorcerycraft.spell.*; import com.thebrokenrail.sorcerycraft.spell.api.registry.SpellRegistry;
import com.thebrokenrail.sorcerycraft.spell.util.SpellHelper;
import net.minecraft.client.item.TooltipContext; import net.minecraft.client.item.TooltipContext;
import net.minecraft.entity.Entity; import net.minecraft.entity.Entity;
import net.minecraft.entity.LivingEntity;
import net.minecraft.entity.player.PlayerEntity; import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.item.Item; import net.minecraft.item.Item;
import net.minecraft.item.ItemGroup; import net.minecraft.item.ItemGroup;
import net.minecraft.item.ItemStack; import net.minecraft.item.ItemStack;
import net.minecraft.server.network.ServerPlayerEntity;
import net.minecraft.sound.SoundEvents;
import net.minecraft.text.LiteralText;
import net.minecraft.text.Text; import net.minecraft.text.Text;
import net.minecraft.text.TranslatableText; import net.minecraft.util.ActionResult;
import net.minecraft.util.*; import net.minecraft.util.Hand;
import net.minecraft.util.Identifier;
import net.minecraft.util.Rarity;
import net.minecraft.util.TypedActionResult;
import net.minecraft.util.collection.DefaultedList;
import net.minecraft.world.World; import net.minecraft.world.World;
import java.util.HashMap; import java.util.HashMap;
@ -28,42 +31,44 @@ public class SpellItem extends Item {
} }
@Override @Override
public TypedActionResult<ItemStack> use(World world, PlayerEntity playerEntity, Hand hand) { public TypedActionResult<ItemStack> use(World world, PlayerEntity playerEntity, Hand hand) {
ItemStack itemStack = playerEntity.getStackInHand(hand); ItemStack itemStack = playerEntity.getStackInHand(hand);
Map<String, Integer> spells = SpellTag.getSpells(itemStack); if (!world.isClient()) {
SorceryCraft.playSpellSound(playerEntity);
if (spells.size() > 0) { playerEntity.incrementStat(SorceryCraft.CAST_SPELL_STAT);
playerEntity.playSound(SoundEvents.BLOCK_ENCHANTMENT_TABLE_USE, 1.0f, 1.0f);
if (!world.isClient()) { SpellEntity entity = new SpellEntity(world, playerEntity);
SpellEntity entity = new SpellEntity(world, playerEntity); entity.setItem(itemStack);
entity.setItem(itemStack); entity.setProperties(playerEntity, playerEntity.pitch, playerEntity.yaw, 0.0f, 1.5f, 1.0f);
entity.setProperties(playerEntity, playerEntity.pitch, playerEntity.yaw, 0.0f, 1.5f, 1.0f); world.spawnEntity(entity);
world.spawnEntity(entity);
}
if (!playerEntity.isCreative()) {
itemStack.decrement(1);
}
return new TypedActionResult<>(ActionResult.SUCCESS, itemStack);
} else {
return new TypedActionResult<>(ActionResult.FAIL, itemStack);
} }
if (!playerEntity.isCreative()) {
itemStack.decrement(1);
}
return new TypedActionResult<>(ActionResult.SUCCESS, itemStack);
} }
@Override @Override
public boolean hasEnchantmentGlint(ItemStack stack) { public ActionResult useOnEntity(ItemStack stack, PlayerEntity user, LivingEntity entity, Hand hand) {
Map<String, Integer> spells = SpellTag.getSpells(stack); use(user.getEntityWorld(), user, hand);
return spells.size() > 0; return ActionResult.SUCCESS;
}
@Override
public boolean hasGlint(ItemStack stack) {
Map<Identifier, Integer> spells = SpellHelper.getSpells(stack);
return spells.size() > 0 || super.hasGlint(stack);
} }
@Override @Override
public void appendTooltip(ItemStack itemStack, World world, List<Text> tooltip, TooltipContext tooltipContext) { public void appendTooltip(ItemStack itemStack, World world, List<Text> tooltip, TooltipContext tooltipContext) {
Map<String, Integer> spells = SpellTag.getSpells(itemStack); Map<Identifier, Integer> spells = SpellHelper.getSpells(itemStack);
for (Map.Entry<String, Integer> entry : spells.entrySet()) { for (Map.Entry<Identifier, Integer> entry : spells.entrySet()) {
tooltip.add(SpellTag.getTranslatedSpell(entry.getKey(), entry.getValue())); tooltip.add(SpellHelper.getTranslatedSpell(entry.getKey(), entry.getValue()));
} }
} }
@ -74,9 +79,9 @@ public class SpellItem extends Item {
Spell[] spells = SpellRegistry.getSpells(); Spell[] spells = SpellRegistry.getSpells();
for (Spell value : spells) { for (Spell value : spells) {
ItemStack item = new ItemStack(this); ItemStack item = new ItemStack(this);
Map<String, Integer> spell = new HashMap<>(); Map<Identifier, Integer> spell = new HashMap<>();
spell.put(value.getID(), value.getLevel()); spell.put(value.getID(), value.getLevel());
SpellTag.setSpells(item, spell); SpellHelper.setSpells(item, spell);
stacks.add(item); stacks.add(item);
} }
} }
@ -92,35 +97,9 @@ public class SpellItem extends Item {
super.inventoryTick(stack, world, entity, slot, selected); super.inventoryTick(stack, world, entity, slot, selected);
if (!world.isClient() && entity instanceof PlayerEntity) { if (!world.isClient() && entity instanceof PlayerEntity) {
PlayerEntity player = (PlayerEntity) entity; PlayerEntity player = (PlayerEntity) entity;
SpellPlayerEntity spellPlayer = (SpellPlayerEntity) player; Map<Identifier, Integer> itemSpells = SpellHelper.getSpells(player.inventory.getStack(slot));
Map<String, Integer> playerSpells = spellPlayer.getSpells(); SpellHelper.learnSpells(player, itemSpells);
Map<String, Integer> itemSpells = SpellTag.getSpells(player.inventory.getInvStack(slot));
boolean changed = false;
for (Map.Entry<String, Integer> entry : itemSpells.entrySet()) {
Spell spell = SpellRegistry.getSpell(entry.getKey(), entry.getValue());
if (spell != null) {
if (spell.getLevel() >= spell.getMaxLevel()) {
spell = SpellRegistry.getSpell(entry.getKey(), spell.getMaxLevel() - 1);
}
assert spell != null;
if (!playerSpells.containsKey(spell.getID()) || playerSpells.get(spell.getID()) < spell.getLevel()) {
changed = true;
playerSpells.put(spell.getID(), spell.getLevel());
assert world.getServer() != null;
Text text = new LiteralText("[").append(SpellTag.getTranslatedSpell(spell.getID(), spell.getLevel()).getString()).append("]").formatted(Formatting.GREEN);
world.getServer().getPlayerManager().sendToAll(new TranslatableText("chat.sorcerycraft.new_spell", player.getDisplayName(), text));
}
}
}
if (changed) {
LearnedNewSpellS2CPacket.send((ServerPlayerEntity) player);
spellPlayer.setSpells(playerSpells);
}
} }
} }
} }

View File

@ -0,0 +1,14 @@
package com.thebrokenrail.sorcerycraft.mixin;
import net.minecraft.advancement.criterion.Criteria;
import net.minecraft.advancement.criterion.Criterion;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.gen.Invoker;
@Mixin(Criteria.class)
public interface CriteriaRegistryHook {
@Invoker("register")
static <T extends Criterion<?>> T callRegister(T criterion) {
return criterion;
}
}

View File

@ -0,0 +1,24 @@
package com.thebrokenrail.sorcerycraft.mixin;
import com.thebrokenrail.sorcerycraft.client.gui.CastingTableScreen;
import net.minecraft.client.font.TextRenderer;
import net.minecraft.client.gui.DrawableHelper;
import net.minecraft.client.gui.widget.AbstractButtonWidget;
import net.minecraft.client.util.math.MatrixStack;
import net.minecraft.text.Text;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Redirect;
@SuppressWarnings("ConstantConditions")
@Mixin(AbstractButtonWidget.class)
public class MixinAbstractButtonWidget {
@Redirect(at = @At(value = "INVOKE", target = "Lnet/minecraft/client/gui/widget/AbstractButtonWidget;drawCenteredText(Lnet/minecraft/client/util/math/MatrixStack;Lnet/minecraft/client/font/TextRenderer;Lnet/minecraft/text/Text;III)V"), method = "renderButton")
public void drawCenteredText(MatrixStack matrices, TextRenderer textRenderer, Text text, int centerX, int y, int color) {
if ((Object) this instanceof CastingTableScreen.WidgetButtonPage) {
DrawableHelper.drawStringWithShadow(matrices, textRenderer, text.getString(), ((CastingTableScreen.WidgetButtonPage) (Object) this).x + 5, y, color);
} else {
DrawableHelper.drawCenteredText(matrices, textRenderer, text, centerX, y, color);
}
}
}

View File

@ -2,6 +2,8 @@ package com.thebrokenrail.sorcerycraft.mixin;
import com.thebrokenrail.sorcerycraft.SorceryCraft; import com.thebrokenrail.sorcerycraft.SorceryCraft;
import com.thebrokenrail.sorcerycraft.entity.SpellEntity; import com.thebrokenrail.sorcerycraft.entity.SpellEntity;
import net.fabricmc.api.EnvType;
import net.fabricmc.api.Environment;
import net.minecraft.client.network.ClientPlayNetworkHandler; import net.minecraft.client.network.ClientPlayNetworkHandler;
import net.minecraft.client.world.ClientWorld; import net.minecraft.client.world.ClientWorld;
import net.minecraft.entity.Entity; import net.minecraft.entity.Entity;
@ -14,12 +16,14 @@ import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
@SuppressWarnings("unused") @SuppressWarnings("unused")
@Environment(EnvType.CLIENT)
@Mixin(ClientPlayNetworkHandler.class) @Mixin(ClientPlayNetworkHandler.class)
public class MixinClientPlayNetworkHandler { public class MixinClientPlayNetworkHandler {
@Shadow private ClientWorld world; @Shadow
private ClientWorld world;
@Inject(method = "onEntitySpawn", at = @At(value = "TAIL")) @Inject(method = "onEntitySpawn", at = @At(value = "TAIL"))
public void onEntitySpawn(EntitySpawnS2CPacket packet, CallbackInfo callbackInfo) { public void onEntitySpawn(EntitySpawnS2CPacket packet, CallbackInfo info) {
EntityType<?> entityType = packet.getEntityTypeId(); EntityType<?> entityType = packet.getEntityTypeId();
Entity entity = null; Entity entity = null;

View File

@ -0,0 +1,30 @@
package com.thebrokenrail.sorcerycraft.mixin;
import com.thebrokenrail.sorcerycraft.client.gui.CastingTableScreen;
import net.fabricmc.api.EnvType;
import net.fabricmc.api.Environment;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.network.ClientPlayerEntity;
import net.minecraft.util.Identifier;
import org.spongepowered.asm.mixin.Final;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import java.util.Map;
@SuppressWarnings("unused")
@Mixin(ClientPlayerEntity.class)
@Environment(EnvType.CLIENT)
public class MixinClientPlayerEntity extends MixinPlayerEntity {
@Shadow
@Final
protected MinecraftClient client;
@Override
public void setDiscoveredSpells(Map<Identifier, Integer> spells) {
super.setDiscoveredSpells(spells);
if (client.currentScreen instanceof CastingTableScreen) {
((CastingTableScreen) client.currentScreen).resetIndex();
}
}
}

View File

@ -1,38 +1,51 @@
package com.thebrokenrail.sorcerycraft.mixin; package com.thebrokenrail.sorcerycraft.mixin;
import com.thebrokenrail.sorcerycraft.spell.SpellPlayerEntity; import com.thebrokenrail.sorcerycraft.gui.CastingTableScreenHandler;
import com.thebrokenrail.sorcerycraft.spell.SpellTag; import com.thebrokenrail.sorcerycraft.spell.util.SpellHelper;
import com.thebrokenrail.sorcerycraft.spell.util.SpellPlayerEntity;
import net.minecraft.entity.player.PlayerEntity; import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.nbt.CompoundTag; import net.minecraft.nbt.CompoundTag;
import net.minecraft.screen.ScreenHandler;
import net.minecraft.util.Identifier;
import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.Unique;
import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import java.util.HashMap;
import java.util.Map; import java.util.Map;
@SuppressWarnings("unused") @SuppressWarnings("unused")
@Mixin(PlayerEntity.class) @Mixin(PlayerEntity.class)
public class MixinPlayerEntity implements SpellPlayerEntity { public class MixinPlayerEntity implements SpellPlayerEntity {
private Map<String, Integer> spells; @Shadow
public ScreenHandler currentScreenHandler;
@Unique
private Map<Identifier, Integer> discoveredSpells = new HashMap<>();
@Inject(at = @At("HEAD"), method = "readCustomDataFromTag") @Inject(at = @At("HEAD"), method = "readCustomDataFromTag")
public void readCustomDataFromTag(CompoundTag tag, CallbackInfo info) { public void readCustomDataFromTag(CompoundTag tag, CallbackInfo info) {
spells = SpellTag.getSpells(tag); discoveredSpells = SpellHelper.getSpells(tag);
} }
@Inject(at = @At("HEAD"), method = "writeCustomDataToTag") @Inject(at = @At("HEAD"), method = "writeCustomDataToTag")
public void writeCustomDataToTag(CompoundTag tag, CallbackInfo info) { public void writeCustomDataToTag(CompoundTag tag, CallbackInfo info) {
tag.put(SpellTag.SPELL_TAG, SpellTag.createSpellsTag(spells)); tag.put(SpellHelper.SPELL_TAG, SpellHelper.createSpellsTag(discoveredSpells));
} }
@Override @Override
public void setSpells(Map<String, Integer> spells) { public void setDiscoveredSpells(Map<Identifier, Integer> spells) {
this.spells = spells; discoveredSpells = spells;
if (currentScreenHandler instanceof CastingTableScreenHandler) {
//noinspection ConstantConditions
((CastingTableScreenHandler) currentScreenHandler).setSpells((PlayerEntity) (Object) this);
}
} }
@Override @Override
public Map<String, Integer> getSpells() { public Map<Identifier, Integer> getDiscoveredSpells() {
return spells; return discoveredSpells;
} }
} }

View File

@ -1,30 +1,45 @@
package com.thebrokenrail.sorcerycraft.mixin; package com.thebrokenrail.sorcerycraft.mixin;
import com.thebrokenrail.sorcerycraft.gui.CastingTableScreenHandler;
import com.thebrokenrail.sorcerycraft.packet.UpdateKnownSpellsS2CPacket; import com.thebrokenrail.sorcerycraft.packet.UpdateKnownSpellsS2CPacket;
import com.thebrokenrail.sorcerycraft.spell.SpellPlayerEntity; import com.thebrokenrail.sorcerycraft.spell.util.SpellHelper;
import com.thebrokenrail.sorcerycraft.spell.SpellTag; import com.thebrokenrail.sorcerycraft.spell.util.SpellPlayerEntity;
import com.thebrokenrail.sorcerycraft.spell.util.SpellServerPlayerEntity;
import net.fabricmc.api.EnvType;
import net.fabricmc.api.Environment;
import net.minecraft.nbt.CompoundTag; import net.minecraft.nbt.CompoundTag;
import net.minecraft.server.network.ServerPlayerEntity; import net.minecraft.server.network.ServerPlayerEntity;
import net.minecraft.util.Identifier;
import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import java.util.Map;
@SuppressWarnings("unused") @SuppressWarnings("unused")
@Mixin(ServerPlayerEntity.class) @Mixin(ServerPlayerEntity.class)
public abstract class MixinServerPlayerEntity implements SpellPlayerEntity { public abstract class MixinServerPlayerEntity extends MixinPlayerEntity implements SpellServerPlayerEntity {
@Inject(at = @At("HEAD"), method = "copyFrom") @Inject(at = @At("HEAD"), method = "copyFrom")
public void copyFrom(ServerPlayerEntity oldPlayer, boolean alive, CallbackInfo ci) { public void copyFrom(ServerPlayerEntity oldPlayer, boolean alive, CallbackInfo info) {
SpellPlayerEntity oldSpellPlayer = (SpellPlayerEntity) oldPlayer; SpellPlayerEntity oldSpellPlayer = (SpellPlayerEntity) oldPlayer;
SpellPlayerEntity newSpellPlayer = this; SpellPlayerEntity newSpellPlayer = this;
newSpellPlayer.setSpells(oldSpellPlayer.getSpells()); newSpellPlayer.setDiscoveredSpells(oldSpellPlayer.getDiscoveredSpells());
} }
@Inject(at = @At("HEAD"), method = "playerTick") @Override
public void playerTick(CallbackInfo ignored) { public void setDiscoveredSpells(Map<Identifier, Integer> spells) {
super.setDiscoveredSpells(spells);
if (currentScreenHandler instanceof CastingTableScreenHandler) {
sync();
}
}
@Override
public void sync() {
CompoundTag tag = new CompoundTag(); CompoundTag tag = new CompoundTag();
tag.put(SpellTag.SPELL_TAG, SpellTag.createSpellsTag(getSpells())); tag.put(SpellHelper.SPELL_TAG, SpellHelper.createSpellsTag(getDiscoveredSpells()));
//noinspection ConstantConditions //noinspection ConstantConditions
UpdateKnownSpellsS2CPacket.send((ServerPlayerEntity) (Object) this, tag); UpdateKnownSpellsS2CPacket.send((ServerPlayerEntity) (Object) this, tag);
} }

View File

@ -1,24 +0,0 @@
package com.thebrokenrail.sorcerycraft.packet;
import com.thebrokenrail.sorcerycraft.SorceryCraft;
import io.netty.buffer.Unpooled;
import net.fabricmc.fabric.api.network.PacketContext;
import net.minecraft.network.packet.s2c.play.CustomPayloadS2CPacket;
import net.minecraft.server.network.ServerPlayerEntity;
import net.minecraft.sound.SoundEvents;
import net.minecraft.util.Identifier;
import net.minecraft.util.PacketByteBuf;
public class LearnedNewSpellS2CPacket {
@SuppressWarnings("unused")
public static void handle(PacketContext context, PacketByteBuf bytes) {
if (context.getPlayer() != null) {
context.getPlayer().playSound(SoundEvents.BLOCK_ENCHANTMENT_TABLE_USE, 1.0f, 1.0f);
}
}
public static void send(ServerPlayerEntity player) {
PacketByteBuf bytes = new PacketByteBuf(Unpooled.buffer());
player.networkHandler.sendPacket(new CustomPayloadS2CPacket(new Identifier(SorceryCraft.NAMESPACE, "learned_new_spell"), bytes));
}
}

View File

@ -1,25 +1,28 @@
package com.thebrokenrail.sorcerycraft.packet; package com.thebrokenrail.sorcerycraft.packet;
import com.thebrokenrail.sorcerycraft.SorceryCraft; import com.thebrokenrail.sorcerycraft.SorceryCraft;
import com.thebrokenrail.sorcerycraft.block.CastingTableContainer; import com.thebrokenrail.sorcerycraft.gui.CastingTableScreenHandler;
import io.netty.buffer.Unpooled; import io.netty.buffer.Unpooled;
import net.fabricmc.api.EnvType;
import net.fabricmc.api.Environment;
import net.fabricmc.fabric.api.network.PacketContext; import net.fabricmc.fabric.api.network.PacketContext;
import net.minecraft.client.MinecraftClient; import net.minecraft.client.MinecraftClient;
import net.minecraft.container.Container; import net.minecraft.network.PacketByteBuf;
import net.minecraft.network.packet.c2s.play.CustomPayloadC2SPacket; import net.minecraft.network.packet.c2s.play.CustomPayloadC2SPacket;
import net.minecraft.screen.ScreenHandler;
import net.minecraft.util.Identifier; import net.minecraft.util.Identifier;
import net.minecraft.util.PacketByteBuf;
public class SelectSpellC2SPacket { public class SelectSpellC2SPacket {
public static void handle(PacketContext context, PacketByteBuf bytes) { public static void handle(PacketContext context, PacketByteBuf bytes) {
int index = bytes.readInt(); int index = bytes.readInt();
Container container = context.getPlayer().container; ScreenHandler handler = context.getPlayer().currentScreenHandler;
if (container instanceof CastingTableContainer) { if (handler instanceof CastingTableScreenHandler) {
CastingTableContainer merchantContainer = (CastingTableContainer) container; CastingTableScreenHandler merchantContainer = (CastingTableScreenHandler) handler;
merchantContainer.setIndex(index); merchantContainer.setIndex(index);
} }
} }
@Environment(EnvType.CLIENT)
public static void send(MinecraftClient minecraft, int index) { public static void send(MinecraftClient minecraft, int index) {
PacketByteBuf bytes = new PacketByteBuf(Unpooled.buffer()); PacketByteBuf bytes = new PacketByteBuf(Unpooled.buffer());
bytes.writeInt(index); bytes.writeInt(index);

View File

@ -1,22 +1,25 @@
package com.thebrokenrail.sorcerycraft.packet; package com.thebrokenrail.sorcerycraft.packet;
import com.thebrokenrail.sorcerycraft.SorceryCraft; import com.thebrokenrail.sorcerycraft.SorceryCraft;
import com.thebrokenrail.sorcerycraft.spell.SpellPlayerEntity; import com.thebrokenrail.sorcerycraft.spell.util.SpellPlayerEntity;
import com.thebrokenrail.sorcerycraft.spell.SpellTag; import com.thebrokenrail.sorcerycraft.spell.util.SpellHelper;
import io.netty.buffer.Unpooled; import io.netty.buffer.Unpooled;
import net.fabricmc.api.EnvType;
import net.fabricmc.api.Environment;
import net.fabricmc.fabric.api.network.PacketContext; import net.fabricmc.fabric.api.network.PacketContext;
import net.minecraft.nbt.CompoundTag; import net.minecraft.nbt.CompoundTag;
import net.minecraft.network.PacketByteBuf;
import net.minecraft.network.packet.s2c.play.CustomPayloadS2CPacket; import net.minecraft.network.packet.s2c.play.CustomPayloadS2CPacket;
import net.minecraft.server.network.ServerPlayerEntity; import net.minecraft.server.network.ServerPlayerEntity;
import net.minecraft.util.Identifier; import net.minecraft.util.Identifier;
import net.minecraft.util.PacketByteBuf;
public class UpdateKnownSpellsS2CPacket { public class UpdateKnownSpellsS2CPacket {
@Environment(EnvType.CLIENT)
public static void handle(PacketContext context, PacketByteBuf bytes) { public static void handle(PacketContext context, PacketByteBuf bytes) {
CompoundTag tag = bytes.readCompoundTag(); CompoundTag tag = bytes.readCompoundTag();
if (context.getPlayer() != null) { if (context.getPlayer() != null) {
SpellPlayerEntity spellPlayer = (SpellPlayerEntity) context.getPlayer(); SpellPlayerEntity spellPlayer = (SpellPlayerEntity) context.getPlayer();
spellPlayer.setSpells(SpellTag.getSpells(tag)); spellPlayer.setDiscoveredSpells(SpellHelper.getSpells(tag));
} }
} }

View File

@ -0,0 +1,54 @@
package com.thebrokenrail.sorcerycraft.spell;
import com.thebrokenrail.sorcerycraft.spell.api.Spell;
import net.minecraft.entity.Entity;
import net.minecraft.item.ItemStack;
import net.minecraft.item.Items;
import net.minecraft.state.property.Properties;
import net.minecraft.tag.BlockTags;
import net.minecraft.util.Identifier;
import net.minecraft.util.hit.BlockHitResult;
import net.minecraft.util.math.BlockPos;
import net.minecraft.world.World;
public class CoolingSpell extends Spell {
public CoolingSpell(Identifier id, int level) {
super(id, level);
}
@Override
public void execute(World world, Entity source, Entity attacker, Entity target) {
target.setFireTicks(0);
}
@Override
public void execute(World world, Entity source, Entity attacker, BlockHitResult hitResult) {
BlockPos blockPos = hitResult.getBlockPos();
if (world.getBlockState(blockPos).isIn(BlockTags.FIRE)) {
world.removeBlock(blockPos, false);
}
if (world.getBlockState(blockPos).contains(Properties.LIT) && world.getBlockState(blockPos).get(Properties.LIT)) {
world.setBlockState(blockPos, world.getBlockState(blockPos).with(Properties.LIT, false));
}
BlockPos sideBlockPos = blockPos.offset(hitResult.getSide());
if (world.getBlockState(sideBlockPos).isIn(BlockTags.FIRE)) {
world.removeBlock(sideBlockPos, false);
}
}
@Override
public int getXPCost() {
return 10;
}
@Override
public ItemStack getItemCost() {
return new ItemStack(Items.ICE);
}
@Override
public int getMaxLevel() {
return 1;
}
}

View File

@ -1,18 +1,21 @@
package com.thebrokenrail.sorcerycraft.spell; package com.thebrokenrail.sorcerycraft.spell;
import com.thebrokenrail.sorcerycraft.spell.api.Spell;
import net.minecraft.entity.Entity; import net.minecraft.entity.Entity;
import net.minecraft.entity.LivingEntity; import net.minecraft.entity.LivingEntity;
import net.minecraft.entity.effect.StatusEffects; import net.minecraft.entity.effect.StatusEffects;
import net.minecraft.item.ItemStack; import net.minecraft.item.ItemStack;
import net.minecraft.item.Items; import net.minecraft.item.Items;
import net.minecraft.util.Identifier;
import net.minecraft.world.World;
public class DamageSpell extends Spell { public class DamageSpell extends Spell {
public DamageSpell(String id, int level) { public DamageSpell(Identifier id, int level) {
super(id, level); super(id, level);
} }
@Override @Override
public void execute(Entity target, Entity source, Entity attacker) { public void execute(World world, Entity source, Entity attacker, Entity target) {
if (target instanceof LivingEntity) { if (target instanceof LivingEntity) {
StatusEffects.INSTANT_DAMAGE.applyInstantEffect(source, attacker, (LivingEntity) target, getLevel(), 1.0d); StatusEffects.INSTANT_DAMAGE.applyInstantEffect(source, attacker, (LivingEntity) target, getLevel(), 1.0d);
} }
@ -22,10 +25,10 @@ public class DamageSpell extends Spell {
public int getXPCost() { public int getXPCost() {
switch (getLevel()) { switch (getLevel()) {
case 0: { case 0: {
return 5; return 4;
} }
case 1: { case 1: {
return 10; return 8;
} }
} }
return -1; return -1;

View File

@ -1,17 +1,20 @@
package com.thebrokenrail.sorcerycraft.spell; package com.thebrokenrail.sorcerycraft.spell;
import com.thebrokenrail.sorcerycraft.spell.api.Spell;
import net.minecraft.entity.Entity; import net.minecraft.entity.Entity;
import net.minecraft.entity.LivingEntity; import net.minecraft.entity.LivingEntity;
import net.minecraft.item.ItemStack; import net.minecraft.item.ItemStack;
import net.minecraft.item.Items; import net.minecraft.item.Items;
import net.minecraft.util.Identifier;
import net.minecraft.world.World;
public class DissolveSpell extends Spell { public class DissolveSpell extends Spell {
public DissolveSpell(String id, int level) { public DissolveSpell(Identifier id, int level) {
super(id, level); super(id, level);
} }
@Override @Override
public void execute(Entity target, Entity source, Entity attacker) { public void execute(World world, Entity source, Entity attacker, Entity target) {
if (target instanceof LivingEntity) { if (target instanceof LivingEntity) {
((LivingEntity) target).clearStatusEffects(); ((LivingEntity) target).clearStatusEffects();
} }
@ -19,7 +22,7 @@ public class DissolveSpell extends Spell {
@Override @Override
public int getXPCost() { public int getXPCost() {
return 12; return 8;
} }
@Override @Override

View File

@ -1,17 +1,31 @@
package com.thebrokenrail.sorcerycraft.spell; package com.thebrokenrail.sorcerycraft.spell;
import com.thebrokenrail.sorcerycraft.spell.api.Spell;
import net.minecraft.entity.Entity; import net.minecraft.entity.Entity;
import net.minecraft.entity.LivingEntity;
import net.minecraft.item.AutomaticItemPlacementContext;
import net.minecraft.item.ItemStack; import net.minecraft.item.ItemStack;
import net.minecraft.item.Items; import net.minecraft.item.Items;
import net.minecraft.util.Identifier;
import net.minecraft.util.hit.BlockHitResult;
import net.minecraft.world.World;
public class FlameSpell extends Spell { public class FlameSpell extends Spell {
public FlameSpell(String id, int level) { public FlameSpell(Identifier id, int level) {
super(id, level); super(id, level);
} }
@Override @Override
public void execute(Entity target, Entity source, Entity attacker) { public void execute(World world, Entity source, Entity attacker, Entity target) {
target.setFireTicks(400 + (getLevel() * 200)); if (attacker instanceof LivingEntity) {
((LivingEntity) attacker).onAttacking(target);
}
target.setOnFireFor(8 + (getLevel() * 4));
}
@Override
public void execute(World world, Entity source, Entity attacker, BlockHitResult hitResult) {
Items.FLINT_AND_STEEL.useOnBlock(new AutomaticItemPlacementContext(world, hitResult.getBlockPos(), hitResult.getSide().getOpposite(), new ItemStack(Items.FLINT_AND_STEEL), hitResult.getSide()));
} }
@Override @Override
@ -21,7 +35,7 @@ public class FlameSpell extends Spell {
return 8; return 8;
} }
case 1: { case 1: {
return 16; return 12;
} }
} }
return -1; return -1;

View File

@ -1,18 +1,21 @@
package com.thebrokenrail.sorcerycraft.spell; package com.thebrokenrail.sorcerycraft.spell;
import com.thebrokenrail.sorcerycraft.spell.api.Spell;
import net.minecraft.entity.Entity; import net.minecraft.entity.Entity;
import net.minecraft.entity.LivingEntity; import net.minecraft.entity.LivingEntity;
import net.minecraft.entity.effect.StatusEffects; import net.minecraft.entity.effect.StatusEffects;
import net.minecraft.item.ItemStack; import net.minecraft.item.ItemStack;
import net.minecraft.item.Items; import net.minecraft.item.Items;
import net.minecraft.util.Identifier;
import net.minecraft.world.World;
public class HealSpell extends Spell { public class HealSpell extends Spell {
public HealSpell(String id, int level) { public HealSpell(Identifier id, int level) {
super(id, level); super(id, level);
} }
@Override @Override
public void execute(Entity target, Entity source, Entity attacker) { public void execute(World world, Entity source, Entity attacker, Entity target) {
if (target instanceof LivingEntity) { if (target instanceof LivingEntity) {
StatusEffects.INSTANT_HEALTH.applyInstantEffect(source, attacker, (LivingEntity) target, getLevel(), 1.0d); StatusEffects.INSTANT_HEALTH.applyInstantEffect(source, attacker, (LivingEntity) target, getLevel(), 1.0d);
} }
@ -22,10 +25,10 @@ public class HealSpell extends Spell {
public int getXPCost() { public int getXPCost() {
switch (getLevel()) { switch (getLevel()) {
case 0: { case 0: {
return 5; return 4;
} }
case 1: { case 1: {
return 10; return 8;
} }
} }
return -1; return -1;

View File

@ -0,0 +1,27 @@
package com.thebrokenrail.sorcerycraft.spell;
import com.thebrokenrail.sorcerycraft.spell.api.Spell;
import net.minecraft.item.ItemStack;
import net.minecraft.item.Items;
import net.minecraft.util.Identifier;
public class InwardSpell extends Spell {
public InwardSpell(Identifier id, int level) {
super(id, level);
}
@Override
public int getXPCost() {
return 16;
}
@Override
public ItemStack getItemCost() {
return new ItemStack(Items.ENDER_EYE);
}
@Override
public int getMaxLevel() {
return 1;
}
}

View File

@ -1,19 +1,22 @@
package com.thebrokenrail.sorcerycraft.spell; package com.thebrokenrail.sorcerycraft.spell;
import com.thebrokenrail.sorcerycraft.spell.api.Spell;
import net.minecraft.entity.Entity; import net.minecraft.entity.Entity;
import net.minecraft.entity.LivingEntity; import net.minecraft.entity.LivingEntity;
import net.minecraft.entity.effect.StatusEffectInstance; import net.minecraft.entity.effect.StatusEffectInstance;
import net.minecraft.entity.effect.StatusEffects; import net.minecraft.entity.effect.StatusEffects;
import net.minecraft.item.ItemStack; import net.minecraft.item.ItemStack;
import net.minecraft.item.Items; import net.minecraft.item.Items;
import net.minecraft.util.Identifier;
import net.minecraft.world.World;
public class LevitateSpell extends Spell { public class LevitateSpell extends Spell {
public LevitateSpell(String id, int level) { public LevitateSpell(Identifier id, int level) {
super(id, level); super(id, level);
} }
@Override @Override
public void execute(Entity target, Entity source, Entity attacker) { public void execute(World world, Entity source, Entity attacker, Entity target) {
if (target instanceof LivingEntity) { if (target instanceof LivingEntity) {
((LivingEntity) target).addStatusEffect(new StatusEffectInstance(StatusEffects.LEVITATION, 400 + (getLevel() * 160))); ((LivingEntity) target).addStatusEffect(new StatusEffectInstance(StatusEffects.LEVITATION, 400 + (getLevel() * 160)));
} }
@ -26,7 +29,7 @@ public class LevitateSpell extends Spell {
return 12; return 12;
} }
case 1: { case 1: {
return 21; return 24;
} }
} }
return -1; return -1;

View File

@ -0,0 +1,56 @@
package com.thebrokenrail.sorcerycraft.spell;
import com.thebrokenrail.sorcerycraft.spell.api.Spell;
import net.minecraft.entity.Entity;
import net.minecraft.entity.EntityType;
import net.minecraft.entity.LightningEntity;
import net.minecraft.item.ItemStack;
import net.minecraft.item.Items;
import net.minecraft.server.network.ServerPlayerEntity;
import net.minecraft.server.world.ServerWorld;
import net.minecraft.util.Identifier;
import net.minecraft.util.hit.BlockHitResult;
import net.minecraft.util.math.Vec3d;
import net.minecraft.world.World;
public class LightningSpell extends Spell {
public LightningSpell(Identifier id, int level) {
super(id, level);
}
@Override
public void execute(World world, Entity source, Entity attacker, Entity target) {
strike(world, target.getPos(), attacker);
}
@Override
public void execute(World world, Entity source, Entity attacker, BlockHitResult hitResult) {
strike(world, hitResult.getPos(), attacker);
}
private void strike(World world, Vec3d pos, Entity attacker) {
ServerWorld serverWorld = (ServerWorld) world;
LightningEntity lightningEntity = EntityType.LIGHTNING_BOLT.create(world);
assert lightningEntity != null;
lightningEntity.updatePosition(pos.x, pos.y, pos.z);
if (attacker instanceof ServerPlayerEntity) {
lightningEntity.setChanneler((ServerPlayerEntity) attacker);
}
serverWorld.spawnEntity(lightningEntity);
}
@Override
public int getXPCost() {
return 18;
}
@Override
public ItemStack getItemCost() {
return new ItemStack(Items.END_ROD);
}
@Override
public int getMaxLevel() {
return 1;
}
}

View File

@ -1,36 +0,0 @@
package com.thebrokenrail.sorcerycraft.spell;
import java.util.HashMap;
import java.util.Map;
import net.minecraft.item.ItemStack;
import net.minecraft.loot.condition.LootCondition;
import net.minecraft.loot.context.LootContext;
import net.minecraft.loot.function.ConditionalLootFunction;
import net.minecraft.loot.function.LootFunction;
public class RandomSpellLootTableFunction extends ConditionalLootFunction {
private RandomSpellLootTableFunction(LootCondition[] conditions) {
super(conditions);
}
public ItemStack process(ItemStack stack, LootContext context) {
Spell[] spells = SpellRegistry.getSpells();
int index = context.getRandom().nextInt(spells.length);
Map<String, Integer> spell = new HashMap<>();
spell.put(spells[index].getID(), spells[index].getLevel());
SpellTag.setSpells(stack, spell);
return stack;
}
public static class Builder extends ConditionalLootFunction.Builder<RandomSpellLootTableFunction.Builder> {
@Override
protected RandomSpellLootTableFunction.Builder getThisBuilder() {
return this;
}
public LootFunction build() {
return new RandomSpellLootTableFunction(getConditions());
}
}
}

View File

@ -1,30 +0,0 @@
package com.thebrokenrail.sorcerycraft.spell;
import net.minecraft.entity.Entity;
import net.minecraft.item.ItemStack;
public abstract class Spell {
private final String id;
private final int level;
public Spell(String id, int level) {
this.id = id;
this.level = level;
}
public String getID() {
return id;
}
public int getLevel() {
return level;
}
public abstract void execute(Entity target, Entity source, Entity attacker);
public abstract int getXPCost();
public abstract ItemStack getItemCost();
public abstract int getMaxLevel();
}

View File

@ -1,9 +0,0 @@
package com.thebrokenrail.sorcerycraft.spell;
import java.util.Map;
public interface SpellPlayerEntity {
void setSpells(Map<String, Integer> spells);
Map<String, Integer> getSpells();
}

View File

@ -1,61 +0,0 @@
package com.thebrokenrail.sorcerycraft.spell;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class SpellRegistry {
private static final Map<String, Class<?>> spells = new HashMap<>();
public static Spell getSpell(String id, int level) {
if (!spells.containsKey(id)) {
return null;
}
try {
return (Spell) spells.get(id).getConstructor(String.class, int.class).newInstance(id, level);
} catch (NoSuchMethodException | InstantiationException | IllegalAccessException | InvocationTargetException e) {
e.printStackTrace();
return null;
}
}
public static int getMaxLevel(String id) {
Spell tempSpell = getSpell(id, 0);
if (tempSpell == null) {
return -1;
}
return tempSpell.getMaxLevel();
}
public static Spell[] getSpells() {
List<Spell> out = new ArrayList<>();
for (Map.Entry<String, Class<?>> entry : spells.entrySet()) {
int maxLevel = getMaxLevel(entry.getKey());
if (maxLevel == -1) {
continue;
}
for (int i = 0; i < maxLevel; i++) {
Spell spell = getSpell(entry.getKey(), i);
if (spell != null) {
out.add(spell);
}
}
}
return out.toArray(new Spell[0]);
}
private static void registerSpell(String id, Class<?> spell) {
spells.put(id, spell);
}
public static void init() {
SpellRegistry.registerSpell("heal_spell", HealSpell.class);
SpellRegistry.registerSpell("damage_spell", DamageSpell.class);
SpellRegistry.registerSpell("dissolve_spell", DissolveSpell.class);
SpellRegistry.registerSpell("steadfast_spell", SteadfastSpell.class);
SpellRegistry.registerSpell("flame_spell", FlameSpell.class);
SpellRegistry.registerSpell("levitate_spell", LevitateSpell.class);
}
}

View File

@ -1,85 +0,0 @@
package com.thebrokenrail.sorcerycraft.spell;
import com.thebrokenrail.sorcerycraft.SorceryCraft;
import net.minecraft.item.ItemStack;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.nbt.ListTag;
import net.minecraft.nbt.Tag;
import net.minecraft.text.Text;
import net.minecraft.text.TranslatableText;
import net.minecraft.util.Formatting;
import java.util.HashMap;
import java.util.Map;
public class SpellTag {
public static final String SPELL_TAG = "Spells";
public static void setSpells(ItemStack itemStack, Map<String, Integer> map) {
CompoundTag tag;
if (itemStack.hasTag()) {
tag = itemStack.getTag();
} else {
tag = new CompoundTag();
tag.put(SPELL_TAG, new ListTag());
}
assert tag != null;
tag.put(SPELL_TAG, createSpellsTag(map));
itemStack.setTag(tag);
}
public static ListTag createSpellsTag(Map<String, Integer> map) {
ListTag spells = new ListTag();
for (Map.Entry<String, Integer> entry : map.entrySet()) {
CompoundTag spell = new CompoundTag();
spell.putString("id", entry.getKey());
spell.putInt("level", entry.getValue());
spells.add(spell);
}
return spells;
}
public static Map<String, Integer> getSpells(ItemStack itemStack) {
return getSpells(itemStack.getTag());
}
public static Map<String, Integer> getSpells(CompoundTag tag) {
if (tag == null) {
tag = new CompoundTag();
tag.put(SPELL_TAG, new ListTag());
}
Tag spellsTag = tag.get(SPELL_TAG);
ListTag spells;
if (spellsTag instanceof ListTag) {
spells = (ListTag) spellsTag;
} else {
spells = new ListTag();
}
Map<String, Integer> map = new HashMap<>();
for (int i = 0; i < spells.size(); i++) {
CompoundTag spell = spells.getCompound(i);
String id = spell.getString("id");
int level = spell.getInt("level");
if (map.get(id) == null || map.get(id) < level) {
map.put(id, level);
}
}
return map;
}
public static Text getTranslatedSpell(String id, int level) {
Text text = new TranslatableText("spell." + SorceryCraft.NAMESPACE + '.' + id);
text.formatted(Formatting.GRAY);
if (level != 0 || SpellRegistry.getMaxLevel(id) != 1) {
text.append(" ").append(new TranslatableText("enchantment.level." + (level + 1)));
}
return text;
}
}

View File

@ -1,18 +1,15 @@
package com.thebrokenrail.sorcerycraft.spell; package com.thebrokenrail.sorcerycraft.spell;
import net.minecraft.entity.Entity; import com.thebrokenrail.sorcerycraft.spell.api.Spell;
import net.minecraft.item.ItemStack; import net.minecraft.item.ItemStack;
import net.minecraft.item.Items; import net.minecraft.item.Items;
import net.minecraft.util.Identifier;
public class SteadfastSpell extends Spell { public class SteadfastSpell extends Spell {
public SteadfastSpell(String id, int level) { public SteadfastSpell(Identifier id, int level) {
super(id, level); super(id, level);
} }
@Override
public void execute(Entity target, Entity source, Entity attacker) {
}
@Override @Override
public int getXPCost() { public int getXPCost() {
return 18; return 18;

View File

@ -0,0 +1,87 @@
package com.thebrokenrail.sorcerycraft.spell;
import com.thebrokenrail.sorcerycraft.spell.api.Spell;
import net.minecraft.entity.Entity;
import net.minecraft.entity.LivingEntity;
import net.minecraft.item.ItemStack;
import net.minecraft.item.Items;
import net.minecraft.sound.SoundCategory;
import net.minecraft.sound.SoundEvents;
import net.minecraft.util.Identifier;
import net.minecraft.util.math.MathHelper;
import net.minecraft.world.World;
public class TeleportSpell extends Spell {
public TeleportSpell(Identifier id, int level) {
super(id, level);
}
private int getMaxTeleport(World world) {
return world.getRegistryKey() != World.OVERWORLD ? 128 : 256;
}
@Override
public void execute(World world, Entity source, Entity attacker, Entity target) {
int range = 16 + (8 * getLevel());
if (target instanceof LivingEntity) {
LivingEntity user = (LivingEntity) target;
if (!world.isClient()) {
double d = user.getX();
double e = user.getY();
double f = user.getZ();
for (int i = 0; i < range; ++i) {
double x = user.getX() + (user.getRandom().nextDouble() - 0.5D) * range;
double y = MathHelper.clamp(user.getY() + (double) (user.getRandom().nextInt(range) - (range / 2)), 0d, getMaxTeleport(world) - 1);
double z = user.getZ() + (user.getRandom().nextDouble() - 0.5D) * range;
if (user.hasVehicle()) {
user.stopRiding();
}
if (user.teleport(x, y, z, true)) {
world.playSound(null, d, e, f, SoundEvents.ITEM_CHORUS_FRUIT_TELEPORT, SoundCategory.PLAYERS, 1.0F, 1.0F);
user.playSound(SoundEvents.ITEM_CHORUS_FRUIT_TELEPORT, 1.0F, 1.0F);
break;
}
}
}
}
}
@Override
public int getXPCost() {
switch (getLevel()) {
case 0: {
return 12;
}
case 1: {
return 18;
}
case 2: {
return 24;
}
}
return -1;
}
@Override
public ItemStack getItemCost() {
switch (getLevel()) {
case 0: {
return new ItemStack(Items.ENDER_PEARL);
}
case 1: {
return new ItemStack(Items.CHORUS_FRUIT);
}
case 2: {
return new ItemStack(Items.CHORUS_FLOWER);
}
}
return ItemStack.EMPTY;
}
@Override
public int getMaxLevel() {
return 3;
}
}

View File

@ -0,0 +1,101 @@
package com.thebrokenrail.sorcerycraft.spell.api;
import com.thebrokenrail.sorcerycraft.spell.api.registry.SpellRegistry;
import net.minecraft.entity.Entity;
import net.minecraft.item.ItemStack;
import net.minecraft.text.MutableText;
import net.minecraft.text.TranslatableText;
import net.minecraft.util.Identifier;
import net.minecraft.util.hit.BlockHitResult;
import net.minecraft.world.World;
/**
* Spell Implementation Base Class
*/
public abstract class Spell {
private final Identifier id;
private final int level;
public Spell(Identifier id, int level) {
this.id = id;
this.level = level;
}
/**
* Get the ID of this spell
* @return The Spell ID
*/
public Identifier getID() {
return id;
}
/**
* Get the level of this spell
* @return The Spell's Level
*/
public int getLevel() {
return level;
}
/**
* Execute this spell on an Entity
* @param world World
* @param source A SpellEntity
* @param attacker The Entity that cast this spell
* @param target The Target
*/
public void execute(World world, Entity source, Entity attacker, Entity target) {
// NOOP
}
/**
* Execute this spell on a block
* @param world World
* @param source A SpellEntity
* @param attacker The Entity that cast this spell
* @param hitResult The block's HitResult
*/
public void execute(World world, Entity source, Entity attacker, BlockHitResult hitResult) {
// NOOP
}
/**
* Get the amount of levels required to make this spell in a Casting Table
* @return The XP cost
*/
public abstract int getXPCost();
/**
* Get the item(s) required to make this spell in a Casting Table, or ItemStack.EMPTY if an item is not required
* @return The item cost
*/
public abstract ItemStack getItemCost();
/**
* Get the maximum level of this spell
* @return The Spell's Max Level
*/
public abstract int getMaxLevel();
/**
* Default Implementation for Spell.getTranslation()
* @param id Spell ID
* @param level Spell Level
* @return Translated Display Name
*/
public static MutableText getDefaultTranslation(Identifier id, int level) {
MutableText text = new TranslatableText("spell." + id.getNamespace() + '.' + id.getPath());
if (level != 0 || SpellRegistry.getMaxLevel(id) != 1) {
text.append(" ").append(new TranslatableText("enchantment.level." + (level + 1)));
}
return text;
}
/**
* Get Translated Display Name
* @return Translated Display Name
*/
public MutableText getTranslation() {
return getDefaultTranslation(getID(), getLevel());
}
}

View File

@ -0,0 +1,114 @@
package com.thebrokenrail.sorcerycraft.spell.api.registry;
import com.thebrokenrail.sorcerycraft.spell.api.Spell;
import net.minecraft.util.Identifier;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* Spell Implementation Registry
*/
public class SpellRegistry {
private static final Map<Identifier, Class<?>> spells = new HashMap<>();
/**
* Get Spell Implementation
* @param entry Map Entry
* @return Spell Implementation
*/
public static Spell getSpell(Map.Entry<Identifier, Integer> entry) {
return getSpell(entry.getKey(), entry.getValue());
}
/**
* Get Spell Implementation
* @param id Spell ID
* @param level Spell Level
* @return Spell Implementation
*/
public static Spell getSpell(Identifier id, int level) {
if (!spells.containsKey(id)) {
return null;
}
try {
return (Spell) spells.get(id).getConstructor(Identifier.class, int.class).newInstance(id, level);
} catch (NoSuchMethodException | InstantiationException | IllegalAccessException | InvocationTargetException e) {
e.printStackTrace();
return null;
}
}
/**
* Get Max Level of a Spell
* @param id Spell ID
* @return Max Level
*/
public static int getMaxLevel(Identifier id) {
Spell tempSpell = getSpell(id, 0);
if (tempSpell == null) {
return -1;
}
return tempSpell.getMaxLevel();
}
/**
* Get All Spell Implementations
* @return List of Spell Implementations
*/
public static Spell[] getSpells() {
List<Spell> out = new ArrayList<>();
for (Map.Entry<Identifier, Class<?>> entry : spells.entrySet()) {
int maxLevel = getMaxLevel(entry.getKey());
if (maxLevel == -1) {
continue;
}
for (int i = 0; i < maxLevel; i++) {
Spell spell = getSpell(entry.getKey(), i);
if (spell != null) {
out.add(spell);
}
}
}
return out.toArray(new Spell[0]);
}
/**
* Get All Max Level Spell Implementations
* @return List of Spell Implementations
*/
public static Spell[] getMaxSpells() {
List<Spell> out = new ArrayList<>();
for (Map.Entry<Identifier, Class<?>> entry : spells.entrySet()) {
int maxLevel = getMaxLevel(entry.getKey());
if (maxLevel == -1) {
continue;
}
out.add(getSpell(entry.getKey(), maxLevel - 1));
}
return out.toArray(new Spell[0]);
}
/**
* Register a Spell
* @param id The Spell ID
* @param spell The Spell Class
* @return The Spell ID
*/
public static Identifier register(Identifier id, Class<?> spell) {
spells.put(id, spell);
return id;
}
/**
* Gat All Spell IDs
* @return List of Spell IDs
*/
public static Identifier[] getSpellsID() {
return spells.keySet().toArray(new Identifier[0]);
}
}

View File

@ -0,0 +1,44 @@
package com.thebrokenrail.sorcerycraft.spell.api.registry;
import com.thebrokenrail.sorcerycraft.SorceryCraft;
import com.thebrokenrail.sorcerycraft.spell.CoolingSpell;
import com.thebrokenrail.sorcerycraft.spell.DamageSpell;
import com.thebrokenrail.sorcerycraft.spell.DissolveSpell;
import com.thebrokenrail.sorcerycraft.spell.FlameSpell;
import com.thebrokenrail.sorcerycraft.spell.HealSpell;
import com.thebrokenrail.sorcerycraft.spell.InwardSpell;
import com.thebrokenrail.sorcerycraft.spell.LevitateSpell;
import com.thebrokenrail.sorcerycraft.spell.LightningSpell;
import com.thebrokenrail.sorcerycraft.spell.SteadfastSpell;
import com.thebrokenrail.sorcerycraft.spell.TeleportSpell;
import net.minecraft.util.Identifier;
/**
* All Builtin Spells
*/
@SuppressWarnings("unused")
public class Spells {
public static final Identifier HEAL_SPELL;
public static final Identifier DAMAGE_SPELL;
public static final Identifier DISSOLVE_SPELL;
public static final Identifier STEADFAST_SPELL;
public static final Identifier FLAME_SPELL;
public static final Identifier LEVITATE_SPELL;
public static final Identifier TELEPORT_SPELL;
public static final Identifier INWARD_SPELL;
public static final Identifier COOLING_SPELL;
public static final Identifier LIGHTNING_SPELL;
static {
HEAL_SPELL = SpellRegistry.register(new Identifier(SorceryCraft.NAMESPACE, "heal_spell"), HealSpell.class);
DAMAGE_SPELL = SpellRegistry.register(new Identifier(SorceryCraft.NAMESPACE, "damage_spell"), DamageSpell.class);
DISSOLVE_SPELL = SpellRegistry.register(new Identifier(SorceryCraft.NAMESPACE, "dissolve_spell"), DissolveSpell.class);
STEADFAST_SPELL = SpellRegistry.register(new Identifier(SorceryCraft.NAMESPACE, "steadfast_spell"), SteadfastSpell.class);
FLAME_SPELL = SpellRegistry.register(new Identifier(SorceryCraft.NAMESPACE, "flame_spell"), FlameSpell.class);
LEVITATE_SPELL = SpellRegistry.register(new Identifier(SorceryCraft.NAMESPACE, "levitate_spell"), LevitateSpell.class);
TELEPORT_SPELL = SpellRegistry.register(new Identifier(SorceryCraft.NAMESPACE, "teleport_spell"), TeleportSpell.class);
INWARD_SPELL = SpellRegistry.register(new Identifier(SorceryCraft.NAMESPACE, "inward_spell"), InwardSpell.class);
COOLING_SPELL = SpellRegistry.register(new Identifier(SorceryCraft.NAMESPACE, "cooling_spell"), CoolingSpell.class);
LIGHTNING_SPELL = SpellRegistry.register(new Identifier(SorceryCraft.NAMESPACE, "lightning_spell"), LightningSpell.class);
}
}

View File

@ -0,0 +1,63 @@
package com.thebrokenrail.sorcerycraft.spell.util;
import com.google.gson.JsonDeserializationContext;
import com.google.gson.JsonObject;
import com.thebrokenrail.sorcerycraft.SorceryCraft;
import com.thebrokenrail.sorcerycraft.spell.api.Spell;
import com.thebrokenrail.sorcerycraft.spell.api.registry.SpellRegistry;
import net.minecraft.item.ItemStack;
import net.minecraft.loot.condition.LootCondition;
import net.minecraft.loot.context.LootContext;
import net.minecraft.loot.function.ConditionalLootFunction;
import net.minecraft.loot.function.LootFunction;
import net.minecraft.loot.function.LootFunctionType;
import net.minecraft.util.Identifier;
import net.minecraft.util.registry.Registry;
import java.util.Map;
public class RandomSpellLootTableFunction extends ConditionalLootFunction {
private RandomSpellLootTableFunction(LootCondition[] conditions) {
super(conditions);
}
public ItemStack process(ItemStack stack, LootContext context) {
double chance = 1.0d;
while (!(context.getRandom().nextDouble() > chance)) {
Spell[] spells = SpellRegistry.getSpells();
int index = context.getRandom().nextInt(spells.length);
Map<Identifier, Integer> spell = SpellHelper.getSpells(stack);
spell.put(spells[index].getID(), spells[index].getLevel());
SpellHelper.setSpells(stack, spell);
chance = chance * 0.25d;
}
return stack;
}
@Override
public LootFunctionType getType() {
return Registry.LOOT_FUNCTION_TYPE.get(new Identifier(SorceryCraft.NAMESPACE, "random_spell"));
}
public static class Factory extends ConditionalLootFunction.Serializer<RandomSpellLootTableFunction> {
public Factory() {
super();
}
@Override
public RandomSpellLootTableFunction fromJson(JsonObject json, JsonDeserializationContext context, LootCondition[] conditions) {
return (RandomSpellLootTableFunction) new com.thebrokenrail.sorcerycraft.spell.util.RandomSpellLootTableFunction.Builder().build();
}
}
public static class Builder extends ConditionalLootFunction.Builder<RandomSpellLootTableFunction.Builder> {
@Override
protected RandomSpellLootTableFunction.Builder getThisBuilder() {
return this;
}
public LootFunction build() {
return new RandomSpellLootTableFunction(getConditions());
}
}
}

View File

@ -0,0 +1,138 @@
package com.thebrokenrail.sorcerycraft.spell.util;
import com.thebrokenrail.sorcerycraft.SorceryCraft;
import com.thebrokenrail.sorcerycraft.spell.api.Spell;
import com.thebrokenrail.sorcerycraft.spell.api.registry.SpellRegistry;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.item.ItemStack;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.nbt.ListTag;
import net.minecraft.nbt.Tag;
import net.minecraft.network.MessageType;
import net.minecraft.network.packet.s2c.play.GameMessageS2CPacket;
import net.minecraft.server.network.ServerPlayerEntity;
import net.minecraft.text.LiteralText;
import net.minecraft.text.MutableText;
import net.minecraft.text.Text;
import net.minecraft.text.TranslatableText;
import net.minecraft.util.Formatting;
import net.minecraft.util.Identifier;
import net.minecraft.util.Util;
import net.minecraft.world.World;
import java.util.HashMap;
import java.util.Map;
public class SpellHelper {
public static final String SPELL_TAG = "Spells";
public static void setSpells(ItemStack itemStack, Map<Identifier, Integer> map) {
CompoundTag tag;
if (itemStack.hasTag()) {
tag = itemStack.getTag();
} else {
tag = new CompoundTag();
tag.put(SPELL_TAG, new ListTag());
}
assert tag != null;
tag.put(SPELL_TAG, createSpellsTag(map));
itemStack.setTag(tag);
}
public static ListTag createSpellsTag(Map<Identifier, Integer> map) {
ListTag spells = new ListTag();
for (Map.Entry<Identifier, Integer> entry : map.entrySet()) {
CompoundTag spell = new CompoundTag();
spell.putString("id", entry.getKey().toString());
spell.putInt("level", entry.getValue());
spells.add(spell);
}
return spells;
}
public static Map<Identifier, Integer> getSpells(ItemStack itemStack) {
return getSpells(itemStack.getTag());
}
public static Map<Identifier, Integer> getSpells(CompoundTag tag) {
if (tag == null) {
tag = new CompoundTag();
tag.put(SPELL_TAG, new ListTag());
}
Tag spellsTag = tag.get(SPELL_TAG);
ListTag spells;
if (spellsTag instanceof ListTag) {
spells = (ListTag) spellsTag;
} else {
spells = new ListTag();
}
Map<Identifier, Integer> map = new HashMap<>();
for (int i = 0; i < spells.size(); i++) {
CompoundTag spell = spells.getCompound(i);
Identifier id = new Identifier(spell.getString("id"));
int level = spell.getInt("level");
if (map.get(id) == null || map.get(id) < level) {
map.put(id, level);
}
}
return map;
}
public static Text getTranslatedSpell(Identifier id, int level) {
Spell spell = SpellRegistry.getSpell(id, level);
MutableText text;
if (spell != null) {
text = spell.getTranslation();
} else {
text = Spell.getDefaultTranslation(id, level);
}
text.formatted(Formatting.GRAY);
return text;
}
public static Text getTranslatedSpellChat(Identifier id, int level) {
return new LiteralText("[").append(SpellHelper.getTranslatedSpell(id, level).getString()).append("]").formatted(Formatting.GREEN);
}
public static void learnSpells(PlayerEntity player, Map<Identifier, Integer> itemSpells) {
SpellPlayerEntity spellPlayer = (SpellPlayerEntity) player;
World world = player.getEntityWorld();
Map<Identifier, Integer> playerSpells = spellPlayer.getDiscoveredSpells();
boolean changed = false;
for (Map.Entry<Identifier, Integer> entry : itemSpells.entrySet()) {
Spell spell = SpellRegistry.getSpell(entry);
if (spell != null) {
if (spell.getLevel() >= spell.getMaxLevel()) {
spell = SpellRegistry.getSpell(entry.getKey(), spell.getMaxLevel() - 1);
}
assert spell != null;
if (!playerSpells.containsKey(spell.getID()) || playerSpells.get(spell.getID()) < spell.getLevel()) {
changed = true;
playerSpells.put(spell.getID(), spell.getLevel());
assert world.getServer() != null;
Text text = getTranslatedSpellChat(spell.getID(), spell.getLevel());
world.getServer().getPlayerManager().sendToAll(new GameMessageS2CPacket(new TranslatableText("chat." + SorceryCraft.NAMESPACE + ".discovered_spell", player.getDisplayName(), text), MessageType.CHAT, Util.NIL_UUID));
}
}
}
if (changed) {
SorceryCraft.playSpellSound(player);
spellPlayer.setDiscoveredSpells(playerSpells);
SorceryCraft.DISCOVER_ALL_SPELLS_CRITERION.trigger((ServerPlayerEntity) player);
}
}
}

View File

@ -0,0 +1,11 @@
package com.thebrokenrail.sorcerycraft.spell.util;
import net.minecraft.util.Identifier;
import java.util.Map;
public interface SpellPlayerEntity {
void setDiscoveredSpells(Map<Identifier, Integer> spells);
Map<Identifier, Integer> getDiscoveredSpells();
}

View File

@ -0,0 +1,5 @@
package com.thebrokenrail.sorcerycraft.spell.util;
public interface SpellServerPlayerEntity extends SpellPlayerEntity {
void sync();
}

View File

@ -1,18 +1,38 @@
{ {
"item.sorcerycraft.spell": "Spell", "item.sorcerycraft.spell": "Spell",
"block.sorcerycraft.casting_table": "Casting Table", "block.sorcerycraft.casting_table": "Casting Table",
"itemGroup.sorcerycraft.spells": "SorceryCraft", "itemGroup.sorcerycraft.spells": "SorceryCraft",
"container.sorcerycraft.casting_table": "Casting Table", "container.sorcerycraft.casting_table": "Casting Table",
"container.sorcerycraft.spells": "Spells", "container.sorcerycraft.spells": "Spells",
"chat.sorcerycraft.new_spell": "%s has learned the spell %s", "chat.sorcerycraft.discovered_spell": "%s has discovered the spell %s",
"entity.sorcerycraft.spell": "Spell", "entity.sorcerycraft.spell": "Spell",
"command.sorcerycraft.spell.invalid_action": "Invalid sub-command \"%s\"", "command.sorcerycraft.spell.forgotten_spell": "%s has forgotten the spell %s",
"command.sorcerycraft.spell.cleared_spells": "All spells cleared from %s", "command.sorcerycraft.spell.listing_spells": "%s has discovered the following spells: %s",
"command.sorcerycraft.spell.listing_spells": "%s knows the following spells:", "command.sorcerycraft.spell.unknown_spell": "Unknown spell: %s",
"spell.sorcerycraft.damage_spell": "Damage", "command.sorcerycraft.spell.not_holding_spell": "%s is not holding a spell",
"spell.sorcerycraft.heal_spell": "Heal", "command.sorcerycraft.spell.applied_spell": "Applied Spell %s",
"spell.sorcerycraft.dissolve_spell": "Dissolve", "command.sorcerycraft.spell.removed_spell": "Removed Spell %s",
"spell.sorcerycraft.steadfast_spell": "Steadfast", "stat.sorcerycraft.interact_with_casting_table": "Interactions with Casting Table",
"spell.sorcerycraft.flame_spell": "Flame", "stat.sorcerycraft.cast_spell": "Spells Cast",
"spell.sorcerycraft.levitate_spell": "Levitate" "text.autoconfig.sorcerycraft.title": "SorceryCraft Config",
"text.autoconfig.sorcerycraft.option.failureChance": "Spell Failure Chance",
"text.autoconfig.sorcerycraft.option.limitCastingTable": "Limit Casting Table To Discovered Spells",
"text.autoconfig.sorcerycraft.option.limitCastingTable.creative": "Creative Mode",
"text.autoconfig.sorcerycraft.option.limitCastingTable.survival": "Survival Mode",
"advancements.sorcerycraft.adventure.discover_spell.title": "Witchcraft!",
"advancements.sorcerycraft.adventure.discover_spell.description": "Discover a spell",
"advancements.sorcerycraft.adventure.create_spell.title": "Spellbinding!",
"advancements.sorcerycraft.adventure.create_spell.description": "Cast a spell using a Casting Table",
"advancements.sorcerycraft.adventure.discover_all_spells.title": "Master of Magic!",
"advancements.sorcerycraft.adventure.discover_all_spells.description": "Discover all spells",
"spell.sorcerycraft.damage_spell": "Damage",
"spell.sorcerycraft.heal_spell": "Heal",
"spell.sorcerycraft.dissolve_spell": "Dissolve",
"spell.sorcerycraft.steadfast_spell": "Steadfast",
"spell.sorcerycraft.flame_spell": "Flame",
"spell.sorcerycraft.levitate_spell": "Levitate",
"spell.sorcerycraft.teleport_spell": "Teleport",
"spell.sorcerycraft.inward_spell": "Inward",
"spell.sorcerycraft.cooling_spell": "Cooling",
"spell.sorcerycraft.lightning_spell": "Lightning"
} }

View File

@ -1,6 +1,6 @@
{ {
"parent": "item/generated", "parent": "item/generated",
"textures": { "textures": {
"layer0": "minecraft:item/paper" "layer0": "sorcerycraft:item/spell"
} }
} }

Binary file not shown.

Before

Width:  |  Height:  |  Size: 528 B

After

Width:  |  Height:  |  Size: 362 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

View File

@ -0,0 +1,23 @@
{
"parent": "sorcerycraft:adventure/discover_spell",
"display": {
"icon": {
"item": "sorcerycraft:casting_table"
},
"title": {
"translate": "advancements.sorcerycraft.adventure.create_spell.title"
},
"description": {
"translate": "advancements.sorcerycraft.adventure.create_spell.description"
},
"frame": "task",
"show_toast": true,
"announce_to_chat": true,
"hidden": false
},
"criteria": {
"sorcerycraft:casting_table": {
"trigger": "sorcerycraft:create_spell"
}
}
}

View File

@ -0,0 +1,27 @@
{
"parent": "sorcerycraft:adventure/create_spell",
"display": {
"icon": {
"item": "sorcerycraft:spell",
"nbt": "{Spells: [{id: \"sorcerycraft:damage_spell\", level: 0}]}"
},
"title": {
"translate": "advancements.sorcerycraft.adventure.discover_all_spells.title"
},
"description": {
"translate": "advancements.sorcerycraft.adventure.discover_all_spells.description"
},
"frame": "challenge",
"show_toast": true,
"announce_to_chat": true,
"hidden": false
},
"criteria": {
"sorcerycraft:spell": {
"trigger": "sorcerycraft:discover_all_spells"
}
},
"rewards": {
"experience": 100
}
}

View File

@ -0,0 +1,36 @@
{
"parent": "minecraft:adventure/root",
"display": {
"icon": {
"item": "sorcerycraft:spell"
},
"title": {
"translate": "advancements.sorcerycraft.adventure.discover_spell.title"
},
"description": {
"translate": "advancements.sorcerycraft.adventure.discover_spell.description"
},
"frame": "task",
"show_toast": true,
"announce_to_chat": true,
"hidden": false
},
"criteria": {
"sorcerycraft:spell": {
"trigger": "minecraft:inventory_changed",
"conditions": {
"items": [
{
"item": "sorcerycraft:spell"
}
]
}
}
},
"rewards": {
"recipes": [
"sorcerycraft:spell",
"sorcerycraft:casting_table"
]
}
}

View File

@ -1,19 +1,19 @@
{ {
"type": "minecraft:block", "type": "minecraft:block",
"pools": [ "pools": [
{
"rolls": 1,
"entries": [
{ {
"type": "minecraft:item", "rolls": 1,
"name": "sorcerycraft:casting_table" "entries": [
{
"type": "minecraft:item",
"name": "sorcerycraft:casting_table"
}
],
"conditions": [
{
"condition": "minecraft:survives_explosion"
}
]
} }
], ]
"conditions": [
{
"condition": "minecraft:survives_explosion"
}
]
}
]
} }

View File

@ -1,24 +1,23 @@
{ {
"type": "minecraft:crafting_shaped", "type": "minecraft:crafting_shaped",
"pattern": [ "pattern": [
"LCL", "LSL",
"CDC", "SDS",
"LCL" "LSL"
], ],
"key": { "key": {
"L": { "L": {
"item": "minecraft:lapis_lazuli" "item": "minecraft:lapis_lazuli"
},
"S": {
"item": "minecraft:smooth_stone"
},
"D": {
"item": "minecraft:diamond"
}
}, },
"C": { "result": {
"item": "minecraft:cobblestone" "item": "sorcerycraft:casting_table"
},
"D": {
"item": "minecraft:diamond"
} }
},
"result": {
"item": "sorcerycraft:casting_table",
"count": 1
}
} }

View File

@ -1,21 +1,21 @@
{ {
"type": "minecraft:crafting_shaped", "type": "minecraft:crafting_shaped",
"pattern": [ "pattern": [
" L ", " L ",
"LPL", "LPL",
" L " " L "
], ],
"key": { "key": {
"L": { "L": {
"item": "minecraft:lapis_lazuli" "item": "minecraft:lapis_lazuli"
},
"P": {
"item": "minecraft:paper"
}
}, },
"P": { "result": {
"item": "minecraft:paper" "item": "sorcerycraft:spell",
"count": 1
} }
},
"result": {
"item": "sorcerycraft:spell",
"count": 1
}
} }

View File

@ -1,33 +1,43 @@
{ {
"schemaVersion": 1, "schemaVersion": 1,
"id": "sorcerycraft", "id": "sorcerycraft",
"version": "${version}", "version": "${version}",
"name": "SorceryCraft", "name": "${name}",
"description": "Cast Spells in Minecraft!", "description": "Cast Spells in Minecraft!",
"authors": [ "authors": [
"TheBrokenRail" "TheBrokenRail"
],
"contact": {
"homepage": "https://thebrokenrail.com/",
"sources": "https://gitea.thebrokenrail.com/TheBrokenRail/SorceryCraft.git"
},
"license": "MIT",
"icon": "assets/sorcerycraft/textures/block/casting_table.png",
"environment": "*",
"entrypoints": {
"main": [
"com.thebrokenrail.sorcerycraft.SorceryCraft"
], ],
"client": [ "contact": {
"com.thebrokenrail.sorcerycraft.SorceryCraft" "homepage": "https://thebrokenrail.com/",
] "sources": "https://gitea.thebrokenrail.com/TheBrokenRail/SorceryCraft.git",
}, "issues": "https://gitea.thebrokenrail.com/TheBrokenRail/SorceryCraft/issues"
"mixins": [ },
"sorcerycraft.mixins.json" "license": "MIT",
], "icon": "assets/sorcerycraft/textures/item/spell.png",
"depends": { "environment": "*",
"fabricloader": ">=0.7.4", "entrypoints": {
"fabric": "*", "main": [
"minecraft": "1.16.x" "com.thebrokenrail.sorcerycraft.SorceryCraft"
} ],
"client": [
"com.thebrokenrail.sorcerycraft.client.SorceryCraftClient"
],
"modmenu": [
"com.thebrokenrail.sorcerycraft.client.ModMenu"
]
},
"mixins": [
"sorcerycraft.mixins.json"
],
"depends": {
"fabricloader": ">=0.7.4",
"fabric": "*",
"minecraft": "1.16.x"
},
"custom": {
"modupdater": {
"strategy": "curseforge",
"projectID": 365308
}
}
} }

View File

@ -1,15 +1,18 @@
{ {
"required": true, "required": true,
"package": "com.thebrokenrail.sorcerycraft.mixin", "package": "com.thebrokenrail.sorcerycraft.mixin",
"compatibilityLevel": "JAVA_8", "compatibilityLevel": "JAVA_8",
"client": [ "client": [
"MixinClientPlayNetworkHandler" "MixinClientPlayerEntity",
], "MixinClientPlayNetworkHandler",
"mixins": [ "MixinAbstractButtonWidget"
"MixinPlayerEntity", ],
"MixinServerPlayerEntity" "mixins": [
], "CriteriaRegistryHook",
"injectors": { "MixinPlayerEntity",
"defaultRequire": 1 "MixinServerPlayerEntity"
} ],
"injectors": {
"defaultRequire": 1
}
} }