@ -1,41 +1,142 @@
#![ allow(clippy::upper_case_acronyms) ]
#![ allow(clippy::new_without_default) ]
#![ allow(unused) ]
use macroquad ::prelude ::* ;
use macroquad ::rand ::* ;
const FISH_PARTS_RIGHT : ( & str , & str ) = ( "><" , ">" ) ;
const FISH_PARTS_LEFT : ( & str , & str ) = ( "<" , "><" ) ;
const WATER_LEVEL : i32 = 100 ;
const WIDTH : i32 = 780 ;
const FISH_ZONE : i32 = 500 ;
const FONT_SIZE : f32 = 20.0 ;
const ANIM_CHANGE : u32 = 150 ; // in milliseconds
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 : FISH_ZONE ,
window_height : HEIGHT ,
fullscreen : false ,
. . Default ::default ( )
}
}
struct Fish {
is_right : bool ,
// middle length
length : u8 ,
pos : Vec2 ,
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 {
fn new ( ) -> Self {
Self {
state : State ::new ( ) ,
draw : Draw ::new ( ) ,
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 ;
}
}
}
}
}
@ -43,16 +144,78 @@ impl Game {
struct Draw {
// only two frames of animation
first_frame : bool ,
frame_counter : usize ,
// animated sprites
animations : Vec < Animation > ,
// static sprites
sprites : Vec < Sprite > ,
font : Font ,
}
impl Draw {
fn new ( ) -> Self {
Self {
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 ,
frame_counter : 0 ,
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 ( )
} ,
) ;
}
}
}
@ -61,13 +224,14 @@ 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 ( ) ) ;
assert_eq ! ( width * height , frames [ 1 ] . len ( ) ) ;
assert_eq ! ( width * height , frames [ 0 ] . len ( ) - height + 1 ) ;
assert_eq ! ( width * height , frames [ 1 ] . len ( ) - height + 1 ) ;
Self {
width ,
height ,
@ -76,28 +240,58 @@ impl Animation {
}
}
fn draw ( & self , pos : Vec2 ) {
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_SIZE ,
self . height as f32 * FONT_SIZE ,
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 > ,
player : Vec2 ,
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 ( ) ,
player : Vec2 ::new ( FISH_ZONE as f32 / 2.0 , WATER_LEVEL as f32 ) ,
hook : IVec2 ::new ( GRID_WIDTH as i32 / 2 , WATER_LEVEL + 5 ) ,
is_hooked : None ,
positions : Vec ::new ( ) ,
sprites : Vec ::new ( ) ,
caught_fish : Vec ::new ( ) ,
}
}
@ -105,27 +299,418 @@ impl State {
#[ macroquad::main(window_conf) ]
async fn main ( ) {
let mut game = Game ::new ( ) ;
srand ( get_time ( ) . to_bits ( ) ) ;
let mut game = Game ::new ( ) . await . unwrap ( ) ;
let mut updated = get_time ( ) ;
let mut anim_updated = get_time ( ) ;
loop {
if is_key_down ( KeyCode ::Escape ) {
break ;
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 ;
}
}
}
draw ( & game ) ;
next_frame ( ) . await ;
}
}
fn draw ( game : & Game ) {
const FZ : f32 = FISH_ZONE as f32 ;
const WL : f32 = WATER_LEVEL as f32 ;
clear_background ( SKYBLUE ) ;
// sky
draw_rectangle ( 0.0 , 0.0 , FZ , WL , WHITE ) ;
// ground
draw_rectangle ( 0.0 , FZ - 30.0 , FZ , 30.0 , BEIGE ) ;
// static sprites
// animated stuff
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
// sidebar
draw_rectangle ( FZ , 0.0 , WIDTH as f32 - FZ , FZ , LIGHTGRAY ) ;
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 ( )
} ,
) ;
}