1
0
mirror of https://gitlab.com/Deukhoofd/BattleSim.git synced 2025-10-29 10:40:04 +00:00

Lots of stuff

This commit is contained in:
Deukhoofd
2016-02-01 23:19:30 +01:00
commit d7316d5799
6681 changed files with 527969 additions and 0 deletions

View File

@@ -0,0 +1,85 @@
class Teams extends Backbone.Collection
model: Team
class @Battle extends Backbone.AssociatedModel
relations: [
type: Backbone.Many
key: 'teams'
relatedModel: Team
collectionType: Teams
]
defaults:
spectating: true
finished: false
_.extend(this.prototype, PokeBattle.mixins.BattleProtocolParser)
initialize: (attributes) =>
@updateQueue = []
{@numActive, spectators} = attributes
@spectators = new UserList(spectators) unless !spectators
@set('generation', Formats[@get('format')].generation)
@set('notifications', 0)
@set('turn', 0)
@set('teams', [{hidden: true}, {hidden: true}])
@set('spectating', !@has('index'))
@set('index', Math.floor(2 * Math.random())) unless @has('index')
receiveTeams: (receivedTeams) =>
teams = @get('teams')
for receivedTeam, i in receivedTeams
receivedTeam.hidden = true
team = teams.at(i)
team.set(receivedTeam) if team.get('hidden')
receiveTeam: (team) =>
teams = @get('teams')
teams.at(@get('index')).unset('hidden', silent: true).set(team)
makeMove: (moveName, forSlot, callback) =>
pokemon = @getPokemon(@get('index'), forSlot)
options = {}
options['megaEvolve'] = pokemon.get('megaEvolve') if pokemon.get('megaEvolve')
PokeBattle.primus.send(
'sendMove', @id, moveName, forSlot,
@get('turn'), options, callback,
)
makeSwitch: (toSlot, forSlot, callback) =>
PokeBattle.primus.send(
'sendSwitch', @id, toSlot, forSlot, @get('turn'), callback
)
makeCancel: =>
PokeBattle.primus.send 'sendCancelAction', @id, @get('turn')
arrangeTeam: (arrangement) =>
PokeBattle.primus.send 'arrangeTeam', @id, arrangement
switch: (fromIndex, toIndex) =>
you = @getTeam().pokemon
[you[fromIndex], you[toIndex]] = [you[toIndex], you[fromIndex]]
getTeam: (playerIndex = @get('index')) =>
@get("teams").at(playerIndex)
getOpponentTeam: (playerIndex = @get('index')) =>
@get("teams").at(1 - playerIndex)
getPokemon: (playerIndex, slot = 0) =>
team = @getTeam(playerIndex)
team.at(slot)
isPlaying: =>
!@get('finished') && !@get('spectating')
forfeit: =>
PokeBattle.primus.send('forfeit', @id)
# TODO: Opponent switch. Use some logic to determine whether the switch is
# to a previously seen Pokemon or a new Pokemon. In the latter case, we
# should reveal a previously unknown Pokeball if it's not a Wi-Fi battle.
notify: =>
@set('notifications', @get('notifications') + 1)

View File

