人狼」カテゴリーアーカイブ

【iPhone / iPad】続・Ohajiki Web ブラウザでねじれ天国のUIをそこそこ快適にする

はじめに

前回記事では検索窓の幅調整だけというショボい内容だったが、今回大幅にUI改善を加えてみた。Ohajiki Web ブラウザの導入は前回記事を参考にし、貼り付けるコードをこの記事の最後のものに置き換えてほしい。

2021/03/26追記

コードを修正し、発言ボタンの2度押しを防止する機能を追加。また、各機能の有効/無効をフラグでコードの先頭に追加した。

追加内容

文学ブロッカー

規定文字数以上の発言を途中で省略する。コード内の BLK_MAX_LENGTH の値を変えれば何文字以上で省略するかを変更可能。デフォルトは800文字。

サイドバー格納

左の参加者リストおよび検索窓をデフォルトで非表示にし、ログを画面幅最大で表示できるように。サイドバーの中身は「リスト・検索窓を開く▼」ボタンで開閉可能(表示位置が適当なのはご愛敬)。

発言欄のフォントサイズを調整

これによって入力時にズームされないようになり、発言の全体が見やすくなった(はず)。

「村を出る」ボタンタップ時に確認ダイアログ表示

誤タップによる退村を防げる。

発言ボタン2度押し防止 2021/03/26追加

そのまま。通常の発言欄だけでなく、すべてのフォームに適用される。

その他

最初にねじ天の村かどうかを判定しているので、他のサイトで悪さをする可能性はありません。

修正版コード

タップでコピーされます。適用手順は前回記事を確認してください。このコードより下に文章はありません。

