add the mod

This commit is contained in:
Hexugory 2024-02-23 02:01:37 -06:00
parent 300d509baa
commit 871c10bba3
44 changed files with 897 additions and 86 deletions

View File

@ -11,7 +11,7 @@ loader_version=0.14.25
# Mod Properties # Mod Properties
mod_version=1.0.0 mod_version=1.0.0
maven_group=net.touhoudiscord maven_group=net.touhoudiscord
archives_base_name=hardcore-redeploy archives_base_name=hardcore_redeploy
# Dependencies # Dependencies
fabric_version=0.91.0+1.20.1 fabric_version=0.91.0+1.20.1

View File

@ -0,0 +1,9 @@
package net.touhoudiscord;
import net.minecraft.util.math.BlockPos;
public interface BuyStationCapable {
default void hardcoreredeploy_openBuyStationScreen(BlockPos blockPos) {
}
}

View File

@ -1,19 +1,33 @@
package net.touhoudiscord; package net.touhoudiscord;
import net.fabricmc.api.ModInitializer; import net.fabricmc.api.ModInitializer;
import net.fabricmc.fabric.api.command.v2.CommandRegistrationCallback; import net.fabricmc.fabric.api.command.v2.CommandRegistrationCallback;
import net.fabricmc.fabric.api.item.v1.FabricItemSettings; import net.fabricmc.fabric.api.item.v1.FabricItemSettings;
import net.fabricmc.fabric.api.networking.v1.PacketByteBufs;
import net.fabricmc.fabric.api.networking.v1.ServerPlayConnectionEvents;
import net.fabricmc.fabric.api.networking.v1.ServerPlayNetworking;
import net.fabricmc.fabric.api.object.builder.v1.block.FabricBlockSettings; import net.fabricmc.fabric.api.object.builder.v1.block.FabricBlockSettings;
import net.fabricmc.fabric.api.object.builder.v1.block.entity.FabricBlockEntityTypeBuilder; import net.fabricmc.fabric.api.object.builder.v1.block.entity.FabricBlockEntityTypeBuilder;
import net.minecraft.block.Block; import net.minecraft.block.Block;
import net.minecraft.block.Blocks; import net.minecraft.block.BlockState;
import net.minecraft.block.HorizontalFacingBlock;
import net.minecraft.block.entity.BlockEntityType; import net.minecraft.block.entity.BlockEntityType;
import net.minecraft.entity.effect.StatusEffect; import net.minecraft.entity.effect.StatusEffect;
import net.minecraft.entity.projectile.FireworkRocketEntity;
import net.minecraft.item.Item; import net.minecraft.item.Item;
import net.minecraft.item.ItemStack;
import net.minecraft.nbt.NbtCompound;
import net.minecraft.network.PacketByteBuf;
import net.minecraft.registry.Registries; import net.minecraft.registry.Registries;
import net.minecraft.registry.Registry; import net.minecraft.registry.Registry;
import net.minecraft.server.MinecraftServer;
import net.minecraft.server.network.ServerPlayerEntity;
import net.minecraft.sound.SoundEvent;
import net.minecraft.util.Identifier; import net.minecraft.util.Identifier;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.Direction;
import net.minecraft.util.math.Vec3d;
import net.minecraft.world.GameMode;
import net.touhoudiscord.block.BuyStation; import net.touhoudiscord.block.BuyStation;
import net.touhoudiscord.block.BuyStationEntity; import net.touhoudiscord.block.BuyStationEntity;
import net.touhoudiscord.commands.RedeployPlayerCommand; import net.touhoudiscord.commands.RedeployPlayerCommand;
@ -23,26 +37,104 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import software.bernie.geckolib.GeckoLib; import software.bernie.geckolib.GeckoLib;
import java.util.UUID;
import static net.touhoudiscord.HardcoreRedeployConfigHandler.config;
import static net.touhoudiscord.block.BuyStation.BUY_STATION_PART;
public class HardcoreRedeploy implements ModInitializer { public class HardcoreRedeploy implements ModInitializer {
public static final String MOD_ID = "hardcore-redeploy"; public static final String MOD_ID = "hardcore_redeploy";
public static final Logger LOGGER = LoggerFactory.getLogger("hardcore-redeploy"); public static final Logger LOGGER = LoggerFactory.getLogger("hardcore_redeploy");
public static final StatusEffect REDEPLOYING = new RedeployingStatusEffect(); public static final StatusEffect REDEPLOYING = new RedeployingStatusEffect();
public static final Block BUY_STATION = Registry.register(Registries.BLOCK, new Identifier(HardcoreRedeploy.MOD_ID, "buy_station"), new BuyStation(FabricBlockSettings.copyOf(Blocks.HOPPER).nonOpaque())); public static final Block BUY_STATION = Registry.register(Registries.BLOCK, new Identifier(HardcoreRedeploy.MOD_ID, "buy_station"), new BuyStation(FabricBlockSettings.create().nonOpaque().requiresTool().resistance(6).hardness(3)));
public static final Item BUY_STATION_ITEM = Registry.register(Registries.ITEM, new Identifier(HardcoreRedeploy.MOD_ID, "buy_station"), new BuyStationItem(HardcoreRedeploy.BUY_STATION, new FabricItemSettings())); public static final Item BUY_STATION_ITEM = Registry.register(Registries.ITEM, new Identifier(HardcoreRedeploy.MOD_ID, "buy_station"), new BuyStationItem(HardcoreRedeploy.BUY_STATION, new FabricItemSettings()));
public static final Identifier BUY_STATION_SOUND_ID = new Identifier(HardcoreRedeploy.MOD_ID, "buy_station");
public static SoundEvent BUY_STATION_SOUND_EVENT = SoundEvent.of(BUY_STATION_SOUND_ID);
public static BlockEntityType<BuyStationEntity> BUY_STATION_ENTITY; public static BlockEntityType<BuyStationEntity> BUY_STATION_ENTITY;
public static final Identifier SEND_REVIVES_UPDATE = new Identifier(HardcoreRedeploy.MOD_ID, "send_revives_update");
public static final Identifier REQUEST_REVIVE = new Identifier(HardcoreRedeploy.MOD_ID, "request_revive");
public static final Identifier SEND_REVIVE = new Identifier(HardcoreRedeploy.MOD_ID, "send_revive");
public static final Identifier SYNC_CONFIG = new Identifier(HardcoreRedeploy.MOD_ID, "sync_config");
private static final ItemStack firework;
static {
firework = new ItemStack(Registries.ITEM.get(new Identifier("minecraft", "firework_rocket")));
NbtCompound nbt = new NbtCompound();
nbt.putByte("Flight", (byte)3);
firework.setSubNbt("Fireworks", nbt);
}
@Override @Override
public void onInitialize() { public void onInitialize() {
LOGGER.info("Initializing Hardcore Redeploy"); LOGGER.info("Initializing Hardcore Redeploy");
GeckoLib.initialize(); GeckoLib.initialize();
ServerPlayNetworking.registerGlobalReceiver(REQUEST_REVIVE, (server, player, handler, buf, responseSender) -> {
ServerPlayerEntity spectator = server.getPlayerManager().getPlayer(buf.readUuid());
if (spectator == null) return;
BlockPos blockPos = buf.readBlockPos();
BlockState invokingBlock = player.getWorld().getBlockState(blockPos);
if (invokingBlock.getBlock() instanceof BuyStation && player.getPos().isInRange(blockPos.toCenterPos(), 5)) {
int cost = config.baseCost+config.additiveCost*RedeployStateSaver.getPlayerState(spectator).timesRevived;
boolean isCreative = player.interactionManager.getGameMode() == GameMode.CREATIVE;
if (!isCreative && player.experienceLevel < cost) return;
Vec3d fireworkPos = blockPos.toCenterPos();
BlockState blockState = player.getWorld().getBlockState(blockPos);
Direction offset = blockState.get(HorizontalFacingBlock.FACING).rotateYClockwise();
if (blockState.get(BUY_STATION_PART) == BuyStation.BuyStationPart.AUX) offset = offset.getOpposite();
FireworkRocketEntity fireworkRocketEntity = new FireworkRocketEntity(player.getWorld(), fireworkPos.x+offset.getOffsetX()/2., fireworkPos.y, fireworkPos.z+offset.getOffsetZ()/2., firework);
player.getWorld().spawnEntity(fireworkRocketEntity);
if (!isCreative) player.setExperienceLevel(player.experienceLevel-cost);
((TimerAccess) server).hardcoreredeploy_redeployInTicks(spectator, player, 60L);
server.execute(() -> {
PacketByteBuf buf1 = PacketByteBufs.create();
ServerPlayNetworking.send(spectator, SEND_REVIVE, buf1);
});
}
});
ServerPlayConnectionEvents.JOIN.register((handler, sender, server) -> {
syncConfig(server, handler.getPlayer());
RedeployStateSaver.getServerState(server).players.forEach((uuid, playerData) -> {
syncRevives(server, handler.getPlayer(), uuid);
});
});
BUY_STATION_ENTITY = Registry.register(Registries.BLOCK_ENTITY_TYPE, BUY_STATION_ENTITY = Registry.register(Registries.BLOCK_ENTITY_TYPE,
new Identifier(HardcoreRedeploy.MOD_ID, "buy_station_entity"), new Identifier(HardcoreRedeploy.MOD_ID, "buy_station_entity"),
FabricBlockEntityTypeBuilder.create(BuyStationEntity::new, FabricBlockEntityTypeBuilder.create(BuyStationEntity::new,
HardcoreRedeploy.BUY_STATION).build()); HardcoreRedeploy.BUY_STATION).build());
Registry.register(Registries.STATUS_EFFECT, new Identifier(HardcoreRedeploy.MOD_ID, "redeploying"), REDEPLOYING); Registry.register(Registries.STATUS_EFFECT, new Identifier(HardcoreRedeploy.MOD_ID, "redeploying"), REDEPLOYING);
CommandRegistrationCallback.EVENT.register((dispatcher, registryAccess, environment) -> RedeployPlayerCommand.register(dispatcher)); CommandRegistrationCallback.EVENT.register((dispatcher, registryAccess, environment) -> RedeployPlayerCommand.register(dispatcher));
Registry.register(Registries.SOUND_EVENT, BUY_STATION_SOUND_ID, BUY_STATION_SOUND_EVENT);
}
public static void syncConfig(MinecraftServer server, ServerPlayerEntity receiver) {
PacketByteBuf buf = PacketByteBufs.create();
buf.writeInt(HardcoreRedeployConfigHandler.config.baseCost);
buf.writeInt(HardcoreRedeployConfigHandler.config.additiveCost);
server.execute(() -> {
ServerPlayNetworking.send(receiver, SYNC_CONFIG, buf);
});
}
public static void syncRevives(MinecraftServer server, ServerPlayerEntity receiver, UUID uuid) {
PlayerData playerData = RedeployStateSaver.getPlayerState(server, uuid);
PacketByteBuf buf = PacketByteBufs.create();
buf.writeUuid(uuid);
buf.writeInt(playerData.timesRevived);
server.execute(() -> {
ServerPlayNetworking.send(receiver, SEND_REVIVES_UPDATE, buf);
});
} }
} }

