examples/moles: tweak distances
[nit.git] / examples / mnit_moles / src / moles.nit
index fa05869..613a712 100644 (file)
 # Classic moles game
 #
 # This is a minimal practical example of the mnit framework.
-module moles
+module moles is
+       app_version(1, 0, git_revision)
+       app_name("Crazy Groundhogs")
+end
 
 import mnit
-import realtime
 
+import drawing
+
+# A hole with a possible mole, or a trap in it
 class Hole
        var game: Game
+
+       # Horizontal center of the hole
        var x: Int
+
+       # Vertical center of the hole
        var y: Int
-       var dim: Int
 
-       # state
-       var up = false
-       var hitted = false
+       # Half width of the hit box
+       var dx = 200.0
 
-       init (g: Game, x, y: Int)
-       do
-               game = g
-               self.x = x
-               self.y = y
-               dim = game.img_dim
-       end
+       # Height of the hit box
+       var dy = 800.0
+
+       # Content (and state) of this hole
+       var content: nullable HoleContent = null
 
        fun do_turn
        do
-               if up then
-                       if hitted then
+               var content = content
+               if content != null then
+                       if content == game.down then
                                if (20.0*game.speed_modifier).to_i.rand == 0 then
                                        # dead / hide
-                                       hitted = false
-                                       up = false
+                                       self.content = null
                                end
                        else if (80.0*game.speed_modifier).to_i.rand == 0 then
                                # hide
-                               up = false
+                               self.content = null
                        end
                else if (100.0*game.speed_modifier).to_i.rand == 0 then
-                       # show up
-                       up = true
+                       self.content = to_pop
+               end
+       end
+
+       # Get next `HoleContent` to pop
+       fun to_pop: HoleContent
+       do
+               # show traps only at 10 points and up
+               if game.points > 10 then
+
+                       # After 50 points we have more and more traps until point 1000
+                       var d = 1250-(game.points - 50)
+                       if d < 200 then d = 200
+
+                       if d.rand < 100 then return game.trap
                end
+
+               return game.up
        end
 
+       # Does this hole intercepts `event`?
        fun intercepts(event: PointerEvent): Bool
        do
-               var ex = event.x.to_i
-               var ey = event.y.to_i
-               return ex > x and ex < x + dim and
-                       ey > y and ey < y + dim
+               if content == null then return false
+
+               var dx = (dx*display_scale).to_i
+               var dy = (dy*display_scale).to_i
+               var ex = event.x.to_i - display_offset_x
+               var ey = event.y.to_i - display_offset_y
+               return ex > x - dx and ex < x + dx and
+                       ey > y - dy and ey < y
        end
 
-       fun hit
+       # Draw this hole and content to `display`
+       fun draw(display: Display)
        do
-               if hitted then return
+               # The hole itself
+               var img = app.assets.empty
+               var dx = 300.0*display_scale
+               var dy = 256.0*display_scale
+               img.scale = display_scale
+               display.blit(img, x-dx.to_i+display_offset_x, y-dy.to_i+display_offset_y)
+
+               # The mole in the hole, or other content
+               var content = self.content
+               if content != null then
+                       content.draw(display, x, y)
+               end
+       end
+end
+
+# Content of a `Hole`
+class HoleContent
+       # Image
+       var img: Image
 
-               if up then
-                       hitted = true
-                       game.points += 1
-               else abort # should not happen
+       # Offset of the horizontal center of the hole
+       var dx: Float
+
+       # Offset of the vertical center of the hole
+       var dy: Float
+
+       # Hit this hole content
+       fun hit(game: Game, hole: Hole, event: PointerEvent) do end
+
+       # Draw this content to `display`
+       fun draw(display: Display, x, y: Int)
+       do
+               img.scale = display_scale
+               display.blit(img,
+                       x-dx.to_i+display_offset_x,
+                       y-dy.to_i+display_offset_y)
+       end
+end
+
+# A mole in a hole
+class Mole
+       super HoleContent
+
+       # Points value when hit
+       var value: Int
+
+       redef fun hit(game, hole, event)
+       do
+               game.points += value
+               hole.content = game.down
+       end
+end
+
+# A trap held out of a hole
+class Trap
+       super HoleContent
+
+       # Points penalty when hit
+       var penalty: Int
+
+       redef fun hit(game, hole, event)
+       do
+               game.points -= penalty
+               if game.points < 0 then game.points = 0
+               hole.content = null
        end
 end
 
 class Game
-       var holes = new Array[Hole].with_capacity(4)
+       # All holes, filled or not
+       var holes = new Array[Hole]
 
        # rule / const
-       var modifier_half_life = 40.0
-       var rows = 5
-       var columns = 3
+       var modifier_half_life = 1000.0
+
+       # Row count
+       fun rows: Int do return 4
 
-       # state
+       # Columns count
+       fun columns: Int do return 5
+
+       # Score
        var points = 0
+
+       # Acceleration
        var speed_modifier = 1.0
 
-       # configs
-       var img_ori_dim: Int = 256
-       fun img_dim: Int do return 210
+       # Vertical offset between rows
+       var dist_between_rows = 448
+
+       # Horizontal offset between columns
+       var dist_between_columns = 600
+
+       # Global accumulation control, applied to `speed_modifier`
        fun global_speed_modifier: Float do return 2.0
 
