1 #include "lib/widget/button.h"
2 #include "lib/widget/label.h"
3 #include "lib/widget/bar.h"
4 #include "lib/sound/audio_id.h"
5 #include "lib/sound/audio.h"
6 #include "manufacture.h"
7 #include "../mission.h"
8 #include "../qtscript.h"
9 
10 STRUCTURE *ManufactureController::highlightedFactory = nullptr;
11 
getFactoryOrNullptr(STRUCTURE * factory)12 FACTORY *getFactoryOrNullptr(STRUCTURE *factory)
13 {
14 	ASSERT_OR_RETURN(nullptr, StructIsFactory(factory), "Invalid factory pointer");
15 	return (FACTORY *)factory->pFunctionality;
16 }
17 
getProductionLoops(STRUCTURE * structure)18 static uint8_t getProductionLoops(STRUCTURE *structure)
19 {
20 	if (structure == nullptr)
21 	{
22 		return 0;
23 	}
24 
25 	auto psFactory = getFactoryOrNullptr(structure);
26 	ASSERT_NOT_NULLPTR_OR_RETURN(0, psFactory);
27 	return psFactory->productionLoops;
28 }
29 
makeProductionRunSizeLabel()30 static std::shared_ptr<W_LABEL> makeProductionRunSizeLabel()
31 {
32 	auto init = W_LABINIT();
33 	init.x = OBJ_TEXTX;
34 	init.y = OBJ_T1TEXTY;
35 	init.width = 16;
36 	init.height = 16;
37 	return std::make_shared<W_LABEL>(&init);
38 }
39 
updateData()40 void ManufactureController::updateData()
41 {
42 	updateFactoriesList();
43 	updateHighlighted();
44 	updateManufactureOptionsList();
45 }
46 
adjustFactoryProduction(DROID_TEMPLATE * manufactureOption,bool add)47 void ManufactureController::adjustFactoryProduction(DROID_TEMPLATE *manufactureOption, bool add)
48 {
49 	factoryProdAdjust(getHighlightedObject(), manufactureOption, add);
50 }
51 
adjustFactoryLoop(bool add)52 void ManufactureController::adjustFactoryLoop(bool add)
53 {
54 	factoryLoopAdjust(getHighlightedObject(), add);
55 }
56 
releaseFactoryProduction(STRUCTURE * structure)57 void ManufactureController::releaseFactoryProduction(STRUCTURE *structure)
58 {
59 	releaseProduction(structure, ModeQueue);
60 }
61 
cancelFactoryProduction(STRUCTURE * structure)62 void ManufactureController::cancelFactoryProduction(STRUCTURE *structure)
63 {
64 	if (!StructureIsManufacturingPending(structure))
65 	{
66 		return;
67 	}
68 
69 	if (!StructureIsOnHoldPending(structure))
70 	{
71 		holdProduction(structure, ModeQueue);
72 		return;
73 	}
74 
75 	cancelProduction(structure, ModeQueue);
76 	audio_PlayTrack(ID_SOUND_WINDOWCLOSE);
77 }
78 
startDeliveryPointPosition()79 void ManufactureController::startDeliveryPointPosition()
80 {
81 	auto factory = getHighlightedObject();
82 	ASSERT_NOT_NULLPTR_OR_RETURN(, factory);
83 
84 	// make sure that the factory isn't assigned to a commander
85 	assignFactoryCommandDroid(factory, nullptr);
86 	auto psFlag = FindFactoryDelivery(factory);
87 	if (psFlag)
88 	{
89 		startDeliveryPosition(psFlag);
90 	}
91 }
92 
compareFactories(STRUCTURE * a,STRUCTURE * b)93 static inline bool compareFactories(STRUCTURE *a, STRUCTURE *b)
94 {
95 	if (a == nullptr || b == nullptr)
96 	{
97 		return (a == nullptr) < (b == nullptr);
98 	}
99 	auto x = getFactoryOrNullptr(a);
100 	ASSERT_NOT_NULLPTR_OR_RETURN(false, x);
101 	auto y = getFactoryOrNullptr(b);
102 	ASSERT_NOT_NULLPTR_OR_RETURN(false, y);
103 	ASSERT_NOT_NULLPTR_OR_RETURN(false, x->psAssemblyPoint);
104 	ASSERT_NOT_NULLPTR_OR_RETURN(false, y->psAssemblyPoint);
105 	if (x->psAssemblyPoint->factoryType != y->psAssemblyPoint->factoryType)
106 	{
107 		return x->psAssemblyPoint->factoryType < y->psAssemblyPoint->factoryType;
108 	}
109 
110 	return x->psAssemblyPoint->factoryInc < y->psAssemblyPoint->factoryInc;
111 }
112 
updateFactoriesList()113 void ManufactureController::updateFactoriesList()
114 {
115 	factories.clear();
116 
117 	for (auto structure = interfaceStructList(); structure != nullptr; structure = structure->psNext)
118 	{
119 		if (structure->status == SS_BUILT && structure->died == 0 && StructIsFactory(structure))
120 		{
121 			factories.push_back(structure);
122 		}
123 	}
124 
125 	std::sort(factories.begin(), factories.end(), compareFactories);
126 }
127 
updateManufactureOptionsList()128 void ManufactureController::updateManufactureOptionsList()
129 {
130 	if (auto factory = getHighlightedObject())
131 	{
132 		stats = fillTemplateList(factory);
133 	}
134 	else
135 	{
136 		stats.clear();
137 	}
138 }
139 
getObjectStatsAt(size_t objectIndex) const140 DROID_TEMPLATE *ManufactureController::getObjectStatsAt(size_t objectIndex) const
141 {
142 	auto factory = getFactoryOrNullptr(getObjectAt(objectIndex));
143 	return factory == nullptr ? nullptr : factory->psSubject;
144 }
145 
refresh()146 void ManufactureController::refresh()
147 {
148 	updateData();
149 
150 	if (objectsSize() == 0)
151 	{
152 		closeInterface();
153 	}
154 }
155 
setHighlightedObject(BASE_OBJECT * object)156 void ManufactureController::setHighlightedObject(BASE_OBJECT *object)
157 {
158 	if (object == nullptr)
159 	{
160 		highlightedFactory = nullptr;
161 		return;
162 	}
163 
164 	auto factory = castStructure(object);
165 	ASSERT_OR_RETURN(, StructIsFactory(factory), "Invalid factory pointer");
166 	highlightedFactory = factory;
167 }
168 
169 class ManufactureObjectButton : public ObjectButton
170 {
171 private:
172 	typedef ObjectButton BaseWidget;
173 
174 protected:
ManufactureObjectButton()175 	ManufactureObjectButton() {}
176 
177 public:
make(const std::shared_ptr<ManufactureController> & controller,size_t objectIndex)178 	static std::shared_ptr<ManufactureObjectButton> make(const std::shared_ptr<ManufactureController> &controller, size_t objectIndex)
179 	{
180 		class make_shared_enabler: public ManufactureObjectButton {};
181 		auto widget = std::make_shared<make_shared_enabler>();
182 		widget->controller = controller;
183 		widget->objectIndex = objectIndex;
184 		widget->initialize();
185 		return widget;
186 	}
187 
jump()188 	void jump() override
189 	{
190 		if (!offWorldKeepLists)
191 		{
192 			BaseWidget::jump();
193 		}
194 	}
195 
clickPrimary()196 	void clickPrimary() override
197 	{
198 		controller->clearStructureSelection();
199 		controller->selectObject(controller->getObjectAt(objectIndex));
200 		jump();
201 		BaseStatsController::scheduleDisplayStatsForm(controller);
202 	}
203 
204 protected:
initialize()205 	void initialize()
206 	{
207 		attach(factoryNumberLabel = std::make_shared<W_LABEL>());
208 		factoryNumberLabel->setGeometry(OBJ_TEXTX, OBJ_B1TEXTY, 16, 16);
209 	}
210 
display(int xOffset,int yOffset)211 	void display(int xOffset, int yOffset) override
212 	{
213 		updateLayout();
214 		auto factory = controller->getObjectAt(objectIndex);
215 		ASSERT_NOT_NULLPTR_OR_RETURN(, factory);
216 		ASSERT_OR_RETURN(, !isDead(factory), "Factory is dead");
217 		displayIMD(Image(), ImdObject::Structure(factory), xOffset, yOffset);
218 		displayIfHighlight(xOffset, yOffset);
219 	}
220 
updateLayout()221 	void updateLayout() override
222 	{
223 		BaseWidget::updateLayout();
224 		auto factory = getFactoryOrNullptr(controller->getObjectAt(objectIndex));
225 		ASSERT_NOT_NULLPTR_OR_RETURN(, factory);
226 		if (factory->psAssemblyPoint == nullptr)
227 		{
228 			factoryNumberLabel->setString("");
229 			return;
230 		}
231 		factoryNumberLabel->setString(WzString::fromUtf8(astringf("%u", factory->psAssemblyPoint->factoryInc + 1)));
232 	}
233 
getTip()234 	std::string getTip() override
235 	{
236 		auto factory = controller->getObjectAt(objectIndex);
237 		ASSERT_NOT_NULLPTR_OR_RETURN("", factory);
238 		return getStatsName(factory->pStructureType);
239 	}
240 
getController() const241 	ManufactureController &getController() const override
242 	{
243 		return *controller.get();
244 	}
245 
246 private:
247 	std::shared_ptr<ManufactureController> controller;
248 	std::shared_ptr<W_LABEL> factoryNumberLabel;
249 };
250 
251 class ManufactureStatsButton: public StatsButton
252 {
253 private:
254 	typedef	StatsButton BaseWidget;
255 
256 protected:
ManufactureStatsButton()257 	ManufactureStatsButton() {}
258 
259 public:
make(const std::shared_ptr<ManufactureController> & controller,size_t objectIndex)260 	static std::shared_ptr<ManufactureStatsButton> make(const std::shared_ptr<ManufactureController> &controller, size_t objectIndex)
261 	{
262 		class make_shared_enabler: public ManufactureStatsButton {};
263 		auto widget = std::make_shared<make_shared_enabler>();
264 		widget->controller = controller;
265 		widget->objectIndex = objectIndex;
266 		widget->initialize();
267 		return widget;
268 	}
269 
270 protected:
display(int xOffset,int yOffset)271 	void display(int xOffset, int yOffset) override
272 	{
273 		updateLayout();
274 
275 		auto factory = controller->getObjectAt(objectIndex);
276 		auto production = getStats();
277 		auto productionPending = factory && StructureIsManufacturingPending(factory);
278 		auto objectImage = productionPending && production ? ImdObject::DroidTemplate(production): ImdObject::Component(nullptr);
279 
280 		displayIMD(Image(), objectImage, xOffset, yOffset);
281 
282 		if (productionPending && StructureIsOnHoldPending(factory))
283 		{
284 			iV_DrawImage(IntImages, ((realTime / 250) % 2) == 0 ? IMAGE_BUT0_DOWN : IMAGE_BUT_HILITE, xOffset + x(), yOffset + y());
285 		}
286 		else
287 		{
288 			displayIfHighlight(xOffset, yOffset);
289 		}
290 	}
291 
updateLayout()292 	void updateLayout() override
293 	{
294 		BaseWidget::updateLayout();
295 		auto factory = controller->getObjectAt(objectIndex);
296 		updateProgressBar(factory);
297 		updateProductionRunSizeLabel(factory, FactoryGetTemplate(StructureGetFactory(factory)));
298 	}
299 
300 private:
getStats()301 	DROID_TEMPLATE *getStats() override
302 	{
303 		return controller->getObjectStatsAt(objectIndex);
304 	}
305 
initialize()306 	void initialize()
307 	{
308 		addProgressBar();
309 		addProductionRunSizeLabel();
310 	}
311 
addProductionRunSizeLabel()312 	void addProductionRunSizeLabel()
313 	{
314 		attach(productionRunSizeLabel = makeProductionRunSizeLabel());
315 		productionRunSizeLabel->setFontColour(WZCOL_ACTION_PRODUCTION_RUN_TEXT);
316 	}
317 
updateProductionRunSizeLabel(STRUCTURE * factory,DROID_TEMPLATE * droidTemplate)318 	void updateProductionRunSizeLabel(STRUCTURE *factory, DROID_TEMPLATE *droidTemplate)
319 	{
320 		auto productionRemaining = getProduction(factory, droidTemplate).numRemaining();
321 		if (productionRemaining > 0 && factory && StructureIsManufacturingPending(factory))
322 		{
323 			productionRunSizeLabel->setString(WzString::fromUtf8(astringf("%d", productionRemaining)));
324 			productionRunSizeLabel->show();
325 		}
326 		else
327 		{
328 			productionRunSizeLabel->hide();
329 		}
330 	}
331 
updateProgressBar(STRUCTURE * factory)332 	void updateProgressBar(STRUCTURE *factory)
333 	{
334 		progressBar->hide();
335 
336 		ASSERT_NOT_NULLPTR_OR_RETURN(, factory);
337 		ASSERT_OR_RETURN(, !isDead(factory), "Factory is dead");
338 
339 		if (!StructureIsManufacturingPending(factory))
340 		{
341 			return;
342 		}
343 
344 		auto manufacture = StructureGetFactory(factory);
345 		ASSERT_NOT_NULLPTR_OR_RETURN(, manufacture);
346 
347 		if (manufacture->psSubject != nullptr && manufacture->buildPointsRemaining < calcTemplateBuild(manufacture->psSubject))
348 		{
349 			// Started production. Set the colour of the bar to yellow.
350 			int buildPointsTotal = calcTemplateBuild(FactoryGetTemplate(manufacture));
351 			int buildRate = manufacture->timeStartHold == 0 ? getBuildingProductionPoints(factory) : 0;
352 			formatTime(progressBar.get(), buildPointsTotal - manufacture->buildPointsRemaining, buildPointsTotal, buildRate, _("Construction Progress"));
353 		}
354 		else
355 		{
356 			// Not yet started production.
357 			int neededPower = checkPowerRequest(factory);
358 			int powerToBuild = manufacture->psSubject ? calcTemplatePower(manufacture->psSubject) : 0;
359 			formatPower(progressBar.get(), neededPower, powerToBuild);
360 		}
361 	}
362 
isHighlighted() const363 	bool isHighlighted() const override
364 	{
365 		auto factory = controller->getObjectAt(objectIndex);
366 		return factory && (factory->selected || factory == controller->getHighlightedObject());
367 	}
368 
clickPrimary()369 	void clickPrimary() override
370 	{
371 		auto factory = controller->getObjectAt(objectIndex);
372 		ASSERT_NOT_NULLPTR_OR_RETURN(, factory);
373 		controller->releaseFactoryProduction(factory);
374 		controller->clearStructureSelection();
375 		controller->selectObject(factory);
376 		BaseStatsController::scheduleDisplayStatsForm(controller);
377 	}
378 
clickSecondary()379 	void clickSecondary() override
380 	{
381 		auto factory = controller->getObjectAt(objectIndex);
382 		ASSERT_NOT_NULLPTR_OR_RETURN(, factory);
383 		controller->clearStructureSelection();
384 		controller->cancelFactoryProduction(factory);
385 		controller->setHighlightedObject(factory);
386 		controller->refresh();
387 		BaseStatsController::scheduleDisplayStatsForm(controller);
388 	}
389 
390 	std::shared_ptr<ManufactureController> controller;
391 	std::shared_ptr<W_LABEL> productionRunSizeLabel;
392 	size_t objectIndex;
393 };
394 
395 class ManufactureOptionButton: public StatsFormButton
396 {
397 private:
398 	typedef StatsFormButton BaseWidget;
399 	using BaseWidget::BaseWidget;
400 
401 public:
make(const std::shared_ptr<ManufactureController> & controller,size_t manufactureOptionIndex)402 	static std::shared_ptr<ManufactureOptionButton> make(const std::shared_ptr<ManufactureController> &controller, size_t manufactureOptionIndex)
403 	{
404 		class make_shared_enabler: public ManufactureOptionButton {};
405 		auto widget = std::make_shared<make_shared_enabler>();
406 		widget->controller = controller;
407 		widget->manufactureOptionIndex = manufactureOptionIndex;
408 		widget->initialize();
409 		return widget;
410 	}
411 
412 protected:
display(int xOffset,int yOffset)413 	void display(int xOffset, int yOffset) override
414 	{
415 		updateLayout();
416 		auto stat = getStats();
417 		ASSERT_NOT_NULLPTR_OR_RETURN(, stat);
418 
419 		displayIMD(Image(), ImdObject::DroidTemplate(stat), xOffset, yOffset);
420 		displayIfHighlight(xOffset, yOffset);
421 	}
422 
423 private:
getStats()424 	DROID_TEMPLATE *getStats() override
425 	{
426 		return controller->getStatsAt(manufactureOptionIndex);
427 	}
428 
initialize()429 	void initialize()
430 	{
431 		addCostBar();
432 		addProductionRunSizeLabel();
433 	}
434 
addProductionRunSizeLabel()435 	void addProductionRunSizeLabel()
436 	{
437 		attach(productionRunSizeLabel = makeProductionRunSizeLabel());
438 	}
439 
updateProductionRunSizeLabel(STRUCTURE * structure,DROID_TEMPLATE * droidTemplate)440 	void updateProductionRunSizeLabel(STRUCTURE *structure, DROID_TEMPLATE *droidTemplate)
441 	{
442 		auto production = getProduction(structure, droidTemplate);
443 		if (production.isValid())
444 		{
445 			auto productionLoops = getProductionLoops(structure);
446 			auto labelText = astringf(productionLoops > 0 ? "%d/%d" : "%d", production.numRemaining(), production.quantity);
447 			productionRunSizeLabel->setString(WzString::fromUtf8(labelText));
448 			productionRunSizeLabel->show();
449 		}
450 		else
451 		{
452 			productionRunSizeLabel->hide();
453 		}
454 	}
455 
isHighlighted() const456 	bool isHighlighted() const override
457 	{
458 		return controller->isHighlightedObjectStats(manufactureOptionIndex);
459 	}
460 
updateLayout()461 	void updateLayout() override
462 	{
463 		BaseWidget::updateLayout();
464 		if (isMouseOverWidget())
465 		{
466 			intSetShadowPower(getCost());
467 		}
468 		costBar->majorSize = std::min(100, (int32_t)(getCost() / POWERPOINTS_DROIDDIV));
469 		updateProductionRunSizeLabel(controller->getHighlightedObject(), getStats());
470 	}
471 
getCost()472 	uint32_t getCost() override
473 	{
474 		DROID_TEMPLATE* psTemplate = getStats();
475 		return psTemplate ? calcTemplatePower(psTemplate) : 0;
476 	}
477 
clickPrimary()478 	void clickPrimary() override
479 	{
480 		adjustFactoryProduction(true);
481 	}
482 
clickSecondary()483 	void clickSecondary() override
484 	{
485 		adjustFactoryProduction(false);
486 	}
487 
adjustFactoryProduction(bool add)488 	void adjustFactoryProduction(bool add)
489 	{
490 		auto clickedStats = controller->getStatsAt(manufactureOptionIndex);
491 		ASSERT_NOT_NULLPTR_OR_RETURN(, clickedStats);
492 
493 		auto controllerRef = controller;
494 		widgScheduleTask([add, clickedStats, controllerRef]() {
495 			controllerRef->adjustFactoryProduction(clickedStats, add);
496 		});
497 	}
498 
499 	std::shared_ptr<ManufactureController> controller;
500 	std::shared_ptr<W_LABEL> productionRunSizeLabel;
501 	size_t manufactureOptionIndex;
502 };
503 
504 class ManufactureObjectsForm: public ObjectsForm
505 {
506 private:
507 	typedef ObjectsForm BaseWidget;
508 	using BaseWidget::BaseWidget;
509 
510 public:
make(const std::shared_ptr<ManufactureController> & controller)511 	static std::shared_ptr<ManufactureObjectsForm> make(const std::shared_ptr<ManufactureController> &controller)
512 	{
513 		class make_shared_enabler: public ManufactureObjectsForm {};
514 		auto widget = std::make_shared<make_shared_enabler>();
515 		widget->controller = controller;
516 		widget->initialize();
517 		return widget;
518 	}
519 
makeStatsButton(size_t buttonIndex) const520 	std::shared_ptr<StatsButton> makeStatsButton(size_t buttonIndex) const override
521 	{
522 		return ManufactureStatsButton::make(controller, buttonIndex);
523 	}
524 
makeObjectButton(size_t buttonIndex) const525 	std::shared_ptr<ObjectButton> makeObjectButton(size_t buttonIndex) const override
526 	{
527 		return ManufactureObjectButton::make(controller, buttonIndex);
528 	}
529 
530 protected:
getController() const531 	ManufactureController &getController() const override
532 	{
533 		return *controller.get();
534 	}
535 
536 private:
537 	std::shared_ptr<ManufactureController> controller;
538 };
539 
540 class ManufactureStatsForm: public ObjectStatsForm
541 {
542 private:
543 	typedef ObjectStatsForm BaseWidget;
544 	using BaseWidget::BaseWidget;
545 
546 public:
make(const std::shared_ptr<ManufactureController> & controller)547 	static std::shared_ptr<ManufactureStatsForm> make(const std::shared_ptr<ManufactureController> &controller)
548 	{
549 		class make_shared_enabler: public ManufactureStatsForm {};
550 		auto widget = std::make_shared<make_shared_enabler>();
551 		widget->controller = controller;
552 		widget->initialize();
553 		return widget;
554 	}
555 
makeOptionButton(size_t buttonIndex) const556 	std::shared_ptr<StatsFormButton> makeOptionButton(size_t buttonIndex) const override
557 	{
558 		return ManufactureOptionButton::make(controller, buttonIndex);
559 	}
560 
561 protected:
getController() const562 	ManufactureController &getController() const override
563 	{
564 		return *controller.get();
565 	}
566 
567 private:
initialize()568 	void initialize() override
569 	{
570 		BaseWidget::initialize();
571 		addObsoleteButton();
572 		attach(std::make_shared<DeliveryPointButton>(controller));
573 		addLoopProductionWidgets();
574 	}
575 
addObsoleteButton()576 	void addObsoleteButton()
577 	{
578 		auto obsoleteButton = std::make_shared<MultipleChoiceButton>();
579 		attach(obsoleteButton);
580 		obsoleteButton->style |= WBUT_SECONDARY;
581 		obsoleteButton->setChoice(controller->shouldShowRedundantDesign());
582 		obsoleteButton->setImages(false, MultipleChoiceButton::Images(Image(IntImages, IMAGE_OBSOLETE_HIDE_UP), Image(IntImages, IMAGE_OBSOLETE_HIDE_UP), Image(IntImages, IMAGE_OBSOLETE_HIDE_HI)));
583 		obsoleteButton->setTip(false, _("Hiding Obsolete Tech"));
584 		obsoleteButton->setImages(true,  MultipleChoiceButton::Images(Image(IntImages, IMAGE_OBSOLETE_SHOW_UP), Image(IntImages, IMAGE_OBSOLETE_SHOW_UP), Image(IntImages, IMAGE_OBSOLETE_SHOW_HI)));
585 		obsoleteButton->setTip(true, _("Showing Obsolete Tech"));
586 		obsoleteButton->move(4 + Image(IntImages, IMAGE_FDP_UP).width() + 4, STAT_SLDY);
587 
588 		auto weakController = std::weak_ptr<ManufactureController>(controller);
589 		obsoleteButton->addOnClickHandler([weakController](W_BUTTON &button) {
590 			if (auto manufactureController = weakController.lock())
591 			{
592 				auto &obsoleteButton = static_cast<MultipleChoiceButton &>(button);
593 				auto newValue = !obsoleteButton.getChoice();
594 				manufactureController->setShouldShowRedundantDesign(newValue);
595 				obsoleteButton.setChoice(newValue);
596 			}
597 		});
598 	}
599 
addLoopProductionWidgets()600 	void addLoopProductionWidgets()
601 	{
602 		auto loopProductionButton = std::make_shared<LoopProductionButton>(controller);
603 		attach(loopProductionButton);
604 		loopProductionButton->move(STAT_SLDX + STAT_SLDWIDTH + 2, STAT_SLDY);
605 
606 		auto loopProductionLabel = std::make_shared<LoopProductionLabel>(controller);
607 		attach(loopProductionLabel);
608 		loopProductionLabel->setGeometry(loopProductionButton->x() - 15, loopProductionButton->y(), 12, 15);
609 	}
610 
611 	std::shared_ptr<ManufactureController> controller;
612 
613 	class DeliveryPointButton: public W_BUTTON
614 	{
615 	private:
616 		typedef W_BUTTON BaseWidget;
617 
618 	public:
DeliveryPointButton(const std::shared_ptr<ManufactureController> & controller)619 		DeliveryPointButton(const std::shared_ptr<ManufactureController> &controller): BaseWidget(), controller(controller)
620 		{
621 			style |= WBUT_SECONDARY;
622 			move(4, STAT_SLDY);
623 			setTip(_("Factory Delivery Point"));
624 
625 			auto weakController = std::weak_ptr<ManufactureController>(controller);
626 			addOnClickHandler([weakController](W_BUTTON &) {
627 				auto controller = weakController.lock();
628 				ASSERT_NOT_NULLPTR_OR_RETURN(, controller);
629 				controller->startDeliveryPointPosition();
630 			});
631 		}
632 
633 	protected:
display(int xOffset,int yOffset)634 		void display(int xOffset, int yOffset)
635 		{
636 			updateLayout();
637 			BaseWidget::display(xOffset, yOffset);
638 		}
639 
640 	private:
updateLayout()641 		void updateLayout()
642 		{
643 			auto factory = controller->getHighlightedObject();
644 			ASSERT_NOT_NULLPTR_OR_RETURN(, factory);
645 
646 			switch (factory->pStructureType->type)
647 			{
648 			default:
649 			case REF_FACTORY:
650 				setImages(Image(IntImages, IMAGE_FDP_UP), Image(IntImages, IMAGE_FDP_DOWN), Image(IntImages, IMAGE_FDP_HI));
651 				break;
652 			case REF_CYBORG_FACTORY:
653 				setImages(Image(IntImages, IMAGE_CDP_UP), Image(IntImages, IMAGE_CDP_DOWN), Image(IntImages, IMAGE_CDP_HI));
654 				break;
655 			case REF_VTOL_FACTORY:
656 				setImages(Image(IntImages, IMAGE_VDP_UP), Image(IntImages, IMAGE_VDP_DOWN), Image(IntImages, IMAGE_VDP_HI));
657 				break;
658 			}
659 		}
660 
661 		std::shared_ptr<ManufactureController> controller;
662 	};
663 
664 	class LoopProductionButton: public W_BUTTON
665 	{
666 	private:
667 		typedef W_BUTTON BaseWidget;
668 
669 	public:
LoopProductionButton(const std::shared_ptr<ManufactureController> & controller)670 		LoopProductionButton(const std::shared_ptr<ManufactureController> &controller): BaseWidget(), controller(controller)
671 		{
672 			style |= WBUT_SECONDARY;
673 			setImages(Image(IntImages, IMAGE_LOOP_UP), Image(IntImages, IMAGE_LOOP_DOWN), Image(IntImages, IMAGE_LOOP_HI));
674 			setTip(_("Loop Production"));
675 		}
676 
released(W_CONTEXT * psContext,WIDGET_KEY key)677 		void released(W_CONTEXT *psContext, WIDGET_KEY key) override
678 		{
679 			BaseWidget::released(psContext, key);
680 			controller->adjustFactoryLoop(key == WKEY_PRIMARY);
681 		}
682 
683 protected:
display(int xOffset,int yOffset)684 		void display(int xOffset, int yOffset) override
685 		{
686 			updateLayout();
687 			BaseWidget::display(xOffset, yOffset);
688 		}
689 
updateLayout()690 		void updateLayout()
691 		{
692 			setState(getProductionLoops(controller->getHighlightedObject()) == 0 ? 0: WBUT_CLICKLOCK);
693 		}
694 
695 	private:
696 		std::shared_ptr<ManufactureController> controller;
697 	};
698 
699 	class LoopProductionLabel: public W_LABEL
700 	{
701 	private:
702 		typedef W_LABEL BaseWidget;
703 
704 	public:
LoopProductionLabel(const std::shared_ptr<ManufactureController> & controller)705 		LoopProductionLabel(const std::shared_ptr<ManufactureController> &controller): BaseWidget(), controller(controller)
706 		{
707 		}
708 
709 	protected:
display(int xOffset,int yOffset)710 		void display(int xOffset, int yOffset) override
711 		{
712 			updateLayout();
713 			BaseWidget::display(xOffset, yOffset);
714 		}
715 
updateLayout()716 		void updateLayout()
717 		{
718 			auto productionLoops = getProductionLoops(controller->getHighlightedObject());
719 			if (productionLoops != lastProductionLoop)
720 			{
721 				lastProductionLoop = productionLoops;
722 				setString(WzString::fromUtf8(getNewString()));
723 			}
724 		}
725 
726 	private:
getNewString()727 		std::string getNewString()
728 		{
729 			switch (lastProductionLoop)
730 			{
731 			case 0:
732 				return "";
733 			case INFINITE_PRODUCTION:
734 				return "∞";
735 			default:
736 				return astringf("%u", lastProductionLoop + 1);
737 			}
738 		}
739 
740 		uint8_t lastProductionLoop = 0;
741 		std::shared_ptr<ManufactureController> controller;
742 	};
743 };
744 
showInterface()745 bool ManufactureController::showInterface()
746 {
747 	updateData();
748 	if (factories.empty())
749 	{
750 		return false;
751 	}
752 
753 	auto objectsForm = ManufactureObjectsForm::make(shared_from_this());
754 	psWScreen->psForm->attach(objectsForm);
755 	displayStatsForm();
756 	triggerEvent(TRIGGER_MENU_MANUFACTURE_UP);
757 	return true;
758 }
759 
makeStatsForm()760 std::shared_ptr<StatsForm> ManufactureController::makeStatsForm()
761 {
762 	return ManufactureStatsForm::make(shared_from_this());
763 }
764