Replaced all insertion sorts with quicksort. The performance gain is ludicrous.

This commit is contained in:
Mari the Deer 2020-04-29 02:11:08 +02:00
commit b6d3d69f48
7 changed files with 228 additions and 141 deletions

View file

@ -43,5 +43,5 @@ server bool swwm_upgradebosses = true; // vanilla bosses will be "upgraded" to
server bool swwm_extraalert = false; // enables A_AlertMonsters calls for certain things which may be cpu-heavy
user bool swwm_accdamage = false; // damage within the same tic is grouped into a single damage number
user int swwm_maxtargets = 40; // maximum targeter healthbars to display (0 = unlimited)
user int swwm_maxscorenums = 20; // maximum score numbers to display (0 = unlimited)
user int swwm_maxdamnums = 40; // maximum damage/healing numbers to display (0 = unlimited)
user int swwm_maxscorenums = 0; // maximum score numbers to display (0 = unlimited)
user int swwm_maxdamnums = 0; // maximum damage/healing numbers to display (0 = unlimited)

View file

@ -120,9 +120,9 @@ TOOLTIP_SWWM_BOSSHEALTHBARS = "Show a healthbar for vanilla boss encounters at t
TOOLTIP_SWWM_UPGRADEBOSSES = "Buffs the health of vanilla bosses in order to make the fights more \"fair\" with this mod, and less prone to instant wins.";
TOOLTIP_SWWM_EXTRAALERT = "Allows monsters to hear things such as bouncing projectiles, ricocheting bullets, and more. Due to the recursive nature of the A_AlertMonsters function, this has a MASSIVE performance hit on very complex maps.";
TOOLTIP_SWWM_ACCDAMAGE = "Damage dealt to a target within the same tic will be grouped into a single damage number, which may make the screen less cluttered in most cases.";
TOOLTIP_SWWM_MAXTARGETS = "Limit the amount of healthbars to display on-screen. Helps keep the screen less cluttered, and may even improve performance.";
TOOLTIP_SWWM_MAXDAMNUMS = "Limit the amount of damage/healing numbers to display on-screen. Helps keep the screen less cluttered, and may even improve performance.";
TOOLTIP_SWWM_MAXSCORENUMS = "Limit the amount of score numbers to display on-screen. Helps keep the screen less cluttered, and may even improve performance.";
TOOLTIP_SWWM_MAXTARGETS = "Limit the amount of healthbars to display on-screen. Helps keep the screen less cluttered.";
TOOLTIP_SWWM_MAXDAMNUMS = "Limit the amount of damage/healing numbers to display on-screen. Helps keep the screen less cluttered.";
TOOLTIP_SWWM_MAXSCORENUMS = "Limit the amount of score numbers to display on-screen. Helps keep the screen less cluttered.";
// knowledge base
SWWM_COMINGSOON = "(coming soon)";
SWWM_MISSTAB = "Mission";

View file

@ -117,9 +117,9 @@ TOOLTIP_SWWM_BOSSHEALTHBARS = "Muestra una barra de vida para bosses vanilla en
TOOLTIP_SWWM_UPGRADEBOSSES = "Incrementa la salud de bosses vanilla para hacer que las peleas sean más \"justas\" con este mod, y no tan propensas a ganarse al instante.";
TOOLTIP_SWWM_EXTRAALERT = "Permite a los enemigos oir cosas como proyectiles y balas rebotando, entre otros. Debido a la naturaleza recursiva de la función A_AlertMonsters, esto puede tener un impacto MASIVO en el rendimiento en mapas muy complejos.";
TOOLTIP_SWWM_ACCDAMAGE = "El daño hecho a un objetivo en el mismo tic será agrupado en un único número, lo cual resultará en una pantalla más despejada en la mayoría de casos.";
TOOLTIP_SWWM_MAXTARGETS = "Limita la cantidad de barras de salud a mostrar en pantalla. Ayuda a mantener la pantalla más despejada, y puede incluso mejorar el rendimiento.";
TOOLTIP_SWWM_MAXDAMNUMS = "Limita la cantidad de números de daño/curación a mostrar en pantalla. Ayuda a mantener la pantalla más despejada, y puede incluso mejorar el rendimiento.";
TOOLTIP_SWWM_MAXSCORENUMS = "Limita la cantidad de números de puntuación a mostrar en pantalla. Ayuda a mantener la pantalla más despejada, y puede incluso mejorar el rendimiento.";
TOOLTIP_SWWM_MAXTARGETS = "Limita la cantidad de barras de salud a mostrar en pantalla. Ayuda a mantener la pantalla más despejada.";
TOOLTIP_SWWM_MAXDAMNUMS = "Limita la cantidad de números de daño/curación a mostrar en pantalla. Ayuda a mantener la pantalla más despejada.";
TOOLTIP_SWWM_MAXSCORENUMS = "Limita la cantidad de números de puntuación a mostrar en pantalla. Ayuda a mantener la pantalla más despejada.";
// knowledge base
SWWM_COMINGSOON = "(próximamente)";
SWWM_MISSTAB = "Misión";

