Skip to main content

siglus_scene_vm/runtime/forms/
se.rs

1use anyhow::{bail, Result};
2
3use crate::runtime::{CommandContext, Value};
4
5use super::codes::se_op;
6
7fn store_or_push_se_prop(ctx: &mut CommandContext, op: i32, args: &[Value]) {
8    let form_key = if ctx.ids.form_global_se != 0 {
9        ctx.ids.form_global_se
10    } else {
11        super::codes::FORM_GLOBAL_SE
12    };
13    let prop = op;
14    if let Some(v) = args.get(0).cloned() {
15        match v {
16            Value::Str(s) => {
17                ctx.globals
18                    .str_props
19                    .entry(form_key)
20                    .or_default()
21                    .insert(prop, s);
22            }
23            Value::Int(n) => {
24                ctx.globals
25                    .int_props
26                    .entry(form_key)
27                    .or_default()
28                    .insert(prop, n);
29            }
30            _ => {}
31        }
32        ctx.push(Value::Int(0));
33        return;
34    }
35    if let Some(s) = ctx
36        .globals
37        .str_props
38        .get(&form_key)
39        .and_then(|m| m.get(&prop))
40        .cloned()
41    {
42        ctx.push(Value::Str(s));
43        return;
44    }
45    let v = ctx
46        .globals
47        .int_props
48        .get(&form_key)
49        .and_then(|m| m.get(&prop).copied())
50        .unwrap_or(0);
51    ctx.push(Value::Int(v));
52}
53
54fn arg_str<'a>(args: &'a [Value], idx: usize) -> Option<&'a str> {
55    match args.get(idx) {
56        Some(Value::Str(s)) => Some(s.as_str()),
57        _ => None,
58    }
59}
60
61fn arg_int(args: &[Value], idx: usize) -> Option<i64> {
62    match args.get(idx) {
63        Some(Value::Int(v)) => Some(*v),
64        _ => None,
65    }
66}
67
68fn resolve_se_file_name(ctx: &CommandContext, se_no: i64) -> Option<&str> {
69    if se_no < 0 {
70        return None;
71    }
72    ctx.tables
73        .se_file_names
74        .get(se_no as usize)
75        .and_then(|v| v.as_deref())
76        .filter(|s| !s.is_empty())
77}
78
79pub fn dispatch(ctx: &mut CommandContext, args: &[Value]) -> Result<bool> {
80    let ret_form: Option<i64> = crate::runtime::forms::prop_access::current_vm_meta(ctx).1;
81    let Some(op) = crate::runtime::forms::prop_access::current_op_from_ctx_or_args(ctx, args)
82    else {
83        bail!("SE form expects an element opcode");
84    };
85    let args = crate::runtime::forms::prop_access::params_without_op(ctx, args);
86
87    match op {
88        se_op::PLAY_BY_FILE_NAME => {
89            let name = match arg_str(args, 0) {
90                Some(s) => s,
91                None => {
92                    store_or_push_se_prop(ctx, op, args);
93                    return Ok(true);
94                }
95            };
96            let ok = {
97                let (se, audio) = (&mut ctx.se, &mut ctx.audio);
98                se.play_file_name(audio, name).is_ok()
99            };
100            if !ok {
101                ctx.unknown
102                    .record_note(&format!("se.play_file.failed:{name}"));
103            }
104            Ok(true)
105        }
106        se_op::STOP => {
107            // The original engine supports an optional fade time.
108            let fade = arg_int(args, 0);
109            ctx.se.stop(fade)?;
110            Ok(true)
111        }
112        se_op::SET_VOLUME => {
113            let vol = match arg_int(args, 0) {
114                Some(v) => v.clamp(0, 255) as u8,
115                None => {
116                    store_or_push_se_prop(ctx, op, args);
117                    return Ok(true);
118                }
119            };
120            let fade = arg_int(args, 1).unwrap_or(0);
121            let (se, audio) = (&mut ctx.se, &mut ctx.audio);
122            se.set_volume_raw_fade(audio, vol, fade)?;
123            Ok(true)
124        }
125        se_op::SET_VOLUME_MAX => {
126            let fade = arg_int(args, 0).unwrap_or(0);
127            let (se, audio) = (&mut ctx.se, &mut ctx.audio);
128            se.set_volume_raw_fade(audio, 255, fade)?;
129            Ok(true)
130        }
131        se_op::SET_VOLUME_MIN => {
132            let fade = arg_int(args, 0).unwrap_or(0);
133            let (se, audio) = (&mut ctx.se, &mut ctx.audio);
134            se.set_volume_raw_fade(audio, 0, fade)?;
135            Ok(true)
136        }
137        se_op::GET_VOLUME => {
138            ctx.push(Value::Int(ctx.se.volume_raw() as i64));
139            Ok(true)
140        }
141        se_op::CHECK => {
142            let playing = ctx.se.is_playing_any();
143            ctx.push(Value::Int(if playing { 1 } else { 0 }));
144            Ok(true)
145        }
146        se_op::WAIT => {
147            ctx.wait
148                .wait_audio(crate::runtime::wait::AudioWait::SeAny, false);
149            if ret_form.unwrap_or(0) != 0 {
150                ctx.push(Value::Int(0));
151            }
152            Ok(true)
153        }
154        se_op::WAIT_KEY => {
155            ctx.wait
156                .wait_audio(crate::runtime::wait::AudioWait::SeAny, true);
157            if ret_form.unwrap_or(0) != 0 {
158                ctx.push(Value::Int(0));
159            }
160            Ok(true)
161        }
162
163        se_op::PLAY | se_op::PLAY_BY_SE_NO => {
164            let se_no = match arg_int(args, 0) {
165                Some(v) => v,
166                None => {
167                    store_or_push_se_prop(ctx, op, args);
168                    return Ok(true);
169                }
170            };
171            if let Some(name) = resolve_se_file_name(ctx, se_no).map(|s| s.to_string()) {
172                let ok = {
173                    let (se, audio) = (&mut ctx.se, &mut ctx.audio);
174                    se.play_file_name(audio, &name).is_ok()
175                };
176                if !ok {
177                    ctx.unknown
178                        .record_note(&format!("se.play.failed:{se_no}:{name}"));
179                }
180            } else {
181                ctx.unknown
182                    .record_note(&format!("se.table.missing:{se_no}"));
183            }
184            Ok(true)
185        }
186
187        se_op::PLAY_BY_KOE_NO => {
188            let koe_no = match arg_int(args, 0) {
189                Some(v) => v,
190                None => {
191                    store_or_push_se_prop(ctx, op, args);
192                    return Ok(true);
193                }
194            };
195            let ok = {
196                let (se, audio) = (&mut ctx.se, &mut ctx.audio);
197                se.play_koe_no(audio, koe_no).is_ok()
198            };
199            if ok {
200                return Ok(true);
201            }
202            store_or_push_se_prop(ctx, op, args);
203            Ok(true)
204        }
205
206        _ => {
207            store_or_push_se_prop(ctx, op, args);
208            Ok(true)
209        }
210    }
211}