Last active
December 19, 2020 18:50
-
-
Save Oppodelldog/0bdde2885d7de5584ed6a9a3f8304377 to your computer and use it in GitHub Desktop.
Phasmophobia Deduction Helper
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<html lang="de"> | |
<head> | |
<meta charset="utf-8"> | |
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no"> | |
<title>Phasmo-Praktikant</title> | |
<style> | |
* { | |
margin: 0; | |
padding: 0; | |
outline: none; | |
font-size: 18px; | |
box-sizing: border-box; | |
} | |
h1 { | |
font-size: 26px; | |
} | |
body { | |
color: white; | |
background-color: black; | |
margin-left: 20px; | |
margin-right: 20px; | |
} | |
h1 { | |
cursor: pointer; | |
} | |
.content { | |
margin-top: 40px; | |
display: flex; | |
flex-direction: column; | |
justify-items: center; | |
align-items: center; | |
} | |
.content > * + * { | |
margin-top: 40px; | |
} | |
.ghost { | |
display: flex; | |
flex-direction: row; | |
flex-wrap: wrap; | |
align-items: center; | |
padding-left: 10px; | |
padding-right: 10px; | |
} | |
.ghost + .ghost { | |
margin-top: 8px; | |
} | |
.ghost > * + * { | |
margin-left: 10px; | |
} | |
.ghost-name { | |
font-weight: bold; | |
width: 100px; | |
} | |
.ghost-evidence { | |
font-size: 12px; | |
} | |
form, form > * + * { | |
margin-top: 10px; | |
} | |
.option-headline { | |
font-weight: bold; | |
} | |
.evidence { | |
width: 100%; | |
display: flex; | |
flex-direction: row; | |
align-items: center; | |
justify-content: space-between; | |
} | |
.evidence-proven { | |
color: greenyellow; | |
} | |
.evidence-excluded { | |
color: #5d0000; | |
} | |
.ghost-proven { | |
border: 1px solid greenyellow; | |
background-color: darkgreen; | |
} | |
.ghost-excluded, ghost-excluded > * { | |
text-decoration: line-through; | |
color: #5d0000; | |
} | |
.label { | |
width: 120px; | |
} | |
.option { | |
margin-left: 30px; | |
} | |
</style> | |
</head> | |
<body> | |
<div class="content"> | |
<h1 id="headline">Phasmo-Praktikant</h1> | |
<div id="overview" class="overview"></div> | |
<div id="controls" class="controls"> | |
<div class="evidence"> | |
<div class="label"></div> | |
<div class="option-headline">Nein</div> | |
<div class="option-headline">?</div> | |
<div class="option-headline">Ja</div> | |
</div> | |
<form id="form"></form> | |
</div> | |
</div> | |
<script type="application/ecmascript"> | |
const evidence = { | |
"1": "Box", | |
"2": "Fingerabd.", | |
"3": "Buch", | |
"4": "Gefriertemp.", | |
"5": "EMF - 5", | |
"6": "Orb", | |
} | |
const kinds = [ | |
{ name: "Spirit", evidence: ["1", "2", "3"] }, | |
{ name: "Gespenst", evidence: ["2", "4", "1"] }, | |
{ name: "Phantom", evidence: ["5", "6", "4"] }, | |
{ name: "Poltergeist", evidence: ["1", "2", "6"] }, | |
{ name: "Banshee", evidence: ["5", "2", "4"] }, | |
{ name: "Dshinn", evidence: ["1", "6", "5"] }, | |
{ name: "Mare", evidence: ["1", "6", "4"] }, | |
{ name: "Revenant", evidence: ["5", "2", "3"] }, | |
{ name: "Shade", evidence: ["5", "6", "3"] }, | |
{ name: "Dämon", evidence: ["1", "3", "4"] }, | |
{ name: "Yurei", evidence: ["6", "3", "4"] }, | |
{ name: "Oni", evidence: ["5", "1", "3"] }, | |
] | |
function getEvidences(sel, e) { | |
return e.evidence.map((evId) => { | |
const name = evidence[evId] | |
const ev = document.createElement("div") | |
const status = parseInt(sel[evId]); | |
let statusClass = status === -1 ? "evidence-excluded" : status === 1 ? "evidence-proven" : "tbd"; | |
ev.classList.add("ghost-evidence") | |
ev.classList.add(statusClass) | |
ev.innerHTML = name | |
return ev | |
}) | |
} | |
function createEvidenceControls(name, id) { | |
const div = document.createElement("div") | |
const label = document.createElement("div") | |
const rdo1 = document.createElement("input") | |
const rdo2 = document.createElement("input") | |
const rdo3 = document.createElement("input") | |
div.classList.add("evidence") | |
label.innerHTML = name | |
label.classList.add("label") | |
rdo1.setAttribute("type", "radio") | |
rdo1.setAttribute("name", "evidence_" + id) | |
rdo1.setAttribute("value", "-1") | |
rdo1.classList.add("option") | |
rdo2.setAttribute("type", "radio") | |
rdo2.setAttribute("name", "evidence_" + id) | |
rdo2.setAttribute("value", "0") | |
rdo2.setAttribute("checked", "checked") | |
rdo2.classList.add("option") | |
rdo3.setAttribute("type", "radio") | |
rdo3.setAttribute("name", "evidence_" + id) | |
rdo3.setAttribute("value", "1") | |
rdo3.classList.add("option") | |
div.appendChild(label) | |
div.appendChild(rdo1) | |
div.appendChild(rdo2) | |
div.appendChild(rdo3) | |
return div; | |
} | |
function getProofs(ghost, sel) { | |
return ghost.evidence.filter((ev) => parseInt(sel[ev]) > 0).length; | |
} | |
function getExcludes(ghost, sel) { | |
return ghost.evidence.filter((ev) => parseInt(sel[ev]) < 0).length; | |
} | |
function getScoreClass(e, sel, proofs) { | |
const scoredProofs = getProofs(e, sel); | |
const matchedExcluded = getExcludes(e, sel); | |
let scoreClass = "tbd" | |
if (matchedExcluded > 0) { | |
scoreClass = "ghost-excluded" | |
} else if (scoredProofs < proofs) { | |
scoreClass = "ghost-excluded" | |
} | |
if (scoredProofs === 3) { | |
scoreClass = "ghost-proven" | |
} | |
return scoreClass; | |
} | |
function renderOverview() { | |
const sel = getEvidenceSelections(); | |
const proofs = Object.keys(sel).filter((e) => parseInt(sel[e]) === 1).length; | |
const overview = document.getElementById("overview"); | |
while (overview.firstChild) { | |
overview.firstChild.remove() | |
} | |
kinds.map((e) => { | |
const ghost = document.createElement("div") | |
const name = document.createElement("div") | |
const scoreClass = getScoreClass(e, sel, proofs); | |
ghost.classList.add("ghost") | |
ghost.classList.add(scoreClass) | |
name.innerHTML = e.name | |
name.classList.add("ghost-name") | |
ghost.appendChild(name) | |
getEvidences(sel, e).map((ev) => ghost.appendChild(ev)) | |
overview.appendChild(ghost) | |
}) | |
} | |
function renderControls() { | |
const form = document.getElementById("form") | |
form.addEventListener("change", onChangeForm) | |
for (let k of Object.keys(evidence)) { | |
form.appendChild(createEvidenceControls(evidence[k], k)) | |
} | |
} | |
function getEvidenceSelections() { | |
const form = document.getElementById("form") | |
const fd = new FormData(form) | |
const evidenceSelection = {}; | |
Object.keys(evidence).map((k) => { | |
evidenceSelection[k] = fd.get("evidence_" + k) | |
}) | |
return evidenceSelection; | |
} | |
function onChangeForm(_) { | |
renderOverview(); | |
} | |
window.onload = () => { | |
document.getElementById("headline").addEventListener("click", () => location.reload()) | |
renderControls() | |
renderOverview() | |
} | |
</script> | |
</body> | |
</html> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment