Skip to content

Instantly share code, notes, and snippets.

@thrakt
Last active May 5, 2017 13:44
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save thrakt/9f9d383e7cb66812f49a734e73a0e54f to your computer and use it in GitHub Desktop.
Save thrakt/9f9d383e7cb66812f49a734e73a0e54f to your computer and use it in GitHub Desktop.
FGO Activity Predictor
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
<title>FGO Activity Predictor</title>
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u"
crossorigin="anonymous">
<script src="https://unpkg.com/vue"></script>
<script src="https://unpkg.com/vuex"></script>
<script type="text/x-template" id="ap-input-template">
<div class="col-md-2 col-xs-6">
<label>
<p>{{title}}</p>
<input v-if="type === 'number'" ref="input" class="form-control" type="number" v-bind:value="value" v-on:input="update($event.target.value)" />
<input v-else-if="type === 'time'" class="form-control" type="time" v-bind:value="value" v-on:input="update($event.target.value)" />
</label>
</div>
</script>
<script type="text/x-template" id="main-template">
<div class="container-fluid">
<div class="row">
<div class="col-md-12 col-xs-12">
<h5>
FGO Activity Prediction
</h5>
</div>
</div>
<div class="row">
<ap-input title="現在AP" type="number" name="currentPoint" v-model="currentPoint" />
<ap-input title="最大AP" type="number" name="maxPoint" v-model="maxPoint" />
<ap-input title="周回AP" type="number" name="intervalPoint" v-model="intervalPoint" />
<ap-input title="次回開始時刻" type="time" name="targetTime" v-model="targetTime" />
</div>
<button v-if="currentPoint && intervalPoint && currentPoint > intervalPoint" class="btn btn-default btn-sm" v-on:click="goAround">周回</button>
<hr />
<div class="row">
<div class="col-md-2 col-xs-6">
<p class="lead">経済AP</p>
<p><strong>{{efficientPoint}}</strong></p>
<p class="lead">最大AP到達時刻</p>
<p><strong>{{maxPointTime}}</strong> 到達</p>
</div>
<div class="col-md-2 col-xs-6">
<p class="lead">周回経済AP</p>
<div v-for="e in efficientIntervals">
<p>
<strong v-if="e.efficiently">{{e.point}} {{e.time}} 到達</strong>
<span v-else>{{e.point}} {{e.time}} 到達</span>
</p>
</div>
</div>
</div>
</div>
</script>
<script>
var app, store
window.addEventListener('load', () => {
const toNumber = s => s ? Number(s) : null
store = new Vuex.Store({
state: {
currentPoint: toNumber(localStorage.getItem('currentPoint')),
maxPoint: toNumber(localStorage.getItem('maxPoint')),
intervalPoint: toNumber(localStorage.getItem('intervalPoint')),
targetTime: localStorage.getItem('targetTime'),
currentTime: new Date(toNumber(localStorage.getItem('currentTime')) || new Date())
},
mutations: {
_updateState(state, payload) {
state[payload.name] = payload.value
}
},
actions: {
updateCurrentPoint({ commit, dispatch }, { value, updateAt }) {
setTimeout(() => dispatch('recalcCurrentPoint'), 1000 * 60 * 5 + 100)
const point = Number(value) > 0 ? Number(value) : null
const date = updateAt || new Date()
commit('_updateState', { name: 'currentPoint', value: point })
commit('_updateState', { name: 'currentTime', value: date })
localStorage.setItem('currentPoint', point)
localStorage.setItem('currentTime', date.getTime())
},
recalcCurrentPoint({ state, dispatch }) {
const pointDelta = Math.floor((Date.now() - state.currentTime.getTime()) / (1000 * 60) / 5)
if (pointDelta > 0 && state.currentPoint < state.maxPoint) {
const updateAt = new Date(state.currentTime.getTime() + pointDelta * 5 * 1000 * 60)
dispatch('updateCurrentPoint', { value: state.currentPoint + pointDelta, updateAt: updateAt })
}
},
updateMaxPoint({ commit }, { value }) {
const point = Number(value) > 0 ? Number(value) : null
commit('_updateState', { name: 'maxPoint', value: point })
localStorage.setItem('maxPoint', point)
},
updateIntervalPoint({ commit }, { value }) {
const point = Number(value) > 0 ? Number(value) : null
commit('_updateState', { name: 'intervalPoint', value: point })
localStorage.setItem('intervalPoint', point)
},
updateTargetTime({ commit }, { value }) {
if (!value.match(/^[0-9]{2}:[0-9]{2}$/g)) {
return
}
commit('_updateState', { name: 'targetTime', value: value })
localStorage.setItem('targetTime', value)
},
goAround({ state, dispatch }) {
if (!(state.currentPoint && state.intervalPoint)){
return
}
dispatch('updateCurrentPoint', { value: state.currentPoint - state.intervalPoint, updateAt: state.currentTime })
}
},
getters: {
timeDifference: function (state) {
if (!state.targetTime) {
return null
}
let date = new Date()
date.setHours(state.targetTime.substring(0, 2), state.targetTime.substring(3, 5), 0)
if (date < state.currentTime) {
date.setDate(date.getDate() + 1)
}
const d = date.getTime() - state.currentTime.getTime()
return Math.floor(d / 1000 / 60)
},
efficientPoint: function (state, getter) {
if (!(state.maxPoint && getter.timeDifference)) {
return null
}
return state.maxPoint - Math.ceil(getter.timeDifference / 5)
},
maxPointTime: function (state, getter) {
if (!(state.currentPoint && state.maxPoint)) {
return null
}
const t = new Date(state.currentTime.getTime())
t.setMinutes(t.getMinutes() + (state.maxPoint - state.currentPoint) * 5)
return `${t.getHours()}:${('0' + t.getMinutes()).slice(-2)}`
},
efficientIntervals: function (state, getter) {
if (!(state.currentPoint && state.maxPoint && state.intervalPoint && getter.timeDifference)) {
return []
}
return [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14]
.map(i => i * state.intervalPoint)
.filter(i => i >= state.currentPoint)
.filter(i => i <= Math.max(state.maxPoint, state.currentPoint - getter.efficientPoint))
.map(i => ({
point: i,
time: (() => {
const t = new Date(state.currentTime.getTime())
t.setMinutes(t.getMinutes() + (i - state.currentPoint) * 5)
return `${t.getHours()}:${('0' + t.getMinutes()).slice(-2)}`
})(),
efficiently: (state.currentPoint - getter.efficientPoint + state.intervalPoint) > i
}))
.slice(0, 5)
}
}
})
Vue.component('ap-input', {
store,
template: '#ap-input-template',
props: ['title', 'name', 'type', 'value'],
methods: {
update: function (v) {
this.$store.dispatch('update' + this.name.charAt(0).toUpperCase() + this.name.substring(1), { value: v })
}
}
})
app = new Vue({
store,
el: '#main',
template: '#main-template',
computed: Object.assign({},
Vuex.mapState([
'currentPoint',
'maxPoint',
'intervalPoint',
'targetTime'
]),
Vuex.mapGetters([
'efficientPoint',
'maxPointTime',
'efficientIntervals'
])
),
methods: Object.assign({},
Vuex.mapActions(['goAround'])
)
})
store.dispatch('recalcCurrentPoint')
setTimeout(() => store.dispatch('recalcCurrentPoint'), 1000 * 60 * 5 + 100)
})
</script>
</head>
<body>
<div id="main">
</div>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment