diff --git a/backup_non_stable_docker b/backup_non_stable_docker
new file mode 100644
index 0000000..e69de29
diff --git a/rpg/armour.csv b/rpg/armour.csv
deleted file mode 100644
index 1afa6f2..0000000
--- a/rpg/armour.csv
+++ /dev/null
@@ -1,3 +0,0 @@
-name,type,locations,ap_head,ap_body,ap_larm,ap_rarm,ap_lleg,ap_rleg,max_ag,traits,weight,availability,source,notes
-Flak Vest,armor,"Body",0,4,0,0,0,0,,Flak,7,Common,"DH2 CRB","PRZYKŁAD – ZASTĄP"
-Power Armour (Astartes),armor,"All",8,10,8,8,9,9,,Environmental; Auto-senses,100,"Very Rare","DW CRB","PRZYKŁAD – ZASTĄP"
diff --git a/rpg/attack_test.js b/rpg/attack_test.js
deleted file mode 100644
index 2f8e32d..0000000
--- a/rpg/attack_test.js
+++ /dev/null
@@ -1,60 +0,0 @@
-// DH2 Attack Test v2 — presets: Aim/Range/Fire + Size/Light/Cover
-const actor = canvas.tokens.controlled[0]?.actor ?? game.user.character;
-if (!actor) return ui.notifications.warn("Zaznacz token albo przypisz postać.");
-new Dialog({
- title: "🎯 Attack Test (WS/BS)",
- content: `
-
`,
- buttons: {
- roll: {
- label: "Roll",
- callback: async (html) => {
- const get = n => Number(html.find(`[name="${n}"]`).val());
- const base = get("base");
- const total = base + get("aim") + get("range") + get("stance") + get("size") + get("light") + get("cover") + get("mod");
- const r = await(new Roll("1d100")).roll({async:true});
- const ok = r.total <= total;
- const margin = Math.abs(total - r.total);
- const dox = ok ? 1 + Math.floor(margin/10) : Math.floor(margin/10);
- const table = `
-
- | Target | ${total} | Roll | ${r.total} |
- | Result | ${ok?'SUCCESS':'FAIL'} — ${ok?dox+' DoS':dox+' DoF'} |
-
`;
- r.toMessage({speaker: ChatMessage.getSpeaker({actor}), flavor: `🎯 Attack Test
${table}`});
- }
- }
- }
-}).render(true);
diff --git a/rpg/critical_hits.csv b/rpg/critical_hits.csv
deleted file mode 100644
index d30ef62..0000000
--- a/rpg/critical_hits.csv
+++ /dev/null
@@ -1,6 +0,0 @@
-Roll,Result
-1,Energy/Head — wpis 1
-2,Energy/Head — wpis 2
-3,Energy/Head — wpis 3
-4,Energy/Head — wpis 4
-5,Energy/Head — wpis 5
diff --git a/rpg/focus_power.js b/rpg/focus_power.js
deleted file mode 100644
index 8b8cddd..0000000
--- a/rpg/focus_power.js
+++ /dev/null
@@ -1,50 +0,0 @@
-// 🧠 Focus Power (DH2) — WP test + Phenomena/Perils with mode presets
-const actor = canvas.tokens.controlled[0]?.actor ?? game.user.character;
-if (!actor) return ui.notifications.warn("Zaznacz token.");
-new Dialog({
- title: "🧠 Focus Power",
- content: `
- `,
- buttons: {
- roll: { label: "Roll", callback: async html => {
- const wp = Number(html.find('[name="wp"]').val());
- const pr = Number(html.find('[name="pr"]').val());
- const mode = html.find('[name="mode"]').val();
- const flat = Number(html.find('[name="flat"]').val());
- const thr = Number(html.find('[name="thr"]').val());
- let effPR = pr, ppmod = 0, ppAlways = false, note = "";
- if (mode==="fettered"){ effPR = Math.max(1, Math.floor(pr/2)); note="(Fettered: PR/2, brak Phenomena)"; }
- if (mode==="push"){ effPR = pr+3; ppmod=10; ppAlways = true; note="(Push: +3 PR, Phenomena zawsze, +10)"; }
- const target = wp + flat;
- const roll = await (new Roll("1d100")).roll({async:true});
- const ok = roll.total <= target;
- const dos = ok ? 1 + Math.floor((target - roll.total)/10) : Math.floor((roll.total - target)/10);
- const doubles = (roll.total%11===0) || (roll.total===100);
- const info = `
- | Target | ${target} | Roll | ${roll.total} |
- | Result | ${ok?'SUCCESS':'FAIL'} — ${ok?dos+' DoS':dos+' DoF'} ${doubles?' — DOUBLES':''} |
- | Eff. PR | ${effPR} | Range hint | ${effPR*10} m (jeśli moc tak działa) |
-
`;
- roll.toMessage({speaker: ChatMessage.getSpeaker({actor}), flavor:`🧠 Focus Power ${note}
${info}`});
- const needPP = (mode==="unfettered" && doubles) || (mode==="push") ;
- if (needPP){
- const tbl = game.tables.getName("Psychic Phenomena");
- if (tbl){
- const r = await (new Roll(`1d100 + ${ppmod}`)).roll({async:true});
- await tbl.draw({displayResults:true, roll:r});
- if (r.total >= thr){
- const per = game.tables.getName("Perils of the Warp");
- if (per) await per.draw({displayResults:true});
- }
- } else ChatMessage.create({content:"Utwórz RollTable: Psychic Phenomena (+ Perils of the Warp)"});
- }
- }}
- }
-}).render(true);
diff --git a/rpg/hit_location.js b/rpg/hit_location.js
deleted file mode 100644
index 7c2b2a6..0000000
--- a/rpg/hit_location.js
+++ /dev/null
@@ -1,22 +0,0 @@
-// 🎯 Hit Location (DH mapping by reversed roll)
-new Dialog({
- title:"🎯 Hit Location",
- content:``,
- buttons:{
- go:{label:"Resolve", callback: html=>{
- const n = Math.max(1, Math.min(100, Number(html.find('[name="roll"]').val())));
- const rev = Number(String(n).padStart(2,"0").split("").reverse().join(""));
- let loc = "";
- if (rev<=10) loc="Head";
- else if (rev<=20) loc="Right Arm";
- else if (rev<=30) loc="Left Arm";
- else if (rev<=70) loc="Body";
- else if (rev<=85) loc="Right Leg";
- else loc="Left Leg";
- ChatMessage.create({content:`🎯 Hit Location: roll ${n} → reversed ${rev} → ${loc}`});
- }}
- }
- }).render(true);
-
\ No newline at end of file
diff --git a/rpg/hose_rules.js b/rpg/hose_rules.js
deleted file mode 100644
index eec1dcd..0000000
--- a/rpg/hose_rules.js
+++ /dev/null
@@ -1,6 +0,0 @@
-const text=`House Rules — DH2 chassis
-• Unnatural mapowanie: dawne ×2 bonus → Unnatural (+X) tak, by SB/TB odzwierciedlały linię źródłową.
-• Pancerz Astartes: zachowaj AP; zużycie zasilania jako +1 Fatigue co X scen zamiast liczenia minut.
-• Aptitudes: archetypy z RT/DW/BC mają przypisane 2–3 Aptitudes DH2 dla kosztów XP.
-• Psy: testy i PR z DH2 dla wszystkich; GK posiada Aegis Discipline (1×/scena reroll Perils) + 1–2 signature powers z DW.
-• RT Acquisition → DH2 Influence z modyfikatorami kontekstowymi (teatr działań, mandat Inkwizycji, czas).`;ChatMessage.create({content:text});
diff --git a/rpg/inititative.js b/rpg/inititative.js
deleted file mode 100644
index f37d0c8..0000000
--- a/rpg/inititative.js
+++ /dev/null
@@ -1,11 +0,0 @@
-// 🚦 Initiative for selected tokens (1d10 + AG Bonus prompt)
-if (!canvas.tokens.controlled.length) return ui.notifications.warn("Zaznacz co najmniej jeden token.");
-const combat = game.combat ?? await Combat.implementation.create({});
-for (const t of canvas.tokens.controlled){
- if (!combat.combatants.some(c=>c.tokenId===t.id)) await combat.createEmbeddedDocuments("Combatant",[ {tokenId:t.id, sceneId: canvas.scene.id, hidden:false} ]);
- const ag = Number(await Dialog.prompt({title:`AG Bonus for ${t.name}`, content:``, label:"OK"}));
- const r = await (new Roll(`1d10 + ${ag}`)).roll({async:true});
- await combat.setInitiative(combat.combatants.find(c=>c.tokenId===t.id).id, r.total);
- r.toMessage({flavor:`🚦 Initiative — ${t.name}: ${r.total}`});
-}
-ui.notifications.info("Inicjatywy ustawione.");
diff --git a/rpg/perils.csv b/rpg/perils.csv
deleted file mode 100644
index aa97592..0000000
--- a/rpg/perils.csv
+++ /dev/null
@@ -1,21 +0,0 @@
-Roll,Result
-1-5,Perils 1–5 — WPISZ
-6-10,Perils 6–10 — WPISZ
-11-15,Perils 11–15 — WPISZ
-16-20,Perils 16–20 — WPISZ
-21-25,Perils 21–25 — WPISZ
-26-30,Perils 26–30 — WPISZ
-31-35,Perils 31–35 — WPISZ
-36-40,Perils 36–40 — WPISZ
-41-45,Perils 41–45 — WPISZ
-46-50,Perils 46–50 — WPISZ
-51-55,Perils 51–55 — WPISZ
-56-60,Perils 56–60 — WPISZ
-61-65,Perils 61–65 — WPISZ
-66-70,Perils 66–70 — WPISZ
-71-75,Perils 71–75 — WPISZ
-76-80,Perils 76–80 — WPISZ
-81-85,Perils 81–85 — WPISZ
-86-90,Perils 86–90 — WPISZ
-91-95,Perils 91–95 — WPISZ
-96-100,Perils 96–100 — WPISZ
diff --git a/rpg/powers.csv b/rpg/powers.csv
deleted file mode 100644
index c274d44..0000000
--- a/rpg/powers.csv
+++ /dev/null
@@ -1,3 +0,0 @@
-name,type,discipline,action,test,range,sustained,effect,source,notes
-Smite,power,Biomancy,Half,"WP Challenging (+0)","PR*10m",No,"1d10+PR E; Tearing","DH2 CRB","PRZYKŁAD – ZASTĄP"
-Foreboding,power,Divination,Reaction,"Per Difficult (-10)","Self",No,"Use as Evasion; DoS rules","DH2 CRB","PRZYKŁAD – ZASTĄP"
diff --git a/rpg/pp.csv b/rpg/pp.csv
deleted file mode 100644
index a5bd82f..0000000
--- a/rpg/pp.csv
+++ /dev/null
@@ -1,21 +0,0 @@
-Roll,Result
-1-5,PP 1–5 — WPISZ
-6-10,PP 6–10 — WPISZ
-11-15,PP 11–15 — WPISZ
-16-20,PP 16–20 — WPISZ
-21-25,PP 21–25 — WPISZ
-26-30,PP 26–30 — WPISZ
-31-35,PP 31–35 — WPISZ
-36-40,PP 36–40 — WPISZ
-41-45,PP 41–45 — WPISZ
-46-50,PP 46–50 — WPISZ
-51-55,PP 51–55 — WPISZ
-56-60,PP 56–60 — WPISZ
-61-65,PP 61–65 — WPISZ
-66-70,PP 66–70 — WPISZ
-71-75,PP 71–75 — WPISZ
-76-80,PP 76–80 — WPISZ
-81-85,PP 81–85 — WPISZ
-86-90,PP 86–90 — WPISZ
-91-95,PP 91–95 — WPISZ
-96-100,PP 96–100 — WPISZ
diff --git a/rpg/quick_conditions.js b/rpg/quick_conditions.js
deleted file mode 100644
index 90689f6..0000000
--- a/rpg/quick_conditions.js
+++ /dev/null
@@ -1,28 +0,0 @@
-// 🩹 Toggle conditions on selected tokens (Foundry v13)
-const choices = [
- {id:"fatigued", label:"Fatigued"},
- {id:"stunned", label:"Stunned"},
- {id:"prone", label:"Prone"},
- {id:"frightened", label:"Frightened (Fear)"}
- ];
- const opts = choices.map(c=>``).join("
");
- new Dialog({
- title:"🩹 Conditions",
- content:``,
- buttons:{
- go:{label:"Apply",callback: html=>{
- const ids = Array.from(html.find('input[name="c"]:checked')).map(e=>e.value);
- const mode = html.find('[name="mode"]').val();
- const getEf = id => CONFIG.statusEffects.find(e=>e.id===id) ?? {id};
- canvas.tokens.controlled.forEach(t=>{
- ids.forEach(id=>{
- if (mode==="toggle") t.toggleEffect(getEf(id));
- else if (mode==="on") t.actor?.effects?.some(e=>e.getFlag("core","statusId")===id) ? null : t.toggleEffect(getEf(id));
- else if (mode==="off") t.actor?.effects?.some(e=>e.getFlag("core","statusId")===id) ? t.toggleEffect(getEf(id)) : null;
- });
- });
- }}
- }
- }).render(true);
-
\ No newline at end of file
diff --git a/rpg/quick_damage.js b/rpg/quick_damage.js
deleted file mode 100644
index 2020436..0000000
--- a/rpg/quick_damage.js
+++ /dev/null
@@ -1,38 +0,0 @@
-// 💥 Quick Damage — supports Tearing, Proven(X), Primitive(X), flat mod
-new Dialog({
- title:"💥 Damage Roller",
- content: `
- `,
- buttons:{
- go:{label:"Roll", callback: async html=>{
- const mod = Number(html.find('[name="mod"]').val());
- const tearing = html.find('[name="tear"]')[0].checked;
- const proven = html.find('[name="prov"]')[0].checked ? Number(html.find('[name="provV"]').val()) : 0;
- const primitive = html.find('[name="prim"]')[0].checked ? Number(html.find('[name="primV"]').val()) : 0;
- // base die (d10) with tearing (best of 2)
- const r1 = await (new Roll("1d10")).roll({async:true});
- const r2 = tearing ? await (new Roll("1d10")).roll({async:true}) : null;
- let die = tearing ? Math.max(r1.total, r2.total) : r1.total;
- // apply Proven/Primitive
- if (proven>0) die = Math.max(die, proven);
- if (primitive>0) die = Math.min(die, primitive);
- const rf = (die===10); // potential Zealous Hatred trigger
- const total = die + mod;
- let flavor = `💥 Damage
Die: ${die}${tearing?` (Tearing ${r1.total}/${r2.total.total})`:''} + Mod ${mod} = ${total}`;
- if (proven>0) flavor += `
Proven(${proven}) zastosowano`;
- if (primitive>0) flavor += `
Primitive(${primitive}) zastosowano`;
- if (rf) flavor += `
⚡ Natural 10 — rozważ Zealous Hatred.`;
- ChatMessage.create({speaker: ChatMessage.getSpeaker(), content: flavor});
- }}
- }
- }).render(true);
-
\ No newline at end of file
diff --git a/rpg/table_gen.js b/rpg/table_gen.js
deleted file mode 100644
index ec0f29c..0000000
--- a/rpg/table_gen.js
+++ /dev/null
@@ -1,26 +0,0 @@
-// 📚 Create placeholder RollTables for Crits + Psychic Phenomena/Perils (with icons)
-const icon = {Energy:"⚡", Impact:"🔨", Rending:"🗡️", Explosive:"💥"};
-const dmgTypes = ["Energy","Impact","Rending","Explosive"];
-const locs = ["Head","Body","Left Arm","Right Arm","Left Leg","Right Leg"];
-async function makeCrit(dtype, loc){
- const name = `Crit: ${dtype} - ${loc}`;
- if (game.tables.getName(name)) return;
- const results = [];
- for (let i=1;i<=5;i++){
- results.push({type:0, text:`${icon[dtype]||""} ${dtype}/${loc} — wpis ${i} (uzupełnij z PDF)`, weight:1, range:[i,i]});
- }
- await RollTable.implementation.create({name, formula:"1d5", replacement:true, displayRoll:false, results});
-}
-async function makeWide(name, emoji){
- if (game.tables.getName(name)) return;
- const results = [];
- for (let i=0;i<20;i++){
- const lo=i*5+1, hi=i*5+5;
- results.push({type:0, text:`${emoji} ${name} ${lo}-${hi} — wpis (uzupełnij z PDF)`, weight:1, range:[lo,hi]});
- }
- await RollTable.implementation.create({name, formula:"1d100", replacement:true, displayRoll:false, results});
-}
-for (const d of dmgTypes) for (const l of locs) await makeCrit(d,l);
-await makeWide("Psychic Phenomena","🌀");
-await makeWide("Perils of the Warp","☠️");
-ui.notifications.info("Utworzono puste tabele: Crits (4×6) + Phenomena + Perils.");
diff --git a/rpg/talents.csv b/rpg/talents.csv
deleted file mode 100644
index 529b9bb..0000000
--- a/rpg/talents.csv
+++ /dev/null
@@ -1,3 +0,0 @@
-name,type,tier,aptitudes,prereq,effect,source,notes
-Ambidextrous,talent,1,"Agility; Offence","Ag 30","-10 to off-hand penalty","DH2 CRB","PRZYKŁAD – ZASTĄP"
-Aegis Discipline (GK),talent,2,"Willpower; Defence","Psyker","Reroll Perils 1×scene","HOUSE","DODAJ WŁASNY OPIS"
diff --git a/rpg/weapons.csv b/rpg/weapons.csv
deleted file mode 100644
index 8c9eee5..0000000
--- a/rpg/weapons.csv
+++ /dev/null
@@ -1,3 +0,0 @@
-name,type,subtype,damage,pen,range,rof,qualities,weight,availability,source,notes
-Lasgun M36,weapon,Basic,"1d10+3",0,100m,"S/3/–","Reliable",4,Common,"DH2 CRB p.142","PRZYKŁAD – ZASTĄP"
-Astartes Bolter,weapon,Basic,"1d10+9",4,90m,"S/2/–","Tearing; Unreliable",9,Rare,"DW CRB","PRZYKŁAD – ZASTĄP"
diff --git a/rpg/zealous_hatred.js b/rpg/zealous_hatred.js
deleted file mode 100644
index a945ca9..0000000
--- a/rpg/zealous_hatred.js
+++ /dev/null
@@ -1,33 +0,0 @@
-// ⚡ Zealous Hatred helper (DH2) — roll 1d5 crit OR add +1d5 dmg
-new Dialog({
- title: "⚡ Zealous Hatred",
- content: `
- `,
- buttons: {
- go: { label: "Resolve", callback: async html => {
- const pen = html.find('[name="penetrated"]').val();
- if (pen === "yes") {
- const dtype = html.find('[name="dtype"]').val();
- const loc = html.find('[name="loc"]').val();
- const override = html.find('[name="tname"]').val()?.trim();
- const name = override || `Crit: ${dtype} - ${loc}`;
- const r = await (new Roll("1d5")).roll({async:true});
- const table = game.tables.getName(name);
- if (table) await table.draw({displayResults:true, roll:r});
- else r.toMessage({flavor:`⚡ Zealous Hatred: Critical ${r.total} — brak tabeli ${name} (utwórz lub zmień nazwę).`});
- } else {
- const r = await (new Roll("1d5")).roll({async:true});
- r.toMessage({flavor:"⚡ Zealous Hatred: Dodaj do obrażeń +1d5 (atak nie przebił Soak)."});
- }
- }}
- }
- }).render(true);
-
\ No newline at end of file
diff --git a/working_copy/conanjurer_functions.py b/working_copy/conanjurer_functions.py
index c0b2011..b94d18f 100644
--- a/working_copy/conanjurer_functions.py
+++ b/working_copy/conanjurer_functions.py
@@ -9,7 +9,26 @@ from typing import AsyncIterator, Callable, Awaitable
from aiomcrcon import Client as _Rcon # Source RCON over TCP
import asyncssh
+load_dotenv()
+
log = logging.getLogger("discord")
+@dataclass
+class Event:
+ kind: str # "chat" | "login" | "logout" | "death" | "raw"
+ text: str # gotowy do wyświetlenia tekst
+ raw: str # oryginalna linia (do debugowania)
+
+
+# --- REGEXY DO DOSTROJENIA NA WŁASNYM LOGU ---
+# Poniżej PRZYKŁADOWE wzorce. Conan/Pippi/Tot logują różnie — sprawdź swój log
+# i dopasuj. Jeśli wzorzec nie pasuje, linia trafia jako "raw" tylko do debug.
+_PATTERNS: list[tuple[str, re.Pattern]] = [
+ ("chat", re.compile(r"Chat:\s*(?P.+?):\s*(?P.+)$", re.I)),
+ ("login", re.compile(r"(?P.+?)\s+(joined|connected|logged in)", re.I)),
+ ("logout", re.compile(r"(?P.+?)\s+(left|disconnected|logged out)", re.I)),
+ ("death", re.compile(r"(?P.+?)\s+was killed by\s+(?P.+)$", re.I)),
+]
+
@dataclass(frozen=True)
class Config:
# Discord
@@ -91,22 +110,6 @@ class RconClient:
pass
self._client = None
-@dataclass
-class Event:
- kind: str # "chat" | "login" | "logout" | "death" | "raw"
- text: str # gotowy do wyświetlenia tekst
- raw: str # oryginalna linia (do debugowania)
-
-
-# --- REGEXY DO DOSTROJENIA NA WŁASNYM LOGU ---
-# Poniżej PRZYKŁADOWE wzorce. Conan/Pippi/Tot logują różnie — sprawdź swój log
-# i dopasuj. Jeśli wzorzec nie pasuje, linia trafia jako "raw" tylko do debug.
-_PATTERNS: list[tuple[str, re.Pattern]] = [
- ("chat", re.compile(r"Chat:\s*(?P.+?):\s*(?P.+)$", re.I)),
- ("login", re.compile(r"(?P.+?)\s+(joined|connected|logged in)", re.I)),
- ("logout", re.compile(r"(?P.+?)\s+(left|disconnected|logged out)", re.I)),
- ("death", re.compile(r"(?P.+?)\s+was killed by\s+(?P.+)$", re.I)),
-]
def parse_line(line: str) -> Event | None:
@@ -195,8 +198,6 @@ async def watch(cfg, on_event: Callable[[Event], Awaitable[None]]) -> None:
except Exception:
log.exception("Błąd obsługi zdarzenia")
-load_dotenv()
-
def _req(key: str) -> str:
val = os.getenv(key)
if not val or val.startswith("wklej") or val.startswith("000000"):