siglus_scene_vm/runtime/forms/
se.rs1use 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 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}