ウマサポLua設定集【初級・中級編】

[お知らせ]
しばらくヒカセンとエルデンに時間を割いていたのですが、新シナリオ投入されたので最近戻ってきました。休んでいた間にいろいろ状況が変わっていたので※、またいちからスクリプト整理しなおしています。こちらの記事は情報が古くなっており、本文が見えないと紛らわしいので無料化しました。ロジックを参考にする程度に留めていただければと思います。
※ウマサポの仕様とかサポカイベントの仕様とか

[このエントリの目的]
自分ならこういうプレイングをするというアイディアと設定.luaへの実装例を書いていきます。そのうち体系的に整理したい。

[今あるコンテンツ]
【初級】
・固定イベントのステータスボーナスを考慮してトレーニング優先度を制御
・シニアの正月明け(1月前半)では賢さ練習をしない
・育成キャラごとに取得するスキルのタイプを設定したい
・固有継承スキルは取りたくない
・たづなの絆ゲージを早い段階で緑にしておきたい
【中級】
・自作スクリプトを設定.luaの外で管理したい
・スキル習得やヒント獲得状況に合わせてヒントを踏むか制御したい
・目標値に近づく程トレーニングの評価点を下方補正したい
・強制出走レースのリストをcsvで管理したい(8/29修正)
・チャンピオンズミーティングの条件別にスキルの取得優先度をまとめて変更したい(10/3追加)
・地固め習得を効率よく回したい
・SS2B育成か4B育成を判定してそれぞれに合う育成をしたい(11/5追加)

【初級】
数行のコードを追加・変更するレベル

 目標ステータスを超えたら関連するトレーニングの優先度を下げる

【背景】例えば4B育成する際にスピードがここまで上がったら後は一切スピード練習しないレベルで避けたい
【実装先】getStatusScore関数

function getStatusScore( statusName, trainingName, hp, failureRate )

(中略)

    -- 実質的に超えたいステータス
	local goal_speed = 600
    -- URA決勝~育成終了までに貰えるステータスボーナス
	local lastBonus = 30
    -- 設定用ステータス
	local target_speed = goal_speed - lastBonus

(中略)

    -- 目標値を超えたら優先度を下方補正
	if g_status[STATUS.SPEED] > target_speed and statusName == STATUS.SPEED then score = score * 0.01 end

	return score
end

 シニアの正月明け(1月前半)では賢さ練習をしない

【背景】直後に福引イベントで高確率で体力回復が発生するため、賢さトレーニングをすると体力が溢れがち
【実装先】getStatusScore関数

function getStatusScore( statusName, trainingName, hp, failureRate )

(中略)

    -- 3年目の正月明けの練習は直後に回復イベントが来るので賢さは選ばないようにする
	if g_status[STATUS.SEASON].index == SEASON.SENIOR_1A.index then
		if trainingName == STATUS.INT then score = -10000 end
	end

	return score
end

 育成キャラごとに取得するスキルのタイプを設定したい

【背景】育成キャラの限定タイプ習得スキル設定しても機能してなくない?
※v0.07で限定タイプ習得スキル設定に逃げ/先行/差し/追込が追加されましたが、設定:デフォルト.luaの関連個所が更新されていないのでそのままでは機能しません。
【実装先】getSkillScore関数

function getSkillScore( skillInfo, hintLevel )

(中略)

    -- 限定タイプ習得スキル設定の逃げ・先行・差し・追込の各脚質優先度のスコアから最大値を設定
	local t1 = skillInfo.stylePriority[STYLE.NIGE]
	local t2 = skillInfo.stylePriority[STYLE.SENKOU]
	local t3 = skillInfo.stylePriority[STYLE.SASHI]
	local t4 = skillInfo.stylePriority[STYLE.OIKOMI]
	local score = math.max(t1, t2, t3, t4)

(中略)

    -- 限定タイプスキルの場合
   	-- INDEFINITEは"-"でなにも設定されていない
	if skillInfo.limitedType ~= INDEFINITE then

		-- 限定タイプ習得スキルに設定されている場合
		if characterInfo.limitedTypeSkill[skillInfo.limitedType] then
			
			-- スコアをそのまま使用
			
		-- 限定タイプ習得スキルに設定されていない場合
		else
			-- スコアを0にする
			score = 0
		end
	end

    return score
end

 固有継承スキルは取りたくない

【背景】継承用ウマ娘には固有継承とっても意味がないし、ウマサポ上で固有スキルの優先度をポチポチ0にしていくのめんどくさい
【実装先】getSkillScore関数

function getSkillScore( skillInfo, hintLevel )

(中略)

    -- 固有スキルは習得しない
	if skillInfo.rare == "固有" then
		
		score = -1	-- -1にしておけば他に取るスキルが無くなった時にスコア0を優先する
	
	end

(中略)

    return score
end

 たづなの絆ゲージを早い段階で緑にしておきたい

