Merge branch 'gamma-delta:main' into main

This commit is contained in:
ChuijkYahus 2023-02-16 15:01:23 +08:00 committed by GitHub
commit fdef4b0632
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
531 changed files with 7421 additions and 5400 deletions

View file

@ -1,21 +1,20 @@
// 1.19.2 2022-11-09T19:44:43.409223 Item Models: hexcasting
// 1.19.2 2023-02-15T01:34:30.703213817 Item Models: hexcasting
f2156b3a7041cf99891b528393db64c6b9ca1a4f assets/hexcasting/models/item/abacus.json
783d8454d6b74f926be0d3e02d87c6505e9d76d0 assets/hexcasting/models/item/acacia_staff.json
933059cd7c2dbaff0e643c644c14af0c0e77aa29 assets/hexcasting/models/item/acacia_staff.json
19730853397b109cfedd0c3bbda83d5de6cd15b9 assets/hexcasting/models/item/akashic_record.json
8c735feff09d46d00ed681311f46f61a50cfdc9b assets/hexcasting/models/item/amethyst_dust.json
d1b0892de9d751e7bebc763e6407d5285363c851 assets/hexcasting/models/item/artifact.json
7eb3eb776e70eb616c12ada500b9d1d6a3249a6a assets/hexcasting/models/item/artifact_filled.json
82e3be7bbdad92d2b4c728be54d9d2f2809a0ac2 assets/hexcasting/models/item/battery.json
3dcc41ab5cbf7004f9c959d89be961aff0ce6032 assets/hexcasting/models/item/birch_staff.json
f05937151873b1de302a011851edc62d0554e4db assets/hexcasting/models/item/bosnia_staff.json
d1310c25bca32980214aa1054eddb1772fb55d12 assets/hexcasting/models/item/birch_staff.json
ec7c3a51882a432185fdbb6a449e66165b6a4c4c assets/hexcasting/models/item/charged_amethyst.json
c64ed609ece68994ce23dd2809145040bce13579 assets/hexcasting/models/item/conjured.json
c64ed609ece68994ce23dd2809145040bce13579 assets/hexcasting/models/item/conjured_block.json
c8da4227db3c80e3e2e7f2fb2ae2649656429d68 assets/hexcasting/models/item/creative_unlocker.json
21c0b8d424043a1b0d2748f59d292b129193d9a5 assets/hexcasting/models/item/crimson_staff.json
b0ad71595b450f5b9a6bdc2a14fc03afef0b850a assets/hexcasting/models/item/crimson_staff.json
e47acd1d6ef29a3e1941afb1b212bd40b963cb72 assets/hexcasting/models/item/cypher.json
2db55347092ad6bc9e58bc968e88a3b6c5fd77c1 assets/hexcasting/models/item/cypher_filled.json
81eed736b6bae7e427d3c6972ef15a8d967489e5 assets/hexcasting/models/item/dark_oak_staff.json
cfaf12703ca40959bc5bedac4d1c120273704fcf assets/hexcasting/models/item/dark_oak_staff.json
113c51af571a92009f5f687a82e10bc5ce97b010 assets/hexcasting/models/item/dye_colorizer_black.json
b5a04716775ba2e1b137abc513025b2f1065e5d1 assets/hexcasting/models/item/dye_colorizer_blue.json
0bb3afbd937b2e07523a581f6e3f389e11078595 assets/hexcasting/models/item/dye_colorizer_brown.json
@ -37,7 +36,7 @@ c9faada6299f388afc2d2798843d2b45159950d1 assets/hexcasting/models/item/edified_d
7f22e012a844cc2c5e30b0fcbdc2e7e4afac1c40 assets/hexcasting/models/item/edified_log.json
6b2c9d4aca0c869d7e18707c22b00c14e1d30f0c assets/hexcasting/models/item/edified_pressure_plate.json
31b4d60ff15a6d6de7aecb6feeba25a366bba2fd assets/hexcasting/models/item/edified_slab.json
66d951804b32cb77bbe6e81d5053ea14c9690b1b assets/hexcasting/models/item/edified_staff.json
ff0e3934116fdae9ecad0d006402868914ab42ec assets/hexcasting/models/item/edified_staff.json
2584421c2e9e1cdf22a703018b54cf449613d7d9 assets/hexcasting/models/item/edified_stairs.json
ae58c5b7c304d33cbde60caf44a4c4ee4ec1a633 assets/hexcasting/models/item/edified_trapdoor.json
084183e4351973c8165f6f459c0f0dba2463d957 assets/hexcasting/models/item/edified_wood.json
@ -45,10 +44,11 @@ ae58c5b7c304d33cbde60caf44a4c4ee4ec1a633 assets/hexcasting/models/item/edified_t
947d1539d88f9d6fd0afcdf831f4327356d19baf assets/hexcasting/models/item/focus_filled.json
cb2d973af25a2ec07e6094ecc106c896a06918dc assets/hexcasting/models/item/focus_sealed.json
6ec61fea7d8c49cc0c45b64857fd926451b4845f assets/hexcasting/models/item/jeweler_hammer.json
e8ce683966f007b56cc551b3137c77f0a1fe2d9a assets/hexcasting/models/item/jungle_staff.json
c34501cbfc0bb1b6943b4a9986fbe927ff177c61 assets/hexcasting/models/item/jungle_staff.json
abfc028c974a02780aed3d7a5859352503bbd920 assets/hexcasting/models/item/lens.json
a34a6d777ae265c7e49c8bb23c15f04359236544 assets/hexcasting/models/item/lore_fragment.json
9c79526e19bfafb8370718ddcc9126204ed85e3a assets/hexcasting/models/item/oak_staff.json
371c16c6c882d6d0370135a8953c408fa1658bed assets/hexcasting/models/item/mangrove_staff.json
a17e45161b6723e72c710695a536e39ba0905725 assets/hexcasting/models/item/oak_staff.json
38d1dc73c49d185b86c098b13611bf3ec07b255c assets/hexcasting/models/item/old_staff.json
82fa0a2bb17e40c0b3f826e97b2e95445ec24ab8 assets/hexcasting/models/item/patchouli_book.json
d69d10e6cb967b98b3294cc86174182c671de671 assets/hexcasting/models/item/phial_large_0.json
@ -97,11 +97,13 @@ e6452f95b60240e0067769d7f32a0b9fa7718a1b assets/hexcasting/models/item/slate_wri
986674763b45e0f9381f9f34a708082e5230652a assets/hexcasting/models/item/spellbook.json
f962c13ab9e299885387cee35b16006651821e81 assets/hexcasting/models/item/spellbook_filled.json
c29e6e7b2168eeeb13b1fc3e93ffc3e0c9bd11ce assets/hexcasting/models/item/spellbook_sealed.json
7f03c6ea7a07cfedc7d580bb9ba5fcdc54cccb86 assets/hexcasting/models/item/spruce_staff.json
2f422cc59c84f6c997475367967390186adce5f0 assets/hexcasting/models/item/spruce_staff.json
d6ebc87cb0fa6f86bee3a4eade7329ebb0cf2d38 assets/hexcasting/models/item/stripped_edified_log.json
ea3f18f75776022127f3a108119e3f7a5c211c0f assets/hexcasting/models/item/stripped_edified_wood.json
0a100b64e77394606018320bbc5752a546fe0af4 assets/hexcasting/models/item/sub_sandwich.json
6a7f5af82cf8ec72c3457ef4c1ae11a76717bf88 assets/hexcasting/models/item/thought_knot.json
93b2191ffab47003f661b75a85cd833ec64f0c15 assets/hexcasting/models/item/thought_knot_written.json
5f4831d11d8f45b037a6f48e12d2e794ada7b961 assets/hexcasting/models/item/trinket.json
946970e74b8d3c76c15191f494bc1f3d7e36aa43 assets/hexcasting/models/item/trinket_filled.json
c6523de66cbfae3a1e6361c635cc693a0a089bb3 assets/hexcasting/models/item/uuid_colorizer.json
f5b45d5997acc8abe8cc70065a5790b642234fcb assets/hexcasting/models/item/warped_staff.json
a882b3dbb46c9abee345c446fb83f4d293442c5f assets/hexcasting/models/item/warped_staff.json

View file

@ -12,12 +12,6 @@
"predicate": {
"hexcasting:funny_level": 1.0
}
},
{
"model": "hexcasting:item/bosnia_staff",
"predicate": {
"hexcasting:funny_level": 2.0
}
}
],
"textures": {

View file

@ -12,12 +12,6 @@
"predicate": {
"hexcasting:funny_level": 1.0
}
},
{
"model": "hexcasting:item/bosnia_staff",
"predicate": {
"hexcasting:funny_level": 2.0
}
}
],
"textures": {

View file

@ -1,6 +0,0 @@
{
"parent": "minecraft:item/handheld_rod",
"textures": {
"layer0": "hexcasting:item/staves/bosnia"
}
}

View file

@ -12,12 +12,6 @@
"predicate": {
"hexcasting:funny_level": 1.0
}
},
{
"model": "hexcasting:item/bosnia_staff",
"predicate": {
"hexcasting:funny_level": 2.0
}
}
],
"textures": {

View file

@ -12,12 +12,6 @@
"predicate": {
"hexcasting:funny_level": 1.0
}
},
{
"model": "hexcasting:item/bosnia_staff",
"predicate": {
"hexcasting:funny_level": 2.0
}
}
],
"textures": {

View file

@ -12,12 +12,6 @@
"predicate": {
"hexcasting:funny_level": 1.0
}
},
{
"model": "hexcasting:item/bosnia_staff",
"predicate": {
"hexcasting:funny_level": 2.0
}
}
],
"textures": {

View file

@ -12,12 +12,6 @@
"predicate": {
"hexcasting:funny_level": 1.0
}
},
{
"model": "hexcasting:item/bosnia_staff",
"predicate": {
"hexcasting:funny_level": 2.0
}
}
],
"textures": {

View file

@ -0,0 +1,20 @@
{
"parent": "minecraft:item/handheld_rod",
"overrides": [
{
"model": "hexcasting:item/mangrove_staff",
"predicate": {
"hexcasting:funny_level": 0.0
}
},
{
"model": "hexcasting:item/old_staff",
"predicate": {
"hexcasting:funny_level": 1.0
}
}
],
"textures": {
"layer0": "hexcasting:item/staves/mangrove"
}
}

View file

@ -12,12 +12,6 @@
"predicate": {
"hexcasting:funny_level": 1.0
}
},
{
"model": "hexcasting:item/bosnia_staff",
"predicate": {
"hexcasting:funny_level": 2.0
}
}
],
"textures": {

View file

@ -12,12 +12,6 @@
"predicate": {
"hexcasting:funny_level": 1.0
}
},
{
"model": "hexcasting:item/bosnia_staff",
"predicate": {
"hexcasting:funny_level": 2.0
}
}
],
"textures": {

View file

@ -0,0 +1,20 @@
{
"parent": "minecraft:item/generated",
"overrides": [
{
"model": "hexcasting:item/thought_knot",
"predicate": {
"hexcasting:written": 0.0
}
},
{
"model": "hexcasting:item/thought_knot_written",
"predicate": {
"hexcasting:written": 1.0
}
}
],
"textures": {
"layer0": "hexcasting:item/thought_knot"
}
}

View file

@ -0,0 +1,7 @@
{
"parent": "minecraft:item/generated",
"textures": {
"layer0": "hexcasting:item/thought_knot",
"layer1": "hexcasting:item/thought_knot_overlay"
}
}

View file

@ -12,12 +12,6 @@
"predicate": {
"hexcasting:funny_level": 1.0
}
},
{
"model": "hexcasting:item/bosnia_staff",
"predicate": {
"hexcasting:funny_level": 2.0
}
}
],
"textures": {

View file

@ -2,8 +2,8 @@ Hello, intrepid Github reader!
The "flavor text" words for things in this mod and the internal names are different. (Sorry.)
- A "Hex" is a `Cast`, cast through a [`CastingHarness`](api/spell/casting/CastingHarness.kt)
- A "Pattern" is a [`HexPattern`](api/spell/math/HexPattern.kt)
- An "Action" is an [`Operator`](api/spell/Action.kt)
- An action that pushes a spell is a [`Spell`](api/spell/SpellAction.kt)
- A "Hex" is a `Cast`, cast through a [`CastingHarness`](api/casting/eval/CastingHarness.kt)
- A "Pattern" is a [`HexPattern`](api/casting/math/HexPattern.kt)
- An "Action" is an [`Operator`](api/casting/castables/Action.kt)
- An action that pushes a spell is a [`Spell`](api/casting/castables/SpellAction.kt)

View file

@ -1,7 +1,15 @@
package at.petrak.hexcasting.api;
import at.petrak.hexcasting.api.casting.ActionRegistryEntry;
import at.petrak.hexcasting.api.casting.castables.SpecialHandler;
import com.google.common.base.Suppliers;
import net.minecraft.ChatFormatting;
import net.minecraft.network.chat.Component;
import net.minecraft.resources.ResourceKey;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.EntityType;
import net.minecraft.world.phys.Vec3;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
@ -22,6 +30,63 @@ public interface HexAPI {
}
});
/**
* Return the localization key for the given action.
* <p>
* Note we're allowed to have action <em>resource keys</em> on the client, just no actual actions.
* <p>
* Special handlers should be calling {@link SpecialHandler#getName()}
*/
default String getActionI18nKey(ResourceKey<ActionRegistryEntry> action) {
return "hexcasting.action.%s".formatted(action.location().toString());
}
default String getSpecialHandlerI18nKey(ResourceKey<SpecialHandler.Factory<?>> action) {
return "hexcasting.special.%s".formatted(action.location().toString());
}
/**
* Currently introspection/retrospection/consideration are hardcoded, but at least their names won't be
*/
default String getRawHookI18nKey(ResourceLocation name) {
return "hexcasting.rawhook.%s".formatted(name);
}
default Component getActionI18n(ResourceKey<ActionRegistryEntry> key, boolean isGreat) {
return Component.translatable(getActionI18nKey(key))
.withStyle(isGreat ? ChatFormatting.GOLD : ChatFormatting.LIGHT_PURPLE);
}
default Component getSpecialHandlerI18n(ResourceKey<SpecialHandler.Factory<?>> key) {
return Component.translatable(getSpecialHandlerI18nKey(key))
.withStyle(ChatFormatting.LIGHT_PURPLE);
}
default Component getRawHookI18n(ResourceLocation name) {
return Component.translatable(getRawHookI18nKey(name)).withStyle(ChatFormatting.LIGHT_PURPLE);
}
/**
* Register an entity with the given ID to have its velocity as perceived by OpEntityVelocity be different
* than it's "normal" velocity
*/
// Should be OK to use the type directly as the key as they're singleton identity objects
default <T extends Entity> void registerSpecialVelocityGetter(EntityType<T> key, EntityVelocityGetter<T> getter) {
}
/**
* If the entity has had a special getter registered with {@link HexAPI#registerSpecialVelocityGetter} then
* return that, otherwise return its normal delta movement
*/
default Vec3 getEntityVelocitySpecial(Entity entity) {
return entity.getDeltaMovement();
}
@FunctionalInterface
interface EntityVelocityGetter<T extends Entity> {
Vec3 getVelocity(T entity);
}
static HexAPI instance() {
return INSTANCE.get();
}

View file

