/**************************************************************************** * Copyright (C) 2015-2017 by the SotS Team * * * * This file is part of Sovereign of the Skies. * * * * Sovereign of the Skies is free software: you can redistribute it * * and/or modify it * * under the terms of the GNU Lesser General Public License as published * * by the Free Software Foundation, either version 3 of the License, or * * (at your option) any later version provided you include a copy of the * * licence and this header. * * * * Sovereign of the Skies is distributed in the hope that it will be * * useful, but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU Lesser General Public License for more details. * * * * You should have received a copy of the GNU Lesser General Public * * License along with Sovereign of the Skies. * * If not, see <http://www.gnu.org/licenses/>. * ****************************************************************************/ /** * @file evolution_methods.c * @author Sturmvogel * @date 18 apr 2017 * @brief decide if a pokémon is able to evolve and what the consequences are. * */ #include <game_engine.h> #include <pkmn_attributes.h> #include <agb_debug.h> #include <math.h> #include <config.h> #include <moves.h> #include <pokemon.h> #include <pkmn_types.h> #define EVO_NULL \ { \ 0, 0, 0, { false, 0 } \ } #define MAX_EVOLUTIONS 5 #define EVO_NO_EVO \ (struct evo_result) { false, false, 0 } #define HAPPY_BOUND 219 #define BEAUTY_BOUND 170 #define GENDER_DC 0 #define GENDER_MALE 1 #define GENDER_FEMALE 2 #define EVO_HAPPINESS 1 #define EVO_LEVEL_UP 4 #define EVO_TRADE 5 #define EVO_TRADE_ITEM 6 #define EVO_STONE 7 #define EVO_ATK 8 #define EVO_DEF 9 #define EVO_ADEQU 10 #define EVO_PERSO_HIGH 11 #define EVO_PERSO_LOW 12 #define EVO_SPAWN 13 #define EVO_SPAWNED 14 #define EVO_BEAUTY 15 #define EVO_WEAR_ITEM 16 #define EVO_WEAR_ITEM_NIGHT 17 #define EVO_WEAR_ITEM_DAY 18 #define EVO_LEVEL_NIGHT 19 #define EVO_LEVEL_DAY 20 #define EVO_LEVEL_VAR 21 #define EVO_LEVEL_MOVE 22 #define EVO_LEVEL_POKEMON 23 #define EVO_LEVEL_TYPE 24 #define EVO_LEVEL_MOVE_TYPE 25 #define EVO_MEGA_ONE 26 #define EVO_MEGA_TWO 27 #define EVO_PROTO 28 enum evo_source { LEVEL_UP, TRADE, STONE_EVOLUTION, STONE_REQUEST }; struct evo_information { u16 method; u16 argument; u16 evolve_to; struct { u16 gender : 2; u16 versatile : 14; } argument_2; }; struct evo_result { u8 can_evolve; u8 consume_item; u8 evolve_to; }; struct evo_information evolutions[][MAX_EVOLUTIONS] = { {EVO_NULL, EVO_NULL, EVO_NULL, EVO_NULL, EVO_NULL}, //Nothing {{EVO_LEVEL_MOVE_TYPE, 7, 20, {GENDER_DC, TYPE_GRASS}}, EVO_NULL, EVO_NULL, EVO_NULL, EVO_NULL}, //BISASAM {{EVO_LEVEL_UP, 32, 3, {GENDER_DC, 0}}, EVO_NULL, EVO_NULL, EVO_NULL, EVO_NULL}, //BISAKNOSP }; struct evo_call_arguments { u16 item; u16 level; enum evo_source source; u16 stoneId; struct pokemon *poke; struct evo_information evolution; }; typedef struct evo_result (*evolution_callback)(struct evo_call_arguments); struct evo_result evolve_by_level(struct evo_call_arguments arguments) { u8 gender = pokemon_get_gender(arguments.poke); u8 gender_arg = arguments.evolution.argument_2.gender; dprintf("A pokemon with gender value %d\n", gender); if (gender_arg == 1) { if (gender) return EVO_NO_EVO; } if (gender_arg == 2) { if (!gender) return EVO_NO_EVO; } if (arguments.evolution.argument <= arguments.level && arguments.source == LEVEL_UP) { return (struct evo_result){true, false, arguments.evolution.evolve_to}; } else { return (struct evo_result){false, false, 0}; } } struct evo_result evolve_by_trade_group(struct evo_call_arguments arguments) { if (arguments.source != TRADE) { return EVO_NO_EVO; } if (arguments.evolution.method == EVO_TRADE_ITEM) { if (arguments.item != arguments.evolution.argument) return (struct evo_result){false, false, 0}; else return (struct evo_result){true, true, arguments.evolution.evolve_to}; } else if (arguments.evolution.method == EVO_TRADE) { return (struct evo_result){true, false, arguments.evolution.evolve_to}; } else { dprintf("An invalid trade group method was reached in \"evolve_by_trade_group\"\nmethod: %d", arguments.evolution.method); return (struct evo_result){false, false, 0}; } } struct evo_result evolve_random(struct evo_call_arguments arguments) { if (arguments.source != LEVEL_UP) { return EVO_NO_EVO; } u32 pid = pokemon_get_attribute(arguments.poke, ATTR_PID, NULL); pid = pid & 0xFFFF; u8 mod = (pid % 10); dprintf("A pokemon tries to evolve at random: pid: %d, low: %d, mod: %d\n", pid, pid, mod); if (mod >= 5) { if (arguments.evolution.method == EVO_PERSO_HIGH) return (struct evo_result){true, false, arguments.evolution.evolve_to}; else return EVO_NO_EVO; } else { if (arguments.evolution.method == EVO_PERSO_LOW) return (struct evo_result){true, false, arguments.evolution.evolve_to}; else return EVO_NO_EVO; } } struct evo_result evolve_by_stone(struct evo_call_arguments arguments) { u8 gender = pokemon_get_gender(arguments.poke); u8 gender_arg = arguments.evolution.argument_2.gender; if (gender_arg == 1) { if (gender) return EVO_NO_EVO; } if (gender_arg == 2) { if (!gender) return EVO_NO_EVO; } if (arguments.source != STONE_EVOLUTION && arguments.source != STONE_REQUEST) { return (struct evo_result){false, false, 0}; } if (arguments.stoneId == arguments.evolution.argument) { return (struct evo_result){true, false, arguments.evolution.evolve_to}; } else { return (struct evo_result){false, false, 0}; } } struct evo_result evolve_by_pokemon(struct evo_call_arguments arguments) { u8 has_required_pokemon = false; u16 species_required = arguments.evolution.argument_2.versatile; dprintf("Required: %d\n", species_required); for (int i = 0; i < 6; ++i) { u16 current_species = pokemon_get_attribute(&(pokemon_party_player[i]), ATTR_SPECIES, NULL); dprintf("Found pkmn: %d\n", current_species); if (current_species == species_required) { has_required_pokemon = true; break; } } if (!has_required_pokemon) return EVO_NO_EVO; return evolve_by_level(arguments); } struct evo_result evolve_by_atk_def(struct evo_call_arguments arguments) { u32 atk = pokemon_get_attribute(arguments.poke, ATTR_ATTACK, NULL); u32 def = pokemon_get_attribute(arguments.poke, ATTR_DEFENCE, NULL); dprintf("A pokemon wants to evolve by atk and def.\n"); dprintf("Level required: %d, pkmn level: %d, pkmn atk: %d, pkmn def: %d\n", arguments.evolution.argument, arguments.level, atk, def); if (arguments.evolution.method == EVO_ATK) { if (atk > def) return evolve_by_level(arguments); else return EVO_NO_EVO; } if (arguments.evolution.method == EVO_DEF) { if (def > atk) return evolve_by_level(arguments); else return EVO_NO_EVO; } if (arguments.evolution.method == EVO_ADEQU) { if (atk == def) return evolve_by_level(arguments); else return EVO_NO_EVO; } dprintf("invalid atk and def evo code reached.\n"); return EVO_NO_EVO; } struct evo_result evolve_by_type(struct evo_call_arguments arguments) { u8 has_required_pokemon = false; u16 type_required = arguments.evolution.argument_2.versatile; dprintf("Required: %d\n", type_required); for (int i = 0; i < 6; ++i) { u16 current_species = pokemon_get_attribute(&(pokemon_party_player[i]), ATTR_SPECIES, NULL); if (current_species == 0) continue; u8 type_one = pokemon_base_stats[current_species].type_one; u8 type_two = pokemon_base_stats[current_species].type_two; dprintf("Found type: %d/%d\n", type_one, type_two); if (type_one == type_required || type_two == type_required) { has_required_pokemon = true; break; } } if (!has_required_pokemon) return EVO_NO_EVO; return evolve_by_level(arguments); } struct evo_result evolve_by_happiness(struct evo_call_arguments arguments) { u32 happiness = pokemon_get_attribute(arguments.poke, ATTR_HAPPINESS, NULL); dprintf("A pokemon wants to evolve by happiness.\n"); dprintf("Happiness value: %d; needed: %d\n", happiness, HAPPY_BOUND); if ((happiness > HAPPY_BOUND) && (arguments.source == LEVEL_UP)) { return (struct evo_result){true, false, arguments.evolution.evolve_to}; } else { return (struct evo_result){false, false, 0}; } } struct evo_result evolve_by_special_place(struct evo_call_arguments arguments) { u16 value = var_get(EVO_VAR); dprintf("A pokemon tried to evolve using the var evo method: value: %d needed: %d\n", value, arguments.evolution.argument_2.versatile); if (arguments.evolution.argument_2.versatile != value) { return EVO_NO_EVO; } return evolve_by_level(arguments); } struct evo_result evolve_by_beauty(struct evo_call_arguments arguments) { u32 beauty = pokemon_get_attribute(arguments.poke, ATTR_BEAUTY, NULL); dprintf("A pokemon tires to evolve by beauty: value: %d; required: %d.\n", beauty, BEAUTY_BOUND); if (beauty > BEAUTY_BOUND && arguments.source == LEVEL_UP) return evolve_by_level(arguments); return EVO_NO_EVO; } struct evo_result evolve_by_worn_item(struct evo_call_arguments arguments) { struct evo_result level_result = evolve_by_level(arguments); if (!level_result.can_evolve) return EVO_NO_EVO; u16 item_to_wear = arguments.evolution.argument_2.versatile; dprintf("A pokemon tried to evolve by item. pkmn_item: %d; argument item: %d", arguments.item, item_to_wear); if (arguments.item != item_to_wear) return EVO_NO_EVO; return (struct evo_result){true, false, arguments.evolution.evolve_to}; } struct evo_result evolve_by_worn_item_day(struct evo_call_arguments arguments) { dprintf("A pokemon tried to use the (not implmented) evolve_by_worn_item_day evo method.\n"); return evolve_by_worn_item(arguments); } struct evo_result evolve_by_worn_item_night(struct evo_call_arguments arguments) { dprintf("A pokemon tried to use the (not implmented) evolve_by_worn_item_night evo method.\n"); return evolve_by_worn_item(arguments); } struct evo_result evolve_by_move(struct evo_call_arguments arguments) { u16 move_one = pokemon_get_attribute(arguments.poke, ATTR_ATTACK_1, NULL); u16 move_two = pokemon_get_attribute(arguments.poke, ATTR_ATTACK_2, NULL); u16 move_three = pokemon_get_attribute(arguments.poke, ATTR_ATTACK_3, NULL); u16 move_four = pokemon_get_attribute(arguments.poke, ATTR_ATTACK_4, NULL); u16 move_needed = arguments.evolution.argument_2.versatile; dprintf("A pokemon tried to evolve using evolve_by_move: needed: %d, one: %d, two: %d, three: %d, four: %d\n", move_needed, move_one, move_two, move_three, move_four); if ((move_one == move_needed) || (move_two == move_needed) || (move_three == move_needed) || (move_four == move_needed)) return evolve_by_level(arguments); else return EVO_NO_EVO; } struct evo_result evolve_by_move_type(struct evo_call_arguments arguments) { u16 needed_type = arguments.evolution.argument_2.versatile; u8 knows_required_move = false; for (int i = ATTR_ATTACK_1; i <= ATTR_ATTACK_4; ++i) { u16 current_move = pokemon_get_attribute(arguments.poke, i, NULL); if (current_move == 0) continue; u8 current_type = move_table[current_move].type; dprintf("found move type: %d on move %d\n", current_type, current_move); if (current_type == needed_type) { knows_required_move = true; break; } } if (knows_required_move) return evolve_by_level(arguments); else return EVO_NO_EVO; } struct evo_result evolve_no_method(struct evo_call_arguments arguments) { //For shedninja return EVO_NO_EVO; } struct evo_result evolve_invalid_method(struct evo_call_arguments arguments) { dprintf("A pokemon tried to execute an evolution method that is not yet implemented.\n"); return (struct evo_result){false, false, 0}; } static evolution_callback evolution_methods[] = { evolve_invalid_method, //Method 0 INVALID evolve_by_happiness, //Method 1 evolve_invalid_method, //TODO: Happiness DAY Method 2 evolve_invalid_method, //TODO: Happiness NIGHT Method 3 evolve_by_level, //Method 4 evolve_by_trade_group, //Method 5 evolve_by_trade_group, //Method 6 evolve_by_stone, //Method 7 evolve_by_atk_def, //Method 8 evolve_by_atk_def, //Method 9 evolve_by_atk_def, //Method 10 evolve_random, //Method 11 evolve_random, //Method 12 evolve_by_level, //Shedninja SPAWN Method 13 evolve_no_method, //Shedninja SPAWNED //Method 14 evolve_by_beauty, //Method 15 evolve_by_worn_item, //Method 16 evolve_by_worn_item_day, //TODO implement day Method 17 evolve_by_worn_item_night, //TODO implement night Method 18 evolve_invalid_method, //TODO implement level night Method 19 evolve_invalid_method, //TODO implement level day Method 20 evolve_by_special_place, //Method 21 evolve_by_move, //Method 22 evolve_by_pokemon, //Method 23 evolve_by_type, //Method 24 evolve_by_move_type, //Method 25 evolve_no_method, //Method 26 evolve_no_method, //Method 26 evolve_no_method, //Method 26 evolve_no_method, //Method 26 }; u16 evolution_try_evolve(struct pokemon *pokemon, enum evo_source source, u16 stoneId) { u16 held_item = pokemon_get_attribute(pokemon, ATTR_HELD_ITEM, NULL); u16 species = pokemon_get_attribute(pokemon, ATTR_SPECIES, NULL); u16 level = pokemon_get_attribute(pokemon, ATTR_LEVEL, NULL); dprintf("Species %d tried to evolve.\n", species); dprintf("Cause: %d\n", source); if (species > 2) { dprintf("Currently no evolution possible due to short table!\n"); return 0; } struct evo_information *current_evolution_structure = evolutions[species]; struct evo_result result; for (int i = 0; i < MAX_EVOLUTIONS; ++i) { if (current_evolution_structure[i].method != 0) { dprintf("found valid evolution with method %d for species %d\n", current_evolution_structure[i].method, species); struct evo_call_arguments args = {held_item, level, source, stoneId, pokemon, current_evolution_structure[i]}; result = evolution_methods[current_evolution_structure[i].method](args); if (result.can_evolve) break; } } if (result.can_evolve) { if (result.consume_item) { u16 zero = 0; pokemon_set_attribute(pokemon, ATTR_HELD_ITEM, &zero); } return result.evolve_to; } else return 0; }