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