Skip to content

Instantly share code, notes, and snippets.

@oliverbooth
Created January 4, 2021 00:39
Show Gist options
  • Save oliverbooth/3ef1c9a5c7a92fa588d2584b1fc16279 to your computer and use it in GitHub Desktop.
Save oliverbooth/3ef1c9a5c7a92fa588d2584b1fc16279 to your computer and use it in GitHub Desktop.
QoL Enhancements for https://phasmophobiatracker.site/
// ==UserScript==
// @name Phasmophobia Evidence Tracker Enhanced
// @version 1
// @namespace https://phasmophobiatracker.site/
// @run-at document-start
// ==/UserScript==
const addJQuery = function(callback) {
const script = document.createElement('script');
script.setAttribute('src', '//ajax.googleapis.com/ajax/libs/jquery/1/jquery.min.js');
script.addEventListener('load', function() {
const script = document.createElement('script');
script.textContent = 'window.jQ=jQuery.noConflict(true);(' + callback.toString() + ')();';
document.body.appendChild(script);
});
document.body.appendChild(script);
}
const run = function() {
const sanitizeClassName = function(name) {
return name.toLowerCase().replace(' ', '-');
}
class Evidence {
constructor(name, klass) {
this._name = name;
this._class = klass || sanitizeClassName(name);
}
class() {
return this._class;
}
name() {
return this._name;
}
};
const Evidences = {
SpiritBox: new Evidence("Spirit Box"),
Fingerprints: new Evidence("Fingerprints"),
GhostWriting: new Evidence("Ghost Writing"),
FreezingTemperatures: new Evidence("Freezing Temperatures", "freezing-temp"),
EMF5: new Evidence("EMF 5", "emf"),
GhostOrbs: new Evidence("Ghost Orbs")
};
const matchEvidence = function(name) {
for (let evidence of Object.values(Evidences)) {
if (evidence.class() == name) {
return evidence;
}
}
return null;
};
class Ghost {
constructor(name, evidence) {
this._name = name;
this._class = sanitizeClassName(name);
this._evidence = evidence;
}
class() {
return this._class;
}
evidence() {
return this._evidence;
}
name() {
return this._name;
}
hasEvidence(evidence) {
if (!(evidence instanceof Evidence)) {
return false;
}
for (const e of this._evidence) {
if (evidence.class() == e.class()) {
return true;
}
}
return false;
}
};
const Ghosts = {
Spirit: new Ghost("Spirit", [ Evidences.SpiritBox, Evidences.Fingerprints, Evidences.GhostWriting ]),
Wraith: new Ghost("Wraith", [ Evidences.SpiritBox, Evidences.Fingerprints, Evidences.FreezingTemperatures ]),
Phantom: new Ghost("Phantom", [ Evidences.FreezingTemperatures, Evidences.EMF5, Evidences.GhostOrbs ]),
Poltergeist: new Ghost("Poltergeist", [ Evidences.SpiritBox, Evidences.Fingerprints, Evidences.GhostOrbs ]),
Banshee: new Ghost("Banshee", [ Evidences.Fingerprints, Evidences.FreezingTemperatures, Evidences.EMF5 ]),
Jinn: new Ghost("Jinn", [ Evidences.SpiritBox, Evidences.EMF5, Evidences.GhostOrbs ]),
Mare: new Ghost("Mare", [ Evidences.SpiritBox, Evidences.FreezingTemperatures, Evidences.GhostOrbs ]),
Revenant: new Ghost("Revenant", [ Evidences.Fingerprints, Evidences.GhostWriting, Evidences.EMF5 ]),
Shade: new Ghost("Shade", [ Evidences.GhostWriting, Evidences.EMF5, Evidences.GhostOrbs ]),
Demon: new Ghost("Demon", [ Evidences.SpiritBox, Evidences.GhostWriting, Evidences.FreezingTemperatures ] ),
Yurei: new Ghost("Yurei", [ Evidences.GhostWriting, Evidences.FreezingTemperatures, Evidences.GhostOrbs ]),
Oni: new Ghost("Oni", [ Evidences.SpiritBox, Evidences.GhostWriting, Evidences.EMF5 ])
};
const includeGhost = function(ghost) {
const DOM = document.querySelector(`#row-${ghost.class()}`);
DOM.style.backgroundColor = '';
DOM.classList.add('row-active');
};
const excludeGhost = function(ghost, highlight) {
const DOM = document.querySelector(`#row-${ghost.class()}`);
if (highlight === true) {
DOM.style.backgroundColor = '#f44336';
} else {
DOM.style.backgroundColor = '';
}
DOM.classList.remove('row-active');
};
const resetGhost = function(ghost) {
const DOM = document.querySelector(`#row-${ghost.class()}`);
DOM.style.backgroundColor = '';
DOM.classList.remove('row-active');
};
const extendGhostTable = function() {
const table = document.getElementById('evidence-table');
const tr = table.getElementsByTagName('tr')[0];
const th = tr.getElementsByTagName('th')[0];
th.colSpan = 1;
tr.insertCell(1).innerText = '✅';
tr.insertCell(2).innerText = '❌';
const tbody = table.getElementsByTagName('tbody')[0];
for (let tr of tbody.children) {
const td = tr.getElementsByTagName("td");
if (td.length == 2) {
const rowEvidence = tr.getElementsByTagName('input')[0].value;
tr.children[1].innerHTML = `<input class="include-evidence" type="checkbox" name="check-${rowEvidence}" id="check-${rowEvidence}" value="${rowEvidence}">`
tr.insertCell(2).innerHTML = `<input class="exclude-evidence" type="checkbox" name="check-ex-${rowEvidence}" id="check-ex-${rowEvidence}" value="${rowEvidence}">`
} else {
td[0].colSpan = 3;
}
}
};
const getIncludedEvidence = function () {
const checkedEvidence = [];
jQ('.include-evidence:checked').each(function() {
checkedEvidence.push(matchEvidence(jQ(this).val()));
});
return checkedEvidence;
};
const getExcludedEvidence = function () {
const checkedEvidence = [];
jQ('.exclude-evidence:checked').each(function() {
checkedEvidence.push(matchEvidence(jQ(this).val()));
});
return checkedEvidence;
};
const updateGhostTable = function() {
const included = getIncludedEvidence();
const excluded = getExcludedEvidence();
const ghosts = Object.values(Ghosts);
let possibleGhosts = [];
const impossibleGhosts = [];
for (const ghost of ghosts) {
resetGhost(ghost);
let hasAllEvidence = included.length > 0;
for (const evidence of included) {
if (!ghost.hasEvidence(evidence)) {
hasAllEvidence = false;
}
}
if (hasAllEvidence) {
possibleGhosts.push(ghost);
}
}
for (const ghost of possibleGhosts) {
includeGhost(ghost);
}
if (excluded.length) {
for (const ghost of ghosts) {
for (const evidence of excluded) {
if (ghost.hasEvidence(evidence)) {
impossibleGhosts.push(ghost);
break;
}
}
}
possibleGhosts = possibleGhosts.filter(g => impossibleGhosts.includes(g));
for (const ghost of possibleGhosts) {
includeGhost(ghost);
}
for (const ghost of impossibleGhosts) {
excludeGhost(ghost, possibleGhosts.length == 0);
}
}
};
const resetEvents = function() {
jQ('input[type="checkbox"]').on('click', () => {
const el = jQ(this);
if (el.prop(':checked')) {
if (el.class().includes('include-')) {
jQ(`#check-ex-${el.val()}`).prop('checked', false);
} else {
jQ(`#check-${el.val()}`).prop('checked', true);
}
}
updateGhostTable();
});
};
extendGhostTable();
resetEvents();
};
window.addEventListener('load', () => addJQuery(run), false);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment