worlds-terminal 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/bin/worlds +24 -0
- data/lib/worlds/area.rb +21 -0
- data/lib/worlds/components/component.rb +37 -0
- data/lib/worlds/components/ticker.rb +10 -0
- data/lib/worlds/components/travel.rb +25 -0
- data/lib/worlds/entity.rb +20 -0
- data/lib/worlds/special_commands.rb +10 -0
- data/lib/worlds/terminal/helper.rb +42 -0
- data/lib/worlds/terminal/runner.rb +100 -0
- data/lib/worlds/terminal/updater.rb +69 -0
- data/lib/worlds/terminal/version.rb +5 -0
- data/lib/worlds/world.rb +87 -0
- metadata +159 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 5f66773252b9e9c4dde7a90174f3715e8be7cb32ba46bf88833698f631aa8a71
|
4
|
+
data.tar.gz: 20ef4d48c714b41cf58b236d3368b80357a212b44bf0f7ed5b23ab8b1e930733
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 221cf441e1d4a20f2f86de54c71fbe3db670e9db1f0f7888a92749e2988cdc1bcd04404cf1df8bf9a1509c0e0413a085cac0f2b8180b56606368886ee7f662cc
|
7
|
+
data.tar.gz: 0c0f66fb15ae29187705e1360040da4113777cb044bf6c2baea029fc230eb9d4e5a2c361fd48defc0d36ac0b33e06381073438ca204f730bdc51e154d2830f62
|
data/bin/worlds
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
# Runs the game.
|
4
|
+
|
5
|
+
require_relative '../lib/worlds/terminal/helper'
|
6
|
+
require_relative '../lib/worlds/terminal/runner'
|
7
|
+
|
8
|
+
# Not namespaced as Worlds::Terminal because the Worlds module will eventually be
|
9
|
+
# split into its own gem, which can be used by more than this terminal interface
|
10
|
+
# (e.g. a web interface).
|
11
|
+
module Worlds
|
12
|
+
module Terminal
|
13
|
+
Helper.io_mode_raw!
|
14
|
+
Helper.hide_cursor!
|
15
|
+
|
16
|
+
begin
|
17
|
+
Runner.io_loop
|
18
|
+
ensure
|
19
|
+
Helper.show_cursor!
|
20
|
+
Helper.io_mode_normal!
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
data/lib/worlds/area.rb
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
module Worlds
|
2
|
+
class Area
|
3
|
+
attr_reader :name, :entities, :linked_areas
|
4
|
+
|
5
|
+
def initialize(name)
|
6
|
+
@name = name
|
7
|
+
@entities = []
|
8
|
+
@linked_areas = []
|
9
|
+
end
|
10
|
+
|
11
|
+
def update(ms)
|
12
|
+
entities.flat_map { |entity|
|
13
|
+
entity.update(ms)
|
14
|
+
}
|
15
|
+
end
|
16
|
+
|
17
|
+
def to_s
|
18
|
+
name
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
module Worlds
|
2
|
+
module Components
|
3
|
+
class Component
|
4
|
+
attr_reader :owner
|
5
|
+
attr_accessor :commands
|
6
|
+
|
7
|
+
def initialize(entity, commands: [])
|
8
|
+
@owner = entity
|
9
|
+
@commands = commands
|
10
|
+
end
|
11
|
+
|
12
|
+
def select_heading
|
13
|
+
"Select an option:"
|
14
|
+
end
|
15
|
+
|
16
|
+
def select_options
|
17
|
+
[]
|
18
|
+
end
|
19
|
+
|
20
|
+
def select
|
21
|
+
return nil unless select_options.any?
|
22
|
+
|
23
|
+
{
|
24
|
+
type: :select,
|
25
|
+
heading: select_heading,
|
26
|
+
options: select_options.map(&:to_s),
|
27
|
+
}
|
28
|
+
end
|
29
|
+
|
30
|
+
def invoke
|
31
|
+
end
|
32
|
+
|
33
|
+
def update(ms)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module Worlds
|
2
|
+
module Components
|
3
|
+
class Travel < Component
|
4
|
+
def select_heading
|
5
|
+
"Choose a destination:"
|
6
|
+
end
|
7
|
+
|
8
|
+
def select_options
|
9
|
+
owner.area.linked_areas
|
10
|
+
end
|
11
|
+
|
12
|
+
# @param target [Area, Integer] an Area, or the index of an Area in the
|
13
|
+
# owner's linked areas.
|
14
|
+
def invoke(target)
|
15
|
+
target = target.is_a?(Area) ? target : owner.area.linked_areas[target]
|
16
|
+
|
17
|
+
owner.area&.entities&.delete(owner)
|
18
|
+
target.entities << owner
|
19
|
+
owner.area = target
|
20
|
+
|
21
|
+
{ color: :green, content: "You are now in #{target.name}" }
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module Worlds
|
2
|
+
class Entity
|
3
|
+
attr_reader :components
|
4
|
+
attr_accessor :area
|
5
|
+
|
6
|
+
class << self
|
7
|
+
attr_accessor :player
|
8
|
+
end
|
9
|
+
|
10
|
+
def initialize
|
11
|
+
@components = []
|
12
|
+
end
|
13
|
+
|
14
|
+
def update(ms)
|
15
|
+
components.flat_map { |component|
|
16
|
+
component.update(ms)
|
17
|
+
}
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
module Worlds
|
2
|
+
module Terminal
|
3
|
+
# Utility methods for allowing output above the input line in the terminal.
|
4
|
+
class Helper
|
5
|
+
# To allow output above the input line, disables input buffering.
|
6
|
+
def self.io_mode_raw! = `stty raw`
|
7
|
+
def self.io_mode_normal! = `stty -raw`
|
8
|
+
|
9
|
+
# From https://stackoverflow.com/a/50152099
|
10
|
+
# If the cursor weren't hidden, it would appear at the beginning of the line
|
11
|
+
# due to ::io_mode_raw!
|
12
|
+
def self.hide_cursor! = print "\033[?25l"
|
13
|
+
def self.show_cursor! = print "\033[?25h"
|
14
|
+
|
15
|
+
# Reads newly inputted characters in a way that doesn't block output,
|
16
|
+
# to allow output above the input line. Based on https://stackoverflow.com/a/9900628
|
17
|
+
# @return [String] all inputted characters.
|
18
|
+
def self.read_nonblock
|
19
|
+
line = ''
|
20
|
+
|
21
|
+
while char = STDIN.read_nonblock(1, exception: false)
|
22
|
+
return line if char == :wait_readable
|
23
|
+
line << char
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
# To allow output above the input line, wraps `puts` in a change to the
|
28
|
+
# terminal mode. Also right-pads the output with spaces to prevent the input
|
29
|
+
# from "bleeding over" into output wherever an output line is shorter than a
|
30
|
+
# line being inputted.
|
31
|
+
# @parmam str [String] The string to print.
|
32
|
+
def self.puts(str)
|
33
|
+
# From https://gist.github.com/KINGSABRI/4687864
|
34
|
+
terminal_width = `tput cols`.to_i
|
35
|
+
|
36
|
+
io_mode_normal!
|
37
|
+
Kernel.puts str.ljust(terminal_width, ' ')
|
38
|
+
io_mode_raw!
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,100 @@
|
|
1
|
+
require 'pastel'
|
2
|
+
require_relative 'helper'
|
3
|
+
require_relative 'updater'
|
4
|
+
|
5
|
+
module Worlds
|
6
|
+
module Terminal
|
7
|
+
# A container for the input loop, which is needed because input is read in
|
8
|
+
# a non-blocking way, i.e. input is read while new output is displayed.
|
9
|
+
class Runner
|
10
|
+
PASTEL = Pastel.new
|
11
|
+
CURSOR = '█'
|
12
|
+
BACKSPACE = "\x7F"
|
13
|
+
CTRL_BACKSPACE = "\x17" # or Cmd+Backspace on MacOS
|
14
|
+
INTERRUPT = "\x03" # Ctrl+C
|
15
|
+
|
16
|
+
# Loops continuously, reading input and allowing Worlds to update and output.
|
17
|
+
def self.io_loop
|
18
|
+
loop do
|
19
|
+
# We need our own input buffer here because the terminal input buffer is
|
20
|
+
# disabled due to Helper::io_mode_raw!
|
21
|
+
@input_buffer ||= ''
|
22
|
+
|
23
|
+
new_input = Helper.read_nonblock
|
24
|
+
|
25
|
+
if new_input
|
26
|
+
return if new_input.include?(INTERRUPT)
|
27
|
+
|
28
|
+
# Handle Enter.
|
29
|
+
new_input_has_newline = new_input.include?("\n") || new_input.include?("\r")
|
30
|
+
new_input = new_input.split(/[\n\r]/).first if new_input_has_newline
|
31
|
+
|
32
|
+
# Add new input to buffer (or add nothing, if no new input).
|
33
|
+
@input_buffer << (new_input || '')
|
34
|
+
|
35
|
+
# Handle deletion: Ctrl + Backspace (line), or Backspace (character).
|
36
|
+
# In either case, re-print the input buffer with spaces at the end
|
37
|
+
# to cover over the deleted characters.
|
38
|
+
if @input_buffer.include?(CTRL_BACKSPACE)
|
39
|
+
print "#{CURSOR}#{' ' * @input_buffer.length}\r"
|
40
|
+
@input_buffer = ''
|
41
|
+
else
|
42
|
+
backspace_count = @input_buffer.count(BACKSPACE)
|
43
|
+
|
44
|
+
while @input_buffer.length > 0 && @input_buffer.include?(BACKSPACE)
|
45
|
+
@input_buffer.sub!(/[^#{BACKSPACE}]#{BACKSPACE}/, '')
|
46
|
+
@input_buffer = '' if @input_buffer.chars.uniq == [BACKSPACE]
|
47
|
+
end
|
48
|
+
|
49
|
+
print "#{@input_buffer}#{CURSOR}#{' ' * backspace_count}\r"
|
50
|
+
end
|
51
|
+
|
52
|
+
# Echo input. The \r is to make the line replaceable by new output,
|
53
|
+
# while the input line will re-appear below the new output; in effect,
|
54
|
+
# to allow output above the input line.
|
55
|
+
print "#{@input_buffer}#{CURSOR}\r"
|
56
|
+
end
|
57
|
+
|
58
|
+
# Empty the input buffer if Enter was pressed.
|
59
|
+
if new_input_has_newline
|
60
|
+
input_line = @input_buffer.strip
|
61
|
+
@input_buffer = ''
|
62
|
+
end
|
63
|
+
|
64
|
+
# If a line was just inputted, set up an input hash as either a number
|
65
|
+
# selection (if a selection menu was just shown) or else a new command.
|
66
|
+
if input_line
|
67
|
+
if @select_for && input_line.match?(/\A\d+\z/)
|
68
|
+
input = { type: :select, command: @select_for, selection: input_line.to_i }
|
69
|
+
@select_for = nil
|
70
|
+
else
|
71
|
+
input = { type: :command, command: input_line }
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
# Allow Worlds to loop, and print outputs if any.
|
76
|
+
if outputs = Worlds::Updater.tick(input)
|
77
|
+
outputs.each do |output|
|
78
|
+
case output[:type]
|
79
|
+
when :exit
|
80
|
+
return
|
81
|
+
when :select # selection menu
|
82
|
+
Helper.puts PASTEL.white(output[:heading])
|
83
|
+
output[:options].each.with_index do |option, i|
|
84
|
+
Helper.puts PASTEL.blue("#{i + 1}. ") + PASTEL.white(option)
|
85
|
+
end
|
86
|
+
|
87
|
+
@select_for = input_line
|
88
|
+
else # informational
|
89
|
+
Helper.puts PASTEL.send(output[:color], output[:content])
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
# Reset input, to remain empty until next time Enter is pressed.
|
95
|
+
input_line = nil && input = nil if input_line
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
@@ -0,0 +1,69 @@
|
|
1
|
+
require_relative '../special_commands'
|
2
|
+
require_relative '../world'
|
3
|
+
|
4
|
+
module Worlds
|
5
|
+
# A container for ::tick, which processes input and updates the world state.
|
6
|
+
class Updater
|
7
|
+
UPDATES_PER_SECOND = 1
|
8
|
+
|
9
|
+
# Whether the world has been started.
|
10
|
+
# @return [Boolean]
|
11
|
+
def self.started?
|
12
|
+
!!@time_start
|
13
|
+
end
|
14
|
+
|
15
|
+
# Processes input if any, or performs an update if enough time has passed.
|
16
|
+
# @param input [Hash] a line of input from the player.
|
17
|
+
# @return [Array<Hash>, nil] an array of output hashes, if input was processed
|
18
|
+
# or an update was performed.
|
19
|
+
def self.tick(input = nil)
|
20
|
+
unless started?
|
21
|
+
# On why not Time.now, see
|
22
|
+
# https://blog.dnsimple.com/2018/03/elapsed-time-with-ruby-the-right-way
|
23
|
+
@time_start = Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
24
|
+
|
25
|
+
return World.setup
|
26
|
+
end
|
27
|
+
|
28
|
+
return process_input(input) if input
|
29
|
+
|
30
|
+
time_now = Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
31
|
+
time_elapsed = time_now - @time_start
|
32
|
+
|
33
|
+
if time_elapsed >= (1.0 / UPDATES_PER_SECOND)
|
34
|
+
@time_start = Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
35
|
+
return update
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
# Processes input and returns output in response.
|
40
|
+
# @param input [Hash] a line of input from the player.
|
41
|
+
# @return [Array<Hash>, nil] an array of output hashes.
|
42
|
+
private_class_method def self.process_input(input)
|
43
|
+
outputs = []
|
44
|
+
|
45
|
+
if action = SpecialCommands::ACTIONS[input[:command].to_sym]
|
46
|
+
outputs += action.call
|
47
|
+
else
|
48
|
+
outputs += [World.input(input)].flatten.compact
|
49
|
+
end
|
50
|
+
|
51
|
+
if outputs.empty?
|
52
|
+
outputs << { color: :red, content: "Invalid command: #{input[:command]}" }
|
53
|
+
end
|
54
|
+
|
55
|
+
outputs
|
56
|
+
end
|
57
|
+
|
58
|
+
# Updates the world state and returns any resulting output.
|
59
|
+
# @return [Array<Hash>, nil] an array of output hashes.
|
60
|
+
private_class_method def self.update
|
61
|
+
outputs = []
|
62
|
+
|
63
|
+
ms = 1000 / UPDATES_PER_SECOND.round(2)
|
64
|
+
outputs += World.update(ms)
|
65
|
+
|
66
|
+
outputs
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
data/lib/worlds/world.rb
ADDED
@@ -0,0 +1,87 @@
|
|
1
|
+
require_relative 'entity'
|
2
|
+
require_relative 'components/component'
|
3
|
+
require_relative 'components/travel'
|
4
|
+
require_relative 'components/ticker'
|
5
|
+
require_relative 'area'
|
6
|
+
|
7
|
+
module Worlds
|
8
|
+
class World
|
9
|
+
attr_reader :areas
|
10
|
+
|
11
|
+
class << self
|
12
|
+
attr_reader :instance
|
13
|
+
end
|
14
|
+
|
15
|
+
def self.setup
|
16
|
+
@instance = new
|
17
|
+
@instance.demo_setup
|
18
|
+
end
|
19
|
+
|
20
|
+
def self.input(input)
|
21
|
+
instance.input(input)
|
22
|
+
end
|
23
|
+
|
24
|
+
def self.update(ms)
|
25
|
+
instance.update(ms)
|
26
|
+
end
|
27
|
+
|
28
|
+
def initialize
|
29
|
+
@areas = []
|
30
|
+
end
|
31
|
+
|
32
|
+
def input(input)
|
33
|
+
Entity.player.components.each do |component|
|
34
|
+
if component.commands.include? input[:command]
|
35
|
+
if input[:type] == :select
|
36
|
+
selection_index = input[:selection] - 1
|
37
|
+
|
38
|
+
if selection_index >= component.select_options.count
|
39
|
+
return { color: :red, content: "Invalid selection" }
|
40
|
+
end
|
41
|
+
|
42
|
+
return component.invoke(selection_index)
|
43
|
+
else
|
44
|
+
return [component.select] || [component.invoke]
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
nil
|
50
|
+
end
|
51
|
+
|
52
|
+
def update(ms)
|
53
|
+
outputs = []
|
54
|
+
|
55
|
+
areas.each { |area|
|
56
|
+
area_outputs = area.update(ms)
|
57
|
+
outputs = area_outputs.compact if area == Entity.player.area
|
58
|
+
}
|
59
|
+
|
60
|
+
return outputs
|
61
|
+
end
|
62
|
+
|
63
|
+
def demo_setup
|
64
|
+
hero = Entity.new
|
65
|
+
hero.components << Components::Travel.new(hero, commands: %w[travel t go leave])
|
66
|
+
Entity.player = hero
|
67
|
+
|
68
|
+
clock = Entity.new
|
69
|
+
clock.components << Components::Ticker.new(clock)
|
70
|
+
|
71
|
+
street = Area.new("a quiet street")
|
72
|
+
street.entities << hero
|
73
|
+
|
74
|
+
shop = Area.new("Rolf's Clock Shop")
|
75
|
+
shop.linked_areas << street
|
76
|
+
shop.entities << clock
|
77
|
+
street.linked_areas << shop
|
78
|
+
|
79
|
+
@areas << street
|
80
|
+
@areas << shop
|
81
|
+
|
82
|
+
output = Entity.player.components.find { _1.is_a? Components::Travel }.invoke(street)
|
83
|
+
|
84
|
+
[output]
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
metadata
ADDED
@@ -0,0 +1,159 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: worlds-terminal
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Felipe Vogel
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2024-03-02 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: pastel
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0.8'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0.8'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: debug
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '1.7'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '1.7'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: minitest
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '5.0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '5.0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: minitest-reporters
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - "~>"
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '1.6'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - "~>"
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '1.6'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: shoulda-context
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - "~>"
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '2.0'
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - "~>"
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '2.0'
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: pretty-diffs
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - "~>"
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '1.0'
|
90
|
+
type: :development
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - "~>"
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '1.0'
|
97
|
+
- !ruby/object:Gem::Dependency
|
98
|
+
name: rubycritic
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - "~>"
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: '4.7'
|
104
|
+
type: :development
|
105
|
+
prerelease: false
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - "~>"
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: '4.7'
|
111
|
+
description:
|
112
|
+
email:
|
113
|
+
- fps.vogel@gmail.com
|
114
|
+
executables:
|
115
|
+
- worlds
|
116
|
+
extensions: []
|
117
|
+
extra_rdoc_files: []
|
118
|
+
files:
|
119
|
+
- bin/worlds
|
120
|
+
- lib/worlds/area.rb
|
121
|
+
- lib/worlds/components/component.rb
|
122
|
+
- lib/worlds/components/ticker.rb
|
123
|
+
- lib/worlds/components/travel.rb
|
124
|
+
- lib/worlds/entity.rb
|
125
|
+
- lib/worlds/special_commands.rb
|
126
|
+
- lib/worlds/terminal/helper.rb
|
127
|
+
- lib/worlds/terminal/runner.rb
|
128
|
+
- lib/worlds/terminal/updater.rb
|
129
|
+
- lib/worlds/terminal/version.rb
|
130
|
+
- lib/worlds/world.rb
|
131
|
+
homepage: https://github.com/fpsvogel/worlds-terminal
|
132
|
+
licenses:
|
133
|
+
- MIT
|
134
|
+
metadata:
|
135
|
+
allowed_push_host: https://rubygems.org
|
136
|
+
homepage_uri: https://github.com/fpsvogel/worlds-terminal
|
137
|
+
source_code_uri: https://github.com/fpsvogel/worlds-terminal
|
138
|
+
changelog_uri: https://github.com/fpsvogel/worlds-terminal/blob/master/CHANGELOG.md
|
139
|
+
post_install_message:
|
140
|
+
rdoc_options: []
|
141
|
+
require_paths:
|
142
|
+
- lib
|
143
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
144
|
+
requirements:
|
145
|
+
- - ">="
|
146
|
+
- !ruby/object:Gem::Version
|
147
|
+
version: 3.0.0
|
148
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
149
|
+
requirements:
|
150
|
+
- - ">="
|
151
|
+
- !ruby/object:Gem::Version
|
152
|
+
version: '0'
|
153
|
+
requirements: []
|
154
|
+
rubygems_version: 3.5.6
|
155
|
+
signing_key:
|
156
|
+
specification_version: 4
|
157
|
+
summary: A command-line interface for Worlds, a text-based world simulation and role-playing
|
158
|
+
game toolkit.
|
159
|
+
test_files: []
|