285 lines
13 KiB
Python
285 lines
13 KiB
Python
#!/usr/bin/env python3
|
|
"""Compile the reviewed 88-species combat catalogue; never edits worlds or historical eggs."""
|
|
import json
|
|
import re
|
|
from pathlib import Path
|
|
|
|
ROOT = Path(__file__).resolve().parents[1]
|
|
# Species: initiative, signature, learned alternative. The engine composes the
|
|
# same native interactions; these explicit rows prevent silent generic fallbacks.
|
|
ROWS = """
|
|
bat MARK MARK DECOY
|
|
cat STRIKE PUSH INTERCEPT
|
|
chicken STRIKE PUSH GLIDE
|
|
cod WATER WATER PUSH
|
|
cow STRIKE CLEANSE GUARD
|
|
donkey STRIKE FOOD PUSH
|
|
fox STRIKE PICKUP DECOY
|
|
frog STRIKE PULL RESCUE
|
|
horse CHARGE CHARGE DASH
|
|
llama SPIT SPIT SPIT
|
|
mule STRIKE DELIVERY PICKUP
|
|
pig STRIKE TRACK PUSH
|
|
rabbit STRIKE DECOY LEAP
|
|
salmon CHARGE PUSH DASH
|
|
sheep GUARD GUARD PLATFORM
|
|
skeleton ARROW ARROW ARROW
|
|
slime STRIKE REBOUND PLATFORM
|
|
spider STRIKE WEB CHARGE
|
|
squid STRIKE CLOUD PUSH
|
|
tadpole MARK TRACK GUARD
|
|
tropical_fish MARK MARK DECOY
|
|
zombie STRIKE ROOT HEAL
|
|
armadillo GUARD GUARD CHARGE
|
|
bee STRIKE MARK STRIKE
|
|
camel STRIKE LEAP GUARD
|
|
cave_spider STRIKE STRIKE WEB
|
|
copper_golem STRIKE REDSTONE GUARD
|
|
dolphin CHARGE DASH RESCUE
|
|
drowned STRIKE ROOT ANCHOR
|
|
glow_squid MARK MARK MARK
|
|
goat STRIKE CHARGE INTERCEPT
|
|
husk STRIKE CLOUD STRIKE
|
|
ocelot STRIKE CHARGE INTERCEPT
|
|
panda STRIKE CHARGE GUARD
|
|
parrot MARK DECOY MARK
|
|
pufferfish STRIKE ZONE PUSH
|
|
polar_bear STRIKE GUARD STRIKE
|
|
snow_golem SNOWBALL SNOWBALL WATER
|
|
strider STRIKE DASH RESCUE
|
|
turtle GUARD GUARD GUARD
|
|
villager GUARD GUARD FOOD
|
|
wolf STRIKE INTERCEPT CHARGE
|
|
bogged ARROW ARROW ZONE
|
|
parched ARROW ARROW ARROW
|
|
allay PICKUP DELIVERY PICKUP
|
|
axolotl STRIKE VANISH HEAL
|
|
blaze FIREBALL FIREBALL ZONE
|
|
breeze WIND WIND GUARD
|
|
camel_husk STRIKE CLOUD RESCUE
|
|
creeper STRIKE EXPLOSION EXPLOSION
|
|
endermite STRIKE SWAP CHARGE
|
|
guardian BEAM BEAM BEAM
|
|
hoglin STRIKE CHARGE GUARD
|
|
magma_cube STRIKE ZONE ZONE
|
|
mooshroom STRIKE FOOD CLOUD
|
|
nautilus WATER WATER GUARD
|
|
phantom CHARGE CHARGE GLIDE
|
|
piglin STRIKE STRIKE ARROW
|
|
pillager ARROW ARROW ARROW
|
|
silverfish STRIKE ROOT CAVITY
|
|
sulfur_cube SPIT ZONE SPIT
|
|
trader_llama SPIT SPIT RALLY
|
|
wandering_trader MARK VANISH FOOD
|
|
zombie_horse STRIKE ANCHOR PUSH
|
|
zombie_villager STRIKE HEAL HEAL
|
|
zombified_piglin STRIKE MARK INTERCEPT
|
|
stray ARROW ARROW ZONE
|
|
vindicator STRIKE STRIKE STRIKE
|
|
zoglin STRIKE CHARGE PUSH
|
|
creaking STRIKE ROOT GUARD
|
|
elder_guardian BEAM GUARD PUSH
|
|
enderman STRIKE RESCUE PORTAL
|
|
evoker FANGS FANGS FANGS
|
|
ghast FIREBALL FIREBALL FIREBALL
|
|
happy_ghast GUARD LEAP PLATFORM
|
|
iron_golem STRIKE GUARD CHARGE
|
|
piglin_brute STRIKE GUARD DECOY
|
|
ravager STRIKE CHARGE PUSH
|
|
shulker SPIT SPIT GUARD
|
|
skeleton_horse STRIKE DASH RESCUE
|
|
sniffer STRIKE ROOT TRACK
|
|
vex CHARGE CHARGE CHARGE
|
|
witch POTION POTION CLOUD
|
|
wither_skeleton STRIKE STRIKE GUARD
|
|
zombie_nautilus STRIKE RESCUE ANCHOR
|
|
ender_dragon CHARGE BREATH PUSH
|
|
warden STRIKE BEAM MARK
|
|
wither SKULL SKULL SKULL
|
|
"""
|
|
EN_NAMES = """
|
|
bat|Echolocation|Diversion
|
|
cat|Arched back|Swift paw
|
|
chicken|Great wingbeat|Soft landing
|
|
cod|Rescue bubble|Splash
|
|
cow|Emergency milk|Steady shoulder
|
|
donkey|Supply point|Back off!
|
|
fox|Snatch|Feint
|
|
frog|Tongue lash|To the rescue
|
|
horse|Breakthrough|Clear the way
|
|
llama|Stopping spit|Volley
|
|
mule|Urgent delivery|Relay
|
|
pig|Snout to the ground|Shove
|
|
rabbit|False trail|Shared leap
|
|
salmon|Against the current|Wake
|
|
sheep|Wool cocoon|Cushion
|
|
skeleton|Aimed shot|Covering fire
|
|
slime|Bounce|Springboard
|
|
spider|Taut thread|Surprise descent
|
|
squid|Ink screen|Rear jet
|
|
tadpole|Way out!|Guard bubble
|
|
tropical_fish|Colour flash|False target
|
|
zombie|Hold on|Second wind
|
|
armadillo|Rolling shield|Ricochet
|
|
bee|Pollen|Precise sting
|
|
camel|Stride|High rampart
|
|
cave_spider|Tenacious bite|Web nest
|
|
copper_golem|Trigger|Copper parry
|
|
dolphin|Evacuation wake|Rescue
|
|
drowned|Grapple|Shared anchor
|
|
glow_squid|Glowing ink|Rally signal
|
|
goat|Battering ram|Counter-charge
|
|
husk|Dust|Dry grip
|
|
ocelot|Ambush|Sidestep
|
|
panda|Roll|Steady seat
|
|
parrot|Imitation|Targeted warning
|
|
pufferfish|Prickles|Sudden inflation
|
|
polar_bear|Cover me|Paw swipe
|
|
snow_golem|Snow flurry|Cool down
|
|
strider|Burning passage|Evacuation
|
|
turtle|Forward shell|Lockdown
|
|
villager|Makeshift shelter|Distribution
|
|
wolf|Interception|Pursuit
|
|
bogged|Poison arrow|Low shot
|
|
parched|Exhausting bolt|Reserve fire
|
|
allay|Aerial delivery|Recovery
|
|
axolotl|Play dead|Convalescence
|
|
blaze|Salvo|Heat curtain
|
|
breeze|Gust|Deflection
|
|
camel_husk|Dusty crossing|Extraction
|
|
creeper|Detonation|Ambush
|
|
endermite|Swap|Detour
|
|
guardian|Focused beam|Sweep
|
|
hoglin|Gore|Barrage
|
|
magma_cube|Burning landing|Warm core
|
|
mooshroom|Shared soup|Spore veil
|
|
nautilus|Air bell|Shared shell
|
|
phantom|Dive|Rescue wing
|
|
piglin|Armed feint|Covered retreat
|
|
pillager|Covering fire|Crossfire
|
|
silverfish|Cling|Wall probe
|
|
sulfur_cube|Acid puddle|Splash
|
|
trader_llama|Guard circle|Rally
|
|
wandering_trader|Quiet escape|Emergency cache
|
|
zombie_horse|Stubborn road|Return kick
|
|
zombie_villager|Vigil|Relief
|
|
zombified_piglin|Designation|Shared retaliation
|
|
stray|Cold arrow|Frost line
|
|
vindicator|Cleave|Guard breaker
|
|
zoglin|Continuous charge|Turnaround
|
|
creaking|Rooting|Sentry
|
|
elder_guardian|Sanctuary|Recoil wave
|
|
enderman|Extraction|Passage
|
|
evoker|Jaws|Fang circle
|
|
ghast|Explosive fireball|Barrage fire
|
|
happy_ghast|Hoist|Landing
|
|
iron_golem|Interposition|Uppercut
|
|
piglin_brute|Parry and riposte|Challenge
|
|
ravager|Ram|Stomp
|
|
shulker|Rising bullet|Shell
|
|
skeleton_horse|Spectral crossing|Shore relay
|
|
sniffer|Defensive tilling|Scent
|
|
vex|Spectral breakthrough|Lightning return
|
|
witch|Precise throw|Mist
|
|
wither_skeleton|Funereal slash|Funereal guard
|
|
zombie_nautilus|Ascent|Mooring
|
|
ender_dragon|Dragon breath|Protective wings
|
|
warden|Sonic wave|Deep listening
|
|
wither|Three skulls|Reaping
|
|
"""
|
|
|
|
def compile_catalogue():
|
|
rows = {parts[0]: parts[1:] for line in ROWS.strip().splitlines() if (parts := line.split())}
|
|
names = {parts[0]: parts[1:] for line in EN_NAMES.strip().splitlines() if (parts := line.split('|'))}
|
|
source = (ROOT / 'docs/familiar-combat-design.md').read_text()
|
|
entries = {}
|
|
for line in source.splitlines():
|
|
match = re.match(r'\| (\d+) · .*?\(`([^`]+)`\)', line)
|
|
if not match:
|
|
continue
|
|
fields = [part.strip() for part in line.split('|')[1:-1]]
|
|
entries[match[2]] = fields
|
|
registry = (ROOT / 'mods/sanctuary/src/main/java/fr/koka/sanctuary/companion/CompanionType.java').read_text()
|
|
rarity = {name.lower(): group.lower() for name, group in re.findall(r'^ (\w+)\(Rarity\.(\w+),', registry, re.M)}
|
|
assert set(rows) == set(entries) == set(rarity) == set(names) and len(rows) == 88
|
|
tanks = set('cow sheep armadillo panda polar_bear turtle drowned hoglin nautilus creaking elder_guardian iron_golem piglin_brute ravager zombie_nautilus'.split())
|
|
fragile = set('bat rabbit cod tadpole tropical_fish bee parrot allay endermite silverfish vex'.split())
|
|
ranged = {'ARROW','SPIT','SNOWBALL','FIREBALL','WIND','SKULL','BEAM','FANGS','POTION'}
|
|
# Parameters are reviewable data, bounded again on the server at load time.
|
|
result = []
|
|
for species, actions in rows.items():
|
|
fields = entries[species]
|
|
role = re.search(r'\*\*(.*?)\*\*', fields[1])[1].rstrip('.')
|
|
techniques = []
|
|
for index, action in enumerate(actions):
|
|
auto = index == 0
|
|
title_fr = 'Initiative' if auto else re.search(r'\*\*(.*?)\*\*', fields[index+1])[1]
|
|
title_en = 'Initiative' if auto else names[species][index-1]
|
|
desc_fr = fields[1] if auto else fields[index+1]
|
|
desc_fr = re.sub(r'\*\*.*?\*\*\s*:?\s*', '', desc_fr).strip()
|
|
power = 1.5 if species in fragile else 2.5 if species in tanks else 2
|
|
if not auto: power *= 2
|
|
cooldown = (60 if species in tanks else 40) if auto else (360 if index == 1 else 420)
|
|
windup = (10 if action in ranged else 5) if auto else 20
|
|
if species in {'ghast','ravager','ender_dragon','warden','wither'}:
|
|
power += 1; cooldown = 80 if auto else 600; windup = 25 if auto else 40
|
|
value = dict(action=action,nameFr=title_fr,nameEn=title_en,helpFr=desc_fr,
|
|
helpEn='',cooldown=cooldown,windup=windup,range=12 if action in ranged else 8,
|
|
power=power,duration=40 if auto else 80,radius=1.5 if auto else 3,
|
|
resource='',effect='',count=1)
|
|
if action == 'STRIKE': value['range'] = 1.7; value['radius'] = 1
|
|
if action == 'ARROW': value['resource'] = 'arrow'
|
|
if action == 'SNOWBALL': value['resource'] = 'snowball'
|
|
if action == 'POTION': value['resource'] = 'splash_potion'
|
|
if action == 'HEAL': value['resource'] = 'food'; value['power'] = 4
|
|
if action == 'FOOD': value['resource'] = 'offhand'; value['count'] = 1 if species == 'donkey' else 4
|
|
if action == 'DELIVERY': value['resource'] = 'offhand'; value['count'] = 4
|
|
if action == 'CLEANSE': value['resource'] = 'milk'
|
|
if action == 'GUARD': value['power'] = 2 if auto else 6; value['duration'] = 40 if auto else 100
|
|
if action == 'GUARD' and species == 'villager': value['resource'] = 'shield'
|
|
if action == 'PLATFORM' and species == 'sheep': value['resource'] = 'wool'
|
|
if action == 'ZONE': value['power'] = 1; value['duration'] = 100
|
|
if action == 'WATER': value['power'] = 10 if auto else 30
|
|
if action in {'MARK','TRACK','DECOY'}: value['duration'] = 40 if auto else 100
|
|
if action in {'PUSH','PULL','REBOUND','WIND'}: value['power'] = .25 if auto else .7
|
|
if species in {'bee','cave_spider','pufferfish','bogged'} and action in {'STRIKE','ARROW','ZONE'}: value['effect'] = 'poison'
|
|
if species in {'husk','parched'} and not auto: value['effect'] = 'weakness'
|
|
if species == 'stray': value['effect'] = 'slowness'
|
|
if species == 'wither_skeleton' and index == 1 or species == 'wither': value['effect'] = 'wither'
|
|
if species == 'shulker': value['effect'] = 'levitation'
|
|
if species in {'blaze','magma_cube'}: value['effect'] = 'fire'
|
|
if species == 'sulfur_cube': value['effect'] = 'acid'
|
|
if species == 'ghast': value['effect'] = 'explosive'; value['windup'] += 10
|
|
if species == 'creeper': value['windup'] = 30 if index == 1 else 40; value['power'] = 6; value['radius'] = 3
|
|
if index and species in {'llama','trader_llama','blaze','pillager','snow_golem','wither'}:
|
|
value['count'] = 3 if species != 'llama' or index == 2 else 1
|
|
if index == 2 and species in {'vindicator','polar_bear','evoker','guardian','ghast'}: value['count'] = 3
|
|
if species == 'turtle' and index == 2: value['power'] = 10; value['effect'] = 'stationary'
|
|
if species == 'armadillo': value['effect'] = 'projectile'
|
|
if species == 'happy_ghast' and action == 'GUARD': value['effect'] = 'overhead'
|
|
if species == 'breeze' and action == 'GUARD': value['effect'] = 'deflect'
|
|
if species == 'cow' and action == 'GUARD' or species == 'iron_golem' and action == 'GUARD': value['effect'] = 'share_health'
|
|
if species == 'strider': value['effect'] = 'lava'
|
|
if species == 'skeleton_horse': value['effect'] = 'water'
|
|
if species == 'witch' and action == 'CLOUD': value['resource'] = 'splash_potion'; value['effect'] = 'potion'
|
|
if species == 'wandering_trader' and action == 'VANISH': value['resource'] = 'invisibility'
|
|
if species == 'axolotl' and action == 'VANISH': value['effect'] = 'play_dead'
|
|
if species == 'spider' and index == 2: value['effect'] = 'from_above'
|
|
if species == 'ocelot' and index == 1: value['effect'] = 'flank'
|
|
if species in {'endermite','vex'} and action == 'CHARGE': value['effect'] = 'phase'
|
|
if species == 'wither' and index == 2: value['effect'] = 'lifesteal'; value['count'] = 1
|
|
value['helpEn'] = f"{title_en}: {action.lower().replace('_',' ')} from the companion. Preparation {value['windup']/20:g}s; recovery {value['cooldown']/20:g}s."
|
|
techniques.append(value)
|
|
result.append(dict(schema=1,id='sanctuary:'+species,revision=1,entity='minecraft:'+species,
|
|
rarity=rarity[species],health=24 if species in tanks else 10 if species in fragile else 16,
|
|
speed=.23 if species in tanks else .34 if species in fragile else .28,
|
|
distance=7 if actions[0] in ranged else 3,initiative=techniques[0],signature=techniques[1],variant=techniques[2],
|
|
legacyWork=species,roleFr=role,roleEn=actions[0].lower().replace('_',' ')))
|
|
target=ROOT/'mods/sanctuary/src/main/resources/data/sanctuary/sanctuary_familiars/vanilla.json'
|
|
target.parent.mkdir(parents=True,exist_ok=True)
|
|
target.write_text(json.dumps(dict(schema=1,profiles=result),ensure_ascii=False,indent=2)+'\n')
|
|
print(f'{len(result)} profiles compiled')
|
|
|
|
if __name__ == '__main__':
|
|
compile_catalogue()
|