View File

@ -1,12 +1,45 @@
package net.touhoudiscord; package net.touhoudiscord;
import net.fabricmc.api.ClientModInitializer; import net.fabricmc.api.ClientModInitializer;
import net.fabricmc.api.EnvType;
import net.fabricmc.api.Environment;
import net.fabricmc.fabric.api.client.networking.v1.ClientPlayNetworking;
import net.minecraft.client.render.block.entity.BlockEntityRendererFactories; import net.minecraft.client.render.block.entity.BlockEntityRendererFactories;
import net.touhoudiscord.HardcoreRedeployConfigHandler.HardcoreRedeployConfig;
import net.touhoudiscord.block.client.BuyStationRenderer; import net.touhoudiscord.block.client.BuyStationRenderer;
import net.touhoudiscord.screen.RedeployingScreen;
import java.util.HashMap;
import java.util.UUID;
@Environment(EnvType.CLIENT)
public class HardcoreRedeployClient implements ClientModInitializer { public class HardcoreRedeployClient implements ClientModInitializer {
public static HashMap<UUID, Integer> reviveMap = new HashMap<>();
public static HardcoreRedeployConfig serverConfig = HardcoreRedeployConfigHandler.config;
@Override @Override
public void onInitializeClient() { public void onInitializeClient() {
BlockEntityRendererFactories.register(HardcoreRedeploy.BUY_STATION_ENTITY, BuyStationRenderer::new); BlockEntityRendererFactories.register(HardcoreRedeploy.BUY_STATION_ENTITY, BuyStationRenderer::new);
ClientPlayNetworking.registerGlobalReceiver(HardcoreRedeploy.SEND_REVIVES_UPDATE, (client, handler, buf, responseSender) -> {
reviveMap.put(buf.readUuid(), buf.readInt());
HardcoreRedeploy.LOGGER.info("Synced player revives");
});
ClientPlayNetworking.registerGlobalReceiver(HardcoreRedeploy.SEND_REVIVE, (client, handler, buf, responseSender) -> {
client.execute(() -> {
client.setScreen(new RedeployingScreen(6*20));
});
});
ClientPlayNetworking.registerGlobalReceiver(HardcoreRedeploy.SYNC_CONFIG, (client, handler, buf, responseSender) -> {
serverConfig = new HardcoreRedeployConfig();
serverConfig.baseCost = buf.readInt();
serverConfig.additiveCost = buf.readInt();
HardcoreRedeploy.LOGGER.info("Synced server config");
});
} }
} }

View File

