--- /dev/null
+leapfrog:
+ ../../bin/nitc leapfrog.nit --cc-lib-name curses
--- /dev/null
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Curses for Nit
+module curses
+
+# A curse windows
+extern Window
+ # Initialize the screen
+ new is extern "initscr"
+
+ # print a string somewhere
+ # NOTE: as with the curses API, the position is (y,x)
+ fun mvaddstr(y,x: Int, str: String) is extern import String::to_cstring
+
+ fun refresh is extern
+ fun wclear is extern
+ fun delwin is extern
+ fun endwin is extern
+end
+
+redef class Sys
+ # Wait a specific number of second and nanoseconds
+ # FIXME: should not be in the curses module
+ fun nanosleep(sec, nanosec: Int) is extern
+end
+
+redef class Stdin
+ # Is these something to read? (non blocking)
+ # FIXME: should not be in the curses module
+ fun poll_in: Bool is extern
+end
--- /dev/null
+/*
+ Extern implementation of Nit module curses
+ File initially created by nits to customize type of extern classes
+*/
+
+#include "curses.nit.h"
+#include <time.h>
+#include <poll.h>
+
+/*
+C implementation of curses::Window::init
+*/
+Window new_Window___impl( )
+{
+ WINDOW *res;
+ res = initscr();
+ if (res == NULL) {
+ fprintf(stderr, "Error initialising ncurses.\n");
+ exit(EXIT_FAILURE);
+ }
+ raw();
+ keypad(res, TRUE);
+ noecho();
+}
+
+/*
+C implementation of curses::Window::mvaddstr
+
+Imported methods signatures:
+ char * String_to_cstring( String recv ) for string::String::to_cstring
+*/
+void Window_mvaddstr___impl( Window recv, bigint y, bigint x, String str )
+{
+ char *c_string = String_to_cstring( str );
+ mvaddstr(y, x, c_string);
+}
+
+/*
+C implementation of curses::Window::refresh
+*/
+void Window_refresh___impl( Window recv )
+{
+ refresh();
+}
+
+/*
+C implementation of curses::Window::wclear
+*/
+void Window_wclear___impl( Window recv ) {
+ wclear(recv);
+}
+
+/*
+C implementation of curses::Window::delwin
+*/
+void Window_delwin___impl( Window recv )
+{
+ delwin(recv);
+}
+
+/*
+C implementation of curses::Window::endwin
+*/
+void Window_endwin___impl( Window recv )
+{
+ endwin();
+}
+
+/*
+C implementation of curses::Sys::nanosleep
+*/
+void Sys_nanosleep___impl( Sys recv, bigint sec, bigint nanosec ) {
+ const struct timespec req = {sec, nanosec};
+ nanosleep(&req, NULL);
+}
+
+/*
+C implementation of curses::Stdin::poll_in
+*/
+int Stdin_poll_in___impl( Stdin recv ) {
+ struct pollfd fd = {0, POLLIN, 0};
+ int res = poll(&fd, 1, 0);
+ if (res == -1) {
+ endwin();
+ perror("Error poll stdin");
+ exit(EXIT_FAILURE);
+ }
+ return res > 0;
+}
--- /dev/null
+/*
+ Extern implementation of Nit module curses
+ File initially created by nits to implement extern methods body
+*/
+
+#ifndef CURSES_NIT_H
+#define CURSES_NIT_H
+
+#include <curses._nitni.h>
+#include <curses.h>
+
+#define Window void*
+Window new_Window___impl( );
+void Window_mvaddstr___impl( Window recv, bigint y, bigint x, String str );
+void Window_refresh___impl( Window recv );
+void Window_wclear___impl( Window recv );
+void Window_delwin___impl( Window recv );
+void Window_endwin___impl( Window recv );
+void Sys_nanosleep___impl( Sys recv, bigint sec, bigint nanosec );
+int Stdin_poll_in___impl( Stdin recv );
+#endif
--- /dev/null
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# 2D management of game elements
+#
+# FIXME: game is a bad name
+# TODO: collision framework (with quad tree?)
+module game
+
+# The root class of the living objects (sprites, group of sprites, etc.)
+abstract class LiveObject
+ # Compute the position, state and appearance.
+ fun update do end
+
+ # Controls whether `update' and `draw' are automatically called by `LiveGroup'
+ var exists writable = true
+
+ # Redefine this method to asks how to draw on a view
+ fun draw(view: View) is abstract
+end
+
+
+# The basic atomic living and moving object.
+#
+# A sprite has a position and a velocity
+class Sprite
+ super LiveObject
+
+ # x coordinate of the top-left point
+ var x: Int writable = 0
+
+ # y coordinate of the top-left point
+ var y: Int writable = 0
+
+ # width of the sprite
+ var width: Int writable = 100
+
+ # height of the sprite
+ var height: Int writable = 100
+
+ # x velocity (applied by `update')
+ var vx: Int writable = 0
+
+ # y velocity (applied by `update')
+ var vy: Int writable = 0
+
+ redef fun update
+ do
+ self.x += self.vx
+ self.y += self.vy
+ end
+
+ redef fun draw(view) do view.draw_sprite(self)
+
+ # Is self overlaps (or contains) an other sprite
+ # `x', `y', `width', and `height' of both sprites are considered
+ fun overlaps(other: Sprite): Bool
+ do
+ return self.x+self.width > other.x and self.x < other.x+other.width and self.y+self.height > other.y and self.y < other.y+other.width
+ end
+end
+
+# Organizational class to manage groups of sprites and other live objects.
+class LiveGroup[E: LiveObject]
+ super LiveObject
+ super List[E]
+
+ init
+ do
+ end
+
+ # Recursively update each live objects that `exists'
+ redef fun update
+ do
+ for x in self do if x.exists then x.update
+ end
+
+ # Recursively draw each live objects that `exists'
+ redef fun draw(view)
+ do
+ for x in self do if x.exists then x.draw(view)
+ end
+end
+
+# Abstract view do draw sprites
+#
+# Concrete views are specific for each back-end.
+# View can also be used to implements camera and other fun things.
+interface View
+ # Draw a specific sprite on the view
+ #
+ # This method must be implemented for each specific view.
+ # A traditional way of implementation is to use a double-dispatch mechanism
+ #
+ # Exemple:
+ # class MyView
+ # redef fun draw_sprite(s) do s.draw_on_myview(self)
+ # end
+ # redef class Sprite
+ # # How to draw a sprite on my specific view
+ # fun draw_on_myview(myview: MyView) is abstract
+ # end
+ fun draw_sprite(s: Sprite) is abstract
+end
--- /dev/null
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# game of leapfrog: be a sheep and avoid the duck to grab apples
+#
+# This module is an example of a simple game using a curses backend
+module leapfrog
+
+import curses
+import game
+
+# A simple game view in a curses windows
+class CursesView
+ super View
+
+ # The associated curses window
+ var window: Window
+
+ redef fun draw_sprite(s: Sprite) do s.draw_on_curses(window)
+end
+
+redef class Sprite
+ # Drawing of a sprite in the curse windows
+ fun draw_on_curses(window: Window) is abstract
+end
+
+# A falling apple
+# If the sheep grab it, it scores one point.
+class Apple
+ super Sprite
+
+ redef fun draw_on_curses(window)
+ do
+ var x = self.x/100
+ var y = self.y/100
+ window.mvaddstr(y, x, "o")
+ end
+end
+
+# Common class for the sheep and the duck
+class Animal
+ super Sprite
+
+ # Is the sprite stunned?
+ # 0 if no, >0 if stunned.
+ # The value indicate the number of step that remain to be stunt
+ #
+ # If a animal is stunned, it cannot move horizontally
+ var stunt_ttl: Int writable = 0
+
+ # Common update for animal
+ # handle stunt and edge collision
+ redef fun update
+ do
+ if stunt_ttl > 0 then
+ stunt_ttl -= 1
+ else
+ self.x += self.vx
+ end
+ self.y += self.vy
+
+ if self.x + self.width > 7900 then
+ self.vx = -self.vx
+ self.x = 7900 - self.width
+ else if self.x < 0 then
+ self.vx = -self.vx
+ self.x = 0
+ end
+ end
+end
+
+# The player sprite
+class Sheep
+ super Animal
+
+ # Which frame to show when walking
+ var leg_state: Int = 0
+
+ # Is the sheep currently jumping (or falling)
+ var is_jumping = false
+
+ init
+ do
+ self.y = 2000 - 200
+ self.vx = 100
+ self.width = 600
+ self.height = 200
+ end
+
+ redef fun update
+ do
+ super
+
+ if is_jumping then
+ if self.y + self.height > 2000 then
+ is_jumping = false
+ self.vy = 0
+ self.y = 2000 - self.height
+ else
+ self.vy += 10 # gravity
+ end
+ end
+ if not is_jumping then
+ self.leg_state = 1 - self.leg_state # change leg
+ end
+ end
+
+ # Try to jump is possible (ie if walking and not stunt)
+ fun jump
+ do
+ if is_jumping or stunt_ttl > 0 then return
+ self.vy = -100
+ self.is_jumping = true
+ end
+
+ redef fun draw_on_curses(window)
+ do
+ var x = self.x/100
+ var y = self.y/100
+ if self.is_jumping then
+ if self.vy > 0 then
+ # falling
+ if self.vx > 0 then
+ window.mvaddstr(y, x, "'---@>")
+ window.mvaddstr(y+1, x, " \\-\\'")
+ else
+ window.mvaddstr(y, x, "<@---'")
+ window.mvaddstr(y+1, x, " '/-/")
+ end
+ else
+ # jumping
+ if self.vx > 0 then
+ window.mvaddstr(y, x, ",---@>")
+ window.mvaddstr(y+1, x, " /-/'")
+ else
+ window.mvaddstr(y, x, "<@---,")
+ window.mvaddstr(y+1, x, " '\\-\\")
+ end
+ end
+ else if self.vx > 0 then
+ if self.leg_state == 0 then
+ window.mvaddstr(y, x, ",---@>")
+ window.mvaddstr(y+1, x, " /-|'")
+ else
+ window.mvaddstr(y, x, ",---@>")
+ window.mvaddstr(y+1, x, " |-\\'")
+ end
+ else
+ if self.leg_state == 0 then
+ window.mvaddstr(y, x, "<@---,")
+ window.mvaddstr(y+1, x, " '/-|")
+ else
+ window.mvaddstr(y, x, "<@---,")
+ window.mvaddstr(y+1, x, " '|-\\")
+ end
+ end
+ end
+end
+
+# The nemesis of the sheep.
+# It just go back and forth
+class Duck
+ super Animal
+ init
+ do
+ self.y = 2000 - 200
+ self.x = 7900 - 400
+ self.vx = -80
+ self.width = 400
+ self.height = 200
+ end
+
+ redef fun draw_on_curses(window)
+ do
+ var x = self.x/100
+ var y = self.y/100
+
+ if self.vx > 0 then
+ window.mvaddstr(y, x, " @<")
+ window.mvaddstr(y+1, x, "<__)")
+ else
+ window.mvaddstr(y, x, ">@")
+ window.mvaddstr(y+1, x, "(__>")
+ end
+ end
+end
+
+class Game
+ fun run(window: Window)
+ do
+ var main_view = new CursesView(window)
+
+ var apples = new LiveGroup[Apple]
+ var duck = new Duck
+ var sheep = new Sheep
+
+ var score = 0
+
+ var sprites = new LiveGroup[LiveObject]
+ sprites.add(apples)
+ sprites.add(duck)
+ sprites.add(sheep)
+
+ loop
+
+ # Call update on all sprites
+ sprites.update
+
+ # Need a new apple
+ if 10.rand < 2 then
+ var a = new Apple
+ a.x = (60.rand + 10) * 100
+ a.y = 0
+ a.vx = 0
+ a.vy = 70.rand + 30
+ apples.add(a)
+ end
+
+ # Eat apple or fallen apple
+ for a in apples do
+ if not a.exists then continue
+ if a.overlaps(sheep) then
+ score += 1
+ a.exists = false
+ end
+ if a.y > 2000 then
+ a.exists = false
+ end
+ end
+
+ # Sheep vs duck
+ if sheep.overlaps(duck) then
+ if sheep.is_jumping and sheep.vy > 0 then
+ duck.stunt_ttl = 5
+ sheep.vy = -150
+ else
+ if sheep.x < duck.x then
+ sheep.x = duck.x - sheep.width
+ else
+ sheep.x = duck.x + duck.width
+ end
+ sheep.vx = - sheep.vx
+ duck.vx = - duck.vx
+ end
+ end
+
+ # Redraw the screen
+ window.wclear
+ sprites.draw(main_view)
+ window.mvaddstr(0, 0, "'q' to quit - score: {score}")
+ window.mvaddstr(20, 0, "#"*80)
+ window.refresh
+
+ # Wait the next frame
+ sys.nanosleep(0, 48000000)
+
+ # Keyboard input
+ while stdin.as(Stdin).poll_in do
+ if stdin.eof then return
+ var c = stdin.read_char
+ if c == 'q'.ascii then return
+ sheep.jump
+ end
+ end
+ end
+end
+
+var game = new Game
+
+var win = new Window
+
+game.run(win)
+
+win.delwin
+win.endwin
+win.refresh
+