1 /*-------------------------------------------------------------------------------
2 
3 	BARONY
4 	File: mechanism.cpp
5 	Desc: implements all mechanism related code
6 
7 	Copyright 2013-2016 (c) Turning Wheel LLC, all rights reserved.
8 	See LICENSE for details.
9 
10 -------------------------------------------------------------------------------*/
11 
12 #include "main.hpp"
13 #include "game.hpp"
14 #include "stat.hpp"
15 #include "entity.hpp"
16 #include "sound.hpp"
17 #include "net.hpp"
18 #include "player.hpp"
19 #include "scores.hpp"
20 
21 //Circuits do not overlap. They connect to all their neighbors, allowing for circuits to interfere with eachother.
22 
actCircuit(Entity * my)23 void actCircuit(Entity* my)
24 {
25 	my->flags[PASSABLE] = true; // these should ALWAYS be passable. No exceptions
26 }
27 
circuitPowerOn()28 void Entity::circuitPowerOn()
29 {
30 	if (behavior == actCircuit && circuit_status && circuit_status != CIRCUIT_ON)
31 	{
32 		circuit_status = CIRCUIT_ON; //On.
33 		//TODO: Play a sound effect?
34 
35 		updateCircuitNeighbors(); //This'll power on all neighbors that are unpowered.
36 	}
37 }
38 
circuitPowerOff()39 void Entity::circuitPowerOff()
40 {
41 	if (behavior == actCircuit && circuit_status != CIRCUIT_OFF)
42 	{
43 		circuit_status = CIRCUIT_OFF; //Off.
44 		//TODO: Play a sound effect?
45 
46 		updateCircuitNeighbors(); //Send the poweroff signal to all neighbors.
47 	}
48 }
49 
updateCircuitNeighbors()50 void Entity::updateCircuitNeighbors()
51 {
52 	//Send the power on or off signal to all neighboring circuits & mechanisms.
53 	list_t* neighbors = getPowerableNeighbors(); //Grab a list of all neighboring circuits and mechanisms.
54 
55 	if (neighbors)
56 	{
57 		node_t* node = NULL;
58 		for (node = neighbors->first; node != NULL; node = node->next)
59 		{
60 			if (node->element)
61 			{
62 				Entity* powerable = (Entity*)(node->element);
63 
64 				if (powerable)
65 				{
66 					if (powerable->behavior == actCircuit)
67 					{
68 						(circuit_status > 1) ? powerable->circuitPowerOn() : powerable->circuitPowerOff();
69 					}
70 					else if ( powerable->behavior == &::actSignalTimer )
71 					{
72 						int x1 = static_cast<int>(this->x / 16);
73 						int x2 = static_cast<int>(powerable->x / 16);
74 						int y1 = static_cast<int>(this->y / 16);
75 						int y2 = static_cast<int>(powerable->y / 16);
76 						//messagePlayer(0, "%d, %d, %d, %d", x1, x2, y1, y2);
77 						switch ( powerable->signalInputDirection )
78 						{
79 							case 0: // west
80 								if ( (x1 + 1) == x2 )
81 								{
82 									(circuit_status > 1) ? powerable->mechanismPowerOn() : powerable->mechanismPowerOff();
83 								}
84 								break;
85 							case 1: // south
86 								if ( (y1 - 1) == y2 )
87 								{
88 									(circuit_status > 1) ? powerable->mechanismPowerOn() : powerable->mechanismPowerOff();
89 								}
90 								break;
91 							case 2: // east
92 								if ( (x1 - 1) == x2 )
93 								{
94 									(circuit_status > 1) ? powerable->mechanismPowerOn() : powerable->mechanismPowerOff();
95 								}
96 								break;
97 							case 3: // north
98 								if ( (y1 + 1) == y2 )
99 								{
100 									(circuit_status > 1) ? powerable->mechanismPowerOn() : powerable->mechanismPowerOff();
101 								}
102 								break;
103 							default:
104 								break;
105 						}
106 					}
107 					else
108 					{
109 						(circuit_status > 1) ? powerable->mechanismPowerOn() : powerable->mechanismPowerOff();
110 					}
111 				}
112 			}
113 		}
114 
115 		//Free the list.
116 		list_FreeAll(neighbors);
117 		free(neighbors);
118 	}
119 }
120 
121 
122 
123 
124 
125 
126 
127 
128 
129 //I don't want an explicit mechanism entity.
130 //Rather, I want normal object entities, such as traps and whatever else will interact with circuits to have the variables necessary to function as a mechanism.
131 //Hence, no actMechanism(). Each individual entity will handle that in its own act() function.
132 
mechanismPowerOn()133 void Entity::mechanismPowerOn()
134 {
135 	if (skill)
136 	{
137 		circuit_status = CIRCUIT_ON;    //Power on.
138 	}
139 }
140 
mechanismPowerOff()141 void Entity::mechanismPowerOff()
142 {
143 	if (skill)
144 	{
145 		circuit_status = CIRCUIT_OFF;    //Power off.
146 	}
147 }
148 
149 
150 
151 
152 
153 
154 
155 
156 
157 
158 /*
159  * skill[0] = power status.
160  * * 0 = off
161  * * 1 = on
162  */
163 
164 
actSwitch(Entity * my)165 void actSwitch(Entity* my)
166 {
167 	//TODO: If powered on, and it detects a depowered neighbor, it should pulse that neighbor to turn on.
168 	//Thus, this function needs to be called periodically.
169 	//This is so that if a switch goes off and there's another switch on the network, the entire network shuts off regardless of the other switch's status.
170 	//So then when that second switch's actSwitch() comes up, and if it's on, it'll repower the entire network -- which will stay powered until ALL connected switches go off.
171 	my->flags[PASSABLE] = true; // these should ALWAYS be passable. No exceptions
172 
173 	if ( multiplayer != CLIENT )
174 	{
175 		int i = 0;
176 		for (i = 0; i < MAXPLAYERS; ++i)
177 		{
178 			if ( (i == 0 && selectedEntity == my) || (client_selected[i] == my) )
179 			{
180 				if (inrange[i])   //Act on it only if the player (or monster, if/when this is changed to support monster interaction?) is in range.
181 				{
182 					messagePlayer(i, language[1110]);
183 					playSoundEntity(my, 56, 64);
184 					my->toggleSwitch();
185 				}
186 			}
187 		}
188 		if ( my->isInteractWithMonster() )
189 		{
190 			Entity* monsterInteracting = uidToEntity(my->interactedByMonster);
191 			if ( monsterInteracting && monsterInteracting->getMonsterTypeFromSprite() == GYROBOT )
192 			{
193 				Entity* leader = monsterInteracting->monsterAllyGetPlayerLeader();
194 				if ( leader )
195 				{
196 					achievementObserver.playerAchievements[monsterInteracting->monsterAllyIndex].checkPathBetweenObjects(leader, my, AchievementObserver::BARONY_ACH_LEVITANT_LACKEY);
197 				}
198 			}
199 			my->toggleSwitch();
200 			my->clearMonsterInteract();
201 		}
202 
203 		if (my->skill[0])
204 		{
205 			//Power on any neighbors that don't have power.
206 			my->switchUpdateNeighbors();
207 			//TODO: Alternatively, instead of using CPU cycles on this, have the recursive network shutdown alert any switches connected to it that are powered on that it's shutting down, so that they can repower the network come next frame.
208 		}
209 	}
210 	else
211 	{
212 		my->flags[NOUPDATE] = true;
213 	}
214 
215 	// Rotate the switch when it is on/off.
216 	if ( my->skill[0] )
217 	{
218 		if ( my->roll > -PI / 4 )
219 		{
220 			my->roll -= std::max<real_t>((my->roll + PI / 4) / 2, .05);
221 		}
222 		else
223 		{
224 			my->roll = -PI / 4;
225 		}
226 	}
227 	else
228 	{
229 		if ( my->roll < PI / 4 )
230 		{
231 			my->roll += std::max<real_t>(-(my->roll - PI / 4) / 2, .05);
232 		}
233 		else
234 		{
235 			my->roll = PI / 4;
236 		}
237 	}
238 }
239 
actSwitchWithTimer(Entity * my)240 void actSwitchWithTimer(Entity* my)
241 {
242 	my->flags[PASSABLE] = true; // these should ALWAYS be passable. No exceptions
243 
244 	if ( multiplayer != CLIENT )
245 	{
246 		int i = 0;
247 		for ( i = 0; i < MAXPLAYERS; ++i )
248 		{
249 			if ( (i == 0 && selectedEntity == my) || (client_selected[i] == my) )
250 			{
251 				// server/client has clicked on the entity.
252 				if ( inrange[i] )   //Act on it only if the player (or monster, if/when this is changed to support monster interaction?) is in range.
253 				{
254 					switch ( my->leverStatus )
255 					{
256 						case 0:
257 							messagePlayer(i, language[2360]);
258 							break;
259 						case 1:
260 							messagePlayer(i, language[2361]);
261 							break;
262 						case 2:
263 							messagePlayer(i, language[2362]);
264 							break;
265 						default:
266 							messagePlayer(i, language[2363]);
267 							break;
268 					}
269 
270 					if ( my->leverStatus < 3 )
271 					{
272 						++my->leverStatus;
273 						playSoundEntity(my, 248, 64);
274 						serverUpdateEntitySkill(my, 1);
275 						if ( my->leverStatus == 3 )
276 						{
277 							playSoundEntity(my, 56, 64);
278 							my->toggleSwitch();
279 						}
280 					}
281 				}
282 			}
283 		}
284 
285 		if ( my->leverStatus == 4 )
286 		{
287 			//Power on any neighbors that don't have power.
288 			my->switchUpdateNeighbors();
289 			//TODO: Alternatively, instead of using CPU cycles on this, have the recursive network shutdown alert any switches connected to it that are powered on that it's shutting down, so that they can repower the network come next frame.
290 		}
291 	}
292 	else
293 	{
294 		my->flags[NOUPDATE] = true;
295 	}
296 
297 	// Rotate the switch when it is on/off.
298 	if ( my->leverStatus == 0 )
299 	{
300 		if ( my->roll > -PI / 4 )
301 		{
302 			my->roll -= std::max<real_t>((my->roll + PI / 4) / 2, .05);
303 		}
304 		else
305 		{
306 			my->roll = -PI / 4;
307 		}
308 	}
309 	else if (my->leverStatus == 1 ) // 1/3 of the way up
310 	{
311 		if ( my->roll < -PI / 12 )
312 		{
313 			my->roll += std::max<real_t>(-(my->roll + PI / 12) / 8, .02);
314 		}
315 		else
316 		{
317 			my->roll = -PI / 12;
318 		}
319 	}
320 	else if ( my->leverStatus == 2 ) // 2/3 of the way up
321 	{
322 		if ( my->roll < PI / 12 )
323 		{
324 			my->roll += std::max<real_t>(-(my->roll - PI / 12) / 8, .02);
325 		}
326 		else
327 		{
328 			my->roll = PI / 12;
329 		}
330 	}
331 	else if ( my->leverStatus == 3 ) // all the way up
332 	{
333 		if ( my->roll < PI / 4 )
334 		{
335 			my->roll += std::max<real_t>(-(my->roll - PI / 4) / 4, .02);
336 		}
337 		else
338 		{
339 			my->roll = PI / 4;
340 			if ( multiplayer != CLIENT )
341 			{
342 				my->leverStatus = 4;
343 				serverUpdateEntitySkill(my, 1);
344 			}
345 		}
346 	}
347 	else if ( my->leverStatus == 4 ) // ticking down
348 	{
349 		if ( my->roll > -PI / 12 )
350 		{
351 			my->roll -= (PI / 3) / static_cast<real_t>(my->leverTimerTicks); // move slowly towards 2/3rds of the resting point
352 			if ( my->ticks % 10 == 0 )
353 			{
354 				playSoundEntityLocal(my, 247, 32);
355 			}
356 		}
357 		else
358 		{
359 			my->roll = -PI / 12;
360 			if ( multiplayer != CLIENT )
361 			{
362 				playSoundEntity(my, 56, 64);
363 				my->leverStatus = 0;
364 				serverUpdateEntitySkill(my, 1);
365 				my->toggleSwitch();
366 			}
367 		}
368 	}
369 }
370 
371 #define TRAP_ON my->skill[0]
actTrap(Entity * my)372 void actTrap(Entity* my)
373 {
374 	// activates circuit when certain entities are occupying its tile
375 	node_t* node;
376 	Entity* entity;
377 	bool somebodyonme = false;
378 	my->flags[PASSABLE] = true; // these should ALWAYS be passable. No exceptions
379 
380 	if ( TRAP_ON )
381 	{
382 		my->switchUpdateNeighbors();
383 	}
384 
385 	std::vector<list_t*> entLists = TileEntityList.getEntitiesWithinRadiusAroundEntity(my, 2);
386 	for ( std::vector<list_t*>::iterator it = entLists.begin(); it != entLists.end() && !somebodyonme; ++it )
387 	{
388 		list_t* currentList = *it;
389 		for ( node = currentList->first; node != nullptr; node = node->next )
390 		{
391 			entity = (Entity*)node->element;
392 			if ( entity->behavior == &actPlayer || entity->behavior == &actItem
393 				|| entity->behavior == &actMonster || entity->behavior == &actBoulder
394 				|| entity->behavior == &actBomb || entity->behavior == &actDecoyBox )
395 			{
396 				if ( floor(entity->x / 16) == floor(my->x / 16) && floor(entity->y / 16) == floor(my->y / 16) )
397 				{
398 					somebodyonme = true;
399 					if ( !TRAP_ON )
400 					{
401 						my->toggleSwitch();
402 						TRAP_ON = 1;
403 					}
404 					break;
405 				}
406 			}
407 		}
408 	}
409 	if ( !somebodyonme )
410 	{
411 		if ( TRAP_ON )
412 		{
413 			my->toggleSwitch();
414 			TRAP_ON = 0;
415 		}
416 	}
417 }
418 
419 #define TRAPPERMANENT_ON my->skill[0]
actTrapPermanent(Entity * my)420 void actTrapPermanent(Entity* my)
421 {
422 	// activates circuit when certain entities are occupying its tile
423 	// unlike actTrap, never deactivates
424 	node_t* node;
425 	Entity* entity;
426 	my->flags[PASSABLE] = true; // these should ALWAYS be passable. No exceptions
427 
428 	if ( !strcmp(map.name, "Boss") )
429 	{
430 		for ( node = map.creatures->first; node != nullptr; node = node->next ) //Only looking at players? Don't search full map.entities.
431 		{
432 			entity = (Entity*)node->element;
433 			if ( entity->behavior == &actPlayer )
434 			{
435 				if ( entity->x < 26 * 16 || entity->y < 6 * 16 || entity->y >= 26 * 16 )   // hardcoded, I know...
436 				{
437 					return;
438 				}
439 			}
440 		}
441 	}
442 	else if ( !strcmp(map.name, "Sanctum") )
443 	{
444 		if ( my->x > 50 * 16 )
445 		{
446 			// exit gate, act abnormal!
447 			bool monsterAlive = false;
448 			for ( node = map.creatures->first; node != nullptr; node = node->next )
449 			{
450 				entity = (Entity*)node->element;
451 				if ( entity->behavior == &actMonster && (entity->getRace() == LICH_FIRE || entity->getRace() == LICH_ICE) )
452 				{
453 					monsterAlive = true;
454 				}
455 			}
456 			if ( !monsterAlive )
457 			{
458 				// turn on when safe.
459 				TRAPPERMANENT_ON = 1;
460 			}
461 		}
462 		else if ( my->x < 27 * 16 )
463 		{
464 			// entry gates, act normal!
465 		}
466 		else
467 		{
468 			// fight trigger plates, wait for players to assemble.
469 			for ( node = map.creatures->first; node != nullptr; node = node->next ) //Only looking at players? Don't search full map.entities.
470 			{
471 				entity = (Entity*)node->element;
472 				if ( entity->behavior == &actPlayer )
473 				{
474 					if ( entity->x < 29 * 16 )   // hardcoded, I know...
475 					{
476 						return;
477 					}
478 				}
479 			}
480 		}
481 	}
482 
483 	if ( TRAPPERMANENT_ON )
484 	{
485 		my->switchUpdateNeighbors();
486 	}
487 	else
488 	{
489 		if ( my->skill[1] == 1 )
490 		{
491 			// skip checking for entities.
492 			return;
493 		}
494 		std::vector<list_t*> entLists = TileEntityList.getEntitiesWithinRadiusAroundEntity(my, 2);
495 		for ( std::vector<list_t*>::iterator it = entLists.begin(); it != entLists.end(); ++it )
496 		{
497 			list_t* currentList = *it;
498 			for ( node = currentList->first; node != nullptr; node = node->next )
499 			{
500 				entity = (Entity*)node->element;
501 				if ( entity->behavior == &actPlayer || entity->behavior == &actItem
502 					|| entity->behavior == &actMonster || entity->behavior == &actBoulder
503 					|| entity->behavior == &actBomb || entity->behavior == &actDecoyBox )
504 				{
505 					if ( floor(entity->x / 16) == floor(my->x / 16) && floor(entity->y / 16) == floor(my->y / 16) )
506 					{
507 						my->toggleSwitch();
508 						TRAPPERMANENT_ON = 1;
509 					}
510 				}
511 			}
512 		}
513 	}
514 }
515 
516 //This is called when the switch is toggled by the player.
toggleSwitch()517 void Entity::toggleSwitch()
518 {
519 	//If off, power on and send poweron signal. If on, power off and send poweroff signal.
520 
521 	switch_power = (switch_power == SWITCH_UNPOWERED);
522 	serverUpdateEntitySkill(this, 0);
523 
524 	//(my->skill[0]) ? my->sprite = 171 : my->sprite = 168;
525 
526 	list_t* neighbors = getPowerableNeighbors(); //Grab a list of all neighboring circuits and mechanisms.
527 
528 	if (neighbors)
529 	{
530 		node_t* node = NULL;
531 		for (node = neighbors->first; node != NULL; node = node->next)
532 		{
533 			if (node->element)
534 			{
535 				Entity* powerable = (Entity*)(node->element);
536 
537 				if (powerable)
538 				{
539 					if (powerable->behavior == actCircuit)
540 					{
541 						(switch_power) ? powerable->circuitPowerOn() : powerable->circuitPowerOff();
542 					}
543 					else if ( powerable->behavior == &::actSignalTimer )
544 					{
545 						int x1 = static_cast<int>(this->x / 16);
546 						int x2 = static_cast<int>(powerable->x / 16);
547 						int y1 = static_cast<int>(this->y / 16);
548 						int y2 = static_cast<int>(powerable->y / 16);
549 						//messagePlayer(0, "%d, %d, %d, %d", x1, x2, y1, y2);
550 						switch ( powerable->signalInputDirection )
551 						{
552 							case 0: // west
553 								if ( (x1 + 1) == x2 )
554 								{
555 									(switch_power) ? powerable->mechanismPowerOn() : powerable->mechanismPowerOff();
556 								}
557 								break;
558 							case 1: // south
559 								if ( (y1 - 1) == y2 )
560 								{
561 									(switch_power) ? powerable->mechanismPowerOn() : powerable->mechanismPowerOff();
562 								}
563 								break;
564 							case 2: // east
565 								if ( (x1 - 1) == x2 )
566 								{
567 									(switch_power) ? powerable->mechanismPowerOn() : powerable->mechanismPowerOff();
568 								}
569 								break;
570 							case 3: // north
571 								if ( (y1 + 1) == y2 )
572 								{
573 									(switch_power) ? powerable->mechanismPowerOn() : powerable->mechanismPowerOff();
574 								}
575 								break;
576 							default:
577 								break;
578 						}
579 					}
580 					else
581 					{
582 						(switch_power) ? powerable->mechanismPowerOn() : powerable->mechanismPowerOff();
583 					}
584 				}
585 			}
586 		}
587 
588 		list_FreeAll(neighbors); //Free the list.
589 		free(neighbors);
590 	}
591 }
592 
switchUpdateNeighbors()593 void Entity::switchUpdateNeighbors()
594 {
595 	list_t* neighbors = getPowerableNeighbors(); //Grab a list of all neighboring circuits and mechanisms.
596 
597 	if (neighbors)
598 	{
599 		node_t* node = NULL;
600 		for (node = neighbors->first; node != NULL; node = node->next)
601 		{
602 			if (node->element)
603 			{
604 				Entity* powerable = (Entity*)(node->element);
605 
606 				if (powerable)
607 				{
608 					if (powerable->circuit_status != CIRCUIT_ON)
609 					{
610 						if (powerable->behavior == actCircuit)
611 						{
612 							powerable->circuitPowerOn();
613 						}
614 						else if ( powerable->behavior == &::actSignalTimer )
615 						{
616 							int x1 = static_cast<int>(this->x / 16);
617 							int x2 = static_cast<int>(powerable->x / 16);
618 							int y1 = static_cast<int>(this->y / 16);
619 							int y2 = static_cast<int>(powerable->y / 16);
620 							//messagePlayer(0, "%d, %d, %d, %d", x1, x2, y1, y2);
621 							switch ( powerable->signalInputDirection )
622 							{
623 								case 0: // west
624 									if ( (x1 + 1) == x2 )
625 									{
626 										powerable->mechanismPowerOn();
627 									}
628 									break;
629 								case 1: // south
630 									if ( (y1 - 1) == y2 )
631 									{
632 										powerable->mechanismPowerOn();
633 									}
634 									break;
635 								case 2: // east
636 									if ( (x1 - 1) == x2 )
637 									{
638 										powerable->mechanismPowerOn();
639 									}
640 									break;
641 								case 3: // north
642 									if ( (y1 + 1) == y2 )
643 									{
644 										powerable->mechanismPowerOn();
645 									}
646 									break;
647 								default:
648 									break;
649 							}
650 						}
651 						else
652 						{
653 							powerable->mechanismPowerOn();
654 						}
655 					}
656 				}
657 			}
658 		}
659 
660 		list_FreeAll(neighbors); //Free the list.
661 		free(neighbors);
662 	}
663 }
664 
getPowerablesOnTile(int x,int y,list_t ** list)665 void getPowerablesOnTile(int x, int y, list_t** list)
666 {
667 
668 	//Take the return value of checkTileForEntity() and sort that list for powerables.
669 	//if (entity->powerable == true)
670 	//And then free the list returned by checkTileForEntity.
671 
672 	//Right. First, grab all the entities on the tile.
673 	list_t* entities = NULL;
674 	entities = checkTileForEntity(x, y);
675 
676 	if (!entities)
677 	{
678 		return;    //No use continuing, got no entities.
679 	}
680 
681 	node_t* node = NULL;
682 	node_t* node2 = NULL;
683 	//Loop through the list of entities.
684 	for (node = entities->first; node != NULL; node = node->next)
685 	{
686 		if (node->element)
687 		{
688 			Entity* entity = (Entity*) node->element;
689 			//Check if the entity is powerable.
690 			if (entity && entity->skill[28])   //If skill 28 = 0, the entity is not a powerable.
691 			{
692 				//If this is the first powerable found, the list needs to be created.
693 				if (!(*list))
694 				{
695 					*list = (list_t*) malloc(sizeof(list_t));
696 					(*list)->first = NULL;
697 					(*list)->last = NULL;
698 				}
699 
700 				//Add the current entity to it.
701 				node2 = list_AddNodeLast(*list);
702 				node2->element = entity;
703 				node2->deconstructor = &emptyDeconstructor;
704 			}
705 		}
706 	}
707 
708 	/*if (entities)
709 	{
710 		list_FreeAll(entities);
711 		free(entities);
712 	}*/
713 
714 	//return return_val;
715 }
716 
getPowerableNeighbors()717 list_t* Entity::getPowerableNeighbors()
718 {
719 	list_t* return_val = NULL;
720 
721 
722 	int tx = x / 16;
723 	int ty = y / 16;
724 
725 	getPowerablesOnTile(tx, ty, &return_val); //Check current tile
726 	getPowerablesOnTile(tx - 1, ty, &return_val); //Check tile to the left.
727 	getPowerablesOnTile(tx + 1, ty, &return_val); //Check tile to the right.
728 	getPowerablesOnTile(tx, ty - 1, &return_val); //Check tile up.
729 	getPowerablesOnTile(tx, ty + 1, &return_val); //Check tile down.
730 	//getPowerablesOnTile(tx - 1, ty - 1, &return_val); //Check tile diagonal up left.
731 	//getPowerablesOnTile(tx + 1, ty - 1, &return_val); //Check tile diagonal up right.
732 	//getPowerablesOnTile(tx - 1, ty + 1, &return_val); //Check tile diagonal down left.
733 	//getPowerablesOnTile(tx + 1, ty + 1, &return_val); //Check tile diagonal down right.
734 
735 	return return_val;
736 }
737 
actSoundSource(Entity * my)738 void actSoundSource(Entity* my)
739 {
740 	if ( !my )
741 	{
742 		return;
743 	}
744 
745 	my->actSoundSource();
746 }
747 
actSoundSource()748 void Entity::actSoundSource()
749 {
750 #ifdef SOUND
751 	if ( multiplayer == CLIENT )
752 	{
753 		return;
754 	}
755 
756 	if ( soundSourceDelay > 0 && soundSourceDelayCounter == 0 )
757 	{
758 		soundSourceDelayCounter = soundSourceDelay;
759 	}
760 
761 	if ( circuit_status == CIRCUIT_ON )
762 	{
763 		// received power
764 		if ( soundSourceDelayCounter > 0 )
765 		{
766 			--soundSourceDelayCounter;
767 			if ( soundSourceDelayCounter != 0 )
768 			{
769 				return;
770 			}
771 		}
772 		if ( !soundSourceFired )
773 		{
774 			soundSourceFired = 1;
775 			if ( soundSourceToPlay >= 0 && soundSourceToPlay < numsounds )
776 			{
777 				if ( soundSourceOrigin == 1 )
778 				{
779 					for ( int c = 0; c < MAXPLAYERS; ++c )
780 					{
781 						playSoundPlayer(c, soundSourceToPlay, soundSourceVolume);
782 					}
783 				}
784 				else
785 				{
786 					playSoundEntity(this, soundSourceToPlay, soundSourceVolume);
787 				}
788 			}
789 		}
790 	}
791 	else if ( circuit_status == CIRCUIT_OFF )
792 	{
793 		if ( soundSourceDelay > 0 )
794 		{
795 			soundSourceDelayCounter = soundSourceDelay;
796 		}
797 		if ( soundSourceFired && !soundSourceLatchOn )
798 		{
799 			soundSourceFired = 0;
800 		}
801 	}
802 #endif // SOUND
803 }
804 
805 #define SIGNALTIMER_DELAYCOUNT skill[6]
806 #define SIGNALTIMER_TIMERCOUNT skill[7]
807 #define SIGNALTIMER_REPEATCOUNT skill[8]
808 
actSignalTimer(Entity * my)809 void actSignalTimer(Entity* my)
810 {
811 	if ( !my )
812 	{
813 		return;
814 	}
815 
816 	my->actSignalTimer();
817 }
818 
actSignalTimer()819 void Entity::actSignalTimer()
820 {
821 	if ( multiplayer == CLIENT )
822 	{
823 		return;
824 	}
825 
826 	int tx = x / 16;
827 	int ty = y / 16;
828 	list_t *neighbors = nullptr;
829 	bool updateNeighbors = false;
830 
831 	if ( circuit_status == CIRCUIT_ON || signalTimerLatchInput == 2 )
832 	{
833 		if ( signalTimerLatchInput == 1 )
834 		{
835 			signalTimerLatchInput = 2;
836 		}
837 		if ( signalActivateDelay > 0 && SIGNALTIMER_DELAYCOUNT == 0 && switch_power == SWITCH_UNPOWERED )
838 		{
839 			SIGNALTIMER_DELAYCOUNT = signalActivateDelay;
840 		}
841 		if ( SIGNALTIMER_DELAYCOUNT > 0 )
842 		{
843 			--SIGNALTIMER_DELAYCOUNT;
844 			if ( SIGNALTIMER_DELAYCOUNT != 0 )
845 			{
846 				return;
847 			}
848 		}
849 		if ( switch_power == SWITCH_UNPOWERED )
850 		{
851 			switch_power = SWITCH_POWERED;
852 			updateNeighbors = true;
853 			if ( signalTimerRepeatCount > 0 && SIGNALTIMER_REPEATCOUNT <= 0 )
854 			{
855 				SIGNALTIMER_REPEATCOUNT = signalTimerRepeatCount;
856 			}
857 		}
858 		else if ( signalTimerInterval > 0 )
859 		{
860 			if ( SIGNALTIMER_TIMERCOUNT == 0 )
861 			{
862 				SIGNALTIMER_TIMERCOUNT = signalTimerInterval;
863 			}
864 			if ( SIGNALTIMER_TIMERCOUNT > 0 )
865 			{
866 				--SIGNALTIMER_TIMERCOUNT;
867 				if ( SIGNALTIMER_TIMERCOUNT != 0 )
868 				{
869 					return;
870 				}
871 			}
872 			if ( switch_power == SWITCH_POWERED )
873 			{
874 				switch_power = 2;
875 				updateNeighbors = true;
876 			}
877 			else
878 			{
879 				if ( signalTimerRepeatCount > 0 )
880 				{
881 					if ( SIGNALTIMER_REPEATCOUNT > 1 )
882 					{
883 						switch_power = SWITCH_POWERED;
884 						updateNeighbors = true;
885 						--SIGNALTIMER_REPEATCOUNT;
886 					}
887 				}
888 				else
889 				{
890 					switch_power = SWITCH_POWERED;
891 					updateNeighbors = true;
892 				}
893 			}
894 		}
895 	}
896 	else if ( circuit_status == CIRCUIT_OFF && signalTimerLatchInput == 0 )
897 	{
898 		if ( switch_power != SWITCH_UNPOWERED )
899 		{
900 			switch_power = SWITCH_UNPOWERED;
901 			updateNeighbors = true;
902 		}
903 		if ( signalTimerInterval > 0 )
904 		{
905 			SIGNALTIMER_TIMERCOUNT = signalTimerInterval;
906 		}
907 		if ( signalTimerRepeatCount > 0 )
908 		{
909 			SIGNALTIMER_REPEATCOUNT = signalTimerRepeatCount;
910 		}
911 	}
912 
913 	if ( updateNeighbors )
914 	{
915 		switch ( signalInputDirection )
916 		{
917 			case 0: // west
918 				getPowerablesOnTile(tx + 1, ty, &neighbors); //Check tile to the left.
919 				break;
920 			case 1: // south
921 				getPowerablesOnTile(tx, ty - 1, &neighbors); //Check tile to the north.
922 				break;
923 			case 2: // east
924 				getPowerablesOnTile(tx - 1, ty, &neighbors); //Check tile to the right.
925 				break;
926 			case 3: // north
927 				getPowerablesOnTile(tx, ty + 1, &neighbors); //Check tile to the south
928 				break;
929 		}
930 		if ( neighbors != nullptr )
931 		{
932 			node_t* node = nullptr;
933 			for ( node = neighbors->first; node != nullptr; node = node->next )
934 			{
935 				if ( node->element )
936 				{
937 					Entity* powerable = (Entity*)(node->element);
938 
939 					if ( powerable )
940 					{
941 						if ( powerable->behavior == actCircuit )
942 						{
943 							(switch_power == SWITCH_POWERED) ? powerable->circuitPowerOn() : powerable->circuitPowerOff();
944 						}
945 						else
946 						{
947 							if ( powerable->behavior == &::actSignalTimer )
948 							{
949 								switch ( powerable->signalInputDirection )
950 								{
951 									case 0: // west
952 										if ( static_cast<int>(this->x / 16) == static_cast<int>((powerable->x / 16) - 1) )
953 										{
954 											(switch_power == SWITCH_POWERED) ? powerable->mechanismPowerOn() : powerable->mechanismPowerOff();
955 										}
956 										break;
957 									case 1: // south
958 										if ( static_cast<int>(this->y / 16) == static_cast<int>((powerable->y / 16) - 1) )
959 										{
960 											(switch_power == SWITCH_POWERED) ? powerable->mechanismPowerOn() : powerable->mechanismPowerOff();
961 										}
962 										break;
963 									case 2: // east
964 										if ( static_cast<int>(this->x / 16) == static_cast<int>((powerable->x / 16) + 1) )
965 										{
966 											(switch_power == SWITCH_POWERED) ? powerable->mechanismPowerOn() : powerable->mechanismPowerOff();
967 										}
968 										break;
969 									case 3: // north
970 										if ( static_cast<int>(this->y / 16) == static_cast<int>((powerable->y / 16) + 1) )
971 										{
972 											(switch_power == SWITCH_POWERED) ? powerable->mechanismPowerOn() : powerable->mechanismPowerOff();
973 										}
974 										break;
975 									default:
976 										break;
977 								}
978 							}
979 							else
980 							{
981 								(switch_power == SWITCH_POWERED) ? powerable->mechanismPowerOn() : powerable->mechanismPowerOff();
982 							}
983 						}
984 					}
985 				}
986 			}
987 			list_FreeAll(neighbors); //Free the list.
988 			free(neighbors);
989 		}
990 	}
991 }