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