(function() {
  // 各機能ごとの有効フラグ。不要なものは true を false にすると無効になります。
  const USE_BLK_LTR = true;    // 文学ブロッカー
  const USE_ADJ_SEARCH = true; // 検索窓幅調整
  const USE_RMV_SIDE = true;   // 左リスト畳み込み&発言入力欄ズームなし
  const USE_CFM_EXT = true;    // 村を出るボタンに確認ダイアログ追加
  const USE_BLK_DBL = true;    // 発言ボタン2度押し抑止

  // BLK_MAX_LENGTH 以上の文字数を短縮表示する
  const BLK_MAX_LENGTH = 800;

  const url = window.location.href;
  if (!url.match(/nejiten\.halfmoon\.jp\/index\.cgi\?vid=/)) {
    return;
  }

  if (USE_BLK_LTR) {
    blockLiterature();
  }
  if (USE_ADJ_SEARCH) {
    adjustSearchBox();
  }
  if (USE_RMV_SIDE) {
    removeSidebar();
  }
  if (USE_CFM_EXT) {
    confirmExit();
  }
  if (USE_BLK_DBL) {
    disableSubmit();
  }

  // 文学ブロッカー
  function blockLiterature() {
    // 表示変更リンクのテキスト
    const OPEN_MES = '▼全文表示';
    const CLOSE_MES = '▲短縮表示';

    function createOpenDiv(mes_num) {
      let div = document.createElement('div');
      div.style.color = 'blue';
      let a = document.createElement('a')
      a.id = 'open' + mes_num;
      a.innerText = OPEN_MES;
      a.style.cursor = 'pointer';
      div.append(a);
      return div;
    }

    function createCloseDiv(mes_num) {
      let div = document.createElement('div');
          div.style.color = 'blue';
      let a = document.createElement('a')
      a.id = 'close' + mes_num;
      a.innerText = CLOSE_MES;
      a.style.cursor = 'pointer';
      div.append(a);
      return div;
    }

    function createMsgDiv(msg, id) {
      let div = document.createElement('div');
      div.id = id;
      div.innerText = msg;

      return div;
    }

    let mes = document.querySelectorAll('[class$=body1]');
    for (let i = 0; i < mes.length; i++) {
      if (BLK_MAX_LENGTH < mes[i].innerText.length) {
        let msg = mes[i].innerText;
        let shortMsg = msg.slice(0, BLK_MAX_LENGTH) + "...\n\n";
        mes[i].innerText = '';

        // 長文を退避
        let msgDiv = createMsgDiv(msg, 'msg' + i);
        let closeDiv = createCloseDiv(i);
        msgDiv.append(closeDiv);
        msgDiv.style.display = 'none';

        // 短縮文を作成
        let shortMsgDiv = createMsgDiv(shortMsg, 'shortMsg' + i);
        let openDiv = createOpenDiv(i);
        shortMsgDiv.append(openDiv);

        // クリック時に開閉を呼ぶようにイベントリスナ設定
        closeDiv.addEventListener('click', function() {
          toggle(i, true);
        });
        openDiv.addEventListener('click', function() {
          toggle(i, false);
        });

        // 追記
        mes[i].append(shortMsgDiv);
        mes[i].append(msgDiv);

      }
    }

    function toggle(i, isOpened) {
      const msgDivId = 'msg' + i;
      const shortMsgDivId = 'shortMsg' + i;
      let msgDiv = document.getElementById(msgDivId);
      let shortMsgDiv = document.getElementById(shortMsgDivId);

      if (isOpened) {
        // 全文表示から短縮表示に切り替える
        msgDiv.style.display = 'none';
        shortMsgDiv.style.display = 'inline';

        // 短縮時に発言が詰まってしまうので元発言のトップにスクロール
        shortMsgDiv.scrollIntoView(true);
      } else {
        // 短縮表示から全文表示に切り替える
        shortMsgDiv.style.display = 'none';
        msgDiv.style.display = 'inline';
      }
    }
  }

  // 検索窓幅調整
  function adjustSearchBox() {
    var target = document.querySelector('input#cse-search-box');
    const observer = new MutationObserver((mutations) => {
        mutations.forEach((mutation) => {
            target.style.width = "auto";
        });
    });

    var config = { attributes: true, childList: true, characterData: true }
    observer.observe(target, config);
  }

  // 左リスト削除
  function removeSidebar() {
    var side = document.querySelector('#side');
    var side2 = document.querySelector('#side2');
    var sides = document.createElement('div');
    sides.id = 'sides';
    sides.style.display = 'none';
    sides.appendChild(side);
    sides.appendChild(side2);
    var btn = document.createElement('span');
    var btnMsg = document.createTextNode('リスト・検索窓を開く▼');
    btn.id = 'btn';
    btn.classList.add('hope_toggle');
    btn.appendChild(btnMsg);
    btn.onclick = () => {
      if (sides.style.display === 'none') {
        btn.innerText = btn.innerText.replace('開く▼', '閉じる▲');
        sides.style.display = 'block';
      } else {
        btn.innerText = btn.innerText.replace('閉じる▲', '開く▼');
        sides.style.display = 'none';
      }
    }
    var content = document.querySelector('#content');
    content.appendChild(btn);
    content.appendChild(sides);
  
    document.querySelector('body > table > tbody > tr > td > table > tbody > tr > td > table > tbody > tr:nth-child(2) > td:nth-child(1)').style.display = 'none';
    document.querySelector('#main > td:nth-child(1)').style.display = 'none';
    document.querySelector('body > table > tbody > tr > td > table > tbody > tr > td > table > tbody > tr:nth-child(5) > td:nth-child(1)').style.display = 'none';
    document.querySelector('body > table > tbody > tr > td > table').style.width = 'auto';
    
    // 発言フォームのフォントサイズ変更(フォーカス時にズームされないようにする)
    let textAreas = document.querySelectorAll('textarea');
    (Array.from(textAreas)).forEach((ta) => { ta.style.fontSize = '24px' });
  }

  // 村を出るボタンに確認ダイアログ追加
  function confirmExit() {
    var exit = document.querySelector('input[value="村を出る"]');
    if (exit) {
      var form = exit.closest('form');
      form.onsubmit = () => {
        if (window.confirm('村を出ますか?')) {
          return true;
        } else {
          return false;
        }
      };
    }
  }

  // 発言ボタン2度押し防止
  function disableSubmit() {
    let forms = Array.from(document.querySelectorAll('form'));
    let btn = Array.from(document.querySelectorAll('input[type="submit"]'));
    
    forms.forEach((form) => {
        form.onsubmit = () => {
            btn.forEach((b) => {
                b.disabled = true;
            })
        }
    });
  }
})();

【iPhone / iPad】Ohajiki Web ブラウザでねじれ天国のUIをちょっと快適にする

