İlan
05 Haz 2026 28 çevrimiçi üye Duyurular
Ana Sayfa Metin2 PvP Panel - Web
Ana Sayfa Forum Metin2 PvP Panel - Web Oynatılabilir Karta Basit Değişiklikler Yapabilen ...
İlan Yan banner placeholder

Oynatılabilir Karta Basit Değişiklikler Yapabilen Bir Editör

mhazar 08.04.2026 06:41 632 görüntüleme 3 cevap
Son Mesaj
mhazar
Üye
Üye
Katılım25 Ara 2025
Konular4
Mesajlar46
Elmas Konular0
Başarım0
ZirveCoin 0
Ticaret Puanı
+0 -0
08.04.2026 06:41 #1
Forum'da paylaşılan kadar görüntü odaklı değil ama gayet işlevsel iş görür nitelik de
local-bsd üzerinde Ngix-PHP8-PHP-FPM Kullanıyorum Farklı yerler de denemedim.
Gerekli oldukça güncellerim.
Kurulum için destek yok.

mob_proto.txt,mob_names.txt ve mobproto.php aynı klasörde olmak zorunda

pkg install -y nginx php82 php82-mysqli php82-mbstring php82-gd php82-curl php82-zlib php82-zip php82-xml php82-fpm php82-filter php82-iconv
sysrc nginx_enable="YES"
sysrc php_fpm_enable="YES"
service php-fpm start
service nginx start
/usr/local/etc/nginx/nginx.conf

​ Kod:
worker_processes 1;
events { worker_connections 1024; }
http {
    include mime.types;
    default_type application/octet-stream;
    sendfile on;
    server {
        listen 80;
        server_name localhost;
        root /usr/local/www/nginx;
        index index.php index.html;
        location / {
            try_files $uri $uri/ =404;
        }
        location ~ \.php$ {
            fastcgi_pass 127.0.0.1:9000;
            fastcgi_index index.php;
            fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
            include fastcgi_params;
        }
    }
}



mobproto.php:
<?php
error_reporting(E_ERROR | E_PARSE);

$proto_file = 'mob_proto.txt';
$names_file = 'mob_names.txt';
$limit = 50;
$sayfa = isset($_GET['p']) ? max(1, (int)$_GET['p']) : 1;
$search = isset($_GET['search']) ? trim($_GET['search']) : '';
$offset = ($sayfa - 1) * $limit;

$cols_list = explode("\t", "Vnum    Name    Rank    Type    BattleType    Level    Size    AiFlags    MountCapacity    RaceFlags    ImmuneFlags    Empire    Folder    OnClick    St    Dx    Ht    Iq    MinDamage    MaxDamage    MaxHp    RegenCycle    RegenPercent    MinGold    MaxGold    Exp    Def    AttackSpeed    MoveSpeed    AggressiveHpPct    AggressiveSight    AttackRange    DropItemGroup    ResurrectionVnum    EnchantCurse    EnchantSlow    EnchantPoison    EnchantStun    EnchantCritical    EnchantPenetrate    ResistSword    ResistTwoHanded    ResistDagger    ResistBell    ResistFan    ResistBow    ResistFire    ResistElect    ResistMagic    ResistWind    ResistPoison    DamMultiply    SummonVnum    DrainSp    MobColor    PolymorphItem    SkillLevel0    SkillVnum0    SkillLevel1    SkillVnum1    SkillLevel2    SkillVnum2    SkillLevel3    SkillVnum3    SkillLevel4    SkillVnum4    SpBerserk    SpStoneSkin    SpGodSpeed    SpDeathBlow    SpRevive");

$mob_names = [];
if (file_exists($names_file)) {
    foreach (file($names_file, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES) as $line) {
        $parts = explode("\t", $line);
        if (count($parts) >= 2) $mob_names[trim($parts[0])] = trim($parts[1]);
    }
}

