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 CounterWoodEnd = 0xCF,
269 CounterWoodMiddle = 0xD0,
270 BenchWood2Middle = 0xD1,
271 BenchWood2Side = 0xD2,
272 BenchWood2Middle2 = 0xD3,
273 },
274 Plant = 3 has Growth, Owned, SnowCovered, Collectable {
276 BarrelCactus = 0x00,
278 RoundCactus = 0x01,
279 ShortCactus = 0x02,
280 MedFlatCactus = 0x03,
281 ShortFlatCactus = 0x04,
282 LargeCactus = 0x05,
283 TallCactus = 0x06,
284 BlueFlower = 0x10,
286 PinkFlower = 0x11,
287 PurpleFlower = 0x12,
288 RedFlower = 0x13,
289 WhiteFlower = 0x14,
290 YellowFlower = 0x15,
291 Sunflower = 0x16,
292 Moonbell = 0x17,
293 Pyrebloom = 0x18,
294 LushFlower = 0x19,
295 LanternFlower = 0x1A,
296 LongGrass = 0x20,
299 MediumGrass = 0x21,
300 ShortGrass = 0x22,
301 Fern = 0x23,
302 LargeGrass = 0x24,
303 Reed = 0x25,
304 TaigaGrass = 0x26,
305 GrassBlue = 0x27,
306 SavannaGrass = 0x28,
307 TallSavannaGrass = 0x29,
308 RedSavannaGrass = 0x2A,
309 SavannaBush = 0x2B,
310 Welwitch = 0x2C,
311 LeafyPlant = 0x2D,
312 DeadBush = 0x2E,
313 JungleFern = 0x2F,
314 GrassBlueShort = 0x30,
315 GrassBlueMedium = 0x31,
316 GrassBlueLong = 0x32,
317 CavernLillypadBlue = 0x33,
318 EnsnaringVines = 0x34,
319 LillyPads = 0x35,
320 JungleLeafyPlant = 0x36,
321 JungleRedGrass = 0x37,
322 LanternPlant = 0x38,
323 SporeReed = 0x39,
324 DeadPlant = 0x3A,
325 Corn = 0x41,
327 WheatYellow = 0x42,
328 WheatGreen = 0x43, LingonBerry = 0x44,
330 Blueberry = 0x45,
331 Lettuce = 0x46,
332 Pumpkin = 0x47,
333 Carrot = 0x48,
334 Tomato = 0x49,
335 Radish = 0x4A,
336 Turnip = 0x4B,
337 Flax = 0x4C,
338 Mushroom = 0x4D,
339 CaveMushroom = 0x4E,
340 Cotton = 0x4F,
341 WildFlax = 0x50,
342 SewerMushroom = 0x51,
343 LushMushroom = 0x52,
344 RockyMushroom = 0x53,
345 GlowMushroom = 0x54,
346 StonyCoral = 0x61,
348 SoftCoral = 0x62,
349 SeaweedTemperate = 0x63,
350 SeaweedTropical = 0x64,
351 GiantKelp = 0x65,
352 BullKelp = 0x66,
353 WavyAlgae = 0x67,
354 SeaGrapes = 0x68,
355 MermaidsFan = 0x69,
356 SeaAnemone = 0x6A,
357 Seagrass = 0x6B,
358 RedAlgae = 0x6C,
359 Liana = 0x71,
361 MycelBlue = 0x72,
362 CeilingMushroom = 0x73,
363 Mold = 0x74,
364 Root = 0x75,
365 CeilingLanternPlant = 0x76,
366 CeilingLanternFlower = 0x77,
367 CeilingJungleLeafyPlant = 0x78,
368 },
369 Resource = 4 has Owned, SnowCovered {
372 Twigs = 0x00,
375 Wood = 0x01,
376 Bamboo = 0x02,
377 Hardwood = 0x03,
378 Ironwood = 0x04,
379 Frostwood = 0x05,
380 Eldwood = 0x06,
381 Apple = 0x20,
383 Coconut = 0x21,
384 Stones = 0x22,
385 Seashells = 0x23,
386 Beehive = 0x24,
387 Bowl = 0x25,
388 PotionMinor = 0x26,
389 VialEmpty = 0x28,
391 LegoomLeaf = 0x29,
393 GnarlingCarving = 0x2A,
394 },
395 MineableResource = 5 has Damage {
396 Amethyst = 0x00,
397 Ruby = 0x01,
398 Sapphire = 0x02,
399 Emerald = 0x03,
400 Topaz = 0x04,
401 Diamond = 0x05,
402 Bloodstone = 0x06,
403 Coal = 0x07,
404 Cobalt = 0x08,
405 Copper = 0x09,
406 Iron = 0x0A,
407 Tin = 0x0B,
408 Silver = 0x0C,
409 Gold = 0x0D,
410 Velorite = 0x0E,
411 VeloriteFrag = 0x0F,
412 Mud = 0x10,
413 Grave = 0x11,
414 Lodestone = 0x12,
415 },
416 Structural = 6 has Ori {
418 Door = 0x00,
420 DoorDark = 0x01,
421 DoorWide = 0x02,
422 BoneKeyhole = 0x03,
423 BoneKeyDoor = 0x04,
424 Keyhole = 0x05,
425 KeyDoor = 0x06,
426 GlassKeyhole = 0x07,
427 KeyholeBars = 0x08,
428 HaniwaKeyDoor = 0x09,
429 HaniwaKeyhole = 0x0A,
430 TerracottaKeyDoor = 0x0B,
431 TerracottaKeyhole = 0x0C,
432 SahaginKeyhole = 0x0D,
433 SahaginKeyDoor = 0x0E,
434 VampireKeyDoor = 0x0F,
435 VampireKeyhole = 0x10,
436 MyrmidonKeyDoor = 0x11,
437 MyrmidonKeyhole = 0x12,
438 MinotaurKeyhole = 0x13,
439
440 Window1 = 0x14,
442 Window2 = 0x15,
443 Window3 = 0x16,
444 Window4 = 0x17,
445 WitchWindow = 0x18,
446 WindowArabic = 0x19,
447 GlassBarrier = 0x20,
449 SeaDecorBlock = 0x21,
450 CliffDecorBlock = 0x22,
451 MagicalBarrier = 0x23,
452 OneWayWall = 0x24,
453 SeaDecorWindowHor = 0x30,
455 SeaDecorWindowVer = 0x31,
456 DropGate = 0x32,
457 DropGateBottom = 0x33,
458 WoodBarricades = 0x34,
459 Rope = 0x40,
461 SeaDecorChain = 0x41,
462 IronSpike = 0x42,
463 DoorBars = 0x43,
464 HaniwaTrap = 0x44,
465 HaniwaTrapTriggered = 0x45,
466 TerracottaStatue = 0x46,
467 TerracottaBlock = 0x47,
468 MetalChain = 0x48,
469 Bell = 0x49,
470 DoorWideAirship = 0x4A,
471 FencePost = 0x50,
472 },
473 Decor = 7 has Ori {
475 Bones = 0x00,
477 IceCrystal = 0x01,
478 GlowIceCrystal = 0x02,
479 CrystalHigh = 0x03,
480 CrystalLow = 0x04,
481 UnderwaterVent = 0x05,
482 SeaUrchin = 0x06,
483 IceSpike = 0x07,
484 Orb = 0x08,
485 EnsnaringWeb = 0x09,
486 DiamondLight = 0x0A,
487
488 Gravestone = 0x10,
490 Melon = 0x11,
491 ForgeTools = 0x12,
492 JugAndBowlArabic = 0x13,
493 JugArabic = 0x14,
494 DecorSetArabic = 0x15,
495 SepareArabic = 0x16,
496 Candle = 0x17,
497 SmithingTable = 0x18,
498 Forge0 = 0x19,
499 GearWheel0 = 0x1A,
500 Quench0 = 0x1B,
501 SeaDecorEmblem = 0x1C,
502 SeaDecorPillar = 0x1D,
503 MagicalSeal = 0x1E,
504 JugAndCupsCoastal = 0x1F,
505 },
506 Lamp = 8 has Ori, LightEnabled {
507 Lantern = 0x00,
509 StreetLamp = 0x01,
510 StreetLampTall = 0x02,
511 SeashellLantern = 0x03,
512 FireBowlGround = 0x04,
513 MesaLantern = 0x05,
514 LanternpostWoodLantern = 0x06,
515 LampMetalShinglesRed = 0x07,
516 LampTerracotta = 0x08,
517 LampMetalShinglesCyan = 0x09,
518 LanternAirshipWallBlackS = 0x0A,
519 LanternAirshipWallBrownS = 0x0B,
520 LanternAirshipWallChestnutS = 0x0C,
521 LanternAirshipWallRedS = 0x0D,
522 LanternAirshipGroundBlackS = 0x0E,
523 LanternAirshipGroundBrownS = 0x0F,
524 LanternAirshipGroundChestnutS = 0x10,
525 LanternAirshipGroundRedS = 0x11,
526 },
527 Container = 9 has Ori, Owned, Collectable {
532 Chest = 0x00,
533 DungeonChest0 = 0x01,
534 DungeonChest1 = 0x02,
535 DungeonChest2 = 0x03,
536 DungeonChest3 = 0x04,
537 DungeonChest4 = 0x05,
538 DungeonChest5 = 0x06,
539 CoralChest = 0x07,
540 HaniwaUrn = 0x08,
541 TerracottaChest = 0x09,
542 SahaginChest = 0x0A,
543 CommonLockedChest = 0x0B,
544 ChestBuried = 0x0C,
545 Crate = 0x0D,
546 WitchChest = 0x10,
549 PirateChest = 0x11,
550 },
551 Modular = 10 has Ori, AdjacentType {
552 FenceWoodWoodland = 0x00,
553 }
554}
555
556attributes! {
557 Ori { bits: 3, err: Infallible, from: |bits| Ok(Self(bits as u8)), into: |Ori(x)| x as u16 },
558 MirrorX { bits: 1, err: Infallible, from: |bits| Ok(Self(bits == 1)), into: |MirrorX(x)| x as u16 },
559 MirrorY { bits: 1, err: Infallible, from: |bits| Ok(Self(bits == 1)), into: |MirrorY(x)| x as u16 },
560 MirrorZ { bits: 1, err: Infallible, from: |bits| Ok(Self(bits == 1)), into: |MirrorZ(x)| x as u16 },
561 Growth { bits: 4, err: Infallible, from: |bits| Ok(Self(bits as u8)), into: |Growth(x)| x as u16 },
562 LightEnabled { bits: 1, err: Infallible, from: |bits| Ok(Self(bits == 1)), into: |LightEnabled(x)| x as u16 },
563 Collectable { bits: 1, err: Infallible, from: |bits| Ok(Self(bits == 1)), into: |Collectable(x)| x as u16 },
564 Damage { bits: 3, err: Infallible, from: |bits| Ok(Self(bits as u8)), into: |Damage(x)| x as u16 },
565 Owned { bits: 1, err: Infallible, from: |bits| Ok(Self(bits == 1)), into: |Owned(x)| x as u16 },
566 AdjacentType { bits: 3, err: Infallible, from: |bits| Ok(Self(bits as u8)), into: |AdjacentType(x)| x as u16 },
567 SnowCovered { bits: 1, err: Infallible, from: |bits| Ok(Self(bits == 1)), into: |SnowCovered(x)| x as u16 },
568}
569
570#[derive(Copy, Clone, Default, Debug, PartialEq, Eq, Deserialize)]
572pub struct Ori(pub u8);
573
574#[derive(Copy, Clone, Default, Debug, PartialEq, Eq, Deserialize)]
575pub struct MirrorX(pub bool);
576
577#[derive(Copy, Clone, Default, Debug, PartialEq, Eq, Deserialize)]
578pub struct MirrorY(pub bool);
579
580#[derive(Copy, Clone, Default, Debug, PartialEq, Eq, Deserialize)]
581pub struct MirrorZ(pub bool);
582
583#[derive(Copy, Clone, Debug, PartialEq, Eq, Deserialize)]
585pub struct Growth(pub u8);
586
587impl Default for Growth {
588 fn default() -> Self { Self(15) }
589}
590
591#[derive(Copy, Clone, Debug, PartialEq, Eq, Deserialize)]
593pub struct LightEnabled(pub bool);
594
595impl Default for LightEnabled {
596 fn default() -> Self { Self(true) }
597}
598
599#[derive(Copy, Clone, Debug, PartialEq, Eq, Deserialize)]
600pub struct Collectable(pub bool);
601
602impl Default for Collectable {
603 fn default() -> Self { Self(true) }
604}
605
606#[derive(Default, Copy, Clone, Debug, PartialEq, Eq, Deserialize)]
607pub struct Owned(pub bool);
608
609#[derive(Default, Copy, Clone, Debug, PartialEq, Eq, Deserialize, FromPrimitive, Hash)]
619#[repr(u8)]
620pub enum RelativeNeighborPosition {
621 #[default]
622 I,
623 L,
624 T,
625 X,
626 End,
627}
628
629#[derive(Copy, Clone, Debug, PartialEq, Eq, Deserialize)]
630#[serde(from = "RelativeNeighborPosition")]
631pub struct AdjacentType(pub u8);
632
633impl From<RelativeNeighborPosition> for AdjacentType {
634 fn from(value: RelativeNeighborPosition) -> Self { Self(value as u8) }
635}
636
637impl Default for AdjacentType {
638 fn default() -> Self { Self::from(RelativeNeighborPosition::I) }
639}
640
641#[derive(Copy, Clone, Debug, PartialEq, Eq, Default, Deserialize)]
643pub struct Damage(pub u8);
644
645#[derive(Copy, Clone, Default, Debug, PartialEq, Eq, Deserialize)]
647pub struct SnowCovered(pub bool);
648
649#[derive(Clone, Copy, Debug)]
650pub enum SpriteAdjecencyRequirement {
651 AllSolid(&'static [Vec3<i32>]),
653 AnySolid(&'static [Vec3<i32>]),
655}
656
657impl SpriteKind {
658 #[inline]
659 pub fn solid_height(&self) -> Option<f32> {
661 Some(match self {
664 SpriteKind::Bedroll => 0.3,
665 SpriteKind::BedrollSnow => 0.4,
666 SpriteKind::BedrollPirate => 0.3,
667 SpriteKind::Tomato => 1.65,
668 SpriteKind::BarrelCactus => 0.909,
669 SpriteKind::LargeCactus => 3.0,
670 SpriteKind::TallCactus => 2.63,
671 SpriteKind::Scarecrow => 3.0,
672 SpriteKind::Turnip => 0.36,
673 SpriteKind::Pumpkin => 0.81,
674 SpriteKind::Chest => 1.09,
675 SpriteKind::CommonLockedChest
676 | SpriteKind::DungeonChest0
677 | SpriteKind::DungeonChest1
678 | SpriteKind::DungeonChest2
679 | SpriteKind::DungeonChest3
680 | SpriteKind::DungeonChest4
681 | SpriteKind::DungeonChest5
682 | SpriteKind::CoralChest
683 | SpriteKind::HaniwaUrn
684 | SpriteKind::SahaginChest
685 | SpriteKind::TerracottaChest
686 | SpriteKind::WitchChest
687 | SpriteKind::PirateChest => 1.09,
688 SpriteKind::TerracottaStatue => 5.29,
689 SpriteKind::TerracottaBlock => 1.00,
690 SpriteKind::FenceWoodWoodland => 1.09,
692 SpriteKind::SeaDecorChain => 1.09,
693 SpriteKind::CounterWoodMiddle => 1.00,
694 SpriteKind::SeaDecorBlock => 1.00,
695 SpriteKind::SeaDecorWindowHor => 0.55,
696 SpriteKind::SeaDecorWindowVer => 1.09,
697 SpriteKind::SeaDecorPillar => 2.55,
698 SpriteKind::SeashellLantern => 2.09,
699 SpriteKind::MesaLantern => 1.3,
700 SpriteKind::Rope => 1.09,
701 SpriteKind::MetalChain => 1.09,
702 SpriteKind::FencePost => 1.09,
703 SpriteKind::StreetLamp => 2.65,
704 SpriteKind::Carrot => 0.18,
705 SpriteKind::Radish => 0.18,
706 SpriteKind::FireBowlGround => 0.55,
707 SpriteKind::BedMesa => 0.82,
708 SpriteKind::DungeonWallDecor => 1.0,
709 SpriteKind::Planter => 1.09,
710 SpriteKind::WardrobeSingleMesa => 2.0,
711 SpriteKind::WardrobeDoubleMesa => 2.0,
712 SpriteKind::MirrorMesa => 2.0,
713 SpriteKind::Mud => 0.36,
714 SpriteKind::ChestBuried => 0.91,
715 SpriteKind::StonyCoral => 1.4,
716 SpriteKind::CraftingBench => 1.18,
717 SpriteKind::Forge => 1.818,
718 SpriteKind::Cauldron => 1.27,
719 SpriteKind::SpinningWheel => 1.454,
720 SpriteKind::TanningRack => 1.363,
721 SpriteKind::Loom => 1.545,
722 SpriteKind::Anvil => 1.18,
723 SpriteKind::CookingPot => 1.091,
724 SpriteKind::DismantlingBench => 1.091,
725 SpriteKind::IceSpike => 1.0,
726 SpriteKind::RepairBench => 1.2,
727 SpriteKind::RoundCactus => 0.72,
728 SpriteKind::ShortCactus => 1.36,
729 SpriteKind::MedFlatCactus => 1.36,
730 SpriteKind::ShortFlatCactus => 0.91,
731 SpriteKind::Bell => 1.0,
732 SpriteKind::Apple
734 | SpriteKind::Beehive
735 | SpriteKind::Velorite
736 | SpriteKind::VeloriteFrag
737 | SpriteKind::Coconut
738 | SpriteKind::StreetLampTall
739 | SpriteKind::Window1
740 | SpriteKind::Window2
741 | SpriteKind::Window3
742 | SpriteKind::Window4
743 | SpriteKind::DropGate
744 | SpriteKind::WitchWindow
745 | SpriteKind::SeaUrchin
746 | SpriteKind::IronSpike
747 | SpriteKind::GlassBarrier
748 | SpriteKind::GlassKeyhole
749 | SpriteKind::Keyhole
750 | SpriteKind::KeyDoor
751 | SpriteKind::BoneKeyhole
752 | SpriteKind::BoneKeyDoor
753 | SpriteKind::HaniwaKeyhole
754 | SpriteKind::HaniwaKeyDoor
755 | SpriteKind::SahaginKeyhole
756 | SpriteKind::SahaginKeyDoor
757 | SpriteKind::VampireKeyhole
758 | SpriteKind::VampireKeyDoor
759 | SpriteKind::HaniwaTrap
760 | SpriteKind::HaniwaTrapTriggered
761 | SpriteKind::TerracottaKeyDoor
762 | SpriteKind::TerracottaKeyhole
763 | SpriteKind::MyrmidonKeyDoor
764 | SpriteKind::MyrmidonKeyhole
765 | SpriteKind::MinotaurKeyhole
766 | SpriteKind::Bomb
767 | SpriteKind::OneWayWall
768 | SpriteKind::DoorBars
769 | SpriteKind::KeyholeBars
770 | SpriteKind::WoodBarricades
771 | SpriteKind::DiamondLight
772 | SpriteKind::CrystalBall
773 | SpriteKind::Ladder => 1.0,
774 SpriteKind::Shelf => 1.0,
776 SpriteKind::Lantern => 0.9,
777 SpriteKind::CrystalHigh | SpriteKind::CrystalLow => 1.5,
778 SpriteKind::Bloodstone
779 | SpriteKind::Coal
780 | SpriteKind::Cobalt
781 | SpriteKind::Copper
782 | SpriteKind::Iron
783 | SpriteKind::Tin
784 | SpriteKind::Silver
785 | SpriteKind::Gold
786 | SpriteKind::Lodestone => 0.6,
787 SpriteKind::EnsnaringVines
788 | SpriteKind::CavernLillypadBlue
789 | SpriteKind::EnsnaringWeb => 0.15,
790 SpriteKind::LillyPads => 0.1,
791 SpriteKind::WindowArabic | SpriteKind::BookshelfArabic => 1.9,
792 SpriteKind::DecorSetArabic => 2.6,
793 SpriteKind::SepareArabic => 2.2,
794 SpriteKind::CushionArabic => 0.4,
795 SpriteKind::JugArabic => 1.4,
796 SpriteKind::TableArabicSmall => 0.9,
797 SpriteKind::TableArabicLarge => 1.0,
798 SpriteKind::TableCoastalLarge => 1.0,
799 SpriteKind::BenchCoastal => 1.0,
800 SpriteKind::CanapeArabic => 1.2,
801 SpriteKind::CupboardArabic => 4.5,
802 SpriteKind::WallTableArabic => 2.3,
803 SpriteKind::JugAndBowlArabic => 1.4,
804 SpriteKind::JugAndCupsCoastal => 1.4,
805 SpriteKind::Melon => 0.7,
806 SpriteKind::OvenArabic => 3.2,
807 SpriteKind::FountainArabic => 2.4,
808 SpriteKind::Hearth => 2.3,
809 SpriteKind::ForgeTools => 2.8,
810 SpriteKind::CliffDecorBlock | SpriteKind::FireBlock => 1.0,
811 SpriteKind::Wood
812 | SpriteKind::Hardwood
813 | SpriteKind::Ironwood
814 | SpriteKind::Frostwood
815 | SpriteKind::Eldwood => 7.0 / 11.0,
816 SpriteKind::Bamboo => 9.0 / 11.0,
817 SpriteKind::MagicalBarrier => 3.0,
818 SpriteKind::MagicalSeal => 1.0,
819 SpriteKind::Helm => 1.909,
820 SpriteKind::Sign => 16.0 / 11.0,
821 SpriteKind::SmithingTable => 13.0 / 11.0,
822 SpriteKind::Forge0 => 17.0 / 11.0,
823 SpriteKind::GearWheel0 => 3.0 / 11.0,
824 SpriteKind::Quench0 => 8.0 / 11.0,
825 SpriteKind::HotSurface => 0.01,
826 SpriteKind::Barrel => 1.0,
827 SpriteKind::CrateBlock => 1.0,
828 SpriteKind::BarrelWoodWater | SpriteKind::BarrelWoodCoal => 1.545,
829 SpriteKind::LanternpostWoodLantern | SpriteKind::LanternpostWoodUpper => 2.000,
830 SpriteKind::LanternpostWoodBase => 3.000,
831 SpriteKind::LampMetalShinglesRed => 1.000,
832 SpriteKind::LampMetalShinglesCyan => 1.000,
833 SpriteKind::LampMetalBase => 2.818,
834 SpriteKind::LampTerracotta => 1.727,
835 SpriteKind::BlacksmithBellows => 0.545,
836 SpriteKind::CarpenterTable => 2.000,
837 SpriteKind::CarpenterCrateWoodS => 0.727,
838 SpriteKind::CarpenterCrateWoodL => 1.273,
839 SpriteKind::CarpenterLogCutter => 1.545,
840 SpriteKind::BasketWovenL | SpriteKind::JugClayM => 1.000,
841 SpriteKind::BasketWovenM => 0.909,
842 SpriteKind::BasketWovenS => 0.818,
843 SpriteKind::BonfireMLit | SpriteKind::BonfireMUnlit => 2.273,
844 SpriteKind::BucketWoodM | SpriteKind::SackLeatherM => 1.091,
845 SpriteKind::MirrorWoodM => 1.364,
846 SpriteKind::TrophyframeWoodBear => 1.455,
847 SpriteKind::TrophyframeWoodDeer => 1.727,
848 SpriteKind::ChestWoodDouble => 1.182,
849 SpriteKind::DiningtableWoodCorner => 1.273,
850 SpriteKind::DiningtableWoodBody => 1.273,
851 SpriteKind::BenchWoodEnd => 0.636,
852 SpriteKind::BenchWoodMiddle => 0.636,
853 SpriteKind::LogsWoodCoreEnd => 0.818,
854 SpriteKind::LogsWoodCoreMiddle => 0.818,
855 SpriteKind::LogsWoodBarkEnd => 1.091,
856 SpriteKind::LogsWoodBarkMiddle => 1.091,
857 SpriteKind::LogsWoodBranchEnd => 1.091,
858 SpriteKind::LogsWoodBranchMiddle => 1.091,
859 SpriteKind::LogsWoodBranchS => 1.091,
860 SpriteKind::SeatWoodBlueMiddle => 1.818,
861 SpriteKind::SeatWoodBlueSide => 1.818,
862 SpriteKind::LanternAirshipWallBlackS
863 | SpriteKind::LanternAirshipWallBrownS
864 | SpriteKind::LanternAirshipWallChestnutS
865 | SpriteKind::LanternAirshipWallRedS => 1.182,
866 SpriteKind::LanternAirshipGroundBlackS
867 | SpriteKind::LanternAirshipGroundBrownS
868 | SpriteKind::LanternAirshipGroundChestnutS
869 | SpriteKind::LanternAirshipGroundRedS => 0.909,
870 SpriteKind::RopeCoilM => 0.363,
871 SpriteKind::BedCliffHead => 0.636,
872 SpriteKind::BedCliffMiddle => 0.636,
873 SpriteKind::BedCliffTail => 0.636,
874 SpriteKind::BedCoastalHead => 0.636,
875 SpriteKind::BedCoastalMiddle => 0.636,
876 SpriteKind::BedCoastalTail => 0.636,
877 SpriteKind::BedDesertHead => 0.545,
878 SpriteKind::BedDesertMiddle => 0.545,
879 SpriteKind::BedDesertTail => 0.545,
880 SpriteKind::BedSavannahHead => 0.545,
881 SpriteKind::BedSavannahMiddle => 0.545,
882 SpriteKind::BedSavannahTail => 0.545,
883 SpriteKind::BedWoodWoodlandHead => 0.727,
884 SpriteKind::BedWoodWoodlandMiddle => 0.727,
885 SpriteKind::BedWoodWoodlandTail => 0.727,
886 SpriteKind::BookshelfEnd => 3.0,
887 SpriteKind::BookshelfMiddle => 3.0,
888 SpriteKind::BenchWoodWoodlandGreen1 => 1.545,
889 SpriteKind::BenchWoodWoodlandGreen2 => 1.545,
890 SpriteKind::BenchWoodWoodlandGreen3 => 1.545,
891 SpriteKind::BenchWoodWoodland => 1.545,
892 SpriteKind::ChairWoodWoodland => 1.636,
893 SpriteKind::ChairWoodWoodland2 => 1.727,
894 SpriteKind::CoatrackMetalWoodland => 2.364,
895 SpriteKind::CoatrackWoodWoodland => 2.364,
896 SpriteKind::Crate => 0.909,
897 SpriteKind::DrawerWoodWoodlandS => 1.000,
898 SpriteKind::DrawerWoodWoodlandM1 => 0.909,
899 SpriteKind::DrawerWoodWoodlandM2 => 0.909,
900 SpriteKind::DrawerWoodWoodlandL1 => 1.273,
901 SpriteKind::DrawerWoodWoodlandL2 => 1.273,
902 SpriteKind::DiningtableWoodWoodlandRound => 1.273,
903 SpriteKind::DiningtableWoodWoodlandSquare => 1.273,
904 SpriteKind::TableWoodFancyWoodlandCorner => 1.273,
905 SpriteKind::TableWoodFancyWoodlandBody => 1.273,
906 SpriteKind::WardrobesingleWoodWoodland => 2.364,
907 SpriteKind::WardrobesingleWoodWoodland2 => 2.364,
908 SpriteKind::WardrobedoubleWoodWoodland => 2.364,
909 SpriteKind::WardrobedoubleWoodWoodland2 => 2.364,
910 SpriteKind::FlowerpotWoodWoodlandS => 0.455,
911 SpriteKind::HandCartWoodHead => 1.091,
912 SpriteKind::HandCartWoodMiddle => 1.091,
913 SpriteKind::HandCartWoodTail => 1.091,
914 SpriteKind::HandrailWoodWoodlandBase | SpriteKind::HandrailWoodWoodlandMiddle => 1.727,
915 SpriteKind::HandrailWoodWoodlandTop => 1.181,
916 SpriteKind::Hay => 1.09,
917 SpriteKind::BenchWood2Middle
918 | SpriteKind::BenchWood2Side
919 | SpriteKind::BenchWood2Middle2 => 0.636,
920 _ => return None,
921 })
922 }
923
924 pub const fn adjecency_requirement(&self) -> Option<SpriteAdjecencyRequirement> {
925 use SpriteAdjecencyRequirement::*;
926 const NEG_X: Vec3<i32> = Vec3::new(-1, 0, 0);
927 const Z: Vec3<i32> = Vec3::new(0, 0, 1);
928 const NEG_Z: Vec3<i32> = Vec3::new(0, 0, -1);
929
930 Some(match self {
931 Self::Beehive
932 | Self::CeilingMushroom
933 | Self::CeilingLanternPlant
934 | Self::CeilingLanternFlower
935 | Self::CeilingJungleLeafyPlant => AllSolid(&[Z]),
936 Self::Apple | Self::Coconut => AnySolid(&[Z, NEG_Z]),
937
938 _ if matches!(self.category(), Category::Plant) => AllSolid(&[NEG_Z]),
939 Self::Lantern | Self::LanternpostWoodBase | Self::LanternpostWoodUpper | Self::Bomb => {
940 AllSolid(&[NEG_Z])
941 },
942
943 Self::LanternpostWoodLantern => AllSolid(&[NEG_X]),
944
945 _ => return None,
946 })
947 }
948
949 pub fn valid_collision_dir(
950 &self,
951 entity_aabb: Aabb<f32>,
952 block_aabb: Aabb<f32>,
953 move_dir: Vec3<f32>,
954 parent: &Block,
955 ) -> bool {
956 let dir = entity_aabb.collision_vector_with_aabb(block_aabb);
958
959 let max_axis = dir.map(|e| e.abs()).reduce_partial_min();
962
963 let resolve_dir = -dir.map(|e| {
964 if e.abs().to_bits() == max_axis.to_bits() {
965 e.signum()
966 } else {
967 0.0
968 }
969 });
970
971 let is_moving_into = move_dir.dot(resolve_dir) <= 0.0;
972
973 match self {
974 SpriteKind::LillyPads | SpriteKind::CavernLillypadBlue => {
975 is_moving_into && resolve_dir.z > 0.0
976 },
977 SpriteKind::OneWayWall => {
978 is_moving_into
979 && parent.get_attr().is_ok_and(|Ori(ori)| {
980 Vec2::new(
981 0.0,
982 parent.get_attr::<MirrorY>().map_or(1.0, |m| match m.0 {
983 true => -1.0,
984 false => 1.0,
985 }),
986 )
987 .rotated_z(std::f32::consts::PI * 0.25 * ori as f32)
988 .with_z(0.0)
989 .map2(resolve_dir, |e, r| (e - r).abs() < 0.1)
990 .reduce_and()
991 })
992 },
993 _ => true,
994 }
995 }
996
997 #[inline]
1006 pub fn default_loot_spec(&self) -> Option<Option<LootSpec<&'static str>>> {
1007 let item = LootSpec::Item;
1008 let table = LootSpec::LootTable;
1009 Some(Some(match self {
1010 SpriteKind::Apple => item("common.items.food.apple"),
1011 SpriteKind::Mushroom => item("common.items.food.mushroom"),
1012 SpriteKind::Velorite => item("common.items.mineral.ore.velorite"),
1013 SpriteKind::VeloriteFrag => item("common.items.mineral.ore.veloritefrag"),
1014 SpriteKind::Lodestone => item("common.items.mineral.ore.lodestone"),
1015 SpriteKind::RedFlower => item("common.items.flowers.red"),
1019 SpriteKind::Sunflower => item("common.items.flowers.sunflower"),
1022 SpriteKind::Lettuce => item("common.items.food.lettuce"),
1026 SpriteKind::Coconut => item("common.items.food.coconut"),
1027 SpriteKind::Beehive => item("common.items.crafting_ing.honey"),
1028 SpriteKind::Stones => item("common.items.crafting_ing.stones"),
1029 SpriteKind::Twigs => item("common.items.crafting_ing.twigs"),
1030 SpriteKind::VialEmpty => item("common.items.crafting_ing.empty_vial"),
1031 SpriteKind::Bowl => item("common.items.crafting_ing.bowl"),
1032 SpriteKind::PotionMinor => item("common.items.consumable.potion_minor"),
1033 SpriteKind::Amethyst => item("common.items.mineral.gem.amethyst"),
1034 SpriteKind::Ruby => item("common.items.mineral.gem.ruby"),
1035 SpriteKind::Diamond => item("common.items.mineral.gem.diamond"),
1036 SpriteKind::Sapphire => item("common.items.mineral.gem.sapphire"),
1037 SpriteKind::Topaz => item("common.items.mineral.gem.topaz"),
1038 SpriteKind::Emerald => item("common.items.mineral.gem.emerald"),
1039 SpriteKind::Bloodstone => item("common.items.mineral.ore.bloodstone"),
1040 SpriteKind::Coal => item("common.items.mineral.ore.coal"),
1041 SpriteKind::Cobalt => item("common.items.mineral.ore.cobalt"),
1042 SpriteKind::Copper => item("common.items.mineral.ore.copper"),
1043 SpriteKind::Iron => item("common.items.mineral.ore.iron"),
1044 SpriteKind::Tin => item("common.items.mineral.ore.tin"),
1045 SpriteKind::Silver => item("common.items.mineral.ore.silver"),
1046 SpriteKind::Gold => item("common.items.mineral.ore.gold"),
1047 SpriteKind::Cotton => item("common.items.crafting_ing.cotton_boll"),
1048 SpriteKind::Moonbell => item("common.items.flowers.moonbell"),
1049 SpriteKind::Pyrebloom => item("common.items.flowers.pyrebloom"),
1050 SpriteKind::WildFlax => item("common.items.flowers.wild_flax"),
1051 SpriteKind::Seashells => item("common.items.crafting_ing.seashells"),
1052 SpriteKind::RoundCactus => item("common.items.crafting_ing.cactus"),
1053 SpriteKind::ShortFlatCactus => item("common.items.crafting_ing.cactus"),
1054 SpriteKind::MedFlatCactus => item("common.items.crafting_ing.cactus"),
1055 SpriteKind::Bomb => item("common.items.utility.bomb"),
1056 SpriteKind::Chest => table("common.loot_tables.sprite.chest"),
1057 SpriteKind::DungeonChest0 => table("common.loot_tables.dungeon.gnarling.chest"),
1058 SpriteKind::DungeonChest1 => table("common.loot_tables.dungeon.adlet.chest"),
1059 SpriteKind::DungeonChest2 => table("common.loot_tables.dungeon.sahagin.chest"),
1060 SpriteKind::DungeonChest3 => table("common.loot_tables.dungeon.haniwa.chest"),
1061 SpriteKind::DungeonChest4 => table("common.loot_tables.dungeon.myrmidon.chest"),
1062 SpriteKind::DungeonChest5 => table("common.loot_tables.dungeon.cultist.chest"),
1063 SpriteKind::CoralChest => table("common.loot_tables.dungeon.sea_chapel.chest_coral"),
1064 SpriteKind::HaniwaUrn => table("common.loot_tables.dungeon.haniwa.key"),
1065 SpriteKind::TerracottaChest => {
1066 table("common.loot_tables.dungeon.terracotta.chest_terracotta")
1067 },
1068 SpriteKind::SahaginChest => table("common.loot_tables.dungeon.sahagin.key_chest"),
1069 SpriteKind::CommonLockedChest => table("common.loot_tables.dungeon.sahagin.chest"),
1070 SpriteKind::ChestBuried => table("common.loot_tables.sprite.chest-buried"),
1071 SpriteKind::Crate => table("common.loot_tables.sprite.crate"),
1072 SpriteKind::Mud => table("common.loot_tables.sprite.mud"),
1073 SpriteKind::Grave => table("common.loot_tables.sprite.mud"),
1074 SpriteKind::Wood => item("common.items.log.wood"),
1075 SpriteKind::Bamboo => item("common.items.log.bamboo"),
1076 SpriteKind::Hardwood => item("common.items.log.hardwood"),
1077 SpriteKind::Ironwood => item("common.items.log.ironwood"),
1078 SpriteKind::Frostwood => item("common.items.log.frostwood"),
1079 SpriteKind::Eldwood => item("common.items.log.eldwood"),
1080 SpriteKind::LegoomLeaf => item("common.items.quest.legoom_leaf"),
1081 SpriteKind::GnarlingCarving => item("common.items.quest.gnarling_carving"),
1082 SpriteKind::MagicalBarrier => table("common.loot_tables.sprite.chest"),
1084 SpriteKind::WitchChest => table("common.loot_tables.spot.witch"),
1085 SpriteKind::PirateChest => table("common.loot_tables.spot.buccaneer"),
1086 SpriteKind::Keyhole
1087 | SpriteKind::BoneKeyhole
1088 | SpriteKind::HaniwaKeyhole
1089 | SpriteKind::VampireKeyhole
1090 | SpriteKind::GlassKeyhole
1091 | SpriteKind::KeyholeBars
1092 | SpriteKind::SahaginKeyhole
1093 | SpriteKind::TerracottaKeyhole
1094 | SpriteKind::MyrmidonKeyhole
1095 | SpriteKind::MinotaurKeyhole => {
1096 return Some(None);
1097 },
1098 _ => return None,
1099 }))
1100 }
1101
1102 #[inline]
1110 pub fn collectible_info(&self) -> Option<Option<ToolKind>> {
1111 self.default_loot_spec().map(|_| self.mine_tool())
1112 }
1113
1114 #[inline]
1132 pub fn is_defined_as_container(&self) -> bool { self.category() == Category::Container }
1133
1134 #[inline]
1144 pub fn should_drop_mystery(&self) -> bool {
1145 self.is_defined_as_container()
1146 || matches!(
1147 self.default_loot_spec(),
1148 Some(Some(LootSpec::LootTable { .. } | LootSpec::Lottery { .. }))
1149 )
1150 }
1151
1152 #[inline]
1154 pub fn mount_offset(&self) -> Option<(Vec3<f32>, Vec3<f32>)> {
1156 match self {
1157 SpriteKind::ChairWoodWoodland
1158 | SpriteKind::ChairWoodWoodland2
1159 | SpriteKind::BenchWoodWoodlandGreen1
1160 | SpriteKind::BenchWoodWoodlandGreen2
1161 | SpriteKind::BenchWoodWoodlandGreen3
1162 | SpriteKind::BenchWoodWoodland
1163 | SpriteKind::BenchWoodEnd
1164 | SpriteKind::BenchWoodMiddle
1165 | SpriteKind::BenchCoastal => Some((Vec3::new(0.0, 0.0, 0.5), Vec3::unit_x())),
1166 SpriteKind::SeatWoodBlueMiddle | SpriteKind::SeatWoodBlueSide => {
1167 Some((Vec3::new(0.4, 0.0, 0.5), Vec3::unit_x()))
1168 },
1169 SpriteKind::BenchWood2Middle
1170 | SpriteKind::BenchWood2Side
1171 | SpriteKind::BenchWood2Middle2 => Some((Vec3::new(0.4, 0.0, 0.62), Vec3::unit_x())),
1172 SpriteKind::Helm => Some((Vec3::new(0.0, -1.1, 0.0), Vec3::unit_y())),
1173 SpriteKind::BedWoodWoodlandHead
1174 | SpriteKind::BedCliffHead
1175 | SpriteKind::BedDesertHead
1176 | SpriteKind::BedCoastalHead
1177 | SpriteKind::BedSavannahHead => Some((Vec3::new(1.4, 0.0, 0.5), Vec3::unit_x())),
1178 SpriteKind::BedMesa => Some((Vec3::new(0.0, 0.0, 0.6), -Vec3::unit_y())),
1179 SpriteKind::BedrollSnow | SpriteKind::BedrollPirate => {
1180 Some((Vec3::new(0.0, 0.0, 0.1), -Vec3::unit_x()))
1181 },
1182 SpriteKind::Bedroll => Some((Vec3::new(0.0, 0.0, 0.1), Vec3::unit_y())),
1183 _ => None,
1184 }
1185 }
1186
1187 pub fn is_bed(&self) -> bool {
1188 matches!(
1189 self,
1190 SpriteKind::BedWoodWoodlandHead
1191 | SpriteKind::BedMesa
1192 | SpriteKind::BedCliffHead
1193 | SpriteKind::BedCoastalHead
1194 | SpriteKind::BedDesertHead
1195 | SpriteKind::BedSavannahHead
1196 | SpriteKind::Bedroll
1197 | SpriteKind::BedrollSnow
1198 | SpriteKind::BedrollPirate
1199 )
1200 }
1201
1202 #[inline]
1203 pub fn is_mountable(&self) -> bool { self.mount_offset().is_some() }
1204
1205 #[inline]
1207 pub fn mount_buffs(&self) -> Option<Vec<BuffEffect>> {
1208 match self {
1209 sprite if sprite.is_bed() => Some(vec![BuffEffect {
1210 kind: BuffKind::RestingHeal,
1211 data: BuffData::new(0.02, Some(Secs(1.0))),
1212 cat_ids: Vec::new(),
1213 }]),
1214 _ => None,
1215 }
1216 }
1217
1218 #[inline]
1219 pub fn is_controller(&self) -> bool { matches!(self, SpriteKind::Helm) }
1220
1221 #[inline]
1222 pub fn is_door(&self) -> bool {
1223 matches!(
1224 self,
1225 SpriteKind::Door
1226 | SpriteKind::DoorWide
1227 | SpriteKind::DoorDark
1228 | SpriteKind::DoorWideAirship
1229 )
1230 }
1231
1232 #[inline]
1234 pub fn mine_tool(&self) -> Option<ToolKind> {
1235 match self {
1236 SpriteKind::Velorite
1237 | SpriteKind::VeloriteFrag
1238 | SpriteKind::Amethyst
1240 | SpriteKind::Ruby
1241 | SpriteKind::Diamond
1242 | SpriteKind::Sapphire
1243 | SpriteKind::Emerald
1244 | SpriteKind::Topaz
1245 | SpriteKind::Bloodstone
1246 | SpriteKind::Coal
1247 | SpriteKind::Cobalt
1248 | SpriteKind::Copper
1249 | SpriteKind::Iron
1250 | SpriteKind::Tin
1251 | SpriteKind::Silver
1252 | SpriteKind::Gold
1253 | SpriteKind::Lodestone => Some(ToolKind::Pick),
1254 SpriteKind::Grave | SpriteKind::Mud => Some(ToolKind::Shovel),
1255 _ => None,
1256 }
1257 }
1258
1259 pub fn required_mine_damage(&self) -> Option<u8> {
1260 Some(match self {
1261 SpriteKind::Gold => 6,
1262 SpriteKind::Silver => 6,
1263 SpriteKind::Bloodstone => 6,
1264 SpriteKind::Cobalt => 6,
1265 SpriteKind::Coal => 4,
1266 SpriteKind::Iron => 4,
1267 SpriteKind::Copper => 3,
1268 SpriteKind::Tin => 3,
1269 SpriteKind::Amethyst => 3,
1270 SpriteKind::Ruby => 3,
1271 SpriteKind::Sapphire => 3,
1272 SpriteKind::Emerald => 3,
1273 SpriteKind::Topaz => 3,
1274 SpriteKind::Diamond => 3,
1275 SpriteKind::Velorite => 3,
1276 SpriteKind::VeloriteFrag => 2,
1277 SpriteKind::Lodestone => 2,
1278 _ => return None,
1279 })
1280 }
1281
1282 pub fn mine_drop_interval(&self) -> u8 {
1285 match self {
1286 SpriteKind::Gold => 3,
1287 SpriteKind::Silver => 3,
1288 SpriteKind::Bloodstone => 3,
1289 SpriteKind::Cobalt => 3,
1290 SpriteKind::Coal => 2,
1291 SpriteKind::Iron => 2,
1292 SpriteKind::Copper => 1,
1293 SpriteKind::Tin => 1,
1294 SpriteKind::Emerald => 3,
1295 SpriteKind::Sapphire => 3,
1296 SpriteKind::Amethyst => 3,
1297 SpriteKind::Topaz => 3,
1298 SpriteKind::Diamond => 3,
1299 SpriteKind::Ruby => 3,
1300 SpriteKind::Velorite => 3,
1301 SpriteKind::VeloriteFrag => 2,
1302 SpriteKind::Lodestone => 2,
1303 _ => 1,
1304 }
1305 }
1306
1307 pub fn unlock_condition(self, cfg: Option<&SpriteCfg>) -> Option<Cow<'_, UnlockKind>> {
1313 let kind = if let Some(unlock) = cfg.and_then(|cfg| cfg.unlock.as_ref()) {
1314 Cow::Borrowed(unlock)
1315 } else {
1316 Cow::Owned(match self {
1317 SpriteKind::CommonLockedChest => {
1318 UnlockKind::Consumes(ItemDefinitionIdOwned::Simple(String::from(
1319 "common.items.utility.lockpick.lockpick_copper",
1320 )))
1321 },
1322 SpriteKind::SahaginKeyhole => UnlockKind::Consumes(ItemDefinitionIdOwned::Simple(
1323 String::from("common.items.keys.sahagin_key"),
1324 )),
1325 SpriteKind::BoneKeyhole => UnlockKind::Consumes(ItemDefinitionIdOwned::Simple(
1326 String::from("common.items.keys.bone_key"),
1327 )),
1328 SpriteKind::HaniwaKeyhole => UnlockKind::Consumes(ItemDefinitionIdOwned::Simple(
1329 String::from("common.items.keys.haniwa_key"),
1330 )),
1331 SpriteKind::VampireKeyhole => UnlockKind::Consumes(ItemDefinitionIdOwned::Simple(
1332 String::from("common.items.keys.vampire_key"),
1333 )),
1334 SpriteKind::GlassKeyhole => UnlockKind::Consumes(ItemDefinitionIdOwned::Simple(
1335 String::from("common.items.keys.glass_key"),
1336 )),
1337 SpriteKind::TerracottaChest => UnlockKind::Consumes(
1338 ItemDefinitionIdOwned::Simple(String::from(
1339 "common.items.keys.terracotta_key_chest",
1340 ))
1341 .to_owned(),
1342 ),
1343 SpriteKind::TerracottaKeyhole => UnlockKind::Consumes(
1344 ItemDefinitionIdOwned::Simple(String::from(
1345 "common.items.keys.terracotta_key_door",
1346 ))
1347 .to_owned(),
1348 ),
1349 SpriteKind::MyrmidonKeyhole => UnlockKind::Consumes(
1350 ItemDefinitionIdOwned::Simple(String::from("common.items.keys.myrmidon_key"))
1351 .to_owned(),
1352 ),
1353 SpriteKind::MinotaurKeyhole => UnlockKind::Consumes(
1354 ItemDefinitionIdOwned::Simple(String::from("common.items.keys.minotaur_key"))
1355 .to_owned(),
1356 ),
1357 SpriteKind::WitchChest => UnlockKind::Consumes(ItemDefinitionIdOwned::Simple(
1358 String::from("common.items.utility.lockpick.lockpick_cobalt"),
1359 )),
1360 SpriteKind::PirateChest => UnlockKind::Consumes(ItemDefinitionIdOwned::Simple(
1361 String::from("common.items.utility.lockpick.lockpick_iron"),
1362 )),
1363 _ => return None,
1364 })
1365 };
1366 Some(kind)
1367 }
1368
1369 pub fn content(&self, cfg: Option<SpriteCfg>) -> Option<Content> {
1371 cfg.and_then(|cfg| cfg.content)
1372 }
1373
1374 #[inline]
1376 pub fn has_ori(&self) -> bool { self.category().has_attr::<Ori>() }
1377}
1378
1379impl fmt::Display for SpriteKind {
1380 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "{:?}", self) }
1381}
1382
1383use strum::IntoEnumIterator;
1384
1385lazy_static! {
1386 pub static ref SPRITE_KINDS: HashMap<String, SpriteKind> =
1387 SpriteKind::iter().map(|sk| (sk.to_string(), sk)).collect();
1388}
1389
1390impl<'a> TryFrom<&'a str> for SpriteKind {
1391 type Error = ();
1392
1393 #[inline]
1394 fn try_from(s: &'a str) -> Result<Self, Self::Error> { SPRITE_KINDS.get(s).copied().ok_or(()) }
1395}
1396
1397#[derive(Clone, Debug, Serialize, Deserialize)]
1399pub enum UnlockKind {
1400 Free,
1402 Requires(ItemDefinitionIdOwned),
1405 Consumes(ItemDefinitionIdOwned),
1408}
1409
1410#[derive(Default, Clone, Debug, Serialize, Deserialize)]
1411pub struct SpriteCfg {
1412 pub unlock: Option<UnlockKind>,
1414 pub content: Option<Content>,
1419 pub loot_table: Option<String>,
1439}
1440
1441#[cfg(test)]
1442mod tests {
1443 use super::*;
1444
1445 #[test]
1446 fn sprite_containers_are_collectible() {
1447 for sprite in SpriteKind::all() {
1448 if sprite.is_defined_as_container() {
1449 assert!(sprite.collectible_info().is_some());
1450 }
1451 }
1452 }
1453
1454 #[test]
1455 fn sprite_conv_kind() {
1456 for sprite in SpriteKind::all() {
1457 let block = Block::air(*sprite);
1458 assert_eq!(block.sprite_category(), Some(sprite.category()));
1459 assert_eq!(block.get_sprite(), Some(*sprite));
1460 }
1461 }
1462
1463 #[test]
1464 fn sprite_attr() {
1465 for category in Category::all() {
1466 if category.has_attr::<Ori>() {
1467 for sprite in category.all_sprites() {
1468 for i in 0..4 {
1469 let block = Block::air(*sprite).with_attr(Ori(i)).unwrap();
1470 assert_eq!(block.get_attr::<Ori>().unwrap(), Ori(i));
1471 assert_eq!(block.get_sprite(), Some(*sprite));
1472 }
1473 }
1474 }
1475 if category.has_attr::<Growth>() {
1476 for sprite in category.all_sprites() {
1477 for i in 0..16 {
1478 let block = Block::air(*sprite).with_attr(Growth(i)).unwrap();
1479 assert_eq!(block.get_attr::<Growth>().unwrap(), Growth(i));
1480 assert_eq!(block.get_sprite(), Some(*sprite));
1481 }
1482 }
1483 }
1484 }
1485 }
1486}