※2021/03/25追記 こちらで紹介しているよりも多機能なスクリプトを次の記事で公開しています。合わせてご覧ください。

はじめに

500以上の役職が遊べる人狼サイト、ねじれ天国。99人村のようなアホみたいな村も不定期に開催されており、そのカオスっぷりは多くの人狼プレイヤーを引き付けてやまない。

この記事ではiOS向けブラウザアプリの「Ohajiki Web ブラウザ」でユーザースクリプトを適用して使うことで、スマホからでもちょっと快適にねじれ天国を操作する方法を解説する。具体的には、村中での左リスト下のGoogle検索ボックスが横に伸びてしまっているのを、適切なサイズに調整する。

※Android版は現在配信されていないようなので、本記事はiPhone / iPad ユーザーのみが対象です。もしAndroidでもユーザースクリプトが使えるブラウザがあれば教えてください。

Ohajiki のインストールと設定手順

インストール

AppStore Ohajiki Web ブラウザからアプリをインストールしてねじれ天国にアクセスして、適当な村を開く。

強制ダークモードの解除

※OSの設定がダークモードでなければ、この設定は不要かもしれないので飛ばしてください。

強制ダークモードがONになっているとねじれ天国の表示が崩れるため、オフにしておく。

強制ダークモードONの状態

画面中央下のアイコンをタップ→上に現れた同じアイコンをもう一度タップ→左上の歯車アイコン(設定)をタップして設定画面に遷移して行う。タップじゃなくてスワイプでも可。

タップ1度目
タップ2度目。左上に設定のアイコンが表示される。

設定画面の「目の保護機能」にある「強制ダークモードを使用」をオフにする。

強制ダークモードがONの状態
オフにすると画面全体が明るくなる。
ねじれ天国の表示も正常になった。

ユーザースクリプトの設定

上のスクショでもわかるように、画面左下のGoogle検索ボックスが横長になっていて、レイアウトが崩れている。ユーザースクリプトを適用することでこれを適切な幅に調整するため、先ほど同様に設定画面から「コンテンツ」にある「ユーザースクリプトの編集」をタップ。

ユーザースクリプトを実行するタイミングで設定箇所が異なる。

「タイミング」の画面が表示されたら「読み込み後」の方の書き込みボタンをタップする(おそらく読み込み前でも動くが、たぶん読み込み後の方がいい)。

読み込み後に適用されるユーザースクリプトの初期表示

初期値を削除し、ここに以下のコードをコピーアンドペーストする。タップでコピーされます。

(function() {
    'use strict';

    var target = document.querySelector('input#cse-search-box');
    const observer = new MutationObserver((mutations) => {
        mutations.forEach((mutation) => {
            target.style.width = "auto";
        });
    });

    var config = { attributes: true, childList: true, characterData: true }

    observer.observe(target, config);
})();
貼り付けたあと

「戻る」からタイミング画面に戻り、読み込み後のスイッチをONにする。

スイッチは緑が有効

これで設定完了。村のページをリロードすると検索ボックスの幅が正常になっているはず。

ENHANCED BY Google がちゃんと収まった

その他つぶやき

  • 上記の検索ボックス幅調整スクリプトはPC版でも利用できます。Tampermonkey や Greasemonkey などが導入してあれば、こちらからインストールできます。
  • 他にも文学ブロッカーやレイアウトの変更など、UIを改善するコードを追加公開するかも。需要次第なので村とかTwitterで声をかけてもらえるとやる気が出ます。
  • 仕様的にねじれ天国と関係のないサイトでもスクリプトが実行されてそう。害はないと思うけど、気が向いたら修正します。

人狼PL向けの自己紹介

※この記事は人狼SNSに記載していた「ゆーろという人狼プレイヤーについて」という日記の転載(一部リンク等修正版)です。
人狼SNSが閉鎖するため、こちらに避難しました。


自分の10年の歴史を振り返るついでに、自己紹介がてらおすすめログを貼っておきます。
といっても最初期から振り返るのではなくて、人狼BBSでデビュー〜短期村で場数を踏む、というバックグラウンドがあった上でのログなので、万一読まれる場合はそのあたりを考慮してください。
また、スタイル確立以後もやる気次第でサボりがちなので、今回はあまりサボってない村だけを厳選してお届けしております。。

