// NEKO — Shop layer: catalog, product cards, cart, FAQ accordion, sky/terrain decor // Exports to window. Requires site-core.jsx. // --------------------------------------------------------------- // Catalog (placeholder content — cosmetic / supporter only) // --------------------------------------------------------------- const NEKO_CATEGORIES = ["Rütbeler", "Kozmetikler", "Petler", "Efektler", "Kasalar", "Etiketler", "Paketler", "Diyar Özelleştirme"]; const NEKO_PRODUCTS = [ { id: "rank-gezgin", name: "Gezgin Rütbesi", cat: "Rütbeler", price: 950, badge: "Destekçi", badgeCls: "badge-supporter", desc: "Renkli sohbet rengi, /nick ve sezonluk selamlama.", ph: "rütbe ikonu\n(piksel art)" }, { id: "rank-muhafiz", name: "Muhafız Rütbesi", cat: "Rütbeler", price: 1900, badge: "Destekçi", badgeCls: "badge-supporter", desc: "Gezgin'in tümü + dolu sunucuda sıra önceliği.", ph: "rütbe ikonu\n(piksel art)" }, { id: "koz-sapka", name: "Kâşif Şapkası", cat: "Kozmetikler", price: 260, badge: "Kozmetik", badgeCls: "badge-cosmetic", desc: "Geniş kenarlı piksel şapka. Yalnızca görünüm.", ph: "şapka ikonu\n(piksel art)" }, { id: "koz-pelerin", name: "Gece Pelerini", cat: "Kozmetikler", price: 540, badge: "Kozmetik", badgeCls: "badge-cosmetic", desc: "Yıldız desenli pelerin; rüzgârda dalgalanır.", ph: "pelerin ikonu\n(piksel art)" }, { id: "pet-tilki", name: "Alev Tilkisi", cat: "Petler", price: 480, badge: "Kozmetik", badgeCls: "badge-cosmetic", desc: "Yanında dolaşan tilki dostun. Oyun gücü vermez.", ph: "pet ikonu\n(piksel art)" }, { id: "pet-golem", name: "Taş Golemi", cat: "Petler", price: 520, badge: "Kozmetik", badgeCls: "badge-cosmetic", desc: "Ağır, sessiz ve sadık. Peşinden gelir.", ph: "pet ikonu\n(piksel art)" }, { id: "fx-ender", name: "Ender İzi", cat: "Efektler", price: 320, badge: "Yeni", badgeCls: "badge-new", desc: "Yürürken beliren mor kıvılcımlar.", ph: "efekt ikonu\n(piksel art)" }, { id: "fx-bal", name: "Bal Damlası", cat: "Efektler", price: 300, badge: "Kozmetik", badgeCls: "badge-cosmetic", desc: "Zıpladığında altın damlalar saçılır.", ph: "efekt ikonu\n(piksel art)" }, { id: "kasa-gun", name: "Gün Batımı Kasası", cat: "Kasalar", price: 250, badge: "Kozmetik", badgeCls: "badge-cosmetic", desc: "Rastgele bir kozmetik içerir. Yalnızca görsel ödüller.", ph: "kasa ikonu\n(piksel art)" }, { id: "kasa-obs", name: "Obsidyen Kasası", cat: "Kasalar", price: 400, badge: "Yeni", badgeCls: "badge-new", desc: "Gece temalı kozmetik koleksiyonu.", ph: "kasa ikonu\n(piksel art)", soldOut: true }, { id: "tag-kasif", name: "\u201CKâşif\u201D Etiketi", cat: "Etiketler", price: 150, badge: "Kozmetik", badgeCls: "badge-cosmetic", desc: "Sohbette isminin yanında belirir.", ph: "etiket\n(piksel art)" }, { id: "tag-bekci", name: "\u201CSurların Bekçisi\u201D", cat: "Etiketler", price: 180, badge: "Kozmetik", badgeCls: "badge-cosmetic", desc: "Sezon 4'e özel sohbet etiketi.", ph: "etiket\n(piksel art)" }, { id: "paket-sezon", name: "Sezon Destekçi Paketi", cat: "Paketler", price: 2400, badge: "Destekçi", badgeCls: "badge-supporter", desc: "Gezgin Rütbesi + bir pet + bir etiket.", ph: "paket ikonu\n(piksel art)" }, { id: "paket-koz", name: "Kozmetik Başlangıç", cat: "Paketler", price: 900, badge: "Kozmetik", badgeCls: "badge-cosmetic", desc: "Bir pet, bir efekt ve bir etiket seç.", ph: "paket ikonu\n(piksel art)" }, { id: "diyar-renk", name: "Diyar Renk Paketi", cat: "Diyar Özelleştirme", price: 700, badge: "Diyar", badgeCls: "badge-diyar", desc: "Kasabanın profil rengini ve harita vurgusunu özelleştir.", ph: "renk paleti\n(piksel art)" }, { id: "diyar-ikon", name: "Diyar İkon Paketi", cat: "Diyar Özelleştirme", price: 850, badge: "Diyar", badgeCls: "badge-diyar", desc: "Diyarın için özel ikonlar, amblemler ve küçük rozetler.", ph: "amblem seti\n(piksel art)" }, { id: "diyar-bayrak", name: "Diyar Bayrak Seti", cat: "Diyar Özelleştirme", price: 1100, badge: "Diyar", badgeCls: "badge-diyar", desc: "Topluluğunu temsil eden piksel bayrak stilleri.", ph: "bayrak önizleme\n(piksel art)" }, { id: "diyar-kimlik", name: "Diyar Kimlik Paketi", cat: "Diyar Özelleştirme", price: 2100, badge: "Diyar", badgeCls: "badge-diyar", desc: "Renk, ikon ve çerçeveyi tek pakette aç.", ph: "kimlik paketi\n(piksel art)" }, ]; const NEKO_PACKS = [ { id: "k500", amount: 500, bonus: null, note: "Denemek için" }, { id: "k1000", amount: 1000, bonus: "+%5 bonus", note: "En çok tercih edilen başlangıç" }, { id: "k2500", amount: 2500, bonus: "+%10 bonus", note: "En popüler", popular: true }, { id: "k5000", amount: 5000, bonus: "+%15 bonus", note: "Sezonu rahat geçir" }, ]; const NEKO_MODES = [ { name: "RPG Towny", desc: "Kasabanı kur, ittifaklar oluştur, bölgeni geliştir ve Neko dünyasında kendi hikâyeni yaz.", stat: "128 kasaba", href: null }, { name: "Görevler", desc: "Haftalık görev zincirleri, özel NPC rotaları, zindanlar ve ödüllü maceralar.", stat: "240+ görev", href: null }, { name: "Pazar", desc: "Oyuncular arası ticaret, ilanlar, ürün listeleme ve sunucu ekonomisinin merkezi.", stat: "Oyuncu pazarı", href: "Pazar.html" }, { name: "Etkinlikler", desc: "Sezonluk etkinlikler, topluluk yarışmaları, özel görevler ve ödüllü aktiviteler.", stat: "Haftalık", href: "Etkinlikler.html" }, { name: "Kozmetikler", desc: "Şapka, pelerin, efekt, pet ve karakterini öne çıkaran görsel özelleştirmeler.", stat: "Yeni: Alev Tilkisi", href: "Magaza.html?kategori=Kozmetikler" }, { name: "Topluluk", desc: "Discord, arkadaşlıklar, ekipler, sohbet kanalları ve oyuncularla birlikte büyüyen bir dünya.", stat: NEKO.discord, href: "#topluluk", mascot: true }, { name: "Duyurular & Güncellemeler", desc: "Sunucu duyuruları, bakım notları, yenilikler, sezon haberleri ve geliştirme güncellemeleri.", stat: "Sezon 4", href: "Duyurular.html" }, ]; const NEKO_NEWS = [ { title: "Sezon 4 başladı: yeni harita ve sur bölgesi", cat: "Güncelleme", date: "12 Mayıs 2026", ph: "sezon görseli — 16:9" }, { title: "Cuma etkinliği: Sur Savunması geri döndü", cat: "Etkinlik", date: "29 Mayıs 2026", ph: "etkinlik görseli — 16:9" }, { title: "Mağazaya yaz kozmetikleri eklendi", cat: "Mağaza", date: "3 Haziran 2026", ph: "kozmetik görseli — 16:9" }, { title: "Kural güncellemesi: yeni itiraz süreci", cat: "Duyuru", date: "8 Haziran 2026", ph: "duyuru görseli — 16:9" }, ]; const NEKO_SUPPORTERS = [ { user: "TaşçıUsta", item: "Gezgin Rütbesi", hours: 128, seed: 0, showCosmetics: true, cosmetics: ["Kâşif Şapkası", "Ender İzi", "Alev Tilkisi"] }, { user: "GölgeNehri", item: "Alev Tilkisi", hours: 342, seed: 1, showCosmetics: false, cosmetics: [] }, { user: "KuleMimarı", item: "Ender İzi", hours: 76, seed: 2, showCosmetics: true, cosmetics: ["Gece Pelerini", "“Kâşif” Etiketi"] }, { user: "SessizDağ", item: "Gün Batımı Kasası", hours: 215, seed: 3, showCosmetics: true, cosmetics: ["Taş Golemi", "Bal Damlası", "“Surların Bekçisi”"] }, ]; // Backwards-compatible alias (older drafts referenced NEKO_ACTIVITY) const NEKO_ACTIVITY = NEKO_SUPPORTERS; const NEKO_FAQ_KREDI = [ { q: "Kredi nedir?", a: "Kredi, Neko'nın site bakiyesidir. Hesabına yüklersin ve mağazadaki kozmetik ürünlerde harcarsın. Gerçek paraya geri çevrilemez." }, { q: "Krediler oyun gücü verir mi?", a: "Hayır. Kredilerle yalnızca görsel ürünler alınır: petler, efektler, etiketler ve sezonluk kozmetikler. Neko'da oyun gücü satılmaz." }, { q: "Yüklediğim kredi ne zaman hesabıma geçer?", a: "Ödeme altyapısı bağlandığında yükleme anında işlenecek. Şu an bu sayfa tasarım önizlemesidir; gerçek ödeme alınmaz." }, { q: "İade alabilir miyim?", a: "Kullanılmamış krediler için 14 gün içinde destek ekibine yazabilirsin. Harcanan krediler iade edilmez." }, ]; const NEKO_FAQ_DESTEK = [ { q: "Sunucuya bağlanamıyorum", a: "Adresi kontrol et: " + NEKO.ip + " ve sürümün " + NEKO.version + " aralığında olduğundan emin ol. Sorun sürerse Hata Bildirimi kategorisinden bilet aç." }, { q: "Satın aldığım ürün gelmedi", a: "Önce sunucudan çıkıp tekrar gir. Ürün hâlâ yoksa Ödeme Sorunu kategorisinden, işlem numaranla birlikte bilet aç." }, { q: "Ban itirazı nasıl yapılır?", a: "Aşağıdaki formdan Ban İtirazı kategorisini seç. Kullanıcı adını ve itiraz gerekçeni yaz; ekip 48 saat içinde döner." }, { q: "Hesabıma giremiyorum", a: "Giriş, Minecraft hesabınla yapılır; ayrı bir şifre yoktur. Sorun yaşıyorsan Hesap / Giriş kategorisinden bize ulaş." }, ]; // --------------------------------------------------------------- // Small shared pieces // --------------------------------------------------------------- function ItemSlot({ label, size }) { const tiny = typeof size === "number" && size < 72; return (
{tiny ? "" : label}
); } function Price({ value, big }) { return ( {value} Kredi ); } // --------------------------------------------------------------- // Product card types — active site logic (category → A/B/C) // A: V.I.P. / rütbe / destekçi paketleri (premium, altın vurgu) // B: eşya & kozmetik satışları (eşya çerçevesi, kompakt) // C: efekt / pet / karakter görselleri (büyülü, cyan-mor) // Eski taslak varyantlar ("A/B/C" tek tip) Tweaks'te durmaya devam eder; // "auto" seçiliyken kart tipi ürün kategorisinden türetilir. // --------------------------------------------------------------- function nekoCardType(p) { if (p.cat === "Diyar Özelleştirme") return "D"; if (p.cat === "Rütbeler" || p.badge === "Destekçi") return "A"; if (p.cat === "Petler" || p.cat === "Efektler") return "C"; return "B"; } const NEKO_TYPE_COLORS = { A: "var(--c-gold)", B: "var(--c-accent)", C: "var(--c-magic)", D: "var(--c-diyar)" }; function CardAddBtn({ p, onAdd, cls, style, label }) { return ( ); } // Type A — V.I.P. / Destekçi: güçlü çerçeve, altın vurgu, p2w-değil notu function ProductCardTypeA({ p, onAdd }) { return (
V.I.P. · Destekçi {p.badge}
{p.name}

