「Lua」に関する日記

美文のキャラ倉庫とkawax/niconicoと日記

美文のキャラ倉庫

基本データ表示のためのLuaスクリプトほぼできた!
どうせサイトを公開したらスクリプトも全体公開になるのでソースコード置いときます。

(長いから折りたたむよ)

Module:BasicData

local p = {}

function p.process(frame)
    local args = frame:getParent().args
    local result = p.generate(args)

    return frame:preprocess(result)
end

function p.generate(args)
    local string_utils = require('Module:StringUtils')
    
    local fields = {
        { key = 'ふりがな', val = args['ふりがな'] },
        { key = '正式名称', val = args['正式名称'] },
        { key = 'フルネーム', val = args['フルネーム'] },
        { key = '英字表記', val = args['英字表記'] },
        { key = '他の名前', val = args['他の名前'] },
        { key = '公式サイト', val = args['公式サイト'] },
        { key = '登場作品', val = args['登場作品'] },
        { key = '種族', val = args['種族'] },
        { key = '性別', val = args['性別'] },
        { key = '年齢', val = args['年齢'] },
        { key = '大きさ', val = args['大きさ'] },
        { key = '一人称', val = args['一人称'] },
        { key = '二人称', val = args['二人称'] },
    }
    
    local info_rows = {}
    local cargo_rows = {}

    for _, field in ipairs(fields) do
        -- 値が存在し、空文字列でない場合のみ行を生成
        if field.val and field.val ~= '' then
            local key = field.key
            local value = field.val
            if key == 'ふりがな' then
                local sortkey = string_utils.sortkey(value)
                table.insert(cargo_rows, string.format('Furigana = %s', value))
                table.insert(cargo_rows, string.format('FuriganaSort = %s', sortkey))
                if args['表示用ふりがな'] and args['表示用ふりがな'] ~= '' then
                    value = args['表示用ふりがな']
                end
                value = string.format('%s{{DEFAULTSORT:%s}}', value, sortkey)
            elseif key == '登場作品' then
                local titles = mw.text.split(value, '、', true)
                local converted_titles = {}
                for _, title in ipairs(titles) do
                    if title == 'ふにゃ(ゲーム)' then
                        table.insert(converted_titles, '[[ふにゃ(ゲーム)|ふにゃ]][[Category:登場作品:ふにゃ]]')
                    else
                        table.insert(converted_titles, string.format('[[%s]][[Category:登場作品:%s]]', title, title))
                    end
                end
                value = table.concat(converted_titles, '、')
            elseif key == '種族' then
                local kinds = mw.text.split(value, '、', true)
                local converted_kinds = {}
                for _, kind in ipairs(kinds) do
                    table.insert(converted_kinds, string.format('[[%s]][[Category:種族:%s]]', kind, kind))
                end
                value = table.concat(converted_kinds, '、')
            elseif key == '性別' then
                value = string.format('%s[[Category:性別:%s]]', value, value)
            end
            table.insert(info_rows, string.format('<tr><th>%s</th><td>%s</td></tr>', key, value))
        end
    end


    local result = ""
    
    if next(info_rows) then
        result = string.format("%s<table class=\"wikitable\">%s</table>", result, table.concat(info_rows, ""))
    end
    
    if next(cargo_rows) then
        result = string.format("%s{{#cargo_store:_table = BasicData|%s}}", result, table.concat(cargo_rows, "|"))
    end

    return result
end

return p

Module:StringUtils

local p = {}