+       # A mole, in a hole
+       var up = new Mole(app.assets.up, 212.0*display_scale, 820.0*display_scale, 1) is lazy
+
+       # A mole that was hit
+       var down = new HoleContent(app.assets.hit, 250.0*display_scale, 512.0*display_scale) is lazy
+
+       # A trap out of the hole
+       var trap = new Trap(app.assets.trap, 212.0*display_scale, 830.0*display_scale, 10) is lazy
+
        init
        do
-               var d = img_dim
-               for x in [0 .. rows[do
-                       for y in [0 .. columns[do
-                               holes.add(new Hole(self, x*d, y*d))
+               var dx = (dist_between_columns.to_f*display_scale).to_i
+               var dy = (dist_between_rows.to_f*display_scale).to_i
+               for x in [0 .. columns[ do
+                       for y in [0 .. rows[ do
+                               holes.add(new Hole(self, x*dx, y*dy))
                        end
                end
        end
 
-       fun do_turn do
+       fun do_turn
+       do
                for hole in holes do hole.do_turn
 
                speed_modifier = modifier_half_life / (modifier_half_life+points.to_f) * global_speed_modifier
@@ -114,53 +221,33 @@ end
 
 # Where all the UI stuff is done
 class Screen
-       var empty_img: Image
-       var up_img: Image
-       var hit_img: Image
-       var numbers: NumberImages
-
+       # The running game
        var game = new Game
 
-       init (app: App)
+       fun do_frame(display: Display)
        do
-               empty_img = app.load_image("images/empty.png")
-               up_img = app.load_image("images/up.png")
-               hit_img = app.load_image("images/hit.png")
-               numbers = app.load_numbers("images/#.png")
+               display.clear(0.0, 0.45, 0.0)
 
-               var scale = game.img_dim.to_f / game.img_ori_dim.to_f
-               empty_img.scale = scale
-               up_img.scale = scale
-               hit_img.scale = scale
-       end
+               app.assets.sign_warning.scale = display_scale
+               display.blit(app.assets.sign_warning, (380.0*display_scale).to_i, (256.0*display_scale).to_i)
 
-       fun do_frame(display: Display)
-       do
-               display.clear(0.0, 0.7, 0.0)
+               app.assets.sign_cute.scale = display_scale
+               display.blit(app.assets.sign_cute, (1024.0*display_scale).to_i, (64.0*display_scale).to_i)
 
                for hole in game.holes do
-                       var img
-
-                       if hole.hitted then
-                               img = hit_img
-                       else if hole.up then
-                               img = up_img
-                       else
-                               img = empty_img
-                       end
-
-                       display.blit(img, hole.x, hole.y-64)
+                       hole.draw display
                end
 
-               display.blit_number(numbers, game.points, 20, 20)
+               draw_hud display
        end
 
        fun input(event: InputEvent): Bool
        do
                if event isa PointerEvent then
-                       for hole in game.holes do
+                       for hole in game.holes.reverse_iterator do
                                if hole.intercepts(event) then
-                                       if hole.up then hole.hit
+                                       var hole_content = hole.content
+                                       if hole_content != null then hole_content.hit(game, hole, event)
                                        return true
                                end
                        end
@@ -168,56 +255,77 @@ class Screen
 
                return false
        end
+
+       # Draw the HUD as the topmost layer of the screen
+       fun draw_hud(display: Display)
+       do
+               var board = app.assets.points_board
+               board.scale = display_scale
+               display.blit(board, (32.0*display_scale).to_i, -10)
+
+               draw_score(display)
+       end
+
+       # Draw the score
+       fun draw_score(display: Display, score: nullable Int)
+       do
+               if score == null then score = game.points
+
+               # Draw the score itself
+               for img in app.numbers.imgs do img.scale = display_scale
+               display.blit_number(app.numbers, score,
+                       (92.0*display_scale).to_i,
+                       (172.0*display_scale).to_i)
+       end
 end
 
-class MyApp
-       super App
+redef class App
 
-       var screen: nullable Screen = null
+       # Main play screen
+       var screen = new Screen
 
-       var target_dt = 20000000
+       # Image set generate by inkscape_tools
+       var assets = new DrawingImages
 
-       init do super
+       # Numbers to display the score
+       var numbers = new NumberImages(assets.n)
 
-       redef fun init_window
+       redef fun on_start
        do
                super
-
-               screen = new Screen(self)
+               assets.load_all self
        end
 
-       redef fun frame_core(display)
+       redef fun on_create
        do
-               var screen = self.screen
-               if screen != null then
-                       var clock = new Clock
+               super
 
-                       screen.game.do_turn
-                       screen.do_frame(display)
+               maximum_fps = 50
+       end
 
-                       var dt = clock.lapse
-                       if dt.sec == 0 and dt.nanosec < target_dt then
-                               var sleep_t = target_dt - dt.nanosec
-                               sys.nanosleep(0, sleep_t)
-                       end
-               end
+       redef fun frame_core(display)
+       do
+               screen.game.do_turn
+               screen.do_frame(display)
        end
 
        redef fun input(ie)
        do
-               var screen = screen
                if ie isa QuitEvent or
                        (ie isa KeyEvent and ie.to_c == 'q') then
                        quit = true
                        return true
-               else if screen != null then
-                       return screen.input(ie)
-               else
-                       print "unknown input: {ie}"
-                       return false
                end
+
+               return screen.input(ie)
        end
 end
 
-var app = new MyApp
-app.main_loop
+# Main zoom
+fun display_scale: Float do return 1.0
+
+# Depends on the hole center in the uo image
+fun display_offset_x: Int do return (512.0*display_scale).to_i
+
+# Depends on the width of the holes
+fun display_offset_y: Int do return (1000.0*display_scale).to_i