Skip to content

Commit

Permalink
Cobalt Lichen Sac: charming lichen support monster
Browse files Browse the repository at this point in the history
Another possible member of later lichen bands, the spore sac is able to
charm you (and your allies) both with a spore melee attack, and via
spreading spore clouds released on death (reusing the plague shambler
cloud spreader again).

Spore clouds are even worse than regular charming as they can charm
you for *any* species in sight, not just that of the attacker (since
the original attacker has gone away, this was a mechanical necessity.)

An unused plant tile was repurposed for the placeholder.
  • Loading branch information
chucksellick committed Feb 5, 2025
1 parent db480eb commit a9b2e27
Show file tree
Hide file tree
Showing 13 changed files with 140 additions and 0 deletions.
1 change: 1 addition & 0 deletions crawl-ref/source/cloud-type.h
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ enum cloud_type
CLOUD_ELECTRICITY,
CLOUD_FAINT_MIASMA,
CLOUD_MAGNETISED_DUST,
CLOUD_SPORE,
NUM_CLOUD_TYPES,

// Random per-square.
Expand Down
33 changes: 33 additions & 0 deletions crawl-ref/source/cloud.cc
Original file line number Diff line number Diff line change
Expand Up @@ -332,6 +332,12 @@ static const cloud_data clouds[] = {
ETC_ELECTRICITY, // colour
{ TILE_CLOUD_MAGNETISED_DUST, CTVARY_RANDOM }, // tile
},
// CLOUD_SPORE,
{ "empathogenic spores", nullptr, // terse, verbose name
LIGHTCYAN, // colour
// XX: Make it CTVARY_DUR to see spores decreasing
{ TILE_CLOUD_SPORE, CTVARY_RANDOM }, // tile
},
};
COMPILE_CHECK(ARRAYSZ(clouds) == NUM_CLOUD_TYPES);

Expand Down Expand Up @@ -865,6 +871,7 @@ static bool _cloud_has_negative_side_effects(cloud_type cloud)
case CLOUD_ACID:
case CLOUD_MISERY:
case CLOUD_BLASTMOTES:
case CLOUD_SPORE:
return true;
default:
return false;
Expand Down Expand Up @@ -968,6 +975,8 @@ bool actor_cloud_immune(const actor &act, cloud_type type)
return act.res_polar_vortex();
case CLOUD_RAIN:
return !act.is_fiery();
case CLOUD_SPORE:
return act.is_unbreathing();
default:
return false;
}
Expand Down Expand Up @@ -1205,6 +1214,30 @@ static bool _actor_apply_cloud_side_effects(actor *act,
explode_blastmotes_at(cloud.pos);
return true;

case CLOUD_SPORE:
{
// Agent has died so don't even try to look them up:
// Choose a random hostile monster in LOS to be charmed by instead
// (yes, even if agent was friendly before dying)
monster *agent = choose_random_nearby_monster([](const monster& mon){
return mon.temp_attitude() == ATT_HOSTILE;
});

if (player)
{
if (!agent)
mpr("You experience the fleeting memory of a long forgotten friend.");
else
act->as_player()->charm(agent, 30);
}
else if (agent && ench_flavour_affects_monster(agent, BEAM_CHARM,
act->as_monster()))
{
enchant_actor_with_flavour(act->as_monster(), agent, BEAM_CHARM, 30);
}
}
break;

default:
break;
}
Expand Down
8 changes: 8 additions & 0 deletions crawl-ref/source/dat/descript/monsters.txt
Original file line number Diff line number Diff line change
Expand Up @@ -993,6 +993,14 @@ is that a jaw opening, and are those teeth?
It is able to compost the flesh of its victims to provide healing nutrients to
other lichen in its mycelial network.
%%%%
cobalt lichen sac

A carpet of colourful but toxic moss-like fungus found often in woodlands. From
it grows a squirming, oozing bulb of tiny spores.

It releases empathogenic spores when it attacks or a bigger cloud when it dies,
which can charm you or your allies.
%%%%
creeping inferno

