CDW SAVE セーブ、LOAD ロードを実装した


2022年 7月 8日
 
 
 まだ完全では無いけど、セーブ、ロードを実装した。

 だいぶ時間がかかったけど、たしかに仕事しながらだと進まないというのもあるけど、セーブ機能も案外ややこしいので手間取った、というのもある。

 SAVE、LOAD機能を実装するくらいになると製作も終盤になる、なぜかと言うとセーブするデータがある程度そろわないと作りにくいから。
 
 

 今回のセーブデータは、システムデータ sys.dat、ゲームデータ data.dat、初期配置データ sdat.dat、リプレイデータ rep_data.datがある。
 
 
 セーブ初期

////////// save load //////////

*save_ini

	datb=1+1+3+1+1+1+1		; lx、scf、dbpl、mb、plb、pn、padn
	datb+=mb*2				; map、hwd
	datb+=lddb				; 領土
	datb+=(ldb+1)*ldlb+1	; 隣接国
	datb+=(ldb+1)*10
	datb+=(plb+1)*7
	datb+=21				; カラー

	dim dtf ,8    ; データフラグ
	dim dat ,datb ; セーブデータ
	dim sdat,datb ; 初期セーブデータ

	smpdb=mb*8+1 ; セーブマップ表示データ
	dim sys,30+smpdb+10 ; システムデータ


; 表示座標

	slwx1=22 : slwxn=62
	slwx2=slwx1+8
	slwx3=slwx1+(8*(slwxn-1))

	slwy1=106
	slwyk=103 ; slwyn=12
	dim slwy,9
	repeat 8
	 slwy(cnt+1)=slwy1+(slwyk*cnt)
	 ;slwy(cnt+1)=slwy1+(slwyn*8)*cnt
	loop
	slwy(8)=846


; キ-座標

	dim sl_kx1,17
	dim sl_kx2,17
	dim sl_ky1,17
	dim sl_ky2,17

	x=slwxn*8 : xk1=390 : xk2=x-xk1
	y=90 ; y=slwyn*8
	repeat 8,1
	 sl_kx1(cnt)  =slwx1
	 sl_kx2(cnt)  =slwx1+xk1
	 sl_kx1(cnt+8)=slwx1+xk1
	 sl_kx2(cnt+8)=slwx1+x

	 sl_ky1(cnt)  =slwy(cnt)
	 sl_ky2(cnt)  =slwy(cnt)+y
	 sl_ky1(cnt+8)=slwy(cnt)
	 sl_ky2(cnt+8)=slwy(cnt)+y
	loop

; 確認座標
	smx0=30	 : smy0=200
	smx1=26  : smy1=719
	smx2=277 : smy2=719

	return

 dtf データフラグは読み込みの際に、その番のデータがあるか。

 datがセーブするデータだけど、セーブの仕方がこれで良いのかどうか分からないけど、自分はベタに一つの変数 datに全部放り込んで bsaveする方法にしてる。

 効率は悪い(ややこしい)と思うけど、他に方法を知らない。

 セーブするデータの総量を算出して datbにし、dim dat,datbで定義してる。

 sdatは初期配置データで「始めからやりなおし RESTART」の時のデータ、まだ実装してない。

 sysはシステムデータ。
 
 
 システムデータセーブ

; システムデータ、セーブ
#deffunc sys_save

	sys(1)=bpwt1f	; Map display speed
	sys(2)=bpwt2f	; Battle display speed
	sys(3)=btl_putf	; 戦闘表示
	sys(4)=glv		; GAAME LEVEL
	sys(5)=cld(55)	; USER COLOR
	sys(6)=cld(56)
	sys(7)=cld(57)

; セーブマップ表示データ

	n=30+saven*55*hyb
	repeat mb
	 sys(n+cnt)=map(cnt)
	loop

	sys(30+8*55*hyb+1+saven)=hxb ; xの長さ

	bsave "sys.dat",sys ; システムデータ、セーブ

	return

 1~7までは、いわいるコンフィング関係のデータで、例えば表示速度とかゲームレベルとか、ゲーム中に変えられるようなデータ。

 音関係はまだ実装してないので入ってない。

 30~はセーブ画面のミニマップ表示用のマップデータを 8つ分。

 ミニマップというのはこれ

 今回、苦労したものの一つがこれで、これが無いとセーブデータの内容が具体的に分かりにくいと思ったので作った。

 表示速度的にどうかとも思ったけど、表示毎フレーム 8マップ分、2ドットの大きさで 1つずつ描く仕様になってる。

 copyが使えればねえ。
 
 
 表示