@ -0,0 +1,39 @@
package net.touhoudiscord;
import com.google.gson.Gson;
import net.fabricmc.loader.api.FabricLoader;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.StandardOpenOption;
public class HardcoreRedeployConfigHandler {
private static final Path configPath = FabricLoader.getInstance().getConfigDir().resolve("hardcore_redeploy.json");
private static final Gson gson = new Gson();
public static HardcoreRedeployConfig config = readOrCreateConfig();
public HardcoreRedeployConfigHandler() {
}
private static HardcoreRedeployConfig readOrCreateConfig() {
try {
return gson.fromJson(Files.readString(configPath), HardcoreRedeployConfig.class);
}
catch(IOException e) {
HardcoreRedeployConfig newConfig = new HardcoreRedeployConfig();
try {
Files.writeString(configPath, gson.toJson(newConfig), StandardOpenOption.TRUNCATE_EXISTING, StandardOpenOption.CREATE);
} catch (IOException ex) {
throw new RuntimeException(ex);
}
return newConfig;
}
}
public static class HardcoreRedeployConfig {
public int baseCost = 10;
public int additiveCost = 10;
HardcoreRedeployConfig(){}
}
}

View File

@ -0,0 +1,5 @@
package net.touhoudiscord;
public class PlayerData {
public int timesRevived = 0;
}

View File

@ -0,0 +1,13 @@
package net.touhoudiscord;
import java.util.UUID;
public class PlayerTimer {
public UUID target;
public Long ticks;
public PlayerTimer(UUID target, Long ticks) {
this.target = target;
this.ticks = ticks;
}
}

View File

@ -6,8 +6,15 @@ import net.minecraft.world.GameMode;
public class RedeployPlayer { public class RedeployPlayer {
public static void redeploy(ServerPlayerEntity spectator, ServerPlayerEntity target) { public static void redeploy(ServerPlayerEntity spectator, ServerPlayerEntity target) {
if (!(spectator.interactionManager.getGameMode() == GameMode.SPECTATOR)) return;
if (!target.getServerWorld().getDimension().hasCeiling()) spectator.addStatusEffect(new StatusEffectInstance(HardcoreRedeploy.REDEPLOYING, 20*20, 0)); if (!target.getServerWorld().getDimension().hasCeiling()) spectator.addStatusEffect(new StatusEffectInstance(HardcoreRedeploy.REDEPLOYING, 20*20, 0));
spectator.teleport(target.getServerWorld(), target.getPos().x, target.getServerWorld().getDimension().hasCeiling() ? target.getPos().y : 320, target.getPos().z, 0, 30); spectator.teleport(target.getServerWorld(), target.getPos().x, target.getServerWorld().getDimension().hasCeiling() ? target.getPos().y : 320, target.getPos().z, 0, 30);
spectator.changeGameMode(GameMode.SURVIVAL); spectator.changeGameMode(GameMode.SURVIVAL);
RedeployStateSaver.getPlayerState(spectator).timesRevived++;
spectator.server.getPlayerManager().getPlayerList().forEach(player -> {
HardcoreRedeploy.syncRevives(player.server, player, spectator.getUuid());
});
} }
} }

View File

@ -0,0 +1,74 @@
package net.touhoudiscord;
import net.minecraft.entity.LivingEntity;
import net.minecraft.nbt.NbtCompound;
import net.minecraft.server.MinecraftServer;
import net.minecraft.world.PersistentState;
import net.minecraft.world.PersistentStateManager;
import net.minecraft.world.World;
import java.util.HashMap;
import java.util.UUID;
public class RedeployStateSaver extends PersistentState {
public HashMap<UUID, PlayerData> players = new HashMap<>();
@Override
public NbtCompound writeNbt(NbtCompound nbt) {
NbtCompound playersNbt = new NbtCompound();
players.forEach((uuid, playerData) -> {
NbtCompound playerNbt = new NbtCompound();
playerNbt.putInt("timesRevived", playerData.timesRevived);
playersNbt.put(uuid.toString(), playerNbt);
});
nbt.put("players", playersNbt);
return nbt;
}
public static RedeployStateSaver createFromNbt(NbtCompound tag) {
RedeployStateSaver state = new RedeployStateSaver();
NbtCompound playersNbt = tag.getCompound("players");
playersNbt.getKeys().forEach(key -> {
PlayerData playerData = new PlayerData();
playerData.timesRevived = playersNbt.getCompound(key).getInt("timesRevived");
UUID uuid = UUID.fromString(key);
state.players.put(uuid, playerData);
});
return state;
}
public static RedeployStateSaver getServerState(MinecraftServer server) {
PersistentStateManager persistentStateManager = server.getWorld(World.OVERWORLD).getPersistentStateManager();
RedeployStateSaver state = persistentStateManager.getOrCreate(RedeployStateSaver::createFromNbt, RedeployStateSaver::new, HardcoreRedeploy.MOD_ID);
state.markDirty();
return state;
}
public static PlayerData getPlayerState(LivingEntity player) {
RedeployStateSaver serverState = getServerState(player.getWorld().getServer());
PlayerData playerState = serverState.players.computeIfAbsent(player.getUuid(), uuid -> new PlayerData());
return playerState;
}
public static PlayerData getPlayerState(MinecraftServer server, UUID uuid) {
RedeployStateSaver serverState = getServerState(server);
PlayerData playerState = serverState.players.computeIfAbsent(uuid, i -> new PlayerData());
return playerState;
}
}

View File

@ -0,0 +1,7 @@
package net.touhoudiscord;
import net.minecraft.server.network.ServerPlayerEntity;
public interface TimerAccess {
void hardcoreredeploy_redeployInTicks(ServerPlayerEntity spectator, ServerPlayerEntity target, Long ticks);
}

View File

