siglus_scene_vm/runtime/forms/
pcmevent.rs1use anyhow::Result;
11
12use crate::runtime::globals::{PcmEventLine, PcmEventState};
13use crate::runtime::wait::AudioWait;
14use crate::runtime::{CommandContext, Value};
15
16enum PcmEventOp {
17 StartOneShot,
18 StartLoop,
19 StartRandom,
20 Stop,
21 Check,
22 Wait,
23 WaitKey,
24 Unknown,
25}
26
27fn resolve_pcm_event_op(op: i32) -> PcmEventOp {
28 match op {
29 crate::runtime::constants::PCMEVENT_START_ONESHOT => PcmEventOp::StartOneShot,
30 crate::runtime::constants::PCMEVENT_START_LOOP => PcmEventOp::StartLoop,
31 crate::runtime::constants::PCMEVENT_START_RANDOM => PcmEventOp::StartRandom,
32 crate::runtime::constants::PCMEVENT_STOP => PcmEventOp::Stop,
33 crate::runtime::constants::PCMEVENT_CHECK => PcmEventOp::Check,
34 crate::runtime::constants::PCMEVENT_WAIT_KEY => PcmEventOp::WaitKey,
35 crate::runtime::constants::PCMEVENT_WAIT => PcmEventOp::Wait,
36 _ => PcmEventOp::Unknown,
37 }
38}
39
40fn named_int(args: &[Value], id: i32) -> Option<i64> {
41 args.iter().find_map(|v| match v {
42 Value::NamedArg { id: nid, value } if *nid == id => value.as_i64(),
43 _ => None,
44 })
45}
46
47fn collect_lines(args: &[Value], random: bool) -> Vec<PcmEventLine> {
48 let mut out = Vec::new();
49 for v in args {
50 match v {
51 Value::Str(s) => out.push(PcmEventLine {
52 file_name: s.clone(),
53 probability: if random { 1 } else { 0 },
54 min_time: 0,
55 max_time: 0,
56 }),
57 Value::List(items) if !items.is_empty() => {
58 let file_name = items
59 .first()
60 .and_then(Value::as_str)
61 .unwrap_or("")
62 .to_string();
63 if file_name.is_empty() {
64 continue;
65 }
66 let mut line = PcmEventLine {
67 file_name,
68 probability: if random { 1 } else { 0 },
69 min_time: 0,
70 max_time: 0,
71 };
72 if random {
73 if let Some(v) = items.get(1).and_then(Value::as_i64) {
74 line.probability = v as i32;
75 }
76 if let Some(v) = items.get(2).and_then(Value::as_i64) {
77 line.min_time = v as i32;
78 line.max_time = v as i32;
79 }
80 if let Some(v) = items.get(3).and_then(Value::as_i64) {
81 line.max_time = v as i32;
82 }
83 } else {
84 if let Some(v) = items.get(1).and_then(Value::as_i64) {
85 line.min_time = v as i32;
86 line.max_time = v as i32;
87 }
88 if let Some(v) = items.get(2).and_then(Value::as_i64) {
89 line.max_time = v as i32;
90 }
91 }
92 out.push(line);
93 }
94 _ => {}
95 }
96 }
97 out
98}
99
100pub fn dispatch(ctx: &mut CommandContext, args: &[Value]) -> Result<bool> {
101 let form_global_pcm_event = ctx.ids.form_global_pcm_event;
102 let elm_array = ctx.ids.elm_array;
103 let Some((chain_pos, chain)) = crate::runtime::forms::prop_access::parse_element_chain_ctx(
104 ctx,
105 form_global_pcm_event,
106 args,
107 ) else {
108 return Ok(false);
109 };
110 let chain = chain.to_vec();
111 if chain.len() < 3 || chain[1] != elm_array {
112 return Ok(false);
113 }
114 let idx = chain[2].max(0) as usize;
115
116 {
117 let list = ctx
118 .globals
119 .pcm_event_lists
120 .entry(form_global_pcm_event)
121 .or_insert_with(Vec::new);
122 if list.len() <= idx {
123 list.resize(idx + 1, PcmEventState::default());
124 }
125 }
126
127 if chain.len() == 3 {
129 return Ok(true);
130 }
131
132 let op = resolve_pcm_event_op(chain[3]);
133 let script_args = if chain_pos == args.len() {
134 crate::runtime::forms::prop_access::script_args(args, chain_pos)
135 } else {
136 &args[..chain_pos]
137 };
138 match op {
139 PcmEventOp::StartOneShot | PcmEventOp::StartLoop | PcmEventOp::StartRandom => {
140 let random = matches!(op, PcmEventOp::StartRandom);
141 let looped = matches!(op, PcmEventOp::StartLoop);
142 let lines = collect_lines(script_args, random);
143 let active = if let Some(line) = lines.first() {
144 let (pcm, audio) = (&mut ctx.pcm, &mut ctx.audio);
145 pcm.play_in_slot(audio, idx, &line.file_name, looped)
146 .is_ok()
147 } else {
148 false
149 };
150 if let Some(st) = ctx
151 .globals
152 .pcm_event_lists
153 .get_mut(&form_global_pcm_event)
154 .and_then(|v| v.get_mut(idx))
155 {
156 st.reinit();
157 st.random = random;
158 st.looped = looped;
159 st.volume_type = named_int(script_args, 3).unwrap_or(0) as i32;
160 st.bgm_fade_target_flag = named_int(script_args, 4).unwrap_or(0) != 0;
161 st.bgm_fade2_target_flag = named_int(script_args, 5).unwrap_or(0) != 0;
162 st.chara_no = named_int(script_args, 6).unwrap_or(-1) as i32;
163 st.time_type = named_int(script_args, 11).unwrap_or(0) != 0;
164 st.bgm_fade2_source_flag = named_int(script_args, 12).unwrap_or(0) != 0;
165 st.real_flag = true;
166 st.lines = lines;
167 st.active = active;
168 }
169 ctx.push(Value::Int(0));
170 Ok(true)
171 }
172 PcmEventOp::Stop => {
173 let fade = script_args.first().and_then(Value::as_i64).unwrap_or(0);
174 let _ = ctx.pcm.stop_slot(idx, Some(fade));
175 if let Some(st) = ctx
176 .globals
177 .pcm_event_lists
178 .get_mut(&form_global_pcm_event)
179 .and_then(|v| v.get_mut(idx))
180 {
181 st.active = false;
182 }
183 ctx.push(Value::Int(0));
184 Ok(true)
185 }
186 PcmEventOp::Check => {
187 let playing = ctx.pcm.is_playing_slot(idx);
188 if let Some(st) = ctx
189 .globals
190 .pcm_event_lists
191 .get_mut(&form_global_pcm_event)
192 .and_then(|v| v.get_mut(idx))
193 {
194 st.active = playing;
195 }
196 ctx.push(Value::Int(if playing { 1 } else { 0 }));
197 Ok(true)
198 }
199 PcmEventOp::Wait => {
200 ctx.wait.wait_audio(AudioWait::PcmSlot(idx as u8), false);
201 Ok(true)
202 }
203 PcmEventOp::WaitKey => {
204 ctx.wait.wait_audio(AudioWait::PcmSlot(idx as u8), true);
205 Ok(true)
206 }
207 PcmEventOp::Unknown => Ok(false),
208 }
209}