Compare commits

..

14 Commits
0.0.2 ... main

Author SHA1 Message Date
1427eb9e9a Add initial ideas for Chipi Mod features and enhancements 2025-12-27 18:08:21 +01:00
0ad489f82f Add pregnant effect texture 2025-12-27 18:08:16 +01:00
43ad0cca5a Add Jingle Block and associated features, including tooltips, recipes, and models 2025-12-27 18:08:11 +01:00
72274c08f8 Add contributing guide with Java style formatting rules 2025-12-25 16:35:09 +01:00
6b95a3891f Refactor imports and improve code organization across multiple classes
- Reordered and grouped import statements in various files for better readability.
- Removed unnecessary imports and cleaned up unused code.
- Simplified constructors and methods in several classes to enhance clarity.
- Standardized formatting and spacing for consistency throughout the codebase.
- Ensured that all necessary imports are included where required, particularly in structure-related classes.
2025-12-25 16:34:58 +01:00
36e2b9a886 Add pregnancy effect and related features, including jump clamping and denial of drinking milk 2025-12-23 05:38:26 +01:00
932254b1d9 Add music discs and associated features, including recipes, sounds, and textures 2025-12-23 05:38:17 +01:00
1b9f7d85c0 Add PlayerJr entity and associated rendering and command features 2025-12-22 20:01:56 +01:00
77d7e7412e Add server.properties and server icon for Minecraft server configuration 2025-12-22 20:01:47 +01:00
fd94a17dbe Enhance inject_options function to copy server.properties and server icon to run directory 2025-12-22 20:01:37 +01:00
758cce1623 Bump version to 0.0.3 in build.gradle 2025-12-22 20:01:31 +01:00
629ab9fba3 Add player milk item and associated features: tooltips, sound, and model 2025-12-22 16:58:54 +01:00
6c530c2651 Add chipper ore generation and player milk interaction features 2025-12-22 16:58:40 +01:00
0d5bc6a5bf Enhance chipper script: add run directory management and role-based execution; update options for multiplayer warnings 2025-12-22 16:57:43 +01:00
104 changed files with 2054 additions and 247 deletions

2
.gitignore vendored
View File

@ -36,3 +36,5 @@ loom-cache/
__pycache__/
*.pyc
run-*/

270
CONTRIBUTING.md Normal file
View File

@ -0,0 +1,270 @@
# Contributing Guide (Java Style)
This project follows a strict, C/C++-style Java formatting rule set. The goal is to preserve intent, control flow clarity, and fast mental parsing. Please follow these rules for any Java changes.
If a tool or formatter fights these rules, the tool is wrong.
## Core Philosophy
- Java is written as if it were C/C++.
- Formatting must never change perceived control flow.
- Readability means fast, correct parsing in your head.
- Tools adapt to the codebase, not the other way around.
## Indentation & Whitespace
- Indentation: 4 spaces.
- No tabs.
- Avoid excessive vertical whitespace.
- Blank lines only when they add logical separation.
✅ Good:
```java
int sum(int a, int b) {
int r = a + b;
return r;
}
```
❌ Bad:
```java
int sum(int a, int b)
{
int r = a + b;
return r;
}
```
## Braces
- Braces are always required.
- Opening brace stays on the same line.
- Never rely on implicit scopes.
✅ Good:
```java
if (value == null) {
return;
}
```
❌ Bad:
```java
if (value == null)
{
return;
}
```
## Imports (Strict Rules)
### Never use fully-qualified class names inline
❌ Forbidden:
```java
net.minecraft.util.math.BlockPos pos = new net.minecraft.util.math.BlockPos(0, 0, 0);
```
✅ Required:
```java
import net.minecraft.util.math.BlockPos;
BlockPos pos = new BlockPos(0, 0, 0);
```
### Import grouping and ordering
1) Imports from external/other packages first.
2) Blank line.
3) Imports from this project (net.Chipperfluff.*) next.
Inside each block, order by:
1) Classes
2) Functions (static method imports)
3) Variables/constants (static field imports)
4) Enums
If there are no project imports, do not add a blank line.
## Method Signatures
- Method signatures must stay on one line.
- Do not break parameters across multiple lines.
- Long lines are acceptable; ambiguity is not.
✅ Good:
```java
void processUser(User user, int flags, boolean force, long timeout) {
...
}
```
❌ Bad:
```java
void processUser(
User user,
int flags,
boolean force,
long timeout
) {
...
}
```
## Function Calls & Argument Layout
### Default rule
- Single-line calls are preferred.
- Flat calls stay flat.
- Length alone is not a reason to wrap.
✅ Good:
```java
Color color = user.getProfile().getSettings().getTheme().getPrimaryColor();
```
### Multiline calls are allowed only when:
- The call is structural or declarative.
- Each argument is conceptually distinct.
- Nesting would otherwise hide meaning.
✅ Good (structural/declarative):
```java
BiomeModifications.addFeature(
BiomeSelectors.foundInOverworld(),
GenerationStep.Feature.UNDERGROUND_ORES,
RegistryKey.of(
RegistryKeys.PLACED_FEATURE,
new Identifier("chipi", "chipper_ore")
)
);
```
❌ Bad (no semantic gain):
```java
doThing(
a,
b,
c
);
```
## Chained Calls
- Flat chains stay on one line.
- Long or builder-style chains may be split vertically.
- Each chained step gets its own line.
✅ Good:
```java
builder
.withColor(theme.getPrimaryColor())
.withSize(32)
.enableShadow()
.build();
```
❌ Bad:
```java
builder.withColor(
theme.getPrimaryColor()
).withSize(
32
).enableShadow().build();
```
## Control Flow & Returns
- Early returns are encouraged.
- Avoid artificial nesting.
- Single-exit functions are not required.
✅ Good:
```java
void handle(User user) {
if (user == null) return;
if (!user.isActive()) return;
process(user);
}
```
❌ Bad:
```java
void handle(User user) {
if (user != null) {
if (user.isActive()) {
process(user);
}
}
}
```
## One-Liners
- One-liners allowed for simple guard clauses.
- No complex logic on one line.
✅ Allowed:
```java
if (value == null) return;
```
❌ Not allowed:
```java
if (a == b && c != d && flag && check()) doThing();
```
## Null Handling
- null is a valid, intentional state.
- Do not over-engineer around it.
- Prefer clarity over defensive clutter.
✅ Good:
```java
User user = findUser(id);
if (user == null) return;
```
## Logging & Debug Output
- Logging must be short and readable.
- Prefer System.out.println for quick diagnostics.
- Verbose logging only when justified.
✅ Good:
```java
System.out.println("Loaded structure: " + id);
```
## Autoformatters & Linters
Autoformatters must not:
- Break method signatures.
- Move braces to new lines.
- Introduce unwanted trailing newlines.
- Rewrap stable code repeatedly.
If a formatter fights these rules: disable or reconfigure it.
## Empty Methods
Empty methods or constructors must be written on one line:
```java
public static void register() {}
```
## Summary (Non-Negotiable)
- Java written with C/C++ structure.
- Compact signatures, explicit layout.
- Imports always used (no fully-qualified inline types).
- Multiline formatting only when it adds meaning.
- Formatting reflects logic, not fashion.

121
IDEAS.md Normal file
View File

@ -0,0 +1,121 @@
# Chipi Mod Ideas
## Progression Loop
### Idea
Add a portal activator + key system that unlocks deeper rooms in the Chipi dimension.
### What It Does
Creates a clear loop: build portal → explore → collect keys → access new room tiers.
## Dungeon Variety
### Idea
Add multiple room archetypes: loot rooms, trap rooms, dead ends, vertical shafts.
### What It Does
Breaks up repetition and makes each run feel less predictable.
## Chipi Loot Tables
### Idea
Create loot tables for rooms/corridors with unique tools, curios, and lore items.
### What It Does
Rewards exploration beyond ore grinding.
## Miniboss or Boss
### Idea
Add a rare boss like "Mep Alpha" or "Void Warden" that guards a core material.
### What It Does
Gives a big milestone and a reason to return to the dimension.
## Player Jr Modes
### Idea
Add follow/guard/wander + sit/teleport behaviors, plus simple leveling perks.
### What It Does
Turns Player Jr into a companion system instead of a single behavior.
## Jr Recall Item
### Idea
Add a "Jr Whistle" to recall Player Jr to you or to the portal spawn.
### What It Does
Fixes pathing loss and makes Jr easier to manage.
## Aura-Driven Tool Skills
### Idea
Add a short dash, shield, or blink that consumes the aura bar.
### What It Does
Lets the aura system do more than passive energy display.
## Chipi Shrine
### Idea
Add a recharge block or ritual that refills aura energy for materials.
### What It Does
Creates a resource sink and a strategic rest point.
## Expanded Milk Uses
### Idea
Use Mep Milk and Player Milk in brewing or special recipes.
### What It Does
Gives the items more long-term value.
## Mep Variants
### Idea
Add a rare friendly Mep variant with unique drops or interactions.
### What It Does
Adds discovery moments and variety to the mob.
## Chipper Upgrades
### Idea
Add reinforced or blessed chipper armor/tools with small set bonuses.
### What It Does
Extends gear progression past base chipper items.
## Portal Effects
### Idea
Add particles, ambient hum, and a short screen tint on teleport.
### What It Does
Makes portal travel feel more special.
## Void Hazard Zones
### Idea
Add a "void fog" or patches that drain aura instead of insta-kill.
### What It Does
Creates tension without instant death.
## Dimension Ambience
### Idea
Custom sky color, fog distance, and looping ambient sound in Chipi.
### What It Does
Strengthens the dimension's atmosphere.
## Chipi Compass
### Idea
Add a compass item that points to the nearest room or portal.
### What It Does
Helps navigation in the dungeon grid.
## Block Family
### Idea
Add stairs/slabs/walls for Chipi blocks.
### What It Does
Improves building options in the dimension.
## Rare Ore Nodes
### Idea
Add rare geodes or ore nodes unique to Chipi for discs or relics.
### What It Does
Creates a high-value hunt target.
## Trader NPC
### Idea
Add a small trader that spawns in certain rooms.
### What It Does
Introduces a non-hostile encounter with unique trades.
## New Advancements
### Idea
Add a few fun advancements for Mep interactions and Player Jr milestones.
### What It Does
Highlights quirky features and gives players goals.
## Structure Test Tools
### Idea
Add a command for random room placement to quickly test layouts.
### What It Does
Speeds up iteration on dungeon design.

View File

@ -3,7 +3,7 @@ plugins {
id 'maven-publish'
}
version = "0.0.2"
version = "0.0.3"
group = "net.Chipperfluff"
repositories {
@ -25,3 +25,21 @@ java {
languageVersion = JavaLanguageVersion.of(17)
}
}
loom {
runs {
server {
server()
runDir "run-server"
}
client1 {
client()
runDir "run-1"
}
client2 {
client()
runDir "run-2"
}
}
}

317
chipper
View File

