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