@ -1,31 +1,144 @@
package net.touhoudiscord.block; package net.touhoudiscord.block;
import net.minecraft.block.BlockRenderType; import net.minecraft.block.*;
import net.minecraft.block.BlockState;
import net.minecraft.block.BlockWithEntity;
import net.minecraft.block.entity.BlockEntity; import net.minecraft.block.entity.BlockEntity;
import net.minecraft.entity.LivingEntity;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.item.ItemPlacementContext;
import net.minecraft.item.ItemStack;
import net.minecraft.state.StateManager;
import net.minecraft.state.property.EnumProperty;
import net.minecraft.util.ActionResult;
import net.minecraft.util.Hand;
import net.minecraft.util.StringIdentifiable;
import net.minecraft.util.hit.BlockHitResult;
import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.Direction;
import net.minecraft.util.shape.VoxelShape;
import net.minecraft.world.BlockView;
import net.minecraft.world.World;
import net.minecraft.world.WorldEvents;
import net.minecraft.world.WorldView; import net.minecraft.world.WorldView;
import net.touhoudiscord.BuyStationCapable;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
public class BuyStation extends BlockWithEntity { public class BuyStation extends BlockWithEntity {
public static final EnumProperty<BuyStationPart> BUY_STATION_PART = EnumProperty.of("part", BuyStationPart.class);
protected static final VoxelShape NORTH_SHAPE = Block.createCuboidShape(1.0, 0.0, 1.0, 16.0, 12.0, 15.0);
protected static final VoxelShape SOUTH_SHAPE = Block.createCuboidShape(0.0, 0.0, 1.0, 15.0, 12.0, 15.0);
protected static final VoxelShape WEST_SHAPE = Block.createCuboidShape(1.0, 0.0, 0.0, 15.0, 12.0, 15.0);
protected static final VoxelShape EAST_SHAPE = Block.createCuboidShape(1.0, 0.0, 1.0, 15.0, 12.0, 16.0);
public BuyStation(Settings settings) { public BuyStation(Settings settings) {
super(settings); super(settings);
this.setDefaultState(this.getDefaultState()
.with(HorizontalFacingBlock.FACING, Direction.NORTH)
.with(BUY_STATION_PART, BuyStationPart.MAIN));
}
@Nullable
@Override
public BlockState getPlacementState(ItemPlacementContext ctx) {
return this.getDefaultState().with(HorizontalFacingBlock.FACING, ctx.getHorizontalPlayerFacing());
}
@Override
public void onPlaced(World world, BlockPos pos, BlockState state, @Nullable LivingEntity placer, ItemStack itemStack) {
super.onPlaced(world, pos, state, placer, itemStack);
if (!world.isClient) {
BlockPos blockPos = pos.offset(state.get(HorizontalFacingBlock.FACING).rotateYClockwise());
world.setBlockState(blockPos, state.with(BUY_STATION_PART, BuyStationPart.AUX), Block.NOTIFY_ALL);
world.updateNeighbors(pos, Blocks.AIR);
state.updateNeighbors(world, pos, Block.NOTIFY_ALL);
}
}
@Override
public void onBreak(World world, BlockPos pos, BlockState state, PlayerEntity player) {
if (world.isClient()) {
super.onBreak(world, pos, state, player);
}
BuyStationPart part = state.get(BUY_STATION_PART);
if (part == BuyStationPart.MAIN) {
BlockPos otherpos = pos.offset(state.get(HorizontalFacingBlock.FACING).rotateYClockwise());
BlockState otherstate = world.getBlockState(otherpos);
if (otherstate.getBlock() == this) {
world.setBlockState(otherpos, Blocks.AIR.getDefaultState(), Block.NOTIFY_ALL);
world.syncWorldEvent(player, WorldEvents.BLOCK_BROKEN, otherpos, Block.getRawIdFromState(otherstate));
}
}
else if (part == BuyStationPart.AUX) {
BlockPos otherpos = pos.offset(state.get(HorizontalFacingBlock.FACING).rotateYCounterclockwise());
BlockState otherstate = world.getBlockState(otherpos);
if (otherstate.getBlock() == this) {
world.setBlockState(otherpos, Blocks.AIR.getDefaultState(), Block.NOTIFY_ALL);
world.syncWorldEvent(player, WorldEvents.BLOCK_BROKEN, otherpos, Block.getRawIdFromState(otherstate));
}
}
}
public VoxelShape getOutlineShape(BlockState state, BlockView world, BlockPos pos, ShapeContext context) {
Direction direction = state.get(HorizontalFacingBlock.FACING);
BuyStationPart part = state.get(BUY_STATION_PART);
if (part == BuyStationPart.AUX) direction = direction.getOpposite();
return switch (direction) {
default -> NORTH_SHAPE;
case SOUTH -> SOUTH_SHAPE;
case WEST -> WEST_SHAPE;
case EAST -> EAST_SHAPE;
};
}
@Override
public ActionResult onUse(BlockState state, World world, BlockPos pos, PlayerEntity player, Hand hand, BlockHitResult hit) {
Block block = world.getBlockState(pos).getBlock();
if (block instanceof BuyStation) {
((BuyStationCapable) player).hardcoreredeploy_openBuyStationScreen(pos);
return ActionResult.success(world.isClient);
} else {
return ActionResult.PASS;
}
}
@Override
protected void appendProperties(StateManager.Builder<Block, BlockState> builder) {
builder.add(HorizontalFacingBlock.FACING, BUY_STATION_PART);
} }
@Nullable @Nullable
@Override @Override
public BlockEntity createBlockEntity(BlockPos pos, BlockState state) { public BlockEntity createBlockEntity(BlockPos pos, BlockState state) {
return new BuyStationEntity(pos, state); return state.get(BUY_STATION_PART) == BuyStationPart.MAIN ? new BuyStationEntity(pos, state) : null;
} }
@Override @Override
public BlockRenderType getRenderType(BlockState state) { public BlockRenderType getRenderType(BlockState state) {
return BlockRenderType.MODEL; return state.get(BUY_STATION_PART) == BuyStationPart.MAIN ? BlockRenderType.MODEL : BlockRenderType.INVISIBLE;
} }
@Override @Override
public boolean canPlaceAt(BlockState state, WorldView world, BlockPos pos) { public boolean canPlaceAt(BlockState state, WorldView world, BlockPos pos) {
return super.canPlaceAt(state, world, pos); return world.getBlockState(pos.offset(state.get(HorizontalFacingBlock.FACING).rotateYClockwise())).isReplaceable() && super.canPlaceAt(state, world, pos);
}
public enum BuyStationPart implements StringIdentifiable {
MAIN("main"),
AUX("aux");
private final String name;
BuyStationPart(String name) {
this.name = name;
}
public String toString() {
return this.name;
}
public String asString() {
return this.name;
}
} }
} }

View File

