lib.rs 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183
  1. use ggez::graphics::spritebatch::{SpriteBatch, SpriteIdx};
  2. use ggez::graphics::{DrawParam, Drawable, Image};
  3. use ggez::{Context, GameError};
  4. use std::path::Path;
  5. #[derive(Eq, PartialEq, Debug, Copy, Clone)]
  6. pub enum Color {
  7. Red,
  8. Green,
  9. Blue,
  10. }
  11. impl From<Color> for ggez::graphics::Color {
  12. fn from(color: Color) -> ggez::graphics::Color {
  13. match color {
  14. Color::Red => [1.0, 0.0, 0.0, 1.0].into(),
  15. Color::Green => [0.0, 1.0, 0.0, 1.0].into(),
  16. Color::Blue => [0.0, 0.0, 1.0, 1.0].into(),
  17. }
  18. }
  19. }
  20. pub struct Board<Idx: Tile> {
  21. size: Size,
  22. contents: Vec<(Idx, SpriteIdx)>,
  23. tileset: Tileset<Idx>,
  24. }
  25. pub trait Tile: Copy {
  26. fn to_location(self) -> [f32;4];
  27. fn blank() -> Self;
  28. }
  29. impl Tile for u8 {
  30. fn to_location(self) -> [f32;4] {
  31. const TILE_SIZE: f32 = 1.0 / 16.0;
  32. let u = f32::from(self % 16) * TILE_SIZE;
  33. let v = f32::from(self / 16) * TILE_SIZE;
  34. [u, v, TILE_SIZE, TILE_SIZE]
  35. }
  36. fn blank() -> Self {
  37. 0
  38. }
  39. }
  40. pub struct Tileset<Idx: Tile> {
  41. tile_size: Size,
  42. batch: SpriteBatch,
  43. idx: std::marker::PhantomData<Idx>,
  44. }
  45. impl<Idx: Tile> Tileset<Idx> {
  46. pub fn from_file(
  47. ctx: &mut Context,
  48. tile_size: impl Into<Size>,
  49. file: impl AsRef<Path>,
  50. ) -> Result<Tileset<Idx>, GameError> {
  51. let tile_size = tile_size.into();
  52. let image = Image::new(ctx, file)?;
  53. let mut batch = SpriteBatch::new(image);
  54. batch.set_filter(ggez::graphics::FilterMode::Nearest);
  55. Ok(Tileset {
  56. tile_size,
  57. batch,
  58. idx: std::marker::PhantomData,
  59. })
  60. }
  61. fn to_screen(&self, coord: impl Into<Coord>) -> [f32; 2] {
  62. let Coord { x, y } = coord.into();
  63. [
  64. (x * self.tile_size.width) as f32,
  65. (y * self.tile_size.height) as f32,
  66. ]
  67. }
  68. }
  69. #[derive(Debug, PartialEq, Eq, Hash)]
  70. pub struct Size {
  71. width: usize,
  72. height: usize,
  73. }
  74. impl From<[usize; 2]> for Size {
  75. fn from([width, height]: [usize; 2]) -> Size {
  76. Size { width, height }
  77. }
  78. }
  79. #[derive(Debug, PartialEq, Eq, Hash)]
  80. pub struct Coord {
  81. x: usize,
  82. y: usize,
  83. }
  84. impl From<[usize; 2]> for Coord {
  85. fn from([x, y]: [usize; 2]) -> Coord {
  86. Coord { x, y }
  87. }
  88. }
  89. impl<Idx: Tile> Board<Idx> {
  90. pub fn new(size: impl Into<Size>, mut tileset: Tileset<Idx>) -> Board<Idx> {
  91. let size = size.into();
  92. let mut contents = Vec::new();
  93. for y in 0..size.height {
  94. for x in 0..size.width {
  95. let param = DrawParam::new()
  96. .src(Idx::blank().to_location().into())
  97. .dest(tileset.to_screen([x, y]));
  98. let idx = tileset.batch.add(param);
  99. contents.push((Idx::blank(), idx));
  100. }
  101. }
  102. Board {
  103. size,
  104. contents,
  105. tileset,
  106. }
  107. }
  108. // fn sprite_location(ch: u8) -> [f32; 4] {
  109. // let u = f32::from(ch % 16) * TILE_SIZE;
  110. // let v = f32::from(ch / 16) * TILE_SIZE;
  111. // [u, v, TILE_SIZE, TILE_SIZE]
  112. // }
  113. pub fn draw(&self, ctx: &mut Context) -> Result<(), ggez::GameError> {
  114. self.tileset.batch.draw(ctx, DrawParam::new())
  115. }
  116. pub fn set(&mut self, at: impl Into<Coord>, ch: Idx) {
  117. let at = at.into();
  118. let idx = at.x + at.y * self.size.width;
  119. let param = DrawParam::new()
  120. .src(ch.to_location().into())
  121. .dest(self.tileset.to_screen(at));
  122. self.tileset.batch.set(self.contents[idx].1, param).unwrap();
  123. }
  124. pub fn set_with_color(
  125. &mut self,
  126. at: impl Into<Coord>,
  127. ch: Idx,
  128. color: impl Into<ggez::graphics::Color>,
  129. ) {
  130. let at = at.into();
  131. let idx = at.x + at.y * self.size.width;
  132. let param = DrawParam::new()
  133. .src(ch.to_location().into())
  134. .dest(self.tileset.to_screen(at))
  135. .color(color.into());
  136. self.tileset.batch.set(self.contents[idx].1, param).unwrap();
  137. }
  138. pub fn get(&mut self, at: impl Into<Coord>) -> Idx {
  139. let at = at.into();
  140. let idx = at.x + at.y * self.size.width;
  141. self.contents[idx].0
  142. }
  143. pub fn clear(&mut self) {
  144. for (n, contents) in self.contents.iter_mut().enumerate() {
  145. let x = n % self.size.width;
  146. let y = n / self.size.width;
  147. let param = DrawParam::new()
  148. .src(Idx::blank().to_location().into())
  149. .dest(self.tileset.to_screen([x, y]));
  150. contents.0 = Idx::blank();
  151. self.tileset.batch.set(contents.1, param).unwrap();
  152. }
  153. }
  154. }
  155. impl Board<u8> {
  156. pub fn print(&mut self, at: impl Into<Coord>, msg: &str) {
  157. let Coord { x, y } = at.into();
  158. for (idx, ch) in msg.chars().enumerate() {
  159. self.set([idx + x, y], ch as u8);
  160. }
  161. }
  162. }