Merge branch 'main' into 1.0
# Conflicts: # Common/src/generated/resources/.cache/cache # Common/src/generated/resources/assets/hexcasting/models/block/conjured.json # Common/src/main/java/at/petrak/hexcasting/api/PatternRegistry.kt # Common/src/main/java/at/petrak/hexcasting/api/addldata/ManaHolder.java # Common/src/main/java/at/petrak/hexcasting/api/block/circle/BlockEntityAbstractImpetus.java # Common/src/main/java/at/petrak/hexcasting/api/item/ColorizerItem.java # Common/src/main/java/at/petrak/hexcasting/api/item/DataHolderItem.java # Common/src/main/java/at/petrak/hexcasting/api/item/HexHolderItem.java # Common/src/main/java/at/petrak/hexcasting/api/item/ManaHolderItem.java # Common/src/main/java/at/petrak/hexcasting/api/spell/Action.kt # Common/src/main/java/at/petrak/hexcasting/api/spell/SpellDatum.kt # Common/src/main/java/at/petrak/hexcasting/api/spell/Widget.kt # Common/src/main/java/at/petrak/hexcasting/api/spell/casting/CastingContext.kt # Common/src/main/java/at/petrak/hexcasting/api/spell/casting/CastingHarness.kt # Common/src/main/java/at/petrak/hexcasting/api/spell/mishaps/MishapNoSpellCircle.kt # Common/src/main/java/at/petrak/hexcasting/api/spell/mishaps/MishapNotEnoughArgs.kt # Common/src/main/java/at/petrak/hexcasting/api/utils/ManaHelper.kt # Common/src/main/java/at/petrak/hexcasting/client/RegisterClientStuff.java # Common/src/main/java/at/petrak/hexcasting/client/ShiftScrollListener.java # Common/src/main/java/at/petrak/hexcasting/client/gui/GuiSpellcasting.kt # Common/src/main/java/at/petrak/hexcasting/common/blocks/akashic/BlockAkashicFloodfiller.java # Common/src/main/java/at/petrak/hexcasting/common/blocks/entity/BlockEntityStoredPlayerImpetus.java # Common/src/main/java/at/petrak/hexcasting/common/casting/RegisterPatterns.java # Common/src/main/java/at/petrak/hexcasting/common/casting/operators/OpBlockAxisRaycast.kt # Common/src/main/java/at/petrak/hexcasting/common/casting/operators/OpTheCoolerRead.kt # Common/src/main/java/at/petrak/hexcasting/common/casting/operators/OpTheCoolerReadable.kt # Common/src/main/java/at/petrak/hexcasting/common/casting/operators/OpWrite.kt # Common/src/main/java/at/petrak/hexcasting/common/casting/operators/lists/OpRemove.kt # Common/src/main/java/at/petrak/hexcasting/common/casting/operators/math/OpPowProj.kt # Common/src/main/java/at/petrak/hexcasting/common/casting/operators/math/logic/OpBoolIdentityKindOf.kt # Common/src/main/java/at/petrak/hexcasting/common/casting/operators/spells/OpBreakBlock.kt # Common/src/main/java/at/petrak/hexcasting/common/casting/operators/spells/OpConjureBlock.kt # Common/src/main/java/at/petrak/hexcasting/common/casting/operators/spells/OpCreateWater.kt # Common/src/main/java/at/petrak/hexcasting/common/casting/operators/spells/OpDestroyWater.kt # Common/src/main/java/at/petrak/hexcasting/common/casting/operators/spells/OpEdifySapling.kt # Common/src/main/java/at/petrak/hexcasting/common/casting/operators/spells/OpErase.kt # Common/src/main/java/at/petrak/hexcasting/common/casting/operators/spells/OpExplode.kt # Common/src/main/java/at/petrak/hexcasting/common/casting/operators/spells/OpExtinguish.kt # Common/src/main/java/at/petrak/hexcasting/common/casting/operators/spells/OpIgnite.kt # Common/src/main/java/at/petrak/hexcasting/common/casting/operators/spells/OpMakeBattery.kt # Common/src/main/java/at/petrak/hexcasting/common/casting/operators/spells/OpMakePackagedSpell.kt # Common/src/main/java/at/petrak/hexcasting/common/casting/operators/spells/OpPlaceBlock.kt # Common/src/main/java/at/petrak/hexcasting/common/casting/operators/spells/OpPotionEffect.kt # Common/src/main/java/at/petrak/hexcasting/common/casting/operators/spells/OpRecharge.kt # Common/src/main/java/at/petrak/hexcasting/common/casting/operators/spells/OpTheOnlyReasonAnyoneDownloadedPsi.kt # Common/src/main/java/at/petrak/hexcasting/common/casting/operators/spells/great/OpBrainsweep.kt # Common/src/main/java/at/petrak/hexcasting/common/casting/operators/spells/great/OpCreateLava.kt # Common/src/main/java/at/petrak/hexcasting/common/casting/operators/spells/great/OpTeleport.kt # Common/src/main/java/at/petrak/hexcasting/common/casting/operators/stack/OpAlwinfyHasAscendedToABeingOfPureMath.kt # Common/src/main/java/at/petrak/hexcasting/common/casting/operators/stack/OpMask.kt # Common/src/main/java/at/petrak/hexcasting/common/command/ListPatternsCommand.java # Common/src/main/java/at/petrak/hexcasting/common/items/magic/ItemCreativeUnlocker.java # Common/src/main/java/at/petrak/hexcasting/common/items/magic/ItemMediaHolder.java # Common/src/main/java/at/petrak/hexcasting/common/lib/HexBlocks.java # Common/src/main/java/at/petrak/hexcasting/common/lib/HexItems.java # Common/src/main/java/at/petrak/hexcasting/common/misc/AkashicTreeGrower.java # Common/src/main/java/at/petrak/hexcasting/common/network/MsgNewSpellPatternAck.java # Common/src/main/java/at/petrak/hexcasting/common/network/MsgShiftScrollSyn.java # Common/src/main/java/at/petrak/hexcasting/datagen/HexAdvancements.java # Common/src/main/java/at/petrak/hexcasting/datagen/HexItemTagProvider.java # Common/src/main/java/at/petrak/hexcasting/datagen/recipe/HexplatRecipes.kt # Common/src/main/java/at/petrak/hexcasting/mixin/accessor/client/AccessorMouseHandler.java # Common/src/main/java/at/petrak/hexcasting/xplat/IXplatAbstractions.java # Common/src/main/resources/assets/hexcasting/lang/en_us.json # Fabric/gradle.properties # Fabric/src/generated/resources/.cache/cache # Fabric/src/generated/resources/data/hexcasting/recipes/dye_colorizer_black.json # Fabric/src/generated/resources/data/hexcasting/recipes/dye_colorizer_blue.json # Fabric/src/generated/resources/data/hexcasting/recipes/dye_colorizer_brown.json # Fabric/src/generated/resources/data/hexcasting/recipes/dye_colorizer_cyan.json # Fabric/src/generated/resources/data/hexcasting/recipes/dye_colorizer_gray.json # Fabric/src/generated/resources/data/hexcasting/recipes/dye_colorizer_green.json # Fabric/src/generated/resources/data/hexcasting/recipes/dye_colorizer_light_blue.json # Fabric/src/generated/resources/data/hexcasting/recipes/dye_colorizer_light_gray.json # Fabric/src/generated/resources/data/hexcasting/recipes/dye_colorizer_lime.json # Fabric/src/generated/resources/data/hexcasting/recipes/dye_colorizer_magenta.json # Fabric/src/generated/resources/data/hexcasting/recipes/dye_colorizer_orange.json # Fabric/src/generated/resources/data/hexcasting/recipes/dye_colorizer_pink.json # Fabric/src/generated/resources/data/hexcasting/recipes/dye_colorizer_purple.json # Fabric/src/generated/resources/data/hexcasting/recipes/dye_colorizer_red.json # Fabric/src/generated/resources/data/hexcasting/recipes/dye_colorizer_white.json # Fabric/src/generated/resources/data/hexcasting/recipes/dye_colorizer_yellow.json # Fabric/src/generated/resources/data/hexcasting/recipes/pride_colorizer_agender.json # Fabric/src/generated/resources/data/hexcasting/recipes/pride_colorizer_aroace.json # Fabric/src/generated/resources/data/hexcasting/recipes/pride_colorizer_aromantic.json # Fabric/src/generated/resources/data/hexcasting/recipes/pride_colorizer_asexual.json # Fabric/src/generated/resources/data/hexcasting/recipes/pride_colorizer_bisexual.json # Fabric/src/generated/resources/data/hexcasting/recipes/pride_colorizer_demiboy.json # Fabric/src/generated/resources/data/hexcasting/recipes/pride_colorizer_demigirl.json # Fabric/src/generated/resources/data/hexcasting/recipes/pride_colorizer_gay.json # Fabric/src/generated/resources/data/hexcasting/recipes/pride_colorizer_genderfluid.json # Fabric/src/generated/resources/data/hexcasting/recipes/pride_colorizer_genderqueer.json # Fabric/src/generated/resources/data/hexcasting/recipes/pride_colorizer_intersex.json # Fabric/src/generated/resources/data/hexcasting/recipes/pride_colorizer_lesbian.json # Fabric/src/generated/resources/data/hexcasting/recipes/pride_colorizer_nonbinary.json # Fabric/src/generated/resources/data/hexcasting/recipes/pride_colorizer_pansexual.json # Fabric/src/generated/resources/data/hexcasting/recipes/pride_colorizer_plural.json # Fabric/src/generated/resources/data/hexcasting/recipes/pride_colorizer_transgender.json # Fabric/src/main/java/at/petrak/hexcasting/fabric/FabricHexClientInitializer.kt # Fabric/src/main/java/at/petrak/hexcasting/fabric/FabricHexConfig.java # Fabric/src/main/java/at/petrak/hexcasting/fabric/FabricHexInitializer.kt # Fabric/src/main/java/at/petrak/hexcasting/fabric/cc/CCDataHolder.java # Fabric/src/main/java/at/petrak/hexcasting/fabric/cc/HexCardinalComponents.java # Fabric/src/main/java/at/petrak/hexcasting/fabric/interop/emi/EmiBrainsweepRecipe.java # Fabric/src/main/java/at/petrak/hexcasting/fabric/interop/emi/HexEMIPlugin.java # Fabric/src/main/java/at/petrak/hexcasting/fabric/interop/rei/BrainsweepRecipeCategory.java # Fabric/src/main/java/at/petrak/hexcasting/fabric/interop/rei/HexREIPlugin.java # Fabric/src/main/java/at/petrak/hexcasting/fabric/interop/rei/PatternRendererREI.java # Fabric/src/main/java/at/petrak/hexcasting/fabric/recipe/FabricUnsealedIngredient.java # Fabric/src/main/java/at/petrak/hexcasting/fabric/storage/FabricImpetusStorage.kt # Fabric/src/main/java/at/petrak/hexcasting/fabric/xplat/FabricXplatImpl.java # Fabric/src/main/resources/fabric.mod.json # Forge/build.gradle # Forge/gradle.properties # Forge/src/generated/resources/.cache/cache # Forge/src/generated/resources/data/hexcasting/recipes/dye_colorizer_black.json # Forge/src/generated/resources/data/hexcasting/recipes/dye_colorizer_blue.json # Forge/src/generated/resources/data/hexcasting/recipes/dye_colorizer_brown.json # Forge/src/generated/resources/data/hexcasting/recipes/dye_colorizer_cyan.json # Forge/src/generated/resources/data/hexcasting/recipes/dye_colorizer_gray.json # Forge/src/generated/resources/data/hexcasting/recipes/dye_colorizer_green.json # Forge/src/generated/resources/data/hexcasting/recipes/dye_colorizer_light_blue.json # Forge/src/generated/resources/data/hexcasting/recipes/dye_colorizer_light_gray.json # Forge/src/generated/resources/data/hexcasting/recipes/dye_colorizer_lime.json # Forge/src/generated/resources/data/hexcasting/recipes/dye_colorizer_magenta.json # Forge/src/generated/resources/data/hexcasting/recipes/dye_colorizer_orange.json # Forge/src/generated/resources/data/hexcasting/recipes/dye_colorizer_pink.json # Forge/src/generated/resources/data/hexcasting/recipes/dye_colorizer_purple.json # Forge/src/generated/resources/data/hexcasting/recipes/dye_colorizer_red.json # Forge/src/generated/resources/data/hexcasting/recipes/dye_colorizer_white.json # Forge/src/generated/resources/data/hexcasting/recipes/dye_colorizer_yellow.json # Forge/src/generated/resources/data/hexcasting/recipes/pride_colorizer_agender.json # Forge/src/generated/resources/data/hexcasting/recipes/pride_colorizer_aroace.json # Forge/src/generated/resources/data/hexcasting/recipes/pride_colorizer_aromantic.json # Forge/src/generated/resources/data/hexcasting/recipes/pride_colorizer_asexual.json # Forge/src/generated/resources/data/hexcasting/recipes/pride_colorizer_bisexual.json # Forge/src/generated/resources/data/hexcasting/recipes/pride_colorizer_demiboy.json # Forge/src/generated/resources/data/hexcasting/recipes/pride_colorizer_demigirl.json # Forge/src/generated/resources/data/hexcasting/recipes/pride_colorizer_gay.json # Forge/src/generated/resources/data/hexcasting/recipes/pride_colorizer_genderfluid.json # Forge/src/generated/resources/data/hexcasting/recipes/pride_colorizer_genderqueer.json # Forge/src/generated/resources/data/hexcasting/recipes/pride_colorizer_intersex.json # Forge/src/generated/resources/data/hexcasting/recipes/pride_colorizer_lesbian.json # Forge/src/generated/resources/data/hexcasting/recipes/pride_colorizer_nonbinary.json # Forge/src/generated/resources/data/hexcasting/recipes/pride_colorizer_pansexual.json # Forge/src/generated/resources/data/hexcasting/recipes/pride_colorizer_plural.json # Forge/src/generated/resources/data/hexcasting/recipes/pride_colorizer_transgender.json # Forge/src/main/java/at/petrak/hexcasting/forge/ForgeHexClientInitializer.java # Forge/src/main/java/at/petrak/hexcasting/forge/ForgeHexConfig.java # Forge/src/main/java/at/petrak/hexcasting/forge/ForgeHexInitializer.java # Forge/src/main/java/at/petrak/hexcasting/forge/cap/ForgeCapabilityHandler.java # Forge/src/main/java/at/petrak/hexcasting/forge/cap/ForgeImpetusCapability.java # Forge/src/main/java/at/petrak/hexcasting/forge/datagen/HexForgeDataGenerators.java # Forge/src/main/java/at/petrak/hexcasting/forge/datagen/xplat/HexBlockStatesAndModels.java # Forge/src/main/java/at/petrak/hexcasting/forge/interop/jei/HexJEIPlugin.java # Forge/src/main/java/at/petrak/hexcasting/forge/recipe/ForgeUnsealedIngredient.java # Forge/src/main/java/at/petrak/hexcasting/forge/xplat/ForgeXplatImpl.java # Forge/src/main/resources/META-INF/accesstransformer.cfg # gradle.properties
This commit is contained in:
commit
a01ff27453
193 changed files with 5970 additions and 1250 deletions
|
@ -38,6 +38,9 @@ dependencies {
|
|||
compileOnly "at.petra-k.paucal:paucal-common-$minecraftVersion:$paucalVersion"
|
||||
compileOnly "vazkii.patchouli:Patchouli-xplat:$minecraftVersion-$patchouliVersion"
|
||||
|
||||
compileOnly "org.jetbrains:annotations:$jetbrainsAnnotationsVersion"
|
||||
testCompileOnly "org.jetbrains:annotations:$jetbrainsAnnotationsVersion"
|
||||
|
||||
testImplementation 'org.junit.jupiter:junit-jupiter-api:5.6.1'
|
||||
testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.6.1'
|
||||
}
|
||||
|
|
|
@ -4,9 +4,7 @@
|
|||
"conditions": {
|
||||
"items": [
|
||||
{
|
||||
"items": [
|
||||
"hexcasting:charged_amethyst"
|
||||
]
|
||||
"tag": "hexcasting:grants_root_advancement"
|
||||
}
|
||||
]
|
||||
},
|
||||
|
|
267
Common/src/main/java/at/petrak/hexcasting/api/PatternRegistry.kt
Normal file
267
Common/src/main/java/at/petrak/hexcasting/api/PatternRegistry.kt
Normal file
|
@ -0,0 +1,267 @@
|
|||
package at.petrak.hexcasting.api
|
||||
|
||||
import at.petrak.hexcasting.api.spell.Operator
|
||||
import at.petrak.hexcasting.api.spell.math.EulerPathFinder
|
||||
import at.petrak.hexcasting.api.spell.math.HexDir
|
||||
import at.petrak.hexcasting.api.spell.math.HexPattern
|
||||
import at.petrak.hexcasting.api.spell.mishaps.MishapInvalidPattern
|
||||
import at.petrak.hexcasting.api.utils.getSafe
|
||||
import net.minecraft.nbt.CompoundTag
|
||||
import net.minecraft.resources.ResourceLocation
|
||||
import net.minecraft.server.level.ServerLevel
|
||||
import net.minecraft.world.level.saveddata.SavedData
|
||||
import java.util.concurrent.ConcurrentHashMap
|
||||
import java.util.concurrent.ConcurrentLinkedDeque
|
||||
import java.util.concurrent.ConcurrentMap
|
||||
|
||||
/**
|
||||
* Register your patterns and associate them with spells here.
|
||||
*
|
||||
* Most patterns are matches to their operators solely by their *angle signature*.
|
||||
* This is a record of all the angles in the pattern, independent of the direction the player started drawing in.
|
||||
* It's written in shorthand, where `w` is straight ahead, `q` is left, `a` is left-back, `d` is right-back,
|
||||
* and `e` is right.
|
||||
*
|
||||
* For example, the signature for a straight line of 3 segments is `"ww"`. The signatures for the "get caster"
|
||||
* operator (the diamond) are `"qaq"` and `"ede"`.
|
||||
*/
|
||||
object PatternRegistry {
|
||||
private val operatorLookup = ConcurrentHashMap<ResourceLocation, Operator>()
|
||||
private val keyLookup = ConcurrentHashMap<Operator, ResourceLocation>()
|
||||
private val specialHandlers: ConcurrentLinkedDeque<SpecialHandlerEntry> = ConcurrentLinkedDeque()
|
||||
|
||||
// Map signatures to the "preferred" direction they start in and their operator ID.
|
||||
private val regularPatternLookup: ConcurrentMap<String, RegularEntry> =
|
||||
ConcurrentHashMap()
|
||||
|
||||
private val perWorldPatternLookup: ConcurrentMap<ResourceLocation, PerWorldEntry> =
|
||||
ConcurrentHashMap()
|
||||
|
||||
/**
|
||||
* Associate a given angle signature with a SpellOperator.
|
||||
*/
|
||||
@JvmStatic
|
||||
@JvmOverloads
|
||||
@Throws(RegisterPatternException::class)
|
||||
fun mapPattern(pattern: HexPattern, id: ResourceLocation, operator: Operator, isPerWorld: Boolean = false) {
|
||||
this.operatorLookup[id]?.let {
|
||||
throw RegisterPatternException("The operator with id `$id` was already registered to: $it")
|
||||
}
|
||||
|
||||
this.operatorLookup[id] = operator
|
||||
this.keyLookup[operator] = id
|
||||
if (isPerWorld) {
|
||||
this.perWorldPatternLookup[id] = PerWorldEntry(pattern, id)
|
||||
} else {
|
||||
this.regularPatternLookup[pattern.anglesSignature()] = RegularEntry(pattern.startDir, id)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Add a special handler, to take an arbitrary pattern and return whatever kind of operator you like.
|
||||
*/
|
||||
@JvmStatic
|
||||
fun addSpecialHandler(handler: SpecialHandlerEntry) {
|
||||
this.specialHandlers.add(handler)
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun addSpecialHandler(id: ResourceLocation, handler: SpecialHandler) {
|
||||
this.addSpecialHandler(SpecialHandlerEntry(id, handler))
|
||||
}
|
||||
|
||||
/**
|
||||
* Internal use only.
|
||||
*/
|
||||
@JvmStatic
|
||||
fun matchPattern(pat: HexPattern, overworld: ServerLevel): Operator =
|
||||
matchPatternAndID(pat, overworld).first
|
||||
|
||||
/**
|
||||
* Internal use only.
|
||||
*/
|
||||
@JvmStatic
|
||||
fun matchPatternAndID(pat: HexPattern, overworld: ServerLevel): Pair<Operator, ResourceLocation> {
|
||||
// Is it global?
|
||||
val sig = pat.anglesSignature()
|
||||
this.regularPatternLookup[sig]?.let {
|
||||
val op = this.operatorLookup[it.opId] ?: throw MishapInvalidPattern()
|
||||
return op to it.opId
|
||||
}
|
||||
|
||||
// Look it up in the world?
|
||||
val ds = overworld.dataStorage
|
||||
val perWorldPatterns: Save =
|
||||
ds.computeIfAbsent(Save.Companion::load, { Save.create(overworld.seed) }, TAG_SAVED_DATA)
|
||||
perWorldPatterns.fillMissingEntries(overworld.seed)
|
||||
perWorldPatterns.lookup[sig]?.let {
|
||||
val op = this.operatorLookup[it.first]!!
|
||||
return op to it.first
|
||||
}
|
||||
|
||||
// Lookup a special handler
|
||||
// Do this last to prevent conflicts with great spells; this has happened a few times with
|
||||
// create phial hahaha
|
||||
for (handler in specialHandlers) {
|
||||
val op = handler.handler.handlePattern(pat)
|
||||
if (op != null) return op to handler.id
|
||||
}
|
||||
|
||||
throw MishapInvalidPattern()
|
||||
}
|
||||
|
||||
/**
|
||||
* Internal use only.
|
||||
*/
|
||||
@JvmStatic
|
||||
fun getPerWorldPatterns(overworld: ServerLevel): Map<String, Pair<ResourceLocation, HexDir>> {
|
||||
val ds = overworld.dataStorage
|
||||
val perWorldPatterns: Save =
|
||||
ds.computeIfAbsent(Save.Companion::load, { Save.create(overworld.seed) }, TAG_SAVED_DATA)
|
||||
return perWorldPatterns.lookup
|
||||
}
|
||||
|
||||
/**
|
||||
* Internal use only.
|
||||
*/
|
||||
@JvmStatic
|
||||
fun lookupPattern(op: Operator): ResourceLocation? = this.keyLookup[op]
|
||||
|
||||
/**
|
||||
* Internal use only.
|
||||
*/
|
||||
@JvmStatic
|
||||
fun lookupPattern(opId: ResourceLocation): PatternEntry {
|
||||
this.perWorldPatternLookup[opId]?.let {
|
||||
return PatternEntry(it.prototype, this.operatorLookup[it.opId]!!, true)
|
||||
}
|
||||
for ((sig, entry) in this.regularPatternLookup) {
|
||||
if (entry.opId == opId) {
|
||||
val pattern = HexPattern.fromAngles(sig, entry.preferredStart)
|
||||
return PatternEntry(pattern, this.operatorLookup[entry.opId]!!, false)
|
||||
}
|
||||
}
|
||||
|
||||
throw IllegalArgumentException("could not find a pattern for $opId")
|
||||
}
|
||||
|
||||
/**
|
||||
* Internal use only.
|
||||
*/
|
||||
@JvmStatic
|
||||
fun getAllPerWorldPatternNames(): Set<ResourceLocation> {
|
||||
return this.perWorldPatternLookup.keys.toSet()
|
||||
}
|
||||
|
||||
/**
|
||||
* Special handling of a pattern. Before checking any of the normal angle-signature based patterns,
|
||||
* a given pattern is run by all of these special handlers patterns. If none of them return non-null,
|
||||
* then its signature is checked.
|
||||
*
|
||||
* In the base mod, this is used for number patterns.
|
||||
*/
|
||||
fun interface SpecialHandler {
|
||||
fun handlePattern(pattern: HexPattern): Operator?
|
||||
}
|
||||
|
||||
data class SpecialHandlerEntry(val id: ResourceLocation, val handler: SpecialHandler)
|
||||
|
||||
class RegisterPatternException(msg: String) : Exception(msg)
|
||||
|
||||
private data class RegularEntry(val preferredStart: HexDir, val opId: ResourceLocation)
|
||||
private data class PerWorldEntry(val prototype: HexPattern, val opId: ResourceLocation)
|
||||
|
||||
// Fake class we pretend to use internally
|
||||
data class PatternEntry(val prototype: HexPattern, val operator: Operator, val isPerWorld: Boolean)
|
||||
|
||||
/**
|
||||
* Maps angle sigs to resource locations and their preferred start dir so we can look them up in the main registry
|
||||
*/
|
||||
|
||||
class Save(val lookup: MutableMap<String, Pair<ResourceLocation, HexDir>>, var missingEntries: Boolean) : SavedData() {
|
||||
constructor(lookup: MutableMap<String, Pair<ResourceLocation, HexDir>>) : this(lookup, missingAny(lookup))
|
||||
|
||||
override fun save(tag: CompoundTag): CompoundTag {
|
||||
for ((sig, rhs) in this.lookup) {
|
||||
val (id, startDir) = rhs
|
||||
val entry = CompoundTag()
|
||||
entry.putString(TAG_OP_ID, id.toString())
|
||||
entry.putByte(TAG_START_DIR, startDir.ordinal.toByte())
|
||||
tag.put(sig, entry)
|
||||
}
|
||||
return tag
|
||||
}
|
||||
|
||||
fun fillMissingEntries(seed: Long) {
|
||||
if (missingEntries) {
|
||||
var doneAny = false
|
||||
|
||||
val allIds = lookup.values.map { it.first }
|
||||
for ((prototype, opId) in perWorldPatternLookup.values) {
|
||||
if (opId !in allIds) {
|
||||
scrungle(lookup, prototype, opId, seed)
|
||||
doneAny = true
|
||||
}
|
||||
}
|
||||
|
||||
if (doneAny) {
|
||||
setDirty()
|
||||
missingEntries = false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
fun missingAny(lookup: MutableMap<String, Pair<ResourceLocation, HexDir>>): Boolean {
|
||||
val allIds = lookup.values.map { it.first }
|
||||
return perWorldPatternLookup.values.any { it.opId !in allIds }
|
||||
}
|
||||
|
||||
fun load(tag: CompoundTag): Save {
|
||||
val map = HashMap<String, Pair<ResourceLocation, HexDir>>()
|
||||
val allIds = mutableSetOf<ResourceLocation>()
|
||||
for (sig in tag.allKeys) {
|
||||
val entry = tag.getCompound(sig)
|
||||
val opId = ResourceLocation.tryParse(entry.getString(TAG_OP_ID)) ?: continue
|
||||
allIds.add(opId)
|
||||
val startDir = HexDir.values().getSafe(entry.getByte(TAG_START_DIR))
|
||||
map[sig] = opId to startDir
|
||||
}
|
||||
val missingEntries = perWorldPatternLookup.values.any { it.opId !in allIds }
|
||||
return Save(map, missingEntries)
|
||||
}
|
||||
|
||||
fun scrungle(lookup: MutableMap<String, Pair<ResourceLocation, HexDir>>, prototype: HexPattern, opId: ResourceLocation, seed: Long) {
|
||||
val scrungled = EulerPathFinder.findAltDrawing(prototype, seed) {
|
||||
val sig = it.anglesSignature()
|
||||
!lookup.contains(sig) &&
|
||||
!regularPatternLookup.contains(sig)
|
||||
&& specialHandlers.none { handler -> handler.handler.handlePattern(it) != null }
|
||||
}
|
||||
lookup[scrungled.anglesSignature()] = opId to scrungled.startDir
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun create(seed: Long): Save {
|
||||
val map = mutableMapOf<String, Pair<ResourceLocation, HexDir>>()
|
||||
for ((prototype, opId) in perWorldPatternLookup.values) {
|
||||
scrungle(map, prototype, opId, seed)
|
||||
}
|
||||
val save = Save(map)
|
||||
save.setDirty()
|
||||
return save
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const val TAG_SAVED_DATA = "hex.per-world-patterns"
|
||||
private const val TAG_OP_ID = "op_id"
|
||||
private const val TAG_START_DIR = "start_dir"
|
||||
|
||||
@JvmStatic
|
||||
fun getPatternCountInfo(): String = "Loaded ${regularPatternLookup.size} regular patterns, " +
|
||||
"${perWorldPatternLookup.size} per-world patterns, and " +
|
||||
"${specialHandlers.size} special handlers."
|
||||
}
|
|
@ -0,0 +1,103 @@
|
|||
package at.petrak.hexcasting.api.addldata;
|
||||
|
||||
import org.jetbrains.annotations.ApiStatus;
|
||||
|
||||
public interface ManaHolder {
|
||||
|
||||
/**
|
||||
* Use {@code withdrawMana(-1, true)}
|
||||
*
|
||||
* @see ManaHolder#withdrawMana(int, boolean)
|
||||
*/
|
||||
@ApiStatus.OverrideOnly
|
||||
int getMana();
|
||||
|
||||
/**
|
||||
* Use {@code withdrawMana(-1, true) + insertMana(-1, true)} where possible
|
||||
*
|
||||
* @see ManaHolder#insertMana(int, boolean)
|
||||
* @see ManaHolder#withdrawMana(int, boolean)
|
||||
*/
|
||||
@ApiStatus.OverrideOnly
|
||||
int getMaxMana();
|
||||
|
||||
/**
|
||||
* Use {@code insertMana(mana - withdrawMana(-1, true), false)} where possible
|
||||
*
|
||||
* @see ManaHolder#insertMana(int, boolean)
|
||||
* @see ManaHolder#withdrawMana(int, boolean)
|
||||
*/
|
||||
@ApiStatus.OverrideOnly
|
||||
void setMana(int mana);
|
||||
|
||||
/**
|
||||
* Whether this mana holder can have mana inserted into it.
|
||||
*/
|
||||
boolean canRecharge();
|
||||
|
||||
/**
|
||||
* Whether this mana holder can be extracted from.
|
||||
*/
|
||||
boolean canProvide();
|
||||
|
||||
/**
|
||||
* The priority for this mana holder to be selected when casting a hex. Higher priorities are taken first.
|
||||
*
|
||||
* By default,
|
||||
* * Charged Amethyst has priority 1
|
||||
* * Amethyst Shards have priority 2
|
||||
* * Amethyst Dust has priority 3
|
||||
* * Items which hold mana have priority 40
|
||||
*/
|
||||
int getConsumptionPriority();
|
||||
|
||||
/**
|
||||
* Whether the mana inside this mana holder may be used to construct a battery.
|
||||
*/
|
||||
boolean canConstructBattery();
|
||||
|
||||
/**
|
||||
* Withdraws mana from the holder. Returns the amount of mana extracted, which may be less or more than the cost.
|
||||
*
|
||||
* Even if {@link ManaHolder#canProvide} is false, you can still withdraw mana this way.
|
||||
*
|
||||
* Withdrawing a negative amount will act as though you attempted to withdraw as much mana as the holder contains.
|
||||
*/
|
||||
default int withdrawMana(int cost, boolean simulate) {
|
||||
var manaHere = getMana();
|
||||
if (cost < 0) {
|
||||
cost = manaHere;
|
||||
}
|
||||
if (!simulate) {
|
||||
var manaLeft = manaHere - cost;
|
||||
setMana(manaLeft);
|
||||
}
|
||||
return Math.min(cost, manaHere);
|
||||
}
|
||||
|
||||
/**
|
||||
* Inserts mana into the holder. Returns the amount of mana inserted, which may be less than the requested amount.
|
||||
*
|
||||
* Even if {@link ManaHolder#canRecharge} is false, you can still insert mana this way.
|
||||
*
|
||||
* Inserting a negative amount will act as though you attempted to insert exactly as much mana as the holder was missing.
|
||||
*/
|
||||
default int insertMana(int amount, boolean simulate) {
|
||||
var manaHere = getMana();
|
||||
int emptySpace = getMaxMana() - manaHere;
|
||||
if (emptySpace <= 0) {
|
||||
return 0;
|
||||
}
|
||||
if (amount < 0) {
|
||||
amount = emptySpace;
|
||||
}
|
||||
|
||||
int inserting = Math.min(amount, emptySpace);
|
||||
|
||||
if (!simulate) {
|
||||
var newMana = manaHere + inserting;
|
||||
setMana(newMana);
|
||||
}
|
||||
return inserting;
|
||||
}
|
||||
}
|
|
@ -35,7 +35,7 @@ public class OvercastTrigger extends SimpleCriterionTrigger<OvercastTrigger.Inst
|
|||
super.trigger(player, inst -> {
|
||||
var manaToHealth = HexConfig.common().manaToHealthRate();
|
||||
var healthUsed = manaGenerated / manaToHealth;
|
||||
return inst.test(manaGenerated, healthUsed / player.getMaxHealth(), player.getHealth() - (float) healthUsed);
|
||||
return inst.test(manaGenerated, healthUsed / player.getMaxHealth(), player.getHealth());
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -15,8 +15,6 @@ import at.petrak.hexcasting.common.lib.HexItems;
|
|||
import at.petrak.hexcasting.common.lib.HexSounds;
|
||||
import at.petrak.hexcasting.xplat.IXplatAbstractions;
|
||||
import com.mojang.datafixers.util.Pair;
|
||||
import net.minecraft.client.multiplayer.ClientLevel;
|
||||
import net.minecraft.client.player.LocalPlayer;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.core.Direction;
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
|
@ -35,6 +33,7 @@ import net.minecraft.world.entity.player.Player;
|
|||
import net.minecraft.world.item.DyeColor;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import net.minecraft.world.item.Items;
|
||||
import net.minecraft.world.level.Level;
|
||||
import net.minecraft.world.level.block.Blocks;
|
||||
import net.minecraft.world.level.block.entity.BlockEntityType;
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
|
@ -43,6 +42,7 @@ import net.minecraft.world.phys.Vec3;
|
|||
import org.jetbrains.annotations.Contract;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.text.DecimalFormat;
|
||||
import java.util.*;
|
||||
|
||||
public abstract class BlockEntityAbstractImpetus extends HexBlockEntity implements WorldlyContainer {
|
||||
|
@ -55,6 +55,8 @@ public abstract class BlockEntityAbstractImpetus extends HexBlockEntity implemen
|
|||
TAG_MANA = "mana",
|
||||
TAG_LAST_MISHAP = "last_mishap";
|
||||
|
||||
private static final DecimalFormat DUST_AMOUNT = new DecimalFormat("###,###.##");
|
||||
|
||||
@Nullable
|
||||
private UUID activator = null;
|
||||
@Nullable
|
||||
|
@ -113,9 +115,9 @@ public abstract class BlockEntityAbstractImpetus extends HexBlockEntity implemen
|
|||
}
|
||||
|
||||
public void applyScryingLensOverlay(List<Pair<ItemStack, Component>> lines,
|
||||
BlockState state, BlockPos pos,
|
||||
LocalPlayer observer, ClientLevel world,
|
||||
Direction hitFace, InteractionHand lensHand) {
|
||||
BlockState state, BlockPos pos,
|
||||
Player observer, Level world,
|
||||
Direction hitFace) {
|
||||
if (world.getBlockEntity(pos) instanceof BlockEntityAbstractImpetus beai) {
|
||||
if (beai.getMana() < 0) {
|
||||
lines.add(new Pair<>(new ItemStack(HexItems.AMETHYST_DUST), ItemCreativeUnlocker.infiniteMedia(world)));
|
||||
|
@ -532,23 +534,26 @@ public abstract class BlockEntityAbstractImpetus extends HexBlockEntity implemen
|
|||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clearContent() {
|
||||
// NO-OP
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canPlaceItem(int index, ItemStack stack) {
|
||||
if (remainingManaCapacity() == 0)
|
||||
return false;
|
||||
|
||||
if (stack.is(HexItems.CREATIVE_UNLOCKER))
|
||||
return true;
|
||||
|
||||
var manamount = extractManaFromItem(stack, true);
|
||||
return manamount > 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clearContent() {
|
||||
this.mana = 0;
|
||||
this.stopCasting();
|
||||
this.sync();
|
||||
}
|
||||
|
||||
public int extractManaFromItem(ItemStack stack, boolean simulate) {
|
||||
if (this.mana < 0) {
|
||||
if (this.mana < 0)
|
||||
return 0;
|
||||
}
|
||||
return ManaHelper.extractMana(stack, remainingManaCapacity(), true, simulate);
|
||||
}
|
||||
|
||||
|
@ -559,7 +564,7 @@ public abstract class BlockEntityAbstractImpetus extends HexBlockEntity implemen
|
|||
} else {
|
||||
var manamount = extractManaFromItem(stack, false);
|
||||
if (manamount > 0) {
|
||||
this.mana += manamount;
|
||||
this.mana = Math.min(manamount + mana, MAX_CAPACITY);
|
||||
this.sync();
|
||||
}
|
||||
}
|
||||
|
@ -571,9 +576,8 @@ public abstract class BlockEntityAbstractImpetus extends HexBlockEntity implemen
|
|||
}
|
||||
|
||||
public int remainingManaCapacity() {
|
||||
if (this.mana < 0) {
|
||||
if (this.mana < 0)
|
||||
return 0;
|
||||
}
|
||||
return MAX_CAPACITY - this.mana;
|
||||
return Math.max(0, MAX_CAPACITY - this.mana);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,21 +4,20 @@ import at.petrak.hexcasting.xplat.IXplatAbstractions;
|
|||
import com.google.common.collect.Lists;
|
||||
import com.mojang.datafixers.util.Pair;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.multiplayer.ClientLevel;
|
||||
import net.minecraft.client.player.LocalPlayer;
|
||||
import net.minecraft.world.level.Level;
|
||||
import net.minecraft.world.entity.player.Player;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.core.Direction;
|
||||
import net.minecraft.network.chat.Component;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.world.InteractionHand;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import net.minecraft.world.level.block.BeehiveBlock;
|
||||
import net.minecraft.world.level.block.Block;
|
||||
import net.minecraft.world.level.block.Blocks;
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
import net.minecraft.world.phys.BlockHitResult;
|
||||
import net.minecraft.world.phys.HitResult;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
@ -38,15 +37,22 @@ public final class ScryingLensOverlayRegistry {
|
|||
private static final List<Pair<OverlayPredicate, OverlayBuilder>> PREDICATE_LOOKUP = new Vector<>();
|
||||
|
||||
// implemented as a map to allow for weak dereferencing
|
||||
private static final Map<LocalPlayer, Pair<BlockPos, Integer>> comparatorData = new WeakHashMap<>();
|
||||
private static final Map<Player, Pair<BlockPos, Integer>> comparatorData = new WeakHashMap<>();
|
||||
private static final Map<Player, Pair<BlockPos, Integer>> beeData = new WeakHashMap<>();
|
||||
|
||||
public static void receiveComparatorValue(BlockPos pos, int value) {
|
||||
LocalPlayer player = Minecraft.getInstance().player;
|
||||
public static void receiveComparatorAndBeeValue(BlockPos pos, int comparator, int bee) {
|
||||
Player player = Minecraft.getInstance().player;
|
||||
if (player != null) {
|
||||
if (pos == null || value == -1) {
|
||||
if (pos == null || comparator == -1) {
|
||||
comparatorData.remove(player);
|
||||
} else {
|
||||
comparatorData.put(player, new Pair<>(pos, value));
|
||||
comparatorData.put(player, new Pair<>(pos, comparator));
|
||||
}
|
||||
|
||||
if (pos == null || bee == -1) {
|
||||
beeData.remove(player);
|
||||
} else {
|
||||
beeData.put(player, new Pair<>(pos, bee));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -80,6 +86,34 @@ public final class ScryingLensOverlayRegistry {
|
|||
return comparatorValue.getSecond();
|
||||
}
|
||||
|
||||
public static int getBeeValue() {
|
||||
var mc = Minecraft.getInstance();
|
||||
var player = mc.player;
|
||||
var level = mc.level;
|
||||
var result = mc.hitResult;
|
||||
|
||||
if (player == null || level == null || result == null || result.getType() != HitResult.Type.BLOCK) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
var beeValue = beeData.get(player);
|
||||
if (beeValue == null) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
var pos = ((BlockHitResult) result).getBlockPos();
|
||||
if (!pos.equals(beeValue.getFirst())) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
var state = mc.level.getBlockState(pos);
|
||||
if (!(state.getBlock() instanceof BeehiveBlock)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return beeValue.getSecond();
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the block to display things when the player is holding a lens and looking at it.
|
||||
*
|
||||
|
@ -115,17 +149,17 @@ public final class ScryingLensOverlayRegistry {
|
|||
* Internal use only.
|
||||
*/
|
||||
public static @NotNull List<Pair<ItemStack, Component>> getLines(BlockState state, BlockPos pos,
|
||||
LocalPlayer observer, ClientLevel world,
|
||||
Direction hitFace, @Nullable InteractionHand lensHand) {
|
||||
Player observer, Level world,
|
||||
Direction hitFace) {
|
||||
List<Pair<ItemStack, Component>> lines = Lists.newArrayList();
|
||||
var idLookedup = ID_LOOKUP.get(IXplatAbstractions.INSTANCE.getID(state.getBlock()));
|
||||
if (idLookedup != null) {
|
||||
idLookedup.addLines(lines, state, pos, observer, world, hitFace, lensHand);
|
||||
idLookedup.addLines(lines, state, pos, observer, world, hitFace);
|
||||
}
|
||||
|
||||
for (var pair : PREDICATE_LOOKUP) {
|
||||
if (pair.getFirst().test(state, pos, observer, world, hitFace, lensHand)) {
|
||||
pair.getSecond().addLines(lines, state, pos, observer, world, hitFace, lensHand);
|
||||
if (pair.getFirst().test(state, pos, observer, world, hitFace)) {
|
||||
pair.getSecond().addLines(lines, state, pos, observer, world, hitFace);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -140,9 +174,9 @@ public final class ScryingLensOverlayRegistry {
|
|||
@FunctionalInterface
|
||||
public interface OverlayBuilder {
|
||||
void addLines(List<Pair<ItemStack, Component>> lines,
|
||||
BlockState state, BlockPos pos, LocalPlayer observer,
|
||||
ClientLevel world,
|
||||
Direction hitFace, @Nullable InteractionHand lensHand);
|
||||
BlockState state, BlockPos pos, Player observer,
|
||||
Level world,
|
||||
Direction hitFace);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -150,8 +184,8 @@ public final class ScryingLensOverlayRegistry {
|
|||
*/
|
||||
@FunctionalInterface
|
||||
public interface OverlayPredicate {
|
||||
boolean test(BlockState state, BlockPos pos, LocalPlayer observer,
|
||||
ClientLevel world,
|
||||
Direction hitFace, @Nullable InteractionHand lensHand);
|
||||
boolean test(BlockState state, BlockPos pos, Player observer,
|
||||
Level world,
|
||||
Direction hitFace);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,6 +2,7 @@ package at.petrak.hexcasting.api.item;
|
|||
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import net.minecraft.world.phys.Vec3;
|
||||
import org.jetbrains.annotations.ApiStatus;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
|
@ -11,6 +12,7 @@ import java.util.UUID;
|
|||
* On both the Forge and Fabric sides, the registry will be scanned for all items which implement this interface,
|
||||
* and the appropriate cap/CC will be attached.
|
||||
*/
|
||||
@ApiStatus.OverrideOnly
|
||||
public interface ColorizerItem {
|
||||
int color(ItemStack stack, UUID owner, float time, Vec3 position);
|
||||
}
|
||||
|
|
|
@ -0,0 +1,60 @@
|
|||
package at.petrak.hexcasting.api.item;
|
||||
|
||||
import at.petrak.hexcasting.api.spell.SpellDatum;
|
||||
import at.petrak.hexcasting.api.utils.NBTHelper;
|
||||
import net.minecraft.ChatFormatting;
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
import net.minecraft.nbt.NbtUtils;
|
||||
import net.minecraft.network.chat.Component;
|
||||
import net.minecraft.network.chat.TextComponent;
|
||||
import net.minecraft.network.chat.TranslatableComponent;
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import net.minecraft.world.item.TooltipFlag;
|
||||
import org.jetbrains.annotations.ApiStatus;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@ApiStatus.OverrideOnly
|
||||
public interface DataHolderItem {
|
||||
String TAG_OVERRIDE_VISUALLY = "VisualOverride";
|
||||
|
||||
@Nullable CompoundTag readDatumTag(ItemStack stack);
|
||||
|
||||
@Nullable
|
||||
default SpellDatum<?> readDatum(ItemStack stack, ServerLevel world) {
|
||||
var tag = readDatumTag(stack);
|
||||
if (tag != null) {
|
||||
return SpellDatum.fromNBT(tag, world);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
default SpellDatum<?> emptyDatum(ItemStack stack) {
|
||||
return null;
|
||||
}
|
||||
|
||||
boolean canWrite(ItemStack stack, @Nullable SpellDatum<?> datum);
|
||||
|
||||
void writeDatum(ItemStack stack, @Nullable SpellDatum<?> datum);
|
||||
|
||||
static void appendHoverText(DataHolderItem self, ItemStack pStack, List<Component> pTooltipComponents,
|
||||
TooltipFlag pIsAdvanced) {
|
||||
var datumTag = self.readDatumTag(pStack);
|
||||
if (datumTag != null) {
|
||||
var component = SpellDatum.displayFromNBT(datumTag);
|
||||
pTooltipComponents.add(new TranslatableComponent("hexcasting.spelldata.onitem", component));
|
||||
|
||||
if (pIsAdvanced.isAdvanced()) {
|
||||
pTooltipComponents.add(new TextComponent("").append(NbtUtils.toPrettyComponent(datumTag)));
|
||||
}
|
||||
} else if (NBTHelper.hasString(pStack, DataHolderItem.TAG_OVERRIDE_VISUALLY)) {
|
||||
pTooltipComponents.add(new TranslatableComponent("hexcasting.spelldata.onitem",
|
||||
new TranslatableComponent("hexcasting.spelldata.anything").withStyle(ChatFormatting.LIGHT_PURPLE)));
|
||||
|
||||
}
|
||||
}
|
||||
}
|
|
@ -3,6 +3,7 @@ package at.petrak.hexcasting.api.item;
|
|||
import at.petrak.hexcasting.api.spell.iota.Iota;
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import org.jetbrains.annotations.ApiStatus;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.List;
|
||||
|
@ -13,6 +14,7 @@ import java.util.List;
|
|||
* On both the Forge and Fabric sides, the registry will be scanned for all items which implement this interface,
|
||||
* and the appropriate cap/CC will be attached.
|
||||
*/
|
||||
@ApiStatus.OverrideOnly
|
||||
public interface HexHolderItem extends MediaHolderItem {
|
||||
|
||||
boolean canDrawManaFromInventory(ItemStack stack);
|
||||
|
|
|
@ -0,0 +1,59 @@
|
|||
package at.petrak.hexcasting.api.item;
|
||||
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import org.jetbrains.annotations.ApiStatus;
|
||||
|
||||
/**
|
||||
* This interface should not be accessed direc
|
||||
*/
|
||||
@ApiStatus.OverrideOnly
|
||||
public interface ManaHolderItem {
|
||||
int getMana(ItemStack stack);
|
||||
|
||||
int getMaxMana(ItemStack stack);
|
||||
|
||||
void setMana(ItemStack stack, int mana);
|
||||
|
||||
boolean manaProvider(ItemStack stack);
|
||||
|
||||
boolean canRecharge(ItemStack stack);
|
||||
|
||||
default float getManaFullness(ItemStack stack) {
|
||||
int max = getMaxMana(stack);
|
||||
if (max == 0) {
|
||||
return 0;
|
||||
}
|
||||
return (float) getMana(stack) / (float) max;
|
||||
}
|
||||
|
||||
default int withdrawMana(ItemStack stack, int cost, boolean simulate) {
|
||||
var manaHere = getMana(stack);
|
||||
if (cost < 0) {
|
||||
cost = manaHere;
|
||||
}
|
||||
if (!simulate) {
|
||||
var manaLeft = manaHere - cost;
|
||||
setMana(stack, manaLeft);
|
||||
}
|
||||
return Math.min(cost, manaHere);
|
||||
}
|
||||
|
||||
default int insertMana(ItemStack stack, int amount, boolean simulate) {
|
||||
var manaHere = getMana(stack);
|
||||
int emptySpace = getMaxMana(stack) - manaHere;
|
||||
if (emptySpace <= 0) {
|
||||
return 0;
|
||||
}
|
||||
if (amount < 0) {
|
||||
amount = emptySpace;
|
||||
}
|
||||
|
||||
int inserting = Math.min(amount, emptySpace);
|
||||
|
||||
if (!simulate) {
|
||||
var newMana = manaHere + inserting;
|
||||
setMana(stack, newMana);
|
||||
}
|
||||
return inserting;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,99 @@
|
|||
package at.petrak.hexcasting.api.misc;
|
||||
|
||||
import at.petrak.hexcasting.api.addldata.ManaHolder;
|
||||
import at.petrak.hexcasting.api.spell.casting.CastingContext;
|
||||
import at.petrak.hexcasting.api.spell.casting.CastingHarness;
|
||||
import com.google.common.collect.Lists;
|
||||
import net.minecraft.util.ToFloatFunction;
|
||||
import net.minecraft.world.entity.player.Player;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.function.BiFunction;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
public class DiscoveryHandlers {
|
||||
private static final List<Predicate<Player>> HAS_LENS_PREDICATE = new ArrayList<>();
|
||||
private static final List<Function<CastingHarness, List<ManaHolder>>> MANA_HOLDER_DISCOVERY = new ArrayList<>();
|
||||
private static final List<ToFloatFunction<Player>> GRID_SCALE_MODIFIERS = new ArrayList<>();
|
||||
private static final List<Function<CastingContext, List<ItemStack>>> ITEM_SLOT_DISCOVERER = new ArrayList<>();
|
||||
private static final List<Function<CastingContext, List<ItemStack>>> OPERATIVE_SLOT_DISCOVERER = new ArrayList<>();
|
||||
private static final List<BiFunction<Player, String, ItemStack>> DEBUG_DISCOVERER = new ArrayList<>();
|
||||
|
||||
public static boolean hasLens(Player player) {
|
||||
for (var predicate : HAS_LENS_PREDICATE) {
|
||||
if (predicate.test(player)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public static List<ManaHolder> collectManaHolders(CastingHarness harness) {
|
||||
List<ManaHolder> holders = Lists.newArrayList();
|
||||
for (var discoverer : MANA_HOLDER_DISCOVERY) {
|
||||
holders.addAll(discoverer.apply(harness));
|
||||
}
|
||||
return holders;
|
||||
}
|
||||
|
||||
public static float gridScaleModifier(Player player) {
|
||||
float mod = 1;
|
||||
for (var modifier : GRID_SCALE_MODIFIERS) {
|
||||
mod *= modifier.apply(player);
|
||||
}
|
||||
return mod;
|
||||
}
|
||||
|
||||
public static List<ItemStack> collectItemSlots(CastingContext ctx) {
|
||||
List<ItemStack> stacks = Lists.newArrayList();
|
||||
for (var discoverer : ITEM_SLOT_DISCOVERER) {
|
||||
stacks.addAll(discoverer.apply(ctx));
|
||||
}
|
||||
return stacks;
|
||||
}
|
||||
|
||||
public static List<ItemStack> collectOperableSlots(CastingContext ctx) {
|
||||
List<ItemStack> stacks = Lists.newArrayList();
|
||||
for (var discoverer : OPERATIVE_SLOT_DISCOVERER) {
|
||||
stacks.addAll(discoverer.apply(ctx));
|
||||
}
|
||||
return stacks;
|
||||
}
|
||||
|
||||
public static ItemStack findDebugItem(Player player, String type) {
|
||||
for (var discoverer : DEBUG_DISCOVERER) {
|
||||
var stack = discoverer.apply(player, type);
|
||||
if (!stack.isEmpty()) {
|
||||
return stack;
|
||||
}
|
||||
}
|
||||
return ItemStack.EMPTY;
|
||||
}
|
||||
|
||||
public static void addLensPredicate(Predicate<Player> predicate) {
|
||||
HAS_LENS_PREDICATE.add(predicate);
|
||||
}
|
||||
|
||||
public static void addManaHolderDiscoverer(Function<CastingHarness, List<ManaHolder>> discoverer) {
|
||||
MANA_HOLDER_DISCOVERY.add(discoverer);
|
||||
}
|
||||
|
||||
public static void addGridScaleModifier(ToFloatFunction<Player> modifier) {
|
||||
GRID_SCALE_MODIFIERS.add(modifier);
|
||||
}
|
||||
|
||||
public static void addItemSlotDiscoverer(Function<CastingContext, List<ItemStack>> discoverer) {
|
||||
ITEM_SLOT_DISCOVERER.add(discoverer);
|
||||
}
|
||||
|
||||
public static void addOperativeSlotDiscoverer(Function<CastingContext, List<ItemStack>> discoverer) {
|
||||
OPERATIVE_SLOT_DISCOVERER.add(discoverer);
|
||||
}
|
||||
|
||||
public static void addDebugItemDiscoverer(BiFunction<Player, String, ItemStack> discoverer) {
|
||||
DEBUG_DISCOVERER.add(discoverer);
|
||||
}
|
||||
}
|
|
@ -43,6 +43,8 @@ public class HexConfig {
|
|||
boolean DEFAULT_INVERT_SPELLBOOK_SCROLL = false;
|
||||
boolean DEFAULT_INVERT_ABACUS_SCROLL = false;
|
||||
double DEFAULT_GRID_SNAP_THRESHOLD = 0.5;
|
||||
boolean DEFAULT_INVERT_SPELLBOOK_SCROLL = false;
|
||||
boolean DEFAULT_INVERT_ABACUS_SCROLL = false;
|
||||
}
|
||||
|
||||
public interface ServerConfigAccess {
|
||||
|
|
|
@ -12,6 +12,7 @@ public class HexItemTags {
|
|||
public static final TagKey<Item> EDIFIED_PLANKS = create("edified_planks");
|
||||
public static final TagKey<Item> STAVES = create("staves");
|
||||
public static final TagKey<Item> PHIAL_BASE = create("phial_base");
|
||||
public static final TagKey<Item> GRANTS_ROOT_ADVANCEMENT = create("grants_root_advancement");
|
||||
|
||||
public static TagKey<Item> create(String name) {
|
||||
return create(modLoc(name));
|
||||
|
|
|
@ -1,9 +1,15 @@
|
|||
package at.petrak.hexcasting.api.spell
|
||||
|
||||
import at.petrak.hexcasting.api.PatternRegistry
|
||||
import at.petrak.hexcasting.api.spell.casting.CastingContext
|
||||
import at.petrak.hexcasting.api.spell.casting.SpellContinuation
|
||||
import at.petrak.hexcasting.api.utils.asTranslatedComponent
|
||||
import at.petrak.hexcasting.api.utils.lightPurple
|
||||
import net.minecraft.network.chat.Component
|
||||
import net.minecraft.resources.ResourceLocation
|
||||
import at.petrak.hexcasting.api.spell.iota.Iota
|
||||
import net.minecraft.world.phys.Vec3
|
||||
import java.text.DecimalFormat
|
||||
|
||||
/**
|
||||
* Manipulates the stack in some way, usually by popping some number of values off the stack
|
||||
|
@ -46,6 +52,11 @@ interface Action {
|
|||
*/
|
||||
val causesBlindDiversion: Boolean get() = this is SpellAction
|
||||
|
||||
/**
|
||||
* The component for displaying this pattern's name. Override for dynamic patterns.
|
||||
*/
|
||||
val displayName: Component get() = "hexcasting.spell.${PatternRegistry.lookupPattern(this)}".asTranslatedComponent.lightPurple
|
||||
|
||||
companion object {
|
||||
// I see why vzakii did this: you can't raycast out to infinity!
|
||||
const val MAX_DISTANCE: Double = 32.0
|
||||
|
@ -63,6 +74,20 @@ interface Action {
|
|||
override fun execute(args: List<Iota>, ctx: CastingContext): List<Iota> =
|
||||
listOf(x)
|
||||
}
|
||||
|
||||
private val DOUBLE_FORMATTER = DecimalFormat("####.####")
|
||||
|
||||
@JvmStatic
|
||||
fun makeConstantOp(x: Double, key: ResourceLocation): Operator = object : ConstManaOperator {
|
||||
override val argc: Int
|
||||
get() = 0
|
||||
|
||||
override fun execute(args: List<SpellDatum<*>>, ctx: CastingContext): List<SpellDatum<*>> =
|
||||
x.asSpellResult
|
||||
|
||||
override val displayName: Component
|
||||
get() = "hexcasting.spell.$key".asTranslatedComponent(DOUBLE_FORMATTER.format(x)).lightPurple
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,288 @@
|
|||
package at.petrak.hexcasting.api.spell
|
||||
|
||||
import at.petrak.hexcasting.api.spell.casting.CastingContext
|
||||
import at.petrak.hexcasting.api.spell.math.HexPattern
|
||||
import at.petrak.hexcasting.api.spell.mishaps.MishapInvalidSpellDatumType
|
||||
import at.petrak.hexcasting.api.utils.*
|
||||
import net.minecraft.nbt.CompoundTag
|
||||
import net.minecraft.nbt.ListTag
|
||||
import net.minecraft.nbt.NbtUtils
|
||||
import net.minecraft.nbt.Tag
|
||||
import net.minecraft.network.chat.Component
|
||||
import net.minecraft.server.level.ServerLevel
|
||||
import net.minecraft.world.entity.Entity
|
||||
import net.minecraft.world.phys.Vec3
|
||||
import java.util.*
|
||||
|
||||
/**
|
||||
* Data allowed into a spell.
|
||||
*
|
||||
* We use the following types:
|
||||
* * [Entity]
|
||||
* * [Double]
|
||||
* * [Vec3] as both position and (when normalized) direction
|
||||
* * [Widget]; [Widget.NULL] is used as our null value
|
||||
* * [SpellList]
|
||||
* * [HexPattern]! Yes, we have meta-evaluation everyone.
|
||||
* The constructor guarantees we won't pass a type that isn't one of those types.
|
||||
*
|
||||
*
|
||||
*/
|
||||
class SpellDatum<T : Any> private constructor(val payload: T) {
|
||||
val clazz: Class<T> = payload.javaClass
|
||||
|
||||
fun serializeToNBT(): CompoundTag = this.serializeToNBTWithDepthCheck(0, 0)?.first
|
||||
?: NBTBuilder { TAG_WIDGET %= Widget.GARBAGE.name }
|
||||
|
||||
|
||||
// The second int returned is the number of datums contained in this one.
|
||||
fun serializeToNBTWithDepthCheck(depth: Int, total: Int): Pair<CompoundTag, Int>? {
|
||||
if (total > MAX_SERIALIZATION_TOTAL)
|
||||
return null
|
||||
|
||||
val tag = NBTBuilder {
|
||||
when (val pl = payload) {
|
||||
is SpellList -> {
|
||||
// handle it specially!
|
||||
if (depth + 1 > MAX_SERIALIZATION_DEPTH) {
|
||||
return null
|
||||
}
|
||||
|
||||
val outList = ListTag()
|
||||
var total1 = total + 1 // make mutable and include the list itself
|
||||
for (elt in pl) {
|
||||
val (t, subtotal) = elt.serializeToNBTWithDepthCheck(depth + 1, total1) ?: return null
|
||||
total1 = subtotal
|
||||
outList.add(t)
|
||||
}
|
||||
return Pair(
|
||||
NBTBuilder { TAG_LIST %= outList },
|
||||
total1
|
||||
)
|
||||
}
|
||||
is Entity -> TAG_ENTITY %= compound {
|
||||
TAG_ENTITY_UUID %= NbtUtils.createUUID(pl.uuid)
|
||||
TAG_ENTITY_NAME_CHEATY %= Component.Serializer.toJson(pl.displayName)
|
||||
}
|
||||
is Double -> TAG_DOUBLE %= pl
|
||||
is Vec3 -> TAG_VEC3 %= pl.serializeToNBT()
|
||||
is Widget -> TAG_WIDGET %= pl.name
|
||||
is HexPattern -> TAG_PATTERN %= pl.serializeToNBT()
|
||||
else -> throw RuntimeException("cannot serialize $pl because it is of type ${pl.javaClass.canonicalName} which is not serializable")
|
||||
}
|
||||
}
|
||||
return Pair(tag, total + 1)
|
||||
}
|
||||
|
||||
override fun toString(): String =
|
||||
buildString {
|
||||
append("SpellDatum[")
|
||||
append(this@SpellDatum.payload.toString())
|
||||
append(']')
|
||||
}
|
||||
|
||||
override fun equals(other: Any?): Boolean {
|
||||
return other is SpellDatum<*> && other.payload == payload
|
||||
}
|
||||
|
||||
override fun hashCode(): Int {
|
||||
return Objects.hash(clazz, this.payload)
|
||||
}
|
||||
|
||||
fun display(): Component {
|
||||
val nbt = this.serializeToNBT()
|
||||
return displayFromNBT(nbt)
|
||||
}
|
||||
|
||||
fun getType(): DatumType =
|
||||
when (this.payload) {
|
||||
is Entity -> DatumType.ENTITY
|
||||
is Widget -> DatumType.WIDGET
|
||||
is SpellList -> DatumType.LIST
|
||||
is HexPattern -> DatumType.PATTERN
|
||||
is Double -> DatumType.DOUBLE
|
||||
is Vec3 -> DatumType.VEC
|
||||
else -> DatumType.OTHER
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val MAX_SERIALIZATION_DEPTH = 256
|
||||
const val MAX_SERIALIZATION_TOTAL = 1024
|
||||
|
||||
@JvmStatic
|
||||
@Suppress("PLATFORM_CLASS_MAPPED_TO_KOTLIN")
|
||||
fun make(payload: Any): SpellDatum<*> =
|
||||
if (payload is SpellDatum<*>) {
|
||||
payload
|
||||
} else if (payload is List<*>) {
|
||||
SpellDatum(SpellList.LList(0, payload.map {
|
||||
when (it) {
|
||||
null -> make(Widget.NULL)
|
||||
else -> make(it)
|
||||
}
|
||||
}))
|
||||
} else if (payload is Vec3) {
|
||||
SpellDatum(
|
||||
Vec3(
|
||||
fixNAN(payload.x),
|
||||
fixNAN(payload.y),
|
||||
fixNAN(payload.z),
|
||||
)
|
||||
)
|
||||
} else if (isValidType(payload)) {
|
||||
SpellDatum(payload)
|
||||
} else if (payload is java.lang.Double) {
|
||||
// Check to see if it's a java *boxed* double, for when we call this from Java
|
||||
val num = payload.toDouble()
|
||||
SpellDatum(fixNAN(num))
|
||||
} else {
|
||||
throw MishapInvalidSpellDatumType(payload)
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun fromNBT(nbt: CompoundTag, world: ServerLevel): SpellDatum<*> {
|
||||
val keys = nbt.allKeys
|
||||
if (keys.size != 1)
|
||||
return SpellDatum(Widget.GARBAGE) // Invalid iota format
|
||||
|
||||
return when (val key = keys.iterator().next()) {
|
||||
TAG_ENTITY -> {
|
||||
val subtag = nbt.getCompound(key)
|
||||
val uuid = subtag.getUUID(TAG_ENTITY_UUID) // and throw away name
|
||||
val entity = world.getEntity(uuid)
|
||||
// If the entity died or something return Unit
|
||||
SpellDatum(if (entity == null || !entity.isAlive) Widget.NULL else entity)
|
||||
}
|
||||
TAG_DOUBLE -> SpellDatum(nbt.getDouble(key))
|
||||
TAG_VEC3 -> SpellDatum(vecFromNBT(nbt.getLongArray(key)))
|
||||
TAG_LIST -> {
|
||||
SpellDatum(SpellList.fromNBT(nbt.getList(key, Tag.TAG_COMPOUND), world))
|
||||
}
|
||||
TAG_WIDGET -> {
|
||||
SpellDatum(Widget.fromString(nbt.getString(key)))
|
||||
}
|
||||
TAG_PATTERN -> {
|
||||
SpellDatum(HexPattern.fromNBT(nbt.getCompound(TAG_PATTERN)))
|
||||
}
|
||||
else -> SpellDatum(Widget.GARBAGE) // Invalid iota type
|
||||
}
|
||||
}
|
||||
|
||||
@Deprecated(
|
||||
"use the [Level] overload", ReplaceWith(
|
||||
"DeserializeFromNBT(nbt, ctx.world)",
|
||||
"at.petrak.hexcasting.api.spell.SpellDatum.Companion.DeserializeFromNBT"
|
||||
)
|
||||
)
|
||||
@JvmStatic
|
||||
fun fromNBT(nbt: CompoundTag, ctx: CastingContext): SpellDatum<*> =
|
||||
fromNBT(nbt, ctx.world)
|
||||
|
||||
@JvmStatic
|
||||
fun displayFromNBT(nbt: CompoundTag): Component {
|
||||
val keys = nbt.allKeys
|
||||
val out = "".asTextComponent
|
||||
|
||||
if (keys.size != 1)
|
||||
out += "hexcasting.spelldata.unknown".asTranslatedComponent.white
|
||||
else {
|
||||
when (val key = keys.iterator().next()) {
|
||||
TAG_DOUBLE -> out += String.format(
|
||||
"%.4f",
|
||||
nbt.getDouble(TAG_DOUBLE)
|
||||
).green
|
||||
TAG_VEC3 -> {
|
||||
val vec = vecFromNBT(nbt.getLongArray(key))
|
||||
// the focus color is really more red, but we don't want to show an error-y color
|
||||
out += String.format(
|
||||
"(%.2f, %.2f, %.2f)",
|
||||
vec.x,
|
||||
vec.y,
|
||||
vec.z
|
||||
).lightPurple
|
||||
}
|
||||
TAG_LIST -> {
|
||||
out += "[".white
|
||||
|
||||
val arr = nbt.getList(key, Tag.TAG_COMPOUND)
|
||||
for ((i, subtag) in arr.withIndex()) {
|
||||
out += displayFromNBT(subtag.asCompound)
|
||||
if (i != arr.lastIndex) {
|
||||
out += ", ".white
|
||||
}
|
||||
}
|
||||
|
||||
out += "]".white
|
||||
}
|
||||
TAG_WIDGET -> {
|
||||
val widget = Widget.fromString(nbt.getString(key))
|
||||
|
||||
out += if (widget == Widget.GARBAGE)
|
||||
"arimfexendrapuse".darkGray.obfuscated
|
||||
else
|
||||
widget.toString().darkPurple
|
||||
}
|
||||
TAG_PATTERN -> {
|
||||
val pat = HexPattern.fromNBT(nbt.getCompound(TAG_PATTERN))
|
||||
var angleDesc = pat.anglesSignature()
|
||||
if (angleDesc.isNotBlank()) angleDesc = " $angleDesc"
|
||||
out += "HexPattern(".gold
|
||||
out += "${pat.startDir}$angleDesc".white
|
||||
out += ")".gold
|
||||
}
|
||||
TAG_ENTITY -> {
|
||||
val subtag = nbt.getCompound(TAG_ENTITY)
|
||||
val json = subtag.getString(TAG_ENTITY_NAME_CHEATY)
|
||||
// handle pre-0.5.0 foci not having the tag
|
||||
out += Component.Serializer.fromJson(json)?.aqua
|
||||
?: "hexcasting.spelldata.entity.whoknows".asTranslatedComponent.white
|
||||
}
|
||||
else -> {
|
||||
out += "hexcasting.spelldata.unknown".asTranslatedComponent.white
|
||||
}
|
||||
}
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
// Set of legal types to go in a spell
|
||||
val ValidTypes: Set<Class<*>> = setOf(
|
||||
Entity::class.java,
|
||||
Double::class.java,
|
||||
Vec3::class.java,
|
||||
SpellList::class.java,
|
||||
Widget::class.java,
|
||||
HexPattern::class.java,
|
||||
)
|
||||
|
||||
const val TAG_ENTITY = "entity"
|
||||
const val TAG_DOUBLE = "double"
|
||||
const val TAG_VEC3 = "vec3"
|
||||
const val TAG_LIST = "list"
|
||||
const val TAG_WIDGET = "widget"
|
||||
const val TAG_PATTERN = "pattern"
|
||||
|
||||
const val TAG_ENTITY_UUID = "uuid"
|
||||
|
||||
// Also encode the entity's name as a component for the benefit of the client
|
||||
const val TAG_ENTITY_NAME_CHEATY = "name"
|
||||
|
||||
fun <T : Any> isValidType(checkee: T): Boolean =
|
||||
ValidTypes.any { clazz -> clazz.isAssignableFrom(checkee.javaClass) }
|
||||
|
||||
@JvmStatic
|
||||
fun tagForType(datumType: DatumType): String {
|
||||
return when (datumType) {
|
||||
DatumType.ENTITY -> TAG_ENTITY
|
||||
DatumType.WIDGET -> TAG_WIDGET
|
||||
DatumType.LIST -> TAG_LIST
|
||||
DatumType.PATTERN -> TAG_PATTERN
|
||||
DatumType.DOUBLE -> TAG_DOUBLE
|
||||
DatumType.VEC -> TAG_VEC3
|
||||
DatumType.OTHER, DatumType.EMPTY -> ""
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,28 @@
|
|||
package at.petrak.hexcasting.api.spell
|
||||
|
||||
import at.petrak.hexcasting.api.spell.casting.CastingContext
|
||||
import at.petrak.hexcasting.api.utils.getSafe
|
||||
|
||||
/**
|
||||
* Miscellaneous spell datums used as markers, etc.
|
||||
*
|
||||
* They act as operators that push themselves.
|
||||
*/
|
||||
enum class Widget : ConstManaOperator {
|
||||
NULL,
|
||||
OPEN_PAREN, CLOSE_PAREN, ESCAPE,
|
||||
GARBAGE;
|
||||
|
||||
override val argc: Int
|
||||
get() = 0
|
||||
|
||||
override fun execute(args: List<SpellDatum<*>>, ctx: CastingContext): List<SpellDatum<*>> =
|
||||
this.asSpellResult
|
||||
|
||||
companion object {
|
||||
@JvmStatic
|
||||
fun fromString(key: String): Widget {
|
||||
return values().getSafe(key, GARBAGE)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,12 +1,14 @@
|
|||
package at.petrak.hexcasting.api.spell.casting
|
||||
|
||||
import at.petrak.hexcasting.api.HexAPI.modLoc
|
||||
import at.petrak.hexcasting.api.misc.DiscoveryHandlers
|
||||
import at.petrak.hexcasting.api.mod.HexConfig
|
||||
import at.petrak.hexcasting.api.spell.Action
|
||||
import at.petrak.hexcasting.api.spell.mishaps.MishapEntityTooFarAway
|
||||
import at.petrak.hexcasting.api.spell.mishaps.MishapEvalTooDeep
|
||||
import at.petrak.hexcasting.api.spell.mishaps.MishapLocationTooFarAway
|
||||
import at.petrak.hexcasting.api.utils.otherHand
|
||||
import at.petrak.hexcasting.common.items.magic.ItemCreativeUnlocker
|
||||
import at.petrak.hexcasting.xplat.IXplatAbstractions
|
||||
import net.minecraft.core.BlockPos
|
||||
import net.minecraft.server.level.ServerLevel
|
||||
|
@ -14,8 +16,8 @@ import net.minecraft.server.level.ServerPlayer
|
|||
import net.minecraft.world.InteractionHand
|
||||
import net.minecraft.world.entity.Entity
|
||||
import net.minecraft.world.entity.player.Player
|
||||
import net.minecraft.world.item.Item
|
||||
import net.minecraft.world.item.ItemStack
|
||||
import net.minecraft.world.level.GameType
|
||||
import net.minecraft.world.phys.Vec3
|
||||
import java.util.function.Predicate
|
||||
import kotlin.math.min
|
||||
|
@ -39,10 +41,14 @@ data class CastingContext(
|
|||
private val entitiesGivenMotion = mutableSetOf<Entity>()
|
||||
|
||||
inline fun getHeldItemToOperateOn(acceptItemIf: (ItemStack) -> Boolean): Pair<ItemStack, InteractionHand> {
|
||||
val handItem = caster.getItemInHand(castingHand)
|
||||
if (!acceptItemIf(handItem))
|
||||
return caster.getItemInHand(otherHand) to otherHand
|
||||
return handItem to castingHand
|
||||
val handItem = caster.getItemInHand(otherHand)
|
||||
if (!acceptItemIf(handItem)) {
|
||||
val castingItem = caster.getItemInHand(castingHand)
|
||||
if (acceptItemIf(castingItem)) {
|
||||
return castingItem to castingHand
|
||||
}
|
||||
}
|
||||
return handItem to otherHand
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -87,7 +93,8 @@ data class CastingContext(
|
|||
return entitiesGivenMotion.contains(target)
|
||||
}
|
||||
|
||||
fun isVecInWorld(vec: Vec3) = world.isInWorldBounds(BlockPos(vec)) && world.worldBorder.isWithinBounds(vec.x, vec.z, 0.5)
|
||||
fun isVecInWorld(vec: Vec3) =
|
||||
world.isInWorldBounds(BlockPos(vec)) && world.worldBorder.isWithinBounds(vec.x, vec.z, 0.5)
|
||||
|
||||
fun isVecInRange(vec: Vec3): Boolean {
|
||||
val sentinel = IXplatAbstractions.INSTANCE.getSentinel(caster)
|
||||
|
@ -102,15 +109,12 @@ data class CastingContext(
|
|||
if (this.spellCircle != null) {
|
||||
// we use the eye position cause thats where the caster gets their "position" from
|
||||
val range = this.caster.bbHeight
|
||||
if (this.spellCircle.activatorAlwaysInRange && vec.distanceToSqr(this.caster.eyePosition) < range * range)
|
||||
if (this.spellCircle.activatorAlwaysInRange && vec.distanceToSqr(this.caster.eyePosition) <= range * range)
|
||||
return true
|
||||
return this.spellCircle.aabb.contains(vec)
|
||||
}
|
||||
|
||||
if (vec.distanceToSqr(this.caster.position()) < Action.MAX_DISTANCE * Action.MAX_DISTANCE)
|
||||
return true
|
||||
|
||||
return false
|
||||
return vec.distanceToSqr(this.caster.eyePosition) <= Operator.MAX_DISTANCE * Operator.MAX_DISTANCE
|
||||
}
|
||||
|
||||
fun isEntityInWorld(entity: Entity) = isVecInWorld(entity.position())
|
||||
|
@ -121,6 +125,12 @@ data class CastingContext(
|
|||
return isVecInRange(entity.position())
|
||||
}
|
||||
|
||||
fun canEditBlockAt(pos: BlockPos): Boolean {
|
||||
return this.isVecInRange(Vec3.atCenterOf(pos))
|
||||
&& this.caster.gameMode.gameModeForPlayer != GameType.ADVENTURE
|
||||
&& this.world.mayInteract(this.caster, pos)
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the slot from which to take blocks and items.
|
||||
*/
|
||||
|
@ -129,25 +139,12 @@ data class CastingContext(
|
|||
// for what purpose i cannot imagine
|
||||
// http://redditpublic.com/images/b/b2/Items_slot_number.png looks right
|
||||
// and offhand is 150 Inventory.java:464
|
||||
fun getOperativeSlot(stackOK: Predicate<ItemStack>): Int? {
|
||||
val otherHandStack = this.caster.getItemInHand(this.otherHand)
|
||||
if (stackOK.test(otherHandStack)) {
|
||||
return when (this.otherHand) {
|
||||
InteractionHand.MAIN_HAND -> this.caster.inventory.selected
|
||||
InteractionHand.OFF_HAND -> 150
|
||||
}
|
||||
}
|
||||
val anchorSlot = when (this.castingHand) {
|
||||
// slot to the right of the wand
|
||||
InteractionHand.MAIN_HAND -> (this.caster.inventory.selected + 1) % 9
|
||||
// first hotbar slot
|
||||
InteractionHand.OFF_HAND -> 0
|
||||
}
|
||||
for (delta in 0 until 9) {
|
||||
val slot = (anchorSlot + delta) % 9
|
||||
val stack = this.caster.inventory.getItem(slot)
|
||||
fun getOperativeSlot(stackOK: Predicate<ItemStack>): ItemStack? {
|
||||
val operable = DiscoveryHandlers.collectOperableSlots(this)
|
||||
|
||||
for (stack in operable) {
|
||||
if (stackOK.test(stack)) {
|
||||
return slot
|
||||
return stack
|
||||
}
|
||||
}
|
||||
return null
|
||||
|
@ -158,17 +155,16 @@ data class CastingContext(
|
|||
* Return whether the withdrawal was successful.
|
||||
*/
|
||||
// https://github.com/VazkiiMods/Psi/blob/master/src/main/java/vazkii/psi/common/spell/trick/block/PieceTrickPlaceBlock.java#L143
|
||||
fun withdrawItem(item: Item, count: Int, actuallyRemove: Boolean): Boolean {
|
||||
fun withdrawItem(item: ItemStack, count: Int, actuallyRemove: Boolean): Boolean {
|
||||
if (this.caster.isCreative) return true
|
||||
|
||||
val inv = this.caster.inventory
|
||||
val operativeItem = item.copy()
|
||||
|
||||
// TODO: withdraw from ender chest given a specific ender charm?
|
||||
val stacksToExamine = inv.items.toMutableList().apply { removeAt(inv.selected) }.asReversed().toMutableList()
|
||||
stacksToExamine.addAll(inv.offhand)
|
||||
stacksToExamine.add(inv.getSelected())
|
||||
val stacksToExamine = DiscoveryHandlers.collectItemSlots(this)
|
||||
|
||||
fun matches(stack: ItemStack): Boolean =
|
||||
!stack.isEmpty && stack.`is`(item)
|
||||
!stack.isEmpty && ItemStack.isSameItemSameTags(operativeItem, stack)
|
||||
|
||||
val presentCount = stacksToExamine.fold(0) { acc, stack ->
|
||||
acc + if (matches(stack)) stack.count else 0
|
||||
|
@ -210,4 +206,32 @@ data class CastingContext(
|
|||
val advs = this.caster.advancements
|
||||
return advs.getOrStartProgress(adv!!).isDone
|
||||
}
|
||||
|
||||
val debugPatterns: Boolean by lazy {
|
||||
!DiscoveryHandlers.findDebugItem(this.caster, ItemCreativeUnlocker.DISPLAY_PATTERNS).isEmpty
|
||||
}
|
||||
|
||||
companion object {
|
||||
init {
|
||||
DiscoveryHandlers.addItemSlotDiscoverer {
|
||||
val inv = it.caster.inventory
|
||||
inv.items.toMutableList().apply { removeAt(inv.selected) }.asReversed().toMutableList().apply {
|
||||
addAll(inv.offhand)
|
||||
add(inv.getSelected())
|
||||
}
|
||||
}
|
||||
|
||||
DiscoveryHandlers.addOperativeSlotDiscoverer {
|
||||
val slots = mutableListOf<ItemStack>()
|
||||
val anchorSlot = if (it.castingHand == InteractionHand.MAIN_HAND) (it.caster.inventory.selected + 1) % 9 else 0
|
||||
|
||||
slots.add(it.caster.getItemInHand(it.otherHand))
|
||||
for (delta in 0 until 9) {
|
||||
val slot = (anchorSlot + delta) % 9
|
||||
slots.add(it.caster.inventory.getItem(slot))
|
||||
}
|
||||
slots
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,6 +3,7 @@ package at.petrak.hexcasting.api.spell.casting
|
|||
import at.petrak.hexcasting.api.PatternRegistry
|
||||
import at.petrak.hexcasting.api.advancements.HexAdvancementTriggers
|
||||
import at.petrak.hexcasting.api.block.circle.BlockEntityAbstractImpetus
|
||||
import at.petrak.hexcasting.api.misc.DiscoveryHandlers
|
||||
import at.petrak.hexcasting.api.misc.FrozenColorizer
|
||||
import at.petrak.hexcasting.api.misc.HexDamageSources
|
||||
import at.petrak.hexcasting.api.mod.HexConfig
|
||||
|
@ -18,9 +19,8 @@ import at.petrak.hexcasting.api.spell.math.HexDir
|
|||
import at.petrak.hexcasting.api.spell.math.HexPattern
|
||||
import at.petrak.hexcasting.api.spell.mishaps.*
|
||||
import at.petrak.hexcasting.api.utils.*
|
||||
import at.petrak.hexcasting.common.items.magic.ItemCreativeUnlocker
|
||||
import at.petrak.hexcasting.common.lib.HexIotaTypes
|
||||
import at.petrak.hexcasting.xplat.IXplatAbstractions
|
||||
import net.minecraft.Util
|
||||
import net.minecraft.nbt.CompoundTag
|
||||
import net.minecraft.nbt.Tag
|
||||
import net.minecraft.resources.ResourceLocation
|
||||
|
@ -53,6 +53,30 @@ class CastingHarness private constructor(
|
|||
*/
|
||||
fun executeIota(iota: Iota, world: ServerLevel): ControllerInfo = executeIotas(listOf(iota), world)
|
||||
|
||||
private fun displayPattern(pattern: Operator?, iota: SpellDatum<*>) {
|
||||
if (this.ctx.debugPatterns) {
|
||||
this.ctx.caster.sendMessage(pattern?.displayName ?: iota.display(), Util.NIL_UUID)
|
||||
}
|
||||
}
|
||||
|
||||
private fun getOperatorForPattern(iota: SpellDatum<*>, world: ServerLevel): Operator? {
|
||||
if (iota.getType() == DatumType.PATTERN)
|
||||
return PatternRegistry.matchPattern(iota.payload as HexPattern, world)
|
||||
return null
|
||||
}
|
||||
|
||||
private fun getPatternForFrame(frame: ContinuationFrame): HexPattern? {
|
||||
if (frame !is ContinuationFrame.Evaluate) return null
|
||||
|
||||
return frame.list.car.payload as? HexPattern
|
||||
}
|
||||
|
||||
private fun getOperatorForFrame(frame: ContinuationFrame, world: ServerLevel): Operator? {
|
||||
if (frame !is ContinuationFrame.Evaluate) return null
|
||||
|
||||
return getOperatorForPattern(frame.list.car, world)
|
||||
}
|
||||
|
||||
/**
|
||||
* Given a list of iotas, execute them in sequence.
|
||||
*/
|
||||
|
@ -66,7 +90,23 @@ class CastingHarness private constructor(
|
|||
// Take the top of the continuation stack...
|
||||
val next = continuation.frame
|
||||
// ...and execute it.
|
||||
val result = next.evaluate(continuation.next, world, this)
|
||||
val result = try {
|
||||
next.evaluate(continuation.next, world, this)
|
||||
} catch (mishap: Mishap) {
|
||||
val pattern = getPatternForFrame(next)
|
||||
val operator = getOperatorForFrame(next, world)
|
||||
CastResult(
|
||||
continuation,
|
||||
null,
|
||||
mishap.resolutionType(ctx),
|
||||
listOf(
|
||||
OperatorSideEffect.DoMishap(
|
||||
mishap,
|
||||
Mishap.Context(pattern ?: HexPattern(HexDir.WEST), operator)
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
// Then write all pertinent data back to the harness for the next iteration.
|
||||
if (result.newData != null) {
|
||||
this.applyFunctionalData(result.newData)
|
||||
|
@ -124,7 +164,7 @@ class CastingHarness private constructor(
|
|||
listOf(
|
||||
OperatorSideEffect.DoMishap(
|
||||
mishap,
|
||||
Mishap.Context((iota as? PatternIota)?.pattern ?: HexPattern(HexDir.WEST), null)
|
||||
Mishap.Context((iota as? PatternIota)?.pattern ?: HexPattern(HexDir.WEST), getOperatorForPattern(iota, world))
|
||||
)
|
||||
),
|
||||
)
|
||||
|
@ -137,7 +177,7 @@ class CastingHarness private constructor(
|
|||
listOf(
|
||||
OperatorSideEffect.DoMishap(
|
||||
MishapError(exception),
|
||||
Mishap.Context((iota as? PatternIota)?.pattern ?: HexPattern(HexDir.WEST), null)
|
||||
Mishap.Context((iota as? PatternIota)?.pattern ?: HexPattern(HexDir.WEST), getOperatorForPattern(iota, world))
|
||||
)
|
||||
)
|
||||
)
|
||||
|
@ -174,6 +214,7 @@ class CastingHarness private constructor(
|
|||
var ravenmindChanged = false
|
||||
|
||||
if (!unenlightened || pattern.alwaysProcessGreatSpell) {
|
||||
displayPattern(pattern, SpellDatum.make(newPat))
|
||||
val result = pattern.operate(
|
||||
continuation,
|
||||
this.stack.toMutableList(),
|
||||
|
@ -226,7 +267,7 @@ class CastingHarness private constructor(
|
|||
continuation,
|
||||
null,
|
||||
mishap.resolutionType(ctx),
|
||||
listOf(OperatorSideEffect.DoMishap(mishap, Mishap.Context(newPat, actionIdPair?.second))),
|
||||
listOf(OperatorSideEffect.DoMishap(mishap, Mishap.Context(newPat, actionIdPair?.first))),
|
||||
)
|
||||
} catch (exception: Exception) {
|
||||
exception.printStackTrace()
|
||||
|
@ -237,7 +278,7 @@ class CastingHarness private constructor(
|
|||
listOf(
|
||||
OperatorSideEffect.DoMishap(
|
||||
MishapError(exception),
|
||||
Mishap.Context(newPat, actionIdPair?.second)
|
||||
Mishap.Context(newPat, actionIdPair?.first)
|
||||
)
|
||||
)
|
||||
)
|
||||
|
@ -302,7 +343,7 @@ class CastingHarness private constructor(
|
|||
}
|
||||
val sig = iota.pattern.anglesSignature()
|
||||
|
||||
if (this.parenCount > 0) {
|
||||
val out = if (this.parenCount > 0) {
|
||||
if (this.escapeNext) {
|
||||
val newParens = this.parenthesized.toMutableList()
|
||||
newParens.add(iota)
|
||||
|
@ -387,6 +428,11 @@ class CastingHarness private constructor(
|
|||
null
|
||||
}
|
||||
}
|
||||
|
||||
if (out != null) {
|
||||
displayPattern(operator, iota)
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -422,21 +468,21 @@ class CastingHarness private constructor(
|
|||
val casterHexHolder = IXplatAbstractions.INSTANCE.findHexHolder(casterStack)
|
||||
val hexHolderDrawsFromInventory = if (casterHexHolder != null) {
|
||||
if (casterManaHolder != null) {
|
||||
val manaAvailable = casterManaHolder.media
|
||||
val manaAvailable = casterManaHolder.withdrawMana(-1, true)
|
||||
val manaToTake = min(costLeft, manaAvailable)
|
||||
if (!fake) casterManaHolder.media = manaAvailable - manaToTake
|
||||
if (!fake) casterManaHolder.withdrawMana(manaToTake, false)
|
||||
costLeft -= manaToTake
|
||||
}
|
||||
casterHexHolder.canDrawManaFromInventory()
|
||||
} else {
|
||||
false
|
||||
}
|
||||
if (casterStack.`is`(HexItemTags.STAVES) || hexHolderDrawsFromInventory) {
|
||||
val manableItems = this.ctx.caster.inventory.items
|
||||
.filter(::isManaItem)
|
||||
|
||||
if (casterStack.`is`(HexItemTags.WANDS) || hexHolderDrawsFromInventory) {
|
||||
val manaSources = DiscoveryHandlers.collectManaHolders(this)
|
||||
.sortedWith(Comparator(::compareManaItem).reversed())
|
||||
for (stack in manableItems) {
|
||||
costLeft -= extractMana(stack, costLeft, simulate = fake && !ItemCreativeUnlocker.isDebug(stack))
|
||||
for (source in manaSources) {
|
||||
costLeft -= extractMana(source, costLeft, simulate = fake)
|
||||
if (costLeft <= 0)
|
||||
break
|
||||
}
|
||||
|
@ -448,13 +494,17 @@ class CastingHarness private constructor(
|
|||
val manaAbleToCastFromHP = this.ctx.caster.health * manaToHealth
|
||||
|
||||
val manaToActuallyPayFor = min(manaAbleToCastFromHP.toInt(), costLeft)
|
||||
if (!fake) {
|
||||
HexAdvancementTriggers.OVERCAST_TRIGGER.trigger(this.ctx.caster, manaToActuallyPayFor)
|
||||
this.ctx.caster.awardStat(HexStatistics.MANA_OVERCASTED, manaCost - costLeft)
|
||||
|
||||
costLeft -= if (!fake) {
|
||||
Mishap.trulyHurt(this.ctx.caster, HexDamageSources.OVERCAST, healthtoRemove.toFloat())
|
||||
|
||||
val actuallyTaken = (manaAbleToCastFromHP - (this.ctx.caster.health * manaToHealth)).toInt()
|
||||
|
||||
HexAdvancementTriggers.OVERCAST_TRIGGER.trigger(this.ctx.caster, actuallyTaken)
|
||||
this.ctx.caster.awardStat(HexStatistics.MANA_OVERCASTED, manaCost - costLeft)
|
||||
actuallyTaken
|
||||
} else {
|
||||
manaToActuallyPayFor
|
||||
}
|
||||
costLeft -= manaToActuallyPayFor
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -503,6 +553,24 @@ class CastingHarness private constructor(
|
|||
const val TAG_ESCAPE_NEXT = "escape_next"
|
||||
const val TAG_PREPACKAGED_COLORIZER = "prepackaged_colorizer"
|
||||
|
||||
init {
|
||||
DiscoveryHandlers.addManaHolderDiscoverer {
|
||||
it.ctx.caster.inventory.items
|
||||
.filter(::isManaItem)
|
||||
.mapNotNull(IXplatAbstractions.INSTANCE::findManaHolder)
|
||||
}
|
||||
DiscoveryHandlers.addManaHolderDiscoverer {
|
||||
it.ctx.caster.inventory.armor
|
||||
.filter(::isManaItem)
|
||||
.mapNotNull(IXplatAbstractions.INSTANCE::findManaHolder)
|
||||
}
|
||||
DiscoveryHandlers.addManaHolderDiscoverer {
|
||||
it.ctx.caster.inventory.offhand
|
||||
.filter(::isManaItem)
|
||||
.mapNotNull(IXplatAbstractions.INSTANCE::findManaHolder)
|
||||
}
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun fromNBT(nbt: CompoundTag, ctx: CastingContext): CastingHarness {
|
||||
return try {
|
||||
|
|
|
@ -149,7 +149,9 @@ sealed interface ContinuationFrame {
|
|||
|
||||
// If we still have data to process...
|
||||
val (stackTop, newCont) = if (data.nonEmpty) {
|
||||
// Push the next datum to the top of the stack,
|
||||
// Increment the evaluation depth,
|
||||
harness.ctx.incDepth()
|
||||
// push the next datum to the top of the stack,
|
||||
data.car to continuation
|
||||
// put the next Thoth object back on the stack for the next Thoth cycle,
|
||||
.pushFrame(ForEach(data.cdr, code, stack, acc))
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
package at.petrak.hexcasting.api.spell.casting
|
||||
|
||||
import java.util.*
|
||||
import at.petrak.hexcasting.api.utils.getSafe
|
||||
|
||||
enum class ResolvedPatternType(val color: Int, val fadeColor: Int, val success: Boolean) {
|
||||
UNRESOLVED(0x7f7f7f, 0xcccccc, false),
|
||||
|
@ -12,8 +12,7 @@ enum class ResolvedPatternType(val color: Int, val fadeColor: Int, val success:
|
|||
companion object {
|
||||
@JvmStatic
|
||||
fun fromString(key: String): ResolvedPatternType {
|
||||
val lowercaseKey = key.lowercase(Locale.ROOT)
|
||||
return values().firstOrNull { it.name.lowercase(Locale.ROOT) == lowercaseKey } ?: UNRESOLVED
|
||||
return values().getSafe(key)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
package at.petrak.hexcasting.api.spell.math
|
||||
|
||||
import at.petrak.hexcasting.api.HexAPI
|
||||
import java.util.*
|
||||
import kotlin.random.Random
|
||||
|
||||
|
@ -8,11 +9,31 @@ object EulerPathFinder {
|
|||
* Find an alternative way to draw the given pattern, based on a random seed.
|
||||
*/
|
||||
@JvmStatic
|
||||
fun findAltDrawing(original: HexPattern, seed: Long): HexPattern {
|
||||
@JvmOverloads
|
||||
fun findAltDrawing(original: HexPattern, seed: Long, rule: (HexPattern) -> Boolean = { true }): HexPattern {
|
||||
// http://www.graph-magics.com/articles/euler.php
|
||||
|
||||
val rand = Random(seed)
|
||||
|
||||
// Don't try for too long, in case all paths are exhausted.
|
||||
// Unlikely to ever actually reach this limit, and can only happen if the same pattern
|
||||
// is registered both as a Great Pattern and as a special handler or regular pattern,
|
||||
// or if the random seed has some sort of strange repeating property to it.
|
||||
var iterationsLeft = 100
|
||||
var path: HexPattern
|
||||
while (iterationsLeft > 0) {
|
||||
iterationsLeft--
|
||||
path = walkPath(original, rand)
|
||||
if (rule(path)) {
|
||||
return path
|
||||
}
|
||||
}
|
||||
|
||||
HexAPI.LOGGER.warn("Didn't find alternate path for {} in time", original)
|
||||
return original
|
||||
}
|
||||
|
||||
private fun walkPath(original: HexPattern, rand: Random): HexPattern {
|
||||
val graph = toGraph(original)
|
||||
val oddNodes = graph.filter { (_, dirs) -> dirs.size % 2 == 1 }
|
||||
var current: HexCoord = when (oddNodes.size) {
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
package at.petrak.hexcasting.api.spell.math
|
||||
|
||||
import java.util.*
|
||||
import at.petrak.hexcasting.api.utils.getSafe
|
||||
|
||||
enum class HexDir {
|
||||
NORTH_EAST, EAST, SOUTH_EAST, SOUTH_WEST, WEST, NORTH_WEST;
|
||||
|
@ -28,8 +28,7 @@ enum class HexDir {
|
|||
companion object {
|
||||
@JvmStatic
|
||||
fun fromString(key: String): HexDir {
|
||||
val lowercaseKey = key.lowercase(Locale.ROOT)
|
||||
return values().firstOrNull { it.name.lowercase(Locale.ROOT) == lowercaseKey } ?: WEST
|
||||
return values().getSafe(key, WEST)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,6 +3,7 @@ package at.petrak.hexcasting.api.spell.math
|
|||
import at.petrak.hexcasting.api.utils.NBTBuilder
|
||||
import at.petrak.hexcasting.api.utils.coordToPx
|
||||
import at.petrak.hexcasting.api.utils.findCenter
|
||||
import at.petrak.hexcasting.api.utils.getSafe
|
||||
import net.minecraft.nbt.CompoundTag
|
||||
import net.minecraft.nbt.Tag
|
||||
import net.minecraft.world.phys.Vec2
|
||||
|
@ -136,8 +137,8 @@ data class HexPattern(public val startDir: HexDir, public val angles: MutableLis
|
|||
|
||||
@JvmStatic
|
||||
fun fromNBT(tag: CompoundTag): HexPattern {
|
||||
val startDir = HexDir.values()[tag.getByte(TAG_START_DIR).toInt()]
|
||||
val angles = tag.getByteArray(TAG_ANGLES).map { HexAngle.values()[it.toInt()] }
|
||||
val startDir = HexDir.values().getSafe(tag.getByte(TAG_START_DIR))
|
||||
val angles = tag.getByteArray(TAG_ANGLES).map(HexAngle.values()::getSafe)
|
||||
return HexPattern(startDir, angles.toMutableList())
|
||||
}
|
||||
|
||||
|
|
|
@ -2,6 +2,7 @@ package at.petrak.hexcasting.api.spell.mishaps
|
|||
|
||||
import at.petrak.hexcasting.api.misc.FrozenColorizer
|
||||
import at.petrak.hexcasting.api.mod.HexItemTags
|
||||
import at.petrak.hexcasting.api.spell.Operator
|
||||
import at.petrak.hexcasting.api.spell.ParticleSpray
|
||||
import at.petrak.hexcasting.api.spell.casting.CastingContext
|
||||
import at.petrak.hexcasting.api.spell.casting.ResolvedPatternType
|
||||
|
@ -10,11 +11,11 @@ import at.petrak.hexcasting.api.spell.math.HexPattern
|
|||
import at.petrak.hexcasting.api.utils.asTranslatedComponent
|
||||
import at.petrak.hexcasting.api.utils.lightPurple
|
||||
import at.petrak.hexcasting.common.lib.HexItems
|
||||
import at.petrak.hexcasting.ktxt.lastHurt
|
||||
import at.petrak.hexcasting.ktxt.*
|
||||
import at.petrak.hexcasting.xplat.IXplatAbstractions
|
||||
import net.minecraft.Util
|
||||
import net.minecraft.core.BlockPos
|
||||
import net.minecraft.network.chat.Component
|
||||
import net.minecraft.resources.ResourceLocation
|
||||
import net.minecraft.world.InteractionHand
|
||||
import net.minecraft.world.damagesource.DamageSource
|
||||
import net.minecraft.world.entity.LivingEntity
|
||||
|
@ -53,8 +54,8 @@ abstract class Mishap : Throwable() {
|
|||
protected fun error(stub: String, vararg args: Any): Component =
|
||||
"hexcasting.mishap.$stub".asTranslatedComponent(*args)
|
||||
|
||||
protected fun actionName(action: ResourceLocation?): Component =
|
||||
"hexcasting.spell.${action ?: "unknown"}".asTranslatedComponent.lightPurple
|
||||
protected fun actionName(action: Operator?): Component =
|
||||
action?.displayName ?: "hexcasting.spell.null".asTranslatedComponent.lightPurple
|
||||
|
||||
protected fun yeetHeldItemsTowards(ctx: CastingContext, targetPos: Vec3) {
|
||||
// Knock the player's items out of their hands
|
||||
|
@ -75,6 +76,8 @@ abstract class Mishap : Throwable() {
|
|||
|
||||
protected fun yeetHeldItem(ctx: CastingContext, hand: InteractionHand) {
|
||||
val item = ctx.caster.getItemInHand(hand).copy()
|
||||
if (hand == ctx.castingHand && IXplatAbstractions.INSTANCE.findHexHolder(item) != null)
|
||||
return
|
||||
ctx.caster.setItemInHand(hand, ItemStack.EMPTY)
|
||||
|
||||
val delta = ctx.caster.lookAngle.scale(0.5)
|
||||
|
@ -98,10 +101,11 @@ abstract class Mishap : Throwable() {
|
|||
return ctx.world.getBlockState(pos).block.name
|
||||
}
|
||||
|
||||
data class Context(val pattern: HexPattern, val action: ResourceLocation?)
|
||||
data class Context(val pattern: HexPattern, val action: Operator?)
|
||||
|
||||
companion object {
|
||||
fun trulyHurt(entity: LivingEntity, source: DamageSource, amount: Float) {
|
||||
val targetHealth = entity.health - amount
|
||||
if (entity.invulnerableTime > 10) {
|
||||
val lastHurt = entity.lastHurt
|
||||
if (lastHurt < amount)
|
||||
|
@ -109,7 +113,29 @@ abstract class Mishap : Throwable() {
|
|||
else
|
||||
entity.lastHurt -= amount
|
||||
}
|
||||
entity.hurt(source, amount)
|
||||
if (!entity.hurt(source, amount) &&
|
||||
!entity.isInvulnerableTo(source) &&
|
||||
!entity.level.isClientSide &&
|
||||
!entity.isDeadOrDying) {
|
||||
|
||||
// Ok, if you REALLY don't want to play nice...
|
||||
entity.health = targetHealth
|
||||
entity.markHurt()
|
||||
|
||||
if (entity.isDeadOrDying) {
|
||||
if (!entity.checkTotemDeathProtection(source)) {
|
||||
val sound = entity.deathSoundAccessor
|
||||
if (sound != null) {
|
||||
entity.playSound(sound, entity.soundVolumeAccessor, entity.voicePitch)
|
||||
}
|
||||
entity.die(source)
|
||||
}
|
||||
} else {
|
||||
entity.playHurtSound(source)
|
||||
}
|
||||
|
||||
entity.setHurtWithStamp(source, entity.level.gameTime)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,33 @@
|
|||
package at.petrak.hexcasting.api.spell.mishaps
|
||||
|
||||
import at.petrak.hexcasting.api.misc.FrozenColorizer
|
||||
import at.petrak.hexcasting.api.spell.SpellDatum
|
||||
import at.petrak.hexcasting.api.spell.casting.CastingContext
|
||||
import at.petrak.hexcasting.api.utils.aqua
|
||||
import at.petrak.hexcasting.api.utils.asTranslatedComponent
|
||||
import net.minecraft.network.chat.Component
|
||||
import net.minecraft.world.entity.Entity
|
||||
import net.minecraft.world.entity.item.ItemEntity
|
||||
import net.minecraft.world.item.DyeColor
|
||||
|
||||
class MishapBadEntity(val entity: Entity, val wanted: Component) : Mishap() {
|
||||
override fun accentColor(ctx: CastingContext, errorCtx: Context): FrozenColorizer =
|
||||
dyeColor(DyeColor.BROWN)
|
||||
|
||||
override fun execute(ctx: CastingContext, errorCtx: Context, stack: MutableList<SpellDatum<*>>) {
|
||||
yeetHeldItemsTowards(ctx, entity.position())
|
||||
}
|
||||
|
||||
override fun errorMessage(ctx: CastingContext, errorCtx: Context) =
|
||||
error("bad_entity", actionName(errorCtx.action), wanted, entity.displayName.plainCopy().aqua)
|
||||
|
||||
companion object {
|
||||
@JvmStatic
|
||||
fun of(entity: Entity, stub: String): Mishap {
|
||||
val component = "hexcasting.mishap.bad_item.$stub".asTranslatedComponent
|
||||
if (entity is ItemEntity)
|
||||
return MishapBadItem(entity, component)
|
||||
return MishapBadEntity(entity, component)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -3,6 +3,7 @@ package at.petrak.hexcasting.api.spell.mishaps
|
|||
import at.petrak.hexcasting.api.misc.FrozenColorizer
|
||||
import at.petrak.hexcasting.api.spell.iota.Iota
|
||||
import at.petrak.hexcasting.api.spell.casting.CastingContext
|
||||
import at.petrak.hexcasting.api.utils.aqua
|
||||
import net.minecraft.world.entity.Entity
|
||||
import net.minecraft.world.item.DyeColor
|
||||
|
||||
|
@ -15,5 +16,5 @@ class MishapImmuneEntity(val entity: Entity) : Mishap() {
|
|||
}
|
||||
|
||||
override fun errorMessage(ctx: CastingContext, errorCtx: Context) =
|
||||
error("immune_entity", actionName(errorCtx.action), entity.displayName)
|
||||
error("immune_entity", actionName(errorCtx.action), entity.displayName.plainCopy().aqua)
|
||||
}
|
||||
|
|
|
@ -3,14 +3,31 @@ package at.petrak.hexcasting.api.spell.mishaps
|
|||
import at.petrak.hexcasting.api.misc.FrozenColorizer
|
||||
import at.petrak.hexcasting.api.spell.iota.Iota
|
||||
import at.petrak.hexcasting.api.spell.casting.CastingContext
|
||||
import net.minecraft.world.entity.player.Player
|
||||
import net.minecraft.world.item.DyeColor
|
||||
import net.minecraft.world.item.ItemStack
|
||||
import net.minecraft.world.item.enchantment.EnchantmentHelper
|
||||
|
||||
class MishapNoSpellCircle : Mishap() {
|
||||
override fun accentColor(ctx: CastingContext, errorCtx: Context): FrozenColorizer =
|
||||
dyeColor(DyeColor.LIGHT_BLUE)
|
||||
|
||||
override fun execute(ctx: CastingContext, errorCtx: Context, stack: MutableList<Iota>) {
|
||||
ctx.caster.inventory.dropAll()
|
||||
private inline fun dropAll(player: Player, stacks: MutableList<ItemStack>, filter: (ItemStack) -> Boolean = { true }) {
|
||||
for (index in stacks.indices) {
|
||||
val item = stacks[index]
|
||||
if (!item.isEmpty && filter(item)) {
|
||||
player.drop(item, true, false)
|
||||
stacks[index] = ItemStack.EMPTY
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun execute(ctx: CastingContext, errorCtx: Context, stack: MutableList<SpellDatum<*>>) {
|
||||
dropAll(ctx.caster, ctx.caster.inventory.items)
|
||||
dropAll(ctx.caster, ctx.caster.inventory.offhand)
|
||||
dropAll(ctx.caster, ctx.caster.inventory.armor) {
|
||||
!EnchantmentHelper.hasBindingCurse(it)
|
||||
}
|
||||
}
|
||||
|
||||
override fun errorMessage(ctx: CastingContext, errorCtx: Context) =
|
||||
|
|
|
@ -1,5 +1,9 @@
|
|||
package at.petrak.hexcasting.api.spell.mishaps
|
||||
|
||||
import at.petrak.hexcasting.api.misc.FrozenColorizer
|
||||
import at.petrak.hexcasting.api.spell.SpellDatum
|
||||
import at.petrak.hexcasting.api.spell.Widget
|
||||
import at.petrak.hexcasting.api.spell.casting.CastingContext
|
||||
import at.petrak.hexcasting.api.misc.FrozenColorizer
|
||||
import at.petrak.hexcasting.api.spell.casting.CastingContext
|
||||
import at.petrak.hexcasting.api.spell.iota.Iota
|
||||
|
@ -15,5 +19,8 @@ class MishapNotEnoughArgs(val expected: Int, val got: Int) : Mishap() {
|
|||
}
|
||||
|
||||
override fun errorMessage(ctx: CastingContext, errorCtx: Context) =
|
||||
error("not_enough_args", actionName(errorCtx.action), expected, got)
|
||||
if (got == 0)
|
||||
error("no_args", actionName(errorCtx.action), expected)
|
||||
else
|
||||
error("not_enough_args", actionName(errorCtx.action), expected, got)
|
||||
}
|
||||
|
|
|
@ -89,6 +89,18 @@ fun pxToCoord(px: Vec2, size: Float, offset: Vec2): HexCoord {
|
|||
HexCoord(q, r + (rf + 0.5 * qf).roundToInt())
|
||||
}
|
||||
|
||||
@JvmOverloads
|
||||
fun <T : Enum<T>> Array<T>.getSafe(key: String, default: T = this[0]): T {
|
||||
val lowercaseKey = key.lowercase(Locale.ROOT)
|
||||
return firstOrNull { it.name.lowercase(Locale.ROOT) == lowercaseKey } ?: default
|
||||
}
|
||||
|
||||
@JvmOverloads
|
||||
fun <T : Enum<T>> Array<T>.getSafe(index: Byte, default: T = this[0]) = getSafe(index.toInt(), default)
|
||||
|
||||
@JvmOverloads
|
||||
fun <T : Enum<T>> Array<T>.getSafe(index: Int, default: T = this[0]) = if (index in indices) this[index] else default
|
||||
|
||||
fun String.withStyle(op: (Style) -> Style): MutableComponent = asTextComponent.withStyle(op)
|
||||
fun String.withStyle(style: Style): MutableComponent = asTextComponent.withStyle(style)
|
||||
fun String.withStyle(formatting: ChatFormatting): MutableComponent = asTextComponent.withStyle(formatting)
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
package at.petrak.hexcasting.api.utils
|
||||
|
||||
import at.petrak.hexcasting.api.addldata.ManaHolder
|
||||
import at.petrak.hexcasting.xplat.IXplatAbstractions
|
||||
import net.minecraft.util.Mth
|
||||
import net.minecraft.world.item.ItemStack
|
||||
|
@ -31,26 +32,38 @@ fun extractMana(
|
|||
): Int {
|
||||
val manaHolder = IXplatAbstractions.INSTANCE.findManaHolder(stack) ?: return 0
|
||||
|
||||
if (drainForBatteries && !manaHolder.canConstructBattery())
|
||||
return extractMana(manaHolder, cost, drainForBatteries, simulate)
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract [cost] mana from [holder]. If [cost] is less than zero, extract all mana instead.
|
||||
* This may mutate the stack underlying [holder] (and may consume it) unless [simulate] is set.
|
||||
*
|
||||
* If [drainForBatteries] is false, this will only consider forms of mana that can be used to make new batteries.
|
||||
*
|
||||
* Return the amount of mana extracted. This may be over [cost] if mana is wasted.
|
||||
*/
|
||||
fun extractMana(
|
||||
holder: ManaHolder,
|
||||
cost: Int = -1,
|
||||
drainForBatteries: Boolean = false,
|
||||
simulate: Boolean = false
|
||||
): Int {
|
||||
if (drainForBatteries && !holder.canConstructBattery())
|
||||
return 0
|
||||
|
||||
return manaHolder.withdrawMedia(cost, simulate)
|
||||
return holder.withdrawMana(cost, simulate)
|
||||
}
|
||||
|
||||
/**
|
||||
* Sorted from least important to most important
|
||||
*/
|
||||
fun compareManaItem(astack: ItemStack, bstack: ItemStack): Int {
|
||||
val aMana = IXplatAbstractions.INSTANCE.findManaHolder(astack)
|
||||
val bMana = IXplatAbstractions.INSTANCE.findManaHolder(bstack)
|
||||
fun compareManaItem(aMana: ManaHolder, bMana: ManaHolder): Int {
|
||||
val priority = aMana.consumptionPriority - bMana.consumptionPriority
|
||||
if (priority != 0)
|
||||
return priority
|
||||
|
||||
return if (astack.item != bstack.item) {
|
||||
(aMana?.consumptionPriority ?: 0) - (bMana?.consumptionPriority ?: 0)
|
||||
} else if (aMana != null && bMana != null) {
|
||||
aMana.media - bMana.media
|
||||
} else {
|
||||
astack.count - bstack.count
|
||||
}
|
||||
return aMana.withdrawMana(-1, true) - bMana.withdrawMana(-1, true)
|
||||
}
|
||||
|
||||
fun manaBarColor(mana: Int, maxMana: Int): Int {
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
package at.petrak.hexcasting.client;
|
||||
|
||||
import at.petrak.hexcasting.api.client.ScryingLensOverlayRegistry;
|
||||
import at.petrak.hexcasting.api.misc.DiscoveryHandlers;
|
||||
import at.petrak.hexcasting.api.player.Sentinel;
|
||||
import at.petrak.hexcasting.common.lib.HexItems;
|
||||
import at.petrak.hexcasting.xplat.IXplatAbstractions;
|
||||
import com.google.common.collect.Lists;
|
||||
import com.mojang.blaze3d.platform.GlStateManager;
|
||||
|
@ -22,8 +22,6 @@ import net.minecraft.locale.Language;
|
|||
import net.minecraft.network.chat.FormattedText;
|
||||
import net.minecraft.network.chat.Style;
|
||||
import net.minecraft.util.Mth;
|
||||
import net.minecraft.world.InteractionHand;
|
||||
import net.minecraft.world.entity.EquipmentSlot;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import net.minecraft.world.phys.BlockHitResult;
|
||||
import net.minecraft.world.phys.HitResult;
|
||||
|
@ -157,22 +155,8 @@ public class HexAdditionalRenderers {
|
|||
return;
|
||||
}
|
||||
|
||||
boolean foundLens = false;
|
||||
InteractionHand lensHand = null;
|
||||
for (var hand : InteractionHand.values()) {
|
||||
if (player.getItemInHand(hand).is(HexItems.SCRYING_LENS)) {
|
||||
lensHand = hand;
|
||||
foundLens = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!foundLens && player.getItemBySlot(EquipmentSlot.HEAD).is(HexItems.SCRYING_LENS)) {
|
||||
foundLens = true;
|
||||
}
|
||||
|
||||
if (!foundLens) {
|
||||
if (!DiscoveryHandlers.hasLens(player))
|
||||
return;
|
||||
}
|
||||
|
||||
var hitRes = mc.hitResult;
|
||||
if (hitRes != null && hitRes.getType() == HitResult.Type.BLOCK) {
|
||||
|
@ -180,7 +164,7 @@ public class HexAdditionalRenderers {
|
|||
var pos = bhr.getBlockPos();
|
||||
var bs = level.getBlockState(pos);
|
||||
|
||||
var lines = ScryingLensOverlayRegistry.getLines(bs, pos, player, level, bhr.getDirection(), lensHand);
|
||||
var lines = ScryingLensOverlayRegistry.getLines(bs, pos, player, level, bhr.getDirection());
|
||||
|
||||
int totalHeight = 8;
|
||||
List<Pair<ItemStack, List<FormattedText>>> actualLines = Lists.newArrayList();
|
||||
|
|
|
@ -25,6 +25,7 @@ import net.minecraft.client.color.block.BlockColor;
|
|||
import net.minecraft.client.color.item.ItemColor;
|
||||
import net.minecraft.client.renderer.RenderType;
|
||||
import net.minecraft.client.renderer.blockentity.BlockEntityRendererProvider;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.core.Direction;
|
||||
import net.minecraft.network.chat.Component;
|
||||
import net.minecraft.network.chat.Style;
|
||||
|
@ -33,11 +34,14 @@ import net.minecraft.util.Mth;
|
|||
import net.minecraft.world.item.Item;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import net.minecraft.world.item.Items;
|
||||
import net.minecraft.world.level.Level;
|
||||
import net.minecraft.world.level.block.*;
|
||||
import net.minecraft.world.level.block.entity.BlockEntity;
|
||||
import net.minecraft.world.level.block.entity.BlockEntityType;
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
import net.minecraft.world.level.block.state.properties.ComparatorMode;
|
||||
import net.minecraft.world.level.block.state.properties.NoteBlockInstrument;
|
||||
import net.minecraft.world.level.block.state.properties.RailShape;
|
||||
import net.minecraft.world.level.material.MaterialColor;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
|
@ -144,15 +148,15 @@ public class RegisterClientStuff {
|
|||
|
||||
private static void addScryingLensStuff() {
|
||||
ScryingLensOverlayRegistry.addPredicateDisplayer(
|
||||
(state, pos, observer, world, direction, lensHand) -> state.getBlock() instanceof BlockAbstractImpetus,
|
||||
(lines, state, pos, observer, world, direction, lensHand) -> {
|
||||
(state, pos, observer, world, direction) -> state.getBlock() instanceof BlockAbstractImpetus,
|
||||
(lines, state, pos, observer, world, direction) -> {
|
||||
if (world.getBlockEntity(pos) instanceof BlockEntityAbstractImpetus beai) {
|
||||
beai.applyScryingLensOverlay(lines, state, pos, observer, world, direction, lensHand);
|
||||
beai.applyScryingLensOverlay(lines, state, pos, observer, world, direction);
|
||||
}
|
||||
});
|
||||
|
||||
ScryingLensOverlayRegistry.addDisplayer(Blocks.NOTE_BLOCK,
|
||||
(lines, state, pos, observer, world, direction, lensHand) -> {
|
||||
(lines, state, pos, observer, world, direction) -> {
|
||||
int note = state.getValue(NoteBlock.NOTE);
|
||||
|
||||
float rCol = Math.max(0.0F, Mth.sin((note / 24F + 0.0F) * Mth.TWO_PI) * 0.65F + 0.35F);
|
||||
|
@ -174,7 +178,7 @@ public class RegisterClientStuff {
|
|||
});
|
||||
|
||||
ScryingLensOverlayRegistry.addDisplayer(HexBlocks.AKASHIC_BOOKSHELF,
|
||||
(lines, state, pos, observer, world, direction, lensHand) -> {
|
||||
(lines, state, pos, observer, world, direction) -> {
|
||||
if (world.getBlockEntity(pos) instanceof BlockEntityAkashicBookshelf tile) {
|
||||
var iotaTag = tile.getIotaTag();
|
||||
if (iotaTag != null) {
|
||||
|
@ -184,8 +188,20 @@ public class RegisterClientStuff {
|
|||
}
|
||||
});
|
||||
|
||||
ScryingLensOverlayRegistry.addDisplayer(HexBlocks.AKASHIC_RECORD,
|
||||
(lines, state, pos, observer, world, direction) -> {
|
||||
if (world.getBlockEntity(pos) instanceof BlockEntityAkashicRecord tile) {
|
||||
int count = tile.getCount();
|
||||
|
||||
lines.add(new Pair<>(new ItemStack(HexBlocks.AKASHIC_BOOKSHELF), new TranslatableComponent(
|
||||
"hexcasting.tooltip.lens.akashic.record.count" + (count == 1 ? ".single" : ""),
|
||||
count
|
||||
)));
|
||||
}
|
||||
});
|
||||
|
||||
ScryingLensOverlayRegistry.addDisplayer(Blocks.COMPARATOR,
|
||||
(lines, state, pos, observer, world, direction, lensHand) -> {
|
||||
(lines, state, pos, observer, world, direction) -> {
|
||||
int comparatorValue = ScryingLensOverlayRegistry.getComparatorValue(true);
|
||||
lines.add(new Pair<>(
|
||||
new ItemStack(Items.REDSTONE),
|
||||
|
@ -200,16 +216,36 @@ public class RegisterClientStuff {
|
|||
.withStyle(redstoneColor(compare ? 0 : 15))));
|
||||
});
|
||||
|
||||
ScryingLensOverlayRegistry.addDisplayer(Blocks.POWERED_RAIL,
|
||||
(lines, state, pos, observer, world, direction) -> {
|
||||
int power = getPoweredRailStrength(world, pos, state);
|
||||
lines.add(new Pair<>(
|
||||
new ItemStack(Items.POWERED_RAIL),
|
||||
new TextComponent(String.valueOf(power))
|
||||
.withStyle(redstoneColor(power, 9))));
|
||||
});
|
||||
|
||||
ScryingLensOverlayRegistry.addDisplayer(Blocks.REPEATER,
|
||||
(lines, state, pos, observer, world, direction, lensHand) -> lines.add(new Pair<>(
|
||||
(lines, state, pos, observer, world, direction) -> lines.add(new Pair<>(
|
||||
new ItemStack(Items.CLOCK),
|
||||
Component.literal(String.valueOf(state.getValue(RepeaterBlock.DELAY)))
|
||||
.withStyle(ChatFormatting.YELLOW))));
|
||||
|
||||
ScryingLensOverlayRegistry.addPredicateDisplayer(
|
||||
(state, pos, observer, world, direction, lensHand) -> state.isSignalSource() && !state.is(
|
||||
(state, pos, observer, world, direction) -> state.getBlock() instanceof BeehiveBlock,
|
||||
(lines, state, pos, observer, world, direction) -> {
|
||||
int count = ScryingLensOverlayRegistry.getBeeValue();
|
||||
lines.add(new Pair<>(new ItemStack(Items.BEE_NEST), count == -1 ? new TextComponent("") :
|
||||
new TranslatableComponent(
|
||||
"hexcasting.tooltip.lens.bee" + (count == 1 ? ".single" : ""),
|
||||
count
|
||||
)));
|
||||
});
|
||||
|
||||
ScryingLensOverlayRegistry.addPredicateDisplayer(
|
||||
(state, pos, observer, world, direction) -> state.isSignalSource() && !state.is(
|
||||
Blocks.COMPARATOR),
|
||||
(lines, state, pos, observer, world, direction, lensHand) -> {
|
||||
(lines, state, pos, observer, world, direction) -> {
|
||||
int signalStrength = 0;
|
||||
if (state.getBlock() instanceof RedStoneWireBlock) {
|
||||
signalStrength = state.getValue(RedStoneWireBlock.POWER);
|
||||
|
@ -226,8 +262,8 @@ public class RegisterClientStuff {
|
|||
});
|
||||
|
||||
ScryingLensOverlayRegistry.addPredicateDisplayer(
|
||||
(state, pos, observer, world, direction, lensHand) -> state.hasAnalogOutputSignal(),
|
||||
(lines, state, pos, observer, world, direction, lensHand) -> {
|
||||
(state, pos, observer, world, direction) -> state.hasAnalogOutputSignal(),
|
||||
(lines, state, pos, observer, world, direction) -> {
|
||||
int comparatorValue = ScryingLensOverlayRegistry.getComparatorValue(false);
|
||||
lines.add(
|
||||
new Pair<>(
|
||||
|
@ -242,7 +278,11 @@ public class RegisterClientStuff {
|
|||
}
|
||||
|
||||
private static UnaryOperator<Style> redstoneColor(int power) {
|
||||
return color(RedStoneWireBlock.getColorForPower(Mth.clamp(power, 0, 15)));
|
||||
return redstoneColor(power, 15);
|
||||
}
|
||||
|
||||
private static UnaryOperator<Style> redstoneColor(int power, int max) {
|
||||
return color(RedStoneWireBlock.getColorForPower(Mth.clamp((power * max) / 15, 0, 15)));
|
||||
}
|
||||
|
||||
private static int instrumentColor(NoteBlockInstrument instrument) {
|
||||
|
@ -265,7 +305,7 @@ public class RegisterClientStuff {
|
|||
}
|
||||
|
||||
private static void registerDataHolderOverrides(IotaHolderItem item, Predicate<ItemStack> hasIota,
|
||||
Predicate<ItemStack> isSealed) {
|
||||
Predicate<ItemStack> isSealed) {
|
||||
IClientXplatAbstractions.INSTANCE.registerItemProperty((Item) item, ItemFocus.OVERLAY_PRED,
|
||||
(stack, level, holder, holderID) -> {
|
||||
if (!hasIota.test(stack) && !NBTHelper.hasString(stack, IotaHolderItem.TAG_OVERRIDE_VISUALLY)) {
|
||||
|
@ -278,6 +318,114 @@ public class RegisterClientStuff {
|
|||
});
|
||||
}
|
||||
|
||||
private static int getPoweredRailStrength(Level level, BlockPos pos, BlockState state) {
|
||||
if (level.hasNeighborSignal(pos))
|
||||
return 9;
|
||||
int positiveValue = findPoweredRailSignal(level, pos, state, true, 0);
|
||||
int negativeValue = findPoweredRailSignal(level, pos, state, false, 0);
|
||||
return Math.max(positiveValue, negativeValue);
|
||||
}
|
||||
|
||||
// Copypasta from PoweredRailBlock.class
|
||||
private static int findPoweredRailSignal(Level level, BlockPos pos, BlockState state, boolean travelPositive, int depth) {
|
||||
if (depth >= 8) {
|
||||
return 0;
|
||||
} else {
|
||||
int x = pos.getX();
|
||||
int y = pos.getY();
|
||||
int z = pos.getZ();
|
||||
boolean descending = true;
|
||||
RailShape shape = state.getValue(PoweredRailBlock.SHAPE);
|
||||
switch(shape) {
|
||||
case NORTH_SOUTH:
|
||||
if (travelPositive) {
|
||||
++z;
|
||||
} else {
|
||||
--z;
|
||||
}
|
||||
break;
|
||||
case EAST_WEST:
|
||||
if (travelPositive) {
|
||||
--x;
|
||||
} else {
|
||||
++x;
|
||||
}
|
||||
break;
|
||||
case ASCENDING_EAST:
|
||||
if (travelPositive) {
|
||||
--x;
|
||||
} else {
|
||||
++x;
|
||||
++y;
|
||||
descending = false;
|
||||
}
|
||||
|
||||
shape = RailShape.EAST_WEST;
|
||||
break;
|
||||
case ASCENDING_WEST:
|
||||
if (travelPositive) {
|
||||
--x;
|
||||
++y;
|
||||
descending = false;
|
||||
} else {
|
||||
++x;
|
||||
}
|
||||
|
||||
shape = RailShape.EAST_WEST;
|
||||
break;
|
||||
case ASCENDING_NORTH:
|
||||
if (travelPositive) {
|
||||
++z;
|
||||
} else {
|
||||
--z;
|
||||
++y;
|
||||
descending = false;
|
||||
}
|
||||
|
||||
shape = RailShape.NORTH_SOUTH;
|
||||
break;
|
||||
case ASCENDING_SOUTH:
|
||||
if (travelPositive) {
|
||||
++z;
|
||||
++y;
|
||||
descending = false;
|
||||
} else {
|
||||
--z;
|
||||
}
|
||||
|
||||
shape = RailShape.NORTH_SOUTH;
|
||||
}
|
||||
|
||||
int power = getPowerFromRail(level, new BlockPos(x, y, z), travelPositive, depth, shape);
|
||||
|
||||
if (power > 0) {
|
||||
return power;
|
||||
} else if (descending) {
|
||||
return getPowerFromRail(level, new BlockPos(x, y - 1, z), travelPositive, depth, shape);
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static int getPowerFromRail(Level level, BlockPos pos, boolean travelPositive, int depth, RailShape shape) {
|
||||
BlockState otherState = level.getBlockState(pos);
|
||||
if (!otherState.is(Blocks.POWERED_RAIL)) {
|
||||
return 0;
|
||||
} else {
|
||||
RailShape otherShape = otherState.getValue(PoweredRailBlock.SHAPE);
|
||||
if (shape == RailShape.EAST_WEST && (otherShape == RailShape.NORTH_SOUTH || otherShape == RailShape.ASCENDING_NORTH || otherShape == RailShape.ASCENDING_SOUTH)) {
|
||||
return 0;
|
||||
} else if (shape == RailShape.NORTH_SOUTH && (otherShape == RailShape.EAST_WEST || otherShape == RailShape.ASCENDING_EAST || otherShape == RailShape.ASCENDING_WEST)) {
|
||||
return 0;
|
||||
} else if (otherState.getValue(PoweredRailBlock.POWERED)) {
|
||||
return level.hasNeighborSignal(pos) ? 8 - depth : findPoweredRailSignal(level, pos, otherState, travelPositive, depth + 1);
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void registerScrollOverrides(ItemScroll scroll) {
|
||||
IClientXplatAbstractions.INSTANCE.registerItemProperty(scroll, ItemScroll.ANCIENT_PREDICATE,
|
||||
(stack, level, holder, holderID) -> NBTHelper.hasString(stack, ItemScroll.TAG_OP_ID) ? 1f : 0f);
|
||||
|
|
|
@ -26,6 +26,11 @@ public class ShiftScrollListener {
|
|||
// not .isCrouching! that fails for players who are not on the ground
|
||||
// yes, this does work if you remap your sneak key
|
||||
if (player != null && (player.isShiftKeyDown() || !needsSneaking)) {
|
||||
// Spectators shouldn't interact with items!
|
||||
if (player.isSpectator()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (IsScrollableItem(player.getMainHandItem().getItem())) {
|
||||
mainHandDelta += delta;
|
||||
return true;
|
||||
|
|
|
@ -125,7 +125,7 @@ public class WallScrollRenderer extends EntityRenderer<EntityWallScroll> {
|
|||
if (wallScroll.getShowsStrokeOrder()) {
|
||||
var spotFrac = 0.8f * wallScroll.blockSize;
|
||||
|
||||
var animTime = wallScroll.tickCount;
|
||||
var animTime = wallScroll.tickCount + partialTicks;
|
||||
var pointCircuit =
|
||||
(animTime * HexConfig.client().patternPointSpeedMultiplier()) % (points.size() + 10);
|
||||
if (pointCircuit < points.size() - 1) {
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
package at.petrak.hexcasting.client.gui
|
||||
|
||||
import at.petrak.hexcasting.api.misc.DiscoveryHandlers
|
||||
import at.petrak.hexcasting.api.mod.HexConfig
|
||||
import at.petrak.hexcasting.api.mod.HexItemTags
|
||||
import at.petrak.hexcasting.api.spell.casting.ControllerInfo
|
||||
|
@ -14,6 +15,10 @@ import at.petrak.hexcasting.api.utils.gold
|
|||
import at.petrak.hexcasting.api.utils.otherHand
|
||||
import at.petrak.hexcasting.client.*
|
||||
import at.petrak.hexcasting.client.ktxt.accumulatedScroll
|
||||
import at.petrak.hexcasting.client.ShiftScrollListener
|
||||
import at.petrak.hexcasting.client.drawPatternFromPoints
|
||||
import at.petrak.hexcasting.client.drawSpot
|
||||
import at.petrak.hexcasting.client.ktxt.accumulatedScroll
|
||||
import at.petrak.hexcasting.client.sound.GridSoundInstance
|
||||
import at.petrak.hexcasting.common.lib.HexIotaTypes
|
||||
import at.petrak.hexcasting.common.lib.HexItems
|
||||
|
@ -34,6 +39,10 @@ import net.minecraft.util.Mth
|
|||
import net.minecraft.world.InteractionHand
|
||||
import net.minecraft.world.phys.Vec2
|
||||
import kotlin.math.*
|
||||
import kotlin.math.atan2
|
||||
import kotlin.math.roundToInt
|
||||
import kotlin.math.sign
|
||||
import kotlin.math.sqrt
|
||||
|
||||
class GuiSpellcasting constructor(
|
||||
private val handOpenedWith: InteractionHand,
|
||||
|
@ -61,8 +70,9 @@ class GuiSpellcasting constructor(
|
|||
this.calculateIotaDisplays()
|
||||
}
|
||||
|
||||
fun recvServerUpdate(info: ControllerInfo) {
|
||||
this.patterns.lastOrNull()?.let {
|
||||
fun recvServerUpdate(info: ControllerInfo, index: Int) {
|
||||
this.stackDescs = info.stackDesc
|
||||
this.patterns.getOrNull(index)?.let {
|
||||
it.type = info.resolutionType
|
||||
}
|
||||
|
||||
|
@ -250,10 +260,6 @@ class GuiSpellcasting constructor(
|
|||
is PatternDrawState.JustStarted -> {
|
||||
// Well, we never managed to get anything on the stack this go-around.
|
||||
this.drawState = PatternDrawState.BetweenPatterns
|
||||
if (this.patterns.isEmpty()) {
|
||||
Minecraft.getInstance().setScreen(null)
|
||||
Minecraft.getInstance().soundManager.stop(HexSounds.CASTING_AMBIANCE.location, null)
|
||||
}
|
||||
}
|
||||
is PatternDrawState.Drawing -> {
|
||||
val (start, _, pat) = this.drawState as PatternDrawState.Drawing
|
||||
|
@ -452,13 +458,12 @@ class GuiSpellcasting constructor(
|
|||
|
||||
/** Distance between adjacent hex centers */
|
||||
fun hexSize(): Float {
|
||||
val hasLens = Minecraft.getInstance().player!!
|
||||
.getItemInHand(otherHand(this.handOpenedWith)).`is`(HexItems.SCRYING_LENS)
|
||||
val scaleModifier = DiscoveryHandlers.gridScaleModifier(Minecraft.getInstance().player)
|
||||
|
||||
// Originally, we allowed 32 dots across. Assuming a 1920x1080 screen this allowed like 500-odd area.
|
||||
// Let's be generous and give them 512.
|
||||
val baseScale = sqrt(this.width.toDouble() * this.height / 512.0)
|
||||
return baseScale.toFloat() * if (hasLens) 0.75f else 1f
|
||||
return baseScale.toFloat() * scaleModifier
|
||||
}
|
||||
|
||||
fun coordsOffset(): Vec2 = Vec2(this.width.toFloat() * 0.5f, this.height.toFloat() * 0.5f)
|
||||
|
|
|
@ -0,0 +1,83 @@
|
|||
package at.petrak.hexcasting.common.blocks.akashic;
|
||||
|
||||
import at.petrak.hexcasting.api.misc.TriPredicate;
|
||||
import at.petrak.hexcasting.common.lib.HexBlocks;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.core.Direction;
|
||||
import net.minecraft.world.level.Level;
|
||||
import net.minecraft.world.level.block.Block;
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.ArrayDeque;
|
||||
import java.util.HashSet;
|
||||
|
||||
public class BlockAkashicFloodfiller extends Block {
|
||||
public BlockAkashicFloodfiller(Properties p_49795_) {
|
||||
super(p_49795_);
|
||||
}
|
||||
|
||||
public @Nullable
|
||||
BlockPos getRecordPosition(BlockPos here, BlockState state, Level world) {
|
||||
return floodFillFor(here, world,
|
||||
(pos, bs, level) -> bs.is(HexBlocks.AKASHIC_RECORD));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRemove(BlockState pState, Level pLevel, BlockPos pPos, BlockState pNewState, boolean pIsMoving) {
|
||||
var recordPos = this.getRecordPosition(pPos, pState, pLevel);
|
||||
if (recordPos != null && pLevel.getBlockEntity(recordPos) instanceof BlockEntityAkashicRecord akashic) {
|
||||
akashic.removeFloodfillerAt(pPos);
|
||||
}
|
||||
|
||||
super.onRemove(pState, pLevel, pPos, pNewState, pIsMoving);
|
||||
}
|
||||
|
||||
public boolean canBeFloodedThrough(BlockPos pos, BlockState state, Level world) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static BlockPos floodFillFor(BlockPos start, Level world,
|
||||
TriPredicate<BlockPos, BlockState, Level> isValid, TriPredicate<BlockPos, BlockState, Level> isTarget, int maxRange) {
|
||||
var seenBlocks = new HashSet<BlockPos>();
|
||||
var todo = new ArrayDeque<BlockPos>();
|
||||
todo.add(start);
|
||||
|
||||
while (!todo.isEmpty()) {
|
||||
var here = todo.remove();
|
||||
|
||||
for (var dir : Direction.values()) {
|
||||
var neighbor = here.relative(dir);
|
||||
|
||||
if (neighbor.distSqr(start) > maxRange * maxRange)
|
||||
continue;
|
||||
|
||||
if (seenBlocks.add(neighbor)) {
|
||||
var bs = world.getBlockState(neighbor);
|
||||
if (isTarget.test(neighbor, bs, world)) {
|
||||
return neighbor;
|
||||
} else if (isValid.test(neighbor, bs, world)) {
|
||||
todo.add(neighbor);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static BlockPos floodFillFor(BlockPos start, Level world,
|
||||
TriPredicate<BlockPos, BlockState, Level> isTarget) {
|
||||
return floodFillFor(start, world, BlockAkashicFloodfiller::canItBeFloodedThrough, isTarget, 32);
|
||||
}
|
||||
|
||||
public static boolean canItBeFloodedThrough(BlockPos pos, BlockState state, Level world) {
|
||||
if (!(state.getBlock() instanceof BlockAkashicFloodfiller flooder)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return flooder.canBeFloodedThrough(pos, state, world);
|
||||
}
|
||||
}
|
|
@ -5,18 +5,18 @@ import at.petrak.hexcasting.api.utils.NBTHelper;
|
|||
import at.petrak.hexcasting.common.lib.HexBlockEntities;
|
||||
import com.mojang.authlib.GameProfile;
|
||||
import com.mojang.datafixers.util.Pair;
|
||||
import net.minecraft.client.multiplayer.ClientLevel;
|
||||
import net.minecraft.client.player.LocalPlayer;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.core.Direction;
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
import net.minecraft.nbt.NbtUtils;
|
||||
import net.minecraft.nbt.Tag;
|
||||
import net.minecraft.network.chat.Component;
|
||||
import net.minecraft.network.chat.TranslatableComponent;
|
||||
import net.minecraft.world.InteractionHand;
|
||||
import net.minecraft.world.entity.player.Player;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import net.minecraft.world.item.Items;
|
||||
import net.minecraft.world.level.Level;
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
|
@ -82,10 +82,10 @@ public class BlockEntityStoredPlayerImpetus extends BlockEntityAbstractImpetus {
|
|||
}
|
||||
|
||||
public void applyScryingLensOverlay(List<Pair<ItemStack, Component>> lines,
|
||||
BlockState state, BlockPos pos, LocalPlayer observer,
|
||||
ClientLevel world,
|
||||
Direction hitFace, InteractionHand lensHand) {
|
||||
super.applyScryingLensOverlay(lines, state, pos, observer, world, hitFace, lensHand);
|
||||
BlockState state, BlockPos pos, Player observer,
|
||||
Level world,
|
||||
Direction hitFace) {
|
||||
super.applyScryingLensOverlay(lines, state, pos, observer, world, hitFace);
|
||||
|
||||
var name = this.getPlayerName();
|
||||
if (name != null) {
|
||||
|
|
|
@ -40,6 +40,10 @@ import at.petrak.hexcasting.common.casting.operators.stack.*;
|
|||
import at.petrak.hexcasting.common.lib.HexItems;
|
||||
import it.unimi.dsi.fastutil.booleans.BooleanArrayList;
|
||||
import net.minecraft.world.effect.MobEffects;
|
||||
import net.minecraft.world.item.Items;
|
||||
import net.minecraft.world.level.block.Blocks;
|
||||
import net.minecraft.world.level.block.LayeredCauldronBlock;
|
||||
import net.minecraft.world.level.material.Fluids;
|
||||
import net.minecraft.world.phys.Vec3;
|
||||
|
||||
import static at.petrak.hexcasting.api.HexAPI.modLoc;
|
||||
|
@ -211,10 +215,13 @@ public class RegisterPatterns {
|
|||
modLoc("colorize"),
|
||||
OpColorize.INSTANCE);
|
||||
PatternRegistry.mapPattern(HexPattern.fromAngles("aqawqadaq", HexDir.SOUTH_EAST), modLoc("create_water"),
|
||||
OpCreateWater.INSTANCE);
|
||||
new OpCreateFluid(false, ManaConstants.DUST_UNIT,
|
||||
Items.WATER_BUCKET,
|
||||
Blocks.WATER_CAULDRON.defaultBlockState().setValue(LayeredCauldronBlock.LEVEL, LayeredCauldronBlock.MAX_FILL_LEVEL),
|
||||
Fluids.WATER));
|
||||
PatternRegistry.mapPattern(HexPattern.fromAngles("dedwedade", HexDir.SOUTH_WEST),
|
||||
modLoc("destroy_water"),
|
||||
OpDestroyWater.INSTANCE);
|
||||
OpDestroyFluid.INSTANCE);
|
||||
PatternRegistry.mapPattern(HexPattern.fromAngles("aaqawawa", HexDir.SOUTH_EAST), modLoc("ignite"),
|
||||
OpIgnite.INSTANCE);
|
||||
PatternRegistry.mapPattern(HexPattern.fromAngles("ddedwdwd", HexDir.SOUTH_WEST), modLoc("extinguish"),
|
||||
|
@ -296,13 +303,15 @@ public class RegisterPatterns {
|
|||
modLoc("sentinel/wayfind"),
|
||||
OpGetSentinelWayfind.INSTANCE);
|
||||
|
||||
|
||||
PatternRegistry.mapPattern(HexPattern.fromAngles("waadwawdaaweewq", HexDir.EAST),
|
||||
modLoc("lightning"), OpLightning.INSTANCE, true);
|
||||
PatternRegistry.mapPattern(HexPattern.fromAngles("eawwaeawawaa", HexDir.NORTH_WEST),
|
||||
modLoc("flight"), OpFlight.INSTANCE, true);
|
||||
PatternRegistry.mapPattern(HexPattern.fromAngles("eaqawqadaqd", HexDir.EAST),
|
||||
modLoc("create_lava"), OpCreateLava.INSTANCE, true);
|
||||
modLoc("create_lava"), new OpCreateFluid(true, ManaConstants.CRYSTAL_UNIT,
|
||||
Items.LAVA_BUCKET,
|
||||
Blocks.LAVA_CAULDRON.defaultBlockState(),
|
||||
Fluids.LAVA), true);
|
||||
PatternRegistry.mapPattern(
|
||||
HexPattern.fromAngles("wwwqqqwwwqqeqqwwwqqwqqdqqqqqdqq", HexDir.EAST),
|
||||
modLoc("teleport"), OpTeleport.INSTANCE, true);
|
||||
|
@ -505,7 +514,7 @@ public class RegisterPatterns {
|
|||
if (negate) {
|
||||
accumulator = -accumulator;
|
||||
}
|
||||
return Action.makeConstantOp(new DoubleIota(accumulator));
|
||||
return Operator.makeConstantOp(new DoubleIota(accumulator), modLoc("number"));
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
|
@ -541,7 +550,7 @@ public class RegisterPatterns {
|
|||
return null;
|
||||
}
|
||||
|
||||
return new OpMask(mask);
|
||||
return new OpMask(mask, modLoc("mask"));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,6 +19,8 @@ object OpBlockAxisRaycast : ConstManaAction {
|
|||
val origin = args.getVec3(0, argc)
|
||||
val look = args.getVec3(1, argc)
|
||||
|
||||
ctx.assertVecInRange(origin)
|
||||
|
||||
val blockHitResult = ctx.world.clip(
|
||||
ClipContext(
|
||||
origin,
|
||||
|
|
|
@ -19,6 +19,8 @@ object OpBlockRaycast : ConstManaAction {
|
|||
val origin = args.getVec3(0, argc)
|
||||
val look = args.getVec3(1, argc)
|
||||
|
||||
ctx.assertVecInRange(origin)
|
||||
|
||||
val blockHitResult = ctx.world.clip(
|
||||
ClipContext(
|
||||
origin,
|
||||
|
|
|
@ -19,6 +19,8 @@ object OpEntityRaycast : ConstManaAction {
|
|||
val look = args.getVec3(1, argc)
|
||||
val endp = Action.raycastEnd(origin, look)
|
||||
|
||||
ctx.assertVecInRange(origin)
|
||||
|
||||
val entityHitResult = ProjectileUtil.getEntityHitResult(
|
||||
ctx.caster,
|
||||
origin,
|
||||
|
|
|
@ -1,11 +1,16 @@
|
|||
package at.petrak.hexcasting.common.casting.operators
|
||||
|
||||
import at.petrak.hexcasting.api.spell.ConstManaOperator
|
||||
import at.petrak.hexcasting.api.spell.SpellDatum
|
||||
import at.petrak.hexcasting.api.spell.ConstManaAction
|
||||
import at.petrak.hexcasting.api.spell.casting.CastingContext
|
||||
import at.petrak.hexcasting.api.spell.getChecked
|
||||
import at.petrak.hexcasting.api.spell.mishaps.MishapBadEntity
|
||||
import at.petrak.hexcasting.api.spell.getItemEntity
|
||||
import at.petrak.hexcasting.api.spell.iota.Iota
|
||||
import at.petrak.hexcasting.api.spell.mishaps.MishapBadItem
|
||||
import at.petrak.hexcasting.xplat.IXplatAbstractions
|
||||
import net.minecraft.world.entity.Entity
|
||||
|
||||
object OpTheCoolerRead : ConstManaAction {
|
||||
override val argc = 1
|
||||
|
@ -14,13 +19,12 @@ object OpTheCoolerRead : ConstManaAction {
|
|||
args: List<Iota>,
|
||||
ctx: CastingContext
|
||||
): List<Iota> {
|
||||
val target = args.getItemEntity(0, argc)
|
||||
val target = args.getEntity(0, argc)
|
||||
|
||||
ctx.assertEntityInRange(target)
|
||||
|
||||
val stack = target.item
|
||||
val datumHolder = IXplatAbstractions.INSTANCE.findDataHolder(stack)
|
||||
?: throw MishapBadItem.of(target, "iota.read")
|
||||
val datumHolder = IXplatAbstractions.INSTANCE.findDataHolder(target)
|
||||
?: throw MishapBadEntity.of(target, "iota.read")
|
||||
|
||||
val datum = datumHolder.readIota(ctx.world)
|
||||
?: datumHolder.emptyIota()
|
||||
|
|
|
@ -14,15 +14,15 @@ object OpTheCoolerReadable : ConstManaAction {
|
|||
args: List<Iota>,
|
||||
ctx: CastingContext
|
||||
): List<Iota> {
|
||||
val target = args.getItemEntity(0, argc)
|
||||
val target = args.getEntity(0, argc)
|
||||
ctx.assertEntityInRange(target)
|
||||
|
||||
val stack = target.item
|
||||
val datumHolder = IXplatAbstractions.INSTANCE.findDataHolder(stack)
|
||||
?: return false.asActionResult
|
||||
val datumHolder = IXplatAbstractions.INSTANCE.findDataHolder(target)
|
||||
?: return false.asSpellResult
|
||||
|
||||
if (datumHolder.readIota(ctx.world) == null && datumHolder.emptyIota() == null)
|
||||
return false.asActionResult
|
||||
datumHolder.readDatum(ctx.world)
|
||||
?: datumHolder.emptyDatum()
|
||||
?: return false.asSpellResult
|
||||
|
||||
return true.asActionResult
|
||||
}
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
package at.petrak.hexcasting.common.casting.operators
|
||||
|
||||
import at.petrak.hexcasting.api.addldata.DataHolder
|
||||
import at.petrak.hexcasting.api.spell.ParticleSpray
|
||||
import at.petrak.hexcasting.api.spell.RenderedSpell
|
||||
import at.petrak.hexcasting.api.spell.SpellAction
|
||||
|
@ -35,22 +36,15 @@ object OpWrite : SpellAction {
|
|||
throw MishapOthersName(trueName)
|
||||
|
||||
return Triple(
|
||||
Spell(datum),
|
||||
Spell(datum, datumHolder),
|
||||
0,
|
||||
listOf()
|
||||
)
|
||||
}
|
||||
|
||||
private data class Spell(val datum: Iota) : RenderedSpell {
|
||||
private data class Spell(val datum: Iota, val datumHolder: DataHolder) : RenderedSpell {
|
||||
override fun cast(ctx: CastingContext) {
|
||||
val (handStack) = ctx.getHeldItemToOperateOn {
|
||||
val datumHolder = IXplatAbstractions.INSTANCE.findDataHolder(it)
|
||||
|
||||
datumHolder != null && datumHolder.writeIota(datum, true)
|
||||
}
|
||||
|
||||
val datumHolder = IXplatAbstractions.INSTANCE.findDataHolder(handStack)
|
||||
datumHolder?.writeIota(datum, false)
|
||||
datumHolder.writeDatum(datum, false)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -13,7 +13,9 @@ object OpRemove : ConstManaAction {
|
|||
|
||||
override fun execute(args: List<Iota>, ctx: CastingContext): List<Iota> {
|
||||
val list = args.getList(0, argc).toMutableList()
|
||||
val index = args.getPositiveIntUnder(1, list.size, argc)
|
||||
val index = args.getInt(1, argc)
|
||||
if (index < 0 || index >= list.size)
|
||||
return list.asActionResult
|
||||
list.removeAt(index)
|
||||
return list.asActionResult
|
||||
}
|
||||
|
|
|
@ -1,11 +1,15 @@
|
|||
package at.petrak.hexcasting.common.casting.operators.math
|
||||
|
||||
import at.petrak.hexcasting.api.spell.ConstManaOperator
|
||||
import at.petrak.hexcasting.api.spell.SpellDatum
|
||||
import at.petrak.hexcasting.api.spell.ConstManaAction
|
||||
import at.petrak.hexcasting.api.spell.asActionResult
|
||||
import at.petrak.hexcasting.api.spell.casting.CastingContext
|
||||
import at.petrak.hexcasting.api.spell.getNumOrVec
|
||||
import at.petrak.hexcasting.api.spell.iota.Iota
|
||||
import at.petrak.hexcasting.api.spell.mishaps.MishapDivideByZero
|
||||
import at.petrak.hexcasting.api.spell.numOrVec
|
||||
import at.petrak.hexcasting.api.spell.spellListOf
|
||||
import net.minecraft.world.phys.Vec3
|
||||
import kotlin.math.pow
|
||||
|
||||
|
@ -41,7 +45,7 @@ object OpPowProj : ConstManaAction {
|
|||
{ rvec ->
|
||||
if (lvec == Vec3.ZERO)
|
||||
throw MishapDivideByZero.of(args[0], args[1], "project")
|
||||
rvec.scale(rvec.dot(lvec) / lvec.dot(lvec)).asActionResult
|
||||
lvec.scale(rvec.dot(lvec) / lvec.dot(lvec))
|
||||
}
|
||||
)
|
||||
})
|
||||
|
|
|
@ -6,9 +6,8 @@ import at.petrak.hexcasting.api.spell.casting.CastingContext
|
|||
import at.petrak.hexcasting.api.spell.iota.Iota
|
||||
import at.petrak.hexcasting.api.spell.mishaps.MishapImmuneEntity
|
||||
import at.petrak.hexcasting.api.spell.mishaps.MishapLocationTooFarAway
|
||||
import at.petrak.hexcasting.common.network.MsgBlinkAck
|
||||
import at.petrak.hexcasting.xplat.IXplatAbstractions
|
||||
import net.minecraft.server.level.ServerPlayer
|
||||
import at.petrak.hexcasting.common.casting.operators.spells.great.OpTeleport
|
||||
import at.petrak.hexcasting.common.lib.HexEntityTags
|
||||
import net.minecraft.world.entity.Entity
|
||||
import kotlin.math.roundToInt
|
||||
|
||||
|
@ -22,7 +21,7 @@ object OpBlink : SpellAction {
|
|||
val delta = args.getDouble(1, argc)
|
||||
ctx.assertEntityInRange(target)
|
||||
|
||||
if (!target.canChangeDimensions())
|
||||
if (!target.canChangeDimensions() || target.type.`is`(HexEntityTags.CANNOT_TELEPORT))
|
||||
throw MishapImmuneEntity(target)
|
||||
|
||||
val dvec = target.lookAngle.scale(delta)
|
||||
|
@ -47,12 +46,8 @@ object OpBlink : SpellAction {
|
|||
|
||||
private data class Spell(val target: Entity, val delta: Double) : RenderedSpell {
|
||||
override fun cast(ctx: CastingContext) {
|
||||
val dvec = target.lookAngle.scale(delta)
|
||||
target.setPos(target.position().add(dvec))
|
||||
if (target is ServerPlayer) {
|
||||
target.connection.resetPosition()
|
||||
IXplatAbstractions.INSTANCE.sendPacketToPlayer(target, MsgBlinkAck(dvec))
|
||||
}
|
||||
val delta = target.lookAngle.scale(delta)
|
||||
OpTeleport.teleportRespectSticky(target, delta)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -24,7 +24,7 @@ object OpBreakBlock : SpellAction {
|
|||
ctx.assertVecInRange(pos)
|
||||
|
||||
return Triple(
|
||||
Spell(pos),
|
||||
Spell(bpos),
|
||||
(ManaConstants.DUST_UNIT * 1.125).toInt(),
|
||||
listOf(ParticleSpray.burst(Vec3.atCenterOf(pos), 1.0))
|
||||
)
|
||||
|
@ -32,7 +32,7 @@ object OpBreakBlock : SpellAction {
|
|||
|
||||
private data class Spell(val pos: BlockPos) : RenderedSpell {
|
||||
override fun cast(ctx: CastingContext) {
|
||||
if (!ctx.world.mayInteract(ctx.caster, pos))
|
||||
if (!ctx.canEditBlockAt(pos))
|
||||
return
|
||||
|
||||
val blockstate = ctx.world.getBlockState(pos)
|
||||
|
|
|
@ -9,6 +9,7 @@ import at.petrak.hexcasting.api.spell.SpellAction
|
|||
import at.petrak.hexcasting.api.spell.casting.CastingContext
|
||||
import at.petrak.hexcasting.api.spell.mishaps.MishapBadOffhandItem
|
||||
import at.petrak.hexcasting.xplat.IXplatAbstractions
|
||||
import net.minecraft.world.item.ItemStack
|
||||
|
||||
object OpColorize : SpellAction {
|
||||
override val argc = 0
|
||||
|
@ -26,22 +27,20 @@ object OpColorize : SpellAction {
|
|||
)
|
||||
}
|
||||
return Triple(
|
||||
Spell,
|
||||
Spell(handStack),
|
||||
ManaConstants.DUST_UNIT,
|
||||
listOf()
|
||||
)
|
||||
}
|
||||
|
||||
private object Spell : RenderedSpell {
|
||||
private data class Spell(val stack: ItemStack) : RenderedSpell {
|
||||
override fun cast(ctx: CastingContext) {
|
||||
val handStack = ctx.getHeldItemToOperateOn(IXplatAbstractions.INSTANCE::isColorizer).first.copy()
|
||||
if (IXplatAbstractions.INSTANCE.isColorizer(handStack)) {
|
||||
if (ctx.withdrawItem(handStack.item, 1, true)) {
|
||||
IXplatAbstractions.INSTANCE.setColorizer(
|
||||
ctx.caster,
|
||||
FrozenColorizer(handStack, ctx.caster.uuid)
|
||||
)
|
||||
}
|
||||
val copy = stack.copy()
|
||||
if (ctx.withdrawItem(copy, 1, true)) {
|
||||
IXplatAbstractions.INSTANCE.setColorizer(
|
||||
ctx.caster,
|
||||
FrozenColorizer(copy, ctx.caster.uuid)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -44,7 +44,7 @@ class OpConjureBlock(val light: Boolean) : SpellAction {
|
|||
|
||||
private data class Spell(val pos: BlockPos, val light: Boolean) : RenderedSpell {
|
||||
override fun cast(ctx: CastingContext) {
|
||||
if (!ctx.world.mayInteract(ctx.caster, pos))
|
||||
if (!ctx.canEditBlockAt(pos))
|
||||
return
|
||||
|
||||
val placeContext = DirectionalPlaceContext(ctx.world, pos, Direction.DOWN, ItemStack.EMPTY, Direction.UP)
|
||||
|
|
|
@ -0,0 +1,59 @@
|
|||
package at.petrak.hexcasting.common.casting.operators.spells
|
||||
|
||||
import at.petrak.hexcasting.api.spell.*
|
||||
import at.petrak.hexcasting.api.spell.casting.CastingContext
|
||||
import at.petrak.hexcasting.xplat.IXplatAbstractions
|
||||
import net.minecraft.core.BlockPos
|
||||
import net.minecraft.world.item.BucketItem
|
||||
import net.minecraft.world.item.Item
|
||||
import net.minecraft.world.item.ItemStack
|
||||
import net.minecraft.world.level.block.Blocks
|
||||
import net.minecraft.world.level.block.state.BlockState
|
||||
import net.minecraft.world.level.material.Fluid
|
||||
import net.minecraft.world.phys.Vec3
|
||||
|
||||
class OpCreateFluid(override val isGreat: Boolean, val cost: Int, val bucket: Item, val cauldron: BlockState, val fluid: Fluid) : SpellOperator {
|
||||
override val argc = 1
|
||||
override fun execute(
|
||||
args: List<SpellDatum<*>>,
|
||||
ctx: CastingContext
|
||||
): Triple<RenderedSpell, Int, List<ParticleSpray>> {
|
||||
val target = args.getChecked<Vec3>(0, argc)
|
||||
ctx.assertVecInRange(target)
|
||||
|
||||
return Triple(
|
||||
Spell(target, bucket, cauldron, fluid),
|
||||
cost,
|
||||
listOf(ParticleSpray.burst(Vec3.atCenterOf(BlockPos(target)), 1.0))
|
||||
)
|
||||
}
|
||||
|
||||
private data class Spell(val target: Vec3, val bucket: Item, val cauldron: BlockState, val fluid: Fluid) : RenderedSpell {
|
||||
override fun cast(ctx: CastingContext) {
|
||||
val pos = BlockPos(target)
|
||||
|
||||
if (!ctx.canEditBlockAt(pos) || !IXplatAbstractions.INSTANCE.isPlacingAllowed(
|
||||
ctx.world,
|
||||
pos,
|
||||
ItemStack(bucket),
|
||||
ctx.caster
|
||||
)
|
||||
)
|
||||
return
|
||||
|
||||
val state = ctx.world.getBlockState(pos)
|
||||
|
||||
if (state.block == Blocks.CAULDRON)
|
||||
ctx.world.setBlock(pos, cauldron, 3)
|
||||
else if (!IXplatAbstractions.INSTANCE.tryPlaceFluid(
|
||||
ctx.world,
|
||||
ctx.castingHand,
|
||||
pos,
|
||||
fluid
|
||||
) && bucket is BucketItem) {
|
||||
// make the player null so we don't give them a usage statistic for example
|
||||
bucket.emptyContents(null, ctx.world, pos, null)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,68 +0,0 @@
|
|||
package at.petrak.hexcasting.common.casting.operators.spells
|
||||
|
||||
import at.petrak.hexcasting.api.HexAPI
|
||||
import at.petrak.hexcasting.api.misc.ManaConstants
|
||||
import at.petrak.hexcasting.api.spell.ParticleSpray
|
||||
import at.petrak.hexcasting.api.spell.RenderedSpell
|
||||
import at.petrak.hexcasting.api.spell.SpellAction
|
||||
import at.petrak.hexcasting.api.spell.casting.CastingContext
|
||||
import at.petrak.hexcasting.api.spell.getBlockPos
|
||||
import at.petrak.hexcasting.api.spell.iota.Iota
|
||||
import at.petrak.hexcasting.xplat.IXplatAbstractions
|
||||
import net.minecraft.core.BlockPos
|
||||
import net.minecraft.world.item.BucketItem
|
||||
import net.minecraft.world.item.ItemStack
|
||||
import net.minecraft.world.item.Items
|
||||
import net.minecraft.world.level.block.AbstractCauldronBlock
|
||||
import net.minecraft.world.level.block.Blocks
|
||||
import net.minecraft.world.level.block.LayeredCauldronBlock
|
||||
import net.minecraft.world.level.material.Fluids
|
||||
import net.minecraft.world.phys.Vec3
|
||||
|
||||
object OpCreateWater : SpellAction {
|
||||
override val argc = 1
|
||||
override fun execute(
|
||||
args: List<Iota>,
|
||||
ctx: CastingContext
|
||||
): Triple<RenderedSpell, Int, List<ParticleSpray>> {
|
||||
val target = args.getBlockPos(0, argc)
|
||||
|
||||
return Triple(
|
||||
Spell(target),
|
||||
ManaConstants.DUST_UNIT,
|
||||
listOf(ParticleSpray.burst(Vec3.atCenterOf(target), 1.0))
|
||||
)
|
||||
}
|
||||
|
||||
private data class Spell(val pos: BlockPos) : RenderedSpell {
|
||||
override fun cast(ctx: CastingContext) {
|
||||
if (!ctx.world.mayInteract(ctx.caster, pos) || !IXplatAbstractions.INSTANCE.isPlacingAllowed(ctx.world, pos, ItemStack(Items.WATER_BUCKET), ctx.caster))
|
||||
return
|
||||
val state = ctx.world.getBlockState(pos)
|
||||
|
||||
if (state.block is AbstractCauldronBlock)
|
||||
ctx.world.setBlock(
|
||||
pos,
|
||||
Blocks.WATER_CAULDRON.defaultBlockState().setValue(LayeredCauldronBlock.LEVEL, 3),
|
||||
3
|
||||
)
|
||||
else if (!IXplatAbstractions.INSTANCE.tryPlaceFluid(
|
||||
ctx.world,
|
||||
ctx.castingHand,
|
||||
pos,
|
||||
ItemStack(Items.WATER_BUCKET),
|
||||
Fluids.WATER
|
||||
)
|
||||
) {
|
||||
// Just steal bucket code lmao
|
||||
val charlie = Items.WATER_BUCKET
|
||||
if (charlie is BucketItem) {
|
||||
// make the player null so we don't give them a usage statistic for example
|
||||
charlie.emptyContents(null, ctx.world, pos, null)
|
||||
} else {
|
||||
HexAPI.LOGGER.warn("Items.WATER_BUCKET wasn't a BucketItem?")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -3,42 +3,54 @@ package at.petrak.hexcasting.common.casting.operators.spells
|
|||
import at.petrak.hexcasting.api.misc.ManaConstants
|
||||
import at.petrak.hexcasting.api.spell.*
|
||||
import at.petrak.hexcasting.api.spell.casting.CastingContext
|
||||
import at.petrak.hexcasting.api.spell.iota.Iota
|
||||
import at.petrak.hexcasting.xplat.IXplatAbstractions
|
||||
import net.minecraft.core.BlockPos
|
||||
import net.minecraft.core.Direction
|
||||
import net.minecraft.core.particles.ParticleTypes
|
||||
import net.minecraft.sounds.SoundEvents
|
||||
import net.minecraft.sounds.SoundSource
|
||||
import net.minecraft.world.level.block.Block
|
||||
import net.minecraft.world.level.block.Blocks
|
||||
import net.minecraft.world.level.block.BucketPickup
|
||||
import net.minecraft.world.level.block.LiquidBlock
|
||||
import net.minecraft.world.level.block.*
|
||||
import net.minecraft.world.level.block.entity.BlockEntity
|
||||
import net.minecraft.world.level.material.Fluids
|
||||
import net.minecraft.world.level.material.Material
|
||||
import net.minecraft.world.phys.Vec3
|
||||
|
||||
object OpDestroyWater : SpellAction {
|
||||
object OpDestroyFluid : SpellOperator {
|
||||
override val argc = 1
|
||||
override fun execute(
|
||||
args: List<Iota>,
|
||||
args: List<SpellDatum<*>>,
|
||||
ctx: CastingContext
|
||||
): Triple<RenderedSpell, Int, List<ParticleSpray>> {
|
||||
val target = args.getBlockPos(0, argc)
|
||||
val target = args.getChecked<Vec3>(0, argc)
|
||||
ctx.assertVecInRange(target)
|
||||
|
||||
return Triple(
|
||||
Spell(target),
|
||||
2 * ManaConstants.CRYSTAL_UNIT,
|
||||
listOf(ParticleSpray.burst(Vec3.atCenterOf(target), 3.0))
|
||||
listOf(ParticleSpray.burst(target, 3.0))
|
||||
)
|
||||
}
|
||||
|
||||
const val MAX_DESTROY_COUNT = 1024
|
||||
|
||||
private data class Spell(val basePos: BlockPos) : RenderedSpell {
|
||||
private data class Spell(val target: Vec3) : RenderedSpell {
|
||||
|
||||
override fun cast(ctx: CastingContext) {
|
||||
val basePos = BlockPos(target)
|
||||
|
||||
// Try draining from fluid handlers first, and if so, don't do the normal behavior
|
||||
if (ctx.canEditBlockAt(basePos)) {
|
||||
if (IXplatAbstractions.INSTANCE.drainAllFluid(ctx.world, basePos)) {
|
||||
return
|
||||
} else {
|
||||
val state = ctx.world.getBlockState(basePos)
|
||||
if (state.block is AbstractCauldronBlock && state.block != Blocks.CAULDRON) {
|
||||
ctx.world.setBlock(basePos, Blocks.CAULDRON.defaultBlockState(), 3)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// SpongeBlock.java
|
||||
val todo = ArrayDeque<BlockPos>()
|
||||
val seen = HashSet<BlockPos>()
|
||||
|
@ -51,12 +63,7 @@ object OpDestroyWater : SpellAction {
|
|||
var successes = 0
|
||||
while (todo.isNotEmpty() && successes <= MAX_DESTROY_COUNT) {
|
||||
val here = todo.removeFirst()
|
||||
val distFromFocus =
|
||||
ctx.caster.position().distanceToSqr(Vec3.atCenterOf(here))
|
||||
if (distFromFocus < Action.MAX_DISTANCE * Action.MAX_DISTANCE
|
||||
&& seen.add(here)
|
||||
&& ctx.world.mayInteract(ctx.caster, here)
|
||||
) {
|
||||
if (ctx.canEditBlockAt(here) && seen.add(here)) {
|
||||
// never seen this pos in my life
|
||||
val fluid = ctx.world.getFluidState(here)
|
||||
if (fluid != Fluids.EMPTY.defaultFluidState()) {
|
||||
|
@ -115,9 +122,9 @@ object OpDestroyWater : SpellAction {
|
|||
if (successes > 0) {
|
||||
ctx.world.playSound(
|
||||
null,
|
||||
basePos.x.toDouble(),
|
||||
basePos.y.toDouble(),
|
||||
basePos.z.toDouble(),
|
||||
target.x,
|
||||
target.y,
|
||||
target.z,
|
||||
SoundEvents.FIRE_EXTINGUISH,
|
||||
SoundSource.BLOCKS,
|
||||
1.0f,
|
|
@ -4,6 +4,7 @@ import at.petrak.hexcasting.api.misc.ManaConstants
|
|||
import at.petrak.hexcasting.api.spell.ParticleSpray
|
||||
import at.petrak.hexcasting.api.spell.RenderedSpell
|
||||
import at.petrak.hexcasting.api.spell.SpellAction
|
||||
import at.petrak.hexcasting.api.spell.*
|
||||
import at.petrak.hexcasting.api.spell.casting.CastingContext
|
||||
import at.petrak.hexcasting.api.spell.getBlockPos
|
||||
import at.petrak.hexcasting.api.spell.iota.Iota
|
||||
|
@ -36,8 +37,9 @@ object OpEdifySapling : SpellAction {
|
|||
private data class Spell(val pos: BlockPos) : RenderedSpell {
|
||||
override fun cast(ctx: CastingContext) {
|
||||
val blockstate = ctx.world.getBlockState(pos)
|
||||
if (!ctx.world.mayInteract(ctx.caster, pos) ||
|
||||
!IXplatAbstractions.INSTANCE.isBreakingAllowed(ctx.world, pos, blockstate, ctx.caster))
|
||||
if (!ctx.canEditBlockAt(pos) ||
|
||||
!IXplatAbstractions.INSTANCE.isBreakingAllowed(ctx.world, pos, blockstate, ctx.caster)
|
||||
)
|
||||
return
|
||||
|
||||
val bs = ctx.world.getBlockState(pos)
|
||||
|
|
|
@ -8,6 +8,7 @@ import at.petrak.hexcasting.api.spell.SpellAction
|
|||
import at.petrak.hexcasting.api.spell.casting.CastingContext
|
||||
import at.petrak.hexcasting.api.spell.mishaps.MishapBadOffhandItem
|
||||
import at.petrak.hexcasting.xplat.IXplatAbstractions
|
||||
import net.minecraft.world.item.ItemStack
|
||||
|
||||
class OpErase : SpellAction {
|
||||
override val argc = 0
|
||||
|
@ -33,22 +34,15 @@ class OpErase : SpellAction {
|
|||
}
|
||||
|
||||
return Triple(
|
||||
Spell,
|
||||
Spell(handStack),
|
||||
ManaConstants.DUST_UNIT, listOf()
|
||||
)
|
||||
}
|
||||
|
||||
private object Spell : RenderedSpell {
|
||||
private data class Spell(val stack: ItemStack) : RenderedSpell {
|
||||
override fun cast(ctx: CastingContext) {
|
||||
val (handStack) = ctx.getHeldItemToOperateOn {
|
||||
val hexHolder = IXplatAbstractions.INSTANCE.findHexHolder(it)
|
||||
val datumHolder = IXplatAbstractions.INSTANCE.findDataHolder(it)
|
||||
|
||||
(hexHolder?.hasHex() == true) ||
|
||||
(datumHolder?.writeIota(null, true) == true)
|
||||
}
|
||||
val hexHolder = IXplatAbstractions.INSTANCE.findHexHolder(handStack)
|
||||
val datumHolder = IXplatAbstractions.INSTANCE.findDataHolder(handStack)
|
||||
val hexHolder = IXplatAbstractions.INSTANCE.findHexHolder(stack)
|
||||
val datumHolder = IXplatAbstractions.INSTANCE.findDataHolder(stack)
|
||||
|
||||
if (hexHolder?.hasHex() == true)
|
||||
hexHolder.clearHex()
|
||||
|
|
|
@ -19,7 +19,8 @@ class OpExplode(val fire: Boolean) : SpellAction {
|
|||
val pos = args.getVec3(0, argc)
|
||||
val strength = args.getPositiveDoubleUnder(1, 10.0, argc)
|
||||
ctx.assertVecInRange(pos)
|
||||
val cost = ManaConstants.DUST_UNIT * (3 * strength + if (fire) 0.125 else 1.0)
|
||||
val clampedStrength = Mth.clamp(strength, 0.0, 10.0)
|
||||
val cost = ManaConstants.DUST_UNIT * (3 * clampedStrength + if (fire) 1.0 else 0.125)
|
||||
return Triple(
|
||||
Spell(pos, strength, this.fire),
|
||||
cost.toInt(),
|
||||
|
@ -29,7 +30,8 @@ class OpExplode(val fire: Boolean) : SpellAction {
|
|||
|
||||
private data class Spell(val pos: Vec3, val strength: Double, val fire: Boolean) : RenderedSpell {
|
||||
override fun cast(ctx: CastingContext) {
|
||||
if (!ctx.world.mayInteract(ctx.caster, BlockPos(pos)))
|
||||
// TODO: you can use this to explode things *outside* of the worldborder?
|
||||
if (!ctx.canEditBlockAt(BlockPos(pos)))
|
||||
return
|
||||
|
||||
ctx.world.explode(
|
||||
|
|
|
@ -46,14 +46,9 @@ object OpExtinguish : SpellAction {
|
|||
var successes = 0
|
||||
while (todo.isNotEmpty() && successes <= MAX_DESTROY_COUNT) {
|
||||
val here = todo.removeFirst()
|
||||
val distFromFocus =
|
||||
ctx.caster.position().distanceToSqr(Vec3.atCenterOf(here))
|
||||
val distFromTarget = target.distSqr(here) // max distance to prevent runaway shenanigans
|
||||
if (distFromFocus < Action.MAX_DISTANCE * Action.MAX_DISTANCE
|
||||
&& seen.add(here)
|
||||
&& distFromTarget < 10 * 10
|
||||
&& ctx.world.mayInteract(ctx.caster, here)
|
||||
) {
|
||||
val distFromTarget =
|
||||
target.distanceTo(Vec3.atCenterOf(here)) // max distance to prevent runaway shenanigans
|
||||
if (ctx.canEditBlockAt(here) && distFromTarget < 10 && seen.add(here)) {
|
||||
// never seen this pos in my life
|
||||
val blockstate = ctx.world.getBlockState(here)
|
||||
if (IXplatAbstractions.INSTANCE.isBreakingAllowed(ctx.world, here, blockstate, ctx.caster)) {
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
package at.petrak.hexcasting.common.casting.operators.spells
|
||||
|
||||
import at.petrak.hexcasting.api.HexAPI
|
||||
import at.petrak.hexcasting.api.misc.ManaConstants
|
||||
import at.petrak.hexcasting.api.spell.ParticleSpray
|
||||
import at.petrak.hexcasting.api.spell.RenderedSpell
|
||||
|
@ -13,7 +12,7 @@ import at.petrak.hexcasting.xplat.IXplatAbstractions
|
|||
import net.minecraft.core.BlockPos
|
||||
import net.minecraft.core.Direction
|
||||
import net.minecraft.world.InteractionHand
|
||||
import net.minecraft.world.item.FireChargeItem
|
||||
import net.minecraft.world.item.Item
|
||||
import net.minecraft.world.item.ItemStack
|
||||
import net.minecraft.world.item.Items
|
||||
import net.minecraft.world.phys.BlockHitResult
|
||||
|
@ -37,27 +36,26 @@ object OpIgnite : SpellAction {
|
|||
|
||||
private data class Spell(val pos: BlockPos) : RenderedSpell {
|
||||
override fun cast(ctx: CastingContext) {
|
||||
// steal petra code that steals bucket code
|
||||
val maxwell = Items.FIRE_CHARGE
|
||||
|
||||
// TODO should we do these checks in the action part of the spell
|
||||
if (!ctx.world.mayInteract(ctx.caster, pos) || !IXplatAbstractions.INSTANCE.isPlacingAllowed(ctx.world, pos, ItemStack(maxwell), ctx.caster))
|
||||
if (!ctx.canEditBlockAt(pos))
|
||||
return
|
||||
|
||||
if (maxwell is FireChargeItem) {
|
||||
// help
|
||||
maxwell.useOn(
|
||||
UseOnContext(
|
||||
ctx.world,
|
||||
null,
|
||||
InteractionHand.MAIN_HAND,
|
||||
ItemStack(maxwell.asItem()),
|
||||
BlockHitResult(Vec3.atCenterOf(pos), Direction.UP, pos, false)
|
||||
)
|
||||
)
|
||||
} else {
|
||||
HexAPI.LOGGER.warn("Items.FIRE_CHARGE wasn't a FireChargeItem?")
|
||||
// help
|
||||
if (!tryToClick(ctx, pos, Items.FIRE_CHARGE)) {
|
||||
tryToClick(ctx, pos, Items.FLINT_AND_STEEL)
|
||||
}
|
||||
}
|
||||
|
||||
fun tryToClick(ctx: CastingContext, pos: BlockPos, item: Item): Boolean {
|
||||
return IXplatAbstractions.INSTANCE.isPlacingAllowed(ctx.world, pos, ItemStack(item), ctx.caster) &&
|
||||
item.useOn(
|
||||
UseOnContext(
|
||||
ctx.world,
|
||||
null,
|
||||
InteractionHand.MAIN_HAND,
|
||||
ItemStack(item),
|
||||
BlockHitResult(target, Direction.UP, pos, false)
|
||||
)
|
||||
).consumesAction()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,6 +5,7 @@ import at.petrak.hexcasting.api.mod.HexItemTags
|
|||
import at.petrak.hexcasting.api.spell.ParticleSpray
|
||||
import at.petrak.hexcasting.api.spell.RenderedSpell
|
||||
import at.petrak.hexcasting.api.spell.SpellAction
|
||||
import at.petrak.hexcasting.api.spell.*
|
||||
import at.petrak.hexcasting.api.spell.casting.CastingContext
|
||||
import at.petrak.hexcasting.api.spell.getItemEntity
|
||||
import at.petrak.hexcasting.api.spell.iota.Iota
|
||||
|
@ -14,6 +15,7 @@ import at.petrak.hexcasting.api.utils.extractMana
|
|||
import at.petrak.hexcasting.api.utils.isManaItem
|
||||
import at.petrak.hexcasting.common.items.magic.ItemMediaHolder
|
||||
import at.petrak.hexcasting.common.lib.HexItems
|
||||
import net.minecraft.world.InteractionHand
|
||||
import net.minecraft.world.entity.item.ItemEntity
|
||||
import net.minecraft.world.item.ItemStack
|
||||
|
||||
|
@ -59,16 +61,13 @@ object OpMakeBattery : SpellAction {
|
|||
)
|
||||
}
|
||||
|
||||
return Triple(
|
||||
Spell(entity),
|
||||
ManaConstants.CRYSTAL_UNIT, listOf(ParticleSpray.burst(entity.position(), 0.5))
|
||||
)
|
||||
return Triple(Spell(entity, hand),
|
||||
ManaConstants.CRYSTAL_UNIT, listOf(ParticleSpray.burst(entity.position(), 0.5)))
|
||||
}
|
||||
|
||||
private data class Spell(val itemEntity: ItemEntity) : RenderedSpell {
|
||||
private data class Spell(val itemEntity: ItemEntity, val hand: InteractionHand) : RenderedSpell {
|
||||
override fun cast(ctx: CastingContext) {
|
||||
val (handStack, hand) = ctx.getHeldItemToOperateOn { it.`is`(HexItemTags.PHIAL_BASE) }
|
||||
if (handStack.`is`(HexItemTags.PHIAL_BASE) && itemEntity.isAlive) {
|
||||
if (itemEntity.isAlive) {
|
||||
val entityStack = itemEntity.item.copy()
|
||||
val manaAmt = extractMana(entityStack, drainForBatteries = true)
|
||||
if (manaAmt > 0) {
|
||||
|
|
|
@ -11,6 +11,7 @@ import at.petrak.hexcasting.api.utils.isManaItem
|
|||
import at.petrak.hexcasting.common.items.magic.ItemPackagedHex
|
||||
import at.petrak.hexcasting.xplat.IXplatAbstractions
|
||||
import net.minecraft.world.entity.item.ItemEntity
|
||||
import net.minecraft.world.item.ItemStack
|
||||
|
||||
class OpMakePackagedSpell<T : ItemPackagedHex>(val itemType: T, val cost: Int) : SpellAction {
|
||||
override val argc = 2
|
||||
|
@ -49,13 +50,12 @@ class OpMakePackagedSpell<T : ItemPackagedHex>(val itemType: T, val cost: Int) :
|
|||
if (trueName != null)
|
||||
throw MishapOthersName(trueName)
|
||||
|
||||
return Triple(Spell(entity, patterns), cost, listOf(ParticleSpray.burst(entity.position(), 0.5)))
|
||||
return Triple(Spell(entity, patterns, handStack), cost, listOf(ParticleSpray.burst(entity.position(), 0.5)))
|
||||
}
|
||||
|
||||
private inner class Spell(val itemEntity: ItemEntity, val patterns: List<Iota>) : RenderedSpell {
|
||||
private inner class Spell(val itemEntity: ItemEntity, val patterns: List<Iota>, val stack: ItemStack) : RenderedSpell {
|
||||
override fun cast(ctx: CastingContext) {
|
||||
val (handStack) = ctx.getHeldItemToOperateOn { it.`is`(itemType) }
|
||||
val hexHolder = IXplatAbstractions.INSTANCE.findHexHolder(handStack)
|
||||
val hexHolder = IXplatAbstractions.INSTANCE.findHexHolder(stack)
|
||||
if (hexHolder != null
|
||||
&& !hexHolder.hasHex()
|
||||
&& itemEntity.isAlive
|
||||
|
|
|
@ -31,12 +31,8 @@ object OpPlaceBlock : SpellAction {
|
|||
val pos = args.getBlockPos(0, argc)
|
||||
ctx.assertVecInRange(pos)
|
||||
|
||||
if (!ctx.world.mayInteract(ctx.caster, pos))
|
||||
return null
|
||||
|
||||
|
||||
val blockHit = BlockHitResult(
|
||||
Vec3.ZERO, ctx.caster.direction, pos, false
|
||||
target, ctx.caster.direction, pos, false
|
||||
)
|
||||
val itemUseCtx = UseOnContext(ctx.caster, ctx.castingHand, blockHit)
|
||||
val placeContext = BlockPlaceContext(itemUseCtx)
|
||||
|
@ -54,18 +50,16 @@ object OpPlaceBlock : SpellAction {
|
|||
|
||||
private data class Spell(val pos: BlockPos) : RenderedSpell {
|
||||
override fun cast(ctx: CastingContext) {
|
||||
if (!ctx.world.mayInteract(ctx.caster, pos))
|
||||
if (!ctx.canEditBlockAt(pos))
|
||||
return
|
||||
|
||||
val blockHit = BlockHitResult(
|
||||
Vec3.ZERO, ctx.caster.direction, pos, false
|
||||
vec, ctx.caster.direction, pos, false
|
||||
)
|
||||
|
||||
val bstate = ctx.world.getBlockState(pos)
|
||||
val placeeSlot = ctx.getOperativeSlot { it.item is BlockItem }
|
||||
if (placeeSlot != null) {
|
||||
val placeeStack = ctx.caster.inventory.getItem(placeeSlot).copy()
|
||||
|
||||
val placeeStack = ctx.getOperativeSlot { it.item is BlockItem }?.copy()
|
||||
if (placeeStack != null) {
|
||||
if (!IXplatAbstractions.INSTANCE.isPlacingAllowed(ctx.world, pos, placeeStack, ctx.caster))
|
||||
return
|
||||
|
||||
|
@ -81,13 +75,12 @@ object OpPlaceBlock : SpellAction {
|
|||
val itemUseCtx = UseOnContext(ctx.caster, ctx.castingHand, blockHit)
|
||||
val placeContext = BlockPlaceContext(itemUseCtx)
|
||||
if (bstate.canBeReplaced(placeContext)) {
|
||||
val placee = placeeStack.item as BlockItem
|
||||
if (ctx.withdrawItem(placee, 1, false)) {
|
||||
if (ctx.withdrawItem(placeeStack, 1, false)) {
|
||||
val res = spoofedStack.useOn(placeContext)
|
||||
|
||||
ctx.caster.setItemInHand(ctx.castingHand, oldStack)
|
||||
if (res != InteractionResult.FAIL) {
|
||||
ctx.withdrawItem(placee, 1, true)
|
||||
ctx.withdrawItem(placeeStack, 1, true)
|
||||
|
||||
ctx.world.playSound(
|
||||
ctx.caster,
|
||||
|
|
|
@ -44,8 +44,10 @@ class OpPotionEffect(
|
|||
private class Spell(val effect: MobEffect, val target: LivingEntity, val duration: Double, val potency: Double) :
|
||||
RenderedSpell {
|
||||
override fun cast(ctx: CastingContext) {
|
||||
val effectInst = MobEffectInstance(effect, (duration * 20).toInt(), potency.toInt() - 1)
|
||||
target.addEffect(effectInst)
|
||||
if (duration > 1.0 / 20.0) {
|
||||
val effectInst = MobEffectInstance(effect, (duration * 20).toInt(), potency.toInt() - 1)
|
||||
target.addEffect(effectInst)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -13,6 +13,7 @@ import at.petrak.hexcasting.api.utils.extractMana
|
|||
import at.petrak.hexcasting.api.utils.isManaItem
|
||||
import at.petrak.hexcasting.xplat.IXplatAbstractions
|
||||
import net.minecraft.world.entity.item.ItemEntity
|
||||
import net.minecraft.world.item.ItemStack
|
||||
|
||||
object OpRecharge : SpellAction {
|
||||
override val argc = 1
|
||||
|
@ -24,7 +25,7 @@ object OpRecharge : SpellAction {
|
|||
|
||||
val (handStack, hand) = ctx.getHeldItemToOperateOn {
|
||||
val mana = IXplatAbstractions.INSTANCE.findManaHolder(it)
|
||||
mana != null && mana.canRecharge() && mana.media /* doo doo da do doo */ < mana.maxMedia
|
||||
mana != null && mana.canRecharge() && mana.insertMana(-1, true) != 0
|
||||
}
|
||||
|
||||
val mana = IXplatAbstractions.INSTANCE.findManaHolder(handStack)
|
||||
|
@ -45,33 +46,28 @@ object OpRecharge : SpellAction {
|
|||
)
|
||||
}
|
||||
|
||||
if (mana.media >= mana.maxMedia)
|
||||
if (mana.insertMana(-1, true) == 0)
|
||||
return null
|
||||
|
||||
return Triple(
|
||||
Spell(entity),
|
||||
Spell(entity, handStack),
|
||||
ManaConstants.SHARD_UNIT,
|
||||
listOf(ParticleSpray.burst(entity.position(), 0.5))
|
||||
)
|
||||
}
|
||||
|
||||
private data class Spell(val itemEntity: ItemEntity) : RenderedSpell {
|
||||
private data class Spell(val itemEntity: ItemEntity, val stack: ItemStack) : RenderedSpell {
|
||||
override fun cast(ctx: CastingContext) {
|
||||
val (handStack) = ctx.getHeldItemToOperateOn {
|
||||
val mana = IXplatAbstractions.INSTANCE.findManaHolder(it)
|
||||
mana != null && mana.canRecharge() && mana.media < mana.maxMedia
|
||||
}
|
||||
val mana = IXplatAbstractions.INSTANCE.findManaHolder(handStack)
|
||||
val mana = IXplatAbstractions.INSTANCE.findManaHolder(stack)
|
||||
|
||||
if (mana != null && itemEntity.isAlive) {
|
||||
val entityStack = itemEntity.item.copy()
|
||||
|
||||
val maxMana = mana.maxMedia
|
||||
val existingMana = mana.media
|
||||
val emptySpace = mana.insertMana(-1, true)
|
||||
|
||||
val manaAmt = extractMana(entityStack, maxMana - existingMana)
|
||||
val manaAmt = extractMana(entityStack, emptySpace)
|
||||
|
||||
mana.media = manaAmt + existingMana
|
||||
mana.insertMana(manaAmt, false)
|
||||
|
||||
itemEntity.item = entityStack
|
||||
if (entityStack.isEmpty)
|
||||
|
|
|
@ -28,7 +28,7 @@ object OpTheOnlyReasonAnyoneDownloadedPsi : SpellAction {
|
|||
return Triple(
|
||||
Spell(target),
|
||||
(ManaConstants.DUST_UNIT * 1.125).toInt(),
|
||||
listOf(ParticleSpray.burst(Vec3.atCenterOf(target), 1.0))
|
||||
listOf(ParticleSpray.burst(Vec3.atCenterOf(BlockPos(target)), 1.0))
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
@ -40,6 +40,9 @@ object OpBrainsweep : SpellAction {
|
|||
|
||||
val state = ctx.world.getBlockState(pos)
|
||||
|
||||
if (!ctx.canEditBlockAt(pos))
|
||||
return null
|
||||
|
||||
val recman = ctx.world.recipeManager
|
||||
val recipes = recman.getAllRecipesFor(HexRecipeStuffRegistry.BRAINSWEEP_TYPE)
|
||||
val recipe = recipes.find { it.matches(state, sacrifice) }
|
||||
|
@ -60,6 +63,7 @@ object OpBrainsweep : SpellAction {
|
|||
) : RenderedSpell {
|
||||
override fun cast(ctx: CastingContext) {
|
||||
ctx.world.setBlockAndUpdate(pos, BrainsweepRecipe.copyProperties(state, recipe.result))
|
||||
|
||||
Brainsweeping.brainsweep(sacrifice)
|
||||
if (HexConfig.server().doVillagersTakeOffenseAtMindMurder()) {
|
||||
sacrifice.tellWitnessesThatIWasMurdered(ctx.caster)
|
||||
|
|
|
@ -1,66 +0,0 @@
|
|||
package at.petrak.hexcasting.common.casting.operators.spells.great
|
||||
|
||||
import at.petrak.hexcasting.api.HexAPI
|
||||
import at.petrak.hexcasting.api.misc.ManaConstants
|
||||
import at.petrak.hexcasting.api.spell.ParticleSpray
|
||||
import at.petrak.hexcasting.api.spell.RenderedSpell
|
||||
import at.petrak.hexcasting.api.spell.SpellAction
|
||||
import at.petrak.hexcasting.api.spell.casting.CastingContext
|
||||
import at.petrak.hexcasting.api.spell.getBlockPos
|
||||
import at.petrak.hexcasting.api.spell.iota.Iota
|
||||
import at.petrak.hexcasting.xplat.IXplatAbstractions
|
||||
import net.minecraft.core.BlockPos
|
||||
import net.minecraft.world.item.BucketItem
|
||||
import net.minecraft.world.item.ItemStack
|
||||
import net.minecraft.world.item.Items
|
||||
import net.minecraft.world.level.block.AbstractCauldronBlock
|
||||
import net.minecraft.world.level.block.Blocks
|
||||
import net.minecraft.world.level.material.Fluids
|
||||
import net.minecraft.world.phys.Vec3
|
||||
|
||||
object OpCreateLava : SpellAction {
|
||||
override val argc = 1
|
||||
override val isGreat = true
|
||||
override fun execute(
|
||||
args: List<Iota>,
|
||||
ctx: CastingContext
|
||||
): Triple<RenderedSpell, Int, List<ParticleSpray>> {
|
||||
val target = args.getBlockPos(0, argc)
|
||||
ctx.assertVecInRange(target)
|
||||
|
||||
return Triple(
|
||||
Spell(target),
|
||||
ManaConstants.CRYSTAL_UNIT,
|
||||
listOf(ParticleSpray.burst(Vec3.atCenterOf(BlockPos(target)), 1.0)),
|
||||
)
|
||||
}
|
||||
|
||||
private data class Spell(val pos: BlockPos) : RenderedSpell {
|
||||
override fun cast(ctx: CastingContext) {
|
||||
if (!ctx.world.mayInteract(ctx.caster, pos) || !IXplatAbstractions.INSTANCE.isPlacingAllowed(ctx.world, pos, ItemStack(Items.LAVA_BUCKET), ctx.caster))
|
||||
return
|
||||
|
||||
val state = ctx.world.getBlockState(pos)
|
||||
|
||||
if (state.block is AbstractCauldronBlock)
|
||||
ctx.world.setBlock(pos, Blocks.LAVA_CAULDRON.defaultBlockState(), 3)
|
||||
else if (!IXplatAbstractions.INSTANCE.tryPlaceFluid(
|
||||
ctx.world,
|
||||
ctx.castingHand,
|
||||
pos,
|
||||
ItemStack(Items.LAVA_BUCKET),
|
||||
Fluids.LAVA
|
||||
)
|
||||
) {
|
||||
// Just steal bucket code lmao
|
||||
val charlie = Items.LAVA_BUCKET
|
||||
if (charlie is BucketItem) {
|
||||
// make the player null so we don't give them a usage statistic for example
|
||||
charlie.emptyContents(null, ctx.world, pos, null)
|
||||
} else {
|
||||
HexAPI.LOGGER.warn("Items.LAVA_BUCKET wasn't a BucketItem?")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -6,6 +6,7 @@ import at.petrak.hexcasting.api.spell.*
|
|||
import at.petrak.hexcasting.api.spell.casting.CastingContext
|
||||
import at.petrak.hexcasting.api.spell.iota.Iota
|
||||
import at.petrak.hexcasting.xplat.IXplatAbstractions
|
||||
import net.minecraft.server.level.ServerLevel
|
||||
import net.minecraft.server.level.ServerPlayer
|
||||
import net.minecraft.world.entity.LivingEntity
|
||||
import net.minecraft.world.phys.Vec3
|
||||
|
@ -27,7 +28,7 @@ object OpFlight : SpellAction {
|
|||
val time = (timeRaw * 20.0).roundToInt()
|
||||
return Triple(
|
||||
Spell(target, time, radiusRaw, ctx.position),
|
||||
ManaConstants.DUST_UNIT * (0.25 * (timeRaw * radiusRaw + 1.0)).roundToInt(),
|
||||
(ManaConstants.DUST_UNIT * 0.25 * (timeRaw * radiusRaw + 1.0)).roundToInt(),
|
||||
listOf(ParticleSpray(target.position(), Vec3(0.0, 2.0, 0.0), 0.0, 0.1))
|
||||
)
|
||||
}
|
||||
|
@ -97,4 +98,10 @@ object OpFlight : SpellAction {
|
|||
}
|
||||
|
||||
}
|
||||
|
||||
fun tickAllPlayers(world: ServerLevel) {
|
||||
for (player in world.players()) {
|
||||
tickDownFlight(player)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,10 +6,12 @@ import at.petrak.hexcasting.api.spell.casting.CastingContext
|
|||
import at.petrak.hexcasting.api.spell.iota.Iota
|
||||
import at.petrak.hexcasting.api.spell.mishaps.MishapImmuneEntity
|
||||
import at.petrak.hexcasting.api.spell.mishaps.MishapLocationTooFarAway
|
||||
import at.petrak.hexcasting.common.lib.HexEntityTags
|
||||
import at.petrak.hexcasting.common.network.MsgBlinkAck
|
||||
import at.petrak.hexcasting.xplat.IXplatAbstractions
|
||||
import net.minecraft.server.level.ServerPlayer
|
||||
import net.minecraft.world.entity.Entity
|
||||
import net.minecraft.world.item.enchantment.EnchantmentHelper
|
||||
import net.minecraft.world.phys.Vec3
|
||||
|
||||
// TODO while we're making breaking changes I *really* want to have the vector in the entity's local space
|
||||
|
@ -25,7 +27,7 @@ object OpTeleport : SpellAction {
|
|||
val delta = args.getVec3(1, argc)
|
||||
ctx.assertEntityInRange(teleportee)
|
||||
|
||||
if (!teleportee.canChangeDimensions())
|
||||
if (!teleportee.canChangeDimensions() || teleportee.type.`is`(HexEntityTags.CANNOT_TELEPORT))
|
||||
throw MishapImmuneEntity(teleportee)
|
||||
|
||||
val targetPos = teleportee.position().add(delta)
|
||||
|
@ -49,10 +51,7 @@ object OpTeleport : SpellAction {
|
|||
|
||||
// TODO make this not a magic number (config?)
|
||||
if (distance < 32768.0) {
|
||||
teleportee.setPos(teleportee.position().add(delta))
|
||||
if (teleportee is ServerPlayer) {
|
||||
IXplatAbstractions.INSTANCE.sendPacketToPlayer(teleportee, MsgBlinkAck(delta))
|
||||
}
|
||||
teleportRespectSticky(teleportee, delta)
|
||||
}
|
||||
|
||||
if (teleportee is ServerPlayer && teleportee == ctx.caster) {
|
||||
|
@ -65,6 +64,9 @@ object OpTeleport : SpellAction {
|
|||
// having to rearrange those. Also it makes sense for LORE REASONS probably, since the caster is more
|
||||
// aware of items they use often.
|
||||
for (armorItem in teleportee.inventory.armor) {
|
||||
if (EnchantmentHelper.hasBindingCurse(armorItem))
|
||||
continue
|
||||
|
||||
if (Math.random() < baseDropChance * 0.25) {
|
||||
teleportee.drop(armorItem.copy(), true, false)
|
||||
armorItem.shrink(armorItem.count)
|
||||
|
@ -83,6 +85,39 @@ object OpTeleport : SpellAction {
|
|||
// we also don't drop the offhand just to be nice
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun teleportRespectSticky(teleportee: Entity, delta: Vec3) {
|
||||
val base = teleportee.rootVehicle
|
||||
|
||||
val playersToUpdate = mutableListOf<ServerPlayer>()
|
||||
val indirect = base.indirectPassengers
|
||||
|
||||
val sticky = indirect.any { it.type.`is`(HexEntityTags.STICKY_TELEPORTERS) }
|
||||
val cannotSticky = indirect.none { it.type.`is`(HexEntityTags.CANNOT_TELEPORT) }
|
||||
if (sticky && cannotSticky)
|
||||
return
|
||||
|
||||
if (sticky) {
|
||||
// this handles teleporting the passengers
|
||||
val target = base.position().add(delta)
|
||||
base.teleportTo(target.x, target.y, target.z)
|
||||
indirect
|
||||
.filterIsInstance<ServerPlayer>()
|
||||
.forEach(playersToUpdate::add)
|
||||
} else {
|
||||
// Break it into two stacks
|
||||
teleportee.stopRiding()
|
||||
teleportee.passengers.forEach(Entity::stopRiding)
|
||||
teleportee.setPos(teleportee.position().add(delta))
|
||||
if (teleportee is ServerPlayer) {
|
||||
playersToUpdate.add(teleportee)
|
||||
}
|
||||
}
|
||||
|
||||
for (player in playersToUpdate) {
|
||||
player.connection.resetPosition()
|
||||
IXplatAbstractions.INSTANCE.sendPacketToPlayer(player, MsgBlinkAck(delta))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -30,6 +30,12 @@ class OpWeather(val rain: Boolean) : SpellAction {
|
|||
val w = ctx.world
|
||||
if (w.isRaining != rain) {
|
||||
w.levelData.isRaining = rain // i hex the rains down in minecraftia
|
||||
|
||||
if (rain) {
|
||||
w.setWeatherParameters(0, 6000, true, w.random.nextDouble() < 0.05)
|
||||
} else {
|
||||
w.setWeatherParameters(6000, 0, false, false)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -24,7 +24,7 @@ class OpCreateSentinel(val extendsRange: Boolean) : SpellAction {
|
|||
|
||||
return Triple(
|
||||
Spell(target, this.extendsRange),
|
||||
ManaConstants.DUST_UNIT,
|
||||
ManaConstants.DUST_UNIT * if (extendsRange) 2 else 1,
|
||||
listOf(ParticleSpray.burst(target, 2.0))
|
||||
)
|
||||
}
|
||||
|
|
|
@ -8,6 +8,8 @@ import at.petrak.hexcasting.api.spell.getPositiveInt
|
|||
import at.petrak.hexcasting.api.spell.iota.Iota
|
||||
import at.petrak.hexcasting.api.spell.mishaps.MishapNotEnoughArgs
|
||||
import it.unimi.dsi.fastutil.ints.IntArrayList
|
||||
import kotlin.math.abs
|
||||
import kotlin.math.roundToInt
|
||||
|
||||
// "lehmer code"
|
||||
object OpAlwinfyHasAscendedToABeingOfPureMath : Action {
|
||||
|
@ -43,8 +45,6 @@ object OpAlwinfyHasAscendedToABeingOfPureMath : Action {
|
|||
editTarget = editTarget.subList(1, editTarget.size)
|
||||
}
|
||||
|
||||
// val cost = (ln((strides.lastOrNull() ?: 0).toFloat()) * ManaConstants.DUST_UNIT).toInt()
|
||||
|
||||
return OperationResult(
|
||||
continuation,
|
||||
stack,
|
||||
|
|
|
@ -3,9 +3,13 @@ package at.petrak.hexcasting.common.casting.operators.stack
|
|||
import at.petrak.hexcasting.api.spell.ConstManaAction
|
||||
import at.petrak.hexcasting.api.spell.iota.Iota
|
||||
import at.petrak.hexcasting.api.spell.casting.CastingContext
|
||||
import at.petrak.hexcasting.api.utils.asTranslatedComponent
|
||||
import at.petrak.hexcasting.api.utils.lightPurple
|
||||
import it.unimi.dsi.fastutil.booleans.BooleanList
|
||||
import net.minecraft.network.chat.Component
|
||||
import net.minecraft.resources.ResourceLocation
|
||||
|
||||
class OpMask(val mask: BooleanList) : ConstManaAction {
|
||||
class OpMask(val mask: BooleanList, val key: ResourceLocation) : ConstManaAction {
|
||||
override val argc: Int
|
||||
get() = mask.size
|
||||
|
||||
|
@ -17,4 +21,7 @@ class OpMask(val mask: BooleanList) : ConstManaAction {
|
|||
}
|
||||
return out
|
||||
}
|
||||
|
||||
override val displayName: Component
|
||||
get() = "hexcasting.spell.$key".asTranslatedComponent(mask.map { if (it) '-' else 'v' }.joinToString("")).lightPurple
|
||||
}
|
||||
|
|
|
@ -8,6 +8,7 @@ import at.petrak.hexcasting.common.lib.HexItems;
|
|||
import com.mojang.brigadier.builder.LiteralArgumentBuilder;
|
||||
import net.minecraft.commands.CommandSourceStack;
|
||||
import net.minecraft.commands.Commands;
|
||||
import net.minecraft.commands.arguments.EntityArgument;
|
||||
import net.minecraft.commands.arguments.ResourceLocationArgument;
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
import net.minecraft.network.chat.Component;
|
||||
|
@ -15,96 +16,130 @@ import net.minecraft.resources.ResourceLocation;
|
|||
import net.minecraft.server.level.ServerPlayer;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
public class ListPatternsCommand {
|
||||
public static void add(LiteralArgumentBuilder<CommandSourceStack> cmd) {
|
||||
public static void register(LiteralArgumentBuilder<CommandSourceStack> cmd) {
|
||||
cmd.then(Commands.literal("patterns")
|
||||
.requires(dp -> dp.hasPermission(Commands.LEVEL_ADMINS))
|
||||
.then(Commands.literal("list").executes(ctx -> {
|
||||
.requires(dp -> dp.hasPermission(Commands.LEVEL_GAMEMASTERS))
|
||||
.then(Commands.literal("list")
|
||||
.executes(ctx -> list(ctx.getSource())))
|
||||
.then(Commands.literal("give")
|
||||
.then(Commands.argument("patternName", PatternResLocArgument.id())
|
||||
.executes(ctx ->
|
||||
giveOne(ctx.getSource(),
|
||||
getDefaultTarget(ctx.getSource()),
|
||||
ResourceLocationArgument.getId(ctx, "patternName"),
|
||||
PatternResLocArgument.getPattern(ctx, "patternName")))
|
||||
.then(Commands.argument("targets", EntityArgument.players())
|
||||
.executes(ctx ->
|
||||
giveOne(ctx.getSource(),
|
||||
EntityArgument.getPlayers(ctx, "targets"),
|
||||
ResourceLocationArgument.getId(ctx, "patternName"),
|
||||
PatternResLocArgument.getPattern(ctx, "patternName"))))))
|
||||
.then(Commands.literal("giveAll")
|
||||
.executes(ctx ->
|
||||
giveAll(ctx.getSource(),
|
||||
getDefaultTarget(ctx.getSource())))
|
||||
.then(Commands.argument("targets", EntityArgument.players())
|
||||
.executes(ctx ->
|
||||
giveAll(ctx.getSource(),
|
||||
EntityArgument.getPlayers(ctx, "targets")))))
|
||||
);
|
||||
}
|
||||
private static Collection<ServerPlayer> getDefaultTarget(CommandSourceStack source) {
|
||||
if (source.getEntity() instanceof ServerPlayer player) {
|
||||
return List.of(player);
|
||||
}
|
||||
return List.of();
|
||||
}
|
||||
|
||||
var lookup = PatternRegistry.getPerWorldPatterns(ctx.getSource().getLevel());
|
||||
var listing = lookup.entrySet()
|
||||
.stream()
|
||||
.sorted((a, b) -> compareResLoc(a.getValue().getFirst(), b.getValue().getFirst()))
|
||||
.toList();
|
||||
private static int list(CommandSourceStack source) {
|
||||
var lookup = PatternRegistry.getPerWorldPatterns(source.getLevel());
|
||||
var listing = lookup.entrySet()
|
||||
.stream()
|
||||
.sorted((a, b) -> compareResLoc(a.getValue().getFirst(), b.getValue().getFirst()))
|
||||
.toList();
|
||||
|
||||
ctx.getSource().sendSuccess(Component.translatable("command.hexcasting.pats.listing"), false);
|
||||
for (var pair : listing) {
|
||||
HexPattern hexPattern = HexPattern.fromAngles(pair.getKey(), pair.getValue().getSecond());
|
||||
ctx.getSource().sendSuccess(Component.literal(pair.getValue().getFirst().toString())
|
||||
.append(": ")
|
||||
.append(PatternIota.display(hexPattern)), false);
|
||||
}
|
||||
source.sendSuccess(new TranslatableComponent("command.hexcasting.pats.listing"), false);
|
||||
for (var pair : listing) {
|
||||
source.sendSuccess(new TextComponent(pair.getValue().getFirst().toString())
|
||||
.append(": ")
|
||||
.append(SpellDatum.make(HexPattern.fromAngles(pair.getKey(), pair.getValue().getSecond()))
|
||||
.display()), false);
|
||||
}
|
||||
|
||||
|
||||
return lookup.size();
|
||||
}))
|
||||
.then(Commands.literal("give")
|
||||
.then(Commands.argument("patternName", PatternResLocArgument.id()).executes(ctx -> {
|
||||
var sender = ctx.getSource().getEntity();
|
||||
if (sender instanceof ServerPlayer player) {
|
||||
var targetId = ResourceLocationArgument.getId(ctx, "patternName");
|
||||
var pat = PatternResLocArgument.getPattern(ctx, "patternName");
|
||||
return lookup.size();
|
||||
}
|
||||
|
||||
private static int giveAll(CommandSourceStack source, Collection<ServerPlayer> targets) {
|
||||
if (!targets.isEmpty()) {
|
||||
var lookup = PatternRegistry.getPerWorldPatterns(source.getLevel());
|
||||
|
||||
var tag = new CompoundTag();
|
||||
tag.putString(ItemScroll.TAG_OP_ID, targetId.toString());
|
||||
tag.put(ItemScroll.TAG_PATTERN,
|
||||
pat.serializeToNBT());
|
||||
lookup.forEach((pattern, entry) -> {
|
||||
var opId = entry.component1();
|
||||
var startDir = entry.component2();
|
||||
|
||||
var stack = new ItemStack(HexItems.SCROLL_LARGE);
|
||||
stack.setTag(tag);
|
||||
var tag = new CompoundTag();
|
||||
tag.putString(ItemScroll.TAG_OP_ID, opId.toString());
|
||||
tag.put(ItemScroll.TAG_PATTERN,
|
||||
HexPattern.fromAngles(pattern, startDir).serializeToNBT());
|
||||
|
||||
ctx.getSource().sendSuccess(
|
||||
Component.translatable(
|
||||
"command.hexcasting.pats.specific.success",
|
||||
stack.getDisplayName(),
|
||||
targetId),
|
||||
true);
|
||||
var stack = new ItemStack(HexItems.SCROLL_LARGE);
|
||||
stack.setTag(tag);
|
||||
|
||||
var stackEntity = player.drop(stack, false);
|
||||
if (stackEntity != null) {
|
||||
stackEntity.setNoPickUpDelay();
|
||||
stackEntity.setOwner(player.getUUID());
|
||||
}
|
||||
|
||||
return 1;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
for (var player : targets) {
|
||||
var stackEntity = player.drop(stack, false);
|
||||
if (stackEntity != null) {
|
||||
stackEntity.setNoPickUpDelay();
|
||||
stackEntity.setOwner(player.getUUID());
|
||||
}
|
||||
)))
|
||||
.then(Commands.literal("giveAll").executes(ctx -> {
|
||||
var sender = ctx.getSource().getEntity();
|
||||
if (sender instanceof ServerPlayer player) {
|
||||
var lookup = PatternRegistry.getPerWorldPatterns(ctx.getSource().getLevel());
|
||||
|
||||
lookup.forEach((pattern, entry) -> {
|
||||
var opId = entry.getFirst();
|
||||
var startDir = entry.getSecond();
|
||||
|
||||
var tag = new CompoundTag();
|
||||
tag.putString(ItemScroll.TAG_OP_ID, opId.toString());
|
||||
tag.put(ItemScroll.TAG_PATTERN,
|
||||
HexPattern.fromAngles(pattern, startDir).serializeToNBT());
|
||||
|
||||
var stack = new ItemStack(HexItems.SCROLL_LARGE);
|
||||
stack.setTag(tag);
|
||||
|
||||
var stackEntity = player.drop(stack, false);
|
||||
if (stackEntity != null) {
|
||||
stackEntity.setNoPickUpDelay();
|
||||
stackEntity.setOwner(player.getUUID());
|
||||
}
|
||||
});
|
||||
|
||||
ctx.getSource().sendSuccess(
|
||||
Component.translatable("command.hexcasting.pats.all", lookup.size()), true);
|
||||
return lookup.size();
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}))
|
||||
);
|
||||
});
|
||||
|
||||
source.sendSuccess(
|
||||
new TranslatableComponent("command.hexcasting.pats.all",
|
||||
lookup.size(),
|
||||
targets.size() == 1 ? targets.iterator().next().getDisplayName() : targets.size()),
|
||||
true);
|
||||
return lookup.size();
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
private static int giveOne(CommandSourceStack source, Collection<ServerPlayer> targets, ResourceLocation patternName, HexPattern pat) {
|
||||
if (!targets.isEmpty()) {
|
||||
var tag = new CompoundTag();
|
||||
tag.putString(ItemScroll.TAG_OP_ID, patternName.toString());
|
||||
tag.put(ItemScroll.TAG_PATTERN,
|
||||
pat.serializeToNBT());
|
||||
|
||||
var stack = new ItemStack(HexItems.SCROLL_LARGE);
|
||||
stack.setTag(tag);
|
||||
|
||||
source.sendSuccess(
|
||||
new TranslatableComponent(
|
||||
"command.hexcasting.pats.specific.success",
|
||||
stack.getDisplayName(),
|
||||
patternName,
|
||||
targets.size() == 1 ? targets.iterator().next().getDisplayName() : targets.size()),
|
||||
true);
|
||||
|
||||
for (var player : targets) {
|
||||
var stackEntity = player.drop(stack, false);
|
||||
if (stackEntity != null) {
|
||||
stackEntity.setNoPickUpDelay();
|
||||
stackEntity.setOwner(player.getUUID());
|
||||
}
|
||||
}
|
||||
|
||||
return targets.size();
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
private static int compareResLoc(ResourceLocation a, ResourceLocation b) {
|
||||
|
|
|
@ -1,29 +1,28 @@
|
|||
package at.petrak.hexcasting.common.items;
|
||||
|
||||
import at.petrak.hexcasting.annotations.SoftImplement;
|
||||
import at.petrak.hexcasting.api.misc.DiscoveryHandlers;
|
||||
import at.petrak.hexcasting.common.lib.HexItems;
|
||||
import at.petrak.hexcasting.common.network.MsgUpdateComparatorVisualsAck;
|
||||
import at.petrak.hexcasting.xplat.IXplatAbstractions;
|
||||
import com.mojang.datafixers.util.Pair;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.core.BlockSource;
|
||||
import net.minecraft.core.dispenser.OptionalDispenseItemBehavior;
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
import net.minecraft.sounds.SoundEvent;
|
||||
import net.minecraft.sounds.SoundEvents;
|
||||
import net.minecraft.stats.Stats;
|
||||
import net.minecraft.world.InteractionHand;
|
||||
import net.minecraft.world.InteractionResultHolder;
|
||||
import net.minecraft.world.entity.Entity;
|
||||
import net.minecraft.world.entity.EquipmentSlot;
|
||||
import net.minecraft.world.entity.Mob;
|
||||
import net.minecraft.world.entity.player.Player;
|
||||
import net.minecraft.world.item.ArmorItem;
|
||||
import net.minecraft.world.item.Item;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import net.minecraft.world.item.Wearable;
|
||||
import net.minecraft.world.level.Level;
|
||||
import net.minecraft.world.level.block.BeehiveBlock;
|
||||
import net.minecraft.world.level.block.Blocks;
|
||||
import net.minecraft.world.level.block.DispenserBlock;
|
||||
import net.minecraft.world.level.block.entity.BeehiveBlockEntity;
|
||||
import net.minecraft.world.level.block.state.properties.BlockStateProperties;
|
||||
import net.minecraft.world.phys.BlockHitResult;
|
||||
import net.minecraft.world.phys.HitResult;
|
||||
|
@ -35,6 +34,15 @@ import java.util.WeakHashMap;
|
|||
|
||||
public class ItemLens extends Item implements Wearable {
|
||||
|
||||
static {
|
||||
DiscoveryHandlers.addLensPredicate(player -> player.getItemBySlot(EquipmentSlot.MAINHAND).is(HexItems.SCRYING_LENS));
|
||||
DiscoveryHandlers.addLensPredicate(player -> player.getItemBySlot(EquipmentSlot.OFFHAND).is(HexItems.SCRYING_LENS));
|
||||
DiscoveryHandlers.addLensPredicate(player -> player.getItemBySlot(EquipmentSlot.HEAD).is(HexItems.SCRYING_LENS));
|
||||
|
||||
DiscoveryHandlers.addGridScaleModifier(player -> player.getItemBySlot(EquipmentSlot.MAINHAND).is(HexItems.SCRYING_LENS) ? 0.75f : 1);
|
||||
DiscoveryHandlers.addGridScaleModifier(player -> player.getItemBySlot(EquipmentSlot.OFFHAND).is(HexItems.SCRYING_LENS) ? 0.75f : 1);
|
||||
}
|
||||
|
||||
public ItemLens(Properties pProperties) {
|
||||
super(pProperties);
|
||||
DispenserBlock.registerBehavior(this, new OptionalDispenseItemBehavior() {
|
||||
|
@ -53,67 +61,62 @@ public class ItemLens extends Item implements Wearable {
|
|||
return EquipmentSlot.HEAD;
|
||||
}
|
||||
|
||||
@Override
|
||||
public InteractionResultHolder<ItemStack> use(Level world, Player player, InteractionHand hand) {
|
||||
ItemStack itemstack = player.getItemInHand(hand);
|
||||
EquipmentSlot equipmentslot = Mob.getEquipmentSlotForItem(itemstack);
|
||||
ItemStack stack = player.getItemBySlot(equipmentslot);
|
||||
if (stack.isEmpty()) {
|
||||
player.setItemSlot(equipmentslot, itemstack.copy());
|
||||
if (!world.isClientSide()) {
|
||||
player.awardStat(Stats.ITEM_USED.get(this));
|
||||
}
|
||||
|
||||
itemstack.setCount(0);
|
||||
return InteractionResultHolder.sidedSuccess(itemstack, world.isClientSide());
|
||||
} else {
|
||||
return InteractionResultHolder.fail(itemstack);
|
||||
public static void tickAllPlayers(ServerLevel world) {
|
||||
for (ServerPlayer player : world.players()) {
|
||||
tickLens(player);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void inventoryTick(ItemStack pStack, Level pLevel, Entity pEntity, int pSlotId, boolean pIsSelected) {
|
||||
if (!pLevel.isClientSide() && pEntity instanceof ServerPlayer player) {
|
||||
if (pStack == player.getItemBySlot(EquipmentSlot.HEAD) ||
|
||||
pStack == player.getItemBySlot(EquipmentSlot.MAINHAND) ||
|
||||
pStack == player.getItemBySlot(EquipmentSlot.OFFHAND)) {
|
||||
sendComparatorDataToClient(player);
|
||||
}
|
||||
public static void tickLens(Entity pEntity) {
|
||||
if (!pEntity.getLevel().isClientSide() && pEntity instanceof ServerPlayer player && DiscoveryHandlers.hasLens(player)) {
|
||||
sendComparatorDataToClient(player);
|
||||
}
|
||||
}
|
||||
|
||||
private static final Map<ServerPlayer, Pair<BlockPos, Integer>> comparatorDataMap = new WeakHashMap<>();
|
||||
private static final Map<ServerPlayer, Pair<BlockPos, Integer>> beeDataMap = new WeakHashMap<>();
|
||||
|
||||
private void sendComparatorDataToClient(ServerPlayer player) {
|
||||
private static void sendComparatorDataToClient(ServerPlayer player) {
|
||||
double reachAttribute = IXplatAbstractions.INSTANCE.getReachDistance(player);
|
||||
double distance = player.isCreative() ? reachAttribute : reachAttribute - 0.5;
|
||||
var hitResult = player.pick(distance, 0, false);
|
||||
if (hitResult.getType() == HitResult.Type.BLOCK) {
|
||||
var pos = ((BlockHitResult) hitResult).getBlockPos();
|
||||
var state = player.level.getBlockState(pos);
|
||||
|
||||
int bee = -1;
|
||||
|
||||
if (state.getBlock() instanceof BeehiveBlock && player.level.getBlockEntity(pos) instanceof BeehiveBlockEntity bees) {
|
||||
bee = bees.getOccupantCount();
|
||||
}
|
||||
|
||||
if (state.is(Blocks.COMPARATOR)) {
|
||||
syncComparatorValue(player, pos,
|
||||
state.getDirectSignal(player.level, pos, state.getValue(BlockStateProperties.HORIZONTAL_FACING)));
|
||||
state.getDirectSignal(player.level, pos, state.getValue(BlockStateProperties.HORIZONTAL_FACING)), bee);
|
||||
} else if (state.hasAnalogOutputSignal()) {
|
||||
syncComparatorValue(player, pos, state.getAnalogOutputSignal(player.level, pos));
|
||||
syncComparatorValue(player, pos, state.getAnalogOutputSignal(player.level, pos), bee);
|
||||
} else {
|
||||
syncComparatorValue(player, null, -1);
|
||||
syncComparatorValue(player, null, -1, bee);
|
||||
}
|
||||
} else {
|
||||
syncComparatorValue(player, null, -1);
|
||||
syncComparatorValue(player, null, -1, -1);
|
||||
}
|
||||
}
|
||||
|
||||
private void syncComparatorValue(ServerPlayer player, BlockPos pos, int value) {
|
||||
var previous = comparatorDataMap.get(player);
|
||||
if (value == -1) {
|
||||
if (previous != null) {
|
||||
private static void syncComparatorValue(ServerPlayer player, BlockPos pos, int comparator, int bee) {
|
||||
var previousComparator = comparatorDataMap.get(player);
|
||||
var previousBee = beeDataMap.get(player);
|
||||
if (comparator == -1 && bee == -1) {
|
||||
if (previousComparator != null || previousBee != null) {
|
||||
comparatorDataMap.remove(player);
|
||||
IXplatAbstractions.INSTANCE.sendPacketToPlayer(player, new MsgUpdateComparatorVisualsAck(null, -1));
|
||||
beeDataMap.remove(player);
|
||||
IXplatAbstractions.INSTANCE.sendPacketToPlayer(player, new MsgUpdateComparatorVisualsAck(null, -1, -1));
|
||||
}
|
||||
} else if (previous == null || (!pos.equals(previous.getFirst()) || value != previous.getSecond())) {
|
||||
comparatorDataMap.put(player, new Pair<>(pos, value));
|
||||
IXplatAbstractions.INSTANCE.sendPacketToPlayer(player, new MsgUpdateComparatorVisualsAck(pos, value));
|
||||
} else if (previousComparator == null || !pos.equals(previousComparator.getFirst()) || comparator != previousComparator.getSecond() ||
|
||||
previousBee == null || !pos.equals(previousBee.getFirst()) || bee != previousBee.getSecond()) {
|
||||
comparatorDataMap.put(player, new Pair<>(pos, comparator));
|
||||
beeDataMap.put(player, new Pair<>(pos, bee));
|
||||
IXplatAbstractions.INSTANCE.sendPacketToPlayer(player, new MsgUpdateComparatorVisualsAck(pos, comparator, bee));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,55 @@
|
|||
package at.petrak.hexcasting.common.items.magic;
|
||||
|
||||
import at.petrak.hexcasting.api.addldata.ManaHolder;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
|
||||
public record DebugUnlockerHolder(ItemStack creativeUnlocker) implements ManaHolder {
|
||||
@Override
|
||||
public int getMana() {
|
||||
return Integer.MAX_VALUE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMaxMana() {
|
||||
return Integer.MAX_VALUE - 1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setMana(int mana) {
|
||||
// NO-OP
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canRecharge() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canProvide() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getConsumptionPriority() {
|
||||
return 1000;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canConstructBattery() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int withdrawMana(int cost, boolean simulate) {
|
||||
ItemCreativeUnlocker.addToIntArray(creativeUnlocker, ItemCreativeUnlocker.TAG_EXTRACTIONS, cost);
|
||||
|
||||
return cost < 0 ? getMana() : cost;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int insertMana(int amount, boolean simulate) {
|
||||
ItemCreativeUnlocker.addToIntArray(creativeUnlocker, ItemCreativeUnlocker.TAG_INSERTIONS, amount);
|
||||
|
||||
return amount;
|
||||
}
|
||||
}
|
|
@ -1,9 +1,13 @@
|
|||
package at.petrak.hexcasting.common.items.magic;
|
||||
|
||||
import at.petrak.hexcasting.api.block.circle.BlockEntityAbstractImpetus;
|
||||
import at.petrak.hexcasting.api.item.ManaHolderItem;
|
||||
import at.petrak.hexcasting.api.misc.DiscoveryHandlers;
|
||||
import at.petrak.hexcasting.api.item.MediaHolderItem;
|
||||
import at.petrak.hexcasting.api.misc.ManaConstants;
|
||||
import at.petrak.hexcasting.api.utils.NBTHelper;
|
||||
import at.petrak.hexcasting.common.lib.HexItems;
|
||||
import at.petrak.hexcasting.common.lib.HexSounds;
|
||||
import net.minecraft.ChatFormatting;
|
||||
import net.minecraft.advancements.Advancement;
|
||||
import net.minecraft.locale.Language;
|
||||
|
@ -12,13 +16,17 @@ import net.minecraft.network.chat.MutableComponent;
|
|||
import net.minecraft.network.chat.TextColor;
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
import net.minecraft.sounds.SoundSource;
|
||||
import net.minecraft.util.Mth;
|
||||
import net.minecraft.world.InteractionResult;
|
||||
import net.minecraft.world.entity.Entity;
|
||||
import net.minecraft.world.entity.LivingEntity;
|
||||
import net.minecraft.world.item.Item;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import net.minecraft.world.item.TooltipFlag;
|
||||
import net.minecraft.world.item.context.UseOnContext;
|
||||
import net.minecraft.world.level.Level;
|
||||
import net.minecraft.world.level.block.entity.BlockEntity;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
@ -30,25 +38,74 @@ import static at.petrak.hexcasting.api.HexAPI.modLoc;
|
|||
|
||||
public class ItemCreativeUnlocker extends Item implements MediaHolderItem {
|
||||
|
||||
public static final String DISPLAY_MEDIA = "media";
|
||||
public static final String DISPLAY_PATTERNS = "patterns";
|
||||
|
||||
static {
|
||||
DiscoveryHandlers.addDebugItemDiscoverer((player, type) -> {
|
||||
for (ItemStack item : player.getInventory().items) {
|
||||
if (isDebug(item, type)) {
|
||||
return item;
|
||||
}
|
||||
}
|
||||
|
||||
// Technically possible with commands!
|
||||
for (ItemStack item : player.getInventory().armor) {
|
||||
if (isDebug(item, type)) {
|
||||
return item;
|
||||
}
|
||||
}
|
||||
|
||||
for (ItemStack item : player.getInventory().offhand) {
|
||||
if (isDebug(item, type)) {
|
||||
return item;
|
||||
}
|
||||
}
|
||||
return ItemStack.EMPTY;
|
||||
});
|
||||
|
||||
DiscoveryHandlers.addManaHolderDiscoverer(harness -> {
|
||||
var player = harness.getCtx().getCaster();
|
||||
if (!player.isCreative())
|
||||
return List.of();
|
||||
|
||||
ItemStack stack = DiscoveryHandlers.findDebugItem(player, DISPLAY_MEDIA);
|
||||
if (!stack.isEmpty())
|
||||
return List.of(new DebugUnlockerHolder(stack));
|
||||
|
||||
return List.of();
|
||||
});
|
||||
}
|
||||
|
||||
public static boolean isDebug(ItemStack stack) {
|
||||
return stack.is(HexItems.CREATIVE_UNLOCKER)
|
||||
&& stack.hasCustomHoverName()
|
||||
&& stack.getHoverName().getString().toLowerCase(Locale.ROOT).contains("debug");
|
||||
return isDebug(stack, null);
|
||||
}
|
||||
|
||||
public static boolean isDebug(ItemStack stack, String flag) {
|
||||
if (!stack.is(HexItems.CREATIVE_UNLOCKER) || !stack.hasCustomHoverName()) {
|
||||
return false;
|
||||
}
|
||||
var keywords = Arrays.asList(stack.getHoverName().getString().toLowerCase(Locale.ROOT).split(" "));
|
||||
if (!keywords.contains("debug")) {
|
||||
return false;
|
||||
}
|
||||
return flag == null || keywords.contains(flag);
|
||||
}
|
||||
|
||||
public static Component infiniteMedia(Level level) {
|
||||
String prefix = "item.hexcasting.creative_unlocker.";
|
||||
|
||||
String emphasis = Language.getInstance().getOrDefault(prefix + "for_emphasis");
|
||||
MutableComponent emphasized = Component.literal("");
|
||||
MutableComponent emphasized = new TextComponent("");
|
||||
for (int i = 0; i < emphasis.length(); i++) {
|
||||
emphasized.append(rainbow(Component.literal("" + emphasis.charAt(i)), i, level));
|
||||
emphasized.append(rainbow(new TextComponent("" + emphasis.charAt(i)), i, level));
|
||||
}
|
||||
|
||||
return emphasized;
|
||||
}
|
||||
|
||||
private static final String TAG_EXTRACTIONS = "extractions";
|
||||
public static final String TAG_EXTRACTIONS = "extractions";
|
||||
public static final String TAG_INSERTIONS = "insertions";
|
||||
|
||||
public ItemCreativeUnlocker(Properties properties) {
|
||||
super(properties);
|
||||
|
@ -76,22 +133,37 @@ public class ItemCreativeUnlocker extends Item implements MediaHolderItem {
|
|||
|
||||
@Override
|
||||
public boolean canRecharge(ItemStack stack) {
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
public static void addToIntArray(ItemStack stack, String tag, int n) {
|
||||
int[] arr = NBTHelper.getIntArray(stack, tag);
|
||||
if (arr == null) {
|
||||
arr = new int[0];
|
||||
}
|
||||
int[] newArr = Arrays.copyOf(arr, arr.length + 1);
|
||||
newArr[newArr.length - 1] = n;
|
||||
NBTHelper.putIntArray(stack, tag, newArr);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int withdrawMana(ItemStack stack, int cost, boolean simulate) {
|
||||
if (!simulate && isDebug(stack)) {
|
||||
int[] arr = NBTHelper.getIntArray(stack, TAG_EXTRACTIONS);
|
||||
if (arr == null) {
|
||||
arr = new int[0];
|
||||
}
|
||||
int[] newArr = Arrays.copyOf(arr, arr.length + 1);
|
||||
newArr[newArr.length - 1] = cost;
|
||||
NBTHelper.putIntArray(stack, TAG_EXTRACTIONS, newArr);
|
||||
// In case it's withdrawn through other means
|
||||
if (!simulate && isDebug(stack, DISPLAY_MEDIA)) {
|
||||
addToIntArray(stack, TAG_EXTRACTIONS, cost);
|
||||
}
|
||||
|
||||
return cost < 0 ? 1 : cost;
|
||||
return cost < 0 ? getMana(stack) : cost;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int insertMana(ItemStack stack, int amount, boolean simulate) {
|
||||
// In case it's inserted through other means
|
||||
if (!simulate && isDebug(stack, DISPLAY_MEDIA)) {
|
||||
addToIntArray(stack, TAG_INSERTIONS, amount);
|
||||
}
|
||||
|
||||
return amount < 0 ? getMaxMana(stack) : amount;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -101,29 +173,45 @@ public class ItemCreativeUnlocker extends Item implements MediaHolderItem {
|
|||
|
||||
@Override
|
||||
public void inventoryTick(ItemStack stack, Level level, Entity entity, int slot, boolean selected) {
|
||||
if (isDebug(stack) && !level.isClientSide) {
|
||||
int[] arr = NBTHelper.getIntArray(stack, TAG_EXTRACTIONS);
|
||||
if (arr != null) {
|
||||
NBTHelper.remove(stack, TAG_EXTRACTIONS);
|
||||
for (int i : arr) {
|
||||
if (i < 0) {
|
||||
entity.sendSystemMessage(Component.translatable("hexcasting.debug.mana_withdrawn",
|
||||
stack.getDisplayName(),
|
||||
Component.translatable("hexcasting.debug.all_mana").withStyle(ChatFormatting.GRAY))
|
||||
.withStyle(ChatFormatting.LIGHT_PURPLE));
|
||||
} else {
|
||||
entity.sendSystemMessage(Component.translatable("hexcasting.debug.mana_withdrawn.with_dust",
|
||||
stack.getDisplayName(),
|
||||
Component.literal("" + i).withStyle(ChatFormatting.WHITE),
|
||||
Component.literal(String.format("%.2f", i * 1.0 / ManaConstants.DUST_UNIT)).withStyle(
|
||||
ChatFormatting.WHITE))
|
||||
.withStyle(ChatFormatting.LIGHT_PURPLE));
|
||||
}
|
||||
if (isDebug(stack, DISPLAY_MEDIA) && !level.isClientSide) {
|
||||
debugDisplay(stack, TAG_EXTRACTIONS, "withdrawn", "all_mana", entity);
|
||||
debugDisplay(stack, TAG_INSERTIONS, "inserted", "infinite_mana", entity);
|
||||
}
|
||||
}
|
||||
|
||||
private void debugDisplay(ItemStack stack, String tag, String langKey, String allKey, Entity entity) {
|
||||
int[] arr = NBTHelper.getIntArray(stack, tag);
|
||||
if (arr != null) {
|
||||
NBTHelper.remove(stack, tag);
|
||||
for (int i : arr) {
|
||||
if (i < 0) {
|
||||
entity.sendMessage(new TranslatableComponent("hexcasting.debug.mana_" + langKey,
|
||||
stack.getDisplayName(),
|
||||
new TranslatableComponent("hexcasting.debug." + allKey).withStyle(ChatFormatting.GRAY))
|
||||
.withStyle(ChatFormatting.LIGHT_PURPLE), Util.NIL_UUID);
|
||||
} else {
|
||||
entity.sendMessage(new TranslatableComponent("hexcasting.debug.mana_" + langKey + ".with_dust",
|
||||
stack.getDisplayName(),
|
||||
new TextComponent("" + i).withStyle(ChatFormatting.WHITE),
|
||||
new TextComponent(String.format("%.2f", i * 1.0 / ManaConstants.DUST_UNIT)).withStyle(
|
||||
ChatFormatting.WHITE))
|
||||
.withStyle(ChatFormatting.LIGHT_PURPLE), Util.NIL_UUID);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public InteractionResult useOn(UseOnContext context) {
|
||||
BlockEntity be = context.getLevel().getBlockEntity(context.getClickedPos());
|
||||
if (be instanceof BlockEntityAbstractImpetus impetus) {
|
||||
impetus.setInfiniteMana();
|
||||
context.getLevel().playSound(null, context.getClickedPos(), HexSounds.SPELL_CIRCLE_FIND_BLOCK, SoundSource.PLAYERS, 1f, 1f);
|
||||
return InteractionResult.sidedSuccess(context.getLevel().isClientSide());
|
||||
}
|
||||
return InteractionResult.PASS;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemStack finishUsingItem(ItemStack stack, Level level, LivingEntity consumer) {
|
||||
if (level instanceof ServerLevel slevel && consumer instanceof ServerPlayer player) {
|
||||
|
@ -151,8 +239,6 @@ public class ItemCreativeUnlocker extends Item implements MediaHolderItem {
|
|||
return copy;
|
||||
}
|
||||
|
||||
private static final TextColor HEX_COLOR = TextColor.fromRgb(0xb38ef3);
|
||||
|
||||
private static MutableComponent rainbow(MutableComponent component, int shift, Level level) {
|
||||
if (level == null) {
|
||||
return component.withStyle(ChatFormatting.WHITE);
|
||||
|
@ -165,16 +251,14 @@ public class ItemCreativeUnlocker extends Item implements MediaHolderItem {
|
|||
@Override
|
||||
public void appendHoverText(ItemStack stack, @Nullable Level level, List<Component> tooltipComponents,
|
||||
TooltipFlag isAdvanced) {
|
||||
String prefix = "item.hexcasting.creative_unlocker.";
|
||||
|
||||
Component emphasized = infiniteMedia(level);
|
||||
|
||||
MutableComponent modName = Component.translatable(prefix + "mod_name").withStyle(
|
||||
(s) -> s.withColor(HEX_COLOR));
|
||||
MutableComponent modName = new TranslatableComponent("item.hexcasting.creative_unlocker.mod_name").withStyle(
|
||||
(s) -> s.withColor(ItemManaHolder.HEX_COLOR));
|
||||
|
||||
tooltipComponents.add(
|
||||
Component.translatable(prefix + "tooltip.0", emphasized).withStyle(ChatFormatting.GRAY));
|
||||
tooltipComponents.add(Component.translatable(prefix + "tooltip.1", modName).withStyle(ChatFormatting.GRAY));
|
||||
new TranslatableComponent("hexcasting.spelldata.onitem", emphasized).withStyle(ChatFormatting.GRAY));
|
||||
tooltipComponents.add(new TranslatableComponent("item.hexcasting.creative_unlocker.tooltip", modName).withStyle(ChatFormatting.GRAY));
|
||||
}
|
||||
|
||||
private static void addChildren(Advancement root, List<Advancement> out) {
|
||||
|
|
|
@ -1,10 +1,14 @@
|
|||
package at.petrak.hexcasting.common.items.magic;
|
||||
|
||||
import at.petrak.hexcasting.api.item.ManaHolderItem;
|
||||
import at.petrak.hexcasting.api.misc.ManaConstants;
|
||||
import at.petrak.hexcasting.api.item.MediaHolderItem;
|
||||
import at.petrak.hexcasting.api.utils.ManaHelper;
|
||||
import at.petrak.hexcasting.api.utils.NBTHelper;
|
||||
import net.minecraft.ChatFormatting;
|
||||
import net.minecraft.network.chat.Component;
|
||||
import net.minecraft.network.chat.TextColor;
|
||||
import net.minecraft.network.chat.TextComponent;
|
||||
import net.minecraft.network.chat.TranslatableComponent;
|
||||
import net.minecraft.util.Mth;
|
||||
import net.minecraft.world.item.Item;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
|
@ -12,12 +16,24 @@ import net.minecraft.world.item.TooltipFlag;
|
|||
import net.minecraft.world.level.Level;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.math.RoundingMode;
|
||||
import java.text.DecimalFormat;
|
||||
import java.util.List;
|
||||
|
||||
public abstract class ItemMediaHolder extends Item implements MediaHolderItem {
|
||||
public static final String TAG_MANA = "hexcasting:mana";
|
||||
public static final String TAG_MAX_MANA = "hexcasting:start_mana";
|
||||
|
||||
public static final TextColor HEX_COLOR = TextColor.fromRgb(0xb38ef3);
|
||||
|
||||
private static final DecimalFormat PERCENTAGE = new DecimalFormat("####");
|
||||
|
||||
static {
|
||||
PERCENTAGE.setRoundingMode(RoundingMode.DOWN);
|
||||
}
|
||||
|
||||
private static final DecimalFormat DUST_AMOUNT = new DecimalFormat("###,###.##");
|
||||
|
||||
public ItemMediaHolder(Properties pProperties) {
|
||||
super(pProperties);
|
||||
}
|
||||
|
@ -74,12 +90,24 @@ public abstract class ItemMediaHolder extends Item implements MediaHolderItem {
|
|||
@Override
|
||||
public void appendHoverText(ItemStack pStack, @Nullable Level pLevel, List<Component> pTooltipComponents,
|
||||
TooltipFlag pIsAdvanced) {
|
||||
if (pIsAdvanced.isAdvanced() && getMaxMedia(pStack) > 0) {
|
||||
var maxMana = getMaxMana(pStack);
|
||||
if (maxMana > 0) {
|
||||
var mana = getMana(pStack);
|
||||
var fullness = getManaFullness(pStack);
|
||||
|
||||
var color = TextColor.fromRgb(ManaHelper.manaBarColor(mana, maxMana));
|
||||
|
||||
var manaAmount = new TextComponent(DUST_AMOUNT.format(mana / (float) ManaConstants.DUST_UNIT));
|
||||
var percentFull = new TextComponent(PERCENTAGE.format(100f * fullness) + "%");
|
||||
var maxCapacity = new TranslatableComponent("hexcasting.tooltip.mana", DUST_AMOUNT.format(maxMana / (float) ManaConstants.DUST_UNIT));
|
||||
|
||||
manaAmount.withStyle(style -> style.withColor(HEX_COLOR));
|
||||
maxCapacity.withStyle(style -> style.withColor(HEX_COLOR));
|
||||
percentFull.withStyle(style -> style.withColor(color));
|
||||
|
||||
pTooltipComponents.add(
|
||||
Component.translatable("item.hexcasting.manaholder.amount",
|
||||
String.format("%,d", getMedia(pStack)),
|
||||
String.format("%,d", getMaxMedia(pStack)),
|
||||
100f * getManaFullness(pStack)).withStyle(ChatFormatting.GRAY));
|
||||
new TranslatableComponent("hexcasting.tooltip.mana_amount.advanced",
|
||||
manaAmount, maxCapacity, percentFull));
|
||||
}
|
||||
|
||||
super.appendHoverText(pStack, pLevel, pTooltipComponents, pIsAdvanced);
|
||||
|
|
|
@ -0,0 +1,16 @@
|
|||
package at.petrak.hexcasting.common.lib;
|
||||
|
||||
import net.minecraft.core.Registry;
|
||||
import net.minecraft.tags.TagKey;
|
||||
import net.minecraft.world.entity.EntityType;
|
||||
|
||||
import static at.petrak.hexcasting.api.HexAPI.modLoc;
|
||||
|
||||
public class HexEntityTags {
|
||||
public static final TagKey<EntityType<?>> STICKY_TELEPORTERS = create("sticky_teleporters");
|
||||
public static final TagKey<EntityType<?>> CANNOT_TELEPORT = create("cannot_teleport");
|
||||
|
||||
public static TagKey<EntityType<?>> create(String name) {
|
||||
return TagKey.create(Registry.ENTITY_TYPE_REGISTRY, modLoc(name));
|
||||
}
|
||||
}
|
|
@ -65,7 +65,7 @@ public class HexItems {
|
|||
public static final ItemSlate SLATE = make("slate", new ItemSlate(HexBlocks.SLATE, props()));
|
||||
|
||||
public static final ItemMediaBattery BATTERY = make("battery",
|
||||
new ItemMediaBattery(new Item.Properties().stacksTo(1)));
|
||||
new ItemMediaBattery(unstackable()));
|
||||
|
||||
public static final EnumMap<DyeColor, ItemDyeColorizer> DYE_COLORIZERS = Util.make(() -> {
|
||||
var out = new EnumMap<DyeColor, ItemDyeColorizer>(DyeColor.class);
|
||||
|
|
|
@ -20,13 +20,14 @@ import org.jetbrains.annotations.Nullable;
|
|||
|
||||
import java.util.List;
|
||||
import java.util.OptionalInt;
|
||||
import java.util.Random;
|
||||
|
||||
public class AkashicTreeGrower extends AbstractTreeGrower {
|
||||
public static final AkashicTreeGrower INSTANCE = new AkashicTreeGrower();
|
||||
|
||||
public static final List<Holder<ConfiguredFeature<TreeConfiguration, ?>>> GROWERS = Lists.newArrayList();
|
||||
|
||||
static {
|
||||
public static void init() {
|
||||
GROWERS.add(buildTreeFeature(HexBlocks.AMETHYST_EDIFIED_LEAVES, "1"));
|
||||
GROWERS.add(buildTreeFeature(HexBlocks.AVENTURINE_EDIFIED_LEAVES, "2"));
|
||||
GROWERS.add(buildTreeFeature(HexBlocks.CITRINE_EDIFIED_LEAVES, "3"));
|
||||
|
|
|
@ -16,7 +16,7 @@ import static at.petrak.hexcasting.api.HexAPI.modLoc;
|
|||
/**
|
||||
* Sent server->client when the player finishes casting a spell.
|
||||
*/
|
||||
public record MsgNewSpellPatternAck(ControllerInfo info) implements IMessage {
|
||||
public record MsgNewSpellPatternAck(ControllerInfo info, int index) implements IMessage {
|
||||
public static final ResourceLocation ID = modLoc("pat_sc");
|
||||
|
||||
@Override
|
||||
|
@ -30,6 +30,7 @@ public record MsgNewSpellPatternAck(ControllerInfo info) implements IMessage {
|
|||
var wasSpellCast = buf.readBoolean();
|
||||
var isStackEmpty = buf.readBoolean();
|
||||
var resolutionType = buf.readEnum(ResolvedPatternType.class);
|
||||
var index = buf.readInt();
|
||||
|
||||
var stack = buf.readList(FriendlyByteBuf::readNbt);
|
||||
var parens = buf.readList(FriendlyByteBuf::readNbt);
|
||||
|
@ -38,7 +39,7 @@ public record MsgNewSpellPatternAck(ControllerInfo info) implements IMessage {
|
|||
var parenCount = buf.readVarInt();
|
||||
|
||||
return new MsgNewSpellPatternAck(
|
||||
new ControllerInfo(wasSpellCast, isStackEmpty, resolutionType, stack, parens, raven, parenCount)
|
||||
new ControllerInfo(wasSpellCast, isStackEmpty, resolutionType, stack, parens, raven, parenCount), index
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -47,6 +48,7 @@ public record MsgNewSpellPatternAck(ControllerInfo info) implements IMessage {
|
|||
buf.writeBoolean(this.info.getMakesCastSound());
|
||||
buf.writeBoolean(this.info.isStackClear());
|
||||
buf.writeEnum(this.info.getResolutionType());
|
||||
buf.writeInt(this.index);
|
||||
|
||||
buf.writeCollection(this.info.getStack(), FriendlyByteBuf::writeNbt);
|
||||
buf.writeCollection(this.info.getParenthesized(), FriendlyByteBuf::writeNbt);
|
||||
|
@ -69,7 +71,7 @@ public record MsgNewSpellPatternAck(ControllerInfo info) implements IMessage {
|
|||
if (self.info().isStackClear()) {
|
||||
mc.setScreen(null);
|
||||
} else {
|
||||
spellGui.recvServerUpdate(self.info());
|
||||
spellGui.recvServerUpdate(self.info(), self.index());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -110,7 +110,7 @@ public record MsgNewSpellPatternSyn(InteractionHand handUsed, HexPattern pattern
|
|||
IXplatAbstractions.INSTANCE.setPatterns(sender, resolvedPatterns);
|
||||
}
|
||||
|
||||
IXplatAbstractions.INSTANCE.sendPacketToPlayer(sender, new MsgNewSpellPatternAck(clientInfo));
|
||||
IXplatAbstractions.INSTANCE.sendPacketToPlayer(sender, new MsgNewSpellPatternAck(clientInfo, resolvedPatterns.size() - 1));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
package at.petrak.hexcasting.common.network;
|
||||
|
||||
import at.petrak.hexcasting.api.utils.HexUtils;
|
||||
import at.petrak.hexcasting.common.entities.EntityWallScroll;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.core.BlockPos;
|
||||
|
@ -34,7 +35,7 @@ public record MsgNewWallScrollAck(ClientboundAddEntityPacket inner, BlockPos pos
|
|||
public static MsgNewWallScrollAck deserialize(FriendlyByteBuf buf) {
|
||||
var inner = new ClientboundAddEntityPacket(buf);
|
||||
var pos = buf.readBlockPos();
|
||||
var dir = Direction.values()[buf.readByte()];
|
||||
var dir = HexUtils.getSafe(Direction.values(), buf.readByte());
|
||||
var scroll = buf.readItem();
|
||||
var strokeOrder = buf.readBoolean();
|
||||
var blockSize = buf.readVarInt();
|
||||
|
|
|
@ -12,7 +12,7 @@ import static at.petrak.hexcasting.api.HexAPI.modLoc;
|
|||
/**
|
||||
* Sent server->client when a player is looking at a block through a lens whose comparator value is not the same as what they last saw.
|
||||
*/
|
||||
public record MsgUpdateComparatorVisualsAck(BlockPos pos, int value) implements IMessage {
|
||||
public record MsgUpdateComparatorVisualsAck(BlockPos pos, int comparator, int bee) implements IMessage {
|
||||
public static final ResourceLocation ID = modLoc("cmp");
|
||||
|
||||
@Override
|
||||
|
@ -23,15 +23,17 @@ public record MsgUpdateComparatorVisualsAck(BlockPos pos, int value) implements
|
|||
public static MsgUpdateComparatorVisualsAck deserialize(ByteBuf buffer) {
|
||||
var buf = new FriendlyByteBuf(buffer);
|
||||
|
||||
int value = buf.readInt();
|
||||
BlockPos pos = value == -1 ? null : buf.readBlockPos();
|
||||
int comparator = buf.readInt();
|
||||
int bee = buf.readInt();
|
||||
BlockPos pos = comparator == -1 && bee == -1 ? null : buf.readBlockPos();
|
||||
|
||||
return new MsgUpdateComparatorVisualsAck(pos, value);
|
||||
return new MsgUpdateComparatorVisualsAck(pos, comparator, bee);
|
||||
}
|
||||
|
||||
public void serialize(FriendlyByteBuf buf) {
|
||||
buf.writeInt(this.value);
|
||||
if (this.value != -1) {
|
||||
buf.writeInt(this.comparator);
|
||||
buf.writeInt(this.bee);
|
||||
if (this.comparator != -1 || this.bee != -1) {
|
||||
buf.writeBlockPos(this.pos);
|
||||
}
|
||||
}
|
||||
|
@ -40,7 +42,7 @@ public record MsgUpdateComparatorVisualsAck(BlockPos pos, int value) implements
|
|||
Minecraft.getInstance().execute(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
ScryingLensOverlayRegistry.receiveComparatorValue(msg.pos(), msg.value());
|
||||
ScryingLensOverlayRegistry.receiveComparatorAndBeeValue(msg.pos(), msg.comparator(), msg.bee());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
|
@ -7,12 +7,17 @@ import at.petrak.hexcasting.api.advancements.SpendManaTrigger;
|
|||
import at.petrak.hexcasting.api.misc.ManaConstants;
|
||||
import at.petrak.hexcasting.common.items.ItemLoreFragment;
|
||||
import at.petrak.hexcasting.common.lib.HexBlocks;
|
||||
import at.petrak.hexcasting.api.mod.HexItemTags;
|
||||
import at.petrak.hexcasting.common.lib.HexItems;
|
||||
import at.petrak.paucal.api.datagen.PaucalAdvancementProvider;
|
||||
import net.minecraft.advancements.Advancement;
|
||||
import net.minecraft.advancements.DisplayInfo;
|
||||
import net.minecraft.advancements.FrameType;
|
||||
import net.minecraft.advancements.critereon.*;
|
||||
import net.minecraft.advancements.critereon.EntityPredicate;
|
||||
import net.minecraft.advancements.critereon.InventoryChangeTrigger;
|
||||
import net.minecraft.advancements.critereon.ItemPredicate;
|
||||
import net.minecraft.advancements.critereon.MinMaxBounds;
|
||||
import net.minecraft.data.DataGenerator;
|
||||
import net.minecraft.network.chat.Component;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
|
@ -37,8 +42,8 @@ public class HexAdvancements extends PaucalAdvancementProvider {
|
|||
new ResourceLocation("minecraft", "textures/block/calcite.png"),
|
||||
FrameType.TASK, true, true, true))
|
||||
// the only thing making this vaguely tolerable is the knowledge the json files are worse somehow
|
||||
.addCriterion("has_charged_amethyst",
|
||||
InventoryChangeTrigger.TriggerInstance.hasItems(HexItems.CHARGED_AMETHYST))
|
||||
.addCriterion("has_charged_amethyst", InventoryChangeTrigger.TriggerInstance.hasItems(
|
||||
ItemPredicate.Builder.item().of(HexItemTags.GRANTS_ROOT_ADVANCEMENT).build()))
|
||||
.save(consumer, prefix("root")); // how the hell does one even read this
|
||||
|
||||
// weird names so we have alphabetical parity
|
||||
|
|
|
@ -31,6 +31,8 @@ public class HexItemTagProvider extends PaucalItemTagProvider {
|
|||
HexItems.STAFF_CRIMSON, HexItems.STAFF_WARPED);
|
||||
|
||||
tag(HexItemTags.PHIAL_BASE).add(Items.GLASS_BOTTLE);
|
||||
tag(HexItemTags.GRANTS_ROOT_ADVANCEMENT).add(HexItems.AMETHYST_DUST, Items.AMETHYST_SHARD,
|
||||
HexItems.CHARGED_AMETHYST);
|
||||
|
||||
this.copy(HexBlockTags.EDIFIED_LOGS, HexItemTags.EDIFIED_LOGS);
|
||||
this.copy(HexBlockTags.EDIFIED_PLANKS, HexItemTags.EDIFIED_PLANKS);
|
||||
|
|
|
@ -0,0 +1,9 @@
|
|||
package at.petrak.hexcasting.datagen;
|
||||
|
||||
import net.minecraft.data.recipes.RecipeBuilder;
|
||||
|
||||
public interface IXplatConditionsBuilder extends RecipeBuilder {
|
||||
IXplatConditionsBuilder whenModLoaded(String modid);
|
||||
|
||||
IXplatConditionsBuilder whenModMissing(String modid);
|
||||
}
|
|
@ -1,5 +1,6 @@
|
|||
package at.petrak.hexcasting.datagen;
|
||||
|
||||
import at.petrak.hexcasting.datagen.recipe.builders.FarmersDelightToolIngredient;
|
||||
import net.minecraft.world.item.DyeColor;
|
||||
import net.minecraft.world.item.crafting.Ingredient;
|
||||
|
||||
|
@ -23,4 +24,10 @@ public interface IXplatIngredients {
|
|||
EnumMap<DyeColor, Ingredient> dyes();
|
||||
|
||||
Ingredient stick();
|
||||
|
||||
Ingredient whenModIngredient(Ingredient defaultIngredient, String modid, Ingredient modIngredient);
|
||||
|
||||
FarmersDelightToolIngredient axeStrip();
|
||||
|
||||
FarmersDelightToolIngredient axeDig();
|
||||
}
|
||||
|
|
|
@ -1,388 +0,0 @@
|
|||
package at.petrak.hexcasting.datagen.recipe;
|
||||
|
||||
import at.petrak.hexcasting.api.HexAPI;
|
||||
import at.petrak.hexcasting.api.advancements.OvercastTrigger;
|
||||
import at.petrak.hexcasting.api.mod.HexItemTags;
|
||||
import at.petrak.hexcasting.common.items.ItemStaff;
|
||||
import at.petrak.hexcasting.common.items.colorizer.ItemPrideColorizer;
|
||||
import at.petrak.hexcasting.common.lib.HexBlocks;
|
||||
import at.petrak.hexcasting.common.lib.HexItems;
|
||||
import at.petrak.hexcasting.common.recipe.SealFocusRecipe;
|
||||
import at.petrak.hexcasting.common.recipe.SealSpellbookRecipe;
|
||||
import at.petrak.hexcasting.common.recipe.ingredient.StateIngredientHelper;
|
||||
import at.petrak.hexcasting.common.recipe.ingredient.VillagerIngredient;
|
||||
import at.petrak.hexcasting.datagen.IXplatIngredients;
|
||||
import at.petrak.hexcasting.datagen.recipe.builders.BrainsweepRecipeBuilder;
|
||||
import at.petrak.paucal.api.datagen.PaucalRecipeProvider;
|
||||
import net.minecraft.advancements.critereon.EntityPredicate;
|
||||
import net.minecraft.advancements.critereon.MinMaxBounds;
|
||||
import net.minecraft.core.Registry;
|
||||
import net.minecraft.data.DataGenerator;
|
||||
import net.minecraft.data.recipes.*;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.tags.ItemTags;
|
||||
import net.minecraft.world.item.DyeColor;
|
||||
import net.minecraft.world.item.DyeItem;
|
||||
import net.minecraft.world.item.Item;
|
||||
import net.minecraft.world.item.Items;
|
||||
import net.minecraft.world.item.crafting.Ingredient;
|
||||
import net.minecraft.world.item.crafting.SimpleRecipeSerializer;
|
||||
import net.minecraft.world.level.block.Blocks;
|
||||
|
||||
import java.util.function.Consumer;
|
||||
|
||||
|
||||
public class HexplatRecipes extends PaucalRecipeProvider {
|
||||
public DataGenerator generator;
|
||||
public IXplatIngredients ingredients;
|
||||
|
||||
public HexplatRecipes(DataGenerator pGenerator, IXplatIngredients ingredients) {
|
||||
super(pGenerator, HexAPI.MOD_ID);
|
||||
this.generator = pGenerator;
|
||||
this.ingredients = ingredients;
|
||||
}
|
||||
|
||||
protected void makeRecipes(Consumer<FinishedRecipe> recipes) {
|
||||
specialRecipe(recipes, SealFocusRecipe.SERIALIZER);
|
||||
specialRecipe(recipes, SealSpellbookRecipe.SERIALIZER);
|
||||
|
||||
wandRecipe(recipes, HexItems.STAFF_OAK, Items.OAK_PLANKS);
|
||||
wandRecipe(recipes, HexItems.STAFF_BIRCH, Items.BIRCH_PLANKS);
|
||||
wandRecipe(recipes, HexItems.STAFF_SPRUCE, Items.SPRUCE_PLANKS);
|
||||
wandRecipe(recipes, HexItems.STAFF_JUNGLE, Items.JUNGLE_PLANKS);
|
||||
wandRecipe(recipes, HexItems.STAFF_DARK_OAK, Items.DARK_OAK_PLANKS);
|
||||
wandRecipe(recipes, HexItems.STAFF_ACACIA, Items.ACACIA_PLANKS);
|
||||
wandRecipe(recipes, HexItems.STAFF_CRIMSON, Items.CRIMSON_PLANKS);
|
||||
wandRecipe(recipes, HexItems.STAFF_WARPED, Items.WARPED_PLANKS);
|
||||
wandRecipe(recipes, HexItems.STAFF_EDIFIED, HexBlocks.EDIFIED_PLANKS.asItem());
|
||||
|
||||
ringCornered(HexItems.FOCUS, 1, ingredients.glowstoneDust(),
|
||||
ingredients.leather(), Ingredient.of(HexItems.CHARGED_AMETHYST))
|
||||
.unlockedBy("has_item", hasItem(HexItemTags.STAVES))
|
||||
.save(recipes);
|
||||
|
||||
ShapedRecipeBuilder.shaped(HexItems.SPELLBOOK)
|
||||
.define('N', ingredients.goldNugget())
|
||||
.define('B', Items.WRITABLE_BOOK)
|
||||
.define('A', HexItems.CHARGED_AMETHYST)
|
||||
.define('F', Items.CHORUS_FRUIT) // i wanna gate this behind the end SOMEHOW
|
||||
// hey look its my gender ^^
|
||||
.pattern("NBA")
|
||||
.pattern("NFA")
|
||||
.pattern("NBA")
|
||||
.unlockedBy("has_focus", hasItem(HexItems.FOCUS))
|
||||
.unlockedBy("has_chorus", hasItem(Items.CHORUS_FRUIT)).save(recipes);
|
||||
|
||||
ringCornerless(HexItems.CYPHER, 1,
|
||||
ingredients.copperIngot(),
|
||||
Ingredient.of(HexItems.AMETHYST_DUST))
|
||||
.unlockedBy("has_item", hasItem(HexItemTags.STAVES)).save(recipes);
|
||||
|
||||
ringCornerless(HexItems.TRINKET, 1,
|
||||
ingredients.ironIngot(),
|
||||
Ingredient.of(Items.AMETHYST_SHARD))
|
||||
.unlockedBy("has_item", hasItem(HexItemTags.STAVES)).save(recipes);
|
||||
|
||||
ShapedRecipeBuilder.shaped(HexItems.ARTIFACT)
|
||||
.define('F', ingredients.goldIngot())
|
||||
.define('A', HexItems.CHARGED_AMETHYST)
|
||||
// why in god's name does minecraft have two different places for item tags
|
||||
.define('D', ItemTags.MUSIC_DISCS)
|
||||
.pattern(" F ")
|
||||
.pattern("FAF")
|
||||
.pattern(" D ")
|
||||
.unlockedBy("has_item", hasItem(HexItemTags.STAVES)).save(recipes);
|
||||
|
||||
ringCornerless(HexItems.SCRYING_LENS, 1, Items.GLASS, HexItems.AMETHYST_DUST)
|
||||
.unlockedBy("has_item", hasItem(HexItemTags.STAVES)).save(recipes);
|
||||
|
||||
ShapedRecipeBuilder.shaped(HexItems.ABACUS)
|
||||
.define('S', Items.STICK)
|
||||
.define('A', Items.AMETHYST_SHARD)
|
||||
.define('W', ItemTags.PLANKS)
|
||||
.pattern("WAW")
|
||||
.pattern("SAS")
|
||||
.pattern("WAW")
|
||||
.unlockedBy("has_item", hasItem(HexItemTags.STAVES)).save(recipes);
|
||||
|
||||
// Why am I like this
|
||||
ShapedRecipeBuilder.shaped(HexItems.SUBMARINE_SANDWICH)
|
||||
.define('S', Items.STICK)
|
||||
.define('A', Items.AMETHYST_SHARD)
|
||||
.define('C', Items.COOKED_BEEF)
|
||||
.define('B', Items.BREAD)
|
||||
.pattern(" SA")
|
||||
.pattern(" C ")
|
||||
.pattern(" B ")
|
||||
.unlockedBy("has_item", hasItem(Items.AMETHYST_SHARD)).save(recipes);
|
||||
|
||||
for (var dye : DyeColor.values()) {
|
||||
var item = HexItems.DYE_COLORIZERS.get(dye);
|
||||
ShapedRecipeBuilder.shaped(item)
|
||||
.define('D', HexItems.AMETHYST_DUST)
|
||||
.define('C', DyeItem.byColor(dye))
|
||||
.pattern(" D ")
|
||||
.pattern("DCD")
|
||||
.pattern(" D ")
|
||||
.unlockedBy("has_item", hasItem(HexItems.AMETHYST_DUST)).save(recipes);
|
||||
}
|
||||
|
||||
gayRecipe(recipes, ItemPrideColorizer.Type.AGENDER, Items.GLASS);
|
||||
gayRecipe(recipes, ItemPrideColorizer.Type.AROACE, Items.WHEAT_SEEDS);
|
||||
gayRecipe(recipes, ItemPrideColorizer.Type.AROMANTIC, Items.ARROW);
|
||||
gayRecipe(recipes, ItemPrideColorizer.Type.ASEXUAL, Items.BREAD);
|
||||
gayRecipe(recipes, ItemPrideColorizer.Type.BISEXUAL, Items.WHEAT);
|
||||
gayRecipe(recipes, ItemPrideColorizer.Type.DEMIBOY, Items.RAW_IRON);
|
||||
gayRecipe(recipes, ItemPrideColorizer.Type.DEMIGIRL, Items.RAW_COPPER);
|
||||
gayRecipe(recipes, ItemPrideColorizer.Type.GAY, Items.STONE_BRICK_WALL);
|
||||
gayRecipe(recipes, ItemPrideColorizer.Type.GENDERFLUID, Items.WATER_BUCKET);
|
||||
gayRecipe(recipes, ItemPrideColorizer.Type.GENDERQUEER, Items.GLASS_BOTTLE);
|
||||
gayRecipe(recipes, ItemPrideColorizer.Type.INTERSEX, Items.AZALEA);
|
||||
gayRecipe(recipes, ItemPrideColorizer.Type.LESBIAN, Items.HONEYCOMB);
|
||||
gayRecipe(recipes, ItemPrideColorizer.Type.NONBINARY, Items.MOSS_BLOCK);
|
||||
gayRecipe(recipes, ItemPrideColorizer.Type.PANSEXUAL, Items.CARROT);
|
||||
gayRecipe(recipes, ItemPrideColorizer.Type.PLURAL, Items.REPEATER);
|
||||
gayRecipe(recipes, ItemPrideColorizer.Type.TRANSGENDER, Items.EGG);
|
||||
|
||||
ShapedRecipeBuilder.shaped(HexItems.UUID_COLORIZER)
|
||||
.define('B', Items.BOWL)
|
||||
.define('D', HexItems.AMETHYST_DUST)
|
||||
.define('C', Items.AMETHYST_SHARD)
|
||||
.pattern(" C ")
|
||||
.pattern(" D ")
|
||||
.pattern(" B ")
|
||||
.unlockedBy("has_item", hasItem(HexItems.AMETHYST_DUST)).save(recipes);
|
||||
|
||||
ShapedRecipeBuilder.shaped(HexItems.SCROLL_SMOL)
|
||||
.define('P', Items.PAPER)
|
||||
.define('A', Items.AMETHYST_SHARD)
|
||||
.pattern(" A")
|
||||
.pattern("P ")
|
||||
.unlockedBy("has_item", hasItem(Items.AMETHYST_SHARD)).save(recipes);
|
||||
ShapedRecipeBuilder.shaped(HexItems.SCROLL_MEDIUM)
|
||||
.define('P', Items.PAPER)
|
||||
.define('A', Items.AMETHYST_SHARD)
|
||||
.pattern(" A")
|
||||
.pattern("PP ")
|
||||
.pattern("PP ")
|
||||
.unlockedBy("has_item", hasItem(Items.AMETHYST_SHARD)).save(recipes);
|
||||
ShapedRecipeBuilder.shaped(HexItems.SCROLL_LARGE)
|
||||
.define('P', Items.PAPER)
|
||||
.define('A', Items.AMETHYST_SHARD)
|
||||
.pattern("PPA")
|
||||
.pattern("PPP")
|
||||
.pattern("PPP")
|
||||
.unlockedBy("has_item", hasItem(Items.AMETHYST_SHARD)).save(recipes);
|
||||
|
||||
ShapedRecipeBuilder.shaped(HexItems.SLATE, 6)
|
||||
.define('S', Items.DEEPSLATE)
|
||||
.define('A', HexItems.AMETHYST_DUST)
|
||||
.pattern(" A ")
|
||||
.pattern("SSS")
|
||||
.unlockedBy("has_item", hasItem(HexItems.AMETHYST_DUST)).save(recipes);
|
||||
|
||||
ShapedRecipeBuilder.shaped(HexItems.JEWELER_HAMMER)
|
||||
.define('I', ingredients.ironIngot())
|
||||
.define('N', ingredients.ironNugget())
|
||||
.define('A', Items.AMETHYST_SHARD)
|
||||
.define('S', ingredients.stick())
|
||||
.pattern("IAN")
|
||||
.pattern(" S ")
|
||||
.pattern(" S ")
|
||||
.unlockedBy("has_item", hasItem(Items.AMETHYST_SHARD)).save(recipes);
|
||||
|
||||
ShapedRecipeBuilder.shaped(HexBlocks.SLATE_BLOCK)
|
||||
.define('S', HexItems.SLATE)
|
||||
.pattern("S")
|
||||
.pattern("S")
|
||||
.unlockedBy("has_item", hasItem(HexItems.SLATE))
|
||||
.save(recipes, modLoc("slate_block_from_slates"));
|
||||
|
||||
ringAll(HexBlocks.SLATE_BLOCK, 8, Blocks.DEEPSLATE, HexItems.AMETHYST_DUST)
|
||||
.unlockedBy("has_item", hasItem(HexItems.SLATE)).save(recipes);
|
||||
|
||||
packing(HexItems.AMETHYST_DUST, HexBlocks.AMETHYST_DUST_BLOCK.asItem(), "amethyst_dust",
|
||||
false, recipes);
|
||||
|
||||
ringAll(HexBlocks.AMETHYST_TILES, 8, Blocks.AMETHYST_BLOCK, HexItems.AMETHYST_DUST)
|
||||
.unlockedBy("has_item", hasItem(HexItems.AMETHYST_DUST)).save(recipes);
|
||||
SingleItemRecipeBuilder.stonecutting(Ingredient.of(Blocks.AMETHYST_BLOCK), HexBlocks.AMETHYST_TILES)
|
||||
.unlockedBy("has_item", hasItem(Blocks.AMETHYST_BLOCK))
|
||||
.save(recipes, modLoc("stonecutting/amethyst_tiles"));
|
||||
|
||||
ringAll(HexBlocks.SCROLL_PAPER, 8, Items.PAPER, Items.AMETHYST_SHARD)
|
||||
.unlockedBy("has_item", hasItem(Items.AMETHYST_SHARD)).save(recipes);
|
||||
ShapelessRecipeBuilder.shapeless(HexBlocks.ANCIENT_SCROLL_PAPER, 8)
|
||||
.requires(ingredients.dyes().get(DyeColor.BROWN))
|
||||
.requires(HexBlocks.SCROLL_PAPER, 8)
|
||||
.unlockedBy("has_item", hasItem(HexBlocks.SCROLL_PAPER)).save(recipes);
|
||||
stack(HexBlocks.SCROLL_PAPER_LANTERN, 1, HexBlocks.SCROLL_PAPER, Items.TORCH)
|
||||
.unlockedBy("has_item", hasItem(HexBlocks.SCROLL_PAPER)).save(recipes);
|
||||
stack(HexBlocks.ANCIENT_SCROLL_PAPER_LANTERN, 1, HexBlocks.ANCIENT_SCROLL_PAPER, Items.TORCH)
|
||||
.unlockedBy("has_item", hasItem(HexBlocks.ANCIENT_SCROLL_PAPER)).save(recipes);
|
||||
ShapelessRecipeBuilder.shapeless(HexBlocks.ANCIENT_SCROLL_PAPER_LANTERN, 8)
|
||||
.requires(ingredients.dyes().get(DyeColor.BROWN))
|
||||
.requires(HexBlocks.SCROLL_PAPER_LANTERN, 8)
|
||||
.unlockedBy("has_item", hasItem(HexBlocks.SCROLL_PAPER_LANTERN))
|
||||
.save(recipes, modLoc("ageing_scroll_paper_lantern"));
|
||||
|
||||
stack(HexBlocks.SCONCE, 4, Ingredient.of(HexItems.CHARGED_AMETHYST),
|
||||
ingredients.copperIngot())
|
||||
.unlockedBy("has_item", hasItem(HexItems.CHARGED_AMETHYST)).save(recipes);
|
||||
|
||||
ShapelessRecipeBuilder.shapeless(HexBlocks.EDIFIED_PLANKS, 4)
|
||||
.requires(HexItemTags.EDIFIED_LOGS)
|
||||
.unlockedBy("has_item", hasItem(HexItemTags.EDIFIED_LOGS)).save(recipes);
|
||||
ShapedRecipeBuilder.shaped(HexBlocks.EDIFIED_WOOD, 3)
|
||||
.define('W', HexBlocks.EDIFIED_LOG)
|
||||
.pattern("WW")
|
||||
.pattern("WW")
|
||||
.unlockedBy("has_item", hasItem(HexBlocks.EDIFIED_LOG)).save(recipes);
|
||||
ShapedRecipeBuilder.shaped(HexBlocks.STRIPPED_EDIFIED_WOOD, 3)
|
||||
.define('W', HexBlocks.STRIPPED_EDIFIED_LOG)
|
||||
.pattern("WW")
|
||||
.pattern("WW")
|
||||
.unlockedBy("has_item", hasItem(HexBlocks.STRIPPED_EDIFIED_LOG)).save(recipes);
|
||||
ring(HexBlocks.EDIFIED_PANEL, 8, HexItemTags.EDIFIED_PLANKS, null)
|
||||
.unlockedBy("has_item", hasItem(HexItemTags.EDIFIED_PLANKS)).save(recipes);
|
||||
ShapedRecipeBuilder.shaped(HexBlocks.EDIFIED_TILE, 6)
|
||||
.define('W', HexItemTags.EDIFIED_PLANKS)
|
||||
.pattern("WW ")
|
||||
.pattern("W W")
|
||||
.pattern(" WW")
|
||||
.unlockedBy("has_item", hasItem(HexItemTags.EDIFIED_PLANKS)).save(recipes);
|
||||
ShapedRecipeBuilder.shaped(HexBlocks.EDIFIED_DOOR, 3)
|
||||
.define('W', HexItemTags.EDIFIED_PLANKS)
|
||||
.pattern("WW")
|
||||
.pattern("WW")
|
||||
.pattern("WW")
|
||||
.unlockedBy("has_item", hasItem(HexItemTags.EDIFIED_PLANKS)).save(recipes);
|
||||
ShapedRecipeBuilder.shaped(HexBlocks.EDIFIED_TRAPDOOR, 2)
|
||||
.define('W', HexItemTags.EDIFIED_PLANKS)
|
||||
.pattern("WWW")
|
||||
.pattern("WWW")
|
||||
.unlockedBy("has_item", hasItem(HexItemTags.EDIFIED_PLANKS)).save(recipes);
|
||||
ShapedRecipeBuilder.shaped(HexBlocks.EDIFIED_STAIRS, 4)
|
||||
.define('W', HexItemTags.EDIFIED_PLANKS)
|
||||
.pattern("W ")
|
||||
.pattern("WW ")
|
||||
.pattern("WWW")
|
||||
.unlockedBy("has_item", hasItem(HexItemTags.EDIFIED_PLANKS)).save(recipes);
|
||||
ShapedRecipeBuilder.shaped(HexBlocks.EDIFIED_SLAB, 6)
|
||||
.define('W', HexItemTags.EDIFIED_PLANKS)
|
||||
.pattern("WWW")
|
||||
.unlockedBy("has_item", hasItem(HexItemTags.EDIFIED_PLANKS)).save(recipes);
|
||||
ShapedRecipeBuilder.shaped(HexBlocks.EDIFIED_PRESSURE_PLATE, 1)
|
||||
.define('W', HexItemTags.EDIFIED_PLANKS)
|
||||
.pattern("WW")
|
||||
.unlockedBy("has_item", hasItem(HexItemTags.EDIFIED_PLANKS)).save(recipes);
|
||||
ShapelessRecipeBuilder.shapeless(HexBlocks.EDIFIED_BUTTON)
|
||||
.requires(HexItemTags.EDIFIED_PLANKS)
|
||||
.unlockedBy("has_item", hasItem(HexItemTags.EDIFIED_PLANKS)).save(recipes);
|
||||
|
||||
var enlightenment = new OvercastTrigger.Instance(EntityPredicate.Composite.ANY,
|
||||
MinMaxBounds.Ints.ANY,
|
||||
// add a little bit of slop here
|
||||
MinMaxBounds.Doubles.atLeast(0.8),
|
||||
MinMaxBounds.Doubles.between(0.1, 2.05));
|
||||
|
||||
ShapedRecipeBuilder.shaped(HexBlocks.EMPTY_IMPETUS)
|
||||
.define('B', Items.IRON_BARS)
|
||||
.define('A', HexItems.CHARGED_AMETHYST)
|
||||
.define('S', HexBlocks.SLATE_BLOCK)
|
||||
.define('P', Items.PURPUR_BLOCK)
|
||||
.pattern("PSS")
|
||||
.pattern("BAB")
|
||||
.pattern("SSP")
|
||||
.unlockedBy("enlightenment", enlightenment).save(recipes);
|
||||
|
||||
ShapedRecipeBuilder.shaped(HexBlocks.EMPTY_DIRECTRIX)
|
||||
.define('C', Items.COMPARATOR)
|
||||
.define('O', Items.OBSERVER)
|
||||
.define('A', HexItems.CHARGED_AMETHYST)
|
||||
.define('S', HexBlocks.SLATE_BLOCK)
|
||||
.pattern("CSS")
|
||||
.pattern("OAO")
|
||||
.pattern("SSC")
|
||||
.unlockedBy("enlightenment", enlightenment).save(recipes);
|
||||
|
||||
ShapedRecipeBuilder.shaped(HexBlocks.AKASHIC_BOOKSHELF)
|
||||
.define('L', HexItemTags.EDIFIED_LOGS)
|
||||
.define('P', HexItemTags.EDIFIED_PLANKS)
|
||||
.define('C', Items.BOOK)
|
||||
/*this is the*/.pattern("LPL") // and what i have for you today is
|
||||
.pattern("CCC")
|
||||
.pattern("LPL")
|
||||
.unlockedBy("enlightenment", enlightenment).save(recipes);
|
||||
ShapedRecipeBuilder.shaped(HexBlocks.AKASHIC_LIGATURE)
|
||||
.define('L', HexItemTags.EDIFIED_LOGS)
|
||||
.define('P', HexItemTags.EDIFIED_PLANKS)
|
||||
.define('C', HexItems.CHARGED_AMETHYST)
|
||||
.pattern("LPL")
|
||||
.pattern("CCC")
|
||||
.pattern("LPL")
|
||||
.unlockedBy("enlightenment", enlightenment).save(recipes);
|
||||
|
||||
new BrainsweepRecipeBuilder(StateIngredientHelper.of(Blocks.AMETHYST_BLOCK),
|
||||
new VillagerIngredient(null, null, 3),
|
||||
Blocks.BUDDING_AMETHYST.defaultBlockState())
|
||||
.unlockedBy("enlightenment", enlightenment)
|
||||
.save(recipes, modLoc("brainsweep/budding_amethyst"));
|
||||
|
||||
new BrainsweepRecipeBuilder(StateIngredientHelper.of(HexBlocks.EMPTY_IMPETUS),
|
||||
new VillagerIngredient(new ResourceLocation("toolsmith"), null, 2),
|
||||
HexBlocks.IMPETUS_RIGHTCLICK.defaultBlockState())
|
||||
.unlockedBy("enlightenment", enlightenment)
|
||||
.save(recipes, modLoc("brainsweep/impetus_rightclick"));
|
||||
new BrainsweepRecipeBuilder(StateIngredientHelper.of(HexBlocks.EMPTY_IMPETUS),
|
||||
new VillagerIngredient(new ResourceLocation("fletcher"), null, 2),
|
||||
HexBlocks.IMPETUS_LOOK.defaultBlockState())
|
||||
.unlockedBy("enlightenment", enlightenment)
|
||||
.save(recipes, modLoc("brainsweep/impetus_look"));
|
||||
new BrainsweepRecipeBuilder(StateIngredientHelper.of(HexBlocks.EMPTY_IMPETUS),
|
||||
new VillagerIngredient(new ResourceLocation("cleric"), null, 2),
|
||||
HexBlocks.IMPETUS_STOREDPLAYER.defaultBlockState())
|
||||
.unlockedBy("enlightenment", enlightenment)
|
||||
.save(recipes, modLoc("brainsweep/impetus_storedplayer"));
|
||||
|
||||
new BrainsweepRecipeBuilder(StateIngredientHelper.of(HexBlocks.EMPTY_DIRECTRIX),
|
||||
new VillagerIngredient(new ResourceLocation("mason"), null, 1),
|
||||
HexBlocks.DIRECTRIX_REDSTONE.defaultBlockState())
|
||||
.unlockedBy("enlightenment", enlightenment)
|
||||
.save(recipes, modLoc("brainsweep/directrix_redstone"));
|
||||
|
||||
new BrainsweepRecipeBuilder(StateIngredientHelper.of(HexBlocks.AKASHIC_LIGATURE),
|
||||
new VillagerIngredient(new ResourceLocation("librarian"), null, 5),
|
||||
HexBlocks.AKASHIC_RECORD.defaultBlockState())
|
||||
.unlockedBy("enlightenment", enlightenment)
|
||||
.save(recipes, modLoc("brainsweep/akashic_record"));
|
||||
}
|
||||
|
||||
private void wandRecipe(Consumer<FinishedRecipe> recipes, ItemStaff wand, Item plank) {
|
||||
ShapedRecipeBuilder.shaped(wand)
|
||||
.define('W', plank)
|
||||
.define('S', Items.STICK)
|
||||
.define('A', HexItems.CHARGED_AMETHYST)
|
||||
.pattern(" SA")
|
||||
.pattern(" WS")
|
||||
.pattern("S ")
|
||||
.unlockedBy("has_item", hasItem(HexItems.CHARGED_AMETHYST))
|
||||
.save(recipes);
|
||||
}
|
||||
|
||||
private void gayRecipe(Consumer<FinishedRecipe> recipes, ItemPrideColorizer.Type type, Item material) {
|
||||
var colorizer = HexItems.PRIDE_COLORIZERS.get(type);
|
||||
ShapedRecipeBuilder.shaped(colorizer)
|
||||
.define('D', HexItems.AMETHYST_DUST)
|
||||
.define('C', material)
|
||||
.pattern(" D ")
|
||||
.pattern("DCD")
|
||||
.pattern(" D ")
|
||||
.unlockedBy("has_item", hasItem(HexItems.AMETHYST_DUST)).save(recipes);
|
||||
}
|
||||
|
||||
protected void specialRecipe(Consumer<FinishedRecipe> consumer, SimpleRecipeSerializer<?> serializer) {
|
||||
var name = Registry.RECIPE_SERIALIZER.getKey(serializer);
|
||||
SpecialRecipeBuilder.special(serializer).save(consumer, HexAPI.MOD_ID + ":dynamic/" + name.getPath());
|
||||
}
|
||||
}
|
|
@ -0,0 +1,486 @@
|
|||
package at.petrak.hexcasting.datagen.recipe
|
||||
|
||||
import at.petrak.hexcasting.api.HexAPI
|
||||
import at.petrak.hexcasting.api.advancements.OvercastTrigger
|
||||
import at.petrak.hexcasting.api.mod.HexItemTags
|
||||
import at.petrak.hexcasting.common.items.ItemWand
|
||||
import at.petrak.hexcasting.common.items.colorizer.ItemPrideColorizer
|
||||
import at.petrak.hexcasting.common.lib.HexBlocks
|
||||
import at.petrak.hexcasting.common.lib.HexItems
|
||||
import at.petrak.hexcasting.common.recipe.SealFocusRecipe
|
||||
import at.petrak.hexcasting.common.recipe.SealSpellbookRecipe
|
||||
import at.petrak.hexcasting.common.recipe.ingredient.StateIngredientHelper
|
||||
import at.petrak.hexcasting.common.recipe.ingredient.VillagerIngredient
|
||||
import at.petrak.hexcasting.datagen.IXplatConditionsBuilder
|
||||
import at.petrak.hexcasting.datagen.IXplatIngredients
|
||||
import at.petrak.hexcasting.datagen.recipe.builders.BrainsweepRecipeBuilder
|
||||
import at.petrak.hexcasting.datagen.recipe.builders.CompatIngredientValue
|
||||
import at.petrak.hexcasting.datagen.recipe.builders.CreateCrushingRecipeBuilder
|
||||
import at.petrak.hexcasting.datagen.recipe.builders.FarmersDelightCuttingRecipeBuilder
|
||||
import at.petrak.paucal.api.datagen.PaucalRecipeProvider
|
||||
import net.minecraft.advancements.critereon.EntityPredicate
|
||||
import net.minecraft.advancements.critereon.MinMaxBounds
|
||||
import net.minecraft.core.Registry
|
||||
import net.minecraft.data.DataGenerator
|
||||
import net.minecraft.data.recipes.*
|
||||
import net.minecraft.resources.ResourceLocation
|
||||
import net.minecraft.sounds.SoundEvents
|
||||
import net.minecraft.tags.ItemTags
|
||||
import net.minecraft.world.item.DyeColor
|
||||
import net.minecraft.world.item.DyeItem
|
||||
import net.minecraft.world.item.Item
|
||||
import net.minecraft.world.item.Items
|
||||
import net.minecraft.world.item.crafting.Ingredient
|
||||
import net.minecraft.world.item.crafting.SimpleRecipeSerializer
|
||||
import net.minecraft.world.level.block.Blocks
|
||||
import java.util.function.Consumer
|
||||
|
||||
class HexplatRecipes(
|
||||
val generator: DataGenerator,
|
||||
val ingredients: IXplatIngredients,
|
||||
val conditions: (RecipeBuilder) -> IXplatConditionsBuilder
|
||||
) : PaucalRecipeProvider(generator, HexAPI.MOD_ID) {
|
||||
|
||||
override fun makeRecipes(recipes: Consumer<FinishedRecipe>) {
|
||||
specialRecipe(recipes, SealFocusRecipe.SERIALIZER)
|
||||
specialRecipe(recipes, SealSpellbookRecipe.SERIALIZER)
|
||||
|
||||
wandRecipe(recipes, HexItems.WAND_OAK, Items.OAK_PLANKS)
|
||||
wandRecipe(recipes, HexItems.WAND_BIRCH, Items.BIRCH_PLANKS)
|
||||
wandRecipe(recipes, HexItems.WAND_SPRUCE, Items.SPRUCE_PLANKS)
|
||||
wandRecipe(recipes, HexItems.WAND_JUNGLE, Items.JUNGLE_PLANKS)
|
||||
wandRecipe(recipes, HexItems.WAND_DARK_OAK, Items.DARK_OAK_PLANKS)
|
||||
wandRecipe(recipes, HexItems.WAND_ACACIA, Items.ACACIA_PLANKS)
|
||||
wandRecipe(recipes, HexItems.WAND_CRIMSON, Items.CRIMSON_PLANKS)
|
||||
wandRecipe(recipes, HexItems.WAND_WARPED, Items.WARPED_PLANKS)
|
||||
wandRecipe(recipes, HexItems.WAND_AKASHIC, HexBlocks.AKASHIC_PLANKS.asItem())
|
||||
|
||||
ringCornered(HexItems.FOCUS, 1,
|
||||
ingredients.glowstoneDust(),
|
||||
ingredients.leather(),
|
||||
Ingredient.of(HexItems.CHARGED_AMETHYST))
|
||||
.unlockedBy("has_item", hasItem(HexItemTags.WANDS))
|
||||
.save(recipes)
|
||||
|
||||
ShapedRecipeBuilder.shaped(HexItems.SPELLBOOK)
|
||||
.define('N', ingredients.goldNugget())
|
||||
.define('B', Items.WRITABLE_BOOK)
|
||||
.define('A', HexItems.CHARGED_AMETHYST)
|
||||
.define('F', Items.CHORUS_FRUIT) // i wanna gate this behind the end SOMEHOW
|
||||
// hey look its my gender ^^
|
||||
.pattern("NBA")
|
||||
.pattern("NFA")
|
||||
.pattern("NBA")
|
||||
.unlockedBy("has_focus", hasItem(HexItems.FOCUS))
|
||||
.unlockedBy("has_chorus", hasItem(Items.CHORUS_FRUIT)).save(recipes)
|
||||
|
||||
ringCornerless(
|
||||
HexItems.CYPHER, 1,
|
||||
ingredients.copperIngot(),
|
||||
Ingredient.of(HexItems.AMETHYST_DUST))
|
||||
.unlockedBy("has_item", hasItem(HexItemTags.WANDS)).save(recipes)
|
||||
|
||||
ringCornerless(
|
||||
HexItems.TRINKET, 1,
|
||||
ingredients.ironIngot(),
|
||||
Ingredient.of(Items.AMETHYST_SHARD))
|
||||
.unlockedBy("has_item", hasItem(HexItemTags.WANDS)).save(recipes)
|
||||
|
||||
ShapedRecipeBuilder.shaped(HexItems.ARTIFACT)
|
||||
.define('F', ingredients.goldIngot())
|
||||
.define('A', HexItems.CHARGED_AMETHYST)
|
||||
// why in god's name does minecraft have two different places for item tags
|
||||
.define('D', ItemTags.MUSIC_DISCS)
|
||||
.pattern(" F ")
|
||||
.pattern("FAF")
|
||||
.pattern(" D ")
|
||||
.unlockedBy("has_item", hasItem(HexItemTags.WANDS)).save(recipes)
|
||||
|
||||
ringCornerless(HexItems.SCRYING_LENS, 1, Items.GLASS, HexItems.AMETHYST_DUST)
|
||||
.unlockedBy("has_item", hasItem(HexItemTags.WANDS)).save(recipes)
|
||||
|
||||
ShapedRecipeBuilder.shaped(HexItems.ABACUS)
|
||||
.define('S', Items.STICK)
|
||||
.define('A', Items.AMETHYST_SHARD)
|
||||
.define('W', ItemTags.PLANKS)
|
||||
.pattern("WAW")
|
||||
.pattern("SAS")
|
||||
.pattern("WAW")
|
||||
.unlockedBy("has_item", hasItem(HexItemTags.WANDS)).save(recipes)
|
||||
|
||||
// Why am I like this
|
||||
ShapedRecipeBuilder.shaped(HexItems.SUBMARINE_SANDWICH)
|
||||
.define('S', Items.STICK)
|
||||
.define('A', Items.AMETHYST_SHARD)
|
||||
.define('C', Items.COOKED_BEEF)
|
||||
.define('B', Items.BREAD)
|
||||
.pattern(" SA")
|
||||
.pattern(" C ")
|
||||
.pattern(" B ")
|
||||
.unlockedBy("has_item", hasItem(Items.AMETHYST_SHARD)).save(recipes)
|
||||
|
||||
for (dye in DyeColor.values()) {
|
||||
val item = HexItems.DYE_COLORIZERS[dye]!!
|
||||
ShapedRecipeBuilder.shaped(item)
|
||||
.define('D', HexItems.AMETHYST_DUST)
|
||||
.define('C', DyeItem.byColor(dye))
|
||||
.pattern(" D ")
|
||||
.pattern("DCD")
|
||||
.pattern(" D ")
|
||||
.unlockedBy("has_item", hasItem(HexItems.AMETHYST_DUST)).save(recipes)
|
||||
}
|
||||
|
||||
gayRecipe(recipes, ItemPrideColorizer.Type.AGENDER, Ingredient.of(Items.GLASS))
|
||||
gayRecipe(recipes, ItemPrideColorizer.Type.AROACE, Ingredient.of(Items.WHEAT_SEEDS))
|
||||
gayRecipe(recipes, ItemPrideColorizer.Type.AROMANTIC, Ingredient.of(Items.ARROW))
|
||||
gayRecipe(recipes, ItemPrideColorizer.Type.ASEXUAL, Ingredient.of(Items.BREAD))
|
||||
gayRecipe(recipes, ItemPrideColorizer.Type.BISEXUAL, Ingredient.of(Items.WHEAT))
|
||||
gayRecipe(recipes, ItemPrideColorizer.Type.DEMIBOY, Ingredient.of(Items.RAW_IRON))
|
||||
gayRecipe(recipes, ItemPrideColorizer.Type.DEMIGIRL, Ingredient.of(Items.RAW_COPPER))
|
||||
gayRecipe(recipes, ItemPrideColorizer.Type.GAY, Ingredient.of(Items.STONE_BRICK_WALL))
|
||||
gayRecipe(recipes, ItemPrideColorizer.Type.GENDERFLUID, Ingredient.of(Items.WATER_BUCKET))
|
||||
gayRecipe(recipes, ItemPrideColorizer.Type.GENDERQUEER, Ingredient.of(Items.GLASS_BOTTLE))
|
||||
gayRecipe(recipes, ItemPrideColorizer.Type.INTERSEX, Ingredient.of(Items.AZALEA))
|
||||
gayRecipe(recipes, ItemPrideColorizer.Type.LESBIAN, Ingredient.of(Items.HONEYCOMB))
|
||||
gayRecipe(recipes, ItemPrideColorizer.Type.NONBINARY, Ingredient.of(Items.MOSS_BLOCK))
|
||||
gayRecipe(recipes, ItemPrideColorizer.Type.PANSEXUAL, ingredients.whenModIngredient(
|
||||
Ingredient.of(Items.CARROT),
|
||||
"farmersdelight",
|
||||
CompatIngredientValue.of("farmersdelight:skillet")
|
||||
))
|
||||
gayRecipe(recipes, ItemPrideColorizer.Type.PLURAL, Ingredient.of(Items.REPEATER))
|
||||
gayRecipe(recipes, ItemPrideColorizer.Type.TRANSGENDER, Ingredient.of(Items.EGG))
|
||||
|
||||
ShapedRecipeBuilder.shaped(HexItems.UUID_COLORIZER)
|
||||
.define('B', Items.BOWL)
|
||||
.define('D', HexItems.AMETHYST_DUST)
|
||||
.define('C', Items.AMETHYST_SHARD)
|
||||
.pattern(" C ")
|
||||
.pattern(" D ")
|
||||
.pattern(" B ")
|
||||
.unlockedBy("has_item", hasItem(HexItems.AMETHYST_DUST)).save(recipes)
|
||||
|
||||
ShapedRecipeBuilder.shaped(HexItems.SCROLL_SMOL)
|
||||
.define('P', Items.PAPER)
|
||||
.define('A', Items.AMETHYST_SHARD)
|
||||
.pattern(" A")
|
||||
.pattern("P ")
|
||||
.unlockedBy("has_item", hasItem(Items.AMETHYST_SHARD)).save(recipes)
|
||||
|
||||
ShapedRecipeBuilder.shaped(HexItems.SCROLL_MEDIUM)
|
||||
.define('P', Items.PAPER)
|
||||
.define('A', Items.AMETHYST_SHARD)
|
||||
.pattern(" A")
|
||||
.pattern("PP ")
|
||||
.pattern("PP ")
|
||||
.unlockedBy("has_item", hasItem(Items.AMETHYST_SHARD)).save(recipes)
|
||||
|
||||
ShapedRecipeBuilder.shaped(HexItems.SCROLL_LARGE)
|
||||
.define('P', Items.PAPER)
|
||||
.define('A', Items.AMETHYST_SHARD)
|
||||
.pattern("PPA")
|
||||
.pattern("PPP")
|
||||
.pattern("PPP")
|
||||
.unlockedBy("has_item", hasItem(Items.AMETHYST_SHARD)).save(recipes)
|
||||
|
||||
ShapedRecipeBuilder.shaped(HexItems.SLATE, 6)
|
||||
.define('S', Items.DEEPSLATE)
|
||||
.define('A', HexItems.AMETHYST_DUST)
|
||||
.pattern(" A ")
|
||||
.pattern("SSS")
|
||||
.unlockedBy("has_item", hasItem(HexItems.AMETHYST_DUST)).save(recipes)
|
||||
|
||||
ShapedRecipeBuilder.shaped(HexItems.JEWELER_HAMMER)
|
||||
.define('I', ingredients.ironIngot())
|
||||
.define('N', ingredients.ironNugget())
|
||||
.define('A', Items.AMETHYST_SHARD)
|
||||
.define('S', ingredients.stick())
|
||||
.pattern("IAN")
|
||||
.pattern(" S ")
|
||||
.pattern(" S ")
|
||||
.unlockedBy("has_item", hasItem(Items.AMETHYST_SHARD)).save(recipes)
|
||||
|
||||
ShapedRecipeBuilder.shaped(HexBlocks.SLATE_BLOCK)
|
||||
.define('S', HexItems.SLATE)
|
||||
.pattern("S")
|
||||
.pattern("S")
|
||||
.unlockedBy("has_item", hasItem(HexItems.SLATE))
|
||||
.save(recipes, modLoc("slate_block_from_slates"))
|
||||
|
||||
ringAll(HexBlocks.SLATE_BLOCK, 8, Blocks.DEEPSLATE, HexItems.AMETHYST_DUST)
|
||||
.unlockedBy("has_item", hasItem(HexItems.SLATE)).save(recipes)
|
||||
|
||||
packing(HexItems.AMETHYST_DUST, HexBlocks.AMETHYST_DUST_BLOCK.asItem(), "amethyst_dust",
|
||||
false, recipes)
|
||||
|
||||
ringAll(HexBlocks.AMETHYST_TILES, 8, Blocks.AMETHYST_BLOCK, HexItems.AMETHYST_DUST)
|
||||
.unlockedBy("has_item", hasItem(HexItems.AMETHYST_DUST)).save(recipes)
|
||||
|
||||
SingleItemRecipeBuilder.stonecutting(Ingredient.of(Blocks.AMETHYST_BLOCK), HexBlocks.AMETHYST_TILES)
|
||||
.unlockedBy("has_item", hasItem(Blocks.AMETHYST_BLOCK))
|
||||
.save(recipes, modLoc("stonecutting/amethyst_tiles"))
|
||||
|
||||
ringAll(HexBlocks.SCROLL_PAPER, 8, Items.PAPER, Items.AMETHYST_SHARD)
|
||||
.unlockedBy("has_item", hasItem(Items.AMETHYST_SHARD)).save(recipes)
|
||||
|
||||
ShapelessRecipeBuilder.shapeless(HexBlocks.ANCIENT_SCROLL_PAPER, 8)
|
||||
.requires(ingredients.dyes()[DyeColor.BROWN]!!)
|
||||
.requires(HexBlocks.SCROLL_PAPER, 8)
|
||||
.unlockedBy("has_item", hasItem(HexBlocks.SCROLL_PAPER)).save(recipes)
|
||||
|
||||
stack(HexBlocks.SCROLL_PAPER_LANTERN, 1, HexBlocks.SCROLL_PAPER, Items.TORCH)
|
||||
.unlockedBy("has_item", hasItem(HexBlocks.SCROLL_PAPER)).save(recipes)
|
||||
|
||||
stack(HexBlocks.ANCIENT_SCROLL_PAPER_LANTERN, 1, HexBlocks.ANCIENT_SCROLL_PAPER, Items.TORCH)
|
||||
.unlockedBy("has_item", hasItem(HexBlocks.ANCIENT_SCROLL_PAPER)).save(recipes)
|
||||
|
||||
ShapelessRecipeBuilder.shapeless(HexBlocks.ANCIENT_SCROLL_PAPER_LANTERN, 8)
|
||||
.requires(ingredients.dyes()[DyeColor.BROWN]!!)
|
||||
.requires(HexBlocks.SCROLL_PAPER_LANTERN, 8)
|
||||
.unlockedBy("has_item", hasItem(HexBlocks.SCROLL_PAPER_LANTERN))
|
||||
.save(recipes, modLoc("ageing_scroll_paper_lantern"))
|
||||
|
||||
stack(HexBlocks.SCONCE, 4,
|
||||
Ingredient.of(HexItems.CHARGED_AMETHYST),
|
||||
ingredients.copperIngot())
|
||||
.unlockedBy("has_item", hasItem(HexItems.CHARGED_AMETHYST)).save(recipes)
|
||||
|
||||
ShapelessRecipeBuilder.shapeless(HexBlocks.AKASHIC_PLANKS, 4)
|
||||
.requires(HexItemTags.AKASHIC_LOGS)
|
||||
.unlockedBy("has_item", hasItem(HexItemTags.AKASHIC_LOGS)).save(recipes)
|
||||
|
||||
ShapedRecipeBuilder.shaped(HexBlocks.AKASHIC_WOOD, 3)
|
||||
.define('W', HexBlocks.AKASHIC_LOG)
|
||||
.pattern("WW")
|
||||
.pattern("WW")
|
||||
.unlockedBy("has_item", hasItem(HexBlocks.AKASHIC_LOG)).save(recipes)
|
||||
|
||||
ShapedRecipeBuilder.shaped(HexBlocks.AKASHIC_WOOD_STRIPPED, 3)
|
||||
.define('W', HexBlocks.AKASHIC_LOG_STRIPPED)
|
||||
.pattern("WW")
|
||||
.pattern("WW")
|
||||
.unlockedBy("has_item", hasItem(HexBlocks.AKASHIC_LOG_STRIPPED)).save(recipes)
|
||||
|
||||
ring(HexBlocks.AKASHIC_PANEL, 8,
|
||||
HexItemTags.AKASHIC_PLANKS, null)
|
||||
.unlockedBy("has_item", hasItem(HexItemTags.AKASHIC_PLANKS)).save(recipes)
|
||||
|
||||
ShapedRecipeBuilder.shaped(HexBlocks.AKASHIC_TILE, 6)
|
||||
.define('W', HexItemTags.AKASHIC_PLANKS)
|
||||
.pattern("WW ")
|
||||
.pattern("W W")
|
||||
.pattern(" WW")
|
||||
.unlockedBy("has_item", hasItem(HexItemTags.AKASHIC_PLANKS)).save(recipes)
|
||||
|
||||
ShapedRecipeBuilder.shaped(HexBlocks.AKASHIC_DOOR, 3)
|
||||
.define('W', HexItemTags.AKASHIC_PLANKS)
|
||||
.pattern("WW")
|
||||
.pattern("WW")
|
||||
.pattern("WW")
|
||||
.unlockedBy("has_item", hasItem(HexItemTags.AKASHIC_PLANKS)).save(recipes)
|
||||
|
||||
ShapedRecipeBuilder.shaped(HexBlocks.AKASHIC_TRAPDOOR, 2)
|
||||
.define('W', HexItemTags.AKASHIC_PLANKS)
|
||||
.pattern("WWW")
|
||||
.pattern("WWW")
|
||||
.unlockedBy("has_item", hasItem(HexItemTags.AKASHIC_PLANKS)).save(recipes)
|
||||
|
||||
ShapedRecipeBuilder.shaped(HexBlocks.AKASHIC_STAIRS, 4)
|
||||
.define('W', HexItemTags.AKASHIC_PLANKS)
|
||||
.pattern("W ")
|
||||
.pattern("WW ")
|
||||
.pattern("WWW")
|
||||
.unlockedBy("has_item", hasItem(HexItemTags.AKASHIC_PLANKS)).save(recipes)
|
||||
|
||||
ShapedRecipeBuilder.shaped(HexBlocks.AKASHIC_SLAB, 6)
|
||||
.define('W', HexItemTags.AKASHIC_PLANKS)
|
||||
.pattern("WWW")
|
||||
.unlockedBy("has_item", hasItem(HexItemTags.AKASHIC_PLANKS)).save(recipes)
|
||||
|
||||
ShapedRecipeBuilder.shaped(HexBlocks.AKASHIC_PRESSURE_PLATE, 1)
|
||||
.define('W', HexItemTags.AKASHIC_PLANKS)
|
||||
.pattern("WW")
|
||||
.unlockedBy("has_item", hasItem(HexItemTags.AKASHIC_PLANKS)).save(recipes)
|
||||
|
||||
ShapelessRecipeBuilder.shapeless(HexBlocks.AKASHIC_BUTTON)
|
||||
.requires(HexItemTags.AKASHIC_PLANKS)
|
||||
.unlockedBy("has_item", hasItem(HexItemTags.AKASHIC_PLANKS)).save(recipes)
|
||||
|
||||
val enlightenment = OvercastTrigger.Instance(
|
||||
EntityPredicate.Composite.ANY,
|
||||
MinMaxBounds.Ints.ANY, // add a little bit of slop here
|
||||
MinMaxBounds.Doubles.atLeast(0.8),
|
||||
MinMaxBounds.Doubles.between(0.1, 2.05)
|
||||
)
|
||||
ShapedRecipeBuilder.shaped(HexBlocks.EMPTY_IMPETUS)
|
||||
.define('B', Items.IRON_BARS)
|
||||
.define('A', HexItems.CHARGED_AMETHYST)
|
||||
.define('S', HexBlocks.SLATE_BLOCK)
|
||||
.define('P', Items.PURPUR_BLOCK)
|
||||
.pattern("PSS")
|
||||
.pattern("BAB")
|
||||
.pattern("SSP")
|
||||
.unlockedBy("enlightenment", enlightenment).save(recipes)
|
||||
|
||||
ShapedRecipeBuilder.shaped(HexBlocks.EMPTY_DIRECTRIX)
|
||||
.define('C', Items.COMPARATOR)
|
||||
.define('O', Items.OBSERVER)
|
||||
.define('A', HexItems.CHARGED_AMETHYST)
|
||||
.define('S', HexBlocks.SLATE_BLOCK)
|
||||
.pattern("CSS")
|
||||
.pattern("OAO")
|
||||
.pattern("SSC")
|
||||
.unlockedBy("enlightenment", enlightenment).save(recipes)
|
||||
|
||||
ShapedRecipeBuilder.shaped(HexBlocks.AKASHIC_BOOKSHELF)
|
||||
.define('L', HexItemTags.AKASHIC_LOGS)
|
||||
.define('P', HexItemTags.AKASHIC_PLANKS)
|
||||
.define('C', Items.BOOK)
|
||||
/*this is the*/ .pattern("LPL") // and what i have for you today is
|
||||
.pattern("CCC")
|
||||
.pattern("LPL")
|
||||
.unlockedBy("enlightenment", enlightenment).save(recipes)
|
||||
|
||||
ShapedRecipeBuilder.shaped(HexBlocks.AKASHIC_CONNECTOR)
|
||||
.define('L', HexItemTags.AKASHIC_LOGS)
|
||||
.define('P', HexItemTags.AKASHIC_PLANKS)
|
||||
.define('C', HexItems.CHARGED_AMETHYST)
|
||||
.pattern("LPL")
|
||||
.pattern("CCC")
|
||||
.pattern("LPL")
|
||||
.unlockedBy("enlightenment", enlightenment).save(recipes)
|
||||
|
||||
BrainsweepRecipeBuilder(StateIngredientHelper.of(Blocks.AMETHYST_BLOCK),
|
||||
VillagerIngredient(null, null, 3),
|
||||
Blocks.BUDDING_AMETHYST.defaultBlockState())
|
||||
.unlockedBy("enlightenment", enlightenment)
|
||||
.save(recipes, modLoc("brainsweep/budding_amethyst"))
|
||||
|
||||
BrainsweepRecipeBuilder(StateIngredientHelper.of(HexBlocks.EMPTY_IMPETUS),
|
||||
VillagerIngredient(ResourceLocation("toolsmith"), null, 2),
|
||||
HexBlocks.IMPETUS_RIGHTCLICK.defaultBlockState())
|
||||
.unlockedBy("enlightenment", enlightenment)
|
||||
.save(recipes, modLoc("brainsweep/impetus_rightclick"))
|
||||
|
||||
BrainsweepRecipeBuilder(StateIngredientHelper.of(HexBlocks.EMPTY_IMPETUS),
|
||||
VillagerIngredient(ResourceLocation("fletcher"), null, 2),
|
||||
HexBlocks.IMPETUS_LOOK.defaultBlockState())
|
||||
.unlockedBy("enlightenment", enlightenment)
|
||||
.save(recipes, modLoc("brainsweep/impetus_look"))
|
||||
|
||||
BrainsweepRecipeBuilder(StateIngredientHelper.of(HexBlocks.EMPTY_IMPETUS),
|
||||
VillagerIngredient(ResourceLocation("cleric"), null, 2),
|
||||
HexBlocks.IMPETUS_STOREDPLAYER.defaultBlockState())
|
||||
.unlockedBy("enlightenment", enlightenment)
|
||||
.save(recipes, modLoc("brainsweep/impetus_storedplayer"))
|
||||
|
||||
BrainsweepRecipeBuilder(StateIngredientHelper.of(HexBlocks.EMPTY_DIRECTRIX),
|
||||
VillagerIngredient(ResourceLocation("mason"), null, 1),
|
||||
HexBlocks.DIRECTRIX_REDSTONE.defaultBlockState())
|
||||
.unlockedBy("enlightenment", enlightenment)
|
||||
.save(recipes, modLoc("brainsweep/directrix_redstone"))
|
||||
|
||||
BrainsweepRecipeBuilder(StateIngredientHelper.of(HexBlocks.AKASHIC_CONNECTOR),
|
||||
VillagerIngredient(ResourceLocation("librarian"), null, 5),
|
||||
HexBlocks.AKASHIC_RECORD.defaultBlockState())
|
||||
.unlockedBy("enlightenment", enlightenment)
|
||||
.save(recipes, modLoc("brainsweep/akashic_record"))
|
||||
|
||||
// Create compat
|
||||
CreateCrushingRecipeBuilder()
|
||||
.withInput(Blocks.AMETHYST_CLUSTER)
|
||||
.duration(150)
|
||||
.withOutput(Items.AMETHYST_SHARD, 7)
|
||||
.withOutput(HexItems.AMETHYST_DUST, 5)
|
||||
.withOutput(0.25f, HexItems.CHARGED_AMETHYST)
|
||||
.withConditions()
|
||||
.whenModLoaded("create")
|
||||
.save(recipes, ResourceLocation("create", "crushing/amethyst_cluster"))
|
||||
|
||||
CreateCrushingRecipeBuilder()
|
||||
.withInput(Blocks.AMETHYST_BLOCK)
|
||||
.duration(150)
|
||||
.withOutput(Items.AMETHYST_SHARD, 3)
|
||||
.withOutput(0.5f, HexItems.AMETHYST_DUST, 4)
|
||||
.withConditions()
|
||||
.whenModLoaded("create")
|
||||
.save(recipes, ResourceLocation("create", "crushing/amethyst_block"))
|
||||
|
||||
CreateCrushingRecipeBuilder()
|
||||
.withInput(Items.AMETHYST_SHARD)
|
||||
.duration(150)
|
||||
.withOutput(HexItems.AMETHYST_DUST, 4)
|
||||
.withOutput(0.5f, HexItems.AMETHYST_DUST)
|
||||
.withConditions()
|
||||
.whenModLoaded("create")
|
||||
.save(recipes, modLoc("compat/create/crushing/amethyst_shard"))
|
||||
|
||||
// FD compat
|
||||
FarmersDelightCuttingRecipeBuilder()
|
||||
.withInput(HexBlocks.AKASHIC_LOG)
|
||||
.withTool(ingredients.axeStrip())
|
||||
.withOutput(HexBlocks.AKASHIC_LOG_STRIPPED)
|
||||
.withOutput("farmersdelight:tree_bark")
|
||||
.withSound(SoundEvents.AXE_STRIP)
|
||||
.withConditions()
|
||||
.whenModLoaded("farmersdelight")
|
||||
.save(recipes, modLoc("compat/farmersdelight/cutting/akashic_log"))
|
||||
|
||||
FarmersDelightCuttingRecipeBuilder()
|
||||
.withInput(HexBlocks.AKASHIC_WOOD)
|
||||
.withTool(ingredients.axeStrip())
|
||||
.withOutput(HexBlocks.AKASHIC_WOOD_STRIPPED)
|
||||
.withOutput("farmersdelight:tree_bark")
|
||||
.withSound(SoundEvents.AXE_STRIP)
|
||||
.withConditions()
|
||||
.whenModLoaded("farmersdelight")
|
||||
.save(recipes, modLoc("compat/farmersdelight/cutting/akashic_wood"))
|
||||
|
||||
FarmersDelightCuttingRecipeBuilder()
|
||||
.withInput(HexBlocks.AKASHIC_TRAPDOOR)
|
||||
.withTool(ingredients.axeDig())
|
||||
.withOutput(HexBlocks.AKASHIC_PLANKS)
|
||||
.withConditions()
|
||||
.whenModLoaded("farmersdelight")
|
||||
.save(recipes, modLoc("compat/farmersdelight/cutting/akashic_trapdoor"))
|
||||
|
||||
FarmersDelightCuttingRecipeBuilder()
|
||||
.withInput(HexBlocks.AKASHIC_DOOR)
|
||||
.withTool(ingredients.axeDig())
|
||||
.withOutput(HexBlocks.AKASHIC_PLANKS)
|
||||
.withConditions()
|
||||
.whenModLoaded("farmersdelight")
|
||||
.save(recipes, modLoc("compat/farmersdelight/cutting/akashic_door"))
|
||||
}
|
||||
|
||||
private fun wandRecipe(recipes: Consumer<FinishedRecipe>, wand: ItemWand, plank: Item) {
|
||||
ShapedRecipeBuilder.shaped(wand)
|
||||
.define('W', plank)
|
||||
.define('S', Items.STICK)
|
||||
.define('A', HexItems.CHARGED_AMETHYST)
|
||||
.pattern(" SA")
|
||||
.pattern(" WS")
|
||||
.pattern("S ")
|
||||
.unlockedBy("has_item", hasItem(HexItems.CHARGED_AMETHYST))
|
||||
.save(recipes)
|
||||
}
|
||||
|
||||
private fun gayRecipe(recipes: Consumer<FinishedRecipe>, type: ItemPrideColorizer.Type, material: Ingredient) {
|
||||
val colorizer = HexItems.PRIDE_COLORIZERS[type]!!
|
||||
ShapedRecipeBuilder.shaped(colorizer)
|
||||
.define('D', HexItems.AMETHYST_DUST)
|
||||
.define('C', material)
|
||||
.pattern(" D ")
|
||||
.pattern("DCD")
|
||||
.pattern(" D ")
|
||||
.unlockedBy("has_item", hasItem(HexItems.AMETHYST_DUST))
|
||||
.save(recipes)
|
||||
}
|
||||
|
||||
private fun specialRecipe(consumer: Consumer<FinishedRecipe>, serializer: SimpleRecipeSerializer<*>) {
|
||||
val name = Registry.RECIPE_SERIALIZER.getKey(serializer)
|
||||
SpecialRecipeBuilder.special(serializer).save(consumer, HexAPI.MOD_ID + ":dynamic/" + name!!.path)
|
||||
}
|
||||
|
||||
private fun RecipeBuilder.withConditions(): IXplatConditionsBuilder = conditions(this)
|
||||
}
|
|
@ -0,0 +1,33 @@
|
|||
package at.petrak.hexcasting.datagen.recipe.builders;
|
||||
|
||||
import com.google.gson.JsonObject;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import net.minecraft.world.item.crafting.Ingredient;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
public class CompatIngredientValue implements Ingredient.Value {
|
||||
public final String item;
|
||||
|
||||
public CompatIngredientValue(String name) {
|
||||
this.item = name;
|
||||
}
|
||||
|
||||
public @NotNull Collection<ItemStack> getItems() {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
public @NotNull JsonObject serialize() {
|
||||
JsonObject jsonObject = new JsonObject();
|
||||
jsonObject.addProperty("item", item);
|
||||
return jsonObject;
|
||||
}
|
||||
|
||||
public static Ingredient of(String itemName) {
|
||||
return new Ingredient(Stream.of(new CompatIngredientValue(itemName)));
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
package at.petrak.hexcasting.datagen.recipe.builders;
|
||||
|
||||
import com.google.gson.JsonObject;
|
||||
|
||||
public record CompatProcessingOutput(String name, int count, float chance) implements ProcessingOutput {
|
||||
@Override
|
||||
public JsonObject serialize() {
|
||||
JsonObject json = new JsonObject();
|
||||
json.addProperty("item", name);
|
||||
if (count != 1) {
|
||||
json.addProperty("count", count);
|
||||
}
|
||||
if (chance != 1) {
|
||||
json.addProperty("chance", chance);
|
||||
}
|
||||
return json;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,163 @@
|
|||
package at.petrak.hexcasting.datagen.recipe.builders;
|
||||
|
||||
import com.google.gson.JsonArray;
|
||||
import com.google.gson.JsonObject;
|
||||
import net.minecraft.advancements.CriterionTriggerInstance;
|
||||
import net.minecraft.data.recipes.FinishedRecipe;
|
||||
import net.minecraft.data.recipes.RecipeBuilder;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.world.item.Item;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import net.minecraft.world.item.Items;
|
||||
import net.minecraft.world.item.crafting.Ingredient;
|
||||
import net.minecraft.world.item.crafting.RecipeSerializer;
|
||||
import net.minecraft.world.level.ItemLike;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
// Largely adapted from the following classes:
|
||||
// https://github.com/Creators-of-Create/Create/blob/82be76d8934af03b4e52cad6a9f74a4175ba7b05/src/main/java/com/simibubi/create/content/contraptions/processing/ProcessingOutput.java
|
||||
// https://github.com/Creators-of-Create/Create/blob/82be76d8934af03b4e52cad6a9f74a4175ba7b05/src/main/java/com/simibubi/create/foundation/data/recipe/ProcessingRecipeGen.java
|
||||
// https://github.com/Creators-of-Create/Create/blob/82be76d8934af03b4e52cad6a9f74a4175ba7b05/src/main/java/com/simibubi/create/content/contraptions/processing/ProcessingRecipeBuilder.java
|
||||
// https://github.com/Creators-of-Create/Create/blob/82be76d8934af03b4e52cad6a9f74a4175ba7b05/src/main/java/com/simibubi/create/content/contraptions/processing/ProcessingRecipeSerializer.java
|
||||
public class CreateCrushingRecipeBuilder implements RecipeBuilder {
|
||||
private String group = "";
|
||||
private Ingredient input;
|
||||
private final List<ProcessingOutput> results = new ArrayList<>();
|
||||
private int processingTime = 100;
|
||||
|
||||
@Override
|
||||
public @NotNull CreateCrushingRecipeBuilder unlockedBy(@NotNull String name, @NotNull CriterionTriggerInstance trigger) {
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull CreateCrushingRecipeBuilder group(@Nullable String name) {
|
||||
group = name;
|
||||
return this;
|
||||
}
|
||||
|
||||
public CreateCrushingRecipeBuilder duration(int duration) {
|
||||
this.processingTime = duration;
|
||||
return this;
|
||||
}
|
||||
|
||||
public CreateCrushingRecipeBuilder withInput(ItemLike item) {
|
||||
return withInput(Ingredient.of(item));
|
||||
}
|
||||
|
||||
public CreateCrushingRecipeBuilder withInput(ItemStack stack) {
|
||||
return withInput(Ingredient.of(stack));
|
||||
}
|
||||
|
||||
public CreateCrushingRecipeBuilder withInput(Ingredient ingredient) {
|
||||
this.input = ingredient;
|
||||
return this;
|
||||
}
|
||||
|
||||
public CreateCrushingRecipeBuilder withOutput(ItemLike output) {
|
||||
return withOutput(1f, output, 1);
|
||||
}
|
||||
|
||||
public CreateCrushingRecipeBuilder withOutput(float chance, ItemLike output) {
|
||||
return withOutput(chance, output, 1);
|
||||
}
|
||||
|
||||
public CreateCrushingRecipeBuilder withOutput(ItemLike output, int count) {
|
||||
return withOutput(1f, output, count);
|
||||
}
|
||||
|
||||
public CreateCrushingRecipeBuilder withOutput(float chance, ItemLike output, int count) {
|
||||
return withOutput(new ItemStack(output, count), chance);
|
||||
}
|
||||
|
||||
public CreateCrushingRecipeBuilder withOutput(ItemStack output, float chance) {
|
||||
this.results.add(new ItemProcessingOutput(output, chance));
|
||||
return this;
|
||||
}
|
||||
|
||||
public CreateCrushingRecipeBuilder withOutput(String name) {
|
||||
return withOutput(1f, name, 1);
|
||||
}
|
||||
|
||||
public CreateCrushingRecipeBuilder withOutput(String name, int count) {
|
||||
return withOutput(1f, name, count);
|
||||
}
|
||||
|
||||
public CreateCrushingRecipeBuilder withOutput(float chance, String name) {
|
||||
return withOutput(chance, name, 1);
|
||||
}
|
||||
|
||||
public CreateCrushingRecipeBuilder withOutput(float chance, String name, int count) {
|
||||
this.results.add(new CompatProcessingOutput(name, count, chance));
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull Item getResult() {
|
||||
return Items.AIR; // Irrelevant, we implement serialization ourselves
|
||||
}
|
||||
|
||||
@Override
|
||||
public void save(@NotNull Consumer<FinishedRecipe> consumer, @NotNull ResourceLocation resourceLocation) {
|
||||
consumer.accept(new CrushingRecipe(resourceLocation));
|
||||
}
|
||||
|
||||
public class CrushingRecipe implements FinishedRecipe {
|
||||
|
||||
private final ResourceLocation id;
|
||||
|
||||
public CrushingRecipe(ResourceLocation id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void serializeRecipeData(@NotNull JsonObject json) {
|
||||
json.addProperty("type", "create:crushing");
|
||||
|
||||
if (!group.isEmpty()) {
|
||||
json.addProperty("group", group);
|
||||
}
|
||||
|
||||
JsonArray jsonIngredients = new JsonArray();
|
||||
JsonArray jsonOutputs = new JsonArray();
|
||||
|
||||
jsonIngredients.add(input.toJson());
|
||||
|
||||
results.forEach(o -> jsonOutputs.add(o.serialize()));
|
||||
|
||||
json.add("ingredients", jsonIngredients);
|
||||
json.add("results", jsonOutputs);
|
||||
|
||||
int processingDuration = processingTime;
|
||||
if (processingDuration > 0) {
|
||||
json.addProperty("processingTime", processingDuration);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull ResourceLocation getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull RecipeSerializer<?> getType() {
|
||||
return RecipeSerializer.SHAPELESS_RECIPE; // Irrelevant, we implement serialization ourselves
|
||||
}
|
||||
|
||||
@Override
|
||||
public JsonObject serializeAdvancement() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ResourceLocation getAdvancementId() {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,165 @@
|
|||
package at.petrak.hexcasting.datagen.recipe.builders;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
import com.google.gson.JsonArray;
|
||||
import com.google.gson.JsonObject;
|
||||
import net.minecraft.advancements.CriterionTriggerInstance;
|
||||
import net.minecraft.core.Registry;
|
||||
import net.minecraft.data.recipes.FinishedRecipe;
|
||||
import net.minecraft.data.recipes.RecipeBuilder;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.sounds.SoundEvent;
|
||||
import net.minecraft.world.item.Item;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import net.minecraft.world.item.Items;
|
||||
import net.minecraft.world.item.crafting.Ingredient;
|
||||
import net.minecraft.world.item.crafting.RecipeSerializer;
|
||||
import net.minecraft.world.level.ItemLike;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
public class FarmersDelightCuttingRecipeBuilder implements RecipeBuilder {
|
||||
private String group = "";
|
||||
private final List<ProcessingOutput> outputs = Lists.newArrayList();
|
||||
private Ingredient input;
|
||||
private FarmersDelightToolIngredient toolAction;
|
||||
private SoundEvent sound;
|
||||
|
||||
@Override
|
||||
public FarmersDelightCuttingRecipeBuilder unlockedBy(String s, CriterionTriggerInstance criterionTriggerInstance) {
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull FarmersDelightCuttingRecipeBuilder group(@Nullable String name) {
|
||||
group = name;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Item getResult() {
|
||||
return Items.AIR; // Irrelevant, we implement serialization ourselves
|
||||
}
|
||||
|
||||
public FarmersDelightCuttingRecipeBuilder withInput(ItemLike item) {
|
||||
return withInput(Ingredient.of(item));
|
||||
}
|
||||
|
||||
public FarmersDelightCuttingRecipeBuilder withInput(ItemStack stack) {
|
||||
return withInput(Ingredient.of(stack));
|
||||
}
|
||||
|
||||
public FarmersDelightCuttingRecipeBuilder withInput(Ingredient ingredient) {
|
||||
this.input = ingredient;
|
||||
return this;
|
||||
}
|
||||
|
||||
public FarmersDelightCuttingRecipeBuilder withOutput(ItemLike output) {
|
||||
return withOutput(1f, output, 1);
|
||||
}
|
||||
|
||||
public FarmersDelightCuttingRecipeBuilder withOutput(float chance, ItemLike output) {
|
||||
return withOutput(chance, output, 1);
|
||||
}
|
||||
|
||||
public FarmersDelightCuttingRecipeBuilder withOutput(ItemLike output, int count) {
|
||||
return withOutput(1f, output, count);
|
||||
}
|
||||
|
||||
public FarmersDelightCuttingRecipeBuilder withOutput(float chance, ItemLike output, int count) {
|
||||
return withOutput(new ItemStack(output, count), chance);
|
||||
}
|
||||
|
||||
public FarmersDelightCuttingRecipeBuilder withOutput(ItemStack output, float chance) {
|
||||
this.outputs.add(new ItemProcessingOutput(output, chance));
|
||||
return this;
|
||||
}
|
||||
|
||||
public FarmersDelightCuttingRecipeBuilder withOutput(String name) {
|
||||
return withOutput(1f, name, 1);
|
||||
}
|
||||
|
||||
public FarmersDelightCuttingRecipeBuilder withOutput(String name, int count) {
|
||||
return withOutput(1f, name, count);
|
||||
}
|
||||
|
||||
public FarmersDelightCuttingRecipeBuilder withOutput(float chance, String name) {
|
||||
return withOutput(chance, name, 1);
|
||||
}
|
||||
|
||||
public FarmersDelightCuttingRecipeBuilder withOutput(float chance, String name, int count) {
|
||||
this.outputs.add(new CompatProcessingOutput(name, count, chance));
|
||||
return this;
|
||||
}
|
||||
|
||||
public FarmersDelightCuttingRecipeBuilder withTool(FarmersDelightToolIngredient ingredient) {
|
||||
this.toolAction = ingredient;
|
||||
return this;
|
||||
}
|
||||
|
||||
public FarmersDelightCuttingRecipeBuilder withSound(SoundEvent sound) {
|
||||
this.sound = sound;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void save(Consumer<FinishedRecipe> consumer, ResourceLocation resourceLocation) {
|
||||
consumer.accept(new CuttingRecipe(resourceLocation));
|
||||
}
|
||||
|
||||
public class CuttingRecipe implements FinishedRecipe {
|
||||
|
||||
private final ResourceLocation id;
|
||||
|
||||
public CuttingRecipe(ResourceLocation id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void serializeRecipeData(@NotNull JsonObject json) {
|
||||
json.addProperty("type", "farmersdelight:cutting");
|
||||
|
||||
if (!group.isEmpty()) {
|
||||
json.addProperty("group", group);
|
||||
}
|
||||
|
||||
JsonArray jsonIngredients = new JsonArray();
|
||||
JsonArray jsonOutputs = new JsonArray();
|
||||
|
||||
jsonIngredients.add(input.toJson());
|
||||
|
||||
outputs.forEach(o -> jsonOutputs.add(o.serialize()));
|
||||
|
||||
json.add("ingredients", jsonIngredients);
|
||||
json.add("tool", toolAction.serialize());
|
||||
json.add("result", jsonOutputs);
|
||||
|
||||
if (sound != null) {
|
||||
json.addProperty("sound", Registry.SOUND_EVENT.getKey(sound).toString());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull ResourceLocation getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull RecipeSerializer<?> getType() {
|
||||
return RecipeSerializer.SHAPELESS_RECIPE; // Irrelevant, we implement serialization ourselves
|
||||
}
|
||||
|
||||
@Override
|
||||
public JsonObject serializeAdvancement() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ResourceLocation getAdvancementId() {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
package at.petrak.hexcasting.datagen.recipe.builders;
|
||||
|
||||
import com.google.gson.JsonObject;
|
||||
|
||||
public interface FarmersDelightToolIngredient {
|
||||
JsonObject serialize();
|
||||
}
|
|
@ -0,0 +1,27 @@
|
|||
package at.petrak.hexcasting.datagen.recipe.builders;
|
||||
|
||||
import com.google.gson.JsonObject;
|
||||
import com.google.gson.JsonParser;
|
||||
import net.minecraft.core.Registry;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
|
||||
public record ItemProcessingOutput(ItemStack stack, float chance) implements ProcessingOutput {
|
||||
@Override
|
||||
public JsonObject serialize() {
|
||||
JsonObject json = new JsonObject();
|
||||
ResourceLocation resourceLocation = Registry.ITEM.getKey(stack.getItem());
|
||||
json.addProperty("item", resourceLocation.toString());
|
||||
int count = stack.getCount();
|
||||
if (count != 1) {
|
||||
json.addProperty("count", count);
|
||||
}
|
||||
if (stack.hasTag()) {
|
||||
json.add("nbt", JsonParser.parseString(stack.getTag().toString()));
|
||||
}
|
||||
if (chance != 1) {
|
||||
json.addProperty("chance", chance);
|
||||
}
|
||||
return json;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
package at.petrak.hexcasting.datagen.recipe.builders;
|
||||
|
||||
import com.google.gson.JsonObject;
|
||||
|
||||
public interface ProcessingOutput {
|
||||
JsonObject serialize();
|
||||
}
|
|
@ -1,6 +1,7 @@
|
|||
package at.petrak.hexcasting.interop;
|
||||
|
||||
import at.petrak.hexcasting.interop.pehkui.PehkuiInterop;
|
||||
import at.petrak.hexcasting.xplat.IClientXplatAbstractions;
|
||||
import at.petrak.hexcasting.xplat.IXplatAbstractions;
|
||||
import at.petrak.hexcasting.xplat.Platform;
|
||||
import vazkii.patchouli.api.PatchouliAPI;
|
||||
|
@ -13,10 +14,12 @@ public class HexInterop {
|
|||
public static final String PEHKUI_ID = "pehkui";
|
||||
|
||||
public static final class Forge {
|
||||
public static final String CURIOS_API_ID = "curios";
|
||||
}
|
||||
|
||||
public static final class Fabric {
|
||||
public static final String GRAVITY_CHANGER_API_ID = "gravitychanger";
|
||||
public static final String TRINKETS_API_ID = "trinkets";
|
||||
}
|
||||
|
||||
public static void init() {
|
||||
|
@ -30,6 +33,10 @@ public class HexInterop {
|
|||
xplat.initPlatformSpecific();
|
||||
}
|
||||
|
||||
public static void clientInit() {
|
||||
IClientXplatAbstractions.INSTANCE.initPlatformSpecific();
|
||||
}
|
||||
|
||||
private static void initPatchouli() {
|
||||
var integrations = List.of(PEHKUI_ID);
|
||||
|
||||
|
|
|
@ -0,0 +1,33 @@
|
|||
package at.petrak.hexcasting.interop.patchouli;
|
||||
|
||||
import net.minecraft.client.resources.language.I18n;
|
||||
import vazkii.patchouli.api.IComponentProcessor;
|
||||
import vazkii.patchouli.api.IVariable;
|
||||
import vazkii.patchouli.api.IVariableProvider;
|
||||
|
||||
public class PatternProcessor implements IComponentProcessor {
|
||||
private String translationKey;
|
||||
|
||||
@Override
|
||||
public void setup(IVariableProvider vars) {
|
||||
if (vars.has("header"))
|
||||
translationKey = vars.get("header").asString();
|
||||
else {
|
||||
IVariable key = vars.get("op_id");
|
||||
String opName = key.asString();
|
||||
|
||||
String prefix = "hexcasting.spell.";
|
||||
boolean hasOverride = I18n.exists(prefix + "book." + opName);
|
||||
translationKey = prefix + (hasOverride ? "book." : "") + opName;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public IVariable process(String key) {
|
||||
if (key.equals("translation_key")) {
|
||||
return IVariable.wrap(translationKey);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue