[[category:ソード・ワールド2.5]]
本ツールはTRPGシステム「ソード・ワールド2.5」を遊ぶためのWebアプリです。ユーザーは自身のキャラクターを登録し、特定の活動を行うユニットに所属させる事ができます。アプリ側はユニットに設定されている属性(活動内容や所在地、メンバーの平均レベルなど)を元にキャラクターが実行可能なイベントを提示し、その成否を記録していくことができます。
==プロジェクト全体仕様まとめ(2025/05/18時点)==
開発環境:http://localhost/index.php
===基本設計===
*TRPG(SW2.5)向けのユニット・キャラクター・イベント管理ツール
*フラットファイル(JSON)ベースでストレージ管理(DBレス)
*ユーザーアカウントごとに個別ファイルを管理(characters, units, logs, イベントなど)
*管理者アカウントは「config.php」ファイル内で設定
open_sw25/
├── public/ # 公開ディレクトリ(Webサーバーのドキュメントルート)
│ ├── index.php # フロントコントローラー
│ └── assets/ # 静的ファイル(CSS, JS, 画像など)
│ ├── css/
│ │ └── style.css
│ ├── js/
│ │ └── main.js
│ └── images/
├── src/ # PHPのソースコード
│ ├── Auth.php
│ ├── Character.php
│ ├── Unit.php
│ ├── Member.php
│ ├── Event.php
│ └── Log.php
├── data/ # ユーザーデータ(JSONファイル)
│ ├── users/ # 各ユーザーごとのディレクトリ
│ │ ├── users.json # ユーザ情報の管理用ファイル
│ │ ├── user_{uid}/ # ユーザごとの個別データ
│ │ │ ├── characters.json
│ │ │ ├── units.json
│ │ │ ├── user_events.json # 一般アカウント用イベントデータ
│ │ │ ├── character_log.json
│ │ │ ├── invites.json
│ │ │ └── unit_log.json
│ │ └── ...
│ ├── lists
│ │ ├── works_list.json
│ │ ├── local_list.json
│ │ └── ...
│ └── events/ # 全体公開イベントデータ
│ ├── 冒険者.json
│ └── ...
├── config/ # 設定ファイル
│ ├── function.php # その他補助用の関数格納
│ └── config.php
└── README.md # プロジェクトの説明
===🔐 アカウント===
各ユーザーアカウントは以下のデータを個別に保持:
*characters.json:キャラクター管理
*units.json:ユニット管理
*character_log.json:キャラクターのログ
*unit_log.json:ユニットのログ
===👤 キャラクター登録・管理(アカウント単位)===
*id(キャラクターID):一意のランダム英数字(自動生成)
*name(キャラクター名):キャラ名(フリーテキスト)
*sheetUrl(キャラクターURL):キャラシートURL(json取得に使用)
*description(キャラクター概要):キャラ概要(改行可)
*tags(タグ):タグ(一覧上での並び替え等に使う)
===🛡️ ユニット登録・管理(アカウント単位)===
*id(ユニットID):一意のランダム英数字(自動生成)
*name(ユニット名):ユニット名
*description(ユニット概要):概要(改行可)
*works(ワークス):活動内容(works_list.jsonから選択)
*location(所在地):
**place_name(所在地:地名):地名(フリーテキスト)
**region(所在地:地方):地方(local_list.jsonから選択)
**scale(所在地:規模):規模(「キャンプ」「基地」「村」「町」「都市」「国」から選択)
*base(拠点):
**base_name(拠点名):拠点名
**base_description(拠点概要):拠点概要(改行可)
**base_cost(維持費):維持費(G単位)
*skill_type(技能):"冒険者" or "一般技能"(自由入力)
*members(メンバー):
**character_id(キャラクターID)※
**owner_gid:キャラクター保有アカウントのgid
**position(役職):役職(フリーテキスト)
**level(レベル):スキルlevel(数字入力)
**status:承認状況
*AL(平均レベル):技能の平均レベル(数字入力)
*leader_id(リーダー):リーダーキャラID(メンバーから選択)
*creator(ユニット作成者):ユニット作成者のアカウントID
*created_at / updated_at(作成日時/更新日時):タイムスタンプ
※メンバーに選択可能なキャラクター
自身が所有するキャラクターのみ選択可能ですが、他アカウントのキャラクターであっても「招待中」という形でメンバー候補に加えられる。招待中のキャラクターを保有しているユーザーは、該当のユニットページで「ユニットに参加する」と操作することで正式にユニットへ参加できます(その後、ユニットからは任意のタイミングで脱退できます)。
🔄 キャラクターの兼業
キャラが複数のユニットに所属可能。選択中キャラが属する各ユニットで可能なイベントが選択可能になる。UI的には、画面上部にキャラ一覧→選択→ユニットとイベント表示、みたいな流れ。
===🎯 イベント(events/ワークス名.json)===
====イベントデータ構造====
*id(イベントID)
*title(イベント名)
*description(イベント詳細):難易度含めたすべてのテキスト(自由記述)
*min_al(min_al):必要AL
*requires(前提イベント:省略可):前提イベントID(文字列 or 配列)
*以下、ユニット条件でフィルタ可能なオプションキー(全部文字列 or 配列)
**unit_id(自身のアカウント保有ユニットから選択)
**base_name
**place_name
**region
**scale
====フィルタロジック====
*指定されたユニットの情報に対して、すべての条件を満たすイベントのみ提示
*条件が存在しないものは無条件(つまり、広く使える)
※ 条件に合うイベントのみ表示可能。前提イベントを駆使すればストーリー仕立てが可能になる設計。
例:
{
"id": "event_001",
"title": "港の怪しい取引",
"description": "夜の港で奇妙な荷物が…\\n依頼主は名もなき老漁師だった。",
"min_al": 3
}
===🗃️ ログの種類===
*キャラクターログ(character_log.json)
**所属ユニットの履歴
**実行したイベントと結果
**ユニット脱退・解散などの経緯
{
"char_abc123": ["event_001", "event_007"]
}
{
"action": "join unit",
"unit_id": "unit_abc",
"timestamp": "2025-05-15T15:00:00+09:00"
}
{
"event_id": "ev_battle_001",
"unit_id": "unit_abc",
"result": "success",
"timestamp": "2025-05-18T15:00:00+09:00"
}
*ユニットログ(unit_log.json)
**メンバー加入・脱退
**イベント実行履歴
**ユニットの更新・解散履歴
===🖥️ UI構成と機能整理===
====① メイン画面(ゲームプレイ/イベント選択)====
*表示内容:
**選択中キャラクター(アイコン・名前・URL)
**所属ユニットの一覧(選択可)
**条件に一致するイベントの一覧(前提イベントチェック、AL/拠点などの一致で絞り込み)
**イベントの詳細(description)
**「このイベントを実行」ボタン(ログ保存)
*ユーザーができる操作:
**キャラ切り替え
**ユニット選択
**イベント実行 → 成功/失敗などの選択肢 → ログに記録
====② キャラクター管理画面====
*表示内容:
**自アカウント保有キャラ一覧
**各キャラのステータス、所属ユニット、ログボタン
**ユーザーができる操作:
**キャラクター登録(URL・名前・概要・タグ)
**キャラクター編集
**キャラクター削除(脱退処理込み)
**キャラクターログ閲覧(所属履歴、イベント履歴)
====③ ユニット管理画面====
*表示内容:
**自アカウントが作成・管理するユニット一覧
**ユニット詳細、所属キャラ、作成者、権限保持者など
*ユーザーができる操作:
**ユニット作成
**ユニット編集(名前、ワークス、拠点、構成メンバーなど)
**メンバー追加・削除
**操作権限の付与(他アカウント)
**ユニット削除(解散ログ記録)
====④ イベント管理画面====
※サイト管理者と一般ユーザーで仕様が異なる!
🔒 サイト管理者(管理者フラグあり)
*できること:
**ユーザー全体へのイベント提供が可能
👤 一般ユーザー
*できること:
**自分が作成したユニット専用イベントの作成・編集
**条件:そのユニット名/拠点名/所在地に限定
**そのユニットに所属しているキャラのみが選択可能
**自作イベントの削除・修正
**自作イベントでも、他人に使わせたくないものはロック可(?)
"public_scope": "global" | "unit_only"
イベント作成・編集フォーム内にチェックボックスを設置:
・表示ラベル例:「全体に公開する(管理者のみ)」
・管理者ログイン時のみこのチェックボックスを操作可能
・一般ユーザーにはこの項目は表示されるが「読み取り専用(disabled)」で操作不可
====⑤ アカウント管理画面====
*表示内容:
**表示名
**ユーザーID
**パスワード変更
**アカウント削除ボタン
*ユーザーができる操作:
**表示名・パス変更
**アカウント削除(ユニットとキャラにログが残る)
===🔥 削除時の挙動定義===
*アカウント削除時
**保有ユニット:解散 → キャラログに「ユニット解散(アカウント削除)」記録
**保有キャラ:削除 → 所属ユニットには「キャラ脱退(アカウント削除)」を記録
*ユニット削除時
**所属キャラのログに「ユニット解散(ユニット削除)」を記録
*キャラクター削除時
**所属ユニットから脱退 → ユニットログに「キャラ脱退(キャラ削除)」を記録
===🔧 その他設定ファイル===
*works_list.json:ワークス名一覧
*local_list.json:地方名一覧
=テストしたAPI=
==①―A アカウントA登録 & cookie保存==
curl -X POST http://localhost/api/user/register \
-H "Content-Type: application/json" \
-d '{"display":"ユーザA","id":"userA","pass":"passA"}' \
-c cookieA.txt
==①―B アカウントB登録==
curl -X POST http://localhost/api/user/register \
-H "Content-Type: application/json" \
-d '{"display":"ユーザB","id":"userB","pass":"passB"}' \
-c cookieB.txt
==② アカウントAでログイン==
curl -X POST http://localhost/api/user/login \
-H "Content-Type: application/json" \
-d '{"id":"userA","pass":"passA"}' \
-b cookieA.txt -c cookieA.txt
==③ キャラA作成 & ユニット作成==
===キャラA===
curl -X POST http://localhost/api/char/create \
-H "Content-Type: application/json" \
-b cookieA.txt -c cookieA.txt \
-d '{"name":"キャラA","sheetUrl":"https://sheet/a","description":"Aのキャラ","tags":"test"}'
# 返り値の "id" を控える → 例: CHAR_A_ID
===ユニットA===
curl -X POST http://localhost/api/unit/create \
-H "Content-Type: application/json" \
-b cookieA.txt -c cookieA.txt \
-d '{"name":"ユニットA","description":"テストユニット"}'
# 返り値の "id" を控える → 例: UNIT_ID
==④ アカウントBでログイン & キャラB作成==
curl -X POST http://localhost/api/user/login \
-H "Content-Type: application/json" \
-d '{"id":"userB","pass":"passB"}' \
-b cookieB.txt -c cookieB.txt
curl -X POST http://localhost/api/char/create \
-H "Content-Type: application/json" \
-b cookieB.txt -c cookieB.txt \
-d '{"name":"キャラB","sheetUrl":"https://sheet/b","description":"Bのキャラ","tags":"test"}'
# 返り値 "id" → CHAR_B_ID
==⑤ アカウントAでユニットに自キャラA即時追加==
(招待を経由しないパターン確認)
curl -X POST http://localhost/api/member/invite \
-H "Content-Type: application/json" \
-b cookieA.txt -c cookieA.txt \
-d '{"unitOwnerGid":"9139e4d21e497f51",
"unitId":"1890fa5d",
"charOwnerGid":"9139e4d21e497f51",
"charId":"b944034b"}'
==⑥ アカウントA → アカウントB キャラBを招待==
curl -X POST http://localhost/api/member/invite \
-H "Content-Type: application/json" \
-b cookieA.txt -c cookieA.txt \
-d '{"unitOwnerGid":"9139e4d21e497f51",
"unitId":"1890fa5d",
"charOwnerGid":"2f11472dba7fc9a7",
"charId":"f2de295c"}'
==⑦ アカウントBで招待承認==
curl -X GET http://localhost/api/member/invites \
-b cookieB.txt
curl -X POST http://localhost/api/member/accept \
-H "Content-Type: application/json" \
-b cookieB.txt -c cookieB.txt \
-d '{"unitOwnerGid":"9139e4d21e497f51",
"unitId":"1890fa5d",
"charOwnerGid":"2f11472dba7fc9a7",
"charId":"f2de295c"}'
==⑧ 最終確認:ユニット詳細 & キャラ所属確認==
===ユニットAには2キャラ入ってAL計算されている?===
curl -X GET "http://localhost/api/unit/detail?gid=9139e4d21e497f51&unitId=1890fa5d" -b cookieB.txt -c cookieB.txt
===キャラBに unit_memberships が反映されている?===
curl -X GET "http://localhost/api/character/detail?gid=2f11472dba7fc9a7&charId=f2de295c" -b cookieB.txt -c cookieB.txt
==今後ためす==
curl -X POST http://localhost/api/unit/delete \
-H "Content-Type: application/json" \
-b cookie.txt \
-d '{
"id": "ユニットID"
}'
curl -X POST http://localhost/api/user/delete -b cookie.txt -c cookie.txt