feat: core gameplay

This commit is contained in:
YK 2024-07-14 20:25:02 +03:00
parent 631ad0c274
commit 87288601ca
4 changed files with 114 additions and 30 deletions

View File

@ -16,7 +16,13 @@ pub fn button_system (
}
}
pub fn despawn_screen <T: Component> (to_despawn: Query<Entity, With<T>>, mut commands: Commands) {
pub fn despawn_screen <T: Component> (to_despawn: Query<Entity, With<T>>, commands: &mut Commands) {
for entity in &to_despawn {
commands.entity(entity).despawn_recursive();
}
}
pub fn despawn_screen_move <T: Component> (to_despawn: Query<Entity, With<T>>, mut commands: Commands) {
for entity in &to_despawn {
commands.entity(entity).despawn_recursive();
}

View File

@ -1,11 +1,13 @@
use crate::prelude::*;
use itertools::Itertools;
use rand::{distributions::{Distribution, WeightedIndex}, seq::IteratorRandom};
use crate::{common::despawn_screen, prelude::*};
#[derive(Component)]
pub struct FieldView;
pub fn enter (
mut commands: Commands,
asset_server: Res<AssetServer>,
@ -80,10 +82,10 @@ pub fn enter (
let mut start = vec![0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1];
start.shuffle(thread);
*field = Field(start);
*field = Field(start, 4);
render_field(commands, asset_server, field.into());
// render_field(commands, asset_server, field.into(), q);
}
pub fn update_scoreboard (score: Res<Score>, mut query: Query<&mut Text, With<ScoreboardUi>>) {
@ -95,30 +97,39 @@ pub fn render_field (
mut commands: Commands,
asset_server: Res<AssetServer>,
field: Res<Field>,
mut prev_field: ResMut<PrevField>,
q: Query<Entity, With<FieldView>>,
) {
if prev_field.0 != field.0 {
despawn_screen::<FieldView>(q, &mut commands);
for (idx, &val) in field.iter().enumerate() {
if val > 0 {
spawn_square_at(&mut commands, val, idx, asset_server.clone());
}
}
prev_field.0 = field.0.clone();
}
}
fn spawn_square_at (commands: &mut Commands, level: u32, position: usize, asset_server: AssetServer) {
fn spawn_square_at (commands: &mut Commands, level: u8, position: usize, asset_server: AssetServer) {
let font = asset_server.load("proxima_thin.ttf");
let Some(&(x, y)) = sq_origins.get(position) else {
return;
};
let text = 2i32.pow(level);
let text = 2i32.pow(level as u32).to_string();
let label = Text2dBundle {
text: Text::from_section(text.to_string(), TextStyle {
text: Text::from_section(text, TextStyle {
font,
font_size: 44.,
color: Color::WHITE,
}),
transform: Transform {
translation: Vec3::new(x, y, 0.0),
translation: Vec3::new(x, y, 1.),
..Default::default()
},
..Default::default()
@ -150,19 +161,21 @@ pub fn process_inputs (
score: ResMut<Score>,
) {
let mover = |dir: MoveDirection| move_field(dir, field, score);
for key in kb.get_just_pressed() {
match key {
KeyCode::ArrowUp | KeyCode::KeyW => {
return move_field(MoveDirection::Up, field, score);
return mover(MoveDirection::Up);
},
KeyCode::ArrowDown | KeyCode::KeyS => {
return move_field(MoveDirection::Down, field, score);
return mover(MoveDirection::Down);
},
KeyCode::ArrowLeft | KeyCode::KeyA => {
return move_field(MoveDirection::Left, field, score);
return mover(MoveDirection::Left);
},
KeyCode::ArrowRight | KeyCode::KeyD => {
return move_field(MoveDirection::Right, field, score);
return mover(MoveDirection::Right);
},
_ => ()
}
@ -179,28 +192,89 @@ pub fn process_inputs (
// controls (with custom actuation point)
// https://bevy-cheatbook.github.io/input/gamepad.html#gamepad-settings
if icgbp(GBT::DPadLeft) {
return move_field(MoveDirection::Left, field, score);
return mover(MoveDirection::Left);
} else if icgbp(GBT::DPadRight) {
return move_field(MoveDirection::Right, field, score);
return mover(MoveDirection::Right);
} else if icgbp(GBT::DPadUp) {
return move_field(MoveDirection::Up, field, score);
return mover(MoveDirection::Up);
} else if icgbp(GBT::DPadDown) {
return move_field(MoveDirection::Down, field, score);
return mover(MoveDirection::Down);
}
}
}
pub fn move_field (
fn move_field (
direction: MoveDirection,
field: ResMut<Field>,
score: ResMut<Score>
mut field: ResMut<Field>,
mut score: ResMut<Score>,
) {
println!("{:?} {:?}", direction, score);
let (new, score_incr) = calculate_move(&field.0, field.1, direction);
if new != field.0 {
field.0 = new;
score.0 += score_incr;
spawn_new_square(field);
}
}
pub fn redraw_field () {
fn calculate_move (source: &[u8], size: usize, dir: MoveDirection) -> (Vec<u8>, usize) {
let mut score_changes = vec![];
let mut target = vec![0; source.len()];
let mut qs = Vec::with_capacity(size);
for x in 0..size {
let mut col = Vec::with_capacity(size);
for y in 0..size {
if dir == MoveDirection::Up || dir == MoveDirection::Down {
col.push(y * size + x);
} else {
col.push(x * size + y);
}
}
if dir == MoveDirection::Down || dir == MoveDirection::Right {
col.reverse();
}
qs.push(col);
}
for q in qs {
let mut write_cursor = 0;
let mut cursor = 0;
let _in = q.iter().map(|&idx| source[idx]).filter(|&e| e != 0).collect_vec();
while let Some(&cur) = _in.get(cursor) {
let next = _in.get(cursor + 1);
match (cur, next) {
(a, Some(b)) if a == *b => {
target[q[write_cursor]] = cur + 1;
score_changes.push(cur as usize);
write_cursor += 1;
cursor += 2;
},
_ => {
target[q[write_cursor]] = cur;
write_cursor += 1;
cursor += 1;
}
}
}
}
(target, score_changes.iter().sum())
}
fn spawn_new_square (mut field: ResMut<Field>) {
let rng = &mut thread_rng();
let Some(empty) = field.0.iter().enumerate().filter(|&(_, &val)| val == 0).map(|(idx, _)| idx).choose(rng) else { return };
let choices = [1, 2, 3];
let weights = [8, 4, 1];
let Ok(index) = WeightedIndex::new(&weights) else { return };
field.0[empty] = choices[index.sample(rng)];
}

View File

@ -17,12 +17,13 @@ impl Plugin for SqPlugin {
.insert_state(State4096::Game)
.insert_state(InGameState::Static)
.insert_resource(Score(0))
.insert_resource(Field(vec![]))
.insert_resource(Field(vec![], 0))
.insert_resource(PrevField(vec![]))
.add_systems(Startup, setup::setup)
.add_systems(Update, (game::update_scoreboard, game::process_inputs).run_if(in_state(State4096::Game)))
.add_systems(Update, (game::render_field, game::update_scoreboard, game::process_inputs).run_if(in_state(State4096::Game)))
.add_systems(Update, (menu::actions, common::button_system).run_if(in_state(State4096::Menu)))
.add_systems(OnEnter(State4096::Menu), menu::enter)
.add_systems(OnExit(State4096::Menu), common::despawn_screen::<menu::Menu>)
.add_systems(OnExit(State4096::Menu), common::despawn_screen_move::<menu::Menu>)
.add_systems(OnEnter(State4096::Game), game::enter)
;
}

View File

@ -9,7 +9,10 @@ pub struct ScoreboardUi;
#[derive(Resource, Deref, DerefMut)]
pub struct Field (pub Vec<u32>);
pub struct Field (#[deref] pub Vec<u8>, pub usize);
#[derive(Resource, Deref, DerefMut)]
pub struct PrevField (pub Vec<u8>);
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub enum MoveDirection {