this repo has no description
2
fork

Configure Feed

Select the types of activity you want to include in your feed.

at main 253 lines 7.3 kB view raw
1#![allow(clippy::type_complexity)] 2 3use std::collections::VecDeque; 4use std::time::{SystemTime, UNIX_EPOCH}; 5 6use rand::seq::SliceRandom; 7use rand::Rng; 8use valence::prelude::*; 9use valence::protocol::sound::{Sound, SoundCategory}; 10use valence::spawn::IsFlat; 11 12const START_POS: BlockPos = BlockPos::new(0, 100, 0); 13const VIEW_DIST: u8 = 10; 14 15const BLOCK_TYPES: [BlockState; 7] = [ 16 BlockState::GRASS_BLOCK, 17 BlockState::OAK_LOG, 18 BlockState::BIRCH_LOG, 19 BlockState::OAK_LEAVES, 20 BlockState::BIRCH_LEAVES, 21 BlockState::DIRT, 22 BlockState::MOSS_BLOCK, 23]; 24 25pub fn main() { 26 App::new() 27 .add_plugins(DefaultPlugins) 28 .add_systems( 29 Update, 30 ( 31 init_clients, 32 reset_clients.after(init_clients), 33 manage_chunks.after(reset_clients).before(manage_blocks), 34 manage_blocks, 35 despawn_disconnected_clients, 36 ), 37 ) 38 .run(); 39} 40 41#[derive(Component)] 42struct GameState { 43 blocks: VecDeque<BlockPos>, 44 score: u32, 45 combo: u32, 46 target_y: i32, 47 last_block_timestamp: u128, 48} 49 50fn init_clients( 51 mut clients: Query< 52 ( 53 Entity, 54 &mut Client, 55 &mut VisibleChunkLayer, 56 &mut IsFlat, 57 &mut GameMode, 58 ), 59 Added<Client>, 60 >, 61 server: Res<Server>, 62 dimensions: Res<DimensionTypeRegistry>, 63 biomes: Res<BiomeRegistry>, 64 mut commands: Commands, 65) { 66 for (entity, mut client, mut visible_chunk_layer, mut is_flat, mut game_mode) in &mut clients { 67 visible_chunk_layer.0 = entity; 68 is_flat.0 = true; 69 *game_mode = GameMode::Adventure; 70 71 client.send_chat_message("Welcome to epic infinite parkour game!".italic()); 72 73 let state = GameState { 74 blocks: VecDeque::new(), 75 score: 0, 76 combo: 0, 77 target_y: 0, 78 last_block_timestamp: 0, 79 }; 80 81 let layer = ChunkLayer::new(ident!("overworld"), &dimensions, &biomes, &server); 82 83 commands.entity(entity).insert((state, layer)); 84 } 85} 86 87fn reset_clients( 88 mut clients: Query<( 89 &mut Client, 90 &mut Position, 91 &mut Look, 92 &mut GameState, 93 &mut ChunkLayer, 94 )>, 95) { 96 for (mut client, mut pos, mut look, mut state, mut layer) in &mut clients { 97 let out_of_bounds = (pos.0.y as i32) < START_POS.y - 32; 98 99 if out_of_bounds || state.is_added() { 100 if out_of_bounds && !state.is_added() { 101 client.send_chat_message( 102 "Your score was ".italic() 103 + state 104 .score 105 .to_string() 106 .color(Color::GOLD) 107 .bold() 108 .not_italic(), 109 ); 110 } 111 112 // Init chunks. 113 for pos in ChunkView::new(START_POS.into(), VIEW_DIST).iter() { 114 layer.insert_chunk(pos, UnloadedChunk::new()); 115 } 116 117 state.score = 0; 118 state.combo = 0; 119 120 for block in &state.blocks { 121 layer.set_block(*block, BlockState::AIR); 122 } 123 state.blocks.clear(); 124 state.blocks.push_back(START_POS); 125 layer.set_block(START_POS, BlockState::STONE); 126 127 for _ in 0..10 { 128 generate_next_block(&mut state, &mut layer, false); 129 } 130 131 pos.set([ 132 f64::from(START_POS.x) + 0.5, 133 f64::from(START_POS.y) + 1.0, 134 f64::from(START_POS.z) + 0.5, 135 ]); 136 look.yaw = 0.0; 137 look.pitch = 0.0; 138 } 139 } 140} 141 142fn manage_blocks(mut clients: Query<(&mut Client, &Position, &mut GameState, &mut ChunkLayer)>) { 143 for (mut client, pos, mut state, mut layer) in &mut clients { 144 let pos_under_player = BlockPos::new( 145 (pos.0.x - 0.5).round() as i32, 146 pos.0.y as i32 - 1, 147 (pos.0.z - 0.5).round() as i32, 148 ); 149 150 if let Some(index) = state 151 .blocks 152 .iter() 153 .position(|block| *block == pos_under_player) 154 { 155 if index > 0 { 156 let power_result = 2_f32.powf((state.combo as f32) / 45.0); 157 let max_time_taken = (1000_f32 * (index as f32) / power_result) as u128; 158 159 let current_time_millis = SystemTime::now() 160 .duration_since(UNIX_EPOCH) 161 .unwrap() 162 .as_millis(); 163 164 if current_time_millis - state.last_block_timestamp < max_time_taken { 165 state.combo += index as u32 166 } else { 167 state.combo = 0 168 } 169 170 for _ in 0..index { 171 generate_next_block(&mut state, &mut layer, true) 172 } 173 174 let pitch = 0.9 + ((state.combo as f32) - 1.0) * 0.05; 175 client.play_sound( 176 Sound::BlockNoteBlockBass, 177 SoundCategory::Master, 178 pos.0, 179 1.0, 180 pitch, 181 ); 182 183 client.set_title(""); 184 client.set_subtitle(state.score.to_string().color(Color::LIGHT_PURPLE).bold()); 185 } 186 } 187 } 188} 189 190fn manage_chunks(mut clients: Query<(&Position, &OldPosition, &mut ChunkLayer), With<Client>>) { 191 for (pos, old_pos, mut layer) in &mut clients { 192 let old_view = ChunkView::new(old_pos.get().into(), VIEW_DIST); 193 let view = ChunkView::new(pos.0.into(), VIEW_DIST); 194 195 if old_view != view { 196 for pos in old_view.diff(view) { 197 layer.remove_chunk(pos); 198 } 199 200 for pos in view.diff(old_view) { 201 layer.chunk_entry(pos).or_default(); 202 } 203 } 204 } 205} 206 207fn generate_next_block(state: &mut GameState, layer: &mut ChunkLayer, in_game: bool) { 208 if in_game { 209 let removed_block = state.blocks.pop_front().unwrap(); 210 layer.set_block(removed_block, BlockState::AIR); 211 212 state.score += 1 213 } 214 215 let last_pos = *state.blocks.back().unwrap(); 216 let block_pos = generate_random_block(last_pos, state.target_y); 217 218 if last_pos.y == START_POS.y { 219 state.target_y = 0 220 } else if last_pos.y < START_POS.y - 30 || last_pos.y > START_POS.y + 30 { 221 state.target_y = START_POS.y; 222 } 223 224 let mut rng = rand::thread_rng(); 225 226 layer.set_block(block_pos, *BLOCK_TYPES.choose(&mut rng).unwrap()); 227 state.blocks.push_back(block_pos); 228 229 // Combo System 230 state.last_block_timestamp = SystemTime::now() 231 .duration_since(UNIX_EPOCH) 232 .unwrap() 233 .as_millis(); 234} 235 236fn generate_random_block(pos: BlockPos, target_y: i32) -> BlockPos { 237 let mut rng = rand::thread_rng(); 238 239 // if above or below target_y, change y to gradually reach it 240 let y = match target_y { 241 0 => rng.gen_range(-1..2), 242 y if y > pos.y => 1, 243 _ => -1, 244 }; 245 let z = match y { 246 1 => rng.gen_range(1..3), 247 -1 => rng.gen_range(2..5), 248 _ => rng.gen_range(1..4), 249 }; 250 let x = rng.gen_range(-3..4); 251 252 BlockPos::new(pos.x + x, pos.y + y, pos.z + z) 253}