Advertisement
Guest User

Untitled

a guest
Mar 7th, 2019
173
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 43.64 KB | None | 0 0
  1. # ===================================================================
  2. #
  3. # Script: Component_MessageTextRenderer
  4. #
  5. # $$COPYRIGHT$$
  6. #
  7. # ===================================================================
  8. class Component_MessageTextRenderer extends gs.Component_TextRenderer
  9. @objectCodecBlackList = ["onLinkClick", "onBatchDisappear"]
  10. ###*
  11. * Called if this object instance is restored from a data-bundle. It can be used
  12. * re-assign event-handler, anonymous functions, etc.
  13. *
  14. * @method onDataBundleRestore.
  15. * @param Object data - The data-bundle
  16. * @param gs.ObjectCodecContext context - The codec-context.
  17. ###
  18. onDataBundleRestore: (data, context) ->
  19. @setupEventHandlers()
  20. l = 0
  21. lastLine = null
  22.  
  23. for message in @object.messages
  24. if @object.settings.useCharacterColor
  25. @object.font.color = new gs.Color(message.character.textColor)
  26.  
  27. lineWidth = if @isRunningInMultiPartMode() then lastLine?.width || 0 else 0
  28. @lines = @calculateLines(lcsm(message.text), yes, lineWidth)
  29. for line in @lines
  30. bitmap = @createBitmap(line)
  31. if line == @line
  32. @drawLineContent(line, bitmap, @charIndex+1)
  33. else
  34. @drawLineContent(line, bitmap, -1)
  35. @allSprites[l].bitmap = bitmap
  36. lastLine = line
  37. l++
  38.  
  39. for customObject in @customObjects
  40. SceneManager.scene.addObject(customObject)
  41.  
  42. return null
  43.  
  44. ###*
  45. * A text-renderer component to render an animated and interactive message text using
  46. * dimensions of the game object's destination-rectangle. The message is displayed
  47. * using a sprite for each line instead of drawing to the game object's bitmap object.
  48. *
  49. * @module gs
  50. * @class Component_MessageTextRenderer
  51. * @extends gs.Component_TextRenderer
  52. * @memberof gs
  53. * @constructor
  54. ###
  55. constructor: ->
  56. super
  57.  
  58. ###*
  59. * An array containing all sprites of the current message.
  60. * @property sprites
  61. * @type gs.Sprite[]
  62. * @protected
  63. ###
  64. @sprites = []
  65.  
  66. ###*
  67. * An array containing all sprites of all messages. In NVL mode
  68. * a page can contain multiple messages.
  69. * @property allSprites
  70. * @type gs.Sprite[]
  71. * @protected
  72. ###
  73. @allSprites = []
  74. ###*
  75. * An array containing all line-objects of the current message.
  76. * @property lines
  77. * @type gs.TextRendererLine[]
  78. * @readOnly
  79. ###
  80. @lines = null
  81.  
  82. ###*
  83. * The line currently rendered.
  84. * @property line
  85. * @type number
  86. * @readOnly
  87. ###
  88. @line = 0
  89.  
  90. ###*
  91. * The left and right padding per line.
  92. * @property padding
  93. * @type number
  94. ###
  95. @padding = 6
  96.  
  97. ###*
  98. * The minimum height of the line currently rendered. If 0, the measured
  99. * height of the line will be used.
  100. * @property minLineHeight
  101. * @type number
  102. ###
  103. @minLineHeight = 0
  104.  
  105. ###*
  106. * The spacing between text lines in pixels.
  107. * @property lineSpacing
  108. * @type number
  109. ###
  110. @lineSpacing = 2
  111.  
  112. ###*
  113. * The line currently rendered.
  114. * @property currentLine
  115. * @type number
  116. * @protected
  117. ###
  118. @currentLine = 0
  119.  
  120. ###*
  121. * The height of the line currently rendered.
  122. * @property currentLineHeight
  123. * @type number
  124. * @protected
  125. ###
  126. @currentLineHeight = 0
  127.  
  128. ###*
  129. * Index of the current character to draw.
  130. * @property charIndex
  131. * @type number
  132. * @readOnly
  133. ###
  134. @charIndex = 0
  135.  
  136. ###*
  137. * Position of the message caret. The caret is like an invisible
  138. * cursor pointing to the x/y coordinates of the last rendered character of
  139. * the message. That position can be used to display a waiting- or processing-animation for example.
  140. * @property caretPosition
  141. * @type gs.Point
  142. * @readOnly
  143. ###
  144. @caretPosition = new gs.Point()
  145.  
  146. ###*
  147. * Indicates that the a message is currently in progress.
  148. * @property isRunning
  149. * @type boolean
  150. * @readOnly
  151. ###
  152. @isRunning = no
  153.  
  154. ###*
  155. * The current x-coordinate of the caret/cursor.
  156. * @property currentX
  157. * @type number
  158. * @readOnly
  159. ###
  160. @currentX = 0
  161.  
  162. ###*
  163. * The current y-coordinate of the caret/cursor.
  164. * @property currentY
  165. * @type number
  166. * @readOnly
  167. ###
  168. @currentY = 0
  169.  
  170. ###*
  171. * The current sprites used to display the current text-line/part.
  172. * @property currentSprite
  173. * @type gs.Sprite
  174. * @readOnly
  175. ###
  176. @currentSprite = null
  177.  
  178. ###*
  179. * Indicates if the message-renderer is currently waiting like for a user-action.
  180. * @property isWaiting
  181. * @type boolean
  182. * @readOnly
  183. ###
  184. @isWaiting = no
  185.  
  186. ###*
  187. * Indicates if the message-renderer is currently waiting for a key-press or mouse/touch action.
  188. * @property waitForKey
  189. * @type boolean
  190. * @readOnly
  191. ###
  192. @waitForKey = no
  193.  
  194. ###*
  195. * Number of frames the message-renderer should wait before continue.
  196. * @property waitCounter
  197. * @type number
  198. ###
  199. @waitCounter = 0
  200.  
  201. ###*
  202. * Speed of the message-drawing. The smaller the value, the faster the message is displayed.
  203. * @property speed
  204. * @type number
  205. ###
  206. @speed = 1
  207.  
  208. ###*
  209. * Indicates if the message should be rendered immedialtely without any animation or delay.
  210. * @property drawImmediately
  211. * @type boolean
  212. ###
  213. @drawImmediately = no
  214.  
  215. ###*
  216. * Indicates if the message should wait for a user-action or a certain amount of time
  217. * before finishing.
  218. * @property waitAtEnd
  219. * @type boolean
  220. ###
  221. @waitAtEnd = yes
  222.  
  223. ###*
  224. * The number of frames to wait before finishing a message.
  225. * before finishing.
  226. * @property waitAtEndTime
  227. * @type number
  228. ###
  229. @waitAtEndTime = 0
  230.  
  231. ###*
  232. * Indicates if auto word-wrap should be used. Default is <b>true</b>
  233. * @property wordWrap
  234. * @type boolean
  235. ###
  236. @wordWrap = yes
  237.  
  238. ###*
  239. * Custom game objects which are alive until the current message is erased. Can be used to display
  240. * animated icons, etc.
  241. * @property customObjects
  242. * @type gs.Object_Base[]
  243. ###
  244. @customObjects = []
  245.  
  246. ###*
  247. * A hashtable/dictionary object to store custom-data useful like for token-processing. The data must be
  248. * serializable.
  249. * @property customObjects
  250. * @type Object
  251. ###
  252. @customData = {}
  253.  
  254. ###*
  255. * A callback function called if the player clicks on a non-stylable link (LK text-code) to trigger
  256. * the specified common event.
  257. * @property onLinkClick
  258. * @type Function
  259. ###
  260. @onLinkClick = (e) ->
  261. eventId = e.data.linkData.commonEventId
  262. event = RecordManager.commonEvents[eventId]
  263. if !event
  264. event = RecordManager.commonEvents.first (x) => x.name == eventId
  265. eventId = event.index if event
  266. if !event
  267. SceneManager.scene.interpreter.jumpToLabel(eventId)
  268. else
  269. SceneManager.scene.interpreter.callCommonEvent(eventId, null, yes)
  270.  
  271. ###*
  272. * A callback function called if a batched messsage has been faded out. It triggers the execution of
  273. * the next message.
  274. * @property onBatchDisappear
  275. * @type Function
  276. ###
  277. @onBatchDisappear = (e) =>
  278. @drawImmediately = no
  279. @isWaiting = no
  280. @object.opacity = 255
  281. @executeBatch()
  282.  
  283.  
  284. ###*
  285. * Serializes the message text-renderer into a data-bundle.
  286. * @method toDataBundle
  287. * @return {Object} A data-bundle.
  288. ###
  289. toDataBundle: ->
  290. ignore = ["object", "font", "sprites", "allSprites", "currentSprite", "currentX"]
  291. bundle = { currentSpriteIndex: @sprites.indexOf(@currentSprite) }
  292.  
  293. for k of this
  294. if ignore.indexOf(k) == -1
  295. bundle[k] = this[k]
  296.  
  297. return bundle
  298.  
  299.  
  300.  
  301. ###*
  302. * Disposes the message text-renderer and all sprites used to display
  303. * the message.
  304. * @method dispose
  305. ###
  306. dispose: ->
  307. super
  308.  
  309. @disposeEventHandlers()
  310.  
  311. for sprite in @allSprites
  312. sprite.bitmap?.dispose()
  313. sprite.dispose()
  314.  
  315. ###*
  316. * Removes all attached event handlers
  317. * the message.
  318. * @method disposeEventHandlers
  319. ###
  320. disposeEventHandlers: ->
  321. gs.GlobalEventManager.offByOwner("mouseUp", @object)
  322. gs.GlobalEventManager.offByOwner("keyUp", @object)
  323.  
  324. ###*
  325. * Adds event-handlers for mouse/touch events
  326. *
  327. * @method setupEventHandlers
  328. ###
  329. setupEventHandlers: ->
  330. gs.GlobalEventManager.offByOwner("mouseUp", @object)
  331. gs.GlobalEventManager.offByOwner("keyUp", @object)
  332.  
  333. gs.GlobalEventManager.on "mouseUp", ((e) =>
  334. return if @object.findComponentByName("animation") or (GameManager.settings.autoMessage.enabled and !GameManager.settings.autoMessage.stopOnAction)
  335. return if gs.GameManager.isCatWait();
  336. #if @object.dstRect.contains(Input.Mouse.x - @object.origin.x, Input.Mouse.y - @object.origin.y)
  337. if @isWaiting and not (@waitCounter > 0 or @waitForKey)
  338. e.breakChain = yes
  339. @continue()
  340. else
  341. e.breakChain = @isRunning
  342. @drawImmediately = !@waitForKey
  343. @waitCounter = 0
  344. @waitForKey = no
  345. @isWaiting = no
  346.  
  347. if @waitForKey
  348. if Input.Mouse.buttons[Input.Mouse.LEFT] == 2
  349. e.breakChain = yes
  350. Input.clear()
  351. @waitForKey = no
  352. @isWaiting = no
  353.  
  354.  
  355.  
  356.  
  357. ), null, @object
  358.  
  359. gs.GlobalEventManager.on "keyUp", ((e) =>
  360. return if gs.GameManager.isCatWait();
  361. if Input.keys[Input.C] and (!@isWaiting or (@waitCounter > 0 or @waitForKey))
  362. @drawImmediately = !@waitForKey
  363. @waitCounter = 0
  364. @waitForKey = no
  365. @isWaiting = no
  366.  
  367. if @isWaiting and !@waitForKey and !@waitCounter and Input.keys[Input.C]
  368. @continue()
  369.  
  370. if @waitForKey
  371. if Input.keys[Input.C]
  372. Input.clear()
  373. @waitForKey = no
  374. @isWaiting = no
  375.  
  376. ), null, @object
  377.  
  378. ###*
  379. * Sets up the renderer. Registers necessary event handlers.
  380. * @method setup
  381. ###
  382. setup: ->
  383. @setupEventHandlers()
  384.  
  385. ###*
  386. * Restores the message text-renderer's state from a data-bundle.
  387. * @method restore
  388. * @param {Object} bundle - A data-bundle containing message text-renderer state.
  389. ###
  390. restore: (bundle) ->
  391. for k of bundle
  392. if k == "currentSpriteIndex"
  393. @currentSprite = @sprites[bundle.currentSpriteIndex]
  394. else
  395. this[k] = bundle[k]
  396.  
  397. if @sprites.length > 0
  398. @currentY = @sprites.last().y - @object.origin.y - @object.dstRect.y
  399. @line = @maxLines
  400. @isWaiting = @isWaiting || @isRunning
  401.  
  402. return null
  403.  
  404.  
  405. ###*
  406. * Continues message-processing if currently waiting.
  407. * @method continue
  408. ###
  409. continue: ->
  410. @isWaiting = no
  411.  
  412. if @line >= @lines.length
  413. @isRunning = no
  414. @object.events?.emit("messageFinish", this)
  415. else
  416. @object.events?.emit("messageBatch", this)
  417. fading = GameManager.tempSettings.messageFading
  418. duration = if GameManager.tempSettings.skip then 0 else fading.duration
  419. @object.animator.disappear(fading.animation, fading.easing, duration, gs.CallBack("onBatchDisappear", this))
  420.  
  421. ###*
  422. * Updates the text-renderer.
  423. * @method update
  424. ###
  425. update: ->
  426. for sprite in @allSprites
  427. sprite.opacity = @object.opacity
  428. sprite.visible = @object.visible
  429. sprite.ox = -@object.offset.x
  430. sprite.oy = -@object.offset.y
  431. sprite.mask.value = @object.mask.value
  432. sprite.mask.vague = @object.mask.vague
  433. sprite.mask.source = @object.mask.source
  434. sprite.mask.type = @object.mask.type
  435.  
  436. for object in @customObjects
  437. object.opacity = @object.opacity
  438. object.visible = @object.visible
  439.  
  440. if not @isRunning and @waitCounter > 0 and !gs.GameManager.isCatWait()
  441. @waitCounter--
  442. if @waitCounter == 0
  443. @continue()
  444. return
  445.  
  446. if @object.visible and @lines?.length > 0
  447. @updateLineWriting()
  448. @updateWaitForKey()
  449. @updateWaitCounter()
  450. @updateCaretPosition()
  451.  
  452.  
  453. ###*
  454. * Indicates if its a batched messages.
  455. *
  456. * @method isBatched
  457. * @return If <b>true</b> it is a batched message. Otherwise <b>false</b>.
  458. ###
  459. isBatched: -> @lines.length > @maxLines
  460.  
  461. ###*
  462. * Indicates if the batch is still in progress and not done.
  463. *
  464. * @method isBatchInProgress
  465. * @return If <b>true</b> the batched message is still not done. Otherwise <b>false</b>
  466. ###
  467. isBatchInProgress: -> @lines.length - @line > @maxLines
  468.  
  469. ###*
  470. * Indicates if the renderer runs in multi-part message mode which
  471. * means that a single message might be constructed from multiple drawFormattedText
  472. * calls.
  473. *
  474. * @method isRunningInMultiPartMode
  475. * @return If <b>true</b> the renderer runs in multi-part mode. Otherwise <b>false</b>.
  476. ###
  477. isRunningInMultiPartMode: -> !@object.settings.autoErase and @object.settings.paragraphSpacing <= 0
  478.  
  479. ###*
  480. * Starts displaying the next page of text if a message is too long to fit
  481. * into one message box.
  482. *
  483. * @method executeBatch
  484. ###
  485. executeBatch: ->
  486. @clearAllSprites()
  487. @lines = @lines.slice(@line)
  488. @line = 0
  489. @currentX = 0
  490. @currentY = 0
  491. @currentLineHeight = 0
  492. @tokenIndex = 0
  493. @charIndex = 0
  494. @token = @lines[@line].content[@tokenIndex] || new gs.RendererToken(null, "");
  495. @maxLines = @calculateMaxLines(@lines)
  496. @lineAnimationCount = @speed
  497. @sprites = @createSprites(@lines)
  498. @allSprites = @allSprites.concat(@sprites)
  499. @currentSprite = @sprites[@line]
  500. @currentSprite.x = @currentX + @object.origin.x + @object.dstRect.x
  501. @drawNext()
  502.  
  503. ###*
  504. * Calculates the duration(in frames) the message-renderer needs to display
  505. * the message.
  506. *
  507. * @method calculateDuration
  508. * @return {number} The duration in frames.
  509. ###
  510. calculateDuration: ->
  511. duration = 0
  512.  
  513. if @lines?
  514. for line in @lines
  515. for token in line.content
  516. if token?
  517. duration += @calculateDurationForToken(token)
  518. return duration
  519.  
  520. ###*
  521. * Calculates the duration(in frames) the message-renderer needs to display
  522. * the specified line.
  523. *
  524. * @method calculateDurationForLine
  525. * @param {gs.RendererTextLine} line The line to calculate the duration for.
  526. * @return {number} The duration in frames.
  527. ###
  528. calculateDurationForLine: (line) ->
  529. duration = 0
  530.  
  531. if line
  532. for token in line.content
  533. if token?
  534. duration += @calculateDurationForToken(token)
  535.  
  536. return duration
  537.  
  538. ###*
  539. * Calculates the duration(in frames) the message-renderer needs to process
  540. * the specified token.
  541. *
  542. * @method calculateDurationForToken
  543. * @param {string|Object} token - The token.
  544. * @return {number} The duration in frames.
  545. ###
  546. calculateDurationForToken: (token) ->
  547. duration = 0
  548.  
  549. if token.code?
  550. switch token.code
  551. when "W"
  552. if token.value != "A"
  553. duration = token.value / 1000 * Graphics.frameRate
  554. else
  555. duration = token.value.length * @speed
  556.  
  557. return duration
  558.  
  559. ###*
  560. * Calculates the maximum of lines which can be displayed in one message.
  561. *
  562. * @method calculateMaxLines
  563. * @param {Array} lines - An array of line-objects.
  564. * @return {number} The number of displayable lines.
  565. ###
  566. calculateMaxLines: (lines) ->
  567. height = 0
  568. result = 0
  569.  
  570. for line in lines
  571. height += line.height + @lineSpacing
  572. if @currentY+height > (@object.dstRect.height)
  573. break
  574. result++
  575.  
  576. return Math.min(lines.length, result || 1)
  577.  
  578. ###*
  579. * Displays the character or processes the next control-token.
  580. *
  581. * @method drawNext
  582. ###
  583. drawNext: ->
  584. token = @processToken()
  585.  
  586. if token?.value.length > 0
  587. @char = @token.value.charAt(@charIndex)
  588.  
  589. size = @font.measureTextPlain(@char)
  590. lineSpacing = @lineSpacing
  591.  
  592. if @currentLine != @line
  593. @currentLine = @line
  594. # @currentY += @currentLineHeight + lineSpacing * Graphics.scale
  595. @currentLineHeight = 0
  596.  
  597. @currentSprite.y = @object.origin.y + @object.dstRect.y + @currentY
  598. @currentSprite.visible = yes
  599. @drawLineContent(@lines[@line], @currentSprite.bitmap, @charIndex+1)
  600. @currentSprite.srcRect.width = @currentSprite.bitmap.width #Math.min(@currentSprite.srcRect.width + size.width, @currentSprite.bitmap.width)
  601.  
  602. @currentLineHeight = @lines[@line].height
  603. @currentX = Math.min(@lines[@line].width, @currentX + size.width)
  604.  
  605. ###*
  606. * Processes the next character/token of the message.
  607. * @method nextChar
  608. * @private
  609. ###
  610. nextChar: ->
  611. loop
  612. @charIndex++
  613. @lineAnimationCount = @speed
  614.  
  615. if @token.code? or @charIndex >= @token.value.length
  616. @token.onEnd?()
  617. @tokenIndex++
  618. if @tokenIndex >= @lines[@line].content.length
  619. @tokenIndex = 0
  620. @line++
  621. @currentSprite.srcRect.width = @currentSprite.bitmap.width
  622. @currentSprite = @sprites[@line]
  623. if @currentSprite?
  624. @currentSprite.x = @object.origin.x + @object.dstRect.x
  625. if @line < @maxLines
  626. @currentY += (@currentLineHeight || @font.lineHeight) + @lineSpacing * Graphics.scale
  627. @charIndex = 0
  628. @currentX = 0
  629. @token = @lines[@line].content[@tokenIndex] || new gs.RendererToken(null, "")
  630. else
  631. @charIndex = 0
  632. @token = @lines[@line].content[@tokenIndex] || new gs.RendererToken(null, "")
  633. @token.onStart?()
  634.  
  635.  
  636. if !@token or @token.value != "\n" or !@lines[@line]
  637. break
  638. ###*
  639. * Finishes the message. Depending on the message configuration, the
  640. * message text-renderer will now wait for a user-action or a certain amount
  641. * of time.
  642. *
  643. * @method finish
  644. ###
  645. finish: ->
  646. if @waitAtEnd
  647. @isWaiting = yes
  648. @object.events?.emit("messageWaiting", this)
  649. else if @waitAtEndTime > 0
  650. @waitCounter = @waitAtEndTime
  651. @isWaiting = no
  652.  
  653. @object.events?.emit("messageWaiting", this)
  654. else
  655. @object.events?.emit("messageWaiting", this)
  656. @continue()
  657.  
  658. ###*
  659. * Returns the position of the caret in pixels. The caret is like an invisible
  660. * cursor pointing to the x/y coordinates of the last rendered character of
  661. * the message. That position can be used to display a waiting- or processing-animation for example.
  662. *
  663. * @method updateCaretPosition
  664. ###
  665. updateCaretPosition: ->
  666. @caretPosition.x = @currentX + @padding
  667. @caretPosition.y = @currentY + @currentLineHeight/2
  668.  
  669. ###*
  670. * Updates the line writing.
  671. *
  672. * @method updateLineWriting
  673. * @private
  674. ###
  675. updateLineWriting: ->
  676. if @isRunning and !@isWaiting and !@waitForKey and @waitCounter <= 0
  677. if @lineAnimationCount <= 0
  678. loop
  679. if @line < @maxLines
  680. @nextChar()
  681.  
  682. if @line >= @maxLines
  683. @finish()
  684. else
  685. @drawNext()
  686.  
  687. break unless (@token.code or @lineAnimationCount <= 0 or @drawImmediately) and !@waitForKey and @waitCounter <= 0 and @isRunning and @line < @maxLines
  688.  
  689. if GameManager.tempSettings.skip
  690. @lineAnimationCount = 0
  691. else
  692. @lineAnimationCount--
  693.  
  694. ###*
  695. * Updates wait-for-key state. If skipping is enabled, the text renderer will
  696. * not wait for key press.
  697. *
  698. * @method updateWaitForKey
  699. * @private
  700. ###
  701. updateWaitForKey: ->
  702. if @waitForKey
  703. @isWaiting = !GameManager.tempSettings.skip
  704. @waitForKey = @isWaiting
  705.  
  706. ###*
  707. * Updates wait counter if the text renderer is waiting for a certain amount of time to pass. If skipping is enabled, the text renderer will
  708. * not wait for the actual amount of time and sets the wait-counter to 1 frame instead.
  709. *
  710. * @method updateWaitForKey
  711. * @private
  712. ###
  713. updateWaitCounter: ->
  714. if @waitCounter > 0 and not gs.GameManager.isCatWait()
  715. if GameManager.tempSettings.skip
  716. @waitCounter = 1
  717. @isWaiting = yes
  718. @waitCounter--
  719. if @waitCounter <= 0
  720. @isWaiting = no
  721. @continue() if @line >= @maxLines
  722.  
  723. ###*
  724. * Creates a token-object for a specified text-code.
  725. *
  726. * @method createToken
  727. * @param {string} code - The code/type of the text-code.
  728. * @param {string} value - The value of the text-code.
  729. * @return {Object} The token-object.
  730. ###
  731. createToken: (code, value) ->
  732. tokenObject = null
  733.  
  734. switch code
  735. when "CE"
  736. data = value.split("/")
  737. value = data.shift()
  738. value = if isNaN(value) then value else parseInt(value)
  739. for i in [0...data]
  740. if data[i].startsWith('"') and data[i].endsWith('"')
  741. data[i] = data[i].substring(1, data[i].length-1)
  742. else
  743. data[i] = if isNaN(data[i]) then data[i] else parseFloat(data[i])
  744. tokenObject = { code: code, value: value, values: data }
  745. else
  746. tokenObject = super(code, value)
  747.  
  748.  
  749. return tokenObject
  750. ###*
  751. * <p>Measures a control-token. If a token produces a visual result like displaying an icon then it must return the size taken by
  752. * the visual result. If the token has no visual result, <b>null</b> must be returned. This method is called for every token when the message is initialized.</p>
  753. *
  754. * <p>This method is not called while the message is running. For that case, see <i>processControlToken</i> method which is called
  755. * for every token while the message is running.</p>
  756. *
  757. * @param {Object} token - A control-token.
  758. * @return {gs.Size} The size of the area taken by the visual result of the token or <b>null</b> if the token has no visual result.
  759. * @method analyzeControlToken
  760. * @protected
  761. ###
  762. measureControlToken: (token) -> return super(token)
  763.  
  764. ###*
  765. * <p>Draws the visual result of a token, like an icon for example, to the specified bitmap. This method is called for every token when the message is initialized and the sprites for each
  766. * text-line are created.</p>
  767. *
  768. * <p>This method is not called while the message is running. For that case, see <i>processControlToken</i> method which is called
  769. * for every token while the message is running.</p>
  770. *
  771. * @param {Object} token - A control-token.
  772. * @param {gs.Bitmap} bitmap - The bitmap used for the current text-line. Can be used to draw something on it like an icon, etc.
  773. * @param {number} offset - An x-offset for the draw-routine.
  774. * @param {number} length - Determines how many characters of the token should be drawn. Can be ignored for tokens
  775. * not drawing any characters.
  776. * @method drawControlToken
  777. * @protected
  778. ###
  779. drawControlToken: (token, bitmap, offset, length) ->
  780. switch token.code
  781. when "RT" # Ruby Text
  782. super(token, bitmap, offset, length)
  783. when "SLK" # Stylable Link
  784. if !token.customData.offsetX?
  785. token.customData.offsetX = offset
  786. if @customData.linkData
  787. linkData = @customData.linkData[@line]
  788. if linkData then for data in linkData
  789. @sprites[@line].bitmap.clearRect(data.cx,
  790. 0,
  791. data.width,
  792. data.height)
  793.  
  794.  
  795. ###*
  796. * Processes a control-token. A control-token is a token which influences
  797. * the text-rendering like changing the fonts color, size or style. Changes
  798. * will be automatically applied to the game object's font.
  799. *
  800. * For message text-renderer, a few additional control-tokens like
  801. * speed-change, waiting, etc. needs to be processed here.
  802. *
  803. * This method is called for each token while the message is initialized and
  804. * also while the message is running. See <i>formattingOnly</i> parameter.
  805. *
  806. * @param {Object} token - A control-token.
  807. * @param {boolean} formattingOnly - If <b>true</b> the message is initializing right now and only
  808. * format-tokens should be processed which is necessary for the message to calculated sizes correctly.
  809. * @return {Object} A new token which is processed next or <b>null</b>.
  810. * @method processControlToken
  811. * @protected
  812. ###
  813. processControlToken: (token, formattingOnly) ->
  814. return super(token) if formattingOnly
  815. result = null
  816.  
  817. switch token.code
  818. when "CR" # Change Current Character
  819. character = RecordManager.charactersArray.first (c) -> (c.name.defaultText ? c.name) == token.value
  820. if character
  821. SceneManager.scene.currentCharacter = character
  822. when "CE" # Call Common Event
  823. params = { "values": token.values }
  824. @object.events?.emit("callCommonEvent", @object, { commonEventId: token.value, params: params, finish: no, waiting: yes })
  825. when "X" # Script
  826. token.value?(@object)
  827. when "A" # Play Animation
  828. animation = RecordManager.animationsArray.first (a) -> a.name == token.value
  829. if !animation
  830. animation = RecordManager.animations[token.value]
  831. if animation?.graphic.name?
  832. bitmap = ResourceManager.getBitmap("Graphics/Pictures/#{animation.graphic.name}")
  833. object = new gs.Object_Animation(animation)
  834.  
  835. @addCustomObject(object)
  836. @currentX += Math.round(bitmap.width / animation.framesX)
  837. @currentSprite.srcRect.width += Math.round(bitmap.width / animation.framesX)
  838.  
  839. when "RT" # Ruby Text
  840. if token.rtSize.width > token.rbSize.width
  841. @currentX += token.rtSize.width
  842. @font.set(@getRubyTextFont(token))
  843. else
  844. @currentX += token.rbSize.width
  845.  
  846. when "LK" # Link
  847. if token.value == 'E' # End Link
  848. object = new ui.Object_Hotspot()
  849. object.enabled = yes
  850. object.setup()
  851.  
  852. @addCustomObject(object)
  853.  
  854. object.dstRect.x = @object.dstRect.x + @object.origin.x + @customData.linkData.cx
  855. object.dstRect.y = @object.dstRect.y + @object.origin.y + @customData.linkData.cy
  856. object.dstRect.width = @currentX - @customData.linkData.cx
  857. object.dstRect.height = @currentLineHeight
  858.  
  859. object.events.on("click", gs.CallBack("onLinkClick", this), linkData: @customData.linkData, this)
  860. else # Begin Link
  861. @customData.linkData = { cx: @currentX, cy: @currentY, commonEventId: token.value, tokenIndex: @tokenIndex }
  862. when "SLK" # Styleable Link
  863. if token.value == 'E' # End Link
  864. linkData = @customData.linkData[@line].last()
  865. line = @lines[@line].content
  866. linkStart = @findToken(@tokenIndex-1, "SLK", -1, line)
  867. textTokens = @findTokensBetween(linkData.tokenIndex, @tokenIndex, null, line)
  868.  
  869. linkData.cx = linkStart.customData.offsetX
  870. linkData.width = @currentX - linkData.cx + @padding
  871. linkData.height = @currentSprite.bitmap.height
  872.  
  873. object = new ui.Object_Text()
  874. object.text = textTokens.select((x) => x.value).join("")
  875. #object.sizeToFit = yes
  876. object.formatting = no
  877. object.wordWrap = no
  878. object.ui = new ui.Component_UIBehavior()
  879. object.enabled = yes
  880. object.addComponent(object.ui)
  881. object.addComponent(new gs.Component_HotspotBehavior())
  882. object.behavior.padding.left = 0
  883. object.behavior.padding.right = 0
  884. object.dstRect.width = linkData.width
  885. object.dstRect.height = linkData.height
  886.  
  887. if linkData.styleIndex == -1
  888. ui.UIManager.addControlStyles(object, ["hyperlink"])
  889. else
  890. ui.UIManager.addControlStyles(object, ["hyperlink-"+linkData.styleIndex])
  891.  
  892. object.setup()
  893.  
  894. @addCustomObject(object)
  895.  
  896. object.dstRect.x = @currentSprite.x + linkData.cx
  897. object.dstRect.y = @object.dstRect.y + @object.origin.y + linkData.cy
  898.  
  899. object.events.on("click", gs.CallBack("onLinkClick", this), linkData: linkData, this)
  900. else # Begin Link
  901. if !@customData.linkData
  902. @customData.linkData = []
  903. if !@customData.linkData[@line]
  904. @customData.linkData[@line] = []
  905. if token.value?.contains(",")
  906. values = token.value.split(",")
  907. @customData.linkData[@line].push({ cx: @currentX, cy: @currentY, commonEventId: values[0], styleIndex: parseInt(values[1]), tokenIndex: @tokenIndex })
  908. else
  909. @customData.linkData[@line].push({ cx: @currentY, cy: @currentY, commonEventId: token.value, tokenIndex: @tokenIndex, styleIndex: -1 })
  910.  
  911. when "E" # Change Expression
  912. expression = RecordManager.characterExpressionsArray.first (c) -> (c.name.defaultText ? c.name) == token.value
  913. if !expression
  914. expression = RecordManager.characterExpressions[token.value]
  915.  
  916. character = SceneManager.scene.currentCharacter
  917. if expression? and character?.index?
  918. duration = GameManager.defaults.character.expressionDuration
  919. easing = gs.Easings.fromObject(GameManager.defaults.character.changeEasing)
  920. animation = GameManager.defaults.character.changeAnimation
  921. object = SceneManager.scene.characters.first (c) -> c.rid == character.index
  922. object?.behavior.changeExpression(expression, animation, easing, duration)
  923.  
  924. when "SP" # Play Sound
  925. sound = RecordManager.system.sounds[token.value-1]
  926. AudioManager.playSound(sound)
  927. when "S" # Change Speed
  928. GameManager.settings.messageSpeed = token.value
  929. when "W" # Wait
  930. @drawImmediately = no
  931. if !GameManager.tempSettings.skip
  932. if token.value == "A"
  933. @waitForKey = yes
  934. else
  935. @waitCounter = Math.round(token.value / 1000 * Graphics.frameRate)
  936. when "WE" # Wait at End
  937. @waitAtEnd = token.value == "Y"
  938. when "DI" # Draw Immedialty
  939. @drawImmediately = token.value == 1 or token.value == "Y" # Draw immediately
  940. else
  941. result = super(token)
  942.  
  943. return result
  944. ###*
  945. * Clears/Resets the text-renderer.
  946. *
  947. * @method clear
  948. ###
  949. clear: ->
  950. @charIndex = 0
  951. @currentX = 0
  952. @currentY = 0
  953. @line = 0
  954. @lines = []
  955. @clearCustomObjects()
  956. @object.bitmap?.clear()
  957.  
  958. for sprite in @allSprites
  959. sprite.dispose()
  960. sprite.bitmap?.dispose()
  961. @allSprites = []
  962. return null
  963.  
  964. ###*
  965. * Clears/Disposes all sprites used to display the text-lines/parts.
  966. *
  967. * @method clearAllSprites
  968. ###
  969. clearAllSprites: ->
  970. for sprite in @allSprites
  971. sprite.dispose()
  972. sprite.bitmap?.dispose()
  973.  
  974. return null
  975.  
  976. ###*
  977. * Clears/Disposes the sprites used to display the text-lines/parts of the current/last message.
  978. *
  979. * @method clearSprites
  980. ###
  981. clearSprites: ->
  982. for sprite in @sprites
  983. sprite.dispose()
  984. sprite.bitmap?.dispose()
  985.  
  986. return null
  987.  
  988.  
  989. ###*
  990. * Removes a game object from the message.
  991. *
  992. * @method removeCustomObject
  993. * @param object {gs.Object_Base} The game object to remove.
  994. ###
  995. removeCustomObject: (object) ->
  996. SceneManager.scene.removeObject(object)
  997. object.dispose()
  998. @customObjects.remove(object)
  999.  
  1000. ###*
  1001. * Adds a game object to the message which is alive until the message is
  1002. * erased. Can be used to display animationed-icons, etc. in a message.
  1003. *
  1004. * @method addCustomObject
  1005. * @param object {gs.Object_Base} The game object to add.
  1006. ###
  1007. addCustomObject: (object) ->
  1008. object.dstRect.x = @object.dstRect.x + @object.origin.x + @currentX
  1009. object.dstRect.y = @object.dstRect.y + @object.origin.y + @currentY
  1010. object.zIndex = @object.zIndex + 1
  1011. object.update()
  1012.  
  1013. SceneManager.scene.addObject(object)
  1014. @customObjects.push(object)
  1015.  
  1016. ###*
  1017. * Clears the list of custom game objects. All game objects are disposed and removed
  1018. * from the scene.
  1019. *
  1020. * @method clearCustomObjects
  1021. * @param object {Object} The game object to add.
  1022. ###
  1023. clearCustomObjects: ->
  1024. for object in @customObjects
  1025. object.dispose()
  1026. SceneManager.scene.removeObject(object)
  1027.  
  1028. @customObjects = []
  1029.  
  1030. ###*
  1031. * Creates the bitmap for a specified line-object.
  1032. *
  1033. * @method createBitmap
  1034. * @private
  1035. * @param {Object} line - A line-object.
  1036. * @return {Bitmap} A newly created bitmap containing the line-text.
  1037. ###
  1038. createBitmap: (line) ->
  1039. @font = @object.font
  1040. bitmap = new Bitmap(@object.dstRect.width, Math.max(@minLineHeight, line.height))
  1041. bitmap.font = @font
  1042.  
  1043. return bitmap
  1044.  
  1045. ###*
  1046. * Draws the line's content on the specified bitmap.
  1047. *
  1048. * @method drawLineContent
  1049. * @protected
  1050. * @param {Object} line - A line-object which should be drawn on the bitmap.
  1051. * @param {gs.Bitmap} bitmap - The bitmap to draw the line's content on.
  1052. * @param {number} length - Determines how many characters of the specified line should be drawn. You can
  1053. * specify -1 to draw all characters.
  1054. ###
  1055. drawLineContent: (line, bitmap, length) ->
  1056. bitmap.clear()
  1057. currentX = @padding
  1058. drawAll = length == -1
  1059.  
  1060. for token, i in line.content
  1061. break if i > @tokenIndex and !drawAll
  1062. if token.code?
  1063. size = @measureControlToken(token, bitmap)
  1064. @drawControlToken(token, bitmap, currentX)
  1065. if size then currentX += size.width
  1066. @processControlToken(token, yes, line)
  1067. else if token.value.length > 0
  1068. token.applyFormat(@font)
  1069. value = token.value
  1070. if !drawAll and @tokenIndex == i and value.length > length
  1071. value = value.substring(0, length)
  1072. if value != "\n"
  1073. size = @font.measureTextPlain(value)
  1074. bitmap.drawText(currentX, line.height - (size.height - @font.descent) - line.descent, size.width, bitmap.height, value, 0, 0)
  1075. currentX += size.width
  1076.  
  1077. line.contentWidth = currentX + @font.measureTextPlain(" ").width
  1078.  
  1079. ###*
  1080. * Creates the sprite for a specified line-object.
  1081. *
  1082. * @method createSprite
  1083. * @private
  1084. * @param {Object} line - A line-object.
  1085. * @return {Sprite} A newly created sprite object containing the line-text as bitmap.
  1086. ###
  1087. createSprite: (line) ->
  1088. bitmap = @createBitmap(line)
  1089.  
  1090. @currentX = 0
  1091. @waitCounter = 0
  1092. @waitForKey = no
  1093.  
  1094. sprite = new Sprite(Graphics.viewport)
  1095. sprite.bitmap = bitmap
  1096. sprite.visible = yes
  1097. sprite.z = @object.zIndex + 1
  1098.  
  1099. sprite.srcRect = new Rect(0, 0, 0, bitmap.height)
  1100.  
  1101. return sprite
  1102.  
  1103. ###*
  1104. * Creates the sprites for a specified array of line-objects.
  1105. *
  1106. * @method createSprites
  1107. * @private
  1108. * @see gs.Component_MessageTextRenderer.createSprite.
  1109. * @param {Array} lines - An array of line-objects.
  1110. * @return {Array} An array of sprites.
  1111. ###
  1112. createSprites: (lines) ->
  1113. @fontSize = @object.font.size
  1114. result = []
  1115. for line, i in lines
  1116. sprite = @createSprite(line)
  1117. result.push(sprite)
  1118. return result
  1119.  
  1120. ###*
  1121. * Starts a new line.
  1122. *
  1123. * @method newLine
  1124. ###
  1125. newLine: ->
  1126. @currentX = 0
  1127. @currentY += @currentLineHeight + @lineSpacing
  1128.  
  1129. ###*
  1130. * Displays a formatted text immediately without any delays or animations. The
  1131. * Component_TextRenderer.drawFormattedText method from the base-class cannot
  1132. * be used here because it would render to the game object's bitmap object while
  1133. * this method is rendering to the sprites.
  1134. *
  1135. * @method drawFormattedTextImmediately
  1136. * @param {number} x - The x-coordinate of the text's position.
  1137. * @param {number} y - The y-coordinate of the text's position.
  1138. * @param {number} width - Deprecated. Can be null.
  1139. * @param {number} height - Deprecated. Can be null.
  1140. * @param {string} text - The text to draw.
  1141. * @param {boolean} wordWrap - If wordWrap is set to true, line-breaks are automatically created.
  1142. ###
  1143. drawFormattedTextImmediately: (x, y, width, height, text, wordWrap) ->
  1144. @drawFormattedText(x, y, width, height, text, wordWrap)
  1145.  
  1146. loop
  1147. @nextChar()
  1148.  
  1149. if @line >= @maxLines
  1150. @isRunning = no
  1151. else
  1152. @drawNext()
  1153.  
  1154. break unless @isRunning
  1155.  
  1156. @currentY += @currentLineHeight + @lineSpacing
  1157.  
  1158. return null
  1159.  
  1160.  
  1161. ###*
  1162. * Starts the rendering-process for the message.
  1163. *
  1164. * @method drawFormattedText
  1165. * @param {number} x - The x-coordinate of the text's position.
  1166. * @param {number} y - The y-coordinate of the text's position.
  1167. * @param {number} width - Deprecated. Can be null.
  1168. * @param {number} height - Deprecated. Can be null.
  1169. * @param {string} text - The text to draw.
  1170. * @param {boolean} wordWrap - If wordWrap is set to true, line-breaks are automatically created.
  1171. ###
  1172. drawFormattedText: (x, y, width, height, text, wordWrap) ->
  1173. text = text || " " # Use a space character if no text is specified.
  1174. @font.set(@object.font)
  1175. @speed = 11 - Math.round(GameManager.settings.messageSpeed * 2.5)
  1176. @isRunning = yes
  1177. @drawImmediately = no
  1178. @lineAnimationCount = @speed
  1179. @currentLineHeight = 0
  1180. @isWaiting = no
  1181. @waitForKey = no
  1182. @charIndex = 0
  1183. @token = null
  1184. @tokenIndex = 0
  1185. @message = text
  1186. @line = 0
  1187. @currentLine = @line
  1188. currentX = @currentX #Math.max(@currentX, @padding)
  1189. @lines = @calculateLines(lcsm(@message), wordWrap, @currentX)
  1190. @sprites = @createSprites(@lines)
  1191. @allSprites = @allSprites.concat(@sprites)
  1192. @currentX = currentX
  1193. @currentSprite = @sprites[@line]
  1194. @currentSprite.x = @currentX + @object.origin.x + @object.dstRect.x
  1195. @maxLines = @calculateMaxLines(@lines)
  1196. @token = @lines[@line]?.content[@tokenIndex] || new gs.RendererToken(null, "")
  1197.  
  1198.  
  1199. @start()
  1200.  
  1201. ###*
  1202. * Starts the message-rendering process.
  1203. *
  1204. * @method start
  1205. * @protected
  1206. ###
  1207. start: ->
  1208. if GameManager.tempSettings.skip and GameManager.tempSettings.skipTime == 0
  1209. @instantSkip()
  1210. else if @maxLines == 0
  1211. # If first line is empty then it doesn't fit into current line, so finish.
  1212. if @lines[0]?.content == ""
  1213. @finish()
  1214. else
  1215. @maxLines = 1
  1216. @drawNext()
  1217. else
  1218. @drawNext()
  1219.  
  1220. ###*
  1221. * Skips the current message and finishes the message-processing immediately. The message
  1222. * tokens are processed but not rendered.
  1223. *
  1224. * @method instantSkip
  1225. ###
  1226. instantSkip: ->
  1227. loop
  1228. if @line < @maxLines
  1229. @nextChar()
  1230.  
  1231. if @line >= @maxLines
  1232. break
  1233. else
  1234. @processToken()
  1235.  
  1236. break unless @isRunning and @line < @maxLines
  1237.  
  1238. @object.events?.emit("messageWaiting", this)
  1239. @continue()
  1240.  
  1241. ###*
  1242. * Processes the current token.
  1243. *
  1244. * @method processToken
  1245. ###
  1246. processToken: ->
  1247. token = null
  1248.  
  1249. if @token.code?
  1250. token = @processControlToken(@token, no)
  1251. if token?
  1252. @token = token
  1253. @token.onStart?()
  1254. else
  1255. token = @token
  1256.  
  1257. return token
  1258.  
  1259.  
  1260.  
  1261. gs.Component_MessageTextRenderer = Component_MessageTextRenderer
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement