diff --git a/data/json/effects.json b/data/json/effects.json index 2ec88e025be7d..c4c23e91ad885 100644 --- a/data/json/effects.json +++ b/data/json/effects.json @@ -623,6 +623,15 @@ ], "flags": [ "EFFECT_LIMB_SCORE_MOD" ] }, + { + "type": "effect_type", + "id": "immobilization", + "name": [ "Immobilized" ], + "desc": [ "You can't move!" ], + "rating": "bad", + "show_in_info": true, + "flags": [ "EFFECT_IMPEDING" ] + }, { "type": "effect_type", "id": "stunned", diff --git a/data/json/traps.json b/data/json/traps.json index ecfcea1fd47b5..edd288091a892 100644 --- a/data/json/traps.json +++ b/data/json/traps.json @@ -202,6 +202,17 @@ "difficulty": 0, "action": "snare_light" }, + { + "type": "trap", + "id": "tr_species_snare", + "name": "species snare trap", + "color": "red", + "symbol": "~", + "visibility": 1, + "avoidance": 8, + "difficulty": 0, + "action": "snare_species" + }, { "type": "trap", "id": "tr_beartrap", diff --git a/data/mods/Xedra_Evolved/eocs/inventor.json b/data/mods/Xedra_Evolved/eocs/inventor.json index 17445c0efbfba..47f65b57168ae 100644 --- a/data/mods/Xedra_Evolved/eocs/inventor.json +++ b/data/mods/Xedra_Evolved/eocs/inventor.json @@ -65,7 +65,8 @@ "mod_inv_pistol_booster", "inventor_research_base_3", "inventor_research_energy_1", - "software_AI" + "software_AI", + "stunning_qr_trap" ], "type": "recipe", "true_eocs": [ "EOC_INVENTOR_MESSAGE_GOOD" ], @@ -101,6 +102,7 @@ "vision_halo", "sonic_gun", "mace_magic", + "stunning_arg_trap", "inventor_fists_plus", "inventor_plasma_axe_off", "inventor_research_energy_1" diff --git a/data/mods/Xedra_Evolved/items/alchemy.json b/data/mods/Xedra_Evolved/items/alchemy.json index 2180a51700682..fb05d73ad7268 100644 --- a/data/mods/Xedra_Evolved/items/alchemy.json +++ b/data/mods/Xedra_Evolved/items/alchemy.json @@ -113,6 +113,30 @@ "vitamins": [ [ "cheval", 1000 ] ] } }, + { + "id": "nether_snare", + "type": "TOOL", + "name": "snaring pentagram trap", + "category": "traps", + "properties": { "capture_species": "NETHER" }, + "description": "A metallic pentagram full of mystic symbols. Will trap creatures from other dimensions if they wander into it.", + "weight": "1 kg", + "volume": "1000 ml", + "longest_side": "60 cm", + "price": "250 USD", + "price_postapoc": "5 USD", + "material": "lead", + "symbol": "*", + "color": "dark_gray", + "flags": [ "SINGLE_USE" ], + "use_action": { + "type": "place_trap", + "trap": "tr_species_snare", + "moves": 150, + "practice": 2, + "done_message": "You set the pentagram snare." + } + }, { "id": "spiritshow_dust", "name": { "str_sp": "spirit show dust" }, diff --git a/data/mods/Xedra_Evolved/items/hedge_magic_items.json b/data/mods/Xedra_Evolved/items/hedge_magic_items.json index 998794b1b2b28..2c397cd663b77 100644 --- a/data/mods/Xedra_Evolved/items/hedge_magic_items.json +++ b/data/mods/Xedra_Evolved/items/hedge_magic_items.json @@ -158,6 +158,30 @@ "flags": [ "ONE_PER_LAYER", "SKINTIGHT", "NO_UNLOAD", "NO_RELOAD", "MUNDANE" ], "relic_data": { "passive_effects": [ { "has": "WORN", "condition": "ALWAYS", "values": [ { "value": "MAX_MANA", "add": 0 } ] } ] } }, + { + "id": "item_hedge_changeling_snare", + "type": "TOOL", + "name": "betweener's maze", + "category": "traps", + "properties": { "capture_species": "CHANGELING" }, + "description": "A yarn contraption similar to a cat's cradle that looks like a three dimensional maze. When placed properly, it can fascinate the changeling blooded.", + "weight": "20 g", + "volume": "100 ml", + "longest_side": "20 cm", + "price": "250 USD", + "price_postapoc": "5 USD", + "material": "wool", + "symbol": "*", + "color": "green_green", + "flags": [ "SINGLE_USE" ], + "use_action": { + "type": "place_trap", + "trap": "tr_species_snare", + "moves": 150, + "practice": 2, + "done_message": "You place the betweener's maze." + } + }, { "id": "item_hedge_nether_eye_paste", "name": { "str_sp": "Blood-and-Ash Paste" }, diff --git a/data/mods/Xedra_Evolved/items/inventor/traps.json b/data/mods/Xedra_Evolved/items/inventor/traps.json new file mode 100644 index 0000000000000..e975953cea0bd --- /dev/null +++ b/data/mods/Xedra_Evolved/items/inventor/traps.json @@ -0,0 +1,50 @@ +[ + { + "id": "stunning_qr_trap", + "type": "TOOL", + "name": "stunning QR trap", + "category": "traps", + "properties": { "capture_species": "ROBOT" }, + "description": "A jagged and eldritch looking QR code ready to be placed wherever you are concerned about mechanical eyes. Will trap robots if they wander into it.", + "weight": "1 g", + "volume": "5 ml", + "longest_side": "5 cm", + "price": "250 USD", + "price_postapoc": "5 USD", + "material": "plastic", + "symbol": "*", + "color": "light_gray", + "flags": [ "SINGLE_USE" ], + "use_action": { + "type": "place_trap", + "trap": "tr_species_snare", + "moves": 150, + "practice": 2, + "done_message": "You set the QR code." + } + }, + { + "id": "stunning_arg_trap", + "type": "TOOL", + "name": "stunning arg trap", + "category": "traps", + "properties": { "capture_species": "CYBORG" }, + "description": "A jagged and eldritch looking QR code ready to be placed wherever you are concerned about mechanical eyes. Will trap cyborgs if they wander into it.", + "weight": "1 g", + "volume": "5 ml", + "longest_side": "5 cm", + "price": "250 USD", + "price_postapoc": "5 USD", + "material": "plastic", + "symbol": "*", + "color": "light_gray", + "flags": [ "SINGLE_USE" ], + "use_action": { + "type": "place_trap", + "trap": "tr_species_snare", + "moves": 150, + "practice": 2, + "done_message": "You set the QR code." + } + } +] diff --git a/data/mods/Xedra_Evolved/items/spell_learning_items/spellbooks_hedge.json b/data/mods/Xedra_Evolved/items/spell_learning_items/spellbooks_hedge.json index d6fa1ed098986..6a0019666e9ef 100644 --- a/data/mods/Xedra_Evolved/items/spell_learning_items/spellbooks_hedge.json +++ b/data/mods/Xedra_Evolved/items/spell_learning_items/spellbooks_hedge.json @@ -79,5 +79,20 @@ "symbol": "?", "color": "dark_gray", "use_action": { "type": "learn_spell", "spells": [ "hedge_no_nightmares" ] } + }, + { + "id": "spellbook_hedge_snare_changelings", + "type": "BOOK", + "category": "manuals", + "name": { "str": "Monas Hieroglyphica", "str_pl": "copies of Monas Hieroglyphica" }, + "description": "This is a thin reproduction of John Dee's Monas Hieroglyphica. A complicated work whose meaning has long been lost without access to the occult oral tradition of it's time. It's possible that some remnants of that oral tradition still exist today, but it is unknown if they would be able to fully interpret this spellbook.", + "weight": "83 g", + "volume": "132 ml", + "price": "10 USD", + "material": [ "paper" ], + "looks_like": "cookbook", + "symbol": "?", + "color": "dark_gray", + "use_action": { "type": "learn_spell", "spells": [ "hedge_changeling_snare" ] } } ] diff --git a/data/mods/Xedra_Evolved/mod_interactions/BombasticPerks/perks/perk_data/Alchemy1.json b/data/mods/Xedra_Evolved/mod_interactions/BombasticPerks/perks/perk_data/Alchemy1.json index 42cb6f29a4976..775c1fe328105 100644 --- a/data/mods/Xedra_Evolved/mod_interactions/BombasticPerks/perks/perk_data/Alchemy1.json +++ b/data/mods/Xedra_Evolved/mod_interactions/BombasticPerks/perks/perk_data/Alchemy1.json @@ -96,7 +96,7 @@ "condition": { "and": [ { "u_has_trait": "perk_ALCHEMY4" }, { "not": { "u_has_effect": "mental_exhaustion" } } ] }, "effect": [ { - "u_roll_remainder": [ "life_extension_potion", "potion_strength", "potion_dex", "hares_leap_potion" ], + "u_roll_remainder": [ "life_extension_potion", "potion_strength", "potion_dex", "hares_leap_potion", "nether_snare" ], "type": "recipe", "true_eocs": [ "EOC_SUCCESFUL_ROLL_REMAINDER_ALCHEMY" ], "false_eocs": [ "EOC_COMPLETED_ROLL_REMAINDER_ALCHEMY" ] diff --git a/data/mods/Xedra_Evolved/recipes/alchemy.json b/data/mods/Xedra_Evolved/recipes/alchemy.json index 30354032dbea6..28d08d901602b 100644 --- a/data/mods/Xedra_Evolved/recipes/alchemy.json +++ b/data/mods/Xedra_Evolved/recipes/alchemy.json @@ -192,6 +192,20 @@ "tools": [ [ [ "surface_heat", 25, "LIST" ] ] ], "components": [ [ [ "faewild", 1 ] ], [ [ "scrap_dreamdross", 2 ] ], [ [ "bone", 1 ] ], [ [ "corpse_ash", 1 ] ] ] }, + { + "type": "recipe", + "activity_level": "LIGHT_EXERCISE", + "result": "nether_snare", + "category": "CC_XEDRA", + "subcategory": "CSC_XEDRA_ALCHEMY", + "skill_used": "fabrication", + "skills_required": [ "deduction", 1 ], + "difficulty": 3, + "time": "75 m", + "flags": [ "SECRET" ], + "tools": [ [ [ "paint_brush", -1 ] ], [ [ "paint", 1, "LIST" ] ] ], + "components": [ [ [ "scrap_dreamdross", 3 ] ], [ [ "mutant_blood", 1 ] ], [ [ "silver_small", 1 ] ] ] + }, { "type": "recipe", "activity_level": "LIGHT_EXERCISE", diff --git a/data/mods/Xedra_Evolved/recipes/inventor/misc.json b/data/mods/Xedra_Evolved/recipes/inventor/misc.json index f8be4897fc79d..457dc93de599b 100644 --- a/data/mods/Xedra_Evolved/recipes/inventor/misc.json +++ b/data/mods/Xedra_Evolved/recipes/inventor/misc.json @@ -144,6 +144,54 @@ [ [ "amplifier", 2 ] ] ] }, + { + "type": "recipe", + "activity_level": "MODERATE_EXERCISE", + "result": "stunning_qr_trap", + "flags": [ "SECRET" ], + "category": "CC_XEDRA", + "subcategory": "CSC_XEDRA_MISC", + "skill_used": "deduction", + "difficulty": 5, + "skills_required": [ "fabrication", 1 ], + "time": "3 h", + "proficiencies": [ { "proficiency": "prof_plasticworking", "time_multiplier": 2, "skill_penalty": 0 } ], + "qualities": [ { "id": "SCREW", "level": 1 }, { "id": "SAW_M", "level": 1 } ], + "tools": [ [ [ "paint_brush", -1 ] ], [ [ "paint", 1, "LIST" ] ] ], + "components": [ + [ [ "light_disposable_cell", 1 ] ], + [ [ "power_supply", 1 ] ], + [ [ "scrap", 3 ] ], + [ [ "cable", 4 ] ], + [ [ "scrap_dreamdross", 4 ] ], + [ [ "plastic_chunk", 2 ] ], + [ [ "amplifier", 1 ] ] + ] + }, + { + "type": "recipe", + "activity_level": "MODERATE_EXERCISE", + "result": "stunning_arg_trap", + "flags": [ "SECRET" ], + "category": "CC_XEDRA", + "subcategory": "CSC_XEDRA_MISC", + "skill_used": "deduction", + "difficulty": 6, + "skills_required": [ "computer", 3 ], + "time": "3 h", + "proficiencies": [ { "proficiency": "prof_plasticworking", "time_multiplier": 2, "skill_penalty": 0 } ], + "qualities": [ { "id": "SCREW", "level": 1 }, { "id": "SAW_M", "level": 1 } ], + "tools": [ [ [ "paint_brush", -1 ] ], [ [ "paint", 1, "LIST" ] ] ], + "components": [ + [ [ "light_disposable_cell", 1 ] ], + [ [ "power_supply", 1 ] ], + [ [ "scrap", 3 ] ], + [ [ "cable", 4 ] ], + [ [ "scrap_dreamdross", 4 ] ], + [ [ "plastic_chunk", 2 ] ], + [ [ "amplifier", 1 ] ] + ] + }, { "result": "software_AI", "container": "usb_drive", diff --git a/data/mods/Xedra_Evolved/requirements/spell_components.json b/data/mods/Xedra_Evolved/requirements/spell_components.json index 897c46d3052a4..1f091fb798f9f 100644 --- a/data/mods/Xedra_Evolved/requirements/spell_components.json +++ b/data/mods/Xedra_Evolved/requirements/spell_components.json @@ -123,6 +123,11 @@ "components": [ [ [ "corpse_ash", 50 ] ], [ [ "edible_blood", 1, "LIST" ], [ "blood_tainted", 1 ] ], [ [ "wild_herbs", 20 ] ] ], "qualities": [ { "id": "FINE_GRIND", "level": 1 } ] }, + { + "id": "spell_components_hedge_changeling_snare", + "type": "requirement", + "components": [ [ [ "yarn", 10 ] ], [ [ "splinter", 3 ] ], [ [ "blackberries", 1 ] ], [ [ "wild_herbs", 20 ] ] ] + }, { "id": "spell_components_hedge_stunning_spell", "type": "requirement", diff --git a/data/mods/Xedra_Evolved/spells/hedge_magic_spells.json b/data/mods/Xedra_Evolved/spells/hedge_magic_spells.json index b695aaf47f44c..34e8477831265 100644 --- a/data/mods/Xedra_Evolved/spells/hedge_magic_spells.json +++ b/data/mods/Xedra_Evolved/spells/hedge_magic_spells.json @@ -133,6 +133,28 @@ "max_level": 1, "min_damage": 1, "max_damage": 1, + "base_casting_time": 1440000, + "min_duration": 360000, + "max_duration": 720000 + }, + { + "id": "hedge_changeling_snare", + "type": "SPELL", + "name": "Maze the Little Folk", + "description": "A spell to create a snare for catching one of the little folk, a changeling. Has no effect on the elder spirits of wood and water, fire and earth, nor plant or man.", + "message": "You skein a complicated cat's cradle with splinters of wood guiding the way in to blackberries at each of the focus points in your design.", + "flags": [ "VERBAL", "SOMATIC", "NO_FAIL", "PERMANENT", "PERMANENT_ALL_LEVELS" ], + "valid_targets": [ "self" ], + "difficulty": 1, + "spell_class": "HEDGE_MAGIC", + "skill": "survival", + "effect": "spawn_item", + "effect_str": "item_hedge_changeling_snare", + "shape": "blast", + "components": "spell_components_hedge_changeling_snare", + "max_level": 1, + "min_damage": 1, + "max_damage": 1, "base_casting_time": 990000, "min_duration": 360000, "max_duration": 720000 diff --git a/src/iuse_actor.cpp b/src/iuse_actor.cpp index 6d3004ecd5ffd..f2fc696bf13fd 100644 --- a/src/iuse_actor.cpp +++ b/src/iuse_actor.cpp @@ -3925,6 +3925,10 @@ std::optional place_trap_actor::use( Character *p, item &it, const tripoint p->mod_moves( -move_cost_final ); place_and_add_as_known( *p, pos, data.trap ); + const trap &placed_trap = here.tr_at( pos ); + if( !placed_trap.is_null() ) { + const_cast( placed_trap ).set_trap_data( it.typeId() ); + } for( const tripoint &t : here.points_in_radius( pos, data.trap.obj().get_trap_radius(), 0 ) ) { if( t != pos ) { place_and_add_as_known( *p, t, outer_layer_trap ); diff --git a/src/monmove.cpp b/src/monmove.cpp index cbda3a0a14b7f..10eac2facd6a1 100644 --- a/src/monmove.cpp +++ b/src/monmove.cpp @@ -64,6 +64,7 @@ static const efftype_id effect_downed( "downed" ); static const efftype_id effect_dragging( "dragging" ); static const efftype_id effect_grabbed( "grabbed" ); static const efftype_id effect_harnessed( "harnessed" ); +static const efftype_id effect_immobilization( "immobilization" ); static const efftype_id effect_led_by_leash( "led_by_leash" ); static const efftype_id effect_no_sight( "no_sight" ); static const efftype_id effect_operating( "operating" ); @@ -969,6 +970,10 @@ void monster::move() moves = 0; return; } + if( has_effect( effect_immobilization ) ) { + moves = 0; + return; + } if( has_effect( effect_stunned ) || has_effect( effect_psi_stunned ) ) { stumble(); moves = 0; diff --git a/src/savegame_json.cpp b/src/savegame_json.cpp index 39bef4d736dc5..1257228913c3d 100644 --- a/src/savegame_json.cpp +++ b/src/savegame_json.cpp @@ -4790,8 +4790,10 @@ void submap::store( JsonOut &jsout ) const jsout.start_array(); jsout.write( p.x ); jsout.write( p.y ); + const trap_id &trap_at = get_trap( p ); // TODO: jsout should support writing an id like jsout.write( trap_id ) - jsout.write( get_trap( p ).id().str() ); + jsout.write( trap_at.id().str() ); + jsout.write( trap_at->trap_item_type ); jsout.end_array(); } } @@ -5048,7 +5050,10 @@ void submap::load( const JsonValue &jv, const std::string &member_name, int vers // TODO: jsin should support returning an id like jsin.get_id() const trap_str_id trid( trap_entry.next_string() ); m->trp[p.x][p.y] = trid.id(); - if( trap_entry.size() > 3 ) { + if( trap_entry.has_more() ) { + const_cast( m->trp[p.x][p.y].obj() ).set_trap_data( itype_id( trap_entry.next_string() ) ); + } + if( trap_entry.size() > 4 ) { trap_entry.throw_error( "Too many values for trap entry" ); } } diff --git a/src/trap.cpp b/src/trap.cpp index 5d327f79ec7c1..bea187b2f779e 100644 --- a/src/trap.cpp +++ b/src/trap.cpp @@ -437,6 +437,11 @@ bool trap::easy_take_down() const return avoidance == 0 && difficulty == 0; } +void trap::set_trap_data( itype_id trap_item_type_id ) +{ + trap_item_type = trap_item_type_id; +} + bool trap::can_not_be_disarmed() const { return difficulty >= 99; diff --git a/src/trap.h b/src/trap.h index 518d9d423ea27..0af8983cade74 100644 --- a/src/trap.h +++ b/src/trap.h @@ -37,6 +37,7 @@ bool cot( const tripoint &p, Creature *c, item *i ); bool beartrap( const tripoint &p, Creature *c, item *i ); bool snare_light( const tripoint &p, Creature *c, item *i ); bool snare_heavy( const tripoint &p, Creature *c, item *i ); +bool snare_species( const tripoint &p, Creature *critter, item *trap_item ); bool board( const tripoint &p, Creature *c, item *i ); bool caltrops( const tripoint &p, Creature *c, item *i ); bool caltrops_glass( const tripoint &p, Creature *c, item *i ); @@ -160,6 +161,7 @@ struct trap { // For disassembly? std::vector> components; public: + std::optional trap_item_type; // data required for trapfunc::spell() fake_spell spell_data; int comfort = 0; @@ -229,6 +231,7 @@ struct trap { bool is_trivial_to_spot() const; + void set_trap_data( itype_id trap_item_type_id ); /** * Some traps are part of the terrain (e.g. pits) and can therefore not be disarmed * via the usual mechanics. They can be "disarmed" by changing the terrain they are part of. diff --git a/src/trapfunc.cpp b/src/trapfunc.cpp index 791010d0a2937..409ddcfb73dcb 100644 --- a/src/trapfunc.cpp +++ b/src/trapfunc.cpp @@ -52,6 +52,7 @@ static const damage_type_id damage_pure( "pure" ); static const efftype_id effect_beartrap( "beartrap" ); static const efftype_id effect_heavysnare( "heavysnare" ); +static const efftype_id effect_immobilization( "immobilization" ); static const efftype_id effect_in_pit( "in_pit" ); static const efftype_id effect_lightsnare( "lightsnare" ); static const efftype_id effect_ridden( "ridden" ); @@ -682,6 +683,68 @@ bool trapfunc::snare_heavy( const tripoint &p, Creature *c, item * ) return true; } +bool trapfunc::snare_species( const tripoint &p, Creature *critter, item * ) +{ + map &here = get_map(); + const trap &laid_trap = here.tr_at( p ); + if( !critter ) { // Nothing to capture + return false; + } + + if( laid_trap.is_null() ) { + return false; //Should never happen but just in case + } + + if( !laid_trap.trap_item_type.has_value() ) { + debugmsg( "Active trap %s has no stored item to determine species value. Removing trap to avoid crashing.", + laid_trap.name() ); + here.remove_trap( p ); + return false; + } + + item trap_item = item( item::find_type( laid_trap.trap_item_type.value() ) ); + + if( !trap_item.has_property( "capture_species" ) ) { + debugmsg( "placed trap's item type %s has no \"capture_species\" property!" ); + here.remove_trap( p ); + return false; + } + + const std::string spec = trap_item.get_property_string( "capture_species" ); + const species_id species_to_capture = species_id( spec ); + + if( !critter->in_species( species_to_capture ) ) { + critter->add_msg_if_player( m_good, _( "You harmlessly pass a %1$s." ), + laid_trap.name() ); + return false; + } + + { + sounds::sound( p, 8, sounds::sound_t::combat, _( "phwoom!" ), false, "trap", "species_snare" ); + } + + if( critter ) { + const bodypart_id hit = critter->get_random_body_part_of_type( body_part_type::type::leg ); + + critter->add_msg_if_player( m_bad, _( "A %1$s catches your %2$s!" ), + laid_trap.name(), hit->name.translated() ); + + if( critter->has_effect( effect_ridden ) ) { + add_msg( m_warning, _( "Your %1$s is caught by a %2$s!" ), critter->get_name(), laid_trap.name() ); + } + if( !critter->is_avatar() ) { + add_msg_if_player_sees( p, _( "%1$s is caught by a %2$s!" ), critter->get_name(), + laid_trap.name() ); + } + // Actual effects + critter->add_effect( effect_immobilization, 10_turns, hit ); + critter->check_dead_state(); + } + + here.remove_trap( p ); + return true; +} + bool trapfunc::landmine( const tripoint &p, Creature *c, item * ) { // tiny animals are too light to trigger land mines @@ -1669,6 +1732,7 @@ const trap_function &trap_function_from_string( const std::string &function_name { "blade", trapfunc::blade }, { "snare_light", trapfunc::snare_light }, { "snare_heavy", trapfunc::snare_heavy }, + { "snare_species", trapfunc::snare_species }, { "landmine", trapfunc::landmine }, { "telepad", trapfunc::telepad }, { "goo", trapfunc::goo },