0.9.2b release:

- Extend enemy drops setting to add an ammo-only option (which is now the default).
 - Add stats tracking for buttslams, plus a +300 score bonus.
 - Add obituary for buttslams.
 - Add stats tracking for total items and secrets found.
 - Add stats tracking for teleport distance (not usable yet until next gzdoom).
 - Refactor stats tab main section a bit to make it easier to extend.
 - Prettified version string colors a bit.
 - Increased width of intermission tip textbox to reduce line count and prevent overlap with the stats on certain resolutions.
 - Turned the tag fallback string into a constant, for the sake of cleaner code. Now there's only one instance of AWESOME IT'S PENIS in the code.
 - Adjusted classname beautification function, should handle very exotic names better now.
This commit is contained in:
Mari the Deer 2020-09-19 17:45:52 +02:00
commit d430d3c170
25 changed files with 218 additions and 116 deletions

View file

@ -389,6 +389,7 @@ Current score.
The scoring system is pretty straightforward. Each enemy you kill will give you points according to 5% of its base health, rounded up to the nearest multiple of 10 and capped to 1000, plus some bonuses (in order of application):
* +500 if the enemy was killed with the **Deep Impact** primary (humiliation).
* +300 if the enemy was killed with your butt while dashing (no, seriously).
* x2 for an overkill (enemy was gibbed or received twice its base health in damage).
* x1.5 for each combo level, up to x8 in steps of x0.5. Kills are considered combos if multiple enemies are killed within 5 seconds of each other.
* +100 for killing an enemy without having taken damage since last spawn, with extra +10 boosts for consecutive kills (extra boosts taper off after 10x).

View file

@ -80,5 +80,5 @@ user bool swwm_funtags = true; // replace vanilla monster names with silly one
server bool swwm_simplefog = false; // simplified teleport fogs (useful to speed up slaughterwads)
user bool swwm_bigtags = false; // use a bigger font for targeter tags
user bool swwm_intermusic = false; // use original intermission music
server bool swwm_enemydrops = false; // allow enemies to drop ammo and weapons
server int swwm_enemydrops = 0; // allow enemies to drop ammo and weapons (-1 - no ammo or weapons, 0 - ammo only, 1 - both)
server bool swwm_shotgib = true; // buckshot can gib (some people don't like this for some reason, so it's an option now)

View file

@ -672,6 +672,7 @@ O_YNYKRON = "%o was instantly removed by %k.";
O_YNYKRONALT = "%o was furiously obliterated by %k.";
O_POUND = "%o was very impressed by %k's landing.";
O_DASH = "%o was discombobulated by a very fast moving %k.";
O_BUTT = "%o received a lethal impact from %k's butt.";
O_JUMP = "%o was stepped on %k.";
O_MELEE = "%o was K.O.'d by %k.";
O_MOTH = "%%o was assaulted by %s's moths.";
@ -840,6 +841,7 @@ SWWM_SEXIT = "Secret Exit";
SWWM_LEG = "Legendary ";
SWWM_LEGPREFIX = "L";
SWWM_SHAMEFUL = "Humiliation";
SWWM_BUTTSLAM = "Buttslam";
// score messages
SWWM_FINDSECRET = "\cf%s\cf found a secret. +%d\c-";
SWWM_FINDKEY = "\cf%s\cf got the %s\cf. +%d\c-";

View file

@ -125,7 +125,10 @@ SWWM_MAXBLOOD = "Blood Limit";
SWWM_MAXGIBS = "Gib Limit";
SWWM_MAXCASINGS = "Casing Limit";
SWWM_MAXDEBRIS = "Debris Limit";
SWWM_ENEMYDROPS = "Enemies Drop Weapons and Ammo";
SWWM_ENEMYDROPS = "Enemy Ammo/Weapon Drops";
SWWM_DROPS_NONE = "Disabled";
SWWM_DROPS_AMMO = "Ammo Only";
SWWM_DROPS_WEAPONS = "Ammo And Weapons";
SWWM_SHOTGIB = "Buckshot can Gib";
TOOLTIP_SWWM_VOICETYPE = "Sets the voice pack for the player.";
TOOLTIP_SWWM_MUTEVOICE = "Control what gets muted, if you'd rather have a more silent protagonist.";
@ -193,7 +196,7 @@ TOOLTIP_SWWM_MAXBLOOD = "Caps the maximum amount of blood effects. Surpassing th
TOOLTIP_SWWM_MAXGIBS = "Caps the maximum amount of gibs. Surpassing this limit will cause the excess to fade out.";
TOOLTIP_SWWM_MAXCASINGS = "Caps the maximum amount of casings and spent magazines. Surpassing this limit will cause the excess to fade out.";
TOOLTIP_SWWM_MAXDEBRIS = "Caps the maximum amount of rubble from explosions and others. Surpassing this limit will cause the excess to fade out.";
TOOLTIP_SWWM_ENEMYDROPS = "If enabled, enemies will drop weapons and ammo like they would in vanilla. This is not recommended as it easily breaks progression.";
TOOLTIP_SWWM_ENEMYDROPS = "By default, enemies only drop ammo for a better progression. In maps with excessive amounts of enemies, it's recommended to disable this entirely.";
TOOLTIP_SWWM_SHOTGIB = "Some people don't like this for some reason, so here it is as an option.";
// knowledge base
SWWM_COMINGSOON = "(coming soon)";
@ -208,6 +211,7 @@ SWWM_STATUPTIME = "Uptime: ";
SWWM_STATONFOOT = "Distance on Foot: ";
SWWM_STATFLIGHT = "Distance in Air: ";
SWWM_STATSWIM = "Distance in Water: ";
SWWM_STATTELE = "Distance Teleported: ";
SWWM_STATBOOST = "Times Boosted: ";
SWWM_STATDASH = "Times Dashed: ";
SWWM_STATSTOMP = "Times Stomped: ";
@ -216,6 +220,9 @@ SWWM_STATSPEED = "Top Speed: ";
SWWM_STATAIRTIME = "Longest Air Time: ";
SWWM_STATWPONCH = "Usables Punched: ";
SWWM_STATBUSTS = "Walls Busted: ";
SWWM_STATBUTTS = "Butt Slams: ";
SWWM_STATITEMS = "Total Items: ";
SWWM_STATSECRETS = "Total Secrets: ";
SWWM_STATKILLS = "Total Kills: ";
SWWM_STATDEATHS = "Total Deaths: ";
SWWM_STATDDEALT = "Total Damage Dealt: ";