【背景】ジュニアの内におでかけ発生させるために8月くらいまではたづなの優先度を上げてトレーニングしたい
【実装先】getKizunaScore関数
※ウマサポのバージョンアップで友人用の変数が増えたので今後合わせて更新予定。ひとまず↓のままでも使用可能です。かっしーの分はたづなさんのところを複製してください。

function getKizunaScore( charaName, hintFlag )

(中略)

        -- 友人タイプの場合
	    elseif supportChara.type == SUPPORT_TYPE.FRIEND then
            
            -- たづな用調整
			if charaName == "駿川たづな" then
			
				-- 1年目8月までにLv3(緑)を目指す
				if g_status[STATUS.SEASON].index < SEASON.JUNIOR_8B.index then
					if supportChara.kizunaLevel < 3 then 
						score = score * 15 
					end
				else
					-- 1年目8月過ぎても達成できていなかったら全力
					if supportChara.kizunaLevel < 3 then
						score = score * 100
					end
				end
			end
        end

(中略)

    return score, hintScore
end

【中級】
自分で関数を作ってより細かい制御をする

 自作スクリプトを設定.luaの外で管理したい

※ここらへんからスクリプトの行数が増えるので、ファイルを分けることをお勧めします。(9/1追記)

[設定.lua]
require "myScript"

----------------------------------------------------------------------------------------------------
-- 基本設定
----------------------------------------------------------------------------------------------------
(中略)

このように設定.luaの冒頭に require "[Filename]"と記述すると、ウマサポと同じフォルダの中にある"[Filename].lua"をロードします。

[myScript.lua]
----------------------------------------------------------------------------------------------------
-- グローバル変数の定義
----------------------------------------------------------------------------------------------------
-- URA決勝~育成終了までに貰えるステータスボーナス
lastBonus = 25

(中略)

----------------------------------------------------------------------------------------------------
-- calcVaildHintscore関数
-- サポートカード別に取得したいスキルの習得・ヒント獲得状況に応じてヒントスコアを補正する
----------------------------------------------------------------------------------------------------
-- 引数:referSupport
--   サポートカード名(e.g. アイネスフウジン [飛び出せ、キラメケ])
-- 引数:targetSkills
--   取得したいスキル名のリスト
-- ※スキル名はミス防止のためスキル:デフォルト.csvからコピペすること
-- 戻り値
--   補正したヒントスコア
----------------------------------------------------------------------------------------------------
function calcVaildHintscore(supportChara, referSupport, targetSkills)

	local hintScore = 0

(中略)

グローバル変数や関数を定義しておくと、設定.luaでも同じように呼び出すことができます。
【注意点】
・ファイル名には全角文字を入れない方がいいかも
・自作luaファイルを作って保存する際は文字コードをUTF-8で保存すること
(9/1追記)
ウマサポのバージョンが上がるごとに自作スクリプトを移動するのがめんどくさい場合は別途それ用のフォルダを切っておき、設定.luaでそちらを参照するようにするとよいです。(それでも設定.luaをはじめとするウマサポ由来の設定ファイルは手動移動する必要がありますが)

[設定.lua]
-- いわゆる環境変数PATHを通す処理
package.path = package.path .. ";C:\\Umamusume\\UmasapoExtLibrary\\?.lua"

これを設定.luaの冒頭に記述してからrequireすれば追加したパスへrequire指定されたファイルを検索しに行きます。バックスラッシュはLuaではエスケープ文字なので、2つ重ねる必要があります。
\\?.luaの部分がなければcsvも探しに行ってくれるのでは?と期待したのですが駄目だったので、ファイル指定をするところで相対パスを指定しています。

-- CSVファイルを相対パスで指定(.でカレントディレクトリ、..でカレントディレクトリの1つ上の階層)
-- ※ここで言うカレントディレクトリはウマサポの実行ファイルがあるディレクトリ
-- この例ではウマサポが入っているフォルダの上に「UmasapoExtLibrary」フォルダを作成して、その中ある「racePlanning.csv」を参照
raceplanningAlias, list = setRacePlanning('..\\UmasapoExtLibrary\\racePlanning.csv')

 スキル習得やヒント獲得状況に合わせてヒントを踏むか制御したい

【背景】例えばSRキングヘイローから良バ場○とコーナー回復〇だけ貰ったら他のスキルは不要なので、以後はヒントアイコンが発生していてもトレーニングの優先度計算にヒントスコアを含めないようにしたい
【実装先】getKizunaScore関数とgetSkillScore関数
行数が多くなるので自分で関数を定義して呼び出します。

