1
- use std:: mem;
2
-
3
- use base:: { BlockKind , ItemStack , ValidBlockPosition } ;
4
- use ecs:: { EntityBuilder , SysResult , SystemExecutor } ;
1
+ use anyhow:: Context ;
2
+ use base:: { inventory:: SLOT_HOTBAR_OFFSET , BlockKind , ItemStack , ValidBlockPosition } ;
3
+ use ecs:: { Entity , SysResult , SystemExecutor } ;
5
4
use libcraft_items:: EnchantmentKind ;
6
- use quill_common:: { entities :: Player , entity_init :: EntityInit } ;
5
+ use quill_common:: components :: Instabreak ;
7
6
8
7
pub struct DestroyStateChange ( pub ValidBlockPosition , pub u8 ) ;
9
8
10
- use crate :: { Game , World } ;
9
+ use crate :: { entities :: player :: HotbarSlot , Game , Window , World } ;
11
10
12
11
// Comparing to 0.7 ensures good feeling in the client
13
12
pub const BREAK_THRESHOLD : f32 = 0.7 ;
14
13
15
- #[ derive( Clone ) ]
16
- pub enum BlockBreaker {
17
- Active ( ActiveBreaker ) ,
18
- Finished ( FinishedBreaker ) ,
19
- Inactive ,
20
- }
21
- impl BlockBreaker {
22
- /// Create a new active instance pointing to `block_pos`. Calculates the time needed using `world.block_at(block_pos)` and `equipped_item`.
23
- pub fn new (
24
- world : & mut World ,
25
- block_pos : ValidBlockPosition ,
26
- equipped_item : Option < & ItemStack > ,
27
- ) -> Self {
28
- ActiveBreaker :: new ( world, block_pos, equipped_item)
29
- . map ( Self :: Active )
30
- . unwrap_or ( Self :: Inactive )
31
- }
32
- /// If active, produces a `DestroyStateChange` event for the adequate position.
33
- pub fn destroy_change_event ( & self ) -> Option < DestroyStateChange > {
34
- Some ( DestroyStateChange ( self . position ( ) ?, self . destroy_stage ( ) ) )
35
- }
36
- /// If active or finished, returns the pointed to position.
37
- pub fn position ( & self ) -> Option < ValidBlockPosition > {
38
- match self {
39
- BlockBreaker :: Active ( a) => Some ( a. position ) ,
40
- BlockBreaker :: Finished ( f) => Some ( f. position ) ,
41
- BlockBreaker :: Inactive => None ,
42
- }
43
- }
44
- /// If active, returns the underlying `ActiveBreaker`.
45
- pub fn active ( & self ) -> Option < & ActiveBreaker > {
46
- match self {
47
- Self :: Active ( a) => Some ( a) ,
48
- _ => None ,
49
- }
50
- }
51
- /// If finished, returns the underlying `FinishedBreaker`.
52
- pub fn finished ( & self ) -> Option < & FinishedBreaker > {
53
- match self {
54
- Self :: Finished ( f) => Some ( f) ,
55
- _ => None ,
56
- }
57
- }
58
- /// Progresses block breaking. Returns a (newly_finished, do_destry_state_change) tuple.
59
- /// If this operation finishes block breaking, this turns `self` into `Self::Finished` with the same position.
60
- pub fn tick ( & mut self ) -> ( bool , bool ) {
61
- let ( block_break, stage_update) = if let Self :: Active ( breaker) = self {
62
- breaker. tick ( )
63
- } else {
64
- ( false , false )
14
+ pub fn start_digging (
15
+ game : & mut Game ,
16
+ player : Entity ,
17
+ position : ValidBlockPosition ,
18
+ ) -> anyhow:: Result < bool > {
19
+ if game. ecs . get :: < Instabreak > ( player) ?. 0 {
20
+ game. break_block ( position) ;
21
+ } else {
22
+ let breaker = {
23
+ let window = game. ecs . get :: < Window > ( player) ?;
24
+ let hotbar_slot = game. ecs . get :: < HotbarSlot > ( player) ?. get ( ) ;
25
+ let main_hand = window. item ( SLOT_HOTBAR_OFFSET + hotbar_slot) ?;
26
+ ActiveBreaker :: new ( & mut game. world , position, main_hand. option_ref ( ) )
27
+ . context ( "Cannot mine this block" ) ?
65
28
} ;
66
- if block_break {
67
- let fin = match mem:: take ( self ) {
68
- Self :: Active ( a) => a. finish ( ) ,
69
- _ => unreachable ! ( ) ,
70
- } ;
71
- * self = Self :: Finished ( fin) ;
72
- }
73
- ( block_break, stage_update)
74
- }
75
- /// Returns the block destroying progress in a range of 0 - 9. When inactive or finished, returns 10.
76
- pub fn destroy_stage ( & self ) -> u8 {
77
- match self {
78
- BlockBreaker :: Active ( a) => a. destroy_stage ( ) ,
79
- _ => 10 ,
80
- }
81
- }
82
- /// Set `self` to `Self::Inactive`.
83
- pub fn cancel ( & mut self ) {
84
- * self = Self :: Inactive ;
85
- }
86
- /// Check if the breaker points to `pos`. Returns `true` when `self` is `Self::Inactive`.
87
- pub fn matches_position ( & self , pos : ValidBlockPosition ) -> bool {
88
- match self {
89
- BlockBreaker :: Active ( a) => a. position == pos,
90
- BlockBreaker :: Finished ( f) => f. position == pos,
91
- BlockBreaker :: Inactive => true ,
92
- }
93
- }
94
- /// Attempts to finish breaking the target block, optionally turning `self` into `Self::Finished`.
95
- pub fn try_finish ( & mut self ) -> Option < FinishedBreaker > {
96
- let this = self . clone ( ) ;
97
- match this {
98
- BlockBreaker :: Active ( a) => {
99
- if a. can_break ( ) {
100
- let fin = a. finish ( ) ;
101
- * self = Self :: Finished ( fin. clone ( ) ) ;
102
- Some ( fin)
103
- } else {
104
- None
105
- }
106
- }
107
- BlockBreaker :: Finished ( f) => Some ( f) ,
108
- BlockBreaker :: Inactive => None ,
109
- }
110
- }
111
- }
112
- impl Default for BlockBreaker {
113
- fn default ( ) -> Self {
114
- Self :: Inactive
29
+ game. ecs . insert ( player, breaker) ?;
115
30
}
31
+ Ok ( true )
116
32
}
117
- #[ derive( Clone ) ]
118
- pub struct FinishedBreaker {
119
- pub position : ValidBlockPosition ,
120
- pub drop_item : bool ,
121
- pub fake_finished : bool ,
33
+ pub fn cancel_digging (
34
+ game : & mut Game ,
35
+ player : Entity ,
36
+ position : ValidBlockPosition ,
37
+ ) -> anyhow:: Result < bool > {
38
+ if game. ecs . get :: < ActiveBreaker > ( player) . is_err ( ) {
39
+ return Ok ( false ) ;
40
+ }
41
+ game. ecs . remove :: < ActiveBreaker > ( player) ?;
42
+ game. ecs
43
+ . insert_entity_event ( player, DestroyStateChange ( position, 10 ) ) ?;
44
+ Ok ( true )
122
45
}
123
- impl FinishedBreaker {
124
- /// Breaks the targeted block and spawns its drops. TODO: make drops work.
125
- pub fn break_block ( & self , game : & mut Game ) -> SysResult {
126
- let target_block = match game. block ( self . position ) {
127
- Some ( b) => b,
128
- None => anyhow:: bail!( "cannot break unloaded block" ) ,
129
- } ;
130
- game. break_block ( self . position ) ;
131
- if let Some ( _item_drop) = base:: Item :: from_name ( target_block. kind ( ) . name ( ) ) {
132
- if !self . drop_item {
133
- return Ok ( ( ) ) ;
134
- }
135
- let mut item_entity = EntityBuilder :: new ( ) ;
136
- crate :: entities:: item:: build_default ( & mut item_entity) ;
137
- let builder = game. create_entity_builder ( self . position . position ( ) , EntityInit :: Item ) ;
138
- game. spawn_entity ( builder) ;
139
- }
140
- Ok ( ( ) )
141
- }
46
+ pub fn finish_digging (
47
+ game : & mut Game ,
48
+ player : Entity ,
49
+ position : ValidBlockPosition ,
50
+ ) -> anyhow:: Result < bool > {
51
+ if game. ecs . get :: < Instabreak > ( player) ?. 0 {
52
+ return Ok ( true ) ;
53
+ }
54
+ let success = if let Ok ( breaker) = game. ecs . get :: < ActiveBreaker > ( player) {
55
+ breaker. can_break ( )
56
+ } else {
57
+ false
58
+ } ;
59
+ if success {
60
+ let pos = game. ecs . get :: < ActiveBreaker > ( player) ?. position ;
61
+ game. break_block ( pos) ; // TODO: drop an item
62
+ game. ecs . remove :: < ActiveBreaker > ( player) ?;
63
+ }
64
+ game. ecs
65
+ . insert_entity_event ( player, DestroyStateChange ( position, 10 ) ) ?;
66
+ Ok ( success)
142
67
}
143
68
#[ derive( Clone ) ]
144
69
pub struct ActiveBreaker {
145
70
pub position : ValidBlockPosition ,
146
71
pub drop_item : bool ,
147
- pub fake_finished : bool ,
148
72
pub progress : f32 ,
149
73
pub damage : f32 ,
150
74
}
@@ -154,7 +78,7 @@ impl ActiveBreaker {
154
78
self . progress += self . damage ;
155
79
let after = self . destroy_stage ( ) ;
156
80
let break_block = self . can_break ( ) ;
157
- let change_stage = before != after || break_block ;
81
+ let change_stage = break_block || before != after;
158
82
( break_block, change_stage)
159
83
}
160
84
/// Check if the block has been damaged enough to break.
@@ -175,21 +99,16 @@ impl ActiveBreaker {
175
99
( Some ( _) , None ) => false ,
176
100
( Some ( tools) , Some ( tool) ) => tools. contains ( & tool. item ( ) ) ,
177
101
} ;
178
- let dig_multiplier = block
102
+ let dig_multiplier = block // TODO: calculate with Haste effect
179
103
. dig_multipliers ( )
180
104
. iter ( )
181
105
. find_map ( |( item, speed) | {
182
- equipped_item
183
- . and_then ( |e| {
184
- bool:: then_some ( e. item ( ) == * item, * speed)
185
- } )
106
+ equipped_item. and_then ( |e| bool:: then_some ( e. item ( ) == * item, * speed) )
186
107
} )
187
108
. unwrap_or ( 1.0 ) ;
188
109
let effi_level = equipped_item
189
110
. and_then ( ItemStack :: metadata)
190
- . and_then ( |meta| {
191
- meta. get_enchantment_level ( EnchantmentKind :: Efficiency )
192
- } ) ;
111
+ . and_then ( |meta| meta. get_enchantment_level ( EnchantmentKind :: Efficiency ) ) ;
193
112
let effi_speed = effi_level. map ( |level| level * level + 1 ) . unwrap_or ( 0 ) as f32 ;
194
113
let damage = if harvestable {
195
114
( dig_multiplier + effi_speed) / block. hardness ( ) / 30.0
@@ -199,25 +118,16 @@ impl ActiveBreaker {
199
118
Some ( Self {
200
119
position : block_pos,
201
120
drop_item : true ,
202
- fake_finished : false ,
203
- progress : 0.0 ,
121
+ progress : damage,
204
122
damage,
205
123
} )
206
124
}
207
125
/// Get the destroying progress.
208
126
pub fn destroy_stage ( & self ) -> u8 {
209
- if self . fake_finished {
210
- 10
211
- } else {
212
- ( self . progress / BREAK_THRESHOLD * 9.0 ) . round ( ) as u8
213
- }
127
+ ( self . progress * 9.0 ) . round ( ) as u8
214
128
}
215
- pub fn finish ( self ) -> FinishedBreaker {
216
- FinishedBreaker {
217
- position : self . position ,
218
- drop_item : self . drop_item ,
219
- fake_finished : self . fake_finished ,
220
- }
129
+ pub fn destroy_change_event ( & self ) -> DestroyStateChange {
130
+ DestroyStateChange ( self . position , self . destroy_stage ( ) )
221
131
}
222
132
}
223
133
@@ -226,36 +136,19 @@ pub fn register(systems: &mut SystemExecutor<Game>) {
226
136
}
227
137
228
138
fn process_block_breaking ( game : & mut Game ) -> SysResult {
229
- let mut break_queue = vec ! [ ] ;
230
139
let mut update_queue = vec ! [ ] ;
231
- for ( entity, breaker) in game. ecs . query :: < & mut BlockBreaker > ( ) . iter ( ) {
232
- let ( break_block , update_stage) = breaker. tick ( ) ;
140
+ for ( entity, breaker) in game. ecs . query :: < & mut ActiveBreaker > ( ) . iter ( ) {
141
+ let ( _ , update_stage) = breaker. tick ( ) ;
233
142
if update_stage {
234
143
update_queue. push ( entity) ;
235
144
}
236
- // Break block when client requests to finish in order to prevent desyncs
237
- if break_block && breaker. finished ( ) . unwrap ( ) . fake_finished
238
- || game. ecs . get :: < Player > ( entity) . is_err ( )
239
- {
240
- break_queue. push ( entity) ;
241
- }
242
145
}
243
146
for entity in update_queue {
244
147
let event = game
245
148
. ecs
246
- . get_mut :: < BlockBreaker > ( entity) ?
247
- . destroy_change_event ( )
248
- . unwrap ( ) ;
149
+ . get_mut :: < ActiveBreaker > ( entity) ?
150
+ . destroy_change_event ( ) ;
249
151
game. ecs . insert_entity_event ( entity, event) ?;
250
152
}
251
- for entity in break_queue. into_iter ( ) {
252
- let breaker = game
253
- . ecs
254
- . get :: < BlockBreaker > ( entity) ?
255
- . finished ( )
256
- . unwrap ( )
257
- . clone ( ) ;
258
- breaker. break_block ( game) ?;
259
- }
260
153
Ok ( ( ) )
261
154
}
0 commit comments