oakdex-battle 0.0.2 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: a1d0972ff95da713b2117cf6f642ac45edc48daa
4
- data.tar.gz: 9d71621683079bebffd2703b7c448a3df79873ca
3
+ metadata.gz: 2066dbb4e78aff0e298029c4eb76f345326e172e
4
+ data.tar.gz: bf2201bd2ebfdb085b1fa2b766ec344e41c8a97c
5
5
  SHA512:
6
- metadata.gz: '08b23d0d4936817b5a93f044fd69af58ee74b534e973e97d143555d9f0f6b2af9b23a95c910833aa689bd13e794dcf4a37d383f09173906c4d632ffcd2613acd'
7
- data.tar.gz: 25f79543b12fde79d5777cc6857a65b08aaee38bca10a11fa9c32b2bb259b50634d2f01a6502221432d786c04edcc9bd86bfc673846b47921ffc5f3c39d07ee0
6
+ metadata.gz: 30127361db91d8748335a8a58609a045ddf3c7faceac02a034e8da966496c89271f9c4ebe2cd3e3af3e83b6e819b2904e6ae80b823d22501b28696cb3fb5e7d7
7
+ data.tar.gz: 61482125d2312a029a6de584efc8069be965e9c21b26cdf7163e3501e3273c7d519b8578b61284adb770a5d6570c7e4b320e94371d8126d769d1ba228ad2f4a0
data/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # <img src="https://v20.imgup.net/oakdex_logfbad.png" alt="fixer" width=282>
2
2
 