function p.sortkey(furigana)
    if furigana == "" then return "" end

    local sortkey_table = {
        {"が", "か"}, {"ぎ", "き"}, {"ぐ", "く"}, {"げ", "け"}, {"ご", "こ"},
        {"ざ", "さ"}, {"じ", "し"}, {"ず", "す"}, {"ぜ", "せ"}, {"ぞ", "そ"},
        {"だ", "た"}, {"ぢ", "ち"}, {"づ", "つ"}, {"で", "て"}, {"ど", "と"},
        {"ば", "は"}, {"び", "ひ"}, {"ぶ", "ふ"}, {"べ", "へ"}, {"ぼ", "ほ"},
        {"ぱ", "は"}, {"ぴ", "ひ"}, {"ぷ", "ふ"}, {"ぺ", "へ"}, {"ぽ", "ほ"},
        {"ゔ", "う"},
        {"ぁ", "あ"}, {"ぃ", "い"}, {"ぅ", "う"}, {"ぇ", "え"}, {"ぉ", "お"},
        {"ゃ", "や"}, {"ゅ", "ゆ"}, {"ょ", "よ"}, {"っ", "つ"}, {"ゎ", "わ"},
        {"あー", "ああ"}, {"いー", "いい"}, {"うー", "うう"}, {"えー", "ええ"}, {"おー", "おお"},
        {"かー", "かあ"}, {"きー", "きい"}, {"くー", "くう"}, {"けー", "けえ"}, {"こー", "こお"},
        {"さー", "さあ"}, {"しー", "しい"}, {"すー", "すう"}, {"せー", "せえ"}, {"そー", "そお"},
        {"たー", "たあ"}, {"ちー", "ちい"}, {"つー", "つう"}, {"てー", "てえ"}, {"とー", "とお"},
        {"なー", "なあ"}, {"にー", "にい"}, {"ぬー", "ぬう"}, {"ねー", "ねえ"}, {"のー", "のお"},
        {"はー", "はあ"}, {"ひー", "ひい"}, {"ふー", "ふう"}, {"へー", "へえ"}, {"ほー", "ほお"},
        {"まー", "まあ"}, {"みー", "みい"}, {"むー", "むう"}, {"めー", "めえ"}, {"もー", "もお"},
        {"やー", "やあ"},                   {"ゆー", "ゆう"},                   {"よー", "よお"},
        {"らー", "らあ"}, {"りー", "りい"}, {"るー", "るう"}, {"れー", "れえ"}, {"ろー", "ろお"},
        {"わー", "わあ"}, {"ゐー", "ゐい"},                   {"ゑー", "ゑえ"}, {"をー", "をお"},
    }

    for i, v in pairs(sortkey_table) do
        local from = v[1]
        local to = v[2]
        furigana = string.gsub(furigana, from, to)
    end
    
    return furigana
end

return p

まあ、おおむね引数で渡された項目を表に表示して、一部項目に特殊対応を入れている程度です。
ソートキー生成は、アンパンマンDB用にPHPで作っていたものをLuaに移植したもの。
文字列連結を繰り返すと遅くなるらしいので、ある程度はテーブルやstring.formatを使って連結を減らしています。
どこまで効果あるのか知らないですけどね。

欲を言えば、年齢や大きさも特殊対応入れたい。
昔作ったC#用単位・数値変換クラスをLuaに移植して、年齢順一覧とか大きさ順一覧とか作りたいんですよね。
ただ、ここで欲張ると公開がさらに先になっちゃいますからね。

kawax/niconico(TGWS内部処理)

PHPのバージョンを8.5に上げたいとか言ってましたが、思わぬところで問題が出ました。

kawax/niconico: ニコニコ動画API

ニコニコ動画の情報取得に使ってるこのライブラリ、誤動作とか致命的なエラーはないんですが、PHP8.4でDEPRECATEDが出るようになったのです。
場所を修正方法は把握していて、niconico/src/Search/Query.phpの16行目と、同29行目のパラメータです。
これが、null許可しないと明示されているのに初期値null扱いされているので、将来のバージョンでエラーにするから今のうちに直しとけよという警告を出してくれているというわけです。
昔のバージョンでは、null許可すると明示しなくても初期値nullにしておけば、暗黙的にnull許可扱いにしてくれていたんですが、それをやめることになったんですね。
だから、修正方針としては以下のように、型指定に「?」を付けて、null許可を明示します。

-    public function __construct(array $query = null)
+    public function __construct(?array $query = null)
-    public static function create(array $query = null): static
+    public static function create(?array $query = null): static

…というIssueとプルリクを明日出しとこう。そうしよう。

日記

さっきDIFFっぽいもの書きましたが、DIFFとしてのシンタックスハイライトは入れていません。
「```diff-php」とやればPHPのDIFFが出ると期待したんですが、コードブロック機能が壊れてしまいました。
なので、今は「```php」として、普通のPHPコード扱いでシンタックスハイライトを入れています。
今使っているシンタックスハイライトがDIFFに対応しているのかはわからないのですが、コードブロック機能が壊れるのは避けたほうがよさそうな気がします。
このコードブロック機能は私が作ったTgwsMarkの機能なんで、Issueとプルリクというより、自分で直すべき案件ですね。

