88#include < charconv>
99#include < optional>
1010#include < string_view>
11+ #include < unordered_map>
12+ #include < vector>
1113
1214#include < expected.hpp>
1315#include < magic_enum/magic_enum.hpp>
@@ -132,7 +134,6 @@ void LoadQuestDialogFromFile()
132134{
133135 const std::string_view filename = " txtdata\\ towners\\ quest_dialog.tsv" ;
134136 DataFile dataFile = DataFile::loadOrDie (filename);
135- dataFile.skipHeaderOrDie (filename);
136137
137138 // Initialize table with TEXT_NONE
138139 TownerQuestDialogTable.clear ();
@@ -141,26 +142,74 @@ void LoadQuestDialogFromFile()
141142 row.fill (TEXT_NONE);
142143 }
143144
145+ // Parse header to find which quest columns exist
146+ DataFileRecord headerRecord = *dataFile.begin ();
147+ std::unordered_map<std::string, unsigned > columnMap;
148+ unsigned columnIndex = 0 ;
149+ for (DataFileField field : headerRecord) {
150+ columnMap[std::string (field.value ())] = columnIndex++;
151+ }
152+
153+ // Reset header position and skip for data reading
154+ dataFile.resetHeader ();
155+ dataFile.skipHeaderOrDie (filename);
156+
157+ // Find the towner_type column index
158+ auto townerTypeColIt = columnMap.find (" towner_type" );
159+ if (townerTypeColIt == columnMap.end ()) {
160+ return ; // Invalid file format
161+ }
162+ unsigned townerTypeColIndex = townerTypeColIt->second ;
163+
164+ // Build quest column index map
165+ std::unordered_map<quest_id, unsigned > questColumnMap;
166+ for (quest_id quest : magic_enum::enum_values<quest_id>()) {
167+ if (quest == Q_INVALID || quest >= MAXQUESTS) continue ;
168+
169+ auto questName = magic_enum::enum_name (quest);
170+ auto questColIt = columnMap.find (std::string (questName));
171+ if (questColIt != columnMap.end ()) {
172+ questColumnMap[quest] = questColIt->second ;
173+ }
174+ }
175+
176+ // Read data rows
144177 for (DataFileRecord record : dataFile) {
145- RecordReader reader { record, filename };
178+ // Read all fields into a map keyed by column index for indexed access
179+ std::unordered_map<unsigned , std::string_view> fields;
180+ for (DataFileField field : record) {
181+ fields[field.column ()] = field.value ();
182+ }
146183
147- _talker_id townerType;
148- reader.read (" towner_type" , townerType, ParseEnum<_talker_id>);
184+ // Read towner_type
185+ auto townerTypeFieldIt = fields.find (townerTypeColIndex);
186+ if (townerTypeFieldIt == fields.end ()) {
187+ continue ; // Invalid row
188+ }
189+
190+ auto townerTypeResult = ParseEnum<_talker_id>(townerTypeFieldIt->second );
191+ if (!townerTypeResult.has_value ()) {
192+ continue ; // Invalid towner type
193+ }
194+ _talker_id townerType = townerTypeResult.value ();
149195
150196 if (static_cast <size_t >(townerType) >= TownerQuestDialogTable.size ()) {
151197 continue ;
152198 }
153199
154200 auto &dialogRow = TownerQuestDialogTable[static_cast <size_t >(townerType)];
155201
156- // Read each quest column using magic_enum to iterate quest IDs
157- for (quest_id quest : magic_enum::enum_values<quest_id>()) {
158- if (quest == Q_INVALID || quest >= MAXQUESTS) continue ;
159-
160- auto questName = magic_enum::enum_name (quest);
161- _speech_id speech = TEXT_NONE;
162- reader.read (questName, speech, ParseSpeechId);
163- dialogRow[quest] = speech;
202+ // Read quest columns that exist in this file
203+ for (const auto &[quest, colIndex] : questColumnMap) {
204+ auto fieldIt = fields.find (colIndex);
205+ if (fieldIt == fields.end ()) {
206+ continue ; // Column missing in this row
207+ }
208+
209+ auto speechResult = ParseSpeechId (fieldIt->second );
210+ if (speechResult.has_value ()) {
211+ dialogRow[quest] = speechResult.value ();
212+ }
164213 }
165214 }
166215}
0 commit comments