rustapi/actions/
recess.rs1use crate::prelude::*;
2use rusterix::{PixelSource, Value};
3
4pub struct Recess {
5 id: TheId,
6 nodeui: TheNodeUI,
7}
8
9impl Action for Recess {
10 fn new() -> Self
11 where
12 Self: Sized,
13 {
14 let mut nodeui: TheNodeUI = TheNodeUI::default();
15
16 let item = TheNodeUIItem::FloatEditSlider(
17 "actionRecessDepth".into(),
18 "".into(),
19 "".into(),
20 0.1,
21 0.0..=1.0,
22 false,
23 );
24 nodeui.add_item(item);
25
26 let item = TheNodeUIItem::Selector(
27 "actionRecessTarget".into(),
28 "".into(),
29 "".into(),
30 vec!["Front".to_string(), "Back".to_string()],
31 1,
32 );
33 nodeui.add_item(item);
34
35 let item = TheNodeUIItem::Icons(
36 "actionRecessTiles".into(),
37 "".into(),
38 "".into(),
39 vec![
40 (
41 TheRGBABuffer::new(TheDim::sized(36, 36)),
42 "CAP".to_string(),
43 Uuid::nil(),
44 ),
45 (
46 TheRGBABuffer::new(TheDim::sized(36, 36)),
47 "SIDE".to_string(),
48 Uuid::nil(),
49 ),
50 ],
51 );
52 nodeui.add_item(item);
53
54 let item = TheNodeUIItem::Markdown("desc".into(), "".into());
55 nodeui.add_item(item);
56
57 Self {
58 id: TheId::named(&fl!("action_recess")),
59 nodeui,
60 }
61 }
62
63 fn id(&self) -> TheId {
64 self.id.clone()
65 }
66
67 fn info(&self) -> String {
68 fl!("action_recess_desc")
69 }
70
71 fn role(&self) -> ActionRole {
72 ActionRole::Editor
73 }
74
75 fn accel(&self) -> Option<TheAccelerator> {
76 Some(TheAccelerator::new(TheAcceleratorKey::ALT, 'r'))
77 }
78
79 fn is_applicable(&self, map: &Map, _ctx: &mut TheContext, server_ctx: &ServerContext) -> bool {
80 !map.selected_sectors.is_empty()
82 && server_ctx.editor_view_mode == EditorViewMode::D2
83 && server_ctx.editing_surface.is_some()
84 }
85
86 fn load_params(&mut self, map: &Map) {
87 if let Some(sector_id) = map.selected_sectors.first() {
88 if let Some(sector) = map.find_sector(*sector_id) {
89 self.nodeui.set_f32_value(
90 "actionRecessDepth",
91 sector.properties.get_float_default("profile_amount", 0.1),
92 );
93 self.nodeui.set_i32_value(
94 "actionRecessTarget",
95 sector.properties.get_int_default("profile_target", 1),
96 );
97 }
98 }
99 }
100
101 fn load_params_project(&mut self, project: &Project, server_ctx: &mut ServerContext) {
102 let mut cap_icon = TheRGBABuffer::new(TheDim::sized(36, 36));
103 let mut jamb_icon = TheRGBABuffer::new(TheDim::sized(36, 36));
104 let mut cap_id = Uuid::nil();
105 let mut jamb_id = Uuid::nil();
106
107 if let Some(map) = project.get_map(server_ctx) {
108 if let Some(sector_id) = map.selected_sectors.first() {
109 if let Some(sector) = map.find_sector(*sector_id) {
110 if let Some(Value::Source(PixelSource::TileId(id))) =
111 sector.properties.get("cap_source")
112 {
113 if let Some(tile) = project.tiles.get(id)
114 && !tile.is_empty()
115 {
116 cap_icon = tile.textures[0].to_rgba();
117 cap_id = *id;
118 }
119 }
120 if let Some(Value::Source(PixelSource::TileId(id))) =
121 sector.properties.get("jamb_source")
122 {
123 if let Some(tile) = project.tiles.get(id)
124 && !tile.is_empty()
125 {
126 jamb_icon = tile.textures[0].to_rgba();
127 jamb_id = *id;
128 }
129 }
130 }
131 }
132 }
133
134 if let Some(item) = self.nodeui.get_item_mut("actionRecessTiles") {
135 match item {
136 TheNodeUIItem::Icons(_, _, _, items) => {
137 if items.len() == 2 {
138 items[0].0 = cap_icon;
139 items[0].2 = cap_id;
140 items[1].0 = jamb_icon;
141 items[1].2 = jamb_id;
142 }
143 }
144 _ => {}
145 }
146 }
147 }
148
149 fn apply(
150 &self,
151 map: &mut Map,
152 _ui: &mut TheUI,
153 _ctx: &mut TheContext,
154 server_ctx: &mut ServerContext,
155 ) -> Option<ProjectUndoAtom> {
156 let mut changed = false;
157 let prev = map.clone();
158
159 let depth = self
160 .nodeui
161 .get_f32_value("actionRecessDepth")
162 .unwrap_or(0.0);
163
164 let target = self.nodeui.get_i32_value("actionRecessTarget").unwrap_or(1);
165
166 let cap = self.nodeui.get_tile_id("actionRecessTiles", 0);
167 let jamb = self.nodeui.get_tile_id("actionRecessTiles", 1);
168
169 for sector_id in &map.selected_sectors.clone() {
170 if let Some(sector) = map.find_sector_mut(*sector_id) {
171 sector.properties.set("profile_op", Value::Int(2));
172 sector.properties.set("profile_amount", Value::Float(depth));
173 sector.properties.set("profile_target", Value::Int(target));
174
175 if let Some(cap) = cap
176 && cap != Uuid::nil()
177 {
178 sector.properties.set(
179 "cap_source",
180 Value::Source(rusterix::PixelSource::TileId(cap)),
181 );
182 }
183 if let Some(jamb) = jamb
184 && jamb != Uuid::nil()
185 {
186 sector.properties.set(
187 "jamb_source",
188 Value::Source(rusterix::PixelSource::TileId(jamb)),
189 );
190 }
191 changed = true;
192 }
193 }
194
195 if changed {
196 Some(ProjectUndoAtom::MapEdit(
197 server_ctx.pc,
198 Box::new(prev),
199 Box::new(map.clone()),
200 ))
201 } else {
202 None
203 }
204 }
205
206 fn params(&self) -> TheNodeUI {
207 self.nodeui.clone()
208 }
209
210 fn handle_event(
211 &mut self,
212 event: &TheEvent,
213 project: &mut Project,
214 _ui: &mut TheUI,
215 ctx: &mut TheContext,
216 _server_ctx: &mut ServerContext,
217 ) -> bool {
218 if let TheEvent::TileDropped(id, tile_id, index) = event {
219 if let Some(item) = self.nodeui.get_item_mut(&id.name) {
220 match item {
221 TheNodeUIItem::Icons(_, _, _, items) => {
222 if *index < items.len() {
223 if let Some(tile) = project.tiles.get(tile_id)
224 && !tile.is_empty()
225 {
226 items[*index].0 = tile.textures[0].to_rgba();
227 items[*index].2 = *tile_id;
228 ctx.ui.send(TheEvent::Custom(
229 TheId::named("Update Action List"),
230 TheValue::Empty,
231 ));
232 return true;
233 }
234 }
235 }
236 _ => {}
237 }
238 }
239 }
240 self.nodeui.handle_event(event)
241 }
242}