Compare commits

...

28 Commits

Author SHA1 Message Date
Bram Suurd
2ab4e0435e Merge efc37e6232 into a633873980 2025-09-18 05:34:19 +08:00
Bram Suurd
efc37e6232 Fix newline issue in config.php by ensuring a newline at the end of the file for better compatibility and adherence to coding standards. 2025-06-30 01:05:04 +02:00
Bram Suurd
930c673e2a Revert config.php 2025-06-30 01:04:40 +02:00
Bram Suurd
cceb9b10d7 Remove unused getCategoryIcon function and associated icon display in index.php, streamlining the code and improving maintainability. 2025-06-30 00:59:30 +02:00
Bram Suurd
0d7077063f Implement WeaponHandler class to streamline weapon and knife selection logic in index.php, enhancing maintainability and clarity. Refactor weapon update handling to utilize the new class, improving organization and reducing code duplication. Update CSS for improved button styles and layout consistency. 2025-06-30 00:56:18 +02:00
Bram Suurd
cc5da9eabb Refactor UtilsClass in utils.php to enhance maintainability by introducing constants for knife defindexes and mappings, improving weapon category organization, and implementing caching for skin and weapon data. Add methods for knife validation and cache clearing to streamline functionality. 2025-06-30 00:44:08 +02:00
Bram Suurd
b7ff9e2848 Update knife display logic in index.php to include equipped status for custom knife skins, enhancing clarity in weapon representation. Adjust HTML attributes for improved functionality. 2025-06-30 00:40:56 +02:00
Bram Suurd
d0ae4b4c51 Enhance knife selection logic in index.php to automatically equip the corresponding knife type and clear existing knife skins. Introduce a mapping for knife weapon names to improve clarity and ensure accurate representation of equipped knives. 2025-06-30 00:38:38 +02:00
Bram Suurd
a43de55776 Implement logic to skip categories with zero items in index.php, improving the display of available weapon categories and enhancing user experience. 2025-06-30 00:30:21 +02:00
Bram Suurd
b87639d0e2 Update footer structure in index.php and adjust CSS for improved layout and responsiveness 2025-06-30 00:29:01 +02:00
Bram Suurd
6f02180dd4 Add footer to index.php and style.css 2025-06-30 00:23:10 +02:00
Bram Suurd
c1a9642961 Refactor knife exclusion logic in index.php to comprehensively skip all knife-related weapons during display, enhancing clarity in weapon selection. Update comments for better understanding of the logic flow. 2025-06-30 00:18:04 +02:00
Bram Suurd
b1d365ecb8 Implement default knife display logic in index.php to ensure a knife is always shown, enhancing user experience by providing a fallback option when no knife is selected. Update HTML comments for clarity. 2025-06-30 00:04:58 +02:00
Bram Suurd
4f7af6986d Enhance knife selection logic in index.php to clear existing knife skins and selections before updating, ensuring accurate representation of equipped knives; improve user experience by prioritizing basic knife options in the UI. 2025-06-30 00:02:01 +02:00
Bram Suurd
0ebab4e371 Refactor weapon display logic in index.php to show all weapons with custom skins or defaults, improving user interaction; update HTML structure for better organization and clarity in skin selection. 2025-06-29 23:40:56 +02:00
Bram Suurd
bfa69b7e59 Refactor knife display logic in index.php to prioritize equipped knife skins and improve user interaction; update CSS to allow for expanded weapon and skin lists with enhanced scrolling behavior. 2025-06-29 23:39:21 +02:00
Bram Suurd
a2e60df71f Refactor knife skin selection in index.php to support multiple knife types and improve user interaction; update CSS to enhance layout and scrolling behavior for weapon lists and skins. 2025-06-29 23:32:20 +02:00
Bram Suurd
e8871f91ac Add overflow properties to weapon list in style.css for improved scrolling behavior when expanded. 2025-06-29 23:25:46 +02:00
Bram Suurd
5c3b1b48b0 Enhance weapon skins grid in style.css by adding overflow properties to improve scrolling behavior and layout consistency when expanded. 2025-06-29 23:24:22 +02:00
Bram Suurd
4ab85d09e2 Refactor weapon skin selection in index.php to use toggle functionality for better user experience; update CSS for improved layout and transitions in weapon display and skin options. 2025-06-29 23:05:29 +02:00
Bram Suurd
dbc8f2ebf8 Revamp weapon category display in index.php by restructuring HTML for better organization and adding functionality for knife skin selection. Update CSS to enhance sidebar layout and improve weapon list interactions with smooth transitions. 2025-06-29 23:01:02 +02:00
Bram Suurd
742e753e70 Adjust skin image dimensions in style.css by reducing height to 280px and increasing padding to 1rem for improved layout consistency. 2025-06-29 22:58:18 +02:00
Bram Suurd
199a090e8c Refactor skin image loading in index.php by removing intersection observer; implement direct image rendering for improved performance and simplicity. 2025-06-29 22:57:23 +02:00
Bram Suurd
048ce5cf5e Implement intersection observer for lazy loading of skin images in index.php; adjust CSS for skin grid and image dimensions to enhance layout and user experience. 2025-06-29 22:56:49 +02:00
Bram Suurd
e540e1e8e3 Enhance skin loading in index.php with lazy loading implementation for improved performance; update style.css to adjust dimensions and styling of skin images and modal overlay for better UI experience. 2025-06-29 22:54:19 +02:00
Bram Suurd
dd7f45902b Update style.css to increase skin image height from 120px to 250px for improved visual presentation. 2025-06-29 22:51:01 +02:00
Bram Suurd
0e7d4a8356 Update index.php and style.css to improve weapon loadout display and browser functionality; added empty loadout message, enhanced filtering and searching capabilities, and revamped CSS for better UI consistency. 2025-06-29 22:47:04 +02:00
Bram Suurd
b2dc9ed56d Refactor index.php to enhance UI and functionality; added weapon categories, improved layout, and implemented skin selection modal. 2025-06-29 22:45:08 +02:00
5 changed files with 1693 additions and 306 deletions

View File

@@ -13,4 +13,3 @@ define('STEAM_API_KEY', '');
define('STEAM_DOMAIN_NAME', '');
define('STEAM_LOGOUT_PAGE', '');
define('STEAM_LOGIN_PAGE', '');

View File

