1mod splitter;
2#[cfg(feature = "codeclient")]
3pub mod template_sender;
4
5pub use splitter::split_templates;
6
7use std::{
8 collections::HashMap,
9 io::{Read, Write},
10};
11
12use base64::{Engine, prelude::BASE64_STANDARD};
13use flate2::{read::GzDecoder, write::GzEncoder};
14use serde_derive::{Deserialize, Serialize};
15use serde_json::Value;
16
17#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
18pub struct Template {
19 pub blocks: Vec<Block>,
20}
21
22#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
26#[serde(from = "JSONBlock", into = "JSONBlock")]
27pub enum Block {
28 PlayerEvent {
29 action: String,
30 ls_cancel: bool,
31 },
32 EntityEvent {
33 action: String,
34 ls_cancel: bool,
35 },
36 Function {
37 args: Args,
38 name: String,
39 },
40 Process {
41 args: Args,
42 name: String,
43 },
44 PlayerAction {
45 args: Args,
46 action: String,
47 #[serde(skip_serializing_if = "Option::is_none")]
48 target: Option<Target>,
49 },
50 IfPlayer {
51 args: Args,
52 action: String,
53 #[serde(skip_serializing_if = "Option::is_none")]
54 target: Option<Target>,
55 is_negated: bool,
56 },
57 StartProcess {
58 args: Args,
59 proc: String,
60 },
61 CallFunction {
62 args: Args,
63 func: String,
64 },
65 Control {
66 args: Args,
67 action: String,
68 },
69 SetVariable {
70 args: Args,
71 action: String,
72 },
73 IfEntity {
74 args: Args,
75 action: String,
76 target: Option<Target>,
77 is_negated: bool,
78 },
79 EntityAction {
80 args: Args,
81 action: String,
82 target: Option<Target>,
83 },
84 IfVariable {
85 args: Args,
86 action: String,
87 is_negated: bool,
88 },
89 SelectObject {
90 args: Args,
91 action: String,
92 sub_action: Option<String>,
93 is_negated: bool,
94 },
95 GameAction {
96 args: Args,
97 action: String,
98 },
99 Repeat {
100 args: Args,
101 action: String,
102 sub_action: Option<String>,
103 is_negated: bool,
104 },
105 IfGame {
106 args: Args,
107 action: String,
108 is_negated: bool,
109 },
110 Else,
111 Bracket {
112 direction: BracketDirection,
113 kind: BracketKind,
114 },
115}
116
117#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
118#[serde(from = "JSONArgs", into = "JSONArgs")]
119pub struct Args(pub Vec<(usize, Item)>);
120
121#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
122#[serde(tag = "id", content = "data")]
123pub enum Item {
124 #[serde(rename = "txt")]
125 String { name: String },
126 #[serde(rename = "comp")]
127 Text { name: String },
128 #[serde(rename = "num")]
129 Number { name: String },
130 #[serde(rename = "item")]
131 Item { item: String },
132 #[serde(rename = "loc")]
133 Location {
134 #[serde(rename = "isBlock")]
135 is_block: bool,
136 loc: Location,
137 },
138 #[serde(rename = "vec")]
139 Vector { x: f64, y: f64, z: f64 },
140 #[serde(rename = "var")]
141 Variable { name: String, scope: VariableScope },
142 #[serde(rename = "g_val")]
143 GameValue {
144 #[serde(rename = "type")]
145 kind: String,
146 target: Target,
147 },
148 #[serde(rename = "pn_el")]
149 Param {
150 name: String,
151 #[serde(rename = "type")]
152 kind: ParamKind,
153 #[serde(skip_serializing_if = "Option::is_none")]
154 default_value: Option<Box<Item>>,
155 plural: bool,
156 optional: bool,
157 #[serde(skip_serializing_if = "Option::is_none")]
158 description: Option<String>,
159 #[serde(skip_serializing_if = "Option::is_none")]
160 note: Option<String>,
161 },
162 #[serde(rename = "bl_tag")]
163 Tag {
164 option: String,
165 tag: String,
166 action: String,
167 block: CodeBlock,
168 },
169 #[serde(rename = "snd")]
170 Sound {
171 pitch: f64,
172 vol: f64,
173 sound: String,
174 #[serde(flatten)]
175 extra: HashMap<String, Value>,
176 },
177 #[serde(rename = "part")]
178 Particle {
179 particle: String,
180 #[serde(flatten)]
181 extra: HashMap<String, Value>,
182 },
183 #[serde(rename = "pot")]
184 Potion {
185 #[serde(rename = "pot")]
186 potion: String,
187 #[serde(rename = "dur")]
188 duration: u32,
189 #[serde(rename = "amp")]
190 amplifier: u32,
191 },
192}
193
194#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
195pub struct Location {
196 pub x: f64,
197 pub y: f64,
198 pub z: f64,
199 pub pitch: f64,
200 pub yaw: f64,
201}
202
203#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
206struct JSONArgs {
207 items: Vec<SlottedItem>,
208}
209
210#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
211struct SlottedItem {
212 item: Item,
213 slot: usize,
214}
215
216#[derive(Debug, Serialize, Deserialize)]
221#[serde(tag = "id")]
222enum JSONBlock {
223 #[serde(rename = "block")]
224 CodeBlock {
225 block: CodeBlock,
226 #[serde(skip_serializing_if = "Option::is_none")]
227 args: Option<Args>,
228 #[serde(skip_serializing_if = "Option::is_none")]
229 action: Option<String>,
230 #[serde(skip_serializing_if = "Option::is_none")]
231 data: Option<String>,
232 #[serde(skip_serializing_if = "Option::is_none")]
233 attribute: Option<String>,
234 #[serde(rename = "subAction")]
235 #[serde(skip_serializing_if = "Option::is_none")]
236 sub_action: Option<String>,
237 #[serde(skip_serializing_if = "Option::is_none")]
238 target: Option<Target>,
239 },
240 #[serde(rename = "bracket")]
241 Bracket {
242 #[serde(rename = "direct")]
243 direction: BracketDirection,
244 #[serde(rename = "type")]
245 kind: BracketKind,
246 },
247}
248
249#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
254pub enum CodeBlock {
255 #[serde(rename = "event")]
256 PlayerEvent,
257 #[serde(rename = "entity_event")]
258 EntityEvent,
259 #[serde(rename = "func")]
260 Function,
261 #[serde(rename = "process")]
262 Process,
263 #[serde(rename = "player_action")]
264 PlayerAction,
265 #[serde(rename = "if_player")]
266 IfPlayer,
267 #[serde(rename = "start_process")]
268 StartProcess,
269 #[serde(rename = "call_func")]
270 CallFunction,
271 #[serde(rename = "control")]
272 Control,
273 #[serde(rename = "set_var")]
274 SetVariable,
275 #[serde(rename = "if_entity")]
276 IfEntity,
277 #[serde(rename = "entity_action")]
278 EntityAction,
279 #[serde(rename = "if_var")]
280 IfVariable,
281 #[serde(rename = "select_obj")]
282 SelectObject,
283 #[serde(rename = "game_action")]
284 GameAction,
285 #[serde(rename = "repeat")]
286 Repeat,
287 #[serde(rename = "if_game")]
288 IfGame,
289 #[serde(rename = "else")]
290 Else,
291}
292
293#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
294pub enum Target {
295 Selection,
296 Default,
297 Killer,
298 Damager,
299 Victim,
300 AllPlayers,
301 Shooter,
302 Projectile,
303 AllEntities,
304 AllMobs,
305 LastEntity,
306}
307
308#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
309#[serde(rename_all = "camelCase")]
310enum JSONBlockId {
311 Block,
312 Bracket,
313}
314
315#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
316#[serde(rename_all = "camelCase")]
317pub enum VariableScope {
318 #[serde(rename = "unsaved")]
319 Game,
320 Saved,
321 Local,
322 Line,
323}
324
325#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
326#[serde(rename_all = "camelCase")]
327pub enum ParamKind {
328 #[serde(rename = "any")]
329 Any,
330 #[serde(rename = "dict")]
331 Dictionary,
332 #[serde(rename = "item")]
333 Item,
334 #[serde(rename = "vec")]
335 Vector,
336 #[serde(rename = "num")]
337 Number,
338 #[serde(rename = "part")]
339 Particle,
340 #[serde(rename = "pot")]
341 Potion,
342 #[serde(rename = "txt")]
343 String,
344 #[serde(rename = "loc")]
345 Location,
346 #[serde(rename = "comp")]
347 Text,
348 #[serde(rename = "list")]
349 List,
350 #[serde(rename = "snd")]
351 Sound,
352 #[serde(rename = "var")]
353 Variable,
354}
355
356#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
357#[serde(rename_all = "camelCase")]
358pub enum BracketDirection {
359 Open,
360 Close,
361}
362
363#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
364pub enum BracketKind {
365 #[serde(rename = "norm")]
366 Normal,
367 #[serde(rename = "repeat")]
368 Repeat,
369}
370
371impl Args {
376 pub fn with(values: Vec<Item>) -> Self {
377 Args(values.into_iter().enumerate().collect())
378 }
379
380 pub fn with_tags(args: Vec<Item>, tags: Vec<Item>) -> Self {
381 let tags_begin = 27 - tags.len();
382
383 Args(
384 (0..=27)
385 .zip(args)
386 .chain((tags_begin..=27).zip(tags))
387 .collect(),
388 )
389 }
390}
391
392impl From<JSONBlock> for Block {
393 fn from(json: JSONBlock) -> Self {
394 match json {
395 JSONBlock::CodeBlock {
396 block,
397 args,
398 action,
399 data,
400 attribute,
401 sub_action,
402 target,
403 } => match block {
404 CodeBlock::PlayerEvent => Block::PlayerEvent {
405 action: action.expect("action should be defined"),
406 ls_cancel: attribute.is_some(),
407 },
408 CodeBlock::EntityEvent => Block::EntityEvent {
409 action: action.expect("action should be defined"),
410 ls_cancel: attribute.is_some(),
411 },
412 CodeBlock::Function => Block::Function {
413 args: args.expect("args should be defined"),
414 name: data.expect("data should be defined"),
415 },
416 CodeBlock::Process => Block::Process {
417 args: args.expect("args should be defined"),
418 name: data.expect("data should be defined"),
419 },
420 CodeBlock::PlayerAction => Block::PlayerAction {
421 args: args.expect("args should be defined"),
422 action: action.expect("action should be defined"),
423 target,
424 },
425 CodeBlock::IfPlayer => Block::IfPlayer {
426 args: args.expect("args should be defined"),
427 action: action.expect("action should be defined"),
428 target,
429 is_negated: attribute.is_some(),
430 },
431 CodeBlock::StartProcess => Block::StartProcess {
432 args: args.expect("args should be defined"),
433 proc: data.expect("data should be defined"),
434 },
435 CodeBlock::CallFunction => Block::CallFunction {
436 args: args.expect("args should be defined"),
437 func: data.expect("data should be defined"),
438 },
439 CodeBlock::Control => Block::Control {
440 args: args.expect("args should be defined"),
441 action: action.expect("action should be defined"),
442 },
443 CodeBlock::SetVariable => Block::SetVariable {
444 args: args.expect("args should be defined"),
445 action: action.expect("action should be defined"),
446 },
447 CodeBlock::IfEntity => Block::IfEntity {
448 args: args.expect("args should be defined"),
449 action: action.expect("action should be defined"),
450 target,
451 is_negated: attribute.is_some(),
452 },
453 CodeBlock::EntityAction => Block::EntityAction {
454 args: args.expect("args should be defined"),
455 action: action.expect("action should be defined"),
456 target,
457 },
458 CodeBlock::IfVariable => Block::IfVariable {
459 args: args.expect("args should be defined"),
460 action: action.expect("action should be defined"),
461 is_negated: attribute.is_some(),
462 },
463 CodeBlock::SelectObject => Block::SelectObject {
464 args: args.expect("args should be defined"),
465 action: action.expect("action should be defined"),
466 sub_action,
467 is_negated: attribute.is_some(),
468 },
469 CodeBlock::GameAction => Block::GameAction {
470 args: args.expect("args should be defined"),
471 action: action.expect("action should be defined"),
472 },
473 CodeBlock::Repeat => Block::Repeat {
474 args: args.expect("args should be defined"),
475 action: action.expect("action should be defined"),
476 sub_action,
477 is_negated: attribute.is_some(),
478 },
479 CodeBlock::IfGame => Block::IfGame {
480 args: args.expect("args should be defined"),
481 action: action.expect("action should be defined"),
482 is_negated: attribute.is_some(),
483 },
484 CodeBlock::Else => Block::Else,
485 },
486 JSONBlock::Bracket { direction, kind } => Block::Bracket { direction, kind },
487 }
488 }
489}
490
491impl From<Block> for JSONBlock {
492 fn from(block: Block) -> Self {
493 match block {
494 Block::PlayerEvent { action, ls_cancel } => JSONBlock::CodeBlock {
495 block: CodeBlock::PlayerEvent,
496 args: None,
497 action: Some(action),
498 data: None,
499 attribute: ls_cancel.then(|| "LS-CANCEL".to_string()),
500 sub_action: None,
501 target: None,
502 },
503 Block::EntityEvent { action, ls_cancel } => JSONBlock::CodeBlock {
504 block: CodeBlock::EntityEvent,
505 args: None,
506 action: Some(action),
507 data: None,
508 attribute: ls_cancel.then(|| "LS-CANCEL".to_string()),
509 sub_action: None,
510 target: None,
511 },
512 Block::Function { args, name } => JSONBlock::CodeBlock {
513 block: CodeBlock::Function,
514 args: Some(args),
515 action: None,
516 data: Some(name),
517 attribute: None,
518 sub_action: None,
519 target: None,
520 },
521 Block::Process { args, name } => JSONBlock::CodeBlock {
522 block: CodeBlock::Process,
523 args: Some(args),
524 action: None,
525 data: Some(name),
526 attribute: None,
527 sub_action: None,
528 target: None,
529 },
530 Block::PlayerAction {
531 args,
532 action,
533 target,
534 } => JSONBlock::CodeBlock {
535 block: CodeBlock::PlayerAction,
536 args: Some(args),
537 action: Some(action),
538 data: None,
539 attribute: None,
540 sub_action: None,
541 target,
542 },
543 Block::IfPlayer {
544 args,
545 action,
546 target,
547 is_negated,
548 } => JSONBlock::CodeBlock {
549 block: CodeBlock::IfPlayer,
550 args: Some(args),
551 action: Some(action),
552 data: None,
553 attribute: is_negated.then(|| "NOT".to_string()),
554 sub_action: None,
555 target,
556 },
557 Block::StartProcess { args, proc } => JSONBlock::CodeBlock {
558 block: CodeBlock::StartProcess,
559 args: Some(args),
560 action: None,
561 data: Some(proc),
562 attribute: None,
563 sub_action: None,
564 target: None,
565 },
566 Block::CallFunction { args, func } => JSONBlock::CodeBlock {
567 block: CodeBlock::CallFunction,
568 args: Some(args),
569 action: None,
570 data: Some(func),
571 attribute: None,
572 sub_action: None,
573 target: None,
574 },
575 Block::Control { args, action } => JSONBlock::CodeBlock {
576 block: CodeBlock::Control,
577 args: Some(args),
578 action: Some(action),
579 data: None,
580 attribute: None,
581 sub_action: None,
582 target: None,
583 },
584 Block::SetVariable { args, action } => JSONBlock::CodeBlock {
585 block: CodeBlock::SetVariable,
586 args: Some(args),
587 action: Some(action),
588 data: None,
589 attribute: None,
590 sub_action: None,
591 target: None,
592 },
593 Block::IfEntity {
594 args,
595 action,
596 target,
597 is_negated,
598 } => JSONBlock::CodeBlock {
599 block: CodeBlock::IfEntity,
600 args: Some(args),
601 action: Some(action),
602 data: None,
603 attribute: is_negated.then(|| "NOT".to_string()),
604 sub_action: None,
605 target,
606 },
607 Block::EntityAction {
608 args,
609 action,
610 target,
611 } => JSONBlock::CodeBlock {
612 block: CodeBlock::EntityAction,
613 args: Some(args),
614 action: Some(action),
615 data: None,
616 attribute: None,
617 sub_action: None,
618 target,
619 },
620 Block::IfVariable {
621 args,
622 action,
623 is_negated,
624 } => JSONBlock::CodeBlock {
625 block: CodeBlock::IfVariable,
626 args: Some(args),
627 action: Some(action),
628 data: None,
629 attribute: is_negated.then(|| "NOT".to_string()),
630 sub_action: None,
631 target: None,
632 },
633 Block::SelectObject {
634 args,
635 action,
636 sub_action,
637 is_negated,
638 } => JSONBlock::CodeBlock {
639 block: CodeBlock::SelectObject,
640 args: Some(args),
641 action: Some(action),
642 data: None,
643 attribute: is_negated.then(|| "NOT".to_string()),
644 sub_action,
645 target: None,
646 },
647 Block::GameAction { args, action } => JSONBlock::CodeBlock {
648 block: CodeBlock::GameAction,
649 args: Some(args),
650 action: Some(action),
651 data: None,
652 attribute: None,
653 sub_action: None,
654 target: None,
655 },
656 Block::Repeat {
657 args,
658 action,
659 sub_action,
660 is_negated,
661 } => JSONBlock::CodeBlock {
662 block: CodeBlock::Repeat,
663 args: Some(args),
664 action: Some(action),
665 data: None,
666 attribute: is_negated.then(|| "NOT".to_string()),
667 sub_action,
668 target: None,
669 },
670 Block::IfGame {
671 args,
672 action,
673 is_negated,
674 } => JSONBlock::CodeBlock {
675 block: CodeBlock::IfGame,
676 args: Some(args),
677 action: Some(action),
678 data: None,
679 attribute: is_negated.then(|| "NOT".to_string()),
680 sub_action: None,
681 target: None,
682 },
683 Block::Else => JSONBlock::CodeBlock {
684 block: CodeBlock::Else,
685 args: None,
686 action: None,
687 data: None,
688 attribute: None,
689 sub_action: None,
690 target: None,
691 },
692 Block::Bracket {
693 direction: bracket_direction,
694 kind: bracket_type,
695 } => JSONBlock::Bracket {
696 direction: bracket_direction,
697 kind: bracket_type,
698 },
699 }
700 }
701}
702
703impl From<JSONArgs> for Args {
704 fn from(json: JSONArgs) -> Self {
705 Args(
706 json.items
707 .into_iter()
708 .map(|item| (item.slot, item.item))
709 .collect(),
710 )
711 }
712}
713
714impl From<Args> for JSONArgs {
715 fn from(args: Args) -> Self {
716 JSONArgs {
717 items: args
718 .0
719 .into_iter()
720 .map(|(slot, item)| SlottedItem { item, slot })
721 .collect(),
722 }
723 }
724}
725
726impl Template {
727 pub fn new(blocks: Vec<Block>) -> Self {
728 Template { blocks }
729 }
730
731 pub fn encode(&self) -> Option<String> {
732 let json = serde_json::to_string(self).ok()?;
734
735 let mut gz_encoder = GzEncoder::new(Vec::new(), flate2::Compression::default());
736 gz_encoder.write_all(json.as_bytes()).ok()?;
737
738 let gzipped = gz_encoder.finish().ok()?;
739 let base64 = BASE64_STANDARD.encode(gzipped);
740
741 Some(base64)
742 }
743
744 pub fn decode(data: &str) -> Option<Self> {
745 let gzipped = BASE64_STANDARD.decode(data).ok()?;
747
748 let mut gz_decoder = GzDecoder::new(&gzipped[..]);
749 let mut json = String::new();
750 gz_decoder.read_to_string(&mut json).ok()?;
751
752 let template: Template = serde_json::from_str(&json).ok()?;
753
754 Some(template)
755 }
756
757 pub fn get_name(&self) -> Option<String> {
758 if let Some(Block::Function { name, .. }) = self.blocks.first() {
759 Some(name.clone())
760 } else {
761 None
762 }
763 }
764
765 pub fn add_block(&mut self, block: Block) -> &mut Self {
766 self.blocks.push(block);
767 self
768 }
769
770 pub fn set_var(&mut self, action: impl Into<String>, args: Args) -> &mut Self {
771 self.add_block(Block::SetVariable {
772 args,
773 action: action.into(),
774 });
775 self
776 }
777
778 pub fn set_var_bitwise(
779 &mut self,
780 bitwise_op: impl Into<String>,
781 result: Item,
782 a: Item,
783 b: Item,
784 ) -> &mut Self {
785 self.set_var(
786 "Bitwise",
787 Args::with_tags(
788 vec![result, a, b],
789 vec![
790 Item::Tag {
791 option: "64-bit".to_string(),
792 tag: "Bit Precision".to_string(),
793 action: "Bitwise".to_string(),
794 block: CodeBlock::SetVariable,
795 },
796 Item::Tag {
797 option: bitwise_op.into(),
798 tag: "Operator".to_string(),
799 action: "Bitwise".to_string(),
800 block: CodeBlock::SetVariable,
801 },
802 ],
803 ),
804 )
805 }
806
807 pub fn if_var(&mut self, action: impl Into<String>, args: Args) -> &mut Self {
808 self.add_block(Block::IfVariable {
809 args,
810 action: action.into(),
811 is_negated: false,
812 });
813 self
814 }
815
816 pub fn else_block(&mut self) -> &mut Self {
817 self.add_block(Block::Else);
818 self
819 }
820
821 pub fn repeat(&mut self, action: impl Into<String>, args: Args) -> &mut Self {
822 self.add_block(Block::Repeat {
823 args,
824 action: action.into(),
825 sub_action: None,
826 is_negated: false,
827 });
828 self
829 }
830
831 pub fn repeat_subaction(
832 &mut self,
833 action: impl Into<String>,
834 sub_action: impl Into<String>,
835 args: Args,
836 ) -> &mut Self {
837 self.add_block(Block::Repeat {
838 args,
839 action: action.into(),
840 sub_action: Some(sub_action.into()),
841 is_negated: false,
842 });
843 self
844 }
845
846 pub fn open_bracket(&mut self) -> &mut Self {
847 self.add_block(Block::Bracket {
848 direction: BracketDirection::Open,
849 kind: BracketKind::Normal,
850 });
851 self
852 }
853
854 pub fn close_bracket(&mut self) -> &mut Self {
855 self.add_block(Block::Bracket {
856 direction: BracketDirection::Close,
857 kind: BracketKind::Normal,
858 });
859 self
860 }
861
862 pub fn open_bracket_repeat(&mut self) -> &mut Self {
863 self.add_block(Block::Bracket {
864 direction: BracketDirection::Open,
865 kind: BracketKind::Repeat,
866 });
867 self
868 }
869
870 pub fn close_bracket_repeat(&mut self) -> &mut Self {
871 self.add_block(Block::Bracket {
872 direction: BracketDirection::Close,
873 kind: BracketKind::Repeat,
874 });
875 self
876 }
877
878 pub fn control(&mut self, action: impl ToString, args: Args) -> &mut Self {
879 self.add_block(Block::Control {
880 args,
881 action: action.to_string(),
882 });
883 self
884 }
885
886 pub fn call_function(&mut self, func: impl ToString, args: Args) -> &mut Self {
887 self.add_block(Block::CallFunction {
888 args,
889 func: func.to_string(),
890 });
891 self
892 }
893
894 pub fn print(&mut self, args: Args) -> &mut Self {
895 self.add_block(Block::PlayerAction {
896 args,
897 action: "SendMessage".to_string(),
898 target: Some(Target::AllPlayers),
899 });
900 self
901 }
902
903 pub fn start_function(name: impl ToString) -> Self {
904 Self {
905 blocks: vec![Block::Function {
906 args: Args::with_tags(
907 vec![],
908 vec![Item::Tag {
909 option: "False".to_string(),
910 tag: "Is Hidden".to_string(),
911 action: "dynamic".to_string(),
912 block: CodeBlock::Function,
913 }],
914 ),
915 name: name.to_string(),
916 }],
917 }
918 }
919
920 pub fn start_function_hidden(name: impl ToString) -> Self {
921 Self {
922 blocks: vec![Block::Function {
923 args: Args::with_tags(
924 vec![],
925 vec![Item::Tag {
926 option: "True".to_string(),
927 tag: "Is Hidden".to_string(),
928 action: "dynamic".to_string(),
929 block: CodeBlock::Function,
930 }],
931 ),
932 name: name.to_string(),
933 }],
934 }
935 }
936}
937
938impl Item {
939 pub fn num(name: impl ToString) -> Self {
940 Item::Number {
941 name: name.to_string(),
942 }
943 }
944
945 pub fn var(kind: impl Into<String>) -> Self {
946 Item::Variable {
947 name: kind.into(),
948 scope: VariableScope::Game,
949 }
950 }
951
952 pub fn string(name: impl Into<String>) -> Self {
953 Item::String { name: name.into() }
954 }
955}
956
957#[cfg(test)]
962mod tests {
963 use super::*;
964
965 #[test]
966 fn test_block_conversion() {
967 let block = Block::PlayerEvent {
968 action: "test".to_string(),
969 ls_cancel: true,
970 };
971 let json: JSONBlock = block.clone().into();
972 let block2: Block = json.into();
973 assert_eq!(block, block2);
974 }
975}