veloren_common/terrain/sprite/
mod.rs

1//! Here's the deal.
2//!
3//! Blocks are always 4 bytes. The first byte is the [`BlockKind`]. For filled
4//! blocks, the remaining 3 sprites are the block colour. For unfilled sprites
5//! (air, water, etc.) the remaining 3 bytes correspond to sprite data. That's
6//! not a lot to work with! As a result, we're pulling every rabbit out of the
7//! bit-twiddling hat to squash as much information as possible into those 3
8//! bytes.
9//!
10//! Fundamentally, sprites are composed of one or more elements: the
11//! [`SpriteKind`], which tells us what the sprite *is*, and a list of
12//! attributes that define extra properties that the sprite has. Some examples
13//! of attributes might include:
14//!
15//! - the orientation of the sprite (with respect to the volume it sits within)
16//! - whether the sprite has snow cover on it
17//! - a 'variation seed' that allows frontends to pseudorandomly customise the
18//!   appearance of the sprite in a manner that's consistent across clients
19//! - Whether doors are open, closed, or permanently locked
20//! - The stage of growth of a plant
21//! - The kind of plant that sits in pots/planters/vessels
22//! - The colour of the sprite
23//! - The material of the sprite
24//!
25//! # Category
26//!
27//! The first of the three bytes is the sprite 'category'. As much as possible,
28//! we should try to have the properties of each sprite within a category be
29//! consistent with others in the category, to improve performance.
30//!
31//! Since a single byte is not enough to disambiguate the [`SpriteKind`] (we
32//! have more than 256 kinds, so there's not enough space), the category also
33//! corresponds to a 'kind mask': a bitmask that, when applied to the first two
34//! of the three bytes gives us the [`SpriteKind`].
35
36mod magic;
37//use inline_tweak::tweak_fn;
38pub use self::magic::{Attribute, AttributeError};
39use crate::{
40    attributes,
41    comp::{BuffData, BuffKind, item::ItemDefinitionIdOwned, tool::ToolKind},
42    effect::BuffEffect,
43    lottery::LootSpec,
44    make_case_elim,
45    resources::Secs,
46    sprites,
47    terrain::Block,
48};
49use common_i18n::Content;
50use hashbrown::HashMap;
51use lazy_static::lazy_static;
52use num_derive::FromPrimitive;
53use serde::{Deserialize, Serialize};
54use std::{
55    convert::{Infallible, TryFrom},
56    fmt,
57};
58use strum::EnumIter;
59use vek::*;
60
61/// A sprite that can be deserialized with all its attributes.
62///
63/// Say we have created the sprites:
64/// ```ignore
65/// sprites! {
66///    Furniture = 0 has Ori, MirrorX {
67///       Chair,
68///       Table,
69///    }
70/// }
71/// ```
72/// And given we're deserializing from ron we could deserialize an array
73/// of `StructureSprite` that look like this:
74/// ```ignore
75/// [
76///    // This will be a `SpriteKind::Chair` with default attributes
77///    Chair(),
78///    // This will be a `SpriteKind::Chair` with the given attributes `Ori(2)` and `MirrorX(true)`.
79///    Chair(Ori(2), MirrorX(true)),
80///    // This will be a `SpriteKind::Table` with the given attribute `Ori(2)` and the rest of its
81///    // attributes set to default.
82///    Table(Ori(4)),
83/// ]
84/// ```
85#[derive(Copy, Clone, Debug, Eq, PartialEq, Deserialize)]
86#[serde(transparent)]
87pub struct StructureSprite(StructureSpriteKind);
88
89impl StructureSprite {
90    pub fn get_block(self, with_sprite: impl FnMut(SpriteKind) -> Block) -> Block {
91        self.0.get_block(with_sprite)
92    }
93}
94
95sprites! {
96    Void = 0 {
97        Empty = 0,
98    },
99    // Generic collection of sprites, no attributes but anything goes
100    Misc = 1 {
101        Ember      = 0x00,
102        SmokeDummy = 0x01,
103        Bomb       = 0x02,
104        FireBlock  = 0x03, // FireBlock for Burning Buff
105        HotSurface = 0x04,
106        Stones2    = 0x05, // Same as `Stones` but not collectible
107    },
108    // Furniture. In the future, we might add an attribute to customise material
109    // TODO: Remove sizes and variants, represent with attributes
110    Furniture = 2 has Ori, MirrorX {
111        // Indoor
112        BookshelfArabic    = 0x0D,
113        WallTableArabic    = 0x0E,
114        TableArabicLarge   = 0x0F,
115        TableArabicSmall   = 0x10,
116        CupboardArabic     = 0x11,
117        OvenArabic         = 0x12,
118        CushionArabic      = 0x13,
119        CanapeArabic       = 0x14,
120        Shelf              = 0x15,
121        Planter            = 0x16,
122        BedMesa            = 0x18,
123        WallTableMesa      = 0x19,
124        MirrorMesa         = 0x1A,
125        WardrobeSingleMesa = 0x1B,
126        WardrobeDoubleMesa = 0x1C,
127        CupboardMesa       = 0x1D,
128        TableCoastalLarge  = 0x1E,
129        BenchCoastal       = 0x1F,
130        // Crafting
131        CraftingBench    = 0x20,
132        Forge            = 0x21,
133        Cauldron         = 0x22,
134        Anvil            = 0x23,
135        CookingPot       = 0x24,
136        SpinningWheel    = 0x25,
137        TanningRack      = 0x26,
138        Loom             = 0x27,
139        DismantlingBench = 0x28,
140        RepairBench      = 0x29,
141        // Wall
142        HangingBasket     = 0x50,
143        HangingSign       = 0x51,
144        ChristmasOrnament = 0x52,
145        ChristmasWreath   = 0x53,
146        WallLampWizard    = 0x54,
147        WallLamp          = 0x55,
148        WallLampSmall     = 0x56,
149        WallSconce        = 0x57,
150        DungeonWallDecor  = 0x58,
151        WallLampMesa      = 0x59,
152        // Outdoor
153        Tent          = 0x60,
154        Bedroll       = 0x61,
155        BedrollSnow   = 0x62,
156        BedrollPirate = 0x63,
157        Sign          = 0x64,
158        Helm          = 0x65,
159        // Misc
160        Scarecrow      = 0x70,
161        FountainArabic = 0x71,
162        Hearth         = 0x72,
163        ChestWoodDouble= 0x73,
164        LanternpostWoodUpper = 0x74,
165        LanternpostWoodBase = 0x75,
166        LampMetalBase = 0x76,
167        BlacksmithBellows = 0x77,
168        CarpenterTable = 0x78,
169        CarpenterCrateWoodS = 0x79,
170        CarpenterCrateWoodL = 0x7A,
171        CarpenterToolsWall = 0x7B,
172        CarpenterLogCutter = 0x7C,
173        BarrelWoodCoal = 0x7D,
174        BarrelWoodWater = 0x7E,
175        BasketWovenL = 0x7F,
176        BasketWovenM = 0x80,
177        BasketWovenS = 0x81,
178        BonfireMLit = 0x82,
179        BonfireMUnlit = 0x83,
180        BucketWoodM = 0x84,
181        MirrorWoodM = 0x85,
182        SackLeatherM = 0x86,
183        TrophyframeWoodBear = 0x87,
184        TrophyframeWoodDeer = 0x88,
185        JugClayM = 0x89,
186        LogsWoodBranchS = 0x8A,
187        DiningtableWoodCorner = 0x8B,
188        DiningtableWoodBody = 0x8C,
189        BenchWoodEnd = 0x8D,
190        BenchWoodMiddle = 0x8E,
191        LogsWoodCoreEnd = 0x8F,
192        LogsWoodCoreMiddle = 0x90,
193        LogsWoodBarkEnd = 0x91,
194        LogsWoodBarkMiddle = 0x92,
195        LogsWoodBranchEnd = 0x93,
196        LogsWoodBranchMiddle = 0x94,
197        SeatWoodBlueMiddle = 0x95,
198        SeatWoodBlueSide = 0x96,
199        RopeCoilM = 0x97,
200        BedWoodWoodlandHead = 0x99,
201        BedWoodWoodlandMiddle = 0x9A,
202        BedWoodWoodlandTail = 0x9B,
203        BenchWoodWoodlandGreen1 = 0x9C,
204        BenchWoodWoodlandGreen2 = 0x9D,
205        BenchWoodWoodlandGreen3 = 0x9E,
206        BenchWoodWoodland = 0xA0,
207        ChairWoodWoodland = 0xA1,
208        ChairWoodWoodland2 = 0xA2,
209        CoatrackMetalWoodland = 0xA3,
210        CoatrackWoodWoodland = 0xA4,
211        DrawerWoodWoodlandL1 = 0xA5,
212        DrawerWoodWoodlandL2 = 0xA6,
213        DrawerWoodWoodlandM1 = 0xA7,
214        DrawerWoodWoodlandM2 = 0xA8,
215        DrawerWoodWoodlandS = 0xA9,
216        HandCartWoodHead = 0xAA,
217        HandCartWoodMiddle = 0xAB,
218        HandCartWoodTail = 0xAC,
219        FlowerpotWoodWoodlandS = 0xAD,
220        DiningtableWoodWoodlandRound = 0xAE,
221        DiningtableWoodWoodlandSquare = 0xAF,
222        TableWoodFancyWoodlandCorner = 0xB0,
223        TableWoodFancyWoodlandBody = 0xB1,
224        WardrobedoubleWoodWoodland = 0xB2,
225        WardrobedoubleWoodWoodland2 = 0xB3,
226        WardrobesingleWoodWoodland = 0xB4,
227        WardrobesingleWoodWoodland2 = 0xB5,
228        BedCliffHead = 0xB6,
229        BedCliffMiddle = 0xB7,
230        BedCliffTail = 0xB8,
231        BedCoastalHead = 0xB9,
232        BedCoastalMiddle = 0xBA,
233        BedCoastalTail = 0xBB,
234        BedDesertHead = 0xBC,
235        BedDesertMiddle = 0xBD,
236        BedDesertTail = 0xBE,
237        BedSavannahHead = 0xBF,
238        BedSavannahMiddle = 0xC0,
239        BedSavannahTail = 0xC1,
240        Ladder = 0xC2,
241        BookshelfEnd = 0xC3,
242        BookshelfMiddle = 0xC4,
243        HandrailWoodWoodlandBase = 0xC5,
244        HandrailWoodWoodlandMiddle = 0xC6,
245        HandrailWoodWoodlandTop = 0xC7,
246        BroomWoodWoodlandBlue = 0xC8,
247        ShovelWoodWoodlandGreen = 0xC9,
248        PitchforkWoodWoodlandGreen = 0xCA,
249        RakeWoodWoodland = 0xCB,
250        FenceWoodGateWoodland = 0xCC,
251    },
252    // Sprites representing plants that may grow over time (this does not include plant parts, like fruit).
253    Plant = 3 has Growth, Owned, SnowCovered, Collectable {
254        // Cacti
255        BarrelCactus    = 0x00,
256        RoundCactus     = 0x01,
257        ShortCactus     = 0x02,
258        MedFlatCactus   = 0x03,
259        ShortFlatCactus = 0x04,
260        LargeCactus     = 0x05,
261        TallCactus      = 0x06,
262        // Flowers
263        BlueFlower    = 0x10,
264        PinkFlower    = 0x11,
265        PurpleFlower  = 0x12,
266        RedFlower     = 0x13,
267        WhiteFlower   = 0x14,
268        YellowFlower  = 0x15,
269        Sunflower     = 0x16,
270        Moonbell      = 0x17,
271        Pyrebloom     = 0x18,
272        LushFlower    = 0x19,
273        LanternFlower = 0x1A,
274        // Grasses, ferns, and other 'wild' plants/fungi
275        // TODO: remove sizes, make part of the `Growth` attribute
276        LongGrass          = 0x20,
277        MediumGrass        = 0x21,
278        ShortGrass         = 0x22,
279        Fern               = 0x23,
280        LargeGrass         = 0x24,
281        Reed               = 0x25,
282        TaigaGrass         = 0x26,
283        GrassBlue          = 0x27,
284        SavannaGrass       = 0x28,
285        TallSavannaGrass   = 0x29,
286        RedSavannaGrass    = 0x2A,
287        SavannaBush        = 0x2B,
288        Welwitch           = 0x2C,
289        LeafyPlant         = 0x2D,
290        DeadBush           = 0x2E,
291        JungleFern         = 0x2F,
292        GrassBlueShort     = 0x30,
293        GrassBlueMedium    = 0x31,
294        GrassBlueLong      = 0x32,
295        CavernLillypadBlue = 0x33,
296        EnsnaringVines     = 0x34,
297        LillyPads          = 0x35,
298        JungleLeafyPlant   = 0x36,
299        JungleRedGrass     = 0x37,
300        LanternPlant       = 0x38,
301        SporeReed          = 0x39,
302        DeadPlant          = 0x3A,
303        // Crops, berries, and fungi
304        Corn          = 0x41,
305        WheatYellow   = 0x42,
306        WheatGreen    = 0x43, // TODO: Remove `WheatGreen`, make part of the `Growth` attribute
307        LingonBerry   = 0x44,
308        Blueberry     = 0x45,
309        Lettuce       = 0x46,
310        Pumpkin       = 0x47,
311        Carrot        = 0x48,
312        Tomato        = 0x49,
313        Radish        = 0x4A,
314        Turnip        = 0x4B,
315        Flax          = 0x4C,
316        Mushroom      = 0x4D,
317        CaveMushroom  = 0x4E,
318        Cotton        = 0x4F,
319        WildFlax      = 0x50,
320        SewerMushroom = 0x51,
321        LushMushroom  = 0x52,
322        RockyMushroom = 0x53,
323        GlowMushroom  = 0x54,
324        // Seaweeds, corals, and other underwater plants
325        StonyCoral       = 0x61,
326        SoftCoral        = 0x62,
327        SeaweedTemperate = 0x63,
328        SeaweedTropical  = 0x64,
329        GiantKelp        = 0x65,
330        BullKelp         = 0x66,
331        WavyAlgae        = 0x67,
332        SeaGrapes        = 0x68,
333        MermaidsFan      = 0x69,
334        SeaAnemone       = 0x6A,
335        Seagrass         = 0x6B,
336        RedAlgae         = 0x6C,
337        // Danglying ceiling plants/fungi
338        Liana                   = 0x71,
339        MycelBlue               = 0x72,
340        CeilingMushroom         = 0x73,
341        Mold                    = 0x74,
342        Root                    = 0x75,
343        CeilingLanternPlant     = 0x76,
344        CeilingLanternFlower    = 0x77,
345        CeilingJungleLeafyPlant = 0x78,
346    },
347    // Solid resources
348    // TODO: Remove small variants, make deposit size be an attribute
349    Resource = 4 has Owned, SnowCovered {
350        // Gems and ores
351        // Woods and twigs
352        Twigs     = 0x00,
353        Wood      = 0x01,
354        Bamboo    = 0x02,
355        Hardwood  = 0x03,
356        Ironwood  = 0x04,
357        Frostwood = 0x05,
358        Eldwood   = 0x06,
359        // Other
360        Apple       = 0x20,
361        Coconut     = 0x21,
362        Stones      = 0x22,
363        Seashells   = 0x23,
364        Beehive     = 0x24,
365        Bowl        = 0x25,
366        PotionMinor = 0x26,
367        PotionDummy = 0x27,
368        VialEmpty   = 0x28,
369    },
370    MineableResource = 5 has Damage {
371        Amethyst      = 0x00,
372        Ruby          = 0x01,
373        Sapphire      = 0x02,
374        Emerald       = 0x03,
375        Topaz         = 0x04,
376        Diamond       = 0x05,
377        Bloodstone    = 0x06,
378        Coal          = 0x07,
379        Cobalt        = 0x08,
380        Copper        = 0x09,
381        Iron          = 0x0A,
382        Tin           = 0x0B,
383        Silver        = 0x0C,
384        Gold          = 0x0D,
385        Velorite      = 0x0E,
386        VeloriteFrag  = 0x0F,
387        Mud           = 0x10,
388        Grave         = 0x11,
389    },
390    // Structural elements including doors and building parts
391    Structural = 6 has Ori {
392        // Doors and keyholes
393        Door         = 0x00,
394        DoorDark     = 0x01,
395        DoorWide     = 0x02,
396        BoneKeyhole  = 0x03,
397        BoneKeyDoor  = 0x04,
398        Keyhole      = 0x05,
399        KeyDoor      = 0x06,
400        GlassKeyhole = 0x07,
401        KeyholeBars  = 0x08,
402        HaniwaKeyDoor = 0x09,
403        HaniwaKeyhole = 0x0A,
404        TerracottaKeyDoor = 0x0B,
405        TerracottaKeyhole = 0x0C,
406        SahaginKeyhole = 0x0D,
407        SahaginKeyDoor = 0x0E,
408        VampireKeyDoor = 0x0F,
409        VampireKeyhole = 0x10,
410        MyrmidonKeyDoor = 0x11,
411        MyrmidonKeyhole = 0x12,
412        MinotaurKeyhole = 0x13,
413
414        // Windows
415        Window1      = 0x14,
416        Window2      = 0x15,
417        Window3      = 0x16,
418        Window4      = 0x17,
419        WitchWindow  = 0x18,
420        WindowArabic = 0x19,
421        // Walls
422        GlassBarrier    = 0x20,
423        SeaDecorBlock   = 0x21,
424        CliffDecorBlock = 0x22,
425        MagicalBarrier  = 0x23,
426        OneWayWall      = 0x24,
427        // Gates and grates
428        SeaDecorWindowHor = 0x30,
429        SeaDecorWindowVer = 0x31,
430        DropGate          = 0x32,
431        DropGateBottom    = 0x33,
432        WoodBarricades    = 0x34,
433        // Misc
434        Rope          = 0x40,
435        SeaDecorChain = 0x41,
436        IronSpike     = 0x42,
437        DoorBars      = 0x43,
438        HaniwaTrap    = 0x44,
439        HaniwaTrapTriggered = 0x45,
440        TerracottaStatue = 0x46,
441        TerracottaBlock = 0x47,
442        MetalChain = 0x48,
443    },
444    // Decorative items, both natural and artificial
445    Decor = 7 has Ori {
446        // Natural
447        Bones          = 0x00,
448        IceCrystal     = 0x01,
449        GlowIceCrystal = 0x02,
450        CrystalHigh    = 0x03,
451        CrystalLow     = 0x04,
452        UnderwaterVent = 0x05,
453        SeaUrchin      = 0x06,
454        IceSpike       = 0x07,
455        Orb            = 0x08,
456        EnsnaringWeb   = 0x09,
457        DiamondLight   = 0x0A,
458
459        // Artificial
460        Gravestone        = 0x10,
461        Melon             = 0x11,
462        ForgeTools        = 0x12,
463        JugAndBowlArabic  = 0x13,
464        JugArabic         = 0x14,
465        DecorSetArabic    = 0x15,
466        SepareArabic      = 0x16,
467        Candle            = 0x17,
468        SmithingTable     = 0x18,
469        Forge0            = 0x19,
470        GearWheel0        = 0x1A,
471        Quench0           = 0x1B,
472        SeaDecorEmblem    = 0x1C,
473        SeaDecorPillar    = 0x1D,
474        MagicalSeal       = 0x1E,
475        JugAndCupsCoastal = 0x1F,
476    },
477    Lamp = 8 has Ori, LightEnabled {
478        // Standalone lights
479        Lantern         = 0x00,
480        StreetLamp      = 0x01,
481        StreetLampTall  = 0x02,
482        SeashellLantern = 0x03,
483        FireBowlGround  = 0x04,
484        MesaLantern     = 0x05,
485        LanternpostWoodLantern = 0x06,
486        LampMetalShinglesRed = 0x07,
487        LampTerracotta = 0x08,
488        LampMetalShinglesCyan = 0x09,
489        LanternAirshipWallBlackS = 0x0A,
490        LanternAirshipWallBrownS = 0x0B,
491        LanternAirshipWallChestnutS = 0x0C,
492        LanternAirshipWallRedS = 0x0D,
493        LanternAirshipGroundBlackS = 0x0E,
494        LanternAirshipGroundBrownS = 0x0F,
495        LanternAirshipGroundChestnutS = 0x10,
496        LanternAirshipGroundRedS = 0x11,
497    },
498    Container = 9 has Ori, Owned, Collectable {
499        Chest             = 0x00,
500        DungeonChest0     = 0x01,
501        DungeonChest1     = 0x02,
502        DungeonChest2     = 0x03,
503        DungeonChest3     = 0x04,
504        DungeonChest4     = 0x05,
505        DungeonChest5     = 0x06,
506        CoralChest        = 0x07,
507        HaniwaUrn         = 0x08,
508        TerracottaChest   = 0x09,
509        SahaginChest      = 0x0A,
510        CommonLockedChest = 0x0B,
511        ChestBuried       = 0x0C,
512        Crate             = 0x0D,
513        Barrel            = 0x0E,
514        CrateBlock        = 0x0F,
515    },
516    Modular = 10 has Ori, AdjacentType {
517        FenceWoodWoodland = 0x00,
518    }
519}
520
521attributes! {
522    Ori { bits: 3, err: Infallible, from: |bits| Ok(Self(bits as u8)), into: |Ori(x)| x as u16 },
523    MirrorX { bits: 1, err: Infallible, from: |bits| Ok(Self(bits == 1)), into: |MirrorX(x)| x as u16 },
524    MirrorY { bits: 1, err: Infallible, from: |bits| Ok(Self(bits == 1)), into: |MirrorY(x)| x as u16 },
525    MirrorZ { bits: 1, err: Infallible, from: |bits| Ok(Self(bits == 1)), into: |MirrorZ(x)| x as u16 },
526    Growth { bits: 4, err: Infallible, from: |bits| Ok(Self(bits as u8)), into: |Growth(x)| x as u16 },
527    LightEnabled { bits: 1, err: Infallible, from: |bits| Ok(Self(bits == 1)), into: |LightEnabled(x)| x as u16 },
528    Collectable { bits: 1, err: Infallible, from: |bits| Ok(Self(bits == 1)), into: |Collectable(x)| x as u16 },
529    Damage { bits: 3, err: Infallible, from: |bits| Ok(Self(bits as u8)), into: |Damage(x)| x as u16 },
530    Owned { bits: 1, err: Infallible, from: |bits| Ok(Self(bits == 1)), into: |Owned(x)| x as u16 },
531    AdjacentType { bits: 3, err: Infallible, from: |bits| Ok(Self(bits as u8)), into: |AdjacentType(x)| x as u16 },
532    SnowCovered { bits: 1, err: Infallible, from: |bits| Ok(Self(bits == 1)), into: |SnowCovered(x)| x as u16 },
533}
534
535// The orientation of the sprite, 0..16
536#[derive(Copy, Clone, Default, Debug, PartialEq, Eq, Deserialize)]
537pub struct Ori(pub u8);
538
539#[derive(Copy, Clone, Default, Debug, PartialEq, Eq, Deserialize)]
540pub struct MirrorX(pub bool);
541
542#[derive(Copy, Clone, Default, Debug, PartialEq, Eq, Deserialize)]
543pub struct MirrorY(pub bool);
544
545#[derive(Copy, Clone, Default, Debug, PartialEq, Eq, Deserialize)]
546pub struct MirrorZ(pub bool);
547
548// The growth of the plant, 0..16
549#[derive(Copy, Clone, Debug, PartialEq, Eq, Deserialize)]
550pub struct Growth(pub u8);
551
552impl Default for Growth {
553    fn default() -> Self { Self(15) }
554}
555
556// Whether a light has been toggled on or off.
557#[derive(Copy, Clone, Debug, PartialEq, Eq, Deserialize)]
558pub struct LightEnabled(pub bool);
559
560impl Default for LightEnabled {
561    fn default() -> Self { Self(true) }
562}
563
564#[derive(Copy, Clone, Debug, PartialEq, Eq, Deserialize)]
565pub struct Collectable(pub bool);
566
567impl Default for Collectable {
568    fn default() -> Self { Self(true) }
569}
570
571#[derive(Default, Copy, Clone, Debug, PartialEq, Eq, Deserialize)]
572pub struct Owned(pub bool);
573
574/** Relative Neighbor Position:
575    an enum to determine the exact sprite for AdjacentType sprites
576    I - Straight - 0
577    L - Corner - 1
578    T - Junction - 2
579    X - Intersection - 3
580    End - single connection - 4
581**/
582
583#[derive(Default, Copy, Clone, Debug, PartialEq, Eq, Deserialize, FromPrimitive, Hash)]
584#[repr(u8)]
585pub enum RelativeNeighborPosition {
586    #[default]
587    I,
588    L,
589    T,
590    X,
591    End,
592}
593
594#[derive(Copy, Clone, Debug, PartialEq, Eq, Deserialize)]
595#[serde(from = "RelativeNeighborPosition")]
596pub struct AdjacentType(pub u8);
597
598impl From<RelativeNeighborPosition> for AdjacentType {
599    fn from(value: RelativeNeighborPosition) -> Self { Self(value as u8) }
600}
601
602impl Default for AdjacentType {
603    fn default() -> Self { Self::from(RelativeNeighborPosition::I) }
604}
605
606// Damage of an ore
607#[derive(Copy, Clone, Debug, PartialEq, Eq, Default, Deserialize)]
608pub struct Damage(pub u8);
609
610// Whether a sprite has snow on it
611#[derive(Copy, Clone, Default, Debug, PartialEq, Eq, Deserialize)]
612pub struct SnowCovered(pub bool);
613
614impl SpriteKind {
615    #[inline]
616    //#[tweak_fn]
617    pub fn solid_height(&self) -> Option<f32> {
618        // Beware: the height *must* be <= `MAX_HEIGHT` or the collision system will not
619        // properly detect it!
620        Some(match self {
621            SpriteKind::Bedroll => 0.3,
622            SpriteKind::BedrollSnow => 0.4,
623            SpriteKind::BedrollPirate => 0.3,
624            SpriteKind::Tomato => 1.65,
625            SpriteKind::BarrelCactus => 0.909,
626            SpriteKind::LargeCactus => 3.0,
627            SpriteKind::TallCactus => 2.63,
628            SpriteKind::Scarecrow => 3.0,
629            SpriteKind::Turnip => 0.36,
630            SpriteKind::Pumpkin => 0.81,
631            SpriteKind::Chest => 1.09,
632            SpriteKind::CommonLockedChest => 1.09,
633            SpriteKind::DungeonChest0 => 1.09,
634            SpriteKind::DungeonChest1 => 1.09,
635            SpriteKind::DungeonChest2 => 1.09,
636            SpriteKind::DungeonChest3 => 1.09,
637            SpriteKind::DungeonChest4 => 1.09,
638            SpriteKind::DungeonChest5 => 1.09,
639            SpriteKind::CoralChest => 1.09,
640            SpriteKind::HaniwaUrn => 1.09,
641            SpriteKind::SahaginChest => 1.09,
642            SpriteKind::TerracottaChest => 1.09,
643            SpriteKind::TerracottaStatue => 5.29,
644            SpriteKind::TerracottaBlock => 1.00,
645            SpriteKind::FenceWoodWoodland => 1.09,
646            SpriteKind::SeaDecorChain => 1.09,
647            SpriteKind::SeaDecorBlock => 1.00,
648            SpriteKind::SeaDecorWindowHor => 0.55,
649            SpriteKind::SeaDecorWindowVer => 1.09,
650            SpriteKind::SeaDecorPillar => 2.55,
651            SpriteKind::SeashellLantern => 2.09,
652            SpriteKind::MesaLantern => 1.3,
653            SpriteKind::Rope => 1.09,
654            SpriteKind::MetalChain => 1.09,
655            SpriteKind::StreetLamp => 2.65,
656            SpriteKind::Carrot => 0.18,
657            SpriteKind::Radish => 0.18,
658            SpriteKind::FireBowlGround => 0.55,
659            SpriteKind::BedMesa => 0.82,
660            SpriteKind::DungeonWallDecor => 1.0,
661            SpriteKind::Planter => 1.09,
662            SpriteKind::WardrobeSingleMesa => 2.0,
663            SpriteKind::WardrobeDoubleMesa => 2.0,
664            SpriteKind::MirrorMesa => 2.0,
665            SpriteKind::Mud => 0.36,
666            SpriteKind::ChestBuried => 0.91,
667            SpriteKind::StonyCoral => 1.4,
668            SpriteKind::CraftingBench => 1.18,
669            SpriteKind::Forge => 1.818,
670            SpriteKind::Cauldron => 1.27,
671            SpriteKind::SpinningWheel => 1.454,
672            SpriteKind::TanningRack => 1.454,
673            SpriteKind::Loom => 1.636,
674            SpriteKind::Anvil => 1.18,
675            SpriteKind::CookingPot => 1.090,
676            SpriteKind::DismantlingBench => 1.18,
677            SpriteKind::IceSpike => 1.0,
678            SpriteKind::RepairBench => 1.2,
679            SpriteKind::RoundCactus => 0.72,
680            SpriteKind::ShortCactus => 1.36,
681            SpriteKind::MedFlatCactus => 1.36,
682            SpriteKind::ShortFlatCactus => 0.91,
683            // TODO: Find suitable heights.
684            SpriteKind::Apple
685            | SpriteKind::Beehive
686            | SpriteKind::Velorite
687            | SpriteKind::VeloriteFrag
688            | SpriteKind::Coconut
689            | SpriteKind::StreetLampTall
690            | SpriteKind::Window1
691            | SpriteKind::Window2
692            | SpriteKind::Window3
693            | SpriteKind::Window4
694            | SpriteKind::DropGate
695            | SpriteKind::WitchWindow
696            | SpriteKind::SeaUrchin
697            | SpriteKind::IronSpike
698            | SpriteKind::GlassBarrier
699            | SpriteKind::GlassKeyhole
700            | SpriteKind::Keyhole
701            | SpriteKind::KeyDoor
702            | SpriteKind::BoneKeyhole
703            | SpriteKind::BoneKeyDoor
704            | SpriteKind::HaniwaKeyhole
705            | SpriteKind::HaniwaKeyDoor
706            | SpriteKind::SahaginKeyhole
707            | SpriteKind::SahaginKeyDoor
708            | SpriteKind::VampireKeyhole
709            | SpriteKind::VampireKeyDoor
710            | SpriteKind::HaniwaTrap
711            | SpriteKind::HaniwaTrapTriggered
712            | SpriteKind::TerracottaKeyDoor
713            | SpriteKind::TerracottaKeyhole
714            | SpriteKind::MyrmidonKeyDoor
715            | SpriteKind::MyrmidonKeyhole
716            | SpriteKind::MinotaurKeyhole
717            | SpriteKind::Bomb
718            | SpriteKind::OneWayWall
719            | SpriteKind::DoorBars
720            | SpriteKind::KeyholeBars
721            | SpriteKind::WoodBarricades
722            | SpriteKind::DiamondLight => 1.0,
723            // TODO: Figure out if this should be solid or not.
724            SpriteKind::Shelf => 1.0,
725            SpriteKind::Lantern => 0.9,
726            SpriteKind::CrystalHigh | SpriteKind::CrystalLow => 1.5,
727            SpriteKind::Bloodstone
728            | SpriteKind::Coal
729            | SpriteKind::Cobalt
730            | SpriteKind::Copper
731            | SpriteKind::Iron
732            | SpriteKind::Tin
733            | SpriteKind::Silver
734            | SpriteKind::Gold => 0.6,
735            SpriteKind::EnsnaringVines
736            | SpriteKind::CavernLillypadBlue
737            | SpriteKind::EnsnaringWeb => 0.15,
738            SpriteKind::LillyPads => 0.1,
739            SpriteKind::WindowArabic | SpriteKind::BookshelfArabic => 1.9,
740            SpriteKind::DecorSetArabic => 2.6,
741            SpriteKind::SepareArabic => 2.2,
742            SpriteKind::CushionArabic => 0.4,
743            SpriteKind::JugArabic => 1.4,
744            SpriteKind::TableArabicSmall => 0.9,
745            SpriteKind::TableArabicLarge => 1.0,
746            SpriteKind::TableCoastalLarge => 1.0,
747            SpriteKind::BenchCoastal => 1.0,
748            SpriteKind::CanapeArabic => 1.2,
749            SpriteKind::CupboardArabic => 4.5,
750            SpriteKind::WallTableArabic => 2.3,
751            SpriteKind::JugAndBowlArabic => 1.4,
752            SpriteKind::JugAndCupsCoastal => 1.4,
753            SpriteKind::Melon => 0.7,
754            SpriteKind::OvenArabic => 3.2,
755            SpriteKind::FountainArabic => 2.4,
756            SpriteKind::Hearth => 2.3,
757            SpriteKind::ForgeTools => 2.8,
758            SpriteKind::CliffDecorBlock | SpriteKind::FireBlock => 1.0,
759            SpriteKind::Wood
760            | SpriteKind::Hardwood
761            | SpriteKind::Ironwood
762            | SpriteKind::Frostwood
763            | SpriteKind::Eldwood => 7.0 / 11.0,
764            SpriteKind::Bamboo => 9.0 / 11.0,
765            SpriteKind::MagicalBarrier => 3.0,
766            SpriteKind::MagicalSeal => 1.0,
767            SpriteKind::Helm => 1.909,
768            SpriteKind::Sign => 16.0 / 11.0,
769            SpriteKind::SmithingTable => 13.0 / 11.0,
770            SpriteKind::Forge0 => 17.0 / 11.0,
771            SpriteKind::GearWheel0 => 3.0 / 11.0,
772            SpriteKind::Quench0 => 8.0 / 11.0,
773            SpriteKind::HotSurface => 0.01,
774            SpriteKind::Barrel => 1.0,
775            SpriteKind::CrateBlock => 1.0,
776            SpriteKind::BarrelWoodWater | SpriteKind::BarrelWoodCoal => 1.545,
777            SpriteKind::LanternpostWoodLantern | SpriteKind::LanternpostWoodUpper => 2.000,
778            SpriteKind::LanternpostWoodBase => 3.000,
779            SpriteKind::LampMetalShinglesRed => 1.000,
780            SpriteKind::LampMetalShinglesCyan => 1.000,
781            SpriteKind::LampMetalBase => 2.818,
782            SpriteKind::LampTerracotta => 1.727,
783            SpriteKind::BlacksmithBellows => 0.545,
784            SpriteKind::CarpenterTable => 2.000,
785            SpriteKind::CarpenterCrateWoodS => 0.727,
786            SpriteKind::CarpenterCrateWoodL => 1.273,
787            SpriteKind::CarpenterLogCutter => 1.545,
788            SpriteKind::BasketWovenL | SpriteKind::JugClayM => 1.000,
789            SpriteKind::BasketWovenM => 0.909,
790            SpriteKind::BasketWovenS => 0.818,
791            SpriteKind::BonfireMLit | SpriteKind::BonfireMUnlit => 2.273,
792            SpriteKind::BucketWoodM | SpriteKind::SackLeatherM => 1.091,
793            SpriteKind::MirrorWoodM => 1.364,
794            SpriteKind::TrophyframeWoodBear => 1.455,
795            SpriteKind::TrophyframeWoodDeer => 1.727,
796            SpriteKind::ChestWoodDouble => 1.182,
797            SpriteKind::DiningtableWoodCorner => 1.273,
798            SpriteKind::DiningtableWoodBody => 1.273,
799            SpriteKind::BenchWoodEnd => 0.636,
800            SpriteKind::BenchWoodMiddle => 0.636,
801            SpriteKind::LogsWoodCoreEnd => 0.818,
802            SpriteKind::LogsWoodCoreMiddle => 0.818,
803            SpriteKind::LogsWoodBarkEnd => 1.091,
804            SpriteKind::LogsWoodBarkMiddle => 1.091,
805            SpriteKind::LogsWoodBranchEnd => 1.091,
806            SpriteKind::LogsWoodBranchMiddle => 1.091,
807            SpriteKind::LogsWoodBranchS => 1.091,
808            SpriteKind::SeatWoodBlueMiddle => 1.818,
809            SpriteKind::SeatWoodBlueSide => 1.818,
810            SpriteKind::LanternAirshipWallBlackS
811            | SpriteKind::LanternAirshipWallBrownS
812            | SpriteKind::LanternAirshipWallChestnutS
813            | SpriteKind::LanternAirshipWallRedS => 1.182,
814            SpriteKind::LanternAirshipGroundBlackS
815            | SpriteKind::LanternAirshipGroundBrownS
816            | SpriteKind::LanternAirshipGroundChestnutS
817            | SpriteKind::LanternAirshipGroundRedS => 0.909,
818            SpriteKind::RopeCoilM => 0.363,
819            SpriteKind::BedCliffHead => 0.636,
820            SpriteKind::BedCliffMiddle => 0.636,
821            SpriteKind::BedCliffTail => 0.636,
822            SpriteKind::BedCoastalHead => 0.636,
823            SpriteKind::BedCoastalMiddle => 0.636,
824            SpriteKind::BedCoastalTail => 0.636,
825            SpriteKind::BedDesertHead => 0.545,
826            SpriteKind::BedDesertMiddle => 0.545,
827            SpriteKind::BedDesertTail => 0.545,
828            SpriteKind::BedSavannahHead => 0.545,
829            SpriteKind::BedSavannahMiddle => 0.545,
830            SpriteKind::BedSavannahTail => 0.545,
831            SpriteKind::BedWoodWoodlandHead => 0.727,
832            SpriteKind::BedWoodWoodlandMiddle => 0.727,
833            SpriteKind::BedWoodWoodlandTail => 0.727,
834            SpriteKind::BookshelfEnd => 3.0,
835            SpriteKind::BookshelfMiddle => 3.0,
836            SpriteKind::BenchWoodWoodlandGreen1 => 1.545,
837            SpriteKind::BenchWoodWoodlandGreen2 => 1.545,
838            SpriteKind::BenchWoodWoodlandGreen3 => 1.545,
839            SpriteKind::BenchWoodWoodland => 1.545,
840            SpriteKind::ChairWoodWoodland => 1.636,
841            SpriteKind::ChairWoodWoodland2 => 1.727,
842            SpriteKind::CoatrackMetalWoodland => 2.364,
843            SpriteKind::CoatrackWoodWoodland => 2.364,
844            SpriteKind::Crate => 0.909,
845            SpriteKind::DrawerWoodWoodlandS => 1.000,
846            SpriteKind::DrawerWoodWoodlandM1 => 0.909,
847            SpriteKind::DrawerWoodWoodlandM2 => 0.909,
848            SpriteKind::DrawerWoodWoodlandL1 => 1.273,
849            SpriteKind::DrawerWoodWoodlandL2 => 1.273,
850            SpriteKind::DiningtableWoodWoodlandRound => 1.273,
851            SpriteKind::DiningtableWoodWoodlandSquare => 1.273,
852            SpriteKind::TableWoodFancyWoodlandCorner => 1.273,
853            SpriteKind::TableWoodFancyWoodlandBody => 1.273,
854            SpriteKind::WardrobesingleWoodWoodland => 2.364,
855            SpriteKind::WardrobesingleWoodWoodland2 => 2.364,
856            SpriteKind::WardrobedoubleWoodWoodland => 2.364,
857            SpriteKind::WardrobedoubleWoodWoodland2 => 2.364,
858            SpriteKind::FlowerpotWoodWoodlandS => 0.455,
859            SpriteKind::HandCartWoodHead => 1.091,
860            SpriteKind::HandCartWoodMiddle => 1.091,
861            SpriteKind::HandCartWoodTail => 1.091,
862            SpriteKind::HandrailWoodWoodlandBase | SpriteKind::HandrailWoodWoodlandMiddle => 1.727,
863            SpriteKind::HandrailWoodWoodlandTop => 1.181,
864            _ => return None,
865        })
866    }
867
868    pub fn valid_collision_dir(
869        &self,
870        entity_aabb: Aabb<f32>,
871        block_aabb: Aabb<f32>,
872        move_dir: Vec3<f32>,
873        parent: &Block,
874    ) -> bool {
875        match self {
876            SpriteKind::OneWayWall => {
877                // Find the intrusion vector of the collision
878                let dir = entity_aabb.collision_vector_with_aabb(block_aabb);
879
880                // Determine an appropriate resolution vector (i.e: the minimum distance
881                // needed to push out of the block)
882                let max_axis = dir.map(|e| e.abs()).reduce_partial_min();
883                let resolve_dir = -dir.map(|e| {
884                    if e.abs().to_bits() == max_axis.to_bits() {
885                        e.signum()
886                    } else {
887                        0.0
888                    }
889                });
890
891                let is_moving_into = move_dir.dot(resolve_dir) <= 0.0;
892
893                is_moving_into
894                    && parent.get_attr().is_ok_and(|Ori(ori)| {
895                        Vec2::new(
896                            0.0,
897                            parent.get_attr::<MirrorY>().map_or(1.0, |m| match m.0 {
898                                true => -1.0,
899                                false => 1.0,
900                            }),
901                        )
902                        .rotated_z(std::f32::consts::PI * 0.25 * ori as f32)
903                        .with_z(0.0)
904                        .map2(resolve_dir, |e, r| (e - r).abs() < 0.1)
905                        .reduce_and()
906                    })
907            },
908            _ => true,
909        }
910    }
911
912    /// What loot table would collecting this sprite draw from, by default?
913    ///
914    /// NOTE: `Item::try_reclaim_from_block` is what you probably looking for
915    /// instead.
916    ///
917    /// None = block cannot be collected
918    /// Some(None) = block can be collected, but does not give back an item
919    /// Some(Some(_)) = block can be collected and gives back an item
920    #[inline]
921    pub fn default_loot_spec(&self) -> Option<Option<LootSpec<&'static str>>> {
922        let item = LootSpec::Item;
923        let table = LootSpec::LootTable;
924        Some(Some(match self {
925            SpriteKind::Apple => item("common.items.food.apple"),
926            SpriteKind::Mushroom => item("common.items.food.mushroom"),
927            SpriteKind::Velorite => item("common.items.mineral.ore.velorite"),
928            SpriteKind::VeloriteFrag => item("common.items.mineral.ore.veloritefrag"),
929            //SpriteKind::BlueFlower => item("common.items.flowers.blue"),
930            //SpriteKind::PinkFlower => item("common.items.flowers.pink"),
931            //SpriteKind::PurpleFlower => item("common.items.flowers.purple"),
932            SpriteKind::RedFlower => item("common.items.flowers.red"),
933            //SpriteKind::WhiteFlower => item("common.items.flowers.white"),
934            //SpriteKind::YellowFlower => item("common.items.flowers.yellow"),
935            SpriteKind::Sunflower => item("common.items.flowers.sunflower"),
936            //SpriteKind::LongGrass => item("common.items.grasses.long"),
937            //SpriteKind::MediumGrass => item("common.items.grasses.medium"),
938            //SpriteKind::ShortGrass => item("common.items.grasses.short"),
939            SpriteKind::Lettuce => item("common.items.food.lettuce"),
940            SpriteKind::Coconut => item("common.items.food.coconut"),
941            SpriteKind::Beehive => item("common.items.crafting_ing.honey"),
942            SpriteKind::Stones => item("common.items.crafting_ing.stones"),
943            SpriteKind::Twigs => item("common.items.crafting_ing.twigs"),
944            SpriteKind::VialEmpty => item("common.items.crafting_ing.empty_vial"),
945            SpriteKind::Bowl => item("common.items.crafting_ing.bowl"),
946            SpriteKind::PotionMinor => item("common.items.consumable.potion_minor"),
947            SpriteKind::Amethyst => item("common.items.mineral.gem.amethyst"),
948            SpriteKind::Ruby => item("common.items.mineral.gem.ruby"),
949            SpriteKind::Diamond => item("common.items.mineral.gem.diamond"),
950            SpriteKind::Sapphire => item("common.items.mineral.gem.sapphire"),
951            SpriteKind::Topaz => item("common.items.mineral.gem.topaz"),
952            SpriteKind::Emerald => item("common.items.mineral.gem.emerald"),
953            SpriteKind::Bloodstone => item("common.items.mineral.ore.bloodstone"),
954            SpriteKind::Coal => item("common.items.mineral.ore.coal"),
955            SpriteKind::Cobalt => item("common.items.mineral.ore.cobalt"),
956            SpriteKind::Copper => item("common.items.mineral.ore.copper"),
957            SpriteKind::Iron => item("common.items.mineral.ore.iron"),
958            SpriteKind::Tin => item("common.items.mineral.ore.tin"),
959            SpriteKind::Silver => item("common.items.mineral.ore.silver"),
960            SpriteKind::Gold => item("common.items.mineral.ore.gold"),
961            SpriteKind::Cotton => item("common.items.crafting_ing.cotton_boll"),
962            SpriteKind::Moonbell => item("common.items.flowers.moonbell"),
963            SpriteKind::Pyrebloom => item("common.items.flowers.pyrebloom"),
964            SpriteKind::WildFlax => item("common.items.flowers.wild_flax"),
965            SpriteKind::Seashells => item("common.items.crafting_ing.seashells"),
966            SpriteKind::RoundCactus => item("common.items.crafting_ing.cactus"),
967            SpriteKind::ShortFlatCactus => item("common.items.crafting_ing.cactus"),
968            SpriteKind::MedFlatCactus => item("common.items.crafting_ing.cactus"),
969            SpriteKind::Bomb => item("common.items.utility.bomb"),
970            SpriteKind::DungeonChest0 => table("common.loot_tables.dungeon.gnarling.chest"),
971            SpriteKind::DungeonChest1 => table("common.loot_tables.dungeon.adlet.chest"),
972            SpriteKind::DungeonChest2 => table("common.loot_tables.dungeon.sahagin.chest"),
973            SpriteKind::DungeonChest3 => table("common.loot_tables.dungeon.haniwa.chest"),
974            SpriteKind::DungeonChest4 => table("common.loot_tables.dungeon.myrmidon.chest"),
975            SpriteKind::DungeonChest5 => table("common.loot_tables.dungeon.cultist.chest"),
976            SpriteKind::Chest => table("common.loot_tables.sprite.chest"),
977            SpriteKind::CommonLockedChest => table("common.loot_tables.dungeon.sahagin.chest"),
978            SpriteKind::ChestBuried => table("common.loot_tables.sprite.chest-buried"),
979            SpriteKind::CoralChest => table("common.loot_tables.dungeon.sea_chapel.chest_coral"),
980            SpriteKind::HaniwaUrn => table("common.loot_tables.dungeon.haniwa.key"),
981            SpriteKind::TerracottaChest => {
982                table("common.loot_tables.dungeon.terracotta.chest_terracotta")
983            },
984            SpriteKind::SahaginChest => table("common.loot_tables.dungeon.sahagin.key_chest"),
985            SpriteKind::Mud => table("common.loot_tables.sprite.mud"),
986            SpriteKind::Grave => table("common.loot_tables.sprite.mud"),
987            SpriteKind::Crate => table("common.loot_tables.sprite.crate"),
988            SpriteKind::Wood => item("common.items.log.wood"),
989            SpriteKind::Bamboo => item("common.items.log.bamboo"),
990            SpriteKind::Hardwood => item("common.items.log.hardwood"),
991            SpriteKind::Ironwood => item("common.items.log.ironwood"),
992            SpriteKind::Frostwood => item("common.items.log.frostwood"),
993            SpriteKind::Eldwood => item("common.items.log.eldwood"),
994            SpriteKind::MagicalBarrier => table("common.loot_tables.sprite.chest"),
995            SpriteKind::Keyhole
996            | SpriteKind::BoneKeyhole
997            | SpriteKind::HaniwaKeyhole
998            | SpriteKind::VampireKeyhole
999            | SpriteKind::GlassKeyhole
1000            | SpriteKind::KeyholeBars
1001            | SpriteKind::SahaginKeyhole
1002            | SpriteKind::TerracottaKeyhole
1003            | SpriteKind::MyrmidonKeyhole
1004            | SpriteKind::MinotaurKeyhole => {
1005                return Some(None);
1006            },
1007            _ => return None,
1008        }))
1009    }
1010
1011    /// Is this sprite *expected* to be picked up?
1012    ///
1013    /// None means sprite can't be collected
1014    /// Some(None) means sprite can be collected without any mine tool
1015    /// Some(Some(_)) means sprite can be collected but requires a tool
1016    #[inline]
1017    pub fn default_tool(&self) -> Option<Option<ToolKind>> {
1018        self.default_loot_spec().map(|_| self.mine_tool())
1019    }
1020
1021    /// Is the sprite should behave like a container? Whatever that means.
1022    ///
1023    /// If you just asking where you can collect this sprite without any tool,
1024    /// use `SpriteKind::is_collectible`.
1025    ///
1026    /// Use `SpriteKind::default_tool` if you can afford potential
1027    /// false positives, it doesn't require SpriteCfg.
1028    ///
1029    /// Implicit invariant of this method is that only sprites listed here
1030    /// can use SpriteCfg.loot_table.
1031    #[inline]
1032    pub fn is_defined_as_container(&self) -> bool { self.category() == Category::Container }
1033
1034    #[inline]
1035    /// Some items may drop random items, yet aren't containers.
1036    pub fn should_drop_mystery(&self) -> bool {
1037        self.is_defined_as_container()
1038            || matches!(
1039                self.default_loot_spec(),
1040                Some(Some(LootSpec::LootTable { .. } | LootSpec::Lottery { .. }))
1041            )
1042    }
1043
1044    /// Get the position and direction to mount this sprite if any.
1045    #[inline]
1046    //#[tweak_fn]
1047    pub fn mount_offset(&self) -> Option<(Vec3<f32>, Vec3<f32>)> {
1048        match self {
1049            SpriteKind::ChairWoodWoodland
1050            | SpriteKind::ChairWoodWoodland2
1051            | SpriteKind::BenchWoodWoodlandGreen1
1052            | SpriteKind::BenchWoodWoodlandGreen2
1053            | SpriteKind::BenchWoodWoodlandGreen3
1054            | SpriteKind::BenchWoodWoodland
1055            | SpriteKind::BenchWoodEnd
1056            | SpriteKind::BenchWoodMiddle
1057            | SpriteKind::BenchCoastal => Some((Vec3::new(0.0, 0.0, 0.5), Vec3::unit_x())),
1058            SpriteKind::SeatWoodBlueMiddle | SpriteKind::SeatWoodBlueSide => {
1059                Some((Vec3::new(0.4, 0.0, 0.5), Vec3::unit_x()))
1060            },
1061            SpriteKind::Helm => Some((Vec3::new(0.0, -1.1, 0.0), Vec3::unit_y())),
1062            SpriteKind::BedWoodWoodlandHead
1063            | SpriteKind::BedCliffHead
1064            | SpriteKind::BedDesertHead
1065            | SpriteKind::BedCoastalHead
1066            | SpriteKind::BedSavannahHead => Some((Vec3::new(1.4, 0.0, 0.5), Vec3::unit_x())),
1067            SpriteKind::BedMesa => Some((Vec3::new(0.0, 0.0, 0.6), -Vec3::unit_y())),
1068            SpriteKind::BedrollSnow | SpriteKind::BedrollPirate => {
1069                Some((Vec3::new(0.0, 0.0, 0.1), -Vec3::unit_x()))
1070            },
1071            SpriteKind::Bedroll => Some((Vec3::new(0.0, 0.0, 0.1), Vec3::unit_y())),
1072            _ => None,
1073        }
1074    }
1075
1076    pub fn is_bed(&self) -> bool {
1077        matches!(
1078            self,
1079            SpriteKind::BedWoodWoodlandHead
1080                | SpriteKind::BedMesa
1081                | SpriteKind::BedCliffHead
1082                | SpriteKind::BedCoastalHead
1083                | SpriteKind::BedDesertHead
1084                | SpriteKind::BedSavannahHead
1085                | SpriteKind::Bedroll
1086                | SpriteKind::BedrollSnow
1087                | SpriteKind::BedrollPirate
1088        )
1089    }
1090
1091    #[inline]
1092    pub fn is_mountable(&self) -> bool { self.mount_offset().is_some() }
1093
1094    /// Get the buff provided by the block (currently used for mounting)
1095    #[inline]
1096    pub fn mount_buffs(&self) -> Option<Vec<BuffEffect>> {
1097        match self {
1098            SpriteKind::BedWoodWoodlandHead
1099            | SpriteKind::BedMesa
1100            | SpriteKind::BedrollSnow
1101            | SpriteKind::BedrollPirate
1102            | SpriteKind::Bedroll => Some(vec![BuffEffect {
1103                kind: BuffKind::RestingHeal,
1104                data: BuffData::new(0.02, Some(Secs(1.0))),
1105                cat_ids: Vec::new(),
1106            }]),
1107            _ => None,
1108        }
1109    }
1110
1111    #[inline]
1112    pub fn is_controller(&self) -> bool { matches!(self, SpriteKind::Helm) }
1113
1114    #[inline]
1115    pub fn is_door(&self) -> bool {
1116        matches!(
1117            self,
1118            SpriteKind::Door | SpriteKind::DoorWide | SpriteKind::DoorDark
1119        )
1120    }
1121
1122    /// Which tool (if any) is needed to collect this sprite?
1123    #[inline]
1124    pub fn mine_tool(&self) -> Option<ToolKind> {
1125        match self {
1126            SpriteKind::Velorite
1127            | SpriteKind::VeloriteFrag
1128            // Gems
1129            | SpriteKind::Amethyst
1130            | SpriteKind::Ruby
1131            | SpriteKind::Diamond
1132            | SpriteKind::Sapphire
1133            | SpriteKind::Emerald
1134            | SpriteKind::Topaz
1135            | SpriteKind::Bloodstone
1136            | SpriteKind::Coal
1137            | SpriteKind::Cobalt
1138            | SpriteKind::Copper
1139            | SpriteKind::Iron
1140            | SpriteKind::Tin
1141            | SpriteKind::Silver
1142            | SpriteKind::Gold => Some(ToolKind::Pick),
1143            SpriteKind::Grave | SpriteKind::Mud => Some(ToolKind::Shovel),
1144            _ => None,
1145        }
1146    }
1147
1148    pub fn required_mine_damage(&self) -> Option<u8> {
1149        Some(match self {
1150            SpriteKind::Gold => 6,
1151            SpriteKind::Silver => 6,
1152            SpriteKind::Bloodstone => 6,
1153            SpriteKind::Cobalt => 6,
1154            SpriteKind::Coal => 6,
1155            SpriteKind::Iron => 6,
1156            SpriteKind::Copper => 3,
1157            SpriteKind::Tin => 3,
1158            SpriteKind::Amethyst => 3,
1159            SpriteKind::Ruby => 6,
1160            SpriteKind::Sapphire => 3,
1161            SpriteKind::Emerald => 3,
1162            SpriteKind::Topaz => 3,
1163            SpriteKind::Diamond => 6,
1164            SpriteKind::Velorite => 3,
1165            SpriteKind::VeloriteFrag => 2,
1166            _ => return None,
1167        })
1168    }
1169
1170    /// Defines how much damage it takes for a mined resource to possibly
1171    /// make an extra drop.
1172    pub fn mine_drop_interval(&self) -> u8 {
1173        match self {
1174            SpriteKind::Gold => 2,
1175            SpriteKind::Silver => 2,
1176            SpriteKind::Bloodstone => 2,
1177            SpriteKind::Cobalt => 2,
1178            SpriteKind::Coal => 2,
1179            SpriteKind::Iron => 2,
1180            SpriteKind::Copper => 1,
1181            SpriteKind::Tin => 1,
1182            SpriteKind::Emerald => 1,
1183            SpriteKind::Sapphire => 1,
1184            SpriteKind::Amethyst => 1,
1185            SpriteKind::Topaz => 1,
1186            SpriteKind::Diamond => 2,
1187            SpriteKind::Ruby => 2,
1188            SpriteKind::Velorite => 1,
1189            SpriteKind::VeloriteFrag => 1,
1190            _ => 1,
1191        }
1192    }
1193
1194    /// Requires this item in the inventory to harvest, uses item_definition_id
1195    // TODO: Do we want to consolidate this with mine_tool at all? Main differences
1196    // are that mine tool requires item to be an equippable tool, be equipped, and
1197    // does not consume item while required_item requires that the item be in the
1198    // inventory and will consume the item on collecting the sprite.
1199    pub fn unlock_condition(&self, cfg: Option<SpriteCfg>) -> UnlockKind {
1200        cfg.and_then(|cfg| cfg.unlock)
1201            .unwrap_or_else(|| match self {
1202                SpriteKind::CommonLockedChest => UnlockKind::Consumes(
1203                    ItemDefinitionIdOwned::Simple(String::from("common.items.utility.lockpick_0")),
1204                ),
1205                SpriteKind::SahaginKeyhole => UnlockKind::Consumes(ItemDefinitionIdOwned::Simple(
1206                    String::from("common.items.keys.sahagin_key"),
1207                )),
1208                SpriteKind::BoneKeyhole => UnlockKind::Consumes(ItemDefinitionIdOwned::Simple(
1209                    String::from("common.items.keys.bone_key"),
1210                )),
1211                SpriteKind::HaniwaKeyhole => UnlockKind::Consumes(ItemDefinitionIdOwned::Simple(
1212                    String::from("common.items.keys.haniwa_key"),
1213                )),
1214                SpriteKind::VampireKeyhole => UnlockKind::Consumes(ItemDefinitionIdOwned::Simple(
1215                    String::from("common.items.keys.vampire_key"),
1216                )),
1217                SpriteKind::GlassKeyhole => UnlockKind::Consumes(ItemDefinitionIdOwned::Simple(
1218                    String::from("common.items.keys.glass_key"),
1219                )),
1220                SpriteKind::TerracottaChest => UnlockKind::Consumes(
1221                    ItemDefinitionIdOwned::Simple(String::from(
1222                        "common.items.keys.terracotta_key_chest",
1223                    ))
1224                    .to_owned(),
1225                ),
1226                SpriteKind::TerracottaKeyhole => UnlockKind::Consumes(
1227                    ItemDefinitionIdOwned::Simple(String::from(
1228                        "common.items.keys.terracotta_key_door",
1229                    ))
1230                    .to_owned(),
1231                ),
1232                SpriteKind::MyrmidonKeyhole => UnlockKind::Consumes(
1233                    ItemDefinitionIdOwned::Simple(String::from("common.items.keys.myrmidon_key"))
1234                        .to_owned(),
1235                ),
1236                SpriteKind::MinotaurKeyhole => UnlockKind::Consumes(
1237                    ItemDefinitionIdOwned::Simple(String::from("common.items.keys.minotaur_key"))
1238                        .to_owned(),
1239                ),
1240                _ => UnlockKind::Free,
1241            })
1242    }
1243
1244    /// Get the [`Content`] that this sprite is labelled with.
1245    pub fn content(&self, cfg: Option<SpriteCfg>) -> Option<Content> {
1246        cfg.and_then(|cfg| cfg.content)
1247    }
1248
1249    // TODO: phase out use of this method in favour of `sprite.has_attr::<Ori>()`
1250    #[inline]
1251    pub fn has_ori(&self) -> bool { self.category().has_attr::<Ori>() }
1252}
1253
1254impl fmt::Display for SpriteKind {
1255    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "{:?}", self) }
1256}
1257
1258use strum::IntoEnumIterator;
1259
1260lazy_static! {
1261    pub static ref SPRITE_KINDS: HashMap<String, SpriteKind> =
1262        SpriteKind::iter().map(|sk| (sk.to_string(), sk)).collect();
1263}
1264
1265impl<'a> TryFrom<&'a str> for SpriteKind {
1266    type Error = ();
1267
1268    #[inline]
1269    fn try_from(s: &'a str) -> Result<Self, Self::Error> { SPRITE_KINDS.get(s).copied().ok_or(()) }
1270}
1271
1272#[derive(PartialEq, Clone, Debug, Serialize, Deserialize)]
1273pub enum UnlockKind {
1274    /// The sprite can be freely unlocked without any conditions
1275    Free,
1276    /// The sprite requires that the opening character has a given item in their
1277    /// inventory
1278    Requires(ItemDefinitionIdOwned),
1279    /// The sprite will consume the given item from the opening character's
1280    /// inventory
1281    Consumes(ItemDefinitionIdOwned),
1282}
1283
1284#[derive(Default, PartialEq, Clone, Debug, Serialize, Deserialize)]
1285pub struct SpriteCfg {
1286    /// Signifies that this sprite needs an item to be unlocked.
1287    pub unlock: Option<UnlockKind>,
1288    /// Signifies a text associated with this sprite.
1289    /// This also allows (but not requires) internationalization.
1290    ///
1291    /// Notice boards are an example of sprite that uses this.
1292    pub content: Option<Content>,
1293    /// Signifies a loot table associated with this sprite. The string referes
1294    /// to the loot table asset identifier.
1295    ///
1296    /// Chests are an example of sprite that can use this. For simple sprites
1297    /// like flowers using default_loot_spec() method is recommended instead.
1298    ///
1299    /// If you place a custom loot table on a sprite, make sure it's listed in
1300    /// SpriteKind::is_defined_as_container() to avoid minor bugs, which should
1301    /// be enforced in tests, in possible.
1302    ///
1303    /// NOTE: this is sent to the clients, we may potentionally strip this info
1304    /// on sending.
1305    pub loot_table: Option<String>,
1306}
1307
1308#[cfg(test)]
1309pub mod tests {
1310    use super::*;
1311
1312    #[test]
1313    fn sprite_conv_kind() {
1314        for sprite in SpriteKind::all() {
1315            let block = Block::air(*sprite);
1316            assert_eq!(block.sprite_category(), Some(sprite.category()));
1317            assert_eq!(block.get_sprite(), Some(*sprite));
1318        }
1319    }
1320
1321    #[test]
1322    fn sprite_attr() {
1323        for category in Category::all() {
1324            if category.has_attr::<Ori>() {
1325                for sprite in category.all_sprites() {
1326                    for i in 0..4 {
1327                        let block = Block::air(*sprite).with_attr(Ori(i)).unwrap();
1328                        assert_eq!(block.get_attr::<Ori>().unwrap(), Ori(i));
1329                        assert_eq!(block.get_sprite(), Some(*sprite));
1330                    }
1331                }
1332            }
1333            if category.has_attr::<Growth>() {
1334                for sprite in category.all_sprites() {
1335                    for i in 0..16 {
1336                        let block = Block::air(*sprite).with_attr(Growth(i)).unwrap();
1337                        assert_eq!(block.get_attr::<Growth>().unwrap(), Growth(i));
1338                        assert_eq!(block.get_sprite(), Some(*sprite));
1339                    }
1340                }
1341            }
1342        }
1343    }
1344}