----------------------------------------------------------------------------------------------------
-- calcVaildHintscore関数
-- サポートカード別に取得したいスキルの習得・ヒント獲得状況に応じてヒントスコアを補正する
----------------------------------------------------------------------------------------------------
-- 引数:referSupport
--   サポートカード名(e.g. アイネスフウジン [飛び出せ、キラメケ])
-- 引数:targetSkills
--   取得したいスキル名のリスト
-- ※スキル名はミス防止のためスキル:デフォルト.csvからコピペすること
-- 戻り値
--   補正したヒントスコア
----------------------------------------------------------------------------------------------------
function calcVaildHintscore(referSupport, targetSkills)

	local hintScore = 0

	-- 因子化したいスキルの数を設定
	local hopeSkillNum = #targetSkills
	local gotHintNum = 0

	for i = 1, hopeSkillNum do
		local gotFlg = 0

		-- チェックするスキルを習得済みなら+1
		if  #g_status[STATUS.ACQUIRE_SKILL] > 0 then

			for j = 1, #g_status[STATUS.ACQUIRE_SKILL] do
				if g_status[STATUS.ACQUIRE_SKILL][j].name == targetSkills[i] then
					gotFlg = 1 
					break
				end
			end
		end

		-- ヒントLvがg_skillInfoに追加済みの場合
		if isKeyExist(g_skillInfo[targetSkills[i]], "hintLevel") then
			
			-- チェックするスキルを習得していない場合、ヒントLvが1以上ならヒント獲得フラグを立てる
			if gotFlg == 0 and g_skillInfo[targetSkills[i]].hintLevel > 0 then gotFlg = 1 end
		
		-- ヒントLvがg_skillInfoに追加されていない場合(デビュー戦前・一度スクリプトを止めてから再度実行時)
		else
			-- ヒントを得ていないものとして扱う
			-- 何もしない
		end

		gotHintNum = gotHintNum + gotFlg
	end

	-- 該当のサポカで欲しいスキルのヒントを取り切れていなかったらヒントを踏む
	if gotHintNum < hopeSkillNum then

		-- サポートキャラクターのデータベースに登録しているヒントマークの優先度をスコアにする
		hintScore = g_supportCharacterInfo[referSupport].hintPriority
	end
	
	if hintScore > 0 then print("(!)" .. removeNickName(referSupport) .. "は有効なヒント") end

	return hintScore

end

----------------------------------------------------------------------------------------------------
-- isKeyExist関数
-- 対象のテーブルのキーに指定のkeyが存在するかどうかチェックする
----------------------------------------------------------------------------------------------------
-- 引数:tbl
--   チェックするテーブル
-- 引数:key
--   確認するkeyの名前(文字列指定)
-- 戻り値
--   存在すればTrue、なければFalseを返す
----------------------------------------------------------------------------------------------------
function isKeyExist( tbl, key )
	for k, v in pairs(tbl) do
		if k == key then return true end
	end
	return false
end

そしたらgetKizunaScore関数内の「if hintFlag then~」の中で狙いを絞りたいスキルを指定します。

SSRキタサンブラックでコーナー回復〇、逃げ直線〇に狙いを絞る場合

function getKizunaScore( charaName, hintFlag )

(中略)

	-- ヒントマーク(!)によるスコアの算出
	local hintScore = 0
	if hintFlag then

        -- 取得済みスキルとヒントLvを参照して必要なヒントのみ優先する
    	-- ヒントLvはgetSkillScoreでg_skillInfoにhintLevelをkey追加して初めて参照できるため、以下の条件では参照できない
    	-- ①デビュー戦前、②スクリプトを停止してから再実行してからスキル習得画面を開くまで
    	-- ①、②の場合とも、欲しいヒントを得ていない前提で処理する
    	-- 【注意】○や◎は手打ちせずに、データシートからコピペすること
    	-- いずれチャンミ設定をリストか何かで引数にしてそれに対するアウトプットみたいなことをやりたい
    
    	-- スピード
    	if charaName == "キタサンブラック" then
            hintScore = calcVaildHintscore("キタサンブラック [迫る熱に押されて]", {"コーナー回復○", "逃げ直線○"})
        end
    end

(中略)

    return score, hintScore
end

但しg_skillInfoにはヒントLvを格納する列がないため、このままでは習得したスキルの考慮はできてもヒントの考慮ができません。そこでhintLevelを引数で受けるgetSkillScore関数の中でg_skillInfoにhintLevelのKeyを追加します。

function getSkillScore( skillInfo, hintLevel )

(中略)

    -- ヒントLvを格納
	g_skillInfo[skillInfo.name].hintLevel = hintLevel

(中略)

    return score
end

g_skillInfoはグローバルテーブルなのでどこからでもアクセスできますが、getSkillScore関数はスキルを習得するタイミングでのみ呼び出されるため、ヒントLvを参照できるようになるのは出走レース前にスキルを習得しにいくタイミング以降になります。従って以下の制約があります。
①デビュー戦前までは機能しない、isCheckAcquireSkill関数でチェックタイミングをある程度頻繁に行うようにする必要がある

