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 } ;
54use libcraft_items:: EnchantmentKind ;
6- use quill_common:: { entities :: Player , entity_init :: EntityInit } ;
5+ use quill_common:: components :: Instabreak ;
76
87pub struct DestroyStateChange ( pub ValidBlockPosition , pub u8 ) ;
98
10- use crate :: { Game , World } ;
9+ use crate :: { entities :: player :: HotbarSlot , Game , Window , World } ;
1110
1211// Comparing to 0.7 ensures good feeling in the client
1312pub const BREAK_THRESHOLD : f32 = 0.7 ;
1413
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" ) ?
6528 } ;
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) ?;
11530 }
31+ Ok ( true )
11632}
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 )
12245}
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)
14267}
14368#[ derive( Clone ) ]
14469pub struct ActiveBreaker {
14570 pub position : ValidBlockPosition ,
14671 pub drop_item : bool ,
147- pub fake_finished : bool ,
14872 pub progress : f32 ,
14973 pub damage : f32 ,
15074}
@@ -154,7 +78,7 @@ impl ActiveBreaker {
15478 self . progress += self . damage ;
15579 let after = self . destroy_stage ( ) ;
15680 let break_block = self . can_break ( ) ;
157- let change_stage = before != after || break_block ;
81+ let change_stage = break_block || before != after;
15882 ( break_block, change_stage)
15983 }
16084 /// Check if the block has been damaged enough to break.
@@ -175,21 +99,16 @@ impl ActiveBreaker {
17599 ( Some ( _) , None ) => false ,
176100 ( Some ( tools) , Some ( tool) ) => tools. contains ( & tool. item ( ) ) ,
177101 } ;
178- let dig_multiplier = block
102+ let dig_multiplier = block // TODO: calculate with Haste effect
179103 . dig_multipliers ( )
180104 . iter ( )
181105 . 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) )
186107 } )
187108 . unwrap_or ( 1.0 ) ;
188109 let effi_level = equipped_item
189110 . 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 ) ) ;
193112 let effi_speed = effi_level. map ( |level| level * level + 1 ) . unwrap_or ( 0 ) as f32 ;
194113 let damage = if harvestable {
195114 ( dig_multiplier + effi_speed) / block. hardness ( ) / 30.0
@@ -199,25 +118,16 @@ impl ActiveBreaker {
199118 Some ( Self {
200119 position : block_pos,
201120 drop_item : true ,
202- fake_finished : false ,
203- progress : 0.0 ,
121+ progress : damage,
204122 damage,
205123 } )
206124 }
207125 /// Get the destroying progress.
208126 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
214128 }
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 ( ) )
221131 }
222132}
223133
@@ -226,36 +136,19 @@ pub fn register(systems: &mut SystemExecutor<Game>) {
226136}
227137
228138fn process_block_breaking ( game : & mut Game ) -> SysResult {
229- let mut break_queue = vec ! [ ] ;
230139 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 ( ) ;
233142 if update_stage {
234143 update_queue. push ( entity) ;
235144 }
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- }
242145 }
243146 for entity in update_queue {
244147 let event = game
245148 . ecs
246- . get_mut :: < BlockBreaker > ( entity) ?
247- . destroy_change_event ( )
248- . unwrap ( ) ;
149+ . get_mut :: < ActiveBreaker > ( entity) ?
150+ . destroy_change_event ( ) ;
249151 game. ecs . insert_entity_event ( entity, event) ?;
250152 }
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- }
260153 Ok ( ( ) )
261154}
0 commit comments