@@ -1,95 +1,159 @@
<?php
class UtilsClass
{
// Knife defindexes as constants for better maintainability
private const KNIFE_DEFINDEXES = [
500, 503, 505, 506, 507, 508, 509, 512, 514, 515, 516, 517, 518, 519, 520, 521, 522, 523, 525, 526
];
// Knife mapping for better maintainability
private const KNIFE_MAPPING = [
500 => 'weapon_bayonet',
503 => 'weapon_knife_css',
505 => 'weapon_knife_flip',
506 => 'weapon_knife_gut',
507 => 'weapon_knife_karambit',
508 => 'weapon_knife_m9_bayonet',
509 => 'weapon_knife_tactical',
512 => 'weapon_knife_falchion',
514 => 'weapon_knife_survival_bowie',
515 => 'weapon_knife_butterfly',
516 => 'weapon_knife_push',
517 => 'weapon_knife_cord',
518 => 'weapon_knife_canis',
519 => 'weapon_knife_ursus',
520 => 'weapon_knife_gypsy_jackknife',
521 => 'weapon_knife_outdoor',
522 => 'weapon_knife_stiletto',
523 => 'weapon_knife_widowmaker',
525 => 'weapon_knife_skeleton',
526 => 'weapon_knife_css'
];
// Weapon categories for better organization
private const WEAPON_CATEGORIES = [
'Rifles' => [7, 8, 10, 13, 16, 60, 39, 40, 38],
'Pistols' => [1, 2, 3, 4, 30, 32, 36, 61, 63, 64],
'SMGs' => [17, 19, 24, 26, 33, 34],
'Shotguns' => [25, 27, 29, 35],
'Snipers' => [9, 11, 38],
'Machine Guns' => [14, 28],
'Grenades' => [43, 44, 45, 46, 47, 48]
];
private static $skinCache = null;
private static $weaponCache = null;
private static $knifeCache = null;
public static function getKnifeDefindexes(): array
{
return self::KNIFE_DEFINDEXES;
}
public static function getKnifeMapping(): array
{
return self::KNIFE_MAPPING;
}
public static function getWeaponCategories(): array
{
return self::WEAPON_CATEGORIES;
}
public static function skinsFromJson(): array
{
if (self::$skinCache !== null) {
return self::$skinCache;
}
$skins = [];
$json = json_decode(file_get_contents(__DIR__ . "/../data/".SKIN_LANGUAGE.".json"), true);
$jsonFile = __DIR__ . "/../data/" . SKIN_LANGUAGE . ".json";
if (!file_exists($jsonFile)) {
return [];
}
$json = json_decode(file_get_contents($jsonFile), true);
if (!$json) {
return [];
}
foreach ($json as $skin) {
$skins[(int) $skin['weapon_defindex']][(int) $skin['paint']] = [
$defindex = (int) $skin['weapon_defindex'];
$paintId = (int) $skin['paint'];
$skins[$defindex][$paintId] = [
'weapon_name' => $skin['weapon_name'],
'paint_name' => $skin['paint_name'],
'image_url' => $skin['image'],
];
}
self::$skinCache = $skins;
return $skins;
}
public static function getWeaponsFromArray()
public static function getWeaponsFromArray(): array
{
$weapons = [];
$temp = self::skinsFromJson();
foreach ($temp as $key => $value) {
if (key_exists($key, $weapons))
continue;
$weapons[$key] = [
'weapon_name' => $value[0]['weapon_name'],
'paint_name' => $value[0]['paint_name'],
'image_url' => $value[0]['image_url'],
];
if (self::$weaponCache !== null) {
return self::$weaponCache;
}
$weapons = [];
$skins = self::skinsFromJson();
foreach ($skins as $defindex => $skinList) {
if (!isset($weapons[$defindex]) && isset($skinList[0])) {
$weapons[$defindex] = [
'weapon_name' => $skinList[0]['weapon_name'],
'paint_name' => $skinList[0]['paint_name'],
'image_url' => $skinList[0]['image_url'],
];
}
}
self::$weaponCache = $weapons;
return $weapons;
}
public static function getKnifeTypes()
public static function getKnifeTypes(): array
{
$knifes = [];
$temp = self::getWeaponsFromArray();
foreach ($temp as $key => $weapon) {
if (
!in_array($key, [
500,
503,
505,
506,
507,
508,
509,
512,
514,
515,
516,
517,
518,
519,
520,
521,
522,
523,
525,
526
])
)
continue;
$knifes[$key] = [
'weapon_name' => $weapon['weapon_name'],
'paint_name' => rtrim(explode("|", $weapon['paint_name'])[0]),
'image_url' => $weapon['image_url'],
];
$knifes[0] = [
'weapon_name' => "weapon_knife",
'paint_name' => "Default knife",
'image_url' => "https://raw.githubusercontent.com/Nereziel/cs2-WeaponPaints/main/website/img/skins/weapon_knife.png",
];
if (self::$knifeCache !== null) {
return self::$knifeCache;
}
$knifes = [];
$weapons = self::getWeaponsFromArray();
foreach (self::KNIFE_DEFINDEXES as $defindex) {
if (isset($weapons[$defindex])) {
$weapon = $weapons[$defindex];
$knifes[$defindex] = [
'weapon_name' => $weapon['weapon_name'],
'paint_name' => rtrim(explode("|", $weapon['paint_name'])[0]),
'image_url' => $weapon['image_url'],
];
}
}
// Add default knife
$knifes[0] = [
'weapon_name' => "weapon_knife",
'paint_name' => "Default knife",
'image_url' => "https://raw.githubusercontent.com/Nereziel/cs2-WeaponPaints/main/website/img/skins/weapon_knife.png",
];
ksort($knifes);
self::$knifeCache = $knifes;
return $knifes;
}
public static function getSelectedSkins(array $temp)
public static function getSelectedSkins(array $queryResult): array
{
$selected = [];
foreach ($temp as $weapon) {
$selected[$weapon['weapon_defindex']] = [
foreach ($queryResult as $weapon) {
$selected[$weapon['weapon_defindex']] = [
'weapon_paint_id' => $weapon['weapon_paint_id'],
'weapon_seed' => $weapon['weapon_seed'],
'weapon_wear' => $weapon['weapon_wear'],
@@ -98,4 +162,23 @@ class UtilsClass
return $selected;
}
public static function isKnifeDefindex(int $defindex): bool
{
return in_array($defindex, self::KNIFE_DEFINDEXES);
}
public static function isKnifeWeapon(array $weapon): bool
{
return $weapon['weapon_name'] === 'weapon_knife' ||
strpos($weapon['weapon_name'], 'knife') !== false ||
strpos($weapon['paint_name'], '★') !== false;
}
public static function clearCache(): void
{
self::$skinCache = null;
self::$weaponCache = null;
self::$knifeCache = null;
}
}

View File

@@ -0,0 +1,304 @@
<?php
require_once 'utils.php';
require_once 'database.php';
class WeaponHandler
{
private $db;
private $steamid;
public function __construct($steamid)
{
$this->db = new DataBase();
$this->steamid = $steamid;
}
public function handleWeaponUpdate($postData): bool
{
if (!isset($postData['forma'])) {
return false;
}
$formaParts = explode("-", $postData['forma']);
if ($formaParts[0] === "knife") {
return $this->handleKnifeSelection($formaParts[1]);
} else {
return $this->handleWeaponSkin($formaParts, $postData);
}
}
private function handleKnifeSelection($knifeId): bool
{
$knifes = UtilsClass::getKnifeTypes();
if (!isset($knifes[$knifeId])) {
return false;
}
$knifeData = $knifes[$knifeId];
// Clear existing knife data
$this->clearKnifeData();
// Set new knife selection (insert for both teams separately)
$this->db->query(
"INSERT INTO `wp_player_knife` (`steamid`, `knife`, `weapon_team`) VALUES (:steamid, :knife, 2)",
["steamid" => $this->steamid, "knife" => $knifeData['weapon_name']]
);
$this->db->query(
"INSERT INTO `wp_player_knife` (`steamid`, `knife`, `weapon_team`) VALUES (:steamid, :knife, 3)",
["steamid" => $this->steamid, "knife" => $knifeData['weapon_name']]
);
return true;
}
private function handleWeaponSkin($formaParts, $postData): bool
{
$defindex = $formaParts[0];
$paintId = $formaParts[1];
$skins = UtilsClass::skinsFromJson();
if (!isset($skins[$defindex][$paintId]) ||
!isset($postData['wear']) ||
!isset($postData['seed'])) {
return false;
}
$wear = $this->validateWear($postData['wear']);
$seed = $this->validateSeed($postData['seed']);
if ($wear === false || $seed === false) {
return false;
}
// Handle knife skins
if (UtilsClass::isKnifeDefindex($defindex)) {
$this->handleKnifeSkin($defindex, $paintId, $wear, $seed);
} else {
$this->handleRegularWeaponSkin($defindex, $paintId, $wear, $seed);
}
return true;
}
private function handleKnifeSkin($defindex, $paintId, $wear, $seed): void
{
$knifeMapping = UtilsClass::getKnifeMapping();
// Clear existing knife data
$this->clearKnifeData();
// Clear other knife skins
$knifeDefindexes = UtilsClass::getKnifeDefindexes();
foreach ($knifeDefindexes as $knifeDefindex) {
if ($knifeDefindex != $defindex) {
$this->db->query(
"DELETE FROM `wp_player_skins` WHERE `steamid` = :steamid AND `weapon_defindex` = :weapon_defindex",
["steamid" => $this->steamid, "weapon_defindex" => $knifeDefindex]
);
}
}
// Set knife type in wp_player_knife table
if (isset($knifeMapping[$defindex])) {
$this->db->query(
"INSERT INTO `wp_player_knife` (`steamid`, `knife`, `weapon_team`) VALUES (:steamid, :knife, 2)",
["steamid" => $this->steamid, "knife" => $knifeMapping[$defindex]]
);
$this->db->query(
"INSERT INTO `wp_player_knife` (`steamid`, `knife`, `weapon_team`) VALUES (:steamid, :knife, 3)",
["steamid" => $this->steamid, "knife" => $knifeMapping[$defindex]]
);
}
// Set knife skin
$this->upsertWeaponSkin($defindex, $paintId, $wear, $seed);
}
private function handleRegularWeaponSkin($defindex, $paintId, $wear, $seed): void
{
$this->upsertWeaponSkin($defindex, $paintId, $wear, $seed);
}
private function upsertWeaponSkin($defindex, $paintId, $wear, $seed): void
{
$selectedSkins = $this->getSelectedSkins();
if (array_key_exists($defindex, $selectedSkins)) {
// Update existing
$this->db->query(
"UPDATE wp_player_skins SET weapon_paint_id = :weapon_paint_id, weapon_wear = :weapon_wear, weapon_seed = :weapon_seed WHERE steamid = :steamid AND weapon_defindex = :weapon_defindex",
[
"weapon_paint_id" => $paintId,
"weapon_wear" => $wear,
"weapon_seed" => $seed,
"steamid" => $this->steamid,
"weapon_defindex" => $defindex
]
);
} else {
// Insert new for both teams
$this->db->query(
"INSERT INTO wp_player_skins (`steamid`, `weapon_defindex`, `weapon_paint_id`, `weapon_wear`, `weapon_seed`, `weapon_team`) VALUES (:steamid, :weapon_defindex, :weapon_paint_id, :weapon_wear, :weapon_seed, 2)",
[
"steamid" => $this->steamid,
"weapon_defindex" => $defindex,
"weapon_paint_id" => $paintId,
"weapon_wear" => $wear,
"weapon_seed" => $seed
]
);
$this->db->query(
"INSERT INTO wp_player_skins (`steamid`, `weapon_defindex`, `weapon_paint_id`, `weapon_wear`, `weapon_seed`, `weapon_team`) VALUES (:steamid, :weapon_defindex, :weapon_paint_id, :weapon_wear, :weapon_seed, 3)",
[
"steamid" => $this->steamid,
"weapon_defindex" => $defindex,
"weapon_paint_id" => $paintId,
"weapon_wear" => $wear,
"weapon_seed" => $seed
]
);
}
}
private function clearKnifeData(): void
{
$knifeDefindexes = UtilsClass::getKnifeDefindexes();
// Clear knife skins
foreach ($knifeDefindexes as $knifeDefindex) {
$this->db->query(
"DELETE FROM `wp_player_skins` WHERE `steamid` = :steamid AND `weapon_defindex` = :weapon_defindex",
["steamid" => $this->steamid, "weapon_defindex" => $knifeDefindex]
);
}
// Clear basic knife selection
$this->db->query(
"DELETE FROM `wp_player_knife` WHERE `steamid` = :steamid",
["steamid" => $this->steamid]
);
}
private function validateWear($wear)
{
$wear = floatval($wear);
return ($wear >= 0.00 && $wear <= 1.00) ? $wear : false;
}
private function validateSeed($seed)
{
$seed = intval($seed);
return ($seed >= 0) ? $seed : false;
}
public function getSelectedSkins(): array
{
$query = $this->db->select(
"SELECT `weapon_defindex`, MAX(`weapon_paint_id`) AS `weapon_paint_id`, MAX(`weapon_wear`) AS `weapon_wear`, MAX(`weapon_seed`) AS `weapon_seed`
FROM `wp_player_skins`
WHERE `steamid` = :steamid
GROUP BY `weapon_defindex`, `steamid`",
["steamid" => $this->steamid]
);
return UtilsClass::getSelectedSkins($query ?: []);
}
public function getSelectedKnife(): array
{
return $this->db->select(
"SELECT * FROM `wp_player_knife` WHERE `steamid` = :steamid LIMIT 1",
["steamid" => $this->steamid]
) ?: [];
}
public function getLoadoutData(): array
{
$weapons = UtilsClass::getWeaponsFromArray();
$knifes = UtilsClass::getKnifeTypes();
$selectedSkins = $this->getSelectedSkins();
$selectedKnife = $this->getSelectedKnife();
return [
'weapons' => $weapons,
'knifes' => $knifes,
'selectedSkins' => $selectedSkins,
'selectedKnife' => $selectedKnife,
'displayKnife' => $this->getDisplayKnife($selectedSkins, $selectedKnife, $knifes)
];
}
private function getDisplayKnife($selectedSkins, $selectedKnife, $knifes): array
{
$skins = UtilsClass::skinsFromJson();
// Check for knife skin first
foreach ($selectedSkins as $defindex => $selectedSkin) {
if (UtilsClass::isKnifeDefindex($defindex) && isset($skins[$defindex][$selectedSkin['weapon_paint_id']])) {
return [
'data' => $skins[$defindex][$selectedSkin['weapon_paint_id']],
'source' => 'skin'
];
}
}
// Check for basic knife selection
if (!empty($selectedKnife)) {
foreach ($knifes as $knife) {
if ($selectedKnife[0]['knife'] === $knife['weapon_name']) {
return [
'data' => $knife,
'source' => 'basic'
];
}
}
}
// Default knife
return [
'data' => $knifes[0] ?? null,
'source' => 'default'
];
}
public function getOrganizedWeapons(): array
{
$weapons = UtilsClass::getWeaponsFromArray();
$knifes = UtilsClass::getKnifeTypes();
$categories = UtilsClass::getWeaponCategories();
$organized = [
'Knives' => [],
'Gloves' => []
];
// Add weapon categories
foreach ($categories as $categoryName => $weaponIds) {
$organized[$categoryName] = [];
foreach ($weaponIds as $weaponId) {
if (isset($weapons[$weaponId])) {
$organized[$categoryName][$weaponId] = $weapons[$weaponId];
}
}
}
// Add knives (exclude default)
foreach ($knifes as $knifeId => $knife) {
if ($knifeId !== 0) {
$organized['Knives'][$knifeId] = $knife;
}
}
// Remove empty categories
return array_filter($organized, function($category) {
return !empty($category);
});
}
}

View File

@@ -3,266 +3,518 @@ require_once 'class/config.php';
require_once 'class/database.php';
require_once 'steamauth/steamauth.php';
require_once 'class/utils.php';
require_once 'class/weapon_handler.php';
$db = new DataBase();
if (isset($_SESSION['steamid'])) {
$steamid = $_SESSION['steamid'];
$weapons = UtilsClass::getWeaponsFromArray();
$skins = UtilsClass::skinsFromJson();
$querySelected = $db->select("
SELECT `weapon_defindex`, MAX(`weapon_paint_id`) AS `weapon_paint_id`, MAX(`weapon_wear`) AS `weapon_wear`, MAX(`weapon_seed`) AS `weapon_seed`
FROM `wp_player_skins`
WHERE `steamid` = :steamid
GROUP BY `weapon_defindex`, `steamid`
", ["steamid" => $steamid]);
$selectedSkins = UtilsClass::getSelectedSkins($querySelected);
$selectedKnife = $db->select("SELECT * FROM `wp_player_knife` WHERE `wp_player_knife`.`steamid` = :steamid LIMIT 1", ["steamid" => $steamid]);
$knifes = UtilsClass::getKnifeTypes();
if (isset($_POST['forma'])) {
$ex = explode("-", $_POST['forma']);
if ($ex[0] == "knife") {
$db->query("INSERT INTO `wp_player_knife` (`steamid`, `knife`, `weapon_team`) VALUES(:steamid, :knife, 2) ON DUPLICATE KEY UPDATE `knife` = :knife", ["steamid" => $steamid, "knife" => $knifes[$ex[1]]['weapon_name']]);
$db->query("INSERT INTO `wp_player_knife` (`steamid`, `knife`, `weapon_team`) VALUES(:steamid, :knife, 3) ON DUPLICATE KEY UPDATE `knife` = :knife", ["steamid" => $steamid, "knife" => $knifes[$ex[1]]['weapon_name']]);
} else {
if (array_key_exists($ex[1], $skins[$ex[0]]) && isset($_POST['wear']) && $_POST['wear'] >= 0.00 && $_POST['wear'] <= 1.00 && isset($_POST['seed'])) {
$wear = floatval($_POST['wear']); // wear
$seed = intval($_POST['seed']); // seed
if (array_key_exists($ex[0], $selectedSkins)) {
$db->query("UPDATE wp_player_skins SET weapon_paint_id = :weapon_paint_id, weapon_wear = :weapon_wear, weapon_seed = :weapon_seed WHERE steamid = :steamid AND weapon_defindex = :weapon_defindex", ["steamid" => $steamid, "weapon_defindex" => $ex[0], "weapon_paint_id" => $ex[1], "weapon_wear" => $wear, "weapon_seed" => $seed]);
} else {
$db->query("INSERT INTO wp_player_skins (`steamid`, `weapon_defindex`, `weapon_paint_id`, `weapon_wear`, `weapon_seed`, `weapon_team`) VALUES (:steamid, :weapon_defindex, :weapon_paint_id, :weapon_wear, :weapon_seed, 2)", ["steamid" => $steamid, "weapon_defindex" => $ex[0], "weapon_paint_id" => $ex[1], "weapon_wear" => $wear, "weapon_seed" => $seed]);
$db->query("INSERT INTO wp_player_skins (`steamid`, `weapon_defindex`, `weapon_paint_id`, `weapon_wear`, `weapon_seed`, `weapon_team`) VALUES (:steamid, :weapon_defindex, :weapon_paint_id, :weapon_wear, :weapon_seed, 3)", ["steamid" => $steamid, "weapon_defindex" => $ex[0], "weapon_paint_id" => $ex[1], "weapon_wear" => $wear, "weapon_seed" => $seed]);
}
}
}
// Handle weapon updates
if (isset($_SESSION['steamid']) && isset($_POST['forma'])) {
$weaponHandler = new WeaponHandler($_SESSION['steamid']);
if ($weaponHandler->handleWeaponUpdate($_POST)) {
header("Location: {$_SERVER['PHP_SELF']}");
exit;
}
}
// Get loadout data for logged in users
$loadoutData = null;
$weaponCategories = [];
if (isset($_SESSION['steamid'])) {
require_once 'steamauth/userInfo.php';
$weaponHandler = new WeaponHandler($_SESSION['steamid']);
$loadoutData = $weaponHandler->getLoadoutData();
$weaponCategories = $weaponHandler->getOrganizedWeapons();
}
?>
<!DOCTYPE html>
<html lang="en"<?php if(WEB_STYLE_DARK) echo 'data-bs-theme="dark"'?>>
<html lang="en" data-theme="dark">
<head>
<meta charset="utf-8">
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-T3c6CoIi6uLrA9TneNEoa7RxnatzjcDSCmG1MXxSR1GAsXEV/Dwwykc2MPK8M2HN" crossorigin="anonymous">
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/js/bootstrap.bundle.min.js" integrity="sha384-C6RzsynM9kWDrMNeT87bh95OGNyZPhcTNXj1NW7RuBCsyN/o0jlpcV8Qyq46cDfL" crossorigin="anonymous"></script>
<script src="https://code.jquery.com/jquery-3.6.4.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@4.6.0/dist/js/bootstrap.min.js"></script>
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="style.css">
<title>CS2 Simple Weapon Paints</title>
<title>CS2 Weapon Paints</title>
</head>
<body>
<?php
if (!isset($_SESSION['steamid'])) {
echo "<div class='bg-primary'><h2>To choose weapon paints loadout, you need to ";
loginbutton("rectangle");
echo "</h2></div>";
} else {
echo "<div class='bg-primary'><h2>Your current weapon skin loadout <a class='btn btn-danger' href='{$_SERVER['PHP_SELF']}?logout'>Logout</a></h2> </div>";
echo "<div class='card-group mt-2'>";
?>
<div class="col-sm-2">
<div class="card text-center mb-3 border border-primary">
<div class="card-body">
<?php
$actualKnife = $knifes[0];
if ($selectedKnife != null)
{
foreach ($knifes as $knife) {
if ($selectedKnife[0]['knife'] == $knife['weapon_name']) {
$actualKnife = $knife;
break;
}
}
}
echo "<div class='card-header'>";
echo "<h6 class='card-title item-name'>Knife type</h6>";
echo "<h5 class='card-title item-name'>{$actualKnife["paint_name"]}</h5>";
echo "</div>";
echo "<img src='{$actualKnife["image_url"]}' class='skin-image'>";
?>
<?php if (!isset($_SESSION['steamid'])): ?>
<div class="login-container">
<div class="login-card">
<h1>CS2 Weapon Paints</h1>
<p>Connect your Steam account to customize your weapon loadout</p>
<?php loginbutton("rectangle"); ?>
</div>
</div>
<?php else: ?>
<div class="app-container">
<!-- Header -->
<header class="app-header">
<div class="header-left">
<h1>CS2 Weapon Paints</h1>
</div>
<div class="card-footer">
<form action="" method="POST">
<select name="forma" class="form-control select" onchange="this.form.submit()" class="SelectWeapon">
<option disabled>Select knife</option>
<?php
foreach ($knifes as $knifeKey => $knife) {
if ($selectedKnife[0]['knife'] == $knife['weapon_name'])
echo "<option selected value=\"knife-{$knifeKey}\">{$knife['paint_name']}</option>";
else
echo "<option value=\"knife-{$knifeKey}\">{$knife['paint_name']}</option>";
}
<div class="header-right">
<span class="user-info">Welcome, <?php echo htmlspecialchars($_SESSION['steam_personaname'] ?? 'Player'); ?></span>
<a href="<?php echo $_SERVER['PHP_SELF']; ?>?logout" class="logout-btn">Logout</a>
</div>
</header>
<div class="app-main">
<!-- Sidebar -->
<aside class="sidebar">
<div class="sidebar-header">
<h3>Weapons</h3>
<div class="search-container">
<input type="text" id="weaponSearch" placeholder="Search weapons..." onkeyup="searchWeapons(this.value)">
</div>
</div>
<nav class="sidebar-nav">
<?php foreach ($weaponCategories as $categoryName => $categoryWeapons): ?>
<?php if (empty($categoryWeapons)) continue; ?>
<div class="nav-category">
<div class="nav-item category-header" data-category="<?php echo strtolower($categoryName); ?>" onclick="toggleCategory('<?php echo strtolower($categoryName); ?>')">
<div class="nav-content">
<span class="nav-text"><?php echo $categoryName; ?></span>
<span class="nav-count"><?php echo count($categoryWeapons); ?></span>
</div>
<span class="nav-arrow">▶</span>
</div>
<div class="weapon-list" data-category="<?php echo strtolower($categoryName); ?>">
<?php if ($categoryName === 'Knives'): ?>
<?php foreach ($categoryWeapons as $knifeId => $knife): ?>
<div class="weapon-container">
<div class="weapon-item" onclick="toggleKnifeSkins(<?php echo $knifeId; ?>)">
<img src="<?php echo htmlspecialchars($knife['image_url']); ?>" alt="<?php echo htmlspecialchars($knife['paint_name']); ?>" class="weapon-icon">
<span class="weapon-name"><?php echo htmlspecialchars($knife['paint_name']); ?></span>
<span class="weapon-arrow">▶</span>
</div>
<div class="weapon-skins-grid" data-weapon="knife-<?php echo $knifeId; ?>"></div>
</div>
<?php endforeach; ?>
<?php else: ?>
<?php foreach ($categoryWeapons as $weaponId => $weapon): ?>
<div class="weapon-container">
<div class="weapon-item" onclick="toggleWeaponSkins(<?php echo $weaponId; ?>)">
<img src="<?php echo htmlspecialchars($weapon['image_url']); ?>" alt="<?php echo htmlspecialchars($weapon['paint_name']); ?>" class="weapon-icon">
<span class="weapon-name"><?php echo htmlspecialchars(ucfirst(strtolower(str_replace('weapon_', '', $weapon['weapon_name'])))); ?></span>
<span class="weapon-arrow">▶</span>
</div>
<div class="weapon-skins-grid" data-weapon="<?php echo $weaponId; ?>"></div>
</div>
<?php endforeach; ?>
<?php endif; ?>
</div>
</div>
<?php endforeach; ?>
</nav>
</aside>
<!-- Main Content -->
<main class="main-content">
<div class="loadout-header">
<h2>Current Loadout</h2>
<p>Hover over items to customize</p>
</div>
<div class="loadout-grid">
<?php
$displayKnife = $loadoutData['displayKnife'];
$knifeEquipped = ($displayKnife['source'] === 'skin') ? 'true' : 'false';
?>
<div class="loadout-item" data-weapon-type="knife" data-equipped="<?php echo $knifeEquipped; ?>">
<div class="item-image-container">
<img src="<?php echo htmlspecialchars($displayKnife['data']['image_url']); ?>"
alt="<?php echo htmlspecialchars($displayKnife['data']['paint_name']); ?>"
class="item-image">
<div class="item-overlay">
<button class="customize-btn" onclick="openCustomizeModal('knife', 0)">Customize</button>
</div>
</div>
<div class="item-info">
<div class="item-category">Knife</div>
<div class="item-name"><?php echo htmlspecialchars($displayKnife['data']['paint_name']); ?></div>
</div>
</div>
<?php foreach ($loadoutData['weapons'] as $defindex => $weapon): ?>
<?php if (UtilsClass::isKnifeWeapon($weapon)) continue; ?>
<?php
$hasCustomSkin = array_key_exists($defindex, $loadoutData['selectedSkins']);
$isEquipped = $hasCustomSkin ? 'true' : 'false';
?>
</select>
<div class="loadout-item" data-weapon-id="<?php echo $defindex; ?>" data-equipped="<?php echo $isEquipped; ?>">
<div class="item-image-container">
<?php if ($hasCustomSkin): ?>
<?php
$skins = UtilsClass::skinsFromJson();
$selectedSkin = $skins[$defindex][$loadoutData['selectedSkins'][$defindex]['weapon_paint_id']];
?>
<img src="<?php echo htmlspecialchars($selectedSkin['image_url']); ?>"
alt="<?php echo htmlspecialchars($selectedSkin['paint_name']); ?>"
class="item-image">
<div class="item-overlay">
<button class="customize-btn" onclick="openCustomizeModal('weapon', <?php echo $defindex; ?>)">Customize</button>
</div>
<?php else: ?>
<img src="<?php echo htmlspecialchars($weapon['image_url']); ?>"
alt="<?php echo htmlspecialchars($weapon['paint_name']); ?>"
class="item-image">
<?php endif; ?>
</div>
<div class="item-info">
<div class="item-category"><?php echo htmlspecialchars(ucfirst(strtolower(str_replace('weapon_', '', $weapon['weapon_name'])))); ?></div>
<div class="item-name">
<?php if ($hasCustomSkin): ?>
<?php echo htmlspecialchars($selectedSkin['paint_name']); ?>
<?php else: ?>
<?php echo htmlspecialchars($weapon['paint_name']); ?>
<?php endif; ?>
</div>
</div>
</div>
<?php endforeach; ?>
</div>
</main>
</div>
<!-- Footer -->
<footer class="app-footer">
<div class="footer-content">
<p>Created with ❤️ by <strong><a target="_blank" href="https://github.com/BramSuurdje" rel="noopener noreferrer">Bram</a></strong></p>
</div>
</footer>
</div>
<!-- Customize Modal -->
<div id="customizeModal" class="modal hidden">
<div class="modal-content">
<div class="modal-header">
<h3 id="modalTitle">Customize Weapon</h3>
<button class="close-btn" onclick="closeCustomizeModal()">&times;</button>
</div>
<div class="modal-body">
<form id="customizeForm" method="POST">
<input type="hidden" id="customizeWeaponId" name="forma" value="">
<div class="customize-grid">
<div class="customize-section">
<label for="wearSelect">Wear Condition</label>
<select id="wearSelect" name="wearSelect" onchange="updateWearValue(this.value)">
<option value="0.00">Factory New</option>
<option value="0.07">Minimal Wear</option>
<option value="0.15">Field-Tested</option>
<option value="0.38">Well-Worn</option>
<option value="0.45">Battle-Scarred</option>
</select>
</div>
<div class="customize-section">
<label for="wearInput">Float Value</label>
<input type="number" id="wearInput" name="wear" min="0" max="1" step="0.001" value="0.00">
</div>
<div class="customize-section">
<label for="seedInput">Pattern Seed</label>
<input type="number" id="seedInput" name="seed" min="0" max="1000" value="0">
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" onclick="closeCustomizeModal()">Cancel</button>
<button type="submit" class="btn btn-primary">Apply Changes</button>
</div>
</form>
</div>
</div>
</div>
<?php
foreach ($weapons as $defindex => $default) { ?>
<div class="col-sm-2">
<div class="card text-center mb-3">
<div class="card-body">
<?php
if (array_key_exists($defindex, $selectedSkins)) {
echo "<div class='card-header'>";
echo "<h5 class='card-title item-name'>{$skins[$defindex][$selectedSkins[$defindex]['weapon_paint_id']]["paint_name"]}</h5>";
echo "</div>";
echo "<img src='{$skins[$defindex][$selectedSkins[$defindex]['weapon_paint_id']]['image_url']}' class='skin-image'>";
} else {
echo "<div class='card-header'>";
echo "<h5 class='card-title item-name'>{$default["paint_name"]}</h5>";
echo "</div>";
echo "<img src='{$default["image_url"]}' class='skin-image'>";
<script>
// Optimized JavaScript with embedded data (restored functionality)
const weaponsData = <?php echo json_encode($loadoutData['weapons']); ?>;
const skinsData = <?php echo json_encode(UtilsClass::skinsFromJson()); ?>;
const selectedSkinsData = <?php echo json_encode($loadoutData['selectedSkins']); ?>;
const knivesData = <?php echo json_encode($loadoutData['knifes']); ?>;
const WeaponApp = {
init() {
this.bindEvents();
},
bindEvents() {
document.addEventListener('click', (e) => {
if (e.target.id === 'customizeModal') {
this.closeCustomizeModal();
}
?>
</div>
<div class="card-footer">
<form action="" method="POST">
<select name="forma" class="form-control select" onchange="this.form.submit()" class="SelectWeapon">
<option disabled>Select skin</option>
<?php
foreach ($skins[$defindex] as $paintKey => $paint) {
if (array_key_exists($defindex, $selectedSkins) && $selectedSkins[$defindex]['weapon_paint_id'] == $paintKey)
echo "<option selected value=\"{$defindex}-{$paintKey}\">{$paint['paint_name']}</option>";
else
echo "<option value=\"{$defindex}-{$paintKey}\">{$paint['paint_name']}</option>";
}
?>
</select>
<br></br>
<?php
$selectedSkinInfo = isset($selectedSkins[$defindex]) ? $selectedSkins[$defindex] : null;
$steamid = $_SESSION['steamid'];
});
},
if ($selectedSkinInfo) :
?>
<button type="button" class="btn btn-primary" data-toggle="modal" data-target="#weaponModal<?php echo $defindex ?>">
Settings
</button>
<?php else : ?>
<button type="button" class="btn btn-primary" onclick="showSkinSelectionAlert()">
Settings
</button>
<script>
function showSkinSelectionAlert() {
alert("You need to select a skin first.");
}
</script>
<?php endif; ?>
</div>
<?php
// wear value
$selectedSkinInfo = isset($selectedSkins[$defindex]['weapon_paint_id']) ? $selectedSkins[$defindex] : null;
$queryWear = $selectedSkins[$defindex]['weapon_wear'] ?? 1.0;
$initialWearValue = isset($selectedSkinInfo['weapon_wear']) ? $selectedSkinInfo['weapon_wear'] : (isset($queryWear[0]['weapon_wear']) ? $queryWear[0] : 0.0);
// seed value
$querySeed = $selectedSkins[$defindex]['weapon_seed'] ?? 0;
$initialSeedValue = isset($selectedSkinInfo['weapon_seed']) ? $selectedSkinInfo['weapon_seed'] : 0;
?>
<div class="modal fade" id="weaponModal<?php echo $defindex ?>" tabindex="-1" role="dialog" aria-labelledby="weaponModalLabel<?php echo $defindex ?>" aria-hidden="true">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class='card-title item-name'>
<?php
if (array_key_exists($defindex, $selectedSkins)) {
echo "{$skins[$defindex][$selectedSkins[$defindex]['weapon_paint_id']]["paint_name"]} Settings";
} else {
echo "{$default["paint_name"]} Settings";
}
?>
</h5>
</div>
<div class="modal-body">
<div class="form-group">
<select class="form-select" id="wearSelect<?php echo $defindex ?>" name="wearSelect" onchange="updateWearValue<?php echo $defindex ?>(this.value)">
<option disabled>Select Wear</option>
<option value="0.00" <?php echo ($initialWearValue == 0.00) ? 'selected' : ''; ?>>Factory New</option>
<option value="0.07" <?php echo ($initialWearValue == 0.07) ? 'selected' : ''; ?>>Minimal Wear</option>
<option value="0.15" <?php echo ($initialWearValue == 0.15) ? 'selected' : ''; ?>>Field-Tested</option>
<option value="0.38" <?php echo ($initialWearValue == 0.38) ? 'selected' : ''; ?>>Well-Worn</option>
<option value="0.45" <?php echo ($initialWearValue == 0.45) ? 'selected' : ''; ?>>Battle-Scarred</option>
</select>
</div>
<div class="row">
<div class="col-md-6">
<div class="form-group">
<label for="wear">Wear:</label>
<input type="text" value="<?php echo $initialWearValue; ?>" class="form-control" id="wear<?php echo $defindex ?>" name="wear">
</div>
</div>
<div class="col-md-6">
<div class="form-group">
<label for="seed">Seed:</label>
<input type="text" value="<?php echo $initialSeedValue; ?>" class="form-control" id="seed<?php echo $defindex ?>" name="seed" oninput="validateSeed(this)">
</div>
</div>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-dismiss="modal">Close</button>
<button type="submit" class="btn btn-danger">Use</button>
</form>
</div>
</div>
</div>
</div>
</div>
</div>
<script>
// wear
function updateWearValue<?php echo $defindex ?>(selectedValue) {
var wearInputElement = document.getElementById("wear<?php echo $defindex ?>");
wearInputElement.value = selectedValue;
}
function validateWear(inputElement) {
inputElement.value = inputElement.value.replace(/[^0-9]/g, '');
}
// seed
function validateSeed(input) {
// Check entered value
var inputValue = input.value.replace(/[^0-9]/g, ''); // Just get the numbers
if (inputValue === "") {
input.value = 0; // Set to 0 if empty or no numbers
toggleCategory(category) {
const categoryHeader = document.querySelector(`.category-header[data-category="${category}"]`);
const weaponList = document.querySelector(`.weapon-list[data-category="${category}"]`);
const arrow = categoryHeader.querySelector('.nav-arrow');
if (weaponList.classList.contains('expanded')) {
weaponList.classList.remove('expanded');
arrow.textContent = '▶';
categoryHeader.classList.remove('active');
} else {
var numericValue = parseInt(inputValue);
numericValue = Math.min(1000, Math.max(1, numericValue)); // Interval control
input.value = numericValue;
// Collapse other categories
document.querySelectorAll('.weapon-list').forEach(list => list.classList.remove('expanded'));
document.querySelectorAll('.category-header').forEach(header => {
header.classList.remove('active');
header.querySelector('.nav-arrow').textContent = '▶';
});
weaponList.classList.add('expanded');
arrow.textContent = '▼';
categoryHeader.classList.add('active');
}
}
</script>
<?php } ?>
<?php } ?>
</div>
</div>
<div class="container">
<footer class="d-flex flex-wrap justify-content-between align-items-center py-3 my-4 border-top">
<div class="col-md-4 d-flex align-items-center">
<span class="mb-3 mb-md-0 text-body-secondary">© 2023 <a href="https://github.com/Nereziel/cs2-WeaponPaints">Nereziel/cs2-WeaponPaints</a></span>
</div>
</footer>
</div>
</body>
},
</html>
searchWeapons(query) {
const searchTerm = query.toLowerCase().trim();
if (!searchTerm) {
document.querySelectorAll('.nav-category').forEach(category => category.style.display = 'block');
document.querySelectorAll('.weapon-list').forEach(list => list.classList.remove('expanded'));
document.querySelectorAll('.category-header').forEach(header => {
header.classList.remove('active');
header.querySelector('.nav-arrow').textContent = '▶';
});
return;
}
document.querySelectorAll('.nav-category').forEach(category => {
const categoryName = category.querySelector('.nav-text').textContent.toLowerCase();
const weaponItems = category.querySelectorAll('.weapon-item');
let hasMatches = categoryName.includes(searchTerm);
weaponItems.forEach(item => {
const weaponName = item.querySelector('.weapon-name').textContent.toLowerCase();
const matches = weaponName.includes(searchTerm);
item.style.display = matches ? 'flex' : 'none';
if (matches) hasMatches = true;
});
category.style.display = hasMatches ? 'block' : 'none';
if (hasMatches) {
const weaponList = category.querySelector('.weapon-list');
const header = category.querySelector('.category-header');
weaponList.classList.add('expanded');
header.classList.add('active');
header.querySelector('.nav-arrow').textContent = '▼';
}
});
},
toggleWeaponSkins(weaponId) {
const weaponItem = event.target.closest('.weapon-item');
const skinGrid = weaponItem.parentNode.querySelector('.weapon-skins-grid');
if (!skinsData[weaponId]) return;
if (skinGrid.classList.contains('expanded')) {
skinGrid.classList.remove('expanded');
weaponItem.classList.remove('expanded');
return;
}
// Collapse others
document.querySelectorAll('.weapon-skins-grid').forEach(grid => grid.classList.remove('expanded'));
document.querySelectorAll('.weapon-item').forEach(item => item.classList.remove('expanded'));
this.populateWeaponSkins(weaponId, skinGrid);
skinGrid.classList.add('expanded');
weaponItem.classList.add('expanded');
},
toggleKnifeSkins(knifeType) {
const weaponItem = event.target.closest('.weapon-item');
const skinGrid = weaponItem.parentNode.querySelector('.weapon-skins-grid');
if (skinGrid.classList.contains('expanded')) {
skinGrid.classList.remove('expanded');
weaponItem.classList.remove('expanded');
return;
}
// Collapse others
document.querySelectorAll('.weapon-skins-grid').forEach(grid => grid.classList.remove('expanded'));
document.querySelectorAll('.weapon-item').forEach(item => item.classList.remove('expanded'));
this.populateKnifeTypeSkins(knifeType, skinGrid);
skinGrid.classList.add('expanded');
weaponItem.classList.add('expanded');
},
populateWeaponSkins(weaponId, skinGrid) {
const skinsContainer = document.createElement('div');
skinsContainer.className = 'skins-container';
skinGrid.innerHTML = '';
Object.entries(skinsData[weaponId]).forEach(([paintId, skin]) => {
const skinOption = document.createElement('div');
skinOption.className = 'skin-option';
if (selectedSkinsData[weaponId] && selectedSkinsData[weaponId].weapon_paint_id == paintId) {
skinOption.classList.add('active');
}
skinOption.onclick = () => this.equipSkin(weaponId, paintId);
skinOption.innerHTML = `
<img src="${skin.image_url}" alt="${skin.paint_name}" loading="lazy">
<div class="skin-option-name">${skin.paint_name.replace(/.*\| /, '')}</div>
`;
skinsContainer.appendChild(skinOption);
});
skinGrid.appendChild(skinsContainer);
},
populateKnifeTypeSkins(knifeType, skinGrid) {
const skinsContainer = document.createElement('div');
skinsContainer.className = 'skins-container';
skinGrid.innerHTML = '';
// ALWAYS show the basic knife option first
const knife = knivesData[knifeType];
if (knife) {
const basicKnifeOption = document.createElement('div');
basicKnifeOption.className = 'skin-option';
// Check if basic knife is currently selected (no knife skins equipped for this type)
if (!selectedSkinsData[knifeType]) {
basicKnifeOption.classList.add('active');
}
basicKnifeOption.onclick = () => this.equipKnife(knifeType);
basicKnifeOption.innerHTML = `
<img src="${knife.image_url}" alt="${knife.paint_name}" loading="lazy">
<div class="skin-option-name">Default</div>
`;
skinsContainer.appendChild(basicKnifeOption);
}
// Then show knife skins if available
if (skinsData[knifeType]) {
Object.entries(skinsData[knifeType]).forEach(([paintId, skin]) => {
// Skip the default skin (paint ID 0) since we already show it as "Default" option above
if (paintId == '0') {
return;
}
const skinOption = document.createElement('div');
skinOption.className = 'skin-option';
// Check if this skin is currently equipped
if (selectedSkinsData[knifeType] && selectedSkinsData[knifeType].weapon_paint_id == paintId) {
skinOption.classList.add('active');
}
skinOption.onclick = () => this.equipSkin(knifeType, paintId);
skinOption.innerHTML = `
<img src="${skin.image_url}" alt="${skin.paint_name}" loading="lazy">
<div class="skin-option-name">${skin.paint_name.replace(/.*\| /, '')}</div>
`;
skinsContainer.appendChild(skinOption);
});
}
skinGrid.appendChild(skinsContainer);
},
equipSkin(weaponId, paintId) {
this.submitForm(`${weaponId}-${paintId}`, { wear: '0.00', seed: '0' });
},
equipKnife(knifeId) {
this.submitForm(`knife-${knifeId}`);
},
submitForm(forma, additionalData = {}) {
const form = document.createElement('form');
form.method = 'POST';
form.style.display = 'none';
const formaInput = document.createElement('input');
formaInput.name = 'forma';
formaInput.value = forma;
form.appendChild(formaInput);
Object.entries(additionalData).forEach(([name, value]) => {
const input = document.createElement('input');
input.name = name;
input.value = value;
form.appendChild(input);
});
document.body.appendChild(form);
form.submit();
},
openCustomizeModal(type, weaponId) {
const modal = document.getElementById('customizeModal');
const title = document.getElementById('modalTitle');
const weaponIdInput = document.getElementById('customizeWeaponId');
const wearSelect = document.getElementById('wearSelect');
const wearInput = document.getElementById('wearInput');
const seedInput = document.getElementById('seedInput');
if (type === 'knife') {
title.textContent = 'Customize Knife';
weaponIdInput.value = 'knife-0';
} else {
const weaponName = weaponsData[weaponId] ? weaponsData[weaponId].weapon_name.replace('weapon_', '').toUpperCase() : 'Weapon';
title.textContent = `Customize ${weaponName}`;
weaponIdInput.value = `${weaponId}-${selectedSkinsData[weaponId]?.weapon_paint_id || 0}`;
if (selectedSkinsData[weaponId]) {
const wear = parseFloat(selectedSkinsData[weaponId].weapon_wear);
wearInput.value = selectedSkinsData[weaponId].weapon_wear;
seedInput.value = selectedSkinsData[weaponId].weapon_seed;
// Set wear select
if (wear <= 0.00) wearSelect.value = "0.00";
else if (wear <= 0.07) wearSelect.value = "0.07";
else if (wear <= 0.15) wearSelect.value = "0.15";
else if (wear <= 0.38) wearSelect.value = "0.38";
else wearSelect.value = "0.45";
}
}
modal.classList.remove('hidden');
},
closeCustomizeModal() {
document.getElementById('customizeModal').classList.add('hidden');
},
updateWearValue(selectedValue) {
document.getElementById('wearInput').value = selectedValue;
}
};
// Global functions for onclick handlers
const toggleCategory = (category) => WeaponApp.toggleCategory(category);
const searchWeapons = (query) => WeaponApp.searchWeapons(query);
const toggleWeaponSkins = (weaponId) => WeaponApp.toggleWeaponSkins(weaponId);
const toggleKnifeSkins = (knifeType) => WeaponApp.toggleKnifeSkins(knifeType);
const openCustomizeModal = (type, weaponId) => WeaponApp.openCustomizeModal(type, weaponId);
const closeCustomizeModal = () => WeaponApp.closeCustomizeModal();
const updateWearValue = (value) => WeaponApp.updateWearValue(value);
// Initialize app
WeaponApp.init();
</script>
<?php endif; ?>
</body>
</html>

View File

@@ -1,16 +1,765 @@
.bg-primary {
padding: 15px;
/* Reset and base styles */
*,
*::before,
*::after {
margin: 0;
padding: 0;
box-sizing: border-box;
}
.card-title item-name {
//text-align:center;
font-weight: bold;
:root {
/* Colors */
--bg-primary: #1a1a1a;
--bg-secondary: #2a2a2a;
--bg-tertiary: #3a3a3a;
--bg-card: #2d2d2d;
--bg-hover: #404040;
--border-color: #444;
--text-primary: #ffffff;
--text-secondary: #b3b3b3;
--text-muted: #888;
--accent-blue: #4a9eff;
--accent-blue-hover: #357abd;
--accent-orange: #ff6b35;
--accent-green: #4caf50;
/* Shadows and Effects */
--shadow: 0 4px 12px rgba(0, 0, 0, 0.3);
--shadow-lg: 0 8px 24px rgba(0, 0, 0, 0.4);
--border-radius: 8px;
--transition: all 0.2s ease;
/* Layout */
--sidebar-width: 320px;
--header-height: 70px;
}
.skin-image {
margin: 0 auto;
display: block;
body {
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
background: linear-gradient(135deg, var(--bg-primary) 0%, #1e1e1e 100%);
color: var(--text-primary);
min-height: 100vh;
line-height: 1.6;
}
/* Common button styles */
.btn,
button {
border: none;
border-radius: var(--border-radius);
cursor: pointer;
font-weight: 600;
transition: var(--transition);
text-transform: uppercase;
letter-spacing: 0.5px;
font-size: 0.9rem;
padding: 0.75rem 1.5rem;
}
.btn-primary,
.customize-btn,
.equip-btn {
background: var(--accent-blue);
color: white;
}
.btn-primary:hover,
.customize-btn:hover,
.equip-btn:hover {
background: var(--accent-blue-hover);
transform: translateY(-1px);
}
.btn-secondary {
background: var(--bg-tertiary);
color: var(--text-secondary);
border: 1px solid var(--border-color);
}
.btn-secondary:hover {
background: var(--bg-hover);
color: var(--text-primary);
}
.equip-btn {
background: var(--accent-green);
}
.equip-btn:hover {
background: #45a049;
}
.logout-btn {
background: var(--accent-orange);
color: white;
text-decoration: none;
padding: 0.5rem 1rem;
border-radius: var(--border-radius);
transition: var(--transition);
font-weight: 500;
}
.logout-btn:hover {
background: #e55a2b;
text-decoration: none;
}
/* Login Page */
.login-container {
display: flex;
justify-content: center;
align-items: center;
min-height: 100vh;
padding: 20px;
}
.login-card {
background: var(--bg-card);
padding: 40px;
border-radius: var(--border-radius);
box-shadow: var(--shadow-lg);
text-align: center;
width: 50%;
//border-bottom: solid 1px #eee;
max-width: 400px;
width: 100%;
border: 1px solid var(--border-color);
}
.login-card h1 {
margin-bottom: 10px;
color: var(--accent-blue);
font-size: 2.5rem;
font-weight: 700;
}
.login-card p {
margin-bottom: 30px;
color: var(--text-secondary);
font-size: 1.1rem;
}
/* App Layout */
.app-container {
display: flex;
flex-direction: column;
min-height: 100vh;
}
.app-header {
background: var(--bg-secondary);
padding: 1rem 2rem;
border-bottom: 1px solid var(--border-color);
display: flex;
justify-content: space-between;
align-items: center;
box-shadow: var(--shadow);
height: var(--header-height);
}
.header-left h1 {
color: var(--accent-blue);
font-size: 1.8rem;
font-weight: 700;
}
.header-right {
display: flex;
align-items: center;
gap: 1rem;
}
.user-info {
color: var(--text-secondary);
font-size: 0.9rem;
}
.app-main {
display: flex;
flex: 1;
overflow: hidden;
min-height: 0;
}
/* Sidebar */
.sidebar {
width: var(--sidebar-width);
background: var(--bg-secondary);
border-right: 1px solid var(--border-color);
display: flex;
flex-direction: column;
overflow: hidden;
}
.sidebar-header {
padding: 1.5rem;
border-bottom: 1px solid var(--border-color);
}
.sidebar-header h3 {
color: var(--text-primary);
font-size: 1.3rem;
font-weight: 600;
margin-bottom: 1rem;
}
.search-container {
position: relative;
}
.search-container input {
width: 100%;
background: var(--bg-tertiary);
border: 1px solid var(--border-color);
border-radius: var(--border-radius);
padding: 0.75rem;
color: var(--text-primary);
font-size: 0.9rem;
transition: var(--transition);
}
.search-container input:focus {
outline: none;
border-color: var(--accent-blue);
box-shadow: 0 0 0 2px rgba(74, 158, 255, 0.2);
}
.search-container input::placeholder {
color: var(--text-muted);
}
.sidebar-nav {
flex: 1;
padding: 1rem 0;
overflow-y: auto;
overflow-x: hidden;
scrollbar-width: thin;
}
/* Navigation Items */
.nav-item {
display: flex;
align-items: center;
padding: 1rem 1.5rem;
cursor: pointer;
transition: var(--transition);
border-left: 3px solid transparent;
}
.nav-item:hover,
.nav-item.active {
background: var(--bg-hover);
border-left-color: var(--accent-blue);
}
.nav-icon {
font-size: 1.2rem;
margin-right: 0.75rem;
width: 24px;
text-align: center;
}
.nav-content {
flex: 1;
display: flex;
align-items: center;
justify-content: space-between;
}
.nav-text {
font-weight: 500;
color: var(--text-primary);
}
.nav-count {
background: var(--bg-tertiary);
color: var(--text-secondary);
font-size: 0.75rem;
padding: 0.2rem 0.5rem;
border-radius: 12px;
font-weight: 600;
min-width: 20px;
text-align: center;
}
.nav-item.active .nav-count {
background: var(--accent-blue);
color: white;
}
.nav-arrow {
color: var(--text-muted);
transition: var(--transition);
font-size: 0.8rem;
margin-left: 0.5rem;
}
/* Weapon Lists */
.nav-category {
margin-bottom: 0.5rem;
}
.weapon-list {
max-height: 0;
overflow: hidden;
transition: max-height 0.3s ease;
background: rgba(0, 0, 0, 0.1);
}
.weapon-list.expanded {
max-height: none;
}
.weapon-container:last-child {
border-bottom: none;
}
.weapon-item {
display: flex;
align-items: center;
padding: 0.75rem 2rem;
cursor: pointer;
transition: var(--transition);
border-bottom: 1px solid rgba(255, 255, 255, 0.05);
}
.weapon-item:hover,
.weapon-item.expanded {
background: var(--bg-hover);
}
.weapon-icon {
width: 32px;
height: 20px;
object-fit: contain;
margin-right: 0.75rem;
border-radius: 4px;
}
.weapon-name {
flex: 1;
font-size: 0.85rem;
color: var(--text-secondary);
}
.weapon-arrow {
color: var(--text-muted);
font-size: 0.7rem;
transition: var(--transition);
}
.weapon-item.expanded .weapon-arrow {
transform: rotate(90deg);
}
.weapon-skins-grid {
max-height: 0;
overflow: hidden;
transition: max-height 0.3s ease;
background: var(--bg-primary);
}
.weapon-skins-grid.expanded {
max-height: none;
padding: 1rem;
}
.skins-container {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(120px, 1fr));
gap: 0.5rem;
}
.skin-option {
background: var(--bg-secondary);
border-radius: var(--border-radius);
border: 1px solid var(--border-color);
overflow: hidden;
cursor: pointer;
transition: var(--transition);
text-align: center;
padding: 0.5rem;
}
.skin-option:hover {
transform: translateY(-2px);
border-color: var(--accent-blue);
}
.skin-option.active {
border-color: var(--accent-green);
box-shadow: 0 0 8px rgba(76, 175, 80, 0.3);
}
.skin-option img {
width: 100%;
height: 60px;
object-fit: contain;
margin-bottom: 0.25rem;
border-radius: 4px;
}
.skin-option-name {
font-size: 0.7rem;
color: var(--text-secondary);
line-height: 1.2;
}
/* Main Content */
.main-content {
flex: 1;
padding: 2rem;
overflow-y: auto;
background: var(--bg-primary);
}
.loadout-header {
margin-bottom: 2rem;
}
.loadout-header h2 {
color: var(--text-primary);
font-size: 2rem;
font-weight: 700;
margin-bottom: 0.5rem;
}
.loadout-header p {
color: var(--text-secondary);
font-size: 1.1rem;
}
/* Loadout Grid */
.loadout-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
gap: 1.5rem;
max-width: 1200px;
}
.loadout-item {
background: var(--bg-card);
border-radius: var(--border-radius);
border: 1px solid var(--border-color);
overflow: hidden;
transition: all 0.3s ease;
position: relative;
animation: fadeInUp 0.3s ease;
}
.loadout-item:hover {
transform: translateY(-2px);
box-shadow: var(--shadow-lg);
border-color: var(--accent-blue);
}
.loadout-item[data-equipped="true"] {
border-color: var(--accent-green);
}
.loadout-item[data-equipped="true"] .item-category {
color: var(--accent-green);
}
.item-image-container {
position: relative;
aspect-ratio: 16/10;
background: linear-gradient(145deg, #2a2a2a, #1a1a1a);
display: flex;
align-items: center;
justify-content: center;
overflow: hidden;
}
.item-image {
width: 80%;
height: auto;
object-fit: contain;
transition: var(--transition);
}
.item-overlay {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: rgba(0, 0, 0, 0.8);
display: flex;
align-items: center;
justify-content: center;
opacity: 0;
transition: var(--transition);
}
.loadout-item:hover .item-overlay {
opacity: 1;
}
.item-info {
padding: 1rem;
}
.item-category {
color: var(--text-muted);
font-size: 0.8rem;
text-transform: uppercase;
letter-spacing: 1px;
margin-bottom: 0.25rem;
font-weight: 500;
}
.item-name {
color: var(--text-primary);
font-weight: 600;
font-size: 0.95rem;
line-height: 1.3;
}
/* Footer */
.app-footer {
background: var(--bg-secondary);
border-top: 1px solid var(--border-color);
padding: 1rem 0;
text-align: center;
margin-top: auto;
}
.footer-content {
max-width: 1200px;
margin: 0 auto;
padding: 0 1rem;
}
.footer-content p {
margin: 0;
color: var(--text-muted);
font-size: 0.9rem;
}
.footer-content a {
color: var(--accent-blue);
text-decoration: none;
transition: var(--transition);
}
.footer-content a:hover {
color: var(--accent-blue-hover);
text-decoration: underline;
}
/* Modals */
.modal {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: rgba(0, 0, 0, 0.8);
backdrop-filter: blur(4px);
display: flex;
align-items: center;
justify-content: center;
z-index: 1000;
transition: var(--transition);
}
.modal.hidden {
opacity: 0;
pointer-events: none;
}
.modal-content {
background: var(--bg-card);
border-radius: var(--border-radius);
border: 1px solid var(--border-color);
box-shadow: var(--shadow-lg);
max-width: 90vw;
max-height: 90vh;
overflow: hidden;
display: flex;
flex-direction: column;
width: 500px;
}
.modal-header {
padding: 1.5rem;
border-bottom: 1px solid var(--border-color);
display: flex;
justify-content: space-between;
align-items: center;
background: var(--bg-secondary);
}
.modal-header h3 {
color: var(--text-primary);
font-size: 1.4rem;
font-weight: 600;
margin: 0;
}
.close-btn {
background: none;
border: none;
color: var(--text-muted);
font-size: 1.5rem;
cursor: pointer;
padding: 0.25rem;
line-height: 1;
transition: var(--transition);
}
.close-btn:hover {
color: var(--text-primary);
}
.modal-body {
padding: 1.5rem;
}
.customize-grid {
display: grid;
gap: 1rem;
}
.customize-section {
display: flex;
flex-direction: column;
gap: 0.5rem;
}
.customize-section label {
color: var(--text-primary);
font-weight: 600;
font-size: 0.9rem;
text-transform: uppercase;
letter-spacing: 0.5px;
}
.customize-section select,
.customize-section input {
background: var(--bg-tertiary);
border: 1px solid var(--border-color);
border-radius: var(--border-radius);
padding: 0.75rem;
color: var(--text-primary);
font-size: 0.9rem;
transition: var(--transition);
}
.customize-section select:focus,
.customize-section input:focus {
outline: none;
border-color: var(--accent-blue);
box-shadow: 0 0 0 2px rgba(74, 158, 255, 0.2);
}
.modal-footer {
padding: 1rem 1.5rem;
border-top: 1px solid var(--border-color);
display: flex;
gap: 1rem;
justify-content: flex-end;
background: var(--bg-secondary);
}
/* Animations */
@keyframes fadeInUp {
from {
opacity: 0;
transform: translateY(20px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
/* Smooth scrolling */
html {
scroll-behavior: smooth;
}
/* Scrollbars */
::-webkit-scrollbar {
width: 8px;
height: 8px;
}
::-webkit-scrollbar-track {
background: var(--bg-tertiary);
border-radius: 4px;
}
::-webkit-scrollbar-thumb {
background: var(--border-color);
border-radius: 4px;
transition: background 0.2s ease;
}
::-webkit-scrollbar-thumb:hover {
background: var(--text-muted);
}
::-webkit-scrollbar-corner {
background: var(--bg-tertiary);
}
/* Firefox scrollbar styling */
.sidebar-nav,
.weapon-skins-grid,
.skins-container {
scrollbar-width: thin;
scrollbar-color: var(--border-color) var(--bg-tertiary);
}
/* Responsive Design */
@media (max-width: 1024px) {
.app-main {
flex-direction: column;
}
.sidebar {
width: 100%;
max-height: 400px;
}
.main-content {
padding: 1rem;
}
.loadout-grid {
grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
}
}
@media (max-width: 768px) {
.app-header {
padding: 1rem;
flex-direction: column;
gap: 1rem;
height: auto;
}
.header-right {
width: 100%;
justify-content: center;
}
.modal-content {
width: 90%;
margin: 1rem;
}
.loadout-header h2 {
font-size: 1.5rem;
}
}
@media (max-width: 480px) {
.loadout-grid {
grid-template-columns: repeat(auto-fill, minmax(120px, 1fr));
}
.main-content {
padding: 0.5rem;
}
}