1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
#![doc(html_root_url = "https://docs.rs/inv-sys/0.1.2")]

#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
pub struct Inv<T> {
	slots: Vec<Option<(T, usize)>>,
	maxslots: usize,
	selected_slot: usize
}

pub trait Stacksize {
	fn get_max_stacksize(&self) -> usize;
}

impl<T> Inv<T> where 
T: Stacksize + Eq + Clone {
	pub fn new(maxslots: usize) -> Self {
		Inv {
			slots: vec![None; maxslots],
			maxslots: maxslots,
			selected_slot: 0
		}
	}

	/// Returns an Optional Item for the amount of an item, that could not be placed
	pub fn place_at(&mut self, item: (T, usize), slot: usize) -> Option<(T, usize)> {
		if self.slot_valid(slot) {
			let item_at_spot = self.slots.get_mut(slot).unwrap().clone();
			return match item_at_spot {
				Some(ias) => {
					let new_item = self.split_item_for_stacksize((item.0, item.1 + ias.1));
					let ptr = self.slots.get_mut(slot).unwrap();
					*ptr = new_item[0].clone();
					new_item[1].clone()
				},
				None => {
					let new_item = self.split_item_for_stacksize(item);
					let ptr = self.slots.get_mut(slot).unwrap();
					*ptr = new_item[0].clone();
					new_item[1].clone()
				}
			}
		}
		Some(item)
	}

	/// Splits an item, so that the first one is safe to store in the inventory, 
	/// and the second is able to be processed by the user, if there is need
	fn split_item_for_stacksize(&self, item: (T, usize)) -> [Option<(T, usize)>; 2] {
		//check if item is over the stacksize
		if item.1 == 0 {
			return [None, None];
		}

		let max_stacksize = item.0.get_max_stacksize();
		if item.1 > max_stacksize {
			return [Some((item.0.clone(), max_stacksize)), Some((item.0, item.1 - max_stacksize))];
		}

		return [Some(item), None];
	}

	/// Tries to stack the item, returns remaining item.assert_eq!
	/// If no item is found, that it can be stacked on, the place_at_first_free method is used
	pub fn stack(&mut self, item: (T, usize)) -> Option<(T, usize)> {
		for (i, slot) in self.slots.iter_mut().enumerate() {
			match slot {
				None => continue,
				Some(slotval) => {
					//if items are equal and slot not full (allow overflow)
					if slotval.0 == item.0 
					&& slotval.1 < slotval.0.get_max_stacksize() {
						let rest = self.place_at(item, i);
						if rest != None && self.can_be_filled_with(rest.clone().unwrap()) {
							return self.stack(rest.unwrap());
						}
						else {
							return rest;
						}
					}
				}
			}
		}
		//no item found => try to use first free spot
		self.place_at_first_free(item)
	}

	/// If there still can be filled some slot with a minimum amount of 1, this should return true
	pub fn can_be_filled_with(&self, item: (T, usize)) -> bool {
		for slot in self.slots.iter() {
			let x = match slot {
				None => true,
				Some(slotval) => slotval.0 == item.0 && slotval.1 < slotval.0.get_max_stacksize()
			};
			if x {
				return true;
			}
		}
		false
	}

	/// Try to place the item at the first free spot, returns the remaining item
	pub fn place_at_first_free(&mut self, item: (T, usize)) -> Option<(T, usize)> {
		for (i, slot) in self.slots.iter_mut().enumerate() {
			match slot {
				None => {
					let rest = self.place_at(item, i);
					if rest != None && self.can_be_filled_with(rest.clone().unwrap()) {
						return self.place_at_first_free(rest.unwrap());
					}
					else {
						return rest;
					}
				},
				_ => continue
			}
		}
		Some(item)
	}

	/// Get item of a specific slot position
	pub fn get_slot(&self, slot: usize) -> Option<&(T, usize)> {
		self.slots.get(slot).unwrap().as_ref()
	}

	/// Get item of the selected slot position
	pub fn get_selected_slot(&self) -> Option<&(T, usize)> {
		self.slots.get(self.selected_slot).unwrap().as_ref()
	}

	/// Set the selected slot
	pub fn set_selected_slot(&mut self, slot: usize) -> bool {
		return if self.slot_valid(slot) {
			self.selected_slot = slot;
			true
		} else {
			false
		}
	}

	/// Decreases the amount of the item in the selected slot by 1
	pub fn decrease_selected_slot(&mut self) -> bool {
		let item = self.slots.get_mut(self.selected_slot).unwrap();
		match item {
			None => false,
			Some(x) => {
				x.1 -= 1;
				if x.1 == 0 {
					*item = None
				}
				true
			} 
		}
	}

	//checks if a slot would be valid in this inventory
	fn slot_valid(&self, slot: usize) -> bool {
		slot < self.maxslots
	}
}

#[cfg(test)]
mod test;