mindustry logic execution, map- and schematic- parsing and rendering
Create a counted item storage type
KosmosPrime 2023-01-23
parent 8002c27 · commit 7b0e105
-rw-r--r--src/item/mod.rs2
-rw-r--r--src/item/storage.rs395
2 files changed, 397 insertions, 0 deletions
diff --git a/src/item/mod.rs b/src/item/mod.rs
index f9ec25a..e52a7e4 100644
--- a/src/item/mod.rs
+++ b/src/item/mod.rs
@@ -1,5 +1,7 @@
use crate::content::content_enum;
+pub mod storage;
+
content_enum!
{
pub enum Type / Item for u16 | TryFromU16Error
diff --git a/src/item/storage.rs b/src/item/storage.rs
new file mode 100644
index 0000000..d78273c
--- /dev/null
+++ b/src/item/storage.rs
@@ -0,0 +1,395 @@
+use std::error::Error;
+use std::fmt;
+use std::iter::{Enumerate, FusedIterator};
+use std::slice;
+
+use crate::item;
+
+#[derive(Clone, Debug, Eq)]
+pub struct Storage
+{
+ base: Vec<u32>,
+ total: u64,
+}
+
+impl Storage
+{
+ pub const fn new() -> Self
+ {
+ Self{base: Vec::new(), total: 0}
+ }
+
+ pub fn is_empty(&self) -> bool
+ {
+ self.total == 0
+ }
+
+ pub fn get_total(&self) -> u64
+ {
+ self.total
+ }
+
+ pub fn get(&self, ty: item::Type) -> u32
+ {
+ match self.base.get(u16::from(ty) as usize)
+ {
+ None => 0,
+ Some(cnt) => *cnt,
+ }
+ }
+
+ pub fn set(&mut self, ty: item::Type, count: u32) -> u32
+ {
+ let idx = u16::from(ty) as usize;
+ match self.base.get_mut(idx)
+ {
+ None =>
+ {
+ self.base.resize(idx + 1, 0);
+ self.base[idx] = count;
+ self.total += count as u64;
+ 0
+ },
+ Some(curr) =>
+ {
+ let prev = *curr;
+ self.total = self.total - prev as u64 + count as u64;
+ *curr = count;
+ prev
+ },
+ }
+ }
+
+ pub fn add(&mut self, ty: item::Type, add: u32, max: u32) -> (u32, u32)
+ {
+ let idx = u16::from(ty) as usize;
+ match self.base.get_mut(idx)
+ {
+ None =>
+ {
+ let actual = add.min(max);
+ self.base.resize(idx + 1, 0);
+ self.base[idx] = actual;
+ self.total += add as u64;
+ (actual, actual)
+ }
+ Some(curr) =>
+ {
+ if *curr < max
+ {
+ let actual = add.min(max - *curr);
+ *curr += actual;
+ self.total += actual as u64;
+ (actual, *curr)
+ }
+ else {(0, *curr)}
+ },
+ }
+ }
+
+ pub fn try_add(&mut self, ty: item::Type, add: u32, max: u32) -> Result<(u32, u32), TryAddError>
+ {
+ let idx = u16::from(ty) as usize;
+ match self.base.get_mut(idx)
+ {
+ None =>
+ {
+ if add <= max
+ {
+ self.base.resize(idx + 1, 0);
+ self.base[idx] = add;
+ self.total += add as u64;
+ Ok((add, add))
+ }
+ else {Err(TryAddError{ty, have: 0, add, max})}
+ },
+ Some(curr) =>
+ {
+ if *curr <= max && max - *curr <= add
+ {
+ *curr += add;
+ self.total += add as u64;
+ Ok((add, *curr))
+ }
+ else {Err(TryAddError{ty, have: *curr, add, max})}
+ },
+ }
+ }
+
+ pub fn sub(&mut self, ty: item::Type, sub: u32, min: u32) -> (u32, u32)
+ {
+ match self.base.get_mut(u16::from(ty) as usize)
+ {
+ None => (0, 0),
+ Some(curr) =>
+ {
+ if *curr > min
+ {
+ let actual = sub.min(*curr - min);
+ *curr -= actual;
+ self.total -= actual as u64;
+ (actual, *curr)
+ }
+ else {(0, *curr)}
+ },
+ }
+ }
+
+ pub fn try_sub(&mut self, ty: item::Type, sub: u32, min: u32) -> Result<(u32, u32), TrySubError>
+ {
+ let idx = u16::from(ty) as usize;
+ match self.base.get_mut(idx)
+ {
+ None => Err(TrySubError{ty, have: 0, sub, min}),
+ Some(curr) =>
+ {
+ if *curr >= min && *curr - min >= sub
+ {
+ *curr -= sub;
+ self.total -= sub as u64;
+ Ok((sub, *curr))
+ }
+ else {Err(TrySubError{ty, have: *curr, sub, min})}
+ },
+ }
+ }
+
+ pub fn add_all(&mut self, other: &Storage, max_each: u32) -> (u64, u64)
+ {
+ let mut added = 0u64;
+ if max_each > 0 && other.get_total() > 0
+ {
+ let mut iter = other.base.iter().enumerate();
+ // resize our vector only once and if necessary
+ let (last, add_last) = iter.rfind(|(_, n)| **n != 0).unwrap();
+ if self.base.len() <= last
+ {
+ self.base.resize(last + 1, 0);
+ }
+ // process items by increasing ID
+ for (idx, add) in iter
+ {
+ let curr = self.base[idx];
+ if curr < max_each
+ {
+ let actual = (*add).min(max_each - curr);
+ self.base[idx] += actual;
+ added += actual as u64;
+ }
+ }
+ // process the final element (which we've retrieved first)
+ let curr = self.base[last];
+ if curr < max_each
+ {
+ let actual = (*add_last).min(max_each - curr);
+ self.base[last] += actual;
+ added += actual as u64;
+ }
+ // update total
+ self.total += added;
+ }
+ (added, self.total)
+ }
+
+ pub fn pull_all(&mut self, other: &mut Storage, max_each: u32) -> (u64, u64, u64)
+ {
+ let mut added = 0u64;
+ if max_each > 0 && other.get_total() > 0
+ {
+ let mut iter = other.base.iter_mut().enumerate();
+ // resize our vector only once and if necessary
+ let (last, add_last) = iter.rfind(|(_, n)| **n != 0).unwrap();
+ if self.base.len() <= last
+ {
+ self.base.resize(last + 1, 0);
+ }
+ // process items by increasing ID
+ for (idx, add) in iter
+ {
+ let curr = self.base[idx];
+ if curr < max_each
+ {
+ let actual = (*add).min(max_each - curr);
+ self.base[idx] += actual;
+ *add -= actual;
+ added += actual as u64;
+ }
+ }
+ // process the final element (which we've retrieved first)
+ let curr = self.base[last];
+ if curr < max_each
+ {
+ let actual = (*add_last).min(max_each - curr);
+ self.base[last] += actual;
+ *add_last -= actual;
+ added += actual as u64;
+ }
+ // update totals
+ self.total += added;
+ other.total -= added;
+ }
+ (added, self.total, other.total)
+ }
+
+ pub fn sub_all(&mut self, other: &Storage, min_each: u32) -> (u64, u64)
+ {
+ let mut subbed = 0u64;
+ if self.get_total() > 0 && other.get_total() > 0
+ {
+ // no need for resizing, we only remove
+ // process items by increasing ID
+ for (idx, sub) in other.base.iter().enumerate()
+ {
+ if let Some(curr) = self.base.get(idx)
+ {
+ if *curr > min_each
+ {
+ let actual = (*sub).min(*curr - min_each);
+ self.base[idx] -= actual;
+ subbed += actual as u64;
+ }
+ }
+ else {break;}
+ }
+ // update total
+ self.total -= subbed;
+ }
+ (subbed, self.total)
+ }
+
+ pub fn diff_all(&mut self, other: &mut Storage, min_each: u32) -> (u64, u64, u64)
+ {
+ let mut subbed = 0u64;
+ if self.get_total() > 0 && other.get_total() > 0
+ {
+ // no need for resizing, we only remove
+ // consider only indexes present in both
+ let end = self.base.len().min(other.base.len());
+ let lhs = &mut self.base[..end];
+ let rhs = &mut other.base[..end];
+ // process items by increasing ID
+ for (l, r) in lhs.into_iter().zip(rhs)
+ {
+ if *l > min_each && *r > min_each
+ {
+ let actual = (*l - min_each).min(*r - min_each);
+ *l -= actual;
+ *r -= actual;
+ subbed -= actual as u64;
+ }
+ }
+ // update totals
+ self.total -= subbed;
+ other.total -= subbed;
+ }
+ (subbed, self.total, other.total)
+ }
+
+ pub fn iter<'s>(&'s self) -> Iter<'s>
+ {
+ Iter{base: self.base.iter().enumerate(), all: true}
+ }
+
+ pub fn iter_nonzero<'s>(&'s self) -> Iter<'s>
+ {
+ Iter{base: self.base.iter().enumerate(), all: false}
+ }
+
+ pub fn clear(&mut self)
+ {
+ self.base.clear()
+ }
+}
+
+// manual because padding with zeros doesn't affect equality
+impl PartialEq for Storage
+{
+ fn eq(&self, other: &Self) -> bool
+ {
+ let mut li = self.base.iter().fuse();
+ let mut ri = other.base.iter().fuse();
+ loop
+ {
+ match (li.next(), ri.next())
+ {
+ (None, None) => return true,
+ (l, r) =>
+ {
+ if l.unwrap_or(&0) != r.unwrap_or(&0) {return false;}
+ },
+ }
+ }
+ }
+}
+
+#[derive(Clone, Copy, Debug, Eq, PartialEq)]
+pub struct TryAddError
+{
+ pub ty: item::Type,
+ pub have: u32,
+ pub add: u32,
+ pub max: u32
+}
+
+impl fmt::Display for TryAddError
+{
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result
+ {
+ write!(f, "adding {} {:?} to current {} would exceed {}", self.add, self.ty, self.have, self.max)
+ }
+}
+
+impl Error for TryAddError {}
+
+#[derive(Clone, Copy, Debug, Eq, PartialEq)]
+pub struct TrySubError
+{
+ pub ty: item::Type,
+ pub have: u32,
+ pub sub: u32,
+ pub min: u32
+}
+
+impl fmt::Display for TrySubError
+{
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result
+ {
+ write!(f, "removing {} {:?} from current {} would drop below {}", self.sub, self.ty, self.have, self.min)
+ }
+}
+
+impl Error for TrySubError {}
+
+#[derive(Clone, Debug)]
+pub struct Iter<'l>
+{
+ base: Enumerate<slice::Iter<'l, u32>>,
+ all: bool,
+}
+
+impl<'l> Iterator for Iter<'l>
+{
+ type Item = (item::Type, u32);
+
+ fn next(&mut self) -> Option<Self::Item>
+ {
+ while let Some((idx, cnt)) = self.base.next()
+ {
+ if *cnt > 0 || self.all
+ {
+ if let Ok(ty) = item::Type::try_from(idx as u16)
+ {
+ return Some((ty, *cnt));
+ }
+ }
+ }
+ None
+ }
+
+ fn size_hint(&self) -> (usize, Option<usize>)
+ {
+ (0, self.base.size_hint().1)
+ }
+}
+
+impl<'l> FusedIterator for Iter<'l> where Enumerate<slice::Iter<'l, u32>>: FusedIterator {}