adjective-rpg-engine 0.0.2 → 0.0.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/adjective/concerns/imbibable.rb +69 -0
- data/lib/adjective/concerns/statusable.rb +79 -0
- data/lib/adjective/concerns/storable.rb +134 -0
- data/lib/adjective/concerns/temporable.rb +94 -0
- data/lib/adjective/concerns/vulnerable.rb +41 -0
- data/lib/adjective/item.rb +0 -0
- data/lib/adjective/status.rb +137 -0
- data/lib/adjective/table.rb +73 -0
- metadata +9 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 1e6db5b958ab81f3548404fa67db1822c9ca0c4a086e7eaf535dc0f917bc2ab7
|
4
|
+
data.tar.gz: 1e5174dd569949d858a020f4a1be2524b78d5c9800a59617f1b1f3bb67f381f7
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: c15b279c70eac977095b596167e8905b851c5eec1d9bcaefc38fe11bafd398f26f9ab33ca8d987322d860e0d1728b6702be92607ce4280ef4661e539be7858f1
|
7
|
+
data.tar.gz: 15fb8216f0531aae3fad0e958975d79f3c5acee7102c846fcf13a09781fd970ae11bb26391ce0323a9c69bd6944245aee9706aba76ae86d8c1faa26ac41042b9
|
@@ -0,0 +1,69 @@
|
|
1
|
+
module Adjective
|
2
|
+
|
3
|
+
module Imbibable
|
4
|
+
# Able to be enhanced by experience
|
5
|
+
|
6
|
+
def initialize_experience(opts)
|
7
|
+
@level = opts[:level] ||= 1
|
8
|
+
@experience = opts[:initial_exp] ||= 0
|
9
|
+
# This may throw an error if the user decides to instantiate a class without a table present.
|
10
|
+
@active_exp_set = opts[:exp_table]
|
11
|
+
[:active_exp_set, :level].each {|attribute| self.class.send(:attr_reader, attribute)}
|
12
|
+
self.class.send(:attr_accessor, :experience)
|
13
|
+
set_experience_to_level_minimum
|
14
|
+
end
|
15
|
+
|
16
|
+
def set_experience_to_level_minimum
|
17
|
+
@experience = @active_exp_set[@level]
|
18
|
+
end
|
19
|
+
|
20
|
+
def level_up
|
21
|
+
until !can_level_up?
|
22
|
+
@level += 1
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
# Level down functionality should work the same way as simply setting a level.
|
27
|
+
|
28
|
+
def normalize_experience
|
29
|
+
@experience = 0 if @experience < 0
|
30
|
+
end
|
31
|
+
|
32
|
+
def max_level
|
33
|
+
@active_exp_set.length - 1
|
34
|
+
end
|
35
|
+
|
36
|
+
def max_level?
|
37
|
+
@active_exp_set.length - 1 <= @level
|
38
|
+
end
|
39
|
+
|
40
|
+
def can_level_up?
|
41
|
+
return false if max_level?
|
42
|
+
@experience >= @active_exp_set[@level+1]
|
43
|
+
end
|
44
|
+
|
45
|
+
def grant_experience(exp, opts = {})
|
46
|
+
return false if max_level?
|
47
|
+
@experience += exp
|
48
|
+
level_up if !opts[:suppress_level_up]
|
49
|
+
end
|
50
|
+
|
51
|
+
def set_level(num, opts = {})
|
52
|
+
@level = num
|
53
|
+
@experience = @active_exp_set[num] if !opts[:constrain_exp]
|
54
|
+
end
|
55
|
+
|
56
|
+
def grant_levels(num, opts = {})
|
57
|
+
@level += num
|
58
|
+
@experience = @active_exp_set[@level] if !opts[:constrain_exp]
|
59
|
+
end
|
60
|
+
|
61
|
+
def experience_to_next_level
|
62
|
+
return nil if max_level?
|
63
|
+
return @active_exp_set[@level+1] - @experience
|
64
|
+
end
|
65
|
+
|
66
|
+
|
67
|
+
end
|
68
|
+
|
69
|
+
end
|
@@ -0,0 +1,79 @@
|
|
1
|
+
module Adjective
|
2
|
+
|
3
|
+
# I plan on having this module handle the messier side of buff/debuff processing
|
4
|
+
# It will need to include a way to manage the coupling between Status and the target model.
|
5
|
+
# It will need to be able to process and return values that can be easily passed into other
|
6
|
+
# methods.
|
7
|
+
|
8
|
+
module Statusable
|
9
|
+
|
10
|
+
def initialize_status_data
|
11
|
+
@statuses = []
|
12
|
+
self.class.send(:attr_accessor, :statuses)
|
13
|
+
end
|
14
|
+
|
15
|
+
def apply_status(status, &block)
|
16
|
+
validate_modifier_existence(status)
|
17
|
+
validate_initial_attributes(status.affected_attributes)
|
18
|
+
yield(self, status) if block_given?
|
19
|
+
@statuses.push(status)
|
20
|
+
return @statuses
|
21
|
+
end
|
22
|
+
|
23
|
+
# Actually has three cases
|
24
|
+
# 1: has and responds to given method PERIOD
|
25
|
+
def has_status?(attribute, match)
|
26
|
+
@statuses.each do |status|
|
27
|
+
if status.respond_to?(attribute)
|
28
|
+
return true if status.send(attribute) == match
|
29
|
+
end
|
30
|
+
end
|
31
|
+
return false
|
32
|
+
end
|
33
|
+
|
34
|
+
def tick_all(&block)
|
35
|
+
# Provides baseline functionality to + or - values from a given status, as this is
|
36
|
+
# the most common of implementations.
|
37
|
+
# Strings and other values are simply set.
|
38
|
+
# Yielding to an arbitrary block should curcumvent any issues with extension and post-effect hooks.
|
39
|
+
@statuses.each do |status|
|
40
|
+
validate_modifier_existence(status)
|
41
|
+
status.modifiers.each do |key, value|
|
42
|
+
attribute = key.to_s
|
43
|
+
if (value.is_a?(Integer) || value.is_a?(Float))
|
44
|
+
eval("self.#{attribute} += #{value}") if self.respond_to?(attribute+"=")
|
45
|
+
else
|
46
|
+
send("#{attribute}=", value) if self.respond_to?(attribute+"=")
|
47
|
+
end
|
48
|
+
end
|
49
|
+
status.tick
|
50
|
+
end
|
51
|
+
yield(self, @statuses) if block_given?
|
52
|
+
return @statuses
|
53
|
+
end
|
54
|
+
|
55
|
+
def clear_expired_statuses
|
56
|
+
unexpired = @statuses.select { |status| status.duration != 0 }
|
57
|
+
diff = @statuses - unexpired
|
58
|
+
@statuses = unexpired
|
59
|
+
return diff
|
60
|
+
end
|
61
|
+
|
62
|
+
private
|
63
|
+
|
64
|
+
def validate_initial_attributes(affected_attributes)
|
65
|
+
invalid = affected_attributes.select {|att| !instance_variable_defined?(att) }
|
66
|
+
warn_about_attributes if invalid.length > 0
|
67
|
+
end
|
68
|
+
|
69
|
+
def validate_modifier_existence(status)
|
70
|
+
raise RuntimeError, "Given status does not respond to #modifiers." if !status.respond_to?(:modifiers)
|
71
|
+
end
|
72
|
+
|
73
|
+
def warn_about_attributes
|
74
|
+
warn("[#{Time.now}]: Gave affected_attributes in a Status that are not present on #{self.class.name}: #{invalids}.")
|
75
|
+
end
|
76
|
+
|
77
|
+
end
|
78
|
+
|
79
|
+
end
|
@@ -0,0 +1,134 @@
|
|
1
|
+
module Adjective
|
2
|
+
|
3
|
+
module Storable
|
4
|
+
|
5
|
+
def initialize_storage_data(items = [], opts = {})
|
6
|
+
@items = items
|
7
|
+
@initialized_at = Time.now
|
8
|
+
@max_size = opts[:max_size] ||= :unlimited
|
9
|
+
@default_sort_method = opts[:default_sort_method] ||= :object_id
|
10
|
+
validate_inventory_capacity
|
11
|
+
[:items, :initialized_at, :max_size, :default_sort_method].each {|attribute| self.class.send(:attr_accessor, attribute) }
|
12
|
+
end
|
13
|
+
|
14
|
+
# Utility Methods
|
15
|
+
def empty?
|
16
|
+
@items.length === 0
|
17
|
+
end
|
18
|
+
|
19
|
+
def full?
|
20
|
+
@max_size == :unlimited ? false : @items.length < @max_size
|
21
|
+
end
|
22
|
+
|
23
|
+
def can_store?(items)
|
24
|
+
return true if @max_size == :unlimited
|
25
|
+
return true if (Array(items).length + @items.length) <= @max_size
|
26
|
+
return false
|
27
|
+
end
|
28
|
+
|
29
|
+
def empty_slots
|
30
|
+
@max_size == :unlimited ? :unlimited : @max_size - @items.length
|
31
|
+
end
|
32
|
+
|
33
|
+
# Simple Search
|
34
|
+
# options for scope are: :all, :attributes, :values
|
35
|
+
def query(term, scope = :all)
|
36
|
+
validate_query_scope(scope)
|
37
|
+
matches = []
|
38
|
+
@items.each do |item|
|
39
|
+
chunks = []
|
40
|
+
attributes = item.instance_variables.map {|ivar| ivar.to_s.gsub("@", "").to_sym}
|
41
|
+
attributes.each do |attribute|
|
42
|
+
chunks.push(construct_query_data(attribute.to_s, item.send(attribute).to_s)[scope])
|
43
|
+
end
|
44
|
+
matches << item if chunks.join.include?(term)
|
45
|
+
end
|
46
|
+
return matches
|
47
|
+
end
|
48
|
+
|
49
|
+
# Store - Put
|
50
|
+
def store(items)
|
51
|
+
if can_store?(items)
|
52
|
+
Array(items).each do |item|
|
53
|
+
@items << item
|
54
|
+
end
|
55
|
+
return items
|
56
|
+
else
|
57
|
+
return false
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
# retrieve_by - Get
|
62
|
+
def retrieve_by(attribute, value)
|
63
|
+
@items.select do |item|
|
64
|
+
item if item.respond_to?(attribute) && item.send(attribute) === value
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
# Dump - Delete all
|
69
|
+
def dump
|
70
|
+
outbound_items = @items
|
71
|
+
@items = []
|
72
|
+
return outbound_items
|
73
|
+
end
|
74
|
+
|
75
|
+
# Dump selection - Selective delete
|
76
|
+
def dump_by(attribute, value)
|
77
|
+
outbound_items = retrieve_by(attribute, value)
|
78
|
+
@items = @items.select {|item| !outbound_items.include?(item) }
|
79
|
+
return outbound_items
|
80
|
+
end
|
81
|
+
|
82
|
+
# Sorting
|
83
|
+
def sort
|
84
|
+
@items.sort_by { |item| item.send(@default_sort_method) }
|
85
|
+
return @items
|
86
|
+
end
|
87
|
+
|
88
|
+
def sort!
|
89
|
+
@items = @items.sort_by { |item| item.send(@default_sort_method) }
|
90
|
+
return @items
|
91
|
+
end
|
92
|
+
|
93
|
+
def sort_by(attribute, order = :asc)
|
94
|
+
sorted = @items.sort_by(&attribute)
|
95
|
+
return order == :asc ? sorted : sorted.reverse
|
96
|
+
end
|
97
|
+
|
98
|
+
def sort_by!(attribute, order = :asc)
|
99
|
+
sorted = @items.sort_by(&attribute)
|
100
|
+
return order == :asc ? @items = sorted : @items = sorted.reverse
|
101
|
+
end
|
102
|
+
|
103
|
+
alias_method :deposit, :store
|
104
|
+
alias_method :put, :store
|
105
|
+
alias_method :get_by, :retrieve_by
|
106
|
+
alias_method :find_by, :retrieve_by
|
107
|
+
alias_method :clear, :dump
|
108
|
+
alias_method :clear_by, :dump_by
|
109
|
+
alias_method :search, :query
|
110
|
+
|
111
|
+
private
|
112
|
+
|
113
|
+
def validate_query_scope(scope)
|
114
|
+
raise ArgumentError, "[#{Time.now}]: Please provide :full, :attributes, or :values to the scope parameter: given #{scope}" if ![:all, :attributes, :values].include?(scope)
|
115
|
+
end
|
116
|
+
|
117
|
+
def construct_query_data(attribute, val)
|
118
|
+
# Delimiting with &: to avoid issues with intermingled data
|
119
|
+
return {
|
120
|
+
all: attribute + "&:" + val + "&:",
|
121
|
+
attributes: attribute + "&:",
|
122
|
+
values: val + "&:"
|
123
|
+
}
|
124
|
+
end
|
125
|
+
|
126
|
+
def validate_inventory_capacity
|
127
|
+
return true if @max_size == :unlimited
|
128
|
+
total_length = @items.length
|
129
|
+
raise ArgumentError, "#{Time.now}]: items argument length larger than max size: max_size: #{@max_size}, items_length: #{total_length}" if total_length > @max_size
|
130
|
+
end
|
131
|
+
|
132
|
+
end
|
133
|
+
|
134
|
+
end
|
@@ -0,0 +1,94 @@
|
|
1
|
+
module Adjective
|
2
|
+
|
3
|
+
module Temporable
|
4
|
+
|
5
|
+
# This is to be used to emulate turn-based interactions or to emulate basic time within the context of a parent class.
|
6
|
+
|
7
|
+
# The idea behind setting an initial_duration is so you can see what the subject started out as
|
8
|
+
# instead of making an assumption. This could matter if multiple same-identity classes have the same attributes,
|
9
|
+
# but have different durations linked to them.
|
10
|
+
|
11
|
+
# Initialize module data for Temporable
|
12
|
+
# @param opts [Hash]
|
13
|
+
# @return [Object]
|
14
|
+
# @example
|
15
|
+
# initialize_temporality({max_duration: 5, remaining_duration: 4})
|
16
|
+
def initialize_temporality(opts={})
|
17
|
+
@max_duration = opts[:max_duration] ||= 1
|
18
|
+
@remaining_duration = opts[:remaining_duration] ||= @max_duration
|
19
|
+
|
20
|
+
throw_duration_theshold_error if invalid_durations?
|
21
|
+
|
22
|
+
[:max_duration, :remaining_duration].each do |attribute|
|
23
|
+
self.class.send(:attr_accessor, attribute)
|
24
|
+
end
|
25
|
+
normalize_remaining_duration
|
26
|
+
return self
|
27
|
+
end
|
28
|
+
|
29
|
+
# Checks if the status is at it's maximum duration
|
30
|
+
# @return [Boolean]
|
31
|
+
# @example
|
32
|
+
# SurrogateClass.max_duration? #=> True/False
|
33
|
+
def max_duration?
|
34
|
+
@max_duration == @remaining_duration
|
35
|
+
end
|
36
|
+
|
37
|
+
# Checks if remaining_duration is at 0.
|
38
|
+
# @return [Boolean]
|
39
|
+
# @example
|
40
|
+
# SurrogateClass.expired? #=> True/False
|
41
|
+
def expired?
|
42
|
+
@remaining_duration == 0
|
43
|
+
end
|
44
|
+
|
45
|
+
# Checks if remaining_duration is at 1.
|
46
|
+
# @return [Boolean]
|
47
|
+
# @example
|
48
|
+
# SurrogateClass.expired? #=> True/False
|
49
|
+
def expiring?
|
50
|
+
# This method seems like a meme, but I think it makes sense
|
51
|
+
@remaining_duration == 1
|
52
|
+
end
|
53
|
+
|
54
|
+
# Checks and sets remaining_duration if it is out of bounds.
|
55
|
+
# @return [Integer]
|
56
|
+
# @example
|
57
|
+
# SurrogateClass.normalize_remaining_duration
|
58
|
+
def normalize_remaining_duration
|
59
|
+
@remaining_duration = @max_duration if @remaining_duration > @max_duration
|
60
|
+
@remaining_duration = 0 if @remaining_duration < 0
|
61
|
+
end
|
62
|
+
|
63
|
+
# Extends the duration and keeps it within bounds.
|
64
|
+
# @param extension [Integer]
|
65
|
+
# @return [Integer]
|
66
|
+
# @example
|
67
|
+
# SurrogateClass.extend_by(2)
|
68
|
+
def extend_by(extension)
|
69
|
+
@remaining_duration += extension
|
70
|
+
normalize_remaining_duration
|
71
|
+
return @remaining_duration
|
72
|
+
end
|
73
|
+
|
74
|
+
private
|
75
|
+
|
76
|
+
# Triggers error to the thrown is thesholds are exceeed on initialization.
|
77
|
+
# @return [Boolean]
|
78
|
+
# @private
|
79
|
+
# @example
|
80
|
+
# SurrogateClass.throw_duration_threshold_error
|
81
|
+
def throw_duration_theshold_error
|
82
|
+
raise ArgumentError, "Provded initial_duration or remaining_duration values exceed max_duration: max: #{@max_duration}, remaining: #{@remaining_duration}."
|
83
|
+
end
|
84
|
+
|
85
|
+
# Checks if initial durations are valid.
|
86
|
+
# @return [Boolean]
|
87
|
+
# @example
|
88
|
+
# SurrogateClass.invalid_durations?
|
89
|
+
def invalid_durations?
|
90
|
+
@remaining_duration > @max_duration
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
module Adjective
|
2
|
+
|
3
|
+
module Vulnerable
|
4
|
+
def initialize_vulnerability(hitpoints = 1, max_hitpoints = 10)
|
5
|
+
@hitpoints = hitpoints
|
6
|
+
@max_hitpoints = max_hitpoints
|
7
|
+
self.class.send(:attr_accessor, :hitpoints)
|
8
|
+
self.class.send(:attr_accessor, :max_hitpoints)
|
9
|
+
end
|
10
|
+
|
11
|
+
def take_damage(damage)
|
12
|
+
@hitpoints -= damage
|
13
|
+
normalize_hitpoints
|
14
|
+
return self
|
15
|
+
end
|
16
|
+
|
17
|
+
def heal_to_full
|
18
|
+
@hitpoints = @max_hitpoints
|
19
|
+
normalize_hitpoints
|
20
|
+
end
|
21
|
+
|
22
|
+
def heal_for(healing)
|
23
|
+
@hitpoints += healing
|
24
|
+
normalize_hitpoints
|
25
|
+
end
|
26
|
+
|
27
|
+
def alive?
|
28
|
+
@hitpoints > 0
|
29
|
+
end
|
30
|
+
|
31
|
+
def dead?
|
32
|
+
@hitpoints == 0
|
33
|
+
end
|
34
|
+
|
35
|
+
def normalize_hitpoints
|
36
|
+
@hitpoints = 0 if @hitpoints < 0
|
37
|
+
@hitpoints = @max_hitpoints if @hitpoints > @max_hitpoints
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
end
|
File without changes
|
@@ -0,0 +1,137 @@
|
|
1
|
+
module Adjective
|
2
|
+
|
3
|
+
# Status is different from something like an attack in that it applies
|
4
|
+
# to things that afflict the subject for one or more turns.
|
5
|
+
module Status
|
6
|
+
|
7
|
+
include Adjective::Temporable
|
8
|
+
|
9
|
+
# Initialize module data for Status
|
10
|
+
# @param opts [Hash]
|
11
|
+
# @return [Object]
|
12
|
+
# @example
|
13
|
+
# class SurrogateClass
|
14
|
+
# include Adjective::Status
|
15
|
+
# initialize_status({affected_attributes: { hitpoints: 3}, max_duration: 5})
|
16
|
+
# end
|
17
|
+
def initialize_status(opts = {})
|
18
|
+
attributes = opts[:affected_attributes]
|
19
|
+
@modifiers = attributes ||= {}
|
20
|
+
@affected_attributes = attributes.map{|entry| entry[0]}
|
21
|
+
|
22
|
+
# @applied_at Can be used to track simple object intantation if class is created when status is applied.
|
23
|
+
# TODO: If held in memory, opts will need to be given a :timestamp with a value comparable with a Time object. (Custom values should help?)
|
24
|
+
# If the user wishes to sort by a specific attribute in Statusable, then they should pass a block and do so there. (Maybe?)
|
25
|
+
@applied_at = opts[:timestamp] ||= Time.now
|
26
|
+
|
27
|
+
[:initialized_at, :affected_attributes, :modifiers].each do |attribute|
|
28
|
+
self.class.send(:attr_reader, attribute)
|
29
|
+
end
|
30
|
+
|
31
|
+
initialize_temporality(opts)
|
32
|
+
normalize_remaining_duration
|
33
|
+
assign_affected_attributes
|
34
|
+
return self
|
35
|
+
end
|
36
|
+
|
37
|
+
# Will perform tick functionality, whose default action is to reduce @remaining_duration (from Temporable) by 1.
|
38
|
+
# Otherwise, it will accept a block and bypass all default functionality.
|
39
|
+
# @param block [Block]
|
40
|
+
# @return [Object]
|
41
|
+
# @example
|
42
|
+
# SurrogateClass.tick
|
43
|
+
def tick(&block)
|
44
|
+
if block_given?
|
45
|
+
yield(self)
|
46
|
+
else
|
47
|
+
# Default
|
48
|
+
@remaining_duration -= 1
|
49
|
+
end
|
50
|
+
return self
|
51
|
+
end
|
52
|
+
|
53
|
+
# Checks if the status has a modifier present
|
54
|
+
# @return [Boolean]
|
55
|
+
# @example
|
56
|
+
# SurrogateClass.has_modifier?(:hitpoints)
|
57
|
+
def has_modifier?(attribute)
|
58
|
+
@modifiers.key?(attribute)
|
59
|
+
end
|
60
|
+
|
61
|
+
# Adds or updates the modifier hash.
|
62
|
+
# @param attribute [Symbol]
|
63
|
+
# @param value [Integer, Float, String]
|
64
|
+
# @return [Hash]
|
65
|
+
# @example
|
66
|
+
# SurrogateClass.add_or_update_modifer(:hitpoints, 10)
|
67
|
+
def add_or_update_modifier(attribute, value)
|
68
|
+
if has_modifier?(attribute)
|
69
|
+
@modifiers[attribute] = value
|
70
|
+
else
|
71
|
+
@modifiers.store(attribute, value)
|
72
|
+
end
|
73
|
+
assign_affected_attributes
|
74
|
+
return @modifiers
|
75
|
+
end
|
76
|
+
|
77
|
+
# Updates the modifier in @modifiers. Will warn and NOT amend if modifier does not exist.
|
78
|
+
# @param attribute [Symbol]
|
79
|
+
# @param value [Integer, Float, String]
|
80
|
+
# @return [Hash]
|
81
|
+
# @example
|
82
|
+
# SurrogateClass.update_modifier(:hitpoints, 12)
|
83
|
+
def update_modifier(attribute, value)
|
84
|
+
if has_modifier?(attribute)
|
85
|
+
@modifiers[attribute] = value
|
86
|
+
assign_affected_attributes
|
87
|
+
else
|
88
|
+
warn("[#{Time.now}]: Attempted to update a modifier that wasn't present: #{attribute}. Use #add_modifier or #add_or_update_modifier instead.")
|
89
|
+
end
|
90
|
+
return @modifiers
|
91
|
+
end
|
92
|
+
|
93
|
+
# Adds to the modifier to @modifiers. Will warn and NOT amend if modifier already exists.
|
94
|
+
# @param attribute [Symbol]
|
95
|
+
# @param value [Integer, Float, String]
|
96
|
+
# @return [Hash]
|
97
|
+
# @example
|
98
|
+
# SurrogateClass.add_modifer(:strength, 20)
|
99
|
+
def add_modifier(attribute, value)
|
100
|
+
if !has_modifier?(attribute)
|
101
|
+
@modifiers.store(attribute, value)
|
102
|
+
assign_affected_attributes
|
103
|
+
else
|
104
|
+
warn("[#{Time.now}]: Attempted to add duplicate modifier: #{attribute}. The new value has NOT been set. (Currently '#{@modifiers[attribute]}'.")
|
105
|
+
end
|
106
|
+
return @modifiers
|
107
|
+
end
|
108
|
+
|
109
|
+
# Removes the specified modifier from @modifers.
|
110
|
+
# @param attribute [Symbol]
|
111
|
+
# @param value [Integer, Float, String]
|
112
|
+
# @return [Hash]
|
113
|
+
# @example
|
114
|
+
# SurrogateClass.add_modifer(:strength, 20)
|
115
|
+
def remove_modifier(attribute)
|
116
|
+
if has_modifier?(attribute)
|
117
|
+
temp = {}.store(attribute, modifiers[attribute])
|
118
|
+
@modifiers.delete(attribute)
|
119
|
+
else
|
120
|
+
warn("[#{Time.now}]: Attempted to remove modifier that does not exist: #{attribute}")
|
121
|
+
end
|
122
|
+
return temp
|
123
|
+
end
|
124
|
+
|
125
|
+
private
|
126
|
+
|
127
|
+
# Converts the modifier hash into a digestable array for other modules to use. Includes '@' in modifier value,
|
128
|
+
# as the format for the metaprogramming in other modules requires the attribute to be called and set explicitly if not publicly writable.
|
129
|
+
# This should most likely be left alone and not called outside of the implementation here.
|
130
|
+
# @private
|
131
|
+
# @return [Hash]
|
132
|
+
def assign_affected_attributes
|
133
|
+
@affected_attributes.map!{|attribute| ("@"+attribute.to_s).to_sym }
|
134
|
+
end
|
135
|
+
|
136
|
+
end
|
137
|
+
end
|
@@ -0,0 +1,73 @@
|
|
1
|
+
require 'yaml'
|
2
|
+
|
3
|
+
module Adjective
|
4
|
+
class Table
|
5
|
+
attr_accessor :name
|
6
|
+
attr_reader :data
|
7
|
+
|
8
|
+
def initialize(dir, name = nil)
|
9
|
+
file_existence_catch(dir)
|
10
|
+
@name = name
|
11
|
+
@data = YAML.load_file(dir)
|
12
|
+
@_created_at = Time.now
|
13
|
+
end
|
14
|
+
|
15
|
+
def load(dir)
|
16
|
+
file_existence_catch(dir)
|
17
|
+
@data = YAML.load_file(dir)
|
18
|
+
end
|
19
|
+
|
20
|
+
def set_exists?(name)
|
21
|
+
@data.key?(name)
|
22
|
+
end
|
23
|
+
|
24
|
+
private
|
25
|
+
|
26
|
+
def file_existence_catch(dir)
|
27
|
+
raise RuntimeError, "#{Time.now}]: Invalid path to YAML file: #{dir}" if !File.exist?(dir)
|
28
|
+
end
|
29
|
+
|
30
|
+
end
|
31
|
+
|
32
|
+
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
|
33
|
+
# Will separate out when I get the dir structures set up properly.
|
34
|
+
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
|
35
|
+
|
36
|
+
class Table::Experience < Table
|
37
|
+
# This class sets a 'by index' paradigm and expects an ordered set
|
38
|
+
# to be present after being given the parsed YAML file data.
|
39
|
+
|
40
|
+
# This is primarily to keep the access points standardized and would only require
|
41
|
+
# someone consuming the library to call something like level.to_i to maintain the convention.
|
42
|
+
|
43
|
+
attr_reader :thresholds
|
44
|
+
|
45
|
+
def initialize(dir, name = nil)
|
46
|
+
# raise ArgumentError
|
47
|
+
super(dir, name)
|
48
|
+
@thresholds = @data[@name]
|
49
|
+
|
50
|
+
if !@thresholds.is_a?(Array)
|
51
|
+
raise RuntimeError, "#{Time.now}]: Experience table '#{@name}' is not an Array: #{@exp_thresholds.class}"
|
52
|
+
elsif threshold_sorted?
|
53
|
+
raise RuntimeError, "#{Time.now}]: Experience table '#{@name}' is not sequential: #{@exp_thresholds}"
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
def at_level(level)
|
58
|
+
# Convenience methods to translate string cases might be worth it... but the
|
59
|
+
# general convention is that you pass through whole integers to grab data that is
|
60
|
+
# more reliable within the structure of the code itself. Going to just keep to
|
61
|
+
# convention for the moment.
|
62
|
+
raise RuntimeError, "#{Time.now}]: Level provided is not an Integer: #{level}" if !level.is_a?(Integer)
|
63
|
+
return @thresholds[level]
|
64
|
+
end
|
65
|
+
|
66
|
+
private
|
67
|
+
|
68
|
+
def threshold_sorted?
|
69
|
+
@thresholds != @thresholds.sort
|
70
|
+
end
|
71
|
+
|
72
|
+
end
|
73
|
+
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: adjective-rpg-engine
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Kevin Maze
|
@@ -45,6 +45,14 @@ extensions: []
|
|
45
45
|
extra_rdoc_files: []
|
46
46
|
files:
|
47
47
|
- lib/adjective.rb
|
48
|
+
- lib/adjective/concerns/imbibable.rb
|
49
|
+
- lib/adjective/concerns/statusable.rb
|
50
|
+
- lib/adjective/concerns/storable.rb
|
51
|
+
- lib/adjective/concerns/temporable.rb
|
52
|
+
- lib/adjective/concerns/vulnerable.rb
|
53
|
+
- lib/adjective/item.rb
|
54
|
+
- lib/adjective/status.rb
|
55
|
+
- lib/adjective/table.rb
|
48
56
|
homepage: http://rubygems.org/gems/adjective-rpg-engine
|
49
57
|
licenses:
|
50
58
|
- MIT
|