1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
use crate::{
    comp::{
        item::{
            armor::ArmorKind,
            tool::{Tool, ToolKind},
            Item, ItemKind, Utility,
        },
        Density, Mass, Ori,
    },
    consts::WATER_DENSITY,
    make_case_elim,
    util::Dir,
};
use rand::prelude::*;
use serde::{Deserialize, Serialize};
use std::f32::consts::PI;
use vek::Vec3;

make_case_elim!(
    armor,
    #[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
    #[repr(u32)]
    pub enum ItemDropArmorKind {
        Shoulder = 0,
        Chest = 1,
        Belt = 2,
        Hand = 3,
        Pants = 4,
        Foot = 5,
        Back = 6,
        Ring = 7,
        Neck = 8,
        Head = 9,
        Tabard = 10,
        Bag = 11,
    }
);

make_case_elim!(
    body,
    #[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
    #[repr(u32)]
    pub enum Body {
        Tool(tool: ToolKind) = 0,
        ModularComponent = 1,
        Lantern = 2,
        Glider = 3,
        Armor(armor: ItemDropArmorKind) = 4,
        Utility = 5,
        Consumable = 6,
        Throwable = 7,
        Ingredient = 8,
        Coins = 9,
        CoinPouch = 10,
        Empty = 11,
    }
);

impl From<Body> for super::Body {
    fn from(body: Body) -> Self { super::Body::ItemDrop(body) }
}

impl From<&Item> for Body {
    fn from(item: &Item) -> Self {
        match &*item.kind() {
            ItemKind::Tool(Tool { kind, .. }) => Body::Tool(*kind),
            ItemKind::ModularComponent(_) => Body::ModularComponent,
            ItemKind::Lantern(_) => Body::Lantern,
            ItemKind::Glider => Body::Glider,
            ItemKind::Armor(armor) => match armor.kind {
                ArmorKind::Shoulder => Body::Armor(ItemDropArmorKind::Shoulder),
                ArmorKind::Chest => Body::Armor(ItemDropArmorKind::Chest),
                ArmorKind::Belt => Body::Armor(ItemDropArmorKind::Belt),
                ArmorKind::Hand => Body::Armor(ItemDropArmorKind::Hand),
                ArmorKind::Pants => Body::Armor(ItemDropArmorKind::Pants),
                ArmorKind::Foot => Body::Armor(ItemDropArmorKind::Foot),
                ArmorKind::Back => Body::Armor(ItemDropArmorKind::Back),
                ArmorKind::Backpack => Body::Armor(ItemDropArmorKind::Back),
                ArmorKind::Ring => Body::Armor(ItemDropArmorKind::Ring),
                ArmorKind::Neck => Body::Armor(ItemDropArmorKind::Neck),
                ArmorKind::Head => Body::Armor(ItemDropArmorKind::Head),
                ArmorKind::Tabard => Body::Armor(ItemDropArmorKind::Tabard),
                ArmorKind::Bag => Body::Armor(ItemDropArmorKind::Bag),
            },
            ItemKind::Utility { kind, .. } => match kind {
                Utility::Coins => {
                    if item.amount() > 100 {
                        Body::CoinPouch
                    } else {
                        Body::Coins
                    }
                },
                _ => Body::Utility,
            },
            ItemKind::Consumable { .. } => Body::Consumable,
            ItemKind::Throwable { .. } => Body::Throwable,
            ItemKind::Ingredient { .. } => Body::Ingredient,
            _ => Body::Empty,
        }
    }
}

impl Body {
    pub fn to_string(self) -> &'static str {
        match self {
            Body::Tool(_) => "tool",
            Body::ModularComponent => "modular_component",
            Body::Lantern => "lantern",
            Body::Glider => "glider",
            Body::Armor(_) => "armor",
            Body::Utility => "utility",
            Body::Consumable => "consumable",
            Body::Throwable => "throwable",
            Body::Ingredient => "ingredient",
            Body::Coins => "coins",
            Body::CoinPouch => "coin_pouch",
            Body::Empty => "empty",
        }
    }

    pub fn density(&self) -> Density { Density(1.1 * WATER_DENSITY) }

    pub fn mass(&self) -> Mass { Mass(2.0) }

    pub fn dimensions(&self) -> Vec3<f32> { Vec3::new(0.0, 0.1, 0.0) }

    pub fn orientation(&self, rng: &mut impl Rng) -> Ori {
        let random = rng.gen_range(-1.0..1.0f32);
        let default = Ori::default();
        match self {
            Body::Tool(_) => default
                .pitched_down(PI / 2.0)
                .yawed_left(PI / 2.0)
                .pitched_towards(
                    Dir::from_unnormalized(Vec3::new(
                        random,
                        rng.gen_range(-1.0..1.0f32),
                        rng.gen_range(-1.0..1.0f32),
                    ))
                    .unwrap_or_default(),
                ),
            Body::Armor(kind) => match kind {
                ItemDropArmorKind::Neck | ItemDropArmorKind::Back | ItemDropArmorKind::Tabard => {
                    default.yawed_left(random).pitched_down(PI / 2.0)
                },
                _ => default.yawed_left(random),
            },
            _ => default.yawed_left(random),
        }
    }
}