HSPポータル
サイトマップ お問い合わせ


HSPTV!掲示板


未解決 解決 停止 削除要請

2025
0304
アキアキノヒロロ"mod_vpad.as"によるバーチャルパッドを2つ表示し、作動させる方法は?13解決


アキアキノヒロロ

リンク

2025/3/4(Tue) 11:07:24|NO.103201

2つ目を表示させるには、[vpad.png] を [gmode 3] [gcopy] でコピー表示し、
作動させるには、クリックの座標取得によるしかないのでしょうか。
"mod_vpad.as"によって、2つ目も表示作動させることはできないのでしょうか。
それが可能な方法があるなら、教えていただきたいです。



この記事に返信する


ze-na

リンク

2025/3/4(Tue) 22:47:57|NO.103203

>アキアキノヒロロ さん

3.7b5以降で追加された公式のVPADモジュールのことですね。

このbeta版モジュールは共通化(Dish以外にも対応)したことによる
入力制限があるためキー入力前提のシングルタップ(?)VPADとなっているようです。

これでは使いにくいのでちょっと改良してみました。
2つ目の表示と動作に対応したのでよろしければ使ってみて下さい。

■mod_vpad_dish.as
・ベースモジュールはmod_vpad.as
・Dish限定モジュール化によるマルチタップ対応
・サムエリアによる複数ボタン押しへの対応
・ダブルパッド表示(A/W/D/S)対応(hspvpad_init p2=8)
・入力エリアの表示対応(hspvpad_init p2=64)

;---------------------------------------------------------------- ; バーチャルパッドモジュール / onitama 2023 ; 使用に関する制限はありません。ご自由にお使いください。 ; mtinfo+thumb area+wpad(hsp3dish only) / ze-na 2025 ;---------------------------------------------------------------- #include "hsp3dish.as" #module "_vpad_dish" #deffunc hspvpad_init int _p1, int _p2, int _p3, int _p4 ; バーチャルパッドの初期化 ; hspvpad_init p1,p2 ; p1 : パッド画像を格納するバッファID ; p2 : パッド配置(1=左PAD/2=上PAD/4=移動のみ) ; 8=ダブルPAD/64=入力表示 ; p3 : X方向オフセット ; p4 : Y方向オフセット ; vpbuf=_p1 if vpbuf<=0 : vpbuf=ginfo(25) celload "vpad.png",vpbuf celdiv vpbuf,64,64 vppos=_p2 sx=ginfo_sx:sy=ginfo_sy vpadx=32:if vppos&1 : vpadx=sx-223 i=22:if vppos&2 : i=4 vpady=sy*i/30 vpadx += _p3 : vpady += _p4 ; vpkey=0 vpmax=0 dim vp_key,16 dim vp_id,16 dim vp_x,16 dim vp_y,16 ; if (vppos&8)=0 { hspvpad_addkey vpadx,vpady,1,7 hspvpad_addkey vpadx+128,vpady,4,5 hspvpad_addkey vpadx+64,vpady-64,2,4 hspvpad_addkey vpadx+64,vpady+64,8,6 }else{ hspvpad_addkey vpadx,vpady,16384,7 hspvpad_addkey vpadx+128,vpady,65536,5 hspvpad_addkey vpadx+64,vpady-64,32768,4 hspvpad_addkey vpadx+64,vpady+64,131072,6 } ; vpadx=sx-223:if vppos&1 : vpadx=32 ; if (vppos&4)=0 { if (vppos&8)=0 { hspvpad_addkey vpadx,vpady,16,3 hspvpad_addkey vpadx+128,vpady,2048,0 hspvpad_addkey vpadx+64,vpady-64,4096,2 hspvpad_addkey vpadx+64,vpady+64,8192,1 }else{ hspvpad_addkey vpadx,vpady,1,7 hspvpad_addkey vpadx+128,vpady,4,5 hspvpad_addkey vpadx+64,vpady-64,2,4 hspvpad_addkey vpadx+64,vpady+64,8,6 } } ; return #deffunc hspvpad_addkey int _p1, int _p2, int _p3, int _p4 ; ボタンの配置(内部用) vp_x(vpmax)=_p1 vp_y(vpmax)=_p2 vp_key(vpmax)=_p3 vp_id(vpmax)=_p4 vpmax++ return #deffunc hspvpad_set int _p1, int _p2, int _p3 ; バーチャルパッドのボタン位置調整 ; hspvpad_set p1,p2,p3 ; p1 : ボタンID(0=上/1=下/2=左/3=右/4=チェック/5=〇/6=△/7=×) ; p2 : X位置(絶対座標) (マイナス値の場合は表示なし) ; p3 : Y位置(絶対座標) ; vp_x(_p1)=_p2 vp_y(_p1)=_p3 return #deffunc hspvpad_stick int _p1, int _p2 ; バーチャルパッドのキー置き換え ; hspvpad_stick p1,p2 ; p1 : ボタンID(0=上/1=下/2=左/3=右/4=チェック/5=〇/6=△/7=×) ; p2 : キー値(stick命令のキー情報値) ; vp_key(_p1)=_p2 return #deffunc hspvpad_key var _p1, int _p2 ; バーチャルパッドのキー処理 ; hspvpad_key var ; var = stickで値を取得した変数名 ; mtlist touchid:num=stat repeat num id=touchid(cnt):mtinfo touch,id t=touch(0):x=touch(1):y=touch(2) if t{ repeat vpmax x1=vp_x(cnt) if x1>=0 { x1+=32:y1=vp_y(cnt)+32 if powf(x1-x,2)+powf(y1-y,2)<=powf(64,2) { _p1|=vp_key(cnt) } } loop } loop vpkey=_p1 return #deffunc hspvpad_put int _p1, int _p2 cx=ginfo_cx:cy=ginfo_cy rgb=(ginfo_r*65536)+(ginfo_g*256)+ginfo_b ; バーチャルパッドの表示処理 ; hspvpad_put p1 ; p1 : 強制ONを行うkey値 ; i=vpkey|_p1 gmode 3,,,128 repeat vpmax x=vp_x(cnt) if x>=0 { pos x,vp_y(cnt) j=vp_id(cnt):if i&vp_key(cnt) : j+=8 celput vpbuf,j if (j>=8)&((vppos&64)!0){ rgbcolor $ff circle x,vp_y(cnt),x+64,vp_y(cnt)+64,0 } } loop if vppos&64{ mtlist touchid:num=stat repeat num id=touchid(cnt):mtinfo touch,id t=touch(0):x=touch(1):y=touch(2) if t{ rgbcolor $ffff:circle x-32,y-32,x+32,y+32,0 pos x-32,y-48:mes strf("#%02d",id+1) } loop } rgbcolor rgb:pos cx,cy return #global



