Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Change milking to an activity, restore udders on load catchup #37523

Merged
merged 3 commits into from Feb 4, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
10 changes: 5 additions & 5 deletions data/json/effects.json
Original file line number Diff line number Diff line change
Expand Up @@ -1680,11 +1680,11 @@
"rating": "bad"
},
{
"type": "effect_type",
"id": "milked",
"name": [ "Milked" ],
"desc": [ "The creature has been partially or fully milked." ],
"max_duration": "1 d"
"type": "effect_type",
"id": "milked",
"name": [ "Milked" ],
"desc": [ "The creature has been partially or fully milked." ],
"max_duration": "1 d"
},
{
"type": "effect_type",
Expand Down
4 changes: 2 additions & 2 deletions data/json/monsters/mammal.json
Original file line number Diff line number Diff line change
Expand Up @@ -313,7 +313,7 @@
"melee_cut": 6,
"dodge": 2,
"armor_bash": 2,
"starting_ammo": { "milk_raw": 10 },
"starting_ammo": { "milk_raw": 40 },
"path_settings": { "max_dist": 10 },
"anger_triggers": [ "HURT" ],
"fear_triggers": [ "PLAYER_CLOSE" ],
Expand Down Expand Up @@ -1845,7 +1845,7 @@
"melee_dice_sides": 6,
"melee_cut": 2,
"dodge": 2,
"starting_ammo": { "milk_raw": 2 },
"starting_ammo": { "milk_raw": 5 },
"anger_triggers": [ ],
"harvest": "mammal_large_wool",
"reproduction": { "baby_monster": "mon_sheep_lamb", "baby_count": 1, "baby_timer": 275 },
Expand Down
10 changes: 10 additions & 0 deletions data/json/player_activities.json
Original file line number Diff line number Diff line change
Expand Up @@ -448,6 +448,16 @@
"based_on": "neither",
"no_resume": true
},
{
"id": "ACT_MILK",
"type": "activity_type",
"activity_level": "LIGHT_EXERCISE",
"verb": "milking an animal",
"suspendable": false,
"rooted": true,
"based_on": "time",
"no_resume": true
},
{
"id": "ACT_HOTWIRE_CAR",
"type": "activity_type",
Expand Down
40 changes: 40 additions & 0 deletions src/activity_handlers.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -224,6 +224,7 @@ activity_handlers::finish_functions = {
{ ACT_WASH, washing_finish },
{ ACT_HACKSAW, hacksaw_finish },
{ ACT_CHOP_TREE, chop_tree_finish },
{ ACT_MILK, milk_finish },
{ ACT_CHOP_LOGS, chop_logs_finish },
{ ACT_CHOP_PLANKS, chop_planks_finish },
{ ACT_JACKHAMMER, jackhammer_finish },
Expand Down Expand Up @@ -1274,6 +1275,45 @@ void activity_handlers::butcher_finish( player_activity *act, player *p )
resume_for_multi_activities( *p );
}

void activity_handlers::milk_finish( player_activity *act, player *p )
{
if( act->coords.empty() ) {
debugmsg( "milking activity with no position of monster stored" );
return;
}
const tripoint source_pos = g->m.getlocal( act->coords.at( 0 ) );
monster *source_mon = g->critter_at<monster>( source_pos );
if( source_mon == nullptr ) {
debugmsg( "could not find source creature for liquid transfer" );
return;
}
auto milked_item = source_mon->ammo.find( "milk_raw" );
if( milked_item == source_mon->ammo.end() ) {
debugmsg( "animal has no milk ammo type" );
return;
}
This conversation was marked as resolved.
Show resolved Hide resolved
if( milked_item->second <= 0 ) {
debugmsg( "started milking but udders are now empty before milking finishes" );
return;
}
item milk( milked_item->first, calendar::turn, milked_item->second );
milk.set_item_temperature( 311.75 );
if( liquid_handler::handle_liquid( milk, nullptr, 1, nullptr, nullptr, -1, source_mon ) ) {
milked_item->second = 0;
if( milk.charges > 0 ) {
milked_item->second = milk.charges;
} else {
p->add_msg_if_player( _( "The %s's udders run dry." ), source_mon->get_name() );
}
}
// if the monster was not manually tied up, but needed to be fixed in place temporarily then
// remove that now.
if( !act->str_values.empty() && act->str_values[0] == "temp_tie" ) {
source_mon->remove_effect( effect_tied );
}
act->set_to_null();
}

void activity_handlers::fill_liquid_do_turn( player_activity *act, player *p )
{
player_activity &act_ref = *act;
Expand Down
1 change: 1 addition & 0 deletions src/activity_handlers.h
Original file line number Diff line number Diff line change
Expand Up @@ -206,6 +206,7 @@ void pickaxe_finish( player_activity *act, player *p );
void reload_finish( player_activity *act, player *p );
void start_fire_finish( player_activity *act, player *p );
void train_finish( player_activity *act, player *p );
void milk_finish( player_activity *act, player *p );
void vehicle_finish( player_activity *act, player *p );
void start_engines_finish( player_activity *act, player *p );
void churn_finish( player_activity *act, player *p );
Expand Down
1 change: 1 addition & 0 deletions src/cata_string_consts.h
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ static const activity_id ACT_FERTILIZE_PLOT( "ACT_FERTILIZE_PLOT" );
static const activity_id ACT_FETCH_REQUIRED( "ACT_FETCH_REQUIRED" );
static const activity_id ACT_FIELD_DRESS( "ACT_FIELD_DRESS" );
static const activity_id ACT_FILL_LIQUID( "ACT_FILL_LIQUID" );
static const activity_id ACT_MILK( "ACT_MILK" );
static const activity_id ACT_FILL_PIT( "ACT_FILL_PIT" );
static const activity_id ACT_FIND_MOUNT( "ACT_FIND_MOUNT" );
static const activity_id ACT_FIRSTAID( "ACT_FIRSTAID" );
Expand Down
6 changes: 5 additions & 1 deletion src/game.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4111,7 +4111,11 @@ void game::monmove()
}

m.creature_in_field( critter );

if( calendar::once_every( 1_days ) ) {
critter.refill_udders();
critter.try_biosignature();
critter.try_reproduce();
codemime marked this conversation as resolved.
Show resolved Hide resolved
}
while( critter.moves > 0 && !critter.is_dead() && !critter.has_effect( effect_ridden ) ) {
critter.made_footstep = false;
// Controlled critters don't make their own plans
Expand Down
33 changes: 9 additions & 24 deletions src/monexamine.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -668,39 +668,24 @@ void monexamine::tie_or_untie( monster &z )

void monexamine::milk_source( monster &source_mon )
{
const auto milked_item = source_mon.type->starting_ammo.find( "milk_raw" );
if( milked_item == source_mon.type->starting_ammo.end() ) {
const auto milked_item = source_mon.ammo.find( "milk_raw" );
if( milked_item == source_mon.ammo.end() ) {
debugmsg( "%s is milkable but has no milk in its starting ammo!",
source_mon.get_name() );
return;
}
const int milk_per_day = milked_item->second;
const time_duration milking_freq = 1_days / milk_per_day;

int remaining_milk = milk_per_day;
if( source_mon.has_effect( effect_milked ) ) {
remaining_milk -= source_mon.get_effect_dur( effect_milked ) / milking_freq;
}

if( remaining_milk > 0 ) {
if( milked_item->second > 0 ) {
const int moves = to_moves<int>( time_duration::from_minutes( milked_item->second / 2 ) );
g->u.assign_activity( ACT_MILK, moves, -1 );
g->u.activity.coords.push_back( g->m.getabs( source_mon.pos() ) );
// pin the cow in place if it isn't already
bool temp_tie = !source_mon.has_effect( effect_tied );
if( temp_tie ) {
source_mon.add_effect( effect_tied, 1_turns, num_bp, true );
g->u.activity.str_values.push_back( "temp_tie" );
}

item milk( milked_item->first, calendar::turn, remaining_milk );
milk.set_item_temperature( 311.75 );
if( liquid_handler::handle_liquid( milk, nullptr, 1, nullptr, nullptr, -1, &source_mon ) ) {
add_msg( _( "You milk the %s." ), source_mon.get_name() );
int transferred_milk = remaining_milk - milk.charges;
source_mon.add_effect( effect_milked, milking_freq * transferred_milk );
g->u.mod_moves( -to_moves<int>( transferred_milk * 1_minutes / 5 ) );
}
if( temp_tie ) {
source_mon.remove_effect( effect_tied );
}
add_msg( _( "You milk the %s." ), source_mon.get_name() );
} else {
add_msg( _( "The %s's udders run dry." ), source_mon.get_name() );
add_msg( _( "The %s has no more milk." ), source_mon.get_name() );
}
}
24 changes: 24 additions & 0 deletions src/monster.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,7 @@ monster::monster()
upgrade_time = -1;
last_updated = 0;
biosig_timer = -1;
udder_timer = calendar::turn;
horde_attraction = MHA_NULL;
}

Expand Down Expand Up @@ -359,6 +360,28 @@ void monster::try_reproduce()
}
}

void monster::refill_udders()
{
if( !has_flag( MF_MILKABLE ) ) {
return;
}
const auto milked_item = type->starting_ammo.find( "milk_raw" );
auto current_milk = ammo.find( "milk_raw" );
if( milked_item == type->starting_ammo.end() || current_milk == ammo.end() ) {
debugmsg( "%s is milkable but has no milk in its starting ammo!", get_name() );
return;
}
if( current_milk->second == milked_item->second ) {
// already full up
return;
}
if( calendar::turn - udder_timer > 1_days ) {
// no point granularizing this really, you milk once a day.
current_milk->second = milked_item->second;
udder_timer = calendar::turn;
}
}

void monster::try_biosignature()
{
if( !biosignatures ) {
Expand Down Expand Up @@ -2793,6 +2816,7 @@ void monster::on_load()
try_upgrade( false );
try_reproduce();
try_biosignature();
refill_udders();

const time_duration dt = calendar::turn - last_updated;
last_updated = calendar::turn;
Expand Down
2 changes: 2 additions & 0 deletions src/monster.h
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,7 @@ class monster : public Creature
void try_upgrade( bool pin_time );
void try_reproduce();
void try_biosignature();
void refill_udders();
void spawn( const tripoint &p );
m_size get_size() const override;
units::mass get_weight() const override;
Expand Down Expand Up @@ -548,6 +549,7 @@ class monster : public Creature
cata::optional<time_point> baby_timer;
bool biosignatures;
cata::optional<time_point> biosig_timer;
time_point udder_timer;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The previous approach (adding an effect) was sound. Can it still be used?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Effects do not progress outside of the reality bubble.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Isn't it fast-forwarded when the player re-enters the reality bubble. If it's not, I'd consider that a bug.

monster_horde_attraction horde_attraction;
/** Found path. Note: Not used by monsters that don't pathfind! **/
std::vector<tripoint> path;
Expand Down
3 changes: 3 additions & 0 deletions src/savegame_json.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1926,6 +1926,8 @@ void monster::load( const JsonObject &data )
biosignatures = data.get_bool( "biosignatures", type->biosignatures );
biosig_timer = data.get_int( "biosig_timer", -1 );

data.read( "udder_timer", udder_timer );

horde_attraction = static_cast<monster_horde_attraction>( data.get_int( "horde_attraction", 0 ) );

data.read( "inv", inv );
Expand Down Expand Up @@ -2008,6 +2010,7 @@ void monster::store( JsonOut &json ) const
json.member( "baby_timer", baby_timer );
json.member( "biosignatures", biosignatures );
json.member( "biosig_timer", biosig_timer );
json.member( "udder_timer", udder_timer );

json.member( "summon_time_limit", summon_time_limit );

Expand Down