②g_skillInfoはスクリプト開始時にスキル.csvとスキル:デフォルト.csvからロードして作成されるテーブルであるため、一度スクリプトを停止すると再びスキル習得モードに入るまではヒントLvを参照できなくなる。
※Luaはファイルの書き込みをするioモジュールがあるのですがコンパイルされていない?ためか今の所使えない。
→v0.09でioモジュールが組み込まれたので可能になりました。いずれ更新

 目標値に近づく程トレーニングの評価点を下方補正したい

【背景】4B育成を狙う際に平均的にステータスを伸ばしたいが、サポカ編成の偏りによってはバランス良く伸ばせずにB未満になってしまう場合がある
【実装先】getStatusScore関数
ステータスが目標値に近づくほど、スコアが低く補正されるようにする関数を作ります。

----------------------------------------------------------------------------------------------------
-- softAdjust関数
-- 三角比的に目標値近辺に近づくほど下方補正がかかる係数でスコアを補正する
----------------------------------------------------------------------------------------------------
-- 引数:inScore
--   補正前のスコア
-- 引数:statusName
--   対象のステータス
-- 引数:target_parms
--   実質目標ステータスのリスト
-- 戻り値
--   補正したスコアを返す
----------------------------------------------------------------------------------------------------
-- 目標値に近づくほどスコアの優先度を下げる
function softAdjust(inScore, statusName, target_parms)

	for i = 1,#status_tbl do
		if statusName == status_tbl[i] then

			-- 目標値に対する現在値の達成度
			local progress = g_status[statusName] / target_parms[i]

			-- 達成度が1未満の時はこれをcosθとみなし、補正係数をsinθで算出してスコアに適用する
			if progress < 1 then

				return math.ceil(inScore * math.sqrt(1 - (progress * progress)))

			-- 達成度が1以上の時は、0.01に補正(できるだけ高いパラメータを選択するため)
			else
				return math.ceil(inScore * 0.01)
			end
		end
	end
end

----------------------------------------------------------------------------------------------------
-- isValExist関数
-- 対象のリストに指定の要素が存在するかどうかチェックする
----------------------------------------------------------------------------------------------------
-- 引数:list
--   チェックするリスト
-- 引数:val
--   確認するkeyの名前(文字列指定)
-- 戻り値
--   存在すればTrue、なければFalseを返す
----------------------------------------------------------------------------------------------------
function isValExist( list, val )
	for i = 1, #list do
		if list[i] == val then return true end
	end
	return false
end

とりあえず、現在値が目標値よりも遠いときは緩やかに、近づくほど強く補正を受けることを表現するものとして三角比を選びました(適当)
getStatusScore関数の中ではこのように呼び出します。

function getStatusScore( statusName, trainingName, hp, failureRate )

(中略)

        -- 0から100までの範囲内に収める
		if score > 100 then
			score = 100
		elseif score < 0 then
			score = 0
		end
	end

    -- メインステータスならスコア補正をする
    local target_tbl = {600,600,600,300,600}
    local tbl = { STATUS.SPEED, STATUS.STAMINA, STATUS.POWER, STATUS.GUTS, STATUS.INT }
	if isValExist(tbl , statusName) then
        score = softAdjust(score, statusName, target_tbl)
    end
   
    return score

end

target_tbl = {600,600,600,300,600}はスピ・スタ・パワ・根性・賢さの目標値のテーブルです。説明の簡略化のためにローカル定義していますが、実際は設定.luaの冒頭でグローバル変数で定義して使う方が便利です。
※わざわざテーブルを作ってステータスを指定しているのは、getStatusScore関数はトレーニングで得られるスキルポイントの評価計算にも使われているため、これを避けるためです。

 強制出走レースのリストをcsvで管理したい(8/29修正)

【背景】因子用の育成で重賞ボーナスを狙う場合、G2/G3まで含めると追加出走レースだけで20近く記述しないといけません。キャラごとにいちいち手打ちしたり、管理するのが面倒なのでキャラ別の出走レースリストをcsvで管理して楽したい。
【実装先】自作関数
v0.09で追加されたioモジュールを使ってcsvファイルを読み込み、強制出走レースの配列に格納していくだけです。
csvファイルはExcelで重賞ボーナスを調整しながら作ったカレンダーからワンクリックで吐き出せるようにしたもので、以下のような中身になっています。

キャプチャ_raceplanningcsv

----------------------------------------------------------------------------------------------------
-- setRacePlanning関数
-- 強制出走レースをcsvファイルから参照して設定する
----------------------------------------------------------------------------------------------------
-- 引数:filename
--   参照するcsvファイル
-- 戻り値
--   csvファイルのコメント
----------------------------------------------------------------------------------------------------
function setRacePlanning(filename)
	local ret = ''
	local f = io.open(filename, 'r')

	local j = 1	-- 読込行数
	for line in f:lines() do

		local data = split(line, ",")

		-- 1行目はコメント用
		if j == 1 then

			-- 戻り値
			ret = data[2]
		end

		if j > 1 then
			CONFIG.RUN_RACE[j - 1] = { name = data[1] , season = SEASON[data[2]] }
		end
		j = j + 1
	end

	f:close()
	return ret
