From ab0dd2de6e1d4244a47368ac0e6d7aecf3fc4dd7 Mon Sep 17 00:00:00 2001 From: Kara Alexandra Date: Tue, 8 Oct 2024 09:03:58 -0700 Subject: [PATCH] Preliminary implementation of twitch integration --- include/extern.h | 8 ++ src/allmain.c | 7 ++ src/potion.c | 1 - src/pray.c | 2 - src/twitch.c | 218 ++++++++++++++++++++++++++++++++++++++++++ sys/unix/Makefile.src | 10 +- 6 files changed, 238 insertions(+), 8 deletions(-) create mode 100644 src/twitch.c diff --git a/include/extern.h b/include/extern.h index 91fb732a5..59080ad54 100644 --- a/include/extern.h +++ b/include/extern.h @@ -2419,6 +2419,7 @@ extern const char *udeadinside(void); /* ### potion.c ### */ +extern long itimeout_incr(long, int); extern void set_itimeout(long *, long) NONNULLARG1; extern void incr_itimeout(long *, int) NONNULLARG1; extern void make_confused(long, boolean); @@ -2453,6 +2454,8 @@ extern void speed_up(long); extern boolean critically_low_hp(boolean); extern boolean stuck_in_wall(void); +extern int in_trouble(void); +extern void fix_worst_trouble(int); extern void desecrate_altar(boolean, aligntyp); extern int dosacrifice(void); extern boolean can_pray(boolean); @@ -3230,6 +3233,11 @@ extern void ignite_items(struct obj *) NO_NNARGS; extern void trap_ice_effects(coordxy x, coordxy y, boolean ice_is_melting); extern void trap_sanity_check(void); +/* ### twitch.c ### */ + +extern void open_twitch(void); +extern void check_twitch(void); + /* ### u_init.c ### */ extern void u_init(void); diff --git a/src/allmain.c b/src/allmain.c index 4d281fe7f..3247b10ad 100644 --- a/src/allmain.c +++ b/src/allmain.c @@ -105,6 +105,8 @@ moveloop_preamble(boolean resuming) invent is fully populated and the in_moveloop flag has been set */ if (iflags.perm_invent) update_inventory(); + + open_twitch(); } staticfn void @@ -505,6 +507,7 @@ moveloop_core(void) #ifdef MAIL ckmailstatus(); #endif + check_twitch(); rhack(0); } if (u.utotype) /* change dungeon level */ @@ -801,6 +804,10 @@ newgame(void) void welcome(boolean new_game) /* false => restoring an old game */ { +#ifndef NO_SIGNAL + (void) signal(SIGPIPE, SIG_IGN); +#endif + char buf[BUFSZ]; boolean currentgend = Upolyd ? u.mfemale : flags.female; diff --git a/src/potion.c b/src/potion.c index 68c9d165e..874c5940c 100644 --- a/src/potion.c +++ b/src/potion.c @@ -6,7 +6,6 @@ #include "hack.h" staticfn long itimeout(long); -staticfn long itimeout_incr(long, int); staticfn void ghost_from_bottle(void); staticfn int drink_ok(struct obj *); staticfn void peffect_restore_ability(struct obj *); diff --git a/src/pray.c b/src/pray.c index 0adf67129..774ac7847 100644 --- a/src/pray.c +++ b/src/pray.c @@ -7,9 +7,7 @@ staticfn int prayer_done(void); staticfn void maybe_turn_mon_iter(struct monst *); staticfn struct obj *worst_cursed_item(void); -staticfn int in_trouble(void); staticfn void fix_curse_trouble(struct obj *, const char *); -staticfn void fix_worst_trouble(int); staticfn void angrygods(aligntyp); staticfn void at_your_feet(const char *); staticfn void gcrownu(void); diff --git a/src/twitch.c b/src/twitch.c new file mode 100644 index 000000000..2236b0156 --- /dev/null +++ b/src/twitch.c @@ -0,0 +1,218 @@ +#include "hack.h" + +#include +#include +#include + +int twitch_in_fd = -1; +int twitch_out_fd = -1; +char twitch_buf[1024]; +int twitch_buf_size; +char infile[SAVESIZE + 3]; +char outfile[SAVESIZE + 4]; + +staticfn void process_twitch_cmd(char*); +staticfn boolean has_out_file(void); +staticfn void failed_effect(char*); +staticfn void successful_effect(char*); +staticfn void grant_object(char*); + +void open_twitch(void) { + snprintf(infile, SAVESIZE + 3, "%s.in", gs.SAVEF); + snprintf(outfile, SAVESIZE + 4, "%s.out", gs.SAVEF); + + twitch_in_fd = open(infile, O_RDONLY | O_NONBLOCK); + if (twitch_in_fd >= 0) { + twitch_out_fd = open(outfile, O_WRONLY | O_NONBLOCK); + } +} + +void check_twitch(void) { + if (twitch_in_fd < 0) { + return; + } + + int size; + size = read(twitch_in_fd, &twitch_buf[twitch_buf_size], 1024 - twitch_buf_size); + + if (size < 0) { + if (errno == EAGAIN) { + return; + } + + return; + } + + if (size > 0) { + twitch_buf_size += size; + + int i; + boolean done; + done = FALSE; + + while (!done) { + done = TRUE; + for (i = 0; i < twitch_buf_size; i++) { + if (twitch_buf[i] == '\r' || twitch_buf[i] == '\n') { + twitch_buf[i] = '\0'; + if (i > 0) { + process_twitch_cmd(twitch_buf); + } + int j; + for (j = 0; j + i + 1 < twitch_buf_size; j++) { + twitch_buf[j] = twitch_buf[j + i + 1]; + } + twitch_buf_size -= i + 1; + done = FALSE; + break; + } + } + } + } +} + +staticfn void process_twitch_cmd(char *cmd) { + char *id = strtok(cmd, " "); + char *effect = strtok(NULL, " "); + + if (effect == NULL) { + return; + } + + if (!strcmp(effect, "create_monster")) { + create_critters(1, NULL, FALSE); + successful_effect(id); + } else if (!strcmp(effect, "identify")) { + identify_pack(1, TRUE); + successful_effect(id); + } else if (!strcmp(effect, "teleport")) { + tele(); + successful_effect(id); + } else if (!strcmp(effect, "level_teleport")) { + if (u.uhave.amulet || In_endgame(&u.uz) || In_sokoban(&u.uz)) { + failed_effect(id); + } else { + level_tele(); + successful_effect(id); + } + } else if (!strcmp(effect, "healing")) { + You_feel("better."); + healup(8 + d(4, 4), 0, FALSE, FALSE); + successful_effect(id); + } else if (!strcmp(effect, "extra_healing")) { + You_feel("much better."); + healup(16 + d(4, 8), 0, FALSE, FALSE); + successful_effect(id); + } else if (!strcmp(effect, "full_healing")) { + You_feel("completely healed."); + healup(400, 0, FALSE, FALSE); + successful_effect(id); + } else if (!strcmp(effect, "curse_inventory")) { + if (!Blind) { + You("notice a %s glow surrounding you.", hcolor(NH_BLACK)); + } + rndcurse(); + successful_effect(id); + } else if (!strcmp(effect, "reduce_luck")) { + You_feel("unlucky."); + change_luck(-1); + successful_effect(id); + } else if (!strcmp(effect, "fix_trouble")) { + int trouble = in_trouble(); + if (trouble != 0) { + fix_worst_trouble(trouble); + successful_effect(id); + } else { + failed_effect(id); + } + } else if (!strcmp(effect, "darkness")) { + litroom(FALSE, NULL); + successful_effect(id); + } else if (!strcmp(effect, "confuse")) { + if (!Confusion) { + if (Hallucination) { + pline("What a trippy feeling!"); + } else { + pline("Huh, What? Where am I?"); + } + } + + make_confused(itimeout_incr(HConfusion, rn1(7, 16)), FALSE); + successful_effect(id); + } else if (!strcmp(effect, "hallucinate")) { + (void) make_hallucinated(itimeout_incr(HHallucination, d(10, 12)), TRUE, 0L); + successful_effect(id); + } else if (!strcmp(effect, "stun")) { + make_stunned(itimeout_incr(HStun, rn1(10, 10)), TRUE); + successful_effect(id); + } else if (!strcmp(effect, "blind")) { + make_blinded(itimeout_incr(BlindedTimeout, d(10, 12)), (boolean) !Blind); + successful_effect(id); + } else if (!strcmp(effect, "sleep")) { + if (Sleep_resistance) { + You("yawn."); + failed_effect(id); + } else { + You("suddenly fall asleep!"); + fall_asleep(-rn1(10, 13), TRUE); + successful_effect(id); + } + } else if (!strcmp(effect, "grant_object")) { + char *object = strtok(NULL, "\n"); + grant_object(object); + successful_effect(id); + } +} + +staticfn void grant_object(char *objname) { + struct obj *otmp; + otmp = readobjnam(objname, NULL); + if (!otmp) { + otmp = readobjnam((char *) 0, (struct obj *) 0); + } else if (otmp == &hands_obj) { + return; + } + + if (otmp->oartifact) { + pline("For a moment, you feel %s in your %s, but it disappears!", + something, makeplural(body_part(HAND))); + return; + } + + const char *verb = ((Is_airlevel(&u.uz) || u.uinwater) ? "slip" : "drop"), + *oops_msg = (u.uswallow + ? "Oops! %s out of your reach!" + : (Is_airlevel(&u.uz) || Is_waterlevel(&u.uz) + || levl[u.ux][u.uy].typ < IRONBARS + || levl[u.ux][u.uy].typ >= ICE) + ? "Oops! %s away from you!" + : "Oops! %s to the floor!"); + + (void) hold_another_object(otmp, oops_msg, The(aobjnam(otmp, verb)), (const char *) 0); +} + +staticfn boolean has_out_file(void) { + if (twitch_out_fd >= 0) { + return TRUE; + } + + twitch_out_fd = open(outfile, O_WRONLY | O_NONBLOCK); + + return twitch_out_fd >= 0; +} + +staticfn void failed_effect(char *id) { + if (has_out_file()) { + write(twitch_out_fd, "failure ", 8); + write(twitch_out_fd, id, strlen(id)); + write(twitch_out_fd, "\n", 1); + } +} + +staticfn void successful_effect(char *id) { + if (has_out_file()) { + write(twitch_out_fd, "success ", 8); + write(twitch_out_fd, id, strlen(id)); + write(twitch_out_fd, "\n", 1); + } +} diff --git a/sys/unix/Makefile.src b/sys/unix/Makefile.src index f21982f66..0b3086aa9 100644 --- a/sys/unix/Makefile.src +++ b/sys/unix/Makefile.src @@ -520,8 +520,8 @@ HACKCSRC = allmain.c alloc.c apply.c artifact.c attrib.c ball.c bones.c \ priest.c quest.c questpgr.c read.c rect.c region.c report.c restore.c \ rip.c rnd.c role.c rumors.c save.c selvar.c sfstruct.c \ shk.c shknam.c sit.c sounds.c sp_lev.c spell.c stairs.c steal.c steed.c \ - strutil.c symbols.c sys.c teleport.c \ - timeout.c topten.c track.c trap.c u_init.c utf8map.c \ + strutil.c symbols.c sys.c teleport.c timeout.c \ + topten.c track.c trap.c twitch.c u_init.c utf8map.c \ uhitm.c vault.c version.c vision.c weapon.c were.c wield.c \ windows.c wizard.c wizcmds.c worm.c worn.c write.c zap.c @@ -616,9 +616,9 @@ HOBJ = $(TARGETPFX)allmain.o $(TARGETPFX)alloc.o \ $(TARGETPFX)stairs.o $(TARGETPFX)symbols.o $(TARGETPFX)sys.o \ $(TARGETPFX)steal.o $(TARGETPFX)steed.o $(TARGETPFX)strutil.o \ $(TARGETPFX)teleport.o $(TARGETPFX)timeout.o $(TARGETPFX)topten.o \ - $(TARGETPFX)track.o $(TARGETPFX)trap.o $(TARGETPFX)u_init.o \ - $(TARGETPFX)uhitm.o $(TARGETPFX)utf8map.o $(TARGETPFX)vault.o \ - $(TARGETPFX)vision.o $(TARGETPFX)weapon.o \ + $(TARGETPFX)track.o $(TARGETPFX)trap.o $(TARGETPFX)twitch.o \ + $(TARGETPFX)u_init.o $(TARGETPFX)uhitm.o $(TARGETPFX)utf8map.o \ + $(TARGETPFX)vault.o $(TARGETPFX)vision.o $(TARGETPFX)weapon.o \ $(TARGETPFX)were.o $(TARGETPFX)wield.o $(TARGETPFX)windows.o \ $(TARGETPFX)wizard.o $(TARGETPFX)wizcmds.o $(TARGETPFX)worm.o \ $(TARGETPFX)worn.o $(TARGETPFX)write.o $(TARGETPFX)zap.o \