#deffunc save_put

	gmode 2 : gmulcolor 255,255,255

; 背景
	pos px,py : celput opt1w
	pos 100+px,29+py : celput gsft35w


	if smf=0 {
	repeat 8,1
	 pos sl_kx1(cnt)+px,sl_ky1(cnt)+py : celput sl_winw ; ウィンドウ
	loop


; ミニマップ

	x1=slwx1+96+px
	repeat 8
	 if dtf(cnt)=0 : continue
	 cn=cnt ; n番目
	  mn=30+cn*55*hyb ; マップデータ座標
	   if cnt=0 : cn=8

	 mnmx=sys(30+8*55*hyb+1+cnt) ; xの長さ

	 repeat hyb : cy=cnt ; y座標
	  x2=x1+cy\2
	   if mnmx=19 : x2+=36
	   if mnmx=37 : x2+=18
	   
	  y=slwy(cn)+15+cy*2+py

	  repeat mnmx : mpn=sys(mn) : mn+ ; x座標
	 ; repeat hxb : mn=hxb*cy+cnt : mpn=smpd(mn) ; x座標
	   if mpn=0 or mpn=-1 : continue

	   if mpn=-2 : gmulcolor   8, 24,160 ; 海
	   if mpn=-3 : gmulcolor  72,104,176 ; 湖
	   if mpn=-4 : gmulcolor 208,136, 56 ; 山

	   if mpn>0 {
	    pn=lpd(mpn)
	    if pn>0 { ; プレイヤー領土
	     cln=(clpn(pn)-1)*3
	      gmulcolor cld(cln+1)+16,cld(cln+2)+16,cld(cln+3)+16
	    }
	   }

	   pos x2+cnt*2,y : celput smhbw
	  loop
	 loop
	loop


	gmulcolor 255,255,255

; アイコン
	repeat 8
	 pos slwx1+30+px ,slwy(cnt+1)+21+py : celput s_icw

	 if dtf(cnt)=0 : continue
	 cn=cnt : if cnt=0 : cn=8
	 pos slwx1+417+px,slwy(cn)+21+py : celput l_icw
	loop


	sl_mj2="AUTO SAVE"
	repeat 8,1 : cn=cnt
	 if cnt<8 {
	  sl_mj1="DATA "+str(cn)
	  bfp sl_mj1,240+px,slwy(cnt)+12+py
	 }
	 if cnt=8 : bfp sl_mj2+px,240,slwy(cnt)+12+py
	loop

	repeat 8
	 if dtf(cnt)=0 : continue
	 cn=cnt : if cnt=0 : cn=8

	 n=gettime(0) : s=str(n) ; 年
	  bfp s,250+px,slwy(cn)+38+py
	 n=gettime(1) : s=str(n) ; 月
	  bfp s,300+px,slwy(cn)+38+py
	 n=gettime(3) : s=str(n) ; 日
	  bfp s,330+px,slwy(cn)+38+py
	 n=gettime(4) : s=str(n) ; 時
	  bfp s,360+px,slwy(cn)+38+py
	 n=gettime(5) : s=str(n) ; 分
	  bfp s,390+px,slwy(cn)+38+py

	 bfp "TARN",260+px,slwy(cn)+64+py
	loop
}


	if smf=1 {
	 ;pos 90,200 : celput sinfow
	 pos smx0+px,smy0+py : celput sinfow;,0,0.8,0.8

; yes
	 pos smx1+px   ,smy1+py    : celput gsbt1w
	 pos smx1+80+px,smy2+18+py : celput slft1w

; no
	 pos smx2+px   ,smy1+py    : celput gsbt1w
	 pos smx2+95+px,smy2+18+py : celput slft2w
	}

	if smf=2 {
	 ;pos 90,200 : celput sinfow
	 pos smx0+px,smy0+py : celput sinfow;,0,0.8,0.8

; yes
	 pos smx1+px   ,smy1+py    : celput gsbt1w
	 pos smx1+80+px,smy2+18+py : celput slft1w

; no
	 pos smx2+px   ,smy1+py    : celput gsbt1w
	 pos smx2+95+px,smy2+18+py : celput slft2w
	}

	return

 ミニマップの所が、そのミニマップ表示の部分。

 ゲーム画面が 1画面~3画面と 3種類あるので、その HEXの横 xの数を、システムデータ sysの最後に 8マップ分格納している。

 smfは、セーブモードフラグで、0=通常、1や2はセーブしますか、ロードしますかの画面。

 これもまだ出来てないけど「セーブしますか?」のような文字を描く。

 YESを押せばセーブされます、それ以外だと smf=0に戻ります。
 
 
 データセーブ

