Skip to content

Commit

Permalink
Merge pull request #1889 from ZombieFreak115/main
Browse files Browse the repository at this point in the history
Improving leader selection in land and naval battles
  • Loading branch information
schombert authored Feb 1, 2025
2 parents c97335b + 3dfe7d0 commit b6497e2
Show file tree
Hide file tree
Showing 2 changed files with 95 additions and 19 deletions.
110 changes: 92 additions & 18 deletions src/military/military.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4485,6 +4485,7 @@ void add_army_to_battle(sys::state& state, dcon::army_id a, dcon::land_battle_id

state.world.army_set_battle_from_army_battle_participation(a, b);
state.world.army_set_arrival_time(a, sys::date{}); // pause movement
update_battle_leaders(state, b);
}

void army_arrives_in_province(sys::state& state, dcon::army_id a, dcon::province_id p, crossing_type crossing, dcon::land_battle_id from) {
Expand Down Expand Up @@ -4687,6 +4688,7 @@ void add_navy_to_battle(sys::state& state, dcon::navy_id n, dcon::naval_battle_i
for(auto em : state.world.navy_get_army_transport(n)) {
em.get_army().set_arrival_time(sys::date{});
}
update_battle_leaders(state, b);
}

bool retreat(sys::state& state, dcon::navy_id n) {
Expand Down Expand Up @@ -4836,18 +4838,25 @@ dcon::nation_id get_land_battle_lead_defender(sys::state& state, dcon::land_batt
return dcon::nation_id{};
}

float get_leader_select_score(sys::state& state, dcon::leader_id l) {
float get_leader_select_score(sys::state& state, dcon::leader_id l, bool is_attacking) {
/*
- Each side has a leader that is in charge of the combat, which is the leader with the greatest
value as determined by the following formula: (organization x 5 + attack + defend + morale +
speed + attrition + experience / 2 + reconnaissance / 5 + reliability / 5) x (prestige + 1)
*/
auto per = state.world.leader_get_personality(l);
auto bak = state.world.leader_get_background(l);
//
// atk and def are both set to 0 initally, and will be set to its actual amount depending if on the on_attacking input param
// this makes it so for example if the leader is attacking, it will disregard all "defence" stats for the purposes of calculating the score
float atk = 0.f;
float def = 0.f;
auto org = state.world.leader_trait_get_organisation(per) + state.world.leader_trait_get_organisation(bak);
auto atk = state.world.leader_trait_get_attack(per) + state.world.leader_trait_get_attack(bak);
auto def = state.world.leader_trait_get_defense(per) + state.world.leader_trait_get_defense(bak);
if(is_attacking) {
atk = state.world.leader_trait_get_attack(per) + state.world.leader_trait_get_attack(bak);
}
else {
def = state.world.leader_trait_get_defense(per) + state.world.leader_trait_get_defense(bak);
}
auto spd = state.world.leader_trait_get_speed(per) + state.world.leader_trait_get_speed(bak);
auto mor = state.world.leader_trait_get_morale(per) + state.world.leader_trait_get_morale(bak);
auto att = state.world.leader_trait_get_experience(per) + state.world.leader_trait_get_experience(bak);
Expand All @@ -4857,22 +4866,81 @@ float get_leader_select_score(sys::state& state, dcon::leader_id l) {
auto lp = state.world.leader_get_prestige(l);
return (org * 5.f + atk + def + mor + spd + att + exp / 2.f + rec / 5.f + rel / 5.f) * (lp + 1.f);
}

bool is_attacker_in_battle(sys::state& state, dcon::army_id a) {
assert(state.world.army_get_battle_from_army_battle_participation(a)); // make sure the army is actually in a battle
auto battle = state.world.army_get_battle_from_army_battle_participation(a);
auto lead_attacker = get_land_battle_lead_attacker(state, battle);
auto lead_defender = get_land_battle_lead_defender(state, battle);
auto thisnation = state.world.army_get_controller_from_army_control(a);
bool war_attacker = state.world.land_battle_get_war_attacker_is_attacker(battle);
// country vs country
if(lead_attacker && lead_defender) {
for(const auto par : state.world.nation_get_war_participant(thisnation)) {
if((par.get_is_attacker() && war_attacker) || (!par.get_is_attacker() && !war_attacker)) {
return true;
}
else if((!par.get_is_attacker() && war_attacker) || (par.get_is_attacker() && !war_attacker)) {
return false;
}
}
return false;
}
// country vs rebels
else {
// if the "this" nation is the rebels, and they are attacking
if(!thisnation) {
return war_attacker;
}
// if the "this" nation is not rebels
else {
return !war_attacker;
}
}
}

bool is_attacker_in_battle(sys::state& state, dcon::navy_id a) {
assert(state.world.navy_get_battle_from_navy_battle_participation(a)); // make sure the army is actually in a battle
auto battle = state.world.navy_get_battle_from_navy_battle_participation(a);
auto thisnation = state.world.navy_get_controller_from_navy_control(a);
// country vs country
bool war_attacker = state.world.naval_battle_get_war_attacker_is_attacker(battle);
for(const auto par : state.world.nation_get_war_participant(thisnation)) {
if((par.get_is_attacker() && war_attacker) || (!par.get_is_attacker() && !war_attacker)) {
return true;
} else if((!par.get_is_attacker() && war_attacker) || (par.get_is_attacker() && !war_attacker)) {
return false;
}
}
return false;

}


void update_battle_leaders(sys::state& state, dcon::land_battle_id b) {
auto la = get_land_battle_lead_attacker(state, b);
/*auto la = get_land_battle_lead_attacker(state, b);*/
dcon::leader_id a_lid;
float a_score = 0.f;
auto ld = get_land_battle_lead_defender(state, b);
// starting a_score and d_score set to low number, so no-leader won't take over sometimes even if there is a leader available
float a_score = -999.f;
/*auto ld = get_land_battle_lead_defender(state, b);*/
dcon::leader_id d_lid;
float d_score = 0.f;
float d_score = -999.f;
for(const auto a : state.world.land_battle_get_army_battle_participation(b)) {
auto l = a.get_army().get_general_from_army_leadership();
auto score = get_leader_select_score(state, l);
if(a.get_army().get_controller_from_army_control() == la) {
// if its no leader, skip
if(!l.is_valid()) {
continue;
}
bool is_attacking = is_attacker_in_battle(state, a.get_army());
auto score = get_leader_select_score(state, l, is_attacking);
/*if(a.get_army().get_controller_from_army_control() == la) {*/
if(is_attacking) {
if(score > a_score) {
a_lid = l;
a_score = score;
}
} else if(a.get_army().get_controller_from_army_control() == ld) {
} /*else if(a.get_army().get_controller_from_army_control() == ld) {*/
else if(!is_attacking) {
if(score > d_score) {
d_lid = l;
d_score = score;
Expand All @@ -4885,21 +4953,27 @@ void update_battle_leaders(sys::state& state, dcon::land_battle_id b) {
state.world.defending_general_set_general(ab, d_lid);
}
void update_battle_leaders(sys::state& state, dcon::naval_battle_id b) {
auto la = get_naval_battle_lead_attacker(state, b);
/*auto la = get_naval_battle_lead_attacker(state, b);*/
dcon::leader_id a_lid;
float a_score = 0.f;
auto ld = get_naval_battle_lead_defender(state, b);
// starting a_score and d_score set to low number, so no-leader won't take over sometimes even if there is a leader available
float a_score = -999.f;
/*auto ld = get_naval_battle_lead_defender(state, b);*/
dcon::leader_id d_lid;
float d_score = 0.f;
float d_score = -999.f;
for(const auto a : state.world.naval_battle_get_navy_battle_participation(b)) {
auto l = a.get_navy().get_admiral_from_navy_leadership();
auto score = get_leader_select_score(state, l);
if(a.get_navy().get_controller_from_navy_control() == la) {
// if its no leader, skip
if(!l.is_valid()) {
continue;
}
bool is_attacking = is_attacker_in_battle(state, a.get_navy());
auto score = get_leader_select_score(state, l, is_attacking);
if(is_attacking) {
if(score > a_score) {
a_lid = l;
a_score = score;
}
} else if(a.get_navy().get_controller_from_navy_control() == ld) {
} else if(!is_attacking) {
if(score > d_score) {
d_lid = l;
d_score = score;
Expand Down
4 changes: 3 additions & 1 deletion src/military/military.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -496,7 +496,9 @@ dcon::nation_id get_land_battle_lead_defender(sys::state& state, dcon::land_batt
dcon::nation_id get_naval_battle_lead_defender(sys::state& state, dcon::naval_battle_id b);
dcon::nation_id get_naval_battle_lead_attacker(sys::state& state, dcon::naval_battle_id b);

float get_leader_select_score(sys::state& state, dcon::leader_id l);
float get_leader_select_score(sys::state& state, dcon::leader_id l, bool is_attacking);
bool is_attacker_in_battle(sys::state& state, dcon::army_id a);
bool is_attacker_in_battle(sys::state& state, dcon::navy_id a);
void update_battle_leaders(sys::state& state, dcon::land_battle_id b);
void update_battle_leaders(sys::state& state, dcon::naval_battle_id b);

Expand Down

0 comments on commit b6497e2

Please sign in to comment.