[PR]上記の広告は3ヶ月以上新規記事投稿のないブログに表示されています。新しい記事を書く事で広告が消えます。
(雛´-`).。oO(#TRPG
— しゅんしゅんひよこ (@Shunshun94) May 1, 2020
探索者の簡単な紹介カードをキャラクター保管所で作った CoC のキャラクターシートからささっと作れるツールを作ってみた。)https://t.co/xuK9fIqwp6 pic.twitter.com/MWxcioE0DZ
先日、クトゥルフ神話 TRPG で庭師は何を口遊む(リンク先シナリオ注意。読むとプレイできなくなる)というシナリオをやってきた。こちら、「秘匿ハンドアウト」というルールが使われておりキャラクターシートを作った後そのまま他の PL に共有することはできない。だが、公開可能な範囲で他のプレイヤーにデータを共有したい、という要求があった。
そこで、キャラクターの紹介カードを生成するツールを用意した。キャラクター画像の URL とキャラクターシートの URL を入れることでそれっぽいカードを生成できる。カードを生成した後、これをスクリーンショットして共有すればよい。ただ、これを画像で保存する機能を用意できていない。
カードには画像、名前、職業、性別、年齢、その他のメモおよび主な能力値が表示されている。名前、その他のメモ、主な能力値は後から編集が可能。主な能力値にはよく使う現在正気度ポイント、目星、図書館、聞き耳およびその他の能力からトップ4の値になっているものを表示している。ただしトップ4の値の中に母国語は含まれていない。
なお、キャラクター画像には Picrew の共有用 URLを使うことも可能になっている。
もっとモジュール使えば楽、特に HTML のパースはそうなんだろうけど、さくらインターネットのレンタルサーバ ライトプランで動くことを前提に書いている。
#!/usr/bin/perl
use HTML::Parser ();
use LWP::UserAgent;
use JSON::PP;
use CGI;
use Encode;
my $cgi = CGI::new();
my $browser = LWP::UserAgent->new;
my $enc = "utf8";
print "Status: 200 OK\n";
print "Content-Type: application/json; charset=UTF-8\n\n";
my $url = $cgi->url_param('url');
my %ogps = (result => "OK");
sub when_open_tag_found {
my ($self, $tagname, $attr) = @_;
if($tagname eq 'meta' and $attr->{'property'} and $attr->{'content'} ){
$ogps{$attr->{'property'}} = Encode::decode($enc, $attr->{'content'});
}
if($tagname eq 'meta' and $attr->{'name'} and $attr->{'content'} ){
$ogps{$attr->{'name'}} = Encode::decode($enc, $attr->{'content'});
}
}
if($url) {
eval {
my $response = $browser->get($url);
$ogps{'url'} = $url;
my $parser = HTML::Parser->new(
api_version => 3,
start_h => [\&when_open_tag_found, "self, tagname, attr"]);
$parser->parse($response->content);
my $json = JSON::PP->new->utf8->space_after->encode(\%ogps);
print $json;
};
if (my $error = $@) {
$json = JSON::PP->new->utf8->space_after->encode({
result => "NG",
message => $error
});
print $json;
}
} else {
print '{"result": "NG", "message":"No URL is found in parameter"}'
}
exit;
例によって jQuery でやっているがイベントのバインド部分と途中経過の表示部分のみ。
fileReader に素直なループで読み込ませると fileReader.result がどんどん上書きされてしまうのでウェイトを入れている。ただ、 fileReader を複数 new すればこんなことしなくてもいい気はする。さらに、今は Blog.text() なるメソッドを使う方がベターっぽい。
const fileReader = new FileReader();
fileReader.onload = (e) => {
$('#result').val(`${$('#result').val()}\n${fileReader.result}\n------------------------------`);
};
$('#base').on('drop', (e) => {
e.preventDefault();
const files = e.originalEvent.dataTransfer.files;
const recursiveRead = (array, cursor) => {
if(array.length > cursor) {
$('#base').text(`読み込み中…… ${cursor + 1} / ${array.length}`);
fileReader.readAsText(array[cursor]);
setTimeout(()=>{recursiveRead(array, cursor + 1);}, 500);
} else {
$('#base').text(`おしまい!`);
}
};
recursiveRead(files, 0);
});
妖精の手はオンセでは使いにくい。その処理があまりに独特であり、オンセではちょっと頭を使わないと処理がおいつかない。Twitter で検索してみるとこんな感じである。
これをオンセ中にササッと計算するブックマークレットを用意した。
最後に出た目を10とし、クリティカルしたものとして判定を継続するための式を出力する。クリティカル値が11以上だった場合などは考慮されていないので注意。
なお、以下からも試せる
元々の判定結果:
振り直しの式:
JRuby でなんかなんとかした。コアな部分は以下。全体を見るならば https://github.com/Shunshun94/myJruby/tree/5a765cbbaf79364e4f7a7c7ad436b885ca240a82 からどうぞ。
public class App
{
public static void main( String[] args )
{
ScriptEngineManager manager = new ScriptEngineManager();
ScriptEngine engine = manager.getEngineByName("jruby");
try {
// 実行ディレクトリを MyJRuby/src/main/resources/BCDice/src にしている
// ソースコードは https://github.com/ysakasin/bcdice-api/blob/master/lib/bcdice_wrap.rb からコピペしたのをいじいじ。
engine.eval("$:.unshift File.dirname(__FILE__)");
engine.eval("require \"./bcdiceCore\"\nrequire \"diceBot/DiceBot\"\nrequire \"diceBot/DiceBotLoader\"");
engine.eval("DICEBOTS = ([DiceBot.new] + DiceBotLoader.collectDiceBots).\r\n" +
" map { |diceBot| [diceBot.id, diceBot] }.\r\n" +
" to_h.\r\n" +
" freeze\r\n\r\n" +
" SYSTEMS = DICEBOTS.keys.\r\n" +
" sort.\r\n" +
" freeze\r\n\r\n" +
" NAMES = DICEBOTS.\r\n" +
" map { |gameType, diceBot| {system: diceBot.id, name: diceBot.name} }.\r\n" +
" freeze\r\n" +
"");
engine.eval("dicebot = DICEBOTS['SwordWorld2.0']");
engine.eval("bcdice = BCDiceMaker.new.newBcDice");
engine.eval("dicebot.bcdice = bcdice");
engine.eval("puts dicebot.dice_command('aaaaa', 'SwordWorld2.0')");
engine.eval("puts dicebot.dice_command('FT', 'SwordWorld2.0')");
engine.eval("puts dicebot.dice_command('2d6', 'SwordWorld2.0')");
engine.eval("puts dicebot.dice_command('K20', 'SwordWorld2.0')");
engine.eval("puts dicebot.dice_command('k20', 'SwordWorld2.0')");
} catch (ScriptException e) {
e.printStackTrace();
}
}
}