// ========================================================================= // 5. SPECIAL: Player Profile - /profile/ // ========================================================================= add_shortcode('otg_player_profile', 'otg_render_player_profile'); function otg_render_player_filter_dropdown() { global $wpdb; // Fetch all players sorted alphabetically $all_players = $wpdb->get_results(" SELECT ID, Name FROM otg_players ORDER BY Name ASC "); $current_player_id = isset($_GET['player_id']) ? intval($_GET['player_id']) : 0; $html = '
'; $html .= '
'; $html .= ''; $html .= '
'; $html .= '
'; return $html; } function otg_render_player_profile($atts) { global $wpdb; $a = shortcode_atts(array('id' => 0), $atts, 'otg_player_profile'); $player_id = isset($_GET['player_id']) ? intval($_GET['player_id']) : intval($a['id']); // Always render the player selection dropdown box at the top $html = otg_render_player_filter_dropdown(); if (!$player_id) { return $html . "Player ID required."; } $p = $wpdb->get_row($wpdb->prepare("SELECT * FROM otg_players WHERE ID = %d", $player_id)); if (!$p) return $html . "Player not found."; $game_type = isset($_GET['game_type']) ? intval($_GET['game_type']) : 2; // Updated CSS: Simplified background rules for NYR (dark blue), NHL (dark grey), and Other (black/darker grey) + Last 10 Form Box Styles + DNP Tiles + Milestones Card Header Wrapper $html .= ''; // Render Bio Card First $html .= otg_render_player_bio($p); // Check if player has played any regular season games for the Rangers $has_played_for_rangers = false; $is_goalie = ($p->Position === 'Goaltender'); if (!empty($p->nhl_id)) { $rangers_gp_count = $wpdb->get_var($wpdb->prepare(" SELECT COUNT(*) FROM otg_rangers_player_boxscores b JOIN otg_rangers_games g ON b.game_id = g.game_id WHERE b.player_id = %d AND g.game_state IS NULL AND g.game_type = 2 ", $p->nhl_id)); if (intval($rangers_gp_count) > 0) { $has_played_for_rangers = true; } } // --- Active Player Milestone Progress Banner & Milestones Matrix (Rendered only if they have played for Rangers) --- if ($has_played_for_rangers && isset($p->is_active) && intval($p->is_active) === 1 && !empty($p->nhl_id)) { // Open the container card with the "Next Rangers Milestones" header row (using h4) $html .= '
'; $html .= '

Next Rangers Milestones

'; if (!$is_goalie) { $all_boxscores = $wpdb->get_results($wpdb->prepare(" SELECT g.game_id, b.goals, b.assists, b.points FROM otg_rangers_player_boxscores b JOIN otg_rangers_games g ON b.game_id = g.game_id WHERE b.player_id = %d AND g.game_state IS NULL AND g.game_type = 2 ORDER BY g.game_date ASC, g.game_id ASC ", $p->nhl_id)); $curr_gp = count($all_boxscores); $curr_g = 0; $curr_a = 0; $curr_p = 0; foreach ($all_boxscores as $bs) { $curr_g += intval($bs->goals); $curr_a += intval($bs->assists); $curr_p += intval($bs->points); } $next_gp = (floor($curr_gp / 100) + 1) * 100; $next_g = (floor($curr_g / 100) + 1) * 100; $next_a = (floor($curr_a / 100) + 1) * 100; $next_p = (floor($curr_p / 100) + 1) * 100; $away_gp = $next_gp - $curr_gp; $away_g = $next_g - $curr_g; $away_a = $next_a - $curr_a; $away_p = $next_p - $curr_p; $html .= '
'; $html .= '
Games Played
'.number_format($away_gp).' away from '.number_format($next_gp).'
'; $html .= '
Goals
'.number_format($away_g).' away from '.number_format($next_g).'
'; $html .= '
Assists
'.number_format($away_a).' away from '.number_format($next_a).'
'; $html .= '
Points
'.number_format($away_p).' away from '.number_format($next_p).'
'; $html .= '
'; } else { $all_boxscores = $wpdb->get_results($wpdb->prepare(" SELECT g.game_id, b.decision, b.time_on_ice FROM otg_rangers_player_boxscores b JOIN otg_rangers_games g ON b.game_id = g.game_id WHERE b.player_id = %d AND g.game_state IS NULL AND g.game_type = 2 ORDER BY g.game_date ASC, g.game_id ASC ", $p->nhl_id)); $curr_gp = 0; $curr_gs = 0; $curr_w = 0; foreach ($all_boxscores as $bs) { $toi_val = $bs->time_on_ice; $toi_sec = is_numeric($toi_val) ? floatval($toi_val) : (function_exists('toi_to_seconds') ? toi_to_seconds($toi_val) : 0); if ($toi_sec > 0) { $curr_gp += 1; $curr_gs += 1; if (isset($bs->decision) && strtoupper(trim($bs->decision)) === 'W') { $curr_w += 1; } } } $next_gp = (floor($curr_gp / 100) + 1) * 100; $next_gs = (floor($curr_gs / 100) + 1) * 100; $next_w = (floor($curr_w / 100) + 1) * 100; $away_gp = $next_gp - $curr_gp; $away_gs = $next_gs - $curr_gs; $away_w = $next_w - $curr_w; $html .= '
'; $html .= '
Games Played
'.number_format($away_gp).' away from '.number_format($next_gp).'
'; $html .= '
Games Started
'.number_format($away_gs).' away from '.number_format($next_gs).'
'; $html .= '
Wins
'.number_format($away_w).' away from '.number_format($next_w).'
'; $html .= '
'; } // Close the container card $html .= '
'; } // --- Last 10 Games Logic (Skaters & Goaltenders) --- $is_active_player = isset($p->is_active) && intval($p->is_active) === 1; $player_status_val = trim($p->Status ?? $p->status ?? ''); if ($is_active_player && strcasecmp($player_status_val, 'Rangers') === 0 && !empty($p->nhl_id)) { if (!$is_goalie) { $last_10_games = $wpdb->get_results($wpdb->prepare(" SELECT g.game_id, g.game_date, g.is_home, g.opponent_abbr, g.game_state, b.goals, b.assists, b.points, b.shots, b.pim, b.plus_minus, b.player_id AS played_in_game FROM otg_rangers_games g LEFT JOIN otg_rangers_player_boxscores b ON g.game_id = b.game_id AND b.player_id = %d WHERE g.game_state IS NULL AND g.game_type IN (2, 3) ORDER BY g.game_date DESC, g.game_id DESC LIMIT 10 ", $p->nhl_id)); if (!empty($last_10_games)) { $last_10_games = array_reverse($last_10_games); $l10_total_pts = 0; $l10_total_g = 0; $l10_total_a = 0; $l10_total_shots = 0; foreach ($last_10_games as $g_stat) { if (!empty($g_stat->played_in_game)) { $l10_total_pts += intval($g_stat->points); $l10_total_g += intval($g_stat->goals); $l10_total_a += intval($g_stat->assists); $l10_total_shots += intval($g_stat->shots); } } $html .= '
'; $html .= '
Recent Form • Last ' . count($last_10_games) . ' Games
'; $html .= '
'; $html .= '
' . $l10_total_pts . ' pts • ' . $l10_total_g . ' G • ' . $l10_total_a . ' A • ' . $l10_total_shots . ' shots in last ' . count($last_10_games) . '
'; $html .= '
'; $html .= '
'; foreach ($last_10_games as $gs) { $prefix = ($gs->is_home == 1) ? '' : '@ '; $opp_label = $prefix . esc_html($gs->opponent_abbr); $did_play = !empty($gs->played_in_game); if (!$did_play) { $tile_class = 'otg-l10-tile did-not-play'; } else { $is_high_impact = (intval($gs->points) >= 3); $tile_class = $is_high_impact ? 'otg-l10-tile highlighted' : 'otg-l10-tile'; } $game_date_formatted = date('M j', strtotime($gs->game_date)); $box_link = '/box/?game_id=' . esc_attr($gs->game_id); $html .= '
'; $html .= ''; if ($did_play) { $shot_val = intval($gs->shots); $underline_opacity = min(1.0, max(0.2, $shot_val / 8)); $html .= '
'.intval($gs->points).' Pts
'; $html .= '
'.intval($gs->goals).'G '.intval($gs->assists).'A
'; $html .= '
'.$shot_val.' SOG
'; if ($shot_val > 0) { $html .= '
'; } } else { $html .= '
DNP
'; $html .= '
-
'; } $html .= '
'; } $html .= '
'; $html .= '
'; } } else { $last_10_games = $wpdb->get_results($wpdb->prepare(" SELECT g.game_id, g.game_date, g.is_home, g.opponent_abbr, g.game_state, b.decision, b.goals_against, b.shots_against, b.time_on_ice, b.player_id AS played_in_game FROM otg_rangers_games g LEFT JOIN otg_rangers_player_boxscores b ON g.game_id = b.game_id AND b.player_id = %d WHERE g.game_state IS NULL AND g.game_type IN (2, 3) ORDER BY g.game_date DESC, g.game_id DESC LIMIT 10 ", $p->nhl_id)); if (!empty($last_10_games)) { $last_10_games = array_reverse($last_10_games); $l10_wins = 0; $l10_losses = 0; $l10_ot = 0; $l10_sa = 0; $l10_ga = 0; $l10_sv = 0; $l10_min_sec = 0; foreach ($last_10_games as $g_stat) { $toi_val = $g_stat->time_on_ice; $toi_sec = is_numeric($toi_val) ? floatval($toi_val) : (function_exists('toi_to_seconds') ? toi_to_seconds($toi_val) : 0); if (!empty($g_stat->played_in_game) && $toi_sec > 0) { $dec = strtoupper(trim($g_stat->decision ?? '')); if ($dec === 'W') $l10_wins++; elseif ($dec === 'L') $l10_losses++; elseif ($dec === 'O' || $dec === 'T') $l10_ot++; $l10_sa += intval($g_stat->shots_against); $l10_ga += intval($g_stat->goals_against); $l10_min_sec += $toi_sec; } } $l10_sv = $l10_sa - $l10_ga; $l10_svp = ($l10_sa > 0) ? number_format(($l10_sv / $l10_sa), 3) : '.000'; $l10_hours = $l10_min_sec / 3600; $l10_gaa = ($l10_hours > 0) ? number_format(($l10_ga / $l10_hours), 2) : '0.00'; $html .= '
'; $html .= '
Recent Form • Last ' . count($last_10_games) . ' Games
'; $html .= '
'; $html .= '
' . $l10_wins . '-' . $l10_losses . '-' . $l10_ot . ' • ' . $l10_svp . ' SV% • ' . $l10_gaa . ' GAA • ' . $l10_sa . ' SA in last ' . count($last_10_games) . '
'; $html .= '
'; $html .= '
'; foreach ($last_10_games as $gs) { $prefix = ($gs->is_home == 1) ? '' : '@ '; $opp_label = $prefix . esc_html($gs->opponent_abbr); $did_play = false; if (!empty($gs->played_in_game)) { $toi_val = $gs->time_on_ice; $toi_sec = is_numeric($toi_val) ? floatval($toi_val) : (function_exists('toi_to_seconds') ? toi_to_seconds($toi_val) : 0); if ($toi_sec > 0) { $did_play = true; } } if (!$did_play) { $tile_class = 'otg-l10-tile did-not-play'; } else { $dec = strtoupper(trim($gs->decision ?? '')); $is_win = ($dec === 'W'); $tile_class = $is_win ? 'otg-l10-tile highlighted' : 'otg-l10-tile'; } $game_date_formatted = date('M j', strtotime($gs->game_date)); $box_link = '/box/?game_id=' . esc_attr($gs->game_id); $html .= '
'; $html .= ''; if ($did_play) { $sa = intval($gs->shots_against); $ga = intval($gs->goals_against); $sv = $sa - $ga; $dec_label = !empty($gs->decision) ? $gs->decision : '-'; $tile_svp = ($sa > 0) ? number_format(($sv / $sa), 3) : '.000'; $toi_g = $gs->time_on_ice; $toi_sec_g = is_numeric($toi_g) ? floatval($toi_g) : (function_exists('toi_to_seconds') ? toi_to_seconds($toi_g) : 0); $tile_hours = $toi_sec_g / 3600; $tile_gaa = ($tile_hours > 0) ? number_format(($ga / $tile_hours), 2) : '0.00'; $html .= '
'.esc_html($dec_label).' • '.$tile_svp.'
'; $html .= '
'.$tile_gaa.' GAA • '.$sa.' SA
'; } else { $html .= '
DNP
'; $html .= '
-
'; } $html .= '
'; } $html .= '
'; $html .= '
'; } } } $stats_table = $is_goalie ? 'otg_goalie_seasonal_stats' : 'otg_player_seasonal_stats'; $stats = $wpdb->get_results($wpdb->prepare("SELECT * FROM {$stats_table} WHERE nhl_id = %d AND game_type_id = %d ORDER BY season ASC", $p->nhl_id, $game_type)); $calc_age = function($season_string, $birthday) { if (empty($birthday) || $birthday === '1800-01-01') return '-'; $year_str = substr($season_string, 0, 4); if (!is_numeric($year_str)) return '-'; $dec_31 = new DateTime($year_str . '-12-31'); $bday = new DateTime($birthday); if ($bday > $dec_31) return '-'; return $bday->diff($dec_31)->y; }; if (!empty($stats)) { $html .= '