ze-na

リンク

2025/3/5(Wed) 01:30:29|NO.103204

>アキアキノヒロロ さん

サンプルスクリプト:vpadmove.hspをmod_vpad_dish.asに差し替えて
ダブルパッド対応したものをWebDishサービスに置いておきます。
質問にあります2つ目の表示と作動についてご確認下さい。

https://dev.onionsoft.net/seed/webdish/8bbd1d6bbefb6a195ef3da0a8da8a446.html

スマホで確認したところ入力表示が指で見えなかったのでmod_vpad_dish.asの入力表示を以下に変更しています。

(156行目のif条件内の処理を以下に変更) rgbcolor $ffff:circle x-32,y-32,x+32,y+32,0 rgbcolor $ffff00:circle x-64,y-64,x+64,y+64,0 pos x-64,y-64:mes strf("#%02d",id+1)
WebDish上のhsp3dish.jsのバージョンによりますが、
どなたかの書き込みでmtinfoのXY座標がズレる問題があると言っていたので、
それが再現する可能性があります。

それとダブルパッド機能のためにキーボードにてstick命令で入力する際には
4キー以上の同時入力(斜め入力2つ)に対応していることが前提となります(Nキー機能)
mod_vpad_dish.asのマルチタッチ入力によるキー値にはこの影響はありません。



しまくろねこ

リンク

2025/3/5(Wed) 04:22:58|NO.103206

場違いな書き込みだったようでしたので、書き込み削除しました。
失礼しました。



アキアキノヒロロ

リンク

2025/3/5(Wed) 11:09:13|NO.103208

ご返事が遅れました。
しまくろねこさん、ze-naさん、ありがとうございました。