{p.desc}

Oyun gücü vermez — görsel & destekçi ayrıcalıkları
); } // Type B — Eşya / Kozmetik: eşya-çerçevesi hissi, kompakt, grid dostu function ProductCardTypeB({ p, onAdd }) { return (
{p.name} {p.badge}
); } // Type C — Efekt / Pet: büyülü önizleme alanı, cyan-mor detaylar function ProductCardTypeC({ p, onAdd }) { const kind = p.cat === "Petler" ? "Pet" : "Efekt"; return (
{p.name} {kind}

{p.desc}

); } // Type D — Diyar Özelleştirme: arma/sancak hissi, RPG Towny diyar kimliği // "Diyar Önizle" → DiyarPreviewModal (görsel prototip editörü) function ProductCardTypeD({ p, onAdd }) { const [pv, setPv] = React.useState(false); return (
Diyar Kimliği Diyar
{p.name} RPG Towny

{p.desc}

{pv && setPv(false)} />}
); } // --------------------------------------------------------------- // Diyar Önizlemesi — RPG Towny diyar kimliği editörü (görsel prototip) // Renk / arma / sancak seçimleri örnek “Asteria Diyarı” üzerinde canlı // önizlenir. Seçimler satın alım sonrası oyun içinde uygulanır. // --------------------------------------------------------------- const DIYAR_COLORS = [ ["Zümrüt", "#76C5FF"], ["Buz Mavisi", "#76C5FF"], ["Mor Sis", "#C48CF5"], ["Güneş", "#F5C53C"], ["Kor", "#EE3F86"], ["Gümüş", "#A6B0BC"], ]; const DIYAR_EMBLEMS = ["Kale", "Kılıç", "Yıldız", "Kurt", "Çapa", "Taç"]; const DIYAR_BANNERS = [ ["Kırlangıç", "polygon(0 0, 100% 0, 100% 100%, 50% 76%, 0 100%)"], ["Sivri Uç", "polygon(0 0, 100% 0, 100% 78%, 50% 100%, 0 78%)"], ["Kalıp", "polygon(0 0, 100% 0, 100% 100%, 0 100%)"], ]; function DiyarPreviewModal({ p, onAdd, onClose }) { const [ci, setCi] = React.useState(0); const [ei, setEi] = React.useState(0); const [bi, setBi] = React.useState(0); const [after, setAfter] = React.useState(true); React.useEffect(() => { const onKey = (e) => { if (e.key === "Escape") onClose(); }; window.addEventListener("keydown", onKey); return () => window.removeEventListener("keydown", onKey); }, [onClose]); const col = after ? DIYAR_COLORS[ci][1] : "#5B6473"; // önce: boyasız taş grisi const emblem = DIYAR_EMBLEMS[ei].slice(0, 2); const mapCells = 21; // 7×3 mini harita const body = (

