mirror of
https://gitlab.com/Deukhoofd/BattleSim.git
synced 2025-10-28 18:20:04 +00:00
Lots of stuff
This commit is contained in:
5
client/app/js/concerns/achievements.coffee
Normal file
5
client/app/js/concerns/achievements.coffee
Normal file
@@ -0,0 +1,5 @@
|
||||
$currentModal = null
|
||||
|
||||
PokeBattle.primus.on 'achievementsEarned', (achievements) ->
|
||||
# TODO: Add achievements to the current modal if one is already open
|
||||
$currentModal = PokeBattle.modal('modals/achievements', window: window, achievements: achievements)
|
||||
10
client/app/js/concerns/alts.coffee
Normal file
10
client/app/js/concerns/alts.coffee
Normal file
@@ -0,0 +1,10 @@
|
||||
PokeBattle.primus.on 'altList', (list) ->
|
||||
PokeBattle.alts.list = list
|
||||
|
||||
PokeBattle.primus.on 'altCreated', (altName) ->
|
||||
PokeBattle.alts.list.push(altName)
|
||||
|
||||
PokeBattle.alts =
|
||||
list: []
|
||||
createAlt: (altName) ->
|
||||
PokeBattle.primus.send('createAlt', altName)
|
||||
52
client/app/js/concerns/battle_consumer.coffee
Normal file
52
client/app/js/concerns/battle_consumer.coffee
Normal file
@@ -0,0 +1,52 @@
|
||||
# Send primus event when leaving battles.
|
||||
PokeBattle.battles.on 'remove', (battle) ->
|
||||
PokeBattle.primus.send('leaveChatroom', battle.id)
|
||||
|
||||
# Event listeners
|
||||
PokeBattle.primus.on 'updateBattle', (id, queue) ->
|
||||
battle = PokeBattle.battles.get(id)
|
||||
if !battle
|
||||
console.log "Received events for #{id}, but no longer in battle!"
|
||||
return
|
||||
battle.update(queue)
|
||||
|
||||
# Create a BattleView when spectating a battle
|
||||
PokeBattle.primus.on 'spectateBattle', (id, format, numActive, index, playerIds, log) ->
|
||||
if PokeBattle.battles.get(id)
|
||||
console.log "Already spectating battle #{id}!"
|
||||
return
|
||||
battle = new Battle({id, format, numActive, index, playerIds})
|
||||
|
||||
# Create BattleView
|
||||
$battle = $(JST['battle_window']({battle, window}))
|
||||
$('#main-section').append($battle)
|
||||
battle.view = new BattleView(el: $battle, model: battle)
|
||||
battle.view.skip = 0
|
||||
battle.view.$('.battle_pane').hide()
|
||||
|
||||
# Add to collection
|
||||
PokeBattle.battles.add(battle)
|
||||
|
||||
# Update log
|
||||
battle.update(log)
|
||||
|
||||
PokeBattle.primus.on 'updateTimers', (id, timers) ->
|
||||
battle = PokeBattle.battles.get(id)
|
||||
if !battle
|
||||
console.log "Received events for #{id}, but no longer in battle!"
|
||||
return
|
||||
battle.view.updateTimers(timers)
|
||||
|
||||
PokeBattle.primus.on 'resumeTimer', (id, player) ->
|
||||
battle = PokeBattle.battles.get(id)
|
||||
if !battle
|
||||
console.log "Received events for #{id}, but no longer in battle!"
|
||||
return
|
||||
battle.view.resumeTimer(player)
|
||||
|
||||
PokeBattle.primus.on 'pauseTimer', (id, player, timeSinceLastAction) ->
|
||||
battle = PokeBattle.battles.get(id)
|
||||
if !battle
|
||||
console.log "Received events for #{id}, but no longer in battle!"
|
||||
return
|
||||
battle.view.pauseTimer(player, timeSinceLastAction)
|
||||
2
client/app/js/concerns/battle_list.coffee
Normal file
2
client/app/js/concerns/battle_list.coffee
Normal file
@@ -0,0 +1,2 @@
|
||||
PokeBattle.primus.on 'battleList', (battles) ->
|
||||
PokeBattle.battleList.refreshListComplete(battles)
|
||||
290
client/app/js/concerns/commands.coffee
Normal file
290
client/app/js/concerns/commands.coffee
Normal file
@@ -0,0 +1,290 @@
|
||||
PokeBattle.commands ?= {}
|
||||
|
||||
Commands = {}
|
||||
|
||||
desc = (description) ->
|
||||
desc.lastDescription = description
|
||||
|
||||
makeCommand = (commandNames..., func) ->
|
||||
for commandName in commandNames
|
||||
Commands[commandName] = func
|
||||
|
||||
# Generate description
|
||||
description = ""
|
||||
if commandNames.length > 1
|
||||
aliases = commandNames[1...].map((n) -> "/#{n}").join(', ')
|
||||
description += " <i>Also #{aliases}. </i>"
|
||||
description += desc.lastDescription
|
||||
# TODO: Hardcoded user level
|
||||
HelpDescriptions['1'][commandNames[0]] = description
|
||||
delete desc.lastDescription
|
||||
|
||||
parseCommand = (line) ->
|
||||
[ commandName, args... ] = line.split(/\s+/)
|
||||
if commandName[0] == '/'
|
||||
# It's a command. Remove leading slash
|
||||
commandName = commandName[1...]
|
||||
args = args.join(' ').split(/,/g)
|
||||
return [commandName, args]
|
||||
return null
|
||||
|
||||
PokeBattle.commands.execute = (room, line) ->
|
||||
result = parseCommand(line)
|
||||
return false if !result
|
||||
[commandName, args] = result
|
||||
command = Commands[commandName]
|
||||
if !command
|
||||
# Fall-through to server.
|
||||
return false
|
||||
command(room, args...)
|
||||
return true
|
||||
|
||||
desc 'Displays a list of all commands.'
|
||||
makeCommand "commands", "help", "h", (room) ->
|
||||
user = room.get('users').get(PokeBattle.username)
|
||||
|
||||
for level, descriptions of HelpDescriptions
|
||||
level = Number(level)
|
||||
continue if user.get('authority') < level
|
||||
|
||||
message = []
|
||||
# TODO: Hardcoded levels
|
||||
authLevels = {1: "USER", 2: "DRIVER", 3: "MOD", 4: "ADMIN", 5: "OWNER"}
|
||||
humanLevel = authLevels[level]
|
||||
message.push("<b>#{humanLevel} COMMANDS:</b>")
|
||||
for name, description of descriptions
|
||||
message.push("<b>/#{name}:</b> #{description}")
|
||||
message = message.join("<br>")
|
||||
room.announce('success', message)
|
||||
true
|
||||
|
||||
desc 'Opens the challenge for a specific user. Usage: /challenge username'
|
||||
makeCommand "challenge", "chall", "c", (room, username) ->
|
||||
if !username
|
||||
PokeBattle.events.trigger("errorMessage", "Usage: /challenge username")
|
||||
return
|
||||
message = PokeBattle.messages.add(id: username)
|
||||
message.openChallenge(username)
|
||||
|
||||
desc 'Private messages a certain user. Usage: /message username, message'
|
||||
makeCommand "message", "msg", "pm", "whisper", "w", (room, username, messages...) ->
|
||||
username = username?.trim()
|
||||
if !username
|
||||
PokeBattle.events.trigger("errorMessage", "Usage: /message username, msg")
|
||||
return
|
||||
message = PokeBattle.messages.add(id: username)
|
||||
|
||||
if messages.length > 0
|
||||
text = messages.join(',')
|
||||
PokeBattle.primus.send('privateMessage', message.id, text)
|
||||
else
|
||||
# The PM is opened without a message.
|
||||
message.trigger('open', message)
|
||||
|
||||
desc 'Clears the chat.'
|
||||
makeCommand "clear", (room) ->
|
||||
room.clear()
|
||||
|
||||
desc 'Displays a Pokemon\'s PokeBattle value, or displays all Pokemon at or under a particular PBV. Usage: /pbv pkmn1, pkmn2, OR /pbv number'
|
||||
makeCommand "pbv", (room, pokemon...) ->
|
||||
pbv = Number(pokemon[0])
|
||||
if !isNaN(pbv)
|
||||
messages = findPokemonAtPBV(pbv)
|
||||
else
|
||||
messages = findTotalPBV(pokemon)
|
||||
|
||||
if messages.length == 0
|
||||
room.announce('error', "<b>PBV error:</b> Enter valid Pokemon or PBV.")
|
||||
else
|
||||
room.announce('success', "<b>PBV:</b> #{messages.join('; ')}")
|
||||
|
||||
findPokemonAtPBV = (pbv) ->
|
||||
messages = []
|
||||
counter = 0
|
||||
for speciesName, formes of window.Generations.XY.FormeData
|
||||
for formeName, formeData of formes
|
||||
if formeData.pokeBattleValue <= pbv
|
||||
counter += 1
|
||||
dexEntry = "pokemon/#{slugify(speciesName)}/#{slugify(formeName)}"
|
||||
icon = pokemonIcon(speciesName, formeName)
|
||||
formattedName = formatName(speciesName, formeName)
|
||||
messages.push("#{linkToDex(dexEntry, icon + formattedName)}:
|
||||
#{formeData.pokeBattleValue}")
|
||||
if messages.length > 10
|
||||
messages = _.sample(messages, 10)
|
||||
messages.push(linkToDex("pokemon/?pbv=<#{pbv + 1}",
|
||||
"See more Pokemon »"))
|
||||
if messages.length > 0
|
||||
plural = if messages.length == 1 then "is" else "are"
|
||||
messages.unshift("There #{plural} #{counter} Pokemon with a PBV of
|
||||
#{pbv} or less")
|
||||
messages
|
||||
|
||||
findTotalPBV = (pokemon) ->
|
||||
pokemon = _(pokemon).map(findPokemon)
|
||||
messages = []
|
||||
total = 0
|
||||
for array in pokemon
|
||||
continue unless array
|
||||
[speciesName, formeName] = array
|
||||
pbv = PokeBattle.PBV.determinePBV(window.Generations.XY,
|
||||
species: speciesName, forme: formeName)
|
||||
total += pbv
|
||||
dexEntry = "pokemon/#{slugify(speciesName)}/#{slugify(formeName)}"
|
||||
icon = pokemonIcon(speciesName, formeName)
|
||||
formattedName = formatName(speciesName, formeName)
|
||||
messages.push("#{linkToDex(dexEntry, icon + formattedName)}: #{pbv}")
|
||||
messages.push("Total: #{total}") if messages.length > 1
|
||||
messages
|
||||
|
||||
desc 'Looks up information about a Pokemon, move, item, or ability.'
|
||||
makeCommand "data", "dex", (room, query) ->
|
||||
if (pokemon = findPokemon(query))
|
||||
message = dataPokemon(pokemon)
|
||||
else if (item = findItem(query))
|
||||
message = dataItem(item)
|
||||
else if (move = findMove(query))
|
||||
message = dataMove(move)
|
||||
else if (ability = findAbility(query))
|
||||
message = dataAbility(ability)
|
||||
else
|
||||
room.announce("error", "<b>Data error:</b> Enter a valid Pokemon, item,
|
||||
move, or ability.</div>")
|
||||
return
|
||||
room.announce('success', message)
|
||||
|
||||
dataPokemon = (pokemon) ->
|
||||
[speciesName, formeName] = pokemon
|
||||
[speciesSlug, formeSlug] = [slugify(speciesName), slugify(formeName)]
|
||||
forme = window.Generations.XY.FormeData[speciesName][formeName]
|
||||
{types, abilities, hiddenAbility, stats, pokeBattleValue} = forme
|
||||
|
||||
# Format abilities
|
||||
abilities = _.clone(abilities)
|
||||
abilities.push(hiddenAbility) if hiddenAbility?
|
||||
abilities = _(abilities).map((a) -> linkToDex("abilities/#{slugify(a)}", a))
|
||||
abilities = abilities.join('/')
|
||||
abilities += " (H)" if hiddenAbility?
|
||||
|
||||
# Format types, stats, and icon
|
||||
types = _(types).map (t) ->
|
||||
linkToDex("types/#{slugify(t)}",
|
||||
"<img src='#{window.TypeSprite(t)}' alt='#{t}'/>")
|
||||
statNames = [ 'HP', 'Attack', 'Defense', 'Sp.Attack', 'Sp.Defense', 'Speed']
|
||||
stats = [ stats.hp, stats.attack, stats.defense,
|
||||
stats.specialAttack, stats.specialDefense, stats.speed ]
|
||||
statsText = _.map(_.zip(statNames, stats), (a) -> a.join(': ')).join(' / ')
|
||||
|
||||
# Build data
|
||||
message = """#{pokemonIcon(speciesName, formeName, "left")}
|
||||
<p class="ml3">
|
||||
<b>#{formatName(speciesName, formeName)}:</b> #{types.join('')} |
|
||||
#{abilities}<br />#{statsText} |
|
||||
#{_(stats).reduce((a, b) -> a + b)} <abbr title="Base Stat Total">BST</abbr>
|
||||
| <abbr title="PokeBattle Value">PBV</abbr>: #{pokeBattleValue}
|
||||
#{linkToDex("pokemon/#{speciesSlug}/#{formeSlug}", "See dex entry »")}
|
||||
</p>
|
||||
"""
|
||||
message
|
||||
|
||||
dataItem = (itemName) ->
|
||||
item = window.Generations.XY.ItemData[itemName]
|
||||
message = "<b>#{itemName}:</b> #{item.description}"
|
||||
message += " Natural Gift is #{item.naturalGift.type} type
|
||||
and has #{item.naturalGift.power} base power." if item.naturalGift
|
||||
message += " Fling has #{item.flingPower} base power." if item.flingPower
|
||||
message += " Currently unreleased in Gen 6." if item.unreleased
|
||||
message
|
||||
|
||||
dataMove = (moveName) ->
|
||||
move = window.Generations.XY.MoveData[moveName]
|
||||
type = linkToDex("types/#{slugify(move.type)}",
|
||||
"<img src='#{window.TypeSprite(move.type)}' alt='#{move.type}'/>")
|
||||
category = """<img src="#{CategorySprite(move.damage)}"
|
||||
alt="#{move.damage}"/>"""
|
||||
target = """<img src="#{TargetSprite(move)}"
|
||||
alt="#{move.target}"/>"""
|
||||
power = move.power || "—"
|
||||
acc = move.accuracy || "—"
|
||||
maxpp = Math.floor(move.pp * 8/5)
|
||||
if move.priority > 0
|
||||
priority = "+#{move.priority}"
|
||||
else if move.priority < 0
|
||||
priority = move.priority
|
||||
message = """<b>#{moveName}:</b> #{type} #{category} #{target} """
|
||||
message += "<b>Power:</b> #{power} <b>Acc:</b> #{acc} <b>PP:</b> #{move.pp} (max #{maxpp})"
|
||||
message += "<br />"
|
||||
message += "Priority #{priority}. " if priority
|
||||
message += move.description
|
||||
message += " "
|
||||
message += linkToDex("moves/#{slugify(moveName)}",
|
||||
"See who learns this move »")
|
||||
message
|
||||
|
||||
dataAbility = (abilityName) ->
|
||||
ability = window.Generations.XY.AbilityData[abilityName]
|
||||
message = """<b>#{abilityName}:</b> #{ability.description}
|
||||
#{linkToDex("abilities/#{slugify(abilityName)}",
|
||||
"See who obtains this ability »")}"""
|
||||
message
|
||||
|
||||
# Finds the most lenient match possible.
|
||||
findPokemon = (pokemonName) ->
|
||||
pokemonName = normalize(pokemonName)
|
||||
for speciesName, speciesData of window.Generations.XY.FormeData
|
||||
for formeName of speciesData
|
||||
name = speciesName
|
||||
name += formeName unless formeName == 'default'
|
||||
name = normalize(name)
|
||||
name += name
|
||||
return [speciesName, formeName] if name.indexOf(pokemonName) != -1
|
||||
# Return blank match
|
||||
null
|
||||
|
||||
# Finds the most lenient match possible.
|
||||
findItem = (itemName) ->
|
||||
normalized = normalize(itemName)
|
||||
for name of window.Generations.XY.ItemData
|
||||
return name if normalized == normalize(name)
|
||||
# Return blank match
|
||||
null
|
||||
|
||||
# Finds the most lenient match possible.
|
||||
findMove = (moveName) ->
|
||||
normalized = normalize(moveName)
|
||||
for name of window.Generations.XY.MoveData
|
||||
return name if normalized == normalize(name)
|
||||
# Return blank match
|
||||
null
|
||||
|
||||
# Finds the most lenient match possible.
|
||||
findAbility = (abilityName) ->
|
||||
normalized = normalize(abilityName)
|
||||
for name of window.Generations.XY.AbilityData
|
||||
return name if normalized == normalize(name)
|
||||
# Return blank match
|
||||
null
|
||||
|
||||
slugify = (str) ->
|
||||
str.toLowerCase().replace(/[^a-z0-9]/g, '-').replace(/\-{2,}/g, '-')
|
||||
|
||||
normalize = (str) ->
|
||||
str.trim().toLowerCase().replace(/[^a-zA-Z0-9]+/g, '')
|
||||
|
||||
formatName = (speciesName, formeName) ->
|
||||
if formeName == 'default'
|
||||
pokemonName = speciesName
|
||||
else
|
||||
pokemonName = speciesName
|
||||
pokemonName += ' '
|
||||
pokemonName += formeName.split('-')
|
||||
.map((n) -> n[0].toUpperCase() + n[1...])
|
||||
.join('-')
|
||||
return pokemonName
|
||||
|
||||
linkToDex = (slug, text) ->
|
||||
"<a href='//pokebattle.com/dex/#{slug}' target='_blank'>#{text}</a>"
|
||||
|
||||
pokemonIcon = (speciesName, formeName, classes="") ->
|
||||
style = window.PokemonIconBackground(speciesName, formeName)
|
||||
"""<span class="pokemon_icon #{classes}" style="#{style}"></span>"""
|
||||
39
client/app/js/concerns/connections.coffee
Normal file
39
client/app/js/concerns/connections.coffee
Normal file
@@ -0,0 +1,39 @@
|
||||
$body = $("body")
|
||||
$popup = $('#popup')
|
||||
|
||||
PokeBattle.primus.on 'open', ->
|
||||
$popup.hide()
|
||||
PokeBattle.rooms.rawMessage("Connected to the server!", class: "yellow italic")
|
||||
|
||||
PokeBattle.primus.on 'reconnecting', ->
|
||||
PokeBattle.rooms.rawMessage("Lost connection to the server...", class: "red italic")
|
||||
|
||||
PokeBattle.primus.on 'end', ->
|
||||
PokeBattle.rooms.rawMessage("Connection terminated!", class: "red italic")
|
||||
|
||||
PokeBattle.primus.on 'reconnecting', (opts) ->
|
||||
seconds = Math.floor(opts.timeout / 1000)
|
||||
if $popup.length == 0
|
||||
$popup = $('<div id="popup" class="reconnect popup-absolute"/>').hide()
|
||||
$div = $('<span/>')
|
||||
.addClass('alert')
|
||||
.html("<strong>You lost your connection to PokeBattle!</strong>
|
||||
<span class='reconnect-text'></span>")
|
||||
.appendTo($popup)
|
||||
$popup.appendTo($body)
|
||||
$popup.find('.reconnect-text').html("""Reconnecting in
|
||||
<span class='reconnect-timer'>#{seconds}</span> seconds...""")
|
||||
$popup.fadeIn(250)
|
||||
$body.data('seconds', seconds)
|
||||
reconnectTimer($popup)
|
||||
|
||||
reconnectTimer = ($popup) ->
|
||||
seconds = $body.data('seconds') ? 1
|
||||
if seconds == 0
|
||||
$popup.find('.reconnect-text').text("Reconnecting...")
|
||||
else
|
||||
time = "#{Math.floor(seconds / 60)}:"
|
||||
time += "00#{seconds % 60}".substr(-2)
|
||||
$popup.find('.reconnect-timer').text(time)
|
||||
$body.data('seconds', seconds - 1)
|
||||
setTimeout(reconnectTimer.bind(this, $popup), 1000)
|
||||
57
client/app/js/concerns/errors.coffee
Normal file
57
client/app/js/concerns/errors.coffee
Normal file
@@ -0,0 +1,57 @@
|
||||
PokeBattle.primus.on 'errorMessage', (args...) ->
|
||||
PokeBattle.events.trigger('errorMessage', args...)
|
||||
|
||||
PokeBattle.events.on "errorMessage", (type, args...) ->
|
||||
e = PokeBattle.errors
|
||||
switch type
|
||||
when e.INVALID_SESSION
|
||||
$('#errors-modal').remove() if $('#errors-modal').length > 0
|
||||
options =
|
||||
title: "Your login timed out!"
|
||||
body: """To access the simulator, you need to
|
||||
<a href="http://91.121.152.74/">login again</a>."""
|
||||
$modal = PokeBattle.modal('modals/errors', options)
|
||||
$modal.find('.modal-footer button').first().focus()
|
||||
PokeBattle.primus.end()
|
||||
|
||||
when e.BANNED
|
||||
$('#errors-modal').remove() if $('#errors-modal').length > 0
|
||||
[reason, length] = args
|
||||
if length < 0
|
||||
length = "is permanent"
|
||||
else
|
||||
length = "lasts for #{Math.round(length / 60)} minute(s)"
|
||||
body = "This ban #{length}."
|
||||
if reason
|
||||
body += "You were banned for the following reason: #{reason}"
|
||||
options =
|
||||
title: "You have been banned!"
|
||||
body: body
|
||||
$modal = PokeBattle.modal('modals/errors', options)
|
||||
$modal.find('.modal-footer button').first().focus()
|
||||
PokeBattle.primus.end()
|
||||
|
||||
when e.FIND_BATTLE
|
||||
PokeBattle.events.trigger("findBattleCanceled")
|
||||
|
||||
# Show errors
|
||||
[errors] = args
|
||||
alert(errors)
|
||||
when e.BATTLE_DNE
|
||||
[battleId] = args
|
||||
message = 'This battle no longer exists.'
|
||||
PokeBattle.rooms.get(battleId)?.announce('error', message)
|
||||
when e.COMMAND_ERROR
|
||||
[ roomId, message ] = args
|
||||
PokeBattle.rooms.get(roomId).announce('error', message)
|
||||
when e.PRIVATE_MESSAGE
|
||||
[ toUser, messageText ] = args
|
||||
message = PokeBattle.messages.get(toUser)
|
||||
message.add(toUser, messageText, type: "error")
|
||||
when e.INVALID_ALT_NAME
|
||||
[ messageText ] = args
|
||||
alert(messageText)
|
||||
PokeBattle.events.trigger("invalidAltName")
|
||||
else
|
||||
console.log("Received error: #{type}")
|
||||
console.log(" with content: #{args}")
|
||||
7
client/app/js/concerns/exit_while_in_battle.coffee
Normal file
7
client/app/js/concerns/exit_while_in_battle.coffee
Normal file
@@ -0,0 +1,7 @@
|
||||
# Prevents a clean escape while you're in a battle.
|
||||
$(window).on 'beforeunload', ->
|
||||
if PokeBattle.battles.isPlaying()
|
||||
"You are currently in a battle."
|
||||
else
|
||||
# Do not prompt
|
||||
return
|
||||
4
client/app/js/concerns/logging.coffee
Normal file
4
client/app/js/concerns/logging.coffee
Normal file
@@ -0,0 +1,4 @@
|
||||
PokeBattle.primus.on 'data', (args...) ->
|
||||
try
|
||||
console.log(args...) if window.localStorage.debug == 'true'
|
||||
catch
|
||||
50
client/app/js/concerns/main_buttons.coffee
Normal file
50
client/app/js/concerns/main_buttons.coffee
Normal file
@@ -0,0 +1,50 @@
|
||||
$ ->
|
||||
$mainButtons = $('.main_buttons')
|
||||
|
||||
$mainButtons.on 'click', '.teambuilder_button', (e) ->
|
||||
PokeBattle.navigation.showTeambuilder()
|
||||
|
||||
createChallengePane
|
||||
eventName: "findBattle"
|
||||
populate: $mainButtons.find('.find_battle_select_team')
|
||||
button: $mainButtons.find('.find_battle')
|
||||
defaultClauses: [
|
||||
Conditions.SLEEP_CLAUSE
|
||||
Conditions.EVASION_CLAUSE
|
||||
Conditions.SPECIES_CLAUSE
|
||||
Conditions.OHKO_CLAUSE
|
||||
Conditions.PRANKSTER_SWAGGER_CLAUSE
|
||||
Conditions.UNRELEASED_BAN
|
||||
Conditions.RATED_BATTLE
|
||||
Conditions.TIMED_BATTLE
|
||||
]
|
||||
blockedClauses: true
|
||||
|
||||
$mainButtons.find('.find_battle').on 'challenge', ->
|
||||
$this = $(this)
|
||||
$this.find('.find-icon')
|
||||
.addClass('icon-spinner spinner-anim')
|
||||
.removeClass("icon-earth")
|
||||
|
||||
$mainButtons.find('.display_credits').click ->
|
||||
$modal = PokeBattle.modal('modals/credits')
|
||||
$modal.find('.modal-footer button').first().focus()
|
||||
|
||||
# Depresss Find Battle once one is found
|
||||
depressFindBattle = ->
|
||||
$mainButtons = $('.main_buttons')
|
||||
$button = $mainButtons.find('.find_battle')
|
||||
$button.removeClass("disabled")
|
||||
$button.find('.find-icon')
|
||||
.removeClass("icon-spinner spinner-anim")
|
||||
.addClass("icon-earth")
|
||||
$mainButtons.find('.find_battle_select_team .select').removeClass('disabled')
|
||||
|
||||
$(window).load ->
|
||||
$mainButtons = $('.main_buttons')
|
||||
PokeBattle.battles.on 'add', (battle) ->
|
||||
if !battle.get('spectating')
|
||||
depressFindBattle()
|
||||
|
||||
PokeBattle.primus.on 'findBattleCanceled', depressFindBattle
|
||||
PokeBattle.events.on 'findBattleCanceled', depressFindBattle
|
||||
31
client/app/js/concerns/private_messages.coffee
Normal file
31
client/app/js/concerns/private_messages.coffee
Normal file
@@ -0,0 +1,31 @@
|
||||
# Clicking a person's name will open a private message session with that person.
|
||||
$(document).on 'click', '.open_pm', ->
|
||||
$this = $(this)
|
||||
message = PokeBattle.messages.add(id: $this.data('user-id'))
|
||||
message.trigger('open', message)
|
||||
message.trigger('focus', message)
|
||||
|
||||
# Receive private message events
|
||||
PokeBattle.primus.on 'privateMessage', (messageId, fromUserId, messageText) ->
|
||||
message = PokeBattle.messages.add(id: messageId)
|
||||
message.add(fromUserId, messageText)
|
||||
|
||||
# Challenges
|
||||
PokeBattle.primus.on 'challenge', (fromUserId, generation, conditions) ->
|
||||
message = PokeBattle.messages.add(id: fromUserId)
|
||||
message.add(fromUserId, "You have been challenged!", type: "alert")
|
||||
message.openChallenge(fromUserId, generation, conditions)
|
||||
|
||||
PokeBattle.primus.on 'cancelChallenge', (fromUserId) ->
|
||||
message = PokeBattle.messages.add(id: fromUserId)
|
||||
message.add(fromUserId, "The challenge was canceled!", type: "alert")
|
||||
message.closeChallenge(fromUserId)
|
||||
|
||||
PokeBattle.primus.on 'rejectChallenge', (fromUserId) ->
|
||||
message = PokeBattle.messages.add(id: fromUserId)
|
||||
message.add(fromUserId, "The challenge was rejected!", type: "alert")
|
||||
message.closeChallenge(fromUserId)
|
||||
|
||||
PokeBattle.primus.on 'challengeSuccess', (fromUserId) ->
|
||||
message = PokeBattle.messages.add(id: fromUserId)
|
||||
message.closeChallenge(fromUserId)
|
||||
21
client/app/js/concerns/router.coffee
Normal file
21
client/app/js/concerns/router.coffee
Normal file
@@ -0,0 +1,21 @@
|
||||
class PokeBattleRouter extends Backbone.Router
|
||||
routes:
|
||||
"" : "main"
|
||||
"battles/:id" : "spectateBattle"
|
||||
|
||||
main: =>
|
||||
$navigation = $('#navigation')
|
||||
$navigation.find('.nav_item').first().click()
|
||||
|
||||
spectateBattle: (id) =>
|
||||
if PokeBattle.battles.get(id)
|
||||
PokeBattle.navigation.changeWindowToBattle(id)
|
||||
else
|
||||
PokeBattle.primus.send('spectateBattle', id)
|
||||
|
||||
PokeBattle.router = new PokeBattleRouter()
|
||||
|
||||
PokeBattle.primus.once "loginSuccess", ->
|
||||
return if Backbone.History.started
|
||||
PokeBattle.events.trigger("ready")
|
||||
routed = Backbone.history.start(pushState: true)
|
||||
4
client/app/js/concerns/spectating.coffee
Normal file
4
client/app/js/concerns/spectating.coffee
Normal file
@@ -0,0 +1,4 @@
|
||||
$(document).on 'click', '.spectate', ->
|
||||
battleId = $(this).data('battle-id')
|
||||
PokeBattle.router.navigate("battles/#{battleId}", trigger: true)
|
||||
return false
|
||||
6
client/app/js/concerns/stale_assets.coffee
Normal file
6
client/app/js/concerns/stale_assets.coffee
Normal file
@@ -0,0 +1,6 @@
|
||||
PokeBattle.primus.on 'version', (version) ->
|
||||
if version != PokeBattle.CLIENT_VERSION
|
||||
$modal = PokeBattle.modal 'modals/new_client', ($modal) ->
|
||||
$modal.on 'click', '.button_refresh', ->
|
||||
window.location.reload(true)
|
||||
$modal.find('button').first().focus()
|
||||
57
client/app/js/concerns/tab_notifications.coffee
Normal file
57
client/app/js/concerns/tab_notifications.coffee
Normal file
@@ -0,0 +1,57 @@
|
||||
DEFAULT_INTERVAL = 1000
|
||||
original = document.title
|
||||
timeoutId = undefined
|
||||
|
||||
$.flashTitle = (newMsg, interval) ->
|
||||
if newMsg == false
|
||||
# stop flashing and reset title
|
||||
clearTimeout(timeoutId)
|
||||
document.title = original
|
||||
else
|
||||
# loop flashing
|
||||
interval = interval || DEFAULT_INTERVAL
|
||||
timeoutId = setTimeout( ->
|
||||
clearTimeout(timeoutId)
|
||||
document.title = if (document.title == original) then newMsg else original
|
||||
timeoutId = setTimeout(arguments.callee, interval)
|
||||
, interval)
|
||||
|
||||
|
||||
PokeBattle.NotificationTypes =
|
||||
PRIVATE_MESSAGE:
|
||||
showDesktop: false
|
||||
BATTLE_STARTED:
|
||||
showDesktop: true
|
||||
prefix: "bs"
|
||||
title: "Battle Started"
|
||||
body: "Your battle has started!"
|
||||
ACTION_REQUESTED:
|
||||
showDesktop: true
|
||||
prefix: "ar"
|
||||
title: "Battle Action Requested"
|
||||
body: "A battle is ready for your input!"
|
||||
|
||||
notifications = []
|
||||
|
||||
# Currently called in concerns/find_battle.coffee
|
||||
PokeBattle.requestNotifyPermission = =>
|
||||
if notify.permissionLevel() == notify.PERMISSION_DEFAULT
|
||||
notify.requestPermission()
|
||||
|
||||
# TODO: Count the notifications by unique type/identifier combos
|
||||
PokeBattle.notifyUser = (type, identifier) =>
|
||||
return if document.hasFocus()
|
||||
$.flashTitle "You have new notification(s)"
|
||||
|
||||
if type.showDesktop
|
||||
notification = notify.createNotification type.title,
|
||||
icon: "//media.pokebattle.com/logo/pb_red.png"
|
||||
body: type.body
|
||||
tag: "PokeBattle_#{type.prefix}_#{identifier}"
|
||||
|
||||
notifications.push notification
|
||||
|
||||
$(window).focus ->
|
||||
$.flashTitle(false)
|
||||
notification.close() for notification in notifications
|
||||
notifications = []
|
||||
70
client/app/js/concerns/team_store.coffee
Normal file
70
client/app/js/concerns/team_store.coffee
Normal file
@@ -0,0 +1,70 @@
|
||||
class TeamStore extends Backbone.Collection
|
||||
model: Team
|
||||
|
||||
initialize: ->
|
||||
@on('add remove reset remoteSync', @saveLocally)
|
||||
@loadLocally()
|
||||
|
||||
# Only locally save teams without an id or is trying to save.
|
||||
unsavedTeams: =>
|
||||
@filter((team) -> !team.id || team.get('saving'))
|
||||
|
||||
saveLocally: =>
|
||||
teams = @unsavedTeams()
|
||||
json = _.map(teams, (team) -> team.toJSON())
|
||||
try
|
||||
window.localStorage.setItem('local_teams', JSON.stringify(json))
|
||||
catch
|
||||
console.error("Failed to save teams locally.")
|
||||
|
||||
loadLocally: =>
|
||||
try
|
||||
json = window.localStorage.getItem('local_teams')
|
||||
return unless json
|
||||
teams = JSON.parse(json)
|
||||
@add(teams) if teams.length > 0
|
||||
catch
|
||||
console.error("Failed to load teams locally.")
|
||||
|
||||
saveRemotely: =>
|
||||
teams = @unsavedTeams()
|
||||
team.save() for team in teams
|
||||
|
||||
PokeBattle.TeamStore = new TeamStore()
|
||||
|
||||
PokeBattle.primus.on 'receiveTeams', (remoteTeams) ->
|
||||
remoteTeams = remoteTeams.map (team) ->
|
||||
team.teambuilder = true
|
||||
new Team(team)
|
||||
|
||||
# First, find teams that are already saved locally -- these exclude deleted
|
||||
# teams on either side. The remote copy of the team is checked against the
|
||||
# local copy of the team. If they differ, display a modal asking whether to
|
||||
# override or keep the local changes.
|
||||
ids = PokeBattle.TeamStore.pluck('id')
|
||||
for remoteTeam in remoteTeams when remoteTeam.id in ids
|
||||
remoteJSON = remoteTeam.toJSON()
|
||||
localTeam = PokeBattle.TeamStore.get(remoteTeam.id)
|
||||
unsavedTeam = localTeam.clone()
|
||||
unsavedTeam.set(localTeam.previousAttributes(), silent: true)
|
||||
localJSON = unsavedTeam.toJSON()
|
||||
if !_.isEqual(remoteJSON, localJSON)
|
||||
# Whoa! Versions are different! Let's ask the user what to do.
|
||||
teamText = PokeBattle.exportTeam(remoteJSON.pokemon)
|
||||
domId = "teams-differ-#{remoteTeam.id}"
|
||||
$modal = PokeBattle.modal('modals/teams_differ', domId, {teamText})
|
||||
$modal.find('textarea').first().focus()
|
||||
do (localTeam, remoteJSON) ->
|
||||
# We want to override the current version with the one on the server.
|
||||
# This is extremely hacky due to hidden state and clones everywhere on
|
||||
# the teambuilder.
|
||||
$modal.find('.button_override').one 'click', ->
|
||||
localTeam.set(remoteJSON, silent: true)
|
||||
localTeam.trigger('render', localTeam)
|
||||
$modal.modal('hide')
|
||||
|
||||
# Now, add teams we haven't seen yet to the store.
|
||||
PokeBattle.TeamStore.add(remoteTeams)
|
||||
|
||||
PokeBattle.primus.on 'loginSuccess', ->
|
||||
PokeBattle.primus.send('requestTeams')
|
||||
9
client/app/js/concerns/toggle_navigation.coffee
Normal file
9
client/app/js/concerns/toggle_navigation.coffee
Normal file
@@ -0,0 +1,9 @@
|
||||
# Used to handle the show navigation button in the header, which is visible when the browser
|
||||
# window becomes small enough.
|
||||
$ ->
|
||||
$navigation = $("#navigation")
|
||||
$(".show-navigation").click =>
|
||||
active = $navigation.hasClass('active')
|
||||
$navigation.toggleClass('active', !active)
|
||||
|
||||
$navigation.on 'click', '.nav_item', -> $navigation.removeClass("active")
|
||||
Reference in New Issue
Block a user