summaryrefslogtreecommitdiffhomepage
path: root/samples/00_beginner_ruby_primer/app/main.rb
blob: 6822cf3eeefced2ec1b5ff0817c1a44761122101 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
# ==========================================================================
#  _    _ ________     __  _      _____  _____ _______ ______ _   _ _ _ _ _
# | |  | |  ____\ \   / / | |    |_   _|/ ____|__   __|  ____| \ | | | | | |
# | |__| | |__   \ \_/ /  | |      | | | (___    | |  | |__  |  \| | | | | |
# |  __  |  __|   \   /   | |      | |  \___ \   | |  |  __| | . ` | | | | |
# | |  | | |____   | |    | |____ _| |_ ____) |  | |  | |____| |\  |_|_|_|_|
# |_|  |_|______|  |_|    |______|_____|_____/   |_|  |______|_| \_(_|_|_|_)
#
#
#                                   |
#                                   |
#                                   |
#                                   |
#                                   |
#                                   |
#                                   |
#                                   |
#                                   |
#                                   |
#                                \  |  /
#                                 \ | /
#                                   +
#
# If you are new to the programming language Ruby, then you may find the
# following code a bit overwhelming. This sample is only designed to be
# run interactively (as opposed to being manipulated via source code).
#
# Start up this sample and follow along by visiting:
# https://s3.amazonaws.com/s3.dragonruby.org/dragonruby-gtk-primer.mp4
#
# It is STRONGLY recommended that you work through all the samples before
# looking at the code in this file.
# ==========================================================================

class TutorialOutputs
  attr_accessor :solids, :sprites, :labels, :lines, :borders

  def initialize
    @solids  = []
    @sprites = []
    @labels  = []
    @lines   = []
    @borders = []
  end

  def tick
    @solids  ||= []
    @sprites ||= []
    @labels  ||= []
    @lines   ||= []
    @borders ||= []
    @solids.each  { |p| $gtk.args.outputs.reserved << p.solid  }
    @sprites.each { |p| $gtk.args.outputs.reserved << p.sprite }
    @labels.each  { |p| $gtk.args.outputs.reserved << p.label  }
    @lines.each   { |p| $gtk.args.outputs.reserved << p.line   }
    @borders.each { |p| $gtk.args.outputs.reserved << p.border }
  end

  def clear
    @solids.clear
    @sprites.clear
    @labels.clear
    @borders.clear
  end
end

def defaults
  state.reset_button ||=
    state.new_entity(
      :button,
      label:  [1190, 68, "RESTART", -2, 0, 0, 0, 0].label,
      background: [1160, 38, 120, 50, 255, 255, 255].solid
    )
  $gtk.log_level = :off
end

def tick_reset_button
  return unless state.hello_dragonruby_confirmed
  $gtk.args.outputs.reserved << state.reset_button.background
  $gtk.args.outputs.reserved << state.reset_button.label
  if inputs.mouse.click && inputs.mouse.click.point.inside_rect?(state.reset_button.background)
    restart_tutorial
  end
end

def seperator
  @seperator = "=" * 80
end

def tick_intro
  queue_message "Welcome to the DragonRuby GTK primer! Try typing the
code below and press ENTER:

    puts \"Hello DragonRuby!\"
"
end

def tick_hello_dragonruby
  return unless console_has? "Hello DragonRuby!"

  $gtk.args.state.hello_dragonruby_confirmed = true

  queue_message "Well HELLO to you too!

If you ever want to RESTART the tutorial, just click the \"RESTART\"
button in the bottom right-hand corner.

Let's continue shall we? Type the code below and press ENTER:

    outputs.solids << [910, 200, 100, 100, 255, 0, 0]
"

end

def tick_explain_solid
  return unless $tutorial_outputs.solids.any? {|s| s == [910, 200, 100, 100, 255, 0, 0]}

  queue_message "Sweet!

The code: outputs.solids << [910, 200, 100, 100, 255, 0, 0]
Does the following:
1. GET the place where SOLIDS go: outputs.solids
2. Request that a new SOLID be ADDED: <<
3. The DEFINITION of a SOLID is the ARRAY:
   [910, 200, 100, 100, 255, 0, 0]

      GET       ADD     X      Y    WIDTH  HEIGHT RED  GREEN  BLUE
       |         |      |      |      |      |     |     |     |
       |         |      |      |      |      |     |     |     |
outputs.solids  <<    [910,   200,   100,   100,  255,   0,    0]
                      |_________________________________________|
                                           |
                                           |
                                         ARRAY

Now let's create a blue SOLID. Type:

    outputs.solids << [1010, 200, 100, 100, 0, 0, 255]
"

  state.explain_solid_confirmed = true
end

def tick_explain_solid_blue
  return unless state.explain_solid_confirmed
  return unless $tutorial_outputs.solids.any? {|s| s == [1010, 200, 100, 100, 0, 0, 255]}
  state.explain_solid_blue_confirmed = true

  queue_message "And there is our blue SOLID!

The ARRAY is the MOST important thing in DragonRuby GTK.

Let's create a SPRITE using an ARRAY:

  outputs.sprites << [1110, 200, 100, 100, 'sprites/dragon_fly_0.png']
"
end

