1use configs::game_config::GameConfig;
32use essences::character_state::CharacterState;
33use event_system::script::random::GameRng;
34use uuid::Uuid;
35
36use crate::behaviors::{BehaviorKind, BehaviorMeta, BehaviorRegistry};
37use crate::event::OverlordEvent;
38use crate::mechanics::content_lookups::ContentLookups;
39use crate::mechanics::loop_tasks;
40
41pub struct AdditionalQuestsCtx<'a> {
45 pub character_state: &'a CharacterState,
47 pub rng: &'a GameRng,
49 pub config: &'a GameConfig,
50 pub lookups: &'a ContentLookups,
51}
52
53pub type AdditionalQuestsFn = fn(&AdditionalQuestsCtx) -> anyhow::Result<Vec<OverlordEvent>>;
55
56fn set_custom_value(key: &str, value: i64) -> OverlordEvent {
57 OverlordEvent::SetCustomValue {
58 key: key.to_string(),
59 value,
60 }
61}
62
63fn finish_and_advance(ctx: &AdditionalQuestsCtx) -> anyhow::Result<Vec<OverlordEvent>> {
69 let mut events = Vec::new();
70 loop_tasks::on_finish_regular_loop_task(&mut events, ctx.character_state);
71 loop_tasks::advance_loop(&mut events, ctx.config, ctx.lookups, ctx.character_state);
72 Ok(events)
73}
74
75fn set_value_then_advance(
77 ctx: &AdditionalQuestsCtx,
78 key: &str,
79 value: i64,
80) -> anyhow::Result<Vec<OverlordEvent>> {
81 let mut events = vec![set_custom_value(key, value)];
82 loop_tasks::advance_loop(&mut events, ctx.config, ctx.lookups, ctx.character_state);
83 Ok(events)
84}
85
86fn set_value_only(
88 _ctx: &AdditionalQuestsCtx,
89 key: &str,
90 value: i64,
91) -> anyhow::Result<Vec<OverlordEvent>> {
92 Ok(vec![set_custom_value(key, value)])
93}
94
95pub fn loop_finish_advance(ctx: &AdditionalQuestsCtx) -> anyhow::Result<Vec<OverlordEvent>> {
103 finish_and_advance(ctx)
104}
105
106pub fn loop_finish_advance_first_crystals(
108 ctx: &AdditionalQuestsCtx,
109) -> anyhow::Result<Vec<OverlordEvent>> {
110 let mut events = finish_and_advance(ctx)?;
111 events.push(set_custom_value(
112 "loop_tasks.first_loop_skill_crystals_received",
113 1,
114 ));
115 Ok(events)
116}
117
118pub fn loop_finish_prepare_advance(
121 ctx: &AdditionalQuestsCtx,
122) -> anyhow::Result<Vec<OverlordEvent>> {
123 let mut events = Vec::new();
124 loop_tasks::on_finish_regular_loop_task(&mut events, ctx.character_state);
125 loop_tasks::prepare_loop(&mut events, ctx.config, ctx.character_state, ctx.rng);
126 loop_tasks::advance_loop(&mut events, ctx.config, ctx.lookups, ctx.character_state);
127 Ok(events)
128}
129
130pub fn loop_prepare_advance(ctx: &AdditionalQuestsCtx) -> anyhow::Result<Vec<OverlordEvent>> {
132 let mut events = Vec::new();
133 loop_tasks::prepare_loop(&mut events, ctx.config, ctx.character_state, ctx.rng);
134 loop_tasks::advance_loop(&mut events, ctx.config, ctx.lookups, ctx.character_state);
135 Ok(events)
136}
137
138pub fn milestone_level<const N: i64>(
142 ctx: &AdditionalQuestsCtx,
143) -> anyhow::Result<Vec<OverlordEvent>> {
144 set_value_then_advance(ctx, "loop_tasks.last_milestone.level", N)
145}
146
147pub fn milestone_stage<const N: i64>(
149 ctx: &AdditionalQuestsCtx,
150) -> anyhow::Result<Vec<OverlordEvent>> {
151 set_value_then_advance(ctx, "loop_tasks.last_milestone.stage", N)
152}
153
154pub fn mimic_next_item<const CODE: i64>(
158 ctx: &AdditionalQuestsCtx,
159) -> anyhow::Result<Vec<OverlordEvent>> {
160 set_value_only(ctx, "next_mimic_item_code", CODE)
161}
162
163pub const REDIRECT_KILL_3_ENEMIES: u128 = 0x019cd267_a50f_7d1b_81a5_83147141fb8c;
168pub const REDIRECT_OPEN_CHEST_6: u128 = 0x019cd268_6b9e_7b89_826c_96b0e1cb4c55;
169pub const REDIRECT_SELL_5_GEAR: u128 = 0x019cd268_a523_7e35_bb43_3cff76868568;
170pub const REDIRECT_REACH_LEVEL_3: u128 = 0x019cd269_8571_7361_936b_08a48ca9cffe;
171
172pub fn redirect_to<const QUEST: u128>(
174 _ctx: &AdditionalQuestsCtx,
175) -> anyhow::Result<Vec<OverlordEvent>> {
176 Ok(vec![OverlordEvent::UpdateActiveLoopTaskId {
177 quest_id: Uuid::from_u128(QUEST),
178 }])
179}
180
181fn set_loop_task_and_redirect(value: i64, next_quest_id: &str) -> Vec<OverlordEvent> {
193 let quest_id = Uuid::parse_str(next_quest_id).expect("valid quest uuid literal");
194 vec![
195 set_custom_value("loop_task", value),
196 OverlordEvent::UpdateActiveLoopTaskId { quest_id },
197 ]
198}
199
200pub fn test_loop_task_1(_ctx: &AdditionalQuestsCtx) -> anyhow::Result<Vec<OverlordEvent>> {
202 Ok(set_loop_task_and_redirect(
203 1,
204 "29540ca2-21f3-4f0e-a478-240adafed4e3",
205 ))
206}
207
208pub fn test_loop_task_2(_ctx: &AdditionalQuestsCtx) -> anyhow::Result<Vec<OverlordEvent>> {
210 Ok(set_loop_task_and_redirect(
211 2,
212 "9d3d2428-af3c-409f-a580-593de0fd06cf",
213 ))
214}
215
216pub fn test_loop_task_0(_ctx: &AdditionalQuestsCtx) -> anyhow::Result<Vec<OverlordEvent>> {
218 Ok(set_loop_task_and_redirect(
219 0,
220 "305300e2-f432-4cbf-ad46-85386a9a9410",
221 ))
222}
223
224pub fn test_loop_task_seed(_ctx: &AdditionalQuestsCtx) -> anyhow::Result<Vec<OverlordEvent>> {
231 Ok(vec![
232 set_custom_value("loop_task", 0),
233 OverlordEvent::NewQuests {
234 quest_ids: vec![
235 Uuid::parse_str("305300e2-f432-4cbf-ad46-85386a9a9410").unwrap(),
236 Uuid::parse_str("29540ca2-21f3-4f0e-a478-240adafed4e3").unwrap(),
237 Uuid::parse_str("9d3d2428-af3c-409f-a580-593de0fd06cf").unwrap(),
238 ],
239 },
240 OverlordEvent::UpdateActiveLoopTaskId {
241 quest_id: Uuid::parse_str("305300e2-f432-4cbf-ad46-85386a9a9410").unwrap(),
242 },
243 ])
244}
245
246pub fn register(registry: &mut BehaviorRegistry) {
248 let mut reg = |name: &str, title: &str, desc: &str, f: AdditionalQuestsFn| {
249 registry.register_additional_quests(
250 BehaviorMeta {
251 name: name.to_string(),
252 category: BehaviorKind::AdditionalQuests,
253 title: title.to_string(),
254 description: desc.to_string(),
255 },
256 f,
257 );
258 };
259
260 reg(
261 "loop_finish_advance",
262 "Доп. квесты: завершить и продвинуть loop",
263 "on_finish_regular_loop_task (no-op) + advance_loop; общий для регулярных loop-квестов.",
264 loop_finish_advance,
265 );
266 reg(
267 "loop_finish_advance_first_crystals",
268 "Доп. квесты: завершить/продвинуть + флаг первых кристаллов (019a6ef3)",
269 "finish + advance, затем SetCustomValue(loop_tasks.first_loop_skill_crystals_received, 1).",
270 loop_finish_advance_first_crystals,
271 );
272 reg(
273 "loop_finish_prepare_advance",
274 "Доп. квесты: завершить + prepare(RNG) + продвинуть (019bdb53)",
275 "on_finish_regular_loop_task + prepare_loop (RNG) + advance_loop.",
276 loop_finish_prepare_advance,
277 );
278 reg(
279 "loop_prepare_advance",
280 "Доп. квесты: prepare(RNG) + продвинуть (019cd269)",
281 "prepare_loop (RNG) + advance_loop.",
282 loop_prepare_advance,
283 );
284
285 let milestones: &[(&str, &str, AdditionalQuestsFn)] = &[
287 ("aq_milestone_level_1", "level=1", milestone_level::<1>),
288 ("aq_milestone_level_2", "level=2", milestone_level::<2>),
289 ("aq_milestone_level_3", "level=3", milestone_level::<3>),
290 ("aq_milestone_level_4", "level=4", milestone_level::<4>),
291 ("aq_milestone_level_5", "level=5", milestone_level::<5>),
292 ("aq_milestone_level_6", "level=6", milestone_level::<6>),
293 ("aq_milestone_level_7", "level=7", milestone_level::<7>),
294 ("aq_milestone_level_8", "level=8", milestone_level::<8>),
295 ("aq_milestone_level_9", "level=9", milestone_level::<9>),
296 ("aq_milestone_level_10", "level=10", milestone_level::<10>),
297 ("aq_milestone_level_11", "level=11", milestone_level::<11>),
298 ("aq_milestone_level_12", "level=12", milestone_level::<12>),
299 ("aq_milestone_level_13", "level=13", milestone_level::<13>),
300 ("aq_milestone_level_14", "level=14", milestone_level::<14>),
301 ("aq_milestone_stage_1", "stage=1", milestone_stage::<1>),
302 ("aq_milestone_stage_2", "stage=2", milestone_stage::<2>),
303 ("aq_milestone_stage_3", "stage=3", milestone_stage::<3>),
304 ("aq_milestone_stage_4", "stage=4", milestone_stage::<4>),
305 ("aq_milestone_stage_5", "stage=5", milestone_stage::<5>),
306 ("aq_milestone_stage_6", "stage=6", milestone_stage::<6>),
307 ("aq_milestone_stage_7", "stage=7", milestone_stage::<7>),
308 ("aq_milestone_stage_8", "stage=8", milestone_stage::<8>),
309 ("aq_milestone_stage_9", "stage=9", milestone_stage::<9>),
310 ("aq_milestone_stage_10", "stage=10", milestone_stage::<10>),
311 ("aq_milestone_stage_11", "stage=11", milestone_stage::<11>),
312 ("aq_milestone_stage_12", "stage=12", milestone_stage::<12>),
313 ("aq_milestone_stage_13", "stage=13", milestone_stage::<13>),
314 ];
315 for (name, label, f) in milestones {
316 reg(
317 name,
318 &format!("Доп. квесты: веха {label} + advance"),
319 "SetCustomValue(loop_tasks.last_milestone.<k>, n) + advance_loop.",
320 *f,
321 );
322 }
323
324 let mimic_codes: &[(&str, AdditionalQuestsFn)] = &[
326 ("aq_mimic_next_item_0", mimic_next_item::<0>),
327 ("aq_mimic_next_item_1002", mimic_next_item::<1002>),
328 ("aq_mimic_next_item_1011", mimic_next_item::<1011>),
329 ("aq_mimic_next_item_2010", mimic_next_item::<2010>),
330 ("aq_mimic_next_item_3002", mimic_next_item::<3002>),
331 ("aq_mimic_next_item_4001", mimic_next_item::<4001>),
332 ];
333 for (name, f) in mimic_codes {
334 let code = name.rsplit('_').next().unwrap();
335 reg(
336 name,
337 &format!("Доп. квесты: next_mimic_item_code={code}"),
338 "SetCustomValue(next_mimic_item_code, <код>).",
339 *f,
340 );
341 }
342
343 let redirects: &[(&str, &str, AdditionalQuestsFn)] = &[
345 (
346 "aq_redirect_to_kill_3_enemies",
347 "019cd267",
348 redirect_to::<REDIRECT_KILL_3_ENEMIES>,
349 ),
350 (
351 "aq_redirect_to_open_chest_6",
352 "019cd268-6b9e",
353 redirect_to::<REDIRECT_OPEN_CHEST_6>,
354 ),
355 (
356 "aq_redirect_to_sell_5_gear",
357 "019cd268-a523",
358 redirect_to::<REDIRECT_SELL_5_GEAR>,
359 ),
360 (
361 "aq_redirect_to_reach_level_3",
362 "019cd269",
363 redirect_to::<REDIRECT_REACH_LEVEL_3>,
364 ),
365 ];
366 for (name, target, f) in redirects {
367 reg(
368 name,
369 &format!("Доп. квесты: переключить loop task → {target}"),
370 "UpdateActiveLoopTaskId(<целевой loop-task квест>).",
371 *f,
372 );
373 }
374
375 reg(
377 "test_loop_task_1",
378 "Тест: loop_task=1 → 29540ca2",
379 "SetCustomValue(loop_task, 1) + UpdateActiveLoopTaskId(29540ca2).",
380 test_loop_task_1,
381 );
382 reg(
383 "test_loop_task_2",
384 "Тест: loop_task=2 → 9d3d2428",
385 "SetCustomValue(loop_task, 2) + UpdateActiveLoopTaskId(9d3d2428).",
386 test_loop_task_2,
387 );
388 reg(
389 "test_loop_task_0",
390 "Тест: loop_task=0 → 305300e2",
391 "SetCustomValue(loop_task, 0) + UpdateActiveLoopTaskId(305300e2).",
392 test_loop_task_0,
393 );
394 reg(
395 "test_loop_task_seed",
396 "Тест: выдать loop-task цикл (305300e2/29540ca2/9d3d2428)",
397 "SetCustomValue(loop_task, 0) + NewQuests([3 loop tasks]) + UpdateActiveLoopTaskId(305300e2).",
398 test_loop_task_seed,
399 );
400
401 register_default_loop_tasks(registry);
402}
403
404pub struct DefaultLoopTaskCtx;
407
408pub type DefaultLoopTaskFn = fn(&DefaultLoopTaskCtx) -> anyhow::Result<Uuid>;
410
411pub fn default_loop_task_const(_ctx: &DefaultLoopTaskCtx) -> anyhow::Result<Uuid> {
413 Ok(Uuid::from_u128(0x019cd267_a50f_7d1b_81a5_83147141fb8c))
414}
415
416pub fn test_default_loop_task(_ctx: &DefaultLoopTaskCtx) -> anyhow::Result<Uuid> {
418 Ok(Uuid::from_u128(0x305300e2_f432_4cbf_ad46_85386a9a9410))
419}
420
421fn register_default_loop_tasks(registry: &mut BehaviorRegistry) {
424 registry.register_default_loop_task(
425 BehaviorMeta {
426 name: "default_loop_task_const".to_string(),
427 category: BehaviorKind::DefaultLoopTask,
428 title: "Дефолтное loop-задание (константный uuid)".to_string(),
429 description: "Возвращает константный uuid loop-задания.".to_string(),
430 },
431 default_loop_task_const,
432 );
433 registry.register_default_loop_task(
434 BehaviorMeta {
435 name: "test_default_loop_task".to_string(),
436 category: BehaviorKind::DefaultLoopTask,
437 title: "Тест: дефолтное loop-задание (305300e2)".to_string(),
438 description: "Возвращает 305300e2 (фикстурный путь восстановления loop-task)."
439 .to_string(),
440 },
441 test_default_loop_task,
442 );
443}