Skip to content

Commit d79772a

Browse files
committed
Large update of 57A onwards to properly handle player.rs try_move function.
1 parent 3fdddca commit d79772a

35 files changed

Lines changed: 718 additions & 380 deletions

File tree

book/src/chapter_57a.md

Lines changed: 29 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -494,7 +494,26 @@ crate::spatial::for_each_tile_content(idx, |mob| targets.push(mob));
494494

495495
### Fixing player.rs
496496

497-
The function `try_move_player` does a really big query of the spatial indexing system. Replacing it with the new API is mostly a matter of using the new functions, and performing the index check inside the closure. Here's the new function:
497+
The function `try_move_player` does a really big query of the spatial indexing system. It also sometimes returns mid-calculation, which our API doesn't currently support. We'll add a new function to our `spatial/mod.rs` file to enable this:
498+
499+
```rust
500+
pub fn for_each_tile_content_with_gamemode<F>(idx: usize, mut f: F) -> RunState
501+
where F : FnMut(Entity)->Option<RunState>
502+
{
503+
let lock = SPATIAL_MAP.lock().unwrap();
504+
for entity in lock.tile_content[idx].iter() {
505+
if let Some(rs) = f(entity.0) {
506+
return rs;
507+
}
508+
}
509+
510+
RunState::AwaitingInput
511+
}
512+
```
513+
514+
This function runs like the other one, but accepts an optional game mode from the closure. If the game mode is `Some(x)`, then it returns `x`. If it hasn't received any modes by the end, it returns `AwaitingInput`.
515+
516+
Replacing it with the new API is mostly a matter of using the new functions, and performing the index check inside the closure. Here's the new function:
498517

499518
```rust
500519
pub fn try_move_player(delta_x: i32, delta_y: i32, ecs: &mut World) -> RunState {
@@ -519,7 +538,7 @@ pub fn try_move_player(delta_x: i32, delta_y: i32, ecs: &mut World) -> RunState
519538
if pos.x + delta_x < 1 || pos.x + delta_x > map.width-1 || pos.y + delta_y < 1 || pos.y + delta_y > map.height-1 { return RunState::AwaitingInput; }
520539
let destination_idx = map.xy_idx(pos.x + delta_x, pos.y + delta_y);
521540

522-
crate::spatial::for_each_tile_content(destination_idx, |potential_target| {
541+
result = crate::spatial::for_each_tile_content_with_gamemode(destination_idx, |potential_target| {
523542
let mut hostile = true;
524543
if combat_stats.get(potential_target).is_some() {
525544
if let Some(faction) = factions.get(potential_target) {
@@ -544,11 +563,12 @@ pub fn try_move_player(delta_x: i32, delta_y: i32, ecs: &mut World) -> RunState
544563
let mut ppos = ecs.write_resource::<Point>();
545564
ppos.x = pos.x;
546565
ppos.y = pos.y;
566+
return Some(RunState::Ticking);
547567
} else {
548568
let target = combat_stats.get(potential_target);
549569
if let Some(_target) = target {
550570
wants_to_melee.insert(entity, WantsToMelee{ target: potential_target }).expect("Add target failed");
551-
result = RunState::Ticking;
571+
return Some(RunState::Ticking);
552572
}
553573
}
554574
let door = doors.get_mut(potential_target);
@@ -559,8 +579,9 @@ pub fn try_move_player(delta_x: i32, delta_y: i32, ecs: &mut World) -> RunState
559579
let glyph = renderables.get_mut(potential_target).unwrap();
560580
glyph.glyph = rltk::to_cp437('/');
561581
viewshed.dirty = true;
562-
result = RunState::Ticking;
582+
return Some(RunState::Ticking);
563583
}
584+
None
564585
});
565586

566587
if !crate::spatial::is_blocked(destination_idx) {
@@ -587,9 +608,12 @@ pub fn try_move_player(delta_x: i32, delta_y: i32, ecs: &mut World) -> RunState
587608
for m in swap_entities.iter() {
588609
let their_pos = positions.get_mut(m.0);
589610
if let Some(their_pos) = their_pos {
590-
// TODO: Swapping
611+
let old_idx = map.xy_idx(their_pos.x, their_pos.y);
591612
their_pos.x = m.1;
592613
their_pos.y = m.2;
614+
let new_idx = map.xy_idx(their_pos.x, their_pos.y);
615+
crate::spatial::move_entity(m.0, old_idx, new_idx);
616+
result = RunState::Ticking;
593617
}
594618
}
595619

book/src/chapter_58.md

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -689,12 +689,12 @@ let mut swap_entities : Vec<(Entity, i32, i32)> = Vec::new();
689689

690690
for (entity, _player, pos, viewshed) in (&entities, &players, &mut positions, &mut viewsheds).join() {
691691
if pos.x + delta_x < 1 || pos.x + delta_x > map.width-1 || pos.y + delta_y < 1 || pos.y + delta_y > map.height-1 { return RunState::AwaitingInput; }
692-
let destination_idx = map.xy_idx(pos.x + delta_x, pos.y + delta_y);
692+
let destination_idx = map.xy_idx(pos.x + delta_x, pos.y + delta_y);
693693

694-
for potential_target in map.tile_content[destination_idx].iter() {
695-
if let Some(_vendor) = vendors.get(*potential_target) {
696-
return RunState::ShowVendor{ vendor: *potential_target, mode : VendorMode::Sell }
697-
}
694+
result = crate::spatial::for_each_tile_content_with_gamemode(destination_idx, |potential_target| {
695+
if let Some(_vendor) = vendors.get(potential_target) {
696+
return Some(RunState::ShowVendor{ vendor: potential_target, mode : VendorMode::Sell });
697+
}
698698
...
699699
```
700700

