pfrpg_core 0.0.1
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/Rakefile +7 -0
- data/lib/pfrpg_core/affectable.rb +7 -0
- data/lib/pfrpg_core/bonus.rb +8 -0
- data/lib/pfrpg_core/bonuses.rb +57 -0
- data/lib/pfrpg_core/effect.rb +39 -0
- data/lib/pfrpg_core/effect_processor.rb +49 -0
- data/lib/pfrpg_core/feat_finder.rb +49 -0
- data/lib/pfrpg_core/filterable.rb +11 -0
- data/lib/pfrpg_core/level_parser.rb +34 -0
- data/lib/pfrpg_core/math_helper.rb +6 -0
- data/lib/pfrpg_core/null_object.rb +37 -0
- data/lib/pfrpg_core/prerequisite.rb +58 -0
- data/lib/pfrpg_core/quick_classed.rb +37 -0
- data/lib/pfrpg_core/version.rb +3 -0
- data/lib/pfrpg_core.rb +11 -0
- data/test/feat_totaler_test.rb +14 -0
- data/test/helper.rb +20 -0
- metadata +90 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 04e10d025ab0c0eabc6af56107b277194255e478
|
4
|
+
data.tar.gz: 7c2dfb7e770d7399d495ab5eae8aaedabcd97adc
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 5f755c54bebcc8ede1c1cdcd7c5aa094b00de3f63e3e46d7cec2f40d6c97f7538fb5af38d6bfee79fc6763ef39c8e014c8e912536cb72bbcf479681f7ba85e97
|
7
|
+
data.tar.gz: a69a83f0f8d0e08596b5f2f49cd2fa94475862194449c6ba6d5af2765eff2c5ad1aed2b896866a7ff1e5f9623cc1370be5a36a3f765005951b78857605f31adb
|
data/Rakefile
ADDED
@@ -0,0 +1,57 @@
|
|
1
|
+
module PfrpgCore
|
2
|
+
class Bonuses
|
3
|
+
attr_reader :bonus_pool
|
4
|
+
def initialize
|
5
|
+
@bonus_pool = {}
|
6
|
+
end
|
7
|
+
|
8
|
+
def specials
|
9
|
+
['speed', 'damage_reduction', 'spell_resistance', 'class_skill']
|
10
|
+
end
|
11
|
+
|
12
|
+
def get(key)
|
13
|
+
if specials.include? key
|
14
|
+
return self.send(key)
|
15
|
+
else
|
16
|
+
return NullObject.maybe(@bonus_pool[key])
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def plus(key, value)
|
21
|
+
begin
|
22
|
+
v = MathHelper.is_number(value) ? value.to_i : value
|
23
|
+
if specials.include? key
|
24
|
+
if @bonus_pool[key]
|
25
|
+
@bonus_pool[key] << value
|
26
|
+
else
|
27
|
+
@bonus_pool[key] = [value]
|
28
|
+
end
|
29
|
+
elsif @bonus_pool[key]
|
30
|
+
@bonus_pool[key] += v
|
31
|
+
else
|
32
|
+
@bonus_pool[key] = v
|
33
|
+
end
|
34
|
+
rescue Exception => e
|
35
|
+
ap @bonus_pool
|
36
|
+
ap "Trying to add #{key}, #{value}"
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def speed
|
41
|
+
[ @bonus_pool['speed'] ].flatten
|
42
|
+
end
|
43
|
+
|
44
|
+
def class_skill
|
45
|
+
[ @bonus_pool['class_skill'] ].flatten
|
46
|
+
end
|
47
|
+
|
48
|
+
def damage_reduction
|
49
|
+
[ @bonus_pool['damage_reduction'] ].flatten
|
50
|
+
end
|
51
|
+
|
52
|
+
def spell_resistance
|
53
|
+
[ @bonus_pool['spell_resistance'] ].flatten
|
54
|
+
end
|
55
|
+
|
56
|
+
end
|
57
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
class PfrpgCore::Effect
|
2
|
+
attr_accessor :type, :key, :value, :affect
|
3
|
+
def initialize(type, key, value, affect=nil)
|
4
|
+
# standard effect proc; add a value
|
5
|
+
basic = Proc.new do |character, attribute, value|
|
6
|
+
character.bonuses.plus(attribute, value)
|
7
|
+
end
|
8
|
+
affect ||= basic
|
9
|
+
@type = type
|
10
|
+
@key = key
|
11
|
+
@value = value
|
12
|
+
@affect = affect
|
13
|
+
end
|
14
|
+
|
15
|
+
def self.load(effects_string)
|
16
|
+
return effects_string if effects_string.instance_of? Array
|
17
|
+
return [ effects_string ] if effects_string.instance_of? PfrpgCore::Effect
|
18
|
+
return [] if effects_string.nil? || effects_string.empty?
|
19
|
+
unless effects_string.instance_of? String
|
20
|
+
ap "Error effect: "
|
21
|
+
ap effects_string
|
22
|
+
end
|
23
|
+
raise Exception unless effects_string.instance_of? String
|
24
|
+
effects_string.split(";").map { |effect| parse_effect(effect) }
|
25
|
+
end
|
26
|
+
|
27
|
+
def self.parse_effect(string)
|
28
|
+
p = string.split(":")
|
29
|
+
type = p[0]
|
30
|
+
key = p[1]
|
31
|
+
value = p[2]
|
32
|
+
PfrpgCore::Effect.new(type, key, value)
|
33
|
+
end
|
34
|
+
|
35
|
+
def apply(character)
|
36
|
+
@affect.call(character, @key, @value)
|
37
|
+
end
|
38
|
+
|
39
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
module PfrpgCore
|
2
|
+
class EffectProcessor
|
3
|
+
attr_reader :character, :effects
|
4
|
+
def initialize(character, effects)
|
5
|
+
@character = character
|
6
|
+
@effects = effects
|
7
|
+
end
|
8
|
+
|
9
|
+
def process_effects
|
10
|
+
final = []
|
11
|
+
no_stacking = effects_hash(@effects)
|
12
|
+
no_stacking.keys.each do |key|
|
13
|
+
if Tables::Bonus.stackable?(key) || Tables::Bonus.special?(key)
|
14
|
+
final << no_stacking[key]
|
15
|
+
else
|
16
|
+
final << find_max(no_stacking[key])
|
17
|
+
end
|
18
|
+
end
|
19
|
+
return final.flatten
|
20
|
+
end
|
21
|
+
|
22
|
+
def effects_hash(effects)
|
23
|
+
hash = {}
|
24
|
+
effects.each do |effect|
|
25
|
+
if hash[effect.key] == nil
|
26
|
+
hash[effect.key] = []
|
27
|
+
end
|
28
|
+
hash[effect.key] << effect
|
29
|
+
end
|
30
|
+
hash
|
31
|
+
end
|
32
|
+
|
33
|
+
def find_max(effects)
|
34
|
+
max = nil
|
35
|
+
effects.each do |e|
|
36
|
+
if max == nil || is_larger(e.value, max.value)
|
37
|
+
max = e
|
38
|
+
end
|
39
|
+
end
|
40
|
+
return max
|
41
|
+
end
|
42
|
+
|
43
|
+
def is_larger(a, b)
|
44
|
+
int_a = MathHelper.is_number(a) ? a.to_i : -999
|
45
|
+
int_b = MathHelper.is_number(b) ? b.to_i : -999
|
46
|
+
int_a > int_b
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
module PfrpgCore
|
2
|
+
class FeatFinder
|
3
|
+
attr_reader :entity, :feats
|
4
|
+
def initialize(entity, feats)
|
5
|
+
@entity = entity
|
6
|
+
@feats = feats
|
7
|
+
end
|
8
|
+
|
9
|
+
def find_feats
|
10
|
+
feats = @feats
|
11
|
+
feats = filter_feats_by_owned(feats, entity)
|
12
|
+
feats = filter_feats_by_prereq(feats, entity)
|
13
|
+
return feats
|
14
|
+
end
|
15
|
+
|
16
|
+
def filter_feats_by_owned(feats, entity)
|
17
|
+
owned_feats = entity.feats
|
18
|
+
feats.select do |f|
|
19
|
+
repeat_list.include?(f.name) ||
|
20
|
+
doesnt_include_feat(owned_feats, f)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def doesnt_include_feat(feats, feat)
|
25
|
+
!(feats.find { |x| x.name == feat.name })
|
26
|
+
end
|
27
|
+
|
28
|
+
def filter_feats_by_prereq(feats, entity)
|
29
|
+
feats.select do |f|
|
30
|
+
prereq_check(entity, f)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def prereq_check(entity, feat)
|
35
|
+
prereqs = Prerequisite.load(feat.prereq_code)
|
36
|
+
prereqs.all? { |x| x.match(entity) }
|
37
|
+
end
|
38
|
+
|
39
|
+
def repeat_list
|
40
|
+
[
|
41
|
+
'Exotic Weapon Proficiency',
|
42
|
+
'Martial Weapon Proficiency',
|
43
|
+
'Weapon Focus',
|
44
|
+
'Spell Focus',
|
45
|
+
'Custom Feat'
|
46
|
+
]
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
module PfrpgCore
|
2
|
+
class LevelParser
|
3
|
+
attr_reader :level_string, :levels
|
4
|
+
def initialize(level_string)
|
5
|
+
@level_string = level_string
|
6
|
+
@levels = parse
|
7
|
+
end
|
8
|
+
|
9
|
+
def total
|
10
|
+
return @levels.values.inject(0) { |sum, l| sum + l }
|
11
|
+
end
|
12
|
+
|
13
|
+
def parse
|
14
|
+
levels = {}
|
15
|
+
splits = @level_string.split(';')
|
16
|
+
raise Exception if splits[0].empty?
|
17
|
+
|
18
|
+
splits.each do |s|
|
19
|
+
level = s.split(':')[1].to_i
|
20
|
+
hc = s.split(':')[0]
|
21
|
+
raise Exception if (hc.empty?)
|
22
|
+
levels[hc] = level
|
23
|
+
end
|
24
|
+
return levels
|
25
|
+
end
|
26
|
+
|
27
|
+
def valid_total_level
|
28
|
+
return total <= 20
|
29
|
+
rescue Exception
|
30
|
+
return false
|
31
|
+
end
|
32
|
+
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
class PfrpgCore::NullObject < Numeric
|
2
|
+
def to_a; []; end
|
3
|
+
def to_s; ""; end
|
4
|
+
def to_str; ""; end
|
5
|
+
def to_f; 0.0; end
|
6
|
+
def to_i; 0; end
|
7
|
+
|
8
|
+
def empty?
|
9
|
+
true
|
10
|
+
end
|
11
|
+
|
12
|
+
def as_json(options={})
|
13
|
+
""
|
14
|
+
end
|
15
|
+
|
16
|
+
def method_missing(*args, &block)
|
17
|
+
self
|
18
|
+
end
|
19
|
+
|
20
|
+
def self.maybe(value)
|
21
|
+
case value
|
22
|
+
when nil then NullObject.new
|
23
|
+
else value
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def +(other)
|
28
|
+
case other
|
29
|
+
when String
|
30
|
+
to_s + other
|
31
|
+
when Fixnum
|
32
|
+
to_i + other
|
33
|
+
when Float
|
34
|
+
to_f + other
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
module PfrpgCore
|
2
|
+
class Prerequisite
|
3
|
+
|
4
|
+
def initialize(attribute, value, matcher=nil)
|
5
|
+
default_matcher = Proc.new do |character, attribute, value|
|
6
|
+
true
|
7
|
+
end
|
8
|
+
@attribute = attribute
|
9
|
+
@value = value
|
10
|
+
@matcher = matcher || default_matcher
|
11
|
+
end
|
12
|
+
|
13
|
+
def self.load(prereq_string)
|
14
|
+
begin
|
15
|
+
prereqs = []
|
16
|
+
p = prereq_string.split(";")
|
17
|
+
p.each { |x| prereqs << parse_prereq(x) }
|
18
|
+
prereqs
|
19
|
+
rescue Exception
|
20
|
+
[]
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def self.parse_prereq(string)
|
25
|
+
p = string.split(":")
|
26
|
+
type = p[0]
|
27
|
+
attribute = p[1]
|
28
|
+
value = p[2]
|
29
|
+
case type
|
30
|
+
when "class"
|
31
|
+
Prerequisite::ClassPrereq.new(attribute, value)
|
32
|
+
when "attribute"
|
33
|
+
Prerequisite::AttributePrereq.new(attribute, value)
|
34
|
+
when "alignment"
|
35
|
+
Prerequisite::AlignmentPrereq.new(attribute, value)
|
36
|
+
when "feat"
|
37
|
+
Prerequisite::FeatPrereq.new(attribute, value)
|
38
|
+
when "skill"
|
39
|
+
Prerequisite::SkillPrereq.new(attribute, value)
|
40
|
+
when "class_feature"
|
41
|
+
Prerequisite::HeroclassFeaturePrereq.new(attribute, value)
|
42
|
+
when "combat"
|
43
|
+
Prerequisite::BabPrereq.new(attribute, value)
|
44
|
+
when "language"
|
45
|
+
Prerequisite::LanguagePrereq.new(attribute, value)
|
46
|
+
when "misc"
|
47
|
+
Prerequisite::MiscPrereq.new(attribute, value)
|
48
|
+
else
|
49
|
+
Prerequisite.new(attribute, value)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
def match(character)
|
54
|
+
@matcher.call(character, @attribute, @value)
|
55
|
+
end
|
56
|
+
|
57
|
+
end
|
58
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
# basic class math. classes must provide latest_levels method
|
2
|
+
# with a map of heroclass name & level
|
3
|
+
module PfrpgCore
|
4
|
+
module QuickClassed
|
5
|
+
def get_class_level(heroclass)
|
6
|
+
l = latest_levels[heroclass.name]
|
7
|
+
l ||= 0
|
8
|
+
return l
|
9
|
+
end
|
10
|
+
|
11
|
+
def explode_levels
|
12
|
+
# TODO: implement me
|
13
|
+
[]
|
14
|
+
end
|
15
|
+
|
16
|
+
def get_total_level
|
17
|
+
latest_levels.keys.inject(0) do |key, sum|
|
18
|
+
sum = sum + latest_levels[key]
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def hit_dice
|
23
|
+
get_total_level
|
24
|
+
end
|
25
|
+
|
26
|
+
def get_caster_level
|
27
|
+
# TODO constantize somewhere better
|
28
|
+
caster_classes = ['Bard', 'Cleric', 'Druid', 'Paladin', 'Ranger', 'Sorcerer', 'Wizard']
|
29
|
+
caster_levels = []
|
30
|
+
latest_levels.keys.each do |key|
|
31
|
+
caster_levels << latest_levels[key] if caster_classes.include? key
|
32
|
+
end
|
33
|
+
ek_level = get_class_level(Heroclass.by_name('Eldritch Knight'))
|
34
|
+
return (caster_levels.max + ek_level) || 0
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
data/lib/pfrpg_core.rb
ADDED
@@ -0,0 +1,11 @@
|
|
1
|
+
require 'pfrpg_core/effect'
|
2
|
+
require 'pfrpg_core/effect_processor'
|
3
|
+
require 'pfrpg_core/math_helper'
|
4
|
+
require 'pfrpg_core/null_object'
|
5
|
+
|
6
|
+
Dir[File.expand_path(File.join(File.dirname(File.absolute_path(__FILE__)), 'pfrpg_core/')) + "/**/*.rb"].each do |file|
|
7
|
+
require file
|
8
|
+
end
|
9
|
+
|
10
|
+
module PfrpgCore
|
11
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
require 'minitest/autorun'
|
2
|
+
require 'test/helper'
|
3
|
+
|
4
|
+
class FeatFinderTest < Minitest::Test
|
5
|
+
|
6
|
+
def test_filter_feats_by_owned
|
7
|
+
e = PsuedoEntity.new
|
8
|
+
feats = [ PsuedoFeat.new('Feat1') ]
|
9
|
+
finder = FeatFinder.new(e, feats)
|
10
|
+
assert finder.find_feats.empty?
|
11
|
+
end
|
12
|
+
|
13
|
+
|
14
|
+
end
|
data/test/helper.rb
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
class Helper
|
2
|
+
|
3
|
+
PsuedoFeat = Struct.new(:name) do
|
4
|
+
def prereq_code
|
5
|
+
nil
|
6
|
+
end
|
7
|
+
end
|
8
|
+
|
9
|
+
class PsuedoEntity
|
10
|
+
attr_accessor :feats
|
11
|
+
def initialize(feats = nil)
|
12
|
+
@feats = feats
|
13
|
+
@feats ||= default_feats
|
14
|
+
end
|
15
|
+
|
16
|
+
def default_feats
|
17
|
+
[ PsuedoFeat.new('Feat1') ]
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
metadata
ADDED
@@ -0,0 +1,90 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: pfrpg_core
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Jordan OMara
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2014-11-11 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: rails
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: 4.1.1
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: 4.1.1
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: sqlite3
|
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
|
+
description: core
|
42
|
+
email:
|
43
|
+
- jordan@herosheets.com
|
44
|
+
executables: []
|
45
|
+
extensions: []
|
46
|
+
extra_rdoc_files: []
|
47
|
+
files:
|
48
|
+
- Rakefile
|
49
|
+
- lib/pfrpg_core.rb
|
50
|
+
- lib/pfrpg_core/affectable.rb
|
51
|
+
- lib/pfrpg_core/bonus.rb
|
52
|
+
- lib/pfrpg_core/bonuses.rb
|
53
|
+
- lib/pfrpg_core/effect.rb
|
54
|
+
- lib/pfrpg_core/effect_processor.rb
|
55
|
+
- lib/pfrpg_core/feat_finder.rb
|
56
|
+
- lib/pfrpg_core/filterable.rb
|
57
|
+
- lib/pfrpg_core/level_parser.rb
|
58
|
+
- lib/pfrpg_core/math_helper.rb
|
59
|
+
- lib/pfrpg_core/null_object.rb
|
60
|
+
- lib/pfrpg_core/prerequisite.rb
|
61
|
+
- lib/pfrpg_core/quick_classed.rb
|
62
|
+
- lib/pfrpg_core/version.rb
|
63
|
+
- test/feat_totaler_test.rb
|
64
|
+
- test/helper.rb
|
65
|
+
homepage: http://herosheets.com
|
66
|
+
licenses: []
|
67
|
+
metadata: {}
|
68
|
+
post_install_message:
|
69
|
+
rdoc_options: []
|
70
|
+
require_paths:
|
71
|
+
- lib
|
72
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
73
|
+
requirements:
|
74
|
+
- - ">="
|
75
|
+
- !ruby/object:Gem::Version
|
76
|
+
version: '0'
|
77
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
78
|
+
requirements:
|
79
|
+
- - ">="
|
80
|
+
- !ruby/object:Gem::Version
|
81
|
+
version: '0'
|
82
|
+
requirements: []
|
83
|
+
rubyforge_project:
|
84
|
+
rubygems_version: 2.2.2
|
85
|
+
signing_key:
|
86
|
+
specification_version: 4
|
87
|
+
summary: PFRPG core
|
88
|
+
test_files:
|
89
|
+
- test/feat_totaler_test.rb
|
90
|
+
- test/helper.rb
|