Christian Loffel / Bedrock Portfolio
Mission Take Off  -  Cartogo and G-Square on runway
Case Study · Cargo Transport System
Cartogo
Cargo
System
Ground loading, aerial drops, mid-air vehicle entry - built entirely within Bedrock's JSON constraints. Cartogo G-Square 2 Cargo Slots
00 - The Problem

What Bedrock Can't Do

No vehicle bays, no cross-entity property sharing, no mid-air spawn APIs, no async callbacks. Every mechanic required a custom workaround.

No entity-in-entity vehicle slots
Frozen Loader entity as positional anchor + rider hijacking
A permanently frozen entity spawns at ~29.75 blocks behind Cartogo. Bedrock's rider system teleports G-Square to that position via teleport_rider - no custom physics, no tick-precise tracking.
No cross-entity property sharing
G-Square state serialized into Cartogo Dynamic Properties per slot
On load: crashLives, variant, fuel, fuelMax read from G-Square -> stored in c17_loaded_g_state_slot_1/2. On unload: fully restored to new G-Square instance. Legacy fuel fallback keys supported.
No mid-air spawn with timed player entry
jump_to_g_ready property + 40-tick window + attack sensor gate
After aerial drop, a 40-tick jump window opens. A second attack on Cartogo tags the player (c17_jump_request). 1-tick intentional delay ensures the tag lands before mount logic runs.
No persistent entity state between sessions
Spawn Egg with human-readable + script-parseable item lore
On despawn: full Cartogo state encoded into item lore - readable for the player, parsed by script on respawn. Each loaded G-Square also refunded as its own egg. The item is both a save file and inventory UI.
01 - In Action

Visuals

Cartogo and G-Square promo art
MISSION: TAKE OFF! - Cartogo (C17 Globemaster) + G-Square on runway at sunset · Promotional Art Key Art
VIDEO GIF / Video G-Square drives onto Loader -> loading animation -> cargo bay closes
Ground Loading
Loader UI · 3-Button form · slot animation
VIDEO GIF / Video Bay door opens mid-flight -> G-Square falls with parachute
Midair Drop
slow_falling · parachute sound · actionbar countdown
VIDEO GIF / Video Player attacks Cartogo mid-flight -> jumps into falling G-Square
Jump to G-Square
40-tick window · forced rider mount · full state intact
IMAGE Screenshot Loader 3-Button UI: Load / Unload / Despawn
Loader UI
Form UI · busy state feedback · slot count
IMAGE Screenshot Spawn Egg item lore: Fuel % · Condition · Cargo count
Spawn Egg State Save
Human-readable lore · parsed on respawn · full restore
02 - Event Flow

Interactive Event Chain

Click any step to expand. Three flows share the same attack input but route differently based on entity state properties.

Full System Flow · Cartogo + G-Square
01Player attacks Cartogo - on groundPlayer
0-damage attack sensor fires. Attack controller checks q.is_on_ground && !q.property('aria:ground_mode') -> routes to ground_mode_on. Second ground attack while mode is active toggles it off and despawns Loader.
"ground_mode_on": "q.property('aria:player_attack') == 1 && q.is_on_ground && !q.property('aria:ground_mode')"
02scriptevent -> Loader spawns behind CartogoLoader
Script spawns Loader at ~29.75 block offset behind Cartogo facing direction. Loader is permanently frozen. Bidirectional ID linking: c17_loader_id on Cartogo, owner_c17_id on Loader. Orphan cleanup runs every 100 ticks across all dimensions.
c17_loader_id -> Cartogo dynamic property owner_c17_id -> Loader dynamic property // Orphan sweep: every 100 ticks, all dimensions
03G-Square approaches - Player opens Loader UIPlayer
Player interacts with Loader -> 3-button form UI: Load / Unload / Despawn. Script checks: busy timer (aria:busy_until), G-Square within 3 blocks, loaded_g_count < 2 (max 2 slots).
aria:busy_until check -> reject if busy aria:loaded_g_count check -> reject if 2 slots full
04Loader mounts G-Square - teleports into bayLoader
Loader controller detects mount_trigger == 1 -> mount_g state runs the ride command. teleport_rider snaps G-Square to Loader's frozen position instantly - directly inside the Cartogo cargo bay geometry.
"/ride @e[type=aria_pp:g_class,r=3,c=1] start_riding @s teleport_rider"
05G-Square state saved -> entity removedCartogo
Script reads G-Square properties and writes to slot: crashLives, variant, fuel, fuelMax with legacy fallback keys. G-Square despawns. Cargo bay geo layer activates. loaded_g_count +1.
c17_loaded_g_state_slot_1 = {crashLives, variant, fuel, fuelMax} aria:loaded_g_count += 1 -> cargo geo activates
06Unload: animation + 58-tick delayed spawnCartogo
Unload animation plays first. After 58 ticks, new G-Square spawns at Loader with full state restored. pending_unload_token UUID prevents duplicate spawns from racing timeouts. loaded_g_count -1 only after animation ends - keeps render controller in sync.
aria:pending_unload_token // UUID - stale tokens produce no spawn setTimeout(58 ticks) -> spawn -> restore state -> count -1
03 - State Reference

