ippa-chingu 0.4.8 → 0.5
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.
- data/History.txt +5 -0
- data/README.rdoc +43 -23
- data/chingu.gemspec +4 -4
- data/examples/example1.rb +2 -0
- data/examples/example2.rb +63 -33
- data/examples/example4.rb +16 -3
- data/examples/example5.rb +3 -3
- data/examples/example6.rb +1 -40
- data/examples/example7.rb +9 -8
- data/examples/example8.rb +109 -0
- data/examples/example9.rb +83 -0
- data/lib/chingu.rb +17 -2
- data/lib/chingu/actor.rb +15 -0
- data/lib/chingu/animation.rb +27 -2
- data/lib/chingu/basic_game_object.rb +140 -0
- data/lib/chingu/core_extensions.rb +15 -5
- data/lib/chingu/effects.rb +1 -1
- data/lib/chingu/fpscounter.rb +0 -1
- data/lib/chingu/game_object.rb +85 -155
- data/lib/chingu/game_state.rb +4 -3
- data/lib/chingu/game_state_manager.rb +3 -4
- data/lib/chingu/game_states/debug.rb +43 -0
- data/lib/chingu/game_states/fade_to.rb +2 -2
- data/lib/chingu/helpers.rb +2 -0
- data/lib/chingu/parallax.rb +2 -2
- data/lib/chingu/particle.rb +5 -4
- data/lib/chingu/traits/collision_detection.rb +33 -0
- data/lib/chingu/traits/deprecated_module_visual.rb +108 -0
- data/lib/chingu/traits/deprecated_visual.rb +100 -0
- data/lib/chingu/traits/effect.rb +80 -0
- data/lib/chingu/traits/input.rb +15 -0
- data/lib/chingu/traits/velocity.rb +59 -0
- data/lib/chingu/window.rb +18 -3
- metadata +16 -4
data/examples/example6.rb
CHANGED
@@ -54,43 +54,4 @@ class State2 < Chingu::GameState
|
|
54
54
|
end
|
55
55
|
end
|
56
56
|
|
57
|
-
Game.new.show
|
58
|
-
|
59
|
-
#
|
60
|
-
# The has now become premade game state shippet with Chingu.
|
61
|
-
# See chingu\game_states\fade_to.rb
|
62
|
-
#
|
63
|
-
#class FadeTo < Chingu::GameState
|
64
|
-
# def initialize(game_state)
|
65
|
-
# @new_game_state = game_state
|
66
|
-
# end
|
67
|
-
#
|
68
|
-
# def setup
|
69
|
-
# @color = Gosu::Color.new(0,0,0,0)
|
70
|
-
# @alpha = 0.0
|
71
|
-
# @fading_in = false
|
72
|
-
# end
|
73
|
-
#
|
74
|
-
# def update(dt)
|
75
|
-
# @alpha += (@fading_in ? -2 : 2)
|
76
|
-
# if @alpha >= 255
|
77
|
-
# @fading_in = true
|
78
|
-
# else
|
79
|
-
# @color.alpha = @alpha.to_i
|
80
|
-
# end
|
81
|
-
# end
|
82
|
-
#
|
83
|
-
# def draw
|
84
|
-
# previous_game_state.draw if @fading_in == false
|
85
|
-
# @new_game_state.draw if @fading_in == true
|
86
|
-
#
|
87
|
-
# $window.draw_quad( 0,0,@color,
|
88
|
-
# $window.width,0,@color,
|
89
|
-
# $window.width,$window.height,@color,
|
90
|
-
# 0,$window.height,@color,999)
|
91
|
-
#
|
92
|
-
# if @fading_in == true && @alpha == 0
|
93
|
-
# switch_game_state(@new_game_state)
|
94
|
-
# end
|
95
|
-
# end
|
96
|
-
#end
|
57
|
+
Game.new.show
|
data/examples/example7.rb
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
require 'rubygems'
|
2
2
|
require File.join(File.dirname($0), "..", "lib", "chingu")
|
3
3
|
include Gosu
|
4
|
+
include Chingu
|
4
5
|
|
5
6
|
#
|
6
7
|
# GFXHelpers example - demonstrating Chingus GFX
|
@@ -78,20 +79,20 @@ class Particles < Chingu::GameState
|
|
78
79
|
@yellow = Color.new(0xFFF9F120)
|
79
80
|
|
80
81
|
# Thanks jsb in #gosu of Encave-fame for fireball.png :)
|
81
|
-
@fireball_animation = Animation.new(:file => media_path("fireball.png"), :width => 32, :height => 32)
|
82
|
+
@fireball_animation = Chingu::Animation.new(:file => media_path("fireball.png"), :width => 32, :height => 32)
|
82
83
|
@ground_y = $window.height * 0.95
|
83
84
|
end
|
84
85
|
|
85
|
-
def update
|
86
|
+
def update
|
86
87
|
#
|
87
88
|
# Fire 1. Dies quickly (big :fade). Small in size (small :zoom)
|
88
89
|
#
|
89
90
|
Chingu::Particle.new( :x => 100,
|
90
91
|
:y => @ground_y,
|
91
92
|
:animation => @fireball_animation,
|
92
|
-
:
|
93
|
-
:
|
94
|
-
:
|
93
|
+
:zooming => +0.05,
|
94
|
+
:fading => -10,
|
95
|
+
:rotating => +1,
|
95
96
|
:mode => :default
|
96
97
|
)
|
97
98
|
|
@@ -101,9 +102,9 @@ class Particles < Chingu::GameState
|
|
101
102
|
Chingu::Particle.new( :x => 300,
|
102
103
|
:y => @ground_y,
|
103
104
|
:animation => @fireball_animation,
|
104
|
-
:
|
105
|
-
:
|
106
|
-
:
|
105
|
+
:zooming => +0.2,
|
106
|
+
:fading => -4,
|
107
|
+
:rotating => +3,
|
107
108
|
:mode => :default
|
108
109
|
)
|
109
110
|
#
|
@@ -0,0 +1,109 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require File.join(File.dirname($0), "..", "lib", "chingu")
|
3
|
+
include Gosu
|
4
|
+
include Chingu
|
5
|
+
|
6
|
+
#
|
7
|
+
# Demonstrating domponents "velocity" and "effect"
|
8
|
+
#
|
9
|
+
class Game < Chingu::Window
|
10
|
+
def initialize
|
11
|
+
super(640,400)
|
12
|
+
self.input = {:esc => :exit}
|
13
|
+
self.caption = "Example of game object traits 'velocity' and 'effect'"
|
14
|
+
push_game_state(Particles)
|
15
|
+
puts RUBY_VERSION
|
16
|
+
end
|
17
|
+
|
18
|
+
def next_effect; pop_game_state; end
|
19
|
+
end
|
20
|
+
|
21
|
+
class Plasma < Chingu::GameObject
|
22
|
+
has_traits :velocity, :effect
|
23
|
+
|
24
|
+
def initialize(options)
|
25
|
+
super
|
26
|
+
@image = Image["particle.png"]
|
27
|
+
@mode = :additive
|
28
|
+
|
29
|
+
# initialize with a rightwards velocity with some damping to look more realistic
|
30
|
+
@velocity_x = options[:velocity_x] || 10
|
31
|
+
@acceleration_x = -0.1
|
32
|
+
|
33
|
+
# Simulate gravity
|
34
|
+
@acceleration_y = 0.4
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
class Particles < Chingu::GameState
|
39
|
+
def setup
|
40
|
+
@color1 = Color.new(0xFFFFEA02)
|
41
|
+
@color2 = Color.new(0xFF078B20)
|
42
|
+
|
43
|
+
#
|
44
|
+
# +1 fps
|
45
|
+
#
|
46
|
+
#@ground_y = $window.height * 0.95
|
47
|
+
@ground_y = ($window.height * 0.95).to_i
|
48
|
+
end
|
49
|
+
|
50
|
+
def update
|
51
|
+
|
52
|
+
#
|
53
|
+
# old velocity.rb 350 particles, 49 fps
|
54
|
+
# first optimization: 490 particles, 47 fps (350 @ 60)
|
55
|
+
# optimized GameObject if/elsif: 490 particles, 50 fps
|
56
|
+
#
|
57
|
+
Plasma.new(:x => 0, :y => 0 + rand(5), :color => Color.new(0xFF86EFFF), :velocity_x => 10)
|
58
|
+
Plasma.new(:x => 0, :y => 50 + rand(5), :color => Color.new(0xFF86EFFF), :velocity_x => 14)
|
59
|
+
Plasma.new(:x => 0, :y => 100 + rand(5), :color => Color.new(0xFF86EFFF), :velocity_x => 7)
|
60
|
+
Plasma.new(:x => 0, :y => 200 + rand(5), :color => Color.new(0xFF86EFFF), :velocity_x => 6)
|
61
|
+
|
62
|
+
Plasma.all.each do |particle|
|
63
|
+
#
|
64
|
+
# +1 fps
|
65
|
+
#
|
66
|
+
# particle.x += 1 - rand(2)
|
67
|
+
# -just removed, not replaced-
|
68
|
+
|
69
|
+
#
|
70
|
+
# If particle hits the ground:
|
71
|
+
#
|
72
|
+
if particle.y >= @ground_y
|
73
|
+
|
74
|
+
# 1) "Bounce" it up particle by reversing velocity_y with damping
|
75
|
+
slower = particle.velocity_y/3
|
76
|
+
particle.velocity_y = -(slower + rand(slower))
|
77
|
+
|
78
|
+
# 2) "Bounce" it randomly to left and right
|
79
|
+
if rand(2) == 0
|
80
|
+
particle.velocity_x = particle.velocity_y/2 + rand(2) # Randomr.randomr / 50
|
81
|
+
particle.acceleration_x = -0.02
|
82
|
+
else
|
83
|
+
particle.velocity_x = -particle.velocity_y/2 - rand(2) # Randomr.randomr / 50
|
84
|
+
particle.acceleration_x = 0.02
|
85
|
+
end
|
86
|
+
|
87
|
+
# 3) Start fading the alphachannel
|
88
|
+
particle.fading = -3
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
#
|
93
|
+
# +4 fps
|
94
|
+
#
|
95
|
+
#self.game_objects.reject! { |object| object.outside_window? || object.color.alpha == 0 }
|
96
|
+
self.game_objects.reject! { |object| object.color.alpha == 0 }
|
97
|
+
|
98
|
+
super
|
99
|
+
end
|
100
|
+
|
101
|
+
def draw
|
102
|
+
$window.caption = "particle example (esc to quit) [particles#: #{game_objects.size} - framerate: #{$window.fps}]"
|
103
|
+
fill_gradient(:from => Color.new(255,0,0,0), :to => Color.new(255,60,60,80), :rect => [0,0,$window.width,@ground_y])
|
104
|
+
fill_gradient(:from => Color.new(255,100,100,100), :to => Color.new(255,50,50,50), :rect => [0,@ground_y,$window.width,$window.height-@ground_y])
|
105
|
+
super
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
Game.new.show
|
@@ -0,0 +1,83 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require File.join(File.dirname($0), "..", "lib", "chingu")
|
3
|
+
include Gosu
|
4
|
+
include Chingu
|
5
|
+
|
6
|
+
#
|
7
|
+
# Demonstrating domponents "velocity" and "effect"
|
8
|
+
#
|
9
|
+
class Game < Chingu::Window
|
10
|
+
def initialize
|
11
|
+
super(800,800)
|
12
|
+
self.input = {:esc => :exit}
|
13
|
+
self.caption = "Example of game object traits 'velocity' and 'effect'"
|
14
|
+
push_game_state(Particles)
|
15
|
+
end
|
16
|
+
|
17
|
+
def next_effect; pop_game_state; end
|
18
|
+
end
|
19
|
+
|
20
|
+
class FireCube < Chingu::GameObject
|
21
|
+
has_traits :velocity, :effect
|
22
|
+
|
23
|
+
def initialize(options)
|
24
|
+
super
|
25
|
+
@mode = :additive
|
26
|
+
|
27
|
+
# initialize with a rightwards velocity with some damping to look more realistic
|
28
|
+
@velocity_x = options[:velocity_x] || 3 - rand(6)
|
29
|
+
@velocity_y = options[:velocity_y] || 3 - rand(6)
|
30
|
+
@rect = Rect.new([@x, @y, 20, 20])
|
31
|
+
end
|
32
|
+
|
33
|
+
def update
|
34
|
+
super
|
35
|
+
@rect.x = @x
|
36
|
+
@rect.y = @y
|
37
|
+
end
|
38
|
+
|
39
|
+
def draw
|
40
|
+
$window.fill_rect(@rect, Color.new(@color.alpha,100,255,255))
|
41
|
+
end
|
42
|
+
|
43
|
+
end
|
44
|
+
|
45
|
+
class Particles < Chingu::GameState
|
46
|
+
def setup
|
47
|
+
self.input = { :space => :new_fire_cube }
|
48
|
+
100.times { new_fire_cube }
|
49
|
+
end
|
50
|
+
|
51
|
+
def new_fire_cube
|
52
|
+
FireCube.new(:x => rand($window.width), :y => rand($window.height))
|
53
|
+
end
|
54
|
+
|
55
|
+
def update
|
56
|
+
FireCube.all.each do |particle|
|
57
|
+
if particle.x < 0 || particle.x > $window.width
|
58
|
+
particle.velocity_x = -particle.velocity_x
|
59
|
+
end
|
60
|
+
|
61
|
+
if particle.y < 0 || particle.y > $window.height
|
62
|
+
particle.velocity_y = -particle.velocity_y
|
63
|
+
end
|
64
|
+
end
|
65
|
+
#FireCube.all.each do |particle|
|
66
|
+
# FireCube.all.each do |particle2|
|
67
|
+
# if particle.rect.collides_with
|
68
|
+
#
|
69
|
+
# end
|
70
|
+
#end
|
71
|
+
|
72
|
+
self.game_objects.reject! { |object| object.color.alpha == 0 }
|
73
|
+
|
74
|
+
super
|
75
|
+
end
|
76
|
+
|
77
|
+
def draw
|
78
|
+
$window.caption = "particle example (esc to quit) [particles#: #{game_objects.size} - framerate: #{$window.fps}]"
|
79
|
+
super
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
Game.new.show
|
data/lib/chingu.rb
CHANGED
@@ -1,13 +1,27 @@
|
|
1
1
|
#
|
2
2
|
#
|
3
3
|
#
|
4
|
-
|
4
|
+
unless RUBY_VERSION =~ /1\.9/
|
5
|
+
require 'rubygems'
|
6
|
+
end
|
5
7
|
require 'gosu'
|
6
8
|
require 'set'
|
7
9
|
|
10
|
+
%w{ collision_detection
|
11
|
+
effect
|
12
|
+
velocity
|
13
|
+
input
|
14
|
+
}.each do |lib|
|
15
|
+
root ||= File.dirname(File.expand_path(__FILE__))
|
16
|
+
require File.join(root,"chingu","components",lib)
|
17
|
+
end
|
18
|
+
|
8
19
|
%w{ helpers
|
9
20
|
gfx_helpers
|
21
|
+
core_extensions
|
22
|
+
basic_game_object
|
10
23
|
game_object
|
24
|
+
actor
|
11
25
|
effects
|
12
26
|
game_state_manager
|
13
27
|
game_state
|
@@ -28,11 +42,12 @@ require 'set'
|
|
28
42
|
|
29
43
|
%w{ pause
|
30
44
|
fade_to
|
45
|
+
debug
|
31
46
|
}.each do |lib|
|
32
47
|
root ||= File.dirname(File.expand_path(__FILE__))
|
33
48
|
require File.join(root,"chingu","game_states",lib)
|
34
49
|
end
|
35
50
|
|
36
51
|
module Chingu
|
37
|
-
VERSION = "0.4.
|
52
|
+
VERSION = "0.4.9"
|
38
53
|
end
|
data/lib/chingu/actor.rb
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
module Chingu
|
2
|
+
#
|
3
|
+
# A game object class with most components included, nice for quick prototypes
|
4
|
+
#
|
5
|
+
class Actor < Chingu::GameObject
|
6
|
+
#add_component :visual, :effect, :input
|
7
|
+
|
8
|
+
#def initialize(options = {})
|
9
|
+
# super
|
10
|
+
# visual_setup(options)
|
11
|
+
# effect_setup(options)
|
12
|
+
# input_setup(options)
|
13
|
+
#end
|
14
|
+
end
|
15
|
+
end
|
data/lib/chingu/animation.rb
CHANGED
@@ -30,16 +30,39 @@ module Chingu
|
|
30
30
|
@delay = options[:delay]
|
31
31
|
@dt = 0
|
32
32
|
|
33
|
+
if options[:size]
|
34
|
+
@width = options[:size][0]
|
35
|
+
@height = options[:size][0]
|
36
|
+
end
|
37
|
+
|
33
38
|
@frame_actions = []
|
34
39
|
@frames = Gosu::Image.load_tiles($window, @file, @width, @height, true)
|
35
40
|
@step = 1
|
36
41
|
end
|
37
42
|
|
38
43
|
#
|
39
|
-
#
|
44
|
+
# Returns first frame (GOSU::Image) from animation
|
45
|
+
#
|
46
|
+
def first
|
47
|
+
@frames.first
|
48
|
+
end
|
49
|
+
|
50
|
+
#
|
51
|
+
# Returns last frame (GOSU::Image) from animation
|
52
|
+
#
|
53
|
+
def last
|
54
|
+
@frames.first
|
55
|
+
end
|
56
|
+
|
57
|
+
#
|
58
|
+
# Fetch a frame or frames:
|
59
|
+
#
|
60
|
+
# @animation[0] # returns first frame
|
61
|
+
# @animation[0..2] # returns a new Animation-instance with first, second and third frame
|
40
62
|
#
|
41
63
|
def [](index)
|
42
|
-
@frames[index]
|
64
|
+
return @frames[index] if index.is_a? (Fixnum)
|
65
|
+
return self.new_from_frames(index) if index.is_a? (Range)
|
43
66
|
end
|
44
67
|
|
45
68
|
#
|
@@ -51,9 +74,11 @@ module Chingu
|
|
51
74
|
|
52
75
|
#
|
53
76
|
# Resets the animation, re-starts it at frame 0
|
77
|
+
# returns itself.
|
54
78
|
#
|
55
79
|
def reset!
|
56
80
|
@index = 0
|
81
|
+
self
|
57
82
|
end
|
58
83
|
|
59
84
|
#
|
@@ -0,0 +1,140 @@
|
|
1
|
+
module Chingu
|
2
|
+
#
|
3
|
+
# BasicGameObject. Resonating with 1.9.1, this is our most basic class that all game objects ultimate should build on.
|
4
|
+
#
|
5
|
+
# All objects that inherits from this class will by default be automaticly be updated and drawn.
|
6
|
+
# It will also acts as a container for the component-system of chingu.
|
7
|
+
#
|
8
|
+
class BasicGameObject
|
9
|
+
attr_reader :options, :parent
|
10
|
+
|
11
|
+
#
|
12
|
+
# Create class variable @components in every new class derived from GameObject
|
13
|
+
#
|
14
|
+
def self.inherited(subclass)
|
15
|
+
subclass.instance_variable_set("@components", Set.new)
|
16
|
+
end
|
17
|
+
|
18
|
+
class << self
|
19
|
+
attr_accessor :components
|
20
|
+
end
|
21
|
+
|
22
|
+
#
|
23
|
+
# adds a component to a certain game class
|
24
|
+
#
|
25
|
+
# Executes a ruby "include" the specified module
|
26
|
+
# and sets up update and draw hooks.
|
27
|
+
#
|
28
|
+
def self.add_component(*components)
|
29
|
+
Array(components).each do |component|
|
30
|
+
|
31
|
+
if component.is_a?(::Symbol) || component.is_a?(::String)
|
32
|
+
string = "Chingu::Components::#{component.to_s.downcase.capitalize}"
|
33
|
+
klass_or_module = eval(string)
|
34
|
+
|
35
|
+
if klass_or_module.is_a?(Class)
|
36
|
+
component = klass_or_module.new(self, {})
|
37
|
+
@components << component
|
38
|
+
elsif klass_or_module.is_a?(Module)
|
39
|
+
include klass_or_module
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
|
46
|
+
#
|
47
|
+
# BasicGameUnit initialize
|
48
|
+
#
|
49
|
+
# - caches component methods for fast calls later on
|
50
|
+
# - call .setup() on components that implements it
|
51
|
+
# - adds game object to correct game state or $window if no game state exists
|
52
|
+
#
|
53
|
+
def initialize(options = {})
|
54
|
+
@options = options
|
55
|
+
setupable_components
|
56
|
+
updateable_components
|
57
|
+
drawable_components
|
58
|
+
|
59
|
+
@setupable_components.each { |c| c.setup(self, options) }
|
60
|
+
|
61
|
+
#
|
62
|
+
# A GameObject can either belong to a GameState or our mainwindow ($window)
|
63
|
+
# .. or live in limbo with manual updates
|
64
|
+
#
|
65
|
+
if $window && $window.respond_to?(:game_state_manager)
|
66
|
+
@parent = $window.game_state_manager.inside_state || $window
|
67
|
+
@parent.add_game_object(self) if @parent
|
68
|
+
end
|
69
|
+
|
70
|
+
end
|
71
|
+
|
72
|
+
#
|
73
|
+
# Get all components
|
74
|
+
#
|
75
|
+
def components; self.class.components || []; end
|
76
|
+
|
77
|
+
def setupable_components
|
78
|
+
@setupable_components ||= components.select { |c| c.respond_to?(:setup) }
|
79
|
+
end
|
80
|
+
def updateable_components
|
81
|
+
@updateable_components ||= components.select { |c| c.respond_to?(:update) }
|
82
|
+
end
|
83
|
+
def drawable_components
|
84
|
+
@drawable_components ||= components.select { |c| c.respond_to?(:draw) }
|
85
|
+
end
|
86
|
+
|
87
|
+
#
|
88
|
+
# Call .update on all components that implements it
|
89
|
+
#
|
90
|
+
def update
|
91
|
+
@updateable_components.each { |c| c.update(self) }
|
92
|
+
end
|
93
|
+
|
94
|
+
#
|
95
|
+
# Call .draw on all components that implements it
|
96
|
+
#
|
97
|
+
def draw
|
98
|
+
@drawable_components.each { |c| c.draw(self) }
|
99
|
+
end
|
100
|
+
|
101
|
+
|
102
|
+
#
|
103
|
+
# Fetch all objects of a current class.
|
104
|
+
# Bullet.all # => Enumerator of all objects of class Bullet
|
105
|
+
#
|
106
|
+
# NOTE: ObjectSpace doesn't play nice with jruby.
|
107
|
+
#
|
108
|
+
def self.all
|
109
|
+
ObjectSpace.each_object(self)
|
110
|
+
end
|
111
|
+
|
112
|
+
#
|
113
|
+
# Destroy all instances of current class that fills a certain condition
|
114
|
+
# Enemy.destroy_if(&:dead?) # Assumes Enemy.dead? returns true/false depending on aliveness :)
|
115
|
+
#
|
116
|
+
#
|
117
|
+
def self.destroy_if(&block)
|
118
|
+
all.each do |object|
|
119
|
+
object.destroy! if yield(object)
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
#
|
124
|
+
# Clear all intances of objects class:
|
125
|
+
# Bullet.clear # Removes all Bullet objects from the game
|
126
|
+
#
|
127
|
+
def self.clear
|
128
|
+
all.each { |object| object.destroy! }
|
129
|
+
end
|
130
|
+
|
131
|
+
#
|
132
|
+
# Removes object from the update cycle and freezes the object to prevent further modifications.
|
133
|
+
# If the object isn't being managed by Chingu (ie. you're doing manual update/draw calls) the object is only frozen, not removed from any updae cycle (because you are controlling that).
|
134
|
+
#
|
135
|
+
def destroy!
|
136
|
+
@parent.remove_game_object(self) if @parent
|
137
|
+
self.freeze
|
138
|
+
end
|
139
|
+
end
|
140
|
+
end
|