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