File size: 3,655 Bytes
6c7a453
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
import random
from typing import Any, Dict, Optional

from .smogon_oracle import smogon_damage_oracle


STATUS_TO_SMOGON = {
    "burn": "brn",
    "paralysis": "par",
    "poison": "psn",
    "toxic": "tox",
    "sleep": "slp",
    "freeze": "frz",
}

WEATHER_TO_SMOGON = {
    "raindance": "Rain",
    "sunnyday": "Sun",
    "sandstorm": "Sand",
    "hail": "Snow",
    "snow": "Snow",
    "primordialsea": "Primordial Sea",
    "desolateland": "Desolate Land",
    "deltastream": "Delta Stream",
    "none": None,
}

STAT_TO_SMOGON = {
    "attack": "atk",
    "defense": "def",
    "special_attack": "spa",
    "special_defense": "spd",
    "speed": "spe",
}


def _clean_name(value: Any) -> Optional[str]:
    if not value:
        return None
    text = str(value)
    if text.lower().replace(" ", "").replace("-", "") in {"", "none", "noability", "unknown"}:
        return None
    return text


def _normalize_spread(values: Dict[str, int] | None, default: int) -> Dict[str, int]:
    values = values or {}
    result = {}
    for key in ["hp", "atk", "def", "spa", "spd", "spe"]:
        long_key = {
            "atk": "attack",
            "def": "defense",
            "spa": "special_attack",
            "spd": "special_defense",
            "spe": "speed",
        }.get(key, key)
        result[key] = int(values.get(key, values.get(long_key, default)))
    return result


def _pokemon_payload(pokemon) -> Dict[str, Any]:
    ability = _clean_name(getattr(getattr(pokemon, "ability", None), "name", None))
    item = _clean_name(getattr(pokemon, "item", None))
    boosts = {}
    for stat, stage in getattr(pokemon, "stat_stages", {}).items():
        key = STAT_TO_SMOGON.get(stat)
        if key:
            boosts[key] = int(stage)

    return {
        "species": getattr(pokemon, "name", "Pikachu"),
        "level": getattr(pokemon, "level", 100),
        "ability": ability,
        "item": item,
        "nature": getattr(pokemon, "nature", "Hardy"),
        "evs": _normalize_spread(getattr(pokemon, "evs", {}), 0),
        "ivs": _normalize_spread(getattr(pokemon, "ivs", {}), 31),
        "boosts": boosts,
        "status": STATUS_TO_SMOGON.get(getattr(pokemon, "major_status", None)),
    }


def smogon_damage_for_move(attacker, defender, move, field: Dict[str, Any] | None = None) -> Optional[Dict[str, Any]]:
    if getattr(move, "fixed_damage", None) or getattr(move, "ohko", False):
        return None
    if getattr(move, "category", "status") == "status" or getattr(move, "power", 0) <= 0:
        return None

    field = field or {}
    gen = field.get("gen", 9)
    
    # Mega Evolution is not standard in Gen 9, use Gen 7 for Megas to ensure support
    attacker_name = getattr(attacker, "name", "").lower()
    defender_name = getattr(defender, "name", "").lower()
    if "-mega" in attacker_name or "-mega" in defender_name:
        gen = 7

    payload = {
        "gen": gen,
        "attacker": _pokemon_payload(attacker),
        "defender": _pokemon_payload(defender),
        "move": {"name": getattr(move, "name", "")},
        "field": {
            "weather": WEATHER_TO_SMOGON.get(field.get("weather"), field.get("weather")),
            "terrain": field.get("terrain"),
            "isReflect": bool(field.get("is_reflect")),
            "isLightScreen": bool(field.get("is_light_screen")),
            "isAuroraVeil": bool(field.get("is_aurora_veil")),
        },
    }
    result = smogon_damage_oracle.calculate(payload)
    if not result or result.get("error") or not result.get("damage"):
        return None
    result["selected_damage"] = random.choice(result["damage"])
    return result