Add kills and level stats to Stats tab.

This commit is contained in:
Mari the Deer 2021-12-14 22:45:29 +01:00
commit 00790e9cd2
6 changed files with 269 additions and 32 deletions

View file

@ -1,3 +1,3 @@
[default]
SWWM_MODVER="\chSWWM \czGZ\c- \cw1.2pre r79 \cu(Tue 14 Dec 22:44:47 CET 2021)\c-";
SWWM_SHORTVER="\cw1.2pre r79 \cu(2021-12-14 22:44:47)\c-";
SWWM_MODVER="\chSWWM \czGZ\c- \cw1.2pre r80 \cu(Tue 14 Dec 22:45:29 CET 2021)\c-";
SWWM_SHORTVER="\cw1.2pre r80 \cu(2021-12-14 22:45:29)\c-";

View file

@ -6,6 +6,16 @@ Class DemolitionistMenuList ui
Array<DemolitionistMenuListItem> items;
int selected;
override void OnDestroy()
{
for ( int i=0; i<items.Size(); i++ )
{
// just in case do a null pointer check
if ( !items[i] ) continue;
items[i].Destroy();
}
}
// get the highest width among all items
int GetWidth()
{
@ -56,6 +66,7 @@ Class DemolitionistMenuListItem ui
{
self.master = master;
label = (txt.Left(1)~=="$")?StringTable.Localize(txt):txt;
xpos = ypos = 0;
return self;
}
@ -87,7 +98,6 @@ Class DemolitionistMenuListItem ui
virtual void Drawer( Vector2 pos, bool selected, int cliptop, int clipbottom, int clipleft, int clipright )
{
// TODO skip draw if out of bounds (is it even needed, I'd assume gzdoom itself would optimize that)
Screen.DrawText(master.LargeFont,selected?Font.CR_FIRE:Font.CR_WHITE,master.origin.x+pos.x,master.origin.y+pos.y,label,DTA_VirtualWidthF,master.ss.x,DTA_VirtualHeightF,master.ss.y,DTA_KeepRatio,true,DTA_ClipTop,cliptop,DTA_ClipBottom,clipbottom,DTA_ClipLeft,clipleft,DTA_ClipRight,clipright);
}
}
@ -98,13 +108,27 @@ Class DemolitionistMenuKillItem : DemolitionistMenuListItem
MonsterKill s;
int width;
DemolitionistMenuKillItem Init( DemolitionistMenu master, MonsterKill s, int width )
DemolitionistMenuKillItem Init( DemolitionistMenu master, MonsterKill s, int width = 0 )
{
Super.Init(master,"");
self.s = s;
self.width = width;
let m = GetDefaultByType(s.m);
self.label = m.GetTag(FallbackTag);
if ( self.label == FallbackTag )
{
self.label = m.GetClassName();
SWWMUtility.BeautifyClassName(self.label);
}
return self;
}
override void Drawer( Vector2 pos, bool selected, int cliptop, int clipbottom, int clipleft, int clipright )
{
Screen.DrawText(master.LargeFont,Font.CR_FIRE,master.origin.x+pos.x,master.origin.y+pos.y,label,DTA_VirtualWidthF,master.ss.x,DTA_VirtualHeightF,master.ss.y,DTA_KeepRatio,true,DTA_ClipTop,cliptop,DTA_ClipBottom,clipbottom,DTA_ClipLeft,clipleft,DTA_ClipRight,clipright);
String str = String.Format("%d",s.kills);
Screen.DrawText(master.LargeFont,Font.CR_WHITE,master.origin.x+pos.x+width-master.LargeFont.StringWidth(str),master.origin.y+pos.y,str,DTA_VirtualWidthF,master.ss.x,DTA_VirtualHeightF,master.ss.y,DTA_KeepRatio,true,DTA_ClipTop,cliptop,DTA_ClipBottom,clipbottom,DTA_ClipLeft,clipleft,DTA_ClipRight,clipright);
}
}
// map stat item
@ -112,14 +136,42 @@ Class DemolitionistMenuMapStatItem : DemolitionistMenuListItem
{
LevelStat s;
int width;
int maxlen[4];
DemolitionistMenuMapStatItem Init( DemolitionistMenu master, LevelStat s, int width )
DemolitionistMenuMapStatItem Init( DemolitionistMenu master, LevelStat s, int width = 0 )
{
Super.Init(master,"");
self.s = s;
self.width = width;
self.label = s.hub?s.levelname:String.Format("%s - %s",s.mapname.MakeUpper(),s.levelname);
return self;
}
override void Drawer( Vector2 pos, bool selected, int cliptop, int clipbottom, int clipleft, int clipright )
{
String str = label;
if ( selected ) str = "\cd▸\c- "..str;
bool smallname = master.LargeFont.StringWidth(str)>(width-(maxlen[3]+maxlen[2]+maxlen[1]+maxlen[0]+24));
Screen.DrawText(smallname?master.SmallFont:master.LargeFont,Font.CR_FIRE,master.origin.x+pos.x,master.origin.y+pos.y+smallname*2,str,DTA_VirtualWidthF,master.ss.x,DTA_VirtualHeightF,master.ss.y,DTA_KeepRatio,true,DTA_ClipTop,cliptop,DTA_ClipBottom,clipbottom,DTA_ClipLeft,clipleft,DTA_ClipRight,clipright);
double xx = pos.x+width;
double yy = pos.y+2;
int sec = Thinker.Tics2Seconds(s.time);
str = String.Format("%02d\cu:\c-%02d\cu:\c-%02d",sec/3600,(sec%3600)/60,sec%60);
Screen.DrawText(master.SmallFont,((s.suck>0)&&(sec>=(s.suck*3600)))?Font.CR_RED:(sec<=s.par)?Font.CR_GOLD:Font.CR_WHITE,master.origin.x+xx-master.SmallFont.StringWidth(str),master.origin.y+yy,str,DTA_VirtualWidthF,master.ss.x,DTA_VirtualHeightF,master.ss.y,DTA_KeepRatio,true,DTA_ClipTop,cliptop,DTA_ClipBottom,clipbottom,DTA_ClipLeft,clipleft,DTA_ClipRight,clipright);
Screen.DrawText(master.SmallFont,Font.CR_FIRE,master.origin.x+xx-maxlen[0],master.origin.y+yy,"T",DTA_VirtualWidthF,master.ss.x,DTA_VirtualHeightF,master.ss.y,DTA_KeepRatio,true,DTA_ClipTop,cliptop,DTA_ClipBottom,clipbottom,DTA_ClipLeft,clipleft,DTA_ClipRight,clipright);
xx -= maxlen[0]+8;
str = String.Format("%d\cu/\c-%d",s.scount,s.stotal);
Screen.DrawText(master.SmallFont,(s.scount>=s.stotal)?Font.CR_GOLD:Font.CR_WHITE,master.origin.x+xx-master.SmallFont.StringWidth(str),master.origin.y+yy,str,DTA_VirtualWidthF,master.ss.x,DTA_VirtualHeightF,master.ss.y,DTA_KeepRatio,true,DTA_ClipTop,cliptop,DTA_ClipBottom,clipbottom,DTA_ClipLeft,clipleft,DTA_ClipRight,clipright);
Screen.DrawText(master.SmallFont,Font.CR_FIRE,master.origin.x+xx-maxlen[1],master.origin.y+yy,"S",DTA_VirtualWidthF,master.ss.x,DTA_VirtualHeightF,master.ss.y,DTA_KeepRatio,true,DTA_ClipTop,cliptop,DTA_ClipBottom,clipbottom,DTA_ClipLeft,clipleft,DTA_ClipRight,clipright);
xx -= maxlen[1]+8;
str = String.Format("%d\cu/\c-%d",s.icount,s.itotal);
Screen.DrawText(master.SmallFont,(s.icount>=s.itotal)?Font.CR_GOLD:Font.CR_WHITE,master.origin.x+xx-master.SmallFont.StringWidth(str),master.origin.y+yy,str,DTA_VirtualWidthF,master.ss.x,DTA_VirtualHeightF,master.ss.y,DTA_KeepRatio,true,DTA_ClipTop,cliptop,DTA_ClipBottom,clipbottom,DTA_ClipLeft,clipleft,DTA_ClipRight,clipright);
Screen.DrawText(master.SmallFont,Font.CR_FIRE,master.origin.x+xx-maxlen[2],master.origin.y+yy,"I",DTA_VirtualWidthF,master.ss.x,DTA_VirtualHeightF,master.ss.y,DTA_KeepRatio,true,DTA_ClipTop,cliptop,DTA_ClipBottom,clipbottom,DTA_ClipLeft,clipleft,DTA_ClipRight,clipright);
xx -= maxlen[2]+8;
str = String.Format("%d\cu/\c-%d",s.kcount,s.ktotal);
Screen.DrawText(master.SmallFont,(s.kcount>=s.ktotal)?Font.CR_GOLD:Font.CR_WHITE,master.origin.x+xx-master.SmallFont.StringWidth(str),master.origin.y+yy,str,DTA_VirtualWidthF,master.ss.x,DTA_VirtualHeightF,master.ss.y,DTA_KeepRatio,true,DTA_ClipTop,cliptop,DTA_ClipBottom,clipbottom,DTA_ClipLeft,clipleft,DTA_ClipRight,clipright);
Screen.DrawText(master.SmallFont,Font.CR_FIRE,master.origin.x+xx-maxlen[3],master.origin.y+yy,"K",DTA_VirtualWidthF,master.ss.x,DTA_VirtualHeightF,master.ss.y,DTA_KeepRatio,true,DTA_ClipTop,cliptop,DTA_ClipBottom,clipbottom,DTA_ClipLeft,clipleft,DTA_ClipRight,clipright);
}
}
// achievement item
@ -128,7 +180,7 @@ Class DemolitionistMenuAchievementItem : DemolitionistMenuListItem
SWWMAchievementInfo a;
int width;
DemolitionistMenuAchievementItem Init( DemolitionistMenu master, SWWMAchievementInfo a, int width )
DemolitionistMenuAchievementItem Init( DemolitionistMenu master, SWWMAchievementInfo a, int width = 0 )
{
Super.Init(master,"");
self.a = a;

View file

@ -33,6 +33,10 @@ Class DemolitionistMenuTextBox ui
}
return self;
}
override void OnDestroy()
{
l.Destroy();
}
// called when sending a scroll input
// returns true if the position actually changed

