1mod magic;
37pub use self::magic::{Attribute, AttributeError};
39use crate::{
40 attributes,
41 comp::{item::ItemDefinitionIdOwned, tool::ToolKind},
42 lottery::LootSpec,
43 make_case_elim, sprites,
44 terrain::Block,
45};
46use common_i18n::Content;
47use hashbrown::HashMap;
48use lazy_static::lazy_static;
49use num_derive::FromPrimitive;
50use serde::{Deserialize, Serialize};
51use std::{
52 convert::{Infallible, TryFrom},
53 fmt,
54};
55use strum::EnumIter;
56use vek::*;
57
58#[derive(Copy, Clone, Debug, Eq, PartialEq, Deserialize)]
83#[serde(transparent)]
84pub struct StructureSprite(StructureSpriteKind);
85
86impl StructureSprite {
87 pub fn get_block(self, with_sprite: impl FnMut(SpriteKind) -> Block) -> Block {
88 self.0.get_block(with_sprite)
89 }
90}
91
92sprites! {
93 Void = 0 {
94 Empty = 0,
95 },
96 Misc = 1 {
98 Ember = 0x00,
99 SmokeDummy = 0x01,
100 Bomb = 0x02,
101 FireBlock = 0x03, HotSurface = 0x04,
103 Stones2 = 0x05, },
105 Furniture = 2 has Ori, MirrorX {
108 BookshelfArabic = 0x0D,
110 WallTableArabic = 0x0E,
111 TableArabicLarge = 0x0F,
112 TableArabicSmall = 0x10,
113 CupboardArabic = 0x11,
114 OvenArabic = 0x12,
115 CushionArabic = 0x13,
116 CanapeArabic = 0x14,
117 Shelf = 0x15,
118 Planter = 0x16,
119 BedMesa = 0x18,
120 WallTableMesa = 0x19,
121 MirrorMesa = 0x1A,
122 WardrobeSingleMesa = 0x1B,
123 WardrobeDoubleMesa = 0x1C,
124 CupboardMesa = 0x1D,
125 TableCoastalLarge = 0x1E,
126 BenchCoastal = 0x1F,
127 CraftingBench = 0x20,
129 Forge = 0x21,
130 Cauldron = 0x22,
131 Anvil = 0x23,
132 CookingPot = 0x24,
133 SpinningWheel = 0x25,
134 TanningRack = 0x26,
135 Loom = 0x27,
136 DismantlingBench = 0x28,
137 RepairBench = 0x29,
138 HangingBasket = 0x50,
140 HangingSign = 0x51,
141 ChristmasOrnament = 0x52,
142 ChristmasWreath = 0x53,
143 WallLampWizard = 0x54,
144 WallLamp = 0x55,
145 WallLampSmall = 0x56,
146 WallSconce = 0x57,
147 DungeonWallDecor = 0x58,
148 WallLampMesa = 0x59,
149 Tent = 0x60,
151 Bedroll = 0x61,
152 BedrollSnow = 0x62,
153 BedrollPirate = 0x63,
154 Sign = 0x64,
155 Helm = 0x65,
156 Scarecrow = 0x70,
158 FountainArabic = 0x71,
159 Hearth = 0x72,
160 ChestWoodDouble= 0x73,
161 LanternpostWoodUpper = 0x74,
162 LanternpostWoodBase = 0x75,
163 LampMetalBase = 0x76,
164 BlacksmithBellows = 0x77,
165 CarpenterTable = 0x78,
166 CarpenterCrateWoodS = 0x79,
167 CarpenterCrateWoodL = 0x7A,
168 CarpenterToolsWall = 0x7B,
169 CarpenterLogCutter = 0x7C,
170 BarrelWoodCoal = 0x7D,
171 BarrelWoodWater = 0x7E,
172 BasketWovenL = 0x7F,
173 BasketWovenM = 0x80,
174 BasketWovenS = 0x81,
175 BonfireMLit = 0x82,
176 BonfireMUnlit = 0x83,
177 BucketWoodM = 0x84,
178 MirrorWoodM = 0x85,
179 SackLeatherM = 0x86,
180 TrophyframeWoodBear = 0x87,
181 TrophyframeWoodDeer = 0x88,
182 JugClayM = 0x89,
183 LogsWoodBranchS = 0x8A,
184 DiningtableWoodCorner = 0x8B,
185 DiningtableWoodBody = 0x8C,
186 BenchWoodEnd = 0x8D,
187 BenchWoodMiddle = 0x8E,
188 LogsWoodCoreEnd = 0x8F,
189 LogsWoodCoreMiddle = 0x90,
190 LogsWoodBarkEnd = 0x91,
191 LogsWoodBarkMiddle = 0x92,
192 LogsWoodBranchEnd = 0x93,
193 LogsWoodBranchMiddle = 0x94,
194 SeatWoodBlueMiddle = 0x95,
195 SeatWoodBlueSide = 0x96,
196 RopeCoilM = 0x97,
197 BedWoodWoodlandHead = 0x99,
198 BedWoodWoodlandMiddle = 0x9A,
199 BedWoodWoodlandTail = 0x9B,
200 BenchWoodWoodlandGreen1 = 0x9C,
201 BenchWoodWoodlandGreen2 = 0x9D,
202 BenchWoodWoodlandGreen3 = 0x9E,
203 BenchWoodWoodland = 0xA0,
204 ChairWoodWoodland = 0xA1,
205 ChairWoodWoodland2 = 0xA2,
206 CoatrackMetalWoodland = 0xA3,
207 CoatrackWoodWoodland = 0xA4,
208 DrawerWoodWoodlandL1 = 0xA5,
209 DrawerWoodWoodlandL2 = 0xA6,
210 DrawerWoodWoodlandM1 = 0xA7,
211 DrawerWoodWoodlandM2 = 0xA8,
212 DrawerWoodWoodlandS = 0xA9,
213 HandCartWoodHead = 0xAA,
214 HandCartWoodMiddle = 0xAB,
215 HandCartWoodTail = 0xAC,
216 FlowerpotWoodWoodlandS = 0xAD,
217 DiningtableWoodWoodlandRound = 0xAE,
218 DiningtableWoodWoodlandSquare = 0xAF,
219 TableWoodFancyWoodlandCorner = 0xB0,
220 TableWoodFancyWoodlandBody = 0xB1,
221 WardrobedoubleWoodWoodland = 0xB2,
222 WardrobedoubleWoodWoodland2 = 0xB3,
223 WardrobesingleWoodWoodland = 0xB4,
224 WardrobesingleWoodWoodland2 = 0xB5,
225 BedCliffHead = 0xB6,
226 BedCliffMiddle = 0xB7,
227 BedCliffTail = 0xB8,
228 BedCoastalHead = 0xB9,
229 BedCoastalMiddle = 0xBA,
230 BedCoastalTail = 0xBB,
231 BedDesertHead = 0xBC,
232 BedDesertMiddle = 0xBD,
233 BedDesertTail = 0xBE,
234 BedSavannahHead = 0xBF,
235 BedSavannahMiddle = 0xC0,
236 BedSavannahTail = 0xC1,
237 Ladder = 0xC2,
238 BookshelfEnd = 0xC3,
239 BookshelfMiddle = 0xC4,
240 },
241 Plant = 3 has Growth, Owned, SnowCovered {
243 BarrelCactus = 0x00,
245 RoundCactus = 0x01,
246 ShortCactus = 0x02,
247 MedFlatCactus = 0x03,
248 ShortFlatCactus = 0x04,
249 LargeCactus = 0x05,
250 TallCactus = 0x06,
251 BlueFlower = 0x10,
253 PinkFlower = 0x11,
254 PurpleFlower = 0x12,
255 RedFlower = 0x13,
256 WhiteFlower = 0x14,
257 YellowFlower = 0x15,
258 Sunflower = 0x16,
259 Moonbell = 0x17,
260 Pyrebloom = 0x18,
261 LushFlower = 0x19,
262 LanternFlower = 0x1A,
263 LongGrass = 0x20,
266 MediumGrass = 0x21,
267 ShortGrass = 0x22,
268 Fern = 0x23,
269 LargeGrass = 0x24,
270 Reed = 0x25,
271 TaigaGrass = 0x26,
272 GrassBlue = 0x27,
273 SavannaGrass = 0x28,
274 TallSavannaGrass = 0x29,
275 RedSavannaGrass = 0x2A,
276 SavannaBush = 0x2B,
277 Welwitch = 0x2C,
278 LeafyPlant = 0x2D,
279 DeadBush = 0x2E,
280 JungleFern = 0x2F,
281 GrassBlueShort = 0x30,
282 GrassBlueMedium = 0x31,
283 GrassBlueLong = 0x32,
284 CavernLillypadBlue = 0x33,
285 EnsnaringVines = 0x34,
286 LillyPads = 0x35,
287 JungleLeafyPlant = 0x36,
288 JungleRedGrass = 0x37,
289 LanternPlant = 0x38,
290 SporeReed = 0x39,
291 DeadPlant = 0x3A,
292 Corn = 0x41,
294 WheatYellow = 0x42,
295 WheatGreen = 0x43, LingonBerry = 0x44,
297 Blueberry = 0x45,
298 Cabbage = 0x46,
299 Pumpkin = 0x47,
300 Carrot = 0x48,
301 Tomato = 0x49,
302 Radish = 0x4A,
303 Turnip = 0x4B,
304 Flax = 0x4C,
305 Mushroom = 0x4D,
306 CaveMushroom = 0x4E,
307 Cotton = 0x4F,
308 WildFlax = 0x50,
309 SewerMushroom = 0x51,
310 LushMushroom = 0x52,
311 RockyMushroom = 0x53,
312 GlowMushroom = 0x54,
313 StonyCoral = 0x61,
315 SoftCoral = 0x62,
316 SeaweedTemperate = 0x63,
317 SeaweedTropical = 0x64,
318 GiantKelp = 0x65,
319 BullKelp = 0x66,
320 WavyAlgae = 0x67,
321 SeaGrapes = 0x68,
322 MermaidsFan = 0x69,
323 SeaAnemone = 0x6A,
324 Seagrass = 0x6B,
325 RedAlgae = 0x6C,
326 Liana = 0x71,
328 MycelBlue = 0x72,
329 CeilingMushroom = 0x73,
330 Mold = 0x74,
331 Root = 0x75,
332 CeilingLanternPlant = 0x76,
333 CeilingLanternFlower = 0x77,
334 CeilingJungleLeafyPlant = 0x78,
335 },
336 Resource = 4 has Owned, SnowCovered {
339 Twigs = 0x00,
342 Wood = 0x01,
343 Bamboo = 0x02,
344 Hardwood = 0x03,
345 Ironwood = 0x04,
346 Frostwood = 0x05,
347 Eldwood = 0x06,
348 Apple = 0x20,
350 Coconut = 0x21,
351 Stones = 0x22,
352 Seashells = 0x23,
353 Beehive = 0x24,
354 Bowl = 0x25,
355 PotionMinor = 0x26,
356 PotionDummy = 0x27,
357 VialEmpty = 0x28,
358 },
359 MineableResource = 5 has Damage {
360 Amethyst = 0x00,
361 Ruby = 0x01,
362 Sapphire = 0x02,
363 Emerald = 0x03,
364 Topaz = 0x04,
365 Diamond = 0x05,
366 Bloodstone = 0x06,
367 Coal = 0x07,
368 Cobalt = 0x08,
369 Copper = 0x09,
370 Iron = 0x0A,
371 Tin = 0x0B,
372 Silver = 0x0C,
373 Gold = 0x0D,
374 Velorite = 0x0E,
375 VeloriteFrag = 0x0F,
376 Mud = 0x10,
377 Grave = 0x11,
378 },
379 Structural = 6 has Ori {
381 Door = 0x00,
383 DoorDark = 0x01,
384 DoorWide = 0x02,
385 BoneKeyhole = 0x03,
386 BoneKeyDoor = 0x04,
387 Keyhole = 0x05,
388 KeyDoor = 0x06,
389 GlassKeyhole = 0x07,
390 KeyholeBars = 0x08,
391 HaniwaKeyDoor = 0x09,
392 HaniwaKeyhole = 0x0A,
393 TerracottaKeyDoor = 0x0B,
394 TerracottaKeyhole = 0x0C,
395 SahaginKeyhole = 0x0D,
396 SahaginKeyDoor = 0x0E,
397 VampireKeyDoor = 0x0F,
398 VampireKeyhole = 0x10,
399 MyrmidonKeyDoor = 0x11,
400 MyrmidonKeyhole = 0x12,
401 MinotaurKeyhole = 0x13,
402
403 Window1 = 0x14,
405 Window2 = 0x15,
406 Window3 = 0x16,
407 Window4 = 0x17,
408 WitchWindow = 0x18,
409 WindowArabic = 0x19,
410 GlassBarrier = 0x20,
412 SeaDecorBlock = 0x21,
413 CliffDecorBlock = 0x22,
414 MagicalBarrier = 0x23,
415 OneWayWall = 0x24,
416 SeaDecorWindowHor = 0x30,
418 SeaDecorWindowVer = 0x31,
419 DropGate = 0x32,
420 DropGateBottom = 0x33,
421 WoodBarricades = 0x34,
422 Rope = 0x40,
424 SeaDecorChain = 0x41,
425 IronSpike = 0x42,
426 DoorBars = 0x43,
427 HaniwaTrap = 0x44,
428 HaniwaTrapTriggered = 0x45,
429 TerracottaStatue = 0x46,
430 TerracottaBlock = 0x47,
431 MetalChain = 0x48,
432 },
433 Decor = 7 has Ori {
435 Bones = 0x00,
437 IceCrystal = 0x01,
438 GlowIceCrystal = 0x02,
439 CrystalHigh = 0x03,
440 CrystalLow = 0x04,
441 UnderwaterVent = 0x05,
442 SeaUrchin = 0x06,
443 IceSpike = 0x07,
444 Orb = 0x08,
445 EnsnaringWeb = 0x09,
446 DiamondLight = 0x0A,
447
448 Gravestone = 0x10,
450 Melon = 0x11,
451 ForgeTools = 0x12,
452 JugAndBowlArabic = 0x13,
453 JugArabic = 0x14,
454 DecorSetArabic = 0x15,
455 SepareArabic = 0x16,
456 Candle = 0x17,
457 SmithingTable = 0x18,
458 Forge0 = 0x19,
459 GearWheel0 = 0x1A,
460 Quench0 = 0x1B,
461 SeaDecorEmblem = 0x1C,
462 SeaDecorPillar = 0x1D,
463 MagicalSeal = 0x1E,
464 JugAndCupsCoastal = 0x1F,
465 },
466 Lamp = 8 has Ori, LightEnabled {
467 Lantern = 0x00,
469 StreetLamp = 0x01,
470 StreetLampTall = 0x02,
471 SeashellLantern = 0x03,
472 FireBowlGround = 0x04,
473 MesaLantern = 0x05,
474 LanternpostWoodLantern = 0x06,
475 LampMetalShinglesRed = 0x07,
476 LampTerracotta = 0x08,
477 LampMetalShinglesCyan = 0x09,
478 LanternAirshipWallBlackS = 0x0A,
479 LanternAirshipWallBrownS = 0x0B,
480 LanternAirshipWallChestnutS = 0x0C,
481 LanternAirshipWallRedS = 0x0D,
482 LanternAirshipGroundBlackS = 0x0E,
483 LanternAirshipGroundBrownS = 0x0F,
484 LanternAirshipGroundChestnutS = 0x10,
485 LanternAirshipGroundRedS = 0x11,
486 },
487 Container = 9 has Ori, Owned {
488 Chest = 0x00,
489 DungeonChest0 = 0x01,
490 DungeonChest1 = 0x02,
491 DungeonChest2 = 0x03,
492 DungeonChest3 = 0x04,
493 DungeonChest4 = 0x05,
494 DungeonChest5 = 0x06,
495 CoralChest = 0x07,
496 HaniwaUrn = 0x08,
497 TerracottaChest = 0x09,
498 SahaginChest = 0x0A,
499 CommonLockedChest = 0x0B,
500 ChestBuried = 0x0C,
501 Crate = 0x0D,
502 Barrel = 0x0E,
503 CrateBlock = 0x0F,
504 },
505 Modular = 10 has Ori, AdjacentType {
506 Fence = 0x00,
507 }
508}
509
510attributes! {
511 Ori { bits: 3, err: Infallible, from: |bits| Ok(Self(bits as u8)), into: |Ori(x)| x as u16 },
512 MirrorX { bits: 1, err: Infallible, from: |bits| Ok(Self(bits == 1)), into: |MirrorX(x)| x as u16 },
513 MirrorY { bits: 1, err: Infallible, from: |bits| Ok(Self(bits == 1)), into: |MirrorY(x)| x as u16 },
514 MirrorZ { bits: 1, err: Infallible, from: |bits| Ok(Self(bits == 1)), into: |MirrorZ(x)| x as u16 },
515 Growth { bits: 4, err: Infallible, from: |bits| Ok(Self(bits as u8)), into: |Growth(x)| x as u16 },
516 LightEnabled { bits: 1, err: Infallible, from: |bits| Ok(Self(bits == 1)), into: |LightEnabled(x)| x as u16 },
517 Damage { bits: 3, err: Infallible, from: |bits| Ok(Self(bits as u8)), into: |Damage(x)| x as u16 },
518 Owned { bits: 1, err: Infallible, from: |bits| Ok(Self(bits == 1)), into: |Owned(x)| x as u16 },
519 AdjacentType { bits: 3, err: Infallible, from: |bits| Ok(Self(bits as u8)), into: |AdjacentType(x)| x as u16 },
520 SnowCovered { bits: 1, err: Infallible, from: |bits| Ok(Self(bits == 1)), into: |SnowCovered(x)| x as u16 },
521}
522
523#[derive(Copy, Clone, Default, Debug, PartialEq, Eq, Deserialize)]
525pub struct Ori(pub u8);
526
527#[derive(Copy, Clone, Default, Debug, PartialEq, Eq, Deserialize)]
528pub struct MirrorX(pub bool);
529
530#[derive(Copy, Clone, Default, Debug, PartialEq, Eq, Deserialize)]
531pub struct MirrorY(pub bool);
532
533#[derive(Copy, Clone, Default, Debug, PartialEq, Eq, Deserialize)]
534pub struct MirrorZ(pub bool);
535
536#[derive(Copy, Clone, Debug, PartialEq, Eq, Deserialize)]
538pub struct Growth(pub u8);
539
540impl Default for Growth {
541 fn default() -> Self { Self(15) }
542}
543
544#[derive(Copy, Clone, Debug, PartialEq, Eq, Deserialize)]
546pub struct LightEnabled(pub bool);
547
548impl Default for LightEnabled {
549 fn default() -> Self { Self(true) }
550}
551
552#[derive(Default, Copy, Clone, Debug, PartialEq, Eq, Deserialize)]
553pub struct Owned(pub bool);
554
555#[derive(Default, Copy, Clone, Debug, PartialEq, Eq, Deserialize, FromPrimitive, Hash)]
565#[repr(u8)]
566pub enum RelativeNeighborPosition {
567 #[default]
568 I,
569 L,
570 T,
571 X,
572 End,
573}
574
575#[derive(Copy, Clone, Debug, PartialEq, Eq, Deserialize)]
576#[serde(from = "RelativeNeighborPosition")]
577pub struct AdjacentType(pub u8);
578
579impl From<RelativeNeighborPosition> for AdjacentType {
580 fn from(value: RelativeNeighborPosition) -> Self { Self(value as u8) }
581}
582
583impl Default for AdjacentType {
584 fn default() -> Self { Self::from(RelativeNeighborPosition::I) }
585}
586
587#[derive(Copy, Clone, Debug, PartialEq, Eq, Default, Deserialize)]
589pub struct Damage(pub u8);
590
591#[derive(Copy, Clone, Default, Debug, PartialEq, Eq, Deserialize)]
593pub struct SnowCovered(pub bool);
594
595impl SpriteKind {
596 #[inline]
597 pub fn solid_height(&self) -> Option<f32> {
599 Some(match self {
602 SpriteKind::Bedroll => 0.3,
603 SpriteKind::BedrollSnow => 0.4,
604 SpriteKind::BedrollPirate => 0.3,
605 SpriteKind::Tomato => 1.65,
606 SpriteKind::BarrelCactus => 0.909,
607 SpriteKind::LargeCactus => 3.0,
608 SpriteKind::TallCactus => 2.63,
609 SpriteKind::Scarecrow => 3.0,
610 SpriteKind::Turnip => 0.36,
611 SpriteKind::Pumpkin => 0.81,
612 SpriteKind::Cabbage => 0.45,
613 SpriteKind::Chest => 1.09,
614 SpriteKind::CommonLockedChest => 1.09,
615 SpriteKind::DungeonChest0 => 1.09,
616 SpriteKind::DungeonChest1 => 1.09,
617 SpriteKind::DungeonChest2 => 1.09,
618 SpriteKind::DungeonChest3 => 1.09,
619 SpriteKind::DungeonChest4 => 1.09,
620 SpriteKind::DungeonChest5 => 1.09,
621 SpriteKind::CoralChest => 1.09,
622 SpriteKind::HaniwaUrn => 1.09,
623 SpriteKind::SahaginChest => 1.09,
624 SpriteKind::TerracottaChest => 1.09,
625 SpriteKind::TerracottaStatue => 5.29,
626 SpriteKind::TerracottaBlock => 1.00,
627 SpriteKind::Fence => 1.09,
628 SpriteKind::SeaDecorChain => 1.09,
629 SpriteKind::SeaDecorBlock => 1.00,
630 SpriteKind::SeaDecorWindowHor => 0.55,
631 SpriteKind::SeaDecorWindowVer => 1.09,
632 SpriteKind::SeaDecorPillar => 2.55,
633 SpriteKind::SeashellLantern => 2.09,
634 SpriteKind::MesaLantern => 1.3,
635 SpriteKind::Rope => 1.09,
636 SpriteKind::MetalChain => 1.09,
637 SpriteKind::StreetLamp => 2.65,
638 SpriteKind::Carrot => 0.18,
639 SpriteKind::Radish => 0.18,
640 SpriteKind::FireBowlGround => 0.55,
641 SpriteKind::BedMesa => 0.82,
642 SpriteKind::DungeonWallDecor => 1.0,
643 SpriteKind::Planter => 1.09,
644 SpriteKind::WardrobeSingleMesa => 2.0,
645 SpriteKind::WardrobeDoubleMesa => 2.0,
646 SpriteKind::MirrorMesa => 2.0,
647 SpriteKind::Mud => 0.36,
648 SpriteKind::ChestBuried => 0.91,
649 SpriteKind::StonyCoral => 1.4,
650 SpriteKind::CraftingBench => 1.18,
651 SpriteKind::Forge => 1.818,
652 SpriteKind::Cauldron => 1.27,
653 SpriteKind::SpinningWheel => 1.454,
654 SpriteKind::TanningRack => 1.454,
655 SpriteKind::Loom => 1.636,
656 SpriteKind::Anvil => 1.18,
657 SpriteKind::CookingPot => 1.090,
658 SpriteKind::DismantlingBench => 1.18,
659 SpriteKind::IceSpike => 1.0,
660 SpriteKind::RepairBench => 1.2,
661 SpriteKind::RoundCactus => 0.72,
662 SpriteKind::ShortCactus => 1.36,
663 SpriteKind::MedFlatCactus => 1.36,
664 SpriteKind::ShortFlatCactus => 0.91,
665 SpriteKind::Apple
667 | SpriteKind::Beehive
668 | SpriteKind::Velorite
669 | SpriteKind::VeloriteFrag
670 | SpriteKind::Coconut
671 | SpriteKind::StreetLampTall
672 | SpriteKind::Window1
673 | SpriteKind::Window2
674 | SpriteKind::Window3
675 | SpriteKind::Window4
676 | SpriteKind::DropGate
677 | SpriteKind::WitchWindow
678 | SpriteKind::SeaUrchin
679 | SpriteKind::IronSpike
680 | SpriteKind::GlassBarrier
681 | SpriteKind::GlassKeyhole
682 | SpriteKind::Keyhole
683 | SpriteKind::KeyDoor
684 | SpriteKind::BoneKeyhole
685 | SpriteKind::BoneKeyDoor
686 | SpriteKind::HaniwaKeyhole
687 | SpriteKind::HaniwaKeyDoor
688 | SpriteKind::SahaginKeyhole
689 | SpriteKind::SahaginKeyDoor
690 | SpriteKind::VampireKeyhole
691 | SpriteKind::VampireKeyDoor
692 | SpriteKind::HaniwaTrap
693 | SpriteKind::HaniwaTrapTriggered
694 | SpriteKind::TerracottaKeyDoor
695 | SpriteKind::TerracottaKeyhole
696 | SpriteKind::MyrmidonKeyDoor
697 | SpriteKind::MyrmidonKeyhole
698 | SpriteKind::MinotaurKeyhole
699 | SpriteKind::Bomb
700 | SpriteKind::OneWayWall
701 | SpriteKind::DoorBars
702 | SpriteKind::KeyholeBars
703 | SpriteKind::WoodBarricades
704 | SpriteKind::DiamondLight => 1.0,
705 SpriteKind::Shelf => 1.0,
707 SpriteKind::Lantern => 0.9,
708 SpriteKind::CrystalHigh | SpriteKind::CrystalLow => 1.5,
709 SpriteKind::Bloodstone
710 | SpriteKind::Coal
711 | SpriteKind::Cobalt
712 | SpriteKind::Copper
713 | SpriteKind::Iron
714 | SpriteKind::Tin
715 | SpriteKind::Silver
716 | SpriteKind::Gold => 0.6,
717 SpriteKind::EnsnaringVines
718 | SpriteKind::CavernLillypadBlue
719 | SpriteKind::EnsnaringWeb => 0.15,
720 SpriteKind::LillyPads => 0.1,
721 SpriteKind::WindowArabic | SpriteKind::BookshelfArabic => 1.9,
722 SpriteKind::DecorSetArabic => 2.6,
723 SpriteKind::SepareArabic => 2.2,
724 SpriteKind::CushionArabic => 0.4,
725 SpriteKind::JugArabic => 1.4,
726 SpriteKind::TableArabicSmall => 0.9,
727 SpriteKind::TableArabicLarge => 1.0,
728 SpriteKind::TableCoastalLarge => 1.0,
729 SpriteKind::BenchCoastal => 1.0,
730 SpriteKind::CanapeArabic => 1.2,
731 SpriteKind::CupboardArabic => 4.5,
732 SpriteKind::WallTableArabic => 2.3,
733 SpriteKind::JugAndBowlArabic => 1.4,
734 SpriteKind::JugAndCupsCoastal => 1.4,
735 SpriteKind::Melon => 0.7,
736 SpriteKind::OvenArabic => 3.2,
737 SpriteKind::FountainArabic => 2.4,
738 SpriteKind::Hearth => 2.3,
739 SpriteKind::ForgeTools => 2.8,
740 SpriteKind::CliffDecorBlock | SpriteKind::FireBlock => 1.0,
741 SpriteKind::Wood
742 | SpriteKind::Hardwood
743 | SpriteKind::Ironwood
744 | SpriteKind::Frostwood
745 | SpriteKind::Eldwood => 7.0 / 11.0,
746 SpriteKind::Bamboo => 9.0 / 11.0,
747 SpriteKind::MagicalBarrier => 3.0,
748 SpriteKind::MagicalSeal => 1.0,
749 SpriteKind::Helm => 1.909,
750 SpriteKind::Sign => 16.0 / 11.0,
751 SpriteKind::SmithingTable => 13.0 / 11.0,
752 SpriteKind::Forge0 => 17.0 / 11.0,
753 SpriteKind::GearWheel0 => 3.0 / 11.0,
754 SpriteKind::Quench0 => 8.0 / 11.0,
755 SpriteKind::HotSurface => 0.01,
756 SpriteKind::Barrel => 1.0,
757 SpriteKind::CrateBlock => 1.0,
758 SpriteKind::BarrelWoodWater | SpriteKind::BarrelWoodCoal => 1.545,
759 SpriteKind::LanternpostWoodLantern | SpriteKind::LanternpostWoodUpper => 2.000,
760 SpriteKind::LanternpostWoodBase => 3.000,
761 SpriteKind::LampMetalShinglesRed => 1.000,
762 SpriteKind::LampMetalShinglesCyan => 1.000,
763 SpriteKind::LampMetalBase => 2.818,
764 SpriteKind::LampTerracotta => 1.727,
765 SpriteKind::BlacksmithBellows => 0.545,
766 SpriteKind::CarpenterTable => 2.000,
767 SpriteKind::CarpenterCrateWoodS => 0.727,
768 SpriteKind::CarpenterCrateWoodL => 1.273,
769 SpriteKind::CarpenterLogCutter => 1.545,
770 SpriteKind::BasketWovenL | SpriteKind::JugClayM => 1.000,
771 SpriteKind::BasketWovenM => 0.909,
772 SpriteKind::BasketWovenS => 0.818,
773 SpriteKind::BonfireMLit | SpriteKind::BonfireMUnlit => 2.273,
774 SpriteKind::BucketWoodM | SpriteKind::SackLeatherM => 1.091,
775 SpriteKind::MirrorWoodM => 1.364,
776 SpriteKind::TrophyframeWoodBear => 1.455,
777 SpriteKind::TrophyframeWoodDeer => 1.727,
778 SpriteKind::ChestWoodDouble => 1.182,
779 SpriteKind::DiningtableWoodCorner => 1.273,
780 SpriteKind::DiningtableWoodBody => 1.273,
781 SpriteKind::BenchWoodEnd => 0.636,
782 SpriteKind::BenchWoodMiddle => 0.636,
783 SpriteKind::LogsWoodCoreEnd => 0.818,
784 SpriteKind::LogsWoodCoreMiddle => 0.818,
785 SpriteKind::LogsWoodBarkEnd => 1.091,
786 SpriteKind::LogsWoodBarkMiddle => 1.091,
787 SpriteKind::LogsWoodBranchEnd => 1.091,
788 SpriteKind::LogsWoodBranchMiddle => 1.091,
789 SpriteKind::LogsWoodBranchS => 1.091,
790 SpriteKind::SeatWoodBlueMiddle => 1.818,
791 SpriteKind::SeatWoodBlueSide => 1.818,
792 SpriteKind::LanternAirshipWallBlackS
793 | SpriteKind::LanternAirshipWallBrownS
794 | SpriteKind::LanternAirshipWallChestnutS
795 | SpriteKind::LanternAirshipWallRedS => 1.182,
796 SpriteKind::LanternAirshipGroundBlackS
797 | SpriteKind::LanternAirshipGroundBrownS
798 | SpriteKind::LanternAirshipGroundChestnutS
799 | SpriteKind::LanternAirshipGroundRedS => 0.909,
800 SpriteKind::RopeCoilM => 0.363,
801 SpriteKind::BedCliffHead => 0.636,
802 SpriteKind::BedCliffMiddle => 0.636,
803 SpriteKind::BedCliffTail => 0.636,
804 SpriteKind::BedCoastalHead => 0.636,
805 SpriteKind::BedCoastalMiddle => 0.636,
806 SpriteKind::BedCoastalTail => 0.636,
807 SpriteKind::BedDesertHead => 0.545,
808 SpriteKind::BedDesertMiddle => 0.545,
809 SpriteKind::BedDesertTail => 0.545,
810 SpriteKind::BedSavannahHead => 0.545,
811 SpriteKind::BedSavannahMiddle => 0.545,
812 SpriteKind::BedSavannahTail => 0.545,
813 SpriteKind::BedWoodWoodlandHead => 0.727,
814 SpriteKind::BedWoodWoodlandMiddle => 0.727,
815 SpriteKind::BedWoodWoodlandTail => 0.727,
816 SpriteKind::BookshelfEnd => 3.0,
817 SpriteKind::BookshelfMiddle => 3.0,
818 SpriteKind::BenchWoodWoodlandGreen1 => 1.545,
819 SpriteKind::BenchWoodWoodlandGreen2 => 1.545,
820 SpriteKind::BenchWoodWoodlandGreen3 => 1.545,
821 SpriteKind::BenchWoodWoodland => 1.545,
822 SpriteKind::ChairWoodWoodland => 1.636,
823 SpriteKind::ChairWoodWoodland2 => 1.727,
824 SpriteKind::CoatrackMetalWoodland => 2.364,
825 SpriteKind::CoatrackWoodWoodland => 2.364,
826 SpriteKind::Crate => 0.909,
827 SpriteKind::DrawerWoodWoodlandS => 1.000,
828 SpriteKind::DrawerWoodWoodlandM1 => 0.909,
829 SpriteKind::DrawerWoodWoodlandM2 => 0.909,
830 SpriteKind::DrawerWoodWoodlandL1 => 1.273,
831 SpriteKind::DrawerWoodWoodlandL2 => 1.273,
832 SpriteKind::DiningtableWoodWoodlandRound => 1.273,
833 SpriteKind::DiningtableWoodWoodlandSquare => 1.273,
834 SpriteKind::TableWoodFancyWoodlandCorner => 1.273,
835 SpriteKind::TableWoodFancyWoodlandBody => 1.273,
836 SpriteKind::WardrobesingleWoodWoodland => 2.364,
837 SpriteKind::WardrobesingleWoodWoodland2 => 2.364,
838 SpriteKind::WardrobedoubleWoodWoodland => 2.364,
839 SpriteKind::WardrobedoubleWoodWoodland2 => 2.364,
840 SpriteKind::FlowerpotWoodWoodlandS => 0.455,
841 SpriteKind::HandCartWoodHead => 1.091,
842 SpriteKind::HandCartWoodMiddle => 1.091,
843 SpriteKind::HandCartWoodTail => 1.091,
844 _ => return None,
845 })
846 }
847
848 pub fn valid_collision_dir(
849 &self,
850 entity_aabb: Aabb<f32>,
851 block_aabb: Aabb<f32>,
852 move_dir: Vec3<f32>,
853 parent: &Block,
854 ) -> bool {
855 match self {
856 SpriteKind::OneWayWall => {
857 let dir = entity_aabb.collision_vector_with_aabb(block_aabb);
859
860 let max_axis = dir.map(|e| e.abs()).reduce_partial_min();
863 let resolve_dir = -dir.map(|e| {
864 if e.abs().to_bits() == max_axis.to_bits() {
865 e.signum()
866 } else {
867 0.0
868 }
869 });
870
871 let is_moving_into = move_dir.dot(resolve_dir) <= 0.0;
872
873 is_moving_into
874 && parent.get_attr().is_ok_and(|Ori(ori)| {
875 Vec2::new(
876 0.0,
877 parent.get_attr::<MirrorY>().map_or(1.0, |m| match m.0 {
878 true => -1.0,
879 false => 1.0,
880 }),
881 )
882 .rotated_z(std::f32::consts::PI * 0.25 * ori as f32)
883 .with_z(0.0)
884 .map2(resolve_dir, |e, r| (e - r).abs() < 0.1)
885 .reduce_and()
886 })
887 },
888 _ => true,
889 }
890 }
891
892 #[inline]
901 pub fn default_loot_spec(&self) -> Option<Option<LootSpec<&'static str>>> {
902 let item = LootSpec::Item;
903 let table = LootSpec::LootTable;
904 Some(Some(match self {
905 SpriteKind::Apple => item("common.items.food.apple"),
906 SpriteKind::Mushroom => item("common.items.food.mushroom"),
907 SpriteKind::Velorite => item("common.items.mineral.ore.velorite"),
908 SpriteKind::VeloriteFrag => item("common.items.mineral.ore.veloritefrag"),
909 SpriteKind::RedFlower => item("common.items.flowers.red"),
913 SpriteKind::Sunflower => item("common.items.flowers.sunflower"),
916 SpriteKind::Coconut => item("common.items.food.coconut"),
920 SpriteKind::Beehive => item("common.items.crafting_ing.honey"),
921 SpriteKind::Stones => item("common.items.crafting_ing.stones"),
922 SpriteKind::Twigs => item("common.items.crafting_ing.twigs"),
923 SpriteKind::VialEmpty => item("common.items.crafting_ing.empty_vial"),
924 SpriteKind::Bowl => item("common.items.crafting_ing.bowl"),
925 SpriteKind::PotionMinor => item("common.items.consumable.potion_minor"),
926 SpriteKind::Amethyst => item("common.items.mineral.gem.amethyst"),
927 SpriteKind::Ruby => item("common.items.mineral.gem.ruby"),
928 SpriteKind::Diamond => item("common.items.mineral.gem.diamond"),
929 SpriteKind::Sapphire => item("common.items.mineral.gem.sapphire"),
930 SpriteKind::Topaz => item("common.items.mineral.gem.topaz"),
931 SpriteKind::Emerald => item("common.items.mineral.gem.emerald"),
932 SpriteKind::Bloodstone => item("common.items.mineral.ore.bloodstone"),
933 SpriteKind::Coal => item("common.items.mineral.ore.coal"),
934 SpriteKind::Cobalt => item("common.items.mineral.ore.cobalt"),
935 SpriteKind::Copper => item("common.items.mineral.ore.copper"),
936 SpriteKind::Iron => item("common.items.mineral.ore.iron"),
937 SpriteKind::Tin => item("common.items.mineral.ore.tin"),
938 SpriteKind::Silver => item("common.items.mineral.ore.silver"),
939 SpriteKind::Gold => item("common.items.mineral.ore.gold"),
940 SpriteKind::Cotton => item("common.items.crafting_ing.cotton_boll"),
941 SpriteKind::Moonbell => item("common.items.flowers.moonbell"),
942 SpriteKind::Pyrebloom => item("common.items.flowers.pyrebloom"),
943 SpriteKind::WildFlax => item("common.items.flowers.wild_flax"),
944 SpriteKind::Seashells => item("common.items.crafting_ing.seashells"),
945 SpriteKind::RoundCactus => item("common.items.crafting_ing.cactus"),
946 SpriteKind::ShortFlatCactus => item("common.items.crafting_ing.cactus"),
947 SpriteKind::MedFlatCactus => item("common.items.crafting_ing.cactus"),
948 SpriteKind::Bomb => item("common.items.utility.bomb"),
949 SpriteKind::DungeonChest0 => table("common.loot_tables.dungeon.gnarling.chest"),
950 SpriteKind::DungeonChest1 => table("common.loot_tables.dungeon.adlet.chest"),
951 SpriteKind::DungeonChest2 => table("common.loot_tables.dungeon.sahagin.chest"),
952 SpriteKind::DungeonChest3 => table("common.loot_tables.dungeon.haniwa.chest"),
953 SpriteKind::DungeonChest4 => table("common.loot_tables.dungeon.myrmidon.chest"),
954 SpriteKind::DungeonChest5 => table("common.loot_tables.dungeon.cultist.chest"),
955 SpriteKind::Chest => table("common.loot_tables.sprite.chest"),
956 SpriteKind::CommonLockedChest => table("common.loot_tables.dungeon.sahagin.chest"),
957 SpriteKind::ChestBuried => table("common.loot_tables.sprite.chest-buried"),
958 SpriteKind::CoralChest => table("common.loot_tables.dungeon.sea_chapel.chest_coral"),
959 SpriteKind::HaniwaUrn => table("common.loot_tables.dungeon.haniwa.key"),
960 SpriteKind::TerracottaChest => {
961 table("common.loot_tables.dungeon.terracotta.chest_terracotta")
962 },
963 SpriteKind::SahaginChest => table("common.loot_tables.dungeon.sahagin.key_chest"),
964 SpriteKind::Mud => table("common.loot_tables.sprite.mud"),
965 SpriteKind::Grave => table("common.loot_tables.sprite.mud"),
966 SpriteKind::Crate => table("common.loot_tables.sprite.crate"),
967 SpriteKind::Wood => item("common.items.log.wood"),
968 SpriteKind::Bamboo => item("common.items.log.bamboo"),
969 SpriteKind::Hardwood => item("common.items.log.hardwood"),
970 SpriteKind::Ironwood => item("common.items.log.ironwood"),
971 SpriteKind::Frostwood => item("common.items.log.frostwood"),
972 SpriteKind::Eldwood => item("common.items.log.eldwood"),
973 SpriteKind::MagicalBarrier => table("common.loot_tables.sprite.chest"),
974 SpriteKind::Keyhole
975 | SpriteKind::BoneKeyhole
976 | SpriteKind::HaniwaKeyhole
977 | SpriteKind::VampireKeyhole
978 | SpriteKind::GlassKeyhole
979 | SpriteKind::KeyholeBars
980 | SpriteKind::SahaginKeyhole
981 | SpriteKind::TerracottaKeyhole
982 | SpriteKind::MyrmidonKeyhole
983 | SpriteKind::MinotaurKeyhole => {
984 return Some(None);
985 },
986 _ => return None,
987 }))
988 }
989
990 #[inline]
992 pub fn is_collectible(&self, sprite_cfg: Option<&SpriteCfg>) -> bool {
993 sprite_cfg.and_then(|cfg| cfg.loot_table.as_ref()).is_some()
994 || self.default_tool() == Some(None)
995 }
996
997 #[inline]
1003 pub fn default_tool(&self) -> Option<Option<ToolKind>> {
1004 self.default_loot_spec().map(|_| self.mine_tool())
1005 }
1006
1007 #[inline]
1018 pub fn is_defined_as_container(&self) -> bool { self.category() == Category::Container }
1019
1020 #[inline]
1021 pub fn should_drop_mystery(&self) -> bool {
1023 self.is_defined_as_container()
1024 || matches!(
1025 self.default_loot_spec(),
1026 Some(Some(LootSpec::LootTable { .. } | LootSpec::Lottery { .. }))
1027 )
1028 }
1029
1030 #[inline]
1032 pub fn mount_offset(&self) -> Option<(Vec3<f32>, Vec3<f32>)> {
1034 match self {
1035 SpriteKind::ChairWoodWoodland
1036 | SpriteKind::ChairWoodWoodland2
1037 | SpriteKind::BenchWoodWoodlandGreen1
1038 | SpriteKind::BenchWoodWoodlandGreen2
1039 | SpriteKind::BenchWoodWoodlandGreen3
1040 | SpriteKind::BenchWoodWoodland
1041 | SpriteKind::BenchWoodEnd
1042 | SpriteKind::BenchWoodMiddle
1043 | SpriteKind::BenchCoastal => Some((Vec3::new(0.0, 0.0, 0.5), Vec3::unit_x())),
1044 SpriteKind::SeatWoodBlueMiddle | SpriteKind::SeatWoodBlueSide => {
1045 Some((Vec3::new(0.4, 0.0, 0.5), Vec3::unit_x()))
1046 },
1047 SpriteKind::Helm => Some((Vec3::new(0.0, -1.1, 0.0), Vec3::unit_y())),
1048 SpriteKind::BedWoodWoodlandHead
1049 | SpriteKind::BedCliffHead
1050 | SpriteKind::BedDesertHead
1051 | SpriteKind::BedCoastalHead
1052 | SpriteKind::BedSavannahHead => Some((Vec3::new(1.4, 0.0, 0.5), Vec3::unit_x())),
1053 SpriteKind::BedMesa => Some((Vec3::new(0.0, 0.0, 0.6), -Vec3::unit_y())),
1054 SpriteKind::BedrollSnow | SpriteKind::BedrollPirate => {
1055 Some((Vec3::new(0.0, 0.0, 0.1), -Vec3::unit_x()))
1056 },
1057 SpriteKind::Bedroll => Some((Vec3::new(0.0, 0.0, 0.1), Vec3::unit_y())),
1058 _ => None,
1059 }
1060 }
1061
1062 pub fn is_bed(&self) -> bool {
1063 matches!(
1064 self,
1065 SpriteKind::BedWoodWoodlandHead
1066 | SpriteKind::BedMesa
1067 | SpriteKind::BedCliffHead
1068 | SpriteKind::BedCoastalHead
1069 | SpriteKind::BedDesertHead
1070 | SpriteKind::BedSavannahHead
1071 | SpriteKind::Bedroll
1072 | SpriteKind::BedrollSnow
1073 | SpriteKind::BedrollPirate
1074 )
1075 }
1076
1077 #[inline]
1078 pub fn is_mountable(&self) -> bool { self.mount_offset().is_some() }
1079
1080 #[inline]
1081 pub fn is_controller(&self) -> bool { matches!(self, SpriteKind::Helm) }
1082
1083 #[inline]
1084 pub fn is_door(&self) -> bool {
1085 matches!(
1086 self,
1087 SpriteKind::Door | SpriteKind::DoorWide | SpriteKind::DoorDark
1088 )
1089 }
1090
1091 #[inline]
1093 pub fn mine_tool(&self) -> Option<ToolKind> {
1094 match self {
1095 SpriteKind::Velorite
1096 | SpriteKind::VeloriteFrag
1097 | SpriteKind::Amethyst
1099 | SpriteKind::Ruby
1100 | SpriteKind::Diamond
1101 | SpriteKind::Sapphire
1102 | SpriteKind::Emerald
1103 | SpriteKind::Topaz
1104 | SpriteKind::Bloodstone
1105 | SpriteKind::Coal
1106 | SpriteKind::Cobalt
1107 | SpriteKind::Copper
1108 | SpriteKind::Iron
1109 | SpriteKind::Tin
1110 | SpriteKind::Silver
1111 | SpriteKind::Gold => Some(ToolKind::Pick),
1112 SpriteKind::Grave | SpriteKind::Mud => Some(ToolKind::Shovel),
1113 _ => None,
1114 }
1115 }
1116
1117 pub fn required_mine_damage(&self) -> Option<u8> {
1118 Some(match self {
1119 SpriteKind::Gold => 6,
1120 SpriteKind::Silver => 6,
1121 SpriteKind::Bloodstone => 6,
1122 SpriteKind::Cobalt => 6,
1123 SpriteKind::Coal => 6,
1124 SpriteKind::Iron => 6,
1125 SpriteKind::Copper => 3,
1126 SpriteKind::Tin => 3,
1127 SpriteKind::Amethyst => 3,
1128 SpriteKind::Ruby => 6,
1129 SpriteKind::Sapphire => 3,
1130 SpriteKind::Emerald => 3,
1131 SpriteKind::Topaz => 3,
1132 SpriteKind::Diamond => 6,
1133 SpriteKind::Velorite => 3,
1134 SpriteKind::VeloriteFrag => 2,
1135 _ => return None,
1136 })
1137 }
1138
1139 pub fn mine_drop_interval(&self) -> u8 {
1142 match self {
1143 SpriteKind::Gold => 2,
1144 SpriteKind::Silver => 2,
1145 SpriteKind::Bloodstone => 2,
1146 SpriteKind::Cobalt => 2,
1147 SpriteKind::Coal => 2,
1148 SpriteKind::Iron => 2,
1149 SpriteKind::Copper => 1,
1150 SpriteKind::Tin => 1,
1151 SpriteKind::Emerald => 1,
1152 SpriteKind::Sapphire => 1,
1153 SpriteKind::Amethyst => 1,
1154 SpriteKind::Topaz => 1,
1155 SpriteKind::Diamond => 2,
1156 SpriteKind::Ruby => 2,
1157 SpriteKind::Velorite => 1,
1158 SpriteKind::VeloriteFrag => 1,
1159 _ => 1,
1160 }
1161 }
1162
1163 pub fn unlock_condition(&self, cfg: Option<SpriteCfg>) -> UnlockKind {
1169 cfg.and_then(|cfg| cfg.unlock)
1170 .unwrap_or_else(|| match self {
1171 SpriteKind::CommonLockedChest => UnlockKind::Consumes(
1172 ItemDefinitionIdOwned::Simple(String::from("common.items.utility.lockpick_0")),
1173 ),
1174 SpriteKind::SahaginKeyhole => UnlockKind::Consumes(ItemDefinitionIdOwned::Simple(
1175 String::from("common.items.keys.sahagin_key"),
1176 )),
1177 SpriteKind::BoneKeyhole => UnlockKind::Consumes(ItemDefinitionIdOwned::Simple(
1178 String::from("common.items.keys.bone_key"),
1179 )),
1180 SpriteKind::HaniwaKeyhole => UnlockKind::Consumes(ItemDefinitionIdOwned::Simple(
1181 String::from("common.items.keys.haniwa_key"),
1182 )),
1183 SpriteKind::VampireKeyhole => UnlockKind::Consumes(ItemDefinitionIdOwned::Simple(
1184 String::from("common.items.keys.vampire_key"),
1185 )),
1186 SpriteKind::GlassKeyhole => UnlockKind::Consumes(ItemDefinitionIdOwned::Simple(
1187 String::from("common.items.keys.glass_key"),
1188 )),
1189 SpriteKind::TerracottaChest => UnlockKind::Consumes(
1190 ItemDefinitionIdOwned::Simple(String::from(
1191 "common.items.keys.terracotta_key_chest",
1192 ))
1193 .to_owned(),
1194 ),
1195 SpriteKind::TerracottaKeyhole => UnlockKind::Consumes(
1196 ItemDefinitionIdOwned::Simple(String::from(
1197 "common.items.keys.terracotta_key_door",
1198 ))
1199 .to_owned(),
1200 ),
1201 SpriteKind::MyrmidonKeyhole => UnlockKind::Consumes(
1202 ItemDefinitionIdOwned::Simple(String::from("common.items.keys.myrmidon_key"))
1203 .to_owned(),
1204 ),
1205 SpriteKind::MinotaurKeyhole => UnlockKind::Consumes(
1206 ItemDefinitionIdOwned::Simple(String::from("common.items.keys.minotaur_key"))
1207 .to_owned(),
1208 ),
1209 _ => UnlockKind::Free,
1210 })
1211 }
1212
1213 pub fn content(&self, cfg: Option<SpriteCfg>) -> Option<Content> {
1215 cfg.and_then(|cfg| cfg.content)
1216 }
1217
1218 #[inline]
1220 pub fn has_ori(&self) -> bool { self.category().has_attr::<Ori>() }
1221}
1222
1223impl fmt::Display for SpriteKind {
1224 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "{:?}", self) }
1225}
1226
1227use strum::IntoEnumIterator;
1228
1229lazy_static! {
1230 pub static ref SPRITE_KINDS: HashMap<String, SpriteKind> =
1231 SpriteKind::iter().map(|sk| (sk.to_string(), sk)).collect();
1232}
1233
1234impl<'a> TryFrom<&'a str> for SpriteKind {
1235 type Error = ();
1236
1237 #[inline]
1238 fn try_from(s: &'a str) -> Result<Self, Self::Error> { SPRITE_KINDS.get(s).copied().ok_or(()) }
1239}
1240
1241#[derive(PartialEq, Clone, Debug, Serialize, Deserialize)]
1242pub enum UnlockKind {
1243 Free,
1245 Requires(ItemDefinitionIdOwned),
1248 Consumes(ItemDefinitionIdOwned),
1251}
1252
1253#[derive(Default, PartialEq, Clone, Debug, Serialize, Deserialize)]
1254pub struct SpriteCfg {
1255 pub unlock: Option<UnlockKind>,
1257 pub content: Option<Content>,
1262 pub loot_table: Option<String>,
1275}
1276
1277#[cfg(test)]
1278pub mod tests {
1279 use super::*;
1280
1281 #[test]
1282 fn sprite_conv_kind() {
1283 for sprite in SpriteKind::all() {
1284 let block = Block::air(*sprite);
1285 assert_eq!(block.sprite_category(), Some(sprite.category()));
1286 assert_eq!(block.get_sprite(), Some(*sprite));
1287 }
1288 }
1289
1290 #[test]
1291 fn sprite_attr() {
1292 for category in Category::all() {
1293 if category.has_attr::<Ori>() {
1294 for sprite in category.all_sprites() {
1295 for i in 0..4 {
1296 let block = Block::air(*sprite).with_attr(Ori(i)).unwrap();
1297 assert_eq!(block.get_attr::<Ori>().unwrap(), Ori(i));
1298 assert_eq!(block.get_sprite(), Some(*sprite));
1299 }
1300 }
1301 }
1302 if category.has_attr::<Growth>() {
1303 for sprite in category.all_sprites() {
1304 for i in 0..16 {
1305 let block = Block::air(*sprite).with_attr(Growth(i)).unwrap();
1306 assert_eq!(block.get_attr::<Growth>().unwrap(), Growth(i));
1307 assert_eq!(block.get_sprite(), Some(*sprite));
1308 }
1309 }
1310 }
1311 }
1312 }
1313}