end

----------------------------------------------------------------------------------------------------
-- split関数
-- 文字列を指定のデリミタで分割したリストを作成する
----------------------------------------------------------------------------------------------------
-- 引数:str
--   対象文字列
-- 引数:delim
--   区切り文字列
-- 戻り値
--   デリミタがなければ元の文字列を、あれば分割してテーブル(リスト)を返す
----------------------------------------------------------------------------------------------------
function split(str, delim)

	-- Eliminate bad cases...
	if string.find(str, delim) == nil then
		print('no delimitor:' .. str)
		return { str }
	end

	local t = {} 
		i=1
		for s in string.gmatch(str, "([^"..delim.."]+)") do
			t[i] = s
			i = i + 1
		end

	return t
end


これを設定.luaで呼びだして、戻り値をonStartTraining関数でprintすれば育成開始時に何の設定をしたかが可視化されます。

--------------------------------------------------------------------------------------
-- 強制出走レース
--------------------------------------------------------------------------------------
raceplanningAlias = setRacePlanning('racePlanning.csv')

----------------------------------------------------------------------------------------------------
-- onStartTraining関数
-- 育成開始時に呼び出される
-- 呼び出されるタイミングは実行開始後onStartTurn関数が呼び出される直前で1回のみ呼び出される
-- ステータス等は認識されている状態になっている
-- 初期化処理等を記述したい場合に利用する
----------------------------------------------------------------------------------------------------
function onStartTraining()

    -- ■ 出走レースプラン:中距離逃げ因子用ブルボン と上記例のcsvを使うと表示される
	print("■ 出走レースプラン:" .. raceplanningAlias)

end

参考までにExcelのマクロ。こちらのツールも綺麗化できたらご紹介したいですね。

Sub createRacePlanTable()

   '出走レーステーブル作成シート
   Dim c As Worksheet
   Set c = ThisWorkbook.Worksheets("出走レーステーブル作成")
   
   'Umamusume Trainer's Clipboard(UTCと略称)を使用して出走カレンダーを作成しているため、
   'UTCにおけるレース名表記とウマサポのテーブルの表記を紐づけるための読替表を間に入れています
   '出走レーステーブル名読替表
   Dim tr As Worksheet
   Set tr = ThisWorkbook.Worksheets("レース名読替")
   
   '読込対象の情報セット
   '対象シート
   Dim t As Worksheet
   Set t = ThisWorkbook.Worksheets(c.Range("C2").Value)
   
   '年度の列・行範囲
   Dim y_col, t_col, char As Long
   Dim row_s, row_e As Long
   Dim comment As String
   With c
       y_col = Range("D3")
       t_col = Range("D4")
       row_s = Range("C5")
       row_e = Range("C6")
       char = Range("D7")
       comment = Range("C8")
   End With

   'ウマサポの強制出走レースに設定するシーズンとレース名の配列を作成
   Dim season_arr(), race_arr() As String
   Dim racename_clip, racename_umasapo As String
   Dim season, year, turn As String
   
   Dim race_count As Long  '出走レース数
   race_count = 0
   
   Dim i As Long
   For i = row_s To row_e
   
       '一応初期化
       racename_clip = ""
       racename_umasapo = ""
       season = ""
       year = ""
       turn = ""
                   
       If t.Cells(i, char) <> "" Then
           race_count = race_count + 1
           
           'レース名をUmamusume Trainer's Clipboardの表記からウマサポのテーブルの表記に読み替えて配列に格納
           racename_clip = t.Cells(i, char)
           racename_umasapo = WorksheetFunction.VLookup(racename_clip, tr.Range("A:B"), 2, False)
           
           ReDim Preserve race_arr(race_count)
           race_arr(race_count - 1) = racename_umasapo
           
           
           '時期をウマサポのseason用のフォーマットで作成して配列に格納
           year = t.Cells(i, y_col)
           
           Select Case year
               Case "ジュニア"
                   year = "JUNIOR"
               Case "クラシック"
                   year = "CLASSIC"
               Case "シニア"
                   year = "SENIOR"
           End Select
           
           turn = t.Cells(i, t_col)
           season = year & "_" & turn
           
           ReDim Preserve season_arr(race_count)
           season_arr(race_count - 1) = season
           
       End If

   Next i

   'csvに出力
   
   '出力ファイル名
   Dim racePlanTable As String
   Const FILENAME As String = "racePlanning.csv"
   
   'ストリーム、最終行番号
   Dim outStream As New ADODB.Stream

   'ストリーム準備
   Dim j As Long
   Dim line As String

   With outStream
       .Type = adTypeText
       .Charset = "UTF-8"
       .LineSeparator = adCRLF
       .Open
   End With

   '全レコードへの処理
   For i = 1 To UBound(race_arr) + 1
       
       '1行目はコメント用
       If i = 1 Then
           line = "コメント," & comment
       
       '2行目以降にテーブル本体
       Else    
           line = race_arr(i - 2) & "," & season_arr(i - 2)
       End If
   
       'i行目のレコードをoutStreamに出力
       outStream.WriteText line, adWriteLine
   
   Next i

   '出力CSV保存
   outStream.SaveToFile ThisWorkbook.Path & "\" & FILENAME, adSaveCreateOverWrite

   '後処理
   outStream.Close
   Set outStream = Nothing
   MsgBox "出力完了!", vbOKOnly + vbInformation, "強制出走レーステーブル作成処理"

