Files
vtt_work/help_scripts/generate_manifestsv2.py
T
2025-10-09 20:55:30 +02:00

183 lines
9.2 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# generate_manifests_v2.py
# Fortel Trappa — manifesty o wysokiej różnorodności (HOLD I/II/III) w dwóch ZIP-ach.
# Uruchom: python generate_manifests_v2.py
# Wynik: FORTEL_MANIFESTS_v2_FULL.zip oraz FORTEL_MANIFESTS_v2_HOLD_I.zip
import random, zipfile, datetime
# ===== USTAWIENIA =====
SEED = None # ustaw np. na liczbę, jeśli chcesz powtarzalny wynik
SUSP_RATIO = 0.34 # ~1/3 pozycji ma „prawdę” w ( ) o xeno/kontrabandzie
QTY_MIN, QTY_MAX = 1, 10
LINES_PER_SECTION = 123
CAP_PER_SECTION = 1000
if SEED is None:
random.seed() # świeża losowość
else:
random.seed(SEED)
# ===== PULI I KOMPONENTY NAZW =====
WORLDS = [
"Ostia","Avachrus","Enoch","Gilead Prime","Nethreus","Ryza","Vostroya","Scintilla","Footfall",
"Hydraphur","Malfi","Port Wander","Voss Prime","Lucius","Graia","Metalica","Sollex","Sibellus"
]
FOODS = [
"Ziarno kultywacyjne","Pszenica","Owies","Jęczmień","Mąka","Kasza jęczmienna","Kasza owsiana",
"Groch","Fasola","Ciecierzyca","Słód jasny","Słód ciemny","Suszone jarzyny","Sól żywnościowa","Olej lampowy"
]
INDUSTRY = [
"Złącza heksowe","Zawory nadciśnieniowe","Łożyska baryłkowe","Łożyska kulkowe","Łożyska stożkowe",
"Taśmy stalowe","Plandeki światłochłonne","Pudła ciszy (wygłuszenie)","Rury segmentowe PL","Kable PL",
"Siatki zabezpieczające","Zasuwy grodziowe","Zaczepy stalowe","Kątowniki ciężkie","Nity kadłubowe",
"Skrzynki na dźwięk (wygłuszenie)","Filtry rebreatherów","Transpondery nawigacyjne","Przekaźniki proste",
"Zawiesia wielokrążkowe","Sprężyny powrotne","Kołnierze izolacyjne","Kleje statkowe","Klemy zasilające",
"Rolki transportowe","Farba kadłubowa","Żywica naprawcza","Kity hermetyczne","Szkła ochronne",
"Panele grodziowe","Uszczelki retencyjne","Siłowniki serwo (obudowy)"
]
DEVOTION = [
"Lampiony procesyjne","Witraże polowe (składane)","Zwoje pergaminowe","Maski nabożne",
"Relikwie podręczne (skrzynki)","Szkatuły dewocyjne (stal miękka)","Amfory gliniane",
"Szaty Pokora (robocze)","Kopułki modlitewne (mini)","Płaszcze Wachta",
"Świece czarne Negocjacje","Świece woskowe Cisza Nocna","Figurki pobożne",
"Piety miniaturowe","Popiół posypny (liturgiczny)","Ołtarze polowe (składane)",
"Skarbonki parafialne","Księgi wizualne (albumy)","Księgi zakładkowe",
"Ikony ścienne (odlewy)","Reliefy miedziane","Baldachimy Krem","Puchary kapłańskie (rekwizyt)"
]
ADJ = [
"Hostia","Pielgrzym","Kocioł’","Pilgrim","Stałość’","Ordo Cargo","Mgła","Cień’",
"Błękit Służbowy","Sztywna Karta","Jedność’","Głuchy Spaw","Kierunek","Gotowość’","Neutral",
"Mk II","Mk III","Mk IV","Mk V","Plus","Premium","Export","Seria V","Seria IX","Nocny"
]
INN_NOTES = [
"Zwykły wyrób, zgodny z normą diecezjalną.",
"Do użytku cywilnego; dokumentacja pełna.",
"Na potrzeby konserwacji/edukacji polowej.",
"Brak zasilania, brak funkcji specjalnych.",
"Wersja eksportowa; gwinty zgodne z PL-3.",
"Na morale i porządek; bez cech bojowych.",
"Próbki kontrolne — wynik negatywny na scrapcode.",
"Rekwizyty katechetyczne; brak statusu kultowego.",
"Puste, czyste, opieczętowane.",
"Zabezpieczone audytem Ministorum.",
"Składane; bez ostrych krawędzi w papierach.",
"Zwykła jakość magazynowa; brak niespodzianek.",
"Brak reakcji psionicznej (pomiar: tło).",
"Standard przemysłowy; kompatybilność wysoka.",
"Do ekspozycji; nieotwierane przy inspekcji.",
"Zestaw muzealny; kojąco nudny.",
"Oznaczenie cywilne; instrukcja w komplecie.",
"Zgodne z przepisem P-77 (ładownie ogólne).",
"Pieczęć diecezjalna aktualna; datowanie bieżące.",
"Bezpieczne dla chóru i czytników vox."
]
HARMLESS = [
"zwykły ładunek cywilny","materiały szkoleniowe/rekonstrukcja","zestaw konserwatorski",
"komplet części serwisowych","puste i czyste","standard magazynowy","produkt spożywczy",
"wyposażenie pomocnicze","materiały dekoracyjne","narzędzia warsztatowe"
]
XENO_REAL = [
"fragmenty wraithbone Aeldari (martwe, obrobione na szkło)",
"odpryski spirit-stone (pozbawione ładunku)",
"ukryty vox-scrambler xeno (obudowa cywilna)",
"mikrofiolki po jadu Lhamaean (dokładnie wyparzone)",
"moduł holo-emisyjny Aeldari (matryca wycięta, nieaktywny)",
"kaseta rozliczeń cold trade (podwójne dno)",
"nekrotech — fragment stele bezcielesnej (kamień ozdobny)",
"obudowy tłumiące sygnał (technologia nieidentyfikowana)",
"orb psioniczny — tłumik nastroju (źródło Aeldari)",
"halo-device: element oprawy (niekompletne, bez rdzenia)",
"resztki proscribed tomes (okładki, wkład wymieniony na blanki)",
"zegar-pikietaż dla transakcji cold trade (ukryty znacznik czasu)",
"ampułki po obscura klasy ceremonialnej (umyte; testy czyste)",
"fiolki po ichorze drukhari (sterylnie czyste)",
"masker polowy Drukhari — tkanina z włóknem ziggurat (zdezaktywowana)",
"matryca soniczna Laer (obudowana pudłem ciszy)",
"półprzezroczysta łuska necron — fragment dekoracyjny (brak reakcji)",
]
def compose_name():
# wybór kategorii z wagami
genre = random.choices(["food","industry","devotion"], [4,5,3])[0]
base = random.choice({"food":FOODS, "industry":INDUSTRY, "devotion":DEVOTION}[genre])
# modyfikacje wersji/serii
if "PL" in base:
base = base.replace("PL", f"PL-{random.randint(3,12)}")
if "Kable PL" in base and random.random()<0.7:
base = base.replace("Kable PL", f"Kable PL-{random.randint(2,8)}")
flair=[]
if random.random()<0.7: flair.append(random.choice(ADJ))
if random.random()<0.25: flair.append(f"Seria {random.randint(2,12)}")
return base if not flair else f"{base} " + " ".join(flair)
def make_line(idx, suspicious_ratio=SUSP_RATIO):
q = random.randint(QTY_MIN, QTY_MAX)
nm = compose_name()
ow = random.choice(WORLDS)
note = random.choice(INN_NOTES)
paren = f"({random.choice(XENO_REAL)})" if random.random()<suspicious_ratio else f"({random.choice(HARMLESS)})"
return f"{idx}) {q}×{nm}{ow} — [{note}] {paren}", q
def make_section_text(hold_label, s_idx, s_cnt):
header = (
"=== FORTEL TRAPPA — MANIFEST ŁADUNKOWY ===\n"
f"{hold_label} / SEKCJA {s_idx} z {s_cnt}\n"
"(Format: [Liczba 110] × Standardowy Imperialny Kontener — Nazwa — Pochodzenie — [Uwaga kapitańska] (co to naprawdę jest))\n\n"
)
total=0; entries=[]
# ogranicz powtórzenia nazw w obrębie sekcji
seen=set()
for i in range(1, LINES_PER_SECTION+1):
tries=0
while True:
line,q = make_line(i)
name_part = line.split("—")[1].strip() # „N× — NAZWA — ...”
if name_part not in seen or tries>7:
seen.add(name_part)
break
tries+=1
entries.append([line,q]); total+=q
# przytnij sumę kontenerów do CAP_PER_SECTION
if total>CAP_PER_SECTION:
excess=total-CAP_PER_SECTION
for i in range(len(entries)-1,-1,-1):
if excess<=0: break
q=entries[i][1]
if q>1:
red=min(q-1,excess); entries[i][1]-=red; excess-=red
prefix,rest=entries[i][0].split("× —",1)
idx_str=prefix.split(")")[0]+")"
entries[i][0]=f"{idx_str} {entries[i][1]}×{rest}"
body="\n".join(e[0] for e in entries)
footer=f"\n\n— KONIEC: {hold_label} / SEKCJA {s_idx}/{s_cnt} —"
return header+body+footer
def build_zip(full_path, hold1_path):
# FULL: HOLD I 0416; HOLD II 0115; HOLD III 0115
with zipfile.ZipFile(full_path,"w",compression=zipfile.ZIP_DEFLATED) as z:
for s in range(4,17): # HOLD I
z.writestr(f"HOLD_I_SEKCJA_{s:02d}.txt", make_section_text("HOLD I", s, 16))
for s in range(1,16): # HOLD II
z.writestr(f"HOLD_II_SEKCJA_{s:02d}.txt", make_section_text("HOLD II", s, 15))
for s in range(1,16): # HOLD III
z.writestr(f"HOLD_III_SEKCJA_{s:02d}.txt", make_section_text("HOLD III", s, 15))
z.writestr("README.txt",
f"FORTEL TRAPPA — MANIFESTY v2 ({datetime.datetime.utcnow().isoformat()}Z)\n"
"HOLD I: 0416; HOLD II: 0115; HOLD III: 0115\n"
f"~{int(SUSP_RATIO*100)}% pozycji w ( ) ujawnia real-tożsamość (xeno/kontrabanda); [ ] — noty urzędowo niewinne.\n"
"123 pozycji/sekcję; 110 kontenerów/pozycję; suma ≤1000/sekcję.\n")
# HOLD I only
with zipfile.ZipFile(hold1_path,"w",compression=zipfile.ZIP_DEFLATED) as z:
for s in range(4,17):
z.writestr(f"HOLD_I_SEKCJA_{s:02d}.txt", make_section_text("HOLD I", s, 16))
z.writestr("README_HOLD_I.txt", f"HOLD I (0416) v2 — {datetime.datetime.utcnow().isoformat()}Z\n")
if __name__ == "__main__":
build_zip("FORTEL_MANIFESTS_v2_FULL.zip","FORTEL_MANIFESTS_v2_HOLD_I.zip")
print("OK -> FORTEL_MANIFESTS_v2_FULL.zip | FORTEL_MANIFESTS_v2_HOLD_I.zip")