Compare commits
3 Commits
64e12603de
...
f7aa71e9eb
| Author | SHA1 | Date | |
|---|---|---|---|
| f7aa71e9eb | |||
| cc18066c17 | |||
| c176263ec5 |
@@ -7,8 +7,12 @@ on:
|
|||||||
- 'addon/**'
|
- 'addon/**'
|
||||||
- 'lobby-addon/**'
|
- 'lobby-addon/**'
|
||||||
- 'hub-return-addon/**'
|
- 'hub-return-addon/**'
|
||||||
- 'easter-egg-addon/**'
|
|
||||||
- 'village-evolution-addon/**'
|
- 'village-evolution-addon/**'
|
||||||
|
- 'monkey-addon/**'
|
||||||
|
- 'private-chest-addon/**'
|
||||||
|
- 'home-sign-addon/**'
|
||||||
|
- 'keep-inventory-addon/**'
|
||||||
|
- 'postal-service-addon/**'
|
||||||
- 'docker-compose.yml'
|
- 'docker-compose.yml'
|
||||||
- 'scripts/**'
|
- 'scripts/**'
|
||||||
|
|
||||||
@@ -26,6 +30,7 @@ jobs:
|
|||||||
script: |
|
script: |
|
||||||
set -e
|
set -e
|
||||||
APP_DIR="$HOME/minecraft-multiworld"
|
APP_DIR="$HOME/minecraft-multiworld"
|
||||||
|
PATHS="addon/ lobby-addon/ hub-return-addon/ village-evolution-addon/ monkey-addon/ private-chest-addon/ home-sign-addon/ keep-inventory-addon/ postal-service-addon/ docker-compose.yml"
|
||||||
|
|
||||||
# First run: clone. Subsequent: pull.
|
# First run: clone. Subsequent: pull.
|
||||||
if [ ! -d "$APP_DIR/.git" ]; then
|
if [ ! -d "$APP_DIR/.git" ]; then
|
||||||
@@ -33,11 +38,11 @@ jobs:
|
|||||||
git init
|
git init
|
||||||
git remote add origin https://git.silverlabs.uk/SilverLABS/minecraft-aiworld.git
|
git remote add origin https://git.silverlabs.uk/SilverLABS/minecraft-aiworld.git
|
||||||
git fetch origin main
|
git fetch origin main
|
||||||
git checkout -f origin/main -- addon/ lobby-addon/ hub-return-addon/ easter-egg-addon/ village-evolution-addon/ docker-compose.yml
|
git checkout -f origin/main -- $PATHS
|
||||||
else
|
else
|
||||||
cd "$APP_DIR"
|
cd "$APP_DIR"
|
||||||
git fetch origin main
|
git fetch origin main
|
||||||
git checkout -f origin/main -- addon/ lobby-addon/ hub-return-addon/ easter-egg-addon/ village-evolution-addon/ docker-compose.yml
|
git checkout -f origin/main -- $PATHS
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Recreate containers so any new docker-compose volume mounts are applied,
|
# Recreate containers so any new docker-compose volume mounts are applied,
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
"minecraft:entity": {
|
"minecraft:entity": {
|
||||||
"description": {
|
"description": {
|
||||||
"identifier": "silverlabs:anthrax_cat",
|
"identifier": "silverlabs:anthrax_cat",
|
||||||
"is_spawnable": false,
|
"is_spawnable": true,
|
||||||
"is_summonable": true,
|
"is_summonable": true,
|
||||||
"is_experimental": false
|
"is_experimental": false
|
||||||
},
|
},
|
||||||
@@ -39,18 +39,6 @@
|
|||||||
"minecraft:knockback_resistance": {
|
"minecraft:knockback_resistance": {
|
||||||
"value": 1.0
|
"value": 1.0
|
||||||
},
|
},
|
||||||
"minecraft:damage_sensor": {
|
|
||||||
"triggers": [
|
|
||||||
{
|
|
||||||
"cause": "all",
|
|
||||||
"deals_damage": false
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"cause": "override",
|
|
||||||
"deals_damage": true
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"minecraft:health": {
|
"minecraft:health": {
|
||||||
"value": 20,
|
"value": 20,
|
||||||
"max": 20
|
"max": 20
|
||||||
|
|||||||
@@ -26,6 +26,15 @@ services:
|
|||||||
- ./addon/heyhe_pet_RP:/data/resource_packs/heyhe_pet_RP
|
- ./addon/heyhe_pet_RP:/data/resource_packs/heyhe_pet_RP
|
||||||
- ./addon/anthrax_cat_BP:/data/behavior_packs/anthrax_cat_BP
|
- ./addon/anthrax_cat_BP:/data/behavior_packs/anthrax_cat_BP
|
||||||
- ./addon/anthrax_cat_RP:/data/resource_packs/anthrax_cat_RP
|
- ./addon/anthrax_cat_RP:/data/resource_packs/anthrax_cat_RP
|
||||||
|
- ./monkey-addon/monkey_BP:/data/behavior_packs/monkey_BP
|
||||||
|
- ./monkey-addon/monkey_RP:/data/resource_packs/monkey_RP
|
||||||
|
- ./private-chest-addon/private_chest_BP:/data/behavior_packs/private_chest_BP
|
||||||
|
- ./private-chest-addon/private_chest_RP:/data/resource_packs/private_chest_RP
|
||||||
|
- ./home-sign-addon/home_sign_BP:/data/behavior_packs/home_sign_BP
|
||||||
|
- ./home-sign-addon/home_sign_RP:/data/resource_packs/home_sign_RP
|
||||||
|
- ./keep-inventory-addon/keep_inventory_BP:/data/behavior_packs/keep_inventory_BP
|
||||||
|
- ./postal-service-addon/postal_service_BP:/data/behavior_packs/postal_service_BP
|
||||||
|
- ./postal-service-addon/postal_service_RP:/data/resource_packs/postal_service_RP
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
networks:
|
networks:
|
||||||
- mc-network
|
- mc-network
|
||||||
@@ -42,6 +51,7 @@ services:
|
|||||||
ONLINE_MODE: "false"
|
ONLINE_MODE: "false"
|
||||||
SERVER_PORT: "19132"
|
SERVER_PORT: "19132"
|
||||||
LEVEL_NAME: "Jamie World"
|
LEVEL_NAME: "Jamie World"
|
||||||
|
LEVEL_SEED: "-6717666844935858147"
|
||||||
MAX_PLAYERS: "10"
|
MAX_PLAYERS: "10"
|
||||||
DEFAULT_PLAYER_PERMISSION_LEVEL: operator
|
DEFAULT_PLAYER_PERMISSION_LEVEL: operator
|
||||||
OP_PERMISSION_LEVEL: "4"
|
OP_PERMISSION_LEVEL: "4"
|
||||||
@@ -50,14 +60,13 @@ services:
|
|||||||
volumes:
|
volumes:
|
||||||
- jamie-data:/data
|
- jamie-data:/data
|
||||||
- ./hub-return-addon/hub_return_transfer_BP:/data/behavior_packs/hub_return_transfer_BP
|
- ./hub-return-addon/hub_return_transfer_BP:/data/behavior_packs/hub_return_transfer_BP
|
||||||
- ./addon/spark_pet_BP:/data/behavior_packs/spark_pet_BP
|
- ./private-chest-addon/private_chest_BP:/data/behavior_packs/private_chest_BP
|
||||||
- ./addon/spark_pet_RP:/data/resource_packs/spark_pet_RP
|
- ./private-chest-addon/private_chest_RP:/data/resource_packs/private_chest_RP
|
||||||
- ./addon/heyhe_pet_BP:/data/behavior_packs/heyhe_pet_BP
|
- ./home-sign-addon/home_sign_BP:/data/behavior_packs/home_sign_BP
|
||||||
- ./addon/heyhe_pet_RP:/data/resource_packs/heyhe_pet_RP
|
- ./home-sign-addon/home_sign_RP:/data/resource_packs/home_sign_RP
|
||||||
- ./addon/anthrax_cat_BP:/data/behavior_packs/anthrax_cat_BP
|
- ./keep-inventory-addon/keep_inventory_BP:/data/behavior_packs/keep_inventory_BP
|
||||||
- ./addon/anthrax_cat_RP:/data/resource_packs/anthrax_cat_RP
|
- ./postal-service-addon/postal_service_BP:/data/behavior_packs/postal_service_BP
|
||||||
- ./village-evolution-addon/village_evolution_BP:/data/behavior_packs/village_evolution_BP
|
- ./postal-service-addon/postal_service_RP:/data/resource_packs/postal_service_RP
|
||||||
- ./village-evolution-addon/enabled_packs.json:/data/config/default/enabled_packs.json
|
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
networks:
|
networks:
|
||||||
- mc-network
|
- mc-network
|
||||||
@@ -89,6 +98,13 @@ services:
|
|||||||
- ./addon/anthrax_cat_BP:/data/behavior_packs/anthrax_cat_BP
|
- ./addon/anthrax_cat_BP:/data/behavior_packs/anthrax_cat_BP
|
||||||
- ./addon/anthrax_cat_RP:/data/resource_packs/anthrax_cat_RP
|
- ./addon/anthrax_cat_RP:/data/resource_packs/anthrax_cat_RP
|
||||||
- ./village-evolution-addon/village_evolution_BP:/data/behavior_packs/village_evolution_BP
|
- ./village-evolution-addon/village_evolution_BP:/data/behavior_packs/village_evolution_BP
|
||||||
|
- ./private-chest-addon/private_chest_BP:/data/behavior_packs/private_chest_BP
|
||||||
|
- ./private-chest-addon/private_chest_RP:/data/resource_packs/private_chest_RP
|
||||||
|
- ./home-sign-addon/home_sign_BP:/data/behavior_packs/home_sign_BP
|
||||||
|
- ./home-sign-addon/home_sign_RP:/data/resource_packs/home_sign_RP
|
||||||
|
- ./keep-inventory-addon/keep_inventory_BP:/data/behavior_packs/keep_inventory_BP
|
||||||
|
- ./postal-service-addon/postal_service_BP:/data/behavior_packs/postal_service_BP
|
||||||
|
- ./postal-service-addon/postal_service_RP:/data/resource_packs/postal_service_RP
|
||||||
- ./village-evolution-addon/enabled_packs.json:/data/config/default/enabled_packs.json
|
- ./village-evolution-addon/enabled_packs.json:/data/config/default/enabled_packs.json
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
networks:
|
networks:
|
||||||
@@ -121,6 +137,13 @@ services:
|
|||||||
- ./addon/anthrax_cat_BP:/data/behavior_packs/anthrax_cat_BP
|
- ./addon/anthrax_cat_BP:/data/behavior_packs/anthrax_cat_BP
|
||||||
- ./addon/anthrax_cat_RP:/data/resource_packs/anthrax_cat_RP
|
- ./addon/anthrax_cat_RP:/data/resource_packs/anthrax_cat_RP
|
||||||
- ./village-evolution-addon/village_evolution_BP:/data/behavior_packs/village_evolution_BP
|
- ./village-evolution-addon/village_evolution_BP:/data/behavior_packs/village_evolution_BP
|
||||||
|
- ./private-chest-addon/private_chest_BP:/data/behavior_packs/private_chest_BP
|
||||||
|
- ./private-chest-addon/private_chest_RP:/data/resource_packs/private_chest_RP
|
||||||
|
- ./home-sign-addon/home_sign_BP:/data/behavior_packs/home_sign_BP
|
||||||
|
- ./home-sign-addon/home_sign_RP:/data/resource_packs/home_sign_RP
|
||||||
|
- ./keep-inventory-addon/keep_inventory_BP:/data/behavior_packs/keep_inventory_BP
|
||||||
|
- ./postal-service-addon/postal_service_BP:/data/behavior_packs/postal_service_BP
|
||||||
|
- ./postal-service-addon/postal_service_RP:/data/resource_packs/postal_service_RP
|
||||||
- ./village-evolution-addon/enabled_packs.json:/data/config/default/enabled_packs.json
|
- ./village-evolution-addon/enabled_packs.json:/data/config/default/enabled_packs.json
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
networks:
|
networks:
|
||||||
|
|||||||
@@ -240,8 +240,14 @@ function placeAllEggs(playerPos) {
|
|||||||
// Validate at least one stored egg block actually exists
|
// Validate at least one stored egg block actually exists
|
||||||
const stored = getEggPositions();
|
const stored = getEggPositions();
|
||||||
if (stored && stored.length > 0) {
|
if (stored && stored.length > 0) {
|
||||||
|
// Check far-ring eggs (last 5) — they're hardest to reach and least likely
|
||||||
|
// to have been collected yet. Avoids false "blocks missing" detection when
|
||||||
|
// only the close-ring eggs (1-5) have been collected.
|
||||||
let found = 0;
|
let found = 0;
|
||||||
for (let i = 0; i < Math.min(5, stored.length); i++) {
|
const checkAt = [45, 46, 47, 48, 49].filter(i => i < stored.length);
|
||||||
|
const fallback = checkAt.length === 0;
|
||||||
|
const indices = fallback ? [0, 1, 2, 3, 4].filter(i => i < stored.length) : checkAt;
|
||||||
|
for (const i of indices) {
|
||||||
try {
|
try {
|
||||||
const block = overworld.getBlock({ x: stored[i].x, y: stored[i].y, z: stored[i].z });
|
const block = overworld.getBlock({ x: stored[i].x, y: stored[i].y, z: stored[i].z });
|
||||||
if (block && block.typeId.includes("glazed_terracotta")) found++;
|
if (block && block.typeId.includes("glazed_terracotta")) found++;
|
||||||
|
|||||||
27
home-sign-addon/home_sign_BP/blocks/home_sign.json
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
{
|
||||||
|
"format_version": "1.21.0",
|
||||||
|
"minecraft:block": {
|
||||||
|
"description": {
|
||||||
|
"identifier": "silverlabs:home_sign",
|
||||||
|
"menu_category": {
|
||||||
|
"category": "items",
|
||||||
|
"group": "itemGroup.name.decorations"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"components": {
|
||||||
|
"minecraft:destructible_by_mining": {
|
||||||
|
"seconds_to_destroy": 1.5
|
||||||
|
},
|
||||||
|
"minecraft:destructible_by_explosion": {
|
||||||
|
"explosion_resistance": 200.0
|
||||||
|
},
|
||||||
|
"minecraft:map_color": "#C0703A",
|
||||||
|
"minecraft:material_instances": {
|
||||||
|
"*": {
|
||||||
|
"texture": "home_sign",
|
||||||
|
"render_method": "opaque"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
701
home-sign-addon/home_sign_BP/entities/cat.json
Normal file
@@ -0,0 +1,701 @@
|
|||||||
|
{
|
||||||
|
"format_version": "1.26.10",
|
||||||
|
"minecraft:entity": {
|
||||||
|
"description": {
|
||||||
|
"identifier": "minecraft:cat",
|
||||||
|
"spawn_category": "creature",
|
||||||
|
"is_spawnable": true,
|
||||||
|
"is_summonable": true,
|
||||||
|
"properties": {
|
||||||
|
"minecraft:sound_variant": {
|
||||||
|
"type": "enum",
|
||||||
|
"values": ["default", "royal"],
|
||||||
|
"default": "default",
|
||||||
|
"client_sync": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
"component_groups": {
|
||||||
|
"minecraft:cat_baby": {
|
||||||
|
"minecraft:is_baby": {},
|
||||||
|
"minecraft:scale": {
|
||||||
|
"value": 0.4
|
||||||
|
},
|
||||||
|
"minecraft:ageable": {
|
||||||
|
"duration": 1200,
|
||||||
|
"feed_items": ["fish", "salmon"],
|
||||||
|
"pause_growth_items": [ "golden_dandelion" ],
|
||||||
|
"reset_growth_items": [ "golden_dandelion" ],
|
||||||
|
"grow_up": {
|
||||||
|
"event": "minecraft:ageable_grow_up",
|
||||||
|
"target": "self"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"minecraft:cat_adult": {
|
||||||
|
"minecraft:experience_reward": {
|
||||||
|
"on_bred": "Math.Random(1,7)",
|
||||||
|
"on_death": "query.last_hit_by_player ? Math.Random(1,3) : 0"
|
||||||
|
},
|
||||||
|
"minecraft:loot": {
|
||||||
|
"table": "loot_tables/entities/cat.json"
|
||||||
|
},
|
||||||
|
"minecraft:scale": {
|
||||||
|
"value": 0.8
|
||||||
|
},
|
||||||
|
"minecraft:leashable_to": {},
|
||||||
|
"minecraft:breedable": {
|
||||||
|
"require_tame": true,
|
||||||
|
"require_full_health": true,
|
||||||
|
"allow_sitting": true,
|
||||||
|
"breeds_with": {
|
||||||
|
"minecraft:cat": {}
|
||||||
|
},
|
||||||
|
"breed_items": ["fish", "salmon"]
|
||||||
|
},
|
||||||
|
"minecraft:behavior.breed": {
|
||||||
|
"priority": 3,
|
||||||
|
"speed_multiplier": 1.0
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"minecraft:cat_wild": {
|
||||||
|
"minecraft:health": {
|
||||||
|
"value": 10,
|
||||||
|
"max": 10
|
||||||
|
},
|
||||||
|
"minecraft:tameable": {
|
||||||
|
"probability": 0.33,
|
||||||
|
"tame_items": ["fish", "salmon"],
|
||||||
|
"tame_event": {
|
||||||
|
"event": "minecraft:on_tame",
|
||||||
|
"target": "self"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"minecraft:rideable": {
|
||||||
|
"seat_count": 1,
|
||||||
|
"family_types": ["baby_undead"],
|
||||||
|
"seats": {
|
||||||
|
// This value results in zombies floating when riding baby cats,
|
||||||
|
// but switching to a different setup would break pre-existing mobs.
|
||||||
|
"position": [0.0, 0.35, 0.0]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"minecraft:behavior.nearest_attackable_target": {
|
||||||
|
"priority": 1,
|
||||||
|
"reselect_targets": true,
|
||||||
|
"within_radius": 16.0,
|
||||||
|
"entity_types": [
|
||||||
|
{
|
||||||
|
"filters": {
|
||||||
|
"test": "is_family",
|
||||||
|
"subject": "other",
|
||||||
|
"value": "rabbit"
|
||||||
|
},
|
||||||
|
"max_dist": 8
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filters": {
|
||||||
|
"all_of": [
|
||||||
|
{
|
||||||
|
"test": "is_family",
|
||||||
|
"subject": "other",
|
||||||
|
"value": "baby_turtle"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"test": "in_water",
|
||||||
|
"subject": "other",
|
||||||
|
"operator": "!=",
|
||||||
|
"value": true
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"max_dist": 8
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"minecraft:behavior.tempt": {
|
||||||
|
"priority": 5,
|
||||||
|
"speed_multiplier": 0.5,
|
||||||
|
"within_radius": 16,
|
||||||
|
"can_get_scared": true,
|
||||||
|
"tempt_sound": "tempt",
|
||||||
|
"sound_interval": [0, 100],
|
||||||
|
"items": ["fish", "salmon"]
|
||||||
|
},
|
||||||
|
"minecraft:behavior.avoid_mob_type": {
|
||||||
|
"priority": 6,
|
||||||
|
"entity_types": [
|
||||||
|
{
|
||||||
|
"filters": {
|
||||||
|
"test": "is_family",
|
||||||
|
"subject": "other",
|
||||||
|
"value": "player"
|
||||||
|
},
|
||||||
|
"max_dist": 10,
|
||||||
|
"walk_speed_multiplier": 0.8,
|
||||||
|
"sprint_speed_multiplier": 1.33
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"minecraft:behavior.move_towards_dwelling_restriction": {
|
||||||
|
"priority": 7
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"minecraft:cat_tame": {
|
||||||
|
"minecraft:is_tamed": {},
|
||||||
|
"minecraft:health": {
|
||||||
|
"value": 20,
|
||||||
|
"max": 20
|
||||||
|
},
|
||||||
|
"minecraft:color": {
|
||||||
|
"value": 14
|
||||||
|
},
|
||||||
|
"minecraft:sittable": {},
|
||||||
|
"minecraft:is_dyeable": {
|
||||||
|
"interact_text": "action.interact.dye"
|
||||||
|
},
|
||||||
|
"minecraft:on_wake_with_owner": {
|
||||||
|
"event": "minecraft:pet_slept_with_owner",
|
||||||
|
"target": "self"
|
||||||
|
},
|
||||||
|
"minecraft:behavior.teleport_to_owner": {
|
||||||
|
"priority": 0,
|
||||||
|
"filters": {
|
||||||
|
"all_of": [
|
||||||
|
{ "test": "owner_distance", "operator": ">", "value": 12 },
|
||||||
|
{ "test": "is_panicking" }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"minecraft:behavior.pet_sleep_with_owner": {
|
||||||
|
"priority": 2,
|
||||||
|
"speed_multiplier": 1.2,
|
||||||
|
"search_radius": 10,
|
||||||
|
"search_height": 10,
|
||||||
|
"goal_radius": 1.0
|
||||||
|
},
|
||||||
|
"minecraft:behavior.stay_while_sitting": {
|
||||||
|
"priority": 3
|
||||||
|
},
|
||||||
|
"minecraft:behavior.tempt": {
|
||||||
|
"priority": 5,
|
||||||
|
"speed_multiplier": 0.5,
|
||||||
|
"within_radius": 16,
|
||||||
|
"items": ["fish", "salmon"]
|
||||||
|
},
|
||||||
|
"minecraft:behavior.ocelot_sit_on_block": {
|
||||||
|
"priority": 7,
|
||||||
|
"speed_multiplier": 1.0
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"minecraft:cat_gift_for_owner": {
|
||||||
|
"minecraft:behavior.drop_item_for": {
|
||||||
|
"priority": 1,
|
||||||
|
"seconds_before_pickup": 0.0,
|
||||||
|
"cooldown": 0.25,
|
||||||
|
"drop_item_chance": 0.7,
|
||||||
|
"offering_distance": 5.0,
|
||||||
|
"minimum_teleport_distance": 2.0,
|
||||||
|
"max_head_look_at_height": 10.0,
|
||||||
|
"target_range": [5.0, 5.0, 5.0],
|
||||||
|
"teleport_offset": [0.0, 1.0, 0.0],
|
||||||
|
"time_of_day_range": {
|
||||||
|
"min": 0.74999,
|
||||||
|
"max": 0.8
|
||||||
|
},
|
||||||
|
"speed_multiplier": 1.0,
|
||||||
|
"search_range": 5,
|
||||||
|
"search_height": 2,
|
||||||
|
"search_count": 0,
|
||||||
|
"goal_radius": 1.0,
|
||||||
|
"entity_types": [
|
||||||
|
{
|
||||||
|
"filters": {
|
||||||
|
"test": "is_family",
|
||||||
|
"subject": "other",
|
||||||
|
"value": "player"
|
||||||
|
},
|
||||||
|
"max_dist": 6
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"loot_table": "loot_tables/entities/cat_gift.json",
|
||||||
|
"on_drop_attempt": {
|
||||||
|
"event": "minecraft:cat_gifted_owner",
|
||||||
|
"target": "self"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"minecraft:cat_white": {
|
||||||
|
"minecraft:variant": {
|
||||||
|
"value": 0
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"minecraft:cat_tuxedo": {
|
||||||
|
"minecraft:variant": {
|
||||||
|
"value": 1
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"minecraft:cat_red": {
|
||||||
|
"minecraft:variant": {
|
||||||
|
"value": 2
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"minecraft:cat_siamese": {
|
||||||
|
"minecraft:variant": {
|
||||||
|
"value": 3
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"minecraft:cat_british": {
|
||||||
|
"minecraft:variant": {
|
||||||
|
"value": 4
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"minecraft:cat_calico": {
|
||||||
|
"minecraft:variant": {
|
||||||
|
"value": 5
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"minecraft:cat_persian": {
|
||||||
|
"minecraft:variant": {
|
||||||
|
"value": 6
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"minecraft:cat_ragdoll": {
|
||||||
|
"minecraft:variant": {
|
||||||
|
"value": 7
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"minecraft:cat_tabby": {
|
||||||
|
"minecraft:variant": {
|
||||||
|
"value": 8
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"minecraft:cat_black": {
|
||||||
|
"minecraft:variant": {
|
||||||
|
"value": 9
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"minecraft:cat_jellie": {
|
||||||
|
"minecraft:variant": {
|
||||||
|
"value": 10
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
"components": {
|
||||||
|
"minecraft:ambient_sound_interval": {
|
||||||
|
"value": 120,
|
||||||
|
"range": 60,
|
||||||
|
"event_name": "ambient"
|
||||||
|
},
|
||||||
|
"minecraft:offspring": {
|
||||||
|
"offspring_pairs": {
|
||||||
|
"minecraft:cat": "minecraft:cat"
|
||||||
|
},
|
||||||
|
"combine_parent_colors": true
|
||||||
|
},
|
||||||
|
"minecraft:spawn_egg_interaction": {},
|
||||||
|
"minecraft:leashable": {},
|
||||||
|
"minecraft:balloonable": {
|
||||||
|
"mass": 0.6
|
||||||
|
},
|
||||||
|
"minecraft:is_hidden_when_invisible": {},
|
||||||
|
"minecraft:attack_damage": {
|
||||||
|
"value": 4
|
||||||
|
},
|
||||||
|
"minecraft:nameable": {},
|
||||||
|
"minecraft:type_family": {
|
||||||
|
"family": ["cat", "mob"]
|
||||||
|
},
|
||||||
|
"minecraft:breathable": {
|
||||||
|
"total_supply": 15,
|
||||||
|
"suffocate_time": 0
|
||||||
|
},
|
||||||
|
"minecraft:collision_box": {
|
||||||
|
"width": 0.6,
|
||||||
|
"height": 0.7
|
||||||
|
},
|
||||||
|
"minecraft:healable": {
|
||||||
|
"items": [
|
||||||
|
{
|
||||||
|
"item": "fish",
|
||||||
|
"heal_amount": 2
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"item": "salmon",
|
||||||
|
"heal_amount": 2
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"minecraft:hurt_on_condition": {
|
||||||
|
"damage_conditions": [
|
||||||
|
{
|
||||||
|
"filters": {
|
||||||
|
"test": "in_lava",
|
||||||
|
"subject": "self",
|
||||||
|
"operator": "==",
|
||||||
|
"value": true
|
||||||
|
},
|
||||||
|
"cause": "lava",
|
||||||
|
"damage_per_tick": 4
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"minecraft:movement": {
|
||||||
|
"value": 0.3
|
||||||
|
},
|
||||||
|
"minecraft:navigation.walk": {
|
||||||
|
"can_float": true,
|
||||||
|
"avoid_water": true,
|
||||||
|
"avoid_damage_blocks": true
|
||||||
|
},
|
||||||
|
"minecraft:movement.basic": {},
|
||||||
|
"minecraft:jump.static": {},
|
||||||
|
"minecraft:can_climb": {},
|
||||||
|
"minecraft:damage_sensor": {
|
||||||
|
"triggers": {
|
||||||
|
"cause": "fall",
|
||||||
|
"deals_damage": "no"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"minecraft:dweller": {
|
||||||
|
"dwelling_type": "village",
|
||||||
|
"dweller_role": "passive",
|
||||||
|
"update_interval_base": 60,
|
||||||
|
"update_interval_variant": 40,
|
||||||
|
"can_find_poi": false,
|
||||||
|
"can_migrate": true,
|
||||||
|
"first_founding_reward": 0
|
||||||
|
},
|
||||||
|
"minecraft:despawn": {
|
||||||
|
"despawn_from_distance": {}
|
||||||
|
},
|
||||||
|
"minecraft:physics": {},
|
||||||
|
"minecraft:pushable_by_entity": {
|
||||||
|
},
|
||||||
|
"minecraft:pushable_by_block": {
|
||||||
|
},
|
||||||
|
"minecraft:conditional_bandwidth_optimization": {},
|
||||||
|
"minecraft:behavior.float": {
|
||||||
|
"priority": 0
|
||||||
|
},
|
||||||
|
"minecraft:behavior.panic": {
|
||||||
|
"priority": 1,
|
||||||
|
"speed_multiplier": 1.25
|
||||||
|
},
|
||||||
|
"minecraft:behavior.mount_pathing": {
|
||||||
|
"priority": 1,
|
||||||
|
"speed_multiplier": 1.25,
|
||||||
|
"target_dist": 0,
|
||||||
|
"track_target": true
|
||||||
|
},
|
||||||
|
"minecraft:behavior.leap_at_target": {
|
||||||
|
"priority": 3,
|
||||||
|
"target_dist": 0.3
|
||||||
|
},
|
||||||
|
"minecraft:behavior.ocelotattack": {
|
||||||
|
"priority": 4,
|
||||||
|
"cooldown_time": 1.0,
|
||||||
|
"x_max_rotation": 30.0,
|
||||||
|
"y_max_head_rotation": 30.0,
|
||||||
|
"max_distance": 15.0,
|
||||||
|
"max_sneak_range": 15.0,
|
||||||
|
"max_sprint_range": 4.0,
|
||||||
|
"reach_multiplier": 2.0,
|
||||||
|
"sneak_speed_multiplier": 0.6,
|
||||||
|
"sprint_speed_multiplier": 1.33,
|
||||||
|
"walk_speed_multiplier": 0.8
|
||||||
|
},
|
||||||
|
"minecraft:behavior.random_stroll": {
|
||||||
|
"priority": 8,
|
||||||
|
"speed_multiplier": 0.8
|
||||||
|
},
|
||||||
|
"minecraft:behavior.look_at_player": {
|
||||||
|
"priority": 9
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
"events": {
|
||||||
|
"minecraft:entity_spawned": {
|
||||||
|
"sequence": [
|
||||||
|
{
|
||||||
|
"randomize": [
|
||||||
|
{
|
||||||
|
"weight": 3,
|
||||||
|
"remove": {},
|
||||||
|
"add": {
|
||||||
|
"component_groups": [
|
||||||
|
"minecraft:cat_adult",
|
||||||
|
"minecraft:cat_wild"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"weight": 1,
|
||||||
|
"remove": {},
|
||||||
|
"add": {
|
||||||
|
"component_groups": [
|
||||||
|
"minecraft:cat_baby",
|
||||||
|
"minecraft:cat_wild"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"randomize": [
|
||||||
|
{
|
||||||
|
"weight": 15,
|
||||||
|
"add": {
|
||||||
|
"component_groups": ["minecraft:cat_white"]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"weight": 15,
|
||||||
|
"add": {
|
||||||
|
"component_groups": ["minecraft:cat_tuxedo"]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"weight": 15,
|
||||||
|
"add": {
|
||||||
|
"component_groups": ["minecraft:cat_red"]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"weight": 15,
|
||||||
|
"add": {
|
||||||
|
"component_groups": ["minecraft:cat_siamese"]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"weight": 15,
|
||||||
|
"add": {
|
||||||
|
"component_groups": ["minecraft:cat_british"]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"weight": 15,
|
||||||
|
"add": {
|
||||||
|
"component_groups": ["minecraft:cat_calico"]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"weight": 15,
|
||||||
|
"add": {
|
||||||
|
"component_groups": ["minecraft:cat_persian"]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"weight": 15,
|
||||||
|
"add": {
|
||||||
|
"component_groups": ["minecraft:cat_ragdoll"]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"weight": 15,
|
||||||
|
"add": {
|
||||||
|
"component_groups": ["minecraft:cat_tabby"]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"weight": 15,
|
||||||
|
"add": {
|
||||||
|
"component_groups": ["minecraft:cat_black"]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"weight": 15,
|
||||||
|
"add": {
|
||||||
|
"component_groups": ["minecraft:cat_jellie"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"trigger": "minecraft:randomize_sound_variant"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"minecraft:spawn_from_village": {
|
||||||
|
"sequence": [
|
||||||
|
{
|
||||||
|
"randomize": [
|
||||||
|
{
|
||||||
|
"weight": 3,
|
||||||
|
"trigger": "minecraft:spawn_wild_adult"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"weight": 1,
|
||||||
|
"trigger": "minecraft:spawn_wild_baby"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"randomize": [
|
||||||
|
{
|
||||||
|
"weight": 15,
|
||||||
|
"add": {
|
||||||
|
"component_groups": ["minecraft:cat_tuxedo"]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"weight": 15,
|
||||||
|
"add": {
|
||||||
|
"component_groups": ["minecraft:cat_red"]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"weight": 15,
|
||||||
|
"add": {
|
||||||
|
"component_groups": ["minecraft:cat_siamese"]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"weight": 15,
|
||||||
|
"add": {
|
||||||
|
"component_groups": ["minecraft:cat_white"]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"weight": 15,
|
||||||
|
"add": {
|
||||||
|
"component_groups": ["minecraft:cat_british"]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"weight": 15,
|
||||||
|
"add": {
|
||||||
|
"component_groups": ["minecraft:cat_calico"]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"weight": 15,
|
||||||
|
"add": {
|
||||||
|
"component_groups": ["minecraft:cat_persian"]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"weight": 15,
|
||||||
|
"add": {
|
||||||
|
"component_groups": ["minecraft:cat_ragdoll"]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"weight": 15,
|
||||||
|
"add": {
|
||||||
|
"component_groups": ["minecraft:cat_tabby"]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"weight": 15,
|
||||||
|
"add": {
|
||||||
|
"component_groups": ["minecraft:cat_jellie"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"minecraft:spawn_midnight_cat": {
|
||||||
|
"sequence": [
|
||||||
|
{
|
||||||
|
"trigger": "minecraft:spawn_wild_adult",
|
||||||
|
"add": {
|
||||||
|
"component_groups": ["minecraft:cat_black"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"minecraft:entity_born": {
|
||||||
|
"sequence": [
|
||||||
|
{
|
||||||
|
"filters": {
|
||||||
|
"test": "is_tamed"
|
||||||
|
},
|
||||||
|
"trigger": "minecraft:spawn_tame_baby"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filters": {
|
||||||
|
"test": "is_tamed",
|
||||||
|
"value": false
|
||||||
|
},
|
||||||
|
"trigger": "minecraft:spawn_wild_baby"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"minecraft:spawn_wild_baby": {
|
||||||
|
"add": {
|
||||||
|
"component_groups": ["minecraft:cat_baby", "minecraft:cat_wild"]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"minecraft:spawn_wild_adult": {
|
||||||
|
"add": {
|
||||||
|
"component_groups": ["minecraft:cat_adult", "minecraft:cat_wild"]
|
||||||
|
},
|
||||||
|
"trigger": "minecraft:randomize_sound_variant"
|
||||||
|
},
|
||||||
|
"minecraft:spawn_tame_baby": {
|
||||||
|
"add": {
|
||||||
|
"component_groups": ["minecraft:cat_baby", "minecraft:cat_tame"]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"minecraft:spawn_tame_adult": {
|
||||||
|
"add": {
|
||||||
|
"component_groups": ["minecraft:cat_adult", "minecraft:cat_tame"]
|
||||||
|
},
|
||||||
|
"trigger": "minecraft:randomize_sound_variant"
|
||||||
|
},
|
||||||
|
"minecraft:ageable_grow_up": {
|
||||||
|
"remove": {
|
||||||
|
"component_groups": ["minecraft:cat_baby"]
|
||||||
|
},
|
||||||
|
"add": {
|
||||||
|
"component_groups": ["minecraft:cat_adult"]
|
||||||
|
},
|
||||||
|
"trigger": "minecraft:randomize_sound_variant"
|
||||||
|
},
|
||||||
|
"minecraft:on_tame": {
|
||||||
|
"sequence": [
|
||||||
|
{
|
||||||
|
"remove": {
|
||||||
|
"component_groups": ["minecraft:cat_wild"]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"add": {
|
||||||
|
"component_groups": ["minecraft:cat_tame"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"minecraft:pet_slept_with_owner": {
|
||||||
|
"add": {
|
||||||
|
"component_groups": ["minecraft:cat_gift_for_owner"]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"minecraft:cat_gifted_owner": {
|
||||||
|
"remove": {
|
||||||
|
"component_groups": ["minecraft:cat_gift_for_owner"]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"minecraft:randomize_sound_variant": {
|
||||||
|
"randomize": [
|
||||||
|
{
|
||||||
|
"weight": 1,
|
||||||
|
"set_property": {
|
||||||
|
"minecraft:sound_variant": "default"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"weight": 1,
|
||||||
|
"set_property": {
|
||||||
|
"minecraft:sound_variant": "royal"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
34
home-sign-addon/home_sign_BP/manifest.json
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
{
|
||||||
|
"format_version": 2,
|
||||||
|
"header": {
|
||||||
|
"name": "Home Sweet Home",
|
||||||
|
"description": "Defines a 32-block home zone for tamed cats; quieter cat meows",
|
||||||
|
"uuid": "c8e51d72-9a4f-4b3e-b8c1-2f7d3e6a4b80",
|
||||||
|
"version": [1, 0, 0],
|
||||||
|
"min_engine_version": [1, 21, 0]
|
||||||
|
},
|
||||||
|
"modules": [
|
||||||
|
{
|
||||||
|
"type": "data",
|
||||||
|
"uuid": "c8e51d72-9a4f-4b3e-b8c1-2f7d3e6a4b81",
|
||||||
|
"version": [1, 0, 0]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "script",
|
||||||
|
"language": "javascript",
|
||||||
|
"uuid": "c8e51d72-9a4f-4b3e-b8c1-2f7d3e6a4b82",
|
||||||
|
"version": [1, 0, 0],
|
||||||
|
"entry": "scripts/main.js"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"dependencies": [
|
||||||
|
{
|
||||||
|
"module_name": "@minecraft/server",
|
||||||
|
"version": "1.17.0"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"uuid": "c8e51d72-9a4f-4b3e-b8c1-2f7d3e6a4b83",
|
||||||
|
"version": [1, 0, 1]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
18
home-sign-addon/home_sign_BP/recipes/home_sign.json
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
{
|
||||||
|
"format_version": "1.21.0",
|
||||||
|
"minecraft:recipe_shapeless": {
|
||||||
|
"description": {
|
||||||
|
"identifier": "silverlabs:home_sign_recipe"
|
||||||
|
},
|
||||||
|
"tags": ["crafting_table"],
|
||||||
|
"unlock": { "context": "AlwaysUnlocked" },
|
||||||
|
"ingredients": [
|
||||||
|
{ "item": "minecraft:oak_sign" },
|
||||||
|
{ "item": "minecraft:red_dye" }
|
||||||
|
],
|
||||||
|
"result": {
|
||||||
|
"item": "silverlabs:home_sign",
|
||||||
|
"count": 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
191
home-sign-addon/home_sign_BP/scripts/main.js
Normal file
@@ -0,0 +1,191 @@
|
|||||||
|
import { world, system, ItemStack } from "@minecraft/server";
|
||||||
|
|
||||||
|
// ─── Constants ──────────────────────────────────────────────
|
||||||
|
const HOME_BLOCK = "silverlabs:home_sign";
|
||||||
|
const PROP_KEY = "home_signs_v1";
|
||||||
|
|
||||||
|
const HOME_RADIUS = 32; // user-facing "home area" size
|
||||||
|
const STRAY_RADIUS = 16; // cat is nudged back beyond this — keeps cats actually at home, not just "within shouting distance"
|
||||||
|
const RETURN_RADIUS_MIN = 2;
|
||||||
|
const RETURN_RADIUS_MAX = 10;
|
||||||
|
const TICK_INTERVAL = 40; // 2 s
|
||||||
|
|
||||||
|
// ─── State ──────────────────────────────────────────────────
|
||||||
|
// In-memory mirror: { "x,y,z,dim": { ownerId, ownerName } }
|
||||||
|
let homes = {};
|
||||||
|
|
||||||
|
function loadState() {
|
||||||
|
try {
|
||||||
|
const raw = world.getDynamicProperty(PROP_KEY);
|
||||||
|
if (raw && typeof raw === "string") {
|
||||||
|
homes = JSON.parse(raw);
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
world.sendMessage(`§c[Home Sweet Home] Failed to load state: ${e.message}`);
|
||||||
|
homes = {};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function saveState() {
|
||||||
|
try {
|
||||||
|
world.setDynamicProperty(PROP_KEY, JSON.stringify(homes));
|
||||||
|
} catch (e) {
|
||||||
|
world.sendMessage(`§c[Home Sweet Home] Failed to save state: ${e.message}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function keyOf(loc, dimensionId) {
|
||||||
|
return `${Math.floor(loc.x)},${Math.floor(loc.y)},${Math.floor(loc.z)},${dimensionId}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
function claim(loc, dimensionId, player) {
|
||||||
|
homes[keyOf(loc, dimensionId)] = { ownerId: player.id, ownerName: player.name };
|
||||||
|
saveState();
|
||||||
|
}
|
||||||
|
|
||||||
|
function release(loc, dimensionId) {
|
||||||
|
delete homes[keyOf(loc, dimensionId)];
|
||||||
|
saveState();
|
||||||
|
}
|
||||||
|
|
||||||
|
function getOwner(loc, dimensionId) {
|
||||||
|
return homes[keyOf(loc, dimensionId)] || null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ─── Placement: claim home zone ────────────────────────────
|
||||||
|
world.afterEvents.playerPlaceBlock.subscribe((event) => {
|
||||||
|
const block = event.block;
|
||||||
|
if (block.typeId !== HOME_BLOCK) return;
|
||||||
|
const player = event.player;
|
||||||
|
claim(block.location, block.dimension.id, player);
|
||||||
|
player.sendMessage(
|
||||||
|
`§a[Home Sweet Home] §7Home zone set — tamed cats will stay within ${HOME_RADIUS} blocks.`
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
// ─── Break: owner-only ─────────────────────────────────────
|
||||||
|
try {
|
||||||
|
world.beforeEvents.playerBreakBlock.subscribe((event) => {
|
||||||
|
const block = event.block;
|
||||||
|
if (block.typeId !== HOME_BLOCK) return;
|
||||||
|
const owner = getOwner(block.location, block.dimension.id);
|
||||||
|
if (!owner) return; // unclaimed — let it break
|
||||||
|
|
||||||
|
const player = event.player;
|
||||||
|
if (owner.ownerId !== player.id) {
|
||||||
|
event.cancel = true;
|
||||||
|
system.run(() =>
|
||||||
|
player.sendMessage(
|
||||||
|
`§c[Home Sweet Home] §7This sign belongs to §f${owner.ownerName}§7. You can't break it.`
|
||||||
|
)
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Owner break — drop the item back, clear the registry, remove the block.
|
||||||
|
event.cancel = true;
|
||||||
|
const loc = { x: block.location.x, y: block.location.y, z: block.location.z };
|
||||||
|
const dim = block.dimension;
|
||||||
|
system.run(() => {
|
||||||
|
try {
|
||||||
|
const dropPos = { x: loc.x + 0.5, y: loc.y + 0.5, z: loc.z + 0.5 };
|
||||||
|
dim.spawnItem(new ItemStack(HOME_BLOCK, 1), dropPos);
|
||||||
|
dim.runCommand(`setblock ${loc.x} ${loc.y} ${loc.z} air`);
|
||||||
|
} catch (e) {
|
||||||
|
player.sendMessage(`§c[Home Sweet Home] Error during break: ${e.message}`);
|
||||||
|
}
|
||||||
|
release(loc, dim.id);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
} catch (e) {
|
||||||
|
console.warn(`[Home Sweet Home] beforeEvents.playerBreakBlock unavailable: ${e}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ─── Cat homing: nudge stray tamed cats back to the nearest home ──
|
||||||
|
function homesByDimension() {
|
||||||
|
const out = {};
|
||||||
|
for (const key in homes) {
|
||||||
|
const [xs, ys, zs, dim] = key.split(",");
|
||||||
|
if (!out[dim]) out[dim] = [];
|
||||||
|
out[dim].push({
|
||||||
|
x: parseInt(xs, 10) + 0.5,
|
||||||
|
y: parseInt(ys, 10) + 0.5,
|
||||||
|
z: parseInt(zs, 10) + 0.5,
|
||||||
|
ownerName: homes[key].ownerName,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
function nearestHome(catLoc, homeList) {
|
||||||
|
let best = null;
|
||||||
|
let bestD2 = Infinity;
|
||||||
|
for (const h of homeList) {
|
||||||
|
const dx = catLoc.x - h.x;
|
||||||
|
const dy = catLoc.y - h.y;
|
||||||
|
const dz = catLoc.z - h.z;
|
||||||
|
const d2 = dx * dx + dy * dy + dz * dz;
|
||||||
|
if (d2 < bestD2) {
|
||||||
|
bestD2 = d2;
|
||||||
|
best = h;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return { home: best, distance: Math.sqrt(bestD2) };
|
||||||
|
}
|
||||||
|
|
||||||
|
function pickReturnSpot(home) {
|
||||||
|
const angle = Math.random() * Math.PI * 2;
|
||||||
|
const r = RETURN_RADIUS_MIN + Math.random() * (RETURN_RADIUS_MAX - RETURN_RADIUS_MIN);
|
||||||
|
return {
|
||||||
|
x: home.x + Math.cos(angle) * r,
|
||||||
|
y: home.y,
|
||||||
|
z: home.z + Math.sin(angle) * r,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
system.runInterval(() => {
|
||||||
|
const byDim = homesByDimension();
|
||||||
|
const dimIds = Object.keys(byDim);
|
||||||
|
if (dimIds.length === 0) return;
|
||||||
|
|
||||||
|
for (const dimId of dimIds) {
|
||||||
|
let dim;
|
||||||
|
try {
|
||||||
|
dim = world.getDimension(dimId);
|
||||||
|
} catch (e) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
let cats;
|
||||||
|
try {
|
||||||
|
// Tamed cats only — vanilla cats gain the "tamed" family on tame.
|
||||||
|
cats = dim.getEntities({ type: "minecraft:cat", families: ["tamed"] });
|
||||||
|
} catch (e) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const cat of cats) {
|
||||||
|
try {
|
||||||
|
const { home, distance } = nearestHome(cat.location, byDim[dimId]);
|
||||||
|
if (!home || distance <= STRAY_RADIUS) continue;
|
||||||
|
const target = pickReturnSpot(home);
|
||||||
|
const ok = cat.tryTeleport(target, { checkForBlocks: true });
|
||||||
|
if (!ok) {
|
||||||
|
// Retry tighter
|
||||||
|
cat.tryTeleport(
|
||||||
|
{ x: home.x, y: home.y, z: home.z },
|
||||||
|
{ checkForBlocks: false }
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
// Skip bad cat, continue with next
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, TICK_INTERVAL);
|
||||||
|
|
||||||
|
// ─── Boot ──────────────────────────────────────────────────
|
||||||
|
system.run(() => {
|
||||||
|
loadState();
|
||||||
|
world.sendMessage("§a[Home Sweet Home] §7Loaded — place a sign to claim a 32-block home zone.");
|
||||||
|
});
|
||||||
4
home-sign-addon/home_sign_RP/blocks.json
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
{
|
||||||
|
"format_version": [1, 1, 0],
|
||||||
|
"silverlabs:home_sign": { "sound": "wood" }
|
||||||
|
}
|
||||||
17
home-sign-addon/home_sign_RP/manifest.json
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
{
|
||||||
|
"format_version": 2,
|
||||||
|
"header": {
|
||||||
|
"name": "Home Sweet Home Resources",
|
||||||
|
"description": "Texture and lang for the silverlabs:home_sign block",
|
||||||
|
"uuid": "c8e51d72-9a4f-4b3e-b8c1-2f7d3e6a4b83",
|
||||||
|
"version": [1, 0, 0],
|
||||||
|
"min_engine_version": [1, 21, 0]
|
||||||
|
},
|
||||||
|
"modules": [
|
||||||
|
{
|
||||||
|
"type": "resources",
|
||||||
|
"uuid": "c8e51d72-9a4f-4b3e-b8c1-2f7d3e6a4b84",
|
||||||
|
"version": [1, 0, 1]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
BIN
home-sign-addon/home_sign_RP/pack_icon.png
Normal file
|
After Width: | Height: | Size: 971 B |
1
home-sign-addon/home_sign_RP/texts/en_US.lang
Normal file
@@ -0,0 +1 @@
|
|||||||
|
tile.silverlabs:home_sign.name=Home Sweet Home
|
||||||
BIN
home-sign-addon/home_sign_RP/textures/blocks/home_sign.png
Normal file
|
After Width: | Height: | Size: 268 B |
11
home-sign-addon/home_sign_RP/textures/terrain_texture.json
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
{
|
||||||
|
"resource_pack_name": "home_sign_RP",
|
||||||
|
"texture_name": "atlas.terrain",
|
||||||
|
"padding": 8,
|
||||||
|
"num_mip_levels": 4,
|
||||||
|
"texture_data": {
|
||||||
|
"home_sign": {
|
||||||
|
"textures": "textures/blocks/home_sign"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
30
keep-inventory-addon/keep_inventory_BP/manifest.json
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
{
|
||||||
|
"format_version": 2,
|
||||||
|
"header": {
|
||||||
|
"name": "Keep Inventory",
|
||||||
|
"description": "Asserts gamerule keepInventory true on world load (Lyla's world)",
|
||||||
|
"uuid": "b4d72e91-8c43-4f1a-95d2-7e3b6c8a0d10",
|
||||||
|
"version": [1, 0, 0],
|
||||||
|
"min_engine_version": [1, 21, 0]
|
||||||
|
},
|
||||||
|
"modules": [
|
||||||
|
{
|
||||||
|
"type": "data",
|
||||||
|
"uuid": "b4d72e91-8c43-4f1a-95d2-7e3b6c8a0d11",
|
||||||
|
"version": [1, 0, 0]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "script",
|
||||||
|
"language": "javascript",
|
||||||
|
"uuid": "b4d72e91-8c43-4f1a-95d2-7e3b6c8a0d12",
|
||||||
|
"version": [1, 0, 0],
|
||||||
|
"entry": "scripts/main.js"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"dependencies": [
|
||||||
|
{
|
||||||
|
"module_name": "@minecraft/server",
|
||||||
|
"version": "1.17.0"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
17
keep-inventory-addon/keep_inventory_BP/scripts/main.js
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
import { world, system } from "@minecraft/server";
|
||||||
|
|
||||||
|
function assertKeepInventory() {
|
||||||
|
try {
|
||||||
|
world.getDimension("overworld").runCommand("gamerule keepInventory true");
|
||||||
|
} catch (e) {
|
||||||
|
// Non-fatal — chunk-load races, etc. The next interval will retry.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
system.run(() => {
|
||||||
|
assertKeepInventory();
|
||||||
|
world.sendMessage("§a[Keep Inventory] §7keepInventory enforced — items stay on death.");
|
||||||
|
});
|
||||||
|
|
||||||
|
// Re-assert every 10 minutes so anything that toggles it off (manual op, world reset, etc.) gets corrected.
|
||||||
|
system.runInterval(assertKeepInventory, 12000);
|
||||||
@@ -10,9 +10,10 @@ const PORTAL_BLOCKS = {
|
|||||||
|
|
||||||
// Coordinate-based portal zones (fallback detection)
|
// Coordinate-based portal zones (fallback detection)
|
||||||
const PORTAL_ZONES = [
|
const PORTAL_ZONES = [
|
||||||
{ name: "Jamie's World", x: -15, y: 65, z: -24, host: "10.0.0.247", port: 19133, color: "§a" },
|
{ name: "Jamie's World", x: 436, y: 66, z: -296, host: "10.0.0.247", port: 19133, color: "§a" },
|
||||||
{ name: "Lyla's World", x: 0, y: 65, z: -24, host: "10.0.0.247", port: 19134, color: "§d" },
|
{ name: "Lyla's World", x: 462, y: 65, z: -322, host: "10.0.0.247", port: 19134, color: "§d" },
|
||||||
{ name: "Mya's World", x: 15, y: 65, z: -24, host: "10.0.0.247", port: 19135, color: "§b" },
|
{ name: "Lyla's World", x: 474, y: 65, z: -281, host: "10.0.0.247", port: 19134, color: "§d" }, // Super Kitties portal
|
||||||
|
{ name: "Mya's World", x: 488, y: 66, z: -296, host: "10.0.0.247", port: 19135, color: "§b" },
|
||||||
];
|
];
|
||||||
|
|
||||||
const PORTAL_RADIUS_X = 2.5;
|
const PORTAL_RADIUS_X = 2.5;
|
||||||
@@ -114,14 +115,15 @@ world.afterEvents.playerLeave.subscribe((event) => {
|
|||||||
function placePortalSigns() {
|
function placePortalSigns() {
|
||||||
const overworld = world.getDimension("overworld");
|
const overworld = world.getDimension("overworld");
|
||||||
const signs = [
|
const signs = [
|
||||||
{ x: -15, y: 70, z: -23, name: "Jamie's", color: "§a" },
|
{ x: 438, y: 71, z: -296, name: "Jamie's", color: "§a", facing: 5 }, // east-facing
|
||||||
{ x: 0, y: 70, z: -23, name: "Lyla's", color: "§d" },
|
{ x: 462, y: 71, z: -320, name: "Lyla's", color: "§d", facing: 3 }, // south-facing
|
||||||
{ x: 15, y: 70, z: -23, name: "Mya's", color: "§b" },
|
{ x: 486, y: 71, z: -296, name: "Mya's", color: "§b", facing: 4 }, // west-facing
|
||||||
|
{ x: 474, y: 74, z: -281, name: "Super Kitties\n§fLyla's", color: "§d", facing: 2 }, // north-facing (Super Kitties portal)
|
||||||
];
|
];
|
||||||
|
|
||||||
for (const sign of signs) {
|
for (const sign of signs) {
|
||||||
try {
|
try {
|
||||||
overworld.runCommand(`setblock ${sign.x} ${sign.y} ${sign.z} oak_wall_sign ["facing_direction":3]`);
|
overworld.runCommand(`setblock ${sign.x} ${sign.y} ${sign.z} oak_wall_sign ["facing_direction":${sign.facing}]`);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
// Non-fatal — chunks may not be loaded
|
// Non-fatal — chunks may not be loaded
|
||||||
}
|
}
|
||||||
|
|||||||
41
monkey-addon/monkey_BP/entities/banana_projectile.json
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
{
|
||||||
|
"format_version": "1.21.0",
|
||||||
|
"minecraft:entity": {
|
||||||
|
"description": {
|
||||||
|
"identifier": "silverlabs:banana_projectile",
|
||||||
|
"is_spawnable": false,
|
||||||
|
"is_summonable": true,
|
||||||
|
"is_experimental": false
|
||||||
|
},
|
||||||
|
|
||||||
|
"components": {
|
||||||
|
"minecraft:collision_box": {
|
||||||
|
"width": 0.25,
|
||||||
|
"height": 0.25
|
||||||
|
},
|
||||||
|
"minecraft:physics": {},
|
||||||
|
"minecraft:pushable": {
|
||||||
|
"is_pushable": false,
|
||||||
|
"is_pushable_by_piston": false
|
||||||
|
},
|
||||||
|
"minecraft:projectile": {
|
||||||
|
"on_hit": {
|
||||||
|
"impact_damage": {
|
||||||
|
"damage": 0,
|
||||||
|
"knockback": true,
|
||||||
|
"semi_random_diff_damage": false
|
||||||
|
},
|
||||||
|
"remove_on_hit": {}
|
||||||
|
},
|
||||||
|
"power": 1.3,
|
||||||
|
"gravity": 0.05,
|
||||||
|
"inertia": 0.99,
|
||||||
|
"liquid_inertia": 0.6,
|
||||||
|
"anchor": 1,
|
||||||
|
"offset": [0, -0.1, 0],
|
||||||
|
"should_bounce": false,
|
||||||
|
"hit_sound": "mob.slime.small"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
118
monkey-addon/monkey_BP/entities/cheeky_monkey.json
Normal file
@@ -0,0 +1,118 @@
|
|||||||
|
{
|
||||||
|
"format_version": "1.21.0",
|
||||||
|
"minecraft:entity": {
|
||||||
|
"description": {
|
||||||
|
"identifier": "silverlabs:cheeky_monkey",
|
||||||
|
"is_spawnable": true,
|
||||||
|
"is_summonable": true,
|
||||||
|
"is_experimental": false
|
||||||
|
},
|
||||||
|
|
||||||
|
"components": {
|
||||||
|
"minecraft:type_family": {
|
||||||
|
"family": ["cheeky_monkey", "monkey", "animal", "mob"]
|
||||||
|
},
|
||||||
|
"minecraft:physics": {},
|
||||||
|
"minecraft:pushable": {
|
||||||
|
"is_pushable": true,
|
||||||
|
"is_pushable_by_piston": true
|
||||||
|
},
|
||||||
|
"minecraft:collision_box": {
|
||||||
|
"width": 0.5,
|
||||||
|
"height": 0.9
|
||||||
|
},
|
||||||
|
"minecraft:health": {
|
||||||
|
"value": 8,
|
||||||
|
"max": 8
|
||||||
|
},
|
||||||
|
"minecraft:movement": {
|
||||||
|
"value": 0.3
|
||||||
|
},
|
||||||
|
"minecraft:navigation.walk": {
|
||||||
|
"can_path_over_water": false,
|
||||||
|
"avoid_water": true,
|
||||||
|
"avoid_damage_blocks": true
|
||||||
|
},
|
||||||
|
"minecraft:movement.basic": {},
|
||||||
|
"minecraft:jump.static": {},
|
||||||
|
"minecraft:can_climb": {},
|
||||||
|
"minecraft:breathable": {
|
||||||
|
"total_supply": 15,
|
||||||
|
"suffocate_time": -1
|
||||||
|
},
|
||||||
|
"minecraft:nameable": {},
|
||||||
|
"minecraft:hurt_on_condition": {
|
||||||
|
"damage_conditions": [
|
||||||
|
{
|
||||||
|
"filters": { "test": "in_lava", "subject": "self", "operator": "==", "value": true },
|
||||||
|
"cause": "lava",
|
||||||
|
"damage_per_tick": 4
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
|
||||||
|
"minecraft:loot": {
|
||||||
|
"table": "loot_tables/entities/cheeky_monkey.json"
|
||||||
|
},
|
||||||
|
|
||||||
|
"minecraft:shooter": {
|
||||||
|
"def": "silverlabs:banana_projectile"
|
||||||
|
},
|
||||||
|
|
||||||
|
"minecraft:behavior.float": { "priority": 0 },
|
||||||
|
|
||||||
|
"minecraft:behavior.panic": {
|
||||||
|
"priority": 1,
|
||||||
|
"speed_multiplier": 1.6
|
||||||
|
},
|
||||||
|
|
||||||
|
"minecraft:behavior.ranged_attack": {
|
||||||
|
"priority": 3,
|
||||||
|
"attack_interval_min": 3.0,
|
||||||
|
"attack_interval_max": 6.0,
|
||||||
|
"attack_radius": 10.0,
|
||||||
|
"speed_multiplier": 0.9
|
||||||
|
},
|
||||||
|
|
||||||
|
"minecraft:behavior.nearest_attackable_target": {
|
||||||
|
"priority": 4,
|
||||||
|
"within_radius": 16,
|
||||||
|
"entity_types": [
|
||||||
|
{
|
||||||
|
"filters": { "test": "is_family", "subject": "other", "value": "player" },
|
||||||
|
"max_dist": 16
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"must_see": false,
|
||||||
|
"reselect_targets": true,
|
||||||
|
"must_reach": false
|
||||||
|
},
|
||||||
|
|
||||||
|
"minecraft:behavior.move_towards_target": {
|
||||||
|
"priority": 5,
|
||||||
|
"speed_multiplier": 0.8,
|
||||||
|
"within_radius": 10.0
|
||||||
|
},
|
||||||
|
|
||||||
|
"minecraft:behavior.look_at_player": {
|
||||||
|
"priority": 7,
|
||||||
|
"target_distance": 14,
|
||||||
|
"look_time": [3, 7],
|
||||||
|
"probability": 0.9
|
||||||
|
},
|
||||||
|
|
||||||
|
"minecraft:behavior.random_stroll": {
|
||||||
|
"priority": 6,
|
||||||
|
"speed_multiplier": 0.7,
|
||||||
|
"interval": 60,
|
||||||
|
"xz_dist": 8,
|
||||||
|
"y_dist": 4
|
||||||
|
},
|
||||||
|
|
||||||
|
"minecraft:behavior.random_look_around": {
|
||||||
|
"priority": 9,
|
||||||
|
"look_time": [2, 5]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
32
monkey-addon/monkey_BP/items/banana.json
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
{
|
||||||
|
"format_version": "1.21.0",
|
||||||
|
"minecraft:item": {
|
||||||
|
"description": {
|
||||||
|
"identifier": "silverlabs:banana",
|
||||||
|
"menu_category": {
|
||||||
|
"category": "nature"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"components": {
|
||||||
|
"minecraft:icon": {
|
||||||
|
"textures": {
|
||||||
|
"default": "banana"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"minecraft:display_name": {
|
||||||
|
"value": "Banana"
|
||||||
|
},
|
||||||
|
"minecraft:max_stack_size": 64,
|
||||||
|
"minecraft:use_animation": "eat",
|
||||||
|
"minecraft:use_modifiers": {
|
||||||
|
"use_duration": 1.6,
|
||||||
|
"movement_modifier": 0.35
|
||||||
|
},
|
||||||
|
"minecraft:food": {
|
||||||
|
"nutrition": 4,
|
||||||
|
"saturation_modifier": 0.6,
|
||||||
|
"can_always_eat": false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
{
|
||||||
|
"pools": [
|
||||||
|
{
|
||||||
|
"rolls": 1,
|
||||||
|
"entries": [
|
||||||
|
{
|
||||||
|
"type": "item",
|
||||||
|
"name": "silverlabs:banana",
|
||||||
|
"weight": 1,
|
||||||
|
"functions": [
|
||||||
|
{
|
||||||
|
"function": "set_count",
|
||||||
|
"count": { "min": 1, "max": 2 }
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
23
monkey-addon/monkey_BP/manifest.json
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
{
|
||||||
|
"format_version": 2,
|
||||||
|
"header": {
|
||||||
|
"name": "Cheeky Monkey",
|
||||||
|
"description": "Curious banana-throwing monkeys that spawn naturally in jungles.",
|
||||||
|
"uuid": "c9d1f11c-0eef-4374-81ee-ebde67d7a0a3",
|
||||||
|
"version": [1, 0, 0],
|
||||||
|
"min_engine_version": [1, 21, 0]
|
||||||
|
},
|
||||||
|
"modules": [
|
||||||
|
{
|
||||||
|
"type": "data",
|
||||||
|
"uuid": "48d1d747-0765-49ab-8f50-e9b0236c8e32",
|
||||||
|
"version": [1, 0, 0]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"dependencies": [
|
||||||
|
{
|
||||||
|
"uuid": "7195b65a-171d-4324-a2df-e36ba75ec48a",
|
||||||
|
"version": [1, 0, 0]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
BIN
monkey-addon/monkey_BP/pack_icon.png
Normal file
|
After Width: | Height: | Size: 1.1 KiB |
41
monkey-addon/monkey_BP/spawn_rules/cheeky_monkey.json
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
{
|
||||||
|
"format_version": "1.8.0",
|
||||||
|
"minecraft:spawn_rules": {
|
||||||
|
"description": {
|
||||||
|
"identifier": "silverlabs:cheeky_monkey",
|
||||||
|
"population_control": "animal"
|
||||||
|
},
|
||||||
|
"conditions": [
|
||||||
|
{
|
||||||
|
"minecraft:spawns_on_surface": {},
|
||||||
|
"minecraft:brightness_filter": {
|
||||||
|
"min": 7,
|
||||||
|
"max": 15,
|
||||||
|
"adjust_for_weather": false
|
||||||
|
},
|
||||||
|
"minecraft:weight": {
|
||||||
|
"default": 8
|
||||||
|
},
|
||||||
|
"minecraft:herd": {
|
||||||
|
"min_size": 2,
|
||||||
|
"max_size": 4
|
||||||
|
},
|
||||||
|
"minecraft:density_limit": {
|
||||||
|
"surface": 4
|
||||||
|
},
|
||||||
|
"minecraft:biome_filter": {
|
||||||
|
"any_of": [
|
||||||
|
{ "test": "has_biome_tag", "value": "jungle" },
|
||||||
|
{ "test": "has_biome_tag", "value": "bamboo" }
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"minecraft:spawns_on_block_filter": [
|
||||||
|
"minecraft:grass_block",
|
||||||
|
"minecraft:dirt",
|
||||||
|
"minecraft:podzol",
|
||||||
|
"minecraft:moss_block"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
{
|
||||||
|
"format_version": "1.10.0",
|
||||||
|
"animation_controllers": {
|
||||||
|
"controller.animation.cheeky_monkey": {
|
||||||
|
"initial_state": "idle",
|
||||||
|
"states": {
|
||||||
|
"idle": {
|
||||||
|
"animations": ["idle"],
|
||||||
|
"transitions": [
|
||||||
|
{ "walk": "query.modified_move_speed > 0.1" }
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"walk": {
|
||||||
|
"animations": ["walk", "idle"],
|
||||||
|
"transitions": [
|
||||||
|
{ "idle": "query.modified_move_speed <= 0.1" }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
{
|
||||||
|
"format_version": "1.8.0",
|
||||||
|
"animations": {
|
||||||
|
"animation.banana_projectile.spin": {
|
||||||
|
"loop": true,
|
||||||
|
"bones": {
|
||||||
|
"banana": {
|
||||||
|
"rotation": ["query.anim_time * 720", "0", "query.anim_time * 360"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,70 @@
|
|||||||
|
{
|
||||||
|
"format_version": "1.8.0",
|
||||||
|
"animations": {
|
||||||
|
"animation.cheeky_monkey.idle": {
|
||||||
|
"loop": true,
|
||||||
|
"bones": {
|
||||||
|
"body": {
|
||||||
|
"rotation": ["0", "math.sin(query.anim_time * 120) * 1.5", "0"]
|
||||||
|
},
|
||||||
|
"head": {
|
||||||
|
"rotation": ["math.sin(query.anim_time * 90) * 3", "math.sin(query.anim_time * 60) * 5", "0"]
|
||||||
|
},
|
||||||
|
"tail": {
|
||||||
|
"rotation": ["25 + math.sin(query.anim_time * 180) * 8", "math.sin(query.anim_time * 140) * 10", "0"]
|
||||||
|
},
|
||||||
|
"arm_left": {
|
||||||
|
"rotation": ["math.sin(query.anim_time * 100) * 3", "0", "0"]
|
||||||
|
},
|
||||||
|
"arm_right": {
|
||||||
|
"rotation": ["-math.sin(query.anim_time * 100) * 3", "0", "0"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
"animation.cheeky_monkey.walk": {
|
||||||
|
"loop": true,
|
||||||
|
"anim_time_update": "query.modified_distance_moved * 1.5",
|
||||||
|
"bones": {
|
||||||
|
"leg_left": {
|
||||||
|
"rotation": ["math.sin(query.anim_time * 240) * 30", "0", "0"]
|
||||||
|
},
|
||||||
|
"leg_right": {
|
||||||
|
"rotation": ["-math.sin(query.anim_time * 240) * 30", "0", "0"]
|
||||||
|
},
|
||||||
|
"arm_left": {
|
||||||
|
"rotation": ["-math.sin(query.anim_time * 240) * 25", "0", "0"]
|
||||||
|
},
|
||||||
|
"arm_right": {
|
||||||
|
"rotation": ["math.sin(query.anim_time * 240) * 25", "0", "0"]
|
||||||
|
},
|
||||||
|
"tail": {
|
||||||
|
"rotation": ["25 + math.sin(query.anim_time * 240) * 15", "0", "0"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
"animation.cheeky_monkey.throw": {
|
||||||
|
"loop": false,
|
||||||
|
"animation_length": 0.6,
|
||||||
|
"bones": {
|
||||||
|
"arm_right": {
|
||||||
|
"rotation": {
|
||||||
|
"0.0": [0, 0, 0],
|
||||||
|
"0.15": [-110, 0, 15],
|
||||||
|
"0.35": [60, 0, -10],
|
||||||
|
"0.6": [0, 0, 0]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"body": {
|
||||||
|
"rotation": {
|
||||||
|
"0.0": [0, 0, 0],
|
||||||
|
"0.15": [0, -10, 0],
|
||||||
|
"0.35": [0, 15, 0],
|
||||||
|
"0.6": [0, 0, 0]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
26
monkey-addon/monkey_RP/entity/banana_projectile.entity.json
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
{
|
||||||
|
"format_version": "1.10.0",
|
||||||
|
"minecraft:client_entity": {
|
||||||
|
"description": {
|
||||||
|
"identifier": "silverlabs:banana_projectile",
|
||||||
|
"materials": {
|
||||||
|
"default": "entity_alphatest"
|
||||||
|
},
|
||||||
|
"textures": {
|
||||||
|
"default": "textures/entity/banana_projectile"
|
||||||
|
},
|
||||||
|
"geometry": {
|
||||||
|
"default": "geometry.banana_projectile"
|
||||||
|
},
|
||||||
|
"render_controllers": [
|
||||||
|
"controller.render.banana_projectile"
|
||||||
|
],
|
||||||
|
"animations": {
|
||||||
|
"spin": "animation.banana_projectile.spin"
|
||||||
|
},
|
||||||
|
"scripts": {
|
||||||
|
"animate": ["spin"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
33
monkey-addon/monkey_RP/entity/cheeky_monkey.entity.json
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
{
|
||||||
|
"format_version": "1.10.0",
|
||||||
|
"minecraft:client_entity": {
|
||||||
|
"description": {
|
||||||
|
"identifier": "silverlabs:cheeky_monkey",
|
||||||
|
"materials": {
|
||||||
|
"default": "entity_alphatest"
|
||||||
|
},
|
||||||
|
"textures": {
|
||||||
|
"default": "textures/entity/cheeky_monkey"
|
||||||
|
},
|
||||||
|
"geometry": {
|
||||||
|
"default": "geometry.cheeky_monkey"
|
||||||
|
},
|
||||||
|
"render_controllers": [
|
||||||
|
"controller.render.cheeky_monkey"
|
||||||
|
],
|
||||||
|
"animations": {
|
||||||
|
"idle": "animation.cheeky_monkey.idle",
|
||||||
|
"walk": "animation.cheeky_monkey.walk",
|
||||||
|
"throw": "animation.cheeky_monkey.throw",
|
||||||
|
"controller": "controller.animation.cheeky_monkey"
|
||||||
|
},
|
||||||
|
"scripts": {
|
||||||
|
"animate": ["controller"]
|
||||||
|
},
|
||||||
|
"spawn_egg": {
|
||||||
|
"base_color": "#6B4423",
|
||||||
|
"overlay_color": "#F4D03F"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
17
monkey-addon/monkey_RP/manifest.json
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
{
|
||||||
|
"format_version": 2,
|
||||||
|
"header": {
|
||||||
|
"name": "Cheeky Monkey Resources",
|
||||||
|
"description": "Textures, models, and animations for the cheeky monkey.",
|
||||||
|
"uuid": "7195b65a-171d-4324-a2df-e36ba75ec48a",
|
||||||
|
"version": [1, 0, 0],
|
||||||
|
"min_engine_version": [1, 21, 0]
|
||||||
|
},
|
||||||
|
"modules": [
|
||||||
|
{
|
||||||
|
"type": "resources",
|
||||||
|
"uuid": "52b854fe-8b36-4a0d-a94a-d20b8fbca74a",
|
||||||
|
"version": [1, 0, 0]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -0,0 +1,41 @@
|
|||||||
|
{
|
||||||
|
"format_version": "1.12.0",
|
||||||
|
"minecraft:geometry": [
|
||||||
|
{
|
||||||
|
"description": {
|
||||||
|
"identifier": "geometry.banana_projectile",
|
||||||
|
"texture_width": 16,
|
||||||
|
"texture_height": 16,
|
||||||
|
"visible_bounds_width": 1,
|
||||||
|
"visible_bounds_height": 1,
|
||||||
|
"visible_bounds_offset": [0, 0.25, 0]
|
||||||
|
},
|
||||||
|
"bones": [
|
||||||
|
{ "name": "root", "pivot": [0, 0, 0] },
|
||||||
|
{
|
||||||
|
"name": "banana",
|
||||||
|
"parent": "root",
|
||||||
|
"pivot": [0, 0, 0],
|
||||||
|
"rotation": [0, 0, 25],
|
||||||
|
"cubes": [
|
||||||
|
{
|
||||||
|
"origin": [-2, 0, -0.5],
|
||||||
|
"size": [4, 1, 1],
|
||||||
|
"uv": [0, 0]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"origin": [-3, 0.5, -0.5],
|
||||||
|
"size": [1, 1, 1],
|
||||||
|
"uv": [0, 2]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"origin": [2, 0.5, -0.5],
|
||||||
|
"size": [1, 1, 1],
|
||||||
|
"uv": [0, 2]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
152
monkey-addon/monkey_RP/models/entity/cheeky_monkey.geo.json
Normal file
@@ -0,0 +1,152 @@
|
|||||||
|
{
|
||||||
|
"format_version": "1.12.0",
|
||||||
|
"minecraft:geometry": [
|
||||||
|
{
|
||||||
|
"description": {
|
||||||
|
"identifier": "geometry.cheeky_monkey",
|
||||||
|
"texture_width": 64,
|
||||||
|
"texture_height": 32,
|
||||||
|
"visible_bounds_width": 2,
|
||||||
|
"visible_bounds_height": 2.5,
|
||||||
|
"visible_bounds_offset": [0, 1, 0]
|
||||||
|
},
|
||||||
|
"bones": [
|
||||||
|
{ "name": "root", "pivot": [0, 0, 0] },
|
||||||
|
|
||||||
|
{
|
||||||
|
"name": "body",
|
||||||
|
"parent": "root",
|
||||||
|
"pivot": [0, 8, 0],
|
||||||
|
"cubes": [
|
||||||
|
{
|
||||||
|
"origin": [-3, 4, -2],
|
||||||
|
"size": [6, 8, 4],
|
||||||
|
"uv": [0, 0]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
"name": "head",
|
||||||
|
"parent": "body",
|
||||||
|
"pivot": [0, 12, 0],
|
||||||
|
"cubes": [
|
||||||
|
{
|
||||||
|
"origin": [-3, 12, -3],
|
||||||
|
"size": [6, 6, 6],
|
||||||
|
"uv": [20, 0]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
"name": "muzzle",
|
||||||
|
"parent": "head",
|
||||||
|
"pivot": [0, 14, -3],
|
||||||
|
"cubes": [
|
||||||
|
{
|
||||||
|
"origin": [-1.5, 13, -5],
|
||||||
|
"size": [3, 2, 2],
|
||||||
|
"uv": [44, 0]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
"name": "ear_left",
|
||||||
|
"parent": "head",
|
||||||
|
"pivot": [-3, 17, 0],
|
||||||
|
"cubes": [
|
||||||
|
{
|
||||||
|
"origin": [-4, 16, -1],
|
||||||
|
"size": [1, 2, 2],
|
||||||
|
"uv": [50, 0]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
"name": "ear_right",
|
||||||
|
"parent": "head",
|
||||||
|
"pivot": [3, 17, 0],
|
||||||
|
"mirror": true,
|
||||||
|
"cubes": [
|
||||||
|
{
|
||||||
|
"origin": [3, 16, -1],
|
||||||
|
"size": [1, 2, 2],
|
||||||
|
"uv": [50, 0]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
"name": "arm_left",
|
||||||
|
"parent": "body",
|
||||||
|
"pivot": [-3, 11, 0],
|
||||||
|
"cubes": [
|
||||||
|
{
|
||||||
|
"origin": [-5, 5, -1],
|
||||||
|
"size": [2, 6, 2],
|
||||||
|
"uv": [0, 16]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
"name": "arm_right",
|
||||||
|
"parent": "body",
|
||||||
|
"pivot": [3, 11, 0],
|
||||||
|
"mirror": true,
|
||||||
|
"cubes": [
|
||||||
|
{
|
||||||
|
"origin": [3, 5, -1],
|
||||||
|
"size": [2, 6, 2],
|
||||||
|
"uv": [0, 16]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
"name": "leg_left",
|
||||||
|
"parent": "root",
|
||||||
|
"pivot": [-1.5, 4, 0],
|
||||||
|
"cubes": [
|
||||||
|
{
|
||||||
|
"origin": [-2.5, 0, -1],
|
||||||
|
"size": [2, 4, 2],
|
||||||
|
"uv": [16, 16]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
"name": "leg_right",
|
||||||
|
"parent": "root",
|
||||||
|
"pivot": [1.5, 4, 0],
|
||||||
|
"mirror": true,
|
||||||
|
"cubes": [
|
||||||
|
{
|
||||||
|
"origin": [0.5, 0, -1],
|
||||||
|
"size": [2, 4, 2],
|
||||||
|
"uv": [16, 16]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
"name": "tail",
|
||||||
|
"parent": "body",
|
||||||
|
"pivot": [0, 5, 2],
|
||||||
|
"rotation": [-25, 0, 0],
|
||||||
|
"cubes": [
|
||||||
|
{
|
||||||
|
"origin": [-0.5, 4.5, 1],
|
||||||
|
"size": [1, 1, 5],
|
||||||
|
"uv": [32, 16]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
BIN
monkey-addon/monkey_RP/pack_icon.png
Normal file
|
After Width: | Height: | Size: 1.1 KiB |
@@ -0,0 +1,19 @@
|
|||||||
|
{
|
||||||
|
"format_version": "1.10.0",
|
||||||
|
"render_controllers": {
|
||||||
|
"controller.render.cheeky_monkey": {
|
||||||
|
"geometry": "geometry.default",
|
||||||
|
"materials": [
|
||||||
|
{ "*": "material.default" }
|
||||||
|
],
|
||||||
|
"textures": ["texture.default"]
|
||||||
|
},
|
||||||
|
"controller.render.banana_projectile": {
|
||||||
|
"geometry": "geometry.default",
|
||||||
|
"materials": [
|
||||||
|
{ "*": "material.default" }
|
||||||
|
],
|
||||||
|
"textures": ["texture.default"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
3
monkey-addon/monkey_RP/texts/en_US.lang
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
entity.silverlabs:cheeky_monkey.name=Cheeky Monkey
|
||||||
|
item.spawn_egg.entity.silverlabs:cheeky_monkey.name=Spawn Cheeky Monkey
|
||||||
|
item.silverlabs:banana=Banana
|
||||||
BIN
monkey-addon/monkey_RP/textures/entity/banana_projectile.png
Normal file
|
After Width: | Height: | Size: 99 B |
BIN
monkey-addon/monkey_RP/textures/entity/cheeky_monkey.png
Normal file
|
After Width: | Height: | Size: 254 B |
9
monkey-addon/monkey_RP/textures/item_texture.json
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
{
|
||||||
|
"resource_pack_name": "monkey_RP",
|
||||||
|
"texture_name": "atlas.items",
|
||||||
|
"texture_data": {
|
||||||
|
"banana": {
|
||||||
|
"textures": "textures/items/banana"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
BIN
monkey-addon/monkey_RP/textures/items/banana.png
Normal file
|
After Width: | Height: | Size: 215 B |
27
postal-service-addon/postal_service_BP/blocks/mailbox.json
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
{
|
||||||
|
"format_version": "1.21.0",
|
||||||
|
"minecraft:block": {
|
||||||
|
"description": {
|
||||||
|
"identifier": "silverlabs:mailbox",
|
||||||
|
"menu_category": {
|
||||||
|
"category": "items",
|
||||||
|
"group": "itemGroup.name.chest"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"components": {
|
||||||
|
"minecraft:destructible_by_mining": {
|
||||||
|
"seconds_to_destroy": 2.0
|
||||||
|
},
|
||||||
|
"minecraft:destructible_by_explosion": {
|
||||||
|
"explosion_resistance": 1200.0
|
||||||
|
},
|
||||||
|
"minecraft:map_color": "#C83232",
|
||||||
|
"minecraft:material_instances": {
|
||||||
|
"*": {
|
||||||
|
"texture": "mailbox",
|
||||||
|
"render_method": "opaque"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
{
|
||||||
|
"format_version": "1.21.0",
|
||||||
|
"minecraft:block": {
|
||||||
|
"description": {
|
||||||
|
"identifier": "silverlabs:post_office",
|
||||||
|
"menu_category": {
|
||||||
|
"category": "items",
|
||||||
|
"group": "itemGroup.name.chest"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"components": {
|
||||||
|
"minecraft:destructible_by_mining": {
|
||||||
|
"seconds_to_destroy": 2.5
|
||||||
|
},
|
||||||
|
"minecraft:destructible_by_explosion": {
|
||||||
|
"explosion_resistance": 1200.0
|
||||||
|
},
|
||||||
|
"minecraft:map_color": "#8B5A2B",
|
||||||
|
"minecraft:material_instances": {
|
||||||
|
"*": {
|
||||||
|
"texture": "post_office",
|
||||||
|
"render_method": "opaque"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
38
postal-service-addon/postal_service_BP/manifest.json
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
{
|
||||||
|
"format_version": 2,
|
||||||
|
"header": {
|
||||||
|
"name": "Postal Service",
|
||||||
|
"description": "Player-to-player mail: personal mailboxes and a post office send block",
|
||||||
|
"uuid": "d7a4b9c1-2e3f-4a5b-8c6d-1f2e3d4c5b60",
|
||||||
|
"version": [1, 0, 0],
|
||||||
|
"min_engine_version": [1, 21, 0]
|
||||||
|
},
|
||||||
|
"modules": [
|
||||||
|
{
|
||||||
|
"type": "data",
|
||||||
|
"uuid": "d7a4b9c1-2e3f-4a5b-8c6d-1f2e3d4c5b61",
|
||||||
|
"version": [1, 0, 0]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "script",
|
||||||
|
"language": "javascript",
|
||||||
|
"uuid": "d7a4b9c1-2e3f-4a5b-8c6d-1f2e3d4c5b62",
|
||||||
|
"version": [1, 0, 0],
|
||||||
|
"entry": "scripts/main.js"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"dependencies": [
|
||||||
|
{
|
||||||
|
"module_name": "@minecraft/server",
|
||||||
|
"version": "1.17.0"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"module_name": "@minecraft/server-ui",
|
||||||
|
"version": "1.3.0"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"uuid": "d7a4b9c1-2e3f-4a5b-8c6d-1f2e3d4c5b63",
|
||||||
|
"version": [1, 0, 0]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
18
postal-service-addon/postal_service_BP/recipes/mailbox.json
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
{
|
||||||
|
"format_version": "1.21.0",
|
||||||
|
"minecraft:recipe_shapeless": {
|
||||||
|
"description": {
|
||||||
|
"identifier": "silverlabs:mailbox_recipe"
|
||||||
|
},
|
||||||
|
"tags": ["crafting_table"],
|
||||||
|
"unlock": { "context": "AlwaysUnlocked" },
|
||||||
|
"ingredients": [
|
||||||
|
{ "item": "minecraft:chest" },
|
||||||
|
{ "item": "minecraft:gold_ingot" }
|
||||||
|
],
|
||||||
|
"result": {
|
||||||
|
"item": "silverlabs:mailbox",
|
||||||
|
"count": 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
{
|
||||||
|
"format_version": "1.21.0",
|
||||||
|
"minecraft:recipe_shaped": {
|
||||||
|
"description": {
|
||||||
|
"identifier": "silverlabs:post_office_recipe"
|
||||||
|
},
|
||||||
|
"tags": ["crafting_table"],
|
||||||
|
"unlock": { "context": "AlwaysUnlocked" },
|
||||||
|
"pattern": [
|
||||||
|
"III",
|
||||||
|
"PCP",
|
||||||
|
"PPP"
|
||||||
|
],
|
||||||
|
"key": {
|
||||||
|
"I": { "item": "minecraft:iron_ingot" },
|
||||||
|
"P": { "item": "minecraft:oak_planks" },
|
||||||
|
"C": { "item": "minecraft:chest" }
|
||||||
|
},
|
||||||
|
"result": {
|
||||||
|
"item": "silverlabs:post_office",
|
||||||
|
"count": 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
390
postal-service-addon/postal_service_BP/scripts/main.js
Normal file
@@ -0,0 +1,390 @@
|
|||||||
|
import { world, system, ItemStack } from "@minecraft/server";
|
||||||
|
import { ActionFormData, MessageFormData } from "@minecraft/server-ui";
|
||||||
|
|
||||||
|
// ─── Constants ──────────────────────────────────────────────
|
||||||
|
const MAILBOX_BLOCK = "silverlabs:mailbox";
|
||||||
|
const POST_OFFICE_BLOCK = "silverlabs:post_office";
|
||||||
|
const VANILLA_CHEST = "minecraft:chest";
|
||||||
|
const PROP_KEY = "postal_state_v1";
|
||||||
|
|
||||||
|
// ─── State ──────────────────────────────────────────────────
|
||||||
|
// mailboxes: { "x,y,z,dim": { ownerId, ownerName } }
|
||||||
|
// registry: { [ownerId]: { name, x, y, z, dim } } — reverse lookup for recipient picker
|
||||||
|
// pending: { [ownerId]: [{ from, itemSummary, ts }] } — queued offline notifications
|
||||||
|
let state = { mailboxes: {}, registry: {}, pending: {} };
|
||||||
|
|
||||||
|
function loadState() {
|
||||||
|
try {
|
||||||
|
const raw = world.getDynamicProperty(PROP_KEY);
|
||||||
|
if (raw && typeof raw === "string") {
|
||||||
|
const parsed = JSON.parse(raw);
|
||||||
|
state = {
|
||||||
|
mailboxes: parsed.mailboxes || {},
|
||||||
|
registry: parsed.registry || {},
|
||||||
|
pending: parsed.pending || {},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
world.sendMessage(`§c[Postal] Failed to load state: ${e.message}`);
|
||||||
|
state = { mailboxes: {}, registry: {}, pending: {} };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function saveState() {
|
||||||
|
try {
|
||||||
|
world.setDynamicProperty(PROP_KEY, JSON.stringify(state));
|
||||||
|
} catch (e) {
|
||||||
|
world.sendMessage(`§c[Postal] Failed to save state: ${e.message}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function keyOf(loc, dimensionId) {
|
||||||
|
return `${Math.floor(loc.x)},${Math.floor(loc.y)},${Math.floor(loc.z)},${dimensionId}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getMailboxOwner(loc, dimensionId) {
|
||||||
|
return state.mailboxes[keyOf(loc, dimensionId)] || null;
|
||||||
|
}
|
||||||
|
|
||||||
|
function claimMailbox(loc, dimensionId, player) {
|
||||||
|
const k = keyOf(loc, dimensionId);
|
||||||
|
state.mailboxes[k] = { ownerId: player.id, ownerName: player.name };
|
||||||
|
state.registry[player.id] = {
|
||||||
|
name: player.name,
|
||||||
|
x: Math.floor(loc.x),
|
||||||
|
y: Math.floor(loc.y),
|
||||||
|
z: Math.floor(loc.z),
|
||||||
|
dim: dimensionId,
|
||||||
|
};
|
||||||
|
saveState();
|
||||||
|
}
|
||||||
|
|
||||||
|
function releaseMailbox(loc, dimensionId) {
|
||||||
|
const k = keyOf(loc, dimensionId);
|
||||||
|
const entry = state.mailboxes[k];
|
||||||
|
delete state.mailboxes[k];
|
||||||
|
if (entry && state.registry[entry.ownerId]) {
|
||||||
|
const reg = state.registry[entry.ownerId];
|
||||||
|
if (reg.x === Math.floor(loc.x) && reg.y === Math.floor(loc.y) && reg.z === Math.floor(loc.z) && reg.dim === dimensionId) {
|
||||||
|
delete state.registry[entry.ownerId];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
saveState();
|
||||||
|
}
|
||||||
|
|
||||||
|
function hasClaim(playerId) {
|
||||||
|
return !!state.registry[playerId];
|
||||||
|
}
|
||||||
|
|
||||||
|
// ─── Chest facing from player rotation ──────────────────────
|
||||||
|
function chestFacing(yaw) {
|
||||||
|
let y = yaw;
|
||||||
|
while (y > 180) y -= 360;
|
||||||
|
while (y < -180) y += 360;
|
||||||
|
if (y >= -45 && y < 45) return "north";
|
||||||
|
if (y >= 45 && y < 135) return "east";
|
||||||
|
if (y >= -135 && y < -45) return "west";
|
||||||
|
return "south";
|
||||||
|
}
|
||||||
|
|
||||||
|
// ─── Mailbox placement ──────────────────────────────────────
|
||||||
|
world.afterEvents.playerPlaceBlock.subscribe((event) => {
|
||||||
|
const block = event.block;
|
||||||
|
if (block.typeId !== MAILBOX_BLOCK) return;
|
||||||
|
|
||||||
|
const player = event.player;
|
||||||
|
const loc = block.location;
|
||||||
|
const dim = block.dimension;
|
||||||
|
|
||||||
|
if (hasClaim(player.id)) {
|
||||||
|
// Revert placement and refund the item
|
||||||
|
try {
|
||||||
|
dim.runCommand(`setblock ${loc.x} ${loc.y} ${loc.z} air`);
|
||||||
|
dim.spawnItem(new ItemStack(MAILBOX_BLOCK, 1), {
|
||||||
|
x: loc.x + 0.5,
|
||||||
|
y: loc.y + 0.5,
|
||||||
|
z: loc.z + 0.5,
|
||||||
|
});
|
||||||
|
} catch (_) {}
|
||||||
|
player.sendMessage(`§c[Postal] §7You already have a mailbox in this world.`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const facing = chestFacing(player.getRotation().y);
|
||||||
|
try {
|
||||||
|
dim.runCommand(
|
||||||
|
`setblock ${loc.x} ${loc.y} ${loc.z} chest ["minecraft:cardinal_direction":"${facing}"]`
|
||||||
|
);
|
||||||
|
} catch (_) {
|
||||||
|
try {
|
||||||
|
dim.runCommand(`setblock ${loc.x} ${loc.y} ${loc.z} chest`);
|
||||||
|
} catch (_) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
claimMailbox(loc, dim.id, player);
|
||||||
|
player.sendMessage(`§6[Postal] §7Mailbox locked to you. Only you can open or break it.`);
|
||||||
|
});
|
||||||
|
|
||||||
|
// ─── Interact: gate mailbox opening for non-owners ──────────
|
||||||
|
world.beforeEvents.playerInteractWithBlock.subscribe((event) => {
|
||||||
|
const block = event.block;
|
||||||
|
if (!block) return;
|
||||||
|
|
||||||
|
// Post-office block: open send UI (cancel default interact)
|
||||||
|
if (block.typeId === POST_OFFICE_BLOCK) {
|
||||||
|
event.cancel = true;
|
||||||
|
const player = event.player;
|
||||||
|
system.run(() => openSendForm(player));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Vanilla chest: gate if it's a claimed mailbox
|
||||||
|
if (block.typeId !== VANILLA_CHEST) return;
|
||||||
|
const owner = getMailboxOwner(block.location, block.dimension.id);
|
||||||
|
if (!owner) return;
|
||||||
|
if (owner.ownerId === event.player.id) return;
|
||||||
|
event.cancel = true;
|
||||||
|
const playerRef = event.player;
|
||||||
|
system.run(() =>
|
||||||
|
playerRef.sendMessage(`§c[Postal] §7This mailbox belongs to §f${owner.ownerName}§7.`)
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
// ─── Break: protect mailboxes; owner break returns custom item ──
|
||||||
|
world.beforeEvents.playerBreakBlock.subscribe((event) => {
|
||||||
|
const block = event.block;
|
||||||
|
if (block.typeId !== VANILLA_CHEST) return;
|
||||||
|
const owner = getMailboxOwner(block.location, block.dimension.id);
|
||||||
|
if (!owner) return;
|
||||||
|
|
||||||
|
const player = event.player;
|
||||||
|
|
||||||
|
if (owner.ownerId !== player.id) {
|
||||||
|
event.cancel = true;
|
||||||
|
system.run(() =>
|
||||||
|
player.sendMessage(`§c[Postal] §7This mailbox belongs to §f${owner.ownerName}§7. You can't break it.`)
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
event.cancel = true;
|
||||||
|
const loc = { x: block.location.x, y: block.location.y, z: block.location.z };
|
||||||
|
const dim = block.dimension;
|
||||||
|
|
||||||
|
system.run(() => {
|
||||||
|
try {
|
||||||
|
const inv = dim.getBlock(loc)?.getComponent("inventory");
|
||||||
|
const container = inv?.container;
|
||||||
|
const dropPos = { x: loc.x + 0.5, y: loc.y + 0.5, z: loc.z + 0.5 };
|
||||||
|
if (container) {
|
||||||
|
for (let i = 0; i < container.size; i++) {
|
||||||
|
const item = container.getItem(i);
|
||||||
|
if (item) {
|
||||||
|
dim.spawnItem(item, dropPos);
|
||||||
|
container.setItem(i, undefined);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
dim.spawnItem(new ItemStack(MAILBOX_BLOCK, 1), dropPos);
|
||||||
|
dim.runCommand(`setblock ${loc.x} ${loc.y} ${loc.z} air`);
|
||||||
|
} catch (e) {
|
||||||
|
player.sendMessage(`§c[Postal] Error during break: ${e.message}`);
|
||||||
|
}
|
||||||
|
releaseMailbox(loc, dim.id);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// ─── Post Office: send form ─────────────────────────────────
|
||||||
|
function getHeldItem(player) {
|
||||||
|
try {
|
||||||
|
const inv = player.getComponent("inventory")?.container;
|
||||||
|
if (!inv) return { item: undefined, slot: -1 };
|
||||||
|
const slot = player.selectedSlotIndex;
|
||||||
|
const item = inv.getItem(slot);
|
||||||
|
return { item, slot, inv };
|
||||||
|
} catch (_) {
|
||||||
|
return { item: undefined, slot: -1 };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function itemSummary(item) {
|
||||||
|
const niceName = item.typeId.replace(/^minecraft:/, "").replace(/_/g, " ");
|
||||||
|
return `${item.amount} × ${niceName}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function openSendForm(player) {
|
||||||
|
const { item, slot, inv } = getHeldItem(player);
|
||||||
|
if (!item) {
|
||||||
|
player.sendMessage(`§c[Postal] §7Hold the item you want to send in your hotbar first.`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const candidates = [];
|
||||||
|
for (const [ownerId, reg] of Object.entries(state.registry)) {
|
||||||
|
if (ownerId === player.id) continue;
|
||||||
|
candidates.push({ ownerId, reg });
|
||||||
|
}
|
||||||
|
|
||||||
|
if (candidates.length === 0) {
|
||||||
|
player.sendMessage(`§c[Postal] §7No other players have claimed a mailbox yet.`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const form = new ActionFormData()
|
||||||
|
.title("Post Office")
|
||||||
|
.body(`Sending §f${itemSummary(item)}§r\n\nChoose a recipient:`);
|
||||||
|
for (const c of candidates) form.button(c.reg.name);
|
||||||
|
form.button("§cCancel");
|
||||||
|
|
||||||
|
let response;
|
||||||
|
try {
|
||||||
|
response = await form.show(player);
|
||||||
|
} catch (_) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (response.canceled || response.selection === undefined) return;
|
||||||
|
if (response.selection >= candidates.length) return; // cancel button
|
||||||
|
|
||||||
|
const chosen = candidates[response.selection];
|
||||||
|
|
||||||
|
const confirm = new MessageFormData()
|
||||||
|
.title("Confirm Send")
|
||||||
|
.body(`Send §f${itemSummary(item)}§r\nto §f${chosen.reg.name}§r?`)
|
||||||
|
.button1("Send")
|
||||||
|
.button2("Cancel");
|
||||||
|
|
||||||
|
let conf;
|
||||||
|
try {
|
||||||
|
conf = await confirm.show(player);
|
||||||
|
} catch (_) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (conf.canceled || conf.selection !== 0) return;
|
||||||
|
|
||||||
|
// Re-fetch held item in case it changed while form was open
|
||||||
|
const fresh = getHeldItem(player);
|
||||||
|
if (!fresh.item || fresh.item.typeId !== item.typeId || fresh.item.amount !== item.amount) {
|
||||||
|
player.sendMessage(`§c[Postal] §7Held item changed — send cancelled.`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
deliver(player, chosen.ownerId, chosen.reg, fresh.item, fresh.slot, fresh.inv);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ─── Delivery ───────────────────────────────────────────────
|
||||||
|
function deliver(senderPlayer, recipientId, reg, itemStack, slot, inv, retry = 0) {
|
||||||
|
const dim = world.getDimension(reg.dim);
|
||||||
|
const loc = { x: reg.x, y: reg.y, z: reg.z };
|
||||||
|
let block;
|
||||||
|
try {
|
||||||
|
block = dim.getBlock(loc);
|
||||||
|
} catch (_) {
|
||||||
|
block = undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!block) {
|
||||||
|
if (retry >= 1) {
|
||||||
|
senderPlayer.sendMessage(
|
||||||
|
`§c[Postal] §7Couldn't reach §f${reg.name}§7's mailbox (chunk not loaded). Try again later.`
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// Force chunk load briefly, then retry
|
||||||
|
const taName = `postal_tmp_${recipientId}`.replace(/[^a-zA-Z0-9_]/g, "_").slice(0, 20);
|
||||||
|
try {
|
||||||
|
dim.runCommand(
|
||||||
|
`tickingarea add ${reg.x} ${reg.y} ${reg.z} ${reg.x} ${reg.y} ${reg.z} ${taName}`
|
||||||
|
);
|
||||||
|
} catch (_) {}
|
||||||
|
system.runTimeout(() => {
|
||||||
|
deliver(senderPlayer, recipientId, reg, itemStack, slot, inv, retry + 1);
|
||||||
|
try {
|
||||||
|
dim.runCommand(`tickingarea remove ${taName}`);
|
||||||
|
} catch (_) {}
|
||||||
|
}, 20);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (block.typeId !== VANILLA_CHEST) {
|
||||||
|
// Mailbox was destroyed externally — clean up registry
|
||||||
|
releaseMailbox(loc, reg.dim);
|
||||||
|
senderPlayer.sendMessage(
|
||||||
|
`§c[Postal] §f${reg.name}§7's mailbox is missing. Send cancelled.`
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const owner = getMailboxOwner(loc, reg.dim);
|
||||||
|
if (!owner || owner.ownerId !== recipientId) {
|
||||||
|
releaseMailbox(loc, reg.dim);
|
||||||
|
senderPlayer.sendMessage(
|
||||||
|
`§c[Postal] §f${reg.name}§7's mailbox is no longer claimed. Send cancelled.`
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const container = block.getComponent("inventory")?.container;
|
||||||
|
if (!container) {
|
||||||
|
senderPlayer.sendMessage(`§c[Postal] §7Couldn't access the mailbox contents.`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const leftover = container.addItem(itemStack);
|
||||||
|
|
||||||
|
// Remove the full stack from sender's inventory first
|
||||||
|
try {
|
||||||
|
inv.setItem(slot, undefined);
|
||||||
|
} catch (_) {}
|
||||||
|
|
||||||
|
// Refund leftover (mailbox full) back to sender
|
||||||
|
if (leftover) {
|
||||||
|
const p = senderPlayer.location;
|
||||||
|
try {
|
||||||
|
senderPlayer.dimension.spawnItem(leftover, p);
|
||||||
|
} catch (_) {}
|
||||||
|
senderPlayer.sendMessage(
|
||||||
|
`§c[Postal] §f${reg.name}§7's mailbox is full — partial delivery. Leftover dropped at your feet.`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const summary = itemSummary(itemStack);
|
||||||
|
senderPlayer.sendMessage(`§6[Postal] §7Sent §f${summary}§7 to §f${reg.name}§7.`);
|
||||||
|
|
||||||
|
// Notify recipient
|
||||||
|
const onlineRecipient = world.getAllPlayers().find((p) => p.id === recipientId);
|
||||||
|
if (onlineRecipient) {
|
||||||
|
onlineRecipient.sendMessage(`§6[Postal] §7You've got mail from §f${senderPlayer.name}§7! (${summary})`);
|
||||||
|
} else {
|
||||||
|
if (!state.pending[recipientId]) state.pending[recipientId] = [];
|
||||||
|
state.pending[recipientId].push({
|
||||||
|
from: senderPlayer.name,
|
||||||
|
itemSummary: summary,
|
||||||
|
ts: Date.now(),
|
||||||
|
});
|
||||||
|
saveState();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ─── Flush pending notifications on login ───────────────────
|
||||||
|
world.afterEvents.playerSpawn.subscribe((event) => {
|
||||||
|
if (!event.initialSpawn) return;
|
||||||
|
const player = event.player;
|
||||||
|
const queue = state.pending[player.id];
|
||||||
|
if (!queue || queue.length === 0) return;
|
||||||
|
|
||||||
|
system.runTimeout(() => {
|
||||||
|
player.sendMessage(`§6[Postal] §7You have §f${queue.length}§7 new mail notification(s):`);
|
||||||
|
for (const n of queue) {
|
||||||
|
const when = new Date(n.ts).toISOString().replace("T", " ").slice(0, 16);
|
||||||
|
player.sendMessage(`§7[${when}] §7from §f${n.from}§7 — ${n.itemSummary}`);
|
||||||
|
}
|
||||||
|
delete state.pending[player.id];
|
||||||
|
saveState();
|
||||||
|
}, 40);
|
||||||
|
});
|
||||||
|
|
||||||
|
// ─── Boot ──────────────────────────────────────────────────
|
||||||
|
system.run(() => {
|
||||||
|
loadState();
|
||||||
|
world.sendMessage("§6[Postal] §7Postal service loaded.");
|
||||||
|
});
|
||||||
5
postal-service-addon/postal_service_RP/blocks.json
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
{
|
||||||
|
"format_version": [1, 1, 0],
|
||||||
|
"silverlabs:mailbox": { "sound": "stone" },
|
||||||
|
"silverlabs:post_office": { "sound": "wood" }
|
||||||
|
}
|
||||||
17
postal-service-addon/postal_service_RP/manifest.json
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
{
|
||||||
|
"format_version": 2,
|
||||||
|
"header": {
|
||||||
|
"name": "Postal Service Resources",
|
||||||
|
"description": "Textures and lang for silverlabs:mailbox and silverlabs:post_office",
|
||||||
|
"uuid": "d7a4b9c1-2e3f-4a5b-8c6d-1f2e3d4c5b63",
|
||||||
|
"version": [1, 0, 0],
|
||||||
|
"min_engine_version": [1, 21, 0]
|
||||||
|
},
|
||||||
|
"modules": [
|
||||||
|
{
|
||||||
|
"type": "resources",
|
||||||
|
"uuid": "d7a4b9c1-2e3f-4a5b-8c6d-1f2e3d4c5b64",
|
||||||
|
"version": [1, 0, 0]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
BIN
postal-service-addon/postal_service_RP/pack_icon.png
Normal file
|
After Width: | Height: | Size: 971 B |
2
postal-service-addon/postal_service_RP/texts/en_US.lang
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
tile.silverlabs:mailbox.name=Mailbox
|
||||||
|
tile.silverlabs:post_office.name=Post Office
|
||||||
|
After Width: | Height: | Size: 317 B |
|
After Width: | Height: | Size: 317 B |
@@ -0,0 +1,14 @@
|
|||||||
|
{
|
||||||
|
"resource_pack_name": "postal_service_RP",
|
||||||
|
"texture_name": "atlas.terrain",
|
||||||
|
"padding": 8,
|
||||||
|
"num_mip_levels": 4,
|
||||||
|
"texture_data": {
|
||||||
|
"mailbox": {
|
||||||
|
"textures": "textures/blocks/mailbox"
|
||||||
|
},
|
||||||
|
"post_office": {
|
||||||
|
"textures": "textures/blocks/post_office"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
{
|
||||||
|
"format_version": "1.21.0",
|
||||||
|
"minecraft:block": {
|
||||||
|
"description": {
|
||||||
|
"identifier": "silverlabs:private_chest",
|
||||||
|
"menu_category": {
|
||||||
|
"category": "items",
|
||||||
|
"group": "itemGroup.name.chest"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"components": {
|
||||||
|
"minecraft:destructible_by_mining": {
|
||||||
|
"seconds_to_destroy": 2.5
|
||||||
|
},
|
||||||
|
"minecraft:destructible_by_explosion": {
|
||||||
|
"explosion_resistance": 1200.0
|
||||||
|
},
|
||||||
|
"minecraft:map_color": "#B0B0B0",
|
||||||
|
"minecraft:material_instances": {
|
||||||
|
"*": {
|
||||||
|
"texture": "private_chest",
|
||||||
|
"render_method": "opaque"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
34
private-chest-addon/private_chest_BP/manifest.json
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
{
|
||||||
|
"format_version": 2,
|
||||||
|
"header": {
|
||||||
|
"name": "Private Chests",
|
||||||
|
"description": "Owner-locked chests — only the placer can open or break them",
|
||||||
|
"uuid": "9a3f8d2e-7c5b-4e1a-b9d2-1f6e3c4a8b50",
|
||||||
|
"version": [1, 0, 0],
|
||||||
|
"min_engine_version": [1, 21, 0]
|
||||||
|
},
|
||||||
|
"modules": [
|
||||||
|
{
|
||||||
|
"type": "data",
|
||||||
|
"uuid": "9a3f8d2e-7c5b-4e1a-b9d2-1f6e3c4a8b51",
|
||||||
|
"version": [1, 0, 0]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "script",
|
||||||
|
"language": "javascript",
|
||||||
|
"uuid": "9a3f8d2e-7c5b-4e1a-b9d2-1f6e3c4a8b52",
|
||||||
|
"version": [1, 0, 0],
|
||||||
|
"entry": "scripts/main.js"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"dependencies": [
|
||||||
|
{
|
||||||
|
"module_name": "@minecraft/server",
|
||||||
|
"version": "1.17.0"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"uuid": "9a3f8d2e-7c5b-4e1a-b9d2-1f6e3c4a8b53",
|
||||||
|
"version": [1, 0, 1]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
{
|
||||||
|
"format_version": "1.21.0",
|
||||||
|
"minecraft:recipe_shapeless": {
|
||||||
|
"description": {
|
||||||
|
"identifier": "silverlabs:private_chest_recipe"
|
||||||
|
},
|
||||||
|
"tags": ["crafting_table"],
|
||||||
|
"unlock": { "context": "AlwaysUnlocked" },
|
||||||
|
"ingredients": [
|
||||||
|
{ "item": "minecraft:chest" },
|
||||||
|
{ "item": "minecraft:iron_ingot" }
|
||||||
|
],
|
||||||
|
"result": {
|
||||||
|
"item": "silverlabs:private_chest",
|
||||||
|
"count": 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
158
private-chest-addon/private_chest_BP/scripts/main.js
Normal file
@@ -0,0 +1,158 @@
|
|||||||
|
import { world, system, ItemStack } from "@minecraft/server";
|
||||||
|
|
||||||
|
// ─── Constants ──────────────────────────────────────────────
|
||||||
|
const PRIVATE_BLOCK = "silverlabs:private_chest";
|
||||||
|
const VANILLA_CHEST = "minecraft:chest";
|
||||||
|
const PROP_KEY = "private_chests_v1";
|
||||||
|
|
||||||
|
// ─── State ──────────────────────────────────────────────────
|
||||||
|
// In-memory mirror of dynamic property: { "x,y,z,dim": { ownerId, ownerName } }
|
||||||
|
let chests = {};
|
||||||
|
|
||||||
|
function loadState() {
|
||||||
|
try {
|
||||||
|
const raw = world.getDynamicProperty(PROP_KEY);
|
||||||
|
if (raw && typeof raw === "string") {
|
||||||
|
chests = JSON.parse(raw);
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
world.sendMessage(`§c[Private Chest] Failed to load state: ${e.message}`);
|
||||||
|
chests = {};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function saveState() {
|
||||||
|
try {
|
||||||
|
world.setDynamicProperty(PROP_KEY, JSON.stringify(chests));
|
||||||
|
} catch (e) {
|
||||||
|
world.sendMessage(`§c[Private Chest] Failed to save state: ${e.message}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function keyOf(loc, dimensionId) {
|
||||||
|
return `${Math.floor(loc.x)},${Math.floor(loc.y)},${Math.floor(loc.z)},${dimensionId}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
function claim(loc, dimensionId, player) {
|
||||||
|
chests[keyOf(loc, dimensionId)] = { ownerId: player.id, ownerName: player.name };
|
||||||
|
saveState();
|
||||||
|
}
|
||||||
|
|
||||||
|
function release(loc, dimensionId) {
|
||||||
|
delete chests[keyOf(loc, dimensionId)];
|
||||||
|
saveState();
|
||||||
|
}
|
||||||
|
|
||||||
|
function getOwner(loc, dimensionId) {
|
||||||
|
return chests[keyOf(loc, dimensionId)] || null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ─── Chest facing from player rotation ──────────────────────
|
||||||
|
// Bedrock yaw: 0=south, 90=west, 180=north, -90=east. Chest front faces the player.
|
||||||
|
function chestFacing(yaw) {
|
||||||
|
let y = yaw;
|
||||||
|
while (y > 180) y -= 360;
|
||||||
|
while (y < -180) y += 360;
|
||||||
|
if (y >= -45 && y < 45) return "north"; // player facing south
|
||||||
|
if (y >= 45 && y < 135) return "east"; // player facing west
|
||||||
|
if (y >= -135 && y < -45) return "west"; // player facing east
|
||||||
|
return "south"; // player facing north
|
||||||
|
}
|
||||||
|
|
||||||
|
// ─── Placement: swap custom block → vanilla chest + claim ──
|
||||||
|
world.afterEvents.playerPlaceBlock.subscribe((event) => {
|
||||||
|
const block = event.block;
|
||||||
|
if (block.typeId !== PRIVATE_BLOCK) return;
|
||||||
|
|
||||||
|
const player = event.player;
|
||||||
|
const loc = block.location;
|
||||||
|
const dim = block.dimension;
|
||||||
|
const facing = chestFacing(player.getRotation().y);
|
||||||
|
|
||||||
|
try {
|
||||||
|
dim.runCommand(
|
||||||
|
`setblock ${loc.x} ${loc.y} ${loc.z} chest ["minecraft:cardinal_direction":"${facing}"]`
|
||||||
|
);
|
||||||
|
} catch (e) {
|
||||||
|
// Older block-state syntax fallback (rare)
|
||||||
|
try {
|
||||||
|
dim.runCommand(`setblock ${loc.x} ${loc.y} ${loc.z} chest`);
|
||||||
|
} catch (_) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
claim(loc, dim.id, player);
|
||||||
|
player.sendMessage(`§6[Private Chest] §7Locked to you. Only you can open or break it.`);
|
||||||
|
});
|
||||||
|
|
||||||
|
// ─── Interact: block opening for non-owners ─────────────────
|
||||||
|
try {
|
||||||
|
world.beforeEvents.playerInteractWithBlock.subscribe((event) => {
|
||||||
|
const block = event.block;
|
||||||
|
if (block.typeId !== VANILLA_CHEST) return;
|
||||||
|
const owner = getOwner(block.location, block.dimension.id);
|
||||||
|
if (!owner) return;
|
||||||
|
if (owner.ownerId === event.player.id) return;
|
||||||
|
event.cancel = true;
|
||||||
|
const playerRef = event.player;
|
||||||
|
system.run(() =>
|
||||||
|
playerRef.sendMessage(`§c[Private Chest] §7This chest belongs to §f${owner.ownerName}§7.`)
|
||||||
|
);
|
||||||
|
});
|
||||||
|
} catch (e) {
|
||||||
|
console.warn(`[Private Chest] beforeEvents.playerInteractWithBlock unavailable: ${e}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ─── Break: protect for non-owners; owner break drops the custom item back ──
|
||||||
|
try {
|
||||||
|
world.beforeEvents.playerBreakBlock.subscribe((event) => {
|
||||||
|
const block = event.block;
|
||||||
|
if (block.typeId !== VANILLA_CHEST) return;
|
||||||
|
const owner = getOwner(block.location, block.dimension.id);
|
||||||
|
if (!owner) return;
|
||||||
|
|
||||||
|
const player = event.player;
|
||||||
|
|
||||||
|
if (owner.ownerId !== player.id) {
|
||||||
|
event.cancel = true;
|
||||||
|
system.run(() =>
|
||||||
|
player.sendMessage(`§c[Private Chest] §7This chest belongs to §f${owner.ownerName}§7. You can't break it.`)
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Owner breaking — cancel default drop, manually eject contents + return the custom item.
|
||||||
|
event.cancel = true;
|
||||||
|
const loc = { x: block.location.x, y: block.location.y, z: block.location.z };
|
||||||
|
const dim = block.dimension;
|
||||||
|
|
||||||
|
system.run(() => {
|
||||||
|
try {
|
||||||
|
const inv = dim.getBlock(loc)?.getComponent("inventory");
|
||||||
|
const container = inv?.container;
|
||||||
|
const dropPos = { x: loc.x + 0.5, y: loc.y + 0.5, z: loc.z + 0.5 };
|
||||||
|
if (container) {
|
||||||
|
for (let i = 0; i < container.size; i++) {
|
||||||
|
const item = container.getItem(i);
|
||||||
|
if (item) {
|
||||||
|
dim.spawnItem(item, dropPos);
|
||||||
|
container.setItem(i, undefined);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
dim.spawnItem(new ItemStack(PRIVATE_BLOCK, 1), dropPos);
|
||||||
|
dim.runCommand(`setblock ${loc.x} ${loc.y} ${loc.z} air`);
|
||||||
|
} catch (e) {
|
||||||
|
player.sendMessage(`§c[Private Chest] Error during break: ${e.message}`);
|
||||||
|
}
|
||||||
|
release(loc, dim.id);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
} catch (e) {
|
||||||
|
console.warn(`[Private Chest] beforeEvents.playerBreakBlock unavailable: ${e}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ─── Boot ──────────────────────────────────────────────────
|
||||||
|
system.run(() => {
|
||||||
|
loadState();
|
||||||
|
world.sendMessage("§6[Private Chest] §7Owner-locked chest system loaded.");
|
||||||
|
});
|
||||||
4
private-chest-addon/private_chest_RP/blocks.json
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
{
|
||||||
|
"format_version": [1, 1, 0],
|
||||||
|
"silverlabs:private_chest": { "sound": "stone" }
|
||||||
|
}
|
||||||
17
private-chest-addon/private_chest_RP/manifest.json
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
{
|
||||||
|
"format_version": 2,
|
||||||
|
"header": {
|
||||||
|
"name": "Private Chests Resources",
|
||||||
|
"description": "Textures and lang for the silverlabs:private_chest block",
|
||||||
|
"uuid": "9a3f8d2e-7c5b-4e1a-b9d2-1f6e3c4a8b53",
|
||||||
|
"version": [1, 0, 0],
|
||||||
|
"min_engine_version": [1, 21, 0]
|
||||||
|
},
|
||||||
|
"modules": [
|
||||||
|
{
|
||||||
|
"type": "resources",
|
||||||
|
"uuid": "9a3f8d2e-7c5b-4e1a-b9d2-1f6e3c4a8b54",
|
||||||
|
"version": [1, 0, 1]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
BIN
private-chest-addon/private_chest_RP/pack_icon.png
Normal file
|
After Width: | Height: | Size: 971 B |
1
private-chest-addon/private_chest_RP/texts/en_US.lang
Normal file
@@ -0,0 +1 @@
|
|||||||
|
tile.silverlabs:private_chest.name=Private Chest
|
||||||
|
After Width: | Height: | Size: 317 B |
@@ -0,0 +1,11 @@
|
|||||||
|
{
|
||||||
|
"resource_pack_name": "private_chest_RP",
|
||||||
|
"texture_name": "atlas.terrain",
|
||||||
|
"padding": 8,
|
||||||
|
"num_mip_levels": 4,
|
||||||
|
"texture_data": {
|
||||||
|
"private_chest": {
|
||||||
|
"textures": "textures/blocks/private_chest"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,6 +1,14 @@
|
|||||||
{
|
{
|
||||||
"behavior_packs": [
|
"behavior_packs": [
|
||||||
{"pack_id": "c7e91f32-4a8b-4d6e-b21c-9f3a05d8e047", "version": [1, 0, 0]}
|
{"pack_id": "c7e91f32-4a8b-4d6e-b21c-9f3a05d8e047", "version": [1, 0, 0]},
|
||||||
|
{"pack_id": "9a3f8d2e-7c5b-4e1a-b9d2-1f6e3c4a8b50", "version": [1, 0, 0]},
|
||||||
|
{"pack_id": "c8e51d72-9a4f-4b3e-b8c1-2f7d3e6a4b80", "version": [1, 0, 0]},
|
||||||
|
{"pack_id": "b4d72e91-8c43-4f1a-95d2-7e3b6c8a0d10", "version": [1, 0, 0]},
|
||||||
|
{"pack_id": "d7a4b9c1-2e3f-4a5b-8c6d-1f2e3d4c5b60", "version": [1, 0, 0]}
|
||||||
],
|
],
|
||||||
"resource_packs": []
|
"resource_packs": [
|
||||||
|
{"pack_id": "9a3f8d2e-7c5b-4e1a-b9d2-1f6e3c4a8b53", "version": [1, 0, 1]},
|
||||||
|
{"pack_id": "c8e51d72-9a4f-4b3e-b8c1-2f7d3e6a4b83", "version": [1, 0, 1]},
|
||||||
|
{"pack_id": "d7a4b9c1-2e3f-4a5b-8c6d-1f2e3d4c5b63", "version": [1, 0, 0]}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
|
|||||||