しまくろねこさん、全く関係がないわけでなく、場違いな書き込みではなかったと思います。
しまくろねこさんのものも、そして窓月ららさんのものも、ダウンロードさせて頂きました。
やはり同じようなことを思い、それを実現している方がおられるのを知れただけでも収穫でした。
まだ、よく理解しきれても使いきれてもいませんが、参考になることが多々あると感じました。
ありがとうございました。

ただ、今回の自分の目的により一層沿っているのは、ze-naさんのもののようです。
色々自分なりに工夫して取り入れてみようと考えています。

最初、自分が感じたのは、"mod_vpad.as"をコピーして、変数名を変えて、
".as"の名前も、例えば"mod_vpad_new.as"として、それを
> #include "mod_vpad.as"
> #include "mod_vpad_new.as"
のような形にすれば、どうにかなるんじゃないか、などと簡単に考えていましたが、上手くいかず、
> 2つ目を表示させるには、[vpad.png] を [gmode 3] [gcopy] でコピー表示し、
> 作動させるには、クリックの座標取得によるしかない
と諦め、そうしようと始めましたが、いい加減、面倒になってきたので、
もしかして、そういうモジュールがすでに作られているんじゃないか、と思い、
ここに質問してみたわけです。

解決としていいかとも思いますが、実際に自分のものに取り入れて、
その成功を待って、解決としたいと思います。



窓月らら

リンク

2025/3/5(Wed) 11:09:52|NO.103209

全くの横からのどうでもよさそうな情報ですが、
うちが2015年に作成したモジュールのファイル名とまったく同じで申し訳ありませんww
いちおううちの方が先なんですがw
というわけで、そのうちVer2にして名前(モジュール名)は変えときます。。



しまくろねこ

リンク

2025/3/5(Wed) 11:35:48|NO.103210

>窓月ららさん
公式に同じ名前のものがあったのですね。
てっきり窓月ららさんのものだと思ってしまいました。
失礼しました。



アキアキノヒロロ

リンク

2025/3/5(Wed) 20:48:32|NO.103213

ze-naさんの■mod_vpad_dish.as による、ダブルパッド表示、成功いたしました。
ze-naさん、有難うございます。

ライト、サウンド等、のテストも兼ねたものです。Xに動画(1.2倍速再生)載せました。
https://x.com/akiakinohiroro/status/1897249067203490165

Webアプリ、私のHPに載せました。
https://3dishjsaa.yu-yake.com/index_2.html
ここの「ライト色々テスト」です。

物理設定はしていないので、モデルたちは、衝突することなく、素通りしてしまいます。
あしからず。



アキアキノヒロロ

リンク

2025/3/5(Wed) 21:12:50|NO.103214

[data/] の問題では、ファイル名のみの指定ということですが、
何となく思っていたことを試してみたところ、上手くいってしまいました。

どういうことかというと、必要なファイルをどれも[res]フォルダに入れ、
[hsp]スクリプトでの読込み記述では、[res/]としました。
これで、[hsp]スクリプトの実行もできますし、
このまま、「HSP3dish helper」での変換もでき、Webアプリも成功しました。
ただし、[mod_vpad.as] で使っている [vpad.png] は、[data/] に入れないとダメなようです。



MIZUSHIKI

リンク

2025/3/5(Wed) 22:31:04|NO.103215

> ze-na さん

ze-naさんの mod_vpad_dish.as は公式のVPADモジュールをマルチタッチに対応したもので、アキアキノヒロロさんのように公式のVPADモジュールを使ったことのある方にはすんなり使えるので HSP3Dish.js に大変有用なものだと思いました。

