Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,18 @@ All notable changes to OLManager will be documented in this file.

The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project uses GPL-3.0 licensing inherited from the OpenFootManager lineage unless otherwise documented.

## [0.2.1] - 2026-05-13

### Added

- Added profile image URL support to player data and related frontend components.
- Added SoloQ rank and staff impact localisation strings across supported languages.

### Changed

- Improved transfer handling in squad flows, including roster destination behaviour and related transfer tests.
- Improved player role resolution so active lineup role data and natural position are handled consistently across live match, dashboard, squad and training views.

## [0.2.0] - 2026-05-07

### Added
Expand Down
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "openleaguemanager",
"private": true,
"version": "0.2.0",
"version": "0.2.1",
"type": "module",
"scripts": {
"dev": "vite",
Expand Down
2 changes: 1 addition & 1 deletion src-tauri/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion src-tauri/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "openleaguemanager"
version = "0.2.0"
version = "0.2.1"
description = "Open League Manager"
authors = ["KOI Noboris Development Team <noboriskoi@gmail.com>"]
repository = "https://github.com/OpenLeagueManager/OLManager"
Expand Down
3 changes: 3 additions & 0 deletions src-tauri/crates/engine/src/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,9 @@ pub enum PlayStyle {
pub struct PlayerData {
pub id: String,
pub name: String,
/// Optional explicit player portrait URL carried from the domain roster.
#[serde(default)]
pub profile_image_url: Option<String>,
/// Player's LoL role (Top, Jungle, Mid, Adc, Support)
pub role: LolRole,
pub condition: u8, // 0-100
Expand Down
2 changes: 2 additions & 0 deletions src-tauri/crates/engine/tests/live_match_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ fn make_player(id: &str, name: &str, pos: &str, skill: u8) -> PlayerData {
PlayerData {
id: id.to_string(),
name: name.to_string(),
profile_image_url: None,
role: football_position_to_lol_role(pos),
condition: 90,
fitness: 75,
Expand Down Expand Up @@ -966,6 +967,7 @@ fn make_player_with_traits(
PlayerData {
id: id.to_string(),
name: name.to_string(),
profile_image_url: None,
role: football_position_to_lol_role(pos),
condition: 90,
fitness: 75,
Expand Down
1 change: 1 addition & 0 deletions src-tauri/crates/engine/tests/simulation_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ fn make_player(id: &str, name: &str, position: &str, skill: u8) -> PlayerData {
PlayerData {
id: id.to_string(),
name: name.to_string(),
profile_image_url: None,
role: football_position_to_lol_role(position),
condition: 90,
fitness: 75,
Expand Down
167 changes: 155 additions & 12 deletions src-tauri/crates/ofm_core/src/champions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,46 @@ fn normalize_key(value: &str) -> String {
.replace(|ch: char| !ch.is_ascii_alphanumeric(), "")
}

fn role_for_lineup_index(index: usize) -> Option<domain::player::LolRole> {
match index {
0 => Some(domain::player::LolRole::Top),
1 => Some(domain::player::LolRole::Jungle),
2 => Some(domain::player::LolRole::Mid),
3 => Some(domain::player::LolRole::Adc),
4 => Some(domain::player::LolRole::Support),
_ => None,
}
}

fn role_label_for_position(pos: domain::player::LolRole) -> &'static str {
match pos {
domain::player::LolRole::Top => "Top",
domain::player::LolRole::Jungle => "Jungle",
domain::player::LolRole::Mid => "Mid",
domain::player::LolRole::Adc => "ADC",
domain::player::LolRole::Support => "Support",
domain::player::LolRole::Unknown => "Unknown",
}
}

fn current_role_for_player(
game: &Game,
team_id: &str,
player_id: &str,
natural_position: domain::player::LolRole,
) -> domain::player::LolRole {
game.teams
.iter()
.find(|team| team.id == team_id)
.and_then(|team| {
team.active_lineup_ids
.iter()
.position(|id| id == player_id)
.and_then(role_for_lineup_index)
})
.unwrap_or(natural_position)
}

fn normalize_role(value: &str) -> Option<String> {
match normalize_key(value).as_str() {
"top" => Some("Top".to_string()),
Expand Down Expand Up @@ -789,17 +829,6 @@ pub fn delegate_champion_training_to_coach(game: &mut Game) -> Result<usize, Str
}
};

let role_for_position = |pos: &domain::player::LolRole| -> String {
match pos {
domain::player::LolRole::Top => "Top".to_string(),
domain::player::LolRole::Jungle => "Jungle".to_string(),
domain::player::LolRole::Mid => "Mid".to_string(),
domain::player::LolRole::Adc => "ADC".to_string(),
domain::player::LolRole::Support => "Support".to_string(),
domain::player::LolRole::Unknown => "Unknown".to_string(),
}
};

// Collect all meta entries upfront
let meta_entries: Vec<ChampionMetaEntry> = game.champion_patch.hidden_meta.clone();

Expand Down Expand Up @@ -832,7 +861,12 @@ pub fn delegate_champion_training_to_coach(game: &mut Game) -> Result<usize, Str

for player_id in player_ids {
let player = game.players.iter().find(|p| p.id == player_id).unwrap();
let role = role_for_position(&player.natural_position);
let role = role_label_for_position(current_role_for_player(
game,
&manager_team_id,
&player_id,
player.natural_position,
));

let role_meta: Vec<&ChampionMetaEntry> = meta_entries
.iter()
Expand Down Expand Up @@ -1396,3 +1430,112 @@ pub fn process_daily_champion_system(game: &mut Game) {

process_meta_discovery(game);
}

#[cfg(test)]
mod tests {
use super::*;
use crate::clock::GameClock;
use chrono::Utc;
use domain::manager::Manager;
use domain::player::{LolRole, Player, PlayerAttributes};
use domain::team::Team;

fn attrs() -> PlayerAttributes {
PlayerAttributes {
pace: 60,
mental_resilience: 60,
strength: 60,
champion_pool: 60,
passing: 60,
laning: 60,
tackling: 60,
mechanics: 60,
defending: 60,
positioning: 60,
macro_play: 60,
consistency: 60,
discipline: 60,
aggression: 60,
teamfighting: 60,
shotcalling: 60,
handling: 20,
reflexes: 20,
aerial: 20,
}
}

fn game_with_lineup(lineup: Vec<&str>) -> Game {
let mut manager = Manager::new(
"manager-1".to_string(),
"Jane".to_string(),
"Manager".to_string(),
"1980-01-01".to_string(),
"ES".to_string(),
);
manager.hire("team-1".to_string());

let mut team = Team::new(
"team-1".to_string(),
"Team One".to_string(),
"ONE".to_string(),
"ES".to_string(),
"Madrid".to_string(),
"Arena".to_string(),
10_000,
);
team.active_lineup_ids = lineup.into_iter().map(str::to_string).collect();

Game::new(
GameClock::new(Utc::now()),
manager,
vec![team],
Vec::new(),
Vec::new(),
Vec::new(),
)
}

#[test]
fn current_role_for_player_uses_active_lineup_slot_before_natural_role() {
let mut game = game_with_lineup(vec!["new-top", "jungle", "mid", "adc", "support"]);
let mut player = Player::new(
"new-top".to_string(),
"New Top".to_string(),
"New Top".to_string(),
"2000-01-01".to_string(),
"ES".to_string(),
LolRole::Support,
attrs(),
);
player.team_id = Some("team-1".to_string());
game.players.push(player.clone());

expect_role(&game, &player, LolRole::Top);
}

#[test]
fn current_role_for_player_keeps_bench_player_natural_role() {
let mut game = game_with_lineup(vec!["top", "jungle", "mid", "adc", "support"]);
let mut player = Player::new(
"bench-support".to_string(),
"Bench Support".to_string(),
"Bench Support".to_string(),
"2000-01-01".to_string(),
"ES".to_string(),
LolRole::Support,
attrs(),
);
player.team_id = Some("team-1".to_string());
game.players.push(player.clone());

expect_role(&game, &player, LolRole::Support);
}

fn expect_role(game: &Game, player: &Player, expected: LolRole) {
let team_id = player.team_id.as_deref().unwrap();
assert_eq!(
current_role_for_player(game, team_id, &player.id, player.natural_position),
expected,
);
}
}
Loading
Loading