Statistics

'; $html .= '
'; $html .= '
'; $html .= '
'; $html .= 'Regular Season'; $html .= 'Playoffs'; $html .= '
'; $html .= '
'; $html .= '
'; $td_style = 'padding: 4px 6px; border-bottom: 1px solid rgba(255,255,255,0.05); vertical-align: middle; line-height: 1.2;'; $th_style = 'padding: 8px 6px; border-bottom: 2px solid #555; background-color: #1a1a1a; color: #fff; text-transform: uppercase; font-size: 0.9em;'; if ($is_goalie) { // 1. Header for Goaltenders (Including GS column) $html .= ''; // 2. Data Rows & Aggregation for Goaltenders $totals = [ 'NHL' => ['GP' => 0, 'GS' => 0, 'W' => 0, 'L' => 0, 'TOT' => 0, 'MIN' => 0, 'SO' => 0, 'GA' => 0, 'SA' => 0, 'G' => 0, 'A' => 0, 'PIM' => 0], 'NYR' => ['GP' => 0, 'GS' => 0, 'W' => 0, 'L' => 0, 'TOT' => 0, 'MIN' => 0, 'SO' => 0, 'GA' => 0, 'SA' => 0, 'G' => 0, 'A' => 0, 'PIM' => 0] ]; $highs = ['GS'=>0, 'W'=>0, 'L'=>0, 'SO'=>0, 'SV_PCT'=>0, 'GAA'=>99, 'G'=>0, 'A'=>0, 'PIM'=>0, 'GP'=>0, 'TOT'=>0, 'MIN'=>0, 'GA'=>0, 'SV'=>0, 'SA'=>0]; $count = 0; // Helper function to convert MM:SS or HHH:SS to total seconds if not already defined if (!function_exists('toi_to_seconds_local')) { function toi_to_seconds_local($toi) { if (empty($toi)) return 0; if (is_numeric($toi)) return (int)$toi; $parts = explode(':', $toi); if (count($parts) === 2) { return ((int)$parts[0] * 60) + (int)$parts[1]; } return 0; } } foreach ($stats as $s) { $games_started = (isset($s->games_started) && is_numeric($s->games_started)) ? (int)$s->games_started : 0; $losses = (isset($s->losses) && is_numeric($s->losses)) ? (int)$s->losses : 0; $ties = (isset($s->ties) && is_numeric($s->ties)) ? (int)$s->ties : 0; $ot_losses = (isset($s->ot_losses) && is_numeric($s->ot_losses)) ? (int)$s->ot_losses : 0; $shots_against = (isset($s->shots_against) && is_numeric($s->shots_against)) ? (float)$s->shots_against : 0; $goals_against = (isset($s->goals_against) && is_numeric($s->goals_against)) ? (float)$s->goals_against : 0; $tot = $ties + $ot_losses; $sv = $shots_against - $goals_against; $y1 = substr($s->season, 0, 4); $y2 = str_pad((int)substr($s->season, 0, 4) + 1, 4, '0', STR_PAD_LEFT); $disp_season = $y1 . '-' . substr($y2, 2, 2); $player_age = $calc_age($s->season, $p->Birthday); $is_nhl = ($s->league_name === 'NHL'); // Fixed: Explicitly evaluating team_name for New York Rangers rows in otg_goalie_season_stats $is_nyr = (trim($s->team_name) === 'New York Rangers' && $is_nhl); $row_classes = 'league-' . ($is_nhl ? 'nhl' : 'other'); if ($is_nyr) { $row_classes .= ' team-nyr'; } $display = ($is_nhl || $is_nyr) ? '' : 'display: none;'; $html .= ''; // Aggregation if ($is_nhl) { $t = &$totals['NHL']; $t['GP'] += (int)$s->games_played; $t['GS'] += $games_started; $t['W'] += (int)$s->wins; $t['L'] += $losses; $t['TOT'] += $tot; $t['MIN'] += toi_to_seconds_local($s->time_on_ice); $t['SO'] += (int)$s->shutouts; $t['GA'] += (float)$s->goals_against; $t['SA'] += (float)$s->shots_against; $t['G'] += (int)$s->goals; $t['A'] += (int)$s->assists; $t['PIM'] += (int)$s->pim; } if ($is_nyr) { $t = &$totals['NYR']; $t['GP'] += (int)$s->games_played; $t['GS'] += $games_started; $t['W'] += (int)$s->wins; $t['L'] += $losses; $t['TOT'] += $tot; $t['MIN'] += toi_to_seconds_local($s->time_on_ice); $t['SO'] += (int)$s->shutouts; $t['GA'] += (float)$s->goals_against; $t['SA'] += (float)$s->shots_against; $t['G'] += (int)$s->goals; $t['A'] += (int)$s->assists; $t['PIM'] += (int)$s->pim; } // Track Highs (NHL Only) if ($is_nhl) { $count++; $current_gaa = ($s->time_on_ice > 0) ? ($s->goals_against / ($s->time_on_ice / 60)) : 0; $highs['GP'] = max($highs['GP'], (int)$s->games_played); $highs['GS'] = max($highs['GS'], (int)$games_started); $highs['W'] = max($highs['W'], (int)$s->wins); $highs['L'] = max($highs['L'], (int)$losses); $highs['TOT'] = max($highs['TOT'], (int)($s->ties + $s->ot_losses)); $highs['SO'] = max($highs['SO'], (int)$s->shutouts); $highs['GA'] = max($highs['GA'], (int)$s->goals_against); $highs['G'] = max($highs['G'], (int)$s->goals); $highs['A'] = max($highs['A'], (int)$s->assists); $highs['PIM'] = max($highs['PIM'], (int)$s->pim); $highs['SV_PCT'] = max($highs['SV_PCT'], (float)$s->save_pctg); $highs['MIN'] = max($highs['MIN'], function_exists('toi_to_seconds') ? toi_to_seconds($s->time_on_ice) : 0); $highs['SV'] = max($highs['SV'], (int)$sv); $highs['SA'] = max($highs['SA'], (int)$s->shots_against); if ($current_gaa > 0) { $highs['GAA'] = ($highs['GAA'] === 99.00) ? $current_gaa : min($highs['GAA'], $current_gaa); } } } // 3. Summary Rows with Separator for Goaltenders $f = 'padding: 7px 4px; font-weight: bold; text-align: center;'; $fl = 'padding: 7px 4px; font-weight: bold; text-align: left;'; $html .= ''; $labels_map = [ 'NHL' => 'NHL Career Totals', 'NYR' => 'NYR Career Totals' ]; foreach (['NHL', 'NYR'] as $scope) { if (!empty($totals[$scope]) && $totals[$scope]['GP'] > 0) { $t = $totals[$scope]; $row_class = ($scope === 'NYR') ? 'summary-row-nyr' : 'summary-row-nhl'; $sv = $t['SA'] - $t['GA']; $total_minutes = $t['MIN'] / 60; $gaa = ($total_minutes > 0) ? ($t['GA'] / ($total_minutes / 60)) : 0; $svp = ($t['SA'] > 0) ? ($sv / $t['SA']) : 0; $html .= ''; $html .= ''; $html .= ''; $html .= ''; $html .= ''; $html .= ''; $html .= ''; $html .= ''; $html .= ''; $html .= ''; } } // Career Highs & Averages for Goaltenders $html .= ''; if ($count > 0 && !empty($totals['NHL'])) { $t = $totals['NHL']; $avg_gp = round($t['GP'] / $count); $avg_gs = round($t['GS'] / $count); $avg_w = round($t['W'] / $count); $avg_l = round($t['L'] / $count); $avg_tot = round($t['TOT'] / $count); $avg_min_sec = function_exists('format_seconds_to_toi') ? format_seconds_to_toi(floor($t['MIN'] / $count)) : floor($t['MIN'] / $count); $avg_so = round($t['SO'] / $count); $avg_ga = round($t['GA'] / $count); $avg_sv = round(($t['SA'] - $t['GA']) / $count); $avg_sa = round($t['SA'] / $count); $total_minutes = $t['MIN'] / 60; $gaa = ($total_minutes > 0) ? ($t['GA'] / ($total_minutes / 60)) : 0; $svp = ($t['SA'] > 0) ? (($t['SA'] - $t['GA']) / $t['SA']) : 0; $html .= ''; $html .= ''; $html .= ''; $html .= ''; $html .= ''; $html .= ''; $html .= ''; $html .= ''; $html .= ''; $html .= ''; $html .= ''; $html .= ''; } } else { $html .= ''; foreach ($stats as $s) { $y1 = substr($s->season, 0, 4); $y2 = str_pad((int)substr($s->season, 0, 4) + 1, 4, '0', STR_PAD_LEFT); $disp_season = $y1 . '-' . substr($y2, 2, 2); $player_age = $calc_age($s->season, $p->Birthday); $is_nhl = ($s->league_name === 'NHL'); $is_nyr = ($s->team_name === 'New York Rangers'); $row_classes = 'league-' . ($is_nhl ? 'nhl' : 'other'); if ($is_nyr) { $row_classes .= ' team-nyr'; } $display = ($is_nhl || $is_nyr) ? '' : 'display: none;'; $pm = ($s->plus_minus > 0) ? '+' . $s->plus_minus : $s->plus_minus; $html .= ''; } $nhl_stats = array_filter($stats, function($s) { return ($s->league_name === 'NHL'); }); $nyr_stats = array_filter($stats, function($s) { return ($s->team_name === 'New York Rangers' && $s->league_name === 'NHL'); }); function get_stats_data($data_set) { $res = ['GP' => 0, 'G' => 0, 'A' => 0, 'P' => 0, 'PIM' => 0, '+/-' => 0, 'PPG' => 0, 'PPP' => 0, 'SHG' => 0, 'SHP' => 0, 'OTG' => 0, 'GWG' => 0, 'S' => 0, 'TOI_SEC' => 0]; $highs = ['G' => 0, 'A' => 0, 'P' => 0, 'PIM' => 0, 'plus_minus' => -100, 'PPG' => 0, 'PPP' => 0, 'SHG' => 0, 'SHP' => 0, 'OTG' => 0, 'GWG' => 0, 'S' => 0, 'TOI_SEC' => 0]; foreach ($data_set as $s) { $res['GP'] += $s->games_played; $res['G'] += $s->goals; $res['A'] += $s->assists; $res['P'] += $s->points; $res['PIM'] += $s->pim; $res['+/-'] += $s->plus_minus; $res['PPG'] += $s->power_play_goals; $res['PPP'] += $s->power_play_points; $res['SHG'] += $s->shorthanded_goals; $res['SHP'] += $s->shorthanded_points; $res['OTG'] += $s->ot_goals; $res['GWG'] += $s->game_winning_goals; $res['S'] += $s->shots; list($m, $ss) = explode(':', $s->avg_toi); $res['TOI_SEC'] += ($m * 60) + $ss; if ($s->goals > $highs['G']) $highs['G'] = $s->goals; if ($s->assists > $highs['A']) $highs['A'] = $s->assists; if ($s->points > $highs['P']) $highs['P'] = $s->points; if ($s->pim > $highs['PIM']) $highs['PIM'] = $s->pim; if ($s->plus_minus > $highs['plus_minus']) $highs['plus_minus'] = $s->plus_minus; if ($s->power_play_goals > $highs['PPG']) $highs['PPG'] = $s->power_play_goals; if ($s->power_play_points > $highs['PPP']) $highs['PPP'] = $s->power_play_points; if ($s->shorthanded_goals > $highs['SHG']) $highs['SHG'] = $s->shorthanded_goals; if ($s->shorthanded_points > $highs['SHP']) $highs['SHP'] = $s->shorthanded_points; if ($s->ot_goals > $highs['OTG']) $highs['OTG'] = $s->ot_goals; if ($s->game_winning_goals > $highs['GWG']) $highs['GWG'] = $s->game_winning_goals; if ($s->shots > $highs['S']) $highs['S'] = $s->shots; $toi_total = ($m * 60) + $ss; if ($toi_total > $highs['TOI_SEC']) $highs['TOI_SEC'] = $toi_total; } return [$res, $highs]; } if (!empty($nhl_stats)) { list($nhl_tot, $nhl_high) = get_stats_data($nhl_stats); list($nyr_tot, $nyr_high) = get_stats_data($nyr_stats); $count = count($nhl_stats); $f = 'padding: 7px 4px; font-weight: bold; vertical-align: middle; text-align: center;'; $fl = 'padding: 7px 4px; font-weight: bold; vertical-align: middle; text-align: left;'; $html .= ''; $html .= ''; if (!empty($nyr_stats)) { $html .= ''; } $avg_m = floor(($nhl_tot['TOI_SEC'] / $count) / 60); $avg_s = str_pad(round(fmod(($nhl_tot['TOI_SEC'] / $count), 60)), 2, '0', STR_PAD_LEFT); $html .= ''; $high_m = floor(($nhl_high['TOI_SEC']) / 60); $high_s = str_pad(round(fmod($nhl_high['TOI_SEC'], 60)), 2, '0', STR_PAD_LEFT); $html .= ''; } } $html .= '
Season Age Team League GP GS W L T/OT MIN SO GA SV SA GAA Sv% G A PIM
'.esc_html($disp_season).' '.esc_html($player_age).' '.esc_html($s->team_name).' '.esc_html($s->league_name).' '.esc_html($s->games_played).' '.esc_html($games_started).' '.esc_html($s->wins).' '.esc_html($losses).' '.esc_html($tot).' '.esc_html($s->time_on_ice).' '.esc_html($s->shutouts).' '.esc_html($s->goals_against).' '.esc_html($sv).' '.esc_html($s->shots_against).' '.number_format($s->goals_against_avg, 2).' '.number_format($s->save_pctg, 3).' '.esc_html($s->goals).' '.esc_html($s->assists).' '.esc_html($s->pim).'
'.$labels_map[$scope].''.($scope === 'NYR' ? 'NHL' : 'NHL').''.$t['GP'].''.$t['GS'].''.$t['W'].''.$t['L'].''.$t['TOT'].''.(function_exists('format_seconds_to_toi') ? esc_html(format_seconds_to_toi($t['MIN'])) : $t['MIN']).''.$t['SO'].''.$t['GA'].''.$sv.''.$t['SA'].''.number_format($gaa, 2).''.number_format($svp, 3).''.$t['G'].''.$t['A'].''.$t['PIM'].'
NHL Career Highs NHL '.$highs['GP'].' '.$highs['GS'].' '.$highs['W'].' '.$highs['L'].' '.$highs['TOT'].' '.(function_exists('format_seconds_to_toi') ? format_seconds_to_toi($highs['MIN']) : $highs['MIN']).' '.$highs['SO'].' '.$highs['GA'].' '.$highs['SV'].' '.$highs['SA'].' '.number_format($highs['GAA'], 2).' '.number_format($highs['SV_PCT'], 3).' '.$highs['G'].' '.$highs['A'].' '.$highs['PIM'].'
NHL Career AveragesNHL'.$avg_gp.''.$avg_gs.''.$avg_w.''.$avg_l.''.$avg_tot.''.$avg_min_sec.''.$avg_so.''.$avg_ga.''.$avg_sv.''.$avg_sa.''.number_format($gaa, 2).''.number_format($svp, 3).''.round($t['G'] / $count).''.round($t['A'] / $count).''.round($t['PIM'] / $count).'
SeasonAgeTeamLeagueGPGAPPIM+/-PPGPPPSHGSHPOTGGWGSS%FO%TOI
'.esc_html($disp_season).''.esc_html($player_age).''.esc_html($s->team_name).''.esc_html($s->league_name).''.esc_html($s->games_played).''.esc_html($s->goals).''.esc_html($s->assists).''.esc_html($s->points).''.esc_html($s->pim).''.$pm.''.esc_html($s->power_play_goals).''.esc_html($s->power_play_points).''.esc_html($s->shorthanded_goals).''.esc_html($s->shorthanded_points).''.esc_html($s->ot_goals).''.esc_html($s->game_winning_goals).''.esc_html($s->shots).''.number_format((float)$s->shooting_pctg * 100, 1).'%'.number_format((float)$s->faceoff_winning_pctg * 100, 1).'%'.esc_html($s->avg_toi).'
NHL Career TotalsNHL'.$nhl_tot['GP'].''.$nhl_tot['G'].''.$nhl_tot['A'].''.$nhl_tot['P'].''.$nhl_tot['PIM'].''.(($nhl_tot['+/-'] > 0) ? '+' . $nhl_tot['+/-'] : $nhl_tot['+/-']).''.$nhl_tot['PPG'].''.$nhl_tot['PPP'].''.$nhl_tot['SHG'].''.$nhl_tot['SHP'].''.$nhl_tot['OTG'].''.$nhl_tot['GWG'].''.$nhl_tot['S'].'
NYR Career TotalsNHL'.$nyr_tot['GP'].''.$nyr_tot['G'].''.$nyr_tot['A'].''.$nyr_tot['P'].''.$nyr_tot['PIM'].''.(($nyr_tot['+/-'] > 0) ? '+' . $nyr_tot['+/-'] : $nyr_tot['+/-']).''.$nyr_tot['PPG'].''.$nyr_tot['PPP'].''.$nyr_tot['SHG'].''.$nyr_tot['SHP'].''.$nyr_tot['OTG'].''.$nyr_tot['GWG'].''.$nyr_tot['S'].'
NHL Career AveragesNHL'.round($nhl_tot['GP']/$count).''.round($nhl_tot['G']/$count).''.round($nhl_tot['A']/$count).''.round($nhl_tot['P']/$count).''.round($nhl_tot['PIM']/$count).''.(($nhl_tot['+/-'] > 0) ? '+' . round($nhl_tot['+/-']/$count) : round($nhl_tot['+/-']/$count)).''.round($nhl_tot['PPG']/$count).''.round($nhl_tot['PPP']/$count).''.round($nhl_tot['SHG']/$count).''.round($nhl_tot['SHP']/$count).''.round($nhl_tot['OTG']/$count).''.round($nhl_tot['GWG']/$count).''.round($nhl_tot['S']/$count).''.$avg_m.':'.$avg_s.'
NHL Career HighsNHL'.$nhl_high['G'].''.$nhl_high['A'].''.$nhl_high['P'].''.$nhl_high['PIM'].''.(($nhl_high['plus_minus'] > 0) ? '+' . $nhl_high['plus_minus'] : $nhl_high['plus_minus']).''.$nhl_high['PPG'].''.$nhl_high['PPP'].''.$nhl_high['SHG'].''.$nhl_high['SHP'].''.$nhl_high['OTG'].''.$nhl_high['GWG'].''.$nhl_high['S'].''.$high_m.':'.$high_s.'
'; } // ===================================================================== // COMPLETED MILESTONES MATRIX SECTION (Skaters & Goaltenders) // ===================================================================== if ($has_played_for_rangers && !empty($p->nhl_id)) { if (!$is_goalie) { $game_boxscores = $wpdb->get_results($wpdb->prepare(" SELECT g.game_id, g.game_date, g.opponent_abbr, g.is_home, b.goals, b.assists, b.points FROM otg_rangers_player_boxscores b JOIN otg_rangers_games g ON b.game_id = g.game_id WHERE b.player_id = %d AND g.game_state IS NULL AND g.game_type = 2 ORDER BY g.game_date ASC, g.game_id ASC ", $p->nhl_id)); if (!empty($game_boxscores)) { $running_gp = 0; $running_g = 0; $running_a = 0; $running_p = 0; $found_gp_milestones = []; $found_g_milestones = []; $found_a_milestones = []; $found_p_milestones = []; $milestone_matrix_data = [ 'Games Played' => [], 'Goals' => [], 'Assists' => [], 'Points' => [] ]; foreach ($game_boxscores as $gb) { $prev_gp = $running_gp; $prev_g = $running_g; $prev_a = $running_a; $prev_p = $running_p; $running_gp += 1; $running_g += intval($gb->goals); $running_a += intval($gb->assists); $running_p += intval($gb->points); $prefix = ($gb->is_home == 1) ? '' : '@ '; $game_link = '' . $prefix . esc_html($gb->opponent_abbr) . ' (' . date('M j, Y', strtotime($gb->game_date)) . ')'; // Target GP Check $target_gp = floor($running_gp / 100) * 100; if ($target_gp >= 100 && $prev_gp < $target_gp && !in_array($target_gp, $found_gp_milestones)) { $found_gp_milestones[] = $target_gp; $milestone_matrix_data['Games Played'][$target_gp] = $game_link; } // Target Goals Check $target_g = floor($running_g / 100) * 100; if ($target_g >= 100 && $prev_g < $target_g && !in_array($target_g, $found_g_milestones)) { $found_g_milestones[] = $target_g; $milestone_matrix_data['Goals'][$target_g] = $game_link; } // Target Assists Check $target_a = floor($running_a / 100) * 100; if ($target_a >= 100 && $prev_a < $target_a && !in_array($target_a, $found_a_milestones)) { $found_a_milestones[] = $target_a; $milestone_matrix_data['Assists'][$target_a] = $game_link; } // Target Points Check $target_p = floor($running_p / 100) * 100; if ($target_p >= 100 && $prev_p < $target_p && !in_array($target_p, $found_p_milestones)) { $found_p_milestones[] = $target_p; $milestone_matrix_data['Points'][$target_p] = $game_link; } } $all_tiers = []; foreach ($milestone_matrix_data as $cat_arr) { foreach (array_keys($cat_arr) as $tier) { $all_tiers[] = $tier; } } $all_tiers = array_unique($all_tiers); sort($all_tiers); if (!empty($all_tiers)) { $html .= '
'; $html .= '

