Skip to content

Commit

Permalink
Merge pull request CleverRaven#70600 from gkarfakis19/dormant_zombie
Browse files Browse the repository at this point in the history
Introduces dormant zombies
  • Loading branch information
I-am-Erk authored Jan 3, 2024
2 parents eb4ec40 + 86901b0 commit 622d57d
Show file tree
Hide file tree
Showing 25 changed files with 341 additions and 31 deletions.
3 changes: 2 additions & 1 deletion data/json/monstergroups/zombies.json
Original file line number Diff line number Diff line change
Expand Up @@ -1007,7 +1007,8 @@
"//": "Corpses buried at a mass grave. No ferals, and few animals.",
"default": "mon_zombie",
"monsters": [
{ "monster": "mon_zombie", "weight": 264, "cost_multiplier": 0 },
{ "monster": "mon_zombie", "weight": 132, "cost_multiplier": 0 },
{ "monster": "mon_pseudo_dormant_zombie", "weight": 132, "cost_multiplier": 0 },
{ "monster": "mon_zombie_fat", "weight": 266, "cost_multiplier": 0 },
{ "monster": "mon_zombie_child", "weight": 100, "cost_multiplier": 0 },
{ "monster": "mon_zombie_tough", "weight": 100, "cost_multiplier": 0 },
Expand Down
10 changes: 10 additions & 0 deletions data/json/monsters/monster_flags.json
Original file line number Diff line number Diff line change
Expand Up @@ -289,6 +289,16 @@
"type": "monster_flag",
"//": "Monster corpse will revive after a short period of time"
},
{
"id": "DORMANT",
"type": "monster_flag",
"//": "Monster corpse can be revived by dormant corpse traps"
},
{
"id": "QUIETDEATH",
"type": "monster_flag",
"//": "Monster dies quietly and doesn't display a message."
},
{
"id": "VERMIN",
"type": "monster_flag",
Expand Down
110 changes: 110 additions & 0 deletions data/json/monsters/zed_dormant.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
[
{
"type": "SPELL",
"id": "kill_pseudo_zombie",
"name": { "str": "kill pseudo zombie" },
"description": "You should not see this. Kills pseudo zombie to turn it into a corpse for revival.",
"valid_targets": [ "self" ],
"max_level": 1,
"flags": [ "SILENT", "NO_EXPLOSION_SFX", "PERCENTAGE_DAMAGE", "NO_FAIL" ],
"base_casting_time": 1,
"shape": "blast",
"min_damage": 100,
"max_damage": 100,
"min_aoe": 1,
"max_aoe": 1,
"message": "",
"effect": "attack",
"damage_type": "pure"
},
{
"type": "SPELL",
"id": "pseudo_dormant_trap_setup",
"name": { "str": "dormant corpse setup" },
"description": "You should not see this. Sets up trap for dormant zombies.",
"valid_targets": [ "self" ],
"max_level": 1,
"flags": [ "SILENT", "NO_EXPLOSION_SFX" ],
"base_casting_time": 1,
"shape": "blast",
"min_aoe": 1,
"max_aoe": 1,
"message": "",
"effect": "add_trap",
"effect_str": "tr_dormant_corpse",
"extra_effects": [ { "id": "kill_pseudo_zombie", "hit_self": true } ]
},
{
"id": "mon_pseudo_dormant_zombie",
"type": "MONSTER",
"name": { "str": "zombie" },
"description": "Fake zombie used for spawning dormant zombies. If you see this, open an issue on github.",
"copy-from": "mon_zombie",
"looks_like": "corpse_mon_zombie",
"hp": 5,
"speed": 1,
"flags": [ "FILTHY", "REVIVES", "DORMANT", "QUIETDEATH" ],
"zombify_into": "mon_zombie",
"special_attacks": [
{
"id": "pseudo_dormant_trap_setup_attk",
"type": "spell",
"spell_data": { "id": "pseudo_dormant_trap_setup", "hit_self": true },
"cooldown": 1,
"allow_no_target": true,
"monster_message": ""
}
]
},
{
"type": "trap",
"id": "tr_dormant_corpse",
"name": "dormant zombie corpse",
"//": "This zombie 'corpse' looks eerily like it could get up and start walking at any moment.",
"color": "light_green",
"symbol": "Z",
"visibility": 8,
"avoidance": 20,
"difficulty": 99,
"flags": [ "UNDODGEABLE", "AVATAR_ONLY", "PROXIMITY" ],
"action": "spell",
"spell_data": { "id": "wake_up_dormant" },
"trigger_message_u": "A zombie rises from the ground!",
"trigger_message_npc": "A zombie rises from the ground!",
"sound_threshold": [ 10, 20 ]
},
{
"type": "SPELL",
"id": "cause_loud_moaning",
"name": { "str": "cause loud moaning" },
"description": "You should not see this. Causes loud noise.",
"valid_targets": [ "ground", "ally" ],
"max_level": 1,
"shape": "blast",
"effect": "noise",
"flags": [ "NO_EXPLOSION_SFX" ],
"sound_type": "combat",
"sound_id": "melee_attack",
"sound_variant": "monster_melee_hit",
"sound_description": "a zombie moaning!",
"min_damage": 60,
"max_damage": 60,
"//": "volume is damage/3, so 20"
},
{
"type": "SPELL",
"id": "wake_up_dormant",
"name": { "str": "dormant corpse waking up" },
"description": "You should not see this. Causes all dormant corpses in the tile to revive.",
"valid_targets": [ "ground" ],
"max_level": 1,
"flags": [ "NO_EXPLOSION_SFX" ],
"base_casting_time": 1,
"shape": "blast",
"min_aoe": 1,
"max_aoe": 1,
"effect": "revive_dormant",
"effect_str": "ZOMBIE",
"extra_effects": [ { "id": "cause_loud_moaning" } ]
}
]
2 changes: 1 addition & 1 deletion data/json/traps.json
Original file line number Diff line number Diff line change
Expand Up @@ -1166,6 +1166,6 @@
"avoidance": 8,
"difficulty": 0,
"action": "sound_detect",
"sound_threshold": 5
"sound_threshold": [ 15, 25 ]
}
]
1 change: 1 addition & 0 deletions doc/JSON_FLAGS.md
Original file line number Diff line number Diff line change
Expand Up @@ -1137,6 +1137,7 @@ Other monster flags.
- ```COMBAT_MOUNT``` This mount has better chance to ignore hostile monster fear
- ```CONSOLE_DESPAWN``` Despawns when a nearby console is properly hacked.
- ```CONVERSATION``` This monster can engage in conversation. Will need to have chat_topics as well.
- ```DORMANT``` This monster will be revived by dormant corpse traps.
- ```DEADLY_VIRUS``` This monster can inflict the zombie_virus effect
- ```DESTROYS``` Bashes down walls and more. (2.5x bash multiplier, where base is the critter's max melee bashing)
- ```DIGS``` Digs through the ground. Will not travel through non-diggable terrain such as roads.
Expand Down
2 changes: 1 addition & 1 deletion doc/JSON_INFO.md
Original file line number Diff line number Diff line change
Expand Up @@ -3035,7 +3035,7 @@ See [MUTATIONS.md](MUTATIONS.md)
},
"trigger_message_u": "A bear trap closes on your foot!", // This message will be printed when player steps on a trap
"trigger_message_npc": "A bear trap closes on <npcname>'s foot!", // This message will be printed when NPC or monster steps on a trap
"sound_threshold": 5 // Optional. Minimum volume of sound that will trigger this trap. Defaults to 0 (Will not trigger from sound).
"sound_threshold": [5,10] // Optional. Minimum volume of sound that will trigger this trap. Defaults to [0,0] (Will not trigger from sound). If two values [min,max] are provided, trap triggers on a linearly increasing chance depending on volume, from 25% (min) to 100%(max). To always trigger at some noise, say noise level N, specify as [N,N]. IMPORTANT: Not all traps work with this system. Make sure to double check and test.
```
### Vehicle Groups
Expand Down
2 changes: 2 additions & 0 deletions doc/MAGIC.md
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,7 @@ Below is a table of currently implemented effects, along with special rules for

Effect | Description
--- |---
`add_trap` | Adds a trap in the target tile. This always succeeds (unless there is an existing trap) and only places 1 trap. The `effect_str` is the id of the trap.
`area_pull` | Pulls `valid_targets` in its aoe toward the target location. Currently, the pull distance is set to 1 (see `directed_push`).
`area_push` | Pushes `valid_targets` in its aoe away from the target location. Currently, the push distance is set to 1 (see `directed_push`).
`attack` | Causes damage to `valid_targets` in its aoe, and applies `effect_str` named effect to targets. To damage terrain use `bash`.
Expand All @@ -189,6 +190,7 @@ Effect | Description
`remove_effect` | Removes `effect_str` effects from all creatures in the aoe.
`remove_field` | Removes a `effect_str` field in the aoe. Causes teleglow of varying intensity and potentially teleportation depending on field density, if the field removed is `fd_fatigue`.
`revive` | Revives a monster like a zombie necromancer. The monster must have the `REVIVES` flag.
`revive_dormant` | Revives a dormant monster. The monster must have the `REVIVES` AND the `DORMANT` flag.
`short_range_teleport` | Teleports the player randomly range spaces with aoe variation. See also the `TARGET_TELEPORT` and `UNSAFE_TELEPORT` flags.
`slime_split` | The slime splits into two large or normal slimes, depending on mass. Note: hardcoded for `mon_blob`-type enemies, check the monster `death_function` + spell `summon` combination.
`spawn_item` | Spawns an item that will disappear at the end of its duration. Default duration is 0.
Expand Down
7 changes: 6 additions & 1 deletion src/game.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5443,6 +5443,11 @@ bool game::is_sheltered( const tripoint &p )
}

bool game::revive_corpse( const tripoint &p, item &it )
{
return revive_corpse( p, it, 1 );
}

bool game::revive_corpse( const tripoint &p, item &it, int radius )
{
if( !it.is_corpse() ) {
debugmsg( "Tried to revive a non-corpse." );
Expand Down Expand Up @@ -5481,7 +5486,7 @@ bool game::revive_corpse( const tripoint &p, item &it )
}
}

return place_critter_at( newmon_ptr, p );
return place_critter_around( newmon_ptr, p, radius );
}

void game::save_cyborg( item *cyborg, const tripoint &couch_pos, Character &installer )
Expand Down
2 changes: 2 additions & 0 deletions src/game.h
Original file line number Diff line number Diff line change
Expand Up @@ -510,6 +510,8 @@ class game
* If reviving failed, the item is unchanged, as is the environment (no new monsters).
*/
bool revive_corpse( const tripoint &p, item &it );
// same as above, but with relaxed placement radius.
bool revive_corpse( const tripoint &p, item &it, int radius );
/**Turns Broken Cyborg monster into Cyborg NPC via surgery*/
void save_cyborg( item *cyborg, const tripoint &couch_pos, Character &installer );
/** Asks if the player wants to cancel their activity, and if so cancels it. */
Expand Down
14 changes: 14 additions & 0 deletions src/item.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,7 @@
#include "string_id_utils.h"
#include "text_snippets.h"
#include "translations.h"
#include "trap.h"
#include "try_parse_integer.h"
#include "units.h"
#include "units_fwd.h"
Expand Down Expand Up @@ -13032,6 +13033,19 @@ bool item::process_corpse( map &here, Character *carrier, const tripoint &pos )
return false;
}

if( corpse->has_flag( mon_flag_DORMANT ) ) {
//if dormant, ensure trap still exists.
const trap *trap_here = &here.tr_at( pos );
if( trap_here->is_null() ) {
// if there isn't a trap, we need to add one again.
here.trap_set( pos, trap_id( "tr_dormant_corpse" ) );
} else if( trap_here->loadid != trap_id( "tr_dormant_corpse" ) ) {
// if there is a trap, but it isn't the right one, we need to revive the zombie manually.
return g->revive_corpse( pos, *this, 3 );
}
return false;
}

// handle human corpses rising as zombies
if( corpse->id == mtype_id::NULL_ID() && !has_var( "zombie_form" ) &&
!mon_human->zombify_into.is_empty() ) {
Expand Down
7 changes: 7 additions & 0 deletions src/magic.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3015,6 +3015,13 @@ spell fake_spell::get_spell( const Creature &caster, int min_level_override ) co
return sp;
}

// intended for spells without casters
spell fake_spell::get_spell() const
{
spell sp( id );
return sp;
}

void spell_events::notify( const cata::event &e )
{
switch( e.type() ) {
Expand Down
6 changes: 6 additions & 0 deletions src/magic.h
Original file line number Diff line number Diff line change
Expand Up @@ -179,6 +179,7 @@ struct fake_spell {

// gets the spell with an additional override for minimum level (default 0)
spell get_spell( const Creature &caster, int min_level_override = 0 ) const;
spell get_spell() const;

bool is_valid() const;
void load( const JsonObject &jo );
Expand Down Expand Up @@ -788,6 +789,9 @@ void dash( const spell &sp, Creature &caster, const tripoint &target );
void banishment( const spell &sp, Creature &caster, const tripoint &target );
// revives a monster into some kind of zombie if the monster has the revives flag
void revive( const spell &sp, Creature &caster, const tripoint &target );
// revives a dormant monster if it has the revives and the dormant flag
void revive_dormant( const spell &sp, Creature &caster, const tripoint &target );
void add_trap( const spell &sp, Creature &caster, const tripoint &target );
void upgrade( const spell &sp, Creature &caster, const tripoint &target );
// causes guilt to the target as if it killed the caster
void guilt( const spell &sp, Creature &caster, const tripoint &target );
Expand All @@ -811,6 +815,7 @@ std::map<std::string, std::function<void( const spell &, Creature &, const tripo
effect_map{
{ "pain_split", spell_effect::pain_split },
{ "attack", spell_effect::attack },
{ "add_trap", spell_effect::add_trap},
{ "targeted_polymorph", spell_effect::targeted_polymorph },
{ "short_range_teleport", spell_effect::short_range_teleport },
{ "spawn_item", spell_effect::spawn_ethereal_item },
Expand Down Expand Up @@ -838,6 +843,7 @@ effect_map{
{ "dash", spell_effect::dash },
{ "banishment", spell_effect::banishment },
{ "revive", spell_effect::revive },
{ "revive_dormant", spell_effect::revive_dormant },
{ "upgrade", spell_effect::upgrade },
{ "guilt", spell_effect::guilt },
{ "remove_effect", spell_effect::remove_effect },
Expand Down
39 changes: 36 additions & 3 deletions src/magic_spell_effect.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1438,7 +1438,7 @@ void spell_effect::revive( const spell &sp, Creature &caster, const tripoint &ta
for( item &corpse : here.i_at( aoe ) ) {
const mtype *mt = corpse.get_mtype();
if( !( corpse.is_corpse() && corpse.can_revive() && corpse.active &&
mt->has_flag( mon_flag_REVIVES ) && mt->in_species( spec ) &&
mt->has_flag( mon_flag_REVIVES ) && !mt->has_flag( mon_flag_DORMANT ) && mt->in_species( spec ) &&
!mt->has_flag( mon_flag_NO_NECRO ) ) ) {
continue;
}
Expand All @@ -1450,6 +1450,37 @@ void spell_effect::revive( const spell &sp, Creature &caster, const tripoint &ta
}
}

// identical to above, but checks for REVIVES && DORMANT flag. Ignores NO_NECRO.
void spell_effect::revive_dormant( const spell &sp, Creature &caster, const tripoint &target )
{
const std::set<tripoint> area = spell_effect_area( sp, target, caster );
::map &here = get_map();
const species_id spec( sp.effect_data() );
for( const tripoint &aoe : area ) {
for( item &corpse : here.i_at( aoe ) ) {
const mtype *mt = corpse.get_mtype();
if( !( corpse.is_corpse() && corpse.can_revive() && corpse.active &&
mt->has_flag( mon_flag_REVIVES ) && mt->has_flag( mon_flag_DORMANT ) && mt->in_species( spec ) ) ) {
continue;
}
// relaxed revive with radius.
if( g->revive_corpse( aoe, corpse, 3 ) ) {
here.i_rem( aoe, &corpse );
break;
}
}
}
}

void spell_effect::add_trap( const spell &sp, Creature &, const tripoint &target )
{
::map &here = get_map();
const trap_id tr_id( sp.effect_data() );
if( here.tr_at( target ) == tr_null ) {
here.trap_set( target, tr_id );
}
}

void spell_effect::upgrade( const spell &sp, Creature &caster, const tripoint &target )
{
const std::set<tripoint> area = spell_effect_area( sp, target, caster );
Expand Down Expand Up @@ -1752,7 +1783,8 @@ void spell_effect::banishment( const spell &sp, Creature &caster, const tripoint
}
}

void spell_effect::effect_on_condition( const spell &sp, Creature &caster, const tripoint &target )
void spell_effect::effect_on_condition( const spell &sp, Creature &caster,
const tripoint &target )
{
const std::set<tripoint> area = spell_effect_area( sp, target, caster );

Expand All @@ -1773,7 +1805,8 @@ void spell_effect::effect_on_condition( const spell &sp, Creature &caster, const
}
}

void spell_effect::slime_split_on_death( const spell &sp, Creature &caster, const tripoint &target )
void spell_effect::slime_split_on_death( const spell &sp, Creature &caster,
const tripoint &target )
{
sp.make_sound( target, caster );
int mass = caster.get_speed_base();
Expand Down
Loading

0 comments on commit 622d57d

Please sign in to comment.