1 /*
2  * Copyright (C) 2002-2020 by the Widelands Development Team
3  *
4  * This program is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU General Public License
6  * as published by the Free Software Foundation; either version 2
7  * of the License, or (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software
16  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
17  *
18  */
19 
20 #ifndef WL_LOGIC_MAP_OBJECTS_TRIBES_PRODUCTIONSITE_H
21 #define WL_LOGIC_MAP_OBJECTS_TRIBES_PRODUCTIONSITE_H
22 
23 #include <memory>
24 
25 #include "base/macros.h"
26 #include "logic/map_objects/tribes/bill_of_materials.h"
27 #include "logic/map_objects/tribes/building.h"
28 #include "logic/map_objects/tribes/production_program.h"
29 #include "logic/map_objects/tribes/program_result.h"
30 #include "scripting/lua_table.h"
31 
32 namespace Widelands {
33 
34 class Request;
35 class Soldier;
36 class WorkerDescr;
37 
38 enum class FailNotificationType { kDefault, kFull };
39 
40 /**
41  * Every building that is part of the economics system is a production site.
42  *
43  * A production site has a worker.
44  * A production site can have one (or more) output wares types (in theory it
45  * should be possible to burn wares for some virtual result such as "mana", or
46  *  maybe even just for the fun of it, although that's not planned).
47  * A production site can have one (or more) input wares types. Every input
48  * wares type has an associated store.
49  */
50 class ProductionSiteDescr : public BuildingDescr {
51 public:
52 	friend struct ProductionProgram;  // To add animations
53 
54 	ProductionSiteDescr(const std::string& init_descname,
55 	                    const std::string& msgctxt,
56 	                    MapObjectType type,
57 	                    const LuaTable& t,
58 	                    const Tribes& tribes,
59 	                    const World& world);
60 	ProductionSiteDescr(const std::string& init_descname,
61 	                    const std::string& msgctxt,
62 	                    const LuaTable& t,
63 	                    const Tribes& tribes,
64 	                    const World& world);
65 
66 	Building& create_object() const override;
67 
68 	// List of wares to register having economy checks. Parsed by the tribes during postload and must
69 	// be nullptr after loading has finished
70 	std::set<DescriptionIndex>* ware_demand_checks() const;
71 	// List of workers to register having economy checks. Parsed by the tribes during postload and
72 	// must be nullptr after loading has finished
73 	std::set<DescriptionIndex>* worker_demand_checks() const;
74 	// Clear ware and worker demand check info
75 	void clear_demand_checks();
76 
nr_working_positions()77 	uint32_t nr_working_positions() const {
78 		uint32_t result = 0;
79 		for (const auto& working_pos : working_positions()) {
80 			result += working_pos.second;
81 		}
82 		return result;
83 	}
working_positions()84 	const BillOfMaterials& working_positions() const {
85 		return working_positions_;
86 	}
is_output_ware_type(const DescriptionIndex & i)87 	bool is_output_ware_type(const DescriptionIndex& i) const {
88 		return output_ware_types_.count(i);
89 	}
is_output_worker_type(const DescriptionIndex & i)90 	bool is_output_worker_type(const DescriptionIndex& i) const {
91 		return output_worker_types_.count(i);
92 	}
input_wares()93 	const BillOfMaterials& input_wares() const {
94 		return input_wares_;
95 	}
input_workers()96 	const BillOfMaterials& input_workers() const {
97 		return input_workers_;
98 	}
99 	using Output = std::set<DescriptionIndex>;
output_ware_types()100 	const Output& output_ware_types() const {
101 		return output_ware_types_;
102 	}
output_worker_types()103 	const Output& output_worker_types() const {
104 		return output_worker_types_;
105 	}
106 	const ProductionProgram* get_program(const std::string&) const;
107 	using Programs = std::map<std::string, std::unique_ptr<ProductionProgram>>;
programs()108 	const Programs& programs() const {
109 		return programs_;
110 	}
111 
out_of_resource_title()112 	const std::string& out_of_resource_title() const {
113 		return out_of_resource_title_;
114 	}
115 
out_of_resource_heading()116 	const std::string& out_of_resource_heading() const {
117 		return out_of_resource_heading_;
118 	}
119 
out_of_resource_message()120 	const std::string& out_of_resource_message() const {
121 		return out_of_resource_message_;
122 	}
123 
resource_not_needed_message()124 	const std::string& resource_not_needed_message() const {
125 		return resource_not_needed_message_;
126 	}
127 
out_of_resource_productivity_threshold()128 	uint32_t out_of_resource_productivity_threshold() const {
129 		return out_of_resource_productivity_threshold_;
130 	}
131 
highlight_overlapping_workarea_for(const std::string & n,bool * positive)132 	bool highlight_overlapping_workarea_for(const std::string& n, bool* positive) const {
133 		const auto it = highlight_overlapping_workarea_for_.find(n);
134 		if (it == highlight_overlapping_workarea_for_.end()) {
135 			return false;
136 		} else {
137 			*positive = it->second;
138 			return true;
139 		}
140 	}
141 
get_highlight_overlapping_workarea_for()142 	const std::map<std::string, bool>& get_highlight_overlapping_workarea_for() const {
143 		return highlight_overlapping_workarea_for_;
144 	}
145 
146 private:
147 	std::unique_ptr<std::set<DescriptionIndex>> ware_demand_checks_;
148 	std::unique_ptr<std::set<DescriptionIndex>> worker_demand_checks_;
149 	BillOfMaterials working_positions_;
150 	BillOfMaterials input_wares_;
151 	BillOfMaterials input_workers_;
152 	Output output_ware_types_;
153 	Output output_worker_types_;
154 	Programs programs_;
155 	std::string out_of_resource_title_;
156 	std::string out_of_resource_heading_;
157 	std::string out_of_resource_message_;
158 	std::string resource_not_needed_message_;
159 	int out_of_resource_productivity_threshold_;
160 	std::map<std::string, bool> highlight_overlapping_workarea_for_;
161 
162 	DISALLOW_COPY_AND_ASSIGN(ProductionSiteDescr);
163 };
164 
165 class ProductionSite : public Building {
166 	friend class MapBuildingdataPacket;
167 	friend struct ProductionProgram::ActReturn;
168 	friend struct ProductionProgram::ActReturn::WorkersNeedExperience;
169 	friend struct ProductionProgram::ActCall;
170 	friend struct ProductionProgram::ActCallWorker;
171 	friend struct ProductionProgram::ActSleep;
172 	friend struct ProductionProgram::ActAnimate;
173 	friend struct ProductionProgram::ActConsume;
174 	friend struct ProductionProgram::ActProduce;
175 	friend struct ProductionProgram::ActRecruit;
176 	friend struct ProductionProgram::ActMine;
177 	friend struct ProductionProgram::ActCheckSoldier;
178 	friend struct ProductionProgram::ActTrain;
179 	friend struct ProductionProgram::ActPlaySound;
180 	friend struct ProductionProgram::ActConstruct;
181 	MO_DESCR(ProductionSiteDescr)
182 
183 public:
184 	explicit ProductionSite(const ProductionSiteDescr& descr);
185 	~ProductionSite() override;
186 
187 	void log_general_info(const EditorGameBase&) const override;
188 
is_stopped()189 	bool is_stopped() const {
190 		return is_stopped_;
191 	}
192 	void set_stopped(bool);
193 
194 	struct WorkingPosition {
195 		WorkingPosition(Request* const wr = nullptr, Worker* const w = nullptr)
worker_requestWorkingPosition196 		   : worker_request(wr), worker(w) {
197 		}
198 		Request* worker_request;
199 		Worker* worker;
200 	};
201 
working_positions()202 	WorkingPosition const* working_positions() const {
203 		return working_positions_;
204 	}
205 
206 	virtual bool has_workers(DescriptionIndex targetSite, Game& game);
get_statistics_percent()207 	uint8_t get_statistics_percent() {
208 		return last_stat_percent_ / 10;
209 	}
210 
211 	// receives the duration of the last period and the result (true if something was produced)
212 	// and sets actual_percent_ to new value
213 	void update_actual_statistics(uint32_t, bool);
214 
get_actual_statistics()215 	uint8_t get_actual_statistics() {
216 		return actual_percent_ / 10;
217 	}
218 
production_result()219 	const std::string& production_result() const {
220 		return production_result_;
221 	}
222 
223 	// Production and worker programs set this to explain the current
224 	// state of the production. This string is shown as a tooltip
225 	// when the mouse hovers over the building.
set_production_result(const std::string & text)226 	void set_production_result(const std::string& text) {
227 		production_result_ = text;
228 	}
229 
230 	InputQueue& inputqueue(DescriptionIndex, WareWorker) override;
231 
232 	bool init(EditorGameBase&) override;
233 	void cleanup(EditorGameBase&) override;
234 	void act(Game&, uint32_t data) override;
235 
236 	void remove_worker(Worker&) override;
237 	int warp_worker(EditorGameBase&, const WorkerDescr& wd);
238 
239 	bool fetch_from_flag(Game&) override;
240 	bool get_building_work(Game&, Worker&, bool success) override;
241 
242 	void set_economy(Economy*, WareWorker) override;
243 
244 	using InputQueues = std::vector<InputQueue*>;
inputqueues()245 	const InputQueues& inputqueues() const {
246 		return input_queues_;
247 	}
248 
249 	const std::vector<Worker*>& workers() const;
250 
251 	bool can_start_working() const;
252 
253 	/// sends a message to the player e.g. if the building's resource can't be found
254 	void notify_player(Game& game,
255 	                   uint8_t minutes,
256 	                   FailNotificationType type = FailNotificationType::kDefault);
257 	void unnotify_player();
258 
259 	void set_default_anim(std::string);
260 
261 	const BuildingSettings* create_building_settings() const override;
262 
263 protected:
264 	void update_statistics_string(std::string* statistics) override;
265 
266 	void load_finish(EditorGameBase& egbase) override;
267 
268 protected:
269 	struct State {
270 		const ProductionProgram* program;  ///< currently running program
271 		size_t ip;                         ///< instruction pointer
272 		ProgramResult phase;               ///< micro-step index (instruction dependent)
273 		uint32_t flags;                    ///< pfXXX flags
274 
275 		/**
276 		 * Instruction-dependent additional data.
277 		 */
278 		/*@{*/
279 		ObjectPointer objvar;
280 		Coords coord;
281 		/*@}*/
282 
StateState283 		State()
284 		   : program(nullptr), ip(0), phase(ProgramResult::kNone), flags(0), coord(Coords::null()) {
285 		}
286 	};
287 
288 	Request& request_worker(DescriptionIndex);
289 	static void
290 	request_worker_callback(Game&, Request&, DescriptionIndex, Worker*, PlayerImmovable&);
291 
292 	/**
293 	 * Determine the next program to be run when the last program has finished.
294 	 * The default implementation starts program "work".
295 	 */
296 	virtual void find_and_start_next_program(Game&);
297 
top_state()298 	State& top_state() {
299 		assert(stack_.size());
300 		return *stack_.rbegin();
301 	}
get_state()302 	State* get_state() {
303 		return stack_.size() ? &*stack_.rbegin() : nullptr;
304 	}
305 	void program_act(Game&);
306 
307 	/// \param phase can be used to pass a value on to the next step in the
308 	/// program. For example if one step is a mine command, it can calculate
309 	/// how long it should take to mine, given the particular circumstances,
310 	/// and pass the result to the following animation command, to set the
311 	/// duration.
312 	void program_step(Game&, uint32_t delay = 10, ProgramResult phase = ProgramResult::kNone);
313 
314 	void program_start(Game&, const std::string& program_name);
315 	virtual void program_end(Game&, ProgramResult);
316 	virtual void train_workers(Game&);
317 
318 	void format_statistics_string();
319 	void try_start_working(Game&);
set_post_timer(int32_t const t)320 	void set_post_timer(int32_t const t) {
321 		post_timer_ = t;
322 	}
323 
324 protected:  // TrainingSite must have access to this stuff
325 	WorkingPosition* working_positions_;
326 
327 	int32_t fetchfromflag_;  ///< Number of wares to fetch from flag
328 
329 	/// If a program has ended with the result Failed or Skipped, that program may not
330 	/// start again until a certain time has passed. This is a map from program
331 	/// name to game time. When a program ends with the result Failed or Skipped, its name
332 	/// is added to this map, with the current game time. (When the program ends
333 	/// with any other result, its name is removed from the map.)
334 	using FailedSkippedPrograms = std::map<std::string, Time>;
335 	FailedSkippedPrograms failed_skipped_programs_;
336 
337 	using Stack = std::vector<State>;
338 	Stack stack_;           ///<  program stack
339 	bool program_timer_;    ///< execute next instruction based on pointer
340 	int32_t program_time_;  ///< timer time
341 	int32_t post_timer_;    ///< Time to schedule after ends
342 
343 	BillOfMaterials produced_wares_;
344 	BillOfMaterials recruited_workers_;
345 	InputQueues input_queues_;  ///< input queues for all inputs
346 	uint16_t last_stat_percent_;
347 	// integer 0-10000000, to be divided by 10000 to get a percent, to avoid float (target range:
348 	// 0-100)
349 	uint32_t actual_percent_;  // basically this is percent * 10 to avoid floats
350 	uint32_t last_program_end_time;
351 	bool is_stopped_;
352 	std::string default_anim_;  // normally "idle", "empty", if empty mine.
353 
354 private:
355 	enum class Trend { kUnchanged, kRising, kFalling };
356 	Trend trend_;
357 	std::string statistics_string_on_changed_statistics_;
358 	std::string production_result_;  // hover tooltip text
359 
360 	int32_t main_worker_;
361 
362 	DISALLOW_COPY_AND_ASSIGN(ProductionSite);
363 };
364 
365 /**
366  * Describes, how many wares of a certain ware can be stored in a house.
367  *
368  * This class will be extended to support ordering of certain wares directly or
369  * releasing some wares out of a building
370  */
371 struct Input {
InputInput372 	Input(const DescriptionIndex& Ware, uint8_t const Max) : ware_(Ware), max_(Max) {
373 	}
~InputInput374 	~Input() {
375 	}
376 
wareInput377 	DescriptionIndex ware() const {
378 		return ware_;
379 	}
maxInput380 	uint8_t max() const {
381 		return max_;
382 	}
383 
384 private:
385 	DescriptionIndex ware_;
386 	uint8_t max_;
387 };
388 
389 /**
390  * Note to be published when a production site is out of resources
391  */
392 // A note we're using to notify the AI
393 struct NoteProductionSiteOutOfResources {
394 	CAN_BE_SENT_AS_NOTE(NoteId::ProductionSiteOutOfResources)
395 
396 	// The production site that is out of resources.
397 	ProductionSite* ps;
398 
399 	// The player that owns the production site.
400 	Player* player;
401 
NoteProductionSiteOutOfResourcesNoteProductionSiteOutOfResources402 	NoteProductionSiteOutOfResources(ProductionSite* const init_ps, Player* init_player)
403 	   : ps(init_ps), player(init_player) {
404 	}
405 };
406 
407 }  // namespace Widelands
408 
409 #endif  // end of include guard: WL_LOGIC_MAP_OBJECTS_TRIBES_PRODUCTIONSITE_H
410