comparison src/th06/anm0_vm.rs @ 639:a8e0219162b6

Implement AnmRunner.
author Emmanuel Gil Peyrot <linkmauve@linkmauve.fr>
date Thu, 04 Jul 2019 15:21:46 +0200
parents
children 37d151fe000b
comparison
equal deleted inserted replaced
638:a806f28e94fc 639:a8e0219162b6
1 //! Animation runner.
2
3 use crate::th06::anm0::{
4 Script,
5 Anm0,
6 Call,
7 Instruction,
8 };
9 use std::cell::RefCell;
10 use std::rc::Rc;
11
12 #[derive(Debug, Clone)]
13 struct Interpolator;
14
15 /// Base visual element.
16 #[derive(Debug, Clone, Default)]
17 pub struct Sprite {
18 blendfunc: u32,
19 frame: u16,
20
21 width_override: f32,
22 height_override: f32,
23 angle: f32,
24
25 removed: bool,
26 changed: bool,
27 visible: bool,
28 force_rotation: bool,
29 automatic_orientation: bool,
30 allow_dest_offset: bool,
31 mirrored: bool,
32 corner_relative_placement: bool,
33
34 scale_interpolator: Option<Interpolator>,
35 fade_interpolator: Option<Interpolator>,
36 offset_interpolator: Option<Interpolator>,
37 rotation_interpolator: Option<Interpolator>,
38 color_interpolator: Option<Interpolator>,
39
40 anm: Option<Anm0>,
41
42 dest_offset: [f32; 3],
43 texcoords: [f32; 4],
44 texoffsets: [f32; 2],
45 rescale: [f32; 2],
46 scale_speed: [f32; 2],
47 rotations_3d: [f32; 3],
48 rotations_speed_3d: [f32; 3],
49 color: [u8; 4],
50 }
51
52 impl Sprite {
53 /// Create a new sprite.
54 pub fn new() -> Sprite {
55 Default::default()
56 }
57
58 /// Update sprite values from the interpolators.
59 pub fn update(&mut self) {
60 self.changed = false;
61 }
62 }
63
64 /// Interpreter for `Anm0` instructions to update a `Sprite`.
65 pub struct AnmRunner {
66 anm: Anm0,
67 sprite: Rc<RefCell<Sprite>>,
68 running: bool,
69 sprite_index_offset: u32,
70 script: Script,
71 instruction_pointer: usize,
72 frame: u16,
73 waiting: bool,
74 variables: ([i32; 4], [f32; 4], [i32; 4]),
75 timeout: Option<u16>,
76 }
77
78 impl AnmRunner {
79 /// Create a new `AnmRunner`.
80 pub fn new(anm: &Anm0, script_id: u8, sprite: Rc<RefCell<Sprite>>, sprite_index_offset: u32) -> AnmRunner {
81 let mut runner = AnmRunner {
82 anm: anm.clone(),
83 sprite: sprite,
84 running: true,
85 waiting: false,
86
87 script: anm.scripts[&script_id].clone(),
88 frame: 0,
89 timeout: None,
90 instruction_pointer: 0,
91 variables: ([0, 0, 0, 0 ],
92 [0., 0., 0., 0.],
93 [0, 0, 0, 0 ]),
94
95 sprite_index_offset: sprite_index_offset,
96 };
97 runner.run_frame();
98 runner.sprite_index_offset = 0;
99 runner
100 }
101
102 /// Trigger an interrupt.
103 pub fn interrupt(&mut self, interrupt: i32) -> bool {
104 let mut new_ip = self.script.interrupts.get(&interrupt);
105 if new_ip.is_none() {
106 new_ip = self.script.interrupts.get(&-1);
107 }
108 let new_ip = if let Some(new_ip) = new_ip {
109 *new_ip as usize
110 } else {
111 return false;
112 };
113 self.instruction_pointer = new_ip;
114 let Call { time: frame, instr: _ } = &self.script.instructions[self.instruction_pointer];
115 self.frame = *frame;
116 self.waiting = false;
117 self.sprite.borrow_mut().visible = true;
118 true
119 }
120
121 /// Advance the Anm of a single frame.
122 pub fn run_frame(&mut self) -> bool {
123 if !self.running {
124 return false;
125 }
126
127 while self.running && !self.waiting {
128 let Call { time: frame, instr } = self.script.instructions[self.instruction_pointer];
129 let frame = frame.clone();
130
131 if frame > self.frame {
132 break;
133 } else {
134 self.instruction_pointer += 1;
135 }
136
137 if frame == self.frame {
138 self.run_instruction(instr);
139 self.sprite.borrow_mut().changed = true;
140 }
141 }
142
143 if !self.waiting {
144 self.frame += 1;
145 } else if let Some(timeout) = self.timeout {
146 if timeout == self.sprite.borrow().frame { // TODO: check if it’s happening at the correct frame.
147 self.waiting = false;
148 }
149 }
150
151 self.sprite.borrow_mut().update();
152
153 self.running
154 }
155
156 fn run_instruction(&mut self, instruction: Instruction) {
157 let mut sprite = self.sprite.borrow_mut();
158 match instruction {
159 Instruction::Delete() => {
160 sprite.removed = true;
161 self.running = false;
162 }
163 Instruction::LoadSprite(sprite_index) => {
164 sprite.anm = Some(self.anm.clone());
165 let texcoords = &self.anm.sprites[(sprite_index + self.sprite_index_offset) as usize];
166 sprite.texcoords = [texcoords.x, texcoords.y, texcoords.width, texcoords.height];
167 }
168 Instruction::SetScale(sx, sy) => {
169 sprite.rescale = [sx, sy];
170 }
171 Instruction::SetAlpha(alpha) => {
172 // TODO: check this modulo.
173 sprite.color[3] = (alpha % 256) as u8;
174 }
175 Instruction::SetColor(b, g, r) => {
176 if sprite.fade_interpolator.is_none() {
177 sprite.color[0] = r;
178 sprite.color[1] = g;
179 sprite.color[2] = b;
180 }
181 }
182 Instruction::Jump(pointer) => {
183 // TODO: is that really how it works?
184 self.instruction_pointer = pointer as usize;
185 self.frame = self.script.instructions[pointer as usize].time;
186 }
187 Instruction::ToggleMirrored() => {
188 sprite.mirrored = !sprite.mirrored;
189 }
190 Instruction::SetRotations3d(rx, ry, rz) => {
191 sprite.rotations_3d = [rx, ry, rz];
192 }
193 Instruction::SetRotationsSpeed3d(srx, sry, srz) => {
194 sprite.rotations_speed_3d = [srx, sry, srz];
195 }
196 Instruction::SetScaleSpeed(ssx, ssy) => {
197 sprite.scale_speed = [ssx, ssy];
198 }
199 Instruction::Fade(new_alpha, duration) => {
200 // XXX: implement fade().
201 //sprite.fade(duration, new_alpha)
202 }
203 Instruction::SetBlendmodeAlphablend() => {
204 sprite.blendfunc = 1;
205 }
206 Instruction::SetBlendmodeAdd() => {
207 sprite.blendfunc = 0;
208 }
209 Instruction::KeepStill() => {
210 self.running = false;
211 }
212 Instruction::LoadRandomSprite(min_index, amplitude) => {
213 //self.load_sprite(min_index + randrange(amp))
214 }
215 Instruction::Move(x, y, z) => {
216 sprite.dest_offset = [x, y, z];
217 }
218 Instruction::MoveToLinear(x, y, z, duration) => {
219 // XXX: implement move_in().
220 //sprite.move_in(duration, x, y, z);
221 }
222 Instruction::MoveToDecel(x, y, z, duration) => {
223 // XXX: implement move_in().
224 //sprite.move_in(duration, x, y, z, 2*x - x**2);
225 }
226 Instruction::MoveToAccel(x, y, z, duration) => {
227 // XXX: implement move_in().
228 //sprite.move_in(duration, x, y, z, x**2);
229 }
230 Instruction::Wait() => {
231 self.waiting = true;
232 }
233 // There is nothing to do here.
234 Instruction::InterruptLabel(label) => (),
235 Instruction::SetCornerRelativePlacement() => {
236 sprite.corner_relative_placement = true;
237 }
238 Instruction::WaitEx() => {
239 sprite.visible = false;
240 self.waiting = true;
241 }
242 Instruction::SetAllowOffset(value) => {
243 sprite.allow_dest_offset = value == 1
244 }
245 Instruction::SetAutomaticOrientation(value) => {
246 sprite.automatic_orientation = value == 1
247 }
248 Instruction::ShiftTextureX(dx) => {
249 let [tox, toy] = sprite.texoffsets;
250 sprite.texoffsets = [tox + dx, toy];
251 }
252 Instruction::ShiftTextureY(dy) => {
253 let [tox, toy] = sprite.texoffsets;
254 sprite.texoffsets = [tox, toy + dy];
255 }
256 Instruction::SetVisible(visible) => {
257 sprite.visible = (visible & 1) != 0;
258 }
259 Instruction::ScaleIn(sx, sy, duration) => {
260 // TODO: implement scale_in().
261 //sprite.scale_in(duration, sx, sy)
262 }
263 Instruction::Todo(todo) => {
264 // TODO.
265 }
266 }
267 }
268 }
269
270 #[cfg(test)]
271 mod tests {
272 use super::*;
273 use std::io::{self, Read};
274 use std::fs::File;
275
276 #[test]
277 fn anm_runner() {
278 let file = File::open("/home/linkmauve/games/pc/東方/TH06 ~ The Embodiment of Scarlet Devil/CM/player01.anm").unwrap();
279 let mut file = io::BufReader::new(file);
280 let mut buf = vec![];
281 file.read_to_end(&mut buf).unwrap();
282 let anm0 = Anm0::from_slice(&buf).unwrap();
283 assert_eq!(anm0.size, (256, 256));
284 assert_eq!(anm0.format, 5);
285 let sprite = Rc::new(RefCell::new(Sprite::new()));
286 let mut anm_runner = AnmRunner::new(&anm0, 1, sprite.clone(), 0);
287 for _ in 0..50 {
288 anm_runner.run_frame();
289 }
290 }
291 }