おすすめログシリーズ

まず比較的初期のログから。

人狼物語@欧州6【試験弐型】ガチ好き達の集う村

家庭教師 ソニア (euro) @ 3d
しゃーないな、うちが出した宿題で今んとこ提出されてる分の採点でもしよか。
2点や。
状況推理がまだやから、75点中2点ね。
(239) 2007/12/07(Fri) 17:10:39

人の考察に点数をつけた村。このソニアは村人、そしてつけた相手は真占い師。ひどい。
なんかこの時代はさすがに若いなーという感じがする。。


続いて、俺的第二次長期人狼ブームと言える2010年〜2011年頃のログから続けていくつか。

人狼議事AS13 2010年Cレギュ村

受付 アイリス(euro) @ 6d
あとイアン。
あなたが村人なら。
アイリスが村人だってことを信じてくれたら。
もう勝ってるわ。
だからお互い見極めましょう。
ROUND4よ。
18 2010-01-14 (木) 午前07時頃

俺の中である意味伝説となっている、うにイアンと毎日毎日灰で殴り合った村。
当時の体力はすげーなと今になって思いました。。

受付 アイリス(euro) @ ep
さっさと決め打っちゃうメリットは、引き返せることだと思うな。
それって決め打ちじゃないって言われそうだけど。でも、早めにローラーしちゃうよりは、その決め打ちが合ってればすぐ決着がつく可能性がある。
間違ってて長引いたときでも、偽が真を演じたまま決着するのって結構難しいんだよね。襲撃が不自然だったり、すごく白いところに黒を出さなきゃいけなくなるから。
そうなったときに、本当に決め打ちでいいのか考え直せる人。
そういう人なら、とりあえず可能性の高そうな方で決め打って進んだ方が勝率は上がるんじゃないかなって思うな。
1329 2010-01-17 (日) 午後03時半頃

ついでに、同じ村で、なんかこないだも似たようなセリフ吐いてたのでピックアップ。
だいたいこの頃にはガチの思考としては早期決め打ち派というのが固まっていたみたいです。

人狼物語@リア充7 季節外れの村

家庭教師 ソニア(666) @ 人狼SNS
2010年 04月18日 15:12
おれは狂人いりだと思ってたが、実は狂信者だった。

最強の狂人バーニィをやった村での、狩人の感想。
あの狂人はもう出来る気がしない。現に10年で一度しかできていない。

人狼議事AS57 【飛び入り歓迎!】C国変則レギュガチ村

受付 アイリス(karanabe) @ 見物人
ソフィア面白いねー
ブレーキの外れたダンプカーみたいな感じ
@1 2010-09-03 (金) 午前07時頃

ブレーキの外れたダンプカー、って言い回しが面白かったので引用しました。
こないだのマリアと同じような勢いで喋っていたのがソフィア、ゆーろのようです。なんかたまにノリノリだとこんな感じになるらしい。あ、ちなみに惜しまれつつも初回で吊られました。

ぐた14 【赤】トロイカ村

※ぐた国消滅のためログ消失

イヴァン(euro) @ 1d
じゃあ、この話は誤爆ってことで終わりにしよう。
誤爆じゃないとして終わりにしちゃうと、きっとみんな逃げたと思うだろう。だからあれは誤爆だ。そして俺は狼だ。(「・ω・)「がおー
俺を狼だということにしてみんなの推理を進める。
俺は今日と明日で恐ろしいまでの白さを発揮して、その前提を打ち破ることにする。
(31) 2010/09/10(Fri) 13時半頃

なんかどうでもいい発言を誤爆だと疑われ、面倒になったので人狼COした村。もちろん村人で。
そうしたら初回に食われた。いくらなんでも人狼COして食われるとは思わなかった。

人狼物語@瓜科613 人間力で殴り愛ながら茶飲み話する村

娼婦 エルヴィラ (euro) @ ep
襲撃ミス全部俺やないか!!!!
( 10 ) 2011/02/04(金) 06:03:54

占い師のいない、人狼は仲間がわからない、というアホみたいな設定のクソ村に入ったら、妖魔を引いた。
そしたら襲撃なしが4回出て、全部俺を襲撃していた。何を言っているかわからないと思うが略

