diff --git a/BackgroundEffects/StretchNoLoop.lua b/BackgroundEffects/StretchNoLoop.lua index c48fe01b0a..0051588fea 100644 --- a/BackgroundEffects/StretchNoLoop.lua +++ b/BackgroundEffects/StretchNoLoop.lua @@ -2,7 +2,17 @@ local Color1 = color(Var "Color1"); local t = Def.ActorFrame { LoadActor(Var "File1") .. { - OnCommand=cmd(x,SCREEN_CENTER_X;y,SCREEN_CENTER_Y;scale_or_crop_background;diffuse,Color1;loop,false;effectclock,"music"); + OnCommand=function(self) + self:xy(SCREEN_CENTER_X,SCREEN_CENTER_Y) + self:scale_or_crop_background() + self:diffuse(Color1) + if self.loop ~= nil then + self:loop(false) + -- make videos start at beginning to prevent sticking on last frame + self:position(0) + end + self:effectclock("music") + end; GainFocusCommand=cmd(play); LoseFocusCommand=cmd(pause); }; diff --git a/BackgroundEffects/StretchRewind.lua b/BackgroundEffects/StretchRewind.lua index 2e48a26010..9d649131f2 100644 --- a/BackgroundEffects/StretchRewind.lua +++ b/BackgroundEffects/StretchRewind.lua @@ -2,7 +2,15 @@ local Color1 = color(Var "Color1"); local t = Def.ActorFrame { LoadActor(Var "File1") .. { - OnCommand=cmd(x,SCREEN_CENTER_X;y,SCREEN_CENTER_Y;scale_or_crop_background;diffuse,Color1;position,0;effectclock,"music"); + OnCommand=function(self) + self:xy(SCREEN_CENTER_X,SCREEN_CENTER_Y) + self:scale_or_crop_background() + self:diffuse(Color1) + if self.position ~= nil then + self:position(0) + end + self:effectclock("music") + end; GainFocusCommand=cmd(play); LoseFocusCommand=cmd(pause); }; diff --git a/Docs/Changelog_sm5.txt b/Docs/Changelog_sm5.txt index 5407c5f707..a1c06e7e9f 100644 --- a/Docs/Changelog_sm5.txt +++ b/Docs/Changelog_sm5.txt @@ -4,22 +4,47 @@ The StepMania 5 Changelog covers all post-sm-ssc changes. For a list of changes from StepMania 4 alpha 5 to sm-ssc v1.2.5, see Changelog_sm-ssc.txt. ________________________________________________________________________________ +2014/11/15 +---------- +* [Preferences] Default Fail Type preference mechanism changed internally again. + Set your Default Fail Type preference again. + +2014/11/05 +---------- +* [ScreenPrompt] Answer OnCommand metrics fixed to actually work. + +2014/11/01 +---------- +* [RollingNumbers] Cropping and color during tweens fixed. + +2014/10/23 +---------- +* [Global] approach, multiapproach, lerp, and lerp_color lua functions added. + +2014/10/20 +---------- +* [StageStats] GetStepsSeconds function added. +* [Steps] If an unrecognized step type is saved, preserve that step instead of + deleting it. A warning will be placed in the log file during song load. + ================================================================================ StepMania 5.0 beta 4a | 20141015 -------------------------------------------------------------------------------- +2014/10/19 +---------- +* [BackgroundEffects] Fixed errors in StretchNoLoop and StretchRewind. + 2014/10/13 ---------- -* [NoteDisplay] Var Player and Var Controller work for non-receptor arrows. +* [NoteDisplay] Var Player and Var Controller work for non-receptor arrows. [hanubeki] * [Mac OS X] Allow StepMania to be built and run in Yosemite. 2014/10/11 ---------- * [NoteDisplay] Add two noteskin metrics, {PartName}NoteColorType and -{PartName}NoteColorCount. View https://github.com/stepmania/stepmania/pull/328 -for more information. -* [Steps] If an unrecognized step type is saved, preserve that step instead of -deleting it. A warning will be placed in the log file during song load. + {PartName}NoteColorCount. View https://github.com/stepmania/stepmania/pull/328 + for more information. [hanubeki] ================================================================================ StepMania 5.0 beta 4 | 20140930 diff --git a/Docs/Luadoc/Lua.xml b/Docs/Luadoc/Lua.xml index 56e03b147b..ab33c6e73d 100644 --- a/Docs/Luadoc/Lua.xml +++ b/Docs/Luadoc/Lua.xml @@ -203,6 +203,7 @@ + @@ -219,11 +220,14 @@ + + + @@ -1584,6 +1588,7 @@ + diff --git a/Docs/Luadoc/LuaDocumentation.xml b/Docs/Luadoc/LuaDocumentation.xml index e92ed90d92..c6209eb1d4 100644 --- a/Docs/Luadoc/LuaDocumentation.xml +++ b/Docs/Luadoc/LuaDocumentation.xml @@ -31,6 +31,10 @@ save yourself some time, copy this for undocumented things: [02 Colors.lua] Returns a color with the specified alpha. + + Use this to make a current value approach a goal value at the given speed. Speed must not be negative. The value will not overshoot the goal.
+ Note: When you see the error "approach: speed 1 is negative." it means that the speed value passed was negative. The 1 is there because approach and multiapproach use the same internal function and can be ignored when using approach. +
[03 CustomSpeedMods.lua] @@ -136,7 +140,7 @@ save yourself some time, copy this for undocumented things: [03 Gameplay.lua]
- [02 Utilities.lua] + [02 Utilities.lua] Old name for approach. [02 Utilities.lua] Return the index of a true value in list. @@ -327,6 +331,12 @@ save yourself some time, copy this for undocumented things: [02 Colors.lua] + + Returns a number linearly interpolated between start and end by percent. + + + Same as lerp, but for colors. All channels will reach the end of the interpolation at the same time. + Returns an Actor definition for the actor at sPath. If sPath points to a Lua file, any additional arguments will be passed to that script. @@ -369,6 +379,11 @@ save yourself some time, copy this for undocumented things: Returns Month m as a string. + + Similar to approach, but operates on tables of values instead of single values. This will modify the contents of currents in place, as well as returning currents.
+ currents, goals, and speeds must all be the same size and contain only numbers.
+ Note: When you see the error "approach: speed 1 is negative." it means that a speed value passed was negative. The 1 tells you which entry in the table was invalid. +
"Allows a program to traverse all fields of a table. Its first argument is a table and its second argument is an index in this table. @@ -969,7 +984,7 @@ save yourself some time, copy this for undocumented things: Sets the Actor's alpha level to fAlpha, where fAlpha is in the range 0..1. - Makes the Actor switch between two colors immediately. + Makes the Actor switch between two colors immediately. See Themerdocs/effect_colors.txt for an example. Sets the Actor's bottom edge color to c. @@ -987,13 +1002,13 @@ save yourself some time, copy this for undocumented things: Sets the Actor's lower right corner color to c. - Makes the Actor switch between two colors, jumping back to the first after reaching the second. + Makes the Actor switch between two colors, jumping back to the first after reaching the second. See Themerdocs/effect_colors.txt for an example. Sets the Actor's right edge color to c. - Makes the Actor shift between two colors smoothly. + Makes the Actor shift between two colors smoothly. See Themerdocs/effect_colors.txt for an example. Sets the Actor's top edge color to c. @@ -1129,13 +1144,13 @@ save yourself some time, copy this for undocumented things: Sets the Actor's glow color. - Makes the Actor glow between two colors immediately. + Makes the Actor glow between two colors immediately. See Themerdocs/effect_colors.txt for an example. - Makes the Actor glow between two colors smoothly, jumping back to the first at the end. + Makes the Actor glow between two colors smoothly, jumping back to the first at the end. See Themerdocs/effect_colors.txt for an example. - Makes the Actor glow between two colors smoothly. + Makes the Actor glow between two colors smoothly. See Themerdocs/effect_colors.txt for an example. Set the fractional horizontal alignment of the Actor according to fAlign which should be a float in the range 0..1. An alignment of 0 is left aligned while an alignment of 1 is right aligned. See for the common case. @@ -1192,7 +1207,7 @@ save yourself some time, copy this for undocumented things: Basically creates a command named !sMessageName (Note the ! at the beginning. The source code says this: "Hack: use "!" as a marker to broadcast a command, instead of playing a command, so we don't have to add yet another element to every tween state for this rarely-used command.") - Makes the Actor change colors continually using colors of the rainbow. + Makes the Actor change colors continually using colors of the rainbow. Each channel follows a cosine wave, red starts at 0, green starts at 2pi/3, and blue starts at 4pi/3. Sets the roll of this Actor to fRoll. @@ -2464,6 +2479,9 @@ save yourself some time, copy this for undocumented things: Returns the current stage index. + + Returns the current StepsSeconds, which is the time value used to set the samples in a player's life record. + Return the random seed for the current stage. diff --git a/Docs/Mapping_keys_for_edit_mode.txt b/Docs/Mapping_keys_for_edit_mode.txt new file mode 100644 index 0000000000..5f237910a9 --- /dev/null +++ b/Docs/Mapping_keys_for_edit_mode.txt @@ -0,0 +1,287 @@ +Don't try to change the way keys are mapped in edit mode unless you have a good reason. It's complicated and easy to mess up. +If you do make a mistake, you can go back to the default edit mode keymapping just by removing the "User Data Folder/Save/EditMode_Keymaps.ini" file. + +Edit Mode loads key mapping settings from "User Data Folder/Save/EditMode_Keymaps.ini". "User Data Folder" is a special name that means the folder where all your user data for stepmania is stored, the name depends on what OS you're on. See Docs/Userdocs/sm5_beginner.txt and search for "User Data Folder" to learn where it is on your OS. + +Entries in EditMode_Keymaps.ini set which what keys trigger an action. Any function that doesn't have an entry in EditMode_Keymaps.ini will use the default mapping. Be careful when choosing a key for a function to make sure that same key isn't already mapped to something else either in the default mapping or in the mapping you are creating. +If you hold F3, you can use the Debug Menu to reload the screen while in Edit Mode, which will reload the key mapping, so you can adjust the mapping while stepmania is running. + +EditMode_Keymaps.ini is broken up into four sections, one for each major mode that Edit Mode can be in. The "Edit" section is for when in normal editing mode. The "Play" section is for when Edit Mode is playing back a song. The "Record" section is for record mode, and the "RecordPaused" section is for when Record mode is paused. + +Each line in a section is the name of the function being set, followed by "=", then the name of the key for that function. You can list two keys by seperating them with a ":". Examples: +SNAP_NEXT=Key_s +SNAP_PREV=Key_d:Key_v + +Note that you cannot change which keys need to be held down for certain things, like holding shift for rolls. + +Next will be a list of function names (headed by "Function Names"), then a list of key names (headed by "Key Names"), then the default key mapping (headed by "Default Mapping") as an example. + +Function Names: +COLUMN_0 +COLUMN_1 +COLUMN_2 +COLUMN_3 +COLUMN_4 +COLUMN_5 +COLUMN_6 +COLUMN_7 +COLUMN_8 +COLUMN_9 +RIGHT_SIDE +LAY_ROLL +LAY_TAP_ATTACK +REMOVE_NOTE +CYCLE_TAP_LEFT +CYCLE_TAP_RIGHT +CYCLE_SEGMENT_LEFT +CYCLE_SEGMENT_RIGHT +SCROLL_UP_LINE +SCROLL_UP_PAGE +SCROLL_UP_TS +SCROLL_DOWN_LINE +SCROLL_DOWN_PAGE +SCROLL_DOWN_TS +SCROLL_NEXT_MEASURE +SCROLL_PREV_MEASURE +SCROLL_HOME +SCROLL_END +SCROLL_NEXT +SCROLL_PREV +SEGMENT_NEXT +SEGMENT_PREV +SCROLL_SELECT +LAY_SELECT +SCROLL_SPEED_UP +SCROLL_SPEED_DOWN +SNAP_NEXT +SNAP_PREV +OPEN_EDIT_MENU +OPEN_TIMING_MENU +OPEN_ALTER_MENU +OPEN_AREA_MENU +OPEN_BGCHANGE_LAYER1_MENU +OPEN_BGCHANGE_LAYER2_MENU +OPEN_COURSE_MENU +OPEN_COURSE_ATTACK_MENU +OPEN_STEP_ATTACK_MENU +ADD_STEP_MODS +OPEN_INPUT_HELP +BAKE_RANDOM_FROM_SONG_GROUP +BAKE_RANDOM_FROM_SONG_GROUP_AND_GENRE +PLAY_FROM_START +PLAY_FROM_CURSOR +PLAY_SELECTION +RECORD_FROM_CURSOR +RECORD_SELECTION +RETURN_TO_EDIT +INSERT +DELETE +INSERT_SHIFT_PAUSES +DELETE_SHIFT_PAUSES +OPEN_NEXT_STEPS +OPEN_PREV_STEPS +PLAY_SAMPLE_MUSIC +BPM_UP +BPM_DOWN +STOP_UP +STOP_DOWN +DELAY_UP +DELAY_DOWN +OFFSET_UP +OFFSET_DOWN +SAMPLE_START_UP +SAMPLE_START_DOWN +SAMPLE_LENGTH_UP +SAMPLE_LENGTH_DOWN +ADJUST_FINE +SAVE +UNDO +ADD_COURSE_MODS +SWITCH_PLAYERS +SWITCH_TIMINGS + + +Key Names: +(If you're really having trouble finding the name for a key, map it to something in the normal controller mapping screen and check Keymaps.ini to see what its name is.) +Most keys can be set with something like this: "Key_a", where "a" is the character typed by the key. This includes keys like ".", ",", and so on. For other keys, there are special names that can be used, which are listed below. "Key_." and "Key_period" both mean the same thing. +Some key names have spaces in them. + +Key_period +Key_comma +Key_space +Key_delete +Key_backspace +Key_tab +Key_enter +Key_pause +Key_escape +Key_F1 +Key_F2 +(Don't map F3, it's used for the debug menu, so probably won't work.) +Key_F4 +Key_F5 +Key_F6 +Key_F7 +Key_F8 +Key_F9 +Key_F10 +Key_F11 +Key_F12 +Key_F13 +Key_F14 +Key_F15 +Key_F16 +Key_left ctrl +Key_right ctrl +Key_left shift +Key_right shift +Key_left alt +Key_right alt +Key_left meta +Key_right meta +Key_left super +Key_right super +Key_menu +Key_function +Key_num lock +Key_scroll lock +Key_caps lock +Key_prtsc +Key_up +Key_down +Key_left +Key_right +Key_insert +Key_home +Key_end +Key_pgup +Key_pgdn +Key_KP 0 +Key_KP 1 +Key_KP 2 +Key_KP 3 +Key_KP 4 +Key_KP 5 +Key_KP 6 +Key_KP 7 +Key_KP 8 +Key_KP 9 +Key_KP / +Key_KP * +Key_KP - +Key_KP + +Key_KP . +Key_KP = +Key_KP enter + + +Default Mapping: +Comments are in this section to tell which things require a modifier key to be held. +Note that not all sections map all functions. This is because those functions aren't useful in all modes. +(if you spot a key name that is in all caps in this section, I missed it when editing at 4 AM and it's supposed to be lower case) + +[Edit] +SCROLL_UP_LINE=Key_up +SCROLL_DOWN_LINE=Key_down +SCROLL_UP_PAGE=Key_pgup:Key_; +SCROLL_DOWN_PAGE=Key_pgdn:Key_' +# Scrolling by timing segments actually requires holding Ctrl. +SCROLL_UP_TS=Key_pgup:Key_; +SCROLL_DOWN_TS=Key_pgdn:Key_' +SCROLL_HOME=Key_home +SCROLL_END=Key_end +SCROLL_NEXT=Key_period +SCROLL_PREV=Key_comma +# Hold Ctrl for SEGMENT_NEXT and SEGMENT_PREV. +SEGMENT_NEXT=Key_period +SEGMENT_PREV=Key_comma +SCROLL_SELECT=Key_left shift:Key_right shift +LAY_SELECT=Key_space +# Hold Ctrl for PLAY_FROM_START. +PLAY_FROM_START=Key_p +# Hold Shift for PLAY_FROM_CURSOR. +PLAY_FROM_CURSOR=Key_p +PLAY_SELECTION=Key_p +OPEN_TIMING_MENU=Key_F4 +# OPEN_PREV_STEPS/OPEN_NEXT_STEPS aren't allowed in home mode. It breaks the "delay creation until first save" logic. +OPEN_PREV_STEPS=Key_F5 +OPEN_NEXT_STEPS=Key_F6 +BPM_DOWN=Key_F7 +BPM_UP=Key_F8 +STOP_DOWN=Key_F9 +STOP_UP=Key_F10 +# Hold Shift for DELAY_DOWN and DELAY_UP. +DELAY_DOWN=Key_F9 +DELAY_UP=Key_F10 +OFFSET_DOWN=Key_F11 +OFFSET_UP=Key_F12 +SAMPLE_START_UP=Key_] +SAMPLE_START_DOWN=Key_[ +# Hold Shift for SAMPLE_LENGTH_UP and SAMPLE_LENGTH_DOWN. +SAMPLE_LENGTH_UP=Key_] +SAMPLE_LENGTH_DOWN=Key_[ +PLAY_SAMPLE_MUSIC=Key_l +OPEN_BGCHANGE_LAYER1_MENU=Key_b +# Hold Shift for OPEN_BGCHANGE_LAYER2_MENU. +OPEN_BGCHANGE_LAYER2_MENU=Key_b +# Hold Ctrl for INSERT_SHIFT_PAUSES and DELETE_SHIFT_PAUSES. +INSERT_SHIFT_PAUSES=Key_insert +DELETE_SHIFT_PAUSES=Key_DEL +COLUMN_0=Key_1 +COLUMN_1=Key_2 +COLUMN_2=Key_3 +COLUMN_3=Key_4 +COLUMN_4=Key_5 +COLUMN_5=Key_6 +COLUMN_6=Key_7 +COLUMN_7=Key_8 +COLUMN_8=Key_9 +COLUMN_9=Key_0 +# Yeah, you're screwed when editing techno-double8, bm-double5, or bm-double7, or any other style with more than 10 columns. +RIGHT_SIDE=Key_left alt:Key_right alt +LAY_ROLL=Key_left shift:Key_right shift +CYCLE_TAP_LEFT=Key_n +CYCLE_TAP_RIGHT=Key_m +# Hold Ctrl for CYCLE_SEGMENT_LEFT and CYCLE_SEGMENT_RIGHT. +CYCLE_SEGMENT_LEFT=Key_n +CYCLE_SEGMENT_RIGHT=Key_m +# Hold Ctrl for SCROLL_SPEED_UP and SCROLL_SPEED_DOWN +SCROLL_SPEED_UP=Key_up +SCROLL_SPEED_DOWN=Key_down +SCROLL_SELECT=Key_left shift:Key_right shift +SNAP_NEXT=Key_left +SNAP_PREV=Key_right +OPEN_EDIT_MENU=Key_escape +OPEN_AREA_MENU=Key_enter +OPEN_ALTER_MENU=Key_a +OPEN_INPUT_HELP=Key_F1 +# Hold Alt for BAKE_RANDOM_FROM_SONG_GROUP. +BAKE_RANDOM_FROM_SONG_GROUP=Key_b +# Hold Ctrl for BAKE_RANDOM_FROM_SONG_GROUP_AND_GENRE. +BAKE_RANDOM_FROM_SONG_GROUP_AND_GENRE=Key_b +# Hold Ctrl for RECORD_SELECTION. +RECORD_SELECTION=Key_r +INSERT=Key_insert:Key_\ +DELETE=Key_delete +ADJUST_FINE=Key_right alt:Key_left alt +# Hold Ctrl or Cmd (OS X) for SAVE. +SAVE=Key_s +UNDO=Key_u +SWITCH_PLAYERS=Key_/ +SWITCH_TIMINGS=Key_t + +[Play] +RETURN_TO_EDIT=Key_enter:Key_escape + +[Record] +LAY_ROLL=Key_left shift:Key_right shift +REMOVE_NOTE=Key_left alt:Key_right alt +RETURN_TO_EDIT=Key_escape:Key_enter + +[RecordPaused] +PLAY_SELECTION=Key_p +# Hold Ctrl for RECORD_SELECTION. +RECORD_SELECTION=Key_r +RECORD_FROM_CURSOR=Key_r +RETURN_TO_EDIT=Key_escape +UNDO=Key_u diff --git a/Docs/Themerdocs/announcer_files.txt b/Docs/Themerdocs/announcer_files.txt new file mode 100644 index 0000000000..1bb9d84aee --- /dev/null +++ b/Docs/Themerdocs/announcer_files.txt @@ -0,0 +1,84 @@ +This could use a bit nicer formatting, and something else should explain screen inheritance, but here's everything I was able to find on announcers from searching the source. +Each entry lists the event that triggers it, then the name of the folder that is searched for a sound to play. + + +Screen inheritance note: +The screen names listed below are actually the names of screen classes. Every screen has a class type, and some screen class types inherit from others. You can find the class type of a screen by looking at the metrics for the theme and reading the Class metric in a screen's metric group. +So sound names with "" in them will actually occur on any screen that inherits from that class type, and the actual name of the screen will be inserted into the string when the announcer system goes to play the sound. + + +ScreenNameEntry +(entering screen) "name entry" + +ScreenEnding +(entering screen) "music scroll" + +ScreenEvaluation +(entering screen): (only one will occur, listed in order of precedence) +"evaluation new record" +"evaluation full combo W1" +"evaluation full combo W2" +grade string values: "AAAA" (tier 1), "AAA" (tier 2), "AA" (tier 3), "A" (tier 4), "B" (tier 5), "C" (tier 6), "D" (tier 7), "E" (tier failed), "N" (tier nodata) +"evaluation final " (only in course mode on summary screen) +"evaluation win" (only in battle mode) +"evaluation lose" (only in battle mode) +"evaluation " (only in non-course, non-battle) +(after CheerDelaySeconds) "evaluation cheer" + +ScreenPlayerOptions +(entering screen) "player options intro" + +ScreenSelectCharacter +(entering screen) "select group intro" + +ScreenSelectMaster +(ScreenSelectMaster is the name of the class type of several different screens, so these will actually occur on any of the following: "ScreenTitleMenu", "ScreenSelectLanguage", "ScreenUnlockBrowse", "ScreenSelectStyle", "ScreenSelectPlayMode", "ScreenGameInformation", or any other screen that has "ScreenSelectMaster" as its Class metric) +(on moving to second page) "select difficulty challenge" +(side note: choices on ScreenSelectMaster are split into two pages, all choices after the amount set by the NumChoicesOnPage1 metric are on the second page. Default is 1024, so this will never play unless the theme sets the metric lower to have a second page) +(on picking a choice) " comment " +( will be the full name of the screen, will be the name field of the gamecommand that defines the choice, so this could be literally anything the themer feels like) + +ScreenSelect +(every IdleCommentSeconds) " IdleComment" + +ScreenTitleMenu +(entering screen) "title menu game name" + +ScreenWithMenuElements +(entering screen) " intro" + +ScreenSelectMusic +(entering screen) "select music intro" +(every IdleCommentSeconds) " IdleComment" +(on picking a song played already this round) "select music comment repeat" +(on picking a song never played before) "select music comment new" +(on picking a song with a meter >= 10) "select music comment hard" +(on picking a song that is not one of the above) "select music comment general" +(on picking a course) "select course comment general" + +ScreenGameplay +(entering screen) "gameplay intro" +(after fading in) "gameplay ready" +(on song start extra stage) "gameplay here we go extra" +(on song start final stage) "gameplay here we go final" +(on song start non-final non-extra stage) "gameplay here we go normal" +(on enemy death in battle mode?) "gameplay battle damage level3" +(on finishing gameplay, earned extra) "gameplay extra" +(on finishing gameplay, not earned extra) "gameplay cleared" +(when failing over halfway through course) "gameplay oni failed halfway" +(when failing less than halfway through course) "gameplay oni failed" +(when failing non-course) "gameplay failed" +(following separated by SecondsBetweenComments metric) +(when one life meter is hot) "gameplay comment hot" +(when one life meter is in danger) "gameplay comment danger" +(when neither of the above two) "gameplay comment good" +(in nonstop, oni, or endless mode) "gameplay comment oni" +(every hundred combos) "gameplay combo" ( ranges from 100 to 1000) +(combo stopped) "gameplay combo stopped" +(combo continuing (every hundred over 1k)) "gameplay combo overflow" +(when a battle trick occurs) "gameplay battle trick level" ( can be 1, 2, or 3) +(when battle damage occurs) "gameplay battle damage level" ( can be 1, 2, or 3) + +(when a menu timer runs low) "hurry up" + +(whenever the theme calls SOUND:PlayAnnouncer("")) "" diff --git a/Docs/Themerdocs/effect_colors.txt b/Docs/Themerdocs/effect_colors.txt new file mode 100644 index 0000000000..4ebecb1ba8 --- /dev/null +++ b/Docs/Themerdocs/effect_colors.txt @@ -0,0 +1,16 @@ +General note on functions that use effect colors: +diffuseblink, diffuseramp, diffuseshift, glowblink, glowramp, and glowshift all use the two effect colors. +They set the effect colors to white when called, so you should call effectcolor1 and effectcolor2 afterwards to set the effect colors to the colors you want. +You should also call effectperiod to set the effect period if you don't want the default of 1. + +Example: +Def.Quad{ + Name= "glow quad test", InitCommand= function(self) + self:setsize(40, 40) + self:glowshift() + self:effectcolor1(color("#dc322f")) + self:effectcolor2(color("#2aa198")) + self:effectperiod(4) + self:xy(_screen.cx, _screen.cy) + end +} diff --git a/Themes/_fallback/Scripts/02 Colors.lua b/Themes/_fallback/Scripts/02 Colors.lua index 3fe7335e3d..31b2036ba6 100644 --- a/Themes/_fallback/Scripts/02 Colors.lua +++ b/Themes/_fallback/Scripts/02 Colors.lua @@ -191,8 +191,6 @@ end -- ColorToHex(c) -- Takes in a normal color("") and returns the hex representation. --- Adapted from code in LuaBit (http://luaforge.net/projects/bit/), --- which is MIT licensed and copyright (C) 2006~2007 hanzhao. function ColorToHex(c) local r = c[1] local g = c[2] @@ -200,27 +198,7 @@ function ColorToHex(c) local a = HasAlpha(c) local function hex(value) - value = math.ceil(value) - - local hexVals = { 'A', 'B', 'C', 'D', 'E', 'F' } - local out = "" - local last = 0 - - while(value ~= 0) do - last = math.mod(value, 16) - if(last < 10) then - out = tostring(last) .. out - else - out = hexVals[(last-10)+1] .. out - end - value = math.floor(value/16) - end - - if(out == "") then - return "00" - end - - return string.format( "%02X", tonumber(out,16) ) + return ("%02X"):format(value) end local rX = hex( scale(r, 0, 1, 0, 255) ) diff --git a/Themes/_fallback/Scripts/02 Utilities.lua b/Themes/_fallback/Scripts/02 Utilities.lua index 4750c48b7d..53a3acf534 100644 --- a/Themes/_fallback/Scripts/02 Utilities.lua +++ b/Themes/_fallback/Scripts/02 Utilities.lua @@ -66,6 +66,8 @@ function wrap(val,n) end function fapproach(val, other_val, to_move) + -- This does not use the (faster) C++ side version of approach because I + -- don't want to find out how many themes pass a negative speed. -Kyz if val == other_val then return val -- already done! end diff --git a/Themes/_fallback/metrics.ini b/Themes/_fallback/metrics.ini index 80822b09eb..8f5acd8502 100644 --- a/Themes/_fallback/metrics.ini +++ b/Themes/_fallback/metrics.ini @@ -706,7 +706,7 @@ PercentUseRemainder=false ApplyScoreDisplayOptions=true DancePointsDigits=5 # -Format="%2d" +Format=FormatPercentScore # RemainderFormat= # @@ -1399,7 +1399,7 @@ DancePointsDigits=1 PercentUseRemainder=true ApplyScoreDisplayOptions=false FormatPercentScore=FormatPercentScore -Format= +Format=FormatPercentScore [SoundEffectControl] LockToHold=false diff --git a/Themes/default/BGAnimations/ScreenEvaluation decorations/default.lua b/Themes/default/BGAnimations/ScreenEvaluation decorations/default.lua index 7e27b3437d..1a738fc954 100644 --- a/Themes/default/BGAnimations/ScreenEvaluation decorations/default.lua +++ b/Themes/default/BGAnimations/ScreenEvaluation decorations/default.lua @@ -70,13 +70,13 @@ local t = LoadFallbackB(); t[#t+1] = StandardDecorationFromFileOptional("StageDisplay","StageDisplay"); -if ShowStandardDecoration("GraphDisplay") and not GAMESTATE:GetPlayMode() == "PlayMode_Rave" then +if ShowStandardDecoration("GraphDisplay") and GAMESTATE:GetPlayMode() ~= "PlayMode_Rave" then for pn in ivalues(GAMESTATE:GetHumanPlayers()) do t[#t+1] = StandardDecorationFromTable( "GraphDisplay" .. ToEnumShortString(pn), GraphDisplay(pn) ); end end -if ShowStandardDecoration("ComboGraph") and not GAMESTATE:GetPlayMode() == "PlayMode_Rave" then +if ShowStandardDecoration("ComboGraph") and GAMESTATE:GetPlayMode() ~= "PlayMode_Rave" then for pn in ivalues(GAMESTATE:GetHumanPlayers()) do t[#t+1] = StandardDecorationFromTable( "ComboGraph" .. ToEnumShortString(pn), ComboGraph(pn) ); end diff --git a/Themes/default/Languages/en.ini b/Themes/default/Languages/en.ini index 59926aa903..4ff4543f29 100644 --- a/Themes/default/Languages/en.ini +++ b/Themes/default/Languages/en.ini @@ -246,7 +246,7 @@ Information=Information Feet=Your feet will be used to play! Tap=When the arrows rise to this point,\nstep on the matching panels. Jump=Step on both panels if two different\narrows appear at the same time! -Miss=If you misstep repeatedly, your dance\nguage will decrease until the game is\nis over! +Miss=If you misstep repeatedly, your dance\ngauge will decrease until the game\nis over! [Protiming] diff --git a/Themes/default/metrics.ini b/Themes/default/metrics.ini index 1ca22b3422..dc6b0a6018 100644 --- a/Themes/default/metrics.ini +++ b/Themes/default/metrics.ini @@ -140,8 +140,6 @@ NumLivesP2OnCommand=zoomx,-1 NumLivesP2LoseLifeCommand=zoomx,-1.5;zoomy,1.5;linear,0.15;zoomx,-1;zoomy,1 [LifeMeterBattery Percent] -# still asking for this even though it's in fallback... -aj -Format= # PercentP2OnCommand=zoom,0.7;zoomx,-0.7;shadowlength,0;diffuse,PlayerColor(PLAYER_2) DancePointsP2OnCommand=zoom,0.7;zoomx,-0.7;shadowlength,0;diffuse,PlayerColor(PLAYER_2) diff --git a/src/Actor.cpp b/src/Actor.cpp index c93509fa69..15eab5f0b5 100644 --- a/src/Actor.cpp +++ b/src/Actor.cpp @@ -370,21 +370,21 @@ void Actor::PreDraw() // calculate actor properties /* XXX: Should diffuse_blink and diffuse_shift multiply the tempState color? * (That would have the same effect with 1,1,1,1, and allow tweening the diffuse * while blinking and shifting.) */ - for(int i=0; i<4; i++) + for(int i=0; iGetLineage() + '/'; - sPath += ssprintf( "<%s> %s", typeid(*this).name(), m_sName.c_str() ); + sPath += ssprintf( " %s", typeid(*this).name(), m_sName.c_str() ); return sPath; } @@ -889,7 +889,7 @@ void Actor::ScaleTo( const RectF &rect, StretchType st ) void Actor::SetEffectClockString( const RString &s ) { if (s.EqualsNoCase("timer")) this->SetEffectClock( CLOCK_TIMER ); - if (s.EqualsNoCase("timerglobal")) this->SetEffectClock( CLOCK_TIMER_GLOBAL ); + else if(s.EqualsNoCase("timerglobal")) this->SetEffectClock( CLOCK_TIMER_GLOBAL ); else if(s.EqualsNoCase("beat")) this->SetEffectClock( CLOCK_BGM_BEAT ); else if(s.EqualsNoCase("music")) this->SetEffectClock( CLOCK_BGM_TIME ); else if(s.EqualsNoCase("bgm")) this->SetEffectClock( CLOCK_BGM_BEAT ); // compat, deprecated @@ -899,9 +899,13 @@ void Actor::SetEffectClockString( const RString &s ) { CabinetLight cl = StringToCabinetLight( s ); if( cl == CabinetLight_Invalid ) - FAIL_M(ssprintf("Invalid cabinet light: %s", s.c_str())); - - this->SetEffectClock( (EffectClock) (cl + CLOCK_LIGHT_1) ); + { + LuaHelpers::ReportScriptErrorFmt("String '%s' is not an effect clock string or the name of a cabinet light.", s.c_str()); + } + else + { + this->SetEffectClock(static_cast(cl + CLOCK_LIGHT_1)); + } } } @@ -1130,7 +1134,7 @@ void Actor::RunCommands( const LuaReference& cmds, const LuaReference *pParamTab { if( !cmds.IsSet() || cmds.IsNil() ) { - LuaHelpers::ReportScriptError("RunCommands: command is unset or nil"); + LuaHelpers::ReportScriptErrorFmt("RunCommands: commands for %s are unset or nil", GetLineage().c_str()); return; } @@ -1140,7 +1144,7 @@ void Actor::RunCommands( const LuaReference& cmds, const LuaReference *pParamTab cmds.PushSelf( L ); if( lua_isnil(L, -1) ) { - LuaHelpers::ReportScriptError("Error compiling commands"); + LuaHelpers::ReportScriptErrorFmt("RunCommands: Error compiling commands for %s", GetLineage().c_str()); LUA->Release(L); return; } @@ -1155,7 +1159,7 @@ void Actor::RunCommands( const LuaReference& cmds, const LuaReference *pParamTab pParamTable->PushSelf( L ); // call function with 2 arguments and 0 results - RString Error= "Error playing command: "; + RString Error= "Error playing command:"; LuaHelpers::RunScriptOnStack(L, Error, 2, 0, true); LUA->Release(L); @@ -1182,7 +1186,7 @@ float Actor::GetTweenTimeLeft() const * being manipulated, which would add overhead ... */ void Actor::SetGlobalDiffuseColor( RageColor c ) { - for( int i=0; i<4; i++ ) // color, not alpha + for( int i=0; i 0 ) + RageColor stroke_color= GetCurrStrokeColor(); + if( stroke_color.a > 0 ) { - RageColor c = m_StrokeColor; - c.a *= m_pTempState->diffuse[0].a; + stroke_color.a *= m_pTempState->diffuse[0].a; for( unsigned i=0; iClearAttributes(); return 0; } static int strokecolor( T* p, lua_State *L ) { RageColor c; c.FromStackCompat( L, 1 ); p->SetStrokeColor( c ); return 0; } + DEFINE_METHOD(getstrokecolor, GetStrokeColor()); static int uppercase( T* p, lua_State *L ) { p->SetUppercase( BArg(1) ); return 0; } static int textglowmode( T* p, lua_State *L ) { p->SetTextGlowMode( Enum::Check(L, 1) ); return 0; } @@ -933,6 +987,7 @@ public: ADD_METHOD( AddAttribute ); ADD_METHOD( ClearAttributes ); ADD_METHOD( strokecolor ); + ADD_METHOD( getstrokecolor ); ADD_METHOD( uppercase ); ADD_METHOD( textglowmode ); //ADD_METHOD( LoadFromFont ); diff --git a/src/BitmapText.h b/src/BitmapText.h index 329c5540f1..b4cb21205d 100644 --- a/src/BitmapText.h +++ b/src/BitmapText.h @@ -19,6 +19,41 @@ public: virtual void LoadFromNode( const XNode* pNode ); virtual BitmapText *Copy() const; + struct BMT_TweenState + { + // We'd be better off not adding strokes to things we can't control + // themewise (ScreenDebugOverlay for example). -Midiman + BMT_TweenState(): m_stroke_color(RageColor(0,0,0,0)) {} + static void MakeWeightedAverage(BMT_TweenState& out, + BMT_TweenState const& from, BMT_TweenState const& to, float between); + bool operator==(BMT_TweenState const& other) const; + bool operator!=(BMT_TweenState const& other) const { return !operator==(other); } + void SetStrokeColor(RageColor const& c) { m_stroke_color= c; } + RageColor const& GetStrokeColor() { return m_stroke_color; } + private: + RageColor m_stroke_color; + }; + + BMT_TweenState& BMT_DestTweenState() + { + if(BMT_Tweens.empty()) + { return BMT_current; } + else + { return BMT_Tweens.back(); } + } + BMT_TweenState const& BMT_DestTweenState() const { return const_cast(this)->BMT_DestTweenState(); } + + virtual void SetCurrentTweenStart(); + virtual void EraseHeadTween(); + virtual void UpdatePercentThroughTween(float between); + virtual void BeginTweening(float time, ITween* interp); + // This function exists because the compiler tried to connect a call of + // "BeginTweening(1.2f)" to the function above. -Kyz + virtual void BeginTweening(float time, TweenType tt = TWEEN_LINEAR) + { Actor::BeginTweening(time, tt); } + virtual void StopTweening(); + virtual void FinishTweening(); + bool LoadFromFont( const RString& sFontName ); bool LoadFromTextureAndChars( const RString& sTexturePath, const RString& sChars ); virtual void SetText( const RString& sText, const RString& sAlternateText = "", int iWrapWidthPixels = -1 ); @@ -40,8 +75,10 @@ public: void SetHorizAlign( float f ); - void SetStrokeColor( RageColor c ) { m_StrokeColor = c; } - RageColor GetStrokeColor() { return m_StrokeColor; } + void SetStrokeColor(RageColor c) { BMT_DestTweenState().SetStrokeColor(c); } + RageColor const& GetStrokeColor() { return BMT_DestTweenState().GetStrokeColor(); } + void SetCurrStrokeColor(RageColor c) { BMT_current.SetStrokeColor(c); } + RageColor const& GetCurrStrokeColor() { return BMT_current.GetStrokeColor(); } void SetTextGlowMode( TextGlowMode tgm ) { m_TextGlowMode = tgm; } @@ -56,7 +93,7 @@ public: { Attribute() : length(-1), glow() { } int length; - RageColor diffuse[4]; + RageColor diffuse[NUM_DIFFUSE_COLORS]; RageColor glow; void FromStack( lua_State *L, int iPos ); @@ -91,7 +128,6 @@ protected: map m_mAttributes; bool m_bHasGlowAttribute; - RageColor m_StrokeColor; TextGlowMode m_TextGlowMode; // recalculate the items in SetText() @@ -101,6 +137,9 @@ protected: private: void SetTextInternal(); + vector BMT_Tweens; + BMT_TweenState BMT_current; + BMT_TweenState BMT_start; }; #endif diff --git a/src/Foreground.cpp b/src/Foreground.cpp index 38d73b6ff1..95d3f8fed2 100644 --- a/src/Foreground.cpp +++ b/src/Foreground.cpp @@ -54,7 +54,9 @@ void Foreground::LoadFromSong( const Song *pSong ) if( bga.m_bga == NULL ) continue; bga.m_bga->SetName( sBGName ); - bga.m_bga->PlayCommand( "Init" ); + // ActorUtil::MakeActor calls LoadFromNode to load the actor, and + // LoadFromNode takes care of running the InitCommand, so do not run the + // InitCommand here. -Kyz bga.m_fStartBeat = change.m_fStartBeat; bga.m_bFinished = false; diff --git a/src/InputMapper.cpp b/src/InputMapper.cpp index cc61833c64..b6a4e90cc9 100644 --- a/src/InputMapper.cpp +++ b/src/InputMapper.cpp @@ -639,7 +639,7 @@ const InputScheme *InputMapper::GetInputScheme() const return m_pInputScheme; } -static const RString DEVICE_INPUT_SEPARATOR = ":"; // this isn't used in any key names +const RString DEVICE_INPUT_SEPARATOR = ":"; // this isn't used in any key names void InputMapper::ReadMappingsFromDisk() { diff --git a/src/InputMapper.h b/src/InputMapper.h index 9d2f2c10f5..64c5c8bf34 100644 --- a/src/InputMapper.h +++ b/src/InputMapper.h @@ -9,6 +9,7 @@ struct Game; const int NUM_GAME_TO_DEVICE_SLOTS = 5; // five device inputs may map to one game input const int NUM_SHOWN_GAME_TO_DEVICE_SLOTS = 3; const int NUM_USER_GAME_TO_DEVICE_SLOTS = 2; +extern const RString DEVICE_INPUT_SEPARATOR; struct AutoMappingEntry { diff --git a/src/LuaManager.h b/src/LuaManager.h index 644780eb67..8573394ee4 100644 --- a/src/LuaManager.h +++ b/src/LuaManager.h @@ -266,6 +266,11 @@ void LuaFunc_Register_##func( lua_State *L ); \ void LuaFunc_Register_##func( lua_State *L ) { lua_register( L, #func, LuaFunc_##func ); } \ REGISTER_WITH_LUA_FUNCTION( LuaFunc_Register_##func ); +#define LUAFUNC_REGISTER_COMMON(func_name) \ +void LuaFunc_Register_##func_name(lua_State* L); \ +void LuaFunc_Register_##func_name(lua_State* L) { lua_register(L, #func_name, LuaFunc_##func_name); } \ +REGISTER_WITH_LUA_FUNCTION(LuaFunc_Register_##func_name); + #endif /* diff --git a/src/NotesLoaderSM.cpp b/src/NotesLoaderSM.cpp index 0907970741..11d91d5226 100644 --- a/src/NotesLoaderSM.cpp +++ b/src/NotesLoaderSM.cpp @@ -73,7 +73,7 @@ void SMLoader::LoadFromTokens( Trim( sDifficulty ); Trim( sNoteData ); - // LOG->Trace( "Steps::LoadFromTokens()" ); + // LOG->Trace( "Steps::LoadFromTokens(), %s", sStepsType.c_str() ); // backwards compatibility hacks: // HACK: We eliminated "ez2-single-hard", but we should still handle it. @@ -85,6 +85,7 @@ void SMLoader::LoadFromTokens( sStepsType = "para-single"; out.m_StepsType = GAMEMAN->StringToStepsType( sStepsType ); + out.m_StepsTypeStr = sStepsType; out.SetDescription( sDescription ); out.SetCredit( sDescription ); // this is often used for both. out.SetChartName(sDescription); // yeah, one more for good measure. diff --git a/src/NotesLoaderSSC.cpp b/src/NotesLoaderSSC.cpp index 9f4be80a2c..5f1d084dfb 100644 --- a/src/NotesLoaderSSC.cpp +++ b/src/NotesLoaderSSC.cpp @@ -643,6 +643,7 @@ bool SSCLoader::LoadFromSimfile( const RString &sPath, Song &out, bool bFromCach if( sValueName=="STEPSTYPE" ) { pNewNotes->m_StepsType = GAMEMAN->StringToStepsType( sParams[1] ); + pNewNotes->m_StepsTypeStr= sParams[1]; } else if( sValueName=="CHARTSTYLE" ) @@ -965,6 +966,7 @@ bool SSCLoader::LoadEditFromMsd(const MsdFile &msd, if( sValueName=="STEPSTYPE" ) { pNewNotes->m_StepsType = GAMEMAN->StringToStepsType( sParams[1] ); + pNewNotes->m_StepsTypeStr= sParams[1]; bSSCFormat = true; } diff --git a/src/NotesWriterSM.cpp b/src/NotesWriterSM.cpp index 8f131b9210..504b3ebafe 100644 --- a/src/NotesWriterSM.cpp +++ b/src/NotesWriterSM.cpp @@ -206,9 +206,9 @@ static RString GetSMNotesTag( const Song &song, const Steps &in ) lines.push_back( "" ); // Escape to prevent some clown from making a comment of "\r\n;" lines.push_back( ssprintf("//---------------%s - %s----------------", - GAMEMAN->GetStepsTypeInfo(in.m_StepsType).szName, SmEscape(in.GetDescription()).c_str()) ); + in.m_StepsTypeStr.c_str(), SmEscape(in.GetDescription()).c_str()) ); lines.push_back( song.m_vsKeysoundFile.empty() ? "#NOTES:" : "#NOTES2:" ); - lines.push_back( ssprintf( " %s:", GAMEMAN->GetStepsTypeInfo(in.m_StepsType).szName ) ); + lines.push_back( ssprintf( " %s:", in.m_StepsTypeStr.c_str() ) ); RString desc = (USE_CREDIT ? in.GetCredit() : in.GetChartName()); lines.push_back( ssprintf( " %s:", SmEscape(desc).c_str() ) ); lines.push_back( ssprintf( " %s:", DifficultyToString(in.GetDifficulty()).c_str() ) ); diff --git a/src/NotesWriterSSC.cpp b/src/NotesWriterSSC.cpp index 3bb8fdfa73..1f5971f149 100644 --- a/src/NotesWriterSSC.cpp +++ b/src/NotesWriterSSC.cpp @@ -349,10 +349,10 @@ static RString GetSSCNoteData( const Song &song, const Steps &in, bool bSavingCa lines.push_back( "" ); // Escape to prevent some clown from making a comment of "\r\n;" lines.push_back( ssprintf("//---------------%s - %s----------------", - GAMEMAN->GetStepsTypeInfo(in.m_StepsType).szName, SmEscape(in.GetDescription()).c_str()) ); + in.m_StepsTypeStr.c_str(), SmEscape(in.GetDescription()).c_str()) ); lines.push_back( "#NOTEDATA:;" ); // our new separator. lines.push_back( ssprintf( "#CHARTNAME:%s;", SmEscape(in.GetChartName()).c_str())); - lines.push_back( ssprintf( "#STEPSTYPE:%s;", GAMEMAN->GetStepsTypeInfo(in.m_StepsType).szName ) ); + lines.push_back( ssprintf( "#STEPSTYPE:%s;", in.m_StepsTypeStr.c_str() ) ); lines.push_back( ssprintf( "#DESCRIPTION:%s;", SmEscape(in.GetDescription()).c_str() ) ); lines.push_back( ssprintf( "#CHARTSTYLE:%s;", SmEscape(in.GetChartStyle()).c_str() ) ); lines.push_back( ssprintf( "#DIFFICULTY:%s;", DifficultyToString(in.GetDifficulty()).c_str() ) ); diff --git a/src/PercentageDisplay.cpp b/src/PercentageDisplay.cpp index 28eec2fa7e..99cc9971e4 100644 --- a/src/PercentageDisplay.cpp +++ b/src/PercentageDisplay.cpp @@ -33,10 +33,22 @@ void PercentageDisplay::LoadFromNode( const XNode* pNode ) pNode->GetAttrValue( "AutoRefresh", m_bAutoRefresh ); { Lua *L = LUA->Get(); - if( pNode->PushAttrValue(L, "FormatPercentScore") ) + if(pNode->PushAttrValue(L, "FormatPercentScore")) + { m_FormatPercentScore.SetFromStack( L ); + if(m_FormatPercentScore.GetLuaType() != LUA_TFUNCTION) + { + // Not reported as an error because _fallback and default provided bad + // examples in their [LifeMeterBattery Percent]:Format metric and nobody + // realized it was supposed to be set to a function. -Kyz + LOG->Trace("Format attribute for PercentageDisplay named '%s' is not a function. Defaulting to 'FormatPercentScore'.", GetName().c_str()); + m_FormatPercentScore.SetFromExpression("FormatPercentScore"); + } + } else + { lua_pop(L, 1); + } LUA->Release(L); } @@ -86,9 +98,12 @@ void PercentageDisplay::Load( const PlayerState *pPlayerState, const PlayerStage m_sPercentFormat = THEME->GetMetric( sMetricsGroup, "PercentFormat" ); m_sRemainderFormat = THEME->GetMetric( sMetricsGroup, "RemainderFormat" ); - if( m_FormatPercentScore.IsNil() ) + if(m_FormatPercentScore.GetLuaType() != LUA_TFUNCTION) { - LOG->Trace( "Format is nil in [%s]. Defaulting to 'FormatPercentScore'.", sMetricsGroup.c_str() ); + // Not reported as an error because _fallback and default provided bad + // examples in their [LifeMeterBattery Percent]:Format metric and nobody + // realized it was supposed to be set to a function. -Kyz + LOG->Trace("Format metric is not a function in [%s]. Defaulting to 'FormatPercentScore'.", sMetricsGroup.c_str()); m_FormatPercentScore.SetFromExpression( "FormatPercentScore" ); } @@ -157,14 +172,16 @@ void PercentageDisplay::Refresh() } else { - Lua *L = LUA->Get(); - m_FormatPercentScore.PushSelf( L ); - ASSERT( !lua_isnil(L, -1) ); - LuaHelpers::Push( L, fPercentDancePoints ); - RString Error= "Error running FormatPercentScore: "; - LuaHelpers::RunScriptOnStack(L, Error, 1, 1, true); // 1 arg, 1 result - LuaHelpers::Pop( L, sNumToDisplay ); - LUA->Release(L); + if(m_FormatPercentScore.GetLuaType() == LUA_TFUNCTION) + { + Lua *L = LUA->Get(); + m_FormatPercentScore.PushSelf( L ); + LuaHelpers::Push( L, fPercentDancePoints ); + RString Error= "Error running FormatPercentScore: "; + LuaHelpers::RunScriptOnStack(L, Error, 1, 1, true); // 1 arg, 1 result + LuaHelpers::Pop( L, sNumToDisplay ); + LUA->Release(L); + } // HACK: Use the last frame in the numbers texture as '-' sNumToDisplay.Replace('-','x'); diff --git a/src/PlayerAI.cpp b/src/PlayerAI.cpp index b7bf80905e..29b73a9bf7 100644 --- a/src/PlayerAI.cpp +++ b/src/PlayerAI.cpp @@ -11,6 +11,29 @@ struct TapScoreDistribution { float fPercent[NUM_TapNoteScore]; + void ChangeWeightsToPercents() + { + float sum= 0; + for(int i= 0; i < NUM_TapNoteScore; ++i) + { + sum+= fPercent[i]; + } + for(int i= 0; i < NUM_TapNoteScore; ++i) + { + fPercent[i]/= sum; + } + } + void SetDefaultWeights() + { + fPercent[TNS_None] = 0; + fPercent[TNS_Miss] = 1; + fPercent[TNS_W5] = 0; + fPercent[TNS_W4] = 0; + fPercent[TNS_W3] = 0; + fPercent[TNS_W2] = 0; + fPercent[TNS_W1] = 0; + } + TapNoteScore GetTapNoteScore() { float fRand = randomf(0,1); @@ -31,57 +54,54 @@ static TapScoreDistribution g_Distributions[NUM_SKILL_LEVELS]; void PlayerAI::InitFromDisk() { - bool bSuccess; - IniFile ini; - bSuccess = ini.ReadFile( AI_PATH ); - ASSERT( bSuccess ); - - for( int i=0; iGetAttrValue( "WeightMiss", dist.fPercent[TNS_Miss] ); - SET_MALF_IF(!bSuccess, TNS_Miss); - bSuccess = pNode->GetAttrValue( "WeightW5", dist.fPercent[TNS_W5] ); - SET_MALF_IF(!bSuccess, TNS_W5); - bSuccess = pNode->GetAttrValue( "WeightW4", dist.fPercent[TNS_W4] ); - SET_MALF_IF(!bSuccess, TNS_W4); - bSuccess = pNode->GetAttrValue( "WeightW3", dist.fPercent[TNS_W3] ); - SET_MALF_IF(!bSuccess, TNS_W3); - bSuccess = pNode->GetAttrValue( "WeightW2", dist.fPercent[TNS_W2] ); - SET_MALF_IF(!bSuccess, TNS_W2); - bSuccess = pNode->GetAttrValue( "WeightW1", dist.fPercent[TNS_W1] ); - SET_MALF_IF(!bSuccess, TNS_W1); -#undef SET_MALF_IF + else + { + #define SET_MALF_IF(condition, tns) \ + if(condition) \ + { \ + LuaHelpers::ReportScriptErrorFmt("AI weight for " #tns " in \"%s\" section not set.", sKey.c_str()); \ + dist.fPercent[tns]= 0; \ + } + dist.fPercent[TNS_None] = 0; + bSuccess = pNode->GetAttrValue( "WeightMiss", dist.fPercent[TNS_Miss] ); + SET_MALF_IF(!bSuccess, TNS_Miss); + bSuccess = pNode->GetAttrValue( "WeightW5", dist.fPercent[TNS_W5] ); + SET_MALF_IF(!bSuccess, TNS_W5); + bSuccess = pNode->GetAttrValue( "WeightW4", dist.fPercent[TNS_W4] ); + SET_MALF_IF(!bSuccess, TNS_W4); + bSuccess = pNode->GetAttrValue( "WeightW3", dist.fPercent[TNS_W3] ); + SET_MALF_IF(!bSuccess, TNS_W3); + bSuccess = pNode->GetAttrValue( "WeightW2", dist.fPercent[TNS_W2] ); + SET_MALF_IF(!bSuccess, TNS_W2); + bSuccess = pNode->GetAttrValue( "WeightW1", dist.fPercent[TNS_W1] ); + SET_MALF_IF(!bSuccess, TNS_W1); + #undef SET_MALF_IF + } + dist.ChangeWeightsToPercents(); } - - float fSum = 0; - for( int j=0; jm_DefaultFailType; m_sNoteSkin = ""; } diff --git a/src/PrefsManager.cpp b/src/PrefsManager.cpp index 54f9059add..d3d4702397 100644 --- a/src/PrefsManager.cpp +++ b/src/PrefsManager.cpp @@ -221,7 +221,7 @@ PrefsManager::PrefsManager() : m_iSongsPerPlay ( "SongsPerPlay", 3, ValidateSongsPerPlay ), m_bDelayedCreditsReconcile ( "DelayedCreditsReconcile", false ), m_bComboContinuesBetweenSongs ( "ComboContinuesBetweenSongs", false ), - m_ShowSongOptions ( "ShowSongOptions", Maybe_YES ), + m_ShowSongOptions ( "ShowSongOptions", Maybe_NO ), m_bDancePointsForOni ( "DancePointsForOni", true ), m_bPercentageScoring ( "PercentageScoring", false ), m_fMinPercentageForMachineSongHighScore ( "MinPercentageForMachineSongHighScore", 0.0001f ), // This is for home, who cares how bad you do? @@ -286,7 +286,6 @@ PrefsManager::PrefsManager() : m_sCoursesToShowRanking ( "CoursesToShowRanking", "" ), m_bQuirksMode ( "QuirksMode", false ), - m_DefaultFailType("DefaultFailtype", FailType_ImmediateContinue), /* Debug: */ m_bLogToDisk ( "LogToDisk", true ), diff --git a/src/PrefsManager.h b/src/PrefsManager.h index 97d40fb5c4..19f51e07e2 100644 --- a/src/PrefsManager.h +++ b/src/PrefsManager.h @@ -294,7 +294,6 @@ public: /** @brief Enable some quirky behavior used by some older versions of StepMania. */ Preference m_bQuirksMode; - Preference m_DefaultFailType; // Debug: Preference m_bLogToDisk; diff --git a/src/RageBitmapTexture.cpp b/src/RageBitmapTexture.cpp index 17252e7f59..c6628ed6ba 100644 --- a/src/RageBitmapTexture.cpp +++ b/src/RageBitmapTexture.cpp @@ -289,6 +289,13 @@ void RageBitmapTexture::Create() if( !TEXTUREMAN->GetOddDimensionWarning() ) bRunCheck = false; + // Don't check if this is the screen texture, the theme can't do anything + // about it. -Kyz + if(actualID == TEXTUREMAN->GetScreenTextureID()) + { + bRunCheck= false; + } + if( bRunCheck ) { float fFrameWidth = this->GetSourceWidth() / (float)this->GetFramesWide(); diff --git a/src/RageTypes.cpp b/src/RageTypes.cpp index b3d96215a2..b554a042e7 100644 --- a/src/RageTypes.cpp +++ b/src/RageTypes.cpp @@ -74,6 +74,14 @@ RString RageColor::NormalizeColorString( RString sColor ) return c.ToString(); } +void lerp_rage_color(RageColor& out, RageColor const& a, RageColor const& b, float t) +{ + out.b= lerp(t, a.b, b.b); + out.g= lerp(t, a.g, b.g); + out.r= lerp(t, a.r, b.r); + out.a= lerp(t, a.a, b.a); +} + void WeightedAvergeOfRSVs(RageSpriteVertex& average_out, RageSpriteVertex const& rsv1, RageSpriteVertex const& rsv2, float percent_between) { average_out.p= lerp(percent_between, rsv1.p, rsv2.p); @@ -207,8 +215,21 @@ int LuaFunc_color( lua_State *L ) c.PushTable( L ); return 1; } -void LuaFunc_Register_color( lua_State *L ) { lua_register( L, "color", LuaFunc_color ); } -REGISTER_WITH_LUA_FUNCTION( LuaFunc_Register_color ); +LUAFUNC_REGISTER_COMMON(color); + +int LuaFunc_lerp_color(lua_State *L) +{ + // Args: percent, color, color + // Returns: color + float percent= FArg(1); + RageColor a, b, c; + a.FromStack(L, 2); + b.FromStack(L, 3); + lerp_rage_color(c, a, b, percent); + c.PushTable(L); + return 1; +} +LUAFUNC_REGISTER_COMMON(lerp_color); /* * Copyright (c) 2006 Glenn Maynard diff --git a/src/RageTypes.h b/src/RageTypes.h index 0a5d51a1c9..ddfc3e88ec 100644 --- a/src/RageTypes.h +++ b/src/RageTypes.h @@ -342,6 +342,7 @@ struct RageSpriteVertex // has color RageVector2 t; // texture coordinates }; +void lerp_rage_color(RageColor& out, RageColor const& a, RageColor const& b, float t); void WeightedAvergeOfRSVs(RageSpriteVertex& average_out, RageSpriteVertex const& rsv1, RageSpriteVertex const& rsv2, float percent_between); struct RageModelVertex // doesn't have color. Relies on material color diff --git a/src/RageUtil.cpp b/src/RageUtil.cpp index 8255f9e96d..c2d033f87e 100644 --- a/src/RageUtil.cpp +++ b/src/RageUtil.cpp @@ -2329,6 +2329,76 @@ LuaFunction( PrettyPercent, PrettyPercent( FArg(1), FArg(2) ) ); //LuaFunction( IsHexVal, IsHexVal( SArg(1) ) ); static bool UndocumentedFeature( RString s ){ sm_crash(s); return true; } LuaFunction( UndocumentedFeature, UndocumentedFeature(SArg(1)) ); +LuaFunction( lerp, lerp(FArg(1), FArg(2), FArg(3)) ); + +void luafunc_approach_internal(lua_State* L, int valind, int goalind, int speedind); +void luafunc_approach_internal(lua_State* L, int valind, int goalind, int speedind, int process_index) +{ +#define TONUMBER_NICE(dest, num_name, index) \ + if(!lua_isnumber(L, index)) \ + { \ + luaL_error(L, "approach: " #num_name " for approach %d is not a number.", process_index); \ + } \ + dest= lua_tonumber(L, index); + float val= 0; + float goal= 0; + float speed= 0; + TONUMBER_NICE(val, current, valind); + TONUMBER_NICE(goal, goal, goalind); + TONUMBER_NICE(speed, speed, speedind); +#undef TONUMBER_NICE + if(speed < 0) + { + luaL_error(L, "approach: speed %d is negative.", process_index); + } + fapproach(val, goal, speed); + lua_pushnumber(L, val); +} + +int LuaFunc_approach(lua_State* L); +int LuaFunc_approach(lua_State* L) +{ + // Args: current, goal, speed + // Returns: new_current + luafunc_approach_internal(L, 1, 2, 3, 1); + return 1; +} +LUAFUNC_REGISTER_COMMON(approach); + +int LuaFunc_multiapproach(lua_State* L); +int LuaFunc_multiapproach(lua_State* L) +{ + // Args: {currents}, {goals}, {speeds} + // Returns: {currents} + // Modifies the values in {currents} in place. + if(lua_gettop(L) != 3) + { + luaL_error(L, "multiapproach: A table of current values, a table of goal values, and a table of speeds must be passed."); + } + size_t currents_len= lua_objlen(L, 1); + size_t goals_len= lua_objlen(L, 2); + size_t speeds_len= lua_objlen(L, 3); + if(currents_len != goals_len || currents_len != speeds_len) + { + luaL_error(L, "multiapproach: There must be the same number of current values, goal values, and speeds."); + } + if(!lua_istable(L, 1) || !lua_istable(L, 2) || !lua_istable(L, 3)) + { + luaL_error(L, "multiapproach: current, goal, and speed must all be tables."); + } + for(size_t i= 1; i <= currents_len; ++i) + { + lua_rawgeti(L, 1, i); + lua_rawgeti(L, 2, i); + lua_rawgeti(L, 3, i); + luafunc_approach_internal(L, -3, -2, -1, i); + lua_rawseti(L, 1, i); + lua_pop(L, 3); + } + lua_pushvalue(L, 1); + return 1; +} +LUAFUNC_REGISTER_COMMON(multiapproach); /* * Copyright (c) 2001-2005 Chris Danford, Glenn Maynard diff --git a/src/RollingNumbers.cpp b/src/RollingNumbers.cpp index f267ed87b3..db20620013 100644 --- a/src/RollingNumbers.cpp +++ b/src/RollingNumbers.cpp @@ -25,15 +25,32 @@ void RollingNumbers::Load( const RString &sMetricsGroup ) UpdateText(); } +void RollingNumbers::DrawPart(RageColor const* diffuse, RageColor const& stroke, + float crop_left, float crop_right) +{ + for(int i= 0; i < NUM_DIFFUSE_COLORS; ++i) + { + m_pTempState->diffuse[i]= diffuse[i]; + } + SetCurrStrokeColor(stroke); + m_pTempState->crop.left= crop_left; + m_pTempState->crop.right= crop_right; + BitmapText::DrawPrimitives(); +} + void RollingNumbers::DrawPrimitives() { - RageColor c_orig = this->GetDiffuse(); - RageColor c2_orig = this->GetStrokeColor(); - - RageColor c = this->GetDiffuse(); - c *= LEADING_ZERO_MULTIPLY_COLOR; - RageColor c2 = this->GetStrokeColor(); - c2 *= LEADING_ZERO_MULTIPLY_COLOR; + RageColor diffuse_orig[NUM_DIFFUSE_COLORS]; + RageColor diffuse_temp[NUM_DIFFUSE_COLORS]; + RageColor stroke_orig= GetCurrStrokeColor(); + RageColor stroke_temp= stroke_orig * LEADING_ZERO_MULTIPLY_COLOR; + for(int i= 0; i < NUM_DIFFUSE_COLORS; ++i) + { + diffuse_orig[i]= m_pTempState->diffuse[i]; + diffuse_temp[i]= m_pTempState->diffuse[i] * LEADING_ZERO_MULTIPLY_COLOR; + } + float original_crop_left= m_pTempState->crop.left; + float original_crop_right= m_pTempState->crop.right; RString s = this->GetText(); int i; @@ -53,21 +70,14 @@ void RollingNumbers::DrawPrimitives() float f = i / (float)s.length(); // draw leading part - SetDiffuse( c ); - SetStrokeColor( c2 ); - SetCropLeft( 0 ); - SetCropRight( 1-f ); - BitmapText::DrawPrimitives(); - + DrawPart(diffuse_temp, stroke_temp, + max(0, original_crop_left), max(1-f, original_crop_right)); // draw regular color part - SetDiffuse( c_orig ); - SetStrokeColor( c2_orig ); - SetCropLeft( f ); - SetCropRight( 0 ); - BitmapText::DrawPrimitives(); + DrawPart(diffuse_orig, stroke_orig, + max(f, original_crop_left), max(0, original_crop_right)); - SetCropLeft( 0 ); - SetCropRight( 0 ); + m_pTempState->crop.left= original_crop_left; + m_pTempState->crop.right= original_crop_right; } void RollingNumbers::Update( float fDeltaTime ) diff --git a/src/RollingNumbers.h b/src/RollingNumbers.h index 56f04fc2e3..9dec441c37 100644 --- a/src/RollingNumbers.h +++ b/src/RollingNumbers.h @@ -13,6 +13,8 @@ public: void Load( const RString &sMetricsGroup ); virtual RollingNumbers *Copy() const; + void DrawPart(RageColor const* diffuse, RageColor const& stroke, + float crop_left, float crop_right); virtual void DrawPrimitives(); virtual void Update( float fDeltaTime ); diff --git a/src/ScreenEdit.cpp b/src/ScreenEdit.cpp index 315d2a8ade..4878aa53b3 100644 --- a/src/ScreenEdit.cpp +++ b/src/ScreenEdit.cpp @@ -30,6 +30,7 @@ #include "ScreenTextEntry.h" #include "SongManager.h" #include "SongUtil.h" +#include "SpecialFiles.h" #include "StepsUtil.h" #include "Style.h" #include "ThemeManager.h" @@ -115,8 +116,120 @@ static const char *EditStateNames[] = { XToString( EditState ); LuaXType( EditState ); +map name_to_edit_button; + void ScreenEdit::InitEditMappings() { + // Created courtesy of query replace regex. + name_to_edit_button["COLUMN_0"]= EDIT_BUTTON_COLUMN_0; + name_to_edit_button["COLUMN_1"]= EDIT_BUTTON_COLUMN_1; + name_to_edit_button["COLUMN_2"]= EDIT_BUTTON_COLUMN_2; + name_to_edit_button["COLUMN_3"]= EDIT_BUTTON_COLUMN_3; + name_to_edit_button["COLUMN_4"]= EDIT_BUTTON_COLUMN_4; + name_to_edit_button["COLUMN_5"]= EDIT_BUTTON_COLUMN_5; + name_to_edit_button["COLUMN_6"]= EDIT_BUTTON_COLUMN_6; + name_to_edit_button["COLUMN_7"]= EDIT_BUTTON_COLUMN_7; + name_to_edit_button["COLUMN_8"]= EDIT_BUTTON_COLUMN_8; + name_to_edit_button["COLUMN_9"]= EDIT_BUTTON_COLUMN_9; + + name_to_edit_button["RIGHT_SIDE"]= EDIT_BUTTON_RIGHT_SIDE; + name_to_edit_button["LAY_ROLL"]= EDIT_BUTTON_LAY_ROLL; + name_to_edit_button["LAY_TAP_ATTACK"]= EDIT_BUTTON_LAY_TAP_ATTACK; + name_to_edit_button["REMOVE_NOTE"]= EDIT_BUTTON_REMOVE_NOTE; + + name_to_edit_button["CYCLE_TAP_LEFT"]= EDIT_BUTTON_CYCLE_TAP_LEFT; + name_to_edit_button["CYCLE_TAP_RIGHT"]= EDIT_BUTTON_CYCLE_TAP_RIGHT; + + name_to_edit_button["CYCLE_SEGMENT_LEFT"]= EDIT_BUTTON_CYCLE_SEGMENT_LEFT; + name_to_edit_button["CYCLE_SEGMENT_RIGHT"]= EDIT_BUTTON_CYCLE_SEGMENT_RIGHT; + + name_to_edit_button["SCROLL_UP_LINE"]= EDIT_BUTTON_SCROLL_UP_LINE; + name_to_edit_button["SCROLL_UP_PAGE"]= EDIT_BUTTON_SCROLL_UP_PAGE; + name_to_edit_button["SCROLL_UP_TS"]= EDIT_BUTTON_SCROLL_UP_TS; + name_to_edit_button["SCROLL_DOWN_LINE"]= EDIT_BUTTON_SCROLL_DOWN_LINE; + name_to_edit_button["SCROLL_DOWN_PAGE"]= EDIT_BUTTON_SCROLL_DOWN_PAGE; + name_to_edit_button["SCROLL_DOWN_TS"]= EDIT_BUTTON_SCROLL_DOWN_TS; + name_to_edit_button["SCROLL_NEXT_MEASURE"]= EDIT_BUTTON_SCROLL_NEXT_MEASURE; + name_to_edit_button["SCROLL_PREV_MEASURE"]= EDIT_BUTTON_SCROLL_PREV_MEASURE; + name_to_edit_button["SCROLL_HOME"]= EDIT_BUTTON_SCROLL_HOME; + name_to_edit_button["SCROLL_END"]= EDIT_BUTTON_SCROLL_END; + name_to_edit_button["SCROLL_NEXT"]= EDIT_BUTTON_SCROLL_NEXT; + name_to_edit_button["SCROLL_PREV"]= EDIT_BUTTON_SCROLL_PREV; + + name_to_edit_button["SEGMENT_NEXT"]= EDIT_BUTTON_SEGMENT_NEXT; + name_to_edit_button["SEGMENT_PREV"]= EDIT_BUTTON_SEGMENT_PREV; + + name_to_edit_button["SCROLL_SELECT"]= EDIT_BUTTON_SCROLL_SELECT; + + name_to_edit_button["LAY_SELECT"]= EDIT_BUTTON_LAY_SELECT; + + name_to_edit_button["SCROLL_SPEED_UP"]= EDIT_BUTTON_SCROLL_SPEED_UP; + name_to_edit_button["SCROLL_SPEED_DOWN"]= EDIT_BUTTON_SCROLL_SPEED_DOWN; + + name_to_edit_button["SNAP_NEXT"]= EDIT_BUTTON_SNAP_NEXT; + name_to_edit_button["SNAP_PREV"]= EDIT_BUTTON_SNAP_PREV; + + name_to_edit_button["OPEN_EDIT_MENU"]= EDIT_BUTTON_OPEN_EDIT_MENU; + name_to_edit_button["OPEN_TIMING_MENU"]= EDIT_BUTTON_OPEN_TIMING_MENU; + name_to_edit_button["OPEN_ALTER_MENU"]= EDIT_BUTTON_OPEN_ALTER_MENU; + name_to_edit_button["OPEN_AREA_MENU"]= EDIT_BUTTON_OPEN_AREA_MENU; + name_to_edit_button["OPEN_BGCHANGE_LAYER1_MENU"]= EDIT_BUTTON_OPEN_BGCHANGE_LAYER1_MENU; + name_to_edit_button["OPEN_BGCHANGE_LAYER2_MENU"]= EDIT_BUTTON_OPEN_BGCHANGE_LAYER2_MENU; + name_to_edit_button["OPEN_COURSE_MENU"]= EDIT_BUTTON_OPEN_COURSE_MENU; + name_to_edit_button["OPEN_COURSE_ATTACK_MENU"]= EDIT_BUTTON_OPEN_COURSE_ATTACK_MENU; + + name_to_edit_button["OPEN_STEP_ATTACK_MENU"]= EDIT_BUTTON_OPEN_STEP_ATTACK_MENU; + name_to_edit_button["ADD_STEP_MODS"]= EDIT_BUTTON_ADD_STEP_MODS; + + name_to_edit_button["OPEN_INPUT_HELP"]= EDIT_BUTTON_OPEN_INPUT_HELP; + + name_to_edit_button["BAKE_RANDOM_FROM_SONG_GROUP"]= EDIT_BUTTON_BAKE_RANDOM_FROM_SONG_GROUP; + name_to_edit_button["BAKE_RANDOM_FROM_SONG_GROUP_AND_GENRE"]= EDIT_BUTTON_BAKE_RANDOM_FROM_SONG_GROUP_AND_GENRE; + + name_to_edit_button["PLAY_FROM_START"]= EDIT_BUTTON_PLAY_FROM_START; + name_to_edit_button["PLAY_FROM_CURSOR"]= EDIT_BUTTON_PLAY_FROM_CURSOR; + name_to_edit_button["PLAY_SELECTION"]= EDIT_BUTTON_PLAY_SELECTION; + name_to_edit_button["RECORD_FROM_CURSOR"]= EDIT_BUTTON_RECORD_FROM_CURSOR; + name_to_edit_button["RECORD_SELECTION"]= EDIT_BUTTON_RECORD_SELECTION; + + name_to_edit_button["RETURN_TO_EDIT"]= EDIT_BUTTON_RETURN_TO_EDIT; + + name_to_edit_button["INSERT"]= EDIT_BUTTON_INSERT; + name_to_edit_button["DELETE"]= EDIT_BUTTON_DELETE; + name_to_edit_button["INSERT_SHIFT_PAUSES"]= EDIT_BUTTON_INSERT_SHIFT_PAUSES; + name_to_edit_button["DELETE_SHIFT_PAUSES"]= EDIT_BUTTON_DELETE_SHIFT_PAUSES; + + name_to_edit_button["OPEN_NEXT_STEPS"]= EDIT_BUTTON_OPEN_NEXT_STEPS; + name_to_edit_button["OPEN_PREV_STEPS"]= EDIT_BUTTON_OPEN_PREV_STEPS; + name_to_edit_button["PLAY_SAMPLE_MUSIC"]= EDIT_BUTTON_PLAY_SAMPLE_MUSIC; + + name_to_edit_button["BPM_UP"]= EDIT_BUTTON_BPM_UP; + name_to_edit_button["BPM_DOWN"]= EDIT_BUTTON_BPM_DOWN; + name_to_edit_button["STOP_UP"]= EDIT_BUTTON_STOP_UP; + name_to_edit_button["STOP_DOWN"]= EDIT_BUTTON_STOP_DOWN; + + name_to_edit_button["DELAY_UP"]= EDIT_BUTTON_DELAY_UP; + name_to_edit_button["DELAY_DOWN"]= EDIT_BUTTON_DELAY_DOWN; + + name_to_edit_button["OFFSET_UP"]= EDIT_BUTTON_OFFSET_UP; + name_to_edit_button["OFFSET_DOWN"]= EDIT_BUTTON_OFFSET_DOWN; + name_to_edit_button["SAMPLE_START_UP"]= EDIT_BUTTON_SAMPLE_START_UP; + name_to_edit_button["SAMPLE_START_DOWN"]= EDIT_BUTTON_SAMPLE_START_DOWN; + name_to_edit_button["SAMPLE_LENGTH_UP"]= EDIT_BUTTON_SAMPLE_LENGTH_UP; + name_to_edit_button["SAMPLE_LENGTH_DOWN"]= EDIT_BUTTON_SAMPLE_LENGTH_DOWN; + + name_to_edit_button["ADJUST_FINE"]= EDIT_BUTTON_ADJUST_FINE; + + name_to_edit_button["SAVE"]= EDIT_BUTTON_SAVE; + + name_to_edit_button["UNDO"]= EDIT_BUTTON_UNDO; + + name_to_edit_button["ADD_COURSE_MODS"]= EDIT_BUTTON_ADD_COURSE_MODS; + + name_to_edit_button["SWITCH_PLAYERS"]= EDIT_BUTTON_SWITCH_PLAYERS; + + name_to_edit_button["SWITCH_TIMINGS"]= EDIT_BUTTON_SWITCH_TIMINGS; + m_EditMappingsDeviceInput.Clear(); // Common mappings: @@ -381,8 +494,50 @@ void ScreenEdit::InitEditMappings() m_RecordPausedMappingsDeviceInput.button[EDIT_BUTTON_RETURN_TO_EDIT][0] = DeviceInput(DEVICE_KEYBOARD, KEY_ESC); m_RecordPausedMappingsMenuButton.button[EDIT_BUTTON_RETURN_TO_EDIT][1] = GAME_BUTTON_BACK; m_RecordPausedMappingsDeviceInput.button[EDIT_BUTTON_UNDO][0] = DeviceInput(DEVICE_KEYBOARD, KEY_Cu); + + IniFile mapping_ini; + // Only use the mappings file if it exists. It's meant to be optional, and + // only used in rare cases like someone having critical keys broken. -Kyz + if(mapping_ini.ReadFile(SpecialFiles::EDIT_MODE_KEYMAPS_PATH)) + { + LoadKeymapSectionIntoMappingsMember(mapping_ini.GetChild("Edit"), + m_EditMappingsDeviceInput); + LoadKeymapSectionIntoMappingsMember(mapping_ini.GetChild("Play"), + m_PlayMappingsDeviceInput); + LoadKeymapSectionIntoMappingsMember(mapping_ini.GetChild("Record"), + m_RecordMappingsDeviceInput); + LoadKeymapSectionIntoMappingsMember(mapping_ini.GetChild("RecordPaused"), + m_RecordPausedMappingsDeviceInput); + } } +void ScreenEdit::LoadKeymapSectionIntoMappingsMember(XNode const* section, MapEditToDI& mappings) +{ + if(section == NULL) {return;} // Not an error, sections are optional. -Kyz + FOREACH_CONST_Attr(section, attr) + { + map::iterator name_entry= + name_to_edit_button.find(attr->first); + if(name_entry != name_to_edit_button.end()) + { + RString joined_names; + attr->second->GetValue(joined_names); + vector key_names; + split(joined_names, DEVICE_INPUT_SEPARATOR, key_names, false); + for(size_t k= 0; k < key_names.size() && k < NUM_EDIT_TO_DEVICE_SLOTS; ++k) + { + DeviceInput devi; + devi.FromString(key_names[k]); + if(devi.IsValid()) + { + mappings.button[name_entry->second][k]= devi; + } + } + } + } +} + + /* Given a DeviceInput that was just depressed, return an active edit function. */ EditButton ScreenEdit::DeviceToEdit( const DeviceInput &DeviceI ) const { diff --git a/src/ScreenEdit.h b/src/ScreenEdit.h index 79836f08de..481ea6f35d 100644 --- a/src/ScreenEdit.h +++ b/src/ScreenEdit.h @@ -39,6 +39,7 @@ LuaDeclareType( EditState ); enum EditButton { + // Add to the name_to_edit_button list when adding to this enum. -Kyz EDIT_BUTTON_COLUMN_0, EDIT_BUTTON_COLUMN_1, EDIT_BUTTON_COLUMN_2, @@ -151,6 +152,7 @@ enum EditButton EDIT_BUTTON_SWITCH_TIMINGS, /**< Allow switching between Song and Step TimingData. */ + // Add to the name_to_edit_button list when adding to this enum. -Kyz NUM_EditButton, // leave this at the end EditButton_Invalid }; @@ -665,6 +667,7 @@ public: bool EditIsBeingPressed( EditButton button ) const; const MapEditToDI *GetCurrentDeviceInputMap() const; const MapEditButtonToMenuButton *GetCurrentMenuButtonMap() const; + void LoadKeymapSectionIntoMappingsMember(XNode const* section, MapEditToDI& mappings); MapEditToDI m_EditMappingsDeviceInput; MapEditToDI m_PlayMappingsDeviceInput; MapEditToDI m_RecordMappingsDeviceInput; diff --git a/src/ScreenGameplay.cpp b/src/ScreenGameplay.cpp index 5b6c7ff8fa..3d058200e3 100644 --- a/src/ScreenGameplay.cpp +++ b/src/ScreenGameplay.cpp @@ -2369,7 +2369,7 @@ bool ScreenGameplay::Input( const InputEventPlus &input ) { AbortGiveUp( true ); - if( GamePreferences::m_AutoPlay == PC_HUMAN && GAMESTATE->m_pPlayerState[PLAYER_1]->m_PlayerOptions.GetCurrent().m_fPlayerAutoPlay == 0 ) + if( GamePreferences::m_AutoPlay == PC_HUMAN && GAMESTATE->m_pPlayerState[input.pn]->m_PlayerOptions.GetCurrent().m_fPlayerAutoPlay == 0 ) { PlayerInfo& pi = GetPlayerInfoForInput( input ); diff --git a/src/ScreenOptions.cpp b/src/ScreenOptions.cpp index c91c44d520..72bedd4007 100644 --- a/src/ScreenOptions.cpp +++ b/src/ScreenOptions.cpp @@ -673,7 +673,7 @@ void ScreenOptions::PositionRows( bool bTween ) i < first_start || (i >= first_end && i < second_start) || i >= second_end; - for( int j=0; j<4; j++ ) + for( int j=0; jSetDestination( tsDestination, bTween ); } diff --git a/src/ScreenOptionsMasterPrefs.cpp b/src/ScreenOptionsMasterPrefs.cpp index 7098210eeb..4b50face7d 100644 --- a/src/ScreenOptionsMasterPrefs.cpp +++ b/src/ScreenOptionsMasterPrefs.cpp @@ -30,10 +30,12 @@ static void GetPrefsDefaultModifiers( PlayerOptions &po, SongOptions &so ) static void SetPrefsDefaultModifiers( const PlayerOptions &po, const SongOptions &so ) { vector as; - if( po.GetString() != "" ) - as.push_back( po.GetString() ); - if( so.GetString() != "" ) - as.push_back( so.GetString() ); +#define remove_empty_back() if(as.back() == "") { as.pop_back(); } + as.push_back(po.GetString()); + remove_empty_back(); + as.push_back(so.GetString()); + remove_empty_back(); +#undef remove_empty_back PREFSMAN->m_sDefaultModifiers.Set( join(", ",as) ); } @@ -336,6 +338,32 @@ static void DefaultNoteSkin( int &sel, bool ToSel, const ConfOption *pConfOption } } +static void DefaultFailChoices(vector& out) +{ + out.push_back("Immediate"); + out.push_back("ImmediateContinue"); + out.push_back("EndOfSong"); + out.push_back("Off"); +} + +static void DefaultFailType(int& sel, bool to_sel, const ConfOption* conf_option) +{ + if(to_sel) + { + PlayerOptions po; + po.FromString(PREFSMAN->m_sDefaultModifiers); + sel= po.m_FailType; + } + else + { + PlayerOptions po; + SongOptions so; + GetPrefsDefaultModifiers(po, so); + po.m_FailType= static_cast(sel); + SetPrefsDefaultModifiers(po, so); + } +} + // Background options static void BGBrightness( int &sel, bool ToSel, const ConfOption *pConfOption ) { @@ -708,7 +736,7 @@ static void InitializeConfOptions() ADD( ConfOption( "ProgressiveLifebar", MovePref, "Off","|1","|2","|3","|4","|5","|6","|7","|8") ); ADD( ConfOption( "ProgressiveStageLifebar", MovePref, "Off","|1","|2","|3","|4","|5","|6","|7","|8","Insanity") ); ADD( ConfOption( "ProgressiveNonstopLifebar", MovePref, "Off","|1","|2","|3","|4","|5","|6","|7","|8","Insanity") ); - ADD( ConfOption( "DefaultFailType", MovePref, "Immediate","ImmediateContinue","EndOfSong","Off" ) ); + ADD( ConfOption( "DefaultFailType", DefaultFailType, DefaultFailChoices ) ); ADD( ConfOption( "CoinsPerCredit", CoinsPerCredit, "|1","|2","|3","|4","|5","|6","|7","|8","|9","|10","|11","|12","|13","|14","|15","|16" ) ); ADD( ConfOption( "Premium", MovePref, "Off","Double for 1 Credit","2 Players for 1 Credit" ) ); ADD( ConfOption( "JointPremium", JointPremium, "Off","2 Players for 1 Credit" ) ); diff --git a/src/ScreenPrompt.cpp b/src/ScreenPrompt.cpp index b033a1746a..94dd89d561 100644 --- a/src/ScreenPrompt.cpp +++ b/src/ScreenPrompt.cpp @@ -66,7 +66,12 @@ void ScreenPrompt::Init() for( int i=0; iGetPathF(m_sName,"answer") ); - LOAD_ALL_COMMANDS( m_textAnswer[i] ); + // The name of the actor isn't set because it is not known at this point + // how many answers there will be, and the name depends on the number of + // answers as a clumsy way of letting the themer set different positions + // for different answer groups. The name is set in BeginScreen, and + // then the commands are loaded. -Kyz (At least, that seems like the + // explanation to me, reading the code years after the author left) this->AddChild( &m_textAnswer[i] ); } @@ -90,6 +95,9 @@ void ScreenPrompt::BeginScreen() { RString sElem = ssprintf("Answer%dOf%d", i+1, g_PromptType+1); m_textAnswer[i].SetName( sElem ); + LOAD_ALL_COMMANDS(m_textAnswer[i]); + // Side note: Because LOAD_ALL_COMMANDS occurs here, InitCommand will + // not be run for the actors. People can just use OnCommand instead. RString sAnswer = PromptAnswerToString( (PromptAnswer)i ); // FRAGILE if( g_PromptType == PROMPT_OK ) diff --git a/src/Song.cpp b/src/Song.cpp index 9d1495a681..30bc97265e 100644 --- a/src/Song.cpp +++ b/src/Song.cpp @@ -42,7 +42,7 @@ * @brief The internal version of the cache for StepMania. * * Increment this value to invalidate the current cache. */ -const int FILE_CACHE_VERSION = 220; +const int FILE_CACHE_VERSION = 222; /** @brief How long does a song sample last by default? */ const float DEFAULT_MUSIC_SAMPLE_LENGTH = 12.f; @@ -89,6 +89,11 @@ Song::~Song() FOREACH( Steps*, m_vpSteps, s ) SAFE_DELETE( *s ); m_vpSteps.clear(); + FOREACH(Steps*, m_UnknownStyleSteps, s) + { + SAFE_DELETE(*s); + } + m_UnknownStyleSteps.clear(); // It's the responsibility of the owner of this Song to make sure // that all pointers to this Song and its Steps are invalidated. @@ -99,6 +104,7 @@ void Song::DetachSteps() m_vpSteps.clear(); FOREACH_ENUM( StepsType, st ) m_vpStepsByType[st].clear(); + m_UnknownStyleSteps.clear(); } float Song::GetFirstSecond() const @@ -154,6 +160,11 @@ void Song::Reset() m_vpSteps.clear(); FOREACH_ENUM( StepsType, st ) m_vpStepsByType[st].clear(); + FOREACH(Steps*, m_UnknownStyleSteps, s) + { + SAFE_DELETE(*s); + } + m_UnknownStyleSteps.clear(); Song empty; *this = empty; @@ -1023,6 +1034,10 @@ bool Song::SaveToSMFile() vpStepsToSave.push_back( pSteps ); } + FOREACH_CONST(Steps*, m_UnknownStyleSteps, s) + { + vpStepsToSave.push_back(*s); + } return NotesWriterSM::Write( sPath, *this, vpStepsToSave ); @@ -1055,6 +1070,10 @@ bool Song::SaveToSSCFile( RString sPath, bool bSavingCache ) pSteps->SetFilename(path); vpStepsToSave.push_back( pSteps ); } + FOREACH_CONST(Steps*, m_UnknownStyleSteps, s) + { + vpStepsToSave.push_back(*s); + } if (bSavingCache) { @@ -1504,9 +1523,19 @@ RString Song::GetTranslitFullTitle() const void Song::AddSteps( Steps* pSteps ) { - m_vpSteps.push_back( pSteps ); - ASSERT_M( pSteps->m_StepsType < NUM_StepsType, ssprintf("%i", pSteps->m_StepsType) ); - m_vpStepsByType[pSteps->m_StepsType].push_back( pSteps ); + // Songs of unknown stepstype are saved as a forwards compatibility feature + // so that editing a simfile made by a future version that has a new style + // won't delete those steps. -Kyz + if(pSteps->m_StepsType != StepsType_Invalid) + { + m_vpSteps.push_back( pSteps ); + ASSERT_M( pSteps->m_StepsType < NUM_StepsType, ssprintf("%i", pSteps->m_StepsType) ); + m_vpStepsByType[pSteps->m_StepsType].push_back( pSteps ); + } + else + { + m_UnknownStyleSteps.push_back(pSteps); + } } void Song::DeleteSteps( const Steps* pSteps, bool bReAutoGen ) diff --git a/src/Song.h b/src/Song.h index 65486e7327..d786cb1b90 100644 --- a/src/Song.h +++ b/src/Song.h @@ -447,6 +447,8 @@ private: vector m_vpSteps; /** @brief the Steps of a particular StepsType that belong to this Song. */ vector m_vpStepsByType[NUM_StepsType]; + /** @brief the Steps that are of unrecognized Styles. */ + vector m_UnknownStyleSteps; }; #endif diff --git a/src/SpecialFiles.cpp b/src/SpecialFiles.cpp index 27d4e0515a..5f6d5c50e8 100644 --- a/src/SpecialFiles.cpp +++ b/src/SpecialFiles.cpp @@ -4,6 +4,7 @@ const RString SpecialFiles::USER_PACKAGES_DIR = "UserPackages/"; const RString SpecialFiles::PACKAGES_DIR = "Packages/"; const RString SpecialFiles::KEYMAPS_PATH = "Save/Keymaps.ini"; +const RString SpecialFiles::EDIT_MODE_KEYMAPS_PATH = "Save/EditMode_Keymaps.ini"; const RString SpecialFiles::PREFERENCES_INI_PATH = "Save/Preferences.ini"; const RString SpecialFiles::THEMES_DIR = "Themes/"; const RString SpecialFiles::LANGUAGES_SUBDIR = "Languages/"; diff --git a/src/SpecialFiles.h b/src/SpecialFiles.h index 32bb54b6d2..dc6782d0d5 100644 --- a/src/SpecialFiles.h +++ b/src/SpecialFiles.h @@ -16,6 +16,10 @@ namespace SpecialFiles * This is not the user packages directory. */ extern const RString PACKAGES_DIR; extern const RString KEYMAPS_PATH; + /** @brief Edit Mode keymaps are separate from standard keymaps because + * it should not change with the gametype, and to avoid possible + * interference with the normal keymaps system. -Kyz */ + extern const RString EDIT_MODE_KEYMAPS_PATH; extern const RString PREFERENCES_INI_PATH; /** @brief The directory that contains the themes. */ extern const RString THEMES_DIR; diff --git a/src/StageStats.cpp b/src/StageStats.cpp index 0a0812bf52..348b0c0ecb 100644 --- a/src/StageStats.cpp +++ b/src/StageStats.cpp @@ -377,6 +377,7 @@ public: static int AllFailed( T* p, lua_State *L ) { lua_pushboolean(L, p->AllFailed()); return 1; } static int GetStage( T* p, lua_State *L ) { LuaHelpers::Push( L, p->m_Stage ); return 1; } DEFINE_METHOD( GetStageIndex, m_iStageIndex ) + DEFINE_METHOD(GetStepsSeconds, m_fStepsSeconds) static int PlayerHasHighScore( T* p, lua_State *L ) { lua_pushboolean(L, p->PlayerHasHighScore(Enum::Check(L, 1))); @@ -395,6 +396,7 @@ public: ADD_METHOD( AllFailed ); ADD_METHOD( GetStage ); ADD_METHOD( GetStageIndex ); + ADD_METHOD( GetStepsSeconds ); ADD_METHOD( PlayerHasHighScore ); ADD_METHOD( GetEarnedExtraStage ); } diff --git a/src/Steps.cpp b/src/Steps.cpp index 767c130f55..c7640e2e88 100644 --- a/src/Steps.cpp +++ b/src/Steps.cpp @@ -258,8 +258,20 @@ float Steps::PredictMeter() const void Steps::TidyUpData() { + // Don't set the StepsType to dance single if it's invalid. That just + // causes unrecognized charts to end up where they don't belong. + // Leave it as StepsType_Invalid so the Song can handle it specially. This + // is a forwards compatibility feature, so that if a future version adds a + // new style, editing a simfile with unrecognized Steps won't silently + // delete them. -Kyz if( m_StepsType == StepsType_Invalid ) - m_StepsType = StepsType_dance_single; + { + LOG->Warn("Detected steps with unknown style '%s' in '%s'", m_StepsTypeStr.c_str(), m_pSong->m_sSongFileName.c_str()); + } + else if(m_StepsTypeStr == "") + { + m_StepsTypeStr= GAMEMAN->GetStepsTypeInfo(m_StepsType).szName; + } if( GetDifficulty() == Difficulty_Invalid ) SetDifficulty( StringToDifficulty(GetDescription()) ); @@ -465,12 +477,14 @@ void Steps::AutogenFrom( const Steps *parent_, StepsType ntTo ) { parent = parent_; m_StepsType = ntTo; + m_StepsTypeStr= GAMEMAN->GetStepsTypeInfo(ntTo).szName; m_Timing = parent->m_Timing; } void Steps::CopyFrom( Steps* pSource, StepsType ntTo, float fMusicLengthSeconds ) // pSource does not have to be of the same StepsType { m_StepsType = ntTo; + m_StepsTypeStr= GAMEMAN->GetStepsTypeInfo(ntTo).szName; NoteData noteData; pSource->GetNoteData( noteData ); noteData.SetNumTracks( GAMEMAN->GetStepsTypeInfo(ntTo).iNumTracks ); @@ -489,6 +503,7 @@ void Steps::CopyFrom( Steps* pSource, StepsType ntTo, float fMusicLengthSeconds void Steps::CreateBlank( StepsType ntTo ) { m_StepsType = ntTo; + m_StepsTypeStr= GAMEMAN->GetStepsTypeInfo(ntTo).szName; NoteData noteData; noteData.SetNumTracks( GAMEMAN->GetStepsTypeInfo(ntTo).iNumTracks ); this->SetNoteData( noteData ); diff --git a/src/Steps.h b/src/Steps.h index 178a0ab725..05440011b6 100644 --- a/src/Steps.h +++ b/src/Steps.h @@ -177,6 +177,8 @@ public: void PushSelf( lua_State *L ); StepsType m_StepsType; + /** @brief The string form of the StepsType, for dealing with unrecognized styles. */ + RString m_StepsTypeStr; /** @brief The Song these Steps are associated with */ Song *m_pSong;