View file

@ -625,6 +625,7 @@ O_YNYKRON = "%o fue borrad@[ao_esp] instantáneamente por %k.";
O_YNYKRONALT = "%o fue aniquilad@[ao_esp] furiosamente por %k.";
O_POUND = "%o se llevó una gran impresión del aterrizaje de %k.";
O_DASH = "%o fue descuajeringad@[ao_esp] a todo gas por %k.";
O_BUTT = "%o recibió un impacto letal del trasero de %k.";
O_JUMP = "%o fue pisotead@[ao_esp] por %k.";
O_MELEE = "%o fue noquead@[ao_esp] por %k.";
O_MOTH = "%%o fue asaltad@[ao_esp] por las polillas de %s.";
@ -783,6 +784,7 @@ SWWM_SEXIT = "Salida Secreta";
SWWM_LEG = " Legendario";
SWWM_LEGPREFIX = "R";
SWWM_SHAMEFUL = "Humillación";
SWWM_BUTTSLAM = "Culazo";
// score messages
SWWM_FINDSECRET = "\cf%s\cf encontró un secreto. +%d\c-";
SWWM_FINDKEY = "\cf%s\cf obtuvo la %s\cf. +%d\c-";

View file

@ -122,7 +122,10 @@ SWWM_MAXBLOOD = "Límite de Sangre";
SWWM_MAXGIBS = "Límite de Vísceras";
SWWM_MAXCASINGS = "Límite de Casquillos";
SWWM_MAXDEBRIS = "Límite de Escombros";
SWWM_ENEMYDROPS = "Enemigos Sueltan Armas y Munición";
SWWM_ENEMYDROPS = "Drops de Armas y Munición de Enemigos";
SWWM_DROPS_NONE = "Desactivado";
SWWM_DROPS_AMMO = "Solo Munición";
SWWM_DROPS_WEAPONS = "Munición y Armas";
SWWM_SHOTGIB = "Los Perdigones pueden Desviscerar";
TOOLTIP_SWWM_VOICETYPE = "Selecciona el pack de voz para el jugador.";
TOOLTIP_SWWM_MUTEVOICE = "Controla lo que se mutea, si prefieres tener un protagonista más silencioso.";
@ -190,7 +193,7 @@ TOOLTIP_SWWM_MAXBLOOD = "Limita la cantidad máxima de efectos de sangre. Sobrep
TOOLTIP_SWWM_MAXGIBS = "Limita la cantidad máxima de vísceras. Sobrepasar este límite causará que el exceso se desvanezca.";
TOOLTIP_SWWM_MAXCASINGS = "Limita la cantidad máxima de casquillos y cargadores usados. Sobrepasar este límite causará que el exceso se desvanezca.";
TOOLTIP_SWWM_MAXDEBRIS = "Limita la cantidad máxima de escombros por explosiones y otros. Sobrepasar este límite causará que el exceso se desvanezca.";
TOOLTIP_SWWM_ENEMYDROPS = "Al activar, los enemigos soltarán armas y munición como harían en vanilla. Ésto no se recomienda ya que rompe fácilmente la progresión.";
TOOLTIP_SWWM_ENEMYDROPS = "Por defecto, los enemigos solo dropean munición para una mejor progresión. En mapas con enemigos excesivos, se recomienda desactivar los drops por completo.";
TOOLTIP_SWWM_SHOTGIB = "Hay gente a la que no le gusta esto por alguna razón, así que aquí está como opción.";
// knowledge base
SWWM_COMINGSOON = "(próximamente)";
@ -205,6 +208,7 @@ SWWM_STATUPTIME = "Tiempo Activo: ";
SWWM_STATONFOOT = "Distancia a Pie: ";
SWWM_STATFLIGHT = "Distancia en Aire: ";
SWWM_STATSWIM = "Distancia en Agua: ";
SWWM_STATTELE = "Distancia por Teletransporte: ";
SWWM_STATBOOST = "Veces Impulsado: ";
SWWM_STATDASH = "Veces Esprintado: ";
SWWM_STATSTOMP = "Veces Impactado: ";
@ -213,6 +217,9 @@ SWWM_STATSPEED = "Velocidad Punta: ";
SWWM_STATAIRTIME = "Mayor Tiempo en Aire: ";
SWWM_STATWPONCH = "Usables Aporreados: ";
SWWM_STATBUSTS = "Paredes Reventadas: ";
SWWM_STATBUTTS = "Golpes de Culo: ";
SWWM_STATITEMS = "Items Totales: ";
SWWM_STATSECRETS = "Secretos Totales: ";
SWWM_STATKILLS = "Bajas Enemigas Totales: ";
SWWM_STATDEATHS = "Muertes Totales: ";
SWWM_STATDDEALT = "Daño Total Infligido: ";

View file

@ -1,2 +1,2 @@
[default]
SWWM_MODVER="\chSWWM \cwGZ\c- 0.9.1b r550 (Sat 19 Sep 11:27:35 CEST 2020)";
SWWM_MODVER="\chSWWM \czGZ\c- \cw0.9.2b r551 \cu(Sat 19 Sep 17:45:52 CEST 2020)";

View file