人狼物語@リア充35 ガチ好き達の集う村

悪戯好き イリス (thylakoid) @ 8d
これまではボンヤリと白いな、と思っている程度じゃったが。今日ひたすらログを読み、そしてテレサの返答を聞いて、完全に一本、『村人 テレサ』という像が浮かび上がったんじゃ。
正直に言って、「美しい」とすら思った。黒要素の付けようがない。
全ての行動に、意図が付随している。まだ聞いていない部分もあるが、聞いても白いと思えるだろうことが確信できる。
「この人が狼なら、負けてもいい」。これ、多分最終決定を出すまとめ役が言ってはいけない科白なんじゃと思う。
しかし、あえて言わせてもらう。もし狼で、ここまで意図を含ませた行動、言動を8日間も続けられるのなら。
黒要素を一つも挙げられんオレには、吊ることが出来ん。この人が狼なら、オレには吊ることが出来ん。
これがテレサを外した理由じゃ。【白決め打ち】、と言わせてもらおう。
(106) 2011/02/21(Mon) 01:15:41

ちょっと気恥ずかしい発言をいただきました、テレサが村人のゆーろです。
このイリスの言葉は強かったなあという気がする。


上記のガチ会で燃え尽きたのか、就職して人狼やる暇がなくなったのか、まあしばらく適当な村しかやっていなかったのですが、2013年末〜2014年春、夏は休んで秋〜冬にかけて第三次人狼ブームが来たので、そこから最新と呼べるログをみっつ。

人狼物語@リア充200 リア充天国

【赤】 アナスタシア (euro) @ 8d
だ・・・駄目だ まだ笑うな・・・
こらえるんだ・・・
し・・・しかし・・・
(*7) 2014/02/08(Sat) 07:28:09

人狼で村人を滅ぼしたというより、村人が村人を滅ぼした村。
人狼の勝ち方とはこうあるべきだと思っている。わりと理想論ではある。

人狼物語@薔薇の下273 鍋をつついて立冬を迎える村

情報屋 イングリッド(euro) @ 1d
【占い師CO】よ。
あたしの情報網を使えば、人狼かどうかなんてすぐにわかるわ。
一晩で3匹見つけるのも不可能ではないわね。
(6) 2014/11/11(Tue) 07:04:43

もっとも直近の人狼(役職)ログ。なんかもう、とにかく占い師を騙れば勝てる、というジンクスが自分の中でわりとあるので、人狼を引くとつい騙ってしまう。
この村もそれなりに仕事をしました。

ぐた659 宿縁の箱舟村

※ぐた国消滅のためログ消失

【人】 サンドラ(mikado) @ 3d
カチューシャ考察。今なら言語化出来る。
凄いぶっきらぼうにわかりやすく話すと、カチュは手順や足切りで死ぬ狼を相手にしていない、もしくはそれに対する考察を重要視していない。
ってのが根本にあり、経験や統計、勘を頼りにそれが正しいか「●▼」をつかって検証しつつ、今後の展開を狼の全体像を想像、そこから逆算して狼を見つけようとしている。
白い黒いは参考までにって感じじゃないかな。
此処が理解されてなくて、よくわからんって印象の人物になってると思う。
天気予報とかこの過程で出てきたものじゃないかな。
なんで、俺的には割と理解できる。
その上で逆説、これが狼にできるかどうか。
俺は自分狼の時にやらない。でも、出来ないとは言わない。
っていうのが、カチューシャに思う所。
だからあとは自分で頑張れw
(162) 2014/11/24(Mon) 23時頃

さっきのイリスに言われたのと似たようなこと(個人的に似てると思ってること)を言われた村人カチューシャがゆーろです。
自分の中での感覚的なスタイルを、他人から上手く言語化してもらったときって、うれしいですよね。


というわけで、そんな感じで。
ナルシストなので感想は随時受け付けております。

【人狼】長期クローン国の村立て人には廃村の権利がある

※もちろん、サーバ固有のルールがある場合はそちらに従いましょう。
 
人狼マニュアルボットなる twitter bot が、こんなことをのたまっているのを見かけました。


……いつから義務になったんですかね。心がけはよいと思うけれども。
 
