chingu 0.7.0 → 0.7.5
Sign up to get free protection for your applications and to get access to all the features.
- 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
|