emirhanHCL 1
emirhanHCL
farkmt2official 1
farkmt2official
mannaxxx 1
mannaxxx
kralhakan2009 1
kralhakan2009
Vahsi Uzman 1
Vahsi Uzman
Hikaye Ekle

Quest Builder / Görev ve Zindan Quest Hazırlama

Türkiye'de ilk Mobil & PC Aynı Anda Metin2 Oyna. Triarchonline kalıcı ve uzun ömürlü yapısı ile 24 Temmuz'da açılıyor. | 1-99 Mobil Metin2 Triarch HEMEN TIKLA!

Basit form doldurarak görev questi hazırlamak için yapıldı umarım işinize yarar

Ekran-goruntusu-2026-06-10-011935.png

Ekran-goruntusu-2026-06-10-012019.png



HTML:
<!DOCTYPE html>
<html lang="tr">
<head>
<meta charset="UTF-8"><meta name="viewport" content="width=device-width,initial-scale=1">
<title>Metin2 Quest Oluşturucu</title>
<style>
:root{
  --bg0:#030406;
  --bg1:#070a10;
  --surface:#0c111a;
  --surface2:#101722;
  --surface3:#151f2e;
  --line:rgba(255,255,255,.10);
  --line2:rgba(255,255,255,.16);
  --text:#f4f7fb;
  --muted:#8f9bad;
  --soft:#b8c2d4;
  --accent:#c8a45d;
  --accent2:#e7c77d;
  --blue:#7dd3fc;
  --green:#6ee7b7;
  --red:#fb7185;
  --shadow:0 28px 90px rgba(0,0,0,.56);
  --radius:22px;
  --radius2:16px;
}
*{box-sizing:border-box}
html{scroll-behavior:smooth}
body{
  margin:0;
  min-height:100vh;
  font-family:Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Arial, sans-serif;
  color:var(--text);
  background:
    radial-gradient(circle at 18% 8%, rgba(200,164,93,.18), transparent 26%),
    radial-gradient(circle at 88% 0%, rgba(125,211,252,.10), transparent 28%),
    linear-gradient(135deg,#030406 0%,#080b12 46%,#040507 100%);
  padding:28px;
}
body::before{
  content:"";
  position:fixed;
  inset:0;
  pointer-events:none;
  background-image:
    linear-gradient(rgba(255,255,255,.025) 1px, transparent 1px),
    linear-gradient(90deg, rgba(255,255,255,.025) 1px, transparent 1px);
  background-size:40px 40px;
  mask-image:linear-gradient(to bottom,rgba(0,0,0,.7),transparent 85%);
}
.app{
  width:min(1520px,100%);
  margin:0 auto;
  display:grid;
  grid-template-columns:292px minmax(0,1fr);
  min-height:calc(100vh - 56px);
  border:1px solid var(--line2);
  border-radius:28px;
  overflow:visible;
  background:rgba(8,12,18,.78);
  box-shadow:var(--shadow);
  backdrop-filter:blur(18px);
}
.sidebar{
  position:sticky;
  top:28px;
  height:calc(100vh - 56px);
  padding:22px;
  border-right:1px solid var(--line);
  background:
    linear-gradient(180deg,rgba(255,255,255,.055),rgba(255,255,255,.018)),
    rgba(9,13,19,.88);
  border-radius:28px 0 0 28px;
}
.brand{
  display:flex;
  align-items:center;
  gap:13px;
  padding:4px 2px 22px;
  border-bottom:1px solid var(--line);
  margin-bottom:18px;
}
.logo{
  width:48px;
  height:48px;
  border-radius:16px;
  display:grid;
  place-items:center;
  font-weight:900;
  letter-spacing:.5px;
  color:#111827;
  background:
    linear-gradient(145deg,var(--accent2),var(--accent) 55%,#806333);
  box-shadow:0 12px 34px rgba(200,164,93,.25), inset 0 1px 0 rgba(255,255,255,.5);
}
.brand h1{
  font-size:17px;
  margin:0;
  letter-spacing:.2px;
}
.brand p{
  margin:4px 0 0;
  color:var(--muted);
  font-size:12px;
}
.nav{
  display:grid;
  gap:9px;
}
.nav button{
  width:100%;
  text-align:left;
  background:transparent;
  color:var(--muted);
  border:1px solid transparent;
  padding:13px 14px;
  border-radius:15px;
  cursor:pointer;
  font-weight:750;
  transition:.18s ease;
}
.nav button:hover{
  color:var(--text);
  border-color:var(--line);
  background:rgba(255,255,255,.045);
  transform:translateX(2px);
}
.nav button.active{
  color:var(--text);
  border-color:rgba(200,164,93,.42);
  background:
    linear-gradient(135deg,rgba(200,164,93,.18),rgba(255,255,255,.045));
  box-shadow:inset 3px 0 0 var(--accent);
}
.side-card{
  margin-top:22px;
  padding:16px;
  border:1px solid var(--line);
  border-radius:18px;
  background:
    linear-gradient(180deg,rgba(255,255,255,.06),rgba(255,255,255,.025));
}
.side-card b{
  display:block;
  font-size:13px;
  letter-spacing:.3px;
  margin-bottom:7px;
}
.side-card p{
  margin:0;
  color:var(--muted);
  font-size:12.5px;
  line-height:1.55;
}
main{
  min-width:0;
  padding:26px;
}
.topbar{
  display:flex;
  justify-content:space-between;
  gap:20px;
  align-items:center;
  margin-bottom:20px;
  padding:18px 20px;
  border:1px solid var(--line);
  border-radius:22px;
  background:rgba(255,255,255,.035);
}
.title h2{
  margin:0;
  font-size:27px;
  letter-spacing:-.03em;
}
.title p{
  margin:6px 0 0;
  color:var(--muted);
}
button{
  border:0;
  border-radius:14px;
  padding:11px 15px;
  cursor:pointer;
  color:var(--text);
  font-weight:850;
  background:linear-gradient(180deg,rgba(255,255,255,.10),rgba(255,255,255,.045));
  border:1px solid var(--line);
  transition:.18s ease;
}
button:hover{
  transform:translateY(-1px);
  border-color:var(--line2);
  filter:brightness(1.05);
}
.primary{
  color:#111827;
  background:linear-gradient(145deg,var(--accent2),var(--accent));
  border-color:rgba(255,255,255,.18);
  box-shadow:0 12px 30px rgba(200,164,93,.18);
}
.success{
  color:#052e1c;
  background:linear-gradient(145deg,#a7f3d0,var(--green));
}
.warning{
  color:#1f1300;
  background:linear-gradient(145deg,#fde68a,var(--accent2));
}
.danger{
  color:#fff;
  background:linear-gradient(145deg,#fb7185,#be123c);
}
.ghost{
  background:rgba(255,255,255,.04);
}
.actions,.inline{
  display:flex;
  gap:10px;
  flex-wrap:wrap;
}
.grid{display:grid;gap:18px}
.grid-2{grid-template-columns:1fr 1fr}
.grid-3{grid-template-columns:repeat(3,1fr)}
.split{
  display:grid;
  grid-template-columns:minmax(420px,5fr) minmax(0,7fr);
  gap:20px;
  align-items:start;
}
.card{
  position:relative;
  background:
    linear-gradient(180deg,rgba(255,255,255,.072),rgba(255,255,255,.030)),
    rgba(10,15,23,.82);
  border:1px solid var(--line);
  border-radius:var(--radius);
  box-shadow:0 18px 55px rgba(0,0,0,.30);
  padding:20px;
}
.card::before{
  content:"";
  position:absolute;
  inset:0;
  border-radius:inherit;
  pointer-events:none;
  background:linear-gradient(135deg,rgba(255,255,255,.10),transparent 28%);
  opacity:.55;
}
.card>*{position:relative}
.card h3{
  margin:0 0 14px;
  font-size:17px;
  letter-spacing:-.01em;
}
.metric{
  padding:19px;
  border-radius:20px;
  background:
    linear-gradient(135deg,rgba(200,164,93,.18),rgba(125,211,252,.06)),
    rgba(255,255,255,.035);
  border:1px solid var(--line);
}
.metric span{
  color:var(--muted);
  font-size:13px;
}
.metric strong{
  display:block;
  font-size:29px;
  margin-top:8px;
  letter-spacing:-.04em;
}
label{
  display:block;
  color:var(--soft);
  font-size:12.5px;
  font-weight:850;
  margin:13px 0 7px;
}
input,select,textarea{
  width:100%;
  background:rgba(3,7,18,.72);
  color:var(--text);
  border:1px solid var(--line);
  border-radius:14px;
  padding:12px;
  outline:none;
  transition:.18s ease;
}
input:focus,select:focus,textarea:focus{
  border-color:rgba(200,164,93,.58);
  box-shadow:0 0 0 4px rgba(200,164,93,.10);
}
textarea{
  min-height:96px;
  resize:vertical;
}
.row{
  display:grid;
  grid-template-columns:1fr 1fr;
  gap:12px;
}
.hidden{display:none!important}
.hint{
  margin:9px 0 0;
  color:var(--muted);
  font-size:12px;
  line-height:1.55;
}
.chips{
  display:flex;
  gap:8px;
  flex-wrap:wrap;
  margin-top:9px;
}
.chip{
  padding:8px 11px;
  border-radius:999px;
  background:rgba(255,255,255,.055);
  border:1px solid var(--line);
  color:#dbeafe;
  font-size:12px;
  cursor:pointer;
  transition:.18s ease;
}
.chip:hover{
  color:#111827;
  background:linear-gradient(145deg,var(--accent2),var(--accent));
}
pre{
  margin:0;
  background:
    linear-gradient(180deg,rgba(255,255,255,.035),transparent),
    #02050a;
  border:1px solid var(--line);
  border-radius:18px;
  padding:18px;
  min-height:560px;
  max-height:72vh;
  overflow:auto;
  white-space:pre-wrap;
  line-height:1.55;
  color:#d1fae5;
  box-shadow:inset 0 0 0 1px rgba(255,255,255,.02);
}
.flow{
  background:rgba(2,5,10,.86);
  border:1px solid var(--line);
  border-radius:18px;
  padding:15px;
  color:#cbd5e1;
  line-height:1.75;
  margin-bottom:14px;
}
.floor,.reward,.fold{
  border:1px solid var(--line);
  border-radius:18px;
  background:rgba(255,255,255,.028);
  margin:12px 0;
  overflow:hidden;
}
.fold>summary,.floor>summary{
  list-style:none;
  cursor:pointer;
  padding:15px 16px;
  display:flex;
  align-items:center;
  justify-content:space-between;
  gap:14px;
  font-weight:850;
  color:var(--text);
  background:
    linear-gradient(90deg,rgba(200,164,93,.11),transparent 54%),
    linear-gradient(180deg,rgba(255,255,255,.065),rgba(255,255,255,.020));
}
.fold>summary::-webkit-details-marker,.floor>summary::-webkit-details-marker{display:none}
.fold>summary::after,.floor>summary::after{
  content:"Aç";
  font-size:12px;
  color:var(--accent2);
  border:1px solid rgba(200,164,93,.35);
  border-radius:999px;
  padding:4px 10px;
  background:rgba(200,164,93,.08);
}
.fold[open]>summary::after,.floor[open]>summary::after{content:"Kapat"}
.fold-body,.floor-body{padding:17px}
.floor-subtitle{
  display:block;
  margin-top:5px;
  font-size:12px;
  font-weight:450;
  color:var(--muted);
}
.floor-actions{
  display:flex;
  gap:10px;
  align-items:center;
  justify-content:flex-end;
  margin-top:13px;
}
.floor-actions button{margin-top:0}
.issue{
  padding:12px;
  border-radius:15px;
  border:1px solid var(--line);
  background:rgba(255,255,255,.04);
  margin:8px 0;
}
.ok{border-color:rgba(110,231,183,.48)}
.warn{border-color:rgba(231,199,125,.54)}
.err{border-color:rgba(251,113,133,.50)}
.lib{
  display:grid;
  grid-template-columns:repeat(3,1fr);
  gap:12px;
}
.lib-card{
  padding:15px;
  border-radius:18px;
  background:rgba(255,255,255,.04);
  border:1px solid var(--line);
}
.lib-card span{
  color:var(--muted);
  font-size:13px;
}
::-webkit-scrollbar{width:12px;height:12px}
::-webkit-scrollbar-track{background:#05070b}
::-webkit-scrollbar-thumb{background:#222b3a;border:3px solid #05070b;border-radius:999px}
::-webkit-scrollbar-thumb:hover{background:#364155}
@media(max-width:1180px){
  body{padding:16px}
  .app{grid-template-columns:1fr}
  .sidebar{
    position:relative;
    top:auto;
    height:auto;
    border-radius:28px 28px 0 0;
    border-right:0;
    border-bottom:1px solid var(--line);
  }
  .split,.grid-2,.grid-3,.lib{grid-template-columns:1fr}
}
</style></head>
<body><div class="app">
<aside class="sidebar"><div class="brand"><div class="logo">M2</div><div><h1>Quest Oluşturucu</h1><p>Görev ve zindan üretim paneli</p></div></div><div class="nav">
<button class="active" onclick="showTab('dashboard',this)">🏠 Genel Bakış</button><button onclick="showTab('quest',this)">📜 Görev Oluşturucu</button><button onclick="showTab('dungeon',this)">🕯️ Zindan Akışı</button><button onclick="showTab('library',this)">📚 VNUM Kütüphanesi</button><button onclick="showTab('validator',this)">✅ Quest Kontrol</button></div>
<div class="side-card"><b>Çalışma Mantığı</b><p>Alanları doldurdukça sağ tarafta quest kodu ve açıklama yorumları otomatik oluşur.</p></div></aside>
<main><div class="topbar"><div class="title"><h2 id="pageTitle">Genel Bakış</h2><p id="pageDesc">Metin2 quest ve zindan kodlarını panelden üret.</p></div><div class="actions"><button class="ghost" onclick="copyQuest()">Kopyala</button><button class="success" onclick="downloadQuest()">.quest İndir</button><button class="warning" onclick="validateQuest(true)">Kontrol Et</button></div></div>

<section id="dashboard" class="tab"><div class="grid grid-3"><div class="metric"><span>Quest Modülü</span><strong>4 Tip</strong></div><div class="metric"><span>Zindan Kat Sistemi</span><strong>Dinamik</strong></div><div class="metric"><span>Kontrol Motoru</span><strong>Aktif</strong></div></div>
<div class="grid grid-2" style="margin-top:18px"><div class="card"><h3>Hızlı Başlangıç</h3><p style="color:var(--muted);line-height:1.7">Görev oluşturucu ile basit görevler, zindan akışı ile katlı sistemler oluştur. Sağ üstten kodu kopyalayabilir veya .quest olarak indirebilirsin.</p><div class="inline"><button class="primary" onclick="go('quest')">Quest Oluştur</button><button class="primary" onclick="go('dungeon')">Zindan Oluştur</button></div></div><div class="card"><h3>Son Akış</h3><div id="dashFlow" class="flow">Henüz kod üretilmedi.</div></div></div></section>

<section id="quest" class="tab hidden"><div class="split"><div class="card"><h3>Görev Oluşturucu</h3><label>Quest Tipi</label><select id="questType" onchange="generateQuest()"><option value="kill">Belirlenen mobdan istenen adet kadar kestir</option>
<option value="talk">Oyuncuyu seçilen NPC ile konuşturup görevi bitir</option>
<option value="biolog">Belirlenen itemleri NPC'ye teslim ettir</option>
<option value="daily">Her 24 saatte bir yapılabilen av görevi oluştur</option></select>
<div class="row"><div><label>Quest Dosya Adı</label><input id="questName" value="metin2_gorev" oninput="generateQuest()"></div><div><label>Görev Başlığı</label><input id="questTitle" value="Yeni Görev" oninput="generateQuest()"></div></div>
<div class="row">
  <div>
    <label>Başlama leveli</label>
    <input id="level" type="number" value="30" oninput="generateQuest()">
  </div>
  <div>
    <label>Kaç adet hedef tamamlanacak?</label>
    <input id="targetCount" type="number" value="10" oninput="generateQuest()">
  </div>
</div>
<p class="hint">Mob kesme ve günlük görevlerde bu alan “kaç adet mob kesilecek” anlamına gelir. Biyolog görevinde “kaç item teslim edilecek” olarak kullanılır.</p>
<label>NPC Ara</label><input id="npcSearch" oninput="searchList('npc')" placeholder="Uriel, Biyolog..."><div id="npcChips" class="chips"></div><div class="row"><div><label>NPC Adı</label><input id="npcName" value="Uriel" oninput="generateQuest()"></div><div><label>NPC VNUM</label><input id="npcVnum" value="20011" oninput="generateQuest()"></div></div>
<details class="fold" open>
  <summary>Kesilecek mob bilgileri</summary>
  <div class="fold-body">
    <label>Mob ara ve seç</label>
    <input id="mobSearch" oninput="searchList('mob')" placeholder="Yabani Köpek, Ork, Azrail...">
    <div id="mobChips" class="chips"></div>
    <div class="row">
      <div>
        <label>Kesilecek mob adı</label>
        <input id="mobName" value="Yabani Köpek" oninput="generateQuest()">
      </div>
      <div>
        <label>Kesilecek mob VNUM</label>
        <input id="mobVnum" value="101" oninput="generateQuest()">
      </div>
    </div>
    <p class="hint">Bu bilgiler quest içindeki <code>when MOB_VNUM.kill</code> satırını oluşturur.</p>
  </div>
</details>
<label>Açıklama</label><textarea id="description" oninput="generateQuest()">Bu görevi tamamlayarak ödül kazanabilirsin.</textarea><h3>Çoklu Ödül</h3><div id="rewards"></div><button class="ghost" onclick="addReward(false)">+ Ödül Ekle</button><div class="inline"><button class="primary" onclick="generateQuest()">Quest Oluştur</button></div></div>
<div class="card"><h3>Önizleme</h3><div id="flow" class="flow"></div><pre id="output"></pre></div></div></section>

<section id="dungeon" class="tab hidden">
  <div class="split">
    <div class="card">
      <h3>Zindan Akışı</h3>

      <details class="fold" open>
        <summary>Başlangıç Bilgileri</summary>
        <div class="fold-body">
          <label>Hazır Şablon</label>
          <select id="template" onchange="applyTemplate()">
            <option value="">Şablon seç</option>
            <option value="tower">Şeytan Kulesi benzeri sıralı zindan</option>
            <option value="boss">Mob temizleyip boss kesilen arena</option>
            <option value="key">Anahtar veya harita toplayarak ilerlenen zindan</option>
            <option value="metin">Metin taşı kırarak ilerlenen zindan</option>
          </select>

          <div class="row">
            <div><label>Zindan Quest Adı</label><input id="dQuestName" value="custom_zindan" oninput="generateDungeon()"></div>
            <div><label>Giriş Başlığı</label><input id="dTitle" value="Zindana Gir" oninput="generateDungeon()"></div>
          </div>

          <div class="row">
            <div><label>Giriş NPC VNUM</label><input id="dNpcVnum" value="20011" oninput="generateDungeon()"></div>
            <div><label>Min Level</label><input id="dLevel" type="number" value="75" oninput="generateDungeon()"></div>
          </div>
        </div>
      </details>

      <details class="fold">
        <summary>Harita ve Süre Ayarları</summary>
        <div class="fold-body">
          <div class="row">
            <div><label>Dungeon Map Index</label><input id="dMap" value="66" oninput="generateDungeon()"></div>
            <div><label>Süre / Dakika</label><input id="dMinutes" type="number" value="60" oninput="generateDungeon()"></div>
          </div>

          <div class="row">
            <div><label>Giriş X</label><input id="dX" value="5905" oninput="generateDungeon()"></div>
            <div><label>Giriş Y</label><input id="dY" value="1105" oninput="generateDungeon()"></div>
          </div>

          <div class="row">
            <div><label>Çıkış Map</label><input id="exitMap" value="1" oninput="generateDungeon()"></div>
            <div><label>Cooldown / Dakika</label><input id="cooldown" type="number" value="0" oninput="generateDungeon()"></div>
          </div>
        </div>
      </details>

      <details class="fold">
        <summary>Giriş Şartları</summary>
        <div class="fold-body">
          <div class="row">
            <div><label>Giriş Item VNUM</label><input id="entryItem" value="0" oninput="generateDungeon()"></div>
            <div><label>Giriş Yang</label><input id="entryYang" type="number" value="0" oninput="generateDungeon()"></div>
          </div>

          <div class="row">
            <div>
              <label>Parti Gerekli</label>
              <select id="partyReq" onchange="generateDungeon()">
                <option value="no">Hayır</option>
                <option value="yes">Evet</option>
              </select>
            </div>
            <div><label>Min Parti Üye</label><input id="minParty" type="number" value="2" oninput="generateDungeon()"></div>
          </div>
        </div>
      </details>

      <details class="fold" open>
        <summary>Katlar</summary>
        <div class="fold-body">
          <div id="floors"></div>
          <div class="inline">
            <button class="ghost" onclick="addFloor('kill')">+ Mob Katı</button>
            <button class="ghost" onclick="addFloor('drop')">+ Drop Katı</button>
            <button class="ghost" onclick="addFloor('boss')">+ Boss Katı</button>
            <button class="ghost" onclick="addFloor('metin')">+ Metin Katı</button>
          </div>
        </div>
      </details>

      <details class="fold">
        <summary>Zindan Ödülleri</summary>
        <div class="fold-body">
          <div id="dRewards"></div>
          <button class="ghost" onclick="addReward(true)">+ Zindan Ödülü Ekle</button>
        </div>
      </details>
    </div>

    <div class="card">
      <h3>Zindan Önizleme</h3>
      <div id="dFlow" class="flow"></div>
      <pre id="dOutput"></pre>
    </div>
  </div>
</section>

<section id="library" class="tab hidden"><div class="card"><h3>VNUM Kütüphanesi</h3><p style="color:var(--muted)">Şimdilik örnek veriler var. Sonraki aşamada mob_names.txt ve item_names.txt import eklenebilir.</p><div id="libraryList" class="lib"></div></div></section>
<section id="validator" class="tab hidden"><div class="card"><h3>Quest Kontrol Paneli</h3><button class="warning" onclick="validateQuest(false)">Kontrol Et</button><div id="issues" style="margin-top:14px"></div></div></section>
</main></div>

<script>
const npcs=[["Uriel",20011],["Biyolog Chaegirab",20084],["Demirci",20016],["Depocu",9005],["Yaşlı Kadın",9006],["Kapıcı",20348],["Genel Satıcı",9003]];
const mobs=[["Yabani Köpek",101],["Ork",601],["Siyah Ork",636],["Zehirli Örümcek",2031],["Kraliçe Örümcek",2091],["Azrail",1093],["Mavi Ejderha",2493],["Metin Taşı",8001]];
const items=[["Dolunay Kılıcı",19],["Kutsama Kağıdı",25040],["Beceri Kitabı",50300],["Ruh Taşı",50513],["Ork Dişi",30006],["Anahtar",50084],["Harita",30179],["Giriş Bileti",71130]];
function byId(id){return document.getElementById(id)} function val(id,f=""){return byId(id)?.value||f}
function clean(s){return String(s||"quest").toLowerCase().replaceAll("ı","i").replaceAll("ğ","g").replaceAll("ü","u").replaceAll("ş","s").replaceAll("ö","o").replaceAll("ç","c").replace(/[^a-z0-9_]/g,"_")}
function esc(s){return String(s||"").replaceAll('"',"'")}
function showTab(name,btn){document.querySelectorAll(".tab").forEach(t=>t.classList.add("hidden"));byId(name).classList.remove("hidden");document.querySelectorAll(".nav button").forEach(b=>b.classList.remove("active"));if(btn)btn.classList.add("active");const m={dashboard:["Genel Bakış","Metin2 quest ve zindan kodlarını panelden üret."],quest:["Quest Builder","Normal görev, günlük görev ve biyolog questleri oluştur."],dungeon:["Dungeon Builder","Katlı zindan, drop, boss, giriş şartı ve ödül sistemi kur."],library:["VNUM Kütüphanesi","NPC, mob ve item VNUM listelerini yönet."],validator:["Quest Kontrol","Üretilen kodu hata ve eksik bilgi açısından kontrol et."]};byId("pageTitle").textContent=m[name][0];byId("pageDesc").textContent=m[name][1]}
function go(name){showTab(name,[...document.querySelectorAll(".nav button")].find(b=>b.textContent.toLowerCase().includes(name==="quest"?"quest":name==="dungeon"?"dungeon":name)))}
function searchList(type){const data=type==="npc"?npcs:mobs,q=val(type+"Search").toLowerCase(),box=byId(type+"Chips");box.innerHTML="";data.filter(x=>x[0].toLowerCase().includes(q)).slice(0,8).forEach(x=>{const c=document.createElement("span");c.className="chip";c.textContent=`${x[0]} (${x[1]})`;c.onclick=()=>{if(type==="npc"){byId("npcName").value=x[0];byId("npcVnum").value=x[1]}else{byId("mobName").value=x[0];byId("mobVnum").value=x[1]}generateQuest()};box.appendChild(c)})}
function addReward(d=false){const wrap=document.createElement("div");wrap.className="reward";wrap.dataset.reward="1";wrap.innerHTML=`<div class="row"><div><label>Item VNUM</label><input value="25040" data-field="item" oninput="${d?'generateDungeon':'generateQuest'}()"></div><div><label>Adet</label><input type="number" value="1" data-field="count" oninput="${d?'generateDungeon':'generateQuest'}()"></div></div><label>Yang</label><input type="number" value="0" data-field="yang" oninput="${d?'generateDungeon':'generateQuest'}()"><button class="danger" onclick="this.parentElement.remove();${d?'generateDungeon':'generateQuest'}()">Sil</button>`;byId(d?"dRewards":"rewards").appendChild(wrap);d?generateDungeon():generateQuest()}
function rewardCode(container){let code="";byId(container).querySelectorAll("[data-reward]").forEach(b=>{const item=b.querySelector('[data-field="item"]').value,count=b.querySelector('[data-field="count"]').value,yang=b.querySelector('[data-field="yang"]').value;if(Number(yang)>0)code+=`                pc.change_money(${yang})\n`;if(Number(item)>0)code+=`                pc.give_item2(${item}, ${count})\n`});return code||"                -- Ödül eklenmedi\n"}
function generateQuest(){const type=val("questType"),q=clean(val("questName")),title=esc(val("questTitle")),level=val("level"),target=val("targetCount"),npc=val("npcVnum"),npcName=esc(val("npcName")),mob=val("mobVnum"),mobName=esc(val("mobName")),desc=esc(val("description"));let code="",flow="";
if(type==="talk"){flow=`${level}. level → ${npcName} ile konuş → ödül al`;code=`quest ${q} begin\n    state start begin\n        when login or levelup with pc.get_level() >= ${level} begin set_state(run) end\n    end\n    state run begin\n        when letter begin send_letter("${title}") end\n        when ${npc}.chat."${title}" begin\n            say_title("${npcName}") say("${desc}")\n${rewardCode("rewards")}            set_state(__COMPLETE__)\n        end\n    end\n    state __COMPLETE__ begin end\nend`}
else{flow=`${level}. level → ${npcName} görev verir → ${target} ${mobName} hedefi → ödül`;code=`quest ${q} begin\n    state start begin\n        when login or levelup with pc.get_level() >= ${level} begin set_state(run) end\n    end\n    state run begin\n        when letter begin send_letter("${title}") end\n        when ${npc}.chat."${title}" begin\n            say_title("${npcName}") say("${desc}") say("${target} tane ${mobName} öldür.")\n            pc.setqf("count",0) set_state(hunt)\n        end\n    end\n    state hunt begin\n        when ${mob}.kill begin\n            local c=pc.getqf("count")+1 pc.setqf("count",c)\n            if c >= ${target} then\n                say_title("${title}") say("Görev tamamlandı.")\n${rewardCode("rewards")}                set_state(__COMPLETE__)\n            end\n        end\n    end\n    state __COMPLETE__ begin end\nend`}
byId("flow").innerHTML=flow;byId("output").textContent=code;byId("dashFlow").textContent=flow}
function floorTypeLabel(type){
  if(type==="drop") return "Drop Katı";
  if(type==="boss") return "Boss Katı";
  if(type==="metin") return "Metin Taşı Katı";
  return "Mob Kesme Katı";
}
function updateFloorHeaders(){
  [...byId("floors").querySelectorAll(".floor")].forEach((el,i)=>{
    const type=el.querySelector('[data-f="type"]')?.value || "kill";
    const vnum=el.querySelector('[data-f="vnum"]')?.value || "0";
    const count=el.querySelector('[data-f="count"]')?.value || "1";
    const drop=el.querySelector('[data-f="dropItem"]')?.value || "0";
    const title=el.querySelector(".floor-title");
    const sub=el.querySelector(".floor-subtitle");
    if(title) title.textContent=`${i+1}. Kat — ${floorTypeLabel(type)}`;
    if(sub){
      if(type==="drop") sub.textContent=`Hedef VNUM: ${vnum} · Drop Item: ${drop}`;
      else sub.textContent=`Hedef VNUM: ${vnum} · Hedef Sayı: ${count}`;
    }
  });
}
function addFloor(type="kill"){
  const el=document.createElement("details");
  el.className="floor";
  el.open=true;
  el.innerHTML=`<summary><span><span class="floor-title">Kat</span><span class="floor-subtitle">Ayarlar</span></span></summary>
  <div class="floor-body">
    <div class="row">
      <div>
        <label>Katın amacı</label>
        <select data-f="type" onchange="updateFloorHeaders();generateDungeon()">
          <option value="kill">Oyuncu bu katta belirlenen mobları keser</option>
          <option value="drop">Oyuncu bu katta anahtar/harita/mühür düşürür</option>
          <option value="boss">Oyuncu bu katta boss öldürür</option>
          <option value="metin">Oyuncu bu katta metin taşı kırar</option>
        </select>
      </div>
      <div>
        <label>Hedef VNUM</label>
        <input data-f="vnum" value="${type==='boss'?1093:type==='metin'?8001:601}" oninput="updateFloorHeaders();generateDungeon()">
      </div>
    </div>

    <div class="row">
      <div>
        <label>Hedef Sayı</label>
        <input data-f="count" type="number" value="${type==='boss'?1:50}" oninput="updateFloorHeaders();generateDungeon()">
      </div>
      <div>
        <label>Spawn X,Y</label>
        <input data-f="spawn" value="6000,1200" oninput="generateDungeon()">
      </div>
    </div>

    <details class="fold">
      <summary>Drop Ayarları</summary>
      <div class="fold-body">
        <div class="row">
          <div>
            <label>Düşecek Item VNUM</label>
            <input data-f="dropItem" value="50084" oninput="updateFloorHeaders();generateDungeon()">
          </div>
          <div>
            <label>Düşme Şansı %</label>
            <input data-f="chance" type="number" value="15" oninput="generateDungeon()">
          </div>
        </div>

        <div class="row">
          <div>
            <label>Kaç kill sonra garanti düşsün?</label>
            <input data-f="guarantee" type="number" value="20" oninput="generateDungeon()">
          </div>
          <div>
            <label>Kaç item toplanınca kat geçilsin?</label>
            <input data-f="need" type="number" value="1" oninput="generateDungeon()">
          </div>
        </div>
      </div>
    </details>

    <div class="floor-actions">
      <button class="danger" onclick="this.closest('.floor').remove();updateFloorHeaders();generateDungeon()">Katı Sil</button>
    </div>
  </div>`;
  el.querySelector('[data-f="type"]').value=type;
  byId("floors").appendChild(el);
  updateFloorHeaders();
  generateDungeon();
}
function floorData(){return[...byId("floors").querySelectorAll(".floor")].map((el,i)=>{const g=k=>el.querySelector(`[data-f="${k}"]`).value;return{no:i+1,type:g("type"),vnum:g("vnum"),count:g("count"),dropItem:g("dropItem"),chance:g("chance"),guarantee:g("guarantee"),need:g("need")}})}
function applyTemplate(){byId("floors").innerHTML="";const t=val("template");if(t==="tower"){addFloor("kill");addFloor("drop");addFloor("metin");addFloor("boss")}if(t==="boss"){addFloor("kill");addFloor("boss")}if(t==="key"){addFloor("drop");addFloor("boss")}if(t==="metin"){addFloor("metin");addFloor("boss")}generateDungeon()}
function generateDungeon(){const q=clean(val("dQuestName","custom_zindan")),title=esc(val("dTitle")),npc=val("dNpcVnum"),level=val("dLevel"),map=val("dMap"),x=val("dX"),y=val("dY"),mins=val("dMinutes"),cd=Number(val("cooldown",0))*60,entryItem=val("entryItem",0),entryYang=val("entryYang",0),party=val("partyReq"),minParty=val("minParty");const fs=floorData();updateFloorHeaders();let body="",flow=`Giriş NPC → Level ${level}+ → ${fs.length} kat → Ödül`;
fs.forEach(f=>{if(f.type==="drop"){body+=`\n        when ${f.vnum}.kill with pc.in_dungeon() begin\n            if d.getf("floor") == ${f.no} then\n                local c=d.getf("drop_${f.no}_kill")+1 d.setf("drop_${f.no}_kill",c)\n                if number(1,100) <= ${f.chance} or c >= ${f.guarantee} then\n                    game.drop_item_with_ownership(${f.dropItem},1) d.setf("drop_${f.no}_kill",0) notice("Gerekli item düştü.")\n                end\n            end\n        end\n        when ${f.dropItem}.use with pc.in_dungeon() begin\n            if d.getf("floor") == ${f.no} then item.remove() local k=d.getf("drop_${f.no}_item")+1 d.setf("drop_${f.no}_item",k) if k >= ${f.need} then d.setf("floor",${f.no+1}) end end\n        end`}else{body+=`\n        when ${f.vnum}.kill with pc.in_dungeon() begin\n            if d.getf("floor") == ${f.no} then\n                local c=d.getf("floor_${f.no}_count")+1 d.setf("floor_${f.no}_count",c)\n                if c >= ${f.count} then notice("${f.no}. kat tamamlandı.") d.setf("floor",${f.no+1}) end\n            end\n        end`}})
const partyCode=party==="yes"?`\n            if not party.is_party() or not party.is_leader() then say("Sadece parti lideri giriş yapabilir.") return end\n            if party.get_near_count() + 1 < ${minParty} then say("Yeterli parti üyesi yok.") return end`:"";const entryCode=(Number(entryItem)>0?`\n            if pc.count_item(${entryItem}) < 1 then say("Giriş biletin yok.") return end\n            pc.remove_item(${entryItem},1)`:"")+(Number(entryYang)>0?`\n            if pc.get_money() < ${entryYang} then say("Yeterli yang yok.") return end\n            pc.change_money(-${entryYang})`:"");const cdCode=cd>0?`\n            if pc.getqf("last_enter") > 0 and get_time() < pc.getqf("last_enter") + ${cd} then say("Cooldown süren dolmadı.") return end\n            pc.setqf("last_enter", get_time())`:"";
const code=`quest ${q} begin\n    state start begin\n        when ${npc}.chat."${title}" begin\n            say_title("${title}")\n            if pc.get_level() < ${level} then say("Levelin yetersiz.") return end${partyCode}${entryCode}${cdCode}\n            local s=select("Gir","Vazgeç") if s==2 then return end\n            d.new_jump(${map},${x},${y}) d.setf("floor",1)\n            server_timer("${q}_timer",${Number(mins)*60},d.get_map_index())\n        end${body}\n        when login with pc.in_dungeon() begin\n            if d.getf("floor") > ${fs.length} then\n                notice("Zindan tamamlandı.")\n${rewardCode("dRewards")}                d.exit_all()\n            end\n        end\n        when ${q}_timer.server_timer begin\n            if d.select(get_server_timer_arg()) then notice("Zindan süresi doldu.") d.exit_all() end\n        end\n    end\nend`;byId("dFlow").innerHTML=flow;byId("dOutput").textContent=code;byId("dashFlow").textContent=flow}
function currentCode(){return byId("dungeon").classList.contains("hidden")?byId("output").textContent:byId("dOutput").textContent}function copyQuest(){navigator.clipboard.writeText(currentCode());alert("Kod kopyalandı.")}
function downloadQuest(){const code=currentCode(),name=byId("dungeon").classList.contains("hidden")?clean(val("questName")):clean(val("dQuestName"));const a=document.createElement("a");a.href=URL.createObjectURL(new Blob([code],{type:"text/plain;charset=utf-8"}));a.download=name+".quest";a.click()}
function validateQuest(jump){const code=currentCode(),issues=[];if(!code.trim())issues.push(["err","Kod üretilmemiş."]);if(code.includes(" 0)")||code.includes(" 0,"))issues.push(["warn","Bazı VNUM değerleri 0 olabilir."]);if(!code.includes("quest "))issues.push(["err","Quest başlangıcı bulunamadı."]);if(!issues.length)issues.push(["ok","Kontrol tamam: belirgin hata görünmüyor."]);byId("issues").innerHTML=issues.map(i=>`<div class="issue ${i[0]}">${i[1]}</div>`).join("");if(jump)go("validator")}
function renderLibrary(){const box=byId("libraryList");box.innerHTML="";[...npcs.map(x=>["NPC",...x]),...mobs.map(x=>["Mob",...x]),...items.map(x=>["Item",...x])].forEach(x=>{const d=document.createElement("div");d.className="lib-card";d.innerHTML=`<b>${x[1]}</b><span>${x[0]} VNUM: ${x[2]}</span>`;box.appendChild(d)})}
addReward(false);addReward(true);addFloor("kill");addFloor("boss");searchList("npc");searchList("mob");renderLibrary();generateQuest();generateDungeon();
</script></body></html>
 

En Çok Reaksiyon Alan Mesajlar

Şu an konuyu görüntüleyenler (Toplam : 0, Üye: 0, Misafir: 0)

Geri
Üst