public/lib/wardrobe.js

分割 1 / 7 · 元ファイル 1〜196行付近

原文の連続部分です。長い圧縮行は行の途中で分割する場合があります。全分割を順に連結するとファイル全文になります。

/* ===== ショップの品を描く部品(絵は1枚も使わない) =====
   ここに入っているのは3つの棚。
     COSTUMES … 特別衣装 51種(1つ目は「そのまま」=何も描かない)
     HATS     … 帽子・頭装備 21種(1つ目は「なし」=何も描かない)
     STONES   … 石の見た目 10種(1つ目は今の石そのもの)
   全部見た目だけ。強さ・当たり判定は一切変わらない。

   衣装の描き方:
     draw(g, x, shY, bot, r, col, dir)
       x   … 体の真ん中の横位置
       shY … 肩の高さ / bot … 体の下端
       r   … 頭の半径(大きさは全部これ基準。r を変えれば丸ごと伸び縮みする)
       col … その人の服の色 / dir … 向き(+1で右・-1で左)
     人の胴は上が狭く下が広い台形。その上から描くので、台形が透けないよう必ず塗りつぶす。
     裾は下へ r*0.9、横へ r*1.7 まで出してよい。

   帽子の描き方:
     draw(g, x, yHead, r, dir, hairCol)
     髪はもう描かれているので、頭のてっぺんをきちんと隠す形にする。上へは r*2.2 まで。

   石の描き方:
     draw(g, r, rot)   原点(0,0)が石の真ん中。r*1.3 の外へはみ出さない。 */
(function () {

  /* ---- 色を作る小道具(その人の服の色から明るい色・暗い色を作る) ---- */
  function hx(c) {
    c = String(c).replace('#', '');
    if (c.length === 3) c = c[0] + c[0] + c[1] + c[1] + c[2] + c[2];
    return [parseInt(c.slice(0, 2), 16), parseInt(c.slice(2, 4), 16), parseInt(c.slice(4, 6), 16)];
  }
  function rgb(a) { return 'rgb(' + (a[0] | 0) + ',' + (a[1] | 0) + ',' + (a[2] | 0) + ')'; }
  function lite(c, f) { var a = hx(c); return rgb([a[0] + (255 - a[0]) * f, a[1] + (255 - a[1]) * f, a[2] + (255 - a[2]) * f]); }
  function dark(c, f) { var a = hx(c); return rgb([a[0] * (1 - f), a[1] * (1 - f), a[2] * (1 - f)]); }

  /* ---- 決め打ちの差し色(人ごとに変わらない) ---- */
  var WH = '#f3f0e7', CR = '#e7e0cc', BK = '#2b2b31', GD = '#e0b24a', PK = '#e58aa8',
      RD = '#c4443a', DN = '#4a6f9a', KH = '#8a8256', SV = '#b9bcc2', BR = '#8a6240', GR = '#5f8f63';

  /* ---- 胴の形を測る小道具 ----
     台形の上辺は幅 0.74r、下辺は 1.16r。t=0で肩、t=1で下端。 */
  function T(shY, bot, t) { return shY + (bot - shY) * t; }
  function HW(r, t) { return r * (0.74 + 0.42 * t); }

  /* 胴をまるごと塗る(下の服を完全に隠す。どの衣装も最初にこれを呼ぶ) */
  function base(g, x, shY, bot, r, col) {
    g.fillStyle = col;
    g.beginPath();
    g.moveTo(x - r * 0.74, shY); g.lineTo(x + r * 0.74, shY);
    g.lineTo(x + r * 1.16, bot); g.lineTo(x - r * 1.16, bot);
    g.closePath(); g.fill();
  }
  /* 胴の横一段ぶんを別の色で塗る(胸の切り替え・帯・ベルトに使う) */
  function panel(g, x, shY, bot, r, t0, t1, col) {
    var y0 = T(shY, bot, t0), y1 = T(shY, bot, t1), w0 = HW(r, t0), w1 = HW(r, t1);
    g.fillStyle = col;
    g.beginPath(); g.moveTo(x - w0, y0); g.lineTo(x + w0, y0);
    g.lineTo(x + w1, y1); g.lineTo(x - w1, y1); g.closePath(); g.fill();
  }
  /* 腰から下を広がるスカートにする(flare=下辺の広さ・drop=下端より下へ出す量) */
  function skirt(g, x, shY, bot, r, t, col, flare, drop) {
    var y0 = T(shY, bot, t), y1 = bot + r * (drop == null ? 0.5 : drop);
    var w0 = HW(r, t), w1 = r * (flare == null ? 1.45 : flare);
    g.fillStyle = col;
    g.beginPath(); g.moveTo(x - w0, y0); g.lineTo(x + w0, y0);
    g.lineTo(x + w1, y1); g.lineTo(x - w1, y1); g.closePath(); g.fill();
  }
  /* ふわふわの縁(丸を横に並べる) */
  function frill(g, x, y, halfW, rad, col, n) {
    g.fillStyle = col;
    for (var i = 0; i < n; i++) {
      g.beginPath(); g.arc(x - halfW + (i + 0.5) * (halfW * 2 / n), y, rad, 0, 7); g.fill();
    }
  }
  /* たての筋(プリーツ) */
  function pleat(g, x, y0, y1, hw0, hw1, n, col, w) {
    g.strokeStyle = col; g.lineWidth = w || 0.9;
    for (var i = 1; i < n; i++) {
      var f = i / n * 2 - 1;
      g.beginPath(); g.moveTo(x + hw0 * f, y0); g.lineTo(x + hw1 * f, y1); g.stroke();
    }
  }
  /* よこの筋(編み目・ダウンの段) */
  function rib(g, x, shY, bot, r, t0, t1, n, col, w) {
    g.strokeStyle = col; g.lineWidth = w || 0.9;
    for (var i = 0; i < n; i++) {
      var t = t0 + (t1 - t0) * (i + 0.5) / n, y = T(shY, bot, t), hw = HW(r, t);
      g.beginPath(); g.moveTo(x - hw, y); g.lineTo(x + hw, y); g.stroke();
    }
  }
  /* リボン */
  function bow(g, cx, cy, s, col) {
    g.fillStyle = col;
    g.beginPath(); g.moveTo(cx, cy); g.lineTo(cx - s, cy - s * 0.72); g.lineTo(cx - s, cy + s * 0.72); g.closePath(); g.fill();
    g.beginPath(); g.moveTo(cx, cy); g.lineTo(cx + s, cy - s * 0.72); g.lineTo(cx + s, cy + s * 0.72); g.closePath(); g.fill();
    g.beginPath(); g.arc(cx, cy, s * 0.36, 0, 7); g.fill();
  }
  /* Vに開いた胸元 */
  function vneck(g, x, shY, r, col, dep) {
    g.fillStyle = col;
    g.beginPath(); g.moveTo(x - r * 0.42, shY); g.lineTo(x + r * 0.42, shY);
    g.lineTo(x, shY + r * (dep || 0.62)); g.closePath(); g.fill();
  }
  /* 丸い襟 */
  function rcollar(g, x, shY, r, col) {
    g.fillStyle = col;
    g.beginPath(); g.arc(x - r * 0.30, shY + r * 0.02, r * 0.30, 0, 7); g.fill();
    g.beginPath(); g.arc(x + r * 0.30, shY + r * 0.02, r * 0.30, 0, 7); g.fill();
  }
  /* 合わせの襟(着物・道着) */
  function xcollar(g, x, shY, bot, r, col) {
    var yb = T(shY, bot, 0.42);
    g.fillStyle = col;
    g.beginPath(); g.moveTo(x - r * 0.66, shY); g.lineTo(x - r * 0.34, shY); g.lineTo(x + r * 0.16, yb); g.lineTo(x - r * 0.10, yb); g.closePath(); g.fill();
    g.beginPath(); g.moveTo(x + r * 0.66, shY); g.lineTo(x + r * 0.34, shY); g.lineTo(x - r * 0.16, yb); g.lineTo(x + r * 0.10, yb); g.closePath(); g.fill();
  }
  /* 前を留めるボタン */
  function buttons(g, x, shY, bot, r, col, n, t0, t1) {
    g.fillStyle = col;
    for (var i = 0; i < n; i++) {
      var t = (t0 == null ? 0.28 : t0) + ((t1 == null ? 0.9 : t1) - (t0 == null ? 0.28 : t0)) * (n === 1 ? 0.5 : i / (n - 1));
      g.beginPath(); g.arc(x, T(shY, bot, t), r * 0.075, 0, 7); g.fill();
    }
  }
  /* ポケット */
  function pocket(g, cx, cy, w, h, col) {
    g.fillStyle = col; g.fillRect(cx - w / 2, cy, w, h);
  }
  /* パーカーのフード(首の後ろのふくらみ) */
  function hood(g, x, shY, r, col) {
    g.fillStyle = col;
    g.beginPath(); g.ellipse(x, shY + r * 0.24, r * 1.00, r * 0.66, 0, Math.PI, Math.PI * 2); g.closePath(); g.fill();
    g.fillStyle = dark(col, 0.18);
    g.beginPath(); g.ellipse(x, shY + r * 0.26, r * 0.66, r * 0.44, 0, Math.PI, Math.PI * 2); g.closePath(); g.fill();
  }
  /* 肩から吊る紐(サロペット・キャミ) */
  function straps(g, x, shY, bot, r, col, w) {
    g.strokeStyle = col; g.lineWidth = w || r * 0.13;
    g.beginPath(); g.moveTo(x - r * 0.55, shY); g.lineTo(x - r * 0.34, T(shY, bot, 0.36)); g.stroke();
    g.beginPath(); g.moveTo(x + r * 0.55, shY); g.lineTo(x + r * 0.34, T(shY, bot, 0.36)); g.stroke();
  }
  /* 前を開けた上着(中が見える) */
  function openFront(g, x, shY, bot, r, outer, inner) {
    base(g, x, shY, bot, r, inner);
    g.fillStyle = outer;
    g.beginPath(); g.moveTo(x - r * 0.74, shY); g.lineTo(x - r * 0.14, shY);
    g.lineTo(x - r * 0.30, bot); g.lineTo(x - r * 1.16, bot); g.closePath(); g.fill();
    g.beginPath(); g.moveTo(x + r * 0.74, shY); g.lineTo(x + r * 0.14, shY);
    g.lineTo(x + r * 0.30, bot); g.lineTo(x + r * 1.16, bot); g.closePath(); g.fill();
  }

  /* ================= 特別衣装 51種 ================= */
  var COSTUMES = [
    /* 何も着せない(買う前の姿) */
    { id: 'plain', nm: 'そのまま', price: 0, tone: 'neutral', draw: function () { } },

    /* ---- かわいい・女性向け寄り(35種) ---- */
    { id: 'frill', nm: 'フリル', price: 180, tone: 'cute', draw: function (g, x, shY, bot, r, col) {
      base(g, x, shY, bot, r, col);
      skirt(g, x, shY, bot, r, 0.44, col, 1.42, 0.42);
      var f = lite(col, 0.55);
      /* 下半分に明るい布を重ね、その縁を丸く波打たせる */
      g.fillStyle = f;
      g.beginPath();
      g.moveTo(x - r * 1.16, T(shY, bot, 0.74)); g.lineTo(x + r * 1.16, T(shY, bot, 0.74));
      g.lineTo(x + r * 1.42, bot + r * 0.42); g.lineTo(x - r * 1.42, bot + r * 0.42); g.closePath(); g.fill();
      frill(g, x, T(shY, bot, 0.74), r * 1.16, r * 0.15, f, 5);
      frill(g, x, bot + r * 0.42, r * 1.42, r * 0.17, f, 7);
      frill(g, x, T(shY, bot, 0.10), HW(r, 0.10), r * 0.11, lite(col, 0.78), 5);
    } },
    { id: 'maid', nm: 'メイド服風', price: 320, tone: 'cute', draw: function (g, x, shY, bot, r, col) {
      base(g, x, shY, bot, r, dark(col, 0.62));
      skirt(g, x, shY, bot, r, 0.50, dark(col, 0.62), 1.48, 0.46);
      /* 白いエプロン */
      g.fillStyle = WH;
      g.beginPath(); g.moveTo(x - r * 0.34, T(shY, bot, 0.16)); g.lineTo(x + r * 0.34, T(shY, bot, 0.16));
      g.lineTo(x + r * 0.86, bot + r * 0.36); g.lineTo(x - r * 0.86, bot + r * 0.36); g.closePath(); g.fill();
      rcollar(g, x, shY, r, WH);
      frill(g, x, bot + r * 0.46, r * 1.44, r * 0.16, WH, 7);
      panel(g, x, shY, bot, r, 0.50, 0.58, WH);
      bow(g, x, T(shY, bot, 0.20), r * 0.26, PK);
    } },
    { id: 'idol', nm: 'アイドル風', price: 360, tone: 'cute', draw: function (g, x, shY, bot, r, col) {
      base(g, x, shY, bot, r, lite(col, 0.42));
      skirt(g, x, shY, bot, r, 0.52, WH, 1.52, 0.30);
      pleat(g, x, T(shY, bot, 0.52), bot + r * 0.30, HW(r, 0.52), r * 1.52, 6, '#c9c4b4', 1);
      panel(g, x, shY, bot, r, 0.48, 0.56, GD);
      /* 肩のふくらみ */
      g.fillStyle = WH;
      g.beginPath(); g.arc(x - r * 0.72, shY + r * 0.06, r * 0.24, 0, 7); g.fill();
      g.beginPath(); g.arc(x + r * 0.72, shY + r * 0.06, r * 0.24, 0, 7); g.fill();
      /* 胸の星 */
      g.fillStyle = GD;
      g.beginPath();
      for (var i = 0; i < 10; i++) {
        var a = -Math.PI / 2 + i * Math.PI / 5, rr = r * (i % 2 ? 0.10 : 0.24);
        if (i === 0) g.moveTo(x + Math.cos(a) * rr, T(shY, bot, 0.26) + Math.sin(a) * rr);