new spells get your new spells

This commit is contained in:
gamma-delta 2022-01-23 17:20:59 -06:00
parent 524384f1d5
commit fb2ebc568a
18 changed files with 506 additions and 207 deletions

View file

@ -17,7 +17,7 @@ apply plugin: 'maven-publish'
apply plugin: 'net.minecraftforge.gradle'
apply plugin: 'org.parchmentmc.librarian.forgegradle'
version = '0.2.1'
version = '0.3.0'
group = 'at.petra-k.hexcasting' // http://maven.apache.org/guides/mini/guide-naming-conventions.html
archivesBaseName = 'hexcasting'

View file

@ -20,7 +20,7 @@ public class HexConfig {
public HexConfig(ForgeConfigSpec.Builder builder) {
manaToHealthRate = builder.comment("How many points of mana a half-heart is worth when casting from HP")
.defineInRange("manaToHealthRate", 100_000.0 / 20.0, 0.0, Double.POSITIVE_INFINITY);
.defineInRange("manaToHealthRate", 200_000.0 / 20.0, 0.0, Double.POSITIVE_INFINITY);
patternPointSpeedMultiplier = builder.comment(
"How fast the point showing you the stroke order on patterns moves")
.defineInRange("manaToHealthRate", 1.0, 0.0, Double.POSITIVE_INFINITY);

View file

@ -58,14 +58,16 @@ data class CastingContext(
}
}
/**
* Check to make sure a vec is in range
*/
fun assertVecInRange(vec: Vec3) {
if (vec.distanceToSqr(this.caster.position()) > Operator.MAX_DISTANCE * Operator.MAX_DISTANCE)
throw CastException(CastException.Reason.TOO_FAR, vec)
}
/**
* Return the slot from which to take blocks and itempicking.json.
* Return the slot from which to take blocks and itemsn.
*/
// https://wiki.vg/Inventory is WRONG
// slots 0-8 are the hotbar
@ -97,7 +99,7 @@ data class CastingContext(
}
/**
* Remove the given gound of the specified item from somewhere in the inventory, favoring slots not in the hotbar.
* Remove the given count of the specified item from somewhere in the inventory, favoring slots not in the hotbar.
* 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

View file

@ -87,6 +87,13 @@ class CastingHarness private constructor(
// we know the operator is ok here
val (manaCost, spells) = operator!!.modifyStack(this.stack, this.ctx)
val leftover = this.withdrawMana(manaCost, ctx.canOvercast)
if (ctx.caster.isDeadOrDying)
return CastResult.Died
else if (leftover > 0)
return CastResult.QuitCasting
// is great IMPLIES caster is enlightened
if (!operator.isGreat || ctx.isCasterEnlightened) {
spellsToCast = spells
@ -94,9 +101,6 @@ class CastingHarness private constructor(
Advancements.FAIL_GREAT_SPELL_TRIGGER.trigger(ctx.caster)
}
this.withdrawMana(manaCost, ctx.canOvercast)
if (ctx.caster.isDeadOrDying)
return CastResult.Died
}
if (spellsToCast.isNotEmpty()) {

View file

@ -14,8 +14,10 @@ import at.petrak.hexcasting.common.casting.operators.selectors.OpGetCaster;
import at.petrak.hexcasting.common.casting.operators.selectors.OpGetEntitiesBy;
import at.petrak.hexcasting.common.casting.operators.selectors.OpGetEntityAt;
import at.petrak.hexcasting.common.casting.operators.spells.*;
import at.petrak.hexcasting.common.casting.operators.spells.great.OpCreateLava;
import at.petrak.hexcasting.common.casting.operators.spells.great.OpFlight;
import at.petrak.hexcasting.common.casting.operators.spells.great.OpLightning;
import at.petrak.hexcasting.common.casting.operators.spells.great.OpTeleport;
import at.petrak.hexcasting.common.items.magic.ItemArtifact;
import at.petrak.hexcasting.common.items.magic.ItemCypher;
import at.petrak.hexcasting.common.items.magic.ItemTrinket;
@ -100,12 +102,19 @@ public class RegisterPatterns {
new OpMakePackagedSpell<>(ItemTrinket.class, 500_000));
PatternRegistry.addRegularPattern("wwaqqqqqeawqwqwqwqwqwwqqeadaeqqeqqeadaeqq",
new OpMakePackagedSpell<>(ItemArtifact.class, 1_000_000));
PatternRegistry.addRegularPattern("aqawqadaq", OpCreateWater.INSTANCE);
PatternRegistry.addRegularPattern("dedwedade", OpDestroyWater.INSTANCE);
PatternRegistry.addRegularPatternPerWorld(HexPattern.FromAnglesSig("waadwawdaaweewq", HexDir.EAST),
prefix("lightning"), OpLightning.INSTANCE);
PatternRegistry.addRegularPatternPerWorld(HexPattern.FromAnglesSig("eawwaeawawaa", HexDir.NORTH_EAST),
prefix("flight"), OpFlight.INSTANCE);
PatternRegistry.addRegularPatternPerWorld(HexPattern.FromAnglesSig("eaqawqadaqd", HexDir.EAST),
prefix("create_lava"), OpCreateLava.INSTANCE);
PatternRegistry.addRegularPatternPerWorld(
HexPattern.FromAnglesSig("wwwqqqwwwqqeqqwwwqqwqqdqqqqqdqq", HexDir.EAST),
prefix("teleport"), OpTeleport.INSTANCE);
// == Meta stuff ==
PatternRegistry.addRegularPattern("qqq", Widget.OPEN_PAREN);

View file

@ -21,6 +21,7 @@ object OpBlink : SpellOperator {
val delta = args.getChecked<Double>(1)
ctx.assertVecInRange(target.position())
ctx.assertVecInRange(target.position().add(target.lookAngle.scale(delta)))
return Pair(
Spell(target, delta),
@ -28,7 +29,7 @@ object OpBlink : SpellOperator {
)
}
class Spell(val target: Entity, val delta: Double) : RenderedSpell {
private data class Spell(val target: Entity, val delta: Double) : RenderedSpell {
override fun cast(ctx: CastingContext) {
val look = target.lookAngle
// https://github.com/VazkiiMods/Psi/blob/master/src/main/java/vazkii/psi/common/spell/trick/entity/PieceTrickBlink.java#L74

View file

@ -0,0 +1,38 @@
package at.petrak.hexcasting.common.casting.operators.spells
import at.petrak.hexcasting.HexMod
import at.petrak.hexcasting.api.Operator.Companion.getChecked
import at.petrak.hexcasting.api.RenderedSpell
import at.petrak.hexcasting.api.SpellDatum
import at.petrak.hexcasting.api.SpellOperator
import at.petrak.hexcasting.common.casting.CastingContext
import net.minecraft.core.BlockPos
import net.minecraft.world.item.BucketItem
import net.minecraft.world.item.Items
import net.minecraft.world.phys.Vec3
object OpCreateWater : SpellOperator {
override val argc = 1
override fun execute(args: List<SpellDatum<*>>, ctx: CastingContext): Pair<RenderedSpell, Int> {
val target = args.getChecked<Vec3>(0)
ctx.assertVecInRange(target)
return Pair(
Spell(target),
10_000
)
}
private data class Spell(val target: Vec3) : RenderedSpell {
override fun cast(ctx: CastingContext) {
// 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, BlockPos(target), null)
} else {
HexMod.getLogger().warn("Items.WATER_BUCKET wasn't a BucketItem?")
}
}
}
}

View file

@ -0,0 +1,112 @@
package at.petrak.hexcasting.common.casting.operators.spells
import at.petrak.hexcasting.api.Operator
import at.petrak.hexcasting.api.Operator.Companion.getChecked
import at.petrak.hexcasting.api.RenderedSpell
import at.petrak.hexcasting.api.SpellDatum
import at.petrak.hexcasting.api.SpellOperator
import at.petrak.hexcasting.common.casting.CastingContext
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.tags.FluidTags
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.entity.BlockEntity
import net.minecraft.world.level.material.Material
import net.minecraft.world.phys.Vec3
object OpDestroyWater : SpellOperator {
override val argc = 1
override fun execute(args: List<SpellDatum<*>>, ctx: CastingContext): Pair<RenderedSpell, Int> {
val target = args.getChecked<Vec3>(0)
ctx.assertVecInRange(target)
return Pair(
Spell(target),
200_000
)
}
const val MAX_DESTROY_COUNT = 1024
private data class Spell(val target: Vec3) : RenderedSpell {
override fun cast(ctx: CastingContext) {
// SpongeBlock.java
val todo = ArrayDeque<BlockPos>()
val seen = HashSet<BlockPos>()
todo.add(BlockPos(target))
var successes = 0
while (todo.isNotEmpty() && successes <= MAX_DESTROY_COUNT) {
val here = todo.removeFirst()
val distFromFocus =
ctx.caster.position().distanceToSqr(Vec3(here.x.toDouble(), here.y.toDouble(), here.z.toDouble()))
if (distFromFocus < Operator.MAX_DISTANCE * Operator.MAX_DISTANCE && seen.add(here)) {
// never seen this pos in my life
val fluid = ctx.world.getFluidState(here)
if (fluid.`is`(FluidTags.WATER)) {
val blockstate = ctx.world.getBlockState(here)
val material = blockstate.material
val success =
if (blockstate.block is BucketPickup && !(blockstate.block as BucketPickup).pickupBlock(
ctx.world,
here,
blockstate
).isEmpty
) {
true
} else if (blockstate.block is LiquidBlock) {
ctx.world.setBlock(here, Blocks.AIR.defaultBlockState(), 3)
true
} else if (material == Material.WATER_PLANT || material == Material.REPLACEABLE_WATER_PLANT) {
val blockentity: BlockEntity? =
if (blockstate.hasBlockEntity()) ctx.world.getBlockEntity(here) else null
Block.dropResources(blockstate, ctx.world, here, blockentity)
ctx.world.setBlock(here, Blocks.AIR.defaultBlockState(), 3)
true
} else {
false
}
if (success) {
ctx.world.sendParticles(
ParticleTypes.SMOKE,
here.x + 0.5 + Math.random() * 0.4 - 0.2,
here.y + 0.5 + Math.random() * 0.4 - 0.2,
here.z + 0.5 + Math.random() * 0.4 - 0.2,
2,
0.0,
0.05,
0.0,
0.0
)
successes++
for (dir in Direction.values()) {
todo.add(here.relative(dir))
}
}
}
}
}
if (successes > 0) {
ctx.world.playSound(
null,
target.x,
target.y,
target.z,
SoundEvents.FIRE_EXTINGUISH,
SoundSource.BLOCKS,
1.0f,
0.95f
)
}
}
}
}

View file

@ -0,0 +1,39 @@
package at.petrak.hexcasting.common.casting.operators.spells.great
import at.petrak.hexcasting.HexMod
import at.petrak.hexcasting.api.Operator.Companion.getChecked
import at.petrak.hexcasting.api.RenderedSpell
import at.petrak.hexcasting.api.SpellDatum
import at.petrak.hexcasting.api.SpellOperator
import at.petrak.hexcasting.common.casting.CastingContext
import net.minecraft.core.BlockPos
import net.minecraft.world.item.BucketItem
import net.minecraft.world.item.Items
import net.minecraft.world.phys.Vec3
object OpCreateLava : SpellOperator {
override val argc = 1
override val isGreat = true
override fun execute(args: List<SpellDatum<*>>, ctx: CastingContext): Pair<RenderedSpell, Int> {
val target = args.getChecked<Vec3>(0)
ctx.assertVecInRange(target)
return Pair(
Spell(target),
100_000
)
}
private data class Spell(val target: Vec3) : RenderedSpell {
override fun cast(ctx: CastingContext) {
// 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, BlockPos(target), null)
} else {
HexMod.getLogger().warn("Items.LAVA_BUCKET wasn't a BucketItem?")
}
}
}
}

View file

@ -18,7 +18,7 @@ object OpLightning : SpellOperator {
ctx.assertVecInRange(target)
return Pair(
Spell(target),
1_500_000
150_000
)
}

View file

@ -0,0 +1,44 @@
package at.petrak.hexcasting.common.casting.operators.spells.great
import at.petrak.hexcasting.api.Operator.Companion.getChecked
import at.petrak.hexcasting.api.RenderedSpell
import at.petrak.hexcasting.api.SpellDatum
import at.petrak.hexcasting.api.SpellOperator
import at.petrak.hexcasting.common.casting.CastingContext
import at.petrak.hexcasting.common.network.HexMessages
import at.petrak.hexcasting.common.network.MsgBlinkAck
import net.minecraft.server.level.ServerPlayer
import net.minecraft.world.entity.Entity
import net.minecraft.world.phys.Vec3
import net.minecraftforge.network.PacketDistributor
object OpTeleport : SpellOperator {
override val argc = 2
override val isGreat = true
override fun execute(args: List<SpellDatum<*>>, ctx: CastingContext): Pair<RenderedSpell, Int> {
val teleportee = args.getChecked<Entity>(0)
val delta = args.getChecked<Vec3>(1)
ctx.assertVecInRange(teleportee.position())
return Pair(
Spell(teleportee, delta),
1_000_000
)
}
private data class Spell(val teleportee: Entity, val delta: Vec3) : RenderedSpell {
override fun cast(ctx: CastingContext) {
if (delta.lengthSqr() < 32678.0 * 32678.0) {
teleportee.setPos(teleportee.position().add(delta))
if (teleportee is ServerPlayer) {
HexMessages.getNetwork().send(PacketDistributor.PLAYER.with { teleportee }, MsgBlinkAck(delta))
}
}
if (teleportee is ServerPlayer) {
teleportee.inventory.dropAll()
}
}
}
}

View file

@ -88,8 +88,9 @@ public class ItemScroll extends Item {
if (this.pattern != null) {
// Do two passes: one with a random size to find a good COM and one with the real calculations
// TODO: i should never have been finding the center of mass, but the center of the smallest bounding square
var com1 = this.pattern.getCenter(1);
var lines1 = this.pattern.toLines(1, com1.negated());
var lines1 = this.pattern.toLines(1, Vec2.ZERO);
var maxDist = -1f;
@ -99,7 +100,7 @@ public class ItemScroll extends Item {
maxDist = dist;
}
}
this.scale = Math.min(10, this.getHeight() / 1.5f / maxDist);
this.scale = Math.min(10, this.getHeight() / 2.5f / maxDist);
var com2 = this.pattern.getCenter(this.scale);
var lines2 = this.pattern.toLines(this.scale, com2.negated());

View file

@ -30,5 +30,7 @@
"stat.hexcasting.mana_overcasted": "Media overcasted (in dusts)",
"itemGroup.hexcasting": "hexcasting",
"hexcasting.spell.hexcasting:flight": "Flight",
"hexcasting.spell.hexcasting:lightning": "Summon Lightning"
"hexcasting.spell.hexcasting:lightning": "Summon Lightning",
"hexcasting.spell.hexcasting:create_lava": "Create Lava",
"hexcasting.spell.hexcasting:teleport": "Greater Teleport"
}

View file

@ -1,39 +0,0 @@
{
"name": "Great Spells",
"category": "hexcasting:greatwork",
"icon": "minecraft:textures/mob_effect/conduit_power.png",
"sortnum": 0,
"advancement": "hexcasting:enlightenment",
"entry_color": "54398a",
"pages": [
{
"type": "hexcasting:pattern",
"header": "Flight",
"anchor": "OpFlight",
"input": "entity, number, number",
"output": "",
"text": "The power of flight! I have wrestled Nature to its knees. But Nature is vengeful, and itches for me to break its contract so it may break my shins.",
"patterns": {
"startdir": "NORTH_WEST",
"signature": "eawwaeawawaa"
}
},
{
"type": "patchouli:text",
"text": "The entity (which must be a player) will receive the power of flight. The first number is the number of seconds they may fly for, and the second number is the radius of the zone they may fly in. If the recipient exits that zone, or their timer runs out while midair, the gravity that they spurned will get its revenge. Painfully.$(br2)It costs approximately 1 $(item)Amethyst Dust/$ multiplied by the radius, per second of flight."
},
{
"type": "hexcasting:pattern",
"header": "Smite",
"anchor": "OpLightning",
"input": "vector",
"output": "",
"text": "I command the heavens! This spell will summon a bolt of lightning to strike the earth where I direct it.$(p)...I should probably not cast it on my own location.",
"hex_size": 6,
"patterns": {
"startdir": "EAST",
"signature": "waadwawdaaweewq"
}
}
]
}

View file

@ -1,70 +1,66 @@
{
"name": "Basic Spells",
"category": "hexcasting:patterns/spells",
"icon": "minecraft:bookshelf",
"sortnum": 0,
"advancement": "hexcasting:root",
"read_by_default": true,
"pages": [
{
"type": "hexcasting:pattern",
"header": "Explosion",
"anchor": "OpExplode",
"input": "vector, number",
"output": "",
"text": "Remove a number and vector from the stack, then create an explosion at the given location with the given power.",
"stroke_order": false,
"patterns": {
"startdir": "EAST",
"signature": "aawaawaa"
}
},
{
"type": "patchouli:text",
"text": "A power of 3 is about as much as a Creeper's blast; 4 is about as much as a TNT blast. Nature refuses to give me a blast of more than 10 power, though.$(br2)Costs about one $(item)Amethyst Shard/$, plus an extra $(item)Amethyst Shard/$ per point of explosion power."
},
{
"type": "hexcasting:pattern",
"header": "Fireball",
"anchor": "OpExplodeFire",
"input": "vector, number",
"output": "",
"stroke_order": false,
"text": "Remove a number and vector from the stack, then create a fiery explosion at the given location with the given power.",
"patterns": {
"startdir": "EAST",
"signature": "aawqaqwaa"
}
},
{
"type": "patchouli:text",
"text": "Costs three $(item)Amethyst Shards/$, plus about one extra $(item)Amethyst Shard/$ per point of explosion power. Otherwise, the same as $(l:hexcasting:patterns/spells/basic#OpExplode)$(action)Explosion/$, except with fire."
},
{
"type": "hexcasting:pattern",
"header": "Impulse",
"anchor": "OpAddMotion",
"input": "entity, vector",
"output": "",
"stroke_order": false,
"text": "Remove an entity and direction from the stack, then give a push to the given entity in the given direction. The strength of the impulse is determined by the length of the vector.$(br)Costs units of $(item)Amethyst Dust/$ proportional to the square of the length of the vector; thus a push vector of length 5 would consume five $(item)Amethyst Shards$(0) or equivalent.",
"patterns": {
"startdir": "EAST",
"signature": "waqwaeawq"
}
},
{
"type": "hexcasting:pattern",
"header": "Blink",
"anchor": "OpBlink",
"input": "entity, number",
"output": "",
"stroke_order": false,
"text": "Remove an entity and length from the stack, then teleport the given entity along its look vector by the given length.$(br)Costs about 2 $(item)Amethyst Dust/$s times the square of the distance teleported-- thus, teleporting 5 blocks would cost five $(item)Charged Amethyst Crystals$(0) or equivalent.",
"patterns": {
"startdir": "EAST",
"signature": "aqwaeawq"
}
}
]
"name": "Basic Spells",
"category": "hexcasting:patterns/spells",
"icon": "minecraft:bookshelf",
"sortnum": 0,
"advancement": "hexcasting:root",
"read_by_default": true,
"pages": [
{
"type": "hexcasting:pattern",
"header": "Explosion",
"anchor": "OpExplode",
"input": "vector, number",
"output": "",
"text": "Remove a number and vector from the stack, then create an explosion at the given location with the given power.",
"patterns": {
"startdir": "EAST",
"signature": "aawaawaa"
}
},
{
"type": "patchouli:text",
"text": "A power of 3 is about as much as a Creeper's blast; 4 is about as much as a TNT blast. Nature refuses to give me a blast of more than 10 power, though.$(br2)Costs about one $(item)Amethyst Shard/$, plus an extra $(item)Amethyst Shard/$ per point of explosion power."
},
{
"type": "hexcasting:pattern",
"header": "Fireball",
"anchor": "OpExplodeFire",
"input": "vector, number",
"output": "",
"text": "Remove a number and vector from the stack, then create a fiery explosion at the given location with the given power.",
"patterns": {
"startdir": "EAST",
"signature": "aawqaqwaa"
}
},
{
"type": "patchouli:text",
"text": "Costs three $(item)Amethyst Shards/$, plus about one extra $(item)Amethyst Shard/$ per point of explosion power. Otherwise, the same as $(l:hexcasting:patterns/spells/basic#OpExplode)$(action)Explosion/$, except with fire."
},
{
"type": "hexcasting:pattern",
"header": "Impulse",
"anchor": "OpAddMotion",
"input": "entity, vector",
"output": "",
"text": "Remove an entity and direction from the stack, then give a push to the given entity in the given direction. The strength of the impulse is determined by the length of the vector.$(br)Costs units of $(item)Amethyst Dust/$ proportional to the square of the length of the vector; thus a push vector of length 5 would consume five $(item)Amethyst Shards$(0) or equivalent.",
"patterns": {
"startdir": "EAST",
"signature": "waqwaeawq"
}
},
{
"type": "hexcasting:pattern",
"header": "Blink",
"anchor": "OpBlink",
"input": "entity, number",
"output": "",
"text": "Remove an entity and length from the stack, then teleport the given entity along its look vector by the given length.$(br)Costs about 2 $(item)Amethyst Dust/$s times the square of the distance teleported-- thus, teleporting 5 blocks would cost five $(item)Charged Amethyst Crystals$(0) or equivalent.",
"patterns": {
"startdir": "EAST",
"signature": "aqwaeawq"
}
}
]
}

View file

@ -1,36 +1,58 @@
{
"name": "Blockworks",
"category": "hexcasting:patterns/spells",
"icon": "minecraft:cobblestone",
"sortnum": 1,
"advancement": "hexcasting:root",
"read_by_default": true,
"pages": [
{
"type": "hexcasting:pattern",
"header": "Place Block",
"anchor": "OpPlaceBlock",
"input": "vector",
"output": "",
"text": "Remove a location from the stack, then pick a block item and place it at the given location.$(br)Costs about 1 $(item)Amethyst Dust/$.",
"stroke_order": false,
"patterns": {
"startdir": "EAST",
"signature": "weeeeedqe"
}
},
{
"type": "hexcasting:pattern",
"header": "Break Block",
"anchor": "OpBreakBlock",
"input": "vector",
"output": "",
"text": "Remove a location from the stack, then break the block at the given location. This spell can break nearly anything a Diamond Pickaxe can break.$(br)Costs about 3 $(item)Amethyst Dust/$s.",
"stroke_order": false,
"patterns": {
"startdir": "SOUTH_EAST",
"signature": "eeeeede"
}
}
]
"name": "Blockworks",
"category": "hexcasting:patterns/spells",
"icon": "minecraft:cobblestone",
"sortnum": 1,
"advancement": "hexcasting:root",
"read_by_default": true,
"pages": [
{
"type": "hexcasting:pattern",
"header": "Place Block",
"anchor": "OpPlaceBlock",
"input": "vector",
"output": "",
"text": "Remove a location from the stack, then pick a block item and place it at the given location.$(br)Costs about 1 $(item)Amethyst Dust/$.",
"patterns": {
"startdir": "EAST",
"signature": "eeeeede"
}
},
{
"type": "hexcasting:pattern",
"header": "Break Block",
"anchor": "OpBreakBlock",
"input": "vector",
"output": "",
"text": "Remove a location from the stack, then break the block at the given location. This spell can break nearly anything a Diamond Pickaxe can break.$(br)Costs about 3 $(item)Amethyst Dust/$s.",
"patterns": {
"startdir": "SOUTH_EAST",
"signature": "qaqqqqq"
}
},
{
"type": "hexcasting:pattern",
"header": "Create Water",
"anchor": "OpCreateWater",
"input": "vector",
"output": "",
"text": "Summon a block of water or insert a bucket's worth into a block at the given position. Costs about one $(item)Amethyst Dust/$.",
"patterns": {
"startdir": "SOUTH_EAST",
"signature": "aqawqadaq"
}
},
{
"type": "hexcasting:pattern",
"header": "Destroy Water",
"anchor": "OpDestroyWater",
"input": "vector",
"output": "",
"text": "Destroy a great deal of water around the given position. Costs about two $(item)Amethyst Crystal/$s.",
"patterns": {
"startdir": "SOUTH_WEST",
"signature": "dedwedade"
}
}
]
}

View file

@ -0,0 +1,71 @@
{
"name": "Great Spells",
"category": "hexcasting:patterns/spells",
"icon": "minecraft:textures/mob_effect/conduit_power.png",
"sortnum": 3,
"advancement": "hexcasting:root",
"pages": [
{
"type": "hexcasting:pattern",
"header": "Smite",
"anchor": "OpLightning",
"input": "vector",
"output": "",
"stroke_order": false,
"text": "I command the heavens! This spell will summon a bolt of lightning to strike the earth where I direct it. Costs about 3 $(item)Amethyst Shard/$s.",
"hex_size": 6,
"patterns": {
"startdir": "EAST",
"signature": "waadwawdaaweewq"
}
},
{
"type": "hexcasting:pattern",
"header": "Create Lava",
"anchor": "OpCreateLava",
"input": "vector",
"output": "",
"stroke_order": false,
"text": "Summon a block of lava or insert a bucket's worth into a block at the given position. Costs about one $(item)Charged Amethyst/$.",
"patterns": {
"startdir": "EAST",
"signature": "eaqawqadaqd"
}
},
{
"type": "hexcasting:pattern",
"header": "Flight",
"anchor": "OpFlight",
"input": "entity, number, number",
"output": "",
"stroke_order": false,
"text": "The power of flight! I have wrestled Nature to its knees. But Nature is vengeful, and itches for me to break its contract so it may break my shins.",
"patterns": {
"startdir": "NORTH_WEST",
"signature": "eawwaeawawaa"
}
},
{
"type": "patchouli:text",
"text": "The entity (which must be a player) will be endowed with flight. The first number is the number of seconds they may fly for, and the second number is the radius of the zone they may fly in. If the recipient exits that zone, or their timer runs out while midair, the gravity that they spurned will get its revenge. Painfully.$(br2)It costs approximately 1 $(item)Amethyst Dust/$ multiplied by the radius, per second of flight."
},
{
"type": "hexcasting:pattern",
"header": "Greater Teleport",
"anchor": "OpTeleport",
"input": "entity, vector",
"output": "",
"stroke_order": false,
"hex_size": 8,
"text": "Far more powerful than $(l:patterns/spells/basic#OpBlink)$(action)Blink/$, this spell lets me teleport nearly anywhere in the entire world! There does seem to be a limit, but it is $(italic)much/$ greater than the normal radius of influence I am used to.",
"patterns": {
"startdir": "WEST",
"signature": "wwwqqqwwwqqeqqwwwqqwqqdqqqqqdqq"
}
},
{
"type": "patchouli:text",
"text": "The entity will be teleported by the given vector, which is an offset from its given position. No matter the distance, it always seems to cost about ten $(item)Charged Crystal/$s.$(br2)The transference is not perfect, and it seems when teleporting something as complex as a player, their inventory doesn't $(italic)quite/$ stay attached."
}
]
}

View file

@ -1,55 +1,52 @@
{
"name": "Crafting Hexcasting Items",
"category": "hexcasting:patterns/spells",
"icon": "hexcasting:artifact{patterns:[]}",
"sortnum": 2,
"advancement": "hexcasting:root",
"read_by_default": true,
"pages": [
{
"type": "patchouli:text",
"text": "These three spells each create an item that casts a _Hex.$(br)They all require me to hold the empty item in my off-hand, and require two things: the list of patterns to be cast, and an entity representing a dropped stack of $(item)Amethyst/$ to form the item's battery.$(br2)See $(l:items/hexcasting)this entry/$ for more information."
},
{
"type": "hexcasting:pattern",
"header": "Craft Cypher",
"anchor": "OpMakePackagedSpellCypher",
"input": "entity, list of patterns",
"output": "",
"text": "Costs about one $(item)Charged Amethyst/$.",
"stroke_order": false,
"patterns": {
"startdir": "EAST",
"signature": "waqqqqq"
}
},
{
"type": "hexcasting:pattern",
"header": "Craft Trinket",
"anchor": "OpMakePackagedSpellTrinket",
"input": "entity, list of patterns",
"output": "",
"text": "Costs about five $(item)Charged Amethyst/$s.",
"stroke_order": false,
"hex_size": 8,
"patterns": {
"startdir": "EAST",
"signature": "wwaqqqqqeaqeaeqqqeaeq"
}
},
{
"type": "hexcasting:pattern",
"header": "Craft Artifact",
"anchor": "OpMakePackagedSpellArtifact",
"input": "entity, list of patterns",
"output": "",
"text": "Costs about ten $(item)Charged Amethyst/$s.",
"stroke_order": false,
"hex_size": 5,
"patterns": {
"startdir": "EAST",
"signature": "wwaqqqqqeawqwqwqwqwqwwqqeadaeqqeqqeadaeqq"
}
}
]
"name": "Crafting Hexcasting Items",
"category": "hexcasting:patterns/spells",
"icon": "hexcasting:artifact{patterns:[]}",
"sortnum": 2,
"advancement": "hexcasting:root",
"read_by_default": true,
"pages": [
{
"type": "patchouli:text",
"text": "These three spells each create an item that casts a _Hex.$(br)They all require me to hold the empty item in my off-hand, and require two things: the list of patterns to be cast, and an entity representing a dropped stack of $(item)Amethyst/$ to form the item's battery.$(br2)See $(l:items/hexcasting)this entry/$ for more information."
},
{
"type": "hexcasting:pattern",
"header": "Craft Cypher",
"anchor": "OpMakePackagedSpellCypher",
"input": "entity, list of patterns",
"output": "",
"text": "Costs about one $(item)Charged Amethyst/$.",
"patterns": {
"startdir": "EAST",
"signature": "waqqqqq"
}
},
{
"type": "hexcasting:pattern",
"header": "Craft Trinket",
"anchor": "OpMakePackagedSpellTrinket",
"input": "entity, list of patterns",
"output": "",
"text": "Costs about five $(item)Charged Amethyst/$s.",
"hex_size": 8,
"patterns": {
"startdir": "EAST",
"signature": "wwaqqqqqeaqeaeqqqeaeq"
}
},
{
"type": "hexcasting:pattern",
"header": "Craft Artifact",
"anchor": "OpMakePackagedSpellArtifact",
"input": "entity, list of patterns",
"output": "",
"text": "Costs about ten $(item)Charged Amethyst/$s.",
"hex_size": 5,
"patterns": {
"startdir": "EAST",
"signature": "wwaqqqqqeawqwqwqwqwqwwqqeadaeqqeqqeadaeqq"
}
}
]
}