Achievement system fixes and stackable chanceboxes.

This commit is contained in:
Mari the Deer 2022-01-11 02:18:38 +01:00
commit 35b0ad1b2b
10 changed files with 126 additions and 37 deletions

View file

@ -3,7 +3,8 @@
# basename: the base name used to construct cvars and localization strings
# maxval: limit progress value (if any), a value of -1 means it needs special
# handling in zscript (usually for cases where this is dynamic, like
# the "all collectibles" achievement)
# the "all collectibles" achievement), and a value below -1 indicates
# this is a bitfield, where abs(maxval) bits must be set
# hasformat: the TXT string has a %d in it to substitute for maxval
# gametype: the game this belongs to (any, doom, heretic, hexen, raven, etc.)
acid,50,yes,any
@ -25,7 +26,6 @@ bune,500,yes,any
bustin,50,yes,any
butts,100,yes,any
candy,1000,yes,any
cheat,0,no,any
cliffyb,0,no,nothexen
clonk,0,no,any
conga,10,yes,any
@ -65,6 +65,7 @@ lead,1500,yes,any
ligma,0,no,any
love,10,yes,any
#mashiro,0,no,any
matryoshka,0,no,any
mbf,0,no,doom
mega,10000,yes,any
moth,50,yes,any

View file

Before

Width:  |  Height:  |  Size: 2.5 KiB

After

Width:  |  Height:  |  Size: 2.5 KiB

Before After
Before After

View file

@ -1146,8 +1146,6 @@ SWWM_ACHIEVEMENT_BUTTS_TAG = "Let's get to Bashing Butts";
SWWM_ACHIEVEMENT_BUTTS_TXT = "Kill %d enemies by dashing backwards";
SWWM_ACHIEVEMENT_CANDY_TAG = "Piece of Candy";
SWWM_ACHIEVEMENT_CANDY_TXT = "Collect %d nuggets";
SWWM_ACHIEVEMENT_CHEAT_TAG = "Mishe Trickery";
SWWM_ACHIEVEMENT_CHEAT_TXT = "Successfully input a cheat code";
SWWM_ACHIEVEMENT_CLIFFYB_TAG = "Errand Boy Bullshit";
SWWM_ACHIEVEMENT_CLIFFYB_TXT = "Finish a map without collecting any keys";
SWWM_ACHIEVEMENT_CLONK_TAG = "CLONK";
@ -1230,6 +1228,8 @@ SWWM_ACHIEVEMENT_LOVE_TAG = "Lethal Love";
SWWM_ACHIEVEMENT_LOVE_TXT = "Kill %d enemies with a blown kiss (not counting insta-kills)";
SWWM_ACHIEVEMENT_MASHIRO_TAG = "Layers of White";
SWWM_ACHIEVEMENT_MASHIRO_TXT = "Summon the White Lady";
SWWM_ACHIEVEMENT_MATRYOSHKA_TAG = "Mishe Trickery";
SWWM_ACHIEVEMENT_MATRYOSHKA_TXT = "A Chancebox inside a Chancebox inside a Chancebox inside a Chancebox inside a...";
SWWM_ACHIEVEMENT_MBF_TAG = "You can Pet the Dog";
SWWM_ACHIEVEMENT_MBF_TXT = "Pet a dog";
SWWM_ACHIEVEMENT_MEGA_TAG = "Mass Massacre";

View file