@ -1,336 +0,0 @@
package at.petrak.hexcasting.api;
import at.petrak.hexcasting.api.spell.Action;
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 com.mojang.datafixers.util.Pair;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.world.level.saveddata.SavedData;
import org.jetbrains.annotations.Nullable;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentLinkedDeque;
import java.util.concurrent.ConcurrentMap;
import java.util.stream.Collectors;
public class PatternRegistry {
private static final ConcurrentMap<ResourceLocation, Action> actionLookup = new ConcurrentHashMap<>();
private static final ConcurrentMap<Action, ResourceLocation> keyLookup = new ConcurrentHashMap<>();
private static final ConcurrentLinkedDeque<SpecialHandlerEntry> specialHandlers = new ConcurrentLinkedDeque<>();
// Map signatures to the "preferred" direction they start in and their operator ID.
private static final ConcurrentMap<String, RegularEntry> regularPatternLookup =
new ConcurrentHashMap<>();
private static final ConcurrentMap<ResourceLocation, PerWorldEntry> perWorldPatternLookup =
new ConcurrentHashMap<>();
public static void mapPattern(HexPattern pattern, ResourceLocation id,
Action action) throws RegisterPatternException {
mapPattern(pattern, id, action, false);
}
/**
* Associate a given angle signature with a SpellOperator.
*/
public static void mapPattern(HexPattern pattern, ResourceLocation id, Action action,
boolean isPerWorld) throws RegisterPatternException {
if (actionLookup.containsKey(id)) {
throw new RegisterPatternException("The operator with id `%s` was already registered to: %s", id,
actionLookup.get(id));
}
actionLookup.put(id, action);
keyLookup.put(action, id);
if (isPerWorld) {
perWorldPatternLookup.put(id, new PerWorldEntry(pattern, id));
} else {
regularPatternLookup.put(pattern.anglesSignature(), new RegularEntry(pattern.getStartDir(), id));
}
}
/**
* Add a special handler, to take an arbitrary pattern and return whatever kind of operator you like.
*/
public static void addSpecialHandler(SpecialHandlerEntry handler) {
specialHandlers.add(handler);
}
/**
* Add a special handler, to take an arbitrary pattern and return whatever kind of operator you like.
*/
public static void addSpecialHandler(ResourceLocation id, SpecialHandler handler) {
addSpecialHandler(new SpecialHandlerEntry(id, handler));
}
/**
* Internal use only.
*/
public static Action matchPattern(HexPattern pat, ServerLevel overworld) throws MishapInvalidPattern {
return matchPatternAndID(pat, overworld).getFirst();
}
/**
* Internal use only.
*/
public static Pair<Action, ResourceLocation> matchPatternAndID(HexPattern pat,
ServerLevel overworld) throws MishapInvalidPattern {
// Pipeline:
// patterns are registered here every time the game boots
// when we try to look
for (var handler : specialHandlers) {
var op = handler.handler.handlePattern(pat);
if (op != null) {
return new Pair<>(op, handler.id);
}
}
// Is it global?
var sig = pat.anglesSignature();
if (regularPatternLookup.containsKey(sig)) {
var it = regularPatternLookup.get(sig);
if (!actionLookup.containsKey(it.opId)) {
throw new MishapInvalidPattern();
}
var op = actionLookup.get(it.opId);
return new Pair<>(op, it.opId);
}
// Look it up in the world?
var ds = overworld.getDataStorage();
Save perWorldPatterns =
ds.computeIfAbsent(Save::load, () -> Save.create(overworld.getSeed()), TAG_SAVED_DATA);
perWorldPatterns.fillMissingEntries(overworld.getSeed());
if (perWorldPatterns.lookup.containsKey(sig)) {
var it = perWorldPatterns.lookup.get(sig);
return new Pair<>(actionLookup.get(it.getFirst()), it.getFirst());
}
throw new MishapInvalidPattern();
}
/**
* Internal use only.
*/
@Nullable
public static Action lookupPatternByShape(HexPattern pat) {
// Pipeline:
// patterns are registered here every time the game boots
// when we try to look
for (var handler : specialHandlers) {
var op = handler.handler.handlePattern(pat);
if (op != null) {
return op;
}
}
// Is it global?
var sig = pat.anglesSignature();
if (regularPatternLookup.containsKey(sig)) {
var it = regularPatternLookup.get(sig);
if (!actionLookup.containsKey(it.opId)) {
return null;
}
return actionLookup.get(it.opId);
}
// Currently, there's no way to look up the name of a Great Spell, as the client is unaware of the correct
// mapping.
// TODO: add code to match any pattern in the shape of a Great Spell to its operator.
// var ds = overworld.getDataStorage();
// Save perWorldPatterns =
// ds.computeIfAbsent(Save::load, () -> Save.create(overworld.getSeed()), TAG_SAVED_DATA);
// perWorldPatterns.fillMissingEntries(overworld.getSeed());
// if (perWorldPatterns.lookup.containsKey(sig)) {
// var it = perWorldPatterns.lookup.get(sig);
// return new Pair<>(actionLookup.get(it.getFirst()), it.getFirst());
// }
return null;
}
/**
* Internal use only.
* <p>
* Map of signatures to (op id, canonical start dir)
*/
public static Map<String, Pair<ResourceLocation, HexDir>> getPerWorldPatterns(ServerLevel overworld) {
var ds = overworld.getDataStorage();
Save perWorldPatterns =
ds.computeIfAbsent(Save::load, () -> Save.create(overworld.getSeed()), TAG_SAVED_DATA);
return perWorldPatterns.lookup;
}
public static ResourceLocation lookupPattern(Action action) {
return keyLookup.get(action);
}
/**
* Internal use only.
*/
public static PatternEntry lookupPattern(ResourceLocation opId) {
if (perWorldPatternLookup.containsKey(opId)) {
var it = perWorldPatternLookup.get(opId);
return new PatternEntry(it.prototype, actionLookup.get(it.opId), true);
}
for (var kv : regularPatternLookup.entrySet()) {
var sig = kv.getKey();
var entry = kv.getValue();
if (entry.opId.equals(opId)) {
var pattern = HexPattern.fromAngles(sig, entry.preferredStart);
return new PatternEntry(pattern, actionLookup.get(entry.opId), false);
}
}
throw new IllegalArgumentException("could not find a pattern for " + opId);
}
/**
* Internal use only.
*/
public static Set<ResourceLocation> getAllPerWorldPatternNames() {
return perWorldPatternLookup.keySet();
}
/**
* 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.
* <p>
* In the base mod, this is used for number patterns and Bookkeeper's Gambit.
*/
@FunctionalInterface
public interface SpecialHandler {
@Nullable Action handlePattern(HexPattern pattern);
}
public record SpecialHandlerEntry(ResourceLocation id, SpecialHandler handler) {
}
public static class RegisterPatternException extends Exception {
public RegisterPatternException(String msg, Object... formats) {
super(String.format(msg, formats));
}
}
private record RegularEntry(HexDir preferredStart, ResourceLocation opId) {
}
private record PerWorldEntry(HexPattern prototype, ResourceLocation opId) {
}
// Fake class we pretend to use internally
public record PatternEntry(HexPattern prototype, Action action, boolean isPerWorld) {
}
/**
* Maps angle sigs to resource locations and their preferred start dir so we can look them up in the main registry
* Save this on the world in case the random algorithm changes.
*/
public static class Save extends SavedData {
private static final String TAG_OP_ID = "op_id";
private static final String TAG_START_DIR = "start_dir";
// Maps hex signatures to (op ids, canonical start dir)
private Map<String, Pair<ResourceLocation, HexDir>> lookup;
private boolean missingEntries;
public Save(Map<String, Pair<ResourceLocation, HexDir>> lookup, boolean missingEntries) {
this.lookup = lookup;
this.missingEntries = missingEntries;
}
public Save(Map<String, Pair<ResourceLocation, HexDir>> lookup) {
this(lookup, missingEntries(lookup));
}
private static boolean missingEntries(Map<String, Pair<ResourceLocation, HexDir>> lookup) {
var allIds = lookup.values().stream().map(Pair::getFirst).collect(Collectors.toSet());
return perWorldPatternLookup.values().stream().anyMatch(it -> allIds.contains(it.opId));
}
private void fillMissingEntries(long seed) {
if (missingEntries) {
var doneAny = false;
var allIds = lookup.values().stream().map(Pair::getFirst).collect(Collectors.toSet());
for (var entry : perWorldPatternLookup.values()) {
if (!allIds.contains(entry.opId)) {
scrungle(lookup, entry.prototype, entry.opId, seed);
doneAny = true;
}
}
if (doneAny) {
setDirty();
missingEntries = false;
}
}
}
@Override
public CompoundTag save(CompoundTag tag) {
this.lookup.forEach((sig, rhs) -> {
var entry = new CompoundTag();
entry.putString(TAG_OP_ID, rhs.getFirst().toString());
entry.putInt(TAG_START_DIR, rhs.getSecond().ordinal());
tag.put(sig, entry);
});
return tag;
}
private static Save load(CompoundTag tag) {
var map = new HashMap<String, Pair<ResourceLocation, HexDir>>();
var allIds = new HashSet<ResourceLocation>();
for (var sig : tag.getAllKeys()) {
var entry = tag.getCompound(sig);
var opId = ResourceLocation.tryParse(entry.getString(TAG_OP_ID));
allIds.add(opId);
var startDir = HexDir.values()[entry.getInt(TAG_START_DIR)];
map.put(sig, new Pair<>(opId, startDir));
}
var missingEntries = perWorldPatternLookup.values().stream().anyMatch(it -> allIds.contains(it.opId));
return new Save(map, missingEntries);
}
private static void scrungle(Map<String, Pair<ResourceLocation, HexDir>> lookup, HexPattern prototype,
ResourceLocation opId, long seed) {
var scrungled = EulerPathFinder.findAltDrawing(prototype, seed, it -> {
var sig = it.anglesSignature();
return !lookup.containsKey(sig) &&
!regularPatternLookup.containsKey(sig)
&& specialHandlers.stream().noneMatch(handler -> handler.handler.handlePattern(it) != null);
});
lookup.put(scrungled.anglesSignature(), new Pair<>(opId, scrungled.getStartDir()));
}
public static Save create(long seed) {
var map = new HashMap<String, Pair<ResourceLocation, HexDir>>();
PatternRegistry.perWorldPatternLookup.values().forEach(it -> scrungle(map, it.prototype, it.opId, seed));
var save = new Save(map);
save.setDirty();
return save;
}
}
public static final String TAG_SAVED_DATA = "hex.per-world-patterns";
public static String getPatternCountInfo() {
return String.format(
"Loaded %d regular patterns, " +
"%d per-world patterns, and " +
"%d special handlers.", regularPatternLookup.size(), perWorldPatternLookup.size(),
specialHandlers.size());
}
}

View file

@ -1,6 +1,6 @@
package at.petrak.hexcasting.api.addldata;
import at.petrak.hexcasting.api.spell.iota.Iota;
import at.petrak.hexcasting.api.casting.iota.Iota;
import net.minecraft.server.level.ServerLevel;
import org.jetbrains.annotations.Nullable;

View file

@ -1,6 +1,6 @@
package at.petrak.hexcasting.api.addldata;
import at.petrak.hexcasting.api.spell.iota.Iota;
import at.petrak.hexcasting.api.casting.iota.Iota;
import at.petrak.hexcasting.common.lib.hex.HexIotaTypes;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.server.level.ServerLevel;

View file

@ -1,6 +1,6 @@
package at.petrak.hexcasting.api.addldata;
import at.petrak.hexcasting.api.spell.iota.Iota;
import at.petrak.hexcasting.api.casting.iota.Iota;
import at.petrak.hexcasting.common.entities.EntityWallScroll;
import at.petrak.hexcasting.xplat.IXplatAbstractions;
import net.minecraft.nbt.CompoundTag;

View file

@ -1,6 +1,6 @@
package at.petrak.hexcasting.api.block.circle;
import at.petrak.hexcasting.api.spell.math.HexPattern;
import at.petrak.hexcasting.api.casting.math.HexPattern;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.server.level.ServerLevel;

View file

@ -1,6 +1,6 @@
package at.petrak.hexcasting.api.block.circle;
import at.petrak.hexcasting.api.spell.math.HexPattern;
import at.petrak.hexcasting.api.casting.math.HexPattern;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.world.level.Level;

View file

