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