// ==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 // @downloadURL https://update.greasyfork.cloud/scripts/425783/meatmemo.user.js // @updateURL https://update.greasyfork.cloud/scripts/425783/meatmemo.meta.js // ==/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 = ``; for (let i in option) { let issl = i == selected ? "selected" : ""; s += ``; } s += ""; 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: "人外と推理した占い師の結果は
完グレ判定に使用しない", 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 = `${val}`; this.tds.push(td); } text() { let tds = this.tds.join(""); let tr = `${tds}`; 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 = `
ログの取り込み
リセット
更新
発言ログ
投票履歴
絞り込み
全員表示
生存+役職のみ
役職入力
なし
最新のみ
全日
`; let float = `
メモ表示/非表示
`; $("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 && /投票/.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 = [ "", "
", "
", "
", "", ].join(""); let submitbutton = ""; $("#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 = /(\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("
")[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("全ログ"); for (let player of this.list) { namerow.add(`${player.name}`, `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(`${day + 1}日目`); for (let player of this.list) { let no = player.no; let talknum = this.memo.log.talknum(player.name, day) || ""; row.add(`${talknum}`, `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("
") : "-"; 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 = $("", { id: "log_day" + day }); let trs = `${day + 1}日目`; for (let log of logs) { let cl = "talk_player" + this.memo.playerManager.no(log.name); let name = `${log.name}さん`; if (log.name == "ゲームマスター") { name = `${log.name}`; } trs += `${name}${log.content}`; } 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 = $("
", { id: "colorSetting", class: "window southEast", }).appendTo($("body")); $("#toolArea_hid").append("名前色設定"); let settingTable = $("
", { id: "colorSettingtable" }).appendTo(settingArea); settingArea.append(`
`); $("#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 = $("
", { id: "setting", class: "window southEast" }).appendTo($("body")); $("#toolArea_hid").append("設定"); let settingTable = $("
", { id: "settingtable" }).appendTo(settingArea); settingArea.append(`
`); $("#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 = `

`; $("body").append(randomWindow); $("#toolArea_hid").append("乱数"); $("#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;}", ]); } $("").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("
自動更新:" + onoff + "
"); 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 = $("
", { id: "warningArea" }).appendTo($("body")); warningArea.show(); var cmbplayer = $("select[name=CMBPLAYER]").clone(); var votebutton = $("", { type: "button", value: "投票", on: { click: function () { $("select").eq(0).val("VOTE"); document.forms[0].submit(); }, }, }); this.left = counts; warningArea.html("未投票です! あと秒"); 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 = `アイコン色:${colorselect}`; var iconsupport = ""; 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)