#deffunc save int _saven,int _savef

	saven=_saven ; セーブNo.
	savef=_savef ; フラグ

	sys_save ; システムデータ、セーブ

	if savef=1 : return ; システムデータのみ


; データ格納

	n=0
	dat(n)=lx   : n+ ; スクロール表示位置
	dat(n)=scf  : n+ ; スクロールフラグ
	dat(n)=dbpl : n+ ; 最大戦力プレイヤー
	dat(n)=plb  : n+ ; プレイヤー数
	dat(n)=pn	: n+ ; 現在のプレイヤー
	dat(n)=padn : n+ ; 現在の順番

	dat(n)=mb   : n+ ; 全HEX数
	dat(n)=ldb	: n+ ; 国数

	repeat mb : dat(n)=map(cnt) : n+ : loop ; マップ
	repeat mb : dat(n)=hwd(cnt) : n+ : loop ; HEXの外周枠情報

	repeat lddb : dat(n)=ld(cnt) : n+ : loop ; 領土
	repeat (ldb+1)*ldlb+1 : dat(n)=ldl(cnt) : n+ : loop ; 隣接国

	repeat ldb+1 : dat(n)=lpd(cnt)  : n+ : loop ; 領土のプレイヤー
	repeat ldb+1 : dat(n)=ldcd(cnt) : n+ : loop ; ダイス数
	repeat ldb+1 : dat(n)=ldcu(cnt) : n+ : loop ; キャラクターユニット
	repeat ldb+1 : dat(n)=dpd(cnt)  : n+ : loop ; 国のダイス表示順番

	repeat ldb+1 : dat(n)=lcx(cnt)  : n+ : loop ; 中間座標x
	repeat ldb+1 : dat(n)=lcy(cnt)  : n+ : loop ; 中間座標y
	repeat ldb+1 : dat(n)=ldhx1(cnt): n+ : loop ; 最左ライン
	repeat ldb+1 : dat(n)=ldhx2(cnt): n+ : loop ; 最右ライン
	repeat ldb+1 : dat(n)=ldhy1(cnt): n+ : loop ; 最上ライン
	repeat ldb+1 : dat(n)=ldhy2(cnt): n+ : loop ; 最下ライン

	repeat plb+1 : dat(n)=pfd(cnt)  : n+ : loop ; プレイヤー操作
	repeat plb+1 : dat(n)=pld(cnt)  : n+ : loop ; プレイヤーの領土数
	repeat plb+1 : dat(n)=pdca(cnt) : n+ : loop ; 増ダイス数
	repeat plb+1 : dat(n)=pad(cnt)  : n+ : loop ; 行動順
	repeat plb+1 : dat(n)=dp(cnt)   : n+ : loop ; 全ダイス戦力数
	repeat plb+1 : dat(n)=dcpn(cnt) : n+ : loop ; 表示ダイスグラNo.
	repeat plb+1 : dat(n)=pdsd(cnt) : n+ : loop ; ストックダイス数

	repeat 21 : dat(n)=clpn(cnt) : n+ : loop ; カラー

	s="data"+saven+".dat"
	bsave s,dat ; データセーブ


; リプレイデータセーブ

	dim repd2,repc

	repeat repc
	 repd2(cnt)=repd(cnt)
	loop

	repd2(0)=repc ; カウンター
	repd2(1)=repk ; データ位置

	bsave "rep_data"+saven+".dat",repd2

	return

 見ての通り、dat データ n番目にデータを次々と放り込むベタなやり方になってます。

 ロードはこの逆です。

 システムデータとリプレイデータもセーブしてます、後は初期配置データもセーブされるよう後で実装します。
 
 
 ロードはこうなります。

