# 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
- # Center of the hole
+ # Horizontal center of the hole
var x: Int
+
+ # Vertical center of the hole
var y: Int
# Half width of the hit box
var dx = 200.0
- # Heigth of the hit box
+
+ # Height of the hit box
var dy = 800.0
- # state
- var up = false
- var hitted = false
-
- init (g: Game, x, y: Int)
- do
- game = g
- self.x = x
- self.y = y
- end
+ # 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
- if not up or hitted then return false
+ if content == null then return false
var dx = (dx*display_scale).to_i
var dy = (dy*display_scale).to_i
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 modifier_half_life = 1000.0
+
+ # Row count
fun rows: Int do return 4
+
+ # Columns count
fun columns: Int do return 5
- # state
+ # Score
var points = 0
+
+ # Acceleration
var speed_modifier = 1.0
- # configs
- var dist_between_rows = 512
+ # 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 dx = (dist_between_columns.to_f*display_scale).to_i
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
# Where all the UI stuff is done
class Screen
- var empty_img: Image
- var up_img: Image
- var hit_img: Image
- var numbers: NumberImages
-
- var sign_warning: Image
- var sign_cute: Image
- var sign_hits: Image
-
+ # The running game
var game = new Game
- init (app: App)
- 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")
-
- sign_warning = app.load_image("images/sign-warning.png")
- sign_cute = app.load_image("images/sign-cute.png")
- sign_hits = app.load_image("images/sign-hits.png")
- end
-
fun do_frame(display: Display)
do
- display.clear(0.1, 0.65, 0.2)
+ display.clear(0.0, 0.45, 0.0)
- sign_warning.scale = display_scale
- sign_cute.scale = display_scale
- sign_hits.scale = display_scale
- for img in numbers.imgs do img.scale = display_scale
+ 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)
- display.blit(sign_warning, (-120.0*display_scale).to_i, (-235.0*display_scale).to_i)
- display.blit(sign_cute, (540.0*display_scale).to_i, (-180.0*display_scale).to_i)
- display.blit(sign_hits, (1340.0*display_scale).to_i, (55.0*display_scale).to_i)
- display.blit_number(numbers, game.points, (1460.0*display_scale).to_i, (270.0*display_scale).to_i)
+ 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
- # Hole
- var img = empty_img
- var dx = 512.0*display_scale
- var dy = 512.0*display_scale
- img.scale = display_scale
- display.blit(img, hole.x-dx.to_i+display_offset_x, hole.y-dy.to_i+display_offset_y)
-
- # Mole
- var empty = false
- if hole.hitted then
- img = hit_img
- dx = 256.0*display_scale
- dy = 417.0*display_scale
- else if hole.up then
- img = up_img
- dx = 512.0*display_scale
- dy = 830.0*display_scale
- else empty = true
-
- if not empty then
- img.scale = display_scale
- display.blit(img, hole.x-dx.to_i+display_offset_x, hole.y-dy.to_i+display_offset_y)
- end
+ hole.draw display
end
+
+ 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
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
-
- init_screen_and_game
+ assets.load_all self
end
- fun init_screen_and_game do screen = new Screen(self)
-
- 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
+# 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 (800.0*display_scale).to_i
-
-var app = new MyApp
-app.main_loop
+fun display_offset_y: Int do return (1000.0*display_scale).to_i