End Sub

※8/29修正内容
SEASONが単なる添え字と勘違いしてSEASON.JUNIOR_8Bを単なる変数値か何かと思ってました。実際のところSEASONはテーブルで、

SEASON.JUNIOR_8B = {name = "ジュニア級8月後半", index = 16, race1? = "クローバー賞", ... race20? = "" }}

という構造のテーブルになっています。(race1?のkey名は未確認)
なので、csvの方はJUNIOR_8Bの部分だけにして、これをSEASONに渡すことでちゃんとテーブルをseasonに渡すようにしたら上手くいきました。

 チャンピオンズミーティングの条件別にスキルの取得優先度をまとめて変更したい

【背景】チャンミの条件ごとに緑スキルや特攻スキルの取得優先度をウマサポの画面でチマチマ変えるのめんどくさい
【実装先】自作関数
強制出走レース設定の二番煎じ。とりあえず作った感ある雑さですがせっかくなので載せておきます

こんな感じのマクロでまずcsvをこさえます

画像2

----------------------------------------------------------------------------------------------------
-- setRaseSpecificSkillPriority関数
-- チャンミの条件別のスキル優先度設定をcsvファイルから参照して設定する
----------------------------------------------------------------------------------------------------
-- 引数:filename
--   参照するcsvファイル
-- 戻り値
--   csvファイルのコメント
-- ※デフォルトcsvのロード→ユーザー設定csvのロード→この関数の設定をロードの順にテーブルを上書きする
----------------------------------------------------------------------------------------------------
function setRaseSpecificSkillPriority(filename)


	local ret = ''
	local list = {}

	local f = io.open(filename, 'r')

	local j = 1	-- 読込行数
	for line in f:lines() do

		-- 1行目はコメント用
		if j == 1 then

			-- 戻り値
			ret = line
		end

		if j > 1 then

			g_skillInfo[line].stylePriority[STYLE.NIGE] = 1000
			g_skillInfo[line].stylePriority[STYLE.SENKOU] = 1000
			g_skillInfo[line].stylePriority[STYLE.SASHI] = 1000
			g_skillInfo[line].stylePriority[STYLE.OIKOMI] = 1000

		end

		j = j + 1


	end

	f:close()

	return ret

end

これをonStartTrainingで実行します

function onStartTraining()

	specificSkillAilias = setRaseSpecificSkillPriority('raceSpecificSkills.csv')
	print("■ 目標チャンピオンズミーティング:" .. specificSkillAilias)

end

予めこの関数の対象になるスキルの優先度はウマサポの設定で全部0にしておいてください。

 地固め習得を効率よく回したい

【背景】地固め因子を狙っていくときにSRエアグルーヴを編成してできるだけ手動で育成するときと同じような動きを再現したい。
例えば個人的には以下のような動きをしています。
①SRエアグルーヴのヒントを最優先にする※1,※2
②SRエアグルーヴから地固めを貰う確率を上げるために彼女から貰える他のスキルを最優先で習得する
※1.予めウマサポでサポートキャラクター一覧のヒント優先度を高くしておくか、上記で紹介しているcalcVaildHintscore関数のスコアの部分に係数を掛けるなりすればOK。
※2.強制出走レースは重賞ボーナス狙いのためエアグルーヴのヒントがあってもレースを優先しています
【実装先】getKizunaScore関数、getSkillScore関数
その内モジュール化しようかなと思いつつ現状やりたいのが地固めとSRエアグルーヴだけなのでハードコーディングしちゃってます。

まずエアグルーヴのヒントを踏むかどうかは上記で紹介したcalcVaildHintscore関数を使います。
挙動を説明すると以下の場合、地固めを習得済みかヒントを得ている場合はエアグルーヴのヒントを無視します。

function getKizunaScore( charaName, hintFlag , trainingInfo )

(中略)

-- ヒントマーク(!)によるスコアの算出
	local hintScore = 0
    if hintFlag then
        (中略)
        if charaName == "エアグルーヴ" then hintScore = calcVaildHintscore("エアグルーヴ [副会長の一刺し]",	{"地固め"}) end
    end