Diyar Önizlemesi

{p.name} RPG Towny
{/* Sol — canlı önizleme: Asteria Diyarı */}
{/* Diyar künyesi */}
Asteria Diyarı LV 24
{/* Sancak */}
{/* Harita işaretçisi */}
Harita görünümü
{Array.from({ length: mapCells }).map((_, i) => )} Asteria
{/* Önce / Sonra */}
{/* Sağ — seçiciler */}
Sancak rengi — {DIYAR_COLORS[ci][0]}
{DIYAR_COLORS.map(([n, c], i) => ( ))}
Arma — {DIYAR_EMBLEMS[ei]}
{DIYAR_EMBLEMS.map((n, i) => ( ))}
Sancak stili — {DIYAR_BANNERS[bi][0]}
{DIYAR_BANNERS.map(([n, clip], i) => ( ))}
Görsel önizleme — seçimlerin satın alım sonrası oyun içinde diyarına uygulanır.
); // Kartın clip-path'li atalarından kaçmak için body'ye portal return ReactDOM.createPortal(body, document.body); } // --------------------------------------------------------------- // Product card — "auto" (aktif site: kategoriye göre A/B/C/D) + // eski taslak varyantlar A / B / C (Tweaks'ten zorlanabilir, silinmedi) // + D (yeni diyar kartı, Tweaks'ten tüm ürünlerde önizlenebilir) // --------------------------------------------------------------- function ProductCard({ p, variant, onAdd }) { const v = variant || "auto"; if (v === "auto") { const t = nekoCardType(p); if (t === "A") return ; if (t === "C") return ; if (t === "D") return ; return ; } if (v === "D") return ; const addBtn = (cls, style, label) => ( ); if (v === "B") { return (
{p.badge}
{p.name}