アンパンマンDBとドラゴンクエスト10と美文のキャラ倉庫とTGWS内部処理

アンパンマンDB(記事更新)

来週の放送情報。
SLマンとコキンちゃんは昔順序逆のやつがあったらしい。

ドラゴンクエスト10(おきがえリポちゃん)

あぶない浴衣
[DQXショップ] おきがえリポちゃん ~ あぶない浴衣 ~ (2026/7/22)|目覚めし冒険者の広場
いつもの月例貸衣装。
浴衣の片側が大きくはだけてインナーが丸出しになっている服。
ちなみに男はインナーなしなので片乳丸出しです。

あぶない浴衣
うーん、確かにこれは危ない男…。

他の写真は以下から。
写真置き場「2026/07/24」

美文のキャラ倉庫

モジュールを読み込んでくれない問題、XMLからモジュールをインポートしてたんですが、そこでのモデルとフォーマットの指定が通常の記事と同じだったので、モジュールじゃなくて記事扱いされていたのでした。
移行用プログラムを修正してScribuntoとして取り込まれるように修正しました。
そしてもう一つ、Luaの実行用バイナリに実行権限がついていなかったのも一因でした。
Windowsめぇ…。

TGWS内部処理

とりあえず、致命的な問題はなさそうなので、PHP8.4に上げてみました。
うーん、事前調査で出なかったDEPRECATEDがいっぱい出てたぞ…。

美文のキャラ倉庫とTGWS内部処理とドラゴンクエスト10

美文のキャラ倉庫

ScribuntoでLua書いてるけど、そもそもモジュールを読み込んでくれない!
何か操作が必要なのか?

TGWS内部処理

ともあれ、古いコードが残りすぎていたPukiWikiは完全に脱したので、PHPのバージョン上げ作業。
今のところ問題はなさそう。
8.4に無事上がったら、もうストッパーになるコンテンツはないので、エラーを見ながら8.5まで早々に上げる予定。

ドラゴンクエスト10(ミフミン)

鉄鬼軍王キラゴルド

  • 鉄鬼軍王スレイヤーの称号を得た!

ミフミンも問題なく勝利!
今回はストームカイザー入りでやったんですが、都合のいい耐性のサポが見当たらなかったんで、ほぼ無策パーティーで特攻することに。
せっかくなのでミフミンも愉快なポーズでゴールドアストロンしたかったけど、キラゴルドさん全然こっち狙ってくれなかった!

ヘルクラウダー
またしてもついでにしばかれるヘルクラウダーさん。

他の写真は以下から。
写真置き場「2026/07/23」

美文のキャラ倉庫とドラゴンクエスト10

美文のキャラ倉庫

WordPressではAdvanced Custom Fieldsを使って運用していたキャラの基本データ、MediaWikiではCargoを使ってみることにしました。
Scribuntoによるプログラミングを組み合わせると結構いろいろできそう。
今は普通に書いたフリガナからソート用のキー生成(コックピード→こつくひいと)を試しています。

しかし…Scribuntoで使うLuaと言えば、Bomber丸World作ってた時にスクリプトエンジンとして検討していたやつじゃないか。
あのときちょっと学んだやつとここで再会するとは。

ドラゴンクエスト10(しろいコキン)

魔法の迷宮ボスモンスター「鉄鬼軍王キラゴルド」登場! (2026/7/14)|目覚めし冒険者の広場
ふくびき所更新!
さっそく引きまくって1等当てたぞ!

ウマしっぽ・白
ウマしっぽ!

はい、おでかけツールのほうの景品です。
プクだと自前のプクしっぽに干渉してしまってちょっと微妙。
白じゃなくてプクしっぽをごまかせるリボンタイプのほうにしたほうが良かったかも。

(そうは言えども…)

鉄鬼軍王キラゴルド

  • 鉄鬼軍王スレイヤーの称号を得た!

はい!ボスコインもちゃんと当てました!
勝ちました!