; データ引き出し

	s="data"+loadn+".dat"

	exist s
	 if strsize!-1 {

	  bload s,dat ; データロード

	n=0
	lx=dat(n)   : n+ ; スクロール表示位置
	scf=dat(n)  : n+ ; スクロールフラグ
	 if scf=1 : hxb=19 ; 横のマスの数
	 if scf=2 : hxb=37
	 if scf=3 : hxb=55

	dbpl=dat(n) : n+ ; 最大戦力プレイヤー
	plb=dat(n)  : n+ ; プレイヤー数
	pn=dat(n)	: n+ ; 現在のプレイヤー
	padn=dat(n)-1 : n+ ; 現在の順番

	mb=dat(n)   : n+ ; 全HEX数
	ldb=dat(n)	: n+ ; 国数

	repeat mb : map(cnt)=dat(n) : n+ : loop ; マップ
	repeat mb : hwd(cnt)=dat(n) : n+ : loop ; HEXの外周枠情報

	repeat lddb : ld(cnt)=dat(n) : n+ : loop ; 領土
	repeat (ldb+1)*ldlb+1 : ldl(cnt)=dat(n) : n+ : loop ; 隣接国

	repeat ldb+1 : lpd(cnt) =dat(n) : n+ : loop ; 領土のプレイヤー
	repeat ldb+1 : ldcd(cnt)=dat(n) : n+ : loop ; ダイス数
	repeat ldb+1 : ldcu(cnt)=dat(n) : n+ : loop ; キャラクターユニット
	repeat ldb+1 : dpd(cnt) =dat(n) : n+ : loop ; 国のダイス表示順番

	repeat ldb+1 : lcx(cnt)=dat(n)  : n+ : loop ; 中間座標x
	repeat ldb+1 : lcy(cnt)=dat(n)  : n+ : loop ; 中間座標y
	repeat ldb+1 : ldhx1(cnt)=dat(n): n+ : loop ; 最左ライン
	repeat ldb+1 : ldhx2(cnt)=dat(n): n+ : loop ; 最右ライン
	repeat ldb+1 : ldhy1(cnt)=dat(n): n+ : loop ; 最上ライン
	repeat ldb+1 : ldhy2(cnt)=dat(n): n+ : loop ; 最下ライン

	repeat plb+1 : pfd(cnt)=dat(n)  : n+ : loop ; プレイヤー操作
	repeat plb+1 : pld(cnt)=dat(n)  : n+ : loop ; プレイヤーの領土数
	repeat plb+1 : pdca(cnt)=dat(n) : n+ : loop ; 増ダイス数
	repeat plb+1 : pad(cnt)=dat(n)  : n+ : loop ; 行動順
	repeat plb+1 : dp(cnt)=dat(n)   : n+ : loop ; 全ダイス戦力数
	repeat plb+1 : dcpn(cnt)=dat(n) : n+ : loop ; 表示ダイスグラNo.
	repeat plb+1 : pdsd(cnt)=dat(n) : n+ : loop ; ストックダイス数

	repeat 21 : clpn(cnt)=dat(n) : n+ : loop ; カラー

	}

; リプレイデータロード
	s="rep_data"+loadn+".dat"
	exist s : n=strsize
	 if n!-1 {

	  dim repd2,n
	   bload s,repd2

	  repc=repd2(0) ; カウンター
	  repk=repd2(1) ; データ位置

	  repeat repc
	   repd(cnt)=repd2(cnt)
	  loop

	  repd(repc)=0 ; 終端処理
	 }

	return

 リプレイデータのセーブファイルの大きさは「可変」にしました、量が大きいので。

 existでファイルがあるか調べ、無ければ strsizeに-1が入り、有れば strsizeにそのデータ量が入ります。

 dim定義で、その strsizeを指定してやります。

 dim repd2,strsizeですね。

 repdは実際に使うリプレイのデータで、100000個で固定です。

 ロードした repd2のデータを repdに移して使います。

 saveの時も使用している repdデータだけを repd2に移してセーブしてます、ファイルのデータ量を抑えられます。

 つまり、ゲーム中で使ってる repdデータは 100000個で固定して使い、セーブファイルだけ必要な量だけにしてセーブしてます。
 
 
 セーブメイン

*save_main ; セーブ

	smf=0 ; モードフラグ

; データフラグ
	repeat 8 : cn=cnt
	 dtf(cn)=0
	 exist "data"+cn+".dat"
	  if strsize!-1 : dtf(cn)=1 ; データが有る
	loop

