/****************************************************************************
* 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 . *
****************************************************************************/
/**
* @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
#include
#include
#include
#include
#include
#include
#include
#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;
}