@ -1,119 +1,236 @@
#!/usr/bin/env bash
# Do NOT use `set -e` with Gradle
set -u
RUN_DIR="run"
OPTIONS_SRC="./config/options.txt"
OPTIONS_DST="$RUN_DIR/options.txt"
GRADLE="./gradlew"
GRADLE_FLAGS=(
GRADLE_BASE_FLAGS=(
"--console=plain"
"--configuration-cache"
"--no-daemon"
"-Dfabric.development=true"
"-Dminecraft.api.env=dev"
"-Dminecraft.session.disable=true"
)
# ---------- helpers ----------
OPTIONS_SRC="./config/options.txt"
inject_options() {
if [[ -f "$OPTIONS_SRC" ]]; then
echo "[*] Injecting options.txt..."
cp "$OPTIONS_SRC" "$OPTIONS_DST"
else
echo "[!] Warning: $OPTIONS_SRC not found, skipping injection"
fi
}
ROLE=""
ROLE_INDEX=""
PASSTHROUGH=()
ensure_run_dir() {
if [[ ! -d "$RUN_DIR" ]]; then
echo "[*] Creating run directory..."
mkdir -p "$RUN_DIR"
fi
}
# ---------- utils ----------
# ---------- actions ----------
rand() { shuf -i 0-$(($1 - 1)) -n 1; }
clean() {
clear
echo "[*] Running gradle clean..."
$GRADLE clean "${GRADLE_FLAGS[@]}"
}
build() {
clear
ensure_run_dir
inject_options
echo "[*] Running client..."
$GRADLE runClient "${GRADLE_FLAGS[@]}"
}
show_tree() {
clear
echo "[*] Project tree (respecting .gitignore):"
tree --gitignore
}
ship() {
clear
echo "[*] Building project..."
$GRADLE build "${GRADLE_FLAGS[@]}"
echo "[+] Build ready to ship."
}
# ---------- help ----------
help_menu() {
cat <<'EOF'
🐿️ S Q U I R R E L B U I L D T O O L 🐿️
Usage:
./chipper [option]
Options:
--clean Run gradle clean
--build Run client (fast path)
--tree Show project tree only
--ship Build project
--help Show this help menu
Notes:
- Uses Gradle configuration cache
- Plain console output (no fake progress)
- Safe to Ctrl+C after EXECUTION starts
The squirrel believes in you.
EOF
}
# ---------- argument parsing ----------
if [[ $# -eq 0 ]]; then
help_menu
exit 0
fi
case "$1" in
--clean)
clean
parse_args() {
local cleaned=()
for ((i=0; i<${#ARGS[@]}; i++)); do
case "${ARGS[$i]}" in
-s|--server)
ROLE="server"
;;
--build)
build
-p1|--player1)
ROLE="client"
ROLE_INDEX=1
;;
--tree)
show_tree
-p2|--player2)
ROLE="client"
ROLE_INDEX=2
;;
--ship)
ship
;;
--help|-h)
help_menu
--)
PASSTHROUGH=( "${ARGS[@]:$((i+1))}" )
break
;;
*)
echo "[!] Unknown option: $1"
echo ""
help_menu
cleaned+=( "${ARGS[$i]}" )
;;
esac
done
ARGS=( "${cleaned[@]}" )
}
# ---------- run-dir helpers ----------
prepare_run_dir() {
local dir="$1"
rm -rf "$dir"
mkdir -p "$dir"
}
inject_options() {
local dir="$1"
# options.txt
if [[ -f "$OPTIONS_SRC" ]]; then
cp "$OPTIONS_SRC" "$dir/options.txt"
fi
# servers.dat (client server list)
if [[ -f "./config/servers.dat" ]]; then
cp "./config/servers.dat" "$dir/servers.dat"
fi
# server.properties
if [[ -f "./config/server.properties" ]]; then
cp "./config/server.properties" "$dir/server.properties"
fi
# server icon
if [[ -f "./config/server-icon.png" ]]; then
cp "./config/server-icon.png" "$dir/server-icon.png"
fi
}
ensure_server_files() {
local dir="$1"
[[ -f "$dir/eula.txt" ]] || echo "eula=true" > "$dir/eula.txt"
if [[ ! -f "$dir/server.properties" ]]; then
cat > "$dir/server.properties" <<EOF
online-mode=false
server-port=25565
level-name=world
enable-command-block=true
EOF
fi
}
# ---------- squirrel ----------
header() {
local action="$1"
local squirrels=(
' /\_/\
( o.o )
> ^ <'
' /\_/\
( -.- )
o_(")(")'
' /\_/\
( @.@ )
> ~ <'
)
clear
echo "${squirrels[$(rand ${#squirrels[@]})]}"
echo
case "$action" in
run) echo "🐿️ running chipper" ;;
clean) echo "🐿️ cleaning workspace" ;;
build) echo "🐿️ building project" ;;
tree) echo "🐿️ observing project tree" ;;
*) echo "🐿️ thinking..." ;;
esac
echo
}
# ---------- commands ----------
cmd_run() {
header run
case "$ROLE" in
server)
run_dir="run-server"
prepare_run_dir "$run_dir"
inject_options "$run_dir"
ensure_server_files "$run_dir"
echo "🖥️🐿️ starting dedicated server"
exec "$GRADLE" runServer "${GRADLE_BASE_FLAGS[@]}" "${PASSTHROUGH[@]}"
;;
client)
run_dir="run-$ROLE_INDEX"
task="runClient$ROLE_INDEX"
prepare_run_dir "$run_dir"
inject_options "$run_dir"
echo "🎮🐿️ starting client $ROLE_INDEX"
exec "$GRADLE" "$task" "${GRADLE_BASE_FLAGS[@]}" "${PASSTHROUGH[@]}"
;;
*)
echo "❌🐿️ no role selected"
echo "👉🐿️ use one of:"
echo " --server"
echo " --player1"
echo " --player2"
exit 1
;;
esac
}
cmd_clean() {
header clean
"$GRADLE" clean "${GRADLE_BASE_FLAGS[@]}" "${PASSTHROUGH[@]}"
}
cmd_build() {
header build
"$GRADLE" build "${GRADLE_BASE_FLAGS[@]}" "${PASSTHROUGH[@]}"
}
cmd_tree() {
header tree
tree --gitignore
}
cmd_help() {
cat <<'EOF'
/\_/\
( o.o )
> ^ <
🐿️ C H I P P E R
Usage:
./chipper run [role] [-- passthrough]
Roles:
-s, --server Start ONLY server
-p1, --player1 Start ONLY client 1
-p2, --player2 Start ONLY client 2
Other commands:
clean Gradle clean
build Gradle build
tree Show project tree
help Show this menu
Examples:
./chipper run --server
./chipper run --player1
./chipper run --player2
./chipper run --player1 -- --debug
./chipper build
./chipper tree
Notes:
- No legacy player spawning
- Each role has its own run directory
- options.txt injected per run dir
- Everything after `--` is forwarded
- The squirrel is mandatory
EOF
}
# ---------- entry ----------
ARGS=( "$@" )
parse_args
case "${ARGS[0]:-help}" in
run) cmd_run ;;
clean) cmd_clean ;;
build) cmd_build ;;
tree) cmd_tree ;;
help|-h) cmd_help ;;
*)
echo "[!] Unknown command: ${ARGS[0]}"
cmd_help
exit 1
;;
esac

View File

@ -72,8 +72,8 @@ tutorialStep:movement
mouseWheelSensitivity:1.0
rawMouseInput:true
glDebugVerbosity:1
skipMultiplayerWarning:false
skipRealms32bitWarning:false
skipMultiplayerWarning:true
skipRealms32bitWarning:true
hideMatchedNames:true
joinedFirstServer:false
hideBundleTutorial:false

BIN
config/server-icon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 417 B

58
config/server.properties Normal file
View File

@ -0,0 +1,58 @@
#Minecraft server properties
#Mon Dec 22 17:38:51 CET 2025
allow-flight=false
allow-nether=true
broadcast-console-to-ops=true
broadcast-rcon-to-ops=true
difficulty=easy
enable-command-block=true
enable-jmx-monitoring=false
enable-query=false
enable-rcon=false
enable-status=true
enforce-secure-profile=true
enforce-whitelist=false
entity-broadcast-range-percentage=100
force-gamemode=false
function-permission-level=2
gamemode=survival
generate-structures=true
generator-settings={}
hardcore=false
hide-online-players=false
initial-disabled-packs=
initial-enabled-packs=vanilla,fabric
level-name=world
level-seed=
level-type=minecraft\:normal
max-chained-neighbor-updates=1000000
max-players=20
max-tick-time=60000
max-world-size=29999984
motd=§6chipi §edev §7server\n§8local test instance
network-compression-threshold=256
online-mode=false
op-permission-level=4
player-idle-timeout=0
prevent-proxy-connections=false
pvp=true
query.port=25565
rate-limit=0
rcon.password=
rcon.port=25575
require-resource-pack=false
resource-pack=
resource-pack-prompt=
resource-pack-sha1=
server-ip=
server-port=25565
simulation-distance=10
spawn-animals=true
spawn-monsters=true
spawn-npcs=true
spawn-protection=16
sync-chunk-writes=true
text-filtering-config=
use-native-transport=true
view-distance=10
white-list=false

BIN
config/servers.dat Normal file

Binary file not shown.

View File

@ -1,20 +1,23 @@
package net.Chipperfluff.chipi;
import net.fabricmc.api.ModInitializer;
import net.fabricmc.fabric.api.object.builder.v1.entity.FabricDefaultAttributeRegistry;
import net.Chipperfluff.chipi.advancement.ModCriteria;
import net.Chipperfluff.chipi.block.ModBlocks;
import net.Chipperfluff.chipi.command.CommandHandler;
import net.Chipperfluff.chipi.effect.ChipiBlessingEvents;
import net.Chipperfluff.chipi.effect.ChipiHungerHandler;
import net.Chipperfluff.chipi.effect.ModEffects;
import net.Chipperfluff.chipi.entity.ModEntities;
import net.Chipperfluff.chipi.entity.SpawnLogic;
import net.Chipperfluff.chipi.entity.MepEntity;
import net.Chipperfluff.chipi.entity.ModEntities;
import net.Chipperfluff.chipi.entity.PlayerJrEntity;
import net.Chipperfluff.chipi.entity.SpawnLogic;
import net.Chipperfluff.chipi.item.ModItemGroups;
import net.Chipperfluff.chipi.item.ModItems;
import net.Chipperfluff.chipi.item.music.ModMusicDiscs;
import net.Chipperfluff.chipi.server.ChipiServerEvents;
import net.Chipperfluff.chipi.sound.ModSounds;
import net.fabricmc.api.ModInitializer;
import net.fabricmc.fabric.api.object.builder.v1.entity.FabricDefaultAttributeRegistry;
import net.Chipperfluff.chipi.util.TickScheduler;
public class ChipiMod implements ModInitializer {
@ -35,12 +38,15 @@ public class ChipiMod implements ModInitializer {
SpawnLogic.register();
ModSounds.register();
ModMusicDiscs.registerAll();
TickScheduler.init();
CommandHandler.register();
FabricDefaultAttributeRegistry.register(ModEntities.MEP, MepEntity.createMepAttributes());
FabricDefaultAttributeRegistry.register(ModEntities.PLAYER_JR, PlayerJrEntity.createAttributes());
ChipiServerEvents.register();
}
}

View File

@ -5,14 +5,9 @@ import net.minecraft.advancement.criterion.Criteria;
public class ModCriteria {
public static final PortalActivatedTrigger PORTAL_ACTIVATED = Criteria.register(new PortalActivatedTrigger());
public static final PortalDestroyedTrigger PORTAL_DESTROYED = Criteria.register(new PortalDestroyedTrigger());
public static final VoidConsumedTrigger VOID_CONSUMED_TRIGGER = Criteria.register(new VoidConsumedTrigger());
public static final VoidConsumedFireTrigger VOID_CONSUMED_FIRE_TRIGGER = Criteria.register(new VoidConsumedFireTrigger());
public static void register() {
// classload trigger
}
public static void register() {}
}

View File