Rangers Milestones

'; $html .= '
'; $html .= ''; $html .= ''; $html .= ''; foreach ($all_tiers as $tier) { $html .= ''; } $html .= ''; $html .= ''; foreach (['Games Played', 'Goals', 'Assists', 'Points'] as $category) { $html .= ''; $html .= ''; foreach ($all_tiers as $tier) { $cell_content = isset($milestone_matrix_data[$category][$tier]) ? $milestone_matrix_data[$category][$tier] : '-'; $html .= ''; } $html .= ''; } $html .= '
Category' . number_format($tier) . '
' . esc_html($category) . '' . $cell_content . '
'; } } } else { $game_boxscores = $wpdb->get_results($wpdb->prepare(" SELECT g.game_id, g.game_date, g.opponent_abbr, g.is_home, b.decision, b.time_on_ice FROM otg_rangers_player_boxscores b JOIN otg_rangers_games g ON b.game_id = g.game_id WHERE b.player_id = %d AND g.game_state IS NULL AND g.game_type = 2 ORDER BY g.game_date ASC, g.game_id ASC ", $p->nhl_id)); if (!empty($game_boxscores)) { $running_gp = 0; $running_gs = 0; $running_w = 0; $found_gp_milestones = []; $found_gs_milestones = []; $found_w_milestones = []; $milestone_matrix_data = [ 'Games Played' => [], 'Games Started' => [], 'Wins' => [] ]; foreach ($game_boxscores as $gb) { $toi_val = $gb->time_on_ice; $toi_sec = is_numeric($toi_val) ? floatval($toi_val) : (function_exists('toi_to_seconds') ? toi_to_seconds($toi_val) : 0); if ($toi_sec > 0) { $prev_gp = $running_gp; $prev_gs = $running_gs; $prev_w = $running_w; $running_gp += 1; $running_gs += 1; if (isset($gb->decision) && strtoupper(trim($gb->decision)) === 'W') { $running_w += 1; } $prefix = ($gb->is_home == 1) ? '' : '@ '; $game_link = '' . $prefix . esc_html($gb->opponent_abbr) . ' (' . date('M j, Y', strtotime($gb->game_date)) . ')'; // Target GP Check (every 100 games for goalies) $target_gp = floor($running_gp / 100) * 100; if ($target_gp >= 100 && $prev_gp < $target_gp && !in_array($target_gp, $found_gp_milestones)) { $found_gp_milestones[] = $target_gp; $milestone_matrix_data['Games Played'][$target_gp] = $game_link; } // Target GS Check (every 100 starts) $target_gs = floor($running_gs / 100) * 100; if ($target_gs >= 100 && $prev_gs < $target_gs && !in_array($target_gs, $found_gs_milestones)) { $found_gs_milestones[] = $target_gs; $milestone_matrix_data['Games Started'][$target_gs] = $game_link; } // Target Wins Check (every 100 wins) $target_w = floor($running_w / 100) * 100; if ($target_w >= 100 && $prev_w < $target_w && !in_array($target_w, $found_w_milestones)) { $found_w_milestones[] = $target_w; $milestone_matrix_data['Wins'][$target_w] = $game_link; } } } $all_tiers = []; foreach ($milestone_matrix_data as $cat_arr) { foreach (array_keys($cat_arr) as $tier) { $all_tiers[] = $tier; } } $all_tiers = array_unique($all_tiers); sort($all_tiers); if (!empty($all_tiers)) { $html .= '
'; $html .= '

