先週、Steamにてサマーセールが開催されていた。その中で気になったゲームが1本あった。ゲーム情報をクリックすると、「ファイアーエムブレムのようなゲームが作れる」とのレビューコメントを目にした。つまり、シミュレーションRPGツクール95の後継みたいなものかと思い、他のレビューにも目を遣る。「SRPGに必要なことはほぼ可能」というコメントが俺の背中を押した。
実際初めてみると、ツクール製品とは全く異なるUIなので、まだ慣れていない部分などもある。購入して今日で丁度一週間だったこともあり、正直まだ1つもマップを作っていない。公式プライグインから、ユニットに属性を付与することと、それをステータス画面に表示することが出来るというものを導入してみた。「シミュレーションRPG」、「属性」、「FE系」と三点揃えば思い当たるのは唯一つ、ファイアーエムブレム烈火の剣のような支援システムである。それは、俺が最後にプレイしたFEシリーズに他ならない。
どう作ればいいのやら、とにかく項目が多い。チェックボックスも多い。何が何処にあるのやら手探り状態だ。散々試行錯誤して気付いた。標準機能で実現出来ないかもしれない、という事態に。となればスクリプトを弄るしかないのだが、JAVA Scriptを扱ったことが無い。と、一時は絶望したものの、標準のデータ設定とスクリプトを組み合わせる力技で、何とか実現できた。
ちなみに実際の烈火の剣と異なるアレンジを加えつつ、最終的に出来上がったものは以下の通り。プラグインとして配布できるようなスキルが無いので、あくまでこうして実現しました、という参考に残しておく。わかりにくいと思うので動画にもしてある。じっくり見て欲しい。
■支援システム仕様
・全味方キャラクター同士で支援を発生させることが可能。
・支援効果を得られるのは各人10人まで。
・支援相手に与える支援効果は、自身の属性により決められている。
1.隣接した状態でターンを終えると、互いに支援ポイントが蓄積する。
2.支援ポイントが一定以上になると、対象の相手と会話が可能。(任意)
3.会話した者同士が、支援スキルを取得。
4.支援スキル取得と同時に、支援レベルが上昇する。
5.さらに支援ポイントが蓄積すると、新しい会話が可能となる。
※ただし、同じマップでの支援レベル上昇・会話発生は1度のみ。
6.同じ人との2回目の会話では、1回目に取得した支援スキルが削除され、上位版の支援スキルが追加される。
手探りで設計できていないため、利用していないメソッドがあったり、効率化しようとした結果非効率になったりしている。JAVAと似ていることで助かったが、それでも色々な不明点は残したまま、今は武器を作っている途中だ。武器が終わったら魔法を作る予定だ。
・呼び出しの仕様が分かっていない。JAVAみたいにPrivateとかPublic Static Void的なものはあるのだろうか。
・Rubyだと配列が柔軟なので、存在しない要素にも代入可能。JAVAはNGだったはずなので、全員分のハコを準備したが、最初にオブジェクトの書き換え方が分かっていれば不要だったかもと思った。本来はデータベースのキャラクター.lengthでIDを全部入れたり、要はスクリプト上で設定したかった。
・最後の色んな括弧をいつも見失う。どれが何処の括弧だろうか。
・計算式考えるのに半日かかった気がする。80人を1変数あたり6人ずつに分けて、属性も6種類、支援スキルは属性毎にレベル違いで12種類を80人分。などと要所要所で数え方が異なるのが今回の反省点。統一感が無いのでややこしいに決まってる。
・Math.Trunc()みたいに利用出来ない関数もあった。
※2022.7.12 支援人数が10以上になった時点で会話フラグをリセットしていなかったので処理を追加。文字数の都合上、前後の状況から判断可能な分岐の一部を省略
//友情ポイント
//配列 friendship[相手キャラID、ポイント、支援レベル] ⇒ 操作 friendship[自キャラ][列]
function setFriendLevel(ui,uj)
{
var n, m, p, v, res = 0;
var ck;
var a = ui.getId();
var b = uj.getId();
ui.custom.friendship[b][1] = 0;
ui.custom.friendship[b][2] += 1;
uj.custom.friendship[a][1] = 0;
uj.custom.friendship[a][2] += 1;
if(ui.custom.friendship[b][2] == 1) {
n = ((b + 1) * 12 + ((ui.custom.attr + 1) * 2) - 13);
m = ((a + 1) * 12 + ((uj.custom.attr + 1) * 2) - 13);
}
else {
n = ((b + 1) * 12 + ((ui.custom.attr + 1) * 2) - 12);
m = ((a + 1) * 12 + ((uj.custom.attr + 1) * 2) - 12);
root.getDataEditor().deleteSkillData(ui.getSkillReferenceList(), root.getBaseData().getSkillList().getData(n - 1));
root.getDataEditor().deleteSkillData(uj.getSkillReferenceList(), root.getBaseData().getSkillList().getData(m - 1));
}
root.getDataEditor().addSkillData(ui.getSkillReferenceList(), root.getBaseData().getSkillList().getData(n));
root.getDataEditor().addSkillData(uj.getSkillReferenceList(), root.getBaseData().getSkillList().getData(m));
v = ((a + 1) * 80) - 81;
res = root.getMetaSession().getVariableTable(4).getVariable(Math.floor((b + 6) / 6) + v);
switch(Math.floor((b + 6) % 6)) {
case 0:
if(Math.floor(res / 1 % 10) == 1) {
root.getMetaSession().getVariableTable(4).setVariable(Math.floor((b + 6) / 6) + v, res - 1);
}
break;
case 1:
if(Math.floor(res / 10 % 10) == 1) {
root.getMetaSession().getVariableTable(4).setVariable(Math.floor((b + 6) / 6) + v, res - 10);
}
break;
//~省略~
case 5:
if(Math.floor(res / 100000 % 10) == 1) {
root.getMetaSession().getVariableTable(4).setVariable(Math.floor((b + 6) / 6) + v, res - 100000);
}
break;
}
v = ((b + 1) * 80) - 81;
res = root.getMetaSession().getVariableTable(4).getVariable(Math.floor((a + 6) / 6) + v);
switch(Math.floor((a + 6) % 6)) {
case 0:
if(Math.floor(res / 1 % 10) == 1) {
root.getMetaSession().getVariableTable(4).setVariable(Math.floor((a + 6) / 6) + v, res - 1);
}
break;
case 1:
if(Math.floor(res / 10 % 10) == 1) {
root.getMetaSession().getVariableTable(4).setVariable(Math.floor((a + 6) / 6) + v, res - 10);
}
break;
//~省略~
case 5:
if(Math.floor(res / 100000 % 10) == 1) {
root.getMetaSession().getVariableTable(4).setVariable(Math.floor((a + 6) / 6) + v, res - 100000);
}
break;
}
//会話して10人になった時点で全フラグ消失させる
for(var i = 0; i
if(ui.custom.friendship[i][2] >= 1) {
p += 1;
}
}
if(p >= 1) {
v = ((a + 1) * 80) - 81;
for(var i = 0; i < 80; i++) {
res = root.getMetaSession().getVariableTable(4).getVariable(Math.floor((i + v) / 6));
switch(Math.floor((i + v + 6) % 6)) {
case 0:
if(Math.floor(res / 1 % 10) == 1) {
root.getMetaSession().getVariableTable(4).setVariable(Math.floor((i + v) / 6), res - 1);
}
break;
case 1:
if(Math.floor(res / 10 % 10) == 1) {
root.getMetaSession().getVariableTable(4).setVariable(Math.floor((i + v) / 6), res - 10);
}
break;
//~省略~
case 5:
if(Math.floor(res / 100000 % 10) == 1) {
root.getMetaSession().getVariableTable(4).setVariable(Math.floor((i + v) / 6), res - 100000);
}
}
}
v = (b + 1) * 80 - 80;
for(var i = 0; i < 80; i++) {
res = root.getMetaSession().getVariableTable(4).getVariable(Math.floor((i + v) / 6));
switch(Math.floor((i + v + 6) % 6)) {
case 0:
if(Math.floor(res / 1 % 10) == 1) {
root.getMetaSession().getVariableTable(4).setVariable(Math.floor((i + v) / 6), res - 1);
}
break;
case 1:
if(Math.floor(res / 10 % 10) == 1) {
root.getMetaSession().getVariableTable(4).setVariable(Math.floor((i + v) / 6), res - 10);
}
break;
//~省略~
case 5:
if(Math.floor(res / 100000 % 10) == 1) {
root.getMetaSession().getVariableTable(4).setVariable(Math.floor((i + v) / 6), res - 100000);
}
}
}
}
p = 0;
for(var i = 0; i
if(uj.custom.friendship[i][2] >= 1) {
p += 1;
}
}
if(p >= 1) {
v = (a + 1) * 80 - 80;
for(var i = 0; i < 80; i++) {
res = root.getMetaSession().getVariableTable(4).getVariable(Math.floor((i + v) / 6));
switch(Math.floor((i + v + 6) % 6)) {
case 0:
if(Math.floor(res / 1 % 10) == 1) {
root.getMetaSession().getVariableTable(4).setVariable(Math.floor((i + v) / 6), res - 1);
}
break;
case 1:
if(Math.floor(res / 10 % 10) == 1) {
root.getMetaSession().getVariableTable(4).setVariable(Math.floor((i + v) / 6), res - 10);
}
break;
case 2:
if(Math.floor(res / 100 % 10) == 1) {
root.getMetaSession().getVariableTable(4).setVariable(Math.floor((i + v) / 6), res - 100);
}
break;
case 3:
if(Math.floor(res / 1000 % 10) == 1) {
root.getMetaSession().getVariableTable(4).setVariable(Math.floor((i + v) / 6), res - 1000);
}
break;
case 4:
if(Math.floor(res / 10000 % 10) == 1) {
root.getMetaSession().getVariableTable(4).setVariable(Math.floor((i + v) / 6), res - 10000);
}
break;
case 5:
if(Math.floor(res / 100000 % 10) == 1) {
root.getMetaSession().getVariableTable(4).setVariable(Math.floor((i + v) / 6), res - 100000);
}
}
}
v = (b + 1) * 80 - 80;
for(var i = 0; i < 80; i++) {
res = root.getMetaSession().getVariableTable(4).getVariable(Math.floor((i + v) / 6));
switch(Math.floor((i + v + 6) % 6)) {
case 0:
if(Math.floor(res / 1 % 10) == 1) {
root.getMetaSession().getVariableTable(4).setVariable(Math.floor((i + v) / 6), res - 1);
}
break;
case 1:
if(Math.floor(res / 10 % 10) == 1) {
root.getMetaSession().getVariableTable(4).setVariable(Math.floor((i + v) / 6), res - 10);
}
break;
//~省略~
case 5:
if(Math.floor(res / 100000 % 10) == 1) {
root.getMetaSession().getVariableTable(4).setVariable(Math.floor((i + v) / 6), res - 100000);
}
}
}
}
}
function checkFriendLevel(ui,uj)
{
return ui.custom.friendship[uj.getId()][2];
}
(function() {
UnitParameter.FriendShip = defineObject(BaseUnitParameter,
{
setInitFriendShip: function(unit, value) {
unit.custom.friendship[unit.getData(this).getId()] = value;
},
//user
flagFriendShip: function(a,b) {
var v = ((a + 1) * 80) - 81;
var res = root.getMetaSession().getVariableTable(4).getVariable(Math.floor((b + 6) / 6) + v);
//トークフラグは加算
switch(Math.floor((b + 6) % 6)) {
case 0:
if(Math.floor(res / 1 % 10) != 1) {
root.getMetaSession().getVariableTable(4).setVariable(Math.floor((b + 6) / 6) + v, res + 1);
}
break;
case 1:
if(Math.floor(res / 10 % 10) != 1) {
root.getMetaSession().getVariableTable(4).setVariable(Math.floor((b + 6) / 6) + v, res + 10);
}
break;
case 2:
if(Math.floor(res / 100 % 10) != 1) {
root.getMetaSession().getVariableTable(4).setVariable(Math.floor((b + 6) / 6) + v, res + 100);
}
break;
case 3:
if(Math.floor(res / 1000 % 10) != 1) {
root.getMetaSession().getVariableTable(4).setVariable(Math.floor((b + 6) / 6) + v, res + 1000);
}
break;
case 4:
if(Math.floor(res / 10000 % 10) != 1) {
root.getMetaSession().getVariableTable(4).setVariable(Math.floor((b + 6) / 6) + v, res + 10000);
}
break;
case 5:
if(Math.floor(res / 100000 % 10) != 1) {
root.getMetaSession().getVariableTable(4).setVariable(Math.floor((b + 6) / 6) + v, res + 100000);
}
break;
}
return;
},
fullFriends: function(ui) {
var n = 0;
for(var i = 0; i
if(ui.custom.friendship[i][2] >= 2) {
n += 1;
}
}
if(n > 10) {
return true
}
return false
},
halfFriends: function(ui) {
var n = 0;
for(var i = 0; i
if(ui.custom.friendship[i][2] >= 1) {
n += 1;
}
}
if(n > 10) {
return true
}
return false
},
setFriendShip: function(unit) {
var len = unit.getCount();
var x1, x2, y1, y2 = 0;
var a, b;
var n;
var ui, uj;
for(var i = 0; i
ui = unit.getData(i);
//友好レベル2(MAX)の相手が10人以上いたら友好ポイント加算しない。
if(UnitParameter.FriendShip.fullFriends(ui) == false) {
x1 = ui.getMapX();
y1 = ui.getMapY();
for(var j = len - 1; j > - 1; j--) {
uj = unit.getData(j);
x2 = uj.getMapX();
y2 = uj.getMapY();
a = ui.getId();
b = uj.getId();
//自分(ui)と相手(uj)が別人の時だけ。
if((a != b)&&(ui.custom.friendship[b][2]
//隣接状態なら自分側から見た相手(自分の友好リストにある相手のID番)の友好ポイントに1を加算する。
if((x1 == x2)&&(y1 + 1 == y2)) {
ui.custom.friendship[b][1] += 1;
}
else if((x1 == x2)&&(y1 - 1 == y2)) {
ui.custom.friendship[b][1] += 1;
}
else if((x1 + 1 == x2)&&(y1 == y2)) {
ui.custom.friendship[b][1] += 1;
}
else if((x1 - 1 == x2)&&(y1 == y2)) {
ui.custom.friendship[b][1] += 1;
}
else{
}
//自分側の相手に対する友好レベルが0段階で、友好ポイント100を超えた時。
if((ui.custom.friendship[b][2] == 0)&&(ui.custom.friendship[b][1] > 0)) {
if(UnitParameter.FriendShip.halfFriends(ui) == false){
UnitParameter.FriendShip.flagFriendShip(a,b);
}
}
// 〃 友好レベルが1段階で、友好ポイント200を超えた時
else if((ui.custom.friendship[b][2] == 1)&&(ui.custom.friendship[b][1] > 1)){
UnitParameter.FriendShip.flagFriendShip(a,b);
}
}
}
}
}
}
}
);
})();
TurnChangeEnd._startNextTurn = function() {
var nextTurnType;
var turnType = root.getCurrentSession().getTurnType();
this._checkActorList();
if (turnType === TurnType.PLAYER) {
// この時点で敵の数が0だった場合は、敵ターンを実行しないようなことも可能ではある。
// しかし、その場合はイベント条件で敵ターン関連を検出できなくなるため、
// 常に敵ターンに切り替えるようにしている。
// 実際には敵の数が0の場合は、画像もBGM変更も行われないため、
// 敵ターンに切り替わったようには見えない。
nextTurnType = TurnType.ENEMY;
}
else if (turnType === TurnType.ENEMY) {
nextTurnType = TurnType.ALLY;
}
else {
UnitParameter.FriendShip.setFriendShip(PlayerList.getSortieOnlyList());
nextTurnType = TurnType.PLAYER;
}
root.getCurrentSession().setTurnType(nextTurnType);
};