book/src/chapter_70.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -257,11 +257,11 @@ fn get_player_target_list(ecs : &mut World) -> Vec<(f32,Entity)> {
257257
let tile_idx = map.xy_idx(tile_point.x, tile_point.y);
258258
let distance_to_target = rltk::DistanceAlg::Pythagoras.distance2d(*tile_point, rltk::Point::new(player_pos.x, player_pos.y));
259259
if distance_to_target < range as f32 {
260-
for possible_target in map.tile_content[tile_idx].iter() {
261-
if *possible_target != *player_entity && factions.get(*possible_target).is_some() {
262-
possible_targets.push((distance_to_target, *possible_target));
260+
crate::spatial::for_each_tile_content(tile_idx, |possible_target| {
261+
if possible_target != *player_entity && factions.get(possible_target).is_some() {
262+
possible_targets.push((distance_to_target, possible_target));
263263
}
264-
}
264+
});
265265
}
266266
}
267267
}

chapter-57a-spatial/src/player.rs

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ pub fn try_move_player(delta_x: i32, delta_y: i32, ecs: &mut World) -> RunState
2828
if pos.x + delta_x < 1 || pos.x + delta_x > map.width-1 || pos.y + delta_y < 1 || pos.y + delta_y > map.height-1 { return RunState::AwaitingInput; }
2929
let destination_idx = map.xy_idx(pos.x + delta_x, pos.y + delta_y);
3030