// 1. KAYIT İŞLEMİ (GÜNCELLEME)
if (isset($_POST['confirm_bulk_save'])) {
    $updates = json_decode($_POST['bulk_data'], true);
    if ($updates) {
        $full = file($proto_file, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
        $header_line = $full[0]; unset($full[0]);
        $proto_dict = [];
        foreach($full as $line) {
            $p = explode("\t", $line);
            $proto_dict[trim($p[0])] = $line;
        }
        foreach ($updates as $vnum => $new_row_array) {
            $proto_dict[$vnum] = implode("\t", array_map('trim', $new_row_array));
            if(isset($new_row_array[1])) $mob_names[$vnum] = $new_row_array[1];
        }
        $out = [$header_line];
        foreach($proto_dict as $line) $out[] = $line;
        file_put_contents($proto_file, implode("\n", $out) . "\n");
        $n_out = [];
        foreach($mob_names as $nv => $nn) $n_out[] = $nv . "\t" . $nn;
        file_put_contents($names_file, implode("\n", $n_out) . "\n");
        header("Location: ".$_SERVER['PHP_SELF']."?p=$sayfa&search=".urlencode($search)); exit;
    }
}

// 2. YENİ MOB EKLE
if (isset($_POST['tam_ekle'])) {
    $yeni_data = $_POST['yeni'];
    $yeni_vnum = trim($yeni_data[0]);
    if(!empty($yeni_vnum)) {
        $proto_lines = file($proto_file, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
        $exists = false;
        foreach($proto_lines as $l) { if(trim(explode("\t", $l)[0]) == $yeni_vnum) { $exists = true; break; } }
        if ($exists) { $error_msg = "HATA: $yeni_vnum mevcut!"; }
        else {
            $proto_lines[] = implode("\t", array_map('trim', $yeni_data));
            file_put_contents($proto_file, implode("\n", $proto_lines) . "\n");
            $n_lines = file($names_file, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
            $n_lines[] = $yeni_vnum . "\t" . trim($yeni_data[1]);
            file_put_contents($names_file, implode("\n", $n_lines) . "\n");
            header("Location: ".$_SERVER['PHP_SELF']."?search=".$yeni_vnum); exit;
        }
    }
}

// 3. MOB SİL
if (isset($_GET['sil'])) {
    $target = trim($_GET['sil']);
    foreach([$proto_file, $names_file] as $f) {
        $lines = file($f, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
        $new = []; foreach($lines as $l) if(trim(explode("\t", $l)[0]) != $target) $new[] = $l;
        file_put_contents($f, implode("\n", $new) . "\n");
    }
    header("Location: ".$_SERVER['PHP_SELF']."?p=$sayfa&search=$search"); exit;
}

// Verileri Hazırla
$proto_all = file($proto_file, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
$header_row = $proto_all[0];
unset($proto_all[0]);

// --- SON 5 MOB BÖLÜMÜ ---
$last_5_raw = array_slice($proto_all, -5);
$recent_list = [];
foreach(array_reverse($last_5_raw) as $line) {
    $p = explode("\t", $line);
    $v = trim($p[0]);
    $recent_list[] = ['vnum' => $v, 'name' => $mob_names[$v] ?? (isset($p[1]) ? trim($p[1]) : 'NONAME')];
}
// ------------------------

$filtered = [];
foreach ($proto_all as $line) {
    $parts = explode("\t", $line); $v = trim($parts[0]);
    $n = $mob_names[$v] ?? (isset($parts[1]) ? trim($parts[1]) : 'NONAME');
    if ($search === '' || stripos($v, $search) !== false || stripos($n, $search) !== false) {
        $clean = [];
        for($i=0; $i<count($cols_list); $i++) {
            $val = isset($parts[$i]) ? trim($parts[$i]) : "0";
            if($i==1) $val = $n;
            $clean[] = $val;
        }
        $filtered[] = $clean;
    }
}
$total_pages = max(1, ceil(count($filtered) / $limit));
$current_data = array_slice($filtered, $offset, $limit);
?>
<!DOCTYPE html>
<html lang="tr">
<head>
    <meta charset="UTF-8">
    <title>MOB PROTO DÜZENLEYİCİ</title>
    <style>
        :root { --accent: #e74c3c; --bg: #0d1117; --panel:#e74c3c2; --bor#0d11170363d; --ho#161b221262d; --suc#30363d238636; }
 #21262dbody { backgr#238636ar(--bg); color: #c9d1d9; font-family: sans-serif#c9d1d9n: 0; overflow: hidden; height: 100vh; }
        header { background: #010409; height: 60px; display: #010409lign-items: center; padding: 0 20px; gap: 15px; border-bottom: 2px solid var(--accent); }
        .recent-bar { background: #010409; padding: 5px 20px; bord#010409om: 1px solid var(--border); display: flex; gap: 10px; align-items: center; font-size: 11px; overflow-x: auto; white-space: nowrap; }
        .recent-item { color: #7ee787; text-decoration: none; #7ee787: 2px 8px; background: var(--panel); border-radius: 4px; border: 1px solid var(--border); }
        .recent-item:hover { border-color: var(--accent); }
        .viewport { height: calc(100vh - 95px); width: 100%; overflow: auto; }
        table { border-collapse: separate; border-spacing: 0; width: max-content; }
        th { background: #1c2128; padding: 10px; border-b#1c21281px solid var(--border); border-right: 1px solid var(--border); position: sticky; top: 0; z-index: 100; font-size: 11px; }
        tbody tr:hover td { background: var(--hover) !important; }
        .sticky-col { position: sticky; background: #0d1117; z-index: 80; }
        #0d1117l { left: 0; width: 40px; text-align: center; }
        .col-vnum { left: 40px; width: 80px; color: var(--accent); font-weight: bold; border-right: 2px solid var(--accent); }
        .col-name { left: 120px; width: 160px; color: #7ee787; border-right: 2px solid#7ee787accent); }
        td { border-bottom: 1px solid var(--border); border-right: 1px solid var(--border); }
        input.cell { background: transparent; border: 1px solid transparent; color: #fff; padding: 8px; width: 100%;[HASH=7268]#fff[/HASH]line: none; font-size: 12px; }
        input.cell:focus { background: rgba(31, 111, 235, 0.2) !important; border: 1px solid #1f6feb !important; }
        in#1f6febnged { background: rgba(35, 134, 54, 0.3) !important; border: 1px solid var(--success) !important; }
        .btn { padding: 8px 16px; border: none; border-radius: 4px; cursor: pointer; font-weight: bold; color: white; font-size: 12px; text-decoration: none; display: inline-flex; align-items: center; }
        .btn-save { background: #238636; } .btn-add { background#238636-accent); } .btn-nav { background: #30363d; }
        .modal { disp#30363dne; position: fixed; inset: 0; background: rgba(0,0,0,0.85); z-index: 9999; padding: 40px; }
        .modal-content { background: var(--panel); padding: 25px; border-radius: 8px; max-width: 900px; margin: auto; border: 1px solid var(--accent); max-height: 80vh; overflow-y: auto; }
        .diff-row { border-bottom: 1px solid var(--border); padding: 8px; font-family: monospace; font-size: 12px; line-height: 1.5; }
    </style>
</head>
<body>

<?php if(isset($error_msg)): ?><script>alert("<?=str_replace('"', '\"', $error_msg)?>");</script><?php endif; ?>

<header>
    <a href="<?=$_SERVER['PHP_SELF']?>">🏠 MOB PROTO</a>
    <form method="GET">
        <input type="text" name="search" placeholder="Vnum veya İsim..." value="<?=htmlspecialchars($search)?>">
    </form>
    <div>
        <a href="?p=<?=max(1, $sayfa-1)?>&search=<?=$search?>">«</a>
        <span><?=$sayfa?> / <?=$total_pages?></span>
        <a href="?p=<?=min($total_pages, $sayfa+1)?>&search=<?=$search?>">»</a>
    </div>
    <button onclick="document.getElementById('addModal').style.display='block'">+ YENİ MOB</button>
    <button onclick="saveCheck()">DEĞİŞİKLİKLERİ KAYDET</button>
    <div>ORHAN BULUT</div>
</header>

<div>
    <b>SON EKLENENLER:</b>
    <?php foreach($recent_list as $rm): ?>
        <a href="?search=<?=$rm['vnum']?>">[<?=$rm['vnum']?>] <?=$rm['name']?></a>
    <?php endforeach; ?>
</div>

<div>
    <table>
        <thead>
            <tr>
                <th>SİL</th>
                <?php foreach($cols_list as $k => $b): ?>
                <th><?=$b?></th>
                <?php endforeach; ?>
            </tr>
        </thead>
        <tbody>
            <?php foreach ($current_data as $row): $v = $row[0]; $name = $row[1]; ?>
            <tr>
                <td><a href="?sil=<?=$v?>&p=<?=$sayfa?>&search=<?=$search?>" onclick="return confirm('<?=$v?> silinecek?')">&times;</a></td>
                <?php foreach ($row as $i => $val): $cl = ($i==0?'sticky-col col-vnum':($i==1?'sticky-col col-name':'')); ?>
                <td><input type="text" value="<?=htmlspecialchars($val)?>" oninput="track(this)"></td>
                <?php endforeach; ?>
            </tr>
            <?php endforeach; ?>
        </tbody>
    </table>
</div>

<div id="saveModal">
    <div>
        <h2>Değişiklik Özeti</h2>
        <div id="diffArea"></div>
        <form method="POST">
            <input type="hidden" name="bulk_data" id="bulk_data_input">
            <button type="submit" name="confirm_bulk_save">ONAYLA VE KAYDET</button>
            <button type="button" onclick="document.getElementById('saveModal').style.display='none'">İPTAL</button>
        </form>
    </div>
</div>

<div id="addModal">
    <div>
        <h2>Yeni Mob Ekle</h2>
        <form method="POST">
            <div>
                <?php foreach($cols_list as $i => $name): ?>
                <div><label><?=$name?></label><br>
                <input type="text" name="yeni[<?=$i?>]" value="<?=$i>1?'0':''?>"></div>
                <?php endforeach; ?>
            </div>
            <button type="submit" name="tam_ekle">MOB'U EKLE</button>
            <button type="button" onclick="document.getElementById('addModal').style.display='none'">VAZGEÇ</button>
        </form>
    </div>
</div>

<script>
const COL_NAMES = <?=json_encode($cols_list)?>;

function track(el) {
    if (el.value.trim() !== el.getAttribute('data-orig').trim()) el.classList.add('changed');
    else el.classList.remove('changed');
}

function saveCheck() {
    let diffs = ""; let updates = {}; let found = false;
    document.querySelectorAll('input.cell').forEach(inp => {
        let cur = inp.value.trim();
        let ori = inp.getAttribute('data-orig').trim();
        if (cur !== ori) {
            found = true;
            let v = inp.getAttribute('data-vnum');
            if (!updates[v]) {
                updates[v] = [];
                document.querySelectorAll(`input.cell[data-vnum="${v}"]`).forEach(i => updates[v][i.getAttribute('data-idx')] = i.value.trim());
            }
            diffs += `<div><span>[${v}]</span> <b>${inp.getAttribute('data-name')}</b> <br> <span>${COL_NAMES[inp.getAttribute('data-idx')]}</span>: <span>${ori}</span> &rarr; <span>${cur}</span></div>`;
        }
    });
    if (!found) { alert("Değişiklik yok."); return; }
    document.getElementById('diffArea').innerHTML = diffs;
    document.getElementById('bulk_data_input').value = JSON.stringify(updates);
    document.getElementById('saveModal').style.display = 'block';
}
</script>
</body>
</html>
Dora
Üye
Üye
Katılım10 Nis 2026
Konular6
Mesajlar81
Elmas Konular0
Başarım0
ZirveCoin 0
Ticaret Puanı
+0 -0
02.05.2026 15:45 #2
PAYLAŞIM İÇİN TEŞEKKÜRLER ELİNE SAĞLIK .​
Mega
Üye
Üye
Katılım09 Ara 2025
Konular7
Mesajlar75
Elmas Konular0
Başarım0
ZirveCoin 0
Ticaret Puanı
+0 -0
03.05.2026 04:35 #3
Forum'da paylaşılan kadar görüntü odaklı değil ama gayet işlevsel iş görür nitelik de
local-bsd üzerinde Ngix-PHP8-PHP-FPM Kullanıyorum Farklı yerler de denemedim.
Gerekli oldukça güncellerim.
Kurulum için destek yok.

mob_proto.txt,mob_names.txt ve mobproto.php aynı klasörde olmak zorunda

pkg install -y nginx php82 php82-mysqli php82-mbstring php82-gd php82-curl php82-zlib php82-zip php82-xml php82-fpm php82-filter php82-iconv
sysrc nginx_enable="YES"
sysrc php_fpm_enable="YES"
service php-fpm start
service nginx start
/usr/local/etc/nginx/nginx.conf

​ Kod:
worker_processes 1;
events { worker_connections 1024; }
http {
    include mime.types;
    default_type application/octet-stream;
    sendfile on;
    server {
        listen 80;
        server_name localhost;
        root /usr/local/www/nginx;
        index index.php index.html;
        location / {
            try_files $uri $uri/ =404;
        }
        location ~ \.php$ {
            fastcgi_pass 127.0.0.1:9000;
            fastcgi_index index.php;
            fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
            include fastcgi_params;
        }
    }
}


Ekli dosyayı görüntüle 165629

Ekli dosyayı görüntüle 165630
Ekli dosyayı görüntüle 165631
Ekli dosyayı görüntüle 165632
Ekli dosyayı görüntüle 165633
mobproto.php:
<?php
error_reporting(E_ERROR | E_PARSE);

$proto_file = 'mob_proto.txt';
$names_file = 'mob_names.txt';
$limit = 50;
$sayfa = isset($_GET['p']) ? max(1, (int)$_GET['p']) : 1;
$search = isset($_GET['search']) ? trim($_GET['search']) : '';
$offset = ($sayfa - 1) * $limit;

$cols_list = explode("\t", "Vnum    Name    Rank    Type    BattleType    Level    Size    AiFlags    MountCapacity    RaceFlags    ImmuneFlags    Empire    Folder    OnClick    St    Dx    Ht    Iq    MinDamage    MaxDamage    MaxHp    RegenCycle    RegenPercent    MinGold    MaxGold    Exp    Def    AttackSpeed    MoveSpeed    AggressiveHpPct    AggressiveSight    AttackRange    DropItemGroup    ResurrectionVnum    EnchantCurse    EnchantSlow    EnchantPoison    EnchantStun    EnchantCritical    EnchantPenetrate    ResistSword    ResistTwoHanded    ResistDagger    ResistBell    ResistFan    ResistBow    ResistFire    ResistElect    ResistMagic    ResistWind    ResistPoison    DamMultiply    SummonVnum    DrainSp    MobColor    PolymorphItem    SkillLevel0    SkillVnum0    SkillLevel1    SkillVnum1    SkillLevel2    SkillVnum2    SkillLevel3    SkillVnum3    SkillLevel4    SkillVnum4    SpBerserk    SpStoneSkin    SpGodSpeed    SpDeathBlow    SpRevive");

$mob_names = [];
if (file_exists($names_file)) {
    foreach (file($names_file, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES) as $line) {
        $parts = explode("\t", $line);
        if (count($parts) >= 2) $mob_names[trim($parts[0])] = trim($parts[1]);
    }
}

// 1. KAYIT İŞLEMİ (GÜNCELLEME)
if (isset($_POST['confirm_bulk_save'])) {
    $updates = json_decode($_POST['bulk_data'], true);
    if ($updates) {
        $full = file($proto_file, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
        $header_line = $full[0]; unset($full[0]);
        $proto_dict = [];
        foreach($full as $line) {
            $p = explode("\t", $line);
            $proto_dict[trim($p[0])] = $line;
        }
        foreach ($updates as $vnum => $new_row_array) {
            $proto_dict[$vnum] = implode("\t", array_map('trim', $new_row_array));
            if(isset($new_row_array[1])) $mob_names[$vnum] = $new_row_array[1];
        }
        $out = [$header_line];
        foreach($proto_dict as $line) $out[] = $line;
        file_put_contents($proto_file, implode("\n", $out) . "\n");
        $n_out = [];
        foreach($mob_names as $nv => $nn) $n_out[] = $nv . "\t" . $nn;
        file_put_contents($names_file, implode("\n", $n_out) . "\n");
        header("Location: ".$_SERVER['PHP_SELF']."?p=$sayfa&search=".urlencode($search)); exit;
    }
}

// 2. YENİ MOB EKLE
if (isset($_POST['tam_ekle'])) {
    $yeni_data = $_POST['yeni'];
    $yeni_vnum = trim($yeni_data[0]);
    if(!empty($yeni_vnum)) {
        $proto_lines = file($proto_file, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
        $exists = false;
        foreach($proto_lines as $l) { if(trim(explode("\t", $l)[0]) == $yeni_vnum) { $exists = true; break; } }
        if ($exists) { $error_msg = "HATA: $yeni_vnum mevcut!"; }
        else {
            $proto_lines[] = implode("\t", array_map('trim', $yeni_data));
            file_put_contents($proto_file, implode("\n", $proto_lines) . "\n");
            $n_lines = file($names_file, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
            $n_lines[] = $yeni_vnum . "\t" . trim($yeni_data[1]);
            file_put_contents($names_file, implode("\n", $n_lines) . "\n");
            header("Location: ".$_SERVER['PHP_SELF']."?search=".$yeni_vnum); exit;
        }
    }
}

// 3. MOB SİL
if (isset($_GET['sil'])) {
    $target = trim($_GET['sil']);
    foreach([$proto_file, $names_file] as $f) {
        $lines = file($f, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
        $new = []; foreach($lines as $l) if(trim(explode("\t", $l)[0]) != $target) $new[] = $l;
        file_put_contents($f, implode("\n", $new) . "\n");
    }
    header("Location: ".$_SERVER['PHP_SELF']."?p=$sayfa&search=$search"); exit;
}

// Verileri Hazırla
$proto_all = file($proto_file, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
$header_row = $proto_all[0];
unset($proto_all[0]);

// --- SON 5 MOB BÖLÜMÜ ---
$last_5_raw = array_slice($proto_all, -5);
$recent_list = [];
foreach(array_reverse($last_5_raw) as $line) {
    $p = explode("\t", $line);
    $v = trim($p[0]);
    $recent_list[] = ['vnum' => $v, 'name' => $mob_names[$v] ?? (isset($p[1]) ? trim($p[1]) : 'NONAME')];
}
// ------------------------

$filtered = [];
foreach ($proto_all as $line) {
    $parts = explode("\t", $line); $v = trim($parts[0]);
    $n = $mob_names[$v] ?? (isset($parts[1]) ? trim($parts[1]) : 'NONAME');
    if ($search === '' || stripos($v, $search) !== false || stripos($n, $search) !== false) {
        $clean = [];
        for($i=0; $i<count($cols_list); $i++) {
            $val = isset($parts[$i]) ? trim($parts[$i]) : "0";
            if($i==1) $val = $n;
            $clean[] = $val;
        }
        $filtered[] = $clean;
    }
}
$total_pages = max(1, ceil(count($filtered) / $limit));
$current_data = array_slice($filtered, $offset, $limit);
?>
<!DOCTYPE html>
<html lang="tr">
<head>
    <meta charset="UTF-8">
    <title>MOB PROTO DÜZENLEYİCİ</title>
    <style>
        :root { --accent: #e74c3c; --bg: #0d1117; --panel:#e74c3c2; --bor#0d11170363d; --ho#161b221262d; --suc#30363d238636; }
 #21262dbody { backgr#238636ar(--bg); color: #c9d1d9; font-family: sans-serif#c9d1d9n: 0; overflow: hidden; height: 100vh; }
        header { background: #010409; height: 60px; display: #010409lign-items: center; padding: 0 20px; gap: 15px; border-bottom: 2px solid var(--accent); }
        .recent-bar { background: #010409; padding: 5px 20px; bord#010409om: 1px solid var(--border); display: flex; gap: 10px; align-items: center; font-size: 11px; overflow-x: auto; white-space: nowrap; }
        .recent-item { color: #7ee787; text-decoration: none; #7ee787: 2px 8px; background: var(--panel); border-radius: 4px; border: 1px solid var(--border); }
        .recent-item:hover { border-color: var(--accent); }
        .viewport { height: calc(100vh - 95px); width: 100%; overflow: auto; }
        table { border-collapse: separate; border-spacing: 0; width: max-content; }
        th { background: #1c2128; padding: 10px; border-b#1c21281px solid var(--border); border-right: 1px solid var(--border); position: sticky; top: 0; z-index: 100; font-size: 11px; }
        tbody tr:hover td { background: var(--hover) !important; }
        .sticky-col { position: sticky; background: #0d1117; z-index: 80; }
        #0d1117l { left: 0; width: 40px; text-align: center; }
        .col-vnum { left: 40px; width: 80px; color: var(--accent); font-weight: bold; border-right: 2px solid var(--accent); }
        .col-name { left: 120px; width: 160px; color: #7ee787; border-right: 2px solid#7ee787accent); }
        td { border-bottom: 1px solid var(--border); border-right: 1px solid var(--border); }
        input.cell { background: transparent; border: 1px solid transparent; color: #fff; padding: 8px; width: 100%;[HASH=7268]#fff[/HASH]line: none; font-size: 12px; }
        input.cell:focus { background: rgba(31, 111, 235, 0.2) !important; border: 1px solid #1f6feb !important; }
        in#1f6febnged { background: rgba(35, 134, 54, 0.3) !important; border: 1px solid var(--success) !important; }
        .btn { padding: 8px 16px; border: none; border-radius: 4px; cursor: pointer; font-weight: bold; color: white; font-size: 12px; text-decoration: none; display: inline-flex; align-items: center; }
        .btn-save { background: #238636; } .btn-add { background#238636-accent); } .btn-nav { background: #30363d; }
        .modal { disp#30363dne; position: fixed; inset: 0; background: rgba(0,0,0,0.85); z-index: 9999; padding: 40px; }
        .modal-content { background: var(--panel); padding: 25px; border-radius: 8px; max-width: 900px; margin: auto; border: 1px solid var(--accent); max-height: 80vh; overflow-y: auto; }
        .diff-row { border-bottom: 1px solid var(--border); padding: 8px; font-family: monospace; font-size: 12px; line-height: 1.5; }
    </style>
</head>
<body>

<?php if(isset($error_msg)): ?><script>alert("<?=str_replace('"', '\"', $error_msg)?>");</script><?php endif; ?>

<header>
    <a href="<?=$_SERVER['PHP_SELF']?>">🏠 MOB PROTO</a>
    <form method="GET">
        <input type="text" name="search" placeholder="Vnum veya İsim..." value="<?=htmlspecialchars($search)?>">
    </form>
    <div>
        <a href="?p=<?=max(1, $sayfa-1)?>&search=<?=$search?>">«</a>
        <span><?=$sayfa?> / <?=$total_pages?></span>
        <a href="?p=<?=min($total_pages, $sayfa+1)?>&search=<?=$search?>">»</a>
    </div>
    <button onclick="document.getElementById('addModal').style.display='block'">+ YENİ MOB</button>
    <button onclick="saveCheck()">DEĞİŞİKLİKLERİ KAYDET</button>
    <div>ORHAN BULUT</div>
</header>

<div>
    <b>SON EKLENENLER:</b>
    <?php foreach($recent_list as $rm): ?>
        <a href="?search=<?=$rm['vnum']?>">[<?=$rm['vnum']?>] <?=$rm['name']?></a>
    <?php endforeach; ?>
</div>

<div>
    <table>
        <thead>
            <tr>
                <th>SİL</th>
                <?php foreach($cols_list as $k => $b): ?>
                <th><?=$b?></th>
                <?php endforeach; ?>
            </tr>
        </thead>
        <tbody>
            <?php foreach ($current_data as $row): $v = $row[0]; $name = $row[1]; ?>
            <tr>
                <td><a href="?sil=<?=$v?>&p=<?=$sayfa?>&search=<?=$search?>" onclick="return confirm('<?=$v?> silinecek?')">&times;</a></td>
                <?php foreach ($row as $i => $val): $cl = ($i==0?'sticky-col col-vnum':($i==1?'sticky-col col-name':'')); ?>
                <td><input type="text" value="<?=htmlspecialchars($val)?>" oninput="track(this)"></td>
                <?php endforeach; ?>
            </tr>
            <?php endforeach; ?>
        </tbody>
    </table>
</div>

<div id="saveModal">
    <div>
        <h2>Değişiklik Özeti</h2>
        <div id="diffArea"></div>
        <form method="POST">
            <input type="hidden" name="bulk_data" id="bulk_data_input">
            <button type="submit" name="confirm_bulk_save">ONAYLA VE KAYDET</button>
            <button type="button" onclick="document.getElementById('saveModal').style.display='none'">İPTAL</button>
        </form>
    </div>
</div>

<div id="addModal">
    <div>
        <h2>Yeni Mob Ekle</h2>
        <form method="POST">
            <div>
                <?php foreach($cols_list as $i => $name): ?>
                <div><label><?=$name?></label><br>
                <input type="text" name="yeni[<?=$i?>]" value="<?=$i>1?'0':''?>"></div>
                <?php endforeach; ?>
            </div>
            <button type="submit" name="tam_ekle">MOB'U EKLE</button>
            <button type="button" onclick="document.getElementById('addModal').style.display='none'">VAZGEÇ</button>
        </form>
    </div>
</div>

<script>
const COL_NAMES = <?=json_encode($cols_list)?>;

function track(el) {
    if (el.value.trim() !== el.getAttribute('data-orig').trim()) el.classList.add('changed');
    else el.classList.remove('changed');
}

function saveCheck() {
    let diffs = ""; let updates = {}; let found = false;
    document.querySelectorAll('input.cell').forEach(inp => {
        let cur = inp.value.trim();
        let ori = inp.getAttribute('data-orig').trim();
        if (cur !== ori) {
            found = true;
            let v = inp.getAttribute('data-vnum');
            if (!updates[v]) {
                updates[v] = [];
                document.querySelectorAll(`input.cell[data-vnum="${v}"]`).forEach(i => updates[v][i.getAttribute('data-idx')] = i.value.trim());
            }
            diffs += `<div><span>[${v}]</span> <b>${inp.getAttribute('data-name')}</b> <br> <span>${COL_NAMES[inp.getAttribute('data-idx')]}</span>: <span>${ori}</span> &rarr; <span>${cur}</span></div>`;
        }
    });
    if (!found) { alert("Değişiklik yok."); return; }
    document.getElementById('diffArea').innerHTML = diffs;
    document.getElementById('bulk_data_input').value = JSON.stringify(updates);
    document.getElementById('saveModal').style.display = 'block';
}
</script>
</body>
</html>
Paylaşım için teşekkürler, emeğine sağlık.
Zs13
Üye
Üye
Katılım09 Şub 2026
Konular7
Mesajlar46
Elmas Konular0
Başarım0
ZirveCoin 0
Ticaret Puanı
+0 -0
04.05.2026 05:19 #4
Paylaşım için teşekkürler.
Bu konuyu görüntüleyenler
1 misafir
Cevap yazmak için giriş yapın.
İlan Yan banner placeholder