@ -18,6 +18,12 @@ OptionValue "SWWMInterType"
1, "$SWWM_INTERART"
//2, "$SWWM_INTER4KOMA"
}
OptionValue "SWWMEnemyDropType"
{
-1, "$SWWM_DROPS_NONE"
0, "$SWWM_DROPS_AMMO"
1, "$SWWM_DROPS_WEAPONS"
}
OptionMenu "SWWMOptionMenu"
{
Class "SWWMOptionMenu"
@ -76,7 +82,7 @@ OptionMenu "SWWMOptionMenu"
//Option "$SWWM_PARTYTIME", "swwm_partytime", "YesNo"
StaticText " "
StaticText "$SWWM_BTITLE", "Gold"
Option "$SWWM_ENEMYDROPS", "swwm_enemydrops", "YesNo"
Option "$SWWM_ENEMYDROPS", "swwm_enemydrops", "SWWMEnemyDropType"
Option "$SWWM_RESETSCORE", "swwm_resetscore", "YesNo"
Option "$SWWM_EXTRAALERT", "swwm_extraalert", "YesNo"
Option "$SWWM_BOSSENHANCE", "swwm_upgradebosses", "YesNo"

View file

@ -23,7 +23,7 @@ Mixin Class SWWMAmmo
override bool SpecialDropAction( Actor dropper )
{
if ( swwm_enemydrops )
if ( swwm_enemydrops >= 0 )
{
if ( Amount == default.Amount ) return false;
// subdivide
@ -202,7 +202,7 @@ Class MagAmmo : Inventory abstract
override bool SpecialDropAction( Actor dropper )
{
if ( swwm_enemydrops )
if ( swwm_enemydrops >= 0 )
{
if ( Amount == default.Amount ) return false;
// subdivide

View file

@ -1545,6 +1545,7 @@ Class Hellblazer : SWWMWeapon
Stamina 90000;
Weapon.AmmoType1 "HellblazerMissiles";
Weapon.AmmoGive1 6;
SWWMWeapon.DropAmmoType "RocketAmmo";
Hellblazer.ClipCount 6;
+SWWMWEAPON.NOFIRSTGIVE;
+WEAPON.EXPLOSIVE;

View file

@ -1476,6 +1476,7 @@ Class Wallbuster : SWWMWeapon
Weapon.UpSound "wallbuster/select";
Weapon.AmmoType1 "RedShell";
Weapon.AmmoGive1 5;
SWWMWeapon.DropAmmoType "Shell";
Stamina 35000;
+SWWMWEAPON.NOFIRSTGIVE;
}

View file

@ -765,6 +765,7 @@ Class Eviscerator : SWWMWeapon
Stamina 50000;
Weapon.AmmoType1 "EvisceratorShell";
Weapon.AmmoGive1 4;
SWWMWeapon.DropAmmoType "EvisceratorShell";
+WEAPON.EXPLOSIVE;
}
States

View file

@ -2824,6 +2824,7 @@ Class Ynykron : SWWMWeapon
Stamina 5000000;
Weapon.AmmoType1 "YnykronAmmo";
Weapon.AmmoGive1 1;
SWWMWeapon.DropAmmoType "YnykronAmmo";
+SWWMWEAPON.NOFIRSTGIVE;
Ynykron.ClipCount 1;
+WEAPON.BFG;

View file

@ -762,6 +762,8 @@ Class SWWMHandler : EventHandler
SWWMCredits.Give(players[i],score);
SWWMScoreObj.Spawn(score,players[i].mo.Vec3Offset(0,0,players[i].mo.Height/2));
lastitemcount[i] = players[i].itemcount;
let s = SWWMStats.Find(players[i]);
s.items++;
}
}
// combat tracking
@ -981,6 +983,17 @@ Class SWWMHandler : EventHandler
scr.xcnt = ++ofs;
}
}
else if ( e.DamageType == 'Buttslam' )
{
score += 200;
if ( scr )
{
scr.xscore[ofs] = 0;
scr.xtcolor[ofs] = Font.CR_FIRE;
scr.xstr[ofs] = StringTable.Localize("$SWWM_BUTTSLAM");
scr.xcnt = ++ofs;
}
}
if ( (e.Damage >= e.Thing.GetSpawnHealth()*2) || (((e.Thing.Health <= e.Thing.GetGibHealth()) || (e.DamageSource.bEXTREMEDEATH) || (e.Inflictor && e.Inflictor.bEXTREMEDEATH) || (e.DamageType == 'Extreme')) && !e.DamageSource.bNOEXTREMEDEATH && (!e.Inflictor || !e.Inflictor.bNOEXTREMEDEATH)) )
{
score *= 2;

View file

@ -152,8 +152,12 @@ Class SWWMStatScreen : StatusScreen abstract
pdata.lasttip.Push(whichtip);
}
String tipstr = "\cd"..String.Format(StringTable.Localize("$SWWM_INTERTIP"),whichtip).."\c-\n"..StringTable.Localize(String.Format("$SWWM_INTERTIP%d",whichtip));
BrokenLines l = fnt.BreakLines(tipstr,300);
int bw = int(308*hs), bh = int((fnt.GetHeight()*l.Count()+12)*hs);
BrokenLines l = fnt.BreakLines(tipstr,400);
int lw = 0;
for ( int i=0; i<l.Count(); i++ )
if ( l.StringWidth(i) > lw )
lw = l.StringWidth(i);
int bw = int((lw+8)*hs), bh = int((fnt.GetHeight()*l.Count()+12)*hs);
double xx = 10, yy = (ss.y-10)-(fnt.GetHeight()*l.Count()+4);
Screen.Dim("Black",.8,int((xx-4)*hs),int((yy-4)*hs),bw,bh);
for ( int i=0; i<l.Count(); i++ )

View file

@ -907,6 +907,10 @@ Class SWWMWeapon : Weapon abstract
Actor pfield; // instance of parry field for current melee attack
bool wallponch; // is punching a wall (for activation checks)
Class<Ammo> dropammotype;
Property DropAmmoType : dropammotype;
FlagDef NoFirstGive : SWeaponFlags, 0; // don't give ammo on first pickup (for weapons with a clip count)
override bool HandlePickup( Inventory item )
@ -1247,7 +1251,24 @@ Class SWWMWeapon : Weapon abstract
}
override bool SpecialDropAction( Actor dropper )
{
if ( swwm_enemydrops ) return false;
if ( swwm_enemydrops > 0 ) return false;
else if ( swwm_enemydrops == 0 )
{
// drop our corresponding ammo
if ( !DropAmmoType ) return true;
let a = Inventory(Spawn(DropAmmoType,pos,ALLOW_REPLACE));
if ( !a ) return true;
a.bDROPPED = true;
a.bNOGRAVITY = false;
if ( !(level.compatflags&COMPATF_NOTOSSDROPS) )
a.TossItem();
if ( a is 'Ammo' )
a.ModifyDropAmount(Ammo(a).DropAmount);
a.bTOSSED = true;
if ( a.SpecialDropAction(dropper) )
a.Destroy();
return true;
}
// no weapon drops from enemies
return true;
}