@ -1,6 +1,5 @@
package net.Chipperfluff.chipi.block;
import net.Chipperfluff.chipi.advancement.ModCriteria;
import net.minecraft.block.BlockState;
import net.minecraft.block.PillarBlock;
import net.minecraft.entity.LivingEntity;
@ -9,6 +8,8 @@ import net.minecraft.server.network.ServerPlayerEntity;
import net.minecraft.util.math.BlockPos;
import net.minecraft.world.World;
import net.Chipperfluff.chipi.advancement.ModCriteria;
public class ChipperFrameBlock extends PillarBlock {
public ChipperFrameBlock(Settings settings) {

View File

@ -15,7 +15,6 @@ import net.minecraft.util.shape.VoxelShape;
import net.minecraft.util.shape.VoxelShapes;
import net.minecraft.world.BlockView;
import net.minecraft.world.World;
import java.util.EnumSet;
public class ChipperPortalBlock extends Block {
@ -71,7 +70,6 @@ public class ChipperPortalBlock extends Block {
player.setOnGround(true);
}
public static BlockPos resolveSafeSpawn(ServerWorld targetWorld) {
BlockPos spawn = DEFAULT_SPAWN;
BlockPos under = spawn.down();

View File

@ -1,6 +1,5 @@
package net.Chipperfluff.chipi.block;
import net.Chipperfluff.chipi.advancement.ModCriteria;
import net.minecraft.block.BlockState;
import net.minecraft.server.network.ServerPlayerEntity;
import net.minecraft.server.world.ServerWorld;
@ -8,6 +7,8 @@ import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.Direction;
import net.minecraft.world.World;
import net.Chipperfluff.chipi.advancement.ModCriteria;
public class ChipperPortalShape {
private final World world;

View File

@ -1,6 +1,5 @@
package net.Chipperfluff.chipi.block;
import net.Chipperfluff.chipi.ChipiMod;
import net.fabricmc.fabric.api.object.builder.v1.block.FabricBlockSettings;
import net.minecraft.block.AbstractBlock;
import net.minecraft.block.Block;
@ -11,6 +10,8 @@ import net.minecraft.registry.Registries;
import net.minecraft.registry.Registry;
import net.minecraft.util.Identifier;
import net.Chipperfluff.chipi.ChipiMod;
public class ModBlocks {
public static final Block VOID_BLOCK = Registry.register(
@ -52,5 +53,16 @@ public class ModBlocks {
new Block(AbstractBlock.Settings.copy(Blocks.IRON_BLOCK).requiresTool())
);
public static final Block JINGLE_BLOCK = Registry.register(
Registries.BLOCK,
new Identifier(ChipiMod.MOD_ID, "jingle_block"),
new Block(
AbstractBlock.Settings.create()
.strength(2.0f)
.mapColor(MapColor.RED)
.requiresTool()
)
);
public static void register() {}
}

View File

@ -1,6 +1,5 @@
package net.Chipperfluff.chipi.block;
import net.Chipperfluff.chipi.advancement.ModCriteria;
import net.minecraft.block.Block;
import net.minecraft.block.BlockState;
import net.minecraft.entity.Entity;
@ -17,6 +16,8 @@ import net.minecraft.util.Identifier;
import net.minecraft.util.math.BlockPos;
import net.minecraft.world.World;
import net.Chipperfluff.chipi.advancement.ModCriteria;
public class VoidBlock extends Block {
public VoidBlock(Settings settings) {

View File

@ -1,12 +1,13 @@
package net.Chipperfluff.chipi.client;
import net.fabricmc.api.ClientModInitializer;
import net.fabricmc.fabric.api.blockrenderlayer.v1.BlockRenderLayerMap;
import net.minecraft.client.render.RenderLayer;
import net.Chipperfluff.chipi.block.ModBlocks;
import net.Chipperfluff.chipi.client.entity.ModEntityRenderers;
import net.Chipperfluff.chipi.client.hud.ChipiStatusBar;
import net.Chipperfluff.chipi.util.ClientTickScheduler;
import net.fabricmc.api.ClientModInitializer;
import net.fabricmc.fabric.api.blockrenderlayer.v1.BlockRenderLayerMap;
import net.minecraft.client.render.RenderLayer;
public class ChipiClient implements ClientModInitializer {

View File

@ -1,14 +1,14 @@
package net.Chipperfluff.chipi.client;
import net.Chipperfluff.chipi.block.ModBlocks;
import net.Chipperfluff.chipi.item.ModItems;
import net.fabricmc.fabric.api.client.item.v1.ItemTooltipCallback;
import net.minecraft.text.Text;
import net.Chipperfluff.chipi.block.ModBlocks;
import net.Chipperfluff.chipi.item.ModItems;
public final class ModTooltips {
private ModTooltips() {
}
private ModTooltips() {}
public static void register() {
@ -30,6 +30,9 @@ public final class ModTooltips {
if (stack.isOf(ModBlocks.CHIPPER_ALLOY_BLOCK.asItem()))
lines.add(Text.translatable("tooltip.chipi.chipper_alloy_block"));
if (stack.isOf(ModBlocks.JINGLE_BLOCK.asItem()))
lines.add(Text.translatable("tooltip.chipi.jingle_block"));
// ===== ITEMS =====
if (stack.isOf(ModItems.RAW_CHIPPER_ORE))
lines.add(Text.translatable("tooltip.chipi.raw_chipper_ore"));
@ -50,6 +53,9 @@ public final class ModTooltips {
if (stack.isOf(ModItems.MEP_MILK))
lines.add(Text.translatable("tooltip.chipi.mep_milk"));
if (stack.isOf(ModItems.PLAYER_MILK))
lines.add(Text.translatable("tooltip.chipi.player_milk"));
// ===== ARMOR =====
if (stack.isOf(ModItems.CHIPPER_HELMET))
lines.add(Text.translatable("tooltip.chipi.chipper_helmet"));

View File

@ -1,13 +1,14 @@
package net.Chipperfluff.chipi.client.entity;
import net.Chipperfluff.chipi.ChipiMod;
import net.Chipperfluff.chipi.entity.MepEntity;
import net.minecraft.client.render.entity.BipedEntityRenderer;
import net.minecraft.client.render.entity.EntityRendererFactory;
import net.minecraft.client.render.entity.model.BipedEntityModel;
import net.minecraft.client.render.entity.model.EntityModelLayers;
import net.minecraft.util.Identifier;
import net.Chipperfluff.chipi.ChipiMod;
import net.Chipperfluff.chipi.entity.MepEntity;
public class MepRenderer extends BipedEntityRenderer<MepEntity, BipedEntityModel<MepEntity>> {
private static final Identifier TEXTURE = new Identifier(ChipiMod.MOD_ID, "textures/entity/mep.png");

View File

@ -1,11 +1,13 @@
package net.Chipperfluff.chipi.client.entity;
import net.Chipperfluff.chipi.entity.ModEntities;
import net.fabricmc.fabric.api.client.rendering.v1.EntityRendererRegistry;
import net.Chipperfluff.chipi.entity.ModEntities;
public class ModEntityRenderers {
public static void register() {
EntityRendererRegistry.register(ModEntities.MEP, MepRenderer::new);
EntityRendererRegistry.register(ModEntities.PLAYER_JR, PlayerJrRenderer::new);
}
}

View File

@ -0,0 +1,54 @@
package net.Chipperfluff.chipi.client.entity;
import net.minecraft.client.network.AbstractClientPlayerEntity;
import net.minecraft.client.render.VertexConsumerProvider;
import net.minecraft.client.render.entity.BipedEntityRenderer;
import net.minecraft.client.render.entity.EntityRendererFactory;
import net.minecraft.client.render.entity.model.BipedEntityModel;
import net.minecraft.client.render.entity.model.EntityModelLayers;
import net.minecraft.client.util.DefaultSkinHelper;
import net.minecraft.client.util.math.MatrixStack;
import net.minecraft.util.Identifier;
import net.Chipperfluff.chipi.entity.PlayerJrEntity;
public class PlayerJrRenderer
extends BipedEntityRenderer<PlayerJrEntity, BipedEntityModel<PlayerJrEntity>> {
public PlayerJrRenderer(EntityRendererFactory.Context ctx) {
super(ctx, new BipedEntityModel<>(ctx.getPart(EntityModelLayers.PLAYER)), 0.3f);
}
@Override
public void render(
PlayerJrEntity entity,
float yaw,
float tickDelta,
MatrixStack matrices,
VertexConsumerProvider vertices,
int light
) {
matrices.scale(0.5f, 0.5f, 0.5f);
super.render(entity, yaw, tickDelta, matrices, vertices, light);
}
@Override
public Identifier getTexture(PlayerJrEntity entity) {
String dadName = entity.getDadName();
if (dadName == null || dadName.isBlank()) {
return DefaultSkinHelper.getTexture();
}
/*
* THIS is how vanilla + mods do it:
* - stable Identifier
* - async skin download
* - cached by SkinProvider
*/
Identifier skin = AbstractClientPlayerEntity.getSkinId(dadName);
AbstractClientPlayerEntity.loadSkin(skin, dadName);
return skin;
}
}

View File

@ -1,12 +1,13 @@
package net.Chipperfluff.chipi.client.hud;
import net.Chipperfluff.chipi.item.ModItems;
import net.fabricmc.fabric.api.client.rendering.v1.HudRenderCallback;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.gui.DrawContext;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.entity.player.PlayerInventory;
import net.Chipperfluff.chipi.item.ModItems;
import static net.Chipperfluff.chipi.util.ChipiTrackedData.CHIPI_ENERGY;
public class ChipiStatusBar {

View File

@ -4,8 +4,6 @@ import com.mojang.brigadier.CommandDispatcher;
import com.mojang.brigadier.arguments.BoolArgumentType;
import com.mojang.brigadier.arguments.StringArgumentType;
import com.mojang.brigadier.context.CommandContext;
import net.Chipperfluff.chipi.world.gen.struct.ChipiStructures;
import net.minecraft.command.CommandSource;
import net.minecraft.command.argument.BlockPosArgumentType;
import net.minecraft.server.command.CommandManager;
@ -14,6 +12,8 @@ import net.minecraft.server.world.ServerWorld;
import net.minecraft.text.Text;
import net.minecraft.util.math.BlockPos;
import net.Chipperfluff.chipi.world.gen.struct.ChipiStructures;
public class ChpCommand {
public static void register(CommandDispatcher<ServerCommandSource> dispatcher) {

View File

@ -12,6 +12,7 @@ public final class CommandHandler {
(dispatcher, registryAccess, environment) -> {
ChpCommand.register(dispatcher);
CspCommand.register(dispatcher);
SpawnJrCommand.register(dispatcher);
}
);
}

View File

@ -3,8 +3,6 @@ package net.Chipperfluff.chipi.command;
import com.mojang.brigadier.CommandDispatcher;
import com.mojang.brigadier.arguments.StringArgumentType;
import com.mojang.brigadier.context.CommandContext;
import net.Chipperfluff.chipi.world.gen.struct.ChipiStructures;
import net.minecraft.command.CommandSource;
import net.minecraft.command.argument.BlockPosArgumentType;
import net.minecraft.server.command.CommandManager;
@ -13,6 +11,8 @@ import net.minecraft.server.world.ServerWorld;
import net.minecraft.text.Text;
import net.minecraft.util.math.BlockPos;
import net.Chipperfluff.chipi.world.gen.struct.ChipiStructures;
public class CspCommand {
public static void register(CommandDispatcher<ServerCommandSource> dispatcher) {

View File

@ -0,0 +1,88 @@
package net.Chipperfluff.chipi.command;
import com.mojang.brigadier.CommandDispatcher;
import com.mojang.brigadier.context.CommandContext;
import net.minecraft.command.argument.EntityArgumentType;
import net.minecraft.server.command.CommandManager;
import net.minecraft.server.command.ServerCommandSource;
import net.minecraft.server.network.ServerPlayerEntity;
import net.minecraft.server.world.ServerWorld;
import net.minecraft.text.Text;
import net.minecraft.util.math.BlockPos;
import net.Chipperfluff.chipi.entity.ModEntities;
import net.Chipperfluff.chipi.entity.PlayerJrEntity;
public final class SpawnJrCommand {
public static void register(CommandDispatcher<ServerCommandSource> dispatcher) {
dispatcher.register(
CommandManager.literal("spawnjr")
.requires(src -> src.hasPermissionLevel(2))
.then(
CommandManager.argument("dad", EntityArgumentType.player())
.executes(SpawnJrCommand::execute)
)
);
}
private static int execute(CommandContext<ServerCommandSource> ctx) {
ServerCommandSource source = ctx.getSource();
System.out.println("[spawnjr] ================================");
System.out.println("[spawnjr] Command invoked");
try {
ServerWorld world = source.getWorld();
ServerPlayerEntity dad = EntityArgumentType.getPlayer(ctx, "dad");
ServerPlayerEntity spawner = source.getPlayerOrThrow();
BlockPos pos = spawner.getBlockPos();
System.out.println("[spawnjr] Dad = " + dad.getName().getString());
System.out.println("[spawnjr] Dad UUID = " + dad.getUuidAsString());
System.out.println("[spawnjr] World = " + world.getRegistryKey().getValue());
System.out.println("[spawnjr] Spawn pos = " + pos);
PlayerJrEntity jr = new PlayerJrEntity(ModEntities.PLAYER_JR, world);
System.out.println("[spawnjr] Entity constructed");
jr.refreshPositionAndAngles(
pos.getX() + 0.5,
pos.getY(),
pos.getZ() + 0.5,
world.random.nextFloat() * 360f,
0f
);
// single source of truth
jr.setDad(dad);
System.out.println("[spawnjr] Dad relationship set");
boolean spawned = world.spawnEntity(jr);
System.out.println("[spawnjr] spawnEntity() returned = " + spawned);
if (!spawned) {
source.sendError(Text.literal("[spawnjr] Spawn failed (entity rejected by world)"));
return 0;
}
System.out.println("[spawnjr] SUCCESS");
System.out.println("[spawnjr] ================================");
return 1;
} catch (Exception e) {
System.out.println("[spawnjr] ================================");
System.out.println("[spawnjr] CRASH");
System.out.println("[spawnjr] hey message Chipperfluff");
System.out.println("[spawnjr] Exception: " + e.getClass().getName());
System.out.println("[spawnjr] Message: " + e.getMessage());
e.printStackTrace();
source.sendError(Text.literal(
"[spawnjr] Internal error. Check logs. (hey message Chipperfluff)"
));
return 0;
}
}
}

View File

@ -1,16 +1,28 @@
package net.Chipperfluff.chipi.effect;
import net.Chipperfluff.chipi.ChipiMod;
import net.minecraft.entity.effect.StatusEffect;
import net.minecraft.registry.Registries;
import net.minecraft.registry.Registry;
import net.minecraft.util.Identifier;
import net.Chipperfluff.chipi.ChipiMod;
public class ModEffects {
public static final StatusEffect CHIPI_BLESSING = new ChipiBlessingEffect();
public static final StatusEffect PREGNANT = new PregnantEffect();
public static void register() {
Registry.register(Registries.STATUS_EFFECT, new Identifier(ChipiMod.MOD_ID, "chipi_blessing"), CHIPI_BLESSING);
Registry.register(
Registries.STATUS_EFFECT,
new Identifier(ChipiMod.MOD_ID, "chipi_blessing"),
CHIPI_BLESSING
);
Registry.register(
Registries.STATUS_EFFECT,
new Identifier(ChipiMod.MOD_ID, "pregnant"),
PREGNANT
);
}
}

View File

@ -0,0 +1,127 @@
package net.Chipperfluff.chipi.effect;
import net.minecraft.entity.LivingEntity;
import net.minecraft.entity.attribute.EntityAttributeInstance;
import net.minecraft.entity.attribute.EntityAttributes;
import net.minecraft.entity.attribute.EntityAttributeModifier;
import net.minecraft.entity.effect.StatusEffect;
import net.minecraft.entity.effect.StatusEffectCategory;
import net.minecraft.entity.effect.StatusEffectInstance;
import net.minecraft.server.network.ServerPlayerEntity;
import net.minecraft.text.Text;
import java.util.UUID;
import net.Chipperfluff.chipi.entity.ModEntities;
import net.Chipperfluff.chipi.entity.PlayerJrEntity;
public class PregnantEffect extends StatusEffect {
public static final int TOTAL_DURATION = 20 * 60 * 10; // 10 min
private static final double MAX_SLOW = 0.90;
private static final UUID SPEED_UUID =
UUID.fromString("7c8b6b8f-4b6c-4f6f-9b7b-01c8e9f8a111");
public PregnantEffect() {
super(StatusEffectCategory.HARMFUL, 0xFFB6C1);
}
/* ---------- helpers used by mixin ---------- */
/** 0.0 → normal jump, 1.0 → no jump */
public static double getJumpClamp(ServerPlayerEntity player) {
StatusEffectInstance inst = player.getStatusEffect(ModEffects.PREGNANT);
if (inst == null) return 0.0;
double progress =
1.0 - (inst.getDuration() / (double) TOTAL_DURATION);
return Math.min(Math.max(progress, 0.0), 1.0);
}
/* ---------- ticking ---------- */
@Override
public boolean canApplyUpdateEffect(int duration, int amplifier) {
return true;
}
@Override
public void applyUpdateEffect(LivingEntity entity, int amplifier) {
if (!(entity instanceof ServerPlayerEntity player)) return;
StatusEffectInstance inst = player.getStatusEffect(this);
if (inst == null) return;
int remaining = inst.getDuration();
double progress =
1.0 - (remaining / (double) TOTAL_DURATION);
progress = Math.min(Math.max(progress, 0.0), 1.0);
applySpeedModifier(player, progress * MAX_SLOW);
if (remaining % 400 == 0 && remaining > 20) {
player.sendMessage(
Text.literal("Your knees file a formal complaint…"),
true
);
}
if (remaining == 1) {
finishBirth(player);
}
}
private void applySpeedModifier(ServerPlayerEntity player, double slowAmount) {
EntityAttributeInstance attr =
player.getAttributeInstance(EntityAttributes.GENERIC_MOVEMENT_SPEED);
if (attr == null) return;
EntityAttributeModifier old = attr.getModifier(SPEED_UUID);
if (old != null) attr.removeModifier(old);
if (slowAmount > 0.01) {
attr.addTemporaryModifier(new EntityAttributeModifier(
SPEED_UUID,
"Pregnancy slowdown",
-slowAmount,
EntityAttributeModifier.Operation.MULTIPLY_TOTAL
));
}
}
private void finishBirth(ServerPlayerEntity dad) {
// cleanup
EntityAttributeInstance attr =
dad.getAttributeInstance(EntityAttributes.GENERIC_MOVEMENT_SPEED);
if (attr != null) {
EntityAttributeModifier old = attr.getModifier(SPEED_UUID);
if (old != null) attr.removeModifier(old);
}
dad.removeStatusEffect(this);
PlayerJrEntity jr = ModEntities.PLAYER_JR.create(dad.getWorld());
if (jr == null) return;
jr.refreshPositionAndAngles(
dad.getX(), dad.getY(), dad.getZ(),
dad.getYaw(), dad.getPitch()
);
jr.setDad(dad);
dad.getWorld().spawnEntity(jr);
dad.sendMessage(
Text.literal(
"You're a dad now. The names " +
jr.getName().getString() +
". Warning: it might cheat."
),
false
);
}
}

View File

@ -3,6 +3,7 @@ package net.Chipperfluff.chipi.entity;
import net.minecraft.block.BlockState;
import net.minecraft.block.SlabBlock;
import net.minecraft.block.StairsBlock;
import net.minecraft.entity.Entity;
import net.minecraft.entity.EntityType;
import net.minecraft.entity.LivingEntity;
import net.minecraft.entity.ai.goal.*;
@ -81,7 +82,7 @@ public class MepEntity extends PathAwareEntity {
// === ATTACK OVERRIDE (AURA IMMUNITY) ===
@Override
public boolean tryAttack(net.minecraft.entity.Entity target) {
public boolean tryAttack(Entity target) {
if (target instanceof PlayerEntity player) {
if (ProtectionAuraHandler.hasAura(player)) {
return false; // chase but never hit

View File

@ -1,12 +1,9 @@
package net.Chipperfluff.chipi.entity;
import net.Chipperfluff.chipi.entity.MepEntity;
import net.fabricmc.fabric.api.object.builder.v1.entity.FabricEntityTypeBuilder;
import net.minecraft.entity.EntityDimensions;
import net.minecraft.entity.SpawnGroup;
import net.minecraft.entity.EntityType;
import net.minecraft.entity.SpawnGroup;
import net.minecraft.registry.Registries;
import net.minecraft.registry.Registry;
import net.minecraft.util.Identifier;
@ -23,6 +20,15 @@ public final class ModEntities {
.build()
);
private ModEntities() {
}
public static final EntityType<PlayerJrEntity> PLAYER_JR =
Registry.register(
Registries.ENTITY_TYPE,
new Identifier("chipi", "player_jr"),
FabricEntityTypeBuilder
.create(SpawnGroup.CREATURE, PlayerJrEntity::new)
.dimensions(EntityDimensions.fixed(0.45f, 0.9f)) // small
.build()
);
private ModEntities() {}
}

View File

@ -0,0 +1,388 @@
package net.Chipperfluff.chipi.entity;
import net.minecraft.entity.EntityType;
import net.minecraft.entity.ai.goal.Goal;
import net.minecraft.entity.ai.goal.LookAroundGoal;
import net.minecraft.entity.ai.goal.LookAtEntityGoal;
import net.minecraft.entity.ai.goal.SwimGoal;
import net.minecraft.entity.attribute.DefaultAttributeContainer;
import net.minecraft.entity.attribute.EntityAttributes;
import net.minecraft.entity.damage.DamageSource;
import net.minecraft.entity.mob.MobEntity;
import net.minecraft.entity.mob.PathAwareEntity;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.nbt.NbtCompound;
import net.minecraft.server.network.ServerPlayerEntity;
import net.minecraft.text.Text;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.Vec3d;
import net.minecraft.world.World;
import java.util.UUID;
public class PlayerJrEntity extends PathAwareEntity {
/* ================= RULES (CHEATING FRIEND) ================= */
private static final double FORCE_FLY_DISTANCE = 50.0; // fly if farther than this (horizontal)
private static final int STUCK_TICKS_TO_FLY = 40; // 2 seconds of no horizontal progress
private static final double STUCK_MIN_CLOSING = 0.10; // horizontal progress threshold
private static final double STUCK_MIN_DISTANCE = 6.0; // don't care if already close
private static final double DAD_MOVING_EPS_SQ = 0.02; // if dad is moving, don't call it stuck
private static final int LAND_RADIUS = 6; // search dry landing within this radius
private static final double LAND_HORIZONTAL_DISTANCE = 2.0; // must be this close (horiz) to land
private static final double HOVER_HEIGHT = 1.25; // hover above landing block
private static final int MIN_FLY_TICKS = 20; // must fly at least this long
private static final double FLY_SPEED = 0.55;
private static final double FLY_ACCEL = 0.25;
private static final int MAX_VERTICAL_GAP = 6; // if dad is this high above -> fly
/* ================= STATE ================= */
private UUID dadUuid;
private String dadName = "";
private enum Mode { GROUND, FLYING }
private Mode mode = Mode.GROUND;
private int flyTicks = 0;
// Stuck tracking (ground)
private int stuckTicks = 0;
private double lastDadHorizDist = -1;
// Landing / hover state
private BlockPos landingSpot = null;
private BlockPos forcedLandingSpot = null;
/* ================= CONSTRUCTOR ================= */
public PlayerJrEntity(EntityType<? extends PathAwareEntity> type, World world) {
super(type, world);
}
public static DefaultAttributeContainer.Builder createAttributes() {
return MobEntity.createMobAttributes()
.add(EntityAttributes.GENERIC_MAX_HEALTH, 10.0)
.add(EntityAttributes.GENERIC_MOVEMENT_SPEED, 0.25)
.add(EntityAttributes.GENERIC_FOLLOW_RANGE, 64.0);
}
/* ================= DAD ================= */
public void setDad(ServerPlayerEntity dad) {
dadUuid = dad.getUuid();
dadName = dad.getGameProfile().getName();
setCustomName(Text.literal(dadName + " jr."));
setCustomNameVisible(true);
}
private ServerPlayerEntity getDad() {
if (dadUuid == null || getWorld().isClient) return null;
return getWorld().getServer().getPlayerManager().getPlayer(dadUuid);
}
public String getDadName() { return dadName; }
/* ================= AI ================= */
@Override
protected void initGoals() {
goalSelector.add(1, new SwimGoal(this));
goalSelector.add(2, new FollowDadGoal(this));
goalSelector.add(3, new LookAtEntityGoal(this, PlayerEntity.class, 6f));
goalSelector.add(4, new LookAroundGoal(this));
}
/* ================= TICK ================= */
@Override
public void tick() {
super.tick();
if (getWorld().isClient) return;
// Never fall damage ever (you asked this specifically)
this.fallDistance = 0.0f;
ServerPlayerEntity dad = getDad();
if (dad == null) return;
switch (mode) {
case GROUND -> doGroundBrain(dad);
case FLYING -> doFlyingBrain(dad);
}
}
/* ================= CORE BRAIN: GROUND ================= */
private void doGroundBrain(ServerPlayerEntity dad) {
// Water cheat: if land is nearby, prefer walking out; if no path, fly briefly to land.
if (this.isTouchingWater()) {
BlockPos escape = findLandingSpotNear(this.getBlockPos(), LAND_RADIUS);
if (escape != null) {
boolean canWalk = getNavigation().startMovingTo(
escape.getX() + 0.5,
escape.getY(),
escape.getZ() + 0.5,
1.1
);
if (canWalk) {
resetStuckTracking(dad);
return;
}
forcedLandingSpot = escape;
startFlying(dad);
return;
}
}
// Update stuck tracking (only on ground mode)
updateStuckTracking(dad);
if (stuckTicks >= STUCK_TICKS_TO_FLY) {
startFlying(dad);
return;
}
// Too far behind (impatient cheat)
if (horizontalDistanceTo(dad) > FORCE_FLY_DISTANCE) {
startFlying(dad);
return;
}
// Dad is way above us (can't reach)
double dy = dad.getY() - getY();
if (dy > MAX_VERTICAL_GAP && horizontalDistanceTo(dad) > 2.0) {
startFlying(dad);
}
}
private void updateStuckTracking(ServerPlayerEntity dad) {
boolean tryingToWalk = !getNavigation().isIdle();
double dadDist = horizontalDistanceTo(dad);
double dadSpeedSq = dad.getVelocity().horizontalLengthSquared();
if (!tryingToWalk || dadDist <= STUCK_MIN_DISTANCE || dadSpeedSq > DAD_MOVING_EPS_SQ) {
stuckTicks = 0;
lastDadHorizDist = dadDist;
return;
}
if (lastDadHorizDist < 0) {
lastDadHorizDist = dadDist;
stuckTicks = 0;
return;
}
double closing = lastDadHorizDist - dadDist; // positive means getting closer
if (closing < STUCK_MIN_CLOSING) stuckTicks++;
else stuckTicks = 0;
lastDadHorizDist = dadDist;
}
private void resetStuckTracking(ServerPlayerEntity dad) {
stuckTicks = 0;
lastDadHorizDist = horizontalDistanceTo(dad);
}
/* ================= CORE BRAIN: FLYING ================= */
private void startFlying(ServerPlayerEntity dad) {
mode = Mode.FLYING;
flyTicks = 0;
landingSpot = null;
setNoGravity(true);
getNavigation().stop();
setVelocity(Vec3d.ZERO);
velocityDirty = true;
dad.sendMessage(Text.literal(getName().getString() + " switched to Game Mode Creative"), false);
}
private void doFlyingBrain(ServerPlayerEntity dad) {
flyTicks++;
double horiz = horizontalDistanceTo(dad);
if (flyTicks >= MIN_FLY_TICKS) {
BlockPos candidate = forcedLandingSpot;
if (candidate == null) {
candidate = findLandingSpotNear(dad.getBlockPos(), LAND_RADIUS);
}
if (candidate != null && !wouldImmediatelyRefly(dad, candidate)) {
landingSpot = candidate;
if (horizontalDistanceTo(landingSpot) <= LAND_HORIZONTAL_DISTANCE) {
stopFlying(dad);
return;
}
}
}
BlockPos targetSpot = forcedLandingSpot != null ? forcedLandingSpot : landingSpot;
Vec3d target = targetSpot != null
? Vec3d.ofCenter(targetSpot).add(0, HOVER_HEIGHT, 0)
: dad.getPos().add(0, 2.2, 0);
flyTowardSmooth(target);
lookAtEntity(dad, 30f, 30f);
}
private void stopFlying(ServerPlayerEntity dad) {
setNoGravity(false);
mode = Mode.GROUND;
landingSpot = null;
forcedLandingSpot = null;
dad.sendMessage(Text.literal(getName().getString() + " switched to Game Mode Survival"), false);
}
/* ================= MOVEMENT HELPERS ================= */
private void flyTowardSmooth(Vec3d target) {
Vec3d delta = target.subtract(getPos());
double distSq = delta.lengthSquared();
if (distSq < 0.0006) {
setVelocity(getVelocity().multiply(0.6));
velocityDirty = true;
return;
}
Vec3d desired = delta.normalize().multiply(FLY_SPEED);
// stronger steer when far (prevents drifting)
double accel = (distSq > 64.0) ? Math.min(0.55, FLY_ACCEL * 2.0) : FLY_ACCEL;
Vec3d blended = getVelocity().multiply(1.0 - accel).add(desired.multiply(accel));
setVelocity(blended);
velocityDirty = true;
}
private double horizontalDistanceTo(ServerPlayerEntity dad) {
double dx = dad.getX() - getX();
double dz = dad.getZ() - getZ();
return Math.sqrt(dx * dx + dz * dz);
}
private double horizontalDistanceTo(BlockPos pos) {
double dx = pos.getX() + 0.5 - getX();
double dz = pos.getZ() + 0.5 - getZ();
return Math.sqrt(dx * dx + dz * dz);
}
/* ================= LAND SEARCH (DRY + SAFE) ================= */
private BlockPos findLandingSpotNear(BlockPos center, int radius) {
BlockPos best = null;
double bestScore = Double.MAX_VALUE;
int minY = center.getY() - 3;
int maxY = center.getY() + 3;
for (int dx = -radius; dx <= radius; dx++) {
for (int dz = -radius; dz <= radius; dz++) {
if ((dx * dx + dz * dz) > radius * radius) continue;
for (int y = minY; y <= maxY; y++) {
BlockPos p = new BlockPos(center.getX() + dx, y, center.getZ() + dz);
if (!canStandAt(p)) continue;
// prefer closer to dad AND closer to us a bit
double toCenter = p.getSquaredDistance(center);
double toMe = this.getPos().squaredDistanceTo(Vec3d.ofCenter(p));
double score = toCenter * 1.0 + toMe * 0.10;
if (score < bestScore) {
bestScore = score;
best = p;
}
}
}
}
return best;
}
private boolean canStandAt(BlockPos pos) {
World w = getWorld();
// feet + head air
if (!w.getBlockState(pos).isAir()) return false;
if (!w.getBlockState(pos.up()).isAir()) return false;
// solid below
BlockPos below = pos.down();
if (!w.getBlockState(below).isSolidBlock(w, below)) return false;
// not in fluid, not standing on fluid
if (!w.getFluidState(pos).isEmpty()) return false;
if (!w.getFluidState(below).isEmpty()) return false;
// also avoid lava blocks around feet area (simple dont be stupid)
// (fluid check already catches lava fluid; this catches blocks like magma? optional)
return true;
}
private boolean wouldImmediatelyRefly(ServerPlayerEntity dad, BlockPos landPos) {
if (horizontalDistanceTo(dad) > FORCE_FLY_DISTANCE) return true;
if (dad.getY() - landPos.getY() > MAX_VERTICAL_GAP) return true;
return false;
}
/* ================= FALL DAMAGE ================= */
@Override
public boolean handleFallDamage(float fallDistance, float damageMultiplier, DamageSource source) {
return false;
}
/* ================= SAVE / LOAD ================= */
@Override
public void writeCustomDataToNbt(NbtCompound nbt) {
if (dadUuid != null) nbt.putUuid("Dad", dadUuid);
nbt.putString("DadName", dadName);
nbt.putString("Mode", mode.name());
nbt.putInt("FlyTicks", flyTicks);
}
@Override
public void readCustomDataFromNbt(NbtCompound nbt) {
if (nbt.containsUuid("Dad")) dadUuid = nbt.getUuid("Dad");
if (nbt.contains("DadName")) dadName = nbt.getString("DadName");
if (nbt.contains("Mode")) {
mode = Mode.valueOf(nbt.getString("Mode"));
setNoGravity(mode != Mode.GROUND);
}
if (nbt.contains("FlyTicks")) flyTicks = nbt.getInt("FlyTicks");
// volatile reset
landingSpot = null;
forcedLandingSpot = null;
lastDadHorizDist = -1;
stuckTicks = 0;
}
/* ================= FOLLOW GOAL ================= */
static class FollowDadGoal extends Goal {
private final PlayerJrEntity jr;
FollowDadGoal(PlayerJrEntity jr) { this.jr = jr; }
@Override
public boolean canStart() {
return jr.mode == Mode.GROUND && jr.getDad() != null;
}
@Override
public void tick() {
ServerPlayerEntity dad = jr.getDad();
if (dad == null) return;
if (jr.squaredDistanceTo(dad) > 4) {
jr.getNavigation().startMovingTo(dad, 1.0);
}
}
}
}

View File

@ -2,10 +2,10 @@ package net.Chipperfluff.chipi.entity;
import net.fabricmc.fabric.api.biome.v1.BiomeModifications;
import net.fabricmc.fabric.api.biome.v1.BiomeSelectors;
import net.minecraft.block.BlockState;
import net.minecraft.block.Blocks;
import net.minecraft.entity.EntityType;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.entity.SpawnGroup;
import net.minecraft.entity.SpawnReason;
import net.minecraft.entity.SpawnRestriction;
@ -13,14 +13,12 @@ import net.minecraft.entity.mob.MobEntity;
import net.minecraft.registry.RegistryKey;
import net.minecraft.registry.RegistryKeys;
import net.minecraft.util.Identifier;
import net.minecraft.util.math.Box;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.random.Random;
import net.minecraft.world.Heightmap;
import net.minecraft.world.ServerWorldAccess;
import net.minecraft.world.biome.Biome;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.util.math.Box;
public final class SpawnLogic {
@ -30,8 +28,7 @@ public final class SpawnLogic {
private static final RegistryKey<Biome> VOID_BIOME =
RegistryKey.of(RegistryKeys.BIOME, new Identifier("chipi", "void"));
private SpawnLogic() {
}
private SpawnLogic() {}
public static void register() {

View File

@ -1,13 +1,14 @@
package net.Chipperfluff.chipi.item;
import net.minecraft.entity.LivingEntity;
import net.minecraft.entity.effect.StatusEffectInstance;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.item.Item;
import net.minecraft.item.ItemStack;
import net.minecraft.item.Items;
import net.minecraft.util.UseAction;
import net.minecraft.world.World;
import net.minecraft.entity.effect.StatusEffectInstance;
import net.Chipperfluff.chipi.effect.ModEffects;
public class MepMilkItem extends Item {

View File

@ -15,4 +15,9 @@ public class ModFoodComponents {
new FoodComponent.Builder()
.alwaysEdible()
.build();
public static final FoodComponent PLAYER_MILK =
new FoodComponent.Builder()
.alwaysEdible()
.build();
}

View File

@ -1,7 +1,5 @@
package net.Chipperfluff.chipi.item;
import net.Chipperfluff.chipi.ChipiMod;
import net.Chipperfluff.chipi.block.ModBlocks;
import net.fabricmc.fabric.api.itemgroup.v1.FabricItemGroup;
import net.minecraft.item.ItemGroup;
import net.minecraft.item.ItemStack;
@ -10,6 +8,10 @@ import net.minecraft.registry.Registry;
import net.minecraft.text.Text;
import net.minecraft.util.Identifier;
import net.Chipperfluff.chipi.ChipiMod;
import net.Chipperfluff.chipi.block.ModBlocks;
import net.Chipperfluff.chipi.item.music.ModMusicDiscs;
public class ModItemGroups {
public static final ItemGroup CHIPI_GROUP = Registry.register(
@ -25,6 +27,7 @@ public class ModItemGroups {
entries.add(ModBlocks.CHIPPER_PORTAL);
entries.add(ModBlocks.CHIPPER_ORE);
entries.add(ModBlocks.CHIPPER_ALLOY_BLOCK);
entries.add(ModBlocks.JINGLE_BLOCK);
// Items
entries.add(ModItems.RAW_CHIPPER_ORE);
@ -35,6 +38,7 @@ public class ModItemGroups {
// Food
entries.add(ModItems.MEP_MILK);
entries.add(ModItems.NUT);
entries.add(ModItems.PLAYER_MILK);
// Armor
entries.add(ModItems.CHIPPER_HELMET);
@ -49,11 +53,13 @@ public class ModItemGroups {
entries.add(ModItems.CHIPPER_SHOVEL);
entries.add(ModItems.CHIPPER_HOE);
// Music discs
for (var disc : ModMusicDiscs.getAll().values()) {
entries.add(disc);
}
})
.build()
);
public static void register() {
// force class load
}
public static void register() {}
}

View File

@ -1,17 +1,16 @@
package net.Chipperfluff.chipi.item;
import net.Chipperfluff.chipi.ChipiMod;
import net.Chipperfluff.chipi.block.ModBlocks;
import net.Chipperfluff.chipi.entity.ModEntities;
import net.Chipperfluff.chipi.item.armor.ChipperArmorMaterial;
import net.fabricmc.fabric.api.item.v1.FabricItemSettings;
import net.minecraft.item.*;
import net.minecraft.registry.Registries;
import net.minecraft.registry.Registry;
import net.minecraft.util.Identifier;
import net.Chipperfluff.chipi.item.tool.ChipperToolMaterial;
import net.Chipperfluff.chipi.item.MepMilkItem;
import net.Chipperfluff.chipi.ChipiMod;
import net.Chipperfluff.chipi.block.ModBlocks;
import net.Chipperfluff.chipi.entity.ModEntities;
import net.Chipperfluff.chipi.item.armor.ChipperArmorMaterial;
import net.Chipperfluff.chipi.item.tool.ChipperToolMaterial;
public class ModItems {
@ -47,6 +46,12 @@ public class ModItems {
new BlockItem(ModBlocks.CHIPPER_ALLOY_BLOCK, new FabricItemSettings())
);
public static final Item JINGLE_BLOCK = Registry.register(
Registries.ITEM,
new Identifier(ChipiMod.MOD_ID, "jingle_block"),
new BlockItem(ModBlocks.JINGLE_BLOCK, new FabricItemSettings())
);
// ===== ENTITY ITEMS =====
public static final Item MEP_SPAWN_EGG = Registry.register(
@ -89,6 +94,17 @@ public class ModItems {
new MepMilkItem(new Item.Settings().maxCount(1).recipeRemainder(Items.BUCKET).food(ModFoodComponents.MEP_MILK))
);
public static final Item PLAYER_MILK = Registry.register(
Registries.ITEM,
new Identifier(ChipiMod.MOD_ID, "player_milk"),
new PlayerMilkItem(
new Item.Settings()
.maxCount(1)
.recipeRemainder(Items.BUCKET)
.food(ModFoodComponents.PLAYER_MILK)
)
);
// ===== ARMOR =====
public static final Item CHIPPER_HELMET = Registry.register(

View File

@ -0,0 +1,95 @@
package net.Chipperfluff.chipi.item;
import net.minecraft.entity.LivingEntity;
import net.minecraft.entity.effect.StatusEffectInstance;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.item.Item;
import net.minecraft.item.ItemStack;
import net.minecraft.item.ItemUsage;
import net.minecraft.item.Items;
import net.minecraft.sound.SoundCategory;
import net.minecraft.text.Text;
import net.minecraft.util.Hand;
import net.minecraft.util.UseAction;
import net.minecraft.util.TypedActionResult;
import net.minecraft.world.World;
import net.Chipperfluff.chipi.effect.ModEffects;
import net.Chipperfluff.chipi.sound.ModSounds;
public class PlayerMilkItem extends Item {
public PlayerMilkItem(Settings settings) {
super(settings);
}
/* ================= DENY DRINKING ================= */
@Override
public TypedActionResult<ItemStack> use(World world, PlayerEntity user, Hand hand) {
ItemStack stack = user.getStackInHand(hand);
// Already pregnant deny drinking
if (user.hasStatusEffect(ModEffects.PREGNANT)) {
if (!world.isClient) {
user.sendMessage(
Text.literal("You already feel something growing..."),
true
);
}
return TypedActionResult.fail(stack);
}
// Start drinking normally
user.setCurrentHand(hand);
return TypedActionResult.consume(stack);
}
/* ================= DRINK ANIMATION ================= */
@Override
public UseAction getUseAction(ItemStack stack) {
return UseAction.DRINK;
}
@Override
public int getMaxUseTime(ItemStack stack) {
return 32;
}
/* ================= FINISH DRINK ================= */
@Override
public ItemStack finishUsing(ItemStack stack, World world, LivingEntity user) {
if (!world.isClient && user instanceof PlayerEntity player) {
// Apply pregnancy effect
player.addStatusEffect(new StatusEffectInstance(
ModEffects.PREGNANT,
20 * 60 * 10, // 10 minutes
0,
false,
true
));
// Play sound
world.playSound(
null,
player.getBlockPos(),
ModSounds.PLAYER_MILK_SONG,
SoundCategory.PLAYERS,
1.0f,
1.0f
);
}
// CORRECT vanilla behavior:
// consumes milk and gives bucket
if (user instanceof PlayerEntity player) {
return ItemUsage.exchangeStack(stack, player, new ItemStack(Items.BUCKET));
}
return stack;
}
}

View File

@ -1,16 +1,16 @@
package net.Chipperfluff.chipi.item.armor;
import net.Chipperfluff.chipi.item.ModItems;
import net.minecraft.item.ArmorItem;
import net.minecraft.item.ArmorMaterial;
import net.minecraft.recipe.Ingredient;
import net.minecraft.sound.SoundEvent;
import net.minecraft.sound.SoundEvents;
import net.minecraft.util.Util;
import java.util.EnumMap;
import java.util.Map;
import net.Chipperfluff.chipi.item.ModItems;
public class ChipperArmorMaterial implements ArmorMaterial {
public static final ChipperArmorMaterial INSTANCE = new ChipperArmorMaterial();

View File

@ -1,6 +1,5 @@
package net.Chipperfluff.chipi.armor;
import net.Chipperfluff.chipi.item.ModItems;
import net.minecraft.block.BlockState;
import net.minecraft.block.SlabBlock;
import net.minecraft.block.StairsBlock;
@ -12,8 +11,10 @@ import net.minecraft.text.Text;
import net.minecraft.util.math.BlockPos;
import net.minecraft.world.World;
import static net.Chipperfluff.chipi.util.ChipiTrackedData.CHIPI_ENERGY;
import net.Chipperfluff.chipi.item.ModItems;
import static net.Chipperfluff.chipi.server.ChipiServerEvents.CHIPI_DIMENSION_KEY;
import static net.Chipperfluff.chipi.util.ChipiTrackedData.CHIPI_ENERGY;
public final class ProtectionAuraHandler {

View File

@ -0,0 +1,67 @@
package net.Chipperfluff.chipi.item.music;
import net.minecraft.item.Item;
import net.minecraft.item.MusicDiscItem;
import net.minecraft.registry.Registries;
import net.minecraft.registry.Registry;
import net.minecraft.sound.SoundEvent;
import net.minecraft.util.Identifier;
import java.util.LinkedHashMap;
import java.util.Map;
import net.Chipperfluff.chipi.ChipiMod;
public class ModMusicDiscs {
private static final Map<String, Item> DISCS = new LinkedHashMap<>();
private static final MusicDiscDef[] DEFINITIONS = {
new MusicDiscDef("aa9", 7, 5, 0),
new MusicDiscDef("abc", 7, 2, 11),
new MusicDiscDef("fs", 7, 2, 0),
new MusicDiscDef("phone", 7, 2, 11),
new MusicDiscDef("wha", 7, 1, 48),
new MusicDiscDef("who", 7, 2, 27),
new MusicDiscDef("working_as_intented", 7, 3, 45)
};
private static Item registerDisc(MusicDiscDef def) {
String id = "chipi_record_" + def.name();
Identifier identifier = new Identifier(ChipiMod.MOD_ID, id);
SoundEvent sound = Registry.register(
Registries.SOUND_EVENT,
identifier,
SoundEvent.of(identifier)
);
Item item = Registry.register(
Registries.ITEM,
identifier,
new MusicDiscItem(
def.comparatorOutput(),
sound,
new Item.Settings().maxCount(1),
toTicks(def.minutes(), def.seconds())
)
);
DISCS.put(def.name(), item);
return item;
}
public static Map<String, Item> getAll() {
return DISCS;
}
public static void registerAll() {
for (MusicDiscDef def : DEFINITIONS) {
registerDisc(def);
}
}
private static int toTicks(int minutes, int seconds) {
int totalSeconds = Math.max(0, minutes) * 60 + Math.max(0, seconds);
return totalSeconds * 20;
}
}

View File

@ -0,0 +1,3 @@
package net.Chipperfluff.chipi.item.music;
public record MusicDiscDef(String name, int comparatorOutput, int minutes, int seconds) {}

View File

@ -2,8 +2,8 @@ package net.Chipperfluff.chipi.item.tool;
import net.minecraft.item.ToolMaterial;
import net.minecraft.recipe.Ingredient;
import net.Chipperfluff.chipi.item.ModItems;
import net.Chipperfluff.chipi.item.ModItems;
public enum ChipperToolMaterial implements ToolMaterial {
INSTANCE;

View File

@ -2,7 +2,6 @@ package net.Chipperfluff.chipi.mixin;
import net.minecraft.entity.data.DataTracker;
import net.minecraft.entity.data.TrackedData;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.injection.At;

View File

@ -1,24 +1,82 @@
package net.Chipperfluff.chipi.mixin;
import com.mojang.authlib.GameProfile;
import net.Chipperfluff.chipi.util.ChipiTrackedData;
import net.minecraft.entity.data.DataTracker;
import net.minecraft.entity.effect.StatusEffectInstance;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.server.network.ServerPlayerEntity;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.Vec3d;
import net.minecraft.world.World;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import net.Chipperfluff.chipi.effect.ModEffects;
import net.Chipperfluff.chipi.effect.PregnantEffect;
import net.Chipperfluff.chipi.util.ChipiTrackedData;
@Mixin(PlayerEntity.class)
public abstract class PlayerEntityMixin {
/* ============================================================
INIT: tracked data
============================================================ */
@Inject(method = "<init>", at = @At("TAIL"))
private void chipi$initTrackedData(World world, BlockPos pos, float yaw, GameProfile profile, CallbackInfo ci) {
private void chipi$initTrackedData(
World world,
BlockPos pos,
float yaw,
GameProfile profile,
CallbackInfo ci
) {
PlayerEntity self = (PlayerEntity)(Object)this;
DataTracker tracker = self.getDataTracker();
tracker.startTracking(ChipiTrackedData.CHIPI_ENERGY, 1.0f);
}
/* ============================================================
JUMP CLAMP: pregnancy logic
============================================================ */
@Inject(
method = "jump",
at = @At("HEAD"),
cancellable = true
)
private void chipi$pregnancyJumpClamp(CallbackInfo ci) {
if (!((Object)this instanceof ServerPlayerEntity player)) return;
StatusEffectInstance inst =
player.getStatusEffect(ModEffects.PREGNANT);
if (inst == null) return; // normal jump
int total = PregnantEffect.TOTAL_DURATION;
int remaining = inst.getDuration();
// progress 0.0 1.0
double progress =
1.0 - ((double)remaining / total);
progress = Math.min(Math.max(progress, 0.0), 1.0);
// vanilla jump velocity 0.42
double maxJump = 0.42 * (1.0 - progress);
// End stage: NO jumping at all
if (maxJump < 0.05) {
ci.cancel();
return;
}
Vec3d vel = player.getVelocity();
player.setVelocity(vel.x, maxJump, vel.z);
player.velocityDirty = true;
// stop vanilla jump
ci.cancel();
}
}

View File

@ -1,24 +1,36 @@
package net.Chipperfluff.chipi.server;
import net.Chipperfluff.chipi.SpawnPlacedState;
import net.Chipperfluff.chipi.armor.ProtectionAuraHandler;
import net.Chipperfluff.chipi.block.ChipperPortalBlock;
import net.Chipperfluff.chipi.world.gen.ChipiDungeonGenerator;
import net.fabricmc.fabric.api.biome.v1.BiomeModifications;
import net.fabricmc.fabric.api.biome.v1.BiomeSelectors;
import net.fabricmc.fabric.api.event.lifecycle.v1.ServerLifecycleEvents;
import net.fabricmc.fabric.api.event.lifecycle.v1.ServerTickEvents;
import net.fabricmc.fabric.api.event.lifecycle.v1.ServerWorldEvents;
import net.fabricmc.fabric.api.event.player.UseEntityCallback;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.item.ItemStack;
import net.minecraft.item.Items;
import net.minecraft.registry.RegistryKey;
import net.minecraft.registry.RegistryKeys;
import net.minecraft.server.MinecraftServer;
import net.minecraft.server.network.ServerPlayerEntity;
import net.minecraft.server.world.ServerWorld;
import net.minecraft.sound.SoundCategory;
import net.minecraft.sound.SoundEvents;
import net.minecraft.structure.StructurePlacementData;
import net.minecraft.structure.StructureTemplate;
import net.minecraft.util.ActionResult;
import net.minecraft.util.Identifier;
import net.minecraft.util.math.BlockPos;
import net.minecraft.world.GameRules;
import net.minecraft.world.World;
import net.minecraft.world.gen.GenerationStep;
import net.Chipperfluff.chipi.SpawnPlacedState;
import net.Chipperfluff.chipi.armor.ProtectionAuraHandler;
import net.Chipperfluff.chipi.block.ChipperPortalBlock;
import net.Chipperfluff.chipi.item.ModItems;
import net.Chipperfluff.chipi.world.gen.ChipiDungeonGenerator;
public final class ChipiServerEvents {
@ -33,6 +45,44 @@ public final class ChipiServerEvents {
private ChipiServerEvents() {}
public static void register() {
BiomeModifications.addFeature(
BiomeSelectors.foundInOverworld(),
GenerationStep.Feature.UNDERGROUND_ORES,
RegistryKey.of(
RegistryKeys.PLACED_FEATURE,
new Identifier("chipi", "chipper_ore")
)
);
// ===== PLAYER MILK INTERACTION =====
UseEntityCallback.EVENT.register((player, world, hand, entity, hitResult) -> {
if (world.isClient()) return ActionResult.PASS;
if (!(entity instanceof PlayerEntity target)) return ActionResult.PASS;
if (target == player) return ActionResult.FAIL;
ItemStack stack = player.getStackInHand(hand);
if (!stack.isOf(Items.BUCKET)) return ActionResult.PASS;
stack.decrement(1);
ItemStack milk = new ItemStack(ModItems.PLAYER_MILK);
if (!player.getInventory().insertStack(milk)) {
player.dropItem(milk, false);
}
world.playSound(
null,
player.getBlockPos(),
SoundEvents.ENTITY_COW_MILK,
SoundCategory.PLAYERS,
1.0f,
1.1f
);
return ActionResult.SUCCESS;
});
ServerLifecycleEvents.SERVER_STARTED.register(server -> SERVER = server);
ServerTickEvents.END_SERVER_TICK.register(ChipiServerEvents::tickPlayers);
ServerTickEvents.END_WORLD_TICK.register(ChipiServerEvents::handleVoidFailsafe);

View File

@ -1,14 +1,16 @@
package net.Chipperfluff.chipi.sound;
import net.Chipperfluff.chipi.ChipiMod;
import net.minecraft.registry.Registries;
import net.minecraft.registry.Registry;
import net.minecraft.sound.SoundEvent;
import net.minecraft.util.Identifier;
import net.Chipperfluff.chipi.ChipiMod;
public class ModSounds {
public static final SoundEvent MEP_MILK = register("entity.mep.milk");
public static final SoundEvent PLAYER_MILK_SONG = register("player_milk_song");
private static SoundEvent register(String id) {
Identifier identifier = new Identifier(ChipiMod.MOD_ID, id);

View File

@ -6,9 +6,7 @@ import net.minecraft.entity.data.TrackedDataHandlerRegistry;
import net.minecraft.entity.player.PlayerEntity;
public final class ChipiTrackedData {
public static final TrackedData<Float> CHIPI_ENERGY =
DataTracker.registerData(PlayerEntity.class, TrackedDataHandlerRegistry.FLOAT);
public static final TrackedData<Float> CHIPI_ENERGY = DataTracker.registerData(PlayerEntity.class, TrackedDataHandlerRegistry.FLOAT);
private ChipiTrackedData() {}
}

View File

@ -2,7 +2,6 @@ package net.Chipperfluff.chipi.util;
import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientTickEvents;
import net.minecraft.client.MinecraftClient;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;

View File

@ -2,7 +2,6 @@ package net.Chipperfluff.chipi.util;
import net.fabricmc.fabric.api.event.lifecycle.v1.ServerTickEvents;
import net.minecraft.server.MinecraftServer;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;

View File

@ -1,11 +1,12 @@
package net.Chipperfluff.chipi.world.gen;
import net.minecraft.block.BlockState;
import net.minecraft.server.world.ServerWorld;
import net.minecraft.util.math.BlockPos;
import net.Chipperfluff.chipi.world.gen.struct.CorridorEWStructure;
import net.Chipperfluff.chipi.world.gen.struct.CorridorNSStructure;
import net.Chipperfluff.chipi.world.gen.struct.RoomBaseStructure;
import net.minecraft.block.BlockState;
import net.minecraft.server.world.ServerWorld;
import net.minecraft.util.math.BlockPos;
public class ChipiDungeonGenerator {
@ -24,13 +25,10 @@ public class ChipiDungeonGenerator {
// Center-to-center step so corridors start 1 block inside the north room and end 1 block inside
// the south room.
private static final int STEP_Z =
ROOM_EXTENT_SOUTH + CORRIDOR_LENGTH + ROOM_EXTENT_NORTH - 1; // 25
private static final int STEP_X =
ROOM_EXTENT_EAST + CORRIDOR_LENGTH + ROOM_EXTENT_WEST - 1; // 25
private static final int STEP_Z = ROOM_EXTENT_SOUTH + CORRIDOR_LENGTH + ROOM_EXTENT_NORTH - 1; // 25
private static final int STEP_X = ROOM_EXTENT_EAST + CORRIDOR_LENGTH + ROOM_EXTENT_WEST - 1; // 25
private static final int ROWS_SOUTH =
40; // rows of rooms south of spawn (spawn room itself is already placed)
private static final int ROWS_SOUTH = 40; // rows of rooms south of spawn (spawn room itself is already placed)
private static final int COLS_EW = 40; // columns across X (split roughly evenly around center)
private ChipiDungeonGenerator() {}
@ -40,8 +38,7 @@ public class ChipiDungeonGenerator {
// Spawn is already placed. Fixed anchors (not relative to portal):
// first corridor origin, then the first generated room center is south of spawn.
BlockPos firstCorridorAnchor = new BlockPos(5, CORRIDOR_Y, 11);
BlockPos firstGeneratedRoomCenter =
new BlockPos(5, ROOM_Y, 24); // explicit first floor anchor
BlockPos firstGeneratedRoomCenter = new BlockPos(5, ROOM_Y, 24); // explicit first floor anchor
// First room/corridor use direct instances as requested.
RoomBaseStructure firstRoom = WorldMaster.getDefaultRoom();
@ -60,16 +57,14 @@ public class ChipiDungeonGenerator {
// Place rooms strictly southward (Z never decreases), across X in both directions.
for (int row = 0; row < ROWS_SOUTH; row++) {
for (int col = minCol; col <= maxCol; col++) {
int gridX =
col; // grid 0,0 is the first generated room; -X -> negative gridX, +X ->
// positive gridX
int gridX = col; // grid 0,0 is the first generated room; -X -> negative gridX, +X -> positive gridX
int gridY = row; // gridY increases southward only
BlockPos center =
new BlockPos(
BlockPos center = new BlockPos(
firstGeneratedRoomCenter.getX() + (col * STEP_X),
ROOM_Y,
firstGeneratedRoomCenter.getZ() + (row * STEP_Z));
firstGeneratedRoomCenter.getZ() + (row * STEP_Z)
);
RoomBaseStructure room = WorldMaster.resolveRoom(ctx(world, gridX, gridY, center, null));
room.placeAt(world, center);
@ -84,20 +79,19 @@ public class ChipiDungeonGenerator {
int gridX = col;
int gridY = row;
BlockPos currentCenter =
new BlockPos(
BlockPos currentCenter = new BlockPos(
firstGeneratedRoomCenter.getX() + (col * STEP_X),
ROOM_Y,
firstGeneratedRoomCenter.getZ() + (row * STEP_Z));
firstGeneratedRoomCenter.getZ() + (row * STEP_Z)
);
BlockPos anchorSouth =
new BlockPos(
BlockPos anchorSouth = new BlockPos(
currentCenter.getX(),
CORRIDOR_Y,
currentCenter.getZ() + ROOM_EXTENT_SOUTH);
currentCenter.getZ() + ROOM_EXTENT_SOUTH
);
CorridorNSStructure corridorNS =
WorldMaster.resolveCorridorNS(ctx(world, gridX, gridY, anchorSouth, null));
CorridorNSStructure corridorNS = WorldMaster.resolveCorridorNS(ctx(world, gridX, gridY, anchorSouth, null));
corridorNS.placeAt(world, anchorSouth);
WorldMaster.afterPlaceCorridorNS(ctx(world, gridX, gridY, anchorSouth, corridorNS));
}
@ -109,20 +103,19 @@ public class ChipiDungeonGenerator {
int gridX = col;
int gridY = row;
BlockPos currentCenter =
new BlockPos(
BlockPos currentCenter = new BlockPos(
firstGeneratedRoomCenter.getX() + (col * STEP_X),
ROOM_Y,
firstGeneratedRoomCenter.getZ() + (row * STEP_Z));
firstGeneratedRoomCenter.getZ() + (row * STEP_Z)
);
BlockPos anchorEast =
new BlockPos(
BlockPos anchorEast = new BlockPos(
currentCenter.getX() + ROOM_EXTENT_EAST,
CORRIDOR_Y,
currentCenter.getZ());
currentCenter.getZ()
);
CorridorEWStructure corridorEW =
WorldMaster.resolveCorridorEW(ctx(world, gridX, gridY, anchorEast, null));
CorridorEWStructure corridorEW = WorldMaster.resolveCorridorEW(ctx(world, gridX, gridY, anchorEast, null));
corridorEW.placeAt(world, anchorEast);
WorldMaster.afterPlaceCorridorEW(ctx(world, gridX, gridY, anchorEast, corridorEW));
}
@ -136,18 +129,10 @@ public class ChipiDungeonGenerator {
runInChipi(world, "fill 7 87 15 3 91 16 minecraft:air");
runInChipi(
world,
"setblock 4 88 15 minecraft:deepslate_tile_stairs[facing=west,waterlogged=true]");
runInChipi(
world,
"setblock 4 88 16 minecraft:deepslate_tile_stairs[facing=west,waterlogged=true]");
runInChipi(
world,
"setblock 6 88 15 minecraft:deepslate_tile_stairs[facing=east,waterlogged=true]");
runInChipi(
world,
"setblock 6 88 16 minecraft:deepslate_tile_stairs[facing=east,waterlogged=true]");
runInChipi(world, "setblock 4 88 15 minecraft:deepslate_tile_stairs[facing=west,waterlogged=true]");
runInChipi(world, "setblock 4 88 16 minecraft:deepslate_tile_stairs[facing=west,waterlogged=true]");
runInChipi(world, "setblock 6 88 15 minecraft:deepslate_tile_stairs[facing=east,waterlogged=true]");
runInChipi(world, "setblock 6 88 16 minecraft:deepslate_tile_stairs[facing=east,waterlogged=true]");
runInChipi(world, "fill 7 88 15 7 91 16 minecraft:deepslate_tiles");
runInChipi(world, "fill 3 88 16 3 91 15 minecraft:deepslate_tiles");
@ -183,7 +168,8 @@ public class ChipiDungeonGenerator {
.getCommandManager()
.executeWithPrefix(
world.getServer().getCommandSource(),
"execute in chipi:chipi_dimension run " + command);
"execute in chipi:chipi_dimension run " + command
);
}
private static DungeonContext ctx(ServerWorld world, int gridX, int gridY, BlockPos origin, ChipiStructure structure) {

View File

@ -1,6 +1,6 @@
package net.Chipperfluff.chipi.world.gen;
import java.util.Optional;
import net.minecraft.block.Blocks;
import net.minecraft.block.entity.StructureBlockBlockEntity;
import net.minecraft.block.enums.StructureBlockMode;
import net.minecraft.server.world.ServerWorld;
@ -9,7 +9,7 @@ import net.minecraft.structure.StructureTemplate;
import net.minecraft.util.Identifier;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.Vec3i;
import net.minecraft.block.Blocks;
import java.util.Optional;
public abstract class ChipiStructure {

View File

@ -1,10 +1,11 @@
package net.Chipperfluff.chipi.world.gen;
import net.minecraft.block.Blocks;
import net.minecraft.util.math.BlockPos;
import net.Chipperfluff.chipi.world.gen.struct.CorridorEWStructure;
import net.Chipperfluff.chipi.world.gen.struct.CorridorNSStructure;
import net.Chipperfluff.chipi.world.gen.struct.RoomBaseStructure;
import net.minecraft.block.Blocks;
import net.minecraft.util.math.BlockPos;
public final class WorldMaster {

View File

@ -1,11 +1,11 @@
package net.Chipperfluff.chipi.world.gen.struct;
import net.Chipperfluff.chipi.world.gen.ChipiStructure; // <-- THIS WAS MISSING
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import net.Chipperfluff.chipi.world.gen.ChipiStructure; // <-- THIS WAS MISSING
public class ChipiStructures {
private static final Map<String, ChipiStructure> REGISTRY = new HashMap<>();

View File

@ -1,8 +1,9 @@
package net.Chipperfluff.chipi.world.gen.struct;
import net.Chipperfluff.chipi.world.gen.ChipiStructure;
import net.minecraft.util.Identifier;
import net.Chipperfluff.chipi.world.gen.ChipiStructure;
public class CorridorEWStructure extends ChipiStructure {
public CorridorEWStructure() {

View File

@ -1,8 +1,9 @@
package net.Chipperfluff.chipi.world.gen.struct;
import net.Chipperfluff.chipi.world.gen.ChipiStructure;
import net.minecraft.util.Identifier;
import net.Chipperfluff.chipi.world.gen.ChipiStructure;
public class CorridorNSStructure extends ChipiStructure {
public CorridorNSStructure() {

View File

@ -1,8 +1,9 @@
package net.Chipperfluff.chipi.world.gen.struct;
import net.Chipperfluff.chipi.world.gen.ChipiStructure;
import net.minecraft.util.Identifier;
import net.Chipperfluff.chipi.world.gen.ChipiStructure;
public class RoomBaseStructure extends ChipiStructure {
public RoomBaseStructure() {

View File

@ -1,8 +1,9 @@
package net.Chipperfluff.chipi.world.gen.struct;
import net.Chipperfluff.chipi.world.gen.ChipiStructure;
import net.minecraft.util.Identifier;
import net.Chipperfluff.chipi.world.gen.ChipiStructure;
public class SpawnStructure extends ChipiStructure {
public SpawnStructure() {

View File

@ -0,0 +1,5 @@
{
"variants": {
"": { "model": "chipi:block/jingle_block" }
}
}

View File

@ -2,17 +2,32 @@
"death.attack.void_block": "§8%1$s stepped beyond safety — the Outside answered.",
"death.attack.chipi.void_block_fire": "§c%1$s tried to save themselves.§r §8It got worse.",
"itemGroup.chipi.chipi": "Chipi",
"effect.chipi.chipi_blessing": "Chipi's Blessing",
"entity.chipi.mep": "Merp :3",
"effect.chipi.chipi_blessing": "§bChipi's Blessing",
"effect.chipi.pregnant": "§dyou are Pregnant",
"block.chipi.void_block": "Void Block",
"block.chipi.chipper_frame": "Chipper Frame",
"block.chipi.chipper_portal": "Chipper Portal",
"block.chipi.chipper_ore": "Chipper Ore",
"block.chipi.chipper_alloy_block": "Chipper Alloy Block",
"block.chipi.jingle_block": "Jingle Block",
"item.chipi.chipi_record_aa9": "Chipi Record Aa9",
"item.chipi.chipi_record_aa9.desc": "Chipi Record Aa9",
"item.chipi.chipi_record_abc": "Chipi Record Abc",
"item.chipi.chipi_record_abc.desc": "Chipi Record Abc",
"item.chipi.chipi_record_fs": "Chipi Record Fs",
"item.chipi.chipi_record_fs.desc": "Chipi Record Fs",
"item.chipi.chipi_record_phone": "Chipi Record Phone",
"item.chipi.chipi_record_phone.desc": "Chipi Record Phone",
"item.chipi.chipi_record_wha": "Chipi Record Wha",
"item.chipi.chipi_record_wha.desc": "Chipi Record Wha",
"item.chipi.chipi_record_who": "Chipi Record Who",
"item.chipi.chipi_record_who.desc": "Chipi Record Who",
"item.chipi.chipi_record_working_as_intented": "Chipi Record Working As Intented",
"item.chipi.chipi_record_working_as_intented.desc": "Chipi Record Working As Intented",
"itemGroup.chipi.chipi": "Chipi",
"item.chipi.void_block": "Void Block",
"item.chipi.chipper_frame": "Chipper Frame",
@ -23,9 +38,7 @@
"item.chipi.chipper_ingot": "Chipper Ingot",
"item.chipi.chipper_alloy": "Chipper Alloy",
"item.chipi.mep_spawn_egg": "Mep Spawn Egg",
"item.chipi.nut": "Nut",
"item.chipi.mep_milk": "Mep Milk",
"item.chipi.jingle_block": "Jingle Block",
"item.chipi.chipper_helmet": "Chipper Helmet",
"item.chipi.chipper_chestplate": "Chipper Chestplate",
@ -38,19 +51,21 @@
"item.chipi.chipper_shovel": "Chipper Shovel",
"item.chipi.chipper_hoe": "Chipper Hoe",
"item.chipi.nut": "Nut",
"item.chipi.mep_milk": "Mep Milk",
"item.chipi.player_milk": "Player Milk",
"tooltip.chipi.void_block": "§8It hums quietly.§r §7You are not supposed to notice that.",
"tooltip.chipi.chipper_frame": "§7Built to hold a mistake.§r §8It is doing its best.",
"tooltip.chipi.chipper_portal": "§5Something on the other side noticed you.§r §8It did not look away.",
"tooltip.chipi.chipper_ore": "§7Common.§r §8Suspiciously so.",
"tooltip.chipi.chipper_alloy_block": "§7Pressed together until it stopped complaining.§r §8Mostly.",
"tooltip.chipi.jingle_block": "§7It makes a noise.§r §8You didnt ask when.",
"tooltip.chipi.nut": "§7Probably edible.§r §8Confidence not included.",
"tooltip.chipi.raw_chipper_ore": "§7Still warm to the touch.§r §8That seems unnecessary.",
"tooltip.chipi.chipper_ingot": "§7Dense and stubborn.§r §8Refuses to fail politely.",
"tooltip.chipi.chipper_alloy": "§7Stronger than it looks.§r §8Judges you silently.",
"tooltip.chipi.mep_spawn_egg": "§8It already knows where you are.§r §7Spawning is a courtesy.",
"tooltip.chipi.mep_milk": "§7Temporary comfort in liquid form.§r §8The bucket survives.",
"tooltip.chipi.chipper_helmet": "§7Heavy and awkward.§r §8Feels incomplete alone.",
"tooltip.chipi.chipper_chestplate": "§7Looks like armor.§r §8Does nothing by itself.",
@ -61,5 +76,9 @@
"tooltip.chipi.chipper_pickaxe": "§7Fast bite on stone.§r §8Gives up immediately after.",
"tooltip.chipi.chipper_axe": "§7Clean chop.§r §8Handle resents you.",
"tooltip.chipi.chipper_shovel": "§7Quick dig.§r §8Gravel smells weakness.",
"tooltip.chipi.chipper_hoe": "§7Turns soil fast.§r §8Blunts before the sprouts."
"tooltip.chipi.chipper_hoe": "§7Turns soil fast.§r §8Blunts before the sprouts.",
"tooltip.chipi.nut": "§7Probably edible.§r §8Confidence not included.",
"tooltip.chipi.mep_milk": "§7Temporary comfort in liquid form.§r §8The bucket survives.",
"tooltip.chipi.player_milk": "what the fuck did you just just put in me"
}

View File

@ -0,0 +1,6 @@
{
"parent": "minecraft:block/cube_all",
"textures": {
"all": "chipi:block/jingle_block"
}
}

View File

@ -0,0 +1,6 @@
{
"parent": "minecraft:item/generated",
"textures": {
"layer0": "chipi:item/chipi_record_aa9"
}
}

View File

@ -0,0 +1,6 @@
{
"parent": "minecraft:item/generated",
"textures": {
"layer0": "chipi:item/chipi_record_abc"
}
}

View File

@ -0,0 +1,6 @@
{
"parent": "minecraft:item/generated",
"textures": {
"layer0": "chipi:item/chipi_record_fs"
}
}

View File

@ -0,0 +1,6 @@
{
"parent": "minecraft:item/generated",
"textures": {
"layer0": "chipi:item/chipi_record_phone"
}
}

View File

@ -0,0 +1,6 @@
{
"parent": "minecraft:item/generated",
"textures": {
"layer0": "chipi:item/chipi_record_wha"
}
}

View File

@ -0,0 +1,6 @@
{
"parent": "minecraft:item/generated",
"textures": {
"layer0": "chipi:item/chipi_record_who"
}
}

View File

@ -0,0 +1,6 @@
{
"parent": "minecraft:item/generated",
"textures": {
"layer0": "chipi:item/chipi_record_working_as_intented"
}
}

View File

@ -0,0 +1,3 @@
{
"parent": "chipi:block/jingle_block"
}

View File

@ -0,0 +1,6 @@
{
"parent": "minecraft:item/generated",
"textures": {
"layer0": "chipi:item/player_milk"
}
}

View File

@ -3,5 +3,31 @@
"sounds": [
"chipi:mep_milk"
]
},
"player_milk_song": {
"sounds": [
"chipi:player_milk"
]
},
"chipi_record_aa9": {
"sounds": [{ "name": "chipi:chipi_record_aa9", "stream": true }]
},
"chipi_record_abc": {
"sounds": [{ "name": "chipi:chipi_record_abc", "stream": true }]
},
"chipi_record_fs": {
"sounds": [{ "name": "chipi:chipi_record_fs", "stream": true }]
},
"chipi_record_phone": {
"sounds": [{ "name": "chipi:chipi_record_phone", "stream": true }]
},
"chipi_record_wha": {
"sounds": [{ "name": "chipi:chipi_record_wha", "stream": true }]
},
"chipi_record_who": {
"sounds": [{ "name": "chipi:chipi_record_who", "stream": true }]
},
"chipi_record_working_as_intented": {
"sounds": [{ "name": "chipi:chipi_record_working_as_intented", "stream": true }]
}
}

Binary file not shown.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

After

Width:  |  Height:  |  Size: 336 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

After

Width:  |  Height:  |  Size: 759 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 263 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 225 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 312 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 312 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 312 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 312 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 312 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 312 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 312 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 339 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

View File

@ -0,0 +1,13 @@
{
"type": "minecraft:crafting_shaped",
"pattern": [
"xxx",
"xyx",
"xxx"
],
"key": {
"x": { "item": "chipi:raw_chipper_ore" },
"y": { "item": "minecraft:emerald" }
},
"result": { "item": "chipi:chipi_record_aa9" }
}

View File

@ -0,0 +1,13 @@
{
"type": "minecraft:crafting_shaped",
"pattern": [
"xxx",
"xyx",
"xxx"
],
"key": {
"x": { "item": "minecraft:orange_wool" },
"y": { "item": "minecraft:emerald" }
},
"result": { "item": "chipi:chipi_record_abc" }
}

View File

@ -0,0 +1,13 @@
{
"type": "minecraft:crafting_shaped",
"pattern": [
"xxx",
"xyx",
"xxx"
],
"key": {
"x": { "item": "minecraft:sweet_berries" },
"y": { "item": "minecraft:emerald" }
},
"result": { "item": "chipi:chipi_record_fs" }
}

View File

@ -0,0 +1,13 @@
{
"type": "minecraft:crafting_shaped",
"pattern": [
"xxx",
"xyx",
"xxx"
],
"key": {
"x": { "item": "minecraft:iron_ingot" },
"y": { "item": "minecraft:emerald" }
},
"result": { "item": "chipi:chipi_record_phone" }
}

View File

@ -0,0 +1,13 @@
{
"type": "minecraft:crafting_shaped",
"pattern": [
"xxx",
"xyx",
"xxx"
],
"key": {
"x": { "item": "chipi:chipper_alloy" },
"y": { "item": "minecraft:emerald" }
},
"result": { "item": "chipi:chipi_record_wha" }
}

View File

@ -0,0 +1,13 @@
{
"type": "minecraft:crafting_shaped",
"pattern": [
"xxx",
"xyx",
"xxx"
],
"key": {
"x": { "item": "minecraft:blue_wool" },
"y": { "item": "minecraft:emerald" }
},
"result": { "item": "chipi:chipi_record_who" }
}

View File

@ -0,0 +1,13 @@
{
"type": "minecraft:crafting_shaped",
"pattern": [
"xxx",
"xyx",
"xxx"
],
"key": {
"x": { "item": "minecraft:glass" },
"y": { "item": "minecraft:emerald" }
},
"result": { "item": "chipi:chipi_record_working_as_intented" }
}

View File

@ -0,0 +1,26 @@
{
"type": "minecraft:crafting_shaped",
"group": "chipi",
"category": "misc",
"pattern": [
"ABA",
"BCB",
"ABA"
],
"key": {
"A": {
"item": "chipi:chipper_alloy"
},
"B": {
"item": "minecraft:gold_ingot"
},
"C": {
"item": "minecraft:clock"
}
},
"result": {
"item": "chipi:jingle_block",
"count": 1
},
"show_notification": true
}

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