275 lines
11 KiB
Rust
275 lines
11 KiB
Rust
use rand::Rng; // Allows you to generate random numbers
|
|
use console::Term; // Allows reading key bind input
|
|
use std::io::{stdin,stdout,Write};
|
|
use colored::Colorize;
|
|
|
|
// This function is used to Generate the output out a range of levels to the console, it accepts starting and
|
|
// ending levls to print and then it uses a loop to print out each level in between these, each
|
|
// level is comprised of numbers, each having a specific meaning for instance 0 is an obstacle and
|
|
// -2 is a slanted wall "/"
|
|
fn generate_output(starting_level: u64, ending_level: u64, character_x_coord: u16, character_icon: char, levels: &Vec<Vec<i8>>, debug_mode: bool, boots_mode: bool) -> String {
|
|
let mut output: String = "".to_string();
|
|
if debug_mode {
|
|
let pattern = "0123456789";
|
|
let repeat_count = (levels[0].len() + 9) / 10;
|
|
let mut repeated = pattern.repeat(repeat_count);
|
|
repeated += "\n";
|
|
output += &repeated;
|
|
}
|
|
for i in starting_level..ending_level {
|
|
let level = &levels[i as usize]; // Cache the level for quicker access
|
|
for j in 0..level.len() {
|
|
let object = level[j as usize];
|
|
// Directly check the player's position for rendering the icon
|
|
if i == starting_level && j == character_x_coord as usize {
|
|
output = output + &character_icon.to_string();
|
|
}
|
|
else if i == starting_level+2 && j == character_x_coord as usize {
|
|
output = output + &character_icon.to_string();
|
|
}
|
|
else {
|
|
match object {
|
|
-3 => output.push_str(&"|".to_string()), // Specific case for -3
|
|
0 => output.push_str(&"|".on_truecolor(255,255,255).strikethrough().to_string()), // Specific case for 0
|
|
-1 => output.push_str(&"\\".to_string()), // Specific case for -1
|
|
-2 => output.push_str(&"/".to_string()), // Specific case for -2
|
|
_ => output+=" " // Default, no print for other cases
|
|
}
|
|
}
|
|
}
|
|
output+=" "; // At the end of each level, print that level
|
|
output+=&i.to_string();
|
|
output+="\n";
|
|
}
|
|
return output;
|
|
}
|
|
|
|
fn generate_level(level_to_generate: u64, difficulty: i8, left_wall: &mut i16, right_wall: &mut i16, screen_width: u16, preference: &mut f32, levels: &mut Vec<Vec<i8>>) {
|
|
let mut new_level: Vec<i8> = Vec::new();
|
|
|
|
let left_wall_change = rand::thread_rng().gen_range(-1..2+(*preference as i16));
|
|
let right_wall_change = rand::thread_rng().gen_range(-1..2+(*preference as i16));
|
|
*left_wall = *left_wall + left_wall_change;
|
|
*right_wall = *right_wall + right_wall_change;
|
|
if *left_wall <= 0 {
|
|
*left_wall = 1;
|
|
}
|
|
if *right_wall >= screen_width.try_into().unwrap() {
|
|
*right_wall = screen_width as i16-1
|
|
}
|
|
if (*right_wall-*left_wall).abs() < 8 {
|
|
*left_wall-=3;
|
|
*right_wall+=3;
|
|
}
|
|
|
|
while new_level.len() <= screen_width as usize {
|
|
new_level.push(1)
|
|
}
|
|
|
|
for i in *left_wall..*right_wall {
|
|
let mut object: i8 = 1;
|
|
if level_to_generate >= 5 || (i as f32) < (screen_width as f32 / 2.0)-5.0 || (i as f32) > (screen_width as f32 / 2.0)+5.0 {
|
|
object = rand::thread_rng().gen_range(0..difficulty);
|
|
}
|
|
new_level[i as usize] = object;
|
|
}
|
|
for i in 0..*left_wall {
|
|
if i == *left_wall-1 && left_wall_change > 0 {
|
|
new_level[i as usize] = -1;
|
|
}
|
|
else if i == *left_wall-1 && left_wall_change < 0 {
|
|
new_level[i as usize] = -2;
|
|
}
|
|
else {
|
|
new_level[i as usize] = -3;
|
|
}
|
|
}
|
|
for i in *right_wall..screen_width as i16 {
|
|
if i == *right_wall && right_wall_change > 0 {
|
|
new_level[i as usize] = -1;
|
|
}
|
|
else if i == *right_wall && right_wall_change < 0 {
|
|
new_level[i as usize] = -2;
|
|
}
|
|
else {
|
|
new_level[i as usize] = -3;
|
|
}
|
|
}
|
|
while levels.len() <= level_to_generate as usize {
|
|
levels.push(new_level.clone());
|
|
}
|
|
levels[level_to_generate as usize] = new_level;
|
|
|
|
// Modification of the directional preference logic
|
|
*preference = *preference + rand::thread_rng().gen_range(-0.5..0.5);
|
|
if *preference > 3.0 {
|
|
*preference = -1.0;
|
|
}
|
|
else if *preference < -3.0 {
|
|
*preference = 1.0;
|
|
}
|
|
if *right_wall >= screen_width as i16 - 5 {
|
|
*preference = -1.0;
|
|
}
|
|
else if *left_wall <= 5 {
|
|
*preference = 1.0;
|
|
}
|
|
}
|
|
|
|
fn debug_mode_modify_variables(current_level: &mut u64) -> u8 {
|
|
println!("\n----------------VARIABLE MODIFICATION---------------\n");
|
|
println!("Variables that can be modified:\ncurrent_level");
|
|
println!("\nWhat variable do you want to modify? ('N' to return to game without modification)");
|
|
|
|
let variable_to_modify = input();
|
|
|
|
if variable_to_modify == "N" {
|
|
return 0;
|
|
}
|
|
else {
|
|
println!("New value for {}",variable_to_modify);
|
|
let new_value = input();
|
|
|
|
if variable_to_modify == "current_level" {
|
|
*current_level = new_value.parse().unwrap();
|
|
}
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
fn check_if_alive(levels: &Vec<Vec<i8>>, level: usize, x_coord: usize, boots_mode: bool) -> bool {
|
|
if levels[level][x_coord] <= 0 || levels[level+2][x_coord] <= 0 {
|
|
return false;
|
|
}
|
|
else {
|
|
return true;
|
|
}
|
|
}
|
|
|
|
fn input() -> String{
|
|
let mut s=String::new();
|
|
let _=stdout().flush();
|
|
stdin().read_line(&mut s).expect("Did not enter a correct string");
|
|
if let Some('\n')=s.chars().next_back() {
|
|
s.pop();
|
|
}
|
|
if let Some('\r')=s.chars().next_back() {
|
|
s.pop();
|
|
}
|
|
return s;
|
|
}
|
|
|
|
fn main() {
|
|
let debug_mode = false; // Enable or disable debugging mode, it will print some useful stats and
|
|
// let you modify stats midgame with e
|
|
let can_die = true; // I can't spell invinsiblitlity but this lets you not die...
|
|
|
|
let boots_mode = true; // Boots mode means you're falling after your younger sister and have to
|
|
// prevent both of you dying •_•
|
|
|
|
let mut levels: Vec<Vec<i8>> = Vec::new(); // Define variables for level
|
|
|
|
let (mut screen_width, mut screen_height) = termion::terminal_size().unwrap();
|
|
screen_width = screen_width - 20;
|
|
if debug_mode { // Reduce screen height more if debug mode is on as we need space to print
|
|
// additional information
|
|
screen_height = screen_height - 17;
|
|
}
|
|
else {
|
|
screen_height = screen_height - 2;
|
|
}
|
|
|
|
let stdout = Term::buffered_stdout();
|
|
|
|
let mut current_level: u64 = 0;
|
|
let mut character_icon: char = '☺';
|
|
let mut x_coord: u16 = (screen_width as f32/ 2.0) as u16; // Distance from left wall
|
|
let mut left_wall: i16 = rand::thread_rng().gen_range(0..((screen_width as f32/ 2.0)-5.0) as i16) as i16;
|
|
let mut right_wall: i16 = rand::thread_rng().gen_range(((screen_width as f32 /2.0)+5.0) as i16..screen_width as i16) as i16;
|
|
let mut preference: f32 = rand::thread_rng().gen_range(-2.0..3.0);
|
|
let mut alive: bool = true;
|
|
let mut difficulty: i8 = 5;
|
|
|
|
|
|
for i in 0..screen_height {
|
|
generate_level(i as u64, difficulty, &mut left_wall, &mut right_wall, screen_width, &mut preference, &mut levels)
|
|
};
|
|
'game_loop: loop {
|
|
let start = std::time::Instant::now();
|
|
generate_level(current_level + screen_height as u64, difficulty, &mut left_wall, &mut right_wall, screen_width, &mut preference, &mut levels);
|
|
let end_gen = std::time::Instant::now();
|
|
|
|
let output = generate_output(current_level, current_level + screen_height as u64, x_coord, character_icon, &levels, debug_mode, boots_mode);
|
|
let end_gen_output = std::time::Instant::now();
|
|
if ! debug_mode { // We don't want to clear screen if debug mode is on as it lets us see
|
|
// old stats and see console warnings from rust and stuff
|
|
print!("{}[2J", 27 as char);
|
|
}
|
|
println!("{}",output);
|
|
let end_output = std::time::Instant::now();
|
|
|
|
if debug_mode { // Print out debug info
|
|
println!("Time to Generate Level: {:?}\nTime to Generate Output: {:?}\nTime to Output levels: {:?}\nCurrent Level: {}\nCharacter's X coordinate: {}\nLeft Wall Position: {}\nRight Wall Position: {}\nDirectional Preference: {}\nAlive: {}\nDifficulty: {}\nCharcter Icon: {}\nItem Currently Ontop of: {}\nScreen Width: {}\nScreen Height: {}",
|
|
end_gen-start,
|
|
end_gen_output-end_gen,
|
|
end_output-end_gen_output,
|
|
current_level,
|
|
x_coord,
|
|
left_wall,
|
|
right_wall,
|
|
preference,
|
|
alive,
|
|
difficulty,
|
|
character_icon,
|
|
levels[current_level as usize][x_coord as usize],
|
|
screen_width,
|
|
screen_height
|
|
);
|
|
}
|
|
|
|
if let Ok(character) = stdout.read_char() {
|
|
match character {
|
|
'd' => if x_coord < screen_width-1 { x_coord += 1 },
|
|
'a' => if x_coord > 1 { x_coord -= 1 },
|
|
'e' => if debug_mode { debug_mode_modify_variables(&mut current_level); continue 'game_loop },
|
|
'q' => break 'game_loop,
|
|
_ => (),
|
|
}
|
|
}
|
|
|
|
current_level+=1;
|
|
if can_die {
|
|
alive = check_if_alive(&levels, current_level as usize, x_coord as usize, boots_mode);
|
|
}
|
|
if alive == false {
|
|
break 'game_loop
|
|
}
|
|
}
|
|
if ! debug_mode { // We don't want to clear screen if debug mode is on as it lets us see
|
|
// old stats and see console warnings from rust and stuff
|
|
print!("{}[2J", 27 as char);
|
|
}
|
|
println!("{}","GAME OVER".red());
|
|
println!("You fell {:?}m before going SPLAT",current_level-1);
|
|
println!(" -__");
|
|
println!(" / \\");
|
|
println!(" | -___");
|
|
println!(" | / ________________________");
|
|
println!(" | / | |");
|
|
println!(" ‾ |‾ / _ | Where is the princess? |");
|
|
println!(" / | \\ / | \\ |________________________|");
|
|
println!(" / ‾‾‾‾‾‾‾ \\ / /");
|
|
println!(" __ / / \\• •/ _|");
|
|
println!(" / \\ _/‾|‾\\ / |");
|
|
println!("_ / / \\ / / |");
|
|
println!(" \\/ / /|___/");
|
|
println!(" / \\ ");
|
|
println!(" / \\ / \\");
|
|
println!(" | \\ / \\");
|
|
println!(" |\\____\\_/ \\");
|
|
println!(" \\ / / \\ \\ /");
|
|
println!(" \\ / \\ /");
|
|
println!(" / / / /");
|
|
println!(" _| \\ /_ |");
|
|
}
|