31-
crate::spatial::for_each_tile_content(destination_idx, |potential_target| {
31+
result = crate::spatial::for_each_tile_content_with_gamemode(destination_idx, |potential_target| {
3232
let mut hostile = true;
3333
if combat_stats.get(potential_target).is_some() {
3434
if let Some(faction) = factions.get(potential_target) {
@@ -53,11 +53,12 @@ pub fn try_move_player(delta_x: i32, delta_y: i32, ecs: &mut World) -> RunState
5353
let mut ppos = ecs.write_resource::<Point>();
5454
ppos.x = pos.x;
5555
ppos.y = pos.y;
56+
return Some(RunState::Ticking);
5657
} else {
5758
let target = combat_stats.get(potential_target);
5859
if let Some(_target) = target {
5960
wants_to_melee.insert(entity, WantsToMelee{ target: potential_target }).expect("Add target failed");
60-
result = RunState::Ticking;
61+
return Some(RunState::Ticking);
6162
}
6263
}
6364
let door = doors.get_mut(potential_target);
@@ -68,8 +69,9 @@ pub fn try_move_player(delta_x: i32, delta_y: i32, ecs: &mut World) -> RunState
6869
let glyph = renderables.get_mut(potential_target).unwrap();
6970
glyph.glyph = rltk::to_cp437('/');
7071
viewshed.dirty = true;
71-
result = RunState::Ticking;
72+
return Some(RunState::Ticking);
7273
}
74+
None
7375
});
7476

7577
if !crate::spatial::is_blocked(destination_idx) {

chapter-57a-spatial/src/spatial/mod.rs

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
use std::sync::Mutex;
22
use specs::prelude::*;
3-
use crate::{ Map, tile_walkable };
3+
use crate::{ Map, tile_walkable, RunState };
44

55
struct SpatialMap {
66
blocked : Vec<(bool, bool)>,
@@ -63,6 +63,19 @@ where F : FnMut(Entity)
6363
}
6464
}
6565

66+
pub fn for_each_tile_content_with_gamemode<F>(idx: usize, mut f: F) -> RunState
67+
where F : FnMut(Entity)->Option<RunState>
68+
{
69+
let lock = SPATIAL_MAP.lock().unwrap();
70+
for entity in lock.tile_content[idx].iter() {
71+
if let Some(rs) = f(entity.0) {
72+
return rs;
73+
}
74+
}
75+
76+
RunState::AwaitingInput
77+
}
78+
6679
pub fn move_entity(entity: Entity, moving_from: usize, moving_to: usize) {
6780
let mut lock = SPATIAL_MAP.lock().unwrap();
6881
let mut entity_blocks = false;

chapter-58-itemstats/src/player.rs

Lines changed: 94 additions & 88 deletions
Original file line numberDiff line numberDiff line change
@@ -6,108 +6,114 @@ use super::{Position, Player, Viewshed, State, Map, RunState, Attributes, WantsT
66
EntityMoved, Door, BlocksTile, BlocksVisibility, Renderable, Pools, Faction,
77
raws::Reaction, Vendor, VendorMode};
88

9-
pub fn try_move_player(delta_x: i32, delta_y: i32, ecs: &mut World) -> RunState {
10-
let mut positions = ecs.write_storage::<Position>();
11-
let players = ecs.read_storage::<Player>();
12-
let mut viewsheds = ecs.write_storage::<Viewshed>();
13-
let entities = ecs.entities();
14-
let combat_stats = ecs.read_storage::<Attributes>();
15-
let map = ecs.fetch::<Map>();
16-
let mut wants_to_melee = ecs.write_storage::<WantsToMelee>();
17-
let mut entity_moved = ecs.write_storage::<EntityMoved>();
18-
let mut doors = ecs.write_storage::<Door>();
19-
let mut blocks_visibility = ecs.write_storage::<BlocksVisibility>();
20-
let mut blocks_movement = ecs.write_storage::<BlocksTile>();
21-
let mut renderables = ecs.write_storage::<Renderable>();
22-
let factions = ecs.read_storage::<Faction>();
23-
let mut result = RunState::AwaitingInput;
24-
25-
let mut swap_entities : Vec<(Entity, i32, i32)> = Vec::new();
26-
27-
for (entity, _player, pos, viewshed) in (&entities, &players, &mut positions, &mut viewsheds).join() {
28-
if pos.x + delta_x < 1 || pos.x + delta_x > map.width-1 || pos.y + delta_y < 1 || pos.y + delta_y > map.height-1 { return RunState::AwaitingInput; }
29-
let destination_idx = map.xy_idx(pos.x + delta_x, pos.y + delta_y);
30-
31-
crate::spatial::for_each_tile_content(destination_idx, |potential_target| {
32-
let mut hostile = true;
33-
if combat_stats.get(potential_target).is_some() {
34-
if let Some(faction) = factions.get(potential_target) {
35-
let reaction = crate::raws::faction_reaction(
36-
&faction.name,
37-
"Player",
38-
&crate::raws::RAWS.lock().unwrap()
39-
);
40-
if reaction != Reaction::Attack { hostile = false; }
41-
}
42-
}
43-
if !hostile {
44-
// Note that we want to move the bystander
45-
swap_entities.push((potential_target, pos.x, pos.y));
46-
47-
// Move the player
48-
pos.x = min(map.width-1 , max(0, pos.x + delta_x));
49-
pos.y = min(map.height-1, max(0, pos.y + delta_y));
50-
entity_moved.insert(entity, EntityMoved{}).expect("Unable to insert marker");
51-
52-
viewshed.dirty = true;
53-
let mut ppos = ecs.write_resource::<Point>();
54-
ppos.x = pos.x;
55-
ppos.y = pos.y;
56-
} else {
57-
let target = combat_stats.get(potential_target);
58-
if let Some(_target) = target {
59-
wants_to_melee.insert(entity, WantsToMelee{ target: potential_target }).expect("Add target failed");
60-
result = RunState::Ticking;
61-
}
62-
}
63-
let door = doors.get_mut(potential_target);
64-
if let Some(door) = door {
65-
door.open = true;
66-
blocks_visibility.remove(potential_target);
67-
blocks_movement.remove(potential_target);
68-
let glyph = renderables.get_mut(potential_target).unwrap();
69-
glyph.glyph = rltk::to_cp437('/');
70-
viewshed.dirty = true;
71-
result = RunState::Ticking;
9+
pub fn try_move_player(delta_x: i32, delta_y: i32, ecs: &mut World) -> RunState {
10+
let mut positions = ecs.write_storage::<Position>();
11+
let players = ecs.read_storage::<Player>();
12+
let mut viewsheds = ecs.write_storage::<Viewshed>();
13+
let entities = ecs.entities();
14+
let combat_stats = ecs.read_storage::<Attributes>();
15+
let map = ecs.fetch::<Map>();
16+
let mut wants_to_melee = ecs.write_storage::<WantsToMelee>();
17+
let mut entity_moved = ecs.write_storage::<EntityMoved>();
18+
let mut doors = ecs.write_storage::<Door>();
19+
let mut blocks_visibility = ecs.write_storage::<BlocksVisibility>();
20+
let mut blocks_movement = ecs.write_storage::<BlocksTile>();
21+
let mut renderables = ecs.write_storage::<Renderable>();
22+
let factions = ecs.read_storage::<Faction>();
23+
let mut result = RunState::AwaitingInput;
24+
let vendors = ecs.read_storage::<Vendor>();
25+
26+
let mut swap_entities : Vec<(Entity, i32, i32)> = Vec::new();
27+
28+
for (entity, _player, pos, viewshed) in (&entities, &players, &mut positions, &mut viewsheds).join() {
29+
if pos.x + delta_x < 1 || pos.x + delta_x > map.width-1 || pos.y + delta_y < 1 || pos.y + delta_y > map.height-1 { return RunState::AwaitingInput; }
30+
let destination_idx = map.xy_idx(pos.x + delta_x, pos.y + delta_y);
31+
32+
result = crate::spatial::for_each_tile_content_with_gamemode(destination_idx, |potential_target| {
33+
if let Some(_vendor) = vendors.get(potential_target) {
34+
return Some(RunState::ShowVendor{ vendor: potential_target, mode : VendorMode::Sell });
35+
}
36+
let mut hostile = true;
37+
if combat_stats.get(potential_target).is_some() {
38+
if let Some(faction) = factions.get(potential_target) {
39+
let reaction = crate::raws::faction_reaction(
40+
&faction.name,
41+
"Player",
42+
&crate::raws::RAWS.lock().unwrap()
43+
);
44+
if reaction != Reaction::Attack { hostile = false; }
7245
}
73-
});
74-
75-
if !crate::spatial::is_blocked(destination_idx) {
76-
let old_idx = map.xy_idx(pos.x, pos.y);
46+
}
47+
if !hostile {
48+
// Note that we want to move the bystander
49+
swap_entities.push((potential_target, pos.x, pos.y));
50+
51+
// Move the player
7752
pos.x = min(map.width-1 , max(0, pos.x + delta_x));
7853
pos.y = min(map.height-1, max(0, pos.y + delta_y));
79-
let new_idx = map.xy_idx(pos.x, pos.y);
8054
entity_moved.insert(entity, EntityMoved{}).expect("Unable to insert marker");
81-
crate::spatial::move_entity(entity, old_idx, new_idx);
82-
55+
8356
viewshed.dirty = true;
8457
let mut ppos = ecs.write_resource::<Point>();
8558
ppos.x = pos.x;
8659
ppos.y = pos.y;
87-
result = RunState::Ticking;
88-
match map.tiles[destination_idx] {
89-
TileType::DownStairs => result = RunState::NextLevel,
90-
TileType::UpStairs => result = RunState::PreviousLevel,
91-
_ => {}
60+
return Some(RunState::Ticking);
61+
} else {
62+
let target = combat_stats.get(potential_target);
63+
if let Some(_target) = target {
64+
wants_to_melee.insert(entity, WantsToMelee{ target: potential_target }).expect("Add target failed");
65+
return Some(RunState::Ticking);
9266
}
9367
}
94-
}
95-
96-
for m in swap_entities.iter() {
97-
let their_pos = positions.get_mut(m.0);
98-
if let Some(their_pos) = their_pos {
99-
let old_idx = map.xy_idx(their_pos.x, their_pos.y);
100-
their_pos.x = m.1;
101-
their_pos.y = m.2;
102-
let new_idx = map.xy_idx(their_pos.x, their_pos.y);
103-
crate::spatial::move_entity(m.0, old_idx, new_idx);
104-
result = RunState::Ticking;
68+
let door = doors.get_mut(potential_target);
69+
if let Some(door) = door {
70+
door.open = true;
71+
blocks_visibility.remove(potential_target);
72+
blocks_movement.remove(potential_target);
73+
let glyph = renderables.get_mut(potential_target).unwrap();
74+
glyph.glyph = rltk::to_cp437('/');
75+
viewshed.dirty = true;
76+
return Some(RunState::Ticking);
77+
}
78+
None
79+
});
80+
81+
if !crate::spatial::is_blocked(destination_idx) {
82+
let old_idx = map.xy_idx(pos.x, pos.y);
83+
pos.x = min(map.width-1 , max(0, pos.x + delta_x));
84+
pos.y = min(map.height-1, max(0, pos.y + delta_y));
85+
let new_idx = map.xy_idx(pos.x, pos.y);
86+
entity_moved.insert(entity, EntityMoved{}).expect("Unable to insert marker");
87+
crate::spatial::move_entity(entity, old_idx, new_idx);
88+
89+
viewshed.dirty = true;
90+
let mut ppos = ecs.write_resource::<Point>();
91+
ppos.x = pos.x;
92+
ppos.y = pos.y;
93+
result = RunState::Ticking;
94+
match map.tiles[destination_idx] {
95+
TileType::DownStairs => result = RunState::NextLevel,
96+
TileType::UpStairs => result = RunState::PreviousLevel,
97+
_ => {}
10598
}
10699
}
107-
108-
result
109100
}
110101

102+
for m in swap_entities.iter() {
103+
let their_pos = positions.get_mut(m.0);
104+
if let Some(their_pos) = their_pos {
105+
let old_idx = map.xy_idx(their_pos.x, their_pos.y);
106+
their_pos.x = m.1;
107+
their_pos.y = m.2;
108+
let new_idx = map.xy_idx(their_pos.x, their_pos.y);
109+
crate::spatial::move_entity(m.0, old_idx, new_idx);
110+
result = RunState::Ticking;
111+
}
112+
}
113+
114+
result
115+
}
116+
111117
pub fn try_next_level(ecs: &mut World) -> bool {
112118
let player_pos = ecs.fetch::<Point>();
113119
let map = ecs.fetch::<Map>();

0 commit comments

Comments
 (0)