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の操作が終わるまで表示をスキップするようにしてます。
操作が終わった時点でセーブします。