final bugfixes before release

This commit is contained in:
gamma-delta 2022-01-19 15:48:11 -06:00
parent f33d6d07d3
commit 10ef7b7e8a
19 changed files with 165 additions and 70 deletions

View file

@ -1,16 +1,7 @@
# Hex
A minecraft mod about casting spells, inspired by PSI.
A minecraft mod about casting Hexes, powerful and programmable magical effects, inspired by PSI.
## To Cast A Spell
[Curseforge Link](https://www.curseforge.com/minecraft/mc-mods/hex-mod)
- Get a Wand, which stores mana.
- Right-click with it to start casting a *Hex*.
- Look around to draw a *pattern*, a sequence of lines on the invisible hexagonal grid that permeates reality.
- When you are done with your pattern, release right-click. Hopefully, that pattern corresponds to an *action*. (If not,
you blow up.)
- Actions manipulate a stack of data. The stack starts empty when you first start casting a hex. Some actions just push
data, while some pop off some arguments and push some arguments in return.
- Certain actions, will push a *spell* to the stack. When the stack is empty except for one spell, the hex is
successfully cast with the given effect.
This mod requires Patchouli!

View file

@ -9,6 +9,7 @@ import at.petrak.hex.common.lib.HexSounds
import at.petrak.hex.common.network.HexMessages
import at.petrak.hex.common.network.MsgQuitSpellcasting
import at.petrak.hex.common.network.MsgShiftScrollSyn
import at.petrak.hex.hexmath.HexAngle
import at.petrak.hex.hexmath.HexCoord
import at.petrak.hex.hexmath.HexDir
import at.petrak.hex.hexmath.HexPattern
@ -99,28 +100,37 @@ class GuiSpellcasting(private val handOpenedWith: InteractionHand) : Screen(Text
// The player might have a lousy aim, so set the new anchor point to the "ideal"
// location as if they had hit it exactly on the nose.
val idealNextLoc = anchorCoord + newdir
val success = if (!this.usedSpots.contains(idealNextLoc)) {
var playSound = false
if (!this.usedSpots.contains(idealNextLoc)) {
if (this.drawState is PatternDrawState.JustStarted) {
val pat = HexPattern(newdir)
this.drawState = PatternDrawState.Drawing(anchorCoord, idealNextLoc, pat)
true
playSound = true
} else if (this.drawState is PatternDrawState.Drawing) {
// how anyone gets around without a borrowck is beyond me
val ds = (this.drawState as PatternDrawState.Drawing)
val success = ds.wipPattern.tryAppendDir(newdir)
if (success) {
ds.current = idealNextLoc
val lastDir = ds.wipPattern.finalDir()
if (newdir == lastDir.rotatedBy(HexAngle.BACK)) {
// We're diametrically opposite! Do a backtrack
if (ds.wipPattern.angles.isEmpty()) {
this.drawState = PatternDrawState.JustStarted(ds.current + newdir)
} else {
ds.current += newdir
ds.wipPattern.angles.removeLast()
}
playSound = true
} else {
val success = ds.wipPattern.tryAppendDir(newdir)
if (success) {
ds.current = idealNextLoc
}
playSound = success
}
success
} else {
false
}
} else {
false
}
if (success) {
if (playSound) {
Minecraft.getInstance().soundManager.play(
SimpleSoundInstance.forUI(
HexSounds.ADD_LINE.get(),
@ -278,7 +288,10 @@ class GuiSpellcasting(private val handOpenedWith: InteractionHand) : Screen(Text
/** Distance between adjacent hex centers */
fun hexSize(): Float = this.width.toFloat() / 32.0f
fun coordsOffset(): Vec2 = Vec2(0f, this.hexSize())
fun coordsOffset(): Vec2 = Vec2(
(this.width / 2) % hexSize(),
(this.height / 2) % hexSize(),
).negated()
fun coordToPx(coord: HexCoord) = RenderLib.coordToPx(coord, this.hexSize(), this.coordsOffset())
fun pxToCoord(px: Vec2) = RenderLib.pxToCoord(px, this.hexSize(), this.coordsOffset())

View file

@ -6,8 +6,8 @@ import at.petrak.hex.api.RenderedSpell
import at.petrak.hex.api.SpellDatum
import at.petrak.hex.common.items.ItemWand
import at.petrak.hex.common.items.magic.ItemPackagedSpell
import at.petrak.hex.common.lib.LibDamageSources
import at.petrak.hex.common.lib.HexStatistics
import at.petrak.hex.common.lib.LibDamageSources
import at.petrak.hex.datagen.Advancements
import at.petrak.hex.hexmath.HexPattern
import net.minecraft.nbt.CompoundTag
@ -50,7 +50,6 @@ class CastingHarness private constructor(
if (this.escapeNext) {
this.escapeNext = false
this.parenthesized.add(newPat)
HexMod.LOGGER.info("Escaping onto parenthesized")
} else if (operator == Widget.ESCAPE) {
this.escapeNext = true
} else if (operator == Widget.OPEN_PAREN) {
@ -60,8 +59,8 @@ class CastingHarness private constructor(
} else if (operator == Widget.CLOSE_PAREN) {
this.parenCount--
if (this.parenCount == 0) {
HexMod.LOGGER.info("Finished parenthesizing things")
this.stack.add(SpellDatum.make(this.parenthesized.map { SpellDatum.make(it) }))
this.parenthesized.clear()
} else if (this.parenCount < 0) {
throw CastException(CastException.Reason.TOO_MANY_CLOSE_PARENS)
} else {
@ -74,7 +73,6 @@ class CastingHarness private constructor(
}
} else if (this.escapeNext) {
this.escapeNext = false
HexMod.LOGGER.info("Escaping onto stack")
this.stack.add(SpellDatum.make(newPat))
} else if (operator == Widget.ESCAPE) {
this.escapeNext = true
@ -96,11 +94,6 @@ class CastingHarness private constructor(
return CastResult.Died
}
if (this.parenCount > 0) {
HexMod.LOGGER.info("Paren level ${this.parenCount}; ${this.parenthesized}")
}
HexMod.LOGGER.info("New stack: ${this.stack.map { it.display() }}")
if (spellsToCast.isNotEmpty()) {
CastResult.Cast(spellsToCast, this.stack.isEmpty())
} else if (this.stack.isEmpty()) {

View file

@ -91,6 +91,7 @@ public class Advancements extends AdvancementProvider {
public static void registerTriggers() {
CriteriaTriggers.register(OVERCAST_TRIGGER);
CriteriaTriggers.register(SPEND_MANA_TRIGGER);
}
protected static DisplayInfo simple(ItemLike icon, String name, FrameType frameType) {

View file

@ -10,7 +10,7 @@
"functions": [
{
"function": "set_nbt",
"tag": "{\"patchouli:book\": \"hex\"}"
"tag": "{\"patchouli:book\": \"hex:thehexbook\"}"
}
]
}

View file

@ -11,11 +11,7 @@
},
{
"type": "patchouli:text",
"text": "Anyways, what follows is a list of all the influences I've discovered:$(li)Null, representing nothingness.$(li)Introspection and Retrospection. Drawing Introspection, a bunch of patterns, and then Retrospection will push those middle patterns onto the stack as a list of patterns. The pair is used for the crafting of magical items."
},
{
"type": "patchouli:text",
"text": "$(li)Consideration. Drawing this and then another pattern will \"escape\" the second pattern and push it onto the stack, instead of trying to match it to an action."
"text": "In addition, I've discovered a curious triplet of influences I've named Consideration, Introspection, and Retrospection. I can use these to add patterns to my stack as iotas, instead of matching them to actions. $(l:casting/patterns_as_iotas)My notes on the subject are here/$."
}
]
]
}

View file

@ -0,0 +1,18 @@
{
"name": "Mishaps",
"category": "hex:casting",
"icon": "minecraft:textures/mob_effect/poison.png",
"priority": "true",
"sortnum": 2,
"advancement": "hex:root",
"pages": [
{
"type": "patchouli:text",
"text": "Unfortunately, I am not a perfect being. I make mistakes from time to time, including when casting _Hexes. For example, I might misdraw a pattern, or try an invoke an action with the wrong types of iotas. And usually, Nature does not look kindly on my mistakes, causing what is called a $(italic)mishap/$."
},
{
"type": "patchouli:text",
"text": "I expected some kind of horrible recompense for mishaps, some kind of alchemical disaster or virile illness as I have heard troubled the Thaumaturges of the past... but instead, I just get a cryptic (if helpful) message in my chat. How convenient!$(br2)However, a nagging feeling at the back of my mind tells me I should expect worse consequences once the \"mod updates,\" whatever that means. Perhaps I am not as immune to the madness that seems to afflict practitioners of this art as I thought."
}
]
}

View file

@ -0,0 +1,55 @@
{
"name": "Patterns as Iotas",
"category": "hex:casting",
"icon": "minecraft:emerald",
"sortnum": 3,
"advancement": "hex:root",
"pages": [
{
"type": "patchouli:text",
"text": "One of the many peculiarities of the art is that $(italic)patterns themselves/$ can act as iotas, and be put onto my stack when casting.$(br2)This raises a fairly obvious question: how do I express them? If I were to simply draw a pattern, how would I inform Nature of my intent to have it matched to an action instead of adding it to my stack as an iota?"
},
{
"type": "patchouli:text",
"text": "Fortunately, Nature has provided me with a set of $(l:casting/influences)influences/$ that I can use to work with patterns directly.$(br2)In short, $(action)Consideration/$ lets me add one pattern to the stack, and $(action)Introspection/$ and $(action)Retrospection/$ let me add a whole list."
},
{
"type": "hex:pattern_nosig",
"header": "Consideration",
"anchor": "OpEscape",
"text": "To use $(action)Consideration/$, I draw it, then another arbitrary pattern. That second pattern is added to the stack.",
"patterns": {
"startdir": "WEST",
"signature": "qqqaw"
}
},
{
"type": "patchouli:text",
"text": "I may find it helpful to think of this as \"escaping\" the pattern onto the stack, if I happen to be familiar with the term from computer science.$(br2)To be honest, I haven't found too much use for this pattern yet, but I imagine it could be helpful with advanced _Hexes casting other _Hexes."
},
{
"type": "hex:pattern_nosig",
"header": "Introspection",
"anchor": "OpOpenParen",
"text": "Drawing $(action)Introspection/$ makes my drawing of patterns act differently, for a time. Until I draw $(action)Retrospection/$, any pattern I draw is saved to a list. Then, when I draw $(action)Retrospection/$, the list is pushed to the stack as a list iota.",
"patterns": {
"startdir": "WEST",
"signature": "qqq"
}
},
{
"type": "hex:pattern_nosig",
"header": "Retrospection",
"anchor": "OpCloseParen",
"text": "If I draw another $(action)Introspection/$, it will still be saved, but I'll then have to draw $(italic)two/$ $(action)Retrospection/$s to get back to normal casting.",
"patterns": {
"startdir": "EAST",
"signature": "eee"
}
},
{
"type": "patchouli:text",
"text": "Also, I can escape the special behavior of $(action)Intro/Retrospection/$ by drawing a $(action)Consideration/$ before them, which will simply add them to the list without affecting the number of each I need to draw to return to normal.$(br2)If I draw two $(action)Consideration/$s in a row while introspecting, it will add the second pattern to the list."
}
]
}

View file

@ -14,7 +14,7 @@
},
{
"type": "patchouli:text",
"text": "Additionally, it seems that the mages who manipulated $(thing)Psi energy/$ (the so-called \"spellslingers\"), despite their poor naming sense, had some quite-effective lessons on vectors to teach their acolytes. I've taken the liberty of linking to one of their texts on the next page.$(br2)They seem to have used different language for their spellcasting:$(li)A \"Spell Piece\" was their name for an action;$(li)a \"Trick\" was their name for a spell; and$(li)an \"Operator\" was their name for a non-spell Action."
"text": "Additionally, it seems that the mages who manipulated $(thing)Psi energy/$ (the so-called \"spellslingers\"), despite their poor naming sense, had some quite-effective lessons on vectors to teach their acolytes. I've taken the liberty of linking to one of their texts on the next page.$(br2)They seem to have used different language for their spellcasting:$(li)A \"Spell Piece\" was their name for an action;$(li)a \"Trick\" was their name for a spell; and$(li)an \"Operator\" was their name for a non-spell action."
},
{
"type": "patchouli:link",

View file

@ -26,6 +26,10 @@
"anchor": "charged",
"link_recipe": true,
"text": "Finally, I'll rarely find a large crystal crackling with energy. This has about as much _media inside as ten units of $(item)Amethyst Dust/$s (or two $(item)Amethyst Shard/$s)."
},
{
"type": "patchouli:text",
"text": "$(italic)The old man sighed and raised a hand toward the fire. He unlocked a part of his brain that held the memories of the mountains around them. He pulled the energies from those lands, as he learned to do in Terisia City with Drafna, Hurkyl, the archimandrite, and the other mages of the Ivory Towers. He concentrated, and the flames writhed as they rose from the logs, twisting upon themselves until they finally formed a soft smile./$"
}
]
}

View file

@ -7,7 +7,7 @@
"pages": [
{
"type": "patchouli:text",
"text": "A $(item)Focus/$ can store a single iota.$(br2)When I craft it, it holds the Null influence by default. Using the $(action)Scribe's Gambit/$ while holding a $(item)Focus/$ in my other hand will remove the top of the stack and save it into the $(item)Focus/$. Using the $(action)Scribe's Reflection/$ will copy whatever iota's in the $(item)Focus/$ and add it to the stack."
"text": "A $(item)Focus/$ can store a single iota.$(br2)When I craft it, it holds the Null influence by default. Using the $(action)$(l:patterns/meta#OpWrite)Scribe's Gambit/$ while holding a $(item)Focus/$ in my other hand will remove the top of the stack and save it into the $(item)Focus/$. Using the $(action)$(l:patterns/meta#OpRead)Scribe's Reflection/$ will copy whatever iota's in the $(item)Focus/$ and add it to the stack."
},
{
"type": "patchouli:crafting",

View file

@ -89,7 +89,7 @@
"anchor": "OpBlockRaycast",
"input": "vector, vector",
"output": "vector",
"text": "Combines two vectors (a position and a direction) into the answer to the question: If I stood at the position and looked in the direction, what block would I be looking at? If it doesn't hit anything, the vectors will combine into Null.",
"text": "Combines two vectors (a position and a direction) into the answer to the question: If I stood at the position and looked in the direction, what block would I be looking at?",
"patterns": {
"startdir": "EAST",
"signature": "wqaawdd"
@ -97,7 +97,7 @@
},
{
"type": "patchouli:text",
"text": "For example, casting this with my own position and look vectors will return the coordinates of the block I am looking at."
"text": "If it doesn't hit anything, the vectors will combine into Null.$(br2)For example, casting this with my own position and look vectors will return the coordinates of the block I am looking at."
},
{
"type": "hex:pattern",

View file

@ -2,7 +2,7 @@
"name": "Constants",
"category": "hex:patterns",
"icon": "minecraft:bedrock",
"sortnum": 2,
"sortnum": 3,
"advancement": "hex:root",
"read_by_default": true,
"pages": [

View file

@ -20,7 +20,7 @@
},
{
"type": "hex:pattern",
"header": "Entity Rfxn.: Animal",
"header": "Entity Dstln.: Animal",
"anchor": "OpGetEntityAtAnimal",
"input": "vector",
"output": "entity or null",
@ -32,7 +32,7 @@
},
{
"type": "hex:pattern",
"header": "Entity Rfxn.: Monster",
"header": "Entity Dstln.: Monster",
"anchor": "OpGetEntityAtMonster",
"input": "vector",
"output": "entity or null",
@ -44,7 +44,7 @@
},
{
"type": "hex:pattern",
"header": "Entity Rfxn.: Item",
"header": "Entity Dstln.: Item",
"anchor": "OpGetEntityAtItem",
"input": "vector",
"output": "entity or null",
@ -56,7 +56,7 @@
},
{
"type": "hex:pattern",
"header": "Entity Rfxn.: Player",
"header": "Entity Dstln.: Player",
"anchor": "OpGetEntityAtPlayer",
"input": "vector",
"output": "entity or null",
@ -68,7 +68,7 @@
},
{
"type": "hex:pattern",
"header": "Entity Rfxn.: Living",
"header": "Entity Dstln.: Living",
"anchor": "OpGetEntityAtLiving",
"input": "vector",
"output": "entity or null",
@ -80,7 +80,7 @@
},
{
"type": "hex:pattern",
"header": "Zone Rfxn.: Any",
"header": "Zone Dstln.: Any",
"anchor": "OpGetEntitiesBy",
"input": "vector, number",
"output": "list of entities",
@ -92,7 +92,7 @@
},
{
"type": "hex:pattern",
"header": "Zone Rfxn.: Animal",
"header": "Zone Dstln.: Animal",
"anchor": "OpGetEntitiesByAnimal",
"input": "vector, number",
"output": "list of entities",
@ -104,7 +104,7 @@
},
{
"type": "hex:pattern",
"header": "Zone Rfxn.: Non-Animal",
"header": "Zone Dstln.: Non-Animal",
"anchor": "OpGetEntitiesByAnimalNot",
"input": "vector, number",
"output": "list of entities",
@ -116,7 +116,7 @@
},
{
"type": "hex:pattern",
"header": "Zone Rfxn.: Monster",
"header": "Zone Dstln.: Monster",
"anchor": "OpGetEntitiesByMonster",
"input": "vector, number",
"output": "list of entities",
@ -128,7 +128,7 @@
},
{
"type": "hex:pattern",
"header": "Zone Rfxn.: Non-Monster",
"header": "Zone Dstln.: Non-Monster",
"anchor": "OpGetEntitiesByMonsterNot",
"input": "vector, number",
"output": "list of entities",
@ -140,7 +140,7 @@
},
{
"type": "hex:pattern",
"header": "Zone Rfxn.: Item",
"header": "Zone Dstln.: Item",
"anchor": "OpGetEntitiesByItem",
"input": "vector, number",
"output": "list of entities",
@ -152,7 +152,7 @@
},
{
"type": "hex:pattern",
"header": "Zone Rfxn.: Non-Item",
"header": "Zone Dstln.: Non-Item",
"anchor": "OpGetEntitiesByItemNot",
"input": "vector, number",
"output": "list of entities",
@ -164,7 +164,7 @@
},
{
"type": "hex:pattern",
"header": "Zone Rfxn.: Player",
"header": "Zone Dstln.: Player",
"anchor": "OpGetEntitiesByPlayer",
"input": "vector, number",
"output": "list of entities",
@ -176,7 +176,7 @@
},
{
"type": "hex:pattern",
"header": "Zone Rfxn.: Non-Player",
"header": "Zone Dstln.: Non-Player",
"anchor": "OpGetEntitiesByPkaterNot",
"input": "vector, number",
"output": "list of entities",
@ -188,7 +188,7 @@
},
{
"type": "hex:pattern",
"header": "Zone Rfxn.: Living",
"header": "Zone Dstln.: Living",
"anchor": "OpGetEntitiesByLiving",
"input": "vector, number",
"output": "list of entities",
@ -200,7 +200,7 @@
},
{
"type": "hex:pattern",
"header": "Zone Rfxn.: Non-Living",
"header": "Zone Dstln.: Non-Living",
"anchor": "OpGetEntitiesByLivingNot",
"input": "vector, number",
"output": "list of entities",

View file

@ -2,7 +2,7 @@
"name": "Math",
"category": "hex:patterns",
"icon": "minecraft:stick",
"sortnum": 3,
"sortnum": 2,
"advancement": "hex:root",
"read_by_default": true,
"pages": [
@ -79,7 +79,7 @@
},
{
"type": "hex:pattern",
"header": "Length Distill",
"header": "Length Dstln.",
"anchor": "OpAbsLen",
"input": "num/vec",
"output": "number",
@ -107,7 +107,7 @@
},
{
"type": "patchouli:text",
"text": "As such:$(li)With two numbers, combines them by raising the first to the power of the second.$(li)With a number and a vector, removes the number and raises each component of the vector to the number's power.$(li)With two vectors, combines them into the $(l:https://en.wikipedia.org/wiki/Vector_projection)vector projection of the top of the stack onto the second-from-the-top./$$(br2)In the first and second cases, the first argument or its components are the base, and the second argument or its components are the exponent."
"text": "$(li)With two numbers, combines them by raising the first to the power of the second.$(li)With a number and a vector, removes the number and raises each component of the vector to the number's power.$(li)With two vectors, combines them into the $(l:https://en.wikipedia.org/wiki/Vector_projection)vector projection/$ of the top of the stack onto the second-from-the-top.$(br2)In the first and second cases, the first argument or its components are the base, and the second argument or its components are the exponent."
},
{
"type": "hex:pattern",

View file

@ -6,15 +6,39 @@
"advancement": "hex:root",
"read_by_default": true,
"pages": [
{
"type": "hex:pattern",
"header": "Scribe's Gambit",
"anchor": "OpWrite",
"input": "any",
"output": "",
"text": "Remove the top iota from the stack and save it into the $(item)Focus/$ or $(item)Spellbook/$ in my other hand.",
"patterns": {
"startdir": "EAST",
"signature": "deeeee"
}
},
{
"type": "hex:pattern",
"header": "Scribe's Reflection",
"anchor": "OpRead",
"input": "",
"output": "any",
"text": "Copy the iota stored in the $(item)Focus/$ or $(item)Spellbook/$ in my other hand, and add it to the stack.",
"patterns": {
"startdir": "EAST",
"signature": "aqqqqq"
}
},
{
"type": "hex:pattern",
"header": "Hermes' Gambit",
"anchor": "OpEval",
"input": "list of patterns",
"output": "many",
"text": "Remove a list of patterns from the stack, then cast them sequentially, as if I had drawn them myself with my $(item)Staff/$.$(br)Costs about five $(item)Charged Crystal/$s.",
"text": "Remove a list of patterns from the stack, then cast them sequentially, as if I had drawn them myself with my $(item)Staff/$.$(br)Costs about five $(item)Charged Amethyst/$s.",
"patterns": {
"startdir": "SOUTH_WEST",
"startdir": "SOUTH_EAST",
"signature": "deaqq"
}
},
@ -30,7 +54,7 @@
"output": "many",
"text": "Remove a list of patterns and a list from the stack, then cast the given pattern over each element of the second list.$(br)Costs about 10 $(item)Charged Amethyst/$s.",
"patterns": {
"startdir": "SOUTH_WEST",
"startdir": "NORTH_EAST",
"signature": "dadad"
}
},

View file

@ -15,7 +15,7 @@
},
{
"type": "patchouli:text",
"text": "I also write the types of iota that the action will consume or modify, a \"\u2192\", and the types of iota the action will create.$(p)For example, \"$(n)vector, number/$ \u2192 $(n)vector/$\" means the action will remove a vector and a number from the top of the stack, and then add a vector; or, put another way, will remove a number from the stack, and then modify the vector at the top of the stack. (The number needs to be on the top of the stack, with the vector write below it.)"
"text": "I also write the types of iota that the action will consume or modify, a \"\u2192\", and the types of iota the action will create.$(p)For example, \"$(n)vector, number/$ \u2192 $(n)vector/$\" means the action will remove a vector and a number from the top of the stack, and then add a vector; or, put another way, will remove a number from the stack, and then modify the vector at the top of the stack. (The number needs to be on the top of the stack, with the vector right below it.)"
},
{
"type": "patchouli:text",

View file

@ -1,5 +1,5 @@
{
"name": "Hexcasting Items",
"name": "Crafting Hexcasting Items",
"category": "hex:patterns/spells",
"icon": "hex:artifact{patterns:[]}",
"sortnum": 2,
@ -8,7 +8,7 @@
"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 represetning a dropped stack of $(item)Amethyst/$ to form the item's battery.$(br2)See $(l:items/hexcasting)this entry/$ for more information."
"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": "hex:pattern",