Ruby GTK3移行後のメインリポジトリ
Revisão | 6cd39a019ced21e5ead03056f031ef9f01f1078b (tree) |
---|---|
Hora | 2014-05-06 22:50:06 |
Autor | Shyouzou Sugitani <shy@user...> |
Commiter | Shyouzou Sugitani |
add nekodorif.rb
@@ -1,6 +1,6 @@ | ||
1 | 1 | # -*- coding: utf-8 -*- |
2 | 2 | # |
3 | -# communicate.py - ghost-to-ghost communication mechanism | |
3 | +# communicate.rb - ghost-to-ghost communication mechanism | |
4 | 4 | # Copyright (C) 2002-2014 by Shyouzou Sugitani <shy@users.sourceforge.jp> |
5 | 5 | # Copyright (C) 2002, 2003 by MATSUMURA Namihiko <nie@counterghost.net> |
6 | 6 | # |
@@ -0,0 +1,957 @@ | ||
1 | +# -*- coding: utf-8 -*- | |
2 | +# | |
3 | +# Copyright (C) 2004-2014 by Shyouzou Sugitani <shy@users.sourceforge.jp> | |
4 | +# | |
5 | +# This program is free software; you can redistribute it and/or modify it | |
6 | +# under the terms of the GNU General Public License (version 2) as | |
7 | +# published by the Free Software Foundation. It is distributed in the | |
8 | +# hope that it will be useful, but WITHOUT ANY WARRANTY; without even the | |
9 | +# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR | |
10 | +# PURPOSE. See the GNU General Public License for more details. | |
11 | +# | |
12 | + | |
13 | +# TODO: | |
14 | +# - 「きのこ」へのステータス送信. | |
15 | +# - 「きのこ」の情報の参照. | |
16 | +# - SERIKO/1.2ベースのアニメーション | |
17 | +# - (スキン側の)katochan.txt | |
18 | +# - balloon.txt | |
19 | +# - surface[0/1/2]a.txt(@ゴースト) | |
20 | +# - 自爆イベント | |
21 | +# - headrect.txt : 頭の当たり判定領域データ | |
22 | +# 当たり領域のleft/top/right/bottomを半角カンマでセパレートして記述. | |
23 | +# 1行目がsurface0、2行目がsurface1の領域データ. | |
24 | +# このファイルがない場合、領域は自動計算される. | |
25 | +# - speak.txt | |
26 | +# - katochan が無い場合の処理.(本体の方のpopup menuなども含めて) | |
27 | +# - 設定ダイアログ : [会話/反応]タブ -> [SEND SSTP/1.1] or [SHIORI] | |
28 | +# - 見切れ連続20[s]、もしくは画面内で静止20[s]でアニメーション記述ミスと見なし自動的に落ちる | |
29 | +# - 発言中にバルーンをダブルクリックで即閉じ | |
30 | +# - @ゴースト名は#nameと#forには使えない. もし書いても無視されすべて有効になる | |
31 | +# - 連続落し不可指定 | |
32 | +# チェックしておくと落下物を2個以上同時に落とせなくなる | |
33 | +# - スキンチェンジ時も起動時のトークを行う | |
34 | +# - ファイルセット設定機能 | |
35 | +# インストールされたスキン/落下物のうち使用するものだけを選択できる | |
36 | +# - ターゲットのアイコン化への対応 | |
37 | +# - アイコン化されているときは自動落下しない | |
38 | +# - アイコン化されているときのDirectSSTP SEND/DROPリクエストはエラー(Invisible) | |
39 | +# - 落下物の透明化ON/OFF | |
40 | +# - 落下物が猫どりふ自身にも落ちてくる | |
41 | +# 不在時に1/2、ランダム/全員落し時に1/10の確率で自爆 | |
42 | +# - 一定時間間隔で勝手に物を落とす | |
43 | +# - ターゲット指定落し、ランダム落し、全員落し | |
44 | +# - 出現即ヒットの場合への対応 | |
45 | + | |
46 | +# - 複数ゴーストでの当たり判定. | |
47 | +# - 透明ウィンドウ | |
48 | + | |
49 | +require "gettext" | |
50 | +require "gtk3" | |
51 | + | |
52 | +require "ninix/pix" | |
53 | +require "ninix/home" | |
54 | + | |
55 | +module Nekodorif | |
56 | + | |
57 | + class Menu | |
58 | + include GetText | |
59 | + | |
60 | + bindtextdomain("ninix-aya") | |
61 | + | |
62 | + def initialize(accelgroup) | |
63 | + @parent = nil | |
64 | + ui_info = <<-EOS | |
65 | + <ui> | |
66 | + <popup name='popup'> | |
67 | + <menuitem action='Settings'/> | |
68 | + <menu action='Katochan'> | |
69 | + </menu> | |
70 | + <separator/> | |
71 | + <menuitem action='Exit'/> | |
72 | + </popup> | |
73 | + </ui> | |
74 | + EOS | |
75 | + @__menu_list = { | |
76 | + 'settings' => [['Settings', nil, _('Settings...(_O)'), nil, | |
77 | + '', lambda {|a, b| @parent.handle_request('NOTIFY', 'edit_preferences')}], | |
78 | + '/ui/popup/Settings'], | |
79 | + 'katochan' => [['Katochan', nil, _('Katochan(_K)'), nil], | |
80 | + '/ui/popup/Katochan'], | |
81 | + 'exit' => [['Exit', nil,_('Exit(_Q)'), nil, | |
82 | + '', lambda {|a, b| @parent.handle_request('NOTIFY', 'close')}], | |
83 | + '/ui/popup/Exit'], | |
84 | + } | |
85 | + @__katochan_list = nil | |
86 | + actions = Gtk::ActionGroup.new('Actions') | |
87 | + entry = [] | |
88 | + for value in @__menu_list.values() | |
89 | + entry << value[0] | |
90 | + end | |
91 | + actions.add_actions(entry) | |
92 | + ui_manager = Gtk::UIManager.new() | |
93 | + ui_manager.insert_action_group(actions, 0) | |
94 | + ui_manager.add_ui(ui_info) | |
95 | + @__popup_menu = ui_manager.get_widget('/ui/popup') | |
96 | + for key in @__menu_list.keys | |
97 | + path = @__menu_list[key][-1] | |
98 | + @__menu_list[key][1] = ui_manager.get_widget(path) | |
99 | + end | |
100 | + end | |
101 | + | |
102 | + def set_responsible(parent) | |
103 | + @parent = parent | |
104 | + end | |
105 | + | |
106 | + def popup(button) | |
107 | + katochan_list = @parent.handle_request('GET', 'get_katochan_list') | |
108 | + __set_katochan_menu(katochan_list) | |
109 | + @__popup_menu.popup(nil, nil, button, Gtk.current_event_time) | |
110 | + end | |
111 | + | |
112 | + def __set_katochan_menu(list) ## FIXME | |
113 | + key = 'katochan' | |
114 | + if not list.empty? | |
115 | + menu = Gtk::Menu.new() | |
116 | + for katochan in list | |
117 | + item = Gtk::MenuItem.new(katochan['name']) | |
118 | + item.signal_connect('activate', katochan) do |a, k| | |
119 | + @parent.handle_request('NOTIFY', 'select_katochan', k) | |
120 | + end | |
121 | + menu.add(item) | |
122 | + item.show() | |
123 | + end | |
124 | + @__menu_list[key][1].set_submenu(menu) | |
125 | + menu.show() | |
126 | + @__menu_list[key][1].show() | |
127 | + else | |
128 | + @__menu_list[key][1].hide() | |
129 | + end | |
130 | + end | |
131 | + end | |
132 | + | |
133 | + class Nekoninni | |
134 | + | |
135 | + def initialize | |
136 | + @mode = 1 # 0: SEND SSTP1.1, 1: SHIORI/2.2 | |
137 | + @__running = false | |
138 | + @skin = nil | |
139 | + @katochan = nil | |
140 | + end | |
141 | + | |
142 | + def observer_update(event, args) | |
143 | + if ['set position', 'set surface'].include?(event) | |
144 | + if @skin != nil | |
145 | + @skin.set_position() | |
146 | + end | |
147 | + if @katochan != nil and @katochan.loaded | |
148 | + @katochan.set_position() | |
149 | + end | |
150 | + elsif event == 'set scale' | |
151 | + scale = @target.get_surface_scale() | |
152 | + if @skin != nil | |
153 | + @skin.set_scale(scale) | |
154 | + end | |
155 | + if @katochan != nil | |
156 | + @katochan.set_scale(scale) | |
157 | + end | |
158 | + elsif event == 'finalize' | |
159 | + finalize() | |
160 | + else | |
161 | + ##logging.debug('OBSERVER(nekodorif): ignore - {0}'.format(event)) | |
162 | + end | |
163 | + end | |
164 | + | |
165 | + def load(dir, katochan, target) | |
166 | + if katochan.empty? | |
167 | + return 0 | |
168 | + end | |
169 | + @dir = dir | |
170 | + @target = target | |
171 | + @target.attach_observer(self) | |
172 | + @accelgroup = Gtk::AccelGroup.new() | |
173 | + scale = @target.get_surface_scale() | |
174 | + @skin = Skin.new(@dir, @accelgroup, scale) | |
175 | + @skin.set_responsible(self) | |
176 | + if @skin == nil | |
177 | + return 0 | |
178 | + end | |
179 | + @katochan_list = katochan | |
180 | + @katochan = nil | |
181 | + launch_katochan(@katochan_list[0]) | |
182 | + @__running = true | |
183 | + GLib::Timeout.add(50) { do_idle_tasks } # 50[ms] | |
184 | + return 1 | |
185 | + end | |
186 | + | |
187 | + def handle_request(event_type, event, *arglist, **argdict) | |
188 | + ##assert ['GET', 'NOTIFY'].include?(event_type) | |
189 | + handlers = { | |
190 | + 'get_katochan_list' => lambda { return @katochan_list }, | |
191 | + 'get_mode' => lambda { return @mode }, | |
192 | + } | |
193 | +# handler = handlers.get(event, | |
194 | +# getattr(self, event, | |
195 | +# lambda {|a| return nil})) ## FIXME | |
196 | +# result = handler(*arglist, **argdict) | |
197 | +# if not handlers.include?(event) | |
198 | +# result = @parent.handle_request(event_type, event, *arglist, **argdict) | |
199 | +# else | |
200 | + if handlers.include?(event) | |
201 | + result = handlers[event].call #( *arglist, **argdict) | |
202 | + else | |
203 | + ## FIXME | |
204 | + print("REQ: ", Nekoninni.method_defined?(event), " , ", *arglist, "\n") | |
205 | + result = method(event).call(*arglist)#, **argdict) | |
206 | + end | |
207 | + if event_type == 'GET' | |
208 | + return result | |
209 | + end | |
210 | + end | |
211 | + | |
212 | + def do_idle_tasks | |
213 | + if not @__running | |
214 | + return false | |
215 | + end | |
216 | + @skin.update() | |
217 | + if @katochan != nil | |
218 | + @katochan.update() | |
219 | + end | |
220 | + #self.process_script() | |
221 | + return true | |
222 | + end | |
223 | + | |
224 | + def send_event(event) | |
225 | + if not ['Emerge', # 可視領域内に出現 | |
226 | + 'Hit', # ヒット | |
227 | + 'Drop', # 再落下開始 | |
228 | + 'Vanish', # ヒットした落下物が可視領域内から消滅 | |
229 | + 'Dodge' # よけられてヒットしなかった落下物が可視領域内から消滅 | |
230 | + ].include?(event) | |
231 | + return | |
232 | + end | |
233 | + args = [@katochan.get_name(), | |
234 | + @katochan.get_ghost_name(), | |
235 | + @katochan.get_category(), | |
236 | + @katochan.get_kinoko_flag(), | |
237 | + @katochan.get_target()] | |
238 | + @target.notify_event('OnNekodorifObject' + event.to_s, *args) | |
239 | + end | |
240 | + | |
241 | + def has_katochan | |
242 | + if @katochan != nil | |
243 | + return true | |
244 | + else | |
245 | + return false | |
246 | + end | |
247 | + end | |
248 | + | |
249 | + def select_katochan(args) | |
250 | + launch_katochan(args) | |
251 | + end | |
252 | + | |
253 | + def drop_katochan | |
254 | + @katochan.drop() | |
255 | + end | |
256 | + | |
257 | + def delete_katochan | |
258 | + @katochan.destroy() | |
259 | + @katochan = nil | |
260 | + @skin.reset() | |
261 | + end | |
262 | + | |
263 | + def launch_katochan(katochan) | |
264 | + if @katochan | |
265 | + @katochan.destroy() | |
266 | + end | |
267 | + @katochan = Katochan.new(@target) | |
268 | + @katochan.set_responsible(self) | |
269 | + @katochan.load(katochan) | |
270 | + end | |
271 | + | |
272 | + def edit_preferences | |
273 | + end | |
274 | + | |
275 | + def finalize | |
276 | + @__running = false | |
277 | + @target.detach_observer(self) | |
278 | + if @katochan != nil | |
279 | + @katochan.destroy() | |
280 | + end | |
281 | + if @skin != nil | |
282 | + @skin.destroy() | |
283 | + end | |
284 | + ##if self.balloon is not None: | |
285 | + ## self.balloon.destroy() | |
286 | + end | |
287 | + | |
288 | + def close | |
289 | + finalize() | |
290 | + end | |
291 | + end | |
292 | + | |
293 | + class Skin | |
294 | + | |
295 | + def initialize(dir, accelgroup, scale) | |
296 | + @dir = dir | |
297 | + @accelgroup = accelgroup | |
298 | + @parent = nil | |
299 | + @dragged = false | |
300 | + @x_root = nil | |
301 | + @y_root = nil | |
302 | + @__scale = scale | |
303 | + @__menu = Menu.new(@accelgroup) | |
304 | + @__menu.set_responsible(self) | |
305 | + path = File.join(@dir, 'omni.txt') | |
306 | + if File.file?(path) and File.size(path) == 0 | |
307 | + @omni = 1 | |
308 | + else | |
309 | + @omni = 0 | |
310 | + end | |
311 | + @window = Pix::TransparentWindow.new() | |
312 | + name, top_dir = Home.read_profile_txt(dir) # XXX | |
313 | + @window.set_title(name) | |
314 | + @window.signal_connect('delete_event') do |w, e| | |
315 | + delete(w, e) | |
316 | + end | |
317 | + @window.signal_connect('key_press_event') do |w, e| | |
318 | + key_press(w, e) | |
319 | + end | |
320 | + @window.add_accel_group(@accelgroup) | |
321 | + @darea = @window.darea # @window.get_child() | |
322 | + @darea.set_events(Gdk::Event::EXPOSURE_MASK| | |
323 | + Gdk::Event::BUTTON_PRESS_MASK| | |
324 | + Gdk::Event::BUTTON_RELEASE_MASK| | |
325 | + Gdk::Event::POINTER_MOTION_MASK| | |
326 | + Gdk::Event::POINTER_MOTION_HINT_MASK| | |
327 | + Gdk::Event::LEAVE_NOTIFY_MASK) | |
328 | + @darea.signal_connect('draw') do |w, cr| | |
329 | + redraw(w, cr) | |
330 | + end | |
331 | + @darea.signal_connect('button_press_event') do |w, e| | |
332 | + button_press(w, e) | |
333 | + end | |
334 | + @darea.signal_connect('button_release_event') do |w, e| | |
335 | + button_release(w, e) | |
336 | + end | |
337 | + @darea.signal_connect('motion_notify_event') do |w, e| | |
338 | + motion_notify(w, e) | |
339 | + end | |
340 | + @darea.signal_connect('leave_notify_event') do |w, e| | |
341 | + leave_notify(w, e) | |
342 | + end | |
343 | + @id = [0, nil] | |
344 | + set_surface() | |
345 | + set_position(reset=1) | |
346 | + @window.show_all() | |
347 | + end | |
348 | + | |
349 | + def set_responsible(parent) | |
350 | + @parent = parent | |
351 | + end | |
352 | + | |
353 | + def handle_request(event_type, event, *arglist, **argdict) | |
354 | + ##assert ['GET', 'NOTIFY'].include?(event_type) | |
355 | + handlers = { | |
356 | + } | |
357 | +# handler = handlers.get(event, getattr(self, event, nil)) | |
358 | +# if handler == nil | |
359 | + if not handlers.include?(event) | |
360 | + result = @parent.handle_request(event_type, event, *arglist, **argdict) | |
361 | + else | |
362 | + result = method(event).call(*arglist)#, **argdict) | |
363 | + end | |
364 | + if event_type == 'GET' | |
365 | + return result | |
366 | + end | |
367 | + end | |
368 | + | |
369 | + def set_scale(scale) | |
370 | + @__scale = scale | |
371 | + set_surface() | |
372 | + set_position() | |
373 | + end | |
374 | + | |
375 | + def redraw(widget, cr) | |
376 | + scale = @__scale | |
377 | + cr.scale(scale / 100.0, scale / 100.0) | |
378 | + cr.set_source(@image_surface, 0, 0) | |
379 | + cr.set_operator(Cairo::OPERATOR_SOURCE) | |
380 | + cr.paint() | |
381 | + end | |
382 | + | |
383 | + def delete()#widget, event) | |
384 | + @parent.handle_request('NOTIFY', 'finalize') | |
385 | + end | |
386 | + | |
387 | + def key_press(window, event) | |
388 | + if event.state & (Gdk::Window::ModifierType::CONTROL_MASK | Gdk::Window::ModifierType::SHIFT_MASK) | |
389 | + if event.keyval == Gdk::Keyval::GDK_KEY_F12 | |
390 | + #logging.info('reset skin position') | |
391 | + set_position(reset=1) | |
392 | + end | |
393 | + end | |
394 | + return true | |
395 | + end | |
396 | + | |
397 | + def destroy ## FIXME | |
398 | + @window.destroy() | |
399 | + end | |
400 | + | |
401 | + def button_press(widget, event) | |
402 | + @x_root = event.x_root | |
403 | + @y_root = event.y_root | |
404 | + if event.button == 1 | |
405 | + if event.event_type == Gdk::Event::BUTTON_PRESS | |
406 | + #pass | |
407 | + elsif event.event_type == Gdk::Event::DOUBLE_BUTTON_PRESS # double click | |
408 | + if @parent.handle_request('GET', 'has_katochan') | |
409 | + start() ## FIXME | |
410 | + @parent.handle_request('NOTIFY', 'drop_katochan') | |
411 | + end | |
412 | + end | |
413 | + elsif event.button == 3 | |
414 | + if event.event_type == Gdk::Event::BUTTON_PRESS | |
415 | + @__menu.popup(event.button) | |
416 | + end | |
417 | + end | |
418 | + return true | |
419 | + end | |
420 | + | |
421 | + def set_surface | |
422 | + if @id[1] != nil | |
423 | + path = File.join(@dir, 'surface' + @id[0].to_s + @id[1].to_s + '.png') | |
424 | + if not File.exists?(path) | |
425 | + @id[1] = nil | |
426 | + set_surface() | |
427 | + return | |
428 | + end | |
429 | + else | |
430 | + path = File.join(@dir, 'surface' + @id[0].to_s + '.png') | |
431 | + end | |
432 | + begin | |
433 | + new_surface = Pix.create_surface_from_file(path) | |
434 | + w = [8, (new_surface.width * @__scale / 100).to_i].max | |
435 | + h = [8, (new_surface.height * @__scale / 100).to_i].max | |
436 | + rescue # except: | |
437 | + @parent.handle_request('NOTIFY', 'finalize') | |
438 | + return | |
439 | + end | |
440 | + @w, @h = w, h | |
441 | + @window.update_size(@w, @h) | |
442 | + @image_surface = new_surface | |
443 | + @darea.queue_draw() | |
444 | + end | |
445 | + | |
446 | + def set_position(reset=0) | |
447 | + left, top, scrn_w, scrn_h = Pix.get_workarea() | |
448 | + if reset | |
449 | + @x = left | |
450 | + @y = top + scrn_h - @h | |
451 | + else | |
452 | + if not @omni | |
453 | + @y = top + scrn_h - @h | |
454 | + end | |
455 | + end | |
456 | + @window.move(@x, @y) | |
457 | + end | |
458 | + | |
459 | + def move(x_delta, y_delta) | |
460 | + @x = @x + x_delta | |
461 | + if @omni | |
462 | + @y = @y + y_delta | |
463 | + end | |
464 | + set_position() | |
465 | + end | |
466 | + | |
467 | + def update | |
468 | + if @id[1] != nil | |
469 | + @id[1] += 1 | |
470 | + else | |
471 | + if Random.rand(0..99) != 0 ## XXX | |
472 | + return | |
473 | + end | |
474 | + @id[1] = 0 | |
475 | + end | |
476 | + set_surface() | |
477 | + end | |
478 | + | |
479 | + def start ## FIXME | |
480 | + @id[0] = 1 | |
481 | + set_surface() | |
482 | + end | |
483 | + | |
484 | + def reset ## FIXME | |
485 | + @id[0] = 0 | |
486 | + set_surface() | |
487 | + end | |
488 | + | |
489 | + def button_release(widget, event) | |
490 | + if @dragged | |
491 | + @dragged = false | |
492 | + set_position() | |
493 | + end | |
494 | + @x_root = nil | |
495 | + @y_root = nil | |
496 | + return true | |
497 | + end | |
498 | + | |
499 | + def motion_notify(widget, event) | |
500 | + if event.hint? | |
501 | + _, x, y, state = widget.window.get_device_position(event.device) | |
502 | + else | |
503 | + x, y, state = event.x, event.y, event.state | |
504 | + end | |
505 | + #x, y = self.window.winpos_to_surfacepos(x, y, self.__scale) | |
506 | + if state & Gdk::Window::ModifierType::BUTTON1_MASK | |
507 | + if @x_root != nil and \ | |
508 | + @y_root != nil | |
509 | + @dragged = true | |
510 | + x_delta = (event.x_root - @x_root).to_i | |
511 | + y_delta = (event.y_root - @y_root).to_i | |
512 | + move(x_delta, y_delta) | |
513 | + @x_root = event.x_root | |
514 | + @y_root = event.y_root | |
515 | + end | |
516 | + end | |
517 | + return true | |
518 | + end | |
519 | + | |
520 | + def leave_notify(widget, event) ## FIXME | |
521 | + end | |
522 | + end | |
523 | + | |
524 | + class Balloon | |
525 | + | |
526 | + def initialize | |
527 | + end | |
528 | + | |
529 | + def destroy ## FIXME | |
530 | + #pass | |
531 | + end | |
532 | + end | |
533 | + | |
534 | + class Katochan | |
535 | + | |
536 | + CATEGORY_LIST = ['pain', # 痛い | |
537 | + 'stab', # 刺さる | |
538 | + 'surprise', # びっくり | |
539 | + 'hate', # 嫌い、気持ち悪い | |
540 | + 'huge', # 巨大 | |
541 | + 'love', # 好き、うれしい | |
542 | + 'elegant', # 風流、優雅 | |
543 | + 'pretty', # かわいい | |
544 | + 'food', # 食品 | |
545 | + 'reference', # 見る/読むもの | |
546 | + 'other' # 上記カテゴリに当てはまらないもの | |
547 | + ] | |
548 | + | |
549 | + def initialize(target) | |
550 | + @side = 0 | |
551 | + @target = target | |
552 | + @parent = nil | |
553 | + @settings = {} | |
554 | + @settings['state'] = 'before' | |
555 | + @settings['fall.type'] = 'gravity' | |
556 | + @settings['fall.speed'] = 1 | |
557 | + @settings['slide.type'] = 'none' | |
558 | + @settings['slide.magnitude'] = 0 | |
559 | + @settings['slide.sinwave.degspeed'] = 30 | |
560 | + @settings['wave'] = nil | |
561 | + @settings['wave.loop'] = 0 | |
562 | + @__scale = 100 | |
563 | + @loaded = false | |
564 | + end | |
565 | + | |
566 | + def set_responsible(parent) | |
567 | + @parent = parent | |
568 | + end | |
569 | + | |
570 | + def get_name | |
571 | + return @data['name'] | |
572 | + end | |
573 | + | |
574 | + def get_category | |
575 | + return @data['category'] | |
576 | + end | |
577 | + | |
578 | + def get_kinoko_flag ## FIXME | |
579 | + return 0 # 0/1 = きのこに当たっていない(ない場合を含む)/当たった | |
580 | + end | |
581 | + | |
582 | + def get_target ## FIXME | |
583 | + if @side == 0 | |
584 | + return @target.get_selfname() | |
585 | + else | |
586 | + return @target.get_keroname() | |
587 | + end | |
588 | + end | |
589 | + | |
590 | + def get_ghost_name | |
591 | + if @data.include?('for') # 落下物が主に対象としているゴーストの名前 | |
592 | + return @data['for'] | |
593 | + else | |
594 | + return '' | |
595 | + end | |
596 | + end | |
597 | + | |
598 | + def destroy | |
599 | + @window.destroy() | |
600 | + end | |
601 | + | |
602 | + def delete()#widget, event) | |
603 | + destroy() | |
604 | + end | |
605 | + | |
606 | + def redraw(widget, cr) | |
607 | + scale = @__scale | |
608 | + cr.scale(scale / 100.0, scale / 100.0) | |
609 | + cr.set_source(@image_surface, 0, 0) | |
610 | + cr.set_operator(Cairo::OPERATOR_SOURCE) | |
611 | + cr.paint() | |
612 | + end | |
613 | + | |
614 | + def set_movement(timing) | |
615 | + key = timing + 'fall.type' | |
616 | + if @data.include?(key) and \ | |
617 | + ['gravity', 'evenspeed', 'none'].include?(@data[key]) | |
618 | + @settings['fall.type'] = @data[key] | |
619 | + else | |
620 | + @settings['fall.type'] = 'gravity' | |
621 | + end | |
622 | + if @data.include?(timing + 'fall.speed') | |
623 | + @settings['fall.speed'] = @data[timing + 'fall.speed'] | |
624 | + else | |
625 | + @settings['fall.speed'] =1 | |
626 | + end | |
627 | + if @settings['fall.speed'] < 1 | |
628 | + @settings['fall.speed'] = 1 | |
629 | + end | |
630 | + if @settings['fall.speed'] > 100 | |
631 | + @settings['fall.speed'] = 100 | |
632 | + end | |
633 | + key = timing + 'slide.type' | |
634 | + if @data.include?(key) and \ | |
635 | + ['none', 'sinwave', 'leaf'].include?(@data[key]) | |
636 | + @settings['slide.type'] = @data[key] | |
637 | + else | |
638 | + @settings['slide.type'] = 'none' | |
639 | + end | |
640 | + if @data.include?(timing + 'slide.magnitude') | |
641 | + @settings['slide.magnitude'] = @data[timing + 'slide.magnitude'] | |
642 | + else | |
643 | + @settings['slide.magnitude'] = 0 | |
644 | + end | |
645 | + if @data.include?(timing + 'slide.sinwave.degspeed') | |
646 | + @settings['slide.sinwave.degspeed'] = @data[timing + 'slide.sinwave.degspeed'] | |
647 | + else | |
648 | + @settings['slide.sinwave.degspeed'] = 30 | |
649 | + end | |
650 | + if @data.include?(timing + 'wave') | |
651 | + @settings['wave'] = @data[timing + 'wave'] | |
652 | + else | |
653 | + @settings['wave'] = nil | |
654 | + end | |
655 | + if @data.include?(timing + 'wave.loop') | |
656 | + if @data[timing + 'wave.loop'] == 'on' | |
657 | + @settings['wave.loop'] = 1 | |
658 | + else | |
659 | + @settings['wave.loop'] = 0 | |
660 | + end | |
661 | + else | |
662 | + @settings['wave.loop'] = 0 | |
663 | + end | |
664 | + end | |
665 | + | |
666 | + def set_scale(scale) | |
667 | + @__scale = scale | |
668 | + set_surface() | |
669 | + set_position() | |
670 | + end | |
671 | + | |
672 | + def set_position | |
673 | + if @settings['state'] != 'before' | |
674 | + return | |
675 | + end | |
676 | + target_x, target_y = @target.get_surface_position(@side) | |
677 | + target_w, target_h = @target.get_surface_size(@side) | |
678 | + left, top, scrn_w, scrn_h = Pix.get_workarea() | |
679 | + @x = target_x + target_w / 2 - @w / 2 + (@offset_x * @__scale / 100).to_i | |
680 | + @y = top + (@offset_y * @__scale / 100).to_i | |
681 | + @window.move(@x, @y) | |
682 | + end | |
683 | + | |
684 | + def set_surface | |
685 | + path = File.join(@data['dir'], 'surface' + @id.to_s + '.png') | |
686 | + begin | |
687 | + new_surface = Pix.create_surface_from_file(path) | |
688 | + w = [8, (new_surface.width * @__scale / 100).to_i].max | |
689 | + h = [8, (new_surface.height * @__scale / 100).to_i].max | |
690 | + rescue # except: | |
691 | + @parent.handle_request('NOTIFY', 'finalize') | |
692 | + return | |
693 | + end | |
694 | + @w, @h = w, h | |
695 | + @window.update_size(@w, @h) | |
696 | + #self.darea.queue_draw_area(0, 0, self.w, self.h) | |
697 | + @image_surface = new_surface | |
698 | + @darea.queue_draw() | |
699 | + end | |
700 | + | |
701 | + def load(data) | |
702 | + @data = data | |
703 | + @__scale = @target.get_surface_scale() | |
704 | + set_state('before') | |
705 | + if @data.include?('category') | |
706 | + category = @data['category'].split(',') | |
707 | + if category | |
708 | + if not CATEGORY_LIST.include?(category[0]) | |
709 | + logging.warning('WARNING: unknown major category - {0}'.format(category[0])) | |
710 | + ##self.data['category'] = self.CATEGORY_LIST[-1] | |
711 | + end | |
712 | + else | |
713 | + @data['category'] = CATEGORY_LIST[-1] | |
714 | + end | |
715 | + else | |
716 | + @data['category'] = CATEGORY_LIST[-1] | |
717 | + end | |
718 | + if @data.include?('target') | |
719 | + if @data['target'] == 'sakura' | |
720 | + @side = 0 | |
721 | + elsif @data['target'] == 'kero' | |
722 | + @side = 1 | |
723 | + else | |
724 | + @side = 0 # XXX | |
725 | + end | |
726 | + else | |
727 | + @side = 0 # XXX | |
728 | + end | |
729 | + if @parent.handle_request('GET', 'get_mode') == 1 | |
730 | + @parent.handle_request('NOTIFY', 'send_event', 'Emerge') | |
731 | + else | |
732 | + if @data.include?('before.script') | |
733 | + #pass ## FIXME | |
734 | + else | |
735 | + #pass ## FIXME | |
736 | + end | |
737 | + end | |
738 | + set_movement('before') | |
739 | + if @data.include?('before.appear.direction') | |
740 | + #pass ## FIXME | |
741 | + else | |
742 | + #pass ## FIXME | |
743 | + end | |
744 | + if @data.include?('before.appear.ofset.x') | |
745 | + offset_x = @data['before.appear.ofset.x'] | |
746 | + else | |
747 | + offset_x = 0 | |
748 | + end | |
749 | + if offset_x < -32768 | |
750 | + offset_x = -32768 | |
751 | + end | |
752 | + if offset_x > 32767 | |
753 | + offset_x = 32767 | |
754 | + end | |
755 | + if @data.include?('before.appear.ofset.y') | |
756 | + offset_y = @data['before.appear.ofset.y'] | |
757 | + else | |
758 | + offset_y = 0 | |
759 | + end | |
760 | + if offset_y < -32768 | |
761 | + offset_y = -32768 | |
762 | + end | |
763 | + if offset_y > 32767 | |
764 | + offset_y = 32767 | |
765 | + end | |
766 | + @offset_x = offset_x | |
767 | + @offset_y = offset_y | |
768 | + @window = Pix::TransparentWindow.new() | |
769 | + @window.set_title(@data['name']) | |
770 | + @window.set_skip_taskbar_hint(true) # XXX | |
771 | + @window.signal_connect('delete_event') do |w, e| | |
772 | + delete(w, e) | |
773 | + end | |
774 | + @darea = @window.darea #@window.get_child() | |
775 | + @darea.set_events(Gdk::Event::EXPOSURE_MASK) | |
776 | + @darea.signal_connect('draw') do |w, cr| | |
777 | + redraw(w, cr) | |
778 | + end | |
779 | + @window.show() | |
780 | + @id = 0 | |
781 | + set_surface() | |
782 | + set_position() | |
783 | + @loaded = true | |
784 | + end | |
785 | + | |
786 | + def drop ## FIXME | |
787 | + set_state('fall') | |
788 | + end | |
789 | + | |
790 | + def set_state(state) | |
791 | + @settings['state'] = state | |
792 | + @time = 0 | |
793 | + @hit = 0 | |
794 | + @hit_stop = 0 | |
795 | + end | |
796 | + | |
797 | + def update_surface ## FIXME | |
798 | + #pass | |
799 | + end | |
800 | + | |
801 | + def update_position ## FIXME | |
802 | + if @settings['slide.type'] == 'leaf' | |
803 | + #pass | |
804 | + else | |
805 | + if @settings['fall.type'] == 'gravity' | |
806 | + @y += @settings['fall.speed'].to_i * \ | |
807 | + (@time / 20.0)**2 | |
808 | + elsif @settings['fall.type'] == 'evenspeed' | |
809 | + @y += @settings['fall.speed'] | |
810 | + else | |
811 | + #pass | |
812 | + end | |
813 | + if @settings['slide.type'] == 'sinwave' | |
814 | + #pass ## FIXME | |
815 | + else | |
816 | + #pass | |
817 | + end | |
818 | + end | |
819 | + @window.move(@x, @y) | |
820 | + end | |
821 | + | |
822 | + def check_collision ## FIXME: check self position | |
823 | + for side in [0, 1] | |
824 | + target_x, target_y = @target.get_surface_position(side) | |
825 | + target_w, target_h = @target.get_surface_size(side) | |
826 | + center_x = @x + @w / 2 | |
827 | + center_y = @y + @h / 2 | |
828 | + if target_x < center_x and center_x < target_x + target_w and \ | |
829 | + target_y < center_y and center_y < target_y + target_h | |
830 | + @side = side | |
831 | + return 1 | |
832 | + end | |
833 | + end | |
834 | + return 0 | |
835 | + end | |
836 | + | |
837 | + def check_mikire | |
838 | + left, top, scrn_w, scrn_h = Pix.get_workarea() | |
839 | + if @x + @w - @w / 3 > left + scrn_w or \ | |
840 | + @x + @w / 3 < left or \ | |
841 | + @y + @h - @h / 3 > top + scrn_h or \ | |
842 | + @y + @h / 3 < top | |
843 | + return 1 | |
844 | + else | |
845 | + return 0 | |
846 | + end | |
847 | + end | |
848 | + | |
849 | + def update ## FIXME | |
850 | + if @settings['state'] == 'fall' | |
851 | + update_surface() | |
852 | + update_position() | |
853 | + if check_collision() | |
854 | + set_state('hit') | |
855 | + @hit = 1 | |
856 | + if @parent.handle_request('GET', 'get_mode') == 1 | |
857 | + @id = 1 | |
858 | + set_surface() | |
859 | + @parent.handle_request('NOTIFY', 'send_event', 'Hit') | |
860 | + else | |
861 | + #pass ## FIXME | |
862 | + end | |
863 | + end | |
864 | + if check_mikire() | |
865 | + set_state('dodge') | |
866 | + end | |
867 | + elsif @settings['state'] == 'hit' | |
868 | + if @hit_stop >= @data.get('hit.waittime', 0) | |
869 | + set_state('after') | |
870 | + set_movement('after') | |
871 | + if @parent.handle_request('GET', 'get_mode') == 1 | |
872 | + @id = 2 | |
873 | + set_surface() | |
874 | + @parent.handle_request('NOTIFY', 'send_event', 'Drop') | |
875 | + else | |
876 | + #pass ## FIXME | |
877 | + end | |
878 | + else | |
879 | + @hit_stop += 1 | |
880 | + update_surface() | |
881 | + end | |
882 | + elsif @settings['state'] == 'after' | |
883 | + update_surface() | |
884 | + update_position() | |
885 | + if check_mikire() | |
886 | + set_state('end') | |
887 | + end | |
888 | + elsif @settings['state'] == 'end' | |
889 | + if @parent.handle_request('GET', 'get_mode') == 1 | |
890 | + @parent.handle_request('NOTIFY', 'send_event', 'Vanish') | |
891 | + else | |
892 | + #pass ## FIXME | |
893 | + end | |
894 | + @parent.handle_request('NOTIFY', 'delete_katochan') | |
895 | + return false | |
896 | + elsif @settings['state'] == 'dodge' | |
897 | + if @parent.handle_request('GET', 'get_mode') == 1 | |
898 | + @parent.handle_request('NOTIFY', 'send_event', 'Dodge') | |
899 | + else | |
900 | + #pass ## FIXME | |
901 | + end | |
902 | + @parent.handle_request('NOTIFY', 'delete_katochan') | |
903 | + return false | |
904 | + else | |
905 | + ## check collision and mikire | |
906 | + end | |
907 | + @time += 1 | |
908 | + return true | |
909 | + end | |
910 | + end | |
911 | + | |
912 | + class TEST | |
913 | + | |
914 | + def initialize | |
915 | + nekoninni = Home.search_nekoninni() | |
916 | + katochan = Home.search_katochan() | |
917 | + neko = Nekoninni.new | |
918 | + ninni = nekoninni.sample | |
919 | + neko.load(ninni[1], katochan, self) | |
920 | + Gtk.main | |
921 | + end | |
922 | + | |
923 | + def attach_observer(nekoninni) # dummy | |
924 | + end | |
925 | + | |
926 | + def detach_observer(nekoninni) # dummy | |
927 | + end | |
928 | + | |
929 | + def get_surface_scale | |
930 | + return 100 | |
931 | + end | |
932 | + | |
933 | + def get_selfname | |
934 | + return "Sakura" | |
935 | + end | |
936 | + | |
937 | + def get_keroname | |
938 | + return "Kero" | |
939 | + end | |
940 | + | |
941 | + def notify_event(*a) | |
942 | + end | |
943 | + | |
944 | + def get_surface_position(side) | |
945 | + return 0, 0 | |
946 | + end | |
947 | + | |
948 | + def get_surface_size(side) | |
949 | + return 100, 100 | |
950 | + end | |
951 | + | |
952 | + def handle_request(type, event, *a) # dummy | |
953 | + end | |
954 | + end | |
955 | +end | |
956 | + | |
957 | +Nekodorif::TEST.new |