(中略)

	return score, hintScore
end

更にヒントを踏んだ際に予め地固め以外のスキルを埋めておくため、地固めを習得するかヒントを得るまでは他のスキルを優先的に習得していきます。

function getSkillScore( skillInfo, hintLevel )

	(中略)

	-- 一時的な記述(とりあえずの需要が地固めしかないので)
	-- 地固めを未習得またはヒント未獲得ならそれ以外のスキルの習得優先度を10倍にする
	local zigatame = 0					-- 地固め習得orヒント獲得フラグ
	if  #g_status[STATUS.ACQUIRE_SKILL] > 0 then
		for i = 1, #g_status[STATUS.ACQUIRE_SKILL] do

			if g_status[STATUS.ACQUIRE_SKILL][i].name == "地固め" then
				zigatame = 1
				break
			end
		end
	end

	-- 地固めのヒントを獲得しているかどうか
	if g_skillInfo["地固め"].hintLevel then zigatame = 1 end

    -- SRエアグルーヴに練習で貰える地固め以外のヒント一覧を定義
	local airgrooveHints = {"中距離直線○", "中距離直線◎", "追込ためらい", "追込焦り", "テンポアップ", "ペースキープ"}

	for i = 1, #airgrooveHints do
	
        -- 地固めを獲得していないなら、SRエアグルーヴの他のヒントの習得優先度を10倍にする
		if zigatame == 0 then
			if skillInfo.name == airgrooveHints[i] then score = score * 10 end

		elseif zigatame == 1 then
			if skillInfo.name == airgrooveHints[i] then score = score end

		end
	end

	return score
end

改良点としてはエアグルーヴのヒントは来たのに体力不十分で踏みに行けないシチュを避けるために通常時は体力温存しつつ、エアグルーヴのヒントだけはある程度の失敗率を許容して踏みに行くようにすることでしょうか。getActionあたりに仕込めばできそう。

 SS2B育成か4B育成を判定してそれぞれに合う育成をしたい

最近全然更新できてないので普段使ってる設定でまだ記事化してないものを追加
【背景】SS2B育成と4B育成はステの盛り方が異なるので、目標値から判定してそれぞれの育成モードに分けてトレーニング評価を設定したい
【実装先】getStatusScore関数
やってることとしては、
①目標値に1100以上が設定されていればSSと判断して評価モードを分岐
②4Bでは基本デフォルト通り
③SSの場合は育成序盤にSS目標パラメータの目標値を低めに再設定することで、序盤のステの偏りを防いでいます。特にスピードが低すぎて出走レースで負けやすくなってしまう問題への対応です。

----------------------------------------------------------------------------------------------------
-- 事前設定
-- ウマサポのUIで設定する目標値とは別にこの値さえ超えていればいい、というパラメータを設定しておく
-- goal_xxxx ⇒ 各パラメータの必須目標
-- lastBonus ⇒ 最後のトレーニング以降に自動的に見込める値(URA決勝+シナリオ+友人等)
----------------------------------------------------------------------------------------------------
lastBonus = 30

-- SS2B育成の例
goal_speed = 600
goal_stamina = 1100
goal_power = 600
goal_guts = 300
goal_int = 300

-- 4B育成の例
goal_speed = 600
goal_stamina = 600
goal_power = 600
goal_guts = 300
goal_int = 600

-- 最後のトレーニング+URA決勝後のボーナス分で目標値になっていればよい
target_speed = goal_speed - lastBonus
target_stamina = goal_stamina - lastBonus
target_power = goal_power - lastBonus
target_guts = goal_guts - lastBonus
target_int = goal_int - lastBonus