一応、呪文耐性が必要とか、なんか魅了してくるらしいとか聞いたので、その辺の最低限の対策だけしてから、あとはぶっつけ本番で。

鉄鬼軍王キラゴルド ゴールドアストロン
キラゴルドと言えばこれ!ゴールドアストロン!
受けずにはいられない!!

鉄鬼軍王キラゴルド ゴールドシャワー
金属だから硬そうってイメージで、竜術士連れて行ったけど、相性としては新職のストームカイザーのほうが良かった模様。
まあ危なげなく勝てたからよし!
一発で現物出たのでなおよし!

ヘルクラウダー
ついでにヘルクラウダーも殴った!

他の写真は以下から。
写真置き場「2026/07/22」

PukiWiki

いつまでも残ってたPukiWikiのコンテンツ、必要なページだけ普通のHTMLに移して、PukiWikiで作ったページは閉じました。
うちは自作のシステムが中心となって動いているサイトなので、よそのシステムが入り込んでいると、扱いにくかったんですよね。
しかも、いまやメンテナンスしていない、ほんのわずかのコンテンツにしか価値がないものに、フルスペックのCMSが入っているので、大げさすぎるって言うのもありました。
この移設により、めでたくただのHTML1枚に収まりました。
まあ、内部的には自作のシステムがHTMLを生成してたりするんですが、それでも実質1ファイルなんで、シンプルにはなりました。

同じような境遇のページに、Bomber丸World開発室があるんですが、こっちは残したいコンテンツがまあまあいっぱい眠ってそうなんで、ゆっくりじっくり作業していきます。

CBWLua

LuaのBomber丸World用カスタマイズ版ができたのでアップしました。
本当は5.1.2の日本語対応版を作りたかったんですが、やはりというか日本語化パッチは5.1.2には対応していなかったようで。
そんなわけで、ちょっと古いかもしれませんが5.1の日本語対応版を使うことになりました。
変数名には日本語は使えませんよ!

TEPOLとZTB

デジカメは見つけたんですが使い方がわからず分子模型の写真は取れませんでした。
有機化学のセットだったので金属結晶はほとんど作れないようでした。

今日はNeoMuplのランダム演奏時のスキップ率を実装しました。
それ以外で実装するかどうかは未定です。
もしかしたらオプションで決めるようにするかもしれません。

Luaのほうはというと、変数への代入も案外あっさりできて、ついでにテーブルの新規作成とかも作ってしまって、ガベージコレクションの様子を確認したりもしました。
基本的にLuaでは細かいプログラムをたくさん同時並列的に動かすのでガベージコレクションは何も設定しなくても事実上分散して行われます。
5.1の意味があまりないです。

Lua5.1リファレンスマニュアル

う~ん、Luaの、C側での関数リファレンスも見つけたので、うちのWikiの存在意義が半分ぐらいなくなってしまったようです…。
まあ、関連性の深い関数などをすぐに参照できるというのがうちのWikiの強みだったりするし、順を追って発展させていくチュートリアルなどもあればまた存在価値というのは高めていけそうですが。

で、Bomber丸WorldにLuaを組み込むほうもわりとそれなりにでき始めています。
まあ、lua_openしてloadしてcloseするクラスを作ってそれが今のところエラーを起こしていないというところなんですが。

ルア

う~む、組み込みに特化したプログラミング言語Luaなるものがあるらしいです。
まあ、確かになかなか性能は素晴らしいものがあるのですが、やはりBomber丸Worldに組み込むには狙っている方向性が違っていて組み込めそうにありませんでした。
もっとも、プログラミングコーナーで作り始めてしまった以上どちらにしても後戻りはできなかったんですけどね。
ゲーム本体側からスクリプトの中の特定の機能を気軽に呼び出すというのはLuaでは結構面倒な手順を踏まないといけないんですよ。
スクリプトの実行中にスクリプトの内容そのものを書き換えることもできなさそうだったし。

アキヤメの舞台設定などを復活させました。
前はあまりにも多すぎるキャラデータをHTMLに変換する労力に耐え切れず閉鎖してしまったのですが、簡易的にテキストをHTMLに変換するプログラムを組んでどんどんキャラを出していくことにしました。