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', @getGeneration.maxLevel) 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 = $('
').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) -> move = _(MoveData[moveName]).clone() if typeof move is "undefined" console.log(moveName) 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') || @getGeneration.maxLevel 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) getTier: -> gen = @getGeneration() PokeBattle.Tier.determineTier(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() if @get('species') == "Rayquaza" and "Dragon Ascent" in @get('moves') return true item = @getItem() return false if typeof item == "undefined" 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')) "/pokemon/#{@get('species')}" 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: []