rpg_level 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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 8d6393c8eb999df0a66181c29c1f452a5e7f2ca4
4
+ data.tar.gz: e295f159f00c5aa3511cc883df91693604eacee7
5
+ SHA512:
6
+ metadata.gz: a5be1a3636a6ab3f719337873b4a6c96fe90af51aaa86cf98392f617702a9959615f4115fd04bb7f5e5973006c57f8b0906cd5e971000eb016fb3cd9d96088f9
7
+ data.tar.gz: 00dbb2cdb25ce691d49a82578a613004a505ebc2d4e39bb2fc36591aff51a3ec4190d4a981366629199f28f09869444f4e38ed3e7b6cc9de83d60dc901b6747f
data/.gitignore ADDED
@@ -0,0 +1,10 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
10
+ /vendor/
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --format documentation
2
+ --color
data/.travis.yml ADDED
@@ -0,0 +1,6 @@
1
+ sudo: false
2
+ language: ruby
3
+ rvm:
4
+ - 2.3.1
5
+ - 2.2
6
+ before_install: gem install bundler -v 1.12.5
@@ -0,0 +1,49 @@
1
+ # Contributor Code of Conduct
2
+
3
+ As contributors and maintainers of this project, and in the interest of
4
+ fostering an open and welcoming community, we pledge to respect all people who
5
+ contribute through reporting issues, posting feature requests, updating
6
+ documentation, submitting pull requests or patches, and other activities.
7
+
8
+ We are committed to making participation in this project a harassment-free
9
+ experience for everyone, regardless of level of experience, gender, gender
10
+ identity and expression, sexual orientation, disability, personal appearance,
11
+ body size, race, ethnicity, age, religion, or nationality.
12
+
13
+ Examples of unacceptable behavior by participants include:
14
+
15
+ * The use of sexualized language or imagery
16
+ * Personal attacks
17
+ * Trolling or insulting/derogatory comments
18
+ * Public or private harassment
19
+ * Publishing other's private information, such as physical or electronic
20
+ addresses, without explicit permission
21
+ * Other unethical or unprofessional conduct
22
+
23
+ Project maintainers have the right and responsibility to remove, edit, or
24
+ reject comments, commits, code, wiki edits, issues, and other contributions
25
+ that are not aligned to this Code of Conduct, or to ban temporarily or
26
+ permanently any contributor for other behaviors that they deem inappropriate,
27
+ threatening, offensive, or harmful.
28
+
29
+ By adopting this Code of Conduct, project maintainers commit themselves to
30
+ fairly and consistently applying these principles to every aspect of managing
31
+ this project. Project maintainers who do not follow or enforce the Code of
32
+ Conduct may be permanently removed from the project team.
33
+
34
+ This code of conduct applies both within project spaces and in public spaces
35
+ when an individual is representing the project or its community.
36
+
37
+ Instances of abusive, harassing, or otherwise unacceptable behavior may be
38
+ reported by contacting a project maintainer at kjirou.web@gmail.com. All
39
+ complaints will be reviewed and investigated and will result in a response that
40
+ is deemed necessary and appropriate to the circumstances. Maintainers are
41
+ obligated to maintain confidentiality with regard to the reporter of an
42
+ incident.
43
+
44
+ This Code of Conduct is adapted from the [Contributor Covenant][homepage],
45
+ version 1.3.0, available at
46
+ [http://contributor-covenant.org/version/1/3/0/][version]
47
+
48
+ [homepage]: http://contributor-covenant.org
49
+ [version]: http://contributor-covenant.org/version/1/3/0/
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in rpg_level.gemspec
4
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2016 kjirou
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,63 @@
1
+ # RpgLevel
2
+
3
+ [![Build Status](https://travis-ci.org/kjirou/ruby-rpg_level.svg?branch=master)](https://travis-ci.org/kjirou/ruby-rpg_level)
4
+
5
+ Manage the [Level/EXP](http://gaming.wikia.com/wiki/Level_(RPG))
6
+ that is known as the most commonly growth system of role-playing games.
7
+
8
+
9
+ ## Feature
10
+
11
+ The feature of this module has been aggregated into the following formula:
12
+
13
+ ```ruby
14
+ level = formula_of_exp_table(exp)
15
+ ```
16
+
17
+ This design has been often used in the classic RPGs, such as "Wizardry", "D&D", "Rouge-like", ..etc.
18
+
19
+
20
+ ## Installation
21
+
22
+ Add this line to your application's Gemfile:
23
+
24
+ ```ruby
25
+ gem 'rpg_level'
26
+ ```
27
+
28
+ And then execute:
29
+
30
+ $ bundle
31
+
32
+ Or install it yourself as:
33
+
34
+ $ gem install rpg_level
35
+
36
+
37
+ ## Example
38
+
39
+ ```ruby
40
+ require 'rpg_level'
41
+
42
+ rpg_level = RpgLevel.new
43
+
44
+ # Define EXP Table by formula
45
+ #
46
+ # Lv1 = 0
47
+ # Lv2 = 4
48
+ # Lv3 = 6 (Total = 10)
49
+ # Lv4 = 8 (Total = 18)
50
+ # Lv5 = 10 (Total = 28)
51
+ #
52
+ rpg_level.define_exp_table(5) {|data| data[:level] * 2}
53
+
54
+ # Obtain 12 experience points
55
+ # It also means that leveling up at the same time
56
+ rpg_level.obtain_exp(12)
57
+
58
+ # Get current level
59
+ p(rpg_level.level) # => 3
60
+
61
+ # Get more information
62
+ p(rpg_level.level_status) # => {:level=>3, :next_necessary_exp=>8, :lacking_exp_for_next=>6, :obtained_exp_for_next=>2}
63
+ ```
data/Rakefile ADDED
@@ -0,0 +1,6 @@
1
+ require "bundler/gem_tasks"
2
+ require "rspec/core/rake_task"
3
+
4
+ RSpec::Core::RakeTask.new(:spec)
5
+
6
+ task :default => :spec
data/bin/console ADDED
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "rpg_level"
5
+
6
+ # You can add fixtures and/or initialization code here to make experimenting
7
+ # with your gem easier. You can also use a different console, if you like.
8
+
9
+ # (If you use this, don't forget to add pry to your Gemfile!)
10
+ # require "pry"
11
+ # Pry.start
12
+
13
+ require "irb"
14
+ IRB.start
data/bin/setup ADDED
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
data/lib/rpg_level.rb ADDED
@@ -0,0 +1,239 @@
1
+ require 'rpg_level/version'
2
+
3
+ class RpgLevel
4
+ CLEARED_CACHED_CURRENT_LEVEL_STATUS = nil
5
+
6
+ attr_reader(:exp, :min_level, :necessary_exps)
7
+
8
+ def initialize(min_level: 1)
9
+ @exp = 0
10
+ @min_level = min_level
11
+ # Necessary exps from the @min_level
12
+ @necessary_exps = []
13
+ # A cache of the #generate_status_of_current_level calculation
14
+ # It is too heavy for access to #level_status like a static value
15
+ @cached_current_level_status = CLEARED_CACHED_CURRENT_LEVEL_STATUS
16
+ end
17
+
18
+ def define_exp_table_from_array(necessary_exps)
19
+ necessary_exps.each do |v|
20
+ raise ArgumentError.new('some of necessary_exps are not a integer') unless v.is_a?(Integer)
21
+ raise ArgumentError.new('some of necessary_exps are less than 0') if v < 0
22
+ end
23
+
24
+ @necessary_exps = necessary_exps
25
+ @necessary_exps.freeze
26
+ end
27
+
28
+ def define_exp_table(max_level)
29
+ raise ArgumentError.new('max_level is less than min_level') if max_level < @min_level
30
+ raise ArgumentError unless block_given?
31
+ # TODO: How to convert the block to a proc?
32
+ necessary_exps = generate_necessary_exps(max_level) {|info| yield(info)}
33
+ define_exp_table_from_array(necessary_exps)
34
+ end
35
+
36
+ def max_level
37
+ @min_level + @necessary_exps.length
38
+ end
39
+
40
+ def is_allowed_level?(level)
41
+ level.between?(@min_level, max_level)
42
+ end
43
+
44
+ def find_necessary_exp_by_level(level)
45
+ return nil unless is_allowed_level?(level)
46
+ return 0 if level == @min_level
47
+ @necessary_exps[level - @min_level - 1]
48
+ end
49
+
50
+ def calculate_total_necessary_exp(from_level, to_level)
51
+ raise ArgumentError.new('from_level is greater than to_level') if from_level > to_level
52
+ raise ArgumentError.new('from_level is out of range') unless is_allowed_level?(from_level)
53
+ raise ArgumentError.new('to_level is out of range') unless is_allowed_level?(to_level)
54
+
55
+ (from_level..to_level).inject(0) do |result, level|
56
+ result + find_necessary_exp_by_level(level)
57
+ end
58
+ end
59
+
60
+ def max_exp
61
+ calculate_total_necessary_exp(@min_level, max_level)
62
+ end
63
+
64
+ def level_status
65
+ @cached_current_level_status = generate_status_of_current_level() unless @cached_current_level_status
66
+ @cached_current_level_status.dup
67
+ end
68
+
69
+ def level
70
+ level_status[:level]
71
+ end
72
+
73
+ def is_reached_max_level?
74
+ level == max_level
75
+ end
76
+
77
+ def alter_exp(exp_delta)
78
+ raise ArgumentError.new('exp_delta is not a integer') unless exp_delta.is_a?(Integer)
79
+ before_exp = @exp
80
+ change_exp_result = change_exp(@exp + exp_delta)
81
+ self.class.generate_exp_change_result(
82
+ before_exp, @exp, change_exp_result[:before_level], change_exp_result[:after_level])
83
+ end
84
+
85
+ def obtain_exp(increase_of_exp)
86
+ raise ArgumentError.new('increase_of_exp is less than 0') if increase_of_exp < 0
87
+ alter_exp(increase_of_exp)
88
+ end
89
+
90
+ def drain_exp(decrease_of_exp)
91
+ raise ArgumentError.new('decrease_of_exp is less than 0') if decrease_of_exp < 0
92
+ alter_exp(-decrease_of_exp)
93
+ end
94
+
95
+ def clear_exp
96
+ change_exp(0)
97
+ nil
98
+ end
99
+
100
+ # fraction_mode
101
+ # :omitted
102
+ # ex ) Lv3(15/20) + Lv1 => Lv4( 0/25) - As a Rogue-like games
103
+ # :inherited
104
+ # ex1) Lv3(15/20) + Lv1 => Lv4(15/25)
105
+ # ex2) Lv3(15/20) + Lv1 => Lv4(14/15)
106
+ def obtain_exp_by_level(increase_of_level, fraction_mode)
107
+ raise ArgumentError.new('increase_of_level is not a integer') unless increase_of_level.is_a?(Integer)
108
+ raise ArgumentError.new('increase_of_level is less than 0') if increase_of_level < 0
109
+ raise ArgumentError.new('fraction_mode is invalid') unless [:omitted, :inherited].include?(fraction_mode)
110
+
111
+ from_level = level
112
+ to_level = cut_level_into_valid_range(from_level + increase_of_level)
113
+
114
+ exp_delta = case fraction_mode
115
+ when :omitted then
116
+ calculate_total_necessary_exp(min_level, to_level) - @exp
117
+ when :inherited then
118
+ next_necessary_exp_after_leveling_up = find_necessary_exp_by_level(to_level + 1)
119
+ inherited_fraction_exp = if next_necessary_exp_after_leveling_up
120
+ [level_status[:obtained_exp_for_next], next_necessary_exp_after_leveling_up - 1].min
121
+ else
122
+ 0
123
+ end
124
+ calculate_total_necessary_exp(min_level, to_level) + inherited_fraction_exp - @exp
125
+ end
126
+
127
+ alter_exp(exp_delta)
128
+ end
129
+
130
+ # fraction_mode
131
+ # :omitted
132
+ # ex) Lv3(15/20) - Lv1 => Lv2( 0/10) - As a Wiz-like games
133
+ # :full
134
+ # ex) Lv3(15/20) - Lv1 => Lv2(14/15) - As a Rogue-like games
135
+ def drain_exp_by_level(decrease_of_level, fraction_mode)
136
+ raise ArgumentError.new('decrease_of_level is not a integer') unless decrease_of_level.is_a?(Integer)
137
+ raise ArgumentError.new('decrease_of_level is less than 0') if decrease_of_level < 0
138
+ raise ArgumentError.new('fraction_mode is invalid') unless [:omitted, :full].include?(fraction_mode)
139
+
140
+ from_level = level
141
+ to_level = cut_level_into_valid_range(from_level - decrease_of_level)
142
+
143
+ exp_delta = case fraction_mode
144
+ when :omitted then
145
+ calculate_total_necessary_exp(min_level, to_level) - @exp
146
+ when :full then
147
+ to_level = [to_level + 1, from_level].min
148
+ calculate_total_necessary_exp(min_level, to_level) - @exp - 1
149
+ end
150
+
151
+ alter_exp(exp_delta)
152
+ end
153
+
154
+ private
155
+
156
+ def self.generate_exp_change_result(before_exp, after_exp, before_level, after_level)
157
+ {
158
+ before_exp: before_exp,
159
+ after_exp: after_exp,
160
+ exp_delta: after_exp - before_exp,
161
+ before_level: before_level,
162
+ after_level: after_level,
163
+ level_delta: after_level - before_level,
164
+ is_leveling_up: after_level > before_level,
165
+ is_leveling_down: after_level < before_level
166
+ }
167
+ end
168
+
169
+ def generate_necessary_exps(max_level)
170
+ generated_exps = []
171
+ memo = {}
172
+
173
+ ((@min_level + 1)..max_level).map do |level|
174
+ exp = yield({
175
+ level: level,
176
+ min_level: @min_level,
177
+ max_level: max_level,
178
+ generated_exps: generated_exps,
179
+ memo: memo
180
+ })
181
+ generated_exps << exp
182
+ exp
183
+ end
184
+ end
185
+
186
+ def guess_level_from_necessary_exps_index(index)
187
+ @min_level + index + 1
188
+ end
189
+
190
+ def generate_status_of_current_level
191
+ current_level = @min_level
192
+ next_necessary_exp = nil
193
+ total_necessary_exp = 0
194
+ obtained_exp_for_next = nil
195
+ lacking_exp_for_next = nil
196
+
197
+ @necessary_exps.each_with_index do |necessary_exp, index|
198
+ total_necessary_exp += necessary_exp
199
+
200
+ if @exp < total_necessary_exp
201
+ next_necessary_exp = necessary_exp
202
+ lacking_exp_for_next = total_necessary_exp - @exp
203
+ obtained_exp_for_next = next_necessary_exp - lacking_exp_for_next
204
+ break
205
+ end
206
+
207
+ current_level = guess_level_from_necessary_exps_index(index)
208
+ end
209
+
210
+ {
211
+ level: current_level,
212
+ next_necessary_exp: next_necessary_exp,
213
+ lacking_exp_for_next: lacking_exp_for_next,
214
+ obtained_exp_for_next: obtained_exp_for_next
215
+ }
216
+ end
217
+
218
+ def clear_cached_current_level_status
219
+ @cached_current_level_status = CLEARED_CACHED_CURRENT_LEVEL_STATUS
220
+ end
221
+
222
+ def cut_exp_into_valid_range(suggested_exp)
223
+ [[suggested_exp, max_exp].min, 0].max
224
+ end
225
+
226
+ def change_exp(exp)
227
+ before_level = level
228
+ @exp = cut_exp_into_valid_range(exp)
229
+ clear_cached_current_level_status
230
+ {
231
+ before_level: before_level,
232
+ after_level: level
233
+ }
234
+ end
235
+
236
+ def cut_level_into_valid_range(suggested_level)
237
+ [[suggested_level, max_level].min, @min_level].max
238
+ end
239
+ end
@@ -0,0 +1,3 @@
1
+ class RpgLevel
2
+ VERSION = "0.1.0"
3
+ end
data/rpg_level.gemspec ADDED
@@ -0,0 +1,26 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'rpg_level/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "rpg_level"
8
+ spec.version = RpgLevel::VERSION
9
+ spec.authors = ["kjirou"]
10
+ spec.email = ["sorenariblog@gmail.com"]
11
+
12
+ spec.summary = 'A Level/EXP manager for RPGs.'
13
+ spec.description = 'Manage the Level/EXP that is known as the most commonly growth system of role-playing games.'
14
+ spec.homepage = 'https://github.com/kjirou/ruby-rpg_level'
15
+ spec.license = "MIT"
16
+
17
+ spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
18
+ spec.bindir = "exe"
19
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
20
+ spec.require_paths = ["lib"]
21
+
22
+ spec.add_development_dependency "bundler", "~> 1.12"
23
+ spec.add_development_dependency "pry"
24
+ spec.add_development_dependency "rake", "~> 10.0"
25
+ spec.add_development_dependency "rspec", "~> 3.0"
26
+ end
metadata ADDED
@@ -0,0 +1,114 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: rpg_level
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - kjirou
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2016-09-18 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.12'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.12'
27
+ - !ruby/object:Gem::Dependency
28
+ name: pry
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rake
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '10.0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '10.0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: rspec
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '3.0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '3.0'
69
+ description: Manage the Level/EXP that is known as the most commonly growth system
70
+ of role-playing games.
71
+ email:
72
+ - sorenariblog@gmail.com
73
+ executables: []
74
+ extensions: []
75
+ extra_rdoc_files: []
76
+ files:
77
+ - ".gitignore"
78
+ - ".rspec"
79
+ - ".travis.yml"
80
+ - CODE_OF_CONDUCT.md
81
+ - Gemfile
82
+ - LICENSE.txt
83
+ - README.md
84
+ - Rakefile
85
+ - bin/console
86
+ - bin/setup
87
+ - lib/rpg_level.rb
88
+ - lib/rpg_level/version.rb
89
+ - rpg_level.gemspec
90
+ homepage: https://github.com/kjirou/ruby-rpg_level
91
+ licenses:
92
+ - MIT
93
+ metadata: {}
94
+ post_install_message:
95
+ rdoc_options: []
96
+ require_paths:
97
+ - lib
98
+ required_ruby_version: !ruby/object:Gem::Requirement
99
+ requirements:
100
+ - - ">="
101
+ - !ruby/object:Gem::Version
102
+ version: '0'
103
+ required_rubygems_version: !ruby/object:Gem::Requirement
104
+ requirements:
105
+ - - ">="
106
+ - !ruby/object:Gem::Version
107
+ version: '0'
108
+ requirements: []
109
+ rubyforge_project:
110
+ rubygems_version: 2.5.1
111
+ signing_key:
112
+ specification_version: 4
113
+ summary: A Level/EXP manager for RPGs.
114
+ test_files: []