chingu 0.7.0 → 0.7.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 +1 -1
- data/README.rdoc +110 -49
- data/benchmarks/game_objects_benchmark.rb +50 -0
- data/chingu.gemspec +24 -12
- data/examples/example10_traits_retrofy.rb +6 -4
- data/examples/example11_animation.rb +14 -23
- data/examples/example12_trait_timer.rb +1 -1
- data/examples/example13_high_scores.rb +1 -1
- data/examples/example14_bounding_box_circle.rb +6 -10
- data/examples/example15_trait_timer2.rb +1 -1
- data/examples/example16_online_high_scores.rb +1 -1
- data/examples/example17_gosu_tutorial.rb +4 -4
- data/examples/example18_animation_trait.rb +98 -0
- data/examples/example19_edit_viewport.rb +223 -0
- data/examples/example19_game_objects.yml +1591 -0
- data/examples/example20_trait_inheritence_test.rb +58 -0
- data/examples/example3_parallax.rb +1 -1
- data/examples/example4_gamestates.rb +1 -1
- data/examples/example7_gfx_helpers.rb +14 -9
- data/examples/example8_traits.rb +4 -4
- data/examples/example9_collision_detection.rb +1 -1
- data/examples/game1.rb +33 -38
- data/examples/game_of_life.rb +291 -0
- data/examples/media/droid_11x15.bmp +0 -0
- data/examples/media/droid_11x15.gal +0 -0
- data/examples/media/heli.bmp +0 -0
- data/examples/media/heli.gal +0 -0
- data/examples/media/star_25x25_default.png +0 -0
- data/examples/media/star_25x25_explode.gal +0 -0
- data/examples/media/star_25x25_explode.png +0 -0
- data/examples/media/stone_wall.bmp +0 -0
- data/lib/chingu.rb +1 -1
- data/lib/chingu/animation.rb +78 -9
- data/lib/chingu/basic_game_object.rb +16 -8
- data/lib/chingu/game_object.rb +36 -7
- data/lib/chingu/game_object_list.rb +20 -3
- data/lib/chingu/game_state.rb +8 -7
- data/lib/chingu/game_states/edit.rb +177 -90
- data/lib/chingu/helpers/class_inheritable_accessor.rb +12 -5
- data/lib/chingu/helpers/game_object.rb +45 -4
- data/lib/chingu/helpers/gfx.rb +150 -172
- data/lib/chingu/helpers/input_client.rb +7 -0
- data/lib/chingu/inflector.rb +16 -2
- data/lib/chingu/traits/animation.rb +84 -0
- data/lib/chingu/traits/bounding_box.rb +16 -3
- data/lib/chingu/traits/bounding_circle.rb +18 -4
- data/lib/chingu/traits/collision_detection.rb +10 -1
- data/lib/chingu/traits/velocity.rb +26 -3
- data/lib/chingu/traits/viewport.rb +10 -9
- data/lib/chingu/viewport.rb +103 -22
- data/lib/chingu/window.rb +8 -2
- metadata +46 -16
- data/examples/example18_viewport.rb +0 -173
- data/examples/media/city1.csv +0 -2
- data/examples/media/plane.csv +0 -2
- data/examples/media/saucer.csv +0 -4
- data/examples/media/stickfigure.bmp +0 -0
- data/examples/media/stickfigure.png +0 -0
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
data/lib/chingu.rb
CHANGED
data/lib/chingu/animation.rb
CHANGED
@@ -37,20 +37,69 @@ module Chingu
|
|
37
37
|
@step = options[:step] || 1
|
38
38
|
@dt = 0
|
39
39
|
|
40
|
+
@sub_animations = {}
|
41
|
+
@frame_actions = []
|
42
|
+
@file = media_path(@file) if @file && !File.exist?(@file)
|
43
|
+
|
44
|
+
#
|
45
|
+
# Various ways of determening the framesize
|
46
|
+
#
|
40
47
|
if options[:size] && options[:size].is_a?(Array)
|
41
48
|
@width = options[:size][0]
|
42
49
|
@height = options[:size][1]
|
43
50
|
elsif options[:size]
|
44
51
|
@width = options[:size]
|
45
52
|
@height = options[:size]
|
53
|
+
elsif @file =~ /_(\d+)x(\d+)/
|
54
|
+
# Auto-detect width/height from filename
|
55
|
+
# Tilefile foo_10x25.png would mean frame width 10px and height 25px
|
56
|
+
@width = $1.to_i
|
57
|
+
@height = $2.to_i
|
58
|
+
else
|
59
|
+
# Assume the shortest side is the width/height for each frame
|
60
|
+
@image = Gosu::Image.new($window, @file)
|
61
|
+
@width = @height = (@image.width < @image.height) ? @image.width : @image.height
|
46
62
|
end
|
47
63
|
|
48
|
-
@file = media_path(@file) unless File.exist?(@file)
|
49
|
-
|
50
|
-
@frame_actions = []
|
51
64
|
@frames = Gosu::Image.load_tiles($window, @file, @width, @height, true)
|
52
65
|
end
|
53
66
|
|
67
|
+
#
|
68
|
+
# Remove transparent space from each frame so the actual sprite is touching the border of the image.
|
69
|
+
#
|
70
|
+
def trim
|
71
|
+
@frames.each do |frame|
|
72
|
+
# TODO!
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
#
|
77
|
+
# Put name on specific ranges of frames.
|
78
|
+
# Eg. name_frames(:default => 0..3, :explode => 3..8)
|
79
|
+
#
|
80
|
+
# Can then be accessed with @animation[:explode]
|
81
|
+
#
|
82
|
+
def frame_names=(names)
|
83
|
+
names.each do |key, value|
|
84
|
+
@sub_animations[key] = self.new_from_frames(value) if value.is_a? Range
|
85
|
+
#
|
86
|
+
# TODO: Add support for [1,4,5] array frame selection
|
87
|
+
#
|
88
|
+
# @frame_names[key] = self.new_from_frames(value) if value.is_a? Array
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
#
|
93
|
+
# Return frame names in { :name => range } -form
|
94
|
+
#
|
95
|
+
def frame_names
|
96
|
+
@frame_names
|
97
|
+
end
|
98
|
+
|
99
|
+
def animations
|
100
|
+
@sub_animations.keys
|
101
|
+
end
|
102
|
+
|
54
103
|
#
|
55
104
|
# Returns first frame (GOSU::Image) from animation
|
56
105
|
#
|
@@ -64,18 +113,37 @@ module Chingu
|
|
64
113
|
def last
|
65
114
|
@frames.last
|
66
115
|
end
|
116
|
+
|
117
|
+
#
|
118
|
+
# Returns true if the current frame is the last
|
119
|
+
#
|
120
|
+
def last_frame?
|
121
|
+
@previous_index == @index
|
122
|
+
end
|
67
123
|
|
68
124
|
#
|
69
125
|
# Fetch a frame or frames:
|
70
126
|
#
|
71
|
-
# @animation[0]
|
72
|
-
# @animation[0..2]
|
127
|
+
# @animation[0] # returns first frame
|
128
|
+
# @animation[0..2] # returns a new Animation-instance with first, second and third frame
|
129
|
+
# @animation[:explode] # returns a cached Animation-instance with frames earlier set with @animation.frame_names = { ... }
|
73
130
|
#
|
74
131
|
def [](index)
|
75
132
|
return @frames[index] if index.is_a?(Fixnum)
|
76
133
|
return self.new_from_frames(index) if index.is_a?(Range)
|
134
|
+
return @sub_animations[index] if index.is_a?(Symbol)
|
77
135
|
end
|
78
|
-
|
136
|
+
|
137
|
+
#
|
138
|
+
# Manually initialize a frame-range with an Animation-instance
|
139
|
+
#
|
140
|
+
# @animation[:scan] = Animation.new(...)
|
141
|
+
#
|
142
|
+
def []=(name, animation)
|
143
|
+
@sub_animations[name] = animation
|
144
|
+
return @sub_animations[name]
|
145
|
+
end
|
146
|
+
|
79
147
|
#
|
80
148
|
# Get the current frame (a Gosu#Image)
|
81
149
|
#
|
@@ -96,7 +164,7 @@ module Chingu
|
|
96
164
|
# Returns a new animation with the frames from the original animation.
|
97
165
|
# Specify which frames you want with "range", for example "0..3" for the 4 first frames.
|
98
166
|
#
|
99
|
-
def new_from_frames(range)
|
167
|
+
def new_from_frames(range)
|
100
168
|
new_animation = self.dup
|
101
169
|
new_animation.frames = []
|
102
170
|
range.each do |nr|
|
@@ -104,12 +172,13 @@ module Chingu
|
|
104
172
|
end
|
105
173
|
return new_animation
|
106
174
|
end
|
107
|
-
|
175
|
+
|
108
176
|
#
|
109
177
|
# Propelles the animation forward. Usually called in #update within the class which holds the animation.
|
110
178
|
# Animation#next() will look at bounce and loop flags to always return a correct frame (a Gosu#Image)
|
111
179
|
#
|
112
|
-
def next
|
180
|
+
def next(recursion = true)
|
181
|
+
|
113
182
|
if (@dt += $window.milliseconds_since_last_tick) > @delay
|
114
183
|
@dt = 0
|
115
184
|
@previous_index = @index
|
@@ -20,10 +20,10 @@ module Chingu
|
|
20
20
|
# Adds a trait or traits to a certain game class
|
21
21
|
# Executes a standard ruby "include" the specified module
|
22
22
|
#
|
23
|
-
def self.
|
23
|
+
def self.trait(trait, options = {})
|
24
24
|
|
25
25
|
if trait.is_a?(::Symbol) || trait.is_a?(::String)
|
26
|
-
## puts "
|
26
|
+
## puts "trait #{trait}, #{options}"
|
27
27
|
begin
|
28
28
|
# Convert user-given symbol (eg. :timer) to a Module (eg. Chingu::Traits::Timer)
|
29
29
|
mod = Chingu::Traits.const_get(Chingu::Inflector.camelize(trait))
|
@@ -39,7 +39,7 @@ module Chingu
|
|
39
39
|
extend mod2
|
40
40
|
|
41
41
|
# If the newly included trait has a initialize_trait method in the ClassMethods-scope:
|
42
|
-
# ... call it with the options provided with the
|
42
|
+
# ... call it with the options provided with the trait-line.
|
43
43
|
if mod2.method_defined?(:initialize_trait)
|
44
44
|
initialize_trait(options)
|
45
45
|
end
|
@@ -49,10 +49,17 @@ module Chingu
|
|
49
49
|
end
|
50
50
|
end
|
51
51
|
end
|
52
|
+
class << self; alias :has_trait :trait; end
|
52
53
|
|
53
|
-
def self.
|
54
|
-
Array(traits).each { |
|
54
|
+
def self.traits(*traits)
|
55
|
+
Array(traits).each { |trait_name| trait trait_name }
|
55
56
|
end
|
57
|
+
class << self; alias :has_traits :traits; end
|
58
|
+
|
59
|
+
#def self.inherited(subclass)
|
60
|
+
# subclass.initialize_inherited_trait if subclass.method_defined?(:initialize_inherited_trait)
|
61
|
+
#end
|
62
|
+
|
56
63
|
|
57
64
|
#
|
58
65
|
# BasicGameObject initialize
|
@@ -78,7 +85,7 @@ module Chingu
|
|
78
85
|
|
79
86
|
# This will call #setup_trait on the latest trait mixed in
|
80
87
|
# which then will pass it on to the next setup_trait() with a super-call.
|
81
|
-
setup_trait(options)
|
88
|
+
setup_trait(options)
|
82
89
|
end
|
83
90
|
|
84
91
|
#
|
@@ -90,8 +97,8 @@ module Chingu
|
|
90
97
|
# Chingus "game_objects" which is available in all game states and the main window.
|
91
98
|
#
|
92
99
|
#def self.create(options = {})
|
93
|
-
def self.create(*options)
|
94
|
-
instance = self.new(*options)
|
100
|
+
def self.create(*options, &block)
|
101
|
+
instance = self.new(*options, &block)
|
95
102
|
|
96
103
|
#
|
97
104
|
# Add to parents list of game objects
|
@@ -157,6 +164,7 @@ module Chingu
|
|
157
164
|
#
|
158
165
|
def self.initialize_trait(options); end
|
159
166
|
def setup_trait(options); end
|
167
|
+
def setup; end
|
160
168
|
def update_trait; end
|
161
169
|
def draw_trait; end
|
162
170
|
def update; end
|
data/lib/chingu/game_object.rb
CHANGED
@@ -39,19 +39,27 @@ module Chingu
|
|
39
39
|
|
40
40
|
def initialize(options = {})
|
41
41
|
super
|
42
|
-
|
43
|
-
#
|
42
|
+
|
43
|
+
#
|
44
|
+
# All encapsulated Gosu::Image.draw_rot arguments can be set with hash-options at creation time
|
45
|
+
#
|
44
46
|
if options[:image].is_a?(Gosu::Image)
|
45
47
|
@image = options[:image]
|
46
48
|
elsif options[:image].is_a? String
|
47
|
-
|
49
|
+
begin
|
50
|
+
# 1) Try loading the image the normal way
|
51
|
+
@image = Gosu::Image.new($window, options[:image])
|
52
|
+
rescue
|
53
|
+
# 2) Try looking up the picture using Chingus Image-cache
|
54
|
+
@image = Gosu::Image[options[:image]]
|
55
|
+
end
|
48
56
|
end
|
49
57
|
|
50
58
|
@x = options[:x] || 0
|
51
59
|
@y = options[:y] || 0
|
52
60
|
@angle = options[:angle] || 0
|
53
61
|
|
54
|
-
self.factor = options[:factor] || options[:scale] || 1.0
|
62
|
+
self.factor = options[:factor] || options[:scale] || $window.factor || 1.0
|
55
63
|
@factor_x = options[:factor_x] if options[:factor_x]
|
56
64
|
@factor_y = options[:factor_y] if options[:factor_y]
|
57
65
|
|
@@ -73,6 +81,12 @@ module Chingu
|
|
73
81
|
|
74
82
|
@mode = options[:mode] || :default # :additive is also available.
|
75
83
|
@zorder = options[:zorder] || 100
|
84
|
+
|
85
|
+
### super ## This crashes
|
86
|
+
# Call setup, this class holds an empty setup() to be overriden
|
87
|
+
# setup() will be an easier method to override for init-stuff since you don't need to do super etc..
|
88
|
+
setup
|
89
|
+
|
76
90
|
end
|
77
91
|
|
78
92
|
# Quick way of setting both factor_x and factor_y
|
@@ -117,7 +131,16 @@ module Chingu
|
|
117
131
|
def distance_to(object)
|
118
132
|
distance(self.x, self.y, object.x, object.y)
|
119
133
|
end
|
120
|
-
|
134
|
+
|
135
|
+
#
|
136
|
+
# Returns a filename-friendly string from the current class-name
|
137
|
+
#
|
138
|
+
# "FireBall" -> "fire_ball"
|
139
|
+
#
|
140
|
+
def filename
|
141
|
+
Chingu::Inflector.underscore(self.class.to_s)
|
142
|
+
end
|
143
|
+
|
121
144
|
#
|
122
145
|
# Our encapsulation of GOSU's image.draw_rot, uses the objects variables to draw it on screen if @visible is true
|
123
146
|
#
|
@@ -126,11 +149,17 @@ module Chingu
|
|
126
149
|
end
|
127
150
|
|
128
151
|
#
|
129
|
-
# Works as #draw() but takes offsets for all draw_rot()-arguments. Used among others by
|
152
|
+
# Works as #draw() but takes offsets for all draw_rot()-arguments. Used among others by the viewport-trait.
|
130
153
|
#
|
131
154
|
def draw_relative(x=0, y=0, zorder=0, angle=0, center_x=0, center_y=0, factor_x=0, factor_y=0)
|
132
155
|
@image.draw_rot(@x+x, @y+y, @zorder+zorder, @angle+angle, @center_x+center_x, @center_y+center_y, @factor_x+factor_x, @factor_y+factor_y, @color, @mode) if @visible
|
133
156
|
end
|
134
|
-
|
157
|
+
|
158
|
+
#
|
159
|
+
# Works as #draw() but takes x/y arguments. Used among others by the edit-game state.
|
160
|
+
#
|
161
|
+
def draw_at(x, y)
|
162
|
+
@image.draw_rot(x, y, @zorder, @angle, @center_x, @center_y, @factor_x, @factor_y, @color, @mode) if @visible
|
163
|
+
end
|
135
164
|
end
|
136
165
|
end
|
@@ -29,6 +29,8 @@ module Chingu
|
|
29
29
|
|
30
30
|
def initialize(options = {})
|
31
31
|
@game_objects = options[:game_objects] || []
|
32
|
+
@add_game_objects = []
|
33
|
+
@remove_game_objects = []
|
32
34
|
#@game_objects_by_class = Hash.new
|
33
35
|
end
|
34
36
|
|
@@ -49,12 +51,17 @@ module Chingu
|
|
49
51
|
alias :remove_all :destroy_all
|
50
52
|
|
51
53
|
def add_game_object(object)
|
52
|
-
|
54
|
+
#@game_objects.push(object)
|
55
|
+
@add_game_objects.push(object)
|
56
|
+
|
57
|
+
|
53
58
|
#(@game_objects_by_class[object.class] ||= []).push(object)
|
54
59
|
end
|
55
60
|
|
56
61
|
def remove_game_object(object)
|
57
|
-
|
62
|
+
#@game_objects.delete(object)
|
63
|
+
@remove_game_objects.push(object)
|
64
|
+
|
58
65
|
#@game_objects_by_class[object.class].delete(object)
|
59
66
|
end
|
60
67
|
|
@@ -80,8 +87,18 @@ module Chingu
|
|
80
87
|
object.draw_relative(x, y, zorder, angle, center_x, center_y, factor_x, factor_y)
|
81
88
|
end
|
82
89
|
end
|
83
|
-
|
90
|
+
|
91
|
+
def sync
|
92
|
+
@game_objects += @add_game_objects
|
93
|
+
@add_game_objects.clear
|
94
|
+
|
95
|
+
@game_objects -= @remove_game_objects
|
96
|
+
@remove_game_objects.clear
|
97
|
+
end
|
98
|
+
|
84
99
|
def update
|
100
|
+
sync
|
101
|
+
|
85
102
|
@game_objects.select{ |object| not object.paused }.each do |object|
|
86
103
|
object.update_trait
|
87
104
|
object.update
|
data/lib/chingu/game_state.rb
CHANGED
@@ -57,7 +57,7 @@ module Chingu
|
|
57
57
|
include Chingu::Helpers::ClassInheritableAccessor # adds classmethod class_inheritable_accessor
|
58
58
|
|
59
59
|
attr_reader :options
|
60
|
-
attr_accessor :
|
60
|
+
attr_accessor :game_objects, :game_state_manager
|
61
61
|
|
62
62
|
class_inheritable_accessor :trait_options
|
63
63
|
@trait_options = Hash.new
|
@@ -67,10 +67,10 @@ module Chingu
|
|
67
67
|
# Adds a trait or traits to a certain game class
|
68
68
|
# Executes a standard ruby "include" the specified module
|
69
69
|
#
|
70
|
-
def self.
|
70
|
+
def self.trait(trait, options = {})
|
71
71
|
|
72
72
|
if trait.is_a?(::Symbol) || trait.is_a?(::String)
|
73
|
-
## puts "
|
73
|
+
## puts "trait #{trait}, #{options}"
|
74
74
|
begin
|
75
75
|
# Convert user-given symbol (eg. :timer) to a Module (eg. Chingu::Traits::Timer)
|
76
76
|
mod = Chingu::Traits.const_get(Chingu::Inflector.camelize(trait))
|
@@ -86,7 +86,7 @@ module Chingu
|
|
86
86
|
extend mod2
|
87
87
|
|
88
88
|
# If the newly included trait has a initialize_trait method in the ClassMethods-scope:
|
89
|
-
# ... call it with the options provided with the
|
89
|
+
# ... call it with the options provided with the trait-line.
|
90
90
|
if mod2.method_defined?(:initialize_trait)
|
91
91
|
initialize_trait(options)
|
92
92
|
end
|
@@ -96,11 +96,12 @@ module Chingu
|
|
96
96
|
end
|
97
97
|
end
|
98
98
|
end
|
99
|
+
class << self; alias :has_trait :trait; end
|
99
100
|
|
100
|
-
def self.
|
101
|
-
Array(traits).each { |trait|
|
101
|
+
def self.traits(*traits)
|
102
|
+
Array(traits).each { |trait| trait trait }
|
102
103
|
end
|
103
|
-
|
104
|
+
class << self; alias :has_traits :traits; end
|
104
105
|
|
105
106
|
def initialize(options = {})
|
106
107
|
@options = options
|
@@ -23,52 +23,80 @@ module Chingu
|
|
23
23
|
module GameStates
|
24
24
|
|
25
25
|
#
|
26
|
-
# Premade game state for chingu -
|
27
|
-
#
|
28
|
-
# push_game_state(Chingu::GameStates::
|
26
|
+
# Premade game state for chingu - simple level editing.
|
27
|
+
# Start editing in a gamestate with:
|
28
|
+
# push_game_state(Chingu::GameStates::Edit)
|
29
29
|
#
|
30
30
|
# requires the global $window set to the instance of Gosu::Window (automaticly handled if you use Chingu::Window)
|
31
31
|
#
|
32
32
|
class Edit < Chingu::GameState
|
33
|
+
|
33
34
|
def initialize(options = {})
|
34
35
|
super
|
35
|
-
@grid = options[:grid]
|
36
|
+
@grid = options[:grid] || [8,8]
|
36
37
|
@classes = options[:classes] || []
|
38
|
+
@only = options[:only] || []
|
39
|
+
@debug = options[:debug]
|
40
|
+
@file = options[:file] || "#{previous_game_state.class.to_s.downcase}.yml"
|
41
|
+
@zorder = 10000
|
37
42
|
|
38
|
-
@
|
39
|
-
@red = Gosu::Color.new(0xFFFF0000)
|
40
|
-
@white = Gosu::Color.new(0xFFFFFFFF)
|
43
|
+
@hud_color = Gosu::Color.new(150,100,100,100)
|
41
44
|
@selected_game_object = nil
|
42
45
|
self.input = { :left_mouse_button => :left_mouse_button,
|
43
46
|
:released_left_mouse_button => :released_left_mouse_button,
|
47
|
+
:right_mouse_button => :right_mouse_button,
|
48
|
+
:released_right_mouse_button => :released_right_mouse_button,
|
44
49
|
:delete => :destroy_selected_game_objects,
|
45
50
|
:backspace => :destroy_selected_game_objects,
|
46
51
|
:e => :save_and_quit,
|
47
52
|
:s => :save,
|
48
|
-
:esc => :
|
53
|
+
:esc => :save_and_quit,
|
54
|
+
:holding_up_arrow => :scroll_up,
|
55
|
+
:holding_down_arrow => :scroll_down,
|
56
|
+
:holding_left_arrow => :scroll_left,
|
57
|
+
:holding_right_arrow => :scroll_right,
|
58
|
+
:page_up => :page_up,
|
59
|
+
:page_down => :page_down,
|
49
60
|
:"1" => :create_object_1,
|
50
61
|
:"2" => :create_object_2,
|
51
62
|
:"3" => :create_object_3,
|
52
63
|
:"4" => :create_object_4,
|
53
64
|
:"5" => :create_object_5,
|
54
65
|
}
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
66
|
+
|
67
|
+
x = 20
|
68
|
+
y = 60
|
69
|
+
@classes.each do |klass|
|
70
|
+
puts "Creating a #{klass}" if @debug
|
71
|
+
|
72
|
+
# We initialize x,y,zorder,rotation_center after creation
|
73
|
+
# so they're not overwritten by the class initialize/setup or simular
|
74
|
+
game_object = klass.create
|
75
|
+
game_object.x = x
|
76
|
+
game_object.y = y
|
77
|
+
game_object.zorder = @zorder
|
78
|
+
|
79
|
+
# Scale down big objects, don't scale objects under [32, 32]
|
80
|
+
if game_object.image
|
81
|
+
game_object.factor_x = 32 / game_object.image.width if game_object.image.width > 32
|
82
|
+
game_object.factor_y = 32 / game_object.image.height if game_object.image.height > 32
|
83
|
+
end
|
84
|
+
x += 40
|
62
85
|
end
|
63
|
-
|
64
|
-
|
86
|
+
end
|
87
|
+
|
88
|
+
def setup
|
89
|
+
@title = Text.create("File: #{@file}", :x => 5, :y => 2, :factor => 1, :size => 16, :zorder => @zorder)
|
65
90
|
@title.text += " - Grid: #{@grid}" if @grid
|
66
|
-
@
|
67
|
-
@
|
91
|
+
@text = Text.create("", :x => 200, :y => 20, :factor => 1, :size => 16, :zorder => @zorder)
|
92
|
+
@status_text = Text.create("-", :x => 5, :y => 20, :factor => 1, :size => 16, :zorder => @zorder)
|
93
|
+
#@title2 = Text.create("(1-10) Create object at mouse pos (DEL) Delete selected object (S) Save (E) Save and Quit (ESC) Quit without saving", :x => 5, :y => 30, :factor => 1)
|
68
94
|
end
|
69
95
|
|
70
96
|
def create_object_nr(number)
|
71
|
-
@classes[number].create(:x =>
|
97
|
+
c = @classes[number].create(:x => x, :y => y, :parent => previous_game_state) if @classes[number]
|
98
|
+
c.update
|
99
|
+
#@text.text = "Created a #{c.class} @ #{c.x} / #{c.y}"
|
72
100
|
end
|
73
101
|
|
74
102
|
def create_object_1; create_object_nr(0); end
|
@@ -77,48 +105,61 @@ module Chingu
|
|
77
105
|
def create_object_4; create_object_nr(3); end
|
78
106
|
def create_object_5; create_object_nr(4); end
|
79
107
|
|
80
|
-
|
81
|
-
|
82
108
|
def draw
|
83
|
-
# Draw prev game state onto screen (the level we're editing
|
84
|
-
previous_game_state.draw
|
109
|
+
# Draw prev game state onto screen (the level we're editing)
|
110
|
+
previous_game_state.draw
|
85
111
|
|
86
|
-
#
|
87
|
-
# Draw an edit HUD
|
88
|
-
#
|
89
|
-
$window.draw_quad( 0,0,@color,
|
90
|
-
$window.width,0,@color,
|
91
|
-
$window.width,100,@color,
|
92
|
-
0,100,@color,10)
|
93
|
-
#
|
94
|
-
# Draw debug Texts etc..
|
95
|
-
#
|
96
112
|
super
|
97
113
|
|
98
114
|
#
|
99
|
-
# Draw
|
115
|
+
# Draw an edit HUD
|
100
116
|
#
|
101
|
-
|
102
|
-
|
103
|
-
|
117
|
+
$window.draw_quad( 0,0,@hud_color,
|
118
|
+
$window.width,0,@hud_color,
|
119
|
+
$window.width,100,@hud_color,
|
120
|
+
0,100,@hud_color, @zorder-1)
|
104
121
|
|
105
122
|
#
|
106
|
-
#
|
123
|
+
# Draw red rectangles/circles around all selected game objects
|
107
124
|
#
|
108
|
-
|
109
|
-
$window.mouse_x, $window.mouse_y + 10, @white,
|
110
|
-
$window.mouse_x + 10, $window.mouse_y + 10, @white, 9999)
|
125
|
+
selected_game_objects.each { |game_object| game_object.draw_debug }
|
111
126
|
|
127
|
+
if @cursor_game_object
|
128
|
+
@cursor_game_object.draw_at($window.mouse_x, $window.mouse_y)
|
129
|
+
else
|
130
|
+
#
|
131
|
+
# draw a simple triagle-shaped cursor
|
132
|
+
#
|
133
|
+
$window.draw_triangle( $window.mouse_x, $window.mouse_y, Color::WHITE,
|
134
|
+
$window.mouse_x, $window.mouse_y + 10, Color::WHITE,
|
135
|
+
$window.mouse_x + 10, $window.mouse_y + 10, Color::WHITE, @zorder + 10)
|
136
|
+
end
|
112
137
|
end
|
113
138
|
|
114
139
|
def update
|
140
|
+
# Sync all changes to previous game states game objects list
|
141
|
+
# This is needed since we don't call update on it.
|
142
|
+
previous_game_state.game_objects.sync
|
143
|
+
|
115
144
|
super
|
116
145
|
|
146
|
+
#
|
147
|
+
# We got a selected game object
|
148
|
+
#
|
149
|
+
if @selected_game_object
|
150
|
+
@text.text = "#{@selected_game_object.class.to_s} @ #{@selected_game_object.x} / #{@selected_game_object.y} - zorder: #{@selected_game_object.zorder}"
|
151
|
+
end
|
152
|
+
|
153
|
+
#
|
154
|
+
# We got a selected game object and the left mouse button is held down
|
155
|
+
#
|
117
156
|
if @left_mouse_button && @selected_game_object
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
157
|
+
selected_game_objects.each do |selected_game_object|
|
158
|
+
selected_game_object.x = self.x + selected_game_object.options[:mouse_x_offset]
|
159
|
+
selected_game_object.y = self.y + selected_game_object.options[:mouse_y_offset]
|
160
|
+
selected_game_object.x -= selected_game_object.x % @grid[0]
|
161
|
+
selected_game_object.y -= selected_game_object.y % @grid[1]
|
162
|
+
end
|
122
163
|
|
123
164
|
# TODO: better cleaner sollution
|
124
165
|
if @selected_game_object.respond_to?(:bounding_box)
|
@@ -126,6 +167,8 @@ module Chingu
|
|
126
167
|
@selected_game_object.bounding_box.y = @selected_game_object.y
|
127
168
|
end
|
128
169
|
end
|
170
|
+
|
171
|
+
@status_text.text = "Mouseposition: #{x} / #{y}"
|
129
172
|
end
|
130
173
|
|
131
174
|
def selected_game_objects
|
@@ -136,69 +179,76 @@ module Chingu
|
|
136
179
|
selected_game_objects.each(&:destroy)
|
137
180
|
end
|
138
181
|
|
182
|
+
#
|
183
|
+
# CLICKED LEFT MOUSE BUTTON
|
184
|
+
#
|
139
185
|
def left_mouse_button
|
140
186
|
@left_mouse_button = true
|
141
|
-
x = $window.mouse_x / $window.factor
|
142
|
-
y = $window.mouse_y / $window.factor
|
143
|
-
|
144
|
-
@text.text = "Click @ #{x} / #{y}"
|
145
187
|
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
game_object.
|
188
|
+
if @cursor_game_object && game_object_at(x, y)==nil && game_object_icon_at($window.mouse_x, $window.mouse_y) == nil
|
189
|
+
game_object = @cursor_game_object.class.create(:parent => previous_game_state)
|
190
|
+
game_object.update
|
191
|
+
game_object.options[:selected] = true
|
192
|
+
game_object.x = x
|
193
|
+
game_object.y = y
|
151
194
|
end
|
152
195
|
|
153
|
-
#
|
154
|
-
# Get new object that was clicked at (if any)
|
155
|
-
#
|
196
|
+
# Get editable game object that was clicked at (if any)
|
156
197
|
@selected_game_object = game_object_at(x, y)
|
157
198
|
|
199
|
+
# Check if user clicked on anything in the icon-toolbar of available game objects
|
200
|
+
@cursor_game_object = game_object_icon_at($window.mouse_x, $window.mouse_y)
|
201
|
+
|
158
202
|
if @selected_game_object
|
159
|
-
|
160
|
-
|
203
|
+
#
|
204
|
+
# If clicking on a new object that's wasn't previosly selected
|
205
|
+
# --> deselect all objects unless holding left_ctrl
|
206
|
+
#
|
207
|
+
if @selected_game_object.options[:selected] == nil
|
208
|
+
selected_game_objects.each { |x| x.options[:selected] = nil } unless holding?(:left_ctrl)
|
209
|
+
end
|
161
210
|
|
162
|
-
@
|
163
|
-
|
211
|
+
@selected_game_object.options[:selected] = true
|
212
|
+
#
|
213
|
+
# Re-align all objects x/y offset in relevance to the cursor
|
214
|
+
#
|
215
|
+
selected_game_objects.each do |selected_game_object|
|
216
|
+
selected_game_object.options[:mouse_x_offset] = selected_game_object.x - self.x
|
217
|
+
selected_game_object.options[:mouse_y_offset] = selected_game_object.y - self.y
|
218
|
+
end
|
219
|
+
else
|
220
|
+
selected_game_objects.each { |x| x.options[:selected] = nil } unless holding?(:left_ctrl)
|
164
221
|
end
|
165
|
-
|
166
222
|
end
|
167
223
|
|
168
|
-
def
|
169
|
-
@
|
170
|
-
|
224
|
+
def right_mouse_button
|
225
|
+
@cursor_game_object = game_object_at(x, y)
|
226
|
+
selected_game_objects.each { |x| x.options[:selected] = nil }
|
227
|
+
end
|
228
|
+
def released_right_mouse_button
|
171
229
|
end
|
172
230
|
|
231
|
+
#
|
232
|
+
# RELASED LEFT MOUSE BUTTON
|
233
|
+
#
|
234
|
+
def released_left_mouse_button
|
235
|
+
@left_mouse_button = false
|
236
|
+
end
|
237
|
+
|
238
|
+
def game_object_icon_at(x, y)
|
239
|
+
game_objects.select do |game_object|
|
240
|
+
game_object.respond_to?(:collision_at?) && game_object.collision_at?(x,y)
|
241
|
+
end.first
|
242
|
+
end
|
243
|
+
|
173
244
|
def game_object_at(x, y)
|
174
245
|
previous_game_state.game_objects.select do |game_object|
|
175
|
-
game_object.respond_to?(:
|
246
|
+
game_object.respond_to?(:collision_at?) && game_object.collision_at?(x,y)
|
176
247
|
end.first
|
177
248
|
end
|
178
249
|
|
179
|
-
|
180
250
|
def save
|
181
|
-
|
182
|
-
objects = []
|
183
|
-
previous_game_state.game_objects.each do |game_object|
|
184
|
-
objects << {game_object.class.to_s =>
|
185
|
-
{
|
186
|
-
:x => game_object.x,
|
187
|
-
:y => game_object.y,
|
188
|
-
:angle => game_object.angle,
|
189
|
-
:zorder => game_object.zorder,
|
190
|
-
:factor_x => game_object.factor_x,
|
191
|
-
:factor_y => game_object.factor_y,
|
192
|
-
:center_x => game_object.center_x,
|
193
|
-
:center_y => game_object.center_y,
|
194
|
-
}
|
195
|
-
}
|
196
|
-
end
|
197
|
-
|
198
|
-
#Marshal.dump(previous_game_state.game_objects, File.open(@filename, "w"))
|
199
|
-
File.open(@filename, 'w') do |out|
|
200
|
-
YAML.dump(objects, out)
|
201
|
-
end
|
251
|
+
save_game_objects(:game_objects => previous_game_state.game_objects, :file => @file, :classes => @classes)
|
202
252
|
end
|
203
253
|
|
204
254
|
def save_and_quit
|
@@ -209,13 +259,50 @@ module Chingu
|
|
209
259
|
def quit
|
210
260
|
pop_game_state(:setup => false)
|
211
261
|
end
|
262
|
+
|
263
|
+
def game_object_classes
|
264
|
+
ObjectSpace.enum_for(:each_object, class << GameObject; self; end).to_a.select do |game_class|
|
265
|
+
game_class.instance_methods
|
266
|
+
end
|
267
|
+
end
|
212
268
|
|
269
|
+
def page_up
|
270
|
+
selected_game_objects.each { |game_object| game_object.zorder += 1 }
|
271
|
+
#self.previous_game_state.viewport.y -= $window.height if defined?(self.previous_game_state.viewport)
|
272
|
+
end
|
273
|
+
def page_down
|
274
|
+
selected_game_objects.each { |game_object| game_object.zorder -= 1 }
|
275
|
+
#self.previous_game_state.viewport.y += $window.height if defined?(self.previous_game_state.viewport)
|
276
|
+
end
|
277
|
+
def scroll_up
|
278
|
+
self.previous_game_state.viewport.y -= 10 if defined?(self.previous_game_state.viewport)
|
279
|
+
end
|
280
|
+
def scroll_down
|
281
|
+
self.previous_game_state.viewport.y += 10 if defined?(self.previous_game_state.viewport)
|
282
|
+
end
|
283
|
+
def scroll_left
|
284
|
+
self.previous_game_state.viewport.x -= 10 if defined?(self.previous_game_state.viewport)
|
285
|
+
end
|
286
|
+
def scroll_right
|
287
|
+
self.previous_game_state.viewport.x += 10 if defined?(self.previous_game_state.viewport)
|
288
|
+
end
|
289
|
+
def x
|
290
|
+
x = $window.mouse_x
|
291
|
+
x += self.previous_game_state.viewport.x if defined?(self.previous_game_state.viewport)
|
292
|
+
end
|
293
|
+
def y
|
294
|
+
y = $window.mouse_y
|
295
|
+
y += self.previous_game_state.viewport.y if defined?(self.previous_game_state.viewport)
|
296
|
+
end
|
297
|
+
|
213
298
|
#
|
214
299
|
# If we're editing a game state with automaticly called special methods,
|
215
300
|
# the following takes care of those.
|
216
301
|
#
|
217
302
|
def method_missing(symbol, *args)
|
218
|
-
|
303
|
+
if symbol != :button_down || symbol != :button_up
|
304
|
+
previous_game_state.__send__(symbol, *args)
|
305
|
+
end
|
219
306
|
end
|
220
307
|
|
221
308
|
end
|