3
- [![Build Status](https://travis-ci.org/jalyna/oakdex-battle.svg?branch=master)](https://travis-ci.org/jalyna/oakdex-battle) [![Maintainability](https://api.codeclimate.com/v1/badges/ef91681257a6900f03ac/maintainability)](https://codeclimate.com/github/jalyna/oakdex-battle/maintainability) [![Test Coverage](https://api.codeclimate.com/v1/badges/ef91681257a6900f03ac/test_coverage)](https://codeclimate.com/github/jalyna/oakdex-battle/test_coverage)
3
+ [![Gem Version](https://badge.fury.io/rb/oakdex-battle.svg)](https://badge.fury.io/rb/oakdex-battle) [![Build Status](https://travis-ci.org/jalyna/oakdex-battle.svg?branch=master)](https://travis-ci.org/jalyna/oakdex-battle) [![Maintainability](https://api.codeclimate.com/v1/badges/ef91681257a6900f03ac/maintainability)](https://codeclimate.com/github/jalyna/oakdex-battle/maintainability) [![Test Coverage](https://api.codeclimate.com/v1/badges/ef91681257a6900f03ac/test_coverage)](https://codeclimate.com/github/jalyna/oakdex-battle/test_coverage)
4
4
 
5
5
  Based on [oakdex-pokedex](https://github.com/jalyna/oakdex-pokedex).
6
6
 
@@ -11,8 +11,8 @@ Based on [oakdex-pokedex](https://github.com/jalyna/oakdex-pokedex).
11
11
  ```ruby
12
12
  require 'oakdex/battle'
13
13
 
14
- pok1 = Oakdex::Battle::Pokemon.create('Pikachu', level: 10)
15
- pok2 = Oakdex::Battle::Pokemon.create('Bulbasaur', {
14
+ pok1 = Oakdex::Pokemon.create('Pikachu', level: 10)
15
+ pok2 = Oakdex::Pokemon.create('Bulbasaur', {
16
16
  exp: 120,
17
17
  gender: 'female',
18
18
  ability: 'Soundproof',
@@ -75,8 +75,8 @@ battle.winner # => trainer1
75
75
  ### Other Battle types
76
76
 
77
77
  ```ruby
78
- pok3 = Oakdex::Battle::Pokemon.create('Altaria', level: 20)
79
- pok4 = Oakdex::Battle::Pokemon.create('Elekid', level: 14)
78
+ pok3 = Oakdex::Pokemon.create('Altaria', level: 20)
79
+ pok4 = Oakdex::Pokemon.create('Elekid', level: 14)
80
80
  trainer1 = Oakdex::Battle::Trainer.new('Ash', [pok1, pok3, pok9])
81
81
  trainer2 = Oakdex::Battle::Trainer.new('Misty', [pok2, pok4, pok10])
82
82
  trainer3 = Oakdex::Battle::Trainer.new('Brock', [pok5, pok6])
@@ -90,19 +90,6 @@ battle = Oakdex::Battle.new([trainer1], [trainer2], pokemon_per_side: 3)
90
90
  battle = Oakdex::Battle.new([trainer1, trainer3], [trainer2, trainer4])
91
91
  ```
92
92
 
93
-
94
- ### Breeding
95
-
96
- ```ruby
97
- pok1 = Oakdex::Battle::Pokemon.create('Ditto', level: 20)
98
- pok2 = Oakdex::Battle::Pokemon.create('Pikachu', level: 20, gender: 'female')
99
-
100
- Oakdex::Battle::Breeding.compatible?(pok1, pok2) # => true
101
- Oakdex::Battle::Breeding.chance_in_percentage(pok1, pok2) # => 20
102
- Oakdex::Battle::Breeding.breed(pok1, pok2) #=> Oakdex::Battle::Pokemon Pichu
103
- ```
104
-
105
-
106
93
  ## Contributing
107
94
 
108
95
  I would be happy if you want to add your contribution to the project. In order to contribute, you just have to fork this repository.
data/lib/oakdex/battle.rb CHANGED
@@ -1,6 +1,6 @@
1
- require 'oakdex/pokedex'
1
+ require 'oakdex/pokemon'
2
2
 
3
- require 'oakdex/battle/pokemon'
3
+ require 'oakdex/battle/in_battle_pokemon'
4
4
  require 'oakdex/battle/trainer'
5
5
  require 'oakdex/battle/move_execution'
6
6
  require 'oakdex/battle/action'
@@ -8,8 +8,7 @@ require 'oakdex/battle/damage'
8
8
  require 'oakdex/battle/turn'
9
9
  require 'oakdex/battle/valid_action_service'
10
10
  require 'oakdex/battle/side'
11
- require 'oakdex/battle/in_battle_pokemon'
12
- require 'oakdex/battle/breeding'
11
+ require 'oakdex/battle/active_in_battle_pokemon'
13
12
 
14
13
  module Oakdex
15
14
  # Represents battle, with has n turns and m sides
@@ -76,12 +76,12 @@ module Oakdex
76
76
  end
77
77
 
78
78
  def pokemon_by_position
79
- trainer.in_battle_pokemon
79
+ trainer.active_in_battle_pokemon
80
80
  .find { |ibp| ibp.position == @attributes[:pokemon] }&.pokemon
81
81
  end
82
82
 
83
83
  def target_by_position(side, position)
84
- side.in_battle_pokemon
84
+ side.active_in_battle_pokemon
85
85
  .find { |ibp| ibp.position == position }&.pokemon
86
86
  end
87
87
 
@@ -0,0 +1,152 @@
1
+ require 'forwardable'
2
+
3
+ module Oakdex
4
+ class Battle
5
+ # Represents a pokemon that is actively fighting in battle
6
+ class ActiveInBattlePokemon
7
+ extend Forwardable
8
+
9
+ def_delegators :@pokemon, :moves_with_pp, :fainted?
10
+ def_delegators :@side, :battle
11
+
12
+ attr_reader :pokemon, :position, :side
13
+
14
+ def initialize(pokemon, side, position = 0)
15
+ @pokemon = pokemon
16
+ @side = side
17
+ @position = position
18
+ end
19
+
20
+ def action_added?
21
+ actions.any? { |a| a.pokemon == pokemon }
22
+ end
23
+
24
+ def valid_move_actions
25
+ return [] if action_added?
26
+ moves = moves_with_pp
27
+ moves = [struggle_move] if moves_with_pp.empty?
28
+ moves.flat_map do |move|
29
+ targets_in_battle(move).map do |target|
30
+ {
31
+ action: 'move',
32
+ pokemon: pokemon,
33
+ move: move,
34
+ target: target
35
+ }
36
+ end
37
+ end
38
+ end
39
+
40
+ private
41
+
42
+ def targets_in_battle(move)
43
+ available_targets(move).map do |targets|
44
+ if targets.last.is_a?(Array)
45
+ targets if targets_in_battle?(targets)
46
+ elsif target_in_battle?(targets)
47
+ targets
48
+ end
49
+ end.compact.reject(&:empty?)
50
+ end
51
+
52
+ def targets_in_battle?(targets)
53
+ targets.any? { |target| target[0].pokemon_in_battle?(target[1]) }
54
+ end
55
+
56
+ def target_in_battle?(target)
57
+ target[0].pokemon_in_battle?(target[1]) ||
58
+ (!target[0].pokemon_left? && target[1] == 0)
59
+ end
60
+
61
+ def struggle_move
62
+ @struggle_move ||= Oakdex::Pokemon::Move.create('Struggle')
63
+ end
64
+
65
+ def available_targets(move)
66
+ with_target(move) || multiple_targets_adjacent(move) ||
67
+ multiple_targets(move) || []
68
+ end
69
+
70
+ def multiple_targets(move)
71
+ case move.target
72
+ when 'all_users' then [all_users]
73
+ when 'all_except_user' then [all_targets - [self_target]]
74
+ when 'all' then [all_targets]
75
+ when 'all_foes' then [all_foes]
76
+ end
77
+ end
78
+
79
+ def multiple_targets_adjacent(move)
80
+ case move.target
81
+ when 'all_adjacent' then [adjacent]
82
+ when 'adjacent_foes_all' then [adjacent_foes]
83
+ end
84
+ end
85
+
86
+ def with_target(move)
87
+ case move.target
88
+ when 'user', 'user_and_random_adjacent_foe' then [self_target]
89
+ when 'target_adjacent_user_single' then adjacent_users
90
+ when 'target_adjacent_single' then adjacent
91
+ when 'target_user_or_adjacent_user'
92
+ [self_target] + adjacent_users
93
+ end
94
+ end
95
+
96
+ def all_targets
97
+ all_foes + all_users
98
+ end
99
+
100
+ def target_adjacent_single
101
+ adjacent_foes + adjacent_users
102
+ end
103
+
104
+ def adjacent
105
+ adjacent_foes + adjacent_users
106
+ end
107
+
108
+ def self_target
109
+ [@side, position]
110
+ end
111
+
112
+ def adjacent_foes
113
+ [
114
+ [other_side, position - 1],
115
+ [other_side, position],
116
+ [other_side, position + 1]
117
+ ].select { |t| t[1] >= 0 && t[1] < pokemon_per_side }
118
+ end
119
+
120
+ def adjacent_users
121
+ [
122
+ [@side, position - 1],
123
+ [@side, position + 1]
124
+ ].select { |t| t[1] >= 0 && t[1] < pokemon_per_side }
125
+ end
126
+
127
+ def all_users
128
+ pokemon_per_side.times.map { |i| [@side, i] }
129
+ end
130
+
131
+ def all_foes
132
+ pokemon_per_side.times.map { |i| [other_side, i] }
133
+ end
134
+
135
+ def pokemon_per_side
136
+ battle.pokemon_per_side
137
+ end
138
+
139
+ def other_side
140
+ other_sides.first
141
+ end
142
+
143
+ def other_sides
144
+ battle.sides - [@side]
145
+ end
146
+
147
+ def actions
148
+ battle.actions
149
+ end
150
+ end
151
+ end
152
+ end
@@ -65,13 +65,12 @@ module Oakdex
65
65
  end
66
66
 
67
67
  def stab_modifier
68
- pokemon.types.include?(move.type) ? 1.5 : 1.0
68
+ pokemon.types.include?(move.type_id) ? 1.5 : 1.0
69
69
  end
70
70
 
71
71
  def type_modifier
72
- type_data = Oakdex::Pokedex::Type.find!(move.type)
73
72
  target.types.reduce(1.0) do |factor, type|
74
- factor * type_data.effectivness[type]
73
+ factor * move.type.effectivness[type]
75
74
  end
76
75
  end
77
76
 
@@ -1,158 +1,151 @@
1
1
  require 'forwardable'
2
+ require 'oakdex/battle/status_conditions'
2
3
 
3
4
  module Oakdex
4
5
  class Battle
5
- # Represents a pokemon that is in battle
6
+ # Represents detailed pokemon instance that is part of a Trainer's Team
6
7
  class InBattlePokemon
7
8
  extend Forwardable
8
9
 
9
- def_delegators :@pokemon, :current_hp, :moves_with_pp
10
- def_delegators :@side, :battle
11
-
12
- attr_reader :pokemon, :position, :side
13
-
14
- def initialize(pokemon, side, position = 0)
10
+ OTHER_STATS = %i[accuracy evasion critical_hit]
11
+ ALL_STATS = Oakdex::Pokemon::BATTLE_STATS + OTHER_STATS
12
+ STATUS_CONDITIONS = {
13
+ 'poison' => StatusConditions::Poison,
14
+ 'burn' => StatusConditions::Burn,
15
+ 'freeze' => StatusConditions::Freeze,
16
+ 'paralysis' => StatusConditions::Paralysis,
17
+ 'badly_poisoned' => StatusConditions::BadlyPoisoned,
18
+ 'sleep' => StatusConditions::Sleep
19
+ }
20
+
21
+ STAGE_MULTIPLIERS = {
22
+ -6 => Rational(2, 8),
23
+ -5 => Rational(2, 7),
24
+ -4 => Rational(2, 6),
25
+ -3 => Rational(2, 5),
26
+ -2 => Rational(2, 4),
27
+ -1 => Rational(2, 3),
28
+ 0 => Rational(2, 2),
29
+ 1 => Rational(3, 2),
30
+ 2 => Rational(4, 2),
31
+ 3 => Rational(5, 2),
32
+ 4 => Rational(6, 2),
33
+ 5 => Rational(7, 2),
34
+ 6 => Rational(8, 2)
35
+ }
36
+
37
+ STAGE_MULTIPLIERS_CRITICAL_HIT = {
38
+ 0 => Rational(1, 24),
39
+ 1 => Rational(1, 8),
40
+ 2 => Rational(1, 2),
41
+ 3 => Rational(1, 1)
42
+ }
43
+
44
+ STAGE_MULTIPLIERS_ACC_EVA = {
45
+ -6 => Rational(3, 9),
46
+ -5 => Rational(3, 8),
47
+ -4 => Rational(3, 7),
48
+ -3 => Rational(3, 6),
49
+ -2 => Rational(3, 5),
50
+ -1 => Rational(3, 4),
51
+ 0 => Rational(3, 3),
52
+ 1 => Rational(4, 3),
53
+ 2 => Rational(5, 3),
54
+ 3 => Rational(6, 3),
55
+ 4 => Rational(7, 3),
56
+ 5 => Rational(8, 3),
57
+ 6 => Rational(9, 3)
58
+ }
59
+
60
+ def_delegators :@pokemon, :types, :trainer, :trainer=,
61
+ :name, :moves, :moves_with_pp, :change_hp_by,
62
+ :change_pp_by, :level, :fainted?
63
+
64
+ attr_reader :status_conditions
65
+
66
+ def initialize(pokemon, options = {})
15
67
  @pokemon = pokemon
16
- @side = side
17
- @position = position
18
- end
19
-
20
- def fainted?
21
- current_hp.zero?
22
- end
23
-
24
- def action_added?
25
- actions.any? { |a| a.pokemon == pokemon }
26
- end
27
-
28
- def valid_move_actions
29
- return [] if action_added?
30
- moves = moves_with_pp
31
- moves = [struggle_move] if moves_with_pp.empty?
32
- moves.flat_map do |move|
33
- targets_in_battle(move).map do |target|
34
- {
35
- action: 'move',
36
- pokemon: pokemon,
37
- move: move,
38
- target: target
39
- }
40
- end
68
+ @status_conditions = options[:status_conditions] || []
69
+ if @pokemon.primary_status_condition
70
+ add_status_condition(@pokemon.primary_status_condition)
41
71
  end
72
+ reset_stats
42
73
  end
43
74
 
44
- private
45
-
46
- def targets_in_battle(move)
47
- available_targets(move).map do |targets|
48
- if targets.last.is_a?(Array)
49
- targets if targets_in_battle?(targets)
50
- elsif target_in_battle?(targets)
51
- targets
52
- end
53
- end.compact.reject(&:empty?)
75
+ def change_stat_by(stat, change_by)
76
+ modifiers = stage_multipliers(stat)
77
+ stat_before = @stat_modifiers[stat]
78
+ min_value = modifiers.keys.first
79
+ max_value = modifiers.keys.last
80
+ @stat_modifiers[stat] = if change_by < 0
81
+ [stat_before + change_by, min_value].max
82
+ else
83
+ [stat_before + change_by, max_value].min
84
+ end
85
+ stat_before != @stat_modifiers[stat]
54
86
  end
55
87
 
56
- def targets_in_battle?(targets)
57
- targets.any? { |target| target[0].pokemon_in_battle?(target[1]) }
88
+ def add_status_condition(condition_name)
89
+ @status_conditions << status_condition(condition_name)
90
+ @pokemon.primary_status_condition = condition_name
58
91
  end
59
92
 
60
- def target_in_battle?(target)
61
- target[0].pokemon_in_battle?(target[1]) ||
62
- (!target[0].pokemon_left? && target[1] == 0)
93
+ def remove_status_condition(condition)
94
+ @status_conditions = @status_conditions.reject { |s| s == condition }
95
+ @pokemon.primary_status_condition = nil if @status_conditions.empty?
63
96
  end
64
97
 
65
- def struggle_move
66
- @struggle_move ||= begin
67
- move_type = Oakdex::Pokedex::Move.find('Struggle')
68
- Oakdex::Battle::Move.new(move_type, move_type.pp, move_type.pp)
69
- end
98
+ def reset_stats
99
+ @stat_modifiers = (ALL_STATS - %i[hp]).map do |stat|
100
+ [stat, 0]
101
+ end.to_h
70
102
  end
71
103
 
72
- def available_targets(move)
73
- with_target(move) || multiple_targets_adjacent(move) ||
74
- multiple_targets(move) || []
104
+ def accuracy
105
+ stage(:accuracy)
75
106
  end
76
107
 
77
- def multiple_targets(move)
78
- case move.target
79
- when 'all_users' then [all_users]
80
- when 'all_except_user' then [all_targets - [self_target]]
81
- when 'all' then [all_targets]
82
- when 'all_foes' then [all_foes]
83
- end
108
+ def evasion
109
+ stage(:evasion)
84
110
  end
85
111
 
86
- def multiple_targets_adjacent(move)
87
- case move.target
88
- when 'all_adjacent' then [adjacent]
89
- when 'adjacent_foes_all' then [adjacent_foes]
90
- end
112
+ def critical_hit_prob
113
+ stage(:critical_hit)
91
114
  end
92
115
 
93
- def with_target(move)
94
- case move.target
95
- when 'user', 'user_and_random_adjacent_foe' then [self_target]
96
- when 'target_adjacent_user_single' then adjacent_users
97
- when 'target_adjacent_single' then adjacent
98
- when 'target_user_or_adjacent_user'
99
- [self_target] + adjacent_users
116
+ Oakdex::Pokemon::BATTLE_STATS.each do |stat|
117
+ define_method stat do
118
+ (@pokemon.public_send(stat) * stage(stat) *
119
+ status_condition_modifier(stat)).to_i
100
120
  end
101
121
  end
102
122
 
103
- def all_targets
104
- all_foes + all_users
105
- end
106
-
107
- def target_adjacent_single
108
- adjacent_foes + adjacent_users
109
- end
110
-
111
- def adjacent
112
- adjacent_foes + adjacent_users
113
- end
114
-
115
- def self_target
116
- [@side, position]
117
- end
118
-
119
- def adjacent_foes
120
- [
121
- [other_side, position - 1],
122
- [other_side, position],
123
- [other_side, position + 1]
124
- ].select { |t| t[1] >= 0 && t[1] < pokemon_per_side }
125
- end
126
-
127
- def adjacent_users
128
- [
129
- [@side, position - 1],
130
- [@side, position + 1]
131
- ].select { |t| t[1] >= 0 && t[1] < pokemon_per_side }
132
- end
133
-
134
- def all_users
135
- pokemon_per_side.times.map { |i| [@side, i] }
136
- end
137
-
138
- def all_foes
139
- pokemon_per_side.times.map { |i| [other_side, i] }
140
- end
123
+ private
141
124
 
142
- def pokemon_per_side
143
- battle.pokemon_per_side
125
+ def status_condition_modifier(stat)
126
+ status_conditions.reduce(1.0) do |modifier, condition|
127
+ condition.stat_modifier(stat) * modifier
128
+ end
144
129
  end
145
130
 
146
- def other_side
147
- other_sides.first
131
+ def status_condition(condition_name)
132
+ STATUS_CONDITIONS[condition_name].new(self)
148
133
  end
149
134
 
150
- def other_sides
151
- battle.sides - [@side]
135
+ def stage(stat)
136
+ multipliers = stage_multipliers(stat)
137
+ multipliers[@stat_modifiers[stat] || 0]
152
138
  end
153
139
 
154
- def actions
155
- battle.actions
140
+ def stage_multipliers(stat)
141
+ case stat
142
+ when :evasion, :accuracy
143
+ STAGE_MULTIPLIERS_ACC_EVA
144
+ when :critical_hit
145
+ STAGE_MULTIPLIERS_CRITICAL_HIT
146
+ else
147
+ STAGE_MULTIPLIERS
148
+ end
156
149
  end
157
150
  end
158
151
  end