@ -1006,8 +1006,6 @@ SWWM_ACHIEVEMENT_BUTTS_TAG = "Esos Glúteos Firmes";
SWWM_ACHIEVEMENT_BUTTS_TXT = "Mata %d enemigos embistiendo marcha atrás";
SWWM_ACHIEVEMENT_CANDY_TAG = "Oh un Caramelo";
SWWM_ACHIEVEMENT_CANDY_TXT = "Recoge %d pepitas";
SWWM_ACHIEVEMENT_CHEAT_TAG = "Astucia Mishe";
SWWM_ACHIEVEMENT_CHEAT_TXT = "Consigue meter un código de trampa";
SWWM_ACHIEVEMENT_CLIFFYB_TAG = "Mierdas de Recadero";
SWWM_ACHIEVEMENT_CLIFFYB_TXT = "Termina un mapa sin obtener ninguna llave";
SWWM_ACHIEVEMENT_CLONK_TAG = "CLONK";
@ -1084,6 +1082,8 @@ SWWM_ACHIEVEMENT_LOVE_TAG = "Amor Letal";
SWWM_ACHIEVEMENT_LOVE_TXT = "Mata %d enemigos con un beso lanzado (sin contar insta-kills)";
SWWM_ACHIEVEMENT_MASHIRO_TAG = "Capas de Blanco";
SWWM_ACHIEVEMENT_MASHIRO_TXT = "Invoca a la Dama Blanca";
SWWM_ACHIEVEMENT_MATRYOSHKA_TAG = "Astucia Mishe";
SWWM_ACHIEVEMENT_MATRYOSHKA_TXT = "Una Caja Afortunada dentro de una Caja Afortunada dentro de una Caja Afortunada dentro de una Caja Afortunada dentro de...";
SWWM_ACHIEVEMENT_MBF_TAG = "Puedes Acariciar el Perro";
SWWM_ACHIEVEMENT_MBF_TXT = "Acaricia un perro";
SWWM_ACHIEVEMENT_MEGA_TAG = "Masacre Masiva";

View file

@ -1,3 +1,3 @@
[default]
SWWM_MODVER="\chSWWM \czGZ\c- \cw1.2pre r105 \cu(Sat 8 Jan 17:52:30 CET 2022)\c-";
SWWM_SHORTVER="\cw1.2pre r105 \cu(2022-01-08 17:52:30)\c-";
SWWM_MODVER="\chSWWM \czGZ\c- \cw1.2pre r106 \cu(Tue 11 Jan 02:18:38 CET 2022)\c-";
SWWM_SHORTVER="\cw1.2pre r106 \cu(2022-01-11 02:18:38)\c-";

View file

