mirror of
https://github.com/cyberarm/i-mic-fps.git
synced 2025-12-15 15:42: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:
@@ -1,9 +1,10 @@
|
||||
context Vehicle # Generic, Weapon
|
||||
component(:vehicle) # Generic, Weapon
|
||||
|
||||
on.button_down(:interact) do |event|
|
||||
if event.player.touching?(event.entity)
|
||||
event.player.enter_vehicle
|
||||
elsif event.player.driving?(event.entity) or event.player.passenger?(event.entity)
|
||||
event.player.exit_vehicle
|
||||
end
|
||||
$window.console.stdin("#{event.entity.name} handled button_down(:interact)")
|
||||
# if event.player.touching?(event.entity)
|
||||
# event.player.enter_vehicle
|
||||
# elsif event.player.driving?(event.entity) or event.player.passenger?(event.entity)
|
||||
# event.player.exit_vehicle
|
||||
# end
|
||||
end
|
||||
@@ -1,8 +1,8 @@
|
||||
context Building
|
||||
component(:building)
|
||||
|
||||
on.create do |event|
|
||||
map.add_entity("base", "purchase_terminal", Vector.new(0, 1.5, 0), Vector.new(0, 90, 0), data: {team: event.entity.team})
|
||||
map.add_entity("base", "information_panel", Vector.new(2, 1.5, 0), Vector.new(0, 0, 0))
|
||||
map.add_entity("base", "door", Vector.new(2, 0, 6), Vector.new(0, 0, 0))
|
||||
map.add_entity("base", "door", Vector.new(2, 0, 6), Vector.new(0, 180, 0))
|
||||
event.map.insert_entity("base", "purchase_terminal", event.entity.position + Vector.new(1.5, 1.5, 4.52), Vector.new(0, 200, 0), data: {team: nil})
|
||||
event.map.insert_entity("base", "information_panel", event.entity.position + Vector.new(-3, 0, -1), Vector.new(0, 90, 0))
|
||||
event.map.insert_entity("base", "door", event.entity.position + Vector.new(0, 0, -6), Vector.new(0, 0, 0))
|
||||
event.map.insert_entity("base", "door", event.entity.position + Vector.new(0, 0, -6), Vector.new(0, 180, 0))
|
||||
end
|
||||
@@ -1,2 +1,5 @@
|
||||
name: "war_factory"
|
||||
model: "war_factory.obj"
|
||||
scripts: [
|
||||
"war_factory"
|
||||
]
|
||||
8
assets/base/war_factory/scripts/war_factory.rb
Normal file
8
assets/base/war_factory/scripts/war_factory.rb
Normal file
@@ -0,0 +1,8 @@
|
||||
component(:building)
|
||||
|
||||
on.create do |event|
|
||||
event.map.insert_entity("base", "purchase_terminal", event.entity.position + Vector.new(6, 1.5, 3), Vector.new(0, -90, 0), data: {team: nil})
|
||||
event.map.insert_entity("base", "information_panel", event.entity.position + Vector.new(0.5, 0, 3), Vector.new(0, 90, 0))
|
||||
event.map.insert_entity("base", "door", event.entity.position + Vector.new(3.3, 0, 6), Vector.new(0, 0, 0))
|
||||
event.map.insert_entity("base", "door", event.entity.position + Vector.new(3.3, 0, 6), Vector.new(0, 180, 0))
|
||||
end
|
||||
Binary file not shown.
Binary file not shown.
11
i-mic-fps.rb
11
i-mic-fps.rb
@@ -56,6 +56,17 @@ require_relative "lib/ui/menus/main_menu"
|
||||
require_relative "lib/states/game_states/game"
|
||||
require_relative "lib/states/game_states/loading_state"
|
||||
|
||||
require_relative "lib/scripting"
|
||||
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_lifecycle"
|
||||
|
||||
require_relative "lib/component"
|
||||
require_relative "lib/components/building"
|
||||
|
||||
require_relative "lib/game_objects/entity"
|
||||
require_relative "lib/game_objects/model_loader"
|
||||
require_relative "lib/game_objects/light"
|
||||
|
||||
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")
|
||||
|
||||
@@ -85,70 +85,6 @@
|
||||
"scale": 1,
|
||||
"scripts": []
|
||||
},
|
||||
{
|
||||
"package":"base",
|
||||
"name":"purchase_terminal",
|
||||
"position": {
|
||||
"x":37.8788,
|
||||
"y":1.843869,
|
||||
"z":-6.06061
|
||||
},
|
||||
"orientation": {
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"z": 0
|
||||
},
|
||||
"scale": 1,
|
||||
"scripts": []
|
||||
},
|
||||
{
|
||||
"package":"base",
|
||||
"name":"information_panel",
|
||||
"position": {
|
||||
"x":37.8788,
|
||||
"y":0.343869,
|
||||
"z":-6.06061
|
||||
},
|
||||
"orientation": {
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"z": 0
|
||||
},
|
||||
"scale": 1,
|
||||
"scripts": []
|
||||
},
|
||||
{
|
||||
"package":"base",
|
||||
"name":"door",
|
||||
"position": {
|
||||
"x":37.8788,
|
||||
"y":0.343869,
|
||||
"z":-12.06061
|
||||
},
|
||||
"orientation": {
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"z": 0
|
||||
},
|
||||
"scale": 1,
|
||||
"scripts": []
|
||||
},
|
||||
{
|
||||
"package":"base",
|
||||
"name":"door",
|
||||
"position": {
|
||||
"x":37.8788,
|
||||
"y":0.343869,
|
||||
"z":-12.06061
|
||||
},
|
||||
"orientation": {
|
||||
"x": 0,
|
||||
"y": 180,
|
||||
"z": 0
|
||||
},
|
||||
"scale": 1,
|
||||
"scripts": []
|
||||
},
|
||||
{
|
||||
"package":"base",
|
||||
"name":"war_factory",
|
||||
|
||||
Reference in New Issue
Block a user