----------------------------------------------------------------------------------------------------
-- getStatusScore関数
-- ステータスによるスコアの算出をする
-- トレーニングとイベント選択肢でのステータス上昇によるスコア算出時に呼び出される
----------------------------------------------------------------------------------------------------
function getStatusScore( statusName, trainingInfo )

	local score = 0

	-- キャラクター情報を保持
	local characterInfo = g_characterInfo[g_status[STATUS.NAME]]

	-- 体力の場合
	if statusName == STATUS.HP then

		-- スコアの設定の体力で設定している値
		score = SCORE.HP

	-- 最大体力の場合
	elseif statusName == STATUS.MAXHP then

		-- スコアの設定の最大体力で設定している値
		score = SCORE.MAXHP

	-- やる気の場合
	elseif statusName == STATUS.MOTIVATION then

		-- スコアの設定のやる気で設定している値
		score = SCORE.MOTIVATION

	-- スキルポイントの場合
	elseif statusName == STATUS.SKILLPT then

		-- スコアの設定のスキルポイントで設定している値
		score = SCORE.SKILLPT


		-- 優先度によるスコア補正
		local statusPriority = characterInfo.statusPriority[statusName]
		score = score * statusPriority / 100


	-- それ以外(メインステータス)の場合
	else

		-- 賢さの目標値を高めにしておく場合、序盤から賢さを踏み過ぎてスピスタパワが通常より低くなりやすくなるため事故率が上がる
		-- 賢さは最後に帳尻が合えばいいので、URAファイナルまでは目標値を下げる
		if g_status[STATUS.SEASON].index < SEASON.JUNIOR_12B.index then

			if target_int > 300 then
				characterInfo.statusTarget[STATUS.INT] = target_int - 150
			else
				characterInfo.statusTarget[STATUS.INT] = 250
			end

		elseif g_status[STATUS.SEASON].index < SEASON.CLASSIC_12B.index then

			characterInfo.statusTarget[STATUS.INT] = target_int

		elseif g_status[STATUS.SEASON].index < SEASON.SENIOR_12B.index then

			characterInfo.statusTarget[STATUS.INT] = target_int + 50

		end

		-- 根性も同様
		if g_status[STATUS.SEASON].index < SEASON.JUNIOR_12B.index then

			if target_guts > 300 then
				characterInfo.statusTarget[STATUS.GUTS] = target_guts - 300
			else
				characterInfo.statusTarget[STATUS.GUTS] = 200
			end

		elseif g_status[STATUS.SEASON].index < SEASON.CLASSIC_12B.index then

			characterInfo.statusTarget[STATUS.GUTS] = target_guts - 100

		elseif g_status[STATUS.SEASON].index < SEASON.SENIOR_12B.index then

			characterInfo.statusTarget[STATUS.GUTS] = target_guts - 20

		end

		-- 因子育成でSSを狙う場合
		if math.max(goal_speed, goal_stamina, goal_power, goal_guts, goal_int) >= 1100 then

			-- SSを狙うパラメータ(念のため複数ある場合の優先度は パワー>スタミナ>賢さ>根性>スピード)
			local SSparam
			if goal_speed >= 1100 then SSparam = STATUS.SPEED end
			if goal_guts >= 1100 then SSparam = STATUS.GUTS end
			if goal_int >= 1100 then SSparam = STATUS.INT end
			if goal_stamina >= 1100 then SSparam = STATUS.STAMINA end
			if goal_power >= 1100 then SSparam = STATUS.POWER end
			
			if statusName == SSparam then

				controlmode = SSparam .. "SS育成"

				if g_status[STATUS.SPEED] < 400 then

					-- 一時的にSS狙いパラメータの目標値を400に制限する
					score = (400 - g_status[statusName]) / 10

				elseif g_status[STATUS.SPEED] < 500 then

					-- 一時的にSS狙いパラメータの目標値を700に制限する
					score = (700 - g_status[statusName]) / 10

				else

					-- 規定値を超えたら狙いの値になるまでは評価1.0倍、超えたら0
					-- ステータスの目標値から現在値を引いて10で割った値をスコアにする
					score = (characterInfo.statusTarget[statusName] - g_status[statusName]) / 10

					if g_status[statusName] >= 1100 - lastBonus then
						score = 0
					else
						score = score * 1.0
					end

				end
			else
				score = (characterInfo.statusTarget[statusName] - g_status[statusName]) / 10
			end
		else

			controlmode = "4B/5B育成"

			-- SS狙いがなければ普通にスコア計算
			score = (characterInfo.statusTarget[statusName] - g_status[statusName]) / 10

		end


		-- トレーニングレベルによるスコア補正
		-- トレーニング回数がLvに直結するURAシナリオのみ
		if CONFIG.AUTO_MODE.SCENARIO == SCENARIO.URA then
		
			-- 対象ステータスのトレーニングレベルが低いほど多く倍率がかかる(例:トレーニングレベル1の場合はスコア140%)
			local trainingLevel = g_status[STATUS.TRAINING_LEVEL][statusName]

			-- 夏合宿中はレベル5になる
			if (g_status[STATUS.SEASON].index >= SEASON.CLASSIC_7A.index and g_status[STATUS.SEASON].index <= SEASON.CLASSIC_8B.index) or
				(g_status[STATUS.SEASON].index >= SEASON.SENIOR_7A.index and g_status[STATUS.SEASON].index <= SEASON.SENIOR_8B.index) then
			
				local trainingLevel = 5

			end

			score = score * (15 - trainingLevel) / 10

		end

		-- 優先度によるスコア補正
		local statusPriority = characterInfo.statusPriority[statusName]
		score = score * statusPriority / 100


		-- ステータスボーナスによるスコア補正
		-- ボーナスがあると実質的に欲しいステータスを目指す際に過剰になりやすいため、ボーナスの分だけスコアを下方補正する
		score = score * (100 - g_status[STATUS.GROWTH][statusName]) / 100


		-- 0から100までの範囲内に収める
		if score > 100 then
			score = 100
		elseif score < 0 then
			score = 0
		end
	end

	return score
end


いいなと思ったら応援しよう!