Added engine ease in/out
parent
cb65b67530
commit
088d1f7fb9
|
@ -3,7 +3,9 @@
|
||||||
space.engine = 20
|
space.engine = 20
|
||||||
|
|
||||||
engine.thrust = 100
|
engine.thrust = 100
|
||||||
engine.flare_sprite = "flare::ion"
|
engine.flare.sprite = "flare::ion"
|
||||||
|
engine.flare.on_start = "rise:top"
|
||||||
|
engine.flare.on_stop = "rise:bot"
|
||||||
steering.power = 20
|
steering.power = 20
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -66,6 +66,9 @@ pub struct AnimAutomaton {
|
||||||
/// The texture we're fading to
|
/// The texture we're fading to
|
||||||
/// (if we're moving downwards)
|
/// (if we're moving downwards)
|
||||||
next_texture: u32,
|
next_texture: u32,
|
||||||
|
|
||||||
|
/// If this is some, take this edge next
|
||||||
|
next_edge_override: Option<SectionEdge>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl AnimAutomaton {
|
impl AnimAutomaton {
|
||||||
|
@ -90,6 +93,7 @@ impl AnimAutomaton {
|
||||||
sprite: sprite.handle,
|
sprite: sprite.handle,
|
||||||
current_frame: 0,
|
current_frame: 0,
|
||||||
current_fade: 0.0,
|
current_fade: 0.0,
|
||||||
|
next_edge_override: None,
|
||||||
|
|
||||||
current_direction,
|
current_direction,
|
||||||
current_section,
|
current_section,
|
||||||
|
@ -98,21 +102,56 @@ impl AnimAutomaton {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Reset this animation
|
/// Override the next section transition
|
||||||
pub fn reset(&mut self, ct: &Content) {
|
pub fn next_edge(&mut self, s: SectionEdge) {
|
||||||
*self = Self::new(ct, self.sprite);
|
self.next_edge_override = Some(s);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Reverse this animation's direction
|
/// Force a transition to the given section right now
|
||||||
pub fn reverse(&mut self) {
|
pub fn jump_to(&mut self, ct: &Content, start: StartEdge) {
|
||||||
match self.current_direction {
|
self.take_edge(ct, start.into());
|
||||||
AnimDirection::Stop => {}
|
}
|
||||||
AnimDirection::Up => {
|
|
||||||
|
fn take_edge(&mut self, ct: &Content, e: SectionEdge) {
|
||||||
|
let sprite = ct.get_sprite(self.sprite);
|
||||||
|
let current_section = sprite.get_section(self.current_section);
|
||||||
|
|
||||||
|
match e {
|
||||||
|
SectionEdge::Stop => {
|
||||||
|
self.current_fade = 0.0;
|
||||||
|
self.current_frame = current_section.frames.len() - 1;
|
||||||
|
self.current_direction = AnimDirection::Stop;
|
||||||
|
}
|
||||||
|
SectionEdge::Top { section } => {
|
||||||
|
self.current_section = section;
|
||||||
|
self.current_frame = 0;
|
||||||
self.current_direction = AnimDirection::Down;
|
self.current_direction = AnimDirection::Down;
|
||||||
}
|
}
|
||||||
AnimDirection::Down => {
|
SectionEdge::Bot { section } => {
|
||||||
|
let s = sprite.get_section(section);
|
||||||
|
self.current_section = section;
|
||||||
|
self.current_frame = s.frames.len() - 1;
|
||||||
self.current_direction = AnimDirection::Up;
|
self.current_direction = AnimDirection::Up;
|
||||||
}
|
}
|
||||||
|
SectionEdge::Restart => {
|
||||||
|
self.current_frame = 0;
|
||||||
|
}
|
||||||
|
SectionEdge::Reverse => {
|
||||||
|
// Jump to SECOND frame, since we've already shown the
|
||||||
|
// first during the fade transition
|
||||||
|
|
||||||
|
match self.current_direction {
|
||||||
|
AnimDirection::Stop => {}
|
||||||
|
AnimDirection::Up => {
|
||||||
|
self.current_frame = 0;
|
||||||
|
self.current_direction = AnimDirection::Down;
|
||||||
|
}
|
||||||
|
AnimDirection::Down => {
|
||||||
|
self.current_frame = current_section.frames.len() - 1;
|
||||||
|
self.current_direction = AnimDirection::Up;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -129,15 +168,21 @@ impl AnimAutomaton {
|
||||||
// and we switch to the next frame when it hits 1.0. If we are stepping foward, it increases,
|
// and we switch to the next frame when it hits 1.0. If we are stepping foward, it increases,
|
||||||
// and if we are stepping backwards, it decreases.
|
// and if we are stepping backwards, it decreases.
|
||||||
|
|
||||||
// If this is zero, this section isn't animated.
|
// This should always be positive, and this fact is enforced by the content loader.
|
||||||
if current_section.frame_duration == 0.0 {
|
// if we get here, something is very wrong.
|
||||||
return;
|
assert!(current_section.frame_duration > 0.0);
|
||||||
}
|
|
||||||
|
|
||||||
match self.current_direction {
|
match self.current_direction {
|
||||||
AnimDirection::Down => self.current_fade += t / current_section.frame_duration,
|
AnimDirection::Down => self.current_fade += t / current_section.frame_duration,
|
||||||
AnimDirection::Up => self.current_fade -= t / current_section.frame_duration,
|
AnimDirection::Up => self.current_fade -= t / current_section.frame_duration,
|
||||||
AnimDirection::Stop => {}
|
AnimDirection::Stop => {
|
||||||
|
// Edge case: we're stopped and got a request to transition.
|
||||||
|
// we should transition right away.
|
||||||
|
|
||||||
|
if let Some(e) = self.next_edge_override {
|
||||||
|
self.take_edge(ct, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// We're stepping foward and finished this frame
|
// We're stepping foward and finished this frame
|
||||||
|
@ -150,32 +195,14 @@ impl AnimAutomaton {
|
||||||
if self.current_frame < current_section.frames.len() - 1 {
|
if self.current_frame < current_section.frames.len() - 1 {
|
||||||
self.current_frame += 1;
|
self.current_frame += 1;
|
||||||
} else {
|
} else {
|
||||||
match current_section.edge_bot {
|
let e = {
|
||||||
SectionEdge::Stop => {
|
if self.next_edge_override.is_some() {
|
||||||
self.current_fade = 0.0;
|
self.next_edge_override.take().unwrap()
|
||||||
self.current_frame = current_section.frames.len() - 1;
|
} else {
|
||||||
self.current_direction = AnimDirection::Stop;
|
current_section.edge_bot.clone()
|
||||||
}
|
}
|
||||||
SectionEdge::Top { section } => {
|
};
|
||||||
self.current_section = section;
|
self.take_edge(ct, e);
|
||||||
self.current_frame = 0;
|
|
||||||
}
|
|
||||||
SectionEdge::Bot { section } => {
|
|
||||||
let s = sprite.get_section(section);
|
|
||||||
self.current_section = section;
|
|
||||||
self.current_frame = s.frames.len() - 1;
|
|
||||||
self.reverse();
|
|
||||||
}
|
|
||||||
SectionEdge::Restart => {
|
|
||||||
self.current_frame = 0;
|
|
||||||
}
|
|
||||||
SectionEdge::Reverse => {
|
|
||||||
// Jump to SECOND frame, since we've already shown the
|
|
||||||
// first during the fade transition
|
|
||||||
self.current_frame = current_section.frames.len() - 1;
|
|
||||||
self.reverse()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let current_section = sprite.get_section(self.current_section);
|
let current_section = sprite.get_section(self.current_section);
|
||||||
|
@ -193,30 +220,14 @@ impl AnimAutomaton {
|
||||||
if self.current_frame > 0 {
|
if self.current_frame > 0 {
|
||||||
self.current_frame -= 1;
|
self.current_frame -= 1;
|
||||||
} else {
|
} else {
|
||||||
match current_section.edge_top {
|
let e = {
|
||||||
SectionEdge::Stop => {
|
if self.next_edge_override.is_some() {
|
||||||
self.current_fade = 0.0;
|
self.next_edge_override.take().unwrap()
|
||||||
self.current_frame = 0;
|
} else {
|
||||||
self.current_direction = AnimDirection::Stop;
|
current_section.edge_top.clone()
|
||||||
}
|
}
|
||||||
SectionEdge::Top { section } => {
|
};
|
||||||
self.current_section = section;
|
self.take_edge(ct, e);
|
||||||
self.current_frame = 0;
|
|
||||||
self.reverse();
|
|
||||||
}
|
|
||||||
SectionEdge::Bot { section } => {
|
|
||||||
let s = sprite.get_section(section);
|
|
||||||
self.current_section = section;
|
|
||||||
self.current_frame = s.frames.len() - 1;
|
|
||||||
}
|
|
||||||
SectionEdge::Reverse => {
|
|
||||||
self.current_frame = 0;
|
|
||||||
self.reverse();
|
|
||||||
}
|
|
||||||
SectionEdge::Restart => {
|
|
||||||
self.current_frame = current_section.frames.len() - 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let current_section = sprite.get_section(self.current_section);
|
let current_section = sprite.get_section(self.current_section);
|
||||||
|
|
|
@ -8,25 +8,9 @@
|
||||||
use std::{cmp::Eq, hash::Hash};
|
use std::{cmp::Eq, hash::Hash};
|
||||||
|
|
||||||
/// A lightweight representation of a sprite
|
/// A lightweight representation of a sprite
|
||||||
#[derive(Debug, Clone, Copy)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||||
pub struct SpriteHandle {
|
pub struct SpriteHandle {
|
||||||
pub(crate) index: usize,
|
pub(crate) index: usize,
|
||||||
|
|
||||||
/// The aspect ratio of this sprite (width / height)
|
|
||||||
pub aspect: f32,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Hash for SpriteHandle {
|
|
||||||
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
|
|
||||||
self.index.hash(state)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Eq for SpriteHandle {}
|
|
||||||
impl PartialEq for SpriteHandle {
|
|
||||||
fn eq(&self, other: &Self) -> bool {
|
|
||||||
self.index.eq(&other.index)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A lightweight representation of system body
|
/// A lightweight representation of system body
|
||||||
|
|
|
@ -13,6 +13,7 @@ use galactica_packer::{SpriteAtlas, SpriteAtlasImage};
|
||||||
use std::{
|
use std::{
|
||||||
collections::HashMap,
|
collections::HashMap,
|
||||||
fs::File,
|
fs::File,
|
||||||
|
hash::Hash,
|
||||||
io::Read,
|
io::Read,
|
||||||
path::{Path, PathBuf},
|
path::{Path, PathBuf},
|
||||||
};
|
};
|
||||||
|
@ -122,13 +123,18 @@ trait Build {
|
||||||
/// Stores temporary data while building context objects
|
/// Stores temporary data while building context objects
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub(crate) struct ContentBuildContext {
|
pub(crate) struct ContentBuildContext {
|
||||||
|
/// Map effect names to handles
|
||||||
pub effect_index: HashMap<String, EffectHandle>,
|
pub effect_index: HashMap<String, EffectHandle>,
|
||||||
|
|
||||||
|
/// Maps sprite handles to a map of section name -> section index
|
||||||
|
pub sprite_section_index: HashMap<SpriteHandle, HashMap<String, AnimSectionHandle>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ContentBuildContext {
|
impl ContentBuildContext {
|
||||||
fn new() -> Self {
|
fn new() -> Self {
|
||||||
Self {
|
Self {
|
||||||
effect_index: HashMap::new(),
|
effect_index: HashMap::new(),
|
||||||
|
sprite_section_index: HashMap::new(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,13 +1,14 @@
|
||||||
use anyhow::{bail, Context, Result};
|
use anyhow::{anyhow, Context, Result};
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
handle::SpriteHandle, Content, ContentBuildContext, EffectHandle, OutfitHandle, OutfitSpace,
|
handle::SpriteHandle, Content, ContentBuildContext, EffectHandle, OutfitHandle, OutfitSpace,
|
||||||
|
StartEdge,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub(crate) mod syntax {
|
pub(crate) mod syntax {
|
||||||
use crate::{effect, part::outfitspace, ContentBuildContext};
|
use crate::{effect, part::outfitspace, sprite::syntax::SectionEdge, ContentBuildContext};
|
||||||
use anyhow::{bail, Result};
|
use anyhow::{bail, Result};
|
||||||
use galactica_util::to_radians;
|
use galactica_util::to_radians;
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
|
@ -34,7 +35,14 @@ pub(crate) mod syntax {
|
||||||
#[derive(Debug, Deserialize)]
|
#[derive(Debug, Deserialize)]
|
||||||
pub struct Engine {
|
pub struct Engine {
|
||||||
pub thrust: f32,
|
pub thrust: f32,
|
||||||
pub flare_sprite: String,
|
pub flare: EngineFlare,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize)]
|
||||||
|
pub struct EngineFlare {
|
||||||
|
pub sprite: String,
|
||||||
|
pub on_start: Option<SectionEdge>,
|
||||||
|
pub on_stop: Option<SectionEdge>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Deserialize)]
|
#[derive(Debug, Deserialize)]
|
||||||
|
@ -139,6 +147,12 @@ pub struct Outfit {
|
||||||
/// engine points.
|
/// engine points.
|
||||||
pub engine_flare_sprite: Option<SpriteHandle>,
|
pub engine_flare_sprite: Option<SpriteHandle>,
|
||||||
|
|
||||||
|
/// Jump to this edge when engines turn on
|
||||||
|
pub engine_flare_on_start: Option<StartEdge>,
|
||||||
|
|
||||||
|
/// Jump to this edge when engines turn off
|
||||||
|
pub engine_flare_on_stop: Option<StartEdge>,
|
||||||
|
|
||||||
/// Shield hit points
|
/// Shield hit points
|
||||||
pub shield_strength: f32,
|
pub shield_strength: f32,
|
||||||
|
|
||||||
|
@ -253,6 +267,8 @@ impl crate::Build for Outfit {
|
||||||
engine_thrust: 0.0,
|
engine_thrust: 0.0,
|
||||||
steer_power: 0.0,
|
steer_power: 0.0,
|
||||||
engine_flare_sprite: None,
|
engine_flare_sprite: None,
|
||||||
|
engine_flare_on_start: None,
|
||||||
|
engine_flare_on_stop: None,
|
||||||
space: OutfitSpace::from(outfit.space),
|
space: OutfitSpace::from(outfit.space),
|
||||||
shield_delay: 0.0,
|
shield_delay: 0.0,
|
||||||
shield_generation: 0.0,
|
shield_generation: 0.0,
|
||||||
|
@ -261,16 +277,44 @@ impl crate::Build for Outfit {
|
||||||
|
|
||||||
// Engine stats
|
// Engine stats
|
||||||
if let Some(engine) = outfit.engine {
|
if let Some(engine) = outfit.engine {
|
||||||
let th = match content.sprite_index.get(&engine.flare_sprite) {
|
let sprite_handle = match content.sprite_index.get(&engine.flare.sprite) {
|
||||||
None => bail!(
|
None => {
|
||||||
"In outfit `{}`: flare sprite `{}` doesn't exist",
|
return Err(anyhow!(
|
||||||
outfit_name,
|
"flare sprite `{}` doesn't exist",
|
||||||
engine.flare_sprite
|
engine.flare.sprite
|
||||||
),
|
))
|
||||||
|
.with_context(|| format!("in outfit `{}`", outfit_name));
|
||||||
|
}
|
||||||
Some(t) => *t,
|
Some(t) => *t,
|
||||||
};
|
};
|
||||||
o.engine_thrust = engine.thrust;
|
o.engine_thrust = engine.thrust;
|
||||||
o.engine_flare_sprite = Some(th);
|
o.engine_flare_sprite = Some(sprite_handle);
|
||||||
|
|
||||||
|
o.engine_flare_on_start = {
|
||||||
|
let x = engine.flare.on_start;
|
||||||
|
if x.is_none() {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
let x = x.unwrap();
|
||||||
|
Some(
|
||||||
|
x.resolve_as_start(sprite_handle, build_context)
|
||||||
|
.with_context(|| format!("in outfit `{}`", outfit_name))?,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
o.engine_flare_on_stop = {
|
||||||
|
let x = engine.flare.on_stop;
|
||||||
|
if x.is_none() {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
let x = x.unwrap();
|
||||||
|
Some(
|
||||||
|
x.resolve_as_start(sprite_handle, build_context)
|
||||||
|
.with_context(|| format!("in outfit `{}`", outfit_name))?,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
// Steering stats
|
// Steering stats
|
||||||
|
|
|
@ -4,13 +4,11 @@ use std::collections::HashMap;
|
||||||
use crate::{handle::SpriteHandle, Content, ContentBuildContext};
|
use crate::{handle::SpriteHandle, Content, ContentBuildContext};
|
||||||
|
|
||||||
pub(crate) mod syntax {
|
pub(crate) mod syntax {
|
||||||
use crate::{Content, ContentBuildContext};
|
use crate::{Content, ContentBuildContext, SpriteHandle};
|
||||||
use anyhow::{anyhow, bail, Context, Ok, Result};
|
use anyhow::{anyhow, bail, Context, Ok, Result};
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
use std::{collections::HashMap, path::PathBuf};
|
use std::{collections::HashMap, path::PathBuf};
|
||||||
|
|
||||||
use super::AnimSectionHandle;
|
|
||||||
|
|
||||||
// Raw serde syntax structs.
|
// Raw serde syntax structs.
|
||||||
// These are never seen by code outside this crate.
|
// These are never seen by code outside this crate.
|
||||||
|
|
||||||
|
@ -66,12 +64,9 @@ pub(crate) mod syntax {
|
||||||
impl SpriteSection {
|
impl SpriteSection {
|
||||||
pub fn add_to(
|
pub fn add_to(
|
||||||
&self,
|
&self,
|
||||||
_build_context: &mut ContentBuildContext,
|
this_sprite: SpriteHandle,
|
||||||
|
build_context: &mut ContentBuildContext,
|
||||||
content: &mut Content,
|
content: &mut Content,
|
||||||
|
|
||||||
// An index of all sections in this sprite, used to resolve
|
|
||||||
// top and bot edges.
|
|
||||||
all_sections: &HashMap<String, AnimSectionHandle>,
|
|
||||||
) -> Result<((u32, u32), super::SpriteSection)> {
|
) -> Result<((u32, u32), super::SpriteSection)> {
|
||||||
// Make sure all frames have the same size and add them
|
// Make sure all frames have the same size and add them
|
||||||
// to the frame vector
|
// to the frame vector
|
||||||
|
@ -109,12 +104,12 @@ pub(crate) mod syntax {
|
||||||
}
|
}
|
||||||
|
|
||||||
let edge_top = match &self.top {
|
let edge_top = match &self.top {
|
||||||
Some(x) => x.resolve_as_edge(all_sections)?,
|
Some(x) => x.resolve_as_edge(this_sprite, build_context)?,
|
||||||
None => super::SectionEdge::Stop,
|
None => super::SectionEdge::Stop,
|
||||||
};
|
};
|
||||||
|
|
||||||
let edge_bot = match &self.bot {
|
let edge_bot = match &self.bot {
|
||||||
Some(x) => x.resolve_as_edge(all_sections)?,
|
Some(x) => x.resolve_as_edge(this_sprite, build_context)?,
|
||||||
None => super::SectionEdge::Stop,
|
None => super::SectionEdge::Stop,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -140,10 +135,11 @@ pub(crate) mod syntax {
|
||||||
impl SectionEdge {
|
impl SectionEdge {
|
||||||
pub fn resolve_as_start(
|
pub fn resolve_as_start(
|
||||||
&self,
|
&self,
|
||||||
all_sections: &HashMap<String, AnimSectionHandle>,
|
sprite: SpriteHandle,
|
||||||
|
build_context: &ContentBuildContext,
|
||||||
) -> Result<super::StartEdge> {
|
) -> Result<super::StartEdge> {
|
||||||
let e = self
|
let e = self
|
||||||
.resolve_as_edge(all_sections)
|
.resolve_as_edge(sprite, build_context)
|
||||||
.with_context(|| format!("while resolving start edge"))?;
|
.with_context(|| format!("while resolving start edge"))?;
|
||||||
match e {
|
match e {
|
||||||
super::SectionEdge::Bot { section } => Ok(super::StartEdge::Bot { section }),
|
super::SectionEdge::Bot { section } => Ok(super::StartEdge::Bot { section }),
|
||||||
|
@ -156,8 +152,11 @@ pub(crate) mod syntax {
|
||||||
|
|
||||||
pub fn resolve_as_edge(
|
pub fn resolve_as_edge(
|
||||||
&self,
|
&self,
|
||||||
all_sections: &HashMap<String, AnimSectionHandle>,
|
sprite: SpriteHandle,
|
||||||
|
build_context: &ContentBuildContext,
|
||||||
) -> Result<super::SectionEdge> {
|
) -> Result<super::SectionEdge> {
|
||||||
|
let all_sections = build_context.sprite_section_index.get(&sprite).unwrap();
|
||||||
|
|
||||||
if self.val == "stop" {
|
if self.val == "stop" {
|
||||||
return Ok(super::SectionEdge::Stop);
|
return Ok(super::SectionEdge::Stop);
|
||||||
}
|
}
|
||||||
|
@ -203,7 +202,7 @@ pub(crate) mod syntax {
|
||||||
pub struct AnimSectionHandle(pub(crate) usize);
|
pub struct AnimSectionHandle(pub(crate) usize);
|
||||||
|
|
||||||
/// An edge between two animation sections
|
/// An edge between two animation sections
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone, Copy)]
|
||||||
pub enum SectionEdge {
|
pub enum SectionEdge {
|
||||||
/// Stop at the last frame of this section
|
/// Stop at the last frame of this section
|
||||||
Stop,
|
Stop,
|
||||||
|
@ -228,7 +227,7 @@ pub enum SectionEdge {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Where to start an animation
|
/// Where to start an animation
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone, Copy)]
|
||||||
pub enum StartEdge {
|
pub enum StartEdge {
|
||||||
/// Play the given section from the bottm
|
/// Play the given section from the bottm
|
||||||
Bot {
|
Bot {
|
||||||
|
@ -243,6 +242,15 @@ pub enum StartEdge {
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Into<SectionEdge> for StartEdge {
|
||||||
|
fn into(self) -> SectionEdge {
|
||||||
|
match self {
|
||||||
|
Self::Bot { section } => SectionEdge::Bot { section },
|
||||||
|
Self::Top { section } => SectionEdge::Top { section },
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Represents a sprite that may be used in the game.
|
/// Represents a sprite that may be used in the game.
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct Sprite {
|
pub struct Sprite {
|
||||||
|
@ -276,6 +284,14 @@ impl Sprite {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Get this sprite's starting section
|
||||||
|
pub fn get_start_section(&self) -> AnimSectionHandle {
|
||||||
|
match self.start_at {
|
||||||
|
StartEdge::Bot { section } => section,
|
||||||
|
StartEdge::Top { section } => section,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Iterate this sprite's sections
|
/// Iterate this sprite's sections
|
||||||
pub fn iter_sections(&self) -> impl Iterator<Item = &SpriteSection> {
|
pub fn iter_sections(&self) -> impl Iterator<Item = &SpriteSection> {
|
||||||
self.sections.iter()
|
self.sections.iter()
|
||||||
|
@ -330,10 +346,12 @@ impl crate::Build for Sprite {
|
||||||
|
|
||||||
let h = SpriteHandle {
|
let h = SpriteHandle {
|
||||||
index: content.sprites.len(),
|
index: content.sprites.len(),
|
||||||
aspect,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
content.sprite_index.insert(sprite_name.clone(), h);
|
content.sprite_index.insert(sprite_name.clone(), h);
|
||||||
|
let mut smap = HashMap::new();
|
||||||
|
smap.insert("anim".to_string(), AnimSectionHandle(0));
|
||||||
|
build_context.sprite_section_index.insert(h, smap);
|
||||||
|
|
||||||
content.sprites.push(Self {
|
content.sprites.push(Self {
|
||||||
name: sprite_name,
|
name: sprite_name,
|
||||||
|
@ -357,26 +375,33 @@ impl crate::Build for Sprite {
|
||||||
// Name the one section in this sprite "anim"
|
// Name the one section in this sprite "anim"
|
||||||
section_names.insert("anim".to_owned(), AnimSectionHandle(0));
|
section_names.insert("anim".to_owned(), AnimSectionHandle(0));
|
||||||
|
|
||||||
|
let sprite_handle = SpriteHandle {
|
||||||
|
index: content.sprites.len(),
|
||||||
|
};
|
||||||
|
content
|
||||||
|
.sprite_index
|
||||||
|
.insert(sprite_name.clone(), sprite_handle);
|
||||||
|
let mut smap = HashMap::new();
|
||||||
|
smap.insert("anim".to_string(), AnimSectionHandle(0));
|
||||||
|
build_context
|
||||||
|
.sprite_section_index
|
||||||
|
.insert(sprite_handle, smap);
|
||||||
|
|
||||||
let (dim, section) = s
|
let (dim, section) = s
|
||||||
.add_to(build_context, content, §ion_names)
|
.add_to(sprite_handle, build_context, content)
|
||||||
.with_context(|| format!("while parsing sprite `{}`", sprite_name))?;
|
.with_context(|| format!("while parsing sprite `{}`", sprite_name))?;
|
||||||
let aspect = dim.0 as f32 / dim.1 as f32;
|
let aspect = dim.0 as f32 / dim.1 as f32;
|
||||||
let h = SpriteHandle {
|
|
||||||
index: content.sprites.len(),
|
|
||||||
aspect,
|
|
||||||
};
|
|
||||||
|
|
||||||
let mut sections = Vec::new();
|
let mut sections = Vec::new();
|
||||||
sections.push(section);
|
sections.push(section);
|
||||||
|
|
||||||
content.sprite_index.insert(sprite_name.clone(), h);
|
|
||||||
content.sprites.push(Self {
|
content.sprites.push(Self {
|
||||||
name: sprite_name,
|
name: sprite_name,
|
||||||
sections,
|
sections,
|
||||||
start_at: StartEdge::Bot {
|
start_at: StartEdge::Bot {
|
||||||
section: AnimSectionHandle(0),
|
section: AnimSectionHandle(0),
|
||||||
},
|
},
|
||||||
handle: h,
|
handle: sprite_handle,
|
||||||
aspect,
|
aspect,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -388,9 +413,19 @@ impl crate::Build for Sprite {
|
||||||
idx += 1;
|
idx += 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let sprite_handle = SpriteHandle {
|
||||||
|
index: content.sprites.len(),
|
||||||
|
};
|
||||||
|
content
|
||||||
|
.sprite_index
|
||||||
|
.insert(sprite_name.clone(), sprite_handle);
|
||||||
|
build_context
|
||||||
|
.sprite_section_index
|
||||||
|
.insert(sprite_handle, section_names.clone());
|
||||||
|
|
||||||
let start_at = s
|
let start_at = s
|
||||||
.start_at
|
.start_at
|
||||||
.resolve_as_start(§ion_names)
|
.resolve_as_start(sprite_handle, build_context)
|
||||||
.with_context(|| format!("while loading sprite `{}`", sprite_name))?;
|
.with_context(|| format!("while loading sprite `{}`", sprite_name))?;
|
||||||
|
|
||||||
let mut sections = Vec::with_capacity(idx);
|
let mut sections = Vec::with_capacity(idx);
|
||||||
|
@ -403,7 +438,7 @@ impl crate::Build for Sprite {
|
||||||
for (k, _) in names {
|
for (k, _) in names {
|
||||||
let v = s.section.get(k).unwrap();
|
let v = s.section.get(k).unwrap();
|
||||||
let (d, s) = v
|
let (d, s) = v
|
||||||
.add_to(build_context, content, §ion_names)
|
.add_to(sprite_handle, build_context, content)
|
||||||
.with_context(|| format!("while parsing sprite `{}`", sprite_name))
|
.with_context(|| format!("while parsing sprite `{}`", sprite_name))
|
||||||
.with_context(|| format!("while parsing section `{}`", k))?;
|
.with_context(|| format!("while parsing section `{}`", k))?;
|
||||||
|
|
||||||
|
@ -423,17 +458,11 @@ impl crate::Build for Sprite {
|
||||||
let dim = dim.unwrap();
|
let dim = dim.unwrap();
|
||||||
let aspect = dim.0 as f32 / dim.1 as f32;
|
let aspect = dim.0 as f32 / dim.1 as f32;
|
||||||
|
|
||||||
let h = SpriteHandle {
|
|
||||||
index: content.sprites.len(),
|
|
||||||
aspect,
|
|
||||||
};
|
|
||||||
|
|
||||||
content.sprite_index.insert(sprite_name.clone(), h);
|
|
||||||
content.sprites.push(Self {
|
content.sprites.push(Self {
|
||||||
name: sprite_name,
|
name: sprite_name,
|
||||||
sections,
|
sections,
|
||||||
start_at,
|
start_at,
|
||||||
handle: h,
|
handle: sprite_handle,
|
||||||
aspect,
|
aspect,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,11 +12,11 @@ use crate::{
|
||||||
impl GPUState {
|
impl GPUState {
|
||||||
pub(super) fn phys_push_ship(
|
pub(super) fn phys_push_ship(
|
||||||
&mut self,
|
&mut self,
|
||||||
state: &RenderInput,
|
input: &RenderInput,
|
||||||
// NE and SW corners of screen
|
// NE and SW corners of screen
|
||||||
screen_clip: (Point2<f32>, Point2<f32>),
|
screen_clip: (Point2<f32>, Point2<f32>),
|
||||||
) {
|
) {
|
||||||
for ship in state.systemsim.iter_ships() {
|
for ship in input.systemsim.iter_ships() {
|
||||||
// TODO: move collapse sequence here?
|
// TODO: move collapse sequence here?
|
||||||
|
|
||||||
let ship_pos;
|
let ship_pos;
|
||||||
|
@ -26,21 +26,21 @@ impl GPUState {
|
||||||
ShipState::Dead | ShipState::Landed { .. } => continue,
|
ShipState::Dead | ShipState::Landed { .. } => continue,
|
||||||
|
|
||||||
ShipState::Collapsing { .. } | ShipState::Flying { .. } => {
|
ShipState::Collapsing { .. } | ShipState::Flying { .. } => {
|
||||||
let r = state.systemsim.get_rigid_body(ship.rigid_body).unwrap();
|
let r = input.systemsim.get_rigid_body(ship.rigid_body).unwrap();
|
||||||
let pos = *r.translation();
|
let pos = *r.translation();
|
||||||
ship_pos = Point3::new(pos.x, pos.y, 1.0);
|
ship_pos = Point3::new(pos.x, pos.y, 1.0);
|
||||||
let ship_rot = r.rotation();
|
let ship_rot = r.rotation();
|
||||||
ship_ang = ship_rot.angle();
|
ship_ang = ship_rot.angle();
|
||||||
ship_cnt = state.ct.get_ship(ship.get_data().get_content());
|
ship_cnt = input.ct.get_ship(ship.get_data().get_content());
|
||||||
}
|
}
|
||||||
|
|
||||||
ShipState::UnLanding { current_z, .. } | ShipState::Landing { current_z, .. } => {
|
ShipState::UnLanding { current_z, .. } | ShipState::Landing { current_z, .. } => {
|
||||||
let r = state.systemsim.get_rigid_body(ship.rigid_body).unwrap();
|
let r = input.systemsim.get_rigid_body(ship.rigid_body).unwrap();
|
||||||
let pos = *r.translation();
|
let pos = *r.translation();
|
||||||
ship_pos = Point3::new(pos.x, pos.y, *current_z);
|
ship_pos = Point3::new(pos.x, pos.y, *current_z);
|
||||||
let ship_rot = r.rotation();
|
let ship_rot = r.rotation();
|
||||||
ship_ang = ship_rot.angle();
|
ship_ang = ship_rot.angle();
|
||||||
ship_cnt = state.ct.get_ship(ship.get_data().get_content());
|
ship_cnt = input.ct.get_ship(ship.get_data().get_content());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -48,14 +48,15 @@ impl GPUState {
|
||||||
// TODO: adjust parallax for zoom?
|
// TODO: adjust parallax for zoom?
|
||||||
// 1.0 is z-coordinate, which is constant for ships
|
// 1.0 is z-coordinate, which is constant for ships
|
||||||
let pos: Point2<f32> =
|
let pos: Point2<f32> =
|
||||||
(Point2::new(ship_pos.x, ship_pos.y) - state.camera_pos) / ship_pos.z;
|
(Point2::new(ship_pos.x, ship_pos.y) - input.camera_pos) / ship_pos.z;
|
||||||
|
|
||||||
// Game dimensions of this sprite post-scale.
|
// Game dimensions of this sprite post-scale.
|
||||||
// Post-scale width or height, whichever is larger.
|
// Post-scale width or height, whichever is larger.
|
||||||
// This is in game units.
|
// This is in game units.
|
||||||
//
|
//
|
||||||
// We take the maximum to account for rotated sprites.
|
// We take the maximum to account for rotated sprites.
|
||||||
let m = (ship_cnt.size / ship_pos.z) * ship_cnt.sprite.aspect.max(1.0);
|
let m =
|
||||||
|
(ship_cnt.size / ship_pos.z) * input.ct.get_sprite(ship_cnt.sprite).aspect.max(1.0);
|
||||||
|
|
||||||
// Don't draw sprites that are off the screen
|
// Don't draw sprites that are off the screen
|
||||||
if pos.x < screen_clip.0.x - m
|
if pos.x < screen_clip.0.x - m
|
||||||
|
@ -98,7 +99,7 @@ impl GPUState {
|
||||||
| ShipState::Landing { .. } => true,
|
| ShipState::Landing { .. } => true,
|
||||||
_ => false,
|
_ => false,
|
||||||
};
|
};
|
||||||
ship.get_controls().thrust && is_flying
|
is_flying
|
||||||
} {
|
} {
|
||||||
for (engine_point, anim) in ship.iter_engine_anim() {
|
for (engine_point, anim) in ship.iter_engine_anim() {
|
||||||
self.state.queue.write_buffer(
|
self.state.queue.write_buffer(
|
||||||
|
@ -136,12 +137,12 @@ impl GPUState {
|
||||||
|
|
||||||
pub(super) fn phys_push_projectile(
|
pub(super) fn phys_push_projectile(
|
||||||
&mut self,
|
&mut self,
|
||||||
state: &RenderInput,
|
input: &RenderInput,
|
||||||
// NE and SW corners of screen
|
// NE and SW corners of screen
|
||||||
screen_clip: (Point2<f32>, Point2<f32>),
|
screen_clip: (Point2<f32>, Point2<f32>),
|
||||||
) {
|
) {
|
||||||
for p in state.systemsim.iter_projectiles() {
|
for p in input.systemsim.iter_projectiles() {
|
||||||
let r = state.systemsim.get_rigid_body(p.rigid_body).unwrap();
|
let r = input.systemsim.get_rigid_body(p.rigid_body).unwrap();
|
||||||
let proj_pos = *r.translation();
|
let proj_pos = *r.translation();
|
||||||
let proj_rot = r.rotation();
|
let proj_rot = r.rotation();
|
||||||
let proj_ang = proj_rot.angle();
|
let proj_ang = proj_rot.angle();
|
||||||
|
@ -150,14 +151,14 @@ impl GPUState {
|
||||||
// Position adjusted for parallax
|
// Position adjusted for parallax
|
||||||
// TODO: adjust parallax for zoom?
|
// TODO: adjust parallax for zoom?
|
||||||
// 1.0 is z-coordinate, which is constant for projectiles
|
// 1.0 is z-coordinate, which is constant for projectiles
|
||||||
let pos = (proj_pos - state.camera_pos) / 1.0;
|
let pos = (proj_pos - input.camera_pos) / 1.0;
|
||||||
|
|
||||||
// Game dimensions of this sprite post-scale.
|
// Game dimensions of this sprite post-scale.
|
||||||
// Post-scale width or height, whichever is larger.
|
// Post-scale width or height, whichever is larger.
|
||||||
// This is in game units.
|
// This is in game units.
|
||||||
//
|
//
|
||||||
// We take the maximum to account for rotated sprites.
|
// We take the maximum to account for rotated sprites.
|
||||||
let m = (proj_cnt.size / 1.0) * proj_cnt.sprite.aspect.max(1.0);
|
let m = (proj_cnt.size / 1.0) * input.ct.get_sprite(proj_cnt.sprite).aspect.max(1.0);
|
||||||
|
|
||||||
// Don't draw sprites that are off the screen
|
// Don't draw sprites that are off the screen
|
||||||
if pos.x < screen_clip.0.x - m
|
if pos.x < screen_clip.0.x - m
|
||||||
|
@ -196,22 +197,22 @@ impl GPUState {
|
||||||
|
|
||||||
pub(super) fn phys_push_system(
|
pub(super) fn phys_push_system(
|
||||||
&mut self,
|
&mut self,
|
||||||
state: &RenderInput,
|
input: &RenderInput,
|
||||||
// NE and SW corners of screen
|
// NE and SW corners of screen
|
||||||
screen_clip: (Point2<f32>, Point2<f32>),
|
screen_clip: (Point2<f32>, Point2<f32>),
|
||||||
) {
|
) {
|
||||||
let system = state.ct.get_system(state.current_system);
|
let system = input.ct.get_system(input.current_system);
|
||||||
|
|
||||||
for o in &system.objects {
|
for o in &system.objects {
|
||||||
// Position adjusted for parallax
|
// Position adjusted for parallax
|
||||||
let pos: Point2<f32> = (Point2::new(o.pos.x, o.pos.y) - state.camera_pos) / o.pos.z;
|
let pos: Point2<f32> = (Point2::new(o.pos.x, o.pos.y) - input.camera_pos) / o.pos.z;
|
||||||
|
|
||||||
// Game dimensions of this sprite post-scale.
|
// Game dimensions of this sprite post-scale.
|
||||||
// Post-scale width or height, whichever is larger.
|
// Post-scale width or height, whichever is larger.
|
||||||
// This is in game units.
|
// This is in game units.
|
||||||
//
|
//
|
||||||
// We take the maximum to account for rotated sprites.
|
// We take the maximum to account for rotated sprites.
|
||||||
let m = (o.size / o.pos.z) * o.sprite.aspect.max(1.0);
|
let m = (o.size / o.pos.z) * input.ct.get_sprite(o.sprite).aspect.max(1.0);
|
||||||
|
|
||||||
// Don't draw sprites that are off the screen
|
// Don't draw sprites that are off the screen
|
||||||
if pos.x < screen_clip.0.x - m
|
if pos.x < screen_clip.0.x - m
|
||||||
|
@ -239,7 +240,7 @@ impl GPUState {
|
||||||
}]),
|
}]),
|
||||||
);
|
);
|
||||||
|
|
||||||
let sprite = state.ct.get_sprite(o.sprite);
|
let sprite = input.ct.get_sprite(o.sprite);
|
||||||
let texture_a = sprite.get_first_frame(); // ANIMATE
|
let texture_a = sprite.get_first_frame(); // ANIMATE
|
||||||
|
|
||||||
// Push this object's instance
|
// Push this object's instance
|
||||||
|
|
|
@ -53,6 +53,7 @@ impl Planet {
|
||||||
current_object: None,
|
current_object: None,
|
||||||
|
|
||||||
planet_desc: UiTextArea::new(
|
planet_desc: UiTextArea::new(
|
||||||
|
ct,
|
||||||
state,
|
state,
|
||||||
ct.get_sprite_handle("ui::planet"),
|
ct.get_sprite_handle("ui::planet"),
|
||||||
Point2::new(0.0, 0.0),
|
Point2::new(0.0, 0.0),
|
||||||
|
@ -67,6 +68,7 @@ impl Planet {
|
||||||
),
|
),
|
||||||
|
|
||||||
planet_name: UiTextArea::new(
|
planet_name: UiTextArea::new(
|
||||||
|
ct,
|
||||||
state,
|
state,
|
||||||
ct.get_sprite_handle("ui::planet"),
|
ct.get_sprite_handle("ui::planet"),
|
||||||
Point2::new(0.0, 0.0),
|
Point2::new(0.0, 0.0),
|
||||||
|
@ -135,10 +137,10 @@ impl Planet {
|
||||||
self.sprite.push_to_buffer(input, state);
|
self.sprite.push_to_buffer(input, state);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_textarea(&self, _input: &RenderInput, state: &RenderState) -> [TextArea; 2] {
|
pub fn get_textarea(&self, input: &RenderInput, state: &RenderState) -> [TextArea; 2] {
|
||||||
[
|
[
|
||||||
self.planet_desc.get_textarea(state),
|
self.planet_desc.get_textarea(input, state),
|
||||||
self.planet_name.get_textarea(state),
|
self.planet_name.get_textarea(input, state),
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -133,7 +133,7 @@ impl Radar {
|
||||||
([c[0], c[1], c[2], 1.0], 1.0)
|
([c[0], c[1], c[2], 1.0], 1.0)
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
let size = (ship.size * ship.sprite.aspect) * ship_scale * z_scale;
|
let size = (ship.size * input.ct.get_sprite(ship.sprite).aspect) * ship_scale * z_scale;
|
||||||
let p: Point2<f32> = {
|
let p: Point2<f32> = {
|
||||||
if s.collider == input.player.ship.unwrap() {
|
if s.collider == input.player.ship.unwrap() {
|
||||||
self.last_player_position
|
self.last_player_position
|
||||||
|
|
|
@ -38,7 +38,10 @@ impl UiSprite {
|
||||||
/// Add this image to the gpu sprite buffer
|
/// Add this image to the gpu sprite buffer
|
||||||
pub fn push_to_buffer(&self, input: &RenderInput, state: &mut RenderState) {
|
pub fn push_to_buffer(&self, input: &RenderInput, state: &mut RenderState) {
|
||||||
let pos = Point2::new(self.rect.pos.x, self.rect.pos.y);
|
let pos = Point2::new(self.rect.pos.x, self.rect.pos.y);
|
||||||
let dim = Vector2::new(self.rect.dim.y * self.sprite.aspect, self.rect.dim.y);
|
let dim = Vector2::new(
|
||||||
|
self.rect.dim.y * input.ct.get_sprite(self.sprite).aspect,
|
||||||
|
self.rect.dim.y,
|
||||||
|
);
|
||||||
|
|
||||||
for c in &self.children_under {
|
for c in &self.children_under {
|
||||||
c.push_to_buffer_child(input, state, pos, dim);
|
c.push_to_buffer_child(input, state, pos, dim);
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
use galactica_content::SpriteHandle;
|
use galactica_content::{Content, SpriteHandle};
|
||||||
use glyphon::{cosmic_text::Align, Attrs, Buffer, Color, Metrics, Shaping, TextArea, TextBounds};
|
use glyphon::{cosmic_text::Align, Attrs, Buffer, Color, Metrics, Shaping, TextArea, TextBounds};
|
||||||
use nalgebra::{Point2, Vector2};
|
use nalgebra::{Point2, Vector2};
|
||||||
|
|
||||||
use super::SpriteRect;
|
use super::SpriteRect;
|
||||||
use crate::RenderState;
|
use crate::{RenderInput, RenderState};
|
||||||
|
|
||||||
/// Represents a text area inside a sprite.
|
/// Represents a text area inside a sprite.
|
||||||
pub(crate) struct UiTextArea {
|
pub(crate) struct UiTextArea {
|
||||||
|
@ -32,6 +32,7 @@ pub(crate) struct UiTextArea {
|
||||||
|
|
||||||
impl UiTextArea {
|
impl UiTextArea {
|
||||||
pub fn new(
|
pub fn new(
|
||||||
|
ct: &Content,
|
||||||
state: &mut RenderState,
|
state: &mut RenderState,
|
||||||
sprite: SpriteHandle,
|
sprite: SpriteHandle,
|
||||||
sprite_position: Point2<f32>,
|
sprite_position: Point2<f32>,
|
||||||
|
@ -50,16 +51,16 @@ impl UiTextArea {
|
||||||
align,
|
align,
|
||||||
color,
|
color,
|
||||||
};
|
};
|
||||||
s.set_size(state, sprite_size);
|
s.set_size(ct, state, sprite_size);
|
||||||
return s;
|
return s;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_size(&mut self, state: &mut RenderState, sprite_size: f32) {
|
pub fn set_size(&mut self, ct: &Content, state: &mut RenderState, sprite_size: f32) {
|
||||||
self.sprite_size = sprite_size;
|
self.sprite_size = sprite_size;
|
||||||
self.buffer.set_size(
|
self.buffer.set_size(
|
||||||
&mut state.text_font_system,
|
&mut state.text_font_system,
|
||||||
(self.rect.dim.x * self.sprite_size) * state.window.scale_factor() as f32,
|
(self.rect.dim.x * self.sprite_size) * state.window.scale_factor() as f32,
|
||||||
(self.rect.dim.y * self.sprite_size * self.sprite.aspect)
|
(self.rect.dim.y * self.sprite_size * ct.get_sprite(self.sprite).aspect)
|
||||||
* state.window.scale_factor() as f32,
|
* state.window.scale_factor() as f32,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -74,9 +75,9 @@ impl UiTextArea {
|
||||||
self.buffer.shape_until_scroll(&mut state.text_font_system);
|
self.buffer.shape_until_scroll(&mut state.text_font_system);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_textarea(&self, state: &RenderState) -> TextArea {
|
pub fn get_textarea(&self, input: &RenderInput, state: &RenderState) -> TextArea {
|
||||||
let h = self.sprite_size;
|
let h = self.sprite_size;
|
||||||
let w = self.sprite.aspect * h;
|
let w = input.ct.get_sprite(self.sprite).aspect * h;
|
||||||
|
|
||||||
// Glypon works with physical pixels, so we must convert
|
// Glypon works with physical pixels, so we must convert
|
||||||
let fac = state.window.scale_factor() as f32;
|
let fac = state.window.scale_factor() as f32;
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
use galactica_content::{CollapseEvent, Ship};
|
use galactica_content::{CollapseEvent, Content, Ship};
|
||||||
use nalgebra::{Point2, Vector2};
|
use nalgebra::{Point2, Vector2};
|
||||||
use rand::{rngs::ThreadRng, Rng};
|
use rand::{rngs::ThreadRng, Rng};
|
||||||
use rapier2d::{dynamics::RigidBody, geometry::Collider};
|
use rapier2d::{dynamics::RigidBody, geometry::Collider};
|
||||||
|
@ -32,13 +32,14 @@ impl ShipCollapseSequence {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Pick a random points inside a ship's collider
|
/// Pick a random points inside a ship's collider
|
||||||
fn random_in_ship(&mut self, ship: &Ship, collider: &Collider) -> Vector2<f32> {
|
fn random_in_ship(&mut self, ct: &Content, ship: &Ship, collider: &Collider) -> Vector2<f32> {
|
||||||
let mut y = 0.0;
|
let mut y = 0.0;
|
||||||
let mut x = 0.0;
|
let mut x = 0.0;
|
||||||
let mut a = false;
|
let mut a = false;
|
||||||
while !a {
|
while !a {
|
||||||
x = self.rng.gen_range(-1.0..=1.0) * ship.size / 2.0;
|
x = self.rng.gen_range(-1.0..=1.0) * ship.size / 2.0;
|
||||||
y = self.rng.gen_range(-1.0..=1.0) * ship.size * ship.sprite.aspect / 2.0;
|
y = self.rng.gen_range(-1.0..=1.0) * ship.size * ct.get_sprite(ship.sprite).aspect
|
||||||
|
/ 2.0;
|
||||||
a = collider.shape().contains_local_point(&Point2::new(x, y));
|
a = collider.shape().contains_local_point(&Point2::new(x, y));
|
||||||
}
|
}
|
||||||
Vector2::new(x, y)
|
Vector2::new(x, y)
|
||||||
|
@ -76,7 +77,7 @@ impl ShipCollapseSequence {
|
||||||
let pos: Vector2<f32> = if let Some(pos) = spawner.pos {
|
let pos: Vector2<f32> = if let Some(pos) = spawner.pos {
|
||||||
Vector2::new(pos.x, pos.y)
|
Vector2::new(pos.x, pos.y)
|
||||||
} else {
|
} else {
|
||||||
self.random_in_ship(ship_content, collider)
|
self.random_in_ship(res.ct, ship_content, collider)
|
||||||
};
|
};
|
||||||
let pos = ship_pos + (ship_rot * pos);
|
let pos = ship_pos + (ship_rot * pos);
|
||||||
|
|
||||||
|
@ -120,7 +121,7 @@ impl ShipCollapseSequence {
|
||||||
let pos = if let Some(pos) = spawner.pos {
|
let pos = if let Some(pos) = spawner.pos {
|
||||||
Vector2::new(pos.x, pos.y)
|
Vector2::new(pos.x, pos.y)
|
||||||
} else {
|
} else {
|
||||||
self.random_in_ship(ship_content, collider)
|
self.random_in_ship(res.ct, ship_content, collider)
|
||||||
};
|
};
|
||||||
|
|
||||||
// Position, adjusted for ship rotation
|
// Position, adjusted for ship rotation
|
||||||
|
|
|
@ -64,6 +64,9 @@ pub struct PhysSimShip {
|
||||||
/// This ship's controls
|
/// This ship's controls
|
||||||
pub(crate) controls: ShipControls,
|
pub(crate) controls: ShipControls,
|
||||||
|
|
||||||
|
/// This ship's controls during the last frame
|
||||||
|
last_controls: ShipControls,
|
||||||
|
|
||||||
/// This ship's collapse sequence
|
/// This ship's collapse sequence
|
||||||
collapse_sequence: Option<ShipCollapseSequence>,
|
collapse_sequence: Option<ShipCollapseSequence>,
|
||||||
}
|
}
|
||||||
|
@ -86,6 +89,7 @@ impl PhysSimShip {
|
||||||
data: ShipData::new(ct, handle, faction, personality),
|
data: ShipData::new(ct, handle, faction, personality),
|
||||||
engine_anim: Vec::new(),
|
engine_anim: Vec::new(),
|
||||||
controls: ShipControls::new(),
|
controls: ShipControls::new(),
|
||||||
|
last_controls: ShipControls::new(),
|
||||||
collapse_sequence: Some(ShipCollapseSequence::new(ship_ct.collapse.length)),
|
collapse_sequence: Some(ShipCollapseSequence::new(ship_ct.collapse.length)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -100,14 +104,32 @@ impl PhysSimShip {
|
||||||
self.data.step(res.t);
|
self.data.step(res.t);
|
||||||
self.anim.step(res.ct, res.t);
|
self.anim.step(res.ct, res.t);
|
||||||
|
|
||||||
if self.controls.thrust {
|
for (_, e) in &mut self.engine_anim {
|
||||||
for (_, e) in &mut self.engine_anim {
|
e.step(res.ct, res.t);
|
||||||
e.step(res.ct, res.t);
|
}
|
||||||
}
|
|
||||||
} else {
|
if !self.controls.thrust && self.last_controls.thrust {
|
||||||
for (_, e) in &mut self.engine_anim {
|
let flare = self.get_flare(res.ct);
|
||||||
e.reset(res.ct);
|
if flare.is_some() {
|
||||||
}
|
let flare_outfit = flare.unwrap();
|
||||||
|
let flare = res.ct.get_outfit(flare_outfit);
|
||||||
|
if flare.engine_flare_on_stop.is_some() {
|
||||||
|
for (_, e) in &mut self.engine_anim {
|
||||||
|
e.next_edge(flare.engine_flare_on_stop.unwrap().into());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
} else if self.controls.thrust && !self.last_controls.thrust {
|
||||||
|
let flare = self.get_flare(res.ct);
|
||||||
|
if flare.is_some() {
|
||||||
|
let flare_outfit = flare.unwrap();
|
||||||
|
let flare = res.ct.get_outfit(flare_outfit);
|
||||||
|
if flare.engine_flare_on_start.is_some() {
|
||||||
|
for (_, e) in &mut self.engine_anim {
|
||||||
|
e.next_edge(flare.engine_flare_on_start.unwrap().into());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
match self.data.get_state() {
|
match self.data.get_state() {
|
||||||
|
@ -133,6 +155,8 @@ impl PhysSimShip {
|
||||||
|
|
||||||
ShipState::Dead | ShipState::Landed { .. } => {}
|
ShipState::Dead | ShipState::Landed { .. } => {}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
self.last_controls = self.controls.clone();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Update this frame's physics
|
/// Update this frame's physics
|
||||||
|
@ -196,7 +220,7 @@ impl PhysSimShip {
|
||||||
while !a {
|
while !a {
|
||||||
x = rng.gen_range(-1.0..=1.0) * ship_content.size / 2.0;
|
x = rng.gen_range(-1.0..=1.0) * ship_content.size / 2.0;
|
||||||
y = rng.gen_range(-1.0..=1.0)
|
y = rng.gen_range(-1.0..=1.0)
|
||||||
* ship_content.size * ship_content.sprite.aspect
|
* ship_content.size * res.ct.get_sprite(ship_content.sprite).aspect
|
||||||
/ 2.0;
|
/ 2.0;
|
||||||
a = collider.shape().contains_local_point(&point![x, y]);
|
a = collider.shape().contains_local_point(&point![x, y]);
|
||||||
}
|
}
|
||||||
|
@ -221,25 +245,30 @@ impl PhysSimShip {
|
||||||
|
|
||||||
/// Public mutable
|
/// Public mutable
|
||||||
impl PhysSimShip {
|
impl PhysSimShip {
|
||||||
/// Re-create this ship's engine flare animations
|
fn get_flare(&mut self, ct: &Content) -> Option<OutfitHandle> {
|
||||||
/// Should be called whenever we change outfits
|
|
||||||
fn update_flares(&mut self, ct: &Content) {
|
|
||||||
// TODO: better way to pick flare sprite
|
// TODO: better way to pick flare sprite
|
||||||
let mut flare = None;
|
|
||||||
for (h, _) in self.data.get_outfits().iter_outfits() {
|
for (h, _) in self.data.get_outfits().iter_outfits() {
|
||||||
let c = ct.get_outfit(*h);
|
let c = ct.get_outfit(*h);
|
||||||
if c.engine_flare_sprite.is_some() {
|
if c.engine_flare_sprite.is_some() {
|
||||||
flare = c.engine_flare_sprite;
|
return Some(*h);
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
if flare.is_none() {
|
/// Re-create this ship's engine flare animations
|
||||||
|
/// Should be called whenever we change outfits
|
||||||
|
fn update_flares(&mut self, ct: &Content) {
|
||||||
|
let flare_outfit = self.get_flare(ct);
|
||||||
|
if flare_outfit.is_none() {
|
||||||
self.engine_anim = Vec::new();
|
self.engine_anim = Vec::new();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
let flare = ct
|
||||||
|
.get_outfit(flare_outfit.unwrap())
|
||||||
|
.engine_flare_sprite
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
let flare = flare.unwrap();
|
|
||||||
self.engine_anim = ct
|
self.engine_anim = ct
|
||||||
.get_ship(self.data.get_content())
|
.get_ship(self.data.get_content())
|
||||||
.engines
|
.engines
|
||||||
|
|
Loading…
Reference in New Issue