@ -448,7 +448,6 @@ extend Class SWWMHandler
StatusBar.AttachMessage(m,-1232);
CVar.FindCVar('swwm_oldcheat').SetBool(true);
}
SWWMUtility.MarkAchievement("cheat",players[consoleplayer]);
if ( SWWMUtility.CheatsDisabled(consoleplayer) )
{
kfail = true;

View file

@ -5,8 +5,9 @@ Class SWWMAchievementInfo
int baseindex; // sorting order in the list
String basename; // base name for identifying achievement
TextureID icon; // our icon
int maxval; // maximum value (0: no progress, -1: special scripting needed)
int maxval; // maximum value (0: no progress)
bool hasformat; // achievement description text must be formatted to add maxval
bool bitfield; // progress is tracked as a bitfield
int state, val; // set by other scripts, used to avoid excessive dictionary lookups
}
@ -23,16 +24,35 @@ extend Class SWWMStaticHandler
if ( a.maxval )
{
int prog = achievementprogress.At(a.basename).ToInt();
// special cases
if ( val && (prog < a.maxval) )
// special case for bitfields
if ( a.bitfield )
{
achievementstate.Insert(a.basename,"0");
val = 0;
int pb = 0;
for ( int i=0; i<a.maxval; i++ )
pb += !!(prog&(1<<i));
if ( val && (pb < a.maxval) )
{
achievementstate.Insert(a.basename,"0");
val = 0;
}
else if ( !val && (pb >= a.maxval) )
{
achievementstate.Insert(a.basename,"1");
val = 1;
}
}
else if ( !val && (prog >= a.maxval) )
else
{
achievementstate.Insert(a.basename,"1");
val = 1;
if ( val && (prog < a.maxval) )
{
achievementstate.Insert(a.basename,"0");
val = 0;
}
else if ( !val && (prog >= a.maxval) )
{
achievementstate.Insert(a.basename,"1");
val = 1;
}
}
}
if ( (val == 1) && (gametic > lastachievementnotify) )
@ -118,8 +138,8 @@ extend Class SWWMStaticHandler
// fallback icon if one is not found
if ( !ac.icon.IsValid() ) ac.icon = TexMan.CheckForTexture("graphics/Achievements/DefaultAchievement.png",TexMan.Type_Any);
ac.maxval = ln[1].ToInt();
// special case for maxval
if ( ac.maxval && (ac.basename == "allcoll") )
// special cases for maxval == -1 (currently only one, so this is simplified)
if ( (ac.maxval == -1) && (ac.basename == "allcoll") )
{
int nc = 0;
for ( int i=0; i<AllActorClasses.Size(); i++ )
@ -133,6 +153,12 @@ extend Class SWWMStaticHandler
}
ac.maxval = nc;
}
// bitfield
else if ( ac.maxval < -1 )
{
ac.bitfield = true;
ac.maxval = abs(ac.maxval);
}
ac.hasformat = (ln[2]~=="yes");
achievements.Push(ac);
bidx++;
@ -210,7 +236,7 @@ extend Class SWWMStaticHandler
}
achievementprogress.Insert(keys[i].Left(colon),keys[i].Mid(colon+1));
}
// load achievement info and trim any bogus keys
// load achievement info and trim any bogus keys, as well as adding any new ones that are missing
ParseAchievementList(achievementinfo);
let di = DictionaryIterator.Create(achievementstate);
while ( di.Next() )
@ -223,6 +249,11 @@ extend Class SWWMStaticHandler
deleteme = false;
break;
}
if ( deleteme )
{
if ( developer >= 2 ) Console.Printf("Deleting bogus achievement state %s = %s",key,di.Value());
achievementstate.Remove(key);
}
}
di = DictionaryIterator.Create(achievementprogress);
while ( di.Next() )
@ -236,6 +267,24 @@ extend Class SWWMStaticHandler
deleteme = false;
break;
}
if ( deleteme )
{
if ( developer >= 2 ) Console.Printf("Deleting bogus achievement progress %s = %s",key,di.Value());
achievementprogress.Remove(key);
}
}
for ( int i=0; i<achievementinfo.Size(); i++ )
{
if ( achievementstate.At(achievementinfo[i].basename) == "" )
{
if ( developer >= 2 ) Console.Printf("Adding missing achievement state %s",achievementinfo[i].basename);
achievementstate.Insert(achievementinfo[i].basename,"0");
}
if ( achievementinfo[i].maxval && (achievementprogress.At(achievementinfo[i].basename) == "") )
{
if ( developer >= 2 ) Console.Printf("Adding missing achievement progress %s",achievementinfo[i].basename);
achievementprogress.Insert(achievementinfo[i].basename,"0");
}
}
}

View file

