1mod magic;
37
38pub use self::magic::{Attribute, AttributeError};
39
40use crate::{
41 attributes,
42 comp::{item::ItemDefinitionIdOwned, tool::ToolKind},
43 lottery::LootSpec,
44 make_case_elim, sprites,
45 terrain::Block,
46};
47use common_i18n::Content;
48use hashbrown::HashMap;
49use lazy_static::lazy_static;
50use num_derive::FromPrimitive;
51use serde::{Deserialize, Serialize};
52use std::{
53 convert::{Infallible, TryFrom},
54 fmt,
55};
56use strum::EnumIter;
57use vek::*;
58
59sprites! {
60 Void = 0 {
61 Empty = 0,
62 },
63 Misc = 1 {
65 Ember = 0x00,
66 SmokeDummy = 0x01,
67 Bomb = 0x02,
68 FireBlock = 0x03, HotSurface = 0x04,
70 Stones2 = 0x05, },
72 Furniture = 2 has Ori {
75 CoatRack = 0x00,
77 Bed = 0x01,
78 Bench = 0x02,
79 ChairSingle = 0x03,
80 ChairDouble = 0x04,
81 DrawerLarge = 0x05,
82 DrawerMedium = 0x06,
83 DrawerSmall = 0x07,
84 TableSide = 0x08,
85 TableDining = 0x09,
86 TableDouble = 0x0A,
87 WardrobeSingle = 0x0B,
88 WardrobeDouble = 0x0C,
89 BookshelfArabic = 0x0D,
90 WallTableArabic = 0x0E,
91 TableArabicLarge = 0x0F,
92 TableArabicSmall = 0x10,
93 CupboardArabic = 0x11,
94 OvenArabic = 0x12,
95 CushionArabic = 0x13,
96 CanapeArabic = 0x14,
97 Shelf = 0x15,
98 Planter = 0x16,
99 Pot = 0x17,
100 BedMesa = 0x18,
101 WallTableMesa = 0x19,
102 MirrorMesa = 0x1A,
103 WardrobeSingleMesa = 0x1B,
104 WardrobeDoubleMesa = 0x1C,
105 CupboardMesa = 0x1D,
106 TableCoastalLarge = 0x1E,
107 BenchCoastal = 0x1F,
108 CraftingBench = 0x20,
110 Forge = 0x21,
111 Cauldron = 0x22,
112 Anvil = 0x23,
113 CookingPot = 0x24,
114 SpinningWheel = 0x25,
115 TanningRack = 0x26,
116 Loom = 0x27,
117 DismantlingBench = 0x28,
118 RepairBench = 0x29,
119 HangingBasket = 0x50,
121 HangingSign = 0x51,
122 ChristmasOrnament = 0x52,
123 ChristmasWreath = 0x53,
124 WallLampWizard = 0x54,
125 WallLamp = 0x55,
126 WallLampSmall = 0x56,
127 WallSconce = 0x57,
128 DungeonWallDecor = 0x58,
129 WallLampMesa = 0x59,
130 Tent = 0x60,
132 Bedroll = 0x61,
133 BedrollSnow = 0x62,
134 BedrollPirate = 0x63,
135 Sign = 0x64,
136 Helm = 0x65,
137 Scarecrow = 0x70,
139 FountainArabic = 0x71,
140 Hearth = 0x72,
141 ChestWoodDouble= 0x73,
142 LanternpostWoodUpper = 0x74,
143 LanternpostWoodBase = 0x75,
144 LampMetalBase = 0x76,
145 BlacksmithBellows = 0x77,
146 CarpenterTable = 0x78,
147 CarpenterCrateWoodS = 0x79,
148 CarpenterCrateWoodL = 0x7A,
149 CarpenterToolsWall = 0x7B,
150 CarpenterLogCutter = 0x7C,
151 BarrelWoodCoal = 0x7D,
152 BarrelWoodWater = 0x7E,
153 BasketWovenL = 0x7F,
154 BasketWovenM = 0x80,
155 BasketWovenS = 0x81,
156 BonfireMLit = 0x82,
157 BonfireMUnlit = 0x83,
158 BucketWoodM = 0x84,
159 MirrorWoodM = 0x85,
160 SackLeatherM = 0x86,
161 TrophyframeWoodBear = 0x87,
162 TrophyframeWoodDeer = 0x88,
163 JugClayM = 0x89,
164 LogsWoodBranchS = 0x8A,
165 DiningtableWoodCorner = 0x8B,
166 DiningtableWoodBody = 0x8C,
167 BenchWoodEnd = 0x8D,
168 BenchWoodMiddle = 0x8E,
169 LogsWoodCoreEnd = 0x8F,
170 LogsWoodCoreMiddle = 0x90,
171 LogsWoodBarkEnd = 0x91,
172 LogsWoodBarkMiddle = 0x92,
173 LogsWoodBranchEnd = 0x93,
174 LogsWoodBranchMiddle = 0x94,
175 HandCartWood = 0x95,
176
177 },
178 Plant = 3 has Growth, Owned, SnowCovered {
180 BarrelCactus = 0x00,
182 RoundCactus = 0x01,
183 ShortCactus = 0x02,
184 MedFlatCactus = 0x03,
185 ShortFlatCactus = 0x04,
186 LargeCactus = 0x05,
187 TallCactus = 0x06,
188 BlueFlower = 0x10,
190 PinkFlower = 0x11,
191 PurpleFlower = 0x12,
192 RedFlower = 0x13,
193 WhiteFlower = 0x14,
194 YellowFlower = 0x15,
195 Sunflower = 0x16,
196 Moonbell = 0x17,
197 Pyrebloom = 0x18,
198 LushFlower = 0x19,
199 LanternFlower = 0x1A,
200 LongGrass = 0x20,
203 MediumGrass = 0x21,
204 ShortGrass = 0x22,
205 Fern = 0x23,
206 LargeGrass = 0x24,
207 Reed = 0x25,
208 TaigaGrass = 0x26,
209 GrassBlue = 0x27,
210 SavannaGrass = 0x28,
211 TallSavannaGrass = 0x29,
212 RedSavannaGrass = 0x2A,
213 SavannaBush = 0x2B,
214 Welwitch = 0x2C,
215 LeafyPlant = 0x2D,
216 DeadBush = 0x2E,
217 JungleFern = 0x2F,
218 GrassBlueShort = 0x30,
219 GrassBlueMedium = 0x31,
220 GrassBlueLong = 0x32,
221 CavernLillypadBlue = 0x33,
222 EnsnaringVines = 0x34,
223 LillyPads = 0x35,
224 JungleLeafyPlant = 0x36,
225 JungleRedGrass = 0x37,
226 LanternPlant = 0x38,
227 SporeReed = 0x39,
228 DeadPlant = 0x3A,
229 Corn = 0x41,
231 WheatYellow = 0x42,
232 WheatGreen = 0x43, LingonBerry = 0x44,
234 Blueberry = 0x45,
235 Cabbage = 0x46,
236 Pumpkin = 0x47,
237 Carrot = 0x48,
238 Tomato = 0x49,
239 Radish = 0x4A,
240 Turnip = 0x4B,
241 Flax = 0x4C,
242 Mushroom = 0x4D,
243 CaveMushroom = 0x4E,
244 Cotton = 0x4F,
245 WildFlax = 0x50,
246 SewerMushroom = 0x51,
247 LushMushroom = 0x52,
248 RockyMushroom = 0x53,
249 GlowMushroom = 0x54,
250 StonyCoral = 0x61,
252 SoftCoral = 0x62,
253 SeaweedTemperate = 0x63,
254 SeaweedTropical = 0x64,
255 GiantKelp = 0x65,
256 BullKelp = 0x66,
257 WavyAlgae = 0x67,
258 SeaGrapes = 0x68,
259 MermaidsFan = 0x69,
260 SeaAnemone = 0x6A,
261 Seagrass = 0x6B,
262 RedAlgae = 0x6C,
263 Liana = 0x71,
265 MycelBlue = 0x72,
266 CeilingMushroom = 0x73,
267 Mold = 0x74,
268 Root = 0x75,
269 CeilingLanternPlant = 0x76,
270 CeilingLanternFlower = 0x77,
271 CeilingJungleLeafyPlant = 0x78,
272 },
273 Resource = 4 has Owned, SnowCovered {
276 Twigs = 0x00,
279 Wood = 0x01,
280 Bamboo = 0x02,
281 Hardwood = 0x03,
282 Ironwood = 0x04,
283 Frostwood = 0x05,
284 Eldwood = 0x06,
285 Apple = 0x20,
287 Coconut = 0x21,
288 Stones = 0x22,
289 Seashells = 0x23,
290 Beehive = 0x24,
291 Bowl = 0x25,
292 PotionMinor = 0x26,
293 PotionDummy = 0x27,
294 VialEmpty = 0x28,
295 },
296 MineableResource = 5 has Damage {
297 Amethyst = 0x00,
298 Ruby = 0x01,
299 Sapphire = 0x02,
300 Emerald = 0x03,
301 Topaz = 0x04,
302 Diamond = 0x05,
303 Bloodstone = 0x06,
304 Coal = 0x07,
305 Cobalt = 0x08,
306 Copper = 0x09,
307 Iron = 0x0A,
308 Tin = 0x0B,
309 Silver = 0x0C,
310 Gold = 0x0D,
311 Velorite = 0x0E,
312 VeloriteFrag = 0x0F,
313 Mud = 0x10,
314 Grave = 0x11,
315 },
316 Structural = 6 has Ori {
318 Door = 0x00,
320 DoorDark = 0x01,
321 DoorWide = 0x02,
322 BoneKeyhole = 0x03,
323 BoneKeyDoor = 0x04,
324 Keyhole = 0x05,
325 KeyDoor = 0x06,
326 GlassKeyhole = 0x07,
327 KeyholeBars = 0x08,
328 HaniwaKeyDoor = 0x09,
329 HaniwaKeyhole = 0x0A,
330 TerracottaKeyDoor = 0x0B,
331 TerracottaKeyhole = 0x0C,
332 SahaginKeyhole = 0x0D,
333 SahaginKeyDoor = 0x0E,
334 VampireKeyDoor = 0x0F,
335 VampireKeyhole = 0x10,
336 MyrmidonKeyDoor = 0x11,
337 MyrmidonKeyhole = 0x12,
338 MinotaurKeyhole = 0x13,
339
340 Window1 = 0x14,
342 Window2 = 0x15,
343 Window3 = 0x16,
344 Window4 = 0x17,
345 WitchWindow = 0x18,
346 WindowArabic = 0x19,
347 GlassBarrier = 0x20,
349 SeaDecorBlock = 0x21,
350 CliffDecorBlock = 0x22,
351 MagicalBarrier = 0x23,
352 OneWayWall = 0x24,
353 SeaDecorWindowHor = 0x30,
355 SeaDecorWindowVer = 0x31,
356 DropGate = 0x32,
357 DropGateBottom = 0x33,
358 WoodBarricades = 0x34,
359 Rope = 0x40,
361 SeaDecorChain = 0x41,
362 IronSpike = 0x42,
363 DoorBars = 0x43,
364 HaniwaTrap = 0x44,
365 HaniwaTrapTriggered = 0x45,
366 TerracottaStatue = 0x46,
367 TerracottaBlock = 0x47,
368 MetalChain = 0x48,
369 },
370 Decor = 7 has Ori {
372 Bones = 0x00,
374 IceCrystal = 0x01,
375 GlowIceCrystal = 0x02,
376 CrystalHigh = 0x03,
377 CrystalLow = 0x04,
378 UnderwaterVent = 0x05,
379 SeaUrchin = 0x06,
380 IceSpike = 0x07,
381 Orb = 0x08,
382 EnsnaringWeb = 0x09,
383 DiamondLight = 0x0A,
384
385 Gravestone = 0x10,
387 Melon = 0x11,
388 ForgeTools = 0x12,
389 JugAndBowlArabic = 0x13,
390 JugArabic = 0x14,
391 DecorSetArabic = 0x15,
392 SepareArabic = 0x16,
393 Candle = 0x17,
394 SmithingTable = 0x18,
395 Forge0 = 0x19,
396 GearWheel0 = 0x1A,
397 Quench0 = 0x1B,
398 SeaDecorEmblem = 0x1C,
399 SeaDecorPillar = 0x1D,
400 MagicalSeal = 0x1E,
401 JugAndCupsCoastal = 0x1F,
402 },
403 Lamp = 8 has Ori, LightEnabled {
404 Lantern = 0,
406 StreetLamp = 1,
407 StreetLampTall = 2,
408 SeashellLantern = 3,
409 FireBowlGround = 4,
410 MesaLantern = 5,
411 LanternpostWoodLantern = 6,
412 LampMetal = 7,
413 LampTerracotta = 8,
414
415
416 },
417 Container = 9 has Ori, Owned {
418 Chest = 0x00,
419 DungeonChest0 = 0x01,
420 DungeonChest1 = 0x02,
421 DungeonChest2 = 0x03,
422 DungeonChest3 = 0x04,
423 DungeonChest4 = 0x05,
424 DungeonChest5 = 0x06,
425 CoralChest = 0x07,
426 HaniwaUrn = 0x08,
427 TerracottaChest = 0x09,
428 SahaginChest = 0x0A,
429 CommonLockedChest = 0x0B,
430 ChestBuried = 0x0C,
431 Crate = 0x0D,
432 Barrel = 0x0E,
433 CrateBlock = 0x0F,
434 },
435 Modular = 10 has Ori, AdjacentType {
436 Fence = 0x00,
437 }
438}
439
440attributes! {
441 Ori { bits: 4, err: Infallible, from: |bits| Ok(Self(bits as u8)), into: |Ori(x)| x as u16 },
442 Growth { bits: 4, err: Infallible, from: |bits| Ok(Self(bits as u8)), into: |Growth(x)| x as u16 },
443 LightEnabled { bits: 1, err: Infallible, from: |bits| Ok(Self(bits == 1)), into: |LightEnabled(x)| x as u16 },
444 Damage { bits: 3, err: Infallible, from: |bits| Ok(Self(bits as u8)), into: |Damage(x)| x as u16 },
445 Owned { bits: 1, err: Infallible, from: |bits| Ok(Self(bits == 1)), into: |Owned(x)| x as u16 },
446 AdjacentType { bits: 3, err: Infallible, from: |bits| Ok(Self(bits as u8)), into: |AdjacentType(x)| x as u16 },
447 SnowCovered { bits: 1, err: Infallible, from: |bits| Ok(Self(bits == 1)), into: |SnowCovered(x)| x as u16 },
448}
449
450#[derive(Copy, Clone, Default, Debug, PartialEq, Eq)]
452pub struct Ori(pub u8);
453
454#[derive(Copy, Clone, Debug, PartialEq, Eq)]
456pub struct Growth(pub u8);
457
458impl Default for Growth {
459 fn default() -> Self { Self(15) }
460}
461
462#[derive(Copy, Clone, Debug, PartialEq, Eq)]
464pub struct LightEnabled(pub bool);
465
466#[derive(Default, Copy, Clone, Debug, PartialEq, Eq)]
467pub struct Owned(pub bool);
468
469impl Default for LightEnabled {
470 fn default() -> Self { Self(true) }
471}
472
473#[derive(Default, Copy, Clone, Debug, PartialEq, Eq, Deserialize, FromPrimitive, Hash)]
483#[repr(u8)]
484pub enum RelativeNeighborPosition {
485 #[default]
486 I,
487 L,
488 T,
489 X,
490 End,
491}
492
493#[derive(Copy, Clone, Debug, PartialEq, Eq)]
494pub struct AdjacentType(pub u8);
495
496impl Default for AdjacentType {
497 fn default() -> Self { Self(RelativeNeighborPosition::I as u8) }
498}
499
500#[derive(Copy, Clone, Debug, PartialEq, Eq, Default)]
502pub struct Damage(pub u8);
503
504#[derive(Copy, Clone, Default, Debug, PartialEq, Eq)]
506pub struct SnowCovered(pub bool);
507
508impl SpriteKind {
509 #[inline]
510 pub fn solid_height(&self) -> Option<f32> {
511 Some(match self {
514 SpriteKind::Bedroll => 0.3,
515 SpriteKind::BedrollSnow => 0.4,
516 SpriteKind::BedrollPirate => 0.3,
517 SpriteKind::Tomato => 1.65,
518 SpriteKind::BarrelCactus => 1.0,
519 SpriteKind::LargeCactus => 3.0,
520 SpriteKind::TallCactus => 2.63,
521 SpriteKind::Scarecrow => 3.0,
522 SpriteKind::Turnip => 0.36,
523 SpriteKind::Pumpkin => 0.81,
524 SpriteKind::Cabbage => 0.45,
525 SpriteKind::Chest => 1.09,
526 SpriteKind::CommonLockedChest => 1.09,
527 SpriteKind::DungeonChest0 => 1.09,
528 SpriteKind::DungeonChest1 => 1.09,
529 SpriteKind::DungeonChest2 => 1.09,
530 SpriteKind::DungeonChest3 => 1.09,
531 SpriteKind::DungeonChest4 => 1.09,
532 SpriteKind::DungeonChest5 => 1.09,
533 SpriteKind::CoralChest => 1.09,
534 SpriteKind::HaniwaUrn => 1.09,
535 SpriteKind::SahaginChest => 1.09,
536 SpriteKind::TerracottaChest => 1.09,
537 SpriteKind::TerracottaStatue => 5.29,
538 SpriteKind::TerracottaBlock => 1.00,
539 SpriteKind::Fence => 1.09,
540 SpriteKind::SeaDecorChain => 1.09,
541 SpriteKind::SeaDecorBlock => 1.00,
542 SpriteKind::SeaDecorWindowHor => 0.55,
543 SpriteKind::SeaDecorWindowVer => 1.09,
544 SpriteKind::SeaDecorPillar => 2.55,
545 SpriteKind::SeashellLantern => 2.09,
546 SpriteKind::MesaLantern => 1.3,
547 SpriteKind::Rope => 1.09,
548 SpriteKind::MetalChain => 1.09,
549 SpriteKind::StreetLamp => 2.65,
550 SpriteKind::Carrot => 0.18,
551 SpriteKind::Radish => 0.18,
552 SpriteKind::FireBowlGround => 0.55,
553 SpriteKind::Bed => 0.72,
554 SpriteKind::BedMesa => 0.82,
555 SpriteKind::Bench => 0.5,
556 SpriteKind::ChairSingle => 0.5,
557 SpriteKind::ChairDouble => 0.5,
558 SpriteKind::CoatRack => 2.36,
559 SpriteKind::Crate => 0.90,
560 SpriteKind::DrawerSmall => 1.0,
561 SpriteKind::DrawerMedium => 2.0,
562 SpriteKind::DrawerLarge => 2.0,
563 SpriteKind::DungeonWallDecor => 1.0,
564 SpriteKind::Planter => 1.09,
565 SpriteKind::TableSide => 1.27,
566 SpriteKind::TableDining => 1.45,
567 SpriteKind::TableDouble => 1.45,
568 SpriteKind::WardrobeSingle => 3.0,
569 SpriteKind::WardrobeDouble => 3.0,
570 SpriteKind::WardrobeSingleMesa => 2.0,
571 SpriteKind::WardrobeDoubleMesa => 2.0,
572 SpriteKind::MirrorMesa => 2.0,
573 SpriteKind::Pot => 0.90,
574 SpriteKind::Mud => 0.36,
575 SpriteKind::ChestBuried => 0.91,
576 SpriteKind::StonyCoral => 1.4,
577 SpriteKind::CraftingBench => 1.18,
578 SpriteKind::Forge => 2.7,
579 SpriteKind::Cauldron => 1.27,
580 SpriteKind::SpinningWheel => 1.6,
581 SpriteKind::TanningRack => 2.2,
582 SpriteKind::Loom => 1.27,
583 SpriteKind::Anvil => 1.18,
584 SpriteKind::CookingPot => 1.36,
585 SpriteKind::DismantlingBench => 1.18,
586 SpriteKind::IceSpike => 1.0,
587 SpriteKind::RepairBench => 1.2,
588 SpriteKind::RoundCactus => 0.72,
589 SpriteKind::ShortCactus => 1.36,
590 SpriteKind::MedFlatCactus => 1.36,
591 SpriteKind::ShortFlatCactus => 0.91,
592 SpriteKind::Apple
594 | SpriteKind::Beehive
595 | SpriteKind::Velorite
596 | SpriteKind::VeloriteFrag
597 | SpriteKind::Coconut
598 | SpriteKind::StreetLampTall
599 | SpriteKind::Window1
600 | SpriteKind::Window2
601 | SpriteKind::Window3
602 | SpriteKind::Window4
603 | SpriteKind::DropGate
604 | SpriteKind::WitchWindow
605 | SpriteKind::SeaUrchin
606 | SpriteKind::IronSpike
607 | SpriteKind::GlassBarrier
608 | SpriteKind::GlassKeyhole
609 | SpriteKind::Keyhole
610 | SpriteKind::KeyDoor
611 | SpriteKind::BoneKeyhole
612 | SpriteKind::BoneKeyDoor
613 | SpriteKind::HaniwaKeyhole
614 | SpriteKind::HaniwaKeyDoor
615 | SpriteKind::SahaginKeyhole
616 | SpriteKind::SahaginKeyDoor
617 | SpriteKind::VampireKeyhole
618 | SpriteKind::VampireKeyDoor
619 | SpriteKind::HaniwaTrap
620 | SpriteKind::HaniwaTrapTriggered
621 | SpriteKind::TerracottaKeyDoor
622 | SpriteKind::TerracottaKeyhole
623 | SpriteKind::MyrmidonKeyDoor
624 | SpriteKind::MyrmidonKeyhole
625 | SpriteKind::MinotaurKeyhole
626 | SpriteKind::Bomb
627 | SpriteKind::OneWayWall
628 | SpriteKind::DoorBars
629 | SpriteKind::KeyholeBars
630 | SpriteKind::WoodBarricades
631 | SpriteKind::DiamondLight => 1.0,
632 SpriteKind::Shelf => 1.0,
634 SpriteKind::Lantern => 0.9,
635 SpriteKind::CrystalHigh | SpriteKind::CrystalLow => 1.5,
636 SpriteKind::Bloodstone
637 | SpriteKind::Coal
638 | SpriteKind::Cobalt
639 | SpriteKind::Copper
640 | SpriteKind::Iron
641 | SpriteKind::Tin
642 | SpriteKind::Silver
643 | SpriteKind::Gold => 0.6,
644 SpriteKind::EnsnaringVines
645 | SpriteKind::CavernLillypadBlue
646 | SpriteKind::EnsnaringWeb => 0.15,
647 SpriteKind::LillyPads => 0.1,
648 SpriteKind::WindowArabic | SpriteKind::BookshelfArabic => 1.9,
649 SpriteKind::DecorSetArabic => 2.6,
650 SpriteKind::SepareArabic => 2.2,
651 SpriteKind::CushionArabic => 0.4,
652 SpriteKind::JugArabic => 1.4,
653 SpriteKind::TableArabicSmall => 0.9,
654 SpriteKind::TableArabicLarge => 1.0,
655 SpriteKind::TableCoastalLarge => 1.0,
656 SpriteKind::BenchCoastal => 1.0,
657 SpriteKind::CanapeArabic => 1.2,
658 SpriteKind::CupboardArabic => 4.5,
659 SpriteKind::WallTableArabic => 2.3,
660 SpriteKind::JugAndBowlArabic => 1.4,
661 SpriteKind::JugAndCupsCoastal => 1.4,
662 SpriteKind::Melon => 0.7,
663 SpriteKind::OvenArabic => 3.2,
664 SpriteKind::FountainArabic => 2.4,
665 SpriteKind::Hearth => 2.3,
666 SpriteKind::ForgeTools => 2.8,
667 SpriteKind::CliffDecorBlock | SpriteKind::FireBlock => 1.0,
668 SpriteKind::Wood
669 | SpriteKind::Hardwood
670 | SpriteKind::Ironwood
671 | SpriteKind::Frostwood
672 | SpriteKind::Eldwood => 7.0 / 11.0,
673 SpriteKind::Bamboo => 9.0 / 11.0,
674 SpriteKind::MagicalBarrier => 3.0,
675 SpriteKind::MagicalSeal => 1.0,
676 SpriteKind::Helm => 1.7,
677 SpriteKind::Sign => 16.0 / 11.0,
678 SpriteKind::SmithingTable => 13.0 / 11.0,
679 SpriteKind::Forge0 => 17.0 / 11.0,
680 SpriteKind::GearWheel0 => 3.0 / 11.0,
681 SpriteKind::Quench0 => 8.0 / 11.0,
682 SpriteKind::HotSurface => 0.01,
683 SpriteKind::Barrel => 1.0,
684 SpriteKind::CrateBlock => 1.0,
685 SpriteKind::BarrelWoodWater | SpriteKind::BarrelWoodCoal => 1.545,
686 SpriteKind::LanternpostWoodLantern | SpriteKind::LanternpostWoodUpper => 2.000,
687 SpriteKind::LanternpostWoodBase => 3.000,
688 SpriteKind::LampMetal => 1.000,
689 SpriteKind::LampMetalBase => 2.818,
690 SpriteKind::LampTerracotta => 1.727,
691 SpriteKind::BlacksmithBellows => 0.545,
692 SpriteKind::CarpenterTable => 2.000,
693 SpriteKind::CarpenterCrateWoodS => 0.727,
694 SpriteKind::CarpenterCrateWoodL => 1.273,
695 SpriteKind::CarpenterToolsWall => 1.000,
696 SpriteKind::CarpenterLogCutter => 1.545,
697 SpriteKind::HandCartWood => 3.000,
698 SpriteKind::BasketWovenL | SpriteKind::JugClayM => 1.000,
699 SpriteKind::BasketWovenM => 0.909,
700 SpriteKind::BasketWovenS => 0.818,
701 SpriteKind::BonfireMLit | SpriteKind::BonfireMUnlit => 2.273,
702 SpriteKind::BucketWoodM | SpriteKind::SackLeatherM => 1.091,
703 SpriteKind::MirrorWoodM => 1.364,
704 SpriteKind::TrophyframeWoodBear => 1.455,
705 SpriteKind::TrophyframeWoodDeer => 1.727,
706 SpriteKind::ChestWoodDouble => 1.182,
707 SpriteKind::DiningtableWoodCorner => 1.273,
708 SpriteKind::DiningtableWoodBody => 1.273,
709 SpriteKind::BenchWoodEnd => 0.636,
710 SpriteKind::BenchWoodMiddle => 0.636,
711 SpriteKind::LogsWoodCoreEnd => 0.818,
712 SpriteKind::LogsWoodCoreMiddle => 0.818,
713 SpriteKind::LogsWoodBarkEnd => 1.091,
714 SpriteKind::LogsWoodBarkMiddle => 1.091,
715 SpriteKind::LogsWoodBranchEnd => 1.091,
716 SpriteKind::LogsWoodBranchMiddle => 1.091,
717 SpriteKind::LogsWoodBranchS => 1.091,
718
719 _ => return None,
720 })
721 }
722
723 pub fn valid_collision_dir(
724 &self,
725 entity_aabb: Aabb<f32>,
726 block_aabb: Aabb<f32>,
727 move_dir: Vec3<f32>,
728 parent: &Block,
729 ) -> bool {
730 match self {
731 SpriteKind::OneWayWall => {
732 let dir = entity_aabb.collision_vector_with_aabb(block_aabb);
734
735 let max_axis = dir.map(|e| e.abs()).reduce_partial_min();
738 let resolve_dir = -dir.map(|e| {
739 if e.abs().to_bits() == max_axis.to_bits() {
740 e.signum()
741 } else {
742 0.0
743 }
744 });
745
746 let is_moving_into = move_dir.dot(resolve_dir) <= 0.0;
747
748 is_moving_into
749 && parent.get_ori().is_some_and(|ori| {
750 Vec2::unit_y()
751 .rotated_z(std::f32::consts::PI * 0.25 * ori as f32)
752 .with_z(0.0)
753 .map2(resolve_dir, |e, r| (e - r).abs() < 0.1)
754 .reduce_and()
755 })
756 },
757 _ => true,
758 }
759 }
760
761 #[inline]
766 pub fn collectible_id(&self) -> Option<Option<LootSpec<&'static str>>> {
767 let item = LootSpec::Item;
768 let table = LootSpec::LootTable;
769 Some(Some(match self {
770 SpriteKind::Apple => item("common.items.food.apple"),
771 SpriteKind::Mushroom => item("common.items.food.mushroom"),
772 SpriteKind::Velorite => item("common.items.mineral.ore.velorite"),
773 SpriteKind::VeloriteFrag => item("common.items.mineral.ore.veloritefrag"),
774 SpriteKind::RedFlower => item("common.items.flowers.red"),
778 SpriteKind::Sunflower => item("common.items.flowers.sunflower"),
781 SpriteKind::Coconut => item("common.items.food.coconut"),
785 SpriteKind::Beehive => item("common.items.crafting_ing.honey"),
786 SpriteKind::Stones => item("common.items.crafting_ing.stones"),
787 SpriteKind::Twigs => item("common.items.crafting_ing.twigs"),
788 SpriteKind::VialEmpty => item("common.items.crafting_ing.empty_vial"),
789 SpriteKind::Bowl => item("common.items.crafting_ing.bowl"),
790 SpriteKind::PotionMinor => item("common.items.consumable.potion_minor"),
791 SpriteKind::Amethyst => item("common.items.mineral.gem.amethyst"),
792 SpriteKind::Ruby => item("common.items.mineral.gem.ruby"),
793 SpriteKind::Diamond => item("common.items.mineral.gem.diamond"),
794 SpriteKind::Sapphire => item("common.items.mineral.gem.sapphire"),
795 SpriteKind::Topaz => item("common.items.mineral.gem.topaz"),
796 SpriteKind::Emerald => item("common.items.mineral.gem.emerald"),
797 SpriteKind::Bloodstone => item("common.items.mineral.ore.bloodstone"),
798 SpriteKind::Coal => item("common.items.mineral.ore.coal"),
799 SpriteKind::Cobalt => item("common.items.mineral.ore.cobalt"),
800 SpriteKind::Copper => item("common.items.mineral.ore.copper"),
801 SpriteKind::Iron => item("common.items.mineral.ore.iron"),
802 SpriteKind::Tin => item("common.items.mineral.ore.tin"),
803 SpriteKind::Silver => item("common.items.mineral.ore.silver"),
804 SpriteKind::Gold => item("common.items.mineral.ore.gold"),
805 SpriteKind::Cotton => item("common.items.crafting_ing.cotton_boll"),
806 SpriteKind::Moonbell => item("common.items.flowers.moonbell"),
807 SpriteKind::Pyrebloom => item("common.items.flowers.pyrebloom"),
808 SpriteKind::WildFlax => item("common.items.flowers.wild_flax"),
809 SpriteKind::Seashells => item("common.items.crafting_ing.seashells"),
810 SpriteKind::RoundCactus => item("common.items.crafting_ing.cactus"),
811 SpriteKind::ShortFlatCactus => item("common.items.crafting_ing.cactus"),
812 SpriteKind::MedFlatCactus => item("common.items.crafting_ing.cactus"),
813 SpriteKind::Bomb => item("common.items.utility.bomb"),
814 SpriteKind::DungeonChest0 => table("common.loot_tables.dungeon.gnarling.chest"),
815 SpriteKind::DungeonChest1 => table("common.loot_tables.dungeon.adlet.chest"),
816 SpriteKind::DungeonChest2 => table("common.loot_tables.dungeon.sahagin.chest"),
817 SpriteKind::DungeonChest3 => table("common.loot_tables.dungeon.haniwa.chest"),
818 SpriteKind::DungeonChest4 => table("common.loot_tables.dungeon.myrmidon.chest"),
819 SpriteKind::DungeonChest5 => table("common.loot_tables.dungeon.cultist.chest"),
820 SpriteKind::Chest => table("common.loot_tables.sprite.chest"),
821 SpriteKind::CommonLockedChest => table("common.loot_tables.dungeon.sahagin.chest"),
822 SpriteKind::ChestBuried => table("common.loot_tables.sprite.chest-buried"),
823 SpriteKind::CoralChest => table("common.loot_tables.dungeon.sea_chapel.chest_coral"),
824 SpriteKind::HaniwaUrn => table("common.loot_tables.dungeon.haniwa.key"),
825 SpriteKind::TerracottaChest => {
826 table("common.loot_tables.dungeon.terracotta.chest_terracotta")
827 },
828 SpriteKind::SahaginChest => table("common.loot_tables.dungeon.sahagin.key_chest"),
829 SpriteKind::Mud => table("common.loot_tables.sprite.mud"),
830 SpriteKind::Grave => table("common.loot_tables.sprite.mud"),
831 SpriteKind::Crate => table("common.loot_tables.sprite.crate"),
832 SpriteKind::Wood => item("common.items.log.wood"),
833 SpriteKind::Bamboo => item("common.items.log.bamboo"),
834 SpriteKind::Hardwood => item("common.items.log.hardwood"),
835 SpriteKind::Ironwood => item("common.items.log.ironwood"),
836 SpriteKind::Frostwood => item("common.items.log.frostwood"),
837 SpriteKind::Eldwood => item("common.items.log.eldwood"),
838 SpriteKind::MagicalBarrier => table("common.loot_tables.sprite.chest"),
839 SpriteKind::Keyhole
840 | SpriteKind::BoneKeyhole
841 | SpriteKind::HaniwaKeyhole
842 | SpriteKind::VampireKeyhole
843 | SpriteKind::GlassKeyhole
844 | SpriteKind::KeyholeBars
845 | SpriteKind::SahaginKeyhole
846 | SpriteKind::TerracottaKeyhole
847 | SpriteKind::MyrmidonKeyhole
848 | SpriteKind::MinotaurKeyhole => {
849 return Some(None);
850 },
851 _ => return None,
852 }))
853 }
854
855 #[inline]
857 pub fn is_collectible(&self) -> bool {
858 self.collectible_id().is_some() && self.mine_tool().is_none()
859 }
860
861 #[inline]
863 pub fn is_container(&self) -> bool {
864 matches!(self.collectible_id(), Some(Some(LootSpec::LootTable(_))))
865 }
866
867 #[inline]
869 pub fn mount_offset(&self) -> Option<(Vec3<f32>, Vec3<f32>)> {
870 match self {
871 SpriteKind::ChairSingle
872 | SpriteKind::ChairDouble
873 | SpriteKind::Bench
874 | SpriteKind::BenchCoastal => Some((Vec3::new(0.0, 0.0, 0.5), -Vec3::unit_y())),
875 SpriteKind::Helm => Some((Vec3::new(0.0, -1.0, 0.0), Vec3::unit_y())),
876 SpriteKind::Bed => Some((Vec3::new(0.0, 0.0, 0.6), -Vec3::unit_y())),
877 SpriteKind::BedMesa => Some((Vec3::new(0.0, 0.0, 0.6), -Vec3::unit_y())),
878 SpriteKind::BedrollSnow | SpriteKind::BedrollPirate => {
879 Some((Vec3::new(0.0, 0.0, 0.1), -Vec3::unit_x()))
880 },
881 SpriteKind::Bedroll => Some((Vec3::new(0.0, 0.0, 0.1), Vec3::unit_y())),
882 _ => None,
883 }
884 }
885
886 #[inline]
887 pub fn is_mountable(&self) -> bool { self.mount_offset().is_some() }
888
889 #[inline]
890 pub fn is_controller(&self) -> bool { matches!(self, SpriteKind::Helm) }
891
892 #[inline]
893 pub fn is_door(&self) -> bool {
894 matches!(
895 self,
896 SpriteKind::Door | SpriteKind::DoorWide | SpriteKind::DoorDark
897 )
898 }
899
900 #[inline]
902 pub fn mine_tool(&self) -> Option<ToolKind> {
903 match self {
904 SpriteKind::Velorite
905 | SpriteKind::VeloriteFrag
906 | SpriteKind::Amethyst
908 | SpriteKind::Ruby
909 | SpriteKind::Diamond
910 | SpriteKind::Sapphire
911 | SpriteKind::Emerald
912 | SpriteKind::Topaz
913 | SpriteKind::Bloodstone
914 | SpriteKind::Coal
915 | SpriteKind::Cobalt
916 | SpriteKind::Copper
917 | SpriteKind::Iron
918 | SpriteKind::Tin
919 | SpriteKind::Silver
920 | SpriteKind::Gold => Some(ToolKind::Pick),
921 SpriteKind::Grave | SpriteKind::Mud => Some(ToolKind::Shovel),
922 _ => None,
923 }
924 }
925
926 pub fn required_mine_damage(&self) -> Option<u8> {
927 Some(match self {
928 SpriteKind::Gold => 6,
929 SpriteKind::Silver => 6,
930 SpriteKind::Bloodstone => 6,
931 SpriteKind::Cobalt => 6,
932 SpriteKind::Coal => 6,
933 SpriteKind::Iron => 6,
934 SpriteKind::Copper => 3,
935 SpriteKind::Tin => 3,
936 SpriteKind::Amethyst => 3,
937 SpriteKind::Ruby => 6,
938 SpriteKind::Sapphire => 3,
939 SpriteKind::Emerald => 3,
940 SpriteKind::Topaz => 3,
941 SpriteKind::Diamond => 6,
942 SpriteKind::Velorite => 3,
943 SpriteKind::VeloriteFrag => 2,
944 _ => return None,
945 })
946 }
947
948 pub fn mine_drop_interval(&self) -> u8 {
951 match self {
952 SpriteKind::Gold => 2,
953 SpriteKind::Silver => 2,
954 SpriteKind::Bloodstone => 2,
955 SpriteKind::Cobalt => 2,
956 SpriteKind::Coal => 2,
957 SpriteKind::Iron => 2,
958 SpriteKind::Copper => 1,
959 SpriteKind::Tin => 1,
960 SpriteKind::Emerald => 1,
961 SpriteKind::Sapphire => 1,
962 SpriteKind::Amethyst => 1,
963 SpriteKind::Topaz => 1,
964 SpriteKind::Diamond => 2,
965 SpriteKind::Ruby => 2,
966 SpriteKind::Velorite => 1,
967 SpriteKind::VeloriteFrag => 1,
968 _ => 1,
969 }
970 }
971
972 pub fn unlock_condition(&self, cfg: Option<SpriteCfg>) -> UnlockKind {
978 cfg.and_then(|cfg| cfg.unlock)
979 .unwrap_or_else(|| match self {
980 SpriteKind::CommonLockedChest => UnlockKind::Consumes(
981 ItemDefinitionIdOwned::Simple(String::from("common.items.utility.lockpick_0")),
982 ),
983 SpriteKind::SahaginKeyhole => UnlockKind::Consumes(ItemDefinitionIdOwned::Simple(
984 String::from("common.items.keys.sahagin_key"),
985 )),
986 SpriteKind::BoneKeyhole => UnlockKind::Consumes(ItemDefinitionIdOwned::Simple(
987 String::from("common.items.keys.bone_key"),
988 )),
989 SpriteKind::HaniwaKeyhole => UnlockKind::Consumes(ItemDefinitionIdOwned::Simple(
990 String::from("common.items.keys.haniwa_key"),
991 )),
992 SpriteKind::VampireKeyhole => UnlockKind::Consumes(ItemDefinitionIdOwned::Simple(
993 String::from("common.items.keys.vampire_key"),
994 )),
995 SpriteKind::GlassKeyhole => UnlockKind::Consumes(ItemDefinitionIdOwned::Simple(
996 String::from("common.items.keys.glass_key"),
997 )),
998 SpriteKind::TerracottaChest => UnlockKind::Consumes(
999 ItemDefinitionIdOwned::Simple(String::from(
1000 "common.items.keys.terracotta_key_chest",
1001 ))
1002 .to_owned(),
1003 ),
1004 SpriteKind::TerracottaKeyhole => UnlockKind::Consumes(
1005 ItemDefinitionIdOwned::Simple(String::from(
1006 "common.items.keys.terracotta_key_door",
1007 ))
1008 .to_owned(),
1009 ),
1010 SpriteKind::MyrmidonKeyhole => UnlockKind::Consumes(
1011 ItemDefinitionIdOwned::Simple(String::from("common.items.keys.myrmidon_key"))
1012 .to_owned(),
1013 ),
1014 SpriteKind::MinotaurKeyhole => UnlockKind::Consumes(
1015 ItemDefinitionIdOwned::Simple(String::from("common.items.keys.minotaur_key"))
1016 .to_owned(),
1017 ),
1018 _ => UnlockKind::Free,
1019 })
1020 }
1021
1022 pub fn content(&self, cfg: Option<SpriteCfg>) -> Option<Content> {
1024 cfg.and_then(|cfg| cfg.content)
1025 }
1026
1027 #[inline]
1029 pub fn has_ori(&self) -> bool { self.category().has_attr::<Ori>() }
1030}
1031
1032impl fmt::Display for SpriteKind {
1033 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "{:?}", self) }
1034}
1035
1036use strum::IntoEnumIterator;
1037
1038lazy_static! {
1039 pub static ref SPRITE_KINDS: HashMap<String, SpriteKind> =
1040 SpriteKind::iter().map(|sk| (sk.to_string(), sk)).collect();
1041}
1042
1043impl<'a> TryFrom<&'a str> for SpriteKind {
1044 type Error = ();
1045
1046 #[inline]
1047 fn try_from(s: &'a str) -> Result<Self, Self::Error> { SPRITE_KINDS.get(s).copied().ok_or(()) }
1048}
1049
1050#[derive(Clone, Debug, Serialize, Deserialize)]
1051pub enum UnlockKind {
1052 Free,
1054 Requires(ItemDefinitionIdOwned),
1057 Consumes(ItemDefinitionIdOwned),
1060}
1061
1062#[derive(Default, Clone, Debug, Serialize, Deserialize)]
1063pub struct SpriteCfg {
1064 pub unlock: Option<UnlockKind>,
1065 pub content: Option<Content>,
1066}
1067
1068#[cfg(test)]
1069mod tests {
1070 use super::*;
1071
1072 #[test]
1073 fn sprite_conv_kind() {
1074 for sprite in SpriteKind::all() {
1075 let block = Block::air(*sprite);
1076 assert_eq!(block.sprite_category(), Some(sprite.category()));
1077 assert_eq!(block.get_sprite(), Some(*sprite));
1078 }
1079 }
1080
1081 #[test]
1082 fn sprite_attr() {
1083 for category in Category::all() {
1084 if category.has_attr::<Ori>() {
1085 for sprite in category.all_sprites() {
1086 for i in 0..4 {
1087 let block = Block::air(*sprite).with_attr(Ori(i)).unwrap();
1088 assert_eq!(block.get_attr::<Ori>().unwrap(), Ori(i));
1089 assert_eq!(block.get_sprite(), Some(*sprite));
1090 }
1091 }
1092 }
1093 if category.has_attr::<Growth>() {
1094 for sprite in category.all_sprites() {
1095 for i in 0..16 {
1096 let block = Block::air(*sprite).with_attr(Growth(i)).unwrap();
1097 assert_eq!(block.get_attr::<Growth>().unwrap(), Growth(i));
1098 assert_eq!(block.get_sprite(), Some(*sprite));
1099 }
1100 }
1101 }
1102 }
1103 }
1104}