ItemRT(Item Rare Table)はPSOのレアドロップ表です。敵や箱からレアアイテムが出る際の種類と確率を定義します。「ドラゴンスレイヤー1/456」「総督恩賜パラソル1/3660」などの数値はItemRTに由来します。ItemPT(ドロップの有無と大分類)、ItemPMT(アイテム自体のパラメータ)と合わせて、PSOのドロップシステムを構成します。
PSOではレアドロップ判定が通常ドロップ判定から独立しています。敵の撃破や箱の破壊時には、まずレア判定を行います。
同じ敵でも、通常アイテムとレアアイテムの選択は別経路です。通常アイテムの種類が多いことで、レアドロップ率がさらに下がるわけではありません。
ItemRTは難易度×セクションIDの組み合わせごとにサブテーブルを持ち、合計4 × 10 = 40個です(EP4は別のテーブルを持ちます)。各サブテーブルは次の2部分に分かれます。
PSOBBでは2つのファイルを使用します:ItemRT.gsl(EP1・EP2)とItemRT_Ep4.gsl。GSLはSega独自の小規模なアーカイブ形式(GStreamLib)で、各エントリにサブテーブルを格納します。
(アイテムID, 確率)。PSOBBのItemRTでは、レアアイテムの確率を分数のまま格納しません。代わりにuint8(1バイト)に圧縮し、小さなファイルに数百件のレア情報を格納します。このバイトはSSSSS VVVとして解釈します。
(2 << shift) × (value + 7)で、32ビットの確率値になります。0xFFFFFFFF(4,294,967,295)。例:
0x40000000:最終ドロップ率=25%(0x40000000 / 0xFFFFFFFF ≈ 1/4)0x00FFFFFF:最終ドロップ率≒1/256圧縮形式には256通りの値しかなく、任意の確率を正確には表せません。1/456、1/3660、1/11702などの端数を含む数値は、この形式で表現できる値のうち最も近いものです。
PSOBBのキャラクターは10色のセクションIDのいずれかを持ちます。本ページでは作成時の名前のハッシュで決まる固定のIDとして説明しています。IDは次の役割を持ちます。
特定のレアを狙う際にIDを選ぶのはこのためです。セクションID計算機で目的のIDになる名前を探せます。
(アイテムID, 圧縮確率)です。ItemRTの元の確率はSegaが設定した基準値です。サーバーでは通常、全体のドロップ率倍率を掛けます。
1x(基準値)です。周年イベントのマイルストーンで倍率が上がる場合があります。ItemRTのバイナリ形式は、圧縮確率とアイテムコードの固定長レコードを最小単位とします。難易度×IDの各サブテーブルは次のオフセットヘッダーで始まります。
Offsets(0x10バイト)| オフセット | フィールド | 型 | 説明 |
|---|---|---|---|
| 0x00 | monster_rares_offset | u32 | エネミーのレア候補へのポインター→PackedDrop[0x65](v1は0x33件)。rt_indexで索引します。 |
| 0x04 | box_count | u32 | 箱のレアエントリ総数。通常は30(0x1E)固定です。 |
| 0x08 | box_areas_offset | u32 | 箱のエリア配列→u8[box_count]。要素iはレアエントリiのエリアを示します。 |
| 0x0C | box_rares_offset | u32 | 箱のレア候補へのポインター→PackedDrop[box_count]。box_areasと1対1で対応します。 |
PackedDrop(4バイト)レアレコードは4バイトで、ItemRTの最小単位です。敵と箱で同じ構造を使用します。
| オフセット | フィールド | 型 | 説明 |
|---|---|---|---|
| 0x00 | probability | u8 | 圧縮確率(SSSSS VVVのビット配置)。 |
| 0x01 | item_code | u8×3 | アイテムコードdata1[0..2]。例えば、00 01 00はSaberを表します。 |
1バイトのprobabilityはSSSSS VVVとして解釈します。
Sでシフト量を決定:shift = S − 4、範囲は[−4, 27]。負値は0として扱います。Vで基準値を決定:value = V + 7、範囲は[7, 14]です。(2 << shift) × value。これをレアドロップ率の分子とし、分母は0xFFFFFFFF(全32ビット)です。0xFFFFFFFF例:
| packed(16進数) | shift | value | expanded | 近似値 |
|---|---|---|---|---|
| 0xFF | 27 | 14 | 0xE0000000 | 87.5% |
| 0xC0 | 20 | 7 | 0x01C00000 | 約1/152 |
| 0x80 | 12 | 7 | 0x0001C000 | 約1/38836 |
| 0x00 | 0 | 7 | 0x0000000E | 極めて小さい(3億分の1未満) |
256通りの値しかないため、任意の確率を正確には格納できません。1/456、1/3660、1/11702などは表現可能な値のうち最も近いものです。例えば正確に1/500という確率は指定できません。
ExpandedDropnewservは各PackedDropを計算・操作・シリアライズ用のメモリ上の構造体に展開します。
| フィールド | 型 | 説明 |
|---|---|---|
| probability | u32 | 展開後の32ビット確率(分子。分母は0xFFFFFFFF固定) |
| data | ItemData | 3バイトのIDだけでなくdata1/data2を含む完全なアイテムデータ |
BoxRare箱のレアレコードは、エリアで直接索引する代わりにエリア番号をレコード内に持ちます。
| フィールド | 型 | 説明 |
|---|---|---|
| area_norm_plus_1 | u8 | エリア番号+1(0は未使用) |
| drop | ExpandedDrop | 確率+アイテムデータ |
SpecCollection最上位の各サブテーブルは難易度×セクションIDのキーに対応し、展開後は次を含みます。
| フィールド | 型 | 説明 |
|---|---|---|
| enemy_specs | map⟨Enemy, ExpandedDrop[]⟩ | 各敵種別のレア候補。 |
| box_specs | ExpandedDrop[][] | 外側の配列はarea_normで索引され、各エリアに個別判定される候補一覧を持ちます。 |
敵の撃破または箱の破壊でドロップ処理が始まります。両者は別経路で、まずレア判定を行い、失敗するとItemPTの通常処理へ進みます。
// ① 前提判定:敵がアイテムを落とすか
type_drop_prob = pt.enemy_type_drop_probs[enemy_type] // 0–100
if rand(0..99) >= type_drop_prob:
return nothing // ドロップなし
// ② レア判定(stacking:1回の乱数で全specを判定)
specs = rare_item_set.get_enemy_specs(mode, episode, difficulty, section_id, enemy_type)
det = rand(0, 0x100000000) // 32ビット+1
for spec in specs:
det -= spec.probability // 順に減算
if det < 0:
return create_rare_item(spec.data, area) // レア当選、処理終了
// ③ ItemPTの通常処理:大分類を選択
determinant = allow_meseta ? rand(0..2) : rand(0..1) + 1
switch determinant:
case 0: item_class = 5 (メセタ)
case 1: item_class = 4 (消耗品)
case 2: item_class = pt.enemy_type_item_classes[enemy_type] // 0..3:武器・鎧・盾・ユニット
// ④ 大分類に応じて生成(重み・付加・属性・エクストラアタックなど)
generate_common_item_variances(item, area)// ① レア判定(敵と同じstacking方式)
table_index = table_index_for_area(area)
specs = rare_item_set.get_box_specs(mode, episode, difficulty, section_id, table_index)
det = rand(0, 0x100000000)
for spec in specs:
det -= spec.probability
if det < 0:
return create_rare_item(spec.data, area)
// ② レアなし→box_item_class_prob_tableの重みで大分類を選択
item_class = weighted_pick(pt.box_item_class_prob_table, table_index)
// 0=武器 1=鎧 2=盾 3=ユニット 4=消耗品 5=メセタ 6=空
// ③ item_class < 6ならアイテムを生成
if item_class < 6:
generate_common_item_variances(item, area)stackingに関する注意:
stackingは初期値detから各specの確率を順に引き、負になった時点で当選とします。これは1回の乱数ですべてのspecから結果を決める方式であり、specの順番は最終的な分布に影響しません。
一方、Segaの元のクライアントでは、各specに対して独立に乱数rand(0, 0xFFFFFFFF)を生成し、spec.probabilityと比較します。前の候補が当選すると処理が終了するため、後ろの候補ほど実際の出現率が低くなります。候補が1つの敵では差はありませんが、多数の候補を持つ箱ではstackingと結果が異なります。newservの作者もコメントでこの点を説明しています。
検索画面を読み込めませんでした。ページを再読み込みしてください。