BattleSim/api/index.coffee

180 lines
6.5 KiB
CoffeeScript

restify = require('restify')
generations = require('../server/generations')
learnsets = require('../shared/learnsets')
{makeBiasedRng} = require("../shared/bias_rng")
GenerationJSON = generations.GenerationJSON
getName = (name) ->
require('./name_map.json')[name]
slugify = (str) ->
str.toLowerCase().replace(/[^a-z0-9]/g, '-').replace(/\-{2,}/g, '-')
slugifyArray = (array) ->
hash = {}
for element in array
hash[slugify(element)] = element
hash
attachAPIEndpoints = (server) ->
for gen in generations.ALL_GENERATIONS
do (gen) ->
json = GenerationJSON[gen.toUpperCase()]
GenMoves = slugifyArray(json.MoveList)
GenAbilities = slugifyArray(json.AbilityList)
GenTypes = slugifyArray(json.TypeList)
try
# Preload Battle
{Battle} = require("../server/#{gen}/battle")
catch
# TODO: There is no Battle object for this gen
intGeneration = generations.GENERATION_TO_INT[gen]
server.get "#{gen}/moves", (req, res, next) ->
res.send(json.MoveData)
return next()
server.get "#{gen}/pokemon", (req, res, next) ->
res.send(json.FormeData)
return next()
server.get "#{gen}/pokemon/:species", (req, res, next) ->
species = getName(req.params.species)
return next(new restify.ResourceNotFoundError("Could not find Pokemon: #{req.params.species}")) if !species
pokemon = json.FormeData[species]
res.send(pokemon)
return next()
server.get "#{gen}/items", (req, res, next) ->
res.send(items: json.ItemList)
return next()
server.get "#{gen}/moves", (req, res, next) ->
res.send(moves: json.MoveList)
return next()
server.get "#{gen}/moves/:name", (req, res, next) ->
move = GenMoves[req.params.name]
return next(new restify.ResourceNotFoundError("Could not find Move: #{req.params.name}")) if !move
res.send(pokemon: json.MoveMap[move])
return next()
server.get "#{gen}/abilities", (req, res, next) ->
res.send(abilities: json.AbilityList)
return next()
server.get "#{gen}/abilities/:name", (req, res, next) ->
ability = GenAbilities[req.params.name]
return next(new restify.ResourceNotFoundError("Could not find Ability: #{req.params.name}")) if !ability
res.send(pokemon: json.AbilityMap[ability])
return next()
server.get "#{gen}/types", (req, res, next) ->
res.send(types: json.TypeList)
return next()
server.get "#{gen}/types/:name", (req, res, next) ->
type = GenTypes[req.params.name]
return next(new restify.ResourceNotFoundError("Could not find Type: #{req.params.name}")) if !type
res.send(pokemon: json.TypeMap[type])
return next()
server.get "#{gen}/pokemon/:species/moves", (req, res, next) ->
species = getName(req.params.species)
pokemon = {species: species}
moves = learnsets.learnableMoves(GenerationJSON, pokemon, intGeneration)
return next(new restify.ResourceNotFoundError("Could not find moves for Pokemon: #{req.params.species}")) if !moves || moves.length == 0
res.send(moves: moves)
return next()
server.get "#{gen}/pokemon/:species/:forme/moves", (req, res, next) ->
species = getName(req.params.species)
pokemon = {species: species, forme: req.params.forme}
moves = learnsets.learnableMoves(GenerationJSON, pokemon, intGeneration)
return next(new restify.ResourceNotFoundError("Could not find moves for Pokemon: #{req.params.species}")) if !moves || moves.length == 0
res.send(moves: moves)
return next()
checkMoveset = (req, res, next) ->
species = getName(req.params.species)
return next(new restify.ResourceNotFoundError("Could not find Pokemon: #{req.params.species}")) if !species
pokemon = {species: species}
pokemon.forme = req.params.forme if req.params.forme
moveset = req.query.moves?.split(/,/) || []
valid = learnsets.checkMoveset(GenerationJSON, pokemon, intGeneration, moveset)
errors = []
errors.push("Invalid moveset") if !valid
res.send(errors: errors)
return next()
server.get "#{gen}/pokemon/:species/check", checkMoveset
server.get "#{gen}/pokemon/:species/:forme/check", checkMoveset
server.put "#{gen}/damagecalc", (req, res, next) ->
# todo: catch any invalid data.
moveName = req.params.move
attacker = req.params.attacker
defender = req.params.defender
players = [
{id: "0", name: "0", team: [attacker]}
{id: "1", name: "1", team: [defender]}
]
battle = new Battle('id', players, numActive: 1)
move = battle.getMove(moveName)
if not move
return next(new restify.BadRequest("Invalid move #{moveName}"))
battle.begin()
attackerPokemon = battle.getTeam("0").at(0)
defenderPokemon = battle.getTeam("1").at(0)
# bias the RNG to remove randmomness like critical hits
makeBiasedRng(battle)
battle.rng.bias("next", "ch", 1)
battle.rng.bias("randInt", "miss", 0)
battle.rng.bias("next", "secondary effect", 0)
battle.rng.bias("randInt", "flinch", 100)
# calculate min damage
battle.rng.bias("randInt", "damage roll", 15)
minDamage = move.calculateDamage(battle, attackerPokemon, defenderPokemon)
# calculate max damage
battle.rng.bias("randInt", "damage roll", 0)
maxDamage = move.calculateDamage(battle, attackerPokemon, defenderPokemon)
# TODO: Add remaining HP or anything else that's requested
res.send(
moveType: move.getType(battle, attackerPokemon, defenderPokemon)
basePower: move.basePower(battle, attackerPokemon, defenderPokemon)
minDamage: minDamage
maxDamage: maxDamage
defenderMaxHP: defenderPokemon.stat('hp')
)
return next()
@createServer = (port, done) ->
server = restify.createServer
name: 'pokebattle-api'
version: '0.0.0'
server.pre(restify.pre.sanitizePath())
server.use(restify.acceptParser(server.acceptable))
server.use(restify.queryParser())
server.use(restify.bodyParser())
server.use(restify.gzipResponse())
server.use (req, res, next) ->
res.charSet('utf8')
return next()
attachAPIEndpoints(server)
server.listen port, ->
console.log('%s listening at %s', server.name, server.url)
done?()
server