{"id":84,"date":"2025-10-26T21:47:59","date_gmt":"2025-10-26T21:47:59","guid":{"rendered":"https:\/\/makeyour.life\/?page_id=84"},"modified":"2025-12-24T18:45:04","modified_gmt":"2025-12-24T23:45:04","slug":"build-your-world","status":"publish","type":"page","link":"https:\/\/makeyour.life\/index.php\/build-your-world\/","title":{"rendered":"Build Your World"},"content":{"rendered":"\t\t<div data-elementor-type=\"wp-page\" data-elementor-id=\"84\" class=\"elementor elementor-84\" data-elementor-post-type=\"page\">\n\t\t\t\t<div class=\"elementor-element elementor-element-b2395f6 e-flex e-con-boxed e-con e-parent\" data-id=\"b2395f6\" data-element_type=\"container\" data-e-type=\"container\">\n\t\t\t\t\t<div class=\"e-con-inner\">\n\t\t\t\t<div class=\"elementor-element elementor-element-b45ffe9 elementor-widget__width-inherit elementor-widget elementor-widget-shortcode\" data-id=\"b45ffe9\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"shortcode.default\">\n\t\t\t\t\t\t\t<div class=\"elementor-shortcode\"><div style=\"padding:14px;border:1px solid #e4e7ec;border-radius:12px\">Please log in to enter Make Your Life World.<\/div>\n<\/div>\n\t\t\t\t\t\t<\/div>\n\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t<div class=\"elementor-element elementor-element-764b865 e-flex e-con-boxed e-con e-parent\" data-id=\"764b865\" data-element_type=\"container\" data-e-type=\"container\">\n\t\t\t\t\t<div class=\"e-con-inner\">\n\t\t\t\t<div class=\"elementor-element elementor-element-b51819c elementor-widget elementor-widget-shortcode\" data-id=\"b51819c\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"shortcode.default\">\n\t\t\t\t\t\t\t<div class=\"elementor-shortcode\">  <div id=\"pm-city-root\"\r\n       data-nonce=\"feb45080ca\"\r\n       data-default-theme=\"medieval\"\r\n       data-grid-w=\"12\"\r\n       data-grid-h=\"12\"\r\n       data-tile=\"42\">\r\n    <div class=\"pm-city-top\">\r\n      <div class=\"pm-city-brand\">\ud83c\udfd9\ufe0f <b>City Builder<\/b><\/div>\r\n\r\n      <div class=\"pm-city-controls\">\r\n        <label class=\"pm-city-label\">Theme<\/label>\r\n        <select id=\"pmCityTheme\">\r\n          <option value=\"medieval\">Medieval<\/option>\r\n          <option value=\"modern\">Modern<\/option>\r\n        <\/select>\r\n\r\n        <label class=\"pm-city-label\">Tile<\/label>\r\n        <button type=\"button\" id=\"pmTileMinus\">\u2212<\/button>\r\n        <span id=\"pmTileSize\">42px<\/span>\r\n        <button type=\"button\" id=\"pmTilePlus\">+<\/button>\r\n\r\n        <button type=\"button\" id=\"pmSaveBtn\">Save<\/button>\r\n        <button type=\"button\" id=\"pmResetBtn\">Reset<\/button>\r\n        <span id=\"pmHint\" class=\"pm-city-hint\"><\/span>\r\n      <\/div>\r\n    <\/div>\r\n\r\n    <div class=\"pm-city-layout\">\r\n      <div class=\"pm-city-store\">\r\n        <h3>Store<\/h3>\r\n        <div id=\"pmStore\" class=\"pm-store-items\"><\/div>\r\n        <div class=\"pm-store-note\">\r\n          <b>Rule:<\/b> Select an item then tap a tile to place it.<br>\r\n          <small>Tip: Logged-in users save to account. Guests save locally.<\/small>\r\n        <\/div>\r\n      <\/div>\r\n\r\n      <div class=\"pm-city-map\">\r\n        <h3>City Map<\/h3>\r\n        <div id=\"pmGrid\" class=\"pm-grid\"><\/div>\r\n      <\/div>\r\n    <\/div>\r\n  <\/div>\r\n    <style>\r\n    #pm-city-root{font-family:system-ui,-apple-system,Segoe UI,Roboto,Arial; max-width:1100px; margin:0 auto;}\r\n    .pm-city-top{display:flex; justify-content:space-between; align-items:center; gap:12px; margin:10px 0 16px; flex-wrap:wrap;}\r\n    .pm-city-controls{display:flex; align-items:center; gap:10px; flex-wrap:wrap;}\r\n    .pm-city-label{font-size:12px; opacity:.75}\r\n    .pm-city-controls button, .pm-city-controls select{\r\n      border:1px solid #d6d6d6; background:#fff; padding:8px 10px; border-radius:10px; cursor:pointer;\r\n    }\r\n    .pm-city-controls button:hover{filter:brightness(.97)}\r\n    .pm-city-hint{font-size:12px; opacity:.8; padding-left:6px}\r\n    .pm-city-layout{display:grid; grid-template-columns: 340px 1fr; gap:16px;}\r\n    @media(max-width:900px){ .pm-city-layout{grid-template-columns:1fr;} }\r\n    .pm-city-store,.pm-city-map{background:#fff; border:1px solid #e7e7e7; border-radius:16px; padding:14px;}\r\n    .pm-store-items{display:flex; flex-direction:column; gap:10px;}\r\n    .pm-store-card{border:1px solid #e6e6e6; border-radius:14px; padding:12px; display:flex; gap:12px; align-items:center;}\r\n    .pm-store-thumb{width:52px; height:52px; border-radius:12px; border:1px solid #e6e6e6; background:#fafafa; display:flex; align-items:center; justify-content:center; overflow:hidden;}\r\n    .pm-store-thumb img{max-width:100%; max-height:100%; image-rendering:auto;}\r\n    .pm-store-meta{flex:1;}\r\n    .pm-store-title{font-weight:700; margin:0;}\r\n    .pm-store-sub{font-size:12px; opacity:.75; margin-top:2px;}\r\n    .pm-store-btn{min-width:120px;}\r\n    .pm-store-btn.pm-selected{border-color:#111; font-weight:700;}\r\n    .pm-store-note{margin-top:12px; font-size:13px; opacity:.85; line-height:1.35}\r\n    .pm-grid{display:grid; gap:8px; align-content:start;}\r\n    .pm-tile{\r\n      width:42px; height:42px; border-radius:12px;\r\n      border:1px solid #e6e6e6; background:#f7fff7;\r\n      display:flex; align-items:center; justify-content:center;\r\n      overflow:hidden; cursor:pointer;\r\n      position:relative;\r\n    }\r\n    .pm-tile img{max-width:100%; max-height:100%; pointer-events:none;}\r\n    .pm-tile:hover{filter:brightness(.98)}\r\n  <\/style>\r\n\r\n    <script>\r\n  (function(){\r\n    const root = document.getElementById('pm-city-root');\r\n    if(!root) return;\r\n\r\n    const nonce = root.dataset.nonce;\r\n    const defaultTheme = root.dataset.defaultTheme || 'medieval';\r\n    let gridW = parseInt(root.dataset.gridW || '12', 10);\r\n    let gridH = parseInt(root.dataset.gridH || '12', 10);\r\n    let tileSize = parseInt(root.dataset.tile || '42', 10);\r\n\r\n    const elTheme = document.getElementById('pmCityTheme');\r\n    const elStore = document.getElementById('pmStore');\r\n    const elGrid  = document.getElementById('pmGrid');\r\n    const elHint  = document.getElementById('pmHint');\r\n    const elTileSize = document.getElementById('pmTileSize');\r\n\r\n    const btnMinus = document.getElementById('pmTileMinus');\r\n    const btnPlus  = document.getElementById('pmTilePlus');\r\n    const btnSave  = document.getElementById('pmSaveBtn');\r\n    const btnReset = document.getElementById('pmResetBtn');\r\n\r\n    let assets = null; \/\/ { theme: { categories: {name:[urls...]}}}\r\n    let state = null;  \/\/ { theme, tileSize, gridW, gridH, cells: [{cat, url}] length = W*H }\r\n    let selected = null; \/\/ {cat, url}\r\n\r\n    function hint(msg){ elHint.textContent = msg || ''; }\r\n\r\n    function ajax(action, data){\r\n      const fd = new FormData();\r\n      fd.append('action', action);\r\n      fd.append('nonce', nonce);\r\n      Object.entries(data||{}).forEach(([k,v]) => fd.append(k, typeof v === 'string' ? v : JSON.stringify(v)));\r\n      return fetch('https:\/\/makeyour.life\/wp-admin\/admin-ajax.php', { method:'POST', credentials:'same-origin', body: fd })\r\n        .then(r => r.json());\r\n    }\r\n\r\n    function setTileSize(px){\r\n      tileSize = Math.max(28, Math.min(72, px));\r\n      elTileSize.textContent = tileSize + 'px';\r\n      elGrid.style.setProperty('--pm-tile', tileSize + 'px');\r\n      Array.from(elGrid.querySelectorAll('.pm-tile')).forEach(t => {\r\n        t.style.width = tileSize+'px';\r\n        t.style.height = tileSize+'px';\r\n      });\r\n      if(state) state.tileSize = tileSize;\r\n    }\r\n\r\n    function emptyState(theme){\r\n      return {\r\n        theme,\r\n        tileSize,\r\n        gridW,\r\n        gridH,\r\n        cells: Array(gridW*gridH).fill(null).map(()=>({cat:'land', url:null}))\r\n      };\r\n    }\r\n\r\n    function ensureCells(){\r\n      const want = gridW*gridH;\r\n      if(!state.cells || state.cells.length !== want){\r\n        const old = state.cells || [];\r\n        state.cells = Array(want).fill(null).map((_,i)=> old[i] ? old[i] : ({cat:'land', url:null}));\r\n      }\r\n    }\r\n\r\n    function renderGrid(){\r\n      elGrid.style.gridTemplateColumns = `repeat(${gridW}, ${tileSize}px)`;\r\n      elGrid.innerHTML = '';\r\n\r\n      ensureCells();\r\n\r\n      for(let i=0;i<gridW*gridH;i++){\r\n        const cell = state.cells[i];\r\n        const d = document.createElement('div');\r\n        d.className = 'pm-tile';\r\n        d.style.width = tileSize+'px';\r\n        d.style.height = tileSize+'px';\r\n        d.dataset.idx = i;\r\n\r\n        \/\/ default background\r\n        if(cell && cell.url){\r\n          const img = document.createElement('img');\r\n          img.src = cell.url;\r\n          img.alt = cell.cat || '';\r\n          d.appendChild(img);\r\n        }\r\n\r\n        d.addEventListener('click', () => {\r\n          if(!selected){\r\n            hint('Select an item first.');\r\n            return;\r\n          }\r\n          state.cells[i] = { cat: selected.cat, url: selected.url };\r\n          renderGrid(); \/\/ simple rerender\r\n          hint(`Placed: ${selected.cat}`);\r\n        });\r\n\r\n        elGrid.appendChild(d);\r\n      }\r\n    }\r\n\r\n    function firstThumb(list){\r\n      return (list && list.length) ? list[0] : null;\r\n    }\r\n\r\n    function renderStore(){\r\n      elStore.innerHTML = '';\r\n\r\n      const theme = state.theme;\r\n      const cats = (assets && assets[theme] && assets[theme].categories) ? assets[theme].categories : {};\r\n      const keys = Object.keys(cats);\r\n\r\n      if(!keys.length){\r\n        elStore.innerHTML = '<div style=\"opacity:.7\">No assets found. Check your uploads folders.<\/div>';\r\n        return;\r\n      }\r\n\r\n      keys.forEach(cat => {\r\n        const list = cats[cat] || [];\r\n        const thumb = firstThumb(list);\r\n\r\n        const card = document.createElement('div');\r\n        card.className = 'pm-store-card';\r\n\r\n        const th = document.createElement('div');\r\n        th.className = 'pm-store-thumb';\r\n        if(thumb){\r\n          const img = document.createElement('img');\r\n          img.src = thumb;\r\n          img.alt = cat;\r\n          th.appendChild(img);\r\n        } else {\r\n          th.textContent = '\u2014';\r\n        }\r\n\r\n        const meta = document.createElement('div');\r\n        meta.className = 'pm-store-meta';\r\n        meta.innerHTML = `<div class=\"pm-store-title\">${cat}<\/div>\r\n                          <div class=\"pm-store-sub\">${list.length} images<\/div>`;\r\n\r\n        const btn = document.createElement('button');\r\n        btn.type = 'button';\r\n        btn.className = 'pm-store-btn';\r\n        btn.textContent = (selected && selected.cat === cat) ? 'Selected \u2705' : 'Select';\r\n\r\n        if(selected && selected.cat === cat) btn.classList.add('pm-selected');\r\n\r\n        btn.addEventListener('click', () => {\r\n          if(!list.length){\r\n            hint(`No images in: ${cat}`);\r\n            return;\r\n          }\r\n          \/\/ pick random image each time you select (fun)\r\n          const pick = list[0];\r\n          selected = { cat, url: pick };\r\n          renderStore();\r\n          hint(`Selected: ${cat}`);\r\n        });\r\n\r\n        card.appendChild(th);\r\n        card.appendChild(meta);\r\n        card.appendChild(btn);\r\n        elStore.appendChild(card);\r\n      });\r\n    }\r\n\r\n    function loadLocal(){\r\n      try{\r\n        const k = 'pm_city_builder_state_v1';\r\n        const raw = localStorage.getItem(k);\r\n        return raw ? JSON.parse(raw) : null;\r\n      }catch(e){ return null; }\r\n    }\r\n    function saveLocal(){\r\n      try{\r\n        const k = 'pm_city_builder_state_v1';\r\n        localStorage.setItem(k, JSON.stringify(state));\r\n      }catch(e){}\r\n    }\r\n\r\n    async function loadAll(){\r\n      hint('Loading assets\u2026');\r\n      const res = await ajax('pm_city_assets', {});\r\n      if(!res || !res.success){\r\n        hint('Server error loading assets.');\r\n        return;\r\n      }\r\n      assets = res.data.assets;\r\n\r\n      \/\/ Load state (server first, then local)\r\n      let st = null;\r\n      const stRes = await ajax('pm_city_state_get', {});\r\n      if(stRes && stRes.success && stRes.data && stRes.data.state){\r\n        st = stRes.data.state;\r\n      } else {\r\n        st = loadLocal();\r\n      }\r\n\r\n      if(!st || !st.theme){\r\n        st = emptyState(defaultTheme);\r\n      }\r\n\r\n      state = st;\r\n      elTheme.value = state.theme || defaultTheme;\r\n\r\n      gridW = state.gridW || gridW;\r\n      gridH = state.gridH || gridH;\r\n\r\n      setTileSize(parseInt(state.tileSize || tileSize, 10));\r\n      ensureCells();\r\n\r\n      selected = null;\r\n      renderStore();\r\n      renderGrid();\r\n      hint('Ready \u2705 Select an item then tap the map.');\r\n    }\r\n\r\n    async function saveState(){\r\n      if(!state) return;\r\n      hint('Saving\u2026');\r\n      saveLocal();\r\n      const res = await ajax('pm_city_state_save', { state });\r\n      if(res && res.success){\r\n        hint('Saved \u2705');\r\n      } else {\r\n        \/\/ guest users will fail (no login) \u2014 local already saved\r\n        hint('Saved locally \u2705 (Login to save to account)');\r\n      }\r\n    }\r\n\r\n    function resetState(){\r\n      state = emptyState(elTheme.value || defaultTheme);\r\n      selected = null;\r\n      setTileSize(tileSize);\r\n      renderStore();\r\n      renderGrid();\r\n      saveLocal();\r\n      hint('Reset \u2705');\r\n    }\r\n\r\n    \/\/ Events\r\n    elTheme.addEventListener('change', () => {\r\n      const t = elTheme.value;\r\n      state.theme = t;\r\n      selected = null;\r\n      renderStore();\r\n      renderGrid();\r\n      hint('Theme changed \u2705');\r\n    });\r\n\r\n    btnMinus.addEventListener('click', () => setTileSize(tileSize - 2));\r\n    btnPlus.addEventListener('click', () => setTileSize(tileSize + 2));\r\n    btnSave.addEventListener('click', saveState);\r\n    btnReset.addEventListener('click', resetState);\r\n\r\n    \/\/ Start\r\n    loadAll();\r\n  })();\r\n  <\/script>\r\n  <\/div>\n\t\t\t\t\t\t<\/div>\n\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t","protected":false},"excerpt":{"rendered":"","protected":false},"author":1,"featured_media":0,"parent":0,"menu_order":0,"comment_status":"closed","ping_status":"closed","template":"","meta":{"site-sidebar-layout":"no-sidebar","site-content-layout":"","ast-site-content-layout":"full-width-container","site-content-style":"default","site-sidebar-style":"default","ast-global-header-display":"","ast-banner-title-visibility":"","ast-main-header-display":"","ast-hfb-above-header-display":"","ast-hfb-below-header-display":"","ast-hfb-mobile-header-display":"","site-post-title":"","ast-breadcrumbs-content":"","ast-featured-img":"disabled","footer-sml-layout":"","ast-disable-related-posts":"","theme-transparent-header-meta":"","adv-header-id-meta":"","stick-header-meta":"","header-above-stick-meta":"","header-main-stick-meta":"","header-below-stick-meta":"","astra-migrate-meta-layouts":"default","ast-page-background-enabled":"default","ast-page-background-meta":{"desktop":{"background-color":"var(--ast-global-color-5)","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""},"tablet":{"background-color":"","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""},"mobile":{"background-color":"","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""}},"ast-content-background-meta":{"desktop":{"background-color":"var(--ast-global-color-4)","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""},"tablet":{"background-color":"var(--ast-global-color-4)","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""},"mobile":{"background-color":"var(--ast-global-color-4)","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""}},"footnotes":""},"class_list":["post-84","page","type-page","status-publish","hentry"],"_links":{"self":[{"href":"https:\/\/makeyour.life\/index.php\/wp-json\/wp\/v2\/pages\/84","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/makeyour.life\/index.php\/wp-json\/wp\/v2\/pages"}],"about":[{"href":"https:\/\/makeyour.life\/index.php\/wp-json\/wp\/v2\/types\/page"}],"author":[{"embeddable":true,"href":"https:\/\/makeyour.life\/index.php\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/makeyour.life\/index.php\/wp-json\/wp\/v2\/comments?post=84"}],"version-history":[{"count":46,"href":"https:\/\/makeyour.life\/index.php\/wp-json\/wp\/v2\/pages\/84\/revisions"}],"predecessor-version":[{"id":1141,"href":"https:\/\/makeyour.life\/index.php\/wp-json\/wp\/v2\/pages\/84\/revisions\/1141"}],"wp:attachment":[{"href":"https:\/\/makeyour.life\/index.php\/wp-json\/wp\/v2\/media?parent=84"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}