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