main.rb 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552
  1. # Main eruta script.
  2. #
  3. # This will be executed on start up, and certain well-known functions will be
  4. # called on input events, on timer ticks, or on screen updates.
  5. # Note that, due to muby's syntax, everythg that is defined in this
  6. # file at file scope, will actually be defined at the scope of Object.
  7. # then, though inheritance, every other object will inherit it.
  8. # This may or may not be what you want!!!
  9. # Therefore, it's probably a a good idea to include everything
  10. # except the well-known functions into the module Main below.
  11. #
  12. # The well-known functions that will be called are as follows:
  13. # "on_bump" when a physics collision takes place
  14. # "on_poll" when input events or timer events are received
  15. # "on_start" when the script is loaded the first time
  16. # "on_reload" when the script is reloaded (using the F5 key)
  17. # "on_update" whenever the screen redraw is about to take place
  18. # All these functions should execute quickly so they don't block the Eruta
  19. # engine. They should set the Eruta engine state and let the C side do the
  20. # heavy work.
  21. # Set this to true to start a test map in stead of the normal main menu on startup.
  22. START_TEST_MAP = false
  23. # Play music at startup
  24. PLAY_MUSIC = false
  25. # Show main background
  26. MAIN_BACKGROUND = true
  27. # Helpers
  28. script 'forwardable.rb'
  29. # Registry module
  30. script 'registry.rb'
  31. # Game state
  32. script 'state.rb'
  33. # Load keycodes
  34. script 'keycode.rb'
  35. # Load OO wrappers
  36. script 'timer.rb'
  37. script 'thing.rb'
  38. script 'sprite.rb'
  39. script 'graph.rb'
  40. script 'store.rb'
  41. script 'bitmap.rb'
  42. script 'font.rb'
  43. script 'music.rb'
  44. script 'sound.rb'
  45. script 'tilemap.rb'
  46. # Load UI subsystem
  47. script 'zori.rb'
  48. # Attacks
  49. script 'attack.rb'
  50. # Main menu
  51. script 'mainmenu.rb'
  52. script 'helpers.rb'
  53. puts "Hi from ruby!"
  54. module Main
  55. def ulpcss_load(sprite, layer, name)
  56. full_name = "image/ulpcss/#{name}"
  57. sprite_loadulpcss sprite, layer, full_name
  58. end
  59. def torso_tint(sprite, r, g, b, a = 255)
  60. sprite_tint_rgba sprite, SPRITELAYER_TORSO, r, g, b, a
  61. end
  62. def hair_tint(sprite, r, g, b, a = 255)
  63. sprite_tint_rgba sprite, SPRITELAYER_HAIR, r, g, b, a
  64. end
  65. def start_load_sprites
  66. Timer.make(1.0) do | timer |
  67. puts "In timer tick_tock, total run time: #{timer.total}."
  68. timer.total > 10.0
  69. end
  70. puts "Loading some things and sprites"
  71. player_1 = Thing.make(:player_1, Thing::Kind::PLAYER, 300, 400, 1, 32, 32)
  72. player_1.load_ulpcss(Sprite::Layer::BODY , "body/female/light.png")
  73. player_1.load_ulpcss(Sprite::Layer::TORSO, "torso/dress_female/underdress.png")
  74. player_1.load_ulpcss(Sprite::Layer::HAIR , "hair/female/bangslong.png")
  75. player_1.load_ulpcss(Sprite::Layer::STAFF, "weapons/steelwand_female.png")
  76. player_1.load_ulpcss_slash(Sprite::Layer::BLADE, "weapons/oversize/longsword_female.png")
  77. player_1.load_ulpcss_stab(Sprite::Layer::POLEARM, "weapons/oversize/spear.png")
  78. player_1.load_ulpcss(Sprite::Layer::BOW, "weapons/greatbow.png")
  79. player_1.one_shot_default
  80. player_1.tint_hair(0, 255, 0)
  81. player_1.tint_torso(255, 64, 64)
  82. player_1.direction = Sprite::SOUTH
  83. player_1.pose = Sprite::STAND
  84. player_1.hide_layer(Sprite::Layer::STAFF)
  85. player_1.group = Thing::Kind::PLAYER
  86. # hf = Thing[100].hull_flags= Thing::Flag::DISABLED
  87. # p "set hull flag", hf, Thing[100].hull_flags
  88. Thing.make(:player_2, Thing::Kind::FOE, 400, 400, 1, 32, 32)
  89. Sprite.make(:s101)
  90. Sprite[:s101].load_ulpcss(Sprite::Layer::BODY , "body/female/orc.png")
  91. Sprite[:s101].load_ulpcss(Sprite::Layer::TORSO, "torso/dress_w_sash_female.png")
  92. # Sprite[:s101].load_ulpcss(Sprite::Layer::HAIR , "hair/female/bangsshort.png")
  93. Thing[:player_2].sprite = Sprite[:s101]
  94. Thing[:player_2].tint_hair(255, 255, 0)
  95. Thing[:player_2].tint_torso(128, 128, 255)
  96. Thing[:player_2].pose = Sprite::STAND
  97. Thing[:player_2].direction = Sprite::SOUTH
  98. player_1.group = Thing::Kind::FOE
  99. puts "Things and sprites loaded."
  100. end
  101. def start_load_stuff
  102. end
  103. def start_load_tilemap
  104. tilemap_fn = 'map/map_0001_tiled11.tmx'
  105. tilemap = Tilemap.load(:map_0001, tilemap_fn)
  106. State.tilemap_name = :map_0001
  107. tilemap.activate!()
  108. end
  109. def reload_tilemap
  110. Tilemap.reload
  111. end
  112. def do_start_test_map
  113. start_load_tilemap
  114. start_load_sprites
  115. start_load_stuff
  116. # start_setup_ui
  117. actor_switch(Thing[:player_1])
  118. end
  119. #
  120. # def load_main_menu
  121. # $main_music = Store.load_audio_stream('/music/hiver_com64.ogg')
  122. # $main_back = Store.load_bitmap('/image/background/eruta_mainmenu.png')
  123. # $main_graph = Graph.make_image(0, 0, $main_back.id)
  124. # Eruta::Audio.music_id = $main_music.id
  125. # res = Eruta::Audio.play_music
  126. # puts "play_music #{res} #{$main_music.id}"
  127. # end
  128. POEM_TEXT = "After mankind's summer, winter suddenly came" +
  129. "Most reached for the stars, but some remained" +
  130. "The earth scarred by the Ancients unhostpitable" +
  131. "The hopes low, and so much wisdom forgotten" +
  132. "Yet when it seemed the last hour struck" +
  133. "Our hope returned, a savior arose" +
  134. "Lord Kei, who by Creator's grace" +
  135. "Restored the Trees that guard us all from harm" +
  136. "Thus ushered in a new millennial spring" +
  137. "Lord Kei, watch over us and protect us" +
  138. "Until the Travellers return with bounty of the stars"
  139. INTRO_TEXT = "Millennia have passed since mankind first traveled to the moon. "+
  140. "Civilizations rose as never before, yet to fall again. " +
  141. "When all hope seemed lost, the 21 trees sprouted from the earth. " +
  142. "They brought mysterious powers that protected and strengthened mankind. "+
  143. "Hi!\n\n" +
  144. "Hello µ world, this is me, 無 life should be\nfun for everyone!"
  145. INTRO_TEXT2 = "Something happened, and I was changed!"
  146. def make_sub_menu(parent, x, y, w, h, bh)
  147. sub_menu = parent.make_menu(x, y, w, h, nil)
  148. sub_menu.make_button(x, y + 20, w - 20, bh, "Sub choice 1") do
  149. puts "choice 1"
  150. end
  151. sub_menu.make_button(x, y + 30 + bh, w - 20, bh, "Sub choice 2") do
  152. puts "choice 2"
  153. end
  154. sub_menu.make_button(x, y + 40 + 2* bh, w - 20, bh, "Sub choice 3") do
  155. puts "choice 3"
  156. end
  157. sub_menu.fit_children
  158. sub_menu.hide
  159. return sub_menu
  160. end
  161. def do_main_menu
  162. main_music = Music.load(:main, '/music/nethis-the_writer.ogg')
  163. $lote = nil
  164. $lobe = nil
  165. if PLAY_MUSIC
  166. res = main_music.play!
  167. end
  168. # res = nil
  169. # $main_menu = MainMenu.new
  170. # $main_menu.active = true
  171. Zori.make_page(:default) do |m|
  172. State.talk_box = m.make_longtext(10, 310, 620, 160, "Testing 1 2 3")
  173. State.talk_box.graph.each { |g| g.font = Eruta::Zori.font.id }
  174. State.talk_box.delay = 0.1
  175. State.talk_box.page_lines = 5
  176. State.talk_box.page = 0
  177. State.talk_box.hide
  178. end
  179. Zori.make_page(:main_menu) do |m|
  180. if MAIN_BACKGROUND
  181. main_back = Bitmap.load(:main_back,
  182. '/image/background/eruta_mainmenu.png')
  183. p main_back, main_back.width, main_back.height, main_back.name, main_back.id
  184. main_back = Bitmap.load(:main_back,
  185. '/image/background/eruta_mainmenu.png')
  186. p main_back, main_back.width, main_back.height, main_back.name, main_back.id
  187. p Bitmap[:main_back]
  188. p Store[:bitmap_main_back]
  189. m.graph_image(0, 0, main_back.id)
  190. end
  191. main_menu = m.make_menu(250, 190, 120, 440, nil)
  192. ma = main_menu
  193. main_button_1 = ma.make_button(260, 200, 100, 30, "Continue")
  194. sub_menu = make_sub_menu(main_button_1, 50, 190, 120, 440, 30)
  195. sub_menu.hide
  196. main_button_2 = ma.make_button(260, 240, 100, 30, "New") do
  197. do_start_test_map
  198. Zori.go(:default)
  199. end
  200. main_button_3 = ma.make_button(260, 280, 100, 30, "Settings") do
  201. Zori.go(:settings)
  202. end
  203. main_button_4 = ma.make_button(260, 320, 100, 30, "Instructions")
  204. main_button_5 = ma.make_button(260, 360, 100, 30, "µ£éè")
  205. main_button_5 << sub_menu
  206. main_button_5= ma.make_button(260, 400, 100, 30, "Quit") do
  207. Eruta.quit
  208. end
  209. main_menu.fit_children
  210. end
  211. Zori.make_page(:settings) do |se|
  212. lote2 = se.make_longtext(100, 10, 160, 100, INTRO_TEXT)
  213. lote2.delay = 1
  214. lote2.page = 0
  215. lote2.page_lines = 3
  216. # $lote2.line_stop = 999990
  217. settings_menu = se.make_menu(480, 380, 120, 440, nil)
  218. sm = settings_menu
  219. settings_ok_button = sm.make_button(500, 300, 100, 30, "Font 1") do
  220. if lote2
  221. lote2.graph.each { |g| g.font = 0 }
  222. end
  223. end
  224. settings_ok_button = sm.make_button(500, 350, 100, 30, "Font 2") do
  225. if lote2
  226. lote2.graph.each { |g| g.font = Eruta::Zori.font.id }
  227. end
  228. lote2.text = INTRO_TEXT2
  229. end
  230. settings_ok_button = sm.make_button(500, 400, 100, 30, "OK") do
  231. Zori.go(:main_menu)
  232. if lote
  233. lote.close
  234. lote = nil
  235. end
  236. end
  237. sm.fit_children
  238. end
  239. Zori[:main_menu].hide
  240. Zori[:settings].hide
  241. Zori.go(:main_menu)
  242. end
  243. def ok!
  244. log "OK, it works!"
  245. end
  246. def actor_switch(new_actor)
  247. # Ensure the old actor stops walking.
  248. new_actor.v = [ 0.0, 0.0 ]
  249. Thing.actor = new_actor
  250. new_actor.track
  251. return new_actor
  252. end
  253. def actor_continue_talk
  254. if State.talk_box.at_end?
  255. State.talk_box.hide
  256. State.talk_with = nil
  257. p "Talk ended."
  258. elsif State.talk_box.paused
  259. res = (State.talk_box.next_page)
  260. p "Continue_talk", res
  261. else
  262. State.talk_box.delay = 0.05
  263. p "Talking speed up"
  264. # speed up talk here?
  265. end
  266. end
  267. def actor_start_search
  268. p "starting search"
  269. found = Thing.actor.find_in_front(60, 60)
  270. unless found.empty?
  271. first = found.first
  272. State.talk_box.delay = 0.05
  273. State.talk_box.show
  274. State.talk_box.text = "Let's make this text a bit longer\n
  275. Hi there, I'm #{first.id}!"
  276. State.talk_with = first
  277. end
  278. end
  279. # Searches and interacts with things in front of the actor.
  280. # Or if already talking/reading, continue with it.
  281. def actor_search_or_talk
  282. return if !Thing.actor
  283. if State.talk_with
  284. actor_continue_talk
  285. else
  286. actor_start_search
  287. end
  288. end
  289. # Searches and interacts with things in front of the actor.
  290. # Or if already talking/reading, continue with it.
  291. def actor_attack
  292. return if !Thing.actor
  293. Thing.actor.v = [ 0, 0 ]
  294. pose = Thing.actor.pose
  295. Thing.actor.one_shot_action(Sprite::SLASH)
  296. Thing.actor.pose = Sprite::SLASH
  297. Attack.make(Thing.actor, 64, 64)
  298. end
  299. # Show a reminder of the key bindings on stdout
  300. def show_keybindings
  301. puts "Keybindings:"
  302. puts "A: Switch to actor A."
  303. puts "B: Switch to actor B."
  304. puts "E: REload currently active tile map."
  305. puts "F: Toggle the visibility of the FPS counter"
  306. puts "G: Toggle the visibility of the 2D scene Graph"
  307. puts "H: Hide 2D scene graph GUI"
  308. puts "N: Disable tile map."
  309. puts "P: Toggle the visibility of the area/physics bounds boxes."
  310. puts "R: Toggle the visibility of the area spRites."
  311. puts "S: Show 2D scene graph GUI"
  312. puts "Arrow keys: Move active actor."
  313. puts "Shift keys: use weapon."
  314. puts "Space: search in front of active actor or continue talking."
  315. end
  316. # Handle key down
  317. def on_key_down(time, key)
  318. State.last_key_time = time
  319. State.last_key = key
  320. p "on_key_down #{time} #{key}"
  321. actor = Thing.actor
  322. return nil unless actor
  323. vx, vy = * actor.v
  324. case key
  325. when KEY_A
  326. actor_switch(Thing[:player_1])
  327. when KEY_B
  328. actor_switch(Thing[:player_2])
  329. when KEY_E
  330. reload_tilemap()
  331. when KEY_H
  332. Eruta::Graph.visible_(52, 0)
  333. when KEY_M
  334. active_map_($tilemap_id)
  335. when KEY_N
  336. active_map_(-1)
  337. when KEY_S
  338. Eruta::Graph.visible_(52, 1)
  339. when KEY_F
  340. Eruta.show_fps= !Eruta.show_fps
  341. when KEY_R
  342. Eruta.show_area= !Eruta.show_area
  343. when KEY_G
  344. Eruta.show_graph= !Eruta.show_graph
  345. when KEY_P
  346. Eruta.show_physics= !Eruta.show_physics
  347. when KEY_UP
  348. vy -= 100.0
  349. actor.v = [ vx, vy ]
  350. when KEY_DOWN
  351. vy += 100.0
  352. actor.v = [ vx, vy ]
  353. when KEY_LEFT
  354. vx -= 100.0
  355. actor.v = [ vx, vy ]
  356. when KEY_RIGHT
  357. vx += 100.0
  358. actor.v = [ vx, vy ]
  359. when KEY_D
  360. Thing.actor.pose = Sprite::DOWN
  361. when KEY_SPACE
  362. puts "Searching..."
  363. actor_search_or_talk
  364. when KEY_LSHIFT, KEY_RSHIFT
  365. puts "attacking"
  366. actor_attack
  367. actor.v = [ 0, 0 ]
  368. when KEY_COMMA
  369. show_keybindings
  370. else
  371. end
  372. return nil
  373. end
  374. # Handle key up. XXX: maybe refactor this to a game class or module???
  375. def on_key_up(time, key)
  376. actor = Thing.actor
  377. return nil unless actor
  378. vx, vy = * actor.v
  379. case key
  380. when KEY_UP
  381. vy = 0.0
  382. actor.v = [vx, vy]
  383. when KEY_DOWN
  384. vy = 0.0
  385. actor.v = [vx, vy]
  386. when KEY_LEFT
  387. vx = 0.0
  388. actor.v = [vx, vy]
  389. when KEY_RIGHT
  390. vx = 0.0
  391. actor.v = [vx, vy]
  392. when KEY_SPACE
  393. State.talk_box.delay = 0.1 if State.talk_with
  394. else
  395. end
  396. return nil
  397. end
  398. # Make all methods and constants are available as Main.XXX
  399. extend self
  400. end # module Main
  401. ####################################
  402. # Below are the entry point functions that will be called by the Eruta engine
  403. # at certain events or times.
  404. ####################################
  405. # Called when the main.rb script is loaded for the first time.
  406. def eruta_on_start(*args)
  407. puts "on_start #{args}"
  408. State.time_start = Eruta.time
  409. Zori.open
  410. Main.do_main_menu()
  411. return :ok
  412. end
  413. # Called when the main.rb script is reloaded (through the F5 key).
  414. def eruta_on_reload(*args)
  415. puts "on_reload #{args}"
  416. puts "load thing ok: #{Thing.methods - Object.methods}"
  417. puts "load thing ok: #{Eruta::Thing.methods - Object.methods}"
  418. return :ok
  419. end
  420. # Called on a physics collision.
  421. def eruta_on_bump(t1, t2, h1, h2, kind = nil)
  422. if kind == 1 # Begin of collision
  423. # puts "Begin collision!"
  424. elsif kind == 2 # Collision active
  425. thing1 = Thing[t1]
  426. thing2 = Thing[t2]
  427. if thing1 && thing2
  428. thing1.on_bump(thing2, h1, h2)
  429. else
  430. puts "Colliding! #{t1}<->#{t2} (#{h1}<->#{h2})"
  431. end
  432. elsif kind == 3 # Collision done
  433. puts "Collision done!"
  434. else
  435. puts "Unknown collision type shouldn't happen!"
  436. end
  437. return true
  438. end
  439. # Called on certain sprite events (end of one shot animation)
  440. def eruta_on_sprite(spriteid, thingid, pose, direction, kind = nil)
  441. thing = Thing[thingid]
  442. if thing
  443. thing.on_sprite(spriteid, pose, direction, kind)
  444. end
  445. puts "Sprite event: #{spriteid} #{thingid} #{pose} #{direction} #{kind}."
  446. end
  447. # Called on an update tick, just before drawing to the screen.
  448. def eruta_on_update(dt)
  449. # Update the timers
  450. Timer.update
  451. end
  452. # Called when an input event occurs.
  453. def eruta_on_poll(*args)
  454. # Send to Zori ui first. If it returns non-nil the event is handled,
  455. # otherwise, pass on to key handler.
  456. res = Zori.on_event(*args)
  457. if res
  458. return nil
  459. else
  460. type = args.shift
  461. meth = "on_#{type}".to_sym
  462. if Main.respond_to?(meth)
  463. Main.send(meth, *args)
  464. else
  465. # ignore
  466. log_to "input", "#{__FILE__.split('/').last}:#{__LINE__}: input: #{type} #{meth} #{args}"
  467. # puts "input #{type} #{meth} #{args}"
  468. end
  469. end
  470. end