291 lines
9.9 KiB
CoffeeScript
291 lines
9.9 KiB
CoffeeScript
|
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>"""
|