Skip to content

Commit

Permalink
Merge pull request #214 from bemusic/feature/moar-stuff
Browse files Browse the repository at this point in the history
Feature/moar stuff
  • Loading branch information
dtinth committed Nov 24, 2015
2 parents 6f56c79 + 2ab2073 commit 8d116e3
Show file tree
Hide file tree
Showing 24 changed files with 198 additions and 26 deletions.
1 change: 1 addition & 0 deletions .babelrc
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
"env": {
"development": {
"plugins": [
"react-display-name",
"react-transform"
],
"extra": {
Expand Down
11 changes: 10 additions & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,21 @@ env:
- secure: "vWu/FG20Uc1JgcUmzH467bZ82BKLuRU0wfKnA6f4CGfOIHsiAL62MDA3KRqfsRpsss9JTyERQ2zm7iLEVXu4dOcKody9AsrZRWtC+wvuKBoAllUt6jZnNOgs7Hjac50TI68E0p4Hrz+0xqV+YlppU5I34mGlZ2EhmSTnZ0zdIgw="
- BROWSER=firefox
- NODE_ENV=test
- CXX=g++-4.8
addons:
apt:
sources:
- ubuntu-toolchain-r-test
packages:
- g++-4.8
node_js:
- "5"
before_install:
- "export DISPLAY=:99.0"
- "sh -e /etc/init.d/xvfb start"
- 'npm install -g gulp'
- "npm install -g gulp"
before_script:
- "npm install"
script:
- npm run ci:code-review
- BEMUSE_COV=true bash -c 'gulp build || gulp build || gulp build'
Expand Down
Binary file modified public/skins/default/Tutorial/Page6.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
7 changes: 6 additions & 1 deletion spec/game/state/player-state_spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,11 @@ describe('PlayerState', function () {
expect(state.getNoteStatus(note)).to.equal('judged')
expect(state.getNoteJudgment(note)).to.equal(-1)
})
it('does not end automatically', function () {
advance(2, { 'p1_1': 1 })
advance(3.1, { 'p1_1': 1 })
expect(state.getNoteStatus(note)).to.equal('active')
})
it('judges long note lifted too slow as missed', function () {
advance(2, { 'p1_1': 1 })
advance(4, { 'p1_1': 1 })
Expand All @@ -191,7 +196,7 @@ describe('PlayerState', function () {
expect(state.getNoteStatus(note)).to.equal('active')
expect(state.getNoteJudgment(note)).to.equal(1)
expect(state.notifications.judgments[0].judgment).to.equal(1)
advance(4, { 'p1_SC': 1 })
advance(3.1, { 'p1_SC': 1 })
expect(state.getNoteStatus(note)).to.equal('judged')
expect(state.getNoteJudgment(note)).to.equal(1)
expect(state.notifications.judgments[0].judgment).to.equal(1)
Expand Down
1 change: 1 addition & 0 deletions spec/resources/fixtures/f/meow.dat
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
aaa
1 change: 1 addition & 0 deletions spec/resources/fixtures/f/meow.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
hi
19 changes: 19 additions & 0 deletions spec/resources/resources_spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,25 @@ describe('BemusePackageResources', function () {
})
})

it('supports fallback', function () {
resources = new BemusePackageResources('/spec/resources/fixtures/a/', {
fallback: '/spec/resources/fixtures/f/',
fallbackPattern: /\.txt$/,
})
return resources.file('meow.txt')
.then(file => file.read())
.then(buffer => new Uint8Array(buffer))
.then(array => {
expect([array[0], array[1]]).to.deep.equal([0x68, 0x69])
})
})
it('supports fallback only with the pattern', function () {
resources = new BemusePackageResources('/spec/resources/fixtures/a/', {
fallback: '/spec/resources/fixtures/f/',
fallbackPattern: /\.txt$/,
})
return expect(resources.file('meow.dat')).to.be.rejected
})
})

})
11 changes: 8 additions & 3 deletions src/app/game-launcher.js
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,10 @@ export function launch ({ server, song, chart }) {
let url = server.url + '/' + song.path + '/' + encodeURIComponent(chart.file)
let assetsUrl = resolve(url, 'assets/')
loadSpec.bms = new URLResource(url)
loadSpec.assets = new BemusePackageResources(assetsUrl)
loadSpec.assets = new BemusePackageResources(assetsUrl, {
fallback: url,
fallbackPattern: /\.(?:png|jpg)/,
})
}

let latency = +query.latency || (+options['system.offset.audio-input'] / 1000) || 0
Expand All @@ -68,12 +71,14 @@ export function launch ({ server, song, chart }) {
}

// start loading the game
let { tasks, promise } = GameLoader.load(loadSpec)
let loader = GameLoader.load(loadSpec)
let { tasks, promise } = loader

// display loading scene
let loadingScene = React.createElement(LoadingScene, {
tasks: tasks,
song: chart.info,
song: chart.info,
eyecatchImagePromise: loader.get('EyecatchImage')
})
yield SCENE_MANAGER.push(loadingScene)

Expand Down
2 changes: 1 addition & 1 deletion src/app/ui/MusicList.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import MusicListItem from './MusicListItem.jsx'
export default React.createClass({
mixins: [React.addons.PureRenderMixin],
render () {
return <ul className="MusicList"
return <ul className="MusicList js-scrollable-view"
onTouchStart={this.props.onTouch}>
{this.props.groups.map(({ title, songs }) => [
<li className="MusicListのgroupTitle">{title}</li>,
Expand Down
6 changes: 5 additions & 1 deletion src/app/ui/MusicListItem.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,12 @@ export default React.createClass({
mixins: [React.addons.PureRenderMixin],
render () {
const song = this.props.song
const className = c('MusicListItem', {
'is-active': this.props.selected,
'js-active-song': this.props.selected,
})
return <li
className={c('MusicListItem', { 'is-active': this.props.selected })}
className={className}
onClick={this.handleClick}>
{
song.tutorial
Expand Down
19 changes: 19 additions & 0 deletions src/app/ui/MusicSelectScene.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import './MusicSelectScene.scss'

import React from 'react'
import c from 'classnames'
import $ from 'jquery'
import { connect } from 'bemuse/flux'
import SCENE_MANAGER from 'bemuse/scene-manager'
import online from 'bemuse/online/instance'
Expand Down Expand Up @@ -165,6 +166,24 @@ export const MusicSelectScene = React.createClass({
authenticationPopupVisible: false,
}
},
componentDidMount () {
this.ensureSelectedSongInView()
},
ensureSelectedSongInView () {
const $this = $(React.findDOMNode(this))
const active = $this.find('.js-active-song')[0]
if (!active) return
const scroller = $(active).closest('.js-scrollable-view')[0]
if (!scroller) return
const scrollerRect = scroller.getBoundingClientRect()
const activeRect = active.getBoundingClientRect()
if (activeRect.bottom > scrollerRect.bottom || activeRect.top < scrollerRect.top) {
scroller.scrollTop += (
(activeRect.top + activeRect.height / 2) -
(scrollerRect.top + scrollerRect.height / 2)
)
}
},
handleSongSelect (song, chart) {
Actions.selectSong(song)
if (chart) Actions.selectChart(chart)
Expand Down
12 changes: 11 additions & 1 deletion src/app/ui/ResultScene.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import ResultTable from './ResultTable'
import ResultGrade from './ResultGrade'
import RankingContainer from './RankingContainer'
import Flex from 'bemuse/ui/Flex'
import * as QueryFlags from '../query-flags'

export default React.createClass({
render () {
Expand Down Expand Up @@ -58,14 +59,23 @@ export default React.createClass({
let title = this.props.chart.info.title
let subtitle = this.props.chart.info.subtitles[0] || ''
let score = this.props.result.score
let grade = this.props.result.grade
if (subtitle === '') {
let match = this.props.chart.info.genre.match(/\[([^\]]+)\]$/)
if (match) subtitle = match[1]
}
subtitle = subtitle.trim()
if (subtitle !== '' && !/^[\[\(]/.test(subtitle)) subtitle = `[${subtitle}]`
if (subtitle !== '') subtitle = ` ${subtitle}`
let text = `Played:「 ${title}${subtitle} 」on #Bemuse (Score:${score})` + '\n' + `→ https://bemuse.ninja/`
let url = 'https://bemuse.ninja/'
let server = QueryFlags.getMusicServer()
if (server) {
url = (
(/^http:/.test(server) ? 'http' : 'https') +
'://bemuse.ninja/?server=' + encodeURIComponent(server)
)
}
let text = `Played:「 ${title}${subtitle} 」on #Bemuse (Score:${score} [${grade}])` + '\n' + `→ ${url}`
return 'https://twitter.com/intent/tweet?related=bemusegame&text=' + encodeURIComponent(text)
},
onTweet (e) {
Expand Down
4 changes: 4 additions & 0 deletions src/bmson/notechart-loader.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,10 @@ export function load (source, options) {
]),
spacing: new BMS.Spacing([ ]),
barLines,
images: { // HACK: Hardcoded here, probably should belong in bmson package
eyecatch: data.info.eyecatch_image,
background: data.info.back_image,
}
}

return new Notechart(stuff, options)
Expand Down
9 changes: 9 additions & 0 deletions src/game/display/game-display.scss
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,15 @@
animation-fill-mode: both;
}

&--bg img {
width: 100%;
height: 100%;
display: block;
object-fit: cover;
animation: 1.2s game-display--bg-default;
animation-fill-mode: both;
}

> canvas {
position: relative;
z-index: 100;
Expand Down
13 changes: 9 additions & 4 deletions src/game/display/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,13 @@ import './game-display.scss'
import $ from 'jquery'

export class GameDisplay {
constructor ({ game, context }) {
constructor ({ game, context, backgroundImagePromise }) {
this._game = game
this._context = context
this._players = new Map(game.players.map(player =>
[player, new PlayerDisplay(player)]))
this._stateful = { }
this._wrapper = this._createWrapper()
this._wrapper = this._createWrapper({ backgroundImagePromise })
}
start () {
this._started = new Date().getTime()
Expand Down Expand Up @@ -63,10 +63,15 @@ export class GameDisplay {
let f = gameState.readyFraction
return f > 0.5 ? Math.pow(1 - (f - 0.5) / 0.5, 2) : 0
}
_createWrapper () {
_createWrapper ({ backgroundImagePromise }) {
var $wrapper = $('<div class="game-display"></div>')
.append('<div class="game-display--bg"></div>')
.append('<div class="game-display--bg js-back-image"></div>')
.append(this.view)
if (backgroundImagePromise) {
Promise.resolve(backgroundImagePromise).then(
image => $wrapper.find('.js-back-image').append(image)
)
}
return $wrapper[0]
}
get context () {
Expand Down
16 changes: 13 additions & 3 deletions src/game/loaders/game-loader.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,11 @@ import Progress from 'bemuse/progress'
import SamplingMaster from 'bemuse/sampling-master'
import LoadingContext from 'bemuse/boot/loading-context'

import * as Multitasker from './multitasker'
import SamplesLoader from './samples-loader'
import NotechartLoader from './notechart-loader'
import loadImage from './loadImage'

import * as Multitasker from './multitasker'
import Game from '../game'
import GameController from '../game-controller'
import GameAudio from '../audio'
Expand Down Expand Up @@ -60,6 +61,16 @@ export function load (spec) {
return yield loader.load(arraybuffer, spec.bms, spec.options.players[0])
}))

task('EyecatchImage', null, ['Notechart'],
function (notechart) {
return loadImage(assets, notechart.eyecatchImage)
})

task('BackgroundImage', null, ['Notechart'],
function (notechart) {
return loadImage(assets, notechart.backgroundImage)
})

let audioLoadProgress = new Progress()
let audioDecodeProgress = new Progress()

Expand All @@ -77,7 +88,7 @@ export function load (spec) {

task('GameDisplay', null, ['Game', 'Skin', 'SkinContext'],
function (game, skin, context) {
return new GameDisplay({ game, skin, context })
return new GameDisplay({ game, skin, context, backgroundImagePromise: run('BackgroundImage') })
})

task('Samples', null, ['SamplingMaster', 'Game'],
Expand All @@ -100,5 +111,4 @@ export function load (spec) {
return run('GameController')

})

}
16 changes: 16 additions & 0 deletions src/game/loaders/loadImage.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@

export function loadImage (assets, filename) {
return (assets.file(filename)
.then(asset => asset.read())
.then(arrayBuffer => new Blob([arrayBuffer]))
.then(blob => URL.createObjectURL(blob))
.then(src => new Promise((resolve, reject) => {
const image = new Image()
image.onload = () => resolve(image)
image.onerror = reject
image.src = src
}))
)
}

export default loadImage
2 changes: 1 addition & 1 deletion src/game/loaders/multitasker.js
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,6 @@ export function start (callback) {
taskToWatch.progress.watch(update)
}

return { tasks: status, promise: promise }
return { tasks: status, promise: promise, get: run }

}
12 changes: 12 additions & 0 deletions src/game/notechart.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ export class Notechart {
positioning,
spacing,
barLines,
images,
} = data

invariant(bmsNotes, 'Expected "data.notes"')
Expand All @@ -43,6 +44,7 @@ export class Notechart {
this._infos = new Map(this._notes.map(
note => [note, this._getNoteInfo(note)]))
this._songInfo = songInfo
this._images = images
}

// An Array of note events.
Expand Down Expand Up @@ -85,6 +87,16 @@ export class Notechart {
return this._songInfo
}

// Eyecatch image
get eyecatchImage () {
return (this._images && this._images.eyecatch) || 'eyecatch_image.png'
}

// Background image
get backgroundImage () {
return (this._images && this._images.background) || 'back_image.png'
}

// Returns the characteristic of the note as an Object.
// The returned object includes the following properties:
//
Expand Down
4 changes: 3 additions & 1 deletion src/game/state/player-state.js
Original file line number Diff line number Diff line change
Expand Up @@ -171,7 +171,9 @@ export class PlayerState {
let judgment = judgeEndTime(this._gameTime, note.end.time)
let missed = judgment === MISSED
let lifted = control.changed
return missed || lifted
let scratch = note.column === 'SC'
let passed = this._gameTime >= note.end.time
return missed || lifted || (scratch && passed)
} else {
return false
}
Expand Down
10 changes: 10 additions & 0 deletions src/game/ui/LoadingScene.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ export default React.createClass({

render () {
return <Scene className="LoadingScene" ref="scene">
<div className="LoadingSceneのimage" ref="eyecatch"></div>
<div className="LoadingSceneのinfo">
<LoadingSceneSongInfo song={this.props.song} />
</div>
Expand All @@ -19,6 +20,15 @@ export default React.createClass({
<div className="LoadingSceneのcover"></div>
</Scene>
},

componentDidMount () {
if (this.props.eyecatchImagePromise) {
this.props.eyecatchImagePromise.then(image => {
React.findDOMNode(this.refs.eyecatch).appendChild(image)
})
}
},

teardown () {
React.findDOMNode(this.refs.scene).classList.add('is-exiting')
return Promise.delay(500)
Expand Down
Loading

0 comments on commit 8d116e3

Please sign in to comment.