View file

@ -46,9 +46,9 @@ OptionMenu "SWWMOptionMenu"
Option "$SWWM_ACCDAMAGE", "swwm_accdamage", "YesNo"
Option "$SWWM_SCORENUMS", "swwm_scorenums", "YesNo"
Option "$SWWM_SCOREBONUS", "swwm_scorebonus", "YesNo"
ScaleSlider "$SWWM_MAXTARGETS", "swwm_maxtargets", 0, 100, 1, "$SWWM_UNLIMITED"
ScaleSlider "$SWWM_MAXDAMNUMS", "swwm_maxdamnums", 0, 100, 1, "$SWWM_UNLIMITED"
ScaleSlider "$SWWM_MAXSCORENUMS", "swwm_maxscorenums", 0, 100, 1, "$SWWM_UNLIMITED"
ScaleSlider "$SWWM_MAXTARGETS", "swwm_maxtargets", 0, 1000, 1, "$SWWM_UNLIMITED"
ScaleSlider "$SWWM_MAXDAMNUMS", "swwm_maxdamnums", 0, 1000, 1, "$SWWM_UNLIMITED"
ScaleSlider "$SWWM_MAXSCORENUMS", "swwm_maxscorenums", 0, 1000, 1, "$SWWM_UNLIMITED"
Option "$SWWM_SHADERS", "swwm_shaders", "YesNo"
Option "$SWWM_INTERTYPE", "swwm_intertype", "SWWMInterType"
StaticText " "

View file

@ -21,13 +21,6 @@ Mixin Class SWWMAmmo
return copy;
}
private bool CmpAmmo( Class<Ammo> a, Class<Ammo> b )
{
let amta = GetDefaultByType(a).Amount;
let amtb = GetDefaultByType(b).Amount;
return (amta < amtb);
}
override bool SpecialDropAction( Actor dropper )
{
if ( Amount != default.Amount )
@ -40,6 +33,40 @@ Mixin Class SWWMAmmo
return false;
}
private bool CmpAmmo( Class<Ammo> a, Class<Ammo> b )
{
let amta = GetDefaultByType(a).Amount;
let amtb = GetDefaultByType(b).Amount;
return (amta < amtb);
}
private int partition_ammotypes( Array<Class<Ammo> > a, int l, int h )
{
Class<Ammo> pv = a[h];
int i = (l-1);
for ( int j=l; j<=(h-1); j++ )
{
if ( CmpAmmo(pv,a[j]) )
{
i++;
Class<Ammo> tmp = a[j];
a[j] = a[i];
a[i] = tmp;
}
}
Class<Ammo> tmp = a[h];
a[h] = a[i+1];
a[i+1] = tmp;
return i+1;
}
private void qsort_ammotypes( Array<Class<Ammo> > a, int l, int h )
{
if ( l >= h ) return;
int p = partition_ammotypes(a,l,h);
qsort_ammotypes(a,l,p-1);
qsort_ammotypes(a,p+1,h);
}
override inventory CreateTossable( int amt )
{
if ( bUndroppable || bUntossable || !Owner || (Amount <= 0) || (amt == 0) )
@ -55,22 +82,7 @@ Mixin Class SWWMAmmo
ammotypes.Push((Class<Ammo>)(AllActorClasses[i]));
}
// sort from largest to smallest
for ( int i=0; i<ammotypes.Size(); i++ )
{
int j = 1;
while ( j < ammotypes.Size() )
{
int k = j;
while ( (k > 0) && CmpAmmo(ammotypes[k-1],ammotypes[k]) )
{
Class<Ammo> tmp = ammotypes[k];
ammotypes[k] = ammotypes[k-1];
ammotypes[k-1] = tmp;
k--;
}
j++;
}
}
qsort_ammotypes(ammotypes,0,ammotypes.Size()-1);
// perform subdivision
Inventory last = null;
while ( amt > 0 )

