Skip to content

Commit

Permalink
#44459 disabling a robot is an activity with variable duration
Browse files Browse the repository at this point in the history
  • Loading branch information
Autoquark committed Oct 19, 2020
1 parent f8d184a commit 34e89f3
Show file tree
Hide file tree
Showing 7 changed files with 170 additions and 33 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." ]
},
{
"//": "ACTUAL PLAYER EFFECTS START HERE",
"type": "effect_type",
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 @@ -902,5 +902,13 @@
"activity_level": "LIGHT_EXERCISE",
"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"
}
]
109 changes: 109 additions & 0 deletions src/activity_actor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -55,21 +55,26 @@
#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_sleep( "sleep" );
static const efftype_id effect_stunned( "stunned" );
static const efftype_id effect_worked_on( "worked_on" );

static const itype_id itype_bone_human( "bone_human" );
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 std::string flag_MAG_DESTROY( "MAG_DESTROY" );
static const std::string flag_PERFECT_LOCKPICK( "PERFECT_LOCKPICK" );
static const std::string flag_RELOAD_AND_SHOOT( "RELOAD_AND_SHOOT" );

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 @@ -2138,6 +2143,109 @@ 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( efftype_id( "stunned" ) ) ) &&
!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 );
data.read( "moves_total", actor.moves_total );

return actor.clone();
}

namespace activity_actors
{

Expand All @@ -2149,6 +2257,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_DROP" ), &drop_activity_actor::deserialize },
{ activity_id( "ACT_GUNMOD_REMOVE" ), &gunmod_remove_activity_actor::deserialize },
{ activity_id( "ACT_HACKING" ), &hacking_activity_actor::deserialize },
Expand Down
33 changes: 33 additions & 0 deletions src/activity_actor.h
Original file line number Diff line number Diff line change
Expand Up @@ -847,6 +847,39 @@ class stash_activity_actor: public activity_actor
tripoint placement;
};

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 @@ -35,6 +35,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 @@ -1249,6 +1250,7 @@ static const std::unordered_set<efftype_id> hardcoded_movement_impairing = {{
effect_lightsnare,
effect_tied,
effect_webbed,
effect_worked_on,
}
};

Expand Down
41 changes: 8 additions & 33 deletions src/game.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9230,27 +9230,17 @@ bool game::disable_robot( const tripoint &p )
return false;
}
monster &critter = *mon_ptr;
if( ( critter.friendly == 0 && !critter.has_effect( effect_stunned ) ) ||
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 @@ -9261,25 +9251,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
4 changes: 4 additions & 0 deletions src/monster.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@ static const efftype_id effect_stunned( "stunned" );
static const efftype_id effect_supercharged( "supercharged" );
static const efftype_id effect_tied( "tied" );
static const efftype_id effect_webbed( "webbed" );
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 @@ -1771,6 +1772,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 34e89f3

Please sign in to comment.