mirror of
https://github.com/vladtrc/iw4L
synced 2026-10-26 02:11:39 -04:00
Bind the script glass functions so Strike loads
mp_strike's map script breaks its "brokenglass01" panes in main() with
GetGlassArray and DestroyGlass. Neither native was bound, and an unbound
native refuses the whole program at install ("2 unbound natives:
destroyglass getglassarray"), so the map never loaded.
The glass reset now also reads the map's named glass table (a targetname
and its piece indices), and the session installs it next to the glass
panes as a GlassNames resource. getglassarray, getglass, destroyglass
(the existing script shatter), isglassdestroyed and getglassorigin are
bound on top of it. The map script only runs at the next scheduler pass,
after the glass is installed.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -152,6 +152,8 @@ pub struct FxGlassReset {
|
||||
pub piece_limit: usize,
|
||||
|
||||
pub geo_data_limit: usize,
|
||||
|
||||
pub names: Vec<(String, Vec<u16>)>,
|
||||
}
|
||||
|
||||
impl FxGlassReset {
|
||||
@@ -263,9 +265,36 @@ pub fn build_fx_glass_reset(stream: &ZoneStream<'_>) -> Option<FxGlassReset> {
|
||||
geo_cursor,
|
||||
piece_limit: g.piece_limit,
|
||||
geo_data_limit: g.geo_data_limit,
|
||||
names: glass_names(stream),
|
||||
})
|
||||
}
|
||||
|
||||
fn glass_names(s: &ZoneStream<'_>) -> Vec<(String, Vec<u16>)> {
|
||||
let Some(g) = s.glass_data() else {
|
||||
return Vec::new();
|
||||
};
|
||||
let Some(rows) = g.names else {
|
||||
return Vec::new();
|
||||
};
|
||||
let mut out = Vec::with_capacity(g.name_count);
|
||||
for i in 0..g.name_count {
|
||||
let row = rows.at(i * s.layout(sz::G_GLASS_NAME, 24));
|
||||
let Some(name) = cstr_field(s, row, 0).filter(|name| !name.is_empty()) else {
|
||||
continue;
|
||||
};
|
||||
let count = s.u16_at(row, s.layout(6, 10)).unwrap_or(0) as usize;
|
||||
let pieces = match s.ptr_at(row, s.layout(8, 16)) {
|
||||
Ok(ZonePtr::Offset(q)) => {
|
||||
let q = s.resolve_alias(q);
|
||||
(0..count).map_while(|n| s.u16_at(q, n * 2).ok()).collect()
|
||||
}
|
||||
_ => Vec::new(),
|
||||
};
|
||||
out.push((name, pieces));
|
||||
}
|
||||
out
|
||||
}
|
||||
|
||||
fn glass_material_edge(
|
||||
namespace: asset_material::AssetNamespace,
|
||||
hint: &str,
|
||||
|
||||
@@ -520,6 +520,18 @@ pub fn apply_prepared_match(
|
||||
})
|
||||
.collect();
|
||||
sim.world_objects_mut().install_glass_panes(panes);
|
||||
sim.set_glass_names(
|
||||
glass
|
||||
.names
|
||||
.iter()
|
||||
.map(|(name, pieces)| {
|
||||
(
|
||||
name.clone(),
|
||||
pieces.iter().copied().map(u32::from).collect(),
|
||||
)
|
||||
})
|
||||
.collect(),
|
||||
);
|
||||
}
|
||||
sim.world_objects_mut()
|
||||
.set_map_round_epoch(load_key.match_key.match_epoch);
|
||||
|
||||
@@ -176,6 +176,15 @@ impl SimWorld {
|
||||
crate::script::set_dvar(&mut self.ecs, name, value);
|
||||
}
|
||||
|
||||
pub fn set_glass_names(&mut self, names: Vec<(String, Vec<u32>)>) {
|
||||
if !names.is_empty() {
|
||||
let pieces: usize = names.iter().map(|(_, pieces)| pieces.len()).sum();
|
||||
diag::info!(Sim, "glass: {} named sets, {pieces} pieces", names.len());
|
||||
}
|
||||
self.ecs
|
||||
.insert_resource(crate::script::GlassNames(names.into_iter().collect()));
|
||||
}
|
||||
|
||||
pub fn gsc_realm(&self) -> Option<crate::script::Realm> {
|
||||
self.ecs
|
||||
.resource::<crate::script::Runtime>()
|
||||
|
||||
@@ -7,7 +7,7 @@ use super::math::distance_sq;
|
||||
use crate::bullet_collision::{
|
||||
MASK_PLAYER_SOLID, MASK_SHOT, PLAYER_MAXS, PLAYER_MINS, TraceOutcome,
|
||||
};
|
||||
use crate::script::{Arc, ArrayKey, Namespace, NativeRegistry, Runtime, Value};
|
||||
use crate::script::{Arc, ArrayKey, BTreeMap, Namespace, NativeRegistry, Resource, Runtime, Value};
|
||||
use bevy_ecs::prelude::World;
|
||||
|
||||
const ZERO: [f32; 3] = [0.0; 3];
|
||||
@@ -757,6 +757,7 @@ pub(crate) fn register(registry: &mut NativeRegistry) {
|
||||
register_motion(registry);
|
||||
register_attachments(registry);
|
||||
register_sound_and_fx(registry);
|
||||
register_glass(registry);
|
||||
register_entity_state(registry);
|
||||
super::super::hud::register(registry);
|
||||
register_traces(registry);
|
||||
@@ -2017,6 +2018,64 @@ fn register_level(registry: &mut NativeRegistry) {
|
||||
unavailable!("IW4 party termination is not bound to the IW4L party lifecycle": "endparty");
|
||||
}
|
||||
|
||||
#[derive(Resource, Default)]
|
||||
pub struct GlassNames(pub BTreeMap<String, Vec<u32>>);
|
||||
|
||||
fn glass_named(world: &World, name: &str) -> Vec<u32> {
|
||||
let Some(pieces) = world
|
||||
.get_resource::<GlassNames>()
|
||||
.and_then(|names| names.0.get(name))
|
||||
else {
|
||||
return Vec::new();
|
||||
};
|
||||
pieces.clone()
|
||||
}
|
||||
|
||||
fn glass_id(args: &[Value], index: usize) -> Result<u32, String> {
|
||||
u32::try_from(int(args, index)?).map_err(|_| "glass id must not be negative".into())
|
||||
}
|
||||
|
||||
fn register_glass(registry: &mut NativeRegistry) {
|
||||
use Namespace::Function;
|
||||
|
||||
registry.register(Function, "getglassarray", |world, _, args| {
|
||||
let name = string(args, 0)?;
|
||||
let ids = glass_named(world, &name)
|
||||
.into_iter()
|
||||
.map(|id| Value::Int(id as i32))
|
||||
.collect();
|
||||
new_array(world, ids)
|
||||
});
|
||||
registry.register(Function, "getglass", |world, _, args| {
|
||||
let name = string(args, 0)?;
|
||||
Ok(glass_named(world, &name)
|
||||
.first()
|
||||
.map_or(Value::Undefined, |&id| Value::Int(id as i32)))
|
||||
});
|
||||
registry.register(Function, "destroyglass", |world, _, args| {
|
||||
let id = glass_id(args, 0)?;
|
||||
let now = i32::try_from(super::super::players::now_ms(world)).unwrap_or(i32::MAX);
|
||||
crate::frame::FrameWorld::from_world(world)
|
||||
.world_objects_mut()
|
||||
.script_destroy_glass(id, now);
|
||||
Ok(Value::Undefined)
|
||||
});
|
||||
registry.register(Function, "isglassdestroyed", |world, _, args| {
|
||||
let id = glass_id(args, 0)?;
|
||||
let solid = crate::frame::FrameWorld::from_world(world)
|
||||
.world_objects()
|
||||
.glass_is_solid(id);
|
||||
Ok(Value::Int(i32::from(!solid)))
|
||||
});
|
||||
registry.register(Function, "getglassorigin", |world, _, args| {
|
||||
let id = glass_id(args, 0)?;
|
||||
Ok(crate::frame::FrameWorld::from_world(world)
|
||||
.world_objects()
|
||||
.glass_pane(id)
|
||||
.map_or(Value::Undefined, |pane| Value::Vector(pane.origin)))
|
||||
});
|
||||
}
|
||||
|
||||
fn register_weapon_facts(registry: &mut NativeRegistry) {
|
||||
use Namespace::Function;
|
||||
|
||||
|
||||
@@ -27,7 +27,7 @@ pub(crate) use host::entity_damage::{
|
||||
};
|
||||
pub(crate) use host::mechanics::Mechanics;
|
||||
pub(crate) use host::mechanics::advance_mechanics;
|
||||
pub use host::natives::engine::{EXIT_LEVEL, MAP_RESTART};
|
||||
pub use host::natives::engine::{EXIT_LEVEL, GlassNames, MAP_RESTART};
|
||||
pub(crate) use host::natives::iw4::set_dvar;
|
||||
pub(crate) use host::players::{
|
||||
answer_join, answer_menu, apply_disconnects, apply_player_links, choose_class,
|
||||
|
||||
Reference in New Issue
Block a user