mirror of
https://github.com/cyberarm/i-mic-fps.git
synced 2025-12-15 23:52:35 +00:00
Implemented event system, Implemented initial bit of scripting system, Stubbed component system. Entities can now use the scripting system to place their 'decorations'
This commit is contained in:
40
lib/component.rb
Normal file
40
lib/component.rb
Normal file
@@ -0,0 +1,40 @@
|
||||
class IMICFPS
|
||||
class Component
|
||||
COMPONENTS = {}
|
||||
|
||||
def self.get(name)
|
||||
COMPONENTS.dig(name)
|
||||
end
|
||||
|
||||
def self.inherited(subclass)
|
||||
pp subclass
|
||||
COMPONENTS["__pending"] ||= []
|
||||
COMPONENTS["__pending"] << subclass
|
||||
end
|
||||
|
||||
def self.initiate
|
||||
COMPONENTS["__pending"].each do |klass|
|
||||
component = klass.new
|
||||
COMPONENTS[component.name] = component
|
||||
end
|
||||
|
||||
COMPONENTS.delete("__pending")
|
||||
end
|
||||
|
||||
def initialize
|
||||
setup
|
||||
end
|
||||
|
||||
def name
|
||||
string = self.class.name.split("::").last
|
||||
split = string.scan(/[A-Z][a-z]*/)
|
||||
|
||||
component_name = "#{split.map { |s| s.downcase }.join("_")}".to_sym
|
||||
|
||||
return component_name
|
||||
end
|
||||
|
||||
def setup
|
||||
end
|
||||
end
|
||||
end
|
||||
6
lib/components/building.rb
Normal file
6
lib/components/building.rb
Normal file
@@ -0,0 +1,6 @@
|
||||
class IMICFPS
|
||||
class Components
|
||||
class Building < Component
|
||||
end
|
||||
end
|
||||
end
|
||||
10
lib/event.rb
Normal file
10
lib/event.rb
Normal file
@@ -0,0 +1,10 @@
|
||||
class IMICFPS
|
||||
class EventHandler
|
||||
class Event
|
||||
attr_reader :entity, :context, :map, :player
|
||||
def initialize(entity:, context: nil, map: $window.current_state, player: nil)
|
||||
@entity, @context, @map, @player = entity, context, map, player
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
37
lib/event_handler.rb
Normal file
37
lib/event_handler.rb
Normal file
@@ -0,0 +1,37 @@
|
||||
class IMICFPS
|
||||
class EventHandler
|
||||
@@handlers = {}
|
||||
|
||||
def self.inherited(subclass)
|
||||
@@handlers["__pending"] ||= []
|
||||
|
||||
@@handlers["__pending"] << subclass
|
||||
end
|
||||
|
||||
def self.initiate
|
||||
@@handlers["__pending"].each do |handler|
|
||||
instance = handler.new
|
||||
instance.handles.each do |event|
|
||||
@@handlers[event] = instance
|
||||
end
|
||||
end
|
||||
|
||||
@@handlers.delete("__pending")
|
||||
end
|
||||
|
||||
def self.get(event)
|
||||
@@handlers.dig(event)
|
||||
end
|
||||
|
||||
def initialize
|
||||
end
|
||||
|
||||
def handlers
|
||||
raise NotImplementedError
|
||||
end
|
||||
|
||||
def handle(subscriber, context, *args)
|
||||
raise NotImplementedError
|
||||
end
|
||||
end
|
||||
end
|
||||
16
lib/event_handlers/entity_lifecycle.rb
Normal file
16
lib/event_handlers/entity_lifecycle.rb
Normal file
@@ -0,0 +1,16 @@
|
||||
class IMICFPS
|
||||
class EventHandler
|
||||
class EntityLifeCycle < EventHandler
|
||||
def handles
|
||||
[:create, :entity_move, :destroy]
|
||||
end
|
||||
|
||||
def handle(subscriber, context, *args)
|
||||
return unless subscriber.entity == args.first.first
|
||||
event = EventHandler::Event.new(entity: subscriber.entity, context: context)
|
||||
|
||||
subscriber.trigger(event)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
24
lib/event_handlers/input.rb
Normal file
24
lib/event_handlers/input.rb
Normal file
@@ -0,0 +1,24 @@
|
||||
class IMICFPS
|
||||
class EventHandler
|
||||
class Input < EventHandler
|
||||
def handles
|
||||
[:button_down, :button_up]
|
||||
end
|
||||
|
||||
def handle(subscriber, context, *args)
|
||||
action = subscriber.args.flatten.first
|
||||
key = args.flatten.first
|
||||
|
||||
event = EventHandler::Event.new(entity: subscriber.entity, context: context, player: context)
|
||||
|
||||
if action.is_a?(Numeric) && action == key
|
||||
subscriber.trigger(event)
|
||||
else
|
||||
if InputMapper.get(action) == key
|
||||
subscriber.trigger(event)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -4,6 +4,7 @@ class IMICFPS
|
||||
# A game object is any renderable thing
|
||||
class Entity
|
||||
include CommonMethods
|
||||
include Scripting
|
||||
|
||||
attr_accessor :scale, :visible, :renderable, :backface_culling
|
||||
attr_accessor :position, :orientation, :velocity
|
||||
@@ -38,6 +39,8 @@ class IMICFPS
|
||||
@delta_time = Gosu.milliseconds
|
||||
@last_position = Vector.new(@position.x, @position.y, @position.z)
|
||||
|
||||
load_scripts
|
||||
|
||||
setup
|
||||
|
||||
if @bound_model
|
||||
@@ -52,6 +55,20 @@ class IMICFPS
|
||||
return self
|
||||
end
|
||||
|
||||
def load_scripts
|
||||
@manifest.scripts.each do |script|
|
||||
instance_eval(script.source)
|
||||
end
|
||||
end
|
||||
|
||||
def method_missing(method, *args, &block)
|
||||
unless component = Component.get(method)
|
||||
raise NoMemoryError, "undefined method '#{method}' for #<#{self.class}:#{self.object_id}>"
|
||||
else
|
||||
return component
|
||||
end
|
||||
end
|
||||
|
||||
def collidable?
|
||||
@collidable.include?(@collision)
|
||||
end
|
||||
|
||||
@@ -91,10 +91,10 @@ class IMICFPS
|
||||
vs = a
|
||||
vs = b if a == entity
|
||||
|
||||
broadphase = search(Ray.new(entity.position, Vector.new(0, -1, 0), entity.velocity.y.abs))
|
||||
broadphase = search(Ray.new(entity.position, Vector.down, entity.velocity.y.abs))
|
||||
|
||||
broadphase.detect do |ent|
|
||||
ray = Ray.new(entity.position - ent.position, Vector.new(0, -1, 0))
|
||||
ray = Ray.new(entity.position - ent.position, Vector.down)
|
||||
if ent.model.aabb_tree.search(ray).size > 0
|
||||
on_ground = true
|
||||
return true
|
||||
|
||||
@@ -2,9 +2,15 @@ class IMICFPS
|
||||
module EntityManager # Get included into GameState context
|
||||
def add_entity(entity)
|
||||
@collision_manager.add(entity)# Add every entity to collision manager
|
||||
@publisher.publish(:create, self, entity)
|
||||
@entities << entity
|
||||
end
|
||||
|
||||
def insert_entity(package, name, position, orientation, data = {})
|
||||
ent = Map::Entity.new(package, name, position, orientation, Vector.new(1,1,1))
|
||||
add_entity(IMICFPS::Entity.new(map_entity: ent, manifest: Manifest.new(package: package, name: name)))
|
||||
end
|
||||
|
||||
def find_entity(entity)
|
||||
@entities.detect {|entity| entity == entity}
|
||||
end
|
||||
@@ -13,6 +19,7 @@ class IMICFPS
|
||||
ent = @entities.detect {|entity| entity == entity}
|
||||
if ent
|
||||
@collision_manager.remove(entity)
|
||||
@publisher.publish(:destroy, self, entity)
|
||||
@entities.delete(ent)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -115,6 +115,8 @@ IMICFPS::InputMapper.set(:jump, Gosu::KbSpace)
|
||||
IMICFPS::InputMapper.set(:sprint, [Gosu::KbLeftControl])
|
||||
IMICFPS::InputMapper.set(:turn_180, Gosu::KbX)
|
||||
|
||||
IMICFPS::InputMapper.set(:interact, Gosu::KbE)
|
||||
|
||||
IMICFPS::InputMapper.set(:ascend, Gosu::KbSpace)
|
||||
IMICFPS::InputMapper.set(:descend, Gosu::KbC)
|
||||
IMICFPS::InputMapper.set(:toggle_first_person_view, Gosu::KbF)
|
||||
|
||||
@@ -12,8 +12,6 @@ class IMICFPS
|
||||
|
||||
@file = manifest_file
|
||||
parse(manifest_file)
|
||||
|
||||
pp @scripts
|
||||
end
|
||||
|
||||
def parse(file)
|
||||
|
||||
39
lib/publisher.rb
Normal file
39
lib/publisher.rb
Normal file
@@ -0,0 +1,39 @@
|
||||
class IMICFPS
|
||||
class Publisher
|
||||
def self.subscribe(subscription)
|
||||
raise "Expected IMICFPS::Subscription not #{subscription.class}" unless subscription.is_a?(IMICFPS::Subscription)
|
||||
Publisher.instance.add_sub(subscription)
|
||||
end
|
||||
|
||||
def self.unsubscribe(subscription)
|
||||
end
|
||||
|
||||
def self.instance
|
||||
@@instance
|
||||
end
|
||||
|
||||
def initialize
|
||||
@@instance = self
|
||||
EventHandler.initiate
|
||||
Component.initiate
|
||||
@events = {}
|
||||
end
|
||||
|
||||
def add_sub(subscription)
|
||||
raise "Expected IMICFPS::Subscription not #{subscription.class}" unless subscription.is_a?(IMICFPS::Subscription)
|
||||
@events[subscription.event] ||= []
|
||||
|
||||
@events[subscription.event] << subscription
|
||||
end
|
||||
|
||||
def publish(event, context, *args)
|
||||
if subscribers = @events.dig(event)
|
||||
return unless event_handler = EventHandler.get(event)
|
||||
|
||||
subscribers.each do |subscriber|
|
||||
event_handler.handle(subscriber, context, args)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
11
lib/scripting.rb
Normal file
11
lib/scripting.rb
Normal file
@@ -0,0 +1,11 @@
|
||||
class IMICFPS
|
||||
module Scripting
|
||||
def on
|
||||
Subscription.new(self)
|
||||
end
|
||||
|
||||
def component(name)
|
||||
Component.get(name)
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -5,6 +5,8 @@ class IMICFPS
|
||||
def setup
|
||||
@collision_manager = CollisionManager.new(game_state: self)
|
||||
@renderer = Renderer.new(game_state: self)
|
||||
@publisher = Publisher.new
|
||||
|
||||
@map = @options[:map]
|
||||
add_entity(Terrain.new(map_entity: @map.terrain, manifest: Manifest.new(package: @map.terrain.package, name: @map.terrain.name)))
|
||||
|
||||
@@ -85,6 +87,8 @@ class IMICFPS
|
||||
@last_frame_time = Gosu.milliseconds
|
||||
update_text
|
||||
|
||||
@publisher.publish(:tick, Gosu.milliseconds - @delta_time)
|
||||
|
||||
@collision_manager.update
|
||||
@entities.each(&:update)
|
||||
|
||||
@@ -202,6 +206,7 @@ eos
|
||||
@demo_changed = true
|
||||
end
|
||||
InputMapper.keydown(id)
|
||||
@publisher.publish(:button_down, nil, id)
|
||||
|
||||
@entities.each do |entity|
|
||||
entity.button_down(id) if defined?(entity.button_down)
|
||||
@@ -218,6 +223,7 @@ eos
|
||||
@demo_changed = true
|
||||
end
|
||||
InputMapper.keyup(id)
|
||||
@publisher.publish(:button_up, nil, id)
|
||||
|
||||
@entities.each do |entity|
|
||||
entity.button_up(id) if defined?(entity.button_up)
|
||||
|
||||
40
lib/subscription.rb
Normal file
40
lib/subscription.rb
Normal file
@@ -0,0 +1,40 @@
|
||||
class IMICFPS
|
||||
class Subscription
|
||||
attr_reader :entity, :event, :args, :block
|
||||
def initialize(entity)
|
||||
@entity = entity
|
||||
|
||||
@event = nil
|
||||
@args = nil
|
||||
@block = nil
|
||||
end
|
||||
|
||||
def method_missing(event, *args, &block)
|
||||
return unless subscribable_events.include?(event)
|
||||
|
||||
@event, @args, @block = event, args, block
|
||||
Publisher.subscribe(self)
|
||||
end
|
||||
|
||||
def trigger(event, *args)
|
||||
if @block
|
||||
@block.call(event, *args)
|
||||
end
|
||||
end
|
||||
|
||||
private def subscribable_events
|
||||
[
|
||||
:tick,
|
||||
:create,
|
||||
:destroy,
|
||||
:button_down, :button_up,
|
||||
:mouse_move,
|
||||
:entity_move,
|
||||
:interact,
|
||||
:player_join, :player_leave, :player_die,
|
||||
:pickup_item, :use_item, :drop_item,
|
||||
:enter_vehicle, :exit_vehicle,
|
||||
]
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -3,6 +3,7 @@ class IMICFPS
|
||||
attr_accessor :number_of_vertices, :needs_cursor
|
||||
attr_reader :camera
|
||||
|
||||
attr_reader :console
|
||||
def initialize(window_width = 1280, window_height = 800, fullscreen = false)
|
||||
fps_target = (ARGV.first.to_i != 0) ? ARGV.first.to_i : 60
|
||||
if ARGV.join.include?("--native")
|
||||
|
||||
Reference in New Issue
Block a user