Skip to content

Commit

Permalink
CleverRaven#44459 make disabling a monster an activity with variable …
Browse files Browse the repository at this point in the history
…duration (CleverRaven#44670)

* CleverRaven#44459 disabling a robot is an activity with variable duration
Co-authored-by: Binrui Dong <[email protected]>
Co-authored-by: Kevin Granade <[email protected]>
  • Loading branch information
Autoquark authored Jul 17, 2021
1 parent b47a3c0 commit 9f0d77d
Show file tree
Hide file tree
Showing 7 changed files with 173 additions and 36 deletions.
6 changes: 6 additions & 0 deletions data/json/effects.json
Original file line number Diff line number Diff line change
Expand Up @@ -244,6 +244,12 @@
"name": [ "Pushed" ],
"desc": [ "AI tag used for monsters pushing each other. This is a bug if you have it." ]
},
{
"type": "effect_type",
"id": "worked_on",
"name": [ "Worked On" ],
"desc": [ "AI tag used for robots being disabled. This is a bug if you have it." ]
},
{
"type": "effect_type",
"id": "venom_player1",
Expand Down
8 changes: 8 additions & 0 deletions data/json/player_activities.json
Original file line number Diff line number Diff line change
Expand Up @@ -943,6 +943,14 @@
"verb": { "ctxt": "training", "str": "working out" },
"based_on": "time"
},
{
"id": "ACT_DISABLE",
"type": "activity_type",
"activity_level": "NO_EXERCISE",
"verb": "disabling",
"suspendable": false,
"based_on": "speed"
},
{
"id": "ACT_FURNITURE_MOVE",
"type": "activity_type",
Expand Down
109 changes: 109 additions & 0 deletions src/activity_actor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -77,9 +77,12 @@
#include "vehicle.h"
#include "vpart_position.h"

static const efftype_id effect_docile( "docile" );
static const efftype_id effect_pet( "pet" );
static const efftype_id effect_sensor_stun( "sensor_stun" );
static const efftype_id effect_sheared( "sheared" );
static const efftype_id effect_sleep( "sleep" );
static const efftype_id effect_worked_on( "worked_on" );
static const efftype_id effect_tied( "tied" );

static const itype_id itype_bone_human( "bone_human" );
Expand All @@ -88,12 +91,15 @@ static const itype_id itype_electrohack( "electrohack" );
static const itype_id itype_pseudo_bio_picklock( "pseudo_bio_picklock" );

static const skill_id skill_computer( "computer" );
static const skill_id skill_electronics( "electronics" );
static const skill_id skill_mechanics( "mechanics" );
static const skill_id skill_traps( "traps" );

static const proficiency_id proficiency_prof_lockpicking( "prof_lockpicking" );
static const proficiency_id proficiency_prof_lockpicking_expert( "prof_lockpicking_expert" );


static const mtype_id mon_manhack( "mon_manhack" );
static const mtype_id mon_zombie( "mon_zombie" );
static const mtype_id mon_zombie_fat( "mon_zombie_fat" );
static const mtype_id mon_zombie_rot( "mon_zombie_rot" );
Expand Down Expand Up @@ -2408,6 +2414,108 @@ std::unique_ptr<activity_actor> stash_activity_actor::deserialize( JsonIn &jsin
return actor.clone();
}

void disable_activity_actor::start( player_activity &act, Character &/*who*/ )
{
act.moves_total = moves_total;
act.moves_left = moves_total;
monster &critter = *( g->critter_at<monster>( target ) );
critter.add_effect( effect_worked_on, 1_turns );
}

void disable_activity_actor::do_turn( player_activity &, Character &who )
{
monster *const mon_ptr = g->critter_at<monster>( target );
if( !mon_ptr ) {
who.add_msg_if_player( _( "The robot has moved somewhere else." ) );
who.cancel_activity();
return;
}

monster &critter = *mon_ptr;
if( !can_disable_or_reprogram( critter ) ) {
// I think recovery from stunned is the only reason this could happen
who.add_msg_if_player( _( "The %s recovers before you can finish." ), critter.name() );
who.cancel_activity();
return;
}

critter.add_effect( effect_worked_on, 1_turns );
}

void disable_activity_actor::finish( player_activity &act, Character &/*who*/ )
{
// Should never be null as we just checked in do_turn
monster &critter = *( g->critter_at<monster>( target ) );

if( reprogram ) {
if( critter.has_effect( effect_docile ) ) {
critter.remove_effect( effect_docile );
if( one_in( 3 ) ) {
add_msg( _( "The %s hovers momentarily as it surveys the area." ),
critter.name() );
}
} else {
critter.add_effect( effect_docile, 1_turns, true );
if( one_in( 3 ) ) {
add_msg( _( "The %s lets out a whirring noise and starts to follow you." ),
critter.name() );
}
}
} else {
get_map().add_item_or_charges( target, critter.to_item() );
if( !critter.has_flag( MF_INTERIOR_AMMO ) ) {
for( std::pair<const itype_id, int> &ammodef : critter.ammo ) {
if( ammodef.second > 0 ) {
get_map().spawn_item( target.xy(), ammodef.first, 1, ammodef.second, calendar::turn );
}
}
}
g->remove_zombie( critter );
}

act.set_to_null();
}

bool disable_activity_actor::can_disable_or_reprogram( const monster &monster )
{
if( get_avatar().get_skill_level( skill_electronics ) + get_avatar().get_skill_level(
skill_mechanics ) <= 0 ) {
return false;
}

return ( ( monster.friendly != 0 || monster.has_effect( effect_sensor_stun ) ) &&
!monster.has_flag( MF_RIDEABLE_MECH ) &&
!( monster.has_flag( MF_PAY_BOT ) && monster.has_effect( efftype_id( "paid" ) ) ) ) &&
( !monster.type->revert_to_itype.is_empty() || monster.type->id == mon_manhack );
}

int disable_activity_actor::get_disable_turns()
{
return 2000 / ( get_avatar().get_skill_level( skill_electronics ) + get_avatar().get_skill_level(
skill_mechanics ) );
}

void disable_activity_actor::serialize( JsonOut &jsout ) const
{
jsout.start_object();
jsout.member( "target", target );
jsout.member( "reprogram", reprogram );
jsout.member( "moves_total", moves_total );
jsout.end_object();
}

std::unique_ptr<activity_actor> disable_activity_actor::deserialize( JsonIn &jsin )
{
disable_activity_actor actor = disable_activity_actor();

JsonObject data = jsin.get_object();

data.read( "target", actor.target );
data.read( "reprogram", actor.reprogram );

return actor.clone();
}

void move_furniture_activity_actor::start( player_activity &act, Character & )
{
int moves = g->grabbed_furn_move_time( dp );
Expand Down Expand Up @@ -3243,6 +3351,7 @@ deserialize_functions = {
{ activity_id( "ACT_CRAFT" ), &craft_activity_actor::deserialize },
{ activity_id( "ACT_DIG" ), &dig_activity_actor::deserialize },
{ activity_id( "ACT_DIG_CHANNEL" ), &dig_channel_activity_actor::deserialize },
{ activity_id( "ACT_DISABLE" ), &disable_activity_actor::deserialize },
{ activity_id( "ACT_DISASSEMBLE" ), &disassemble_activity_actor::deserialize },
{ activity_id( "ACT_DROP" ), &drop_activity_actor::deserialize },
{ activity_id( "ACT_GUNMOD_REMOVE" ), &gunmod_remove_activity_actor::deserialize },
Expand Down
35 changes: 35 additions & 0 deletions src/activity_actor.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,13 @@

#include "activity_type.h"
#include "clone_ptr.h"
#include "point.h"
#include "type_id.h"

class Character;
class JsonIn;
class JsonOut;
class monster;
class player_activity;

class activity_actor
Expand Down Expand Up @@ -117,6 +119,39 @@ class activity_actor
void serialize( const cata::clone_ptr<activity_actor> &actor, JsonOut &jsout );
void deserialize( cata::clone_ptr<activity_actor> &actor, JsonIn &jsin );

class disable_activity_actor : public activity_actor
{
public:
disable_activity_actor() = default;
disable_activity_actor( const tripoint &target, int moves_total,
bool reprogram ) : target( target ), moves_total( moves_total ), reprogram( reprogram ) {}

activity_id get_type() const override {
return activity_id( "ACT_DISABLE" );
}

void start( player_activity &act, Character &who ) override;
void do_turn( player_activity & /*&act*/, Character &who ) override;
void finish( player_activity &act, Character &who ) override;

std::unique_ptr<activity_actor> clone() const override {
return std::make_unique<disable_activity_actor>( *this );
}

void serialize( JsonOut &jsout ) const override;
static std::unique_ptr<activity_actor> deserialize( JsonIn &jsin );

/** Returns whether the given monster is a robot and can currently be disabled or reprogrammed */
static bool can_disable_or_reprogram( const monster &monster );

static int get_disable_turns();

private:
tripoint target;
int moves_total;
bool reprogram;
};

namespace activity_actors
{

Expand Down
2 changes: 2 additions & 0 deletions src/effect.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ static const efftype_id effect_lightsnare( "lightsnare" );
static const efftype_id effect_tied( "tied" );
static const efftype_id effect_webbed( "webbed" );
static const efftype_id effect_weed_high( "weed_high" );
static const efftype_id effect_worked_on( "worked_on" );

static const itype_id itype_holybook_bible( "holybook_bible" );
static const itype_id itype_money_bundle( "money_bundle" );
Expand Down Expand Up @@ -1354,6 +1355,7 @@ static const std::unordered_set<efftype_id> hardcoded_movement_impairing = {{
effect_lightsnare,
effect_tied,
effect_webbed,
effect_worked_on,
}
};

Expand Down
43 changes: 8 additions & 35 deletions src/game.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -221,13 +221,11 @@ static const efftype_id effect_laserlocked( "laserlocked" );
static const efftype_id effect_no_sight( "no_sight" );
static const efftype_id effect_npc_suspend( "npc_suspend" );
static const efftype_id effect_onfire( "onfire" );
static const efftype_id effect_paid( "paid" );
static const efftype_id effect_pet( "pet" );
static const efftype_id effect_ridden( "ridden" );
static const efftype_id effect_riding( "riding" );
static const efftype_id effect_sleep( "sleep" );
static const efftype_id effect_stunned( "stunned" );
static const efftype_id effect_sensor_stun( "sensor_stun" );
static const efftype_id effect_tetanus( "tetanus" );
static const efftype_id effect_tied( "tied" );
static const efftype_id effect_winded( "winded" );
Expand Down Expand Up @@ -9512,27 +9510,17 @@ bool game::disable_robot( const tripoint &p )
return false;
}
monster &critter = *mon_ptr;
if( ( critter.friendly == 0 && !critter.has_effect( effect_sensor_stun ) ) ||
critter.has_flag( MF_RIDEABLE_MECH ) ||
( critter.has_flag( MF_PAY_BOT ) && critter.has_effect( effect_paid ) ) ) {
// Can only disable / reprogram friendly or stunned monsters
if( !disable_activity_actor::can_disable_or_reprogram( critter ) ) {
return false;
}

const mtype_id mid = critter.type->id;
const itype_id mon_item_id = critter.type->revert_to_itype;
if( !mon_item_id.is_empty() &&
query_yn( _( "Deactivate the %s?" ), critter.name() ) ) {

u.moves -= 100;
m.add_item_or_charges( p, critter.to_item() );
if( !critter.has_flag( MF_INTERIOR_AMMO ) ) {
for( std::pair<const itype_id, int> &ammodef : critter.ammo ) {
if( ammodef.second > 0 ) {
m.spawn_item( p.xy(), ammodef.first, 1, ammodef.second, calendar::turn );
}
}
}
remove_zombie( critter );
u.assign_activity( player_activity( disable_activity_actor( p,
disable_activity_actor::get_disable_turns(), false ) ) );
return true;
}
// Manhacks are special, they have their own menu here.
Expand All @@ -9543,25 +9531,10 @@ bool game::disable_robot( const tripoint &p )
} else {
choice = uilist( _( "Reprogram the manhack?" ), { _( "Follow me." ) } );
}
switch( choice ) {
case 0:
if( critter.has_effect( effect_docile ) ) {
critter.remove_effect( effect_docile );
if( one_in( 3 ) ) {
add_msg( _( "The %s hovers momentarily as it surveys the area." ),
critter.name() );
}
} else {
critter.add_effect( effect_docile, 1_turns, true );
if( one_in( 3 ) ) {
add_msg( _( "The %s lets out a whirring noise and starts to follow you." ),
critter.name() );
}
}
u.moves -= 100;
return true;
default:
break;

if( choice == 0 ) {
u.assign_activity( player_activity( disable_activity_actor( p,
disable_activity_actor::get_disable_turns(), true ) ) );
}
}
return false;
Expand Down
6 changes: 5 additions & 1 deletion src/monster.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ static const efftype_id effect_hit_by_player( "hit_by_player" );
static const efftype_id effect_in_pit( "in_pit" );
static const efftype_id effect_lightsnare( "lightsnare" );
static const efftype_id effect_monster_armor( "monster_armor" );
static const efftype_id effect_natures_commune( "natures_commune" );
static const efftype_id effect_no_sight( "no_sight" );
static const efftype_id effect_onfire( "onfire" );
static const efftype_id effect_pacified( "pacified" );
Expand All @@ -98,7 +99,7 @@ static const efftype_id effect_venom_player1( "venom_player1" );
static const efftype_id effect_venom_player2( "venom_player2" );
static const efftype_id effect_venom_weaken( "venom_weaken" );
static const efftype_id effect_webbed( "webbed" );
static const efftype_id effect_natures_commune( "natures_commune" );
static const efftype_id effect_worked_on( "worked_on" );

static const itype_id itype_corpse( "corpse" );
static const itype_id itype_milk( "milk" );
Expand Down Expand Up @@ -1924,6 +1925,9 @@ bool monster::move_effects( bool )
}
return false;
}
if( has_effect( effect_worked_on ) ) {
return false;
}

// If we ever get more effects that force movement on success this will need to be reworked to
// only trigger success effects if /all/ rolls succeed
Expand Down

0 comments on commit 9f0d77d

Please sign in to comment.