; システムデータ
	exist "sys.dat"
	 if strsize!-1 : bload "sys.dat",sys ; システムデータ、ロード

	optgf=optf

	kwc=5 : kf=0
	repeat
	 k=0 : mx=0 : my=0 ; 入力リセット
	 repeat 1
	  if kwc>0 : kwc- : break ; ウエイト
	  mx=mousex : my=mousey ; カーソル位置
	  stick k,256 ; マウスボタン、スマホクリック
	 loop

; 押してる
	 repeat 1
	  if k!256 : break

	  if kf=0 {
	   kf=1
	   kepx=mx : kepy=my
	  }

	  if mx-kepx>18 { ; 右へ操作、左の画面へ
	   opt_scf=4
	   ospf=4	; 元の
	   optf=2	; オプション2へ
	   break
	  }

	  if kepx-mx>18 { ; 左へ操作、右の画面へ
	   opt_scf=3
	   ospf=4	; 元の
	   optf=3	; PCチェンジへ
	   break
	  }

	  if kepy-my>18 { ; 上へスクロールして戻る
	   opt_scf=1
	   ospf=4	; 元の
	   optf=0	; メインゲームへ
	   break
	  }

	  if my-kepy>18 { ; 下へスクロールして戻る
	   opt_scf=2
	   ospf=4	; 元の
	   optf=0	; メインゲームへ
	   break
	  }
	 loop

; 押してない
	 repeat 1
	  if k=256 : break ; 放してない
	  if kf=0  : break ; 一度も押してない
	  kf=0

	  sl_keyn=0
	  switch smf
	   case 0
	    repeat 16,1 ; キー入力
	     if sl_kx1(cnt)<mx and sl_ky1(cnt)<my and sl_kx2(cnt)>mx and sl_ky2(cnt)>my {
	      sl_keyn=cnt : break
	     }
	    loop

	    if 1<=sl_keyn and sl_keyn<=7 { ; SAVE
	     smf=1 : sl_keyn2=sl_keyn
	     dtf(sl_keyn2)=1
	    }

	    if 9<=sl_keyn and sl_keyn<=16 { ; LOAD
	     sl_keyn2=sl_keyn-8
	      if sl_keyn=16 : sl_keyn2=0

	     smf=2
	    }
	   swbreak
	   case 1 ; SAVE
	    if smx1+2<mx and smy1+2<my and smx1+235>mx and smy1+66>my { ; YES
	     opt_scf=2
	     ospf=4	; 元の
	     if msf=2 : te_kf=3 : optf=0 : swbreak ; NPC
	     save sl_keyn2 ; セーブ
	    }
	    smf=0
	   swbreak
	   case 2 ; LOAD
	   	if smx1+2<mx and smy1+2<my and smx1+235>mx and smy1+66>my { ; YES
	     load sl_keyn2 ; ロード
	     opt_scf=2
	     ospf=4	; 元の
	     optf=0	; メインゲームへ
	     msf=0	; 抜け用
	     break
	    }
	    smf=0
	   swbreak
	  swend

	 loop

	 if optf!4 : break ; 戻る

	 redraw 0
	  save_put
	  pos 10,600 : mes sl_keyn
	 redraw 1
	 await 1000/50
	loop

	return

 操作関係で、横スワイプだとオプション画面等の切り替えになります。

 縦スワイプでゲームに戻ります。

 
 

 左側のファイル表示を押せばセーブ、右側がロードです。

 一番下はオートセーブで、ゲ-ムのターンエンド時に自動でセーブされます、データ的には dara0になります。

 一般セーブは 7箇所で data1~data7になります。
 
 
 
 セーブ関係のソースでは以上ですけど、これの難しいのは「どのタイミングでセーブするか」で、これもまだ完全では無いんですけど、プレイヤー操作時のセーブは問題ありませんけど、CPUの場合はどの時点でセーブするのか難しいです。

 オプションと並列になってるので、オプションはいつでも操作出来る方が望ましいのでしょうけど、セーブのタイミングを同じにはできません、操作アルゴリズム中の中断デ-タなんてできる訳ありません。

 そこで、セーブ操作をした後、その CPUの勢力が操作を終えた時点でセーブされるようにしてます。

 セーブを決定すると、その CPUの操作が終わるまで表示をスキップするようにしてます。

 操作が終わった時点でセーブします。

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です