ScoringEngine2
This commit is contained in:
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,170 @@
|
||||
--[[DO NOT MODIFY THIS FILE.
|
||||
You can add new scoring modes just by creating a different file and adding them to the
|
||||
ScoringModes table. Replacing this file will cause additions to be lost during updates.
|
||||
It's unnecessary and problematic.]]
|
||||
|
||||
ScoringModes={}
|
||||
|
||||
local function CreateOldScoringShim(name)
|
||||
return function(judg,pss,player,mode)
|
||||
judg,pss,player,mode=coroutine.yield()
|
||||
while true do
|
||||
if mode=="update" then
|
||||
Scoring[name](judg,pss)
|
||||
judg,pss,player,mode=coroutine.yield(pss:GetScore(),pss:GetCurMaxScore())
|
||||
elseif mode=="finalize" then
|
||||
return pss:GetScore(),pss:GetCurMaxScore()
|
||||
else
|
||||
error("scoring shim for mode "..name.." got an unknown state.")
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local ZeroIfNotFound = { __index = function() return 0 end; };
|
||||
|
||||
function GetDirectRadar(player)
|
||||
return GAMESTATE:GetCurrentSteps(player):GetRadarValues(player)
|
||||
end
|
||||
|
||||
--The code here is an example of native modes.
|
||||
|
||||
--[[There are three operation modes:
|
||||
init mode: The JudgmentCommand is invalid in this mode.
|
||||
This mode is designed to allow you to prepare your scoring mode.
|
||||
update mode: all are valid.
|
||||
This lets you respond to score changes.
|
||||
finalize mode: JudgementCommand invalid.
|
||||
This is designed to let you give score bonuses.
|
||||
]]
|
||||
--[[
|
||||
ScoringModes["Oldschool"]=function(judg,pss,player,mode)
|
||||
local tscore = 0
|
||||
local tmaxscore = 1000000000
|
||||
local possibilities= {
|
||||
['TapNoteScore_W1']=999,
|
||||
['TapNoteScore_W2']=IsW1Allowed('TapNoteScore_W2') and 888 or 999,
|
||||
['TapNoteScore_W3']=777,
|
||||
['TapNoteScore_W4']=555,
|
||||
['TapNoteScore_W5']=111,
|
||||
};
|
||||
assert(mode=="init","issues")
|
||||
setmetatable(possibilities, {__index=function() return 0 end;});
|
||||
judg,pss,player,mode = coroutine.yield()
|
||||
--holy crap it's a state machine i guess!
|
||||
while true do
|
||||
if mode=="update" then
|
||||
tscore=tscore+((pss:GetCurrentCombo()*111)^1.1)+ possibilities[judg.TapNoteScore] + (judg.HoldNoteScore == 'HoldNoteScore_Held' and 777 or 0)
|
||||
judg,pss,player,mode = coroutine.yield(tscore,tmaxscore)
|
||||
end
|
||||
if mode=="finalize" then
|
||||
break
|
||||
end
|
||||
end
|
||||
return tscore,tmaxscore
|
||||
end
|
||||
|
||||
ScoringModes['DDRMAX']=function(judge,pss,player,mode)
|
||||
--[en] in init mode, you start here. judge will be nil.
|
||||
--[jp]
|
||||
local multLookup = {
|
||||
TapNoteScore_W1=10,
|
||||
TapNoteScore_W2=10,
|
||||
TapNoteScore_W3=5 }
|
||||
local notesToIgnore = {
|
||||
TapNoteScore_CheckpointHit=true,
|
||||
TapNoteScore_CheckpointMiss=true,
|
||||
TapNoteScore_HitMine=true,
|
||||
TapNoteScore_AvoidMine=true,
|
||||
TapNoteScore_None=true}
|
||||
local radarCategoriesAndBonuses={
|
||||
RadarCategory_Stream=20000000,
|
||||
RadarCategory_Air=10000000,
|
||||
RadarCategory_Voltage=10000000,
|
||||
RadarCategory_Chaos=10000000,
|
||||
RadarCategory_Freeze=10000000}
|
||||
setmetatable(multLookup,ZeroIfNotFound)
|
||||
local numNoteObjects = GetDirectRadar(player):GetValue('RadarCategory_TapsAndHolds')
|
||||
local base = 5000000
|
||||
local curstep = 0
|
||||
local score = 0
|
||||
local bestscore = 0
|
||||
local s = numNoteObjects<1 and 0 or ((numNoteObjects/2)*(1+numNoteObjects))
|
||||
--[en] coroutine.yield() acts somewhat like return, but when the function is run by UpdateScoreKeepers() it will start here.
|
||||
--[jp] xxx
|
||||
judge,pss,player,mode = coroutine.yield()
|
||||
--[en] a while loop is only one way to make the mode selection system, but it's probably the easiest.
|
||||
--ideally, this would just be 'while mode=="update" do', with finalize stuff after it,
|
||||
--but I had written the code already when I thought of that.
|
||||
--[jp] xxx
|
||||
while true do
|
||||
--update mode code
|
||||
if mode == "update" then
|
||||
--[en] this filters out holds and spurious judgments. DDRMAX ignores holds for scoring purposes.
|
||||
--[jp] xxx
|
||||
if (not notesToIgnore[judge.TapNoteScore]) and judge.HoldNoteScore==nil then
|
||||
curstep=curstep+1
|
||||
if curstep>numNoteObjects then error "is bery bad" end
|
||||
score=score+(pss:GetFailed() and 1 or (multLookup[judge.TapNoteScore]*math.floor(base/s)*curstep))+((curstep==numNoteObjects and pss:FullComboOfScore('TapNoteScore_W2')) and (10*(base-(math.floor(base/s)*numNoteObjects))) or 0)
|
||||
bestscore = (curstep==numNoteObjects and 50000000 or bestscore+(10*math.floor(base/s)*curstep))
|
||||
end
|
||||
judge,pss,player,mode=coroutine.yield(score,bestscore)
|
||||
end
|
||||
if mode == "finalize" then
|
||||
--[en] this code would work, but SM5 behavior prevents it from doing anything. it should give the groove radar bonuses.
|
||||
--[jp] xyz
|
||||
local actradar = pss:GetRadarActual()
|
||||
local possradar = pss:GetRadarPossible()
|
||||
for k,v in pairs(radarCategoriesAndBonuses) do
|
||||
local thispossradar=possradar:GetValue(k)
|
||||
if thispossradar>0 then
|
||||
score=score+math.floor((actradar:GetValue(k)/thispossradar)*thispossradar*v)
|
||||
bestscore=bestscore+(thispossradar*v)
|
||||
end
|
||||
end
|
||||
break
|
||||
end
|
||||
end
|
||||
return score,bestscore
|
||||
end]]
|
||||
|
||||
for k,v in pairs(Scoring) do
|
||||
if ScoringModes[k] == nil then
|
||||
ScoringModes[k] = CreateOldScoringShim(k)
|
||||
end
|
||||
end
|
||||
|
||||
function UserPrefScoringMode()
|
||||
local baseChoices = {}
|
||||
for k,v in pairs(ScoringModes) do table.insert(baseChoices,k) end
|
||||
if next(baseChoices) == nil then UndocumentedFeature "No scoring modes available" end
|
||||
local t = {
|
||||
Name = "UserPrefScoringMode";
|
||||
LayoutType = "ShowAllInRow";
|
||||
SelectType = "SelectOne";
|
||||
OneChoiceForAllPlayers = true;
|
||||
ExportOnChange = false;
|
||||
Choices = baseChoices;
|
||||
LoadSelections = function(self, list, pn)
|
||||
if ReadPrefFromFile("UserPrefScoringMode") ~= nil then
|
||||
--Load the saved scoring mode from UserPrefs.
|
||||
local theValue = ReadPrefFromFile("UserPrefScoringMode");
|
||||
local success = false;
|
||||
--HACK: Preview 4 took out 1st and 4th scoring. Replace with a close equivalent.
|
||||
if theValue == "DDR 1stMIX" or theValue == "DDR 4thMIX" then theValue = "Oldschool" end
|
||||
--Search the list of scoring modes for the saved scoring mode.
|
||||
for k,v in ipairs(baseChoices) do if v == theValue then list[k] = true success = true break end end;
|
||||
--We couldn't find it, pick the first available scoring mode as a sane default.
|
||||
if success == false then list[1] = true end;
|
||||
else
|
||||
WritePrefToFile("UserPrefScoringMode", baseChoices[1]);
|
||||
list[1] = true;
|
||||
end;
|
||||
end;
|
||||
SaveSelections = function(self, list, pn)
|
||||
for k,v in ipairs(list) do if v then WritePrefToFile("UserPrefScoringMode", baseChoices[k]) break end end;
|
||||
end;
|
||||
};
|
||||
setmetatable( t, t );
|
||||
return t;
|
||||
end
|
||||
@@ -0,0 +1,118 @@
|
||||
--ScoringEngine2 by FSX v1.41
|
||||
--essentially a really chunky layer to make SetScore elegant(ish)
|
||||
--arguments for scorekeepers: JudgmentMessageCommand,PlayerStageStats,PlayerNumber,State
|
||||
--[[changelog
|
||||
v1.41 6 May 2012
|
||||
*Not really sure.
|
||||
*Included in SM5 for alpha 3.
|
||||
v1.4 18 Apr 2012
|
||||
*Support for judgment filtering.
|
||||
*Minor changes.
|
||||
*First public release (in SE2 Dev Kit) (didn't happen)
|
||||
v1.3 17 Apr 2012
|
||||
*First complete release.
|
||||
*Error reporting in FinalizeScoreKeepers()
|
||||
*Bug fixes.]]
|
||||
--scoring modes written for the old ad-hoc system don't work, but don't cause crashes either.
|
||||
--SM5 has some glue code for them, though
|
||||
local stageKey = nil
|
||||
local ReadiedScoreKeepers = {}
|
||||
local JudgmentFilters = {}
|
||||
|
||||
local function CoroutineIsGood(cr)
|
||||
return coroutine.status(cr)=="suspended"
|
||||
end
|
||||
|
||||
function InitScoreKeepers(p1Mode, p2Mode)
|
||||
local newStgK = {pcall(GameState.GetStageSeed,GAMESTATE)}
|
||||
assert(newStgK[1], "it's too early to initialize the ScoreKeepers, the GameState isn't ready yet")
|
||||
if newStgK[2] == stageKey then
|
||||
Log "[InitScoreKeepers] unnecessary call"
|
||||
return true
|
||||
end
|
||||
ReadiedScoreKeepers={}
|
||||
JudgmentFilters={}
|
||||
stageKey = newStgK
|
||||
local intab = {}
|
||||
if p1Mode ~= nil then
|
||||
intab.P1 = p1Mode
|
||||
end
|
||||
if p2Mode ~= nil then
|
||||
intab.P2 = p2Mode
|
||||
end
|
||||
assert(next(intab), "InitScoreKeepers didn't get any args")
|
||||
for k,v in pairs(intab) do
|
||||
if not ScoringModes[v] then
|
||||
--SPOILER: UndocumentedFeature crashes the game intentionally.
|
||||
UndocumentedFeature(k.."'s scoring mode \""..v.."\" doesn't exist.")
|
||||
end
|
||||
ReadiedScoreKeepers[k] = coroutine.create(ScoringModes[v])
|
||||
--call each scoring mode one time in init mode
|
||||
checks={coroutine.resume(ReadiedScoreKeepers[k],nil,STATSMAN:GetCurStageStats():GetPlayerStageStats("PlayerNumber_"..k), "PlayerNumber_"..k, "init")}
|
||||
if not CoroutineIsGood(ReadiedScoreKeepers[k]) then SCREENMAN:SystemMessage("scoring init error: "..checks[2]) return false end
|
||||
--very very very very safe filtering rule load code
|
||||
if type(checks[2])=="table" then
|
||||
if type(checks[2][1])..type(checks[2][2]) == "tabletable" then
|
||||
local isGood=false
|
||||
local tapscan = Enum.Reverse(TapNoteScore)
|
||||
local holdscan = Enum.Reverse(HoldNoteScore)
|
||||
local finaltable = {{},{}}
|
||||
for i=1,2 do
|
||||
for k,v in pairs(checks[2][i]) do
|
||||
finaltable[i][v]=true
|
||||
if (not (i==2 and holdscan[v] or tapscan[v])) or v==nil then isGood=false break end
|
||||
isGood=true
|
||||
end
|
||||
if not isGood then break end
|
||||
end
|
||||
if isGood then
|
||||
JudgmentFilters[k]=finaltable
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
function UpdateScoreKeepers(JMC)
|
||||
assert(JMC,"pass args, please")
|
||||
runplayer = ToEnumShortString(JMC.Player)
|
||||
if ReadiedScoreKeepers[runplayer] then
|
||||
if JudgmentFilters[runplayer] and (JudgmentFilters[runplayer][1][JMC.TapNoteScore] or JudgmentFilters[runplayer][2][JMC.HoldNoteScore]) then
|
||||
return true
|
||||
end
|
||||
local pss = STATSMAN:GetCurStageStats():GetPlayerStageStats(JMC.Player)
|
||||
local newscores = {coroutine.resume(ReadiedScoreKeepers[runplayer],JMC,pss,JMC.Player,"update")}
|
||||
if CoroutineIsGood(ReadiedScoreKeepers[runplayer]) then
|
||||
pss:SetScore(newscores[2])
|
||||
pss:SetCurMaxScore(newscores[3])
|
||||
return true
|
||||
else
|
||||
SCREENMAN:SystemMessage("scoring update error: "..newscores[2])
|
||||
ReadiedScoreKeepers[runplayer]=nil
|
||||
return false
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function FinalizeScoreKeepers()
|
||||
for k,v in pairs(ReadiedScoreKeepers) do
|
||||
local pss = STATSMAN:GetCurStageStats():GetPlayerStageStats("PlayerNumber_"..k)
|
||||
local newscores = {coroutine.resume(v,nil,pss,"PlayerNumber_"..k,"finalize")}
|
||||
local status = coroutine.status(v)
|
||||
--kill the scorekeeper now, we don't need it again
|
||||
ReadiedScoreKeepers[k] = nil
|
||||
if status=="dead" then
|
||||
if newscores[1]==true then
|
||||
pss:SetScore(newscores[2])
|
||||
pss:SetCurMaxScore(newscores[3])
|
||||
else
|
||||
SCREENMAN:SystemMessage("scoring finalize error: "..newscores[2])
|
||||
return false
|
||||
end
|
||||
else
|
||||
return false
|
||||
end
|
||||
end
|
||||
return true
|
||||
end
|
||||
@@ -1,3 +1,7 @@
|
||||
if not GAMESTATE:IsCourseMode() then
|
||||
FinalizeScoreKeepers()
|
||||
end
|
||||
|
||||
local function GraphDisplay( pn )
|
||||
local t = Def.ActorFrame {
|
||||
Def.GraphDisplay {
|
||||
|
||||
@@ -215,8 +215,7 @@ if( not GAMESTATE:IsCourseMode() ) then
|
||||
params.TapNoteScore ~= 'TapNoteScore_Invalid' and
|
||||
params.TapNoteScore ~= 'TapNoteScore_None'
|
||||
then
|
||||
Scoring[GetUserPref("UserPrefScoringMode")](params,
|
||||
STATSMAN:GetCurStageStats():GetPlayerStageStats(params.Player))
|
||||
UpdateScoreKeepers(params)
|
||||
end
|
||||
end;
|
||||
};
|
||||
|
||||
@@ -77,4 +77,10 @@ t[#t+1] = Def.ActorFrame {
|
||||
};
|
||||
};
|
||||
|
||||
--Get scoring ready.
|
||||
if not GAMESTATE:IsCourseMode() then
|
||||
InitScoringModes((GAMESTATE:IsSideJoined(PLAYER_1) and GetUserPref("UserPrefScoringMode")),
|
||||
(GAMESTATE:IsSideJoined(PLAYER_2) and GetUserPref("UserPrefScoringMode"))
|
||||
end
|
||||
|
||||
return t
|
||||
Reference in New Issue
Block a user