A formless mass of ancient fire, bound by heat and malevolence. It longs to
Expand Down
21 changes: 21 additions & 0 deletions crawl-ref/source/dat/mons/cobalt-lichen-sac.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
name: "cobalt lichen sac"
glyph: {char: "f", colour: yellow}
flags: [amorphous, unblindable, no_skeleton, herd]
resists: {poison: 2, fire: -1}
species: cobalt_lichen
genus: fungus
holiness: [plant]
will: 100
attacks:
- {type: spore, flavour: charm, damage: 28}
hd: 13
hp_10x: 220
ac: 3
ev: 5
has_corpse: true
intelligence: human
habitat: walls
speed: 10
size: little
shape: orb
tile_variance: mod
1 change: 1 addition & 0 deletions crawl-ref/source/describe.cc
Original file line number Diff line number Diff line change
Expand Up @@ -5165,6 +5165,7 @@ static string _flavour_base_desc(attack_flavour flavour)
{ AF_VEX, "cause vexation" },
{ AF_SHED, "sheds detritus from its body" },
{ AF_COMPOST, "compost victims and heal allies with mycelia" },
{ AF_CHARM, "charm" },
{ AF_PLAIN, "" },
};

Expand Down
22 changes: 22 additions & 0 deletions crawl-ref/source/melee-attack.cc
Original file line number Diff line number Diff line change
Expand Up @@ -3845,6 +3845,28 @@ void melee_attack::mons_apply_attack_flavour()
alembic_brew_potion(*attacker->as_monster());
}

case AF_CHARM:
if (attk_type == AT_SPORE)
{
if (defender->is_unbreathing())
break;

if (defender_visible)
{
mprf("%s %s engulfed in a cloud of empathogenic spores!",
defender->name(DESC_THE).c_str(),
defender->conj_verb("are").c_str());
}
}
if (defender->is_player())
you.charm(attacker, 50);
else if (ench_flavour_affects_monster(attacker, BEAM_CHARM,
defender->as_monster()))
{
enchant_actor_with_flavour(defender, attacker, BEAM_CHARM, 50);
}
break;

