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