@@ -0,0 +1,350 @@
class @Pokemon extends Backbone.Model
defaults: =>
moves: []
percent: 100
ivs:
hp: 31
attack: 31
defense: 31
specialAttack: 31
specialDefense: 31
speed: 31
evs:
hp: 0
attack: 0
defense: 0
specialAttack: 0
specialDefense: 0
speed: 0
initialize: (attributes={}) ->
# History lesson: We stored species under `name`. Now that we support
# nicknames, we need the `name` freed up. However, teams are saved to the
# server using the old scheme. Therefore we need to do a simple check for
# the existence of `species`; if it exists, do nothing. If not, use `name`.
@set('species', @get('name')) if !@has('species') && @has('name')
@set('forme', 'default') unless @has('forme')
@set('illu', false)
@normalizeStats(@get('ivs'), 31)
@normalizeStats(@get('evs'), 0)
@resetBoosts()
@isNull = false
# Skip teambuilder-specific properties.
return if @get('teambuilder') != true
@on 'change:ivs', (model, ivs)=>
type = HiddenPower.BW.type(ivs).toLowerCase()
@set("hiddenPowerType", type, silent: true)
@on 'change:hiddenPowerType', (model, type) =>
hpIVs = HiddenPower.BW.ivs[type.toLowerCase()]
ivs = @get('ivs')
for stat, iv of ivs
ivs[stat] = hpIVs[stat] || 31
@set('ivs', ivs, silent: true)
@set('ability', @getAbilities()[0]) unless attributes.ability
@set('level', 100) unless attributes.level
@set('happiness', 100) if isNaN(attributes.happiness)
@set('nature', 'Hardy') unless attributes.nature
hiddenPowerType = HiddenPower.BW.type(@get('ivs')).toLowerCase()
@set('hiddenPowerType', hiddenPowerType, silent: true)
# If there is no gender set and only one possiblity, set the gender
unless @has('gender')
genders = @getGenders()
@set('gender', genders[0], silent: true) if genders.length == 1
resetBoosts: ->
@set 'stages',
hp: 0
attack: 0
defense: 0
specialAttack: 0
specialDefense: 0
speed: 0
accuracy: 0
evasion: 0
normalizeStats: (hash, defaultValue) ->
stats = [ "hp", "attack", "defense", "specialAttack",
"specialDefense", "speed"]
for stat in stats
hash[stat] ?= defaultValue
getName: ->
sanitizedName = $('<div/>').text(@get('name')).html()
sanitizedName = sanitizedName.replace(
/[\u0300-\u036F\u20D0-\u20FF\uFE20-\uFE2F]/g, '')
sanitizedName
getGeneration: (generation) ->
gen = generation || @collection?.generation || DEFAULT_GENERATION
gen = gen.toUpperCase()
window.Generations[gen]
getSpecies: ->
@getGeneration().SpeciesData[@get('species')]
getItem: ->
@getGeneration().ItemData[@get('item')]
getForme: (forme, generation) ->
forme ||= @get('forme')
@getGeneration(generation).FormeData[@get('species')]?[forme]
getFormes: ->
(forme for forme of @getGeneration().FormeData[@get('species')])
# Returns all non-battle only formes
getSelectableFormes: ->
_(@getFormes()).reject((forme) => @getForme(forme).isBattleOnly)
# Returns all mega formes
getMegaFormes: ->
_(@getFormes()).reject((forme) => forme.indexOf('mega') != 0)
getAbilities: ->
forme = @getForme()
abilities = _.clone(forme.abilities)
abilities.push(forme.hiddenAbility) if forme.hiddenAbility
_.unique(abilities)
getGenders: ->
species = @getSpecies()
genders = []
switch species.genderRatio
when -1
genders.push("Genderless")
when 0
genders.push("M")
when 8
genders.push("F")
else
genders.push("M", "F")
genders
hasSelectedMove: (moveName) ->
moveName && moveName in @moves
getMovepool: ->
{SpeciesData, MoveData} = @getGeneration()
generation = GENERATION_TO_INT[@collection?.generation || DEFAULT_GENERATION]
learnset = learnableMoves(window.Generations, @attributes, generation)
# Map each move name to a move object
return _(learnset).map (moveName) ->
console.log(moveName)
move = _(MoveData[moveName]).clone()
move['name'] = moveName
move
getTotalEVs: (options = {}) ->
total = 0
for stat, value of @get("evs")
total += value if stat != options.exclude
total
getTeam: =>
@collection?.parents[0]
setIv: (stat, value) ->
ivs = _.clone(@get("ivs"))
ivs[stat] = value
@set("ivs", ivs) # trigger change event
setEv: (stat, value) ->
evs = _.clone(@get("evs"))
value = value - (value % 4)
evs[stat] = value
@set("evs", evs) # trigger change event
value
iv: (stat) ->
@get("ivs")[stat] ? 31
ev: (stat) ->
@get("evs")[stat] ? 0
natureBoost: (stat) ->
nature = @get('nature')?.toLowerCase()
if nature of natures
natures[nature][stat] || 1
else
1
base: (key) ->
forme = @getForme()
base = forme["stats"][key]
stat: (key) ->
base = @base(key)
return 1 if base == 1 # For Shedinja. key doesn't have to be hp.
level = @get('level') || 100
iv = @iv(key)
ev = Math.floor(@ev(key) / 4)
total = if key == 'hp'
Math.floor((2 * base + iv + ev) * (level / 100) + level + 10)
else
Math.floor(((2 * base + iv + ev) * (level / 100) + 5) * @natureBoost(key))
# Returns the natures that this pokemon can use
# The natures are returned as a list of [id, value] values
# to populate a dropdown field.
# TODO: Should this be needed in more places, return Nature objects instead
getNatures: ->
natureResults = []
for nature, stats of natures
name = nature[0].toUpperCase() + nature.substr(1)
invertedStats = _(stats).invert()
label = name
if invertedStats[PLUS]
# This nature has an effect, so update the label
plusStat = statAbbreviations[invertedStats[PLUS]]
minusStat = statAbbreviations[invertedStats[MINUS]]
label = "#{name} (+#{plusStat}, -#{minusStat})"
natureResults.push [name, label]
return natureResults
getPBV: ->
gen = @getGeneration()
PokeBattle.PBV.determinePBV(gen, @attributes)
setPP: (moveIndex, newPP) ->
array = _.clone(@get('pp'))
array[moveIndex] = newPP
@set('pp', array)
getPercentHP: ->
Math.max(@get('percent'), 0)
getHPColor: ->
percent = @getPercentHP()
switch
when percent <= 20 then 'red'
when percent <= 50 then 'yellow'
else 'green'
isFainted: ->
@get('percent') <= 0
getStatus: ->
status = @get('status')
if status
"#{status[0].toUpperCase()}#{status.substr(1)}"
else
"Healthy"
canMegaEvolve: ->
# TODO: Refactor this to use getPossibleMegaForme()
# I didn't feel like making the change and testing it while implementing getPossibleMegaForme()
item = @getItem()
return false if item.type != 'megastone'
[ species, forme ] = item.mega
return false if @get('species') != species || @get('forme') != 'default'
return true
getPossibleMegaForme: ->
item = @getItem()
return null if item?.type != 'megastone'
[ species, forme ] = item.mega
return forme if @get('species') == species && @get('forme') == 'default'
return null
# Returns the complete web address to the pokedex link for this pokemon.
# For this project, this leads to our website at http://www.pokebattle.com,
# but if you want it to lead somewhere else, edit this function.
getPokedexUrl: ->
# todo: move this function to /shared, or use an actual slugify library
slugify = (str) ->
str.toLowerCase().replace(/[^a-z0-9]/g, '-').replace(/\-{2,}/g, '-')
slugSpecies = slugify(@get('species'))
slugForme = slugify(@get('forme'))
"//pokebattle.com/dex/pokemon/#{slugSpecies}/#{slugForme}"
getIllu: ->
@get('illu')
setIllu: (y) ->
@set('illu', y)
toJSON: ->
attributes = _.clone(@attributes)
delete attributes.gender if attributes.gender == 'Genderless'
delete attributes.hiddenPowerType
delete attributes.teambuilder
attributes
# TODO: These shortenings really should be stored somewhere else.
statAbbreviations =
'hp' : 'HP'
'attack' : 'Atk'
'defense' : 'Def'
'specialAttack' : 'SAtk'
'specialDefense' : 'SDef'
'speed' : 'Spe'
# A hash that keys a nature with the stats that it boosts.
# Neutral natures are ignored.
# TODO: .yml-ify these.
PLUS = 1.1
MINUS = 0.9
natures =
lonely: {attack: PLUS, defense: MINUS}
brave: {attack: PLUS, speed: MINUS}
adamant: {attack: PLUS, specialAttack: MINUS}
naughty: {attack: PLUS, specialDefense: MINUS}
bold: {defense: PLUS, attack: MINUS}
relaxed: {defense: PLUS, speed: MINUS}
impish: {defense: PLUS, specialAttack: MINUS}
lax: {defense: PLUS, specialDefense: MINUS}
timid: {speed: PLUS, attack: MINUS}
hasty: {speed: PLUS, defense: MINUS}
jolly: {speed: PLUS, specialAttack: MINUS}
naive: {speed: PLUS, specialDefense: MINUS}
modest: {specialAttack: PLUS, attack: MINUS}
mild: {specialAttack: PLUS, defense: MINUS}
quiet: {specialAttack: PLUS, speed: MINUS}
rash: {specialAttack: PLUS, specialDefense: MINUS}
calm: {specialDefense: PLUS, attack: MINUS}
gentle: {specialDefense: PLUS, defense: MINUS}
sassy: {specialDefense: PLUS, speed: MINUS}
careful: {specialDefense: PLUS, specialAttack: MINUS}
hardy: {}
docile: {}
serious: {}
bashful: {}
quirky: {}
class @NullPokemon extends Pokemon
initialize: ->
@set('species', null)
@set('forme', 'default')
@isNull = true
getNatures: -> []
getPBV: -> 0
base: -> 0
stat: -> null
iv: -> null
ev: -> null
getSpecies: ->
id: 0
genderRatio: -1
generation: 1
getForme: ->
@getFormes()['default']
getFormes: ->
default:
abilities: []
hiddenAbility: null
isBattleOnly: false
learnset: {}
types: []