def tick_explain_tick_count
  return unless $tutorial_outputs.sprites.any? {|s| s == [1110, 200, 100, 100, 'sprites/dragon_fly_0.png']}
  return if $tutorial_outputs.labels.any? {|l| l == [1210, 200, state.tick_count, 255, 255, 255]}
  state.explain_tick_count_confirmed = true

  queue_message "Look at the cute little dragon!

We can create a LABEL with ARRAYS too. Let's create a LABEL showing
THE PASSAGE OF TIME, which is called TICK_COUNT.

  outputs.labels << [1210, 200, state.tick_count, 0, 255, 0]
"
end

def tick_explain_mod
  return unless $tutorial_outputs.labels.any? {|l| l == [1210, 200, state.tick_count, 0, 255, 0]}
  state.explain_mod_confirmed = true
  queue_message "
The code: outputs.labels << [1210, 200, state.tick_count, 0, 255, 0]
Does the following:
1. GET the place where labels go: outputs.labels
2. Request that a new label be ADDED: <<
3. The DEFINITION of a LABEL is the ARRAY:
   [1210, 200, state.tick_count, 0, 255, 0]

      GET       ADD     X      Y          TEXT         RED  GREEN  BLUE
       |         |      |      |            |           |     |     |
       |         |      |      |            |           |     |     |
outputs.labels  <<    [1210,  200,   state.tick_count,  0,   255,   0]
                      |______________________________________________|
                                              |
                                              |
                                            ARRAY

Now let's do some MATH, save the result to STATE, and create a LABEL:

    state.sprite_frame = state.tick_count.idiv(4).mod(6)
    outputs.labels << [1210, 170, state.sprite_frame, 0, 255, 0]

Type the lines above (pressing ENTER after each line).
"
end

def tick_explain_string_interpolation
  return unless state.explain_mod_confirmed
  return unless state.sprite_frame == state.tick_count.idiv(4).mod(6)
  return unless $tutorial_outputs.labels.any? {|l| l == [1210, 170, state.sprite_frame, 0, 255, 0]}

  queue_message "Here is what the mathematical computation you just typed does:

1. Create an item of STATE named SPRITE_FRAME: state.sprite_frame =
2. Set this SPRITE_FRAME to the PASSAGE OF TIME (tick_count),
   DIVIDED EVENLY (idiv) into 4,
   and then compute the REMAINDER (mod) of 6.

   STATE   SPRITE_FRAME    PASSAGE OF      HOW LONG   HOW MANY
     |          |             TIME         TO SHOW    IMAGES
     |          |              |           AN IMAGE   TO FLIP THROUGH
     |          |              |               |      |
state.sprite_frame =     state.tick_count.idiv(4).mod(6)
                                           |       |
                                           |       +- REMAINDER OF DIVIDE
                                    DIVIDE EVENLY
                                    (NO DECIMALS)

With the information above, we can animate a SPRITE
using STRING INTERPOLATION: \#{}
which creates a unique SPRITE_PATH:

  state.sprite_path =  \"sprites/dragon_fly_\#{state.sprite_frame}.png\"
  outputs.labels    << [910, 330, \"path: \#{state.sprite_path}\", 0, 255, 0]
  outputs.sprites   << [910, 330, 370, 370, state.sprite_path]

Type the lines above (pressing ENTER after each line).
"
end

def tick_reprint_on_error
  return unless console.last_command_errored
  puts $gtk.state.messages.last
  puts "\nWhoops! Try again."
  console.last_command_errored = false
end

def tick_evals
  state.evals ||= []
  if console.last_command && (console.last_command.start_with?("outputs.") || console.last_command.start_with?("state."))
    state.evals << console.last_command
    console.last_command = nil
  end

  state.evals.each do |l|
    Kernel.eval l
  end
rescue Exception => e
  state.evals = state.evals[0..-2]
end

$tutorial_outputs ||= TutorialOutputs.new

def tick args
  $gtk.log_level = :off
  defaults
  console.show
  $tutorial_outputs.clear
  $tutorial_outputs.solids  << [900, 37, 480, 700,   0,   0,   0, 255]
  $tutorial_outputs.borders << [900, 37, 380, 683, 255, 255, 255]
  tick_evals
  $tutorial_outputs.tick
  tick_intro
  tick_hello_dragonruby
  tick_reset_button
  tick_explain_solid
  tick_explain_solid_blue
  tick_reprint_on_error
  tick_explain_tick_count
  tick_explain_mod
  tick_explain_string_interpolation
end

def console
  $gtk.console
end

def queue_message message
  $gtk.args.state.messages ||= []
  return if $gtk.args.state.messages.include? message
  $gtk.args.state.messages << message
  last_three = [$gtk.console.log[-3], $gtk.console.log[-2], $gtk.console.log[-1]].reject_nil
  $gtk.console.log.clear
  puts seperator
  $gtk.console.log += last_three
  puts seperator
  puts message
  puts seperator
end

def console_has? message
  console.log.map(&:upcase).include? "#{message.upcase}\n"
end

def restart_tutorial
  $tutorial_outputs.clear
  $gtk.console.log.clear
  $gtk.reset
  puts "Starting the tutorial over!"
end

def state
  $gtk.args.state
end

def inputs
  $gtk.args.inputs
end

def outputs
  $tutorial_outputs
end