From 1066dfb75a2e07bc860ef737f6a8339bea501835 Mon Sep 17 00:00:00 2001 From: haranp Date: Sat, 23 Dec 2006 18:30:26 +0000 Subject: Implemented David Ploog's "Lucy" abyssal god. An altar has a 20% chance of showing up in each Abyss area. Many things still need to be done: better naming, better descriptive text, differentiation from Makhleb in preferences, etc. Playtesting and debugging required. Breaks savefiles. git-svn-id: https://crawl-ref.svn.sourceforge.net/svnroot/crawl-ref/trunk@698 c06c8d41-db1a-0410-9941-cceddc491573 --- crawl-ref/source/abl-show.cc | 53 +++++++++++++++- crawl-ref/source/abyss.cc | 5 ++ crawl-ref/source/describe.cc | 8 +++ crawl-ref/source/direct.cc | 2 + crawl-ref/source/enum.h | 6 ++ crawl-ref/source/items.cc | 3 + crawl-ref/source/misc.cc | 2 +- crawl-ref/source/religion.cc | 140 +++++++++++++++++++++++++++++++++++-------- crawl-ref/source/travel.cc | 1 + crawl-ref/source/view.cc | 9 +++ 10 files changed, 202 insertions(+), 27 deletions(-) diff --git a/crawl-ref/source/abl-show.cc b/crawl-ref/source/abl-show.cc index 39688e93e4..f55de41952 100644 --- a/crawl-ref/source/abl-show.cc +++ b/crawl-ref/source/abl-show.cc @@ -127,7 +127,11 @@ ability_type god_abilities[MAX_NUM_GODS][MAX_GOD_ABILITIES] = // Elyvilon { ABIL_ELYVILON_LESSER_HEALING, ABIL_ELYVILON_PURIFICATION, ABIL_ELYVILON_HEALING, ABIL_ELYVILON_RESTORATION, - ABIL_ELYVILON_GREATER_HEALING } + ABIL_ELYVILON_GREATER_HEALING }, + // Lucy + { ABIL_LUCY_ABYSS_EXIT, ABIL_NON_ABILITY, + ABIL_LUCY_SUMMON_DEMONS, ABIL_NON_ABILITY, + ABIL_LUCY_ABYSS_ENTER } }; // The description screen was way out of date with the actual costs. @@ -251,6 +255,11 @@ static const struct ability_def Ability_List[] = { ABIL_ELYVILON_RESTORATION, "Restoration", 3, 0, 400, 3, ABFLAG_NONE }, { ABIL_ELYVILON_GREATER_HEALING, "Greater Healing", 6, 0, 600, 4, ABFLAG_NONE }, + // Lucy + { ABIL_LUCY_ABYSS_EXIT, "Depart the Abyss", 0, 0, 100, 10, ABFLAG_PAIN }, + { ABIL_LUCY_SUMMON_DEMONS, "Summon Abyssal Servants", 7, 0, 100, 5, ABFLAG_NONE }, + { ABIL_LUCY_ABYSS_ENTER, "Enter the Abyss", 9, 0, 200, 40, ABFLAG_NONE }, + // These six are unused "evil" god abilities: { ABIL_CHARM_SNAKE, "Charm Snake", 6, 0, 200, 5, ABFLAG_NONE }, { ABIL_TRAN_SERPENT_OF_HELL, "Turn into Demonic Serpent", 16, 0, 600, 8, ABFLAG_NONE }, @@ -1173,6 +1182,48 @@ bool activate_ability(void) exercise( SK_INVOCATIONS, 6 + random2(10) ); break; + case ABIL_LUCY_ABYSS_EXIT: + if ( you.level_type != LEVEL_ABYSS ) + { + mpr("You aren't in the Abyss!"); + return false; // don't incur costs + } + banished(DNGN_EXIT_ABYSS); + exercise(SK_INVOCATIONS, 8 + random2(10)); + + // Lose 1d2 permanent HP + you.hp_max -= (coinflip() ? 2 : 1); + // Deflate HP + set_hp( 1 + random2(you.hp), false ); + + // Lose 1d2 permanent MP + rot_mp(coinflip() ? 2 : 1); + // Deflate MP + if (you.magic_points) + set_mp(random2(you.magic_points), false); + break; + + case ABIL_LUCY_SUMMON_DEMONS: + for ( int i = 0; i < you.skills[SK_INVOCATIONS] / 4; ++i ) + summon_ice_beast_etc( 20 + you.skills[SK_INVOCATIONS] * 3, + summon_any_demon(DEMON_COMMON), true); + exercise(SK_INVOCATIONS, 6 + random2(6)); + break; + + case ABIL_LUCY_ABYSS_ENTER: + if (you.level_type == LEVEL_ABYSS) + { + mpr("You're already here."); + return false; + } + else if (you.level_type == LEVEL_PANDEMONIUM) + { + mpr("That doesn't work from Pandemonium."); + return false; + } + banished(DNGN_ENTER_ABYSS); + break; + //jmf: intended as invocations from evil god(s): case ABIL_CHARM_SNAKE: cast_snake_charm( you.experience_level * 2 diff --git a/crawl-ref/source/abyss.cc b/crawl-ref/source/abyss.cc index be6701534c..dd915a3e80 100644 --- a/crawl-ref/source/abyss.cc +++ b/crawl-ref/source/abyss.cc @@ -47,6 +47,8 @@ void generate_abyss(void) } grd[45][35] = DNGN_FLOOR; + if ( one_chance_in(5) ) + grd[46][35] = DNGN_ALTAR_LUCY; } // end generate_abyss() @@ -379,4 +381,7 @@ void abyss_teleport( bool new_area ) generate_area( 10, 10, (GXM - 10), (GYM - 10) ); grd[you.x_pos][you.y_pos] = DNGN_FLOOR; + if ( one_chance_in(5) ) + grd[you.x_pos + 1][you.y_pos] = DNGN_ALTAR_LUCY; + } diff --git a/crawl-ref/source/describe.cc b/crawl-ref/source/describe.cc index 1514d69379..970ef8008c 100644 --- a/crawl-ref/source/describe.cc +++ b/crawl-ref/source/describe.cc @@ -6544,6 +6544,12 @@ void describe_god( int which_god, bool give_title ) "appreciates the offering of weapons. "; break; + case GOD_LUCY: + description = + "Lucy is a god who revels in the chaos of the Abyss. Followers are sent out" EOL + "to spread Lucy's word in the world. Lucy enjoys seeing her followers spread" EOL + "chaos and destruction."; + break; default: description = "God of Program Bugs is a weird and dangerous God and his presence should" EOL "be reported to dev-team."; @@ -6574,6 +6580,7 @@ void describe_god( int which_god, bool give_title ) (which_god == GOD_TROG) ? "Great Slayer" : (which_god == GOD_NEMELEX_XOBEH) ? "Great Trickster" : (which_god == GOD_SIF_MUNA) ? "Master of the Arcane" : + (which_god == GOD_LUCY) ? "Abyssal Lord" : (which_god == GOD_XOM) ? "Teddy Bear" : "Bogy the Lord of the Bugs"); // Xom and no god is handled before } @@ -6599,6 +6606,7 @@ void describe_god( int which_god, bool give_title ) case GOD_TROG: case GOD_NEMELEX_XOBEH: case GOD_ELYVILON: + case GOD_LUCY: cprintf ( (you.piety >= 120) ? "High Priest" : (you.piety >= 100) ? "Elder" : (you.piety >= 75) ? "Priest" : diff --git a/crawl-ref/source/direct.cc b/crawl-ref/source/direct.cc index 141e8f7444..d7a64cf136 100644 --- a/crawl-ref/source/direct.cc +++ b/crawl-ref/source/direct.cc @@ -1283,6 +1283,8 @@ std::string feature_description(int grid) return ("A sparkling altar of Nemelex Xobeh."); case DNGN_ALTAR_ELYVILON: return ("A silver altar of Elyvilon."); + case DNGN_ALTAR_LUCY: + return ("A corrupt altar of Lucy."); case DNGN_BLUE_FOUNTAIN: return ("A fountain of clear blue water."); case DNGN_SPARKLING_FOUNTAIN: diff --git a/crawl-ref/source/enum.h b/crawl-ref/source/enum.h index 51151c5d09..669ada8aaa 100644 --- a/crawl-ref/source/enum.h +++ b/crawl-ref/source/enum.h @@ -109,6 +109,10 @@ enum ability_type ABIL_ELYVILON_HEALING, ABIL_ELYVILON_RESTORATION, ABIL_ELYVILON_GREATER_HEALING, // 224 + ABIL_LUCY_ABYSS_EXIT, + ABIL_LUCY_SUMMON_DEMONS, + ABIL_LUCY_ABYSS_ENTER, + ABIL_CHARM_SNAKE, ABIL_TRAN_SERPENT_OF_HELL, ABIL_ROTTING, @@ -1032,6 +1036,7 @@ enum dungeon_feature_type DNGN_ALTAR_TROG, DNGN_ALTAR_NEMELEX_XOBEH, // 190 DNGN_ALTAR_ELYVILON, // 191 + DNGN_ALTAR_LUCY, DNGN_BLUE_FOUNTAIN = 200, // 200 DNGN_DRY_FOUNTAIN_I, @@ -1364,6 +1369,7 @@ enum god_type GOD_TROG, // 10 GOD_NEMELEX_XOBEH, GOD_ELYVILON, + GOD_LUCY, NUM_GODS, // always after last god GOD_RANDOM = 100 diff --git a/crawl-ref/source/items.cc b/crawl-ref/source/items.cc index 0923692ed3..ff433e3399 100644 --- a/crawl-ref/source/items.cc +++ b/crawl-ref/source/items.cc @@ -675,6 +675,9 @@ static void describe_floor() { case DNGN_ALTAR_ELYVILON: mpr("There is a silver altar of Elyvilon here."); break; + case DNGN_ALTAR_LUCY: + mpr("There is a corrupted altar of Lucy here."); + break; case DNGN_BLUE_FOUNTAIN: mpr("There is a fountain here (q to drink)."); break; diff --git a/crawl-ref/source/misc.cc b/crawl-ref/source/misc.cc index 40dd9c6224..d011d14d72 100644 --- a/crawl-ref/source/misc.cc +++ b/crawl-ref/source/misc.cc @@ -191,7 +191,7 @@ bool grid_destroys_items( int grid ) // returns 0 if grid is not an altar, else it returns the GOD_* type god_type grid_altar_god( unsigned char grid ) { - if (grid >= DNGN_ALTAR_ZIN && grid <= DNGN_ALTAR_ELYVILON) + if (grid >= DNGN_ALTAR_ZIN && grid <= DNGN_ALTAR_LUCY) return (static_cast( grid - DNGN_ALTAR_ZIN + 1 )); return (GOD_NO_GOD); diff --git a/crawl-ref/source/religion.cc b/crawl-ref/source/religion.cc index 74991b80c7..583a8a7dd8 100644 --- a/crawl-ref/source/religion.cc +++ b/crawl-ref/source/religion.cc @@ -75,6 +75,7 @@ const char *sacrifice[] = { " glows faintly for a moment, then is gone.", " is consumed in a roaring column of flame.", " glows with a rainbow of weird colours and disappears.", + " is consumed by the void.", " evaporates." }; @@ -107,8 +108,7 @@ const char* god_gain_power_messages[MAX_NUM_GODS][MAX_GOD_ABILITIES] = "drain ambient lifeforce", "control the undead" }, // Xom - { "", "", "", - "", "" }, + { "", "", "", "", "" }, // Vehumet { "gain power from killing in Vehumet's name", "You can call upon Vehumet to aid your destructive magics with prayer.", @@ -139,14 +139,19 @@ const char* god_gain_power_messages[MAX_NUM_GODS][MAX_GOD_ABILITIES] = "haste yourself", "" }, // Nemelex - { "", "", "", - "", "" }, + { "", "", "", "", "" }, // Elyvilon { "call upon Elyvilon for minor healing", "call upon Elyvilon for purification", "call upon Elyvilon for moderate healing", "call upon Elyvilon to restore your abilities", - "call upon Elyvilon for incredible healing" } + "call upon Elyvilon for incredible healing" }, + // Lucy + { "depart the Abyss - at a permanent cost", + "", + "summon the demons of the Abyss to your aid", + "", + "gate yourself to the Abyss" } }; const char* god_lose_power_messages[MAX_NUM_GODS][MAX_GOD_ABILITIES] = @@ -178,8 +183,7 @@ const char* god_lose_power_messages[MAX_NUM_GODS][MAX_GOD_ABILITIES] = "drain ambient lifeforce", "control the undead" }, // Xom - { "", "", "", - "", "" }, + { "", "", "", "", "" }, // Vehumet { "gain power from killing in Vehumet's name", "Vehumet will no longer aid your destructive magics.", @@ -211,14 +215,19 @@ const char* god_lose_power_messages[MAX_NUM_GODS][MAX_GOD_ABILITIES] = "haste yourself", "" }, // Nemelex - { "", "", "", - "", "" }, + { "", "", "", "", "" }, // Elyvilon { "call upon Elyvilon for minor healing", "call upon Elyvilon for purification", "call upon Elyvilon for moderate healing", "call upon Elyvilon to restore your abilities", - "call upon Elyvilon for incredible healing" } + "call upon Elyvilon for incredible healing" }, + // Lucy + { "depart the Abyss at will", + "", + "summon the demons of the Abyss to your aid", + "", + "gate yourself to the Abyss" } }; @@ -228,6 +237,16 @@ void divine_retribution(int god); void inc_penance(int god, int val); void inc_penance(int val); +static bool is_evil_god(int god) +{ + return + god == GOD_KIKUBAAQUDGHA || + god == GOD_MAKHLEB || + god == GOD_YREDELEMNUL || + god == GOD_VEHUMET || + god == GOD_LUCY; +} + void dec_penance(int god, int val) { if (you.penance[god] > 0) @@ -735,6 +754,9 @@ char *god_name( int which_god, bool long_name ) // mv - rewritten case GOD_ELYVILON: sprintf(godname_buff, "Elyvilon%s", long_name ? " the Healer" : ""); break; + case GOD_LUCY: + sprintf(godname_buff, "Lucy"); + break; default: sprintf(godname_buff, "The Buggy One (%d)", which_god); } @@ -1221,6 +1243,7 @@ bool did_god_conduct( int thing_done, int level ) case GOD_OKAWARU: case GOD_MAKHLEB: case GOD_TROG: + case GOD_LUCY: simple_god_message(" accepts your offering."); ret = true; if (random2(level + 10) > 5) @@ -1245,6 +1268,7 @@ bool did_god_conduct( int thing_done, int level ) case GOD_VEHUMET: case GOD_MAKHLEB: case GOD_TROG: + case GOD_LUCY: simple_god_message(" accepts your kill."); ret = true; if (random2(level + 18) > 5) @@ -1261,6 +1285,7 @@ bool did_god_conduct( int thing_done, int level ) case GOD_OKAWARU: case GOD_VEHUMET: case GOD_MAKHLEB: + case GOD_LUCY: simple_god_message(" accepts your kill."); ret = true; if (random2(level + 18) > 4) @@ -1313,6 +1338,7 @@ bool did_god_conduct( int thing_done, int level ) case GOD_KIKUBAAQUDGHA: case GOD_YREDELEMNUL: case GOD_MAKHLEB: + case GOD_LUCY: snprintf( info, INFO_SIZE, " accepts your %skill.", (thing_done == DID_KILL_ANGEL) ? "" : "collateral " ); @@ -1349,6 +1375,7 @@ bool did_god_conduct( int thing_done, int level ) case GOD_KIKUBAAQUDGHA: // note: reapers aren't undead case GOD_VEHUMET: case GOD_MAKHLEB: + case GOD_LUCY: simple_god_message(" accepts your collateral kill."); ret = true; if (random2(level + 10) > 5) @@ -1364,6 +1391,7 @@ bool did_god_conduct( int thing_done, int level ) case GOD_SHINING_ONE: case GOD_VEHUMET: case GOD_MAKHLEB: + case GOD_LUCY: simple_god_message(" accepts your collateral kill."); ret = true; if (random2(level + 10) > 5) @@ -1566,8 +1594,8 @@ void gain_piety(char pgn) } if ( you.piety > 160 && old_piety <= 160 && - (you.religion == GOD_SHINING_ONE || you.religion == GOD_ZIN) && - you.num_gifts[you.religion] == 0 ) + (you.religion == GOD_SHINING_ONE || you.religion == GOD_ZIN || + you.religion == GOD_LUCY) && you.num_gifts[you.religion] == 0 ) simple_god_message( " will now bless your weapon at an altar...once."); } @@ -1586,8 +1614,8 @@ void lose_piety(char pgn) if (!player_under_penance() && you.piety != old_piety) { if (you.piety <= 160 && old_piety > 160 && - (you.religion == GOD_SHINING_ONE || you.religion == GOD_ZIN) && - you.num_gifts[you.religion] == 0) + (you.religion == GOD_SHINING_ONE || you.religion == GOD_ZIN || + you.religion == GOD_LUCY) && you.num_gifts[you.religion] == 0) simple_god_message(" is no longer ready to bless your weapon."); for ( int i = 0; i < MAX_GOD_ABILITIES; ++i ) @@ -1651,8 +1679,7 @@ void divine_retribution( int god ) case GOD_SHINING_ONE: // daeva/smiting theme // Doesn't care unless you've gone over to evil/destructive gods - if (you.religion == GOD_KIKUBAAQUDGHA || you.religion == GOD_MAKHLEB - || you.religion == GOD_YREDELEMNUL || you.religion == GOD_VEHUMET) + if (is_evil_god(you.religion)) { if (coinflip()) { @@ -1704,8 +1731,7 @@ void divine_retribution( int god ) case GOD_ZIN: // angels/creeping doom theme: // Doesn't care unless you've gone over to evil - if (you.religion == GOD_KIKUBAAQUDGHA || you.religion == GOD_MAKHLEB - || you.religion == GOD_YREDELEMNUL || you.religion == GOD_VEHUMET) + if (is_evil_god(you.religion)) { if (random2(you.experience_level) > 7 && !one_chance_in(5)) { @@ -2074,6 +2100,42 @@ void divine_retribution( int god ) } break; + case GOD_LUCY: + // abyssal servant theme + if (random2(you.experience_level) > 7 && !one_chance_in(5)) + { + if (create_monster(MONS_GREEN_DEATH + random2(3), 0, + BEH_HOSTILE, you.x_pos, you.y_pos, + MHITYOU, 250) != -1) + { + simple_god_message(" sends a demon after you!", god); + } + else + { + simple_god_message("'s demon is unavoidably detained.", god); + } + } + else + { + success = false; + how_many = 1 + (you.experience_level / 7); + + for (loopy = 0; loopy < how_many; loopy++) + { + if (create_monster(MONS_NEQOXEC + random2(5), 0, BEH_HOSTILE, + you.x_pos, you.y_pos, MHITYOU, 250) != -1) + { + success = true; + } + } + + if (success) + simple_god_message(" sends minions to punish you.", god); + else + simple_god_message("'s minions fail to arrive.", god); + } + break; + case GOD_ELYVILON: // Elyvilon doesn't seek revenge default: return; @@ -2098,6 +2160,7 @@ void divine_retribution( int god ) } } + return; } // end divine_retribution() @@ -2155,7 +2218,7 @@ void excommunication(void) case GOD_TROG: simple_god_message( " does not appreciate desertion!", old_god ); - // Penence has to come before retribution to prevent "mollify" + // Penance has to come before retribution to prevent "mollify" inc_penance( old_god, 50 ); divine_retribution( old_god ); break; @@ -2166,12 +2229,21 @@ void excommunication(void) inc_penance( old_god, 50 ); break; - default: - inc_penance( old_god, 25 ); + case GOD_LUCY: + if ( you.level_type == LEVEL_DUNGEON ) + { + simple_god_message(" casts you back into the Abyss!", old_god); + banished(DNGN_ENTER_ABYSS); + } + inc_penance(old_god, 50); break; case GOD_ELYVILON: // never seeks revenge break; + + default: + inc_penance( old_god, 25 ); + break; } } // end excommunication() @@ -2204,8 +2276,9 @@ static bool bless_weapon( int god, int brand, int colour ) mprf( MSGCH_GOD, "Your weapon shines brightly!" ); simple_god_message( " booms: Use this gift wisely!" ); - // as currently only Zin and TSO do this is our permabrand effect: - holy_word( 100, true ); + if ( god != GOD_LUCY ) + holy_word( 100, true ); + delay(1000); return (true); @@ -2272,6 +2345,18 @@ void altar_prayer(void) } } + // Lucy blesses weapons with distortion + if (you.religion == GOD_LUCY + && !you.num_gifts[GOD_LUCY] + && !player_under_penance() + && you.piety > 160) + { + const int wpn = get_player_wielded_weapon(); + + if (wpn != -1 && get_weapon_brand(you.inv[wpn]) != SPWPN_DISTORTION) + bless_weapon(GOD_LUCY, SPWPN_DISTORTION, RED); + } + i = igrd[you.x_pos][you.y_pos]; while (i != NON_ITEM) { @@ -2424,8 +2509,7 @@ void god_pitch(unsigned char which_god) // Currently penance is just zeroed, this could be much more interesting. you.penance[you.religion] = 0; - if (you.religion == GOD_KIKUBAAQUDGHA || you.religion == GOD_YREDELEMNUL - || you.religion == GOD_VEHUMET || you.religion == GOD_MAKHLEB) + if (is_evil_god(you.religion)) { // Note: Using worshipped[] we could make this sort of grudge // permanent instead of based off of penance. -- bwr @@ -2435,6 +2519,10 @@ void god_pitch(unsigned char which_god) god_speaks(GOD_SHINING_ONE, "\"You will pay for your evil ways, mortal!\""); } } + + if ( you.religion == GOD_LUCY ) + gain_piety(20); // allow instant access to first power + redraw_skill( you.your_name, player_title() ); } // end god_pitch() @@ -2536,6 +2624,7 @@ void handle_god_time(void) break; case GOD_MAKHLEB: + case GOD_LUCY: if (one_chance_in(16)) lose_piety(1); if (you.piety < 1) @@ -2601,6 +2690,7 @@ char god_colour( char god ) //mv - added case GOD_MAKHLEB: case GOD_VEHUMET: case GOD_TROG: + case GOD_LUCY: return(LIGHTRED); case GOD_XOM: diff --git a/crawl-ref/source/travel.cc b/crawl-ref/source/travel.cc index 6045729661..5eea64dab9 100644 --- a/crawl-ref/source/travel.cc +++ b/crawl-ref/source/travel.cc @@ -601,6 +601,7 @@ void initialise_travel() traversable_terrain[DNGN_ALTAR_TROG] = traversable_terrain[DNGN_ALTAR_NEMELEX_XOBEH] = traversable_terrain[DNGN_ALTAR_ELYVILON] = + traversable_terrain[DNGN_ALTAR_LUCY] = traversable_terrain[DNGN_BLUE_FOUNTAIN] = traversable_terrain[DNGN_DRY_FOUNTAIN_I] = traversable_terrain[DNGN_SPARKLING_FOUNTAIN] = diff --git a/crawl-ref/source/view.cc b/crawl-ref/source/view.cc index 2cb97f2c26..83c88f5e8c 100644 --- a/crawl-ref/source/view.cc +++ b/crawl-ref/source/view.cc @@ -1709,6 +1709,7 @@ bool is_feature(int feature, int x, int y) { case DNGN_ALTAR_TROG: case DNGN_ALTAR_NEMELEX_XOBEH: case DNGN_ALTAR_ELYVILON: + case DNGN_ALTAR_LUCY: return true; default: return false; @@ -3049,6 +3050,14 @@ void init_feature_table( void ) Feature[i].seen_colour = LIGHTGREY; break; + case DNGN_ALTAR_LUCY: + Feature[i].colour = GREEN; + Feature[i].symbol = Options.char_table[ DCHAR_ALTAR ]; + Feature[i].notable = true; + Feature[i].map_colour = DARKGREY; + Feature[i].seen_colour = GREEN; + break; + case DNGN_BLUE_FOUNTAIN: Feature[i].colour = BLUE; Feature[i].symbol = Options.char_table[ DCHAR_FOUNTAIN ]; -- cgit v1.2.3-54-g00ecf