View file

@ -16,6 +16,10 @@ Class DemolitionistHelpTab : DemolitionistMenuTab
mtext = new("DemolitionistMenuTextBox").Init(master,str);
return Super.Init(master);
}
override void OnDestroy()
{
mtext.Destroy();
}
override void MenuInput( int key )
{
switch ( key )

View file

@ -128,6 +128,11 @@ Class DemolitionistMissionTab : DemolitionistMenuTab
}
return Super.Init(master);
}
override void OnDestroy()
{
for ( int i=0; i<mtext.Size(); i++ )
mtext[i].Destroy();
}
override void MenuInput( int key )
{

View file

@ -7,9 +7,10 @@ Class DemolitionistStatsTab : DemolitionistMenuTab
String sname[4];
int lwidth;
SWWMStats stats;
int ofs, maxofs;
double smofs;
int ofs[4], maxofs[4];
double smofs[4];
bool drag;
LevelStat clstat;
override DemolitionistMenuTab Init( DemolitionistMenu master )
{
@ -24,40 +25,135 @@ Class DemolitionistStatsTab : DemolitionistMenuTab
lists[i].selected = -1;
}
lwidth += 16; // account for padding of 8px on each side
stats = Demolitionist(players[consoleplayer].mo)?Demolitionist(players[consoleplayer].mo).mystats:null;
if ( !stats ) return Super.Init(master);
// because these types of stats don't actually change while the menu is open, we can initialize their lists ONLY ONCE here
if ( swwm_uniqstats )
{
for ( int i=0; i<stats.lstats.Size(); i++ )
{
if ( stats.lstats[i].mapname ~== level.mapname ) continue;
for ( int j=0; j<lists[2].items.Size(); j++ )
{
if ( !(DemolitionistMenuMapStatItem(lists[2].items[j]).s.mapname ~== stats.lstats[i].mapname) ) continue;
lists[2].items[j].Destroy();
lists[2].items.Delete(j);
j--;
}
lists[2].items.Push(new("DemolitionistMenuMapStatItem").Init(master,stats.lstats[i]));
}
}
else
{
lists[2].items.Resize(stats.lstats.Size());
for ( int i=0; i<stats.lstats.Size(); i++ )
lists[2].items[i] = new("DemolitionistMenuMapStatItem").Init(master,stats.lstats[i]);
}
clstat = new("LevelStat");
clstat.hub = !!(level.clusterflags&level.CLUSTER_HUB);
clstat.mapname = level.mapname;
clstat.levelname = level.levelname;
int iof = clstat.levelname.IndexOf(" - by: ");
if ( iof != -1 ) clstat.levelname.Truncate(iof);
clstat.par = level.partime;
clstat.suck = level.sucktime;
lists[2].items.Push(new("DemolitionistMenuMapStatItem").Init(master,clstat));
lists[2].selected = lists[2].items.Size()-1; // so it shows the "current level" indicator
for ( int i=0; i<lists[2].items.Size(); i++ )
lists[2].items[i].ypos = i*16;
return Super.Init(master);
}
override void OnDestroy()
{
for ( int i=0; i<4; i++ )
lists[i].Destroy();
}
override void OnSelect()
{
section = master.shnd.menustate.At("LastStatSection").ToInt();
smofs[section] = ofs[section];
}
override void OnDeselect()
{
master.shnd.menustate.Insert("LastStatSection",String.Format("%d",section));
smofs[section] = ofs[section];
}
private bool CmpMonsterKill( MonsterKill a, MonsterKill b )
{
if ( a.kills == b.kills )
{
// sort by name instead
String taga, tagb;
taga = GetDefaultByType(a.m).GetTag();
tagb = GetDefaultByType(b.m).GetTag();
// beautify if there's no tag
if ( taga == FallbackTag )
{
taga = a.m.GetClassName();
SWWMUtility.BeautifyClassName(taga);
}
if ( tagb == FallbackTag )
{
tagb = a.m.GetClassName();
SWWMUtility.BeautifyClassName(tagb);
}
return taga > tagb;
}
return a.kills < b.kills;
}
private int partition_mstats( Array<DemolitionistMenuListItem> a, int l, int h )
{
DemolitionistMenuListItem pv = a[h];
int i = (l-1);
for ( int j=l; j<=(h-1); j++ )
{
if ( CmpMonsterKill(DemolitionistMenuKillItem(pv).s,DemolitionistMenuKillItem(a[j]).s) )
{
i++;
DemolitionistMenuListItem tmp = a[j];
a[j] = a[i];
a[i] = tmp;
}
}
DemolitionistMenuListItem tmp = a[h];
a[h] = a[i+1];
a[i+1] = tmp;
return i+1;
}
private void qsort_mstats( Array<DemolitionistMenuListItem> a, int l, int h )
{
if ( l >= h ) return;
int p = partition_mstats(a,l,h);
qsort_mstats(a,l,p-1);
qsort_mstats(a,p+1,h);
}
override void Ticker()
{
if ( !stats ) return;
// update list contents
String str;
int w;
switch ( section )
{
case 0:
if ( !stats ) stats = Demolitionist(players[consoleplayer].mo)?Demolitionist(players[consoleplayer].mo).mystats:null;
if ( lists[0].items.Size() == 0 ) // allocate first
{
for ( int i=0; i<31; i++ )
{
let li = new("DemolitionistMenuListItem").Init(master,"");
li.xpos = 0;
li.ypos = i*16;
lists[0].items.Push(li);
}
}
maxofs = int(16*lists[0].items.Size()-(master.ws.y-46));
if ( !stats ) break;
maxofs[0] = int(16*lists[0].items.Size()-(master.ws.y-46));
// oof
int thour = ((level.totaltime-stats.lastspawn)/(3600*GameTicRate));
int tmin = ((level.totaltime-stats.lastspawn)/(60*GameTicRate))%60;
int tsec = ((level.totaltime-stats.lastspawn)/GameTicRate)%60;
String str = String.Format("\cx%s\c-%02d\cu:\c-%02d\cu:\c-%02d",StringTable.Localize("$SWWM_STATUPTIME"),thour,tmin,tsec);
str = String.Format("\cx%s\c-%02d\cu:\c-%02d\cu:\c-%02d",StringTable.Localize("$SWWM_STATUPTIME"),thour,tmin,tsec);
lists[0].items[0].label = str;
if ( stats.grounddist > 32000. ) str = String.Format("\cx%s\c-%g\cu%s\c-",StringTable.Localize("$SWWM_STATONFOOT"),stats.grounddist/32000.,StringTable.Localize("$SWWM_UNIT_KILOMETER"));
else str = String.Format("\cx%s\c-%g\cu%s\c-",StringTable.Localize("$SWWM_STATONFOOT"),stats.grounddist/32.,StringTable.Localize("$SWWM_UNIT_METER"));
@ -141,10 +237,78 @@ Class DemolitionistStatsTab : DemolitionistMenuTab
else str = String.Format("\cx%s\cu¥\c-%09d",StringTable.Localize("$SWWM_STATHISCORE"),stats.hiscore);
lists[0].items[30].label = str;
break;
case 1:
// theoretically we can assume that the monster stats list will never shrink in the middle of gameplay, only grow
// also the array only ever has elements pushed, never inserted in-between
// if this somehow breaks, I don't know what the hell can do that
if ( lists[1].items.Size() < stats.mstats.Size() )
{
int oldindex = lists[1].items.Size();
lists[1].items.Resize(stats.mstats.Size());
for ( int i=oldindex; i<lists[1].items.Size(); i++ )
lists[1].items[i] = new("DemolitionistMenuKillItem").Init(master,stats.mstats[i]);
}
maxofs[1] = int(16*lists[1].items.Size()-(master.ws.y-46));
// update widths
w = int(master.ws.x-(18+lwidth));
if ( maxofs[1] > 0 ) w -= 8;
for ( int i=0; i<lists[1].items.Size(); i++ )
{
let k = DemolitionistMenuKillItem(lists[1].items[i]);
k.width = w;
}
// sort
qsort_mstats(lists[1].items,0,lists[1].items.Size()-1);
// update y positions after sorting
for ( int i=0; i<lists[1].items.Size(); i++ )
lists[1].items[i].ypos = i*16;
break;
case 2:
maxofs[2] = int(16*lists[2].items.Size()-(master.ws.y-46));
// update current stats
clstat.kcount = level.killed_monsters;
clstat.ktotal = level.total_monsters;
clstat.icount = level.found_items;
clstat.itotal = level.total_items;
clstat.scount = level.found_secrets;
clstat.stotal = level.total_secrets;
clstat.time = level.maptime;
// update widths
w = int(master.ws.x-(18+lwidth));
if ( maxofs[2] > 0 ) w -= 8;
int len[4], maxlen[4];
for ( int i=0; i<4; i++ ) maxlen[i] = 0;
for ( int i=0; i<lists[2].items.Size(); i++ )
{
let l = DemolitionistMenuMapStatItem(lists[2].items[i]);
l.width = w;
// and calculate "max lengths"
int sec = Thinker.Tics2Seconds(l.s.time);
str = String.Format("T %02d:%02d:%02d",sec/3600,(sec%3600)/60,sec%60);
len[0] = master.SmallFont.StringWidth(str);
if ( len[0] > maxlen[0] ) maxlen[0] = len[0];
str = String.Format("S %d/%d",l.s.scount,l.s.stotal);
len[1] = master.SmallFont.StringWidth(str);
if ( len[1] > maxlen[1] ) maxlen[1] = len[1];
str = String.Format("I %d/%d",l.s.icount,l.s.itotal);
len[2] = master.SmallFont.StringWidth(str);
if ( len[2] > maxlen[2] ) maxlen[2] = len[2];
str = String.Format("K %d/%d",l.s.kcount,l.s.ktotal);
len[3] = master.SmallFont.StringWidth(str);
if ( len[3] > maxlen[3] ) maxlen[3] = len[3];
}
// second pass to propagate the "max lengths"
for ( int i=0; i<lists[2].items.Size(); i++ )
{
let l = DemolitionistMenuMapStatItem(lists[2].items[i]);
for ( int j=0; j<4; j++ )
l.maxlen[j] = maxlen[j];
}
break;
}
// update smooth scroll
smofs = (smofs*.6)+(ofs*.4);
if ( abs(smofs-ofs) < (1./master.hs) ) smofs = ofs;
smofs[section] = (smofs[section]*.6)+(ofs[section]*.4);
if ( abs(smofs[section]-ofs[section]) < (1./master.hs) ) smofs[section] = ofs[section];
// tick the current list
lists[section].Ticker();
}
@ -153,20 +317,20 @@ Class DemolitionistStatsTab : DemolitionistMenuTab
// speed: how many pixels to move (either back or forward)
bool Scroll( int speed )
{
if ( maxofs <= 0 ) return false;
int oldofs = ofs;
ofs = clamp(ofs+speed,0,maxofs);
return (ofs != oldofs);
if ( maxofs[section] <= 0 ) return false;
int oldofs = ofs[section];
ofs[section] = clamp(ofs[section]+speed,0,maxofs[section]);
return (ofs[section] != oldofs);
}
// called when clicking on our scrollbar
// returns true if the position actually changed
// y: relative click position
bool SetOffset( double y )
{
if ( maxofs <= 0 ) return false;
int oldofs = ofs;
ofs = clamp(int(round((y-20.5)/((master.ws.y-41.)/maxofs))),0,maxofs);
return (ofs != oldofs);
if ( maxofs[section] <= 0 ) return false;
int oldofs = ofs[section];
ofs[section] = clamp(int(round((y-20.5)/((master.ws.y-41.)/maxofs[section]))),0,maxofs[section]);
return (ofs[section] != oldofs);
}
override void MenuInput( int key )
{
@ -174,15 +338,17 @@ Class DemolitionistStatsTab : DemolitionistMenuTab
{
case MK_LEFT:
master.MenuSound("menu/demoscroll");
smofs[section] = ofs[section];
section--;
if ( section < 0 ) section = 3;
smofs = ofs = 0;
smofs[section] = ofs[section];
break;
case MK_RIGHT:
master.MenuSound("menu/demoscroll");
smofs[section] = ofs[section];
section++;
if ( section > 3 ) section = 0;
smofs = ofs = 0;
smofs[section] = ofs[section];
break;
case MK_DOWN:
if ( Scroll(16) ) master.MenuSound("menu/demoscroll");
@ -219,8 +385,9 @@ Class DemolitionistStatsTab : DemolitionistMenuTab
if ( section != i )
{
master.MenuSound("menu/demoscroll");
smofs[section] = ofs[section];
section = i;
smofs = ofs = 0;
smofs[section] = ofs[section];
}
break;
}
@ -232,12 +399,13 @@ Class DemolitionistStatsTab : DemolitionistMenuTab
MenuInput(MK_RIGHT);
break;
}
return;
}
switch ( btn )
{
case MB_LEFT:
// see if we're clicking the scrollbar (if it exists)
if ( (maxofs > 0) && (pos.x > (master.ws.x-8)) )
if ( (maxofs[section] > 0) && (pos.x > (master.ws.x-8)) )
{
SetOffset(pos.y);
master.MenuSound("menu/demoscroll");
@ -254,6 +422,7 @@ Class DemolitionistStatsTab : DemolitionistMenuTab
}
override void Drawer()
{
if ( !stats ) return;
double xx = 9;
double yy = 23;
for ( int i=0; i<4; i++ )
@ -262,7 +431,11 @@ Class DemolitionistStatsTab : DemolitionistMenuTab
yy += 16;
}
master.DrawVSeparator(lwidth,14,master.ws.y-28);
if ( section == 0 )
if ( section == 3 )
{
// TODO achievement drawer has different margins
}
else
{
xx = lwidth+9;
yy = 23;
@ -270,16 +443,15 @@ Class DemolitionistStatsTab : DemolitionistMenuTab
int clipbottom = int((master.origin.y+master.ws.y-23)*master.hs);
int clipleft = int((master.origin.x+lwidth+9)*master.hs);
int clipright = int((master.origin.x+master.ws.x-9)*master.hs);
lists[0].Drawer((xx,yy-smofs),cliptop,clipbottom,clipleft,clipright);
if ( maxofs > 0 )
lists[section].Drawer((xx,yy-smofs[section]),cliptop,clipbottom,clipleft,clipright);
if ( maxofs[section] > 0 )
{
xx = master.ws.x-8;
master.DrawVSeparator(xx,14,master.ws.y-28);
xx += 2;
yy = floor(smofs*((master.ws.y-39)/maxofs))+13;
yy = floor(smofs[section]*((master.ws.y-39)/maxofs[section]))+13;
Screen.DrawText(master.TewiFont,Font.CR_FIRE,master.origin.x+xx,master.origin.y+yy,"▮",DTA_VirtualWidthF,master.ss.x,DTA_VirtualHeightF,master.ss.y,DTA_KeepRatio,true);
}
}
}
}