@ -3,17 +3,21 @@ package net.touhoudiscord.block;
import net.minecraft.block.BlockState; import net.minecraft.block.BlockState;
import net.minecraft.block.entity.BlockEntity; import net.minecraft.block.entity.BlockEntity;
import net.minecraft.entity.player.PlayerEntity; import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.sound.SoundCategory;
import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.Vec3d; import net.minecraft.util.math.Vec3d;
import net.touhoudiscord.HardcoreRedeploy; import net.touhoudiscord.HardcoreRedeploy;
import software.bernie.geckolib.animatable.GeoBlockEntity; import software.bernie.geckolib.animatable.GeoBlockEntity;
import software.bernie.geckolib.core.animatable.instance.AnimatableInstanceCache; import software.bernie.geckolib.core.animatable.instance.AnimatableInstanceCache;
import software.bernie.geckolib.core.animatable.instance.SingletonAnimatableInstanceCache; import software.bernie.geckolib.core.animatable.instance.SingletonAnimatableInstanceCache;
import software.bernie.geckolib.core.animation.*; import software.bernie.geckolib.core.animation.AnimatableManager;
import software.bernie.geckolib.core.animation.AnimationController;
import software.bernie.geckolib.core.animation.RawAnimation;
import software.bernie.geckolib.util.ClientUtils;
import software.bernie.geckolib.util.RenderUtils; import software.bernie.geckolib.util.RenderUtils;
public class BuyStationEntity extends BlockEntity implements GeoBlockEntity { public class BuyStationEntity extends BlockEntity implements GeoBlockEntity {
private AnimatableInstanceCache cache = new SingletonAnimatableInstanceCache(this); private final AnimatableInstanceCache cache = new SingletonAnimatableInstanceCache(this);
private static final RawAnimation OPEN = RawAnimation.begin().thenPlay("animation.model.open").thenLoop("animation.model.idle"); private static final RawAnimation OPEN = RawAnimation.begin().thenPlay("animation.model.open").thenLoop("animation.model.idle");
private static final RawAnimation CLOSE = RawAnimation.begin().thenPlay("animation.model.close"); private static final RawAnimation CLOSE = RawAnimation.begin().thenPlay("animation.model.close");
@ -26,11 +30,20 @@ public class BuyStationEntity extends BlockEntity implements GeoBlockEntity {
public void registerControllers(AnimatableManager.ControllerRegistrar controllers) { public void registerControllers(AnimatableManager.ControllerRegistrar controllers) {
controllers.add(new AnimationController<>(this, "buystationcontroller", 0, state -> { controllers.add(new AnimationController<>(this, "buystationcontroller", 0, state -> {
Vec3d pos = state.getAnimatable().getPos().toCenterPos(); Vec3d pos = state.getAnimatable().getPos().toCenterPos();
if (state.getAnimatable().getWorld().getClosestPlayer(pos.x, pos.y, pos.z, 5, false) instanceof PlayerEntity) if (state.getAnimatable().getWorld().getClosestPlayer(pos.x, pos.y, pos.z, 5, false) instanceof PlayerEntity) {
return state.setAndContinue(OPEN); return state.setAndContinue(OPEN);
else }
else {
return state.setAndContinue(CLOSE); return state.setAndContinue(CLOSE);
})); }
})
.setSoundKeyframeHandler(event -> {
PlayerEntity player = ClientUtils.getClientPlayer();
if (player != null) {
player.getWorld().playSound(player, this.getPos(), HardcoreRedeploy.BUY_STATION_SOUND_EVENT, SoundCategory.BLOCKS, 1, 1);
}
}));
} }
@Override @Override

View File

@ -1,10 +1,13 @@
package net.touhoudiscord.block.client; package net.touhoudiscord.block.client;
import net.fabricmc.api.EnvType;
import net.fabricmc.api.Environment;
import net.minecraft.util.Identifier; import net.minecraft.util.Identifier;
import net.touhoudiscord.HardcoreRedeploy; import net.touhoudiscord.HardcoreRedeploy;
import net.touhoudiscord.block.BuyStationEntity; import net.touhoudiscord.block.BuyStationEntity;
import software.bernie.geckolib.model.GeoModel; import software.bernie.geckolib.model.GeoModel;
@Environment(EnvType.CLIENT)
public class BuyStationModel extends GeoModel<BuyStationEntity> { public class BuyStationModel extends GeoModel<BuyStationEntity> {
@Override @Override
public Identifier getModelResource(BuyStationEntity animatable) { public Identifier getModelResource(BuyStationEntity animatable) {

View File

@ -1,11 +1,27 @@
package net.touhoudiscord.block.client; package net.touhoudiscord.block.client;
import net.fabricmc.api.EnvType;
import net.fabricmc.api.Environment;
import net.minecraft.block.HorizontalFacingBlock;
import net.minecraft.client.render.VertexConsumer;
import net.minecraft.client.render.VertexConsumerProvider;
import net.minecraft.client.render.block.entity.BlockEntityRendererFactory; import net.minecraft.client.render.block.entity.BlockEntityRendererFactory;
import net.minecraft.client.util.math.MatrixStack;
import net.minecraft.util.math.Direction;
import net.touhoudiscord.block.BuyStationEntity; import net.touhoudiscord.block.BuyStationEntity;
import software.bernie.geckolib.cache.object.BakedGeoModel;
import software.bernie.geckolib.renderer.GeoBlockRenderer; import software.bernie.geckolib.renderer.GeoBlockRenderer;
@Environment(EnvType.CLIENT)
public class BuyStationRenderer extends GeoBlockRenderer<BuyStationEntity> { public class BuyStationRenderer extends GeoBlockRenderer<BuyStationEntity> {
public BuyStationRenderer(BlockEntityRendererFactory.Context context) { public BuyStationRenderer(BlockEntityRendererFactory.Context context) {
super(new BuyStationModel()); super(new BuyStationModel());
} }
@Override
public void preRender(MatrixStack poseStack, BuyStationEntity animatable, BakedGeoModel model, VertexConsumerProvider bufferSource, VertexConsumer buffer, boolean isReRender, float partialTick, int packedLight, int packedOverlay, float red, float green, float blue, float alpha) {
Direction direction = animatable.getCachedState().get(HorizontalFacingBlock.FACING).rotateYClockwise();
poseStack.translate(direction.getOffsetX()/2., 0, direction.getOffsetZ()/2.);
super.preRender(poseStack, animatable, model, bufferSource, buffer, isReRender, partialTick, packedLight, packedOverlay, red, green, blue, alpha);
}
} }

View File

@ -8,10 +8,10 @@ import net.minecraft.server.command.ServerCommandSource;
import net.minecraft.server.network.ServerPlayerEntity; import net.minecraft.server.network.ServerPlayerEntity;
import net.minecraft.text.Text; import net.minecraft.text.Text;
import net.minecraft.world.GameMode; import net.minecraft.world.GameMode;
import net.touhoudiscord.HardcoreRedeploy;
import net.touhoudiscord.RedeployPlayer; import net.touhoudiscord.RedeployPlayer;
import static net.minecraft.server.command.CommandManager.*; import static net.minecraft.server.command.CommandManager.argument;
import static net.minecraft.server.command.CommandManager.literal;
public class RedeployPlayerCommand { public class RedeployPlayerCommand {
@ -36,7 +36,5 @@ public class RedeployPlayerCommand {
return 1; return 1;
}))); })));
HardcoreRedeploy.LOGGER.info("Registered RedeployPlayer command");
} }
} }

View File

