1use std::collections::HashMap;
6use std::sync::OnceLock;
7
8use crate::crc::type_id;
9
10#[derive(Debug, Clone, Copy)]
12pub enum Cond {
13 VersionAtLeast(u32),
15 VersionBelow(u32),
17 Flag(&'static str),
19}
20
21#[derive(Debug, Clone, Copy)]
23pub enum Op {
24 Bool(&'static str),
26 I8(&'static str),
28 I16(&'static str),
30 U16(&'static str),
32 I32(&'static str),
34 U32(&'static str),
36 Time(&'static str),
38 F32(&'static str),
40 Times(&'static str, usize),
42 F32s(&'static str, usize),
44 U32s(&'static str, usize),
46 I32sBy(&'static str, &'static str),
48 F32sBy(&'static str, &'static str),
50 Str(&'static str),
52 WStr(&'static str),
54 Ext(&'static str),
56 Ptr(&'static str),
58 List(&'static str, &'static [Op]),
60 Rest(&'static str, &'static [Op]),
62 Block(&'static str, u32, &'static [Op]),
64 Sub(&'static [Op]),
66 If(Cond, &'static [Op]),
68}
69
70use Cond::*;
71use Op::*;
72
73const WORK: Op = Block(
78 "work",
79 3,
80 &[
81 Ptr("entity"),
82 Ptr("component"),
83 I32("id"),
84 I32("cell"),
85 If(VersionAtLeast(1), &[I32("priority")]),
86 If(
87 VersionAtLeast(3),
88 &[Time("patience"), Time("patience_base"), I32("far_count")],
89 ),
90 ],
91);
92
93const AUTO_PICKUP_WORK: Op = Sub(&[WORK, Block("auto_pickup", 0, &[])]);
95
96const CITIZENS: Op = List("citizens", &[Ptr("citizen")]);
98
99const KEYED_CITIZENS: Op = List("keyed", &[I32("key"), Ptr("citizen")]);
101
102const BASE: Op = Block(
103 "base",
104 0,
105 &[Ptr("entity"), U32("flags"), Ext("description")],
106);
107const ACTION: Op = Block("action", 0, &[Bool("started")]);
109const CONTROLLER: Op = Block("controller", 0, &[]);
111const RESOURCE_PRODUCE: Op = Sub(&[BASE, CONTROLLER, Block("produce", 0, &[Bool("disabled")])]);
113
114const MESH_INSTANCE: Op = Sub(&[
117 List(
118 "animations",
119 &[
120 Ext("animation"),
121 I32("index"),
122 F32("speed"),
123 Time("time0"),
124 Time("time1"),
125 Time("time2"),
126 I32("flags"),
127 Bool("looping"),
128 ],
129 ),
130 Bool("animating"),
131 List("materials", &[Ext("material")]),
132]);
133
134const GOAL_INSTANCE: Op = Block("goal", 0, &[Ext("goal"), I32("value")]);
135
136static SCHEMAS: &[(&str, &[Op])] = &[
137 (
140 "AchievementInstance",
141 &[Block(
142 "instance",
143 1,
144 &[
145 Ext("achievement"),
146 Block("goals", 0, &[List("goals", &[Ptr("goal")])]),
147 ],
148 )],
149 ),
150 ("GoalInstance", &[GOAL_INSTANCE]),
152 (
154 "TimeGoalInstance",
155 &[GOAL_INSTANCE, Block("timer", 0, &[Time("time")])],
156 ),
157 ("FinishTutorialInstance", &[GOAL_INSTANCE, Ptr("tutorial")]),
159 (
161 "ComponentEntity",
162 &[Block(
163 "entity",
164 0,
165 &[
166 Ext("template"),
167 List("components", &[Ptr("component")]),
168 U32("flags"),
169 ],
170 )],
171 ),
172 (
174 "MapComponent",
175 &[
176 BASE,
177 Block(
178 "map",
179 0,
180 &[I16("x"), I16("y"), I16("w"), I16("h"), I32("rotation")],
181 ),
182 ],
183 ),
184 (
186 "ModelComponent",
187 &[
188 BASE,
189 Block(
190 "model",
191 0,
192 &[
193 I16("variant"),
194 I16("mesh"),
195 F32s("rotation", 4),
196 F32s("position", 3),
197 F32("yaw"),
198 MESH_INSTANCE,
199 Bool("picking"),
200 ],
201 ),
202 ],
203 ),
204 (
206 "NaturalResourceComponent",
207 &[
208 BASE,
209 Block(
210 "natural",
211 0,
212 &[F32("value_28"), Bool("flag_2c"), Bool("flag_2d")],
213 ),
214 ],
215 ),
216 (
218 "GrowthComponent",
219 &[
220 BASE,
221 Block(
222 "growth",
223 0,
224 &[
225 Ptr("natural"),
226 Ptr("model"),
227 F32("growth"),
228 F32("maximum"),
229 Time("timer"),
230 ],
231 ),
232 ],
233 ),
234 (
236 "SpawnComponent",
237 &[BASE, Block("spawn", 0, &[List("timers", &[Time("timer")])])],
238 ),
239 (
241 "AmbientEmitterComponent",
242 &[
243 BASE,
244 Block(
245 "emitter",
246 1,
247 &[
248 F32s("position", 3),
249 List("slots", &[Ext("effect"), U32("handle"), F32("volume")]),
250 ],
251 ),
252 ],
253 ),
254 (
256 "AmbientEnvironmentComponent",
257 &[
258 BASE,
259 Block(
260 "environment",
261 0,
262 &[
263 F32s("position", 3),
264 I32("value_28"),
265 I32("value_2c"),
266 I32("value_30"),
267 I32("value_34"),
268 ],
269 ),
270 ],
271 ),
272 (
274 "AnimalComponent",
275 &[
276 BASE,
277 Block(
278 "animal",
279 1,
280 &[
281 Time("timer"),
282 I32("home"),
283 F32("age"),
284 F32("life"),
285 Bool("male"),
286 Time("breed"),
287 I16("x"),
288 I16("y"),
289 I16("w"),
290 I16("h"),
291 If(VersionBelow(1), &[Ptr("population")]),
292 ],
293 ),
294 ],
295 ),
296 (
298 "HerdComponent",
299 &[
300 BASE,
301 Block(
302 "herd",
303 0,
304 &[
305 List("animals", &[Ptr("animal")]),
306 Bool("moving"),
307 Time("rest"),
308 Time("regrow"),
309 I32("target"),
310 Ptr("population"),
311 I32("index"),
312 ],
313 ),
314 ],
315 ),
316 (
318 "PopulationComponent",
319 &[
320 BASE,
321 Block(
322 "population",
323 0,
324 &[
325 Ext("description"),
326 I32("width"),
327 I32("herd_width"),
328 List("cells", &[I32("a"), I32("c"), I32("b")]),
329 List("herds", &[Ptr("herd")]),
330 Time("clock"),
331 I32("cursor"),
332 I32("value_6c"),
333 ],
334 ),
335 ],
336 ),
337 (
339 "WildAnimalComponent",
340 &[BASE, Block("wild", 0, &[Ptr("herd")])],
341 ),
342 ("ZoneComponent", &[BASE, Block("zone", 1, &[])]),
344 (
346 "DecalComponent",
347 &[
348 BASE,
349 Block(
350 "decal",
351 1,
352 &[F32("alpha"), If(VersionAtLeast(1), &[I32("material")])],
353 ),
354 ],
355 ),
356 (
358 "WorkComponent",
359 &[
360 BASE,
361 Block(
362 "work",
363 3,
364 &[
365 I32("work_id"),
366 If(VersionBelow(3), &[Bool("old")]),
367 If(VersionAtLeast(1), &[I32("most_workers")]),
368 If(VersionAtLeast(2), &[I32("kept_priority")]),
369 ],
370 ),
371 ],
372 ),
373 (
375 "StateMachineComponent",
376 &[
377 BASE,
378 Block(
379 "machine",
380 0,
381 &[I8("state"), I8("index"), I8("pending"), I8("transition")],
382 ),
383 ],
384 ),
385 (
387 "ClearAreaComponent",
388 &[BASE, CONTROLLER, Block("clear", 0, &[I32("total")])],
389 ),
390 (
392 "DestroyComponent",
393 &[
394 BASE,
395 CONTROLLER,
396 Block(
397 "destroy",
398 0,
399 &[
400 I32("count"),
401 I32("target"),
402 Bool("refund"),
403 Ext("rebuild"),
404 Bool("damaged"),
405 Time("time"),
406 ],
407 ),
408 ],
409 ),
410 (
412 "PickingComponent",
413 &[BASE, Block("picking", 0, &[Ptr("entity")])],
414 ),
415 (
417 "StatusIconComponent",
418 &[BASE, Block("status", 0, &[U32("bits")])],
419 ),
420 ("HighlightComponent", &[BASE, Block("highlight", 0, &[])]),
422 (
424 "StorageComponent",
425 &[
426 BASE,
427 Block(
428 "storage",
429 1,
430 &[
431 List("items", &[I32("count"), I32("reserved"), Ext("material")]),
432 I32("capacity"),
433 U32("accepts"),
434 ],
435 ),
436 ],
437 ),
438 (
440 "StorageLocationComponent",
441 &[BASE, CONTROLLER, Block("location", 0, &[])],
442 ),
443 (
445 "InteractComponent",
446 &[
447 BASE,
448 Block("interact", 0, &[List("children", &[Ptr("child")])]),
449 ],
450 ),
451 (
453 "FireComponent",
454 &[
455 BASE,
456 Block(
457 "fire",
458 1,
459 &[
460 Time("burn"),
461 Time("spread"),
462 I32("buckets"),
463 If(VersionAtLeast(1), &[I32("value_2c")]),
464 ],
465 ),
466 ],
467 ),
468 (
470 "ParticleComponent",
471 &[BASE, Block("particle", 0, &[List("slots", &[Bool("on")])])],
472 ),
473 (
475 "AbsorbResourceComponent",
476 &[BASE, CONTROLLER, Block("absorb", 0, &[])],
477 ),
478 (
481 "BuildComponent",
482 &[
483 BASE,
484 CONTROLLER,
485 Block(
486 "build",
487 1,
488 &[
489 I32("progress"),
490 I32("stages"),
491 Bool("flag_50"),
492 List("models", &[Ptr("model")]),
493 List("pickings", &[Ptr("picking")]),
494 ],
495 ),
496 ],
497 ),
498 ("StorageDisplayComponent", &[BASE, Block("display", 0, &[])]),
500 (
502 "UIComponent",
503 &[BASE, Block("ui", 0, &[List("pages", &[I8("page")])])],
504 ),
505 (
507 "WorkPlaceComponent",
508 &[
509 BASE,
510 CONTROLLER,
511 Block(
512 "workplace",
513 1,
514 &[
515 List("workers", &[Ptr("worker")]),
516 Bool("enabled"),
517 If(VersionAtLeast(1), &[Ext("profession")]),
518 ],
519 ),
520 ],
521 ),
522 (
524 "CharacterComponent",
525 &[
526 BASE,
527 Block(
528 "character",
529 0,
530 &[
531 F32s("direction", 3),
532 F32("target_heading"),
533 F32("speed"),
534 Bool("flag_28"),
535 I32("stack_top"),
536 List("stack", &[Ptr("action")]),
537 Ptr("on_empty"),
538 Ptr("model"),
539 Ptr("picking"),
540 ],
541 ),
542 ],
543 ),
544 (
546 "MoveTo",
547 &[
548 ACTION,
549 Block(
550 "move",
551 1,
552 &[
553 Ptr("owner"),
554 Ext("animations"),
555 I32("path_len"),
556 I32("path_index"),
557 I32sBy("path", "path_len"),
558 F32s("target", 3),
559 U32("flags"),
560 I32("walk_clip"),
561 I32("stand_clip"),
562 If(VersionBelow(1), &[Ptr("population")]),
563 If(VersionAtLeast(1), &[I32("cost_mode")]),
564 Bool("following"),
565 F32("speed_factor"),
566 ],
567 ),
568 ],
569 ),
570 (
572 "PerformWork",
573 &[
574 ACTION,
575 Block(
576 "perform",
577 0,
578 &[
579 Ptr("owner"),
580 F32("facing"),
581 I32("clip"),
582 Time("duration"),
583 Time("elapsed"),
584 Bool("use_tool"),
585 I32("value_44"),
586 ],
587 ),
588 ],
589 ),
590 (
592 "PlayAnimation",
593 &[
594 ACTION,
595 Block(
596 "play",
597 0,
598 &[Ptr("owner"), I32("clip"), Time("duration"), Time("elapsed")],
599 ),
600 ],
601 ),
602 (
604 "SendComponentMessage",
605 &[
606 ACTION,
607 Block(
608 "send",
609 0,
610 &[
611 Ptr("entity"),
612 Ptr("argument"),
613 U32("component_type"),
614 I32("message"),
615 U32("value_38"),
616 U32("value_3c"),
617 ],
618 ),
619 ],
620 ),
621 (
623 "ShowTool",
624 &[ACTION, Block("show", 0, &[Ptr("owner"), I32("tool")])],
625 ),
626 (
629 "CompleteRoad",
630 &[
631 ACTION,
632 Block("road", 0, &[I32("cell"), I8("built"), I32("ty")]),
633 ],
634 ),
635 (
636 "CompleteTask",
637 &[
638 ACTION,
639 Block(
640 "complete",
641 1,
642 &[
643 Ptr("citizen"),
644 Ptr("work_entity"),
645 I32("param"),
646 If(VersionAtLeast(1), &[I32("work")]),
647 ],
648 ),
649 ],
650 ),
651 (
653 "ConsumeRawMaterial",
654 &[
655 ACTION,
656 Block(
657 "consume",
658 2,
659 &[
660 Ptr("entity"),
661 If(VersionBelow(1), &[Ext("material"), I32("count")]),
662 If(
663 VersionAtLeast(1),
664 &[List("items", &[Ext("material"), I32("count")])],
665 ),
666 If(VersionAtLeast(2), &[Bool("flag_38")]),
667 ],
668 ),
669 ],
670 ),
671 (
673 "CitizenComponent",
674 &[
675 BASE,
676 Block(
677 "citizen",
678 3,
679 &[
680 Str("name"),
681 I32("sex"),
682 F32("age"),
683 F32("lifespan"),
684 F32("hunger"),
685 F32("cold"),
686 F32("reproduction"),
687 F32("health"),
688 U32("health_events"),
689 Time("health_timer"),
690 Time("disease_clock"),
691 Ext("disease"),
692 Time("disease_left"),
693 Time("spread"),
694 F32("happiness"),
695 U32("flags"),
696 Time("happiness_timer"),
697 I32("food_count"),
698 I32sBy("foods", "food_count"),
699 Ext("profession"),
700 I32("tool_uses"),
701 I32("value_1d8"),
702 If(VersionAtLeast(1), &[I32("tool_status")]),
703 I32("clothing"),
704 Time("clothing_remaining"),
705 If(VersionAtLeast(2), &[I32("clothing_status")]),
706 U32("supply_activity"),
707 I32("job"),
708 Ptr("home"),
709 I32("home_slot"),
710 Ptr("spouse"),
711 Ptr("guardian"),
712 Ptr("ring_head"),
713 Ptr("ring_next"),
714 Ptr("ring_prev"),
715 Ptr("workplace"),
716 Ext("accident"),
717 Ptr("school"),
718 I32("education"),
719 If(VersionAtLeast(3), &[Time("school_time")]),
720 Bool("schooled"),
721 Ptr("doctor"),
722 Ptr("priest"),
723 Ptr("character"),
724 Ptr("status_icon"),
725 Ext("citizen_description"),
726 I32("appearance"),
727 ],
728 ),
729 ],
730 ),
731 (
733 "CitizenAIComponent",
734 &[
735 BASE,
736 Block(
737 "ai",
738 5,
739 &[
740 Ptr("citizen"),
741 I32("queued_activity"),
742 If(VersionBelow(5), &[Times("cooldowns", 14)]),
743 If(VersionAtLeast(5), &[Times("cooldowns", 16)]),
744 If(
745 VersionAtLeast(1),
746 &[Bool("urgent_eating"), Bool("urgent_warming")],
747 ),
748 If(VersionAtLeast(2), &[I32("current_activity")]),
749 If(VersionAtLeast(3), &[Time("since_loiter")]),
750 If(VersionAtLeast(4), &[Time("carrying_time")]),
751 ],
752 ),
753 ],
754 ),
755 (
757 "ResidenceComponent",
758 &[
759 BASE,
760 CONTROLLER,
761 Block(
762 "residence",
763 0,
764 &[
765 List("families", &[List("members", &[Ptr("citizen")])]),
766 Time("fuel"),
767 ],
768 ),
769 ],
770 ),
771 (
773 "LivestockComponent",
774 &[
775 BASE,
776 Block("livestock", 0, &[Ptr("pasture"), Time("timer")]),
777 ],
778 ),
779 (
781 "PastureComponent",
782 &[
783 BASE,
784 CONTROLLER,
785 Block(
786 "pasture",
787 0,
788 &[
789 Ext("kind"),
790 List("animals", &[Ptr("animal")]),
791 I32("value_40"),
792 I32("value_44"),
793 ],
794 ),
795 ],
796 ),
797 (
799 "TrackerComponent",
800 &[
801 BASE,
802 Block(
803 "tracker",
804 0,
805 &[
806 List("this_year", &[Ext("material"), I32("amount")]),
807 List("last_year", &[Ext("material"), I32("amount")]),
808 I32("year"),
809 ],
810 ),
811 ],
812 ),
813 (
815 "DiseaseComponent",
816 &[
817 BASE,
818 Block(
819 "disease",
820 1,
821 &[
822 Ext("disease"),
823 I32("value_48"),
824 Time("time_28"),
825 Time("time_30"),
826 Time("time_38"),
827 Time("time_40"),
828 ],
829 ),
830 ],
831 ),
832 (
834 "HappinessComponent",
835 &[
836 BASE,
837 CONTROLLER,
838 Block("happiness", 0, &[Bool("registered")]),
839 ],
840 ),
841 (
844 "HuntComponent",
845 &[RESOURCE_PRODUCE, Block("hunt", 0, &[Time("time_50")])],
846 ),
847 (
849 "CemeteryComponent",
850 &[
851 BASE,
852 CONTROLLER,
853 Block("cemetery", 0, &[I32("count"), I32sBy("graves", "count")]),
854 ],
855 ),
856 (
857 "BoatComponent",
858 &[
859 BASE,
860 CONTROLLER,
861 Block(
862 "boat",
863 0,
864 &[
865 Ptr("post"),
866 F32s("dock", 3),
867 F32("dock_yaw"),
868 Ptr("model"),
869 Ptr("character"),
870 F32s("target", 3),
871 I32("index"),
872 List("route", &[F32s("point", 3)]),
873 ],
874 ),
875 ],
876 ),
877 (
879 "CropFieldComponent",
880 &[
881 BASE,
882 CONTROLLER,
883 Block(
884 "field",
885 0,
886 &[
887 Bool("flag_48"),
888 Bool("flag_49"),
889 Ext("crop"),
890 List(
891 "cells",
892 &[
893 I32("x"),
894 I32("y"),
895 Ptr("entity"),
896 Ptr("resource"),
897 Bool("flag"),
898 ],
899 ),
900 ],
901 ),
902 ],
903 ),
904 (
906 "OrchardComponent",
907 &[
908 BASE,
909 CONTROLLER,
910 Block(
911 "orchard",
912 0,
913 &[
914 Bool("flag_48"),
915 I32("value_4c"),
916 List(
917 "trees",
918 &[
919 I32("x"),
920 I32("y"),
921 Ptr("entity"),
922 Ptr("resource"),
923 Ptr("fruit"),
924 Bool("flag"),
925 ],
926 ),
927 Ext("tree"),
928 ],
929 ),
930 ],
931 ),
932 (
934 "TradeComponent",
935 &[
936 BASE,
937 CONTROLLER,
938 Block(
939 "trade",
940 0,
941 &[
942 List("stock", &[Ext("material"), I32("value_4"), I32("value_8")]),
943 I32("value_2c"),
944 List("requests", &[Ext("material"), I32("count")]),
946 List(
947 "merchants",
948 &[
949 Str("name"),
950 I32("count"),
951 I32sBy("materials", "count"),
952 I32("value_18"),
953 ],
954 ),
955 List("orders", &[U32("value_0"), I32("value_8"), I32("value_4")]),
956 I32("value_28"),
957 Time("time_78"),
958 Bool("flag_71"),
959 Ptr("merchant"),
960 Bool("flag_70"),
961 ],
962 ),
963 ],
964 ),
965 (
967 "FishingComponent",
968 &[
969 RESOURCE_PRODUCE,
970 Block("fishing", 0, &[Bool("flag_50"), F32("value_54")]),
971 ],
972 ),
973 (
975 "ForesterComponent",
976 &[
977 RESOURCE_PRODUCE,
978 Block("forester", 0, &[Bool("plant"), Bool("cut")]),
979 ],
980 ),
981 (
983 "HerbalistComponent",
984 &[RESOURCE_PRODUCE, Block("herbalist", 0, &[])],
985 ),
986 (
989 "ForesterWork",
990 &[
991 AUTO_PICKUP_WORK,
992 Block("forester", 0, &[CITIZENS, I32("value_58")]),
993 ],
994 ),
995 (
997 "HerbalistWork",
998 &[
999 AUTO_PICKUP_WORK,
1000 Block("herbalist", 0, &[CITIZENS, I32("value_58")]),
1001 ],
1002 ),
1003 (
1005 "HuntWork",
1006 &[AUTO_PICKUP_WORK, Block("hunt", 0, &[CITIZENS])],
1007 ),
1008 (
1010 "PastureWork",
1011 &[AUTO_PICKUP_WORK, Block("pasture", 0, &[CITIZENS])],
1012 ),
1013 (
1015 "BuildRoad",
1016 &[
1017 WORK,
1018 Block(
1019 "road",
1020 1,
1021 &[
1022 U32("type"),
1023 I32("cell"),
1024 Ptr("builder"),
1025 Ext("material"),
1026 If(
1027 VersionAtLeast(1),
1028 &[I32("old_kind"), I32("old_built"), I32("old_type")],
1029 ),
1030 ],
1031 ),
1032 ],
1033 ),
1034 (
1036 "BuildStructure",
1037 &[
1038 WORK,
1039 Block(
1040 "build",
1041 0,
1042 &[
1043 Bool("destroy"),
1044 CITIZENS,
1045 List("occupied", &[Bool("done")]),
1046 I32("cursor"),
1047 ],
1048 ),
1049 ],
1050 ),
1051 (
1053 "ClearArea",
1054 &[
1055 WORK,
1056 Block(
1057 "clear",
1058 1,
1059 &[
1060 List("assigned", &[Ptr("entity"), Ptr("citizen")]),
1061 Ext("filter"),
1062 Bool("clear_resources"),
1063 If(
1064 VersionAtLeast(1),
1065 &[KEYED_CITIZENS, Bool("clear_cells"), Bool("clear_piles")],
1066 ),
1067 ],
1068 ),
1069 ],
1070 ),
1071 (
1073 "ClearStorage",
1074 &[WORK, Block("clear_storage", 0, &[CITIZENS])],
1075 ),
1076 (
1078 "ConsumeProduce",
1079 &[
1080 WORK,
1081 Block(
1082 "consume",
1083 0,
1084 &[
1085 CITIZENS,
1086 List("points", &[I32("state")]),
1087 Bool("full"),
1088 I32("cycles"),
1089 U32("type_50"),
1090 I32("value_54"),
1091 ],
1092 ),
1093 ],
1094 ),
1095 (
1097 "FireWork",
1098 &[WORK, Block("fire", 0, &[CITIZENS, I32("value_48")])],
1099 ),
1100 (
1102 "FishingWork",
1103 &[
1104 WORK,
1105 Block(
1106 "fishing",
1107 0,
1108 &[CITIZENS, List("values", &[I32("value")]), I32("value_68")],
1109 ),
1110 ],
1111 ),
1112 (
1114 "GatherRawMaterials",
1115 &[
1116 WORK,
1117 Block(
1118 "gather",
1119 0,
1120 &[
1121 List("stored", &[I32("value")]),
1122 List("transit", &[I32("value")]),
1123 CITIZENS,
1124 ],
1125 ),
1126 ],
1127 ),
1128 (
1130 "LocationWork",
1131 &[WORK, Block("location", 0, &[Ptr("citizen")])],
1132 ),
1133 (
1135 "MarketWork",
1136 &[
1137 WORK,
1138 Block(
1139 "market",
1140 1,
1141 &[
1142 If(VersionBelow(1), &[CITIZENS]),
1143 If(
1144 VersionAtLeast(1),
1145 &[List(
1146 "orders",
1147 &[
1148 Ptr("citizen"),
1149 List("items", &[Ext("description"), I32("count")]),
1150 ],
1151 )],
1152 ),
1153 ],
1154 ),
1155 ],
1156 ),
1157 (
1159 "Pickup",
1160 &[
1161 WORK,
1162 Block(
1163 "pickup",
1164 2,
1165 &[
1166 I32("state"),
1167 Ptr("claimer"),
1168 If(VersionAtLeast(1), &[Ext("profession")]),
1169 ],
1170 ),
1171 ],
1172 ),
1173 (
1175 "PlantArea",
1176 &[
1177 WORK,
1178 Block(
1179 "plant",
1180 0,
1181 &[
1182 KEYED_CITIZENS,
1183 List("cells", &[Bool("done")]),
1184 Ext("description"),
1185 ],
1186 ),
1187 ],
1188 ),
1189 (
1191 "PlantOrchard",
1192 &[
1193 WORK,
1194 Block(
1195 "plant",
1196 0,
1197 &[
1198 KEYED_CITIZENS,
1199 List("cells", &[Bool("done")]),
1200 Ext("description"),
1201 ],
1202 ),
1203 ],
1204 ),
1205 (
1207 "TendArea",
1208 &[WORK, Block("tend", 0, &[CITIZENS, I32("value_58")])],
1209 ),
1210 (
1212 "TradeWork",
1213 &[
1214 WORK,
1215 Block(
1216 "trade",
1217 0,
1218 &[List(
1219 "orders",
1220 &[Ptr("citizen"), I32("count"), Ext("description")],
1221 )],
1222 ),
1223 ],
1224 ),
1225 (
1227 "MarketLocationComponent",
1228 &[BASE, CONTROLLER, Block("market_location", 0, &[])],
1229 ),
1230 (
1232 "MarketComponent",
1233 &[RESOURCE_PRODUCE, Block("market", 0, &[])],
1234 ),
1235 (
1237 "ImmigrationComponent",
1238 &[
1239 BASE,
1240 CONTROLLER,
1241 Block(
1242 "immigration",
1243 0,
1244 &[
1245 I32("value_50"),
1246 List("nomads", &[Ptr("entity")]),
1247 Time("time_48"),
1248 ],
1249 ),
1250 ],
1251 ),
1252 (
1254 "HarvestArea",
1255 &[
1256 AUTO_PICKUP_WORK,
1257 Block(
1258 "harvest",
1259 0,
1260 &[KEYED_CITIZENS, List("cells", &[Bool("done")])],
1261 ),
1262 ],
1263 ),
1264 (
1266 "HarvestOrchard",
1267 &[
1268 AUTO_PICKUP_WORK,
1269 Block(
1270 "harvest",
1271 0,
1272 &[KEYED_CITIZENS, List("cells", &[Bool("done")])],
1273 ),
1274 ],
1275 ),
1276 (
1278 "HuntAnimal",
1279 &[
1280 ACTION,
1281 Block("hunt", 0, &[Ptr("lodge"), Ptr("hunter"), Bool("done")]),
1282 ],
1283 ),
1284 (
1286 "RemoveAnimal",
1287 &[ACTION, Block("remove", 0, &[Ptr("entity")])],
1288 ),
1289 (
1291 "NomadComponent",
1292 &[
1293 BASE,
1294 CONTROLLER,
1295 Block(
1296 "nomad",
1297 0,
1298 &[
1299 Ptr("town_hall"),
1300 F32s("from", 3),
1301 F32s("to", 3),
1302 I32("value_50"),
1303 Time("time_48"),
1304 ],
1305 ),
1306 ],
1307 ),
1308 (
1310 "TerraformComponent",
1311 &[
1312 BASE,
1313 Block("terraform", 0, &[List("points", &[F32s("point", 3)])]),
1314 ],
1315 ),
1316 (
1318 "TombstoneComponent",
1319 &[BASE, Block("tombstone", 2, &[Time("left")])],
1320 ),
1321 (
1323 "TornadoComponent",
1324 &[
1325 BASE,
1326 Block(
1327 "tornado",
1328 0,
1329 &[
1330 F32s("position", 3),
1331 I32("value_50"),
1332 List("path", &[F32s("point", 3)]),
1333 Time("time_28"),
1334 F32s("value_58", 3),
1335 ],
1336 ),
1337 ],
1338 ),
1339 (
1341 "TutorialInstance",
1342 &[Block("tutorial", 0, &[Ext("tutorial"), I32("step")])],
1343 ),
1344 ("WellComponent", &[BASE, CONTROLLER, Block("well", 0, &[])]),
1345 (
1347 "HealerComponent",
1348 &[BASE, CONTROLLER, Block("healer", 0, &[])],
1349 ),
1350 (
1352 "ClearResourceComponent",
1353 &[BASE, CONTROLLER, Block("clear", 0, &[])],
1354 ),
1355 (
1357 "AttendanceComponent",
1358 &[
1359 BASE,
1360 CONTROLLER,
1361 Block("attendance", 0, &[List("visitors", &[Ptr("citizen")])]),
1362 ],
1363 ),
1364 (
1366 "AutoPickupComponent",
1367 &[
1368 BASE,
1369 Block(
1370 "pickup",
1371 0,
1372 &[Bool("on"), I32("cell_count"), I32sBy("cells", "cell_count")],
1373 ),
1374 ],
1375 ),
1376 (
1378 "FloorComponent",
1379 &[BASE, Block("floor", 0, &[Bool("active")])],
1380 ),
1381 (
1383 "FruitComponent",
1384 &[
1385 BASE,
1386 Block(
1387 "fruit",
1388 0,
1389 &[Ptr("orchard"), F32("value_30"), Bool("flag_34")],
1390 ),
1391 ],
1392 ),
1393 ("MineComponent", &[BASE, Block("mine", 0, &[])]),
1395 (
1397 "MultiObjectComponent",
1398 &[
1399 BASE,
1400 CONTROLLER,
1401 Block("multi", 0, &[List("parts", &[Ptr("part")]), I32("stages")]),
1402 ],
1403 ),
1404 ("QuarryComponent", &[BASE, Block("quarry", 0, &[])]),
1406 (
1408 "RawMaterialComponent",
1409 &[
1410 BASE,
1411 Block(
1412 "raw",
1413 2,
1414 &[
1415 I32("count"),
1416 Time("age"),
1417 If(VersionAtLeast(2), &[Ext("profession")]),
1418 ],
1419 ),
1420 ],
1421 ),
1422 (
1424 "ConsumeProduceComponent",
1425 &[
1426 RESOURCE_PRODUCE,
1427 Block(
1428 "consume_produce",
1429 1,
1430 &[
1431 I32("selector"),
1432 I32("weight_count"),
1433 F32sBy("weights", "weight_count"),
1434 I32("produced"),
1435 ],
1436 ),
1437 ],
1438 ),
1439 (
1441 "AnimateNaturalResource",
1442 &[
1443 ACTION,
1444 Block("animate", 0, &[Ptr("entity"), I32("clip"), Bool("flag_2c")]),
1445 ],
1446 ),
1447 (
1449 "CreateNaturalResource",
1450 &[
1451 ACTION,
1452 Block(
1453 "create_natural",
1454 1,
1455 &[
1456 Ext("template"),
1457 I32("cell"),
1458 If(VersionAtLeast(1), &[Ptr("occupant")]),
1459 ],
1460 ),
1461 ],
1462 ),
1463 (
1465 "CreateRawMaterial",
1466 &[
1467 ACTION,
1468 Block(
1469 "create_raw",
1470 3,
1471 &[
1472 Ext("template"),
1473 Ptr("citizen"),
1474 I32("cell"),
1475 F32("factor"),
1476 Bool("flag_50"),
1477 I32("batch"),
1478 Bool("flag_51"),
1479 If(VersionAtLeast(1), &[Ptr("workplace")]),
1480 If(VersionAtLeast(2), &[U32("value_48")]),
1481 If(VersionAtLeast(3), &[Ptr("value_38")]),
1482 ],
1483 ),
1484 ],
1485 ),
1486 (
1488 "DropOffRawMaterial",
1489 &[
1490 ACTION,
1491 Block(
1492 "drop_off",
1493 0,
1494 &[
1495 Ptr("citizen"),
1496 Ptr("storage"),
1497 Ext("material"),
1498 Bool("redirect"),
1499 I32("count"),
1500 ],
1501 ),
1502 ],
1503 ),
1504 (
1506 "PickupRawMaterial",
1507 &[ACTION, Block("pickup", 0, &[Ptr("citizen"), Ptr("entity")])],
1508 ),
1509 (
1511 "PickupStoredRawMaterial",
1512 &[
1513 ACTION,
1514 Block(
1515 "pickup_stored",
1516 0,
1517 &[
1518 Ptr("citizen"),
1519 Ptr("storage"),
1520 List("items", &[Ext("material"), I32("count")]),
1521 Bool("flag_40"),
1522 ],
1523 ),
1524 ],
1525 ),
1526 (
1528 "RemoveNaturalResource",
1529 &[
1530 ACTION,
1531 Block(
1532 "remove",
1533 0,
1534 &[Ptr("citizen"), Ptr("entity"), Bool("flag_30")],
1535 ),
1536 ],
1537 ),
1538];
1539
1540const fn ptr_list(name: &'static str) -> Op {
1543 List(name, &[Ptr("entity")])
1544}
1545
1546pub const ACHIEVEMENTS: Op = Block("achievements", 0, &[List("instances", &[Ptr("instance")])]);
1551
1552pub const TOWN: Op = Block(
1558 "town",
1559 6,
1560 &[
1561 ptr_list("storages"),
1562 ptr_list("markets"),
1563 ptr_list("trading_posts"),
1564 ptr_list("residences"),
1565 ptr_list("healers"),
1566 ptr_list("citizens"),
1567 List("workplaces", &[Ext("profession"), ptr_list("entities")]),
1569 ptr_list("pastures"),
1570 ptr_list("cemeteries"),
1571 ptr_list("happiness"),
1572 ptr_list("attendance"),
1573 ptr_list("livestock"),
1574 ptr_list("herds"),
1575 If(VersionAtLeast(1), &[ptr_list("diseases")]),
1576 ptr_list("populations"),
1577 ptr_list("sites"),
1578 I32("known_material_count"),
1579 I32sBy("known_materials", "known_material_count"),
1580 I32("known_count"),
1581 I32sBy("known", "known_count"),
1582 Time("commute_time"),
1583 Time("time_198"),
1584 List("history", &[I32("count"), I32sBy("values", "count")]),
1585 If(
1586 VersionBelow(2),
1587 &[List("legacy_counts", &[Ext("description"), I32("count")])],
1588 ),
1589 If(
1591 VersionAtLeast(2),
1592 &[List("zones", &[Ext("description"), ptr_list("entities")])],
1593 ),
1594 ptr_list("wells"),
1595 List("water_sources", &[I32("a"), I32("b")]),
1596 If(
1597 VersionAtLeast(5),
1598 &[List(
1599 "values_140",
1600 &[I32("count"), I32sBy("values", "count")],
1601 )],
1602 ),
1603 If(
1604 VersionBelow(4),
1605 &[
1606 U32s("legacy_a", 13),
1607 U32s("legacy_b", 13),
1608 U32s("legacy_c", 13),
1609 Times("legacy_times", 13),
1610 U32s("legacy_d", 9),
1611 U32s("legacy_e", 9),
1612 ],
1613 ),
1614 If(
1615 VersionAtLeast(4),
1616 &[
1617 List(
1618 "stock",
1619 &[
1620 I32("count"),
1621 I32("previous"),
1622 I32("limit"),
1623 I32("used"),
1624 I32("made"),
1625 Time("cooldown"),
1626 ],
1627 ),
1628 List(
1629 "capacity_records",
1630 &[I32("used"), I32("capacity"), Time("cooldown")],
1631 ),
1632 ],
1633 ),
1634 F32("visitor_risk"),
1635 Time("visitor_time"),
1636 If(VersionAtLeast(2), &[I32("trade_value")]),
1637 If(VersionAtLeast(3), &[I32("nomads_admitted")]),
1638 ],
1639);
1640
1641pub const JOBS: Op = Block(
1648 "jobs",
1649 3,
1650 &[
1651 List("works", &[I32("key"), Ptr("work")]),
1652 List("category_1", &[I32("key"), Ptr("work")]),
1653 List("category_2", &[I32("key"), Ptr("work")]),
1654 List("category_3", &[I32("key"), Ptr("work")]),
1655 List("category_4", &[I32("key"), Ptr("work")]),
1656 List("category_5", &[I32("key"), Ptr("work")]),
1657 List("category_6", &[I32("key"), Ptr("work")]),
1658 List("category_7", &[I32("key"), Ptr("work")]),
1659 List("category_8", &[I32("key"), Ptr("work")]),
1660 List("category_9", &[I32("key"), Ptr("work")]),
1661 List("category_10", &[I32("key"), Ptr("work")]),
1662 List("category_11", &[I32("key"), Ptr("work")]),
1663 List("category_12", &[I32("key"), Ptr("work")]),
1664 List("category_13", &[I32("key"), Ptr("work")]),
1665 If(
1666 VersionAtLeast(3),
1667 &[List("category_14", &[I32("key"), Ptr("work")])],
1668 ),
1669 List(
1670 "by_profession",
1671 &[Ext("profession"), List("work", &[I32("key"), Ptr("work")])],
1672 ),
1673 I32("next_id"),
1674 I32("next_priority"),
1675 U32("pass"),
1676 ],
1677);
1678
1679pub const TOOLS: Op = Block(
1683 "tools",
1684 2,
1685 &[
1686 Bool("flag_30"),
1687 I32("value_34"),
1688 F32("value_3c"),
1689 F32("value_40"),
1690 F32("value_44"),
1691 List(
1693 "descriptions",
1694 &[
1695 Ext("description"),
1696 I32("value_8"),
1697 I32("value_c"),
1698 Bool("flag_10"),
1699 ],
1700 ),
1701 If(
1703 VersionAtLeast(1),
1704 &[List(
1705 "entities",
1706 &[
1707 Ptr("entity"),
1708 I32("value_8"),
1709 I32("value_c"),
1710 Bool("flag_10"),
1711 ],
1712 )],
1713 ),
1714 If(
1715 VersionAtLeast(2),
1716 &[List(
1717 "points",
1718 &[
1719 F32s("position", 3),
1720 F32("value_c"),
1721 F32("value_10"),
1722 F32("value_14"),
1723 Bool("flag_18"),
1724 ],
1725 )],
1726 ),
1727 ],
1728);
1729
1730pub const CALENDAR: Op = Block("calendar", 0, &[I32("value"), Time("time")]);
1734
1735pub const EVENT_LOG: Op = Block(
1740 "event_log",
1741 1,
1742 &[
1743 Bool("notify_0"),
1744 Bool("notify_1"),
1745 Bool("notify_2"),
1746 Bool("notify_3"),
1747 Bool("notify_4"),
1748 Bool("notify_5"),
1749 List(
1750 "entries",
1751 &[
1752 U32("color"),
1753 Str("text"),
1754 I32("kind"),
1755 I32("severity"),
1756 I32("cell"),
1757 I32("category"),
1758 I32("icon"),
1759 ],
1760 ),
1761 ],
1762);
1763
1764pub const ENVIRONMENT: Op = Block(
1769 "environment",
1770 0,
1771 &[
1772 Ext("climate"),
1773 Ext("month"),
1774 I32("state"),
1775 I32("level"),
1776 F32("temperature"),
1777 F32s("season", 4),
1778 F32("baseline"),
1779 F32("baseline_target"),
1780 F32("bias"),
1781 F32("bias_target"),
1782 I32("slice"),
1783 Rest(
1784 "effects",
1785 &[
1786 Bool("active"),
1787 If(Flag("active"), &[F32("direction"), F32("intensity")]),
1788 ],
1789 ),
1790 ],
1791);
1792
1793pub const MINIMAP: Op = Block(
1797 "minimap",
1798 0,
1799 &[
1800 I32("count_28"),
1801 I32sBy("image_28", "count_28"),
1802 I32("count_18"),
1803 I32sBy("image_18", "count_18"),
1804 I32("size"),
1805 ],
1806);
1807
1808pub fn schema_for(ty: u32) -> Option<(&'static str, &'static [Op])> {
1810 static BY_ID: OnceLock<HashMap<u32, (&'static str, &'static [Op])>> = OnceLock::new();
1811 BY_ID
1812 .get_or_init(|| {
1813 SCHEMAS
1814 .iter()
1815 .map(|&(name, ops)| (type_id(name), (name, ops)))
1816 .collect()
1817 })
1818 .get(&ty)
1819 .copied()
1820}
1821
1822pub fn default_fields(name: &str) -> Option<crate::savegame::Fields> {
1825 let (_, ops) = schema_for(type_id(name))?;
1826 let mut out = crate::savegame::Fields::default();
1827 defaults_into(ops, 0, &mut out);
1828 Some(out)
1829}
1830
1831pub fn default_block(ops: &[Op], version: u32) -> crate::savegame::Fields {
1834 let mut out = crate::savegame::Fields::default();
1835 defaults_into(ops, version, &mut out);
1836 out
1837}
1838
1839fn defaults_into(ops: &[Op], version: u32, out: &mut crate::savegame::Fields) {
1840 use crate::savegame::{Fields, Value};
1841 for op in ops {
1842 let (name, value) = match *op {
1843 Bool(n) => (n, Value::Bool(false)),
1844 I8(n) => (n, Value::I8(0)),
1845 I16(n) => (n, Value::I16(0)),
1846 U16(n) => (n, Value::U16(0)),
1847 I32(n) => (n, Value::I32(0)),
1848 U32(n) => (n, Value::U32(0)),
1849 Time(n) => (n, Value::I64(0)),
1850 F32(n) => (n, Value::F32(0.0)),
1851 Times(n, k) => (n, Value::I64s(vec![0; k])),
1852 F32s(n, k) => (n, Value::F32s(vec![0.0; k])),
1853 U32s(n, k) => (n, Value::U32s(vec![0; k])),
1854 I32sBy(n, _) => (n, Value::U32s(Vec::new())),
1855 F32sBy(n, _) => (n, Value::F32s(Vec::new())),
1856 Str(n) | WStr(n) => (n, Value::Str(String::new())),
1857 Ext(n) => (n, Value::Ext(u32::MAX)),
1858 Ptr(n) => (n, Value::Ptr(None)),
1859 List(n, _) | Rest(n, _) => (n, Value::List(Vec::new())),
1860 Block(n, v, inner) => {
1861 let mut f = Fields::default();
1862 defaults_into(inner, v, &mut f);
1863 (n, Value::Block(v, f))
1864 }
1865 Sub(inner) => {
1866 defaults_into(inner, version, out);
1867 continue;
1868 }
1869 If(cond, inner) => {
1870 let take = match cond {
1871 VersionAtLeast(v) => version >= v,
1872 VersionBelow(v) => version < v,
1873 Flag(_) => false,
1874 };
1875 if take {
1876 defaults_into(inner, version, out);
1877 }
1878 continue;
1879 }
1880 };
1881 out.0.push((name, value));
1882 }
1883}
1884
1885pub fn type_names() -> impl Iterator<Item = &'static str> {
1887 SCHEMAS.iter().map(|&(name, _)| name)
1888}