sync: foundry v0.2

This commit is contained in:
2025-10-09 20:55:30 +02:00
parent 95a71cb6e2
commit 0a4148abaa
23 changed files with 1285 additions and 143 deletions
+118
View File
@@ -0,0 +1,118 @@
# generate_manifests.py
# Fortel Trappa — pełne manifesty ładunkowe (HOLD I/II/III) w ZIP
# Uruchom: python generate_manifests.py -> powstaną dwa pliki ZIP w bieżącym katalogu.
import random, zipfile, datetime
random.seed(40000) # deterministyczny „smak”
WORLDS = ["Ostia","Avachrus","Enoch","Gilead Prime","Nethreus","Ryza","Vostroya","Scintilla","Footfall"]
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."
]
INN_NAMES = [
"Ziarno kultywacyjne ‘Święty Oset","Mąka Hostia","Kasza jęczmienna Pielgrzym","Groch Kocioł’","Fasola Pilgrim",
"Sól żywnościowa","Olej lampowy","Smary maszynowe Stałość’","Farba kadłubowa Mgła","Pasy mocujące Ordo Cargo",
"Złącza heksowe","Zawory nadciśnieniowe","Łożyska baryłkowe Wytrwałość’","Łożyska kulkowe precyzyjne",
"Taśmy stalowe Krzyżowe","Plandeki światłochłonne Cień’","Pudła ciszy (obudowy wygłuszające)","Lampiony procesyjne",
"Witraże polowe (składane)","Zwoje pergaminowe Sztywna Karta","Maski nabożne Neutral","Relikwie podręczne (skrzynki)",
"Szkatuły dewocyjne (stal miękka)","Szkła ochronne Błękit Służbowy","Barwniki okienne (błękit/purpura)",
"Kity hermetyczne Głuchy Spaw","Rury segmentowe PL-7","Kable PL-3 Uniwersalne","Siatki zabezpieczające Drobne Oczko",
"Zasuwy grodziowe PL-10","Zaczepy stalowe Cierpliwość’","Kątowniki ciężkie Prosto","Nity kadłubowe Amen",
"Skrzynki na dźwięk (wygłuszenie)","Szkła różnobarwne (mix)","Amfory gliniane E-Seria","Szaty Pokora (robocze)",
"Kopułki modlitewne (mini)","Płaszcze przeciwdeszczowe Wachta","Świece czarne Negocjacje","Świece woskowe Cisza Nocna",
"Zwoje kabli heksowych (ekranowane)","Sprężyny powrotne","Zawiesia wielokrążkowe","Przekaźniki proste Miedź’",
"Łożyska stożkowe Kierunek","Szkatuły na krople (apteczne)","Ołtarze polowe (składane)","Figurki pobożne",
"Piety miniaturowe","Popiół posypny (liturgiczny)","Kleje statkowe Głuchy Spaw","Klemy zasilające Moc",
"Kołnierze izolacyjne PL-5","Beczki puste Gotowość’","Rolki transportowe Gładkie","Słód jasny Nocny",
"Skrzydła drzwiowe Jedność’","Skarbonki parafialne","Księgi wizualne (albumy)","Zegary harmoniczne (niski ton)",
"Baldachimy małe Krem","Puchar kapłański (rekwizyt)","Księgi zakładkowe","Pasy transmisyjne Ciągłość’",
"Pudła akustyczne (puste)","Rękawice robocze Skóra+",
]
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)",
]
def inn_note(): return random.choice(INN_NOTES)
def name(): return random.choice(INN_NAMES)
def world(): return random.choice(WORLDS)
def qty(): return random.randint(1, 8)
def make_line(idx, suspicious_ratio=0.34):
q = qty(); nm = name(); ow = world(); note = inn_note()
if random.random() < suspicious_ratio:
paren = f"({random.choice(XENO_REAL)})"
else:
paren = f"({random.choice(['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'])})"
return f"{idx}) {q}×{nm}{ow} — [{note}] {paren}", q
def make_section_text(hold_label, s_idx, s_cnt):
header = f"=== FORTEL TRAPPA — MANIFEST ŁADUNKOWY ===\n{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 = []
for i in range(1, 124):
line, q = make_line(i, suspicious_ratio=0.34)
entries.append([line, q]); total += q
if total > 1000:
excess = total - 1000
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 write_full_zip(path_full, path_hold1):
with zipfile.ZipFile(path_full, "w", compression=zipfile.ZIP_DEFLATED) as z:
for s in range(4, 17): # HOLD I 0416
z.writestr(f"HOLD_I_SEKCJA_{s:02d}.txt", make_section_text("HOLD I", s, 16))
for s in range(1, 16): # HOLD II 0115
z.writestr(f"HOLD_II_SEKCJA_{s:02d}.txt", make_section_text("HOLD II", s, 15))
for s in range(1, 16): # HOLD III 0115
z.writestr(f"HOLD_III_SEKCJA_{s:02d}.txt", make_section_text("HOLD III", s, 15))
z.writestr("README.txt", f"FORTEL TRAPPA — MANIFESTY ({datetime.datetime.utcnow().isoformat()}Z)\nHOLD I: 0416; HOLD II: 0115; HOLD III: 0115\n~1/3 pozycji w ( ) ma real-tożsamość (xeno/kontrabanda), [ ] — noty urzędowo niewinne.\n")
with zipfile.ZipFile(path_hold1, "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) — {datetime.datetime.utcnow().isoformat()}Z\n")
if __name__ == "__main__":
write_full_zip("FORTEL_HOLD_I_II_III_MANIFESTS.zip", "FORTEL_HOLD_I_SEKCJE_04-16.zip")
print("OK: FORTEL_HOLD_I_II_III_MANIFESTS.zip | FORTEL_HOLD_I_SEKCJE_04-16.zip")
+182
View File
@@ -0,0 +1,182 @@
# 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")