1 #include "activity_scheduling_helper.h"
2 
3 #include <climits>
4 #include <cstdlib>
5 #include <list>
6 
7 #include "avatar.h"
8 #include "debug.h"
9 #include "item.h"
10 #include "map_helpers.h"
11 #include "player_activity.h"
12 #include "player_helpers.h"
13 #include "stomach.h"
14 #include "string_formatter.h"
15 
setup(avatar & guy) const16 void activity_schedule::setup( avatar &guy ) const
17 {
18     // Start our task, for however long the schedule says.
19     // This may be longer than the interval, which means that we
20     // never finish this task
21     if( actor ) {
22         guy.assign_activity( player_activity( *actor ), false );
23     } else {
24         guy.assign_activity( player_activity( act, calendar::INDEFINITELY_LONG, -1, INT_MIN,
25                                               "" ), false );
26     }
27 }
28 
do_turn(avatar & guy) const29 void activity_schedule::do_turn( avatar &guy ) const
30 {
31     // Do our activity
32     guy.activity.do_turn( guy );
33     // Ensure we never actually finish an activity
34     if( guy.activity.moves_left < 1000 ) {
35         guy.activity.moves_left = 4000;
36     }
37 }
38 
setup(avatar & guy) const39 void meal_schedule::setup( avatar &guy ) const
40 {
41     item eaten( food );
42     guy.consume( eaten );
43 }
44 
do_turn(avatar &) const45 void meal_schedule::do_turn( avatar & ) const
46 {
47 }
48 
instantaneous() const49 bool meal_schedule::instantaneous() const
50 {
51     return true;
52 }
53 
setup(avatar & guy) const54 void clear_guts::setup( avatar &guy ) const
55 {
56     guy.guts.empty();
57     guy.stomach.empty();
58 }
59 
do_turn(avatar &) const60 void clear_guts::do_turn( avatar & ) const
61 {
62 }
63 
instantaneous() const64 bool clear_guts::instantaneous() const
65 {
66     return true;
67 }
68 
setup(avatar & guy) const69 void sleep_schedule::setup( avatar &guy ) const
70 {
71     guy.fall_asleep();
72 }
73 
do_turn(avatar & guy) const74 void sleep_schedule::do_turn( avatar &guy ) const
75 {
76     if( !guy.has_effect( efftype_id( "sleep" ) ) ) {
77         debugmsg( "Woke up!" );
78     }
79 }
80 
do_activity(tasklist tasks)81 weariness_events do_activity( tasklist tasks )
82 {
83     // Clear stuff, ensure we're in the right place
84     clear_avatar();
85     clear_map();
86 
87     avatar &guy = get_avatar();
88     // Ensure we have enough light to see
89     item bag( "duffelbag" );
90     item light( "atomic_lamp" );
91     guy.worn.push_back( bag );
92     guy.i_add( light );
93     // How long we've been doing activities for
94     time_duration spent = 0_seconds;
95     // How weary we are starting
96     int weariness_lvl = guy.weariness_level();
97     weariness_events activity_log;
98     while( tasks.duration() > spent ) {
99         // What we're working on now
100         const schedule &task = tasks.next_task();
101         task.setup( guy );
102 
103         // How many turn's we've been at it
104         time_duration turns = 0_seconds;
105         while( turns <= task.interval && !task.instantaneous() ) {
106             // Advance a turn
107             calendar::turn += 1_turns;
108             turns += 1_seconds;
109             // Consume food, become weary, etc
110             guy.update_body();
111             // Start each turn with a fresh set of moves
112             guy.moves = 100;
113             task.do_turn( guy );
114         }
115         // Cancel our activity, now that we're done
116         guy.cancel_activity();
117         // How weary we are after ending this
118         int new_weariness = guy.weariness_level();
119         spent += task.interval;
120         tasks.advance( task.interval );
121         // If we're more weary than we were when we started, report it
122         if( new_weariness != weariness_lvl ) {
123             int new_weary = guy.weariness();
124             int new_thresh = guy.weary_threshold();
125             activity_log.log( weariness_lvl, new_weariness, spent,
126                               new_weary, new_thresh );
127             weariness_lvl = new_weariness;
128         }
129     }
130     return activity_log;
131 }
132 
next_task()133 const schedule &tasklist::next_task()
134 {
135     // Uh oh! We ran out of tasks!
136     if( cursor >= tasks.size() ) {
137         debugmsg( "Requested task when none existed!" );
138         if( tasks.empty() ) {
139             abort();
140         }
141         return *tasks[0].first;
142     }
143     return *tasks[cursor].first;
144 }
145 
advance(const time_duration & how_long)146 void tasklist::advance( const time_duration &how_long )
147 {
148     advanced += how_long;
149     if( advanced > tasks[cursor].second ) {
150         advanced = 0_seconds;
151         ++cursor;
152     }
153     // It's valid for us to finish our task and run out of them, putting
154     // the cursor out of bounds. But it's definitely not valid to go further
155     // (If we somehow made it here without complaints)
156     if( cursor > tasks.size() ) {
157         debugmsg( "Attempted to continue advancing once all tasks were finished!" );
158         cursor = 0;
159     }
160 }
161 
enschedule(const schedule & added,const time_duration & how_long)162 void tasklist::enschedule( const schedule &added, const time_duration &how_long )
163 {
164     tasks.insert( tasks.end(), { &added, how_long } );
165 }
166 
clear()167 void tasklist::clear()
168 {
169     cursor = 0;
170     advanced = 0_seconds;
171     tasks.clear();
172 }
173 
duration()174 time_duration tasklist::duration()
175 {
176     time_duration ret;
177     for( const std::pair<const schedule *, time_duration> &cursor : tasks ) {
178         ret += cursor.second;
179     }
180     return ret;
181 }
182 
log(const int old_level,const int new_level,const time_duration & when,const int new_weariness,const int new_threshold)183 void weariness_events::log( const int old_level, const int new_level, const time_duration &when,
184                             const int new_weariness, const int new_threshold )
185 {
186     weary_transition added;
187     added.from = old_level;
188     added.to = new_level;
189     added.minutes = to_minutes<int>( when );
190     added.new_weariness = new_weariness;
191     added.new_threshold = new_threshold;
192 
193     transitions.insert( transitions.end(), added );
194 }
195 
transition_minutes(const int from,const int to,const time_duration & around) const196 int weariness_events::transition_minutes( const int from, const int to,
197         const time_duration &around ) const
198 {
199     int around_mins = to_minutes<int>( around );
200     // first = change.minutes, second = difference from around_mins
201     std::pair<int, int> ret = {INT_MAX, INT_MAX};
202     for( const weary_transition &change : transitions ) {
203         int diff = std::abs( change.minutes - around_mins );
204         if( change.from == from && change.to == to && ret.second > diff ) {
205             ret = { change.minutes, diff };
206         }
207     }
208     return ret.first;
209 }
210 
have_weary_decrease() const211 bool weariness_events::have_weary_decrease() const
212 {
213     for( const weary_transition &change : transitions ) {
214         if( change.from > change.to ) {
215             return true;
216         }
217     }
218 
219     return false;
220 }
221 
summarize() const222 std::string weariness_events::summarize() const
223 {
224     std::string buffer;
225     for( const weary_transition &change : transitions ) {
226         buffer += string_format( "Transition: Weariness lvl from %d to %d at %d min (W %d Th %d)\n",
227                                  change.from, change.to, change.minutes,
228                                  change.new_weariness, change.new_threshold );
229     }
230     return buffer;
231 }
232 
empty() const233 bool weariness_events::empty() const
234 {
235     return transitions.empty();
236 }
237