use crate::{Command, PlayerCamera, SceneHandler, Surface, prelude::*};
use indexmap::IndexMap;
use scenevm::Atom;
use theframework::prelude::*;
use vek::Vec2;
#[derive(PartialEq)]
pub enum ClientDrawMode {
D2,
D3,
}
use ClientDrawMode::*;
pub struct Rusterix {
pub assets: Assets,
pub server: Server,
pub client: Client,
pub audio: Option<AudioEngine>,
pub is_dirty_d2: bool,
pub is_dirty_d3: bool,
pub draw_mode: ClientDrawMode,
pub player_camera: PlayerCamera,
pub scene_handler: SceneHandler,
}
impl Default for Rusterix {
fn default() -> Self {
Self::new()
}
}
impl Rusterix {
fn apply_runtime_render_state(&mut self, map: &Map) {
self.scene_handler.runtime_render_state = self.server.get_render_state(&map.id);
}
pub fn new() -> Self {
let mut scene_handler = SceneHandler::default();
if let Some(bytes) = crate::Embedded::get("shader/2d_shader.wgsl") {
if let Ok(source) = std::str::from_utf8(bytes.data.as_ref()) {
scene_handler.vm.execute(Atom::SetSource2D(source.into()));
}
}
Self {
assets: Assets::default(),
server: Server::default(),
client: Client::default(),
audio: AudioEngine::new().ok(),
is_dirty_d2: true,
is_dirty_d3: true,
draw_mode: ClientDrawMode::D3,
player_camera: PlayerCamera::D2,
scene_handler,
}
}
pub fn set_d2(&mut self) {
self.draw_mode = D2;
}
pub fn set_d3(&mut self) {
self.draw_mode = D3;
}
pub fn set_dirty(&mut self) {
self.is_dirty_d2 = true;
self.is_dirty_d3 = true;
self.scene_handler.mark_dynamics_dirty();
}
pub fn set_overlay_dirty(&mut self) {
self.scene_handler.mark_dynamics_dirty();
}
pub fn set_assets(&mut self, assets: Assets) {
self.assets = assets;
self.load_audio_assets();
}
fn ensure_audio_engine(&mut self) {
if self.audio.is_none() {
self.audio = AudioEngine::new().ok();
}
}
pub fn load_audio_assets(&mut self) {
self.ensure_audio_engine();
let Some(engine) = self.audio.as_ref() else {
return;
};
engine.clear_clips();
for (name, bytes) in &self.assets.audio {
let _ = engine.load_clip_from_bytes(name, bytes);
}
for name in crate::audio::list_audio_fx_names(&self.assets.audio_fx_src) {
if let Ok(bytes) =
crate::audio::synthesize_audio_fx_wav(&self.assets.audio_fx_src, &name)
{
let _ = engine.load_clip_from_bytes(&name, &bytes);
}
}
}
pub fn play_audio(&mut self, name: &str) -> bool {
self.ensure_audio_engine();
let Some(engine) = self.audio.as_ref() else {
return false;
};
engine.play_one_shot(name, 1.0)
}
pub fn play_audio_on_bus(&mut self, name: &str, bus: &str, gain: f32, looping: bool) -> bool {
self.ensure_audio_engine();
let Some(engine) = self.audio.as_ref() else {
return false;
};
engine.play_on_bus(name, bus, gain, looping)
}
pub fn set_audio_bus_volume(&mut self, bus: &str, volume: f32) {
self.ensure_audio_engine();
if let Some(engine) = self.audio.as_ref() {
engine.set_bus_volume(bus, volume);
}
}
pub fn audio_bus_volume(&self, bus: &str) -> f32 {
if let Some(engine) = self.audio.as_ref() {
return engine.bus_volume(bus);
}
1.0
}
pub fn clear_audio_bus(&mut self, bus: &str) {
self.ensure_audio_engine();
if let Some(engine) = self.audio.as_ref() {
engine.clear_bus(bus);
}
}
pub fn clear_all_audio(&mut self) {
self.ensure_audio_engine();
if let Some(engine) = self.audio.as_ref() {
engine.clear_all_buses();
}
}
pub fn create_regions(&mut self) {
for (name, map) in &self.assets.maps {
self.server
.create_region_instance(name.clone(), map.clone(), &self.assets, "".into());
}
self.server.set_state(crate::ServerState::Running);
}
pub fn process_messages(&mut self, map: &Map, says: Vec<crate::server::Say>) {
self.client.process_messages(map, says);
}
pub fn apply_entities_items(
&mut self,
screen_size: Vec2<f32>,
map: &Map,
edit_surface: &Option<Surface>,
draw_sectors: bool,
) {
for e in map.entities.iter() {
if e.is_player() {
if let Some(Value::PlayerCamera(camera)) = e.attributes.get("player_camera") {
if *camera != self.player_camera {
self.player_camera = camera.clone();
if self.player_camera == PlayerCamera::D3Iso {
self.client.camera_d3 = Box::new(D3IsoCamera::new())
} else if self.player_camera == PlayerCamera::D3FirstP {
self.client.camera_d3 = Box::new(D3FirstPCamera::new());
}
}
break;
}
}
}
if self.draw_mode == ClientDrawMode::D2 {
self.apply_runtime_render_state(map);
self.client.apply_entities_items_d2(
screen_size,
map,
&self.assets,
edit_surface,
&mut self.scene_handler,
draw_sectors,
);
} else if self.draw_mode == ClientDrawMode::D3 {
self.client
.apply_entities_items_d3(map, &self.assets, &mut self.scene_handler);
}
}
pub fn build_custom_scene_d2(
&mut self,
screen_size: Vec2<f32>,
map: &Map,
values: &ValueContainer,
edit_surface: &Option<Surface>,
draw_sectors: bool,
) {
self.client.build_custom_scene_d2(
screen_size,
map,
&self.assets,
values,
edit_surface,
&mut self.scene_handler,
draw_sectors,
);
}
pub fn build_entities_items_d3(&mut self, map: &Map) {
self.client.builder_d3.build_entities_items(
map,
self.client.camera_d3.as_ref(),
&self.assets,
&mut self.client.scene,
&mut self.scene_handler,
);
}
pub fn build_dynamics_3d(&mut self, map: &Map, animation_frame: usize) {
let camera = self.client.camera_d3.as_ref();
self.scene_handler
.build_dynamics_3d(map, camera, animation_frame, &self.assets);
}
pub fn build_dynamics_2d(&mut self, map: &Map, animation_frame: usize) {
self.scene_handler
.build_dynamics_2d(map, animation_frame, &self.assets);
}
pub fn build_custom_scene_d3(&mut self, map: &Map, values: &ValueContainer) {
self.client.build_custom_scene_d3(map, &self.assets, values);
}
pub fn draw_custom_d2(&mut self, map: &Map, pixels: &mut [u8], width: usize, height: usize) {
self.apply_runtime_render_state(map);
self.client.draw_custom_d2(
map,
pixels,
width,
height,
&self.assets,
&mut self.scene_handler,
);
}
pub fn draw_d2(&mut self, map: &Map, pixels: &mut [u8], width: usize, height: usize) {
self.apply_runtime_render_state(map);
self.client.draw_d2(
map,
pixels,
width,
height,
&self.assets,
&mut self.scene_handler,
);
}
pub fn draw_d3(&mut self, map: &Map, pixels: &mut [u8], width: usize, height: usize) {
self.apply_runtime_render_state(map);
self.client.draw_d3(
map,
pixels,
width,
height,
&self.assets,
&mut self.scene_handler,
);
}
pub fn draw_scene(&mut self, map: &Map, pixels: &mut [u8], width: usize, height: usize) {
match self.draw_mode {
D2 => {
self.apply_runtime_render_state(map);
self.client.draw_d2(
map,
pixels,
width,
height,
&self.assets,
&mut self.scene_handler,
);
}
D3 => {
self.apply_runtime_render_state(map);
self.client.draw_d3(
map,
pixels,
width,
height,
&self.assets,
&mut self.scene_handler,
);
}
}
}
pub fn setup_client(&mut self) -> Vec<Command> {
let cmds = self.client.setup(&mut self.assets, &mut self.scene_handler);
self.load_audio_assets();
cmds
}
pub fn draw_game(
&mut self,
map: &Map,
messages: Vec<crate::server::Message>,
says: Vec<crate::server::Say>,
choices: Vec<crate::MultipleChoice>,
) {
self.apply_runtime_render_state(map);
self.client.process_messages(map, says);
self.client.draw_game(
map,
&self.assets,
messages,
choices,
&mut self.scene_handler,
);
}
pub fn prepare_game_scene_for_present(&mut self, map: &Map, size: (u32, u32)) -> bool {
self.apply_runtime_render_state(map);
self.client
.prepare_scenevm_direct(map, &self.assets, &mut self.scene_handler, size)
}
pub fn draw_ui_overlay_only(
&mut self,
map: &Map,
messages: Vec<crate::server::Message>,
choices: Vec<crate::MultipleChoice>,
width: u32,
height: u32,
) -> &TheRGBABuffer {
self.apply_runtime_render_state(map);
self.client
.draw_ui_overlay_only(map, &self.assets, messages, choices, width, height)
}
pub fn game_widget_rect(&self) -> Option<crate::Rect> {
self.client.game_widget_rect()
}
pub fn presentation_transform_for_surface(&self, size: (u32, u32)) -> (f32, f32, f32) {
self.client
.presentation_transform_for_surface(size.0, size.1)
}
pub fn clear_say_messages(&mut self) {
self.client.clear_say_messages();
}
pub fn client_touch_dragged(&mut self, coord: Vec2<i32>, map: &Map) {
self.client
.touch_dragged(coord, map, &mut self.scene_handler);
}
pub fn client_touch_hover(&mut self, coord: Vec2<i32>, map: &Map) {
self.client.touch_hover(coord, map, &mut self.scene_handler);
}
pub fn update_server(&mut self) -> Option<String> {
self.server.update(&mut self.assets)
}
pub fn set_tiles(&mut self, textures: IndexMap<Uuid, Tile>, editor: bool) {
let mut all_tiles = textures;
for (idx, col_opt) in self.assets.palette.colors.iter().enumerate() {
let Some(col) = col_opt else {
continue;
};
let tile_id = Uuid::from_u128(0x50414C455454455F0000000000000000u128 | idx as u128);
let [roughness, metallic, opacity, emissive] = self
.assets
.palette_materials
.get(idx)
.copied()
.unwrap_or([0.5, 0.0, 1.0, 0.0]);
let tile = all_tiles.entry(tile_id).or_insert_with(|| {
let mut t = Tile::from_texture(Texture::from_color(col.to_u8_array()));
t.id = tile_id;
t
});
for texture in &mut tile.textures {
texture.set_materials_all(roughness, metallic, opacity, emissive);
}
}
self.scene_handler.build_atlas(&all_tiles, editor);
self.assets.set_tiles(all_tiles);
let palette: Vec<vek::Vec4<f32>> = self
.assets
.palette
.colors
.iter()
.map(|entry| {
if let Some(col) = entry {
let [r, g, b, a] = col.to_array();
let to_linear = |c: f32| c.max(0.0).powf(2.2);
vek::Vec4::new(to_linear(r), to_linear(g), to_linear(b), a)
} else {
vek::Vec4::zero()
}
})
.collect();
self.scene_handler.vm.execute(Atom::SetPalette(palette));
self.scene_handler.clear_runtime_scene();
}
}