{p.desc}

{addBtn("btn btn-secondary btn-sm", { width: "100%" })}
); } if (v === "C") { return (
{p.name} {p.badge}
{addBtn("btn btn-primary btn-sm", { width: "100%", justifyContent: "space-between", fontFamily: "var(--font-body)", fontSize: 13 }, p.soldOut ? "Tükendi" : Sepete Ekle{p.price} Kredi)}
); } // Variant A — Eşya Çerçevesi return (
{p.name} {p.badge}

{p.desc}

{addBtn("btn btn-primary btn-sm")}
); } // --------------------------------------------------------------- // Cart — localStorage, cross-component event sync // --------------------------------------------------------------- function readCart() { try { return JSON.parse(localStorage.getItem("neko-cart-v1") || "[]"); } catch (e) { return []; } } function writeCart(items) { try { localStorage.setItem("neko-cart-v1", JSON.stringify(items)); } catch (e) {} window.dispatchEvent(new CustomEvent("neko:cart")); } function useCart() { const [items, setItems] = React.useState(readCart); React.useEffect(() => { const on = () => setItems(readCart()); window.addEventListener("neko:cart", on); window.addEventListener("storage", on); return () => { window.removeEventListener("neko:cart", on); window.removeEventListener("storage", on); }; }, []); const add = (p) => { const next = [...readCart()]; const ex = next.find((i) => i.id === p.id); if (ex) ex.qty += 1; else next.push({ id: p.id, qty: 1 }); writeCart(next); nekoToast(p.name + " sepete eklendi"); }; const remove = (id) => writeCart(readCart().filter((i) => i.id !== id)); const count = items.reduce((s, i) => s + i.qty, 0); const detailed = items.map((i) => ({ ...i, p: NEKO_PRODUCTS.find((p) => p.id === i.id) })).filter((i) => i.p); const total = detailed.reduce((s, i) => s + i.p.price * i.qty, 0); return { items: detailed, add, remove, count, total }; } function CartDrawer({ cart, open, onClose }) { React.useEffect(() => { if (!open) return; const onKey = (e) => { if (e.key === "Escape") onClose(); }; window.addEventListener("keydown", onKey); return () => window.removeEventListener("keydown", onKey); }, [open, onClose]); if (!open) return null; return (

