screeps/objects/impls/
room_visual.rs

1use js_sys::JsString;
2use serde::Serialize;
3
4use crate::local::RoomName;
5
6#[derive(Debug, Clone, Default, Serialize)]
7#[serde(rename_all = "camelCase")]
8pub struct CircleStyle {
9    #[serde(skip_serializing_if = "Option::is_none")]
10    radius: Option<f32>,
11    #[serde(skip_serializing_if = "Option::is_none")]
12    fill: Option<String>,
13    #[serde(skip_serializing_if = "Option::is_none")]
14    opacity: Option<f32>,
15    #[serde(skip_serializing_if = "Option::is_none")]
16    stroke: Option<String>,
17    #[serde(skip_serializing_if = "Option::is_none")]
18    stroke_width: Option<f32>,
19}
20
21impl CircleStyle {
22    pub fn radius(mut self, val: f32) -> CircleStyle {
23        self.radius = Some(val);
24        self
25    }
26
27    pub fn fill(mut self, val: &str) -> CircleStyle {
28        self.fill = Some(val.to_string());
29        self
30    }
31
32    pub fn opacity(mut self, val: f32) -> CircleStyle {
33        self.opacity = Some(val);
34        self
35    }
36
37    pub fn stroke(mut self, val: &str) -> CircleStyle {
38        self.stroke = Some(val.to_string());
39        self
40    }
41
42    pub fn stroke_width(mut self, val: f32) -> CircleStyle {
43        self.stroke_width = Some(val);
44        self
45    }
46}
47
48#[derive(Debug, Clone, Serialize)]
49pub struct CircleData {
50    x: f32,
51    y: f32,
52    #[serde(rename = "s", skip_serializing_if = "Option::is_none")]
53    style: Option<CircleStyle>,
54}
55
56#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Serialize)]
57#[serde(rename_all = "camelCase")]
58pub enum LineDrawStyle {
59    #[default]
60    Solid,
61    Dashed,
62    Dotted,
63}
64
65impl LineDrawStyle {
66    pub fn is_solid(&self) -> bool {
67        matches!(self, LineDrawStyle::Solid)
68    }
69}
70
71#[derive(Debug, Clone, Default, Serialize)]
72#[serde(rename_all = "camelCase")]
73pub struct LineStyle {
74    #[serde(skip_serializing_if = "Option::is_none")]
75    width: Option<f32>,
76    #[serde(skip_serializing_if = "Option::is_none")]
77    color: Option<String>,
78    #[serde(skip_serializing_if = "Option::is_none")]
79    opacity: Option<f32>,
80    #[serde(skip_serializing_if = "LineDrawStyle::is_solid")]
81    line_style: LineDrawStyle,
82}
83
84impl LineStyle {
85    pub fn width(mut self, val: f32) -> LineStyle {
86        self.width = Some(val);
87        self
88    }
89
90    pub fn color(mut self, val: &str) -> LineStyle {
91        self.color = Some(val.to_string());
92        self
93    }
94
95    pub fn opacity(mut self, val: f32) -> LineStyle {
96        self.opacity = Some(val);
97        self
98    }
99
100    pub fn line_style(mut self, val: LineDrawStyle) -> LineStyle {
101        self.line_style = val;
102        self
103    }
104}
105
106#[derive(Debug, Clone, Serialize)]
107pub struct LineData {
108    x1: f32,
109    y1: f32,
110    x2: f32,
111    y2: f32,
112    #[serde(rename = "s", skip_serializing_if = "Option::is_none")]
113    style: Option<LineStyle>,
114}
115
116#[derive(Debug, Clone, Default, Serialize)]
117#[serde(rename_all = "camelCase")]
118pub struct RectStyle {
119    #[serde(skip_serializing_if = "Option::is_none")]
120    fill: Option<String>,
121    #[serde(skip_serializing_if = "Option::is_none")]
122    opacity: Option<f32>,
123    #[serde(skip_serializing_if = "Option::is_none")]
124    stroke: Option<String>,
125    #[serde(skip_serializing_if = "Option::is_none")]
126    stroke_width: Option<f32>,
127    #[serde(skip_serializing_if = "LineDrawStyle::is_solid")]
128    line_style: LineDrawStyle,
129}
130
131impl RectStyle {
132    pub fn fill(mut self, val: &str) -> RectStyle {
133        self.fill = Some(val.to_string());
134        self
135    }
136
137    pub fn opacity(mut self, val: f32) -> RectStyle {
138        self.opacity = Some(val);
139        self
140    }
141
142    pub fn stroke(mut self, val: &str) -> RectStyle {
143        self.stroke = Some(val.to_string());
144        self
145    }
146
147    pub fn stroke_width(mut self, val: f32) -> RectStyle {
148        self.stroke_width = Some(val);
149        self
150    }
151
152    pub fn line_style(mut self, val: LineDrawStyle) -> RectStyle {
153        self.line_style = val;
154        self
155    }
156}
157
158#[derive(Debug, Clone, Serialize)]
159pub struct RectData {
160    x: f32,
161    y: f32,
162    #[serde(rename = "w")]
163    width: f32,
164    #[serde(rename = "h")]
165    height: f32,
166    #[serde(rename = "s", skip_serializing_if = "Option::is_none")]
167    style: Option<RectStyle>,
168}
169
170#[derive(Debug, Clone, Default, Serialize)]
171#[serde(rename_all = "camelCase")]
172pub struct PolyStyle {
173    #[serde(skip_serializing_if = "Option::is_none")]
174    fill: Option<String>,
175    #[serde(skip_serializing_if = "Option::is_none")]
176    opacity: Option<f32>,
177    #[serde(skip_serializing_if = "Option::is_none")]
178    stroke: Option<String>,
179    #[serde(skip_serializing_if = "Option::is_none")]
180    stroke_width: Option<f32>,
181    #[serde(skip_serializing_if = "LineDrawStyle::is_solid")]
182    line_style: LineDrawStyle,
183}
184
185impl PolyStyle {
186    pub fn fill(mut self, val: &str) -> PolyStyle {
187        self.fill = Some(val.to_string());
188        self
189    }
190
191    pub fn opacity(mut self, val: f32) -> PolyStyle {
192        self.opacity = Some(val);
193        self
194    }
195
196    pub fn stroke(mut self, val: &str) -> PolyStyle {
197        self.stroke = Some(val.to_string());
198        self
199    }
200
201    pub fn stroke_width(mut self, val: f32) -> PolyStyle {
202        self.stroke_width = Some(val);
203        self
204    }
205
206    pub fn line_style(mut self, val: LineDrawStyle) -> PolyStyle {
207        self.line_style = val;
208        self
209    }
210}
211
212#[derive(Debug, Clone, Serialize)]
213pub struct PolyData {
214    points: Vec<(f32, f32)>,
215    #[serde(rename = "s", skip_serializing_if = "Option::is_none")]
216    style: Option<PolyStyle>,
217}
218
219#[derive(Debug, Clone, Serialize)]
220#[serde(untagged)]
221pub enum FontStyle {
222    Size(f32),
223    Custom(String),
224}
225
226#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Serialize)]
227#[serde(rename_all = "camelCase")]
228pub enum TextAlign {
229    #[default]
230    Center,
231    Left,
232    Right,
233}
234
235impl TextAlign {
236    pub fn is_center(&self) -> bool {
237        matches!(self, TextAlign::Center)
238    }
239}
240
241#[derive(Debug, Clone, Default, Serialize)]
242#[serde(rename_all = "camelCase")]
243pub struct TextStyle {
244    #[serde(skip_serializing_if = "Option::is_none")]
245    color: Option<String>,
246    #[serde(skip_serializing_if = "Option::is_none")]
247    font: Option<FontStyle>,
248    #[serde(skip_serializing_if = "Option::is_none")]
249    stroke: Option<String>,
250    #[serde(skip_serializing_if = "Option::is_none")]
251    stroke_width: Option<f32>,
252    #[serde(skip_serializing_if = "Option::is_none")]
253    background_color: Option<String>,
254    #[serde(skip_serializing_if = "Option::is_none")]
255    background_padding: Option<f32>,
256    #[serde(skip_serializing_if = "TextAlign::is_center")]
257    align: TextAlign,
258    #[serde(skip_serializing_if = "Option::is_none")]
259    opacity: Option<f32>,
260}
261
262impl TextStyle {
263    pub fn color(mut self, val: &str) -> TextStyle {
264        self.color = Some(val.to_string());
265        self
266    }
267
268    pub fn font(mut self, val: f32) -> TextStyle {
269        self.font = Some(FontStyle::Size(val));
270        self
271    }
272
273    pub fn custom_font(mut self, val: &str) -> TextStyle {
274        self.font = Some(FontStyle::Custom(val.to_string()));
275        self
276    }
277
278    pub fn stroke(mut self, val: &str) -> TextStyle {
279        self.stroke = Some(val.to_string());
280        self
281    }
282
283    pub fn stroke_width(mut self, val: f32) -> TextStyle {
284        self.stroke_width = Some(val);
285        self
286    }
287
288    pub fn background_color(mut self, val: &str) -> TextStyle {
289        self.background_color = Some(val.to_string());
290        self
291    }
292
293    pub fn background_padding(mut self, val: f32) -> TextStyle {
294        self.background_padding = Some(val);
295        self
296    }
297
298    pub fn align(mut self, val: TextAlign) -> TextStyle {
299        self.align = val;
300        self
301    }
302
303    pub fn opacity(mut self, val: f32) -> TextStyle {
304        self.opacity = Some(val);
305        self
306    }
307}
308
309#[derive(Debug, Clone, Serialize)]
310pub struct TextData {
311    text: String,
312    x: f32,
313    y: f32,
314    #[serde(rename = "s", skip_serializing_if = "Option::is_none")]
315    style: Option<TextStyle>,
316}
317
318#[derive(Debug, Clone, Serialize)]
319#[serde(tag = "t")]
320pub enum Visual {
321    #[serde(rename = "c")]
322    Circle(CircleData),
323    #[serde(rename = "l")]
324    Line(LineData),
325    #[serde(rename = "r")]
326    Rect(RectData),
327    #[serde(rename = "p")]
328    Poly(PolyData),
329    #[serde(rename = "t")]
330    Text(TextData),
331}
332
333impl Visual {
334    pub fn circle(x: f32, y: f32, style: Option<CircleStyle>) -> Visual {
335        Visual::Circle(CircleData { x, y, style })
336    }
337
338    pub fn line(from: (f32, f32), to: (f32, f32), style: Option<LineStyle>) -> Visual {
339        Visual::Line(LineData {
340            x1: from.0,
341            y1: from.1,
342            x2: to.0,
343            y2: to.1,
344            style,
345        })
346    }
347
348    pub fn rect(x: f32, y: f32, width: f32, height: f32, style: Option<RectStyle>) -> Visual {
349        Visual::Rect(RectData {
350            x,
351            y,
352            width,
353            height,
354            style,
355        })
356    }
357
358    pub fn poly(points: Vec<(f32, f32)>, style: Option<PolyStyle>) -> Visual {
359        Visual::Poly(PolyData { points, style })
360    }
361
362    pub fn text(x: f32, y: f32, text: String, style: Option<TextStyle>) -> Visual {
363        Visual::Text(TextData { x, y, text, style })
364    }
365}
366
367#[derive(Debug)]
368pub struct RoomVisual {
369    room_name: Option<RoomName>,
370}
371
372impl RoomVisual {
373    pub fn new(room_name: Option<RoomName>) -> RoomVisual {
374        RoomVisual { room_name }
375    }
376
377    pub fn draw(&self, visual: &Visual) {
378        let name: Option<JsString> = self.room_name.map(|name| name.to_string().into());
379        let val = serde_wasm_bindgen::to_value(visual).expect("expect convert visual to value");
380
381        crate::console::add_visual(name.as_ref(), &val);
382    }
383
384    pub fn draw_multi(&self, visuals: &[Visual]) {
385        let name: Option<JsString> = self.room_name.map(|name| name.to_string().into());
386
387        for visual in visuals {
388            let val = serde_wasm_bindgen::to_value(visual).expect("expect convert visual to value");
389
390            crate::console::add_visual(name.as_ref(), &val);
391        }
392    }
393
394    pub fn circle(&self, x: f32, y: f32, style: Option<CircleStyle>) {
395        self.draw(&Visual::circle(x, y, style));
396    }
397
398    pub fn line(&self, from: (f32, f32), to: (f32, f32), style: Option<LineStyle>) {
399        self.draw(&Visual::line(from, to, style));
400    }
401
402    pub fn rect(&self, x: f32, y: f32, width: f32, height: f32, style: Option<RectStyle>) {
403        self.draw(&Visual::rect(x, y, width, height, style));
404    }
405
406    pub fn poly(&self, points: Vec<(f32, f32)>, style: Option<PolyStyle>) {
407        self.draw(&Visual::poly(points, style));
408    }
409
410    pub fn text(&self, x: f32, y: f32, text: String, style: Option<TextStyle>) {
411        self.draw(&Visual::text(x, y, text, style));
412    }
413}