Convert Potato Cannon projectile types into a dynamic registry (#19)

This commit is contained in:
IThundxr 2025-02-23 20:07:54 -05:00 committed by GitHub
parent 2bd0034f01
commit a50fb9dac0
Failed to generate hash of commit
54 changed files with 1736 additions and 988 deletions

View file

@ -149,3 +149,6 @@ _Now using Flywheel 1.0_
- Synced AllPortalTracks with Create Fabric
- Implemented DyeHelper api (#7265)
- Implemented api to add custom block train conductors (#7030)
- Convert Potato Cannon project types into a dynamic registry
- Everything can be done with datapacks now, and there is no need to write a mod unless you need to add new
Render Modes, Entity Hit Actions or Block Hit Actions

View file

@ -1,4 +1,29 @@
// 1.20.1 2025-02-02T11:59:39.5970888 Create's Generated Registry Entries
// 1.20.1 2025-02-23T18:42:20.032170723 Create's Generated Registry Entries
abb9dd2c98388abb430343d4ac02d460e5edc086 data/create/create/potato_projectile/type/apple.json
1a20cb4e64dc07918a28fbccc73102bca3560f16 data/create/create/potato_projectile/type/baked_potato.json
8450576952f7723d22250da6f554dd0fda4e9fce data/create/create/potato_projectile/type/beetroot.json
9d927ec9c8b76a093e7ff8ff8f81be3a438d4aba data/create/create/potato_projectile/type/blaze_cake.json
46ccaf468caaa4faaca4f1494f60e99cd00785c9 data/create/create/potato_projectile/type/cake.json
22e0f5befe5835c7a9a6448e17e73e6fb7d0fb9c data/create/create/potato_projectile/type/carrot.json
af17d72549ee9421d2643a215ffc2291874fff93 data/create/create/potato_projectile/type/chocolate_berry.json
8cb4811601d46db5fdc0291edce3ec2f3a64e902 data/create/create/potato_projectile/type/chorus_fruit.json
9dd1298993a4657d69a9d2ac4de3ad7879d9aa8d data/create/create/potato_projectile/type/enchanted_golden_apple.json
e3ab3c4bd70ad742d37091ae05774aadffb1d1d5 data/create/create/potato_projectile/type/fallback.json
98bf0ce51484c5a9709fc69d046d0c70d987cfdd data/create/create/potato_projectile/type/fish.json
afe4e7cc87110d6f045f6672fb312a09290ca04a data/create/create/potato_projectile/type/glistering_melon.json
050c9333d41efd2577e1e597c7c68b00436b0963 data/create/create/potato_projectile/type/glow_berry.json
52046463af1abc92c5257992c302d7ab26c63e28 data/create/create/potato_projectile/type/golden_apple.json
c6234751ccad5111857b0ae9e297e42f529ad32a data/create/create/potato_projectile/type/golden_carrot.json
583b75978a4f920c2969473e5353fcc89886b612 data/create/create/potato_projectile/type/honeyed_apple.json
7a269ded36d1397f86f23c4c431c7a99fea74446 data/create/create/potato_projectile/type/melon_block.json
352402ba2fff3f23af178d15d61b0ba0a151097a data/create/create/potato_projectile/type/melon_slice.json
cbfd3028ad878bddbdaeb0eec0cd5a1c50d4c901 data/create/create/potato_projectile/type/poison_potato.json
b58e779803d29d4b36ecf79fde4e918577c71748 data/create/create/potato_projectile/type/potato.json
d15070ca23129e7d4025d86cb4be5701a466e593 data/create/create/potato_projectile/type/pufferfish.json
0c099b5f1bd7ba3832ea2d29e8c1c22c83ed1c3d data/create/create/potato_projectile/type/pumpkin_block.json
7d58a0671c55e40cdb728c60152665c89209fb91 data/create/create/potato_projectile/type/pumpkin_pie.json
2dc21c61b4fd4c60b9ade2974b395df42611ee26 data/create/create/potato_projectile/type/suspicious_stew.json
b4a7c10bd9cdcfe67df2a5766e5ec2e35a3f9b24 data/create/create/potato_projectile/type/sweet_berry.json
030ede1044384c4117ac1e491bf5c78bbd2842f5 data/create/damage_type/crush.json
92b0416950ffeb3ba68811e587177c2f8811c2c5 data/create/damage_type/cuckoo_surprise.json
d2a4fdb64f4ba817e13a7b20c73fd1ca34b825fc data/create/damage_type/fan_fire.json

View file

@ -0,0 +1,10 @@
{
"damage": 5,
"items": "minecraft:apple",
"knockback": 0.5,
"render_mode": {
"type": "create:tumble"
},
"sound_pitch": 1.1,
"velocity_multiplier": 1.45
}

View file

@ -0,0 +1,14 @@
{
"damage": 5,
"items": "minecraft:baked_potato",
"knockback": 0.5,
"pre_entity_hit": {
"type": "create:set_on_fire",
"ticks": 60
},
"reload_ticks": 15,
"render_mode": {
"type": "create:tumble"
},
"velocity_multiplier": 1.25
}

View file

@ -0,0 +1,13 @@
{
"damage": 2,
"items": "minecraft:beetroot",
"knockback": 0.1,
"reload_ticks": 5,
"render_mode": {
"type": "create:toward_motion",
"spin": 2.0,
"sprite_angle_offset": 140
},
"sound_pitch": 1.6,
"velocity_multiplier": 1.6
}

View file

@ -0,0 +1,15 @@
{
"damage": 15,
"items": "create:blaze_cake",
"knockback": 0.3,
"pre_entity_hit": {
"type": "create:set_on_fire",
"ticks": 240
},
"reload_ticks": 20,
"render_mode": {
"type": "create:tumble"
},
"sticky": true,
"velocity_multiplier": 1.1
}

View file

@ -0,0 +1,11 @@
{
"damage": 8,
"items": "minecraft:cake",
"knockback": 0.1,
"reload_ticks": 15,
"render_mode": {
"type": "create:tumble"
},
"sticky": true,
"velocity_multiplier": 1.1
}

View file

@ -0,0 +1,17 @@
{
"damage": 4,
"items": "minecraft:carrot",
"knockback": 0.3,
"on_entity_hit": {
"type": "create:plant_crop",
"block": "minecraft:carrots"
},
"reload_ticks": 12,
"render_mode": {
"type": "create:toward_motion",
"spin": 1.0,
"sprite_angle_offset": 140
},
"sound_pitch": 1.5,
"velocity_multiplier": 1.45
}

View file

@ -0,0 +1,11 @@
{
"damage": 4,
"items": "create:chocolate_glazed_berries",
"knockback": 0.2,
"render_mode": {
"type": "create:tumble"
},
"sound_pitch": 1.25,
"split": 3,
"velocity_multiplier": 1.05
}

View file

@ -0,0 +1,14 @@
{
"damage": 3,
"items": "minecraft:chorus_fruit",
"knockback": 0.05,
"on_entity_hit": {
"type": "create:chorus_teleport",
"teleport_diameter": 20.0
},
"reload_ticks": 15,
"render_mode": {
"type": "create:tumble"
},
"velocity_multiplier": 1.2
}

View file

@ -0,0 +1,51 @@
{
"items": "minecraft:enchanted_golden_apple",
"knockback": 0.05,
"on_entity_hit": {
"type": "create:food_effects",
"food_property": {
"can_always_eat": true,
"effects": [
{
"effect": {
"amplifier": 1,
"duration": 400,
"effect": "minecraft:regeneration"
},
"probability": 1.0
},
{
"effect": {
"duration": 6000,
"effect": "minecraft:resistance"
},
"probability": 1.0
},
{
"effect": {
"duration": 6000,
"effect": "minecraft:fire_resistance"
},
"probability": 1.0
},
{
"effect": {
"amplifier": 3,
"duration": 2400,
"effect": "minecraft:absorption"
},
"probability": 1.0
}
],
"nutrition": 4,
"saturation_modifier": 1.2
},
"recoverable": false
},
"reload_ticks": 100,
"render_mode": {
"type": "create:tumble"
},
"sound_pitch": 1.1,
"velocity_multiplier": 1.45
}

View file

@ -0,0 +1,4 @@
{
"damage": 0,
"items": []
}

View file

@ -0,0 +1,19 @@
{
"damage": 4,
"items": [
"minecraft:cod",
"minecraft:cooked_cod",
"minecraft:salmon",
"minecraft:cooked_salmon",
"minecraft:tropical_fish"
],
"knockback": 0.6,
"render_mode": {
"type": "create:toward_motion",
"spin": 1.0,
"sprite_angle_offset": 140
},
"sound_pitch": 1.3,
"sticky": true,
"velocity_multiplier": 1.3
}

View file

@ -0,0 +1,18 @@
{
"damage": 5,
"items": "minecraft:glistering_melon_slice",
"knockback": 0.1,
"on_entity_hit": {
"type": "create:potion_effect",
"effect": "minecraft:glowing",
"level": 1,
"recoverable": true,
"ticks": 100
},
"reload_ticks": 8,
"render_mode": {
"type": "create:tumble"
},
"sound_pitch": 1.5,
"velocity_multiplier": 1.45
}

View file

@ -0,0 +1,18 @@
{
"damage": 2,
"items": "minecraft:glow_berries",
"knockback": 0.05,
"on_entity_hit": {
"type": "create:potion_effect",
"effect": "minecraft:glowing",
"level": 1,
"recoverable": false,
"ticks": 200
},
"render_mode": {
"type": "create:tumble"
},
"sound_pitch": 1.2,
"split": 2,
"velocity_multiplier": 1.05
}

View file

@ -0,0 +1,13 @@
{
"items": "minecraft:golden_apple",
"knockback": 0.05,
"on_entity_hit": {
"type": "create:cure_zombie_villager"
},
"reload_ticks": 100,
"render_mode": {
"type": "create:tumble"
},
"sound_pitch": 1.1,
"velocity_multiplier": 1.45
}

View file

@ -0,0 +1,13 @@
{
"damage": 12,
"items": "minecraft:golden_carrot",
"knockback": 0.5,
"reload_ticks": 15,
"render_mode": {
"type": "create:toward_motion",
"spin": 2.0,
"sprite_angle_offset": 140
},
"sound_pitch": 1.5,
"velocity_multiplier": 1.45
}

View file

@ -0,0 +1,18 @@
{
"damage": 6,
"items": "create:honeyed_apple",
"knockback": 0.1,
"on_entity_hit": {
"type": "create:potion_effect",
"effect": "minecraft:slowness",
"level": 2,
"recoverable": true,
"ticks": 160
},
"reload_ticks": 15,
"render_mode": {
"type": "create:tumble"
},
"sound_pitch": 1.1,
"velocity_multiplier": 1.35
}

View file

@ -0,0 +1,15 @@
{
"damage": 8,
"items": "minecraft:melon",
"knockback": 2.0,
"on_entity_hit": {
"type": "create:place_block_on_ground",
"block": "minecraft:melon"
},
"reload_ticks": 20,
"render_mode": {
"type": "create:tumble"
},
"sound_pitch": 0.9,
"velocity_multiplier": 0.95
}

View file

@ -0,0 +1,11 @@
{
"damage": 3,
"items": "minecraft:melon_slice",
"knockback": 0.1,
"reload_ticks": 8,
"render_mode": {
"type": "create:tumble"
},
"sound_pitch": 1.5,
"velocity_multiplier": 1.45
}

View file

@ -0,0 +1,17 @@
{
"damage": 5,
"items": "minecraft:poisonous_potato",
"knockback": 0.05,
"on_entity_hit": {
"type": "create:potion_effect",
"effect": "minecraft:poison",
"level": 1,
"recoverable": true,
"ticks": 160
},
"reload_ticks": 15,
"render_mode": {
"type": "create:tumble"
},
"velocity_multiplier": 1.25
}

View file

@ -0,0 +1,14 @@
{
"damage": 5,
"items": "minecraft:potato",
"knockback": 1.5,
"on_entity_hit": {
"type": "create:plant_crop",
"block": "minecraft:potatoes"
},
"reload_ticks": 15,
"render_mode": {
"type": "create:tumble"
},
"velocity_multiplier": 1.25
}

View file

@ -0,0 +1,46 @@
{
"damage": 4,
"items": "minecraft:pufferfish",
"knockback": 0.4,
"on_entity_hit": {
"type": "create:food_effects",
"food_property": {
"effects": [
{
"effect": {
"amplifier": 1,
"duration": 1200,
"effect": "minecraft:poison"
},
"probability": 1.0
},
{
"effect": {
"amplifier": 2,
"duration": 300,
"effect": "minecraft:hunger"
},
"probability": 1.0
},
{
"effect": {
"duration": 300,
"effect": "minecraft:nausea"
},
"probability": 1.0
}
],
"nutrition": 1,
"saturation_modifier": 0.1
},
"recoverable": false
},
"render_mode": {
"type": "create:toward_motion",
"spin": 1.0,
"sprite_angle_offset": 140
},
"sound_pitch": 1.1,
"sticky": true,
"velocity_multiplier": 1.1
}

View file

@ -0,0 +1,15 @@
{
"damage": 6,
"items": "minecraft:pumpkin",
"knockback": 2.0,
"on_entity_hit": {
"type": "create:place_block_on_ground",
"block": "minecraft:pumpkin"
},
"reload_ticks": 15,
"render_mode": {
"type": "create:tumble"
},
"sound_pitch": 0.9,
"velocity_multiplier": 0.95
}

View file

@ -0,0 +1,12 @@
{
"damage": 7,
"items": "minecraft:pumpkin_pie",
"knockback": 0.05,
"reload_ticks": 15,
"render_mode": {
"type": "create:tumble"
},
"sound_pitch": 1.1,
"sticky": true,
"velocity_multiplier": 1.1
}

View file

@ -0,0 +1,19 @@
{
"damage": 3,
"drop_stack": {
"Count": 1,
"id": "minecraft:bowl"
},
"items": "minecraft:suspicious_stew",
"knockback": 0.2,
"on_entity_hit": {
"type": "create:suspicious_stew"
},
"reload_ticks": 40,
"render_mode": {
"type": "create:toward_motion",
"spin": 1.0,
"sprite_angle_offset": 140
},
"velocity_multiplier": 0.8
}

View file

@ -0,0 +1,11 @@
{
"damage": 3,
"items": "minecraft:sweet_berries",
"knockback": 0.1,
"render_mode": {
"type": "create:tumble"
},
"sound_pitch": 1.25,
"split": 3,
"velocity_multiplier": 1.05
}

View file

@ -40,7 +40,6 @@ import com.simibubi.create.content.equipment.blueprint.BlueprintAssignCompleteRe
import com.simibubi.create.content.equipment.clipboard.ClipboardEditPacket;
import com.simibubi.create.content.equipment.extendoGrip.ExtendoGripInteractionPacket;
import com.simibubi.create.content.equipment.potatoCannon.PotatoCannonPacket;
import com.simibubi.create.content.equipment.potatoCannon.PotatoProjectileTypeManager;
import com.simibubi.create.content.equipment.symmetryWand.ConfigureSymmetryWandPacket;
import com.simibubi.create.content.equipment.symmetryWand.SymmetryEffectPacket;
import com.simibubi.create.content.equipment.tool.KnockbackPacket;
@ -190,7 +189,7 @@ public enum AllPackets {
CONTRAPTION_COLLIDER_LOCK_REQUEST(ContraptionColliderLockPacketRequest.class,
ContraptionColliderLockPacketRequest::new, PLAY_TO_SERVER),
RADIAL_WRENCH_MENU_SUBMIT(RadialWrenchMenuSubmitPacket.class, RadialWrenchMenuSubmitPacket::new,
PLAY_TO_SERVER),
PLAY_TO_SERVER),
LOGISTICS_STOCK_REQUEST(LogisticalStockRequestPacket.class, LogisticalStockRequestPacket::new, PLAY_TO_SERVER),
LOGISTICS_PACKAGE_REQUEST(PackageOrderRequestPacket.class, PackageOrderRequestPacket::new, PLAY_TO_SERVER),
CHAIN_CONVEYOR_CONNECT(ChainConveyorConnectionPacket.class, ChainConveyorConnectionPacket::new, PLAY_TO_SERVER),
@ -227,8 +226,6 @@ public enum AllPackets {
SOUL_PULSE(SoulPulseEffectPacket.class, SoulPulseEffectPacket::new, PLAY_TO_CLIENT),
PERSISTENT_DATA(ISyncPersistentData.PersistentDataPacket.class, ISyncPersistentData.PersistentDataPacket::new,
PLAY_TO_CLIENT),
SYNC_POTATO_PROJECTILE_TYPES(PotatoProjectileTypeManager.SyncPacket.class,
PotatoProjectileTypeManager.SyncPacket::new, PLAY_TO_CLIENT),
SYNC_RAIL_GRAPH(TrackGraphSyncPacket.class, TrackGraphSyncPacket::new, PLAY_TO_CLIENT),
SYNC_EDGE_GROUP(SignalEdgeGroupPacket.class, SignalEdgeGroupPacket::new, PLAY_TO_CLIENT),
SYNC_TRAIN(TrainPacket.class, TrainPacket::new, PLAY_TO_CLIENT),
@ -280,7 +277,7 @@ public enum AllPackets {
private PacketType<?> packetType;
<T extends SimplePacketBase> AllPackets(Class<T> type, Function<FriendlyByteBuf, T> factory,
NetworkDirection direction) {
NetworkDirection direction) {
packetType = new PacketType<>(type, factory, direction);
}

View file

@ -12,7 +12,9 @@ import com.simibubi.create.compat.Mods;
import com.simibubi.create.compat.computercraft.ComputerCraftProxy;
import com.simibubi.create.compat.curios.Curios;
import com.simibubi.create.content.decoration.palettes.AllPaletteBlocks;
import com.simibubi.create.content.equipment.potatoCannon.BuiltinPotatoProjectileTypes;
import com.simibubi.create.content.equipment.potatoCannon.AllPotatoProjectileBlockHitActions;
import com.simibubi.create.content.equipment.potatoCannon.AllPotatoProjectileEntityHitActions;
import com.simibubi.create.content.equipment.potatoCannon.AllPotatoProjectileRenderModes;
import com.simibubi.create.content.fluids.tank.BoilerHeaters;
import com.simibubi.create.content.kinetics.TorquePropagator;
import com.simibubi.create.content.kinetics.fan.processing.AllFanProcessingTypes;
@ -56,6 +58,7 @@ import net.minecraftforge.fml.ModLoadingContext;
import net.minecraftforge.fml.common.Mod;
import net.minecraftforge.fml.event.lifecycle.FMLCommonSetupEvent;
import net.minecraftforge.fml.javafmlmod.FMLJavaModLoadingContext;
import net.minecraftforge.registries.RegisterEvent;
@Mod(Create.ID)
public class Create {
@ -68,7 +71,9 @@ public class Create {
.disableHtmlEscaping()
.create();
/** Use the {@link Random} of a local {@link Level} or {@link Entity} or create one */
/**
* Use the {@link Random} of a local {@link Level} or {@link Entity} or create one
*/
@Deprecated
public static final Random RANDOM = new Random();
@ -82,7 +87,7 @@ public class Create {
static {
REGISTRATE.setTooltipModifierFactory(item ->
new ItemDescription.Modifier(item, FontHelper.Palette.STANDARD_CREATE)
.andThen(TooltipModifier.mapNull(KineticStats.create(item)))
.andThen(TooltipModifier.mapNull(KineticStats.create(item)))
);
}
@ -132,6 +137,7 @@ public class Create {
AllConfigs.register(modLoadingContext);
// TODO - Make these use Registry.register and move them into the RegisterEvent
AllArmInteractionPointTypes.register(modEventBus);
AllFanProcessingTypes.register(modEventBus);
AllItemAttributeTypes.register(modEventBus);
@ -148,6 +154,7 @@ public class Create {
CopperRegistries.inject();
modEventBus.addListener(Create::init);
modEventBus.addListener(Create::onRegister);
modEventBus.addListener(AllEntityTypes::registerEntityAttributes);
modEventBus.addListener(EventPriority.LOWEST, CreateDatagen::gatherData);
modEventBus.addListener(AllSoundEvents::register);
@ -166,7 +173,6 @@ public class Create {
// TODO: custom registration should all happen in one place
// Most registration happens in the constructor.
// These registrations use Create's registered objects directly so they must run after registration has finished.
BuiltinPotatoProjectileTypes.register();
BoilerHeaters.registerDefaults();
AllPortalTracks.registerDefaults();
BlockSpoutingBehaviour.registerDefaults();
@ -182,6 +188,12 @@ public class Create {
});
}
public static void onRegister(final RegisterEvent event) {
AllPotatoProjectileRenderModes.init();
AllPotatoProjectileEntityHitActions.init();
AllPotatoProjectileBlockHitActions.init();
}
public static LangBuilder lang() {
return new LangBuilder(ID);
}

View file

@ -0,0 +1,230 @@
package com.simibubi.create.api.equipment.potatoCannon;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import org.jetbrains.annotations.Nullable;
import com.mojang.serialization.Codec;
import com.mojang.serialization.codecs.RecordCodecBuilder;
import com.simibubi.create.api.equipment.potatoCannon.PotatoProjectileEntityHitAction.Type;
import com.simibubi.create.api.equipment.potatoCannon.PotatoProjectileRenderMode.Billboard;
import com.simibubi.create.api.registry.CreateRegistries;
import net.minecraft.core.Holder;
import net.minecraft.core.Holder.Reference;
import net.minecraft.core.HolderSet;
import net.minecraft.core.RegistryCodecs;
import net.minecraft.core.registries.Registries;
import net.minecraft.data.worldgen.BootstapContext;
import net.minecraft.resources.ResourceKey;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.item.Item;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.level.ItemLike;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.LevelAccessor;
import net.minecraft.world.phys.BlockHitResult;
import net.minecraft.world.phys.EntityHitResult;
public record PotatoCannonProjectileType(HolderSet<Item> items, int reloadTicks, int damage, int split, float knockback,
float drag, float velocityMultiplier, float gravityMultiplier,
float soundPitch, boolean sticky, ItemStack dropStack,
PotatoProjectileRenderMode renderMode,
Optional<PotatoProjectileEntityHitAction> preEntityHit,
Optional<PotatoProjectileEntityHitAction> onEntityHit,
Optional<PotatoProjectileBlockHitAction> onBlockHit) {
public static final Codec<PotatoCannonProjectileType> CODEC = RecordCodecBuilder.create(i -> i.group(
RegistryCodecs.homogeneousList(Registries.ITEM).fieldOf("items").forGetter(PotatoCannonProjectileType::items),
Codec.INT.optionalFieldOf("reload_ticks", 10).forGetter(PotatoCannonProjectileType::reloadTicks),
Codec.INT.optionalFieldOf("damage", 1).forGetter(PotatoCannonProjectileType::damage),
Codec.INT.optionalFieldOf("split", 1).forGetter(PotatoCannonProjectileType::split),
Codec.FLOAT.optionalFieldOf("knockback", 1f).forGetter(PotatoCannonProjectileType::knockback),
Codec.FLOAT.optionalFieldOf("drag", .99f).forGetter(PotatoCannonProjectileType::drag),
Codec.FLOAT.optionalFieldOf("velocity_multiplier", 1f).forGetter(PotatoCannonProjectileType::velocityMultiplier),
Codec.FLOAT.optionalFieldOf("gravity_multiplier", 1f).forGetter(PotatoCannonProjectileType::gravityMultiplier),
Codec.FLOAT.optionalFieldOf("sound_pitch", 1f).forGetter(PotatoCannonProjectileType::soundPitch),
Codec.BOOL.optionalFieldOf("sticky", false).forGetter(PotatoCannonProjectileType::sticky),
ItemStack.CODEC.optionalFieldOf("drop_stack", ItemStack.EMPTY).forGetter(PotatoCannonProjectileType::dropStack),
PotatoProjectileRenderMode.CODEC.optionalFieldOf("render_mode", Billboard.INSTANCE).forGetter(PotatoCannonProjectileType::renderMode),
PotatoProjectileEntityHitAction.CODEC.optionalFieldOf("pre_entity_hit").forGetter(p -> p.preEntityHit),
PotatoProjectileEntityHitAction.CODEC.optionalFieldOf("on_entity_hit").forGetter(p -> p.onEntityHit),
PotatoProjectileBlockHitAction.CODEC.optionalFieldOf("on_entity_hit").forGetter(p -> p.onBlockHit)
).apply(i, PotatoCannonProjectileType::new));
@Nullable
public static PotatoCannonProjectileType getTypeForItem(Level level, Item item) {
// Cache this if it causes performance issues, but it probably won't
List<PotatoCannonProjectileType> types = level.registryAccess()
.lookupOrThrow(CreateRegistries.POTATO_PROJECTILE_TYPE)
.listElements()
.map(Reference::value)
.toList();
for (PotatoCannonProjectileType type : types)
if (type.items.contains(item.builtInRegistryHolder()))
return type;
return null;
}
public static Optional<PotatoCannonProjectileType> getTypeForStack(Level level, ItemStack item) {
if (item.isEmpty())
return Optional.empty();
return Optional.ofNullable(getTypeForItem(level, item.getItem()));
}
public boolean preEntityHit(ItemStack stack, EntityHitResult ray) {
return preEntityHit.map(i -> i.execute(stack, ray, Type.PRE_HIT)).orElse(false);
}
public boolean onEntityHit(ItemStack stack, EntityHitResult ray) {
return onEntityHit.map(i -> i.execute(stack, ray, Type.ON_HIT)).orElse(false);
}
public boolean onBlockHit(LevelAccessor level, ItemStack stack, BlockHitResult ray) {
return onBlockHit.map(i -> i.execute(level, stack, ray)).orElse(false);
}
public static class Builder {
private ResourceLocation id;
private final List<Holder<Item>> items = new ArrayList<>();
private int reloadTicks = 10;
private int damage = 1;
private int split = 1;
private float knockback = 1f;
private float drag = 0.99f;
private float velocityMultiplier = 1f;
private float gravityMultiplier = 1f;
private float soundPitch = 1f;
private boolean sticky = false;
private ItemStack dropStack = ItemStack.EMPTY;
private PotatoProjectileRenderMode renderMode = Billboard.INSTANCE;
private PotatoProjectileEntityHitAction preEntityHit = null;
private PotatoProjectileEntityHitAction onEntityHit = null;
private PotatoProjectileBlockHitAction onBlockHit = null;
public Builder(ResourceLocation id) {
this.id = id;
}
public Builder reloadTicks(int reload) {
this.reloadTicks = reload;
return this;
}
public Builder damage(int damage) {
this.damage = damage;
return this;
}
public Builder splitInto(int split) {
this.split = split;
return this;
}
public Builder knockback(float knockback) {
this.knockback = knockback;
return this;
}
public Builder drag(float drag) {
this.drag = drag;
return this;
}
public Builder velocity(float velocity) {
this.velocityMultiplier = velocity;
return this;
}
public Builder gravity(float modifier) {
this.gravityMultiplier = modifier;
return this;
}
public Builder soundPitch(float pitch) {
this.soundPitch = pitch;
return this;
}
public Builder sticky() {
this.sticky = true;
return this;
}
public Builder dropStack(ItemStack stack) {
this.dropStack = stack;
return this;
}
public Builder renderMode(PotatoProjectileRenderMode renderMode) {
this.renderMode = renderMode;
return this;
}
public Builder renderBillboard() {
renderMode(PotatoProjectileRenderMode.Billboard.INSTANCE);
return this;
}
public Builder renderTumbling() {
renderMode(PotatoProjectileRenderMode.Tumble.INSTANCE);
return this;
}
public Builder renderTowardMotion(int spriteAngle, float spin) {
renderMode(new PotatoProjectileRenderMode.TowardMotion(spriteAngle, spin));
return this;
}
public Builder preEntityHit(PotatoProjectileEntityHitAction entityHitAction) {
this.preEntityHit = entityHitAction;
return this;
}
public Builder onEntityHit(PotatoProjectileEntityHitAction entityHitAction) {
this.onEntityHit = entityHitAction;
return this;
}
public Builder onBlockHit(PotatoProjectileBlockHitAction blockHitAction) {
this.onBlockHit = blockHitAction;
return this;
}
public Builder addItems(ItemLike... items) {
for (ItemLike provider : items)
this.items.add(provider.asItem().builtInRegistryHolder());
return this;
}
public void register(BootstapContext<PotatoCannonProjectileType> ctx) {
PotatoCannonProjectileType type = new PotatoCannonProjectileType(
HolderSet.direct(items),
reloadTicks,
damage,
split,
knockback,
drag,
velocityMultiplier,
gravityMultiplier,
soundPitch,
sticky,
dropStack,
renderMode,
Optional.ofNullable(preEntityHit),
Optional.ofNullable(onEntityHit),
Optional.ofNullable(onBlockHit)
);
ctx.register(ResourceKey.create(CreateRegistries.POTATO_PROJECTILE_TYPE, id), type);
}
public void registerAndAssign(BootstapContext<PotatoCannonProjectileType> ctx, ItemLike... items) {
addItems(items);
register(ctx);
}
}
}

View file

@ -0,0 +1,120 @@
package com.simibubi.create.api.equipment.potatoCannon;
import java.util.function.Function;
import com.mojang.serialization.Codec;
import com.mojang.serialization.codecs.RecordCodecBuilder;
import com.simibubi.create.api.registry.CreateBuiltInRegistries;
import com.simibubi.create.foundation.mixin.accessor.FallingBlockEntityAccessor;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.core.Holder;
import net.minecraft.core.registries.BuiltInRegistries;
import net.minecraft.world.entity.item.FallingBlockEntity;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.LevelAccessor;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.phys.BlockHitResult;
import net.minecraftforge.common.IPlantable;
import net.minecraftforge.registries.ForgeRegistries;
public interface PotatoProjectileBlockHitAction {
Codec<PotatoProjectileBlockHitAction> CODEC = CreateBuiltInRegistries.POTATO_PROJECTILE_BLOCK_HIT_ACTION.byNameCodec()
.dispatch(PotatoProjectileBlockHitAction::codec, Function.identity());
boolean execute(LevelAccessor level, ItemStack projectile, BlockHitResult ray);
Codec<? extends PotatoProjectileBlockHitAction> codec();
record PlantCrop(Holder<Block> cropBlock) implements PotatoProjectileBlockHitAction {
public static final Codec<PlantCrop> CODEC = RecordCodecBuilder.create(instance -> instance.group(
BuiltInRegistries.BLOCK.holderByNameCodec().fieldOf("block").forGetter(PlantCrop::cropBlock)
).apply(instance, PlantCrop::new));
public PlantCrop(Block cropBlock) {
this(ForgeRegistries.BLOCKS.getDelegateOrThrow(cropBlock));
}
@Override
public boolean execute(LevelAccessor level, ItemStack projectile, BlockHitResult ray) {
if (level.isClientSide())
return true;
BlockPos hitPos = ray.getBlockPos();
if (level instanceof Level l && !l.isLoaded(hitPos))
return true;
Direction face = ray.getDirection();
if (face != Direction.UP)
return false;
BlockPos placePos = hitPos.relative(face);
if (!level.getBlockState(placePos)
.canBeReplaced())
return false;
if (!(cropBlock.get() instanceof IPlantable))
return false;
BlockState blockState = level.getBlockState(hitPos);
if (!blockState.canSustainPlant(level, hitPos, face, (IPlantable) cropBlock.get()))
return false;
level.setBlock(placePos, cropBlock.get()
.defaultBlockState(), 3);
return true;
}
@Override
public Codec<? extends PotatoProjectileBlockHitAction> codec() {
return CODEC;
}
}
record PlaceBlockOnGround(Holder<Block> block) implements PotatoProjectileBlockHitAction {
public static final Codec<PlaceBlockOnGround> CODEC = RecordCodecBuilder.create(instance -> instance.group(
BuiltInRegistries.BLOCK.holderByNameCodec().fieldOf("block").forGetter(PlaceBlockOnGround::block)
).apply(instance, PlaceBlockOnGround::new));
public PlaceBlockOnGround(Block block) {
this(ForgeRegistries.BLOCKS.getDelegateOrThrow(block));
}
@Override
public boolean execute(LevelAccessor levelAccessor, ItemStack projectile, BlockHitResult ray) {
if (levelAccessor.isClientSide())
return true;
BlockPos hitPos = ray.getBlockPos();
if (levelAccessor instanceof Level l && !l.isLoaded(hitPos))
return true;
Direction face = ray.getDirection();
BlockPos placePos = hitPos.relative(face);
if (!levelAccessor.getBlockState(placePos)
.canBeReplaced())
return false;
if (face == Direction.UP) {
levelAccessor.setBlock(placePos, block.value()
.defaultBlockState(), 3);
} else if (levelAccessor instanceof Level level) {
double y = ray.getLocation().y - 0.5;
if (!level.isEmptyBlock(placePos.above()))
y = Math.min(y, placePos.getY());
if (!level.isEmptyBlock(placePos.below()))
y = Math.max(y, placePos.getY());
FallingBlockEntity falling = FallingBlockEntityAccessor.create$callInit(level, placePos.getX() + 0.5, y,
placePos.getZ() + 0.5, block.get().defaultBlockState());
falling.time = 1;
level.addFreshEntity(falling);
}
return true;
}
@Override
public Codec<? extends PotatoProjectileBlockHitAction> codec() {
return CODEC;
}
}
}

View file

@ -0,0 +1,245 @@
package com.simibubi.create.api.equipment.potatoCannon;
import java.util.UUID;
import java.util.function.Function;
import com.mojang.authlib.GameProfile;
import com.mojang.datafixers.util.Pair;
import com.mojang.serialization.Codec;
import com.mojang.serialization.codecs.RecordCodecBuilder;
import com.simibubi.create.api.registry.CreateBuiltInRegistries;
import com.simibubi.create.foundation.mixin.accessor.SuspiciousStewItemAccessor;
import com.simibubi.create.foundation.utility.CreateCodecs;
import net.createmod.catnip.data.WorldAttached;
import net.minecraft.core.registries.BuiltInRegistries;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.sounds.SoundEvent;
import net.minecraft.sounds.SoundEvents;
import net.minecraft.sounds.SoundSource;
import net.minecraft.util.ExtraCodecs;
import net.minecraft.util.Mth;
import net.minecraft.world.InteractionHand;
import net.minecraft.world.effect.MobEffect;
import net.minecraft.world.effect.MobEffectInstance;
import net.minecraft.world.effect.MobEffects;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.LivingEntity;
import net.minecraft.world.entity.animal.Fox;
import net.minecraft.world.entity.monster.ZombieVillager;
import net.minecraft.world.food.FoodProperties;
import net.minecraft.world.food.Foods;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.Items;
import net.minecraft.world.level.Level;
import net.minecraft.world.phys.EntityHitResult;
import net.minecraft.world.phys.Vec3;
import net.minecraftforge.common.util.FakePlayer;
import net.minecraftforge.event.ForgeEventFactory;
import net.minecraftforge.event.entity.EntityTeleportEvent;
public interface PotatoProjectileEntityHitAction {
Codec<PotatoProjectileEntityHitAction> CODEC = CreateBuiltInRegistries.POTATO_PROJECTILE_ENTITY_HIT_ACTION.byNameCodec()
.dispatch(PotatoProjectileEntityHitAction::codec, Function.identity());
enum Type {
PRE_HIT,
ON_HIT
}
/**
* @return true if the hit should be canceled if the type is {@link Type#PRE_HIT PRE_HIT},
* true if this shouldn't recover the projectile if the type is {@link Type#ON_HIT ON_HIT}
*/
boolean execute(ItemStack projectile, EntityHitResult ray, Type type);
Codec<? extends PotatoProjectileEntityHitAction> codec();
record SetOnFire(int ticks) implements PotatoProjectileEntityHitAction {
public static final Codec<SetOnFire> CODEC = RecordCodecBuilder.create(instance -> instance.group(
ExtraCodecs.POSITIVE_INT.fieldOf("ticks").forGetter(SetOnFire::ticks)
).apply(instance, SetOnFire::new));
public static SetOnFire seconds(int seconds) {
return new SetOnFire(seconds * 20);
}
@Override
public boolean execute(ItemStack projectile, EntityHitResult ray, Type type) {
ray.getEntity()
.setRemainingFireTicks(ticks);
return false;
}
@Override
public Codec<? extends PotatoProjectileEntityHitAction> codec() {
return CODEC;
}
}
record PotionEffect(MobEffect effect, int level, int ticks,
boolean recoverable) implements PotatoProjectileEntityHitAction {
public static final Codec<PotionEffect> CODEC = RecordCodecBuilder.create(instance -> instance.group(
BuiltInRegistries.MOB_EFFECT.byNameCodec().fieldOf("effect").forGetter(PotionEffect::effect),
ExtraCodecs.POSITIVE_INT.fieldOf("level").forGetter(PotionEffect::level),
ExtraCodecs.POSITIVE_INT.fieldOf("ticks").forGetter(PotionEffect::ticks),
Codec.BOOL.fieldOf("recoverable").forGetter(PotionEffect::recoverable)
).apply(instance, PotionEffect::new));
@Override
public boolean execute(ItemStack projectile, EntityHitResult ray, Type type) {
Entity entity = ray.getEntity();
if (entity.level().isClientSide)
return true;
if (entity instanceof LivingEntity)
applyEffect((LivingEntity) entity, new MobEffectInstance(effect, ticks, level - 1));
return !recoverable;
}
@Override
public Codec<? extends PotatoProjectileEntityHitAction> codec() {
return CODEC;
}
}
record FoodEffects(FoodProperties foodProperty, boolean recoverable) implements PotatoProjectileEntityHitAction {
public static final Codec<FoodEffects> CODEC = RecordCodecBuilder.create(instance -> instance.group(
CreateCodecs.FOOD_PROPERTIES.fieldOf("food_property").forGetter(FoodEffects::foodProperty),
Codec.BOOL.fieldOf("recoverable").forGetter(FoodEffects::recoverable)
).apply(instance, FoodEffects::new));
@Override
public boolean execute(ItemStack projectile, EntityHitResult ray, Type type) {
Entity entity = ray.getEntity();
if (entity.level().isClientSide)
return true;
if (entity instanceof LivingEntity livingEntity) {
for (Pair<MobEffectInstance, Float> effect : foodProperty.getEffects()) {
if (livingEntity.getRandom().nextFloat() < effect.getSecond())
applyEffect(livingEntity, new MobEffectInstance(effect.getFirst()));
}
}
return !recoverable;
}
@Override
public Codec<? extends PotatoProjectileEntityHitAction> codec() {
return CODEC;
}
}
record ChorusTeleport(double teleportDiameter) implements PotatoProjectileEntityHitAction {
public static final Codec<ChorusTeleport> CODEC = RecordCodecBuilder.create(instance -> instance.group(
CreateCodecs.POSITIVE_DOUBLE.fieldOf("teleport_diameter").forGetter(ChorusTeleport::teleportDiameter)
).apply(instance, ChorusTeleport::new));
@Override
public boolean execute(ItemStack projectile, EntityHitResult ray, Type type) {
Entity entity = ray.getEntity();
Level level = entity.getCommandSenderWorld();
if (level.isClientSide)
return true;
if (!(entity instanceof LivingEntity livingEntity))
return false;
double entityX = livingEntity.getX();
double entityY = livingEntity.getY();
double entityZ = livingEntity.getZ();
for (int teleportTry = 0; teleportTry < 16; ++teleportTry) {
double teleportX = entityX + (livingEntity.getRandom()
.nextDouble() - 0.5D) * teleportDiameter;
double teleportY = Mth.clamp(entityY + (livingEntity.getRandom()
.nextInt((int) teleportDiameter) - (int) (teleportDiameter / 2)), 0.0D, level.getHeight() - 1);
double teleportZ = entityZ + (livingEntity.getRandom()
.nextDouble() - 0.5D) * teleportDiameter;
EntityTeleportEvent.ChorusFruit event =
ForgeEventFactory.onChorusFruitTeleport(livingEntity, teleportX, teleportY, teleportZ);
if (event.isCanceled())
return false;
if (livingEntity.randomTeleport(event.getTargetX(), event.getTargetY(), event.getTargetZ(), true)) {
if (livingEntity.isPassenger())
livingEntity.stopRiding();
SoundEvent soundevent =
livingEntity instanceof Fox ? SoundEvents.FOX_TELEPORT : SoundEvents.CHORUS_FRUIT_TELEPORT;
level.playSound(null, entityX, entityY, entityZ, soundevent, SoundSource.PLAYERS, 1.0F, 1.0F);
livingEntity.playSound(soundevent, 1.0F, 1.0F);
livingEntity.setDeltaMovement(Vec3.ZERO);
return true;
}
}
return false;
}
@Override
public Codec<? extends PotatoProjectileEntityHitAction> codec() {
return CODEC;
}
}
enum CureZombieVillager implements PotatoProjectileEntityHitAction {
INSTANCE;
private static final FoodEffects EFFECT = new FoodEffects(Foods.GOLDEN_APPLE, false);
private static final GameProfile ZOMBIE_CONVERTER_NAME =
new GameProfile(UUID.fromString("be12d3dc-27d3-4992-8c97-66be53fd49c5"), "Converter");
private static final WorldAttached<FakePlayer> ZOMBIE_CONVERTERS =
new WorldAttached<>(w -> new FakePlayer((ServerLevel) w, ZOMBIE_CONVERTER_NAME));
public static final Codec<CureZombieVillager> CODEC = Codec.unit(INSTANCE);
@Override
public boolean execute(ItemStack projectile, EntityHitResult ray, Type type) {
Entity entity = ray.getEntity();
Level world = entity.level();
if (!(entity instanceof ZombieVillager zombieVillager) || !zombieVillager.hasEffect(MobEffects.WEAKNESS))
return EFFECT.execute(projectile, ray, type);
if (world.isClientSide)
return false;
FakePlayer dummy = ZOMBIE_CONVERTERS.get(world);
dummy.setItemInHand(InteractionHand.MAIN_HAND, new ItemStack(Items.GOLDEN_APPLE, 1));
zombieVillager.mobInteract(dummy, InteractionHand.MAIN_HAND);
return true;
}
@Override
public Codec<? extends PotatoProjectileEntityHitAction> codec() {
return CODEC;
}
}
enum SuspiciousStew implements PotatoProjectileEntityHitAction {
INSTANCE;
public static final Codec<SuspiciousStew> CODEC = Codec.unit(INSTANCE);
@Override
public boolean execute(ItemStack projectile, EntityHitResult ray, Type type) {
if (ray.getEntity() instanceof LivingEntity livingEntity)
SuspiciousStewItemAccessor.create$listPotionEffects(projectile, livingEntity::addEffect);
return true;
}
@Override
public Codec<? extends PotatoProjectileEntityHitAction> codec() {
return CODEC;
}
}
private static void applyEffect(LivingEntity entity, MobEffectInstance effect) {
if (effect.getEffect().isInstantenous()) {
effect.getEffect()
.applyInstantenousEffect(null, null, entity, effect.getDuration(), 1.0);
} else {
entity.addEffect(effect);
}
}
}

View file

@ -1,8 +1,14 @@
package com.simibubi.create.content.equipment.potatoCannon;
package com.simibubi.create.api.equipment.potatoCannon;
import static com.simibubi.create.content.equipment.potatoCannon.PotatoProjectileRenderMode.entityRandom;
import static com.simibubi.create.api.equipment.potatoCannon.PotatoProjectileRenderMode.entityRandom;
import java.util.function.Function;
import com.mojang.blaze3d.vertex.PoseStack;
import com.mojang.serialization.Codec;
import com.mojang.serialization.codecs.RecordCodecBuilder;
import com.simibubi.create.api.registry.CreateBuiltInRegistries;
import com.simibubi.create.content.equipment.potatoCannon.PotatoProjectileEntity;
import dev.engine_room.flywheel.lib.transform.TransformStack;
import net.createmod.catnip.math.AngleHelper;
@ -10,17 +16,23 @@ import net.minecraft.client.Minecraft;
import net.minecraft.util.Mth;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.phys.Vec3;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.api.distmarker.OnlyIn;
public interface PotatoProjectileRenderMode {
Codec<PotatoProjectileRenderMode> CODEC = CreateBuiltInRegistries.POTATO_PROJECTILE_RENDER_MODE.byNameCodec()
.dispatch(PotatoProjectileRenderMode::codec, Function.identity());
@OnlyIn(Dist.CLIENT)
void transform(PoseStack ms, PotatoProjectileEntity entity, float pt);
public static class Billboard implements PotatoProjectileRenderMode {
Codec<? extends PotatoProjectileRenderMode> codec();
public static final Billboard INSTANCE = new Billboard();
enum Billboard implements PotatoProjectileRenderMode {
INSTANCE;
public static final Codec<Billboard> CODEC = Codec.unit(INSTANCE);
@Override
@OnlyIn(Dist.CLIENT)
@ -37,32 +49,37 @@ public interface PotatoProjectileRenderMode {
.rotateXDegrees(AngleHelper.deg(Mth.atan2(diff.y, Mth.sqrt((float) (diff.x * diff.x + diff.z * diff.z)))));
}
@Override
public Codec<? extends PotatoProjectileRenderMode> codec() {
return CODEC;
}
}
public static class Tumble extends Billboard {
enum Tumble implements PotatoProjectileRenderMode {
INSTANCE;
public static final Tumble INSTANCE = new Tumble();
public static final Codec<Tumble> CODEC = Codec.unit(INSTANCE);
@Override
@OnlyIn(Dist.CLIENT)
public void transform(PoseStack ms, PotatoProjectileEntity entity, float pt) {
super.transform(ms, entity, pt);
Billboard.INSTANCE.transform(ms, entity, pt);
TransformStack.of(ms)
.rotateZDegrees((entity.tickCount + pt) * 2 * entityRandom(entity, 16))
.rotateXDegrees((entity.tickCount + pt) * entityRandom(entity, 32));
}
@Override
public Codec<? extends PotatoProjectileRenderMode> codec() {
return CODEC;
}
}
public static class TowardMotion implements PotatoProjectileRenderMode {
private int spriteAngleOffset;
private float spin;
public TowardMotion(int spriteAngleOffset, float spin) {
this.spriteAngleOffset = spriteAngleOffset;
this.spin = spin;
}
record TowardMotion(int spriteAngleOffset, float spin) implements PotatoProjectileRenderMode {
public static final Codec<TowardMotion> CODEC = RecordCodecBuilder.create(instance -> instance.group(
Codec.INT.fieldOf("sprite_angle_offset").forGetter(i -> i.spriteAngleOffset),
Codec.FLOAT.fieldOf("spin").forGetter(i -> i.spin)
).apply(instance, TowardMotion::new));
@Override
@OnlyIn(Dist.CLIENT)
@ -77,15 +94,16 @@ public interface PotatoProjectileRenderMode {
.rotateZDegrees(-spriteAngleOffset);
}
@Override
public Codec<? extends PotatoProjectileRenderMode> codec() {
return CODEC;
}
}
public static class StuckToEntity implements PotatoProjectileRenderMode {
private Vec3 offset;
public StuckToEntity(Vec3 offset) {
this.offset = offset;
}
record StuckToEntity(Vec3 offset) implements PotatoProjectileRenderMode {
public static final Codec<StuckToEntity> CODEC = RecordCodecBuilder.create(instance -> instance.group(
Vec3.CODEC.fieldOf("offset").forGetter(i -> i.offset)
).apply(instance, StuckToEntity::new));
@Override
@OnlyIn(Dist.CLIENT)
@ -93,10 +111,13 @@ public interface PotatoProjectileRenderMode {
TransformStack.of(ms).rotateYDegrees(AngleHelper.deg(Mth.atan2(offset.x, offset.z)));
}
@Override
public Codec<? extends PotatoProjectileRenderMode> codec() {
return CODEC;
}
}
public static int entityRandom(Entity entity, int maxValue) {
static int entityRandom(Entity entity, int maxValue) {
return (System.identityHashCode(entity) * 31) % maxValue;
}
}

View file

@ -2,12 +2,16 @@ package com.simibubi.create.api.registry;
import org.jetbrains.annotations.ApiStatus;
import com.mojang.serialization.Codec;
import com.mojang.serialization.Lifecycle;
import com.simibubi.create.api.behaviour.display.DisplaySource;
import com.simibubi.create.api.behaviour.display.DisplayTarget;
import com.simibubi.create.api.contraption.ContraptionType;
import com.simibubi.create.api.contraption.storage.fluid.MountedFluidStorageType;
import com.simibubi.create.api.contraption.storage.item.MountedItemStorageType;
import com.simibubi.create.api.equipment.potatoCannon.PotatoProjectileBlockHitAction;
import com.simibubi.create.api.equipment.potatoCannon.PotatoProjectileEntityHitAction;
import com.simibubi.create.api.equipment.potatoCannon.PotatoProjectileRenderMode;
import com.simibubi.create.content.kinetics.fan.processing.FanProcessingType;
import com.simibubi.create.content.kinetics.mechanicalArm.ArmInteractionPointType;
import com.simibubi.create.content.logistics.item.filter.attribute.ItemAttributeType;
@ -32,6 +36,9 @@ public class CreateBuiltInRegistries {
public static final Registry<MountedItemStorageType<?>> MOUNTED_ITEM_STORAGE_TYPE = withIntrusiveHolders(CreateRegistries.MOUNTED_ITEM_STORAGE_TYPE);
public static final Registry<MountedFluidStorageType<?>> MOUNTED_FLUID_STORAGE_TYPE = simple(CreateRegistries.MOUNTED_FLUID_STORAGE_TYPE);
public static final Registry<ContraptionType> CONTRAPTION_TYPE = withIntrusiveHolders(CreateRegistries.CONTRAPTION_TYPE);
public static final Registry<Codec<? extends PotatoProjectileRenderMode>> POTATO_PROJECTILE_RENDER_MODE = simple(CreateRegistries.POTATO_PROJECTILE_RENDER_MODE);
public static final Registry<Codec<? extends PotatoProjectileEntityHitAction>> POTATO_PROJECTILE_ENTITY_HIT_ACTION = simple(CreateRegistries.POTATO_PROJECTILE_ENTITY_HIT_ACTION);
public static final Registry<Codec<? extends PotatoProjectileBlockHitAction>> POTATO_PROJECTILE_BLOCK_HIT_ACTION = simple(CreateRegistries.POTATO_PROJECTILE_BLOCK_HIT_ACTION);
private static <T> Registry<T> simple(ResourceKey<Registry<T>> key) {
return register(key, new MappedRegistry<>(key, Lifecycle.stable(), false));

View file

@ -1,11 +1,18 @@
package com.simibubi.create.api.registry;
import org.jetbrains.annotations.ApiStatus.Internal;
import com.mojang.serialization.Codec;
import com.simibubi.create.Create;
import com.simibubi.create.api.behaviour.display.DisplaySource;
import com.simibubi.create.api.behaviour.display.DisplayTarget;
import com.simibubi.create.api.contraption.ContraptionType;
import com.simibubi.create.api.contraption.storage.fluid.MountedFluidStorageType;
import com.simibubi.create.api.contraption.storage.item.MountedItemStorageType;
import com.simibubi.create.api.equipment.potatoCannon.PotatoCannonProjectileType;
import com.simibubi.create.api.equipment.potatoCannon.PotatoProjectileBlockHitAction;
import com.simibubi.create.api.equipment.potatoCannon.PotatoProjectileEntityHitAction;
import com.simibubi.create.api.equipment.potatoCannon.PotatoProjectileRenderMode;
import com.simibubi.create.content.kinetics.fan.processing.FanProcessingType;
import com.simibubi.create.content.kinetics.mechanicalArm.ArmInteractionPointType;
import com.simibubi.create.content.logistics.item.filter.attribute.ItemAttributeType;
@ -13,10 +20,17 @@ import com.simibubi.create.content.logistics.item.filter.attribute.ItemAttribute
import net.minecraft.core.Registry;
import net.minecraft.resources.ResourceKey;
import net.minecraftforge.eventbus.api.SubscribeEvent;
import net.minecraftforge.fml.common.Mod.EventBusSubscriber;
import net.minecraftforge.fml.common.Mod.EventBusSubscriber.Bus;
import net.minecraftforge.registries.DataPackRegistryEvent;
/**
* Keys for registries added by Create.
*
* @see CreateBuiltInRegistries
*/
@EventBusSubscriber(bus = Bus.MOD)
public class CreateRegistries {
public static final ResourceKey<Registry<ArmInteractionPointType>> ARM_INTERACTION_POINT_TYPE = key("arm_interaction_point_type");
public static final ResourceKey<Registry<FanProcessingType>> FAN_PROCESSING_TYPE = key("fan_processing_type");
@ -26,8 +40,22 @@ public class CreateRegistries {
public static final ResourceKey<Registry<MountedItemStorageType<?>>> MOUNTED_ITEM_STORAGE_TYPE = key("mounted_item_storage_type");
public static final ResourceKey<Registry<MountedFluidStorageType<?>>> MOUNTED_FLUID_STORAGE_TYPE = key("mounted_fluid_storage_type");
public static final ResourceKey<Registry<ContraptionType>> CONTRAPTION_TYPE = key("contraption_type");
public static final ResourceKey<Registry<PotatoCannonProjectileType>> POTATO_PROJECTILE_TYPE = key("potato_projectile/type");
public static final ResourceKey<Registry<Codec<? extends PotatoProjectileRenderMode>>> POTATO_PROJECTILE_RENDER_MODE = key("potato_projectile/render_mode");
public static final ResourceKey<Registry<Codec<? extends PotatoProjectileEntityHitAction>>> POTATO_PROJECTILE_ENTITY_HIT_ACTION = key("potato_projectile/entity_hit_action");
public static final ResourceKey<Registry<Codec<? extends PotatoProjectileBlockHitAction>>> POTATO_PROJECTILE_BLOCK_HIT_ACTION = key("potato_projectile/block_hit_action");
private static <T> ResourceKey<Registry<T>> key(String name) {
return ResourceKey.createRegistryKey(Create.asResource(name));
}
@Internal
@SubscribeEvent
public static void registerDatapackRegistries(DataPackRegistryEvent.NewRegistry event) {
event.dataPackRegistry(
POTATO_PROJECTILE_TYPE,
PotatoCannonProjectileType.CODEC,
PotatoCannonProjectileType.CODEC
);
}
}

View file

@ -245,6 +245,7 @@ public class ExtendoGripItem extends Item {
if (lastActiveDamageSource == null)
return;
Entity entity = lastActiveDamageSource.getDirectEntity();
lastActiveDamageSource = null;
if (!(entity instanceof Player player))
return;
if (!isHoldingExtendoGrip(player))

View file

@ -0,0 +1,21 @@
package com.simibubi.create.content.equipment.potatoCannon;
import com.mojang.serialization.Codec;
import com.simibubi.create.Create;
import com.simibubi.create.api.equipment.potatoCannon.PotatoProjectileBlockHitAction;
import com.simibubi.create.api.equipment.potatoCannon.PotatoProjectileBlockHitAction.PlaceBlockOnGround;
import com.simibubi.create.api.equipment.potatoCannon.PotatoProjectileBlockHitAction.PlantCrop;
import com.simibubi.create.api.registry.CreateBuiltInRegistries;
import net.minecraft.core.Registry;
public class AllPotatoProjectileBlockHitActions {
public static void init() {
register("plant_crop", PlantCrop.CODEC);
register("place_block_on_ground", PlaceBlockOnGround.CODEC);
}
private static void register(String name, Codec<? extends PotatoProjectileBlockHitAction> codec) {
Registry.register(CreateBuiltInRegistries.POTATO_PROJECTILE_BLOCK_HIT_ACTION, Create.asResource(name), codec);
}
}

View file

@ -0,0 +1,29 @@
package com.simibubi.create.content.equipment.potatoCannon;
import com.mojang.serialization.Codec;
import com.simibubi.create.Create;
import com.simibubi.create.api.equipment.potatoCannon.PotatoProjectileEntityHitAction;
import com.simibubi.create.api.equipment.potatoCannon.PotatoProjectileEntityHitAction.ChorusTeleport;
import com.simibubi.create.api.equipment.potatoCannon.PotatoProjectileEntityHitAction.CureZombieVillager;
import com.simibubi.create.api.equipment.potatoCannon.PotatoProjectileEntityHitAction.FoodEffects;
import com.simibubi.create.api.equipment.potatoCannon.PotatoProjectileEntityHitAction.PotionEffect;
import com.simibubi.create.api.equipment.potatoCannon.PotatoProjectileEntityHitAction.SetOnFire;
import com.simibubi.create.api.equipment.potatoCannon.PotatoProjectileEntityHitAction.SuspiciousStew;
import com.simibubi.create.api.registry.CreateBuiltInRegistries;
import net.minecraft.core.Registry;
public class AllPotatoProjectileEntityHitActions {
public static void init() {
register("set_on_fire", SetOnFire.CODEC);
register("potion_effect", PotionEffect.CODEC);
register("food_effects", FoodEffects.CODEC);
register("chorus_teleport", ChorusTeleport.CODEC);
register("cure_zombie_villager", CureZombieVillager.CODEC);
register("suspicious_stew", SuspiciousStew.CODEC);
}
private static void register(String name, Codec<? extends PotatoProjectileEntityHitAction> codec) {
Registry.register(CreateBuiltInRegistries.POTATO_PROJECTILE_ENTITY_HIT_ACTION, Create.asResource(name), codec);
}
}

View file

@ -0,0 +1,25 @@
package com.simibubi.create.content.equipment.potatoCannon;
import com.mojang.serialization.Codec;
import com.simibubi.create.Create;
import com.simibubi.create.api.equipment.potatoCannon.PotatoProjectileRenderMode;
import com.simibubi.create.api.equipment.potatoCannon.PotatoProjectileRenderMode.Billboard;
import com.simibubi.create.api.equipment.potatoCannon.PotatoProjectileRenderMode.StuckToEntity;
import com.simibubi.create.api.equipment.potatoCannon.PotatoProjectileRenderMode.TowardMotion;
import com.simibubi.create.api.equipment.potatoCannon.PotatoProjectileRenderMode.Tumble;
import com.simibubi.create.api.registry.CreateBuiltInRegistries;
import net.minecraft.core.Registry;
public class AllPotatoProjectileRenderModes {
public static void init() {
register("billboard", Billboard.CODEC);
register("tumble", Tumble.CODEC);
register("toward_motion", TowardMotion.CODEC);
register("stuck_to_entity", StuckToEntity.CODEC);
}
private static void register(String name, Codec<? extends PotatoProjectileRenderMode> codec) {
Registry.register(CreateBuiltInRegistries.POTATO_PROJECTILE_RENDER_MODE, Create.asResource(name), codec);
}
}

View file

@ -0,0 +1,266 @@
package com.simibubi.create.content.equipment.potatoCannon;
import com.simibubi.create.AllItems;
import com.simibubi.create.Create;
import com.simibubi.create.api.equipment.potatoCannon.PotatoCannonProjectileType;
import com.simibubi.create.api.equipment.potatoCannon.PotatoProjectileBlockHitAction.PlaceBlockOnGround;
import com.simibubi.create.api.equipment.potatoCannon.PotatoProjectileBlockHitAction.PlantCrop;
import com.simibubi.create.api.equipment.potatoCannon.PotatoProjectileEntityHitAction.ChorusTeleport;
import com.simibubi.create.api.equipment.potatoCannon.PotatoProjectileEntityHitAction.CureZombieVillager;
import com.simibubi.create.api.equipment.potatoCannon.PotatoProjectileEntityHitAction.FoodEffects;
import com.simibubi.create.api.equipment.potatoCannon.PotatoProjectileEntityHitAction.PotionEffect;
import com.simibubi.create.api.equipment.potatoCannon.PotatoProjectileEntityHitAction.SetOnFire;
import com.simibubi.create.api.equipment.potatoCannon.PotatoProjectileEntityHitAction.SuspiciousStew;
import com.simibubi.create.api.registry.CreateRegistries;
import net.minecraft.data.worldgen.BootstapContext;
import net.minecraft.resources.ResourceKey;
import net.minecraft.world.effect.MobEffects;
import net.minecraft.world.food.Foods;
import net.minecraft.world.item.Items;
import net.minecraft.world.level.block.Blocks;
public class AllPotatoProjectileTypes {
public static ResourceKey<PotatoCannonProjectileType> FALLBACK = ResourceKey.create(CreateRegistries.POTATO_PROJECTILE_TYPE, Create.asResource("fallback"));
public static void bootstrap(BootstapContext<PotatoCannonProjectileType> ctx) {
create("fallback")
.damage(0)
.register(ctx);
create("potato")
.damage(5)
.reloadTicks(15)
.velocity(1.25f)
.knockback(1.5f)
.renderTumbling()
.onBlockHit(new PlantCrop(Blocks.POTATOES))
.registerAndAssign(ctx, Items.POTATO);
create("baked_potato")
.damage(5)
.reloadTicks(15)
.velocity(1.25f)
.knockback(0.5f)
.renderTumbling()
.preEntityHit(SetOnFire.seconds(3))
.registerAndAssign(ctx, Items.BAKED_POTATO);
create("carrot")
.damage(4)
.reloadTicks(12)
.velocity(1.45f)
.knockback(0.3f)
.renderTowardMotion(140, 1)
.soundPitch(1.5f)
.onBlockHit(new PlantCrop(Blocks.CARROTS))
.registerAndAssign(ctx, Items.CARROT);
create("golden_carrot")
.damage(12)
.reloadTicks(15)
.velocity(1.45f)
.knockback(0.5f)
.renderTowardMotion(140, 2)
.soundPitch(1.5f)
.registerAndAssign(ctx, Items.GOLDEN_CARROT);
create("sweet_berry")
.damage(3)
.reloadTicks(10)
.knockback(0.1f)
.velocity(1.05f)
.renderTumbling()
.splitInto(3)
.soundPitch(1.25f)
.registerAndAssign(ctx, Items.SWEET_BERRIES);
create("glow_berry")
.damage(2)
.reloadTicks(10)
.knockback(0.05f)
.velocity(1.05f)
.renderTumbling()
.splitInto(2)
.soundPitch(1.2f)
.onEntityHit(new PotionEffect(MobEffects.GLOWING, 1, 200, false))
.registerAndAssign(ctx, Items.GLOW_BERRIES);
create("chocolate_berry")
.damage(4)
.reloadTicks(10)
.knockback(0.2f)
.velocity(1.05f)
.renderTumbling()
.splitInto(3)
.soundPitch(1.25f)
.registerAndAssign(ctx, AllItems.CHOCOLATE_BERRIES.get());
create("poison_potato")
.damage(5)
.reloadTicks(15)
.knockback(0.05f)
.velocity(1.25f)
.renderTumbling()
.onEntityHit(new PotionEffect(MobEffects.POISON, 1, 160, true))
.registerAndAssign(ctx, Items.POISONOUS_POTATO);
create("chorus_fruit")
.damage(3)
.reloadTicks(15)
.velocity(1.20f)
.knockback(0.05f)
.renderTumbling()
.onEntityHit(new ChorusTeleport(20))
.registerAndAssign(ctx, Items.CHORUS_FRUIT);
create("apple")
.damage(5)
.reloadTicks(10)
.velocity(1.45f)
.knockback(0.5f)
.renderTumbling()
.soundPitch(1.1f)
.registerAndAssign(ctx, Items.APPLE);
create("honeyed_apple")
.damage(6)
.reloadTicks(15)
.velocity(1.35f)
.knockback(0.1f)
.renderTumbling()
.soundPitch(1.1f)
.onEntityHit(new PotionEffect(MobEffects.MOVEMENT_SLOWDOWN, 2, 160, true))
.registerAndAssign(ctx, AllItems.HONEYED_APPLE.get());
create("golden_apple")
.damage(1)
.reloadTicks(100)
.velocity(1.45f)
.knockback(0.05f)
.renderTumbling()
.soundPitch(1.1f)
.onEntityHit(CureZombieVillager.INSTANCE)
.registerAndAssign(ctx, Items.GOLDEN_APPLE);
create("enchanted_golden_apple")
.damage(1)
.reloadTicks(100)
.velocity(1.45f)
.knockback(0.05f)
.renderTumbling()
.soundPitch(1.1f)
.onEntityHit(new FoodEffects(Foods.ENCHANTED_GOLDEN_APPLE, false))
.registerAndAssign(ctx, Items.ENCHANTED_GOLDEN_APPLE);
create("beetroot")
.damage(2)
.reloadTicks(5)
.velocity(1.6f)
.knockback(0.1f)
.renderTowardMotion(140, 2)
.soundPitch(1.6f)
.registerAndAssign(ctx, Items.BEETROOT);
create("melon_slice")
.damage(3)
.reloadTicks(8)
.knockback(0.1f)
.velocity(1.45f)
.renderTumbling()
.soundPitch(1.5f)
.registerAndAssign(ctx, Items.MELON_SLICE);
create("glistering_melon")
.damage(5)
.reloadTicks(8)
.knockback(0.1f)
.velocity(1.45f)
.renderTumbling()
.soundPitch(1.5f)
.onEntityHit(new PotionEffect(MobEffects.GLOWING, 1, 100, true))
.registerAndAssign(ctx, Items.GLISTERING_MELON_SLICE);
create("melon_block")
.damage(8)
.reloadTicks(20)
.knockback(2.0f)
.velocity(0.95f)
.renderTumbling()
.soundPitch(0.9f)
.onBlockHit(new PlaceBlockOnGround(Blocks.MELON))
.registerAndAssign(ctx, Blocks.MELON);
create("pumpkin_block")
.damage(6)
.reloadTicks(15)
.knockback(2.0f)
.velocity(0.95f)
.renderTumbling()
.soundPitch(0.9f)
.onBlockHit(new PlaceBlockOnGround(Blocks.PUMPKIN))
.registerAndAssign(ctx, Blocks.PUMPKIN);
create("pumpkin_pie")
.damage(7)
.reloadTicks(15)
.knockback(0.05f)
.velocity(1.1f)
.renderTumbling()
.sticky()
.soundPitch(1.1f)
.registerAndAssign(ctx, Items.PUMPKIN_PIE);
create("cake")
.damage(8)
.reloadTicks(15)
.knockback(0.1f)
.velocity(1.1f)
.renderTumbling()
.sticky()
.registerAndAssign(ctx, Items.CAKE);
create("blaze_cake")
.damage(15)
.reloadTicks(20)
.knockback(0.3f)
.velocity(1.1f)
.renderTumbling()
.sticky()
.preEntityHit(SetOnFire.seconds(12))
.registerAndAssign(ctx, AllItems.BLAZE_CAKE.get());
create("fish")
.damage(4)
.knockback(0.6f)
.velocity(1.3f)
.renderTowardMotion(140, 1)
.sticky()
.soundPitch(1.3f)
.registerAndAssign(ctx, Items.COD, Items.COOKED_COD, Items.SALMON, Items.COOKED_SALMON, Items.TROPICAL_FISH);
create("pufferfish")
.damage(4)
.knockback(0.4f)
.velocity(1.1f)
.renderTowardMotion(140, 1)
.sticky()
.onEntityHit(new FoodEffects(Foods.PUFFERFISH, false))
.soundPitch(1.1f)
.registerAndAssign(ctx, Items.PUFFERFISH);
create("suspicious_stew")
.damage(3)
.reloadTicks(40)
.knockback(0.2f)
.velocity(0.8f)
.renderTowardMotion(140, 1)
.dropStack(Items.BOWL.getDefaultInstance())
.onEntityHit(SuspiciousStew.INSTANCE)
.registerAndAssign(ctx, Items.SUSPICIOUS_STEW);
}
private static PotatoCannonProjectileType.Builder create(String name) {
return new PotatoCannonProjectileType.Builder(Create.asResource(name));
}
}

View file

@ -1,420 +0,0 @@
package com.simibubi.create.content.equipment.potatoCannon;
import java.util.UUID;
import java.util.function.BiPredicate;
import java.util.function.Predicate;
import java.util.function.Supplier;
import com.mojang.authlib.GameProfile;
import com.mojang.datafixers.util.Pair;
import com.simibubi.create.AllItems;
import com.simibubi.create.Create;
import com.simibubi.create.foundation.mixin.accessor.FallingBlockEntityAccessor;
import net.createmod.catnip.data.WorldAttached;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.sounds.SoundEvent;
import net.minecraft.sounds.SoundEvents;
import net.minecraft.sounds.SoundSource;
import net.minecraft.util.Mth;
import net.minecraft.world.InteractionHand;
import net.minecraft.world.effect.MobEffect;
import net.minecraft.world.effect.MobEffectInstance;
import net.minecraft.world.effect.MobEffects;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.LivingEntity;
import net.minecraft.world.entity.animal.Fox;
import net.minecraft.world.entity.item.FallingBlockEntity;
import net.minecraft.world.entity.monster.ZombieVillager;
import net.minecraft.world.food.FoodProperties;
import net.minecraft.world.food.Foods;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.Items;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.LevelAccessor;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.Blocks;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.phys.BlockHitResult;
import net.minecraft.world.phys.EntityHitResult;
import net.minecraft.world.phys.Vec3;
import net.minecraftforge.common.IPlantable;
import net.minecraftforge.common.util.FakePlayer;
import net.minecraftforge.event.ForgeEventFactory;
import net.minecraftforge.event.entity.EntityTeleportEvent;
import net.minecraftforge.registries.ForgeRegistries;
public class BuiltinPotatoProjectileTypes {
private static final GameProfile ZOMBIE_CONVERTER_NAME =
new GameProfile(UUID.fromString("be12d3dc-27d3-4992-8c97-66be53fd49c5"), "Converter");
private static final WorldAttached<FakePlayer> ZOMBIE_CONVERTERS =
new WorldAttached<>(w -> new FakePlayer((ServerLevel) w, ZOMBIE_CONVERTER_NAME));
public static final PotatoCannonProjectileType
FALLBACK = create("fallback").damage(0)
.register(),
POTATO = create("potato").damage(5)
.reloadTicks(15)
.velocity(1.25f)
.knockback(1.5f)
.renderTumbling()
.onBlockHit(plantCrop(Blocks.POTATOES))
.registerAndAssign(Items.POTATO),
BAKED_POTATO = create("baked_potato").damage(5)
.reloadTicks(15)
.velocity(1.25f)
.knockback(0.5f)
.renderTumbling()
.preEntityHit(setFire(3))
.registerAndAssign(Items.BAKED_POTATO),
CARROT = create("carrot").damage(4)
.reloadTicks(12)
.velocity(1.45f)
.knockback(0.3f)
.renderTowardMotion(140, 1)
.soundPitch(1.5f)
.onBlockHit(plantCrop(Blocks.CARROTS))
.registerAndAssign(Items.CARROT),
GOLDEN_CARROT = create("golden_carrot").damage(12)
.reloadTicks(15)
.velocity(1.45f)
.knockback(0.5f)
.renderTowardMotion(140, 2)
.soundPitch(1.5f)
.registerAndAssign(Items.GOLDEN_CARROT),
SWEET_BERRIES = create("sweet_berry").damage(3)
.reloadTicks(10)
.knockback(0.1f)
.velocity(1.05f)
.renderTumbling()
.splitInto(3)
.soundPitch(1.25f)
.registerAndAssign(Items.SWEET_BERRIES),
GLOW_BERRIES = create("glow_berry").damage(2)
.reloadTicks(10)
.knockback(0.05f)
.velocity(1.05f)
.renderTumbling()
.splitInto(2)
.soundPitch(1.2f)
.onEntityHit(potion(MobEffects.GLOWING, 1, 200, false))
.registerAndAssign(Items.GLOW_BERRIES),
CHOCOLATE_BERRIES = create("chocolate_berry").damage(4)
.reloadTicks(10)
.knockback(0.2f)
.velocity(1.05f)
.renderTumbling()
.splitInto(3)
.soundPitch(1.25f)
.registerAndAssign(AllItems.CHOCOLATE_BERRIES.get()),
POISON_POTATO = create("poison_potato").damage(5)
.reloadTicks(15)
.knockback(0.05f)
.velocity(1.25f)
.renderTumbling()
.onEntityHit(potion(MobEffects.POISON, 1, 160, true))
.registerAndAssign(Items.POISONOUS_POTATO),
CHORUS_FRUIT = create("chorus_fruit").damage(3)
.reloadTicks(15)
.velocity(1.20f)
.knockback(0.05f)
.renderTumbling()
.onEntityHit(chorusTeleport(20))
.registerAndAssign(Items.CHORUS_FRUIT),
APPLE = create("apple").damage(5)
.reloadTicks(10)
.velocity(1.45f)
.knockback(0.5f)
.renderTumbling()
.soundPitch(1.1f)
.registerAndAssign(Items.APPLE),
HONEYED_APPLE = create("honeyed_apple").damage(6)
.reloadTicks(15)
.velocity(1.35f)
.knockback(0.1f)
.renderTumbling()
.soundPitch(1.1f)
.onEntityHit(potion(MobEffects.MOVEMENT_SLOWDOWN, 2, 160, true))
.registerAndAssign(AllItems.HONEYED_APPLE.get()),
GOLDEN_APPLE = create("golden_apple").damage(1)
.reloadTicks(100)
.velocity(1.45f)
.knockback(0.05f)
.renderTumbling()
.soundPitch(1.1f)
.onEntityHit(ray -> {
Entity entity = ray.getEntity();
Level world = entity.level();
if (!(entity instanceof ZombieVillager) || !((ZombieVillager) entity).hasEffect(MobEffects.WEAKNESS))
return foodEffects(Foods.GOLDEN_APPLE, false).test(ray);
if (world.isClientSide)
return false;
FakePlayer dummy = ZOMBIE_CONVERTERS.get(world);
dummy.setItemInHand(InteractionHand.MAIN_HAND, new ItemStack(Items.GOLDEN_APPLE, 1));
((ZombieVillager) entity).mobInteract(dummy, InteractionHand.MAIN_HAND);
return true;
})
.registerAndAssign(Items.GOLDEN_APPLE),
ENCHANTED_GOLDEN_APPLE = create("enchanted_golden_apple").damage(1)
.reloadTicks(100)
.velocity(1.45f)
.knockback(0.05f)
.renderTumbling()
.soundPitch(1.1f)
.onEntityHit(foodEffects(Foods.ENCHANTED_GOLDEN_APPLE, false))
.registerAndAssign(Items.ENCHANTED_GOLDEN_APPLE),
BEETROOT = create("beetroot").damage(2)
.reloadTicks(5)
.velocity(1.6f)
.knockback(0.1f)
.renderTowardMotion(140, 2)
.soundPitch(1.6f)
.registerAndAssign(Items.BEETROOT),
MELON_SLICE = create("melon_slice").damage(3)
.reloadTicks(8)
.knockback(0.1f)
.velocity(1.45f)
.renderTumbling()
.soundPitch(1.5f)
.registerAndAssign(Items.MELON_SLICE),
GLISTERING_MELON = create("glistering_melon").damage(5)
.reloadTicks(8)
.knockback(0.1f)
.velocity(1.45f)
.renderTumbling()
.soundPitch(1.5f)
.onEntityHit(potion(MobEffects.GLOWING, 1, 100, true))
.registerAndAssign(Items.GLISTERING_MELON_SLICE),
MELON_BLOCK = create("melon_block").damage(8)
.reloadTicks(20)
.knockback(2.0f)
.velocity(0.95f)
.renderTumbling()
.soundPitch(0.9f)
.onBlockHit(placeBlockOnGround(Blocks.MELON))
.registerAndAssign(Blocks.MELON),
PUMPKIN_BLOCK = create("pumpkin_block").damage(6)
.reloadTicks(15)
.knockback(2.0f)
.velocity(0.95f)
.renderTumbling()
.soundPitch(0.9f)
.onBlockHit(placeBlockOnGround(Blocks.PUMPKIN))
.registerAndAssign(Blocks.PUMPKIN),
PUMPKIN_PIE = create("pumpkin_pie").damage(7)
.reloadTicks(15)
.knockback(0.05f)
.velocity(1.1f)
.renderTumbling()
.sticky()
.soundPitch(1.1f)
.registerAndAssign(Items.PUMPKIN_PIE),
CAKE = create("cake").damage(8)
.reloadTicks(15)
.knockback(0.1f)
.velocity(1.1f)
.renderTumbling()
.sticky()
.soundPitch(1.0f)
.registerAndAssign(Items.CAKE),
BLAZE_CAKE = create("blaze_cake").damage(15)
.reloadTicks(20)
.knockback(0.3f)
.velocity(1.1f)
.renderTumbling()
.sticky()
.preEntityHit(setFire(12))
.soundPitch(1.0f)
.registerAndAssign(AllItems.BLAZE_CAKE.get());
private static PotatoCannonProjectileType.Builder create(String name) {
return new PotatoCannonProjectileType.Builder(Create.asResource(name));
}
private static Predicate<EntityHitResult> setFire(int seconds) {
return ray -> {
ray.getEntity()
.setSecondsOnFire(seconds);
return false;
};
}
private static Predicate<EntityHitResult> potion(MobEffect effect, int level, int ticks, boolean recoverable) {
return ray -> {
Entity entity = ray.getEntity();
if (entity.level().isClientSide)
return true;
if (entity instanceof LivingEntity)
applyEffect((LivingEntity) entity, new MobEffectInstance(effect, ticks, level - 1));
return !recoverable;
};
}
private static Predicate<EntityHitResult> foodEffects(FoodProperties food, boolean recoverable) {
return ray -> {
Entity entity = ray.getEntity();
if (entity.level().isClientSide)
return true;
if (entity instanceof LivingEntity) {
for (Pair<MobEffectInstance, Float> effect : food.getEffects()) {
if (Create.RANDOM.nextFloat() < effect.getSecond())
applyEffect((LivingEntity) entity, new MobEffectInstance(effect.getFirst()));
}
}
return !recoverable;
};
}
private static void applyEffect(LivingEntity entity, MobEffectInstance effect) {
if (effect.getEffect()
.isInstantenous())
effect.getEffect()
.applyInstantenousEffect(null, null, entity, effect.getDuration(), 1.0);
else
entity.addEffect(effect);
}
private static BiPredicate<LevelAccessor, BlockHitResult> plantCrop(Supplier<? extends Block> cropBlock) {
return (world, ray) -> {
if (world.isClientSide())
return true;
BlockPos hitPos = ray.getBlockPos();
if (world instanceof Level l && !l.isLoaded(hitPos))
return true;
Direction face = ray.getDirection();
if (face != Direction.UP)
return false;
BlockPos placePos = hitPos.relative(face);
if (!world.getBlockState(placePos)
.canBeReplaced())
return false;
if (!(cropBlock.get() instanceof IPlantable))
return false;
BlockState blockState = world.getBlockState(hitPos);
if (!blockState.canSustainPlant(world, hitPos, face, (IPlantable) cropBlock.get()))
return false;
world.setBlock(placePos, cropBlock.get()
.defaultBlockState(), 3);
return true;
};
}
private static BiPredicate<LevelAccessor, BlockHitResult> plantCrop(Block cropBlock) {
return plantCrop(ForgeRegistries.BLOCKS.getDelegateOrThrow(cropBlock));
}
private static BiPredicate<LevelAccessor, BlockHitResult> placeBlockOnGround(
Supplier<? extends Block> block) {
return (world, ray) -> {
if (world.isClientSide())
return true;
BlockPos hitPos = ray.getBlockPos();
if (world instanceof Level l && !l.isLoaded(hitPos))
return true;
Direction face = ray.getDirection();
BlockPos placePos = hitPos.relative(face);
if (!world.getBlockState(placePos)
.canBeReplaced())
return false;
if (face == Direction.UP) {
world.setBlock(placePos, block.get()
.defaultBlockState(), 3);
} else if (world instanceof Level level) {
double y = ray.getLocation().y - 0.5;
if (!world.isEmptyBlock(placePos.above()))
y = Math.min(y, placePos.getY());
if (!world.isEmptyBlock(placePos.below()))
y = Math.max(y, placePos.getY());
FallingBlockEntity falling = FallingBlockEntityAccessor.create$callInit(level, placePos.getX() + 0.5, y,
placePos.getZ() + 0.5, block.get().defaultBlockState());
falling.time = 1;
world.addFreshEntity(falling);
}
return true;
};
}
private static BiPredicate<LevelAccessor, BlockHitResult> placeBlockOnGround(Block block) {
return placeBlockOnGround(ForgeRegistries.BLOCKS.getDelegateOrThrow(block));
}
private static Predicate<EntityHitResult> chorusTeleport(double teleportDiameter) {
return ray -> {
Entity entity = ray.getEntity();
Level world = entity.getCommandSenderWorld();
if (world.isClientSide)
return true;
if (!(entity instanceof LivingEntity livingEntity))
return false;
double entityX = livingEntity.getX();
double entityY = livingEntity.getY();
double entityZ = livingEntity.getZ();
for (int teleportTry = 0; teleportTry < 16; ++teleportTry) {
double teleportX = entityX + (livingEntity.getRandom()
.nextDouble() - 0.5D) * teleportDiameter;
double teleportY = Mth.clamp(entityY + (livingEntity.getRandom()
.nextInt((int) teleportDiameter) - (int) (teleportDiameter / 2)), 0.0D, world.getHeight() - 1);
double teleportZ = entityZ + (livingEntity.getRandom()
.nextDouble() - 0.5D) * teleportDiameter;
EntityTeleportEvent.ChorusFruit event =
ForgeEventFactory.onChorusFruitTeleport(livingEntity, teleportX, teleportY, teleportZ);
if (event.isCanceled())
return false;
if (livingEntity.randomTeleport(event.getTargetX(), event.getTargetY(), event.getTargetZ(), true)) {
if (livingEntity.isPassenger())
livingEntity.stopRiding();
SoundEvent soundevent =
livingEntity instanceof Fox ? SoundEvents.FOX_TELEPORT : SoundEvents.CHORUS_FRUIT_TELEPORT;
world.playSound(null, entityX, entityY, entityZ, soundevent, SoundSource.PLAYERS, 1.0F, 1.0F);
livingEntity.playSound(soundevent, 1.0F, 1.0F);
livingEntity.setDeltaMovement(Vec3.ZERO);
return true;
}
}
return false;
};
}
public static void register() {
}
}

View file

@ -9,8 +9,9 @@ import org.jetbrains.annotations.Nullable;
import com.simibubi.create.AllEnchantments;
import com.simibubi.create.AllEntityTypes;
import com.simibubi.create.Create;
import com.simibubi.create.CreateClient;
import com.simibubi.create.api.equipment.potatoCannon.PotatoCannonProjectileType;
import com.simibubi.create.api.registry.CreateRegistries;
import com.simibubi.create.content.equipment.armor.BacktankUtil;
import com.simibubi.create.content.equipment.zapper.ShootableGadgetItemMethods;
import com.simibubi.create.foundation.item.CustomArmPoseItem;
@ -50,6 +51,7 @@ import net.minecraft.world.phys.Vec3;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.api.distmarker.OnlyIn;
import net.minecraftforge.client.extensions.common.IClientItemExtensions;
import net.minecraftforge.server.ServerLifecycleHooks;
public class PotatoCannonItem extends ProjectileWeaponItem implements CustomArmPoseItem {
@ -109,14 +111,13 @@ public class PotatoCannonItem extends ProjectileWeaponItem implements CustomArmP
}
@Override
public InteractionResultHolder<ItemStack> use(Level world, Player player, InteractionHand hand) {
public InteractionResultHolder<ItemStack> use(Level level, Player player, InteractionHand hand) {
ItemStack stack = player.getItemInHand(hand);
return findAmmoInInventory(world, player, stack).map(itemStack -> {
return findAmmoInInventory(level, player, stack).map(itemStack -> {
if (ShootableGadgetItemMethods.shouldSwap(player, stack, hand, this::isCannon))
return InteractionResultHolder.fail(stack);
if (world.isClientSide) {
if (level.isClientSide) {
CreateClient.POTATO_CANNON_RENDER_HANDLER.dontAnimateItem(hand);
return InteractionResultHolder.success(stack);
}
@ -128,28 +129,33 @@ public class PotatoCannonItem extends ProjectileWeaponItem implements CustomArmP
.subtract(player.position()
.add(0, player.getEyeHeight(), 0));
PotatoCannonProjectileType projectileType = PotatoProjectileTypeManager.getTypeForStack(itemStack)
.orElse(BuiltinPotatoProjectileTypes.FALLBACK);
PotatoCannonProjectileType projectileType = PotatoCannonProjectileType.getTypeForStack(level, itemStack)
.orElseGet(() ->
level.registryAccess()
.lookupOrThrow(CreateRegistries.POTATO_PROJECTILE_TYPE)
.getOrThrow(AllPotatoProjectileTypes.FALLBACK)
.value()
);
Vec3 lookVec = player.getLookAngle();
Vec3 motion = lookVec.add(correction)
.normalize()
.scale(2)
.scale(projectileType.getVelocityMultiplier());
.scale(projectileType.velocityMultiplier());
float soundPitch = projectileType.getSoundPitch() + (Create.RANDOM.nextFloat() - .5f) / 4f;
float soundPitch = projectileType.soundPitch() + (level.getRandom().nextFloat() - .5f) / 4f;
boolean spray = projectileType.getSplit() > 1;
Vec3 sprayBase = VecHelper.rotate(new Vec3(0, 0.1, 0), 360 * Create.RANDOM.nextFloat(), Axis.Z);
float sprayChange = 360f / projectileType.getSplit();
boolean spray = projectileType.split() > 1;
Vec3 sprayBase = VecHelper.rotate(new Vec3(0, 0.1, 0), 360 * level.getRandom().nextFloat(), Axis.Z);
float sprayChange = 360f / projectileType.split();
for (int i = 0; i < projectileType.getSplit(); i++) {
PotatoProjectileEntity projectile = AllEntityTypes.POTATO_PROJECTILE.create(world);
for (int i = 0; i < projectileType.split(); i++) {
PotatoProjectileEntity projectile = AllEntityTypes.POTATO_PROJECTILE.create(level);
projectile.setItem(itemStack);
projectile.setEnchantmentEffectsFromCannon(stack);
Vec3 splitMotion = motion;
if (spray) {
float imperfection = 40 * (Create.RANDOM.nextFloat() - 0.5f);
float imperfection = 40 * (level.getRandom().nextFloat() - 0.5f);
Vec3 sprayOffset = VecHelper.rotate(sprayBase, i * sprayChange + imperfection, Axis.Z);
splitMotion = splitMotion.add(VecHelper.lookAt(sprayOffset, motion));
}
@ -160,7 +166,7 @@ public class PotatoCannonItem extends ProjectileWeaponItem implements CustomArmP
projectile.setPos(barrelPos.x, barrelPos.y, barrelPos.z);
projectile.setDeltaMovement(splitMotion);
projectile.setOwner(player);
world.addFreshEntity(projectile);
level.addFreshEntity(projectile);
}
if (!player.isCreative()) {
@ -173,8 +179,8 @@ public class PotatoCannonItem extends ProjectileWeaponItem implements CustomArmP
stack.hurtAndBreak(1, player, p -> p.broadcastBreakEvent(hand));
Integer cooldown =
findAmmoInInventory(world, player, stack).flatMap(PotatoProjectileTypeManager::getTypeForStack)
.map(PotatoCannonProjectileType::getReloadTicks)
findAmmoInInventory(level, player, stack).flatMap(i -> PotatoCannonProjectileType.getTypeForStack(level, i))
.map(potatoCannonProjectileType -> potatoCannonProjectileType.reloadTicks())
.orElse(10);
ShootableGadgetItemMethods.applyCooldown(player, stack, hand, this::isCannon, cooldown);
@ -190,9 +196,9 @@ public class PotatoCannonItem extends ProjectileWeaponItem implements CustomArmP
return slotChanged || newStack.getItem() != oldStack.getItem();
}
private Optional<ItemStack> findAmmoInInventory(Level world, Player player, ItemStack held) {
private Optional<ItemStack> findAmmoInInventory(Level level, Player player, ItemStack held) {
ItemStack findAmmo = player.getProjectile(held);
return PotatoProjectileTypeManager.getTypeForStack(findAmmo)
return PotatoCannonProjectileType.getTypeForStack(level, findAmmo)
.map($ -> findAmmo);
}
@ -207,7 +213,7 @@ public class PotatoCannonItem extends ProjectileWeaponItem implements CustomArmP
if (player == null)
return Optional.empty();
ItemStack findAmmo = player.getProjectile(cannon);
Optional<ItemStack> found = PotatoProjectileTypeManager.getTypeForStack(findAmmo)
Optional<ItemStack> found = PotatoCannonProjectileType.getTypeForStack(Minecraft.getInstance().level, findAmmo)
.map($ -> findAmmo);
found.ifPresent(stack -> CLIENT_CURRENT_AMMO = stack);
return found;
@ -215,7 +221,7 @@ public class PotatoCannonItem extends ProjectileWeaponItem implements CustomArmP
@Override
@OnlyIn(Dist.CLIENT)
public void appendHoverText(ItemStack stack, Level world, List<Component> tooltip, TooltipFlag flag) {
public void appendHoverText(ItemStack stack, @Nullable Level level, List<Component> tooltip, TooltipFlag flag) {
int power = stack.getEnchantmentLevel(Enchantments.POWER_ARROWS);
int punch = stack.getEnchantmentLevel(Enchantments.PUNCH_ARROWS);
final float additionalDamageMult = 1 + power * .2f;
@ -229,17 +235,17 @@ public class PotatoCannonItem extends ProjectileWeaponItem implements CustomArmP
tooltip.add(CommonComponents.EMPTY);
tooltip.add(Component.translatable(ammo.getDescriptionId()).append(Component.literal(":"))
.withStyle(ChatFormatting.GRAY));
PotatoCannonProjectileType type = PotatoProjectileTypeManager.getTypeForStack(ammo)
PotatoCannonProjectileType type = PotatoCannonProjectileType.getTypeForStack(Minecraft.getInstance().level, ammo)
.get();
MutableComponent spacing = CommonComponents.space();
ChatFormatting green = ChatFormatting.GREEN;
ChatFormatting darkGreen = ChatFormatting.DARK_GREEN;
float damageF = type.getDamage() * additionalDamageMult;
float damageF = type.damage() * additionalDamageMult;
MutableComponent damage = Component.literal(damageF == Mth.floor(damageF) ? "" + Mth.floor(damageF) : "" + damageF);
MutableComponent reloadTicks = Component.literal("" + type.getReloadTicks());
MutableComponent reloadTicks = Component.literal("" + type.reloadTicks());
MutableComponent knockback =
Component.literal("" + (type.getKnockback() + additionalKnockback));
Component.literal("" + (type.knockback() + additionalKnockback));
damage = damage.withStyle(additionalDamageMult > 1 ? green : darkGreen);
knockback = knockback.withStyle(additionalKnockback > 0 ? green : darkGreen);
@ -255,12 +261,13 @@ public class PotatoCannonItem extends ProjectileWeaponItem implements CustomArmP
.append(CreateLang.translateDirect(_knockback, knockback)
.withStyle(darkGreen)));
});
super.appendHoverText(stack, world, tooltip, flag);
super.appendHoverText(stack, level, tooltip, flag);
}
@Override
public Predicate<ItemStack> getAllSupportedProjectiles() {
return stack -> PotatoProjectileTypeManager.getTypeForStack(stack)
Level level = ServerLifecycleHooks.getCurrentServer().getLevel(Level.OVERWORLD);
return stack -> PotatoCannonProjectileType.getTypeForStack(level, stack)
.isPresent();
}

View file

@ -2,6 +2,7 @@ package com.simibubi.create.content.equipment.potatoCannon;
import com.mojang.blaze3d.vertex.PoseStack;
import com.mojang.math.Axis;
import com.simibubi.create.AllItems;
import com.simibubi.create.Create;
import com.simibubi.create.CreateClient;
import com.simibubi.create.foundation.item.render.CustomRenderedItemModel;
@ -27,7 +28,7 @@ public class PotatoCannonItemRenderer extends CustomRenderedItemModelRenderer {
@Override
protected void render(ItemStack stack, CustomRenderedItemModel model, PartialItemModelRenderer renderer,
ItemDisplayContext transformType, PoseStack ms, MultiBufferSource buffer, int light, int overlay) {
ItemDisplayContext transformType, PoseStack ms, MultiBufferSource buffer, int light, int overlay) {
Minecraft mc = Minecraft.getInstance();
ItemRenderer itemRenderer = mc.getItemRenderer();
renderer.render(model.getOriginalModel(), light);
@ -54,18 +55,18 @@ public class PotatoCannonItemRenderer extends CustomRenderedItemModelRenderer {
ms.popPose();
if (transformType == ItemDisplayContext.GUI) {
PotatoCannonItem.getAmmoforPreview(stack)
.ifPresent(ammo -> {
PoseStack localMs = new PoseStack();
localMs.translate(-1 / 4f, -1 / 4f, 1);
localMs.scale(.5f, .5f, .5f);
TransformStack.of(localMs)
.rotateYDegrees(-34);
itemRenderer.renderStatic(ammo, ItemDisplayContext.GUI, light, OverlayTexture.NO_OVERLAY, localMs,
buffer, mc.level, 0);
});
PotatoCannonItem.getAmmoforPreview(stack).ifPresent(ammo -> {
if (AllItems.POTATO_CANNON.is(ammo)) return;
PoseStack localMs = new PoseStack();
localMs.translate(-1 / 4f, -1 / 4f, 1);
localMs.scale(.5f, .5f, .5f);
TransformStack.of(localMs)
.rotateYDegrees(-34);
itemRenderer.renderStatic(ammo, ItemDisplayContext.GUI, light, OverlayTexture.NO_OVERLAY, localMs,
buffer, mc.level, 0);
});
}
}
}

View file

@ -1,298 +0,0 @@
package com.simibubi.create.content.equipment.potatoCannon;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.function.BiPredicate;
import java.util.function.Consumer;
import java.util.function.Predicate;
import java.util.function.Supplier;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonPrimitive;
import net.createmod.catnip.platform.CatnipServices;
import net.minecraft.ResourceLocationException;
import net.minecraft.core.Holder;
import net.minecraft.network.FriendlyByteBuf;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.item.Item;
import net.minecraft.world.level.ItemLike;
import net.minecraft.world.level.LevelAccessor;
import net.minecraft.world.phys.BlockHitResult;
import net.minecraft.world.phys.EntityHitResult;
import net.minecraftforge.registries.ForgeRegistries;
public class PotatoCannonProjectileType {
private List<Supplier<Item>> items = new ArrayList<>();
private int reloadTicks = 10;
private int damage = 1;
private int split = 1;
private float knockback = 1;
private float drag = 0.99f;
private float velocityMultiplier = 1;
private float gravityMultiplier = 1;
private float soundPitch = 1;
private boolean sticky = false;
private PotatoProjectileRenderMode renderMode = PotatoProjectileRenderMode.Billboard.INSTANCE;
private Predicate<EntityHitResult> preEntityHit = e -> false; // True if hit should be canceled
private Predicate<EntityHitResult> onEntityHit = e -> false; // True if shouldn't recover projectile
private BiPredicate<LevelAccessor, BlockHitResult> onBlockHit = (w, ray) -> false;
protected PotatoCannonProjectileType() {
}
public List<Supplier<Item>> getItems() {
return items;
}
public int getReloadTicks() {
return reloadTicks;
}
public int getDamage() {
return damage;
}
public int getSplit() {
return split;
}
public float getKnockback() {
return knockback;
}
public float getDrag() {
return drag;
}
public float getVelocityMultiplier() {
return velocityMultiplier;
}
public float getGravityMultiplier() {
return gravityMultiplier;
}
public float getSoundPitch() {
return soundPitch;
}
public boolean isSticky() {
return sticky;
}
public PotatoProjectileRenderMode getRenderMode() {
return renderMode;
}
public boolean preEntityHit(EntityHitResult ray) {
return preEntityHit.test(ray);
}
public boolean onEntityHit(EntityHitResult ray) {
return onEntityHit.test(ray);
}
public boolean onBlockHit(LevelAccessor world, BlockHitResult ray) {
return onBlockHit.test(world, ray);
}
public static PotatoCannonProjectileType fromJson(JsonObject object) {
PotatoCannonProjectileType type = new PotatoCannonProjectileType();
try {
JsonElement itemsElement = object.get("items");
if (itemsElement != null && itemsElement.isJsonArray()) {
for (JsonElement element : itemsElement.getAsJsonArray()) {
if (element.isJsonPrimitive()) {
JsonPrimitive primitive = element.getAsJsonPrimitive();
if (primitive.isString()) {
try {
Optional<Holder.Reference<Item>> reference = ForgeRegistries.ITEMS.getDelegate(new ResourceLocation(primitive.getAsString()));
if (reference.isPresent()) {
type.items.add(reference.get());
}
} catch (ResourceLocationException e) {
//
}
}
}
}
}
parseJsonPrimitive(object, "reload_ticks", JsonPrimitive::isNumber, primitive -> type.reloadTicks = primitive.getAsInt());
parseJsonPrimitive(object, "damage", JsonPrimitive::isNumber, primitive -> type.damage = primitive.getAsInt());
parseJsonPrimitive(object, "split", JsonPrimitive::isNumber, primitive -> type.split = primitive.getAsInt());
parseJsonPrimitive(object, "knockback", JsonPrimitive::isNumber, primitive -> type.knockback = primitive.getAsFloat());
parseJsonPrimitive(object, "drag", JsonPrimitive::isNumber, primitive -> type.drag = primitive.getAsFloat());
parseJsonPrimitive(object, "velocity_multiplier", JsonPrimitive::isNumber, primitive -> type.velocityMultiplier = primitive.getAsFloat());
parseJsonPrimitive(object, "gravity_multiplier", JsonPrimitive::isNumber, primitive -> type.gravityMultiplier = primitive.getAsFloat());
parseJsonPrimitive(object, "sound_pitch", JsonPrimitive::isNumber, primitive -> type.soundPitch = primitive.getAsFloat());
parseJsonPrimitive(object, "sticky", JsonPrimitive::isBoolean, primitive -> type.sticky = primitive.getAsBoolean());
} catch (Exception e) {
//
}
return type;
}
private static void parseJsonPrimitive(JsonObject object, String key, Predicate<JsonPrimitive> predicate, Consumer<JsonPrimitive> consumer) {
JsonElement element = object.get(key);
if (element != null && element.isJsonPrimitive()) {
JsonPrimitive primitive = element.getAsJsonPrimitive();
if (predicate.test(primitive)) {
consumer.accept(primitive);
}
}
}
public static void toBuffer(PotatoCannonProjectileType type, FriendlyByteBuf buffer) {
buffer.writeVarInt(type.items.size());
for (Supplier<Item> delegate : type.items) {
buffer.writeResourceLocation(CatnipServices.REGISTRIES.getKeyOrThrow(delegate.get()));
}
buffer.writeInt(type.reloadTicks);
buffer.writeInt(type.damage);
buffer.writeInt(type.split);
buffer.writeFloat(type.knockback);
buffer.writeFloat(type.drag);
buffer.writeFloat(type.velocityMultiplier);
buffer.writeFloat(type.gravityMultiplier);
buffer.writeFloat(type.soundPitch);
buffer.writeBoolean(type.sticky);
}
public static PotatoCannonProjectileType fromBuffer(FriendlyByteBuf buffer) {
PotatoCannonProjectileType type = new PotatoCannonProjectileType();
int size = buffer.readVarInt();
for (int i = 0; i < size; i++) {
Optional<Holder.Reference<Item>> reference = ForgeRegistries.ITEMS.getDelegate(buffer.readResourceLocation());
if (reference.isPresent()) {
type.items.add(reference.get());
}
}
type.reloadTicks = buffer.readInt();
type.damage = buffer.readInt();
type.split = buffer.readInt();
type.knockback = buffer.readFloat();
type.drag = buffer.readFloat();
type.velocityMultiplier = buffer.readFloat();
type.gravityMultiplier = buffer.readFloat();
type.soundPitch = buffer.readFloat();
type.sticky = buffer.readBoolean();
return type;
}
public static class Builder {
protected ResourceLocation id;
protected PotatoCannonProjectileType result;
public Builder(ResourceLocation id) {
this.id = id;
this.result = new PotatoCannonProjectileType();
}
public Builder reloadTicks(int reload) {
result.reloadTicks = reload;
return this;
}
public Builder damage(int damage) {
result.damage = damage;
return this;
}
public Builder splitInto(int split) {
result.split = split;
return this;
}
public Builder knockback(float knockback) {
result.knockback = knockback;
return this;
}
public Builder drag(float drag) {
result.drag = drag;
return this;
}
public Builder velocity(float velocity) {
result.velocityMultiplier = velocity;
return this;
}
public Builder gravity(float modifier) {
result.gravityMultiplier = modifier;
return this;
}
public Builder soundPitch(float pitch) {
result.soundPitch = pitch;
return this;
}
public Builder sticky() {
result.sticky = true;
return this;
}
public Builder renderMode(PotatoProjectileRenderMode renderMode) {
result.renderMode = renderMode;
return this;
}
public Builder renderBillboard() {
renderMode(PotatoProjectileRenderMode.Billboard.INSTANCE);
return this;
}
public Builder renderTumbling() {
renderMode(PotatoProjectileRenderMode.Tumble.INSTANCE);
return this;
}
public Builder renderTowardMotion(int spriteAngle, float spin) {
renderMode(new PotatoProjectileRenderMode.TowardMotion(spriteAngle, spin));
return this;
}
public Builder preEntityHit(Predicate<EntityHitResult> callback) {
result.preEntityHit = callback;
return this;
}
public Builder onEntityHit(Predicate<EntityHitResult> callback) {
result.onEntityHit = callback;
return this;
}
public Builder onBlockHit(BiPredicate<LevelAccessor, BlockHitResult> callback) {
result.onBlockHit = callback;
return this;
}
public Builder addItems(ItemLike... items) {
for (ItemLike provider : items)
result.items.add(ForgeRegistries.ITEMS.getDelegateOrThrow(provider.asItem()));
return this;
}
public PotatoCannonProjectileType register() {
PotatoProjectileTypeManager.registerBuiltinType(id, result);
return result;
}
public PotatoCannonProjectileType registerAndAssign(ItemLike... items) {
addItems(items);
register();
return result;
}
}
}

View file

@ -4,6 +4,9 @@ import org.jetbrains.annotations.NotNull;
import com.simibubi.create.AllEnchantments;
import com.simibubi.create.AllSoundEvents;
import com.simibubi.create.api.equipment.potatoCannon.PotatoCannonProjectileType;
import com.simibubi.create.api.equipment.potatoCannon.PotatoProjectileRenderMode;
import com.simibubi.create.api.registry.CreateRegistries;
import com.simibubi.create.foundation.advancement.AllAdvancements;
import com.simibubi.create.foundation.damageTypes.CreateDamageSources;
import com.simibubi.create.foundation.particle.AirParticleData;
@ -68,8 +71,13 @@ public class PotatoProjectileEntity extends AbstractHurtingProjectile implements
public PotatoCannonProjectileType getProjectileType() {
if (type == null)
type = PotatoProjectileTypeManager.getTypeForStack(stack)
.orElse(BuiltinPotatoProjectileTypes.FALLBACK);
type = PotatoCannonProjectileType.getTypeForStack(level(), stack)
.orElseGet(() ->
level().registryAccess()
.lookupOrThrow(CreateRegistries.POTATO_PROJECTILE_TYPE)
.getOrThrow(AllPotatoProjectileTypes.FALLBACK)
.value()
);
return type;
}
@ -127,7 +135,7 @@ public class PotatoProjectileEntity extends AbstractHurtingProjectile implements
if (getStuckEntity() != null)
return stuckRenderer;
return getProjectileType().getRenderMode();
return getProjectileType().renderMode();
}
public void tick() {
@ -139,15 +147,15 @@ public class PotatoProjectileEntity extends AbstractHurtingProjectile implements
pop(position());
kill();
} else {
stuckFallSpeed += 0.007 * projectileType.getGravityMultiplier();
stuckFallSpeed += 0.007 * projectileType.gravityMultiplier();
stuckOffset = stuckOffset.add(0, -stuckFallSpeed, 0);
Vec3 pos = stuckEntity.position()
.add(stuckOffset);
setPos(pos.x, pos.y, pos.z);
}
} else {
setDeltaMovement(getDeltaMovement().add(0, -0.05 * projectileType.getGravityMultiplier(), 0)
.scale(projectileType.getDrag()));
setDeltaMovement(getDeltaMovement().add(0, -0.05 * projectileType.gravityMultiplier(), 0)
.scale(projectileType.drag()));
}
super.tick();
@ -178,8 +186,8 @@ public class PotatoProjectileEntity extends AbstractHurtingProjectile implements
Vec3 hit = ray.getLocation();
Entity target = ray.getEntity();
PotatoCannonProjectileType projectileType = getProjectileType();
float damage = projectileType.getDamage() * additionalDamageMult;
float knockback = projectileType.getKnockback() + additionalKnockback;
float damage = projectileType.damage() * additionalDamageMult;
float knockback = projectileType.knockback() + additionalKnockback;
Entity owner = this.getOwner();
if (!target.isAlive())
@ -202,7 +210,7 @@ public class PotatoProjectileEntity extends AbstractHurtingProjectile implements
if (target instanceof WitherBoss && ((WitherBoss) target).isPowered())
return;
if (projectileType.preEntityHit(ray))
if (projectileType.preEntityHit(stack, ray))
return;
boolean targetIsEnderman = target.getType() == EntityType.ENDERMAN;
@ -220,9 +228,12 @@ public class PotatoProjectileEntity extends AbstractHurtingProjectile implements
if (targetIsEnderman)
return;
if (!projectileType.onEntityHit(ray) && onServer)
if (random.nextDouble() <= recoveryChance)
if (!projectileType.onEntityHit(stack, ray) && onServer)
if (random.nextDouble() <= recoveryChance) {
recoverItem();
} else {
spawnAtLocation(projectileType.dropStack());
}
if (!(target instanceof LivingEntity livingentity)) {
playHitSound(level(), position());
@ -230,8 +241,8 @@ public class PotatoProjectileEntity extends AbstractHurtingProjectile implements
return;
}
if (type.getReloadTicks() < 10)
livingentity.invulnerableTime = type.getReloadTicks() + 10;
if (type.reloadTicks() < 10)
livingentity.invulnerableTime = type.reloadTicks() + 10;
if (onServer && knockback > 0) {
Vec3 appliedMotion = this.getDeltaMovement()
@ -259,7 +270,7 @@ public class PotatoProjectileEntity extends AbstractHurtingProjectile implements
AllAdvancements.POTATO_CANNON.awardTo(serverplayerentity);
}
if (type.isSticky() && target.isAlive()) {
if (type.sticky() && target.isAlive()) {
setStuckEntity(target);
} else {
kill();
@ -284,7 +295,7 @@ public class PotatoProjectileEntity extends AbstractHurtingProjectile implements
protected void onHitBlock(BlockHitResult ray) {
Vec3 hit = ray.getLocation();
pop(hit);
if (!getProjectileType().onBlockHit(level(), ray) && !level().isClientSide)
if (!getProjectileType().onBlockHit(level(), stack, ray) && !level().isClientSide)
if (random.nextDouble() <= recoveryChance)
recoverItem();
super.onHitBlock(ray);

View file

@ -1,160 +0,0 @@
package com.simibubi.create.content.equipment.potatoCannon;
import java.util.HashMap;
import java.util.IdentityHashMap;
import java.util.Map;
import java.util.Optional;
import java.util.function.Supplier;
import com.google.gson.Gson;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.simibubi.create.AllItems;
import com.simibubi.create.AllPackets;
import com.simibubi.create.foundation.networking.SimplePacketBase;
import net.minecraft.network.FriendlyByteBuf;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.server.packs.resources.ResourceManager;
import net.minecraft.server.packs.resources.SimpleJsonResourceReloadListener;
import net.minecraft.util.profiling.ProfilerFiller;
import net.minecraft.world.item.Item;
import net.minecraft.world.item.ItemStack;
import net.minecraftforge.network.NetworkEvent.Context;
import net.minecraftforge.network.PacketDistributor;
public class PotatoProjectileTypeManager {
private static final Map<ResourceLocation, PotatoCannonProjectileType> BUILTIN_TYPE_MAP = new HashMap<>();
private static final Map<ResourceLocation, PotatoCannonProjectileType> CUSTOM_TYPE_MAP = new HashMap<>();
private static final Map<Item, PotatoCannonProjectileType> ITEM_TO_TYPE_MAP = new IdentityHashMap<>();
public static void registerBuiltinType(ResourceLocation id, PotatoCannonProjectileType type) {
synchronized (BUILTIN_TYPE_MAP) {
BUILTIN_TYPE_MAP.put(id, type);
}
}
public static PotatoCannonProjectileType getBuiltinType(ResourceLocation id) {
return BUILTIN_TYPE_MAP.get(id);
}
public static PotatoCannonProjectileType getCustomType(ResourceLocation id) {
return CUSTOM_TYPE_MAP.get(id);
}
public static PotatoCannonProjectileType getTypeForItem(Item item) {
return ITEM_TO_TYPE_MAP.get(item);
}
public static Optional<PotatoCannonProjectileType> getTypeForStack(ItemStack item) {
if (item.isEmpty())
return Optional.empty();
return Optional.ofNullable(getTypeForItem(item.getItem()));
}
public static void clear() {
CUSTOM_TYPE_MAP.clear();
ITEM_TO_TYPE_MAP.clear();
}
public static void fillItemMap() {
for (Map.Entry<ResourceLocation, PotatoCannonProjectileType> entry : BUILTIN_TYPE_MAP.entrySet()) {
PotatoCannonProjectileType type = entry.getValue();
for (Supplier<Item> delegate : type.getItems()) {
ITEM_TO_TYPE_MAP.put(delegate.get(), type);
}
}
for (Map.Entry<ResourceLocation, PotatoCannonProjectileType> entry : CUSTOM_TYPE_MAP.entrySet()) {
PotatoCannonProjectileType type = entry.getValue();
for (Supplier<Item> delegate : type.getItems()) {
ITEM_TO_TYPE_MAP.put(delegate.get(), type);
}
}
ITEM_TO_TYPE_MAP.remove(AllItems.POTATO_CANNON.get());
}
public static void toBuffer(FriendlyByteBuf buffer) {
buffer.writeVarInt(CUSTOM_TYPE_MAP.size());
for (Map.Entry<ResourceLocation, PotatoCannonProjectileType> entry : CUSTOM_TYPE_MAP.entrySet()) {
buffer.writeResourceLocation(entry.getKey());
PotatoCannonProjectileType.toBuffer(entry.getValue(), buffer);
}
}
public static void fromBuffer(FriendlyByteBuf buffer) {
clear();
int size = buffer.readVarInt();
for (int i = 0; i < size; i++) {
CUSTOM_TYPE_MAP.put(buffer.readResourceLocation(), PotatoCannonProjectileType.fromBuffer(buffer));
}
fillItemMap();
}
public static void syncTo(ServerPlayer player) {
AllPackets.getChannel().send(PacketDistributor.PLAYER.with(() -> player), new SyncPacket());
}
public static void syncToAll() {
AllPackets.getChannel().send(PacketDistributor.ALL.noArg(), new SyncPacket());
}
public static class ReloadListener extends SimpleJsonResourceReloadListener {
private static final Gson GSON = new Gson();
public static final ReloadListener INSTANCE = new ReloadListener();
protected ReloadListener() {
super(GSON, "potato_cannon_projectile_types");
}
@Override
protected void apply(Map<ResourceLocation, JsonElement> map, ResourceManager resourceManager, ProfilerFiller profiler) {
clear();
for (Map.Entry<ResourceLocation, JsonElement> entry : map.entrySet()) {
JsonElement element = entry.getValue();
if (element.isJsonObject()) {
ResourceLocation id = entry.getKey();
JsonObject object = element.getAsJsonObject();
PotatoCannonProjectileType type = PotatoCannonProjectileType.fromJson(object);
CUSTOM_TYPE_MAP.put(id, type);
}
}
fillItemMap();
}
}
public static class SyncPacket extends SimplePacketBase {
private FriendlyByteBuf buffer;
public SyncPacket() {
}
public SyncPacket(FriendlyByteBuf buffer) {
this.buffer = buffer;
}
@Override
public void write(FriendlyByteBuf buffer) {
toBuffer(buffer);
}
@Override
public boolean handle(Context context) {
context.enqueueWork(() -> {
fromBuffer(buffer);
});
return true;
}
}
}

View file

@ -6,7 +6,6 @@ import com.simibubi.create.content.contraptions.ContraptionHandler;
import com.simibubi.create.content.contraptions.actors.trainControls.ControlsServerHandler;
import com.simibubi.create.content.contraptions.minecart.CouplingPhysics;
import com.simibubi.create.content.contraptions.minecart.capability.CapabilityMinecartController;
import com.simibubi.create.content.equipment.potatoCannon.PotatoProjectileTypeManager;
import com.simibubi.create.content.equipment.toolbox.ToolboxHandler;
import com.simibubi.create.content.equipment.wrench.WrenchItem;
import com.simibubi.create.content.equipment.zapper.ZapperInteractionHandler;
@ -39,7 +38,6 @@ import net.minecraftforge.common.capabilities.RegisterCapabilitiesEvent;
import net.minecraftforge.event.AddPackFindersEvent;
import net.minecraftforge.event.AddReloadListenerEvent;
import net.minecraftforge.event.AttachCapabilitiesEvent;
import net.minecraftforge.event.OnDatapackSyncEvent;
import net.minecraftforge.event.RegisterCommandsEvent;
import net.minecraftforge.event.TickEvent.LevelTickEvent;
import net.minecraftforge.event.TickEvent.Phase;
@ -143,20 +141,9 @@ public class CommonEvents {
@SubscribeEvent
public static void addReloadListeners(AddReloadListenerEvent event) {
event.addListener(RecipeFinder.LISTENER);
event.addListener(PotatoProjectileTypeManager.ReloadListener.INSTANCE);
event.addListener(BeltHelper.LISTENER);
}
@SubscribeEvent
public static void onDatapackSync(OnDatapackSyncEvent event) {
ServerPlayer player = event.getPlayer();
if (player != null) {
PotatoProjectileTypeManager.syncTo(player);
} else {
PotatoProjectileTypeManager.syncToAll();
}
}
@SubscribeEvent
public static void serverStopping(ServerStoppingEvent event) {
Create.SCHEMATIC_RECEIVER.shutdown();

View file

@ -5,6 +5,7 @@ import org.spongepowered.asm.mixin.injection.At;
import com.llamalad7.mixinextras.injector.ModifyExpressionValue;
import com.simibubi.create.content.contraptions.AbstractContraptionEntity;
import com.simibubi.create.infrastructure.config.AllConfigs;
import net.minecraft.world.entity.EntityType;
import net.minecraft.world.entity.LivingEntity;
@ -26,7 +27,8 @@ public abstract class PlayerMixin extends LivingEntity {
)
private boolean pretendNotPassenger(boolean isPassenger) {
// avoid touching all items in the contraption's massive hitbox
if (isPassenger && this.getVehicle() instanceof AbstractContraptionEntity) {
boolean shouldSync = AllConfigs.server().kinetics.syncPlayerPickupHitboxWithContraptionHitbox.get();
if (isPassenger && !shouldSync && this.getVehicle() instanceof AbstractContraptionEntity) {
return false;
}

View file

@ -0,0 +1,12 @@
package com.simibubi.create.foundation.mixin.accessor;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.gen.Accessor;
import net.minecraft.world.effect.MobEffectInstance;
@Mixin(MobEffectInstance.class)
public interface MobEffectInstanceAccessor {
@Accessor("hiddenEffect")
MobEffectInstance create$getHiddenEffect();
}

View file

@ -0,0 +1,18 @@
package com.simibubi.create.foundation.mixin.accessor;
import java.util.function.Consumer;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.gen.Invoker;
import net.minecraft.world.effect.MobEffectInstance;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.SuspiciousStewItem;
@Mixin(SuspiciousStewItem.class)
public interface SuspiciousStewItemAccessor {
@Invoker("listPotionEffects")
static void create$listPotionEffects(ItemStack stack, Consumer<MobEffectInstance> output) {
throw new AssertionError();
}
}

View file

@ -1,10 +1,30 @@
package com.simibubi.create.foundation.utility;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.function.Function;
import java.util.function.Supplier;
import java.util.stream.Stream;
import com.google.common.base.Suppliers;
import com.google.common.primitives.UnsignedBytes;
import com.mojang.datafixers.util.Pair;
import com.mojang.serialization.Codec;
import com.mojang.serialization.DataResult;
import com.mojang.serialization.DynamicOps;
import com.mojang.serialization.MapCodec;
import com.mojang.serialization.MapLike;
import com.mojang.serialization.RecordBuilder;
import com.mojang.serialization.codecs.RecordCodecBuilder;
import com.simibubi.create.foundation.item.ItemSlots;
import com.simibubi.create.foundation.mixin.accessor.MobEffectInstanceAccessor;
import net.minecraft.core.registries.BuiltInRegistries;
import net.minecraft.util.ExtraCodecs;
import net.minecraft.world.effect.MobEffectInstance;
import net.minecraft.world.effect.MobEffectInstance.FactorData;
import net.minecraft.world.food.FoodProperties;
import net.minecraftforge.items.ItemStackHandler;
@ -30,4 +50,117 @@ public class CreateCodecs {
i -> i >= min ? DataResult.success(i) : DataResult.error(() -> "Value under minimum of " + min)
);
}
public static final Codec<Double> NON_NEGATIVE_DOUBLE = doubleRangeWithMessage(0, Double.MAX_VALUE,
i -> "Value must be non-negative: " + i);
public static final Codec<Double> POSITIVE_DOUBLE = doubleRangeWithMessage(1, Double.MAX_VALUE,
i -> "Value must be positive: " + i);
private static Codec<Double> doubleRangeWithMessage(double min, double max, Function<Double, String> errorMessage) {
return ExtraCodecs.validate(Codec.DOUBLE, i ->
i.compareTo(min) >= 0 && i.compareTo(max) <= 0 ? DataResult.success(i) : DataResult.error(() ->
errorMessage.apply(i)
)
);
}
public static final Codec<Integer> UNSIGNED_BYTE = Codec.BYTE
.flatComapMap(
UnsignedBytes::toInt,
p_324632_ -> p_324632_ > 255
? DataResult.error(() -> "Unsigned byte was too large: " + p_324632_ + " > 255")
: DataResult.success(p_324632_.byteValue())
);
public static final MapCodec<MobEffectInstance> MOB_EFFECT_INSTANCE = recursive(
"MobEffectInstance",
codec -> RecordCodecBuilder.mapCodec(
instance -> instance.group(
BuiltInRegistries.MOB_EFFECT.byNameCodec().fieldOf("effect").forGetter(MobEffectInstance::getEffect),
Codec.INT.optionalFieldOf("duration", 0).forGetter(MobEffectInstance::getDuration),
UNSIGNED_BYTE.optionalFieldOf("amplifier", 0).forGetter(MobEffectInstance::getAmplifier),
Codec.BOOL.optionalFieldOf("ambient", false).forGetter(MobEffectInstance::isAmbient),
Codec.BOOL.optionalFieldOf("show_particles", true).forGetter(MobEffectInstance::isVisible),
Codec.BOOL.optionalFieldOf("show_icon", true).forGetter(MobEffectInstance::showIcon),
codec.optionalFieldOf("hidden_effect").forGetter(i -> Optional.ofNullable(((MobEffectInstanceAccessor) i).create$getHiddenEffect())),
FactorData.CODEC.optionalFieldOf("factor_data").forGetter(MobEffectInstance::getFactorData)
)
.apply(instance, (effect, duration, amplifier, isAmbient, showParticles, showIcon, hiddenEffect, factorData) ->
new MobEffectInstance(effect, duration, amplifier, isAmbient, showParticles, showIcon, hiddenEffect.orElse(null), factorData))
)
);
public static final Codec<FoodProperties> FOOD_PROPERTIES = RecordCodecBuilder.create(instance -> instance.group(
ExtraCodecs.NON_NEGATIVE_INT.fieldOf("nutrition").forGetter(FoodProperties::getNutrition),
Codec.FLOAT.fieldOf("saturation_modifier").forGetter(FoodProperties::getSaturationModifier),
Codec.BOOL.optionalFieldOf("is_meat", false).forGetter(FoodProperties::isMeat),
Codec.BOOL.optionalFieldOf("can_always_eat", false).forGetter(FoodProperties::canAlwaysEat),
Codec.BOOL.optionalFieldOf("is_fast_food", false).forGetter(FoodProperties::isFastFood),
FoodEffect.CODEC.listOf().optionalFieldOf("effects", List.of()).forGetter(i -> {
List<FoodEffect> effects = new ArrayList<>();
for (Pair<MobEffectInstance, Float> pair : i.getEffects())
effects.add(new FoodEffect(pair.getFirst(), pair.getSecond()));
return effects;
})
).apply(instance, (nutrition, saturationModifier, isMeat, canAlwaysEat, isFastFood, effects) -> {
FoodProperties.Builder builder = new FoodProperties.Builder()
.nutrition(nutrition)
.saturationMod(saturationModifier);
if (isMeat) builder.meat();
if (canAlwaysEat) builder.alwaysEat();
if (isFastFood) builder.fast();
for (FoodEffect effect : effects) builder.effect(effect.effectSupplier(), effect.probability());
return builder.build();
}));
public record FoodEffect(Supplier<MobEffectInstance> effectSupplier, float probability) {
public static final Codec<FoodEffect> CODEC = RecordCodecBuilder.create(instance -> instance.group(
MOB_EFFECT_INSTANCE.fieldOf("effect").forGetter(FoodEffect::effect),
Codec.FLOAT.fieldOf("probability").forGetter(FoodEffect::probability)
).apply(instance, FoodEffect::new));
private FoodEffect(MobEffectInstance effect, float probability) {
this(() -> effect, probability);
}
public MobEffectInstance effect() {
return new MobEffectInstance(this.effectSupplier.get());
}
}
public static <A> MapCodec<A> recursive(final String name, final Function<Codec<A>, MapCodec<A>> wrapped) {
return new RecursiveMapCodec<>(name, wrapped);
}
private static class RecursiveMapCodec<A> extends MapCodec<A> {
private final String name;
private final Supplier<MapCodec<A>> wrapped;
private RecursiveMapCodec(final String name, final Function<Codec<A>, MapCodec<A>> wrapped) {
this.name = name;
this.wrapped = Suppliers.memoize(() -> wrapped.apply(codec()));
}
@Override
public <T> RecordBuilder<T> encode(final A input, final DynamicOps<T> ops, final RecordBuilder<T> prefix) {
return wrapped.get().encode(input, ops, prefix);
}
@Override
public <T> DataResult<A> decode(final DynamicOps<T> ops, final MapLike<T> input) {
return wrapped.get().decode(ops, input);
}
@Override
public <T> Stream<T> keys(final DynamicOps<T> ops) {
return wrapped.get().keys(ops);
}
@Override
public String toString() {
return "RecursiveMapCodec[" + name + ']';
}
}
}

View file

@ -51,6 +51,7 @@ public class CKinetics extends ConfigBase {
public final ConfigBool minecartContraptionInContainers =
b(false, "minecartContraptionInContainers", Comments.minecartContraptionInContainers);
public final ConfigBool stabiliseStableContraptions = b(false, "stabiliseStableContraptions", Comments.stabiliseStableContraptions, "[Technical]");
public final ConfigBool syncPlayerPickupHitboxWithContraptionHitbox = b(false, "syncPlayerPickupHitboxWithContraptionHitbox", Comments.syncPlayerPickupHitboxWithContraptionHitbox, "[Technical]");
public final ConfigGroup stats = group(1, "stats", Comments.stats);
public final ConfigFloat mediumSpeed = f(30, 0, 4096, "mediumSpeed", Comments.rpm, Comments.mediumSpeed);
@ -120,6 +121,7 @@ public class CKinetics extends ConfigBase {
static String reinforcedDeepslateMovement = "Configure how Reinforced Deepslate blocks can be moved by contraptions.";
static String minecartContraptionInContainers = "Whether minecart contraptions can be placed into container items.";
static String stabiliseStableContraptions = "Whether stabilised bearings create a separated entity even on non-rotating contraptions.";
static String syncPlayerPickupHitboxWithContraptionHitbox = "Whether the players hitbox should be expanded to the size of the contraption hitbox.";
}
public enum DeployerAggroSetting {

View file

@ -5,25 +5,27 @@ import java.util.concurrent.CompletableFuture;
import com.simibubi.create.AllDamageTypes;
import com.simibubi.create.Create;
import com.simibubi.create.api.registry.CreateRegistries;
import com.simibubi.create.content.equipment.potatoCannon.AllPotatoProjectileTypes;
import com.simibubi.create.infrastructure.worldgen.AllBiomeModifiers;
import com.simibubi.create.infrastructure.worldgen.AllConfiguredFeatures;
import com.simibubi.create.infrastructure.worldgen.AllPlacedFeatures;
import net.minecraft.core.HolderLookup;
import net.minecraft.core.RegistrySetBuilder;
import net.minecraft.core.RegistrySetBuilder.RegistryBootstrap;
import net.minecraft.core.registries.Registries;
import net.minecraft.data.PackOutput;
import net.minecraftforge.common.data.DatapackBuiltinEntriesProvider;
import net.minecraftforge.registries.ForgeRegistries;
public class GeneratedEntriesProvider extends DatapackBuiltinEntriesProvider {
@SuppressWarnings({ "rawtypes", "unchecked" })
private static final RegistrySetBuilder BUILDER = new RegistrySetBuilder()
.add(Registries.DAMAGE_TYPE, AllDamageTypes::bootstrap)
.add(Registries.CONFIGURED_FEATURE, (RegistryBootstrap) AllConfiguredFeatures::bootstrap)
.add(Registries.PLACED_FEATURE, AllPlacedFeatures::bootstrap)
.add(ForgeRegistries.Keys.BIOME_MODIFIERS, AllBiomeModifiers::bootstrap);
.add(Registries.DAMAGE_TYPE, AllDamageTypes::bootstrap)
.add(Registries.CONFIGURED_FEATURE, AllConfiguredFeatures::bootstrap)
.add(Registries.PLACED_FEATURE, AllPlacedFeatures::bootstrap)
.add(ForgeRegistries.Keys.BIOME_MODIFIERS, AllBiomeModifiers::bootstrap)
.add(CreateRegistries.POTATO_PROJECTILE_TYPE, AllPotatoProjectileTypes::bootstrap);
public GeneratedEntriesProvider(PackOutput output, CompletableFuture<HolderLookup.Provider> registries) {
super(output, registries, BUILDER, Set.of(Create.ID));

View file

@ -31,9 +31,11 @@
"accessor.GameTestHelperAccessor",
"accessor.ItemModelGeneratorsAccessor",
"accessor.LivingEntityAccessor",
"accessor.MobEffectInstanceAccessor",
"accessor.NbtAccounterAccessor",
"accessor.ServerLevelAccessor",
"accessor.StateHolderAccessor",
"accessor.SuspiciousStewItemAccessor",
"accessor.SystemReportAccessor",
"accessor.UseOnContextAccessor"
],