Sepet {cart.count > 0 ? "· " + cart.count : ""}

{cart.items.length === 0 ? (

Sepetin boş. Mağazadan kozmetik bir şeyler seç.

) : (
{cart.items.map((i) => (
{i.p.name}{i.qty > 1 ? " ×" + i.qty : ""}
{i.p.price * i.qty} Kredi
))}
Toplam
Devam için Giriş Yap

Önizleme — gerçek ödeme alınmaz.

)}
); } // --------------------------------------------------------------- // FAQ accordion // --------------------------------------------------------------- function FAQAccordion({ items, idPrefix }) { const [openIdx, setOpenIdx] = React.useState(0); return (
{items.map((f, i) => { const open = openIdx === i; return (

{open && (
{f.a}
)}
); })}
); } // --------------------------------------------------------------- // Sky + blocky terrain (hero decor — squares only, no SVG) // --------------------------------------------------------------- const nekoTerrainHeights = [3, 4, 4, 5, 6, 5, 7, 8, 8, 7, 6, 5, 5, 4, 5, 6, 7, 7, 6, 5, 4, 4, 3, 3, 4, 5, 5, 6, 5, 4, 3, 3]; const nekoOreCols = { 7: 2, 12: 1, 18: 3, 25: 1 }; function NekoTerrain({ theme, unit }) { const u = unit || 16; const sun = theme === "sunlight"; return ( ); } function NekoSky({ theme }) { const sun = theme === "sunlight"; const stars = [[8, 18, 4], [16, 38, 3], [26, 12, 5], [38, 30, 3], [55, 10, 4], [64, 26, 3], [76, 16, 5], [88, 34, 3], [93, 12, 4], [46, 44, 3]]; return ( ); } Object.assign(window, { NEKO_CATEGORIES, NEKO_PRODUCTS, NEKO_PACKS, NEKO_MODES, NEKO_NEWS, NEKO_ACTIVITY, NEKO_SUPPORTERS, NEKO_FAQ_KREDI, NEKO_FAQ_DESTEK, NEKO_TYPE_COLORS, nekoCardType, CardAddBtn, ProductCardTypeA, ProductCardTypeB, ProductCardTypeC, ProductCardTypeD, ItemSlot, Price, ProductCard, useCart, CartDrawer, FAQAccordion, NekoTerrain, NekoSky, DiyarPreviewModal, DIYAR_COLORS, DIYAR_EMBLEMS, DIYAR_BANNERS, });