1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
|
#include "KingSystem/Resource/Actor/resResourceDrop.h"
#include <random/seadGlobalRandom.h>
namespace ksys::res {
bool Drop::parse_(u8* data, size_t, sead::Heap* heap) {
mTableNum.init(0, "TableNum", "テーブルの数", &mObj);
addObj(&mObj, "Header");
if (!data)
return true;
applyResParameterArchive(agl::utl::ResParameterArchive{data});
const s32 num_tables = mTableNum.ref();
if (num_tables < 1)
return true;
mTables.allocBufferAssert(num_tables, heap);
const agl::utl::ResParameterArchive archive{data};
const auto root = archive.getRootList();
const auto header_obj = root.getResParameterObj(0);
for (s32 i = 0; i < num_tables; ++i) {
sead::FormatFixedSafeString<64> name;
name.format("Table%02d", i + 1);
mTables[i].name.init("", name, "テーブル名", &mObj);
}
mObj.applyResParameterObj(header_obj);
for (s32 i = 0; i < num_tables; ++i) {
const auto obj = root.getResParameterObj(i + 1);
mTables[i].repeat_num_min.init(0, "RepeatNumMin", "抽選回数最小", &mTables[i].obj);
mTables[i].repeat_num_max.init(0, "RepeatNumMax", "抽選回数最大", &mTables[i].obj);
mTables[i].approach_type.init(0, "ApproachType", "姿勢", &mTables[i].obj);
mTables[i].occurrence_speed_type.init(0, "OccurrenceSpeedType", "発生速度",
&mTables[i].obj);
mTables[i].column_num.init(0, "ColumnNum", "行数", &mTables[i].obj);
addObj(&mTables[i].obj, mTables[i].name.ref());
applyResParameterArchive(agl::utl::ResParameterArchive{data});
mTables[i].obj.applyResParameterObj(obj);
if (mTables[i].column_num.ref() > 0) {
mTables[i].items.allocBufferAssert(mTables[i].column_num.ref(), heap);
}
}
for (s32 i = 0; i < num_tables; ++i) {
parseItems_(i);
}
applyResParameterArchive(agl::utl::ResParameterArchive{data});
return true;
}
void Drop::parseItems_(s32& table_idx) {
const s32 num = mTables[table_idx].column_num.ref();
for (s32 i = 0; i < num; ++i) {
sead::FormatFixedSafeString<64> name;
name.format("ItemName%02d", i + 1);
mTables[table_idx].items[i].name.init("", name, "アイテム名", &mTables[table_idx].obj);
sead::FormatFixedSafeString<64> name2;
name2.format("ItemProbability%02d", i + 1);
mTables[table_idx].items[i].probability.init(0.0, name2, "確率", &mTables[table_idx].obj);
}
}
s32 Drop::findTableIndex(const sead::SafeString& table_name) const {
if (!mTables.isBufferReady())
return -1;
const s32 num = mTableNum.ref();
for (s32 i = 0; i < num; ++i) {
if (mTables[i].name.ref() == table_name)
return i;
}
return -1;
}
s32 Drop::findTableIndexOrNormal(const sead::SafeString& table_name) const {
if (!mTables.isBufferReady())
return -1;
s32 normal_idx = -1;
const s32 num = mTableNum.ref();
for (s32 i = 0; i < num; ++i) {
if (mTables[i].name.ref() == table_name)
return i;
if (normal_idx < 0 && mTables[i].name.ref() == "Normal")
normal_idx = i;
}
return normal_idx;
}
const sead::SafeString& Drop::getRandomDropFromTable(const sead::SafeString& table_name) const {
return getRandomDropFromTable(findTableIndexOrNormal(table_name));
}
const sead::SafeString& Drop::getRandomDropFromTable(s32 table_idx) const {
if (!mTables.isBufferReady())
return sead::SafeString::cEmptyString;
/// @bug The index check should be done first...
if (!mTables[table_idx].items.isBufferReady() || table_idx < 0)
return sead::SafeString::cEmptyString;
f32 x = sead::GlobalRandom::instance()->getF32() * 100.0;
const Table& table = mTables[table_idx];
const s32 num_items = table.column_num.ref();
for (s32 i = 0; i < num_items; ++i) {
const Item& item = table.items[i];
const f32 probability = item.probability.ref();
if (x < probability)
return item.name.ref();
x -= probability;
}
return sead::SafeString::cEmptyString;
}
s32 Drop::getApproachType(s32 table_idx) const {
if (!mTables.isBufferReady())
return 0;
/// @bug This bounds checking is bugged: the order is absurd and the second check is off-by-one.
if (!mTables[table_idx].items.isBufferReady() || table_idx < 0 || table_idx > mTables.size())
return 0;
return mTables[table_idx].approach_type.ref();
}
s32 Drop::getOccurrenceSpeedType(s32 table_idx) const {
if (!mTables.isBufferReady())
return 0;
/// @bug This bounds checking is bugged: the order is absurd and the second check is off-by-one.
if (!mTables[table_idx].items.isBufferReady() || table_idx < 0 || table_idx > mTables.size())
return 0;
return mTables[table_idx].occurrence_speed_type.ref();
}
s32 Drop::getRepeatNum(s32 table_idx) const {
if (!mTables.isBufferReady())
return 0;
const Table& table = mTables[table_idx];
if (!mTables[table_idx].items.isBufferReady() || table_idx < 0 || table_idx > mTables.size())
return 0;
const s32 num_min = table.repeat_num_min.ref();
const s32 num_max = table.repeat_num_max.ref();
if (num_min == num_max)
return num_min;
return num_min + sead::GlobalRandom::instance()->getU32(1 - num_min + num_max);
}
s32 Drop::getRepeatNum(const sead::SafeString& table_name) const {
return getRepeatNum(findTableIndexOrNormal(table_name));
}
} // namespace ksys::res
|