View File

@@ -0,0 +1,128 @@
class PokemonCollection extends Backbone.Collection
model: (attrs, options) =>
# History lesson: We stored species under `name`. Now that we support
# nicknames, we need the `name` freed up. However, teams are saved to the
# server using the old scheme. Therefore we need to do a simple check for
# the existence of `species`; if it exists, do nothing. If not, use `name`.
if attrs.name || attrs.species
attrs.teambuilder = @parents[0].get('teambuilder')
return new Pokemon(attrs, options)
else
return new NullPokemon()
class @Team extends Backbone.AssociatedModel
relations: [
type: Backbone.Many
key: 'pokemon'
collectionType: PokemonCollection
]
initialize: (attrs={}, options={}) =>
@owner = attrs.owner
@set('generation', DEFAULT_GENERATION) unless attrs.generation
@set('teambuilder', true) if options.teambuilder
@set('pokemon', []) unless attrs.pokemon
getName: =>
@get('name') || "Untitled team"
toJSON: =>
json = {}
json['id'] = @id if @id
json['name'] = @get('name')
json['generation'] = @get('generation')
json['pokemon'] = @get('pokemon').toJSON()
json
# Returns the pokemon at a particular index. Delegates to the internal pokemon collection
at: (idx) => @get('pokemon').at(idx)
# Returns which index the pokemon is in
indexOf: (idx) => @get('pokemon').indexOf(idx)
# Replace a pokemon at a particular index for another
replace: (idx, newPokemon) =>
@get('pokemon').remove(@get('pokemon').at(idx))
@get('pokemon').add(newPokemon, at: idx)
# Equivalent to toJSON, but omits NullPokemon
toNonNullJSON: =>
id: @id
name: @get('name')
generation: @get('generation')
pokemon: @get('pokemon')
.reject((pokemon) -> pokemon.isNull)
.map((pokemon) -> pokemon.toJSON())
clone: =>
attrs = _(@attributes).clone()
attrs.pokemon = @get('pokemon').toJSON()
new Team(attrs)
rearrange: (arrangement) ->
pokemon = @get('pokemon')
pokemon.reset((pokemon.models[index] for index in arrangement))
return true
getFormat: =>
format = @get('generation') # TODO: Migrate to format
format = DEFAULT_FORMAT if format not of Formats
Formats[format]
getGeneration: (generation) ->
gen = generation || @getFormat().generation
gen = gen.toUpperCase()
window.Generations[gen]
getPBV: =>
gen = @getGeneration()
pokemon = @get('pokemon').toJSON()
PokeBattle.PBV.determinePBV(gen, pokemon)
getMaxPBV: =>
{conditions} = @getFormat()
if Conditions.PBV_1000 in conditions
1000
else if Conditions.PBV_500 in conditions
500
else
0
hasPBV: =>
@getMaxPBV() > 0
getNonNullPokemon: =>
@get('pokemon').where(isNull: false)
hasNonNullPokemon: =>
@get('pokemon').some((pokemon) -> not pokemon.isNull)
sync: (method) =>
switch method
when 'create', 'patch', 'update'
@set('saving', true, silent: true)
@trigger('remoteSync', this)
PokeBattle.primus.send 'saveTeam', @toJSON(), (id) =>
# Note: If this model is saved multiple times, then this won't
# tell you if some of the saves failed.
@set('id', id)
@set('saving', false, silent: true)
@trigger('remoteSync', this)
when 'delete'
PokeBattle.primus.send('destroyTeam', @id) if @id
getRandomOrder: =>
order = @get('randomorder')
if typeof order is 'undefined'
@setRandomOrder()
order = @get('randomorder')
order
else
order = @get('randomorder')
order
setRandomOrder: =>
#random order for the icons with team preview when in battle
team = @get('pokemon').toJSON()
teamshuffled = _.shuffle(team)
@set('randomorder', teamshuffled)