mana is now taken directly from amethyst crystals

This commit is contained in:
gamma-delta 2022-01-01 18:33:05 -06:00
parent 88a415c88c
commit 9d3a8422d5
41 changed files with 559 additions and 271 deletions

83
slushfile.txt Normal file
View file

@ -0,0 +1,83 @@
Mana
- Mana is mental energy/thought
- Mana can be generated by:
= natural growth of amethyst crystals
= lightning?
= XP?
= organics -> corpses, shrubs? (anything edible + anything compostable?)
- Mana generation turns amethyst into perfect crystals, recharges batteries
Amethyst
- Amethyst stores mana
- Amethyst comes in dust, crystals, perfect crystals
- Each type is worth X mana; must consume an entire item; use up mana producing items least->greatest
- Later-game create rechargeable mana batteries?
Wandless Spellcasting
- do away with trinkets
- cyphers start with X mana crystals inside and cannot replace them
- artifacts can replace the mana crystals inside as they break
- these are special in that they can partially consume crystals
Per-world patterns
- all spells are like this?
- only great spells are like this?
- find the patterns on scrolls in chests
CastException
- Tiers of spell mishaps:
= puff of smoke, drop your wand
= consume a lot of mana, drop all your items
= just kill you, something apt for a great spell
- Mishap based on the type of exception
- item that gives you more detail on the precise manner of the exception
Great Spells
- require some kind of answer to a question to cast successfully
(see young wizards calculating oxygen amounts to teleport to the moon)
- https://www.dandwiki.com/wiki/SRD:Epic_Spells_and_Powers
- greater teleport:
= total items in inventory? (see chronometer fablehaven not taking your clothes with you)
= some irritating math question about the specific vector involved?
= review eragon's teleportation spell, D&D teleport, oathgates
- flight
= you must be on the ground within X seconds or you take infinite fall damage
- quarry region
- power word kill
Great Work
- Way to generate infinite mana
= recharge wands/batteries upon touching them?
- borrow from FMA and require the death of 100+ entities at once? (And if not, it gets the caster?)
- nigredo albedo citrinas rubedo?
> Nigredo is the dissolution of the false self; the burning of the old to ashes
> Albedo is the "raising" of the remains to the heavenly sphere in gaseous form; take the ashes and make them reborn
> Citrinas is an optional step but it boils down to some form of "aging" or "wisdom" (which comes with age);
take the newborn (true) self and temper it with time and/or knowledge
> Rubedo is the final distillation; you take the results from albedo/citrinas and you compress them down to a usable
form, the stone
-- alwinfy
- instructions in the form of an emerald-tablet-esque riddle
= include one line on each spell scroll
= assemble the poem, crack the riddle, &c
- possible workflow:
= Cast the first half of the spell, requiring an enumeration of your sins:
+ numbers of villagers killed?
+ number of passives killed?
+ total damage dealt?
You must have no items and no XP (caveat for keepInventory players: under 1 level of XP)
= Timer starts (Witness-esque music track?)
= Nigredo: endure 10 seconds of Wither and 10 seconds of constant fire without picking up any items.
= Albedo:
+ teleport 64 blocks in the air and survive the fall?
+ gain levitation for X seconds; by the time you hit the floor you must have done Y?
+ cure a zombie villager?
= Citrinitas:
+ Fight a bunch of mobs?
+ Must have 30+ XP levels to cast the second half?
= Cast the second half of the spell, requiring an enumeration of your accomplishments:
+ diamonds mined?
+ raids won?
+ spells cast?
+ undead slain?
= Rubedo: Attain the Great Work.

View file

@ -1,5 +1,7 @@
72b0863df1de5bd50917aa626e6a27933cc7c485 assets/hex/models/item/amethyst_dust.json
a310c1d496f4930955d1484ff271487d811bafd4 assets/hex/models/item/artifact.json
007d82b95d0c976ef5e1f726542ae752111efb8b assets/hex/models/item/artifact_filled.json
c3bfbb78256d5698e5df065e5fcbb1fcddb0505a assets/hex/models/item/charged_amethyst.json
fcd283e7b444ffacb8ba71462d2a0aff383fe4a2 assets/hex/models/item/cypher.json
9dcb4967450238440e0db6b25e81a6d9704d4835 assets/hex/models/item/cypher_filled.json
17b322e41c7789c5e6e91b53f7821446d7de6714 assets/hex/models/item/focus.json
@ -21,3 +23,5 @@ a354a2dc83d220c43d4c9f156059cbd8255e9e19 assets/hex/models/item/spellbook.json
eaddd1e5ae74293d784748e56e4a238a7607e34c assets/hex/models/item/trinket.json
f0cce957861ee854cbd68984015da2c530c9ef6d assets/hex/models/item/trinket_filled.json
837e7ed749afa44bd3be4114c7d81f3b8fe47852 assets/hex/models/item/wand.json
2ac42506f03235f4335dd62f0d6a0c4f23ca61b9 data/forge/loot_modifiers/global_loot_modifiers.json
a46281d65532086be7fbb63beb8c515edf2baaee data/hex/loot_modifiers/amethyst_cluster.json

View file

@ -0,0 +1,6 @@
{
"parent": "minecraft:item/handheld",
"textures": {
"layer0": "hex:item/amethyst_dust"
}
}

View file

@ -0,0 +1,6 @@
{
"parent": "minecraft:item/handheld",
"textures": {
"layer0": "hex:item/charged_amethyst"
}
}

View file