Rangers Milestones

'; $html .= '
'; $html .= ''; $html .= ''; $html .= ''; foreach ($all_tiers as $tier) { $html .= ''; } $html .= ''; $html .= ''; foreach (['Games Played', 'Games Started', 'Wins'] as $category) { $html .= ''; $html .= ''; foreach ($all_tiers as $tier) { $cell_content = isset($milestone_matrix_data[$category][$tier]) ? $milestone_matrix_data[$category][$tier] : '-'; $html .= ''; } $html .= ''; } $html .= '
Category' . number_format($tier) . '
' . esc_html($category) . '' . $cell_content . '
'; } } } } $html .= ''; return $html; } function otg_render_player_bio($p) { global $wpdb; // --- Height/Birth/Hand Logic --- $feet = floor($p->Height / 12); $inches = $p->Height % 12; $height_display = "{$feet}-{$inches}"; $current_date = new DateTime(); $bday_date = (!empty($p->Birthday) && $p->Birthday !== '1800-01-01') ? new DateTime($p->Birthday) : null; $death_val = !empty($p->Death) ? $p->Death : (!empty($p->death_date) ? $p->death_date : null); $death_date = (!empty($death_val) && $death_val !== '0000-00-00' && $death_val !== '1800-01-01') ? new DateTime($death_val) : null; if ($bday_date) { if ($death_date) { $age_at_death = $bday_date->diff($death_date)->y; $formatted_death_date = $death_date->format('F ') . get_ordinal($death_date->format('j')) . $death_date->format(', Y'); $bday = $bday_date->format('F jS, Y') . ' - ' . $age_at_death . ' years (Died: ' . $formatted_death_date . ')'; } else { $age_current = $bday_date->diff($current_date)->y; $bday = $bday_date->format('F jS, Y') . ' - ' . $age_current . ' years'; } } else { $bday = '-'; } $birthplace = esc_html($p->Birthplace) . (!empty($p->State) ? ' ' . esc_html($p->State) : ''); // --- Draft Logic --- $draft = $wpdb->get_row($wpdb->prepare("SELECT * FROM otg_draft WHERE ID = %d", $p->ID)); if ($draft) { $draft_year = esc_attr($draft->Draft); $draft_link = ''.$draft_year.''; $draft_display = "{$draft->Team} - {$draft_link} " . get_ordinal($draft->Round) . " round (" . get_ordinal($draft->Pick) . " overall)"; } else { $draft_display = "N/A"; } $hand_label = ($p->Position === 'Goaltender') ? 'Catches' : 'Shoots'; $hand_value = ($p->Hand === 'L') ? 'Left' : 'Right'; $debut = !empty($p->nhl_id) ? $wpdb->get_row($wpdb->prepare(" SELECT g.game_date, g.game_id FROM otg_rangers_games g JOIN otg_rangers_player_boxscores b ON g.game_id = b.game_id WHERE b.player_id = %d AND b.team_abbr = 'NYR' AND g.game_type != 1 ORDER BY g.game_date ASC LIMIT 1 ", $p->nhl_id)) : null; $debut_display = $debut ? ''.date('F j, Y', strtotime($debut->game_date)).'' : 'Has not played for Rangers'; $numbers = !empty($p->nhl_id) ? $wpdb->get_col($wpdb->prepare(" SELECT DISTINCT jersey_number FROM otg_rangers_player_boxscores WHERE player_id = %d AND team_abbr = 'NYR' AND jersey_number IS NOT NULL AND jersey_number != '' ORDER BY jersey_number ASC ", $p->nhl_id)) : []; $numbers_display = $numbers ? implode(', ', $numbers) : 'N/A'; $data = [['Position', $p->Position], ['Height', $height_display], ['Weight', $p->Weight], [$hand_label, $hand_value], ['Birthday', $bday], ['Birthplace', $birthplace], ['Drafted', $draft_display], ['Rangers Debut', $debut_display], ['Numbers Worn', $numbers_display]]; $player_status = !empty($p->Status) ? trim($p->Status) : (!empty($p->status) ? trim($p->status) : ''); if (!empty($player_status)) { $player_bucket = isset($p->player_bucket) ? trim($p->player_bucket) : ''; $detailed_status = isset($p->detailed_status) ? trim($p->detailed_status) : ''; $current_team_display = ''; if ($player_status === 'NHL') { $current_team_display = $player_bucket; } elseif ($player_status === 'UFA') { $current_team_display = !empty($detailed_status) ? 'UFA - formerly with ' . $detailed_status : 'UFA'; } elseif ($player_status === 'Ex-Ranger') { if ($player_bucket === 'AHL' || $player_bucket === 'ECHL') { $current_team_display = $detailed_status . ' (' . $player_bucket . ')'; } elseif ($player_bucket === 'Europe') { $current_team_display = $detailed_status; } } if (!empty($current_team_display)) { $data[] = ['Current Team', esc_html($current_team_display)]; } } // --- Pull explicitly active season using active_season = 'y' --- $current_season = $wpdb->get_var("SELECT season FROM otg_rangers_season_settings WHERE active_season = 'y' LIMIT 1"); if (!$current_season) { $current_season = '2026-27'; // Fallback } $contract = $wpdb->get_row($wpdb->prepare("SELECT free_agent_type, fa_season, aav FROM otg_rangers_contracts WHERE player_id = %d LIMIT 1", $p->ID)); if ($contract && !empty($contract->fa_season)) { $fa_season = $contract->fa_season; $current_season_year = intval(substr($current_season, 0, 4)); $fa_season_year = intval(substr($fa_season, 0, 4)); $formatted_aav = !empty($contract->aav) ? '$' . number_format(floatval($contract->aav), 0) . ' AAV' : '$0 AAV'; if ($fa_season === $current_season) { $contract_display = esc_html($contract->free_agent_type); } elseif ($fa_season_year > $current_season_year) { $years_remaining = $fa_season_year - $current_season_year; $year_label = ($years_remaining === 1) ? 'more year' : 'more years'; $contract_display = sprintf('Signed for %d %s - %s (%s, %s)', $years_remaining, $year_label, $formatted_aav, esc_html($contract->free_agent_type), esc_html($fa_season)); } else { $contract_display = esc_html($contract->free_agent_type); } $data[] = ['Contract Status', $contract_display]; } $coach_row = $wpdb->get_row($wpdb->prepare("SELECT CoachID FROM otg_coach_statistics WHERE CoachID = %d LIMIT 1", $p->ID)); $gm_row = $wpdb->get_row($wpdb->prepare("SELECT id, name FROM otg_general_managers WHERE player_id = %d LIMIT 1", $p->ID)); if ($coach_row || $gm_row) { $links = []; if ($coach_row) { $links[] = 'Coach'; } if ($gm_row) { $gm_slug = strtolower(str_replace(' ', '-', $gm_row->name)); $links[] = 'GM'; } $data[] = ['Profiles', implode(' | ', $links)]; } $player_id_val = intval($p->ID); $base_dir = $_SERVER['DOCUMENT_ROOT'] . '/images/players/'; $base_url = 'https://outsidethegarden.com/images/players/'; $default_img = 'https://outsidethegarden.com/images/players/default_player.png'; $display_img = $default_img; $extensions = ['jpg', 'jpeg', 'png']; foreach ($extensions as $ext) { if (file_exists($base_dir . $player_id_val . '.' . $ext)) { $display_img = $base_url . $player_id_val . '.' . $ext; break; } } $html = '
'; $html .= '
'; $html .= '
'; $html .= '