Properties & Dynamic Data

PropertyEntityPurposeValues
aria:loaded_g_countCartogoNumber of G-Squares in cargo bay - gates load/unload and cargo geo0 / 1 / 2
aria:midair_unloadingCartogoLocks out second drop during ongoing animationbool
aria:jump_to_g_readyCartogoRoutes attack to jump_to_g instead of new drop - the 40-tick gate0 / 1
aria:ground_modeCartogoLoader active - enables ground load/unload UI flowbool
aria:autopilot_modeCartogoNo-rider state - prevents crash, enables clean refund sequencebool
aria:is_midair_dropG-SquareActivates parachute controller, parachute geo, blocks normal ride entrybool
aria:fuel / aria:fuel_maxG-SquareSaved/restored per slot - legacy fallback keys supportedfloat
c17_loaded_g_state_slot_1/2Cartogo dyn.Serialized G-Square state per cargo slot - JSON string in dynamic propertyJSON
aria:pending_unload_tokenCartogo dyn.UUID job token - only current holder may spawn on unload timeoutUUID string
aria:busy_untilCartogo dyn.Tick timestamp - blocks new UI actions while animation runstick number
c17_loader_id / owner_c17_idDyn. (both)Bidirectional ID link - enables orphan detection and safe cross-referenceentity ID
04 - Robustness

Edge Cases & Solutions

TIME
Double Action During Animation
aria:busy_until timestamp blocks new UI actions until animation resolves. Communicated as "loader busy" actionbar message. No silent failures.
LOCK
Timeout Race - Duplicate Spawn
aria:pending_unload_token UUID is validated before each spawn. Old timeouts carry stale tokens - they check, fail, and produce no G-Square. Exactly one spawn per unload action.
TARGET
Wrong Jump Target
Multiple aerial G-Squares could exist if two Cartogos drop simultaneously. Each drop gets unique tag c17_midair_drop_<id>. Old tags cleaned before spawn. Jump always targets exact entity.
GHOST
Orphaned Loaders
Loader can lose its Cartogo reference via dimension change or crash. Every 100 ticks, script validates owner_c17_id on all Loader entities across all dimensions - removes any without valid owner.
POWER
High-Speed Drop Glitch
At high airspeed the spawned G-Square clips geometry. Slowness effect applied to Cartogo before drop reduces speed to safe threshold. Explicitly noted in code comments as intentional.
SYNC
1-Tick Attack/Mount Ordering
Jump request uses entityHitEntity to tag player. Mount logic runs same tick. 1-tick intentional delay ensures tag is set before script reads it - prevents missed jumps under Bedrock's event ordering.
05 - Source

Real Controllers - No Pseudocode

// attack_controller_c17  -  single input entry point
// Same 0-damage attack -> different action based on state

"idle": {
  "transitions": [
    // GROUND: toggle loader
    { "ground_mode_on":  "q.property('aria:player_attack') == 1 && q.is_on_ground && !q.property('aria:ground_mode')" },
    { "ground_mode_off": "q.property('aria:player_attack') == 1 && q.is_on_ground && q.property('aria:ground_mode')" },
    // AIR: drop (no window open)
    { "midair_drop": "q.property('aria:player_attack') == 1 && !q.is_on_ground && q.property('aria:jump_to_g_ready') == 0" },
    // AIR: jump into falling G-Square (window open)
    { "jump_to_g":    "q.property('aria:player_attack') == 1 && !q.is_on_ground && q.property('aria:jump_to_g_ready') == 1" }
  ],
  "on_entry": [ "@s aria:set_property_player_attack_0" ]
},
"midair_drop": {
  "animations": ["midair_drop_logic"], // keyframe-timed spawn trigger
  "on_entry": [ "/scriptevent aria_pp:c17_midair_drop", "@s aria:set_property_player_attack_0" ],
  "transitions": [{ "idle": "q.all_animations_finished" }]
},
"jump_to_g": {
  "on_entry": [ "/scriptevent aria_pp:c17_jump_to_g", "@s aria:set_property_player_attack_0", "@s aria:set_jump_to_g_false" ],
  "transitions": [{ "idle": "1" }] // immediate
},
"spawn_loader": {
  "animations": ["delay_300ms"],
  "on_exit": [ "/scriptevent aria_pp:c17_loader_spawn" ],
  "transitions": [{ "idle": "q.all_animations_finished" }]
}
// c17_loader.main  -  the workaround distilled to its purest form
// Frozen entity. Two states. One job.

