diff --git a/src/main/java/carpet/mixins/PointOfInterest_scarpetMixin.java b/src/main/java/carpet/mixins/PointOfInterest_scarpetMixin.java new file mode 100644 index 00000000..6956788b --- /dev/null +++ b/src/main/java/carpet/mixins/PointOfInterest_scarpetMixin.java @@ -0,0 +1,12 @@ +package carpet.mixins; + +import net.minecraft.village.PointOfInterest; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.gen.Accessor; + +@Mixin(PointOfInterest.class) +public interface PointOfInterest_scarpetMixin +{ + @Accessor("freeTickets") + int getFreeTickets(); +} diff --git a/src/main/java/carpet/script/CarpetExpression.java b/src/main/java/carpet/script/CarpetExpression.java index 67bece95..eba711a3 100644 --- a/src/main/java/carpet/script/CarpetExpression.java +++ b/src/main/java/carpet/script/CarpetExpression.java @@ -6,6 +6,7 @@ import carpet.fakes.BiomeArrayInterface; import carpet.fakes.StatTypeInterface; import carpet.helpers.FeatureGenerator; import carpet.mixins.ChunkTicketManager_scarpetMixin; +import carpet.mixins.PointOfInterest_scarpetMixin; import carpet.mixins.ServerChunkManager_scarpetMixin; import carpet.script.Fluff.TriFunction; import carpet.script.bundled.Module; @@ -91,6 +92,9 @@ import net.minecraft.util.math.EulerAngle; import net.minecraft.util.math.BlockBox; import net.minecraft.util.math.Vec3d; import net.minecraft.util.registry.Registry; +import net.minecraft.village.PointOfInterest; +import net.minecraft.village.PointOfInterestStorage; +import net.minecraft.village.PointOfInterestType; import net.minecraft.world.Heightmap; import net.minecraft.world.LightType; import net.minecraft.world.World; @@ -790,6 +794,113 @@ public class CarpetExpression return (c_, t_) -> retval; }); + // poi_get(pos, radius?, type?, occupation?) + this.expr.addLazyFunction("poi_get", -1, (c, t, lv) -> + { + CarpetContext cc = (CarpetContext) c; + if (lv.size() == 0) + { + throw new InternalExpressionException("'poi_get' requires at least one parameter"); + } + BlockValue.LocatorResult locator = BlockValue.fromParams(cc, lv, 0, true); + BlockPos pos = locator.block.getPos(); + PointOfInterestStorage store = cc.s.getWorld().getPointOfInterestStorage(); + if (lv.size() == locator.offset) + { + PointOfInterestType poiType = store.getType(pos).orElse(null); + if (poiType == null) + { + CarpetSettings.LOG.error("no poi"); + return LazyValue.NULL; + } + // this feels wrong, but I don't want to mix-in more than I really need to. + // also distance adds 0.5 to each point which screws up accurate distance calculations + // you shoudn't be using POI with that in mind anyways, so I am not worried about it. + PointOfInterest poi = store.get( + poiType.getCompletionCondition(), + pos, + 1, + PointOfInterestStorage.OccupationStatus.ANY + ).filter(p -> p.getPos().equals(pos)).findFirst().orElse(null); + if (poi == null) + return LazyValue.NULL; + Value ret = ListValue.of( + new StringValue(poi.getType().toString()), + new NumericValue(poiType.getTicketCount() - ((PointOfInterest_scarpetMixin)poi).getFreeTickets()) + ); + return (_c, _t) -> ret; + } + long radius = NumericValue.asNumber(lv.get(locator.offset+0).evalValue(c)).getLong(); + if (radius < 0) return ListValue.lazyEmpty(); + Predicate condition = PointOfInterestType.ALWAYS_TRUE; + if (locator.offset + 1 < lv.size()) + { + String poiType = lv.get(locator.offset+1).evalValue(c).getString().toLowerCase(Locale.ROOT); + if (!"any".equals(poiType)) + { + PointOfInterestType type = Registry.POINT_OF_INTEREST_TYPE.get(new Identifier(poiType)); + condition = (tt) -> tt == type; + } + } + PointOfInterestStorage.OccupationStatus status = PointOfInterestStorage.OccupationStatus.ANY; + if (locator.offset + 2 < lv.size()) + { + String statusString = lv.get(locator.offset+2).evalValue(c).getString().toLowerCase(Locale.ROOT); + if ("occupied".equals(statusString)) + status = PointOfInterestStorage.OccupationStatus.IS_OCCUPIED; + else if ("available".equals(statusString)) + status = PointOfInterestStorage.OccupationStatus.HAS_SPACE; + } + + Value ret = ListValue.wrap(store.get(condition, pos, (int)radius, status).map( p -> + ListValue.of( + new StringValue(p.getType().toString()), + new NumericValue(p.getType().getTicketCount() - ((PointOfInterest_scarpetMixin)p).getFreeTickets()) + ) + ).collect(Collectors.toList())); + + return (c_, t_) -> ret; + }); + + //poi_set(pos, null) poi_set(pos, type, occupied?, + this.expr.addLazyFunction("poi_set", -1, (c, t, lv) -> + { + CarpetContext cc = (CarpetContext) c; + if (lv.size() == 0) + { + throw new InternalExpressionException("'poi_get' requires at least one parameter"); + } + BlockValue.LocatorResult locator = BlockValue.fromParams(cc, lv, 0, true); + BlockPos pos = locator.block.getPos(); + if (lv.size() < locator.offset) + throw new InternalExpressionException("'poi_set' requires the new poi type or null, after position argument"); + Value poi = lv.get(locator.offset+0).evalValue(c); + PointOfInterestStorage store = cc.s.getWorld().getPointOfInterestStorage(); + if (poi == Value.NULL) + { // clear poi information + if (store.getType(pos).isPresent()) store.remove(pos); + return LazyValue.NULL; + } + String poiTypeString = poi.getString().toLowerCase(Locale.ROOT); + PointOfInterestType type = Registry.POINT_OF_INTEREST_TYPE.get(new Identifier(poiTypeString)); + // solving lack of null with defaulted registries + if (type == PointOfInterestType.UNEMPLOYED && !"unemployed".equals(poiTypeString)) + throw new InternalExpressionException("unknown POI type: "+poiTypeString); + int occupancy = 0; + if (locator.offset + 1 < lv.size()) + { + occupancy = (int)NumericValue.asNumber(lv.get(locator.offset + 1).evalValue(c)).getLong(); + if (occupancy < 0) + throw new InternalExpressionException("Occupancy cannot be negative"); + } + if (store.getType(pos).isPresent()) store.remove(pos); + store.add(pos, type); + // setting occupancy for a + for (int i = 0; i < occupancy; i++) store.getPosition((p) -> p == type, p -> true, pos, 0); + return LazyValue.NULL; + }); + + this.expr.addLazyFunction("pos", 1, (c, t, lv) -> { Value arg = lv.get(0).evalValue(c); diff --git a/src/main/java/carpet/script/value/ListValue.java b/src/main/java/carpet/script/value/ListValue.java index 80ef40f7..a4af06fd 100644 --- a/src/main/java/carpet/script/value/ListValue.java +++ b/src/main/java/carpet/script/value/ListValue.java @@ -1,5 +1,6 @@ package carpet.script.value; +import carpet.script.LazyValue; import carpet.script.exception.InternalExpressionException; import net.minecraft.item.ItemStack; import net.minecraft.util.registry.Registry; @@ -86,6 +87,12 @@ public class ListValue extends AbstractListValue implements ContainerValueInterf return ListValue.wrap(Arrays.asList(list)); } + public static LazyValue lazyEmpty() + { + Value ret = new ListValue(); + return (c, t) -> ret; + } + private ListValue() { items = new ArrayList<>(); diff --git a/src/main/resources/carpet.mixins.json b/src/main/resources/carpet.mixins.json index ce82cff2..6f294724 100644 --- a/src/main/resources/carpet.mixins.json +++ b/src/main/resources/carpet.mixins.json @@ -139,6 +139,7 @@ "ChunkTicketManager_scarpetMixin", "ServerChunkManager_scarpetMixin", "StatType_scarpetMixin", + "PointOfInterest_scarpetMixin", "PistonBlock_movableTEMixin",