@ -4,11 +4,11 @@ import at.petrak.hexcasting.api.block.HexBlockEntity;
import at.petrak.hexcasting.api.misc.FrozenColorizer;
import at.petrak.hexcasting.api.misc.MediaConstants;
import at.petrak.hexcasting.api.mod.HexConfig;
import at.petrak.hexcasting.api.spell.ParticleSpray;
import at.petrak.hexcasting.api.spell.casting.CastingContext;
import at.petrak.hexcasting.api.spell.casting.CastingHarness;
import at.petrak.hexcasting.api.spell.casting.SpellCircleContext;
import at.petrak.hexcasting.api.spell.iota.PatternIota;
import at.petrak.hexcasting.api.casting.ParticleSpray;
import at.petrak.hexcasting.api.casting.eval.CastingEnvironment;
import at.petrak.hexcasting.api.casting.eval.CastingHarness;
import at.petrak.hexcasting.api.casting.eval.SpellCircleContext;
import at.petrak.hexcasting.api.casting.iota.PatternIota;
import at.petrak.hexcasting.api.utils.MediaHelper;
import at.petrak.hexcasting.common.items.magic.ItemCreativeUnlocker;
import at.petrak.hexcasting.common.lib.HexItems;
@ -282,7 +282,7 @@ public abstract class BlockEntityAbstractImpetus extends HexBlockEntity implemen
if (player instanceof ServerPlayer splayer) {
var bounds = getBounds(this.trackedBlocks);
var ctx = new CastingContext(splayer, InteractionHand.MAIN_HAND,
var ctx = new CastingEnvironment(splayer, InteractionHand.MAIN_HAND,
new SpellCircleContext(this.getBlockPos(), bounds, this.activatorAlwaysInRange()));
var harness = new CastingHarness(ctx);

View file

@ -0,0 +1,15 @@
package at.petrak.hexcasting.api.casting;
import at.petrak.hexcasting.api.casting.castables.Action;
import at.petrak.hexcasting.api.casting.math.HexPattern;
/**
* A bit of wrapper information around an action to go in the registry.
*
* @param prototype The pattern associated with this action. The start dir acts as the "canonical" start direction
* for display in the book. For per-world patterns, the angle signature is the *shape* of the pattern
* but probably not the pattern itself.
* @param action The action itself
*/
public record ActionRegistryEntry(HexPattern prototype, Action action) {
}

View file

@ -1,8 +1,8 @@
package at.petrak.hexcasting.api.spell
package at.petrak.hexcasting.api.casting
import at.petrak.hexcasting.api.spell.casting.eval.SpellContinuation
import at.petrak.hexcasting.api.spell.casting.sideeffects.OperatorSideEffect
import at.petrak.hexcasting.api.spell.iota.Iota
import at.petrak.hexcasting.api.casting.eval.vm.SpellContinuation
import at.petrak.hexcasting.api.casting.eval.sideeffects.OperatorSideEffect
import at.petrak.hexcasting.api.casting.iota.Iota
/**
* What happens when an operator is through?

View file

@ -1,11 +1,11 @@
@file:JvmName("OperatorUtils")
package at.petrak.hexcasting.api.spell
package at.petrak.hexcasting.api.casting
import at.petrak.hexcasting.api.spell.iota.*
import at.petrak.hexcasting.api.spell.math.HexPattern
import at.petrak.hexcasting.api.spell.mishaps.MishapInvalidIota
import at.petrak.hexcasting.api.spell.mishaps.MishapNotEnoughArgs
import at.petrak.hexcasting.api.casting.iota.*
import at.petrak.hexcasting.api.casting.math.HexPattern
import at.petrak.hexcasting.api.casting.mishaps.MishapInvalidIota
import at.petrak.hexcasting.api.casting.mishaps.MishapNotEnoughArgs
import at.petrak.hexcasting.api.utils.asTranslatedComponent
import com.mojang.datafixers.util.Either
import com.mojang.math.Vector3f
@ -13,9 +13,9 @@ import net.minecraft.core.BlockPos
import net.minecraft.server.level.ServerPlayer
import net.minecraft.world.entity.Entity
import net.minecraft.world.entity.LivingEntity
import net.minecraft.world.entity.Mob
import net.minecraft.world.entity.decoration.ArmorStand
import net.minecraft.world.entity.item.ItemEntity
import net.minecraft.world.entity.npc.Villager
import net.minecraft.world.phys.Vec3
import java.util.function.DoubleUnaryOperator
import kotlin.math.abs
@ -99,14 +99,14 @@ fun List<Iota>.getPlayer(idx: Int, argc: Int = 0): ServerPlayer {
throw MishapInvalidIota.ofType(x, if (argc == 0) idx else argc - (idx + 1), "entity.player")
}
fun List<Iota>.getVillager(idx: Int, argc: Int = 0): Villager {
fun List<Iota>.getMob(idx: Int, argc: Int = 0): Mob {
val x = this.getOrElse(idx) { throw MishapNotEnoughArgs(idx + 1, this.size) }
if (x is EntityIota) {
val e = x.entity
if (e is Villager)
if (e is Mob)
return e
}
throw MishapInvalidIota.ofType(x, if (argc == 0) idx else argc - (idx + 1), "entity.villager")
throw MishapInvalidIota.ofType(x, if (argc == 0) idx else argc - (idx + 1), "entity.mob")
}
fun List<Iota>.getLivingEntityButNotArmorStand(idx: Int, argc: Int = 0): LivingEntity {
@ -216,7 +216,7 @@ fun List<Iota>.getPositiveIntUnderInclusive(idx: Int, max: Int, argc: Int = 0):
if (x is DoubleIota) {
val double = x.double
val rounded = double.roundToInt()
if (abs(double - rounded) <= DoubleIota.TOLERANCE && rounded in 0 .. max) {
if (abs(double - rounded) <= DoubleIota.TOLERANCE && rounded in 0..max) {
return rounded
}
}

View file

@ -1,4 +1,4 @@
package at.petrak.hexcasting.api.spell
package at.petrak.hexcasting.api.casting
import at.petrak.hexcasting.api.misc.FrozenColorizer
import at.petrak.hexcasting.common.network.MsgCastParticleAck

View file

@ -0,0 +1,57 @@
package at.petrak.hexcasting.api.casting;
import at.petrak.hexcasting.api.casting.castables.SpecialHandler;
import net.minecraft.resources.ResourceKey;
/**
* Possible things we find when trying to match a pattern's shape.
*/
public abstract sealed class PatternShapeMatch {
/**
* I've never met that pattern in my life
*/
public static final class Nothing extends PatternShapeMatch {
}
/**
* The shape exactly matches a pattern that isn't altered per world
*/
public static final class Normal extends PatternShapeMatch {
public final ResourceKey<ActionRegistryEntry> key;
public Normal(ResourceKey<ActionRegistryEntry> key) {
this.key = key;
}
}
/**
* The pattern is the right <em>shape</em> to be one of the per-world patterns.
* <p>
* On the server, {@link PerWorld#certain} means whether this is an exact match, or if it's just the
* right shape. (In other words it should only actually be casted if it is true.)
* <p>
* On the client, it is always false.
*/
public static final class PerWorld extends PatternShapeMatch {
public final ResourceKey<ActionRegistryEntry> key;
public final boolean certain;
public PerWorld(ResourceKey<ActionRegistryEntry> key, boolean certain) {
this.key = key;
this.certain = certain;
}
}
/**
* The shape matches a special handler
*/
public static final class Special extends PatternShapeMatch {
public final ResourceKey<SpecialHandler.Factory<?>> key;
public final SpecialHandler handler;
public Special(ResourceKey<SpecialHandler.Factory<?>> key, SpecialHandler handler) {
this.key = key;
this.handler = handler;
}
}
}

View file

@ -0,0 +1,7 @@
package at.petrak.hexcasting.api.casting
import at.petrak.hexcasting.api.casting.eval.CastingEnvironment
interface RenderedSpell {
fun cast(ctx: CastingEnvironment)
}

View file

@ -1,6 +1,6 @@
package at.petrak.hexcasting.api.spell
package at.petrak.hexcasting.api.casting
import at.petrak.hexcasting.api.spell.iota.Iota
import at.petrak.hexcasting.api.casting.iota.Iota
/**
* Restricted interface for functional lists.

View file

@ -0,0 +1,59 @@
package at.petrak.hexcasting.api.casting.castables
import at.petrak.hexcasting.api.casting.OperationResult
import at.petrak.hexcasting.api.casting.eval.CastingEnvironment
import at.petrak.hexcasting.api.casting.eval.vm.SpellContinuation
import at.petrak.hexcasting.api.casting.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
* and pushing one new value.
* For a more "traditional" pop arguments, push return experience, see [ConstMediaAction].
*
* Instances of this can exist on the client, but they should NEVER be used there. They only
* exist on the client because Minecraft's registry system demands they do; any information
* the client needs about them is stored elsewhere. (For example, their canonical stroke order
* is stored in [ActionRegistryEntry], and their localization key is gotten from the resource key
* via [at.petrak.hexcasting.api.HexAPI.getActionI18nKey].)
*/
interface Action {
/**
* Operate on the stack. Return the new stack and any side effects of the cast.
*
* Although this is passed a [MutableList], this is only for the convenience of implementors.
* It is a clone of the stack and modifying it does nothing. You must return the new stack
* with the [OperationResult].
*
* A particle effect at the cast site and various messages and advancements are done automagically.
*/
fun operate(
continuation: SpellContinuation,
stack: MutableList<Iota>,
ravenmind: Iota?,
ctx: CastingEnvironment
): OperationResult
companion object {
// I see why vzakii did this: you can't raycast out to infinity!
const val MAX_DISTANCE: Double = 32.0
const val MAX_DISTANCE_FROM_SENTINEL: Double = 16.0
@JvmStatic
fun raycastEnd(origin: Vec3, look: Vec3): Vec3 =
origin.add(look.normalize().scale(MAX_DISTANCE))
@JvmStatic
fun makeConstantOp(x: Iota): Action = object : ConstMediaAction {
override val argc: Int
get() = 0
override fun execute(args: List<Iota>, ctx: CastingEnvironment): List<Iota> =
listOf(x)
}
public val DOUBLE_FORMATTER = DecimalFormat("####.####")
}
}

View file

@ -1,10 +1,11 @@
package at.petrak.hexcasting.api.spell
package at.petrak.hexcasting.api.casting.castables
import at.petrak.hexcasting.api.spell.casting.CastingContext
import at.petrak.hexcasting.api.spell.casting.eval.SpellContinuation
import at.petrak.hexcasting.api.spell.casting.sideeffects.OperatorSideEffect
import at.petrak.hexcasting.api.spell.iota.Iota
import at.petrak.hexcasting.api.spell.mishaps.MishapNotEnoughArgs
import at.petrak.hexcasting.api.casting.OperationResult
import at.petrak.hexcasting.api.casting.eval.CastingEnvironment
import at.petrak.hexcasting.api.casting.eval.vm.SpellContinuation
import at.petrak.hexcasting.api.casting.eval.sideeffects.OperatorSideEffect
import at.petrak.hexcasting.api.casting.iota.Iota
import at.petrak.hexcasting.api.casting.mishaps.MishapNotEnoughArgs
/**
* A SimpleOperator that always costs the same amount of media.
@ -14,13 +15,13 @@ interface ConstMediaAction : Action {
val mediaCost: Int
get() = 0
fun execute(args: List<Iota>, ctx: CastingContext): List<Iota>
fun execute(args: List<Iota>, ctx: CastingEnvironment): List<Iota>
override fun operate(
continuation: SpellContinuation,
stack: MutableList<Iota>,
ravenmind: Iota?,
ctx: CastingContext
ctx: CastingEnvironment
): OperationResult {
if (this.argc > stack.size)
throw MishapNotEnoughArgs(this.argc, stack.size)

View file

@ -0,0 +1,40 @@
package at.petrak.hexcasting.api.casting.castables;
import at.petrak.hexcasting.api.casting.math.HexPattern;
import net.minecraft.network.chat.Component;
import org.jetbrains.annotations.Nullable;
/**
* 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.
* <p>
* In the base mod, this is used for number patterns and Bookkeeper's Gambit.
* <p>
* There's a separation between the special handlers and their factories so we never have to use
* {@link Action} instances on the client. We can have SpecialHandlers on the client though because they're just
* wrappers.
*/
public interface SpecialHandler {
/**
* Convert this to an action, for modification of the stack and state.
* <p>
* This is called on the SERVER-SIDE ONLY.
*/
Action act();
/**
* Get the name of this handler.
*/
Component getName();
/**
* Given a pattern, possibly make up the special handler from it.
* <p>
* This is what goes in the registry! Think of it like BlockEntityType vs BlockEntity.
*/
@FunctionalInterface
public interface Factory<T extends SpecialHandler> {
@Nullable T tryMatch(HexPattern pattern);
}
}

View file

@ -1,28 +1,31 @@
package at.petrak.hexcasting.api.spell
package at.petrak.hexcasting.api.casting.castables
import at.petrak.hexcasting.api.spell.casting.CastingContext
import at.petrak.hexcasting.api.spell.casting.eval.SpellContinuation
import at.petrak.hexcasting.api.spell.casting.sideeffects.OperatorSideEffect
import at.petrak.hexcasting.api.spell.iota.Iota
import at.petrak.hexcasting.api.spell.mishaps.MishapNotEnoughArgs
import at.petrak.hexcasting.api.casting.OperationResult
import at.petrak.hexcasting.api.casting.ParticleSpray
import at.petrak.hexcasting.api.casting.RenderedSpell
import at.petrak.hexcasting.api.casting.eval.CastingEnvironment
import at.petrak.hexcasting.api.casting.eval.sideeffects.OperatorSideEffect
import at.petrak.hexcasting.api.casting.eval.vm.SpellContinuation
import at.petrak.hexcasting.api.casting.iota.Iota
import at.petrak.hexcasting.api.casting.mishaps.MishapNotEnoughArgs
interface SpellAction : Action {
val argc: Int
fun hasCastingSound(ctx: CastingContext): Boolean = true
fun hasCastingSound(ctx: CastingEnvironment): Boolean = true
fun awardsCastingStat(ctx: CastingContext): Boolean = true
fun awardsCastingStat(ctx: CastingEnvironment): Boolean = true
fun execute(
args: List<Iota>,
ctx: CastingContext
ctx: CastingEnvironment
): Triple<RenderedSpell, Int, List<ParticleSpray>>?
override fun operate(
continuation: SpellContinuation,
stack: MutableList<Iota>,
ravenmind: Iota?,
ctx: CastingContext
ctx: CastingEnvironment
): OperationResult {
if (this.argc > stack.size)
throw MishapNotEnoughArgs(this.argc, stack.size)
@ -36,15 +39,13 @@ interface SpellAction : Action {
if (media > 0)
sideEffects.add(OperatorSideEffect.ConsumeMedia(media))
// Don't have an effect if the caster isn't enlightened, even if processing other side effects
if (!isGreat || ctx.isCasterEnlightened)
sideEffects.add(
OperatorSideEffect.AttemptSpell(
spell,
this.hasCastingSound(ctx),
this.awardsCastingStat(ctx)
)
sideEffects.add(
OperatorSideEffect.AttemptSpell(
spell,
this.hasCastingSound(ctx),
this.awardsCastingStat(ctx)
)
)
for (spray in particles)
sideEffects.add(OperatorSideEffect.Particles(spray))

View file

@ -0,0 +1,20 @@
package at.petrak.hexcasting.api.casting.eval
import at.petrak.hexcasting.api.casting.eval.sideeffects.EvalSound
import at.petrak.hexcasting.api.casting.eval.sideeffects.OperatorSideEffect
import at.petrak.hexcasting.api.casting.eval.vm.FunctionalData
import at.petrak.hexcasting.api.casting.eval.vm.SpellContinuation
/**
* The result of doing something to a cast harness.
*
* Contains the next thing to execute after this is finished, the modified state of the stack,
* and side effects, as well as display information for the client.
*/
data class CastResult(
val continuation: SpellContinuation,
val newData: FunctionalData?,
val sideEffects: List<OperatorSideEffect>,
val resolutionType: ResolvedPatternType,
val sound: EvalSound,
)

View file

@ -1,12 +1,12 @@
package at.petrak.hexcasting.api.spell.casting
package at.petrak.hexcasting.api.casting.eval
import at.petrak.hexcasting.api.HexAPI.modLoc
import at.petrak.hexcasting.api.casting.castables.Action
import at.petrak.hexcasting.api.casting.mishaps.MishapEntityTooFarAway
import at.petrak.hexcasting.api.casting.mishaps.MishapEvalTooDeep
import at.petrak.hexcasting.api.casting.mishaps.MishapLocationTooFarAway
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
@ -25,7 +25,7 @@ import kotlin.math.min
/**
* Transient info about the moment the spell started being cast.
*/
data class CastingContext(
data class CastingEnvironment(
val caster: ServerPlayer,
val castingHand: InteractionHand,
val source: CastSource,
@ -39,7 +39,7 @@ data class CastingContext(
)
constructor(caster: ServerPlayer, castingHand: InteractionHand, spellCircleContext: SpellCircleContext) :
this(caster, castingHand, CastSource.SPELL_CIRCLE, spellCircleContext)
this(caster, castingHand, CastSource.SPELL_CIRCLE, spellCircleContext)
private var depth: Int = 0
@ -49,6 +49,8 @@ data class CastingContext(
private val entitiesGivenMotion = mutableSetOf<Entity>()
// TODO: what the hell does this function even do. why are we using it. why do we continually put a predicate
// into here and then do the *same* predicate *again*
inline fun getHeldItemToOperateOn(acceptItemIf: (ItemStack) -> Boolean): Pair<ItemStack, InteractionHand> {
val handItem = caster.getItemInHand(otherHand)
if (!acceptItemIf(handItem)) {
@ -136,8 +138,8 @@ data class CastingContext(
fun canEditBlockAt(pos: BlockPos): Boolean {
return this.isVecInRange(Vec3.atCenterOf(pos))
&& this.caster.gameMode.gameModeForPlayer != GameType.ADVENTURE
&& this.world.mayInteract(this.caster, pos)
&& this.caster.gameMode.gameModeForPlayer != GameType.ADVENTURE
&& this.world.mayInteract(this.caster, pos)
}
/**

View file

@ -1,38 +1,38 @@
package at.petrak.hexcasting.api.spell.casting
package at.petrak.hexcasting.api.casting.eval
import at.petrak.hexcasting.api.PatternRegistry
import at.petrak.hexcasting.api.HexAPI
import at.petrak.hexcasting.api.advancements.HexAdvancementTriggers
import at.petrak.hexcasting.api.block.circle.BlockEntityAbstractImpetus
import at.petrak.hexcasting.api.casting.ParticleSpray
import at.petrak.hexcasting.api.casting.PatternShapeMatch
import at.petrak.hexcasting.api.casting.PatternShapeMatch.*
import at.petrak.hexcasting.api.casting.SpellList
import at.petrak.hexcasting.api.casting.castables.Action
import at.petrak.hexcasting.api.casting.eval.sideeffects.OperatorSideEffect
import at.petrak.hexcasting.api.casting.eval.vm.FrameEvaluate
import at.petrak.hexcasting.api.casting.eval.vm.FunctionalData
import at.petrak.hexcasting.api.casting.eval.vm.SpellContinuation
import at.petrak.hexcasting.api.casting.iota.Iota
import at.petrak.hexcasting.api.casting.iota.ListIota
import at.petrak.hexcasting.api.casting.iota.PatternIota
import at.petrak.hexcasting.api.casting.math.HexDir
import at.petrak.hexcasting.api.casting.math.HexPattern
import at.petrak.hexcasting.api.casting.mishaps.*
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
import at.petrak.hexcasting.api.mod.HexStatistics
import at.petrak.hexcasting.api.mod.HexTags
import at.petrak.hexcasting.api.spell.Action
import at.petrak.hexcasting.api.spell.ParticleSpray
import at.petrak.hexcasting.api.spell.SpellList
import at.petrak.hexcasting.api.spell.casting.eval.ContinuationFrame
import at.petrak.hexcasting.api.spell.casting.eval.FrameEvaluate
import at.petrak.hexcasting.api.spell.casting.eval.FunctionalData
import at.petrak.hexcasting.api.spell.casting.eval.SpellContinuation
import at.petrak.hexcasting.api.spell.casting.sideeffects.EvalSound
import at.petrak.hexcasting.api.spell.casting.sideeffects.OperatorSideEffect
import at.petrak.hexcasting.api.spell.iota.Iota
import at.petrak.hexcasting.api.spell.iota.ListIota
import at.petrak.hexcasting.api.spell.iota.PatternIota
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.casting.PatternRegistryManifest
import at.petrak.hexcasting.common.lib.hex.HexEvalSounds
import at.petrak.hexcasting.common.lib.hex.HexIotaTypes
import at.petrak.hexcasting.xplat.IXplatAbstractions
import net.minecraft.ChatFormatting
import com.mojang.datafixers.util.Either
import net.minecraft.nbt.CompoundTag
import net.minecraft.nbt.Tag
import net.minecraft.network.chat.Component
import net.minecraft.resources.ResourceLocation
import net.minecraft.server.level.ServerLevel
import net.minecraft.sounds.SoundSource
import net.minecraft.util.Mth
@ -43,7 +43,10 @@ import kotlin.math.min
/**
* Keeps track of a player casting a spell on the server.
* It's stored as NBT on the wand.
* It's stored as NBT on the player.
*
* TODO oh god this entire class needs a gigantic refactor. why are there like 6 different entrypoints for casting
* a pattern. oh god.
*/
class CastingHarness private constructor(
var stack: MutableList<Iota>,
@ -51,13 +54,13 @@ class CastingHarness private constructor(
var parenCount: Int,
var parenthesized: List<Iota>,
var escapeNext: Boolean,
val ctx: CastingContext,
val ctx: CastingEnvironment,
val prepackagedColorizer: FrozenColorizer? // for trinkets with colorizers
) {
@JvmOverloads
constructor(
ctx: CastingContext,
ctx: CastingEnvironment,
prepackagedColorizer: FrozenColorizer? = null
) : this(mutableListOf(), null, 0, mutableListOf(), false, ctx, prepackagedColorizer)
@ -66,7 +69,7 @@ class CastingHarness private constructor(
*/
fun executeIota(iota: Iota, world: ServerLevel): ControllerInfo = executeIotas(listOf(iota), world)
private fun displayPattern(escapeNext: Boolean, parenCount: Int, iotaRepresentation: Component) {
private fun displayPatternDebug(escapeNext: Boolean, parenCount: Int, iotaRepresentation: Component) {
if (this.ctx.debugPatterns) {
val display = " ".repeat(parenCount).asTextComponent
if (escapeNext)
@ -77,24 +80,6 @@ class CastingHarness private constructor(
}
}
private fun getOperatorForPattern(iota: Iota, world: ServerLevel): Action? {
if (iota is PatternIota)
return PatternRegistry.matchPattern(iota.pattern, world)
return null
}
private fun getPatternForFrame(frame: ContinuationFrame): HexPattern? {
if (frame !is FrameEvaluate) return null
return (frame.list.car as? PatternIota)?.pattern
}
private fun getOperatorForFrame(frame: ContinuationFrame, world: ServerLevel): Action? {
if (frame !is FrameEvaluate) return null
return getOperatorForPattern(frame.list.car, world)
}
/**
* Given a list of iotas, execute them in sequence.
*/
@ -109,28 +94,9 @@ class CastingHarness private constructor(
// Take the top of the continuation stack...
val next = continuation.frame
// ...and execute it.
val result = try {
next.evaluate(continuation.next, world, this)
} catch (mishap: Mishap) {
val pattern = getPatternForFrame(next)
val operator = try {
getOperatorForFrame(next, world)
} catch (e: Throwable) {
null
}
CastResult(
continuation,
null,
mishap.resolutionType(ctx),
listOf(
OperatorSideEffect.DoMishap(
mishap,
Mishap.Context(pattern ?: HexPattern(HexDir.WEST), operator)
)
),
HexEvalSounds.MISHAP,
)
}
// TODO there used to be error checking code here; I'm pretty sure any and all mishaps should already
// get caught and folded into CastResult by evaluate.
val result = next.evaluate(continuation.next, world, this)
// Then write all pertinent data back to the harness for the next iteration.
if (result.newData != null) {
this.applyFunctionalData(result.newData)
@ -172,11 +138,35 @@ class CastingHarness private constructor(
)
}
/**
* this DOES NOT THROW THINGS
*/
@Throws()
fun getUpdate(iota: Iota, world: ServerLevel, continuation: SpellContinuation): CastResult {
try {
// TODO we can have a special intro/retro sound
this.handleParentheses(iota)?.let { (data, resolutionType) ->
return@getUpdate CastResult(continuation, data, resolutionType, listOf(), HexEvalSounds.OPERATOR)
// ALSO TODO need to add reader macro-style things
try {
this.handleParentheses(iota)?.let { (data, resolutionType) ->
return@getUpdate CastResult(continuation, data, listOf(), resolutionType, HexEvalSounds.OPERATOR)
}
} catch (e: MishapTooManyCloseParens) {
// This is ridiculous and needs to be fixed
return CastResult(
continuation,
null,
listOf(
OperatorSideEffect.DoMishap(
e,
Mishap.Context(
(iota as? PatternIota)?.pattern ?: HexPattern(HexDir.WEST),
HexAPI.instance().getRawHookI18n(HexAPI.modLoc("close_paren"))
)
)
),
ResolvedPatternType.ERRORED,
HexEvalSounds.MISHAP
)
}
if (iota is PatternIota) {
@ -185,58 +175,32 @@ class CastingHarness private constructor(
return CastResult(
continuation,
null,
ResolvedPatternType.INVALID, // Should never matter
listOf(
OperatorSideEffect.DoMishap(
MishapUnescapedValue(iota),
Mishap.Context(HexPattern(HexDir.WEST), null)
)
),
), // Should never matter
ResolvedPatternType.INVALID,
HexEvalSounds.MISHAP
)
}
} catch (mishap: Mishap) {
val operator = try {
getOperatorForPattern(iota, world)
} catch (e: Throwable) {
null
}
return CastResult(
continuation,
null,
mishap.resolutionType(ctx),
listOf(
OperatorSideEffect.DoMishap(
mishap,
Mishap.Context(
(iota as? PatternIota)?.pattern ?: HexPattern(HexDir.WEST),
operator
)
)
),
HexEvalSounds.MISHAP
)
} catch (exception: Exception) {
// This means something very bad has happened
exception.printStackTrace()
val operator = try {
getOperatorForPattern(iota, world)
} catch (e: Throwable) {
null
}
return CastResult(
continuation,
null,
ResolvedPatternType.ERRORED,
listOf(
OperatorSideEffect.DoMishap(
MishapError(exception),
Mishap.Context(
(iota as? PatternIota)?.pattern ?: HexPattern(HexDir.WEST),
operator
null
)
)
),
ResolvedPatternType.ERRORED,
HexEvalSounds.MISHAP
)
}
@ -246,33 +210,48 @@ class CastingHarness private constructor(
* When the server gets a packet from the client with a new pattern,
* handle it functionally.
*/
fun updateWithPattern(newPat: HexPattern, world: ServerLevel, continuation: SpellContinuation): CastResult {
var actionIdPair: Pair<Action, ResourceLocation>? = null
private fun updateWithPattern(newPat: HexPattern, world: ServerLevel, continuation: SpellContinuation): CastResult {
var castedName: Component? = null
try {
// Don't catch this one
val mojangPair = PatternRegistry.matchPatternAndID(newPat, world)
actionIdPair = mojangPair.first to mojangPair.second
val lookup = PatternRegistryManifest.matchPattern(newPat, world, false)
val lookupResult: Either<Action, List<OperatorSideEffect>> = if (lookup is Normal || lookup is PerWorld) {
val key = when (lookup) {
is Normal -> lookup.key
is PerWorld -> lookup.key
else -> throw IllegalStateException()
}
if (this.ctx.spellCircle == null && !HexConfig.server().isActionAllowed(actionIdPair.second)) {
throw MishapDisallowedSpell()
} else if (this.ctx.spellCircle != null
&& !HexConfig.server().isActionAllowedInCircles(actionIdPair.second)
) {
throw MishapDisallowedSpell("disallowed_circle")
val reqsEnlightenment = isOfTag(IXplatAbstractions.INSTANCE.actionRegistry, key, HexTags.Actions.REQUIRES_ENLIGHTENMENT)
val canEnlighten = isOfTag(IXplatAbstractions.INSTANCE.actionRegistry, key, HexTags.Actions.CAN_START_ENLIGHTEN)
castedName = HexAPI.instance().getActionI18n(key, reqsEnlightenment)
if (!ctx.isCasterEnlightened && reqsEnlightenment) {
Either.right(listOf(OperatorSideEffect.RequiredEnlightenment(canEnlighten)))
} else {
val regiEntry = IXplatAbstractions.INSTANCE.actionRegistry.get(key)!!
Either.left(regiEntry.action)
}
} else if (lookup is Special) {
castedName = lookup.handler.name
Either.left(lookup.handler.act())
} else if (lookup is PatternShapeMatch.Nothing) {
throw MishapInvalidPattern()
} else {
throw IllegalStateException()
}
val pattern = actionIdPair.first
val unenlightened = pattern.isGreat && !ctx.isCasterEnlightened
// TODO: the config denylist should be handled per VM type.
// I just removed it for now, should re-add it...
val sideEffects = mutableListOf<OperatorSideEffect>()
var stack2: List<Iota>? = null
var cont2 = continuation
var ravenmind2: Iota? = null
if (!unenlightened || pattern.alwaysProcessGreatSpell) {
displayPattern(false, 0, pattern.displayName)
val result = pattern.operate(
if (lookupResult.left().isPresent) {
val action = lookupResult.left().get()
displayPatternDebug(false, 0, castedName)
val result = action.operate(
continuation,
this.stack.toMutableList(),
this.ravenmind,
@ -283,13 +262,13 @@ class CastingHarness private constructor(
ravenmind2 = result.newRavenmind
// TODO parens also break prescience
sideEffects.addAll(result.sideEffects)
}
if (unenlightened) {
sideEffects.add(OperatorSideEffect.RequiredEnlightenment(pattern.causesBlindDiversion))
} else {
val problems = lookupResult.right().get()
sideEffects.addAll(problems)
}
// Stick a poofy particle effect at the caster position
// TODO again this should be on the VM lalala
if (this.ctx.spellCircle == null)
sideEffects.add(
OperatorSideEffect.Particles(
@ -311,7 +290,8 @@ class CastingHarness private constructor(
hereFd
}
var soundType = if (this.ctx.source == CastingContext.CastSource.STAFF) {
// TODO again this should be per VM
var soundType = if (this.ctx.source == CastingEnvironment.CastSource.STAFF) {
HexEvalSounds.OPERATOR
} else {
HexEvalSounds.NOTHING
@ -332,8 +312,8 @@ class CastingHarness private constructor(
return CastResult(
cont2,
fd,
ResolvedPatternType.EVALUATED,
sideEffects,
ResolvedPatternType.EVALUATED,
soundType,
)
@ -341,8 +321,8 @@ class CastingHarness private constructor(
return CastResult(
continuation,
null,
listOf(OperatorSideEffect.DoMishap(mishap, Mishap.Context(newPat, castedName))),
mishap.resolutionType(ctx),
listOf(OperatorSideEffect.DoMishap(mishap, Mishap.Context(newPat, actionIdPair?.first))),
HexEvalSounds.MISHAP
)
}
@ -394,6 +374,7 @@ class CastingHarness private constructor(
* Return a non-null value if we handled this in some sort of parenthesey way,
* either escaping it onto the stack or changing the parenthese-handling state.
*/
@Throws(MishapTooManyCloseParens::class)
private fun handleParentheses(iota: Iota): Pair<FunctionalData, ResolvedPatternType>? {
val sig = (iota as? PatternIota)?.pattern?.anglesSignature()
@ -491,14 +472,17 @@ class CastingHarness private constructor(
}
}
// TODO: replace this once we can read things from the client
/*
if (out != null) {
val display = if (iota is PatternIota) {
PatternNameHelper.representationForPattern(iota.pattern)
.copy()
.withStyle(if (out.second == ResolvedPatternType.ESCAPED) ChatFormatting.YELLOW else ChatFormatting.AQUA)
} else iota.display()
displayPattern(this.escapeNext, displayDepth, display)
displayPatternDebug(this.escapeNext, displayDepth, display)
}
*/
return out
}
@ -639,7 +623,7 @@ class CastingHarness private constructor(
}
@JvmStatic
fun fromNBT(nbt: CompoundTag, ctx: CastingContext): CastingHarness {
fun fromNBT(nbt: CompoundTag, ctx: CastingEnvironment): CastingHarness {
return try {
val stack = mutableListOf<Iota>()
val stackTag = nbt.getList(TAG_STACK, Tag.TAG_COMPOUND)
@ -678,12 +662,4 @@ class CastingHarness private constructor(
data class TempControllerInfo(
var earlyExit: Boolean,
)
data class CastResult(
val continuation: SpellContinuation,
val newData: FunctionalData?,
val resolutionType: ResolvedPatternType,
val sideEffects: List<OperatorSideEffect>,
val sound: EvalSound,
)
}

View file

@ -1,4 +1,4 @@
package at.petrak.hexcasting.api.spell.casting
package at.petrak.hexcasting.api.casting.eval
import net.minecraft.nbt.CompoundTag

View file

@ -1,7 +1,7 @@
package at.petrak.hexcasting.api.spell.casting
package at.petrak.hexcasting.api.casting.eval
import at.petrak.hexcasting.api.spell.math.HexCoord
import at.petrak.hexcasting.api.spell.math.HexPattern
import at.petrak.hexcasting.api.casting.math.HexCoord
import at.petrak.hexcasting.api.casting.math.HexPattern
import at.petrak.hexcasting.api.utils.NBTBuilder
import net.minecraft.nbt.CompoundTag
import java.util.*

View file

@ -1,4 +1,4 @@
package at.petrak.hexcasting.api.spell.casting
package at.petrak.hexcasting.api.casting.eval
import at.petrak.hexcasting.api.utils.getSafe

View file

@ -0,0 +1,10 @@
package at.petrak.hexcasting.api.casting.eval;
import at.petrak.hexcasting.api.casting.math.HexDir;
import at.petrak.hexcasting.api.casting.math.HexPattern;
public final class SpecialPatterns {
public static final HexPattern INTROSPECTION = HexPattern.fromAngles("qqq", HexDir.WEST);
public static final HexPattern RETROSPECTION = HexPattern.fromAngles("eee", HexDir.EAST);
public static final HexPattern CONSIDERATION = HexPattern.fromAngles("qqqaw", HexDir.WEST);
}

View file

@ -1,4 +1,4 @@
package at.petrak.hexcasting.api.spell.casting
package at.petrak.hexcasting.api.casting.eval
import at.petrak.hexcasting.api.utils.NBTBuilder
import net.minecraft.core.BlockPos
@ -6,7 +6,7 @@ import net.minecraft.nbt.CompoundTag
import net.minecraft.world.phys.AABB
/**
* Optional field on a [CastingContext] for the spell circle
* Optional field on a [CastingEnvironment] for the spell circle
*/
data class SpellCircleContext(val impetusPos: BlockPos, val aabb: AABB, val activatorAlwaysInRange: Boolean) {
fun serializeToNBT() = NBTBuilder {

View file

@ -1,4 +1,4 @@
package at.petrak.hexcasting.api.spell.casting.sideeffects;
package at.petrak.hexcasting.api.casting.eval.sideeffects;
import net.minecraft.sounds.SoundEvent;
import org.jetbrains.annotations.Nullable;

View file

@ -1,13 +1,13 @@
package at.petrak.hexcasting.api.spell.casting.sideeffects
package at.petrak.hexcasting.api.casting.eval.sideeffects
import at.petrak.hexcasting.api.advancements.HexAdvancementTriggers
import at.petrak.hexcasting.api.block.circle.BlockEntityAbstractImpetus
import at.petrak.hexcasting.api.casting.ParticleSpray
import at.petrak.hexcasting.api.casting.RenderedSpell
import at.petrak.hexcasting.api.casting.eval.CastingHarness
import at.petrak.hexcasting.api.casting.mishaps.Mishap
import at.petrak.hexcasting.api.misc.FrozenColorizer
import at.petrak.hexcasting.api.mod.HexStatistics
import at.petrak.hexcasting.api.spell.ParticleSpray
import at.petrak.hexcasting.api.spell.RenderedSpell
import at.petrak.hexcasting.api.spell.casting.CastingHarness
import at.petrak.hexcasting.api.spell.mishaps.Mishap
import at.petrak.hexcasting.api.utils.asTranslatedComponent
import at.petrak.hexcasting.common.lib.HexItems
import net.minecraft.Util
@ -69,7 +69,7 @@ sealed class OperatorSideEffect {
data class DoMishap(val mishap: Mishap, val errorCtx: Mishap.Context) : OperatorSideEffect() {
override fun performEffect(harness: CastingHarness): Boolean {
val msg = mishap.errorMessage(harness.ctx, errorCtx);
val msg = mishap.errorMessageWithName(harness.ctx, errorCtx);
if (harness.ctx.spellCircle != null) {
val tile = harness.ctx.world.getBlockEntity(harness.ctx.spellCircle.impetusPos)
if (tile is BlockEntityAbstractImpetus) {

View file

@ -1,9 +1,9 @@
package at.petrak.hexcasting.api.spell.casting.eval
package at.petrak.hexcasting.api.casting.eval.vm
import at.petrak.hexcasting.api.spell.SpellList
import at.petrak.hexcasting.api.spell.casting.CastingHarness
import at.petrak.hexcasting.api.spell.casting.CastingHarness.CastResult
import at.petrak.hexcasting.api.spell.iota.Iota
import at.petrak.hexcasting.api.casting.SpellList
import at.petrak.hexcasting.api.casting.eval.CastingHarness
import at.petrak.hexcasting.api.casting.eval.CastResult
import at.petrak.hexcasting.api.casting.iota.Iota
import at.petrak.hexcasting.api.utils.getList
import at.petrak.hexcasting.api.utils.hasList
import at.petrak.hexcasting.common.lib.hex.HexIotaTypes

View file

@ -1,9 +1,10 @@
package at.petrak.hexcasting.api.spell.casting.eval
package at.petrak.hexcasting.api.casting.eval.vm
import at.petrak.hexcasting.api.spell.SpellList
import at.petrak.hexcasting.api.spell.casting.CastingHarness
import at.petrak.hexcasting.api.spell.casting.ResolvedPatternType
import at.petrak.hexcasting.api.spell.iota.Iota
import at.petrak.hexcasting.api.casting.SpellList
import at.petrak.hexcasting.api.casting.eval.CastResult
import at.petrak.hexcasting.api.casting.eval.CastingHarness
import at.petrak.hexcasting.api.casting.eval.ResolvedPatternType
import at.petrak.hexcasting.api.casting.iota.Iota
import at.petrak.hexcasting.api.utils.NBTBuilder
import at.petrak.hexcasting.api.utils.serializeToNBT
import at.petrak.hexcasting.common.lib.hex.HexEvalSounds
@ -23,7 +24,7 @@ data class FrameEvaluate(val list: SpellList, val isMetacasting: Boolean) : Cont
continuation: SpellContinuation,
level: ServerLevel,
harness: CastingHarness
): CastingHarness.CastResult {
): CastResult {
// If there are patterns left...
return if (list.nonEmpty) {
val newCont = if (list.cdr.nonEmpty) { // yay TCO
@ -39,7 +40,7 @@ data class FrameEvaluate(val list: SpellList, val isMetacasting: Boolean) : Cont
}
} else {
// If there are no patterns (e.g. empty Hermes), just return OK.
CastingHarness.CastResult(continuation, null, ResolvedPatternType.EVALUATED, listOf(), HexEvalSounds.HERMES)
CastResult(continuation, null, listOf(), ResolvedPatternType.EVALUATED, HexEvalSounds.HERMES)
}
}

View file

@ -1,8 +1,9 @@
package at.petrak.hexcasting.api.spell.casting.eval
package at.petrak.hexcasting.api.casting.eval.vm
import at.petrak.hexcasting.api.spell.casting.CastingHarness
import at.petrak.hexcasting.api.spell.casting.ResolvedPatternType
import at.petrak.hexcasting.api.spell.iota.Iota
import at.petrak.hexcasting.api.casting.eval.CastResult
import at.petrak.hexcasting.api.casting.eval.CastingHarness
import at.petrak.hexcasting.api.casting.eval.ResolvedPatternType
import at.petrak.hexcasting.api.casting.iota.Iota
import at.petrak.hexcasting.api.utils.NBTBuilder
import at.petrak.hexcasting.common.lib.hex.HexEvalSounds
import net.minecraft.server.level.ServerLevel
@ -20,12 +21,12 @@ object FrameFinishEval : ContinuationFrame {
continuation: SpellContinuation,
level: ServerLevel,
harness: CastingHarness
): CastingHarness.CastResult {
return CastingHarness.CastResult(
): CastResult {
return CastResult(
continuation,
FunctionalData(harness.stack.toList(), 0, listOf(), false, harness.ravenmind),
ResolvedPatternType.EVALUATED,
listOf(),
ResolvedPatternType.EVALUATED,
HexEvalSounds.NOTHING,
)
}

View file

@ -1,10 +1,11 @@
package at.petrak.hexcasting.api.spell.casting.eval
package at.petrak.hexcasting.api.casting.eval.vm
import at.petrak.hexcasting.api.spell.SpellList
import at.petrak.hexcasting.api.spell.casting.CastingHarness
import at.petrak.hexcasting.api.spell.casting.ResolvedPatternType
import at.petrak.hexcasting.api.spell.iota.Iota
import at.petrak.hexcasting.api.spell.iota.ListIota
import at.petrak.hexcasting.api.casting.SpellList
import at.petrak.hexcasting.api.casting.eval.CastResult
import at.petrak.hexcasting.api.casting.eval.CastingHarness
import at.petrak.hexcasting.api.casting.eval.ResolvedPatternType
import at.petrak.hexcasting.api.casting.iota.Iota
import at.petrak.hexcasting.api.casting.iota.ListIota
import at.petrak.hexcasting.api.utils.NBTBuilder
import at.petrak.hexcasting.api.utils.serializeToNBT
import at.petrak.hexcasting.common.lib.hex.HexEvalSounds
@ -39,7 +40,7 @@ data class FrameForEach(
continuation: SpellContinuation,
level: ServerLevel,
harness: CastingHarness
): CastingHarness.CastResult {
): CastResult {
// If this isn't the very first Thoth step (i.e. no Thoth computations run yet)...
val stack = if (baseStack == null) {
// init stack to the harness stack...
@ -67,11 +68,11 @@ data class FrameForEach(
val tStack = stack.toMutableList()
tStack.add(stackTop)
// TODO: this means we could have Thoth casting do a different sound
return CastingHarness.CastResult(
return CastResult(
newCont,
FunctionalData(tStack, 0, listOf(), false, harness.ravenmind),
ResolvedPatternType.EVALUATED,
listOf(),
ResolvedPatternType.EVALUATED,
HexEvalSounds.THOTH,
)
}

View file

@ -1,6 +1,6 @@
package at.petrak.hexcasting.api.spell.casting.eval
package at.petrak.hexcasting.api.casting.eval.vm
import at.petrak.hexcasting.api.spell.iota.Iota
import at.petrak.hexcasting.api.casting.iota.Iota
/**
* A change to the data in a CastHarness after a pattern is drawn.

View file

@ -1,4 +1,4 @@
package at.petrak.hexcasting.api.spell.casting.eval
package at.petrak.hexcasting.api.casting.eval.vm
/**
* A continuation during the execution of a spell.

View file

@ -1,4 +1,4 @@
package at.petrak.hexcasting.api.spell.iota;
package at.petrak.hexcasting.api.casting.iota;
import at.petrak.hexcasting.api.utils.HexUtils;
import at.petrak.hexcasting.common.lib.hex.HexIotaTypes;

View file

@ -1,4 +1,4 @@
package at.petrak.hexcasting.api.spell.iota;
package at.petrak.hexcasting.api.casting.iota;
import at.petrak.hexcasting.api.utils.HexUtils;
import at.petrak.hexcasting.common.lib.hex.HexIotaTypes;

View file

@ -1,4 +1,4 @@
package at.petrak.hexcasting.api.spell.iota;
package at.petrak.hexcasting.api.casting.iota;
import at.petrak.hexcasting.api.utils.HexUtils;
import at.petrak.hexcasting.common.lib.hex.HexIotaTypes;

View file

@ -1,4 +1,4 @@
package at.petrak.hexcasting.api.spell.iota;
package at.petrak.hexcasting.api.casting.iota;
import at.petrak.hexcasting.common.lib.hex.HexIotaTypes;
import net.minecraft.ChatFormatting;

View file

@ -1,4 +1,4 @@
package at.petrak.hexcasting.api.spell.iota;
package at.petrak.hexcasting.api.casting.iota;
import at.petrak.hexcasting.common.lib.hex.HexIotaTypes;
import net.minecraft.nbt.Tag;

View file

@ -1,4 +1,4 @@
package at.petrak.hexcasting.api.spell.iota;
package at.petrak.hexcasting.api.casting.iota;
import at.petrak.hexcasting.common.lib.hex.HexIotaTypes;
import net.minecraft.nbt.Tag;

View file

@ -1,6 +1,6 @@
package at.petrak.hexcasting.api.spell.iota;
package at.petrak.hexcasting.api.casting.iota;
import at.petrak.hexcasting.api.spell.SpellList;
import at.petrak.hexcasting.api.casting.SpellList;
import at.petrak.hexcasting.api.utils.HexUtils;
import at.petrak.hexcasting.common.lib.hex.HexIotaTypes;
import net.minecraft.ChatFormatting;

View file

@ -1,4 +1,4 @@
package at.petrak.hexcasting.api.spell.iota;
package at.petrak.hexcasting.api.casting.iota;
import at.petrak.hexcasting.common.lib.hex.HexIotaTypes;
import net.minecraft.ChatFormatting;

View file

@ -1,6 +1,6 @@
package at.petrak.hexcasting.api.spell.iota;
package at.petrak.hexcasting.api.casting.iota;
import at.petrak.hexcasting.api.spell.math.HexPattern;
import at.petrak.hexcasting.api.casting.math.HexPattern;
import at.petrak.hexcasting.api.utils.HexUtils;
import at.petrak.hexcasting.common.lib.hex.HexIotaTypes;
import net.minecraft.ChatFormatting;

View file

@ -1,4 +1,4 @@
package at.petrak.hexcasting.api.spell.iota;
package at.petrak.hexcasting.api.casting.iota;
import at.petrak.hexcasting.api.utils.HexUtils;
import at.petrak.hexcasting.common.lib.hex.HexIotaTypes;

View file

@ -1,4 +1,4 @@
package at.petrak.hexcasting.api.spell.math
package at.petrak.hexcasting.api.casting.math
import at.petrak.hexcasting.api.HexAPI
import java.util.*

View file

@ -1,4 +1,4 @@
package at.petrak.hexcasting.api.spell.math
package at.petrak.hexcasting.api.casting.math
enum class HexAngle {
FORWARD, RIGHT, RIGHT_BACK, BACK, LEFT_BACK, LEFT;

View file

@ -1,4 +1,4 @@
package at.petrak.hexcasting.api.spell.math
package at.petrak.hexcasting.api.casting.math
import kotlin.math.abs
import kotlin.math.max

View file

@ -1,4 +1,4 @@
package at.petrak.hexcasting.api.spell.math
package at.petrak.hexcasting.api.casting.math
import at.petrak.hexcasting.api.utils.getSafe

View file

@ -1,4 +1,4 @@
package at.petrak.hexcasting.api.spell.math
package at.petrak.hexcasting.api.casting.math
import at.petrak.hexcasting.api.utils.NBTBuilder
import at.petrak.hexcasting.api.utils.coordToPx
@ -129,10 +129,8 @@ data class HexPattern(public val startDir: HexDir, public val angles: MutableLis
@JvmStatic
fun isPattern(tag: CompoundTag): Boolean {
return tag.contains(TAG_START_DIR, Tag.TAG_ANY_NUMERIC.toInt()) && tag.contains(
TAG_ANGLES,
Tag.TAG_BYTE_ARRAY.toInt()
)
return tag.contains(TAG_START_DIR, Tag.TAG_ANY_NUMERIC.toInt())
&& tag.contains(TAG_ANGLES, Tag.TAG_BYTE_ARRAY.toInt())
}
@JvmStatic

View file

@ -1,13 +1,12 @@
package at.petrak.hexcasting.api.spell.mishaps
package at.petrak.hexcasting.api.casting.mishaps
import at.petrak.hexcasting.api.casting.ParticleSpray
import at.petrak.hexcasting.api.casting.eval.CastingEnvironment
import at.petrak.hexcasting.api.casting.eval.ResolvedPatternType
import at.petrak.hexcasting.api.casting.iota.Iota
import at.petrak.hexcasting.api.casting.math.HexPattern
import at.petrak.hexcasting.api.misc.FrozenColorizer
import at.petrak.hexcasting.api.mod.HexTags
import at.petrak.hexcasting.api.spell.Action
import at.petrak.hexcasting.api.spell.ParticleSpray
import at.petrak.hexcasting.api.spell.casting.CastingContext
import at.petrak.hexcasting.api.spell.casting.ResolvedPatternType
import at.petrak.hexcasting.api.spell.iota.Iota
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
@ -26,22 +25,33 @@ import net.minecraft.world.phys.Vec3
abstract class Mishap : Throwable() {
/** Mishaps spray half-red, half-this-color. */
abstract fun accentColor(ctx: CastingContext, errorCtx: Context): FrozenColorizer
abstract fun accentColor(ctx: CastingEnvironment, errorCtx: Context): FrozenColorizer
open fun particleSpray(ctx: CastingContext): ParticleSpray {
open fun particleSpray(ctx: CastingEnvironment): ParticleSpray {
return ParticleSpray(ctx.position.add(0.0, 0.2, 0.0), Vec3(0.0, 2.0, 0.0), 0.2, Math.PI / 4, 40)
}
open fun resolutionType(ctx: CastingContext): ResolvedPatternType = ResolvedPatternType.ERRORED
open fun resolutionType(ctx: CastingEnvironment): ResolvedPatternType = ResolvedPatternType.ERRORED
/**
* Execute the actual effect, not any sfx.
*
* You can also mess up the stack with this.
*/
abstract fun execute(ctx: CastingContext, errorCtx: Context, stack: MutableList<Iota>)
abstract fun execute(ctx: CastingEnvironment, errorCtx: Context, stack: MutableList<Iota>)
abstract fun errorMessage(ctx: CastingContext, errorCtx: Context): Component
abstract protected fun errorMessage(ctx: CastingEnvironment, errorCtx: Context): Component
/**
* Every error message should be prefixed with the name of the action...
*/
public fun errorMessageWithName(ctx: CastingEnvironment, errorCtx: Context): Component {
return if (errorCtx.name != null) {
"hexcasting.mishap".asTranslatedComponent(errorCtx.name, this.errorMessage(ctx, errorCtx))
} else {
this.errorMessage(ctx, errorCtx)
}
}
// Useful helper functions
@ -54,10 +64,10 @@ abstract class Mishap : Throwable() {
protected fun error(stub: String, vararg args: Any): Component =
"hexcasting.mishap.$stub".asTranslatedComponent(*args)
protected fun actionName(action: Action?): Component =
action?.displayName ?: "hexcasting.spell.null".asTranslatedComponent.lightPurple
protected fun actionName(name: Component?): Component =
name ?: "hexcasting.spell.null".asTranslatedComponent.lightPurple
protected fun yeetHeldItemsTowards(ctx: CastingContext, targetPos: Vec3) {
protected fun yeetHeldItemsTowards(ctx: CastingEnvironment, targetPos: Vec3) {
// Knock the player's items out of their hands
val items = mutableListOf<ItemStack>()
for (hand in InteractionHand.values()) {
@ -74,7 +84,7 @@ abstract class Mishap : Throwable() {
}
}
protected fun yeetHeldItem(ctx: CastingContext, hand: InteractionHand) {
protected fun yeetHeldItem(ctx: CastingEnvironment, hand: InteractionHand) {
val item = ctx.caster.getItemInHand(hand).copy()
if (hand == ctx.castingHand && IXplatAbstractions.INSTANCE.findHexHolder(item) != null)
return
@ -84,7 +94,7 @@ abstract class Mishap : Throwable() {
yeetItem(item, ctx, delta)
}
protected fun yeetItem(stack: ItemStack, ctx: CastingContext, delta: Vec3) {
protected fun yeetItem(stack: ItemStack, ctx: CastingEnvironment, delta: Vec3) {
val entity = ItemEntity(
ctx.world,
ctx.position.x, ctx.position.y, ctx.position.z,
@ -97,11 +107,11 @@ abstract class Mishap : Throwable() {
ctx.world.addWithUUID(entity)
}
protected fun blockAtPos(ctx: CastingContext, pos: BlockPos): Component {
protected fun blockAtPos(ctx: CastingEnvironment, pos: BlockPos): Component {
return ctx.world.getBlockState(pos).block.name
}
data class Context(val pattern: HexPattern, val action: Action?)
data class Context(val pattern: HexPattern, val name: Component?)
companion object {
fun trulyHurt(entity: LivingEntity, source: DamageSource, amount: Float) {

View file

@ -0,0 +1,25 @@
package at.petrak.hexcasting.api.casting.mishaps
import at.petrak.hexcasting.api.misc.FrozenColorizer
import at.petrak.hexcasting.api.misc.HexDamageSources
import at.petrak.hexcasting.api.casting.ParticleSpray
import at.petrak.hexcasting.api.casting.eval.CastingEnvironment
import at.petrak.hexcasting.api.casting.iota.Iota
import net.minecraft.world.entity.Mob
import net.minecraft.world.item.DyeColor
class MishapAlreadyBrainswept(val mob: Mob) : Mishap() {
override fun accentColor(ctx: CastingEnvironment, errorCtx: Context): FrozenColorizer =
dyeColor(DyeColor.GREEN)
override fun execute(ctx: CastingEnvironment, errorCtx: Context, stack: MutableList<Iota>) {
mob.hurt(HexDamageSources.overcastDamageFrom(ctx.caster), mob.health)
}
override fun particleSpray(ctx: CastingEnvironment) =
ParticleSpray.burst(mob.eyePosition, 1.0)
override fun errorMessage(ctx: CastingEnvironment, errorCtx: Context) =
error("already_brainswept")
}

View file

@ -1,9 +1,9 @@
package at.petrak.hexcasting.api.spell.mishaps
package at.petrak.hexcasting.api.casting.mishaps
import at.petrak.hexcasting.api.misc.FrozenColorizer
import at.petrak.hexcasting.api.spell.ParticleSpray
import at.petrak.hexcasting.api.spell.iota.Iota
import at.petrak.hexcasting.api.spell.casting.CastingContext
import at.petrak.hexcasting.api.casting.ParticleSpray
import at.petrak.hexcasting.api.casting.iota.Iota
import at.petrak.hexcasting.api.casting.eval.CastingEnvironment
import at.petrak.hexcasting.api.utils.asTranslatedComponent
import net.minecraft.core.BlockPos
import net.minecraft.network.chat.Component
@ -12,17 +12,17 @@ import net.minecraft.world.level.Explosion
import net.minecraft.world.phys.Vec3
class MishapBadBlock(val pos: BlockPos, val expected: Component) : Mishap() {
override fun accentColor(ctx: CastingContext, errorCtx: Context): FrozenColorizer =
override fun accentColor(ctx: CastingEnvironment, errorCtx: Context): FrozenColorizer =
dyeColor(DyeColor.LIME)
override fun execute(ctx: CastingContext, errorCtx: Context, stack: MutableList<Iota>) {
override fun execute(ctx: CastingEnvironment, errorCtx: Context, stack: MutableList<Iota>) {
ctx.world.explode(null, pos.x + 0.5, pos.y + 0.5, pos.z + 0.5, 0.25f, Explosion.BlockInteraction.NONE)
}
override fun particleSpray(ctx: CastingContext) =
override fun particleSpray(ctx: CastingEnvironment) =
ParticleSpray.burst(Vec3.atCenterOf(pos), 1.0)
override fun errorMessage(ctx: CastingContext, errorCtx: Context) =
override fun errorMessage(ctx: CastingEnvironment, errorCtx: Context) =
error("bad_block", expected, this.pos.toShortString(), blockAtPos(ctx, this.pos))
companion object {

View file

@ -0,0 +1,27 @@
package at.petrak.hexcasting.api.casting.mishaps
import at.petrak.hexcasting.api.misc.FrozenColorizer
import at.petrak.hexcasting.api.misc.HexDamageSources
import at.petrak.hexcasting.api.casting.ParticleSpray
import at.petrak.hexcasting.api.casting.eval.CastingEnvironment
import at.petrak.hexcasting.api.casting.iota.Iota
import net.minecraft.core.BlockPos
import net.minecraft.world.entity.Mob
import net.minecraft.world.item.DyeColor
import net.minecraft.world.phys.Vec3
class MishapBadBrainsweep(val mob: Mob, val pos: BlockPos) : Mishap() {
override fun accentColor(ctx: CastingEnvironment, errorCtx: Context): FrozenColorizer =
dyeColor(DyeColor.GREEN)
override fun execute(ctx: CastingEnvironment, errorCtx: Context, stack: MutableList<Iota>) {
trulyHurt(mob, HexDamageSources.overcastDamageFrom(ctx.caster), mob.health)
}
override fun particleSpray(ctx: CastingEnvironment): ParticleSpray {
return ParticleSpray.burst(Vec3.atCenterOf(pos), 1.0)
}
override fun errorMessage(ctx: CastingEnvironment, errorCtx: Context) =
error("bad_brainsweep", blockAtPos(ctx, this.pos))
}

View file

@ -1,8 +1,8 @@
package at.petrak.hexcasting.api.spell.mishaps
package at.petrak.hexcasting.api.casting.mishaps
import at.petrak.hexcasting.api.casting.eval.CastingEnvironment
import at.petrak.hexcasting.api.casting.iota.Iota
import at.petrak.hexcasting.api.misc.FrozenColorizer
import at.petrak.hexcasting.api.spell.casting.CastingContext
import at.petrak.hexcasting.api.spell.iota.Iota
import at.petrak.hexcasting.api.utils.aqua
import at.petrak.hexcasting.api.utils.asTranslatedComponent
import net.minecraft.network.chat.Component
@ -11,15 +11,15 @@ 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 =
override fun accentColor(ctx: CastingEnvironment, errorCtx: Context): FrozenColorizer =
dyeColor(DyeColor.BROWN)
override fun execute(ctx: CastingContext, errorCtx: Context, stack: MutableList<Iota>) {
override fun execute(ctx: CastingEnvironment, errorCtx: Context, stack: MutableList<Iota>) {
yeetHeldItemsTowards(ctx, entity.position())
}
override fun errorMessage(ctx: CastingContext, errorCtx: Context) =
error("bad_entity", actionName(errorCtx.action), wanted, entity.displayName.plainCopy().aqua)
override fun errorMessage(ctx: CastingEnvironment, errorCtx: Context) =
error("bad_entity", wanted, entity.displayName.plainCopy().aqua)
companion object {
@JvmStatic

View file

@ -1,25 +1,25 @@
package at.petrak.hexcasting.api.spell.mishaps
package at.petrak.hexcasting.api.casting.mishaps
import at.petrak.hexcasting.api.casting.eval.CastingEnvironment
import at.petrak.hexcasting.api.casting.iota.Iota
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.asTranslatedComponent
import net.minecraft.network.chat.Component
import net.minecraft.world.entity.item.ItemEntity
import net.minecraft.world.item.DyeColor
class MishapBadItem(val item: ItemEntity, val wanted: Component) : Mishap() {
override fun accentColor(ctx: CastingContext, errorCtx: Context): FrozenColorizer =
override fun accentColor(ctx: CastingEnvironment, errorCtx: Context): FrozenColorizer =
dyeColor(DyeColor.BROWN)
override fun execute(ctx: CastingContext, errorCtx: Context, stack: MutableList<Iota>) {
override fun execute(ctx: CastingEnvironment, errorCtx: Context, stack: MutableList<Iota>) {
item.deltaMovement = item.deltaMovement.add((Math.random() - 0.5) * 0.05, 0.75, (Math.random() - 0.5) * 0.05)
}
override fun errorMessage(ctx: CastingContext, errorCtx: Context) = if (item.item.isEmpty)
error("no_item", actionName(errorCtx.action), wanted)
override fun errorMessage(ctx: CastingEnvironment, errorCtx: Context) = if (item.item.isEmpty)
error("no_item", wanted)
else
error("bad_item", actionName(errorCtx.action), wanted, item.item.count, item.item.displayName)
error("bad_item", wanted, item.item.count, item.item.displayName)
companion object {
@JvmStatic

View file

@ -1,8 +1,8 @@
package at.petrak.hexcasting.api.spell.mishaps
package at.petrak.hexcasting.api.casting.mishaps
import at.petrak.hexcasting.api.casting.eval.CastingEnvironment
import at.petrak.hexcasting.api.casting.iota.Iota
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.asTranslatedComponent
import net.minecraft.network.chat.Component
import net.minecraft.world.InteractionHand
@ -10,17 +10,17 @@ import net.minecraft.world.item.DyeColor
import net.minecraft.world.item.ItemStack
class MishapBadOffhandItem(val item: ItemStack, val hand: InteractionHand, val wanted: Component) : Mishap() {
override fun accentColor(ctx: CastingContext, errorCtx: Context): FrozenColorizer =
override fun accentColor(ctx: CastingEnvironment, errorCtx: Context): FrozenColorizer =
dyeColor(DyeColor.BROWN)
override fun execute(ctx: CastingContext, errorCtx: Context, stack: MutableList<Iota>) {
override fun execute(ctx: CastingEnvironment, errorCtx: Context, stack: MutableList<Iota>) {
yeetHeldItem(ctx, hand)
}
override fun errorMessage(ctx: CastingContext, errorCtx: Context) = if (item.isEmpty)
error("no_item.offhand", actionName(errorCtx.action), wanted)
override fun errorMessage(ctx: CastingEnvironment, errorCtx: Context) = if (item.isEmpty)
error("no_item.offhand", wanted)
else
error("bad_item.offhand", actionName(errorCtx.action), wanted, item.count, item.displayName)
error("bad_item.offhand", wanted, item.count, item.displayName)
companion object {
@JvmStatic

View file

@ -0,0 +1,21 @@
package at.petrak.hexcasting.api.casting.mishaps
import at.petrak.hexcasting.api.casting.eval.CastingEnvironment
import at.petrak.hexcasting.api.casting.eval.ResolvedPatternType
import at.petrak.hexcasting.api.casting.iota.Iota
import at.petrak.hexcasting.api.misc.FrozenColorizer
import net.minecraft.world.item.DyeColor
class MishapDisallowedSpell(val type: String = "disallowed") : Mishap() {
override fun accentColor(ctx: CastingEnvironment, errorCtx: Context): FrozenColorizer =
dyeColor(DyeColor.BLACK)
override fun resolutionType(ctx: CastingEnvironment) = ResolvedPatternType.INVALID
override fun execute(ctx: CastingEnvironment, errorCtx: Context, stack: MutableList<Iota>) {
// NO-OP
}
override fun errorMessage(ctx: CastingEnvironment, errorCtx: Context) =
error(type)
}

View file

@ -1,12 +1,12 @@
package at.petrak.hexcasting.api.spell.mishaps
package at.petrak.hexcasting.api.casting.mishaps
import at.petrak.hexcasting.api.misc.FrozenColorizer
import at.petrak.hexcasting.api.misc.HexDamageSources
import at.petrak.hexcasting.api.spell.casting.CastingContext
import at.petrak.hexcasting.api.spell.iota.DoubleIota
import at.petrak.hexcasting.api.spell.iota.GarbageIota
import at.petrak.hexcasting.api.spell.iota.Iota
import at.petrak.hexcasting.api.spell.iota.Vec3Iota
import at.petrak.hexcasting.api.casting.eval.CastingEnvironment
import at.petrak.hexcasting.api.casting.iota.DoubleIota
import at.petrak.hexcasting.api.casting.iota.GarbageIota
import at.petrak.hexcasting.api.casting.iota.Iota
import at.petrak.hexcasting.api.casting.iota.Vec3Iota
import at.petrak.hexcasting.api.utils.asTranslatedComponent
import net.minecraft.network.chat.Component
import net.minecraft.world.item.DyeColor
@ -14,15 +14,15 @@ import net.minecraft.world.phys.Vec3
class MishapDivideByZero(val operand1: Component, val operand2: Component, val suffix: String = "divide") : Mishap() {
override fun accentColor(ctx: CastingContext, errorCtx: Context): FrozenColorizer =
override fun accentColor(ctx: CastingEnvironment, errorCtx: Context): FrozenColorizer =
dyeColor(DyeColor.RED)
override fun execute(ctx: CastingContext, errorCtx: Context, stack: MutableList<Iota>) {
override fun execute(ctx: CastingEnvironment, errorCtx: Context, stack: MutableList<Iota>) {
stack.add(GarbageIota())
trulyHurt(ctx.caster, HexDamageSources.OVERCAST, ctx.caster.health / 2)
}
override fun errorMessage(ctx: CastingContext, errorCtx: Context) =
override fun errorMessage(ctx: CastingEnvironment, errorCtx: Context) =
error("divide_by_zero.$suffix", operand1, operand2)
companion object {

View file

@ -0,0 +1,21 @@
package at.petrak.hexcasting.api.casting.mishaps
import at.petrak.hexcasting.api.casting.eval.CastingEnvironment
import at.petrak.hexcasting.api.casting.iota.Iota
import at.petrak.hexcasting.api.misc.FrozenColorizer
import net.minecraft.network.chat.Component
import net.minecraft.world.entity.Entity
import net.minecraft.world.item.DyeColor
class MishapEntityTooFarAway(val entity: Entity) : Mishap() {
override fun accentColor(ctx: CastingEnvironment, errorCtx: Context): FrozenColorizer =
dyeColor(DyeColor.PINK)
override fun execute(ctx: CastingEnvironment, errorCtx: Context, stack: MutableList<Iota>) {
// Knock the player's items out of their hands
yeetHeldItemsTowards(ctx, entity.position())
}
override fun errorMessage(ctx: CastingEnvironment, errorCtx: Context): Component =
error("entity_too_far", entity.displayName)
}

View file

@ -0,0 +1,18 @@
package at.petrak.hexcasting.api.casting.mishaps
import at.petrak.hexcasting.api.casting.eval.CastingEnvironment
import at.petrak.hexcasting.api.casting.iota.Iota
import at.petrak.hexcasting.api.misc.FrozenColorizer
import net.minecraft.world.item.DyeColor
class MishapError(val exception: Exception) : Mishap() {
override fun accentColor(ctx: CastingEnvironment, errorCtx: Context): FrozenColorizer =
dyeColor(DyeColor.BLACK)
override fun execute(ctx: CastingEnvironment, errorCtx: Context, stack: MutableList<Iota>) {
// NO-OP
}
override fun errorMessage(ctx: CastingEnvironment, errorCtx: Context) =
error("unknown", exception)
}

View file

@ -0,0 +1,18 @@
package at.petrak.hexcasting.api.casting.mishaps
import at.petrak.hexcasting.api.misc.FrozenColorizer
import at.petrak.hexcasting.api.casting.iota.Iota
import at.petrak.hexcasting.api.casting.eval.CastingEnvironment
import net.minecraft.world.item.DyeColor
class MishapEvalTooDeep : Mishap() {
override fun accentColor(ctx: CastingEnvironment, errorCtx: Context): FrozenColorizer =
dyeColor(DyeColor.BLUE)
override fun execute(ctx: CastingEnvironment, errorCtx: Context, stack: MutableList<Iota>) {
ctx.caster.airSupply -= 290
}
override fun errorMessage(ctx: CastingEnvironment, errorCtx: Context) =
error("eval_too_deep")
}

View file

@ -0,0 +1,20 @@
package at.petrak.hexcasting.api.casting.mishaps
import at.petrak.hexcasting.api.casting.eval.CastingEnvironment
import at.petrak.hexcasting.api.casting.iota.Iota
import at.petrak.hexcasting.api.misc.FrozenColorizer
import at.petrak.hexcasting.api.utils.aqua
import net.minecraft.world.entity.Entity
import net.minecraft.world.item.DyeColor
class MishapImmuneEntity(val entity: Entity) : Mishap() {
override fun accentColor(ctx: CastingEnvironment, errorCtx: Context): FrozenColorizer =
dyeColor(DyeColor.BLUE)
override fun execute(ctx: CastingEnvironment, errorCtx: Context, stack: MutableList<Iota>) {
yeetHeldItemsTowards(ctx, entity.position())
}
override fun errorMessage(ctx: CastingEnvironment, errorCtx: Context) =
error("immune_entity", entity.displayName.plainCopy().aqua)
}

View file

@ -1,9 +1,9 @@
package at.petrak.hexcasting.api.spell.mishaps
package at.petrak.hexcasting.api.casting.mishaps
import at.petrak.hexcasting.api.casting.eval.CastingEnvironment
import at.petrak.hexcasting.api.casting.iota.GarbageIota
import at.petrak.hexcasting.api.casting.iota.Iota
import at.petrak.hexcasting.api.misc.FrozenColorizer
import at.petrak.hexcasting.api.spell.casting.CastingContext
import at.petrak.hexcasting.api.spell.iota.GarbageIota
import at.petrak.hexcasting.api.spell.iota.Iota
import at.petrak.hexcasting.api.utils.asTranslatedComponent
import net.minecraft.network.chat.Component
import net.minecraft.world.item.DyeColor
@ -18,16 +18,16 @@ class MishapInvalidIota(
val reverseIdx: Int,
val expected: Component
) : Mishap() {
override fun accentColor(ctx: CastingContext, errorCtx: Context): FrozenColorizer =
override fun accentColor(ctx: CastingEnvironment, errorCtx: Context): FrozenColorizer =
dyeColor(DyeColor.GRAY)
override fun execute(ctx: CastingContext, errorCtx: Context, stack: MutableList<Iota>) {
override fun execute(ctx: CastingEnvironment, errorCtx: Context, stack: MutableList<Iota>) {
stack[stack.size - 1 - reverseIdx] = GarbageIota();
}
override fun errorMessage(ctx: CastingContext, errorCtx: Context) =
override fun errorMessage(ctx: CastingEnvironment, errorCtx: Context) =
error(
"invalid_value", actionName(errorCtx.action), expected, reverseIdx,
"invalid_value", expected, reverseIdx,
perpetrator.display()
)

View file

@ -0,0 +1,22 @@
package at.petrak.hexcasting.api.casting.mishaps
import at.petrak.hexcasting.api.misc.FrozenColorizer
import at.petrak.hexcasting.api.casting.eval.CastingEnvironment
import at.petrak.hexcasting.api.casting.eval.ResolvedPatternType
import at.petrak.hexcasting.api.casting.iota.GarbageIota
import at.petrak.hexcasting.api.casting.iota.Iota
import net.minecraft.world.item.DyeColor
class MishapInvalidPattern : Mishap() {
override fun accentColor(ctx: CastingEnvironment, errorCtx: Context): FrozenColorizer =
dyeColor(DyeColor.YELLOW)
override fun resolutionType(ctx: CastingEnvironment) = ResolvedPatternType.INVALID
override fun execute(ctx: CastingEnvironment, errorCtx: Context, stack: MutableList<Iota>) {
stack.add(GarbageIota())
}
override fun errorMessage(ctx: CastingEnvironment, errorCtx: Context) =
error("invalid_pattern")
}

View file

@ -0,0 +1,21 @@
package at.petrak.hexcasting.api.casting.mishaps
import at.petrak.hexcasting.api.misc.FrozenColorizer
import at.petrak.hexcasting.api.casting.iota.Iota
import at.petrak.hexcasting.api.casting.eval.CastingEnvironment
import net.minecraft.world.item.DyeColor
/**
* this is bad
*/
class MishapInvalidSpellDatumType(val perpetrator: Any) : Mishap() {
override fun accentColor(ctx: CastingEnvironment, errorCtx: Context): FrozenColorizer =
dyeColor(DyeColor.BLACK)
override fun execute(ctx: CastingEnvironment, errorCtx: Context, stack: MutableList<Iota>) {
// NO-OP
}
override fun errorMessage(ctx: CastingEnvironment, errorCtx: Context) =
error("invalid_spell_datum_type", this.perpetrator.toString(), this.perpetrator.javaClass.typeName)
}

View file

@ -0,0 +1,24 @@
package at.petrak.hexcasting.api.casting.mishaps
import at.petrak.hexcasting.api.casting.eval.CastingEnvironment
import at.petrak.hexcasting.api.casting.iota.GarbageIota
import at.petrak.hexcasting.api.casting.iota.Iota
import at.petrak.hexcasting.api.misc.FrozenColorizer
import net.minecraft.network.chat.Component
import net.minecraft.resources.ResourceLocation
import net.minecraft.world.item.DyeColor
class MishapLocationInWrongDimension(val properDimension: ResourceLocation) : Mishap() {
override fun accentColor(ctx: CastingEnvironment, errorCtx: Context): FrozenColorizer =
dyeColor(DyeColor.MAGENTA)
override fun execute(ctx: CastingEnvironment, errorCtx: Context, stack: MutableList<Iota>) {
stack.add(GarbageIota())
}
override fun errorMessage(ctx: CastingEnvironment, errorCtx: Context): Component =
error(
"wrong_dimension", properDimension.toString(),
ctx.world.dimension().location().toString()
)
}

View file

@ -0,0 +1,21 @@
package at.petrak.hexcasting.api.casting.mishaps
import at.petrak.hexcasting.api.casting.eval.CastingEnvironment
import at.petrak.hexcasting.api.casting.iota.Iota
import at.petrak.hexcasting.api.casting.iota.Vec3Iota
import at.petrak.hexcasting.api.misc.FrozenColorizer
import net.minecraft.network.chat.Component
import net.minecraft.world.item.DyeColor
import net.minecraft.world.phys.Vec3
class MishapLocationTooFarAway(val location: Vec3, val type: String = "too_far") : Mishap() {
override fun accentColor(ctx: CastingEnvironment, errorCtx: Context): FrozenColorizer =
dyeColor(DyeColor.MAGENTA)
override fun execute(ctx: CastingEnvironment, errorCtx: Context, stack: MutableList<Iota>) {
yeetHeldItemsTowards(ctx, location)
}
override fun errorMessage(ctx: CastingEnvironment, errorCtx: Context): Component =
error("location_$type", Vec3Iota.display(location))
}

View file

@ -0,0 +1,19 @@
package at.petrak.hexcasting.api.casting.mishaps
import at.petrak.hexcasting.api.misc.FrozenColorizer
import at.petrak.hexcasting.api.casting.iota.Iota
import at.petrak.hexcasting.api.casting.eval.CastingEnvironment
import net.minecraft.core.BlockPos
import net.minecraft.world.item.DyeColor
class MishapNoAkashicRecord(val pos: BlockPos) : Mishap() {
override fun accentColor(ctx: CastingEnvironment, errorCtx: Context): FrozenColorizer =
dyeColor(DyeColor.PURPLE)
override fun execute(ctx: CastingEnvironment, errorCtx: Context, stack: MutableList<Iota>) {
ctx.caster.giveExperiencePoints(-100)
}
override fun errorMessage(ctx: CastingEnvironment, errorCtx: Context) =
error("no_akashic_record", pos.toShortString())
}

View file

@ -1,15 +1,15 @@
package at.petrak.hexcasting.api.spell.mishaps
package at.petrak.hexcasting.api.casting.mishaps
import at.petrak.hexcasting.api.casting.eval.CastingEnvironment
import at.petrak.hexcasting.api.casting.iota.Iota
import at.petrak.hexcasting.api.misc.FrozenColorizer
import at.petrak.hexcasting.api.spell.casting.CastingContext
import at.petrak.hexcasting.api.spell.iota.Iota
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 =
override fun accentColor(ctx: CastingEnvironment, errorCtx: Context): FrozenColorizer =
dyeColor(DyeColor.LIGHT_BLUE)
private inline fun dropAll(player: Player, stacks: MutableList<ItemStack>, filter: (ItemStack) -> Boolean = { true }) {
@ -22,7 +22,7 @@ class MishapNoSpellCircle : Mishap() {
}
}
override fun execute(ctx: CastingContext, errorCtx: Context, stack: MutableList<Iota>) {
override fun execute(ctx: CastingEnvironment, errorCtx: Context, stack: MutableList<Iota>) {
dropAll(ctx.caster, ctx.caster.inventory.items)
dropAll(ctx.caster, ctx.caster.inventory.offhand)
dropAll(ctx.caster, ctx.caster.inventory.armor) {
@ -30,6 +30,6 @@ class MishapNoSpellCircle : Mishap() {
}
}
override fun errorMessage(ctx: CastingContext, errorCtx: Context) =
error("no_spell_circle", actionName(errorCtx.action))
override fun errorMessage(ctx: CastingEnvironment, errorCtx: Context) =
error("no_spell_circle")
}

View file

@ -0,0 +1,22 @@
package at.petrak.hexcasting.api.casting.mishaps
import at.petrak.hexcasting.api.casting.eval.CastingEnvironment
import at.petrak.hexcasting.api.casting.iota.GarbageIota
import at.petrak.hexcasting.api.casting.iota.Iota
import at.petrak.hexcasting.api.misc.FrozenColorizer
import net.minecraft.world.item.DyeColor
class MishapNotEnoughArgs(val expected: Int, val got: Int) : Mishap() {
override fun accentColor(ctx: CastingEnvironment, errorCtx: Context): FrozenColorizer =
dyeColor(DyeColor.LIGHT_GRAY)
override fun execute(ctx: CastingEnvironment, errorCtx: Context, stack: MutableList<Iota>) {
repeat(expected - got) { stack.add(GarbageIota()) }
}
override fun errorMessage(ctx: CastingEnvironment, errorCtx: Context) =
if (got == 0)
error("no_args", expected)
else
error("not_enough_args", expected, got)
}

View file

@ -1,10 +1,10 @@
package at.petrak.hexcasting.api.spell.mishaps
package at.petrak.hexcasting.api.casting.mishaps
import at.petrak.hexcasting.api.misc.FrozenColorizer
import at.petrak.hexcasting.api.spell.casting.CastingContext
import at.petrak.hexcasting.api.spell.iota.EntityIota
import at.petrak.hexcasting.api.spell.iota.Iota
import at.petrak.hexcasting.api.spell.iota.ListIota
import at.petrak.hexcasting.api.casting.eval.CastingEnvironment
import at.petrak.hexcasting.api.casting.iota.EntityIota
import at.petrak.hexcasting.api.casting.iota.Iota
import at.petrak.hexcasting.api.casting.iota.ListIota
import net.minecraft.world.effect.MobEffectInstance
import net.minecraft.world.effect.MobEffects
import net.minecraft.world.entity.player.Player
@ -14,15 +14,15 @@ import net.minecraft.world.item.DyeColor
* Also throwable for your *own* name, for cases like Chronicler's Gambit
*/
class MishapOthersName(val confidant: Player) : Mishap() {
override fun accentColor(ctx: CastingContext, errorCtx: Context): FrozenColorizer =
override fun accentColor(ctx: CastingEnvironment, errorCtx: Context): FrozenColorizer =
dyeColor(DyeColor.BLACK)
override fun execute(ctx: CastingContext, errorCtx: Context, stack: MutableList<Iota>) {
override fun execute(ctx: CastingEnvironment, errorCtx: Context, stack: MutableList<Iota>) {
val seconds = if (this.confidant == ctx.caster) 5 else 60;
ctx.caster.addEffect(MobEffectInstance(MobEffects.BLINDNESS, seconds * 20))
}
override fun errorMessage(ctx: CastingContext, errorCtx: Context) =
override fun errorMessage(ctx: CastingEnvironment, errorCtx: Context) =
if (this.confidant == ctx.caster)
error("others_name.self")
else

View file

@ -0,0 +1,18 @@
package at.petrak.hexcasting.api.casting.mishaps
import at.petrak.hexcasting.api.misc.FrozenColorizer
import at.petrak.hexcasting.api.misc.HexDamageSources
import at.petrak.hexcasting.api.casting.eval.CastingEnvironment
import at.petrak.hexcasting.api.casting.iota.Iota
import net.minecraft.world.item.DyeColor
class MishapShameOnYou() : Mishap() {
override fun accentColor(ctx: CastingEnvironment, errorCtx: Context): FrozenColorizer =
dyeColor(DyeColor.BLACK)
override fun execute(ctx: CastingEnvironment, errorCtx: Context, stack: MutableList<Iota>) {
Mishap.trulyHurt(ctx.caster, HexDamageSources.SHAME, 69420f)
}
override fun errorMessage(ctx: CastingEnvironment, errorCtx: Context) = error("shame")
}

View file

@ -0,0 +1,20 @@
package at.petrak.hexcasting.api.casting.mishaps
import at.petrak.hexcasting.api.misc.FrozenColorizer
import at.petrak.hexcasting.api.casting.eval.CastingEnvironment
import at.petrak.hexcasting.api.casting.iota.Iota
import at.petrak.hexcasting.api.casting.iota.PatternIota
import net.minecraft.world.item.DyeColor
class MishapTooManyCloseParens : Mishap() {
override fun accentColor(ctx: CastingEnvironment, errorCtx: Context): FrozenColorizer =
dyeColor(DyeColor.ORANGE)
override fun execute(ctx: CastingEnvironment, errorCtx: Context, stack: MutableList<Iota>) {
// TODO this is a kinda shitty mishap
stack.add(PatternIota(errorCtx.pattern))
}
override fun errorMessage(ctx: CastingEnvironment, errorCtx: Context) =
error("too_many_close_parens")
}

View file

@ -1,8 +1,8 @@
package at.petrak.hexcasting.api.spell.mishaps
package at.petrak.hexcasting.api.casting.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.casting.iota.Iota
import at.petrak.hexcasting.api.casting.eval.CastingEnvironment
import net.minecraft.world.item.DyeColor
/**
@ -11,10 +11,10 @@ import net.minecraft.world.item.DyeColor
class MishapUnescapedValue(
val perpetrator: Iota
) : Mishap() {
override fun accentColor(ctx: CastingContext, errorCtx: Context): FrozenColorizer =
override fun accentColor(ctx: CastingEnvironment, errorCtx: Context): FrozenColorizer =
dyeColor(DyeColor.GRAY)
override fun execute(ctx: CastingContext, errorCtx: Context, stack: MutableList<Iota>) {
override fun execute(ctx: CastingEnvironment, errorCtx: Context, stack: MutableList<Iota>) {
// TODO
/*
val idx = stack.indexOfLast { it.getType() == DatumType.LIST }
@ -30,6 +30,6 @@ class MishapUnescapedValue(
*/
}
override fun errorMessage(ctx: CastingContext, errorCtx: Context) =
override fun errorMessage(ctx: CastingEnvironment, errorCtx: Context) =
error("unescaped", perpetrator.display())
}

View file

@ -1,16 +1,16 @@
package at.petrak.hexcasting.api.client;
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.world.level.Level;
import net.minecraft.world.entity.player.Player;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.core.Registry;
import net.minecraft.network.chat.Component;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.block.BeehiveBlock;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.Blocks;
@ -120,7 +120,7 @@ public final class ScryingLensOverlayRegistry {
* @throws IllegalArgumentException if the block is already registered.
*/
public static void addDisplayer(Block block, OverlayBuilder displayer) {
addDisplayer(IXplatAbstractions.INSTANCE.getID(block), displayer);
addDisplayer(Registry.BLOCK.getKey(block), displayer);
}
/**
@ -152,7 +152,7 @@ public final class ScryingLensOverlayRegistry {
Player observer, Level world,
Direction hitFace) {
List<Pair<ItemStack, Component>> lines = Lists.newArrayList();
var idLookedup = ID_LOOKUP.get(IXplatAbstractions.INSTANCE.getID(state.getBlock()));
var idLookedup = ID_LOOKUP.get(Registry.BLOCK.getKey(state.getBlock()));
if (idLookedup != null) {
idLookedup.addLines(lines, state, pos, observer, world, hitFace);
}

View file

@ -1,6 +1,6 @@
package at.petrak.hexcasting.api.item;
import at.petrak.hexcasting.api.spell.iota.Iota;
import at.petrak.hexcasting.api.casting.iota.Iota;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.world.item.ItemStack;
import org.jetbrains.annotations.ApiStatus;

View file

@ -1,6 +1,6 @@
package at.petrak.hexcasting.api.item;
import at.petrak.hexcasting.api.spell.iota.Iota;
import at.petrak.hexcasting.api.casting.iota.Iota;
import at.petrak.hexcasting.api.utils.HexUtils;
import at.petrak.hexcasting.api.utils.NBTHelper;
import at.petrak.hexcasting.client.ClientTickCounter;
@ -27,7 +27,7 @@ import java.util.List;
public interface IotaHolderItem {
/**
* If this key is set on the item, we ignore the rest of the item and render this as if it were of the
* {@link at.petrak.hexcasting.api.spell.iota.IotaType IotaType} given by the resource location.
* {@link at.petrak.hexcasting.api.casting.iota.IotaType IotaType} given by the resource location.
* <p>
* This is not useful to the player at all.
*/
@ -54,7 +54,7 @@ public interface IotaHolderItem {
/**
* What is this considered to contain when nothing can be read?
* <p>
* TODO i'm not sure what this exists for
* TODO i'm not sure what this isCastable for
*/
@Nullable
default Iota emptyIota(ItemStack stack) {
@ -97,7 +97,7 @@ public interface IotaHolderItem {
void writeDatum(ItemStack stack, @Nullable Iota iota);
static void appendHoverText(IotaHolderItem self, ItemStack stack, List<Component> components,
TooltipFlag flag) {
TooltipFlag flag) {
var datumTag = self.readIotaTag(stack);
if (datumTag != null) {
var cmp = HexIotaTypes.getDisplay(datumTag);

View file

@ -1,8 +1,8 @@
package at.petrak.hexcasting.api.misc;
import at.petrak.hexcasting.api.addldata.ADMediaHolder;
import at.petrak.hexcasting.api.spell.casting.CastingContext;
import at.petrak.hexcasting.api.spell.casting.CastingHarness;
import at.petrak.hexcasting.api.casting.eval.CastingEnvironment;
import at.petrak.hexcasting.api.casting.eval.CastingHarness;
import com.google.common.collect.Lists;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.item.ItemStack;
@ -17,8 +17,8 @@ public class DiscoveryHandlers {
private static final List<Predicate<Player>> HAS_LENS_PREDICATE = new ArrayList<>();
private static final List<Function<CastingHarness, List<ADMediaHolder>>> MEDIA_HOLDER_DISCOVERY = new ArrayList<>();
private static final List<FunctionToFloat<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<Function<CastingEnvironment, List<ItemStack>>> ITEM_SLOT_DISCOVERER = new ArrayList<>();
private static final List<Function<CastingEnvironment, 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) {
@ -46,7 +46,7 @@ public class DiscoveryHandlers {
return mod;
}
public static List<ItemStack> collectItemSlots(CastingContext ctx) {
public static List<ItemStack> collectItemSlots(CastingEnvironment ctx) {
List<ItemStack> stacks = Lists.newArrayList();
for (var discoverer : ITEM_SLOT_DISCOVERER) {
stacks.addAll(discoverer.apply(ctx));
@ -54,7 +54,7 @@ public class DiscoveryHandlers {
return stacks;
}
public static List<ItemStack> collectOperableSlots(CastingContext ctx) {
public static List<ItemStack> collectOperableSlots(CastingEnvironment ctx) {
List<ItemStack> stacks = Lists.newArrayList();
for (var discoverer : OPERATIVE_SLOT_DISCOVERER) {
stacks.addAll(discoverer.apply(ctx));
@ -84,11 +84,11 @@ public class DiscoveryHandlers {
GRID_SCALE_MODIFIERS.add(modifier);
}
public static void addItemSlotDiscoverer(Function<CastingContext, List<ItemStack>> discoverer) {
public static void addItemSlotDiscoverer(Function<CastingEnvironment, List<ItemStack>> discoverer) {
ITEM_SLOT_DISCOVERER.add(discoverer);
}
public static void addOperativeSlotDiscoverer(Function<CastingContext, List<ItemStack>> discoverer) {
public static void addOperativeSlotDiscoverer(Function<CastingEnvironment, List<ItemStack>> discoverer) {
OPERATIVE_SLOT_DISCOVERER.add(discoverer);
}

View file

@ -1,24 +0,0 @@
package at.petrak.hexcasting.api.misc;
import net.minecraft.resources.ResourceLocation;
import org.jetbrains.annotations.Nullable;
import static at.petrak.hexcasting.api.HexAPI.modLoc;
public enum ScrollQuantity {
NONE(null),
FEW(modLoc("inject/scroll_loot_few")),
SOME(modLoc("inject/scroll_loot_some")),
MANY(modLoc("inject/scroll_loot_many"));
private final ResourceLocation pool;
ScrollQuantity(ResourceLocation pool) {
this.pool = pool;
}
@Nullable
public ResourceLocation getPool() {
return pool;
}
}

View file

@ -2,7 +2,6 @@ package at.petrak.hexcasting.api.mod;
import at.petrak.hexcasting.api.HexAPI;
import at.petrak.hexcasting.api.misc.MediaConstants;
import at.petrak.hexcasting.api.misc.ScrollQuantity;
import net.minecraft.resources.ResourceKey;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.item.Tier;
@ -22,11 +21,21 @@ public class HexConfig {
double mediaToHealthRate();
int cypherCooldown();
int trinketCooldown();
int artifactCooldown();
int DEFAULT_DUST_MEDIA_AMOUNT = MediaConstants.DUST_UNIT;
int DEFAULT_SHARD_MEDIA_AMOUNT = MediaConstants.SHARD_UNIT;
int DEFAULT_CHARGED_MEDIA_AMOUNT = MediaConstants.CRYSTAL_UNIT;
double DEFAULT_MEDIA_TO_HEALTH_RATE = 2 * MediaConstants.CRYSTAL_UNIT / 20.0;
int DEFAULT_CYPHER_COOLDOWN = 8;
int DEFAULT_TRINKET_COOLDOWN = 5;
int DEFAULT_ARTIFACT_COOLDOWN = 3;
}
public interface ClientConfigAccess {
@ -60,18 +69,11 @@ public class HexConfig {
// fun fact, although dimension keys are a RegistryHolder, they aren't a registry, so i can't do tags
boolean canTeleportInThisDimension(ResourceKey<Level> dimension);
ScrollQuantity scrollsForLootTable(ResourceLocation lootTable);
int DEFAULT_MAX_RECURSE_DEPTH = 512;
int DEFAULT_MAX_SPELL_CIRCLE_LENGTH = 1024;
int DEFAULT_OP_BREAK_HARVEST_LEVEL = 3;
boolean DEFAULT_VILLAGERS_DISLIKE_MIND_MURDER = true;
List<String> DEFAULT_FEW_SCROLL_TABLES = List.of("minecraft:chests/jungle_temple",
"minecraft:chests/simple_dungeon", "minecraft:chests/village/village_cartographer");
List<String> DEFAULT_SOME_SCROLL_TABLES = List.of("minecraft:chests/bastion_treasure",
"minecraft:chests/shipwreck_map");
List<String> DEFAULT_MANY_SCROLL_TABLES = List.of("minecraft:chests/stronghold_library");
List<String> DEFAULT_DIM_TP_DENYLIST = List.of("twilightforest:twilight_forest");
@ -89,11 +91,23 @@ public class HexConfig {
// Simple extensions for resource location configs
public static boolean anyMatch(List<? extends String> keys, ResourceLocation key) {
return keys.stream().map(ResourceLocation::new).anyMatch(key::equals);
for (String s : keys) {
if (ResourceLocation.isValidResourceLocation(s)) {
var rl = new ResourceLocation(s);
if (rl.equals(key)) {
return true;
}
}
}
return false;
}
public static boolean noneMatch(List<? extends String> keys, ResourceLocation key) {
return keys.stream().map(ResourceLocation::new).noneMatch(key::equals);
return !anyMatch(keys, key);
}
public static boolean anyMatchResLoc(List<? extends ResourceLocation> keys, ResourceLocation key) {
return keys.stream().anyMatch(key::equals);
}
// oh man this is aesthetically pleasing

View file

@ -1,5 +1,7 @@
package at.petrak.hexcasting.api.mod;
import at.petrak.hexcasting.api.casting.ActionRegistryEntry;
import at.petrak.hexcasting.xplat.IXplatAbstractions;
import net.minecraft.core.Registry;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.tags.TagKey;
@ -16,6 +18,7 @@ public class HexTags {
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 final TagKey<Item> SEAL_MATERIALS = create("seal_materials");
public static TagKey<Item> create(String name) {
return create(modLoc(name));
@ -43,4 +46,25 @@ public class HexTags {
return TagKey.create(Registry.ENTITY_TYPE_REGISTRY, modLoc(name));
}
}
public static final class Actions {
/**
* Actions with this tag can't be used until the caster is enlightened and send the
* "am I not skilled enough" message
*/
public static final TagKey<ActionRegistryEntry> REQUIRES_ENLIGHTENMENT = create("requires_enlightenment");
/**
* Actions where the pattern is calculated per-world
*/
public static final TagKey<ActionRegistryEntry> PER_WORLD_PATTERN = create("per_world_pattern");
/**
* Actions that can cause Blind Diversion
*/
public static final TagKey<ActionRegistryEntry> CAN_START_ENLIGHTEN = create("can_start_enlighten");
public static TagKey<ActionRegistryEntry> create(String name) {
return TagKey.create(IXplatAbstractions.INSTANCE.getActionRegistry().key(), modLoc(name));
}
}
}

View file

@ -1,93 +0,0 @@
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.eval.SpellContinuation
import at.petrak.hexcasting.api.spell.iota.Iota
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 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
* and pushing one new value.
* For a more "traditional" pop arguments, push return experience, see [ConstMediaAction].
*
* Implementors MUST NOT mutate the context.
*/
interface Action {
/**
* Operate on the stack. Return the new stack and any side effects of the cast.
*
* Although this is passed a [MutableList], this is only for the convenience of implementors.
* It is a clone of the stack and modifying it does nothing. You must return the new stack
* with the [OperationResult].
*
* A particle effect at the cast site and various messages and advancements are done automagically.
*/
fun operate(
continuation: SpellContinuation,
stack: MutableList<Iota>,
ravenmind: Iota?,
ctx: CastingContext
): OperationResult
/**
* Do you need to be enlightened to use this operator? (i.e. is this operator a Great Pattern)
*/
val isGreat: Boolean get() = false
/**
* Should this Great Pattern process and have side effects, even if its user isn't enlightened?
*
* The pattern itself may modify its effects based on whether the user is enlightened or not, regardless of what this value is.
*/
val alwaysProcessGreatSpell: Boolean get() = this is SpellAction
/**
* Can this Great Pattern give you Blind Diversion?
*/
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
const val MAX_DISTANCE_FROM_SENTINEL: Double = 16.0
@JvmStatic
fun raycastEnd(origin: Vec3, look: Vec3): Vec3 =
origin.add(look.normalize().scale(MAX_DISTANCE))
@JvmStatic
fun makeConstantOp(x: Iota): Action = object : ConstMediaAction {
override val argc: Int
get() = 0
override fun execute(args: List<Iota>, ctx: CastingContext): List<Iota> =
listOf(x)
}
private val DOUBLE_FORMATTER = DecimalFormat("####.####")
@JvmStatic
fun makeConstantOp(x: Double, key: ResourceLocation): Action = object : ConstMediaAction {
override val argc: Int
get() = 0
override fun execute(args: List<Iota>, ctx: CastingContext): List<Iota> =
x.asActionResult
override val displayName: Component
get() = "hexcasting.spell.$key".asTranslatedComponent(DOUBLE_FORMATTER.format(x)).lightPurple
}
}
}

View file

@ -1,7 +0,0 @@
package at.petrak.hexcasting.api.spell
import at.petrak.hexcasting.api.spell.casting.CastingContext
interface RenderedSpell {
fun cast(ctx: CastingContext)
}

View file

@ -1,10 +0,0 @@
package at.petrak.hexcasting.api.spell.casting;
import at.petrak.hexcasting.api.spell.math.HexDir;
import at.petrak.hexcasting.api.spell.math.HexPattern;
public final class SpecialPatterns {
public static final HexPattern INTROSPECTION = HexPattern.fromAngles("qqq", HexDir.WEST);
public static final HexPattern RETROSPECTION = HexPattern.fromAngles("eee", HexDir.EAST);
public static final HexPattern CONSIDERATION = HexPattern.fromAngles("qqqaw", HexDir.EAST);
}

Some files were not shown because too many files have changed in this diff Show more