You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
716 lines
22 KiB
716 lines
22 KiB
#![allow(clippy::upper_case_acronyms)]
|
|
#![allow(clippy::new_without_default)]
|
|
use macroquad::prelude::*;
|
|
use macroquad::rand::*;
|
|
|
|
pub mod grammar;
|
|
use grammar::*;
|
|
|
|
const FONT: &str = &"./assets/fira_code.ttf";
|
|
const FONT_WIDTH: u16 = 8;
|
|
const FONT_HEIGHT_REAL: f32 = FONT_WIDTH as f32 / 8.0 * 13.0;
|
|
const FONT_HEIGHT: u16 = (FONT_HEIGHT_REAL + 4.0) as u16;
|
|
const FONT_DIFFERENCE: u16 = FONT_HEIGHT - FONT_HEIGHT_REAL as u16;
|
|
|
|
const GRID_WIDTH: usize = 60;
|
|
const GRID_HEIGHT: usize = 25;
|
|
|
|
// 9 = 3 + height of fishing guy - 1 for some reason
|
|
const WATER_LEVEL: i32 = 9;
|
|
const HEIGHT: i32 = FONT_HEIGHT as i32 * 25;
|
|
// 90 = 60 + 30
|
|
const WIDTH: i32 = FONT_WIDTH as i32 * 90;
|
|
|
|
const UPDATE: f64 = 150.0 / 1000.0; // in seconds
|
|
const ANIM_CHANGE: f64 = 750.0 / 1000.0;
|
|
|
|
fn window_conf() -> Conf {
|
|
Conf {
|
|
window_title: "fishing minigame".to_owned(),
|
|
window_width: WIDTH,
|
|
window_height: HEIGHT,
|
|
fullscreen: false,
|
|
..Default::default()
|
|
}
|
|
}
|
|
|
|
struct Fish {
|
|
is_right: bool,
|
|
// middle length
|
|
length: u8,
|
|
pos: IVec2,
|
|
}
|
|
|
|
impl Fish {
|
|
fn new() -> Self {
|
|
let is_right = gen_range(0, 2) == 0;
|
|
let length = gen_range(0, 6);
|
|
Self {
|
|
is_right,
|
|
length,
|
|
pos: IVec2::new(
|
|
if is_right {
|
|
-(length as i32 + 3)
|
|
} else {
|
|
GRID_WIDTH as i32
|
|
},
|
|
gen_range(WATER_LEVEL + 1, GRID_HEIGHT as i32 - 2),
|
|
),
|
|
}
|
|
}
|
|
|
|
fn into_text(self, grammar: &Grammar) -> Option<String> {
|
|
grammar.flatten()
|
|
}
|
|
}
|
|
|
|
enum Mode {
|
|
Start,
|
|
Play,
|
|
End,
|
|
}
|
|
|
|
struct Game {
|
|
mode: Mode,
|
|
state: State,
|
|
draw: Draw,
|
|
grammar: Grammar,
|
|
}
|
|
|
|
impl Game {
|
|
async fn new() -> Result<Self, FontError> {
|
|
let mut draw = Draw::new().await.unwrap();
|
|
let mut state = State::new();
|
|
game_setup(&mut state, &mut draw);
|
|
let grammar = grammar_setup();
|
|
|
|
Ok(Self {
|
|
mode: Mode::Start,
|
|
state,
|
|
draw,
|
|
grammar,
|
|
})
|
|
}
|
|
|
|
fn update(&mut self) {
|
|
// fish update
|
|
let mut remove = Vec::new();
|
|
for (i, fish) in self.state.fish.iter_mut().enumerate() {
|
|
// if something is hooked and it's not this
|
|
let is_hooked = self.state.is_hooked.map(|idx| idx == i).unwrap_or(false);
|
|
if !is_hooked {
|
|
if fish.is_right {
|
|
fish.pos.x += 1;
|
|
if fish.pos.x >= GRID_WIDTH as i32 {
|
|
remove.push(i);
|
|
}
|
|
} else {
|
|
fish.pos.x -= 1;
|
|
if fish.pos.x <= -(fish.length as i32 + 3) {
|
|
remove.push(i);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
for i in remove.into_iter().rev() {
|
|
self.state.fish.remove(i);
|
|
if let Some(idx) = &mut self.state.is_hooked {
|
|
if *idx > i {
|
|
*idx -= 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
if self.state.fish.len() < 3 && gen_range(0, 15) < 1 {
|
|
self.state.fish.push(Fish::new());
|
|
}
|
|
|
|
// collision
|
|
let hook = self.state.hook;
|
|
if self.state.is_hooked.is_none() {
|
|
for (i, fish) in self.state.fish.iter().enumerate() {
|
|
if fish.pos.y == hook.y
|
|
&& fish.pos.x <= hook.x
|
|
&& fish.pos.x + fish.length as i32 + 3 > hook.x
|
|
{
|
|
self.state.is_hooked = Some(i);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
struct Draw {
|
|
// only two frames of animation
|
|
first_frame: bool,
|
|
// animated sprites
|
|
animations: Vec<Animation>,
|
|
// static sprites
|
|
sprites: Vec<Sprite>,
|
|
font: Font,
|
|
}
|
|
|
|
impl Draw {
|
|
async fn new() -> Result<Self, FontError> {
|
|
// font: fira code. dimensions: 13.0x8.0
|
|
let font = load_ttf_font(FONT).await?;
|
|
Ok(Self {
|
|
first_frame: true,
|
|
animations: Vec::new(),
|
|
sprites: Vec::new(),
|
|
font,
|
|
})
|
|
}
|
|
|
|
fn add_sprite(&mut self, sprite: Sprite) -> usize {
|
|
self.sprites.push(sprite);
|
|
self.sprites.len() - 1
|
|
}
|
|
|
|
fn add_animation(&mut self, animation: Animation) -> usize {
|
|
self.animations.push(animation);
|
|
self.animations.len() - 1
|
|
}
|
|
}
|
|
|
|
struct Sprite {
|
|
width: usize,
|
|
height: usize,
|
|
color: Option<Color>,
|
|
sprite: String,
|
|
}
|
|
|
|
impl Sprite {
|
|
fn new(width: usize, height: usize, color: Option<Color>, sprite: String) -> Self {
|
|
assert_eq!(width * height, sprite.len() - height + 1);
|
|
Self {
|
|
width,
|
|
height,
|
|
color,
|
|
sprite,
|
|
}
|
|
}
|
|
|
|
fn draw(&self, pos: IVec2, font: Font) {
|
|
let pos = pos.as_f32() * Vec2::new(FONT_WIDTH as f32, FONT_HEIGHT as f32);
|
|
if let Some(color) = self.color {
|
|
draw_rectangle(
|
|
pos.x,
|
|
pos.y,
|
|
self.width as f32 * FONT_WIDTH as f32,
|
|
self.height as f32 * FONT_HEIGHT as f32,
|
|
color,
|
|
);
|
|
}
|
|
for (i, row) in self.sprite.split('\n').enumerate() {
|
|
let i = (i + 1) as f32 * FONT_HEIGHT as f32 - FONT_DIFFERENCE as f32;
|
|
draw_text_ex(
|
|
row,
|
|
pos.x,
|
|
pos.y + i,
|
|
TextParams {
|
|
font,
|
|
color: BLACK,
|
|
font_size: FONT_HEIGHT - FONT_DIFFERENCE,
|
|
..Default::default()
|
|
},
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|
|
struct Animation {
|
|
width: usize,
|
|
height: usize,
|
|
color: Color,
|
|
/// animations can have two frames and must be strings
|
|
frames: [String; 2],
|
|
}
|
|
|
|
impl Animation {
|
|
fn new(width: usize, height: usize, color: Color, frames: [String; 2]) -> Self {
|
|
assert_eq!(width * height, frames[0].len() - height + 1);
|
|
assert_eq!(width * height, frames[1].len() - height + 1);
|
|
Self {
|
|
width,
|
|
height,
|
|
color,
|
|
frames,
|
|
}
|
|
}
|
|
|
|
fn draw(&self, pos: IVec2, first_frame: bool, font: Font) {
|
|
let pos = pos.as_f32() * Vec2::new(FONT_WIDTH as f32, FONT_HEIGHT as f32);
|
|
draw_rectangle(
|
|
pos.x,
|
|
pos.y,
|
|
self.width as f32 * FONT_WIDTH as f32,
|
|
self.height as f32 * FONT_HEIGHT as f32,
|
|
self.color,
|
|
);
|
|
for (i, row) in self.frames[first_frame as usize].split('\n').enumerate() {
|
|
let i = (i + 1) as f32 * FONT_HEIGHT as f32 - FONT_DIFFERENCE as f32;
|
|
draw_text_ex(
|
|
row,
|
|
pos.x,
|
|
pos.y + i,
|
|
TextParams {
|
|
font,
|
|
color: BLACK,
|
|
font_size: FONT_HEIGHT - FONT_DIFFERENCE,
|
|
..Default::default()
|
|
},
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|
|
struct State {
|
|
// TODO: consider vecdeque instead of stack
|
|
fish: Vec<Fish>,
|
|
hook: IVec2,
|
|
// which fish is hooked
|
|
is_hooked: Option<usize>,
|
|
// positions of other things (unmoving)
|
|
positions: Vec<IVec2>,
|
|
// parallel vec of sprites corresponding to ^
|
|
sprites: Vec<DrawType>,
|
|
caught_fish: Vec<String>,
|
|
}
|
|
|
|
enum DrawType {
|
|
Anim(usize),
|
|
Sprite(usize),
|
|
}
|
|
|
|
impl State {
|
|
fn new() -> Self {
|
|
State {
|
|
fish: Vec::new(),
|
|
hook: IVec2::new(GRID_WIDTH as i32 / 2, WATER_LEVEL + 5),
|
|
is_hooked: None,
|
|
positions: Vec::new(),
|
|
sprites: Vec::new(),
|
|
caught_fish: Vec::new(),
|
|
}
|
|
}
|
|
}
|
|
|
|
#[macroquad::main(window_conf)]
|
|
async fn main() {
|
|
srand(get_time().to_bits());
|
|
let mut game = Game::new().await.unwrap();
|
|
|
|
let mut updated = get_time();
|
|
let mut anim_updated = get_time();
|
|
|
|
loop {
|
|
match game.mode {
|
|
Mode::Start => {
|
|
// draw
|
|
clear_background(SKYBLUE);
|
|
draw_text_centered(
|
|
&"press ENTER to start",
|
|
HEIGHT as f32 / 2.0 - FONT_HEIGHT as f32 * 2.0,
|
|
game.draw.font,
|
|
);
|
|
draw_text_centered(
|
|
&"use up and down to move the hook",
|
|
HEIGHT as f32 / 2.0 - FONT_HEIGHT as f32,
|
|
game.draw.font,
|
|
);
|
|
draw_text_centered(
|
|
&"use space to catch a fish",
|
|
HEIGHT as f32 / 2.0,
|
|
game.draw.font,
|
|
);
|
|
draw_text_centered(
|
|
&"use escape to end the game",
|
|
HEIGHT as f32 / 2.0 + FONT_HEIGHT as f32,
|
|
game.draw.font,
|
|
);
|
|
if is_key_pressed(KeyCode::Enter) {
|
|
game.mode = Mode::Play;
|
|
}
|
|
}
|
|
Mode::Play => {
|
|
if is_key_pressed(KeyCode::Escape) {
|
|
game.mode = Mode::End;
|
|
continue;
|
|
}
|
|
if is_key_pressed(KeyCode::Up) {
|
|
game.state.hook.y = (game.state.hook.y - 1).max(WATER_LEVEL);
|
|
}
|
|
if is_key_pressed(KeyCode::Down) {
|
|
game.state.hook.y = (game.state.hook.y + 1).min(GRID_HEIGHT as i32 - 2);
|
|
}
|
|
|
|
if let Some(idx) = game.state.is_hooked {
|
|
game.state.fish[idx].pos.y = game.state.hook.y;
|
|
|
|
if is_key_pressed(KeyCode::Space) {
|
|
let fish = game.state.fish.remove(idx);
|
|
game.state
|
|
.caught_fish
|
|
.push(fish.into_text(&game.grammar).unwrap());
|
|
game.state.is_hooked = None;
|
|
}
|
|
}
|
|
|
|
if get_time() - updated >= UPDATE {
|
|
game.update();
|
|
updated = get_time();
|
|
}
|
|
game.draw();
|
|
if get_time() - anim_updated >= ANIM_CHANGE {
|
|
game.draw.first_frame = !game.draw.first_frame;
|
|
anim_updated = get_time();
|
|
}
|
|
}
|
|
Mode::End => {
|
|
clear_background(SKYBLUE);
|
|
draw_text_centered(
|
|
&format!(
|
|
"you caught {} fish, thanks for playing",
|
|
game.state.caught_fish.len()
|
|
),
|
|
HEIGHT as f32 / 2.0,
|
|
game.draw.font,
|
|
);
|
|
draw_text_centered(
|
|
&"press ENTER to restart",
|
|
HEIGHT as f32 / 2.0 + FONT_HEIGHT as f32,
|
|
game.draw.font,
|
|
);
|
|
if is_key_pressed(KeyCode::Enter) {
|
|
game.state = State::new();
|
|
game.draw.animations.clear();
|
|
game.draw.sprites.clear();
|
|
game_setup(&mut game.state, &mut game.draw);
|
|
game.mode = Mode::Start;
|
|
}
|
|
}
|
|
}
|
|
next_frame().await;
|
|
}
|
|
}
|
|
|
|
impl Game {
|
|
fn draw(&self) {
|
|
const FISH_WIDTH: f32 = FONT_WIDTH as f32 * GRID_WIDTH as f32;
|
|
const H: f32 = HEIGHT as f32;
|
|
const WL: f32 = WATER_LEVEL as f32 * FONT_HEIGHT as f32;
|
|
// water
|
|
clear_background(SKYBLUE);
|
|
// sky
|
|
draw_rectangle(0.0, 0.0, FISH_WIDTH, WL, WHITE);
|
|
// ground
|
|
draw_rectangle(
|
|
0.0,
|
|
H - FONT_HEIGHT as f32,
|
|
FISH_WIDTH,
|
|
FONT_HEIGHT as f32,
|
|
BEIGE,
|
|
);
|
|
|
|
// static sprites
|
|
for (pos, sprite) in self.state.positions.iter().zip(self.state.sprites.iter()) {
|
|
match sprite {
|
|
DrawType::Anim(i) => {
|
|
self.draw.animations[*i].draw(*pos, self.draw.first_frame, self.draw.font)
|
|
}
|
|
DrawType::Sprite(i) => self.draw.sprites[*i].draw(*pos, self.draw.font),
|
|
}
|
|
}
|
|
|
|
// fish
|
|
for fish in self.state.fish.iter() {
|
|
let sprite = &self.draw.sprites[fish.length as usize * 2 + fish.is_right as usize];
|
|
sprite.draw(fish.pos, self.draw.font);
|
|
}
|
|
// hook
|
|
self.draw.sprites[12].draw(self.state.hook, self.draw.font);
|
|
|
|
// line
|
|
let sprite = (0..self.state.hook.y - 5)
|
|
.map(|_| "|\n")
|
|
.collect::<String>();
|
|
let sprite = sprite.trim().to_string();
|
|
let sprite = Sprite::new(1, self.state.hook.y as usize - 5, None, sprite);
|
|
sprite.draw(IVec2::new(GRID_WIDTH as i32 / 2, 5), self.draw.font);
|
|
|
|
// sidebar
|
|
draw_rectangle(FISH_WIDTH, 0.0, WIDTH as f32 - FISH_WIDTH, H, LIGHTGRAY);
|
|
|
|
// draw fish info
|
|
// sidebar width: 480 - 8px? margins * 2 = 464 / 16 = 29 characters / line
|
|
let mut y = 30.0;
|
|
let x = GRID_WIDTH as f32 * FONT_WIDTH as f32 + 8.0;
|
|
|
|
for text in self.state.caught_fish.iter().rev() {
|
|
if y > HEIGHT as f32 + FONT_HEIGHT as f32 {
|
|
break;
|
|
}
|
|
y += 20.0;
|
|
let mut line = "".to_string();
|
|
line.reserve_exact(28);
|
|
for word in text.split_whitespace() {
|
|
if line.len() + word.len() > 28 {
|
|
draw_text_ex(
|
|
&line,
|
|
x,
|
|
y,
|
|
TextParams {
|
|
font: self.draw.font,
|
|
color: BLACK,
|
|
font_size: FONT_HEIGHT - FONT_DIFFERENCE,
|
|
..Default::default()
|
|
},
|
|
);
|
|
line.clear();
|
|
y += FONT_HEIGHT as f32;
|
|
}
|
|
line += word;
|
|
line += " ";
|
|
}
|
|
|
|
draw_text_ex(
|
|
&line,
|
|
x,
|
|
y,
|
|
TextParams {
|
|
font: self.draw.font,
|
|
color: BLACK,
|
|
font_size: FONT_HEIGHT - FONT_DIFFERENCE,
|
|
..Default::default()
|
|
},
|
|
);
|
|
y += FONT_HEIGHT as f32;
|
|
}
|
|
}
|
|
}
|
|
|
|
fn grammar_setup() -> Grammar {
|
|
let rules = [
|
|
("origin".to_string(),
|
|
vec!["#fish#. #fishFact#".to_string(),
|
|
"#fish#. #fishFact#".to_string(),
|
|
"#fish#. #fishFact#".to_string(),
|
|
"#fish#. #fishFact#".to_string(),
|
|
"#fish#. #fishFact#".to_string(),
|
|
"#fish#. #fishFact#".to_string(),
|
|
"#fish#. #fishFact#".to_string(),
|
|
"#fish#. #fishFact#".to_string(),
|
|
"#fish#. #fishFact#".to_string(),
|
|
"#seaFish#. #seaFishFact#".to_string(),
|
|
"#largeFish#. #largeFishFact#".to_string(),
|
|
"#whale#. #whaleFact#".to_string(),
|
|
"#unnervingFish#. #unnervingFishFact#".to_string(),
|
|
"sea bass. no, wait--- it's at least a c+!".to_string()]),
|
|
|
|
("fish".to_string(),
|
|
vec!["rainbow trout".to_string(),
|
|
"salmon".to_string(),
|
|
"black bass".to_string(),
|
|
"catfish".to_string(),
|
|
"brook trout".to_string(),
|
|
"cod".to_string(),
|
|
"minnow".to_string(),
|
|
"turbofish".to_string()]),
|
|
|
|
("fishFact".to_string(),
|
|
vec!["this fish loves to swim about.".to_string(),
|
|
"this fish was sleeping and you woke it up, which is kind of rude.".to_string(),
|
|
"this fish will grant you three wishes if you don't eat it but you weren't going to do that anyway.".to_string(),
|
|
"fish are neat :)".to_string(),
|
|
"this fish likes to hide under rocks.".to_string(),
|
|
"this fish can breathe underwater.".to_string(),
|
|
"this fish likes to blow bubbles.".to_string(),
|
|
"this fish likes to flick its tail.".to_string(),
|
|
"this fish can do a backflip.".to_string(),
|
|
"this fish is very tired and wants to rest.".to_string(),
|
|
"this fish wants to show you some sick card tricks.".to_string()]),
|
|
|
|
("unnervingFish".to_string(),
|
|
vec!["swamp eel".to_string(),
|
|
"pike".to_string(),
|
|
"jellyfish".to_string(),
|
|
"sunfish".to_string(),
|
|
"angler fish".to_string(),
|
|
"squid".to_string()]),
|
|
|
|
("unnervingFishFact".to_string(),
|
|
vec!["#fishFact#".to_string(),
|
|
"i'm spooked by this fish.".to_string(),
|
|
"this fish has so many teeth.".to_string(),
|
|
"you feel like this fish will haunt your nightmares.".to_string(),
|
|
"please put it back.".to_string()]),
|
|
|
|
("largeFish".to_string(),
|
|
vec!["lemon shark".to_string(),
|
|
"mako shark".to_string(),
|
|
"whale shark".to_string(),
|
|
"great white shark".to_string()]),
|
|
|
|
("largeFishFact".to_string(),
|
|
vec!["#seaFishFact#".to_string(),
|
|
"#fishFact#".to_string(),
|
|
"this fish is incredibly large.".to_string(),
|
|
"OH GOD IT'S JUST SO BIG".to_string(),
|
|
"this fish might eat you? you're not sure, it hasn't happened yet".to_string(),
|
|
"this fish has so many teeth.".to_string()]),
|
|
|
|
("whale".to_string(),
|
|
vec!["blue whale".to_string(),
|
|
"humpback whale".to_string(),
|
|
"sperm whale".to_string(),
|
|
"right whale".to_string(),
|
|
"razor back whale".to_string()]),
|
|
|
|
("whaleFact".to_string(),
|
|
vec!["#seaFishFact#".to_string(),
|
|
"#largeFishFact#".to_string(),
|
|
"\"Oh, man! Admire and model thyself after the whale! Do thou, too, remain warm among ice. Do thou, too, live in this world without being of it. Be cool at the equator; keep thy blood fluid at the pole. Like the great dome of St. Peter's, and like the great whale, retain, O man! in all seasons a temperature of thine own.\" (melville 343)".to_string(),
|
|
"\"I. A Fast-Fish belongs to the party fast to it. II. A Loose-Fish is fair game for anybody who can soonest catch it.\" (melville 441)".to_string(),
|
|
"\"All men live enveloped in whale lines.\" (melville 315)".to_string(),
|
|
"\"Let him go. I know little more of him,
|
|
nor does anybody else.\" (melville 153)".to_string(),
|
|
"\"a whale is A SPOUTING FISH WITH A HORIZONTAL TAIL. There you have him.\" (melville 148)".to_string()]),
|
|
|
|
("seaFish".to_string(),
|
|
vec!["black sea bass".to_string(),
|
|
"mackerel".to_string(),
|
|
"tuna".to_string(),
|
|
"sea bass".to_string(),
|
|
"anchovy".to_string()]),
|
|
|
|
("seaFishFact".to_string(),
|
|
vec!["#fishFact#".to_string(),
|
|
"this fish lives in the sea and you're not sure how it got here.".to_string(),
|
|
"you're not even sure if this fish can live in freshwater, but okay.".to_string()])
|
|
];
|
|
Grammar::from(rules)
|
|
}
|
|
|
|
fn game_setup(state: &mut State, draw: &mut Draw) {
|
|
// fish
|
|
for i in 0..6 {
|
|
let mut middle = "".to_string();
|
|
middle.reserve_exact(i);
|
|
for _ in 0..i {
|
|
middle += ":";
|
|
}
|
|
draw.add_sprite(Sprite::new(
|
|
i + 3,
|
|
1,
|
|
Some(SKYBLUE),
|
|
"<".to_owned() + &middle.clone() + "><",
|
|
));
|
|
draw.add_sprite(Sprite::new(
|
|
i + 3,
|
|
1,
|
|
Some(SKYBLUE),
|
|
"><".to_owned() + &middle + ">",
|
|
));
|
|
}
|
|
|
|
// hook
|
|
draw.add_sprite(Sprite::new(1, 1, Some(SKYBLUE), "&".to_owned()));
|
|
|
|
// seaweed
|
|
let mut plant_already = [false; GRID_WIDTH];
|
|
for _ in 0..5 {
|
|
let mut x = gen_range(1, GRID_WIDTH - 1);
|
|
while plant_already[x] {
|
|
x = gen_range(1, GRID_WIDTH - 1);
|
|
}
|
|
plant_already[x] = true;
|
|
let height = gen_range(2, (GRID_HEIGHT - WATER_LEVEL as usize) / 2);
|
|
let mut plant = "".to_string();
|
|
plant.reserve_exact(height + 2);
|
|
for _ in 0..height / 2 + 1 {
|
|
plant += "\\\n/\n";
|
|
}
|
|
let start = gen_range(0, 2) == 0;
|
|
|
|
let anim = if start {
|
|
[
|
|
plant[..height * 2 - 1].to_string(),
|
|
plant[2..height * 2 + 1].to_string(),
|
|
]
|
|
} else {
|
|
[
|
|
plant[2..height * 2 + 1].to_string(),
|
|
plant[..height * 2 - 1].to_string(),
|
|
]
|
|
};
|
|
let anim = Animation::new(1, height, GREEN, anim);
|
|
let i = draw.add_animation(anim);
|
|
state
|
|
.positions
|
|
.push(IVec2::new(x as i32, GRID_HEIGHT as i32 - height as i32 - 1));
|
|
state.sprites.push(DrawType::Anim(i));
|
|
}
|
|
|
|
// water
|
|
let mut water = "^".to_string();
|
|
water.reserve_exact(GRID_WIDTH);
|
|
for _ in 0..GRID_WIDTH / 2 {
|
|
water += "~^";
|
|
}
|
|
let anim = Animation::new(
|
|
GRID_WIDTH,
|
|
1,
|
|
SKYBLUE,
|
|
[water[..GRID_WIDTH].to_string(), water[1..].to_string()],
|
|
);
|
|
let i = draw.add_animation(anim);
|
|
state.positions.push(IVec2::new(0, WATER_LEVEL));
|
|
state.sprites.push(DrawType::Anim(i));
|
|
|
|
// ground
|
|
let groud_chars = [
|
|
"-", "=", ".", "__", "--", "==", "..", "___", "---", "===", "...",
|
|
];
|
|
let mut ground = "".to_string();
|
|
let mut i = 0;
|
|
while i < GRID_WIDTH {
|
|
let add = groud_chars[gen_range(0, groud_chars.len())];
|
|
i += add.len();
|
|
ground += add;
|
|
}
|
|
|
|
ground = ground[..GRID_WIDTH].to_string();
|
|
let i = draw.add_sprite(Sprite::new(GRID_WIDTH, 1, Some(BEIGE), ground));
|
|
state.positions.push(IVec2::new(0, GRID_HEIGHT as i32 - 1));
|
|
state.sprites.push(DrawType::Sprite(i));
|
|
|
|
// water
|
|
// dude
|
|
#[rustfmt::skip]
|
|
let dude = r#" O
|
|
|--o_______________________.
|
|
|__
|
|
===== |
|
|
| | |
|
|
| | "#;
|
|
let i = draw.add_sprite(Sprite::new(31, 6, None, dude.to_string()));
|
|
state.positions.push(IVec2::new(0, 3));
|
|
state.sprites.push(DrawType::Sprite(i));
|
|
}
|
|
|
|
fn draw_text_centered(text: &str, y: f32, font: Font) {
|
|
let dims = measure_text(text, Some(font), FONT_HEIGHT - FONT_DIFFERENCE, 1.0);
|
|
draw_text_ex(
|
|
text,
|
|
WIDTH as f32 / 2.0 - dims.width / 2.0,
|
|
y,
|
|
TextParams {
|
|
font,
|
|
color: BLACK,
|
|
font_size: FONT_HEIGHT - FONT_DIFFERENCE,
|
|
..Default::default()
|
|
},
|
|
);
|
|
}
|