diff --git a/README.md b/README.md index c09bc6a..6964647 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # I-MIC FPS Creating a multiplayer first-person-shooter in pure Ruby; Using C extensions only for Rendering, Sound, and Input. ([Gosu](https://libgosu.org) and [opengl-bindings](https://github.com/vaiorabbit/ruby-opengl/)) -![](https://raw.githubusercontent.com/cyberarm/i-mic-fps/master/screenshots/screenshot-game.png) +![screenshot](https://raw.githubusercontent.com/cyberarm/i-mic-fps/master/screenshots/screenshot-game.png) ## Using Requires a Ruby runtime that supports the gosu and opengl-bindings C-extensions (truffleruby 1.0.0-rc12 did not work when tested. Rubinus was not tested.) diff --git a/assets/base/shared/sound/master.yaml b/assets/base/shared/sound/master.yaml new file mode 100644 index 0000000..75768ad --- /dev/null +++ b/assets/base/shared/sound/master.yaml @@ -0,0 +1,11 @@ +--- +playlists: + - menus: + - nighttime: + - daytime: +music: +sounds: + - + name: shield_regen + type: sfx + path: sfx/shield_regen.wav \ No newline at end of file diff --git a/assets/base/shared/sound/sfx/shield_regen.wav b/assets/base/shared/sound/sfx/shield_regen.wav new file mode 100644 index 0000000..514c16a Binary files /dev/null and b/assets/base/shared/sound/sfx/shield_regen.wav differ diff --git a/i-mic-fps.rb b/i-mic-fps.rb index 1f61304..adf7d3e 100644 --- a/i-mic-fps.rb +++ b/i-mic-fps.rb @@ -20,118 +20,35 @@ end Dir.chdir(File.dirname(__FILE__)) -require_relative "lib/ext/numeric" -require_relative "lib/ext/load_opengl" - include CyberarmEngine include OpenGL include GLU -require_relative "lib/version" -require_relative "lib/constants" -require_relative "lib/common_methods" +def require_all(directory) + files = Dir["#{directory}/**/*.rb"].sort! -require_relative "lib/trees/aabb_tree_debug" -require_relative "lib/trees/aabb_tree" -require_relative "lib/trees/aabb_node" + begin + failed = [] + first_name_error = nil -require_relative "lib/managers/input_mapper" -require_relative "lib/managers/entity_manager" -require_relative "lib/managers/light_manager" -require_relative "lib/managers/network_manager" -require_relative "lib/managers/collision_manager" -require_relative "lib/managers/physics_manager" + files.each do |file| + begin + require_relative file + rescue NameError => name_error + failed << file + first_name_error ||= name_error + end + end -require_relative "lib/renderer/renderer" -require_relative "lib/renderer/g_buffer" -require_relative "lib/renderer/opengl_renderer" -require_relative "lib/renderer/bounding_box_renderer" - -require_relative "lib/states/game_state" -require_relative "lib/ui/menu" - -require_relative "lib/ui/command" -require_relative "lib/ui/subcommand" -Dir.glob("#{IMICFPS::GAME_ROOT_PATH}/lib/ui/commands/*.rb").each do |cmd| - require_relative cmd + if failed.size == files.size + raise first_name_error + else + files = failed + end + end until( failed.empty? ) end -require_relative "lib/ui/console" -require_relative "lib/ui/menus/main_menu" -require_relative "lib/ui/menus/settings_menu" -require_relative "lib/ui/menus/extras_menu" -require_relative "lib/ui/menus/multiplayer_menu" -require_relative "lib/ui/menus/level_select_menu" -require_relative "lib/ui/menus/game_pause_menu" -require_relative "lib/states/game_states/boot" -require_relative "lib/states/game_states/close" -require_relative "lib/states/game_states/game" -require_relative "lib/states/game_states/loading_state" - -require_relative "lib/hud" -require_relative "lib/hud/widget" -require_relative "lib/hud/widgets/ammo" -require_relative "lib/hud/widgets/radar" -require_relative "lib/hud/widgets/health" - -require_relative "lib/subscription" -require_relative "lib/publisher" -require_relative "lib/event" -require_relative "lib/event_handler" -require_relative "lib/event_handlers/input" -require_relative "lib/event_handlers/entity_moved" -require_relative "lib/event_handlers/entity_lifecycle" - -require_relative "lib/scripting" -require_relative "lib/scripting/sandbox" -require_relative "lib/scripting/whitelist" - -require_relative "lib/component" -require_relative "lib/components/building" - -require_relative "lib/game_objects/entity" -require_relative "lib/game_objects/light" -require_relative "lib/game_objects/particle_emitter" - -require_relative "lib/game_objects/camera" -require_relative "lib/game_objects/entities/player" -require_relative "lib/game_objects/entities/skydome" -require_relative "lib/game_objects/entities/terrain" - -require_relative "lib/texture" -require_relative "lib/model" -require_relative "lib/model_cache" -require_relative "lib/model/parser" -require_relative "lib/model/model_object" -require_relative "lib/model/material" - -require_relative "lib/model/parsers/wavefront_parser" -require_relative "lib/model/parsers/collada_parser" - -require_relative "lib/map_parser" -require_relative "lib/manifest" -require_relative "lib/map" - -require_relative "lib/scene" -require_relative "lib/scenes/turn_table" - -require_relative "lib/crosshair" -require_relative "lib/demo" - -require_relative "lib/networking/director" -require_relative "lib/networking/packet_handler" -require_relative "lib/networking/client" -require_relative "lib/networking/server" -require_relative "lib/networking/connection" - -require_relative "lib/networking/backends/memory_server" -require_relative "lib/networking/backends/memory_connection" - -require_relative "lib/overlay" -require_relative "lib/window" - -require_relative "lib/tools/asset_viewer" -require_relative "lib/tools/map_editor" +require_all "lib" # Don't launch game if IMICFPS_SERVER_MODE is defined # or if game is being packaged @@ -167,4 +84,4 @@ unless prevent_launch?[0] end else puts prevent_launch?[1] -end \ No newline at end of file +end diff --git a/lib/managers/sound_manager.rb b/lib/managers/sound_manager.rb new file mode 100644 index 0000000..12628ef --- /dev/null +++ b/lib/managers/sound_manager.rb @@ -0,0 +1,53 @@ +class IMICFPS + module SoundManager + extend CyberarmEngine::Common + + MASTERS = {} + EFFECTS = [] + PLAYLISTS = {} + + def self.master_volume + 1.0 + end + + def self.music_volume + 0.25 * master_volume + end + + def self.sfx_volume + 0.5 * master_volume + end + + def self.load_master(package) + return if MASTERS.dig(package) + + yaml = YAML.load_file( "#{IMICFPS.assets_path}/#{package}/shared/sound/master.yaml" ) + MASTERS[package] = yaml + end + + def self.sound(package, name) + if data = sound_data(package, name.to_s) + get_sample("#{IMICFPS.assets_path}/#{package}/shared/sound/#{data["path"]}") + else + raise "Missing sound: '#{name}' in package '#{package}'" + end + end + + def self.sound_data(package, name) + load_master(package) + if master = MASTERS.dig(package) + return master["sounds"].find { |s| s["name"] == name } + end + + return nil + end + + def self.sound_effect(klass, options) + EFFECTS << klass.new(options) + end + + def self.update + EFFECTS.each { |e| e.update; EFFECTS.delete(e) if e.done? } + end + end +end \ No newline at end of file diff --git a/lib/overlay.rb b/lib/overlay.rb index a3048c1..ab5ddf4 100644 --- a/lib/overlay.rb +++ b/lib/overlay.rb @@ -29,7 +29,7 @@ class IMICFPS if window.config.get(:options, :fps) create_slot "FPS: #{Gosu.fps}" - create_slot "Frame time: #{Gosu.milliseconds - window.delta_time}ms" if window.config.get(:debug_options, :stats) + create_slot "Frame time: #{(Gosu.milliseconds - window.delta_time).to_s.rjust(3, "0")}ms" if window.config.get(:debug_options, :stats) end if window.config.get(:debug_options, :stats) diff --git a/lib/sound_effect.rb b/lib/sound_effect.rb new file mode 100644 index 0000000..04e0447 --- /dev/null +++ b/lib/sound_effect.rb @@ -0,0 +1,19 @@ +class IMICFPS + class SoundEffect + attr_reader :sound, :options + def initialize(options = {}) + raise "expected Hash, got #{options.class}" unless options.is_a?(Hash) + @options = options + + raise "sound not specified!" unless @options[:sound] + + setup + end + + def setup + end + + def update + end + end +end \ No newline at end of file diff --git a/lib/sound_effects/fade_in.rb b/lib/sound_effects/fade_in.rb new file mode 100644 index 0000000..5ddcd69 --- /dev/null +++ b/lib/sound_effects/fade_in.rb @@ -0,0 +1,32 @@ +class IMICFPS + class SoundEffect + class FadeIn < SoundEffect + def setup + @start_time = Gosu.milliseconds + @duration = @options[:duration] # in milliseconds + @initial_volume = @options[:volume] ? @options[:volume] : 0.0 + @sound = @options[:sound] + + raise "duration not specified!" unless @duration + + @channel = @sound.play(calculate_volume) + end + + def ratio + (Gosu.milliseconds - @start_time.to_f) / @duration + end + + def calculate_volume + volume = (SoundManager.sfx_volume - @initial_volume) * ratio + end + + def update + @channel.volume = calculate_volume + end + + def done? + (Gosu.milliseconds - @start_time.to_f) / @duration >= 1.0 + end + end + end +end \ No newline at end of file diff --git a/lib/sound_effects/fade_in_and_out.rb b/lib/sound_effects/fade_in_and_out.rb new file mode 100644 index 0000000..4a617d7 --- /dev/null +++ b/lib/sound_effects/fade_in_and_out.rb @@ -0,0 +1,22 @@ +class IMICFPS + class SoundEffect + class FadeInAndOut < FadeIn + def setup + @hang_time = @options[:hang_time] ? @options[:hang_time] : 0.0 + + super + end + + # TODO: Handle hang time + def ratio + r = super + + if r < 0.5 + r * 2 + else + 2.0 - (r * 2) + end + end + end + end +end \ No newline at end of file diff --git a/lib/sound_effects/fade_out.rb b/lib/sound_effects/fade_out.rb new file mode 100644 index 0000000..628bc77 --- /dev/null +++ b/lib/sound_effects/fade_out.rb @@ -0,0 +1,9 @@ +class IMICFPS + class SoundEffect + class FadeOut < FadeIn + def ratio + 1.0 - super + end + end + end +end \ No newline at end of file diff --git a/lib/sound_effects/shield_regen.rb b/lib/sound_effects/shield_regen.rb new file mode 100644 index 0000000..9d1b9f5 --- /dev/null +++ b/lib/sound_effects/shield_regen.rb @@ -0,0 +1,25 @@ +class IMICFPS + class SoundEffect + class ShieldRegen < SoundEffect + def setup + @sound = SoundManager.sound("base", :shield_regen) + @player = @options[:player] + + @channel = @sound.play(0.0, 0.0, true) + end + + def ratio + @player.health + end + + def update + @channel.speed = 0.5 + ratio / 2 + @channel.volume = 1.0 - ratio / 2 + end + + def done? + ratio >= 1.0 + end + end + end +end \ No newline at end of file diff --git a/lib/states/game_states/boot.rb b/lib/states/game_states/boot.rb index 50e3f07..aaa03b3 100644 --- a/lib/states/game_states/boot.rb +++ b/lib/states/game_states/boot.rb @@ -7,6 +7,7 @@ class IMICFPS @start_time = Gosu.milliseconds @time_to_live = 3_000 + # SoundManager.sound_effect(SoundEffect::FadeIn, sound: SoundManager.sound("base", :shield_regen), duration: 3_000.0) window.needs_cursor = false end @@ -32,6 +33,7 @@ class IMICFPS @title.draw + fill(Gosu::Color.rgba(0,0,0, 255 * (1.2 - fraction_left))) end end diff --git a/lib/window.rb b/lib/window.rb index 63a5d2b..985a8b9 100644 --- a/lib/window.rb +++ b/lib/window.rb @@ -68,6 +68,7 @@ class IMICFPS @console.update if @show_console @overlay.update + SoundManager.update @number_of_vertices = 0 @delta_time = Gosu.milliseconds