1use rlua::prelude::*;
2use serde::{Deserialize, Serialize};
3
4use anyhow::anyhow;
5use bitflags::bitflags;
6
7pub mod parser {
8 pub mod slk;
9 pub mod crlf;
10 pub mod profile;
11 pub mod w3obj;
12}
13
14pub mod error;
15pub mod metadata;
16pub mod object;
17pub mod objectstore;
18
19#[derive(Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord, Serialize, Deserialize)]
20pub struct ObjectId {
25 id: u32,
26}
27
28impl ObjectId {
29 pub fn new(id: u32) -> ObjectId {
30 ObjectId { id }
31 }
32
33 pub fn from_bytes(bytes: &[u8]) -> Option<Self> {
34 if bytes.len() > 4 {
35 None
36 } else {
37 let mut value = 0;
38 for i in bytes {
39 value <<= 8;
40 value += u32::from(*i);
41 }
42
43 Some(ObjectId { id: value })
44 }
45 }
46
47 pub fn to_u32(self) -> u32 {
48 self.id
49 }
50
51 pub fn to_string(self) -> Option<String> {
52 let bytes: Vec<u8> = (&self.id.to_be_bytes()).iter().copied().collect();
53 String::from_utf8(bytes).ok()
54 }
55}
56
57impl std::fmt::Debug for ObjectId {
58 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
59 if self.id == 0 {
60 write!(f, "ObjectID(NULL)")
61 } else {
62 let bytes = self.id.to_be_bytes();
63 let pretty = std::str::from_utf8(&bytes).ok();
64
65 if let Some(pretty) = pretty {
66 write!(f, "ObjectID({})", pretty)
67 } else {
68 write!(f, "ObjectID({})", self.id)
69 }
70 }
71 }
72}
73
74impl std::fmt::Display for ObjectId {
75 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
76 if self.id == 0 {
77 write!(f, "NULL")
78 } else {
79 let bytes = self.id.to_be_bytes();
80 let pretty = String::from_utf8_lossy(&bytes);
81
82 write!(f, "{}", pretty)
83 }
84 }
85}
86
87impl From<u32> for ObjectId {
88 fn from(other: u32) -> Self {
89 Self { id: other }
90 }
91}
92
93impl<'lua> FromLua<'lua> for ObjectId {
94 fn from_lua(value: LuaValue<'lua>, _ctx: LuaContext<'lua>) -> Result<Self, LuaError> {
95 match value {
96 LuaValue::String(value) => ObjectId::from_bytes(value.as_bytes()).ok_or_else(|| {
97 LuaError::FromLuaConversionError {
98 from: "string",
99 to: "objectid",
100 message: Some("invalid byte sequence for object id".into()),
101 }
102 }),
103 LuaValue::Integer(value) => Ok(ObjectId::new(value as u32)),
104 _ => Err(LuaError::external(anyhow!(
105 "only strings and integers can be converted to object ids"
106 ))),
107 }
108 }
109}
110
111impl<'lua> ToLua<'lua> for ObjectId {
112 fn to_lua(self, ctx: LuaContext<'lua>) -> Result<LuaValue<'lua>, LuaError> {
113 if let Some(value) = self.to_string() {
114 Ok(LuaValue::String(ctx.create_string(&value)?))
115 } else {
116 Ok(LuaValue::Integer(self.id as i64))
117 }
118 }
119}
120
121#[derive(Debug, Clone, Copy, Deserialize, Serialize, PartialEq, Eq, Hash)]
122pub enum ValueType {
127 Int,
128 Real,
129 Unreal,
130 String,
131}
132
133impl ValueType {
134 pub fn new(input: &str) -> ValueType {
138 match input {
139 "real" => ValueType::Real,
140 "unreal" => ValueType::Unreal,
141 "int" | "bool" | "attackBits" | "deathType" | "defenseTypeInt" | "detectionType"
142 | "teamColor" | "morphFlags" | "silenceFlags" | "stackFlags" | "interactionFlags"
143 | "pickFlags" | "versionFlags" | "fullFlags" | "channelType" | "channelFlags"
144 | "spellDetail" | "techAvail" => ValueType::Int,
145 _ => ValueType::String,
146 }
147 }
148}
149
150bitflags! {
151 #[derive(Serialize, Deserialize)]
152 pub struct ObjectKind: u32 {
154 const ABILITY = 0b1;
155 const BUFF = 0b10;
156 const DESTRUCTABLE = 0b100;
157 const MISC = 0b1000;
158 const UNIT = 0b10000;
159 const UPGRADE = 0b100000;
160 const ITEM = 0b1000000;
161 const DOODAD = 0b10000000;
162 const LIGHTNING = 0b100000000; }
164}
165
166impl ObjectKind {
167 pub fn from_ext(ext: &str) -> ObjectKind {
170 match ext {
171 "w3u" => ObjectKind::UNIT,
172 "w3a" => ObjectKind::ABILITY,
173 "w3t" => ObjectKind::ITEM,
174 "w3b" => ObjectKind::DESTRUCTABLE,
175 "w3d" => ObjectKind::DOODAD,
176 "w3h" => ObjectKind::BUFF,
177 "w3q" => ObjectKind::UPGRADE,
178 "lightning" => ObjectKind::LIGHTNING,
179 _ => ObjectKind::empty(),
180 }
181 }
182
183 pub fn to_ext(self) -> &'static str {
184 match self {
185 ObjectKind::UNIT => "w3u",
186 ObjectKind::ABILITY => "w3a",
187 ObjectKind::ITEM => "w3t",
188 ObjectKind::DESTRUCTABLE => "w3b",
189 ObjectKind::DOODAD => "w3d",
190 ObjectKind::BUFF => "w3h",
191 ObjectKind::UPGRADE => "w3q",
192 ObjectKind::LIGHTNING => "lightning",
193 _ => "none",
194 }
195 }
196
197 pub fn is_data_type(self) -> bool {
202 match self {
203 ObjectKind::DOODAD | ObjectKind::ABILITY | ObjectKind::UPGRADE => true,
204 _ => false,
205 }
206 }
207
208 pub fn to_typestr(self) -> &'static str {
209 match self {
210 ObjectKind::UNIT => "unit",
211 ObjectKind::ABILITY => "ability",
212 ObjectKind::ITEM => "item",
213 ObjectKind::DESTRUCTABLE => "destructable",
214 ObjectKind::DOODAD => "doodad",
215 ObjectKind::BUFF => "buff",
216 ObjectKind::UPGRADE => "upgrade",
217 ObjectKind::LIGHTNING => "lightning",
218 _ => "none",
219 }
220 }
221}