@ -798,10 +798,10 @@ Class CBoxLight : SpotLightAttenuated
Destroy();
return;
}
Vector2 ofs = ((special1<2)?8:-8,(special1%2)?12:-12);
Vector2 ofs = ((special1<2)?8:-8,(special1%2)?12:-12)*target.scale.x;
double ang = (special1<2)?0:180;
angle = target.angle+ang;
SetOrigin(target.Vec3Offset(ofs.x*cos(target.angle)-ofs.y*sin(target.angle),ofs.y*cos(target.angle)+ofs.x*sin(target.angle),10),true);
SetOrigin(target.Vec3Offset(ofs.x*cos(target.angle)-ofs.y*sin(target.angle),ofs.y*cos(target.angle)+ofs.x*sin(target.angle),10*target.scale.y),true);
}
}
@ -883,9 +883,9 @@ Class Chancebox : Actor
break;
}
if ( cz < bceil ) bceil = cz;
if ( cz-fz < 56 ) continue; // too short
if ( cz-fz < 60 ) continue; // too short
bool blockedff = false;
BlockThingsIterator bt = BlockThingsIterator.CreateFromPos(testpos.x,testpos.y,fz,56,256,false);
BlockThingsIterator bt = BlockThingsIterator.CreateFromPos(testpos.x,testpos.y,fz,60,256,false);
while ( bt.Next() )
{
if ( !bt.Thing ) continue;
@ -901,10 +901,10 @@ Class Chancebox : Actor
spots.Push(sp);
}
// spawn at the real floor
if ( bceil-testpos.z < 56 ) continue; // too short
if ( bceil-testpos.z < 60 ) continue; // too short
// don't spawn on sky or hurtfloors if there are 3D floors
if ( (nffloor > 0) && ((s.GetTexture(0) == skyflatnum) || (s.damageamount > 0)) ) continue;
BlockThingsIterator bt = BlockThingsIterator.CreateFromPos(testpos.x,testpos.y,testpos.z,56,256,false);
BlockThingsIterator bt = BlockThingsIterator.CreateFromPos(testpos.x,testpos.y,testpos.z,60,256,false);
while ( bt.Next() )
{
if ( !bt.Thing ) continue;
@ -954,7 +954,19 @@ Class Chancebox : Actor
if ( (candidates.Size() <= 0) || invoker.dud )
{
// no candidates? just burst into treats
if ( Random[Chancebox](0,1) )
if ( (scale.x > .5) && (Random[Chancebox](0,int(9*scale.x*scale.x)) < 3) )
{
// spawn another smaller chancebox
// (chance increases for the inner box, up until a scale factor of 50% is reached)
let a = Spawn("Chancebox",pos+(0,0,3*scale.y));
a.bDROPPED = false;
a.bNOGRAVITY = false;
a.vel.z = FRandom[Chancebox](2,4);
a.angle = angle;
a.scale *= scale.x-.125;
if ( target && (a.scale.x <= .5) ) SWWMUtility.MarkAchievement("matryoshka",target.player);
}
else if ( Random[Chancebox](0,1) )
{
Class<Inventory> vipammodrop = null;
if ( SWWMUtility.ItemExists("Ynykron") && Random[Chancebox](0,1) ) vipammodrop = "YnykronAmmo";
@ -1109,14 +1121,14 @@ Class Chancebox : Actor
}
action void A_Confetti()
{
A_StartSound("misc/tada",CHAN_ITEM);
A_StartSound("misc/tada",CHAN_ITEM,pitch:1./scale.x);
double ang, pt;
int numpt = Random[ExploS](100,120);
for ( int i=0; i<numpt; i++ )
{
ang = FRandom[ExploS](0,360);
pt = FRandom[ExploS](-90,30);
let c = Spawn("FancyConfetti",Vec3Offset(0,0,16));
let c = Spawn("FancyConfetti",Vec3Offset(0,0,16*scale.y));
c.vel = (cos(pt)*cos(ang),cos(pt)*sin(ang),-sin(pt))*FRandom[ExploS](2,8);
}
}
@ -1209,6 +1221,7 @@ Class Chancebox : Actor
SWWMLoreLibrary.Add(user.player,"Chancebox");
specialf2 = AngleTo(user);
SetStateLabel("PreActive");
target = user;
return true;
}
override void PostBeginPlay()
@ -1230,6 +1243,7 @@ Class Chancebox : Actor
dud = true;
break;
}
A_SetSize(default.radius*scale.x,default.height*scale.y);
}
Default
{
@ -1241,6 +1255,7 @@ Class Chancebox : Actor
+SOLID;
+INTERPOLATEANGLES;
+COUNTITEM;
+CANPASS;
Species "Chancebox";
}
States
@ -1263,7 +1278,7 @@ Class Chancebox : Actor
{
angle = specialf2;
specialf1 = angle;
A_StartSound("misc/drumroll",CHAN_WEAPON);
A_StartSound("misc/drumroll",CHAN_WEAPON,pitch:1./scale.x);
}
XZW1 A 1
{
@ -1272,23 +1287,26 @@ Class Chancebox : Actor
pitch = FRandom[Chancebox](-5,5);
roll = FRandom[Chancebox](-5,5);
special1++;
return A_JumpIf(special1>40,"BlowUp");
return A_JumpIf(special1>int(40*scale.x),"BlowUp");
}
Wait;
BlowUp:
XZW2 A 1
{
A_SetSize(12,3);
A_SetSize(default.radius*scale.x,2.5*scale.y);
A_QuakeEx(2,2,2,9,0,500,"",QF_RELATIVE|QF_SCALEDOWN,falloff:200,rollIntensity:.2);
A_StartSound("chancebox/explode",CHAN_VOICE);
A_StartSound("chancebox/explode",CHAN_VOICE,pitch:1./scale.x);
angle = specialf1;
pitch = roll = 0;
let t = Spawn("ChanceboxTop",Vec3Offset(0,0,20));
let t = Spawn("ChanceboxTop",Vec3Offset(0,0,20*scale.y));
t.angle = angle;
let s1 = Spawn("ChanceboxSide",level.Vec3Offset(pos,(RotateVector((12,0),angle+90),0)));
t.scale = scale;
let s1 = Spawn("ChanceboxSide",level.Vec3Offset(pos,(RotateVector((12*scale.x,0),angle+90),0)));
s1.angle = angle+90;
let s2 = Spawn("ChanceboxSide",level.Vec3Offset(pos,(RotateVector((12,0),angle-90),0)));
s1.scale = scale;
let s2 = Spawn("ChanceboxSide",level.Vec3Offset(pos,(RotateVector((12*scale.x,0),angle-90),0)));
s2.angle = angle-90;
s2.scale = scale;
A_DropSomething();
}
XZW2 BCDEFGHIJKLMNO 1;
@ -1312,6 +1330,7 @@ Class ChanceboxTop : Actor
{
Super.PostBeginPlay();
vel = (0,0,20);
A_SetSize(default.radius*scale.x,default.height*scale.y);
}
States
{
@ -1347,6 +1366,7 @@ Class ChanceboxSide : Actor
{
Super.PostBeginPlay();
vel = (cos(angle)*cos(pitch),sin(angle)*cos(pitch),-sin(pitch))*20;
A_SetSize(default.radius*scale.x,default.height*scale.y);
}
States
{

View file

@ -91,7 +91,13 @@ Class SWWMAchievementMenu : GenericMenu
if ( a.maxval && (!ShouldObscure || hasprogress) )
{
yy += (newsmallfont.GetHeight()+2)*CleanYFac_1;
int val = a.val;
int val = 0;
if ( a.bitfield )
{
for ( int i=0; i<a.maxval; i++ )
val += !!(a.val&(1<<i));
}
else val = a.val;
val = clamp(val,0,a.maxval);
if ( completed ) str = String.Format("%s / %s",SWWMUtility.ThousandsNum(a.maxval),SWWMUtility.ThousandsNum(a.maxval));
else str = String.Format("%s / %s",SWWMUtility.ThousandsNum(val),SWWMUtility.ThousandsNum(a.maxval));

View file

@ -98,6 +98,20 @@ Class SWWMUtility
}
hnd.achievementprogress.Insert(pvar,String.Format("%g",pval.ToDouble()+inc));
}
// for bitfields
static clearscope void AchievementProgressOr( String pvar, int val, PlayerInfo p = null )
{
if ( !p || (p != players[consoleplayer]) ) return;
let hnd = SWWMStaticHandler(StaticEventHandler.Find("SWWMStaticHandler"));
if ( !hnd ) return;
String pval = hnd.achievementprogress.At(pvar);
if ( pval == "" )
{
if ( developer >= 2 ) Console.Printf("AchievementProgress: achievement '"..pvar.."' not found");
return;
}
hnd.achievementprogress.Insert(pvar,String.Format("%d",pval.ToInt()|val));
}
// gets the names of all mod cvars
static clearscope void GetCVars( out Array<String> cvarlist )