'; $html .= ''.strtoupper(esc_html($p->Name)).'

'; $html .= ''; $html .= '
'; $html .= '
'; foreach ($data as $index => $row) { $border_style = ($index === count($data) - 1) ? 'border-bottom: none;' : 'border-bottom: 1px solid #808080;'; $html .= '
'.$row[0].':
'.$row[1].'
'; } $html .= '
'; return $html; } TROCHECK TRADED! - OutsideTheGarden
OCTOBER
«
Fri
10
Sat
11
Sun
12
Mon
13
Tue
14
Wed
15
Thu
16
»
Vincent Trocheck

TROCHECK TRADED!

In perhaps their biggest move of the day so far, Chris Drury has traded center Vincent Trocheck to the Utah Mammoth in exchange for defenseman Sean Durzi, prospect center Cole Beaudoin and a 2027 third round pick. The trade ends a months-long saga that began before last season’s trade deadline when the Rangers indicated they’d be interested in moving on from the affordable center.

After failing to make a deal at the trade deadline, Drury was reportedly still asking for a high price for Trocheck, and there were even reports that the Rangers hoped that Dylan Larkin would be traded first in order to open up the trade market for the next best center available. As it turns out he elected not to wait, and appears to have chosen the deal that most fits his offseason plan of strengthening the center and defensive positions with players that are either already in the NHL or close to it.

