oakdex-battle 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.
@@ -0,0 +1,109 @@
1
+ module Oakdex
2
+ class Battle
3
+ # Calculates Pokemon Stats
4
+ class PokemonStat
5
+ STAGE_MULTIPLIERS = {
6
+ -6 => Rational(2, 8),
7
+ -5 => Rational(2, 7),
8
+ -4 => Rational(2, 6),
9
+ -3 => Rational(2, 5),
10
+ -2 => Rational(2, 4),
11
+ -1 => Rational(2, 3),
12
+ 0 => Rational(2, 2),
13
+ 1 => Rational(3, 2),
14
+ 2 => Rational(4, 2),
15
+ 3 => Rational(5, 2),
16
+ 4 => Rational(6, 2),
17
+ 5 => Rational(7, 2),
18
+ 6 => Rational(8, 2)
19
+ }
20
+
21
+ STAGE_MULTIPLIERS_CRITICAL_HIT = {
22
+ 0 => Rational(1, 24),
23
+ 1 => Rational(1, 8),
24
+ 2 => Rational(1, 2),
25
+ 3 => Rational(1, 1)
26
+ }
27
+
28
+ STAGE_MULTIPLIERS_ACC_EVA = {
29
+ -6 => Rational(3, 9),
30
+ -5 => Rational(3, 8),
31
+ -4 => Rational(3, 7),
32
+ -3 => Rational(3, 6),
33
+ -2 => Rational(3, 5),
34
+ -1 => Rational(3, 4),
35
+ 0 => Rational(3, 3),
36
+ 1 => Rational(4, 3),
37
+ 2 => Rational(5, 3),
38
+ 3 => Rational(6, 3),
39
+ 4 => Rational(7, 3),
40
+ 5 => Rational(8, 3),
41
+ 6 => Rational(9, 3)
42
+ }
43
+
44
+ class << self
45
+ def initial_stat(stat, options = {})
46
+ first_part = initial_stat_first_part(stat, options)
47
+ (
48
+ if stat == :hp
49
+ first_part + options[:level] + 10
50
+ elsif stat.to_s == options[:nature].increased_stat
51
+ (first_part + 5) * 1.1
52
+ elsif stat.to_s == options[:nature].decreased_stat
53
+ (first_part + 5) * 0.9
54
+ else
55
+ first_part + 5
56
+ end
57
+ ).to_i
58
+ end
59
+
60
+ def exp_by_level(leveling_rate, level)
61
+ case leveling_rate
62
+ when 'Fast' then ((4.0 * level**3) / 5).to_i
63
+ when 'Slow' then ((5.0 * level**3) / 4).to_i
64
+ when 'Medium Slow' then medium_slow_exp(level)
65
+ when 'Fluctuating' then fluctuating_exp(level)
66
+ else level**3
67
+ end
68
+ end
69
+
70
+ def level_by_exp(leveling_rate, exp)
71
+ level = 2
72
+ level += 1 while exp_by_level(leveling_rate, level) <= exp
73
+ level - 1
74
+ end
75
+
76
+ private
77
+
78
+ def medium_slow_exp(level)
79
+ (
80
+ ((6.0 / 5) * level**3) - 15 * level**2 + (100 * level) - 140
81
+ ).to_i
82
+ end
83
+
84
+ def fluctuating_exp(level)
85
+ (
86
+ if level <= 15
87
+ level**3 * ((((level + 1) / 3.0) + 24) / 50)
88
+ elsif level <= 36
89
+ level**3 * ((level + 14) / 50.0)
90
+ else
91
+ level**3 * (((level / 2.0) + 32) / 50)
92
+ end
93
+ ).to_i
94
+ end
95
+
96
+ def initial_stat_first_part(stat, options = {})
97
+ (
98
+ (
99
+ 2.0 *
100
+ options[:base_stats][stat.to_s] +
101
+ options[:iv][stat] +
102
+ (options[:ev][stat] / 4)
103
+ ) * options[:level]
104
+ ) / 100
105
+ end
106
+ end
107
+ end
108
+ end
109
+ end
@@ -0,0 +1,76 @@
1
+ require 'forwardable'
2
+
3
+ module Oakdex
4
+ class Battle
5
+ # Represents a side in an active battle, has n trainers
6
+ class Side
7
+ extend Forwardable
8
+
9
+ def_delegators :@battle, :add_to_log
10
+
11
+ attr_reader :trainers, :battle
12
+
13
+ def initialize(battle, trainers)
14
+ @battle = battle
15
+ @trainers = trainers
16
+ end
17
+
18
+ def next_position
19
+ left_position.first
20
+ end
21
+
22
+ def send_to_battle
23
+ @trainers.map do |trainer|
24
+ pokemon_per_trainer.times do |i|
25
+ break unless trainer.team[i]
26
+ trainer.send_to_battle(trainer.team[i], self)
27
+ end
28
+ end
29
+ end
30
+
31
+ def remove_fainted
32
+ @trainers.each(&:remove_fainted)
33
+ end
34
+
35
+ def trainer_on_side?(trainer)
36
+ @trainers.include?(trainer)
37
+ end
38
+
39
+ def pokemon_in_battle?(position)
40
+ in_battle_pokemon.any? do |ibp|
41
+ ibp.position == position
42
+ end
43
+ end
44
+
45
+ def pokemon_left?
46
+ !in_battle_pokemon.empty?
47
+ end
48
+
49
+ def in_battle_pokemon
50
+ @trainers.map(&:in_battle_pokemon).flatten(1)
51
+ end
52
+
53
+ def fainted?
54
+ @trainers.all?(&:fainted?)
55
+ end
56
+
57
+ private
58
+
59
+ def pokemon_per_trainer
60
+ (battle.pokemon_per_side / trainers.size)
61
+ end
62
+
63
+ def left_position
64
+ all_position - taken_positions
65
+ end
66
+
67
+ def taken_positions
68
+ in_battle_pokemon.map(&:position).sort
69
+ end
70
+
71
+ def all_position
72
+ battle.pokemon_per_side.times.to_a
73
+ end
74
+ end
75
+ end
76
+ end
@@ -0,0 +1,16 @@
1
+ require 'oakdex/battle/status_conditions/base'
2
+ require 'oakdex/battle/status_conditions/non_volatile'
3
+ require 'oakdex/battle/status_conditions/poison'
4
+ require 'oakdex/battle/status_conditions/burn'
5
+ require 'oakdex/battle/status_conditions/freeze'
6
+ require 'oakdex/battle/status_conditions/paralysis'
7
+ require 'oakdex/battle/status_conditions/badly_poisoned'
8
+ require 'oakdex/battle/status_conditions/sleep'
9
+
10
+ module Oakdex
11
+ class Battle
12
+ # Status conditions that belong to a pokemon
13
+ module StatusConditions
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,36 @@
1
+ module Oakdex
2
+ class Battle
3
+ module StatusConditions
4
+ # Represents BadlyPoisoned status condition
5
+ class BadlyPoisoned < NonVolatile
6
+ def initialize(pokemon)
7
+ super
8
+ @turn_count = 0
9
+ end
10
+
11
+ def after_turn(turn)
12
+ return if pokemon.current_hp.zero?
13
+ turn.battle.add_to_log('damage_by_badly_poisoned',
14
+ pokemon.trainer.name,
15
+ pokemon.name, hp_by_turn)
16
+ pokemon.change_hp_by(hp_by_turn)
17
+ @turn_count += 1
18
+ end
19
+
20
+ def after_switched_out(_battle)
21
+ @turn_count = 0
22
+ end
23
+
24
+ private
25
+
26
+ def hp_by_turn
27
+ [-(pokemon.hp * percent).to_i, -1].min
28
+ end
29
+
30
+ def percent
31
+ ([@turn_count.to_f, 15].min + 1) / 16.0
32
+ end
33
+ end
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,36 @@
1
+ module Oakdex
2
+ class Battle
3
+ module StatusConditions
4
+ # Represents Abstract Class Base
5
+ class Base
6
+ attr_reader :pokemon
7
+
8
+ def initialize(pokemon)
9
+ @pokemon = pokemon
10
+ end
11
+
12
+ def before_turn(turn); end
13
+
14
+ def after_turn(turn); end
15
+
16
+ def after_fainted(battle); end
17
+
18
+ def after_switched_out(battle); end
19
+
20
+ def after_received_damage(move_execution); end
21
+
22
+ def stat_modifier(_stat)
23
+ 1.0
24
+ end
25
+
26
+ def damage_modifier(_move_execution)
27
+ 1.0
28
+ end
29
+
30
+ def prevents_move?(_move_execution)
31
+ false
32
+ end
33
+ end
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,26 @@
1
+ module Oakdex
2
+ class Battle
3
+ module StatusConditions
4
+ # Represents Burn status condition
5
+ class Burn < NonVolatile
6
+ def after_turn(turn)
7
+ return if pokemon.current_hp.zero?
8
+ turn.battle.add_to_log('damage_by_burn',
9
+ pokemon.trainer.name,
10
+ pokemon.name, hp_by_turn)
11
+ pokemon.change_hp_by(hp_by_turn)
12
+ end
13
+
14
+ def damage_modifier(move_execution)
15
+ move_execution.move.category == 'physical' ? 0.5 : super
16
+ end
17
+
18
+ private
19
+
20
+ def hp_by_turn
21
+ [-(pokemon.hp / 16).to_i, -1].min
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,36 @@
1
+ module Oakdex
2
+ class Battle
3
+ module StatusConditions
4
+ # Represents Freeze status condition
5
+ class Freeze < NonVolatile
6
+ def prevents_move?(move_execution)
7
+ move_execution
8
+ .battle
9
+ .add_to_log('frozen',
10
+ move_execution.pokemon.trainer.name,
11
+ move_execution.pokemon.name)
12
+ true
13
+ end
14
+
15
+ def after_received_damage(move_execution)
16
+ return unless move_execution.move.type == 'fire'
17
+ defrost(move_execution.battle)
18
+ end
19
+
20
+ def before_turn(turn)
21
+ return unless rand(1..100) <= 20
22
+ defrost(turn.battle)
23
+ end
24
+
25
+ private
26
+
27
+ def defrost(battle)
28
+ pokemon.remove_status_condition(self)
29
+ battle.add_to_log('defrosts',
30
+ pokemon.trainer.name,
31
+ pokemon.name)
32
+ end
33
+ end
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,12 @@
1
+ module Oakdex
2
+ class Battle
3
+ module StatusConditions
4
+ # Represents Abstract Class Base NonVolatile
5
+ class NonVolatile < Base
6
+ def after_fainted(_battle)
7
+ pokemon.remove_status_condition(self)
8
+ end
9
+ end
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,26 @@
1
+ module Oakdex
2
+ class Battle
3
+ module StatusConditions
4
+ # Represents Paralysis status condition
5
+ class Paralysis < NonVolatile
6
+ def stat_modifier(stat)
7
+ return 0.5 if stat == :speed
8
+ super
9
+ end
10
+
11
+ def prevents_move?(move_execution)
12
+ if rand(1..100) <= 25
13
+ move_execution
14
+ .battle
15
+ .add_to_log('paralysed',
16
+ move_execution.pokemon.trainer.name,
17
+ move_execution.pokemon.name)
18
+ true
19
+ else
20
+ false
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,22 @@
1
+ module Oakdex
2
+ class Battle
3
+ module StatusConditions
4
+ # Represents Poison status condition
5
+ class Poison < NonVolatile
6
+ def after_turn(turn)
7
+ return if pokemon.current_hp.zero?
8
+ turn.battle.add_to_log('damage_by_poison',
9
+ pokemon.trainer.name,
10
+ pokemon.name, hp_by_turn)
11
+ pokemon.change_hp_by(hp_by_turn)
12
+ end
13
+
14
+ private
15
+
16
+ def hp_by_turn
17
+ [-(pokemon.hp / 8).to_i, -1].min
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,37 @@
1
+ module Oakdex
2
+ class Battle
3
+ module StatusConditions
4
+ # Represents Sleep status condition
5
+ class Sleep < NonVolatile
6
+ def initialize(pokemon)
7
+ super
8
+ @turn_count = 0
9
+ @max_turn_count = rand(1..3)
10
+ end
11
+
12
+ def after_turn(turn)
13
+ wake_up(turn.battle) if @turn_count >= @max_turn_count
14
+ @turn_count += 1
15
+ end
16
+
17
+ def prevents_move?(move_execution)
18
+ move_execution
19
+ .battle
20
+ .add_to_log('sleeping',
21
+ pokemon.trainer.name,
22
+ pokemon.name)
23
+ true
24
+ end
25
+
26
+ private
27
+
28
+ def wake_up(battle)
29
+ pokemon.remove_status_condition(self)
30
+ battle.add_to_log('wake_up',
31
+ pokemon.trainer.name,
32
+ pokemon.name)
33
+ end
34
+ end
35
+ end
36
+ end
37
+ end