note description: "[ A {VIEW} that corresponds to the game board in Victory In the Pacific (VITP). This view has widgets that hold and allow control of game actions (e.g. movement, attacks, repositions, etc) of game elements (e.g. ships, control markers, ports, etc.) ]" author: "Jimmy J. Johnson" class BOARD_VIEW inherit VITP_CELL_VIEW redefine create_interface_objects, initialize, add_actions, set_target, draw, pick_notified_operations, pick_ended_operations end FONT_AND_COLOR_CONSTANTS export {NONE} all undefine default_create, copy end POSITION_CONSTANTS export {NONE} all undefine default_create, copy redefine playing_area_ratio end create make feature {NONE} -- Initialization create_interface_objects -- Create objects to be used by `Current' in `initialize' -- Implemented by descendants to create attached objects -- in order to adhere to void-safety due to the implementation bridge pattern. do print ("BOARD_VIEW.create_interface_objects %N") Precursor {VITP_CELL_VIEW} print ("%T BOARD_VIEW.create_interface_objects before creating widget_factory %N") create widget_factory.make (game) print ("%T BOARD_VIEW.create_interface_objects after creatingh widget_factory %N") -- create dot -- create bounding_figure -- Create for Void safety -- create mouse_offset create last_mouse_position -- create containers create task_force_widgets.make (100) create highlighted_widgets.make -- create the corner points used to determine the scale factor create top_left_dot create bottom_right_dot -- create container for the grid cells (rows, columns) -- create cell_array.make (playing_area_height // cell_size, end initialize -- Called after `Default_create' do Precursor {VITP_CELL_VIEW} -- world.extend (dragging_arrow) -- dragging_arrow.hide -- -- create the `handle' -- create handle -- world.extend (handle) build_border -- Attack widgets go on top of all other widgets except the dots -- build_corner_dots -- Place the dots from {POSITION_CONSTANTS} into Current -- The dots move based on scaling of the view, allowing -- cell positions to be determined regardless of the -- actual size of the view. add_widgets world.extend (top_left_dot) world.extend (bottom_right_dot) end add_widgets -- Add the sea areas, ports, and unit widgets to the `world', -- hiding the unit widgets until needed local wf: like widget_factory t: like widget_factory.widgets w: VITP_WIDGET do wf := widget_factory -- Add sea areas first t := wf.sea_area_widgets from t.start until t.after loop -- world.extend (t.item_for_iteration) add_model (t.item_for_iteration) t.forth end -- Add ports t := wf.port_widgets from t.start until t.after loop -- world.extend (t.item_for_iteration) add_model (t.item_for_iteration) t.forth end -- Add unit widgets t := wf.attack_widgets from t.start until t.after loop w := t.item_for_iteration print ("{BOARD_VIEW}.add_widgets: Adding " + w.target.name + " at (" + w.x.out + ", " + w.y.out + ") %N") -- world.extend (w) add_model (w) -- w.hide t.forth end end build_corner_dots -- Create the two dots that will mark the top-left and bottom-right corners of -- the board. These aid calculation of the current scaling factor and scolling do top_left_dot.set_x_y (playing_area_left, playing_area_top) bottom_right_dot.set_x_y (playing_area_right, playing_area_bottom) top_left_dot.set_line_width (10) bottom_right_dot.set_line_width (10) world.extend (top_left_dot) world.extend (bottom_right_dot) end add_actions -- Add actions to the widgets local w: ATTACK_UNIT_WIDGET do -- Do not call precursor -- asia_widget.pointer_button_press_actions.force_extend (agent toggle_land) -- pointer_motion_actions.extend (agent on_pointer_moved) -- pointer_button_release_actions.extend (agent on_button_released) end build_border -- Put a border around the game board to hide some of the land overhangs local poly: EV_MODEL_POLYGON s: INTEGER bw, bh: INTEGER_32 c: EV_COLOR do s := 5 c := Medium_grey bw := board_width bh := board_height -- top create poly.default_create poly.set_line_width (0) poly.extend_point (create {EV_COORDINATE}.make (-s, -s)) poly.extend_point (create {EV_COORDINATE}.make (bw + s, -s)) poly.extend_point (create {EV_COORDINATE}.make (bw + s, 0)) poly.extend_point (create {EV_COORDINATE}.make (-s, 0)) poly.set_background_color (c) poly.set_foreground_color (c) world.extend (poly) -- botton create poly.default_create poly.set_line_width (0) poly.extend_point (create {EV_COORDINATE}.make (-s, bh)) poly.extend_point (create {EV_COORDINATE}.make (bw + s, bh)) poly.extend_point (create {EV_COORDINATE}.make (bw + s, bh + s)) poly.extend_point (create {EV_COORDINATE}.make (-s, bh + s)) poly.set_background_color (c) poly.set_foreground_color (c) world.extend (poly) -- left create poly.default_create poly.set_line_width (0) poly.extend_point (create {EV_COORDINATE}.make (-s, -s)) poly.extend_point (create {EV_COORDINATE}.make (0, -s)) poly.extend_point (create {EV_COORDINATE}.make (0, bh + s)) poly.extend_point (create {EV_COORDINATE}.make (-s, bh + s)) poly.set_background_color (c) poly.set_foreground_color (c) world.extend (poly) -- right create poly.default_create poly.set_line_width (0) poly.extend_point (create {EV_COORDINATE}.make (bw, -s)) poly.extend_point (create {EV_COORDINATE}.make (bw + s, -s)) poly.extend_point (create {EV_COORDINATE}.make (bw + s, bh + s)) poly.extend_point (create {EV_COORDINATE}.make (bw, bh + s)) poly.set_background_color (c) poly.set_foreground_color (c) world.extend (poly) end feature -- Basic operations set_target (a_item: like game) -- Associate `a_item' with Current. do Precursor (a_item) -- Build a polygon that defines the boundary of current for which we -- find the centerpoint, the `dot'. Other widgets can then be built -- and if desired placed in Current relative to `dot' widget_factory.set_game (game) -- paint -- set_pebble (a_item) -- set_accept_cursor (sub_pixmap (bounding_box)) end draw -- Redraw the view -- This means the game has changed in some way -- (e.g. a ship was added). do if not is_view_empty then -- update_attack_widgets -- should be no need to draw the whole view unless the game -- was changed. end end feature -- Access widget_factory: ALL_WIDGETS_FACTORY -- Holds all the widgets on the board playing_area_ratio: REAL_64 -- Ratio of the playing_area_width' to the [possibly] -- scaled size of the `playing_area_width'. Default = one, -- but redefined here to use the distance between the -- two corner dots. do Result := playing_area_width / (bottom_right_dot.x - top_left_dot.x) end feature -- Query cell_at (a_x, a_y: INTEGER_32): VITP_CELL -- The cell at the screen coordinates `a_x', `a_y' local x, y: INTEGER_32 do x := ((a_x * playing_area_ratio) / cell_size).truncated_to_integer + 1 y := ((a_y * playing_area_ratio) / cell_size).truncated_to_integer + 1 Result := game.cell (x, y) end screen_coordinates (a_cell: VITP_CELL): TUPLE [x, y: INTEGER_32] -- The screen coordinates corresponding to `a_cell' local x, y: INTEGER_32 do x := ((a_cell.index.x * cell_size) / playing_area_ratio).truncated_to_integer y := ((a_cell.index.y * cell_size) / playing_area_ratio).truncated_to_integer Result := [x, y] end feature -- Status report is_forwarding: BOOLEAN -- Has a widget been brought to the top? is_dragging: BOOLEAN -- Are we in the process of sliding a widget or a group of widgets? -- is_picking: BOOLEAN -- -- Are we in the process of a pick-and-put operation? is_repositioning: BOOLEAN -- Is a widget being repositioned? is_moving: BOOLEAN -- Is a unit being moved to a new location? is_highlighting: BOOLEAN -- Is a widget or group of widgets being emphasized? is_highlighting_location: BOOLEAN -- Is one or more port widgets being emphasized? is_highlighting_units: BOOLEAN -- Is one or more unit widgets being emphasized? is_highlighting_task_force: BOOLEAN -- Is one or more {TASK_FORCE_WIDGET}s being emphasized? is_processing_task_force: BOOLEAN -- A {TASK_FORCE_WIDGET} is being clicked/move/released feature -- Actions pick_notified_operations (a_target: ANY) -- React to the pick of `a_target' from some view local sop: like game.sequence_of_play set: LINKED_SET [PORT] p: PORT pw: PORT_WIDGET do Precursor (a_target) print ("{BOARD_VIEW}.pick_notified_operations on {" + generating_type.name + "}%N") sop := game.sequence_of_play if sop.is_reinforcement_step and then attached {ATTACK_UNIT} a_target as t then set := t.reinforceable_ports print ("%T undimming " + set.count.out + " areas %N") from set.start until set.after loop p := set.item print ("%T%T making " + p.name + " brighter %N") pw := widget_factory.port_widgets.definite_item (p) pw.set_dimming_level ({DIMABLE}.bright) pw.activate_drop_action highlighted_widgets.extend (pw) set.forth end -- application.process_graphical_events end end pick_ended_operations -- React to the end of a pick and drop operation do Precursor print ("{BOARD_VIEW}.pick_ended_operations on {" + generating_type.name + "} %N") from highlighted_widgets.start until highlighted_widgets.after loop highlighted_widgets.item.deactivate_drop_action highlighted_widgets.item.restore_dimming_level highlighted_widgets.forth end highlighted_widgets.wipe_out end on_pointer_enter (a_widget: VITP_WIDGET) -- React to a pointer entering `a_widget' local tfw: TASK_FORCE_WIDGET do io.put_string ("BOARD_WORLD.on_pointer_enter: on " + a_widget.target.name + " %N") -- if attached {ATTACK_UNIT_WIDGET} a_widget as w then -- if w.unit.is_task_force then -- tfw := task_force_widgets.widget (w.unit.task_force) -- forward_widget_to_top (w, tfw) -- end -- end end on_pointer_leave (a_widget: VITP_WIDGET) -- React to a pointer leaving a `a_widget' do io.put_string ("BOARD_WORLD.on_pointer_leave: on " + a_widget.target.name + " %N") -- if is_forwarding then -- restore_forwarded_widget -- end end on_button_pressed (a_widget: VITP_WIDGET; ax: INTEGER; ay: INTEGER; a_button: INTEGER) -- React to a pointer button pressed notification from `a_widget' do io.put_string ("BOARD_WORLD.on_button_pressed: button " + a_button.out + " pressed at (" + ax.out + ", " + ay.out + ") on " + a_widget.target.name + " %N") -- last_mouse_position.set_precise (ax, ay) -- if is_forwarding then -- restore_forwarded_widget -- end -- if a_button = 1 then -- if attached {ATTACK_UNIT_WIDGET} a_widget as w then -- highlight_unit (w.unit) -- begin_widget_reposition (w, ax, ay) -- elseif attached {LOCATION_WIDGET} a_widget as w then -- highlight_location (w.location) -- highlight_location_occupants (w.location) -- end -- end end on_button_double_pressed (a_widget: VITP_WIDGET; ax: INTEGER; ay: INTEGER; a_button: INTEGER) -- React to a pointer button released notification from `a_widget' do io.put_string ("BOARD_WORLD.on_button_double_pressed: button " + a_button.out + " double-pressed at (" + ax.out + ", " + ay.out + ") on " + a_widget.target.name + "%N") if a_button = 1 then if attached {TASK_FORCE_WIDGET} a_widget as w and then not w.point_on_bounding_figure (ax, ay) then if w.is_stacked then w.tile else w.stack end -- Next lines to prevent undesired movement is_repositioning := false end end end on_button_released (a_widget: VITP_WIDGET; ax: INTEGER; ay: INTEGER; a_button: INTEGER) -- React to a pointer button released notification from `a_widget' do io.put_string ("BOARD_WORLD.on_button_released: button " + a_button.out + " pressed at (" + ax.out + ", " + ay.out + ") %N") if is_highlighting_location then normalize_locations end if is_highlighting_units then normalize_units end if is_repositioning then -- finish_widget_reposition end end on_pointer_motion (a_widget: VITP_WIDGET; ax, ay: INTEGER) -- React to a pointer move notification from `a_widget' do io.put_string ("BOARD_WORLD.on_notify_pointer_motion: at (" + ax.out + ", " + ay.out + ") %N") -- if is_repositioning then ---- last_mouse_position.set_position (ax, ay) -- -- Adjust for difference in the location of `active_widget' and `last_mouse_position' ---- if can_reposition (ax, ay) then ---- set_pointer_style (create {EV_POINTER_STYLE}.make_predefined ({EV_POINTER_STYLE_CONSTANTS}.Standard_cursor)) ---- reposition_active_widget (ax, ay) ---- else ---- set_pointer_style (create {EV_POINTER_STYLE}.make_predefined ({EV_POINTER_STYLE_CONSTANTS}.No_cursor)) ---- end -- end end feature -- Basic operations position_task_force_widget (a_widget: TASK_FORCE_WIDGET) -- Place `a_widget' on the board so it does not overlap other -- widgets, possibly moving other widgets. do end position_widget (a_widget: ATTACK_UNIT_WIDGET) -- Ensure each widget is postioned according to its position, -- perhaps repositioning it to accomodate other widgets. do end feature --{WIDGET_FACTORY} -- Basic operations on_flip_widget (a_widget: SHIP_WIDGET) -- Toggle the raiding status of `a_widget' do io.put_string ("on_flip_raider /n") a_widget.flip ensure ship_status_toggled: a_widget.ship.is_raiding = not (old a_widget.ship.is_raiding) end board_coordinate_to_model_position (a_position: EV_COORDINATE): VITP_POSITION -- `a_position' converted to the model's coordinate system require position_exists: a_position /= Void local brp, tlp: EV_COORDINATE p: EV_COORDINATE x_size, y_size: REAL_64 long, lat: REAL_64 do p := a_position brp := bottom_right_dot.point tlp := top_left_dot.point x_size := brp.x_precise - tlp.x_precise y_size := brp.y_precise - tlp.y_precise long := (p.x_precise - tlp.x_precise) * (board_right / x_size) lat := (p.y_precise - tlp.y_precise) * (board_bottom / y_size) create Result.set_xy (long.truncated_to_integer, lat.truncated_to_integer) end highlight_location (a_location: LOCATION) -- Brighten the widget corresponding to `a_location' require location_exists: a_location /= Void local w: VITP_WIDGET do w := widget_factory.widgets.definite_item (a_location) if world.has (w) then w.set_dimming_level ({DIMABLE}.Bright) w.paint highlighted_widgets.extend (w) is_highlighting_location := true end end highlight_location_occupants (a_location: LOCATION) -- Brighten the widgets corresponding to the occupants of `a_location' require location_exists: a_location /= Void local u_list: LINKED_SET [ATTACK_UNIT] w: VITP_WIDGET do u_list := a_location.units from u_list.start until u_list.after loop w := widget_factory.widgets.definite_item (u_list.item) w.set_dimming_level ({DIMABLE}.Bright) w.paint highlighted_widgets.extend (w) u_list.forth end is_highlighting_units := true end highlight_unit (a_unit: ATTACK_UNIT) -- Brighten the widget corresponding to `a_unit' require unit_exists: a_unit /= Void local tfw: TASK_FORCE_WIDGET w: VITP_WIDGET do -- if a_unit.is_task_force then -- tfw := task_force_widgets.widget (a_unit.task_force) -- if tfw.is_stacked then -- w := tfw -- else -- w := world.attack_widgets.widget (a_unit) -- end -- else -- w := world.attack_widgets.widget (a_unit) -- end w := widget_factory.widgets.definite_item (a_unit) w.set_dimming_level ({DIMABLE}.Bright) w.paint highlighted_widgets.extend (w) is_highlighting_units := true end normalize_locations -- Restor all location widgets to a normal brightness level local w: LOCATION_WIDGET do -- from world.location_widgets.start -- until world.location_widgets.after -- loop -- w := world.location_widgets.item_for_iteration -- w.set_dimming_level ({DIMABLE}.Normal) -- w.paint -- world.location_widgets.forth -- end -- is_highlighting_location := false end normalize_units -- Restore all attack widgets to a normal brightness level local w: ATTACK_UNIT_WIDGET do -- from world.attack_widgets.start -- until world.attack_widgets.after -- loop -- w := world.attack_widgets.item_for_iteration -- w.set_dimming_level ({DIMABLE}.Normal) -- w.paint -- world.attack_widgets.forth -- end -- is_highlighting_units := false end feature --{VITP_WIDGET} -- Implementation task_force_widgets: VITP_WIDGET_TABLE [TASK_FORCE_WIDGET, TASK_FORCE] last_mouse_position: EV_COORDINATE -- For possible restoration to the location where the reposition began forwarded_widget: detachable ATTACK_UNIT_WIDGET -- A widget that has been brought to the top of its owner forwarded_widget_owner: detachable VITP_WIDGET -- The widget in which `forwarded_widget' resides highlighted_widgets: LINKED_SET [VITP_WIDGET] -- The widgets with the focus top_left_dot: EV_MODEL_DOT -- A dot at top left of the board, that will scale and scroll with the view, -- whose position when compared with `bottom_right_dot' provides a scale -- factor used to position widgets bottom_right_dot: EV_MODEL_DOT -- See `top_left_dot' feature {NONE} -- Implementation -- X_stacking_increment: INTEGER -- -- The distance in pixels to offset a stacked widget in the x direction -- local -- w: ATTACK_UNIT_WIDGET -- do -- -- Since we base this distance on the current size of a ship widget, -- -- we must ensure we have a widget from which to calculate the value. -- check not world.attack_widgets.is_empty then -- w := world.attack_widgets.iteration_item (1) ---- Result := (w.tile_size / 10).rounded -- Result := (w.tile_size / 10).rounded -- end -- end -- Y_stacking_increment: INTEGER -- -- The distance in pixels to offset a stacked widget in the x direction -- local -- w: ATTACK_UNIT_WIDGET -- do -- -- Since we base this distance on the current size of a ship widget, -- -- we must ensure we have a widget from which to calculate the value. -- check not world.attack_widgets.is_empty then -- w := world.attack_widgets.iteration_item (1) ---- Result := (w.tile_size / 10).rounded -- Result := (w.tile_size / 10).rounded -- end -- end --feature {NONE} -- Implemetation -- target_imp: detachable VITP_GAME -- -- Detachable implementation of `target' for void safety end