indexing description: "[ Each scene object contains all information for one adventure location including cylindrical background pane, static and moving foreground objects and clickable areas. The scene is able to draw itself including the overlying screen mask, item repository and main menu. It handles mouse events and feeds them to the appropriate objects. This class is part of the XAE Extended Adventure Engine Project. ]" author: "Ralph Wiedemeier, ralphw@student.ethz.ch" date: "$Date$" revision: "$Revision$" deferred class GUI_SCENE inherit EM_SCENE redefine redraw, next_scene, handle_mouse_button_down_event, handle_mouse_button_up_event, handle_mouse_motion_event, handle_key_down_event end GUI_SHARED export {NONE} all undefine default_create end EM_SHARED_BITMAP_FACTORY export {NONE} all undefine default_create end OPEN_GL_LIBRARY export {NONE} all undefine default_create end SINGLE_MATH export {NONE} all undefine default_create end feature -- Initialization initialize_scene is -- Scene initialisation do -- Set up the projection perspective macro_set_projection_perspective -- Create world cylinder create_world_cylinder -- Register the event handlers register_event_handlers -- Create tooltip dialog create tooltip.make_with_text ("Tooltip", gui_constants.font_normal) tooltip.enable_multiline tooltip.set_size (150, 30) tooltip.set_padding_horizontal (7) tooltip.set_font_size (0.6) tooltip.set_special_corner_top_left tooltip.set_special_corner_size (6) tooltip.set_border_color_normal (gui_constants.color_tooltip_border) tooltip.set_fill_color_normal (gui_constants.color_tooltip_fill) tooltip.set_invisible -- Register error handling dialog gui.application.register_exception_action (agent display_unrecoverable_error) -- Various settings drawing_mode := drawing_mode_world reset_scene_time debug_clear -- CHANGES -> event loop and animation initialization make_scene end feature -- Status next_scene: GUI_SCENE feature -- Operation draw_gl_world is -- Draw the background pane and all static and moving items do -- Clear the screen emgl_clear (EM_gl_color_buffer_bit | EM_gl_depth_buffer_bit) if drawing_mode = drawing_mode_closeup then draw_closeup else draw_panorama_world end end reset_scene_time is -- Reset scene time do scene_start := gui_constants.ticks end dispose_texture_memory is -- Free all texture memory used by this scene do from clickables.start until clickables.after loop clickables.item.dispose_texture_memory clickables.forth end -- macro_free_texture (texture_id_1) -- macro_free_texture (texture_id_2) macro_free_texture (texture_1) macro_free_texture (texture_2) end display_unrecoverable_error (a_message: STRING) is -- Display an unrecoverable error do unrecoverable_error := True a_message.replace_substring_all ("/", "/ ") a_message.replace_substring_all ("\", "\ ") error_dialog := create {GUI_ERROR_DIALOG}.make_with_error_message (a_message) error_dialog.set_event_action_option_click (agent handle_menu_event_exit) speed_target := 0 speed_factor := 0 tooltip.set_invisible drawing_mode := drawing_mode_world draw_gl_world macro_make_snapshot (fx_texture_1) drawing_mode := drawing_mode_closeup closeup_start := gui_constants.ticks scene_in_transition := False closeup_dissolve := False game_skin.main_menu.disable_except (game_skin.main_menu.button_exit) event_loop.start end feature {GUI_CLICKABLE} -- Event handlers handle_clickable_click_event (a_target: GUI_CLICKABLE_3D) is -- Handle mouse clicks on clickable scene items do -- Descendants may redefine this feature end handle_repository_item_click_event (a_target: GUI_SPECIAL_FRAME) is -- Handle mouse clicks on repository items do -- Descendants may redefine this feature end handle_option_click_event (a_target: GUI_CLICKABLE) is -- Handle mouse clicks on closeup options do -- Descendants may redefine this feature end handle_menu_event_open_script is -- Button 'open script' is selected from main menu do -- Descendants may redefine this feature end handle_menu_event_load_game is -- Button 'load game' is selected from main menu do -- Descendants may redefine this feature end handle_menu_event_save_game is -- Button 'save game' is selected from main menu do -- Descendants may redefine this feature end handle_menu_event_credits is -- Button 'credits' is selected from main menu do -- Descendants may redefine this feature end handle_menu_event_exit is -- Button 'exit' is selected from main menu do next_scene := Void unrecoverable_error := False prepare_scene_transition end handle_timer_event (a_time: INTEGER) is -- Handle timer event do -- Descendants may redefine this feature end feature {NONE} -- Internal drawing redraw is -- Full screen redraw do -- Calculate absolute and relative scene time scene_time := gui_constants.ticks - scene_start scene_relative_time := scene_time - scene_old_time scene_old_time := scene_time -- Publish timer handle_timer_event (scene_time) if not scene_in_transition then draw_gl_world -- CHANGES -> comment -- screen.gl_enter_2d -- CHANGES -> blitting -- screen.enable_auto_opengl_blitting -- screen.enable_alpha_transparency -- does not have any influences? screen.enable_opengl_blitting game_skin.set_debug_message (debug_string) game_skin.draw (screen) tooltip.draw (screen) -- screen.gl_leave_2d screen.redraw screen.disable_opengl_blitting end end draw_panorama_world is -- Draw world panorama background do -- Calculate angle speed_factor := 0.9 * speed_factor + 0.1 * speed_target if speed_factor.abs < 0.001 then speed_factor := 0 end angle := angle + 0.01 * scene_relative_time * speed_factor if max_angle < 360 then if angle < 39 then angle := 39 elseif angle > max_angle - 41 then angle := max_angle - 41 end else if angle < 0 then angle := angle + 360 elseif angle > 360 then angle := angle - 360 end end -- Calculate click point in cylinder coordinates calculate_cylindric_clickpoint (mouse_x, mouse_y) -- Set up world rotation emgl_load_identity emgl_translatef(0, 50, -50) emgl_rotatef(180 + angle, 0, 1, 0) -- Draw cylindric background pane background_cylinder.draw -- Update and draw all clickable elements from clickables.start until clickables.after loop clickables.item.update (scene_time) clickables.item.draw (screen) clickables.forth end debug_clear debug_print ("Angle: " + angle.truncated_to_integer.out + "%N") debug_print ("Screen x = " + mouse_x.out + " y = " + mouse_y.out + "%N") debug_print ("Cylinder x = " + cyl_click_x.out + " y = " + cyl_click_y.out) end draw_closeup is -- Draw closeup view local blend: DOUBLE closeup_time: INTEGER do if unrecoverable_error then closeup := error_dialog end -- Calculate time (msec) since closeup started closeup_time := gui_constants.ticks - closeup_start -- Draw frozen background emgl_load_identity emgl_translatef (0, 52, -700) macro_disable_depth_test macro_enable_alpha_blending draw_closeup_background (1.0) -- Draw closeup dialog -- CHANGES -> comment -- screen.gl_enter_2d screen.enable_opengl_blitting closeup.set_max_characters (closeup_time // 10) closeup.draw (screen) screen.disable_opengl_blitting -- screen.gl_leave_2d -- Dissolve effect if closeup_dissolve and then closeup_time < 600 then blend := 1 - gui_timing.smooth_bound (closeup_time / 600) draw_closeup_background (blend) end end draw_closeup_background (blend: DOUBLE) is -- Draw freezed background for closeup with specified blend factor -- (0 = fully transparent, 1 = fully opaque) local texture: EM3D_TEXTURE_2D[ EMGL_FORMAT_RGBA ] do texture := fx_texture_1 macro_bind_texture_antialiased_modulate (texture) emgl_begin (EM_gl_quads) emgl_color4d (1, 1, 1, blend) emgl_tex_coord2d (0.0, 0.0) emgl_vertex3d (-512, -256, 0) emgl_tex_coord2d (1.0, 0.0) emgl_vertex3d ( 512, -256, 0) emgl_tex_coord2d (1.0, 1.0) emgl_vertex3d ( 512, 256, 0) emgl_tex_coord2d (0.0, 1.0) emgl_vertex3d (-512, 256, 0) emgl_end texture.unbind end debug_clear is -- Clear debug output do debug_string := Void end debug_print (a_string: STRING) is -- Print debug message do if debug_string = Void then debug_string := a_string else debug_string := debug_string + a_string end end feature {NONE} -- Internal mouse handlers calculate_cylindric_clickpoint (mx: INTEGER; my: INTEGER) is -- Calculate the cylinder coordinates of the provided screen coordinates local sx, sy, sz, screen_z, cx, cy, cz, tx, ty, tz: DOUBLE r, t, a, b, c, diskr, rel_angle, abs_angle: DOUBLE do r := gui_constants.stage_radius screen_z := 647 sx := 0 sy := 384 sz := 0 cx := mx - 512 - sx cy := my - sy cz := screen_z - sz a := cx * cx + cz * cz b := 2 * (sx * cx + sz * cz) c := sx * sx + sz * sz - r * r diskr := b*b - 4*a*c if diskr > 0 then t := - b + sqrt (diskr) / (2*a) tx := sx + t * cx ty := sy + t * cy - 78 tz := sz + t * cz if tx /= 0 then rel_angle := 90 - arc_tangent (tz/tx) * 180 / pi if rel_angle > 90 then rel_angle := rel_angle - 180 end else rel_angle := 0 end abs_angle := angle + rel_angle if abs_angle < 0 then abs_angle := abs_angle + 360 end cyl_click_x := (abs_angle * 4096 / 360).truncated_to_integer if cyl_click_x > 4095 then cyl_click_x := cyl_click_x - 4096 end cyl_click_y := ty.truncated_to_integer -- debug_clear -- debug_print ("Hit point: x:" + cyl_click_x.out + " y:" + cyl_click_y.out) end end handle_mouse_button_down_event (mouse_event: EM_MOUSEBUTTON_EVENT) is -- this feature is called when a mouse button is pressed do mouse_click_x := mouse_event.x mouse_click_y := mouse_event.y if mouse_click_y > world_top and then mouse_click_y < world_bottom then if mouse_event.is_right_button then mouse_moving := True elseif mouse_event.is_left_button then debug_clear if drawing_mode = drawing_mode_closeup then closeup.publish_event_click (mouse_click_x, mouse_click_y) elseif drawing_mode = drawing_mode_world then clickables.do_all (agent {GUI_CLICKABLE_3D}.publish_event_click (cyl_click_x, cyl_click_y)) end end end if mouse_event.is_left_button then game_skin.main_menu.publish_event_click (mouse_event.x, mouse_event.y) game_skin.repository.publish_event_click (mouse_event.x, mouse_event.y) end end handle_mouse_button_up_event (mouse_event: EM_MOUSEBUTTON_EVENT) is -- this feature is called when a mouse button is released do if mouse_event.is_right_button then mouse_moving := False speed_target := 0 end end handle_mouse_motion_event (mouse_event: EM_MOUSEMOTION_EVENT) is -- this feature is called when the mouse is moved do mouse_x := mouse_event.x mouse_y := mouse_event.y if mouse_y > world_top and then mouse_y < world_bottom then tooltip.set_position (mouse_x + 12, mouse_y + 15) if drawing_mode = drawing_mode_closeup then closeup.publish_event_hoover (mouse_x, mouse_y) elseif drawing_mode = drawing_mode_world then clickables.do_all (agent {GUI_CLICKABLE_3D}.publish_event_hoover (cyl_click_x, cyl_click_y)) end else tooltip.set_invisible end game_skin.main_menu.publish_event_hoover (mouse_x, mouse_y) game_skin.repository.publish_event_hoover (mouse_x, mouse_y) if mouse_moving then speed_target := (mouse_x - mouse_click_x) / 40 if speed_target.abs > 4 then speed_target := 4 * speed_target.sign end end end handle_key_down_event (a_keyboard_event: EM_KEYBOARD_EVENT) is -- Handle key down event do if a_keyboard_event.character = 'd' then if game_skin.is_debug_enabled then game_skin.disable_debug else game_skin.enable_debug end end end cyl_click_x, cyl_click_y: INTEGER mouse_click_x, mouse_click_y: INTEGER mouse_x, mouse_y: INTEGER mouse_moving: BOOLEAN feature {NONE} -- Internal routines register_event_handlers is -- Register event handlers do game_skin.main_menu.button_open_script.set_event_action_click (agent handle_menu_event_open_script) game_skin.main_menu.button_load_game.set_event_action_click (agent handle_menu_event_load_game) game_skin.main_menu.button_save_game.set_event_action_click (agent handle_menu_event_save_game) game_skin.main_menu.button_credits.set_event_action_click (agent handle_menu_event_credits) game_skin.main_menu.button_exit.set_event_action_click (agent handle_menu_event_exit) end create_world_cylinder is -- Create the cylindric panorama map local world_factory: GUI_WORLD_FACTORY do create world_factory world_factory.set_panorama_image (first_background_image, second_background_image) background_cylinder := world_factory.create_object max_angle := world_factory.panorama_angle * 57.2958 if max_angle < 91 then max_angle := 80 end -- texture_id_1 := world_factory.texture_id_1 -- texture_id_2 := world_factory.texture_id_2 texture_1 := world_factory.texture_1 texture_2 := world_factory.texture_2 end prepare_scene_transition is -- Prepare for scene transition do if not unrecoverable_error then event_loop.stop speed_target := 0 speed_factor := 0 scene_in_transition := True end end prepare_for_closeup_view is -- Switch to closeup view local mode: INTEGER do if not unrecoverable_error then if drawing_mode = drawing_mode_closeup and then closeup_start > scene_start then scene_start := scene_start + (gui_constants.ticks - closeup_start) end mode := drawing_mode drawing_mode := drawing_mode_world draw_gl_world macro_make_snapshot (fx_texture_1) drawing_mode := mode speed_target := 0 speed_factor := 0 tooltip.set_invisible if drawing_mode /= drawing_mode_closeup then closeup_start := gui_constants.ticks closeup_dissolve := True drawing_mode := drawing_mode_closeup else closeup_dissolve := False end end end prepare_for_scene_view is -- Switch to scene view do if not unrecoverable_error then if drawing_mode = drawing_mode_closeup and then closeup_start > scene_start then scene_start := scene_start + (gui_constants.ticks - closeup_start) end drawing_mode := drawing_mode_world closeup := Void end end feature {NONE} -- Variables Panorama World background_cylinder: EM_3D_OBJECT -- Cylindrical background pane first_background_image: STRING second_background_image: STRING -- Background images for cylindric pane clickables: LINKED_LIST [GUI_CLICKABLE_3D] -- List of clickable scene objects speed_factor, speed_target: DOUBLE -- The factor of the cylinder rotation speed max_angle: DOUBLE angle: DOUBLE timers: LINKED_LIST [TIMER] feature {NONE} -- Variables Closeup View closeup: GUI_CLOSEUP closeup_dissolve: BOOLEAN feature {NONE} -- Variables scene_start: INTEGER scene_time: INTEGER scene_old_time: INTEGER scene_relative_time: INTEGER closeup_start: INTEGER tooltip: GUI_SPECIAL_BUTTON scene_in_transition: BOOLEAN unrecoverable_error: BOOLEAN error_dialog: GUI_ERROR_DIALOG debug_string: STRING -- texture_id_1: INTEGER -- texture_id_2: INTEGER texture_1: EM3D_TEXTURE_2D[ EMGL_FORMAT_RGBA ] texture_2: EM3D_TEXTURE_2D[ EMGL_FORMAT_RGBA ] -- Texture IDs of panorama background (for disposing purposes) selected_item: GUI_SPECIAL_FRAME -- Currently selected repository item drawing_mode: INTEGER drawing_mode_world: INTEGER is 1 drawing_mode_closeup: INTEGER is 2 -- Scene drawing mode world_top: INTEGER is 80 world_bottom: INTEGER is 585 end