summaryrefslogtreecommitdiff
path: root/pages/toys.php
diff options
context:
space:
mode:
Diffstat (limited to 'pages/toys.php')
-rw-r--r--pages/toys.php915
1 files changed, 915 insertions, 0 deletions
diff --git a/pages/toys.php b/pages/toys.php
new file mode 100644
index 0000000..e4e5def
--- /dev/null
+++ b/pages/toys.php
@@ -0,0 +1,915 @@
+<?php
+
+require_once $_SERVER['DOCUMENT_ROOT'] . "/includes/session.php"; global $isLoggedIn;
+if (!$isLoggedIn) header("Location: /-/login") and die();
+
+if (isset($_POST['deleteAction'])) {
+ $data = json_decode(file_get_contents($_SERVER['DOCUMENT_ROOT'] . "/includes/data/toys.json"), true);
+
+ $selected = null;
+ $selectedIndex = -1;
+ $id = $_POST['action'];
+
+ foreach ($data as $index => $item) {
+ if ($item["id"] === $id) {
+ $selectedIndex = $index;
+ $selected = $item;
+ break;
+ }
+ }
+
+ if ($selected === null) {
+ header("Location: /-/toys");
+ die();
+ }
+
+ unset($data[$selectedIndex]);
+ file_put_contents($_SERVER['DOCUMENT_ROOT'] . "/includes/data/toys.json", utf8_encode(json_encode($data)));
+ header("Location: /-/toys/?d&id=" . $id);
+ die();
+}
+
+if (isset($_POST['updateAction'])) {
+ $data = json_decode(file_get_contents($_SERVER['DOCUMENT_ROOT'] . "/includes/data/toys.json"), true);
+
+ $selected = null;
+ $selectedIndex = -1;
+ $id = $_POST['action'];
+
+ foreach ($data as $index => $item) {
+ if ($item["id"] === $id) {
+ $selectedIndex = $index;
+ $selected = $item;
+ break;
+ }
+ }
+
+ if ($selected === null) {
+ header("Location: /-/toys");
+ die();
+ }
+
+ if (isset($_POST["sexual"])) {
+ $selected["sexual"] = true;
+ } else {
+ $selected["sexual"] = false;
+ }
+
+ if (isset($_POST["name"])) $selected["name"] = strip_tags(trim($_POST["name"]));
+ if (isset($_POST["usage"])) $selected["usage"] = strip_tags(trim($_POST["usage"]));
+ if (isset($_POST["irl"])) $selected["irl"] = strip_tags(trim($_POST["irl"]));
+ if (isset($_POST["keywords"])) $selected["keywords"] = array_map(function ($i) {
+ return trim($i);
+ }, explode(",", strip_tags(trim($_POST["keywords"]))));
+ if (isset($_POST["description"])) $selected["description"] = strip_tags(trim($_POST["description"]));
+ if (isset($_POST["water"])) $selected["water"] = match ($_POST["water"]) {
+ "0" => "out",
+ "1" => "in",
+ "2" => "both",
+ "3" => "playground"
+ };
+
+ if (isset($_POST["relations"])) {
+ $ponies = [];
+
+ foreach ($_POST["relations"] as $relation => $d) {
+ $ponies[] = [
+ "members" => explode("-", $relation),
+ "deprecated" => isset($d["deprecated"]),
+ "sexual" => isset($d["sexual"])
+ ];
+ }
+
+ $selected["ponies"] = $ponies;
+ }
+
+ global $_PROFILE;
+ if ($_PROFILE['login'] === "raindrops" && isset($_POST["verified"])) {
+ $selected["verified"] = true;
+ } else {
+ unset($selected["verified"]);
+ }
+
+ if (isset($_POST["untested"])) {
+ $selected["untested"] = true;
+ } else {
+ unset($selected["untested"]);
+ }
+
+ $data[$selectedIndex] = $selected;
+ file_put_contents($_SERVER['DOCUMENT_ROOT'] . "/includes/data/toys.json", utf8_encode(json_encode($data)));
+
+ header("Location: /-/toys/" . $id);
+ die();
+}
+
+if (isset($_POST['createAction'])) {
+ require_once $_SERVER['DOCUMENT_ROOT'] . "/includes/random.php";
+ $data = json_decode(file_get_contents($_SERVER['DOCUMENT_ROOT'] . "/includes/data/toys.json"), true);
+
+ if (!isset($_POST["name"]) || !isset($_POST["water"])) {
+ header("Location: /-/toys");
+ die();
+ }
+ if (trim($_POST["name"]) === "" || !is_numeric($_POST["water"])) {
+ header("Location: /-/toys");
+ die();
+ }
+
+ $water = match ($_POST["water"]) {
+ "0" => "out",
+ "1" => "in",
+ "2" => "both",
+ "3" => "playground"
+ };
+ $name = strip_tags(trim($_POST["name"]));
+ $id = random();
+
+ $ponies = [];
+
+ if (isset($_POST["relations"])) {
+ foreach ($_POST["relations"] as $relation => $_) {
+ $ponies[] = [
+ "members" => explode("-", $relation),
+ "deprecated" => false
+ ];
+ }
+ }
+
+ if (isset($_POST["sexual"])) {
+ $sexual = true;
+ } else {
+ $sexual = false;
+ }
+
+ $data[] = [
+ "id" => $id,
+ "name" => $name,
+ "water" => $water,
+ "description" => null,
+ "ponies" => $ponies,
+ "usage" => null,
+ "irl" => null,
+ "keywords" => [],
+ "sexual" => $sexual
+ ];
+
+ file_put_contents($_SERVER['DOCUMENT_ROOT'] . "/includes/data/toys.json", utf8_encode(json_encode($data)));
+ header("Location: /-/toys/" . $id);
+ die();
+}
+
+global $pagename;
+$parts = explode("/", $pagename);
+$data = json_decode(file_get_contents($_SERVER['DOCUMENT_ROOT'] . "/includes/data/toys.json"), true);
+
+$selected = null;
+$title = "Toys database";
+
+if (isset($parts[1])) {
+ $id = $parts[1];
+
+ foreach ($data as $item) {
+ if ($item["id"] === $id) {
+ $selected = $item;
+ break;
+ }
+ }
+
+ if ($selected === null) {
+ header("Location: /-/toys/?nf&id=" . $id);
+ die();
+ } else {
+ $title = $selected["name"] . " · Toys database";
+ }
+}
+
+require_once $_SERVER['DOCUMENT_ROOT'] . '/includes/header.php';
+require_once $_SERVER['DOCUMENT_ROOT'] . '/includes/keywords.php';
+
+if (!file_exists($_SERVER['DOCUMENT_ROOT'] . "/includes/data/toys.json")) file_put_contents($_SERVER['DOCUMENT_ROOT'] . "/includes/data/toys.json", "[]");
+
+global $_PROFILE;
+
+?>
+
+<script src="/assets/editor/fuse.js"></script>
+
+<br>
+<div class="container">
+ <div id="<?= isset($parts[1]) ? "page-content" : "" ?>">
+ <?php if (isset($_GET['nf'])): ?>
+ <div class="alert alert-danger alert-dismissible">
+ <button onclick='window.history.pushState({"html":null,"pageTitle":document.title},"", "/-/toys/");' type="button" class="btn-close" data-bs-dismiss="alert" style="filter: none !important;"></button>
+ <b>Error: </b> The requested toy (<code><?= strip_tags($_GET['id'] ?? "-") ?></code>) was not found, it may have been deleted or has never existed.
+ </div>
+ <?php endif; ?>
+
+ <?php if (isset($_GET['d'])): ?>
+ <div class="alert alert-success alert-dismissible">
+ <button onclick='window.history.pushState({"html":null,"pageTitle":document.title},"", "/-/toys");' type="button" class="btn-close" data-bs-dismiss="alert" style="filter: none !important;"></button>
+ <b>Success: </b> The toy with ID <code><?= strip_tags($_GET['id'] ?? "-") ?></code> has been successfully deleted.
+ </div>
+ <?php endif; ?>
+
+ <?php if (isset($parts[1])): ?>
+
+ <h2>
+ <span style="vertical-align: middle;"><?= $selected["name"] ?></span>
+ <a href="/-/toys" class="small btn btn-outline-light" style="float:right;margin-top:5px;vertical-align:middle;opacity:1 !important; color:white;">Back</a>
+ </h2>
+ <p>
+ <a onclick="event.target.blur();" data-bs-toggle="modal" data-bs-target="#editor" href="#">Edit</a> ·
+ <?php if ($item["sexual"]): ?>
+ <span style="" class="badge rounded-pill bg-danger">Sexual</span>
+ <?php else: ?>
+ <span style="" class="badge rounded-pill bg-success">Pleasurable</span>
+ <?php endif; ?>
+ <?php if (!isset($item["verified"])): ?><span class="badge bg-warning rounded-pill text-black">Unverified</span><?php endif; ?>
+ <?php if (isset($item["untested"])): ?><span class="badge bg-info rounded-pill">Untested</span><?php endif; ?>
+ <?php if ($item["water"] === "in"): ?>
+ <span style="" class="badge rounded-pill bg-primary">Underwater</span>
+ <?php elseif ($item["water"] === "out"): ?>
+ <span style="background-color:#d63384;" class="badge rounded-pill">Outside of water</span>
+ <?php elseif ($item["water"] === "playground"): ?>
+ <span class="badge rounded-pill" style="background-color:#20c997;">In playground</span>
+ <?php else: ?>
+ <span style="" class="badge rounded-pill bg-primary">Underwater</span>
+ <span style="background-color:#d63384;" class="badge rounded-pill">Outside of water</span>
+ <?php endif; ?>
+ </p>
+
+ <?php if (isset($selected["description"]) && trim($selected["description"]) !== ""): ?>
+ <?= replaceKeyWords(str_replace("\n", "<br>", strip_tags($selected["description"]))); ?>
+ <?php else: ?>
+ <p><i>No description provided for this toy. Enter edit mode to add a description to this toy.</i></p>
+ <?php endif; ?>
+
+ <hr>
+ <b>Can be used by:</b>
+ <?php
+
+ $hasMultipleTypes = false;
+ $seenType = "";
+
+ foreach ($selected["ponies"] as $relation) {
+ if (isset($relation["sexual"]) && $relation["sexual"]) {
+ $type = "sexual";
+ } else {
+ $type = "affectionate";
+ }
+
+ if ($type !== $seenType && $seenType !== "") {
+ $hasMultipleTypes = true;
+ }
+
+ if (trim($seenType) === "") {
+ $seenType = $type;
+ }
+ }
+
+ ?>
+ <ul>
+ <?php foreach ($selected["ponies"] as $relation): if (!$relation["deprecated"]):
+
+ $member1 = getMemberWithoutSystem($relation["members"][0]);
+ $member2 = getMemberWithoutSystem($relation["members"][1]);
+
+ ?>
+ <li>
+ <img style="vertical-align: middle;width:24px;height:24px;" src="/assets/uploads/pt-<?= $member1["name"] ?>.png">
+ <img style="vertical-align: middle;width:24px;height:24px;" src="/assets/uploads/pt-<?= $member2["name"] ?>.png">
+
+ <span style="vertical-align: middle;"><?= getMiniName($member1["display_name"] ?? $member1["name"]) ?> and <?= getMiniName($member2["display_name"] ?? $member2["name"]) ?></span>
+ </li>
+ <?php endif; endforeach; ?>
+ <?php foreach ($selected["ponies"] as $relation): if ($relation["deprecated"]):
+
+ $member1 = getMemberWithoutSystem($relation["members"][0]);
+ $member2 = getMemberWithoutSystem($relation["members"][1]);
+
+ ?>
+ <li>
+ <span style="opacity:.5;">
+ <img style="vertical-align: middle;width:24px;height:24px;" src="/assets/uploads/pt-<?= $member1["name"] ?>.png">
+ <img style="vertical-align: middle;width:24px;height:24px;" src="/assets/uploads/pt-<?= $member2["name"] ?>.png">
+
+ <span style="vertical-align: middle;"><?= getMiniName($member1["display_name"] ?? $member1["name"]) ?> and <?= getMiniName($member2["display_name"] ?? $member2["name"]) ?></span>
+ </span>
+ <span class="badge bg-danger rounded-pill">Deprecated</span>
+ </li>
+ <?php endif; endforeach; ?>
+ </ul>
+
+ <div style="margin-top:10px;"></div>
+
+ <b>Usage:</b><br>
+ <?php if (isset($selected["usage"]) && trim($selected["usage"]) !== ""): ?>
+ <?php
+
+ $lines = explode("\n", strip_tags($selected["usage"]));
+
+ if (count($lines) > 1) echo("<ul>");
+
+ foreach ($lines as $line) {
+ if (count($lines) > 1) echo("<li>");
+
+ $parts = explode(":", $line);
+
+ if (count($parts) > 1 && strlen($parts[0]) < 30) {
+ $p0 = $parts[0]; array_shift($parts);
+ echo(replaceKeyWords("<b>" . $p0 . ":</b>" . implode(":", $parts)));
+ } else {
+ echo(replaceKeyWords(implode(":", $parts)));
+ }
+
+ if (count($lines) > 1) echo("</li>");
+ }
+
+ if (count($lines) > 1) echo("</ul>");
+
+ ?>
+ <?php else: ?>
+ <p><i>No usage provided for this toy. Enter edit mode to add usage information to this toy.</i></p>
+ <?php endif; ?>
+
+ <div style="margin-top:10px;"></div>
+
+ <b>Instructions to craft in real life:</b><br>
+ <?php if (isset($selected["irl"]) && trim($selected["irl"]) !== ""): ?>
+ <?= replaceKeyWords(strip_tags($selected["irl"])) ?>
+ <?php else: ?>
+ <p><i>This toy is not craftable in real life.</i></p>
+ <?php endif; ?>
+
+ <hr>
+
+ <h4>Similar toys</h4>
+ <div class="row">
+ <?php
+
+ $names = [];
+ $currentName = $selected["name"];
+ $namesByDistance = [];
+
+ foreach ($data as $action) {
+ if ($action["name"] !== $currentName) $names[$action["name"]] = [
+ "id" => $action["id"],
+ "water" => $action["water"],
+ "ponies" => $action["ponies"]
+ ];
+ }
+
+ foreach ($names as $name => $data) {
+ $namesByDistance[] = [
+ "name" => $name,
+ "distance" => levenshtein($currentName, $name) + ((int)($data["type"] !== $selected["type"]) * 2),
+ "id" => $data["id"],
+ "water" => $data["water"],
+ "ponies" => $data["ponies"]
+ ];
+ }
+
+ uasort($namesByDistance, function ($a, $b) use ($selected) {
+ return $a["distance"] - $b["distance"];
+ });
+
+ foreach ($namesByDistance as $item) {
+ echo("<!-- " . $currentName . " <-> " . $item["name"] . " => " . $item["distance"] . " (artif: " . ((int)($item["water"] !== $selected["water"]) * 10) . ") -->");
+ }
+
+ $index = 0;
+ foreach ($namesByDistance as $item): if ($index < 3):
+ ?>
+ <div class="col-md-4" style="margin-bottom:20px;">
+ <a class="linked-card" href="/-/actions/<?= $item["id"] ?>"><div class="card">
+ <div class="card-body">
+ <h4 class="card-title"><?= $item["name"] ?></h4>
+ <p class="card-text">
+ <?php
+
+ $uniquePonies = [];
+
+ foreach ($item["ponies"] as $ponies) {
+ foreach ($ponies["members"] as $member) {
+ if (isset($uniquePonies[$member]) && !$uniquePonies[$member]) {
+ $uniquePonies[$member] = false;
+ } else {
+ $uniquePonies[$member] = $ponies["deprecated"];
+ }
+ }
+ }
+
+ foreach ($uniquePonies as $name => $deprecated): if (!$deprecated): ?>
+ <span data-bs-toggle="tooltip" title="<?= getMemberWithoutSystem($name)["display_name"] ?? getMemberWithoutSystem($name)["display_name"] ?>" style="display: inline-block;">
+ <img src="/assets/uploads/pt-<?= getMemberWithoutSystem($name)["name"] ?>.png" style="width:32px;">
+ </span>
+ <?php endif; endforeach; ?>
+ <?php foreach ($uniquePonies as $name => $deprecated): if ($deprecated): ?>
+ <span data-bs-toggle="tooltip" data-bs-html="true" title="<i><?= strip_tags(getMemberWithoutSystem($name)["display_name"] ?? getMemberWithoutSystem($name)["name"]) ?></i>" style="opacity:.5;display: inline-block;">
+ <img src="/assets/uploads/pt-<?= getMemberWithoutSystem($name)["name"] ?>.png" style="width:32px;">
+ </span>
+ <?php endif; endforeach; ?>
+ </p>
+ <?php if ($item["sexual"]): ?>
+ <span style="" class="badge rounded-pill bg-danger">Sexual</span>
+ <?php else: ?>
+ <span style="" class="badge rounded-pill bg-success">Pleasurable</span>
+ <?php endif; ?>
+ <?php if ($item["water"] === "in"): ?>
+ <span style="" class="badge rounded-pill bg-primary">Underwater</span>
+ <?php elseif ($item["water"] === "out"): ?>
+ <span style="background-color:#d63384;" class="badge rounded-pill">Outside of water</span>
+ <?php elseif ($item["water"] === "playground"): ?>
+ <span class="badge rounded-pill" style="background-color:#20c997;">In playground</span>
+ <?php else: ?>
+ <span style="" class="badge rounded-pill bg-primary">Underwater</span>
+ <span style="background-color:#d63384;" class="badge rounded-pill">Outside of water</span>
+ <?php endif; ?>
+ </div>
+ </div></a>
+ </div>
+ <?php $index++; endif; endforeach; ?>
+ </div>
+
+ <div class="modal fade" id="editor">
+ <div class="modal-dialog">
+ <div class="modal-content">
+ <div class="modal-header">
+ <h4 class="modal-title">Edit toy</h4>
+ <button type="button" class="btn-close" data-bs-dismiss="modal"></button>
+ </div>
+
+ <div class="modal-body">
+ <form method="post" style="display:inline;">
+ <input type="text" placeholder="Toy name" name="name" class="form-control" style="color:white;background:#111;border-color:#222;margin-bottom:10px;" value="<?= str_replace('"', '&quot;', $selected["name"]) ?>">
+ <select name="water" class="form-select" style='color:white;background-color:#111;border-color:#222;background-image:url("data:image/svg+xml,%3csvg xmlns=&apos;http://www.w3.org/2000/svg&apos; viewBox=&apos;0 0 16 16&apos;%3e%3cpath fill=&apos;none&apos; stroke=&apos;%23ffffff&apos; stroke-linecap=&apos;round&apos; stroke-linejoin=&apos;round&apos; stroke-width=&apos;2&apos; d=&apos;M2 5l6 6 6-6&apos;/%3e%3c/svg%3e");margin-bottom:10px;'>
+ <option value="0" <?= $selected["water"] === "out" ? "selected" : "" ?>>Usable outside of the water</option>
+ <option value="1" <?= $selected["water"] === "in" ? "selected" : "" ?>>Usable inside of the water</option>
+ <option value="2" <?= $selected["water"] === "both" ? "selected" : "" ?>>Usable both inside and outside</option>
+ <option value="3" <?= $selected["water"] === "playground" ? "selected" : "" ?>>Usable in a playground</option>
+ </select>
+
+ <label style="margin-left:5px;">
+ <input <?= ($selected["sexual"] ?? false) ? "checked" : "" ?> type="checkbox" name="sexual">
+ Sexual toy
+ </label><br>
+
+ <label style="margin-left:5px;">
+ <input <?= ($selected["verified"] ?? false) ? "checked" : "" ?> <?= $_PROFILE['login'] !== "raindrops" ? "disabled" : "" ?> type="checkbox" name="verified">
+ Mark as verified
+ </label><br>
+
+ <label style="margin-left:5px;">
+ <input <?= ($selected["untested"] ?? false) ? "checked" : "" ?> type="checkbox" name="untested">
+ Mark as untested
+ </label>
+
+ <hr>
+
+ <textarea name="description" class="form-control" style="resize: none;color:white;background:#111;border-color:#222;margin-bottom:10px;" placeholder="Description"><?= strip_tags($selected["description"] ?? "") ?></textarea>
+
+ <textarea placeholder="Toy usage" name="usage" class="form-control" style="resize: none;color:white;background:#111;border-color:#222;margin-bottom:10px;"><?= strip_tags($selected["usage"] ?? "") ?></textarea>
+
+ <hr>
+
+ <input type="text" placeholder="Keywords (comma-separated)" name="keywords" class="form-control" style="color:white;background:#111;border-color:#222;margin-bottom:10px;" value="<?= str_replace('"', '&quot;', implode(",", $selected["keywords"] ?? [])) ?>">
+
+ <input type="text" placeholder="Instructions to craft IRL" name="irl" class="form-control" style="color:white;background:#111;border-color:#222;" value="<?= str_replace('"', '&quot;', $selected["irl"] ?? "") ?>">
+
+ <hr>
+
+ <p>Select the groups of ponies who can use this toy:</p>
+ <?php
+
+ $relations = [];
+ $actions = json_decode(file_get_contents($_SERVER['DOCUMENT_ROOT'] . "/includes/data/actions.json"), true);
+
+ foreach ($actions as $action) {
+ if ($action["type"] !== "sexual") continue;
+
+ foreach ($action["ponies"] as $ponies) {
+ $id = implode("", $ponies["members"]);
+
+ $member = getMemberWithoutSystem($ponies["members"][0]);
+ $otherMember = getMemberWithoutSystem($ponies["members"][1]);
+
+ $parts = [
+ $member["id"],
+ $otherMember["id"]
+ ];
+
+ asort($parts);
+
+ $relations[implode("-", $parts)] = [
+ "name" => getMiniName($member["display_name"] ?? $member["name"]) . " and " . getMiniName($otherMember["display_name"] ?? $otherMember["name"]),
+ "images" => [
+ file_exists($_SERVER['DOCUMENT_ROOT'] . "/assets/uploads/pt-$member[name].png") ? "/assets/uploads/pt-$member[name].png" : "/assets/uploads/pt.png",
+ file_exists($_SERVER['DOCUMENT_ROOT'] . "/assets/uploads/pt-$otherMember[name].png") ? "/assets/uploads/pt-$otherMember[name].png" : "/assets/uploads/pt.png",
+ ]
+ ];
+ }
+ }
+
+ $inFileRelations = [];
+ $inFileDeprecations = [];
+
+ foreach ($selected["ponies"] as $ponies) {
+ $inFileRelations[] = $ponies["members"][0] . "-" . $ponies["members"][1];
+
+ if (isset($ponies["deprecated"]) && $ponies["deprecated"]) $inFileDeprecations[] = $ponies["members"][0] . "-" . $ponies["members"][1];
+ }
+
+ foreach ($relations as $id => $relation):
+ ?>
+ <label style="display:block;" class="creator-relation <?= $selected["type"] === "mixed" ? "mixed" : "" ?> <?= in_array($id, $inFileRelations) ? "checked" : "" ?>">
+ <input <?= in_array($id, $inFileRelations) ? "checked" : "" ?> name="relations[<?= $id ?>][member]" style="display:none;" type="checkbox" class="checkbox-input">
+
+ <img style="vertical-align: middle;width:24px;height:24px;" src="<?= $relation["images"][0] ?>">
+ <img style="vertical-align: middle;width:24px;height:24px;" src="<?= $relation["images"][1] ?>">
+ <span style="vertical-align: middle;"><?= $relation["name"] ?></span>
+
+ <label class="deprecated" style="display:none;margin-left: 20px;margin-top: 5px;">
+ <input <?= in_array($id, $inFileDeprecations) ? "checked" : "" ?> name="relations[<?= $id ?>][deprecated]" type="checkbox">
+ Mark as deprecated
+ </label>
+ </label>
+ <?php endforeach; ?>
+
+ <br>
+ <input type="hidden" name="updateAction">
+ <input type="hidden" name="action" value="<?= $selected["id"] ?>">
+ <input type="submit" class="btn btn-primary" value="Save">
+ </form>
+ <form method="post" style="display:inline;">
+ <input type="hidden" name="deleteAction">
+ <input type="hidden" name="action" value="<?= $selected["id"] ?>">
+ <input type="submit" class="btn btn-danger" value="Delete">
+ </form>
+ </div>
+ </div>
+ </div>
+ </div>
+
+ <?php else: ?>
+
+ <h2>Toys database</h2>
+ <p><?= count($data) ?> toys (<?= count(array_filter($data, function ($i) {
+ return $i["sexual"] === false;
+ })) ?> non-sexual, <?= count(array_filter($data, function ($i) {
+ return $i["sexual"] === true;
+ })) ?> sexual, <?= count(array_filter($data, function ($i) {
+ return isset($i["untested"]) && $i["untested"];
+ })) ?> untested, <?= count(array_filter($data, function ($i) {
+ return (isset($i["description"]) && trim($i["description"]) === "") || !isset($i["description"]);
+ })) ?> incomplete)</p>
+
+ <input type="text" placeholder="Search for a toy..." class="form-control" style="margin-bottom:15px;color:white;background:#111;border-color:#222;" onchange="search();" onkeydown="search();" onkeyup="search();" id="search">
+
+ <div id="list">
+ <div class="list-group">
+ <?php
+
+ $init = [];
+ foreach ($data as $value) {
+ $init[$value["name"]] = $value;
+ }
+
+ ksort($init);
+
+ $sorted = array_values($init);
+ uasort($sorted, function ($a, $b) {
+ $uniquePonies1 = [];
+
+ foreach ($a["ponies"] as $ponies) {
+ foreach ($ponies["members"] as $member) {
+ $uniquePonies1[$member] = true;
+ }
+ }
+
+ $uniquePonies2 = [];
+
+ foreach ($b["ponies"] as $ponies) {
+ foreach ($ponies["members"] as $member) {
+ $uniquePonies2[$member] = true;
+ }
+ }
+
+ return count($uniquePonies2) - count($uniquePonies1);
+ });
+
+ foreach ($sorted as $item): ?>
+ <a href="/-/toys/<?= $item["id"] ?>" id="action-<?= $item["id"] ?>" style="display:grid;grid-template-columns: 1fr 1fr 0.2fr;" class="list-group-item list-group-item-action action-listing">
+ <div>
+ <span class="<?= trim($item["description"]) === "" ? "text-danger" : "" ?>"><?= $item["name"] ?></span>
+ <?php if (!isset($item["verified"])): ?><span class="badge bg-warning rounded-pill text-black">Unverified</span><?php endif; ?>
+ <?php if (isset($item["untested"])): ?><span class="badge bg-info rounded-pill">Untested</span><?php endif; ?>
+ </div>
+ <div>
+ <?php
+
+ $uniquePonies = [];
+ $longPonyList = false;
+
+ foreach ($item["ponies"] as $ponies) {
+ foreach ($ponies["members"] as $member) {
+ if (isset($uniquePonies[$member]) && !$uniquePonies[$member]) {
+ $uniquePonies[$member] = false;
+ } else {
+ $uniquePonies[$member] = $ponies["deprecated"];
+ }
+ }
+ }
+
+ if (count($uniquePonies) > 6) {
+ $longPonyList = true;
+ }
+
+ $index = 1;
+ foreach ($uniquePonies as $name => $deprecated): if (!$deprecated): ?>
+ <!-- <?= $index ?> -->
+ <span title="<?= getMemberWithoutSystem($name)["display_name"] ?? getMemberWithoutSystem($name)["display_name"] ?>" style="display: inline-block;<?= $longPonyList && $index % 2 === 0 ? "position:absolute;margin-left:-12px;z-index:" . (999 - $index) . ";" : "position:relative;z-index:" . (999 - $index) . ";" ?>">
+ <img src="/assets/uploads/pt-<?= getMemberWithoutSystem($name)["name"] ?>.png" style="width:24px;">
+ </span>
+ <?php $index++; endif; endforeach; ?>
+ <?php foreach ($uniquePonies as $name => $deprecated): if ($deprecated): ?>
+ <span title="<?= strip_tags(getMemberWithoutSystem($name)["display_name"] ?? getMemberWithoutSystem($name)["name"]) ?> (deprecated)" style="opacity:.5;display: inline-block;<?= $longPonyList && $index % 2 === 0 ? "position:absolute;margin-left:-12px;z-index:" . (999 - $index) . ";" : "position:relative;z-index:" . (999 - $index) . ";" ?>">
+ <img src="/assets/uploads/pt-<?= getMemberWithoutSystem($name)["name"] ?>.png" style="width:24px;">
+ </span>
+ <?php $index++; endif; endforeach; ?>
+ </div>
+ <div style="text-align: right;">
+ <?php if ($item["sexual"]): ?>
+ <span style="" class="badge rounded-pill bg-danger">Sexual</span>
+ <?php else: ?>
+ <span style="" class="badge rounded-pill bg-success">Pleasurable</span>
+ <?php endif; ?>
+ </div>
+ </a>
+ <?php endforeach; ?>
+ </div>
+ </div>
+
+ <div id="search-results" class="list-group"></div>
+
+ <div id="page-content">
+ <hr>
+ <p>Not finding what you are looking for? <a onclick="event.target.blur(); document.getElementById('creator-title').focus();" href="#" data-bs-toggle="modal" data-bs-target="#creator">Add a toy.</a></p>
+ </div>
+
+ <script>
+ window.actions = JSON.parse(atob(`<?= base64_encode(json_encode(array_values(json_decode(file_get_contents($_SERVER['DOCUMENT_ROOT'] . "/includes/data/toys.json"), true)))) ?>`));
+ </script>
+
+ <div class="modal fade" id="creator">
+ <div class="modal-dialog">
+ <div class="modal-content">
+ <div class="modal-header">
+ <h4 class="modal-title">Add a new toy</h4>
+ <button type="button" class="btn-close" data-bs-dismiss="modal"></button>
+ </div>
+
+ <div class="modal-body">
+ <form method="post">
+ <input id="creator-title" type="text" placeholder="Toy name" name="name" class="form-control" style="color:white;background:#111;border-color:#222;margin-bottom:10px;">
+ <select name="water" class="form-select" style='color:white;background-color:#111;border-color:#222;background-image:url("data:image/svg+xml,%3csvg xmlns=&apos;http://www.w3.org/2000/svg&apos; viewBox=&apos;0 0 16 16&apos;%3e%3cpath fill=&apos;none&apos; stroke=&apos;%23ffffff&apos; stroke-linecap=&apos;round&apos; stroke-linejoin=&apos;round&apos; stroke-width=&apos;2&apos; d=&apos;M2 5l6 6 6-6&apos;/%3e%3c/svg%3e");margin-bottom:10px;'>
+ <option value="0" selected>Usable outside of the water</option>
+ <option value="1">Usable inside of the water</option>
+ <option value="2">Usable both inside and outside</option>
+ <option value="3">Usable in a playground</option>
+ </select>
+
+ <label style="margin-left:5px;">
+ <input type="checkbox" name="sexual">
+ Sexual toy
+ </label>
+
+ <hr>
+
+ <p>Select the groups of ponies who can use this toy:</p>
+ <?php
+
+ $relations = [];
+ $actions = json_decode(file_get_contents($_SERVER['DOCUMENT_ROOT'] . "/includes/data/actions.json"), true);
+
+ foreach ($actions as $action) {
+ if ($action["type"] !== "sexual") continue;
+
+ foreach ($action["ponies"] as $ponies) {
+ $id = implode("", $ponies["members"]);
+
+ $member = getMemberWithoutSystem($ponies["members"][0]);
+ $otherMember = getMemberWithoutSystem($ponies["members"][1]);
+
+ $parts = [
+ $member["id"],
+ $otherMember["id"]
+ ];
+
+ asort($parts);
+
+ $relations[implode("-", $parts)] = [
+ "name" => getMiniName($member["display_name"] ?? $member["name"]) . " and " . getMiniName($otherMember["display_name"] ?? $otherMember["name"]),
+ "images" => [
+ file_exists($_SERVER['DOCUMENT_ROOT'] . "/assets/uploads/pt-$member[name].png") ? "/assets/uploads/pt-$member[name].png" : "/assets/uploads/pt.png",
+ file_exists($_SERVER['DOCUMENT_ROOT'] . "/assets/uploads/pt-$otherMember[name].png") ? "/assets/uploads/pt-$otherMember[name].png" : "/assets/uploads/pt.png",
+ ]
+ ];
+ }
+ }
+
+ foreach ($relations as $id => $relation):
+ ?>
+ <label style="display:block;" class="creator-relation">
+ <input name="relations[<?= $id ?>]" style="display:none;" type="checkbox" class="checkbox-input">
+
+ <img style="vertical-align: middle;width:24px;height:24px;" src="<?= $relation["images"][0] ?>">
+ <img style="vertical-align: middle;width:24px;height:24px;" src="<?= $relation["images"][1] ?>">
+ <span style="vertical-align: middle;"><?= $relation["name"] ?></span>
+ </label>
+ <?php endforeach; ?>
+
+ <p style="margin-top:10px;">You can add additional data (description, how to use) after adding the toy.</p>
+ <input type="hidden" name="createAction">
+ <input type="submit" class="btn btn-primary" value="Add">
+ </form>
+ </div>
+ </div>
+ </div>
+ </div>
+
+ <?php endif; ?>
+ </div>
+</div>
+
+<!--suppress JSUnresolvedFunction -->
+<script>
+ Array.from(document.getElementsByClassName("checkbox-input")).forEach((el) => {
+ el.onchange = () => {
+ let parent = el.parentElement;
+
+ if (el.checked) {
+ parent.classList.add("checked");
+ } else {
+ parent.classList.remove("checked");
+ }
+ }
+ });
+
+ const fuse = new Fuse(window.actions, {
+ includeScore: true,
+ keys: [
+ {
+ name: 'name',
+ weight: 1
+ },
+ {
+ name: 'description',
+ weight: 1
+ },
+ {
+ name: 'example',
+ weight: 0.7
+ },
+ {
+ name: 'irl',
+ weight: 0.5
+ }
+ ]
+ })
+
+ function search() {
+ let query = document.getElementById("search").value;
+ let results = fuse.search(query).map((i) => {
+ return {
+ id: i.item.id,
+ score: i.score
+ };
+ });
+
+ let unfiltered = results;
+
+ results = results.filter((i) => {
+ return i.score < 0.7;
+ });
+
+ console.log("Before:", unfiltered, "After:", results);
+
+ document.getElementById("list").style.display = "none";
+ document.getElementById("search-results").style.display = "block";
+ document.getElementById("search-results").innerHTML = "";
+
+ for (let result of results) {
+ document.getElementById("search-results").innerHTML += document.getElementById("action-" + result.id).outerHTML;
+ }
+
+ console.log(results);
+
+ if (query.trim() === "") {
+ document.getElementById("list").style.display = "block";
+ document.getElementById("search-results").style.display = "none";
+ }
+ }
+
+ function changeMixed() {
+ let value = document.getElementById("editor-type").value;
+ console.log(value);
+
+ if (value === "2") {
+ Array.from(document.getElementsByClassName("creator-relation")).forEach((el) => {
+ el.classList.add("mixed");
+ });
+ } else {
+ Array.from(document.getElementsByClassName("creator-relation")).forEach((el) => {
+ el.classList.remove("mixed");
+ });
+ }
+ }
+</script>
+
+<style>
+ .modal-header {
+ border-bottom: 1px solid #353738;
+ }
+
+ .modal-content {
+ border: 1px solid rgba(255, 255, 255, .2);
+ background-color: #111;
+ }
+
+ .btn-close {
+ filter: invert(1);
+ }
+
+ .creator-relation {
+ border-radius: 10px;
+ padding: 5px 10px;
+ opacity: .5;
+ }
+
+ .creator-relation.checked {
+ background-color: rgba(255, 255, 255, .1);
+ opacity: 1;
+ }
+
+ .creator-relation:hover {
+ background-color: rgba(255, 255, 255, .1);
+ }
+
+ .creator-relation.checked:hover {
+ background-color: rgba(255, 255, 255, .25) !important;
+ }
+
+ .creator-relation.checked .deprecated {
+ display: block !important;
+ }
+
+ .creator-relation.mixed.checked .sexual {
+ display: block !important;
+ }
+
+ .list-group-item {
+ color: #fff;
+ background-color: #222;
+ border: 1px solid rgba(255, 255, 255, .125);
+ }
+
+ .list-group-item.disabled {
+ color: #fff;
+ background-color: #222;
+ border-color: rgba(255, 255, 255, .125);
+ opacity: .75;
+ }
+
+ .list-group-item:hover {
+ background-color: #252525;
+ color: #ddd;
+ }
+
+ .list-group-item:active, .list-group-item:focus {
+ background-color: #272727;
+ color: #bbb;
+ }
+
+ @media (max-width: 991px) {
+ .action-listing {
+ grid-template-columns: 1fr !important;
+ text-align: center !important;
+ }
+
+ .action-listing > * {
+ margin-bottom: 10px;
+ text-align: center !important;
+ }
+
+ .action-listing > *:nth-last-child(1) {
+ margin-bottom: 0 !important;
+ }
+
+ .action-listing img {
+ width: 32px !important;
+ }
+ }
+</style>
+
+<?php require_once $_SERVER['DOCUMENT_ROOT'] . '/includes/footer.php'; ?>