pf_sandbox_lib/
json_upgrade.rs

1use serde_json::{Value, Number};
2
3pub fn build_version() -> String { String::from(env!("BUILD_VERSION")) }
4
5pub fn engine_version() -> u64 { 15 }
6
7pub fn engine_version_json() -> Value {
8    Value::Number(Number::from(engine_version()))
9}
10
11fn get_engine_version(object: &Value) -> u64 {
12    if let &Value::Object (ref object) = object {
13        if let Some (engine_version) = object.get("engine_version") {
14            if let Some (value) = engine_version.as_u64() {
15                return value
16            }
17        }
18    }
19    engine_version()
20}
21
22fn upgrade_engine_version(meta: &mut Value) {
23    if let &mut Value::Object (ref mut object) = meta {
24        object.insert(String::from("engine_version"), engine_version_json());
25    }
26}
27
28pub(crate) fn upgrade_to_latest_fighter(fighter: &mut Value, file_name: &str) {
29    let fighter_engine_version = get_engine_version(fighter);
30    if fighter_engine_version > engine_version() {
31        println!("Fighter: {} is newer than this version of PF Sandbox. Please upgrade to the latest version.", file_name);
32        // TODO: Display warning in window
33    }
34    else if fighter_engine_version < engine_version() {
35        for upgrade_from in fighter_engine_version..engine_version() {
36            match upgrade_from {
37                14 => { upgrade_fighter14(fighter) }
38                13 => { upgrade_fighter13(fighter) }
39                12 => { upgrade_fighter12(fighter) }
40                11 => { upgrade_fighter11(fighter) }
41                10 => { upgrade_fighter10(fighter) }
42                9  => { upgrade_fighter9(fighter) }
43                8  => { upgrade_fighter8(fighter) }
44                7  => { upgrade_fighter7(fighter) }
45                6  => { upgrade_fighter6(fighter) }
46                5  => { upgrade_fighter5(fighter) }
47                4  => { upgrade_fighter4(fighter) }
48                3  => { upgrade_fighter3(fighter) }
49                2  => { upgrade_fighter2(fighter) }
50                1  => { upgrade_fighter1(fighter) }
51                0  => { upgrade_fighter0(fighter) }
52                _ => { }
53            }
54        }
55        upgrade_engine_version(fighter);
56    }
57}
58
59pub(crate) fn upgrade_to_latest_stage(stage: &mut Value, file_name: &str) {
60    let stage_engine_version = get_engine_version(stage);
61    if stage_engine_version > engine_version() {
62        println!("Stage: {} is newer than this version of PF Sandbox. Please upgrade to the latest version.", file_name);
63        // TODO: Display warning in window
64    }
65    else if stage_engine_version < engine_version() {
66        // TODO: Handle upgrades here
67        upgrade_engine_version(stage);
68    }
69}
70
71pub(crate) fn upgrade_to_latest_rules(rules: &mut Value) {
72    let rules_engine_version = get_engine_version(rules);
73    if rules_engine_version > engine_version() {
74        println!("rules.json is newer than this version of PF Sandbox. Please upgrade to the latest version.");
75        // TODO: Display warning in window
76    }
77    else if rules_engine_version < engine_version() {
78        // TODO: Handle upgrades here
79        upgrade_engine_version(rules);
80    }
81}
82
83pub(crate) fn upgrade_to_latest_meta(meta: &mut Value) {
84    let meta_engine_version = get_engine_version(meta);
85    if meta_engine_version > engine_version() {
86        println!("meta.json is newer than this version of PF Sandbox. Please upgrade to the latest version.");
87        // TODO: Display warning in window
88    }
89    else if meta_engine_version < engine_version() {
90        // TODO: Handle upgrades here
91        upgrade_engine_version(meta);
92    }
93}
94
95fn get_vec<'a>(parent: &'a mut Value, member: &str) -> Option<&'a mut Vec<Value>> {
96    if let &mut Value::Object (ref mut object) = parent {
97        if let Some (array) = object.get_mut(member) {
98            if let &mut Value::Array (ref mut vector) = array {
99                return Some (vector);
100            }
101        }
102    }
103    return None;
104}
105
106// Important:
107// Upgrades cannot rely on current structs as future changes may break those past upgrades
108//
109/// move set_x_vel/set_y_vel to x_vel_modify/y_vel_modify and x_vel_temp/y_vel_temp
110fn upgrade_fighter14(fighter: &mut Value) {
111    if let Some (actions) = get_vec(fighter, "actions") {
112        for action in actions {
113            if let Some (frames) = get_vec(action, "frames") {
114                for frame in frames {
115                    if let &mut Value::Object (ref mut frame) = frame {
116                        frame.remove(&String::from("set_x_vel"));
117                        frame.remove(&String::from("set_y_vel"));
118                        frame.insert(String::from("x_vel_modify"), json!("None"));
119                        frame.insert(String::from("y_vel_modify"), json!("None"));
120                        frame.insert(String::from("x_vel_temp"), json!("0.0"));
121                        frame.insert(String::from("y_vel_temp"), json!("0.0"));
122                    }
123                }
124            }
125        }
126    }
127}
128
129/// Split spawn action into spawn and respawn
130fn upgrade_fighter13(fighter: &mut Value) {
131    let action = json!({
132      "frames": [
133        {
134          "ecb": {
135            "top": 16.0,
136            "left": -4.0,
137            "right": 4.0,
138            "bottom": 0.0
139          },
140          "colboxes": [],
141          "colbox_links": [],
142          "render_order": [],
143          "effects": [],
144          "item_hold_x": 4.0,
145          "item_hold_y": 11.0,
146          "grab_hold_x": 4.0,
147          "grab_hold_y": 11.0,
148          "pass_through": true,
149          "ledge_cancel": false,
150          "use_platform_angle": false,
151          "ledge_grab_box": null,
152          "force_hitlist_reset": false
153        }
154      ],
155      "iasa": 0
156    });
157
158    if let Some (actions) = get_vec(fighter, "actions") {
159        actions.insert(0, action.clone());
160    }
161}
162
163/// add enable_reverse_hit to Hit
164fn upgrade_fighter12(fighter: &mut Value) {
165    if let Some (actions) = get_vec(fighter, "actions") {
166        for action in actions {
167            if let Some (frames) = get_vec(action, "frames") {
168                for frame in frames {
169                    if let Some (colboxes) = get_vec(frame, "colboxes") {
170                        for colbox in colboxes {
171                            if let &mut Value::Object (ref mut colbox) = colbox {
172                                if let Some (role) = colbox.get_mut("role") {
173                                    if let &mut Value::Object (ref mut role) = role {
174                                        if let Some (hitbox) = role.get_mut("Hit") {
175                                            if let &mut Value::Object (ref mut hitbox) = hitbox {
176                                                hitbox.insert(String::from("enable_reverse_hit"), Value::Bool(true));
177                                            }
178                                        }
179                                    }
180                                }
181                            }
182                        }
183                    }
184                }
185            }
186        }
187    }
188}
189
190/// Upgrade to new ECB format
191fn upgrade_fighter11(fighter: &mut Value) {
192    let ecb = json!({
193        "top": 16.0,
194        "left": -4.0,
195        "right": 4.0,
196        "bottom": 0.0
197    });
198
199    if let Some (actions) = get_vec(fighter, "actions") {
200        for action in actions {
201            if let Some (frames) = get_vec(action, "frames") {
202                for frame in frames {
203                    if let &mut Value::Object (ref mut frame) = frame {
204                        frame.insert(String::from("ecb"), ecb.clone());
205                    }
206                }
207            }
208        }
209    }
210}
211
212/// Change CSS properties
213fn upgrade_fighter10(fighter: &mut Value) {
214    if let &mut Value::Object (ref mut fighter) = fighter {
215        fighter.insert(String::from("css_action"), Value::Number(Number::from(2)));
216        fighter.insert(String::from("css_scale"), Value::Number(Number::from(1)));
217    }
218}
219
220/// Add turn properties to fighter
221fn upgrade_fighter9(fighter: &mut Value) {
222    if let &mut Value::Object (ref mut fighter) = fighter {
223        fighter.insert(String::from("run_turn_flip_dir_frame"), Value::Number(Number::from(30)));
224        fighter.insert(String::from("tilt_turn_flip_dir_frame"), Value::Number(Number::from(5)));
225        fighter.insert(String::from("tilt_turn_into_dash_iasa"), Value::Number(Number::from(5)));
226    }
227}
228
229/// Add use_platform_angle to ActionFrame
230fn upgrade_fighter8(fighter: &mut Value) {
231    if let Some (actions) = get_vec(fighter, "actions") {
232        for action in actions {
233            if let Some (frames) = get_vec(action, "frames") {
234                for frame in frames {
235                    if let &mut Value::Object (ref mut frame) = frame {
236                        frame.insert(String::from("use_platform_angle"), Value::Bool(false));
237                    }
238                }
239            }
240        }
241    }
242}
243
244/// Add MissedTech states
245fn upgrade_fighter7(fighter: &mut Value) {
246    let action_indexes: Vec<usize> = vec!(7, 46, 47, 48, 54, 69);
247    let action = json!({
248      "frames": [
249        {
250          "ecb": {
251            "top_x": 0.0,
252            "top_y": 16.0,
253            "left_x": -4.0,
254            "left_y": 11.0,
255            "right_x": 4.0,
256            "right_y": 11.0,
257            "bot_x": 0.0,
258            "bot_y": 0.0
259          },
260          "colboxes": [],
261          "colbox_links": [],
262          "render_order": [],
263          "effects": [],
264          "item_hold_x": 4.0,
265          "item_hold_y": 11.0,
266          "grab_hold_x": 4.0,
267          "grab_hold_y": 11.0,
268          "pass_through": true,
269          "ledge_cancel": false,
270          "ledge_grab_box": null,
271          "force_hitlist_reset": false
272        }
273      ],
274      "iasa": 0
275    });
276
277    if let Some (actions) = get_vec(fighter, "actions") {
278        for action_index in &action_indexes {
279            actions.insert(*action_index, action.clone());
280        }
281    }
282}
283
284/// Add power shield state
285fn upgrade_fighter6(fighter: &mut Value) {
286    let action_indexes: Vec<usize> = vec!(31, 47, 48, 49);
287    let action = json!({
288      "frames": [
289        {
290          "ecb": {
291            "top_x": 0.0,
292            "top_y": 16.0,
293            "left_x": -4.0,
294            "left_y": 11.0,
295            "right_x": 4.0,
296            "right_y": 11.0,
297            "bot_x": 0.0,
298            "bot_y": 0.0
299          },
300          "colboxes": [],
301          "colbox_links": [],
302          "render_order": [],
303          "effects": [],
304          "item_hold_x": 4.0,
305          "item_hold_y": 11.0,
306          "grab_hold_x": 4.0,
307          "grab_hold_y": 11.0,
308          "pass_through": true,
309          "ledge_cancel": false,
310          "ledge_grab_box": null,
311          "force_hitlist_reset": false
312        }
313      ],
314      "iasa": 0
315    });
316
317    if let Some (actions) = get_vec(fighter, "actions") {
318        for action_index in &action_indexes {
319            actions.insert(*action_index, action.clone());
320        }
321    }
322}
323
324/// teeter + ledge cancel
325fn upgrade_fighter5(fighter: &mut Value) {
326    //ledge_cancel to ActionFrame
327    if let Some (actions) = get_vec(fighter, "actions") {
328        for action in actions {
329            if let Some (frames) = get_vec(action, "frames") {
330                for frame in frames {
331                    if let &mut Value::Object (ref mut frame) = frame {
332                        frame.insert(String::from("ledge_cancel"), Value::Bool(true));
333                    }
334                }
335            }
336        }
337    }
338
339    // add teeter and spotdoge actions
340    let action_indexes: Vec<usize> = vec!(5, 6, 36);
341    let action = json!({
342      "frames": [
343        {
344          "ecb": {
345            "top_x": 0.0,
346            "top_y": 16.0,
347            "left_x": -4.0,
348            "left_y": 11.0,
349            "right_x": 4.0,
350            "right_y": 11.0,
351            "bot_x": 0.0,
352            "bot_y": 0.0
353          },
354          "colboxes": [],
355          "colbox_links": [],
356          "render_order": [],
357          "effects": [],
358          "item_hold_x": 4.0,
359          "item_hold_y": 11.0,
360          "grab_hold_x": 4.0,
361          "grab_hold_y": 11.0,
362          "pass_through": true,
363          "ledge_cancel": false,
364          "ledge_grab_box": null,
365          "force_hitlist_reset": false
366        }
367      ],
368      "iasa": 0
369    });
370
371    if let Some (actions) = get_vec(fighter, "actions") {
372        for action_index in &action_indexes {
373            actions.insert(*action_index, action.clone());
374        }
375    }
376}
377
378/// add ledge actions
379fn upgrade_fighter4(fighter: &mut Value) {
380    let action_indexes: Vec<usize> = vec!(4, 24, 25, 26, 27, 28, 41, 42, 55, 56);
381    let action = json!({
382      "frames": [
383        {
384          "ecb": {
385            "top_x": 0.0,
386            "top_y": 16.0,
387            "left_x": -4.0,
388            "left_y": 11.0,
389            "right_x": 4.0,
390            "right_y": 11.0,
391            "bot_x": 0.0,
392            "bot_y": 0.0
393          },
394          "colboxes": [],
395          "colbox_links": [],
396          "render_order": [],
397          "effects": [],
398          "item_hold_x": 4.0,
399          "item_hold_y": 11.0,
400          "grab_hold_x": 4.0,
401          "grab_hold_y": 11.0,
402          "pass_through": true,
403          "ledge_grab_box": null,
404          "force_hitlist_reset": false
405        }
406      ],
407      "iasa": 0
408    });
409
410    if let Some (actions) = get_vec(fighter, "actions") {
411        for action_index in &action_indexes {
412            actions.insert(*action_index, action.clone());
413        }
414    }
415}
416
417/// add pass_through to ActionFrame
418fn upgrade_fighter3(fighter: &mut Value) {
419    if let Some (actions) = get_vec(fighter, "actions") {
420        for action in actions {
421            if let Some (frames) = get_vec(action, "frames") {
422                for frame in frames {
423                    if let &mut Value::Object (ref mut frame) = frame {
424                        frame.insert(String::from("pass_through"), Value::Bool(true));
425                    }
426                }
427            }
428        }
429    }
430}
431
432/// add force_hitlist_reset to ActionFrame
433fn upgrade_fighter2(fighter: &mut Value) {
434    if let Some (actions) = get_vec(fighter, "actions") {
435        for action in actions {
436            if let Some (frames) = get_vec(action, "frames") {
437                for frame in frames {
438                    if let &mut Value::Object (ref mut frame) = frame {
439                        frame.insert(String::from("force_hitlist_reset"), Value::Bool(false));
440                    }
441                }
442            }
443        }
444    }
445}
446
447/// add hitstun enum to hitboxes
448fn upgrade_fighter1(fighter: &mut Value) {
449    if let Some (actions) = get_vec(fighter, "actions") {
450        for action in actions {
451            if let Some (frames) = get_vec(action, "frames") {
452                for frame in frames {
453                    if let Some (colboxes) = get_vec(frame, "colboxes") {
454                        for colbox in colboxes {
455                            if let &mut Value::Object (ref mut colbox) = colbox {
456                                if let Some (role) = colbox.get_mut("role") {
457                                    if let &mut Value::Object (ref mut role) = role {
458                                        if let Some (hitbox) = role.get_mut("Hit") {
459                                            if let &mut Value::Object (ref mut hitbox) = hitbox {
460                                                let hitstun = json!({"FramesTimesKnockback": 0.5});
461                                                hitbox.insert(String::from("hitstun"), hitstun);
462                                            }
463                                        }
464                                    }
465                                }
466                            }
467                        }
468                    }
469                }
470            }
471        }
472    }
473}
474
475/// Add order vec to frame
476/// Change Meld into MeldFirst
477fn upgrade_fighter0(fighter: &mut Value) {
478    if let Some (actions) = get_vec(fighter, "actions") {
479        for action in actions {
480            if let Some (frames) = get_vec(action, "frames") {
481                for frame in frames {
482                    if let &mut Value::Object (ref mut frame) = frame {
483                        frame.insert(String::from("render_order"), Value::Array(vec!()));
484                    }
485
486                    if let Some (colbox_links) = get_vec(frame, "colbox_links") {
487                        for colbox_link in colbox_links {
488                            if let &mut Value::Object (ref mut colbox_link) = colbox_link {
489                                let mut old_value = false;
490                                if let Some (link_type) = colbox_link.get_mut("link_type") {
491                                    if let &mut Value::String (ref mut link_type_string) = link_type {
492                                        if link_type_string.as_str() == "Meld" {
493                                            old_value = true;
494                                        }
495                                    }
496                                }
497                                if old_value {
498                                    colbox_link.insert(String::from("link_type"), Value::String(String::from("MeldFirst")));
499                                }
500                            }
501                        }
502                    }
503                }
504            }
505        }
506    }
507}