From 9efa73485b23fefe3ed6392e7640131d9683b391 Mon Sep 17 00:00:00 2001 From: Bryan Turner Date: Fri, 10 Feb 2023 20:33:59 -0500 Subject: [PATCH 1/6] Cursed items add long-duration debuf, then uncurse --- src/brogue/IO.c | 2 ++ src/brogue/Items.c | 3 +++ src/brogue/Rogue.h | 2 ++ src/brogue/Time.c | 14 ++++++++++++++ 4 files changed, 21 insertions(+) diff --git a/src/brogue/IO.c b/src/brogue/IO.c index 1c29a224..35df4ff5 100644 --- a/src/brogue/IO.c +++ b/src/brogue/IO.c @@ -4598,6 +4598,8 @@ short printMonsterInfo(creature *monst, short y, boolean dim, boolean highlight) "Lifespan", "Shielded", "Invisible", + "", + "Cursed", }; if (y >= ROWS - 1) { diff --git a/src/brogue/Items.c b/src/brogue/Items.c index bf6c8836..5fa30cb5 100644 --- a/src/brogue/Items.c +++ b/src/brogue/Items.c @@ -6900,6 +6900,7 @@ void readScroll(item *theItem) { } } if (hadEffect) { + player.status[STATUS_CURSED] = 0; message("your pack glows with a cleansing light, and a malevolent energy disperses.", 0); } else { message("your pack glows with a cleansing light, but nothing happens.", 0); @@ -7649,6 +7650,8 @@ boolean equipItem(item *theItem, boolean force, item *unequipHint) { break; } messageWithColor(buf1, &itemMessageColor, 0); + player.status[STATUS_CURSED] += CURSED_ITEM_DURATION; + player.maxStatus[STATUS_CURSED] = player.status[STATUS_CURSED]; } } diff --git a/src/brogue/Rogue.h b/src/brogue/Rogue.h index ab76237d..c3ec2135 100644 --- a/src/brogue/Rogue.h +++ b/src/brogue/Rogue.h @@ -182,6 +182,7 @@ typedef struct pos { #define WEAPON_KILLS_TO_AUTO_ID 20 #define ARMOR_DELAY_TO_AUTO_ID 1000 #define RING_DELAY_TO_AUTO_ID 1500 +#define CURSED_ITEM_DURATION 1000 #define FALL_DAMAGE_MIN 8 #define FALL_DAMAGE_MAX 10 @@ -1960,6 +1961,7 @@ enum statusEffects { STATUS_SHIELDED, STATUS_INVISIBLE, STATUS_AGGRAVATING, + STATUS_CURSED, NUMBER_OF_STATUS_EFFECTS, }; diff --git a/src/brogue/Time.c b/src/brogue/Time.c index 549ccdde..f3fda730 100644 --- a/src/brogue/Time.c +++ b/src/brogue/Time.c @@ -2104,6 +2104,7 @@ void autoRest() { || player.status[STATUS_NAUSEOUS] || player.status[STATUS_POISONED] || player.status[STATUS_DARKNESS] + || player.status[STATUS_CURSED] || initiallyEmbedded) && !rogue.disturbed) { while (i++ < TURNS_FOR_FULL_REGEN @@ -2113,6 +2114,7 @@ void autoRest() { || player.status[STATUS_NAUSEOUS] || player.status[STATUS_POISONED] || player.status[STATUS_DARKNESS] + || player.status[STATUS_CURSED] || cellHasTerrainFlag(player.loc.x, player.loc.y, T_OBSTRUCTS_PASSABILITY)) && !rogue.disturbed && (!initiallyEmbedded || cellHasTerrainFlag(player.loc.x, player.loc.y, T_OBSTRUCTS_PASSABILITY))) { @@ -2320,6 +2322,18 @@ void playerTurnEnded() { } } + // Countdown curse + if (player.status[STATUS_CURSED] > 0) { + player.status[STATUS_CURSED]--; + if (player.status[STATUS_CURSED] == 0) { + // When curse debuf ends, uncurse all equipment + if (rogue.weapon != NULL) rogue.weapon->flags &= ~ITEM_CURSED; + if (rogue.armor != NULL) rogue.armor->flags &= ~ITEM_CURSED; + if (rogue.ringLeft != NULL) rogue.ringLeft->flags &= ~ITEM_CURSED; + if (rogue.ringRight != NULL) rogue.ringRight->flags &= ~ITEM_CURSED; + } + } + if (player.status[STATUS_POISONED] > 0) { player.status[STATUS_POISONED]--; if (inflictDamage(NULL, &player, player.poisonAmount, &green, true)) { From 520c678f3612f33251693d478ef20c49ef7887d0 Mon Sep 17 00:00:00 2001 From: Bryan Turner Date: Fri, 24 Feb 2023 21:03:35 -0500 Subject: [PATCH 2/6] Changelog --- changes/issue-499.md | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 changes/issue-499.md diff --git a/changes/issue-499.md b/changes/issue-499.md new file mode 100644 index 00000000..6953da45 --- /dev/null +++ b/changes/issue-499.md @@ -0,0 +1,4 @@ +- + Cursed items cannot be unequipped for 1000 turns, or until a remove curse is used. + Curses also stack, so equipping two cursed items in quick succession would require + 2000 turns before either item can be unequipped. From 3214ed128ebb3fdf4c396e5489c83fdc3e0a46d6 Mon Sep 17 00:00:00 2001 From: Bryan Turner Date: Sun, 5 Mar 2023 21:58:26 -0500 Subject: [PATCH 3/6] Fix for uncursing items using different means --- src/brogue/Items.c | 30 ++++++++++++++++++++---------- 1 file changed, 20 insertions(+), 10 deletions(-) diff --git a/src/brogue/Items.c b/src/brogue/Items.c index a675f181..244168c1 100644 --- a/src/brogue/Items.c +++ b/src/brogue/Items.c @@ -6844,6 +6844,22 @@ void magicMapCell(short x, short y) { } } +boolean uncurse( item *theItem ) { + if (theItem->flags & ITEM_CURSED) { + + // Uncurse the item + theItem->flags &= ~ITEM_CURSED; + + // Also reduce curse duration if it is equipped + if (theItem->flags & ITEM_EQUIPPED) { + // Need to leave 1 tick on the status in case multiple cursed items contributed to the debuf + player.status[STATUS_CURSED] = max( 1, player.status[STATUS_CURSED] - CURSED_ITEM_DURATION ); + } + return true; + } + return false; +} + void readScroll(item *theItem) { short i, j, x, y, numberOfMonsters = 0; item *tempItem; @@ -6888,10 +6904,7 @@ void readScroll(item *theItem) { break; case SCROLL_REMOVE_CURSE: for (tempItem = packItems->nextItem; tempItem != NULL; tempItem = tempItem->nextItem) { - if (tempItem->flags & ITEM_CURSED) { - hadEffect = true; - tempItem->flags &= ~ITEM_CURSED; - } + hadEffect |= uncurse(tempItem); } if (hadEffect) { player.status[STATUS_CURSED] = 0; @@ -6973,10 +6986,9 @@ void readScroll(item *theItem) { itemName(theItem, buf, false, false, NULL); sprintf(buf2, "your %s gleam%s briefly in the darkness.", buf, (theItem->quantity == 1 ? "s" : "")); messageWithColor(buf2, &itemMessageColor, 0); - if (theItem->flags & ITEM_CURSED) { + if (uncurse(theItem)) { sprintf(buf2, "a malevolent force leaves your %s.", buf); messageWithColor(buf2, &itemMessageColor, 0); - theItem->flags &= ~ITEM_CURSED; } createFlare(player.loc.x, player.loc.y, SCROLL_ENCHANTMENT_LIGHT); break; @@ -6990,10 +7002,9 @@ void readScroll(item *theItem) { itemName(tempItem, buf2, false, false, NULL); sprintf(buf, "a protective golden light covers your %s.", buf2); messageWithColor(buf, &itemMessageColor, 0); - if (tempItem->flags & ITEM_CURSED) { + if (uncurse(tempItem)) { sprintf(buf, "a malevolent force leaves your %s.", buf2); messageWithColor(buf, &itemMessageColor, 0); - tempItem->flags &= ~ITEM_CURSED; } } else { message("a protective golden light surrounds you, but it quickly disperses.", 0); @@ -7007,10 +7018,9 @@ void readScroll(item *theItem) { itemName(tempItem, buf2, false, false, NULL); sprintf(buf, "a protective golden light covers your %s.", buf2); messageWithColor(buf, &itemMessageColor, 0); - if (tempItem->flags & ITEM_CURSED) { + if (uncurse(tempItem)) { sprintf(buf, "a malevolent force leaves your %s.", buf2); messageWithColor(buf, &itemMessageColor, 0); - tempItem->flags &= ~ITEM_CURSED; } if (rogue.weapon->quiverNumber) { rogue.weapon->quiverNumber = rand_range(1, 60000); From 0fb187c540a4ba9f47befe3ddf03d7f8821cf361 Mon Sep 17 00:00:00 2001 From: Bryan Turner Date: Sat, 18 May 2024 21:23:59 -0400 Subject: [PATCH 4/6] Add cursed status --- src/brogue/Rogue.h | 2 ++ src/variants/GlobalsBrogue.c | 1 + 2 files changed, 3 insertions(+) diff --git a/src/brogue/Rogue.h b/src/brogue/Rogue.h index a3fc0e63..a52e28a8 100644 --- a/src/brogue/Rogue.h +++ b/src/brogue/Rogue.h @@ -2000,6 +2000,7 @@ enum statusEffects { STATUS_SHIELDED, STATUS_INVISIBLE, STATUS_AGGRAVATING, + STATUS_CURSED, NUMBER_OF_STATUS_EFFECTS, }; @@ -2360,6 +2361,7 @@ typedef struct gameConstants { const int onHitHallucinateDuration; // duration of on-hit hallucination effect on player const int onHitWeakenDuration; // duration of on-hit weaken effect const int onHitMercyHealPercent; // percentage of damage healed on-hit by mercy weapon effect + const int curseDuration; // Duration of curses (additive) const int fallDamageMin; // minimum for fall damage range const int fallDamageMax; // maximum for fall damage range diff --git a/src/variants/GlobalsBrogue.c b/src/variants/GlobalsBrogue.c index bf0943a6..ee25931b 100644 --- a/src/variants/GlobalsBrogue.c +++ b/src/variants/GlobalsBrogue.c @@ -1034,6 +1034,7 @@ const gameConstants brogueGameConst = { .onHitHallucinateDuration = 20, .onHitWeakenDuration = 300, .onHitMercyHealPercent = 50, + .curseDuration = 1000, .weaponKillsToAutoID = 20, .armorDelayToAutoID = 1000, From 23e69f5bbc0aa69dff3fb9f43d659869fcfeada8 Mon Sep 17 00:00:00 2001 From: Bryan Turner Date: Sat, 18 May 2024 21:34:07 -0400 Subject: [PATCH 5/6] Revert "Variant one" --- changes/issue-499.md | 4 ---- src/brogue/IO.c | 2 -- src/brogue/Items.c | 13 +------------ src/brogue/Rogue.h | 2 -- src/brogue/Time.c | 14 -------------- src/variants/GlobalsBrogue.c | 1 - 6 files changed, 1 insertion(+), 35 deletions(-) delete mode 100644 changes/issue-499.md diff --git a/changes/issue-499.md b/changes/issue-499.md deleted file mode 100644 index 6953da45..00000000 --- a/changes/issue-499.md +++ /dev/null @@ -1,4 +0,0 @@ -- - Cursed items cannot be unequipped for 1000 turns, or until a remove curse is used. - Curses also stack, so equipping two cursed items in quick succession would require - 2000 turns before either item can be unequipped. diff --git a/src/brogue/IO.c b/src/brogue/IO.c index 7729d7f1..b1acd77e 100644 --- a/src/brogue/IO.c +++ b/src/brogue/IO.c @@ -4548,8 +4548,6 @@ short printMonsterInfo(creature *monst, short y, boolean dim, boolean highlight) "Lifespan", "Shielded", "Invisible", - "", - "Cursed", }; if (y >= ROWS - 1) { diff --git a/src/brogue/Items.c b/src/brogue/Items.c index d246a09d..608fdbca 100644 --- a/src/brogue/Items.c +++ b/src/brogue/Items.c @@ -6871,17 +6871,9 @@ static void magicMapCell(short x, short y) { } } -boolean uncurse( item *theItem ) { +static boolean uncurse( item *theItem ) { if (theItem->flags & ITEM_CURSED) { - - // Uncurse the item theItem->flags &= ~ITEM_CURSED; - - // Also reduce curse duration if it is equipped - if (theItem->flags & ITEM_EQUIPPED) { - // Need to leave 1 tick on the status in case multiple cursed items contributed to the debuf - player.status[STATUS_CURSED] = max( 1, player.status[STATUS_CURSED] - CURSED_ITEM_DURATION ); - } return true; } return false; @@ -6932,7 +6924,6 @@ void readScroll(item *theItem) { hadEffect |= uncurse(tempItem); } if (hadEffect) { - player.status[STATUS_CURSED] = 0; message("your pack glows with a cleansing light, and a malevolent energy disperses.", 0); } else { message("your pack glows with a cleansing light, but nothing happens.", 0); @@ -7692,8 +7683,6 @@ boolean equipItem(item *theItem, boolean force, item *unequipHint) { break; } messageWithColor(buf1, &itemMessageColor, 0); - player.status[STATUS_CURSED] += CURSED_ITEM_DURATION; - player.maxStatus[STATUS_CURSED] = player.status[STATUS_CURSED]; } } diff --git a/src/brogue/Rogue.h b/src/brogue/Rogue.h index a52e28a8..a3fc0e63 100644 --- a/src/brogue/Rogue.h +++ b/src/brogue/Rogue.h @@ -2000,7 +2000,6 @@ enum statusEffects { STATUS_SHIELDED, STATUS_INVISIBLE, STATUS_AGGRAVATING, - STATUS_CURSED, NUMBER_OF_STATUS_EFFECTS, }; @@ -2361,7 +2360,6 @@ typedef struct gameConstants { const int onHitHallucinateDuration; // duration of on-hit hallucination effect on player const int onHitWeakenDuration; // duration of on-hit weaken effect const int onHitMercyHealPercent; // percentage of damage healed on-hit by mercy weapon effect - const int curseDuration; // Duration of curses (additive) const int fallDamageMin; // minimum for fall damage range const int fallDamageMax; // maximum for fall damage range diff --git a/src/brogue/Time.c b/src/brogue/Time.c index 0be66d7f..8a2593e5 100644 --- a/src/brogue/Time.c +++ b/src/brogue/Time.c @@ -2100,7 +2100,6 @@ void autoRest() { || player.status[STATUS_NAUSEOUS] || player.status[STATUS_POISONED] || player.status[STATUS_DARKNESS] - || player.status[STATUS_CURSED] || initiallyEmbedded) && !rogue.disturbed) { @@ -2112,7 +2111,6 @@ void autoRest() { || player.status[STATUS_NAUSEOUS] || player.status[STATUS_POISONED] || player.status[STATUS_DARKNESS] - || player.status[STATUS_CURSED] || cellHasTerrainFlag(player.loc, T_OBSTRUCTS_PASSABILITY)) && !rogue.disturbed && (!initiallyEmbedded || cellHasTerrainFlag(player.loc, T_OBSTRUCTS_PASSABILITY))) { @@ -2320,18 +2318,6 @@ void playerTurnEnded() { } } - // Countdown curse - if (player.status[STATUS_CURSED] > 0) { - player.status[STATUS_CURSED]--; - if (player.status[STATUS_CURSED] == 0) { - // When curse debuf ends, uncurse all equipment - if (rogue.weapon != NULL) rogue.weapon->flags &= ~ITEM_CURSED; - if (rogue.armor != NULL) rogue.armor->flags &= ~ITEM_CURSED; - if (rogue.ringLeft != NULL) rogue.ringLeft->flags &= ~ITEM_CURSED; - if (rogue.ringRight != NULL) rogue.ringRight->flags &= ~ITEM_CURSED; - } - } - if (player.status[STATUS_POISONED] > 0) { player.status[STATUS_POISONED]--; if (inflictDamage(NULL, &player, player.poisonAmount, &green, true)) { diff --git a/src/variants/GlobalsBrogue.c b/src/variants/GlobalsBrogue.c index ee25931b..bf0943a6 100644 --- a/src/variants/GlobalsBrogue.c +++ b/src/variants/GlobalsBrogue.c @@ -1034,7 +1034,6 @@ const gameConstants brogueGameConst = { .onHitHallucinateDuration = 20, .onHitWeakenDuration = 300, .onHitMercyHealPercent = 50, - .curseDuration = 1000, .weaponKillsToAutoID = 20, .armorDelayToAutoID = 1000, From ba8af6a1de3795f16d6efbc2e13ec5aa179d07ba Mon Sep 17 00:00:00 2001 From: Bryan Turner Date: Mon, 27 May 2024 19:15:29 -0400 Subject: [PATCH 6/6] Experimental feature; screenshot of each level with items when using stairs. --- src/brogue/IO.c | 88 ++++++++++++++++++++++++++++++++++++ src/brogue/Items.c | 21 +++++++++ src/brogue/Movement.c | 6 +++ src/brogue/Rogue.h | 1 + src/platform/sdl2-platform.c | 4 +- 5 files changed, 119 insertions(+), 1 deletion(-) diff --git a/src/brogue/IO.c b/src/brogue/IO.c index b1acd77e..23d62722 100644 --- a/src/brogue/IO.c +++ b/src/brogue/IO.c @@ -3925,6 +3925,94 @@ void refreshSideBar(short focusX, short focusY, boolean focusedEntityMustGoFirst restoreRNG; } +void levelSummarySidebar() { + short printY, oldPrintY, i, j, x, y, displayEntityCount; + item *theItem = NULL; + char buf[COLS]; + const void *entityList[ROWS] = {0}; + enum entityDisplayTypes entityType[ROWS] = {0}; + char addedEntity[DCOLS][DROWS]; + short oldRNG; + + if (rogue.gameHasEnded || rogue.playbackFastForward) { + return; + } + + oldRNG = rogue.RNG; + rogue.RNG = RNG_COSMETIC; + //assureCosmeticRNG; + + printY = 0; + + zeroOutGrid(addedEntity); + + // Initialization. + displayEntityCount = 0; + for (i=0; inextItem; theItem != NULL && (displayEntityCount * 2) < ROWS; theItem = theItem->nextItem) { + if (!addedEntity[theItem->loc.x][theItem->loc.y] + && (pmap[theItem->loc.x][theItem->loc.y].flags & DISCOVERED) ) { + addedEntity[theItem->loc.x][theItem->loc.y] = true; + entityList[displayEntityCount] = theItem; + entityType[displayEntityCount] = EDT_ITEM; + displayEntityCount++; + } + } + + for (i=0; iloc.x; + y = ((item *) entityList[i])->loc.y; + + unsigned long flags = pmap[x][y].flags; + pmap[x][y].flags |= ANY_KIND_OF_VISIBLE; + + short a = tmap[x][y].light[0]; + short b = tmap[x][y].light[1]; + short c = tmap[x][y].light[2]; + + tmap[x][y].light[0] = tmap[player.loc.x][player.loc.y].light[0]; + tmap[x][y].light[1] = tmap[player.loc.x][player.loc.y].light[1]; + tmap[x][y].light[2] = tmap[player.loc.x][player.loc.y].light[2]; + + printY = printItemInfo((item *) entityList[i], + printY, + false, + false); + + pmap[x][y].flags = flags; + tmap[x][y].light[0] = a; + tmap[x][y].light[1] = b; + tmap[x][y].light[2] = c; + + // Only one line + printY--; + } + for (j=oldPrintY; joriginDepth = rogue.depthLevel; if (theItem->category & FOOD) { + pmap[0][0].machineNumber++; rogue.foodSpawned += foodTable[theItem->kind].power; if (D_MESSAGE_ITEM_GENERATION) printf("\n(:) Depth %i: generated food", rogue.depthLevel); } + // Track how many were generated so they can be displayed on the level memory screen + // TODO: Actually need to track how many were discovered, not generated, but this will do for now.. + if ((theItem->category & POTION) && (theItem->kind == POTION_LIFE)) { + pmap[1][0].machineNumber++; + } + + if ((theItem->category & POTION) && (theItem->kind == POTION_STRENGTH)) { + pmap[2][0].machineNumber++; + } + + if ((theItem->category & SCROLL) && (theItem->kind == SCROLL_ENCHANTING)) { + pmap[3][0].machineNumber++; + } + // Choose a placement location. pos itemPlacementLoc = INVALID_POS; if ((theItem->category & FOOD) || ((theItem->category & POTION) && theItem->kind == POTION_STRENGTH)) { diff --git a/src/brogue/Movement.c b/src/brogue/Movement.c index 1e0ab700..ebd901c5 100644 --- a/src/brogue/Movement.c +++ b/src/brogue/Movement.c @@ -2178,6 +2178,12 @@ boolean useStairs(short stairDirection) { boolean succeeded = false; //screenDisplayBuffer fromBuf, toBuf; + levelSummarySidebar(); + flashTemporaryAlert(" YOUR MEMORY OF THIS DEPTH ", 100); // Not sure why this is needed? Screen refresh delay? + if (takeScreenshot()) { + flashTemporaryAlert(" Screenshot saved in save directory ", 1000); + } + if (stairDirection == 1) { if (rogue.depthLevel < gameConst->deepestLevel) { rogue.cursorLoc = INVALID_POS; diff --git a/src/brogue/Rogue.h b/src/brogue/Rogue.h index a3fc0e63..07a743c8 100644 --- a/src/brogue/Rogue.h +++ b/src/brogue/Rogue.h @@ -2911,6 +2911,7 @@ extern "C" { char nextKeyPress(boolean textInput); void refreshSideBar(short focusX, short focusY, boolean focusedEntityMustGoFirst); + void levelSummarySidebar(void); void printHelpScreen(void); void displayFeatsScreen(void); void printDiscoveriesScreen(void); diff --git a/src/platform/sdl2-platform.c b/src/platform/sdl2-platform.c index 7181e126..d36095fa 100644 --- a/src/platform/sdl2-platform.c +++ b/src/platform/sdl2-platform.c @@ -416,7 +416,9 @@ static boolean _takeScreenshot() { // choose filename char screenshotFilepath[BROGUE_FILENAME_MAX]; - getAvailableFilePath(screenshotFilepath, "Screenshot", SCREENSHOT_SUFFIX); + char tryName[BROGUE_FILENAME_MAX]; + sprintf(tryName, "S%llu D%d T%d", (unsigned long long)rogue.seed, rogue.depthLevel, rogue.absoluteTurnNumber ); + getAvailableFilePath(screenshotFilepath, tryName, SCREENSHOT_SUFFIX); strcat(screenshotFilepath, SCREENSHOT_SUFFIX); // save to PNG