1 /*-------------------------------------------------------------------------------
2
3 BARONY
4 File: actgeneral.cpp
5 Desc: very small and general behavior functions
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 "sound.hpp"
16 #include "entity.hpp"
17 #include "net.hpp"
18 #include "collision.hpp"
19 #include "player.hpp"
20 #include "menu.hpp"
21 #include "magic/magic.hpp"
22 #include "interface/interface.hpp"
23 #include "items.hpp"
24 #include "scores.hpp"
25
26 /*-------------------------------------------------------------------------------
27
28 act*
29
30 The following functions describe various entity behaviors. All functions
31 take a pointer to the entities that use them as an argument.
32
33 -------------------------------------------------------------------------------*/
34
actAnimator(Entity * my)35 void actAnimator(Entity* my)
36 {
37 if ( my->skill[4] == 0 )
38 {
39 my->skill[4] = 1;
40 map.tiles[my->skill[0] + (int)my->y * MAPLAYERS + (int)my->x * MAPLAYERS * map.height] -= my->skill[1] - 1;
41 }
42
43 if ( (int)floor(my->x) < 0 || (int)floor(my->x) >= map.width || (int)floor(my->y) < 0 || (int)floor(my->y) >= map.height )
44 {
45 list_RemoveNode(my->mynode);
46 return;
47 }
48
49 my->skill[3]++;
50 if ( my->skill[3] >= 10 )
51 {
52 my->skill[3] = 0;
53 map.tiles[my->skill[0] + (int)floor(my->y)*MAPLAYERS + (int)floor(my->x)*MAPLAYERS * map.height]++;
54 my->skill[5]++;
55 if (my->skill[5] == my->skill[1])
56 {
57 my->skill[5] = 0;
58 map.tiles[my->skill[0] + (int)floor(my->y)*MAPLAYERS + (int)floor(my->x)*MAPLAYERS * map.height] -= my->skill[1];
59 }
60 }
61 }
62
63 #define TESTSPRITES
64
actRotate(Entity * my)65 void actRotate(Entity* my)
66 {
67 my->yaw += 0.1;
68 my->flags[PASSABLE] = true; // this entity should always be passable
69
70 #ifdef TESTSPRITES
71 if ( keystatus[SDL_SCANCODE_HOME] )
72 {
73 keystatus[SDL_SCANCODE_HOME] = 0;
74 my->sprite++;
75 if ( my->sprite >= nummodels )
76 {
77 my->sprite = 0;
78 }
79 messagePlayer(clientnum, "test sprite: %d", my->sprite);
80 }
81 if ( keystatus[SDL_SCANCODE_END] )
82 {
83 keystatus[SDL_SCANCODE_END] = 0;
84 my->sprite += 10;
85 if ( my->sprite >= nummodels )
86 {
87 my->sprite = 0;
88 }
89 messagePlayer(clientnum, "test sprite: %d", my->sprite);
90 }
91 #endif
92 }
93
94 #define LIQUID_TIMER my->skill[0]
95 #define LIQUID_INIT my->skill[1]
96 #define LIQUID_LAVA my->flags[USERFLAG1]
97 #define LIQUID_LAVANOBUBBLE my->skill[4]
98
actLiquid(Entity * my)99 void actLiquid(Entity* my)
100 {
101 // as of 1.0.7 this function is DEPRECATED
102
103 list_RemoveNode(my->mynode);
104 return;
105
106 if ( !LIQUID_INIT )
107 {
108 LIQUID_INIT = 1;
109 LIQUID_TIMER = 60 * (rand() % 20);
110 if ( LIQUID_LAVA )
111 {
112 my->light = lightSphereShadow(my->x / 16, my->y / 16, 2, 128);
113 }
114 }
115 LIQUID_TIMER--;
116 if ( LIQUID_TIMER <= 0 )
117 {
118 LIQUID_TIMER = 60 * 20 + 60 * (rand() % 20);
119 if ( !LIQUID_LAVA )
120 {
121 playSoundEntityLocal( my, 135, 32 );
122 }
123 else
124 {
125 playSoundEntityLocal( my, 155, 100 );
126 }
127 }
128 if ( LIQUID_LAVA && !LIQUID_LAVANOBUBBLE )
129 {
130 if ( ticks % 40 == my->getUID() % 40 && rand() % 3 == 0 )
131 {
132 int c, j = 1 + rand() % 2;
133 for ( c = 0; c < j; c++ )
134 {
135 Entity* entity = spawnGib( my );
136 entity->x += rand() % 16 - 8;
137 entity->y += rand() % 16 - 8;
138 entity->flags[SPRITE] = true;
139 entity->sprite = 42;
140 entity->fskill[3] = 0.01;
141 double vel = (rand() % 10) / 20.f;
142 entity->vel_x = vel * cos(entity->yaw);
143 entity->vel_y = vel * sin(entity->yaw);
144 entity->vel_z = -.15 - (rand() % 15) / 100.f;
145 entity->z = 7.5;
146 }
147 }
148 }
149 }
150
actEmpty(Entity * my)151 void actEmpty(Entity* my)
152 {
153 // an empty action
154 // used on clients to permit dead reckoning and other interpolation
155 }
156
actFurniture(Entity * my)157 void actFurniture(Entity* my)
158 {
159 if ( !my )
160 {
161 return;
162 }
163
164 if ( !my->flags[BURNABLE] )
165 {
166 my->flags[BURNABLE] = true;
167 }
168
169 my->actFurniture();
170 }
171
actFurniture()172 void Entity::actFurniture()
173 {
174 if ( !furnitureInit )
175 {
176 furnitureInit = 1;
177 if ( furnitureType == FURNITURE_TABLE || FURNITURE_BUNKBED || FURNITURE_BED || FURNITURE_PODIUM )
178 {
179 furnitureHealth = 15 + rand() % 5;
180 }
181 else
182 {
183 furnitureHealth = 4 + rand() % 4;
184 }
185 furnitureMaxHealth = furnitureHealth;
186 flags[BURNABLE] = true;
187 }
188 else
189 {
190 if ( multiplayer != CLIENT )
191 {
192 // burning
193 if ( flags[BURNING] )
194 {
195 if ( ticks % 15 == 0 )
196 {
197 furnitureHealth--;
198 }
199 }
200
201 // furniture mortality :p
202 if ( furnitureHealth <= 0 )
203 {
204 int c;
205 for ( c = 0; c < 5; c++ )
206 {
207 Entity* entity = spawnGib(this);
208 entity->flags[INVISIBLE] = false;
209 entity->sprite = 187; // Splinter.vox
210 entity->x = floor(x / 16) * 16 + 8;
211 entity->y = floor(y / 16) * 16 + 8;
212 entity->y += -3 + rand() % 6;
213 entity->x += -3 + rand() % 6;
214 entity->z = -5 + rand() % 10;
215 entity->yaw = (rand() % 360) * PI / 180.0;
216 entity->pitch = (rand() % 360) * PI / 180.0;
217 entity->roll = (rand() % 360) * PI / 180.0;
218 entity->vel_x = (rand() % 10 - 5) / 10.0;
219 entity->vel_y = (rand() % 10 - 5) / 10.0;
220 entity->vel_z = -.5;
221 entity->fskill[3] = 0.04;
222 serverSpawnGibForClient(entity);
223 }
224 playSoundEntity(this, 176, 128);
225 Entity* entity = uidToEntity(parent);
226 if ( entity != NULL )
227 {
228 entity->itemNotMoving = 0; // drop the item that was on the table
229 entity->itemNotMovingClient = 0; // clear the client item gravity flag
230 serverUpdateEntitySkill(entity, 18); //update both the above flags.
231 serverUpdateEntitySkill(entity, 19);
232 }
233 list_RemoveNode(mynode);
234 return;
235 }
236
237 // using
238 int i;
239 for (i = 0; i < MAXPLAYERS; i++)
240 {
241 if ( (i == 0 && selectedEntity == this) || (client_selected[i] == this) )
242 {
243 if (inrange[i])
244 {
245 switch ( furnitureType )
246 {
247 case FURNITURE_CHAIR:
248 messagePlayer(i, language[476]);
249 break;
250 case FURNITURE_TABLE:
251 messagePlayer(i, language[477]);
252 break;
253 case FURNITURE_BED:
254 messagePlayer(i, language[2493]);
255 break;
256 case FURNITURE_BUNKBED:
257 if ( i == 0 || i == 2 )
258 {
259 messagePlayer(i, language[2494]);
260 }
261 else
262 {
263 messagePlayer(i, language[2495]);
264 }
265 break;
266 case FURNITURE_PODIUM:
267 messagePlayer(i, language[2496]);
268 break;
269 default:
270 messagePlayer(i, language[477]);
271 break;
272 }
273 }
274 }
275 }
276 }
277 }
278 }
279
280 // an easter egg
281 #define MCAXE_USED my->skill[0]
282
actMCaxe(Entity * my)283 void actMCaxe(Entity* my)
284 {
285 my->yaw += .05;
286 if ( my->yaw > PI * 2 )
287 {
288 my->yaw -= PI * 2;
289 }
290 if ( !MCAXE_USED )
291 {
292 if ( multiplayer != CLIENT )
293 {
294 // use
295 int i;
296 for (i = 0; i < MAXPLAYERS; i++)
297 {
298 if ( (i == 0 && selectedEntity == my) || (client_selected[i] == my) )
299 {
300 if (inrange[i])
301 {
302 messagePlayer(i, language[478 + rand() % 5]);
303 MCAXE_USED = 1;
304 serverUpdateEntitySkill(my, 0);
305 }
306 }
307 }
308 }
309
310 // bob
311 my->z -= sin(my->fskill[0] * PI / 180.f);
312 my->fskill[0] += 6;
313 if ( my->fskill[0] >= 360 )
314 {
315 my->fskill[0] -= 360;
316 }
317 my->z += sin(my->fskill[0] * PI / 180.f);
318 }
319 else
320 {
321 my->z += 1;
322 if ( my->z > 64 )
323 {
324 list_RemoveNode(my->mynode);
325 return;
326 }
327 }
328 }
329
actStalagFloor(Entity * my)330 void actStalagFloor(Entity* my)
331 {
332 //TODO: something?
333 if ( !my )
334 {
335 return;
336 }
337
338 my->actStalagFloor();
339 }
340
actStalagFloor()341 void Entity::actStalagFloor()
342 {
343
344 }
345
actStalagCeiling(Entity * my)346 void actStalagCeiling(Entity* my)
347 {
348 //TODO: something?
349 if ( !my )
350 {
351 return;
352 }
353 my->actStalagCeiling();
354 }
355
actStalagCeiling()356 void Entity::actStalagCeiling()
357 {
358
359 }
360
actStalagColumn(Entity * my)361 void actStalagColumn(Entity* my)
362 {
363 //TODO: something?
364 if ( !my )
365 {
366 return;
367 }
368
369 my->actStalagColumn();
370 }
371
actStalagColumn()372 void Entity::actStalagColumn()
373 {
374
375 }
376
actColumn(Entity * my)377 void actColumn(Entity* my)
378 {
379 //TODO: something?
380 if ( !my )
381 {
382 return;
383 }
384 if ( my->flags[BLOCKSIGHT] ) // stop the compiler optimising into a different entity.
385 {
386 my->flags[BLOCKSIGHT] = false;
387 }
388 my->actColumn();
389 }
390
actColumn()391 void Entity::actColumn()
392 {
393
394 }
395
actCeilingTile(Entity * my)396 void actCeilingTile(Entity* my)
397 {
398 if ( !my )
399 {
400 return;
401 }
402 if ( !my->flags[PASSABLE] )
403 {
404 my->flags[PASSABLE] = true;
405 }
406 if ( my->flags[BLOCKSIGHT] )
407 {
408 my->flags[BLOCKSIGHT] = false;
409 }
410 }
411
actPistonBase(Entity * my)412 void actPistonBase(Entity* my)
413 {
414 if ( !my )
415 {
416 return;
417 }
418 }
419
actPistonCam(Entity * my)420 void actPistonCam(Entity* my)
421 {
422 if ( !my )
423 {
424 return;
425 }
426 my->actPistonCam();
427 }
428
actPistonCam()429 void Entity::actPistonCam()
430 {
431 yaw += pistonCamRotateSpeed;
432 while ( yaw > 2 * PI )
433 {
434 yaw -= 2 * PI;
435 }
436 while ( yaw < 0 )
437 {
438 yaw += 2 * PI;
439 }
440 if ( (pistonCamDir == 0 || pistonCamDir == 2) && pistonCamRotateSpeed > 0 )
441 {
442 if ( yaw <= PI && yaw >= -pistonCamRotateSpeed + PI )
443 {
444 yaw = PI;
445 pistonCamRotateSpeed = 0;
446 }
447 }
448 --pistonCamTimer;
449
450 if ( pistonCamDir == 0 ) // bottom
451 {
452 if ( pistonCamTimer <= 0 )
453 {
454 pistonCamDir = 1; // up
455 pistonCamRotateSpeed = 0.2;
456 pistonCamTimer = rand() % 5 * TICKS_PER_SECOND;
457 }
458 }
459 if ( pistonCamDir == 1 ) // up
460 {
461 z -= 0.1;
462 if ( z < -1.75 )
463 {
464 z = -1.75;
465 pistonCamRotateSpeed *= rand() % 2 == 0 ? -1 : 1;
466 pistonCamDir = 2; // top
467 }
468 }
469 else if ( pistonCamDir == 2 ) // top
470 {
471 if ( pistonCamTimer <= 0 )
472 {
473 pistonCamDir = 3; // down
474 pistonCamRotateSpeed = -0.2;
475 pistonCamTimer = rand() % 5 * TICKS_PER_SECOND;
476 }
477 }
478 else if ( pistonCamDir == 3 ) // down
479 {
480 z += 0.1;
481 if ( z > 1.75 )
482 {
483 z = 1.75;
484 pistonCamRotateSpeed *= rand() % 2 == 0 ? -1 : 1;
485 pistonCamDir = 0; // down
486 }
487 }
488 }
489
actFloorDecoration(Entity * my)490 void actFloorDecoration(Entity* my)
491 {
492 if ( !my )
493 {
494 return;
495 }
496 if ( !my->flags[PASSABLE] )
497 {
498 my->flags[PASSABLE] = true;
499 }
500
501 if ( multiplayer == CLIENT )
502 {
503 return;
504 }
505
506 if ( my->floorDecorationInteractText1 == 0 )
507 {
508 // no text.
509 return;
510 }
511
512 // using
513 int i;
514 for ( i = 0; i < MAXPLAYERS; i++ )
515 {
516 if ( (i == 0 && selectedEntity == my) || (client_selected[i] == my) )
517 {
518 if ( inrange[i] )
519 {
520 // assemble the string.
521 char buf[256] = "";
522 int totalChars = 0;
523 for ( int i = 8; i < 60; ++i )
524 {
525 if ( i == 28 ) // circuit_status
526 {
527 continue;
528 }
529 if ( my->skill[i] != 0 )
530 {
531 for ( int c = 0; c < 4; ++c )
532 {
533 buf[totalChars] = static_cast<char>((my->skill[i] >> (c * 8)) & 0xFF);
534 //messagePlayer(0, "%d %d", i, c);
535 ++totalChars;
536 }
537 }
538 }
539 if ( buf[totalChars] != '\0' )
540 {
541 buf[totalChars] = '\0';
542 }
543 std::string output = buf;
544
545
546 size_t foundSignpostCharacter = output.find("#");
547 if ( foundSignpostCharacter == 0 )
548 {
549 output.erase(0, 1);
550 output.insert(0, "The sign says:\n \"");
551 output += "\"";
552 }
553
554 size_t foundInputTag = output.find("@in=");
555 while ( foundInputTag != std::string::npos )
556 {
557 output.erase(foundInputTag, strlen("@in="));
558 std::string impulseStr;
559 size_t inputTagStrIndex = foundInputTag;
560 while ( inputTagStrIndex < output.length()
561 && output.at(inputTagStrIndex) != ' '
562 && output.at(inputTagStrIndex) != '.'
563 && output.at(inputTagStrIndex) != ','
564 && output.at(inputTagStrIndex) != '\0'
565 )
566 {
567 // usage: @in=IN_USE will get the input key for use
568 impulseStr = impulseStr + output.at(inputTagStrIndex);
569 ++inputTagStrIndex;
570 }
571 for ( int i = 0; i < NUMIMPULSES; ++i )
572 {
573 if ( impulseStrings[i].compare(impulseStr) == 0 )
574 {
575 output.erase(output.find(impulseStr), impulseStr.length());
576 std::string inputFormatted = "[";
577 inputFormatted.append(getInputName(impulses[i])).append("]");
578 output.insert(foundInputTag, inputFormatted.c_str());
579 break;
580 }
581 }
582 foundInputTag = output.find("@in=");
583 }
584
585 size_t found = output.find("\\n");
586 while ( found != std::string::npos )
587 {
588 output.erase(found, 2);
589 output.insert(found, 1, '\n');
590 found = output.find("\\n");
591 }
592 strcpy(buf, output.c_str());
593 messagePlayer(i, buf);
594 }
595 }
596 }
597 }
598
actTextSource(Entity * my)599 void actTextSource(Entity* my)
600 {
601 if ( !my )
602 {
603 return;
604 }
605 my->actTextSource();
606 }
607
608 TextSourceScript textSourceScript;
609
textSourceProcessScriptTag(std::string & input,std::string findTag)610 int TextSourceScript::textSourceProcessScriptTag(std::string& input, std::string findTag)
611 {
612 size_t foundScriptTag = input.find(findTag);
613 if ( foundScriptTag != std::string::npos )
614 {
615 if ( !containsOperator(findTag.back()) )
616 {
617 eraseTag(input, findTag, foundScriptTag);
618 // end the function here, return non-error as we found the tag.
619 return 0;
620 }
621 input.erase(foundScriptTag, strlen(findTag.c_str()));
622 if ( input.empty() )
623 {
624 return 0;
625 }
626
627 std::string tagValue;
628
629 while ( foundScriptTag < input.length()
630 && input.at(foundScriptTag) != ' '
631 && input.at(foundScriptTag) != '@'
632 && input.at(foundScriptTag) != '\0'
633 )
634 {
635 // usage: Hello @p @d 123 will send to distance 123 units away and send message "Hello player "
636 tagValue = tagValue + input.at(foundScriptTag);
637 //messagePlayer(clientnum, "%c", output.at(foundDistanceRequirement));
638 ++foundScriptTag;
639 }
640 if ( foundScriptTag < input.length() && input.at(foundScriptTag) == ' ' )
641 {
642 input.erase(input.find(tagValue), tagValue.length() + 1); // clear trailing space
643 }
644 else
645 {
646 input.erase(input.find(tagValue), tagValue.length());
647 }
648
649 size_t foundMapReference = tagValue.find(",");
650 // look for comma or - symbol, e.g @power=15,16 or @power=15-20,16-21
651 if ( foundMapReference != std::string::npos )
652 {
653 if ( findTag.compare("@setvar=") == 0 )
654 {
655 std::string variableName = tagValue.substr(0, foundMapReference);
656 int value = std::stoi(tagValue.substr(foundMapReference + 1, tagValue.length() - foundMapReference));
657 if ( scriptVariables.find(variableName) != scriptVariables.end() )
658 {
659 scriptVariables[variableName] = value;
660 }
661 else
662 {
663 scriptVariables.insert(std::make_pair(variableName, value));
664 }
665 return 0;
666 }
667
668
669 std::string x_str = tagValue.substr(0, foundMapReference);
670 std::string y_str = tagValue.substr(foundMapReference + 1, tagValue.length() - foundMapReference);
671 int x1 = 0;
672 int x2 = 0;
673 int y1 = 0;
674 int y2 = 0;
675 size_t foundMapRange = x_str.find("-");
676 if ( foundMapRange != std::string::npos )
677 {
678 // found map range reference.
679 x1 = std::stoi(x_str.substr(0, foundMapRange));
680 x2 = std::stoi(x_str.substr(foundMapRange + 1, x_str.length() - foundMapRange));
681 }
682 else
683 {
684 x1 = std::stoi(x_str);
685 x2 = x1;
686 }
687 foundMapRange = y_str.find("-");
688 if ( foundMapRange != std::string::npos )
689 {
690 // found map range reference.
691 y1 = std::stoi(y_str.substr(0, foundMapRange));
692 y2 = std::stoi(y_str.substr(foundMapRange + 1, y_str.length() - foundMapRange));
693 }
694 else
695 {
696 y1 = std::stoi(y_str);
697 y2 = y1;
698 }
699
700 x1 = std::min(std::max(0, x1), (int)map.width - 1);
701 y1 = std::min(std::max(0, y1), (int)map.height - 1);
702 x2 = std::min(std::max(x1, x2), (int)map.width - 1);
703 y2 = std::min(std::max(y1, y2), (int)map.height - 1);
704
705 return (x1 & 0xFF) + ((x2 & 0xFF) << 8) + ((y1 & 0xFF) << 16) + ((y2 & 0xFF) << 24);
706 }
707 else
708 {
709 size_t foundSeperator = tagValue.find(";");
710 if ( foundSeperator != std::string::npos )
711 {
712 std::pair<int, int> param1 = std::make_pair(0, 0);
713 std::pair<int, int> param2 = std::make_pair(0, 0);
714 std::string first_str = tagValue.substr(0, foundSeperator);
715 std::string second_str = tagValue.substr(foundSeperator + 1, tagValue.length() - foundSeperator);
716 size_t foundRange = first_str.find("-");
717 if ( foundRange != std::string::npos )
718 {
719 // found range reference.
720 param1.first = std::stoi(first_str.substr(0, foundRange));
721 param1.second = std::stoi(first_str.substr(foundRange + 1, first_str.length() - foundRange));
722 }
723 else
724 {
725 param1.first = std::stoi(first_str);
726 param1.second = param1.first;
727 }
728 foundRange = second_str.find("-");
729 if ( foundRange != std::string::npos )
730 {
731 // found range reference.
732 param2.first = std::stoi(second_str.substr(0, foundRange));
733 param2.second = std::stoi(second_str.substr(foundRange + 1, second_str.length() - foundRange));
734 }
735 else
736 {
737 param2.first = std::stoi(second_str);
738 param2.second = param2.first;
739 }
740
741 param1.second = std::max(param1.first, param1.second);
742 param2.second = std::max(param2.first, param2.second);
743
744 return (param1.first & 0xFF) + ((param1.second & 0xFF) << 8) + ((param2.first & 0xFF) << 16) + ((param2.second & 0xFF) << 24);
745 }
746 else
747 {
748 size_t foundRange = tagValue.find("-");
749 if ( foundRange != std::string::npos )
750 {
751 std::pair<int, int> param1 = std::make_pair(0, 0);
752 // found range reference.
753 std::string first_str = tagValue.substr(0, foundRange);
754 std::string second_str = tagValue.substr(foundRange + 1, tagValue.length() - foundRange);
755
756 param1.first = std::stoi(first_str);
757 param1.second = std::stoi(second_str);
758 param1.second = std::max(param1.first, param1.second);
759 return (param1.first & 0xFF) + ((param1.second & 0xFF) << 8);
760 }
761 }
762 }
763
764 if ( !tagValue.compare("all") )
765 {
766 int x1 = 0;
767 int x2 = map.width - 1;
768 int y1 = 0;
769 int y2 = map.height - 1;
770 return (x1 & 0xFF) + ((x2 & 0xFF) << 8) + ((y1 & 0xFF) << 16) + ((y2 & 0xFF) << 24);
771 }
772 else if ( findTag.compare("@attachto=") == 0 || findTag.compare("@reattachto=") == 0 )
773 {
774 if ( !tagValue.compare("monsters") )
775 {
776 return TO_MONSTERS;
777 }
778 else if ( !tagValue.compare("items") )
779 {
780 return TO_ITEMS;
781 }
782 else if ( !tagValue.compare("players") )
783 {
784 return TO_PLAYERS;
785 }
786 else
787 {
788 for ( int i = 0; i < NUMMONSTERS; ++i )
789 {
790 if ( !tagValue.compare(monstertypename[i]) )
791 {
792 return TO_NOTHING + i;
793 }
794 }
795 return k_ScriptError;
796 }
797 }
798 else if ( findTag.compare("@triggerif=") == 0 )
799 {
800 if ( !tagValue.compare("attached#dies") )
801 {
802 return TRIGGER_ATTACHED_ISREMOVED;
803 }
804 else if ( !tagValue.compare("attached#exists") )
805 {
806 return TRIGGER_ATTACHED_EXISTS;
807 }
808 else if ( !tagValue.compare("attached#invisible") )
809 {
810 return TRIGGER_ATTACHED_INVIS;
811 }
812 else if ( !tagValue.compare("attached#visible") )
813 {
814 return TRIGGER_ATTACHED_VISIBLE;
815 }
816 else if ( !tagValue.compare("always") )
817 {
818 return TRIGGER_ATTACHED_ALWAYS;
819 }
820 else
821 {
822 return k_ScriptError;
823 }
824 }
825
826 return std::stoi(tagValue);
827 }
828 return k_ScriptError;
829 }
830
handleTextSourceScript(Entity & src,std::string input)831 void TextSourceScript::handleTextSourceScript(Entity& src, std::string input)
832 {
833 bool statOnlyUpdateNeeded = false;
834
835 std::vector<std::string> tokens;
836 std::string searchString = input;
837 size_t findToken = searchString.find("@");
838 while ( findToken != std::string::npos )
839 {
840 std::string token = "@";
841 ++findToken;
842 while ( findToken < searchString.length()
843 && searchString.at(findToken) != ' '
844 && searchString.at(findToken) != '@'
845 && searchString.at(findToken) != '\0'
846 )
847 {
848 token = token + searchString.at(findToken);
849 ++findToken;
850 }
851 searchString.erase(searchString.find(token), token.length());
852 tokens.push_back(token);
853 findToken = searchString.find("@");
854 }
855
856 printlog("[SCRIPT]: Starting Execution...");
857 std::string executionLog = "[SCRIPT]: Processed tokens:";
858 std::vector<Entity*> attachedEntities = textSourceScript.getScriptAttachedEntities(src);
859
860 for ( auto it = tokens.begin(); it != tokens.end(); ++it )
861 {
862 executionLog.append(" ").append(*it);
863
864 bool processOnAttachedEntity = false;
865 size_t foundAttachedTag = (*it).find("@attached.");
866 if ( foundAttachedTag != std::string::npos )
867 {
868 processOnAttachedEntity = true;
869 // erase "attached." in token, pass the information on to the tag contents.
870 (*it).erase(foundAttachedTag + 1, strlen("attached."));
871 size_t foundAttachedTag = input.find("@attached.");
872 if ( foundAttachedTag != std::string::npos )
873 {
874 input.erase(foundAttachedTag + 1, strlen("attached."));
875 }
876 }
877
878 if ( (*it).find("@reattachto=") != std::string::npos )
879 {
880 int attachTo = textSourceProcessScriptTag(input, "@reattachto=");
881 if ( attachTo == k_ScriptError )
882 {
883 return;
884 }
885 attachedEntities.clear();
886 list_FreeAll(&src.children); // reattach all the entities again.
887
888 textSourceScript.setScriptType(src.textSourceIsScript, textSourceScript.SCRIPT_ATTACHED);
889 int x1 = static_cast<int>(src.x / 16); // default to just whatever this script is sitting on.
890 int x2 = static_cast<int>(src.x / 16);
891 int y1 = static_cast<int>(src.y / 16);
892 int y2 = static_cast<int>(src.y / 16);
893 if ( input.find("@attachrange=") != std::string::npos )
894 {
895 int result = textSourceProcessScriptTag(input, "@attachrange=");
896 if ( result != k_ScriptError )
897 {
898 x1 = result & 0xFF;
899 x2 = (result >> 8) & 0xFF;
900 y1 = (result >> 16) & 0xFF;
901 y2 = (result >> 24) & 0xFF;
902 }
903 }
904 textSourceScript.setAttachedToEntityType(src.textSourceIsScript, attachTo);
905 for ( node_t* node = map.entities->first; node; node = node->next )
906 {
907 Entity* entity = (Entity*)node->element;
908 if ( entity )
909 {
910 if ( (entity->behavior == &actMonster && attachTo == TO_MONSTERS)
911 || (entity->behavior == &actPlayer && attachTo == TO_PLAYERS)
912 || (entity->behavior == &actItem && attachTo == TO_ITEMS)
913 || (entity->behavior == &actMonster
914 && attachTo >= TO_NOTHING && attachTo <= TO_DUMMYBOT
915 && entity->getRace() == (attachTo - TO_NOTHING)) )
916 {
917 // found our entity.
918 }
919 else
920 {
921 continue;
922 }
923 int findx = static_cast<int>(entity->x) >> 4;
924 int findy = static_cast<int>(entity->y) >> 4;
925 if ( findx >= x1 && findx <= x2 && findy >= y1 && findy <= y2 )
926 {
927 node_t* node = list_AddNodeLast(&src.children);
928 node->deconstructor = &defaultDeconstructor;
929 Uint32* entityUid = (Uint32*)(malloc(sizeof(Uint32)));
930 node->element = entityUid;
931 node->size = sizeof(Uint32);
932 *entityUid = entity->getUID();
933 }
934 }
935 }
936 attachedEntities = textSourceScript.getScriptAttachedEntities(src);
937 }
938 else if ( (*it).find("@clrplayer") != std::string::npos )
939 {
940 int result = textSourceProcessScriptTag(input, "@clrplayer");
941 if ( result != k_ScriptError )
942 {
943 std::vector<Entity*> applyToEntities;
944 if ( processOnAttachedEntity )
945 {
946 for ( auto entity : attachedEntities )
947 {
948 if ( entity->behavior == &actPlayer && stats[entity->skill[2]] && !client_disconnected[entity->skill[2]] )
949 {
950 applyToEntities.push_back(entity);
951 }
952 }
953 }
954 else
955 {
956 for ( int c = 0; c < MAXPLAYERS; ++c )
957 {
958 if ( stats[c] && !client_disconnected[c] && players[c] && players[c]->entity )
959 {
960 applyToEntities.push_back(players[c]->entity);
961 }
962 }
963 }
964
965 for ( auto entity : applyToEntities )
966 {
967 Stat* stats = entity->getStats();
968 int player = entity->skill[2];
969 if ( player == 0 )
970 {
971 playerClearInventory(true);
972 }
973 else
974 {
975 // other players
976 stats->freePlayerEquipment();
977 stats->clearStats();
978 updateClientInformation(player, true, true, TextSourceScript::CLIENT_UPDATE_ALL);
979 }
980 }
981 }
982 }
983 else if ( (*it).find("@class=") != std::string::npos )
984 {
985 int result = textSourceProcessScriptTag(input, "@class=");
986 if ( result != k_ScriptError && result >= CLASS_BARBARIAN && result < NUMCLASSES )
987 {
988 if ( !enabledDLCPack1 && result >= CLASS_CONJURER && result <= CLASS_BREWER )
989 {
990 result = CLASS_BARBARIAN;
991 }
992 if ( !enabledDLCPack2 && result >= CLASS_MACHINIST && result <= CLASS_HUNTER )
993 {
994 result = CLASS_BARBARIAN;
995 }
996
997 std::vector<Entity*> applyToEntities;
998 if ( processOnAttachedEntity )
999 {
1000 for ( auto entity : attachedEntities )
1001 {
1002 if ( entity->behavior == &actPlayer && stats[entity->skill[2]] && !client_disconnected[entity->skill[2]] )
1003 {
1004 applyToEntities.push_back(entity);
1005 }
1006 }
1007 }
1008 else
1009 {
1010 for ( int c = 0; c < MAXPLAYERS; ++c )
1011 {
1012 if ( stats[c] && !client_disconnected[c] && players[c] && players[c]->entity )
1013 {
1014 applyToEntities.push_back(players[c]->entity);
1015 }
1016 }
1017 }
1018
1019 for ( auto entity : applyToEntities )
1020 {
1021 int player = entity->skill[2];
1022 client_classes[player] = result;
1023 bool oldIntro = intro;
1024 intro = true;
1025 initClass(player);
1026 intro = oldIntro;
1027 }
1028
1029 if ( !applyToEntities.empty() )
1030 {
1031 for ( int c = 1; c < MAXPLAYERS; ++c )
1032 {
1033 updateClientInformation(c, false, false, TextSourceScript::CLIENT_UPDATE_CLASS);
1034 }
1035 }
1036 }
1037 }
1038 else if ( (*it).find("@clrstats") != std::string::npos )
1039 {
1040 int result = textSourceProcessScriptTag(input, "@clrstats");
1041 if ( result != k_ScriptError )
1042 {
1043 std::vector<Entity*> applyToEntities;
1044 if ( processOnAttachedEntity )
1045 {
1046 for ( auto entity : attachedEntities )
1047 {
1048 if ( entity->behavior != &actPlayer && entity->behavior != actMonster )
1049 {
1050 continue;
1051 }
1052 if ( entity->behavior == &actPlayer && !client_disconnected[entity->skill[2]] )
1053 {
1054 statOnlyUpdateNeeded = true;
1055 }
1056 applyToEntities.push_back(entity);
1057 }
1058 }
1059 else
1060 {
1061 for ( int c = 0; c < MAXPLAYERS; ++c )
1062 {
1063 if ( stats[c] && !client_disconnected[c] && players[c] && players[c]->entity )
1064 {
1065 applyToEntities.push_back(players[c]->entity);
1066 }
1067 }
1068 }
1069
1070 for ( auto entity : applyToEntities )
1071 {
1072 if ( entity->behavior == &actPlayer )
1073 {
1074 statOnlyUpdateNeeded = true;
1075 }
1076 Stat* stats = entity->getStats();
1077 if ( stats )
1078 {
1079 stats->HP = DEFAULT_HP;
1080 stats->MAXHP = DEFAULT_HP;
1081 stats->OLDHP = stats->HP;
1082 stats->MP = DEFAULT_MP;
1083 stats->MAXMP = DEFAULT_MP;
1084 stats->STR = 0;
1085 stats->DEX = 0;
1086 stats->CON = 0;
1087 stats->INT = 0;
1088 stats->PER = 0;
1089 stats->CHR = 0;
1090 stats->LVL = 1;
1091 stats->GOLD = 0;
1092 stats->EXP = 0;
1093 }
1094 }
1095 }
1096 }
1097 else if ( (*it).find("@hunger=") != std::string::npos )
1098 {
1099 int result = textSourceProcessScriptTag(input, "@hunger=");
1100 if ( result != k_ScriptError )
1101 {
1102 std::vector<Entity*> applyToEntities;
1103 if ( processOnAttachedEntity )
1104 {
1105 for ( auto entity : attachedEntities )
1106 {
1107 if ( entity->behavior != &actPlayer )
1108 {
1109 continue;
1110 }
1111 applyToEntities.push_back(entity);
1112 }
1113 }
1114 else
1115 {
1116 for ( int c = 0; c < MAXPLAYERS; ++c )
1117 {
1118 if ( stats[c] && !client_disconnected[c] && players[c] && players[c]->entity )
1119 {
1120 applyToEntities.push_back(players[c]->entity);
1121 }
1122 }
1123 }
1124
1125 for ( auto entity : applyToEntities )
1126 {
1127 int player = -1;
1128 if ( entity->behavior != &actPlayer )
1129 {
1130 continue;
1131 }
1132 player = entity->skill[2];
1133 if ( stats[player] && !client_disconnected[player] )
1134 {
1135 stats[player]->HUNGER = std::min(result, 2000);
1136 if ( player != clientnum )
1137 {
1138 updateClientInformation(player, false, false, TextSourceScript::CLIENT_UPDATE_HUNGER);
1139 }
1140 }
1141 }
1142 }
1143 }
1144 else if ( (*it).find("@nextlevel=") != std::string::npos )
1145 {
1146 int result = textSourceProcessScriptTag(input, "@nextlevel=");
1147 if ( result != k_ScriptError )
1148 {
1149 loadnextlevel = true;
1150 skipLevelsOnLoad = result;
1151 }
1152 }
1153 else if ( (*it).find("@power=") != std::string::npos )
1154 {
1155 int result = textSourceProcessScriptTag(input, "@power=");
1156 if ( result != k_ScriptError )
1157 {
1158 int x1 = result & 0xFF;
1159 int x2 = (result >> 8) & 0xFF;
1160 int y1 = (result >> 16) & 0xFF;
1161 int y2 = (result >> 24) & 0xFF;
1162 bool foundExisting = false;
1163 std::unordered_set<int> plateSpots;
1164 for ( node_t* node = map.entities->first; node; node = node->next )
1165 {
1166 Entity* pressurePlate = (Entity*)node->element;
1167 if ( pressurePlate && pressurePlate->behavior == &actTrapPermanent )
1168 {
1169 int findx = static_cast<int>(pressurePlate->x) >> 4;
1170 int findy = static_cast<int>(pressurePlate->y) >> 4;
1171 if ( findx >= x1 && findx <= x2 && findy >= y1 && findy <= y2 )
1172 {
1173 if ( pressurePlate->skill[0] == 0 )
1174 {
1175 pressurePlate->toggleSwitch();
1176 }
1177 plateSpots.insert(static_cast<int>(pressurePlate->x / 16) + map.width * static_cast<int>(pressurePlate->y / 16));
1178 }
1179 }
1180 }
1181 for ( int x = x1; x <= x2; ++x )
1182 {
1183 for ( int y = y1; y <= y2; ++y )
1184 {
1185 if ( plateSpots.find(x + map.width * y) == plateSpots.end() )
1186 {
1187 Entity* pressurePlate = newEntity(-1, 1, map.entities, nullptr);
1188 pressurePlate->sizex = 2;
1189 pressurePlate->sizey = 2;
1190 pressurePlate->x = x * 16 + 8;
1191 pressurePlate->y = y * 16 + 8;
1192 pressurePlate->behavior = &actTrapPermanent;
1193 pressurePlate->skill[1] = 1;
1194 pressurePlate->flags[SPRITE] = true;
1195 pressurePlate->flags[INVISIBLE] = true;
1196 pressurePlate->flags[PASSABLE] = true;
1197 pressurePlate->flags[NOUPDATE] = true;
1198 TileEntityList.addEntity(*pressurePlate); // make sure new nodes are added to the tile list to properly update neighbors.
1199 pressurePlate->toggleSwitch();
1200 }
1201 }
1202 }
1203 }
1204 }
1205 else if ( (*it).find("@unpower=") != std::string::npos )
1206 {
1207 int result = textSourceProcessScriptTag(input, "@unpower=");
1208 if ( result != k_ScriptError )
1209 {
1210 int x1 = result & 0xFF;
1211 int x2 = (result >> 8) & 0xFF;
1212 int y1 = (result >> 16) & 0xFF;
1213 int y2 = (result >> 24) & 0xFF;
1214 for ( node_t* node = map.entities->first; node; node = node->next )
1215 {
1216 Entity* pressurePlate = (Entity*)node->element;
1217 if ( pressurePlate && pressurePlate->behavior == &actTrapPermanent )
1218 {
1219 int findx = static_cast<int>(pressurePlate->x) >> 4;
1220 int findy = static_cast<int>(pressurePlate->y) >> 4;
1221 if ( findx >= x1 && findx <= x2 && findy >= y1 && findy <= y2 )
1222 {
1223 if ( pressurePlate->skill[0] != 0 )
1224 {
1225 pressurePlate->toggleSwitch();
1226 pressurePlate->skill[1] = 1; // can't be interacted.
1227 }
1228 }
1229 }
1230 }
1231 }
1232 }
1233 else if ( (*it).find("@freezemonsters=") != std::string::npos )
1234 {
1235 int result = textSourceProcessScriptTag(input, "@freezemonsters=");
1236 if ( result != k_ScriptError )
1237 {
1238 int x1 = result & 0xFF;
1239 int x2 = (result >> 8) & 0xFF;
1240 int y1 = (result >> 16) & 0xFF;
1241 int y2 = (result >> 24) & 0xFF;
1242 if ( processOnAttachedEntity )
1243 {
1244 for ( auto entity : attachedEntities )
1245 {
1246 if ( entity->behavior != &actMonster )
1247 {
1248 continue;
1249 }
1250 int findx = static_cast<int>(entity->x) >> 4;
1251 int findy = static_cast<int>(entity->y) >> 4;
1252 if ( findx >= x1 && findx <= x2 && findy >= y1 && findy <= y2 )
1253 {
1254 entity->setEffect(EFF_STUNNED, true, -1, false);
1255 }
1256 }
1257 }
1258 else
1259 {
1260 for ( node_t* node = map.creatures->first; node; node = node->next )
1261 {
1262 Entity* entity = (Entity*)node->element;
1263 if ( entity && entity->behavior == &actMonster )
1264 {
1265 int findx = static_cast<int>(entity->x) >> 4;
1266 int findy = static_cast<int>(entity->y) >> 4;
1267 if ( findx >= x1 && findx <= x2 && findy >= y1 && findy <= y2 )
1268 {
1269 entity->setEffect(EFF_STUNNED, true, -1, false);
1270 }
1271 }
1272 }
1273 }
1274 }
1275 }
1276 else if ( (*it).find("@unfreezemonsters=") != std::string::npos )
1277 {
1278 int result = textSourceProcessScriptTag(input, "@unfreezemonsters=");
1279 if ( result != k_ScriptError )
1280 {
1281 int x1 = result & 0xFF;
1282 int x2 = (result >> 8) & 0xFF;
1283 int y1 = (result >> 16) & 0xFF;
1284 int y2 = (result >> 24) & 0xFF;
1285 if ( processOnAttachedEntity )
1286 {
1287 for ( auto entity : attachedEntities )
1288 {
1289 if ( entity->behavior != &actMonster )
1290 {
1291 continue;
1292 }
1293 int findx = static_cast<int>(entity->x) >> 4;
1294 int findy = static_cast<int>(entity->y) >> 4;
1295 if ( findx >= x1 && findx <= x2 && findy >= y1 && findy <= y2 )
1296 {
1297 entity->setEffect(EFF_STUNNED, false, 0, false);
1298 }
1299 }
1300 }
1301 else
1302 {
1303 for ( node_t* node = map.creatures->first; node; node = node->next )
1304 {
1305 Entity* entity = (Entity*)node->element;
1306 if ( entity && entity->behavior == &actMonster )
1307 {
1308 int findx = static_cast<int>(entity->x) >> 4;
1309 int findy = static_cast<int>(entity->y) >> 4;
1310 if ( findx >= x1 && findx <= x2 && findy >= y1 && findy <= y2 )
1311 {
1312 entity->setEffect(EFF_STUNNED, false, 0, false);
1313 }
1314 }
1315 }
1316 }
1317 }
1318 }
1319 else if ( (*it).find("@seteffect=") != std::string::npos )
1320 {
1321 int result = textSourceProcessScriptTag(input, "@seteffect=");
1322 if ( result != k_ScriptError )
1323 {
1324 int effect = result & 0xFF;
1325 int effectEndRange = (result >> 8) & 0xFF;
1326 int duration = (result >> 16) & 0xFF;
1327 int durationEndRange = (result >> 24) & 0xFF;
1328 duration = duration + rand() % (std::max(1, (durationEndRange - duration)));
1329 if ( processOnAttachedEntity )
1330 {
1331 for ( auto entity : attachedEntities )
1332 {
1333 if ( entity->behavior != &actMonster && entity->behavior != &actPlayer )
1334 {
1335 continue;
1336 }
1337 for ( int i = effect; i <= effectEndRange && i < NUMEFFECTS && i >= 0; ++i )
1338 {
1339 entity->setEffect(i, true, duration * TICKS_PER_SECOND, false);
1340 }
1341 if ( multiplayer == SERVER )
1342 {
1343 entity->serverUpdateEffectsForEntity(true);
1344 }
1345 }
1346 }
1347 }
1348 }
1349 else if ( (*it).find("@clreffect=") != std::string::npos )
1350 {
1351 int result = textSourceProcessScriptTag(input, "@clreffect=");
1352 if ( result != k_ScriptError )
1353 {
1354 int effect = result & 0xFF;
1355 int effectEndRange = (result >> 8) & 0xFF;
1356 if ( processOnAttachedEntity )
1357 {
1358 for ( auto entity : attachedEntities )
1359 {
1360 if ( entity->behavior != &actMonster && entity->behavior != &actPlayer )
1361 {
1362 continue;
1363 }
1364 for ( int i = effect; i <= effectEndRange && i < NUMEFFECTS && i >= 0; ++i )
1365 {
1366 entity->setEffect(i, false, 0, false);
1367 }
1368 if ( multiplayer == SERVER )
1369 {
1370 entity->serverUpdateEffectsForEntity(true);
1371 }
1372 }
1373 }
1374 }
1375 }
1376 else if ( (*it).find("@findtarget=") != std::string::npos )
1377 {
1378 int result = textSourceProcessScriptTag(input, "@findtarget=");
1379 if ( result != k_ScriptError )
1380 {
1381 int x1 = result & 0xFF;
1382 int x2 = (result >> 8) & 0xFF;
1383 int y1 = (result >> 16) & 0xFF;
1384 int y2 = (result >> 24) & 0xFF;
1385 if ( processOnAttachedEntity )
1386 {
1387 for ( auto entity : attachedEntities )
1388 {
1389 if ( entity->behavior != &actMonster )
1390 {
1391 continue;
1392 }
1393 Stat* stats = entity->getStats();
1394 real_t dist = 10000.f;
1395 real_t sightrange = 256.0;
1396 Entity* toAttack = nullptr;
1397 for ( node_t* node = map.creatures->first; node; node = node->next )
1398 {
1399 Entity* target = (Entity*)node->element;
1400 if ( (target->behavior == &actMonster || target->behavior == &actPlayer) && target != entity
1401 && entity->checkEnemy(target) )
1402 {
1403 int findx = static_cast<int>(target->x) >> 4;
1404 int findy = static_cast<int>(target->y) >> 4;
1405 if ( findx >= x1 && findx <= x2 && findy >= y1 && findy <= y2 )
1406 {
1407 real_t oldDist = dist;
1408 dist = sqrt(pow(entity->x - target->x, 2) + pow(entity->y - target->y, 2));
1409 if ( dist < sightrange && dist <= oldDist )
1410 {
1411 double tangent = atan2(target->y - entity->y, target->x - entity->x);
1412 //lineTrace(entity, entity->x, entity->y, tangent, sightrange, 0, false);
1413 //if ( hit.entity == target )
1414 //{
1415 //my->monsterLookTime = 1;
1416 //my->monsterMoveTime = rand() % 10 + 1;
1417 entity->monsterLookDir = tangent;
1418 toAttack = target;
1419 //}
1420 }
1421 else
1422 {
1423 dist = oldDist;
1424 }
1425 }
1426 }
1427 }
1428 if ( toAttack )
1429 {
1430 entity->monsterAcquireAttackTarget(*toAttack, MONSTER_STATE_PATH);
1431 }
1432 }
1433 }
1434 }
1435 }
1436 else if ( (*it).find("@killall=") != std::string::npos )
1437 {
1438 int result = textSourceProcessScriptTag(input, "@killall=");
1439 if ( result != k_ScriptError )
1440 {
1441 int x1 = result & 0xFF;
1442 int x2 = (result >> 8) & 0xFF;
1443 int y1 = (result >> 16) & 0xFF;
1444 int y2 = (result >> 24) & 0xFF;
1445 if ( processOnAttachedEntity )
1446 {
1447 for ( auto entity : attachedEntities )
1448 {
1449 if ( entity->behavior != &actMonster )
1450 {
1451 continue;
1452 }
1453 int findx = static_cast<int>(entity->x) >> 4;
1454 int findy = static_cast<int>(entity->y) >> 4;
1455 if ( findx >= x1 && findx <= x2 && findy >= y1 && findy <= y2 )
1456 {
1457 entity->setHP(0);
1458 }
1459 }
1460 }
1461 else
1462 {
1463 for ( node_t* node = map.creatures->first; node; node = node->next )
1464 {
1465 Entity* entity = (Entity*)node->element;
1466 if ( entity && entity->behavior == &actMonster )
1467 {
1468 int findx = static_cast<int>(entity->x) >> 4;
1469 int findy = static_cast<int>(entity->y) >> 4;
1470 if ( findx >= x1 && findx <= x2 && findy >= y1 && findy <= y2 )
1471 {
1472 entity->setHP(0);
1473 }
1474 }
1475 }
1476 }
1477 }
1478 }
1479 else if ( (*it).find("@killenemies=") != std::string::npos )
1480 {
1481 int result = textSourceProcessScriptTag(input, "@killenemies=");
1482 if ( result != k_ScriptError )
1483 {
1484 int x1 = result & 0xFF;
1485 int x2 = (result >> 8) & 0xFF;
1486 int y1 = (result >> 16) & 0xFF;
1487 int y2 = (result >> 24) & 0xFF;
1488 if ( processOnAttachedEntity )
1489 {
1490 for ( auto entity : attachedEntities )
1491 {
1492 if ( entity->behavior != &actMonster || !entity->monsterAllyGetPlayerLeader() )
1493 {
1494 continue;
1495 }
1496 int findx = static_cast<int>(entity->x) >> 4;
1497 int findy = static_cast<int>(entity->y) >> 4;
1498 if ( findx >= x1 && findx <= x2 && findy >= y1 && findy <= y2 )
1499 {
1500 entity->setHP(0);
1501 }
1502 }
1503 }
1504 else
1505 {
1506 for ( node_t* node = map.creatures->first; node; node = node->next )
1507 {
1508 Entity* entity = (Entity*)node->element;
1509 if ( entity && entity->behavior == &actMonster && !entity->monsterAllyGetPlayerLeader() )
1510 {
1511 int findx = static_cast<int>(entity->x) >> 4;
1512 int findy = static_cast<int>(entity->y) >> 4;
1513 if ( findx >= x1 && findx <= x2 && findy >= y1 && findy <= y2 )
1514 {
1515 entity->setHP(0);
1516 }
1517 }
1518 }
1519 }
1520 }
1521 }
1522 else if ( (*it).find("@nodropitems=") != std::string::npos )
1523 {
1524 int result = textSourceProcessScriptTag(input, "@nodropitems=");
1525 if ( result != k_ScriptError )
1526 {
1527 int x1 = result & 0xFF;
1528 int x2 = (result >> 8) & 0xFF;
1529 int y1 = (result >> 16) & 0xFF;
1530 int y2 = (result >> 24) & 0xFF;
1531 if ( processOnAttachedEntity )
1532 {
1533 for ( auto entity : attachedEntities )
1534 {
1535 if ( entity->behavior != &actMonster )
1536 {
1537 continue;
1538 }
1539 int findx = static_cast<int>(entity->x) >> 4;
1540 int findy = static_cast<int>(entity->y) >> 4;
1541 if ( findx >= x1 && findx <= x2 && findy >= y1 && findy <= y2 )
1542 {
1543 if ( entity->getStats() )
1544 {
1545 entity->getStats()->monsterNoDropItems = 1;
1546 }
1547 }
1548 }
1549 }
1550 else
1551 {
1552 for ( node_t* node = map.creatures->first; node; node = node->next )
1553 {
1554 Entity* entity = (Entity*)node->element;
1555 if ( entity && entity->behavior == &actMonster && !entity->monsterAllyGetPlayerLeader() )
1556 {
1557 int findx = static_cast<int>(entity->x) >> 4;
1558 int findy = static_cast<int>(entity->y) >> 4;
1559 if ( findx >= x1 && findx <= x2 && findy >= y1 && findy <= y2 )
1560 {
1561 if ( entity->getStats() )
1562 {
1563 entity->getStats()->monsterNoDropItems = 1;
1564 }
1565 }
1566 }
1567 }
1568 }
1569 }
1570 }
1571 else if ( (*it).find("@copyNPC=") != std::string::npos )
1572 {
1573 std::string profTag = "@copyNPC=";
1574 int result = textSourceProcessScriptTag(input, profTag);
1575 if ( result != k_ScriptError )
1576 {
1577 std::vector<Entity*> applyToEntities;
1578 if ( processOnAttachedEntity )
1579 {
1580 for ( auto entity : attachedEntities )
1581 {
1582 if ( entity->behavior != &actMonster && entity->behavior != &actPlayer )
1583 {
1584 continue;
1585 }
1586 if ( entity->behavior == &actPlayer && client_disconnected[entity->skill[2]] )
1587 {
1588 continue;
1589 }
1590 applyToEntities.push_back(entity);
1591 }
1592 }
1593
1594 for ( node_t* node = map.entities->first; node; node = node->next )
1595 {
1596 Entity* scriptEntity = (Entity*)node->element;
1597 if ( scriptEntity && scriptEntity->behavior == &actMonster )
1598 {
1599 Stat* scriptStats = scriptEntity->getStats();
1600 if ( scriptStats && !strcmp(scriptStats->name, "scriptNPC") && (scriptStats->MISC_FLAGS[STAT_FLAG_NPC] & 0xFF) == result )
1601 {
1602 // copy stats.
1603 if ( processOnAttachedEntity )
1604 {
1605 for ( auto entity : applyToEntities )
1606 {
1607 Stat* stats = entity->getStats();
1608 if ( stats )
1609 {
1610 stats->copyNPCStatsAndInventoryFrom(*scriptStats);
1611 if ( entity->behavior == &actPlayer )
1612 {
1613 statOnlyUpdateNeeded = true;
1614 }
1615 }
1616 }
1617 }
1618 else
1619 {
1620 for ( int c = 0; c < MAXPLAYERS && !client_disconnected[c] && players[c] && players[c]->entity; ++c )
1621 {
1622 stats[c]->copyNPCStatsAndInventoryFrom(*scriptStats);
1623 }
1624 statOnlyUpdateNeeded = true;
1625 }
1626 }
1627 }
1628 }
1629 }
1630 }
1631 else if ( (*it).find("@setenemy=") != std::string::npos )
1632 {
1633 std::string profTag = "@setenemy=";
1634 int result = textSourceProcessScriptTag(input, profTag);
1635 if ( result != k_ScriptError )
1636 {
1637 int x1 = result & 0xFF;
1638 int x2 = (result >> 8) & 0xFF;
1639 int y1 = (result >> 16) & 0xFF;
1640 int y2 = (result >> 24) & 0xFF;
1641 if ( processOnAttachedEntity )
1642 {
1643 for ( auto entity : attachedEntities )
1644 {
1645 if ( entity->behavior == &actMonster && !entity->monsterAllyGetPlayerLeader() )
1646 {
1647 int findx = static_cast<int>(entity->x) >> 4;
1648 int findy = static_cast<int>(entity->y) >> 4;
1649 if ( findx >= x1 && findx <= x2 && findy >= y1 && findy <= y2 )
1650 {
1651 if ( entity->getStats() )
1652 {
1653 entity->getStats()->monsterForceAllegiance = Stat::MONSTER_FORCE_PLAYER_ENEMY;
1654 serverUpdateEntityStatFlag(entity, 20);
1655 }
1656 }
1657 }
1658 }
1659 }
1660 else
1661 {
1662 for ( node_t* node = map.creatures->first; node; node = node->next )
1663 {
1664 Entity* entity = (Entity*)node->element;
1665 if ( entity && entity->behavior == &actMonster && !entity->monsterAllyGetPlayerLeader() )
1666 {
1667 int findx = static_cast<int>(entity->x) >> 4;
1668 int findy = static_cast<int>(entity->y) >> 4;
1669 if ( findx >= x1 && findx <= x2 && findy >= y1 && findy <= y2 )
1670 {
1671 if ( entity->getStats() )
1672 {
1673 entity->getStats()->monsterForceAllegiance = Stat::MONSTER_FORCE_PLAYER_ENEMY;
1674 serverUpdateEntityStatFlag(entity, 20);
1675 }
1676 }
1677 }
1678 }
1679 }
1680 }
1681 }
1682 else if ( (*it).find("@setally=") != std::string::npos )
1683 {
1684 std::string profTag = "@setally=";
1685 int result = textSourceProcessScriptTag(input, profTag);
1686 if ( result != k_ScriptError )
1687 {
1688 int x1 = result & 0xFF;
1689 int x2 = (result >> 8) & 0xFF;
1690 int y1 = (result >> 16) & 0xFF;
1691 int y2 = (result >> 24) & 0xFF;
1692 if ( processOnAttachedEntity )
1693 {
1694 for ( auto entity : attachedEntities )
1695 {
1696 if ( entity->behavior == &actMonster && !entity->monsterAllyGetPlayerLeader() )
1697 {
1698 int findx = static_cast<int>(entity->x) >> 4;
1699 int findy = static_cast<int>(entity->y) >> 4;
1700 if ( findx >= x1 && findx <= x2 && findy >= y1 && findy <= y2 )
1701 {
1702 if ( entity->getStats() )
1703 {
1704 entity->getStats()->monsterForceAllegiance = Stat::MONSTER_FORCE_PLAYER_ALLY;
1705 }
1706 }
1707 }
1708 }
1709 }
1710 else
1711 {
1712 for ( node_t* node = map.creatures->first; node; node = node->next )
1713 {
1714 Entity* entity = (Entity*)node->element;
1715 if ( entity && entity->behavior == &actMonster && !entity->monsterAllyGetPlayerLeader() )
1716 {
1717 int findx = static_cast<int>(entity->x) >> 4;
1718 int findy = static_cast<int>(entity->y) >> 4;
1719 if ( findx >= x1 && findx <= x2 && findy >= y1 && findy <= y2 )
1720 {
1721 if ( entity->getStats() )
1722 {
1723 entity->getStats()->monsterForceAllegiance = Stat::MONSTER_FORCE_PLAYER_ALLY;
1724 }
1725 }
1726 }
1727 }
1728 }
1729 }
1730 }
1731 else if ( (*it).find("@setvar=") != std::string::npos )
1732 {
1733 std::string profTag = "@setvar=";
1734 int result = textSourceProcessScriptTag(input, profTag);
1735 if ( result != k_ScriptError )
1736 {
1737 achievementObserver.checkMapScriptsOnVariableSet();
1738 /*for ( auto it = scriptVariables.begin(); it != scriptVariables.end(); ++it )
1739 {
1740 printlog("%s | %d", (*it).first.c_str(), (*it).second);
1741 }*/
1742 }
1743 }
1744 else if ( (*it).find("@addtochest=") != std::string::npos )
1745 {
1746 int result = textSourceProcessScriptTag(input, "@addtochest=");
1747 if ( result != k_ScriptError )
1748 {
1749 int x1 = result & 0xFF;
1750 int x2 = (result >> 8) & 0xFF;
1751 int y1 = (result >> 16) & 0xFF;
1752 int y2 = (result >> 24) & 0xFF;
1753 std::vector<Entity*> applyToEntities;
1754 if ( processOnAttachedEntity && textSourceScript.getAttachedToEntityType(src.textSourceIsScript) == textSourceScript.TO_ITEMS )
1755 {
1756 for ( auto entity : attachedEntities )
1757 {
1758 if ( entity->behavior == &actItem )
1759 {
1760 applyToEntities.push_back(entity);
1761 }
1762 }
1763 Entity* chest = nullptr;
1764 for ( node_t* node = map.entities->first; node; node = node->next )
1765 {
1766 Entity* entity = (Entity*)node->element;
1767 if ( entity && entity->behavior == &actChest )
1768 {
1769 int findx = static_cast<int>(entity->x) >> 4;
1770 int findy = static_cast<int>(entity->y) >> 4;
1771 if ( findx >= x1 && findx <= x2 && findy >= y1 && findy <= y2 )
1772 {
1773 chest = entity;
1774 }
1775 }
1776 }
1777 if ( chest )
1778 {
1779 for ( auto entity : applyToEntities )
1780 {
1781 Item* item = newItemFromEntity(entity);
1782 if ( item )
1783 {
1784 chest->addItemToChest(item);
1785 }
1786 list_RemoveNode(entity->mynode);
1787 entity = nullptr;
1788 }
1789 }
1790 }
1791 else
1792 {
1793 // not implemented, needs to be attached to items.
1794 }
1795 }
1796 }
1797 else
1798 {
1799 for ( int i = 0; i < NUMSTATS; ++i )
1800 {
1801 std::string profTag = "@st";
1802 switch ( i )
1803 {
1804 case STAT_STR:
1805 profTag.append("STR");
1806 break;
1807 case STAT_DEX:
1808 profTag.append("DEX");
1809 break;
1810 case STAT_CON:
1811 profTag.append("INT");
1812 break;
1813 case STAT_INT:
1814 profTag.append("CON");
1815 break;
1816 case STAT_PER:
1817 profTag.append("PER");
1818 break;
1819 case STAT_CHR:
1820 profTag.append("CHR");
1821 break;
1822 default:
1823 break;
1824 }
1825 profTag.append("=");
1826 if ( (*it).find(profTag) != std::string::npos )
1827 {
1828 int result = textSourceProcessScriptTag(input, profTag);
1829 if ( result != k_ScriptError )
1830 {
1831 std::vector<Entity*> applyToEntities;
1832 if ( processOnAttachedEntity )
1833 {
1834 for ( auto entity : attachedEntities )
1835 {
1836 if ( entity->behavior == &actMonster
1837 || (entity->behavior == &actPlayer && stats[entity->skill[2]] && !client_disconnected[entity->skill[2]]) )
1838 {
1839 applyToEntities.push_back(entity);
1840 statOnlyUpdateNeeded = (entity->behavior == &actPlayer);
1841 }
1842 }
1843 }
1844 else
1845 {
1846 for ( int c = 0; c < MAXPLAYERS; ++c )
1847 {
1848 if ( stats[c] && !client_disconnected[c] && players[c] && players[c]->entity )
1849 {
1850 applyToEntities.push_back(players[c]->entity);
1851 statOnlyUpdateNeeded = true;
1852 }
1853 }
1854 }
1855 for ( auto entity : applyToEntities )
1856 {
1857 Stat* stats = entity->getStats();
1858 if ( stats )
1859 {
1860 switch ( i )
1861 {
1862 case STAT_STR:
1863 stats->STR = result;
1864 break;
1865 case STAT_DEX:
1866 stats->DEX = result;
1867 break;
1868 case STAT_CON:
1869 stats->CON = result;
1870 break;
1871 case STAT_INT:
1872 stats->INT = result;
1873 break;
1874 case STAT_PER:
1875 stats->PER = result;
1876 break;
1877 case STAT_CHR:
1878 stats->CHR = result;
1879 break;
1880 default:
1881 break;
1882 }
1883 }
1884 }
1885 statOnlyUpdateNeeded = true;
1886 }
1887 }
1888 }
1889 for ( int i = 0; i < NUMSTATS; ++i )
1890 {
1891 std::string profTag = "@st";
1892 switch ( i )
1893 {
1894 case STAT_STR:
1895 profTag.append("STR");
1896 break;
1897 case STAT_DEX:
1898 profTag.append("DEX");
1899 break;
1900 case STAT_CON:
1901 profTag.append("INT");
1902 break;
1903 case STAT_INT:
1904 profTag.append("CON");
1905 break;
1906 case STAT_PER:
1907 profTag.append("PER");
1908 break;
1909 case STAT_CHR:
1910 profTag.append("CHR");
1911 break;
1912 default:
1913 break;
1914 }
1915 profTag.append("+");
1916 if ( (*it).find(profTag) != std::string::npos )
1917 {
1918 int result = textSourceProcessScriptTag(input, profTag);
1919 if ( result != k_ScriptError )
1920 {
1921 std::vector<Entity*> applyToEntities;
1922 if ( processOnAttachedEntity )
1923 {
1924 for ( auto entity : attachedEntities )
1925 {
1926 if ( entity->behavior == &actMonster
1927 || (entity->behavior == &actPlayer && stats[entity->skill[2]] && !client_disconnected[entity->skill[2]]) )
1928 {
1929 applyToEntities.push_back(entity);
1930 statOnlyUpdateNeeded = (entity->behavior == &actPlayer);
1931 }
1932 }
1933 }
1934 else
1935 {
1936 for ( int c = 0; c < MAXPLAYERS; ++c )
1937 {
1938 if ( stats[c] && !client_disconnected[c] && players[c] && players[c]->entity )
1939 {
1940 applyToEntities.push_back(players[c]->entity);
1941 statOnlyUpdateNeeded = true;
1942 }
1943 }
1944 }
1945 for ( auto entity : applyToEntities )
1946 {
1947 Stat* stats = entity->getStats();
1948 if ( stats )
1949 {
1950 switch ( i )
1951 {
1952 case STAT_STR:
1953 stats->STR += result;
1954 break;
1955 case STAT_DEX:
1956 stats->DEX += result;
1957 break;
1958 case STAT_CON:
1959 stats->CON += result;
1960 break;
1961 case STAT_INT:
1962 stats->INT += result;
1963 break;
1964 case STAT_PER:
1965 stats->PER += result;
1966 break;
1967 case STAT_CHR:
1968 stats->CHR += result;
1969 break;
1970 default:
1971 break;
1972 }
1973 }
1974 }
1975 }
1976 }
1977 }
1978
1979 for ( int i = 0; i < NUMPROFICIENCIES; ++i )
1980 {
1981 std::string profTag = "@pro";
1982 profTag.append(std::to_string(i).append("="));
1983 if ( (*it).find(profTag) != std::string::npos )
1984 {
1985 int result = textSourceProcessScriptTag(input, profTag);
1986 if ( result != k_ScriptError )
1987 {
1988 std::vector<Entity*> applyToEntities;
1989 if ( processOnAttachedEntity )
1990 {
1991 for ( auto entity : attachedEntities )
1992 {
1993 if ( entity->behavior == &actMonster
1994 || (entity->behavior == &actPlayer && stats[entity->skill[2]] && !client_disconnected[entity->skill[2]]) )
1995 {
1996 applyToEntities.push_back(entity);
1997 statOnlyUpdateNeeded = (entity->behavior == &actPlayer);
1998 }
1999 }
2000 }
2001 else
2002 {
2003 for ( int c = 0; c < MAXPLAYERS; ++c )
2004 {
2005 if ( stats[c] && !client_disconnected[c] && players[c] && players[c]->entity )
2006 {
2007 applyToEntities.push_back(players[c]->entity);
2008 statOnlyUpdateNeeded = true;
2009 }
2010 }
2011 }
2012
2013 for ( auto entity : applyToEntities )
2014 {
2015 Stat* stats = entity->getStats();
2016 if ( stats )
2017 {
2018 stats->PROFICIENCIES[i] = result;
2019 }
2020 }
2021 }
2022 }
2023 }
2024 for ( int i = 0; i < NUMPROFICIENCIES; ++i )
2025 {
2026 std::string profTag = "@pro";
2027 profTag.append(std::to_string(i).append("+"));
2028 if ( (*it).find(profTag) != std::string::npos )
2029 {
2030 int result = textSourceProcessScriptTag(input, profTag);
2031 if ( result != k_ScriptError )
2032 {
2033 std::vector<Entity*> applyToEntities;
2034 if ( processOnAttachedEntity )
2035 {
2036 for ( auto entity : attachedEntities )
2037 {
2038 if ( entity->behavior == &actMonster
2039 || (entity->behavior == &actPlayer && stats[entity->skill[2]] && !client_disconnected[entity->skill[2]]) )
2040 {
2041 applyToEntities.push_back(entity);
2042 statOnlyUpdateNeeded = (entity->behavior == &actPlayer);
2043 }
2044 }
2045 }
2046 else
2047 {
2048 for ( int c = 0; c < MAXPLAYERS; ++c )
2049 {
2050 if ( stats[c] && !client_disconnected[c] && players[c] && players[c]->entity )
2051 {
2052 applyToEntities.push_back(players[c]->entity);
2053 statOnlyUpdateNeeded = true;
2054 }
2055 }
2056 }
2057
2058 for ( auto entity : applyToEntities )
2059 {
2060 Stat* stats = entity->getStats();
2061 if ( stats )
2062 {
2063 stats->PROFICIENCIES[i] += result;
2064 }
2065 }
2066 }
2067 }
2068 }
2069 }
2070 }
2071 printlog("%s", executionLog.c_str());
2072 if ( statOnlyUpdateNeeded )
2073 {
2074 for ( int c = 1; c < MAXPLAYERS; ++c )
2075 {
2076 updateClientInformation(c, false, false, TextSourceScript::CLIENT_UPDATE_ALL);
2077 }
2078 }
2079 printlog("[SCRIPT]: Finished running.");
2080 }
2081
actTextSource()2082 void Entity::actTextSource()
2083 {
2084 if ( multiplayer == CLIENT )
2085 {
2086 return;
2087 }
2088
2089 if ( ((textSourceVariables4W >> 16) & 0xFFFF) == 0 ) // store the delay in the 16 leftmost bits.
2090 {
2091 textSourceVariables4W |= (textSourceDelay << 16);
2092 }
2093
2094 bool powered = false;
2095 if ( textSourceScript.getScriptType(textSourceIsScript) == textSourceScript.NO_SCRIPT
2096 || textSourceScript.getTriggerType(textSourceIsScript) == textSourceScript.TRIGGER_POWER )
2097 {
2098 powered = (circuit_status == CIRCUIT_ON);
2099 }
2100 else if ( textSourceScript.getTriggerType(textSourceIsScript) == textSourceScript.TRIGGER_ATTACHED_ALWAYS )
2101 {
2102 textSourceScript.setScriptType(textSourceIsScript, textSourceScript.SCRIPT_ATTACHED_FIRED);
2103 powered = true;
2104 }
2105 else if ( textSourceScript.getScriptType(textSourceIsScript) == textSourceScript.SCRIPT_ATTACHED )
2106 {
2107 int entitiesExisting = 0;
2108 int entitiesVisible = 0;
2109 int entitiesInvisible = 0;
2110 // check if our attached entities still exist.
2111 for ( node_t* node = children.first; node; node = node->next )
2112 {
2113 Uint32 entityUid = *((Uint32*)node->element);
2114 Entity* child = uidToEntity(entityUid);
2115 if ( child )
2116 {
2117 ++entitiesExisting;
2118 if ( child->flags[INVISIBLE] )
2119 {
2120 ++entitiesInvisible;
2121 }
2122 else
2123 {
2124 ++entitiesVisible;
2125 }
2126 }
2127 }
2128 if ( entitiesExisting == 0 )
2129 {
2130 if ( textSourceScript.getTriggerType(textSourceIsScript) == textSourceScript.TRIGGER_ATTACHED_ISREMOVED )
2131 {
2132 textSourceScript.setScriptType(textSourceIsScript, textSourceScript.SCRIPT_ATTACHED_FIRED);
2133 powered = true;
2134 }
2135 }
2136 else
2137 {
2138 if ( textSourceScript.getTriggerType(textSourceIsScript) == textSourceScript.TRIGGER_ATTACHED_EXISTS )
2139 {
2140 textSourceScript.setScriptType(textSourceIsScript, textSourceScript.SCRIPT_ATTACHED_FIRED);
2141 powered = true;
2142 }
2143 else if ( textSourceScript.getTriggerType(textSourceIsScript) == textSourceScript.TRIGGER_ATTACHED_INVIS
2144 && entitiesInvisible == entitiesExisting )
2145 {
2146 textSourceScript.setScriptType(textSourceIsScript, textSourceScript.SCRIPT_ATTACHED_FIRED);
2147 powered = true;
2148 }
2149 else if ( textSourceScript.getTriggerType(textSourceIsScript) == textSourceScript.TRIGGER_ATTACHED_VISIBLE
2150 && entitiesVisible == entitiesExisting )
2151 {
2152 textSourceScript.setScriptType(textSourceIsScript, textSourceScript.SCRIPT_ATTACHED_FIRED);
2153 powered = true;
2154 }
2155 }
2156 }
2157 else if ( textSourceScript.getScriptType(textSourceIsScript) == textSourceScript.SCRIPT_ATTACHED_FIRED )
2158 {
2159 powered = true;
2160 }
2161
2162 if ( textSourceScript.getScriptType(textSourceIsScript) != textSourceScript.NO_SCRIPT )
2163 {
2164 if ( ticks <= 2 )
2165 {
2166 return;
2167 }
2168 }
2169
2170 if ( powered )
2171 {
2172 // received power
2173 if ( textSourceDelay > 0 )
2174 {
2175 --textSourceDelay;
2176 return;
2177 }
2178 else
2179 {
2180 textSourceDelay = (textSourceVariables4W >> 16) & 0xFFFF;
2181 }
2182 if ( (textSourceVariables4W & 0xFF) == 0 )
2183 {
2184 textSourceVariables4W |= 1;
2185
2186 std::string output = textSourceScript.getScriptFromEntity(*this);
2187
2188 Uint32 color = SDL_MapRGB(mainsurface->format, (textSourceColorRGB >> 16) & 0xFF, (textSourceColorRGB >> 8) & 0xFF,
2189 (textSourceColorRGB >> 0) & 0xFF);
2190
2191 if ( textSourceIsScript != textSourceScript.NO_SCRIPT )
2192 {
2193 textSourceScript.handleTextSourceScript(*this, output);
2194 return;
2195 }
2196
2197 size_t foundPlayerRef = output.find("@p");
2198 if ( foundPlayerRef != std::string::npos )
2199 {
2200 output.erase(foundPlayerRef, 2);
2201 output.insert(foundPlayerRef, "%s");
2202 }
2203
2204 size_t foundDistanceRequirement = output.find("@d");
2205 int distanceRequirement = -1;
2206 if ( foundDistanceRequirement != std::string::npos )
2207 {
2208 output.erase(foundDistanceRequirement, 2);
2209 std::string distance;
2210 while ( foundDistanceRequirement < output.length()
2211 && output.at(foundDistanceRequirement) != ' '
2212 && output.at(foundDistanceRequirement) != '\0'
2213 )
2214 {
2215 // usage: Hello @p @d 123 will send to distance 123 units away and send message "Hello player "
2216 distance = distance + output.at(foundDistanceRequirement);
2217 ++foundDistanceRequirement;
2218 }
2219 distanceRequirement = std::stoi(distance);
2220 output.erase(output.find(distance), distance.length());
2221 }
2222
2223 size_t foundInputTag = output.find("@in=");
2224 while ( foundInputTag != std::string::npos )
2225 {
2226 output.erase(foundInputTag, strlen("@in="));
2227 std::string impulseStr;
2228 size_t inputTagStrIndex = foundInputTag;
2229 while ( inputTagStrIndex < output.length()
2230 && output.at(inputTagStrIndex) != ' '
2231 && output.at(inputTagStrIndex) != '.'
2232 && output.at(inputTagStrIndex) != ','
2233 && output.at(inputTagStrIndex) != '\0'
2234 )
2235 {
2236 // usage: @in=IN_USE will get the input key for use
2237 impulseStr = impulseStr + output.at(inputTagStrIndex);
2238 ++inputTagStrIndex;
2239 }
2240 for ( int i = 0; i < NUMIMPULSES; ++i )
2241 {
2242 if ( impulseStrings[i].compare(impulseStr) == 0 )
2243 {
2244 output.erase(output.find(impulseStr), impulseStr.length());
2245 std::string inputFormatted = "[";
2246 inputFormatted.append(getInputName(impulses[i])).append("]");
2247 output.insert(foundInputTag, inputFormatted.c_str());
2248 break;
2249 }
2250 }
2251 foundInputTag = output.find("@in=");
2252 }
2253
2254 size_t found = output.find("\\n");
2255 while ( found != std::string::npos )
2256 {
2257 output.erase(found, 2);
2258 output.insert(found, 1, '\n');
2259 found = output.find("\\n");
2260 }
2261
2262 char buf[256] = "";
2263 strcpy(buf, output.c_str());
2264
2265 for ( int c = 0; c < MAXPLAYERS; ++c )
2266 {
2267 if ( !client_disconnected[c] )
2268 {
2269 if ( distanceRequirement != -1 && !(players[c] && players[c]->entity && entityDist(this, players[c]->entity) <= distanceRequirement) )
2270 {
2271 // not in range.
2272 }
2273 else
2274 {
2275 if ( foundPlayerRef != std::string::npos && stats[c] )
2276 {
2277 messagePlayerColor(c, color, buf, stats[c]->name);
2278 }
2279 else
2280 {
2281 messagePlayerColor(c, color, buf);
2282 }
2283 }
2284 }
2285 }
2286 }
2287 }
2288 else if ( !powered )
2289 {
2290 textSourceDelay = (textSourceVariables4W >> 16) & 0xFFFF;
2291 if ( (textSourceVariables4W & 0xFF) == 1 && ((textSourceVariables4W >> 8) & 0xFF) == 0 )
2292 {
2293 textSourceVariables4W -= 1;
2294 }
2295 }
2296 }
2297
updateClientInformation(int player,bool clearInventory,bool clearStats,ClientInformationType updateType)2298 void TextSourceScript::updateClientInformation(int player, bool clearInventory, bool clearStats, ClientInformationType updateType)
2299 {
2300 if ( multiplayer != SERVER )
2301 {
2302 return;
2303 }
2304 if ( !stats[player] || client_disconnected[player] )
2305 {
2306 return;
2307 }
2308
2309 if ( updateType == CLIENT_UPDATE_ALL )
2310 {
2311 // update client attributes
2312 strcpy((char*)net_packet->data, "SCRU");
2313 net_packet->data[4] = clientnum;
2314 net_packet->data[5] = (Sint8)stats[player]->STR;
2315 net_packet->data[6] = (Sint8)stats[player]->DEX;
2316 net_packet->data[7] = (Sint8)stats[player]->CON;
2317 net_packet->data[8] = (Sint8)stats[player]->INT;
2318 net_packet->data[9] = (Sint8)stats[player]->PER;
2319 net_packet->data[10] = (Sint8)stats[player]->CHR;
2320 net_packet->data[11] = (Sint8)stats[player]->EXP;
2321 net_packet->data[12] = (Sint8)stats[player]->LVL;
2322 SDLNet_Write16((Sint16)stats[player]->HP, &net_packet->data[13]);
2323 SDLNet_Write16((Sint16)stats[player]->MAXHP, &net_packet->data[15]);
2324 SDLNet_Write16((Sint16)stats[player]->MP, &net_packet->data[17]);
2325 SDLNet_Write16((Sint16)stats[player]->MAXMP, &net_packet->data[19]);
2326 SDLNet_Write32((Sint32)stats[player]->GOLD, &net_packet->data[21]);
2327 if ( clearInventory )
2328 {
2329 net_packet->data[25] = 1;
2330 }
2331 else
2332 {
2333 net_packet->data[25] = 0;
2334 }
2335 if ( clearStats )
2336 {
2337 net_packet->data[26] = 1;
2338 }
2339 else
2340 {
2341 net_packet->data[26] = 0;
2342 }
2343
2344 for ( int i = 0; i < NUMPROFICIENCIES; ++i )
2345 {
2346 net_packet->data[27 + i] = (Uint8)stats[player]->PROFICIENCIES[i];
2347 }
2348 net_packet->address.host = net_clients[player - 1].host;
2349 net_packet->address.port = net_clients[player - 1].port;
2350 net_packet->len = 27 + NUMPROFICIENCIES;
2351 sendPacketSafe(net_sock, -1, net_packet, player - 1);
2352
2353 serverUpdatePlayerLVL();
2354 }
2355 else if ( updateType == CLIENT_UPDATE_CLASS )
2356 {
2357 strcpy((char*)net_packet->data, "SCRC");
2358 for ( int i = 0; i < MAXPLAYERS; ++i )
2359 {
2360 net_packet->data[4 + i] = client_classes[i];
2361 }
2362 net_packet->address.host = net_clients[player - 1].host;
2363 net_packet->address.port = net_clients[player - 1].port;
2364 net_packet->len = 5 + MAXPLAYERS;
2365 sendPacketSafe(net_sock, -1, net_packet, player - 1);
2366 }
2367 else if ( updateType == CLIENT_UPDATE_HUNGER )
2368 {
2369 serverUpdateHunger(player);
2370 }
2371 }
2372
playerClearInventory(bool clearStats)2373 void TextSourceScript::playerClearInventory(bool clearStats)
2374 {
2375 deinitShapeshiftHotbar();
2376 for ( int c = 0; c < NUM_HOTBAR_ALTERNATES; ++c )
2377 {
2378 selected_spell_alternate[c] = NULL;
2379 hotbarShapeshiftInit[c] = false;
2380 }
2381 selected_spell = NULL; //So you don't start off with a spell when the game restarts.
2382 selected_spell_last_appearance = -1;
2383 spellcastingAnimationManager_deactivate(&cast_animation);
2384 stats[clientnum]->freePlayerEquipment();
2385 list_FreeAll(&stats[clientnum]->inventory);
2386 shootmode = true;
2387 appraisal_timer = 0;
2388 appraisal_item = 0;
2389
2390 if ( clearStats )
2391 {
2392 stats[clientnum]->clearStats();
2393 }
2394
2395 this->hasClearedInventory = true;
2396 }
2397
getScriptFromEntity(Entity & src)2398 std::string TextSourceScript::getScriptFromEntity(Entity& src)
2399 {
2400 // assemble the string.
2401 char buf[256] = "";
2402 int totalChars = 0;
2403 for ( int i = 4; i < 60; ++i )
2404 {
2405 if ( i == 28 ) // circuit_status
2406 {
2407 continue;
2408 }
2409 if ( src.skill[i] != 0 )
2410 {
2411 for ( int c = 0; c < 4; ++c )
2412 {
2413 buf[totalChars] = static_cast<char>((src.skill[i] >> (c * 8)) & 0xFF);
2414 ++totalChars;
2415 }
2416 }
2417 }
2418 if ( buf[totalChars] != '\0' )
2419 {
2420 buf[totalChars] = '\0';
2421 }
2422 return buf;
2423 }
2424
parseScriptInMapGeneration(Entity & src)2425 void TextSourceScript::parseScriptInMapGeneration(Entity& src)
2426 {
2427 std::string script = getScriptFromEntity(src);
2428 size_t foundScriptTag = script.find("@script");
2429 if ( foundScriptTag != std::string::npos )
2430 {
2431 if ( (foundScriptTag + strlen("@script")) < script.length()
2432 && script.at(foundScriptTag + strlen("@script")) == ' ' )
2433 {
2434 script.erase(foundScriptTag, strlen("@script") + 1); // trailing space.
2435 }
2436 else
2437 {
2438 script.erase(foundScriptTag, strlen("@script"));
2439 }
2440 textSourceScript.setScriptType(src.textSourceIsScript, textSourceScript.SCRIPT_NORMAL);
2441 textSourceScript.setTriggerType(src.textSourceIsScript, textSourceScript.TRIGGER_POWER);
2442 }
2443 if ( src.textSourceIsScript == NO_SCRIPT )
2444 {
2445 return;
2446 }
2447
2448 if ( script.find("@triggerif=") != std::string::npos )
2449 {
2450 int result = textSourceProcessScriptTag(script, "@triggerif=");
2451 if ( result != k_ScriptError )
2452 {
2453 textSourceScript.setTriggerType(src.textSourceIsScript, static_cast<ScriptTriggeredBy>(result));
2454 }
2455 }
2456
2457 if ( script.find("@attachto=") != std::string::npos )
2458 {
2459 int attachTo = textSourceProcessScriptTag(script, "@attachto=");
2460 if ( attachTo == k_ScriptError )
2461 {
2462 return;
2463 }
2464 textSourceScript.setScriptType(src.textSourceIsScript, textSourceScript.SCRIPT_ATTACHED);
2465 int x1 = static_cast<int>(src.x / 16); // default to just whatever this script is sitting on.
2466 int x2 = static_cast<int>(src.x / 16);
2467 int y1 = static_cast<int>(src.y / 16);
2468 int y2 = static_cast<int>(src.y / 16);
2469 if ( script.find("@attachrange=") != std::string::npos )
2470 {
2471 int result = textSourceProcessScriptTag(script, "@attachrange=");
2472 if ( result != k_ScriptError )
2473 {
2474 x1 = result & 0xFF;
2475 x2 = (result >> 8) & 0xFF;
2476 y1 = (result >> 16) & 0xFF;
2477 y2 = (result >> 24) & 0xFF;
2478 }
2479 }
2480 textSourceScript.setAttachedToEntityType(src.textSourceIsScript, attachTo);
2481 for ( node_t* node = map.entities->first; node; node = node->next )
2482 {
2483 Entity* entity = (Entity*)node->element;
2484 if ( entity )
2485 {
2486 if ( (entity->behavior == &actMonster && attachTo == TO_MONSTERS)
2487 || (entity->behavior == &actPlayer && attachTo == TO_PLAYERS)
2488 || (entity->behavior == &actItem && attachTo == TO_ITEMS)
2489 || (entity->behavior == &actMonster
2490 && attachTo >= TO_NOTHING && attachTo <= TO_DUMMYBOT
2491 && entity->getRace() == (attachTo - TO_NOTHING)) )
2492 {
2493 // found our entity.
2494 }
2495 else
2496 {
2497 continue;
2498 }
2499 int findx = static_cast<int>(entity->x) >> 4;
2500 int findy = static_cast<int>(entity->y) >> 4;
2501 if ( findx >= x1 && findx <= x2 && findy >= y1 && findy <= y2 )
2502 {
2503 node_t* node = list_AddNodeLast(&src.children);
2504 node->deconstructor = &defaultDeconstructor;
2505 Uint32* entityUid = (Uint32*)(malloc(sizeof(Uint32)));
2506 node->element = entityUid;
2507 node->size = sizeof(Uint32);
2508 *entityUid = entity->getUID();
2509 }
2510 }
2511 }
2512 }
2513 }