ということで恐縮ながら、ezlocal-dish-js という私の作っているモジュールに同梱している「おすすめモジュール紹介ツール」で紹介させてもらうことにしました。
サンプルもze-naさんの作られたものに似せて作らせて頂きました。
(モジュール自体はこの掲示板のページから#netincludeという自作モジュールによって切り出される形で利用しています。)

以下はツールで使用する自動取得用のスクリプトですが、サンプルの雰囲気は以下リンク先の39行目からを見て貰えれば分かるかもしれません。
https://github.com/MIZUSHIKI/HSP-Module/blob/master/DishJsModSample/DishJsMod_mod_vpad_dish_sample.hsp

ze-naさん、大変便利なモジュールを開発して頂きありがとうございます。



ze-na

リンク

2025/3/6(Thu) 02:02:58|NO.103217

> アキアキノヒロロ さん

>ze-naさんの■mod_vpad_dish.as による、ダブルパッド表示、成功いたしました。

活用できたみたいでよかったです。

>ただし、[mod_vpad.as] で使っている [vpad.png] は、[data/] に入れないとダメなようです。

これはモジュール内のcelload "vpad.png",vpbufにres/パス指定することで解決します。

-----
> MIZUSHIKI さん

おにたま(管理人) さんの公式モジュールをちょっと改良しただけですので
紹介なり改良なり自由に使って頂いて構いません。
まあ、不具合が起きた時を考慮してコメントくらいは残して下さい。

放置するのも何なのでモジュールとサンプルをGitHubに上げておきました。
サンプルはWebDishサービスにUPするためhsptv素材の制約に対応しています。

https://github.com/ze-n-a/mod_vpad_dish

まあ時間あればキートリガー対応やら4方向8方向切替やら対応したかった気もしますが。



hashikemu

リンク

2025/3/6(Thu) 03:03:43|NO.103218

なんかもう解決してそうですが、窓月ららさんのmod_vpad.asを改造して
マルチタッチ&複数スティックできるようにしたものを置いておきます



; ------------------------------------------------------------- ; mod_orvpad - virtual joystick module (based on mod_vpad.as by madotuki lala) ; author : 0rangetail hashikemu ; date : 2022-01-10 ; ------------------------------------------------------------- ; モバイルデバイス向け仮想ゲームパッドを実現するモジュール ; mod_vpadに対して、以下の違いがある ; ・2スティック以上に対応可能(ボタン、スティックどちらも10個まで可能) ; ・mod_keyInputと組み合わせる前提でキーボード入力を拾わない ; ------------------------------------------------------------- ; #define global VPAD_DEBUG1 #module mod_OrVpad ; スキン画像登録 #deffunc vpad_Init str fname, int divSizeIN, int gIDIN gID=gIDIN ; skin画像のID celload fname,gID ; skin画像をロード divSize=divSizeIN celdiv gID,divSize,divSize,divSize/2,divSize/2 vpad_InitState ; 変数初期化 return ; 内部状態初期化 #deffunc vpad_InitState bMax = 0 ; ボタン最大数 sMax = 0 ; スティック最大数 ; s:stick, b:button, p:point ; v:visual, a:actual, s:state dim sv,4,10 dim sa,8,10 ddim ssx,10 ddim ssy,10 dim ssIn,10 dim ssId,10 dim ssOrg,2,10 dim ssCnt,10 dim bv,4,10 dim ba,4,10 ddim bs, 10 dim p,4,10 ; sns:sensor, r:raw, c:calculated ddim snsr,6 ddim snsc,12 pMax=0 : pointID=0 return ; スティック作成 #deffunc vpad_Stick int centerX, int centerY, int radius, int margin, int celIdx sIdx=sMax: sMax++ ; 表示用座標、サイズ、セルインデックス sv(0,sIdx)=centerX : sv(1,sIdx)=centerY sv(2,sIdx)=radius : sv(3,sIdx)=celIdx ; 判定用AABB sa(0,sIdx)=centerX-(radius+margin): sa(1,sIdx)=centerY-(radius+margin) sa(2,sIdx)=centerX+(radius+margin): sa(3,sIdx)=centerY+(radius+margin) sa(4,sIdx)=centerX-(radius): sa(5,sIdx)=centerY-(radius) sa(6,sIdx)=centerX+(radius): sa(7,sIdx)=centerY+(radius) return ; ボタン作成 #deffunc vpad_Button int centerX, int centerY, int radius, int margin, int celIdx #const AD 4 ; アタリ判定を右下(+)または左上(-)にずらす量(px) bIdx=bMax: bMax++ ; 表示用座標、サイズ、セルインデックス bv(0,bIdx)=centerX+AD : bv(1,bIdx)=centerY+AD bv(2,bIdx)=radius : bv(3,bIdx)=celIdx ; 判定用AABB ba(0,bIdx)=centerX-(radius+margin): ba(1,bIdx)=centerY-(radius+margin) ba(2,bIdx)=centerX+(radius+margin): ba(3,bIdx)=centerY+(radius+margin) return ; タッチ情報取得、スティック/ボタン判定 #deffunc vpad_Update ; 初期化 wx=0.0: wy=0.0 dim p,4,10 ddim ssx,10 ddim ssy,10 dim ssId,10 dim ssCnt,10 dim ssInCnt,10 ddim bs, 10 ; ポイントIDリストを取得(statからタッチ数を取得) mtlist pointID : pMax = stat repeat pMax ; タッチ情報取得 pIdx=cnt mtinfo pInfo,pointID(pIdx) p(0,pIdx)=pInfo(1) : p(1,pIdx)=pInfo(2) p(2,pIdx)=pInfo(0) : p(3,pIdx)=pInfo(3) ; p=x,y,onoff,idの順で入る ; ポイントが各スティックのエリア内にある個数のカウンタを更新 repeat sMax sIdx=cnt if (p(0,pIdx)>sa(0,sIdx) & p(1,pIdx)>sa(1,sIdx) & p(0,pIdx)<sa(2,sIdx) & p(1,pIdx)<sa(3,sIdx)){ ssCnt(sIdx)++ } loop loop ; タッチされていたら判定 repeat sMax sIdx=cnt ; スティック判定 if (ssCnt(sIdx)==0){ ssIn(sIdx)=0 ssOrg(0,sIdx)=sv(0,sIdx)+100 ssOrg(1,sIdx)=sv(1,sIdx)+100 } loop repeat sMax sIdx=cnt repeat pMax pIdx=cnt switch ssIn(sIdx) case 0: ; no touchからtouchの遷移判定 if (p(0,pIdx)>sa(4,sIdx) & p(1,pIdx)>sa(5,sIdx) & p(0,pIdx)<sa(6,sIdx) & p(1,pIdx)<sa(7,sIdx)){ ; inner範囲内 ssIn(sIdx)=1 ssId(sIdx)=p(3,pIdx) ssOrg(0,sIdx)=p(0,pIdx) ssOrg(1,sIdx)=p(1,pIdx) } swbreak case 1: ; touchからno touchの遷移判定 if (p(0,pIdx)>sa(0,sIdx) & p(1,pIdx)>sa(1,sIdx) & p(0,pIdx)<sa(2,sIdx) & p(1,pIdx)<sa(3,sIdx)){ ssId(sIdx)=p(3,pIdx) ssInCnt(sIdx)++ }else{ if ssId(sIdx)!=p(3,pIdx) & ssInCnt(sIdx)==0{ ; outer範囲外の場合Inフラグをオフにする ssIn(sIdx)=0 ssOrg(0,sIdx)=sv(0,sIdx)-100 ssOrg(1,sIdx)=sv(1,sIdx)-100 } } swbreak swend ; スティックにタッチありかつ同一IDのポイントのみを使用して出力を更新 if ssIn(sIdx)==1 & ssId(sIdx)==p(3,pIdx){ dX=double(p(0,pIdx)-ssOrg(0,sIdx))/double(sv(2,sIdx)) dY=double(p(1,pIdx)-ssOrg(1,sIdx))/double(sv(2,sIdx)) dL=sqrt(dX*dX+dY*dY) if dL>1.0 : dX=dX/dL : dY=dY/dL ssx(sIdx)=dX ssy(sIdx)=-dY } loop loop repeat pMax pIdx=cnt ; ボタン判定 repeat bMax bIdx=cnt if p(0,pIdx)>ba(0,bIdx) & p(1,pIdx)>ba(1,bIdx) & p(0,pIdx)<ba(2,bIdx) & p(1,pIdx)<ba(3,bIdx) { bs(bIdx)=1.0 } loop loop ; スマホ搭載のセンサを取得する repeat 6 ; 0,1,2=ax,ay,az x+:左 y+:下 z+:奥 ; 3,4,5=gx,gy,gz snsr(cnt)=ginfo(256+cnt) snsc(cnt)=snsr(cnt) loop snsc(6) = atan(snsr(1),snsr(0)) ; ex:landscapeでのステアリング操作 snsc(7) = atan(snsr(2),snsr(1)) snsc(8) = atan(snsr(0),snsr(2)) return ; 情報取得 #defcfunc vpad_GetInfo str type, int idx if type=="x" : ret=ssx(idx) if type=="y" : ret=ssy(idx) if type=="b" : ret=bs(idx) if type=="s" : ret=snsc(idx) return ret ; コントローラ描画 #deffunc vpad_Draw ; debug(アタリ判定確認用) #ifdef VPAD_DEBUG1 color 0,160,160 repeat sMax : vpad_boxline sa(0,cnt),sa(1,cnt),sa(2,cnt),sa(3,cnt) : loop ; stick_outer repeat sMax : vpad_boxline sa(4,cnt),sa(5,cnt),sa(6,cnt),sa(7,cnt) : loop ; stick_inner repeat bMax : vpad_boxline ba(0,cnt),ba(1,cnt),ba(2,cnt),ba(3,cnt) : loop ; button #endif ; スティックとボタンを描画 gfilter 2 : gmode 2 ; スティック repeat sMax pos sv(0,cnt), sv(1,cnt) : celput gID, sv(3,cnt), 4.0*double(sv(2,cnt))/double(divSize),4.0*double(sv(2,cnt))/double(divSize) pos sv(0,cnt)+ssx(cnt)*double(sv(2,cnt)),sv(1,cnt)-ssy(cnt)*double(sv(2,cnt)) : celput gID, sv(3,cnt)+1, 2.0*double(sv(2,cnt))/double(divSize),2.0*double(sv(2,cnt))/double(divSize) loop ; ボタン repeat bMax pos bv(0,cnt), bv(1,cnt) : celput gID, bv(3,cnt)+int(bs(cnt)), 2.0*double(bv(2,cnt))/double(divSize),2.0*double(bv(2,cnt))/double(divSize) loop gfilter 0 ; repeat 10 ; color 0,0,0 ; pos p(0,cnt),p(1,cnt) ; mes ""+p(3,cnt) ; loop ; repeat sMax ; pos ssOrg(0,cnt),ssOrg(1,cnt) ; pvtMes "x" ; mes ssCnt(cnt) ; loop return ; デバッグ用判定範囲描画 #ifdef VPAD_DEBUG1 #deffunc vpad_boxline int _x1, int _y1, int _x2, int _y2 line _x1,_y1,_x2,_y1 ; 上 line _x1,_y2,_x2,_y2 ; 下 line _x1,_y1,_x1,_y2 ; 左 line _x2,_y1,_x2,_y2 ; 右 return #endif #global #define global DEFAULT_PADPAT (90)



アキアキノヒロロ

リンク

2025/3/6(Thu) 07:29:07|NO.103221

Webアプリがスマホでも成功しているので、一応解決といたしますが、
ここまでこのことが広がるとは思っていませんでした。
これからは、段々Webアプリの存在が大きくなってくる予感を
みなさんも感じてきていることもありそうですね。
そして、その簡便な操作方法の必要性もたかまってくるはずですし。

他のことでも色々お世話になっている、hashikemuさんまで加わっていただけて、
有難く思います。3DのWebアプリ、期待しております。

みなさんに感謝です。

私自身、公式のものを含めて、みなさんのモジュール等をしっかり理解しているわけでもないので、
解決とは致しますが、何かあれば、引き続きお寄せ下さい。



MIZUSHIKI

リンク

2025/3/6(Thu) 20:58:08|NO.103223

> ze-na さん
> 放置するのも何なのでモジュールとサンプルをGitHubに上げておきました。

GitHub にもアップして頂きありがとうございます!
自動取得用のスクリプトもGitHub側のモジュールをダウンロード指定するように変更させて頂きました。



記事削除

記事NO.パスワード
(質問が解決したスレッドは他の利用者に活用してもらうため、削除しないようお願いします)

NO.103201への返信

マスコット

好きなマスコットを選んでください。

名前

e-mail
HOME
  1. 初めて利用する方は、HSP3掲示板の使い方をお読みください。
  2. 不要部分の多い長いスクリプトの投稿は ご遠慮ください。
  3. 書き込みは自動改行されません。適度に改行を入れてください。
  4. スクリプトは小文字の<pre>〜</pre>で囲むと見やすく表示できます。

削除用パスワード

解決したら質問者本人がここをチェックしてください。

エラー発生時、再送信すると二重送信になることがあります。
回答が得られたら、お礼書き込み時に[解決]チェックしてください。
SPAM防止のためURLから始まる文章は投稿できません。
SPAM防止のため英文字のみの本文を投稿することはできません。

ONION software Copyright 1997-2025(c) All rights reserved.