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