Skip to main content

rpgmv_tool/command/commands2py/
generate.rs

1mod function_call_writer;
2
3use self::function_call_writer::FunctionCallWriter;
4use self::function_call_writer::Ident;
5use super::Command;
6use super::ConditionalBranchCommand;
7use super::Config;
8use super::ControlVariablesValue;
9use super::ControlVariablesValueGameData;
10use super::GetLocationInfoKind;
11use super::MaybeRef;
12use anyhow::ensure;
13use std::io::Write;
14
15pub fn commands2py<W>(
16    config: &Config,
17    commands: &[(u16, Command)],
18    mut writer: W,
19) -> anyhow::Result<()>
20where
21    W: Write,
22{
23    for (indent, command) in commands.iter() {
24        command2py(config, *indent, command, &mut writer)?;
25    }
26
27    Ok(())
28}
29
30fn command2py<W>(
31    config: &Config,
32    indent: u16,
33    command: &Command,
34    mut writer: W,
35) -> anyhow::Result<()>
36where
37    W: Write,
38{
39    match command {
40        Command::Nop => {}
41        Command::ShowText {
42            face_name,
43            face_index,
44            background,
45            position_type,
46            lines,
47        } => {
48            let mut writer = FunctionCallWriter::new(&mut writer, indent, "show_text")?;
49            writer.write_param("face_name", face_name)?;
50            writer.write_param("face_index", face_index)?;
51            writer.write_param("background", background)?;
52            writer.write_param("position_type", position_type)?;
53            writer.write_param("lines", lines)?;
54            writer.finish()?;
55        }
56        Command::ShowChoices {
57            choices,
58            cancel_type,
59            default_type,
60            position_type,
61            background,
62        } => {
63            let mut writer = FunctionCallWriter::new(&mut writer, indent, "show_choices")?;
64            writer.write_param("choices", choices)?;
65            writer.write_param("cancel_type", cancel_type)?;
66            writer.write_param("default_type", default_type)?;
67            writer.write_param("position_type", position_type)?;
68            writer.write_param("background", background)?;
69            writer.finish()?;
70        }
71        Command::ShowScrollingText {
72            speed,
73            no_fast,
74            lines,
75        } => {
76            let no_fast = stringify_bool(*no_fast);
77
78            write_indent(&mut writer, indent)?;
79            writeln!(&mut writer, "show_scrolling_text(")?;
80
81            write_indent(&mut writer, indent + 1)?;
82            writeln!(&mut writer, "speed={speed},")?;
83
84            write_indent(&mut writer, indent + 1)?;
85            writeln!(&mut writer, "no_fast={no_fast},")?;
86
87            write_indent(&mut writer, indent + 1)?;
88            writeln!(&mut writer, "lines=[")?;
89
90            for line in lines {
91                let line = escape_string(line);
92
93                write_indent(&mut writer, indent + 2)?;
94                writeln!(&mut writer, "'{line}',")?;
95            }
96
97            write_indent(&mut writer, indent + 1)?;
98            writeln!(&mut writer, "],")?;
99
100            write_indent(&mut writer, indent)?;
101            writeln!(&mut writer, ")")?;
102        }
103        Command::Comment { lines } => {
104            for line in lines.iter() {
105                write_indent(&mut writer, indent)?;
106                writeln!(&mut writer, "# {line}")?;
107            }
108        }
109        Command::ConditionalBranch(command) => {
110            write_indent(&mut writer, indent)?;
111            write!(&mut writer, "if ")?;
112            match command {
113                ConditionalBranchCommand::Switch { id, check_true } => {
114                    let name = config.get_switch_name(*id);
115                    let check_true_str = if *check_true { "" } else { "not " };
116                    writeln!(&mut writer, "{check_true_str}{name}:")?;
117                }
118                ConditionalBranchCommand::Variable {
119                    lhs_id,
120                    rhs_id,
121                    operation,
122                } => {
123                    let lhs = config.get_variable_name(*lhs_id);
124                    let rhs = match rhs_id {
125                        MaybeRef::Constant(value) => value.to_string(),
126                        MaybeRef::Ref(id) => config.get_variable_name(*id),
127                    };
128                    let operation = operation.as_str();
129
130                    writeln!(&mut writer, "{lhs} {operation} {rhs}:")?;
131                }
132                ConditionalBranchCommand::SelfSwitch { name, check_true } => {
133                    let name = escape_string(name);
134                    let check_true_str = if *check_true { "" } else { "not " };
135                    writeln!(
136                        &mut writer,
137                        "{check_true_str}game_self_switches.get(map_id=self.map_id, event_id=self.event_id, name='{name}'):"
138                    )?;
139                }
140                ConditionalBranchCommand::ActorInParty { actor_id } => {
141                    let actor_name = config.get_actor_name(*actor_id);
142
143                    writeln!(
144                        &mut writer,
145                        "game_party.members.contains(actor={actor_name}):"
146                    )?;
147                }
148                ConditionalBranchCommand::ActorName { actor_id, name } => {
149                    let actor_name = config.get_actor_name(*actor_id);
150                    let name = escape_string(name);
151
152                    writeln!(&mut writer, "{actor_name}.name() == '{name}':")?;
153                }
154                ConditionalBranchCommand::Timer { value, is_gte } => {
155                    let cmp = if *is_gte { ">=" } else { "<=" };
156
157                    writeln!(&mut writer, "game_timer.seconds() {cmp} {value}:")?;
158                }
159                ConditionalBranchCommand::ActorSkill { actor_id, skill_id } => {
160                    let actor_name = config.get_actor_name(*actor_id);
161                    let skill_name = config.get_skill_name(*skill_id);
162
163                    writeln!(&mut writer, "{actor_name}.has_skill(skill={skill_name}):")?;
164                }
165                ConditionalBranchCommand::ActorArmor { actor_id, armor_id } => {
166                    let actor_name = config.get_actor_name(*actor_id);
167                    let armor_name = config.get_armor_name(*armor_id);
168
169                    writeln!(&mut writer, "{actor_name}.has_armor(armor={armor_name}):")?;
170                }
171                ConditionalBranchCommand::ActorState { actor_id, state_id } => {
172                    let actor_name = config.get_actor_name(*actor_id);
173                    let state_name = config.get_state_name(*state_id);
174
175                    writeln!(&mut writer, "{actor_name}.has_state(state={state_name}):")?;
176                }
177                ConditionalBranchCommand::EnemyState {
178                    enemy_index,
179                    state_id,
180                } => {
181                    let name = config.get_state_name(*state_id);
182
183                    writeln!(
184                        &mut writer,
185                        "game_troop.members[{enemy_index}].is_state_affected(state={name}):"
186                    )?;
187                }
188                ConditionalBranchCommand::Character {
189                    character_id,
190                    direction,
191                } => {
192                    let name = if *character_id < 0 {
193                        "game_player".to_string()
194                    } else {
195                        format!("game_character_{character_id}")
196                    };
197
198                    writeln!(&mut writer, "{name}.direction == {direction}:")?;
199                }
200                ConditionalBranchCommand::Gold { value, check } => {
201                    let check = check.as_str();
202
203                    writeln!(&mut writer, "game_party.gold {check} {value}:")?;
204                }
205                ConditionalBranchCommand::Item { item_id } => {
206                    let name = config.get_item_name(*item_id);
207
208                    writeln!(&mut writer, "game_party.has_item(item={name}):")?;
209                }
210                ConditionalBranchCommand::Button { key_name } => {
211                    let key_name = escape_string(key_name);
212
213                    writeln!(&mut writer, "game_input.is_pressed(key_name='{key_name}'):")?;
214                }
215                ConditionalBranchCommand::Script { value } => {
216                    let value = escape_string(value);
217
218                    writeln!(&mut writer, "execute_script('{value}'):")?;
219                }
220            }
221        }
222        Command::Loop => {
223            write_indent(&mut writer, indent)?;
224            writeln!(&mut writer, "while True:")?;
225        }
226        Command::ExitEventProcessing => {
227            write_indent(&mut writer, indent)?;
228            writeln!(&mut writer, "exit_event_processing()")?;
229        }
230        Command::CommonEvent { id } => {
231            let name = config.get_common_event_name(*id);
232            FunctionCallWriter::new(&mut writer, indent, &name)?.finish()?;
233        }
234        Command::Label { name } => {
235            let mut writer = FunctionCallWriter::new(&mut writer, indent, "set_label")?;
236            writer.set_multiline(false);
237            writer.write_param("name", name)?;
238            writer.finish()?;
239        }
240        Command::JumpToLabel { name } => {
241            let mut writer = FunctionCallWriter::new(&mut writer, indent, "jump_to_label")?;
242            writer.set_multiline(false);
243            writer.write_param("name", name)?;
244            writer.finish()?;
245        }
246        Command::ControlSwitches {
247            start_id,
248            end_id,
249            value,
250        } => {
251            for id in *start_id..(*end_id + 1) {
252                let name = config.get_switch_name(id);
253                let value = stringify_bool(*value);
254
255                write_indent(&mut writer, indent)?;
256                writeln!(&mut writer, "{name} = {value}")?;
257            }
258        }
259        Command::ControlVariables {
260            start_variable_id,
261            end_variable_id,
262            operation,
263            value,
264        } => {
265            let operation = operation.as_str();
266            let value = match value {
267                ControlVariablesValue::Constant { value } => value.to_string(),
268                ControlVariablesValue::Variable { id } => config.get_variable_name(*id),
269                ControlVariablesValue::Random { start, stop } => {
270                    format!("random.randrange(start={start}, stop={stop})")
271                }
272                ControlVariablesValue::GameData(game_data) => match game_data {
273                    ControlVariablesValueGameData::NumItems { item_id } => {
274                        let name = config.get_item_name(*item_id);
275
276                        format!("game_party.get_num_items(item={name})")
277                    }
278                    ControlVariablesValueGameData::ActorLevel { actor_id } => {
279                        let name = config.get_actor_name(*actor_id);
280                        format!("{name}.level")
281                    }
282                    ControlVariablesValueGameData::ActorHp { actor_id } => {
283                        let name = config.get_actor_name(*actor_id);
284                        format!("{name}.hp")
285                    }
286                    ControlVariablesValueGameData::ActorMp { actor_id } => {
287                        let name = config.get_actor_name(*actor_id);
288                        format!("{name}.mp")
289                    }
290                    ControlVariablesValueGameData::ActorParam {
291                        actor_id,
292                        param_index,
293                    } => {
294                        let name = config.get_actor_name(*actor_id);
295                        format!("{name}.param({param_index})")
296                    }
297                    ControlVariablesValueGameData::EnemyParam {
298                        enemy_index,
299                        param_index,
300                    } => {
301                        format!("game_troop.members[{enemy_index}].param({param_index})")
302                    }
303                    ControlVariablesValueGameData::CharacterMapX { character_id } => {
304                        format!("game.get_character(id={character_id}).map_x")
305                    }
306                    ControlVariablesValueGameData::CharacterMapY { character_id } => {
307                        format!("game.get_character(id={character_id}).map_y")
308                    }
309                    ControlVariablesValueGameData::CharacterScreenX { character_id } => {
310                        format!("game.get_character(id={character_id}).screen_x")
311                    }
312                    ControlVariablesValueGameData::CharacterScreenY { character_id } => {
313                        format!("game.get_character(id={character_id}).screen_y")
314                    }
315                    ControlVariablesValueGameData::MapId => "game_map.map_id()".to_string(),
316                    ControlVariablesValueGameData::Gold => "game_party.gold".to_string(),
317                    ControlVariablesValueGameData::Steps => "game_party.steps".to_string(),
318                },
319                ControlVariablesValue::Script { value } => {
320                    let value = escape_string(value);
321                    format!("execute_script('{value}')")
322                }
323            };
324            for variable_id in *start_variable_id..(*end_variable_id + 1) {
325                let name = config.get_variable_name(variable_id);
326
327                write_indent(&mut writer, indent)?;
328                writeln!(&mut writer, "{name} {operation} {value}")?;
329            }
330        }
331        Command::ControlSelfSwitch { key, value } => {
332            let value = stringify_bool(*value);
333
334            write_indent(&mut writer, indent)?;
335            writeln!(&mut writer, "game_self_switches['{key}'] = {value}")?;
336        }
337        Command::ControlTimer { start_seconds } => {
338            write_indent(&mut writer, indent)?;
339            match start_seconds {
340                Some(start_seconds) => {
341                    writeln!(&mut writer, "game_timer.start(seconds={start_seconds})")?
342                }
343                None => writeln!(&mut writer, "game_timer.stop()")?,
344            }
345        }
346        Command::ChangeGold { is_add, value } => {
347            let op = if *is_add { "+=" } else { "-=" };
348            let value = match value {
349                MaybeRef::Constant(value) => value.to_string(),
350                MaybeRef::Ref(id) => config.get_variable_name(*id),
351            };
352
353            write_indent(&mut writer, indent)?;
354            writeln!(&mut writer, "game_party.gold {op} {value}")?;
355        }
356        Command::ChangeItems {
357            item_id,
358            is_add,
359            value,
360        } => {
361            let item = config.get_item_name(*item_id);
362            let sign = if *is_add { "" } else { "-" };
363            let value = match value {
364                MaybeRef::Constant(value) => value.to_string(),
365                MaybeRef::Ref(id) => config.get_variable_name(*id),
366            };
367            let value = format!("{sign}{value}");
368
369            let mut writer = FunctionCallWriter::new(&mut writer, indent, "gain_item")?;
370            writer.set_multiline(false);
371            writer.write_param("item", &Ident(&item))?;
372            writer.write_param("value", &Ident(&value))?;
373            writer.finish()?;
374        }
375        Command::ChangeArmors {
376            armor_id,
377            is_add,
378            value,
379            include_equipped,
380        } => {
381            let armor = config.get_armor_name(*armor_id);
382            let sign = if *is_add { "" } else { "-" };
383            let value = match value {
384                MaybeRef::Constant(value) => value.to_string(),
385                MaybeRef::Ref(id) => config.get_variable_name(*id),
386            };
387            let value = format!("{sign}{value}");
388
389            let mut writer = FunctionCallWriter::new(&mut writer, indent, "gain_armor")?;
390            writer.set_multiline(false);
391            writer.write_param("armor", &Ident(&armor))?;
392            writer.write_param("value", &Ident(&value))?;
393            writer.write_param("include_equipped", include_equipped)?;
394            writer.finish()?;
395        }
396        Command::ChangePartyMember {
397            actor_id,
398            is_add,
399            initialize,
400        } => {
401            let actor_name = config.get_actor_name(*actor_id);
402            let fn_name = if *is_add {
403                "add_party_member"
404            } else {
405                "remove_party_member"
406            };
407
408            let mut writer = FunctionCallWriter::new(&mut writer, indent, fn_name)?;
409            writer.set_multiline(false);
410            writer.write_param("actor", &Ident(&actor_name))?;
411            // The argument is always provided, but ignored by remove ops.
412            if *is_add {
413                writer.write_param("initialize", initialize)?;
414            }
415            writer.finish()?;
416        }
417        Command::ChangeSaveAccess { disable } => {
418            let fn_name = if *disable {
419                "disable_saving"
420            } else {
421                "enable_saving"
422            };
423
424            let mut writer = FunctionCallWriter::new(&mut writer, indent, fn_name)?;
425            writer.finish()?;
426        }
427        Command::SetEventLocation {
428            character_id,
429            x,
430            y,
431            direction,
432        } => {
433            let mut writer = FunctionCallWriter::new(&mut writer, indent, "set_event_location")?;
434            writer.write_param("character_id", character_id)?;
435            match x {
436                MaybeRef::Constant(x) => {
437                    writer.write_param("x", x)?;
438                }
439                MaybeRef::Ref(x) => {
440                    let x = config.get_variable_name(*x);
441                    writer.write_param("x", &Ident(&x))?;
442                }
443            }
444            match y {
445                MaybeRef::Constant(y) => {
446                    writer.write_param("y", y)?;
447                }
448                MaybeRef::Ref(y) => {
449                    let y = config.get_variable_name(*y);
450                    writer.write_param("y", &Ident(&y))?;
451                }
452            }
453            if let Some(direction) = direction {
454                writer.write_param("direction", direction)?;
455            }
456            writer.finish()?;
457        }
458        Command::TransferPlayer {
459            map_id,
460            x,
461            y,
462            direction,
463            fade_type,
464        } => {
465            let mut writer = FunctionCallWriter::new(&mut writer, indent, "transfer_player")?;
466            match map_id {
467                MaybeRef::Constant(id) => {
468                    let name = format!("game_map_{id}");
469                    writer.write_param("map", &Ident(&name))?;
470                }
471                MaybeRef::Ref(id) => {
472                    let name = config.get_variable_name(*id);
473                    writer.write_param("map_id", &Ident(&name))?;
474                }
475            }
476
477            match x {
478                MaybeRef::Constant(value) => {
479                    writer.write_param("x", value)?;
480                }
481                MaybeRef::Ref(id) => {
482                    let name = config.get_variable_name(*id);
483                    writer.write_param("x", &Ident(&name))?;
484                }
485            }
486
487            match y {
488                MaybeRef::Constant(value) => {
489                    writer.write_param("y", value)?;
490                }
491                MaybeRef::Ref(id) => {
492                    let name = config.get_variable_name(*id);
493                    writer.write_param("y", &Ident(&name))?;
494                }
495            }
496
497            writer.write_param("direction", direction)?;
498            writer.write_param("fade_type", fade_type)?;
499
500            writer.finish()?;
501        }
502        Command::SetMovementRoute {
503            character_id,
504            route,
505        } => {
506            let mut writer = FunctionCallWriter::new(&mut writer, indent, "set_movement_route")?;
507            writer.write_param("character_id", character_id)?;
508            writer.write_param("route", route)?;
509            writer.finish()?;
510        }
511        Command::ChangeTransparency { set_transparent } => {
512            let set_transparent = stringify_bool(*set_transparent);
513
514            write_indent(&mut writer, indent)?;
515            writeln!(
516                &mut writer,
517                "change_transparency(set_transparent={set_transparent})"
518            )?
519        }
520        Command::ShowAnimation {
521            character_id,
522            animation_id,
523            wait,
524        } => {
525            let mut writer = FunctionCallWriter::new(&mut writer, indent, "show_animation")?;
526            writer.set_multiline(false);
527            writer.write_param("character_id", character_id)?;
528            writer.write_param("animation_id", animation_id)?;
529            writer.write_param("wait", wait)?;
530            writer.finish()?;
531        }
532        Command::ShowBalloonIcon {
533            character_id,
534            balloon_id,
535            wait,
536        } => {
537            let mut writer = FunctionCallWriter::new(&mut writer, indent, "show_balloon_icon")?;
538            writer.set_multiline(false);
539            writer.write_param("character_id", character_id)?;
540            writer.write_param("balloon_id", balloon_id)?;
541            writer.write_param("wait", wait)?;
542            writer.finish()?;
543        }
544        Command::ChangePlayerFollowers { is_show } => {
545            let fn_name = if *is_show {
546                "show_player_followers"
547            } else {
548                "hide_player_followers"
549            };
550
551            write_indent(&mut writer, indent)?;
552            writeln!(&mut writer, "{fn_name}()")?
553        }
554        Command::FadeoutScreen => {
555            FunctionCallWriter::new(&mut writer, indent, "fadeout_screen")?.finish()?;
556        }
557        Command::FadeinScreen => {
558            FunctionCallWriter::new(&mut writer, indent, "fadein_screen")?.finish()?;
559        }
560        Command::TintScreen {
561            tone,
562            duration,
563            wait,
564        } => {
565            let wait = stringify_bool(*wait);
566
567            write_indent(&mut writer, indent)?;
568            writeln!(
569                &mut writer,
570                "tint_screen(tone={tone:?}, duration={duration}, wait={wait})"
571            )?
572        }
573        Command::FlashScreen {
574            color,
575            duration,
576            wait,
577        } => {
578            let wait = stringify_bool(*wait);
579
580            write_indent(&mut writer, indent)?;
581            writeln!(
582                &mut writer,
583                "flash_screen(color={color:?}, duration={duration}, wait={wait})"
584            )?
585        }
586        Command::ShakeScreen {
587            power,
588            speed,
589            duration,
590            wait,
591        } => {
592            let mut writer = FunctionCallWriter::new(&mut writer, indent, "shake_screen")?;
593            writer.set_multiline(false);
594            writer.write_param("power", power)?;
595            writer.write_param("speed", speed)?;
596            writer.write_param("duration", duration)?;
597            writer.write_param("wait", wait)?;
598            writer.finish()?;
599        }
600        Command::Wait { duration } => {
601            let mut writer = FunctionCallWriter::new(&mut writer, indent, "wait")?;
602            writer.set_multiline(false);
603            writer.write_param("duration", duration)?;
604            writer.finish()?;
605        }
606        Command::ShowPicture {
607            picture_id,
608            picture_name,
609            origin,
610            x,
611            y,
612            scale_x,
613            scale_y,
614            opacity,
615            blend_mode,
616        } => {
617            let picture_name = escape_string(picture_name);
618            let x = match x {
619                MaybeRef::Constant(value) => value.to_string(),
620                MaybeRef::Ref(id) => config.get_variable_name(*id),
621            };
622            let y = match y {
623                MaybeRef::Constant(value) => value.to_string(),
624                MaybeRef::Ref(id) => config.get_variable_name(*id),
625            };
626
627            write_indent(&mut writer, indent)?;
628            writeln!(&mut writer, "show_picture(")?;
629
630            write_indent(&mut writer, indent + 1)?;
631            writeln!(&mut writer, "picture_id={picture_id},")?;
632
633            write_indent(&mut writer, indent + 1)?;
634            writeln!(&mut writer, "picture_name='{picture_name}',")?;
635
636            write_indent(&mut writer, indent + 1)?;
637            writeln!(&mut writer, "origin={origin},")?;
638
639            write_indent(&mut writer, indent + 1)?;
640            writeln!(&mut writer, "x={x},")?;
641
642            write_indent(&mut writer, indent + 1)?;
643            writeln!(&mut writer, "y={y},")?;
644
645            write_indent(&mut writer, indent + 1)?;
646            writeln!(&mut writer, "scale_x={scale_x},")?;
647
648            write_indent(&mut writer, indent + 1)?;
649            writeln!(&mut writer, "scale_y={scale_y},")?;
650
651            write_indent(&mut writer, indent + 1)?;
652            writeln!(&mut writer, "opacity={opacity},")?;
653
654            write_indent(&mut writer, indent + 1)?;
655            writeln!(&mut writer, "blend_mode={blend_mode},")?;
656
657            write_indent(&mut writer, indent)?;
658            writeln!(&mut writer, ")")?;
659        }
660        Command::ErasePicture { picture_id } => {
661            let mut writer = FunctionCallWriter::new(&mut writer, indent, "erase_picture")?;
662            writer.set_multiline(false);
663            writer.write_param("picture_id", picture_id)?;
664            writer.finish()?;
665        }
666        Command::PlayBgm { audio } => {
667            let mut writer = FunctionCallWriter::new(&mut writer, indent, "play_bgm")?;
668            writer.write_param("audio", audio)?;
669            writer.finish()?;
670        }
671        Command::FadeoutBgm { duration } => {
672            let mut writer = FunctionCallWriter::new(&mut writer, indent, "fadeout_bgm")?;
673            writer.set_multiline(false);
674            writer.write_param("duration", duration)?;
675            writer.finish()?;
676        }
677        Command::SaveBgm => {
678            write_indent(&mut writer, indent)?;
679            writeln!(&mut writer, "save_bgm()")?;
680        }
681        Command::ResumeBgm => {
682            write_indent(&mut writer, indent)?;
683            writeln!(&mut writer, "resume_bgm()")?;
684        }
685        Command::PlayBgs { audio } => {
686            write_indent(&mut writer, indent)?;
687            writeln!(&mut writer, "play_bgs(")?;
688
689            write_indent(&mut writer, indent + 1)?;
690            write!(&mut writer, "audio=")?;
691            write_audio_file(&mut writer, indent + 1, audio)?;
692
693            write_indent(&mut writer, indent)?;
694            writeln!(&mut writer, ")")?;
695        }
696        Command::FadeoutBgs { duration } => {
697            write_indent(&mut writer, indent)?;
698            writeln!(&mut writer, "fadeout_bgs(duration={duration})")?;
699        }
700        Command::PlaySe { audio } => {
701            let mut writer = FunctionCallWriter::new(&mut writer, indent, "play_se")?;
702            writer.write_param("audio", audio)?;
703            writer.finish()?;
704        }
705        Command::GetLocationInfo {
706            variable_id,
707            kind,
708            x,
709            y,
710        } => {
711            let variable = config.get_variable_name(*variable_id);
712            let x = match x {
713                MaybeRef::Constant(x) => x.to_string(),
714                MaybeRef::Ref(x) => config.get_variable_name(*x),
715            };
716            let y = match y {
717                MaybeRef::Constant(y) => y.to_string(),
718                MaybeRef::Ref(y) => config.get_variable_name(*y),
719            };
720
721            let value = match kind {
722                GetLocationInfoKind::TerrainTag => {
723                    format!("game_map.get_terrain_tag(x={x}, y={y})")
724                }
725                GetLocationInfoKind::EventId => {
726                    format!("game_map.get_event_id(x={x}, y={y})")
727                }
728            };
729
730            write_indent(&mut writer, indent)?;
731            writeln!(&mut writer, "{variable} = {value}")?;
732        }
733        Command::BattleProcessing {
734            troop_id,
735            can_escape,
736            can_lose,
737        } => {
738            let mut writer = FunctionCallWriter::new(&mut writer, indent, "battle_processing")?;
739            match troop_id {
740                Some(MaybeRef::Constant(id)) => {
741                    let name = config.get_troop_name(*id);
742                    writer.write_param("troop", &Ident(&name))?;
743                }
744                Some(MaybeRef::Ref(id)) => {
745                    let name = config.get_variable_name(*id);
746                    writer.write_param("troop_id", &Ident(&name))?;
747                }
748                None => {
749                    writer.write_param("troop_id", &Ident("game.random_encounter_troop_id()"))?;
750                }
751            }
752            writer.write_param("can_escape", can_escape)?;
753            writer.write_param("can_lose", can_lose)?;
754            writer.finish()?;
755        }
756        Command::NameInputProcessing { actor_id, max_len } => {
757            let actor = config.get_actor_name(*actor_id);
758
759            let mut writer = FunctionCallWriter::new(&mut writer, indent, "name_input_processing")?;
760            writer.set_multiline(false);
761            writer.write_param("actor", &Ident(&actor))?;
762            writer.write_param("max_len", max_len)?;
763            writer.finish()?;
764        }
765        Command::ChangeHp {
766            actor_id,
767            is_add,
768            value,
769            allow_death,
770        } => {
771            let mut writer = FunctionCallWriter::new(&mut writer, indent, "gain_hp")?;
772            writer.set_multiline(false);
773            match actor_id {
774                MaybeRef::Constant(actor_id) => {
775                    let name = config.get_actor_name(*actor_id);
776                    writer.write_param("actor", &Ident(&name))?;
777                }
778                MaybeRef::Ref(variable_id) => {
779                    let name = config.get_variable_name(*variable_id);
780                    writer.write_param("actor_id", &Ident(&name))?;
781                }
782            };
783            let sign = if *is_add { "" } else { "-" };
784            let value = match value {
785                MaybeRef::Constant(value) => value.to_string(),
786                MaybeRef::Ref(id) => config.get_variable_name(*id),
787            };
788            let value = format!("{sign}{value}");
789            writer.write_param("value", &Ident(&value))?;
790            writer.write_param("allow_death", allow_death)?;
791            writer.finish()?;
792        }
793        Command::ChangeMp {
794            actor_id,
795            is_add,
796            value,
797        } => {
798            let mut writer = FunctionCallWriter::new(&mut writer, indent, "gain_mp")?;
799            writer.set_multiline(false);
800            match actor_id {
801                MaybeRef::Constant(actor_id) => {
802                    let name = config.get_actor_name(*actor_id);
803                    writer.write_param("actor", &Ident(&name))?;
804                }
805                MaybeRef::Ref(variable_id) => {
806                    let name = config.get_variable_name(*variable_id);
807                    writer.write_param("actor_id", &Ident(&name))?;
808                }
809            };
810            let sign = if *is_add { "" } else { "-" };
811            let value = match value {
812                MaybeRef::Constant(value) => value.to_string(),
813                MaybeRef::Ref(id) => config.get_variable_name(*id),
814            };
815            let value = format!("{sign}{value}");
816
817            writer.write_param("value", &Ident(&value))?;
818            writer.finish()?;
819        }
820        Command::ChangeState {
821            actor_id,
822            is_add_state,
823            state_id,
824        } => {
825            let actor_arg = match actor_id {
826                MaybeRef::Constant(0) => "actors=game_party".to_string(),
827                MaybeRef::Constant(actor_id) => {
828                    let name = config.get_actor_name(*actor_id);
829                    format!("actor={name}")
830                }
831                MaybeRef::Ref(variable_id) => {
832                    let name = config.get_variable_name(*variable_id);
833                    format!("actor_id={name}")
834                }
835            };
836
837            let fn_name = if *is_add_state {
838                "add_state"
839            } else {
840                "remove_state"
841            };
842            let state = config.get_state_name(*state_id);
843
844            write_indent(&mut writer, indent)?;
845            writeln!(&mut writer, "{fn_name}({actor_arg}, state={state})")?;
846        }
847        Command::ChangeLevel {
848            actor_id,
849            is_add,
850            value,
851            show_level_up,
852        } => {
853            let actor_arg = match actor_id {
854                MaybeRef::Constant(actor_id) => {
855                    let name = config.get_actor_name(*actor_id);
856                    format!("actor={name}")
857                }
858                MaybeRef::Ref(variable_id) => {
859                    let name = config.get_variable_name(*variable_id);
860                    format!("actor_id={name}")
861                }
862            };
863            let sign = if *is_add { "" } else { "-" };
864            let value = match value {
865                MaybeRef::Constant(value) => value.to_string(),
866                MaybeRef::Ref(id) => config.get_variable_name(*id),
867            };
868            let show_level_up = stringify_bool(*show_level_up);
869
870            write_indent(&mut writer, indent)?;
871            writeln!(
872                &mut writer,
873                "gain_level({actor_arg}, value={sign}{value}, show_level_up={show_level_up})"
874            )?;
875        }
876        Command::ChangeSkill {
877            actor_id,
878            is_learn_skill,
879            skill_id,
880        } => {
881            let actor_arg = match actor_id {
882                MaybeRef::Constant(actor_id) => {
883                    let name = config.get_actor_name(*actor_id);
884                    format!("actor={name}")
885                }
886                MaybeRef::Ref(variable_id) => {
887                    let name = config.get_variable_name(*variable_id);
888                    format!("actor_id={name}")
889                }
890            };
891            let fn_name = if *is_learn_skill {
892                "learn_skill"
893            } else {
894                "forget_skill"
895            };
896            let skill = config.get_skill_name(*skill_id);
897
898            write_indent(&mut writer, indent)?;
899            writeln!(&mut writer, "{fn_name}({actor_arg}, skill={skill})")?;
900        }
901        Command::ChangeClass {
902            actor_id,
903            class_id,
904            keep_exp,
905        } => {
906            let actor = config.get_actor_name(*actor_id);
907            let class = config.get_class_name(*class_id);
908            let keep_exp = stringify_bool(*keep_exp);
909
910            write_indent(&mut writer, indent)?;
911            writeln!(
912                &mut writer,
913                "change_class(actor={actor}, klass={class}, keep_exp={keep_exp})"
914            )?;
915        }
916        Command::ChangeActorImages {
917            actor_id,
918            character_name,
919            character_index,
920            face_name,
921            face_index,
922            battler_name,
923        } => {
924            let actor_name = config.get_actor_name(*actor_id);
925            let character_name = escape_string(character_name);
926            let face_name = escape_string(face_name);
927            let battler_name = escape_string(battler_name);
928
929            write_indent(&mut writer, indent)?;
930            writeln!(&mut writer, "change_actor_images(")?;
931
932            write_indent(&mut writer, indent + 1)?;
933            writeln!(&mut writer, "actor={actor_name},")?;
934
935            write_indent(&mut writer, indent + 1)?;
936            writeln!(&mut writer, "character_name='{character_name}',")?;
937
938            write_indent(&mut writer, indent + 1)?;
939            writeln!(&mut writer, "character_index={character_index},")?;
940
941            write_indent(&mut writer, indent + 1)?;
942            writeln!(&mut writer, "face_name='{face_name}',")?;
943
944            write_indent(&mut writer, indent + 1)?;
945            writeln!(&mut writer, "face_index={face_index},")?;
946
947            write_indent(&mut writer, indent + 1)?;
948            writeln!(&mut writer, "battler_name='{battler_name}',")?;
949
950            write_indent(&mut writer, indent)?;
951            writeln!(&mut writer, ")")?;
952        }
953        Command::ForceAction {
954            is_enemy,
955            id,
956            skill_id,
957            target_index,
958        } => {
959            let arg_0 = if *is_enemy {
960                format!("enemy_index={id}")
961            } else {
962                let actor = config.get_actor_name(*id);
963                format!("actor={actor}")
964            };
965            let skill = config.get_skill_name(*skill_id);
966
967            write_indent(&mut writer, indent)?;
968            writeln!(
969                &mut writer,
970                "force_action({arg_0}, skill={skill}, target_index={target_index})"
971            )?;
972        }
973        Command::AbortBattle => {
974            let mut writer = FunctionCallWriter::new(&mut writer, indent, "abort_battle")?;
975            writer.finish()?;
976        }
977        Command::GameOver => {
978            let mut writer = FunctionCallWriter::new(&mut writer, indent, "game_over")?;
979            writer.finish()?;
980        }
981        Command::ReturnToTitleScreen => {
982            let mut writer =
983                FunctionCallWriter::new(&mut writer, indent, "return_to_title_screen")?;
984            writer.finish()?;
985        }
986        Command::Script { lines } => {
987            write_indent(&mut writer, indent)?;
988            writeln!(&mut writer, "script(")?;
989
990            write_indent(&mut writer, indent + 1)?;
991            writeln!(writer, "lines=[")?;
992
993            for line in lines {
994                let line = escape_string(line);
995
996                write_indent(&mut writer, indent + 2)?;
997                writeln!(writer, "'{line}',")?;
998            }
999
1000            write_indent(&mut writer, indent + 1)?;
1001            writeln!(&mut writer, "],")?;
1002
1003            write_indent(&mut writer, indent)?;
1004            writeln!(&mut writer, ")")?;
1005        }
1006        Command::PluginCommand { params } => {
1007            write_indent(&mut writer, indent)?;
1008            write!(&mut writer, "plugin_command(")?;
1009            for (i, param) in params.iter().enumerate() {
1010                if i != 0 {
1011                    write!(&mut writer, ", ")?;
1012                }
1013                let param = escape_string(param);
1014                write!(&mut writer, "'{param}'")?;
1015            }
1016            writeln!(&mut writer, ")")?;
1017        }
1018        Command::When {
1019            choice_index,
1020            choice_name,
1021        } => {
1022            write_indent(&mut writer, indent)?;
1023            writeln!(
1024                &mut writer,
1025                "if get_choice_index() == {choice_index}: # {choice_name}"
1026            )?;
1027        }
1028        Command::WhenCancel {
1029            choice_index,
1030            choice_name,
1031        } => {
1032            ensure!(choice_name.is_none());
1033
1034            write_indent(&mut writer, indent)?;
1035            writeln!(
1036                &mut writer,
1037                "if get_choice_index() == -1: # Cancel, index={choice_index}"
1038            )?;
1039        }
1040        Command::WhenEnd => {
1041            // Trust indents over end commands
1042        }
1043        Command::Else => {
1044            write_indent(&mut writer, indent)?;
1045            writeln!(&mut writer, "else:")?;
1046        }
1047        Command::ConditionalBranchEnd => {
1048            // Trust indents over end commands
1049        }
1050        Command::RepeatAbove => {
1051            // This is just a loop end
1052        }
1053        Command::IfWin => {
1054            writeln!(&mut writer, "if game_battle_result.is_win():")?;
1055        }
1056        Command::IfEscape => {
1057            writeln!(&mut writer, "if game_battle_result.is_escape():")?;
1058        }
1059        Command::IfLose => {
1060            writeln!(&mut writer, "if game_battle_result.is_lose():")?;
1061        }
1062        Command::BattleResultEnd => {
1063            // Trust indents over end commands
1064        }
1065        Command::Unknown { code, parameters } => {
1066            write_indent(&mut writer, indent)?;
1067            writeln!(
1068                &mut writer,
1069                "# Unknown Command Code {code:?}, parameters: {parameters:?}"
1070            )?;
1071        }
1072    }
1073    Ok(())
1074}
1075
1076fn stringify_bool(b: bool) -> &'static str {
1077    match b {
1078        true => "True",
1079        false => "False",
1080    }
1081}
1082
1083fn write_indent<W>(mut writer: W, indent: u16) -> std::io::Result<()>
1084where
1085    W: Write,
1086{
1087    for _ in 0..indent {
1088        write!(writer, "\t")?;
1089    }
1090
1091    Ok(())
1092}
1093
1094fn escape_string(input: &str) -> String {
1095    input.replace('\'', "\\'")
1096}
1097
1098fn write_audio_file<W>(
1099    mut writer: W,
1100    indent: u16,
1101    audio: &rpgmv_types::AudioFile,
1102) -> std::io::Result<()>
1103where
1104    W: Write,
1105{
1106    let audio_name = escape_string(&audio.name);
1107
1108    writeln!(&mut writer, "AudioFile(")?;
1109
1110    write_indent(&mut writer, indent + 1)?;
1111    writeln!(&mut writer, "name='{audio_name}',")?;
1112
1113    write_indent(&mut writer, indent + 1)?;
1114    writeln!(&mut writer, "pan={},", audio.pan)?;
1115
1116    write_indent(&mut writer, indent + 1)?;
1117    writeln!(&mut writer, "pitch={},", audio.pitch)?;
1118
1119    write_indent(&mut writer, indent + 1)?;
1120    writeln!(&mut writer, "volume={},", audio.volume)?;
1121
1122    write_indent(&mut writer, indent)?;
1123    writeln!(&mut writer, "),")?;
1124
1125    Ok(())
1126}