osrscache/definition/osrs/
npc_def.rs1use std::{collections::HashMap, io, io::BufReader};
2
3#[cfg(feature = "serde")]
4use serde::{Deserialize, Serialize};
5
6use super::Definition;
7use crate::{extension::ReadExt, util};
8
9#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
12#[derive(Clone, Eq, PartialEq, Debug, Default)]
13pub struct NpcDefinition {
14 pub id: u16,
15 pub name: String,
16 pub size: usize,
17 pub actions: [String; 5],
18 pub visible_on_minimap: bool,
19 pub combat_level: Option<u16>,
20 pub configs: Vec<u16>,
21 pub varbit_id: Option<u16>,
22 pub varp_index: Option<u16>,
23 pub interactable: bool,
24 pub pet: bool,
25 pub params: HashMap<u32, String>,
26 pub model_data: NpcModelData,
27 pub animation_data: NpcAnimationData,
28}
29
30#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
31#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug, Default)]
32pub struct NpcModelData {
33 pub models: Vec<u16>,
34 pub chat_head_models: Vec<u16>,
35 pub recolor_find: Vec<u16>,
36 pub recolor_replace: Vec<u16>,
37 pub retexture_find: Vec<u16>,
38 pub retexture_replace: Vec<u16>,
39 pub width_scale: u16,
40 pub height_scale: u16,
41 pub render_priority: bool,
42 pub ambient: u8,
43 pub contrast: u8,
44 pub head_icon: Option<u16>,
45 pub rotate_speed: u16,
46 pub rotate_flag: bool,
47}
48
49#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
50#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug, Default)]
51pub struct NpcAnimationData {
52 pub standing: Option<u16>,
53 pub walking: Option<u16>,
54 pub rotate_left: Option<u16>,
55 pub rotate_right: Option<u16>,
56 pub rotate_180: Option<u16>,
57 pub rotate_90_left: Option<u16>,
58 pub rotate_90_right: Option<u16>,
59}
60
61impl Definition for NpcDefinition {
62 fn new(id: u16, buffer: &[u8]) -> crate::Result<Self> {
63 let mut reader = BufReader::new(buffer);
64 let npc_def = decode_buffer(id, &mut reader)?;
65
66 Ok(npc_def)
67 }
68}
69
70#[allow(clippy::too_many_lines)]
71fn decode_buffer(id: u16, reader: &mut BufReader<&[u8]>) -> io::Result<NpcDefinition> {
72 let mut npc_def = NpcDefinition {
73 id,
74 interactable: true,
75 visible_on_minimap: true,
76 model_data: NpcModelData {
77 rotate_flag: true,
78 width_scale: 128,
79 height_scale: 128,
80 rotate_speed: 32,
81 ..NpcModelData::default()
82 },
83 ..NpcDefinition::default()
84 };
85
86 loop {
87 let opcode = reader.read_u8()?;
88
89 match opcode {
90 0 => break,
91 1 => {
92 let len = reader.read_u8()?;
93 for _ in 0..len {
94 npc_def.model_data.models.push(reader.read_u16()?);
95 }
96 }
97 2 => {
98 npc_def.name = reader.read_string()?;
99 }
100 12 => {
101 npc_def.size = reader.read_u8()? as usize;
102 }
103 13 => {
104 npc_def.animation_data.standing = Some(reader.read_u16()?);
105 }
106 14 => {
107 npc_def.animation_data.walking = Some(reader.read_u16()?);
108 }
109 15 => {
110 npc_def.animation_data.rotate_left = Some(reader.read_u16()?);
111 }
112 16 => {
113 npc_def.animation_data.rotate_right = Some(reader.read_u16()?);
114 }
115 17 => {
116 npc_def.animation_data.walking = Some(reader.read_u16()?);
117 npc_def.animation_data.rotate_180 = Some(reader.read_u16()?);
118 npc_def.animation_data.rotate_90_right = Some(reader.read_u16()?);
119 npc_def.animation_data.rotate_90_left = Some(reader.read_u16()?);
120 }
121 30..=34 => {
122 npc_def.actions[opcode as usize - 30] = reader.read_string()?;
123 }
124 40 => {
125 let len = reader.read_u8()?;
126 for _ in 0..len {
127 npc_def.model_data.recolor_find.push(reader.read_u16()?);
128 npc_def.model_data.recolor_replace.push(reader.read_u16()?);
129 }
130 }
131 41 => {
132 let len = reader.read_u8()?;
133 for _ in 0..len {
134 npc_def.model_data.retexture_find.push(reader.read_u16()?);
135 npc_def
136 .model_data
137 .retexture_replace
138 .push(reader.read_u16()?);
139 }
140 }
141 60 => {
142 let len = reader.read_u8()?;
143 for _ in 0..len {
144 npc_def.model_data.chat_head_models.push(reader.read_u16()?);
145 }
146 }
147 93 => npc_def.visible_on_minimap = true,
148 95 => {
149 npc_def.combat_level = Some(reader.read_u16()?);
150 }
151 97 => {
152 npc_def.model_data.width_scale = reader.read_u16()?;
153 }
154 98 => {
155 npc_def.model_data.height_scale = reader.read_u16()?;
156 }
157 99 => npc_def.model_data.render_priority = true,
158 100 => {
159 npc_def.model_data.ambient = reader.read_u8()?;
160 }
161 101 => {
162 npc_def.model_data.contrast = reader.read_u8()?;
163 }
164 102 => {
165 npc_def.model_data.head_icon = Some(reader.read_u16()?);
166 }
167 103 => {
168 npc_def.model_data.rotate_speed = reader.read_u16()?;
169 }
170 106 => {
171 let varbit_id = reader.read_u16()?;
172 npc_def.varbit_id = if varbit_id == std::u16::MAX {
173 None
174 } else {
175 Some(varbit_id)
176 };
177
178 let varp_index = reader.read_u16()?;
179 npc_def.varp_index = if varp_index == std::u16::MAX {
180 None
181 } else {
182 Some(varp_index)
183 };
184
185 npc_def.configs = Vec::new();
186 let len = reader.read_u8()?;
187 for _ in 0..=len {
188 npc_def.configs.push(reader.read_u16()?);
189 }
190 }
191 107 => npc_def.interactable = false,
192 109 => npc_def.model_data.rotate_flag = false,
193 111 => npc_def.pet = true,
194 118 => {
195 let varbit_id = reader.read_u16()?;
196 npc_def.varbit_id = if varbit_id == std::u16::MAX {
197 None
198 } else {
199 Some(varbit_id)
200 };
201
202 let varp_index = reader.read_u16()?;
203 npc_def.varp_index = if varp_index == std::u16::MAX {
204 None
205 } else {
206 Some(varp_index)
207 };
208
209 let _var = reader.read_u16()?;
211
212 npc_def.configs = Vec::new();
213 let len = reader.read_u8()?;
214 for _ in 0..=len {
215 npc_def.configs.push(reader.read_u16()?);
216 }
217 }
218 249 => {
219 npc_def.params = util::read_parameters(reader)?;
220 }
221 _ => unreachable!(),
222 }
223 }
224
225 Ok(npc_def)
226}