"
return tr
}
appendTo(jQueryObject) {
jQueryObject.append(this.text())
}
}
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: "黒",
}
//**********************************************
// Session関係
//**********************************************
function load() {
if (localStorage.getItem("memodata")) {
data = JSON.parse(localStorage.getItem("memodata"))
}
playerInfo = data.playerInfo
indexOfName = data.indexOfName
discussLog = data.discussLog
deathLog = data.deathLog
votetimes = data.votetimes
var old_setting = data.setting
data.setting = {}
for (let k in settingDefault) {
data.setting[k] = k in old_setting ? old_setting[k] : settingDefault[k]
}
setting = data.setting
old_setting = data.colorSetting || colorSettingDefault
data.colorSetting = {}
for (let k in colorSettingDefault) {
data.colorSetting[k] = k in old_setting ? old_setting[k] : colorSettingDefault[k]
}
colorSetting = data.colorSetting
save()
}
function save() {
localStorage.setItem("memodata", JSON.stringify(data))
}
//**********************************************
// import関係
//**********************************************
function importLog() {
updateplayerInfo()
importDiscussLog()
}
function updateplayerInfo() {
//プレイヤー情報を取得
//death…「能力がCOできなくなった日」を指す 3日目吊りなら4日目。3日目噛まれなら3日目
if (discussLog.length >= 2) {
playertable.find("td:odd").each(function (i, v) {
if (!$(v).html()) return false
playerInfo[i].vital = $(v).html().includes("生存中") ? "alive" : "death"
})
} else {
data.playerInfo = []
playerInfo = data.playerInfo
data.indexOfName = {}
indexOfName = data.indexOfName
playertable.find("td:odd").each(function (i, v) {
var html = $(v).html()
if (!html) return false
var name = html.split(" ")[0]
var vital = html.includes("生存中") ? "alive" : "death"
playerInfo.push({
no: i,
name: name,
vital: vital,
job: "gray",
reasoning: "gray",
jobresult: [],
vote: [],
death: 99,
})
data.indexOfName[name] = i
})
}
save()
}
function importDiscussLog() {
//ログ取り込み
if (isdaytime) {
discussLog[today] = []
discusstable.find("tr").each(function (i, v) {
if ($(v).children().length != 2) return true
discussLog[today].push({
name: $(v).children().eq(0).find("b").eq(0).html(),
namehtml: $(v).children().eq(0).html(),
content: $(v).children().eq(1).html(),
})
})
}
//投票結果・死亡ログ
var votelog = []
discusstable.find("td[colspan='2']").each(function (i, v) {
if (
/(無残な姿で発見|死体で発見|村民協議の結果処刑|突然死|猫又の呪い)/.test($(v).text())
) {
importDeath(v)
}
if (/\d{1,2}日目 投票結果。/.test($(v).text())) {
votelog.unshift(v)
}
})
if (votelog.length) importVote(votelog)
filterlog(99, newestDay)
save()
}
function importDeath(log) {
//死体を記録
var day = today
var deathday = today
var no = indexOfName[$(log).find("b").eq(0).text()]
var text = $(log).text()
var reason = ""
if (/無残な姿で発見/.test(text)) {
reason = "bite"
} else if (/死体で発見/.test(text)) {
reason = "note"
} else if (/村民協議の結果|猫又の呪い/.test(text)) {
reason = "exec"
isdaytime ? day-- : deathday++
} else if (/突然死/.test(text)) {
reason = "sudden"
deathday++
}
playerInfo[no].death = deathday
if (!deathLog[reason]) deathLog[reason] = []
deathLog[reason].fillundef([], day)
if (!deathLog[reason][day].includes(no)) {
deathLog[reason][day].push(no)
}
}
function importVote(logs) {
//投票を取り込む
var day = $(logs[0])
.text()
.match(/(\d{1,2})日目 投票結果。/)
day = day[1] - 1
for (var player of playerInfo) {
player.vote[day] = []
player.vote.fillundef("-", logs.length)
}
for (let times = 0; times < logs.length; times++) {
$(logs[times])
.find("tr")
.each(function (i, vote) {
var voter = indexOfName[$(vote).find("b").eq(0).text()]
var target = $(vote).find("b").eq(1).text()
playerInfo[voter].vote[day][times] = target
})
}
votetimes[day] = logs.length
votetimes.fillundef(0)
}
//**********************************************
// refresh関係
//**********************************************
function refresh() {
//再表示まとめて
refreshDiscussLog()
refreshPlayerInfoTable()
refreshVoteTable()
refreshSummary()
}
function refreshPlayerInfoTable() {
//プレイヤー情報更新 くっそ長い
//初期化
playerInfoTable.empty()
if (!playerInfo.length) return false
var playersList = { 99: "" }
for (let i of range()) {
playersList[i] = playerInfo[i].name
}
var joblist = {
gray: "",
fortune: "占い",
necro: "霊能",
share: "共有",
guard: "狩人",
cat: "猫又",
beast: "人外",
}
var reasoninglist = {
gray: "",
real: "真",
fake: "偽",
villager: "村人",
madman: "狂人",
wolf: "人狼",
fox: "妖狐",
}
var resultlist = { notinput: "", white: "○", black: "●" }
//-------------------------名前列
var namerow = new Tr("", "namerow")
namerow.add("全ログ")
for (let player of playerInfo) {
namerow.add(
`${player.name}`,
`player_${player.no}`
)
}
namerow.appendTo(playerInfoTable)
//-------------------------発言数列
for (var day = 1; day <= newestDay; day++) {
var row = new Tr("", "talknumrow")
row.add(`${day + 1}日目`)
for (let player of playerInfo) {
var talknum = $("#log_day" + day).find("tr.talk_player" + player.no).length
if (talknum === 0) talknum = ""
row.add(
`${talknum}`,
"player_" + player.no
)
}
row.appendTo(playerInfoTable)
}
//-------------------------CO列
var jobrow = new Tr("", "jobrow")
jobrow.add("CO")
for (let player of playerInfo) {
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 playerInfo) {
let select = createSelectBox(reasoninglist, player.reasoning, {
id: `player_${player.no}_reasoning`,
class: "reasoningselect",
})
jobrow.add(select, "player_" + player.no)
}
playerInfoTable.append(jobrow.text())
//-------------------------役職結果列
for (day = 1; day <= newestDay; day++) {
var resultrow = new Tr("result_" + day, "resultrow")
resultrow.add(`占霊結果 ${day + 1}日目`)
for (let player of playerInfo) {
if (
(player.job == "fortune" && day < player.death) ||
(player.job == "necro" && day < player.death && 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())
}
refreshJobResult()
switchAliveFilter()
switchInputMode()
coloring()
//絞込機能つける
$("#playerInfoTable a").on("click", function (e) {
var id = $(this).attr("id").split("_")
filterlog(id[1], id[2])
})
//変更は逐一反映
$("select.jobselect").on("change", function () {
var no = $(this).attr("id").split("_")[1] - 0
playerInfo[no].job = $(this).val()
refreshPlayerInfoTable()
refreshJobInitial()
refreshSummary()
if(setting.coloringName.value == "yes") setNameColor()
save()
})
$("select.reasoningselect").on("change", function () {
var no = $(this).attr("id").split("_")[1] - 0
playerInfo[no].reasoning = $(this).val()
refreshJobInitial()
coloring()
save()
})
$("select.jobtarget").on("change", function (e) {
var id, no, day
;[id, no, day] = $(this).attr("id").split("_")
playerInfo[no].jobresult.fillundef({ target: 99, judge: "notinput" }, +day)
playerInfo[no].jobresult[day].target = $(this).val() - 0
refreshSummary()
coloring()
save()
})
$("select.jobjudge").on("change", function (e) {
var id, no, day
;[id, no, day] = $(this).attr("id").split("_")
playerInfo[no].jobresult.fillundef({ target: 99, judge: "notinput" }, +day)
playerInfo[no].jobresult[day].judge = $(this).val()
refreshSummary()
coloring()
save()
})
}
function refreshJobResult() {
var fortunes = playerInfo.filter((p) => {
return p.job == "fortune"
})
for (let fortune of fortunes) {
for (let day = 1; day < Math.min(fortune.death, newestDay + 1); day++) {
if (fortune.jobresult[day]) {
//既に結果が入力されているとき
$("#target_" + fortune.no + "_" + day).val(fortune.jobresult[day].target)
$("#judge_" + fortune.no + "_" + day).val(fortune.jobresult[day].judge)
}
}
}
var necros = playerInfo.filter((p) => {
return p.job == "necro"
})
for (let necro of necros) {
for (let day = 2; day < Math.min(necro.death, newestDay + 1); day++) {
if (necro.jobresult[day]) {
//既に結果が入力されているとき
$("#target_" + necro.no + "_" + day).val(necro.jobresult[day].target)
$("#judge_" + necro.no + "_" + day).val(necro.jobresult[day].judge)
} else if (deathLog.exec[day - 1]) {
$("#target_" + necro.no + "_" + day).val(deathLog.exec[day - 1])
}
}
}
}
function refreshDiscussLog() {
discussLogTable.empty()
if (!discussLog.length) return false
discussLog.forEach(function (logs, day) {
if (!logs) return
var tbody = $("", { id: "log_day" + day })
var trs = `
${day + 1}日目
`
for (var log of logs) {
var cl = log.name in indexOfName ? "talk_player" + indexOfName[log.name] : ""
trs += `
${log.namehtml}
${log.content}
`
}
tbody.append(trs).prependTo(discussLogTable)
})
refreshJobInitial()
}
function refreshJobInitial() {
var jobinitial = {
fortune: "占",
necro: "霊",
share: "共",
cat: "猫",
guard: "狩",
wolf: "狼",
madman: "狂",
fox: "狐",
villager: "村",
real: "真",
fake: "偽",
beast: "外",
gray: "",
}
for (var player of playerInfo) {
var job = jobinitial[player.reasoning] + jobinitial[player.job]
$("tr.talk_player" + player.no + " span").html(job)
}
}
function refreshVoteTable() {
//投票テーブルリライト
voteTable.empty()
var tr = new Tr()
tr.add("プレイヤー")
for (var day = 1; day <= newestDay; day++) {
let colspan = votetimes[day] || 1
tr.addhtml(`
${day + 1}日目
`)
}
voteTable.append(tr.text())
for (var player of playerInfo) {
tr = new Tr()
tr.add(player.name)
for (day = 1; day <= newestDay; day++) {
let times = votetimes[day] || 1
for (let i = 0; i < times; i++) {
var text = player.vote[day] && player.vote[day][i] ? player.vote[day][i] : "-"
tr.add(text)
}
}
voteTable.append(tr.text())
}
}
function refreshSummary() {
var color = { notinput: "?", white: "○", black: "●" }
var reasons = ["bite", "note", "exec", "sudden"]
var reasonflavor = { bite: "無残", note: "デスノ", exec: "処刑", sudden: "突然死" }
summaryTable.empty()
var tr = new Tr()
tr.addhtml("
")
for (var day = 1; day <= newestDay; day++) {
tr.add("" + (day + 1) + "日目")
}
tr.appendTo(summaryTable)
var fn = playerInfo.filter((player) => {
return player.job == "fortune"
})
for (let i = 0; i < fn.length; i++) {
let player = fn[i]
tr = new Tr()
if (i == 0) tr.addhtml(`
占い師
`)
tr.add(player.name)
for (day = 1; day <= newestDay; day++) {
let name = "",
judge = ""
if (player.jobresult[day] && player.jobresult[day].target != 99) {
name = playerInfo[player.jobresult[day].target].name
judge = color[player.jobresult[day].judge]
}
tr.add(name + judge)
}
tr.appendTo(summaryTable)
}
fn = playerInfo.filter((player) => {
return player.job == "necro"
})
for (let i = 0; i < fn.length; i++) {
let player = fn[i]
tr = new Tr()
if (i == 0) tr.addhtml(`
霊能者
`)
tr.add(player.name)
for (let day = 1; day <= newestDay; day++) {
let name = "",
judge = ""
if (player.jobresult[day] && player.jobresult[day].target != 99) {
name = playerInfo[player.jobresult[day].target].name
judge = color[player.jobresult[day].judge]
}
tr.add(name + judge)
}
tr.appendTo(summaryTable)
}
for (var reason of reasons) {
if (!deathLog[reason]) continue
tr = new Tr()
if (reason == "bite") tr.addhtml("
死亡ログ
")
tr.add(reasonflavor[reason])
for (day = 1; day <= newestDay; day++) {
var text = "-"
if (deathLog[reason][day]) {
text = deathLog[reason][day].map((x) => playerInfo[x].name).join(" ")
}
tr.add(text)
}
tr.appendTo(summaryTable)
}
}
//**********************************************
// 表示
//**********************************************
var switchDispArea = function (_this) {
//メモのログ/投票表示切り替え
$("div.tab").removeClass("active")
$(_this).addClass("active")
$("#memoBody > div").hide()
var mode = $(_this).data("value")
$(`#${mode}Area`).show()
}
function switchAliveFilter(_this) {
if (_this) {
data.isfilter = $(_this).data("value") == "on"
save()
}
$("div.select.filter").removeClass("active")
data.isfilter
? $("#showAliveButton").addClass("active")
: $("#showAllButton").addClass("active")
for (var player of playerInfo) {
if (!data.isfilter || player.vital == "alive" || player.job != "gray") {
$("td.player_" + player.no).show()
} else {
$("td.player_" + player.no).hide()
}
}
}
function switchInputMode(_this) {
if (_this) {
data.inputMode = $(_this).data("value")
save()
}
$("div.select.inputmode").removeClass("active")
$(`#input${data.inputMode}Button`).addClass("active")
for (var day = 1; day <= newestDay; day++) {
if (data.inputMode == "full" || (data.inputMode == "simple" && day == newestDay)) {
$("#result_" + day).show()
} else {
$("#result_" + day).hide()
}
}
}
function filterlog(player, day) {
if (player < 99) {
$("#discussLogTable tr").hide()
$("tr.systemlog").show()
$("tr.talk_player" + player).show()
} else {
$("#discussLogTable tr").show()
}
if (day < 99) {
$("#discussLogTable tbody").hide()
$("#log_day" + day).show()
} else {
$("#discussLogTable tbody").show()
}
}
function coloring() {
var notgray = range().filter((i) => playerInfo[i].job != "gray")
var fortunes = playerInfo.filter((player) => player.job == "fortune")
if (setting.grayregion.value == "yes") {
fortunes = fortunes.filter((fortune) => {
return fortune.reasoning == "gray" || fortune.reasoning == "real"
})
}
fortunes.forEach(function (player) {
notgray = notgray.concat(player.jobresult.map((p) => p.target))
})
var graylist = range().filter((no) => {
return !notgray.includes(no)
})
$("tr.namerow td").removeClass("death").removeClass("gray")
for (var player of playerInfo) {
if (player.vital == "death") {
$("tr.namerow .player_" + player.no).addClass("death")
} else if (graylist.includes(player.no)) {
$("tr.namerow .player_" + player.no).addClass("gray")
}
}
}
function reset() {
data = {
playerInfo: [],
indexOfName: {},
isAutoReload: false,
importlogday: 0,
discussLog: [],
deathLog: { exec: [], bite: [] },
isfilter: false,
inputMode: "simple",
villageno: getVillageno(),
setting: data.setting,
votetimes: [],
}
playerInfo = data.playerInfo
indexOfName = data.indexOfName
discussLog = data.discussLog
deathLog = data.deathLog
setting = data.setting
votetimes = data.votetimes
save()
}
//**********************************************
// 乱数表
//**********************************************
function rnd(n, digit) {
//0~n-1の整数乱数 digitを指定するとゼロパディング
digit = digit || false
var r = Math.floor(Math.random() * n)
if (digit) r = padding(r, digit)
return r
}
function padding(num, digit) {
//ゼロパディング
return ("0000000000" + num).slice(-digit)
}
function makernd() {
//四桁乱数
return rnd(10000, 4)
}
function makematrix(num) {
//乱数表
var l = []
var mat = ""
for (var i = 0; i < num; i++) {
l[i] = padding(i + 1, 2)
}
for (i = 0; i < num; i++) {
var r = 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
}
function makerndjob(num) {
//役職対応
var jobs = ["村 人", "占い師", "霊能者", "狩 人", "共有者", "狂 人", "背徳者"]
var cir = ["①", "②", "③", "④", "⑤", "⑥", "⑦"]
var result = ""
var isimo = num == 15 || num == 19
var n = isimo ? 7 : 6
var i, r
if (isimo) {
result += cir[rnd(n)] + "\n\n"
}
var l = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
for (i = 0; i < n; i++) {
r = rnd(10 - i)
result = result + jobs[i] + ":" + l[r] + rnd(100, 2) + "\n"
for (var j = r; j <= num - 1; j++) {
l[j] = l[j + 1]
}
}
return result
}
//**********************************************
// その他小物
//**********************************************
function getToday() {
//表示されている日付チェック
var day = /(\d{1,2})/.exec(body.html())
today = day ? day[1] - 1 : 0
newestDay = Math.max(today, discussLog.length - 1)
}
function getVillageno() {
var vno = $("title").text().slice(0, 6) - 0
return vno
}
function createSelectBox(option, selected, attr) {
//optionを持つselectを作る。str。optionは{value: innerHTML}の形式で
let attrtext = ""
if (attr) {
for (let k in attr) {
attrtext = attrtext + ` ${k}="${attr[k]}"`
}
}
var s = `"
return s
}
function editSpeakField(text) {
//発言欄をtextにする
textarea.val(text)
memoContainer.hide()
}
function popupMessage(text) {
//メッセージ
messageArea.text(text).show()
setTimeout(function () {
messageArea.hide()
}, 1500)
}
//**********************************************
// 便利設定
//**********************************************
var left
function setAlertVote() {
//未投票アラート
if ($("font[size=6]").size()) {
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()
},
},
})
left = counts
warningArea.html("未投票です! あと秒")
warningArea.append(cmbplayer)
warningArea.append(votebutton)
cmbplayer.on("change", function () {
$("select[name=CMBPLAYER]").val($(this).val())
})
$("#left").html(left)
setInterval(function () {
left--
$("#left").html(left)
}, 1000)
}
}
function receiveKeyResponse() {
//キー入力を受け付けるかどうか
$(window).on("keydown", function (e) {
if (setting.send_support.value == "ctrl" && e.ctrlKey && e.keyCode == 13) {
document.forms[0].submit()
} else if (setting.send_support.value == "shift" && e.shiftKey && e.keyCode == 13) {
document.forms[0].submit()
}
})
}
function dispSuggest() {
var colorselect = createSelectBox(COLORLIST, 0, { id: "colorlist" })
var coloredit = `
", { id: "settingtable" }).appendTo(settingArea)
settingArea.append(
``
)
for (var k in setting) {
var item = setting[k]
var tr = new Tr()
tr.add(item.name)
tr.add(createSelectBox(item.option, item.value, { id: k }))
tr.appendTo(settingTable)
}
var colorSettingTable = $("