diff --git a/README.md b/README.md new file mode 100644 index 0000000..31df22d --- /dev/null +++ b/README.md @@ -0,0 +1,17 @@ +# Prelude of the Chambered (JRuby port) + +This is an in-progress JRuby port of notch's 48 hour "Ludum Dare" game competition entry, _Prelude of the Chambered_. + +Notch's Java code is pretty straightforward and I've been looking for an excuse to do a "real" project in JRuby for ages. This seemed an ideal opportunity. + +Porting is still in progress and I'm only as far as the main menu so far. But a lot of "support" code has already been ported so progress is going well. + +## Running + +To run this code, get JRuby installed (rvm install jruby?) and then just run (from the root folder): + + ruby escape.rb + +## License + +Unsure as yet, not sure of the license for the original game. \ No newline at end of file diff --git a/escape.rb b/escape.rb new file mode 100644 index 0000000..2eaa52a --- /dev/null +++ b/escape.rb @@ -0,0 +1,4 @@ +$: << File.expand_path(File.dirname(__FILE__)) +require 'escape/escape' + +Component.main diff --git a/escape/art.rb b/escape/art.rb new file mode 100644 index 0000000..e08e9f4 --- /dev/null +++ b/escape/art.rb @@ -0,0 +1,40 @@ +java_import java.awt.image.BufferedImage +java_import javax.imageio.ImageIO + +class Art < java.lang.Object + def self.load_bitmap(file_name) + url = java.net.URL.new("file://" + ASSETS_DIR + file_name) # nasty little hack due to borked get_resource (means applet won't be easy..) + img = ImageIO.read(url) + + w = img.width + h = img.height + + result = Bitmap.new(w, h) + result.pixels = img.getRGB(0, 0, w, h, nil, 0, w) + + result.pixels.length.times do |i| + inp = result.pixels[i] + + col = (inp & 0xf) + col = -1 if inp == 0xffff00ff + result.pixels[i] = col + end + + result + end + + def self.get_col(c) + r = (c >> 16) & 0xff + g = (c >> 8) & 0xff + b = (c) & 0xff + + r = r * 0x55 / 0xff + g = g * 0x55 / 0xff + b = b * 0x55 / 0xff + + r << 16 | g << 8 | b + end + + FONT = load_bitmap("/tex/font.png") + LOGO = load_bitmap("/gui/logo.png") +end \ No newline at end of file diff --git a/escape/assets/gui/logo.png b/escape/assets/gui/logo.png new file mode 100644 index 0000000..d25112f Binary files /dev/null and b/escape/assets/gui/logo.png differ diff --git a/escape/assets/level/crypt.png b/escape/assets/level/crypt.png new file mode 100644 index 0000000..e0f6028 Binary files /dev/null and b/escape/assets/level/crypt.png differ diff --git a/escape/assets/level/dungeon.png b/escape/assets/level/dungeon.png new file mode 100644 index 0000000..be5c099 Binary files /dev/null and b/escape/assets/level/dungeon.png differ diff --git a/escape/assets/level/ice.png b/escape/assets/level/ice.png new file mode 100644 index 0000000..c80f5f2 Binary files /dev/null and b/escape/assets/level/ice.png differ diff --git a/escape/assets/level/overworld.png b/escape/assets/level/overworld.png new file mode 100644 index 0000000..466963d Binary files /dev/null and b/escape/assets/level/overworld.png differ diff --git a/escape/assets/level/start.png b/escape/assets/level/start.png new file mode 100644 index 0000000..19d89b8 Binary files /dev/null and b/escape/assets/level/start.png differ diff --git a/escape/assets/level/temple.png b/escape/assets/level/temple.png new file mode 100644 index 0000000..9194abf Binary files /dev/null and b/escape/assets/level/temple.png differ diff --git a/escape/assets/snd/altar.wav b/escape/assets/snd/altar.wav new file mode 100644 index 0000000..a80eb0b Binary files /dev/null and b/escape/assets/snd/altar.wav differ diff --git a/escape/assets/snd/bosskill.wav b/escape/assets/snd/bosskill.wav new file mode 100644 index 0000000..260e783 Binary files /dev/null and b/escape/assets/snd/bosskill.wav differ diff --git a/escape/assets/snd/click.wav b/escape/assets/snd/click.wav new file mode 100644 index 0000000..537b413 Binary files /dev/null and b/escape/assets/snd/click.wav differ diff --git a/escape/assets/snd/click2.wav b/escape/assets/snd/click2.wav new file mode 100644 index 0000000..8f3d433 Binary files /dev/null and b/escape/assets/snd/click2.wav differ diff --git a/escape/assets/snd/crumble.wav b/escape/assets/snd/crumble.wav new file mode 100644 index 0000000..174874b Binary files /dev/null and b/escape/assets/snd/crumble.wav differ diff --git a/escape/assets/snd/cut.wav b/escape/assets/snd/cut.wav new file mode 100644 index 0000000..36dcb1c Binary files /dev/null and b/escape/assets/snd/cut.wav differ diff --git a/escape/assets/snd/death.wav b/escape/assets/snd/death.wav new file mode 100644 index 0000000..d42e366 Binary files /dev/null and b/escape/assets/snd/death.wav differ diff --git a/escape/assets/snd/hit.wav b/escape/assets/snd/hit.wav new file mode 100644 index 0000000..f7772da Binary files /dev/null and b/escape/assets/snd/hit.wav differ diff --git a/escape/assets/snd/hurt.wav b/escape/assets/snd/hurt.wav new file mode 100644 index 0000000..5730a2c Binary files /dev/null and b/escape/assets/snd/hurt.wav differ diff --git a/escape/assets/snd/hurt2.wav b/escape/assets/snd/hurt2.wav new file mode 100644 index 0000000..f172521 Binary files /dev/null and b/escape/assets/snd/hurt2.wav differ diff --git a/escape/assets/snd/key.wav b/escape/assets/snd/key.wav new file mode 100644 index 0000000..be0d989 Binary files /dev/null and b/escape/assets/snd/key.wav differ diff --git a/escape/assets/snd/kill.wav b/escape/assets/snd/kill.wav new file mode 100644 index 0000000..68ab3e0 Binary files /dev/null and b/escape/assets/snd/kill.wav differ diff --git a/escape/assets/snd/ladder.wav b/escape/assets/snd/ladder.wav new file mode 100644 index 0000000..04f0ab1 Binary files /dev/null and b/escape/assets/snd/ladder.wav differ diff --git a/escape/assets/snd/pickup.wav b/escape/assets/snd/pickup.wav new file mode 100644 index 0000000..e8eac90 Binary files /dev/null and b/escape/assets/snd/pickup.wav differ diff --git a/escape/assets/snd/potion.wav b/escape/assets/snd/potion.wav new file mode 100644 index 0000000..3c9d06a Binary files /dev/null and b/escape/assets/snd/potion.wav differ diff --git a/escape/assets/snd/roll.wav b/escape/assets/snd/roll.wav new file mode 100644 index 0000000..f527f4e Binary files /dev/null and b/escape/assets/snd/roll.wav differ diff --git a/escape/assets/snd/shoot.wav b/escape/assets/snd/shoot.wav new file mode 100644 index 0000000..8f59a8b Binary files /dev/null and b/escape/assets/snd/shoot.wav differ diff --git a/escape/assets/snd/slide.wav b/escape/assets/snd/slide.wav new file mode 100644 index 0000000..e618f0d Binary files /dev/null and b/escape/assets/snd/slide.wav differ diff --git a/escape/assets/snd/splash.wav b/escape/assets/snd/splash.wav new file mode 100644 index 0000000..9c04d46 Binary files /dev/null and b/escape/assets/snd/splash.wav differ diff --git a/escape/assets/snd/thud.wav b/escape/assets/snd/thud.wav new file mode 100644 index 0000000..1abab4e Binary files /dev/null and b/escape/assets/snd/thud.wav differ diff --git a/escape/assets/snd/treasure.wav b/escape/assets/snd/treasure.wav new file mode 100644 index 0000000..4f51411 Binary files /dev/null and b/escape/assets/snd/treasure.wav differ diff --git a/escape/assets/tex/floors.png b/escape/assets/tex/floors.png new file mode 100644 index 0000000..647d5ef Binary files /dev/null and b/escape/assets/tex/floors.png differ diff --git a/escape/assets/tex/font.png b/escape/assets/tex/font.png new file mode 100644 index 0000000..4051500 Binary files /dev/null and b/escape/assets/tex/font.png differ diff --git a/escape/assets/tex/gamepanel.png b/escape/assets/tex/gamepanel.png new file mode 100644 index 0000000..86c3ac1 Binary files /dev/null and b/escape/assets/tex/gamepanel.png differ diff --git a/escape/assets/tex/items.png b/escape/assets/tex/items.png new file mode 100644 index 0000000..754de68 Binary files /dev/null and b/escape/assets/tex/items.png differ diff --git a/escape/assets/tex/sky.png b/escape/assets/tex/sky.png new file mode 100644 index 0000000..e7821bc Binary files /dev/null and b/escape/assets/tex/sky.png differ diff --git a/escape/assets/tex/sprites.png b/escape/assets/tex/sprites.png new file mode 100644 index 0000000..cbd2493 Binary files /dev/null and b/escape/assets/tex/sprites.png differ diff --git a/escape/assets/tex/walls.png b/escape/assets/tex/walls.png new file mode 100644 index 0000000..c722d0c Binary files /dev/null and b/escape/assets/tex/walls.png differ diff --git a/escape/component.rb b/escape/component.rb new file mode 100644 index 0000000..d76112c --- /dev/null +++ b/escape/component.rb @@ -0,0 +1,147 @@ +java_import java.awt.Canvas +java_import java.awt.BorderLayout +java_import java.awt.Dimension +java_import javax.swing.JFrame +java_import javax.swing.JPanel +java_import java.awt.Toolkit +java_import java.awt.image.BufferedImage +java_import java.awt.image.BufferStrategy +java_import java.awt.Point +java_import java.lang.System + +class Component < Canvas + include java.lang.Runnable + + SERIAL_VERSION_UID = 1 + WIDTH = 160 + HEIGHT = 120 + SCALE = 4 + + attr_accessor :running + + def initialize + super + + size = Dimension.new(WIDTH * SCALE, HEIGHT * SCALE) + self.minimum_size = self.maximum_size = self.preferred_size = self.size = size + + @game = Game.new + @screen = Screen.new(WIDTH, HEIGHT) + @img = BufferedImage.new(WIDTH, HEIGHT, BufferedImage::TYPE_INT_RGB) + @pixels = @img.raster.data_buffer.data + @input_handler = InputHandler.new + add_key_listener @input_handler + add_focus_listener @input_handler + add_mouse_listener @input_handler + add_mouse_motion_listener @input_handler + @empty_cursor = Toolkit.default_toolkit.create_custom_cursor(BufferedImage.new(16, 16, BufferedImage::TYPE_INT_ARGB), Point.new(0, 0), "empty") + @default_cursor = cursor + @running = false + end + + def start + return if @running + @running = true + @thread = java.lang.Thread.new(self) + @thread.start + end + + def stop + return unless @running + @running = false + begin + @thread.join + rescue => e + p e + end + end + + def run + frames = 0 + unprocessed_seconds = 0 + last_time = System.nano_time + seconds_per_tick = 1 / 61.0 + tick_count = 0 + + request_focus + + while running + now = System.nano_time + passed_time = now - last_time + last_time = now + passed_time = 0 if passed_time < 0 + passed_time = 100000000 if passed_time > 100000000 + + unprocessed_seconds += passed_time / 1000000000.0 + ticked = false + + while unprocessed_seconds > seconds_per_tick + tick + unprocessed_seconds -= seconds_per_tick + ticked = true + + tick_count += 1 + if tick_count % 60 == 0 + System.out.println frames.to_s + " fps" + last_time += 1000 + frames = 0 + end + end + + if ticked + render + frames += 1 + else + begin + java.lang.Thread.sleep 1 + rescue => e + p e + end + end + end + end + + def tick + @game.tick(@input_handler.keys) if has_focus + end + + def render + if has_focus != has_focus + has_focus = !had_focus + set_cursor had_focus ? @empty_cursor : @default_cursor + end + + bs = get_buffer_strategy + unless bs + create_buffer_strategy 3 + return + end + + @screen.render(@game, has_focus) + + 0.upto(WIDTH * HEIGHT - 1) do |i| + @pixels[i] = @screen.pixels[i] + end + + g = bs.draw_graphics + g.fill_rect 0, 0, get_width, get_height + g.draw_image @img, 0, 0, WIDTH * SCALE, HEIGHT * SCALE, nil + g.dispose + bs.show + end + + def self.main + game = new + frame = JFrame.new("Prelude of the Chambered!") + panel = JPanel.new(BorderLayout.new) + panel.add(game, BorderLayout::CENTER) + frame.content_pane = panel + frame.pack + frame.resizable = false + frame.location_relative_to = nil + frame.default_close_operation = JFrame::EXIT_ON_CLOSE + frame.visible = true + game.start + end +end + diff --git a/escape/escape.rb b/escape/escape.rb new file mode 100644 index 0000000..052bf35 --- /dev/null +++ b/escape/escape.rb @@ -0,0 +1,15 @@ +$: << File.expand_path(File.dirname(__FILE__)) + +ASSETS_DIR = File.expand_path(File.dirname(__FILE__) + "/assets") + +require 'java' +require 'input_handler' +require 'component' +require 'game' + +require 'gui/bitmap' +require 'gui/screen' + +require 'menu/menu' +require 'menu/title_menu' +require 'art' \ No newline at end of file diff --git a/escape/game.rb b/escape/game.rb new file mode 100644 index 0000000..89e4b6a --- /dev/null +++ b/escape/game.rb @@ -0,0 +1,65 @@ +java_import java.awt.event.KeyEvent + +class Game + attr_accessor :menu, :player, :time, :level + + def initialize + @pause_time = 0 + @time = 0 + @player = nil + @menu = TitleMenu.new + end + + def tick(keys) + if @pause_time > 0 + @pauseTime -= 1 + return + end + + @time += 1 + + strafe = keys[KeyEvent::VK_CONTROL] || keys[KeyEvent::VK_ALT] || keys[KeyEvent::VK_ALT_GRAPH] || keys[KeyEvent::VK_SHIFT] + + lk = keys[KeyEvent::VK_LEFT] || keys[KeyEvent::VK_NUMPAD4] + rk = keys[KeyEvent::VK_RIGHT] || keys[KeyEvent::VK_NUMPAD6] + + up = keys[KeyEvent::VK_W] || keys[KeyEvent::VK_UP] || keys[KeyEvent::VK_NUMPAD8] + down = keys[KeyEvent::VK_S] || keys[KeyEvent::VK_DOWN] || keys[KeyEvent::VK_NUMPAD2] + left = keys[KeyEvent::VK_A] || (strafe && lk) + right = keys[KeyEvent::VK_D] || (strafe && rk) + + turn_left = keys[KeyEvent::VK_Q] || (!strafe && lk) + turn_right = keys[KeyEvent::VK_E] || (!strafe && rk) + + use = keys[KeyEvent::VK_SPACE] + + 8.times do |i| + next unless keys[KeyEvent::VK_1 + i] + keys[KeyEvent::VK_1 + i] = false + @player.selected_slot = i + @player.item_use_time = 0 + end + + if keys[KeyEvent::VK_ESCAPE] + keys[KeyEvent::VK_ESCAPE] = false + set_menu PauseMenu.new unless menu + end + + keys[KeyEvent::VK_SPACE] = false if use + + if menu + keys[KeyEvent::VK_W] = keys[KeyEvent::VK_UP] = keys[KeyEvent::VK_NUMPAD8] = false + keys[KeyEvent::VK_S] = keys[KeyEvent::VK_DOWN] = keys[KeyEvent::VK_NUMPAD2] = false + keys[KeyEvent::VK_A] = false + keys[KeyEvent::VK_D] = false + + menu.tick self, up, down, left, right, use + else + player.tick up, down, left, right, turn_left, turn_right + player.activate if use + + level.tick + end + end + +end \ No newline at end of file diff --git a/escape/gui/bitmap.rb b/escape/gui/bitmap.rb new file mode 100644 index 0000000..d3c21dd --- /dev/null +++ b/escape/gui/bitmap.rb @@ -0,0 +1,53 @@ +class Bitmap + attr_accessor :pixels + attr_reader :height, :width + + CHARS = "" + + "ABCDEFGHIJKLMNOPQRSTUVWXYZ.,!?\"'/\\<>()[]{}" + + "abcdefghijklmnopqrstuvwxyz_ " + + "0123456789+-=*:;ÖÅÄå " + + ""; + + def initialize(width, height) + @width = width + @height = height + @pixels = Array.new(width * height, 0) + end + + def fill(x0, y0, x1, y1, color) + y0.upto(y1 - 1) do |y| + x0.upto(x1 - 1) do |x| + @pixels[x + y * @width] = color + end + end + end + + def draw_string(string, x, y, col) + string.chars.each_with_index do |char, i| + ch = CHARS.index(char) + next if ch < 0 + + xx = ch % 42 + yy = ch / 42 + draw_bitmap(Art::FONT, x + i * 6, y, xx * 6, yy * 8, 5, 8, col) + end + end + + def draw_bitmap(bitmap, x_offs, y_offs, xo, yo, w, h, col) + h.times do |y| + y_pix = y + y_offs + next if (y_pix < 0 || y_pix >= @height) + + w.times do |x| + x_pix = x + x_offs + next if (x_pix < 0 || x_pix >= @width) + + src = bitmap.pixels[(x + xo) + (y + yo) * bitmap.width] + + if src >= 0 + @pixels[x_pix + y_pix * width] = src * col + end + end + end + end +end \ No newline at end of file diff --git a/escape/gui/screen.rb b/escape/gui/screen.rb new file mode 100644 index 0000000..c67f3b9 --- /dev/null +++ b/escape/gui/screen.rb @@ -0,0 +1,38 @@ +class Screen < Bitmap + PANEL_HEIGHT = 29 + + def initialize(width, height) + super(width, height) + #@viewport = Bitmap3D.new(width, height - PANEL_HEIGHT) + end + + def render(game, has_focus) + if game.level + # TODO + else + fill 0, 0, @width, @height, 0 + end + + if game.menu + @pixels.length.times do |i| + @pixels[i] = (@pixels[i] & 0xfcfcfc) >> 2 + end + game.menu.render self + end + + unless has_focus + @pixels.length.times do |i| + @pixels[i] = (@pixels[i] & 0xfcfcfc) >> 2 + end + + if java.lang.System.current_time_millis / 450 % 2 != 0 + msg = "Click to focus!" + draw_string msg, (@width - msg.length() * 6) / 2, @height / 3 + 4, 0xffffff + end + end + + #0.upto(@width * @height - 1) do |i| + # @pixels[i] = rand(2).zero? ? 0x00ff0000 : 0x0000ff00 + #end + end +end \ No newline at end of file diff --git a/escape/input_handler.rb b/escape/input_handler.rb new file mode 100644 index 0000000..7a2cd30 --- /dev/null +++ b/escape/input_handler.rb @@ -0,0 +1,43 @@ +java_import java.awt.event.KeyListener +java_import java.awt.event.FocusListener +java_import java.awt.event.MouseListener +java_import java.awt.event.MouseMotionListener + +class InputHandler + include KeyListener + include FocusListener + include MouseListener + include MouseMotionListener + + attr_accessor :keys + + def initialize + super + @keys = Array.new(65536, false) + end + + def mouse_dragged(arg); end + def mouse_moved(arg); end + def mouse_clicked(arg); end + def mouse_entered(arg); end + def mouse_exited(arg); end + def mouse_pressed(arg); end + def mouse_released(arg); end + def focus_gained(arg); end + + def focus_lost(arg) + keys.each_with_index { |k, i| @keys[i] = true } + end + + def key_pressed(e) + code = e.key_code + @keys[code] = true if code > 0 && code < keys.length + end + + def key_released(e) + code = e.key_code + @keys[code] = false if code > 0 && code < keys.length + end + + def key_typed(arg); end +end \ No newline at end of file diff --git a/escape/menu/menu.rb b/escape/menu/menu.rb new file mode 100644 index 0000000..24a88d8 --- /dev/null +++ b/escape/menu/menu.rb @@ -0,0 +1,4 @@ +class Menu + def render(target); end + def tick(game, up, down, left, right, use); end +end \ No newline at end of file diff --git a/escape/menu/title_menu.rb b/escape/menu/title_menu.rb new file mode 100644 index 0000000..68caa78 --- /dev/null +++ b/escape/menu/title_menu.rb @@ -0,0 +1,54 @@ +class TitleMenu < Menu + + def initialize + super + @first_tick = true + @selected = 0 + @options = ["New game", "Instructions", "About"] + end + + def render(target) + target.fill(0, 0, 160, 120, 0) + target.draw_bitmap(Art::LOGO, 0, 8, 0, 0, 160, 36, Art.get_col(0xffffff)) + + @options.each_with_index do |msg, i| + col = 0x909090 + if @selected == i + msg = "-> " + msg + col = 0xffff80 + end + target.draw_string(msg, 40, 60 + i * 10, Art.get_col(col)) + end + + target.draw_string("Copyright (C) 2011 Mojang", 1+4, 120 - 9, Art.get_col(0x303030)) + end + + def tick(game, up, down, left, right, use) + if @first_tick + @first_tick = false + #Sound.altar.play() + end + + #Sound.click2.play if up || down + @selected -= 1 if up + @selected += 1 if down + + @selected = 0 if @selected < 0 + @selected = @options.length - 1 if @selected >= @options.length + + if use + #Sound.click1.play() + if @selected == 0 + game.menu = nil + game.new_game + end + if @selected == 1 + game.set_menu(InstructionsMenu.new) + end + if @selected == 2 + game.set_menu(AboutMenu.new) + end + end + end + +end \ No newline at end of file