1use crate::components::Transform;
25use crate::hierarchy;
26use crate::script::Ctx;
27use glam::Vec3;
28use kvas_ecs::Entity;
29use std::sync::atomic::{AtomicU64, Ordering};
30
31static STREAM: AtomicU64 = AtomicU64::new(0x1234_5678_9ABC_DEF0);
32
33impl Ctx<'_> {
34 pub fn position(&self) -> Vec3 {
36 self.world_position()
37 }
38
39 pub fn position_of(&self, entity: Entity) -> Option<Vec3> {
41 if self.scene.world.is_alive(entity) && self.scene.world.has::<Transform>(entity) {
42 Some(hierarchy::global_matrix(&self.scene.world, entity).w_axis.truncate())
43 } else {
44 None
45 }
46 }
47
48 pub fn set_position(&mut self, position: Vec3) -> bool {
50 hierarchy::set_world_position(&mut self.scene.world, self.entity, position)
51 }
52
53 pub fn translate(&mut self, delta: Vec3) {
55 self.transform_mut().position += delta;
56 }
57
58 pub fn translate_local(&mut self, delta: Vec3) {
60 let t = self.transform_mut();
61 let q = t.quat();
62 t.position += q * delta;
63 }
64
65 pub fn rotate(&mut self, degrees: Vec3) {
67 self.transform_mut().rotation += degrees;
68 }
69
70 pub fn forward(&self) -> Vec3 {
72 self.transform().forward()
73 }
74
75 pub fn right(&self) -> Vec3 {
77 self.transform().right()
78 }
79
80 pub fn up(&self) -> Vec3 {
82 self.transform().up()
83 }
84
85 pub fn distance_to(&self, entity: Entity) -> Option<f32> {
87 self.position_of(entity).map(|p| p.distance(self.world_position()))
88 }
89
90 pub fn direction_to(&self, target: Vec3) -> Vec3 {
92 (target - self.world_position()).normalize_or_zero()
93 }
94
95 pub fn look_at(&mut self, target: Vec3) {
98 let d = self.direction_to(target);
99 if d == Vec3::ZERO {
100 return;
101 }
102 let yaw = (-d.x).atan2(-d.z).to_degrees();
103 let pitch = d.y.clamp(-1.0, 1.0).asin().to_degrees();
104 self.transform_mut().rotation = Vec3::new(pitch, yaw, 0.0);
105 }
106
107 pub fn look_at_flat(&mut self, target: Vec3) {
109 let mut d = target - self.world_position();
110 d.y = 0.0;
111 if d.length_squared() < 1e-8 {
112 return;
113 }
114 let yaw = (-d.x).atan2(-d.z).to_degrees();
115 self.transform_mut().rotation.y = yaw;
116 }
117
118 pub fn move_towards(&mut self, target: Vec3, speed: f32) -> bool {
122 let here = self.world_position();
123 let delta = target - here;
124 let step = speed.max(0.0) * self.dt();
125 if delta.length() <= step.max(1e-5) {
126 self.set_position(target);
127 true
128 } else {
129 self.set_position(here + delta.normalize() * step);
130 false
131 }
132 }
133
134 pub fn nearest_tagged(&self, tag: &str) -> Option<(Entity, f32)> {
136 let here = self.world_position();
137 self.tagged(tag)
138 .into_iter()
139 .filter(|e| *e != self.entity)
140 .filter_map(|e| self.position_of(e).map(|p| (e, p.distance(here))))
141 .min_by(|a, b| a.1.total_cmp(&b.1))
142 }
143
144 pub fn despawn_self(&mut self) {
146 let me = self.entity;
147 self.despawn(me);
148 }
149
150 pub fn random(&self) -> f32 {
153 let seed = STREAM.fetch_add(0x9E37_79B9_7F4A_7C15, Ordering::Relaxed) ^ self.time.frame ^ self.entity.to_bits();
154 crate::random::Rng::new(seed).f32()
155 }
156
157 pub fn random_range(&self, min: f32, max: f32) -> f32 {
159 min + (max - min) * self.random()
160 }
161
162 pub fn chance(&self, p: f32) -> bool {
164 self.random() < p
165 }
166}
167
168impl Ctx<'_> {
170 pub fn parent(&self) -> Option<Entity> {
172 hierarchy::parent_of(&self.scene.world, self.entity)
173 }
174
175 pub fn parent_of(&self, entity: Entity) -> Option<Entity> {
177 hierarchy::parent_of(&self.scene.world, entity)
178 }
179
180 pub fn root(&self) -> Entity {
182 let mut e = self.entity;
183 while let Some(p) = hierarchy::parent_of(&self.scene.world, e) {
184 e = p;
185 }
186 e
187 }
188
189 pub fn descendants(&self, entity: Entity) -> Vec<Entity> {
191 hierarchy::descendants(&self.scene.world, entity)
192 }
193
194 pub fn find_child(&self, name: &str) -> Option<Entity> {
196 hierarchy::find_descendant(&self.scene.world, self.entity, name, false)
197 }
198
199 pub fn tagged_within(&self, tag: &str, center: Vec3, radius: f32) -> Vec<Entity> {
201 let mut found: Vec<(Entity, f32)> = self.tagged(tag).into_iter().filter_map(|e| self.position_of(e).map(|p| (e, p.distance(center)))).filter(|(_, d)| *d <= radius).collect();
202 found.sort_by(|a, b| a.1.total_cmp(&b.1));
203 found.into_iter().map(|(e, _)| e).collect()
204 }
205
206 pub fn tags_of(&self, entity: Entity) -> Vec<String> {
208 self.scene.world.get::<crate::gameplay::Tags>(entity).map(|t| t.value.split_whitespace().map(str::to_string).collect()).unwrap_or_default()
209 }
210
211 pub fn play_animation(&mut self, entity: Entity, clip: &str) -> bool {
216 if !self.is_alive(entity) {
217 return false;
218 }
219 match self.scene.world.get_mut::<crate::skeletal::Animator>(entity) {
220 Some(a) => {
221 if a.clip != clip {
222 a.clip = clip.to_string();
223 }
224 a.playing = true;
225 }
226 None => self.scene.world.insert(entity, crate::skeletal::Animator { clip: clip.to_string(), ..Default::default() }),
227 }
228 true
229 }
230
231 pub fn set_animation_speed(&mut self, entity: Entity, speed: f32) {
234 if let Some(a) = self.scene.world.get_mut::<crate::skeletal::Animator>(entity) {
235 a.speed = speed.max(0.0);
236 }
237 }
238
239 pub fn ragdoll(&mut self, entity: Entity, impulse: Vec3) -> bool {
243 if !self.is_alive(entity) {
244 return false;
245 }
246 match self.scene.world.get_mut::<crate::ragdoll::Ragdoll>(entity) {
247 Some(r) => {
248 r.active = true;
249 r.impulse = impulse;
250 }
251 None => self.scene.world.insert(entity, crate::ragdoll::Ragdoll { active: true, impulse, ..Default::default() }),
252 }
253 true
254 }
255
256 pub fn stop_ragdoll(&mut self, entity: Entity) {
258 if let Some(r) = self.scene.world.get_mut::<crate::ragdoll::Ragdoll>(entity) {
259 r.active = false;
260 }
261 }
262
263 pub fn look_at_point(&mut self, entity: Entity, point: Option<Vec3>) {
266 match self.scene.world.get_mut::<crate::ik::CharacterIk>(entity) {
267 Some(ik) => ik.look_point = point,
268 None if point.is_some() => self.scene.world.insert(entity, crate::ik::CharacterIk { look_point: point, ..Default::default() }),
269 None => {}
270 }
271 }
272
273 pub fn animation_clips(&self, entity: Entity) -> Vec<String> {
275 self.scene.world.get::<crate::skeletal::Animator>(entity).map(|a| a.clips.clone()).unwrap_or_default()
276 }
277
278 pub fn set_global(&mut self, key: &str, value: serde_json::Value) {
280 self.scene.globals.insert(key.to_string(), value);
281 }
282
283 pub fn global(&self, key: &str) -> Option<&serde_json::Value> {
285 self.scene.globals.get(key)
286 }
287
288 pub fn global_f32(&self, key: &str, default: f32) -> f32 {
290 self.global(key).and_then(|v| v.as_f64()).map_or(default, |v| v as f32)
291 }
292
293 pub fn global_bool(&self, key: &str, default: bool) -> bool {
295 self.global(key).and_then(|v| v.as_bool()).unwrap_or(default)
296 }
297
298 pub fn add_global(&mut self, key: &str, delta: f32) -> f32 {
300 let value = self.global_f32(key, 0.0) + delta;
301 self.set_global(key, serde_json::json!(value));
302 value
303 }
304}
305
306impl Ctx<'_> {
308 pub fn make_editable(&mut self, entity: Entity) -> bool {
311 use crate::components::MeshRenderer;
312 use crate::mesh::MeshData;
313 use crate::mesh_edit::EditableMesh;
314 if self.scene.world.has::<EditableMesh>(entity) {
315 return true;
316 }
317 let Some(name) = self.scene.world.get::<MeshRenderer>(entity).and_then(|m| m.mesh.strip_prefix("primitive:").map(str::to_string)) else { return false };
318 let Some(editable) = MeshData::primitive(&name).and_then(|m| EditableMesh::from_mesh(&m).ok()) else { return false };
319 self.scene.world.insert(entity, editable);
320 true
321 }
322
323 pub fn editable(&mut self, entity: Entity) -> Option<&mut crate::mesh_edit::EditableMesh> {
325 self.scene.world.get_mut::<crate::mesh_edit::EditableMesh>(entity)
326 }
327}
328
329impl Ctx<'_> {
331 pub fn warn(&mut self, message: impl Into<String>) {
333 self.log(format!("warning: {}", message.into()));
334 }
335
336 pub fn error(&mut self, message: impl Into<String>) {
338 self.log(format!("error: {}", message.into()));
339 }
340
341 pub fn watch(&mut self, name: &str, value: impl std::fmt::Debug) {
343 self.log(format!("{name} = {value:?}"));
344 }
345
346 pub fn watch_every(&mut self, seconds: f32, name: &str, value: impl std::fmt::Debug) {
348 if self.every(seconds) {
349 self.watch(name, value);
350 }
351 }
352}