case AF_SHED:
{
if (!defender->is_firewood())
Expand Down
37 changes: 37 additions & 0 deletions crawl-ref/source/mon-death.cc
Original file line number Diff line number Diff line change
Expand Up @@ -3115,6 +3115,43 @@ item_def* monster_die(monster& mons, killer_type killer,
treant_release_fauna(mons);
else if (mons.type == MONS_PHARAOH_ANT && real_death)
_pharaoh_ant_bind_souls(&mons);
else if (mons.type == MONS_COBALT_LICHEN_SAC && real_death && !mons.pacified())
{
// Since monster might be on a wall, find a valid origin for the clouds
coord_def source = mons.pos();
if (cell_is_solid(source))
{
vector<coord_def> valid_spots;
for (adjacent_iterator ai(mons.pos()); ai; ++ai)
{
if (!cell_is_solid(*ai) && you.see_cell_no_trans(*ai))
valid_spots.push_back(*ai);
}

if (!valid_spots.size())
for (adjacent_iterator ai(mons.pos()); ai; ++ai)
{
if (!cell_is_solid(*ai))
valid_spots.push_back(*ai);
}

if (valid_spots.size())
source = valid_spots[random2(valid_spots.size())];
}

// If there were no spots, monster must be somehow encased in rock
if (!cell_is_solid(source))
{
map_cloud_spreader_marker *marker =
new map_cloud_spreader_marker(source, CLOUD_SPORE, 5,
20 + random2(15), LOS_DEFAULT_RANGE,
5, &mons);
// Start the cloud at radius 1, regardless of the speed of the killing blow
marker->speed_increment -= you.time_taken;
env.markers.add(marker);
env.markers.clear_need_activate();
}
}
else if (!mons.is_summoned() && mummy_curse_power(mons.type) > 0)
{
// TODO: set attacker better? (Player attacker is handled by checking
Expand Down
1 change: 1 addition & 0 deletions crawl-ref/source/mon-enum.h
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,7 @@ enum attack_flavour
AF_VEX,
AF_SHED,
AF_COMPOST,
AF_CHARM,
};

// Non-spell "summoning" types to give to monster::mark_summoned(), or
Expand Down
1 change: 1 addition & 0 deletions crawl-ref/source/mon-pick-data.h
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,7 @@ static const vector<pop_entry> population[] =
{ 15, 25, 275, PEAK, MONS_WOLF_SPIDER },
{ 15, 25, 150, FALL, MONS_COBALT_LICHEN_EYESTALK },
{ 22, 26, 150, FALL, MONS_COBALT_LICHEN_MAW },
{ 23, 27, 150, FALL, MONS_COBALT_LICHEN_SAC },
{ 16, 24, 89, PEAK, MONS_REDBACK },
{ 16, 27, 92, PEAK, MONS_ICE_DRAGON },
{ 17, 23, 89, PEAK, MONS_VERY_UGLY_THING },
Expand Down
3 changes: 3 additions & 0 deletions crawl-ref/source/mon-place.cc
Original file line number Diff line number Diff line change
Expand Up @@ -2007,6 +2007,7 @@ static const map<monster_type, band_set> bands_by_leader = {
{ MONS_COBALT_LICHEN, { {}, {{ BAND_COBALT_LICHENS, {0, 1} }}}},
{ MONS_COBALT_LICHEN_EYESTALK, { {}, {{ BAND_COBALT_LICHENS, {0, 1} }}}},
{ MONS_COBALT_LICHEN_MAW, { {}, {{ BAND_COBALT_LICHENS, {0, 1} }}}},
{ MONS_COBALT_LICHEN_SAC, { {}, {{ BAND_COBALT_LICHENS, {0, 1} }}}},
};

static band_type _choose_band(monster_type mon_type, int *band_size_p,
Expand Down Expand Up @@ -2194,6 +2195,7 @@ static band_type _choose_band(monster_type mon_type, int *band_size_p,
case MONS_COBALT_LICHEN:
case MONS_COBALT_LICHEN_EYESTALK:
case MONS_COBALT_LICHEN_MAW:
case MONS_COBALT_LICHEN_SAC:
band_size = random_range(2 + env.absdepth0 / 5, 2 + env.absdepth0 / 2);
break;

Expand Down Expand Up @@ -2715,6 +2717,7 @@ static monster_type _band_member(band_type band, int which,
return random_choose_weighted(
20, MONS_COBALT_LICHEN_EYESTALK,
18, MONS_COBALT_LICHEN_MAW,
15, MONS_COBALT_LICHEN_SAC,
1, MONS_SLEEPCAP);
}
return MONS_COBALT_LICHEN;
Expand Down
2 changes: 2 additions & 0 deletions crawl-ref/source/monster-type.h
Original file line number Diff line number Diff line change
Expand Up @@ -394,6 +394,7 @@ enum monster_type // env.mons[].type
MONS_COBALT_LICHEN,
MONS_COBALT_LICHEN_EYESTALK,
MONS_COBALT_LICHEN_MAW,
MONS_COBALT_LICHEN_SAC,
#endif
MONS_BALLISTOMYCETE_SPORE,
MONS_BALLISTOMYCETE,
Expand Down Expand Up @@ -1339,6 +1340,7 @@ enum monster_type // env.mons[].type
MONS_COBALT_LICHEN,
MONS_COBALT_LICHEN_EYESTALK,
MONS_COBALT_LICHEN_MAW,
MONS_COBALT_LICHEN_SAC,
#endif

NUM_MONSTERS, // used for polymorph
Expand Down
5 changes: 5 additions & 0 deletions crawl-ref/source/rltiles/dc-misc.txt
Original file line number Diff line number Diff line change
Expand Up @@ -413,6 +413,11 @@ cloud_magnetic0 CLOUD_MAGNETISED_DUST
cloud_magnetic2
cloud_magnetic2

# TODO: Better placeholder
UNUSED/other/gold_sparkles1 CLOUD_SPORE
UNUSED/other/gold_sparkles2
UNUSED/other/gold_sparkles3

%sdir mon
fungi_plants/ballistomycete_spore SPORE_OVERLAY

Expand Down
5 changes: 5 additions & 0 deletions crawl-ref/source/rltiles/dc-mon.txt
Original file line number Diff line number Diff line change
Expand Up @@ -1226,6 +1226,11 @@ cobalt_lichen MONS_COBALT_LICHEN
%compose UNUSED/monsters/ravenous_mimic
%finish MONS_COBALT_LICHEN_MAW

%start
%compose cobalt_lichen
%compose UNUSED/monsters/plants/poison_pollen
%finish MONS_COBALT_LICHEN_SAC

## Goblins ('g')
%sdir mon/unique
ijyb MONS_IJYB
Expand Down

0 comments on commit a9b2e27

Please sign in to comment.