# TODO porting old ones forward also STUFF I BROKE # - up/down to move selection in menus doesn't work # - right arrow works on a menu (broke "next: null"?) # - jukebox errors are quietly discarded (and don't change the splash text), not even logged to console (try a bogus track name) # - pausing while looking at a partly-scrolled speech does not end well # - holding down right arrow sometimes attributes lines to the wrong speaker # - holding down right arrow can make text overflow the box # - holding down right arrow is still slow while the speaker tag changes # (curiously, it's much faster when between TAL panels) # - basically fast scrolling is a huge pain # - can't skip through "delay" for some reason # # - people are seeing black background flicker near the end of TTA2; possibly browser evicting too many images? can we wait on transitionend and only hide the old image afterwards? hard to test! # - some of the revealeon stuff is a fucking disaster # - not really happy with the way the textbox transitions back and forth in species-bugs # - component ids are terrible and don't tell me what kind of thing an imagespot is or even which cutscene a thing came from # - species-bugs would be a lot better if i could wait on an animation. actually that goes for the one at the beginning of itchy itchy part 4, too. # - i changed the htmlblock z-index and have no fucking idea if that will break anything really # - i would actually want to use flexbox to position the text + images but the only option is absolute everything; no real layers # - problems in loading sometimes don't actually show 'kaboom'? # - tablet doesn't disappear at the end of revealeons # TODO some thoughts on the problems with this json script format # - really tedious to edit by hand, and really repetitive # - hard to read as a result, too # - lot of copy-pasting of metadata: credits are mostly the same every time, character colors and font effects ought to be shared css or something # TODO easy, important, later # - need a fix for the chrome mobile audio loading thing # - direly need a table of contents # - PROBLEM: going "backwards" works by remembering how you got there... how does that work if you jump arbitrarily?? # - mute, too (possibly solved by pause) # - can't jump back to the scene list if that's where you landed # - need to port all the old cutscenes to the new format # - stage:next and stage:prev work through the pause screen, ooops # # - hashchange event # - show a better loading screen that includes the title of the thing being loaded 8D # - loading screen feels like it should be different depending on whether you are loading the main menu or a particular script # - if it fails to load, try to load again but cachebusted (including css, js... hmm that sounds tricky) # - it might be much more convenient to have an Actor class that combines both a component and its current live element; would give somewhere to put state instead of in jquery data! # TODO embedding # - how much html do i expect people to copy/paste? should be //minimal// # - how do they specify which scripts to load? i feel it should be something declarative # TODO alright so the rules for the introductory splash screen are as follows # - it should be embedded in the html so you get it even if you don't have js support # - it should have a few sections toggleable via css ("requires javascript", "warning: sound") # - when/where it appears should vary: # - if you land DIRECTLY ON A SCENE then it should show with a play button before the thing starts and the music plays (i don't think this should count as a step though?) # - if you land on the main page itself then it should show before the index? # TODO in rough order # - if you hold down ->, you fuck up the text before the credits: somehow the disable transitions overlap and the stage thinks it's busy forever. not sure how to fix since this is a sort of general transition problem :| # - mute button # - carat for scrolling text # - can't advance with click if there's no speech box lol # - hmmm JUMP_WHEN_COMPLETE doesn't let you go backwards. maybe it should only fire once? or reset after it fires once? or appear as a regular item after it fires once? # - hosting this on... somewhere? probably separate from floraverse for now with a placeholder :/ # - haha this error screen sucks # - fast-forward key (or detect holding down ->) that automatically skips through partial-advancement like filling the speechbox? # - make the scroll smarter: account for lag, be slightly faster than one char per frame # - speaker fonts? # - TINY glitch: backdrop seems to fade back in if you try to skip the dim_out before a nothing # - XXX ABSOLUTELY NEED: FALLBACK FOR UNSUPPORTED BROWSERS WITH JUST THE MENUS AND/OR SCRIPT. # - omg is it possible to describe the script entirely in html o_o it's declarative, right...? # - i ran into a weird case where firefox loaded part of an image and sent a Range request for the rest, got back a 206, and... did nothing with it. considered it a failure, even. # TODO future niceties # - fragment to jump around # - jumplist of places you've been before (persistent) # - volume control (persistent) # - replace all the js with declarative html that looks ok displayed? is this feasible even # - convenience merge "pose" with "say"? # - minify this and the js while you're at it? # - ...ditch jquery? # - would be pretty slick if you could say characters A and B can never appear on-screen at the same time, and the exeunting would be handled automatically # - automatically pause the music if the window loses focus, or the tab loses focus, using visibility api? unless you're using the jukebox. also, have a jukebox # - could put a little play icon in the title too, though that doesn't work for iframes. also the title should probably include the title of the current thing being played # TODO tech stuff # - need error checking for menu labels! # - more error checking of arguments # - break this into :filez: # - 'offset' is a hack, not relative to the position at all # - 'back' is also a hack, maybe z-index should be a real thing here, idk # - backdrop should stop being a special case # - the returned components should be little builder wrappers that have their own methods, instead of using script.everything # - i think narrator should be a real thing honestly # - also maybe there should be real css rules for characters # - switching between left/right for the labels is a little flickery # ============================================================================== # LIBRARY STUFF # ------------------------------------------------------------------------------ # Cross-browser amenities requestAnimationFrame = window.requestAnimationFrame || window.mozRequestAnimationFrame || window.webkitRequestAnimationFrame || window.msRequestAnimationFrame cancelAnimationFrame = window.cancelAnimationFrame || window.mozCancelAnimationFrame || window.webkitCancelAnimationFrame window.console ?= {} window.console.log ?= -> window.console.error ?= -> # ------------------------------------------------------------------------------ # Tiny DOM helpers create_element = (tag, attrs={}) -> el = document.createElement tag for key, value of attrs el.setAttribute key, value return el # ------------------------------------------------------------------------------ # Promise/event helpers promise_always = -> ### Return a Deferred that's already been resolved. Mainly useful for cases where you want to defer an action until after a first step has been completed, but the first step is optional. The Deferred's value will be null. ### return $.when null promise_event = ($element, event, failure_event=null) -> ### Return a Deferred that will resolve when the named event (or space-separated list of event names) fires. Optionally, the Deferred will be rejected when the named failure event fires. Either way, the Deferred's value will be the fired event. ### namespace = ".cutscene-promise-event-#{Math.random()}" _namespace_event = (name) -> return [part + namespace for part in name.split ' '].join ' ' event = _namespace_event event promise = $.Deferred() $element.one event, (ev) -> promise.resolve ev # Same thing for the failure event if failure_event? failure_event = _namespace_event failure_event $element.one failure_event, (ev) -> promise.reject ev # Make sure to always unbind as soon as /either event/ fires promise.always -> $element.off namespace return promise promise_transition = ($el) -> $el = $ $el props = window.getComputedStyle($el[0]) # TODO this is nice, but also doesn't check that anything is actually # transitioning at the moment if props.transitionProperty != 'none' and props.transitionDuration != '0s' return promise_event $el, 'transitionend' else return promise_always() promise_wait = (time) -> ### Return a Deferred that will resolve after the specified amount of time has passed (in SECONDS, like Python, what do you think this is). ### promise = $.Deferred() window.setTimeout (-> promise.resolve null), time * 1000 return promise refire = ($target, source, event_types...) -> for event_type in event_types source.on event_type, (event, args...) -> $target.triggerHandler event.type, args... normalize_color = (color) -> # Stupid hack to add the alpha to the background color, regardless # of how the color is formatted d = document.createElement 'div' document.body.appendChild d d.style.backgroundColor = color rgb_color = window.getComputedStyle(d).backgroundColor document.body.removeChild d return rgb_color # ============================================================================== # CUTSCENE IMPLEMENTATION NS = '.cutscene' CAN_PLAY_AUDIO = do -> dummy_audio = document.createElement 'audio' return dummy_audio.canPlayType and dummy_audio.canPlayType 'audio/ogg; codecs="vorbis"' class Widget constructor: (@$parent, @$container) -> if @$parent instanceof Widget @$parent = @$parent.$container if not @$container? @$container = @_create_container() @$parent.append @$container _create_container: -> throw new Error "No container passed to #{@.name} and don't know how to make one" on: (args...) -> return @$container.on args... triggerHandler: (args...) -> return @$container.triggerHandler args... class SplashScreen extends Widget constructor: ($parent, $container) -> super $parent, $container @set_ready() if CAN_PLAY_AUDIO @$container.addClass '-state-audio-on' else @$container.addClass '-state-audio-off' @$parent.on 'stage:next', (event) => # When we're passed a "next frame" event, it really means dismiss # us, if we're dismissable if not @$container.hasClass '-state-ready' return if not @$container.hasClass '-active' return @$container.removeClass '-active' set_loading: -> @$container.removeClass '-state-ready' @$container.removeClass '-state-failed' @$container.addClass '-state-loading' @$container.addClass '-active' set_failed: -> # TODO pretty sure we can do better than this, at least say it was a loading error # TODO and allow trying again or going back or something @$container.removeClass '-state-loading' @$container.removeClass '-state-ready' @$container.addClass '-state-failed' @$container.addClass '-active' set_ready: -> @$container.removeClass '-state-loading' @$container.removeClass '-state-failed' @$container.addClass '-state-ready' @$container.addClass '-active' class ScriptMenu extends Widget constructor: ($parent, $container) -> super $parent, $container @items = [] _create_container: -> return $ '