この bot が原因かどうかはわかりませんが、一部プレイヤーに「長期人狼で立て逃げは死罪」……とまではいかなくても、万国共通のルール違反だと考えている人は少なからずいそうです。
 
でも義務はちょっと違うと思うのです。
長期クローン国というのはここでは概ね asbntby さん作成の「人狼物語」スクリプトを(改造)設置したサーバ群を指すのだと思われますが、元となるスクリプトにはそんな「義務」や「ルール」は明記されていません。カスタマイズして設置しているサーバでも、立て逃げ厳禁とまで書かれている方が少数派のように思います。(そんなガッツリ調べてないので保証はしません)
 
「ルールではないけど、守るべきマナーだ」
上記のような反論を抱かれた方もいるかもしれません。
もっともだと思います。
 
でも、オンラインで卓を立てて遊ぶボードゲームの類で、ルールであれマナーであれ立て逃げが厳禁とされている場って、あまり見かけないように思いません?
そもそも、廃村というシステム自体がないのであれば、立て逃げは確かにリソース(いわゆる村立て枠)を浪費してしまうので、制限してしかるべきでしょう。現に短期人狼サーバの人狼天国なんかは自動廃村のシステムがないので、大量の村が放置されて景観が損なわれていたりします。
 
けれど、人狼物語には廃村システムがあるので、人が集まらなければ自然にその村は消滅します。
集める義務、とやらは無いのですよ。廃村はシステムから許された村立て人の権利です。
 
昨今の長期人狼は村立てのハードルが上がりすぎだと思います。
もう少し、遊びたいときに気楽に立てて、集まらなかったら「うーん、今回は残念だったね。またこんど遊ぼう!」で廃村してもいいんじゃないのですかね、ゲームなんだし。
 
※重ねて書きますが、サーバのルールや注意書きには従うようにしましょう。
※この記事は立て逃げを推奨するものではありません。義務ではないにしろ、村が成立するように周りに呼びかけてみましょう。

こんぶ送別会2戦目 人狼相談ダイジェスト

どこに書こうか迷った結果、とりあえずここに。
 
こんぶ送別会の2戦目はせっかく人狼を引いたのに重くて動画を撮らなかったので、覚えてる限りで人狼の相談をダイジェストで。記憶ベースなので発言者とか内容とか日数が曖昧かつ適当です。あと村視点の展開を書いてないのでとみんぐ氏のレポートとかとあわせて見る感じで。。
 
初日。
動画のとおりゆーろ、ぴこ、しょうたの3人が人狼なのだけど、あれで初めてぴこの中身を知ったので逆に村中は触れづらかったという、、
なんか適当な流れでゆーろが占い師を騙ることになる
 
2日目。
ゆ「初日は占い結果ないんかよ!」(1戦目途中から来たので知らなかった)
ぴ「あーないねえ」
ゆ「おしえてよ! 危うく出すとこやったよ!」
し「あははw」
 
3日目。
ぴ「いでの占い結果どーすんの?」
ゆ「ひみつ」(黒出すつもりしかない)
し「あははw」
 
4日目。
ゆ「狩人食えたし(※吊られてます)さんざい(占い師)残しでええよね?」
ぴ「うん、豚(妖狐)溶かしてもらおう」
し「うんうん」
 
5日目。
ゆ「こんぶ絶対豚やとおもたのにいい!」
ぴ「まあ、こんぶ残そか……」
し「せやね」
 
6日目。
ゆ「さんざいしょうたさんじゃなくてほか占えよおおおおお」
ぴ「これ豚噛まないとまずくね?」
し「あー、そうだね」
ア(GM)「悩ましいねー」←こんとき絶対ニヤニヤしてる
ぴ「1戦目となんか違うしたぬき噛むか……」(とみんぐ豚で正解だった)
 
7日目。
ゆ「さんざいぴこじゃなくてとみんぐ占えよおおおおお」
ぴ「ここはもうさんざい食って力押しでええと思う」
 
8日目。
ゆ・ぴ「(とみんぐ吊れたので)ピサロ食いでー(・∀・)」
 
で、人狼2生存で勝ち。
なんか序盤どっかで「俺が勝たせたるでー」みたいなことをうそぶいたけどそれなりに頑張ったはず、、