View file

@ -191,6 +191,90 @@ Class SWWMStatusBar : BaseStatusBar
return (dista < distb);
}
// quicksort (points of interest)
private int partition_intpoints( Array<SWWMInterest> a, int l, int h )
{
SWWMInterest pv = a[h];
int i = (l-1);
for ( int j=l; j<=(h-1); j++ )
{
if ( CmpInterest(pv,a[j]) || CmpDist(pv.pos,a[j].pos) )
{
i++;
SWWMInterest tmp = a[j];
a[j] = a[i];
a[i] = tmp;
}
}
SWWMInterest tmp = a[h];
a[h] = a[i+1];
a[i+1] = tmp;
return i+1;
}
private void qsort_intpoints( Array<SWWMInterest> a, int l, int h )
{
if ( l >= h ) return;
int p = partition_intpoints(a,l,h);
qsort_intpoints(a,l,p-1);
qsort_intpoints(a,p+1,h);
}
// quicksort (combat trackers)
private int partition_trackers( Array<SWWMCombatTracker> a, int l, int h )
{
SWWMCombatTracker pv = a[h];
int i = (l-1);
for ( int j=l; j<=(h-1); j++ )
{
if ( CmpTarget(pv,a[j]) || CmpDist(pv.pos,a[j].pos) )
{
i++;
SWWMCombatTracker tmp = a[j];
a[j] = a[i];
a[i] = tmp;
}
}
SWWMCombatTracker tmp = a[h];
a[h] = a[i+1];
a[i+1] = tmp;
return i+1;
}
private void qsort_trackers( Array<SWWMCombatTracker> a, int l, int h )
{
if ( l >= h ) return;
int p = partition_trackers(a,l,h);
qsort_trackers(a,l,p-1);
qsort_trackers(a,p+1,h);
}
// quicksort (score objects)
private int partition_scoreobjs( Array<SWWMScoreObj> a, int l, int h )
{
SWWMScoreObj pv = a[h];
int i = (l-1);
for ( int j=l; j<=(h-1); j++ )
{
if ( CmpScore(pv,a[j]) || CmpDist(pv.pos,a[j].pos) )
{
i++;
SWWMScoreObj tmp = a[j];
a[j] = a[i];
a[i] = tmp;
}
}
SWWMScoreObj tmp = a[h];
a[h] = a[i+1];
a[i+1] = tmp;
return i+1;
}
private void qsort_scoreobjs( Array<SWWMScoreObj> a, int l, int h )
{
if ( l >= h ) return;
int p = partition_scoreobjs(a,l,h);
qsort_scoreobjs(a,l,p-1);
qsort_scoreobjs(a,p+1,h);
}
override void Tick()
{
Super.Tick();
@ -251,30 +335,19 @@ Class SWWMStatusBar : BaseStatusBar
int i = 0;
for ( SWWMInterest poi=hnd.intpoints; poi; poi=poi.next )
{
if ( viewvec dot level.Vec3Diff(viewpos,poi.pos) < 0 ) continue;
// ignore points clearly outside of player view
Vector3 tdir = level.Vec3Diff(viewpos,poi.pos);
if ( viewvec dot tdir < 0 ) continue;
proj.ProjectWorldPos(viewpos+tdir);
proj.ProjectToNormal();
if ( !proj.IsInFront() || !proj.IsInScreen() ) continue;
intpoints[i++] = poi;
}
// squeeze if some were discarded
if ( i != hnd.intpoints_cnt )
intpoints.Resize(i);
// sort by distance
sz = intpoints.Size();
for ( int i=0; i<sz; i++ )
{
int j = 1;
while ( j < sz )
{
int k = j;
while ( (k > 0) && (CmpInterest(intpoints[k-1],intpoints[k]) || CmpDist(intpoints[k-1].pos,intpoints[k].pos)) )
{
SWWMInterest tmp = intpoints[k];
intpoints[k] = intpoints[k-1];
intpoints[k-1] = tmp;
k--;
}
j++;
}
}
qsort_intpoints(intpoints,0,intpoints.Size()-1);
}
if ( targetter.GetBool() )
{
@ -282,7 +355,7 @@ Class SWWMStatusBar : BaseStatusBar
if ( trackers.Size() != hnd.trackers_cnt )
trackers.Resize(hnd.trackers_cnt);
int i = 0;
for ( SWWMCombatTracker trk=hnd.trackers; trk && (!maxtargetnum||(i<maxtargetnum)); trk=trk.next )
for ( SWWMCombatTracker trk=hnd.trackers; trk; trk=trk.next )
{
// ignore player unless chasecamming
if ( (trk.mytarget == players[consoleplayer].mo) && (players[consoleplayer].Camera == players[consoleplayer].mo) && !(players[consoleplayer].cheats&CF_CHASECAM) ) continue;
@ -291,29 +364,26 @@ Class SWWMStatusBar : BaseStatusBar
if ( thesight && (trk.lasthealth > 0) ) mtime += 105;
if ( level.maptime > trk.updated+mtime ) continue;
// ignore trackers clearly outside of player view
if ( viewvec dot level.Vec3Diff(viewpos,trk.pos) < 0 ) continue;
Vector3 tdir = level.Vec3Diff(viewpos,trk.pos);
if ( viewvec dot tdir < 0 ) continue;
proj.ProjectWorldPos(viewpos+tdir);
proj.ProjectToNormal();
if ( !proj.IsInFront() || !proj.IsInScreen() ) continue;
trackers[i++] = trk;
}
// squeeze if some were discarded
if ( i != hnd.trackers_cnt )
trackers.Resize(i);
sz = trackers.Size();
// sort by distance (give priority to players)
for ( int i=0; i<sz; i++ )
qsort_trackers(trackers,0,trackers.Size()-1);
// cap if limited (must cap after sorting, though, otherwise it'll look weird)
if ( maxtargetnum && (trackers.Size() > maxtargetnum) )
{
int j = 1;
while ( j < sz )
{
int k = j;
while ( (k > 0) && (CmpTarget(trackers[k-1],trackers[k]) || CmpDist(trackers[k-1].pos,trackers[k].pos)) )
{
SWWMCombatTracker tmp = trackers[k];
trackers[k] = trackers[k-1];
trackers[k-1] = tmp;
k--;
}
j++;
}
int endo = trackers.Size()-maxtargetnum;
// we gotta push the frontmost bars to the start, due to the inverted draw order
for ( int i=maxtargetnum-1; i>=0; i-- )
trackers[i] = trackers[endo+i];
trackers.Resize(maxtargetnum);
}
}
else trackers.Clear();
@ -328,7 +398,12 @@ Class SWWMStatusBar : BaseStatusBar
{
for ( SWWMScoreObj scr=hnd.scorenums; scr && (i<total_sz); scr=scr.next )
{
if ( viewvec dot level.Vec3Diff(viewpos,scr.pos) < 0 ) continue;
// ignore numbers clearly outside of player view
Vector3 tdir = level.Vec3Diff(viewpos,scr.pos);
if ( viewvec dot tdir < 0 ) continue;
proj.ProjectWorldPos(viewpos+tdir);
proj.ProjectToNormal();
if ( !proj.IsInFront() || !proj.IsInScreen() ) continue;
scoreobjs[i++] = scr;
}
}
@ -336,7 +411,12 @@ Class SWWMStatusBar : BaseStatusBar
{
for ( SWWMScoreObj scr=hnd.damnums; scr && (i<total_sz); scr=scr.next )
{
if ( viewvec dot level.Vec3Diff(viewpos,scr.pos) < 0 ) continue;
// ignore numbers clearly outside of player view
Vector3 tdir = level.Vec3Diff(viewpos,scr.pos);
if ( viewvec dot tdir < 0 ) continue;
proj.ProjectWorldPos(viewpos+tdir);
proj.ProjectToNormal();
if ( !proj.IsInFront() || !proj.IsInScreen() ) continue;
scoreobjs[i++] = scr;
}
}
@ -344,23 +424,7 @@ Class SWWMStatusBar : BaseStatusBar
if ( i != total_sz )
scoreobjs.Resize(i);
// sort by distance
sz = scoreobjs.Size();
for ( int i=0; i<sz; i++ )
{
int j = 1;
while ( j < sz )
{
int k = j;
while ( (k > 0) && (CmpScore(scoreobjs[k-1],scoreobjs[k]) || CmpDist(scoreobjs[k-1].pos,scoreobjs[k].pos)) )
{
SWWMScoreObj tmp = scoreobjs[k];
scoreobjs[k] = scoreobjs[k-1];
scoreobjs[k-1] = tmp;
k--;
}
j++;
}
}
qsort_scoreobjs(scoreobjs,0,scoreobjs.Size()-1);
}
override void Init()
@ -452,7 +516,6 @@ Class SWWMStatusBar : BaseStatusBar
}
}
// targetting array
int displayed = 0;
for ( int i=0; i<trackers.Size(); i++ )
{
let targ = trackers[i];

View file

@ -1372,7 +1372,67 @@ Class SWWMKnowledgeBaseMenu : GenericMenu
else if ( (b is 'PowerupGiver') || (b is 'AmmoFabricator') || db.bBIGPOWERUP ) tb = 3;
else if ( (b is 'Health') || (b is 'HealthPickup') || (b is 'SWWMHealth') ) tb = 5;
else if ( (b is 'Armor') || (b is 'SWWMSpareArmor') ) tb = 4;
return ta < tb;
return ta <= tb;
}
private int partition_inventory( Array<Inventory> a, int l, int h )
{
Inventory pv = a[h];
int i = (l-1);
for ( int j=l; j<=(h-1); j++ )
{
if ( CmpInventory(pv,a[j]) )
{
i++;
Inventory tmp = a[j];
a[j] = a[i];
a[i] = tmp;
}
}
Inventory tmp = a[h];
a[h] = a[i+1];
a[i+1] = tmp;
return i+1;
}
private void qsort_inventory( Array<Inventory> a, int l, int h )
{
if ( l >= h ) return;
int p = partition_inventory(a,l,h);
qsort_inventory(a,l,p-1);
qsort_inventory(a,p+1,h);
}
private int partition_store( Array<Class<Inventory> > a, Array<int> b, int l, int h )
{
Class<Inventory> pv = a[h];
int i = (l-1);
for ( int j=l; j<=(h-1); j++ )
{
if ( CmpInventoryClass(pv,a[j]) )
{
i++;
Class<Inventory> tmp = a[j];
a[j] = a[i];
a[i] = tmp;
int tmpi = b[j];
b[j] = b[i];
b[i] = tmpi;
}
}
Class<Inventory> tmp = a[h];
a[h] = a[i+1];
a[i+1] = tmp;
int tmpi = b[h];
b[h] = b[i+1];
b[i+1] = tmpi;
return i+1;
}
private void qsort_store( Array<Class<Inventory> > a, Array<int> b, int l, int h )
{
if ( l >= h ) return;
int p = partition_store(a,b,l,h);
qsort_store(a,b,l,p-1);
qsort_store(a,b,p+1,h);
}
override void Ticker()
@ -1405,22 +1465,7 @@ Class SWWMKnowledgeBaseMenu : GenericMenu
invlist.Push(inv);
}
// re-sort by category
for ( int i=0; i<invlist.Size(); i++ )
{
int j = 1;
while ( j < invlist.Size() )
{
int k = j;
while ( (k > 0) && CmpInventory(invlist[k-1],invlist[k]) )
{
Inventory tmp = invlist[k];
invlist[k] = invlist[k-1];
invlist[k-1] = tmp;
k--;
}
j++;
}
}
qsort_inventory(invlist,0,invlist.Size()-1);
}
else if ( curtab == TAB_KEYS )
{
@ -1515,25 +1560,7 @@ Class SWWMKnowledgeBaseMenu : GenericMenu
storeunits.Push(amt);
}
// re-sort by category
for ( int i=0; i<storelist.Size(); i++ )
{
int j = 1;
while ( j < storelist.Size() )
{
int k = j;
while ( (k > 0) && CmpInventoryClass(storelist[k-1],storelist[k]) )
{
Class<Inventory> tmp = storelist[k];
storelist[k] = storelist[k-1];
storelist[k-1] = tmp;
int tmpi = storeunits[k];
storeunits[k] = storeunits[k-1];
storeunits[k-1] = tmpi;
k--;
}
j++;
}
}
qsort_store(storelist,storeunits,0,storelist.Size()-1);
}
else if ( curtab == TAB_LIBRARY )
{
@ -1598,22 +1625,7 @@ Class SWWMKnowledgeBaseMenu : GenericMenu
invlist.Push(inv);
}
// re-sort by category
for ( int i=0; i<invlist.Size(); i++ )
{
int j = 1;
while ( j < invlist.Size() )
{
int k = j;
while ( (k > 0) && CmpInventory(invlist[k-1],invlist[k]) )
{
Inventory tmp = invlist[k];
invlist[k] = invlist[k-1];
invlist[k-1] = tmp;
k--;
}
j++;
}
}
qsort_inventory(invlist,0,invlist.Size()-1);
}
}
// check if use succeeded