"idle": {
  "transitions": [{ "mount_g": "q.property('aria:mount_trigger') == 1" }],
  "on_entry": [ "@s aria:set_mount_trigger_0" ]
},
"mount_g": {
  "animations": ["delay_10ms"],
  "on_entry": [
    // Core workaround: Bedrock's rider system does all positioning.
    // Loader is frozen at ~29.75 block offset behind Cartogo.
    // teleport_rider snaps G-Square to that exact position instantly.
    "/ride @e[type=aria_pp:g_class,r=3,c=1] start_riding @s teleport_rider",
    "@s aria:set_mount_trigger_0"
  ],
  "transitions": [{ "idle": "q.all_animations_finished" }]
}
// No custom physics. No tick-precise tracking.
// The engine does the work. The Loader just exists in the right place.
// g_class_midair  -  only active when is_midair_drop = true
// Orthogonal to normal G-Square ride logic  -  zero interference

"default": {
  "transitions": [{ "midair_start": "query.property('aria:is_midair_drop')" }]
},
"glide": {
  "on_entry": [
    "/playsound aria_pp.parachute_open @a[r=25]",
    "/effect @s slow_falling 100 1 true"
    // amplifier 1 = very slow descent
    // 100s covers any realistic drop altitude
  ],
  "transitions": [{ "landed": "query.is_on_ground" }]
},
"landed": {
  "animations": ["midair_drop_landing"],
  "on_entry": [
    "/effect @s clear slow_falling",
    "/playsound aria_pp.parachute_open @a[r=25] ~~~ 0.4 1.8"
    // 1.8 pitch = parachute collapsing
    // is_midair_drop set false by script before this -> g_class.rider takes over
  ]
}
// c17.moving  -  full flight lifecycle (bonus)
// Shows crash-lives system and diving/stall warnings

"flying": {
  "transitions": [
    { "not_moving":    "query.is_on_ground" },
    { "diving":        "query.vertical_speed <= -40 && !q.property('aria:autopilot_mode')" },
    { "falling_check": "!q.property('aria:autopilot_mode') && query.ground_speed < 2" }
  ],
  "on_entry": [
    "/effect @s levitation 1 1 true", // 1-tick lift keeps plane airborne
    "/tag @s add aria_pp.liftoff"
  ]
},
"diving": { // vertical_speed <= -40 = nose-down
  "transitions": [
    { "transform_crash": "query.is_on_ground || query.is_in_water" },
    { "flying":          "query.is_baby && query.is_moving" } // pull-up escape
  ],
  "on_entry": [
    "/playsound aria_pp.stalling.alert @a[r=35] ~ ~ ~ 1 0.5",
    "/titleraw @a[r=5] actionbar {\"rawtext\":[{\"translate\":\"aria_pp.all_planes.diving_pull_up\"}]}"
  ]
},
"transform_crash": {
  "on_entry": [
    // Configurable: -1 life (option 1) or -3 lives (option 2)
    "/execute if score aria_pp:crash_setting aria_pp.options matches 1 run event entity @s aria:crash_sub_1",
    "/execute if score aria_pp:crash_setting aria_pp.options matches 2 run event entity @s aria:crash_sub_3"
  ],
  "transitions": [
    { "final_crash": "q.property('aria:crash_lives') == 0" },
    { "not_moving":  "q.is_on_ground && q.property('aria:crash_lives') > 0" }
  ]
},
"final_crash2": {
  "on_entry": [
    "/particle aria_pp:explosion ~ ~ ~",
    "/camerashake add @a[r=100] 0.15 0.5",
    "@s aria:add_transform_crash"
  ]
}
06 - Thinking

Design Decisions

01
Frozen Loader als Cargo Bay
Bedrock's Rider-System positioniert Entities relativ zum Mount. Der Loader spawnt gefreezed am richtigen Offset - die Engine übernimmt das gesamte Positioning. Kein Custom-Physics, kein Tick-Tracking. Bidirektionale ID-Links (c17_loader_id / owner_c17_id) ermöglichen sicheres Cross-Reference und automatisches Orphan-Cleanup.
02
0-Damage Attack als universelles Input-Signal
Bedrock hat kein "Player interacts with entity"-Event in Animation Controllern. Ein Damage Sensor mit 0 Schaden ist der sauberste Workaround: kein Knockback, kein visuelles Feedback. Dasselbe Signal führt zu völlig unterschiedlichem Verhalten je nach ground_mode und jump_to_g_ready - ein einziger Eintrittspunkt für das gesamte System.
03
Token-basiertes Timeout-Handling
Bedrock hat keine nativen Callback-Garantien zwischen Animation und Script. Tick-Timeouts können rasen. pending_unload_token als UUID löst das elegant: nur der aktuelle Job-Inhaber darf spawnen. Ältere Timeouts lesen den falschen Token - kein doppeltes Fahrzeug, kein silenter Fehler.
04
loaded_g_count erst nach Animation-Ende
Beim Unload wird der Counter bewusst nach der Animation dekrementiert. Würde der Count sofort fallen, würde der Render-Controller das Cargo-Geo vorzeitig ausblenden - visuelle Inkonsistenz während der Unload-Animation. State und Render bleiben durch diesen intentionellen Delay synchron.
Next Case Study
Structure Builder ->
<- Back to Portfolio