Skip to content

Commit

Permalink
Merge pull request #73081 from RenechCDDA/crop_decay
Browse files Browse the repository at this point in the history
Crops will overgrow/die if not harvested in time
  • Loading branch information
Maleclypse committed Apr 18, 2024
2 parents cd18044 + a4c021e commit 0f9fa01
Show file tree
Hide file tree
Showing 12 changed files with 134 additions and 52 deletions.
74 changes: 71 additions & 3 deletions data/json/furniture_and_terrain/furniture-domestic_plants.json
Original file line number Diff line number Diff line change
Expand Up @@ -71,14 +71,41 @@
]
}
},
{
"type": "furniture",
"id": "f_plant_unharvested_overgrown",
"looks_like": "t_grass_tall",
"name": "overgrown plant",
"description": "This patch of green used to be a cultivated field or some sort of monocrop agriculture. Neglected for too long, it has been overtaken by nature.",
"symbol": "#",
"color": "red",
"coverage": 50,
"move_cost_mod": 2,
"required_str": -1,
"flags": [
"PLANT",
"SEALED",
"TRANSPARENT",
"CONTAINER",
"NOITEM",
"TINY",
"DONT_REMOVE_ROTTEN",
"GROWTH_OVERGROWN",
"SMALL_HIDE"
],
"examine_action": "clear_overgrown",
"bash": { "str_min": 4, "str_max": 10, "sound": "crunch.", "sound_fail": "whish." },
"plant_data": { "transform": "f_plant_unharvested_overgrown", "base": "f_null" }
},
{
"type": "furniture",
"id": "f_plant_harvest",
"name": "harvestable plant",
"description": "This plant is fully grown and ready to be harvested. Identifying how to harvest it requires closer examination.",
"symbol": "#",
"color": "light_green",
"move_cost_mod": 0,
"coverage": 30,
"move_cost_mod": 1,
"required_str": -1,
"flags": [
"PLANT",
Expand All @@ -93,7 +120,7 @@
],
"examine_action": "harvest_plant_ex",
"bash": { "str_min": 4, "str_max": 10, "sound": "crunch.", "sound_fail": "whish." },
"plant_data": { "transform": "f_null", "base": "f_null" }
"plant_data": { "transform": "f_plant_unharvested_overgrown", "base": "f_null" }
},
{
"type": "furniture",
Expand Down Expand Up @@ -171,6 +198,47 @@
"plant_data": { "transform": "f_planter_seed" },
"examine_action": "dirtmound"
},
{
"type": "furniture",
"id": "f_planter_unharvested_overgrown",
"name": "planter with dead plants",
"description": "Somewhere beneath this heap of dying plants you can see the shape of wooden furniture. Left without care, the plants it used to nurture have shriveled and died.",
"symbol": "#",
"color": "red",
"looks_like": "f_planter",
"move_cost_mod": 3,
"required_str": -1,
"examine_action": "clear_overgrown",
"flags": [
"PLANT",
"SEALED",
"TRANSPARENT",
"CONTAINER",
"NOITEM",
"TINY",
"DONT_REMOVE_ROTTEN",
"GROWTH_OVERGROWN",
"SMALL_HIDE"
],
"deconstruct": {
"items": [
{ "item": "2x4", "count": [ 11, 12 ] },
{ "item": "nail", "charges": [ 30, 36 ] },
{ "item": "pebble", "charges": [ 180, 200 ] },
{ "item": "material_soil", "count": [ 70, 75 ] }
]
},
"bash": {
"str_min": 2,
"str_max": 4,
"sound": "rrrrip!",
"sound_fail": "brush.",
"sound_vol": 1,
"furn_set": "f_planter",
"items": [ { "item": "withered", "count": [ 1, 2 ] } ]
},
"plant_data": { "transform": "f_planter_unharvested_overgrown", "base": "f_planter" }
},
{
"type": "furniture",
"id": "f_planter_harvest",
Expand Down Expand Up @@ -214,7 +282,7 @@
{ "item": "twig", "count": [ 1, 5 ] }
]
},
"plant_data": { "transform": "f_planter", "base": "f_planter" }
"plant_data": { "transform": "f_planter_unharvested_overgrown", "base": "f_planter" }
},
{
"type": "furniture",
Expand Down
4 changes: 2 additions & 2 deletions data/json/furniture_and_terrain/furniture-terrains.json
Original file line number Diff line number Diff line change
Expand Up @@ -1366,7 +1366,7 @@
{ "item": "twig", "count": [ 1, 5 ] }
]
},
"plant_data": { "transform": "f_dirtmound_shallow", "base": "f_dirtmound_shallow" },
"plant_data": { "transform": "f_plant_unharvested_overgrown", "base": "f_dirtmound_shallow" },
"examine_action": "harvest_plant_ex"
},
{
Expand Down Expand Up @@ -1548,7 +1548,7 @@
{ "item": "twig", "count": [ 1, 5 ] }
]
},
"plant_data": { "transform": "f_dirtmound_pile", "base": "f_dirtmound_pile" },
"plant_data": { "transform": "f_plant_unharvested_overgrown", "base": "f_dirtmound_pile" },
"examine_action": "harvest_plant_ex"
},
{
Expand Down
2 changes: 2 additions & 0 deletions src/activity_handlers.h
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ enum class do_activity_reason : int {
NO_ZONE, // There is no required zone anymore
ALREADY_DONE, // the activity is done already ( maybe by someone else )
UNKNOWN_ACTIVITY, // This is probably an error - got to the end of function with no previous reason
NEEDS_CLEARING, // For farming - tile was neglected and became overgrown, can be cleared.
NEEDS_HARVESTING, // For farming - tile is harvestable now.
NEEDS_PLANTING, // For farming - tile can be planted
NEEDS_TILLING, // For farming - tile can be tilled
Expand Down Expand Up @@ -96,6 +97,7 @@ const std::vector<std::string> do_activity_reason_string = {
"NO_ZONE",
"ALREADY_DONE",
"UNKNOWN_ACTIVITY",
"NEEDS_CLEARING",
"NEEDS_HARVESTING",
"NEEDS_PLANTING",
"NEEDS_TILLING",
Expand Down
7 changes: 7 additions & 0 deletions src/activity_item_handling.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1302,6 +1302,10 @@ static activity_reason_info can_do_activity_there( const activity_id &act, Chara
const plot_options &options = dynamic_cast<const plot_options &>( zone.get_options() );
const itype_id seed = options.get_seed();

if( here.has_flag_furn( ter_furn_flag::TFLAG_GROWTH_OVERGROWN, src_loc ) ) {
return activity_reason_info::ok( do_activity_reason::NEEDS_CLEARING );
}

if( here.has_flag_furn( ter_furn_flag::TFLAG_GROWTH_HARVEST, src_loc ) ) {
map_stack items = here.i_at( src_loc );
const map_stack::iterator seed = std::find_if( items.begin(), items.end(), []( const item & it ) {
Expand Down Expand Up @@ -2982,6 +2986,9 @@ static bool generic_multi_activity_do(
here.has_flag_furn( ter_furn_flag::TFLAG_GROWTH_HARVEST, src_loc ) ) {
// TODO: fix point types
iexamine::harvest_plant( you, src_loc.raw(), true );
} else if( ( reason == do_activity_reason::NEEDS_CLEARING ) &&
here.has_flag_furn( ter_furn_flag::TFLAG_GROWTH_OVERGROWN, src_loc ) ) {
iexamine::clear_overgrown( you, src_loc.raw() );
} else if( reason == do_activity_reason::NEEDS_TILLING &&
here.has_flag( ter_furn_flag::TFLAG_PLOWABLE, src_loc ) &&
you.has_quality( qual_DIG, 1 ) && !here.has_furn( src_loc ) ) {
Expand Down
21 changes: 20 additions & 1 deletion src/iexamine.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2626,6 +2626,24 @@ std::list<item> iexamine::get_harvest_items( const itype &type, const int plant_
return result;
}

// Cleaning up plants that have decayed and overgrown
void iexamine::clear_overgrown( Character &you, const tripoint &examp )
{
map &here = get_map();

if( !here.has_flag_furn( ter_furn_flag::TFLAG_GROWTH_OVERGROWN, examp ) ) {
debugmsg( "clear_overgrown called on tile which is not an overgrown crop!" );
return;
}

player_activity act( ACT_HARVEST, to_moves<int>( 60_seconds ) );
you.assign_activity( act );
here.i_clear( examp );
here.furn_set( examp, here.furn( examp )->plant->base );
you.add_msg_if_player( m_neutral,
_( "You pull up what's left of the planted crop and trample the rest." ) );
}

// Only harvest, used for autoforaging
void iexamine::harvest_plant_ex( Character &you, const tripoint &examp )
{
Expand Down Expand Up @@ -2714,7 +2732,7 @@ void iexamine::harvest_plant( Character &you, const tripoint &examp, bool from_a
}
here.add_item_or_charges( you.pos(), i );
}
here.furn_set( examp, furn_str_id( here.furn( examp )->plant->transform ) );
here.furn_set( examp, furn_str_id( here.furn( examp )->plant->base ) );
you.add_msg_if_player( m_neutral, _( "You harvest the plant." ) );
}
}
Expand Down Expand Up @@ -7083,6 +7101,7 @@ iexamine_functions iexamine_functions_from_string( const std::string &function_n
{ "harvest_furn", &iexamine::harvest_furn },
{ "harvest_ter_nectar", &iexamine::harvest_ter_nectar },
{ "harvest_ter", &iexamine::harvest_ter },
{ "clear_overgrown", &iexamine::clear_overgrown },
{ "harvest_plant_ex", &iexamine::harvest_plant_ex },
{ "harvested_plant", &iexamine::harvested_plant },
{ "shrub_marloss", &iexamine::shrub_marloss },
Expand Down
1 change: 1 addition & 0 deletions src/iexamine.h
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,7 @@ std::list<item> get_harvest_items( const itype &type, int plant_count,
std::vector<seed_tuple> get_seed_entries( const std::vector<item *> &seed_inv );
int query_seed( const std::vector<seed_tuple> &seed_entries );
void plant_seed( Character &you, const tripoint &examp, const itype_id &seed_id );
void clear_overgrown( Character &you, const tripoint &examp );
void harvest_plant_ex( Character &you, const tripoint &examp );
void harvest_plant( Character &you, const tripoint &examp, bool from_activity );
void fertilize_plant( Character &you, const tripoint &tile, const itype_id &fertilizer );
Expand Down
6 changes: 3 additions & 3 deletions src/item.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -14145,7 +14145,7 @@ bool item::is_seed() const
return !!type->seed;
}

time_duration item::get_plant_epoch() const
time_duration item::get_plant_epoch( int num_epochs ) const
{
if( !type->seed ) {
return 0_turns;
Expand All @@ -14154,9 +14154,9 @@ time_duration item::get_plant_epoch() const
// the default in-game season length to give
// more accuracy for longer season lengths
// Also note that seed->grow is the time it takes from seeding to harvest, this is
// divided by 3 to get the time it takes from one plant state to the next.
// divided by number of growth stages (usually 3) to get the time it takes from one plant state to the next.
// TODO: move this into the islot_seed
return type->seed->grow * calendar::season_ratio() / 3;
return type->seed->grow * calendar::season_ratio() / num_epochs;
}

std::string item::get_plant_name() const
Expand Down
4 changes: 2 additions & 2 deletions src/item.h
Original file line number Diff line number Diff line change
Expand Up @@ -2039,10 +2039,10 @@ class item : public visitable
*/
bool is_seed() const;
/**
* Time it takes to grow from one stage to another. There are 4 plant stages:
* Time it takes to grow from one stage to another. There are normally 4 plant stages:
* seed, seedling, mature and harvest. Non-seed items return 0.
*/
time_duration get_plant_epoch() const;
time_duration get_plant_epoch( int num_epochs = 3 ) const;
/**
* The name of the plant as it appears in the various informational menus. This should be
* translated. Returns an empty string for non-seed items.
Expand Down
64 changes: 23 additions & 41 deletions src/map.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8390,58 +8390,40 @@ void map::grow_plant( const tripoint &p )
furn_set( p, furn_str_id::NULL_ID() );
return;
}
const time_duration plantEpoch = seed->get_plant_epoch();
if( seed->age() >= plantEpoch * furn.plant->growth_multiplier &&
!furn.has_flag( ter_furn_flag::TFLAG_GROWTH_HARVEST ) ) {
if( seed->age() < plantEpoch * 2 ) {
if( has_flag_furn( ter_furn_flag::TFLAG_GROWTH_SEEDLING, p ) ) {
return;
}

// Remove fertilizer if any
map_stack::iterator fertilizer = std::find_if( items.begin(), items.end(), []( const item & it ) {
return it.has_flag( flag_FERTILIZER );
} );
if( fertilizer != items.end() ) {
items.erase( fertilizer );
// TODO: this should probably be read from the seed's data. But for now, everything uses exactly this many growth stages.
std::map<ter_furn_flag, int> plant_epochs;
plant_epochs[ter_furn_flag::TFLAG_GROWTH_SEEDLING] = 1;
plant_epochs[ter_furn_flag::TFLAG_GROWTH_MATURE] = 2;
plant_epochs[ter_furn_flag::TFLAG_GROWTH_HARVEST] = 3;

const time_duration base_epoch_duration = seed->get_plant_epoch( plant_epochs.size() );
const time_duration epoch_duration = base_epoch_duration * furn.plant->growth_multiplier;
if( seed->age() >= epoch_duration ) {
const int epoch_age = seed->age() / epoch_duration;
int current_epoch = 0;
for( std::pair<const ter_furn_flag, int> pair : plant_epochs ) {
if( has_flag_furn( pair.first, p ) ) {
current_epoch = pair.second;
break;
}
}
const int epochs_to_advance = epoch_age - current_epoch;

rotten_item_spawn( *seed, p );
furn_set( p, furn_str_id( furn.plant->transform ) );
} else if( seed->age() < plantEpoch * 3 * furn.plant->growth_multiplier ) {
if( has_flag_furn( ter_furn_flag::TFLAG_GROWTH_MATURE, p ) ) {
return;
}
for( int i = 0; i < epochs_to_advance; i++ ) {
// Remove fertilizer if any
map_stack::iterator fertilizer = std::find_if( items.begin(), items.end(), []( const item & it ) {
return it.has_flag( flag_FERTILIZER );
} );
if( fertilizer != items.end() ) {
items.erase( fertilizer );
}
// spawn appropriate amount of rot_spawn, equivalent to number of times we iterate this loop
rotten_item_spawn( *seed, p );
//You've skipped the seedling stage so roll monsters twice
if( !has_flag_furn( ter_furn_flag::TFLAG_GROWTH_SEEDLING, p ) ) {
rotten_item_spawn( *seed, p );
}
furn_set( p, furn_str_id( furn.plant->transform ) );

} else {
//You've skipped two stages so roll monsters two times
if( has_flag_furn( ter_furn_flag::TFLAG_GROWTH_SEEDLING, p ) ) {
rotten_item_spawn( *seed, p );
rotten_item_spawn( *seed, p );
//One stage change
} else if( has_flag_furn( ter_furn_flag::TFLAG_GROWTH_MATURE, p ) ) {
rotten_item_spawn( *seed, p );
//Goes from seed to harvest in one check
} else {
rotten_item_spawn( *seed, p );
rotten_item_spawn( *seed, p );
rotten_item_spawn( *seed, p );
}
furn_set( p, furn_str_id( furn.plant->transform ) );
// Get an updated reference to the furniture each time we go through this loop, to make sure we transform each step in turn
const furn_t &current_furn = this->furn( p ).obj();
furn_set( p, furn_str_id( current_furn.plant->transform ) );
}

}
}

Expand Down
1 change: 1 addition & 0 deletions src/mapdata.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -208,6 +208,7 @@ std::string enum_to_string<ter_furn_flag>( ter_furn_flag data )
case ter_furn_flag::TFLAG_CONSOLE: return "CONSOLE";
case ter_furn_flag::TFLAG_PLANTABLE: return "PLANTABLE";
case ter_furn_flag::TFLAG_GROWTH_HARVEST: return "GROWTH_HARVEST";
case ter_furn_flag::TFLAG_GROWTH_OVERGROWN: return "GROWTH_OVERGROWN";
case ter_furn_flag::TFLAG_MOUNTABLE: return "MOUNTABLE";
case ter_furn_flag::TFLAG_RAMP_END: return "RAMP_END";
case ter_furn_flag::TFLAG_FLOWER: return "FLOWER";
Expand Down
1 change: 1 addition & 0 deletions src/mapdata.h
Original file line number Diff line number Diff line change
Expand Up @@ -268,6 +268,7 @@ enum class ter_furn_flag : int {
TFLAG_CONSOLE,
TFLAG_PLANTABLE,
TFLAG_GROWTH_HARVEST,
TFLAG_GROWTH_OVERGROWN,
TFLAG_MOUNTABLE,
TFLAG_RAMP_END,
TFLAG_FLOWER,
Expand Down
1 change: 1 addition & 0 deletions tools/spell_checker/dictionary.txt
Original file line number Diff line number Diff line change
Expand Up @@ -3916,6 +3916,7 @@ Monegasque
Mongolian
Monica
Monoco
monocrop
monocrystalline
monogyna
monometal
Expand Down

0 comments on commit 0f9fa01

Please sign in to comment.