indexing description: "Brick Breaker Blocks" date: "$Date$" revision: "$Revision$" class BB_BLOCK inherit BB_OBJECT redefine out select out end BB_SHARED_AUDIO rename out as out_audio export {NONE} all end creation make, make_from_string feature -- Initialization make (a_row, a_column: INTEGER; original_number_of_hits: INTEGER; a_level: like level) is -- in column `column' and row `row' which has to be hit `number_of_hits' times require a_level_not_void: a_level /= Void original_number_of_hits_allowed: original_number_of_hits >= -1 and original_number_of_hits <= 9 and original_number_of_hits /= 0 valid_row: a_row >= 1 and a_row <= number_of_rows valid_column: a_column >= 1 and a_column <= number_of_columns do level := a_level number_of_hits := original_number_of_hits column := a_column row := a_row load_correct_image create center.make ((column - 1) * image.width + half_width, (row - 1) * image.height + half_height) is_collidable := True initialize object_type := Object_type_block item_type := Item_type_random_item level.blocks.force_last (Current) level.collision_detector.add (Current, object_type) level.block_field.put (True, row, column) if number_of_hits > 0 then level.set_remaining_blocks (level.remaining_blocks + 1) end ensure calumn_set: column = a_column row_set: row = a_row number_of_hits_set: number_of_hits = original_number_of_hits level_set: level = a_level end make_from_string (block_string: STRING; a_level: like level) is -- create a block from `block_string'. Look at `out' for the string format require a_level_not_void: a_level /= Void valid_block_string: is_valid_block_string (block_string) local a_column, a_row, hits, an_item_type: INTEGER block_parameters: LIST [STRING] do block_parameters := block_string.split (' ') a_column := block_parameters.i_th (1).to_integer a_row := block_parameters.i_th (2).to_integer hits := block_parameters.i_th (3).to_integer an_item_type := block_parameters.i_th (4).to_integer make (a_row, a_column, hits, a_level) set_item_type (an_item_type) end feature -- Access out: STRING is -- represent the block as a string " " do Result := column.out + " " + row.out + " " + number_of_hits.out + " " + item_type.out end feature -- Status report number_of_hits: INTEGER -- how often the block has to be hit, 0 is undestructable column: INTEGER -- column of `Current' row: INTEGER -- row of `Current' has_upper_neighbour: BOOLEAN is -- has `Current' an upper neighbour do Result := row = 1 or level.block_field.item (row - 1, column) end has_lower_neighbour: BOOLEAN is -- has `Current' a lower neighbour do if row = level.Number_of_rows then -- since the lowest row is not at the border, there is no lower neighbour Result := False else Result := level.block_field.item (row + 1, column) end end has_left_neighbour: BOOLEAN is -- has `Current' a left neighbour do Result := column = 1 or level.block_field.item (row, column - 1) end has_right_neighbour: BOOLEAN is -- has `Current' a right neighbour do Result := column = level.Number_of_columns or level.block_field.item (row, column + 1) end item_type: INTEGER -- item type (see BB_CONSTANTS for a list) is_collidable: BOOLEAN -- can `Current' collide with a block? (is used for disappearing undestroyable blocks) feature -- Status setting load_correct_image is -- loads the correct image for the block do image := level.block_images.item (number_of_hits) half_height := image.height / 2 half_width := image.width / 2 create image_offset.make (-half_width, -half_height) radius := sqrt((image_offset.x)^2 + (image_offset.y)^2) end set_item_type (an_integer: INTEGER) is -- set `item_type' to `an_integer' require an_integer_in_bounds: an_integer >= -1 and an_integer <= level.Number_of_items do item_type := an_integer ensure item_type_set: item_type = an_integer end set_number_of_hits (an_integer: INTEGER) is -- set `number_of_hits' to `an_integer' require an_integer_in_bounds: an_integer >= -1 and an_integer <= 9 do number_of_hits := an_integer load_correct_image ensure number_of_hits_set: number_of_hits = an_integer end set_collidable (a_boolean: BOOLEAN) is -- set `is_collidable' do is_collidable := a_boolean ensure value_set: is_collidable = a_boolean end feature -- Miscellaneous get_hit (a_ball: BB_BALL) is -- a block gets hit by `a_ball' local score_increment: DOUBLE do if number_of_hits > 1 then set_number_of_hits (number_of_hits - 1) load_correct_image score_increment := (100 + 10 * a_ball.number_of_successive_hits) * level.score_multiplier level.set_score (level.score + score_increment.rounded) a_ball.set_number_of_successive_hits (a_ball.number_of_successive_hits + 1) -- play the sound play_sound (Sound_block_hit) elseif number_of_hits = 1 then if not level.blocks_to_destroy.has (Current) then level.blocks_to_destroy.force_last (Current) score_increment := (100 + 10 * a_ball.number_of_successive_hits) * level.score_multiplier level.set_score (level.score + score_increment.rounded) end a_ball.set_number_of_successive_hits (a_ball.number_of_successive_hits + 1) number_of_hits := 0 elseif number_of_hits = -1 then -- play the sound play_sound (Sound_block_grey_hit) end end destroy is -- destroy `Current' local block_cursor: DS_LINKED_LIST_CURSOR [BB_BLOCK] an_item: BB_ITEM created_item_type, random_number: INTEGER do -- play the sound play_sound (Sound_block_destroy) -- create an item level.random.forth if item_type = Item_type_random_item and (level.random.item \\ 1000) / 1000 <= Item_chance then -- calculate which item is generated. See comment of `Item_probability_array' in BB_CONSTANTS -- using the `LEVEL.Item_probability_array'on purpose to take usage of the "once" routine level.random.forth random_number := level.random.item \\ level.Item_probability_array.item (Number_of_items) from created_item_type := 1 until level.item_probability_array.item (created_item_type) >= random_number loop created_item_type := created_item_type + 1 end create an_item.make (Current, created_item_type) elseif item_type /= Item_type_random_item and item_type /= Item_type_nothing then create an_item.make (Current, item_type) end -- destroy the block block_cursor := level.blocks.new_cursor block_cursor.start block_cursor.search_forth (Current) block_cursor.remove level.collision_detector.remove (Current) level.block_field.put (False, row, column) if number_of_hits /= -1 then level.set_remaining_blocks (level.remaining_blocks - 1) end end is_valid_block_string (block_string: STRING): BOOLEAN is -- is `block_string' a valid block string? (see `out' for format) local block_parameters: LIST [STRING] an_integer: INTEGER do if block_string = Void then Result := False else block_parameters := block_string.split (' ') Result := block_parameters.count = 4 if Result then Result := Result and block_parameters.i_th (1).is_integer and block_parameters.i_th (2).is_integer and block_parameters.i_th (3).is_integer and block_parameters.i_th (4).is_integer end if Result then an_integer := block_parameters.i_th (1).to_integer Result := Result and an_integer >= 1 and an_integer <= number_of_columns an_integer := block_parameters.i_th (2).to_integer Result := Result and an_integer >= 1 and an_integer <= number_of_rows an_integer := block_parameters.i_th (3).to_integer Result := Result and an_integer >= -1 and an_integer <= 9 an_integer := block_parameters.i_th (4).to_integer Result := Result and an_integer >= -1 and an_integer <= Number_of_items end end end invariant number_of_hits_allowed: number_of_hits >= -1 and number_of_hits <= 9 level_set: level /= Void end