// ==UserScript==
// @name meatmemo
// @namespace http://mobajinro.s178.xrea.com/wakamemo/
// @version 2.0.0
// @description わかめて上で動作するわかめてメモのようなものです。
// @author udop_
// @match http://jinrou.dip.jp/~jinrou/cgi_jinro.cgi
// @match http://61.215.66.131/~jinrou/cgi_jinro.cgi
// @match http://www7a.biglobe.ne.jp/~kuri/cgi_jinro.cgi
// @require https://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js
// @run-at document-start
// ==/UserScript==
;(function ($) {
"use strict";
Array.prototype.fillundef = function (def, lastindex) {
lastindex = lastindex + 1 || this.length;
for (let i = 0; i < lastindex; i++) {
if (this[i] === null) {
this[i] = JSON.parse(JSON.stringify(def));
}
}
};
function createSelectBox(option, selected, attr) {
let attrtext = "";
for (let k in attr) {
attrtext = attrtext + ` ${k}="${attr[k]}"`;
}
let s = `<select${attrtext}>`;
for (let i in option) {
let issl = i == selected ? "selected" : "";
s += `<option value="${i}" ${issl}>${option[i]}</option>`;
}
s += "</select>";
return s;
}
var COLORLIST = {
0: "選択▼",
1: "明灰",
2: "暗灰",
3: "黄色",
4: "オレンジ",
5: "赤",
6: "水色",
7: "青",
8: "黄緑",
9: "紫",
10: "桃色",
11: "肌色",
12: "茶色",
13: "緑",
14: "若草色",
15: "真紅",
16: "薄茶色",
17: "藍色",
18: "蒼",
19: "ピンク",
20: "銀色",
21: "薄紫",
22: "象牙色",
23: "黒",
};
const joblist = {
gray: "",
fortune: "占い",
necro: "霊能",
share: "共有",
guard: "狩人",
cat: "猫又",
beast: "人外",
};
const reasoninglist = {
gray: "",
real: "真",
fake: "偽",
villager: "村人",
madman: "狂人",
wolf: "人狼",
fox: "妖狐",
};
const resultlist = { notinput: "", white: "○", black: "●" };
const jobinitial = {
fortune: "占",
necro: "霊",
share: "共",
cat: "猫",
guard: "狩",
wolf: "狼",
madman: "狂",
fox: "狐",
villager: "村",
real: "真",
fake: "偽",
beast: "外",
gray: "",
};
const themeList = {
crimson: { main: "crimson", sub: "#EC365A" },
darkorange: { main: "darkorange", sub: "#FF9F32" },
darkgreen: { main: "darkgreen", sub: "#009300" },
navy: { main: "navy", sub: "#0000b2" },
purple: { main: "purple", sub: "#B200B2" },
sienna: { main: "sienna", sub: "#C66538" },
};
const reasonflavor = { bite: "無残", note: "デスノ", exec: "処刑", sudden: "突然死" };
const settingDefault = {
rewrite_css: {
value: "yes",
option: { yes: "はい", no: "いいえ" },
name: "見た目を変更する",
},
auto_import_log: {
value: "onetime",
option: { none: "しない", onetime: "投票時", alltime: "常時" },
name: "自動ログ取得のタイミング",
},
alert_vote: {
value: "yes",
name: "未投票時に警告する",
option: { yes: "はい", no: "いいえ" },
},
send_support: {
value: "ctrl",
name: "支援キー+ENTERで送信",
option: { none: "しない", ctrl: "CTRL", shift: "SHIFT" },
},
autoreload_interval: {
value: "30",
name: "自動更新(観戦時のみ)",
option: { 10: "10秒", 20: "20秒", 30: "30秒", 60: "60分" },
},
theme_color: {
value: "navy",
name: "テーマカラー",
option: {
crimson: "紅",
darkorange: "オレンジ",
darkgreen: "緑",
navy: "蒼",
purple: "紫",
sienna: "茶",
},
},
layout: {
value: "no",
name: "表を横に並べる(横幅と相談)",
option: { yes: "はい", no: "いいえ" },
},
grayregion: {
value: "no",
name: "人外と推理した占い師の結果は<br>完グレ判定に使用しない",
option: { yes: "はい", no: "いいえ" },
},
coloringName: {
value: "no",
name: "役職で色分けする(試験運用)",
option: { yes: "はい", no: "いいえ" },
},
};
const nameColor = {
black: "なし",
red: "赤",
pink: "ピンク",
blue: "青",
green: "緑",
purple: "紫",
brown: "茶",
gaming: "虹色",
};
const colorSettingDefault = {
gray: {
value: "black",
name: "グレー",
option: nameColor,
},
fortune: {
value: "black",
name: "占い",
option: nameColor,
},
necro: {
value: "black",
name: "霊能",
option: nameColor,
},
share: {
value: "black",
name: "共有",
option: nameColor,
},
guard: {
value: "black",
name: "狩人",
option: nameColor,
},
cat: {
value: "black",
name: "猫又",
option: nameColor,
},
beast: {
value: "black",
name: "人外",
option: nameColor,
},
};
class Tr {
constructor(id, cl) {
this.id = id ? `id="${id}"` : "";
this.cl = cl ? `class="${cl}"` : "";
this.tds = [];
}
add(val, cl, colspan) {
let c = cl ? `class='${cl}'` : "";
let d = colspan ? `colspan=${colspan}` : "";
let td = `<td ${c} ${d}>${val}</td>`;
this.tds.push(td);
}
text() {
let tds = this.tds.join("");
let tr = `<tr ${this.id} ${this.cl}>${tds}</tr>`;
return tr;
}
appendTo(jQueryObject) {
jQueryObject.append(this.text());
}
}
class MeatMemo {
constructor(serverName) {
this.serverName = serverName || "wakamete";
this.playerManager = new PlayerManager(this);
this.log = new LogManager(this);
this.setting = new Setting(this);
this.colorSetting = new ColorSetting(this);
this.random = new Random(this);
this.style = new Style(this);
this.utility = new Utility(this);
this.filterSetting = { show: "All", input: "simple" };
this.isAutoReload = false;
this.newestImportDay = 0;
this.init();
}
init() {
this.load();
this.prepare();
this.setting.init();
this.colorSetting.init();
this.style.injection();
this.random.init();
$(() => {
this.on();
this.utility.init();
});
}
get playerNum() {
this.playerManager.import();
return this.playerManager.list.length;
}
settingIs(key, value = "yes") {
return this.setting.options[key].value == value;
}
settingValue(key) {
return this.setting.options[key].value;
}
prepare() {
let container = `
<div id="memoContainer">
<div id="memoMenu">
<div class='button' id='importButton'>ログの取り込み</div>
<div class='button' id='resetButton'>リセット</div>
<div class='button' id='reloadButton'>更新</div>
</div>
<div id="memoTab">
<div class='tab active' data-value='log'>発言ログ</div>
<div class='tab' data-value='vote'>投票履歴</div>
</div>
<div id="memoBody">
<div id="logArea">
<div id="playerInfoArea">
<table id="playerInfoTable"></table>
</div>
<div id="buttonArea">
<div>絞り込み
<div class='select filter' data-value='All'>全員表示</div>
<div class='select filter' data-value='Alive'>生存+役職のみ</div>
</div>
<div>役職入力
<div class='select inputmode' data-value='none'>なし</div>
<div class='select inputmode' data-value='simple'>最新のみ</div>
<div class='select inputmode' data-value='full'>全日</div>
</div>
</div>
<div id="discussLogArea">
<table id="discussLogTable"></table>
</div>
</div>
<div id="voteArea">
<table id="voteTable"></table>
<table id="summaryTable"></table>
</div>
</div>
</div>
`;
let float = `
<div id="floatButtonArea">
<div id='toolArea'>
<a>ツール</a>
<div id='toolArea_hid'></div>
</div>
<div>
<a id='toggleButton'>メモ表示/非表示</a>
</div>
</div>
`;
$("body").append(container).append(float);
}
autoImport() {
if (this.settingIs("auto_import_log", "alltime")) {
this.import();
}
else if (this.settingIs("auto_import_log", "onetime")) {
let today = this.today;
if (today > this.newestImportDay && /<font size="\+2">投票/.test($("body").html())) {
this.newestImportDay = today;
this.import();
}
}
}
import() {
this.playerManager.import();
this.log.import();
this.save();
}
refresh() {
this.playerManager.refresh();
this.log.refresh();
}
switchDispArea(mode) {
$("div.tab").removeClass("active");
$(`div.tab[data-value=${mode}]`).addClass("active");
$("#memoBody > div").hide();
$(`#${mode}Area`).show();
}
reset() {
this.playerManager.reset();
this.log.reset();
}
switchAliveFilter(mode) {
mode = mode || this.filterSetting.show;
this.filterSetting.show = mode;
$("div.select.filter").removeClass("active");
$(`div.select.filter[data-value=${mode}]`).addClass("active");
this.playerManager.filter(mode);
this.save();
}
switchInputMode(mode) {
mode = mode || this.filterSetting.input;
this.filterSetting.input = mode;
$("div.select.inputmode").removeClass("active");
$(`div.select.inputmode[data-value=${mode}]`).addClass("active");
let newestDay = this.newestDay;
for (var day = 1; day <= newestDay; day++) {
if (mode == "full" || (mode == "simple" && day == newestDay)) {
$("#result_" + day).show();
}
else {
$("#result_" + day).hide();
}
}
this.save();
}
on() {
$("table").eq(1).attr("id", "w_player");
$("table[cellspacing=0]").eq(-1).attr("id", "w_textarea");
$("table[cellpadding=0]").not(".CLSTABLE2").last().attr("id", "w_discuss");
$("table[cellspacing=0]").eq(0).attr("id", "w_info");
$("table[cellspacing=0]").eq(-2).attr("id", "w_command");
let voicebutton = [
"<td class='voiceloud'>",
"<div class='voice' data-value='MSG'>普</div>",
"<div class='voice' data-value='MSG2'><strong>強</strong></div>",
"<div class='voice' data-value='MSG3'><span style='color:#6666ee;'>弱</span></div>",
"</td>",
].join("");
let submitbutton = "<td><input type='submit' value='行動/更新' style='height:100px; width:150px;'></td>";
$("#w_textarea").find("td:last").after(submitbutton).after(voicebutton);
this.switchInputMode();
this.switchAliveFilter();
let _this = this;
$("#toggleButton").on("click", () => {
$("#memoContainer").toggle();
});
$("#importButton").on("click", () => {
this.import();
this.refresh();
});
$("#resetButton").on("click", () => {
if (!window.confirm("ログをすべてリセットします。本当によろしいですか?"))
return false;
this.reset();
this.refresh();
});
$("#reloadButton").on("click", function () {
$("textarea").val("");
document.forms[0].submit();
});
$("div.tab").on("click", function () {
let mode = $(this).data("value");
_this.switchDispArea(mode);
});
$("div.select.filter").on("click", function () {
let mode = $(this).data("value");
_this.switchAliveFilter(mode);
});
$("div.select.inputmode").on("click", function () {
let mode = $(this).data("value");
_this.switchInputMode(mode);
});
$("#toolArea").hover(() => {
$("#toolArea_hid").show();
}, () => {
$("#toolArea_hid").hide();
});
$("textarea").eq(0).focus();
$("div.voice").on("click", function () {
$("select").eq(0).val($(this).data("value"));
$("div.voice").removeClass("voice_selected");
$(this).addClass("voice_selected");
});
$(window).on("keydown", function (e) {
if (e.keyCode == 27) {
$("#memoContainer").hide();
}
});
this.refresh();
}
toggleAutoReload() {
this.isAutoReload = !this.isAutoReload;
this.save();
}
get villageNo() {
return $("title").text().slice(0, 6);
}
load() {
let sdata = localStorage.getItem("memodata");
if (!sdata)
return false;
let memodata = JSON.parse(sdata);
this.isAutoReload = memodata.isAutoReload;
if (memodata.villageNo != this.villageNo)
return false;
this.playerManager.load(memodata.playerInfo);
this.log.load(memodata.discussLog);
if (memodata.filterSetting) {
this.filterSetting = memodata.filterSetting;
}
this.newestImportDay = memodata.newestImportDay || 0;
this.save();
}
save() {
let data = {
villageNo: this.villageNo,
playerInfo: this.playerManager.forSave(),
discussLog: this.log.forSave(),
filterSetting: this.filterSetting,
isAutoReload: this.isAutoReload,
newestImportDay: this.newestImportDay,
};
localStorage.setItem("memodata", JSON.stringify(data));
}
filterLog(no = 99, day = 99) {
if (no < 99) {
$("#discussLogTable tr").hide();
$("tr.systemlog").show();
$("tr.talk_player" + no).show();
}
else {
$("#discussLogTable tr").show();
}
if (day < 99) {
$("#discussLogTable tbody").hide();
$("#log_day" + day).show();
}
else {
$("#discussLogTable tbody").show();
}
}
get today() {
var day = /<font size="\+2">(\d{1,2})/.exec($("body").html());
return day ? +day[1] - 1 : 0;
}
get newestDay() {
return Math.max(this.today, this.log.list.length - 1);
}
get isDaytime() {
return $("body").attr("bgcolor") != "#000000";
}
get isStart() {
return this.log.list.length >= 2;
}
}
class Player {
constructor(data) {
this.no = data.no || 0;
this.name = data.name || "";
this.vital = data.vital || "alive";
this.job = data.job || "gray";
this.reasoning = data.reasoning || "gray";
this.jobresult = data.jobresult || [];
this.vote = data.vote || [];
this.death = data.death || { reason: null, day: null, cantco: 99 };
}
static wakameteHTMLof(no, html) {
let name = html.split("<br>")[0];
let vital = /生存中/.test(html) ? "alive" : "death";
return new Player({
no: no,
name: name,
vital: vital,
});
}
forSave() {
return {
name: this.name,
no: this.no,
vital: this.vital,
job: this.job,
reasoning: this.reasoning,
jobresult: this.jobresult,
vote: this.vote,
deathDetail: this.death,
};
}
updateVital(vital) {
this.vital = vital;
}
setJudge(day, judge) {
this.jobresult.fillundef({ target: 99, judge: "notinput" }, +day);
this.jobresult[day].judge = judge;
}
setTarget(day, target) {
if (!target)
return false;
this.jobresult.fillundef({ target: 99, judge: "notinput" }, +day);
this.jobresult[day].target = target;
}
}
class PlayerManager {
constructor(memo) {
this.list = [];
this.indexOfName = {};
this.memo = memo;
}
reset() {
this.list = [];
this.indexOfName = {};
}
load(data) {
if (!data)
return false;
this.list = [];
this.indexOfName = {};
for (let p of data) {
let player = new Player(p);
this.list.push(player);
this.indexOfName[player.name] = player.no;
}
}
pick(name) {
if (name in this.indexOfName) {
return this.list[this.indexOfName[name]];
}
else {
return null;
}
}
forSave() {
return this.list.map((player) => player.forSave());
}
filter(mode) {
for (var player of this.list) {
if (mode == "All" || player.vital == "alive" || player.job != "gray") {
$("td.player_" + player.no).show();
}
else {
$("td.player_" + player.no).hide();
}
}
}
import() {
this.update();
this.import_vote_wakamete();
this.import_death_wakamete();
}
update() {
switch (this.memo.serverName) {
case "wakamete":
let isStart = this.memo.isStart;
if (isStart) {
this.vitalCheck_wakamete();
}
else {
this.update_wakamete();
}
break;
}
}
no(name) {
if (name in this.indexOfName) {
return this.indexOfName[name];
}
else {
return "";
}
}
vitalCheck_wakamete() {
$("#w_player")
.find("td:odd")
.each((i, v) => {
if (!$(v).html())
return false;
this.list[i].updateVital(/生存中/.test($(v).html()) ? "alive" : "death");
});
}
update_wakamete() {
this.list = [];
this.indexOfName = {};
$("#w_player")
.find("td:odd")
.each((i, v) => {
let html = $(v).html();
if (!html)
return false;
let player = Player.wakameteHTMLof(i, html);
this.list.push(player);
this.indexOfName[player.name] = i;
});
}
import_death_wakamete() {
let deathList = [];
$("#w_discuss")
.find("td[colspan='2']")
.each((i, tr) => {
if (/(で発見|結果処刑|突然死|猫又の呪い)/.test($(tr).text())) {
deathList.push(tr);
}
});
let day = this.memo.today;
let cantco = this.memo.today;
let isdaytime = this.memo.isDaytime;
for (let log of deathList) {
let player = this.pick($(log).find("b").eq(0).text());
let text = $(log).text();
let reason = "";
if (/無残な姿/.test(text))
reason = "bite";
if (/死体で発見/.test(text))
reason = "note";
if (/村民協議の結果|猫又の呪い/.test(text)) {
reason = "exec";
isdaytime ? day-- : cantco++;
}
if (/突然死/.test(text)) {
reason = "sudden";
cantco++;
}
player.death = {
reason: reason,
cantco: cantco,
day: day,
};
}
}
import_vote_wakamete() {
let votelog = [];
$("#w_discuss")
.find("td[colspan='2']")
.each(function (i, v) {
if (/\d{1,2}日目 投票結果。/.test($(v).text())) {
votelog.unshift(v);
}
});
if (!votelog.length)
return false;
let daystr = $(votelog[0])
.text()
.match(/(\d{1,2})日目 投票結果。/);
if (!daystr)
return false;
let day = +daystr[1] - 1;
for (let player of this.list) {
player.vote.fillundef(["-"], day);
player.vote[day].fillundef("-", votelog.length - 1);
}
for (let times = 0; times < votelog.length; times++) {
$(votelog[times])
.find("tr")
.each((i, vote) => {
let voter = this.pick($(vote).find("b").eq(0).text());
let target = $(vote).find("b").eq(1).text();
if (voter)
voter.vote[day][times] = target;
});
}
}
listforSelect() {
let playersList = { 99: "" };
for (let player of this.list) {
playersList[player.no] = player.name;
}
return playersList;
}
refresh() {
this.refreshPlayer();
this.refreshVote();
this.refreshSummary();
}
refreshPlayer() {
let playerInfoTable = $("#playerInfoTable");
playerInfoTable.empty();
if (!this.list.length)
return false;
let playersList = this.listforSelect();
let newestDay = this.memo.newestDay;
let namerow = new Tr("", "namerow");
namerow.add("<a id='filterlink_99_99'>全ログ</a>");
for (let player of this.list) {
namerow.add(`<a id='filterlink_${player.no}_99'>${player.name}</a>`, `player_${player.no}`);
}
namerow.appendTo(playerInfoTable);
let jobrow = new Tr("", "jobrow");
jobrow.add("CO");
for (let player of this.list) {
let select = createSelectBox(joblist, player.job, {
id: `player_${player.no}_job`,
class: "jobselect",
});
jobrow.add(select, "player_" + player.no);
}
jobrow.appendTo(playerInfoTable);
jobrow = new Tr("", "jobrow");
jobrow.add("推理");
for (let player of this.list) {
let select = createSelectBox(reasoninglist, player.reasoning, {
id: `player_${player.no}_reasoning`,
class: "reasoningselect",
});
jobrow.add(select, "player_" + player.no);
}
playerInfoTable.append(jobrow.text());
for (let day = 1; day <= newestDay; day++) {
let row = new Tr("", "talknumrow");
row.add(`<a id="filterlink_99_${day}">${day + 1}日目</a>`);
for (let player of this.list) {
let no = player.no;
let talknum = this.memo.log.talknum(player.name, day) || "";
row.add(`<a id=filterlink_${no}_${day}>${talknum}</a>`, `player_${no}`);
}
row.appendTo(playerInfoTable);
}
for (let day = 1; day <= newestDay; day++) {
let resultrow = new Tr("result_" + day, "resultrow");
resultrow.add(`占霊結果 ${day + 1}日目`);
for (let player of this.list) {
if ((player.job == "fortune" && day < player.death.cantco) ||
(player.job == "necro" && day < player.death.cantco && day > 1)) {
let select1 = createSelectBox(playersList, 99, {
id: `target_${player.no}_${day}`,
class: "jobtarget",
});
let select2 = createSelectBox(resultlist, "notinput", {
id: `judge_${player.no}_${day}`,
class: "jobjudge",
});
resultrow.add(select1 + select2, "player_" + player.no);
}
else {
resultrow.add("", "player_" + player.no);
}
}
playerInfoTable.append(resultrow.text());
}
this.refreshJobResult();
this.memo.switchAliveFilter();
this.memo.switchInputMode();
this.coloringGray();
let _this = this;
$("#playerInfoTable a").on("click", function (e) {
let id = $(this).attr("id");
if (!id)
return false;
let no = +id.split("_")[1];
let day = +id.split("_")[2];
_this.memo.filterLog(no, day);
});
$("select.jobselect").on("change", function (e) {
let id = $(this).attr("id");
if (!id)
return false;
let [i, no, day] = id.split("_");
_this.list[+no].job = String($(this).val());
_this.refresh();
_this.refreshJobInitial();
_this.coloring();
_this.memo.save();
});
$("select.reasoningselect").on("change", function (e) {
let id = $(this).attr("id");
if (!id)
return false;
let [i, no, day] = id.split("_");
_this.list[+no].reasoning = String($(this).val());
_this.refreshJobInitial();
_this.coloring();
_this.memo.save();
});
$("select.jobtarget").on("change", function (e) {
let id = $(this).attr("id");
if (!id)
return false;
let no = +id.split("_")[1];
let day = +id.split("_")[2];
let target = +$(this).val();
_this.list[no].setTarget(day, target);
_this.refreshSummary();
_this.coloringGray();
_this.memo.save();
});
$("select.jobjudge").on("change", function (e) {
let id = $(this).attr("id");
if (!id)
return false;
let [i, no, day] = id.split("_");
let judge = String($(this).val());
_this.list[+no].setJudge(+day, judge);
_this.refreshSummary();
_this.coloringGray();
_this.memo.save();
});
}
refreshVote() {
if (!this.list.length)
return false;
let voteTable = $("#voteTable");
let newestDay = this.memo.newestDay;
voteTable.empty();
var tr = new Tr();
tr.add("プレイヤー");
for (var day = 1; day <= newestDay; day++) {
if (!this.list[0].vote[day])
continue;
let colspan = this.list[0].vote[day].length;
tr.add(`${day + 1}日目`, "", colspan);
}
voteTable.append(tr.text());
for (var player of this.list) {
tr = new Tr();
tr.add(player.name);
for (day = 1; day <= newestDay; day++) {
if (!player.vote[day])
continue;
for (let vote of player.vote[day]) {
tr.add(vote);
}
}
voteTable.append(tr.text());
}
}
refreshJobResult() {
let newestDay = this.memo.newestDay;
let fortunes = this.list.filter((p) => p.job == "fortune");
for (let fortune of fortunes) {
for (let day = 1; day < Math.min(fortune.death.cantco, newestDay + 1); day++) {
if (!fortune.jobresult[day])
continue;
$("#target_" + fortune.no + "_" + day).val(fortune.jobresult[day].target);
$("#judge_" + fortune.no + "_" + day).val(fortune.jobresult[day].judge);
}
}
let necros = this.list.filter((p) => p.job == "necro");
for (let necro of necros) {
for (let day = 2; day < Math.min(necro.death.cantco, newestDay + 1); day++) {
let exec = this.list.filter((p) => p.death.day == day - 1 && p.death.reason == "exec");
if (necro.jobresult[day]) {
$("#target_" + necro.no + "_" + day).val(necro.jobresult[day].target);
$("#judge_" + necro.no + "_" + day).val(necro.jobresult[day].judge);
}
else if (exec) {
$("#target_" + necro.no + "_" + day).val(exec[0].no);
}
}
}
}
refreshSummary() {
let summaryTable = $("#summaryTable");
let newestDay = this.memo.newestDay;
summaryTable.empty();
var tr = new Tr();
tr.add("", "", 2);
for (var day = 1; day <= newestDay; day++) {
tr.add("" + (day + 1) + "日目");
}
tr.appendTo(summaryTable);
var fortunes = this.list.filter((p) => p.job == "fortune");
let necros = this.list.filter((p) => p.job == "necro");
let ability = fortunes.concat(necros);
for (let player of ability) {
tr = new Tr();
tr.add(player.job == "fortune" ? "占い師" : "霊能者");
tr.add(player.name);
for (day = 1; day <= newestDay; day++) {
let name = "", judge = "";
if (player.jobresult[day] && player.jobresult[day].target != 99) {
let result = player.jobresult[day];
name = this.list[result.target].name;
judge = resultlist[result.judge];
}
tr.add(name + judge);
}
tr.appendTo(summaryTable);
}
for (var reason in reasonflavor) {
let deaths = this.list.filter((p) => p.death.reason == reason);
if (!deaths.length)
continue;
tr = new Tr();
tr.add(reasonflavor[reason], "", 2);
for (day = 1; day <= newestDay; day++) {
let cn = deaths.filter((p) => p.death.day == day).map((p) => p.name);
let text = cn.length ? cn.join("<br>") : "-";
tr.add(text);
}
tr.appendTo(summaryTable);
}
}
refreshJobInitial() {
for (let player of this.list) {
let job = jobinitial[player.reasoning] + jobinitial[player.job];
$("tr.talk_player" + player.no + " span").html(job);
}
}
coloringGray() {
let co = this.list.filter((p) => p.job != "gray").map((p) => p.no);
var fortunes = this.list.filter((p) => p.job == "fortune");
if (this.memo.settingIs("grayregion")) {
fortunes = fortunes.filter((f) => f.reasoning == "gray" || f.reasoning == "real");
}
let fortuned = fortunes.map((f) => f.jobresult.map((r) => +r.target)).flat();
let notgray = co.concat(fortuned);
$("tr.namerow td").removeClass("death").removeClass("gray");
for (var player of this.list) {
if (player.vital == "death") {
$("tr.namerow .player_" + player.no).addClass("death");
}
else if (!notgray.includes(player.no)) {
$("tr.namerow .player_" + player.no).addClass("gray");
}
}
}
coloring() {
if (!this.memo.settingIs("coloringName"))
return false;
$("#w_discuss b").each((i, e) => {
let name = $(e).text();
let player = this.pick(name);
if (player) {
let job = player.job;
let color = this.memo.colorSetting.pick(job);
$(e).removeClass().addClass(color);
}
});
}
}
class Log {
constructor(data) {
this.name = data.name;
this.color = data.color;
this.content = data.content;
}
forSave() {
return {
name: this.name,
color: this.color,
content: this.content,
};
}
}
class LogManager {
constructor(memo) {
this.memo = memo;
this.list = [];
}
reset() {
this.list = [];
}
load(data) {
if (!data)
return false;
this.list = [];
data.forEach((logs) => {
let logofday = [];
logs.forEach((d) => {
let log = new Log(d);
logofday.push(log);
});
this.list.push(logofday);
});
}
forSave() {
let result = [];
this.list.forEach((logs) => {
let l = logs.map((log) => log.forSave());
result.push(l);
});
return result;
}
talknum(name, day) {
if (!this.list[day])
return 0;
return this.list[day].filter((l) => l.name == name).length;
}
import() {
switch (this.memo.serverName) {
case "wakamete":
this.import_wakamete();
break;
}
this.memo.filterLog();
}
import_wakamete() {
this.import_discuss_wakamete();
}
import_discuss_wakamete() {
let today = this.memo.today;
let isDaytime = this.memo.isDaytime;
if (!isDaytime)
return false;
this.list.fillundef([], today);
this.list[today] = [];
$("#w_discuss")
.find("tr")
.each((i, tr) => {
if ($(tr).children().length == 2) {
let name = $(tr).children().eq(0).find("b").eq(0).html();
let content = $(tr).children().eq(1).html();
let namehtml = $(tr).children().eq(0).html();
let color = namehtml.match(/color="(.+?)"/)[1];
let log = new Log({
name: name,
color: color,
content: content,
});
this.list[today].push(log);
}
});
}
refresh() {
let discussLogTable = $("#discussLogTable");
discussLogTable.empty();
if (!this.list.length)
return false;
this.list.forEach((logs, day) => {
if (!logs)
return;
let tbody = $("<tbody></tbody>", { id: "log_day" + day });
let trs = `<tr class="systemlog"><td colspan="2">${day + 1}日目</td></tr>`;
for (let log of logs) {
let cl = "talk_player" + this.memo.playerManager.no(log.name);
let name = `<font color="${log.color}">◆</font><b>${log.name}</b>さん`;
if (log.name == "ゲームマスター") {
name = `<font color="${log.color}">◆<b>${log.name}</b></font>`;
}
trs += `<tr class="${cl}"><td>${name}<span class='jobinitial'></span></td><td>${log.content}</td></tr>`;
}
tbody.append(trs).prependTo(discussLogTable);
});
this.memo.playerManager.refreshJobInitial();
}
}
class SettingOption {
constructor(data) {
this.value = data.value;
this.option = data.option;
this.default = data.value;
this.name = data.name;
}
}
class ColorSetting {
constructor(memo) {
this.memo = memo;
this.options = {};
}
pick(job) {
return this.options[job].value;
}
init() {
for (let key in colorSettingDefault) {
this.options[key] = new SettingOption(colorSettingDefault[key]);
}
this.load();
let settingArea = $("<div></div>", {
id: "colorSetting",
class: "window southEast",
}).appendTo($("body"));
$("#toolArea_hid").append("<a id='dispcolorSetting'>名前色設定</a>");
let settingTable = $("<table></table>", { id: "colorSettingtable" }).appendTo(settingArea);
settingArea.append(`<div class="closebutton"><input type="button" id="closeColorSetting" value='閉じる'></div>`);
$("#dispcolorSetting").on("click", () => {
$("#colorSetting").show();
});
$("#closeColorSetting").on("click", () => {
$("#colorSetting").hide();
});
for (let key in this.options) {
let item = this.options[key];
let tr = new Tr();
tr.add(item.name);
tr.add(createSelectBox(item.option, item.value, { id: key }));
tr.appendTo(settingTable);
}
let _this = this;
$("#colorSetting select").on("change", function () {
let val = $(this).val();
_this.setValue($(this).attr("id"), String(val));
_this.coloring();
});
}
coloring() {
this.memo.playerManager.coloring();
}
setValue(key, value) {
if (!(key in this.options))
return false;
this.options[key].value = value;
this.save();
}
load() {
let s = localStorage.getItem("memoColorSetting");
if (!s)
return false;
let data = JSON.parse(s);
for (let key in data) {
if (key in this.options) {
this.options[key].value = data[key];
}
}
}
save() {
localStorage.setItem("memoColorSetting", JSON.stringify(this.forSave()));
}
forSave() {
let result = {};
for (let key in this.options) {
result[key] = this.options[key].value;
}
return result;
}
}
class Setting {
constructor(memo) {
this.memo = memo;
this.options = {};
}
init() {
for (let key in settingDefault) {
this.options[key] = new SettingOption(settingDefault[key]);
}
this.load();
let settingArea = $("<div></div>", { id: "setting", class: "window southEast" }).appendTo($("body"));
$("#toolArea_hid").append("<a id='dispsetting'>設定</a>");
let settingTable = $("<table></table>", { id: "settingtable" }).appendTo(settingArea);
settingArea.append(`<div class="closebutton"><input type="button" id="closeSetting" value='閉じる'></div>`);
$("#dispsetting").on("click", () => {
$("#setting").show();
});
$("#closeSetting").on("click", () => {
$("#setting").hide();
});
for (let key in this.options) {
let item = this.options[key];
let tr = new Tr();
tr.add(item.name);
tr.add(createSelectBox(item.option, item.value, { id: key }));
tr.appendTo(settingTable);
}
let _this = this;
$("#setting select").on("change", function () {
let key = $(this).attr("id");
let val = String($(this).val());
_this.setValue(key, val);
});
}
setValue(key, value) {
if (!(key in this.options))
return false;
this.options[key].value = value;
this.save();
}
load() {
let s = localStorage.getItem("memoSetting");
if (!s)
return false;
let data = JSON.parse(s);
for (let key in data) {
if (key in this.options) {
this.options[key].value = data[key];
}
}
}
save() {
localStorage.setItem("memoSetting", JSON.stringify(this.forSave()));
}
forSave() {
let result = {};
for (let key in this.options) {
result[key] = this.options[key].value;
}
return result;
}
}
class Random {
constructor(memo) {
this.memo = memo;
}
init() {
let randomWindow = `
<div id="random" class="window southEast">
<div class="closebutton">
<input type="button" id="closeRandom" value='閉じる'>
</div>
<input type="button" value="4桁乱数" id="random_rnd4">
<input type="button" value="乱数表" id="random_matrix">
<input type="button" value="背徳用数字" id="random_haitoku">
<br>
<textarea id="forCopy" style="width:200px;height:150px;"></textarea>
<div id='randomMessage'></div>
</div>
`;
$("body").append(randomWindow);
$("#toolArea_hid").append("<a id='dispRandom'>乱数</a>");
$("#dispRandom").on("click", () => {
$("#random").show();
});
$("#closeRandom").on("click", () => {
$("#random").hide();
});
$("#random_rnd4").on("click", () => {
this.copy(this.rnd4());
});
$("#random_matrix").on("click", () => {
this.copy(this.matrix());
});
$("#random_haitoku").on("click", () => {
this.copy(this.haitoku());
});
}
copy(text) {
$("#forCopy").val(text).select();
document.execCommand("copy");
$("#randomMessage").html("コピーしました。");
}
rnd(n) {
return Math.floor(Math.random() * n);
}
padding(num, digit) {
let txt = "0000000000" + num;
return txt.slice(-digit);
}
rnd4() {
let rnd = this.rnd(10000);
return this.padding(rnd, 4);
}
matrix() {
let num = this.memo.playerNum;
var l = [];
var mat = "";
for (var i = 0; i < num; i++) {
l[i] = this.padding(i + 1, 2);
}
for (i = 0; i < num; i++) {
var r = this.rnd(num - i);
mat += l[r];
mat += i % 5 == 4 ? "\n" : " / ";
for (var j = r; j < num - 1; j++) {
l[j] = l[j + 1];
}
}
return mat;
}
haitoku() {
let num = this.memo.playerNum;
var jobs = ["村 人", "占い師", "霊能者", "狩 人", "共有者", "狂 人", "背徳者"];
var cir = ["①", "②", "③", "④", "⑤", "⑥", "⑦"];
var result = "";
var isimo = num == 15 || num == 19;
var n = isimo ? 7 : 6;
if (isimo) {
result += cir[this.rnd(n)] + "\n\n";
}
var l = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
for (let i = 0; i < n; i++) {
let r = this.rnd(10 - i);
let rnd = this.padding(this.rnd(100), 2);
result = result + jobs[i] + ":" + l[r] + rnd + "\n";
l.splice(r, 1);
}
return result;
}
}
class Style {
constructor(memo) {
this.memo = memo;
}
injection() {
let style = [];
if (this.memo.settingIs("rewrite_css")) {
style = [
".CLSTABLE tr td:nth-of-type(even) {font-size: 12px;line-height: 110%;padding: 2px;}",
".CLSTABLE tr td:nth-of-type(odd) {font-size: 0px;padding: 2px;}",
'body[bgcolor="#000000"] font[color="#6666aa"] {color: #ccccff;}',
'font[size="-1"] {font-size: 9pt;}',
"img {padding: 0;}",
"input,select {font-size: 9pt;}",
"table {font-size: 13px;}",
'textarea {font-family: "Meiryo";font-size: 11px;min-height: 100px;}',
'table[cellpadding="0"] tr td:nth-of-type(2){word-break:break-all;}',
];
}
let theme = this.memo.settingValue("theme_color");
let theme_color = themeList[theme].main;
let sub_color = themeList[theme].sub;
style = style.concat([
`:root{--theme-color:${theme_color}; --sub-color:${sub_color};}`,
"*{box-sizing:border-box;}",
"body{margin:0;}",
"form{margin:8px;}",
"#memoContainer{display:none; width:100%; height:100%; position:fixed; top:0px; left:0px; background-color:rgba(180,180,180,0.8); padding:15px; overflow:auto;}",
"#floatButtonArea{position:fixed; right:15px; top:15px;}",
"#floatButtonArea > div{margin:0px 2px; display:inline-block; vertical-align: top; width:110px; box-shadow: 0 3px 5px rgba(0, 0, 0, 0.4);}",
"#warningArea{width:100%; height:40px; padding:5px; position:fixed; bottom:0; display:none; background-color:darkorange; text-align:center; font-size:16px; color:white; font-weight:bold;}",
"#left{font-size:30px;}",
"#warningArea select, #warningArea input{font-size:11pt; vertical-align:middle;}",
"#memoMenu{width:100%; margin: 0 auto; font-size:10px;}",
"#memoMenu input, #memoMenu select, #buttonArea input{font-size:11px;}",
"#memoTab{margin-top:15px;}",
"#memoBody{width:100%; height:calc(100% - 63px); margin: 0 auto; }",
"#logArea,#voteArea{width:100%; height:100%; overflow:auto; margin: 0 auto; background-color:white; padding:10px; border-radius:0px 8px 8px 8px ;}",
"#voteArea{display:none;}",
"#discussLogTable{border-collapse:collapse;}",
"#discussLogTable td{text-align:left;vertical-align:top; color:black; word-break:break-all; font-size:9pt; line-height:140%; padding:2px;}",
"#discussLogTable font{font-size:9pt;}",
"#discussLogTable tr td:first-of-type{min-width:150px;}",
"#discussLogTable tr.systemlog td{font-weight:bold;background-color:var(--theme-color) !important; color:white; text-align:center;}",
"#playerInfoTable a{text-decoration:underline; color:blue; cursor:pointer;}",
"#playerInfoTable tr.namerow td.death {background-color:pink;}",
"#playerInfoTable tr.namerow td.gray {background-color:#e3e3e3;}",
"#playerInfoTable select {max-width:100px;}",
"#voteTable, #summaryTable{font-size:11px; border-collapse:collapse; margin-bottom:10px;color:black;}",
"#voteTable td, #summaryTable td{border:1px solid #666; padding:2px; }",
"#toolArea_hid {display:none;}",
"#setting input[type=number], #setting input[type=text]{width:60px;}",
".coloredit, .iconsupport{display:none;}",
".voiceloud {padding:0px 5px;}",
".voiceloud div:not(:first-of-type){margin-top:5px;}",
".voice {width:30px;height:30px;font-size:16px;border:1px solid black; border-radius:2px;background-color:white;line-height:28px;text-align:center;color:black; cursor:pointer;}",
".voice.voice_selected{border:3px solid red; line-height:24px;}",
"#caspe input[type=text]{width:100px;}",
"#caspe input[type=number]{width:50px}",
".window{box-shadow: 0 3px 5px rgba(0, 0, 0, 0.4); border-top:16px solid var(--theme-color); background-color:#efefef; padding:10px; display:none; }",
".southEast{font-size:13px; position:fixed; right:10px; bottom:10px; width:400px; height:300px;}",
".north{width:400px; height:80px; position:fixed; left:calc(50% - 200px); top:15px;}",
"#floatButtonArea a, .button{width:110px; color:white; background-color:var(--theme-color); cursor:pointer; display:inline-block; font-size:12px; font-weight:bold; line-height:24px; text-align:center;}",
"div.button{margin:0px 2px; box-shadow: 0 3px 5px rgba(0, 0, 0, 0.4); padding:0px 3px;}",
".tab{width:150px; color:white; background-color:var(--theme-color);cursor:pointer; display:inline-block; font-size:12px; line-height:24px; text-align:center; border-radius:8px 8px 0 0;}",
".tab.active{color:var(--theme-color); background-color:white;font-weight:bold;}",
"#floatButtonArea a:hover, div.button:hover{background-color:var(--sub-color);}",
".closebutton{position:absolute; right:5px; top:5px;}",
"#caspe img{padding:2px;}",
"#caspe img.iconselected{border:2px solid var(--theme-color); padding:0px;}",
".jobinitial{user-select:none; color:var(--theme-color); font-weight:bold; font-size:80%;}",
".jobinitial:not(:empty):before{content:'[';}",
".jobinitial:not(:empty):after{content:']';}",
".black{color:black;} .pink{color:deeppink;} .red{color:red;} .green{color:green;}",
".purple{color:purple;} .brown{color:brown;} .blue{color:blue;}",
".gaming{background:linear-gradient(to right, #f33,#ff3,#3f3,#3ff,#33f,#f3f,#f33) ;-webkit-background-clip: text; -webkit-text-fill-color:transparent;}",
]);
if (this.memo.settingIs("layout")) {
style = style.concat([
"#logArea{display: flex; flex-direction:column; flex-wrap:wrap;}",
"#playerInfoArea{height:calc(100% - 50px); overflow:auto hidden; width:50%; padding:0px;}",
"#playerInfoArea select{font-size:8.5pt;}",
"#buttonArea{height:50px;font-size:12px; padding:5px;}",
"#buttonArea > div{ margin-bottom:5px;}",
"#discussLogArea{width:50%; overflow:auto; padding:5px;}",
".select{color:var(--theme-color); border-width:2px 2px 2px 0px ;border-color:var(--theme-color); border-style:solid; background-color:white; cursor:pointer; text-align:center; display:inline-block;width:100px;}",
".select.active{color:white;background-color:var(--theme-color); font-weight:bold;}",
"#buttonArea > div > div.select:first-of-type{margin-left:5px; border-left:2px solid var(--theme-color);}",
"#buttonArea > div > div.select:lastof-type{margin-right:5px;}",
"#playerInfoTable{background-color:white; text-align:center; font-size:8pt; color:black; width:100%; margin:0px 2px 2px 0px;}",
"#playerInfoTable tbody{display:flex; flex-direction:row;border-spacing:0;}",
"#playerInfoTable tr{display:flex; flex-direction:column; flex:0 0 40px;}",
"#playerInfoTable tr.namerow{display:flex; flex-direction:column; flex:0 0 80px; position:sticky; left:0;}",
"#playerInfoTable tr.namerow td{background-color:white;}",
"#playerInfoTable tr.resultrow{display:flex; flex-direction:column; flex:0 0 140px;}",
"#playerInfoTable td{border-right:#666 solid 1px;border-bottom:#666 solid 1px;padding:1px; display:block; width:auto; height:1.8em; }",
"#playerInfoTable tr:first-of-type td{border-left:#666 solid 1px;}",
"#playerInfoTable tr td:first-of-type{border-top:#666 solid 1px;}",
]);
}
else {
style = style.concat([
"#buttonArea{padding:10px;line-height:24px;font-size:9pt;}",
"#buttonArea > div{display:inline-block;}",
"#playerInfoArea{overflow:auto;}",
".select{width:100px; color:var(--theme-color); border-width:2px 2px 2px 0px ;border-color:var(--theme-color); border-style:solid; background-color:white; cursor:pointer; display:inline-block; font-size:12px; line-height:20px; text-align:center;}",
".select.active{color:white;background-color:var(--theme-color); font-weight:bold;}",
"#buttonArea > div > div.select:first-of-type{margin-left:5px; border-left:2px solid var(--theme-color);}",
"#buttonArea > div > div.select:last-of-type{margin-right:5px;}",
"#playerInfoTable {background-color:white;text-align:center; font-size:8pt;border-collapse:collapse; color:black; margin:0 auto;}",
"#playerInfoTable td{border:#666 solid 1px; padding:1px; }",
"#playerInfoTable tr.namerow{height:35px;}",
"#playerInfoTable tr td:first-child{min-width:50px;}",
]);
}
$("<style></style>").html(style.join("\n")).appendTo($("head"));
}
}
class Utility {
constructor(memo) {
this.memo = memo;
this.setting = memo.setting;
this.left = 0;
this.autoReloadFlg = 0;
}
init() {
this.setAlertVote();
this.receiveKeyResponse();
this.dispSuggest();
this.highlightDeathnote();
this.memo.playerManager.coloring();
this.setAutoReload();
}
setAutoReload() {
if ($("td.CLSTD01").eq(1).text() != "◆ 再表示")
return false;
let isAutoReload = this.memo.isAutoReload;
let onoff = isAutoReload ? "ON" : "OFF";
$("#floatButtonArea").prepend("<div><a id='autoReload'>自動更新:" + onoff + "</a></div>");
if (isAutoReload)
this.setReloadTimer();
$("#autoReload").on("click", () => {
this.memo.toggleAutoReload();
let isAutoReload = this.memo.isAutoReload;
let onoff = isAutoReload ? "ON" : "OFF";
$("#autoReload").text("自動更新:" + onoff);
if (isAutoReload) {
this.setReloadTimer();
}
else {
clearTimeout(this.autoReloadFlg);
}
});
}
setReloadTimer() {
this.autoReloadFlg = setTimeout(() => {
$("textarea").eq(0).val("");
document.forms[0].submit();
}, +this.memo.settingValue("autoreload_interval") * 1000);
}
setAlertVote() {
if (!this.memo.settingIs("alert_vote"))
return false;
if ($("font[size=6]").length) {
let warningArea = $("<div></div>", { id: "warningArea" }).appendTo($("body"));
warningArea.show();
var cmbplayer = $("select[name=CMBPLAYER]").clone();
var votebutton = $("<input />", {
type: "button",
value: "投票",
on: {
click: function () {
$("select").eq(0).val("VOTE");
document.forms[0].submit();
},
},
});
this.left = counts;
warningArea.html("未投票です! あと<span id='left'></span>秒");
warningArea.append(cmbplayer);
warningArea.append(votebutton);
cmbplayer.on("change", function () {
let v = $(this).val();
if (!v)
return false;
$("select[name=CMBPLAYER]").val(v);
});
$("#left").html("" + this.left);
setInterval(() => {
$("#left").html(String(--this.left));
}, 1000);
}
}
receiveKeyResponse() {
if (this.memo.settingIs("send_support", "ctrl")) {
$(window).on("keydown", function (e) {
if (e.ctrlKey && e.keyCode == 13) {
document.forms[0].submit();
}
});
}
if (this.memo.settingIs("send_support", "shift")) {
$(window).on("keydown", function (e) {
if (e.shiftKey && e.keyCode == 13) {
document.forms[0].submit();
}
});
}
}
dispSuggest() {
var colorselect = createSelectBox(COLORLIST, 0, { id: "colorlist" });
var coloredit = `<td class="coloredit">アイコン色:</td><td class="coloredit">${colorselect}</td>`;
var iconsupport = "<td class='iconsupport'><input type='button' id='pasteurl' value='/../../imgbbs/img/'></td>";
let commandtable = $("#w_command");
commandtable.find("td").eq(1).after(coloredit).after(iconsupport);
commandtable.find("td").eq(-2).addClass("cmbplayer");
commandtable.find("td").eq(-1).addClass("cmbplayer");
let _this = this;
$("#colorlist").on("change", function () {
_this.editSpeakField(String($(this).val()));
});
$("#pasteurl").on("click", () => {
this.editSpeakField("/../../imgbbs/img/");
});
$("select")
.eq(0)
.on("change", function () {
$("td.coloredit").hide();
$("td.iconsupport").hide();
$("td.cmbplayer").hide();
if ($(this).val() == "ICONCHG") {
$("td.coloredit").show();
}
else if ($(this).val() == "BCONCHG") {
$("td.iconsupport").show();
}
else {
$("td.cmbplayer").show();
}
});
}
editSpeakField(val) {
$("textarea").eq(0).val(val);
}
highlightDeathnote() {
var td = $("#w_info").find("td:last");
if (/アナタの家の前に/.test(td.html())) {
td.css("color", "red");
}
}
}
if ($("body").attr("bgcolor") != "#fee3aa") {
const meatmemo = new MeatMemo("wakamete");
}
})(jQuery)