View file

@ -51,6 +51,7 @@ Class SWWMKnowledgeBaseMenu : GenericMenu
int drag; // when dragging with the mouse, which scroller to affect (1 = ofs0 and so on)
// stats
SWWMStats stats;
Array<String> statlist;
Array<MonsterKill> sorted_mstats; // sorted by killcount
// inventory lists
Array<Inventory> invlist;
@ -429,7 +430,9 @@ Class SWWMKnowledgeBaseMenu : GenericMenu
else if ( curtab == TAB_STATS )
{
int cnt = 0;
if ( sel1 == STAT_KILLS )
if ( sel1 == STAT_MAIN )
cnt = statlist.Size();
else if ( sel1 == STAT_KILLS )
cnt = sorted_mstats.Size();
else if ( sel1 == STAT_LEVEL )
cnt = stats.lstats.Size();
@ -1295,7 +1298,8 @@ Class SWWMKnowledgeBaseMenu : GenericMenu
// are we clicking where the scrollbar should be?
if ( mpos.x < 632 ) return res;
int cnt = 0;
if ( sel1 == STAT_KILLS ) cnt = stats.mstats.Size();
if ( sel1 == STAT_MAIN ) cnt = statlist.Size();
else if ( sel1 == STAT_KILLS ) cnt = stats.mstats.Size();
else if ( sel1 == STAT_LEVEL ) cnt = stats.lstats.Size();
if ( cnt <= 28 ) return res; // no scrollbar
// calculate offset
@ -1490,7 +1494,8 @@ Class SWWMKnowledgeBaseMenu : GenericMenu
else if ( curtab == TAB_STATS )
{
int cnt = 0;
if ( sel1 == STAT_KILLS ) cnt = stats.mstats.Size();
if ( sel1 == STAT_MAIN ) cnt = statlist.Size();
else if ( sel1 == STAT_KILLS ) cnt = stats.mstats.Size();
else if ( sel1 == STAT_LEVEL ) cnt = stats.lstats.Size();
if ( cnt <= 22 ) return res; // no scrollbar
// calculate offset
@ -1856,6 +1861,83 @@ Class SWWMKnowledgeBaseMenu : GenericMenu
if ( sel1 >= invlist.Size() ) sel1 = max(0,invlist.Size()-1);
}
}
else if ( (curtab == TAB_STATS) && (sel1 == STAT_MAIN) )
{
statlist.Clear();
// wish I could use macros for this
int thour = ((level.totaltime-stats.lastspawn)/(3600*Thinker.TICRATE));
int tmin = ((level.totaltime-stats.lastspawn)/(60*Thinker.TICRATE))%60;
int tsec = ((level.totaltime-stats.lastspawn)/Thinker.TICRATE)%60;
String str = String.Format("\cx%s\c-%02d\cu:\c-%02d\cu:\c-%02d",StringTable.Localize("$SWWM_STATUPTIME"),thour,tmin,tsec);
statlist.Push(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_METER"));
else str = String.Format("\cx%s\c-%g\cu%s\c-",StringTable.Localize("$SWWM_STATONFOOT"),stats.grounddist/32.,StringTable.Localize("$SWWM_UNIT_METER"));
statlist.Push(str);
if ( stats.airdist > 32000. ) str = String.Format("\cx%s\c-%g\cu%s\c-",StringTable.Localize("$SWWM_STATFLIGHT"),stats.airdist/32000.,StringTable.Localize("$SWWM_UNIT_KILOMETER"));
else str = String.Format("\cx%s\c-%g\cu%s\c-",StringTable.Localize("$SWWM_STATFLIGHT"),stats.airdist/32.,StringTable.Localize("$SWWM_UNIT_METER"));
statlist.Push(str);
if ( stats.swimdist > 32000. ) str = String.Format("\cx%s\c-%g\cu%s\c-",StringTable.Localize("$SWWM_STATSWIM"),stats.swimdist/32000.,StringTable.Localize("$SWWM_UNIT_KILOMETER"));
else str = String.Format("\cx%s\c-%g\cu%s\c-",StringTable.Localize("$SWWM_STATSWIM"),stats.swimdist/32.,StringTable.Localize("$SWWM_UNIT_METER"));
statlist.Push(str);
if ( stats.teledist > 32000. ) str = String.Format("\cx%s\c-%g\cu%s\c-",StringTable.Localize("$SWWM_STATTELE"),stats.teledist/32000.,StringTable.Localize("$SWWM_UNIT_KILOMETER"));
else str = String.Format("\cx%s\c-%g\cu%s\c-",StringTable.Localize("$SWWM_STATTELE"),stats.teledist/32.,StringTable.Localize("$SWWM_UNIT_METER"));
statlist.Push(str);
str = String.Format("\cx%s\c-%d",StringTable.Localize("$SWWM_STATBOOST"),stats.boostcount);
statlist.Push(str);
str = String.Format("\cx%s\c-%d",StringTable.Localize("$SWWM_STATDASH"),stats.dashcount);
statlist.Push(str);
str = String.Format("\cx%s\c-%d",StringTable.Localize("$SWWM_STATSTOMP"),stats.stompcount);
statlist.Push(str);
str = String.Format("\cx%s\c-%g\cu%s\c-",StringTable.Localize("$SWWM_STATFUEL"),stats.fuelusage/60.,StringTable.Localize("$SWWM_UNIT_LITER"));
statlist.Push(str);
str = String.Format("\cx%s\c-%g\cu%s\c-",StringTable.Localize("$SWWM_STATSPEED"),stats.topspeed*32000./(3600*Thinker.TICRATE),StringTable.Localize("$SWWM_UNIT_KPH"));
statlist.Push(str);
thour = (stats.airtime/(3600*Thinker.TICRATE));
tmin = (stats.airtime/(60*Thinker.TICRATE))%60;
tsec = (stats.airtime/Thinker.TICRATE)%60;
str = String.Format("\cx%s\c-%02d\cu:\c-%02d\cu:\c-%02d",StringTable.Localize("$SWWM_STATAIRTIME"),thour,tmin,tsec);
statlist.Push(str);
str = String.Format("\cx%s\c-%d",StringTable.Localize("$SWWM_STATWPONCH"),stats.wponch);
statlist.Push(str);
str = String.Format("\cx%s\c-%d",StringTable.Localize("$SWWM_STATBUSTS"),stats.busts);
statlist.Push(str);
str = String.Format("\cx%s\c-%d",StringTable.Localize("$SWWM_STATBUTTS"),stats.buttslams);
statlist.Push(str);
str = String.Format("\cx%s\c-%d",StringTable.Localize("$SWWM_STATITEMS"),stats.items);
statlist.Push(str);
str = String.Format("\cx%s\c-%d",StringTable.Localize("$SWWM_STATSECRETS"),stats.secrets);
statlist.Push(str);
str = String.Format("\cx%s\c-%d",StringTable.Localize("$SWWM_STATKILLS"),stats.kills);
statlist.Push(str);
str = String.Format("\cx%s\c-%d",StringTable.Localize("$SWWM_STATDEATHS"),stats.deaths);
statlist.Push(str);
if ( stats.hdamagedealt > 0 ) str = str = String.Format("\cx%s\c-%d%09d",StringTable.Localize("$SWWM_STATDDEALT"),stats.hdamagedealt,stats.damagedealt);
else str = String.Format("\cx%s\c-%d",StringTable.Localize("$SWWM_STATDDEALT"),stats.damagedealt);
statlist.Push(str);
if ( stats.hdamagetaken > 0 ) str = String.Format("\cx%s\c-%d%09d",StringTable.Localize("$SWWM_STATDTAKEN"),stats.hdamagetaken,stats.damagetaken);
else str = String.Format("\cx%s\c-%d",StringTable.Localize("$SWWM_STATDTAKEN"),stats.damagetaken);
statlist.Push(str);
str = String.Format("\cx%s\c-%d",StringTable.Localize("$SWWM_STATTDEALT"),stats.topdealt);
statlist.Push(str);
str = String.Format("\cx%s\c-%d",StringTable.Localize("$SWWM_STATTTAKEN"),stats.toptaken);
statlist.Push(str);
str = String.Format("\cx%s\c-%d",StringTable.Localize("$SWWM_STATMKILL"),stats.mkill);
statlist.Push(str);
str = String.Format("\cx%s\c-%d",StringTable.Localize("$SWWM_STATSKILL"),stats.skill);
statlist.Push(str);
str = String.Format("\cx%s\c-",StringTable.Localize("$SWWM_STATFAVWEAP"));
if ( stats.favweapon == -1 ) str = str.."N/A";
else if ( stats.wstats[stats.favweapon].w == 'SWWMWeapon' ) str = str..StringTable.Localize("$SWWM_YOURSELF");
else
{
let def = GetDefaultByType(stats.wstats[stats.favweapon].w);
str = str..def.GetTag();
}
statlist.Push(str);
if ( stats.hhiscore > 0 ) str = String.Format("\cx%s\cu¥\c-%d%09d",StringTable.Localize("$SWWM_STATHISCORE"),stats.hhiscore,stats.hiscore);
else str = String.Format("\cx%s\cu¥\c-%09d",StringTable.Localize("$SWWM_STATHISCORE"),stats.hiscore);
statlist.Push(str);
}
// ui->play transaction checks
for ( int i=0; i<checklist.Size(); i++ )
{
@ -2140,96 +2222,30 @@ Class SWWMKnowledgeBaseMenu : GenericMenu
{
xx += 9;
yy = 23;
// wish I could use macros for this
int thour = ((level.totaltime-stats.lastspawn)/(3600*Thinker.TICRATE));
int tmin = ((level.totaltime-stats.lastspawn)/(60*Thinker.TICRATE))%60;
int tsec = ((level.totaltime-stats.lastspawn)/Thinker.TICRATE)%60;
str = String.Format("\cx%s\c-%02d\cu:\c-%02d\cu:\c-%02d",StringTable.Localize("$SWWM_STATUPTIME"),thour,tmin,tsec);
Screen.DrawText(fnt,Font.CR_WHITE,origin.x+xx,origin.y+yy,str,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true);
yy += 16;
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_METER"));
else str = String.Format("\cx%s\c-%g\cu%s\c-",StringTable.Localize("$SWWM_STATONFOOT"),stats.grounddist/32.,StringTable.Localize("$SWWM_UNIT_METER"));
Screen.DrawText(fnt,Font.CR_WHITE,origin.x+xx,origin.y+yy,str,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true);
yy += 16;
if ( stats.airdist > 32000. ) str = String.Format("\cx%s\c-%g\cu%s\c-",StringTable.Localize("$SWWM_STATFLIGHT"),stats.airdist/32000.,StringTable.Localize("$SWWM_UNIT_KILOMETER"));
else str = String.Format("\cx%s\c-%g\cu%s\c-",StringTable.Localize("$SWWM_STATFLIGHT"),stats.airdist/32.,StringTable.Localize("$SWWM_UNIT_METER"));
Screen.DrawText(fnt,Font.CR_WHITE,origin.x+xx,origin.y+yy,str,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true);
yy += 16;
if ( stats.swimdist > 32000. ) str = String.Format("\cx%s\c-%g\cu%s\c-",StringTable.Localize("$SWWM_STATSWIM"),stats.swimdist/32000.,StringTable.Localize("$SWWM_UNIT_KILOMETER"));
else str = String.Format("\cx%s\c-%g\cu%s\c-",StringTable.Localize("$SWWM_STATSWIM"),stats.swimdist/32.,StringTable.Localize("$SWWM_UNIT_METER"));
Screen.DrawText(fnt,Font.CR_WHITE,origin.x+xx,origin.y+yy,str,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true);
yy += 16;
str = String.Format("\cx%s\c-%d",StringTable.Localize("$SWWM_STATBOOST"),stats.boostcount);
Screen.DrawText(fnt,Font.CR_WHITE,origin.x+xx,origin.y+yy,str,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true);
yy += 16;
str = String.Format("\cx%s\c-%d",StringTable.Localize("$SWWM_STATDASH"),stats.dashcount);
Screen.DrawText(fnt,Font.CR_WHITE,origin.x+xx,origin.y+yy,str,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true);
yy += 16;
str = String.Format("\cx%s\c-%d",StringTable.Localize("$SWWM_STATSTOMP"),stats.stompcount);
Screen.DrawText(fnt,Font.CR_WHITE,origin.x+xx,origin.y+yy,str,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true);
yy += 16;
str = String.Format("\cx%s\c-%g\cu%s\c-",StringTable.Localize("$SWWM_STATFUEL"),stats.fuelusage/60.,StringTable.Localize("$SWWM_UNIT_LITER"));
Screen.DrawText(fnt,Font.CR_WHITE,origin.x+xx,origin.y+yy,str,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true);
yy += 16;
str = String.Format("\cx%s\c-%g\cu%s\c-",StringTable.Localize("$SWWM_STATSPEED"),stats.topspeed*32000./(3600*Thinker.TICRATE),StringTable.Localize("$SWWM_UNIT_KPH"));
Screen.DrawText(fnt,Font.CR_WHITE,origin.x+xx,origin.y+yy,str,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true);
yy += 16;
thour = (stats.airtime/(3600*Thinker.TICRATE));
tmin = (stats.airtime/(60*Thinker.TICRATE))%60;
tsec = (stats.airtime/Thinker.TICRATE)%60;
str = String.Format("\cx%s\c-%02d\cu:\c-%02d\cu:\c-%02d",StringTable.Localize("$SWWM_STATAIRTIME"),thour,tmin,tsec);
Screen.DrawText(fnt,Font.CR_WHITE,origin.x+xx,origin.y+yy,str,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true);
yy += 16;
str = String.Format("\cx%s\c-%d",StringTable.Localize("$SWWM_STATWPONCH"),stats.wponch);
Screen.DrawText(fnt,Font.CR_WHITE,origin.x+xx,origin.y+yy,str,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true);
yy += 16;
str = String.Format("\cx%s\c-%d",StringTable.Localize("$SWWM_STATBUSTS"),stats.busts);
Screen.DrawText(fnt,Font.CR_WHITE,origin.x+xx,origin.y+yy,str,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true);
yy += 16;
str = String.Format("\cx%s\c-%d",StringTable.Localize("$SWWM_STATKILLS"),stats.kills);
Screen.DrawText(fnt,Font.CR_WHITE,origin.x+xx,origin.y+yy,str,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true);
yy += 16;
str = String.Format("\cx%s\c-%d",StringTable.Localize("$SWWM_STATDEATHS"),stats.deaths);
Screen.DrawText(fnt,Font.CR_WHITE,origin.x+xx,origin.y+yy,str,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true);
yy += 16;
if ( stats.hdamagedealt > 0 ) str = str = String.Format("\cx%s\c-%d%09d",StringTable.Localize("$SWWM_STATDDEALT"),stats.hdamagedealt,stats.damagedealt);
else str = String.Format("\cx%s\c-%d",StringTable.Localize("$SWWM_STATDDEALT"),stats.damagedealt);
Screen.DrawText(fnt,Font.CR_WHITE,origin.x+xx,origin.y+yy,str,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true);
yy += 16;
if ( stats.hdamagetaken > 0 ) str = String.Format("\cx%s\c-%d%09d",StringTable.Localize("$SWWM_STATDTAKEN"),stats.hdamagetaken,stats.damagetaken);
else str = String.Format("\cx%s\c-%d",StringTable.Localize("$SWWM_STATDTAKEN"),stats.damagetaken);
Screen.DrawText(fnt,Font.CR_WHITE,origin.x+xx,origin.y+yy,str,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true);
yy += 16;
str = String.Format("\cx%s\c-%d",StringTable.Localize("$SWWM_STATTDEALT"),stats.topdealt);
Screen.DrawText(fnt,Font.CR_WHITE,origin.x+xx,origin.y+yy,str,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true);
yy += 16;
str = String.Format("\cx%s\c-%d",StringTable.Localize("$SWWM_STATTTAKEN"),stats.toptaken);
Screen.DrawText(fnt,Font.CR_WHITE,origin.x+xx,origin.y+yy,str,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true);
yy += 16;
str = String.Format("\cx%s\c-%d",StringTable.Localize("$SWWM_STATMKILL"),stats.mkill);
Screen.DrawText(fnt,Font.CR_WHITE,origin.x+xx,origin.y+yy,str,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true);
yy += 16;
str = String.Format("\cx%s\c-%d",StringTable.Localize("$SWWM_STATSKILL"),stats.skill);
Screen.DrawText(fnt,Font.CR_WHITE,origin.x+xx,origin.y+yy,str,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true);
yy += 16;
str = String.Format("\cx%s\c-",StringTable.Localize("$SWWM_STATFAVWEAP"));
if ( stats.favweapon == -1 ) str = str.."N/A";
else
int cnt = statlist.Size();
if ( cnt <= 0 )
{
if ( stats.wstats[stats.favweapon].w == 'SWWMWeapon' )
str = str..StringTable.Localize("$SWWM_YOURSELF");
else
{
let def = GetDefaultByType(stats.wstats[stats.favweapon].w);
str = str..def.GetTag();
}
str = StringTable.Localize("$SWWM_NOSTAT");
Screen.DrawText(fnt,Font.CR_FIRE,origin.x+xx+((634-xx)-fnt.StringWidth(str))/2.,(ss.y-fnt.GetHeight())/2.,str,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true);
return;
}
int ofs = clamp(sel0,0,max(0,cnt-22));
for ( int i=ofs; i<cnt; i++ )
{
if ( yy >= 370 ) break;
str = statlist[i];
Screen.DrawText(fnt,Font.CR_WHITE,origin.x+xx,origin.y+yy,str,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true);
yy += 16;
}
// scrollbar
if ( cnt > 22 )
{
Screen.DrawTexture(WindowSeparator,false,origin.x+631,origin.y+14,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true);
xx = 634;
int szr = cnt-22;
yy = floor(ofs*(353./szr))+17;
Screen.DrawText(TewiFont,Font.CR_FIRE,origin.x+xx,origin.y+yy,"▮",DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true);
}
Screen.DrawText(fnt,Font.CR_WHITE,origin.x+xx,origin.y+yy,str,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true);
yy += 16;
if ( stats.hhiscore > 0 ) str = String.Format("\cx%s\cu¥\c-%d%09d",StringTable.Localize("$SWWM_STATHISCORE"),stats.hhiscore,stats.hiscore);
else str = String.Format("\cx%s\cu¥\c-%09d",StringTable.Localize("$SWWM_STATHISCORE"),stats.hiscore);
Screen.DrawText(fnt,Font.CR_WHITE,origin.x+xx,origin.y+yy,str,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true);
yy += 16;
}
else if ( sel1 == STAT_KILLS )
{
@ -2246,9 +2262,9 @@ Class SWWMKnowledgeBaseMenu : GenericMenu
for ( int i=ofs; i<cnt; i++ )
{
if ( yy >= 370 ) break;
str = GetDefaultByType(sorted_mstats[i].m).GetTag("AWESOME IT'S PENIS");
str = GetDefaultByType(sorted_mstats[i].m).GetTag(FallbackTag);
// beautify if there's no tag
if ( str == "AWESOME IT'S PENIS" )
if ( str == FallbackTag )
{
str = sorted_mstats[i].m.GetClassName();
SWWMUtility.BeautifyClassName(str);

View file

@ -269,6 +269,7 @@ Class Demolitionist : PlayerPawn
{
if ( mod == 'Jump' ) return StringTable.Localize("$O_JUMP");
if ( mod == 'Dash' ) return StringTable.Localize("$O_DASH");
if ( mod == 'Buttslam' ) return StringTable.Localize("$O_BUTT");
if ( mod == 'GroundPound' ) return StringTable.Localize("$O_POUND");
return Super.GetObituary(victim,inflictor,mod,playerattack);
}
@ -721,7 +722,7 @@ Class Demolitionist : PlayerPawn
}
if ( a.bSHOOTABLE )
{
dmg = a.DamageMobj(self,self,dmg,'Dash',flg);
dmg = a.DamageMobj(self,self,dmg,buttslam?'Buttslam':'Dash',flg);
if ( a && !a.bNOBLOOD && (raging || !a.bINVULNERABLE) )
{
a.TraceBleed(dmg,self);
@ -732,12 +733,13 @@ Class Demolitionist : PlayerPawn
A_StartSound("demolitionist/buttslam",CHAN_DAMAGE,CHAN_OVERLAP,1.,.2);
Spawn("SWWMItemFog",level.Vec3Offset(pos,diff/2));
A_QuakeEx(8,8,8,8,0,3000,"",QF_RELATIVE|QF_SCALEDOWN,falloff:300,rollIntensity:1.);
mystats.buttslams++;
}
}
if ( raging )
{
let ps = Spawn("BigPunchSplash",level.Vec3Offset(pos,diff/2));
ps.damagetype = 'Dash';
ps.damagetype = buttslam?'Buttslam':'Dash';
ps.target = self;
ps.special1 = dmg;
raging.DoHitFX();
@ -779,7 +781,7 @@ Class Demolitionist : PlayerPawn
{
// busted through
let ps = Spawn("BigPunchSplash",Vec3Offset(0,0,Height));
ps.damagetype = 'Dash';
ps.damagetype = buttslam?'Buttslam':'Dash';
ps.target = self;
ps.special1 = int(15+spd*2.5);
A_StartSound("demolitionist/bump",CHAN_DAMAGE,CHANF_OVERLAP);
@ -790,6 +792,7 @@ Class Demolitionist : PlayerPawn
A_StartSound("demolitionist/buttslam",CHAN_DAMAGE,CHANF_OVERLAP,1.,.2);
Spawn("SWWMItemFog",Vec3Offset(0,0,Height));
A_QuakeEx(8,8,8,8,0,3000,"",QF_RELATIVE|QF_SCALEDOWN,falloff:300,rollIntensity:1.);
mystats.buttslams++;
}
}
}
@ -808,7 +811,7 @@ Class Demolitionist : PlayerPawn
if ( raging )
{
let ps = Spawn("BigPunchSplash",Vec3Offset(0,0,Height));
ps.damagetype = 'Dash';
ps.damagetype = (dir dot viewdir < -3.)?'Buttslam':'Dash';
ps.target = self;
ps.special1 = int(15+spd*2.5);
raging.DoHitFX();
@ -865,7 +868,7 @@ Class Demolitionist : PlayerPawn
{
// busted through
let ps = Spawn("BigPunchSplash",Vec3Offset(dir.x*radius,dir.y*radius,(tempme.Results.Tier==TIER_UPPER)?Height:(tempme.Results.Tier==TIER_LOWER)?0:(Height/2)));
ps.damagetype = 'Dash';
ps.damagetype = buttslam?'Buttslam':'Dash';
ps.target = self;
ps.special1 = int(15+spd*2.5);
A_StartSound("demolitionist/bump",CHAN_DAMAGE,CHANF_OVERLAP);
@ -875,6 +878,7 @@ Class Demolitionist : PlayerPawn
A_StartSound("demolitionist/buttslam",CHAN_DAMAGE,CHANF_OVERLAP,1.,.2);
Spawn("SWWMItemFog",Vec3Offset(dir.x*radius,dir.y*radius,(tempme.Results.Tier==TIER_UPPER)?Height:(tempme.Results.Tier==TIER_LOWER)?0:(Height/2)));
A_QuakeEx(8,8,8,8,0,3000,"",QF_RELATIVE|QF_SCALEDOWN,falloff:300,rollIntensity:1.);
mystats.buttslams++;
}
continue;
}
@ -892,7 +896,7 @@ Class Demolitionist : PlayerPawn
if ( raging )
{
let ps = Spawn("BigPunchSplash",Vec3Offset(dir.x*radius,dir.y*radius,Height/2.));
ps.damagetype = 'Dash';
ps.damagetype = (dir dot viewdir < -3.)?'Buttslam':'Dash';
ps.target = self;
ps.special1 = int(15+spd*2.5);
raging.DoHitFX();
@ -1610,6 +1614,7 @@ Class Demolitionist : PlayerPawn
if ( CheckLocalView() ) SWWMHandler.AddOneliner("findsecret",2,40);
SWWMCredits.Give(player,score);
SWWMScoreObj.Spawn(score,Vec3Offset(0,0,Height/2));
mystats.secrets++;
return true;
}
override void AddInventory( Inventory item )

View file

@ -857,7 +857,7 @@ Class RagekitPower : Powerup
override void ModifyDamage( int damage, Name damageType, out int newdamage, bool passive, Actor inflictor, Actor source, int flags )
{
if ( !passive && ((damageType == 'Melee') || (damageType == 'Jump') || (damageType == 'Dash') || (damageType == 'GroundPound')) )
if ( !passive && ((damageType == 'Melee') || (damageType == 'Jump') || (damageType == 'Dash') || (damageType == 'Buttslam') || (damageType == 'GroundPound')) )
{
newdamage = damage*8;
DoHitFX();

View file

@ -2589,6 +2589,7 @@ Class Spreadgun : SWWMWeapon
Weapon.SelectionOrder 500;
Weapon.AmmoType1 "RedShell";
Weapon.AmmoGive1 1;
SWWMWeapon.DropAmmoType "Clip";
Stamina 15000;
+SWWMWEAPON.NOFIRSTGIVE;
}

View file

@ -2121,6 +2121,7 @@ Class Sparkster : SWWMWeapon
Stamina 200000;
Weapon.AmmoType1 "SparkUnit";
Weapon.AmmoGive1 1;
SWWMWeapon.DropAmmoType "SparkUnit";
Sparkster.ClipCount 8;
+SWWMWEAPON.NOFIRSTGIVE;
+WEAPON.EXPLOSIVE;

View file

@ -1160,6 +1160,7 @@ Class CandyGun : SWWMWeapon
Weapon.AmmoGive1 1;
Weapon.AmmoGive2 1;
Weapon.AmmoUse2 0;
SWWMWeapon.DropAmmoType "CandyGunAmmo";
CandyGun.ClipCount 7;
+SWWMWEAPON.NOFIRSTGIVE;
+WEAPON.EXPLOSIVE;

View file

@ -1078,6 +1078,7 @@ Class SilverBullet : SWWMWeapon
Weapon.AmmoGive1 1;
Weapon.AmmoType2 "SilverBulletAmmo2";
Weapon.AmmoGive2 0;
SWWMWeapon.DropAmmoType "SilverBulletAmmo";
SilverBullet.ClipCount 5;
+SWWMWEAPON.NOFIRSTGIVE;
+WEAPON.NO_AUTO_SWITCH;

View file

@ -29,7 +29,7 @@ Class SWWMStats : Thinker
int lastspawn, dashcount, boostcount, stompcount, airtime, kills,
deaths, damagedealt, hdamagedealt, damagetaken, hdamagetaken,
mkill, hiscore, hhiscore, topdealt, toptaken, skill, wponch,
busts;
busts, buttslams, secrets, items;
double grounddist, airdist, swimdist, fuelusage, topspeed, teledist;
Array<WeaponUsage> wstats;
Array<MonsterKill> mstats;
@ -771,8 +771,8 @@ Class SWWMCombatTracker : Thinker
if ( !funtags ) funtags = CVar.GetCVar('swwm_funtags',players[consoleplayer]);
if ( mytarget && (mytarget.player || mytarget.bISMONSTER || (mytarget is 'BossBrain')) )
{
String realtag = funtags.GetBool()?SWWMUtility.GetFunTag(mytarget,"AWESOME IT'S PENIS"):mytarget.GetTag("AWESOME IT'S PENIS");
if ( realtag == "AWESOME IT'S PENIS" )
String realtag = funtags.GetBool()?SWWMUtility.GetFunTag(mytarget,FallbackTag):mytarget.GetTag(FallbackTag);
if ( realtag == FallbackTag )
{
realtag = mytarget.GetClassName();
SWWMUtility.BeautifyClassName(realtag);

View file

@ -10,6 +10,8 @@ enum EDoExplosionFlags
DE_EXTRAZTHRUST = 32, // applies a higher Z thrust to enemies on ground
};
const FallbackTag = "AWESOME IT'S PENIS"; // used on tag processing, please don't mind the actual string used)
Class SWWMUtility
{
static clearscope void StripColor( out String str )
@ -57,7 +59,15 @@ Class SWWMUtility
if ( i < len-1 )
{
int cp2 = workstr.GetNextCodePoint(i+1);
if ( (String.CharLower(cp1) == cp1) && (String.CharUpper(cp2) == cp2) )
// this looks awkward, but I have to also account for non-letter characters
// uppercase after lowercase
if ( (String.CharUpper(cp1) != cp1) && (String.CharLower(cp2) != cp2) )
str.AppendCharacter(0x20);
// uppercase after non-letter
else if ( (String.CharUpper(cp1) == cp1) && (String.CharLower(cp1) == cp1) && (String.CharLower(cp2) != cp2) )
str.AppendCharacter(0x20);
// non-letter after lowercase
else if ( (String.CharUpper(cp1) != cp1) && (String.CharLower(cp2) == cp2) && (String.CharUpper(cp2) == cp2) )
str.AppendCharacter(0x20);
}
}