In Sean Durzi, the Rangers add a player who plays the right side as a second pair guy who can play 19+ minutes a night. Whether Sullivan can unlock more on his defensive game remains to be seen, but the addition allows the Rangers more flexibility to address their right side. Beaudoin is coming off his overage year in the OHL where he put up 88 points in just 54 games. He’s seen as a high-floor kind of player who probably has his best chance to contribute as a 3rd liner rather than a top six guy, but will bring a strong work ethic.

It’s a pretty decent return for Drury, and allows him to move on from a veteran forward who didn’t fit as well in the line-up and likely would see his offensive numbers drop with the departure of Artemi Panarin last season. It also gets the Rangers a little bit younger and clears up a bit of space in the top six for the new signings of Oliver Bjorkstrand and Pavel Dorofeyev to play in their natural positions, as well as return J.T. Miller to the center which he’s said he prefers.

Vincent Trocheck to Utah, per Elliotte Friedman. Rangers get back RHD Sean Durzi, 27 (5 goals, 27 points), 20-year-old center Cole Beaudoin, and a third round pick in 2027.

ColinSNewsday (@colinsnewsday.bsky.social) 2026-07-01T20:37:28.777Z
0 0 votes
Article Rating
Subscribe
Notify of
guest

This site uses Akismet to reduce spam. Learn how your comment data is processed.

0 Comments
Oldest
Newest Most Voted
September 21, 2026
Preseason Matchup Preview
NYR
NYR
VS
7:00 PM
Prudential Center
NJD
NJD

LATEST FORUM POSTS

View All →
By 11 hours ago
Works great -- thanks much.
By 14 hours ago
I've added a floating button down in the bottom right
By 14 hours ago
Let me see what I can do  
By 14 hours ago
Any way you can put in a "top of page" arrow on the bottom somewhere?
By 14 hours ago
I see -- just a few minor adjustments.  Looks good....glad you got rid of the side bar.  Never did figure out that autoplay thing nor which one it was.
Metropolitan
TeamGPWLOTPts
zCAR8253227113
xPHI8243271298
xPIT8241251698
eWSH824330995
eCBJ8240301292
eNYI824334591
eNJD824237387
eNYR823439977
Active Injuries
Player Status Injury
Matt Rempe LTIR Hand
ON THIS DAY: AUGUST 13
No games played on this day.
Birthdays
No birthdays today.
Transactions
2003 - Alexei Kovalev: Resigned
2002 - Brandon Snee: Signed
1998 - Brent Fedyk: Signed
1985 - Rob Whistle: Signed
[box]
TOP
0
Would love your thoughts, please comment.x
()
x