adjective-rpg-engine 0.0.2 → 0.0.3
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 +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
|