@ -9,7 +9,8 @@ import software.bernie.geckolib.animatable.SingletonGeoAnimatable;
import software.bernie.geckolib.animatable.client.RenderProvider; import software.bernie.geckolib.animatable.client.RenderProvider;
import software.bernie.geckolib.core.animatable.instance.AnimatableInstanceCache; import software.bernie.geckolib.core.animatable.instance.AnimatableInstanceCache;
import software.bernie.geckolib.core.animatable.instance.SingletonAnimatableInstanceCache; import software.bernie.geckolib.core.animatable.instance.SingletonAnimatableInstanceCache;
import software.bernie.geckolib.core.animation.*; import software.bernie.geckolib.core.animation.AnimatableManager;
import software.bernie.geckolib.core.animation.AnimationController;
import software.bernie.geckolib.core.object.PlayState; import software.bernie.geckolib.core.object.PlayState;
import software.bernie.geckolib.util.RenderUtils; import software.bernie.geckolib.util.RenderUtils;
@ -17,7 +18,7 @@ import java.util.function.Consumer;
import java.util.function.Supplier; import java.util.function.Supplier;
public class BuyStationItem extends BlockItem implements GeoItem { public class BuyStationItem extends BlockItem implements GeoItem {
private AnimatableInstanceCache cache = new SingletonAnimatableInstanceCache(this); private final AnimatableInstanceCache cache = new SingletonAnimatableInstanceCache(this);
private final Supplier<Object> renderProvider = GeoItem.makeRenderer(this); private final Supplier<Object> renderProvider = GeoItem.makeRenderer(this);
public BuyStationItem(Block block, Settings settings) { public BuyStationItem(Block block, Settings settings) {

View File

@ -1,10 +1,13 @@
package net.touhoudiscord.item.client; package net.touhoudiscord.item.client;
import net.fabricmc.api.EnvType;
import net.fabricmc.api.Environment;
import net.minecraft.util.Identifier; import net.minecraft.util.Identifier;
import net.touhoudiscord.HardcoreRedeploy; import net.touhoudiscord.HardcoreRedeploy;
import net.touhoudiscord.item.BuyStationItem; import net.touhoudiscord.item.BuyStationItem;
import software.bernie.geckolib.model.GeoModel; import software.bernie.geckolib.model.GeoModel;
@Environment(EnvType.CLIENT)
public class BuyStationItemModel extends GeoModel<BuyStationItem> { public class BuyStationItemModel extends GeoModel<BuyStationItem> {
@Override @Override
public Identifier getModelResource(BuyStationItem animatable) { public Identifier getModelResource(BuyStationItem animatable) {

View File

@ -1,8 +1,11 @@
package net.touhoudiscord.item.client; package net.touhoudiscord.item.client;
import net.fabricmc.api.EnvType;
import net.fabricmc.api.Environment;
import net.touhoudiscord.item.BuyStationItem; import net.touhoudiscord.item.BuyStationItem;
import software.bernie.geckolib.renderer.GeoItemRenderer; import software.bernie.geckolib.renderer.GeoItemRenderer;
@Environment(EnvType.CLIENT)
public class BuyStationItemRenderer extends GeoItemRenderer<BuyStationItem> { public class BuyStationItemRenderer extends GeoItemRenderer<BuyStationItem> {
public BuyStationItemRenderer() { public BuyStationItemRenderer() {
super(new BuyStationItemModel()); super(new BuyStationItemModel());

View File

@ -0,0 +1,13 @@
package net.touhoudiscord.mixin;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.util.math.BlockPos;
import net.touhoudiscord.BuyStationCapable;
import org.spongepowered.asm.mixin.Mixin;
@Mixin(PlayerEntity.class)
public class BuyStationScreenMixin implements BuyStationCapable {
@Override
public void hardcoreredeploy_openBuyStationScreen(BlockPos blockPos) {
}
}

View File

@ -0,0 +1,20 @@
package net.touhoudiscord.mixin;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.network.ClientPlayerEntity;
import net.minecraft.util.math.BlockPos;
import net.touhoudiscord.BuyStationCapable;
import net.touhoudiscord.screen.BuyStationScreen;
import org.spongepowered.asm.mixin.Final;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
@Mixin(ClientPlayerEntity.class)
public class ClientBuyStationScreenMixin implements BuyStationCapable {
@Shadow @Final protected MinecraftClient client;
@Override
public void hardcoreredeploy_openBuyStationScreen(BlockPos blockPos) {
this.client.setScreen(new BuyStationScreen(blockPos));
}
}

View File

@ -0,0 +1,41 @@
package net.touhoudiscord.mixin;
import net.minecraft.server.MinecraftServer;
import net.minecraft.server.PlayerManager;
import net.minecraft.server.network.ServerPlayerEntity;
import net.touhoudiscord.PlayerTimer;
import net.touhoudiscord.RedeployPlayer;
import net.touhoudiscord.TimerAccess;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.Unique;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import java.util.HashMap;
import java.util.UUID;
@Mixin(MinecraftServer.class)
public abstract class RedeployTimerMixin implements TimerAccess {
@Shadow public abstract PlayerManager getPlayerManager();
@Unique
private final HashMap<UUID, PlayerTimer> playerTimers = new HashMap<>();
@Inject(method = "tick", at = @At("TAIL"))
private void onTick(CallbackInfo ci) {
this.playerTimers.forEach((uuid, timer) -> {
if (--timer.ticks == 0L) {
ServerPlayerEntity spectator = this.getPlayerManager().getPlayer(uuid);
ServerPlayerEntity target = this.getPlayerManager().getPlayer(timer.target);
if (spectator != null && target != null) RedeployPlayer.redeploy(spectator, target);
}
});
}
@Override
public void hardcoreredeploy_redeployInTicks(ServerPlayerEntity spectator, ServerPlayerEntity target, Long ticks) {
this.playerTimers.put(spectator.getUuid(), new PlayerTimer(target.getUuid(), ticks));
}
}

View File

@ -0,0 +1,125 @@
package net.touhoudiscord.screen;
import net.fabricmc.api.EnvType;
import net.fabricmc.api.Environment;
import net.fabricmc.fabric.api.client.networking.v1.ClientPlayNetworking;
import net.fabricmc.fabric.api.networking.v1.PacketByteBufs;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.gui.DrawContext;
import net.minecraft.client.gui.Element;
import net.minecraft.client.gui.Selectable;
import net.minecraft.client.gui.screen.Screen;
import net.minecraft.client.gui.widget.ButtonWidget;
import net.minecraft.client.gui.widget.ElementListWidget;
import net.minecraft.network.PacketByteBuf;
import net.minecraft.text.Text;
import net.minecraft.util.math.BlockPos;
import net.minecraft.world.GameMode;
import net.touhoudiscord.HardcoreRedeploy;
import net.touhoudiscord.HardcoreRedeployClient;
import java.util.List;
import java.util.UUID;
import static net.touhoudiscord.HardcoreRedeployClient.serverConfig;
@Environment(EnvType.CLIENT)
public class BuyStationScreen extends Screen {
private final BlockPos blockPos;
private PlayerListWidget playerList;
public BuyStationScreen(BlockPos blockPos) {
super(Text.literal("Buy Station"));
this.blockPos = blockPos;
}
@Override
protected void init() {
super.init();
playerList = new PlayerListWidget(client, width+38, height, height/2-58, height/2+84, 28);
client.player.networkHandler.getListedPlayerListEntries().forEach(entry -> {
if (entry.getGameMode() == GameMode.SPECTATOR) playerList.addPlayerEntry(new PlayerListEntry(Text.literal(entry.getProfile().getName()), entry.getProfile().getId()));
});
addDrawableChild(playerList);
}
@Override
public void render(DrawContext context, int mouseX, int mouseY, float delta) {
renderBackground(context);
context.fill(width/2-93, height/2-84, width/2+93, height/2+84, 0x8F_000000);
context.fill(width/2-93, height/2-84, width/2+93, height/2-58, 0xA0_000000);
context.getMatrices().push();
context.getMatrices().scale(1.5f, 1.5f, 1f);
context.drawText(textRenderer, Text.literal("Buy Station"), Math.round((width/2-80)/1.5f), Math.round((height/2-75)/1.5f), 0xFF_73c0e7, false);
context.getMatrices().pop();
Text money = Text.literal("$").append(Text.literal(String.valueOf(client.player.experienceLevel*100)));
context.drawText(textRenderer, money, width/2+80-textRenderer.getWidth(money), height/2-72, 0xFF_FFFFFF, false);
playerList.render(context, mouseX, mouseY, delta);
super.render(context, mouseX, mouseY, delta);
}
@Override
public boolean shouldPause() {
return false;
}
public class PlayerListWidget extends ElementListWidget<PlayerListEntry> {
public PlayerListWidget(MinecraftClient minecraftClient, int i, int j, int k, int l, int m) {
super(minecraftClient, i, j, k, l, m);
this.setRenderBackground(false);
this.setRenderHorizontalShadows(false);
}
public void addPlayerEntry(PlayerListEntry entry) {
this.addEntry(entry);
}
}
public class PlayerListEntry extends ElementListWidget.Entry<PlayerListEntry> {
private final ButtonWidget button;
private final Text name;
private final UUID uuid;
public PlayerListEntry(Text name, UUID uuid) {
this.button = ButtonWidget.builder(name, button1 -> {
PacketByteBuf buf = PacketByteBufs.create();
buf.writeUuid(uuid);
buf.writeBlockPos(blockPos);
ClientPlayNetworking.send(HardcoreRedeploy.REQUEST_REVIVE, buf);
client.setScreen(null);
})
.position(4,2)
.size(178, 18)
.build();
this.name = name;
this.uuid = uuid;
}
@Override
public List<? extends Selectable> selectableChildren() {
return List.of(button);
}
@Override
public List<? extends Element> children() {
return List.of(button);
}
@Override
public void render(DrawContext context, int index, int y, int x, int entryWidth, int entryHeight, int mouseX, int mouseY, boolean hovered, float tickDelta) {
button.setX(x);
button.setY(y);
boolean isHovered = mouseX >= x && mouseX <= x+178 && mouseY >= y && mouseY <= y+18;
Integer revives = HardcoreRedeployClient.reviveMap.get(this.uuid);
int cost = serverConfig.baseCost+(revives == null ? 0 : revives)*serverConfig.additiveCost;
int backgroundColor = ((isHovered ? 0x30 : 0x20) << 24) +
((client.player.experienceLevel >= cost) ? 0x2397d1 : 0xa3a3a3);
context.fill(x, y, x+178, y+18, backgroundColor);
context.drawText(textRenderer, button.getMessage(), x+4, y+5, 0xFF_73c0e7, false);
Text money = Text.literal("$").append(Text.literal(String.valueOf(cost*100)));
context.drawText(textRenderer, money, x+178-4-textRenderer.getWidth(money), y+5, 0xFF_7efc20, false);
}
}
}

View File

@ -0,0 +1,34 @@
package net.touhoudiscord.screen;
import net.minecraft.client.gui.DrawContext;
import net.minecraft.client.gui.screen.Screen;
import net.minecraft.text.Text;
public class RedeployingScreen extends Screen {
private final float duration;
private float time;
public RedeployingScreen(float duration) {
super(Text.empty());
this.duration = duration;
}
@Override
public void render(DrawContext context, int mouseX, int mouseY, float delta) {
time += delta;
context.fill(0, 0, width, height, Math.round((1-Math.abs((float)Math.sin(((time/(duration/2))-1)*(Math.PI/2))))*255)<<24);
super.render(context, mouseX, mouseY, delta);
if (time > duration) client.setScreen(null);
}
@Override
public boolean shouldPause() {
return false;
}
@Override
public boolean shouldCloseOnEsc() {
return false;
}
}

View File

@ -1,5 +0,0 @@
{
"variants": {
"": { "model": "hardcore-redeploy:block/buy_station" }
}
}

View File

@ -1,4 +0,0 @@
{
"block.hardcore-redeploy.buy_station": "Buy Station",
"effect.hardcore-redeploy.redeploying": "Redeploying"
}

View File

@ -1,11 +0,0 @@
{
"credit": "Made with Blockbench",
"parent": "builtin/entity",
"texture_size": [
64,
64
],
"textures": {
"particle": "minecraft:block/hopper_outside"
}
}

View File

@ -1,3 +0,0 @@
{
"parent": "hardcore-redeploy:block/buy_station"
}

View File

@ -28,7 +28,7 @@
"vector": [0, 0, 0] "vector": [0, 0, 0]
}, },
"0.25": { "0.25": {
"vector": [-25, 0, 0] "vector": [-37.5, 0, 0]
}, },
"1.0": { "1.0": {
"vector": [-10, 0, 0] "vector": [-10, 0, 0]
@ -58,7 +58,8 @@
"vector": [0, 0, 0] "vector": [0, 0, 0]
}, },
"0.3333": { "0.3333": {
"vector": [0, 2, 0] "vector": [0, 2, 0],
"easing": "easeOutBack"
}, },
"0.6667": { "0.6667": {
"vector": [-11, 3, 0], "vector": [-11, 3, 0],
@ -72,7 +73,8 @@
"vector": [0, 0, 0] "vector": [0, 0, 0]
}, },
"0.3333": { "0.3333": {
"vector": [0, 2, 0] "vector": [0, 2, 0],
"easing": "easeOutBack"
}, },
"0.6667": { "0.6667": {
"vector": [11, 3, 0], "vector": [11, 3, 0],
@ -108,6 +110,11 @@
} }
} }
} }
},
"sound_effects": {
"0.0": {
"effect": "buy_station"
}
} }
}, },
"animation.model.idle": { "animation.model.idle": {
@ -182,7 +189,7 @@
"vector": [-10, 0, 0] "vector": [-10, 0, 0]
}, },
"0.5833": { "0.5833": {
"vector": [-25, 0, 0] "vector": [-35, 0, 0]
}, },
"1.0": { "1.0": {
"vector": [0, 0, 0] "vector": [0, 0, 0]
@ -199,69 +206,86 @@
"vector": [0, 6, -2] "vector": [0, 6, -2]
}, },
"0.5833": { "0.5833": {
"vector": [0, 6, -2] "vector": [0, 6, -2],
"easing": "easeOutBack"
}, },
"1.0": { "1.0": {
"vector": [0, 0, 0] "vector": [0, 0, 0],
"easing": "linear"
} }
} }
}, },
"botshelfR": { "botshelfR": {
"position": { "position": {
"0.0": { "0.25": {
"vector": [-11, 3, 0], "vector": [-11, 3, 0],
"easing": "easeOutBack" "easing": "easeOutBack"
}, },
"0.2917": {
"vector": [0, 2, 0]
},
"0.5417": { "0.5417": {
"vector": [0, 0, 0] "vector": [0, 2, 0],
"easing": "easeOutBack"
},
"0.7917": {
"vector": [0, 0, 0],
"easing": "easeOutBack"
} }
} }
}, },
"botshelfL": { "botshelfL": {
"position": { "position": {
"0.0": { "0.25": {
"vector": [11, 3, 0], "vector": [11, 3, 0],
"easing": "easeOutBack" "easing": "easeOutBack"
}, },
"0.2917": {
"vector": [0, 2, 0]
},
"0.5417": { "0.5417": {
"vector": [0, 0, 0] "vector": [0, 2, 0],
"easing": "easeOutBack"
},
"0.7917": {
"vector": [0, 0, 0],
"easing": "easeOutBack"
} }
} }
}, },
"hingeL": { "hingeL": {
"position": { "position": {
"0.0": { "0.25": {
"vector": [1, 3, 0], "vector": [1, 3, 0],
"easing": "easeOutBack" "easing": "easeOutBack"
}, },
"0.2917": { "0.4167": {
"vector": [-1.57, 2.43, 0]
},
"0.5417": {
"vector": [0, 2, 0] "vector": [0, 2, 0]
}, },
"0.5833": { "0.8333": {
"vector": [0, 0, 0] "vector": [0, 0, 0]
} }
} }
}, },
"hingeR": { "hingeR": {
"position": { "position": {
"0.0": { "0.25": {
"vector": [-1, 3, 0], "vector": [-1, 3, 0],
"easing": "easeOutBack" "easing": "easeOutBack"
}, },
"0.2917": { "0.4167": {
"vector": [1.57, 2.43, 0]
},
"0.5417": {
"vector": [0, 2, 0] "vector": [0, 2, 0]
}, },
"0.5833": { "0.8333": {
"vector": [0, 0, 0] "vector": [0, 0, 0]
} }
} }
} }
},
"sound_effects": {
"0.0": {
"effect": "buy_station"
}
} }
} }
}, },

