トップ   編集 差分 履歴 添付 複製 名前変更 リロード   新規 一覧 検索 最終更新   ヘルプ   最終更新のRSS

LimbusMap/apollyon_nw/1 の変更点


#author("2025-07-14T12:31:19+02:00","","")
#html{{
<style>
.content {
  width: 512px;
  max-width: 1152px;
  margin-left: 0 !important;  /* 余白をなくす */
  align-items: flex-start !important;
  font-family: "Meiryo", sans-serif;
  font-size: 10px;
  line-height: 1.2;
  display: flex;
  flex-direction: column;
  gap: 8px;
  padding-left: 0; /* 念のため */
}

.main {
  width: 100%;
}

.map-area {
  width: 100%; /* 親幅に合わせる */
  display: flex;
  justify-content: center;
  margin-bottom: 8px;
}

.map-area img {
  width: 100%; /* 親の幅にフィット */
  height: auto;
  display: block;
  margin: 0 auto;
}

.cards-area {
  width: 700px; /* ここは全体幅、必要に応じて調整 */
  padding: 0;
  box-sizing: border-box;
  display: grid;
  grid-template-columns: repeat(2, 340px); /* 2列でカード幅は340px */
  gap: 8px 8px; /* 縦横ともに8pxの隙間 */
  justify-content: start; /* 左寄せにして余白減らす */
  margin-left: 0;          /* 左マージンリセット */
}

.card {
  padding-left: 48px; /* テキスト開始を1文字半分ぐらい右に */
  position: relative;
  display: flex;
  align-items: flex-start;
  width: 340px;
  height: 74px;
  margin-bottom: 8px;
  background-size: 340px 74px;
  background-repeat: no-repeat;
  background-position: left center;
  /* box-shadowは削除 */
  /* box-shadow: 1px 1px 3px rgba(0,0,0,0.15); */
  box-sizing: border-box;
  /* 他のスタイルはそのまま */
}
  .card.mob-red {
    background-image: url('./images/mob_red.png');
  }
  .card.mob-blue {
    background-image: url('./images/mob_blue.png');
  }
  .mob-icon {
    width: 48px;
    height: 48px;
    position: relative;
    left: -44px; /* 画像を左に24pxずらす */
    margin-top: 8px;
  }
.info {
  position: absolute;
  left: 52px;  
  top: 7px;     /* 11px → 7px にして上に寄せる */
  right: 24px;
  font-size: 10px;
}

  .name {
    font-weight: bold;
    color: #d94b6a;
    font-size: 11px;
    margin-bottom: 0;  /* 2px → 0 にして名前とテキストを詰める */
  }
  .line {
    white-space: nowrap;      /* 折り返さずに1行表示 */
    overflow: visible;        /* はみ出しも表示 */
    text-overflow: clip;      /* 省略記号なし */
    font-size: 10px;
  }
  .line .danger {
    color: red;
    font-weight: bold;
  }
  .sense-icons {
    position: absolute;
    bottom: 8px;
    right: 24px;
    display: flex;
    gap: 2px;
  }
  .sense-icons img {
    width: 12px;
    height: 14px;
  }
  .attr-icon {
    width: 10px;
    height: 10px;
    vertical-align: middle;
    margin-right: 2px;
  }

.map-header {
  display: flex;
  align-items: center;
  padding: 6px 12px;
  gap: 8px;
  font-weight: bold;
  font-size: 18px; /* 14px→18pxに拡大 */
  color: #c94c6d;
}

.zone-floor-title.current-floor {
  color: #c94c6d;
  font-weight: bold;
}

/* 階層ボタンの現在階層もよりわかりやすく */
.floor-buttons img.current {
  filter: none; /* マンドラゴラちゃんにはぼかしを適用しない */
}

.floor-buttons {
  display: flex;
  gap: 6px;
}

.floor-buttons img {
  width: 24px;
  height: 24px;
  cursor: pointer;
  transition: filter 0.2s ease;
}

.floor-buttons img:hover {
  filter: drop-shadow(0 0 3px #c94c6d);
}
</style>

<div class="content">
  <div class="title-bar" id="stageTitle">アポリオン NW #1</div>
  <div class="main">

    <div class="map-header">
      <div class="zone-floor-title" id="zoneFloorTitle">アポリオン NW #1</div>
      <div class="floor-buttons" id="floorButtons"></div>
    </div>

    <div class="map-area">
      <img id="mapImage" src="" alt="">
    </div>

    <div class="cards-area" id="cardsArea"></div>
  </div>
</div>

<script>
(() => {
  let zoneKey = 'apollyon_nw'; // letにして変更可能に
  let floor = 1;               // 数値型に

  const basePath = '/FFXI/Limbusinfo/';

  // ここにゾーンごとの最大階層数を用意(例)
  const zoneMaxFloors = {
    apollyon_nw: 5,
    apollyon_sw: 4,
    apollyon_ne: 5,
    apollyon_se: 4,
    temenos_北塔: 7,
    temenos_西塔: 7,
    temenos_東塔: 7,
    temenos_中央塔: 4,
  };

  const attrIcons = {
    '火': 'Fire-Icon.png',
    '氷': 'Ice-Icon.png',
    '風': 'Wind-Icon.png',
    '土': 'Earth-Icon.png',
    '雷': 'Lightning-Icon.png',
    '水': 'Water-Icon.png',
    '光': 'Light-Icon.png',
    '闇': 'Dark-Icon.png'
  };

  const senseIconMap = {
    'インビジ': 'Invisible.png',
    'スニーク': 'Sneak.png',
    'リンク': 'Link.png',
    '生命': 'Low_HP.png'  // 生命アイコンを追加(ファイル名は適宜)
  };

  function renderAttrIcons(text) {
    if (!text) return '';
    return text.replace(/(火|氷|風|土|雷|水|光|闇)/g, m => {
      return `<img class="attr-icon" src="${basePath}images/icon/${attrIcons[m]}" title="${m}">`;
    });
  }

  function createMonsterCard(mob) {
    const danger = mob.sense.includes('見破り');
    const bgClass = danger ? 'mob-red' : 'mob-blue';

    const icons = [];
    Object.entries(senseIconMap).forEach(([key, icon]) => {
      if (mob.sense.includes(key) || mob.details.includes(key)) {
        icons.push(icon);
      }
    });

    // 生命と見破りを赤文字に加工する関数
    function colorizeKeywords(text) {
      if (!text) return '';
      return text
        .replace(/生命/g, '<span style="color:red;">生命</span>')
        .replace(/見破り/g, '<span style="color:red;">見破り</span>');
    }

    return `
      <div class="card ${bgClass}" style="background-image:url('${basePath}images/${danger ? 'mob_red.png' : 'mob_blue.png'}')">
        <img class="mob-icon" src="${basePath}images/mobs/${mob.image}" alt="${mob.name}">
        <div class="info">
          <div class="name">${mob.name}</div>
          <div class="line">【感知】<span class="${danger ? 'danger' : ''}">${colorizeKeywords(renderAttrIcons(mob.sense))}</span></div>
          <div class="line">【睡眠】${renderAttrIcons(mob.sleep)} 【弱体】${renderAttrIcons(mob.debuff)}</div>
          <div class="line">【弱点】${renderAttrIcons(mob.weakness)} 【耐性】${renderAttrIcons(mob.resistance)}</div>
          <div class="line">【詳細】${mob.details}</div>
        </div>
        <div class="sense-icons">${icons.map(i => `<img src="${basePath}images/icon/${i}">`).join('')}</div>
      </div>
    `;
  }

  // 階層ボタンを生成する関数
  function buildFloorButtons() {
    const floorButtons = document.getElementById('floorButtons');
    floorButtons.innerHTML = ''; // クリア

    const maxFloor = zoneMaxFloors[zoneKey] || 1;
    const zoneFloorTitle = document.getElementById('zoneFloorTitle');

    for(let i = 1; i <= maxFloor; i++) {
      const img = document.createElement('img');
      const numStr = String(i).padStart(2, '0');
      if (i === floor) {
        img.src = `${basePath}images/icon/Mandragora.png`;
      } else {
        img.src = `${basePath}images/icon/${numStr}.png`;
      }
      img.title = `階層 ${i}`;
      img.className = (i === floor) ? 'current' : '';
      img.onclick = () => {
        if (floor !== i) {
          floor = i;
          loadStage(zoneKey, floor);
          buildFloorButtons(); // 強調更新
        }
      };
      floorButtons.appendChild(img);
    }

    // ゾーンタイトルにも現在階層の強調クラス付与
    if(zoneFloorTitle) {
      if(!zoneFloorTitle.classList.contains('current-floor')) {
        zoneFloorTitle.classList.add('current-floor');
      }
    }
  }

  async function loadStage(loadZoneKey = zoneKey, loadFloor = floor) {
    zoneKey = loadZoneKey;
    floor = loadFloor;

    try {
      const res = await fetch(`${basePath}data/${zoneKey}_${floor}.json`);
      if (!res.ok) throw new Error('データ取得失敗');
      const stage = await res.json();

      document.getElementById('stageTitle').textContent = stage.title;
      document.getElementById('zoneFloorTitle').textContent = stage.title;
      document.getElementById('zoneFloorTitle').className = 'current-floor';
      document.getElementById('mapImage').src = basePath + 'map/' + stage.map;

      const cardsArea = document.getElementById('cardsArea');
      cardsArea.innerHTML = stage.monsters.map(createMonsterCard).join('');

      buildFloorButtons();

      // 音声再生省略(必要なら追加可能)
    } catch (e) {
      console.error(e);
      document.getElementById('stageTitle').textContent = 'データがありません';
      document.getElementById('zoneFloorTitle').textContent = 'データがありません';
      document.getElementById('mapImage').src = '';
      document.getElementById('cardsArea').innerHTML = '';
    }
  }

  loadStage();
})();
</script>
}}

#author("2025-07-15T10:18:46+02:00","","")
#limbusview(zone=apollyon_nw floor=1)