@ -0,0 +1,6 @@
{
"replace": false,
"entries": [
"hex:amethyst_cluster"
]
}

View file

@ -0,0 +1,23 @@
{
"conditions": [
{
"condition": "forge:loot_table_id",
"loot_table_id": "minecraft:blocks/amethyst_cluster"
},
{
"condition": "minecraft:inverted",
"term": {
"condition": "minecraft:match_tool",
"predicate": {
"enchantments": [
{
"enchantment": "minecraft:silk_touch"
}
]
}
}
}
],
"chargedChance": 0.95,
"type": "hex:amethyst_cluster"
}

View file

@ -8,35 +8,27 @@ public class HexConfig {
public final ForgeConfigSpec.DoubleValue healthToManaRate;
public final ForgeConfigSpec.IntValue wandMaxMana;
public final ForgeConfigSpec.IntValue wandRechargeRate;
public final ForgeConfigSpec.IntValue cypherMaxMana;
public final ForgeConfigSpec.IntValue trinketMaxMana;
public final ForgeConfigSpec.IntValue artifactMaxMana;
public final ForgeConfigSpec.IntValue artifactRechargeRate;
public final ForgeConfigSpec.IntValue batteryMaxMana;
public final ForgeConfigSpec.IntValue dustManaAmount;
public final ForgeConfigSpec.IntValue shardManaAmount;
public final ForgeConfigSpec.IntValue chargedCrystalManaAmount;
public final ForgeConfigSpec.IntValue opBreakHarvestLevel;
public final ForgeConfigSpec.IntValue maxRecurseDepth;
public HexConfig(ForgeConfigSpec.Builder builder) {
healthToManaRate = builder.comment("How many points of mana a half-heart is worth when casting from HP")
.defineInRange("healthToManaRate", 1_000_000.0 / 20.0, 0.0, 1_000_000.0);
.defineInRange("healthToManaRate", 1_000_000.0 / 20.0, 0.0, Double.POSITIVE_INFINITY);
builder.push("items");
wandMaxMana = builder.comment("The maximum amount of mana a wand can store.")
.defineInRange("wandMaxMana", 1_000_000, 0, Integer.MAX_VALUE);
wandRechargeRate = builder.comment("How many mana points a wand recharges per tick")
.defineInRange("wandRechargeRate", 2_000, 0, Integer.MAX_VALUE);
cypherMaxMana = builder.comment("The maximum amount of mana a cypher can store.")
.defineInRange("cypherMaxMana", 8_000_000, 0, Integer.MAX_VALUE);
trinketMaxMana = builder.comment("The maximum amount of mana a trinket can store.")
.defineInRange("trinketMaxMana", 4_000_000, 0, Integer.MAX_VALUE);
artifactMaxMana = builder.comment("The maximum amount of mana an artifact can store.")
.defineInRange("artifactMaxMana", 1_000_000, 0, Integer.MAX_VALUE);
artifactRechargeRate = builder.comment("How many mana points an artifact recharges per tick")
.defineInRange("artifactRechargeRate", 1_500, 0, Integer.MAX_VALUE);
batteryMaxMana = builder.comment("The maximum amount of mana a mana battery can store.")
.defineInRange("batteryMaxMana", 1_000_000, 0, Integer.MAX_VALUE);
dustManaAmount = builder.comment("How much mana a single Amethyst Dust item is worth")
.defineInRange("dustManaAmount", 100_000, 0, Integer.MAX_VALUE);
shardManaAmount = builder.comment("How much mana a single Amethyst Shard item is worth")
.defineInRange("shardManaAmount", 500_000, 0, Integer.MAX_VALUE);
chargedCrystalManaAmount = builder.comment("How much mana a single Charged Amethyst Crystal item is worth")
.defineInRange("chargedCrystalManaAmount", 1_000_000, 0, Integer.MAX_VALUE);
builder.pop();
builder.push("spells");

View file

@ -1,8 +1,9 @@
package at.petrak.hex;
import at.petrak.hex.common.casting.RegisterPatterns;
import at.petrak.hex.common.items.HexItems;
import at.petrak.hex.common.lib.RegisterPatterns;
import at.petrak.hex.common.network.HexMessages;
import at.petrak.hex.datagen.LootModifiers;
import at.petrak.hex.server.TickScheduler;
import net.minecraftforge.common.ForgeConfigSpec;
import net.minecraftforge.common.MinecraftForge;
@ -35,6 +36,7 @@ public class HexMod {
HexItems.ITEMS.register(evbus);
HexMessages.register();
MinecraftForge.EVENT_BUS.register(TickScheduler.INSTANCE);
LootModifiers.LOOT_MODS.register(evbus);
evbus.register(RegisterPatterns.class);
}

View file

@ -4,7 +4,7 @@ import at.petrak.hex.HexMod;
import at.petrak.hex.common.casting.SpellDatum;
import at.petrak.hex.common.items.HexItems;
import at.petrak.hex.common.items.ItemFocus;
import at.petrak.hex.common.items.ItemPackagedSpell;
import at.petrak.hex.common.items.magic.ItemPackagedSpell;
import net.minecraft.client.renderer.item.ItemProperties;
import net.minecraft.world.item.Item;
import net.minecraftforge.api.distmarker.Dist;

View file

@ -1,6 +1,7 @@
package at.petrak.hex.common.casting
import at.petrak.hex.hexmath.HexPattern
import net.minecraft.core.BlockPos
import net.minecraft.world.phys.Vec3
class CastException(val reason: Reason, vararg val data: Any) : Exception() {
@ -64,6 +65,13 @@ class CastException(val reason: Reason, vararg val data: Any) : Exception() {
* `Class<Item> expected, ItemStack got`
*/
BAD_OFFHAND_ITEM,
/**
* Required an inventory at the given position.
*
* `BlockPos pos`
*/
REQUIRES_INVENTORY
}
override val message: String
@ -76,5 +84,6 @@ class CastException(val reason: Reason, vararg val data: Any) : Exception() {
Reason.TOO_FAR -> "tried to interact with something too far away at ${this.data[0] as Vec3}"
Reason.TOO_MANY_RECURSIVE_EVALS -> "can only recursively call OpEval ${this.data[0] as Int} times but called it ${this.data[1] as Int} times"
Reason.BAD_OFFHAND_ITEM -> "operator expected ${(this.data[0] as Class<*>).typeName} in offhand but got ${this.data[1]}"
Reason.REQUIRES_INVENTORY -> "required an inventory at ${this.data[0] as BlockPos}"
}
}

View file

@ -4,8 +4,9 @@ import at.petrak.hex.HexMod
import at.petrak.hex.HexUtils
import at.petrak.hex.api.Operator
import at.petrak.hex.common.items.ItemDataHolder
import at.petrak.hex.common.items.ItemManaHolder
import at.petrak.hex.common.items.ItemSpellbook
import at.petrak.hex.common.items.ItemWand
import at.petrak.hex.common.items.magic.ItemPackagedSpell
import at.petrak.hex.common.lib.LibDamageSources
import net.minecraft.server.level.ServerLevel
import net.minecraft.server.level.ServerPlayer
@ -13,6 +14,7 @@ import net.minecraft.world.InteractionHand
import net.minecraft.world.item.Item
import net.minecraft.world.item.ItemStack
import net.minecraft.world.phys.Vec3
import java.util.*
import java.util.function.Predicate
import kotlin.math.min
@ -64,22 +66,40 @@ data class CastingContext(
/**
* Might cast from hitpoints.
* Returns the mana cost still remaining after we deplete everything. It will be <= 0 if we could pay for it. */
* Returns the mana cost still remaining after we deplete everything. It will be <= 0 if we could pay for it.
*/
fun withdrawMana(manaCost: Int, allowOvercast: Boolean): Int {
if (this.caster.isCreative) return 0
var costLeft = manaCost
val held = caster.getItemInHand(this.castingHand)
val tag = held.orCreateTag
val item = held.item
if (item is ItemManaHolder) {
costLeft = item.withdrawMana(tag, manaCost)
val casterStack = this.caster.getItemInHand(this.castingHand)
val casterItem = casterStack.item
val ipsCanDrawFromInv = if (casterItem is ItemPackagedSpell) {
val tag = casterStack.orCreateTag
val manaAvailable = tag.getInt(ItemPackagedSpell.TAG_MANA)
val manaToTake = min(costLeft, manaAvailable)
tag.putInt(ItemPackagedSpell.TAG_MANA, manaAvailable - manaToTake)
costLeft -= manaToTake
casterItem.canDrawManaFromInventory()
} else {
false
}
if (casterItem is ItemWand || ipsCanDrawFromInv) {
val manableItems = this.caster.inventory.items
.filter { !Objects.isNull(ManaHelper.priority(it)) }
.sortedByDescending(ManaHelper::priority)
for (stack in manableItems) {
costLeft -= ManaHelper.extractMana(stack, costLeft)!!
if (costLeft <= 0)
return costLeft
}
}
if (allowOvercast && costLeft > 0) {
// Cast from HP!
val healthToMana = HexMod.CONFIG.healthToManaRate.get()
val healthtoRemove = healthToMana * costLeft.toDouble()
val manaAbleToCastFromHP =
if (caster.isInvulnerable) Double.POSITIVE_INFINITY else caster.health / healthToMana
val manaAbleToCastFromHP = caster.health / healthToMana
caster.hurt(LibDamageSources.OVERCAST, healthtoRemove.toFloat())
costLeft = (costLeft.toDouble() - manaAbleToCastFromHP).toInt()
}

View file

@ -0,0 +1,86 @@
package at.petrak.hex.common.casting
import at.petrak.hex.HexMod
import at.petrak.hex.common.items.HexItems
import net.minecraft.util.Mth
import net.minecraft.world.item.ItemStack
import net.minecraft.world.item.Items
import kotlin.math.ceil
import kotlin.math.min
import kotlin.math.roundToInt
object ManaHelper {
/**
* Try to extract the given amount of mana from this item.
* This may mutate the itemstack.
*
* Return the actual amount of mana extracted, or null if this cannot have mana extracted.
*/
fun extractMana(stack: ItemStack, cost: Int): Int? {
val base = when (stack.item) {
HexItems.AMETHYST_DUST.get() -> HexMod.CONFIG.dustManaAmount.get()
Items.AMETHYST_SHARD -> HexMod.CONFIG.shardManaAmount.get()
HexItems.CHARGED_AMETHYST.get() -> HexMod.CONFIG.chargedCrystalManaAmount.get()
else -> return null
}
val itemsReqd = ceil(cost.toFloat() / base.toFloat()).toInt()
val actualItemsConsumed = min(stack.count, itemsReqd)
stack.shrink(actualItemsConsumed)
return base * actualItemsConsumed
}
/**
* Extract the entirety of the mana out of this.
* This may mutate the itemstack (and will probably consume it).
*
* Return the amount of mana extracted, or null if this cannot have mana extracted.
*/
fun extractAllMana(stack: ItemStack): Int? {
val base = when (stack.item) {
HexItems.AMETHYST_DUST.get() -> HexMod.CONFIG.dustManaAmount.get()
Items.AMETHYST_SHARD -> HexMod.CONFIG.shardManaAmount.get()
HexItems.CHARGED_AMETHYST.get() -> HexMod.CONFIG.chargedCrystalManaAmount.get()
else -> return null
}
val count = stack.count
stack.shrink(count)
return base * count
}
/**
* Return the "priority" this should have mana extracted with.
* Higher numbers mean it should be extracted more eagerly.
* Null means it isn't a mana item.
*/
fun priority(stack: ItemStack): Int? {
val base = 100 * when (stack.item) {
HexItems.CHARGED_AMETHYST.get() -> 1
Items.AMETHYST_SHARD -> 2
HexItems.AMETHYST_DUST.get() -> 3
else -> return null
}
return base + stack.count
}
fun barColor(mana: Int, maxMana: Int): Int {
val amt = if (maxMana == 0) {
0f
} else {
mana.toFloat() / maxMana.toFloat()
}
val r = Mth.lerp(amt, 84f, 254f)
val g = Mth.lerp(amt, 57f, 203f)
val b = Mth.lerp(amt, 138f, 230f)
return Mth.color(r / 255f, g / 255f, b / 255f)
}
fun barWidth(mana: Int, maxMana: Int): Int {
val amt = if (maxMana == 0) {
0f
} else {
mana.toFloat() / maxMana.toFloat()
}
return (13f * amt).roundToInt()
}
}

View file

@ -21,7 +21,7 @@ object OpAddMotion : SpellOperator {
val motion = args.getChecked<Vec3>(1)
return Pair(
Spell(target, motion),
motion.lengthSqr().toInt() * 100_000
(motion.lengthSqr() * 100_000f).toInt()
)
}

View file

@ -31,10 +31,11 @@ object OpBreakBlock : SpellOperator {
val tier =
HexMod.CONFIG.opBreakHarvestLevelBecauseForgeThoughtItWasAGoodIdeaToImplementHarvestTiersUsingAnHonestToGodTopoSort
if (!blockstate.isAir && (!blockstate.requiresCorrectToolForDrops() || TierSortingRegistry.isCorrectTierForDrops(
tier,
blockstate
))
if (
!blockstate.isAir
&& blockstate.getDestroySpeed(ctx.world, pos) >= 0f
&& (!blockstate.requiresCorrectToolForDrops()
|| TierSortingRegistry.isCorrectTierForDrops(tier, blockstate))
) {
ctx.world.destroyBlock(pos, true, ctx.caster)
} // TODO: else some kind of failureific particle effect?

View file

@ -2,36 +2,49 @@ package at.petrak.hex.common.casting.operators.spells
import at.petrak.hex.api.Operator.Companion.getChecked
import at.petrak.hex.api.SpellOperator
import at.petrak.hex.common.casting.CastException
import at.petrak.hex.common.casting.CastingContext
import at.petrak.hex.common.casting.RenderedSpell
import at.petrak.hex.common.casting.SpellDatum
import at.petrak.hex.common.items.ItemPackagedSpell
import at.petrak.hex.common.casting.*
import at.petrak.hex.common.items.magic.ItemPackagedSpell
import at.petrak.hex.hexmath.HexPattern
import net.minecraft.nbt.ListTag
import net.minecraft.world.entity.Entity
import net.minecraft.world.entity.item.ItemEntity
class OpMakePackagedSpell<T : ItemPackagedSpell>(val type: Class<T>, val cost: Int) : SpellOperator {
override val argc = 1
override val argc = 2
override fun execute(args: List<SpellDatum<*>>, ctx: CastingContext): Pair<RenderedSpell, Int> {
val otherHandItem = ctx.caster.getItemInHand(ctx.otherHand)
if (!type.isAssignableFrom(otherHandItem.item.javaClass)) {
throw CastException(CastException.Reason.BAD_OFFHAND_ITEM, type, otherHandItem)
}
val patterns = args.getChecked<List<SpellDatum<*>>>(0).map { it.tryGet<HexPattern>() }
return Pair(Spell(patterns), cost)
val entity = args.getChecked<Entity>(0)
val patterns = args.getChecked<List<SpellDatum<*>>>(1).map { it.tryGet<HexPattern>() }
if (entity !is ItemEntity)
throw CastException(CastException.Reason.OP_WRONG_TYPE, ItemEntity::class.java, entity)
return Pair(Spell(entity, patterns), cost)
}
private data class Spell(val patterns: List<HexPattern>) : RenderedSpell {
private data class Spell(val itemEntity: ItemEntity, val patterns: List<HexPattern>) : RenderedSpell {
override fun cast(ctx: CastingContext) {
val otherHandItem = ctx.caster.getItemInHand(ctx.otherHand)
if (otherHandItem.item is ItemPackagedSpell) {
if (otherHandItem.item is ItemPackagedSpell && itemEntity.isAlive) {
val manaAmt = ManaHelper.extractAllMana(itemEntity.item)
if (manaAmt != null) {
val tag = otherHandItem.orCreateTag
tag.putInt(ItemPackagedSpell.TAG_MANA, manaAmt)
tag.putInt(ItemPackagedSpell.TAG_START_MANA, manaAmt)
val patsTag = ListTag()
for (pat in patterns) {
patsTag.add(pat.serializeToNBT())
}
tag.put(ItemPackagedSpell.TAG_PATTERNS, patsTag)
if (itemEntity.item.isEmpty)
itemEntity.kill()
}
}
}
}

View file

@ -1,10 +1,11 @@
package at.petrak.hex.common.items;
import at.petrak.hex.HexMod;
import at.petrak.hex.common.items.magic.ItemArtifact;
import at.petrak.hex.common.items.magic.ItemCypher;
import at.petrak.hex.common.items.magic.ItemTrinket;
import at.petrak.hex.common.lib.LibItemNames;
import com.mojang.datafixers.util.Pair;
import net.minecraft.core.NonNullList;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.world.item.CreativeModeTab;
import net.minecraft.world.item.Item;
import net.minecraft.world.item.ItemStack;
@ -22,39 +23,26 @@ public class HexItems {
@Override
public void fillItemList(NonNullList<ItemStack> items) {
// Make the wand spawn with some sensible NBT
for (Pair<Integer, RegistryObject<Item>> p : new Pair[]{
new Pair<>(HexMod.CONFIG.wandMaxMana.get(), WAND),
new Pair<>(HexMod.CONFIG.cypherMaxMana.get(), CYPHER),
new Pair<>(HexMod.CONFIG.trinketMaxMana.get(), TRINKET),
new Pair<>(HexMod.CONFIG.artifactMaxMana.get(), ARTIFACT),
}) {
var mana = p.getFirst();
var stack = new ItemStack(p.getSecond()::get);
var tag = new CompoundTag();
tag.putInt(ItemManaHolder.TAG_MANA, mana);
tag.putInt(ItemWand.TAG_MAX_MANA, mana);
stack.setTag(tag);
items.add(stack);
}
super.fillItemList(items);
}
};
public static final RegistryObject<Item> WAND = ITEMS.register(LibItemNames.WAND,
() -> new ItemWand(new Item.Properties().stacksTo(1)));
() -> new ItemWand(unstackable()));
public static final RegistryObject<Item> FOCUS = ITEMS.register(LibItemNames.FOCUS,
() -> new ItemFocus(props()));
public static final RegistryObject<Item> SPELLBOOK = ITEMS.register(LibItemNames.SPELLBOOK,
() -> new ItemSpellbook(unstackable()));
public static final RegistryObject<Item> CYPHER = ITEMS.register(LibItemNames.CYPHER,
() -> new ItemCypher(new Item.Properties().stacksTo(1)));
() -> new ItemCypher(unstackable()));
public static final RegistryObject<Item> TRINKET = ITEMS.register(LibItemNames.TRINKET,
() -> new ItemTrinket(new Item.Properties().stacksTo(1)));
() -> new ItemTrinket(unstackable()));
public static final RegistryObject<Item> ARTIFACT = ITEMS.register(LibItemNames.ARTIFACT,
() -> new ItemArtifact(new Item.Properties().stacksTo(1)));
() -> new ItemArtifact(unstackable()));
public static final RegistryObject<Item> AMETHYST_DUST = ITEMS.register(LibItemNames.AMETHYST_DUST,
() -> new Item(props()));
public static final RegistryObject<Item> CHARGED_AMETHYST = ITEMS.register(LibItemNames.CHARGED_AMETHYST,
() -> new Item(props()));
public static Item.Properties props() {
return new Item.Properties().tab(TAB);

View file

@ -1,28 +0,0 @@
package at.petrak.hex.common.items;
import at.petrak.hex.HexMod;
import net.minecraft.nbt.CompoundTag;
/**
* Multi-use recharging magic item.
*/
public class ItemArtifact extends ItemPackagedSpell {
public ItemArtifact(Properties pProperties) {
super(pProperties);
}
@Override
boolean singleUse() {
return false;
}
@Override
int getMaxMana(CompoundTag tag) {
return HexMod.CONFIG.artifactMaxMana.get();
}
@Override
int getManaRechargeRate(CompoundTag tag) {
return HexMod.CONFIG.artifactRechargeRate.get();
}
}

View file

@ -1,28 +0,0 @@
package at.petrak.hex.common.items;
import at.petrak.hex.HexMod;
import net.minecraft.nbt.CompoundTag;
/**
* Single-use magic item.
*/
public class ItemCypher extends ItemPackagedSpell {
public ItemCypher(Properties pProperties) {
super(pProperties);
}
@Override
boolean singleUse() {
return true;
}
@Override
int getMaxMana(CompoundTag tag) {
return HexMod.CONFIG.cypherMaxMana.get();
}
@Override
int getManaRechargeRate(CompoundTag tag) {
return 0;
}
}

View file

@ -0,0 +1,62 @@
package at.petrak.hex.common.items;
import at.petrak.hex.HexMod;
import at.petrak.hex.common.casting.ManaHelper;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.world.item.Item;
import net.minecraft.world.item.ItemStack;
public class ItemManaBattery extends Item {
public static final String TAG_MANA = "hex.mana";
public ItemManaBattery(Properties pProperties) {
super(pProperties);
}
public int getMana(CompoundTag tag) {
return tag.getInt(TAG_MANA);
}
/**
* Return how much mana we lack
*/
public int withdrawMana(CompoundTag tag, int cost) {
var manaHere = getMana(tag);
var manaLeft = manaHere - cost;
tag.putInt(TAG_MANA, Math.max(0, manaLeft));
return Math.max(0, cost - manaHere);
}
@Override
public boolean isDamageable(ItemStack stack) {
return false;
}
@Override
public boolean canBeDepleted() {
return false;
}
@Override
public boolean isBarVisible(ItemStack pStack) {
return true;
}
@Override
public int getBarColor(ItemStack pStack) {
var tag = pStack.getOrCreateTag();
var mana = getMana(tag);
var maxMana = HexMod.CONFIG.batteryMaxMana.get();
return ManaHelper.INSTANCE.barColor(mana, maxMana);
}
@Override
public int getBarWidth(ItemStack pStack) {
var tag = pStack.getOrCreateTag();
var mana = getMana(tag);
var maxMana = HexMod.CONFIG.batteryMaxMana.get();
return ManaHelper.INSTANCE.barWidth(mana, maxMana);
}
}

View file

@ -1,88 +0,0 @@
package at.petrak.hex.common.items;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.util.Mth;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.item.Item;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.level.Level;
public abstract class ItemManaHolder extends Item {
public static final String TAG_MANA = "hex.mana";
public ItemManaHolder(Properties pProperties) {
super(pProperties);
}
abstract int getMaxMana(CompoundTag tag);
abstract int getManaRechargeRate(CompoundTag tag);
public int getMana(CompoundTag tag) {
return tag.getInt(TAG_MANA);
}
/**
* Return how much mana we lack
*/
public int withdrawMana(CompoundTag tag, int cost) {
var manaHere = getMana(tag);
var manaLeft = manaHere - cost;
tag.putInt(TAG_MANA, Math.max(0, manaLeft));
return Math.max(0, cost - manaHere);
}
@Override
public void inventoryTick(ItemStack pStack, Level pLevel, Entity pEntity, int pSlotId, boolean pIsSelected) {
var tag = pStack.getOrCreateTag();
tag.putInt(TAG_MANA, Math.min(getMana(tag) + getManaRechargeRate(tag), getMaxMana(tag)));
}
@Override
public boolean isDamageable(ItemStack stack) {
return false;
}
@Override
public boolean canBeDepleted() {
return false;
}
@Override
public boolean isBarVisible(ItemStack pStack) {
return true;
}
@Override
public int getBarColor(ItemStack pStack) {
var tag = pStack.getOrCreateTag();
var mana = getMana(tag);
var maxMana = getMaxMana(tag);
float amt;
if (maxMana == 0) {
amt = 0f;
} else {
amt = ((float) mana) / ((float) maxMana);
}
var r = Mth.lerp(amt, 149f, 112f);
var g = Mth.lerp(amt, 196f, 219f);
var b = Mth.lerp(amt, 174f, 212f);
return Mth.color(r / 255f, g / 255f, b / 255f);
}
@Override
public int getBarWidth(ItemStack pStack) {
var tag = pStack.getOrCreateTag();
var mana = getMana(tag);
var maxMana = getMaxMana(tag);
float amt;
if (maxMana == 0) {
amt = 0f;
} else {
amt = ((float) mana) / ((float) maxMana);
}
return Math.round(13f * amt);
}
}

View file

@ -1,28 +0,0 @@
package at.petrak.hex.common.items;
import at.petrak.hex.HexMod;
import net.minecraft.nbt.CompoundTag;
/**
* Multi-use but non-recharging magic item.
*/
public class ItemTrinket extends ItemPackagedSpell {
public ItemTrinket(Properties pProperties) {
super(pProperties);
}
@Override
boolean singleUse() {
return false;
}
@Override
int getMaxMana(CompoundTag tag) {
return HexMod.CONFIG.trinketMaxMana.get();
}
@Override
int getManaRechargeRate(CompoundTag tag) {
return 0;
}
}

View file

@ -1,35 +1,22 @@
package at.petrak.hex.common.items;
import at.petrak.hex.HexMod;
import at.petrak.hex.client.gui.GuiSpellcasting;
import net.minecraft.client.Minecraft;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.stats.Stats;
import net.minecraft.world.InteractionHand;
import net.minecraft.world.InteractionResultHolder;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.item.Item;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.level.Level;
public class ItemWand extends ItemManaHolder {
public static final String TAG_MAX_MANA = "maxMana";
public class ItemWand extends Item {
public static final String TAG_HARNESS = "harness";
public ItemWand(Properties pProperties) {
super(pProperties);
}
@Override
int getMaxMana(CompoundTag tag) {
return tag.getInt(TAG_MAX_MANA);
}
@Override
int getManaRechargeRate(CompoundTag tag) {
return HexMod.CONFIG.wandRechargeRate.get();
}
@Override
public InteractionResultHolder<ItemStack> use(Level world, Player player, InteractionHand hand) {
if (world.isClientSide()) {

View file

@ -0,0 +1,17 @@
package at.petrak.hex.common.items.magic;
public class ItemArtifact extends ItemPackagedSpell {
public ItemArtifact(Properties pProperties) {
super(pProperties);
}
@Override
public boolean canDrawManaFromInventory() {
return true;
}
@Override
public boolean singleUse() {
return false;
}
}

View file

@ -0,0 +1,17 @@
package at.petrak.hex.common.items.magic;
public class ItemCypher extends ItemPackagedSpell {
public ItemCypher(Properties pProperties) {
super(pProperties);
}
@Override
public boolean canDrawManaFromInventory() {
return false;
}
@Override
public boolean singleUse() {
return true;
}
}

View file

@ -1,8 +1,9 @@
package at.petrak.hex.common.items;
package at.petrak.hex.common.items.magic;
import at.petrak.hex.HexMod;
import at.petrak.hex.common.casting.CastingContext;
import at.petrak.hex.common.casting.CastingHarness;
import at.petrak.hex.common.casting.ManaHelper;
import at.petrak.hex.hexmath.HexPattern;
import net.minecraft.Util;
import net.minecraft.nbt.CompoundTag;
@ -15,6 +16,7 @@ import net.minecraft.stats.Stats;
import net.minecraft.world.InteractionHand;
import net.minecraft.world.InteractionResultHolder;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.item.Item;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.UseAnim;
import net.minecraft.world.level.Level;
@ -25,7 +27,9 @@ import java.util.List;
/**
* Item that holds a list of patterns in it ready to be cast
*/
public abstract class ItemPackagedSpell extends ItemManaHolder {
public abstract class ItemPackagedSpell extends Item {
public static final String TAG_MANA = "hex:mana";
public static final String TAG_START_MANA = "hex:start_mana";
public static final String TAG_PATTERNS = "patterns";
public static final ResourceLocation HAS_PATTERNS_PRED = new ResourceLocation(HexMod.MOD_ID, "has_patterns");
@ -33,7 +37,9 @@ public abstract class ItemPackagedSpell extends ItemManaHolder {
super(pProperties);
}
abstract boolean singleUse();
public abstract boolean singleUse();
public abstract boolean canDrawManaFromInventory();
@Override
public InteractionResultHolder<ItemStack> use(Level world, Player player, InteractionHand usedHand) {
@ -77,6 +83,7 @@ public abstract class ItemPackagedSpell extends ItemManaHolder {
player.getCooldowns().addCooldown(this, 20);
if (singleUse()) {
stack.shrink(1);
return InteractionResultHolder.consume(stack);
} else {
return InteractionResultHolder.success(stack);
@ -96,9 +103,24 @@ public abstract class ItemPackagedSpell extends ItemManaHolder {
@Override
public boolean isBarVisible(ItemStack pStack) {
var tag = pStack.getOrCreateTag();
return !singleUse() && tag.contains(TAG_PATTERNS);
return tag.contains(TAG_PATTERNS);
}
@Override
public int getBarColor(ItemStack pStack) {
var tag = pStack.getOrCreateTag();
var mana = tag.getInt(TAG_MANA);
var maxMana = tag.getInt(TAG_START_MANA);
return ManaHelper.INSTANCE.barColor(mana, maxMana);
}
@Override
public int getBarWidth(ItemStack pStack) {
var tag = pStack.getOrCreateTag();
var mana = tag.getInt(TAG_MANA);
var maxMana = tag.getInt(TAG_START_MANA);
return ManaHelper.INSTANCE.barWidth(mana, maxMana);
}
private static List<HexPattern> getPatterns(CompoundTag tag) {
var out = new ArrayList<HexPattern>();

View file

@ -0,0 +1,17 @@
package at.petrak.hex.common.items.magic;
public class ItemTrinket extends ItemPackagedSpell {
public ItemTrinket(Properties pProperties) {
super(pProperties);
}
@Override
public boolean canDrawManaFromInventory() {
return false;
}
@Override
public boolean singleUse() {
return false;
}
}

View file

@ -7,4 +7,6 @@ public class LibItemNames {
public static final String CYPHER = "cypher";
public static final String TRINKET = "trinket";
public static final String ARTIFACT = "artifact";
public static final String AMETHYST_DUST = "amethyst_dust";
public static final String CHARGED_AMETHYST = "charged_amethyst";
}

View file

@ -1,14 +1,16 @@
package at.petrak.hex.common.casting;
package at.petrak.hex.common.lib;
import at.petrak.hex.HexMod;
import at.petrak.hex.api.Operator;
import at.petrak.hex.api.PatternRegistry;
import at.petrak.hex.common.casting.SpellDatum;
import at.petrak.hex.common.casting.Widget;
import at.petrak.hex.common.casting.operators.*;
import at.petrak.hex.common.casting.operators.math.*;
import at.petrak.hex.common.casting.operators.spells.*;
import at.petrak.hex.common.items.ItemArtifact;
import at.petrak.hex.common.items.ItemCypher;
import at.petrak.hex.common.items.ItemTrinket;
import at.petrak.hex.common.items.magic.ItemArtifact;
import at.petrak.hex.common.items.magic.ItemCypher;
import at.petrak.hex.common.items.magic.ItemTrinket;
import com.mojang.datafixers.util.Pair;
import net.minecraft.world.phys.Vec3;
import net.minecraftforge.eventbus.api.SubscribeEvent;

View file

@ -18,5 +18,7 @@ public class DataGenerators {
if (ev.includeServer()) {
// recipes
}
// On both sides
gen.addProvider(new LootModifiers(gen));
}
}

View file

@ -3,7 +3,7 @@ package at.petrak.hex.datagen;
import at.petrak.hex.HexMod;
import at.petrak.hex.common.items.HexItems;
import at.petrak.hex.common.items.ItemFocus;
import at.petrak.hex.common.items.ItemPackagedSpell;
import at.petrak.hex.common.items.magic.ItemPackagedSpell;
import com.mojang.datafixers.util.Pair;
import net.minecraft.data.DataGenerator;
import net.minecraft.resources.ResourceLocation;
@ -22,6 +22,8 @@ public class ItemModels extends ItemModelProvider {
protected void registerModels() {
simpleItem(HexItems.WAND.get());
simpleItem(HexItems.SPELLBOOK.get());
simpleItem(HexItems.AMETHYST_DUST.get());
simpleItem(HexItems.CHARGED_AMETHYST.get());
String[] focusTypes = new String[]{
"empty", "entity", "double", "vec3", "widget", "list", "pattern"

View file

@ -0,0 +1,89 @@
package at.petrak.hex.datagen;
import at.petrak.hex.HexMod;
import at.petrak.hex.common.items.HexItems;
import com.google.gson.JsonObject;
import net.minecraft.advancements.critereon.EnchantmentPredicate;
import net.minecraft.advancements.critereon.ItemPredicate;
import net.minecraft.advancements.critereon.MinMaxBounds;
import net.minecraft.data.DataGenerator;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.util.GsonHelper;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.enchantment.EnchantmentHelper;
import net.minecraft.world.item.enchantment.Enchantments;
import net.minecraft.world.level.storage.loot.LootContext;
import net.minecraft.world.level.storage.loot.parameters.LootContextParams;
import net.minecraft.world.level.storage.loot.predicates.LootItemCondition;
import net.minecraft.world.level.storage.loot.predicates.MatchTool;
import net.minecraftforge.common.data.GlobalLootModifierProvider;
import net.minecraftforge.common.loot.GlobalLootModifierSerializer;
import net.minecraftforge.common.loot.LootModifier;
import net.minecraftforge.common.loot.LootTableIdCondition;
import net.minecraftforge.registries.DeferredRegister;
import net.minecraftforge.registries.ForgeRegistries;
import net.minecraftforge.registries.RegistryObject;
import org.jetbrains.annotations.NotNull;
import java.util.List;
public class LootModifiers extends GlobalLootModifierProvider {
public static final DeferredRegister<GlobalLootModifierSerializer<?>> LOOT_MODS = DeferredRegister.create(
ForgeRegistries.LOOT_MODIFIER_SERIALIZERS, HexMod.MOD_ID);
private static final RegistryObject<AmethystClusterModifier.Serializer> AMETHYST_CLUSTER = LOOT_MODS.register(
"amethyst_cluster", AmethystClusterModifier.Serializer::new);
public LootModifiers(DataGenerator gen) {
super(gen, HexMod.MOD_ID);
}
@Override
protected void start() {
add("amethyst_cluster", AMETHYST_CLUSTER.get(), new AmethystClusterModifier(new LootItemCondition[]{
LootTableIdCondition.builder(new ResourceLocation("minecraft:blocks/amethyst_cluster")).build(),
MatchTool.toolMatches(
ItemPredicate.Builder.item().hasEnchantment(
new EnchantmentPredicate(Enchantments.SILK_TOUCH, MinMaxBounds.Ints.ANY)))
.invert().build(),
}, 0.95f));
}
public static final class AmethystClusterModifier extends LootModifier {
private final float chargedChance;
public AmethystClusterModifier(LootItemCondition[] conditions, float chargedChance) {
super(conditions);
this.chargedChance = chargedChance;
}
@NotNull
@Override
protected List<ItemStack> doApply(List<ItemStack> generatedLoot, LootContext context) {
var rand = context.getRandom();
var tool = context.getParamOrNull(LootContextParams.TOOL);
var fortuneLevel = EnchantmentHelper.getItemEnchantmentLevel(Enchantments.BLOCK_FORTUNE, tool);
var dustCount = 1 + Math.round(rand.nextFloat() * fortuneLevel);
var hasCharged = (rand.nextFloat() * (fortuneLevel / 2f + 1)) > this.chargedChance;
generatedLoot.add(new ItemStack(HexItems.AMETHYST_DUST.get(), dustCount));
generatedLoot.add(new ItemStack(HexItems.CHARGED_AMETHYST.get(), hasCharged ? 1 : 0));
return generatedLoot;
}
private static class Serializer extends GlobalLootModifierSerializer<AmethystClusterModifier> {
@Override
public AmethystClusterModifier read(ResourceLocation location, JsonObject object,
LootItemCondition[] conditions) {
var chargedChance = GsonHelper.getAsFloat(object, "chargedChance");
return new AmethystClusterModifier(conditions, chargedChance);
}
@Override
public JsonObject write(AmethystClusterModifier instance) {
var obj = makeConditions(instance.conditions);
obj.addProperty("chargedChance", instance.chargedChance);
return obj;
}
}
}
}

View file

@ -5,6 +5,8 @@
"item.hex.cypher": "Cypher",
"item.hex.trinket": "Trinket",
"item.hex.artifact": "Artifact",
"item.hex.amethyst_dust": "Amethyst Dust",
"item.hex.charged_amethyst": "Charged Amethyst Crystal",
"death.attack.hex.overcast": "%s's mind was consumed into energy",
"hex.spellbook.tooltip.page": "Selected Page %d/%d",

Binary file not shown.

After

Width:  |  Height:  |  Size: 380 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 205 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 408 B

After

Width:  |  Height:  |  Size: 394 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 611 B

After

Width:  |  Height:  |  Size: 603 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 496 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 438 B

After

Width:  |  Height:  |  Size: 431 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 395 B

After

Width:  |  Height:  |  Size: 368 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 364 B

After

Width:  |  Height:  |  Size: 368 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 261 B

After

Width:  |  Height:  |  Size: 272 B