View File

@ -0,0 +1,5 @@
{
"variants": {
"": { "model": "hardcore_redeploy:block/buy_station" }
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

View File

@ -0,0 +1,5 @@
{
"block.hardcore_redeploy.buy_station": "Buy Station",
"effect.hardcore_redeploy.redeploying": "Redeploying",
"subtitles.hardcore_redeploy.buy_station": "Buy station noise"
}

View File

@ -0,0 +1,72 @@
{
"credit": "Made with Blockbench",
"parent": "builtin/entity",
"texture_size": [
64,
64
],
"textures": {
"particle": "minecraft:block/hopper_outside"
},
"display": {
"thirdperson_righthand": {
"scale": [
0.3,
0.3,
0.3
]
},
"thirdperson_lefthand": {
"scale": [
0.3,
0.3,
0.3
]
},
"firstperson_righthand": {
"scale": [
0.2,
0.2,
0.2
]
},
"firstperson_lefthand": {
"scale": [
0.2,
0.2,
0.2
]
},
"ground": {
"scale": [
0.7,
0.7,
0.7
]
},
"gui": {
"rotation": [
0,
23,
21
],
"translation": [
1,
-2.75,
0
],
"scale": [
0.5,
0.5,
0.5
]
},
"fixed": {
"rotation": [
-90,
0,
0
]
}
}
}

View File

@ -0,0 +1,3 @@
{
"parent": "hardcore_redeploy:block/buy_station"
}

View File

@ -0,0 +1,8 @@
{
"buy_station": {
"subtitle": "subtitles.hardcore_redeploy.buy_station",
"sounds": [
"hardcore_redeploy:buy_station"
]
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 928 B

View File

@ -0,0 +1,19 @@
{
"type": "minecraft:block",
"pools": [
{
"rolls": 1,
"entries": [
{
"type": "minecraft:item",
"name": "hardcore_redeploy:buy_station"
}
],
"conditions": [
{
"condition": "minecraft:survives_explosion"
}
]
}
]
}

View File

@ -0,0 +1,21 @@
{
"type": "minecraft:crafting_shaped",
"pattern": [
"III",
"ICI",
"III"
],
"key": {
"I": {
"item": "minecraft:iron_ingot"
},
"C": {
"item": "minecraft:crafting_table"
}
},
"result": {
"item": "hardcore_redeploy:buy_station",
"count": 1
}
}

View File

@ -0,0 +1,6 @@
{
"replace": false,
"values": [
"hardcore_redeploy:buy_station"
]
}

View File

@ -1,19 +1,19 @@
{ {
"schemaVersion": 1, "schemaVersion": 1,
"id": "hardcore-redeploy", "id": "hardcore_redeploy",
"version": "${version}", "version": "${version}",
"name": "Hardcore Redeploy", "name": "Hardcore Redeploy",
"description": "A mod that allows reviving players in hardcore servers, at a cost", "description": "A mod that allows reviving players in hardcore servers, at a cost",
"authors": [ "authors": [
"Hexugory", "Hexugory",
"Kaptcha" "Kaptchadelta"
], ],
"contact": { "contact": {
"homepage": "", "homepage": "",
"sources": "https://git.touhoudiscord.net/Hexugory/HardcoreRedeploy" "sources": "https://git.touhoudiscord.net/Hexugory/HardcoreRedeploy"
}, },
"license": "LGPL-3.0", "license": "LGPL-3.0",
"icon": "assets/hardcore-redeploy/icon.png", "icon": "assets/hardcore_redeploy/icon.png",
"environment": "*", "environment": "*",
"entrypoints": { "entrypoints": {
"main": [ "main": [
@ -24,15 +24,19 @@
] ]
}, },
"mixins": [ "mixins": [
"hardcore-redeploy.mixins.json" "hardcore_redeploy.mixins.json"
], ],
"depends": { "depends": {
"fabricloader": ">=0.14.25", "fabricloader": ">=0.14.25",
"minecraft": "~1.20.1", "minecraft": "~1.20.1",
"java": ">=17", "java": ">=17",
"fabric-api": "*" "fabric-api": "*",
"geckolib": "^4.4.2"
}, },
"suggests": { "custom": {
"another-mod": "*" "loom:injected_interfaces": {
"net/minecraft/class_1657": ["net/touhoudiscord/BuyStationCapable"],
"net/minecraft/class_746": ["net/touhoudiscord/BuyStationCapable"]
} }
} }
}

View File

@ -1,11 +0,0 @@
{
"required": true,
"package": "net.touhoudiscord.mixin",
"compatibilityLevel": "JAVA_17",
"mixins": [
"RedeployingMixin"
],
"injectors": {
"defaultRequire": 1
}
}

View File

@ -0,0 +1,16 @@
{
"required": true,
"package": "net.touhoudiscord.mixin",
"compatibilityLevel": "JAVA_17",
"mixins": [
"BuyStationScreenMixin",
"RedeployingMixin",
"RedeployTimerMixin"
],
"injectors": {
"defaultRequire": 1
},
"client": [
"ClientBuyStationScreenMixin"
]
}