Skip to content

Instantly share code, notes, and snippets.

@revoice1
Last active February 13, 2023 20:54
Show Gist options
  • Save revoice1/203fed43d43cf26de357c1a23a3d6746 to your computer and use it in GitHub Desktop.
Save revoice1/203fed43d43cf26de357c1a23a3d6746 to your computer and use it in GitHub Desktop.
Function that leverages PokéAPI to quickly retrieve damage relationships for any given Pokémon, from any given generation. Wrote this for myself and my son to use while running soullink nuzlockes from various generations. Super helpful when you don't have the damage tables for a given generation memorized.
function Get-PokemonDamage {
<#
.SYNOPSIS
Retrieve damage information of a Pokemon based on its type(s)
.DESCRIPTION
The `Get-PokemonDamage` function retrieves information about the damage a Pokemon takes from different types of attacks, based on its type(s).
It can be invoked using either the name of the Pokemon or its type(s).
By default, the function returns information about the most recent generation of the Pokemon, but it can also be configured to return information about past generations using the `Generation` parameter.
An extra info switch can also be specified to include more details about the Pokemon.
.PARAMETER Pokemon
The name of a pokemon that you would like to receive damage relationship information for
.PARAMETER ExtraInfo
This switch used with the Pokemon parameter that pulls extra info from PokeAPI about the selected Pokemon.
This information includes:
* The generation they were introduced
* The genus of the Pokemon
* The BST of the Pokemon
* The weight of the Pokemon in pounds
* The damage this Pokemon receives from weight based attacks e.g. "low kick"
* A list of alternate forms for the Pokemon
* What the Pokemon evolves from
* Any special types of the Pokemon e.g. Baby, Legendary, Mythical
* A list of possible abilities that the Pokemon can have
.PARAMETER Generation
An integer used with the Pokemon or Types parameter that specifies the generation that you would like to receive damage information for. This is due to some damage relationships changing over different generations.
.PARAMETER Types
A list of types that you want to receive damage relationship information for
.NOTES
This function uses the PokeAPI (https://pokeapi.co) to retrieve information about Pokemon and their types.
.LINK
https://pokeapi.co/
.EXAMPLE
# Returns damage information about Pikachu in the most recent generation, along with extra details about the Pokemon.
PS> Get-PokemonDamage Pikachu -ExtraInfo
*** Pikachu Is Electric Type ***
Serebii Link : https://www.serebii.net/pokemon/pikachu/
--- Stats ---
Introduced : Generation-I
Genus : Mouse Pokémon
BST : 320
Weight (Lbs) : 13.23
Weight Damage : 20
Alt-Forms : Pikachu-Rock-Star, Pikachu-Belle, Pikachu-Pop-Star, Pikachu-Phd, Pikachu-Libre, Pikachu-Cosplay, Pikachu-Original-Cap, Pikachu-Hoenn-Cap, Pikachu-Sinnoh-Cap, Pikachu-Unova-Cap, Pikachu-Kalos-Cap, Pikachu-Alola-Cap, Pikachu-Partner-Cap, Pikachu-Starter, Pikachu-World-Cap, Pikachu-Gmax
Evolves From : Pichu
--- Abilities ---
Static : Has a 30% chance of paralyzing attacking Pokémon on contact.
Lightning Rod* : Redirects single-target electric moves to this Pokémon where possible. Absorbs Electric moves, raising Special Attack one stage.
[* = Hidden]
--- Electric Damage In ---
Double : Ground
Half : Flying, Electric, Steel
--- Electric Damage Out ---
Double : Flying, Water
Half : Grass, Electric, Dragon
None : Ground
.EXAMPLE
# Returns damage information about the Fire type in Generation III.
PS> Get-PokemonDamage Fire -Generation 3
--- Fire Damage In [III] ---
Double : Rock, Water, Ground
Half : Fairy, Grass, Ice, Steel, Bug, Fire
--- Fire Damage Out [III] ---
Double : Bug, Steel, Grass, Ice
Half : Rock, Fire, Water, Dragon
.EXAMPLE
# Returns damage information about Charmander in Generation II.
PS> Get-PokemonDamage Charmander -Generation 2
*** Charmander Is Fire Type [II] ***
Serebii Link : https://www.serebii.net/pokedex-gs/004.shtml
--- Fire Damage In [II] ---
Double : Rock, Water, Ground
Half : Fairy, Grass, Ice, Steel, Bug, Fire
--- Fire Damage Out [II] ---
Double : Bug, Steel, Grass, Ice
Half : Rock, Fire, Water, Dragon
#>
[CmdletBinding(DefaultParameterSetName = 'Pokemon')]
param(
[Parameter(Mandatory = $true, ParameterSetName = 'Pokemon', Position = 0)]
[ArgumentCompleter({
param ($commandName, $parameterName, $wordToComplete)
$Pokemon = $(Invoke-RestMethod "https://pokeapi.co/api/v2/pokemon?limit=2000").results
$Pokemon.name -like "$wordToComplete*"
})]
[string]$Pokemon,
[ArgumentCompleter({
param ($commandName, $parameterName, $wordToComplete)
$Pokemon = $(Invoke-RestMethod "https://pokeapi.co/api/v2/type").results
$Pokemon.name -like "$wordToComplete*"
})]
[Parameter(Mandatory = $true, ParameterSetName = 'Type')]
$Types,
[Parameter(ParameterSetName = 'Pokemon')]
[switch]$ExtraInfo,
[Parameter(ParameterSetName = 'Type')]
[Parameter(ParameterSetName = 'Pokemon')]
[int]$Generation
)
$StringOutput = @()
$DamageInTypes = "quad_damage_from", "double_damage_from", "half_damage_from", "quarter_damage_from", "no_damage_from"
$DamageOutTypes = "double_damage_to", "half_damage_to", "no_damage_to"
if ($Pokemon) {
$Types = $null
try {
$PokemonData = (Invoke-RestMethod "https://pokeapi.co/api/v2/pokemon/$($Pokemon.tolower())")
}
catch {
Write-Warning "Pokemon not valid: $Pokemon"
Write-Verbose "Note: you can use tab-completion with the Pokemon and Type parameters to ensure valid values." -Verbose
continue
}
if ($Generation) {
$PastGenTypes = $PokemonData.past_types
if ($PastGenTypes) {
$AvailableGenerations = $PastGenTypes.generation.url -replace ".*/(\d)/$", '$1' | Where-Object { $_ -ge $Generation } | Sort-Object
if ($AvailableGenerations) {
$Types = ($PastGenTypes | Where-Object { $_.generation.url -match "/$($AvailableGenerations[0])/$" }).types.type.name
}
}
}
if (!$Types) {
$Types = $PokemonData.types.type.name
}
$Dex = switch ($Generation) {
1 { "pokedex/$($PokemonData.id.tostring().padleft(3,"0")).shtml" }
2 { "pokedex-gs/$($PokemonData.id.tostring().padleft(3,"0")).shtml" }
3 { "pokedex-rs/$($PokemonData.id.tostring().padleft(3,"0")).shtml" }
4 { "pokedex-dp/$($PokemonData.id.tostring().padleft(3,"0")).shtml" }
5 { "pokedex-bw/$($PokemonData.id.tostring().padleft(3,"0")).shtml" }
6 { "pokedex-xy/$($PokemonData.id.tostring().padleft(3,"0")).shtml" }
7 { "pokedex-sm/$($PokemonData.id.tostring().padleft(3,"0")).shtml" }
8 { "pokedex-swsh/$($PokemonData.species.name)/" }
9 { "pokedex-sv/$($PokemonData.species.name)/" }
default { "pokemon/$($PokemonData.species.name)/" }
}
Write-Output ""
Write-Output "*** $((Get-Culture).TextInfo.ToTitleCase("$Pokemon is $($Types -join "/") type")) $(if($Generation){"[$(Convert-IntToRomanNumeral $Generation)] "})***"
([PSCustomObject]@{"Serebii Link" = "https://www.serebii.net/$Dex" } | Format-List | Out-String).trim()
Write-Output ""
if ($ExtraInfo) {
$PokemonInfoString = @()
$PokemonInfoString += "--- Stats ---"
$SpeciesInfo = Invoke-RestMethod $PokemonData.species.url
$StatTable = [PSCustomObject]@{
Introduced = "Generation-$(($SpeciesInfo.generation.name -replace ".*-(.*)", '$1').ToUpper())"
Genus = ($SpeciesInfo.genera | Where-Object { $_.language.name -eq "en" }).genus
BST = $($PokemonData.stats.base_stat | Measure-Object -Sum).Sum
"Weight (Lbs)" = [math]::Round((($PokemonData.weight / 10) * 2.20462262), 2)
"Weight Damage" = switch ($PokemonData.weight) {
{ 0..100 -contains $_ } { 20 ; break }
{ 101..250 -contains $_ } { 40 ; break }
{ 251..500 -contains $_ } { 60 ; break }
{ 501..1000 -contains $_ } { 80 ; break }
{ 1001..2000 -contains $_ } { 100 ; break }
{ $_ -gt 2000 } { 120 }
}
}
switch ($SpeciesInfo) {
{ $_.is_baby } {
Add-Member -InputObject $StatTable -NotePropertyName "Special Type" -NotePropertyValue "Baby"
break
}
{ $_.is_legendary } {
Add-Member -InputObject $StatTable -NotePropertyName "Special Type" -NotePropertyValue "Legendary"
break
}
{ $_.is_mythical } {
Add-Member -InputObject $StatTable -NotePropertyName "Special Type" -NotePropertyValue "Mythical"
break
}
}
if ($SpeciesInfo.varieties.count -gt 1) {
Add-Member -InputObject $StatTable -NotePropertyName "Alt-Forms" -NotePropertyValue $((Get-Culture).TextInfo.ToTitleCase($($SpeciesInfo.varieties | Where-Object { -not $_.is_default }).pokemon.name -join ", "))
}
if ($SpeciesInfo.evolves_from_species) {
Add-Member -InputObject $StatTable -NotePropertyName "Evolves From" -NotePropertyValue $((Get-Culture).TextInfo.ToTitleCase($SpeciesInfo.evolves_from_species.name))
}
$PokemonInfoString += ($StatTable | Format-List | Out-String).trim()
$PokemonInfoString += ""
$AbilityLine = "--- Abilities ---"
$AbilityTable = [PSCustomObject]@{}
foreach ($Ability in $PokemonData.abilities) {
$AbilityData = Invoke-RestMethod $Ability.ability.url
$AbilityName = $((Get-Culture).TextInfo.ToTitleCase(($Ability.ability.name -replace "-", " "))) + $(if ($Ability.is_hidden) { $HasHidden = $True; "*" })
if ($null -like $($AbilityData.effect_entries)) {
$AbilityValue = $($AbilityData.flavor_text_entries | Where-Object { $_.language.name -eq "en" }).flavor_text -replace "`n+", " "
}
else {
$AbilityValue = $($AbilityData.effect_entries | Where-Object { $_.language.name -eq "en" }).short_effect -replace "`n+", " "
}
Add-Member -InputObject $AbilityTable -NotePropertyName $AbilityName -NotePropertyValue $AbilityValue
}
$PokemonInfoString += $AbilityLine
$PokemonInfoString += ($AbilityTable | Format-List | Out-String).trim()
if ($HasHidden) {
$PokemonInfoString += Write-Output "[* = Hidden]"
}
Write-Output $PokemonInfoString
Write-Output ""
}
}
if ($Types) {
$DamageFrom = @{}
$DamageTo = @{}
foreach ($Type in $Types) {
$damage_relations = $null
Try {
$TypeData = Invoke-RestMethod "https://pokeapi.co/api/v2/type/$($type.tolower())"
}
catch {
Write-Warning "Type not valid: $Type"
Write-Verbose "Note: you can use tab-completion with the Pokemon and Type parameters to ensure valid values." -Verbose
continue
}
if ($Generation) {
$past_damage_relations = $TypeData.past_damage_relations
if ($past_damage_relations) {
$TargetGeneration = $past_damage_relations.generation.url -replace ".*/(\d)/$", '$1' | Where-Object { $_ -eq $Generation }
$EarlierGenerations = $past_damage_relations.generation.url -replace ".*/(\d)/$", '$1' | Where-Object { $_ -le $Generation } | Sort-Object -Descending
$LaterGenerations = $past_damage_relations.generation.url -replace ".*/(\d)/$", '$1' | Where-Object { $_ -gt $Generation } | Sort-Object -Descending
if ($TargetGeneration) {
$damage_relations = ($past_damage_relations | Where-Object { $_.generation.url -match "/$TargetGeneration/$" }).damage_relations
}
elseif ($EarlierGenerations -and $LaterGenerations) {
$damage_relations = ($past_damage_relations | Where-Object { $_.generation.url -match "/$($EarlierGenerations[0])/$" }).damage_relations
}
}
}
if (!$damage_relations) {
$damage_relations = $TypeData.damage_relations
}
foreach ($Property in $damage_relations.psobject.properties) {
$damage_relations.$($Property.name) = $Property.value.name
if ($Property.name -match "from$") {
$Mod = switch -Regex ($Property.name) {
"^double" { 2; break }
"^half" { .5; break }
"^no" { 0; break }
}
foreach ($DamageType in $damage_relations.$($Property.name)) {
if ($null -ne $DamageFrom[$DamageType]) {
$DamageFrom[$DamageType] = $DamageFrom[$DamageType] * $Mod
}
else {
$DamageFrom[$DamageType] = $Mod
}
}
}
}
$DamageTo += @{
$Type = $damage_relations | Select-Object "*_to"
}
}
$FromChart = [PSCustomObject]@{}
Foreach ($DamageType in $DamageInTypes) {
Add-Member -InputObject $FromChart -NotePropertyName $DamageType -NotePropertyValue @()
}
foreach ($Type in $DamageFrom.keys) {
switch ($DamageFrom[$Type]) {
1 { break }
2 { $FromChart."double_damage_from" += $Type ; break }
.5 { $FromChart."half_damage_from" += $Type ; break }
4 { $FromChart."quad_damage_from" += $Type ; break }
0 { $FromChart."no_damage_from" += $Type ; break }
.25 { $FromChart."quarter_damage_from" += $Type ; break }
}
}
$DamageIn = [PSCustomObject]@{}
foreach ($DamageType in $DamageInTypes) {
if ($FromChart.$DamageType -ne @()) {
Add-Member -InputObject $DamageIn -NotePropertyName (Get-Culture).TextInfo.ToTitleCase(($DamageType -replace "damage_from", "" -replace "_", " " -replace "no", "none")) -NotePropertyValue (Get-Culture).TextInfo.ToTitleCase(($FromChart.$DamageType -join ", "))
}
}
$StringOutput += (Get-Culture).TextInfo.ToTitleCase("--- $($Types -join "/") damage in $(if($Generation){"[$(Convert-IntToRomanNumeral $Generation)] "})---")
$StringOutput += ($DamageIn | Format-List | Out-String).trim()
$StringOutput += ""
Foreach ($Type in $DamageTo.keys) {
$StringOutput += (Get-Culture).TextInfo.ToTitleCase("--- $($Type) damage out $(if($Generation){"[$(Convert-IntToRomanNumeral $Generation)] "})---")
$DamageOut = [PSCustomObject]@{}
foreach ($DamageType in $DamageOutTypes) {
if ($null -ne $DamageTo[$Type].$DamageType) {
Add-Member -InputObject $DamageOut -NotePropertyName (Get-Culture).TextInfo.ToTitleCase(($DamageType -replace "damage_to", "" -replace "_", " " -replace "no", "none")) -NotePropertyValue (Get-Culture).TextInfo.ToTitleCase(($DamageTo[$Type].$DamageType -join ", "))
}
}
$StringOutput += ($DamageOut | Format-List | Out-String).trim()
$StringOutput += ""
}
}
return $StringOutput
}
function Convert-IntToRomanNumeral {
[CmdletBinding()]
param (
[Parameter(Mandatory = $true, Position = 0)]
[int]$Number
)
$numerals = @{
1000 = "M"
900 = "CM"
500 = "D"
400 = "CD"
100 = "C"
90 = "XC"
50 = "L"
40 = "XL"
10 = "X"
9 = "IX"
5 = "V"
4 = "IV"
1 = "I"
}
$result = ""
foreach ($key in ($numerals.Keys | Sort-Object -Descending)) {
while ($Number -ge $key) {
$result += $numerals[$key]
$Number -= $key
}
}
return $result
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment