2016/01/06

チップジャーからボードに連絡

新発売!
タッチでログイン/ログアウトするチップジャーですが、スタッフボードと連動する親切設計です(^^)v
つまり、スタッフボードに表示されているトータルの入金金額に、チップジャーに入金された額も加算されるのです。

チップジャー側では、入金額をログインしているスタッフに分配するとき(あ、何人もログインできます!)、誰にいくら入ったかメッセージを送ります。
スタッフボード側ではそのメッセージを聞いて、自分への入金ならその額をトータル金額に加えるわけです。
(ただしチップジャーとスタッフボードは同じSIM内にないと、メッセージは届きません。)

スタッフボード側のスクリプトはこんな感じ:
default {
  state_entry() { // スタッフIDとトータル金額がオブジェクトの「説明」に書いてある
    list info = llParseString2List(llGetObjectDesc(),[" "],[]);
    Id = llList2Key(info,0);
    if (Id) llListen(Channel,"",NULL_KEY,""); // メッセージを聞く
  }
  listen(integer ch,string na,key id,string msg) {
    list add = llParseString2List(msg,["+"],[]);
    if (Id != llList2Key(add,0)) return; // 他人へのメッセージは無視
    list info = llParseString2List(llGetObjectDesc(),[" "],[]);
    integer total = llList2Integer(info,1)+llList2Integer(add,1); // 加算
    llSetObjectDesc((string)Id + " " + (string)total); // 「説明」を書き直す
    llMessageLinked(LINK_THIS,Channel,"Refresh?",NULL_KEY);
  } // "Refresh?"でほかのスクリプトに表示を更新してもらう
}
チップジャーは、「MyTipjar」という名前で発売!

スタッフボードは、以前から売っている「MyStaffBoard」にこの加算機能をつけてバージョンアップしました。
すでにスタッフボードをお買い上げのお客様は、無償でバージョンアップしますので、lovemax までご連絡ください。

築地のお店では、デモ用のボードとチップジャーを置いてますので、いろいろ試してみてください。

SLマーケットプレイス
https://marketplace.secondlife.com/p/lovemax-MyTipjar/8322026
https://marketplace.secondlife.com/p/lovemax-Online-Tip-Board-MyStaffBoard/5177159
築地場外市場(SLurl)
http://slurl.com/secondlife/TSUKIJI/80/55/22/

2015/12/04

ランダムアニメーション

装着するとランダムにアニメーションします。
友だちが「どこでもダンス」とかで、アニメーションをメニューでいちいち選ぶのはめんどう、というので作ってみました(^^)/

オブジェクトを装着するとすぐアニメーションがはじまり、一定時間ごとに切り替わります。
はずせば終了。
とっても簡単です。
(最初はアニメーションを許すか聞いてきますが、次からはそれもなし)

スクリプトはこんな感じ:
default {
  attach(key id) {
    if (id) { // アバターに装着
      Id = id;
      Attach = TRUE;
      llSetTimerEvent(CycleTime); // 時間切替をセット
    }
    else Attach = FALSE; // アバターからはずれた
    // アニメーション許可申請
    llRequestPermissions(Id,PERMISSION_TRIGGER_ANIMATION);
  }
  timer() { // 一定時間ごとに切替
    llRequestPermissions(Id,PERMISSION_TRIGGER_ANIMATION);
  }
  run_time_permissions(integer perms) {
    if (perms & PERMISSION_TRIGGER_ANIMATION) { // アニメーション許可
      if (Anim) llStopAnimation(Anim); // 前のアニメをストップ
      if (Attach) { // アバターに装着されてれば
        integer n = llList2Integer(Nums,Index); // アニメ番号
        Anim = llGetInventoryName(INVENTORY_ANIMATION,n);
        llStartAnimation(Anim); // 新しいアニメをスタート
        if (NumAnims <= ++Index) { // インデックスを増やして超過したら
          Index = 0;
          Nums = llListRandomize(Nums,1); // アニメ番号を並び替え
        }
      }
      else llResetScript(); // アバターからはずれた
    }
  }
}
llListRandomize() という関数は、リストの内容をランダムに並び替えます。
アニメを毎回ランダムに選んでもいいのですが、それだと短い間に同じものが何度もでてくることもあるので、順番をバラバラにした番号リストを一巡して、また順番を並び替えて、というふうにやるとまんべんなくしかもランダムに選べます。

2015/07/15

どちらにも開くドア

タッチするか、ぶつかると、「反対側に」開くドアです。
SLでドアが開くとき、うっかり近すぎていると、手前に開いたドアが自分のカラダを通過したりしますね。
べつに実害はないのですが、なんとなく気持ちよくなかったりもします(^^;;

そこで、必ず「向こうに」開くドアです。
タッチした、またはぶつかった人の位置が、ドアの外側ならドアは内開き、人が内ならドアは外開きに開くようにします。

人がどちら側にいるかを調べるのには「ベクトルの内積」というのを使います。
数学の難しい話は抜きにして、2つのベクトルの内積を計算すると、ベクトルが同じ向きのときにプラスの値になり、反対向きのときマイナスになります。(直角のときはゼロ)
なので、あらかじめドアの「外向き」ベクトルを決めておけば、ドアから見た人の方向ベクトルとの内積が、プラスなら「外にいる」→「内開き」、マイナスなら「内にいる」→「外開き」のようにできます。

スクリプトはこんな感じ:
swing(vector pos) {
  if (pos) { // 開く
    Rot = llGetLocalRot(); // 現在の回転(子プリムなら親に対する)を記録
    if (0 < (Face*llGetRot())*(pos-llGetPos())) // 方向ベクトルの内積
      llSetLocalRot(llEuler2Rot(Angle*DEG_TO_RAD)*Rot); // (+)なら内開き
    else llSetLocalRot(llEuler2Rot(-Angle*DEG_TO_RAD)*Rot); // (-)なら外開き
  }
  else llSetLocalRot(Rot); // 閉まる
}

default {
  touch_start(integer n) { // タッチで開く
    swing(llDetectedPos(0));
    state open;
  }
  collision_start(integer n) { // ぶつかって開く
    swing(llDetectedPos(0));
    state open;
  }
}
state open { // 開いた状態
  state_entry() { // 時間設定
    llSetTimerEvent(TimeOut);
  }
  timer() { // 時間で閉まる
    swing(ZERO_VECTOR);
    llResetScript();
  }
  touch_start(integer n) { // タッチで閉まる
    swing(ZERO_VECTOR);
    llResetScript();
  }
}
このドアは、「MySwingDoor」という名前で売ってます:
上のスクリプトでは省略しましたが、両扉のときもう片方が連動するしかけにもなってます。
SLマーケットプレイス
https://marketplace.secondlife.com/p/lovemax-MySwingDoor/7433954
築地場外市場(SLurl)
http://slurl.com/secondlife/TSUKIJI/80/55/22/

2015/05/05

簡単登録スタッフボードを改造

超簡単登録の「スタッフボード」を、ちょっと改造しました。
  1. 「Online/Offline」表示部分を、今まで写真の下だったのを、表裏で上下どちらにも使えるようにしました。
    (このためのプリムの変形で、ちょっとだけ不便なことになって、このボードを床に置くと、下の部分が少しもぐってしまうので、あとで持ち上げてください。(^^;)

  2. 自動で設定されるプロフ写真を別の写真に張り替えたいとき、今までは張る面を選ばないと「Online/ Offline」表示の部分も写真になって、「し、しまったぁ~~><」となったのですが、もう大丈夫。
    新しいボードでは、何も考えずに全体に写真を張れば、すかさず自動で「Online/Offline」表示をもとに戻します。v(^^)v

「Online/Offline」表示部分を戻すスクリプトはこんな感じ:
default {
  changed(integer change) {
    if (change & CHANGED_TEXTURE) // テキスチャーが変わった
      // (list Faces = [Image,Blank,OnOff,...] 各面の用途を設定してあるリスト)
      integer i = llListFindList(Faces,[OnOff]); // 最初のOnOff表示面
      if (OnOffTexture != llGetTexture(i)) { // 違う画像が貼ってある
        for (; i<NumSides; ++i)
          if (OnOff==llList2Integer(Faces,i))
            llSetTexture(OnOffTexture,i); // OnOff表示面復旧
        for (i=0; i<NumSides; ++i)
          if (Blank==llList2Integer(Faces,i))
            llSetTexture(TEXTURE_BLANK,i); // 空白面復旧
      }
    }
  }
}
張りなおすときにOnOff表示面と空白面を分けて張るのは、まず最初にOnOff表示面を直しておけば、それでまた changed イベントが発生しても、また張りなおしたりにはならない、と思ったからです。

このスタッフボードは、「MyStaffBoard」という名前で売ってます:
SLマーケットプレイス
https://marketplace.secondlife.com/p/lovemax-Online-Tip-Board-MyStaffBoard/5177159
築地場外市場(SLurl)
http://slurl.com/secondlife/TSUKIJI/80/55/22/

2014/09/14

デモ機を自動で出す(Rez)

築地のデモ用飛行船ですが、遊覧飛行に出かけると、あとに新しいのを自動で出す(Rez)ようになってます。
デモ機のほうは一時オブジェクトになって遊覧飛行のあと降りると消えるので、もとの場所に戻す必要がない、とても親切な設計ですね(^^)

このしかけは、デモが始まるとき、デモ機はキーワード "Demo Start" を言うので、これをきっかけに周辺をスキャンしてデモ機がいるかどうか調べ、いなくなったら、中に保管してある新しいデモ機を出す(Rez)のです。
どこに出すかは、あらかじめ所定の位置に置いたデモ機を一度スキャンして調べます。

築地のお店では、このしかけを飛行船に昇る階段(踏み板)に仕込んであります。
薄~い踏み板の中に、この大きさのデモ飛行船を保管しているわけですね~(^^)
default {
  state_entry() {
    Object = llGetInventoryName(INVENTORY_OBJECT,0);
    if (Object) { // 中に保管しているオブジェクト(デモ機)の名前で
      llListen(Channel,Object,NULL_KEY,"Demo Start"); // キーワードを待つ
      llSensor(Object,NULL_KEY,SCRIPTED,Range,PI); // 位置記録のため一度スキャン
    }
  }
  listen(integer chan,string name,key id,string msg) { // キーワードを聞いたら
    llSensorRepeat(Object,NULL_KEY,SCRIPTED,Range,PI,Cycle); // 繰返しスキャン
  }
  sensor(integer num) { // スキャン範囲内にオブジェクトがあって
    if (Pos==ZERO_VECTOR) { // 位置の記録がまだないとき(最初のスキャン)
      Pos = llDetectedPos(0); // 位置と回転を記録
      Rot = llDetectedRot(0);
    } // (繰返しスキャン中、近くにいるときは何もしない)
  }
  no_sensor() { // スキャン範囲からいなくなったら
    if (Pos) {
      llRezAtRoot(Object,Pos,ZERO_VECTOR,Rot,0); // オブジェクトをRez
      llSensorRemove(); // 繰返しスキャンを停止
    }
  }
}

2014/09/13

階段を昇らずにジャンプ

SL内の階段やはしごには、歩いて昇るのが面倒な省エネタイプの人のために、簡単なジャンプをしかけておくと親切です。
このジャンプは「座る」位置を設定して、誰か座った人を目的の場所に立たせるようにします。
省エネの徹底には「メニューから選ぶ」のも省きたいので、「(左)クリックで座る」設定にします。

目的位置の設定も簡単にするため、所有者がそこに立ってタッチするとその位置を記録するようにします。
もしすでに設定済みの場合、普通のタッチ(左クリック)では座ってしまうので、右クリックでメニューを開いてタッチを選び、リセットで全部解除してから、もう一度目的位置に立ってタッチしなおします。

llSitTarget() で位置と向き(回転)を設定しますが、プリムから見た相対的(ローカル)な位置と回転にするため、タッチしたアバターの位置からプリムの位置を差し引き、プリムの回転で割ります。(回転は「引く」のでなく「割る」のだそうです)
default {
  state_entry() {
    llSitTarget(ZERO_VECTOR,ZERO_ROTATION); // 座る位置設定解除
    llSetClickAction(CLICK_ACTION_NONE); // 左クリックの動作設定解除
  }
  touch_start(integer num) {
    if (llDetectedKey(0)==llGetOwner()) { // 所有者だけが設定できる
      if (Target) llResetScript(); // 設定済みならリセット
      vector pos = llDetectedPos(0); // 位置
      rotation rot = llDetectedRot(0); // 回転(向き)
      pos = (pos-llGetPos())/llGetRot(); // ローカル座標に変換
      rot /= llGetRot();
      llSitTarget(pos,rot); // 座る位置と回転を設定
      llSetClickAction(CLICK_ACTION_SIT); // 左クリックの動作「座る」
      Target = TRUE; // 設定が済んだしるし
    }
  }
  changed(integer change) {
    if (change & CHANGED_LINK) { // 座るとリンクが変わる
      key id = llAvatarOnSitTarget();
      if (id) { // アバターが座ったら
        llSleep(0.7); // 落ち着くまで待って
        llUnSit(id); // 立たせる
      }
    }
  }
}

2014/09/05

お店をぐらっと傾けて?

いつもどおりヒマ~な築地のお店なので、気分転換にちょっと模様替えをしました。
床しかなくて全然店っぽくなかったので、レンガの壁を立てると…、いまいち普通っぽすぎるので、それをぐらっと傾けて…。
やっぱり店っぽくならないです…(^^;

築地場外市場(SLurl)
http://slurl.com/secondlife/TSUKIJI/80/55/22/