Added hold at full to actor effect timing. Added the xml to lua converter.
This commit is contained in:
@@ -373,6 +373,7 @@
|
||||
<Function name='effectoffset'/>
|
||||
<Function name='effectperiod'/>
|
||||
<Function name='effecttiming'/>
|
||||
<Function name='effect_hold_at_full'/>
|
||||
<Function name='fadebottom'/>
|
||||
<Function name='fadeleft'/>
|
||||
<Function name='faderight'/>
|
||||
|
||||
@@ -1090,13 +1090,30 @@ save yourself some time, copy this for undocumented things:
|
||||
Set the Actor's effect magnitude in each direction to the given values.
|
||||
</Function>
|
||||
<Function name='effectoffset' return='void' arguments='float fTime'>
|
||||
Set the Actor's effect offset to <code>fTime</code>.
|
||||
Set the Actor's effect offset to <code>fTime</code>. The offset is added to the time into the effect before calculating percent_through_effect.
|
||||
</Function>
|
||||
<Function name='effectperiod' return='void' arguments='float fTime'>
|
||||
Set the Actor's effect period to <code>fTime</code>.
|
||||
</Function>
|
||||
<Function name='effecttiming' return='void' arguments='float fRampUp, float fAtHalf, float fRampDown, float fAtZero'>
|
||||
Set the Actor's effect timing.
|
||||
<Function name='effecttiming' return='void' arguments='float ramp_to_half, float hold_at_half, float ramp_to_full, float hold_at_zero, float hold_at_full'>
|
||||
Set the Actor's effect timing.<br />
|
||||
hold_at_zero is before hold_at_full in the argument list for compatibility. A future version will probably swap them because it makes more sense to have hold_at_full come before hold_at_zero.<br />
|
||||
All effect timings must be greater than or equal to zero, at least one of them must be greater than zero.<br />
|
||||
The effect timing controls how long it takes an effect to cycle and how long it spends in each phase.<br />
|
||||
Depending on the effect clock, the actor's time into effect is updated every frame. That time is then translated into a percent_through_effect using the parameters to this function.<br />
|
||||
<br />
|
||||
ramp_to_half is the amount of time for percent_through_effect to reach 0.5.<br />
|
||||
hold_at_half is the amount of time percent_through_effect will stay at 0.5.<br />
|
||||
ramp_to_full is the amount of time percent_through_effect will take to go from 0.5 to 1.0.<br />
|
||||
hold_at_full is the amount of time percent_through_effect will stay at 1.0.<br />
|
||||
After reaching the end of hold_at_full, percent_through_effect stays at 0 until hold_at_zero is over.<br />
|
||||
<br />
|
||||
The different effects use percent_through_effect in different ways. Some use it to calculate percent_between_colors with this sine wave: sin((percent_through_effect + 0.25f) * 2 * PI ) / 2 + 0.5f<br />
|
||||
Some effects check the internal bool blink_on. blink_on is true if percent_through_effect is greater than 0.5 and false if percent_through_effect is less than or equal to 0.5.<br />
|
||||
Check the effect functions for individual explanations: diffuseblink, diffuseshift, glowblink, glowshift, glowramp, rainbow, wag, bounce, bob, pulse, spin, vibrate.
|
||||
</Function>
|
||||
<Function name='effect_hold_at_full' return='void' arguments='float hold_at_full'>
|
||||
Set the hold_at_full part of the effect timing while leaving the others unchanged.
|
||||
</Function>
|
||||
<Function name='fadebottom' return='void' arguments='float percent'>
|
||||
Fades <code>percent</code> of the Actor from the bottom where <code>percent</code> is in the range 0..1.
|
||||
|
||||
+77
-46
@@ -223,10 +223,11 @@ Actor::Actor( const Actor &cpy ):
|
||||
#endif
|
||||
CPY( m_fSecsIntoEffect );
|
||||
CPY( m_fEffectDelta );
|
||||
CPY( m_fEffectRampUp );
|
||||
CPY( m_fEffectHoldAtHalf );
|
||||
CPY( m_fEffectRampDown );
|
||||
CPY( m_fEffectHoldAtZero );
|
||||
CPY(m_effect_ramp_to_half);
|
||||
CPY(m_effect_hold_at_half);
|
||||
CPY(m_effect_ramp_to_full);
|
||||
CPY(m_effect_hold_at_full);
|
||||
CPY(m_effect_hold_at_zero);
|
||||
CPY(m_effect_period);
|
||||
CPY( m_fEffectOffset );
|
||||
CPY( m_EffectClock );
|
||||
@@ -440,27 +441,26 @@ void Actor::PreDraw() // calculate actor properties
|
||||
const float fTimeIntoEffect = fmodfp( m_fSecsIntoEffect+m_fEffectOffset, fTotalPeriod );
|
||||
|
||||
float fPercentThroughEffect;
|
||||
if( fTimeIntoEffect < m_fEffectRampUp )
|
||||
const float rup_plus_ath= m_effect_ramp_to_half + m_effect_hold_at_half;
|
||||
const float rupath_plus_rdown= rup_plus_ath + m_effect_ramp_to_full;
|
||||
const float rupathrdown_plus_atf= rupath_plus_rdown + m_effect_hold_at_full;
|
||||
if(fTimeIntoEffect < m_effect_ramp_to_half)
|
||||
{
|
||||
fPercentThroughEffect = SCALE(
|
||||
fTimeIntoEffect,
|
||||
0,
|
||||
m_fEffectRampUp,
|
||||
0.0f,
|
||||
0.5f );
|
||||
fPercentThroughEffect = SCALE(fTimeIntoEffect,
|
||||
0, m_effect_ramp_to_half, 0.0f, 0.5f);
|
||||
}
|
||||
else if( fTimeIntoEffect < m_fEffectRampUp + m_fEffectHoldAtHalf )
|
||||
else if(fTimeIntoEffect < rup_plus_ath)
|
||||
{
|
||||
fPercentThroughEffect = 0.5f;
|
||||
}
|
||||
else if( fTimeIntoEffect < m_fEffectRampUp + m_fEffectHoldAtHalf + m_fEffectRampDown )
|
||||
else if(fTimeIntoEffect < rupath_plus_rdown)
|
||||
{
|
||||
fPercentThroughEffect = SCALE(
|
||||
fTimeIntoEffect,
|
||||
m_fEffectRampUp + m_fEffectHoldAtHalf,
|
||||
m_fEffectRampUp + m_fEffectHoldAtHalf + m_fEffectRampDown,
|
||||
0.5f,
|
||||
1.0f );
|
||||
fPercentThroughEffect = SCALE(fTimeIntoEffect,
|
||||
rup_plus_ath, rupath_plus_rdown, 0.5f, 1.0f);
|
||||
}
|
||||
else if(fTimeIntoEffect < rupathrdown_plus_atf)
|
||||
{
|
||||
fPercentThroughEffect= 1.0f;
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -1062,31 +1062,51 @@ void Actor::StretchTo( const RectF &r )
|
||||
SetZoomY( fNewZoomY );
|
||||
}
|
||||
|
||||
|
||||
void Actor::SetEffectPeriod( float fTime )
|
||||
void Actor::RecalcEffectPeriod()
|
||||
{
|
||||
ASSERT( fTime > 0 );
|
||||
m_fEffectRampUp = fTime/2;
|
||||
m_fEffectHoldAtHalf = 0;
|
||||
m_fEffectRampDown = fTime/2;
|
||||
m_fEffectHoldAtZero = 0;
|
||||
m_effect_period= m_fEffectRampUp + m_fEffectHoldAtHalf + m_fEffectRampDown +
|
||||
m_fEffectHoldAtZero;
|
||||
m_effect_period= m_effect_ramp_to_half + m_effect_hold_at_half +
|
||||
m_effect_ramp_to_full + m_effect_hold_at_full + m_effect_hold_at_zero;
|
||||
}
|
||||
|
||||
void Actor::SetEffectTiming( float fRampUp, float fAtHalf, float fRampDown, float fAtZero )
|
||||
void Actor::SetEffectPeriod(float time)
|
||||
{
|
||||
ASSERT(time > 0);
|
||||
m_effect_ramp_to_half= time/2;
|
||||
m_effect_hold_at_half= 0;
|
||||
m_effect_ramp_to_full= time/2;
|
||||
m_effect_hold_at_full= 0;
|
||||
m_effect_hold_at_zero= 0;
|
||||
RecalcEffectPeriod();
|
||||
}
|
||||
|
||||
bool Actor::SetEffectTiming(float ramp_toh, float at_half, float ramp_tof, float at_full, float at_zero, RString& err)
|
||||
{
|
||||
// No negative timings
|
||||
ASSERT( fRampUp >= 0 && fAtHalf >= 0 && fRampDown >= 0 && fAtZero >= 0 );
|
||||
if(ramp_toh < 0 || at_half < 0 || ramp_tof < 0 || at_full < 0 || at_zero < 0)
|
||||
{
|
||||
err= ssprintf("Effect timings (%f,%f,%f,%f,%f) must not be negative;",
|
||||
ramp_toh, at_half, ramp_tof, at_zero, at_full);
|
||||
return false;
|
||||
}
|
||||
// and at least one positive timing.
|
||||
ASSERT( fRampUp > 0 || fAtHalf > 0 || fRampDown > 0 || fAtZero > 0 );
|
||||
if(ramp_toh <= 0 && at_half <= 0 && ramp_tof <= 0 && at_full <= 0 && at_zero <= 0)
|
||||
{
|
||||
err= ssprintf("Effect timings (0,0,0,0,0) must not all be zero;");
|
||||
return false;
|
||||
}
|
||||
m_effect_ramp_to_half= ramp_toh;
|
||||
m_effect_hold_at_half= at_half;
|
||||
m_effect_ramp_to_full= ramp_tof;
|
||||
m_effect_hold_at_full= at_full;
|
||||
m_effect_hold_at_zero= at_zero;
|
||||
RecalcEffectPeriod();
|
||||
return true;
|
||||
}
|
||||
|
||||
m_fEffectRampUp = fRampUp;
|
||||
m_fEffectHoldAtHalf = fAtHalf;
|
||||
m_fEffectRampDown = fRampDown;
|
||||
m_fEffectHoldAtZero = fAtZero;
|
||||
m_effect_period= m_fEffectRampUp + m_fEffectHoldAtHalf + m_fEffectRampDown +
|
||||
m_fEffectHoldAtZero;
|
||||
bool Actor::SetEffectHoldAtFull(float haf, RString& err)
|
||||
{
|
||||
return SetEffectTiming(m_effect_ramp_to_half, m_effect_hold_at_half,
|
||||
m_effect_ramp_to_full, haf, m_effect_hold_at_zero, err);
|
||||
}
|
||||
|
||||
// effect "macros"
|
||||
@@ -1693,19 +1713,30 @@ public:
|
||||
}
|
||||
static int effecttiming( T* p, lua_State *L )
|
||||
{
|
||||
float f1 = FArg(1), f2 = FArg(2), f3 = FArg(3), f4 = FArg(4);
|
||||
if (f1 < 0 || f2 < 0 || f3 < 0 || f4 < 0)
|
||||
float rth= FArg(1);
|
||||
float hah= FArg(2);
|
||||
float rtf= FArg(3);
|
||||
float haz= FArg(4);
|
||||
// Compatibility: hold_at_full is optional.
|
||||
float haf= 0;
|
||||
if(lua_isnumber(L, 5))
|
||||
{
|
||||
LuaHelpers::ReportScriptErrorFmt("Effect timings (%f,%f,%f,%f) must not be negative; ignoring",
|
||||
f1, f2, f3, f4);
|
||||
COMMON_RETURN_SELF;
|
||||
haf= FArg(5);
|
||||
}
|
||||
if (f1 == 0 && f2 == 0 && f3 == 0 && f4 == 0)
|
||||
RString err;
|
||||
if(!p->SetEffectTiming(rth, hah, rtf, haf, haz, err))
|
||||
{
|
||||
LuaHelpers::ReportScriptErrorFmt("Effect timings (0,0,0,0) must not all be zero; ignoring");
|
||||
COMMON_RETURN_SELF;
|
||||
luaL_error(L, err.c_str());
|
||||
}
|
||||
COMMON_RETURN_SELF;
|
||||
}
|
||||
static int effect_hold_at_full(T* p, lua_State*L)
|
||||
{
|
||||
RString err;
|
||||
if(!p->SetEffectHoldAtFull(FArg(1), err))
|
||||
{
|
||||
luaL_error(L, err.c_str());
|
||||
}
|
||||
p->SetEffectTiming(FArg(1), FArg(2), FArg(3), FArg(4));
|
||||
COMMON_RETURN_SELF;
|
||||
}
|
||||
static int effectoffset( T* p, lua_State *L ) { p->SetEffectOffset(FArg(1)); COMMON_RETURN_SELF; }
|
||||
|
||||
+9
-5
@@ -526,9 +526,12 @@ public:
|
||||
// todo: account for SSC_FUTURES by adding an effect as an arg to each one -aj
|
||||
void SetEffectColor1( RageColor c ) { m_effectColor1 = c; }
|
||||
void SetEffectColor2( RageColor c ) { m_effectColor2 = c; }
|
||||
void RecalcEffectPeriod();
|
||||
void SetEffectPeriod( float fTime );
|
||||
float GetEffectPeriod() const { return m_effect_period; }
|
||||
void SetEffectTiming( float fRampUp, float fAtHalf, float fRampDown, float fAtZero );
|
||||
bool SetEffectTiming(float ramp_toh, float at_half, float ramp_tof,
|
||||
float at_zero, float at_full, RString& err);
|
||||
bool SetEffectHoldAtFull(float haf, RString& err);
|
||||
void SetEffectOffset( float fTime ) { m_fEffectOffset = fTime; }
|
||||
void SetEffectClock( EffectClock c ) { m_EffectClock = c; }
|
||||
void SetEffectClockString( const RString &s ); // convenience
|
||||
@@ -686,10 +689,11 @@ protected:
|
||||
float m_fEffectDelta;
|
||||
|
||||
// units depend on m_EffectClock
|
||||
float m_fEffectRampUp;
|
||||
float m_fEffectHoldAtHalf;
|
||||
float m_fEffectRampDown;
|
||||
float m_fEffectHoldAtZero;
|
||||
float m_effect_ramp_to_half;
|
||||
float m_effect_hold_at_half;
|
||||
float m_effect_ramp_to_full;
|
||||
float m_effect_hold_at_full;
|
||||
float m_effect_hold_at_zero;
|
||||
float m_fEffectOffset;
|
||||
// Anything changing ramp_up, hold_at_half, ramp_down, or hold_at_zero must
|
||||
// also update the period so the period is only calculated when changed.
|
||||
|
||||
@@ -111,6 +111,7 @@ list(APPEND SMDATA_FILE_TYPES_SRC
|
||||
"IniFile.cpp"
|
||||
"MsdFile.cpp"
|
||||
"XmlFile.cpp"
|
||||
"XmlToLua.cpp"
|
||||
"XmlFileUtil.cpp"
|
||||
)
|
||||
list(APPEND SMDATA_FILE_TYPES_HPP
|
||||
|
||||
@@ -830,7 +830,7 @@ try_element_again:
|
||||
GetThemeDirFromName(SpecialFiles::BASE_THEME_NAME) + "\".";
|
||||
LOG->UserLog("Theme element", element.c_str(), "%s", error.c_str());
|
||||
LOG->Warn( "%s %s", element.c_str(), error.c_str());
|
||||
LuaHelpers::ScriptErrorMessage(error);
|
||||
LuaHelpers::ScriptErrorMessage("'" + element + "' " + error);
|
||||
}
|
||||
|
||||
// Err?
|
||||
|
||||
@@ -0,0 +1,796 @@
|
||||
#include "global.h"
|
||||
#include "ActorUtil.h"
|
||||
#include "Foreach.h"
|
||||
#include "IniFile.h"
|
||||
#include "RageFile.h"
|
||||
#include "RageFileManager.h"
|
||||
#include "RageLog.h"
|
||||
#include "RageTypes.h"
|
||||
#include "LuaManager.h"
|
||||
#include "LuaBinding.h"
|
||||
#include "XmlFile.h"
|
||||
#include "XmlFileUtil.h"
|
||||
|
||||
#include <vector>
|
||||
#include <map>
|
||||
#include <set>
|
||||
|
||||
#define TWEEN_QUEUE_MAX 50
|
||||
|
||||
RString unique_name();
|
||||
void convert_xmls_in_dir(RString const& dirname);
|
||||
void convert_xml_file(RString const& fname, RString const& dirname);
|
||||
RString maybe_conv_pos(RString pos, RString (*conv_func)(float p));
|
||||
RString add_extension_to_relative_path_from_found_file(
|
||||
RString const& relative_path, RString const& found_file);
|
||||
|
||||
RString unique_name(RString const& type)
|
||||
{
|
||||
static char const* name_chars= "abcdefghijklmnopqrstuvwxyz";
|
||||
static int name_count= 0;
|
||||
int curr_name= name_count;
|
||||
RString ret= "xtl_" + type + "_"; // Minimize the chance of a name collision.
|
||||
ret= ret + name_chars[curr_name%26];
|
||||
while(curr_name / 26 > 0)
|
||||
{
|
||||
curr_name= curr_name / 26;
|
||||
ret= ret + name_chars[curr_name%26];
|
||||
}
|
||||
++name_count;
|
||||
return ret;
|
||||
}
|
||||
|
||||
void convert_xmls_in_dir(RString const& dirname)
|
||||
{
|
||||
vector<RString> listing;
|
||||
FILEMAN->GetDirListing(dirname, listing, false, true);
|
||||
for(vector<RString>::iterator curr_file= listing.begin();
|
||||
curr_file != listing.end(); ++curr_file)
|
||||
{
|
||||
switch(ActorUtil::GetFileType(*curr_file))
|
||||
{
|
||||
case FT_Xml:
|
||||
convert_xml_file(*curr_file, dirname);
|
||||
break;
|
||||
case FT_Directory:
|
||||
convert_xmls_in_dir((*curr_file) + "/");
|
||||
break;
|
||||
default: // Ignore anything not xml or directory.
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
RString convert_xpos(float x)
|
||||
{
|
||||
return "SCREEN_CENTER_X + " + FloatToString(x - 320.0f);
|
||||
}
|
||||
|
||||
RString convert_ypos(float y)
|
||||
{
|
||||
return "SCREEN_CENTER_Y + " + FloatToString(y - 240.0f);
|
||||
}
|
||||
|
||||
RString maybe_conv_pos(RString pos, RString (*conv_func)(float p))
|
||||
{
|
||||
float f;
|
||||
if(pos >> f)
|
||||
{
|
||||
return conv_func(f);
|
||||
}
|
||||
return pos;
|
||||
}
|
||||
|
||||
size_t after_slash_or_zero(RString const& s)
|
||||
{
|
||||
size_t ret= s.rfind('/');
|
||||
if(ret != string::npos)
|
||||
{
|
||||
return ret+1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
RString add_extension_to_relative_path_from_found_file(
|
||||
RString const& relative_path, RString const& found_file)
|
||||
{
|
||||
size_t rel_last_slash= after_slash_or_zero(relative_path);
|
||||
size_t found_last_slash= after_slash_or_zero(found_file);
|
||||
return relative_path.Left(rel_last_slash) +
|
||||
found_file.substr(found_last_slash, string::npos);
|
||||
}
|
||||
|
||||
bool verify_arg_count(RString cmd, vector<RString>& args, size_t req)
|
||||
{
|
||||
if(args.size() < req)
|
||||
{
|
||||
LuaHelpers::ReportScriptError("Not enough args to " + cmd + " command.");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
typedef void (*arg_converter_t)(vector<RString>& args);
|
||||
|
||||
map<RString, arg_converter_t> arg_converters;
|
||||
map<RString, size_t> tween_counters;
|
||||
set<RString> fields_that_are_strings;
|
||||
map<RString, RString> chunks_to_replace;
|
||||
|
||||
#define COMMON_ARG_VERIFY(count) if(!verify_arg_count(args[0], args, count)) return;
|
||||
void x_conv(vector<RString>& args)
|
||||
{
|
||||
COMMON_ARG_VERIFY(2);
|
||||
float pos;
|
||||
if(args[1] >> pos)
|
||||
{
|
||||
args[1]= convert_xpos(pos);
|
||||
}
|
||||
}
|
||||
void y_conv(vector<RString>& args)
|
||||
{
|
||||
COMMON_ARG_VERIFY(2);
|
||||
float pos;
|
||||
if(args[1] >> pos)
|
||||
{
|
||||
args[1]= convert_ypos(pos);
|
||||
}
|
||||
}
|
||||
void string_arg_conv(vector<RString>& args)
|
||||
{
|
||||
COMMON_ARG_VERIFY(2);
|
||||
args[1]= "\"" + args[1] + "\"";
|
||||
}
|
||||
void lower_string_conv(vector<RString>& args)
|
||||
{
|
||||
args[0].MakeLower();
|
||||
}
|
||||
void hidden_conv(vector<RString>& args)
|
||||
{
|
||||
COMMON_ARG_VERIFY(2);
|
||||
args[0]= "visible";
|
||||
if(args[1] == "1")
|
||||
{
|
||||
args[1]= "true";
|
||||
}
|
||||
else
|
||||
{
|
||||
args[1]= "false";
|
||||
}
|
||||
}
|
||||
void diffuse_conv(vector<RString>& args)
|
||||
{
|
||||
COMMON_ARG_VERIFY(2);
|
||||
RString retarg;
|
||||
for(size_t i= 1; i < args.size(); ++i)
|
||||
{
|
||||
retarg+= args[i];
|
||||
if(i < args.size()-1)
|
||||
{
|
||||
retarg+= ",";
|
||||
}
|
||||
}
|
||||
args[1]= "color('" + retarg + "')";
|
||||
args.resize(2);
|
||||
}
|
||||
|
||||
// Prototype for a function that is created by a macro in another translation unit and has no visible prototype, don't do this unless you have a good reason.
|
||||
const RString& BlendModeToString(BlendMode);
|
||||
const RString& CullModeToString(CullMode);
|
||||
void blend_conv(vector<RString>& args)
|
||||
{
|
||||
COMMON_ARG_VERIFY(2);
|
||||
for(int i= 0; i < NUM_BlendMode; ++i)
|
||||
{
|
||||
RString blend_str= BlendModeToString(static_cast<BlendMode>(i));
|
||||
blend_str.MakeLower();
|
||||
if(args[1] == blend_str)
|
||||
{
|
||||
args[1]= "\"BlendMode_" + BlendModeToString(static_cast<BlendMode>(i)) + "\"";
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
void cull_conv(vector<RString>& args)
|
||||
{
|
||||
COMMON_ARG_VERIFY(2);
|
||||
for(int i= 0; i < NUM_CullMode; ++i)
|
||||
{
|
||||
RString cull_str= CullModeToString(static_cast<CullMode>(i));
|
||||
cull_str.MakeLower();
|
||||
if(args[1] == cull_str)
|
||||
{
|
||||
args[1]= "\"CullMode_" + CullModeToString(static_cast<CullMode>(i)) + "\"";
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
#undef COMMON_ARG_VERIFY
|
||||
|
||||
void init_parser_helpers()
|
||||
{
|
||||
arg_converters["x"]= &x_conv;
|
||||
arg_converters["y"]= &y_conv;
|
||||
arg_converters["queuecommand"]= &string_arg_conv;
|
||||
arg_converters["playcommand"]= &string_arg_conv;
|
||||
arg_converters["effectclock"]= &string_arg_conv;
|
||||
arg_converters["EffectMagnitude"]= &lower_string_conv;
|
||||
arg_converters["ZoomToWidth"]= &lower_string_conv;
|
||||
arg_converters["ZoomToHeight"]= &lower_string_conv;
|
||||
arg_converters["blend"]= &blend_conv;
|
||||
arg_converters["cullmode"]= &cull_conv;
|
||||
arg_converters["hidden"]= &hidden_conv;
|
||||
arg_converters["diffuse"]= &diffuse_conv;
|
||||
arg_converters["effectcolor1"]= &diffuse_conv;
|
||||
arg_converters["effectcolor2"]= &diffuse_conv;
|
||||
tween_counters["sleep"]= 2;
|
||||
tween_counters["linear"]= 1;
|
||||
tween_counters["accelerate"]= 1;
|
||||
tween_counters["decelerate"]= 1;
|
||||
tween_counters["spring"]= 1;
|
||||
tween_counters["tween"]= 1;
|
||||
tween_counters["queuecommand"]= 1;
|
||||
fields_that_are_strings.insert("Texture");
|
||||
fields_that_are_strings.insert("Text");
|
||||
fields_that_are_strings.insert("AltText");
|
||||
fields_that_are_strings.insert("File");
|
||||
fields_that_are_strings.insert("Font");
|
||||
fields_that_are_strings.insert("Meshes");
|
||||
fields_that_are_strings.insert("Materials");
|
||||
fields_that_are_strings.insert("Bones");
|
||||
chunks_to_replace["hidden(0)"]= "visible(true)";
|
||||
chunks_to_replace["hidden(1)"]= "visible(false)";
|
||||
chunks_to_replace["effectdelay"]= "effect_hold_at_full";
|
||||
chunks_to_replace["IsPlayerEnabled(0)"]= "IsPlayerEnabled(PLAYER_1)";
|
||||
chunks_to_replace["IsPlayerEnabled(1)"]= "IsPlayerEnabled(PLAYER_2)";
|
||||
}
|
||||
|
||||
// Conditions are mapped by condition string.
|
||||
// So condition_set_t::iterator->first is the lua to execute for the
|
||||
// condition, and condition_set_t::iterator->second is the name of the
|
||||
// condition.
|
||||
typedef map<RString, RString> condition_set_t;
|
||||
typedef map<RString, RString> field_cont_t;
|
||||
struct frame_t
|
||||
{
|
||||
int frame;
|
||||
float delay;
|
||||
frame_t() :frame(0), delay(0.0f) {}
|
||||
};
|
||||
|
||||
struct actor_template_t
|
||||
{
|
||||
RString type;
|
||||
field_cont_t fields;
|
||||
RString condition;
|
||||
RString name;
|
||||
vector<frame_t> frames;
|
||||
vector<actor_template_t> children;
|
||||
RString x;
|
||||
RString y;
|
||||
void make_space_for_frame(int id);
|
||||
void store_cmd(RString const& cmd_name, RString const& full_cmd);
|
||||
void store_field(RString const& field_name, RString const& value, bool cmd_convert, RString const& pref= "", RString const& suf= "");
|
||||
void store_field(RString const& field_name, XNodeValue const* value, bool cmd_convert, RString const& pref= "", RString const& suf= "");
|
||||
void rename_field(RString const& old_name, RString const& new_name);
|
||||
RString get_field(RString const& field_name);
|
||||
void load_frames_from_file(RString const& fname, RString const& rel_path);
|
||||
void load_model_from_file(RString const& fname, RString const& rel_path);
|
||||
void load_node(XNode const& node, RString const& dirname, condition_set_t& conditions);
|
||||
void output_to_file(RageFile* file, RString const& indent);
|
||||
};
|
||||
|
||||
void actor_template_t::make_space_for_frame(int id)
|
||||
{
|
||||
if(id >= static_cast<int>(frames.size()))
|
||||
{
|
||||
frames.resize(id+1);
|
||||
}
|
||||
}
|
||||
|
||||
void actor_template_t::store_cmd(RString const& cmd_name, RString const& full_cmd)
|
||||
{
|
||||
if(full_cmd.Left(1) == "%")
|
||||
{
|
||||
RString cmd_text= full_cmd.Right(full_cmd.size()-1);
|
||||
for(map<RString, RString>::iterator chunk= chunks_to_replace.begin();
|
||||
chunk != chunks_to_replace.end(); ++chunk)
|
||||
{
|
||||
cmd_text.Replace(chunk->first, chunk->second);
|
||||
}
|
||||
fields[cmd_name]= cmd_text;
|
||||
return;
|
||||
}
|
||||
vector<RString> cmds;
|
||||
split(full_cmd, ";", cmds, true);
|
||||
size_t queue_size= 0;
|
||||
// If someone has a simfile that uses a playcommand that pushes tween
|
||||
// states onto the queue, queue size counting will have to be made much
|
||||
// more complex to prevent that from causing an overflow.
|
||||
for(vector<RString>::iterator cmd= cmds.begin(); cmd != cmds.end(); ++cmd)
|
||||
{
|
||||
vector<RString> args;
|
||||
split(*cmd, ",", args, true);
|
||||
if(!args.empty())
|
||||
{
|
||||
for(vector<RString>::iterator arg= args.begin(); arg != args.end(); ++arg)
|
||||
{
|
||||
size_t first_nonspace= 0;
|
||||
size_t last_nonspace= arg->size();
|
||||
while((*arg)[first_nonspace] == ' ')
|
||||
{
|
||||
++first_nonspace;
|
||||
}
|
||||
while((*arg)[last_nonspace] == ' ')
|
||||
{
|
||||
--last_nonspace;
|
||||
}
|
||||
*arg= arg->substr(first_nonspace, last_nonspace - first_nonspace);
|
||||
}
|
||||
map<RString, arg_converter_t>::iterator conv= arg_converters.find(args[0]);
|
||||
if(conv != arg_converters.end())
|
||||
{
|
||||
conv->second(args);
|
||||
}
|
||||
map<RString, size_t>::iterator counter= tween_counters.find(args[0]);
|
||||
if(counter != tween_counters.end())
|
||||
{
|
||||
queue_size+= counter->second;
|
||||
}
|
||||
}
|
||||
*cmd= join(",", args);
|
||||
}
|
||||
// This code is probably actually useless, OITG has the same tween queue size
|
||||
// and the real reason I saw overflows in converted files was a bug in
|
||||
// foreground loading that ran InitCommand twice. -Kyz
|
||||
if(queue_size >= TWEEN_QUEUE_MAX)
|
||||
{
|
||||
size_t num_to_make= (queue_size / TWEEN_QUEUE_MAX) + 1;
|
||||
size_t states_per= (queue_size / num_to_make) + 1;
|
||||
size_t states_in_curr= 0;
|
||||
RString this_name= cmd_name;
|
||||
vector<RString> curr_cmd;
|
||||
for(vector<RString>::iterator cmd= cmds.begin(); cmd != cmds.end(); ++cmd)
|
||||
{
|
||||
curr_cmd.push_back(*cmd);
|
||||
vector<RString> args;
|
||||
split(*cmd, ",", args, true);
|
||||
if(!args.empty())
|
||||
{
|
||||
map<RString, size_t>::iterator counter= tween_counters.find(args[0]);
|
||||
if(counter != tween_counters.end())
|
||||
{
|
||||
states_in_curr+= counter->second;
|
||||
if(states_in_curr >= states_per - 1)
|
||||
{
|
||||
RString next_name= unique_name("cmd");
|
||||
curr_cmd.push_back("queuecommand,\"" + next_name + "\"");
|
||||
fields[this_name]= "cmd(" + join(";", curr_cmd) + ")";
|
||||
curr_cmd.clear();
|
||||
this_name= next_name;
|
||||
states_in_curr= 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if(!curr_cmd.empty())
|
||||
{
|
||||
fields[this_name]= "cmd(" + join(";", curr_cmd) + ")";
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
fields[cmd_name]= "cmd(" + join(";", cmds) + ")";
|
||||
}
|
||||
}
|
||||
|
||||
void actor_template_t::store_field(RString const& field_name, RString const& value, bool cmd_convert, RString const& pref, RString const& suf)
|
||||
{
|
||||
// OITG apparently allowed "Oncommand" as valid.
|
||||
if(field_name.Right(7).MakeLower() != "command")
|
||||
{
|
||||
cmd_convert= false;
|
||||
}
|
||||
if(cmd_convert)
|
||||
{
|
||||
RString real_field_name= field_name.Left(field_name.size()-7) + "Command";
|
||||
store_cmd(real_field_name, value);
|
||||
}
|
||||
else
|
||||
{
|
||||
fields[field_name]= pref + value + suf;
|
||||
}
|
||||
|
||||
}
|
||||
void actor_template_t::store_field(RString const& field_name, XNodeValue const* value, bool cmd_convert, RString const& pref, RString const& suf)
|
||||
{
|
||||
RString val;
|
||||
value->GetValue(val);
|
||||
store_field(field_name, val, cmd_convert, pref, suf);
|
||||
}
|
||||
|
||||
void actor_template_t::rename_field(RString const& old_name, RString const& new_name)
|
||||
{
|
||||
field_cont_t::iterator old_field= fields.find(old_name);
|
||||
if(old_field == fields.end())
|
||||
{
|
||||
return;
|
||||
}
|
||||
fields[new_name]= old_field->second;
|
||||
fields.erase(old_field);
|
||||
}
|
||||
|
||||
RString actor_template_t::get_field(RString const& field_name)
|
||||
{
|
||||
field_cont_t::iterator field= fields.find(field_name);
|
||||
if(field == fields.end())
|
||||
{
|
||||
return "";
|
||||
}
|
||||
return field->second;
|
||||
}
|
||||
|
||||
void actor_template_t::load_frames_from_file(RString const& fname, RString const& rel_path)
|
||||
{
|
||||
IniFile ini;
|
||||
if(!ini.ReadFile(fname))
|
||||
{
|
||||
LOG->Trace("Failed to read sprite file %s: %s", fname.c_str(), ini.GetError().c_str());
|
||||
return;
|
||||
}
|
||||
XNode const* sprite_node= ini.GetChild("Sprite");
|
||||
if(sprite_node != NULL)
|
||||
{
|
||||
FOREACH_CONST_Attr(sprite_node, attr)
|
||||
{
|
||||
// Frame and Delay fields have names of the form "Frame0000" where the
|
||||
// "0000" part is the id of the frame.
|
||||
RString field_type= attr->first.Left(5);
|
||||
if(field_type == "Frame")
|
||||
{
|
||||
int id= StringToInt(attr->first.Right(attr->first.size()-5));
|
||||
make_space_for_frame(id);
|
||||
attr->second->GetValue(frames[id].frame);
|
||||
}
|
||||
else if(field_type == "Delay")
|
||||
{
|
||||
int id= StringToInt(attr->first.Right(attr->first.size()-5));
|
||||
make_space_for_frame(id);
|
||||
attr->second->GetValue(frames[id].delay);
|
||||
}
|
||||
else if(field_type == "Textu")
|
||||
{
|
||||
store_field("Texture", attr->second, false, rel_path, "");
|
||||
}
|
||||
else
|
||||
{
|
||||
// Unrecognized, ignore.
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void actor_template_t::load_model_from_file(RString const& fname, RString const& rel_path)
|
||||
{
|
||||
IniFile ini;
|
||||
if(!ini.ReadFile(fname))
|
||||
{
|
||||
LOG->Trace("Failed to read model file %s: %s", fname.c_str(), ini.GetError().c_str());
|
||||
return;
|
||||
}
|
||||
XNode const* model_node= ini.GetChild("Model");
|
||||
if(model_node != NULL)
|
||||
{
|
||||
FOREACH_CONST_Attr(model_node, attr)
|
||||
{
|
||||
store_field(attr->first, attr->second, false, rel_path, "");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void actor_template_t::load_node(XNode const& node, RString const& dirname, condition_set_t& conditions)
|
||||
{
|
||||
type= node.GetName();
|
||||
bool type_set_by_automagic= false;
|
||||
FOREACH_CONST_Attr(&node, attr)
|
||||
{
|
||||
if(attr->first == "Name")
|
||||
{
|
||||
attr->second->GetValue(name);
|
||||
}
|
||||
else if(attr->first == "Condition")
|
||||
{
|
||||
RString cond_str;
|
||||
attr->second->GetValue(cond_str);
|
||||
condition_set_t::iterator cond= conditions.find(cond_str);
|
||||
if(cond == conditions.end())
|
||||
{
|
||||
condition= unique_name("cond");
|
||||
conditions[cond_str]= condition;
|
||||
}
|
||||
else
|
||||
{
|
||||
condition= cond->second;
|
||||
}
|
||||
}
|
||||
else if(attr->first == "Type" || attr->first == "Class")
|
||||
{
|
||||
if(!type_set_by_automagic)
|
||||
{
|
||||
attr->second->GetValue(type);
|
||||
}
|
||||
}
|
||||
else if(attr->first == "__TEXT__")
|
||||
{
|
||||
// Ignore. This attribute seems to be put in by the xml parser, and
|
||||
// not part of the actual xml code.
|
||||
}
|
||||
else if(attr->first == "File")
|
||||
{
|
||||
RString relative_path;
|
||||
attr->second->GetValue(relative_path);
|
||||
RString sfname= dirname + relative_path;
|
||||
if(FILEMAN->IsADirectory(sfname))
|
||||
{
|
||||
type= "LoadActor";
|
||||
store_field("File", attr->second, false);
|
||||
}
|
||||
else
|
||||
{
|
||||
vector<RString> files_in_dir;
|
||||
FILEMAN->GetDirListing(sfname + "*", files_in_dir, false, true);
|
||||
int handled_level= 0;
|
||||
RString found_file= "";
|
||||
for(vector<RString>::iterator file= files_in_dir.begin();
|
||||
file != files_in_dir.end() && handled_level < 2; ++file)
|
||||
{
|
||||
RString extension= GetExtension(*file);
|
||||
FileType file_type= ActorUtil::GetFileType(*file);
|
||||
RString this_relative=
|
||||
add_extension_to_relative_path_from_found_file(relative_path, *file);
|
||||
switch(file_type)
|
||||
{
|
||||
case FT_Xml:
|
||||
this_relative= SetExtension(this_relative, "lua");
|
||||
case FT_Lua:
|
||||
type= "LoadActor";
|
||||
store_field("File", this_relative, false);
|
||||
handled_level= 2;
|
||||
break;
|
||||
case FT_Ini:
|
||||
break;
|
||||
case FT_Bitmap:
|
||||
case FT_Movie:
|
||||
if(handled_level < 2)
|
||||
{
|
||||
type= "Sprite";
|
||||
store_field("File", this_relative, false);
|
||||
handled_level= 1;
|
||||
}
|
||||
break;
|
||||
case FT_Sound:
|
||||
type= "ActorSound";
|
||||
store_field("File", this_relative, false);
|
||||
handled_level= 1;
|
||||
break;
|
||||
case FT_Sprite:
|
||||
type= "Sprite";
|
||||
load_frames_from_file(dirname + this_relative, Dirname(relative_path));
|
||||
handled_level= 2;
|
||||
break;
|
||||
case FT_Model:
|
||||
type= "Model";
|
||||
store_field("Meshes", this_relative, false);
|
||||
store_field("Materials", this_relative, false);
|
||||
store_field("Bones", this_relative, false);
|
||||
handled_level= 2;
|
||||
break;
|
||||
}
|
||||
if(!handled_level)
|
||||
{
|
||||
if(extension == "model")
|
||||
{
|
||||
type= "Model";
|
||||
load_model_from_file(dirname + this_relative, Dirname(relative_path));
|
||||
handled_level= 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
if(!handled_level)
|
||||
{
|
||||
if(!files_in_dir.empty())
|
||||
{
|
||||
RString this_relative=
|
||||
add_extension_to_relative_path_from_found_file(relative_path, files_in_dir[0]);
|
||||
store_field("File", this_relative, false);
|
||||
}
|
||||
else
|
||||
{
|
||||
store_field("File", attr->second, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// else if(attr->first.Right(1) == "X")
|
||||
// {
|
||||
// attr->second->GetValue(x);
|
||||
// x= maybe_conv_pos(x, convert_xpos);
|
||||
// }
|
||||
// else if(attr->first.Right(1) == "Y")
|
||||
// {
|
||||
// attr->second->GetValue(y);
|
||||
// y= maybe_conv_pos(y, convert_ypos);
|
||||
// }
|
||||
else
|
||||
{
|
||||
store_field(attr->first, attr->second, true);
|
||||
}
|
||||
}
|
||||
if(type == "BitmapText")
|
||||
{
|
||||
rename_field("Texture", "Font");
|
||||
rename_field("File", "Font");
|
||||
}
|
||||
if(type == "Sprite")
|
||||
{
|
||||
rename_field("File", "Texture");
|
||||
}
|
||||
XNode const* xren= node.GetChild("children");
|
||||
if(xren != NULL)
|
||||
{
|
||||
FOREACH_CONST_Child(xren, child)
|
||||
{
|
||||
actor_template_t chill_plate;
|
||||
chill_plate.load_node(*child, dirname, conditions);
|
||||
children.push_back(chill_plate);
|
||||
}
|
||||
}
|
||||
if(!x.empty() || !y.empty())
|
||||
{
|
||||
RString pos_init= "xy," + x + "," + y;
|
||||
field_cont_t::iterator init= fields.find("InitCommand");
|
||||
if(init != fields.end())
|
||||
{
|
||||
pos_init= pos_init + ";queuecommand,xtl_passed_initCommand";
|
||||
fields["xtl_passed_initCommand"]= init->second;
|
||||
}
|
||||
fields["InitCommand"]= "cmd(" + pos_init + ")";
|
||||
}
|
||||
}
|
||||
|
||||
void actor_template_t::output_to_file(RageFile* file, RString const& indent)
|
||||
{
|
||||
if(!condition.empty())
|
||||
{
|
||||
file->Write(indent + "optional_actor(" + condition + "_result,\n");
|
||||
}
|
||||
if(type == "LoadActor")
|
||||
{
|
||||
file->Write(indent + "LoadActor('" + get_field("File") + "') .. {\n");
|
||||
}
|
||||
else
|
||||
{
|
||||
file->Write(indent + "Def." + type + "{\n");
|
||||
}
|
||||
RString subindent= indent + " ";
|
||||
if(name.empty())
|
||||
{
|
||||
name= unique_name("actor");
|
||||
}
|
||||
file->Write(subindent + "Name= \"" + name + "\",\n");
|
||||
if(!frames.empty())
|
||||
{
|
||||
file->Write(subindent + "Frames= {\n");
|
||||
RString frameindent= subindent + " ";
|
||||
for(vector<frame_t>::iterator frame= frames.begin();
|
||||
frame != frames.end(); ++frame)
|
||||
{
|
||||
file->Write(frameindent + "{Frame= " + IntToString(frame->frame) +
|
||||
", Delay= " + FloatToString(frame->delay) + "},\n");
|
||||
}
|
||||
file->Write(indent + "},\n");
|
||||
}
|
||||
for(field_cont_t::iterator field= fields.begin();
|
||||
field != fields.end(); ++field)
|
||||
{
|
||||
set<RString>::iterator is_string= fields_that_are_strings.find(field->first);
|
||||
if(is_string != fields_that_are_strings.end())
|
||||
{
|
||||
file->Write(subindent + field->first + "= \"" + field->second + "\",\n");
|
||||
}
|
||||
else
|
||||
{
|
||||
file->Write(subindent + field->first + "= " + field->second + ",\n");
|
||||
}
|
||||
}
|
||||
for(vector<actor_template_t>::iterator child= children.begin();
|
||||
child != children.end(); ++child)
|
||||
{
|
||||
child->output_to_file(file, subindent);
|
||||
file->Write(",\n");
|
||||
}
|
||||
file->Write(indent + "}");
|
||||
if(!condition.empty())
|
||||
{
|
||||
file->Write(")");
|
||||
}
|
||||
}
|
||||
|
||||
void convert_xml_file(RString const& fname, RString const& dirname)
|
||||
{
|
||||
if(arg_converters.empty())
|
||||
{
|
||||
init_parser_helpers();
|
||||
}
|
||||
LOG->Trace("Beginning conversion of entry: %s", fname.c_str());
|
||||
XNode xml;
|
||||
if(!XmlFileUtil::LoadFromFileShowErrors(xml, fname))
|
||||
{
|
||||
LOG->Trace("Error when loading xml.");
|
||||
return;
|
||||
}
|
||||
actor_template_t plate;
|
||||
condition_set_t conditions;
|
||||
plate.load_node(xml, dirname, conditions);
|
||||
RageFile* file= new RageFile;
|
||||
RString out_name= fname.Left(fname.size()-4) + ".lua";
|
||||
if(!file->Open(out_name, RageFile::WRITE))
|
||||
{
|
||||
LOG->Trace("Could not open %s: %s", out_name.c_str(), file->GetError().c_str());
|
||||
return;
|
||||
}
|
||||
LOG->Trace("Saving conversion to: %s", out_name.c_str());
|
||||
for(condition_set_t::iterator cond= conditions.begin();
|
||||
cond != conditions.end(); ++cond)
|
||||
{
|
||||
file->Write("local " + cond->second + "_result= " + cond->first + "\n\n");
|
||||
}
|
||||
if(!conditions.empty())
|
||||
{
|
||||
file->Write("local function optional_actor(cond, actor)\n"
|
||||
" if cond then return actor end\n"
|
||||
" return Def.Actor{}\n"
|
||||
"end\n\n");
|
||||
}
|
||||
file->Write("return ");
|
||||
plate.output_to_file(file, "");
|
||||
file->Write("\n");
|
||||
file->Close();
|
||||
delete file;
|
||||
}
|
||||
|
||||
int LuaFunc_convert_xml_bgs(lua_State* L);
|
||||
int LuaFunc_convert_xml_bgs(lua_State* L)
|
||||
{
|
||||
RString dir= SArg(1);
|
||||
vector<RString> xml_list;
|
||||
convert_xmls_in_dir(dir + "/");
|
||||
return 0;
|
||||
}
|
||||
|
||||
LUAFUNC_REGISTER_COMMON(convert_xml_bgs);
|
||||
|
||||
/*
|
||||
* (c) 2014 Eric Reese
|
||||
* All rights reserved.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||
* copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, and/or sell copies of the Software, and to permit persons to
|
||||
* whom the Software is furnished to do so, provided that the above
|
||||
* copyright notice(s) and this permission notice appear in all copies of
|
||||
* the Software and that both the above copyright notice(s) and this
|
||||
* permission notice appear in supporting documentation.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF
|
||||
* THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS
|
||||
* INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT
|
||||
* OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
|
||||
* OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
||||
* OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||
* PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
Reference in New Issue
Block a user