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 }