├── res ├── bg.jpg ├── bg.png ├── node.png ├── test.png ├── button.png ├── gauge.png ├── favicon.ico ├── menu-bg.jpg ├── note-bg.png ├── result-bg.jpg ├── turntable.png ├── judge-image.png ├── level-font.png ├── note-black.png ├── note-white.png ├── sound-only.png ├── black-keydown.png ├── fonts │ └── spaceage.ttf ├── great-effect.png ├── note-turntable.png ├── numeral-font.png ├── white-keydown.png ├── turntable-keydown.png ├── loading.js └── va.txt ├── .cocos-project.json ├── test ├── src │ ├── test-utils.coffee │ ├── test-greatEffect.coffee │ ├── test-note.coffee │ ├── test-animationLayer.coffee │ ├── test-playtimeLayer.coffee │ ├── test-bpmLayer.coffee │ ├── test-timer.coffee │ ├── test-keyEffectLayer.coffee │ ├── test-main.coffee │ ├── test-rateLayer.coffee │ ├── test-judgementLayer.coffee │ ├── test-numeralLayer.coffee │ ├── test-parser.coffee │ └── test-statsLayer.coffee ├── project.json └── runner.html ├── .gitignore ├── .travis.yml ├── project.json ├── bower.json ├── src ├── measureNode.coffee ├── eventObserver.coffee ├── judge.coffee ├── searchController.coffee ├── keyboardService.coffee ├── timer.coffee ├── note.coffee ├── bpmLayer.coffee ├── greatEffect.coffee ├── judgementLayer.coffee ├── audio.coffee ├── greatEffectsLayer.coffee ├── playtimeLayer.coffee ├── keyEffectsLayer.coffee ├── numeralLayer.coffee ├── loaderScene.coffee ├── resource.coffee ├── animationLayer.coffee ├── main.coffee ├── fallObj.coffee ├── gameoverScene.coffee ├── searchLayer.coffee ├── rateLayer.coffee ├── statsLayer.coffee ├── menuScene.coffee ├── notesLayer.coffee ├── parser.coffee └── app.coffee ├── manifest.webapp ├── .project ├── package.json ├── index.html ├── README.md ├── gulpfile.coffee └── bms.cson /res/bg.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bokuweb/bmsjs/HEAD/res/bg.jpg -------------------------------------------------------------------------------- /res/bg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bokuweb/bmsjs/HEAD/res/bg.png -------------------------------------------------------------------------------- /res/node.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bokuweb/bmsjs/HEAD/res/node.png -------------------------------------------------------------------------------- /res/test.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bokuweb/bmsjs/HEAD/res/test.png -------------------------------------------------------------------------------- /res/button.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bokuweb/bmsjs/HEAD/res/button.png -------------------------------------------------------------------------------- /res/gauge.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bokuweb/bmsjs/HEAD/res/gauge.png -------------------------------------------------------------------------------- /res/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bokuweb/bmsjs/HEAD/res/favicon.ico -------------------------------------------------------------------------------- /res/menu-bg.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bokuweb/bmsjs/HEAD/res/menu-bg.jpg -------------------------------------------------------------------------------- /res/note-bg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bokuweb/bmsjs/HEAD/res/note-bg.png -------------------------------------------------------------------------------- /res/result-bg.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bokuweb/bmsjs/HEAD/res/result-bg.jpg -------------------------------------------------------------------------------- /res/turntable.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bokuweb/bmsjs/HEAD/res/turntable.png -------------------------------------------------------------------------------- /.cocos-project.json: -------------------------------------------------------------------------------- 1 | { 2 | "has_native": true, 3 | "project_type": "js" 4 | } -------------------------------------------------------------------------------- /res/judge-image.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bokuweb/bmsjs/HEAD/res/judge-image.png -------------------------------------------------------------------------------- /res/level-font.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bokuweb/bmsjs/HEAD/res/level-font.png -------------------------------------------------------------------------------- /res/note-black.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bokuweb/bmsjs/HEAD/res/note-black.png -------------------------------------------------------------------------------- /res/note-white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bokuweb/bmsjs/HEAD/res/note-white.png -------------------------------------------------------------------------------- /res/sound-only.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bokuweb/bmsjs/HEAD/res/sound-only.png -------------------------------------------------------------------------------- /res/black-keydown.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bokuweb/bmsjs/HEAD/res/black-keydown.png -------------------------------------------------------------------------------- /res/fonts/spaceage.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bokuweb/bmsjs/HEAD/res/fonts/spaceage.ttf -------------------------------------------------------------------------------- /res/great-effect.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bokuweb/bmsjs/HEAD/res/great-effect.png -------------------------------------------------------------------------------- /res/note-turntable.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bokuweb/bmsjs/HEAD/res/note-turntable.png -------------------------------------------------------------------------------- /res/numeral-font.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bokuweb/bmsjs/HEAD/res/numeral-font.png -------------------------------------------------------------------------------- /res/white-keydown.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bokuweb/bmsjs/HEAD/res/white-keydown.png -------------------------------------------------------------------------------- /res/turntable-keydown.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bokuweb/bmsjs/HEAD/res/turntable-keydown.png -------------------------------------------------------------------------------- /test/src/test-utils.coffee: -------------------------------------------------------------------------------- 1 | capture = (name)-> if window.callPhantom? then callPhantom {'screenshot': name} 2 | 3 | module.exports = 4 | capture : capture 5 | 6 | -------------------------------------------------------------------------------- /test/project.json: -------------------------------------------------------------------------------- 1 | { 2 | "debugMode" : 1, 3 | "frameRate" : 60, 4 | "id" : "gameCanvas", 5 | "renderMode" : 1, 6 | "jsList" : [] 7 | } 8 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | frameworks/ 3 | tools/ 4 | main.js 5 | CMakeLists.txt 6 | js/ 7 | bower_components/ 8 | bms/ 9 | #package.json# 10 | test/test-all.js 11 | publish/ 12 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | 3 | node_js: 4 | - '0.10' 5 | 6 | install: 7 | - npm install -g bower 8 | - npm install 9 | - bower install -f 10 | 11 | script: 12 | - 'gulp test' 13 | -------------------------------------------------------------------------------- /project.json: -------------------------------------------------------------------------------- 1 | { 2 | "project_type": "javascript", 3 | 4 | "debugMode" : 0, 5 | "showFPS" : false, 6 | "frameRate" : 60, 7 | "id" : "gameCanvas", 8 | "renderMode" : 0, 9 | "engineDir":"frameworks/cocos2d-html5", 10 | 11 | "modules" : ["cocos2d"], 12 | 13 | "jsList" : [ 14 | 15 | ] 16 | } 17 | -------------------------------------------------------------------------------- /bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "bms_proto", 3 | "version": "0.0.0", 4 | "license": "MIT", 5 | "ignore": [ 6 | "**/.*", 7 | "node_modules", 8 | "bower_components", 9 | "test", 10 | "tests" 11 | ], 12 | "dependencies": { 13 | "mocha": "~2.2.5", 14 | "jquery": "~2.1.3", 15 | "chai": "~2.3.0", 16 | "lodash": "~3.9.3" 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/measureNode.coffee: -------------------------------------------------------------------------------- 1 | FallObj = require './fallObj' 2 | 3 | MeasureNode = FallObj.extend 4 | 5 | ctor : (texture, @_timer)-> 6 | @_super texture, @_timer 7 | 8 | start : -> 9 | @_super() 10 | 11 | update : -> 12 | @_super() 13 | if @y < @dstY[@index] 14 | @removeFromParent on 15 | @release() 16 | 17 | module.exports = MeasureNode 18 | -------------------------------------------------------------------------------- /src/eventObserver.coffee: -------------------------------------------------------------------------------- 1 | EventObserver = cc.Class.extend 2 | 3 | ctor : -> 4 | @_listeners = [] 5 | 6 | on: (name, callback, params)-> 7 | @_listeners.push 8 | name: name 9 | callback: callback 10 | params : params 11 | 12 | trigger: (name, data)-> 13 | for listener in @_listeners when listener.name is name 14 | listener.callback name, data, listener.params 15 | return 16 | 17 | module.exports = EventObserver 18 | -------------------------------------------------------------------------------- /manifest.webapp: -------------------------------------------------------------------------------- 1 | { 2 | "version": "1.0", 3 | "name": "bms_proto", 4 | "description": "bms_proto", 5 | "launch_path": "/index.html", 6 | "icons": { 7 | "128": "/res/icon.png" 8 | }, 9 | "developer": { 10 | "name": "Cocos2d-html5", 11 | "url": "http://cocos2d-x.org/" 12 | }, 13 | "default_locale": "en", 14 | "installs_allowed_from": [ 15 | "*" 16 | ], 17 | "orientation": "portrait-primary", 18 | "fullscreen": "true" 19 | } 20 | -------------------------------------------------------------------------------- /src/judge.coffee: -------------------------------------------------------------------------------- 1 | Judge = cc.Class.extend 2 | ctor : (@_config)-> 3 | console.dir @_config 4 | 5 | exec : (diffTime)-> 6 | return "pgreat" if -@_config.pgreat < diffTime < @_config.pgreat 7 | return "great" if -@_config.great < diffTime < @_config.great 8 | return "good" if -@_config.good < diffTime < @_config.good 9 | return "bad" if -@_config.bad < diffTime < @_config.bad 10 | return "poor" 11 | 12 | module.exports = Judge 13 | -------------------------------------------------------------------------------- /src/searchController.coffee: -------------------------------------------------------------------------------- 1 | _ = require 'lodash' 2 | 3 | SearchController = cc.Layer.extend 4 | ctor : (@_items) -> 5 | 6 | search : (txt) -> 7 | visibleItems = _.filter @_items, (item) -> 8 | list = txt.replace(/ /g," ").split(/ /) 9 | _.filter list, (txt) -> 10 | txt = txt.replace /[A-Za-z0-9]/g, (s) -> String.fromCharCode(s.charCodeAt(0) - 0xFEE0) 11 | re = new RegExp txt, "i" 12 | if item.title? 13 | item.title.search(re) isnt -1 14 | .length is list.length 15 | 16 | module.exports = SearchController 17 | -------------------------------------------------------------------------------- /src/keyboardService.coffee: -------------------------------------------------------------------------------- 1 | EventObserver = require './eventObserver' 2 | 3 | KeyboardService = cc.Layer.extend 4 | ctor : (@_timer)-> 5 | @_super() 6 | @_notifier = new EventObserver() 7 | 8 | init : (config)-> 9 | return false unless 'keyboard' of cc.sys.capabilities 10 | cc.eventManager.addListener 11 | event : cc.EventListener.KEYBOARD 12 | onKeyPressed : @_onKeyPressed.bind this 13 | , this 14 | true 15 | 16 | addListener : (key, listener, id)-> 17 | @_notifier.on key, listener, id 18 | 19 | _onKeyPressed : (key, event)-> 20 | @_notifier.trigger key, @_timer.get() 21 | 22 | module.exports = KeyboardService 23 | -------------------------------------------------------------------------------- /.project: -------------------------------------------------------------------------------- 1 | 2 | 3 | bmsjs 4 | 5 | 6 | 7 | 8 | 9 | 10 | org.ccdt.cocosproject 11 | org.ccdt.jsdt.core.jsNature 12 | 13 | 14 | 1.2 15 | 16 | 17 | 18 | script 19 | 2 20 | PROJECT_LOC/frameworks/js-bindings/bindings/script 21 | 22 | 23 | -------------------------------------------------------------------------------- /src/timer.coffee: -------------------------------------------------------------------------------- 1 | Timer = cc.Class.extend 2 | ctor : -> 3 | @_startTime = 0 4 | @_pauseTime = 0 5 | @_isPause = false 6 | 7 | start : -> 8 | @_isPause = false 9 | @_startTime = new Date() 10 | 11 | set : (time_msec)-> 12 | @_startTime = new Date() 13 | @_pauseTime = time_msec 14 | 15 | get : -> 16 | if @_isPause 17 | @_pauseTime 18 | else if @_startTime 19 | ((new Date() - @_startTime) + @_pauseTime) 20 | else 0 21 | 22 | pause : -> 23 | @_isPause = true 24 | @_pauseTime = ((new Date() - @_startTime) + @_pauseTime) 25 | 26 | stop : -> 27 | @_startTime = 0 28 | @_pauseTime = 0 29 | 30 | module.exports = Timer 31 | -------------------------------------------------------------------------------- /src/note.coffee: -------------------------------------------------------------------------------- 1 | FallObj = require './fallObj' 2 | EventObserver = require './eventObserver' 3 | 4 | Note = FallObj.extend 5 | 6 | ctor : (texture, @_timer, @_removeTime) -> 7 | @_super texture, @_timer 8 | @_notifier = new EventObserver() 9 | 10 | addListener: (name, listner) -> 11 | @_notifier.on name, listner 12 | 13 | update : -> 14 | @_super() 15 | time = @_timer.get() 16 | @y = @dstY[@index] if @y <= @dstY[@index] 17 | 18 | if @clear and not @hasJudged 19 | @hasJudged = true 20 | return 21 | 22 | if time > @timing + @_removeTime 23 | @_notifier.trigger 'remove', this 24 | @removeFromParent on 25 | @release() 26 | return 27 | 28 | module.exports = Note 29 | -------------------------------------------------------------------------------- /src/bpmLayer.coffee: -------------------------------------------------------------------------------- 1 | NumeralLayer = require './numeralLayer' 2 | 3 | BpmLayer = cc.Layer.extend 4 | ctor: (@_skin, @_timer, @_bpms) -> 5 | @_super() 6 | @_bpm = new NumeralLayer @_skin 7 | 8 | init : -> 9 | @_index = 0 10 | @_bpm.init 3, @_bpms[0].val 11 | @_bpm.x = cc.screenSize.width / 2 + @_skin.x 12 | @_bpm.y = cc.screenSize.height - @_skin.y 13 | @addChild @_bpm 14 | 15 | start : -> @scheduleUpdate() 16 | 17 | get : -> 18 | i = if @_index >= 1 then @_index - 1 else 0 19 | @_bpms[i].val 20 | 21 | update : -> 22 | time = @_timer.get() 23 | if @_bpms[@_index]? 24 | if time > @_bpms[@_index].timing 25 | @_bpm.reflect @_bpms[@_index].val 26 | @_index++ 27 | 28 | module.exports = BpmLayer 29 | 30 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "bms-proto", 3 | "version": "0.0.1", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "author": "bokuweb", 10 | "license": "MIT", 11 | "devDependencies": { 12 | "gulp": "*", 13 | "coffee-script": "*", 14 | "watchify": "*", 15 | "gulp-watchify": "*", 16 | "gulp-coffee": "*", 17 | "gulp-plumber": "*", 18 | "coffeeify": "*", 19 | "gulp-rename": "*", 20 | "gulp-mocha-phantomjs": "*", 21 | "gulp-webserver": "*", 22 | "gulp-shell": "*", 23 | "gulp-cson": "*" 24 | }, 25 | "browser": { 26 | "jquery": "./bower_components/jquery/dist/jquery.js", 27 | "lodash": "./bower_components/lodash/lodash.min.js" 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/greatEffect.coffee: -------------------------------------------------------------------------------- 1 | GreatEffect = cc.Sprite.extend 2 | ctor : (src, params)-> 3 | w = params.width 4 | h = params.height 5 | @_super src, cc.rect 0, 0, w, h 6 | animationframe = [] 7 | num = params.row * params.colum 8 | 9 | for i in [0...num] 10 | frame = new cc.SpriteFrame src, cc.rect w * (i % params.row), h * ~~(i / params.row), w, h 11 | animationframe.push frame 12 | @_animation = new cc.Animation animationframe, params.delay 13 | @_animation.retain() 14 | 15 | run : -> 16 | cb = -> 17 | @removeFromParent on 18 | @_animation.release() 19 | @release() 20 | 21 | action = cc.sequence( 22 | new cc.Animate @_animation 23 | cc.CallFunc.create cb, this 24 | ) 25 | @runAction action 26 | 27 | module.exports = GreatEffect 28 | -------------------------------------------------------------------------------- /src/judgementLayer.coffee: -------------------------------------------------------------------------------- 1 | JudgementLayer = cc.Layer.extend 2 | ctor: (@_skin) -> 3 | @_super() 4 | 5 | init : -> 6 | @_judgement = new cc.Sprite @_skin.src, cc.rect 0, 0, @_skin.width, @_skin.height 7 | @_judgement.x = cc.screenSize.width / 2 + @_skin.x 8 | @_judgement.y = cc.screenSize.height - @_skin.y 9 | @_judgement.setOpacity 0 10 | @addChild @_judgement 11 | 12 | # 13 | # frame = 14 | # 0 : pgreat 15 | # 1 : grear 16 | # 2 : good 17 | # 3 : bad 18 | # 4 : poor 19 | # 20 | show : (frame, combo, time_sec) -> 21 | @_judgement.setTextureRect cc.rect 0, @_skin.height * frame, @_skin.width, @_skin.height 22 | @_judgement.stopAllActions() 23 | @_judgement.setOpacity 255 24 | @_judgement.runAction cc.fadeOut time_sec 25 | 26 | module.exports = JudgementLayer 27 | -------------------------------------------------------------------------------- /src/audio.coffee: -------------------------------------------------------------------------------- 1 | Audio = cc.Layer.extend 2 | 3 | ctor : (@_timer, @_bgms)-> 4 | @_super() 5 | @_wav = [] 6 | @_index = 0 7 | @_audio = cc.audioEngine 8 | 9 | init : (res, prefix)-> 10 | @_wav[k] = (prefix + encodeURIComponent(v)) for k, v of res 11 | 12 | play : (id)-> 13 | @_audio.playEffect @_wav[id], false if @_wav[id]? 14 | 15 | startBgm : -> 16 | @scheduleUpdate() 17 | 18 | stopBgm : -> 19 | 20 | update : -> 21 | time = @_timer.get() 22 | while time >= @_bgms[@_index]?.timing 23 | @play @_bgms[@_index].id 24 | @_index++ 25 | 26 | onExit : -> 27 | @_super() 28 | @removeAllChildren on 29 | cc.audioEngine.stopAllEffects() 30 | for wav in @_wav when wav? 31 | cc.audioEngine.unloadEffect wav 32 | @_wav = [] 33 | 34 | module.exports = Audio 35 | -------------------------------------------------------------------------------- /src/greatEffectsLayer.coffee: -------------------------------------------------------------------------------- 1 | GreatEffect = require './greatEffect' 2 | 3 | GreatEffectLayer = cc.Layer.extend 4 | ctor : (@_res)-> 5 | @_super() 6 | @_effects = [] 7 | @_index = 0 8 | 9 | init : (num)-> 10 | @_batchNode = new cc.SpriteBatchNode @_res.src 11 | @addChild @_batchNode 12 | for i in [0...num] 13 | params = 14 | width : @_res.width 15 | height : @_res.height 16 | row : @_res.row 17 | colum : @_res.colum 18 | delay : @_res.delay 19 | effect = new GreatEffect @_batchNode.texture, params 20 | effect.y = -effect.y 21 | @_effects.push effect 22 | effect.retain() 23 | return 24 | 25 | run : (x, y)-> 26 | @_effects[@_index].x = x 27 | @_effects[@_index].y = y 28 | @_batchNode.addChild @_effects[@_index] 29 | @_effects[@_index].run() 30 | @_index += 1 31 | 32 | module.exports = GreatEffectLayer 33 | -------------------------------------------------------------------------------- /src/playtimeLayer.coffee: -------------------------------------------------------------------------------- 1 | NumeralLayer = require './numeralLayer' 2 | 3 | PlaytimeLayer = cc.Layer.extend 4 | ctor: (@_skin, @_timer) -> 5 | @_super() 6 | @_oldtime = 0 7 | @_minuite = new NumeralLayer @_skin 8 | @_second = new NumeralLayer @_skin 9 | 10 | init : -> 11 | @_minuite.init 2, 0 12 | @_minuite.x = cc.screenSize.width / 2 + @_skin.minuite.x 13 | @_minuite.y = cc.screenSize.height - @_skin.minuite.y 14 | @addChild @_minuite 15 | 16 | @_second.init 2, 0 17 | @_second.x = cc.screenSize.width / 2 + @_skin.second.x 18 | @_second.y = cc.screenSize.height - @_skin.second.y 19 | @addChild @_second 20 | 21 | start : -> @scheduleUpdate() 22 | 23 | update : -> 24 | now = ~~(@_timer.get() / 1000) 25 | if now isnt @_oldtime 26 | @_minuite.reflect now / 60 27 | @_second.reflect now % 60 28 | @_oldtime = now 29 | 30 | module.exports = PlaytimeLayer 31 | -------------------------------------------------------------------------------- /src/keyEffectsLayer.coffee: -------------------------------------------------------------------------------- 1 | KeyEffectsLayer = cc.Layer.extend 2 | ctor : (@_skin) -> 3 | @_super() 4 | @_effects = [] 5 | 6 | init : (xList) -> 7 | @_effects.length = 0 8 | for x, i in xList 9 | switch i 10 | when 0, 2, 4, 6 then @_effects[i] = new cc.Sprite @_skin.whiteKeydownImage.src 11 | when 1, 3, 5 then @_effects[i] = new cc.Sprite @_skin.blackKeydownImage.src 12 | when 7 then @_effects[i] = new cc.Sprite @_skin.turntableKeydownImage.src 13 | else throw new Error "error unlnown note" 14 | @_effects[i].x = x 15 | @_effects[i].y = cc.screenSize.height - @_skin.y 16 | #@_effects[i].setAnchorPoint cc.p(0.5,0) 17 | @addChild @_effects[i] 18 | @_effects[i].setOpacity 0 19 | return 20 | 21 | show : (id, time_sec) -> 22 | @_effects[id].setOpacity 255 23 | @_effects[id].runAction cc.fadeOut time_sec 24 | 25 | module.exports = KeyEffectsLayer 26 | -------------------------------------------------------------------------------- /src/numeralLayer.coffee: -------------------------------------------------------------------------------- 1 | NumeralLayer = cc.Layer.extend 2 | ctor: (@_skin) -> 3 | @_super() 4 | @_sprites = [] 5 | 6 | init : (@_digits, num = 0) -> 7 | scaledWidth = @_skin.width * @_skin.scale 8 | @_batchNode = new cc.SpriteBatchNode @_skin.src 9 | @addChild @_batchNode 10 | @_frames = for i in [0...10] 11 | new cc.SpriteFrame @_batchNode.texture, cc.rect @_skin.width * i, 0, @_skin.width, @_skin.height 12 | 13 | for i in [0...@_digits] 14 | @_sprites[i] = new cc.Sprite() 15 | @_sprites[i].setSpriteFrame @_frames[0] 16 | @_sprites[i].x = i * (-scaledWidth - @_skin.margin) + (scaledWidth * @_digits / 2) 17 | @_sprites[i].scale = @_skin.scale 18 | @_batchNode.addChild @_sprites[i] 19 | @reflect num 20 | 21 | reflect : (num) -> 22 | for i in [0...@_digits] 23 | @_sprites[i].setSpriteFrame @_frames[~~(num % 10)] 24 | num /= 10 25 | 26 | module.exports = NumeralLayer 27 | -------------------------------------------------------------------------------- /test/runner.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Mocha Test Runner 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /src/loaderScene.coffee: -------------------------------------------------------------------------------- 1 | LoaderScene = cc.LoaderScene.extend 2 | init : -> 3 | @_bgLayer = new cc.LayerColor cc.color(0, 0, 0, 255) 4 | @addChild @_bgLayer, 0 5 | 6 | @_label = new cc.LabelTTF "Loading... 0%", "Arial", 16 7 | @_label.x = cc.director.getWinSize().width / 2 8 | @_label.y = cc.director.getWinSize().height / 2 9 | @_label.setColor cc.color(255, 255, 255) 10 | @_bgLayer.addChild @_label, 10 11 | 12 | _initStage : (img, centerPos) -> 13 | #@_super img, centerPos 14 | 15 | onEnter : -> 16 | @_super() 17 | 18 | onExit : -> 19 | @_super() 20 | 21 | initWithResources : (resources, cb, target) -> 22 | @_super resources, cb, target 23 | 24 | _startLoading : -> 25 | @_super() 26 | 27 | LoaderScene.preload = (resources, cb, target) -> 28 | _cc = cc 29 | if not _cc.loaderScene 30 | _cc.loaderScene = new LoaderScene() 31 | _cc.loaderScene.init() 32 | 33 | _cc.loaderScene.initWithResources resources, cb, target 34 | cc.director.runScene _cc.loaderScene 35 | _cc.loaderScene 36 | 37 | module.exports = LoaderScene 38 | -------------------------------------------------------------------------------- /src/resource.coffee: -------------------------------------------------------------------------------- 1 | resObjs = 2 | bgImage : "res/bg.jpg" 3 | nodeImage : "res/node.png" 4 | noteWhiteImage : "res/note-white.png" 5 | noteBlackImage : "res/note-black.png" 6 | noteTurntableImage : "res/note-turntable.png" 7 | noteBgImage : "res/note-bg.png" 8 | greatEffectImage : "res/great-effect.png" 9 | turntableKeydownImage : "res/turntable-keydown.png" 10 | whiteKeydownImage : "res/white-keydown.png" 11 | blackKeydownImage : "res/black-keydown.png" 12 | testImage : "res/test.png" 13 | meterImage : "res/gauge.png" 14 | numeralImage : "res/number.png" 15 | turntableImage : "res/turntable.png" 16 | soundonlyImage : "res/sound-only.png" 17 | resultBgImage : "res/result-bg.jpg" 18 | menuBgImage : "res/menu-bg.jpg" 19 | itemBgImage : "res/item-bg.png" 20 | levelFontImage : "res/level-font.png" 21 | 22 | resList = [] 23 | 24 | resList.push v for k, v of resObjs 25 | 26 | # fontのみ絶対pathにしないとディレクトリ構造とURLの差異でロードに失敗する 27 | resList.push 28 | type : "font" 29 | name : "sapceage" 30 | srcs : [cc.pathToBmsDir + "res/fonts/spaceage.ttf"] 31 | 32 | module.exports = 33 | resObjs : resObjs 34 | resList : resList 35 | -------------------------------------------------------------------------------- /test/src/test-greatEffect.coffee: -------------------------------------------------------------------------------- 1 | GreatEffectTest = cc.Class.extend 2 | 3 | start : -> 4 | describe 'great effect class test', -> 5 | @timeout 3000 6 | GreatEffect = require '../../src/greatEffect' 7 | capture = require './test-utils' 8 | .capture 9 | expect = chai.expect 10 | capNum = 0 11 | 12 | it 'great effect create and update test', (done)-> 13 | TestScene = cc.Scene.extend 14 | onEnter : -> 15 | @_super() 16 | params = 17 | width : 80 18 | height : 80 19 | row : 6 20 | colum : 3 21 | delay : 0.02 22 | effect = new GreatEffect '../res/great-effect.png', params 23 | effect.x = cc.director.getWinSize().width / 2 24 | effect.y = cc.director.getWinSize().height / 2 25 | @addChild effect 26 | effect.run() 27 | 28 | expect @children.length 29 | .to.be.equal 1 30 | 31 | setTimeout => 32 | expect @children.length 33 | .to.be.equal 0 34 | done() 35 | , 1000 36 | 37 | cc.director.runScene new TestScene() 38 | 39 | 40 | module.exports = GreatEffectTest 41 | 42 | 43 | -------------------------------------------------------------------------------- /src/animationLayer.coffee: -------------------------------------------------------------------------------- 1 | POOR_INDICATOR_TIME_MSEC = 200 2 | 3 | AnimationLayer = cc.Layer.extend 4 | ctor: (@_skin, @_timer) -> 5 | @_super() 6 | @_srcs = [] 7 | 8 | init : (srcs, @_bmps, prefix) -> 9 | @_index = 0 10 | @_isPoor = false 11 | for k, v of srcs 12 | @_srcs[k] = prefix + v 13 | @_firstImage ?= prefix + v 14 | 15 | @_bmp = new cc.Sprite @_firstImage 16 | @_bmp.x = cc.screenSize.width / 2 + @_skin.x 17 | @_bmp.y = cc.screenSize.height - @_skin.y 18 | #@_bmp.width = @_skin.width 19 | #@_bmp.height = @_skin.height 20 | #@_bmp.scale = 3.06 21 | @_bmp.scale = 2.5 22 | @_bmp.setOpacity 0 23 | @addChild @_bmp 24 | 25 | start : -> @scheduleUpdate() 26 | 27 | onPoor : -> 28 | if @_srcs[0]? 29 | @_isPoor = true 30 | @_bmp.setTexture @_srcs[0] 31 | @scheduleOnce @_disablePoor, POOR_INDICATOR_TIME_MSEC / 1000 32 | 33 | update : -> 34 | time = @_timer.get() 35 | if time > @_bmps[@_index]?.timing 36 | @_bmp.setOpacity 255 37 | @_bmp.setTexture @_srcs[@_bmps[@_index].id] unless @_isPoor 38 | @_index++ 39 | 40 | onExit : -> 41 | @_super() 42 | @removeAllChildren on 43 | @_srcs = [] 44 | 45 | _disablePoor : -> @_isPoor = false 46 | 47 | module.exports = AnimationLayer 48 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Cocos2d-html5 Hello World test 6 | 7 | 8 | 9 | 10 | 11 | 12 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /test/src/test-note.coffee: -------------------------------------------------------------------------------- 1 | NoteTest = cc.Class.extend 2 | 3 | start : -> 4 | describe 'note class test', -> 5 | @timeout 10000 6 | Note = require '../../src/note' 7 | Timer = require '../../src/timer' 8 | capture = require './test-utils' 9 | .capture 10 | expect = chai.expect 11 | capNum = 0 12 | 13 | it 'note create and update test', (done)-> 14 | TestScene = cc.Scene.extend 15 | onEnter : -> 16 | @_super() 17 | @_timer = new Timer() 18 | note = new Note '../res/note-white.png', @_timer, 100 19 | note.x = 160 20 | note.y = -note.height 21 | note.timing = 2000 22 | note.clear = false 23 | note.appendFallParams [timing : 0, val : 180], 0, 400 24 | @addChild note 25 | note.start() 26 | @_timer.start() 27 | expect @children.length 28 | .to.be.equal 1 29 | 30 | setTimeout => 31 | capture "test/capture/notes/notes" + capNum++ 32 | , 2000 33 | 34 | setTimeout => 35 | expect @children.length 36 | .to.be.equal 0 37 | done() 38 | , 2500 39 | 40 | cc.director.runScene new TestScene() 41 | 42 | before -> 43 | 44 | after -> 45 | 46 | 47 | module.exports = NoteTest 48 | 49 | 50 | -------------------------------------------------------------------------------- /test/src/test-animationLayer.coffee: -------------------------------------------------------------------------------- 1 | AnimationLayerTest = cc.Class.extend 2 | start : -> 3 | describe 'animation layer class test', -> 4 | @timeout 10000 5 | AnimationLayer = require '../../src/animationLayer' 6 | Timer = require '../../src/timer' 7 | capture = require './test-utils' 8 | .capture 9 | expect = chai.expect 10 | capNum = 0 11 | 12 | it 'initialize stats and capture stats', (done)-> 13 | TestScene = cc.Scene.extend 14 | onEnter : -> 15 | @_super() 16 | size = cc.director.getWinSize() 17 | timer = new Timer() 18 | skin = 19 | x : size.width / 2 20 | y : size.height / 2 21 | bmp = new AnimationLayer skin, timer 22 | srcs = [ 23 | '../res/test1.png' 24 | '../res/test2.png' 25 | '../res/test3.png' 26 | ] 27 | bmps = [ 28 | {timing : 1000, id : 0} 29 | {timing : 2000, id : 1} 30 | {timing : 1000, id : 2} 31 | ] 32 | 33 | bmp.init srcs, bmps, '' 34 | @addChild bmp 35 | bmp.start() 36 | timer.start() 37 | 38 | setTimeout => 39 | capture "test/capture/BmpLayer/bmp" + capNum++ 40 | done() 41 | , 4100 42 | 43 | cc.director.runScene new TestScene() 44 | 45 | module.exports = AnimationLayerTest 46 | 47 | 48 | -------------------------------------------------------------------------------- /test/src/test-playtimeLayer.coffee: -------------------------------------------------------------------------------- 1 | PlaytimeLayerTest = cc.Class.extend 2 | start : -> 3 | describe 'playtime layer class test', -> 4 | @timeout 10000 5 | PlaytimeLayer = require '../../src/playtimeLayer' 6 | Timer = require '../../src/timer' 7 | capture = require './test-utils' 8 | .capture 9 | expect = chai.expect 10 | capNum = 0 11 | 12 | it 'initialize playtime and capture stats', (done)-> 13 | TestScene = cc.Scene.extend 14 | onEnter : -> 15 | @_super() 16 | size = cc.director.getWinSize() 17 | timer = new Timer() 18 | 19 | skin = 20 | src : '../res/numeral-font.png' 21 | width : 29.5 22 | height : 36.5 23 | scale : 0.35 24 | margin : 5 25 | minuite : 26 | x : size.width / 2 - 20 27 | y : size.height / 2 28 | second : 29 | x : size.width / 2 + 20 30 | y : size.height / 2 31 | 32 | playtime = new PlaytimeLayer skin, timer 33 | 34 | playtime.init() 35 | @addChild playtime 36 | playtime.start() 37 | timer.set 58000 38 | timer.start() 39 | setTimeout => 40 | capture "test/capture/playtimeLayer/time" + capNum++ 41 | done() 42 | , 5200 43 | cc.director.runScene new TestScene() 44 | 45 | module.exports = PlaytimeLayerTest 46 | 47 | 48 | -------------------------------------------------------------------------------- /src/main.coffee: -------------------------------------------------------------------------------- 1 | window.bmsStart = (pathToBmsDir) -> 2 | cc.pathToBmsDir = pathToBmsDir 3 | cc.screenSize = window.parent.screen 4 | cc.game.run() 5 | cc.game.onStart = -> 6 | MenuScene = require './menuScene' 7 | resList = require './resource' 8 | .resList 9 | if not cc.sys.isNative and document.getElementById "cocosLoading" 10 | document.body.removeChild(document.getElementById "cocosLoading") 11 | 12 | cc.view.enableRetina off 13 | cc.view.adjustViewPort on 14 | 15 | if cc.sys.isMobile 16 | #height = cc.view.getFrameSize().height / cc.view.getFrameSize().width * 320 17 | #cc.view.setDesignResolutionSize 320, height, cc.ResolutionPolicy.SHOW_ALL 18 | cc.view.setDesignResolutionSize 320, 480, cc.ResolutionPolicy.SHOW_ALL 19 | cc.view.resizeWithBrowserSize on 20 | else 21 | screenSize = window.parent.screen 22 | cc.view.setDesignResolutionSize screenSize.width, screenSize.height, cc.ResolutionPolicy.SHOW_ALL 23 | #if window.innerWidth < 800 or window.innerHeight < 600 24 | # policy = new cc.ResolutionPolicy cc.ContainerStrategy.ORIGINAL_CONTAINER, cc.ContentStrategy.SHOW_ALL 25 | # cc.view.setDesignResolutionSize 800, 600, cc.ContainerStrategy.ORIGINAL_CONTAINER 26 | cc.view.resizeWithBrowserSize off 27 | cc.director.setProjection cc.Director.PROJECTION_2D 28 | cc.director.setContentScaleFactor 2 29 | 30 | require('./loaderScene').preload resList, -> 31 | cc.director.runScene new MenuScene() 32 | , this 33 | 34 | 35 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## bmsjs 2 | 3 | ![](https://travis-ci.org/bokuweb/bmsjs.svg?branch=master) 4 | 5 | *** work in progress *** 6 | 7 | browser bms player project. 8 | 9 | ### Dependencies 10 | 11 | * cocos2d-JS Full v3.6.1 12 | * CoffeeScript v1.9.2 13 | * Gulp v3.8.11 14 | * bower v1.3.12 15 | 16 | ### Installation 17 | 18 | 1. Install cocos2d-JS 19 | download here cocos2d-JS v3.6.1, and run ```setup.py```. 20 | http://www.cocos2d-x.org/download 21 | 22 | 2. Create cocos2d-JS project 23 | 24 | ``` 25 | cocos new bmsjs -l js -d . 26 | ``` 27 | 3. Clone this repository and overwrite to bmsjs(cocos2d-JS project) directory 28 | 29 | 4. Install packages 30 | 31 | ``` 32 | npm i 33 | bower i 34 | ``` 35 | 36 | 5. Build app 37 | build *.coffee to main.js 38 | ``` 39 | gulp build 40 | ``` 41 | 42 | 6. Run on browser 43 | ``` 44 | cocos run -p web 45 | ``` 46 | 47 | ### Run test 48 | 49 | #### Run test on PhantomJS 50 | 51 | ``` 52 | gulp test 53 | ``` 54 | 55 | #### Run test on your browser 56 | 57 | ``` 58 | gulp build 59 | ``` 60 | 61 | Access ```test/runner.html```. 62 | 63 | 64 | ### Online demo 65 | 66 | http://bokuweb.github.io/bmsjs 67 | 68 | ### License 69 | 70 | クリエイティブ・コモンズ・ライセンス
Attribution-NonCommercial-NoDerivatives 4.0 International (CC BY-NC-ND 4.0) 71 | -------------------------------------------------------------------------------- /test/src/test-bpmLayer.coffee: -------------------------------------------------------------------------------- 1 | BpmLayerTest = cc.Class.extend 2 | start : -> 3 | describe 'bpm layer class test', -> 4 | @timeout 10000 5 | Bpm = require '../../src/bpmLayer' 6 | Timer = require '../../src/timer' 7 | capture = require './test-utils' 8 | .capture 9 | expect = chai.expect 10 | capNum = 0 11 | 12 | it 'bpm update test', (done)-> 13 | TestScene = cc.Scene.extend 14 | onEnter : -> 15 | @_super() 16 | size = cc.director.getWinSize() 17 | timer = new Timer() 18 | skin = 19 | src : '../res/numeral-font.png' 20 | width : 29.5 21 | height : 36.5 22 | scale : 0.35 23 | margin : 5 24 | x : size.width / 2 25 | y : size.height / 2 26 | 27 | bpms = [ 28 | {timing : 500, val : 100} 29 | {timing : 1000, val : 200} 30 | {timing : 1500, val : 300} 31 | ] 32 | 33 | bpm = new Bpm skin, timer, bpms 34 | bpm.init() 35 | @addChild bpm 36 | bpm.start() 37 | timer.start() 38 | 39 | setTimeout => 40 | capture "test/capture/BpmLayer/bmp" + capNum++ 41 | , 750 42 | 43 | setTimeout => 44 | capture "test/capture/BpmLayer/bmp" + capNum++ 45 | , 1250 46 | 47 | setTimeout => 48 | capture "test/capture/BpmLayer/bmp" + capNum++ 49 | done() 50 | , 2500 51 | 52 | cc.director.runScene new TestScene() 53 | 54 | module.exports = BpmLayerTest 55 | 56 | 57 | -------------------------------------------------------------------------------- /src/fallObj.coffee: -------------------------------------------------------------------------------- 1 | fallObj = cc.Sprite.extend 2 | 3 | ctor : (texture)-> 4 | @_super texture 5 | 6 | start : -> @scheduleUpdate() 7 | 8 | update : -> 9 | time = @_timer.get() 10 | while time > @bpm.timing[@index] 11 | if @index < @bpm.timing.length - 1 then @index++ 12 | else break 13 | diffTime = @bpm.timing[@index] - time 14 | diffDist = diffTime * @speed[@index] 15 | @y = @dstY[@index] + diffDist 16 | 17 | # 18 | # append BPM, speed, destination y coordinate list to falling object 19 | # 20 | appendFallParams : (bpms, time, fallDist)-> 21 | size = cc.director.getWinSize() 22 | previousBpm = 0 23 | @dstY = [] 24 | @index = 0 25 | @speed = [] 26 | @bpm = 27 | timing : [] 28 | val : [] 29 | 30 | for v, i in bpms when time < v.timing < @timing 31 | @bpm.timing.push v.timing 32 | @bpm.val.push v.val 33 | 34 | # calculate bpm before object will be created 35 | if bpms[0].timing > time then previousBpm = bpms[0].val 36 | else previousBpm = v.val for v in bpms when v.timing <= time 37 | 38 | @dstY[@bpm.timing.length] = size.height - fallDist 39 | @bpm.timing.push @timing 40 | 41 | # calculate destination of Y coordinate 42 | for v, i in @bpm.timing by -1 when i < @bpm.timing.length - 1 43 | diffDist = (@bpm.timing[i+1] - v) * @calcSpeed(@bpm.val[i], fallDist) 44 | @dstY[i] = @dstY[i+1] + diffDist 45 | @bpm.val.splice 0, 0, previousBpm 46 | 47 | @speed.push @calcSpeed v, fallDist for v in @bpm.val 48 | return 49 | 50 | # return object speed [px/msec] 51 | calcSpeed : (bpm, fallDist) -> 52 | measureTime = 240000 / bpm 53 | fallDist / measureTime 54 | 55 | module.exports = fallObj 56 | -------------------------------------------------------------------------------- /test/src/test-timer.coffee: -------------------------------------------------------------------------------- 1 | $ = require 'jquery' 2 | 3 | TimerTest = cc.Class.extend 4 | start : -> 5 | describe 'timer class test', -> 6 | @timeout 10000 7 | Timer = require '../../src/timer' 8 | expect = chai.expect 9 | 10 | it 'should timer.get() return 0 before start', -> 11 | time = @_timer.get() 12 | expect time 13 | .to.be.equal 0 14 | 15 | it 'set timer 1000msec, should get() return about 1000msec', (done)-> 16 | @_timer.start() 17 | setTimeout => 18 | time = @_timer.get() 19 | expect time 20 | .to.be.within 900, 1100 21 | @_timer.stop() 22 | done() 23 | , 1000 24 | 25 | it 'timer pause test, should timer return about 2000msec', (done)-> 26 | @_timer.start() 27 | pause1secAfter = => 28 | d = new $.Deferred 29 | setTimeout => 30 | @_timer.pause() 31 | d.resolve() 32 | , 1000 33 | d.promise() 34 | 35 | restart1secAfter = => 36 | d = new $.Deferred 37 | setTimeout => 38 | @_timer.start() 39 | d.resolve() 40 | , 1000 41 | d.promise() 42 | 43 | check1secAfter = => 44 | d = new $.Deferred 45 | setTimeout => 46 | d.resolve() 47 | , 1000 48 | d.promise() 49 | 50 | pause1secAfter() 51 | .then restart1secAfter 52 | .then check1secAfter 53 | .then => 54 | time = @_timer.get() 55 | expect time 56 | .to.be.within 1900, 2100 57 | done() 58 | 59 | before -> 60 | @_timer = new Timer() 61 | 62 | module.exports = TimerTest 63 | 64 | 65 | -------------------------------------------------------------------------------- /test/src/test-keyEffectLayer.coffee: -------------------------------------------------------------------------------- 1 | keyEffectLayerTest = cc.Class.extend 2 | start : -> 3 | describe 'key effect layer class test', -> 4 | @timeout 3000 5 | $ = require 'jquery' 6 | KeyEffectLayer = require '../../src/keyEffectsLayer' 7 | capture = require './test-utils' 8 | .capture 9 | expect = chai.expect 10 | capNum = 0 11 | 12 | it 'key effect layer create and show test', (done)-> 13 | TestScene = cc.Scene.extend 14 | onEnter : -> 15 | @_super() 16 | xList = [41,63,80,102,119,141,158,0] 17 | skin = 18 | y : 160 19 | turntableKeydownImage : 20 | src : "../res/turntable-keydown.png" 21 | whiteKeydownImage : 22 | src : "../res/white-keydown.png" 23 | blackKeydownImage : 24 | src : "../res/black-keydown.png" 25 | 26 | keyEffectLayer = new KeyEffectLayer skin 27 | keyEffectLayer.init xList 28 | @addChild keyEffectLayer 29 | promise = null 30 | index = 0 31 | promise = do -> 32 | d = new $.Deferred 33 | id = setInterval -> 34 | keyEffectLayer.show index, 0.1 35 | index += 1 36 | console.log xList.length 37 | capture "test/capture/keyEffect/keyEffect" + capNum++ 38 | if xList.length is index 39 | clearInterval id 40 | d.resolve() 41 | , 20 42 | d.promise() 43 | promise.then -> done() 44 | 45 | cc.director.runScene new TestScene() 46 | 47 | before -> 48 | 49 | after -> 50 | 51 | 52 | module.exports = keyEffectLayerTest 53 | 54 | 55 | -------------------------------------------------------------------------------- /gulpfile.coffee: -------------------------------------------------------------------------------- 1 | gulp = require 'gulp' 2 | watchify = require 'gulp-watchify' 3 | plumber = require 'gulp-plumber' 4 | rename = require 'gulp-rename' 5 | mochaPhantomJS = require 'gulp-mocha-phantomjs' 6 | webserver = require 'gulp-webserver' 7 | #shell = require 'gulp-shell' 8 | cson = require 'gulp-cson' 9 | 10 | watching = off 11 | 12 | gulp.task 'enable-watch-mode', -> watching = on 13 | 14 | gulp.task 'build:app', watchify (watchify)-> 15 | gulp.src 'src/main.coffee' 16 | .pipe plumber() 17 | .pipe watchify 18 | watch : watching 19 | extensions: ['.coffee', '.js'] 20 | transform : ['coffeeify'] 21 | .pipe rename 22 | extname: ".js" 23 | .pipe gulp.dest '' 24 | 25 | gulp.task 'build:test', watchify (watchify)-> 26 | gulp.src 'test/src/test-main.coffee' 27 | .pipe plumber() 28 | .pipe watchify 29 | watch : watching 30 | extensions: ['.coffee', '.js'] 31 | transform : ['coffeeify'] 32 | .pipe rename 33 | basename: "test-all", 34 | extname: ".js" 35 | .pipe gulp.dest 'test' 36 | 37 | gulp.task 'cson', -> 38 | gulp.src 'bms.cson' 39 | .pipe cson() 40 | .pipe gulp.dest './' 41 | 42 | #gulp.task 'run', ['build:app'], -> 43 | # shell.task [ 44 | # 'cocos run -p web' 45 | # ] 46 | 47 | gulp.task 'test', ['build:test'], -> 48 | gulp.src './test/runner.html' 49 | .pipe mochaPhantomJS 50 | phantomjs: 51 | viewportSize: 52 | width: 800 53 | height: 600 54 | dump:'test.xml' 55 | 56 | gulp.task 'webserver', -> 57 | gulp.src '' 58 | .pipe webserver 59 | directoryListing: true 60 | open: true 61 | 62 | gulp.task 'build', ['build:app', 'build:test'] 63 | 64 | gulp.task 'watch:app', ['enable-watch-mode', 'build:app'] 65 | 66 | gulp.task 'watch:test', ['enable-watch-mode', 'build:test'] 67 | 68 | gulp.task 'watch', ['enable-watch-mode', 'build:test', 'build:app'] 69 | -------------------------------------------------------------------------------- /test/src/test-main.coffee: -------------------------------------------------------------------------------- 1 | GreatEffectTest = require './test-greatEffect' 2 | keyEffectLayerTest = require './test-keyEffectLayer' 3 | ParserTest = require './test-parser' 4 | TimerTest = require './test-timer' 5 | NoteTest = require './test-note' 6 | NumeralLayerTest = require './test-numeralLayer' 7 | StatsLayerTest = require './test-statsLayer' 8 | PlaytimeLayerTest = require './test-playtimeLayer' 9 | JudgementLayerTest = require './test-judgementLayer' 10 | AnimationLayerTest = require './test-animationLayer' 11 | BpmLayerTest = require './test-bpmLayer' 12 | RateLayerTest = require './test-rateLayer' 13 | 14 | window.onload = -> 15 | cc.game.onStart = -> 16 | 17 | cc.screenSize = window.parent.screen 18 | 19 | cc.view.enableRetina off 20 | cc.view.adjustViewPort on 21 | policy = new cc.ResolutionPolicy cc.ContainerStrategy.ORIGINAL_CONTAINER, cc.ContentStrategy.SHOW_ALL 22 | cc.view.setDesignResolutionSize 800, 600, policy 23 | cc.director.setContentScaleFactor 2 24 | 25 | rateLayerTest = new RateLayerTest() 26 | bpmLayerTest = new BpmLayerTest() 27 | animationLayerTest = new AnimationLayerTest() 28 | judgmentLayerTest = new JudgementLayerTest() 29 | playtimeLayerTest = new PlaytimeLayerTest() 30 | statsLayerTest = new StatsLayerTest() 31 | numeralLayerTest = new NumeralLayerTest() 32 | keyEffectLayerTest = new keyEffectLayerTest() 33 | greatEffectTest = new GreatEffectTest() 34 | parserTest = new ParserTest() 35 | timerTest = new TimerTest() 36 | noteTest = new NoteTest() 37 | 38 | rateLayerTest.start() 39 | bpmLayerTest.start() 40 | animationLayerTest.start() 41 | judgmentLayerTest.start() 42 | playtimeLayerTest.start() 43 | statsLayerTest.start() 44 | numeralLayerTest.start() 45 | keyEffectLayerTest.start() 46 | greatEffectTest.start() 47 | parserTest.start() 48 | timerTest.start() 49 | noteTest.start() 50 | 51 | if window.mochaPhantomJS 52 | mochaPhantomJS.run() 53 | else 54 | mocha.run() 55 | 56 | 57 | cc.game.run 'gameCanvas' 58 | -------------------------------------------------------------------------------- /src/gameoverScene.coffee: -------------------------------------------------------------------------------- 1 | res = require './resource' 2 | .resObjs 3 | 4 | 5 | GameOverLayer = cc.Layer.extend 6 | ctor : (@_stats) -> 7 | @_super() 8 | @_addBackground() 9 | @_MenuScene = require './menuScene' 10 | 11 | toucheventListener = cc.EventListener.create 12 | event: cc.EventListener.TOUCH_ONE_BY_ONE 13 | swallowTouches: true 14 | onTouchBegan: @_onTouch.bind this 15 | cc.eventManager.addListener toucheventListener, this 16 | 17 | if 'keyboard' of cc.sys.capabilities 18 | cc.eventManager.addListener 19 | event : cc.EventListener.KEYBOARD 20 | onKeyPressed : @_onKeyPressed.bind this 21 | , this 22 | 23 | @_stats.score ?= 0 24 | label = new cc.LabelTTF "", "sapceage" , 48 25 | label.x = cc.director.getWinSize().width / 2 26 | label.y = cc.director.getWinSize().height / 2 27 | @addChild label, 5 28 | 29 | label.setString """ 30 | SCORE : #{@_stats.score} 31 | MAX COMBO : #{@_stats.combo} 32 | PGREAT : #{@_stats.pgreat} 33 | GREAT : #{@_stats.great} 34 | GOOD : #{@_stats.good} 35 | BAD : #{@_stats.bad} 36 | POOR : #{@_stats.poor} 37 | """ 38 | 39 | onExit : -> 40 | @_super() 41 | @removeAllChildren on 42 | 43 | _addBackground : -> 44 | bg = new cc.Sprite res.resultBgImage 45 | bg.x = cc.director.getWinSize().width / 2 46 | bg.y = cc.director.getWinSize().height / 2 47 | bg.scale = window.parent.screen.height / bg.height 48 | @addChild bg, 0 49 | 50 | _onTouch : (touch, event)-> 51 | target = event.getCurrentTarget() 52 | locationInNode = target.convertToNodeSpace touch.getLocation() 53 | s = target.getContentSize() 54 | rect = cc.rect 0, 0, s.width, s.height 55 | if cc.rectContainsPoint rect, locationInNode 56 | cc.director.runScene new cc.TransitionFade(1.2, new @_MenuScene()) 57 | return true 58 | return false 59 | 60 | _onKeyPressed : (key, event) -> 61 | cc.director.runScene new cc.TransitionFade(1.2, new @_MenuScene()) 62 | 63 | GameOverScene = cc.Scene.extend 64 | 65 | ctor : (stats) -> 66 | @_super() 67 | layer = new GameOverLayer stats 68 | @addChild layer 69 | 70 | module.exports = GameOverScene 71 | -------------------------------------------------------------------------------- /test/src/test-rateLayer.coffee: -------------------------------------------------------------------------------- 1 | skin = 2 | rate : 3 | z : 10 4 | meter : 5 | src : '../res/gauge.png' 6 | width : 4 7 | height : 12 8 | x : 122 9 | y : 207 10 | z : 10 11 | label : 12 | src : '../res/numeral-font.png' 13 | width : 29.5 14 | height : 36.5 15 | scale : 0.3 16 | margin : 3 17 | x : 362 18 | y : 210 19 | 20 | config = 21 | initRate : 20 22 | great : 1 23 | good : 0.5 24 | bad : -0.4 25 | poor : -0.4 26 | num : 50 27 | clearVal : 40 28 | 29 | RateLayerTest = cc.Class.extend 30 | start : -> 31 | describe 'rate layer class test', -> 32 | @timeout 10000 33 | RateLayer = require '../../src/rateLayer' 34 | capture = require './test-utils' 35 | .capture 36 | expect = chai.expect 37 | capNum = 0 38 | 39 | it 'initialize rate and capture', (done)-> 40 | TestScene = cc.Scene.extend 41 | onEnter : -> 42 | @_super() 43 | size = cc.director.getWinSize() 44 | rate = new RateLayer skin.rate 45 | rate.init config 46 | @addChild rate, skin.rate.z 47 | rate.start() 48 | 49 | setTimeout => 50 | expect(rate.get()).to.be.equal config.initRate 51 | capture "test/capture/StatsLayer/stats" + capNum++ 52 | done() 53 | , 1000 54 | cc.director.runScene new TestScene() 55 | 56 | it 'update rate by great', (done)-> 57 | TestScene = cc.Scene.extend 58 | onEnter : -> 59 | @_super() 60 | size = cc.director.getWinSize() 61 | rate = new RateLayer skin.rate 62 | rate.init config 63 | @addChild rate, skin.rate.z 64 | rate.start() 65 | num = ~~(100 - config.initRate / config.great) + 1 66 | console.log num 67 | for i in [0...num] 68 | rate.reflect 'pgreat' 69 | 70 | setTimeout => 71 | expect(rate.get()).to.be.equal 100 72 | capture "test/capture/StatsLayer/stats" + capNum++ 73 | done() 74 | , 1000 75 | cc.director.runScene new TestScene() 76 | 77 | module.exports = RateLayerTest 78 | 79 | 80 | -------------------------------------------------------------------------------- /src/searchLayer.coffee: -------------------------------------------------------------------------------- 1 | EventObserver = require './eventObserver' 2 | SearchController = require './searchController' 3 | 4 | SearchLayer = cc.Layer.extend 5 | ctor : -> 6 | @_super() 7 | @_notifier = new EventObserver() 8 | 9 | init : (searchItems) -> 10 | @_oldTxt = null 11 | @_searchController = new SearchController searchItems 12 | 13 | if 'touches' of cc.sys.capabilities 14 | cc.eventManager.addListener 15 | event : cc.EventListener.TOUCH_ALL_AT_ONCE 16 | onTouchesEnded: @_onTouchesEnded 17 | , this 18 | else if 'mouse' of cc.sys.capabilities 19 | cc.eventManager.addListener 20 | event : cc.EventListener.MOUSE 21 | onMouseUp: @_onMouseUp.bind this 22 | , this 23 | 24 | @_textField = new cc.TextFieldTTF "search", "Arial", 20 25 | @addChild @_textField 26 | #@_textField.setDelegate this 27 | size = cc.director.getWinSize() 28 | @_textField.x = 120 29 | @_textField.y = 50 30 | 31 | addListener: (name, listner)-> 32 | @_notifier.on name, listner 33 | 34 | start : -> @scheduleUpdate() 35 | 36 | update : -> 37 | txt = @_textField.getContentText() 38 | if @_oldTxt isnt txt 39 | @_oldTxt = txt 40 | @_notifier.trigger 'change', @_searchController.search(txt) 41 | 42 | #if txt is "" 43 | # @_textField.setPlaceHolder "search" 44 | 45 | # CCTextFieldDelegate 46 | onTextFieldAttachWithIME : (sender) -> 47 | 48 | onTextFieldDetachWithIME : (sender) -> 49 | 50 | onTextFieldInsertText : (sender, text, len) -> 51 | 52 | onTextFieldDeleteBackward : (sender, delText, len) -> 53 | 54 | _textInputGetRect : (node) -> 55 | r = cc.rect node.x, node.y, node.width, node.height 56 | r.x -= r.width / 2 57 | r.y -= r.height / 2 58 | r 59 | 60 | _onClickTrackNode : (clicked) -> 61 | #@_textField.setPlaceHolder "" 62 | if clicked 63 | @_textField.attachWithIME() 64 | else 65 | @_textField.detachWithIME() 66 | 67 | _onMouseUp : (event) -> 68 | target = event.getCurrentTarget() 69 | return unless target._textField 70 | point = event.getLocation() 71 | rect = target._textInputGetRect target._textField 72 | target._onClickTrackNode cc.rectContainsPoint(rect, point) 73 | 74 | onExit : -> 75 | @_super() 76 | @unscheduleUpdate() 77 | @removeAllChildren on 78 | 79 | module.exports = SearchLayer 80 | -------------------------------------------------------------------------------- /test/src/test-judgementLayer.coffee: -------------------------------------------------------------------------------- 1 | JudgementLayerTest = cc.Class.extend 2 | start : -> 3 | describe 'judgement layer class test', -> 4 | @timeout 10000 5 | JudgementLayer = require '../../src/judgementLayer' 6 | $ = require 'jquery' 7 | capture = require './test-utils' 8 | .capture 9 | expect = chai.expect 10 | capNum = 0 11 | 12 | it 'show judgement test', (done)-> 13 | TestScene = cc.Scene.extend 14 | onEnter : -> 15 | @_super() 16 | size = cc.director.getWinSize() 17 | judgement = new JudgementLayer 18 | src : '../res/judge-image.png' 19 | width : 51 20 | height : 15 21 | x : size.width / 2 22 | y : size.height / 2 23 | judgement.init() 24 | @addChild judgement 25 | 26 | showGreat = -> 27 | d = new $.Deferred 28 | judgement.show 0, 0, 0.5 29 | setTimeout -> 30 | capture "test/capture/judgementLayer/judgement" + capNum++ 31 | d.resolve() 32 | , 400 33 | d.promise() 34 | 35 | showGood = -> 36 | d = new $.Deferred 37 | judgement.show 1, 0, 0.5 38 | setTimeout -> 39 | capture "test/capture/judgementLayer/judgement" + capNum++ 40 | d.resolve() 41 | , 400 42 | d.promise() 43 | 44 | showBad = -> 45 | d = new $.Deferred 46 | judgement.show 2, 0, 0.5 47 | setTimeout -> 48 | capture "test/capture/judgementLayer/judgement" + capNum++ 49 | d.resolve() 50 | , 400 51 | d.promise() 52 | 53 | showPoor = -> 54 | d = new $.Deferred 55 | judgement.show 3, 0, 0.5 56 | setTimeout -> 57 | capture "test/capture/judgementLayer/judgement" + capNum++ 58 | d.resolve() 59 | , 400 60 | d.promise() 61 | 62 | showGreat() 63 | .then showGood 64 | .then showBad 65 | .then showPoor 66 | .then -> 67 | setTimeout -> 68 | done() 69 | , 500 70 | 71 | cc.director.runScene new TestScene() 72 | 73 | module.exports = JudgementLayerTest 74 | 75 | 76 | -------------------------------------------------------------------------------- /src/rateLayer.coffee: -------------------------------------------------------------------------------- 1 | NumeralLayer = require './numeralLayer' 2 | 3 | RateLayer = cc.Layer.extend 4 | ctor : (@_skin) -> 5 | @_super() 6 | @_label = new NumeralLayer @_skin.label 7 | @_sprites = [] 8 | @_count = 0 9 | 10 | init : (@_config) -> 11 | @_batchNode = new cc.SpriteBatchNode @_skin.meter.src 12 | @addChild @_batchNode 13 | @_frames = for i in [0...4] 14 | new cc.SpriteFrame @_batchNode.texture, cc.rect @_skin.meter.width * i, 0, @_skin.meter.width, @_skin.meter.height 15 | 16 | for i in [0...@_config.num] 17 | @_sprites[i] = new cc.Sprite() 18 | @_sprites[i].setSpriteFrame @_frames[0] 19 | @_sprites[i].x = cc.screenSize.width / 2 + @_skin.meter.x + i * @_skin.meter.width 20 | @_sprites[i].y = cc.screenSize.height - @_skin.meter.y 21 | @_batchNode.addChild @_sprites[i] 22 | @_rate = @_config.initRate 23 | 24 | @_label.init 3, 0 25 | @_label.x = cc.screenSize.width / 2 + @_skin.label.x 26 | @_label.y = cc.screenSize.height - @_skin.label.y 27 | @addChild @_label 28 | @_label.reflect ~~(@_rate.toFixed()) 29 | 30 | console.log @_config.great 31 | 32 | 33 | get : -> ~~(@_rate.toFixed()) 34 | 35 | start : -> @scheduleUpdate() 36 | 37 | update : -> 38 | @_count += 1 39 | return unless @_count % 4 is 0 40 | w = @_skin.meter.width 41 | h = @_skin.meter.height 42 | base = if @_rate - 6 then 0 else @_rate 43 | for i in [base...@_config.num] 44 | if i > @_config.clearVal 45 | if @_rate - 6 <= i * 2 < @_rate - 2 46 | @_sprites[i].setSpriteFrame @_frames[~~(Math.random() * 2) * 2] 47 | else if @_rate - 2 >= i * 2 then @_sprites[i].setSpriteFrame @_frames[0] 48 | else @_sprites[i].setSpriteFrame @_frames[2] 49 | else 50 | if @_rate - 6 <= i * 2 < @_rate - 2 then @_sprites[i].setSpriteFrame @_frames[~~(Math.random() * 2) * 2 + 1] 51 | else if @_rate - 2 >= i * 2 then @_sprites[i].setSpriteFrame @_frames[1] 52 | else @_sprites[i].setSpriteFrame @_frames[3] 53 | return 54 | 55 | 56 | reflect : (judge) -> 57 | switch judge 58 | when "pgreat", "great" 59 | if @_rate + @_config.great >= 100 then @_rate = 100 else @_rate += @_config.great 60 | when "good" 61 | if @_rate + @_config.good >= 100 then @_rate = 100 else @_rate += @_config.good 62 | when "bad" 63 | if @_rate + @_config.bad < 2 then @_rate = 2 else @_rate += @_config.bad 64 | when "poor" 65 | if @_rate + @_config.poor < 2 then @_rate = 2 else @_rate += @_config.poor 66 | when "epoor" 67 | @_rate = if @_rate + @_config.epoor < 2 then @_rate = 2 else @_rate += @_config.epoor 68 | else 69 | 70 | @_label.reflect ~~(@_rate.toFixed()) 71 | 72 | module.exports = RateLayer 73 | -------------------------------------------------------------------------------- /res/loading.js: -------------------------------------------------------------------------------- 1 | (function(){var createStyle=function(){return".cocosLoading{position:absolute;margin:-30px -60px;padding:0;top:50%;left:50%}"+".cocosLoading ul{margin:0;padding:0;}"+".cocosLoading span{color:#FFF;text-align:center;display:block;}"+".cocosLoading li{list-style:none;float:left;border-radius:15px;width:15px;height:15px;background:#FFF;margin:5px 0 0 10px}"+".cocosLoading li .ball,.cocosLoading li .unball{background-color:#2187e7;background-image:-moz-linear-gradient(90deg,#2187e7 25%,#a0eaff);background-image:-webkit-linear-gradient(90deg,#2187e7 25%,#a0eaff);width:15px;height:15px;border-radius:50px}"+".cocosLoading li .ball{transform:scale(0);-moz-transform:scale(0);-webkit-transform:scale(0);animation:showDot 1s linear forwards;-moz-animation:showDot 1s linear forwards;-webkit-animation:showDot 1s linear forwards}"+".cocosLoading li .unball{transform:scale(1);-moz-transform:scale(1);-webkit-transform:scale(1);animation:hideDot 1s linear forwards;-moz-animation:hideDot 1s linear forwards;-webkit-animation:hideDot 1s linear forwards}"+"@keyframes showDot{0%{transform:scale(0,0)}100%{transform:scale(1,1)}}"+"@-moz-keyframes showDot{0%{-moz-transform:scale(0,0)}100%{-moz-transform:scale(1,1)}}"+"@-webkit-keyframes showDot{0%{-webkit-transform:scale(0,0)}100%{-webkit-transform:scale(1,1)}}"+"@keyframes hideDot{0%{transform:scale(1,1)}100%{transform:scale(0,0)}}"+"@-moz-keyframes hideDot{0%{-moz-transform:scale(1,1)}100%{-moz-transform:scale(0,0)}}"+"@-webkit-keyframes hideDot{0%{-webkit-transform:scale(1,1)}100%{-webkit-transform:scale(0,0)}}"};var createDom=function(id,num){id=id||"cocosLoading";num=num||5;var i,item;var div=document.createElement("div");div.className="cocosLoading";div.id=id;var bar=document.createElement("ul");var list=[];for(i=0;i=list.length){direction=!direction;index=0;time=1000}else{time=300}animation()},time)};animation()};(function(){var bgColor=document.body.style.background;document.body.style.background="#000";var style=document.createElement("style");style.type="text/css";style.innerHTML=createStyle();document.head.appendChild(style);var list=createDom();startAnimation(list,function(){var div=document.getElementById("cocosLoading");if(!div){document.body.style.background=bgColor}return !!div})})()})(); -------------------------------------------------------------------------------- /test/src/test-numeralLayer.coffee: -------------------------------------------------------------------------------- 1 | NumeralLayerTest = cc.Class.extend 2 | 3 | start : -> 4 | describe 'numeral layer class test', -> 5 | @timeout 10000 6 | NumeralLayer = require '../../src/numeralLayer' 7 | capture = require './test-utils' 8 | .capture 9 | expect = chai.expect 10 | capNum = 0 11 | 12 | it 'numeral layer create and reflect test', (done)-> 13 | TestScene = cc.Scene.extend 14 | onEnter : -> 15 | @_super() 16 | 17 | numeralLayer = new NumeralLayer 18 | src : '../res/numeral-font.png' 19 | width : 29.5 20 | height : 36.5 21 | scale : 0.35 22 | margin : 3 23 | numeralLayer.init 10, 0 24 | size = cc.director.getWinSize() 25 | numeralLayer.x = size.width / 2 26 | numeralLayer.y = size.height / 2 27 | @addChild numeralLayer 28 | 29 | expect @children.length 30 | .to.be.equal 1 31 | 32 | setTimeout => 33 | numeralLayer.reflect 123456789 34 | capture "test/capture/numeralLayer/num" + capNum++ 35 | , 1000 36 | 37 | setTimeout => 38 | numeralLayer.reflect 9876543210 39 | capture "test/capture/numeralLayer/num" + capNum++ 40 | , 2000 41 | 42 | setTimeout => 43 | capture "test/capture/numeralLayer/num" + capNum++ 44 | done() 45 | , 3000 46 | 47 | cc.director.runScene new TestScene() 48 | 49 | it 'numeral layer create and reflect test x2', (done)-> 50 | TestScene = cc.Scene.extend 51 | onEnter : -> 52 | @_super() 53 | 54 | numeralLayer = new NumeralLayer 55 | src : '../res/numeral-font.png' 56 | width : 29.5 57 | height : 36.5 58 | scale : 0.7 59 | margin : 3 60 | numeralLayer.init 10, 0 61 | size = cc.director.getWinSize() 62 | numeralLayer.x = size.width / 2 63 | numeralLayer.y = size.height / 2 64 | @addChild numeralLayer 65 | 66 | expect @children.length 67 | .to.be.equal 1 68 | 69 | setTimeout => 70 | numeralLayer.reflect 123456789 71 | capture "test/capture/notes/notes" + capNum++ 72 | , 1000 73 | 74 | setTimeout => 75 | numeralLayer.reflect 9876543210 76 | capture "test/capture/notes/notes" + capNum++ 77 | , 2000 78 | 79 | setTimeout => 80 | capture "test/capture/notes/notes" + capNum++ 81 | done() 82 | , 3000 83 | 84 | cc.director.runScene new TestScene() 85 | 86 | before -> 87 | 88 | after -> 89 | 90 | module.exports = NumeralLayerTest 91 | 92 | 93 | -------------------------------------------------------------------------------- /test/src/test-parser.coffee: -------------------------------------------------------------------------------- 1 | ParserTest = cc.Class.extend 2 | 3 | start : -> 4 | expect = chai.expect 5 | Parser = require '../../src/parser' 6 | 7 | describe 'Parser class test', -> 8 | parser = null 9 | 10 | it 'parse WAV', -> 11 | text = """ 12 | #WAV00 test1.ogg 13 | #WAV01 test2.ogg 14 | #WAV02 test3.ogg 15 | #WAV04 test4.ogg 16 | """ 17 | parser = new Parser() 18 | result = parser.parse(text) 19 | expect(result.wav[0]).to.be.equal('test1.ogg') 20 | expect(result.wav[1]).to.be.equal('test2.ogg') 21 | expect(result.wav[2]).to.be.equal('test3.ogg') 22 | expect(result.wav[4]).to.be.equal('test4.ogg') 23 | 24 | it 'parse BMP', -> 25 | text = """ 26 | #BMP00 test1.bmp 27 | #BMP01 test2.bmp 28 | #BMP02 test3.bmp 29 | #BMP04 test4.bmp 30 | """ 31 | parser = new Parser() 32 | result = parser.parse(text) 33 | expect(result.bmp[0]).to.be.equal('test1.bmp') 34 | expect(result.bmp[1]).to.be.equal('test2.bmp') 35 | expect(result.bmp[2]).to.be.equal('test3.bmp') 36 | expect(result.bmp[4]).to.be.equal('test4.bmp') 37 | 38 | it 'parse property', -> 39 | text = """ 40 | #PLAYER 1 41 | #GENRE hoge 42 | #BPM 55 43 | """ 44 | parser = new Parser() 45 | result = parser.parse(text) 46 | expect(result.player).to.be.equal('1') 47 | expect(result.genre).to.be.equal('hoge') 48 | expect(result.bpm).to.be.equal('55') 49 | 50 | it 'parse channel message', -> 51 | text = """ 52 | #00011:11111111 53 | #00011:0022332255224400 54 | #00011:0066 55 | """ 56 | parser = new Parser() 57 | result = parser.parse(text) 58 | 59 | expect(result.data[0].note.key[0].id[0]).to.be.equal(parseInt('11', 36)) 60 | expect(result.data[0].note.key[0].id[1]).to.be.equal(parseInt('22', 36)) 61 | expect(result.data[0].note.key[0].id[2]).to.be.equal(parseInt('33', 36)) 62 | expect(result.data[0].note.key[0].id[3]).to.be.equal(parseInt('22', 36)) 63 | expect(result.data[0].note.key[0].id[4]).to.be.equal(parseInt('66', 36)) 64 | expect(result.data[0].note.key[0].id[5]).to.be.equal(parseInt('22', 36)) 65 | expect(result.data[0].note.key[0].id[6]).to.be.equal(parseInt('44', 36)) 66 | 67 | expect(result.totalNote).to.be.equal(7) 68 | 69 | it 'parse WAV', -> 70 | text = """ 71 | #PLAYER 1 72 | #GENRE hoge 73 | #BPM 60 74 | 75 | #00001:6F 76 | #00001:0007000700070007 77 | #00001:3B 78 | """ 79 | parser = new Parser() 80 | result = parser.parse(text) 81 | 82 | #FIXME: 上2つは同時なので順序が入れ替わる可能性がある 83 | expect(result.data[0].wav.id[0]).to.be.equal(parseInt('6F', 36)) 84 | expect(result.data[0].wav.id[1]).to.be.equal(parseInt('3B', 36)) 85 | expect(result.data[0].wav.id[2]).to.be.equal(parseInt('07', 36)) 86 | expect(result.data[0].wav.id[3]).to.be.equal(parseInt('07', 36)) 87 | expect(result.data[0].wav.id[4]).to.be.equal(parseInt('07', 36)) 88 | expect(result.data[0].wav.id[5]).to.be.equal(parseInt('07', 36)) 89 | 90 | it 'parse BMP', -> 91 | text = """ 92 | #00004:11111111 93 | #00004:0022332255224400 94 | #00004:0066 95 | """ 96 | parser = new Parser() 97 | result = parser.parse(text) 98 | 99 | expect(result.data[0].bmp.id[0]).to.be.equal(parseInt('11', 36)) 100 | expect(result.data[0].bmp.id[1]).to.be.equal(parseInt('22', 36)) 101 | expect(result.data[0].bmp.id[2]).to.be.equal(parseInt('33', 36)) 102 | expect(result.data[0].bmp.id[3]).to.be.equal(parseInt('22', 36)) 103 | expect(result.data[0].bmp.id[4]).to.be.equal(parseInt('66', 36)) 104 | expect(result.data[0].bmp.id[5]).to.be.equal(parseInt('22', 36)) 105 | expect(result.data[0].bmp.id[6]).to.be.equal(parseInt('44', 36)) 106 | 107 | it 'calc total note', -> 108 | text = """ 109 | #PLAYER 1 110 | #GENRE hoge 111 | #BPM 60 112 | 113 | #00011:1111 114 | #00012:0022332255224400 115 | #00013:0066 116 | 117 | #00111:1212 118 | #00211:13 119 | """ 120 | parser = new Parser() 121 | result = parser.parse(text) 122 | 123 | expect(result.totalNote).to.be.equal(12) 124 | 125 | 126 | module.exports = ParserTest 127 | 128 | 129 | -------------------------------------------------------------------------------- /src/statsLayer.coffee: -------------------------------------------------------------------------------- 1 | NumeralLayer = require './numeralLayer' 2 | JudgementLayer = require './judgementLayer' 3 | 4 | StatsLayer = cc.Layer.extend 5 | ctor: (@_skin) -> 6 | @_super() 7 | @_scoreLabel = new NumeralLayer @_skin.score 8 | @_pgreatLabel = new NumeralLayer @_skin.pgreatNum 9 | @_greatLabel = new NumeralLayer @_skin.greatNum 10 | @_goodLabel = new NumeralLayer @_skin.goodNum 11 | @_badLabel = new NumeralLayer @_skin.badNum 12 | @_poorLabel = new NumeralLayer @_skin.poorNum 13 | @_comboLabel = new NumeralLayer @_skin.comboNum 14 | @_judgement = new JudgementLayer @_skin.judge 15 | @addChild @_scoreLabel 16 | @addChild @_pgreatLabel 17 | @addChild @_greatLabel 18 | @addChild @_goodLabel 19 | @addChild @_badLabel 20 | @addChild @_poorLabel 21 | @addChild @_comboLabel 22 | @addChild @_judgement 23 | 24 | init : (@_noteNum, maxScore) -> 25 | @_score = 0 26 | @_dispScore = 0 27 | @_maxCombo = 0 28 | @_combo = 0 29 | @_pgreatNum = 0 30 | @_greatNum = 0 31 | @_goodNum = 0 32 | @_badNum = 0 33 | @_poorNum = 0 34 | @_comboPoint = 0 35 | @_pgreatIncVal = maxScore.pgreat / @_noteNum 36 | @_greatIncVal = maxScore.great / @_noteNum 37 | @_goodIncVal = maxScore.good / @_noteNum 38 | @_comboBonusFactor = maxScore.combo / (10 * (@_noteNum - 1) - 55) 39 | @_incValonUpdate = maxScore.pgreat / @_noteNum / 2 40 | @_judgement.init() 41 | 42 | @_scoreLabel.init @_getDigits(maxScore.pgreat + maxScore.combo), 0 43 | @_scoreLabel.x = cc.screenSize.width / 2 + @_skin.score.x 44 | @_scoreLabel.y = cc.screenSize.height - @_skin.score.y 45 | #@_pgreatLabel = new cc.LabelTTF ' 0', "Arial", 6, cc.size(40, 0), cc.TEXT_ALIGNMENT_LEFT 46 | @_pgreatLabel.init 4, 0 47 | @_pgreatLabel.x = cc.screenSize.width / 2 + @_skin.pgreatNum.x 48 | @_pgreatLabel.y = cc.screenSize.height - @_skin.pgreatNum.y 49 | #@_greatLabel = new cc.LabelTTF ' 0', "Arial", 6, cc.size(40, 0), cc.TEXT_ALIGNMENT_LEFT 50 | @_greatLabel.init 4, 0 51 | @_greatLabel.x = cc.screenSize.width / 2 + @_skin.greatNum.x 52 | @_greatLabel.y = cc.screenSize.height - @_skin.greatNum.y 53 | #@_goodLabel = new cc.LabelTTF ' 0', "Arial", 6, cc.size(40, 0), cc.TEXT_ALIGNMENT_LEFT 54 | @_goodLabel.init 4, 0 55 | @_goodLabel.x = cc.screenSize.width / 2 + @_skin.goodNum.x 56 | @_goodLabel.y = cc.screenSize.height - @_skin.goodNum.y 57 | #@_badLabel = new cc.LabelTTF ' 0', "Arial", 6, cc.size(40, 0), cc.TEXT_ALIGNMENT_LEFT 58 | @_badLabel.init 4, 0 59 | @_badLabel.x = cc.screenSize.width / 2 + @_skin.badNum.x 60 | @_badLabel.y = cc.screenSize.height - @_skin.badNum.y 61 | #@_poorLabel = new cc.LabelTTF ' 0', "Arial", 6, cc.size(40, 0), cc.TEXT_ALIGNMENT_LEFT 62 | @_poorLabel.init 4, 0 63 | @_poorLabel.x = cc.screenSize.width / 2 + @_skin.poorNum.x 64 | @_poorLabel.y = cc.screenSize.height - @_skin.poorNum.y 65 | @_comboLabel.init 4, 0 66 | @_comboLabel.x = cc.screenSize.width / 2 + @_skin.comboNum.x 67 | @_comboLabel.y = cc.screenSize.height - @_skin.comboNum.y 68 | 69 | get : -> 70 | score : ~~(@_score.toFixed()) 71 | combo : @_maxCombo 72 | pgreat : @_pgreatNum 73 | great : @_greatNum 74 | good : @_goodNum 75 | bad : @_badNum 76 | poor : @_poorNum 77 | 78 | start : -> @scheduleUpdate() 79 | 80 | reflect : (judge) -> 81 | switch judge 82 | when "pgreat" 83 | @_score += @_pgreatIncVal 84 | @_combo++ 85 | @_pgreatNum++ 86 | #@_pgreatLabel.setString " #{@_pgreatNum}" 87 | @_pgreatLabel.reflect @_pgreatNum 88 | @_judgement.show 0, 0, 1 89 | 90 | when "great" 91 | @_score += @_greatIncVal 92 | @_combo++ 93 | @_greatNum++ 94 | #@_greatLabel.setString " #{@_greatNum}" 95 | @_greatLabel.reflect @_greatNum 96 | @_judgement.show 1, 0, 0.5 97 | 98 | when "good" 99 | @_score += @_goodIncVal 100 | @_combo++ 101 | @_goodNum++ 102 | #@_goodLabel.setString " #{@_goodNum}" 103 | @_goodLabel.reflect @_goodNum 104 | @_judgement.show 2, 0, 0.5 105 | 106 | when "bad" 107 | @_score += @_comboBonusFactor * @_comboPoint 108 | @_combo = 0 109 | @_comboPoint = 0 110 | @_badNum++ 111 | #@_badLabel.setString " #{@_badNum}" 112 | @_badLabel.reflect @_badNum 113 | @_judgement.show 3, 0, 0.5 114 | 115 | else # poor or epoor 116 | @_score += @_comboBonusFactor * @_comboPoint 117 | @_combo = 0 118 | @_comboPoint = 0 119 | @_poorNum++ 120 | #@_poorLabel.setString " #{@_poorNum}" 121 | @_poorLabel.reflect @_poorNum 122 | @_judgement.show 4, 0, 0.5 123 | 124 | # full combo 125 | if @_combo is @_noteNum 126 | @_score += @_comboBonusFactor * @_comboPoint 127 | @_comboPoint = 0 128 | @_incValonUpdate = @_incValonUpdate * 5 129 | 130 | if 0 < @_combo <= 10 131 | @_comboPoint += @_combo - 1 132 | else if @_combo > 10 133 | @_comboPoint += 10 134 | 135 | if @_combo > @_maxCombo 136 | @_maxCombo = @_combo 137 | @_comboLabel.reflect @_maxCombo 138 | 139 | #@_dispScore = ~~(@_score.toFixed()) 140 | #@_scoreLabel.reflect @_dispScore 141 | 142 | update : -> 143 | if @_dispScore < ~~(@_score.toFixed()) 144 | @_dispScore += @_incValonUpdate 145 | @_dispScore = if @_dispScore > ~~(@_score.toFixed()) then ~~(@_score.toFixed()) else @_dispScore 146 | @_scoreLabel.reflect @_dispScore 147 | 148 | _getDigits : (num)-> Math.log(num) / Math.log(10) + 1 | 0 149 | 150 | module.exports = StatsLayer 151 | -------------------------------------------------------------------------------- /src/menuScene.coffee: -------------------------------------------------------------------------------- 1 | AppScene = require './app' 2 | Parser = require './parser' 3 | SearchLayer = require './searchLayer' 4 | LevelFont = require './numeralLayer' 5 | res = require './resource' 6 | .resObjs 7 | 8 | MenuBaseLayer = cc.Layer.extend 9 | ctor : -> 10 | @_super() 11 | if window? 12 | window.addEventListener 'resize', -> 13 | screenSize = window.parent.screen 14 | cc.view.setDesignResolutionSize screenSize.width, screenSize.height, cc.ResolutionPolicy.SHOW_ALL 15 | 16 | start : -> 17 | if cc.sys.isMobile 18 | message = """ 19 | 申し訳ありません 20 | 現在PCにのみ対応しております 21 | 22 | """ 23 | pconly = new cc.LabelTTF message , "Arial" , 16 24 | pconly.x = cc.screenSize.width / 2 25 | pconly.y = cc.screenSize.height / 2 26 | @addChild pconly, 100 27 | return 28 | @_addBackground() 29 | cc.loader.loadJson cc.pathToBmsDir + 'bms.json', (error, data) => 30 | menu = new MenuController data 31 | menu.init data, cc.director.getWinSize().width / 2 + 200, 70 32 | @addChild menu 33 | 34 | onExit : -> 35 | @_super() 36 | @removeAllChildren on 37 | 38 | _addBackground : -> 39 | bg = new cc.Sprite res.menuBgImage 40 | bg.x = cc.director.getWinSize().width / 2 41 | bg.y = cc.director.getWinSize().height / 2 42 | console.log "scree height #{window.parent.screen.height} bg.height = #{bg.height}" 43 | #if bg.height < window.parent.screen.height 44 | bg.scale = window.parent.screen.height / bg.height 45 | @addChild bg, 0 46 | 47 | MenuController = cc.Layer.extend 48 | ctor : (data) -> 49 | @_super() 50 | @_offsetY = 0 51 | @_menuList = data 52 | 53 | init : (list, x, @_linespace) -> 54 | director = cc.director 55 | size = director.getWinSize() 56 | @_itemMenu = new cc.Menu() 57 | for v, i in list when v.TITLE? 58 | 59 | item = new cc.Sprite res.itemBgImage 60 | title = if v.TITLE.length > 26 then v.TITLE.substr(0, 26)+'...' else v.TITLE 61 | label = new cc.LabelTTF title, "Arial", 22, cc.size(item.width, 0), cc.TEXT_ALIGNMENT_LEFT 62 | label.x = 320 63 | label.y = 38 64 | item.addChild label 65 | 66 | # TODO : to cson 67 | level = new LevelFont 68 | src : res.levelFontImage 69 | width : 26.2 70 | height : 16 71 | scale : 1 72 | margin : 0 73 | level.x = 34 74 | level.y = 39 75 | 76 | item.addChild level 77 | digits = if ~~(v.level / 10) > 0 then 2 else 1 78 | level.init digits, v.PLAYLEVEL 79 | menuItem = new cc.MenuItemSprite item, null, null, @_onMenuCallback, this 80 | @_itemMenu.addChild menuItem, i + 10000 81 | menuItem.x = x 82 | menuItem.y = size.height - (i + 1) * @_linespace 83 | menuItem.title = v.TITLE 84 | 85 | @_itemMenu.width = size.width 86 | @_itemMenu.height = (list.length + 1) * @_linespace 87 | @_itemMenu.x = 0 88 | @_itemMenu.y = (@_itemMenu.height - size.height) / 2 89 | @addChild @_itemMenu 90 | 91 | search = new SearchLayer() 92 | search.init @_itemMenu.children 93 | search.start() 94 | search.addListener 'change', @_onChanged.bind this 95 | @addChild search 96 | 97 | # 'browser' can use touches or mouse. 98 | # The benefit of using 'touches' in a browser, is that it works both with mouse events or touches events 99 | if 'touches' of cc.sys.capabilities 100 | cc.eventManager.addListener 101 | event : cc.EventListener.TOUCH_ALL_AT_ONCE 102 | onTouchesMoved : (touches, event) -> 103 | target = event.getCurrentTarget() 104 | delta = touches[0].getDelta() 105 | target._moveMenu delta 106 | return true 107 | , this 108 | else if 'mouse' of cc.sys.capabilities 109 | cc.eventManager.addListener 110 | event : cc.EventListener.MOUSE 111 | onMouseMove : (event) -> 112 | if event.getButton() is cc.EventMouse.BUTTON_LEFT 113 | event.getCurrentTarget()._moveMenu event.getDelta() 114 | onMouseScroll : (event) -> 115 | deltaY = if cc.sys.isNative then event.getScrollY() * 6 else -event.getScrollY() 116 | event.getCurrentTarget()._moveMenu 117 | y : deltaY 118 | , this 119 | 120 | onExit : -> 121 | @_super() 122 | @removeAllChildren on 123 | 124 | _moveMenu : (delta) -> 125 | newY = @_itemMenu.y + delta.y 126 | size = cc.director.getWinSize() 127 | if @_itemMenu.height < size.height 128 | newY = (@_itemMenu.height - size.height) / 2 129 | else 130 | if newY < 0 131 | @_itemMenu.y = 0 132 | move = cc.moveBy 0.05, 0, 20 133 | else if newY > @_itemMenu.height - size.height 134 | @_itemMenu.y = @_itemMenu.height - size.height 135 | else 136 | @_itemMenu.y = newY 137 | 138 | _getPrefix : (url)-> 139 | m = /^.+\//.exec url 140 | m[0] if m 141 | 142 | _onMenuCallback : (sender) -> 143 | @_offsetY = @_itemMenu.y 144 | id = sender.getLocalZOrder() - 10000 145 | url = @_menuList[id].url 146 | prefix = @_getPrefix url 147 | 148 | cc.loader.loadTxt cc.pathToBmsDir + url, (err, text) => 149 | parser = new Parser() 150 | bms = parser.parse text 151 | resources = [] 152 | for k, v of bms.wav then resources.push (prefix + encodeURIComponent(v)) 153 | #for k, v of bms.bmp then resources.push prefix + v 154 | 155 | require('./loaderScene').preload resources, -> 156 | cc.director.runScene new AppScene bms, prefix 157 | , this 158 | 159 | _onChanged : (name, visibleItems) -> 160 | size = cc.director.getWinSize() 161 | 162 | for item in @_itemMenu.children 163 | item.runAction cc.spawn cc.fadeOut(0.2), cc.scaleTo(0.2, 0) 164 | 165 | for item, i in visibleItems 166 | item.stopAllActions() 167 | destY = size.height - (i + 1) * @_linespace 168 | item.runAction( 169 | cc.spawn( 170 | cc.fadeIn 0.2 171 | cc.scaleTo 0.2, 1 172 | cc.moveTo 0.2, cc.p(size.width / 2 + 200, destY) 173 | ) 174 | ) 175 | 176 | @_itemMenu.height = (visibleItems.length + 1) * @_linespace 177 | @_itemMenu.y = (@_itemMenu.height - size.height) / 2 178 | 179 | MenuScene = cc.Scene.extend 180 | ctor : -> 181 | @_super() 182 | layer = new MenuBaseLayer() 183 | @addChild layer 184 | layer.start() 185 | 186 | onExit : -> 187 | @_super() 188 | @removeAllChildren on 189 | 190 | module.exports = MenuScene 191 | -------------------------------------------------------------------------------- /src/notesLayer.coffee: -------------------------------------------------------------------------------- 1 | EventObserver = require './eventObserver' 2 | Judge = require './judge' 3 | Note = require './note' 4 | GreatEffectsLayer = require './greatEffectsLayer' 5 | KeyEffectsLayer = require './keyEffectsLayer' 6 | MeasureNode = require './measureNode' 7 | 8 | NotesLayer = cc.Layer.extend 9 | ctor : (@_skin, @_timer, @_config)-> 10 | @_super() 11 | @_notifier = new EventObserver() 12 | @_judge = new Judge @_config.judge 13 | @_greatEffectsLayer = new GreatEffectsLayer @_skin.greatEffect 14 | @_keyEffectsLayer = new KeyEffectsLayer @_skin.keyEffect 15 | @_notes = [] 16 | @_nodes = [] 17 | @_genTime = [] 18 | 19 | # 20 | # create notes 21 | # 22 | init : (bms)-> 23 | time = 0 24 | @_index = 0 25 | @_notes.length = 0 26 | @_nodes.length = 0 27 | @_genTime.length = 0 28 | 29 | for v, i in bms.data 30 | node = new MeasureNode @_skin.nodeImage.src, @_timer 31 | node.x = cc.screenSize.width / 2 + @_skin.nodeImage.x 32 | node.y = -node.height 33 | node.timing = v.timing 34 | node.appendFallParams bms.bpms, time, @_skin.fallDist 35 | @_genTime.push time 36 | time = @_getGenTime node, @_skin.fallDist 37 | @_nodes.push node 38 | node.retain() 39 | 40 | @_genTime.sort (a, b) -> a - b 41 | 42 | bg = new cc.Sprite @_skin.bgImage.src 43 | bg.setAnchorPoint cc.p(0.5, 0) 44 | bg.x = cc.screenSize.width / 2 + @_skin.bgImage.x 45 | bg.y = cc.screenSize.height - @_skin.bgImage.y 46 | bg.setOpacity 180 47 | @addChild bg, 0 48 | @_greatEffectsLayer.init bms.totalNote 49 | @addChild @_greatEffectsLayer, 200 50 | 51 | @_generate bms, measure, time for time, measure in @_genTime 52 | xList = for i in [0...@_skin.keyNum] then @_calcNoteXCoordinate i 53 | @_keyEffectsLayer.init xList 54 | @addChild @_keyEffectsLayer, 0 55 | 56 | addListener: (name, listner)-> 57 | @_notifier.on name, listner 58 | 59 | # 60 | # generate and pool note 61 | # 62 | _generate : (bms, measure, time)-> 63 | turntable = @_skin.noteTurntableImage 64 | white = @_skin.noteWhiteImage 65 | black = @_skin.noteBlackImage 66 | fallDist = @_skin.fallDist 67 | bpms = bms.bpms 68 | @_notes[measure] ?= [] 69 | 70 | @_batchNodes = {} 71 | @_batchNodes["white"] = new cc.SpriteBatchNode white.src 72 | @_batchNodes["black"] = new cc.SpriteBatchNode black.src 73 | @_batchNodes["turntable"] = new cc.SpriteBatchNode turntable.src 74 | @addChild v, 100 for k, v of @_batchNodes 75 | 76 | return unless bms.data[measure]? 77 | for key, i in bms.data[measure].note.key 78 | for timing, j in key.timing 79 | switch i 80 | when 0, 2, 4, 6 then type = "white" 81 | when 1, 3, 5 then type = "black" 82 | when 7 then type = "turntable" 83 | else throw new Error "error unlnown note" 84 | 85 | note = new Note @_batchNodes[type].getTexture(), @_timer, @_config.removeTime 86 | note.type = type 87 | note.x = @_calcNoteXCoordinate i 88 | note.y = -note.height 89 | note.setAnchorPoint cc.p(0.5,0) 90 | note.timing = timing 91 | note.wav = key.id[j] 92 | note.key = i 93 | note.clear = false 94 | note.addListener 'remove', @_onRemove.bind this 95 | note.appendFallParams bpms, time, fallDist 96 | note.retain() 97 | @_notes[measure].push note 98 | return 99 | 100 | # 101 | # calculate 102 | # 103 | _calcNoteXCoordinate : (id)-> 104 | turntable = @_skin.noteTurntableImage 105 | white = @_skin.noteWhiteImage 106 | black = @_skin.noteBlackImage 107 | offset = cc.screenSize.width / 2 + @_skin.offsetX 108 | margin = (id + 1) * @_skin.marginX 109 | switch id 110 | when 0, 2, 4, 6 111 | ~~(id / 2) * (black.width + white.width) + (turntable.width + white.width) / 2 + offset + margin 112 | when 1, 3, 5 113 | ~~(id / 2) * (black.width + white.width) + (turntable.width + black.width) / 2 + offset + margin + white.width 114 | when 7 115 | offset 116 | else throw new Error "error unlnown note" 117 | 118 | # 119 | # start note update 120 | # 121 | # @param isAuto - 122 | # 123 | start : (autoplay = false)-> 124 | @scheduleUpdate() 125 | @_isAuto = autoplay 126 | 127 | onTouch : (key, time)-> 128 | return if @_isAuto 129 | @_keyEffectsLayer.show key, 0.5 130 | for batchNode in @children 131 | for note in batchNode.children when note.key is key 132 | diffTime = note.timing - time 133 | unless note.clear 134 | if -@_config.reactionTime < diffTime < @_config.reactionTime 135 | note.clear = true 136 | @_notifier.trigger 'hit', note.wav 137 | judgement = @_judge.exec diffTime 138 | @_notifier.trigger 'judge', judgement 139 | if judgement is 'pgreat' or 'great' 140 | size = cc.director.getWinSize() 141 | y = size.height - @_skin.fallDist 142 | @_greatEffectsLayer.run note.x, y 143 | return 144 | else 145 | @_notifier.trigger 'judge', 'epoor' 146 | return 147 | return 148 | 149 | # 150 | # add note to game scene 151 | # @attention - _add called by window 152 | # 153 | update : -> 154 | if @_isAuto 155 | for batchNode in @children 156 | for note in batchNode.children when note.clear is false 157 | if @_timer.get() >= note.timing 158 | @_keyEffectsLayer.show note.key, 0.5 159 | note.clear = true 160 | y = cc.director.getWinSize().height - @_skin.fallDist 161 | @_greatEffectsLayer.run note.x, y 162 | @_notifier.trigger 'hit', note.wav 163 | @_notifier.trigger 'judge', 'pgreat' 164 | 165 | unless @_genTime[@_index]? 166 | #@_notifier.trigger 'end' 167 | return 168 | 169 | return unless @_genTime[@_index] <= @_timer.get() 170 | @addChild @_nodes[@_index], 99 171 | @_nodes[@_index].start() 172 | for note in @_notes[@_index] 173 | @_batchNodes[note.type].addChild note, 99 174 | note.start() 175 | @_index++ 176 | 177 | _onRemove : (event, note) -> 178 | @_notifier.trigger 'judge', 'poor' unless note.clear 179 | 180 | # 181 | # get time when node y coordinate will be game.height 182 | # to generate next node 183 | # 184 | _getGenTime : (obj, fallDist)-> 185 | size = cc.director.getWinSize() 186 | for v, i in obj.dstY when v < size.height 187 | return ~~(obj.bpm.timing[i] - (v / obj.calcSpeed(obj.bpm.val[i], fallDist))) 188 | return 0 189 | 190 | module.exports = NotesLayer 191 | -------------------------------------------------------------------------------- /test/src/test-statsLayer.coffee: -------------------------------------------------------------------------------- 1 | skin = 2 | z : 10 3 | judge : 4 | src : '../res/judge-image.png' 5 | width : 51 6 | height : 15 7 | x : 216 8 | y : 340 9 | score : 10 | src : '../res/numeral-font.png' 11 | width : 29.5 12 | height : 36.5 13 | scale : 0.35 14 | margin : 3 15 | x : 202 16 | y : 175 17 | pgreatNum : 18 | src : '../res/numeral-font.png' 19 | width : 29.5 20 | height : 36.5 21 | scale : 0.35 22 | margin : 3 23 | x : 296 24 | y : 174 25 | greatNum : 26 | src : '../res/numeral-font.png' 27 | width : 29.5 28 | height : 36.5 29 | scale : 0.35 30 | margin : 2 31 | x : 296 32 | y : 165 33 | goodNum : 34 | src : '../res/numeral-font.png' 35 | width : 29.5 36 | height : 36.5 37 | scale : 0.35 38 | margin : 3 39 | x : 296 40 | y : 156 41 | badNum : 42 | src : '../res/numeral-font.png' 43 | width : 29.5 44 | height : 36.5 45 | scale : 0.35 46 | margin : 3 47 | x : 297 48 | y : 147 49 | poorNum : 50 | src : '../res/numeral-font.png' 51 | width : 29.5 52 | height : 36.5 53 | scale : 0.35 54 | margin : 3 55 | x : 297 56 | y : 138 57 | comboNum : 58 | src : '../res/numeral-font.png' 59 | width : 29.5 60 | height : 36.5 61 | scale : 0.35 62 | margin : 3 63 | x : 211 64 | y : 156 65 | 66 | 67 | StatsLayerTest = cc.Class.extend 68 | start : -> 69 | describe 'stats layer class test', -> 70 | TEST_NOTE_NUM = 30 71 | @timeout 10000 72 | StatsLayer = require '../../src/statsLayer' 73 | capture = require './test-utils' 74 | .capture 75 | expect = chai.expect 76 | capNum = 0 77 | 78 | config = 79 | pgreat : 150000 80 | great : 100000 81 | good : 20000 82 | combo : 50000 83 | 84 | it 'initialize stats and capture stats', (done)-> 85 | TestScene = cc.Scene.extend 86 | onEnter : -> 87 | @_super() 88 | size = cc.director.getWinSize() 89 | stats = new StatsLayer skin 90 | stats.init TEST_NOTE_NUM, config 91 | @addChild stats 92 | expect(stats.get().score).to.be.equal 0 93 | expect(stats.get().combo).to.be.equal 0 94 | expect(stats.get().pgreat).to.be.equal 0 95 | expect(stats.get().great).to.be.equal 0 96 | expect(stats.get().good).to.be.equal 0 97 | expect(stats.get().bad).to.be.equal 0 98 | expect(stats.get().poor).to.be.equal 0 99 | setTimeout => 100 | capture "test/capture/StatsLayer/stats" + capNum++ 101 | done() 102 | , 1000 103 | cc.director.runScene new TestScene() 104 | 105 | it 'score should be 150000 + 50000 when all pgreat', (done)-> 106 | TestScene = cc.Scene.extend 107 | onEnter : -> 108 | @_super() 109 | size = cc.director.getWinSize() 110 | stats = new StatsLayer skin 111 | stats.init TEST_NOTE_NUM, config 112 | @addChild stats 113 | stats.reflect 'pgreat' for i in [0...TEST_NOTE_NUM] 114 | expect(stats.get().score).to.be.equal config.pgreat + config.combo 115 | expect(stats.get().combo).to.be.equal TEST_NOTE_NUM 116 | expect(stats.get().pgreat).to.be.equal TEST_NOTE_NUM 117 | expect(stats.get().great).to.be.equal 0 118 | expect(stats.get().good).to.be.equal 0 119 | expect(stats.get().bad).to.be.equal 0 120 | expect(stats.get().poor).to.be.equal 0 121 | setTimeout => 122 | capture "test/capture/StatsLayer/stats" + capNum++ 123 | done() 124 | , 1000 125 | cc.director.runScene new TestScene() 126 | 127 | it 'score should be 100000 + 50000 when all great', (done)-> 128 | TestScene = cc.Scene.extend 129 | onEnter : -> 130 | @_super() 131 | size = cc.director.getWinSize() 132 | stats = new StatsLayer skin 133 | stats.init TEST_NOTE_NUM, config 134 | @addChild stats 135 | stats.reflect 'great' for i in [0...TEST_NOTE_NUM] 136 | expect(stats.get().score).to.be.equal config.great + config.combo 137 | expect(stats.get().combo).to.be.equal TEST_NOTE_NUM 138 | expect(stats.get().pgreat).to.be.equal 0 139 | expect(stats.get().great).to.be.equal TEST_NOTE_NUM 140 | expect(stats.get().good).to.be.equal 0 141 | expect(stats.get().bad).to.be.equal 0 142 | expect(stats.get().poor).to.be.equal 0 143 | setTimeout => 144 | capture "test/capture/StatsLayer/stats" + capNum++ 145 | done() 146 | , 1000 147 | cc.director.runScene new TestScene() 148 | 149 | it 'score should be 20000 + 50000 when all good', (done)-> 150 | TestScene = cc.Scene.extend 151 | onEnter : -> 152 | @_super() 153 | size = cc.director.getWinSize() 154 | stats = new StatsLayer skin 155 | stats.init TEST_NOTE_NUM, config 156 | @addChild stats 157 | stats.reflect 'good' for i in [0...TEST_NOTE_NUM] 158 | expect(stats.get().score).to.be.equal config.good + config.combo 159 | expect(stats.get().combo).to.be.equal TEST_NOTE_NUM 160 | expect(stats.get().pgreat).to.be.equal 0 161 | expect(stats.get().great).to.be.equal 0 162 | expect(stats.get().good).to.be.equal TEST_NOTE_NUM 163 | expect(stats.get().bad).to.be.equal 0 164 | expect(stats.get().poor).to.be.equal 0 165 | setTimeout => 166 | capture "test/capture/StatsLayer/stats" + capNum++ 167 | done() 168 | , 1000 169 | cc.director.runScene new TestScene() 170 | 171 | it 'score is 0 when all bad', (done)-> 172 | TestScene = cc.Scene.extend 173 | onEnter : -> 174 | @_super() 175 | size = cc.director.getWinSize() 176 | stats = new StatsLayer skin 177 | stats.init TEST_NOTE_NUM, config 178 | @addChild stats 179 | stats.reflect 'bad' for i in [0...TEST_NOTE_NUM] 180 | expect(stats.get().score).to.be.equal 0 181 | expect(stats.get().combo).to.be.equal 0 182 | expect(stats.get().pgreat).to.be.equal 0 183 | expect(stats.get().great).to.be.equal 0 184 | expect(stats.get().good).to.be.equal 0 185 | expect(stats.get().bad).to.be.equal TEST_NOTE_NUM 186 | expect(stats.get().poor).to.be.equal 0 187 | setTimeout => 188 | capture "test/capture/StatsLayer/stats" + capNum++ 189 | done() 190 | , 1000 191 | cc.director.runScene new TestScene() 192 | 193 | it 'score is 0 when all poor', (done)-> 194 | TestScene = cc.Scene.extend 195 | onEnter : -> 196 | @_super() 197 | size = cc.director.getWinSize() 198 | stats = new StatsLayer skin 199 | stats.init TEST_NOTE_NUM, config 200 | @addChild stats 201 | stats.reflect 'poor' for i in [0...TEST_NOTE_NUM] 202 | expect(stats.get().score).to.be.equal 0 203 | expect(stats.get().combo).to.be.equal 0 204 | expect(stats.get().pgreat).to.be.equal 0 205 | expect(stats.get().great).to.be.equal 0 206 | expect(stats.get().good).to.be.equal 0 207 | expect(stats.get().bad).to.be.equal 0 208 | expect(stats.get().poor).to.be.equal TEST_NOTE_NUM 209 | setTimeout => 210 | capture "test/capture/StatsLayer/stats" + capNum++ 211 | done() 212 | , 1000 213 | cc.director.runScene new TestScene() 214 | 215 | ### 216 | it 'all judge num is 6', (done)-> 217 | TestScene = cc.Scene.extend 218 | onEnter : -> 219 | @_super() 220 | size = cc.director.getWinSize() 221 | stats = new StatsLayer skin 222 | stats.init TEST_NOTE_NUM, config 223 | @addChild stats 224 | for i in [0...TEST_NOTE_NUM / 5] 225 | stats.reflect 'pgreat' 226 | stats.reflect 'great' 227 | stats.reflect 'good' 228 | stats.reflect 'bad' 229 | stats.reflect 'poor' 230 | expect(stats.get().score).to.be.equal ~~((MAX_SCORE / TEST_NOTE_NUM * 13.2).toFixed()) 231 | expect(stats.get().combo).to.be.equal 3 232 | expect(stats.get().pgreat).to.be.equal 6 233 | expect(stats.get().great).to.be.equal 6 234 | expect(stats.get().good).to.be.equal 6 235 | expect(stats.get().bad).to.be.equal 6 236 | expect(stats.get().poor).to.be.equal 6 237 | setTimeout => 238 | capture "test/capture/StatsLayer/stats" + capNum++ 239 | done() 240 | , 1000 241 | cc.director.runScene new TestScene() 242 | ### 243 | module.exports = StatsLayerTest 244 | 245 | 246 | -------------------------------------------------------------------------------- /src/parser.coffee: -------------------------------------------------------------------------------- 1 | #_ = require 'lodash' 2 | 3 | Parser = cc.Class.extend 4 | ctor: -> 5 | @bms = 6 | player : null 7 | genre : null 8 | title : null 9 | artist : null 10 | bpm : null 11 | playLevel : null 12 | rank : null 13 | total : null 14 | sategFile : null 15 | keyNum : null 16 | difficulty : null 17 | wav : {} 18 | bmp : {} 19 | data : [] 20 | exbpm : {} 21 | stop : {} 22 | totalNote : 0 23 | # OPTIMIZE: 24 | bgms : [] 25 | animations : [] 26 | bpms : [] 27 | stopTiming : [] 28 | endTime : 0 29 | 30 | @wavMessages = [] 31 | 32 | 33 | parse : (bms_text) -> 34 | @_parse row for row in bms_text.split '\n' 35 | @_modifyAfterParse() 36 | # OPTIMIZE:bpm,bmp,wavは小節に依存しないよう配列に詰めなおす 37 | @bms.bpms[0] = 38 | timing : 0 39 | val : @bms.bpm 40 | @_serialize @bms.bpms, "bpm", @bms.data 41 | @_serialize @bms.animations, "bmp", @bms.data 42 | @_serialize @bms.bgms, "wav", @bms.data 43 | @_serialize @bms.stopTiming, "stop", @bms.data 44 | 45 | console.log "stopTiming" 46 | console.dir @bms.stopTiming 47 | 48 | @bms.totalNote = @_calcTotalNote() 49 | 50 | # total 省略時 51 | unless @bms.total? 52 | @bms.total = 200 + @bms.totalNote 53 | @bms 54 | 55 | _parse : (row) -> 56 | return if row.substring(0, 1) isnt '#' 57 | 58 | wav = /^#WAV(\w{2}) +(.*)/.exec(row) 59 | if wav? 60 | @_parseWAV wav 61 | return 62 | 63 | bmp = /^#BMP(\w{2}) +(.*)/.exec(row) 64 | if bmp? 65 | @_parseBMP bmp 66 | return 67 | 68 | stop = /^#STOP(\w{2}) +(.*)/.exec(row) 69 | if stop? 70 | @_parseSTOP stop 71 | return 72 | 73 | exbpm = /^#BPM(\w{2}) +(.*)/.exec(row) 74 | if exbpm? 75 | @_parseBPM exbpm 76 | return 77 | 78 | channelMsg = /^#([0-9]{3})([0-9]{2}):([\w\.]+)/.exec(row) 79 | if channelMsg? 80 | @_parseChannelMsg channelMsg 81 | return 82 | 83 | property = /^#(\w+) +(.+)/.exec(row) 84 | if property? 85 | @_parseProperty property 86 | return 87 | 88 | _parseWAV : (wav) -> 89 | index = parseInt wav[1], 36 90 | @bms.wav[index] = wav[2] 91 | 92 | _parseBMP : (bmp) -> 93 | index = parseInt bmp[1], 36 94 | @bms.bmp[index] = bmp[2] 95 | 96 | _parseSTOP : (stop) -> 97 | index = parseInt stop[1], 36 98 | @bms.stop[index] = stop[2] 99 | 100 | _parseBPM : (exbpm) -> 101 | index = parseInt exbpm[1], 36 102 | @bms.exbpm[index] = exbpm[2] 103 | 104 | _parseProperty : (property) -> 105 | @bms[property[1].toLowerCase()] = property[2] 106 | 107 | _createBar : -> 108 | timing: 0.0 109 | wav : 110 | message: [] 111 | timing: [] 112 | id : [] 113 | bmp : 114 | message: [] 115 | timing : [] 116 | id : [] 117 | bpm : 118 | message: [] 119 | timing : [] 120 | val : [] 121 | stop : 122 | message: [] 123 | timing : [] 124 | id : [] 125 | meter: 1.0 126 | note : 127 | key : ({ message: [], timing : [], id : [] } for i in [0..8]) 128 | 129 | _parseChannelMsg : (msg) -> 130 | measureNum = parseInt msg[1] 131 | ch = parseInt msg[2] 132 | data = msg[3] 133 | 134 | unless @bms.data[measureNum]? 135 | @bms.data[measureNum] = @_createBar() 136 | 137 | switch ch 138 | when 1 # WAV 139 | @_storeWAV data, @bms.data[measureNum].wav, measureNum 140 | when 2 # Meter 141 | meter = parseFloat(data) 142 | if meter > 0 143 | @bms.data[measureNum].meter = meter 144 | when 3 # BPM 145 | @_storeBPM data, @bms.data[measureNum].bpm 146 | when 4 # BMP 147 | @_storeData data, @bms.data[measureNum].bmp 148 | when 8 # EXBPM 149 | @_storeEXBPM data, @bms.data[measureNum].bpm 150 | when 9 # STOP 151 | @_storeSTOP data, @bms.data[measureNum].stop 152 | when 11, 12, 13, 14, 15 153 | @_storeData data, @bms.data[measureNum].note.key[ch - 11] 154 | when 16, 17 155 | @_storeData data, @bms.data[measureNum].note.key[ch - 9] 156 | when 18, 19 157 | @_storeData data, @bms.data[measureNum].note.key[ch - 13] 158 | else 159 | 160 | _storeWAV : (msg, array, measureNum) -> 161 | @wavMessages[measureNum] ?= [] 162 | @wavMessages[measureNum].push (parseInt(msg[i..i+1],36) for i in [0..msg.length-1] by 2) 163 | 164 | _storeData : (msg, array) -> 165 | data = (parseInt(msg[i..i+1],36) for i in [0..msg.length-1] by 2) 166 | array.message = _merge(array.message, data) 167 | 168 | _storeSTOP : (msg, array) -> 169 | data = (parseInt(msg[i..i+1],16) for i in [0..msg.length-1] by 2) 170 | array.message = _merge(array.message, data) 171 | 172 | _storeBPM : (msg, bpm) -> 173 | bpm.message = (parseInt(msg[i..i+1],16) for i in [0..msg.length-1] by 2) 174 | 175 | _storeEXBPM : (msg, bpm) -> 176 | bpm.message = for i in [0..msg.length-1] by 2 177 | if @bms.exbpm[parseInt(msg[i..i+1],16)]? 178 | parseFloat @bms.exbpm[parseInt(msg[i..i+1],16)] 179 | else 0 180 | console.log bpm.message 181 | 182 | 183 | _lcm = (a,b) -> 184 | gcm = (x,y) -> 185 | if y==0 then x else gcm(y, x % y) 186 | return a / gcm(a,b) * b 187 | 188 | # ex. expand([1,2,3],6) == [1,0,2,0,3,0] 189 | _expand = (array, length) -> 190 | return (0 for i in [0..length-1]) if array.length is 0 191 | interval = length / array.length 192 | return ((if i % interval == 0 then array[i / interval] else 0) \ 193 | for i in [0..length-1]) 194 | 195 | # ex. merge([1,2,3], [0,4,0]) == [1,4,3] 196 | _merge = (ary1, ary2) -> 197 | return ary2 if ary1.length is 0 198 | lcm = _lcm(ary1.length, ary2.length) 199 | ret = _expand(ary1, lcm) 200 | for value, i in _expand(ary2, lcm) 201 | continue if value == 0 202 | ret[i] = value 203 | return ret 204 | 205 | # 全体をパースした後でtiming等を修正 206 | _modifyAfterParse : () -> 207 | bpm = @bms.bpm 208 | time = 0 209 | for bar, i in @bms.data 210 | if not bar? 211 | @bms.data[i] = @_createBar() 212 | @bms.data[i].timing = time 213 | time += (240000 / bpm) 214 | continue 215 | 216 | bar.timing = time 217 | if bar.bpm.message.length == 0 218 | bar.bpm.message = [0] 219 | 220 | @_noteTiming time, bar, bpm 221 | @_bmpTiming time, bar, bpm 222 | @_stopTiming time, bar, bpm 223 | @_wavTiming time, bar, bpm, @wavMessages[i] 224 | 225 | l = bar.bpm.message.length 226 | for val, i in bar.bpm.message 227 | if val != 0 228 | bar.bpm.val.push(val) 229 | bar.bpm.timing.push(time) 230 | bpm = val 231 | time += (240000 / bpm) * (1 / l) * bar.meter 232 | 233 | _calcTiming : (time, objects, bpmobj, bpm, meter) -> 234 | bl = bpmobj.message.length 235 | ol = objects.message.length 236 | lcm = _lcm(bl, ol) 237 | bpms = _expand(bpmobj.message, lcm) 238 | objs = _expand(objects.message, lcm) 239 | t = 0 240 | b = bpm 241 | objects.timing = [] 242 | objects.id = [] 243 | for val, i in bpms 244 | if objs[i] isnt 0 245 | objects.timing.push(time + t) 246 | objects.id.push(objs[i]) 247 | if @bms.endTime < time + t 248 | @bms.endTime = time + t 249 | 250 | b = val if val isnt 0 # change bpm 251 | t += (240000 / b) * (1 / lcm) * meter 252 | return 253 | 254 | _noteTiming : (time, bar, bpm) -> 255 | l = bar.bpm.message.length 256 | for n in bar.note.key when n.message.length isnt 0 257 | @_calcTiming(time, n, bar.bpm, bpm, bar.meter) 258 | return 259 | 260 | _bmpTiming : (time, bar, bpm) -> 261 | @_calcTiming time, bar.bmp, bar.bpm, bpm, bar.meter 262 | 263 | _stopTiming : (time, bar, bpm) -> 264 | @_calcTiming time, bar.stop, bar.bpm, bpm, bar.meter 265 | 266 | _wavTiming : (time, bar, bpm, wavss) -> 267 | return if not wavss? 268 | l = bar.bpm.message.length 269 | result = [] 270 | for ws in wavss 271 | wl = ws.length 272 | lcm = _lcm(l, wl) 273 | bpms = _expand(bar.bpm.message, lcm) 274 | wavs = _expand(ws, lcm) 275 | t = 0 276 | b = bpm 277 | for val, i in bpms 278 | if wavs[i] isnt 0 279 | result.push { timing: time + t, id: wavs[i]} 280 | if @bms.endTime < time + t 281 | @bms.endTime = time + t 282 | if val isnt 0 # change bpm 283 | b = val 284 | t += (240000 / b) * (1 / lcm) * bar.meter 285 | 286 | for w in result.sort( (a,b) -> a['timing'] - b['timing']) 287 | bar.wav.timing.push w.timing 288 | bar.wav.id.push w.id 289 | 290 | # OPTIMIZE: bpm,bmp,wavは小節に依存しないよう配列に詰めなおす 291 | _serialize : (arr, name, bms_data) -> 292 | for v, i in bms_data 293 | for t, j in v[name].timing when t? 294 | if v[name].val? 295 | arr.push 296 | timing : t 297 | val : v[name].val[j] 298 | else if v[name].id? 299 | arr.push 300 | timing : t 301 | id : v[name].id[j] 302 | 303 | _calcTotalNote : -> 304 | @bms.data.reduce ( (t, d) -> t + 305 | d.note.key.reduce ((nt, k) -> nt + k.id.length), 0), 0 306 | 307 | module.exports = Parser 308 | -------------------------------------------------------------------------------- /src/app.coffee: -------------------------------------------------------------------------------- 1 | KeyboardService = require './keyboardService' 2 | NotesLayer = require './notesLayer' 3 | RateLayer = require './rateLayer' 4 | StatsLayer = require './statsLayer' 5 | BpmLayer = require './bpmLayer' 6 | PlaytimeLayer = require './playtimeLayer' 7 | AnimeLayer = require './animationLayer' 8 | Timer = require './timer' 9 | Audio = require './audio' 10 | GameoverScene = require './gameoverScene' 11 | res = require './resource' 12 | .resObjs 13 | 14 | # TODO : move 15 | skin = 16 | body : 17 | bgImage : 18 | src : res.bgImage 19 | turntable : 20 | src : res.turntableImage 21 | x : -274 22 | y : 348 23 | z : 10 24 | notes : 25 | fallDist : 320 26 | keyNum : 8 27 | offsetX : -266 28 | marginX : 1.95 29 | z : 4 30 | nodeImage : 31 | src : res.nodeImage 32 | width : 194 33 | height : 1 34 | x : -190 35 | noteTurntableImage : 36 | src : res.noteTurntableImage 37 | width : 41 38 | height : 6 39 | noteWhiteImage : 40 | src : res.noteWhiteImage 41 | width : 22 42 | height : 6 43 | noteBlackImage : 44 | src : res.noteBlackImage 45 | width : 17 46 | height : 6 47 | bgImage : 48 | src : res.noteBgImage 49 | x : -190 50 | y : 320 51 | greatEffect : 52 | src : res.greatEffectImage 53 | width : 80 54 | height : 80 55 | row : 6 56 | colum : 3 57 | delay : 0.02 58 | z : 5 59 | keyEffect : 60 | y : 162 61 | turntableKeydownImage : 62 | src : res.turntableKeydownImage 63 | whiteKeydownImage : 64 | src : res.whiteKeydownImage 65 | blackKeydownImage : 66 | src : res.blackKeydownImage 67 | rate : 68 | z : 10 69 | meter : 70 | src : res.meterImage 71 | width : 4 72 | height : 12 73 | x : -292 74 | y : 386 75 | z : 10 76 | label : 77 | src : res.numeralImage 78 | width : 26.2 79 | height : 16 80 | scale : 0.5 81 | margin : 0 82 | x : -68 83 | y : 384 84 | stats : 85 | z : 10 86 | judge : 87 | src : './res/judge-image.png' 88 | width : 153 89 | height : 38.8 90 | x : -188 91 | y : 260 92 | score : 93 | src : res.numeralImage 94 | width : 26.2 95 | height : 16 96 | scale : 0.8 97 | margin : 0 98 | x : -122 99 | y : 429 100 | pgreatNum : 101 | src : res.numeralImage 102 | width : 26.2 103 | height : 16 104 | scale : 0.5 105 | margin : 0 106 | x : 18 107 | y : 415 108 | greatNum : 109 | src : res.numeralImage 110 | width : 26.2 111 | height : 16 112 | scale : 0.5 113 | margin : 0 114 | x : 18 115 | y : 428 116 | goodNum : 117 | src : res.numeralImage 118 | width : 26.2 119 | height : 16 120 | scale : 0.5 121 | margin : 0 122 | x : 18 123 | y : 439 124 | badNum : 125 | src : res.numeralImage 126 | width : 26.2 127 | height : 16 128 | scale : 0.5 129 | margin : 0 130 | x : 18 131 | y : 451 132 | poorNum : 133 | src : res.numeralImage 134 | width : 26.2 135 | height : 16 136 | scale : 0.5 137 | margin : 0 138 | x : 18 139 | y : 462 140 | comboNum : 141 | src : res.numeralImage 142 | width : 26.2 143 | height : 16 144 | scale : 0.8 145 | margin : 0 146 | x : -102 147 | y : 451 148 | bpm : 149 | src : res.numeralImage 150 | width : 26.2 151 | height : 16 152 | scale : 0.6 153 | margin : 0 154 | x : 78 155 | y : 450 156 | z : 10 157 | playtime : 158 | src : res.numeralImage 159 | width : 26.2 160 | height : 16 161 | scale : 0.6 162 | margin : 0 163 | z : 10 164 | minuite : 165 | x : 39 166 | y : 393 167 | second : 168 | x : 73 169 | y : 393 170 | bmp : 171 | x : 106 172 | y : 186 173 | 174 | AppLayer = cc.Layer.extend 175 | ctor : (@_bms, prefix)-> 176 | @_super() 177 | @_timer = new Timer() 178 | 179 | # TODO : 180 | @_stopIndex = 0 181 | 182 | @_addBackground() 183 | @_audio = new Audio @_timer, @_bms.bgms 184 | @_audio.init @_bms.wav, prefix 185 | @addChild @_audio 186 | 187 | @_keyboard = new KeyboardService @_timer 188 | 189 | # FIXME : move to argument 190 | keyConfig = [ 191 | 'Z'.charCodeAt(0) 192 | 'S'.charCodeAt(0) 193 | 'X'.charCodeAt(0) 194 | 'D'.charCodeAt(0) 195 | 'C'.charCodeAt(0) 196 | 'F'.charCodeAt(0) 197 | 'V'.charCodeAt(0) 198 | 16 199 | ] 200 | 201 | @_keyboard.init() 202 | @_keyboard.addListener v, @_onKeydown.bind this, id for v, id in keyConfig 203 | @_keyboard.addListener 27, @_onEscKeydown.bind this 204 | @addChild @_keyboard 205 | 206 | # FIXME : move to argument 207 | config = 208 | reactionTime : 200 209 | removeTime : 200 210 | judge : 211 | pgreat : 20 212 | great : 40 213 | good : 105 214 | bad : 150 215 | poor : 200 216 | 217 | @_notesLayer = new NotesLayer skin.notes, @_timer, config 218 | @_notesLayer.init @_bms 219 | @_notesLayer.addListener 'hit', @_onHit.bind this 220 | @_notesLayer.addListener 'judge', @_onJudge.bind this 221 | #@_notesLayer.addListener 'end', @_onEnd.bind this 222 | @addChild @_notesLayer, skin.notes.z 223 | 224 | @_rate = new RateLayer skin.rate 225 | @_rate.init 226 | # FIXME : move to argument 227 | ### 228 | http://2nd.geocities.jp/yoshi_65c816/bms/LR2.html 229 | t=#TOTAL n=total notes 230 | ゲージの種類 PGERAT GREAT GOOD POOR BAD 空POOR 231 | GROOVE t/n t/n (t/n)/2 -6 -4 -2 232 | EASY t/n*1.2 t/n*1.2 (t/n*1.2)/2 -4.8 -3.2 -1.6 233 | HARD 0.1※2 0.1※2 0.05※2 -10※ -6※ -2※ 234 | 段位 0.1 0.1 0.05? -3 -2 -2 235 | 本家GROOVE a a a/2 -6 -2 -2 236 | 本家EASY a a a/2 -4.8 -1.6 -1.6 237 | 本家HARD 0.16 0.16 0 -9 -5 5 238 | 本家段位 0.16 0.16 0.04 -2.5 -1.5 -1.5 239 | ### 240 | initRate : 20 241 | great : @_bms.total / @_bms.totalNote * 1.2 242 | good : @_bms.total / @_bms.totalNote * 0.6 243 | bad : -3.2 244 | poor : -4.8 245 | epoor : -1.6 246 | num : 50 247 | clearVal : 40 248 | @addChild @_rate, skin.rate.z 249 | 250 | @_stats = new StatsLayer skin.stats 251 | @_stats.init @_bms.totalNote, 252 | pgreat : 150000 253 | great : 100000 254 | good : 20000 255 | combo : 50000 256 | 257 | @addChild @_stats, skin.stats.z 258 | 259 | @_bpm = new BpmLayer skin.bpm, @_timer, @_bms.bpms 260 | @_bpm.init() 261 | @addChild @_bpm, skin.bpm.z 262 | @_playtime = new PlaytimeLayer skin.playtime, @_timer 263 | @_playtime.init() 264 | @addChild @_playtime, skin.playtime.z 265 | 266 | @_animeLayer = new AnimeLayer skin.bmp, @_timer 267 | console.dir @_bms.bmp 268 | console.dir @_bms.animations 269 | @_animeLayer.init @_bms.bmp, @_bms.animations, prefix 270 | @addChild @_animeLayer 271 | # FIXME 272 | if @_bms.animations.length is 0 or @_bms.bmp[1]?.match(/mpg$/)? 273 | soundonly = new cc.LabelTTF "Sound Only", "sapceage" , 32 274 | soundonly.x = cc.screenSize.width / 2 + 100 275 | soundonly.y = cc.screenSize.height - 200 276 | soundonly.setOpacity 200 277 | @addChild soundonly, 100 278 | 279 | start : -> 280 | @_notesLayer.start off 281 | @_audio.startBgm() 282 | @_rate.start() 283 | @_bpm.start() 284 | @_playtime.start() 285 | @_animeLayer.start() 286 | @_stats.start() 287 | @_timer.start() 288 | @scheduleUpdate() 289 | 290 | update : -> 291 | time = @_timer.get() 292 | 293 | # TODO : to class 294 | if @_bms.stopTiming[@_stopIndex]? 295 | if time >= @_bms.stopTiming[@_stopIndex].timing 296 | #console.log "stop id=#{@_bms.stopTiming[@_stopIndex].id}, val =#{@_bms.stop[@_bms.stopTiming[@_stopIndex].id]}" 297 | measureTime = 240000 / @_bpm.get() 298 | stopTime = @_bms.stop[@_bms.stopTiming[@_stopIndex].id] / 192 * measureTime 299 | console.log "stop time = #{stopTime}, bpm = #{@_bpm.get()}" 300 | @_timer.pause() 301 | @scheduleOnce -> 302 | @_timer.start() 303 | , stopTime / 1000 304 | @_stopIndex++ 305 | 306 | @_exitGame() if time > @_bms.endTime + 5000 307 | 308 | onExit : -> 309 | @_super() 310 | @removeAllChildren on 311 | 312 | _exitGame : -> 313 | @unscheduleUpdate() 314 | @_timer.stop() 315 | cc.director.runScene new cc.TransitionFade(1.2, new GameoverScene(@_stats.get())) 316 | 317 | _onKeydown : (id, key, time) -> 318 | @_notesLayer.onTouch id, time 319 | 320 | _onEscKeydown : (id, key, time) -> 321 | # FIXME :先頭でrequireするとerror 322 | MenuScene = require './menuScene' 323 | @unscheduleUpdate() 324 | @_timer.stop() 325 | cc.director.runScene new cc.TransitionFade(1.2, new MenuScene()) 326 | 327 | _onHit : (event, wavId) -> 328 | @_audio.play wavId 329 | 330 | _onEnd : (event) -> 331 | @scheduleOnce @_changeSceneToGameOver, 5 332 | 333 | _onJudge : (event, judge) -> 334 | @_rate.reflect judge 335 | @_stats.reflect judge 336 | 337 | @_animeLayer.onPoor() if judge is 'poor' or judge is 'epoor' 338 | 339 | _addBackground : -> 340 | bg = new cc.Sprite res.bgImage 341 | bg.x = cc.director.getWinSize().width / 2 342 | bg.y = cc.director.getWinSize().height / 2 343 | bg.scale = window.parent.screen.height / bg.height 344 | @addChild bg, 0 345 | 346 | # test image 347 | bg = new cc.Sprite "res/test.png" 348 | bg.x = cc.director.getWinSize().width / 2 349 | bg.y = cc.director.getWinSize().height - 240 350 | @addChild bg, 1 351 | 352 | turntable = new cc.Sprite res.turntableImage 353 | turntable.x = cc.screenSize.width / 2 + skin.body.turntable.x 354 | turntable.y = cc.screenSize.height - skin.body.turntable.y 355 | turntable.setOpacity 200 356 | @addChild turntable, skin.body.turntable.z 357 | turntable.runAction new cc.RepeatForever new cc.RotateBy(5, 360) 358 | 359 | #window.onblur = -> 360 | # window.stop(); 361 | 362 | 363 | AppScene = cc.Scene.extend 364 | 365 | ctor : (bms, prefix)-> 366 | @_super() 367 | layer = new AppLayer bms, prefix 368 | @addChild layer 369 | layer.start() 370 | 371 | onExit : -> @removeAllChildren on 372 | 373 | module.exports = AppScene 374 | -------------------------------------------------------------------------------- /bms.cson: -------------------------------------------------------------------------------- 1 | # jsbで読む場合ファイルはutf-8の*.txtである必要がある 2 | # txtはresディレクトリの下に配置する必要あり 3 | [ 4 | {url : "bms/SHK_new04_Reality/Reality_5_Easy.bms" , title : "Reality [5key Easy]" , artist : '' , level : 2} 5 | {url : "bms/SHK_new04_Reality/Reality_5_Hard2.bms" , title : "Reality [5key Hard2]" , artist : '' , level : 7} 6 | {url : "bms/SHK_new04_Reality/Reality_5_Normal.bms" , title : "Reality [5key Normal]" , artist : '' , level : 4} 7 | {url : "bms/SHK_new04_Reality/Reality_5_SuperHard.bms" , title : "Reality [5key Super Hard]" , artist : '' , level : 11} 8 | {url : "bms/SHK_new04_Reality/Reality_5_veryHard.bms" , title : "Reality [5Key very Hard]" , artist : '' , level : 7} 9 | 10 | {url : "bms/SHK_new04_Reality/Reality_7_Easy1.bms" , title : "Reality [7keys Easy1]" , artist : '' , level : 3} 11 | {url : "bms/SHK_new04_Reality/Reality_7_Easy2.bms" , title : "Reality [7keys Easy2]" , artist : '' , level : 2} 12 | {url : "bms/SHK_new04_Reality/Reality_7_Hard.bms" , title : "Reality [7keys Hard]" , artist : '' , level : 8} 13 | {url : "bms/SHK_new04_Reality/Reality_7_Normal.bms" , title : "Reality [7keys Normal]" , artist : '' , level : 3} 14 | {url : "bms/SHK_new04_Reality/Reality_7_SuperHard.bms" , title : "Reality [7keys Super Hard]" , artist : '' , level : 11} 15 | 16 | 17 | {url : "bms/ninth/386.bms" , title : "ninth" , artist : ' sta' , level : 6} 18 | {url : "bms/ninth/6502.bme" , title : "ninth" , artist : ' sta' , level : 7} 19 | {url : "bms/sq_07-01/sq_07-01.bms" , title : "千年女王[Normal]" , artist : ' しらいし' , level : 11} 20 | {url : "bms/sq_07-01/sq_07-02.bms" , title : "千年女王[Hyper]" , artist : ' しらいし' , level : 12} 21 | # if sequence {url : "bms/Declinin/declinin.bms" , title : "Declinin'" , artist : ' ' , level : 4} 22 | {url : "bms/Odin/_Odin_07-1.bms" , title : "Odin[Normal]" , artist : ' しらいし' , level : 6} 23 | {url : "bms/Odin/_Odin_07-2.bms" , title : "Odin[Hyper]" , artist : ' しらいし' , level : 9} 24 | {url : "bms/Odin/_Odin_07-3.bms" , title : "Odin[Another]" , artist : ' しらいし' , level : 11} 25 | {url : "bms/Odin/_Odin_07-4.bms" , title : "Odin[Black Another]" , artist : ' しらいし' , level : 12} 26 | 27 | {url : "bms/sweez/niraikanai7_n_short_bmssp7b.bme" , title : "ニライカナイ(short ver.) [7keys Beginner]", artist : ' sweez & Meine Meinung / BMSSP' , level : 2} 28 | {url : "bms/sweez/niraikanai7_n.bme" , title : "ニライカナイ" , artist : ' sweez & Meine Meinung' , level : 8} 29 | {url : "bms/under_the_moonlight/under_the_moonlight_7n.bme" , title : "under the moonlight [7keys Normal]" , artist : ' Yamajet' , level : 4} 30 | {url : "bms/under_the_moonlight/under_the_moonlight_7h.bme" , title : "under the moonlight [7keys Hyper]" , artist : ' Yamajet' , level : 8} 31 | {url : "bms/under_the_moonlight/under_the_moonlight_7a.bme" , title : "under the moonlight [7keys Another]" , artist : ' Yamajet' , level : 8} 32 | {url : "bms/SAMBISTA/sambista.bms" , title : "SAMBISTA" , artist : ' DOT96' , level : 6} 33 | {url : "bms/SAMBISTA/sambista_bmssp5e.bms" , title : "SAMBISTA[Easy]" , artist : ' DOT96' , level : 2} 34 | {url : "bms/RadioOrganico/01_radio_organico[light7].bme" , title : "Radio Organico[LIGHT7]" , artist : ' Team:SASAKURATION' , level : 2} 35 | {url : "bms/RadioOrganico/02_radio_organico[7keys].bme" , title : "Radio Organico[7KEYS]" , artist : ' Team:SASAKURATION' , level : 4} 36 | {url : "bms/RadioOrganico/03_radio_organico[Another].bme" , title : "Radio Organico[ANOTHER]" , artist : ' Team:SASAKURATION' , level : 6} 37 | {url : "bms/Quark/quark01.bme" , title : "Quark" , artist : ' Hate vs Brilliance' , level : 6} 38 | {url : "bms/Quark/quark02.bme" , title : "Quark [Hyper]" , artist : ' Hate vs Brilliance' , level : 10} 39 | {url : "bms/Quark/quark03.bme" , title : "Quark [Another]" , artist : ' Hate vs Brilliance' , level : 12} 40 | {url : "bms/oceanbird/oceanbird_7_normal.bms" , title : "oceanbird" , artist : ' sXacla / TOHRU MiTSUHASHi' , level : 3} 41 | {url : "bms/oceanbird/oceanbird_7_hyper.bms" , title : "oceanbird [Hyper]" , artist : ' sXacla / TOHRU MiTSUHASHi' , level : 6} 42 | {url : "bms/oceanbird/oceanbird_7_another.bms" , title : "oceanbird [Another]" , artist : ' sXacla / TOHRU MiTSUHASHi' , level : 9} 43 | {url : "bms/moonscape/ms_bmssp7n.bme" , title : "Moonscape [7keys Normal]" , artist : ' syatten / BMSSP' , level : 3} 44 | {url : "bms/moonscape/ms_sph.bme" , title : "Moonscape (SP HYPER)" , artist : ' syatten / BMSSP' , level : 8} 45 | {url : "bms/moonscape/ms_spa.bme" , title : "Moonscape (SP ANOTHER)" , artist : ' syatten / BMSSP' , level : 10} 46 | {url : "bms/Lightstep/lightstep_5e.bms" , title : "Lightstep [5keys Easy]" , artist : ' Kaede a.k.a. fen / BMSSP' , level : 4} 47 | {url : "bms/Lightstep/lightstep_5.bms" , title : "Lightstep" , artist : ' Kaede a.k.a. fen / BMSSP' , level : 6} 48 | {url : "bms/mommy/I'll_forget_you_you'll_never_forget_me_7N.bme", title : "I'll forget you, you'll never forget me.[7key,Normal]" , artist : ' mommy' , level : 6} 49 | {url : "bms/mommy/I'll_forget_you_you'll_never_forget_me_7H.bme", title : "I'll forget you, you'll never forget me.[7key,Hyper]" , artist : ' mommy' , level : 8} 50 | {url : "bms/mommy/I'll_forget_you_you'll_never_forget_me_7A.bme", title : "I'll forget you, you'll never forget me.[7key,Another]" , artist : ' mommy' , level : 11} 51 | {url : "bms/HEisanEnergizer/heiseng7l.bme" , title : 'HE is an Energizer(7keys EASY)' , artist : ' Mr.ABC' , level : 5} 52 | {url : "bms/HEisanEnergizer/heiseng7.bme" , title : 'HE is an Energizer(7keys)' , artist : ' Mr.ABC' , level : 7} 53 | {url : "bms/gy_greens/grsl_7.bme" , title : 'Greensleeves[7key]' , artist : ' BMS:G-Y / MIDI:Haven' , level : 1} 54 | {url : "bms/gy_greens/grsl_7h.bme" , title : 'Greensleeves[7key Hyper]' , artist : ' BMS:G-Y / MIDI:Haven' , level : 5} 55 | {url : "bms/gy_greens/grsl_7a.bme" , title : 'Greensleeves[7key Another]' , artist : ' BMS:G-Y / MIDI:Haven' , level : 8} 56 | {url : "bms/gi_MTM/MTM_7e.bme" , title : 'Maple Town Memories[7key-Easy]' , artist : ' BMS/GYAON 作曲/井上' , level : 4} 57 | {url : "bms/gi_MTM/MTM_7n.bme" , title : 'Maple Town Memories[7key] ' , artist : ' BMS/GYAON 作曲/井上' , level : 6} 58 | {url : "bms/gi_MTM/MTM_7a.bme" , title : 'Maple Town Memories[7key-Another]' , artist : ' BMS/GYAON 作曲/井上' , level : 6} 59 | {url : "bms/evangelize_radio edit/evangelize_spn.bme" , title : 'Evangelize (Radio Edit) (SP NORMAL)' , artist : ' syatten + 140' , level : 5} 60 | {url : "bms/evangelize_radio edit/evangelize_sph.bme" , title : 'Evangelize (Radio Edit) (SP HYPER)' , artist : ' syatten + 140' , level : 7} 61 | {url : "bms/evangelize_radio edit/evangelize_spb.bme" , title : 'Evangelize (Radio Edit) (SP BEGINNER)' , artist : ' syatten + 140' , level : 1} 62 | {url : "bms/evangelize_radio edit/evangelize_spa.bme" , title : 'Evangelize (Radio Edit) (SP ANOTHER)' , artist : ' syatten + 140' , level : 11} 63 | {url : "bms/evangelize/_blurryimages.bms" , title : 'evangelize (blurry images)' , artist : ' stereoberry (original:syatten BGA:ƒÎ=72)' , level : 5} 64 | {url : "bms/AVALON/01_avalon[light7].bme" , title : 'AVALON [light]' , artist : ' Team:SASAKURATION' , level : 5} 65 | {url : "bms/AVALON/02_avalon[7key].bme" , title : 'AVALON [Normal]' , artist : ' Team:SASAKURATION' , level : 10} 66 | {url : "bms/AVALON/03_avalon[Another].bme" , title : 'AVALON [Another]' , artist : ' Team:SASAKURATION' , level : 12} 67 | {url : "bms/AVALON/04_avalon_[IIDX].bme" , title : 'AVALON [IIDX]' , artist : ' Team:SASAKURATION' , level : 0} 68 | {url : "bms/bellydance/a_bga_bmssp_e.bms" , title : 'belly dance (Consistently_Drinkin_remix)' , artist : ' Kotira Sample' , level : 2} 69 | {url : "bms/bellydance/a_bga.bms" , title : 'belly dance (Consistently_Drinkin_remix)' , artist : ' Kotira Sample' , level : 4} 70 | {url : "bms/atonement/ayy2.bms" , title : 'atonement you you' , artist : ' ' , level : 6} 71 | {url : "bms/AbsurdGaff/_abs07_00_bmssp7e.bme" , title : 'Absurd Gaff [7keys Easy]' , artist : ' siromaru / BMSSP' , level : 3} 72 | {url : "bms/AbsurdGaff/_ms_abs07_01.bme" , title : 'Absurd Gaff [Normal]' , artist : ' siromaru / Mentalstock' , level : 8} 73 | {url : "bms/AbsurdGaff/_ms_abs07_02.bme" , title : 'Absurd Gaff [Hyper]' , artist : ' siromaru / Mentalstock' , level : 10} 74 | {url : "bms/AbsurdGaff/_ms_abs07_03.bme" , title : 'Absurd Gaff [Another]' , artist : ' siromaru / Mentalstock' , level : 21} 75 | {url : "bms/Lapis - SHIKI/lapis7key.bme" , title : 'Lapis' , artist : ' SHIKI' , level : 8} 76 | {url : "bms/Lapis - SHIKI/lapis7keya.bme" , title : 'Lapis [Another]' , artist : ' SHIKI' , level : 10} 77 | {url : "bms/Lapis - SHIKI/lapis7keyb.bme" , title : 'Lapis [beginner]' , artist : ' SHIKI' , level : 2} 78 | {url : "bms/Lapis - SHIKI/lapis7keyl.bme" , title : 'Lapis [Light]' , artist : ' SHIKI' , level : 5} 79 | ] 80 | -------------------------------------------------------------------------------- /res/va.txt: -------------------------------------------------------------------------------- 1 | *---------------------- HEADER FIELD 2 | #PLAYER 1 3 | #GENRE retrogressive 4 | #TITLE V(SOFT LANDING PARADISE) 5 | #ARTIST オサムイモン (SUGI & OSAM) feat. VAKA 6 | #BPM 450 7 | #PLAYLEVEL 0 8 | #RANK 3 9 | #TOTAL 1000 10 | #STAGEFILE 11 | %EMAIL 12 | %URL 13 | 14 | #WAV12 17.wav 15 | #WAV14 19.wav 16 | #WAV15 20.wav 17 | #WAV16 21.wav 18 | #WAV17 22.wav 19 | #WAV18 23.wav 20 | #WAV19 24.wav 21 | #WAV1A 25.wav 22 | #WAV1B 26.wav 23 | #WAV1C 27.wav 24 | #WAV1D 28.wav 25 | #WAV1E 29.wav 26 | #WAV20 31.wav 27 | #WAV21 32.wav 28 | #WAV22 33.wav 29 | #WAV23 34.wav 30 | #WAV25 36.wav 31 | #WAV26 37.wav 32 | #WAV27 38.wav 33 | #WAV28 39.wav 34 | #WAV29 40.wav 35 | #WAV2A 41.wav 36 | #WAV2C 43.wav 37 | #WAV2D 44.wav 38 | #WAV2E 45.wav 39 | #WAV2F 46.wav 40 | #WAV30 47.wav 41 | #WAV31 48.wav 42 | #WAV32 49.wav 43 | #WAV33 50.wav 44 | #WAV34 51.wav 45 | #WAV36 53.wav 46 | #WAV37 54.wav 47 | #WAV38 55.wav 48 | 49 | *---------------------- MAIN DATA FIELD 50 | 51 | 52 | #00202:1.05 53 | 54 | #00302:1.1 55 | #00312:1414141414141414 56 | 57 | #00402:1.2 58 | #00412:1414141414141414 59 | #00415:1A1A1A1A1A1A1A1A 60 | 61 | #00502:1.3 62 | #00512:1414141414141414 63 | #00515:1A1A1A1A1A1A1A1A 64 | #00519:2323232323232323 65 | 66 | #00602:1.4 67 | #00612:1414141414141414 68 | #00615:1A1A1A1A1A1A1A1A 69 | #00619:2323232323232323 70 | 71 | #00702:1.5 72 | #00711:0000000012121212 73 | #00713:1515151500000000 74 | #00714:1919191919191919 75 | #00718:2121212121212121 76 | 77 | #00802:1.6 78 | #00811:1212121212121212 79 | #00813:1717171717171717 80 | #00815:1A1A1A1A1A1A1A1A 81 | 82 | #00902:1.7 83 | #00912:1616161616161616 84 | #00914:1919191919191919 85 | #00918:1C1C1C1C1C1C1C1C 86 | 87 | #01002:1.8 88 | #01011:0014141414140000 89 | #01013:1818181818181818 90 | #01015:1B1B1B1B1B1B1B1B 91 | 92 | #01102:1.9 93 | #01112:1616161616161616 94 | #01114:1919191919191919 95 | #01118:1C1C1C1C1C1C1C1C 96 | 97 | #01202:2 98 | #01203:000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000181A1C1E202224272A2D3033 99 | #01211:1414141414141414 100 | #01213:0000000018181818 101 | #01214:1919191900000000 102 | #01215:1B1B1B1B1B1B1B1B 103 | 104 | #01303:36393C3F42454800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004900000000000000000000004A00000000000000000000004B00000000000000000000004C00000000000000000000004D00000000000000000000004E00000000000000000000004F0000000000000000000000500000000000000000000000510000000000000000000000 105 | #01311:2500000000000025250000000000002525000000000000002500000000000000 106 | #01312:0000000000000000000000000000000000000000000000260000000000000026 107 | #01313:1900000000282800000000000028280000000000000028000000000000002800 108 | #01314:0000000000000000000000000000000000000000002A000000000000002A0000 109 | #01315:0000002C2C0000000000002C2C000000000000002C000000000000002C000000 110 | #01318:000000000000000000000000000000000000002D000000000000002D00000000 111 | #01319:0031310000000000003131000000000000002F000000000000002F0000000000 112 | #01316:0000000000000000000000000000000000310000000000000031000000000000 113 | 114 | #01403:520000000000000000000000000000000000005300000000000000000000000000000000000054000000000000000000000000000000000000005500000000000000000000000000000000000056000000000000000000000000000000000000570000000000000000000000000000000000005800000000000000000000000000000000000059000000000000000000000000000000000000005A0000000000000000000000000000000000005B000000000000000000000000000000000000 115 | #01411:0000000000000000000000000000000019190019191900191919000019000000 116 | #01412:00000000000000001A1A1A1A1A000000 117 | #01413:0020000000200000002000000020000000000000000000000000000000000000 118 | #01414:000000001C1C1C1C 119 | #01415:25252525252525252000200020002000 120 | #01418:0000002800000028000000280000002800000000000000000000000000000000 121 | 122 | #01503:5C0000000000000000000000000000000000005D0000000000000000000000000000000000005E000000000000000000000000000000000000005F00000000000000000000000000000000000060000000000000000000000000000000000000610000000000000000000000000000000000006200000000000000000000000000000000000063000000000000000000000000000000000000006400000000000000000000000000000000000065000000000000000000000000000000000000 123 | #01511:1900000019000000190000001900000019000000000000282800000000000028 124 | #01513:0000000000000000000000000000000000000000002C2C0000000000002C2C00 125 | #01514:1C1C1C1C1C000000 126 | #01515:2000000020000000200000002000000020000031310000000000003131000000 127 | #01518:0028 128 | #01519:0000000000000000000000000000000000343400000000000034340000000000 129 | 130 | #01603:66000000000000000000000000000000000000670000000000000000000000000000000000006800000000000000000000000000000000000000690000000000000000000000000000000000006A0000000000000000000000000000000000006B0000000000000000000000000000000000006C0000000000000000000000000000000000006D000000000000000000000000000000000000006E0000000000000000000000000000000000006F000000000000000000000000000000000000 131 | #01611:28280000 132 | #01612:000000000000002A000000000000002A00000000000000000000000000000000 133 | #01613:0000000000002C000000000000002C0000250000002500000025000000250000 134 | #01614:00000000002E000000000000002E000028002800280028002800280028002800 135 | #01615:0030003000000000 136 | #01618:0000003100000000000000310000000000000000000000000000000000000000 137 | #01619:000033000000000000003300000000000000002C0000002C0000002C0000002C 138 | #01616:0034000000000000003400000000000000000000000000000000000000000000 139 | 140 | #01703:700000000000000000000000000000000000007100000000000000000000000000000000000072000000000000000000000000000000000000007300000000000000000000000000000000000074000000000000000000000000000000000000750000000000000000000000000000000000007600000000000000000000000000000000000077000000000000000000000000000000000000007800000000000000000000000000000000000079000000000000000000000000000000000000 141 | #01711:1919191919191919 142 | #01713:1C1C001C1C1C001C1C1C00001C0000001C0000001C0000001C0000001C000000 143 | #01714:1E1E1E1E1E0000000000000000000000 144 | #01718:2020202020202020 145 | 146 | #01803:7A0000000000000000000000000000000000007B0000000000000000000000000000000000007C000000000000000000000000000000000000007D0000000000000000000000000000000000007E0000000000000000000000000000000000007F0000000000000000000000000000000000008000000000000000000000000000000000000081000000000000000000000000000000000000008200000000000000000000000000000000000083000000000000000000000000000000000000 147 | #01811:190000000000002C2C0000000000002C2C000000000000002C00000000000000 148 | #01812:2C000000000000000000000000000000000000000000002E000000000000002E 149 | #01813:0000000000313100000000000031310000000000000030000000000000003000 150 | #01814:0000000000000000000000000000000000000000003100000000000000310000 151 | #01815:0000003434000000000000343400000000000000330000000000000033000000 152 | #01818:2000000000000000000000000000000000000034000000000000003400000000 153 | #01819:0038380000000000003838000000000000003600000000000000360000000000 154 | #01816:0000000000000000000000000000000000380000000000000038000000000000 155 | 156 | #01903:840000000000000000000000000000000000008500000000000000000000000000000000000086000000000000000000000000000000000000008700000000000000000000000000000000000088000000000000000000000000000000000000890000000000000000000000000000000000008A0000000000000000000000000000000000008B000000000000000000000000000000000000008C0000000000000000000000000000000000008D000000000000000000000000000000000000 157 | #01914:0028000000280000002800000028000025250025252500252525000025000000 158 | #01915:2C2C2C2C2C2C2C2C0000000000000000 159 | #01918:00000000000000002727272727000000 160 | #01919:0000003100000031000000310000003100000000000000000000000000000000 161 | 162 | #02003:8E0000000000000000000000000000000000008F00000000000000000000000000000000000090000000000000000000000000000000000000009100000000000000000000000000000000000092000000000000000000000000000000000000930000000000000000000000000000000000009400000000000000000000000000000000000095000000000000000000000000000000000000009600000000000000000000000000000000000097000000000000000000000000000000000000 163 | #02013:0000000023232323 164 | #02014:2525252500000000 165 | #02015:2626262600000000 166 | #02018:0000000029292929 167 | 168 | #02103:98000000000000000000000000000000000000990000000000000000000000000000000000009A000000000000000000000000000000000000009B0000000000000000000000000000000000009C0000000000000000000000000000000000009D0000000000000000000000000000000000009E0000000000000000000000000000000000009F00000000000000000000000000000000000000A0000000000000000000000000000000000000A1000000000000000000000000000000000000 169 | #02111:001E1E1E1E1E1E1E1E1E1E1E1E1E1E1E 170 | #02112:2121212100000000 171 | #02114:0000000023232323 172 | #02119:2A 173 | 174 | #02203:A2000000000000000000000000000000000000A3000000000000000000000000000000000000A400000000000000000000000000000000000000A5000000000000000000000000000000000000A6000000000000000000000000000000000000A7000000000000000000000000000000000000A8000000000000000000000000000000000000A900000000000000000000000000000000000000AA000000000000000000000000000000000000AB000000000000000000000000000000000000 175 | #02211:1E1E1E1E1E1E1E1E1E1E1E1E1E1E1E1E 176 | #02215:2525252500000000 177 | #02218:0000000026262626 178 | #02219:2A 179 | 180 | #02303:AC000000000000000000000000000000000000AD000000000000000000000000000000000000AE00000000000000000000000000000000000000AF000000000000000000000000000000000000B000000000000000ABA29990877E0075006C0063005A005100480000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 181 | #02311:1E1E1E1E1E1E1E1E1D00000000000000 182 | #02313:0020 183 | #02314:0000000000000000000000000000000000002929292929292900290029002900 184 | #02318:2600000026000000260000002600000000002C2C2C2C2C2C2C002C002C002C00 185 | 186 | #02411:00210021002100210000000000000000 187 | #02413:0000250000002500000025000000250026002626262626262600260026002600 188 | #02415:2A0000002A0000002A0000002A0000002A002A2A2A2A2A2A2A002A002A002A00 189 | #02419:2D2D2D2D00000000 190 | 191 | #02512:00230023002300230000000000000000 192 | #02513:0000000000000000000000000000000025002525252525252500250025002500 193 | #02514:00260026002600260000000000000000 194 | #02515:2800000028000000280000002800000028002828282828282800280028002800 195 | #02519:2C2C2C2C00000000 196 | 197 | #02611:00210021002100210000000000000000 198 | #02612:0000000000000000000000000000000023002323232323232300230023002300 199 | #02613:00250025002500250000000000000000 200 | #02614:2600000026000000260000002600000026002626262626262600260026002600 201 | #02618:2A2A2A2A00000000 202 | 203 | #02711:00200020002000201E001E001D001D00 204 | #02712:0000230000002300000023000000230000290000000029000000000000000000 205 | #02713:250000002500000025000000250000002A002A002A00002A002A000000002A00 206 | #02714:000000000000000000000000000000000000002C002C00002C002C002C00002C 207 | #02715:2900000029000000290000002900000000000000000000000000002D002D0000 208 | 209 | #02811:1E1E000000000000 210 | #02812:0000202000000000 211 | #02813:0000000021212121 212 | #02814:002C000000002C00000000000000000000000000002D0000002D0000002D0000 213 | #02815:2D002D002D00002D002D000000002D000000000000002F0000002F0000002F00 214 | #02818:0000002F002F00002F002F002F00002F00000000310000310000003100000031 215 | #02819:0000000000000000000000310031000031000000000000003200000032000000 216 | 217 | #02911:0000000021212121 218 | #02913:212121212A002A00 219 | #02914:00000031000000310000003100000031002C000031000000002C000031000000 220 | #02915:0000320000003200000032000000320000002D000032000000002D0000320000 221 | #02918:003400000034000000340000003400000000002F000034000000002F00003400 222 | #02919:3600000036000000360000003600000000000000000000360000000000000036 223 | 224 | #03012:0000202020202020 225 | #03013:2323000000000000 226 | #03014:00000000002F0000002F0000002F00000000002F0000002F0000002F0000002F 227 | #03015:00000031003100310031003100310031 228 | #03018:3200000032000032000000320000003200320000003200000032000000320000 229 | #03019:0000343434343434 230 | 231 | #03112:2020202000001E1E 232 | #03113:2800280021210000 233 | #03114:002A00002F000000002A00002F00000000000000002D0000002D0000002D0000 234 | #03115:00002C000031000000002C00003100000000000000002F0000002F0000002F00 235 | #03118:0000002D000032000000002D0000320031000000310000310000003100000031 236 | #03119:0000000000000034000000000000003400000000000000003200000032000000 237 | 238 | #03211:0000000000001B1A 239 | #03212:1E0000001E0000001E0000001E0000001D0000001C0000002A00002A0000002A 240 | #03213:000000000000000000000000000000002C00002C0000002C00002C0000002C00 241 | #03214:0000002D0000002D0000002D0000002D00002D0000002D00002D0000002D0000 242 | #03215:00002F0000002F0000002F0000002F00002F0000002F0000000000002F000000 243 | #03218:0031000000310000003100000031000000000000310000000000000000000000 244 | #03219:3232323200000000 245 | 246 | #03311:1919191919191919 247 | #03312:0020000000200000 248 | #03313:00230023000000000023002300000000 249 | #03314:0025002500250025000000000000000000250025002500250000000000000000 250 | #03315:29000000292A002A29000000292A002A 251 | #03318:0000000000000000002C002C002C002C0000000000000000002C002C002C002C 252 | #03319:0000002F0000002F 253 | 254 | #03403:48 255 | #03411:191919191C000000 256 | #03412:0000000000000000000000001D0000001D1D1D1D1D1D1D1D1D1D1D1D1D1D1D1D 257 | #03413:000000002300001E0000000000000000 258 | #03414:2900000029000000000025000020002000000000000000000000000000000000 259 | #03415:0000000000002A00002600260000000023232323232323232323232323232323 260 | #03418:00002D00002C002C000000000000000000000000000000000000000000000000 261 | #03419:002F002F00000000000000000000000000000000000000000000000000000000 262 | 263 | #03513:1E 264 | #03514:2100000021000000000000002100000028282828282828282828282828282828 265 | #03515:000023000000230000002300000023002E2E2E2E2E2E2E2E2E2E2E2E2E2E2E2E 266 | #03518:0025002500250025002500250025002500000000000000000000000000000000 267 | #03519:002A0000 268 | 269 | #03611:00230000 270 | #03612:00250025002500250000000000000000 271 | #03613:2727002700270027002700270027002700000000000000000000000000000000 272 | #03615:000000002A000000000000002A0000002A2A2A2A2A2A2A2A2A2A2A2A2A2A2A2A 273 | #03618:2F00000000000000000000000000000030303030303030303030303030303030 274 | 275 | #03712:0000000000000000000000000000002500000000000000000000000000000000 276 | #03713:0000270000002700000027000000270021212121212121212121212121212121 277 | #03714:2828002800280028002800280028000027272727272727272727272727272727 278 | #03718:002C000000000000 279 | #03719:3100312C00000000 280 | 281 | #03811:00001E0000001E0000001E0000001E001E1E1E1E1E1E1E1E1E1E1E1E1E1E1E1E 282 | #03812:2020002000200020002000200020002000000000000000000000000000000000 283 | #03815:2800000028000000280000002800000028282828282828282828282828282828 284 | 285 | #03902:0.5 286 | #03911:1E1E1E1E1E1E1E1E1E1E1E1E1E1E1E1E 287 | #03914:27272727272727272727272727272727 288 | 289 | #04002:0.125 290 | #04003:09 291 | #04012:20 292 | #04015:28 293 | 294 | #04102:0.5 295 | #04103:48D8000049D900004ADA00004BDB0000 296 | #04113:21212121 297 | 298 | #04203:4CDC00004DDD00004EDE00004FDF000050E0000051E1000052E2000053E30000 299 | #04213:2121212121212121 300 | #04214:2323232323232323 301 | 302 | #04303:54E4000055E5000056E6000057E7000058E8000059E900005AEA00005BEB0000 303 | #04312:2020202020202020 304 | #04314:2323232300000000 305 | #04315:00000000000000002626262626262626 306 | 307 | #04403:5CEC00005DED00005EEE00005FEF000060F0000061F1000062F2000063F30000 308 | #04411:1C1C1C1C00000000 309 | #04413:0000000022222222 310 | #04414:2525252500000000 311 | #04418:00000000000000002828282828282828 312 | 313 | #04503:64F4000065F5000066F6000067F7000068F8000069F900006AFA00006BFB0000 314 | #04511:1E1E1E1E00000000 315 | #04515:272727272A2A2A2A 316 | #04519:00000000000000002D2D2D2D2D2D2D2D 317 | 318 | #04603:6C0000000000FC00000000000000000000000000000000006D0000000000FD00000000000000000000000000000000006E0000000000FE00000000000000000000000000000000006F0000000000FF0000000000000000000000000000000000710000000000FF0000000000000000000000000000000000730000000000FF00000000000000000000000000000000007500000000FF0000000000000000000000000000000000007700000000FF000000000000000000000000000000000000 319 | #04612:0000001C 320 | #04613:0020 321 | #04614:00000000000000282800000000000000 322 | #04615:2C000000002C2C0000002C0000000000 323 | #04618:0000002F2F000000000000002F000000 324 | #04619:00343400000000000000000000003200 325 | 326 | #04703:7900FF0000000000000000007B00FF0000000000000000007D00FF0000000000000000007F00FF0000000000000000008100FF0000000000000000008300FF0000000000000000008500FF000000000000000000870000FF0000000000000000 327 | #04711:15 328 | #04712:0000001E 329 | #04713:0021 330 | #04714:000000000000002A2A00000000000000 331 | #04715:00000000002E2E0000002E0000000000 332 | #04718:31000031310000000000000031000000 333 | #04719:00363600000000000000000000003400 334 | 335 | #04803:890000FF00000000000000000000000000000000000000008B0000FF00000000000000000000000000000000000000008D0000FF00000000000000000000000000000000000000008F0000FF0000000000000000000000000000000000000000910000FF0000000000000000000000000000000000000000930000FF0000000000000000000000000000000000000000950000FF0000000000000000000000000000000000000000970000FF0000000000000000000000000000000000000912 336 | #04811:17 337 | #04812:00000020 338 | #04813:0023 339 | #04814:000000000000002C2C00000000000000 340 | #04815:00000000003030000000300000000000 341 | #04818:33000033330000000000000033000000 342 | #04819:00383800000000000000000000003600 343 | 344 | #04902:0.5 345 | #04903:24 346 | #04911:1900000000000000000000000000000000000000000000000000000000310000 347 | #04912:3100000000000000000000000000000000000000000000000000000031330000 348 | #04913:3431000000000000000000000000000000000000000000000000003133000000 349 | #04914:0034310000000000000000000000000000000000000000000000313300000000 350 | #04915:0000343100000000000000000000000000000000000000000031330000003131 351 | #04918:0000003431313131313131313131313131313131313131313133000000000000 352 | #04919:0000000034343434343434343434343433333333333333333300000000003333 353 | 354 | #05002:0.5 355 | #05011:000000000000000000000000000000000000000000000000000000000000002F 356 | #05012:2F00000000000000000000000000000000000000000000000000000000002F38 357 | #05013:312F2F00000000000000000000000000000000000000000000000000002F3800 358 | #05014:0031312F2F2F2F2F2F2F2F2F2F2F2F2F0000000000000000000000002F380000 359 | #05015:0000003131313131313131313131313100000000000000000000002F38000000 360 | #05018:000000000000000000000000000000002F2F2F2F2F2F2F2F2F2F2F3800000000 361 | #05019:0000000000000000000000000000000038383838383838383838380000000000 362 | 363 | #05102:0.5 364 | #05112:2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D 365 | #05113:0000000000000000000000000000000000000000000000000000000036363636 366 | #05114:0000000000000000000000000000000000000000003636363636363600000000 367 | #05115:0000000000000000000000000000383836363636360000000000000000000000 368 | #05118:0000000000000038383838383838000000000000000000000000000000000000 369 | #05119:3838383838383800000000000000000000000000000000000000000000000000 370 | 371 | #05202:0.5 372 | #05211:2C2C2C2C2C2C2C2C2C2C2C2C2C2C2C2C31313131313131313131313131313131 373 | #05212:0000000000000000000000000000000034340000000000000000000000000000 374 | #05213:0000000000000000000000000000363600003434000000000000000000000000 375 | #05214:0000000000000000000000003636000000000000343434340000000000000000 376 | #05215:0000000000000000363636360000000000000000000000003434343400000000 377 | #05218:0000000036363636000000000000000000000000000000000000000034343434 378 | #05219:3636363600000000000000000000000000000000000000000000000000000000 379 | 380 | #05302:0.5 381 | #05312:31313131313131312F2F2F2F2F2F2F2F2F2F2F2F2F2F2F2F2D2D2D2D2D2D2D2D 382 | #05313:0000000000000000000000000000000000000000000000000000000031313131 383 | #05314:0000000000000000000000000000000000000000003131313131313100000000 384 | #05315:0000000000000000000000000000323231313131310000000000000000000000 385 | #05318:0000000000000032323232323232000000000000000000000000000000000000 386 | #05319:3232323232323200000000000000000000000000000000000000000000000000 387 | 388 | #05402:0.5 389 | #05412:0000000000000000000000000000000000000000000000002A2A2A2A2A2A2A2A 390 | #05413:000000000000000000000000000000002C2C2C2C2C2C2C2C0000000000000000 391 | #05414:00000000000000002C2C2C2C2C2C2C2C00000000000000000000000000000000 392 | #05415:2D2D2D2D2D2D2D2D000000000000000000000000000000000000000000000000 393 | #05418:2F2F2F2F2F2F2F2F2F2F2F2F2F2F2F2F00000000000000000000000000000000 394 | #05419:000000000000000000000000000000002D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D 395 | 396 | #05502:0.5 397 | #05512:002A2A2A2A2A2A2A000000000000000000000000000000000000000000000000 398 | #05513:2A 399 | #05514:2C00000000000000282828282828282828282828282828282626262626262626 400 | #05515:002C2C2C2C2C2C2C2C2C2C2C2C2C2C2C2A2A2A2A2A2A2A2A2A2A2A2A2A2A2A2A 401 | 402 | #05602:0.5 403 | #05603:12 404 | #05611:25252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525252525 405 | #05613:29292929292929290000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 406 | #05614:00000000000000002A2A2A2A2A2A2A2A00000000000000000000000000000000000000000000000000000000000000000000000000000000002A2A2A2A2A2A2A 407 | #05615:000000000000000000000000000000002C2C2C2C2C2C2C2C0000000000000000000000000000000000000000000000002C2C2C2C2C2C2C2C2C00000000000000 408 | #05618:0000000000000000000000000000000000000000000000002D2D2D2D2D2D2D2D00000000000000002D2D2D2D2D2D2D2D00000000000000000000000000000000 409 | #05619:00000000000000000000000000000000000000000000000000000000000000002F2F2F2F2F2F2F2F000000000000000000000000000000000000000000000000 410 | 411 | #05703:48 412 | #05711:25 413 | #05712:20000000000000000025002500250025 414 | #05713:2900292929292929290029002900290000000000000000000000000000000000 415 | #05715:00002C2C2C2C2C2C2C002C002C002C002A0000002A0000002A0000002A000000 416 | #05718:000000002D2D2D2D 417 | 418 | #05811:00000000000000000023002300230023 419 | #05813:2600262626262626260026002600260000002600000026000000260000002600 420 | #05814:0000000028282828 421 | #05815:2A002A2A2A2A2A2A2A002A002A002A0000000000000000000000000000000000 422 | #05818:000000002C2C2C2C 423 | 424 | #05911:00000000000000000021002100210021 425 | #05912:2500252525252525250025002500250000002500000025000000250000002500 426 | #05913:0000000026262626 427 | #05914:2800282828282828280028002800280000000000000000000000000000000000 428 | #05915:000000002A2A2A2A 429 | 430 | #06011:0000000025252525 431 | #06012:2300232323232323230023002300230000000000000000000000000000000000 432 | #06013:2600262626262626260026002600260029290029292900292929002929290029 433 | #06015:00000000000000000000000000000000002C2C2C002C2C2C002C2C2C002C2C2C 434 | #06019:0000000000000000002F002F002F002F 435 | 436 | #06114:2A002A2A2A2A2A2A2A002A002A002A002A002A2A2A2A2A2A2A002A002A002A00 437 | #06118:000000000000000000000000000000002C002C2C2C2C2C2C2C002C002C002C00 438 | #06119:2D002D2D2D2D2D2D2D002D002D002D0000000000000000000000000000000000 439 | 440 | #06212:0000000000000000000000000000000000001E1E1E1E1E1E1E001E001E001E00 441 | #06213:2900292929292929290029002900290000000000000000000000000000000000 442 | #06214:0000000000000000000000000000000000002121212121212100210021002100 443 | #06215:002A 444 | #06218:2C002C2C2C2C2C2C2C002C002C002C0000000000000000000000000000000000 445 | 446 | #06303:000000000000000000000000000000000000000000000000000000004600440042003E003900350030002B00260020001A00000014000F000A00050001FF01FF 447 | #06311:000000000000000000000000000000001D001D1D1D1D1D1D1D001D001D001D00 448 | #06312:1E001E1E1E1E1E1E1E001E001E001E0000000000000000000000000000000000 449 | #06313:2000202020202020200020002000200020002020202020202000200020002000 450 | 451 | #06402:2 452 | #06412:1E 453 | 454 | #06502:0.25 455 | #06511:00000000000000000000003700370037 456 | #06512:00000000003700370037000000000000 457 | #06513:00370037000000000000000000000000 458 | #06514:37 459 | #06515:0037370000000000 460 | #06518:0000003737370000 461 | #06519:0000000000003737 462 | 463 | --------------------------------------------------------------------------------