1 /*
2 * Copyright 2011-2013 Arx Libertatis Team (see the AUTHORS file)
3 *
4 * This file is part of Arx Libertatis.
5 *
6 * Arx Libertatis is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation, either version 3 of the License, or
9 * (at your option) any later version.
10 *
11 * Arx Libertatis is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with Arx Libertatis. If not, see <http://www.gnu.org/licenses/>.
18 */
19 /* Based on:
20 ===========================================================================
21 ARX FATALIS GPL Source Code
22 Copyright (C) 1999-2010 Arkane Studios SA, a ZeniMax Media company.
23
24 This file is part of the Arx Fatalis GPL Source Code ('Arx Fatalis Source Code').
25
26 Arx Fatalis Source Code is free software: you can redistribute it and/or modify it under the terms of the GNU General Public
27 License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
28
29 Arx Fatalis Source Code is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied
30 warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
31
32 You should have received a copy of the GNU General Public License along with Arx Fatalis Source Code. If not, see
33 <http://www.gnu.org/licenses/>.
34
35 In addition, the Arx Fatalis Source Code is also subject to certain additional terms. You should have received a copy of these
36 additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Arx
37 Fatalis Source Code. If not, please request a copy in writing from Arkane Studios at the address below.
38
39 If you have questions concerning this license or the applicable additional terms, you may contact in writing Arkane Studios, c/o
40 ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
41 ===========================================================================
42 */
43 // Code: Cyril Meynier
44 //
45 // Copyright (c) 1999-2000 ARKANE Studios SA. All rights reserved
46
47 #include "script/Script.h"
48
49 #include <stddef.h>
50
51 #include <sstream>
52 #include <cstdio>
53 #include <algorithm>
54
55 #include <boost/algorithm/string/predicate.hpp>
56
57 #include "ai/Paths.h"
58
59 #include "core/GameTime.h"
60 #include "core/Core.h"
61 #include "core/Config.h"
62
63 #include "game/Camera.h"
64 #include "game/Damage.h"
65 #include "game/EntityManager.h"
66 #include "game/Equipment.h"
67 #include "game/Inventory.h"
68 #include "game/Item.h"
69 #include "game/NPC.h"
70 #include "game/Player.h"
71
72 #include "gui/Speech.h"
73
74 #include "graphics/particle/ParticleEffects.h"
75 #include "graphics/Math.h"
76
77 #include "io/resource/PakReader.h"
78 #include "io/log/Logger.h"
79
80 #include "scene/Scene.h"
81 #include "scene/Interactive.h"
82
83 #include "script/ScriptEvent.h"
84
85 using std::sprintf;
86 using std::min;
87 using std::max;
88 using std::transform;
89 using std::string;
90
91 #define MAX_SSEPARAMS 5
92
93 extern long lChangeWeapon;
94 extern Entity * pIOChangeWeapon;
95
96 Entity * LASTSPAWNED = NULL;
97 Entity * EVENT_SENDER = NULL;
98 SCRIPT_VAR * svar = NULL;
99
100 static char SSEPARAMS[MAX_SSEPARAMS][64];
101 long FORBID_SCRIPT_IO_CREATION = 0;
102 long NB_GLOBALS = 0;
103 SCR_TIMER * scr_timer = NULL;
104 long ActiveTimers = 0;
105
FindScriptPos(const EERIE_SCRIPT * es,const string & str)106 long FindScriptPos(const EERIE_SCRIPT * es, const string & str) {
107
108 // TODO(script-parser) remove, respect quoted strings
109
110 const char * start = es->data;
111 const char * end = es->data + es->size;
112
113 while(true) {
114
115 const char * dat = std::search(start, end, str.begin(), str.end());
116 if(dat + str.length() >= end) {
117 return -1;
118 }
119
120 start = dat + 1;
121 if(((unsigned char)dat[str.length()]) > 32) {
122 continue;
123 }
124
125 // Check if the line is commented out!
126 for(const char * search = dat; search[0] != '/' || search[1] != '/'; search--) {
127 if(*search == '\n' || search == es->data) {
128 return dat - es->data;
129 }
130 }
131
132 }
133
134 return -1;
135 }
136
SendMsgToAllIO(ScriptMessage msg,const string & params)137 ScriptResult SendMsgToAllIO(ScriptMessage msg, const string & params) {
138
139 ScriptResult ret = ACCEPT;
140
141 for(size_t i = 0; i < entities.size(); i++) {
142 if(entities[i]) {
143 if(SendIOScriptEvent(entities[i], msg, params) == REFUSE) {
144 ret = REFUSE;
145 }
146 }
147 }
148
149 return ret;
150 }
151
ARX_SCRIPT_SetMainEvent(Entity * io,const string & newevent)152 void ARX_SCRIPT_SetMainEvent(Entity * io, const string & newevent) {
153
154 if(!io) {
155 return;
156 }
157
158 if(newevent == "main") {
159 io->mainevent.clear();
160 } else {
161 io->mainevent = newevent;
162 }
163 }
164
165 //*************************************************************************************
166 //*************************************************************************************
ARX_SCRIPT_ResetObject(Entity * io,long flags)167 void ARX_SCRIPT_ResetObject(Entity * io, long flags)
168 {
169 // Now go for Script INIT/RESET depending on Mode
170 if(io) {
171
172 long num = io->index();
173
174 if (entities[num] && entities[num]->script.data)
175 {
176 entities[num]->script.allowevents = 0;
177
178 if (flags) ScriptEvent::send(&entities[num]->script, SM_INIT, "", entities[num], "");
179
180
181 if (entities[num])
182 ARX_SCRIPT_SetMainEvent(entities[num], "main");
183 }
184
185 // Do the same for Local Script
186 if (entities[num] && entities[num]->over_script.data)
187 {
188 entities[num]->over_script.allowevents = 0;
189
190 if (flags) ScriptEvent::send(&entities[num]->over_script, SM_INIT, "", entities[num], "");
191
192
193 }
194
195 // Sends InitEnd Event
196 if (flags)
197 {
198 if (entities[num] && entities[num]->script.data)
199 ScriptEvent::send(&entities[num]->script, SM_INITEND, "", entities[num], "");
200
201 if (entities[num] && entities[num]->over_script.data)
202 ScriptEvent::send(&entities[num]->over_script, SM_INITEND, "", entities[num], "");
203 }
204
205 if (entities[num])
206 entities[num]->gameFlags &= ~GFLAG_NEEDINIT;
207 }
208 }
209
ARX_SCRIPT_Reset(Entity * io,long flags)210 void ARX_SCRIPT_Reset(Entity * io, long flags) {
211
212 //Release Script Local Variables
213 if(io->script.lvar) {
214 for(long n = 0; n < io->script.nblvar; n++) {
215 free(io->script.lvar[n].text), io->script.lvar[n].text = NULL;
216 }
217 io->script.nblvar = 0;
218 free(io->script.lvar), io->script.lvar = NULL;
219 }
220
221 //Release Script Over-Script Local Variables
222 if(io->over_script.lvar) {
223 for(long n = 0; n < io->over_script.nblvar; n++) {
224 free(io->over_script.lvar[n].text), io->over_script.lvar[n].text = NULL;
225 }
226 io->over_script.nblvar = 0;
227 free(io->over_script.lvar), io->over_script.lvar = NULL;
228 }
229
230 if(!io->scriptload) {
231 ARX_SCRIPT_ResetObject(io, flags);
232 }
233 }
234
ARX_SCRIPT_ResetAll(long flags)235 void ARX_SCRIPT_ResetAll(long flags) {
236 for(size_t i = 0; i < entities.size(); i++) {
237 if(entities[i] && !entities[i]->scriptload) {
238 ARX_SCRIPT_Reset(entities[i], flags);
239 }
240 }
241 }
242
ARX_SCRIPT_AllowInterScriptExec()243 void ARX_SCRIPT_AllowInterScriptExec() {
244
245 static long ppos = 0;
246
247 if(EDITMODE || arxtime.is_paused()) {
248 return;
249 }
250
251 EVENT_SENDER = NULL;
252
253 long heartbeat_count = min(long(entities.size()), 10l);
254
255 for(long n = 0; n < heartbeat_count; n++) {
256
257 long i = ppos++;
258 if(i >= long(entities.size())){
259 ppos = 0;
260 return;
261 }
262
263 if(entities[i] == NULL || !(entities[i]->gameFlags & GFLAG_ISINTREATZONE)) {
264 continue;
265 }
266
267 if(!entities[i]->mainevent.empty()) {
268
269 // Copy the even name to a local variable as it may change during execution
270 // and cause unexpected behavior in SendIOScriptEvent
271 std::string event = entities[i]->mainevent;
272
273 SendIOScriptEvent(entities[i], SM_NULL, std::string(), event);
274
275 } else {
276 SendIOScriptEvent(entities[i], SM_MAIN);
277 }
278 }
279 }
280
ARX_SCRIPT_ReleaseLabels(EERIE_SCRIPT * es)281 void ARX_SCRIPT_ReleaseLabels(EERIE_SCRIPT * es) {
282
283 if(!es || !es->labels) {
284 return;
285 }
286
287 for(long i = 0; i < es->nb_labels; i++) {
288 free(es->labels[i].string);
289 }
290 free(es->labels), es->labels = NULL, es->nb_labels = 0;
291 }
292
ReleaseScript(EERIE_SCRIPT * es)293 void ReleaseScript(EERIE_SCRIPT * es) {
294
295 if(!es) {
296 return;
297 }
298
299 if(es->lvar) {
300 for(long i = 0; i < es->nblvar; i++) {
301 free(es->lvar[i].text);
302 }
303 free(es->lvar), es->lvar = NULL;
304 }
305
306 free(es->data), es->data = NULL;
307
308 ARX_SCRIPT_ReleaseLabels(es);
309 memset(es->shortcut, 0, sizeof(long) * MAX_SHORTCUT);
310 }
311
getSystemVar(const EERIE_SCRIPT * es,Entity * entity,const string & name,std::string & txtcontent,float * fcontent,long * lcontent)312 ValueType getSystemVar(const EERIE_SCRIPT * es, Entity * entity, const string & name,
313 std::string& txtcontent, float * fcontent,long * lcontent) {
314
315 arx_assert_msg(!name.empty() && name[0] == '^', "bad system variable: \"%s\"",
316 name.c_str());
317
318 char c = (name.length() < 2) ? '\0' : name[1];
319 switch(c) {
320
321 case '$': {
322
323 if(name == "^$param1") {
324 txtcontent = SSEPARAMS[0];
325 return TYPE_TEXT;
326 }
327
328 if(name == "^$param2") {
329 txtcontent = SSEPARAMS[1];
330 return TYPE_TEXT;
331 }
332
333 if(name == "^$param3") {
334 txtcontent = SSEPARAMS[2];
335 return TYPE_TEXT;
336 }
337
338 if(name == "^$objontop") {
339 txtcontent = "none";
340 if(entity) {
341 MakeTopObjString(entity, txtcontent);
342 }
343 return TYPE_TEXT;
344 }
345
346 break;
347 }
348
349 case '&': {
350
351 if(name == "^¶m1") {
352 *fcontent = (float)atof(SSEPARAMS[0]);
353 return TYPE_FLOAT;
354 }
355
356 if(name == "^¶m2") {
357 *fcontent = (float)atof(SSEPARAMS[1]);
358 return TYPE_FLOAT;
359 }
360
361 if(name == "^¶m3") {
362 *fcontent = (float)atof(SSEPARAMS[2]);
363 return TYPE_FLOAT;
364 }
365
366 if(name == "^&playerdist") {
367 if(entity) {
368 *fcontent = fdist(player.pos, entity->pos);
369 return TYPE_FLOAT;
370 }
371 }
372
373 break;
374 }
375
376 case '#': {
377
378 if(name == "^#playerdist") {
379 if(entity) {
380 *lcontent = (long)fdist(player.pos, entity->pos);
381 return TYPE_LONG;
382 }
383 }
384
385 if(name == "^#param1") {
386 *lcontent = atol(SSEPARAMS[0]);
387 return TYPE_LONG;
388 }
389
390 if(name == "^#param2") {
391 *lcontent = atol(SSEPARAMS[1]);
392 return TYPE_LONG;
393 }
394
395 if(name == "^#param3") {
396 *lcontent = atol(SSEPARAMS[2]);
397 return TYPE_LONG;
398 }
399
400 if(name == "^#timer1") {
401 if(!entity || entity->script.timers[0] == 0) {
402 *lcontent = 0;
403 } else {
404 *lcontent = long((unsigned long)(arxtime) - es->timers[0]);
405 }
406 return TYPE_LONG;
407 }
408
409 if(name == "^#timer2") {
410 if(!entity || entity->script.timers[1] == 0) {
411 *lcontent = 0;
412 } else {
413 *lcontent = long((unsigned long)(arxtime) - es->timers[1]);
414 }
415 return TYPE_LONG;
416 }
417
418 if(name == "^#timer3") {
419 if(!entity || entity->script.timers[2] == 0) {
420 *lcontent = 0;
421 } else {
422 *lcontent = long((unsigned long)(arxtime) - es->timers[2]);
423 }
424 return TYPE_LONG;
425 }
426
427 if(name == "^#timer4") {
428 if(!entity || entity->script.timers[3] == 0) {
429 *lcontent = 0;
430 } else {
431 *lcontent = long((unsigned long)(arxtime) - es->timers[3]);
432 }
433 return TYPE_LONG;
434 }
435
436 break;
437 }
438
439 case 'g': {
440
441 if(name == "^gore") {
442 *lcontent = 1;
443 return TYPE_LONG;
444 }
445
446 if(name == "^gamedays") {
447 *lcontent = static_cast<long>(float(arxtime) / 864000000);
448 return TYPE_LONG;
449 }
450
451 if(name == "^gamehours") {
452 *lcontent = static_cast<long>(float(arxtime) / 3600000);
453 return TYPE_LONG;
454 }
455
456 if(name == "^gameminutes") {
457 *lcontent = static_cast<long>(float(arxtime) / 60000);
458 return TYPE_LONG;
459 }
460
461 if(name == "^gameseconds") {
462 *lcontent = static_cast<long>(float(arxtime) / 1000);
463 return TYPE_LONG;
464 }
465
466 break;
467 }
468
469 case 'a': {
470
471 if(boost::starts_with(name, "^amount")) {
472 if(entity && (entity->ioflags & IO_ITEM)) {
473 *fcontent = entity->_itemdata->count;
474 } else {
475 *fcontent = 0;
476 }
477 return TYPE_FLOAT;
478 }
479
480 if(name == "^arxdays") {
481 *lcontent = static_cast<long>(float(arxtime) / 7200000);
482 return TYPE_LONG;
483 }
484
485 if(name == "^arxhours") {
486 *lcontent = static_cast<long>(float(arxtime) / 600000);
487 return TYPE_LONG;
488 }
489
490 if(name == "^arxminutes") {
491 *lcontent = static_cast<long>(float(arxtime) / 10000);
492 return TYPE_LONG;
493 }
494
495 if(name == "^arxseconds") {
496 *lcontent = static_cast<long>(float(arxtime) / 1000) * 6;
497 return TYPE_LONG;
498 }
499
500 if(name == "^arxtime_hours") {
501 *lcontent = static_cast<long>(float(arxtime) / 600000);
502 while(*lcontent > 12) {
503 *lcontent -= 12;
504 }
505 return TYPE_LONG;
506 }
507
508 if(name == "^arxtime_minutes") {
509 *lcontent = static_cast<long>(float(arxtime) / 10000);
510 while(*lcontent > 60) {
511 *lcontent -= 60;
512 }
513 return TYPE_LONG;
514 }
515
516 if(name == "^arxtime_seconds") {
517 *lcontent = static_cast<long>(float(arxtime) * 6 / 1000);
518 while(*lcontent > 60) {
519 *lcontent -= 60;
520 }
521 return TYPE_LONG;
522 }
523
524 break;
525 }
526
527 case 'r': {
528
529 if(boost::starts_with(name, "^realdist_")) {
530 if(entity) {
531 const char * obj = name.c_str() + 10;
532
533 if(!strcmp(obj, "player")) {
534 if(entity->room_flags & 1) {
535 UpdateIORoom(entity);
536 }
537 long Player_Room = ARX_PORTALS_GetRoomNumForPosition(&player.pos, 1);
538 *fcontent = SP_GetRoomDist(&entity->pos, &player.pos, entity->room, Player_Room);
539 return TYPE_FLOAT;
540 }
541
542 long t = entities.getById(obj);
543 if(ValidIONum(t)) {
544 if((entity->show == SHOW_FLAG_IN_SCENE
545 || entity->show == SHOW_FLAG_IN_INVENTORY)
546 && (entities[t]->show == SHOW_FLAG_IN_SCENE
547 || entities[t]->show == SHOW_FLAG_IN_INVENTORY)) {
548
549 Vec3f pos, pos2;
550 GetItemWorldPosition(entity, &pos);
551 GetItemWorldPosition(entities[t], &pos2);
552
553 if(entity->room_flags & 1) {
554 UpdateIORoom(entity);
555 }
556
557 if(entities[t]->room_flags & 1) {
558 UpdateIORoom(entities[t]);
559 }
560
561 *fcontent = SP_GetRoomDist(&pos, &pos2, entity->room, entities[t]->room);
562
563 } else {
564 // Out of this world item
565 *fcontent = 99999999999.f;
566 }
567 return TYPE_FLOAT;
568 }
569
570 *fcontent = 99999999999.f;
571 return TYPE_FLOAT;
572 }
573 }
574
575 if(boost::starts_with(name, "^repairprice_")) {
576 long t = entities.getById(name.substr(13));
577 if(ValidIONum(t)) {
578 *fcontent = ARX_DAMAGES_ComputeRepairPrice(entities[t], entity);
579 } else {
580 *fcontent = 0;
581 }
582 return TYPE_FLOAT;
583 }
584
585 if(boost::starts_with(name, "^rnd_")) {
586 const char * max = name.c_str() + 5;
587 // TODO should max be inclusive or exclusive?
588 // if inclusive, use proper integer random, otherwise fix rnd()?
589 if(max[0]) {
590 float t = (float)atof(max);
591 *fcontent = t * rnd();
592 return TYPE_FLOAT;
593 }
594 *fcontent = 0;
595 return TYPE_FLOAT;
596 }
597
598 if(boost::starts_with(name, "^rune_")) {
599 string temp = name.substr(6);
600 *lcontent = 0;
601 if(temp == "aam") {
602 *lcontent = player.rune_flags & FLAG_AAM;
603 } else if(temp == "cetrius") {
604 *lcontent = player.rune_flags & FLAG_CETRIUS;
605 } else if(temp == "comunicatum") {
606 *lcontent = player.rune_flags & FLAG_COMUNICATUM;
607 } else if(temp == "cosum") {
608 *lcontent = player.rune_flags & FLAG_COSUM;
609 } else if(temp == "folgora") {
610 *lcontent = player.rune_flags & FLAG_FOLGORA;
611 } else if(temp == "fridd") {
612 *lcontent = player.rune_flags & FLAG_FRIDD;
613 } else if(temp == "kaom") {
614 *lcontent = player.rune_flags & FLAG_KAOM;
615 } else if(temp == "mega") {
616 *lcontent = player.rune_flags & FLAG_MEGA;
617 } else if(temp == "morte") {
618 *lcontent = player.rune_flags & FLAG_MORTE;
619 } else if(temp == "movis") {
620 *lcontent = player.rune_flags & FLAG_MOVIS;
621 } else if(temp == "nhi") {
622 *lcontent = player.rune_flags & FLAG_NHI;
623 } else if(temp == "rhaa") {
624 *lcontent = player.rune_flags & FLAG_RHAA;
625 } else if(temp == "spacium") {
626 *lcontent = player.rune_flags & FLAG_SPACIUM;
627 } else if(temp == "stregum") {
628 *lcontent = player.rune_flags & FLAG_STREGUM;
629 } else if(temp == "taar") {
630 *lcontent = player.rune_flags & FLAG_TAAR;
631 } else if(temp == "tempus") {
632 *lcontent = player.rune_flags & FLAG_TEMPUS;
633 } else if(temp == "tera") {
634 *lcontent = player.rune_flags & FLAG_TERA;
635 } else if(temp == "vista") {
636 *lcontent = player.rune_flags & FLAG_VISTA;
637 } else if(temp == "vitae") {
638 *lcontent = player.rune_flags & FLAG_VITAE;
639 } else if(temp == "yok") {
640 *lcontent = player.rune_flags & FLAG_YOK;
641 }
642 return TYPE_LONG;
643 }
644
645 break;
646 }
647
648 case 'i': {
649
650 if(boost::starts_with(name, "^inzone_")) {
651 const char * zone = name.c_str() + 8;
652 ARX_PATH * ap = ARX_PATH_GetAddressByName(zone);
653 *lcontent = 0;
654 if(entity && ap) {
655 if(ARX_PATH_IsPosInZone(ap, entity->pos.x, entity->pos.y, entity->pos.z)) {
656 *lcontent = 1;
657 }
658 }
659 return TYPE_LONG;
660 }
661
662 if(boost::starts_with(name, "^ininitpos")) {
663 Vec3f pos;
664 *lcontent = 0;
665 if(entity && GetItemWorldPosition(entity, &pos) && pos == entity->initpos) {
666 *lcontent = 1;
667 }
668 return TYPE_LONG;
669 }
670
671 if(boost::starts_with(name, "^inplayerinventory")) {
672 *lcontent = 0;
673 if(entity && (entity->ioflags & IO_ITEM) && IsInPlayerInventory(entity)) {
674 *lcontent = 1;
675 }
676 return TYPE_LONG;
677 }
678
679 break;
680 }
681
682 case 'b': {
683
684 if(boost::starts_with(name, "^behavior")) {
685 txtcontent = "";
686 if(entity && (entity->ioflags & IO_NPC)) {
687 if(entity->_npcdata->behavior & BEHAVIOUR_LOOK_AROUND) {
688 txtcontent += "l";
689 }
690 if(entity->_npcdata->behavior & BEHAVIOUR_SNEAK) {
691 txtcontent += "s";
692 }
693 if(entity->_npcdata->behavior & BEHAVIOUR_DISTANT) {
694 txtcontent += "d";
695 }
696 if(entity->_npcdata->behavior & BEHAVIOUR_MAGIC) {
697 txtcontent += "m";
698 }
699 if(entity->_npcdata->behavior & BEHAVIOUR_FIGHT) {
700 txtcontent += "f";
701 }
702 if(entity->_npcdata->behavior & BEHAVIOUR_GO_HOME) {
703 txtcontent += "h";
704 }
705 if(entity->_npcdata->behavior & BEHAVIOUR_FRIENDLY) {
706 txtcontent += "r";
707 }
708 if(entity->_npcdata->behavior & BEHAVIOUR_MOVE_TO) {
709 txtcontent += "t";
710 }
711 if(entity->_npcdata->behavior & BEHAVIOUR_FLEE) {
712 txtcontent += "e";
713 }
714 if(entity->_npcdata->behavior & BEHAVIOUR_LOOK_FOR) {
715 txtcontent += "o";
716 }
717 if(entity->_npcdata->behavior & BEHAVIOUR_HIDE) {
718 txtcontent += "i";
719 }
720 if(entity->_npcdata->behavior & BEHAVIOUR_WANDER_AROUND) {
721 txtcontent += "w";
722 }
723 if(entity->_npcdata->behavior & BEHAVIOUR_GUARD) {
724 txtcontent += "u";
725 }
726 if(entity->_npcdata->behavior & BEHAVIOUR_STARE_AT) {
727 txtcontent += "a";
728 }
729 }
730 return TYPE_TEXT;
731 }
732
733 break;
734 }
735
736 case 's': {
737
738 if(boost::starts_with(name, "^sender")) {
739 if(!EVENT_SENDER) {
740 txtcontent = "none";
741 } else if(EVENT_SENDER == entities.player()) {
742 txtcontent = "player";
743 } else {
744 txtcontent = EVENT_SENDER->long_name();
745 }
746 return TYPE_TEXT;
747 }
748
749 if(boost::starts_with(name, "^scale")) {
750 *fcontent = (entity) ? entity->scale * 100.f : 0.f;
751 return TYPE_FLOAT;
752 }
753
754 if(boost::starts_with(name, "^speaking")) {
755 if(entity) {
756 for(size_t i = 0; i < MAX_ASPEECH; i++) {
757 if(aspeech[i].exist && entity == aspeech[i].io) {
758 *lcontent = 1;
759 return TYPE_LONG;
760 }
761 }
762 }
763 *lcontent = 0;
764 return TYPE_LONG;
765 }
766
767 break;
768 }
769
770 case 'm': {
771
772 if(boost::starts_with(name, "^me")) {
773 if(!entity) {
774 txtcontent = "none";
775 } else if(entity == entities.player()) {
776 txtcontent = "player";
777 } else {
778 txtcontent = entity->long_name();
779 }
780 return TYPE_TEXT;
781 }
782
783 if(boost::starts_with(name, "^maxlife")) {
784 *fcontent = 0;
785 if(entity && (entity->ioflags & IO_NPC)) {
786 *fcontent = entity->_npcdata->maxlife;
787 }
788 return TYPE_FLOAT;
789 }
790
791 if(boost::starts_with(name, "^mana")) {
792 *fcontent = 0;
793 if(entity && (entity->ioflags & IO_NPC)) {
794 *fcontent = entity->_npcdata->mana;
795 }
796 return TYPE_FLOAT;
797 }
798
799 if(boost::starts_with(name, "^maxmana")) {
800 *fcontent = 0;
801 if(entity && (entity->ioflags & IO_NPC)) {
802 *fcontent = entity->_npcdata->maxmana;
803 }
804 return TYPE_FLOAT;
805 }
806
807 if(boost::starts_with(name, "^myspell_")) {
808 Spell id = GetSpellId(name.substr(9));
809 if(id != SPELL_NONE) {
810 for(size_t i = 0; i < MAX_SPELLS; i++) {
811 if(spells[i].exist && spells[i].type == id && spells[i].caster >= 0
812 && spells[i].caster < long(entities.size())
813 && entity == entities[spells[i].caster]) {
814 *lcontent = 1;
815 return TYPE_LONG;
816 }
817 }
818 }
819 *lcontent = 0;
820 return TYPE_LONG;
821 }
822
823 if(boost::starts_with(name, "^maxdurability")) {
824 *fcontent = (entity) ? entity->max_durability : 0.f;
825 return TYPE_FLOAT;
826 }
827
828 break;
829 }
830
831 case 'l': {
832
833 if(boost::starts_with(name, "^life")) {
834 *fcontent = 0;
835 if(entity && (entity->ioflags & IO_NPC)) {
836 *fcontent = entity->_npcdata->life;
837 }
838 return TYPE_FLOAT;
839 }
840
841 if(boost::starts_with(name, "^last_spawned")) {
842 txtcontent = (LASTSPAWNED) ? LASTSPAWNED->long_name() : "none";
843 return TYPE_TEXT;
844 }
845
846 break;
847 }
848
849 case 'd': {
850
851 if(boost::starts_with(name, "^dist_")) {
852 if(entity) {
853 const char * obj = name.c_str() + 6;
854
855 if(!strcmp(obj, "player")) {
856 *fcontent = fdist(player.pos, entity->pos);
857 return TYPE_FLOAT;
858 }
859
860 long t = entities.getById(obj);
861 if(ValidIONum(t)) {
862 if((entity->show == SHOW_FLAG_IN_SCENE
863 || entity->show == SHOW_FLAG_IN_INVENTORY)
864 && (entities[t]->show == SHOW_FLAG_IN_SCENE
865 || entities[t]->show == SHOW_FLAG_IN_INVENTORY)) {
866 Vec3f pos, pos2;
867 GetItemWorldPosition(entity, &pos);
868 GetItemWorldPosition(entities[t], &pos2);
869 *fcontent = fdist(pos, pos2);
870 return TYPE_FLOAT;
871 }
872 }
873
874 *fcontent = 99999999999.f;
875 return TYPE_FLOAT;
876 }
877 }
878
879 if(boost::starts_with(name, "^demo")) {
880 *lcontent = (resources->getReleaseType() & PakReader::Demo) ? 1 : 0;
881 return TYPE_LONG;
882 }
883
884 if(boost::starts_with(name, "^durability")) {
885 *fcontent = (entity) ? entity->durability : 0.f;
886 return TYPE_FLOAT;
887 }
888
889 break;
890 }
891
892 case 'p': {
893
894 if(boost::starts_with(name, "^price")) {
895 *fcontent = 0;
896 if(entity && (entity->ioflags & IO_ITEM)) {
897 *fcontent = static_cast<float>(entity->_itemdata->price);
898 }
899 return TYPE_FLOAT;
900 }
901
902 if(boost::starts_with(name, "^player_zone")) {
903 txtcontent = (player.inzone) ? player.inzone->name : "none";
904 return TYPE_TEXT;
905 }
906
907 if(boost::starts_with(name, "^player_life")) {
908 *fcontent = player.Full_life; // TODO why not player.life like everywhere else?
909 return TYPE_FLOAT;
910 }
911
912 if(boost::starts_with(name, "^poisoned")) {
913 *fcontent = 0;
914 if(entity && (entity->ioflags & IO_NPC)) {
915 *fcontent = entity->_npcdata->poisonned;
916 }
917 return TYPE_FLOAT;
918 }
919
920 if(boost::starts_with(name, "^poisonous")) {
921 *fcontent = (entity) ? entity->poisonous : 0.f;
922 return TYPE_FLOAT;
923 }
924
925 if(boost::starts_with(name, "^possess_")) {
926 long t = entities.getById(name.substr(9));
927 if(ValidIONum(t)) {
928 if(IsInPlayerInventory(entities[t])) {
929 *lcontent = 1;
930 return TYPE_LONG;
931 }
932 for(long i = 0; i < MAX_EQUIPED; i++) {
933 if(player.equiped[i] == t) {
934 *lcontent = 2;
935 return TYPE_LONG;
936 }
937 }
938 }
939 *lcontent = 0;
940 return TYPE_LONG;
941 }
942
943 if(boost::starts_with(name, "^player_gold")) {
944 *fcontent = static_cast<float>(player.gold);
945 return TYPE_FLOAT;
946 }
947
948 if(boost::starts_with(name, "^player_maxlife")) {
949 *fcontent = player.Full_maxlife;
950 return TYPE_FLOAT;
951 }
952
953 if(boost::starts_with(name, "^player_attribute_strength")) {
954 *fcontent = player.Full_Attribute_Strength;
955 return TYPE_FLOAT;
956 }
957
958 if(boost::starts_with(name, "^player_attribute_dexterity")) {
959 *fcontent = player.Full_Attribute_Dexterity;
960 return TYPE_FLOAT;
961 }
962
963 if(boost::starts_with(name, "^player_attribute_constitution")) {
964 *fcontent = player.Full_Attribute_Constitution;
965 return TYPE_FLOAT;
966 }
967
968 if(boost::starts_with(name, "^player_attribute_mind")) {
969 *fcontent = player.Full_Attribute_Mind;
970 return TYPE_FLOAT;
971 }
972
973 if(boost::starts_with(name, "^player_skill_stealth")) {
974 *fcontent = player.Full_Skill_Stealth;
975 return TYPE_FLOAT;
976 }
977
978 if(boost::starts_with(name, "^player_skill_mecanism")) {
979 *fcontent = player.Full_Skill_Mecanism;
980 return TYPE_FLOAT;
981 }
982
983 if(boost::starts_with(name, "^player_skill_intuition")) {
984 *fcontent = player.Full_Skill_Intuition;
985 return TYPE_FLOAT;
986 }
987
988 if(boost::starts_with(name, "^player_skill_etheral_link")) {
989 *fcontent = player.Full_Skill_Etheral_Link;
990 return TYPE_FLOAT;
991 }
992
993 if(boost::starts_with(name, "^player_skill_object_knowledge")) {
994 *fcontent = player.Full_Skill_Object_Knowledge;
995 return TYPE_FLOAT;
996 }
997
998 if(boost::starts_with(name, "^player_skill_casting")) {
999 *fcontent = player.Full_Skill_Casting;
1000 return TYPE_FLOAT;
1001 }
1002
1003 if(boost::starts_with(name, "^player_skill_projectile")) {
1004 *fcontent = player.Full_Skill_Projectile;
1005 return TYPE_FLOAT;
1006 }
1007
1008 if(boost::starts_with(name, "^player_skill_close_combat")) {
1009 *fcontent = player.Full_Skill_Close_Combat;
1010 return TYPE_FLOAT;
1011 }
1012
1013 if(boost::starts_with(name, "^player_skill_defense")) {
1014 *fcontent = player.Full_Skill_Defense;
1015 return TYPE_FLOAT;
1016 }
1017
1018 if(boost::starts_with(name, "^player_hunger")) {
1019 *fcontent = player.hunger;
1020 return TYPE_FLOAT;
1021 }
1022
1023 if(boost::starts_with(name, "^player_poison")) {
1024 *fcontent = player.poison;
1025 return TYPE_FLOAT;
1026 }
1027
1028 if(boost::starts_with(name, "^playercasting")) {
1029 for(size_t i = 0; i < MAX_SPELLS; i++) {
1030 if(spells[i].exist && spells[i].caster == 0) {
1031 if(spells[i].type == SPELL_LIFE_DRAIN
1032 || spells[i].type == SPELL_HARM
1033 || spells[i].type == SPELL_FIRE_FIELD
1034 || spells[i].type == SPELL_ICE_FIELD
1035 || spells[i].type == SPELL_LIGHTNING_STRIKE
1036 || spells[i].type == SPELL_MASS_LIGHTNING_STRIKE) {
1037 *lcontent = 1;
1038 return TYPE_LONG;
1039 }
1040 }
1041 }
1042 *lcontent = 0;
1043 return TYPE_LONG;
1044 }
1045
1046 if(boost::starts_with(name, "^playerspell_")) {
1047 string temp = name.substr(13);
1048
1049 Spell id = GetSpellId(temp);
1050 if(id != SPELL_NONE) {
1051 for(size_t i = 0; i < MAX_SPELLS; i++) {
1052 if(spells[i].exist && spells[i].type == id && spells[i].caster == 0) {
1053 *lcontent = 1;
1054 return TYPE_LONG;
1055 }
1056 }
1057 }
1058
1059 if(temp == "invisibility" && entities.player()->invisibility > 0.3f) {
1060 *lcontent = 1;
1061 return TYPE_LONG;
1062 }
1063
1064 *lcontent = 0;
1065 return TYPE_LONG;
1066 }
1067
1068 break;
1069 }
1070
1071 case 'n': {
1072
1073 if(boost::starts_with(name, "^npcinsight")) {
1074 Entity * ioo = ARX_NPC_GetFirstNPCInSight(entity);
1075 if(!ioo) {
1076 txtcontent = "none";
1077 } else if(ioo == entities.player()) {
1078 txtcontent = "player";
1079 } else {
1080 txtcontent = ioo->long_name();
1081 }
1082 return TYPE_TEXT;
1083 }
1084
1085 break;
1086 }
1087
1088 case 't': {
1089
1090 if(boost::starts_with(name, "^target")) {
1091 if(!entity) {
1092 txtcontent = "none";
1093 } else if(entity->targetinfo == 0) {
1094 txtcontent = "player";
1095 } else if(!ValidIONum(entity->targetinfo)) {
1096 txtcontent = "none";
1097 } else {
1098 txtcontent = entities[entity->targetinfo]->long_name();
1099 }
1100 return TYPE_TEXT;
1101 }
1102
1103 break;
1104 }
1105
1106 case 'f': {
1107
1108 if(boost::starts_with(name, "^focal")) {
1109 if(entity && (entity->ioflags & IO_CAMERA)) {
1110 *fcontent = entity->_camdata->cam.focal;
1111 return TYPE_FLOAT;
1112 }
1113 }
1114
1115 if(boost::starts_with(name, "^fighting")) {
1116 *lcontent = long(ARX_PLAYER_IsInFightMode());
1117 return TYPE_LONG;
1118 }
1119
1120 break;
1121 }
1122
1123 }
1124
1125 *lcontent = 0;
1126 return TYPE_LONG;
1127 }
1128
ARX_SCRIPT_Free_All_Global_Variables()1129 void ARX_SCRIPT_Free_All_Global_Variables() {
1130
1131 if(svar) {
1132 for(long i = 0; i < NB_GLOBALS; i++) {
1133 free(svar[i].text);
1134 }
1135 free(svar), svar = NULL, NB_GLOBALS = 0;
1136 }
1137
1138 }
1139
CloneLocalVars(Entity * ioo,Entity * io)1140 void CloneLocalVars(Entity * ioo, Entity * io) {
1141
1142 if(!ioo || !io) {
1143 return;
1144 }
1145
1146 if(ioo->script.lvar) {
1147 for(long n = 0; n < ioo->script.nblvar; n++) {
1148 free(ioo->script.lvar[n].text);
1149 }
1150 free(ioo->script.lvar), ioo->script.lvar = NULL, ioo->script.nblvar = 0;
1151 }
1152
1153 if (io->script.lvar)
1154 {
1155 ioo->script.nblvar = io->script.nblvar;
1156 ioo->script.lvar = (SCRIPT_VAR *)malloc(sizeof(SCRIPT_VAR) * io->script.nblvar);
1157
1158 for (long n = 0; n < io->script.nblvar; n++)
1159 {
1160 memcpy(&ioo->script.lvar[n], &io->script.lvar[n], sizeof(SCRIPT_VAR));
1161
1162 if (io->script.lvar[n].text)
1163 {
1164 ioo->script.lvar[n].text = (char *)malloc(strlen(io->script.lvar[n].text) + 1);
1165 strcpy(ioo->script.lvar[n].text, io->script.lvar[n].text);
1166 }
1167 }
1168 }
1169 }
1170
GetFreeVarSlot(SCRIPT_VAR * & _svff,long & _nb)1171 SCRIPT_VAR * GetFreeVarSlot(SCRIPT_VAR*& _svff, long& _nb)
1172 {
1173
1174 SCRIPT_VAR * svf = _svff;
1175 _svff = (SCRIPT_VAR *) realloc(svf, sizeof(SCRIPT_VAR) * ((_nb) + 1));
1176 svf = _svff;
1177 memset(&svf[_nb], 0, sizeof(SCRIPT_VAR));
1178 _nb++;
1179 return &svf[_nb-1];
1180 }
1181
GetVarAddress(SCRIPT_VAR svf[],size_t nb,const string & name)1182 SCRIPT_VAR * GetVarAddress(SCRIPT_VAR svf[], size_t nb, const string & name) {
1183
1184 for(size_t i = 0; i < nb; i++) {
1185 if(svf[i].type != TYPE_UNKNOWN) {
1186 if(name == svf[i].name) {
1187 return &svf[i];
1188 }
1189 }
1190 }
1191
1192 return NULL;
1193 }
1194
GETVarValueLong(SCRIPT_VAR svf[],size_t nb,const string & name)1195 long GETVarValueLong(SCRIPT_VAR svf[], size_t nb, const string & name) {
1196
1197 const SCRIPT_VAR * tsv = GetVarAddress(svf, nb, name);
1198
1199 if (tsv == NULL) return 0;
1200
1201 return tsv->ival;
1202 }
1203
GETVarValueFloat(SCRIPT_VAR svf[],size_t nb,const string & name)1204 float GETVarValueFloat(SCRIPT_VAR svf[], size_t nb, const string & name) {
1205
1206 const SCRIPT_VAR * tsv = GetVarAddress(svf, nb, name);
1207
1208 if (tsv == NULL) return 0;
1209
1210 return tsv->fval;
1211 }
1212
GETVarValueText(SCRIPT_VAR svf[],size_t nb,const string & name)1213 std::string GETVarValueText(SCRIPT_VAR svf[], size_t nb, const string & name) {
1214
1215 const SCRIPT_VAR* tsv = GetVarAddress(svf, nb, name);
1216
1217 if (!tsv) return "";
1218
1219 return tsv->text;
1220 }
1221
GetVarValueInterpretedAsText(const string & temp1,const EERIE_SCRIPT * esss,Entity * io)1222 string GetVarValueInterpretedAsText(const string & temp1, const EERIE_SCRIPT * esss, Entity * io) {
1223
1224 char var_text[256];
1225 float t1;
1226
1227 if(!temp1.empty())
1228 {
1229 if (temp1[0] == '^')
1230 {
1231 long lv;
1232 float fv;
1233 std::string tv;
1234
1235 switch (getSystemVar(esss,io,temp1,tv,&fv,&lv))//Arx: xrichter (2010-08-04) - fix a crash when $OBJONTOP return to many object name inside tv
1236 {
1237 case TYPE_TEXT:
1238 return tv;
1239 break;
1240 case TYPE_LONG:
1241 sprintf(var_text, "%ld", lv);
1242 return var_text;
1243 break;
1244 default:
1245 sprintf(var_text, "%f", fv);
1246 return var_text;
1247 break;
1248 }
1249
1250 }
1251 else if (temp1[0] == '#')
1252 {
1253 long l1 = GETVarValueLong(svar, NB_GLOBALS, temp1);
1254 sprintf(var_text, "%ld", l1);
1255 return var_text;
1256 }
1257 else if (temp1[0] == '\xA7')
1258 {
1259 long l1 = GETVarValueLong(esss->lvar, esss->nblvar, temp1);
1260 sprintf(var_text, "%ld", l1);
1261 return var_text;
1262 }
1263 else if (temp1[0] == '&') t1 = GETVarValueFloat(svar, NB_GLOBALS, temp1);
1264 else if (temp1[0] == '@') t1 = GETVarValueFloat(esss->lvar, esss->nblvar, temp1);
1265 else if (temp1[0] == '$')
1266 {
1267 SCRIPT_VAR * var = GetVarAddress(svar, NB_GLOBALS, temp1);
1268
1269 if (!var) return "void";
1270 else return var->text;
1271 }
1272 else if (temp1[0] == '\xA3')
1273 {
1274 SCRIPT_VAR * var = GetVarAddress(esss->lvar, esss->nblvar, temp1);
1275
1276 if (!var) return "void";
1277 else return var->text;
1278 }
1279 else
1280 {
1281 return temp1;
1282 }
1283 }
1284 else
1285 {
1286 return "";
1287 }
1288
1289 sprintf(var_text, "%f", t1);
1290 return var_text;
1291 }
1292
GetVarValueInterpretedAsFloat(const string & temp1,const EERIE_SCRIPT * esss,Entity * io)1293 float GetVarValueInterpretedAsFloat(const string & temp1, const EERIE_SCRIPT * esss, Entity * io) {
1294
1295 if(temp1[0] == '^') {
1296 long lv;
1297 float fv;
1298 std::string tv;
1299 switch (getSystemVar(esss,io,temp1,tv,&fv,&lv)) {
1300 case TYPE_TEXT:
1301 return (float)atof(tv.c_str());
1302 case TYPE_LONG:
1303 return (float)lv;
1304 // TODO unreachable code (should it be case TYPE_FLOAT: ?)
1305 //return (fv);
1306 default:
1307 break;
1308 }
1309 } else if(temp1[0] == '#') {
1310 return (float)GETVarValueLong(svar, NB_GLOBALS, temp1);
1311 } else if(temp1[0] == '\xA7') {
1312 return (float)GETVarValueLong(esss->lvar, esss->nblvar, temp1);
1313 } else if(temp1[0] == '&') {
1314 return GETVarValueFloat(svar, NB_GLOBALS, temp1);
1315 } else if(temp1[0] == '@') {
1316 return GETVarValueFloat(esss->lvar, esss->nblvar, temp1);
1317 }
1318
1319 return (float)atof(temp1.c_str());
1320 }
1321
SETVarValueLong(SCRIPT_VAR * & svf,long & nb,const std::string & name,long val)1322 SCRIPT_VAR* SETVarValueLong(SCRIPT_VAR*& svf, long& nb, const std::string& name, long val)
1323 {
1324 SCRIPT_VAR* tsv = GetVarAddress(svf, nb, name);
1325
1326 if (!tsv)
1327 {
1328 tsv = GetFreeVarSlot(svf, nb);
1329
1330 if (!tsv)
1331 return NULL;
1332
1333 strcpy(tsv->name, name.c_str());
1334 }
1335
1336 tsv->ival = val;
1337 return tsv;
1338 }
1339
SETVarValueFloat(SCRIPT_VAR * & svf,long & nb,const std::string & name,float val)1340 SCRIPT_VAR* SETVarValueFloat(SCRIPT_VAR*& svf, long& nb, const std::string& name, float val)
1341 {
1342 SCRIPT_VAR* tsv = GetVarAddress(svf, nb, name);
1343
1344 if (!tsv)
1345 {
1346 tsv = GetFreeVarSlot(svf, nb);
1347
1348 if (!tsv)
1349 return NULL;
1350
1351 strcpy(tsv->name, name.c_str());
1352 }
1353
1354 tsv->fval = val;
1355 return tsv;
1356 }
1357
SETVarValueText(SCRIPT_VAR * & svf,long & nb,const std::string & name,const std::string & val)1358 SCRIPT_VAR* SETVarValueText(SCRIPT_VAR*& svf, long& nb, const std::string& name, const std::string& val)
1359 {
1360 SCRIPT_VAR* tsv = GetVarAddress(svf, nb, name);
1361
1362 if (!tsv)
1363 {
1364 tsv = GetFreeVarSlot(svf, nb);
1365
1366 if (!tsv)
1367 return NULL;
1368
1369 strcpy(tsv->name, name.c_str());
1370 }
1371
1372
1373 tsv->ival = val.length() + 1;
1374
1375 free(tsv->text);
1376 tsv->text = (tsv->ival) ? strdup(val.c_str()) : NULL;
1377
1378 return tsv;
1379 }
1380
1381
1382
1383
1384
MakeGlobalText(std::string & tx)1385 void MakeGlobalText(std::string & tx)
1386 {
1387 char texx[256];
1388
1389 for(long i = 0; i < NB_GLOBALS; i++) {
1390 switch(svar[i].type) {
1391 case TYPE_G_TEXT:
1392 tx += svar[i].name;
1393 tx += " = ";
1394 tx += svar[i].text;
1395 tx += "\r\n";
1396 break;
1397 case TYPE_G_LONG:
1398 tx += svar[i].name;
1399 tx += " = ";
1400 sprintf(texx, "%ld", svar[i].ival);
1401 tx += texx;
1402 tx += "\r\n";
1403 break;
1404 case TYPE_G_FLOAT:
1405 tx += svar[i].name;
1406 tx += " = ";
1407 sprintf(texx, "%f", svar[i].fval);
1408 tx += texx;
1409 tx += "\r\n";
1410 break;
1411 case TYPE_UNKNOWN:
1412 case TYPE_L_TEXT:
1413 case TYPE_L_LONG:
1414 case TYPE_L_FLOAT:
1415 break;
1416 }
1417 }
1418 }
1419
MakeLocalText(EERIE_SCRIPT * es,std::string & tx)1420 void MakeLocalText(EERIE_SCRIPT * es, std::string& tx)
1421 {
1422 char texx[256];
1423
1424 if (es->master != NULL) es = es->master;
1425
1426 if (es->lvar == NULL) return;
1427
1428 for (long i = 0; i < es->nblvar; i++)
1429 {
1430 switch (es->lvar[i].type)
1431 {
1432 case TYPE_L_TEXT:
1433 tx += es->lvar[i].name;
1434 tx += " = ";
1435 tx += es->lvar[i].text;
1436 tx += "\r\n";
1437 break;
1438 case TYPE_L_LONG:
1439 tx += es->lvar[i].name;
1440 tx += " = ";
1441 sprintf(texx, "%ld", es->lvar[i].ival);
1442 tx += texx;
1443 tx += "\r\n";
1444 break;
1445 case TYPE_L_FLOAT:
1446 tx += es->lvar[i].name;
1447 tx += " = ";
1448 sprintf(texx, "%f", es->lvar[i].fval);
1449 tx += texx;
1450 tx += "\r\n";
1451 break;
1452 case TYPE_UNKNOWN:
1453 case TYPE_G_TEXT:
1454 case TYPE_G_LONG:
1455 case TYPE_G_FLOAT:
1456 break;
1457 }
1458 }
1459 }
1460
1461 //*************************************************************************************
1462 // ScriptEvent::send //
1463 // Sends a event to a script. //
1464 // returns ACCEPT to accept default EVENT processing //
1465 // returns REFUSE to refuse default EVENT processing //
1466 //*************************************************************************************
MakeSSEPARAMS(const char * params)1467 void MakeSSEPARAMS(const char * params)
1468 {
1469
1470 for (long i = 0; i < MAX_SSEPARAMS; i++)
1471 {
1472 SSEPARAMS[i][0] = 0;
1473 }
1474
1475 if(params == NULL) {
1476 return;
1477 }
1478
1479 long pos = 0;
1480
1481 while(*params != '\0' && pos < MAX_SSEPARAMS) {
1482
1483 size_t tokensize = 0;
1484 while(params[tokensize] != ' ' && params[tokensize] != '\0') {
1485 tokensize++;
1486 }
1487
1488 arx_assert(tokensize < 64 - 1);
1489 memcpy(SSEPARAMS[pos], params, tokensize);
1490 SSEPARAMS[pos][tokensize] = 0;
1491
1492 params += tokensize;
1493
1494 if(*params != '\0') {
1495 params++;
1496 }
1497
1498 pos++;
1499 }
1500 }
1501
1502 #define MAX_EVENT_STACK 800
1503 struct STACKED_EVENT {
1504 Entity * sender;
1505 long exist;
1506 Entity * io;
1507 ScriptMessage msg;
1508 std::string params;
1509 std::string eventname;
1510 };
1511
1512 STACKED_EVENT eventstack[MAX_EVENT_STACK];
1513
ARX_SCRIPT_EventStackInit()1514 void ARX_SCRIPT_EventStackInit()
1515 {
1516 ARX_SCRIPT_EventStackClear( false ); // Clear everything in the stack
1517 }
ARX_SCRIPT_EventStackClear(bool check_exist)1518 void ARX_SCRIPT_EventStackClear( bool check_exist )
1519 {
1520 LogDebug("Event Stack Clear");
1521 for (long i = 0; i < MAX_EVENT_STACK; i++)
1522 {
1523 if ( check_exist ) // If we're not blatantly clearing everything
1524 if ( !eventstack[i].exist ) // If the Stacked_Event is not being used
1525 continue; // Continue on to the next one
1526
1527 // Otherwise, clear all the fields in this stacked_event
1528 eventstack[i].sender = NULL;
1529 eventstack[i].exist = 0;
1530 eventstack[i].io = NULL;
1531 eventstack[i].msg = SM_NULL;
1532 eventstack[i].params.clear();
1533 eventstack[i].eventname.clear();
1534 }
1535 }
1536
1537 long STACK_FLOW = 8;
1538
ARX_SCRIPT_EventStackClearForIo(Entity * io)1539 void ARX_SCRIPT_EventStackClearForIo(Entity * io)
1540 {
1541 for (long i = 0; i < MAX_EVENT_STACK; i++)
1542 {
1543 if (eventstack[i].exist)
1544 {
1545 if (eventstack[i].io == io)
1546 {
1547 eventstack[i].sender = NULL;
1548 eventstack[i].exist = 0;
1549 eventstack[i].io = NULL;
1550 eventstack[i].msg = SM_NULL;
1551 eventstack[i].params.clear();
1552 eventstack[i].eventname.clear();
1553 }
1554 }
1555 }
1556 }
1557
ARX_SCRIPT_EventStackExecute()1558 void ARX_SCRIPT_EventStackExecute()
1559 {
1560 long count = 0;
1561
1562 for (long i = 0; i < MAX_EVENT_STACK; i++)
1563 {
1564 if (eventstack[i].exist)
1565 {
1566 if (!ValidIOAddress(eventstack[i].io))
1567 goto kill;
1568
1569 if (ValidIOAddress(eventstack[i].sender))
1570 EVENT_SENDER = eventstack[i].sender;
1571 else
1572 EVENT_SENDER = NULL;
1573
1574 SendIOScriptEvent(eventstack[i].io, eventstack[i].msg, eventstack[i].params, eventstack[i].eventname);
1575 kill:
1576 ;
1577
1578 eventstack[i].sender = NULL;
1579 eventstack[i].exist = 0;
1580 eventstack[i].io = NULL;
1581 eventstack[i].msg = SM_NULL;
1582 eventstack[i].params.clear();
1583 eventstack[i].eventname.clear();
1584 count++;
1585
1586 if (count >= STACK_FLOW) return;
1587 }
1588 }
1589 }
1590
ARX_SCRIPT_EventStackExecuteAll()1591 void ARX_SCRIPT_EventStackExecuteAll()
1592 {
1593 STACK_FLOW = 9999999;
1594 ARX_SCRIPT_EventStackExecute();
1595 STACK_FLOW = 20;
1596 }
1597
Stack_SendIOScriptEvent(Entity * io,ScriptMessage msg,const std::string & params,const std::string & eventname)1598 void Stack_SendIOScriptEvent(Entity * io, ScriptMessage msg, const std::string& params, const std::string& eventname)
1599 {
1600 for (long i = 0; i < MAX_EVENT_STACK; i++)
1601 {
1602 if (!eventstack[i].exist)
1603 {
1604 eventstack[i].sender = EVENT_SENDER;
1605 eventstack[i].io = io;
1606 eventstack[i].msg = msg;
1607 eventstack[i].exist = 1;
1608 eventstack[i].params = params;
1609 eventstack[i].eventname = eventname;
1610
1611 return;
1612 }
1613 }
1614 }
1615
SendIOScriptEventReverse(Entity * io,ScriptMessage msg,const std::string & params,const std::string & eventname)1616 ScriptResult SendIOScriptEventReverse(Entity * io, ScriptMessage msg, const std::string& params, const std::string& eventname)
1617 {
1618 // checks invalid IO
1619 if (!io) return REFUSE;
1620
1621 long num = io->index();
1622
1623 // if this IO only has a Local script, send event to it
1624 if (entities[num] && !entities[num]->over_script.data)
1625 {
1626 return ScriptEvent::send(&entities[num]->script, msg, params, entities[num], eventname);
1627 }
1628
1629 // If this IO has a Global script send to Local (if exists)
1630 // then to local if no overriden by Local
1631 if (entities[num] && (ScriptEvent::send(&entities[num]->script, msg, params, entities[num], eventname) != REFUSE))
1632 {
1633
1634 if (entities[num])
1635 return (ScriptEvent::send(&entities[num]->over_script, msg, params, entities[num], eventname));
1636 else
1637 return REFUSE;
1638 }
1639
1640 // Refused further processing.
1641 return REFUSE;
1642 }
1643
SendIOScriptEvent(Entity * io,ScriptMessage msg,const std::string & params,const std::string & eventname)1644 ScriptResult SendIOScriptEvent(Entity * io, ScriptMessage msg, const std::string& params, const std::string& eventname)
1645 {
1646
1647 if(!io) {
1648 return REFUSE;
1649 }
1650
1651 long num = io->index();
1652
1653 Entity * oes = EVENT_SENDER;
1654
1655 if ((msg == SM_INIT) || (msg == SM_INITEND))
1656 {
1657 if (entities[num])
1658 {
1659 SendIOScriptEventReverse(entities[num], msg, params, eventname);
1660 EVENT_SENDER = oes;
1661 }
1662 }
1663
1664 // if this IO only has a Local script, send event to it
1665 if (entities[num] && !entities[num]->over_script.data)
1666 {
1667 ScriptResult ret = ScriptEvent::send(&entities[num]->script, msg, params, entities[num], eventname);
1668 EVENT_SENDER = oes;
1669 return ret;
1670 }
1671
1672 // If this IO has a Global script send to Local (if exists)
1673 // then to Global if no overriden by Local
1674 if (entities[num] && ScriptEvent::send(&entities[num]->over_script, msg, params, entities[num], eventname) != REFUSE) {
1675 EVENT_SENDER = oes;
1676
1677 if (entities[num])
1678 {
1679 ScriptResult ret = ScriptEvent::send(&entities[num]->script, msg, params, entities[num], eventname);
1680 EVENT_SENDER = oes;
1681 return ret;
1682 }
1683 else
1684 return REFUSE;
1685 }
1686
1687 // Refused further processing.
1688 return REFUSE;
1689 }
1690
SendInitScriptEvent(Entity * io)1691 ScriptResult SendInitScriptEvent(Entity * io) {
1692
1693 if (!io) return REFUSE;
1694
1695 Entity * oes = EVENT_SENDER;
1696 EVENT_SENDER = NULL;
1697 long num = io->index();
1698
1699 if (entities[num] && entities[num]->script.data)
1700 {
1701 ScriptEvent::send(&entities[num]->script, SM_INIT, "", entities[num], "");
1702 }
1703
1704 if (entities[num] && entities[num]->over_script.data)
1705 {
1706 ScriptEvent::send(&entities[num]->over_script, SM_INIT, "", entities[num], "");
1707 }
1708
1709 if (entities[num] && entities[num]->script.data)
1710 {
1711 ScriptEvent::send(&entities[num]->script, SM_INITEND, "", entities[num], "");
1712 }
1713
1714 if (entities[num] && entities[num]->over_script.data)
1715 {
1716 ScriptEvent::send(&entities[num]->over_script, SM_INITEND, "", entities[num], "");
1717 }
1718
1719 EVENT_SENDER = oes;
1720 return ACCEPT;
1721 }
1722
1723 //! Checks if timer named texx exists.
ARX_SCRIPT_Timer_Exist(const std::string & texx)1724 static bool ARX_SCRIPT_Timer_Exist(const std::string & texx) {
1725
1726 for(long i = 0; i < MAX_TIMER_SCRIPT; i++) {
1727 if(scr_timer[i].exist) {
1728 if(scr_timer[i].name == texx) {
1729 return true;
1730 }
1731 }
1732 }
1733
1734 return false;
1735 }
1736
ARX_SCRIPT_Timer_GetDefaultName()1737 string ARX_SCRIPT_Timer_GetDefaultName() {
1738
1739 for(size_t i = 1; ; i++) {
1740
1741 std::ostringstream oss;
1742 oss << "timer_" << i;
1743
1744 if(!ARX_SCRIPT_Timer_Exist(oss.str())) {
1745 return oss.str();
1746 }
1747 }
1748 }
1749
1750 //*************************************************************************************
1751 // Get a free script timer
1752 //*************************************************************************************
ARX_SCRIPT_Timer_GetFree()1753 long ARX_SCRIPT_Timer_GetFree() {
1754
1755 for(long i = 0; i < MAX_TIMER_SCRIPT; i++) {
1756 if(!(scr_timer[i].exist))
1757 return i;
1758 }
1759
1760 return -1;
1761 }
1762
1763 //*************************************************************************************
1764 // Count the number of active script timers...
1765 //*************************************************************************************
ARX_SCRIPT_CountTimers()1766 long ARX_SCRIPT_CountTimers() {
1767 return ActiveTimers;
1768 }
1769
1770 //*************************************************************************************
1771 // ARX_SCRIPT_Timer_ClearByNum
1772 // Clears a timer by its Index (long timer_idx) on the timers list
1773 //*************************************************************************************
ARX_SCRIPT_Timer_ClearByNum(long timer_idx)1774 void ARX_SCRIPT_Timer_ClearByNum(long timer_idx) {
1775 if(scr_timer[timer_idx].exist) {
1776 scr_timer[timer_idx].name.clear();
1777 ActiveTimers--;
1778 scr_timer[timer_idx].exist = 0;
1779 }
1780 }
1781
ARX_SCRIPT_Timer_Clear_By_Name_And_IO(const string & timername,Entity * io)1782 void ARX_SCRIPT_Timer_Clear_By_Name_And_IO(const string & timername, Entity * io) {
1783 for(long i = 0; i < MAX_TIMER_SCRIPT; i++) {
1784 if(scr_timer[i].exist && scr_timer[i].io == io && scr_timer[i].name == timername) {
1785 ARX_SCRIPT_Timer_ClearByNum(i);
1786 }
1787 }
1788 }
1789
ARX_SCRIPT_Timer_Clear_All_Locals_For_IO(Entity * io)1790 void ARX_SCRIPT_Timer_Clear_All_Locals_For_IO(Entity * io)
1791 {
1792 for (long i = 0; i < MAX_TIMER_SCRIPT; i++)
1793 {
1794 if (scr_timer[i].exist)
1795 {
1796 if ((scr_timer[i].io == io) && (scr_timer[i].es == &io->over_script))
1797 ARX_SCRIPT_Timer_ClearByNum(i);
1798 }
1799 }
1800 }
1801
ARX_SCRIPT_Timer_Clear_By_IO(Entity * io)1802 void ARX_SCRIPT_Timer_Clear_By_IO(Entity * io)
1803 {
1804 for (long i = 0; i < MAX_TIMER_SCRIPT; i++)
1805 {
1806 if (scr_timer[i].exist)
1807 {
1808 if (scr_timer[i].io == io)
1809 ARX_SCRIPT_Timer_ClearByNum(i);
1810 }
1811 }
1812 }
1813
1814 //*************************************************************************************
1815 // Initialise the timer list for the first time.
1816 //*************************************************************************************
1817 long MAX_TIMER_SCRIPT = 0;
ARX_SCRIPT_Timer_FirstInit(long number)1818 void ARX_SCRIPT_Timer_FirstInit(long number)
1819 {
1820 if (number < 100) number = 100;
1821
1822 MAX_TIMER_SCRIPT = number;
1823
1824 delete[] scr_timer;
1825 scr_timer = new SCR_TIMER[MAX_TIMER_SCRIPT];
1826 ActiveTimers = 0;
1827 }
1828
ARX_SCRIPT_Timer_ClearAll()1829 void ARX_SCRIPT_Timer_ClearAll()
1830 {
1831 if (ActiveTimers)
1832 for (long i = 0; i < MAX_TIMER_SCRIPT; i++)
1833 ARX_SCRIPT_Timer_ClearByNum(i);
1834
1835 ActiveTimers = 0;
1836 }
1837
ARX_SCRIPT_Timer_Clear_For_IO(Entity * io)1838 void ARX_SCRIPT_Timer_Clear_For_IO(Entity * io)
1839 {
1840 for (long i = 0; i < MAX_TIMER_SCRIPT; i++)
1841 {
1842 if (scr_timer[i].exist)
1843 {
1844 if (scr_timer[i].io == io) ARX_SCRIPT_Timer_ClearByNum(i);
1845 }
1846 }
1847 }
1848
ARX_SCRIPT_GetSystemIOScript(Entity * io,const std::string & name)1849 long ARX_SCRIPT_GetSystemIOScript(Entity * io, const std::string & name) {
1850
1851 if(ActiveTimers) {
1852 for(long i = 0; i < MAX_TIMER_SCRIPT; i++) {
1853 if(scr_timer[i].exist && scr_timer[i].io == io && scr_timer[i].name == name) {
1854 return i;
1855 }
1856 }
1857 }
1858
1859 return -1;
1860 }
1861
Manage_Specific_RAT_Timer(SCR_TIMER * st)1862 long Manage_Specific_RAT_Timer(SCR_TIMER * st)
1863 {
1864 Entity * io = st->io;
1865 GetTargetPos(io);
1866 Vec3f target = io->target - io->pos;
1867 fnormalize(target);
1868 Vec3f targ;
1869 Vector_RotateY(&targ, &target, rnd() * 60.f - 30.f);
1870 target = io->target + targ * 100.f;
1871
1872 if (ARX_INTERACTIVE_ConvertToValidPosForIO(io, &target))
1873 {
1874 ARX_INTERACTIVE_Teleport(io, &target);
1875 Vec3f pos;
1876 pos.x = io->pos.x;
1877 pos.y = io->pos.y + io->physics.cyl.height * ( 1.0f / 2 );
1878 pos.z = io->pos.z;
1879 ARX_PARTICLES_Add_Smoke(&pos, 3, 20);
1880 AddRandomSmoke(io, 20);
1881 MakeCoolFx(&io->pos);
1882 io->show = SHOW_FLAG_IN_SCENE;
1883
1884 for (long kl = 0; kl < 10; kl++)
1885 {
1886 FaceTarget2(io);
1887 }
1888
1889 io->gameFlags &= ~GFLAG_INVISIBILITY;
1890 st->times = 1;
1891 }
1892 else
1893 {
1894 st->times++;
1895
1896 st->msecs = static_cast<long>(st->msecs * ( 1.0f / 2 ));
1897
1898
1899 if (st->msecs < 100) st->msecs = 100;
1900
1901 return 1;
1902 }
1903
1904 return 0;
1905 }
1906
ARX_SCRIPT_Timer_Check()1907 void ARX_SCRIPT_Timer_Check() {
1908
1909 if(!ActiveTimers) {
1910 return;
1911 }
1912
1913 for(long i = 0; i < MAX_TIMER_SCRIPT; i++) {
1914
1915 SCR_TIMER * st = &scr_timer[i];
1916 if(!st->exist) {
1917 continue;
1918 }
1919
1920 unsigned long now = static_cast<unsigned long>(arxtime);
1921 unsigned long fire_time = st->tim + st->msecs;
1922 if(fire_time > now) {
1923 // Timer not ready to fire yet
1924 continue;
1925 }
1926
1927 // Skip heartbeat timer events for far away objects
1928 if((st->flags & 1) && !(st->io->gameFlags & GFLAG_ISINTREATZONE)) {
1929 long increment = (now - st->tim) / st->msecs;
1930 st->tim += st->msecs * increment;
1931 arx_assert_msg(st->tim <= now && st->tim + st->msecs > now,
1932 "start=%lu wait=%ld now=%lu", st->tim, st->msecs, now);
1933 continue;
1934 }
1935
1936 EERIE_SCRIPT * es = st->es;
1937 Entity * io = st->io;
1938 long pos = st->pos;
1939
1940 if(!es && st->name == "_r_a_t_") {
1941 if(Manage_Specific_RAT_Timer(st)) {
1942 continue;
1943 }
1944 }
1945
1946 if(st->times == 1) {
1947 ARX_SCRIPT_Timer_ClearByNum(i);
1948 } else {
1949 if(st->times != 0) {
1950 st->times--;
1951 }
1952 st->tim += st->msecs;
1953 }
1954
1955 if(es && ValidIOAddress(io)) {
1956 ScriptEvent::send(es, SM_EXECUTELINE, "", io, "", pos);
1957 }
1958
1959 }
1960 }
1961
ARX_SCRIPT_Init_Event_Stats()1962 void ARX_SCRIPT_Init_Event_Stats() {
1963
1964 ScriptEvent::totalCount = 0;
1965
1966 for(size_t i = 0; i < entities.size(); i++) {
1967 if(entities[i]) {
1968 entities[i]->stat_count = 0;
1969 entities[i]->stat_sent = 0;
1970 }
1971 }
1972 }
1973
ARX_SCRIPT_Get_IO_Max_Events()1974 Entity * ARX_SCRIPT_Get_IO_Max_Events() {
1975
1976 long max = -1;
1977 long ionum = -1;
1978 for(size_t i = 0; i < entities.size(); i++) {
1979 if(entities[i] && entities[i]->stat_count > max) {
1980 ionum = i;
1981 max = entities[i]->stat_count;
1982 }
1983 }
1984
1985 if(max <= 0) {
1986 return NULL;
1987 }
1988
1989 if(ionum > -1) {
1990 return entities[ionum];
1991 }
1992
1993 return NULL;
1994 }
1995
ARX_SCRIPT_Get_IO_Max_Events_Sent()1996 Entity * ARX_SCRIPT_Get_IO_Max_Events_Sent() {
1997
1998 long max = -1;
1999 long ionum = -1;
2000 for(size_t i = 0; i < entities.size(); i++) {
2001 if(entities[i] && entities[i]->stat_sent > max) {
2002 ionum = i;
2003 max = entities[i]->stat_sent;
2004 }
2005 }
2006
2007 if(max <= 0) {
2008 return NULL;
2009 }
2010
2011 if(ionum > -1) {
2012 return entities[ionum];
2013 }
2014
2015 return NULL;
2016 }
2017
ManageCasseDArme(Entity * io)2018 void ManageCasseDArme(Entity * io)
2019 {
2020 if((io->type_flags & OBJECT_TYPE_DAGGER) ||
2021 (io->type_flags & OBJECT_TYPE_1H) ||
2022 (io->type_flags & OBJECT_TYPE_2H) ||
2023 (io->type_flags & OBJECT_TYPE_BOW)) {
2024
2025 if(player.bag) {
2026 Entity * pObjMin = NULL;
2027 Entity * pObjMax = NULL;
2028 Entity * pObjFIX = NULL;
2029 bool bStop = false;
2030
2031 for (int iNbBag = 0; iNbBag < player.bag; iNbBag++) {
2032 for (size_t j = 0; j < INVENTORY_Y; j++) {
2033 for (size_t i = 0; i < INVENTORY_X; i++) {
2034
2035 if ((inventory[iNbBag][i][j].io) &&
2036 (inventory[iNbBag][i][j].io != io) &&
2037 ((inventory[iNbBag][i][j].io->type_flags & OBJECT_TYPE_DAGGER) ||
2038 (inventory[iNbBag][i][j].io->type_flags & OBJECT_TYPE_1H) ||
2039 (inventory[iNbBag][i][j].io->type_flags & OBJECT_TYPE_2H) ||
2040 (inventory[iNbBag][i][j].io->type_flags & OBJECT_TYPE_BOW)))
2041 {
2042
2043 if ((io->ioflags & IO_ITEM) &&
2044 (inventory[iNbBag][i][j].io->ioflags & IO_ITEM) &&
2045 (inventory[iNbBag][i][j].io->_itemdata->equipitem))
2046 {
2047 if (inventory[iNbBag][i][j].io->_itemdata->equipitem->elements[IO_EQUIPITEM_ELEMENT_Damages].value == io->_itemdata->equipitem->elements[IO_EQUIPITEM_ELEMENT_Damages].value)
2048 {
2049 pIOChangeWeapon = inventory[iNbBag][i][j].io;
2050 lChangeWeapon = 2;
2051 bStop = true;
2052 }
2053 else
2054 {
2055 if (inventory[iNbBag][i][j].io->_itemdata->equipitem->elements[IO_EQUIPITEM_ELEMENT_Damages].value > io->_itemdata->equipitem->elements[IO_EQUIPITEM_ELEMENT_Damages].value)
2056 {
2057 if (pObjMin)
2058 {
2059 if (inventory[iNbBag][i][j].io->_itemdata->equipitem->elements[IO_EQUIPITEM_ELEMENT_Damages].value > pObjMin->_itemdata->equipitem->elements[IO_EQUIPITEM_ELEMENT_Damages].value)
2060 {
2061 pObjMin = inventory[iNbBag][i][j].io;
2062 }
2063 }
2064 else
2065 {
2066 pObjMin = inventory[iNbBag][i][j].io;
2067 }
2068 }
2069 else
2070 {
2071 if (inventory[iNbBag][i][j].io->_itemdata->equipitem->elements[IO_EQUIPITEM_ELEMENT_Damages].value < io->_itemdata->equipitem->elements[IO_EQUIPITEM_ELEMENT_Damages].value)
2072 {
2073 if (pObjMax)
2074 {
2075 if (inventory[iNbBag][i][j].io->_itemdata->equipitem->elements[IO_EQUIPITEM_ELEMENT_Damages].value < pObjMax->_itemdata->equipitem->elements[IO_EQUIPITEM_ELEMENT_Damages].value)
2076 {
2077 pObjMax = inventory[iNbBag][i][j].io;
2078 }
2079 }
2080 else
2081 {
2082 pObjMax = inventory[iNbBag][i][j].io;
2083 }
2084 }
2085 }
2086 }
2087 }
2088 else
2089 {
2090 if (!pObjFIX)
2091 {
2092 pObjFIX = inventory[iNbBag][i][j].io;
2093 }
2094 }
2095 }
2096
2097 if (bStop)
2098 {
2099 break;
2100 }
2101 }
2102
2103 if (bStop)
2104 {
2105 break;
2106 }
2107 }
2108
2109 if (bStop)
2110 {
2111 break;
2112 }
2113 else
2114 {
2115 if (pObjMax)
2116 {
2117 pIOChangeWeapon = pObjMax;
2118 lChangeWeapon = 2;
2119 }
2120 else
2121 {
2122 if (pObjMin)
2123 {
2124 pIOChangeWeapon = pObjMin;
2125 lChangeWeapon = 2;
2126 }
2127 else
2128 {
2129 if (pObjFIX)
2130 {
2131 pIOChangeWeapon = pObjFIX;
2132 lChangeWeapon = 2;
2133 }
2134 }
2135 }
2136 }
2137 }
2138 }
2139 }
2140 }
2141
loadScript(EERIE_SCRIPT & script,PakFile * file)2142 void loadScript(EERIE_SCRIPT & script, PakFile * file) {
2143
2144 if(!file) {
2145 return;
2146 }
2147
2148 free(script.data);
2149
2150 script.data = file->readAlloc();
2151 script.size = file->size();
2152
2153 std::transform(script.data, script.data + script.size, script.data, ::tolower);
2154
2155 script.allowevents = 0;
2156
2157 free(script.lvar), script.lvar = NULL, script.nblvar = 0;
2158
2159 script.master = NULL;
2160
2161 for(size_t j = 0; j < MAX_SCRIPTTIMERS; j++) {
2162 script.timers[j] = 0;
2163 }
2164
2165 ARX_SCRIPT_ComputeShortcuts(script);
2166
2167 }
2168