diff --git a/libnethack/dat/Potter.des b/libnethack/dat/Potter.des index f26df7e4..1188c852 100644 --- a/libnethack/dat/Potter.des +++ b/libnethack/dat/Potter.des @@ -38,5 +38,6 @@ STAIR:(74,10),up STAIR:(22,12),down #Potter MONSTER:'@',"Potter",(22,11) +MONSTER:'@',"rtp",(22,8) # LIGHT IT UP REGION: (0,0,74,20),lit,"delphi" diff --git a/libnethack/include/extern.h b/libnethack/include/extern.h index 277e9094..7d08f4a9 100644 --- a/libnethack/include/extern.h +++ b/libnethack/include/extern.h @@ -1392,7 +1392,7 @@ extern struct monst *split_mon(struct monst *, struct monst *); extern const char *bottlename(void); /* ### pray.c ### */ - +extern void gods_upset(aligntyp g_align); extern int dosacrifice(const struct nh_cmd_arg *); extern boolean can_pray(boolean); extern int dopray(const struct nh_cmd_arg *); diff --git a/libnethack/include/monflag.h b/libnethack/include/monflag.h index b371f87d..2a70a6f4 100644 --- a/libnethack/include/monflag.h +++ b/libnethack/include/monflag.h @@ -54,6 +54,7 @@ the above */ # define MS_WERE 38 /* lycanthrope in human form */ # define MS_BOAST 39 /* giants */ +# define MS_RTP 40 /* Root Type People */ # define MR_FIRE 0x01 /* resists fire */ diff --git a/libnethack/include/rtp.h b/libnethack/include/rtp.h new file mode 100644 index 00000000..50e35d32 --- /dev/null +++ b/libnethack/include/rtp.h @@ -0,0 +1,16 @@ +#ifndef RTP_H +#define RTP_H + +#include + +#include "extern.h" +#include "hack.h" +#include "monst.h" + +struct monst *name_rtp(struct monst *mtmp); + +void player_killed_rtp(struct level *lev); + +struct obj *create_rtp_corpse(struct level *lev, int x, int y, enum rng rng); + +#endif // RTP_H diff --git a/libnethack/src/makemon.c b/libnethack/src/makemon.c index 8cb5a76c..0e43d0cf 100644 --- a/libnethack/src/makemon.c +++ b/libnethack/src/makemon.c @@ -8,6 +8,7 @@ #include "emin.h" #include "edog.h" #include "eshk.h" +#include "rtp.h" #include "vault.h" #include @@ -1221,6 +1222,9 @@ makemon(const struct permonst *ptr, struct level *lev, int x, int y, if (!in_mklev && lev == level) newsym(mtmp->mx, mtmp->my); /* make sure the mon shows up */ + if (mndx == PM_RTP) { + mtmp = name_rtp(mtmp); + } return mtmp; } diff --git a/libnethack/src/mon.c b/libnethack/src/mon.c index bec10cf1..db3183b5 100644 --- a/libnethack/src/mon.c +++ b/libnethack/src/mon.c @@ -6,6 +6,7 @@ #include "hack.h" #include "mfndpos.h" #include "edog.h" +#include "rtp.h" #include static boolean restrap(struct monst *); @@ -301,6 +302,9 @@ make_corpse(struct monst *mtmp) obj = mksobj_at(SCR_BLANK_PAPER, level, x, y, TRUE, FALSE, rng_main); mtmp->mnamelth = 0; break; + case PM_RTP: + obj = create_rtp_corpse(level, x, y, rng_main); + break; default_1: default: if (mvitals[mndx].mvflags & G_NOCORPSE) @@ -318,7 +322,8 @@ make_corpse(struct monst *mtmp) if (flags.bypasses) bypass_obj(obj); - if (mtmp->mnamelth) + // RTPs drop the root password not a specialized item + if (mtmp->mnamelth && mndx != PM_RTP) obj = oname(obj, NAME(mtmp)); /* Avoid "It was hidden under a green mold corpse!" during Blind combat. An @@ -2108,13 +2113,18 @@ xkilled(struct monst *mtmp, int dest) cleanup: /* punish bad behaviour */ if (is_human(mdat) && (!always_hostile(mdat) && mtmp->malign <= 0) && - (mndx < PM_ARCHEOLOGIST || mndx > PM_WIZARD) && - u.ualign.type != A_CHAOTIC) { - HTelepat &= ~INTRINSIC; - change_luck(-2); - pline("You murderer!"); - if (Blind && !Blind_telepat) - see_monsters(FALSE); /* Can't sense monsters any more. */ + (mndx < PM_ARCHEOLOGIST || mndx > PM_WIZARD)) { + if (u.ualign.type != A_CHAOTIC) { + HTelepat &= ~INTRINSIC; + change_luck(-2); + pline("You murderer!"); + if (Blind && !Blind_telepat) + see_monsters(FALSE); /* Can't sense monsters any more. */ + } + // Even chaotic players get punishment for killing RTPs + if (mndx == PM_RTP) { + player_killed_rtp(level); + } } if ((mtmp->mpeaceful && !rn2(2)) || mtmp->mtame) change_luck(-1); diff --git a/libnethack/src/monst.c b/libnethack/src/monst.c index 1a756c35..f073013d 100644 --- a/libnethack/src/monst.c +++ b/libnethack/src/monst.c @@ -3489,6 +3489,14 @@ const struct permonst mons[] = { M1_HUMANOID | M1_OMNIVORE, M2_NOPOLY | M2_HUMAN | M2_PEACEFUL | M2_STRONG | M2_COLLECT | M2_MAGIC, M3_INFRAVISIBLE, HI_GUARDIAN), + MON("rtp", S_HUMAN, + LVL(10, 6, 5, 10, 0), G_NOGEN, + A(ATTK(AT_WEAP, AD_PHYS, 2, 6), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), + SIZ(WT_HUMAN, 400, 0, MS_RTP, MZ_HUMAN), 0, 0, + M1_HUMANOID | M1_OMNIVORE, + M2_NOPOLY | M2_HUMAN | M2_PEACEFUL | M2_STRONG | M2_COLLECT, + M3_INFRAVISIBLE, CLR_BLUE), /* * array terminator */ diff --git a/libnethack/src/pray.c b/libnethack/src/pray.c index 14ab38a4..75e86c0d 100644 --- a/libnethack/src/pray.c +++ b/libnethack/src/pray.c @@ -18,7 +18,6 @@ static void godvoice(aligntyp, const char *); static void god_zaps_you(aligntyp); static void fry_by_god(aligntyp); static void gods_angry(aligntyp); -static void gods_upset(aligntyp); static void consume_offering(struct obj *); static boolean water_prayer(boolean); static boolean blocked_boulder(int, int); @@ -1074,7 +1073,7 @@ gods_angry(aligntyp g_align) } /* The g_align god is upset with you. */ -static void +void gods_upset(aligntyp g_align) { if (g_align == u.ualign.type) diff --git a/libnethack/src/rtp.c b/libnethack/src/rtp.c new file mode 100644 index 00000000..2246847b --- /dev/null +++ b/libnethack/src/rtp.c @@ -0,0 +1,131 @@ +#include "rtp.h" + +typedef const struct { + const char* name; + const bool female; +} rtpEntry; + +rtpEntry rtpNames[] = { + {"Angelo DiNardi", false}, + {"Chris Lockfort", false}, + {"Dan Willemsen", false}, + {"Derek Gonyeo", false}, + {"Ethan House", false}, + {"Grant Cohoe", false}, + {"Jordan Rodgers", false}, + {"Kevin Thompson", false}, + {"Liam Middlebrook", false}, + {"McSaucy", false}, + {"Rob Glossop", false}, + {"Russ Harmon", false}, + {"Stephanie Miller", true}, + {"Steve Greene", false}, + {"Will Orr", false}, + {"William Dignazio", false}, +}; + +struct monst * +name_rtp(struct monst *mtmp) +{ + int rtp_id = rn2(sizeof(rtpNames) / sizeof(rtpEntry)); + + if (rtpNames[rtp_id].female) { + mtmp->female = TRUE; + } + + return christen_monst(mtmp, msg_from_string(rtpNames[rtp_id].name)); +} + +struct obj * +create_rtp_corpse(struct level *lev, int x, int y, enum rng rng) +{ + struct obj *obj = NULL; + struct obj *orig_obj = NULL; + // There should be a significant reward in order to tempt the player into + // trying to kill an RTP. Otherwise it's risk without reward. + + // In the future this should be a random selection from a slew of different + // items ranging in usefulness. + obj = mksobj_at(MAGIC_MARKER, lev, x, y, TRUE, FALSE, rng); + + + orig_obj = obj; + + // What else are RTPs good for :D + obj = oname(obj, "The Root Password"); + return obj; +} + +void +player_killed_rtp(struct level *lev) +{ + pline("You hear a faint whisper in the air: \"I'll shred your world\""); + + // get a large sample set + int random = rn2(100); + + // Enumerate through all the possibilities when the player kills an + // RTP + // + if (!(random / 10)) { + change_luck(-3); + } + + // 5% chance that the player hallucinates for a long while + if (!(random / 20)) { + make_hallucinated(rn2(420) + 50, TRUE); + } + + // 1% chance the player gets sick and dies after 42 turns + if (random == 42) { + make_sick(42, "Right before you killed that RTP 42 turns ago they gave you Heartbleed otyp == HELM_OF_OPPOSITE_ALIGNMENT) { + u.ualignbase[A_CURRENT] = new_align; + } else { + u.ualign.type = u.ualignbase[A_CURRENT] = new_align; + } + } + + // 25% chance that you anger your god + if(!(random / 4)) { + gods_upset(u.ualign.type); + } + + // 16.67% chance that player loses a level (if > 1) + if (!(random / 6) && u.ulevel > 1) { + losexp(NULL, FALSE); + } + + // 20% chance to spawn a random (suitable for the level) angry monster near + // the player + if (!(random / 5)) { + makemon(NULL, lev, u.ux, u.uy, MM_ANGRY); + } + + if (!(random / 10)) { + // Neat that this function already exists for our usage! + rndcurse(); + } + + if (!(random / 10)) { + if(uarmg) erode_obj(uarmg, NULL, ERODE_CORRODE, TRUE, TRUE); + } + + if (!(random / 15)) { + polyself(FALSE); + } +} diff --git a/libnethack/src/sounds.c b/libnethack/src/sounds.c index 59e7aafd..112a9ec2 100644 --- a/libnethack/src/sounds.c +++ b/libnethack/src/sounds.c @@ -810,6 +810,27 @@ domonnoise(struct monst *mtmp) else verbl_msg = "Who do you think you are, War?"; break; + case MS_RTP: + { + static const char *const rtp_foe_msg[] = { + "What about our imagine subscription?", + "I can ensure everything is down...", + "See if you can go to sleep now.", + }; + static const char *const rtp_pax_msg[] = { + "Not my server, not my problem.", + "You name it, we don't have to vote on it.", + "I can get it ALL on it!", + "Yeah but that definitely happened again.", + "Yeah I'd like to think about what we want", + "Why did MegaVM start VMs wtf!? Welp... shit shit shit WHO was last logged in?", + "I wasn't saturating the upload on 49net...", + "I can't remember how I fixed it.", + }; + verbl_msg = mtmp->mpeaceful ? rtp_pax_msg[rn2(sizeof(rtp_pax_msg))] + : rtp_foe_msg[rn2(sizeof(rtp_foe_msg))]; + } + break; } if (pline_msg)